这是我的另一个问题的 C++后续
在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)
不幸的是,这个问题的行使用指针算法(p[i]被定义 *(p + i)).其明确地不是由标准允许的任何阵列的外面草案4659用于C++ 17说,在8.7 [expr.add]:
如果表达式P指向具有n个元素的数组对象x的元素x [i],则表达式P + J和J + P(其中J具有值j)指向(可能是假设的)元素x [i + j]如果0 <= …
在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++中,它将是明确的Undefined Behavior,因为数组只能由声明或新表达式创建.但是C语言将数组定义为具有特定成员对象类型的连续分配的非空对象集,称为元素类型.(n1570 C11草案,6.2.5类型§20).因此,如果我们可以确保成员是连续的(意味着它们之间没有填充),那么将其视为数组是合法的.
这是一个简化的示例,它在没有警告的情况下编译,并在运行时给出预期的结果:
#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;
int *ix = &q.x;
for(int i=0; i<4; i++) {
ix[i] = i;
}
printf("Quad: %d %d %d %d\n", q.x, q.y, q.z, q.t);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
它在这里没有意义,但我已经看到了真实世界的例子,在结构的成员之间进行迭代允许更简单的代码,而错误的风险更低.
题:
在上面的例子中,是否static_assert足以使用数组合法化结构的别名?
(注释1)由于结构 …