当前位置: 首页 > news >正文

C 中的 uintptr_t 类型

1、uintptr_t

uintptr_t 在 C99 中的 ,作为一个可选类型。

在这里插入图片描述

任意合法的 void * 指针可转换为该类型值,再将该值转换回 void * 指针后,所得结果与初始指针值相等(在比较操作中判等)。
再大白话一点:主要用途是让我们可以将指针安全地转换为整数,再从整数恢复为指针,而不会丢失信息

  • intptr_t:具备上述属性的带符号整型
  • uintptr_t:具备上述属性的无符号整型

以下这些定义是按照标准 C 的最小要求实现的(例如 C99 要求 long 至少为 32 位)

位数charshortintlong指针
16 位系统1 字节2个字节2个字节4个字节2个字节
32 位系统1 字节2个字节4个字节4个字节4个字节
64 位系统1 字节2个字节4个字节8个字节8个字节

指针在 32 位平台和 64 位平台下均与 long 类型的长度一致,然而在 16 位机器上,long 为 4 个字节,而指针为 2 个字节。

因此,就可以发现 intptr_tuintptr_t 定义的巧妙之处:可以保证 intptr_tuintptr_t 的长度与机器的指针长度一致,因此在进行整数与 指针的相互转换时可以用 intptr_t 进行过渡。uintptr_t 增强了程序的兼容性。

1.1 案例

我们实际举几个例子,看一下 uintptr_t 关键字的实际使用场景。

场景 1

pages 作为指针,需要强转成 _u64。借助 uintptr_t 过渡,先转成与指针等宽的无符号整数,语义明确且安全
如果不加 uintptr_t 过渡,可能被编译器视作不安全,并发出警告。

struct fastrpc_phy_page {u64 addr;		/* physical address */u64 size;		/* size of contiguous region */
};struct fastrpc_invoke_args {__u64 ptr;__u64 length;__s32 fd;__u32 attr;
};static int fastrpc_init_create_static_process(struct fastrpc_user *fl,char __user *argp)
{struct fastrpc_invoke_args *args;struct fastrpc_phy_page pages[1];......args[2].ptr = (u64)(uintptr_t) pages;
}

场景 2
smem_start 作为 unsigned long 型,先使用 uintptr_t 转成与指针等宽的无符号整数,语义明确且安全,再进行指针赋值。
如果不加 uintptr_t 过渡,可能被编译器视作不安全,并发出警告。

struct fb_fix_screeninfo {char id[16];			/* identification string eg "TT Builtin" */unsigned long smem_start;	/* Start of frame buffer mem */......
}struct fb_info {......union {char __iomem *screen_base;	/* Virtual address */char *screen_buffer;};......
}static int hitfb_probe(struct platform_device *dev)
{struct fb_info *info;......info->screen_base = (char __iomem *)(uintptr_t)hitfb_fix.smem_start;......
}

1.2 总结

  总结一句话,intptr_tuintptr_r 变量类型用于跨平台的情况下,不同机器字长(16位、32位、64位)整数与指针相互转移的的通用性。

  通常情况下,编译器内部都会提供 intptr_t 等定义。如果编译器没有提供,则标准库会根据架构用 typedef 自己定义。例如 glibc 标准库中的 /stdlib/stdint.h 文件中:

/* Types for `void *' pointers.  */
#if __WORDSIZE == 64
# ifndef __intptr_t_defined
typedef long int		intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned long int	uintptr_t;
#else
# ifndef __intptr_t_defined
typedef int			intptr_t;
#  define __intptr_t_defined
# endif
typedef unsigned int		uintptr_t;
#endif

1.3 拓展

  看了上面的讲解,不知道你会不会有个问题,为什么指针和整数之间的转换需要 uintptr_t 关键字?而整数和整数之间的转换就不需要?

  这里我们就要了解指针在 C99 标准中是如何定义的《ISO/IEC 9899:1999 (E)》。

6.3.2.3 Pointers 章节

  1. A pointer to void may be converted to or from a pointer to any incomplete or object type. A pointer to any incomplete or object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
  • 指向 void 的指针可以与指向任意不完全类型或对象类型的指针相互转换
  • 指向任意不完全类型或对象类型的指针可以转换为指向 void 的指针,再转换回来,其结果应当与原始指针进行比较时相等
  1. Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type
  • 任何指针类型均可转换为整数类型
  • 除非前文另有规定,此类转换的结果为实现定义(implementation-defined)
    • 除非标准前面有明确规定的特例,否则:把一个指针类型转换为一个整数类型时,转换后的结果值是由具体实现(如编译器 + 平台)自行定义的,并不由 C 语言标准统一规定
  • 若该结果无法被目标整数类型所表示,则行为为未定义(undefined behavior)
    • 如果你试图把一个指针值(例如 void *,通常是 64 位地址)转换成一个目标整数类型(比如 unsigned int,是 32 位),而这个整数类型无法容纳指针的原始值,那么行为是未定义的(undefined behavior)
  • 该结果不必落在任何整数类型的值域范围之内
    • 当你把一个指针转换成整数时,转换的结果可以是某个整数类型所不能表示的值(逻辑上的),因为这种转换是实现定义的,标准并不强制这个结果落在一个合法整数值的范围内

6.3.1.3 Signed and unsigned integers 章节

  1. When a value with integer type is converted to another integer type other than _Bool, if the value can be represented by the new type, it is unchanged.
  2. Otherwise, if the new type is unsigned, the value is converted by repeatedly adding or subtracting one more than the maximum value that can be represented in the new type until the value is in the range of the new type.
  3. Otherwise, the new type is signed and the value cannot be represented in it; either the result is implementation-defined or an implementation-defined signal is raised

  这里就不再具体翻译了,总结一句话就是:所有整数之间的转换是标准定义清晰的,包括类型提升、截断、符号位处理等。

  所以回到我们的场景 1。因为 pages 指针强转成 u64 是实现定义,即由编译器和平台决定。为了确保兼容性、所有平台都能正常运行,所以要先转换成 uintptr_t 一个整数,一个可以足够承载一个指针值,然后再去强转 u64,在语义和行为上具有明确性和安全性。

虽然现代几乎所有编译器都能够正确解决 指针强转整数 的操作,但是为了兼容性、规范性,我们还是需要使用 uintptr_t,否则部分编译器会在编译时蹦出警告

static int fastrpc_init_create_static_process(struct fastrpc_user *fl,char __user *argp)
{struct fastrpc_invoke_args *args;struct fastrpc_phy_page pages[1];......args[2].ptr = (u64)(uintptr_t) pages;
}
http://www.lryc.cn/news/580441.html

相关文章:

  • 深入解析享元模式:通过共享技术高效支持大量细粒度对象
  • ai 工程图相关论文集合
  • HarmonyOS:创建ArkTS卡片
  • HTML知识复习2
  • 汽车制造车间检测机器人与PLC无线以太网实时控制方案
  • 计算机技术的进阶之路:从基础到前沿的深度探索
  • 【网络】Linux 内核优化实战 - net.ipv4.tcp_timestamps
  • Oracle 高级 SQL 查询与函数详解:多表连接、子查询、聚合、分析函数
  • 3dmax一键烘焙很多张贴图合并成一张贴图插件支持fbx/obj/blender多材质模型合并为一张贴图
  • 光伏发电园区管理系统 - Three.js + Django 实现方案
  • SqueezeBERT:计算机视觉能为自然语言处理在高效神经网络方面带来哪些启示?
  • SQL 转 Java 实体类工具
  • 【内存】Linux 内核优化实战 - net.ipv4.tcp_max_tw_buckets
  • HarmonyOS学习2---Stage模型
  • 接口测试之apifox的使用
  • 在 Ubuntu 下配置 oh-my-posh —— 普通用户 + root 各自使用独立主题(共享可执行)
  • 常见高危端口风险分析与防护指南
  • java中,stream的filter和list的removeIf筛选速度比较
  • 【网络协议安全】任务12:二层物理和单臂路由及三层vlanif配置方法
  • Docker文件操作、数据卷、挂载
  • 猎板 PCB 微孔技术:构建 5G 通信设备高效运行的坚实底座
  • 冒泡和快速排序的区别
  • 【Note】《深入理解Linux内核》 第十八章:深入理解 ext2 与 ext3 文件系统
  • 人工智能-基础篇-18-什么是RAG(检索增强生成:知识库+向量化技术+大语言模型LLM整合的技术框架)
  • 2025使用VM虚拟机安装配置Macos苹果系统下Flutter开发环境保姆级教程--中篇
  • 【算法笔记】4.LeetCode-Hot100-数组专项
  • 多任务学习-ESMM
  • 隐马尔可夫模型(HMM):观测背后的状态解码艺术
  • STM32HAL库总结
  • HuggingFists: 无代码处理复杂PDF