C++

移植代码到64位平台

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com C和C++大量依赖于指针的使用,即包含内存地址的变量。尽管指针类型在概念上与整数类型不同,但其物理地址值 始终是一个整数 。开发人员在C/C ++中大量使用整数类型而不是指针类型,即使在编译器随附的系统头文件中也是如此。在过去的几十年中,大多数计算机处理器都使用32位内存地址,因此开发环境是 围绕地址长度为32位的假设 建立的。C/C++代码中存在着大量 指针和整数之间的隐式和显式转换 。使用指针,或union的重叠内存结构方式(overlapping memory structures),去访问变量,也是司空见惯。 当然,32位架构有其局限性。具体来说,它们将计算机内存限制为4GB。应用程序需求不断增长,迫切需要迁移到更长的地址。经常使用的旧式类型带来了新的含义和价值。最具戏剧性,最明显的的变化,是从32位转向64位指针。 不再适用相同的规则 基本类型的某些大小已更改。其中一些保持不变。但是,突然之间,在C和C++代码库中使用的许多隐式假设不再有效。在32位系统中编写和测试的代码在迁移到64位计算机时不再有效。 幸运的是,已经识别并分类了代码可能易于从32位计算机迁移到64位计算机的大多数区域。一些编译器使用主动检查技术来捕获在迁移期间可能出现的漏洞。静态分析工具的供应商(包括Coverity和Klocwork)可以提供进一步帮助将代码从32位机器平稳迁移到64位机器的机制。尽管这些工具不能直接解决代码迁移问题,但它们提供了便利的可扩展性和API,可以创建与32位到64位迁移相关的自定义检查程序。增强这些工具可以进一步帮助发现并有效解决这些兼容性问题。 本文的目的是帮助您使代码库与体系结构无关,因此可以在32位计算机或64位计算机上构建相同的代码库,并为每台计算机生成可行的代码。此外,如果32位程序通过二进制数据交换(通过文件或套接字)与64位程序进行通信,则有多种方法可以确保二进制结构不受两种体系结构类型之间的迁移的影响。 本文包含详细的分析和建议,以解决与32位到64位迁移有关的众多问题。 为了使您的程序与体系结构无关,有两个目标需要实现。 其中之一是内部一致性,即确保程序中使用的所有类型都是一致的,而不管代码是在32位还是64位计算机上编译的。 让我们看一下两种架构上基本类型的大小(请参见表1)。 此处的显着区别是指针类型和long类型的大小变化。由于大小更改,某些基于隐式依赖关系构建的代码可能无法正确迁移。可能的问题主要与整数与整数之间,或者是整数和指针之间的数据交换有关。 整数大小的差异 在32位系统上,int和long类型都是32位,这意味着程序员可以互换使用这两种类型。此外,C没有严格的输入规则,因此函数中的int参数可以传递long参数而不会出错。尽管这不是一个好的编码实践,但是此代码可能会在数年内完美运行。但是,在64位系统中,在某些编译器中,int和long具有不同的大小,并且代码在迁移后将无法正常工作。将long值分配给int变量可能会导致截断。以下代码在32位系统中可以正常工作,但在64位系统中则极有可能正常工作。 int sum; long val1, val2; sum = val1 + val2; // possible truncation in 64-bit system 以下代码可能会在某些编译器中引起编译器警告,但是此代码(在32位模式下工作)可能将无法在64位模式下工作,因为堆栈上的值大小会发生变化。 int add(int parm1, int parm2); // ..... long val1, val2; long sum = add(val1,val2); // mismatch of parm size on 64-bit machine 可能还会发生其他一些细微的事情,从而影响代码的完整性。例如,编译时函数sizeof()返回类型为size_t的结果,size_t类型的占据的字节大小,则取决于该程序中指针类型变量占据的字节数的大小。将此值分配给整数可能导致被截断。如下代码所示: int mySize = sizeof(MyStructure); // possible size truncation 另一个更细微的错误是位字段的符号扩展。 例如:

C++的右值和移动构造函数的测试程序说明

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com 本文用以测试C++的右值,移动构造函数,以及标准库容器的emplace和push操作的区别。 测试环境是Visual C++ 14.0.25431.01 Update 3版本,未启用任何编译优化操作 代码可以在github中获取。 #include <cstring> #include <cstdlib> #include <map> #include <vector> #include <cstdio> class DemoClassA { public: DemoClassA() :num_(0), name_(nullptr) { std::printf("DemoClassA::DemoClassA()\n"); } ~DemoClassA() { if (name_) { std::printf("DemoClassA::~DemoClassA(%d,%s)\n", num_, name_); delete[] name_; name_ = nullptr; } else { std::printf("DemoClassA::~DemoClassA()\n"); } } DemoClassA(int num, const char* name) { num_ = num; auto len = std::strlen(name) + 1; name_ = new char[len]; memset(name_, 0, len); std::strcpy(name_, name); std::printf("DemoClassA::DemoClassA(%d,%s)\n", num_, name_); } DemoClassA(const DemoClassA& rhs) { num_ = rhs.

C/C++中macro的一些特殊使用

请尊重原作者的工作,转载时请务必注明转载自:www.xionggf.com ISO C++ 推出之后,C/C++ 中宏(macro)的使用一直都备受争议,就好像当年保守诟病的goto语句一样。有所不同的是,现在的C/C++ 代码中使用goto语句的使用率是越来越少了。而macro仍然在C/C++ programming中占据一席之地——例如集赞誉之耀和诋毁之辱于一身的Microsoft Foudation Classes就把macro的强大功能发挥得淋漓尽致。套用“存在着就是合理”,macro的存在必定有其合理的因素。事实上,如果在programming的时候充分发挥macro的机制,就能写出一些巧妙精致而且设计合理的代码。我在工作中阅读了一个引擎的代码,引擎在某处就比较巧妙地利用了macro的功能。在此我将其整理出来,供大家学习交流。 大家在进行C/C++ programming的时候,常常定义一些枚举类型的数据,例如: enum EMessage { MSG_OPEN, MSG_CLOSE, MSG_COPY, MSG_PASTE }; 在某些时候,比如在调试某段程序的时候,使用调试器捕获了某个枚举变量的值,这时候我们所能得到的是一些数值。例如当变量EMessage eMsg = MSG_OPEN的时候,我们能观察到eMsg = 0。但是单单一个“0”的话一下子让人难以明白它的含义,如果想在屏幕上也能打印出枚举的名称就更加不好办了。解决这个问题,方法之一,就是利用macro的强大灵活的机制。现在我们来一步一步地分析如何利用macro来解决上述的问题。 想打印枚举的名称,你首先想到的肯定是:使用一个字典数据结构记录枚举变量和描述字符串的映射关系。例如: #include <map> using namespace std; map<EMessage,const char*> MsgDesc; //为了节省空间,不使用string存储描述信息 MsgDesc[MSG_OPEN] = "MSG_OPEN"; MsgDesc[MSG_CLOSE] = "MSG_CLOSE"; MsgDesc[MSG_COPY] = "MSG_COPY"; MsgDesc[MSG_PASTE] = "MSG_PASTE"; 使用字典数据结构很方便,但是如果枚举值很少的时候,使用一个map类型的对象来管理这些东西似乎有点暴殄天物(或许对于现在的硬件设备而言不算什么,但是如果性能有着严格要求的话。。。)。如果枚举值很多的话,每添加一个新的枚举量就要在枚举定义内写一次,在把枚举量描述添加进字典中又要写一次,尤为繁琐。 如果使用macro机制的话,可以通过预编译和macro机制一次性处理上述的问题。 例如:在文件msgdef.h中 #ifndef _MSGDEF_H_ #define _MSGDEF_H_ #undef USEMACRO #if defined(INCLUDE_AS_ENUM) //判断预编译宏的设置情况,判断是转换成enum还是string #define MSG_MACRO(msg) msg, #elif defined(INCLUDE_AS_STRING) #define MSG_MACRO(msg) #msg, //不能遗忘“,”号 #else #error To use this include file, first define either INCLUDE_AS_ENUM or INCLUDE_AS_STRING #endif //有新定义的message 枚举值的话,就只是需要在此添加 MSG_MACRO(MSG_OPEN) MSG_MACRO(MSG_CLOSE) MSG_MACRO(MSG_COPY) MSG_MACRO(MSG_PASTE) #endif 而使用的时候,就要如此使用,例如在main.