为什么在malloc中使用sizeof(*pointer)更安全

54

给定

struct node
{
     int a;
     struct node * next;
};

为了分配一个新的结构体,请使用malloc函数。

struct node *p = malloc(sizeof(*p));
比...更安全。
struct node *p = malloc(sizeof(struct node));

为什么?我以为它们是一样的。


8
使用变量时,sizeof不需要括号:p = malloc(sizeof*p); - luser droog
64
加上括号后更易读。 - Tomer Arazy
7
@TomerArazy:我不同意,使用括号会使阅读性更差。没有括号,它就不能是一种类型名称;缺少括号有助于人类读者消除歧义。(小问题:此外,括号可能会暗示sizeof是一个函数,但实际上它不是) - wildplasser
15
没有括号,加上一个空格后更易读:sizeof *p - Eric Postpischil
10
加上或去掉括号并不会使这些内容更易读,这只是一些人的看法,而不是事实。但事实是,括号不是必需的,因为sizeof作为一元运算符应用于表达式。如果你不总是使用括号来包含每个一元操作符,那么为什么要始终在sizeof中使用它们呢? - Kaz
3
另外,括号并不能解决任何歧义,因为除非你对C一窍不通,否则你知道 sizeof *p 不可能是 (sizeof *) p,而且也知道它不是一个 sizeof 变量被乘以 p - Kaz
3个回答

78

这样更安全,因为您不必两次提及类型名称,并且不必构建 "dereferenced" 版本的正确拼写。例如,在下面的代码中,您无需 "数星星":

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

将此与C语言中基于类型的sizeof进行比较。

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

在使用 sizeof 时,要确保使用了正确数量的 *

为了切换到另一种类型,您只需要更改一个地方(即 p 的声明),而不是两个地方。而习惯于对 malloc 的结果进行强制转型的人则需要更改三个地方。

更一般地说,坚持以下准则非常有意义:类型名称应出现在声明中而不是其他任何地方。实际语句应与类型无关。它们应尽可能避免提及任何类型名称或使用任何其他特定于类型的功能。

后者意味着:避免不必要的强制类型转换。避免不必要的特定于类型的常量语法(如 0.00L,简单的 0 就足够了)。避免在 sizeof 下提及类型名称。等等。


13
值得注意的是,当更改类型时,在声明p的位置进行更改就足够了(不需要在malloc中进行更改),这不仅意味着工作量减少,而且错误的可能性也降低了。因此,这有助于减少错误。 - Eric Postpischil
AndreyT,我理解你的观点,抽象地说...但是我敢打赌,计算机历史上从来没有一个平台使得sizeof(int ****) != sizeof(int ***) :) - Josh
2
@Josh:你的意思是每次需要知道指针大小时,只需使用 sizeof(double *)。是的,这样做可以实现目的,但像这样的事情应该会产生与未匹配的左括号一样的未解决的紧张感(http://xkcd.com/859/)。 - AnT stands with Russia

22

因为如果在以后的某个时间点 p 被指向另一个结构类型,那么您使用malloc进行内存分配的语句就不必更改,仍然分配了新类型所需的足够内存。 它确保:

  • 您无需每次更改要分配内存的类型时修改内存分配语句。
  • 您的代码更健壮,不容易出现手动错误。

一般来说,不依赖具体类型总是一个好习惯,第一种形式就是这样,它没有硬编码类型。


7

这很方便,因为你可以将这个转换成:

struct node *p= malloc(sizeof(*p));

转化为:

#define MALLOC(ptr)   (ptr) = malloc(sizeof(*(ptr) ))

struct node *p;
MALLOC(p);

或者,对于一个数组:
#define MALLOC_ARR(ptr, arrsize) \
       (ptr) = malloc(sizeof(*(ptr) ) * arrsize)

struct node *p;
MALLOC_ARR(p, 20);

这为什么安全呢?因为使用这些宏的用户不太可能犯下AndreyT指出的错误,就像使用DIM()获取静态数组大小的情况一样。

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

这样做也更加安全,因为用户不需要在多个位置上使静态数组大小保持一致。只需在一个地方设置数组大小,然后使用 DIM() 就可以了!编译器会为您处理。


通常宏参数应该加上 (),所以 .... * arrsize) 应该写成 ... * (arrsize)) - chux - Reinstate Monica

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接