这是在 C 中写入数组的真正未定义行为吗?

Is this write to an array truly undefined behavior in C?

如果一个数组超过另一个数组的末尾,则此代码通过指针写入一个值。

#include <stdio.h>
#include <inttypes.h>

extern int first[], second[];

#define ADDR_AFTER(ptr) ((uintptr_t)((ptr) + 1))

int test(int *an_int) {
    *second = 1;
    if (ADDR_AFTER(first) == (uintptr_t)an_int) {
        // ubsan does not like this.
        *an_int = 2;
    }
    return *second;
}

int first[1] = {0}, second[1] = {0};

int main() {
    if (ADDR_AFTER(first) == (uintptr_t)second) {
        printf("test: %d\n", test(second));
        printf("x: %d y: %d\n", *first, *second);
    }
}

我绝不会直接比较两个指向不同对象的指针(因为我先将它们转换为 uintptr_t)。我创建了一个指向数组末尾后一位的指针(这是合法的),但我从不取消引用该指针。据我所知,这应该不打印任何内容,或者打印:

test: 2
x: 0 y: 2

优化为 -O1 或更低时在 Clang 上打印。然而,在 -O2,它打印:

test: 1
x: 0 y: 2

使用 -O2 -fsanitize=undefined 将其打印到标准输出:

test: 2
x: 0 y: 2

并将以下内容发送到 stderr:

runtime error: store to address 0x000000cd9efc with insufficient space for an object of type 'int'
0x000000cd9efc: note: pointer points here
  00 00 00 00 01 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00  00 00 00 00 00 00 00 00

参考 test 中对 an_int 的赋值。这实际上是未定义的行为,还是 Clang 中的错误?

您的代码没有任何无效之处,编译器有误。如果您删除不必要的 ADDR_AFTER 签入 test(),代码 运行 将按预期显示,没有 UBSan 错误。如果你 运行 它启用了优化但没有 UBSan,你会得到错误的输出(test=1,应该是 2)。

test() 中的 ADDR_AFTER(first) == (uintptr_t)an_int 代码使 Clang 在使用 -O2 编译时做错事。

我用 Apple clang version 11.0.3 (clang-1103.0.32.62) 测试过,但看起来 Clang 13 和当前的 t运行k 也有错误:https://godbolt.org/z/s83ncTsbf - 如果你将编译器更改为任何版本的 GCC,你''ll see it can return 1 or 2 from main(), while Clang always returns 1 (mov eax, 1).

您可能应该为此提交一个 Clang 错误。