【问题标题】:Incomprehensible syntax in c/c++!! why can we malloc a buffer like this?c/c++ 中的语法难以理解!!为什么我们可以 malloc 像这样的缓冲区?
【发布时间】:2021-02-25 05:15:44
【问题描述】:

今天,我注意到一堆代码将如下所示的结构实例化,而 ServiceMsg 是一个结构。

ServiceMsg *msg = (ServiceMsg *)malloc(sizeof * msg);

我从来没有见过 malloc 可以这样使用。谁能给我解释一下??? 正如我所知,缓冲区的大小通常像这样分配 malloc(sizeof(int) * n) ..

【问题讨论】:

  • 那是sizeof(*msg),不是乘法。 (而new msg 会好一百万倍(而智能指针又好一百万倍(而且没有称为C/C++ 的语言))
  • @john 为什么会这样?
  • @KenY-N 你知道,如果不是 *msg 之间的空格,我想我会发现的。
  • 向 OP 说清楚。仅当 X 是一种类型时,sizeof(X) 才需要括号。如果X 是一个表达式(如上),则它们不是。

标签: c syntax malloc sizeof


【解决方案1】:

有一些细节可能会让人感到困惑:

  • sizeof 运算符可以以两种方式使用:使用括号 sizeof(type),但参数必须是 int 之类的类型。或者没有括号sizeof expr,那么参数可以是一个表达式。但当然,表达式本身可以包含括号...

  • 在您的情况下,sizeof *msg 采用表达式 *msg 的大小,这意味着取消引用指针 msg(它不是乘法运算符!)所以我们最终得到一个类型为“左值”的临时变量ServiceMsg。其大小为ServiceMsg

  • 通常,在声明的同一行取消引用指针变量将是一个很大的禁忌,它没有被初始化并且还没有指向任何合理的位置。问题是 sizeof 运算符实际上并没有计算(执行)它的操作数,所以表达式 *msg 永远不会被执行并且取消引用永远不会发生。

这就是代码有效的原因。甚至经常推荐这种样式,因为您不必担心指针指向的类型 - 无论您分配什么,它始终是 type* ptr = malloc(sizeof *ptr);

在您的代码中转换为(ServiceMsg*) 是多余的。

【讨论】:

  • 感谢您的解释!!但这意味着什么:类型 ptr* = malloc(sizeof *ptr) ??我将类型更改为 int 并在我的机器上尝试过,它在编译时阻塞。
  • @CaiyiZhou 这是一个错字,已修复。但除此之外,我的意思是你可以在那里写任何有效的类型,而不是type,它的工作原理是一样的。
【解决方案2】:

稍微清理一下,我们得到

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类型ServiceMessagesizeof *msg == sizeof( ServiceMessage )

请记住,sizeof 是运算符,而不是函数 - 仅当操作数是类型名称(如 ServiceMessageintchar),或者是算术表达式(如 1 + 2)时才需要括号.由于sizeof 是一元运算符,它的优先级高于算术运算符,因此sizeof 1 + 2 被解析为(sizeof 1) + 2,这可能不是您想要的。


  1. 除非您将此代码编译为 C++ 或使用 ancient K&R 实现。在 C89 之前,*alloc 函数返回 char * 而不是 void *,因此如果目标不是 char *,则需要进行强制转换。 C++ 不允许从 void * 隐式转换到其他指针类型,因此它也需要强制转换,但是如果您正在编写 C++,则无论如何都不应该使用 C 样式的内存管理。

【讨论】:

    【解决方案3】:

    如果在表达式看起来像乘法表达式的地方重写这个调用会更清楚

    malloc(sizeof * msg);
    

    喜欢

    malloc(sizeof *msg);
    

    sizeof 运算符定义如下

    sizeof unary-expression
    sizeof ( type-name )
    

    解引用指针msg 的表达式*msg 是一元表达式。确定表达式的类型(在本例中为ServiceMsg)并计算此类型对象的大小。不计算表达式本身。编译器只确定它的类型。

    你可以把这个一元表达式括在括号中,得到一个主要的表达式,比如

    malloc(sizeof ( *msg ) );
    

    在这条记录中看起来与这条记录相似

    malloc(sizeof ( ServiceMsg ) );
    

    【讨论】:

      【解决方案4】:

      在sn-p中,

        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..

      【讨论】:

      • ... 或ServiceMsg* msg = new ServiceMsg; 甚至auto msg = std::make_unique<ServiceMsg>();
      • @TedLyngmo 仅适用于 CPP,我不会在 CPP 中使用 malloc
      • 唉,我没有看到问题的双重标记...
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-01
      • 2014-07-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多