C 和 C++ 编译器的问题

Posted

技术标签:

【中文标题】C 和 C++ 编译器的问题【英文标题】:Trouble with C and C++ compiler 【发布时间】:2012-12-05 00:08:30 【问题描述】:

我在尝试将 32 位产品转换为 64 位产品时遇到问题。我使用的是 Visual Studio 2008,代码是 C 和 C++。我希望任何人都可以查看以下两行代码,一行来自 C 源文件,另一行来自 C++ 源文件。这两个文件都包含在 DLL 中。我还包括了两行代码的反汇编。

ewxlcom.c

memcpy(pCM->pSecAccInfo->spUserID,userSecurityInfo.spUserID,
     sizeof(UserID));
000000000EF33BB9  mov         r8d,80h 
000000000EF33BBF  mov         rdx,qword ptr [rsp+828h] 
000000000EF33BC7  mov         rcx,qword ptr [rsp+1F8h] 
000000000EF33BCF  mov         rcx,qword ptr [rcx+0BDEh] 
000000000EF33BD6  call        memcpy (0EF40352h) 

tcputil.cpp

memcpy(serv_temp+INIT_MSG_USERID_OFFSET, pCM->pSecAccInfo->spUserID, INIT_MSG_USERID_LEN);
000000000EF3B8E6  lea         rcx,[rsp+67h] 
000000000EF3B8EB  mov         r8d,80h 
000000000EF3B8F1  mov         rdx,qword ptr [rsp+3B0h] 
000000000EF3B8F9  mov         rdx,qword ptr [rdx+0CBEh] 
000000000EF3B900  call        memcpy (0EF40352h) 

如您所见,第一行将一些字节复制到pCM->pSecAccInfo->spUserID 指向的内存中。第二行将这些相同的字节复制到内存中的另一个位置。 ASM memcpy 将字节从寄存器rdx 指向的内存复制到寄存器rcx 指向的内存。所以在第一行中,一个值被移入寄存器rcx。我已经验证指向pCM。然后将rcx + 0BDEh 指向的值复制到rcx 中。并调用了memcpy。这行得通。

但稍后在第二行将一个值加载到寄存器rdx。我已经验证这也指向与第一行相同的pCM。然后它加载驻留在内存中的指针,该指针与pCM (rdx) 偏移0CBEh。那段内存全为零,所以memcpy 崩溃了。

问题是为什么编译器会为同一个源变量生成不同的代码。我认为这是一个对齐问题。这是C文件和C++文件之间的区别吗? VS 是否对 C 和 C++ 使用相同的编译器?还有什么我应该看的吗?

任何帮助将不胜感激。

【问题讨论】:

您是否尝试过使用较新版本的 VC++?据我们所知,这是编译器中的一个代码生成问题,此后已得到修复。 :-] 这些是调试版本还是优化?也许您可以显示pCM 指向的任何结构? 另外,pCM->pSecAccInfo->spUserID 中有两个重定向似乎很奇怪,但汇编代码似乎只执行一个(在每个示例中)。例如,在您的第一个程序集 sn-p 中,第一次加载到 rcx 大概是将 pCM 从本地堆栈变量加载到寄存器中,然后 rcx 的下一次加载将加载 pSecAccInfo - 但 rcx在调用memcpy() 之前,应该在rcx 中有spUserID。有可能通过优化,第一次加载实际上是pSecAccInfo(但你说你验证了它是pCM)。 【参考方案1】:

如果您要链接 C 和 C++ 代码,您可能需要注意结构中的不同填充特性。也许创建一个临时函数来打印结构的每个成员的偏移量,并将相同的代码从 C 源文件(您编写它的位置)复制到 C++ 源文件。函数的两个副本可以保持不变,因为 C++ 的副本会被破坏,但我会在每个副本的顶部添加一个 printf() 来说明它是哪个版本。然后在崩溃之前从某个地方调用每个,以便您可以比较偏移量。如果它们不同,则需要查看编译器标志来解决该问题。或者...也许您需要添加这样的行...

#ifdef __cplusplus
extern "C" 
#endif
 .
 .    ...your struct definitions & variables go here...
 .
#ifdef __cplusplus

#endif

...围绕您的结构定义,使 C++ 端具有与项目的 C 端相同的填充行为。

【讨论】:

以上是关于C 和 C++ 编译器的问题的主要内容,如果未能解决你的问题,请参考以下文章

为啥 C 程序会在运行时针对 C++ 库编译和链接 C 编译器然后 SIGILL?

为啥要为 c 和 c++ 使用 gcc 和 g++ 编译器驱动程序

zookeeper支持c++吗

将带有结构初始化和赋值的 C 代码移动到 c++ 编译器

具有不同编译器的 C++ 和可变参数

为啥 C 和 C++ 编译器在从未强制执行时允许函数签名中的数组长度?