在结构中相同类型的连续成员之间的指针算术曾经是一种常见做法,而指针算术仅在数组中有效。在C中,它将是显式的未定义行为,因为数组只能由声明或新表达式创建。但是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;
}
这在这里实际上没有意义,但是我已经看到了真实世界的例子,其中在结构的成员之间迭代允许更简单的代码,并且输入错误的风险更小。
问题:
在上面的示例中,static_assert
是否足以使结构的别名与数组合法?
(注1)由于结构描述的是按顺序分配的非空成员对象集,因此后续成员必须具有递增的地址。简单地说,编译器可以包括它们之间的填充。因此,如果 3 倍大小 (int)
加上它之前的总填充,则最后一个成员(此处为 t
)的偏移量。如果偏移量正好是 3 * 大小(int),
则结构中没有填充
作为副本提出的问题包含一个被接受的答案和一个1答案,前者让let认为它是UB,后者让let认为它是合法的,因为我可以确保不存在填充
我要和UB争论。首先,6.5.6添加剂操作员的强制性引用:
当具有整数类型的表达式被添加到指针或从指针中减去时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向从原始元素偏移的元素,使得结果数组元素和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式P指向数组对象的第I个元素,则表达式(P) N(等价地,N (P))和(P)-N(其中N的值为N)分别指向数组对象的第In个和第i-n个元素,前提是它们存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P) 1指向数组对象的最后一个元素之后的一个元素,如果表达式Q指向数组对象的最后一个元素之后的一个元素,则表达式(Q)-1指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者都超过数组对象的最后一个元素,则求值不应产生溢出;否则,行为是未定义的。如果结果比数组对象的最后一个元素多一位,则它不能用作一元*运算符的操作数。
我强调了我认为问题的关键。你说数组对象是“具有特定成员对象类型(称为元素类型)的连续分配的非空对象集”,这是对的。但是反过来是真的吗?连续分配的一组对象构成数组对象吗?
我要说不。对象需要显式创建。
因此,对于您的示例,没有数组对象。在 C 中通常有两种方法可以创建对象,使用自动、静态或线程本地持续时间声明它们。或者分配它们并为存储提供有效的类型。您没有创建数组。这使得算术正式未定义。
这里的问题是您对连续分配的定义:“我们可以确保成员是连续的(意味着它们之间没有填充)”。
尽管这是连续分配的必然结果,但它并没有定义属性。
您的结构成员是具有自动存储持续时间的单独变量,按照特定顺序进行填充或不填充,这取决于您如何控制编译器,仅此而已。因此,给定另一个成员的地址,您不能使用指针算术来到达一个成员,并且这样做的行为是未定义的。
不,将struct
和数组别名为这样是不合法的,这违反了严格的别名。解决方法是将结构包装在联合中,该联合包含数组和单个成员:
union something {
struct quad {
int x;
int y;
int z;
int t;
};
int array [4];
};
这将避免严格的别名冲突,但您可能仍然具有填充字节。您可以使用静态断言检测到它。
另一个问题仍然存在,那就是你不能在指向结构的第一个成员的< code>int*上使用指针算法,因为在加法运算符的指定行为中列出了各种模糊的原因——它们要求指针指向一个数组类型。
避免所有这些的最佳方法是简单地使用上面联合的数组成员。这与静态断言一起产生定义明确,坚固耐用且可移植的代码。
(理论上,您也可以使用指向字符类型的指针来遍历结构——不像int*
,这是允许的6.3.2.3/7.但如果您对单个字节不感兴趣,这是一个更混乱的解决方案。)
怎么判断一个数组是否是以下数据结构,并且判断这些字段名都得存在,可以多但是以下字段必须存在 希望得到结果
我有以下损坏的docker compose文件 此操作失败,并显示以下错误 问题1:如何在中合并数组?我尝试使用的语法是用于合并字典的语法 问题2:如果没有办法合并数组,有没有变通方法? 用例:我有多个服务,其中一些映射一些卷,另一些映射其他卷,另一些映射所有卷。我不想重复我自己。 谢谢你!
我有一个支持以下操作的数据结构: 可以在固定时间内插入项目。对于该项,数据结构分配一个唯一的正整数。(说明:指定的整数不是插入项的函数,用户对指定的整数没有选择权。它完全由数据结构选择。) 它是使用指针数组实现的,其中指定的整数是存储项的索引。未使用的索引以链表方式链接起来,以便进行固定时间的插入。 这种数据结构的名称是/应该是什么?
本文向大家介绍数组和结构之间的区别,包括了数组和结构之间的区别的使用技巧和注意事项,需要的朋友参考一下 在本文中,我们将了解数组和结构之间的区别。 数组 它指的是由相同/相同数据类型的元素组成的集合。 它使用下标/'[]'(方括号)来访问元素。 它是指向集合的第一个元素的指针。 数组对象无法实例化。 数组的大小基于数组中元素的数量是固定的。 此大小是元素数量与每个元素的大小的乘积。 数组中不能使用
我还尝试使用了optional(),但唯一的区别是regex包含了一个‘?’在最后。JSON断言仍然失败。 在存根中,两个结果都返回,但对于测试,我希望测试也能成功。测试断言纯粹是在每个属性的最后一次出现时生成的吗?难道没有可能在数组上使用类似'optional()'的东西吗?
问题内容: 我想有一个通用的方法,无论它是作为指针,切片还是数组提供的,都将始终返回结构值。 我对此的处理方式如下: 去游乐场 如您所见,问题出在从a 或。中获取结构。 我如何扩展上面的函数以从数组或切片中获取struct的值? 更新:我要做的就是将变成。 问题答案: 如果你只是想要的类型,即使片是零,你可以使用像这样: 关于,来自http://golang.org/pkg/reflect/#Ty