如果NULL不是全零位,则全零指针值是否也是'false'?

如何解决如果NULL不是全零位,则全零指针值是否也是'false'?

我知道C编译器不需要为NULL的位表示使用全零,但它们是标准要求NULL进行评估的 * *在布尔值上下文/比较中为false。因此,下面程序中的 2nd printf将始终输出 false

但是我想知道的是:在NULL *不是 *全零的系统上,将一个 * is *全零的指针值在布尔上下文/比较中也评估为假?换句话说,下面程序中的 1st printf是否会输出 true

或者以一种稍微不同的方式问:我可以依靠calloc来生成一个指针值,该值在布尔上下文/比较中将始终为false? this问题的第一个答案使用memset清除名为long*的{​​{1}}的位,然后继续说y是UB,因为{{ 1}}可以是“陷阱表示”(无论是哪种形式)。 y==0也在清除位,所以 第一 y中的calloc也是UB吗?


o->p

解决方法

typedef struct { void * p; } obj;
obj * o = calloc(sizeof(obj),1);
assert(o);  // Let us set aside the case of a failed allocation
printf("%s\n",o->p ? "true" : "false");  // 1st: could print "true" ?

我可以依靠calloc来生成一个指针值,该值在布尔上下文/比较中将始终为false吗?

否-输出可能为"true" * 1

作为指针的全零的位模式可能不是空指针

7.22.3.2 calloc函数
2 calloc函数为nmemb对象的数组分配空间,每个对象的大小均为size。该空间初始化为所有零位。301)
脚注301)请注意,这不必与浮点零或空指针常量的表示相同。


示例:一个实现可能只具有一个单一的 null指针编码,且所有位模式都为1。 (void *)0将全零位模式int 0转换为全void *。不管空指针的位模式如何,if (null_pointer)始终为false。


* 1 但是实际上是的,输出始终为"false"。如今,不使用所有零位模式作为 null指针的实现并不常见。高度可移植的代码不会假定这种实用性。考虑旧的或新的新颖系统可能使用零位模式作为非 null指针-并令人遗憾地破坏了许多假设全零位模式是 null指针的代码库

,

背景信息

请考虑以下使用表达式逻辑值的地方,这些地方全部取自C18,我强调的是 粗斜体

  • 6.3.1.2(布尔型)p1:将任何标量值转换为_Bool时,如果值 等于0 ;否则,结果为1。

  • 6.5.3.3(一元算术运算符)p5:如果逻辑求反符!的操作数 的值不等于0 ,则结果为0。 ,如果其操作数 的值等于0 ,则为1。结果的类型为int。表达式!E等效于(0==E)

  • 6.5.13(逻辑AND运算符)p3:如果&&运算符的两个操作数 都不等于0 ,则应产生1;否则,结果为0。结果的类型为int

  • 6.5.14(逻辑OR运算符)p3:如果||运算符的两个操作数 比较不等于0 ,则应产生1;否则,结果为0。结果的类型为int

  • 6.5.15(条件运算符)p4:对第一个操作数求值;在它的评估与第二个或第三个操作数的评估(无论哪个被评估)之间都有一个序列点。仅当第一个 比较不等于0 时,才对第二个操作数求值;仅当第一个 比较等于0 时,才对第三个操作数求值;结果是第二个或第三个操作数的值(无论哪个被求值),都将转换为下面描述的类型。

  • 6.8.4.1(if语句)p2:在两种形式中,如果表达式 比较不等于0 ,则执行第一个子语句。在else格式中,如果表达式 比较等于0 ,则执行第二个子语句。如果通过标签到达第一个子陈述,则不执行第二个子陈述。

  • 6.8.5(迭代语句)p4:迭代语句使称为 loop body 的语句重复执行,直到控制表达式 等于0 。无论是否循环,都会重复 主体是从迭代语句中输入的,还是通过跳转输入的。

“ E比较等于0”等效于C表达式(E == 0),而“ E比较不等于0”等效于C表达式(E != 0)。相等运算符的约束由下式给出:

  • 6.5.9(平等运营商)p2:应满足以下条件之一:
    • 两个操作数都具有算术类型;
    • 两个操作数都是指向兼容类型的合格或不合格版本的指针;
    • 一个操作数是指向对象类型的指针,而另一个是指向void的合格或不合格版本的指针;或
    • 一个操作数是一个指针,另一个是空指针常量

关于相等运算符的语义,其中至少一个操作数是一个指针:

  • 6.5.9(相等运算符)p5:否则,至少一个操作数是一个指针。如果一个操作数是指针,另一个是空指针常量,则将空指针常量转换为指针的类型。如果一个操作数是一个 指向对象类型的指针,另一个是指向void的合格或不合格版本的指针,前者将转换为后者的类型。

  • p6:当且仅当两个都是空指针时,两个指针比较相等,两个都是指向同一对象的指针(包括指向对象和它的开头的子对象的指针)或函数,都是指向一个对象的指针越过同一数组对象的最后一个元素,或者一个指针指向一个数组对象的末尾,另一个指针指向另一个数组对象的起点,该指针恰好紧随地址中的第一个数组对象空间。

关于空指针常量:

  • 6.3.2.3(指针)p3:值为0的整数常量表达式或强制转换为类型void *的此类表达式称为空指针常量 67 )。如果将空指针常量转换为指针类型,则保证生成的指针称为 null指针,可以将不相等的指针与指向任何对象或函数的指针进行比较。

OP的问题

但是我想知道的是:在NULL不是 全为零的系统上,全为零的指针值也将计算为false在布尔上下文/比较中?

此外:NULL null指针常量,不一定是 null指针(请参见上面的6.3.2.3p3,其中它可以是整数常量表达式)。您真正的意思是一个系统,其中空指针的位表示不全为零。

注意:正如Eric Postpischil在下面的注释中所指出的,一个系统可能具有空指针值的几个位表示形式,因此我们假定它们都不是全零位表示形式。问题。

为使指针值在布尔上下文/比较中评估为false,必须 比较不等于0 。在这种情况下,它必须 比较不等于空指针常量 。通过上面的6.5.9p5,空指针常量将转换为要比较的指针的类型。根据上述6.5.9p6,空指针值将不等于非空指针值。因此,在布尔值上下文中,如果空指针值并非全为零的系统上所有位均为零的非空指针值将求值为true。

或者以略有不同的方式问:我可以依靠calloc来生成一个指针值,该值在布尔上下文/比较中将始终为false?

否,您不能依靠calloc(或字节值0的memset)来生成一个指针值,该值在布尔上下文中将被评估为false。如果具有全零位表示形式的指针值不是空指针值,则在布尔上下文中其结果将为true。

,

此问题的第一个答案是关于NULL和0的大量讨论:What is the difference between NULL,'\0' and 0?

该答案的重点是:

请注意,C语言中的空指针是什么。不要紧 在基础架构上。如果基础架构具有 空指针值定义为地址0xDEADBEEF,则取决于 编译器来解决这个问题。

…即使在这种有趣的架构上,以下方式仍然有效 检查空指针的方法:

if (!pointer)
if (pointer == NULL)
if (pointer == 0)

在同一问题的第二个答案中……

值为0的int类型的常量表达式或表达式 这种类型,强制转换为void *是一个空指针常量,如果 转换为指针将变为空指针。它由 比较不等于指向任何对象或函数的指针的标准。

(简短的回答,是的,您可以使用if (!ptr)检查NULL指针)。

,

核心答案

但是我想知道的是:在NULL *不是 *全零的系统上,将一个 * is *全零的指针值在布尔值上下文/比较中也计算为假?

在C实现中,C标准允许以下任何一项:

  • 全零位是一个空指针,其他位模式都不是。
  • 全零位是一个空指针,一个或多个其他位模式是。
  • 全零位不是空指针,而一个或多个其他位模式是空指针。
换句话说,C实现可以将任何一个或多个位模式指定为空指针,并且可以包括或不包括全零位。 (如果C实现确实允许多个位模式为空指针,则必须确保它们比较相等。)

...下面程序中的 1st printf是否会输出 true

允许打印“ true”; calloc的结果是全部为零的内存,将其解释为void *可能会导致指针值不是空指针值。

补充

...,其中NULL *不是 *全零...

NULL只是源代码中的内容。它是0((void *) 0)或等效值。无论在源代码中将其用作指针的地方(也就是说,您正在执行if (pointer != NULL)之类的正常事情,而不是int x = 3 + NULL;之类的笨拙的地方),编译器都会有效地将其转换为空指针。也就是说,如果在C实现中all-bits-zero不是空指针,则编译器将编译pointer != NULLpointer与表示空指针的某种位模式的比较。>

因此,您的问题全都与空指针有关。他们与NULL无关。

…在…的系统上

什么是空指针的最终确定取决于C实现,而不是其执行的系统。 C实现可以按其希望的任何方式表示指针,并在使用指令中的机器地址时根据需要对其进行转换。

,

您可以使用明确的防御性编码样式来避免此类问题。

如果有_p指针,则编写类似

的结构
    (_p==NULL)?(A):(B)

现在任何读者都立即知道,您打算检查_p是否等于NULL,即使在NULL可能不同于整数值0的机器上,编译器也会自动正确执行。此外,由于依赖于隐式行为,因此静态代码检查器现在也不会警告您。

    (_p)?(A):(B)

只是做得不好

但除此之外,这是一个有趣的技术问题。

C ++委员会在2019年或2020年进行的一次有趣的演讲显示,即使这些人也考虑将兼容性降低到某些奇怪的不确定行为,这在1970年之前需要用于3-4种体系结构。在过去的几十年中,没有任何已知的使用这种材料的方法-至少据我所知。正如对您的问题的第一条评论所述:您几乎找不到博物馆,至少在博物馆之外。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 <select id="xxx"> SELECT di.id, di.name, di.work_type, di.updated... <where> <if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 <property name="dynamic.classpath" value="tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams['font.sans-serif'] = ['SimHei'] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -> systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping("/hires") public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate<String
使用vite构建项目报错 C:\Users\ychen\work>npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-