c/c++ 中难以理解的语法!!为什么我们可以像这样 malloc 缓冲区?
Incomprehensible syntax in c/c++!! why can we malloc a buffer like this?
今天,我注意到一堆代码实例化了一个结构,如下所示,而 ServiceMsg 是一个结构。
ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);
没见过malloc这么用的。谁能给我解释一下???
正如我所知,缓冲区的大小通常像这样分配 malloc(sizeof(int) * n) ..
在代码段中,
ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);
与
相同
ServiceMsg *msg = (ServiceMsg *)malloc( 1* sizeof(*msg));
最好写成
ServiceMsg *msg = malloc(sizeof(*msg));
具体来说,malloc()
需要一个大小作为参数,在这个调用中,我们传递的大小是 sizeof(*msg)
,即 sizeof (ServiceMsg )
.
也就是说,please see this discussion on why not to cast the return value of malloc() and family in C..
如果在表达式看起来像乘法表达式的地方重写这个调用会更清楚
malloc(sizeof * msg);
喜欢
malloc(sizeof *msg);
sizeof
运算符定义如下
sizeof unary-expression
sizeof ( type-name )
取消引用指针 msg
的表达式 *msg
是一元表达式。确定表达式的类型(在本例中为 ServiceMsg
)并计算该类型对象的大小。不评估表达式本身。编译器只确定它的类型。
您可以将这个一元表达式括在括号中,得到一个像
这样的主表达式
malloc(sizeof ( *msg ) );
在此记录中,此记录与此记录相似
malloc(sizeof ( ServiceMsg ) );
有一些细节可能会让人感到困惑:
sizeof
运算符可以有两种使用方式:要么使用括号 sizeof(type)
,但参数必须是类似于 int
的类型。或者不带括号 sizeof expr
,则参数可以是表达式。但是当然,表达式本身可以包含括号...
在你的例子中 sizeof *msg
取表达式 *msg
的大小,这意味着取消引用指针 msg
(它不是乘法运算符!)所以我们最终得到一个 ServiceMsg
类型的“左值”临时变量。其大小为 ServiceMsg
.
通常情况下,在声明的同一行取消引用一个指针变量是一个很大的禁忌,它没有被初始化并且还没有指向任何合理的位置。要注意的是 sizeof
运算符实际上并不计算(执行)它的操作数,因此表达式 *msg
永远不会被执行并且取消引用永远不会发生。
这就是代码有效的原因。这种风格甚至经常被推荐,因为你不必担心指针指向什么类型 - 无论你分配什么,它总是 type* ptr = malloc(sizeof *ptr);
。
在您的代码中转换为 (ServiceMsg*)
是多余的。
稍微清理一下,我们得到
ServiceMsg *msg = malloc(sizeof *msg);
与
完全相同
ServiceMsg *msg = malloc(N * sizeof *msg);
其中 N == 1
。这是 C 中用于动态分配内存的相当常见的习惯用法:
T *p = malloc( N * sizeof *p ); // for any non-function type T
或
T *p;
...
p = malloc( N * sizeof *p );
或
T *p = calloc( N, sizeof *p );
或
T *p;
...
p = calloc( N, sizeof *p );
优点是类型信息不重复,维护起来更容易。
转换是不必要的1,并且由于表达式 *msg
的 type 是 ServiceMessage
, sizeof *msg == sizeof( ServiceMessage )
.
请记住 sizeof
是一个运算符,而不是一个函数 - 只有当操作数是类型名称如 ServiceMessage
或 int
或 char
时才需要括号,或者如果它是像 1 + 2
这样的算术表达式。由于 sizeof
是一元运算符,它的优先级高于算术运算符,因此 sizeof 1 + 2
被解析为 (sizeof 1) + 2
,这可能不是您想要的。
- 除非您将此代码编译为 C++ 或使用 ancient K&R 实现。在 C89 之前,
*alloc
函数返回 char *
而不是 void *
,因此如果目标不是 char *
,则需要强制转换。 C++ 不允许从 void *
到其他指针类型的隐式转换,因此它也需要强制转换,但如果您正在编写 C++,则无论如何都不应该使用 C 风格的内存管理。
今天,我注意到一堆代码实例化了一个结构,如下所示,而 ServiceMsg 是一个结构。
ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);
没见过malloc这么用的。谁能给我解释一下??? 正如我所知,缓冲区的大小通常像这样分配 malloc(sizeof(int) * n) ..
在代码段中,
ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);
与
相同 ServiceMsg *msg = (ServiceMsg *)malloc( 1* sizeof(*msg));
最好写成
ServiceMsg *msg = malloc(sizeof(*msg));
具体来说,malloc()
需要一个大小作为参数,在这个调用中,我们传递的大小是 sizeof(*msg)
,即 sizeof (ServiceMsg )
.
也就是说,please see this discussion on why not to cast the return value of malloc() and family in C..
如果在表达式看起来像乘法表达式的地方重写这个调用会更清楚
malloc(sizeof * msg);
喜欢
malloc(sizeof *msg);
sizeof
运算符定义如下
sizeof unary-expression
sizeof ( type-name )
取消引用指针 msg
的表达式 *msg
是一元表达式。确定表达式的类型(在本例中为 ServiceMsg
)并计算该类型对象的大小。不评估表达式本身。编译器只确定它的类型。
您可以将这个一元表达式括在括号中,得到一个像
这样的主表达式malloc(sizeof ( *msg ) );
在此记录中,此记录与此记录相似
malloc(sizeof ( ServiceMsg ) );
有一些细节可能会让人感到困惑:
sizeof
运算符可以有两种使用方式:要么使用括号sizeof(type)
,但参数必须是类似于int
的类型。或者不带括号sizeof expr
,则参数可以是表达式。但是当然,表达式本身可以包含括号...在你的例子中
sizeof *msg
取表达式*msg
的大小,这意味着取消引用指针msg
(它不是乘法运算符!)所以我们最终得到一个ServiceMsg
类型的“左值”临时变量。其大小为ServiceMsg
.通常情况下,在声明的同一行取消引用一个指针变量是一个很大的禁忌,它没有被初始化并且还没有指向任何合理的位置。要注意的是
sizeof
运算符实际上并不计算(执行)它的操作数,因此表达式*msg
永远不会被执行并且取消引用永远不会发生。
这就是代码有效的原因。这种风格甚至经常被推荐,因为你不必担心指针指向什么类型 - 无论你分配什么,它总是 type* ptr = malloc(sizeof *ptr);
。
在您的代码中转换为 (ServiceMsg*)
是多余的。
稍微清理一下,我们得到
ServiceMsg *msg = malloc(sizeof *msg);
与
完全相同ServiceMsg *msg = malloc(N * sizeof *msg);
其中 N == 1
。这是 C 中用于动态分配内存的相当常见的习惯用法:
T *p = malloc( N * sizeof *p ); // for any non-function type T
或
T *p;
...
p = malloc( N * sizeof *p );
或
T *p = calloc( N, sizeof *p );
或
T *p;
...
p = calloc( N, sizeof *p );
优点是类型信息不重复,维护起来更容易。
转换是不必要的1,并且由于表达式 *msg
的 type 是 ServiceMessage
, sizeof *msg == sizeof( ServiceMessage )
.
请记住 sizeof
是一个运算符,而不是一个函数 - 只有当操作数是类型名称如 ServiceMessage
或 int
或 char
时才需要括号,或者如果它是像 1 + 2
这样的算术表达式。由于 sizeof
是一元运算符,它的优先级高于算术运算符,因此 sizeof 1 + 2
被解析为 (sizeof 1) + 2
,这可能不是您想要的。
- 除非您将此代码编译为 C++ 或使用 ancient K&R 实现。在 C89 之前,
*alloc
函数返回char *
而不是void *
,因此如果目标不是char *
,则需要强制转换。 C++ 不允许从void *
到其他指针类型的隐式转换,因此它也需要强制转换,但如果您正在编写 C++,则无论如何都不应该使用 C 风格的内存管理。