写在前面
总结一下罗剑锋老师的C++实战课程中C++代码预处理, 编译, 汇编, 链接的一些内容.
预处理阶段
预处理阶段编程的操作目的是”源码”, 用各种指令控制预处理器, 把源码改造成另一种形式.
基本格式
- 以
#
开头, 可以忽略缩进层次, 总是顶格书写. - 单独的
#
称为空指令, 可当做特别的预处理空行. - 不可以调试, 可通过
g++ -E
选项输出预处理结果.
下面是一个基本的格式
#
#if __darwin__
# define IS_MAC 1
#endif
#
防卫式声明(Include-Guard)
#ifndef __XX__
#define __XX__
#include <map>
class C{};
... // 包含头文件或者类的声明
#endif // __XX__
宏定义(MACROS)
- 使用宏一定要谨慎, 以简化代码, 清晰易懂为目标, 不要滥用, 避免代码可读性降低.
- 宏是全局生效的, 在不使用之后, 一定要用
#undef <宏名>
取消定义.(可以在取消前先#ifdef
检查)
例子:(用宏代替命名空间)
#define BEGIN_NAMESPACE(x) namespace x {
#define END_NAMESPACE(x) }
BEGIN_NAMESPACE(my_own)
// func...
END_NAMESPACE(my_own)
这样比较清晰明了, 不会因为大括号过多引起匹配错误.
条件编译(与C语言混合编程)
- 使用c++编译器的
__cplusplus
宏, 如果定义了就采用C++编译器, 否则用extern "C"
.
#ifndef __cplusplus
extern "C" {
#endif
#include <stdio.h>
void c_func() {
char a[10];
sprintf(a, "%d", 10);
printf("%s\n", a);
printf("%d\n", atoi("12"));
}
#ifndef __cplusplus
}
#endif
查看编译器提供的更多宏, 使用
g++ -E -dM - < /dev/null
条件编译(块注释的优雅写法)
#if 0
...//这里面的所有内容都不会启用
#endif
#if 1
...//启用代码, 强调代码的必要性
#endif
确实比/* */
要好很多, 使用/**/
总是会重复包含.
编译,汇编和链接阶段
属性(attribute, C++11/14)
语法: [[属性名]]
[[noreturn]]
加在函数返回值之前, 不会返回任何值.[[deprecated("info")]]
, 声明废弃函数[[gnu::unused]]
, 表示虽然现在不用, 但是不要被优化掉.
静态断言(static_assert)
与assert
对比, assert
是一个宏, 但是在预处理阶段不生效, 在运行阶段才生效, 称为动态断言.
静态断言可以在编译时生效.
例子1:
编译阶段判断long类型的大小, 以此判断运行机器的位数(只在64位才为8字节)
static_assert(sizeof(long)>=8 "must run on 64bit machine");
但是对于指针判断是否为空指针不行, 因为静态断言只在编译期生效.