C语言 不使用sizeof(type),使用sizeof *p,它安全和正确吗?



这个使用安全吗,这段代码用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的操作数。 对于可变长度数组,对其进行计算,并且由于pmalloc调用完成之前无效,因此对其应用*运算符会导致未定义的行为。

章节和经文

6.5.3.2 地址和间接寻址运算符
...
4 一元*运算符表示间接寻址。如果操作数指向函数,则结果为 函数指示符;如果它指向一个对象,则结果是一个指示 对象。如果操作数的类型为"指向类型的指针",则结果的类型为">type"。如果 已为指针分配无效值,一元*运算符的行为是 定义。102(

6.5.3.4 运算符的大小和_Alignof

2sizeof运算符产生其操作数的大小(以字节为单位(,这可能是 表达式或类型的括号名称。大小由类型确定 操作数。结果是一个整数。如果操作数的类型是可变长度数组 类型,计算操作数;否则,不计算操作数,结果为 整数常量。
102( ... 一元*运算符取消引用指针的无效值包括空指针,即 地址与指向的对象类型以及对象地址在 其生命周期结束。

现在,我已经多次使用上述代码并且从未遇到任何问题,但这并不能保证任何事情 - 我只在一个特定的架构(x68(和有限的编译器范围内使用它。 不能保证它不会在一些古怪的架构上爆炸。

不过,这是一个非常有用的成语,我有点希望标准措辞更好,以正确定义它。 我没有最新的副本(2011 年是我可以访问的最新版本(,所以我不知道他们是否调整了该语言。

相关内容

  • 没有找到相关文章