(一)预定义宏、__func__、_Pragma、变长参数宏定义以及__VA_ARGS__
作为第一篇,首先要说一下C++11与C99的兼容性。
C++11将 对以下这些C99特性的支持 都纳入新标准中:
1) C99中的预定义宏
2) __func__预定义标识符
3) _Pragma操作符
4) 不定参数宏定义以及__VA_ARGS__
5) 宽窄字符串连接
这些特性并不像语法规则一样常用,并且有的C++编译器实现也都先于标准地将这些特性实现,因此可能大多数程序员没有发现这些不兼容。但将这些C99的特性在C++11中标准化无疑可以更广泛地保证两者的兼容性。我们来分别看一下。
这次,我们只讨论前四个,第五个后面会有具体讨论。
一、预定义宏
下面这些是C++11中与C99兼容的宏,这些宏,我个人感觉并不常用,只是做一个记录,下面那个部分可能会实用些。
宏名称 | 功能描述 |
__STDC_HOSTED__ | 如果编译器的目标系统环境中包含完整的标准C库,那么这个宏就定义为1,否则宏的值为0 |
__STDC__ | C编译器通常用这个宏的值来表示编译器的实现是否和C标准一致。C++11标准中这个宏是否定义以及定成什么值由编译器决定 |
__STDC_VERSION__ | C编译器通常用这个宏来表示所支持的C标准的版本,比如 1999mmL。C++11标准中这个宏是否定义以及定成什么值将由编译器来决定 |
__STDC_ISO_10646__ | 这个宏通常定义为一个yyyymmL格式的整数常量,例如 199712L,用来表示C++编译环境符合某个版本的ISO/IEC 10646标准 |
使用这些宏,我们可以查验机器环境对C标准和C库的支持状况,在我的VS2015上没有找到相关的宏定义,下面是本书作者的一些测试情况:
#include<iostream>
using namespace std; int main()
{
cout << "Standard Clib:" << __STDC_HOSTED__ << endl; //Standard Clib:1
cout << "Standard C:" << __STDC__ << endl; //Standard C:1
cout << "ISO/IEC " << __STDC_ISO_10646__ << endl; //ISO/IEC 200009
}
作者的试验机上也没有第三个宏定义(也符合标准规定,见上表),其余可以打印出一些常量。
预定义宏对于多目标平台代码的编写通常具有重大意义。通过以上的宏,程序员通过使用#ifdef / #endif等预处理指令,就可使得平台相关代码只在适合于当前平台的代码上编译,从而在同一套代码中完成对多平台的支持。从这个意义上讲,平台信息相关的宏越丰富,代码的多平台支持却准确。
二、__func__ 预定义标识符
很多现实的编译器都支持__func__预定义标识符功能
其功能:返回所在函数的名字。
详见代码:
#include<iostream>
using namespace std; const char* hello() { return __func__; }
const char* world() { return __func__; }
int main()
{
cout << hello() << ',' << world() << endl;
}
事实上,按照标准定义,编译器会隐式地在函数的定义之后定义__func__标识符。
例如上述例子中的hello函数等同于如下代码:
const char* hello()
{
static const char* __func__ = "hello"; //当然,你测试的时候是存在__func__的,会报错,你可以改成__func2__
return __func__;
}
__func__预定义标识符对于轻量级的调试代码具有十分重要的作用。
而在C++11中,标准甚至允许其使用在类或者结构体中。
请看如下代码:
struct Test
{
const char*name;
Test():name(__func__){}
}; int main()
{
Test ts;
cout << ts.name << endl;
}
可以看到,在结构体的构造函数中,初始化成员列表使用__func__预定义标识符是可行的,其效果跟在函数中使用一样。
上述代码测试的是结构体,类也是一样。
我们可以用类的数据成员和成员函数分别测试一下:
class Test
{
int t;
const char* m_s;
public:
Test() :t(),m_s(__func__) { }
const char* get_s()const { return m_s; }
void testfunc()const { cout << "成员函数测试结果为:" << __func__ << endl; }
}; int main()
{
Test ts;
cout << "数据成员 m_s 的值为:" << ts.get_s() << endl;
ts.testfunc();
}
结果如我们预期的那样。
不过将__func__标识符作为函数参数的默认值是不允许的,例如
void FuncFail(string func_name = __func__){};
由于在参数声明的时候,__func__还未被定义,前面提到过__func__在函数中的隐式定义。
__func__ usually in function、struct or class body .
三、_Pragma 操作符
在C/C++标准中,#pragma 是一条预处理的指令(preprocessor directive)。简单地说,#pragma 是用来向编译器传达语言标准以外的一些信息。
举个简单的例子,如果我们在代码的头文件中定义了一下语句:
#pragma once
那么该指令会指示编译器(如果编译器支持),该头文件应该只被编译一次。这与使用如下代码来定义头文件所达到的效果是一样的。
#ifndef THIS_HEADER
#define THIS_HEADER
//一些头文件的定义
#endif
在C++11中,标准定义了与预处理指令#pragma 功能相同的操作符_Pragma。 _Pragma操作符的格式如下所示:
_Pragma(字符串字面量)
其使用方法跟sizeof等操作符一样,将字符串字面量作为参数写在括号内即可。那么要达到与上例#pragma类似的效果,则只需要如下代码即可。
_Pragma("once");
由于_Pragma是一个操作符,因此,可以用在一些宏中,形成可以在宏中展开的效果 ,而#pragma则不行,所以,C++11的_Pragma具有更大的灵活性。
由于宏展开很少用到,所以,这里不做测试。
四、变长参数的宏定义以及__VA_ARGS__
在 C99 标准,程序员可以使用变长参数的宏定义。变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号,而预定义宏__VA_ARGS__则可以在宏定义的实现部分替换省略号所代表的字符串。比如:
#define PR(...) printf(__VA_ARGS__)
就可以定义一个printf的别名PR。事实上,变长参数宏与printf是一对好搭档。
简单的一个代码测试如下:
#include<iostream>
using namespace std;
#define PR(...) printf(__VA_ARGS__)
int main()
{
PR("%s = %d\n", "真值",);
}
下面的代码是一个应用:
#include<iostream>
#define LOG(...){\
fprintf(stderr,"%s:Line %d:\t",__FILE__,__LINE__);\
fprintf(stderr,__VA_ARGS__);\
fprintf(stderr,"\n");\
} int main()
{
int x = ;
LOG("x=%d", x);
}
其中__FILE__是一个文件所处的相对路径,__LINE__是当前代码所在的行数
__LINE__还可以规定行数,代码如下所示:
#include<iostream>
#line 30 //规定下一行的__LINE__值为30
#define LOG(...){\
fprintf(stderr,"%s:Line %d:\t",__FILE__,__LINE__);\
fprintf(stderr,__VA_ARGS__);\
fprintf(stderr,"\n");\
} int main()
{
int x = ;
LOG("x=%d", x);
}
从输出结果可以看出来行数发生了变化。
定义LOG宏用于记录代码位置中的一些信息。
程序员可以根据stderr产生的日志追溯到代码中产生这些记录的位置。引入这样的特性,对于轻量级调试 ,简单的错误输出都是具有积极意义的。
我能给大家测试的都测试了,但愿大家能够学到些东西。
感谢您的阅读,生活愉快~
(一)预定义宏、__func__、_Pragma、变长参数宏定义以及__VA_ARGS__的更多相关文章
- 【Unix环境高级编程】编写变长参数函数
文件的格式输入输出函数都支持变长参数.定义时,变长参数列表通过省略号'...'表示, 因此函数定义格式为: type 函数名(parm1, parm2,parmN,...); Unix的变长参数通过v ...
- java常量和变量的定义规则,变长参数的使用
首先是定义的一般规则,类名首字母全部大写,常量全部大写用下划线分隔,变量用驼峰形式.注意使用long赋值用L时不能写小写的L要写大写的,不然会和数字“1”傻傻分不清. 下面是举例: public cl ...
- C++中的变长参数
新参与的项目中,为了使用共享内存和自定义内存池,我们自己定义了MemNew函数,且在函数内部对于非pod类型自动执行构造函数.在需要的地方调用自定义的MemNew函数.这样就带来一个问题,使用stl的 ...
- 《OOC》笔记(3)——C语言变长参数va_list的用法
<OOC>笔记(3)——C语言变长参数va_list的用法 C语言中赫赫有名的printf函数,能够接受的参数数目不固定,这就是变长参数.C#里也有params这个关键字用来实现变长参数. ...
- c(++)变长参数之整形(非字符串类型类似)
0.序言 变长参数,接触的第一个可变长参数函数是 printf , 然后是 scanf .他们的原型如下: printf: _Check_return_opt_ _CRT_STDIO_INLI ...
- Scala 变长参数
如果Scala定义变长参数 def sum(i Int*), 那么调用sum时,可以直接输入sum(1,2,3,4,5) 但是不可以sum(1 to 5) 必须要将1 to 5 强制为seq sum( ...
- C++11变长参数模板
[C++11变长参数模板] C++03只有固定模板参数.C++11 加入新的表示法,允许任意个数.任意类别的模板参数,不必在定义时将参数的个数固定. 实参的个数也可以是 0,所以 tuple<& ...
- 【小白学Lua】之Lua变长参数和unpack函数
一.简介 Lua的变长参数和unpack函数在实际的开发中应用的还挺多的,比如在设计print函数的时候,需要支持对多个变量进行打印输出,这时我们就需要用到Lua中的变长参数和unpack函数了. 二 ...
- Java基础12-工具类;变长参数;IO
作业解析 取出整数的16进制表示形式 \u00ff /** * int2hex * */ public static String int2hex(int i) { String str = &quo ...
随机推荐
- Centos7网络配置(VMware)
在VM虚拟机上装了Centos7,想要用xshell5连接操作,配置网络花了整整一个上午的时间,真是心酸. 登陆后,使用命令 ip addr查看了本机的网络 可以看到我的网络配置文件是ens33, 使 ...
- python基础——python解析yaml类型文件
一.yaml介绍 yaml全称Yet Another Markup Language(另一种标记语言).采用yaml作为配置文件,文件看起来直观.简洁.方便理解.yaml文件可以解析字典.列表和一些基 ...
- marshmallow: 简化Python对象系列化
转载:http://www.thinksaas.cn/topics/0/594/594368.html marshmallow -一个轻量级的库用于将复杂对象转成简单的Python数据类型.或从简单的 ...
- 从一个局长使用BS系统的无奈看测试点
今天我点名买了个B/S系统,听说只要有浏览器就能用.我最讨厌装客户端了,用浏览器就是方便啊. 下面就是我使用这个系统碰到的麻烦事: 我登录失败的时候没有任何提示,这没什么,反正提示也只是说失败…… 进 ...
- 十九、springboot使用@ControllerAdvice(二)之深入理解
前言: 接口类项目开发时,为了便于后期查找问题,一般会拦截器或过滤器中记录每个接口请求的参数与响应值记录, 请求参数很容易从request中获取,但controller的返回值无法从response中 ...
- 栈应用之 括号匹配问题(Python 版)
栈应用之 括号匹配问题(Python 版) 检查括号是否闭合 循序扫描被检查正文(一个字符)里的一个个字符 检查中跳过无关字符(所有非括号字符都与当前处理无关) 遇到开括号将其压入栈 遇到闭括号时弹出 ...
- python-windows下将单个py文件生成exe
突然要生成一个exe给其他人用.紧急搜索下了 命令行参数获取用如下方法 from sys import argv base64path = argv[1] argv这个元组就是你的参数列表了,同C一样 ...
- 洛谷P2661信息传递
传送门啦 一个人要想知道自己的生日,就意味着信息的传递是成环的,因为每轮信息只能传递一个人,传递的轮数就等于环的大小 环的大小就等于环中的两个点到第三个点的距离之和加一,我们就可以在使用并查集时,维护 ...
- Luogu P2069 【松鼠吃果子】
推荐一波数组模拟链表的讲解 这道题呢,数组写的话不好删除(因为后面要接过来),自然想到链表 对于一个果子,我们可以维护其前驱和后继,我们不妨记与一个点相邻的上面的点为其前驱,下面的点为其后继 观察到题 ...
- yum安装Mysql-5.6
MySQL yum库提供了一个简单的和方便的方法来安装和更新MySQL相关的软件包到最新版本. MySQL yum库文档说明:http://dev.mysql.com/doc/mysql-yum-re ...