我要转换 malloc 的结果吗?

问:

在 this question 中,有人在 comment 中建议我应该不转换 malloc 的结果。即,我应该这样做:

int *sieve = malloc(sizeof(*sieve) * length);

而不是:

int *sieve = (int *) malloc(sizeof(*sieve) * length);

为什么会这样?

答1:

huntsbot.com洞察每一个产品背后的需求与收益,从而捕获灵感

TL;博士

int *sieve = (int *) malloc(sizeof(int) * length);

有两个问题。强制转换并且您使用类型而不是变量作为 sizeof 的参数。相反,这样做:

int *sieve = malloc(sizeof *sieve * length);

长版

不;你不投射结果,因为:

这是不必要的,因为在这种情况下 void * 会自动且安全地提升为任何其他指针类型。

它增加了代码的混乱,强制转换不是很容易阅读(特别是如果指针类型很长)。

它会让你重复自己,这通常是不好的。

如果您忘记包含 ,它可以隐藏错误。这可能会导致崩溃(或者,更糟糕的是,直到稍后在代码的某些完全不同的部分中才会导致崩溃)。考虑如果指针和整数大小不同会发生什么;那么你通过强制转换隐藏了一个警告,并且可能会丢失你返回的地址。注意:从 C99 开始,隐式函数从 C 中消失了,这一点不再相关,因为没有自动假设未声明的函数返回 int。

作为澄清,请注意我说“你不施放”,而不是“你不需要施放”。在我看来,即使你做对了,加入演员阵容也是失败的。这样做根本没有好处,但是有一堆潜在的风险,包括演员表表明你不知道这些风险。

另请注意,正如评论员所指出的那样,上面讨论的是纯 C,而不是 C++。我非常坚信 C 和 C++ 是独立的语言。

此外,您的代码会不必要地重复可能导致错误的类型信息 (int)。最好取消引用用于存储返回值的指针,将两者“锁定”在一起:

int *sieve = malloc(length * sizeof *sieve);

这也将 length 移到前面以增加可见性,并用 sizeof 删除多余的括号;它们只有在参数是类型名称时才需要。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。请记住:sizeof 不是函数! 😃

虽然将 length 移到前面可能在一些极少数情况下会增加可见性,但还应注意,在一般情况下,最好将表达式写为:

int *sieve = malloc(sizeof *sieve * length);

在这种情况下,由于首先保留 sizeof,因此可以确保至少使用 size_t 数学来完成乘法。

比较:当 width 和 length 的类型小于 size_t 时,malloc(sizeof *sieve * length * width) 与 malloc(length * width * sizeof *sieve) 第二个可能会溢出 length * width。

请考虑更新答案。演员表不再危险,重复自己不一定是坏事(冗余可以帮助捕捉错误)。

编译器发生了变化。最新的编译器会警告您缺少 malloc 声明。

@nm 好的。我认为假设在这里阅读的任何人都有特定的编译器是不好的。另外,自从 C11 以来,整个“隐式函数”概念都消失了,我不知道。不过,我看不出添加毫无意义的演员阵容有什么意义。您是否也这样做 int x = (int) 12; 只是为了让事情变得清晰?

@nm 如果显式转换 void 指针“帮助”解决了一个错误,您更有可能遇到未定义的行为,这意味着有问题的程序可能有一个更糟糕、尚未发现的错误,您还没有遇到过。有一天,在一个寒冷的冬夜,你下班回家发现你的 GitHub 页面上充斥着抱怨恶魔从用户的鼻子里飞出的问题报告

@unwind 即使我同意你的观点,(int)12 也没有可比性。 12 是 int,演员什么也不做。 malloc() 的 retval 是 void *,而不是转换为的指针类型。 (如果不是 void *。那么与 (int)12 的类比将是 (void*)malloc(…) 没有人在讨论。)

答2:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

在 C 中,您不需要强制转换 malloc 的返回值。 malloc 返回的指向 void 的指针会自动转换为正确的类型。但是,如果您希望您的代码使用 C++ 编译器进行编译,则需要进行强制转换。社区中的首选替代方法是使用以下内容:

int *sieve = malloc(sizeof *sieve * length);

如果您更改 sieve 的类型,这还使您不必担心更改表达式的右侧。

正如人们指出的那样,演员阵容很糟糕。特别是指针转换。

@MAKZ 我认为 malloc(length * sizeof *sieve) 使它看起来像 sizeof 是一个变量 - 所以我认为 malloc(length * sizeof(*sieve)) 更具可读性。

malloc(length * (sizeof *sieve)) 更具可读性。恕我直言。

@Michael Anderson () 问题放在一边,请注意您建议的样式切换了顺序。考虑当元素计数像 length*width 一样计算时,在这种情况下保持 sizeof 优先确保乘法至少用 size_t 数学完成.比较 malloc(sizeof( *ptr) * length * width) 与 malloc(length * width * sizeof (*ptr)) - 当 width,length 是比 size_t 更小的类型时,第二个可能会溢出 length*width。

@chux 这不是很明显,但是答案已经过编辑,因此我的评论不太中肯-最初的建议是 malloc(sizeof *sieve * length)

C 不是 C++。假装他们是最终会导致混乱和悲伤。如果您使用的是 C++,那么 C 风格的转换也很糟糕(除非您使用的是非常旧的 C++ 编译器)。并且 static_cast>() (或 reinterpret_cast<>() )与 C 的任何方言都不兼容。

答3:

huntsbot.com提供全网独家一站式外包任务、远程工作、创意产品分享与订阅服务!

你确实投了,因为:

它使您的代码在 C 和 C++ 之间更具可移植性,并且正如 SO 经验所表明的那样,许多程序员声称他们是在真正用 C++(或 C 加上本地编译器扩展)编写时用 C 编写的。

不这样做可能会隐藏一个错误:注意所有混淆何时编写类型 * 与类型 ** 的 SO 示例。

它使您不会注意到您未能#include 适当的头文件的想法错过了树木的森林。这就像说“不要担心你没有让编译器抱怨没有看到原型的事实——讨厌的 stdlib.h 是要记住的真正重要的事情!”

它强制进行额外的认知交叉检查。它将(所谓的)所需类型放在您为该变量的原始大小执行的算术旁边。我敢打赌,您可以进行一项 SO 研究,该研究表明 malloc() 错误在有演员表时被捕获得更快。与断言一样,揭示意图的注释可以减少错误。

以机器可以检查的方式重复自己通常是一个好主意。事实上,这就是断言,而这种使用 cast 就是断言。断言仍然是我们使代码正确的最通用技术,因为图灵在很多年前就提出了这个想法。

@ulidtko 如果您不知道,可以编写可编译为 C 和 C++ 的代码。事实上大多数头文件都是这样的,而且它们通常包含代码(宏和内联函数)。将 .c/.cpp 文件作为两者进行编译通常不是很有用,但一种情况是在使用 C++ 编译器编译时添加 C++ throw 支持(但在使用 C 编译器编译时添加 return -1; 或其他)。

如果有人在标头中内联 malloc 调用,我不会留下深刻印象,#ifdef __cplusplus 和 extern "C" {} 用于这项工作,而不是添加额外的演员表。

好吧,第 1 点是无关紧要的,因为 C != C++,其他点也是微不足道的,如果您在 malloc 调用中使用 变量:char **foo = malloc(3*sizeof(*foo)); 如果完全证明:3 指针到 char 指针。然后循环,执行foo[i] = calloc(101, sizeof(*(foo[i])));。分配 101 个字符的数组,整齐地初始化为零。不需要演员表。就此而言,将声明更改为 unsigned char 或任何其他类型,您仍然很好

当我坚持我得到它时,它来了!很棒的答案。这是我第一次在 StackOverflow 中 +1 两个相反的答案! +1 不,你不施放,+1 是的,你施放!哈哈。你们太棒了而对于我和我的学生,我下定了决心:我确实是演员。学生犯的那种错误在选角时更容易被发现。

@Leushenko:以无法通过机器或本地检查验证的方式重复自己是不好的。以可以通过这种方式验证的方式重复自己并不那么糟糕。给定 struct Zebra *p; ... p=malloc(sizeof struct Zebra);,malloc 无法避免重复有关 p 类型的信息,但是如果一种类型发生变化而另一种类型没有发生变化,编译器和本地代码检查都不会检测到任何问题。将代码更改为 p=(struct Zebra*)malloc(sizeof struct Zebra);,如果转换类型与 p 不匹配,编译器将发出警告,local 检查将显示...

答4:

打造属于自己的副业,开启自由职业之旅,从huntsbot.com开始!

正如其他人所说,C 不需要它,但 C++ 需要它。如果您认为要使用 C++ 编译器编译 C 代码,无论出于何种原因,您都可以使用宏,例如:

#ifdef __cplusplus
# define NEW(type, count) ((type *)calloc(count, sizeof(type)))
#else
# define NEW(type, count) (calloc(count, sizeof(type)))
#endif

这样你仍然可以以非常紧凑的方式编写它:

int *sieve = NEW(int, 1);

它将为 C 和 C++ 编译。

既然你在使用宏,为什么不在 C++ 的定义中使用 new 呢?

因为没有理由这样做。它主要用于使用 C++ 编译器编译的 C 程序。如果您要使用“新”,那么您唯一得到的就是问题。你还需要一个免费的宏。而且您需要一个宏来释放一个数组,这是 C 中不存在的差异化。

更不用说释放内存的不是你,而是你正在使用的 C 库等等。许多可能的问题没有任何收获。

@Hosam:是的,绝对是。如果使用 new,则必须使用 delete,如果使用 malloc(),则必须使用 free()。切勿混合它们。

如果要采用这种方法,调用宏 NEW 可能不是一个好主意,因为资源永远不会使用 delete(或 DELETE)返回,因此您正在混合词汇表。相反,将其命名为 MALLOC,或者在这种情况下命名为 CALLOC,会更有意义。

答5:

huntsbot.com汇聚了国内外优秀的初创产品创意,可按收入、分类等筛选,希望这些产品与实践经验能给您带来灵感。

从 Wikipedia:

强制转换的优点 包括强制转换可能允许 C 程序或函数编译为 C++。演员表允许最初返回 char * 的 malloc 的 1989 之前版本。如果目标指针类型发生变化,转换可以帮助开发人员识别类型大小的不一致,特别是当指针声明远离 malloc() 调用时(尽管现代编译器和静态分析器可以在不需要转换的情况下警告这种行为)。铸造的缺点 在 ANSI C 标准下,铸造是多余的。添加强制转换可能会掩盖包含标头 stdlib.h 的失败,在该标头中找到了 malloc 的原型。在没有 malloc 原型的情况下,标准要求 C 编译器假定 malloc 返回一个 int。如果没有强制转换,则在将此整数分配给指针时发出警告;但是,使用演员表时,不会产生此警告,从而隐藏了一个错误。在某些架构和数据模型上(例如 64 位系统上的 LP64,其中 long 和指针是 64 位而 int 是 32 位),此错误实际上可能导致未定义的行为,因为隐式声明的 malloc 返回一个 32-位值,而实际定义的函数返回一个 64 位值。根据调用约定和内存布局,这可能会导致堆栈粉碎。这个问题在现代编译器中不太可能被忽视,因为它们一致地产生警告,指出使用了未声明的函数,因此仍然会出现警告。例如,GCC 的默认行为是显示一条警告,内容为“内置函数的不兼容隐式声明”,无论是否存在强制转换。如果指针的类型在其声明时发生更改,则可能还需要更改调用 malloc 并强制转换的所有行。

尽管没有强制转换的 malloc 是首选方法,并且大多数有经验的程序员都会选择它,但您应该使用任何您喜欢的知道问题的方法。

即:如果您需要将 C 程序编译为 C++(尽管它是一门单独的语言),则必须强制转换使用 malloc 的结果。

“如果目标指针类型发生变化,尤其是在声明指针远离 malloc() 调用时,转换可以帮助开发人员识别类型大小的不一致”是什么意思?你能举个例子吗?

@CoolGuy:See an earlier comment on another answer。但请注意,p = malloc(sizeof(*p) * count) 习语会自动获取类型的更改,因此您不必收到警告并进行任何更改。因此,与不铸造的最佳选择相比,这并不是真正的优势。

这是正确的答案:有利也有弊,归根结底是个人喜好问题(除非代码必须编译为 C++——然后强制转换)。

第 3 点没有实际意义,因为如果指针的类型在其声明时发生更改,则应检查涉及该类型的 malloc、realloc 和 free 的每个实例。铸造将迫使你这样做。

如果忘记包含 stdlib.h,并且程序编译,它如何在没有 malloc 定义的情况下链接?如果它仍然链接并运行,那么对于任何给定的 CPU,哪些指令实际上会在该行上运行?我想我应该检查一下godbolt...

答6:

huntsbot.com聚合了超过10+全球外包任务平台的外包需求,寻找外包任务与机会变的简单与高效。

在 C 中,您可以将 void 指针隐式转换为任何其他类型的指针,因此不需要强制转换。使用一个可能会向不经意的观察者暗示需要一个是有某种原因的,这可能会产生误导。

如果在声明器中没有显式注释类型,则转换通常更容易产生误导,因为很容易混淆分配对象的意图(对初始值不感兴趣)和仅分配一些(未初始化的)内存。

答7:

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

您不强制转换 malloc 的结果,因为这样做会给您的代码添加毫无意义的混乱。

人们选择 malloc 结果的最常见原因是他们不确定 C 语言的工作原理。这是一个警告信号:如果您不知道特定语言机制是如何工作的,那么不要猜测。在 Stack Overflow 上查找或询问。

一些评论:

void 指针可以转换为/从任何其他指针类型转换,无需显式转换(C11 6.3.2.3 和 6.5.16.1)。

然而,C++ 不允许在 void* 和另一个指针类型之间进行隐式转换。所以在 C++ 中,强制转换是正确的。但是如果你用 C++ 编程,你应该使用 new 而不是 malloc()。而且你永远不应该使用 C++ 编译器来编译 C 代码。如果您需要使用相同的源代码同时支持 C 和 C++,请使用编译器开关来标记差异。不要试图用相同的代码来满足两种语言标准,因为它们不兼容。

如果 C 编译器因为您忘记包含标头而找不到函数,您将收到一个编译器/链接器错误。因此,如果您忘记包含 这没什么大不了的,您将无法构建您的程序。

在遵循超过 25 年历史的标准版本的古代编译器上,忘记包含 会导致危险行为。因为在那个古老的标准中,没有可见原型的函数会隐式地将返回类型转换为 int。然后显式地从 malloc 转换结果会隐藏这个错误。但这真的不是问题。您没有使用 25 年的计算机,那么为什么要使用 25 年的编译器呢?

“毫无意义的混乱”是不屑一顾的夸张,它往往会破坏任何说服任何不同意你的人的可能性。演员表当然不是毫无意义的。 Ron Burk 和 Kaz 的回答提出了支持选角的论点,我非常同意。这些担忧是否比您提到的担忧更重要,这是一个合理的问题。对我来说,与他们相比,您的担忧相对较小。

6.3.2.3 不支持“空指针可以在没有显式转换的情况下转换为任何其他指针类型/从任何其他指针类型转换”。也许您正在考虑“指向任何对象类型的指针”? “空指针”和“指向函数的指针”不是那么容易转换的。

事实上,参考文献并不完整。 “隐含”的相关部分是简单赋值规则 6.5.16.1。 “一个操作数是指向对象类型的指针,另一个是指向 void 的限定或非限定版本的指针”。为了完整起见,我已将此参考添加到答案中。

正如我对 another answer 所评论的那样,原因是通过滥用 C 语言规则来防止一些混乱。如果没有完整的 C 对象创建规则(包括 有效类型 的工作原理),您也无法解决混淆,因此推理非常不完整。 OTOH,源代码中的类似效果可以在 C++ 中通过使用成员 template operator T*() const {...} 替换此处的 void* 的类类型来实现,但是由于更严格的规则,C++ 仍然不会引起这种混淆。从这个意义上说,答案也是不完整的。

@FrankHB 你是什么意思?有效类型不受演员表影响。返回的对象在您写入之前不会获得有效类型(“左值访问”)。

答8:

huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式

在 C 中,您会得到从 void * 到任何其他(数据)指针的隐式转换。

@Jens:好的,也许更合适的措辞是“隐式转换”。就像在浮点表达式中使用整数变量一样。

@EFraim这实际上会导致演员阵容,并且是隐式演员。

答9:

huntsbot.com精选全球7大洲远程工作机会,涵盖各领域,帮助想要远程工作的数字游民们能更精准、更高效的找到对方。

现在不需要强制转换 malloc() 返回的值,但我想补充一点,似乎没有人指出:

在古代,即在 ANSI C 提供 void * 作为指针的泛型类型之前,char * 是这种用法的类型。在这种情况下,强制转换可以关闭编译器警告。

参考:C FAQ

关闭编译器警告是个坏主意。

@AlbertvanderHorst 如果您通过解决警告的确切问题来警告您,则不会。

@丹。如果通过解决确切的问题意味着重写子程序以返回现代 ANSI C 类型而不是 char *,我同意。我不会称之为关闭编译器。不要屈服于坚持不存在编译器警告的管理人员,而是在每次重新编译时使用它们来发现可能的问题。格罗杰斯·阿尔伯特

答10:

HuntsBot周刊–不定时分享成功产品案例,学习他们如何成功建立自己的副业–huntsbot.com

只是添加我的经验,学习计算机工程,我看到我看到用 C 编写的两三个教授总是使用 malloc,但是我问的那个(具有巨大的 CV 和对 C 的理解)告诉我这是绝对没有必要的,但是以前只是绝对具体,让学生进入绝对具体的心态。本质上,强制转换不会改变它的工作方式,它完全按照它说的去做,分配内存,并且强制转换不会影响它,你得到相同的内存,即使你错误地将它强制转换为其他东西(并且以某种方式逃避编译器错误)C 将以相同的方式访问它。

编辑:铸造有一定的意义。当您使用数组表示法时,生成的代码必须知道它必须提前多少内存位置才能到达下一个元素的开头,这是通过强制转换来实现的。这样,您就知道对于 double,您会提前 8 个字节,而对于 int,您会提前 4 个字节,依此类推。因此,如果您使用指针表示法,它没有效果,在数组表示法中它变得必要。

除非已经提到,强制转换可能会隐藏错误并使编译器或静态分析器更难分析代码。

“基本上铸造不会改变它的工作方式”。转换为匹配的类型不应该改变任何东西,但是如果 var 的类型改变并且转换不再匹配,会出现问题吗? IWOs,cast 和 var 类型应该保持同步——维护工作的两倍。

我明白为什么教授更喜欢铸造。从教育的角度来看,铸造可能很有用,它向教师传达信息并且不需要维护学生代码 - 它的一次性代码。然而,从编码、同行评审和维护的角度来看,p = malloc(sizeof *p * n); 是如此简单和更好。

答11:

huntsbot.com全球7大洲远程工作机会,探索不一样的工作方式

强制转换 malloc 的结果,因为它返回 void* ,并且 void* 可以指向任何数据类型。

void* 不能指向任何启用此功能的东西,这并不是事实。事实上,void* 可以隐式转换 为任何其他指针类型。为了澄清区别,在 C++ 中 void* 仍然可以指向任何东西,但是隐式转换被删除了,所以必须强制转换。

原文链接:https://www.huntsbot.com/qa/OdXO/do-i-cast-the-result-of-malloc?lang=zh_CN&from=csdn

保持自己快人一步,享受全网独家提供的一站式外包任务、远程工作、创意产品订阅服务–huntsbot.com

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值