以下语句是不是将物理地址或虚拟地址分配给指针?
Posted
技术标签:
【中文标题】以下语句是不是将物理地址或虚拟地址分配给指针?【英文标题】:Do the following statements assign either physical or virtual address to a pointer?以下语句是否将物理地址或虚拟地址分配给指针? 【发布时间】:2019-02-11 21:22:39 【问题描述】:来自https://***.com/a/2761494/156458
C 和 C++ 都没有提供严格定义的功能,允许 您将特定的物理地址分配给指针。所以你的 关于“如何将0地址分配给指针”的问题正式 没有答案。您根本无法将特定地址分配给指针 在 C/C++ 中。然而,在实现定义的特性领域, 显式整数到指针转换旨在具有 影响。所以,你会这样做
uintptr_t address = 0; void *p = (void *) address;
注意,这和做的不一样
void *p = 0;
后者总是产生空指针值,而前者在 一般情况下没有。前者通常会产生一个指向 物理地址 0,可能是也可能不是空指针值 在给定的平台上。
我惊讶地发现void *p = 0
没有分配物理地址或虚拟地址 0,而是将 void
的空指针分配给该指针。
引用还说“显式整数到指针的转换”可以将地址分配给指针。
问题:
在void *p = 0
中,是否存在从0
到void*
的隐式转换?
隐式转换是否与显式转换(void *)0
相同,即void *p = 0
是否与void *p = (void*) 0
相同?
void *p = (void*) 0
是否生成指向物理地址或虚拟地址 0 的指针
还是void
的空指针?
如果我使用非零数,例如void *p = 123
,有没有隐含的
从123
到void *
的转换?
隐式转换和显式转换(void *) 123
一样吗?
将void *p = 123
或void *p = (void *)123
变成p
指向物理或虚拟地址123
的指针?
如果void *p = (void *)123
无法生成指向物理地址或虚拟地址123
的指针,int addr = 123; void *p = (void *)addr;
可以吗?我通过在引用的第一个示例中将 unitptr_t
替换为 int
来创建它。
谢谢。
【问题讨论】:
不止1个问题 “Null”不一定表示地址位置0
(物理或虚拟)。在历史上,“null”是一个不同于整数0
的特殊值的系统。 另外考虑一下您可能实际需要使用地址0
而不是“null”的嵌入式系统
您正在开发什么样的系统,您可以访问物理地址?大多数非嵌入式系统处理的是虚拟地址,而不是物理地址——您必须深入了解内核中的内存管理才能操作物理地址。
@JonathanLeffler 谢谢。我不在嵌入式系统上工作,可能是指虚拟地址,所以我将问题更改为“物理地址或虚拟地址”。我链接的答案一直提到“物理地址”,这影响了我的问题,为什么不使用虚拟地址代替?
@JonathanLeffler:是的;并且当您处于内核内存管理的核心时,出于多种原因,您希望使用纯整数类型(例如uint64_t
)作为物理地址(更容易的位旋转,如果您尝试设置,希望编译器抱怨类型不匹配指向物理地址的指针,物理地址与虚拟地址的大小不同)。
【参考方案1】:
如果你说
char *p = 0x12345;
您可能会指定p
指向地址0x12345
。我们不能说这是虚拟地址还是物理地址;这取决于您的机器及其配置方式。 (但如果你的机器使用虚拟内存,如果你正在编写一个普通的用户程序,那么它肯定是一个虚拟地址。)
在上面我说“可能”,部分原因是严格来说,将整数分配给指针并不是明确定义的。所以为了安全起见,你会写
char *p = (char *)0x12345;
这将再次分配p
指向地址0x12345
。
然后我们来看看特殊情况。如果你写
char *p = 0;
或
char *p = (char *)0;
问题是,你有没有指定p
指向地址0
?答案可能是在任何传统机器上,但这不能保证,因为空指针有一种特殊情况。
不是p = 0
不会分配p
指向地址0
——而是它可能不会。在空指针的内部表示不是全位 0 的机器上,赋值 p = 0
会将 p
设置为该空指针值,因此 p
将不指向到地址 0。
【讨论】:
【参考方案2】:TL;DR:您所询问的大部分内容都属于特定于实现的行为和语言扩展领域。如果您确实需要此类行为,请查阅您的编译器文档。
在
void *p = 0
中,是否存在从0
到void*
的隐式转换?
初始化是不一致的,但许多编译器接受它作为扩展。那些确实定义了他们想要的结果,但实际上它们确实提供了到void *
的隐式转换。
因为文字 0
是一个“空指针常量”,因为初始化程序执行与简单赋值相同的转换,并且因为简单赋值具有将空指针常量分配给指针的特殊情况规定,是的,0
隐式转换为类型void *
。此外,因为0
是一个空指针常量,所以这样的转换会产生一个void *
类型的空指针。
隐式转换是否与显式转换
(void *)0
相同,即void *p = 0
是否与void *p = (void*) 0
相同?
有充分的理由期望接受前一种形式的编译器将其视为与后一种形式完全相同,但同样,前者是不一致的,接受它作为扩展的实现定义了它们的自己的语义。
是的。 C 无法区分通过强制类型转换显式指定的转换效果和相同类型之间的自动转换。
void *p = (void*) 0
是否生成指向物理或虚拟地址 0 的指针或void
的空指针?
它初始化p
以包含一个空指针(void *
类型)。根据 C 语言,空指针不指向任何对象,并且 C 语言除了对象或函数之外没有地址意义,因此至少在这种意义上,将这样的指针解释为指向任何特定地址是不正确的。取消引用此类指针的效果不是由 C 定义的,但它可能由某些实现定义——可能试图访问地址 0 处的对象。
如果我使用非零数,例如
void *p = 123
,是否有从123
到void *
的隐式转换?
该初始化不符合标准,但某些编译器提供了隐式转换作为扩展。
隐式转换和显式转换
(void *) 123
一样吗?
有充分的理由期望编译器完全实现这种隐式转换,但同样是“扩展”。
void *p = 123
或void *p = (void *)123
是否会将p
设为指向物理地址或虚拟地址123
的指针?
这是实现定义的。同样,除了对象或函数的地址之外,C 没有地址意义,特别是它本身拒绝指定将整数转换为指针类型的结果,除了首先将指针转换为整数获得的整数,对于值为 0 的整数常量表达式。
然而,在某些实现中,将整数(值 0 的整数常量除外)转换为指针具有将整数解释为地址并转换为指向该地址的指针的效果,就好像存在具有该地址的对象。在托管 C 实现中,这通常是一个虚拟地址。在独立实现中,它通常是一个物理地址。一些实现也可能将此行为扩展到值为 0 的整数常量,这可能会或可能不会本质上是不合格的。
如果
void *p = (void *)123
不能生成指向物理地址或虚拟地址123
的指针,int addr = 123; void *p = (void *)addr;
可以吗?我通过在引用的第一个示例中将unitptr_t
替换为 int 来创建它。
完全有理由期望显式转换值为 123 的 int
变量的结果与显式转换值为 123 的整数常量的结果完全相同,但从技术上讲,它是实现定义的可能会离开为符合标准的编译器提供区分空间。
【讨论】:
void *p = 0
应该是完全有效的,因为0
是每个6.3.2.3p3 的空指针常量(这肯定在今天出现了很多......)
实际上,@AndrewHenle,在回答之前我考虑过这一点,并得出结论,自相矛盾的是,0
形式的空指针常量本身并不是一个指针。特别是,6.3.2.3/3 说“如果将空指针常量转换为指针类型,则生成的指针称为空指针 [...]” (强调补充)。根据标准对转换的要求,没有强制转换的 0
形式没有指针类型,因此不能直接分配给指针。
IMO 6.3.2.3p3 可以改进。我对整段的阅读将使0
和(void *)0
本质上等效(并且与(int *)0
之类的东西明显不同)。鉴于该段落是一个强制安装的 hack,以允许假设 NULL
为 0
的错误代码,可能没有真正干净的方式来解释它。
我想我们不同意,@AndrewHenle。我认为0
是一个空指针常量,其类型为int
,(void*)0
是一个空指针常量,其类型为void *
,以及@987654364 类型的空指针,我认为解释非常简洁直接@,并且两者都遵守各自类型的所有规则,但它们后面的例外都是空指针常量。我还注意到,7.19/3 指定 NULL
扩展为 implementation-defined 空指针常量。如果空指针常量是等价的,那么为什么需要实现定义?
@AndrewHenle,经过进一步分析,虽然我仍然不同意 0
和 (void *)0
的一般等效性,但我发现这比我之前断言的差异要小。如果您对详细信息感兴趣,我已经更新了答案的相关部分。以上是关于以下语句是不是将物理地址或虚拟地址分配给指针?的主要内容,如果未能解决你的问题,请参考以下文章