C语言 在内存地址验证的函数原型中使用uintptr_t [重复]

0kjbasz6  于 2023-03-22  发布在  其他
关注(0)|答案(2)|浏览(176)

此问题在此处已有答案

Is it possible in C (not invoking UB) to check if two objects overlap?(8个答案)
Can the following code be true for pointers to different things(2个答案)
8天前关闭。
我正在编写一个基本函数,用于测试地址的有效性(地址必须在给定的内存范围内)。
我想使用uintptr_t。类似于:

bool verify_address(uintptr_t pAddress)
{
    if ( (pAddress < ...) ||  (pAddress > ...))
        return false;
    else
        return true;
}

但是在查看了uintptr_t类型之后,我想知道这是否正确。据我所知,不能保证数据指针变量的值在转换为uintptr_t之后保持不变(唯一的保证是它可以被转换回相同的值)。
例如:如果我有一个指针unsigned int *ptr = (unsigned int *)0x20000000(uintptr_t)ptr == 0x20000000一定为真吗?

jm81lzqq

jm81lzqq1#

正式的C标准文本是C176.3.2.3:
一个整数可以被转换为任何指针类型。除了前面指定的,结果是实现定义的,可能没有正确对齐,可能没有指向引用类型的实体,并且可能是一个陷阱表示。
任何指针类型都可以转换为整数类型。除非前面指定,否则结果是实现定义的。如果结果不能用整数类型表示,则行为未定义。结果不需要在任何整数类型的值范围内。
值得注意的是,没有要求在单独进行上述两种转换中的任一种之后应该可以再次转换回来。
因此,在一些情况下,如对齐,陷阱表示等,转换可能会调用未定义的行为。在uintptr_t的情况下,整数类型的范围是不相关的,因为它保证“足够大”。大多数时候,转换到/从指针到uintptr_t只是“实现定义”,因为它取决于系统和编译器。
至于在C标准之外的真实的世界中事情是如何工作的,所有系统中的绝大多数都让一个指针简单地对应于一个地址(物理的或虚拟的)。它们不是必须这样做,但实际上,绝大多数计算机/编译器都是这样做的。对于罕见的情况,你有外来的指针格式或外来的内存Map,它通常通过添加非标准扩展(例如near/far关键字)来处理,而不是根据类型保持不同的内部指针表示。
或者在哈佛架构的情况下,可能会有其他非标准的扩展,这取决于处理代码还是数据。在这样的CPU上,函数指针和对象指针可能会有不同的行为,作为一个例子。而在冯诺依曼CPU上,这不太可能。
无论哪种方式,只要你把一个指针转换成一个整数,它当然就都是实现定义的了。C编译器并不真正知道或关心某个系统上存在的各种内存范围,这些业务由链接器/链接器脚本处理。
因此,如果你有一个非外来的指针格式,并且知道给定内存的有效地址范围,那么你可以确定你可以在uintptr_t上做算术,这样的代码在嵌入式系统中很常见,同时编码引导加载器,片上闪存/eeprom驱动程序等。

lx0bsm1f

lx0bsm1f2#

数据(object)指针的值不一定是线性编码的。整数类型uintptr_t上的数学(如<)没有指定为像指针本身上的数学一样工作。
object 指针转换为uintptr_t,然后再转换回相同的指针类型,会产生一个 * 等效 * 指针,而不一定是相同的位模式。
(unsigned int *)0x20000000本身有问题。@Lundin
类似OP的代码可以在许多系统上“工作”。例如@Eric Postpischil。它不是严格可移植的。
甚至(u)intptr_t也是一个 * 可选 * 类型--但非常常见。
通常,最好的方法是重新思考总体目标。

相关问题