笔记1
1. 不要让main函数返回void。
正确的main函数只有下面两种写法:
int main() {}
int main(int argc, char* argv[]) {}
2. 0可以表示整型的0,空指针NULL,字符串结束表示'\0'和逻辑的FALSE(注意还不是bool的false,大小写也有差别,尤其是C语言中没有bool类型)。
需要注意,这里的NULL,FALSE一般都是自定义,或者标准中定义的宏。
例如:
#define NULL 0
#define FALSE 0
3. 对表达式计算顺序不要想当然。
这里分为几种情况,一种就是表达式中运算符的优先级,这个好解决,注意自己用上括号就可以了。
另一种就是表达式本身的求值顺序问题,这也分为两种,一种是函数参数的求值顺序:
int i = 0;
printf("%d %d\n", i, i+=1);
上面的代码中,打印的结果是0 1,还是1 1,实际上并没有准确的说法,这个是编译器决定的。
另外一种就是下面这样的情况:
a = f1() + f2() * f3();
上面代码中,哪个函数是先运行的,没有办法确定。而如果三个函数都有操作相同的资源,就可能导致意想不到的问题。
所以好的做法应该是这样的:
a1 = f1();
a2 = f2();
a = a1 + a2 * f3();
这样顺序就可以按照程序员的要求来运行。
4. 关于#define,要小心各种陷阱。
说到底,#define只是字符替换,所以要注意各种替换时的错误。
好的办法是多使用(),和{}或者do{}while(0),并且参数不要有i++这种。
5. 使用局部指针要及时初始化。
6. 逗号表达式的结果是最右边表达式的值。
7. 不要用复杂的函数指针,多用typedef来简化。
8. 防止文件重复包含。
使用:
#ifndef _XXX_YYY_
#define _XXX_YYY_
//...
#endif
9. 结构体布局及对齐优化。
下面是结构体对齐三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除。(这里说了基本类型,真的吗?不过对于结构体变量来说,本身的地址并不是很重要)
2) 结构体每个成员相对于结构体首地址的偏移量都是成员自身大小的整数倍。
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍。(这里说了基本类型,真的吗?下面的代码确定下)
//首先创建一个非基本类型的结构体
typedef struct _stu {
int a; //4字节
short b; //2字节
char c; //1字节
} stu; //共7字节,但是需要扩展成8字节;
//再将stu放在一个新的结构体中
typedef struct _stu2 {
int a;
stu b;
} stu2;
int main() {
stu a;
stu2 b;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(b));
return 0;
}
打印的结果是8 12。这说明确实是基本类型。
10. 将强制类型转换减少到最少。
11. 优先使用前缀操作符。
主要原因是前缀操作符比后缀操作符具有稍微高一点的运行效率。
原因是无论是前缀还是后缀,理论上的操作符都是operator++(),而为了区别,后缀改成了operator++(int),并在实现中会有一个临时的变量,这就导致了一些运行上的效率损失。但是个人认为这个差距也太少了,并且有些地方后缀操作符会更符合要求,或者本来用的人就多...
12. 掌握变量定义的位置与时机。
这么做的目的是为了降低构造和析构的成本。
不过对于基本类型应该没有这个问题吧。
13. 注意typedef和宏的区别。
typedef声明的是一个别名;宏只是简单的字符替换。
形式也不一样:
#define FALSE 0
typedef unsigned char UINT8;
要注意两个:一个是两者定义的顺序相反,另一个是typedef最后要加分号。
14. 尽量不要使用可变参数。
原因有三:
1) 没有类型检查。
2) 需要手动协调格式。比如printf("%s, %d\n", x, y),那么x,y的类型都要自己确定好,很容易有疏漏。
3) 不支持自定义类型。在C语言还好,但是C++中到处都是自定义类型。
15. 区分void和void *。
void在C/C++中的用途主要就两个:
1. 函数没有返回时注明返回void。
2. 函数没有参数时注明参数是void。
void *是指向void的指针,这本身没有意义,但是从另一方面来说,表示的是void *可以指向任意类型。
void *有以下几点说明:
1. void *可以赋值为任意类型的指针,但是反过来的话,在C++中会报错(C语言中不会),即任意类型不能直接赋值为void *。
2. void *在ANSI标准下不能自增,但是据说在GNU下可以,不过最好是不要。
3. 如果函数的参数可以是任意指针,一般就写void *。比如memcpy等。
PS:以上是第一部分。只写了自己需要注意的部分,没有所条目都列出来。