我投的是 malloc 的结果吗?

共26个回答,已解决, 标签: c malloc casting

这个问题中, 有人在评论中建议, 我应该投的结果 malloc , 即

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

而不是:

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

为什么会出现这种情况?

第1个答案(采用)

不, 我不知道。你不会投结果, 因为:

  • void * 这种情况下, 它是不必要的, 因为它自动和安全地提升到任何其他指针类型。
  • 它增加了代码的混乱, 强制转换不是很容易阅读 (特别是在指针类型很长的情况下)。
  • 它让你重复自己, 这通常是不好的。
  • 如果您忘记包括在内, 它可以隐藏错误 。这可能会导致崩溃 (或者更糟糕的是, 直到后来在代码的某些完全不同的部分, 才会导致崩溃)。考虑如果指针和整数的大小不同, 会发生什么情况;然后, 你隐藏一个警告通过强制转换, 并可能失去你的返回地址的位。注意: 从 C11 开始, 隐式函数从 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

比较: malloc(sizeof *sieve * length * width)malloc(length * width * sizeof *sieve) 第二个可能溢出的时间和 length * width width length 小于 size_t

第2个答案

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

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

这也让你不必担心改变表达式的右侧, 如果你曾经改变过的类型 sieve

正如人们所指出的, 野兽是不好的。特别是指针强制转换。

第3个答案

确实投了, 因为:

  • 它使您的代码在 C 和 C++ 之间更易于移植, 正如 so 经验所显示的那样, 很多程序员声称, 当他们真正用 c++ (或 c 加本地编译器扩展) 编写时, 他们是用 c 编写的。
  • 如果不这样做, 可能会隐藏错误: 请注意所有 so 的示例, 这些示例混淆了何时写入 type * type **
  • 它使您无法注意到您未能获得适当的 #include 头文件, 但它会错过树的林。这同样, 说 "不要担心, 事实上, 你没有要求编译器抱怨没有看到原型-讨厌的 stdlib. h 是真正重要的事情要记住!"
  • 它迫使额外的认知交叉检查。它将 (所谓的) 所需类型放在您为该变量的原始大小所做的算术旁边。我打赌你可以做一项 SO 研究, malloc() 研究表明, 当有演员的时候, 虫子被抓的速度要快得多。与断言一样, 显示意图的注释可减少 bug。
  • 以机器可以检查的方式重复自己, 往往是个主意。事实上, 这就是断言, 而这种对强制转换的使用就是断言。断言仍然是我们最普遍的方法, 可以正确地处理代码, 因为图灵是在这么多年前提出这个想法的。
第4个答案

如其他说明所述, 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++ 编译。

第5个答案

维基百科

铸造的优势

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

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

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

铸造的缺点

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

  • 添加强制转换可能会掩盖失败, 以包括标头_stdlib._h, 在该标头中找到 malloc 的原型。在没有 malloc 原型的情况下, 标准要求 c 编译器假定 malloc 返回 int。如果没有强制转换, 则在将此整数分配给指针时发出警告; 如果没有强制转换, 则会发出警告。然而, 与演员, 这个警告不会产生, 隐藏一个错误。在某些体系结构和数据模型 (如 64 位系统上的 LP64, 其中长和指针是 64 位, int 是 32 位), 此错误实际上可能会导致未定义的行为, 因为隐式声明的 malloc 返回 32 位值, 而实际上反定义函数返回一个 64 位值。根据调用约定和内存布局, 这可能会导致堆栈粉碎。这个问题在现代编译器中不太可能被忽视, 因为它们一致地产生了使用了未声明的函数的警告, 因此仍然会出现警告。例如, GCC 的默认行为是显示一个警告, 显示 "内置函数的不兼容隐式声明", 而不管强制转换是否存在。

  • 如果指针的类型在其声明时更改, 则还可能需要更改调用和强制转换 malloc 的所有行。

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

也就是说: 如果您需要将 C 程序编译为 C++ (尽管这些是单独的语言), 则应 malloc 将其用于强制转换。

第6个答案

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

第7个答案

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

人们投 malloc 结果的最常见原因是他们不确定 C 语言的工作原理。这是一个警告信号: 如果你不知道特定的语言机制是如何工作的, 那就不要猜测。查找它或询问堆栈溢出。

一些评论:

  • 在没有显式强制转换 (C11 6.3.2.3 和 6.5.16.1) 的情况下, 可以将 void 指针从任何其他指针类型转换为/。

  • 但是, c++ 不允许在另一个指针类型之间进行隐式强制转换 void* 。因此, 在 C++ 中, 演员是正确的。但是, 如果您使用 C++ 编程, 则应使用 new 而不是 malloc ()。而且永远不应该使用 C++ 编译器编译 C 代码。

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

  • 如果 C 编译器因为忘记包括标头而找不到函数, 则会收到有关该原因的编译器链接器错误。所以, 如果你忘了 包括这一点, 你将无法建立你的程序。

  • 在遵循超过 2 5 年的标准版本的古代编译器中, 忘记包括会 导致危险的行为。因为在那个古老的标准中, 没有可见原型的函数隐式地将返回类型 int 转换为。然后, 显式地从 malloc 中转换结果将隐藏此 bug。

    但这确实不是问题。你不是在使用 25 年的电脑, 为什么要使用 25 年的编译器呢?

第8个答案

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

第9个答案

现在 malloc() 没有必要铸造返回的值, 但我想补充一点, 似乎没有人指出:

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

参考: c 常见问题

第10个答案

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

第11个答案

只是加上我的经验, 学习计算机工程, 我看到我所见过的两三位教授总是投 malloc, 然而我问的那个 (有大量的简历和对 C 的理解) 告诉我, 这绝对是没有必要的, 但只习惯了 b 是绝对具体的, 并使学生进入绝对具体的心态。本质上, 强制转换不会改变它的工作方式, 它完全按照它说的去做, 分配内存, 强制转换不会影响它, 你得到相同的内存, 即使你错误地将它投给别的东西 (并以某种方式逃避编译器错误) C 将访问它 t 他同样的方式。

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

第12个答案

GNU c 图书馆参考手册是这样说的:

您可以将结果存储 malloc 到没有强制转换的任何指针变量中, 因为 ISO C 会在必要时自动将类型转换为 void * 另一种类型的指针。但是, 在赋值运算符以外的上下文中, 或者如果您可能希望代码在传统 C 中运行, 则强制转换是必需的。

事实上, Iso C11 标准(第 347 页) 是这样说的:

如果分配成功返回的指针将适当对齐, 以便可以将其分配给具有基本对齐要求的任何类型的对象, 然后用于访问此类对象或分配空间中的此类对象的数组 (直到 t 空间被明确释放)

第13个答案

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

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

第14个答案

返回的类型为 void *, 可以将其转换为所需的数据指针类型, 以便不可取消引用。

第15个答案

在 C 语言中, 可以将 void 指针分配给任何指针, 这就是为什么不应使用类型转换的原因。如果您想要 "类型安全" 分配, 我可以推荐以下宏函数, 我总是在我的 C 项目中使用:


#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];

< len(a);="" i++)="" {="" ...="" }="">
第16个答案

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

第17个答案

习惯了海合会和的人都被宠坏了。 外面并没有那么好。

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

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

根据目前的标准没有必要的论点是相当正确的。 但这一论点忽略了现实世界的实用性。 我们不是在一个完全由当时的标准来统治的世界里编码的, 而是在我喜欢称之为 "地方管理的现实领域" 的实用性的情况下编码的。 而这是弯曲和扭曲比以往任何时候都更多的时空。:-)

YMMV。

我倾向于认为把 malloc 当作一种防御性的行动。 不漂亮, 不完美, 但一般都是安全的。 (老实说, 如果你没有包括 stdlibh, 那么你有更多的问题比铸造 malloc!)

第18个答案

我在强制转换中只是为了表示对类型系统中丑陋漏洞的不赞成, 它允许代码 (如以下代码段) 在不进行诊断的情况下进行编译, 即使没有强制转换用于导致糟糕的转换:

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

我希望它不存在 (它不存在于 C++ 中), 所以我投了。它代表了我的品味, 也代表了我的编程政治。我不仅投了一个指针, 而且有效地投了一张选票, 把愚蠢的恶魔赶出去。如果我不能真正摆脱愚蠢, 那么至少让我以抗议的姿态表达这样做的愿望。

事实上, 一个好的做法是 malloc 用返回的函数包装 (和朋友), unsigned char * 基本上永远不会 void * 在代码中使用。 如果您需要一个通用的指针到任何对象, 请使用 a char *unsigned char * , 并具有双向的强制转换。也许, 可以放纵的一种放松是使用 memset 类似和 memcpy 不使用强制转换的功能。

关于强制转换和 C++ 兼容性的主题, 如果编写代码以使其同时编译为 C 和 C++ (在这种情况下, 您必须malloc 将其分配给其他内容时强制转换其返回值) void * , 则可以为自己做一件非常有用的事情: 可以使用宏进行强制转换, 这些宏在编译为 c++ 时转换为 C++ 样式强制转换, 但在编译为 C 时简化为 C 强制转换:

/* In a header somewhere */
#ifdef __cplusplus
#define strip_qual(TYPE, EXPR) (const_cast(EXPR))
#define convert(TYPE, EXPR) (static_cast(EXPR))
#define coerce(TYPE, EXPR) (reinterpret_cast(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 , 但程序的变化方式, 现在涉及类型转换, 您将得到一个诊断, 你必须使用转换的组合来获得所需的转换。

为了帮助您坚持这些宏, GNU C++ (而不是 C!) 编译器有一个漂亮的功能: 为所有出现的 C 样式强制转换而生成的可选诊断。

 -如果在
     c++ 程序中使用了转换为非 void 类型的旧式 (c 样式), 则会发出潜在样式转换 (仅限 c++ 和 Obje-c++ +) 警告
     .  新型强制转换 (动态强制转换、
     静态强制转换、重新解释 \_ cast 和 const \_ cast) 更不容易
     受到意外影响, 并且更易于搜索。

如果 C 代码编译为 C++, 则可以使用此 -Wold-style-cast 选项找出 (type) 可能会爬入代码中的所有转换语法, 并通过在上述宏 (或组合中进行适当选择) 将其替换为相应的诊断来跟进这些诊断, 如有必要)。

这种转换的处理是在 "干净 c" 中工作的唯一最大的独立技术理由: 组合 C 和 C++ 方言, 这反过来又证明技术上证明有理由铸造返回值 malloc

第19个答案

在可能的情况下, 在 C 中编程时, 最好的做法是:

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

通过此过程, 您可以利用 C++ 严格的类型检查, 从而减少 bug 的数量。特别是, 这个程序迫使你包括或 stdlib.h 你会得到

malloc未在此范围内声明

也迫使你投的结果 malloc , 否则你会得到

void* 无效转换为T*

或者你的目标类型是什么

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

  1. C 有一个指定得很好的 ABI
  2. C++ 可以生成更多代码 [异常、RTTI、模板、运行时多态性]

请注意, 在理想情况下, 当将 C 的公共子集与静态多态性功能一起使用时, 第二个缺点应该会消失。

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

auto memblock=static_cast(malloc(n*sizeof(T))); //Mult may overflow...
第20个答案

不, 你不会投出的结果 malloc()

一般情况下, 你不会投进出 void *

不这样做的一个典型原因是失败可能 #include 会被忽视。由于 C99 使隐式函数声明成为非法, 因此如果编译器至少符合 c99, 您将收到一条诊断消息, 因此这已经不是一个问题了。

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

在 C 中,指针强制转换几乎总是一个错误。这是由于以下规则 (§6.5 p7 在N1570, 最新草稿为 c11):

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

这也称为严格的别名规则。因此, 下面的代码是未定义的行为:

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++ 有一整套不同口味的铸造。

第21个答案

我更喜欢做演员, 但不是手动的。我最喜欢的是使用 g_new g_new0 和宏从滑稽可笑。如果不使用 glib, 我将添加类似的宏。这些宏减少了代码重复, 而不会影响类型安全性。如果类型错误, 则会在非 void 指针之间获得隐式强制转换, 这将导致警告 (C++ 中的错误)。如果您忘记包括定义和的标头 g_new g_new0 , 则会出现错误, g_new 并且 g_new0 两者都采用相同的参数, 这与此不同 malloc calloc 。只需添加 0 即可获得零初始化内存。可以使用 C++ 编译器编译代码, 而无需进行更改。

第22个答案

强制转换仅适用于 C++ 而不是 c. 如果您使用的是 c++ 编译器, 最好将其更改为 C 编译器。

第23个答案

Void 指针背后的概念是, 它可以被强制转换为任何数据类型, 这就是 malloc 返回 void 的原因。此外, 您还必须注意自动类型转换。因此, 它不是强制性的强制转换指针, 虽然你必须这样做。它有助于保持代码的干净, 并有助于调试

第24个答案

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

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

第25个答案
  1. 如其他说明所述, C 不是必需的, 而是 C++ 所必需的。

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

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

  4. 但是, 如果你投然后, 它可以隐藏一个错误, 如果你忘了包括 stdlib.h。这可能会导致崩溃 (或者更糟糕的是, 直到后来在代码的某些完全不同的部分, 才会导致崩溃)。

    因为stdlib. h包含 malloc 的原型被找到。在没有 malloc 原型的情况下, 标准要求 C 编译器假定 malloc 返回 int。如果没有强制转换, 则在将此整数分配给指针时发出警告; 如果没有强制转换, 则会发出警告。 然而, 与演员, 这个警告不会产生, 隐藏一个错误。

第26个答案

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

C 中不需要进行铸造, 因为:

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

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

相关问题

我投的是 malloc 的结果吗? 为什么是统计:: st _ size 0 为设备, 但同时 lseek 正确地定义了设备大小? 允许在易失对象上进行优化 如何避免为链表中的每个节点调用 malloc 指针比较如何在 C 中工作?是否可以比较不指向相同数组的指针? 我可以一次检查一小部分 bools 吗? 为什么这声称取消引用类型-punned 指针警告编译器特定?