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); }
那时,我们都知道double的对齐允许编译器在struct Point中不添加填充,我们只是假设指针算术会完成这项工作.
不幸的是,这个有问题的行使用指标算术(p [i]按定义*(p i))在任何数组之外,该标准明确不允许. C11的n1570草案在6.5.6附加操作符§8中说:
When an expression that has integer type is added to or subtracted from a pointerpointer,the
result has the type of the pointer operand. If the pointer operand points to an element of
an array object,and the array is large enough,the result points to an element offset from
the original element such that the difference of the subscripts of the resulting and original
array elements equals the integer expression…
当我们没有相同数组的两个元素时没有说什么,它没有被标准和未定义的行为指定(即使所有常见的编译器都很高兴它…)
题:
因为这个成语允许避免代码复制只改变x和y然后z这是非常容易出错的,所以可能是一种一致的方式来浏览结构的元素,就好像它们是同一个数组的成员一样?
免责声明:它显然只适用于相同类型的元素,并且可以使用简单的static_assert检测填充,如我的that other question所示,因此填充,对齐和混合类型不是我的问题.
解决方法
union Point { struct { double x; double y; double z; }; double coords[3]; };
然后,您可以通过各自的名称或通过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; }