第二章 c数据类型及语句
2.1 关键字
2.1.1 数据类型相关的关键字
用于定义变量或者类型
类型 变量名:
char、short、int、long、float、double、struct、union、enum、signed、unsigned、void
1、char字符型,用于char定义的变量是字符型变量,占1个字节
char ch=‘a’; =为赋值号
char ch1 = ’1‘ 正确
char ch2 = ’123‘ 错误
2、short 短整型,使用short定义的变量是短整型变量,占2个字节
short int a = 11; -32768 ----- 32767
3、int 整型, 用int定义的变量是整型变量,在32位系统下占4个字节,在16平台下占2个字节
int a = 44; -20亿 ----- 20亿
4、长整型 用long定义的变量是长整型,在32位系统下占4字节
long int a = 66;
5、float单浮点型(实数),用float定义的变量是单浮点型的实数,占4个字符
float b = 3.8f;
6、double双浮点型(实数),用double定义的变量是双浮点型的实数,占8个字符
double c = 88;
7、struct 与结构体类型相关,可用来定义结构体类型
8、union 与共用体相关
9、enum 枚举
10、signed 有符号(正负)的意思
在定义char、整型(short、int、long)数据时候用signed修饰,表示可以保存为正数或负数
signed int b = -6;
注意:默认情况下signed可以省略,即 Int a = -10;
11、unsigned无符号的意思,只能保存正数和数字0
unsigned int a = 101;
unsigned int a = 0;
unsigned int a = -1200; //错误
扩展:内存存储
char ch=’a'; //占1个字节,存储的是97
0110 0001
字节:内存的基本单位,8位为一个字节
计算机存储时,只能存储1和0的二进制组合,1和0都分别占1位
字符型数据在内存中存储的不是字符本身,而是存储其ASCAll码
整型变量存储的是其值的二进制
12、void 空类型的数据类型
void不能用来定义变量,没有void类型的变量
void是用来修饰函数的参数或返回值,代表函数没有参数或者没有返回值
例子:
void fun(void)
{
}
代表fun函数没有返回值,fun函数,没有参数
例2:
#include<stdio.h>
int main()
{
char a;
short int b;
int c;
long int d;
float e = 3.8f;
double f = 5.6;
printf("sizeof(char) = %d\n",sizeof(a));
printf("sizeof(char) = %d\n",sizeof(b));
printf("sizeof(char) = %d\n",sizeof(c));
printf("sizeof(char) = %d\n",sizeof(d));
printf("sizeof(char) = %d\n",sizeof(e));
printf("sizeof(char) = %d\n",sizeof(f));
return 0;
}
sizeof(char) = 1
sizeof(char) = 2
sizeof(char) = 4
sizeof(char) = 4
sizeof(char) = 4
sizeof(char) = 8
2.1.2 存储相关的的关键字
register、static、const、auto、extern
1、register 是 寄存器的意思,用register修饰的变量是寄存器变量,
即:在编译的时候告诉编译器这个变量是寄存器变量,尽量将其存储空间分配在寄存器中。
注意:
- 定义的变量不一定真的存放在寄存器中。
- cpu取数据的时候去寄存器中拿数据比去内存中拿数据要快
- 因为寄存器比较宝贵,所以不能定义寄存器数组
- register只能修饰字符型及整型的,不能修饰浮点型
- 因为register修饰的变量可能放在寄存器中不存放在内存中,所以不能对寄存器变量取地址。因为只有存放在内存中的数据才有地址;
register char ch;
register short int b;
register int c;
register long int d;
register float d; //错误
register int a;
int *p;
p =& a; //错误,a可能没有地址
2、static 静态
可修饰全局变量、局部变量、函数
3、const 常量-只读不可修改它的值
也可修饰指针
4、auto int a;与int a等价
5、extern 外部,一般用于函数和全局变量的声明
2.1.3 控制语句相关的关键字
if、else、break、continue、for、while、do、switch、case、goto、default
2.1.4 sizeof、typedef、volatile
1、sizeof
使用来测变量、数组的占用存储空间的大小(字节数)
int a = 10;
int num;
num = sizeof(a);
2、typedef 重命名相关关键字
unsigned short int a =10;
U16
关键字,作用是给一个已有的类型,重新起个类型名,并没有创造一个新的类型
以前大家看程序时见过类似的变量定义方法
INT16 a;
U8 ch;
INT32 b;
c语言中没有INT16 U8这种关键字
都是用typedef定义出来的新的类型名
其实就是short int及unsigned char的别名
typedef起别名方法:
-
用想起名的类型定义一个变量
short int a;
-
用新的类型名代替变量名
short int INT16;
-
在最前面加typedef
typedef short int INT16
-
就可以用新的类型名定义变量了
INT16 b和 short int b //是一个效果
#include<stdio.h>
// short int b;
// short int INT16;
typedef short int INT16;
int main(int argc, char*argv[])
{
short int a = 10;
INT16 c = 111;
printf("a=%d\n",a);
printf("c=%d\n",c);
return 0;
}
3、volatile易改变的意思
用volatile定义的变量,是易改变的,即告诉cpu每次用volatile变量的时候,重新去内存中取保证用的是最新的值,而不是寄存器中的备份。
volatile关键字现在较少使用
volatile int a = 10;
2.2 数据类型
2.2.1 基本类型
char、short int、int、long int、float、double
扩展:常量和变量
常量:在程序运行过程中,其值不可以改变的量
例:100 ‘a’ “hello”
- 整型 100, 125, -100, 0
- 实型 3.14,0.125f, -3.789
- 字符型 ’a’,‘b’,’2‘
- 字符串 “a“,”b",“1232”
变量:其值可以改变的量
int a =100;
a = 101
字符数据
-
字符常量:
直接常量:单引号括起来,如‘a’,‘b’
转义字符:以反斜杠“\”开头,后跟一个或几个字符,如’\n‘,‘\r’,等,分别代表换行、横向跳格。
’\‘双斜杠表示的是\ ’%%‘ ’\”
-
字符变量:
用char定义,每个字符变量被分配一个字节的内存空间
字符值以ASCII码的形式存放在变量的内存单元中;
注意:char al
a = ‘x’;
a变量中存放的是字符’x’的ASCII:120
即a =120跟 a='x’本质上是一至的
#include<stdio.h>
int main(int argc, char* argv[]) {
unsigned int i;
for(i = 0; i <= 255; i++){
printf("%d %c ", i,i);
if (i%10 == 0)
{
printf("\n");
}
}
return 0;
}
字符串常量
是由双括号引起来的字符序列,如“CHINA”、“哈哈哈”
“C program”、“¥12.5”等都是合法的字符串常量
字符串常量与字符常量的不同
‘a’为字符常量,“a”字符串常量
每个字符串的结尾,编译器会自动的添加一个结束标志位’\0‘,
即“a”包含两个字符’a’和’\0‘
整型数据
-
整型常量(按进制分):
十进制: 以正常数字1-9开头,如 457 789
八进制: 以数字0开头,如0123
十六进制:以0x开头,如0x1e
a=10, b=11, c=2, d=13, e=14, f=15
-
整型变量:
- 有/无符号短整型(un/signed)short(int) 2个字节
- 有/无符号基本整型(un/signed)int 4个字节
- 有/无符号长整型(un/signed)long(int) 4个字节 (32位处理器)
实型数据(浮点型)
-
实型常量
- 实型常量也称为实数或者浮点数
**十进制形式:**由数字和小数点组成:0.0、0.12、5.0
**指数形式:**123e3代表123*10的三次方
123e-3
- 不以f结尾的常量是double类型
- 以f结尾的常量(如3.14f)是float类型
-
实型变量
单精度(float)和双精度(double)3.1415926753456
float型:占4字节,7位有效数字,指数-37到38
3333.333 33
double型:占8字节,16位有效数字,指数-307到308
格式化输出字符:
%d | 十进制有符号数 | %u | 十进制无符号数 |
---|---|---|---|
%x | 以十六进制表示的整数 | %o | 以八进制表示的整数 |
%f | float型浮点数 | %lf | double浮点数 |
%e | 指数形式的浮点数 | %s | 字符串 |
%c | 单个字符 | %p | 指针的值 |
特殊应用:
%3d %03d %-3d %5.2f
%3d:要求宽度为3位,如果不足3位,前面空格补齐;如果足够三位,此句无效
%03d:要求宽度为3位,如果不足3位,前面0补齐,如果足够三位,此句无效
%-3d:要求宽度为3位,如果不足3位,后面空格补齐,如果足够三位,此句无效
%5.2f:小数点后只保留2位
2.2.2 构造类型
概念:由若干个相同或不同类型数据构成的集合,这种数据类型被称为构造类型
例:int a[10];
数组、结构体、共用体、枚举
2.2.3 类型转换
数据有不同的类型,不同类型数据之间进行混合运算时必然涉及到类型的转换问题。
转换的方法有两种:
-
自动转换:
遵循一定的规则,由编译系统自动完成。
-
强制类型转换:
把表达式的运算结果强制转换成所需的数据类型
自动转换原则:
1、占用内存字节数少(值域小)的类型,向占用内存字节数多(值域大)的类型转换,以保证精度不降低。
2、转换方向:
-
当表达式中出现了char、short int、int类型中的一种或多种,没有其他类型了参加运算的成员全部变成int类型的参加运算,结果也是int类型的
#include<stdio.h> int main(int arguc,char* argv[]) { printf("%d\n",5/2); return 0; } 2
-
当表达式中出现带小数点的实数,参加运算的成员全部变成double类型的参加运算,结果也是double型
#include<stdio.h>
int main(int arguc,char* argv[])
{
printf("%f\n",5.0/2);
return 0;
}
2.500000
-
当表达式中有有符号数,也有无符号数,参加运算的成员变成无符号数类型的参加运算,结果也是无符号数。(表达式中无实数)
#include<stdio.h> int main() { int a = -8; unsigned int b = 7; if (a + b > 0) { /* code */ printf("a + b > 0\n"); } else if (a + b < 0) { /* code */ printf("a + b < 0\n"); } else { printf("a + b = 0"); } return 0; } a + b > 0
-
在赋值语句中等号右边的类型自动转换为等号左边的类型
#include<stdio.h> int main(int argc, char* argv[]) { int a; float b = 5.8f; a = b; printf("a = %d \n",a); return 0; } a = 5
-
注意自动类型转换都是在运算的过程中进行临时性的转换,并不会影响自动类型转换的变量值和其类型
#include<stdio.h> int main(int argc, char* argv[]) { int a; float b = 5.8f; a = b; printf("a = %d \n",a); printf("b = %f \n",b); return 0; } a = 5 b = 5.800000
强制类型转换:
通过类型转换运算来实现
(类型说明)(表达式)
功能:
把表达式的运算结果强制转换成类型说明符所表示的类型
例如:
(float)a; //把a的值转换成为实型
(int)(x+y); //把x+y的结果转换为整型
注意:
类型说明符必须加括号
#include<stdio.h>
int main(int argc, char* argv[])
{
float x = 0;
int i = 0;
x=3.6f;
i = x;
i = (int)x;
printf("x = %f \n",x);
printf("i = %d \n",i);
return 0;
}
x = 3.600000
i = 3
2.2.4 指针
2.3 运算符
2.3.1 运算符
用算术运算符将运算对象(也称操作数)连接起来的、符合C语法规则的式子,称为C算数表达运算对象包括、变量、常量、函数等
例如:a * b/c - 1.5 + 'a'
2.3.2 运算符的分类
-
双目运算符:即参加运算的操作数有两个
例:+ a + b
-
单目运算符:即参加运算的操作数只有一个
++自增运算符 给变量值+1 ——自减运算符 int a = 10; a ++;
-
三目运算符:即参加运算的操作数有三个
()?():()
2.3.3 算数运算符
+ - * / % += -= *= /= %=
10%3 表达式结果为1
复合运算符:
a+=3 相当于 a = a+3
a*=6+8 相当于 a = a*(6+8)
2.3.4 关系运算符
(>、 <、 == 、 >= 、<= 、 !=)
!= 不等于
2.3.5 逻辑运算符
-
&& 逻辑与
两个条件都满足为真,否则为假
if((a>b)&&(a<c))
-
|| 逻辑或
两个条件至少有一个为真,则结果为真 if((a>b) || (a<c))
-
! 逻辑非
if(!(a>b)) { }
2.3.6 位运算符
十进制转二进制:
方法:除二求余法
例子:123 十进制转二进制 111 1011
十进制转八进制:
方法:除八求余法
十进制转十六进制:
方法:除十六求余法
正数在内存中以原码形式存放,负数在内存中以补码形式存放
正数的 原码=反码=补码
**原码:**将一个整数,转换成二进制,就是其原码。
如单字节的5的原码为:0000 0101; -5的原码为 1000 0101。
**反码:**正数的反码就是其原码;负数的反码是将原码中,除符号位以外,每一位取反。
如果单字节的5的反码为: 0000 0101; -5的反码为:1111 1010。
**补码:**正数的补码就是其原码; 负数的反码+1就是补码。
如单字节的5的补码为:0000 0101; -5的补码为1111 1011。
#include<stdio.h>
int main(int argc, char* argv[])
{
printf("%x \n",-5);
return 0;
}
// 1000 0000 0000 0000 0000 0000 0000 0101 原码
// 1111 1111 1111 1111 1111 1111 1111 1010 反码
// 1111 1111 1111 1111 1111 1111 1111 1011 补码
// FF FF FF FB
fffffffb
在计算机中,正数是直接用原码表示的,负数用补码表示。
无论是正数还是负数,编译系统都是按照内存中存储的内容进行运算。
-
& 按位 与
任何值与0得0,与1保持不变
#include<stdio.h> int main(int argc, char* argv[]) { printf("%x \n",-5 & 10); printf("%x \n",-5 | 10); return 0; } // -5 // 1000 0000 0000 0000 0000 0000 0000 0101 原码 // 1111 1111 1111 1111 1111 1111 1111 1010 反码 // 1111 1111 1111 1111 1111 1111 1111 1011 补码 // 10 // 0000 0000 0000 0000 0000 0000 0000 1010 /** * -5 & 10 * * 1111 1111 1111 1111 1111 1111 1111 1011 * 0000 0000 0000 0000 0000 0000 0000 1010 * --------------------------------------- * 0000 0000 0000 0000 0000 0000 0000 1010 * * a */ /** * -5 | 10 * * 1111 1111 1111 1111 1111 1111 1111 1011 * 0000 0000 0000 0000 0000 0000 0000 1010 * --------------------------------------- * 1111 1111 1111 1111 1111 1111 1111 1011 * * fffffffb */ a fffffffb
-
| 按位 或
任何值或1得1,或0保持不变
#include<stdio.h> int main(int argc, char* argv[]) { printf("%x \n",-5 & 10); printf("%x \n",-5 | 10); return 0; } // -5 // 1000 0000 0000 0000 0000 0000 0000 0101 原码 // 1111 1111 1111 1111 1111 1111 1111 1010 反码 // 1111 1111 1111 1111 1111 1111 1111 1011 补码 // 10 // 0000 0000 0000 0000 0000 0000 0000 1010 /** * -5 & 10 * * 1111 1111 1111 1111 1111 1111 1111 1011 * 0000 0000 0000 0000 0000 0000 0000 1010 * --------------------------------------- * 0000 0000 0000 0000 0000 0000 0000 1010 * * a */ /** * -5 | 10 * * 1111 1111 1111 1111 1111 1111 1111 1011 * 0000 0000 0000 0000 0000 0000 0000 1010 * --------------------------------------- * 1111 1111 1111 1111 1111 1111 1111 1011 * * fffffffb */ a fffffffb
-
~ 按位取反
1变0,0变1
#include<stdio.h> int main(int argc, char* argv[]) { printf("%x \n",~(-5)); return 0; } // 1000 0000 0000 0000 0000 0000 0000 0101 原码 // 1111 1111 1111 1111 1111 1111 1111 1010 反码 // 1111 1111 1111 1111 1111 1111 1111 1011 补码 // ` // 0000 0000 0000 0000 0000 0000 0000 0100 --> 4 4
-
^ 按位异或
相异得1,相同得0
-
位移
>> 右移 << 左移 注意:右移分:逻辑右移、算数右移
(1)、右移
逻辑右移:高位补0,低位溢出
算数右移:高位补符号位,低位溢出 (有符号数)
1000 1111
1111 0000
1111 1100
(A)、逻辑右移:
低位溢出、高位补0
0101 1010 >>3
0000 1011
(B)、算数右移:
对有符号数来说
低位溢出、高位补符号位。
1010 1101 >>3
1111 0101
0101 0011 >>3
0000 1010
总结 右移:
-
逻辑右移:高位补0,低位溢出
注意:无论是有符号数还是无符号数都是高位补0,低位溢出
-
算数右移:高位补符号位,低位溢出(有符号数)
注意:对无符号数来说,高位补0,低位溢出
对有符号数来说,高位补符号位,低位溢出
在一个编译系统中到底是逻辑右移,还是算数右移,取决于编译器
#include<stdio.h> int main(int argc, char* argv[]) { printf("%d \n", -1>>3); return 0; } // 如果结果还是 -1 则是算数右移 // -1 // 1000 0000 0000 0000 0000 0000 0001 原码 // 1111 1111 1111 1111 1111 1111 1110 反码 // 1111 1111 1111 1111 1111 1111 1111 补码 // -1>>3 // 1111 1111 1111 1111 1111 1111 1111 算数右移 -1
-
2.3.7 条件运算符
()?()😦)
A?B:C;
如果?前边的表达式成立,则整个表达式的值,是? 和: 之间的表达式的结果
否则是:之后的结果
#include<stdio.h>
int main(int argc, char* argv[])
{
int a;
a = (3< 5)?(8):(9);
printf("%d\n",a);
return 0;
}
8
2.3.8 逗号运算符
在C语言和C++语言中,可以把多个表达式用逗号联接起来(或者说,把这些表达式用逗号分开),构成一个更大的表达式。其中的逗号称为逗号运算符,所构成的表达式称为逗号表达式。逗号表达式中用逗号分开的表达式分别求值,以最后一个表达式的值作为整个表达式的值。
#include<stdio.h>
int main(int argc, char* argv[])
{
int num;
num = (5,6);
printf("%d\n",num);
return 0;
}
6
注意:逗号运算符的结果是,后边表达式的结果
2.3.9 自增自减运算符
i ++ i–
#include<stdio.h>
int main(int argc, char* argv[])
{
int i = 3;
int num;
num = i++;
printf("num=%d,i=%d\n",num,i); //num=3, i=4
return 0;
}
num=3,i=4
++i 先加后用
#include<stdio.h>
int main(int argc, char* argv[])
{
int i = 3;
int num;
num = ++i;
printf("num=%d,i=%d\n",num,i); //num=4, i=4
return 0;
}
num=4,i=4
2.3.10 运算符优先级及结构性
运算符优先级:
在表达式中按照优先级先后进行运算、优先级高的先于优先级底的运算。
优先级一样的按照结合性来运算
int a;
a = 2+5+3*4-6
运算符结合性:
左结合性:从左向右运算
int a;
a = 2 + 3 + 9 + 10;
右结合性:从右向左运算
int a,b,c,d;
a=b=c=d=100;
2.4 控制语句相关关键字讲解
2.4.1 选择控制语句相关的关键字
-
if语句
-
if else语句
-
if else if 语句
-
switch 语句
switch(表达式)、、表达式只能是字符型或整型(int、 short int、 long int) { case 常量表达式1: 语句1; break; case 常量表达式2: 语句2; break; default: 语句3; break; }
2.4.2 循环控制语句
-
for 循环
for(表达式1;表达式2;表达式3) { }
-
while 循环
-
do{}
while()
-
goto
#include<stdio.h> int main(int argc, char* argv[]) { printf("test000000000000000000000\n"); printf("test111111111111111111111\n"); goto tmp; printf("test22222222222222222222\n"); printf("test33333333333333333333\n"); printf("test44444444444444444444\n"); printf("test55555555555555555555\n"); tmp: printf("test66666666666666666666\n"); return 0; } test000000000000000000000 test111111111111111111111 test66666666666666666666
第三章 数组
3.1 数组的概念
数组是若干个相同类型的变量在内存中有序存储的集合。
int a[10]; //定义了一个整型的数组a, a是数组的名字, 数组中有10个元素,每个元素的类型都是int类型,而且在内存中连续存储。
这十个元素分别是a[0] a[1] …a[9]
a[0] a[1] …a[9]在内存中连续的顺序存储
3.2 数组的分类
3.2.1 按元素的类型分类
-
字符数组
即若干个字符变量的集合,数组中的每个元素都是字符型的变量
char s[10]; s[0],s[1].......s[9];
-
短整型的数组
short int a[0]; a[0],a[0]=4;a[9]=8;
-
整型的数组
int a[10]; a[0] a[9]; a[0]=3; a[0]=6;
-
长整型的数组
long int a[5];
-
浮点型的数组(单、双)
float a[6]; a[4]=3.14f
-
指针数组
char* a[10]; int* a[10];
-
结构体数组
struct stu boy[10];
3.2.2 按维数分类
一维数组:
int a[30];
类似于一排平房
二维数组:
int a[2][30];
可以看成一栋楼房,有多少层,每层有多少个房间,也类似于数学中的矩阵。
二维数组可以看成由多个一维数组构成的。
有行,有列。
多维数组
int a[4][2][10];
三维数组是由多个相同的二维数组构成的
int a[5][4][2][10];
3.3 数组的定义
定义一个数组,在内存里分配空间
3.3.1 一维数组的定义
格式:
数据类型 | 数组名 | [数组元素个数] |
---|---|---|
int | a | [10] |
char | b | [5] |
5个变量分别为 b[0],b[1],b[2],b[3],b[4]
在数组定义的时候可以不给出数组元素的个数,根据初始化的个数来决定数组的大小
#include<stdio.h>
int main(int argc, char *argv[])
{
int a[] = {
1,2,3,4,5};
printf("%d\n",sizeof(a));
return 0;
}
5
3.3.2 二维数组的定义
格式:
数据类型 | 数组名 | [行的个数] [列的个数] |
---|---|---|
int | a | [4] [5] |
定义了20个int类型的变量 分别是
a[0][0],a[0][1],a[0][2],a[0][3],a[0][4];
a[1][1],a[1][1],a[1][2],a[1][3],a[1][4];
a[2][2],a[2][1],a[2][2],a[2][3],a[2][4];
a[3][3],a[3][1],a[3][2],a[3][3],a[3][4];
多维数组定义:
int a[3][4][5]
int a[8][3][4][5];
扩展:
二维数组在定义的时候,可以不给出行数,但必须给出列数,二维数组的大小根据初始化的行数来定。
#include<stdio.h>
int main()
{
int a[][3] = {
{
1,2,3},
{
4,5,6},
{
7,8,9},
{
10,11,12}
};
int b[3][4];
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(b));
return 0;
}
48
48
3.4 定义并初始化
定义数组的时候&#