必须记住!!!(C语言深度解剖)

sizeof是关键字,不是函数。
记住:sizeof 在计算变量所占空间大小时,括号可以省略,而计算类型(模子)大小时不能省略。一般情况下,还是不要偷懒,把括号写上。
sizeof(int)*p 表示什么意思?   4*p(4乘以p)
switch、case组合
1、case 后面只能是整型或字符型的常量或常量表达式(想想字符型数据在内存里是怎么存的)。
case语句的排列顺序:
2、按字母或数字顺序排列各条case 语句。
3、把正常情况放在前面,而把异常情况放在后面。
4、在多重循环中,如果有可能,应当将最长的循环放在最内层,最短的循环放在最外层,以减少CPU 跨切循环层的次数。长循环在最内层,效率高;长循环在最外层,效率低。
5、不能在for 循环体内修改循环变量,防止循环失控。把循环嵌套控制在3 层以内。
6、return 语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。
7、const 修饰的只读变量必须在定义的同时初始化。
因为:在定义的时候就把它的内存空间给限制死了,要是不初始化,那块区域永远就是那个样子了~~~~C语言标准规定的。
8、volatile 关键字和const 一样是一种类型修饰符,用它修饰的变量表示可以被某些编译器未知的因素更改,比如操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
出色注释的基本要求
9、只要斜杠(/)和星号(*)之间没有空格,都会被当作注释的开始。
10、说明一点:汇编程序的注释是以分号开头。
11、对于全局数据(全局变量、常量定义等)必须加注释。
12、注释采用英文,尽量避免在注释中使用缩写,特别是不常用缩写。
因为不一定所有的编译器都能显示中文,别人打开你的代码,你的注释也许是一团乱码。还有,你的代码不一定是懂中文的人阅读。
13、注释的位置应与被描述的代码相邻,可以与语句在同一行,也可以在上行,但不可放在下方。同一结构中不同域的注释要对齐。
14、注释代码段时应注重“为何做(why)”,而不是“怎么做(how)”。
15、数值的单位一定要注释。注释应该说明某数值的单位到底是什么意思。
接续符(\)和转义符
16、C 语言里以反斜杠(\)表示断行。编译器会将反斜杠剔除掉,跟在反斜杠后面的字符自动接续到前一行。但是注意:反斜杠之后不能有空格,反斜杠的下一行之前也不能有空格。
17、左移运算符“<<”是双目运算符。其功能把“<< ”左边的运算数的各二进位全部左移若干位,由“<<”右边的数指定移动的位数,高位丢弃,低位补0。
右移运算符“>>”是双目运算符。其功能是把“>> ”左边的运算数的各二进位全部右移若干位,“>>”右边的数指定移动的位数。但注意:对于有符号数,在右移时,符号位将随同移动。当为正数时, 最高位补0;而为负数时,符号位为1,最高位是补0 或是补1 取决于编译系统的规定。Turbo C 和很多系统规定为补1。
18、。左移和右移的位数不能大于数据的长度,不能小于0。
++、--操作符
19、先看一个简单的例子:
 int i = 3;
(++i)+(++i)+(++i);
表达式的值为多少?15 吗?16 吗?18 吗?其实对于这种情况,C语言标准并没有作出规定。有点编译器计算出来为18,因为i 经过3 次自加后变为6,然后3 个6 相加得18;而有的编译器计算出来为16(比如Visual C++6.0),先计算前两个i 的和,这时候i 自加两次,2 个i 的和为10,然后再加上第三次自加的i 得16。其实这些没有必要辩论,用到哪个编译器写句代码测试就行了。但不会计算出15 的结果来的。
20、++、--作为前缀,我们知道是先自加或自减,然后再做别的运算;但是作为后缀时,到底什么时候自加、自减?后缀运算是在本计算单位计算结束之后再自加或自减。
21、 c = a+++b和下面哪个表达式想当:
A、 a++ +b    B、 a+ ++b
C 语言有这样一个规则:每一个符号应该包含尽可能多的字符。也就是说,编译器将程序分解成符号的方法是,从左到右一个一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符,判断已经读入的两个字符组成的字符串是否可能是一个符号的组成部分;如果可能,继续读入下一个字符,重复上述判断,直到读入的字符组成的字符串已不再可能组成一个有意义的符号。这个处理的策略被称为“贪心法”。需要注意到是,除了字符串与字符常量,符号的中间不能嵌有空白(空格、制表符、换行符等)。比如:==是单个符号,而= =是两个等号。按照这个规则可能很轻松的判断a+++b 表达式与a++ +b 一致。
22、A、B是否正确?
#define BSC //
#define BMC
A、BSC my single-line comment
B、BMC my multi-line comment EMC
A、B都是错误的,为什么呢?因为注释先于预处理指令被处理,当这两行被展开成//…或时,注释已处理完毕,此时再出现//…或自然错误.因此,试图用宏开始或结束一段注释是不行的。
23、关键字sizeof 求值是在编译的时候,所以它可以被当作常量表达式使用(最新的C99标准规定sizeof也可以在运行时刻进行计算)。
int a[5]; sizeof(a[5])的值在32位系统下为4。虽然并不存在a[5]这个元素,但是这里也并没有去真正访问a[5],而是仅仅根据数组元素的类型来确定其值。所以这里使用a[5]并不会出错。
再看下面的代码:
sizeof(a)=20; 
sizeof(&a)=4;
数组的sizeof值等于数组所占用的内存字节数;数组名a作为右值时,代表数组首元素的地址,而非数组的首地址。
25、字节对齐的细节和编译器实现相关,但一般而言,满足三个准则:
1) 结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
2) 结构体每个成员相对于结构体首地址的偏移量(offset)都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节(internal adding);
3) 结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节(trailing padding)。
为什么需要字节对齐。计算机组成原理教导我们这样有助于加快计算机的取数速度,否则就得多花指令周期了。为此,编译器默认会对结构体进行处理(实际上其它地方的数据变量也是如此),让宽度为2的基本数据类型(short等)都位于能被2整除的地址上,让宽度为4的基本数据类型(int等)都位于能被4整除的地址上,以此类推。这样,两个数中间就可能需要加入填充字节,所以整个结构体的sizeof值就增长了。
26、左值:在这个上下文环境中,编译器认为x 的含义是x 所代表的地址。这个地址只有 编译器知道,在编译的时候确定,编译器在一个特定的区域保存这个地址,我们完全不必考虑这个地址保存在哪里。
右值:在这个上下文环境中,编译器认为y 的含义是y 所代表的地址里面的内容。这个内容是什么,只有到运行时才知道。
C 语言引入一个术语-----“可修改的左值”。意思就是,出现在赋值符左边的符号所代表的地址上的内容一定是可以被修改的。换句话说,就是我们只能给非只读变量赋值。
27、C 语言中,当一维数组作为函数参数的时候,编译器总是把它解析成一个指向其首元素首地址的指针。函数本身是没有类型的,只有函数的返回值才有类型。
28、静态区:保存自动全局变量和static 变量(包括static 全局和局部变量)。静态区的内容在总个程序的生命周期内都存在,由编译器在编译的时候分配。
栈:保存局部变量。栈上的内容只在函数的范围内存在,当函数运行结束,这些内容也会自动被销毁。其特点是效率高,但空间大小有限。
堆:由malloc 系列函数或new 操作符分配的内存。其生命周期由free 或delete 决定。在没有释放之前一直存在,直到程序结束。其特点是使用灵活,空间比较大,但容易出错。
29、不管什么时候,我们使用指针之前一定要确保指针是有效的。
30、内存越界:这种错误经常是由于操作数组或指针时出现“多1”或“少1”
31、释放完块内存之后,没有把指针置NULL,这个指针就成为了“野指针”,也有书叫“悬垂指针”。这是很危险的,而且也是经常出错的地方。所以一定要记住一条:free 完之后,一定要给指针置NULL。
编码风格
32、每一个函数都必须有注释,即使函数短到可能只有几行。
每个函数定义结束之后以及每个文件结束之后都要加一个或若干个空行。
在一个函数体内,变量定义与函数语句之间要加空行。
逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。
复杂的函数中,在分支语句,循环语句结束之后需要适当的注释,方便区分各分支或循环体。
修改别人代码的时候不要轻易删除别人的代码,应该用适当的注释方式。
用缩行显示程序结构,使排版整齐,缩进量统一使用4个空格字符(不使用TAB
缩进)。
在函数体的开始、结构/联合的定义、枚举的定义以及循环、判断等语句中的代码都要采用缩行。
代码行最大长度宜控制在80 个字符以内,较长的语句、表达式等要分成多行书写。
如果代码行中的运算符比较多,用括号确定表达式的操作顺序,避免使用默认的优先级。
尽量避免含有否定运算的条件表达式。
函数设计的一般原则和技巧
33、原则上尽量少使用全局变量,因为全局变量的生命周期太长,容易出错,也会长时间占用空间.各个源文件负责本身文件的全局变量,同时提供一对对外函数,方便其它函数使用该函数来访问变量。
如果参数是指针,且仅作输入参数用,则应在类型前加const,以防止该指针在函数体内被意外修改。
return 语句不可返回指向“栈内存”的“指针”,因为该内存在函数体结束时被自动销毁。
34、函数名与返回值类型在语义上不可冲突。
违反这条规则的典型代表就是C语言标准库函数getchar。几乎没有一部名著没有提到getchar函数,因为它实在太经典,太容易让人犯错误了。所以,每一个有经验的作者都会拿这个例子来警示他的读者,我这里也是如此:
char c;
c = getchar();
if(EOF == c)
{
}
按照getchar 名字的意思,应该将变量c 定义为char 类型。但是很不幸,getchar 函数的返回值却是int 类型,其原型为:int getchar(void);
由于c 是char 类型的,取值范围是[-128,127],如果宏EOF 的值在char 的取值范围之外,EOF 的值将无法全部保存到c 内,会发生截断,将EOF 值的低8 位保存到c 里。这样if 语句有可能总是失败。这种潜在的危险,如果不是犯过一次错,肯怕很难发现。
35、在函数体的“入口处”,对参数的有效性进行检查。尤其是指针参数,尽量使用assert宏做入口校验,而不使用if语句校验。
36、函数调用的开销比循环来说要大得多,所以,递归的效率很低,递归的深度太大甚至可能出现错误(比如栈溢出)。所以,平时写代码,不到万不得已,尽量不要用递归。即便是要用递归,也要注意递归的层次不要太深,防止出现栈溢出的错误;同时递归的停止条件一定要正确,否则,递归可能没完没了。
37、采用小写字母命名文件,避免使用一些比较通俗的文件名,如:public.c 等。
永不止步步 发表于02-09 09:23 浏览65535次
分享到:

已有0条评论

暂时还没有回复哟,快来抢沙发吧

添加一条新评论

只有登录用户才能评论,请先登录注册哦!

话题作者

永不止步步
金币:67410个|学分:308117个
立即注册
畅学电子网,带你进入电子开发学习世界
专业电子工程技术学习交流社区,加入畅学一起充电加油吧!

x

畅学电子网订阅号