目录
- 一、intptr_t 和 uintptr_t 详解
- 二、适用场景
- 三、示例
- 四、写在最后
一、intptr_t 和 uintptr_t 详解
intptr_t 和 uintptr_t,这两个数据类型是ISO C99定义的。主要用于处理指针和整数之间的转换。它们在需要将指针转换为整数进行操作,或者将整数转换回指针的场景中非常有用。
具体代码在linux平台的/usr/include/stdint.h头文件中,见下图。
先介绍一下上图中__WORDSIZE 的含义
__WORDSIZE的含义
在计算机科学中,__WORDSIZE是一个与处理器相关的编译器宏,它通常用于标识目标处理器的数据字长,即处理器内部操作的数据宽度。这个宏的值通常等于处理器的指令集架构(ISA)中规定的数据宽度,比如32位或64位。在不同的编译器和操作系统中,__WORDSIZE的具体含义和使用可能有所不同。
在64位系统中
在64位系统中,__WORDSIZE通常被定义为64,表示处理器可以处理64位的数据。这意味着,64位的寄存器可以存储64位整数,指针也可以表示64位的虚拟地址空间。
在32位系统中
而在32位系统中,__WORDSIZE则被定义为32,表示处理器处理的数据宽度为32位。这包括32位的通用寄存器和32位的虚拟地址空间。
__WORDSIZE在编程中的应用
了解__WORDSIZE的值对于编写高效的代码非常重要。例如,在编译代码时,可以根据__WORDSIZE的值来优化数据类型和操作,以充分利用处理器的性能。在某些情况下,程序员可能会根据__WORDSIZE的值来决定是否使用64位整数运算或者指针操作。
根据上图 stdint.h 中的代码,我们可以看到
当是64位系统的情况下
typedef long int intptr_t;
typedef unsigned long int uintptr_t;
不是64位系统的情况下
typedef int intptr_t;
typedef unsigned int uintptr_t;
为什么会这么定义呢?
先看下不同的数据类型在不同字长机器上长度大小。
系统位数 | char | short | int | long | 指针 |
---|---|---|---|---|---|
16 | 1个字节8位 | 2个字节16位 | 2个字节16位 | 4个字节32位 | 2个字节16位 |
32 | 1个字节8位 | 2个字节16位 | 4个字节32位 | 4个字节32位 | 4个字节32位 |
64 | 1个字节8位 | 2个字节16位 | 4个字节32位 | 8个字节64位 | 8个字节64位 |
由上表可以看到,指针在32位平台和64位平台下指针均与long 类型的长度一致,然而在16位机器上,long为4个字节,而指针为2个字节。
因此,就可以发现intptr_t和uintptr_t定义的巧妙之处:在64位机器上,intptr_t为long int,uintptr_t为unsigned long int。而在非64位机器上,intptr_t为int,uintptr_t为unsigned int。
这样就可以保证intptr_t和uintptr_t的长度与机器的指针长度一致,因此在进行整数与指针的相互转换时可以用intptr_t和uintptr_t进行过渡。
二、适用场景
适用场景主要包括以下几种:
1、指针与整数的安全转换
存储指针值: 如果需要存储指针的值,尤其是在需要进行序列化或者临时存储时,使用 uintptr_t(无符号类型)能够确保不会出现符号相关的问题。
与算术运算结合: 若需要对指针值进行算术运算,比如加减操作,使用 intptr_t 可以将指针转换为整型后进行有符号运算,方便按需调整内存地址。
2、数据结构的实现
自定义数据结构: 在实现链表、哈希表、树等数据结构时,可以使用 intptr_t 或 uintptr_t 来存储节点的指针值。这使得在处理指针和索引时能够避免类型不匹配的问题。
3、指针的自由操作
位操作: 在需要进行位级操作时,可以将指针转换为 uintptr_t,这样可以以无符号整数的形式使用,加快处理效率。同时,避免了由于有符号运算可能导致的错误。
4、在底层与操作系统交互
系统调用和库函数: 在与操作系统的底层 API 或特定库函数交互时,常常需要将指针类型转为整数。此时,使用 intptr_t 和 uintptr_t 能够保证所需指针值的安全传递。
5、跨平台兼容性
增强代码的可移植性: 在编写需要跨平台运行的程序时,使用这两个类型能够保证指针和整型之间的转换在不同的平台上都能保持稳定性。一些平台可能在指针大小上有所不同,使用这两个类型可以避免因数据大小导致的错误。
6、处理回调和异步操作
上下文处理: 在使用回调函数或异步通知时,传递上下文信息时可以将指针转换为 intptr_t 或 uintptr_t。这样在处理时,可以将指针信息安全地从一个上下文传递到另一个上下文。
三、示例
下面举两个示例,来演示如何使用intptr_t 和 uintptr_t。
示例1
demo0.c
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[]){
int a = 9;
int *p = &a;
int ptr = (int )p;
printf("Pointer as integer: %d\n",ptr);
return 0;
}
编译时候gcc给出警告
Wpointer-to-int-cast
意思是将指针转换为整型,二者大小不匹配,
修改后的正确代码如下
#include <stdio.h>
#include <stdint.h>
int main(int argc, char *argv[]) {
int value = 42;
int *p = &value; // 指向整型的指针
// 正确的转换:将指针转换为 uintptr_t
uintptr_t ptr = (uintptr_t)p;
printf("Pointer as integer: %lu\n", ptr);
return 0;
}
示例2
demo1.c
#include <stdio.h>
#include <stdint.h>
void displayPointerInfo() {
int value = 42; // 一个整型变量
int *ptr = &value; // 指向整型变量的指针
// 使用 intptr_t 将指针转换为有符号整数
intptr_t intptr_value = (intptr_t)ptr;
printf("Original pointer as intptr_t: %ld\n", intptr_value);
// 使用 uintptr_t 将指针转换为无符号整数
uintptr_t uintptr_value = (uintptr_t)ptr;
printf("Original pointer as uintptr_t: %lu\n", uintptr_value);
// 修改原指针的值
*ptr = 100;
printf("Modified value: %d\n", *ptr);
// 通过从 intptr_t 恢复指针
int *retrieved_ptr1 = (int *)intptr_value;
printf("Retrieved value from intptr_t: %d\n", *retrieved_ptr1);
// 通过从 uintptr_t 恢复指针
int *retrieved_ptr2 = (int *)uintptr_value;
printf("Retrieved value from uintptr_t: %d\n", *retrieved_ptr2);
}
int main() {
displayPointerInfo(); // 调用示例函数
return 0;
}
编译运行
四、写在最后
关于 intptr_t 和 uintptr_t 的总结完毕,如有问题,也欢迎随时交流。