我正在构建一个 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 struct
和double complex
之间的布局兼容性。
答案 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中,允许编译器为每个编写一些对齐 原始类型。通常,对齐是类型的大小。但 它完全是针对具体实现的。
引入了填充字节,因此每个对象都正确对齐。 不允许重新排序。
可能每个远程现代编译器都实现了#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 ++实现。