代码之家  ›  专栏  ›  技术社区  ›  gulpr

是否定义了将“类型的数组指针”转换为“类型指针”?

  •  3
  • gulpr  · 技术社区  · 2 年前

    如本例所示:

    #include <stdio.h>
    
    void foo(int (*p)[5])
    {
        int *i = (int *)p;
        printf("%d\n", *i);
    }
    

    它被定义了吗?IMO不是。

    问题灵感来自 this question


    请记住,此问题已标记 language-lawyer

    1 回复  |  直到 2 年前
        1
  •  3
  •   Eric Postpischil    2 年前

    C 2018标准中没有定义这一点,因此行为是未定义的(4 2:未定义的行为在本文件中用未定义行为或省略任何明确的行为定义来表示。)。

    演员阵容进行转换(6.5.4.5)。转换在6.3中有规定。指针转换在6.3.2.3中有规定。第1段涵盖指向或来自的指针 void * ,这里的情况并非如此。第2段涵盖了同一类型的不同限定版本之间的转换,但这里的情况并非如此。第3段介绍了空指针常量的转换,这里的情况并非如此。第4段介绍了空指针的转换,这里的情况并非如此。第5段介绍了整数到指针的转换,这里的情况并非如此。第6段涉及到整数的转换,这里的情况并非如此。第8段介绍了指针到函数类型的转换,这里的情况并非如此。

    只剩下第7段:

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

    这适用于我们的情况, 但它并没有告诉我们结果的价值 .它告诉我们,当我们将结果转换回原始类型时,我们将具有与原始指针相等的值。但可以想象的是,转换后的值是不适合用于解引用的某种编码形式。

    例如,想象一个为调试而设计的C版本,其中所有指针都插入了出处信息。指向的指针 int [2] 可能是16个字节,其中4个字节包含地址,12个字节包含源于类型的指针的信息 int[2] .将其转换为 int * 可以产生具有完全相同字节的指针,并且将其转换回将产生相同的字节。由于这是C的调试实现,因此每次取消引用指针时,它都会检查存储在这些字节中的指针出处。当我们使用 *p ,它看到字节表示指针起源于指向的指针 int[2] p 是那种类型,所以它允许取消引用并且程序继续。

    然而,当我们使用 *i ,它看到字节表示指针起源于指向的指针 int[2] 但是 i int* ,因此它会打印一条错误消息并停止程序。

    这个假设程序符合C标准:

    • 根据6.3.2.3.7的规定,可以进行转换。
    • 如6.3.2.3.7所述,将指针转换回与原始指针相等的指针,我们可以像使用原始指针一样使用该指针。
    • 将指针用作 int* 没有指定工作,也没有工作,因此符合标准。

    进一步注意,标准中指定了将指针转换为指向字符类型的指针并使用它来访问表示对象的字节,但我们假设的C实现可以允许这样做。它只是批准使用任何指向对象类型的指针来访问字符值,即使出处说它起源于其他类型。

    人们可能会相信 打算 标准的一部分是允许转换后的指针用作指向其新类型的指针,但是 它没有明确地说 因此,该行为在技术上不是由C标准定义的。