gpt4 book ai didi

c - C99 是否保证数组是连续的?

转载 作者:太空狗 更新时间:2023-10-29 16:30:38 29 4
gpt4 key购买 nike

在另一个问题的热门评论线程之后,我开始讨论 C99 标准中关于 C 数组定义的内容和未定义的内容。

基本上,当我定义像 int a[5][5] 这样的二维数组时,标准 C99 是否保证它将是一个连续的整数 block ,我可以将它转换为 (int *)a 并确保我将拥有一个包含 25 个整数的有效一维数组。

根据我对标准的理解,上述属性隐含在 sizeof 定义和指针运算中,但其他人似乎不同意并表示转换为 (int*) 上述结构会产生未定义的行为(即使他们同意 所有现有的实现实际上分配了连续的值)。

更具体地说,如果我们认为一个实现会检测数组以检查所有维度的数组边界并在访问一维数组时返回某种错误,或者不能正确访问第一行以上的元素。这样的实现可以符合标准吗?在这种情况下,C99 标准的哪些部分是相关的。

最佳答案

我们应该首先检查 int a[5][5] 到底是什么。涉及的类型有:

  • 内部
  • 数组[5] 的整数
  • array[5] 个数组

不涉及整数数组[25]。

正确的是,sizeof 语义暗示数组作为一个整体是连续的。 ints的array[5]必须有5*sizeof(int),递归应用,a[5][5]必须有5*5*sizeof(int)。没有额外填充的空间。

此外,当以 sizeof 赋给 memset、memmove 或 memcpy 时,整个数组必须正常工作。还必须可以使用 (char *) 遍历整个数组。所以一个有效的迭代是:

int  a[5][5], i, *pi;
char *pc;

pc = (char *)(&a[0][0]);
for (i = 0; i < 25; i++)
{
pi = (int *)pc;
DoSomething(pi);
pc += sizeof(int);
}

对 (int *) 做同样的事情将是未定义的行为,因为如前所述,没有涉及 int 的数组 [25]。在 Christoph 的回答中使用 union 也应该是有效的。但是还有一点使这进一步复杂化,相等运算符:

6.5.9.6两个指针比较相等当且仅当它们都是空指针,都是指向同一个对象(包括指向对象和其开头的子对象的指针)或函数的指针,都是指向同一数组最后一个元素的指针对象,或者一个是指向一个数组对象末尾的指针,另一个是指向另一个数组对象的开始的指针,该对象恰好紧跟在地址空间中的第一个数组对象之后。 91)

91) 两个对象在内存中可能相邻,因为它们是更大数组的相邻元素或结构的相邻成员,它们之间没有填充,或者因为实现选择这样放置它们,即使它们不相关。如果先前的无效指针操作(例如访问数组边界之外)产生未定义的行为,则后续比较也会产生未定义的行为。

这意味着:

int a[5][5], *i1, *i2;

i1 = &a[0][0] + 5;
i2 = &a[1][0];

i1 比较等于 i2。但是当用 (int *) 遍历数组时,它仍然是未定义的行为,因为它最初是从第一个子数组派生的。它不会神奇地转换为指向第二个子数组的指针。

即使这样做

char *c = (char *)(&a[0][0]) + 5*sizeof(int);
int *i3 = (int *)c;

没用。它比较等于 i1 和 i2,但它不是从任何子数组派生的;它最多是一个指向单个 int 或 int 数组 [1] 的指针。

我不认为这是标准中的错误。恰恰相反:允许这样做会引入一种特殊情况,这种情况要么违反数组的类型系统,要么违反指针算法的规则,或者两者兼而有之。它可能被认为是缺少定义,但不是错误。

因此,即使 a[5][5] 的内存布局与 a[25] 的布局相同,并且可以使用使用 (char *) 的相同循环来迭代两者,一个实现是如果一个用作另一个,则允许爆炸。我不知道为什么它应该或知道任何实现,也许标准中有一个直到现在才提到的事实使它成为明确定义的行为。在那之前,我会认为它是未定义的并保持安全。

关于c - C99 是否保证数组是连续的?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2832970/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com