C++11 与 C99的兼容

2023-10-11 05:08
文章标签 c++ 兼容 c99

本文主要是介绍C++11 与 C99的兼容,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

C++对以下C99特性的支持纳入了新标准之中:
1、C99中的预定义宏
2、__func__预定义标识符
3、_Pragma操作符
4、不定参数宏定义以及__VA_ARGS__
5、宽窄字符串连接



1> c++11中与c99兼容的宏如下表所示:


使用上述宏可以检查机器环境对C标准和C库的支持情况。
#include <iostream>
using namespace std;int main()
{cout << "Standerd Clib" << __STDC_HOSTED__ << endl;cout << "Standerd C" << __STDC__ << endl;//cout << "C Standerd version " << __STDC_VERSION__ << endl;//cout << "ISO/IEC" << __STDC_ISO_10646__ << endl;return 0;
}
在g++ 2.3.3版本中后面两行不能编译通过,说明在g++ 2.3.3版本中可能没有给出这两个宏的定义。

2> __func__预定义标识符

很多现实的编译器都支持C99标准中的__func__预定义标识符功能,其基本功能就是返回所在函数的名字。
#include <iostream>
using namespace std;const char* hello()
{return __func__;
}const char* world()
{return __func__;
}int main()
{cout << hello() << world() << endl;return 0;
}

Tips: vs2012中不支持,g++ 3.3.2版本支持,这个版本的g++还不支持C++11标准。所以这是兼容C99标准。

__func__在按照标准定义,编译器会隐式地在函数定义后面定义__func__标识符。

const char* hello()
{static const char* __func__ = "hello";renturn __func__;
}

在C++11标准中,还允许在类和结构体中使用这个宏。
#include <iostream>
using namespace std;struct TestStruct
{TestStruct(): name(__func__){}const char* name;
};int main()
{TestStruct ts;cout << ts.name << endl;renturn 0;
}

但是将__func__标识符作为函数参数的默认值是不允许的。
void FuncFail(string func_name = __func__){};  //编译不能通过



3>_Pragma操作符

在C/C++标准中,#pragma是一条预处理指令。


#pragma once

上述代码和
#ifndef THIS_HEADER
#define THIS_HEADER//一些头文件定义#endif

C++11中定义一个_Pragma操作符和#pragma作用相同。_Pragma操作符的格式如下:
_Pragma(字符串字面量)
使用_Pragma操作符想要达到#pragma once的效果可以:
_Pragma("once")


4>变长参数的宏定义以及__VA_ARGS__

在C99标准中,程序员可以使用变长参数的宏定义。变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号,而预定义宏__VA_ARGS__则可以在宏定义的实现部分替换省略号所代表的字符串。
#define PR(...) printf(__VA__ARGS__)
就可以定义一个printf的别名PR。事实上,变长参数宏和printf是一对好搭档。

#include <stdio.h>#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);
}

上述例子在vs2012中不支持,在g++ 3.3.2版本支持


5>宽窄字符串连接

在C++11中,在将窄字符串和宽字符串进行连接时,支持C++11标准的编译器会将窄字符串转换成宽字符串,然后再与宽字符串进行连接。


6、扩展的整型
C++11中一共只定义了5种标准的有符号整型:
signed char
short int
int
long int
long long int

标准同时规定,每一种有符号整型都有一种对应的无符号整型版本,而且有符号整型与其对应的无符号整型具有相同的存储空间大小。



7、宏__cplusplus
在C/C++混合编写的代码中,我们经常会在头文件中看到如下的声明:

#ifdef __cplusplus
extern "C"{
#endif
//一些代码
#ifdef __cplusplus
}
#endif
由于extern "C"可以抑制C++对函数名、变量名等符号进行名称重整(name mangling),因此编译出的C目标文件中的变量,函数名称等等符号都是相同的,连接器可以
可靠的对两种类型的目标文件进行连接,这样该做法成为C与C++混用头文件的典型做法。
tips:在C++03标准中,__cplusplus的值被预定为199711L,在C++11中,宏__cplusplus被预定义成201103L。那么可以利用这个宏来检查编译器是否支持C++11新标准。


#if __cplusplus < 20131103L#error "should use C++ 11 implementation"
#endif

这里,使用了预处理指令#error,使得不支持C++11时,编译报错并且终止编译。

3、断言assert
assert是一种边编程中常用的手段。用于排除在逻辑上不应该产生的情况。在C++中标准在<cassert>或者<assert.h>头文件中为程序员提供了assert宏,用于在运行时进行断言。


#include <cassert>using namespace std;double div(double a,double b)
{assert(b != 0.000000);  //断言除数必须不为零return a/b;
}int main()
{double rt = div(10,0);return 0;
}

static_assert在C++11中定义了一个static_assert,这个声明对于模板的调试非常有用,编译器快速执行这个常量表示式参数(不能依赖模板参数)。否则编译器当模板实例化时执行这个常量表达式的参数。

#include <cassert>
#include <cstring>
using namespace std;template<typename t, typename u>
int bit_copy(t& a, u& b)
{static_assert(sizeof(b) == sizeof(a), "the parameters of bit_copy must have same width.");
};int main() {int a = 0x2468;double b;bit_copy(a,b);return 0;
}

在编译的过程中就会出现如下的错误信息:






这篇关于C++11 与 C99的兼容的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/185728

相关文章

C++类和对象之初始化列表的使用方式

《C++类和对象之初始化列表的使用方式》:本文主要介绍C++类和对象之初始化列表的使用方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C++初始化列表详解:性能优化与正确实践什么是初始化列表?初始化列表的三大核心作用1. 性能优化:避免不必要的赋值操作2. 强

C++迭代器失效的避坑指南

《C++迭代器失效的避坑指南》在C++中,迭代器(iterator)是一种类似指针的对象,用于遍历STL容器(如vector、list、map等),迭代器失效是指在对容器进行某些操作后... 目录1. 什么是迭代器失效?2. 哪些操作会导致迭代器失效?2.1 vector 的插入操作(push_back,

使用Python开发Markdown兼容公式格式转换工具

《使用Python开发Markdown兼容公式格式转换工具》在技术写作中我们经常遇到公式格式问题,例如MathML无法显示,LaTeX格式错乱等,所以本文我们将使用Python开发Markdown兼容... 目录一、工具背景二、环境配置(Windows 10/11)1. 创建conda环境2. 获取XSLT

C#如何调用C++库

《C#如何调用C++库》:本文主要介绍C#如何调用C++库方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录方法一:使用P/Invoke1. 导出C++函数2. 定义P/Invoke签名3. 调用C++函数方法二:使用C++/CLI作为桥接1. 创建C++/CL

C++如何通过Qt反射机制实现数据类序列化

《C++如何通过Qt反射机制实现数据类序列化》在C++工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作,所以本文就来聊聊C++如何通过Qt反射机制实现数据类序列化吧... 目录设计预期设计思路代码实现使用方法在 C++ 工程中经常需要使用数据类,并对数据类进行存储、打印、调试等操作。由于数据类

Linux下如何使用C++获取硬件信息

《Linux下如何使用C++获取硬件信息》这篇文章主要为大家详细介绍了如何使用C++实现获取CPU,主板,磁盘,BIOS信息等硬件信息,文中的示例代码讲解详细,感兴趣的小伙伴可以了解下... 目录方法获取CPU信息:读取"/proc/cpuinfo"文件获取磁盘信息:读取"/proc/diskstats"文

C++使用printf语句实现进制转换的示例代码

《C++使用printf语句实现进制转换的示例代码》在C语言中,printf函数可以直接实现部分进制转换功能,通过格式说明符(formatspecifier)快速输出不同进制的数值,下面给大家分享C+... 目录一、printf 原生支持的进制转换1. 十进制、八进制、十六进制转换2. 显示进制前缀3. 指

C++中初始化二维数组的几种常见方法

《C++中初始化二维数组的几种常见方法》本文详细介绍了在C++中初始化二维数组的不同方式,包括静态初始化、循环、全部为零、部分初始化、std::array和std::vector,以及std::vec... 目录1. 静态初始化2. 使用循环初始化3. 全部初始化为零4. 部分初始化5. 使用 std::a

C++ vector的常见用法超详细讲解

《C++vector的常见用法超详细讲解》:本文主要介绍C++vector的常见用法,包括C++中vector容器的定义、初始化方法、访问元素、常用函数及其时间复杂度,通过代码介绍的非常详细,... 目录1、vector的定义2、vector常用初始化方法1、使编程用花括号直接赋值2、使用圆括号赋值3、ve

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元