【问题标题】:Why are these C macros not written as functions?为什么这些 C 宏不写成函数?
【发布时间】:2011-08-23 09:16:31
【问题描述】:

我正在研究 netstat 工具 (Linux) 的代码,AFAIK 主要读取 /proc/net/tcp 文件并漂亮地打印出来。 (我现在的重点是 -t 模式。)

作者选择的编码风格让我有点困惑:

static int tcp_info(void)
{
    INFO_GUTS6(_PATH_PROCNET_TCP, _PATH_PROCNET_TCP6, "AF INET (tcp)", tcp_do_one);
}

在哪里

#define INFO_GUTS6(file,file6,name,proc)                \
 char buffer[8192];                                     \
 int rc = 0;                                            \
 int lnr = 0;                                           \
 if (!flag_arg || flag_inet) {                          \
    INFO_GUTS1(file,name,proc)                          \
 }                                                      \
 if (!flag_arg || flag_inet6) {                         \
    INFO_GUTS2(file6,proc)                              \
 }                                                      \
 INFO_GUTS3

在哪里

 #define INFO_GUTS3                                      \
  return rc;

#if HAVE_AFINET6
#define INFO_GUTS2(file,proc)                           \
   lnr = 0;                                              \
   procinfo = fopen((file), "r");                        \
   if (procinfo != NULL) {                               \
     do {                                                \
       if (fgets(buffer, sizeof(buffer), procinfo))      \
          (proc)(lnr++, buffer);                          \
     } while (!feof(procinfo));                          \
     fclose(procinfo);                                   \
   }
#else
#define INFO_GUTS2(file,proc)
#endif

等等

显然,我的编码意识是倾斜的,并说“那些应该是函数”。我看不到这些宏在这里带来的任何好处。它会破坏可读性等。

周围有没有人熟悉这段代码,可以解释一下“INFO_GUTS”是什么意思,以及这种奇怪的编码风格是否可能(或仍然有)原因?

如果您对它们的使用感到好奇,完整的依赖关系图如下所示:

#               /--->   INFO_GUTS1  <---\    
#  INFO_GUTS --*        INFO_GUTS2  <----*---- INFO_GUTS6
#      î        \--->   INFO_GUTS3  <---/           î 
#      |                                            |
# unix_info()              igmp_info(), tcp_info(), udp_info(), raw_info()

【问题讨论】:

    标签: c networking coding-style macros


    【解决方案1】:

    您认为“那些宏应该是函数”对我来说似乎是正确的;我更愿意将它们视为函数。

    了解宏的使用频率会很有趣。但是,它们使用得越多,如果它们是真正的函数而不是宏,则应该节省更多空间。宏非常大,并且本身使用(本来就很慢的)I/O 函数,因此使用宏不会加快速度。

    现在,如果您想要内联替换函数,您可以在 C(以及 C++)中使用 inline 函数。


    您也可以争辩说 INFO_GUTS2 应该使用直接的 while 循环而不是 do ... while 循环;如果是,它只需要检查一次 EOF:

    while (fgets(buffer, sizeof(buffer), procinfo))
        (*proc)(lnr++, buffer);
    

    事实上,如果通道上出现错误(与 EOF 不同),代码可能会进入无限循环; fgets() 将失败,但 feof() 将返回 false(因为它尚未达到 EOF;它遇到了错误 - 请参阅 ferror()),因此循环将继续。不是一个特别合理的问题;如果文件打开,您很少会收到错误消息。但一个可能的问题。

    【讨论】:

    • do...while 是我并不感到惊讶的事情。要回答您的其他问题,请参阅更新问题中的依赖关系图。
    • 大多数现代编译器将内联视为提示而不是要求,因此将函数标记为内联并不能保证它实际上会被内联。
    • 在实际代码中,宏只使用了一次。 INFO_GUTS2() 宏是有条件定义的,但整个函数可以不写宏而没有任何明显的有害影响。
    【解决方案2】:

    没有理由。编写代码的人可能对一般的代码优化非常困惑,尤其是内联的概念。由于编译器很可能是GCC,所以有几种方法可以实现函数内联,如果这个函数甚至需要内联,我非常怀疑。

    内联一个包含文件 I/O 调用的函数就像给大象剃毛以减轻它的重量......

    【讨论】:

      【解决方案3】:

      实现可选的 IPv6 支持听起来有些糟糕。您必须浏览历史才能确认,但存档似乎只能回到 1.46,而隐含的伤害是 1.20+。

      我发现一个 git 存档可以追溯到 1.24,它仍然存在。旧代码看起来很可疑。

      BusyBox 或 BSD 代码都不包含这种乱七八糟的代码。所以它出现在 Linux 版本中并遭受了严重的比特腐烂。

      【讨论】:

      • 使用 #define 而不是复制粘贴进行快速而肮脏的修改。好的,我想我现在明白为什么事情会这样结束了。
      【解决方案4】:

      宏生成代码:调用时,整个宏定义在调用处展开。如果说 INFO_GUTS6 是一个函数,它就不能声明,例如,缓冲区变量,该变量随后可由宏调用之后的代码使用。您粘贴的示例实际上非常简洁:-)

      【讨论】:

        猜你喜欢
        • 2022-07-21
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-10-07
        • 1970-01-01
        相关资源
        最近更新 更多