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

在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/ 。

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

5.7条件与省略操作数

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

因此,expression

  x ? : y 

如果非零,则有x的值; 否则,y的值。

这个例子完全等同于

  x ? x : y 

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

正如你可能猜到的那样,为了可读性和便携性的原因,build议避免这种情况。 我真的很惊讶看到这样一个与C语言不兼容的扩展

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

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

是速记

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

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

它使用海湾合作委员会的标志,它确实说

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

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

在这种情况下,是的,我认为这比别的什么都要模糊。

K&R BNF显示在“?”之间需要expression式 和“:”。 我不认为gcc应该编译没有诊断。

还有另外一个有用的例子 – 在调用可能返回nil的函数或方法时消除中间variables,我们希望避免两次调用。 例如(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惯例,但大多数现代语言都不允许它,使任何移植工作变得复杂。

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