42

在 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/上编译。

4

7 回答 7

53

这在 GNU 中被允许作为对 C 的模糊扩展

5.7 省略操作数的条件

条件表达式中的中间操作数可以省略。那么如果第一个操作数非零,它的值就是条件表达式的值。

因此,表达式

 x ? : y

如果 x 不为零,则具有 x 的值;否则,y 的值。

这个例子完全等同于

 x ? x : y

在这种简单的情况下,省略中间操作数的能力并不是特别有用。当它变得有用时,第一个操作数确实或可能(如果它是宏参数)包含副作用。然后在中间重复操作数将执行两次副作用。省略中间操作数会使用已经计算的值,而不会产生重新计算的不良影响。

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

于 2010-05-10T20:41:07.557 回答
10

这是一个GCC 扩展,意思是“如果条件为真,使用它,否则使用这个其他值”,所以

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

是简写

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

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

于 2010-05-10T20:41:12.863 回答
6

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

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

于 2010-05-10T20:44:20.353 回答
3

这是一个GCC 扩展,当条件有副作用时它会变得更加有趣和有用。

在这种情况下,是的,我会同意它比其他任何东西都晦涩难懂。

于 2010-05-10T20:41:43.483 回答
1

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

于 2010-05-10T20:37:48.213 回答
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 @[];
}

所以它是有用的语法糖,我觉得它相当易读。缺点是

  • 将指针隐式转换为布尔值。这是一个长期存在的 C 约定,但大多数现代语言不允许它,使任何移植工作变得复杂。

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

于 2016-04-22T17:00:31.370 回答
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");
}
于 2020-03-21T14:50:21.530 回答