C++实战笔记(一)c++的预处理与编译

 
Category: C_C++

写在前面

总结一下罗剑锋老师的C++实战课程中C++代码预处理, 编译, 汇编, 链接的一些内容.

预处理阶段

预处理阶段编程的操作目的是”源码”, 用各种指令控制预处理器, 把源码改造成另一种形式.

基本格式

  • #开头, 可以忽略缩进层次, 总是顶格书写.
  • 单独的#称为空指令, 可当做特别的预处理空行.
  • 不可以调试, 可通过g++ -E选项输出预处理结果.

下面是一个基本的格式

# 
#if __darwin__
#    define IS_MAC 1
#endif
#

防卫式声明(Include-Guard)

#ifndef __XX__
#define __XX__
    #include <map>
    class C{};
	... // 包含头文件或者类的声明
#endif // __XX__

宏定义(MACROS)

  1. 使用宏一定要谨慎, 以简化代码, 清晰易懂为目标, 不要滥用, 避免代码可读性降低.
  2. 宏是全局生效的, 在不使用之后, 一定要用#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)

语法: [[属性名]]

  1. [[noreturn]]加在函数返回值之前, 不会返回任何值.
  2. [[deprecated("info")]], 声明废弃函数
  3. [[gnu::unused]], 表示虽然现在不用, 但是不要被优化掉.

静态断言(static_assert)

assert对比, assert是一个宏, 但是在预处理阶段不生效, 在运行阶段才生效, 称为动态断言.

静态断言可以在编译时生效.

例子1:

编译阶段判断long类型的大小, 以此判断运行机器的位数(只在64位才为8字节)

static_assert(sizeof(long)>=8 "must run on 64bit machine");

但是对于指针判断是否为空指针不行, 因为静态断言只在编译期生效.