Ser*_*sta 8 c arrays struct language-lawyer
在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有y那么z这是很容易出错,这可能是浏览一个结构的元素,就好像它们是同一阵列的成员符合的方式吗?
免责声明:它显然只适用于相同类型的元素,并且可以通过简单检测填充,如我的其他问题static_assert所示,因此填充,对齐和混合类型不是我的问题.
C没有定义任何方式来指定编译器不能在命名成员之间添加填充struct Point,但是许多编译器都有一个扩展来提供它.如果你使用这样的扩展,或者你只是愿意假设没有填充,那么你可以使用union匿名内部struct,如下所示:
union Point {
struct {
double x;
double y;
double z;
};
double coords[3];
};
Run Code Online (Sandbox Code Playgroud)
然后,您可以通过各自的名称或通过coords数组访问坐标:
double dist(union Point *p1, union Point *p2) {
double *coord1 = p1->coords;
double *coord2 = p2->coords;
double d2 = 0;
for (int i = 0; i < 3; i++) {
double d = coord2[i] - coord1[i];
d2 += d * d;
}
return sqrt(d2);
}
int main(void) {
// Note: I don't think the inner braces are necessary, but they silence
// warnings from gcc 4.8.5:
union Point p1 = { { .x = .25, .y = 1, .z = 3 } };
union Point p2;
p2.x = 2.25;
p2.y = -1;
p2.z = 0;
printf("The distance is %lf\n", dist(&p1, &p2));
return 0;
}
Run Code Online (Sandbox Code Playgroud)