c/c++-是否从0偏移UB写入多维数组?[副本]

knsnq2tg  于 2023-08-03  发布在  C/C++
关注(0)|答案(2)|浏览(121)

此问题在此处已有答案

One-dimensional access to a multidimensional array: is it well-defined behaviour?(4个答案)
May I treat a 2D array as a contiguous 1D array?(6个答案)
10天前关闭。
请检查下面的代码:

#include "stdio.h"

#define N 2
#define M 2

int main(void)
{
    int two_d[N][M];
    for(size_t i = 0; i < N*M; ++i) {
        two_d[0][i] = i;  // <---- Pay attention to this line!
    }
    for(size_t i = 0; i < N; ++i) {
        for(size_t j = 0; j < M; ++j) {
            printf("%d\n", two_d[i][j]);
        }
    }
    return 0;
}

字符串
请不要对这个例子持怀疑态度,并迅速判断它是人为的--这个例子是你在一个非常真实的和非常知名的项目中发现的(这个项目非常有名)。
我希望有一个好的语言律师的电话号码!
1.一方面,内存保证是按顺序布局的,所以我一般不会访问对象之外的任何东西;
1.另一方面,我显然是在访问第一个1d数组之外的内存--这样做是UB。
示例在我的机器上编译并运行良好。Godbolt先生展示了C和C++编译器都做同样的事情,并且通过优化都处理like a doctor

所以,问题是

1.这在C中法律的吗?
1.这在C++中法律的吗?
标准报价将不胜感激。

46scxncf

46scxncf1#

在C++中,下标表达式的含义在expr.sub中给出:
使用内置的下标运算符,应该存在一个 expression-list,由单个 assignment-expression 组成。其中一个表达式应该是类型为“array of T”的左值或类型为“pointer to T”的右值,另一个表达式应该是无作用域枚举或整型的右值。结果的类型为“T”。类型“T”应为完全定义的对象类型。表达式E1[E2]*((E1)+(E2))相同(根据定义),除了在数组操作数的情况下,如果该操作数是左值,则结果是左值,否则是x值。
关于+在expr.add中的规则:
当一个整型的表达式J与一个指针型的表达式P相加或相减时,结果的类型为P

  • 如果P的计算结果为空指针值,而J的计算结果为0,则结果为空指针值。(4.2)
  • 否则,如果P指向具有 n 个元素的数组对象x的数组元素 i([dcl.array]),则表达式P + JJ + P(其中J具有值 j)如果0 <= i + j <= n 则指向x的(可能-假设的)数组元素 i + j,并且表达式P - J如果0 <= i - j <= n 则指向x的(可能-假设的)数组元素 i - j
    *否则行为未定义

您的代码段调用了未定义的行为。
C中,规则非常相似。从6.5.2.1/2开始,数组下标:
后缀表达式后跟方括号中的表达式[]是数组对象元素的下标指定。下标运算符[]的定义是E1[E2](*((E1)+(E2)))相同。由于适用于二进制+运算符的转换规则,如果E1是数组对象(相当于指向数组对象初始元素的指针),E2是整数,则E1[E2]指定E1的第E2个元素(从零开始计数)。
然后,根据6.5.6/8,加法运算符:
当一个整数类型的表达式与指针相加或相减时,结果具有指针操作数的类型。如果指针操作数指向数组对象的元素,并且数组足够大,则结果指向与原始元素偏移的元素,使得结果和原始数组元素的下标之差等于整数表达式。换句话说,如果表达式P指向数组对象的第 i 个元素,则表达式(P)+N(相当于N+(P))和(P)-N(其中N的值为 n)分别指向数组对象的第 i+n 个元素和第 i−n 个元素,前提是它们存在。此外,如果表达式P指向数组对象的最后一个元素,则表达式(P)+1指向数组对象的最后一个元素之后的一个元素,如果表达式Q指向数组对象的最后一个元素之后的一个元素,则表达式(Q)-1指向数组对象的最后一个元素。如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象最后一个元素之后的元素,则计算不应产生溢出;否则,行为未定义。如果结果指向数组对象的最后一个元素之后的一个元素,则不应将其用作计算的一元*运算符的操作数。
就像在C++中一样,超出数组边界是未定义的行为,对于“但是如果旁边有另一个数组怎么办”没有特殊的豁免。

mlnl4t2r

mlnl4t2r2#

这在C中法律的吗?
这在C++中法律的吗?
不,两个都是UB
int array[x][y]x阵列的阵列,具有yint元素。如果第二个下标是>= y,那么您访问y元素int数组的边界之外。
'C'中,您可以通过使用union来防止UB。

#define N 2
#define M 2

int main(void)
{
    union
    {
        int two_d[N][M];
        int one_d[N*M];
    }u;

    for(size_t i = 0; i < N*M; ++i) {
        u.one_d[i] = i; 
    }
    for(size_t i = 0; i < N; ++i) {
        for(size_t j = 0; j < M; ++j) {
            printf("%d\n", u.two_d[i][j]);
        }
    }
    return 0;
}

字符串

相关问题