16

我在这个帖子上的回答得到了评论:

函数调用中的 Malloc 似乎在返回时被释放?

简而言之,我有这样的代码:

int * somefunc (void)
{
  int * temp = (int*) malloc (sizeof (int));
  temp[0] = 0;
  return temp;
}

我得到了这个评论:

我可以说,请不要投射malloc的返回值吗?它不是必需的,可以隐藏错误。

我同意 C 中不需要强制转换。它在 C++ 中是强制性的,所以我通常添加它们以防万一有一天我必须在 C++ 中移植代码。

但是,我想知道这样的演员阵容如何隐藏错误。有任何想法吗?

编辑:

似乎双方都有非常好的和有效的论点。感谢您的发布,伙计们。

4

12 回答 12

17

我发表了一个答案似乎很合适,因为我留下了评论:P

基本上,如果您忘记包含stdlib.h,编译器将假定malloc返回一个int. 如果不进行强制转换,您将收到警告。铸造你不会。

因此,通过强制转换,您将一无所获,并冒着压制合法警告的风险。

很多关于这个的文章,一个快速的谷歌搜索会出现更详细的解释。

编辑

有人认为

TYPE * p;
p = (TYPE *)malloc(n*sizeof(TYPE));

当你不小心没有分配足够的内存时,这很明显,因为你认为pwas TYPenot TYPE,因此我们应该强制转换 malloc 因为这种方法的优势覆盖了意外抑制编译器警告的较小成本。

我想指出两点:

  1. 你应该写p = malloc(sizeof(*p)*n);总是确保你分配了正确的空间
  2. 使用上述方法,如果您更改 的类型,则需要在 3 个地方进行更改p:一次在声明中,一次在 中malloc,一次在演员表中。

简而言之,我个人仍然认为没有必要强制转换的返回值,malloc这当然不是最佳实践。

于 2008-09-20T17:17:51.077 回答
11

这个问题被标记为 C 和 C++,所以它至少有两个答案,恕我直言:

C

咳咳……随心所欲。

我相信上面给出的原因“如果你不包含“stdlib”那么你就不会收到警告”不是一个有效的原因,因为人们不应该依赖这种黑客来不要忘记包含一个标题。

可能使您编写强制转换的真正原因是 C 编译器已经默默地将 a 强制void *转换为您想要的任何指针类型,因此,自己做是矫枉过正和无用的。

如果您想要类型安全,您可以切换到 C++ 或编写自己的包装函数,例如:

int * malloc_Int(size_t p_iSize) /* number of ints wanted */
{
   return malloc(sizeof(int) * p_iSize) ;
}

C++

有时,即使在 C++ 中,您也必须从 malloc/realloc/free 实用程序中获利。然后你必须投。但你已经知道了。与往常一样,使用 static_cast<>() 会比 C 风格的强制转换更好。

而在 C 语言中,您可以通过模板覆盖 malloc(和 realloc 等)以实现类型安全:

template <typename T>
T * myMalloc(const size_t p_iSize)
{
 return static_cast<T *>(malloc(sizeof(T) * p_iSize)) ;
}

这将被用作:

int * p = myMalloc<int>(25) ;
free(p) ;

MyStruct * p2 = myMalloc<MyStruct>(12) ;
free(p2) ;

和以下代码:

// error: cannot convert ‘int*’ to ‘short int*’ in initialization
short * p = myMalloc<int>(25) ;
free(p) ;

不会编译,所以,没问题

总而言之,在纯 C++ 中,如果有人在您的代码中发现多个 C malloc,您现在没有任何借口... :-)

C + C++ 交叉

有时,您想要生成可以在 C 和 C++ 中编译的代码(无论出于何种原因……这不是 C++extern "C" {}块的重点吗?)。在这种情况下,C++ 需要强制转换,但 C 不会理解 static_cast 关键字,因此解决方案是 C 风格的强制转换(正是由于这种原因,在 C++ 中仍然是合法的)。

请注意,即使编写纯 C 代码,使用 C++ 编译器编译它也会给您带来更多警告和错误(例如,尝试使用函数而不首先声明它不会编译,这与上面提到的错误不同)。

因此,为了安全起见,编写可以在 C++ 中干净编译的代码,研究并纠正警告,然后使用 C 编译器生成最终的二进制文件。这意味着,再次以 C 风格的演员表编写演员表。

于 2008-09-20T18:55:22.683 回答
7

它可能引入的一个可能错误是,如果您使用 C(不是 C++)在 64 位系统上进行编译。

基本上,如果您忘记包含stdlib.h,则将应用默认的 int 规则。因此,编译器会很高兴地假设在许多 64 位系统上malloc具有原型,int malloc();int 是 32 位,指针是 64 位。

哦,值被截断了,你只能得到指针的低 32 位!现在,如果你转换 的返回值malloc,这个错误会被转换隐藏。但如果你不这样做,你会得到一个错误(类似于“无法将 int 转换为 T *”的性质)。

这当然不适用于 C++,原因有两个。首先,它没有默认的 int 规则,其次它需要强制转换。

总而言之,无论如何,你应该只是新的 C++ 代码:-P。

于 2008-09-20T18:47:15.073 回答
6

好吧,我认为恰恰相反——总是直接将其转换为所需的类型。继续阅读这里!

于 2008-09-20T17:19:16.003 回答
2

“忘记了 stdlib.h”的论点是一个稻草人。现代编译器将检测并警告问题(gcc -Wall)。

您应该始终立即转换 malloc 的结果。不这样做应该被认为是一个错误,而不仅仅是因为它会像 C++ 一样失败。例如,如果您的目标是具有不同类型指针的机器架构,那么如果您不进行强制转换,您可能会遇到一个非常棘手的错误。

编辑:评论员埃文·特兰是正确的。我的错误是认为编译器不必在任何上下文中对 void 指针做任何工作。当我想到 FAR 指针错误时,我会发疯,所以我的直觉是强制转换所有内容。谢谢埃文!

于 2008-09-20T18:58:44.610 回答
1

实际上,转换可以隐藏错误的唯一方法是,如果您正在从一种数据类型转换为较小的数据类型并丢失数据,或者您正在将梨转换为苹果。举个例子:

int int_array[10];
/* initialize array */
int *p = &(int_array[3]);
short *sp = (short *)p;
short my_val = *sp;

在这种情况下,转换为 short 会从 int 中删除一些数据。然后这个案例:

struct {
    /* something */
} my_struct[100];

int my_int_array[100];
/* initialize array */
struct my_struct *p = &(my_int_array[99]);

您最终会指向错误类型的数据,甚至指向无效的内存。

但总的来说,如果你知道自己在做什么,就可以进行选角。更重要的是,当您从 malloc 获取内存时,它恰好返回一个 void 指针,除非您强制转换它,否则您根本无法使用它,并且大多数编译器会警告您如果您正在转换为左值(作业的左侧)无论如何都不能接受。

于 2008-09-20T17:25:26.690 回答
1
#if CPLUSPLUS
#define MALLOC_CAST(T) (T)
#else
#define MALLOC_CAST(T)
#endif
...
int * p;
p = MALLOC_CAST(int *) malloc(sizeof(int) * n);

或者,交替

#if CPLUSPLUS
#define MYMALLOC(T, N) static_cast<T*>(malloc(sizeof(T) * N))
#else
#define MYMALLOC(T, N) malloc(sizeof(T) * N)
#endif
...
int * p;
p = MYMALLOC(int, n);
于 2008-09-28T02:40:16.707 回答
1

stdlib.h人们已经引用了我通常提出的原因:关于不包括和使用的旧(不再适用于大多数编译器)论点sizeof *p以确保类型和大小始终匹配,而不管以后是否更新。我确实想指出另一个反对强制转换的论点。这是一个小问题,但我认为它适用。

C 是相当弱类型的。大多数安全类型转换是自动发生的,而大多数不安全类型转换需要强制转换。考虑:

int from_f(float f)
{
    return *(int *)&f;
}

那是危险的代码。它在技术上是未定义的行为,但实际上它会在几乎每个运行它的平台上做同样的事情。演员帮助告诉你“这个代码是一个可怕的黑客。”

考虑:

int *p = (int *)malloc(sizeof(int) * 10);

我看到一个演员,我想知道,“为什么这是必要的?黑客在哪里?” 它使我的脖子毛骨悚然,发生了一些邪恶的事情,而实际上代码是完全无害的。

只要我们使用 C,强制转换(尤其是指针强制转换)就是一种表达“这里发生了一些邪恶且容易破坏的事情”的方式。他们可能会完成您需要完成的事情,但他们会向您和未来的维护者表明孩子们不好。

在 every 上使用强制转换会malloc减少指针强制转换的“hack”指示。看到类似*(int *)&f;.

注意:C 和 C++ 是不同的语言。C 是弱类型,C++ 是强类型。强制转换在 C++中是必要的,即使它们根本不表示 hack,因为(在我看来)不必要的强大 C++ 类型系统。(真的,这个特殊情况是我认为 C++ 类型系统“太强”的唯一地方,但我想不出它“太弱”的任何地方,这使得它总体上对我的口味来说太强了。)

如果您担心 C++ 兼容性,请不要担心。如果您正在编写 C,请使用 C 编译器。每个平台都有很多非常好的可用。如果出于某种荒谬的原因,您必须编写可以干净地编译为 C++ 的 C 代码,那么您并不是真的在编写 C。如果您需要将 C 移植到 C++,您应该进行大量更改以使您的 C 代码更符合习惯C++。

如果你不能做到这一点,那么无论你做什么,你的代码都不会很漂亮,所以此时你决定如何转换并不重要。我确实喜欢使用模板创建一个返回正确类型的新分配器的想法,尽管这基本上只是重新发明了new关键字。

于 2010-02-10T18:31:23.587 回答
0

将返回 (void *) 的函数转换为 (int *) 是无害的:您将一种类型的指针转​​换为另一种类型。

将返回整数的函数转换为指针很可能是不正确的。如果您没有明确地转换它,编译器会标记它。

于 2008-09-20T17:17:06.770 回答
0

一个可能的错误可能(取决于您是否真正想要)以一种大小的比例进行分配,并分配给不同类型的指针。例如,

int *temp = (int *)malloc(sizeof(double));

在某些情况下您可能想要这样做,但我怀疑它们很少见。

于 2008-09-20T17:19:08.280 回答
0

我认为你应该把演员表放进去。考虑到类型有三个位置:

T1 *p;
p = (T2*) malloc(sizeof(T3));

这两行代码可能相距甚远。因此,编译器强制执行T1 == T2 是件好事。更容易直观地验证 T2 == T3。

如果你错过了T2演员,那么你必须希望T1 == T3。

另一方面,您缺少 stdlib.h 参数-但我认为这不太可能成为问题。

于 2008-09-20T17:41:26.197 回答
-1

另一方面,如果您需要将代码移植到 C++,最好使用“new”运算符。

于 2008-09-20T17:21:46.137 回答