字符串的自动合并
int main( ) { char *Str[] = {"abc" "def"}; printf( "%s\n", Str[0] ); system("pause"); return 0; }
输出结果:abcdef
ANSI C规定相邻的两个字符串合并成一个字符串,必须注意在两个字符串之间添加“ ,”!但多余的“ ,”却并没有什么影响!
2.sizeof 的操作数
int main( ) { int b = 2; int *p = &b; int a = 3 *sizeof *p; printf( "%d\n", a ); system( "pause" ); return 0; }
输出结果:12
在上述程序中sizeof后面的操作数是(*p)所以是计算了 p指向的数据的数据类型的长度。
此处要注意运算符的优先级问题
tips: .高于* []高于* ()高于* ==和!=高于位操作和赋值 算数高于移位 逗号的优先级最低
3.结合性
1)所有赋值运算都具有右结合性
int main( ) { int a, b = 1, c = 2; a = b = c; printf( "%d\n", a ); system( "pause" ); return 0; }
输出结果:2
c赋值给b ,b赋值给c。
4.空格的误用
int main( ) { char a[] = "Hi,I am a\ long string!"; printf( "%d\n", a ); system( "pause" ); return 0; }
此处在转义符的后面添加了空格,语法错误编译不能通过。这样的错误很难被发现,尽量避免!
5.最大一口策略
int main( ) { int a = 0; int b = 0; b = a+++3; printf( "%d,%d\n", b ,a); system( "pause" ); return 0; }
输出结果:3,1
结合性是a++ + 3
int main( ) { int *a = NULL; int *b = NULL; int c=*b/ *a; printf( "%d,%d\n", b ,a); system( "pause" ); return 0; }
在“/”和“*”之间必须有空格,不然编译器将后面的内容自动解析称为注释!
6.优先级规则分析案列
char *const *(*next)();
解释为:next是一个指向函数的指针,该函数的返回值是char *const
7.内存泄漏
内存泄漏:顾名思义,内存泄漏是由于我们使用了内存却没有收回造成的,
这样的问题十分容易降低我们计算机的性能,并且泄漏的内存
往往大于你忘记释放的数据的大小,因为malloc分配的内存通常
会圆整为下一个大于申请数量的2的整数次方。
*同时,我们必须要避免内存损坏,即释放、改写仍在使用的内存。
8.段错误
1).解引用非法值的指针引起
//引起一个段错误 int main() { int *p=0; *p = 7; system( "pause" ); return 0; }
2).如果未初始化的指针恰好具有未对齐的值,会引起总线错误而不是段错误。
3).解引用一个空指针同样引起段错误(常常由于系统返回一个空指针而程序员不加判断直接使用造成)
4).用完了堆栈或者堆空间
引起段错误常见编程问题:1.坏指针值错误:在指针赋值之前就用它来引用内存或在释放之后仍然使用该指针
2.改写错误:越界写入数据
3.指针释放:多次释放或者释放一块不属于你的内存
注意:链表中元素的释放会经常引起此问题,我们必须用一个临时变量来保存下一次的地址才能够进行释放
9.类型转换
a.类型提升
printf("%d\n",sizeof'A');
此程序的输出结果为4,在此char型自动被提升为int型。事实上计算机将char和short提升为整型,将float提升成double型
整型提升规则:每个变量的值被提升为int型的长度,然后对这些值进行计算,最后再对结果进行裁剪。
如果两个char型的加法运算结果没有溢出,那么在实际执行时只需要产生char类型的运算结果,可以省略提升。
float f1,f2; double d; .... f1 = f2*d;
如果编译器可以确定用float运算的结果和用double运算的结果相同,那么编译器也可以使用float类型运算。
C语言中的类型提升
char-->int 位段-->int
enum-->int unsigned
char-->int short-->int
unsigned short-->int
float-->int
任何数组-->相应类型的指针
float-->int 任何数组-->相应类型的指针
***参数也会进行提升,但是如果使用了函数原型,那么缺省的提升将不会发生!
10.有限状态机
有限状态机用于有限数量子程序的发展变化每个程序处理并选择下一个应该进入的状态
例如:注释转换
基本思路:用一张表来保存所可能的状态,并列出进入每个状态之后需要执行的动作,一般的最后一个动作用于决定你将要进入哪个状态。
我们可以通过函数指针数组来调用函数
extern int a() ,b(),c(),d(); int (*State[])()={a,b,c,d}; //可以通过调用数组中的指针来调用函数 (*State[i])();
当然,switch也是一个不错的实现机制
11.数组和指针的规则
a.“表达式中的数组名”相当于指针
b.C语言把数组的下标作为指针的偏移量
c.作为函数参数的数组名相当于指针
关于这一点我们应该明白是出于对效率的考虑,类似的函数的返回值一定不能是一个数组,而是一个指向该数组的指针
Tips:为了保证一致性我们的定义和声明的形式必须保持一致
请尝试一下代码:
char ga[] = "abcdefghi"; void my_array( char ca[10] ) { printf( "数组的地址是:%#x \n",&ca); printf( "数组第一个元素的地址:%#x\n",&(ca[0]) ); printf( "数组第二个元素的地址是:%#x\n",&(ca[1]) ); printf( "++数组名得到的地址是:%#x\n",&(ca[1]) ); } void my_pointer( char *pa ) { printf( "指针的地址是:%#x\n",&pa); printf( "指针偏移0的地址是:%#x\n", &(pa[0]) ); printf( "指针偏移1的地址是:%#x\n", &(pa[1]) ); printf( "++指针的地址是:%#x\n", ++pa ); } int main( ) { printf( "全局变量数组的地址为:%#x\n",&ga ); printf( "全局变量数组ga[0]的地址为:%#x\n", &(ga[0]) ); printf( "全局变量数组ga[1]的地址为:%#x\n", &(ga[1]) ); my_array( ga ); my_pointer( ga ); system( "pause" ); return 0; }
在输出的结果里我们发现:在函数里数组的地址和数组的第一个参数的地址不一样!
**在C语言中,我们无法向一个函数传递一个长度不确定的多维数组,你必须提供给它除了最左边一维以外所有的长度
在数组作为参数时:
1.一维数组 ―― 需要包含一个计数值来方便我们检测是否越界访问
2.二维数组 ―― 不能直接传递给函数,可以改成一个指向向量的指针数组来传参
eg: char **myarray;
3.三维或更多维数组 ―― 无法使用。必须将之分解成几个维度更小的数组。