Зачем вам использовать троичный оператор без присвоения значения для условия “true” (x = x ?:1)
-
25-09-2019 - |
Вопрос
В коде qemu с открытым исходным кодом Android я наткнулся на эту строку кода:
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 Условные выражения с Пропущенными операндами
Средний операнд в условном выражении может быть опущен.Тогда, если первый операнд отличен от нуля, его значением будет значение условного выражения.
Следовательно, выражение
x ? : y
имеет значение x, если оно ненулевое;в противном случае значение y.
Этот пример прекрасно эквивалент к
x ? x : y
В этом простом случае возможность опустить средний операнд не особенно полезна.Когда это становится полезным, это когда первый операнд выполняет, или может (если это аргумент макроса), содержать побочный эффект.Затем повторение операнда в середине привело бы к выполнению побочного эффекта дважды.При пропуске среднего операнда используется уже вычисленное значение без нежелательных последствий его повторного вычисления.
Как вы, вероятно, можете догадаться, избегать этого рекомендуется по соображениям удобочитаемости и переносимости.Я, честно говоря, удивлен, увидев такое несовместимое с грамматикой расширение для C.
Другие советы
Это Расширение GCC Это означает «если условие верно, используйте его, иначе использовать это другое значение», так
machine->max_cpus = machine->max_cpus ?: 1;
Снаряжена
machine->max_cpus = machine->max_cpus ? machine->max_cpus : 1;
Хотя, если условный имеет побочные эффекты, он будет только один раз
Использование гк -педантического флага, он говорит
foo.c: 5: Предупреждение: ISO C запрещает пропустить средний термин a?: выражение
Это Расширение GCC, и становится интереснее и полезно, когда состояние имеет побочные эффекты.
В этом случае да, я за человека согласился бы, что это неясно, что больше всего на свете.
BNF K & R показывает выражение между «?» а также ":". Я не думаю, что GCC должен быть составлен, без диагностики.
Для этого есть еще один полезный случай - устранение промежуточных переменных при вызове функции или способа, который может вернуть 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, но большинство современных языков запрещают его, осложняют любые усилия по переплетам.
Поскольку другие сказали, что это также нестандартное расширение, поэтому следует избегать, если переносимость вообще сообрабатывается.