为什么要使用三元运算符而不为“真”条件赋值(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惯例,但大多数现代语言都不允许它,使任何移植工作变得复杂。
-
正如其他人所说,这也是一个非标准的扩展,所以如果可移植性是一个考虑,应该避免。