为什么先将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类型大小,但这是使我们其余人遭受痛苦的可悲原因!

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...