INFO:
版次 |
2009年2月第1版 |
ISBN |
9787302192114 |
笔记目录与书籍略有不同
-
控制结构
-
错误判断(goto)
- 错误时,goto至错误处理语句(析构),释放未完成资源
-
释放顺序与构建顺序相反
E.g.: 1.文件不存在,goto err1
2.文件打开失败,goto err2
3.所需资源分配失败,goto err3
4.內容读取失败,goto err4
…
扫描二维码关注公众号,回复: 9936487 查看本文章err4: free 3分配的资源
err3: 关闭2打开的文件
err2:
err1:
返回错误码
-
短路计算
可使用位运算代替乘法和除法中的2倍数,尽量避免使用除法。
-
循环语句
-
条件中避免使用函数,避免造成函数的重复调用
(见三-1-2)
-
循环条件中的变量可使用局部变量(尽量避免使用全局变量作为循环条件)
(见二-5-5)
-
-
条件判断
- switch有一个跳转表,执行速度很快,但跳转表会占用更多空间
-
控制结构的优化
- 各控制结构之间可互相替换(基本符号等除外)
- switch以空间换实间。
- If…else…连接判断应以概率大的选项为首,依次排序。
- 尽量使用+=、-=、++、--代替=,可节省时间和内存。(相对复杂的语句的除外)
-
避免将循环算子使用率高的变量设置为全局/静态变量,编译器会自动优化至寄存器,全局/静态变量是在内存中。
(同三-5-2)
-
-
函数
-
函数的本质
-
函数名是一种指针,局部变量存储于栈中,用后即毁。
(见四-3)
-
访问函数时会经过:函数入栈→保存寄存器值→保存返回地址→跳转。
(见三-5-1)
-
-
声明与定义
-
声明:仅告知编译器变量的存在,不分配存储空间。
E.g.:int a;
-
定义:告知编译器变量的存在,且分配存储空间。赋值不是定义
E.g.:int a=3;//此为定义
-
当变量的作用域内只有声明而没有定义时,编译器会自动将第一个声明默认为变量的定义。
E.g.:int a;//此为声明,因没有定义,默认为定义
a=3;//这里仅仅是赋值
-
多文件的符号的声明与定义规则:
- 不允许对同一个符号(变量或函数)有多个定义。
- 如果该符号有一个定义和多个声明,则编译器选择被定义的符号。
- 如果该符号有多个声明,则从其中任选一个作为符号的定义。
-
-
全局变量
-
全局变量(函数外定义),存储与数据段,作用于从该行开始至整个程序的结束。
(见三-)
E.g.:int add(int a, int c)
{ return a + b + c) }//b暂未声明,引发错误
int b=10; //b的作用域为从声明开始至程序结束。
int mule(int a)
{ return a * b) }//此处的b为全局变量b
…
//END-----------
- 初始值为0,自动初始化。
-
static声明的全局变量,作用域将改变为本文件。
(见三-4-1)
-
-
局部变量
- 作用于函数/复合语句内,存储于堆栈内,用后即毁。
- 不会自动初始化,数值随机。
-
static声明的局部变量,作用域不变,不再销毁。
(见三-4-2)
-
static的使用
- static声明的全局变量生命周期不变,作用域缩小至本文件。
- static声明的局部变量作用于不变,生命周期为整个程序执行期间。
-
static声明的函数作用域为本文件,以实现封装和模块化。
(见三-6-4)
-
优化
- 因函数是以时间交换空间,应尽量减少函数的调用。
-
调用频率很高的局部变量会存储与寄存器内,但全局变量依旧存储于数据段,故应将高频率使用(如循环算子)设置为局部变量。
(同二-5-5)
- register类型:寄存器变量,被声明的变量优先分配至寄存器。
-
多文件程序
- auto类型:自动类型,由编译器自动决定存储位置和性质。
- extern类型:外部变量,函数默认为extern类型,使其可以被外部文件调用。
-
使用static改变函数和变量的生命周期。
(见三-4)
- C语言中一个文件就是一个模块。
- static声明的变量和函数无法被外部文件访问,增加文件的安全性,以此实现封装和模块化,使文件隐藏细节,只对外暴露接口。⚠修改模块时,尽量不要修改接口。
-
链接规则
-
符号的声明与定义
(见三-2-4)
-
-
可变参数
-
引用:stdarg.h
va_list parameter;//接收到的包含所有参数的参数列表
va_start(va_list,first parameter)//定位至第一个参数
va_arg(va_list, type)//得到下一个参数
va_end(va_list)//参数处理结束
-
-
-
指针
- sizeof,屏蔽细节,使其具有更好的移植性
-
指针
-
指针别名
(见四-)
- type (*p) [size]:p为数组的指针,应用整个数组;p++表示指针跳过整个数组,获取数组内容需转换为(type *)类型。
- 函数的参数为原值的副本,修改变量需传递变量的指针,修改指针需传递指针的指针…
- void *:指针本质为无符号整型,但C语言不允许不同类型的指针比较。可用void *代替任意类型的指针(未说明该指针指向的数据占用空间的大小),void*类型可以参与运算,但无法调用其指向的数据。
-
-
函数指针
- type (*p) (parameterlist):常被用于回调函数,type常被定义为void*类型,void*被当作泛型,损失效率
- 函数指针数组 type (*p[size])(parameterlist)
-
const
- const修饰的变量不可改变,必须在定义的时候初始化。
-
const type *p=&a,可更改 p的指向,不可通过指针修改內容。
int * const p=&a,不可更改p的指向,可通过指针修改內容。
const type * const p=&a,不可修改p的指向,不可通过指针修改内容。
-
修饰函数的传参和返回值,优化编译。
(见)
-
优点
- 指明该变量不可更改
- 便于调试。编译器会发现const修改无效,否则只会显示内存访问错误。
- 宏替换是数据,会造成多次数据拷贝;而const只是一个地址,只有一份数据。
- const定义的常量通常不分配存储空间,而是存放在符号表中,即没有读取内存的操作,提高程序效率。
-
其他
- C中的const修饰全局变量作用域已就位global,而C++中const修饰的全局变量作用域为local,需使用extern将作用域变为global。其他区别略。
-
define 在预处理阶段展开,没有类型检查,展开时并不会分配内存。
const在编译运行阶段使用,编译阶段进行类型检查,一般会在内存中分配一份数据。
- 非const变量传递给const变量会将变量限制为制度类型,相反却会警告或者报错。
-
C语言高级命令
-
结构体
- 为了提高速度,结构体使用了内存对齐,以空间换取时间,合理改变结构体的顺序可以优化存储空间,但嵌入式等系统中为了节省存储空间一般会禁用对其机制。
-
位运算
- 掩码运算:获取所需字节/标志位信息。为了节约存储空间,通常将多个标识符存储于一个字节中。
-
预处理
-
本模块函数使用static(功能类似于private)
(见三-3-5)
- 头文件内容:
-
-
头文件区 |
其他头文件 |
全局宏区 |
共定义(如调试开关、缓冲区大小等) |
全局变量区 |
非static函数/变量的声明 |
函数接口区 |
所有模块的函数接口 |
comon.h |
结构体,声明函数接口,全局变量 |
list.c |
函数内容,所有函数均为接口 |
main.c |
main函数,包含common.h头文件即可 |
-
调试开关
使用条件编译,使代码调试段不再编译,不必依次注释调试语句
#define DEBUG 1//调试开关
#ifdef DEBUG//调试语句
#define PRINT(str) printf(str);
#define PRINT!(str, arg); printf(str, arg);
#else//若调试开关关闭,则调试语句无效
#define PRINT(str);
#define PRINT!(str, arg);
#endif
-
inline :将函数在编译时直接展开到代码段,执行函数不再跳转,提高执行效率。类似于宏(不是宏)。
只有短的,少量的函数会被展开,是否展开由编译器决定。对递归无效。
- restrict:确定指针参数的唯一性。用于优化编译。
.
,