¿Qué pasa con este código C de 1988?

Estoy tratando de comstackr este fragmento de código del libro “The C Programming Language” (K & R). Es una versión escueta del progtwig wc UNIX:

 #include  #define IN 1; /* inside a word */ #define OUT 0; /* outside a word */ /* count lines, words and characters in input */ main() { int c, nl, nw, nc, state; state = OUT; nl = nw = nc = 0; while ((c = getchar()) != EOF) { ++nc; if (c == '\n') ++nl; if (c == ' ' || c == '\n' || c == '\t') state = OUT; else if (state == OUT) { state = IN; ++nw; } } printf("%d %d %d\n", nl, nw, nc); } 

Y recibo el siguiente error:

 $ gcc wc.c wc.c: In function 'main': wc.c:18: error: 'else' without a previous 'if' wc.c:18: error: expected ')' before ';' token 

La segunda edición de este libro es de 1988 y soy bastante nuevo para C. Tal vez tiene que ver con la versión del comstackdor o tal vez estoy hablando tonterías.

He visto en el moderno código C un uso diferente de la función main :

 int main() { /* code */ return 0; } 

¿Es este un nuevo estándar o puedo seguir usando un main sin tipo?

Su problema es con sus definiciones de preprocesador de IN y OUT :

 #define IN 1; /* inside a word */ #define OUT 0; /* outside a word */ 

Observe cómo tiene un punto y coma final en cada uno de estos. Cuando el preprocesador los expanda, su código se verá más o menos así:

  if (c == ' ' || c == '\n' || c == '\t') state = 0;; /* <--PROBLEM #1 */ else if (state == 0;) { /* <--PROBLEM #2 */ state = 1;; 

Ese segundo punto y coma hace que el else no tenga una coincidencia anterior, porque no está usando llaves. Por lo tanto, elimine los puntos y comas de las definiciones del preprocesador de IN y OUT .

La lección aprendida aquí es que las declaraciones de preprocesador no tienen que terminar con un punto y coma.

Además, siempre debes usar llaves.

  if (c == ' ' || c == '\n' || c == '\t') { state = OUT; } else if (state == OUT) { state = IN; ++nw; } 

No hay ambigüedad colgante en el código anterior.

El principal problema con este código es que no es el código de K & R. Incluye puntos y comas después de las definiciones de las macros, que no estaban presentes en el libro, que como otros señalaron, cambia el significado.

Excepto cuando realice un cambio en un bash de comprender el código, debe dejarlo en paz hasta que lo comprenda. Solo puedes modificar el código que entiendes de forma segura.

Probablemente fue solo un error de tu parte, pero ilustra la necesidad de comprensión y atención a los detalles cuando se progtwig.

No debería haber ningún punto y coma después de las macros,

 #define IN 1 /* inside a word */ #define OUT 0 /* outside a word */ 

y probablemente debería ser

 if (c == ' ' || c == '\n' || c == '\t') 

Las definiciones de IN y OUT deberían verse así:

 #define IN 1 /* inside a word */ #define OUT 0 /* outside a word */ 

¡El punto y coma estaba causando el problema! La explicación es simple: tanto IN como OUT son directivas de preprocesador, esencialmente el comstackdor reemplazará todas las ocurrencias de IN con un 1 y todas las ocurrencias de OUT con un 0 en el código fuente.

Como el código original tenía un punto y coma después de 1 y 0, cuando IN y OUT se reemplazaron en el código, el punto y coma adicional después del número produjo un código no válido, por ejemplo esta línea:

 else if (state == OUT) 

Terminó luciendo así:

 else if (state == 0;) 

Pero lo que querías era esto:

 else if (state == 0) 

Solución: elimine el punto y coma después de los números en la definición original.

No es exactamente un problema, pero la statement de main() también está fechada, debería ser algo así.

 int main(int argc, char** argv) { ... return 0; } 

El comstackdor asumirá un valor de retorno int para una función sin / o, y estoy seguro de que el comstackdor / enlazador funcionará en torno a la falta de statement para argc / argv y la falta de valor de retorno, pero deberían estar allí.

Como ve, había un problema en las macros.

GCC tiene la opción de detenerse después del preprocesamiento. (-E) Esta opción es útil para ver el resultado del preprocesamiento. De hecho, la técnica es importante si está trabajando con una gran base de código en c / c ++. Normalmente, los archivos make tendrán un objective para detenerse después del preprocesamiento.

Para una referencia rápida: La pregunta SO cubre las opciones: ¿Cómo veo un archivo fuente C / C ++ después del preprocesamiento en Visual Studio? . Comienza con vc ++, pero también tiene las opciones de gcc mencionadas a continuación .

Intente agregar llaves explícitas alrededor de los bloques de código. El estilo de K & R puede ser ambiguo.

Mira la línea 18. El comstackdor te dice dónde está el problema.

  if (c == '\n') { ++nl; } if (c == ' ' || c == '\n' || c == '\t') { // You're missing an "=" here; should be "==" state = OUT; } else if (state == OUT) { state = IN; ++nw; } 

Una forma simple es usar corchetes como {} para cada if y else :

 if (c == '\n'){ ++nl; } if (c == ' ' || c == '\n' || c == '\t') { state = OUT; } else if (state == OUT) { state = IN; ++nw; } 

Como señalaron otras respuestas, el problema está en #define y punto y coma. Para minimizar estos problemas, siempre prefiero definir las constantes numéricas como un const int :

 const int IN = 1; const int OUT = 0; 

De esta forma puedes deshacerte de muchos problemas y posibles problemas. Está limitado solo por dos cosas:

  1. Su comstackdor debe ser compatible con const , que en 1988 no era cierto en general, pero ahora es compatible con todos los comstackdores comúnmente utilizados. (AFAIK la const es “prestada” de C ++)

  2. No puede usar estas constantes en algunos lugares especiales donde necesitaría una constante tipo cadena. Pero creo que su progtwig no es ese caso.