2010-05-10 11 views
37

En el código qemu de código abierto Android me encontré con esta línea de código:¿Por qué utilizar el operador ternario sin asignar un valor para la condición "verdadero" (x = x: 1)

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

se trata sólo de una manera confusa de decir:

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

Si es así, ¿no sería más clara como:

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

Curiosamente, este compila y funciona bien con gcc, pero no se compila en http://www.comeaucomputing.com/tryitout/.

+5

¡Espera, qué ?! ... eso parece un error para mí. –

+0

@Konrad - probablemente no. El comentario sugiere que la línea establece un valor predeterminado: si "max_cpus no está configurado, configure el valor predeterminado". – Mac

+0

Si es legal, lo evitaría porque se parece demasiado a un error. Aquí tuvimos una gran discusión al respecto, demostrando que habría sido más claro usar la declaración if. –

Respuesta

48

Este es permitted en GNU como una extensión oscuro para C

5,7 Conditionals con omite Operandos

El operando medio en un expresión condicional puede ser omitido. Entonces, si el primer operando es distinto de cero, su valor es el valor de la expresión condicional .

Por lo tanto, la expresión

x ? : y 

tiene el valor de x si es distinto de cero; de lo contrario, el valor de y.

este ejemplo es perfectamente equivalente a

x ? x : y 

En este caso sencillo, la capacidad de omitir el operando media no es especialmente útil. Cuando se convierte en útil es cuando el primer operando lo hace, o mayo (si es un argumento de macro), contiene un efecto secundario. Luego, repitiendo el operando en el medio sería realizar el efecto secundario dos veces. Omitir el operando medio utiliza el valor ya calculado sin los efectos no deseados de volver a calcularlo.

Como probablemente pueda adivinar, se recomienda evitar esto por motivos de legibilidad y portabilidad. Estoy sinceramente sorprendido de ver una extensión incompatible con la gramática de C.

+4

Interesante. Esto parece increíblemente útil como una característica sintáctica, e increíblemente oscuro de entender. –

+0

Estoy de acuerdo. Pero hay muchas cosas que uno podría agregar a C para hacerlo más elegante, estoy sorprendido de que un proveedor de compiladores explícitamente haga un cambio tan permisivo a la gramática C. Si esto era C++, esa es otra historia, ya que solía ser una jungla de incompatibilidad. – Uri

+13

Oye, así es como '||' se supone que funciona. – Potatoswatter

1

La K & R BNF muestra que se requiere una expresión entre "?" y ":". No creo que gcc deba compilar eso sin un diagnóstico.

+2

Creo que es una extensión de gcc no estándar. Mejor evitado –

10

Esta es una GCC extension que significa "si la condición es verdadera, lo utilizan, bien utilizar este otro valor", por lo

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

es la abreviatura de

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

aunque si tiene el condicional efectos secundarios, solo se ejecutará una vez

3

Es un GCC extension, y se vuelve más interesante y útil cuando la condición tiene efectos secundarios.

En este caso, sí, estoy de acuerdo que es oscuro más que cualquier otra cosa.

6

Usando la bandera -pedantic de gcc, sí dice

foo.c: 5: advertencia: ISO C prohíbe omitiendo el término medio de un:? expresión

0

Hay otra utilidad caso para esto - la eliminación de variables intermedias cuando se llama a una función o método que puede devolver cero, que deseamos evitar llamar dos veces. Por ejemplo (Objective-C), supongamos que deseamos descomprimir un archivo en una matriz si existe, de lo contrario, devolveremos una matriz vacía.

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

Las alternativas son menos concisas.

- (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; 
} 

O feas con múltiples retornos etc.

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

Así que es azúcar sintáctico útil que me resulta bastante fácil de leer. Las desventajas son

  • Conversión implícita de un puntero a bool. Esta es una convención de larga data C , pero la mayoría de los idiomas modernos no lo permiten, lo que complica cualquier esfuerzo de portabilidad.

  • Como otros han dicho que también es una extensión no estándar, por lo que debe evitarse si la portabilidad es una consideración en absoluto.

Cuestiones relacionadas