我正在研究coreutils源代码以便在编程方面做得更好,我在base64.c和其他代码中找到了这些代码:
while ((opt = getopt_long (argc, argv, "diw:", long_options, NULL)) != -1)
switch (opt)
{
// ... other stuff
case_GETOPT_HELP_CHAR; // <- this especially
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
// .. other stuff again
我不知道这意味着什么,直到我在system.h中找到了这个:
#define case_GETOPT_HELP_CHAR \
case GETOPT_HELP_CHAR: \
usage (EXIT_SUCCESS); \
break;
我不知道你实际上可以制作包含如此多语句的宏! 在Macros中使用如此多的语句是不是很危险,或者这是一种我应该学习的好编码风格吗?
编辑:我还注意到coreutils中实际上有很多MACROS。 它让我感到困惑,因为我来自C ++背景。
#define STREQ(a, b) (strcmp (a, b) == 0)
例如上面这个,是否真的有必要?它使得阅读代码更加困难,只需在inf语句中执行STREQ就可以节省很多时间
EDIT2:另一方面,我非常喜欢这个,感谢Jay:
#define COMMAND(NAME) { #NAME, NAME ## _command }
struct command commands[] =
{
COMMAND (quit),
COMMAND (help),
...
};
答案 0 :(得分:1)
使用这样的宏没有风险,只要它们被正确编写(如果它们不正确,可能会导致编译错误或意外行为)。但是,除非你需要它们,否则要避免它们 - 它们往往会使代码难以阅读。
例如,宏#define foo() bar(); bar
会很危险,因为if(...) foo();
最终会调用bar()
(在这种情况下,您会将宏代码包装在do{ ... }while(0)
中})
答案 1 :(得分:1)
这取决于。如果宏大大减少了代码大小,或者只是在一个地方修改内容变得相当容易,那就去吧,即使它有时看起来对代码诗人的眼睛看起来很难看。
在特定情况下,我很怀疑。首先,宏以分号结束,宏的使用也是如此,因此扩展以分号结束,其中一个是空语句。一些棉绒警告这些。在多语句宏之后强制分号的规范方法是
#define FOO(x) do { statement(x); stmnt; } while (0)
但是在这种情况下由于case
而失败了。 (意外的双关语,呵呵)。
其次,我不确定这个宏是否实际上在某个地方被重用了。如果没有,那么我认为它比典型的hackery更多代码混淆。另一方面,看起来system.h
也包含在其他实用程序中,并且在多个coreutils实用程序之间保持一致性是有意义的,例如总是使用相同的选项char“help”,“verbose”等。
答案 2 :(得分:0)
答案 3 :(得分:0)
快速阅读答案会告诉你,这是一个偏好问题。有些人喜欢,有些则不喜欢 我没有。
我的主要问题是使用它的代码不是真正的C代码。因此,当你阅读它并且你知道C时,你仍然无法理解它。
如果使用不当,这些定义可能导致奇怪且难以调试的问题。你看一下代码,看起来是对的,但事实并非如此。
case_GETOPT_HELP_CHAR
宏似乎不太容易出现此类错误。具有参数的case_GETOPT_VERSION_CHAR
可能更危险。
STREQ
是一个更好的宏。它使事情比strcmp(a,b)==0
更清晰(我总觉得这个令人困惑)
根据经验,我更喜欢可能是函数的宏。如果您愿意,可以将STREQ
实现为函数,但不能case_GETOPT_HELP_CHAR
。
COMMAND
是另一回事。有一些丑陋的东西,但强有力的理由 - 它消除了重复。如果没有宏观技巧,你必须重复两次命令名称
另一方面,考虑看到quit_command
函数的穷人,并试图找到它的调用位置。他可以在各个来源中搜索这个名字,他也找不到它。