考虑
T1 *p1;
T2 *p2;
我们可以将p1分配给p2还是相反?如果是这样,可以在不进行强制转换的情况下完成此操作,还是必须使用强制转换?
答案 0 :(得分:2)
当类型T1
和T2
不同时,T1 *p1
和T2 *p2
之间的分配被正式禁止,除非T1
和{{1 }}是T2
,另一个是对象(不是函数)类型。
在许多情况下,不兼容的分配实际上会起作用,尤其是在地址空间为“扁平”且所有指针类型共享相同内部表示的机器(例如当今所有流行的机器)上。
但是,在分配“混合模式”指针之后,由于(1)对齐问题和(2)strict aliasing,所以取消引用指针时很可能会出现问题。
由于“混合模式”指针分配在形式上是非法的,并且通常是一个坏主意,因此大多数编译器都会对其进行警告。大多数编译器允许使用显式强制转换来禁止警告。在大多数情况下,强制转换仅用于抑制警告。它不会引入任何无法执行的实际转换。 (也就是说,将void
更改为p1 = p2
就像将p1 = (T1 *)p2
更改为i = f
,其中i = (int)f
是一个整数,i
是一个整数浮动。)
附录:我写道:“当类型f
和T1
不同时”,但是更精确的说法是它们不兼容。例如,类型T2
和char
是兼容的,因此可以在这些类型的指针之间进行分配。有关更多详细信息,请参见Eric Postpischil的较长答案。
答案 1 :(得分:2)
首先,让我们考虑不进行强制转换的分配。 C 2018 6.5.16.1 1列出了简单分配的约束,表示其中之一必须成立。前两个用于算术,结构和联合类型。最后两个处理涉及空指针常量或_Bool
。中间的两个用于分配指向指针的指针:
左操作数具有原子,合格或不合格的指针类型,并且…两个操作数都是指向兼容类型的合格或不合格版本的指针,并且左侧指向的类型具有所指向类型的所有限定符到右边
左操作数具有原子,限定或非限定的指针类型,并且…一个操作数是指向对象类型的指针,另一个是指向void的限定版本或非限定版本的指针,并且类型指向到左侧的所有限定词都由右侧指向
后者表示只要没有限定符(void *
,const
,volatile
或{{ 1}})。
前者说,只要不删除限定符,就可以将指针分配给兼容类型。什么是兼容类型?
6.2.7 1说:
6.7.2 4说每个枚举类型(restrict
类型)都与实现定义的选择__Atomic
或有符号或无符号整数类型兼容。因此,可以将指向某个enum
的指针分配给指向某个char
或整数类型的指针(反之亦然),但是如果您不了解特定的C实现,就无法知道哪个指针。>
6.7.3 11说,限定类型必须具有相同的限定符才能兼容。因此,enum
与char
不兼容,这会阻止将int
分配给const int
。
6.7.6.1 2说要使两个指针类型兼容,它们必须是指向兼容类型的相同限定的指针。例如,这告诉我们int *
与const int *
不兼容,因此,由于上述分配约束,int *
可能未分配给char *
。
6.7.6.2 6表示,要使两个数组类型兼容,它们必须具有兼容的元素类型,并且如果它们都具有整数常量大小,则它们必须相同。 (这允许大小未知的数组与大小已知的数组兼容。但是,其他文字说,如果数组最终具有不同的大小,则在要求它们兼容的上下文中使用它们具有未定义的行为。因此,指向此类数组的指针分配可能会满足其约束条件,并且编译时不会出错,但是结果程序可能会出现异常。)
6.7.6.3 15为函数类型的兼容性提出了一些复杂的规则。这些规则很复杂,因为可以使用或不使用参数列表,使用椭圆等来声明函数。我将省略对它们的完整讨论。
这些规则告诉您在不进行强制转换的情况下可以进行哪些指针分配。
6.5.4讨论演员表。它的约束条件并不限制可以将哪些指针类型转换为其他指针类型。 (它们确实禁止涉及指针的其他事情,例如将指针类型转换为浮点类型。)因此,您可以在转换中指定所需的任何指针转换,并且只要结果类型与该类型兼容即可,分配,不违反分配或强制转换约束。但是,仍然存在关于转换是否正确的问题。
6.3.2.3指定指针转换的规则。那些处理从指针到指针(不包括整数和空指针常量)的转换的人说:
任何指向对象类型(而不是函数类型)的指针都可以转换为指向void的指针,反之亦然。将对象指针转换为void指针然后返回的结果与原始指针相等。
一个指针可能会被转换为具有更多限定符的相同类型,并且结果将与原始结果进行比较。
如果结果指针针对其类型正确对齐(否则,行为未定义),则可以将指向对象类型的指针转换为指向不同对象类型的指针。转换回时,结果比较等于原始指针。 (请注意,虽然允许您进行此转换,但此规则并不表示可能会使用作为结果的指针来访问新类型的对象。C中还有与此相关的其他规则。)
指向一个函数类型的指针可以转换为指向另一个函数类型的指针。转换回时,结果比较等于原始指针。 (与对象一样,也可以进行此转换,但是使用结果指针来调用不兼容的函数具有未定义的行为。)
因此,在使用强制转换时,只要满足对齐要求,就可以将任何对象指针类型转换为任何对象指针类型并进行分配,并且可以将任何函数指针类型转换为任何函数指针类型并进行分配