我是否强制转换malloc的结果?

这个问题中,有人在评论中建议我应该<强>不抛出malloc的结果。

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

而不是:

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

为什么会出现这种情况?

332457 次浏览

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

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

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

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

正如人们所指出的,转换是不好的。尤其是指针转换。

太长别读

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

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

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

长版本

;您不要转换结果,因为:

  • 这是不必要的,因为在这种情况下,void *会自动安全地提升为任何其他指针类型。
  • 它给代码增加了混乱,强制转换不太容易阅读(特别是如果指针类型很长)。
  • 它让你重复自己,这通常是不好的。
  • 如果你忘记包含<stdlib.h>,它会隐藏一个错误。这可能会导致崩溃(或者更糟糕的是,没有会导致崩溃,直到很久以后在代码的一些完全不同的部分)。考虑一下如果指针和整数的大小不同会发生什么;那么你通过强制转换来隐藏警告,可能会丢失返回地址的位。注意:从C99开始,隐式函数从C中消失了,这一点不再相关,因为没有自动假设未声明的函数返回int

作为澄清,请注意我说的是“you don't cast”,而不是“you don't需要 to cast”。在我看来,即使你做对了,也没有包括演员阵容是一种失败。这样做没有任何好处,但是有一堆潜在的风险,包括演员阵容表明你不知道风险。

还要注意,正如评论员指出的那样,上面谈论的是纯C,而不是C++。我坚信C和C++是独立的语言。

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

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

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


虽然在一些罕见的情况下将length移动到前面可能增加可见性,但也应该注意,在一般情况下,最好将表达式写成:

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

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

比较:malloc(sizeof *sieve * length * width) vs.malloc(length * width * sizeof *sieve)widthlengthsize_t小时,第二个可能会溢出length * width

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

正如其他人所说,它不是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++。

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

投,因为:

  • 它使您的代码在C和C++之间更便携,正如SO经验所示,许多程序员声称他们正在使用C编写,而实际上他们正在使用C++(或C加上本地编译器扩展)编写。
  • 如果不能这样做可以隐藏错误:请注意所有的SO示例,这些示例混淆了何时编写type *type **
  • 它让你注意到你没有#include一个适当的头文件错过了以森林换树木。这就像说“不要担心你没有要求编译器抱怨没有看到原型的事实——那个讨厌的stdlib. h是真正重要的事情要记住!”
  • 它强制执行额外的认知交叉检查。它将(所谓的)所需类型放在您为该变量的原始大小所做的算术旁边。我敢打赌,您可以进行SO研究,该研究表明,当有转换时,malloc()错误被捕获得更快。与断言一样,揭示意图的注释会减少错误。
  • 以机器可以检查的方式重复自己通常是一个伟大的想法。事实上,这就是断言,而这种转换的使用就是断言。断言仍然是我们正确代码的最通用技术,因为图灵多年前提出了这个想法。

返回的类型是vol*,它可以强制转换为所需的数据指针类型,以便可以取消引用。

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

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

参考:C常见问题

这取决于编程语言和编译器。如果您在C中使用malloc,则无需键入cast,因为它会自动键入cast。但是,如果您使用C++,则应键入cast,因为malloc将返回void*类型。

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

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

一些评论:

  • 空指针可以在没有显式转换的情况下转换为/从任何其他指针类型(C116.3.2.3和6.5.16.1)。

  • 但是C++不允许在void*和另一个指针类型之间进行隐式转换。所以在C++,这种转换是正确的。但是如果你在C++中编程,你应该使用new而不是malloc()。并且你不应该使用C++编译器编译C代码。

    如果您需要使用相同的源代码同时支持C和C++,请使用编译器开关标记差异。不要尝试使用相同的代码满足两种语言标准,因为它们不兼容。

  • 如果C编译器因为你忘记包含标头而找不到函数,你会得到一个编译器/链接器错误。所以如果你忘记包含<stdlib.h>,那没什么大不了的,你将无法构建你的程序。

  • 在遵循25年以上标准版本的古老编译器上,忘记包含<stdlib.h>会导致危险的行为。因为在那个古老的标准中,没有可见原型的函数隐式将返回类型转换为int。然后显式转换malloc的结果会隐藏此bug。

    但这真的不是问题。您没有使用25年的计算机,那么为什么要使用25年的编译器呢?

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

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

空指针是一个泛型对象指针,C支持从空指针类型到其他类型的隐式转换,因此不需要显式地对其进行类型转换。

但是,如果您希望相同的代码在不支持隐式转换的C++平台上完美兼容,则需要进行类型转换,因此这一切都取决于可用性。

在可能的情况下用C编程时最好做的事情:

  1. 使您的程序通过C编译器编译,所有警告都打开-Wall并修复所有错误和警告
  2. 确保没有变量声明为auto
  3. 然后使用带有-Wall-std=c++11的C++编译器编译它。修复所有错误和警告。
  4. 现在再次使用C编译器编译。您的程序现在应该在没有任何警告的情况下编译并且包含更少的错误。

此过程允许您利用C++严格的类型检查,从而减少错误的数量。特别是,此过程强制您包含stdlib.h,否则您将获得

malloc未在此范围内声明

并且还强制您转换malloc的结果,否则您将获得

void*T*的转换无效

或者你的目标类型是什么。

我能找到的用C而不是C++编写的唯一好处是

  1. C有一个明确的ABI
  2. C++可以生成更多代码[异常,RTTI,模板,运行时多态]

请注意,在理想情况下,第二个缺点应该在使用C共有的子集和静态多态特征时消失。

对于那些发现C++严格规则不方便的人,我们可以使用带有推断类型的C++11特征

auto memblock=static_cast<T*>(malloc(n*sizeof(T))); //Mult may overflow...

空指针背后的概念是它可以转换为任何数据类型,这就是malloc返回空的原因。此外,您必须了解自动类型转换。因此,尽管您必须这样做,但并不强制转换指针。它有助于保持代码清洁并帮助调试

在C语言中,空指针可以分配给任何指针,这就是为什么你不应该使用类型转换。如果你想要“类型安全”分配,我可以推荐以下宏函数,我总是在我的C项目中使用它们:

#include <stdlib.h>#define NEW_ARRAY(ptr, n) (ptr) = malloc((n) * sizeof *(ptr))#define NEW(ptr) NEW_ARRAY((ptr), 1)

有了这些,你可以简单地说

NEW_ARRAY(sieve, length);

对于非动态数组,第三个必备的函数宏是

#define LEN(arr) (sizeof (arr) / sizeof (arr)[0])

这使得数组循环更安全、更方便:

int i, a[100];
for (i = 0; i < LEN(a); i++) {...}

如果您使用的是C++编译器,您最好将其更改为C编译器。

这就是GNU C库参考手册所说的:

您可以将malloc的结果存储到任何指针变量中,而无需强制转换,因为ISO C会自动将类型void *转换为另一个必要时的指针类型。但在上下文中强制转换是必要的除了赋值操作符或您可能希望您的代码运行在传统的C.

isoc11标准(p347)是这样说的:

如果分配成功则返回的指针适当对齐,以便它可以被分配给指向任何类型的对象的指针基本对齐要求,然后用于访问这样的在分配的空间中的对象或此类对象的数组(直到空间被显式释放)

维基百科

铸造的优点

  • 包括转换可能允许C程序或函数编译为C++。

  • 强制转换允许最初返回char*的1989年之前版本的malloc。

  • 如果目标指针类型发生变化,转换可以帮助开发人员识别类型大小的不一致,特别是当指针声明远离malloc()调用时(尽管现代编译器和静态分析器可以在不需要转换的情况下警告此类行为)。

铸造的缺点

  • 在ANSI C标准下,转换是多余的。

  • 添加转换可能会掩盖失败以包含标题stdlib. h,在找到malloc的原型。在没有malloc的原型,标准要求C编译器假设malloc返回一个int。如果没有强制转换,则会发出警告当将此整数分配给指针时发出;但是,与演员,这个警告不产生,隐藏bug。在某些架构和数据模型(例如64位系统上的LP64,其中long和指针是64位,int是32位),此错误可以实际上导致未定义的行为,正如隐式声明的malloc返回32位值,而实际定义的函数返回64位值。取决于调用约定和内存布局,这可能会导致堆栈粉碎。此问题不太可能在现代编译器中被忽视,因为它们统一产生警告未声明的函数已被使用,因此警告将例如,GCC的默认行为是显示警告读取“内置的不兼容隐式声明”函数“无论演员是否存在。

  • 如果指针的类型在声明时发生了变化,则可以此外,需要更改调用和转换malloc的所有行。

虽然没有铸造的malloc是首选方法,大多数有经验的程序员都选择它,但您应该使用任何您喜欢的问题。

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

习惯了GCC和Clang的人都被宠坏了。外面并不是那么好。

这些年来,我被要求使用的编译器年龄惊人的编译器吓坏了。通常公司和经理在更换编译器时采取超保守的方法,如果他们的系统中可以使用新的编译器(具有更好的标准合规性和代码优化),甚至不会测试。对于工作开发人员来说,实际的现实是,当你编码时,你需要覆盖你的基础,不幸的是,如果你不能控制什么编译器可能应用于你的代码,铸造mallocs是一个好习惯。

我还建议许多组织应用自己的编码标准,如果定义了,人们应该遵循的方法。在没有明确指导的情况下,我倾向于选择最有可能在任何地方编译的,而不是盲目地遵守标准。

有一种观点认为,按照目前的标准,这是不必要的。但这种观点忽略了现实世界的实用性。我们并不是在一个完全由日常标准统治的世界中编码,而是由我喜欢称之为“本地管理的现实场”的实用性统治。这比时空更加扭曲和扭曲。:-)

YMMV。

我倾向于认为转换malloc是一种防御操作。不漂亮,不完美,但通常是安全的。(老实说,如果你没有包含stdlib. h,那么你的问题比转换malloc多0个!)。

我加入强制转换只是为了表示不赞成类型系统中的丑陋漏洞,它允许像以下代码片段这样的代码在没有诊断的情况下编译,即使没有使用强制转换来带来糟糕的转换:

double d;void *p = &d;int *q = p;

我希望它不存在(在C++也不存在),所以我投了。它代表了我的品味和我的编程政治。我不仅投了一个指针,而且有效地投了一票,和驱逐愚蠢的恶魔。如果我不能实际上抛弃愚蠢,那么至少让我用抗议的姿态表达这样做的愿望。

事实上,一个好的做法是用返回unsigned char *的函数包装malloc(和朋友),基本上不要在代码中使用void *。如果你需要一个指向任何对象的通用指针,使用char *unsigned char *,并在两个方向上进行转换。也许可以享受的一种放松是使用像memsetmemcpy这样没有转换的函数。

在转换和C++兼容性的话题上,如果你编写代码,使其编译为C和C++(在这种情况下,你必须malloc的返回值分配给void *以外的东西时),你可以为自己做一件非常有用的事情:你可以使用宏进行转换,当编译为C++时转换为C++风格的转换,但在编译为C时减少为C转换:

/* In a header somewhere */#ifdef __cplusplus#define strip_qual(TYPE, EXPR) (const_cast<TYPE>(EXPR))#define convert(TYPE, EXPR) (static_cast<TYPE>(EXPR))#define coerce(TYPE, EXPR) (reinterpret_cast<TYPE>(EXPR))#else#define strip_qual(TYPE, EXPR) ((TYPE) (EXPR))#define convert(TYPE, EXPR) ((TYPE) (EXPR))#define coerce(TYPE, EXPR) ((TYPE) (EXPR))#endif

如果您坚持这些宏,那么对这些标识符的代码库进行简单的grep搜索将显示您所有的转换位置,因此您可以检查它们中是否有任何不正确的。

然后,如果您定期使用C++编译代码,它将强制使用适当的转换。例如,如果您使用strip_qual只是为了删除constvolatile,但程序以这样的方式更改,现在涉及类型转换,您将得到诊断,并且您必须使用转换的组合来获得所需的转换。

为了帮助您遵守这些宏,GNUC++(不是C!)编译器有一个漂亮的功能:为所有出现的C风格转换生成一个可选的诊断。

-Wold-style-cast (C++ and Objective-C++ only)Warn if an old-style (C-style) cast to a non-void type is usedwithin a C++ program.  The new-style casts (dynamic_cast,static_cast, reinterpret_cast, and const_cast) are less vulnerableto unintended effects and much easier to search for.

如果您的C代码按C++编译,您可以使用此-Wold-style-cast选项找出可能潜入代码的(type)转换语法的所有出现,并通过从上述宏(或组合,如果必要)中选择适当的替换来跟进这些诊断。

这种对转换的处理是在“干净C”中工作的最大的独立技术理由:C和C++方言的组合,这反过来又在技术上证明了转换返回值malloc的合理性。

我更喜欢做强制转换,但不是手动转换。我最喜欢的是使用来自glib的g_newg_new0宏。如果不使用glib,我会添加类似的宏。那些宏在不影响类型安全的情况下减少代码重复。如果你弄错了类型,你会在非空指针之间得到一个隐式强制转换,这会导致警告(C++中的错误)。如果你忘记包含定义g_newg_new0的标头,你会得到一个错误。g_newg_new0都采用相同的参数,不像malloc采用的参数比calloc少。只需添加0即可获得零初始化内存。代码可以使用C++编译器编译,无需更改。

空指针是泛型指针,C支持从空指针类型到其他类型的隐式转换,因此不需要显式类型转换。

但是,如果您希望相同的代码在不支持隐式转换的C++平台上完美兼容,则需要进行类型转换,因此这一切都取决于可用性。

  1. 如前所述,它不是C所需要的,而是C++所需要的。

  2. 包括转换可能允许C程序或函数编译为C++。

  3. 在C中,这是不必要的,因为false*会自动安全地提升为任何其他指针类型。

  4. 但是,如果您当时强制转换,如果您忘记包含,它可能会隐藏错误stdlib. h.这可能会导致崩溃(或者更糟的是,不会导致崩溃)直到后来在代码的一些完全不同的部分)。

    因为找到了stdlib. h包含malloc的原型。在没有malloc的原型,标准要求C编译器假设malloc返回一个int。如果没有强制转换,则将此整数分配给指针时发出警告;但是,在施法时,不会产生此警告,隐藏了bug。

不,您不强制转换malloc()的结果。

一般来说,你不要向或从#0转换

不这样做的一个典型原因是#include <stdlib.h>失败可能会被忽视。由于C99使隐式函数声明非法,这在很长一段时间内不再是问题,因此如果您的编译器至少符合C99,您将收到诊断消息。

但是有一个更有力的理由不要引入不必要的指针转换:

在C中,a指针转换几乎总是一个错误。这是因为以下规则(N1570中的§6.5 p7,C11的最新草案):

对象的存储值只能由具有以下之一的左值表达式访问以下类型:
-与对象的有效类型兼容的类型,
-与对象的有效类型兼容的类型的限定版本,
-对应于有效类型的有符号或无符号类型的类型对象,
-对应于限定版本的有符号或无符号类型的类型对象的有效类型,
-聚合或联合类型,其中包括上述类型之一成员(递归地包括子聚合或包含联合的成员),或
-字符类型。

这也称为严格混淆现象。所以以下代码是未定义行为

long x = 5;double *p = (double *)&x;double y = *p;

而且,有时令人惊讶的是,以下内容也是如此:

struct foo { int x; };struct bar { int x; int y; };struct bar b = { 1, 2};struct foo *p = (struct foo *)&b;int z = p->x;

有时,您需要转换指针,但给定严格混淆现象,您必须非常小心。因此,在您的代码中发生的任何指针转换都是您必须仔细检查它的有效性的地方。因此,您永远不会编写不必要的指针转换。

tl; dr

简而言之:因为在C中,任何出现指针转换应该会对需要特别注意的代码发出红旗,所以您永远不应该编写不必要指针转换。


附带说明:

  • 在某些情况下,您实际上需要转换为void *,例如,如果您想打印指针:

    int x = 5;printf("%p\n", (void *)&x);

    这里需要转换,因为printf()是一个可变参数函数,所以隐式转换不起作用。

  • 在C++,情况有所不同。在处理派生类的对象时,强制转换指针类型有些常见(并且正确)。因此,在C++,与void *的转换是而不是隐式的是有意义的。C++有一整套不同风格的强制转换。

malloc的转换在C中是不必要的,但在C++中是强制性的。

在C中不需要强制转换,因为:

  • 在C的情况下,void *会自动安全地提升为任何其他指针类型。
  • 如果您忘记包含<stdlib.h>,它可能会隐藏错误。这可能会导致崩溃。
  • 如果指针和整数的大小不同,则通过强制转换隐藏警告,并且可能会丢失返回地址的位。
  • 如果指针的类型在其声明时更改,则可能还需要更改调用和转换malloc的所有行。

另一方面,转换可能会增加程序的可移植性。即,它允许C程序或函数C++编译。

对我来说,这里的结论是,在C中转换malloc是完全没有必要的,但如果你转换,它不会影响malloc,因为malloc仍然会分配给你请求的祝福内存空间。另一个带回家的原因是人们做铸造的原因之一,这是为了使他们能够用C或C++编译相同的程序。

可能还有其他原因,但其他原因,几乎可以肯定,迟早会给你带来严重的麻烦。

这个问题是基于意见的滥用的主题。

有时我注意到这样的评论:

不要强制转换malloc的结果

为什么不强制转换malloc的结果

关于OP使用转换的问题。注释本身包含指向此问题的超链接。

这在任何可能的方式中也是不合适和不正确的。当它真正是一个人自己的编码风格的问题时,没有对也没有错。


为什么会这样?

它基于两个原因:

  1. 这个问题确实是观点导向的,从技术上讲,这个问题在几年前就应该被归为观点导向了。一个"0"、"1"或者等同于"2"、"3"的问题,你是无法集中回答的,而且你肯定不能不表达自己的观点。关闭一个问题的原因之一是因为它"可能会带来观点导向的答案",这点很好地说明了。

  2. 许多答案(包括最明显和最被接受的@张曼玉中的回答)要么完全或几乎完全基于意见(例如,如果你进行强制转换或重复自己会很糟糕,会在你的代码中添加一个神秘的“混乱”),并显示出明显而集中的省略强制转换的倾向。他们一方面争论强制转换的冗余,但也甚至更糟地争论解决由编程本身bug /failure引起的bug-如果想使用malloc(),不要#include <stdlib.h>


我想对所讨论的一些观点提出真实的看法,而不是我的个人意见。有几点需要特别注意:

  1. 这样一个很容易陷入自己意见的问题需要一个中立的利弊答案。不仅是缺点还是优点。

    利弊的一个很好的概述在这个答案中列出:

    https://stackoverflow.com/a/33047365/12139179

    (我个人认为这是迄今为止最好的答案。


  1. 最多遇到的一个原因是转换可能隐藏bug。

    如果有人使用隐式声明的malloc()返回int(自C99以来隐式函数从标准中消失)和sizeof(int) != sizeof(int*),如本问题所示

    为什么这段代码在64位架构上出现故障,但在32位架构上运行良好?

    演员会隐藏bug。

    虽然这是真的,但它只显示了故事的一半,因为演员的遗漏只会成为更大bug的前瞻性解决方案-在使用malloc()时不包括stdlib.h

    这永远不会是一个严重的问题,如果你,

    1. 使用符合C99或更高版本的编译器(建议并且应该是强制性的),并且

    2. 当你想在代码中使用malloc()时,不要忘记包含stdlib.h,这本身就是一个巨大的bug。


  1. 有些人争论C代码的C++合规性,因为演员必须C++。

    首先一般来说:用C++编译器编译C代码不是一个好的做法。

    C和C++实际上是两种完全不同的语言,具有不同的语义学。

    但是,如果您真的想要/需要使C代码符合C++反之亦然,请使用编译器开关而不是任何转换。

    由于铸造倾向于被宣布为多余甚至有害,我想把重点放在这些问题上,这些问题给出了铸造有用甚至必要的充分理由:


  1. 当你的代码分别改变分配指针的类型(以及转换的类型)时,转换可能是无益的,尽管这在大多数情况下不太可能。然后,你还需要维护/更改所有的转换,如果你的代码中有几千次对内存管理函数的调用,这真的可以总结并降低维护效率。

总结:

事实是,如果分配的指针指向基本对齐要求的对象(包括大多数对象),则根据C标准(已经从ANSI-C(C89/C90)开始),转换是多余的。

您不需要进行转换,因为在这种情况下指针会自动对齐:

通过连续调用aligned_alloc、calloc、malloc和real loc函数分配的存储的顺序和连续性未指定。如果分配成功则返回的指针被适当地对齐,以便它可以被分配给指向具有基本对齐要求的任何类型对象的指针,然后用于访问分配空间中的此类对象或此类对象数组(直到空间被显式释放)。

来源:C18,§7.22.3/1


"基本对准是小于或等于_Alignof (max_align_t)的有效对齐。实现应支持所有存储期限对象的基本对齐。以下类型的对齐要求应是基本对齐:

-所有原子、限定或不限定的基本类型;

-所有原子、限定或非限定枚举类型;

-所有原子、限定或非限定指针类型;

-元素类型具有基本对齐要求的所有数组类型;57)

-第7条中指定为完整对象类型的所有类型;

-所有结构或联合类型,其所有元素都具有具有基本对齐要求的类型,并且其元素都没有对齐说明符指定不是基本对齐的对齐。

  1. 如6.2.1所述,后面的声明可能会隐藏之前的声明。”

来源:C18,§6.2.8/2

但是,如果您为扩展对齐要求的实现定义对象分配内存,则需要转换。

扩展对准由大于_Alignof (max_align_t)的对齐表示。是否支持任何扩展对齐以及支持它们的存储持续时间是实现定义的。具有扩展对齐要求的类型是过度对齐类型。58)

C18,§6.2.8/3

其他一切都是具体用例和自己意见的问题。

请小心你如何教育自己。

我建议你先仔细阅读迄今为止所做的答案中的第1条(以及他们可能指向失败的评论),然后如果你或者如果你没有在特定的情况下投出第0条的结果,就建立你自己的观点。

请注意:

这个问题的答案没有对错之分。这是一个风格的问题,你自己决定你选择哪种方式(当然,如果你不是因为教育或工作而被迫)。请注意这一点,不要让你受骗


最后一点:我最近投票决定关闭这个基于意见的问题,这确实是多年来所需要的。如果你有关闭/重新打开的特权,我想邀请你也这样做。

malloc的主要问题是获得合适的尺寸

malloc()返回的内存是无类型,由于简单的转换,它不会神奇地获得有效类型

我想这两种方法都很好,选择应该取决于程序员的意图。

  1. 如果为类型分配内存,则使用转换。

ptr = (T*)malloc(sizeof(T));

  1. 如果为给定指针分配内存,则不要使用强制转换。

ptr = malloc(sizeof *ptr);

ad 1

第一种方法通过为给定类型分配内存来确保正确的大小,然后强制转换以确保它被分配给正确的指针。如果使用了不正确的ptr类型,那么编译器将发出警告/错误。如果ptr的类型发生了变化,那么编译器将指出代码需要重构的地方。

此外,第一种方法可以组合成类似于C++中的new运算符的宏。

#define NEW(T) ((T*)malloc(sizeof(T)))...ptr = NEW(T);

此外,如果ptrvoid*,则此方法有效。

ad 2

第二种方法不关心类型,它通过从指针的类型中获取它来确保正确的大小。此方法的主要优点是每当更改ptr的类型时自动调整存储大小。重构时可以节省一些时间(或错误)。

缺点是,如果ptrvoid*,该方法不起作用,但它可能被认为是一件好事。而且它不适用于C++因此它不应该用于C++程序将使用的标头中的内联函数。

就个人而言,我更喜欢第二种选择。