为什么不使用三元运算符而不为“true”条件赋值(x = x?:1)

时间:2010-05-10 20:32:43

标签: c gcc android-emulator conditional-operator qemu

在Android开源qemu代码中,我遇到了这行代码:

machine->max_cpus = machine->max_cpus ?: 1; /* Default to UP */

这只是一种令人困惑的说法:

if (machine->max_cpus) {
   ; //do nothing
} else {
 machine->max_cpus = 1;
}

如果是这样,那就不会更清楚了:

if (machine->max_cpus == 0) machine->max_cpus = 1;

有趣的是,这可以编译并在gcc中正常工作,但不能在http://www.comeaucomputing.com/tryitout/上编译。

7 个答案:

答案 0 :(得分:48)

这在GNU中是permitted,是C

的一个模糊扩展
  

5.7具有省略操作数的条件

     

条件中的中间操作数   表达式可以省略。然后,如果   第一个操作数非零,其值为   有条件的价值   表达

     

因此,表达式

 x ? : y
     如果非零,则

的值为x;   否则,y的值。

     

这个例子非常等效   到

 x ? x : y
     

在这个简单的情况下,能力   省略中间操作数不是   特别有用。当它变成   有用的是当第一个操作数时,   或者可能(如果它是一个宏参数),   含有副作用。然后重复   中间的操作数会   两次执行副作用。   省略中间操作数使用   已经计算过的值没有   重新计算它的不良后果。

正如您可能猜到的那样,出于可读性和可移植性的原因,建议不要这样做。老实说,我很惊讶地看到这种语法不兼容的扩展C.

答案 1 :(得分:10)

这是GCC extension,表示“如果条件为真,请使用它,否则使用其他值”,所以

machine->max_cpus = machine->max_cpus ?: 1;

的简写
machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;

虽然如果条件有副作用,它只会运行一次

答案 2 :(得分:6)

使用gcc的-pedantic标志,它确实说

  

foo.c:5:警告:ISO C禁止   省略?的中期:   表达

答案 3 :(得分:3)

这是GCC extension,当条件有副作用时,它会变得更有趣和有用。

在这种情况下,是的,我会认为它比其他任何东西都更加模糊。

答案 4 :(得分:1)

K& R BNF显示“?”之间需要表达式和“:”。我不认为gcc应该在没有诊断的情况下进行编译。

答案 5 :(得分:0)

还有另一个有用的例子 - 在调用可能返回nil的函数或方法时消除中间变量,我们希望避免调用两次。例如(Objective-C),假设我们想要将文件解压缩到数组中(如果存在),否则返回一个空数组。

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        backlog = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData] ?: backlog;
    }
    return backlog;
}

替代方案不那么简洁。

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSArray *backlog = @[];
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            backlog = tempArray;
        }
    }
    return backlog;
}

或者多次返回的丑陋等等。

- (NSArray*)hydrateBacklogFromFile:(NSString *path)
{
    NSData *backlogData = [NSData dataWithContentsOfFile:path];
    if (backlogData)
    {
        NSArray *tempArray = [NSKeyedUnarchiver unarchiveObjectWithData:backlogData];
        if (tempArray != nil)
        {
            return tempArray;
        }
    }
    return @[];
}

因此,我发现它具有相当可读的有用的语法糖。缺点是

  • 将指针隐式转换为bool。这是一个长期存在的C. 惯例,但大多数现代语言不允许它,复杂化 任何移植工作。

  • 正如其他人所说,它也是一个非标准的扩展,所以它应该 如果可移植性是一个考虑因素,则应避免使用。

答案 6 :(得分:0)

我认为其他答案没有回答标题中的问题,并且也考虑了标签c。因此,我添加了另一个答案。

我使用这种语法来防止我的代码通过if语句变得丑陋。

foo(1) == TRUE ?: error_quit("foo(1) failed");
foo(2) == TRUE ?: error_quit("foo(2) failed");
foo(3) == TRUE ?: error_quit("foo(3) failed");
foo(4) == TRUE ?: error_quit("foo(4) failed");

您可以在行的开头看到实际的函数调用。将其与以下版本进行了比较,其中前导if阻碍了函数调用的直接视图。

if (foo(1) == FALSE) error_quit("foo(1)" failed");
if (foo(2) == FALSE) error_quit("foo(2)" failed");
if (foo(3) == FALSE) error_quit("foo(3)" failed");    
if (foo(4) == FALSE) error_quit("foo(4)" failed");

甚至更难阅读:

if (foo(1) == FALSE){
  error_quit("foo(1)" failed");
}

if (foo(2) == FALSE){
  error_quit("foo(2)" failed");
}

if (foo(3) == FALSE){
  error_quit("foo(3)" failed");
}

if (foo(4) == FALSE){
  error_quit("foo(4)" failed");
}