在ISO C之前的旧时代,以下代码会让人惊讶:
struct Point {
double x;
double y;
double z;
};
double dist(struct Point *p1, struct Point *p2) {
double d2 = 0;
double *coord1 = &p1.x;
double *coord2 = &p2.x;
int i;
for (i=0; i<3; i++) {
double d = coord2[i] - coord1[i]; // THE problem
d2 += d * d;
return sqrt(d2);
}
Run Code Online (Sandbox Code Playgroud)
那时,我们都知道double的对齐允许编译器不添加填充struct Point
,我们只是假设指针算术会完成这项工作.
不幸的是,这个问题的行使用指针算术(p[i]
是由定义 *(p + i)
)的任何阵列的外面,其明确地不是由标准允许的.C11的n1570草案在6.5.6附加运营商§8中说:
当一个具有整数类型的表达式被添加到指针指针或从指针指针中减去时,结果具有指针操作数的类型.如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向偏离原始元素的元素,使得结果元素和原始数组元素的下标的差异等于整数表达式. ..
当我们没有相同数组的两个元素时,没有任何说法,它没有被标准和未定义的行为指定(即使所有常见的编译器都很高兴...)
题:
由于这个成语允许避免代码复制只是改变x …
我正在使用第三方C++库在Julia中做一些繁重的工作.在Julia方面,数据存储在类型的对象中Array{Float64, 2}
(这大致类似于2D数组的双精度数).我可以使用指向a的指针将它传递给C++ double
.但是,在C++方面,数据存储在一个名为的结构中vector3
:
typedef struct _vector3
{
double x, y, z;
} vector3;
Run Code Online (Sandbox Code Playgroud)
我快速而肮脏的方法分为五个步骤:
double*
到vector3*
vector3*
到double*
复制大量数据效率非常低.我可以使用一些神秘的技巧来避免复制数据double
到struct
后面吗?我想以某种方式解释一个1D数组double
(大小是3的倍数)作为具有3个double
成员的结构的一维数组.
这是对另一个问题的后续行动
我试图在编译时建立一个特定的实现是否在结构中添加了未命名的填充.像gcc这样的特定实现允许使用pragma来控制结构中的填充和对齐,但代价是与其他实现的兼容性.由于这两个static_assert
和offset_of
被用于C11的n1570草案所要求的,我想用他们看到实现是否成员之间使用填充.
这是代码的相关部分(引用问题中的完整代码):
#include <stdio.h>
#include <stddef.h>
#include <assert.h>
struct quad {
int x;
int y;
int z;
int t;
};
int main() {
// ensure members are consecutive (note 1)
static_assert(offsetof(struct quad, t) == 3 * sizeof(int),
"unexpected padding in quad struct");
struct quad q;
...
Run Code Online (Sandbox Code Playgroud)
正如6.7.2.1结构和联合说明符§15所说:
在结构对象中,非位字段成员和位字段所在的单元具有按声明顺序增加的地址.指向适当转换的结构对象的指针指向其初始成员(或者如果该成员是位字段,则指向它所在的单元),反之亦然.结构对象中可能存在未命名的填充,但不是在其开头.
我假设如果结构中元素的偏移量是在它之前声明的元素的大小的总和,那么这些元素之间不存在填充,并且它们应该连续分配,从而如果它们是相同类型则构成数组.
问题是:上述假设是错误的,它是(对参考问题的评论让我们思考)为什么?