在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
在这个问题上,有人在评论中建议我不要使用malloc的结果。即,我应该这样做:
int *sieve = malloc(sizeof(*sieve) * length);
而不是:
int *sieve = (int *) malloc(sizeof(*sieve) * length);
为什么会这样?
当前回答
这个问题是基于意见的滥用问题。
有时我会注意到这样的评论:
不要抛出malloc的结果
or
为什么你不投malloc的结果
关于OP使用铸造的问题。评论本身包含指向此问题的超链接。
这在任何可能的情况下都是不恰当和不正确的。当这确实是一个人自己的编码风格的问题时,没有对也没有错。
为什么会发生这种情况?
它基于两个原因:
这个问题确实是基于意见的。从技术上讲,这个问题早在几年前就应该以观点为基础。一个“我应该”或“我不应该”或等效的“我应该吗”或“不应该”问题,如果没有自己的观点,你就无法集中回答。结束一个问题的原因之一是因为它“可能会导致基于意见的答案”,如这里所示。许多答案(包括最明显和最被接受的@unwind答案)要么完全或几乎完全基于观点(例如,如果你自己选角或重复选角,会在代码中添加一个神秘的“杂乱”,这会很糟糕),并显示出明显和专注的省略选角的倾向。他们一边争论演员阵容的冗余,但更糟糕的是,他们还争论解决由编程本身的错误/失败导致的错误——如果想使用malloc(),就不要#include<stdlib.h>。
我想对所讨论的一些观点提出一个真实的观点,而不是我个人的观点。需要特别注意以下几点:
这样一个很容易陷入自己观点的问题需要一个正反中立的答案。不只是缺点或优点。下面的答案中列出了一个很好的利弊概述:https://stackoverflow.com/a/33047365/12139179(由于这个原因,我个人认为这是迄今为止最好的答案。)
最多会遇到一个原因来解释省略强制转换的原因,即强制转换可能隐藏错误。如果有人使用隐式声明的malloc()返回int(自C99以来,隐式函数不再是标准函数)和sizeof(int)!=sizeof(int*),如本问题所示为什么这段代码在64位体系结构上是错误的,但在32位体系结构中运行良好?演员阵容会隐藏一个bug。虽然这是真的,但它只显示了故事的一半,因为省略了强制转换只能向前解决更大的错误-在使用malloc()时不包括stdlib.h。这永远不会是一个严重的问题,如果你,使用符合C99或更高版本的编译器(这是推荐的,应该是强制性的),以及当您想在代码中使用malloc()时,不要忘记包含stdlib.h,这本身就是一个巨大的错误。
有些人认为C代码符合C++,因为在C++中强制执行强制转换。首先要概括地说:用C++编译器编译C代码不是一种好的做法。C和C++实际上是两种完全不同的语言,具有不同的语义。但如果您真的想/需要使C代码符合C++,反之亦然,请使用编译器开关而不是任何强制转换。由于演员阵容被认为是多余的,甚至是有害的,所以我想把重点放在这些问题上,这些问题给出了演员阵容有用甚至必要的充分理由:https://stackoverflow.com/a/34094068/12139179https://stackoverflow.com/a/36297486/12139179https://stackoverflow.com/a/33044300/12139179
当您的代码、分配的指针的类型(以及转换的类型)发生变化时,转换可能是无效的,尽管这在大多数情况下是不可能的。然后,您还需要维护/更改所有的强制转换,如果代码中有几千个对内存管理函数的调用,那么这将真正地总结并降低维护效率。
摘要:
事实是,如果分配的指针指向基本对齐要求的对象(包括所有对象中的大多数),则根据C标准(从ANSI-C(C89/C90)开始),强制转换是冗余的。
在这种情况下,指针自动对齐,因此不需要执行强制转换:
对aligned_alloc、calloc、malloc和realloc函数的连续调用所分配的存储的顺序和连续性未指定。如果分配成功,则返回的指针将适当对齐,以便可以将其分配给具有基本对齐要求的任何类型对象的指针,然后用于访问空间分配中的此类对象或此类对象的数组ated(直到空间被显式释放)。"来源:C18,§7.22.3/1
“基本对齐是指小于或等于_Aligef(max_align_t)的有效对齐。所有存储持续时间的对象的实现应支持基本对齐。以下类型的对齐要求应为基本对齐:-所有原子、合格或不合格的基本类型;-所有原子、限定或非限定枚举类型;-所有原子、限定或非限定指针类型;-其元素类型具有基本对齐要求的所有数组类型;57)-第7条中规定为完整对象类型的所有类型;-所有结构或联合类型,其所有元素都具有具有基本对齐要求的类型,且其所有元素均没有指定非基本对齐的对齐说明符。如6.2.1所述,后面的声明可能会隐藏前面的声明。"来源:C18,§6.2.8/2
但是,如果为扩展对齐需求的实现定义对象分配内存,则需要强制转换。
扩展对齐由大于_align f(max_align_t)的对齐表示。实现定义了是否支持任何扩展对齐以及支持这些对齐的存储持续时间。具有扩展对齐要求的类型是过度对齐类型。58)来源C18,§6.2.8/3
其他一切都取决于具体的用例和个人的看法。
请注意如何教育自己。
我建议您首先仔细阅读到目前为止所做的所有答案(以及他们可能指出失败的评论),然后如果您或如果您没有将malloc()的结果应用于某个特定案例,请建立自己的观点。
请注意:
这个问题没有正确和错误的答案。这是一个风格问题,你自己决定选择哪种方式(当然,如果你不是受教育或工作所迫)。请注意这一点,不要让你上当。
最后一点:我最近投票结束了这个基于意见的问题,这是多年来确实需要的。如果您获得了关闭/重新打开特权,我也想邀请您这样做。
其他回答
这个问题是基于意见的滥用问题。
有时我会注意到这样的评论:
不要抛出malloc的结果
or
为什么你不投malloc的结果
关于OP使用铸造的问题。评论本身包含指向此问题的超链接。
这在任何可能的情况下都是不恰当和不正确的。当这确实是一个人自己的编码风格的问题时,没有对也没有错。
为什么会发生这种情况?
它基于两个原因:
这个问题确实是基于意见的。从技术上讲,这个问题早在几年前就应该以观点为基础。一个“我应该”或“我不应该”或等效的“我应该吗”或“不应该”问题,如果没有自己的观点,你就无法集中回答。结束一个问题的原因之一是因为它“可能会导致基于意见的答案”,如这里所示。许多答案(包括最明显和最被接受的@unwind答案)要么完全或几乎完全基于观点(例如,如果你自己选角或重复选角,会在代码中添加一个神秘的“杂乱”,这会很糟糕),并显示出明显和专注的省略选角的倾向。他们一边争论演员阵容的冗余,但更糟糕的是,他们还争论解决由编程本身的错误/失败导致的错误——如果想使用malloc(),就不要#include<stdlib.h>。
我想对所讨论的一些观点提出一个真实的观点,而不是我个人的观点。需要特别注意以下几点:
这样一个很容易陷入自己观点的问题需要一个正反中立的答案。不只是缺点或优点。下面的答案中列出了一个很好的利弊概述:https://stackoverflow.com/a/33047365/12139179(由于这个原因,我个人认为这是迄今为止最好的答案。)
最多会遇到一个原因来解释省略强制转换的原因,即强制转换可能隐藏错误。如果有人使用隐式声明的malloc()返回int(自C99以来,隐式函数不再是标准函数)和sizeof(int)!=sizeof(int*),如本问题所示为什么这段代码在64位体系结构上是错误的,但在32位体系结构中运行良好?演员阵容会隐藏一个bug。虽然这是真的,但它只显示了故事的一半,因为省略了强制转换只能向前解决更大的错误-在使用malloc()时不包括stdlib.h。这永远不会是一个严重的问题,如果你,使用符合C99或更高版本的编译器(这是推荐的,应该是强制性的),以及当您想在代码中使用malloc()时,不要忘记包含stdlib.h,这本身就是一个巨大的错误。
有些人认为C代码符合C++,因为在C++中强制执行强制转换。首先要概括地说:用C++编译器编译C代码不是一种好的做法。C和C++实际上是两种完全不同的语言,具有不同的语义。但如果您真的想/需要使C代码符合C++,反之亦然,请使用编译器开关而不是任何强制转换。由于演员阵容被认为是多余的,甚至是有害的,所以我想把重点放在这些问题上,这些问题给出了演员阵容有用甚至必要的充分理由:https://stackoverflow.com/a/34094068/12139179https://stackoverflow.com/a/36297486/12139179https://stackoverflow.com/a/33044300/12139179
当您的代码、分配的指针的类型(以及转换的类型)发生变化时,转换可能是无效的,尽管这在大多数情况下是不可能的。然后,您还需要维护/更改所有的强制转换,如果代码中有几千个对内存管理函数的调用,那么这将真正地总结并降低维护效率。
摘要:
事实是,如果分配的指针指向基本对齐要求的对象(包括所有对象中的大多数),则根据C标准(从ANSI-C(C89/C90)开始),强制转换是冗余的。
在这种情况下,指针自动对齐,因此不需要执行强制转换:
对aligned_alloc、calloc、malloc和realloc函数的连续调用所分配的存储的顺序和连续性未指定。如果分配成功,则返回的指针将适当对齐,以便可以将其分配给具有基本对齐要求的任何类型对象的指针,然后用于访问空间分配中的此类对象或此类对象的数组ated(直到空间被显式释放)。"来源:C18,§7.22.3/1
“基本对齐是指小于或等于_Aligef(max_align_t)的有效对齐。所有存储持续时间的对象的实现应支持基本对齐。以下类型的对齐要求应为基本对齐:-所有原子、合格或不合格的基本类型;-所有原子、限定或非限定枚举类型;-所有原子、限定或非限定指针类型;-其元素类型具有基本对齐要求的所有数组类型;57)-第7条中规定为完整对象类型的所有类型;-所有结构或联合类型,其所有元素都具有具有基本对齐要求的类型,且其所有元素均没有指定非基本对齐的对齐说明符。如6.2.1所述,后面的声明可能会隐藏前面的声明。"来源:C18,§6.2.8/2
但是,如果为扩展对齐需求的实现定义对象分配内存,则需要强制转换。
扩展对齐由大于_align f(max_align_t)的对齐表示。实现定义了是否支持任何扩展对齐以及支持这些对齐的存储持续时间。具有扩展对齐要求的类型是过度对齐类型。58)来源C18,§6.2.8/3
其他一切都取决于具体的用例和个人的看法。
请注意如何教育自己。
我建议您首先仔细阅读到目前为止所做的所有答案(以及他们可能指出失败的评论),然后如果您或如果您没有将malloc()的结果应用于某个特定案例,请建立自己的观点。
请注意:
这个问题没有正确和错误的答案。这是一个风格问题,你自己决定选择哪种方式(当然,如果你不是受教育或工作所迫)。请注意这一点,不要让你上当。
最后一点:我最近投票结束了这个基于意见的问题,这是多年来确实需要的。如果您获得了关闭/重新打开特权,我也想邀请您这样做。
你会选演员,因为:
它使您的代码在C和C++之间更具可移植性,正如SO的经验所示,许多程序员声称,当他们真正用C++(或C加本地编译器扩展)编写时,他们是在用C编写。如果不这样做,可能会隐藏一个错误:请注意所有的so示例,它们混淆了何时写入类型*和类型**。它会让你注意不到你没有#包含一个合适的头文件,这一想法让你错过了树的森林。这与说“不要担心你没能让编译器抱怨没有看到原型的事实——讨厌的stdlib.h才是真正需要记住的重要东西!”它强制进行额外的认知交叉检查。它将(所谓的)所需类型放在您对该变量的原始大小所做的算术旁边。我敢打赌,你可以做一个SO研究,表明malloc()错误在有演员时被捕获的速度要快得多。与断言一样,揭示意图的注释可以减少错误。以机器可以检查的方式重复自己的行为通常是一个好主意。事实上,这就是断言,使用强制转换就是断言。断言仍然是我们获得正确代码的最通用技术,因为图灵多年前就提出了这个想法。
在C语言中,您得到了从void*到任何其他(数据)指针的隐式转换。
习惯于GCC和Clang的人被宠坏了。外面不是很好。
这些年来,我被要求使用的过时的编译器吓坏了。公司和管理者通常采用极端保守的方法来更改编译器,甚至不会测试新的编译器(具有更好的标准遵从性和代码优化)是否能在他们的系统中工作。对于正在工作的开发人员来说,实际情况是,当你在编写代码时,你需要覆盖你的基础,不幸的是,如果你不能控制什么编译器可以应用于你的代码,那么使用mallocs是一个好习惯。
我还建议许多组织应用自己的编码标准,如果定义了编码标准,那么这应该是人们遵循的方法。在缺乏明确指导的情况下,我倾向于选择最有可能在任何地方编译,而不是盲目地遵守某个标准。
根据现行标准,这是不必要的,这一论点是非常有效的。但这一论点忽略了现实世界的现实性。在一个完全由当今标准统治的世界里,我们并没有编码,而是由我喜欢称之为“地方管理的现实领域”的实用性来控制。这比太空时代更加弯曲和扭曲
YMMV。
我倾向于将使用malloc作为防御操作。不漂亮,不完美,但总体安全。(老实说,如果你没有包含stdlib.h,那么你会遇到比强制执行malloc更多的问题!)。
TL;博士
int *sieve = (int *) malloc(sizeof(int) * length);
有两个问题。类型转换,并且您使用类型而不是变量作为sizeof的参数。相反,请这样做:
int *sieve = malloc(sizeof *sieve * length);
长版本
不您不必计算结果,因为:
这是不必要的,因为在这种情况下,void*会自动安全地升级为任何其他指针类型。它给代码增加了混乱,强制转换不太容易阅读(特别是如果指针类型很长)。这会让你重复自己,这通常是不好的。如果您忘记包含<stdlib.h>,它可能会隐藏错误。这可能会导致崩溃(或者更糟糕的是,直到稍后在代码的某个完全不同的部分发生崩溃)。考虑如果指针和整数的大小不同会发生什么;然后你通过投射来隐藏警告,可能会丢失返回地址的一部分。注意:从C99开始,隐式函数从C中消失,这一点不再相关,因为没有自动假设未声明的函数返回int。
作为澄清,请注意我说的是“你不需要投”,而不是“你不必投”。在我看来,这是一个失败的演员阵容,即使你做得对。这样做根本没有好处,但有一系列潜在的风险,包括演员阵容表明你不知道这些风险。
另外,正如评论家指出的,上面提到的是直接C,而不是C++。我非常坚信C和C++是独立的语言。
此外,代码不必要地重复类型信息(int),这可能会导致错误。最好取消引用用于存储返回值的指针,将两者“锁定”在一起:
int *sieve = malloc(length * sizeof *sieve);
这还将长度移到前面以增加可见性,并删除sizeof的多余括号;只有当参数是类型名时,才需要它们。许多人似乎不知道(或忽略)这一点,这使得他们的代码更加冗长。记住:sizeof不是函数!:)
虽然在一些罕见的情况下,将长度移到前面可能会增加可见性,但也应注意,在一般情况下,最好将表达式写成:
int *sieve = malloc(sizeof *sieve * length);
在这种情况下,由于首先保持sizeof,确保乘法至少使用size_t数学运算完成。
比较:malloc(sizeof*sive*length*width)与malloc,当width和length的类型小于size_t时,第二个可能会溢出长度*宽度。