一.基本数据类型
以下所写的占用的存储空间在不同编译器中可能不同,标注的只是通常的情况 //数据类型关键字: int:整型 signed(C90中新增):有符号的 long:增长 void(C90中新增):空值 short:缩短 _Bool(C99中新增):布尔值 unsigned:无符号的(仅有正数) _Complex(C99中新增):复数 char:字符 _Imaginary(C99中新增):虚数 float:(单精度)浮点型 double:双精度浮点型1.整型:
用于储存整数,分为: 1.短整型(short int或short):占用2B(不能多于int) 2.整型(int):占用4B 3.长整型(long int或long):占用4B(不能少于int) 4.16进制或8进制整数: ①在前面加0x或0X表示16进制数,如0x10表示10进制下的16 ②在前面加0表示8进制数,如010表示10进制下的8 5.long long int或long long:C99加入;占用至少8B(不能少于long int) 6.unsigned int或unsigned:无符号的整型 可以表示比int允许的更大的数:如16位unsigned取值范围是0~65535,而int是-32768~32767 7.unsigned long int或unsigned long:C90加入 8.unsigned short int或unsigned short:C90加入 9.unsigned long long int或unsigned long long:C99加入 10.在任何有符号类型前加signed,可强调有符号的意图:如short与signed short相同 11._Bool:布尔值,即true和false;实际上也是1种整数类型,用0表示false,1表示true,但原则上仅占用1位 参见 C语言细节.流程控制.2 部分 注意: 1.有些编译器无法识别5/7/8/9 2.优先使用unsigned类型以表示更大的数 3.较大的类型会降低运算速度,尽量避免使用 整型存储方式:2.浮点型:
用于储存小数,分为: 1.单精度浮点数(float):占用4B,7位有效数字 2.双精度浮点数(double):占用8B,15~16位有效数字 3.long double:精度不低于double 存储问题: 1.浮点数的存储可能存在误差 比如float x=6.234,实际存储的可能是6.239999 因此,判断float类型的变量x的值是否为0的标准方法是: if (abs(x)<= 1e-6)//abs()表示取绝对值 printf("是"); else printf("不是"); 判断double类型的变量y的值是否为0的标准方法是: if (abs(y)<= 1e-15) printf("是"); else printf("不是"); //float的精度误差为1e-6,double的精度误差为1e-15 2.对于一些算术运算(如2个大数相减),浮点数损失的精度更多 3.浮点数运算比整数运算慢,虽然现在这个差距已经缩小了 浮点型的存储方式: 注:在计算机内部使用的是二进制和2的幂进行存储,而不是十进制和10的幂3.字符型:
字符(char):用于储存单个字符,使用' '括起,占用1B #注意: 1.C语言中没有可以直接存储多个字符的数据类型,需要通过字符数组来完成该功能(参见 C语言基础.数组.六 部分) 2.char类型不得使用" "扩起,因为这样内部至少有2个字符(如"A"表示"A\0") 不过好像可以通过编译,但警告如下: [Warning] initialization makes integer from pointer without a cast 而且下述命令打印不出来变量值: char m="a"; printf("%c",m);//结果: (没错,什么都没有) 3.''不合法,但' '合法 4.字符变量实际上存储的是该字符相应的ASCII码,因此可以像整数一样进行运算 char ch; scanf("%c",&ch);//输入:s //char ch=getchar();//相当于上面2行 putchar(ch+1);//结果:t printf("\n"); putchar(ch/2);//结果:9 putchar('\n'); putchar(ch-ch/10);//结果:h printf("\n"); printf("%c\n",ch-2);//结果:q二.复合数据类型 1.结构体: 见 结构体 部分
2.枚举(Enumerated Type) (1)什么是枚举:
定义:列出某个有穷集的所有成员 即:把某个事物所有可能的取值全部列举出来(2)语法:
//定义1个枚举:这里是定义了1个数据类型(或模板),而不是定义了1个变量 enum <enum_name> { <name0>[=<val0>],<name1>[=<val1>]... //注意:结尾没有分号(;) };//注意:这里有分号(;) //参数说明: enum_name:枚举的名称 name:该枚举所有可能的成员的名称(这些符号常量称为"枚举符"(Enumerator)) //这里的名称可以是任意值(如为Monday),不一定是char/int...等C语言中的数据类型 val:该枚举所有可能的成员的值 //本质上存储和处理的是这些值,但赋值时不能用相应的值来代替成员名称 //这些可能的值默认会被从0开始按顺序编号(val0为0,val1为1...) //指定某成员的值为x,则之后的值会从x+1开始按顺序编号,直到下1个被指定了值的成员 //定义1个枚举变量:这里才定义了1个变量 enum <enum_name> <evar>[=<val>]; //参数说明:enum_name同上 evar:变量名 val:该变量的值 //此处的值必需在定义枚举时列举的值(val0,val1...)中 //而且不能使用该值的编号代替该值 //实例: #include <stdio.h> enum WeekDay { Monday,Tuesday,Wednesday,Thursday,Friday=10,Saturday,Sunday }; void f(enum WeekDay i) { switch (i) { case 0:printf("Monday\n");break; case 1:printf("Tuesday\n");break; case 2:printf("Wednesday\n");break; case 3:printf("Thursday\n");break; case 10:printf("Friday\n");break; case 11:printf("Saturday\n");break; case 12:printf("Sunday\n");break;//结果:Sunday } } int main(void) { enum WeekDay day1=Wednesday,day2=Sunday; printf("%d\n",day1);//结果:2//因为Wednesday是WeekDay中的第3个选项 //把枚举变量当作int输出,实际上输出的是枚举变量的值的编号 printf("%d\n",)//结果:12 f(day2);//day2实际上是按其值(Sunday)的编号(12)来被处理的 return 0; }(3)枚举的优缺点:
优点:代码更安全;提高了代码的可读性 缺点:书写麻烦(4)自增/减:
C语言允许对枚举变量使用自增/减运算符: //不过C++不允许,所以如果希望兼容C++,就不能使用自增/减运算符 #include <stdio.h> enum colors { red,yellow,green,black }; int main(void) { enum colors color; char * acolors[]={"red","yellow","green","black"}; for (color=red;color<black;color++) { printf("%s ",acolors[color]); } return 0; } //结果: red yellow green3.联合
参见 C语言细节.其他数据类型.一 部分三.变量 1.什么是变量:
变量是那些在程序运行过程中可以改变的量 变量指向某块内存空间,这块内存空间中保存有该变量存储的数据 也就是说,变量的本质就是内存中的1段存储空间 程序一旦运行完毕,为其中所有变量分配的内存空间都会被释放掉 释放指这块内存空间可被分配给其他变量,而非清除内存中的遗留数据 变量类型同数据类型2.变量名:
变量名是1种标识符 //标识符命名规则: 1.只能由字母/数字/下划线(_)组成,数字不能在首位 //在C99/C11中根据通用字符名(UCN)机制添加了扩展字符集,其中包含了更多字符 2.不能用C语言的关键字作为变量名 3.但编译器只识别标识符名的前63个字符;外部标识符只允许31个字符 //如果2个标识符名在63个字符后才有区别,标准并未规定,结果取决于编译器 //C90仅允许6个字符;旧式编译器通常只允许8个字符 4.区分大小写 5.标识符名应尽量有意义(非强制) //注意: 1个变量名在1个程序中只能被定义1次: int a=45; int a=9; //报错: [Error] redefinition of 'a' [Note] previous definition of 'a' was here3.声明变量:
//注意:每条命令后面都需要加";" //声明变量:声明为变量创建并标记内存空间 <type> <var>[=<val>]; //这种只存储单个值的变量有时称为"标量变量"(Scalar Variable) //更详细的规则参见 C语言细节.其他数据类型.四 部分 //参数说明: type:指定数据类型 var:指定变量名 val:指定变量值(这个值本身是1个常量) //也可以把创建和赋值分开写: <type> <var>; <var>=<val>; //说明: 1.C99允许在任何位置定义变量 int price=0; printf("请输入金额(元):"); scanf("%d",&price); int change=100-price; printf("找零为%d元\n",change); 2.ANSI C要求必须在程序开头定义所有变量 int price=0; int change=0; printf("请输入金额(元):"); scanf("%d",&price); change=100-price; printf("找零为%d元\n",change); 3.变量必须先被定义,然后才能使用,如果定义位置在使用位置后,会找不到该变量 4.1个变量名不能在1个作用域内只能使用1次 //实例: int i=3; 等价于:int i;i=3; int i,j;i=3;j=5; 等价于:int i;int j;i=3;j=5; int i,j=3; 等价于:int i;int j;j=3; int i=3,j=5; 等价于:int i;int j;i=3;j=5; int i,j;i=j=5 等价于:int i;int j;i=5;j=54.变量的初始化:
初始化的意思就是赋初值,以覆盖内存中的垃圾值(即上1个占用这块内存空间的变量的遗留值) 如果不初始化,变量的值是0(为int)/空格(为char)/0.000000(为float)或为垃圾值或自动赋1个很大的值 在C语言中,初始化是在声明变量时完成的,如: int a=11; int b=1,c=2; int d,e=0;//注意:这里仅初始化了e四.常量
常量是固定值,在程序执行期间不会改变,又叫字面量 常量就像是常规的变量,不过其值在定义后不能修改 常量分为整数,浮点数,字符1.表示方法:
整数: 10进制:普通写法,如11 16进制:前面加0x/0X,如0x1F表示31 8进制:前面加0(加数字零,不是O),如010表示8 会按int-unsigned-long-unsigned long-long long-unsigned long long尝试存储 要指定作为某类型存储,可在值最后加后缀(10/8/16进制下均可),如12L/12ULL 浮点数: 普通写法:如94.23 科学计数法:如1.23e3表示1230,3.1e-1表示0.31 可在值最后加后缀以指定类型,默认是double,加L/l是long double,加F/f是float 字符: //所有用" "扩起的内容默认都会被在结尾补上1个结束符'\0' //在创建常量时,二者的type都是char 单个字符:用' '扩起,如'a' 注意:'ab'错误 多个字符(字符串;str):用" "扩起,如"ale" 注意:"a"正确,表示"a\0" 注意:输入不符合数据类型的值不一定报错,如:int a=1.2只会导致警告2.声明常量:
//方法1:将<var>定义成1个符号,此时<var>只是<val>的别名 常量名通常全部大写(非强制) 这样定义的常量称为"符号常量"(Symbolic Constant)或"明示常量"(Manifest Constant)或"宏常量"(Macro-Defined Constant) 程序中所有的<var>都会在编译时被替换为<val>,该过程称为"编译时替换"(Compile-Time Substitution) #define <var> <val>; //参数说明: var:指定常量名 val:指定常量值(这个值本身也是1个常量,即字面量) //可以是整数/浮点数/字符/字符串/表达式 //实例: #define Pi 3.1415926f; //方法2:将<var>定义成1个变量并告诉编译器其值固定,如果试图修改其值,则编译时会报错 这种本质上还是变量,称为"常变量";常变量名统称全部小写 const <type> <var> <val>; //参数说明: type:指定数据类型 var:指定常量名 val:指定常量值 //实例: const float pi 3.1415926f;3.常量/变量的二进制代码:
·整数以补码的形式存储,实数按IEEE754标准存储 ·字符按ASCII转换成整数,转换后的整数再以补码形式存储五.数据类型的转换 1.不同类型数据间的相互赋值:
1.字符和整数间可按ASCII互相转换 2.整数间,浮点数间,整数和浮点数间也可以互相转换,不过存在溢出的风险 通常,在语句和表达式中应使用类型相同的变量和常量 但是,如果使用混合类型,C语言会进行自动类型转换,但这有一定的危险性 1.当类型转换出现在表达式时,unsigned或signed的char/short会被自动转换成int,如有必要则 会被转换成unsigned int(如果short与int大小相同,unsigned short就比int大,这种情况下, unsigned short会被转换成unsigned int);在K&R那时的C中,f1oat会被自动转换成double(目 前的C则不是);由于都是从较小类型转换为较大类型,所以这些转换被称为升级(Promotion) 2.涉及2种类型的运算,2个值会被分别转换成2种类型的更高级别 3.类型的级别从高至低依次是long double,double,float,unsigned long long,long long unsigned long,long,unsigned int,int;例外是,当long和int大小相同时,unsigned int比 long的级别高;之所以没有列出short和char,是因为它们已经被升级到int/unsigned int 4.在赋值表达式语句中,计算结果会被转换成被赋值变量的类型,这个过程可能导致类型升级或降 级(Demotion);降级就是指转换成更低级别的类型 5.作为函数参数传递时,char/short被转换成int,float被转换成double int i=45; long j=102345; i=j; printf("%ld,%d\n",i,j);//结果:102345,102345 float x=6.6; double y=8.8; printf("%f,%lf\n",x,y);//结果:6.600000,8.800000 int a=45; int b=2147483649;//会发生溢出 printf("%d,%d\n",a,b);//结果:45,-2147483647 char m="a"; m=a; printf("%c",m);//结果:- char ch='A'; printf("%d\n",ch);//结果:65 char c=65; printf("%c\n",c);//结果:A 注意:升级通常不会有问题,但降级可能导致溢出 // 如果待转换的值和目标类型不符,结果将取决于转换涉及的类型,规则如下: 1.目标类型是无符号整型且待赋的值是整数时,额外的位将被忽略 如,如果目标类型是8位unsigned char,待赋的值是原始值求模256 2.如果目标类型是有符号整型且待赋的值是整数,结果因实现而异 3.如果目标类型是整型且待赋的值是浮点数,该行为是未定义的 4.当浮点类型被降级为整数类型时,原来的浮点值会被截断 如23.12和23.99都会被截断为23,-23.5会被截断为-232.强制类型转换运算符(Cast Operator):
//语法: (<type>)(<var>) //参数说明: type:指定要转换成什么类型 var:要转换的变量;可为变量名或变量值 //实例: printf("%c\n",(char)(39));//结果:' printf("%d\n",(int)('A'));//结果:65//即int→char按ASCII转换 printf("%d\n",(int)(3.7+1.2));//结果:4//即float→int会截断到整数位 printf("%f\n",(float)(3));//结果:3.000000//即int→float会补0 printf("%c\n",(char)(65.78));//结果:A//即float→char会先截断再按ASCII转换(相当于(char)((int)(<float>))) int i; float sum=0.0; for (i=1;i<=100;i++) { printf("%d\n",1/i);//结果:1 0 ... 0 printf("%f\n",1/(float)(i));//结果:1.000000 0.500000 ... 0.010000 printf("%f\n",(float)/(1/i));//结果:1.000000 0.000000 ... 0.000000 sum+=1/(float)(i); //相当于:sum+=1.0/i; printf("%f\n",sum);//结果:1.000000 1.500000 ... 5.187378 } printf("%f\n",sum);//结果:5.187378六.变量的作用域和存储方式 1.变量按作用域分类:
①全局变量:在所有函数外部定义的变量 作用域默认是从定义的位置到程序结尾 ②局部变量:在函数内部定义的变量和函数的形参 作用域只限于当前语句块(不包括上级语句块,也不包括下级语句块) 在main()中定义的变量也是局部变量;main()中也不能使用其它函数中定义的变量—main()与其它函数平权 可以在不同函数中使用相同的变量名,它们表示不同的数据,分配不同的内存,互不干扰 实参给形参传值的过程也就是给局部变量赋值的过程 如果局部变量和全局变量同名,全局变量在该作用域中会被局部变量覆盖 //实例: #include <stdio.h> #include <math.h> void qqq(int i); int j=33,r=22222;//全局变量 int main(void) { int i=12;//局部变量 qqq(i);//传入局部变量 return 0; } void qqq(i) {//局部变量 printf("%d\n",i);//结果:12//使用局部变量 //int j=2233;//加上这行结果是2233//局部变量 void ppp(j) {//局部变量 printf("%d\n",j);//结果:33//使用局部变量 } ppp(j);//传入全局变量;如果加上int j=2233那行,则此次传入的是局部变量 } void ttt(int k) {//局部变量 printf("%d,%d\n",k,r);//使用局部变量和全局变量 }2.变量按存储方式分类:
①静态变量 ②自动变量 ③寄存器变量七.关键字与保留标识符 1.关键字:
以下既包括C89中的关键字,也包括C90/C99/C11中新增的关键字 autobreakcasecharconstcontinuedefaultdodoubleelseenumexternfloatforgotoifinlineintlongregisterrestrictreturnshortsignedsizeofstaticstructswitchtypedefunionunsignedvoidvolatilewhile_Alignas_Alignof_Atomic_Bool_Complex_Generic_Imaginary_Noreturn_Static_assert_Thread_local //使用关键字作为标识符会报错: #include <stdio.h> int main(void) { int return=11; return 0; } //报错:[Error] expected identifier or '(' before 'return'2.保留标识符(Reserved Identifier):
还有一些保留标识符,已被C语言使用或保留了使用权,如printf 使用保留标识符作为标识符不会直接报错,但可能导致其他问题 //实例: #include <stdio.h> int main(void) { int printf=11; printf("AAA\n");//注释掉该行即可编译通过 return 0; } //报错:[Error] called object 'printf' is not a function or function pointer