c++新特性实验(1)预处理

1.参考资料

1.1 C++

1.2 gcc对C++的支持

  • gcc官网:         

    https://gcc.gnu.org/

  • gcc 各版本对c++新特性的支持情况 : 

    https://gcc.gnu.org/projects/cxx-status.html

2.__has_include(C++17 起)

2.1 作用

    预编译函数. 结果仅表明指定名称的头或源文件 是否存在,并不是已经include过了的意思。 

2.2 语法

  __has_include ( " 文件名 " )
  __has_include ( < 文件名 > )(C++17 起)

2.3 示例

    has_inclue.cpp

 1 //#include "has_include.h"
 2 
 3 #if __has_include("has_include.h")
 4     #define NUM 1
 5 #else
 6     #define NUM 0   
 7 #endif
 8 
 9 #include <stdio.h>
10 #include <stdlib.h>
11 
12 
13 int main(int arg,char *argv[])
14 {
15     printf("num = %d\n",NUM);
16     return 0;
17 }
  • 第1行,只要在相应的目录中存在has_include.h文件,就返回true,与第1行的注释打开没有关系.删掉has_include.h后,NUM才是0.
  • 程序在编译完成之后结果就已经确定,运行时动态删除,增加其参数中指定的文件,不会改变结果.

3.__has_cpp_attribute(C++20 起)

3.1 作用

检测标准属性是否存在,检测不了厂商自定义的属性.

attribute-token属性标准
assert[[assert]]201806L(C++20)
carries_dependency[[carries_dependency]]200809L(C++11)
deprecated[[deprecated]]201309L(C++14)
ensures[[ensures]]201806L(C++20)
expects[[expects]]201806L(C++20)
fallthrough[[fallthrough]]201603L(C++17)
likely[[likely]]201803L(C++20)
maybe_unused[[maybe_unused]]201603L(C++17)
no_unique_address[[no_unique_address]]201803L(C++20)
nodiscard[[nodiscard]]201603L(C++17)
noreturn[[noreturn]]200809L(C++11)
unlikely[[unlikely]]201803L(C++20)

3.2 示例

1 #if __has_cpp_attribute                        // 检查 __has_cpp_attribute 是否存在
2 #  if __has_cpp_attribute(deprecated)           // 检查一个属性
3 #    define DEPRECATED(msg) [[deprecated(msg)]]
4 #  endif
5 #endif
6 #ifndef DEPRECATED
7 #    define DEPRECATED(msg)
8 #endif 

 

4.#line 增大最大行号值(C++11 )

4.1 作用

  更改预处理器中的当前行号和文件名。

4.2 语法

#line 行号
或者 #line 行号 "文件名"
  • 行号必须是至少有一个十进制位的序列,并且始终按十进制解释(即使它以 0 开始也是如此)。
  • 行号 为 0 或大于 32767 (C++11 前).则行为未定义,C++11 起最大改为 2147483647 .

4.3 示例

1 #include <cassert>
2 
3 #define FILENAME "line.cpp"
4 
5 int main(int argc, char *argv[]) {
6    #line 167 FILENAME
7     assert(1 + 3 == 5);
8   printf("file = %s,line = %d\n",__FILE__,__LINE__);
9 return 0; 10}

5.#define 可变参数(C++11)

5.1 语法

#define 标识符( 形参, ... ) 替换列表(可选)    (3)    (C++11 起)
#define 标识符( ... )      替换列表(可选)    (4)    (C++11 起)
  • 实参数量必须多于 (C++20 前)形参数量.C++20起改为不少于 .
  • 替换列表 可以含有记号序列“__VA_OPT__ ( 内容 )”,若 __VA_ARGS__ 非空,则它会被 内容 替换,否则不展开成任何内容。

5.2 作用

  • #define 指令的版本 (3) 定义有可变数量实参的仿函数宏。额外的实参可用 __VA_ARGS__ 标识符访问,它会被与要被替换的标识符一起提供的实参替换。
  • #define 指令的版本 (4) 定义有可变数量实参的仿函数宏,但无常规实参。额外的实参只能用 __VA_ARGS__ 标识符访问,它会被与要被替换的标识符一起提供的实参替换。

5.3 示例 

 1 #include <cstdlib>
 2 #include <cstdio>
 3 #include <cstdarg>
 4 
 5 void 
 6 fun(int count,...){
 7     va_list va;
 8 
 9     va_start(va,count);
10 
11     for(int i = 0 ; i < count; ++i){
12         int arg = va_arg(va,int);
13         printf("arg[%d] = %d , ",i,arg);
14     }
15 
16     va_end(va);
17 }
18 
19 #define TYPE int
20 #define F(...) fun(0 __VA_OPT__(,) __VA_ARGS__)
21 #define G(X, ...) fun(0, X __VA_OPT__(,) __VA_ARGS__)
22 #define SDEF(sname, ...) TYPE sname __VA_OPT__([]= { __VA_ARGS__ })
23 
24 
25 int main(int argc,char *argv[]){
26     int a = 10,b = 11,c = 12;
27    
28     F(a, b, c);             // 替换为 fun(0, a, b, c)
29     F();                    // 替换为 fun(0)
30     G(a, b, c);             // 替换为 fun(0, a, b, c)
31     G(a, );                 // 替换为 fun(0, a)
32     G(a);                   // 替换为 fun(0, a)
33     SDEF(foo);              // 替换为 int foo;
34     SDEF(bar, 1, 2,3,4);    // 替换为 int bar[] = { 1,2,3,4 };
35 
36     return 0;
37 }

 

 1 #include <valarray>
 2 #include <cstdio>
 3 #include <cstdlib>
 4 
 5 
 6 int main(int argc,char *argv[]){
 7 
 8     #define showlist(...) puts(#__VA_ARGS__)
 9 
10     showlist();            // 展开成 puts("")
11     showlist(1, "x", int); // 展开成 puts("1, \"x\", int")
12 
13     return 0;
14 }

 

1 #define LOGE(TAG,...) __android_log_print(ANDROID_LOG_ERROR,TAG,__VA_ARGS__)
2 
3 #define LOGW(TAG,...) __android_log_print(ANDROID_LOG_WARN,TAG,__VA_ARGS__)
4 
5 #define LOGD(TAG,...) __android_log_print(ANDROID_LOG_DEBUG,TAG,__VA_ARGS__)
6 
7 #define LOGI(TAG,...) __android_log_print(ANDROID_LOG_INFO,TAG,__VA_ARGS__) 

 

6.新增预定义宏(C++11)

6.1 新增列表

__STDC_HOSTED__  
C++11
若实现有宿主(运行在 OS 下)则展开成整数常量 1,不随 OS 运行则展开成 0​
__STDCPP_DEFAULT_NEW_ALIGNMENT__  
C++17

展开成 std::size_t 字面量,其值为对不具对齐的 operator new 的调用所保证的对齐

(较大的对齐将传递给具对齐重载,如 operator new(std::size_tstd::align_val_t)) 

__STDC_VERSION__C++11若存在则为实现定义值
__STDC_ISO_10646__C++11若 wchar_t 使用 Unicode ,则展开成 yyyymmL 形式的整数常量,日期指示所支持的 Unicode 的最近版本
__STDC_MB_MIGHT_NEQ_WC__C++11

若对于基本字符集成员 'x' == L'x' 可能为假,则展开成 1,如在基于 EBCDIC 并且

为 wchar_t 的系统上。 

__STDCPP_STRICT_POINTER_SAFETY__C++11若实现支持严格的 std::pointer_safety 则展开成 1
__STDCPP_THREADS__C++11若程序能拥有多于一个执行线程则展开成 1

 6.2 示例

 1 #include <cstdio>
 2 #include <cstdlib>
 3 #include <thread>
 4 
 5 
 6 int main(int argc,char *argv[]){
 7 
 8     printf("__STDC_HOSTED__   = %s\n",__STDC_HOSTED__  );
 9     printf("__STDCPP_DEFAULT_NEW_ALIGNMENT__   = %s\n",__STDCPP_DEFAULT_NEW_ALIGNMENT__  );
10     printf("__STDC_VERSION__   = %s\n",__STDC_VERSION__  );
11     printf("__STDC_ISO_10646__   = %s\n",__STDC_ISO_10646__  );
12     printf("__STDC_MB_MIGHT_NEQ_WC__   = %s\n",__STDC_MB_MIGHT_NEQ_WC__);
13     printf("__STDCPP_STRICT_POINTER_SAFETY__   = %s\n",__STDCPP_STRICT_POINTER_SAFETY__);
14     printf("__STDCPP_THREADS__   = %s\n",__STDCPP_THREADS__);
15 
16     return 0;
17 }

7.__func__(C++11)

7.1 特殊的函数局域预定义变量

  C++11 起,在每个函数体的作用域内部,都有一个名为 __func__ 的特殊的函数局域预定义变量,定义为一个持有具有实现定义格式的函数名的静态字符数组。

  它不是预处理器宏.

7.2 示例

 1 int main(int argc,char *argv[]){
 2 
 3     printf("__DATE__ = %s \n ",__DATE__);
 4     printf("__TIME__ = %s \n ",__TIME__);
 5     printf("__FILE__ = %s \n ",__FILE__);
 6     printf("__LINE__ = %d \n ",__LINE__);
 7     printf("__FUNCTION__ = %s \n ",__FUNCTION__);
 8     printf("__func__ = %s \n ",__func__);
 9 
10     return 0;
11 }

 

转载于:https://www.cnblogs.com/sjjg/p/10556015.html

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
预处理是编译器的第一个阶段,主要完成对源程序的预处理工作,包括宏定义和条件编译等。在C语言中,预处理指令都以#开头,例如#define、#include等。 下面是一个简单的C程序示例: ``` #include <stdio.h> #define PI 3.1415926 int main() { double r, s; scanf("%lf", &r); s = PI * r * r; printf("The area of the circle is: %lf\n", s); return 0; } ``` 预处理阶段主要完成以下工作: 1. 将#include指令替换为指定的头文件内容。 在上面的示例中,#include <stdio.h>指令会将stdio.h头文件的内容插入到程序中。 2. 将#define指令替换为指定的宏定义内容。 在上面的示例中,#define PI 3.1415926指令会将程序中所有出现的PI替换为3.1415926。 3. 处理条件编译指令,例如#if、#else、#elif和#endif等。 条件编译指令可以根据不同的条件编译不同的代码段,例如: ``` #define DEBUG ... #ifdef DEBUG printf("Debugging information\n"); #endif ``` 在上面的示例中,如果定义了DEBUG宏,则会输出“Debugging information”,否则不会输出。 4. 处理其他预处理指令,例如#error、#pragma等。 #error指令可以在编译时输出错误信息,例如: ``` #if MAX_SIZE > 1024 #error MAX_SIZE is too large #endif ``` 在上面的示例中,如果MAX_SIZE大于1024,则会输出“MAX_SIZE is too large”。 #pragma指令可以用来控制编译器的行为,例如: ``` #pragma pack(1) struct { char c; int i; } s; ``` 在上面的示例中,#pragma pack(1)指令会将结构体s的对齐方式设置为1字节,而不是默认的4字节。 预处理阶段的输出结果是一个新的C源程序,其中所有的预处理指令都已经被处理完毕。编译器的下一个阶段是编译阶段,将源程序翻译成目标代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值