当前位置: 首页 > 知识库问答 >
问题:

C和C标准是否意味着地址空间中的一个特殊值必须单独存在以表示空指针的值?

花品
2023-03-14

在讨论了这个关于C和C中的空指针的问题之后,我想把这个问题的结尾分开。

如果可以从C和C标准(答案可以针对这两个标准)中推断出解引用一个值等于< code>nullptr(或< code>(void *)0)值的指针变量是未定义的行为,这是否意味着这些语言要求地址空间中的一个特殊值是死的,这意味着除了表示< code>nullptr的角色之外,它是不可用的?如果系统在同一个地址有一个真正有用的函数或数据结构,等于< code>nullptr,该怎么办?这应该永远不会发生吗?因为为编译器编译到的每个系统找出一个不冲突的空指针值是编译器的作者的责任。或者需要访问这样的函数或数据结构的程序员应该满足于以“未定义的行为模式”编程来实现其意图吗?

这看起来像是模糊了编译器和计算机系统角色的界限。我会问这样做是否正确,但我想这里没有这个空间。

这篇博客文章探讨了解决问题的方法

共有3个答案

丌官嘉勋
2023-03-14

这是否意味着这些语言要求地址空间中的特殊值是死的,这意味着除了表示 nullptr 的角色之外,它不可用?

是的。

C对空指针的要求使其不同于对象指针:

(C11,6.3.2.3p3)“[…]如果将空指针常量转换为指针类型,则可以保证生成的指针(称为空指针)与指向任何对象或函数的指针不相等。”

如果系统在等于nullptr的地址上有一个真正有用的函数或数据结构,该怎么办?这是否永远不会发生,因为编译器编写者有责任为编译器编译到的每个系统找出一个不冲突的空指针值?

Derek M. Jones的New C Standard对实现提供了以下评论:

对于许多实现来说,全位零是空指针常量的一种方便的执行时表示,因为它总是存储中最低的地址。(INMOS Transputer[632]有一个带符号的地址空间,它将零放在中间。)虽然在这个位置可能有程序引导信息,但是不太可能在这里放置任何对象或函数。许多操作系统不使用这个存储位置,因为经验表明,程序错误有时会导致值被写入由空指针常量指定的位置(更多面向开发人员的环境试图在访问该位置时引发异常)。

当主机环境不包括地址零作为进程地址空间的一部分时,另一种实现技术是创建一个对象(有时称为 _ _null)作为标准库的一部分。对 null 指针常量的所有引用都引用此对象,其地址将与任何其他对象或函数不相等。

公羊宗清
2023-03-14

这是否意味着这些语言要求地址空间中的特殊值已死,这意味着除了表示nullptr的角色之外,它是不可用的?

号码

编译器需要一个特殊的值来表示空指针,并且必须注意它不会在该地址放置任何对象或函数,因为所有指向对象和函数的指针都需要与空指针进行比较。标准库在实施malloc和朋友时必须采取类似的预防措施。

但是,如果该地址上已经存在某些内容,则没有严格符合要求的程序可以访问某些内容,则允许实现支持取消引用 null 指针以访问它。在标准 C 中,取消引用 null 指针是未定义的,因此实现可以使其执行任何它喜欢的操作,包括显而易见的。

C和C标准都理解as-if规则的概念,这基本上意味着如果对于有效的输入,一个实现与一个符合标准的实现是无法区分的,那么它确实符合标准。C标准使用了一个简单的例子:

5.1.2.3程序执行

10示例2执行片段时

char c1, c2;
/* ... */
c1 = c1 + c2;

"整数提升"要求抽象机器将每个变量的值提升到intsize,然后将两个ints相加并截断总和。如果添加两个chars可以在没有溢出的情况下完成,或者通过溢出无声包装来产生正确的结果,实际执行只需要产生相同的结果,可能会省略提升。

现在,如果< code>c1和< code>c2的值来自寄存器,并且有可能将< code>char范围之外的值强制输入这些寄存器(例如,通过内联汇编),那么可以观察到实现优化了整数提升的事实。然而,由于观察它的唯一方法是通过未定义的行为或实现扩展,因此任何标准代码都不会受此影响,并且实现允许这样做。

这与在取消引用空指针时获得有用结果的逻辑相同:从代码中只有两种方法可以看出,在特定地址上存在有意义的内容:从保证生成指向对象的指针的求值中获取空指针,或者只是尝试一下。前者是我提到的编译器和标准库必须注意的。后者不会影响有效的标准程序

一个著名的例子是DOS实现上的中断向量表,它驻留在地址0。通常只需取消对空指针的引用即可访问它。C和C标准不应该也不能涵盖对中断向量表的访问。他们没有定义这种行为,但也没有限制对其的访问。应该允许实现提供访问它的扩展。

皇甫逸清
2023-03-14

这取决于短语“地址空间”的含义。C标准非正式地使用了这个短语,但没有定义它的含义。

对于每个指针类型,必须有一个值(空指针)与指向任何对象或函数的指针不相等进行比较。这意味着,例如,如果指针类型为 32 位宽,则该类型最多可以有 2个 32-1 个有效的非空值。如果某些地址具有多个表示形式,或者并非所有表示都与有效地址对应,则可能会少于此值。

因此,如果您定义“地址空间”来覆盖2个不同的地址,其中N是指针的宽度(以位为单位),那么是的,必须将其中一个值保留为空指针值。

另一方面,如果地址空间比这窄(例如,典型的64位系统实际上不能访问2个不同的内存位置),那么保留为空指针的值很容易在地址空间之外。

需要注意的一些事项:

    < li >空指针的表示可能是也可能不是全位零。 < li >并非所有指针类型的大小都相同。 < li >并非所有指针类型都必须使用相同的空指针表示形式。

在大多数现代实现中,所有指针类型都是相同的大小,并且都将空指针表示为所有零位,但有充分的理由使函数指针比对象指针宽,或使void*int*宽,或对空指针使用除所有零位以外的表示。

这个答案基于C标准。大多数也适用于C。(一个区别是C有指向成员类型的指针,通常比普通指针宽。)

 类似资料:
  • 有以下挑战:在C中编写多态函数:

  • C++ 指针 指向指针的指针是一种多级间接寻址的形式,或者说是一个指针链。通常,一个指针包含一个变量的地址。当我们定义一个指向指针的指针时,第一个指针包含了第二个指针的地址,第二个指针指向包含实际值的位置。C++ 指针

  • 我想知道是否有保证内存地址是正整数,包括零,在中使用以下c/c函数(或更多)?也许你可以帮我这样列出他们的结果或者给我一个总结,如果没有, 参考文献 虚拟地址空间

  • 我当时正在编写一个动态生成数组的程序,作为一个清除锈迹的实验,我还是一名学生,已经有一段时间没有编写C代码了:请参见下面代码中标有“WORKING”注释的块。 约束:你只有一个空指针指向用户请求/生成的类型数组(数组可以是浮点数,int,双,char) 你不能在主例程中创建任何其他类型的指针。 是否可以自投一个空指针到(int*) 下面是一些奇怪的代码: 帮我解决C的专业ppl!:)

  • 当我想在 PowerShell 脚本的条件下一次检查所有这些内容时: 检查是否存在哈希表密钥 检查键值是否为NULL 检查键值是否为空字符串 检查键值不仅仅是空格 我是这样做的: 输出: 显然,它甚至适用于不存在的钥匙。但是我很好奇是否有更好或更干净的方法来做到这一点?

  • 地址空间 分段机制涉及5个关键内容:逻辑地址(Logical Address,应用程序员看到的地址,在操作系统原理上称为虚拟地址,以后提到虚拟地址就是指逻辑地址)、物理地址(Physical Address, 实际的物理内存地址)、段描述符表(包含多个段描述符的“数组”)、段描述符(描述段的属性,及段描述符表这个“数组”中的“数组元素”)、段选择子(即段寄存器中的值,用于定位段描述符表中段描述符表