C选项好风格?

时间:2011-03-27 20:48:09

标签: c coding-style

我是C的新手,并且在使用按位操作时玩得很开心。我在做什么似乎有效,我只是想知道它是否被认为是好风格。

好的,我们假设我的程序有3个命令行选项,-a-b-c。以前我会有3个整体充当布尔值,比如aFlagbFlagcFlag。然后,当我调用processOpts( int *aFlag, int *bFlag, int *cFlag)函数时,我会将&aFlag&bFlag&cFlag作为参数传递,并将它们设置为*aFlag = 1

这是我的新方法:让1 int *options代表所有选项,并将其视为一组布尔值。所以要在函数中设置它们:

case 'a':
  *options |= 1;
  break;
case 'b':
  *options |= 2;
  break;
case 'c':
  *options |= 4;
  break;

然后,回到main(或任何地方),当我想测试以查看是否选择了一个选项时:

if ( *options & 1 )
  // Option 'a' selected

if ( *options & 2 )
  // Option 'b' selected

if ( *options & 4 )
  // Option 'c' selected

我的问题是:哪种方法被认为是更好的风格?第一种方式可能更清晰,更不容易出错,而第二种方式可能更容易重构(无需更改函数原型,因为它只是一个int)。

或者,有没有更好的方法呢? :D

编辑:根据Mat的建议添加休息时间。

感谢所有回复,我对这个社区印象深刻,并且愿意帮助每个人学习 - 你们摇滚!

8 个答案:

答案 0 :(得分:3)

使用单个变量来表示一组布尔标志,如果它们是相关的,则效果很好。一般来说,如果标志不相关,我会避免这样做。但是在你的情况下,3个标志与程序及其运行方式有关,我会说这是一个很好用的。

就实现而言,不应使用标记的硬编码常量值,而应定义宏或枚举来表示标志。很明显,您正在设置(或取消设置)哪个标志。虽然你可能没有必要在代码中使用指针。

如,

enum option
{
    OPTION_none = 0,
    OPTION_a = 0x1,
    OPTION_b = 0x2,
    OPTION_c = 0x4,
};

enum option processOpts(int argc, char **argv)
{
    enum option options = OPTION_none;
    if (argc > 1)
    {
        int i;
        for (i = 1; i < argc; i++)
        {
            if (argv[i][0] == '-')
            {
                switch (argv[i][1])
                {
                case 'a': options |= OPTION_a; break;
                case 'b': options |= OPTION_b; break;
                case 'c': options |= OPTION_c; break;
                }
            }
        }
    }
    return options;
}

int main(int argc, char *argv[])
{
    enum option options = processOpts(argc, argv);
    if (options & OPTION_a)
    {
        /* do stuff for option a */
    }
    if (options & OPTION_b)
    {
        /* do stuff for option b */
    }
    if (options & OPTION_c)
    {
        /* do stuff for option c */
    }
    return 0;
}

答案 1 :(得分:1)

更好的方法是使用 unsigned 整数类型来表示标志集合。否则,你的新方法在许多方面远远优于一堆单独的标志。

当然,对应于每个选项的位掩码应该由命名常量表示,而不是代码中间的“幻数”(1,2,4 ...)。

答案 2 :(得分:1)

这很好 - 你只是将位打包成int - 我要做的唯一建议是1,2,4等应该是符号常量,而不是上面的文字硬编码值,例如

enum {
    OPTION_A = 1 << 0,
    OPTION_B = 1 << 1,
    OPTION_C = 1 << 2,
};

答案 3 :(得分:1)

使用位标志是更好的风格 - 因为如果您需要更多命令行选项,它可以轻松扩展。

但是,我强烈建议你不要使用普通的魔术数字 - 而是做这样的事情:

#define OPTION_A 1
#define OPTION_B 2 ...

if (*options & OPTION_A) {
}

这使得检查选项或多或少地自我解释,而带有原始数字的按位操作总是闻起来有点像隐藏的魔法。

答案 4 :(得分:1)

您的新方法(使用无符号变量)通常(并且是惯用的)。

通常使用枚举(或#define)定义124等,并为其命名

enum {
    OPTION_LEFT_TO_RIGHT = 1,
    OPTION_TOP_TO_BOTTOM = 2,
    OPTION_BOTTOM_TO_TOP = 4,
    OPTION_RIGHT_TO_LEFT = 8,
};

case 'a': *options |= OPTION_BOTTOM_TO_TOP; break;

if (*options & OPTION_RIGHT_TO_LEFT) { /* ... */ }

答案 5 :(得分:1)

正如我在评论中所建议的那样,我的偏好是使用一些字段来做到这一点。对我来说,它有一个更直观的用法,同时允许编译器为我完成大部分工作(至少我的编译器检查汇编输出确认它在检查/设置标志时正在执行预期和/或操作)。因此,以Jeff M的解决方案为出发点,我将其改为:

// Assume TRUE defined as 1

struct option
{
    unsigned int OptionA : 1;  // defines 1 bit for option A flag
    unsigned int OptionB : 1;  // next bit is for option B
    unsigned int OptionC : 1;  // note, you can define more than one bit per flag
                               // if more complex options are required.
};

struct option processOpts(int argc, char **argv)
{
    struct option options = {0,0,0};  // explicit setting of all flags to false
                                      // compiler optimizes to options=0
    if (argc > 1)
    {
        int i;
        for (i = 1; i < argc; i++)
        {
            if (argv[i][0] == '-')
            {
                switch (argv[i][1])
                {
                case 'a': options.OptionA = TRUE; break;
                case 'b': options.OptionB = TRUE; break;
                case 'c': options.OptionC = TRUE; break;
                }
            }
        }
    }
    return options;
}

int main(int argc, char *argv[])
{
    struct option options = processOpts(argc, argv);
    if (options.OptionA)
    {
        /* do stuff for option a */
    }
    if (options.OptionB)
    {
        /* do stuff for option b */
    }
    if (options.OptionC)
    {
        /* do stuff for option c */
    }
    return 0;
}

答案 6 :(得分:0)

你扭动自己的方式是一种出色的方式。它甚至可能是“最好的”方式。恭喜!做得好。

- 皮特

答案 7 :(得分:0)

解析命令行选项的常用方法是使用getopt()。可以在5 source中找到其使用的一个很好的例子。

请注意,虽然getopt()符合POSIX.2和POSIX.1-2001,但getopt_long()是一个GNU扩展,应该避免IMO。