如本例所示:

#include <stdio.h>

void foo(int (*p)[5])
{
    int *i = (int *)p;
    printf("%d\n", *i);
}

定义了吗?IMO它不是.

问题启发this question


请记住,这个问题是100

推荐答案

C 2018标准中没有任何定义,因此行为是未定义的(4 2:"未定义的行为在本文中以"未定义的行为"或省略任何明确的行为定义来表示.

转换执行转换(6.5.4 5).转换在6.3中详细说明.指针转换在6.3.2.3中规定.第1段涵盖指向或指向void *的指针,但此处并非如此.第2款涵盖同一类型不同限定版本之间的转换,但此处并非如此.第3段介绍了空指针常量的转换,这里不是这样的.第4段涉及空指针的转换,这里不是这样.第5段介绍了整数到指针的转换,这里不是这样的.第6段涉及到整数的转换,这里不是这样.第8段介绍了指针到函数类型的转换,这里不是这样的.

现在只剩下第7段:

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

这也适用于我们的情况,but it does not tell us what the value of the result is.它告诉我们,当我们将结果转换回原始类型时,我们将有一个与原始指针等效的值.但是可以想象,转换后的值是某种编码形式的,不适合用于解引用.

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

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

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

  • 允许转换,如www.example.com 7指定的.
  • 将指针转换回原来的指针,我们可以像使用原始指针一样使用它,正如www.example.com 7所指定的那样.
  • 使用指针作为int *没有指定工作,它不工作,所以这符合标准.

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

人们可能认为标准的intention是允许转换后的指针用作指向它们新类型的指针,但it does not explicitly say that.因此,从技术上讲,这种行为并不是由C标准定义的.

C++相关问答推荐

数组元素的编号索引

malloc实现:判断正确的分配对齐

从内联程序集调用Rust函数和调用约定

特定闪存扇区的内存别名

带有sigLongjMP中断I/O的异常处理程序

平均程序编译,但结果不好

在句子中转换单词的问题

MacOS下C++的无阻塞键盘阅读

通过k&;r语法的c声明无效

为什么电路板被循环删除?

不确定如何处理此编译错误

如何在C中用bool进行文件I/O?

如何组合两个宏来初始化C语言中的字符串数组?

是否有单独的缓冲区用于读写库调用?

c如何传递对 struct 数组的引用,而不是设置 struct 的副本

GCC认为这是一个VLA是对的吗?

在列表中查找素数

函数的typedef是标准 C 语法吗?它与函数指针的typedef有何不同?

尽管将其标记为易失性,但 gcc 是否优化了我的等待代码?

SSE 向量与 Epsilon 的比较