¿Qué tiene de malo este código C de 1988?

9 minutos de lectura

avatar de usuario
césar

Estoy tratando de compilar este fragmento de código del libro “El lenguaje de programación C” (K & R). Es una versión básica del programa UNIX. wc:

#include <stdio.h>

#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 me sale 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 en C. Tal vez tenga que ver con la versión del compilador o tal vez solo esté diciendo tonterías.

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

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

¿Es este un nuevo estándar o todavía puedo usar un principal sin tipo?

  • No es una respuesta, sino otra pieza de código para mirar más de cerca, || c = '\t'). ¿Eso parece lo mismo que el otro código en esa línea?

    – usuario7116

    27 de diciembre de 2011 a las 3:19

  • ¿32 votos a favor para una pregunta de depuración + error tipográfico?

    – Carreras de ligereza en órbita

    27 de diciembre de 2011 a las 15:48

  • @TomalakGeret’kal: ya sabes, las cosas viejas se valoran más (vino, pinturas, código C)

    – Sergio Tulentsev

    27 de diciembre de 2011 a las 17:29


  • @César: Estoy en todo mi derecho de expresar mi opinión y te agradecería que no intentes censurarla. Da la casualidad de que sí, este no es un sitio web para depurar su código y resolver sus errores tipográficos, que son problemas “localizados” que nunca ayudarán a nadie más. es un sitio web para preguntas sobre lenguajes de programacion, no por hacer el trabajo básico de depuración y referencia por usted. El nivel de habilidad es completamente irrelevante. Lea las preguntas frecuentes, y tal vez también esta meta pregunta.

    – Carreras de ligereza en órbita

    28 de diciembre de 2011 a las 15:18


  • @TomalakGeret’kal, por supuesto, puedes expresar tu opinión y no censuraré tu comentario a pesar de ser poco constructivo. Ya he leído las preguntas frecuentes. soy un programador entusiasta preguntando por un problema real que estoy enfrentando

    – César

    28 de diciembre de 2011 a las 15:26

avatar de usuario
usuario7116

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 tener antecedentes if como un partido, porque no está usando aparatos ortopédicos. 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 del preprocesador no tienen que terminar con un punto y coma.

Además, ¡siempre debes usar brackets!

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

No hay ahorcamiento-else ambigüedad en el código anterior.

  • Para mayor claridad, el problema no es el espaciado, son los puntos y comas. No los necesita en declaraciones de preprocesador.

    – Dan

    27 de diciembre de 2011 a las 3:21

  • @Dan gracias por la aclaración! ¡Y los puntos y comas eran de hecho el problema! ¡Gracias chicos!

    – César

    27 de diciembre de 2011 a las 3:23

  • @César: de nada. Con suerte, la sugerencia de refuerzo lo mantendrá fuera de problemas en el futuro, ¡ciertamente me ha ayudado!

    – usuario7116

    27 de diciembre de 2011 a las 3:33

  • @César: también es una buena idea acostumbrarse a poner paréntesis alrededor de las macros, ya que generalmente desea que la macro se evalúe primero. En este caso, no importa, ya que el valor es un token único, pero omitir los paréntesis puede generar resultados inesperados al definir una expresión.

    – estilo

    27 de diciembre de 2011 a las 8:25

  • “no los necesito” != “no debería tenerlos”. lo primero es siempre cierto; este último depende del contexto y es el tema más pertinente en este escenario.

    – Carreras de ligereza en órbita

    27 de diciembre de 2011 a las 15:46


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

Excepto cuando realice un cambio en un intento de comprender el código, debe dejarlo solo hasta que lo comprenda. Solo puede modificar de forma segura el código que comprende.

Esto probablemente fue solo un error tipográfico de su parte, pero ilustra la necesidad de comprender y prestar atención a los detalles al programar.

  • Tu consejo no es muy constructivo para alguien que está aprendiendo a programar. Modificar el código es precisamente cómo entiendes los detalles de la programación.

    – usuario7116

    27 de diciembre de 2011 a las 21:49

  • @sixlettervariables: Y al hacerlo, debe saber qué cambios ha realizado y hacer la menor cantidad de cambios posible. Si el OP hubiera hecho los cambios deliberadamente y hubiera hecho la menor cantidad de cambios posible, probablemente no habría hecho esta pregunta, ya que le habría quedado claro lo que estaba pasando. Habría cambiado la macro por IN, sin errores y luego la macro por OUT con los dos errores, el segundo de los cuales se quejaría del punto y coma que acababa de agregar.

    – jmoreno

    27 de diciembre de 2011 a las 22:07

  • Parece que, a menos que cometa el error de incluir un punto y coma al final de una línea de directiva de preprocesador, es probable que no sepa que no debe incluirlos. Podría tomarlo al pie de la letra, podría leer muchos códigos y notar que nunca parecen estar allí. O bien, el OP podría equivocarse al incluirlos, preguntar sobre el error “extraño” y descubrir: ¡vaya, no se requieren puntos y comas para las directivas del preprocesador! Esto es programación, no un episodio de Scared Straight.

    – usuario7116

    27 de diciembre de 2011 a las 22:17


  • @sixlettervariables: Sí, pero cuando el código no funciona, el primer paso obvio es decir “oh, está bien, entonces lo que cambié sin ningún motivo del código escrito en un libro por el inventor de C, probablemente fue el problema. Voy a deshacer eso entonces”.

    – Carreras de ligereza en órbita

    28 de diciembre de 2011 a las 0:42

  • continuemos esta discusión en el chat

    – usuario7116

    28 de diciembre de 2011 a las 1:54

avatar de usuario
onemach

No debe 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')

  • Gracias, los puntos y comas eran el problema. ¡El segundo fue un error tipográfico!

    – César

    27 de diciembre de 2011 a las 3:24

  • La próxima vez pega el exacto código que utiliza, directamente desde su editor de texto.

    – Carreras de ligereza en órbita

    27 de diciembre de 2011 a las 15:46

  • @TomalakGeret’kal bueno, no lo hice y lo haré, pero ¿cómo lo encontraste?

    – onemach

    28 de diciembre de 2011 a las 0:34

  • @onemach: Dijiste que el ; fue un error tipográfico que no afectó el problema, lo que significa un error tipográfico en su pregunta en lugar de en el código que usted Realmente usado.

    – Carreras de ligereza en órbita

    28 de diciembre de 2011 a las 0:41

avatar de usuario
Óscar López

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

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

¡Los puntos y coma estaban causando el problema! La explicación es simple: tanto IN como OUT son directivas de preprocesador, esencialmente el compilador reemplazará todas las apariciones de IN con un 1 y todas las apariciones de OUT con un 0 en el código fuente.

Dado que el código original tenía un punto y coma después del 1 y el 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.

avatar de usuario
Jayán

Como ves había un problema en las macros.

GCC tiene opción para 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++. Por lo general, los archivos MAKE tienen como objetivo detenerse después del preprocesamiento.

Para 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 que se mencionan a continuación.

avatar de usuario
Cuenta

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

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

El compilador asumirá un valor de retorno int para una función sin uno, y estoy seguro de que el compilador/enlazador solucionará la falta de declaración para argc/argv y la falta de valor de retorno, pero deberían estar ahí.

avatar de usuario
Pedro Mortensen

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

Mire la línea 18. El compilador le 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;
    }

  • ¡Gracias! En realidad, el código funcionó sin llaves en el segundo if 🙂

    – César

    27 de diciembre de 2011 a las 3:26

  • +1. No solo ambiguo sino algo peligroso. Cuando (si) agrega una línea a su if bloquear más adelante, si olvida agregar las llaves porque su bloque ahora tiene más de una línea, puede llevar un tiempo depurar ese error…

    – El111

    27 de diciembre de 2011 a las 3:26


  • @ The111 Nunca, nunca, me pasó a mí. Todavía no creo que esto sea un problema real. He estado usando el estilo sin llaves durante más de una década, nunca me olvidé de agregar las llaves al expandir el cuerpo de un bloque.

    – Konrad Rodolfo

    27 de diciembre de 2011 a las 9:59


  • @The111: En este caso, algunos colaboradores de SO tardaron unos minutos 😛 Y si eres un programador capaz de agregar declaraciones a un if cláusula y “olvidar” actualizar las llaves entonces, bueno, no eres un muy buen programador.

    – Carreras de ligereza en órbita

    27 de diciembre de 2011 a las 15:47


¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad