在C中不必要的指针转换

时间:2008-09-20 17:11:40

标签: c++ c pointers

我在这个帖子上对我的回答发表了评论:

Malloc inside a function call appears to be getting freed on return?

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

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

我收到了这条评论:

  

我可以说,请不要施放   malloc的返回值?它不是   需要并且可以隐藏错误。

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

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

编辑:

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

12 个答案:

答案 0 :(得分:17)

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

基本上,如果您忘记包含stdlib.h,编译器会认为malloc会返回int。没有施法,你会收到警告。铸造你不会。

因此,通过施放你什么也得不到,并冒着压制合法警告的风险。

关于这一点的文章很多,快速的谷歌搜索会提供更详细的解释。

修改

有人认为

TYPE * p;
p = (TYPE *)malloc(n*sizeof(TYPE));
当你意外地没有分配足够的内存时,

显而易见,因为你认为pTYPe而不是TYPE,因此我们应该投射malloc,因为这种方法的优势覆盖了意外抑制编译器警告的较小成本。

我想指出两件事:

  1. 你应该写p = malloc(sizeof(*p)*n);来始终确保你有足够的空间
  2. 使用上述方法,如果您更改了p的类型,则需要在3个位置进行更改:一次在声明中,一次在malloc中,一次在演员表中。
  3. 简而言之,我个人认为没有必要强制转换malloc的返回值,这当然不是最佳做法。

答案 1 :(得分:11)

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

C

唉......做你想做的事。

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

可能让你编写演员表的真正原因是C编译器已经静默地将void *转换为你想要的任何指针类型,因此,自己动手做就是矫枉过正没用。

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

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

C ++

有时,即使在C ++中,您也必须从malloc / realloc / free utils中获利。然后你将不得不施展。但你已经知道了。与往常一样,使用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风格的演员阵容写出演员阵容。

答案 2 :(得分:7)

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

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

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

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

总而言之,无论如何,你应该只使用c ++代码:-P。

答案 3 :(得分:6)

嗯,我认为这恰恰相反 - 总是直接将其转换为所需的类型。 Read on here!

答案 4 :(得分:2)

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

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

修改:评论员Evan Teran是正确的。我的错误是认为编译器不必在任何上下文中对void指针进行任何操作。当我想到FAR指针错误时,我发飙,所以我的直觉就是抛出一切。谢谢Evan!

答案 5 :(得分: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指针,除非你施放它,并且大多数编译器会警告你,如果你正在向lvalue投射(值为无论如何,任务的左侧都无法接受。

答案 6 :(得分: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);

答案 7 :(得分:1)

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

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

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

这是危险的代码。它在技术上是未定义的行为,但在实践中它几乎在你运行它的每个平台上做同样的事情。演员阵容有助于告诉你“这段代码非常糟糕。”

考虑:

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

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

只要我们使用C,演员(特别是指针演员)就是一种说法,“这里有一些邪恶的,容易破碎的东西。”他们可能会完成你需要完成的任务,但是他们向你和未来的维护人员表明孩子们不正常。

在每个malloc上使用强制转换会减少指针转换的“黑客”指示。这样可以减少看到*(int *)&f;等内容的刺激。

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

如果您担心C ++兼容性,请不要这样做。如果您正在编写C,请使用C编译器。每个平台都有很多非常好的产品。如果,出于某种无聊的原因,你来编写C ++编译干净的C代码,那么你真的不是在编写C语言。如果你需要将C语言移植到C ++,你应该做很多改动使你的C代码更加惯用C ++。

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

答案 8 :(得分:0)

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

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

答案 9 :(得分:0)

一个可能的错误(取决于这是你真正想要与否)是用一个大小规模进行mallocing,并分配给不同类型的指针。如,

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

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

答案 10 :(得分:0)

我认为你应该把演员投入。考虑到类型有三个位置:

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

这两行代码可能会分开。因此,编译器enforce T1 == T2是好的。更容易直观地验证T2 == T3。

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

另一方面,你有缺少的stdlib.h参数 - 但我认为它不太可能是一个问题。

答案 11 :(得分:-1)

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