为什么先将sizeof放在malloc中更安全? 最后放置sizeof(X)的问题:首先放置int:有符号度转换为无符号整数的重要性:相关:

如何解决为什么先将sizeof放在malloc中更安全? 最后放置sizeof(X)的问题:首先放置int:有符号度转换为无符号整数的重要性:相关:

两者之间有什么区别

int *p = malloc( h * w * sizeof(*p) );

int *p = malloc( sizeof (*p) * h * w );

hw的类型为int时?

为什么在sizeof(*p)中首先设置malloc比在最后位置更安全?


我已经知道,后一种形式用于确保size_t的数学运算,并且在进行计算以防止有符号整数溢出之前,int操作数将被扩展为size_t herehere,但我不太了解它是如何工作的。

解决方法

首先编写sizeof操作时,通常要确保至少使用size_t数学进行计算。让我们确定这意味着什么。


最后放置sizeof(X)的问题:

想象一下该场景,h的值为200000,而w的值为50000(可能是偶然得到的)。

假设int可以容纳的最大整数值为2147483647,这很常见(您可以从宏INT_MAX-标头{{1中读取的确切实现定义的值}}),两者都是<limits.h>可以保留的合法值。

如果现在使用int,则首先计算malloc( h * w * sizeof(*p) );的部分,因为算术表达式的求值顺序从左到右。这样,您将得到一个有符号整数溢出,因为结果h * w(100亿)不可能用10000000000表示。

其中发生整数溢出的程序的行为是不确定的。 C标准甚至以整数溢出为例说明了未定义行为

3.4.3

1种不确定的行为

使用非便携式或错误程序构造或错误数据时的行为,对此文档没有任何要求

2注释1:可能的不确定行为包括从完全忽略具有不可预知结果的情况到在翻译或程序执行过程中表现为环境特征(无论是否发行)的行为诊断消息),以终止翻译或执行(伴随诊断消息的发布)。

3注释2: J.2概述了导致未定义行为的C程序的属性。

4示例一个未定义行为的示例是整数溢出时的行为。

来源:C18,第3.4.3节


首先放置int

如果您首先使用sizeof(X)操作(例如sizeof),通常 就没有整数溢出的风险。

这有两个原因。

  1. malloc( sizeof(*p) * h * w );获得无符号整数类型sizeof的值。 size_t在最现代的实现中具有比size_t高的整数转换等级和大小。常用值:intsizeof(size_t) == 8

    这对于第2点很重要,这是在算术表达式中出现的称为“ 整数提升”(算术转换)的东西。

  2. 在表达式中,经常发生操作数的自动类型转换。这称为整数隐式类型提升。有关此的更多信息,请查看此有用的FAQ

    对于此升级,整数类型的转换等级很重要,因为整数转换等级较低的操作数的类型将提升为整数转换等级较高的操作数的类型。

    查看C标准中的确切短语:

    “否则,如果两个操作数都具有符号整数类型或都具有无符号整数类型,则将具有较小整数转换等级的类型的操作数转换为具有较高等级的操作数的类型。”

    来源:C18,第6.3.1.8/1

    签名的转换也可以在这里进行,这是重要的,如后所述。

    “否则,如果具有无符号整数类型的操作数的秩大于或等于另一个操作数的类型的秩,则将带符号整数类型的操作数转换为无符号整数类型的操作数的类型。 “

    ....

    “否则,如果带符号整数类型的操作数的类型可以表示带无符号整数类型的操作数的所有值,则带无符号整数类型的操作数将转换为带符号的操作数的类型整数类型。”

    来源:C18,第6.3.1.8/1

    如果sizeof(int) == 4的整数转换等级高于或等于size_t,并且int不能代表int的所有值(自size_t的大小通常比前面提到的int小),类型为size_t的操作数hw被提升为类型int之前计算。


有符号度转换为无符号整数的重要性:

现在您可能会问:为什么将有符号性转换为无符号整数很重要?

这里还有两个原因使第二个更为重要,但是出于完整性考虑,我想同时覆盖这两个原因。

  1. 无符号整数的正范围始终比具有相同整数转换等级的有符号整数大。这是因为有符号整数也始终需要表示一个负值范围。无符号整数没有负范围,因此可以表示正值几乎是有符号整数的两倍。

    但更重要的是:

  2. An unsigned integer can never overflow!

“涉及无符号操作数的计算永远不会溢出,因为不能用所得的无符号整数类型表示的结果的模数要比该所得类型可以表示的最大值大一模。” / p>

来源:C18,第6.2.5 / 9节(强调我的内容)

这就是为什么像size_t那样先放置sizeof操作更安全的原因。

但是,在这种情况下,您将使用无符号整数来超出限制,这是因为包装的内存环绕太小而无法将其用于所需的目的。访问未分配的内存也会调用未定义的行为。

但是,尽管如此,它仍然可以保护您,防止在调用malloc( sizeof(*p) * h * w );本身时出现不确定的行为。


旁注:

  • 请注意,将malloc()放在第二个位置sizeof会在技术上达到相同的效果,尽管这可能会降低可读性。

  • 如果对malloc( h * sizeof(*p) * w )的调用中的算术表达式只有一个或2个操作数(例如malloc()sizeof(x)),则顺序无关紧要。但是要遵循惯例,我建议使用相同的样式,始终将int放在首位:sizeof()。这样,您就不必冒险拥有2个malloc(sizeof(int) * 4)操作数而将其遗忘了。

  • intsize_t使用h之类的无符号整数类型也是一种更明智的选择。这样可以确保首先不会发生未定义的溢出,此外,它更合适,因为wh的含义不是负值。


相关:

,

为什么在malloc中首先设置sizeof(* p)比最后一个位置更安全?

简单的答案是:它不(至少不应该)更安全。

更长的答案:

任何整数计算都可能溢出-一些结果的行为不确定-一些结果不正确,并且可能导致随后的程序失败。

任何整数计算都必须考虑是否会发生溢出。如果您编写的程序在单个malloc调用中分配了超过2G的内存,那么我确定您已经意识到这一点,并确保hw都具有适当的性能类型。

此外,标准并没有确切说明整数类型的最大值是多少。因此,如果要编程“安全”程序,请确保在运行时询问这些限制。

换句话说:“更安全”不是编程目标。如果您编写在边缘运行的程序,则可以使它们安全-不仅是“更安全”

,

虽然乘法是可交换的,但显然编译器不会向前扫描最大的类型,sizeof()为size_t,在64位计算机上,该类型为unsigned long(2 ^ 64-1),因此顺序对于防止溢出很重要,即使所有CPU都将这些信息作为状态位提供(即使不是作为中断),它在许多语言中也会默默地发生!当然,有些程序员希望静默溢出以获取驻留的mod类型大小,但这是使我们其余人遭受痛苦的可悲原因!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 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时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;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,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;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[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 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 -&gt; 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(&quot;/hires&quot;) 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&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-