我可以访问地址零吗?

Joe*_*oel 53 c c++ memory pointers

常量0用作C和C++中的空指针.但随着问题"指向一个特定的固定地址 "似乎有一些可能使用分配固定地址.在任何系统中,对于访问地址0的任何低级任务,是否有任何可能的需求?

如果有,那怎么解决0为空指针而全部?

如果没有,是什么让它确定没有这种需要?

AnT*_*AnT 69

在C和C++中,空指针值都不以任何方式与物理地址相关联0.0 在源代码中使用常量来设置指向空指针值的事实只不过是一段语法糖.编译器需要将其转换为在特定平台上用作空指针值的实际物理地址.

换句话说,0在源代码中没有任何物理重要性.它可能是42或者13,例如.即语言作者,如果他们如此高兴,就可以做到这一点,以便你必须这样做p = 42才能将指针设置p为空指针值.同样,这并不意味着42必须为空指针保留物理地址.编译器需要将源代码p = 42转换为机器代码,将实际的物理空指针值(0x00000xBAAD)填充到指针中p.这就是它现在的恒定状态0.

另请注意,C和C++都不提供严格定义的功能,允许您为指针分配特定的物理地址.因此,关于"如何将0地址分配给指针"的问题正式无效.您无法在C/C++中为指针指定特定地址.但是,在实现定义的功能领域,显式的整数到指针转换旨在产生这种效果.所以,你可以这样做

uintptr_t address = 0;
void *p = (void *) address;
Run Code Online (Sandbox Code Playgroud)

请注意,这与做的不一样

void *p = 0;
Run Code Online (Sandbox Code Playgroud)

后者总是产生空指针值,而前者通常不产生.前者通常会生成一个指向物理地址的指针0,该指针可能是也可能不是给定平台上的空指针值.

  • “你根本无法将特定地址分配给 C/C++ 中的指针。” 那“void *p = (void*)0x12345678”呢?至少在某些平台上(在我的例子中是 ARM MCU),这会将绝对的物理内存地址分配给指针。它通常用于SFR(特殊功能寄存器,即内存映射I/O)。 (2认同)

Joh*_*lén 18

切线说明:您可能有兴趣知道使用Microsoft的C++编译器,成员的NULL指针将表示为32位计算机上的位模式0xFFFFFFFF.那是:

struct foo
{
      int field;
};

int foo::*pmember = 0;     // 'null' member pointer
Run Code Online (Sandbox Code Playgroud)

pmember将具有'所有'的位模式.这是因为您需要此值来区分它

int foo::*pmember = &foo::field;
Run Code Online (Sandbox Code Playgroud)

位模式确实会被"全零" - 因为我们希望将偏移量0放入结构foo中.

其他C++编译器可能会为成员的空指针选择不同的位模式,但关键的观察是它不会是您可能期望的全零位模式.

  • 有趣!+1,即使它与问题几乎没有关系:) (2认同)
  • G++ 也这样做。 (2认同)

Jer*_*fin 12

你是从一个错误的前提出发的.当您为指针指定值为0的整型常量时,它将成为空指针常量.这并没有,但是,意味着一个空指针一定是指满足0恰恰相反,C和C++标准都非常清楚,一个空指针可能指为零以外的一些地址.

什么它归结为是这样的:你必须预留一个空指针会指向一个地址-但它可以是您选择的几乎任何地址.当你将零转换为指针时,它必须引用所选择的地址 - 但这就是所有真正需要的地址.例如,如果您决定将整数转换为某个点意味着将0x8000添加到整数,那么空指针实际上将指向地址0x8000而不是地址0.

值得注意的是,取消引用空指针会导致未定义的行为.这意味着你不能做到这一点在便携代码,但它并不能意味着你不能做到这一点的.当您为小型微控制器等编写代码时,包含一些根本不可移植的代码片段是相当常见的.从一个地址读取可能会从某个传感器中获取值,而写入相同的地址可能会激活步进电机(例如).下一个设备(甚至使用完全相同的处理器)可能已连接,因此这两个地址都改为普通RAM.

即使空指针确实引用了地址0,这也不会阻止您使用它来读取和/或写入发生在该地址的任何内容 - 它只是阻止您这样做可移植 - 但这并不是真的很重要.地址零通常很重要的唯一原因是,如果它被解码为连接到正常存储以外的其他东西,那么你可能无论如何都不能完全移植它.

  • 他的前提很好.他想知道:你如何访问内存地址0,因为(void*)0是特殊的? (16认同)
  • @Daniel:`(void*)0`很特别.`int x = 0; (void*)x`不是.它当然不可移植,但并不是那些需要访问特定内存位置的人所担心的. (2认同)

Whi*_*ind 9

编译器会为您解决此问题(comp.lang.c常见问题解答):

如果机器对空指针使用非零位模式,则编程器负责在程序员通过写入"0"或"NULL"时请求空指针来生成它.因此,在内部空指针非零的机器上将#defining NULL设置为0与任何其他指针一样有效,因为编译器必须(并且可以)仍然生成机器的正确空指针以响应在指针上下文中看到的未修饰的0.

您可以通过从非指针上下文引用零来获得零地址.


Dan*_*ach 7

实际上,C编译器很乐意让你的程序尝试写入地址0.在运行时检查每个指针操作的NULL指针会有点贵.在计算机上,程序将崩溃,因为操作系统禁止它.在没有内存保护的嵌入式系统上,程序确实会写入地址0,这通常会导致整个系统崩溃.

地址0在嵌入式系统上可能很有用(一个不在计算机中的CPU的通用术语;它们可以运行从立体声到数码相机的所有内容).通常,系统的设计使您不需要写入地址0.在我知道的每种情况下,它都是某种特殊地址.即使程序员需要写入它(例如,设置中断表),他们只需要在初始引导序列期间写入它(通常是一小段汇编语言来为C设置环境).


roo*_*ook 6

内存地址0也称为零页面.这由BIOS填充,包含有关系统上运行的硬件的信息.所有现代内核都保护着这个内存区域.您永远不需要访问此内存,但如果您需要在内核中执行此操作,则内​​核模块将执行此操作.

  • 这在x86计算机上是正确的,但对于嵌入式系统则不然. (6认同)
  • 根本不保证内存在页面中,也没有内核. (2认同)

Joh*_*lén 6

在x86上,地址0(或更确切地说,0000:0000)及其附近的实模式是中断向量的位置.在过去的糟糕时期,您通常会将值写入中断向量以安装中断程序(或者如果您更加自律,则使用MS-DOS服务0x25).用于MS-DOS的C编译器定义了一种远指针类型,当指定为NULL或0时,它将在其段部分中接收位模式0000,在其偏移部分中接收0000.

当然,一个行为不端的程序意外地写入了一个值为0000:0000的远指针会导致机器上发生非常糟糕的事情,通常将其锁定并强制重启.

  • 实际上,在x86上写0时不会造成直接的坏事.0表示Divide By Zero异常的中断向量,很少被触发. (3认同)

And*_*rey 5

在链接的问题中,人们正在讨论在微控制器中设置固定地址.编程微控制器时,一切都处于较低的水平.

您甚至没有桌面/服务器PC的操作系统,也没有虚拟内存和那些东西.因此,可以访问特定地址的内存,甚至是必要的.在现代的台式/服务器PC上它是无用的甚至是危险的.

  • 在微控制器中,地址0通常是程序入口点.因此,可以通过跳转到地址0来重置程序. (2认同)