C 中将指向数组的指针作为指向 UB 的指针传递吗?

k1r*_*1t0 4 c pointers undefined-behavior language-lawyer implicit-conversion

我有这样的代码:

#include <stdlib.h>
#include <stdio.h>

void func(int **b)
{
    printf("b = %p\n", b); // 0x7ffe76932330
    *b = *b + 1;
}

int main(void)
{
    int b[10] = {0};

    printf("b = %p\n", &b[0]); // 0x7ffe76932330
    printf("%d\n", b[0]);      // 0

    func(&b);

    printf("%d\n", b[0]); // 4
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

这个代码有UB吗?对我来说似乎是这样,至少由于没有显式强制转换的不同类型int (*)[10] != int **

另外,如果我有呢char b[] = "some string";?行为几乎相同......奇怪。

dbu*_*ush 5

单独传递指针不一定是未定义的行为,但随后使用转换后的指针则是未定义的行为。

C 允许从一种对象类型到另一种对象类型的相互转换,如C 标准第 6.2.3.2p7 节中所述:

指向对象类型的指针可以转换为指向不同对象类型的指针。如果生成的指针未针对引用类型正确对齐,则行为未定义。否则,当再次转换回来时,结果应等于原始指针。当指向对象的指针转换为指向字符类型的指针时,结果指向该对象的最低寻址字节。结果的连续增量,直到对象的大小,产生指向对象的剩余字节的指针。

因此,假设不存在对齐问题(即数组在 64 位系统上以 8 字节偏移量开始),则仅允许将 a 传递int (*)[10]给需要 an 的函数的操作int **,尽管大多数编译器会警告有关转换不兼容的指针类型。

未定义的行为发生在这里:

*b = *b + 1;
Run Code Online (Sandbox Code Playgroud)

因为您通过不兼容的指针类型(除了 a char *)来引用对象。第 6.5p7 节列出了有关允许取消引用的规则:

对象的存储值只能由具有以下类型之一的左值表达式访问:

  • 与对象的有效类型兼容的类型,
  • 与对象的有效类型兼容的类型的限定版本,
  • 与对象的有效类型相对应的有符号或无符号类型,
  • 与对象有效类型的限定版本相对应的有符号或无符号类型的类型,
  • 聚合或联合类型,其成员中包含上述类型之一(递归地包括子聚合或包含联合的成员),或者
  • 一种字符类型。

将 a 取消int (*)[10]引用为 aint **不满足上述任何条件,因此*b未定义的行为也是如此。