这个使用安全吗,这段代码用gcc 4.9.2编译没有任何错误或警告
widget *p;
...
p = malloc(sizeof *p);
我在SEI CERT C编码标准网站上找到了这个。
点击这里 -- 没有类型不匹配问题,无需铸造。 每次都分配适当的内存量。
struct widget;
typedef struct widget widget_t;
struct gadget;
typedef struct gadget gadget_t;
widget_t *newWidget(void)
{
widget_t *p = malloc(sizeof *p);
if (p)
/* initialize members of *p as necessary */
return p;
}
gadget_t *newGadget(void)
{
gadget_t *p = malloc(sizeof *p);
if (p)
/* initialize members of *p as necessary */
return p;
}
void deleteWidget(widget_t **p)
{
/* delete any subelements of *p */
free(*p);
*p = NULL;
}
void deleteGadget(gadget_t **p)
{
/* delete any subelements of *p */
free(*p);
*p = NULL;
}
...
widget_t *p = newWidget();
gadget_t *g = newGadget();
if (p)
/* do stuff with p */
if (g)
/* do stuff with g */
...
deleteWidget(&p);
deleteGadget(&g);
sizeof 运算符具有以下定义
sizeof unary-expression
sizeof ( type-name )
因此在本声明中
widget_t *p = malloc(sizeof *p);
使用了运算符的第一种形式,其中表达式*p
是一元表达式,并且具有widget_t
的类型。
因此这些声明
widget_t *p = malloc(sizeof *p);
widget_t *p = malloc(sizeof( widget_t ) );
是完全等价的。
第一个声明更可取,因为 sizeof 运算符中的表达式不依赖于实际类型。也就是说,指针的类型可以更改,但声明将有效,无需任何其他更改。
在 C 语言中,不需要将从 malloc 返回的指针强制转换为分配的 lvalue 的类型,因为 void * 类型的指针可以分配给指向任何类型的对象的指针。它有时被用来(而且有时有用(使程序自我记录。
这是一个很好的编码实践!
想象一下这段代码
struct structv1 *p1 = malloc(sizeof (struct structv1));
struct structv1 *p2 = malloc(sizeof *p2);
更改为
struct structv2 *p1 = malloc(sizeof (struct structv2));
// ^^^^^^^^ ^^^^^^^^^^^^^^^^^
// two changes! maybe the programmer forgets one of them
struct structv2 *p2 = malloc(sizeof *p2);
// ^^^^^^^^
// one change only. the argument to malloc is already correct
简短的回答:是的,这是安全的。
sizeof
不是一个函数,而是一个运算符。如所示,它返回表达式类型的对象表示形式的大小(以字节为单位(。表达式本身不会在运行时计算;相反,它在编译时将类型提供给sizeof
运算符,因此不会造成伤害或犯规。
使用sizeof *p
而不是sizeof (type)
是安全的,但有一个例外 - 如果p
是指向可变长度数组的未初始化或无效指针,则行为是未定义的。 例如:
size_t rows, cols;
...
T (*p)[cols] = malloc( rows * sizeof *p ); // *p is undefined here
对于除可变修改类型之外的每个类型,不计算sizeof
的操作数。 对于可变长度数组,将对其进行计算,并且由于p
在malloc
调用完成之前无效,因此对其应用*
运算符会导致未定义的行为。
章节和经文
6.5.3.2 地址和间接寻址运算符
...
4 一元*
运算符表示间接寻址。如果操作数指向函数,则结果为 函数指示符;如果它指向一个对象,则结果是一个指示 对象。如果操作数的类型为"指向类型的指针",则结果的类型为">type"。如果 已为指针分配无效值,一元*
运算符的行为是 定义。102(
。
6.5.3.4 运算符的大小和_Alignof
2sizeof
运算符产生其操作数的大小(以字节为单位(,这可能是 表达式或类型的括号名称。大小由类型确定 操作数。结果是一个整数。如果操作数的类型是可变长度数组 类型,计算操作数;否则,不计算操作数,结果为 整数常量。
102( ... 一元*
运算符取消引用指针的无效值包括空指针,即 地址与指向的对象类型以及对象地址在 其生命周期结束。
现在,我已经多次使用上述代码并且从未遇到任何问题,但这并不能保证任何事情 - 我只在一个特定的架构(x68(和有限的编译器范围内使用它。 不能保证它不会在一些古怪的架构上爆炸。
不过,这是一个非常有用的成语,我有点希望标准措辞更好,以正确定义它。 我没有最新的副本(2011 年是我可以访问的最新版本(,所以我不知道他们是否调整了该语言。