为啥/何时使用 `intptr_t` 在 C 中进行类型转换?
Posted
技术标签:
【中文标题】为啥/何时使用 `intptr_t` 在 C 中进行类型转换?【英文标题】:Why / when to use `intptr_t` for type-casting in C?为什么/何时使用 `intptr_t` 在 C 中进行类型转换? 【发布时间】:2011-09-13 15:45:32 【问题描述】:我有一个关于使用intptr_t
与long int
的问题。我观察到递增内存地址(例如通过手动指针算法)因数据类型而异。例如,增加一个 char 指针会将 1 添加到内存地址,而增加一个 int 指针会增加 4、8 用于双精度,16 用于长双精度等...
起初我做了这样的事情:
char myChar, *pChar;
float myFloat, *pFloat;
pChar = &myChar;
pFloat = &myFloat;
printf( "pChar: %d\n", ( int )pChar );
printf( "pFloat: %d\n", ( int )pFloat );
pChar++;
pFloat++;
printf( "and then after incrementing,:\n\n" );
printf( "pChar: %d\n", (int)pChar );
printf( "pFloat: %d\n", (int)pFloat );
编译和执行都很好,但是 XCode 给了我关于类型转换的警告:“从指针转换为不同大小的整数。”
经过一番谷歌搜索(后者是一个词吗?),我看到有人推荐使用intptr_t
:
#include <stdint.h>
...
printf( "pChar: %ld\n", ( intptr_t )pChar );
printf( "pFloat: %ld\n", ( intptr_t )pFloat );
这确实解决了错误。所以,我想,从现在开始,我应该使用intptr_t
来进行类型转换指针...但是经过一番折腾,我发现我可以通过将int
替换为long int
来解决问题:
printf( "pChar: %ld\n", ( long int )pChar );
printf( "pFloat: %ld\n", ( long int )pFloat );
所以我的问题是,为什么intptr_t
有用,什么时候应该使用它?在这种情况下,这似乎是多余的。显然,myChar
和 myFloat
的内存地址太大而无法放入 int
... 所以将它们类型转换为 long int
s 解决了这个问题。
是否有时内存地址对于long int
来说也太大了?现在我考虑了一下,我想如果你有 > 4GB 的 RAM,这是可能的,在这种情况下,内存地址可能超过 2^32 - 1(无符号长整数的最大值......)但 C 早在那之前就被创建了可以想象,对吧?还是他们有那么有先见之明?
谢谢!
【问题讨论】:
是的,binging 是一个词,最初的意思是沉迷于某项活动,尤其是饮食,过度。 将intptr_t
与 printf 函数一起使用是不可移植的,它没有格式说明符。将指针转换为 void 指针并使用 %p
格式说明符。
【参考方案1】:
intptr_t
是一项新发明,是在设想 64 位甚至 128 位内存地址之后创建的。
如果您曾经需要将指针转换为整数类型,总是使用intptr_t
。做任何其他事情都会给将来需要移植您的代码的人带来不必要的问题。
当人们想在 64 位 Linux 上编译它时,需要很长时间才能消除 Mozilla/Firefox 等程序中的所有错误。
【讨论】:
感谢 Zan 的有用回答。 Firefox 不知道这一点。听起来很糟糕。 最好使用uintptr_t
。带符号的类型很乱,用指针肯定没有意义......
谈到 always 使用 intptr_t
这个答案声称它没有被广泛使用 ***.com/a/9492910/1073672 。那该怎么办呢?
@user10607:从 C99 开始提供。这已经超过了十年。因此,除非您使用像 TurboC 这样的古老编译器或非常奇特的平台,没有 int 来保存指针,否则它将可用;)
@MestreLion 这不是真的,它在 C99 中是可选的【参考方案2】:
事情是这样的:在某些平台上,int
是正确的大小,但在其他平台上,long
是正确的大小。你怎么知道你应该使用哪一个?你没有。一个可能是正确的,但标准不保证它会是哪一个(如果是的话)。因此,该标准提供了一种定义为正确大小的类型,无论您使用的是什么平台。你必须在哪里写:
#ifdef PLATFORM_A
typedef long intptr;
#else
typedef int intptr;
#endif
现在你只需写:
#include <stdint.h>
它涵盖了更多的案例。想象一下,为您的代码运行的每个平台专门设置上面的 sn-p。
【讨论】:
嗯,感谢您的澄清。如果您一直使用long
会怎样? long
也不能使用较短的地址吗? long
s 与 int
s 的额外内存分配是否会显着影响性能?
@caravaggisto - 不。 long
仅保证为 32 位,可能不适用于 64 位平台。您总是可以使用long long
,但在 C99 之前不存在。我怀疑分配很重要。更多的是正确性问题。
我会非常怀疑任何教你在指针和整数之间进行转换的 C 书籍。实际上几乎所有的演员表都表明了错误。它们至少是一种代码味道。指针/整数转换更糟糕。
@R..:通常一个提供回调函数的 API 或像 Windows 消息泵这样的消息传递 API 会有一个或两个可以传递的整数数据字段。您的程序可能需要将指针填充到 DWORD
或 int
中,因为这就是 API 中可用的全部内容。
@ZanLynx WinAPI 是一个糟糕的 API,专为 80 年代中期严重损坏的标准 C 编译器而设计。自 Win16 时代以来,它并没有太大变化。 MS试图杀死它是有原因的。【参考方案3】:
首先,intptr_t
仅用于数据指针(不是函数),不保证存在。
那么,不,您不应该将其用于打印目的。 %p
就是为了这个。您只需将指针指向(void*)
即可。
对于算术/访问单个字节也没有好处。改为转换为 (unsigned char*)
。
intptr_t
确实适用于您必须将指针解释为整数的极少数情况(实际上并非如此)。如果不能,就不要那样做。
【讨论】:
谢谢。那么,如果不是(二进制)整数,那么指针是什么?感谢您的宝贵时间。 @caravaggisto,这在很大程度上取决于架构。一些架构具有分段内存,因此地址由两部分组成。例如,简单的整数加法可能并不总是给你一个有效的地址。unsigned char
上的算术就可以了,只要你分配了一个足够大的块。
intptr_t
或 uintptr_t
在某些情况下可能很有用。例如,您正在构建自己的malloc()
版本,这是一个专门的动态内存分配器,可能用于需要专门内存分配器的嵌入式系统或其他计算机系统。假设您在显式空闲链表中跟踪已释放的块。您可以使用 intptr_t 来存储指向链表中下一个和上一个块的指针。这不是有效负载本身的一部分,而是它之前的前一个单词,在块的“页眉”或“页脚”中。【参考方案4】:
您可以使用p
转换说明符让您的生活更轻松:
printf("%p\n", (void *)foo);
此外,打印(u)intptr_t
类型变量的可移植方式是使用来自inttypes.h
的PRI*PTR
宏;以下相当于在我的平台(32位)上使用p
:
printf("%08" PRIxPTR "\n", (uintptr_t)(void *)foo);
void *
的强制转换是完全可移植性所必需的,但在具有统一指针表示的平台上可以省略。
【讨论】:
虽然这可能应该是对问题的评论。以上是关于为啥/何时使用 `intptr_t` 在 C 中进行类型转换?的主要内容,如果未能解决你的问题,请参考以下文章