C安全编码--预处理
建议和规则
建议:
用内联函数或静态函数代替与函数相似的宏
在宏参数名两边加上括号
宏替换列表应该加上括号
应该使用typedef定义编码类型
不要复用标准头文件名
理解连接标记或执行字符串化时的宏替换
把头文件放在包含防护条件中
避免使用连续的问号
保证头文件名唯一
不要用不安全的函数替换安全函数
在一个do-while循环中包装多条语句的宏
规则:
不要通过连接创建统一字符名称
不要在不安全宏的参数中包含赋值、增值、减值、volatile访问或函数调用
用内联函数或静态函数代替与函数相似的宏
宏是危险的,用法与真正的函数相似,但是具有不同的语义。C99在C中增加了内联函数,当内联函数和宏可以互换使用时,应该优先选择内联函数,内联替换并不是文本替换,也没有创建函数,决定一个函数是否为内联函数是一个底层的优化细节,编译器应该不依赖程序换做出这个决定,是否使用内联函数取决于目标编译器对它们的支持,它们对系统性能特征所产生的影响以及可移植性问题,静态函数常常具有与内联函数相同的优点。
下面的例子中,当传递给CUBE宏的参数是一个具有副作用的表达式时,这个宏就具有未定义的行为。
代码1:
#define CUBE(x) ((x) * (x) * (x))
/*...*/
int i = 2;
int a = 81 / CUBE(++i);
在这个例子中,a的初始化表达式展开为: int a = 81/((++i) * (++i) * (++i));
解决方案:
inline int cube(int x)
{
return x * x *x;
}
/*...*/
int i = 2;
int a = 81 / cube(++i);
代码2:
#include
size_t count = 0;
#define EXEC_BUMP(func) (func(), ++count)
void g(void) {
printf("Called g, count = %zu.\n", count);
}
void aFunc(void) {
size_t count = 0;
while(count++ <10) {
EXEC_BUMP(g);
}
}
int main(void){
aFunc();
return 0;
}
运行结果:

解决方案:
#include
size_t count = 0;
void g(void) {
printf("Called g, count = %zu.\n", count);
}
typedef void(*exec_func)(void);
inline void exec_bump(exec_func f) {
f();
++count;
}
void aFunc(void) {
size_t count = 0;
while(count++ <10) {
exec_bump(g);
}
}
int main(void){
aFunc();
return 0;
}