为什么这些C宏不能写成函数?

时间:2011-08-23 09:16:31

标签: c networking coding-style macros

我正在研究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()

4 个答案:

答案 0 :(得分:3)

你觉得“那些宏应该是功能”对我来说似乎是正确的;我更愿意将它们视为功能。

了解宏的使用频率会很有趣。但是,它们使用的越多,如果它们是真正的函数而不是宏,则应该节省空间。这些宏本身非常大并且使用(本身很慢)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()),因此循环将继续。这不是一个特别合理的问题;如果文件打开,你很少会收到错误。但是可能存在问题。

答案 1 :(得分:2)

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

内联包含文件I / O调用的函数与剃大象以减轻其重量是一回事......

答案 2 :(得分:1)

它认为实现可选的IPv6支持是一个很糟糕的想法。您将不得不浏览历史记录以确认,但存档似乎只回到1.46,隐含的损害是1.20 +。

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

BusyBox或BSD代码都不包含这样的混乱代码。所以它出现在Linux版本中并且遭受了重大损失。

答案 3 :(得分:0)

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