Perdón si esta pregunta es ingenua. Considere el siguiente programa:
#include <stdio.h>
int main() {
int i = 1;
i = i + 2;
5;
i;
printf("i: %d\n", i);
}
En el ejemplo anterior, las declaraciones 5;
y i;
parecen totalmente superfluo, sin embargo, los compila el código sin advertencias o errores por defecto (sin embargo, GCC lanzar una warning: statement with no effect [-Wunused-value]
advertencia cuando corrió con -Wall
). No tienen ningún efecto en el resto del programa, entonces, ¿por qué se consideran declaraciones válidas en primer lugar? ¿El compilador simplemente los ignora? ¿Hay algún beneficio en permitir tales declaraciones?
;
después. Complicaría el lenguaje agregar más reglas sobre cuándo las expresiones no pueden ser declaracionesprintf()
? La declaración5;
básicamente dice "hacer lo5
que sea que haga (nada) e ignorar el resultado. Su declaraciónprintf(...)
es" hacer loprintf(...)
que sea que haga e ignorar los resultados (el valor de retorno deprintf()
) ". C los trata de la misma manera. Esto también permite código como(void) i;
dóndei
está un parámetro a una función que sevoid
utiliza para marcarlo como no utilizado deliberadamente.printf()
tienen un efecto, incluso si ignoras el valor que finalmente devuelve. Por el contrario5;
no tiene ningún efecto en absoluto.Respuestas:
Una ventaja de permitir tales declaraciones es el código creado por macros u otros programas, en lugar de ser escrito por humanos.
Como ejemplo, imagine una función
int do_stuff(void)
que se supone que devuelve 0 en caso de éxito o -1 en caso de error. Podría ser que el soporte para "cosas" es opcional, por lo que podría tener un archivo de encabezado queAhora imagine algún código que quiera hacer cosas si es posible, pero que realmente puede o no importarle si tiene éxito o no:
Cuando
STUFF_SUPPORTED
es 0, el preprocesador expandirá la llamadafunc2
a una declaración que solo leey entonces el pase del compilador verá el tipo de declaración "superflua" que parece molestarle. ¿Pero qué más se puede hacer? Si es así
#define do_stuff() // nothing
, entonces el códigofunc1
se romperá. (Y aún tendrá una declaración vacía en lafunc2
que solo se lee;
, lo que quizás sea aún más superfluo). Por otro lado, si tiene que definir realmente unado_stuff()
función que devuelve -1, puede incurrir en el costo de una llamada a función sin ninguna buena razónfuente
((void)0)
.assert
.Las declaraciones simples en C terminan en punto y coma.
Las declaraciones simples en C son expresiones. Una expresión es una combinación de variables, constantes y operadores. Cada expresión da como resultado algún valor de cierto tipo que puede asignarse a una variable.
Dicho esto, algunos "compiladores inteligentes" podrían descartar 5; y yo; declaraciones.
fuente
void
no tiene valor.Se permiten declaraciones sin efecto porque sería más difícil prohibirlas que permitirlas. Esto fue más relevante cuando C se diseñó por primera vez y los compiladores eran más pequeños y simples.
Una declaración de expresión consiste en una expresión seguida de un punto y coma. Su comportamiento es evaluar la expresión y descartar el resultado (si lo hay). Normalmente, el propósito es que la evaluación de la expresión tenga efectos secundarios, pero no siempre es fácil o incluso posible determinar si una expresión dada tiene efectos secundarios.
Por ejemplo, una llamada de función es una expresión, por lo que una llamada de función seguida de un punto y coma es una declaración. ¿Esta declaración tiene algún efecto secundario?
Es imposible saberlo sin ver la implementación de
some_function
.¿Qué tal esto?
Probablemente no, pero si
obj
se define comovolatile
, entonces lo hace.Permitir que cualquier expresión se convierta en una declaración de expresión agregando un punto y coma simplifica la definición del lenguaje. Requerir que la expresión tenga efectos secundarios agregaría complejidad a la definición del lenguaje y al compilador. C se basa en un conjunto coherente de reglas (las llamadas a funciones son expresiones, las asignaciones son expresiones, una expresión seguida de un punto y coma es una declaración) y permite a los programadores hacer lo que quieran sin evitar que hagan cosas que pueden o no tener sentido.
fuente
Las declaraciones que enumeró sin efecto son ejemplos de una declaración de expresión , cuya sintaxis se proporciona en la sección 6.8.3p1 del estándar C de la siguiente manera:
Toda la sección 6.5 está dedicada a la definición de una expresión, pero hablando en términos generales, una expresión consiste en constantes e identificadores vinculados con operadores. En particular, una expresión puede contener o no un operador de asignación y puede contener o no una llamada a la función.
Entonces, cualquier expresión seguida de un punto y coma califica como una declaración de expresión. De hecho, cada una de estas líneas de su código es un ejemplo de una declaración de expresión:
Algunos operadores contienen efectos secundarios como el conjunto de operadores de asignación y los operadores de incremento / decremento previo / posterior, y el operador de llamada de función
()
puede tener un efecto secundario dependiendo de lo que haga la función en cuestión. Sin embargo, no se exige que uno de los operadores deba tener un efecto secundario.Aquí hay otro ejemplo:
Esto es llamar a una función y descartar el resultado, al igual que la llamada
printf
en su ejemplo, pero a diferencia deprintf
la llamada a la función en sí no tiene un efecto secundario.fuente
A veces, tales declaraciones son muy útiles:
O cuando el manual de referencia nos dice que solo leamos los registros para archivar algo, por ejemplo, para borrar o establecer alguna bandera (situación muy común en el mundo de los EE. UU.)
https://godbolt.org/z/6wjh_5
fuente
*SREG
es volátil,*SREG;
no tiene ningún efecto en el modelo especificado por el estándar C. El estándar C especifica que tiene un efecto secundario observable.