【嵌入式】指针与整数的桥梁、跨平台编程的黄金钥匙:揭开 uintptr_t 和 intptr_t 的神秘面纱

<> 博客简介:Linux、rtos系统,arm、stm32等芯片,嵌入式高级工程师、面试官、架构师,日常技术干货、个人总结、职场经验分享

<> 公众号:嵌入式技术部落

<> 系列专栏:C/C++、Linux、rtos、嵌入式开发、流媒体、数据结构、网络协议、开源库、CMake、Makefile、架构设计模式等

一、intptr_t 和 uintptr_t 详解

intptr_t 和 uintptr_t,这两个数据类型是ISO C99定义的。主要用于处理指针和整数之间的转换。它们在需要将指针转换为整数进行操作,或者将整数转换回指针的场景中非常有用。

具体代码在linux平台的/usr/include/stdint.h头文件中,见下图。
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;

为什么会这么定义呢?
先看下不同的数据类型在不同字长机器上长度大小。

系统位数charshortintlong指针
161个字节8位2个字节16位2个字节16位4个字节32位2个字节16位
321个字节8位2个字节16位4个字节32位4个字节32位4个字节32位
641个字节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 的总结完毕,如有问题,也欢迎随时交流。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值