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

发布于 2024-12-01 02:23:39 字数 2149 浏览 0 评论 0原文

我正在研究 netstat 工具(Linux)的代码,据我所知,该工具主要读取 /proc/net/tcp 文件并从中进行 dowa 漂亮的打印。 (我现在的重点是 -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()

I'm studying the code of the netstat tool (Linux), which AFAIK mostly reads a /proc/net/tcp file and dowa pretty-printing out of it. (My focus is on the -t mode right now.)

I'm a bit puzzled by the coding style the authors have chosen:

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

where

#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

where

 #define INFO_GUTS3                                      \
  return rc;

and

#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

etc.

Clearly, my coding sense is tilting and says "those should be functions". I don't see any benefit those macros bring here. It kills readability, etc.

Is anybody around familiar with this code, can shed some light on what "INFO_GUTS" is about here and whether there could have been (or still has) a reason for such an odd coding style?

In case you're curious about their use, the full dependency graph goes like this:

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

孤独难免 2024-12-08 02:23:39

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

了解宏的使用频率会很有趣。但是,如果它们是真正的函数而不是宏,则使用它们的次数越多,就越应该节省空间。这些宏相当大,并且本身使用(本质上很慢的)I/O 函数,因此使用宏不会提高速度。

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


您还可以认为 INFO_GUTS2 应该使用直接的 while 循环而不是 do ... while 循环;如果是以下情况,则只需要检查 EOF 一次:

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

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

Your sense that "those macros should be functions" seems correct to me; I'd prefer to see them as functions.

It would be interesting to know how often the macros are used. However, the more they're used, the more there should be a space saving if they're a real function instead of a macro. The macros are quite big and use (inherently slow) I/O functions themselves, so there isn't going to be a speed-up from using the macro.

And these days, if you want inline substitution of functions, you can use inline functions in C (as well as in C++).


You can also argue that INFO_GUTS2 should be using a straight-forward while loop instead of the do ... while loop; it would only need to check for EOF once if it was:

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

As it is, if there is an error (as opposed to EOF) on the channel, the code would probably go into an infinite loop; the fgets() would fail, but the feof() would return false (because it hasn't reached EOF; it has encountered an error - see ferror()), and so the loop would continue. Not a particularly plausible problem; if the file opens, you will seldom get an error. But a possible problem.

浅唱ヾ落雨殇 2024-12-08 02:23:39

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

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

There is no reason why. The person who wrote the code was likely very confused about code optimizations in general, and the concept of inlining in particular. Since the compiler is most likely GCC, there are several ways to achieve function inlining, if inlining was even necessary for this function, which I very much doubt.

Inlining a function containing file I/O calls would be the same thing as shaving an elephant to reduce its weight...

疑心病 2024-12-08 02:23:39

实施可选的 IPv6 支持听起来是个糟糕的主意。你必须浏览历史才能确认,但存档似乎只返回到 1.46,而隐含伤害为 1.20+。

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

BusyBox 或 BSD 代码都不包含如此混乱的代码。因此它出现在 Linux 版本中并遭受了严重的损坏。

It reads as someones terrible idea to implement optional IPv6 support. You would have to walk through the history to confirm, but the archive only seems to go back to 1.46 and the implied damage is at 1.20+.

I found a git archive going back to 1.24 and it is still there. Older code looks doubtful.

Neither BusyBox or BSD code includes such messy code. So it appeared in the Linux version and suffered major bit rot.

救赎№ 2024-12-08 02:23:39

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

Macros generate code: when it is called, the whole macro definition is expanded at the place of the call. If say, INFO_GUTS6 were a function, it wouldn't be able to declare, e.g., the buffer variable which would subsequently be usable by the code that follows the macro invocation. The example you pasted is actually very neat :-)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文