C和C ++之间的内存布局兼容性

时间:2015-08-24 18:53:15

标签: c++ c

我正在构建一个 C ++ 库,该库使用许多函数以及 C 库中定义的struct。为了避免将任何代码移植到C ++,我将典型的条件预处理添加到C头文件中。例如,

//my_struct.h of the C library
#include <complex.h>

#ifdef __cplusplus
extern "C" {
#endif

typedef struct {
  double d1,d2,d3;
#ifdef __cplusplus
  std::complex<double> z1,z2,z3;
  std::complex<double> *pz;
#else
  double complex z1,z2,z3;
  double complex *pz;
#endif
  int i,j,k;
} my_struct;

//Memory allocating + initialization function
my_struct *
alloc_my_struct(double);

#ifdef __cplusplus
}
#endif

alloc_my_struct()的实施是在 C 中编译的。它只是通过malloc()分配内存并初始化my_struct的成员。

现在,当我在 C ++ 代码中执行以下操作时,

#include "my_struct.h"
...
  my_struct *const ms = alloc_my_struct(2.);

我注意到*ms总是具有预期的内存布局,即任何访问(例如ms->z1)都会计算到预期值。考虑到(CMIIW)分配期间my_struct的内存布局由C编译器(在我的情况下为gcc -std=c11)决定,而在C ++编译器访问期间(在我的情况下{我),我觉得这很酷{1}})。

我的问题是:这种兼容性是否标准化?如果没有,有什么办法吗?

注意:我没有足够的知识来反对对齐,填充和其他实现定义的细节。但值得注意的是,经过C编译的GNU科学库正在实现相同的方法(尽管它们的g++ -std=c++11不涉及C99复数)用于C ++。另一方面,我做了足够的研究,得出结论C ++ 11保证了C99 structdouble complex之间的布局兼容性。

5 个答案:

答案 0 :(得分:5)

C和C ++共享内存布局规则。在两种语言中,结构以相同的方式放在内存中。即使C ++确实想要做一些不同的事情,将结构放在compare : function(a, b) { var columns = sortable.explodeInnerArrays(this[0]); var orders = sortable.explodeInnerArrays(this[1]); function loop(a, b, index) { var currentA = a[columns[index]]; var currentB = b[columns[index]]; if (currentA instanceof Array) { currentA = a[columns[index]][0]; } if (currentB instanceof Array) { currentB = b[columns[index]][0]; } switch (sortable.checkDataType(currentA)) { case "number": if (currentA == currentB) { return loop(a, b, (index+1)); } return (currentA - currentB) * orders[index]; break; case "string": if (currentA == currentB) { return loop(a, b, (index+1)); } currentA = sortable.removePunctuation(currentA); currentB = sortable.removePunctuation(currentB) if (currentA < currentB) { return -1 * orders[index]; } if (currentA > currentB) { return 1 * orders[index]; } break; } } return loop(a, b, 0); } 内也可以保证C布局。

但是你的代码在做什么依赖于C ++ std :: complex和C99复合体是相同的。

所以看:

答案 1 :(得分:3)

它可能并不总是相同!

在这种情况下,sizeof(std::complex<double>)看起来与sizeof(double complex)相同。 还要注意编译器可能(或可能不)根据优化配置向结构添加填充以使它们与特定值对齐。并且填充可能并不总是相同的,导致不同的结构大小(在C和c ++之间)。

相关帖子的链接:

C/C++ Struct memory layout equivalency

  

我会添加特定于编译器的属性来“打包”字段,   从而保证所有的油口相邻且紧凑。这是   关于C与C ++的关系较少,更多关于您可能使用的事实   编译两种语言的两个“不同”编译器,即使   那些编译器来自一个供应商。

     

添加构造函数不会改变布局(尽管它会改变   类非POD),但添加访问说明符之间的私有   这两个字段可能会改变布局(实际上,不仅仅是在   理论)。

C struct memory layout?

  

在C中,允许编译器为每个编写一些对齐   原始类型。通常,对齐是类型的大小。但   它完全是针对具体实现的。

     

引入了填充字节,因此每个对象都正确对齐。   不允许重新排序。

     

可能每个远程现代编译器都实现了#pragma pack   允许控制填充并将其留给程序员遵守   与ABI。 (但这绝对是非标准的。)

     
    

来自C99§6.7.2.1:

         

12结构或联合对象的每个非位字段成员都是对齐的     以适合其类型的实现定义方式。

         

13在结构对象中,非位字段成员和单位     其中位字段驻留的地址按顺序增加     他们被宣布。适当地指向结构对象的指针     转换后,指向其初始成员(或者如果该成员是     位字段,然后到它所在的单元,反之亦然。     结构对象中可能有未命名的填充,但不在其中     开始。

  

答案 2 :(得分:3)

您的程序有未定义的行为:my_struct的定义在词法上不相同。

你赌博,两个编译器之间的对齐,填充和各种其他事情都不会改变,这已经够糟糕了......但是因为这是UB所有事情都可能发生,即使它是真的!

答案 3 :(得分:2)

还要注意,通过 <div class="popover-markup"> <a href="#" class="trigger"><i class="fa fa-floppy-o fa-2x" title="Save Grid Layout" style="color:#108ec6; padding:5px;"></i></a> <div class="head hide">Save Report</div> <div class="content hide"> <div class="form-group"> <asp:TextBox runat="server" ID="txtReportName" CssClass="form-control" Width="150px" placeholder="Report Name" /> </div> <asp:Button CssClass="btn btn-lg btn-info btn-block" ID="btnSaveLayout" runat="server" Text="Save" OnClick="btnSaveLayout_Click"/> </div> 一个带有C ++对象(void OnMouseDrag(){ Debug.Log ("left"); player.GetComponent<PlayerAnimator>().animationState = MovementState.moveLeft; player.transform.position+=Vector3.left / 30; } )的结构,你跳过了ctor,这也是UB - 即使你期望ctor是空的,或者只是零值并且无害被跳过,如果这件事破裂你就不能抱怨。所以你的计划工作是纯粹的运气。

答案 4 :(得分:2)

通常,C和C ++具有兼容的结构布局,因为布局由平台的ABI规则决定,而不仅仅是语言,(对于大多数实现)C和C ++遵循相同的ABI规则类型大小,数据布局,调用约定等。

C ++ 11甚至定义了一个新术语 standard-layout ,这意味着该类型将具有与C中类似类型的兼容布局。这意味着它不能使用虚拟函数,私有数据成员,多重继承(以及其他一些东西)。 C ++标准布局类型应具有与等效C类型相同的布局。

如其他答案中所述,您的特定代码通常不安全,因为std::complex<double>complex double 不是等效类型,并且无法保证它们是布局-兼容。 然而 GCC的C ++标准库确保它可以正常运行,因为std::complex<double>std::complex<float>是根据底层C类型实现的。 GCC的std::complex<double>没有包含两个double,而是有一个__complex__ double类型的成员,编译器实现与等效C类型完全相同。

GCC专门用于支持像你这样的代码,因为它是一件合理的事情。

因此,将GCC对std::complex的特殊工作与标准布局规则和平台ABI相结合,意味着您的代码将与该实现一起使用。

这不一定可以移植到其他C ++实现。