C++之预编译

预编译/预定义/指令

  • _Pragma:#pragma是一条预处理指令,用来向编译器传达语言标准以外的信息,_Pragma是与#pragma功能一样的操作符:格式如下:_Pragma (字符串字面常量),如_Pragma("once"),相比#pragma,由于_Pragma是一个操作符,因此可以用在一些宏中,而#pragma则不能在宏中展开——待验证,
  • 预处理的指令还有哪些?
    • #pragma once:只编译一次
    • #pragma pack():用来指定结构体对齐,括号中指定对其字节数
      • #pragma pack(show):显示当前内存对齐的字节数,vs不会打印出来,但会在编译阶段给出一个警告,说明当前对齐字节数
      • #pragma pack(push [, identifier] [, n]):将当前对齐字节数压入栈顶,并设置n为新的对齐字节数;
      • #pragma pack(pop [, identifier] [, n]): #pragma pack(pop)会弹出栈顶对齐字节数,并设置默认对齐字节
    • #pragma message:用于自定义编译信息输出到终端,一般和#if配合使用,用在控制版本号;在vs下编译时需要用()将message括起来
    • #pragma warning:警告设置,只对当前文件有效
      • #pragma warning(push):存储当前报警设置
      • #pragma warning(push, n):存储当前报警设置,并设置报警级别为n,n为从1到4的自然数
      • #pragma warning(pop):恢复之前压入堆栈的报警设置,在一对push和pop之间的任何设置都将在后面失效
      • #pragma warning(disable: n):将某个报警设置为失效——n有哪些?
      • #pragma warning(default: n):将报警设置为默认
    • vs中的代码收缩:
#pragma region Region_1
void Test() {}
#pragma endregion Region_1

  • 变长参数的宏定义以及__VA_ARGS__:C99中可以使用变长参数的宏定义。变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号,而预定义宏__VA_ARGS__则可以在宏定义的实现部分替换省略号所代表的字符串;除了在宏定义中用来展开参数而外还有其他用途吗
  • 可变长参数函数:#include<stdarg.h> (#include<cstdarg>)
    • 宏:
      • va_list
      • va_start
      • va_arg
      • va_end
    • 实现原理:得益于C语言默认的cdecl调用惯例的自右向左压栈传递方式;cdecl调用惯例保证了参数的正确清除,有些调用惯例是由被调用方负责清除堆栈的参数,但被调用方不知道有多少参数被传递进来,所以没办法清除,cdecl是调用方负责清除堆栈,因此没有这个问题
    • 变长参数函数:函数原型里的...符号前一般都需要一个先导参数,用于标识可变参数的起始位置,并且一般都是用来标识参数个数时使用的
      #define PR(...) printf(__VA_ARGS__)    // 给PR传个参数就能打印出来了
    •  宏__cplusplus:在C++11标准中的值与之前不同,可以通过该值判断是否支持C++11:
      • C++03标准中,__cplusplus值为199711L 
      • C++11标准中,__cplusplus值为201103L
#if __cplusplus>=201103L
...
#endif

预处理常量表达式

如果要查询某个头文件是否存在,使用__has_include("filename")或__has_include(<filename>)预处理器常量表达式。如果头文件存在则返回1,否则返回0。例子:

#if __has_include(<optional>)
  #include<optional>
#elif __has_include(<experimental/optional>)
  #include<experimental/optional>
#endif

断言 

  •  预处理指令:#error“...”, 当预处理器遇到该指令时停止编译并将后面的自定义错误消息输出,通过和预处理指令#if配合,在预处理阶段进行断言。
  • 运行时断言assert(expression):expression为false触发;如果要禁用assert宏,在包含头文件之前#define NDEBUG。会极大影响性能。 (头文件:#include<cassert>或#include<assert.h>)
  • 编译期断言static_assert:语法:static_assert(断言表达式,字符串提警告信息),通常需要返回一个bool值
    • static_assert断言表达式的结果必须是在编译时期可以计算的表达式,即常量表达式
      • 编译期断言的一个简单实现:
        #define STATIC_CHECK(expr) { char unnamed[(expr) ? 1: 0]; }
      • 上面这个解法无法表达正确的错误信息,一个较好的解法是依赖一个名称带有意义的template,如下:
      • 可以说,assert,#error,static_assert三者分工明确,都是在不同时期的断言,根据需要而定;不过感觉大部分情况下可以用static_assert代替#error
      • 可以用在模板中在模板具现化时判断类别信息等
template<bool> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
#define STATIC_CHECK(expr) (CompileTimeError<(expr) != 0>())

 预定义宏

  1. __LINE__:当前源代码行号;
  2. __FILE__:当前源文件名,字符串;
  3. __DATE__:当前 编译日期,格式(内容)为月 日 年
  4. __TIME__:当前 编译时间,格式为hh:mm:ss;
  5. __cplusplus:当编写C++程序时该标识符被定义,可以用来区别c++11(>201103L)。
  6. __func__:当前所在函数名;
    1. 原理是,按照标准定义,编译器会隐式地在函数的定义之后定义__func__标识符
    2. C++11标准允许在类或者结构体中使用;但不允许将__func__作为默认函数值,因为__func__还未被定义
    3. 在类函数中只会显示当前的函数名,而不会显示类名等修饰的部分
  7. __STC__:当要求程序严格遵循ANSI C标准时该标识被赋值为1;——暂时不知道有什么用
// 例1:编译器隐式定义
void hello() 
{    
    static const char *__func__ = "hello";
    // ...
}
// 例2:在自定义类型中使用
struct TestStruct
{
       TestStruct() : name(__func__) {}
       const char* name;
};

其他 

  •  #和##:这两个都是处理变量名而不是变量值
    • #:将宏定义中的变量名转化为字符串,具体为把#后的形参转化成一个字符串,注意不是值,只能修饰带参数的宏的形参
    • ##:变量名称的字符串连接,注意不是值的连接,但拼接完之后不是字符串,是一个名称,中间可以有空格存在,如:
      define AAA(aaa) int symbol ## aaa = 10;

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值