38

作为 C 的新手,我很困惑何时投射指针实际上是可以的。

据我了解,您几乎可以将任何指针类型转换为任何其他类型,编译器会让您这样做。例如:

int a = 5;
int* intPtr = &a;
char* charPtr = (char*) intPtr; 

但是,通常这会调用未定义的行为(尽管它恰好在许多平台上工作)。这就是说,似乎有一些例外:

  • 你可以void*自由地来回投射(?)
  • 你可以char*自由地来回投射(?)

(至少我在代码中见过它......)。

那么指针类型之间的哪些强制转换不是C 中未定义的行为?

编辑:

我尝试研究 C 标准(“6.3.2.3 指针”部分,位于http://c0x.coding-guidelines.com/6.3.2.3.html),但除了关于void*.

编辑2:

只是为了澄清:我明确地只询问“正常”指针,即询问函数指针。我意识到转换函数指针的规则非常严格。事实上,我已经问过这个问题了:-):如果我转换一个函数指针,改变参数的数量会发生什么

4

4 回答 4

35

基本上:

  • aT *可以自由转换为 avoid *并再次返回(其中T *不是函数指针),您将获得原始指针。
  • aT *可以自由转换为 aU *并再次转换回来(其中T *U *不是函数指针),如果对齐要求相同,您将获得原始指针。如果不是,则行为未定义。
  • 函数指针可以自由转换为任何其他函数指针类型并再次转换回来,您将获得原始指针。

注意:( T *对于非函数指针)始终满足char *.

重要提示:这些规则都没有说明如果您将 a 转换为 a然后尝试取消引用它会发生什么。这是标准的一个完全不同的领域。T *U *

于 2011-01-26T21:52:55.857 回答
9

Oli Charlesworth 的优秀答案列出了将指针转换为不同类型的指针会产生明确定义的结果的所有情况。

此外,在四种情况下,转换指针会产生实现定义的结果:

  • 您可以将指针强制转换为足够大 (!) 的整数类型。C99 具有可选类型intptr_tuintptr_t为此目的。结果是实现定义的。在将内存寻址为连续字节流的平台上(大多数现代平台使用的“线性内存模型”),它通常返回指针指向的内存地址的数值,因此只是一个字节数。但是,并非所有平台都使用线性内存模型,这就是实现定义的原因:-)。
  • 相反,您可以将整数转换为指针。如果整数的类型足够大,intptr_t或者uintptr_t是通过转换指针创建的,则将其转换回相同的指针类型将返回该指针(但可能不再有效)。否则结果是实现定义的。请注意,实际上取消引用指针(而不是仅仅读取它的值)可能仍然是 UB。
  • 您可以将指向任何对象的指针强制转换为char*. 然后结果指向对象的最低寻址字节,您可以通过增加指针来读取对象的剩余字节,直到对象的大小。当然,您实际获得的值又是实现定义的......
  • 您可以自由转换空指针,无论指针类型如何,它们都将始终保持空指针:-)。

来源:C99 标准,第 6.3.2.3 节“指针”和第 7.18.1.4 节“能够保存对象指针的整数类型”。

据我所知,指向不同类型指针的指针的所有其他类型转换都是未定义的行为。特别是,如果您没有强制转换为char或足够大的整数类型,则可能总是UB 将指针强制转换为不同的指针类型 - 即使没有取消引用它。

这是因为类型可能具有不同的对齐方式,并且没有通用的、可移植的方法来确保不同类型具有兼容的对齐方式(除了一些特殊情况,例如有符号/无符号整数类型对)。

于 2011-01-27T15:59:48.667 回答
5

通常,如果现在指针本身具有相同的对齐属性,那么问题不在于转换本身,而在于您是否可以通过指针访问数据。

任何对象类型都可以保证转换任何类型:这保证T*给你返回完全相同的指针。是捕获所有对象指针类型。void*Tvoid*

对于对象类型之间的其他类型转换,无法保证,通过这样的指针访问对象可能会导致各种问题,例如对齐(总线错误)、整数的陷阱表示。甚至不保证不同的指针类型具有相同的宽度,因此理论上您甚至可能会丢失信息。

但是,应该始终有效的一种演员是 to (unsigned char*)。通过这样的指针,您可以调查对象的各个字节。

于 2011-01-26T22:04:57.117 回答
0

该标准的作者没有尝试权衡在支持成本很高的平台上的大多数指针类型组合之间支持转换的成本和收益,因为:

  1. 大多数此类转换成本高昂的平台可能都是标准作者不知道的晦涩难懂的平台。

  2. 使用此类平台的人将比标准的作者更好地了解此类支持的成本和收益。

int*如果某些特定平台对and使用不同的表示形式double*,我认为标准会故意允许从double*toint*和 back to的循环转换double*将始终如一地工作,但从int*todouble*和 back to 的转换int*可能会失败。

我不认为该标准的作者打算让此类操作在此类转换不花费任何成本的平台上失败。他们在章程和基本原理文件中将 C 精神描述为包括“不要阻止(或不必要地阻碍)程序员做需要做的事情”的原则。鉴于该原则,该标准没有必要强制要求实现以某种方式处理操作,以帮助程序员完成他们需要做的事情,因为这样做不会花费任何成本,因为真正努力维护的实现无论有没有授权,C 精神都会以这种方式行事。

于 2018-12-03T16:36:57.827 回答