1. 优先级问题。
注意++和--与*结合的优先级。注意自加自减操作与单目运算的结合。最好的方式是使用括号。
2. 注意指针的算术操作,只有加减法。及指针和指向指针的指针意义。及函数传递指针。
3. 函数的参数传递都是值传递。但注意传递指针可操作指针地址的内容。指向指针的指针可操作地址。
a) ADT可以通过头文件和static限定作用域来实现。类似类中的公有和私有成员。
b) 注意递归函数调用造成重复计算。注意递归结束的条件。尽可能使用迭代取代递归。
c) 可变参数列表中参数的缺省参数类型提升。因为…部分参数没有声明(也无法声明)原型,则所有作为可变参数传递给函数的值都将做缺省的参数类型提升。简单举例即char,short将被提升为int ,float将被提升为double。需要注意正确指定类型。
d) 可变参数的只能通过顺序访问。这和参数压入栈原理有关。不过C99提供了va_copy为参数列表复制做出了可能。
4. 数组名的类型是什么。其本质意义是一个指针常量。即当我们定义 int b[10];时。我们使用b其实使用的一个指针常量。B是一个指针,且其值不可变。而其指向的类型由其定义时指定的数组类型决定。总结来说。数组名是一个指向其他类型的指针常量。
仅在两种情况下,数组名不作为指针常量解释(编译器)。1.sizeof关键字操作时,返回的是整个数组的长度。&取地址符操作时。产生指向数组的指针,而不是指向指针常量的指针。
a) 下标的的实现方式。当你知道数组名是一个指针常量时。那么*(b+2)表达什么意思很容易明白。对!它与b[2]完全相等。array[sub]完全等同于*(array+(sub))。下标的方式相当于数组名所指示的地址与下标表达式的加法计算和地址内容。2[array]表示什么?按照刚刚的解释即为*(2+(array))。那么它和*((array)+2)等同。去掉括号则可以看出实际它与array[2]是一致的。当要注意C语言不检查下标是否超出范围。
b) 当下标与指针方式都正确时,下标绝不会比指针更有效,但指针可能比下标更有效。
5. 注意字符串的库函数。有很多需要有一定的了解。尤其注意其返回函数。
6. 联合和结构体。
a) 注意结构体的存储分配问题。即边界对齐。
例
Struct
{
Char c;
Int b;
Char x;
}struct_membig;
与
Struct
{
Int b;
Char c;
Char x;
}struct_memsml;
struct_membig;要占用比struct_memsml更多的内存。因为一般来说编译器会以int(4个字节)做内存对齐的。即两种不同类型的数据存储时,必须按照边界对齐来实现。
b) 声明结构体可以省略结构体标签名。但当结构体内包含自身指针时标签不可省略。
c) 结构体不可以包含自身,(这将产生不可结束的递归)但可以包含自身类型的指针
d) 尽量避免传递结构体参数。因为函数的参数都是拷贝复制传递的。结构体的拷贝复制会占用大量空间和效率。所以尽量使用指针和const关键字组合来使用结构做参数。
7. 注意回调函数利用来解决程序通用新问题。函数转移表:其本质就是就是函数指针数组。通过下标来访问。
8. 注意I/O函数。因为C语言为实现屏蔽I/O接口差异采用了缓冲区机制,而且大多数实现为完全缓冲区,即当I/O缓冲区被写满才执行对设备I/O的数据操作。实现的理解就是当你调用printf()函数时,并不是立即就将信息打印的,而是写入缓冲区。这点在使用prinf函数调试时应注意,当程序崩溃时最后打印的内容可能不是崩溃点信息。因为在上次缓冲区满到崩溃这段时间的缓冲数据有可能并没有达到IO。使用fflush(stdout)来迫使立即将缓冲区内容进行实际IO操作。