(一)预定义宏、__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(0),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", "真值",1);
}

下面的代码是一个应用:

#include<iostream>
#define LOG(...){\
   fprintf(stderr,"%s:Line %d:\t",__FILE__,__LINE__);   fprintf(stderr,__VA_ARGS__);   fprintf(stderr,"\n");}

int main()
{
    int x = 3;
    LOG("x=%d", x);
}

其中__FILE__是一个文件所处的相对路径,__LINE__是当前代码所在的行数

__LINE__还可以规定行数,代码如下所示:

 1 #include<iostream>
 2 #line 30                  //规定下一行的__LINE__值为30
 3 #define LOG(...){ 4    fprintf(stderr,"%s:Line %d:\t",__FILE__,__LINE__); 5    fprintf(stderr,__VA_ARGS__); 6    fprintf(stderr,"\n"); 7 }
 8
 9 int main()
10 {
11     int x = 3;
12     LOG("x=%d", x);
13 }

从输出结果可以看出来行数发生了变化。

定义LOG宏用于记录代码位置中的一些信息。

程序员可以根据stderr产生的日志追溯到代码中产生这些记录的位置。引入这样的特性,对于轻量级调试 ,简单的错误输出都是具有积极意义的。

我能给大家测试的都测试了,但愿大家能够学到些东西。

感谢您的阅读,生活愉快~

原文地址:https://www.cnblogs.com/lv-anchoret/p/8411102.html

时间: 2024-08-24 05:27:31

(一)预定义宏、__func__、_Pragma、变长参数宏定义以及__VA_ARGS__的相关文章

【Unix环境高级编程】编写变长参数函数

文件的格式输入输出函数都支持变长参数.定义时,变长参数列表通过省略号'...'表示, 因此函数定义格式为: type 函数名(parm1, parm2,parmN,...); Unix的变长参数通过va_list对象实现,定义在文件'stdarg.h'中,变长参数的应用模板如下所示: #include <stdarg.h> function(parmN,...){ va_list pvar; ................................. va_start(pvar,par

C++11 变长参数的宏定义以及__VA_ARGS__

[1]变长参数的宏定义以及__VA_ARGS__ 在C99标准中,我们就已经可以使用变长参数的宏定义. 变长参数的宏定义是个神马?就是在宏定义的参数列表中最后一个参数为省略号. 而现在C++ 11中,使用预定义宏__VA_ARGS__可以在宏定义的实现部分替换省略号所代表的字符串. 原书示例: #include <stdio.h> #define LOG(...) { \ fprintf(stderr, "%s: Line %d:\t", __FILE__, __LINE_

如何定义变长参数个数的函数

定义参数个数不确定的函数,需用到头文件stdarg.h,该头文件是专门为变长参数函数所用. 参数变长函数的声明:void function(int intVal, ...),当然参数类型可以为double或其他,返回类型也可以自己修改. 方法: 先用头文件stdarg.h中的宏va_list定义一个指向参数的指针ap,va_list ap: 再用宏va_start初始化指针ap,va_start(ap,知名变量名intVal): 再用va_arg使指针ap指向下一参数,并且取出该参数,va_ar

读书笔记:c语言标准库 - 变长参数

· 变长参数(stdarg.h) 变长参数是c语言的特殊参数形式,例如如下函数声明: int printf(const char * format,...); 如此的声明表明,printf函数除了第一个参数类型为const char*之外,其后可以追加任意数量.任意类型的参数. 在函数实现部分,可以使用stdarg.h里的多个宏来访问各个额外的参数:假设lastarg是变长参数函数的最后一个具名参数(printf里的format),那么在函数内容定义类型为va_list的变量: va_list

C语言--变长参数

一.  实现原理 首先变长参数的实现依赖于cdecl调用,因为其规定了出栈方为函数调用方,从而解决被调用函数无法确定参数个数,其次cdecl规定参数入栈顺序为从右到左.所以第一个不定参数位于栈顶 二. 宏源码讲解  (va ---> variable-argument(可变参数)) 头文件 stdarg.h 2.1 va_list #define va_list char * 定义了一个指针arg_ptr, 用于指示可选的参数. 2.2 va_start(arg_ptr, argN) #defi

Java语法糖初探(三)--变长参数

变长参数概念 在Java5 中提供了变长参数(varargs),也就是在方法定义中可以使用个数不确定的参数,对于同一方法可以使用不同个数的参数调用.形如 function(T -args).但是需要明确的一点是,java方法的变长参数只是语法糖,其本质上还是将变长的实际参数 varargs 包装为一个数组. 看下面的例子: 12345678910111213 public class VariVargs { public static void main(String []args) { tes

scala学习笔记-变长参数(5)

变长参数 在Scala中,有时我们需要将函数定义为参数个数可变的形式,则此时可以使用变长参数定义函数. 1 def sum(nums: Int*) = { 2 var res = 0 3 for (num <- nums) res += num 4 res 5 } 6 7 sum(1, 2, 3, 4, 5) 使用序列调用变长参数 在如果想要将一个已有的序列直接调用变长参数函数,是不对的.比如val s = sum(1 to 5).此时需要使用Scala特殊的语法将参数定义为序列,让Scala解

java变长参数

从java5开始提供了变长参数,可以把变长参数当作数据使用 可变长参数方法的定义 使用...表示可变长参数,例如 print(String... args){ ... } 在具有可变长参数的方法中可以把参数当成数组使用,例如可以循环输出所有的参数值. print(String... args){ for(String temp:args) System.out.println(temp); } 可变长参数的方法的调用 调用的时候可以给出任意多个参数,例如: print("hello")

变长参数

先有如下需求:返回实参中的最大者.但由于实参的个数不确定,难以采用重载的办法. 解决:在定义方法时将形参的格式定义如下: int getMax(int first,int...varArgs){ for(int i:varAgus) .... } 调用: System.out.println("max(1)="+demo.getMax(1)+" ");//1 System.out.println("max(2)="+demo.getMax(2,1