c - 以一致的方式别名结构和数组

标签 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);
}

那时,我们都知道 double 的对齐允许编译器在 struct Point 中添加任何填充,我们只是假设指针算法可以完成这项工作 .

不幸的是,这个有问题的行使用了指针算法(p[i] 根据定义 *(p + i))在标准明确不允许的任何数组之外。 C11 的 n1570 草案在 6.5.6 additive operators §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...

当我们没有同一个数组的两个元素时什么也没有说,它没有被标准指定,并且从那里开始未定义的行为(即使所有常见的编译器都对此感到高兴......)

问题:

因为这个习语允许避免代码复制只改变 xy 然后 z 这是很容易出错的,什么可以是一致的如何浏览结构的元素,就好像它们是同一数组的成员一样?

免责声明:它显然只适用于相同类型的元素,可以使用简单的 static_assert 检测填充,如 that other question 所示。我的问题,所以填充、对齐和混合类型不是我的问题。

最佳答案

C 没有定义任何方式来指定编译器不得在 struct Point 的命名成员之间添加填充,但是许多编译器都有一个扩展可以提供这种填充。如果你使用这样的扩展,或者如果你只是愿意假设没有填充,那么你可以使用一个带有匿名内部 structunion,比如所以:

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;
}

关于c - 以一致的方式别名结构和数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48384399/

相关文章:

c++ - 如何从 S 函数调用 matlab 变量?

c - 从带有指针的函数返回更大类型的日期

c++ - 当前类成员函数的指针数组

javascript - 如何将数据存储到jquery数据表中的javascript全局变量

arrays - 引用不使用变量名称的结构

c++ - 编辑函数中指针指向的位置

c - 了解汇编中的 C 程序

php - 阵列取消设置键

c++ - 将堆上的结构从 C++ 转换为 C

c - 格式字符串未使用数据参数 - 输出长整型