Blog

01 – Introducción General

Una introducción rapida a C

1.1 Comencemos

Ejercicio 1-1

Ejecute el programa «hola, mundo» en su sistema. Experimenta con la omisión de partes del programa, para ver qué mensajes de error se obtienen.

#include 
/* Ejecute el programa "hola, mundo" en su sistema. 
Experimenta con la omisión de partes del programa, 
para ver qué mensajes de error se obtienen.*/

 int main()
 {
 	printf("hola, ");
 	printf("mundo");
 	printf("\n");
 }

Ejercicio 1-2

Experimente el descubrir qué pasa cuando la cadena del argumento de printf contiene \c, en donde c es algún carácter no puesto en la lista anterior (\t, \n, \b, \», \\)

#include <stdio.h>

/* Experimente el descubrir qué pasa cuando la cadena del 
argumento de printf contiene \c, en donde c es algún 
carácter no puesto en la lista anterior (\t, \n, \b, \", \\).*/

int main()
{
printf("hola, ");
printf("mundo \l");
printf("\n");
}
ejer01_02.c: In function 'main':
ejer01_02.c:9:10: warning: unknown escape sequence: '\l'
printf("mundo \l");
^~~~~~~~~~

1.2 Variables y expresiones aritméticas

Ejercicio 1-3

Modifique el programa de conversion de temperaturas de modo que escriba un encabezado sobre la tabla.

#include 
/* Imprime la tabla Fahrenheit-Celsius
   para fahr = 0, 20, ..., 300 
   versión de punto flotante*/

 main()
 {
 	float fahr, celsius;
 	float lower, upper, step;

 	printf("Fahrenheit\tCelsius\n");
 	printf("=======================\n");
 	lower = 0;		/* límite inferior de la tabla de temperatura */
 	upper = 300; 	/* límite superior de la tabla de temperatura */
 	step = 20;		/* tamaño del incremento */

 	fahr = lower;
 	while (fahr <= upper) {
 		celsius = (5.0/9.0) * (fahr - 32);
 		printf("%3.0f\t\t %6.1f\n", fahr, celsius);
 		fahr = fahr + step;
 	}
 }
Fahrenheit      Celsius
=======================
  0               -17.8
 20                -6.7
 40                 4.4
 60                15.6
 80                26.7
100                37.8
120                48.9
140                60.0
160                71.1
180                82.2
200                93.3
220               104.4
240               115.6
260               126.7
280               137.8
300               148.9

Ejercicio 1-4

Escriba un programa que imprima la tabla correspondiente Celsius a Fahrenheit.

#include 
/* Imprime la tabla Celsius_Fahrenheit
   para celsius = 0, 20, ..., 300 
   versión de punto flotante*/

 main()
 {
 	float fahr, celsius;
 	float lower, upper, step;

 	printf("Celsius\tFahrenheit\n");
 	printf("==================\n");
 	lower = 0;		/* límite inferior de la tabla de temperatura */
 	upper = 300; 	/* límite superior de la tabla de temperatura */
 	step = 20;		/* tamaño del incremento */

 	celsius = lower;
 	while (celsius <= upper) {
 		fahr = (9.0/5.0) * celsius - 32;
 		printf("    %3.0f\t    %6.1f\n", celsius, fahr);
 		celsius = celsius + step;
 	}
 }
Celsius Fahrenheit
==================
      0      -32.0
     20        4.0
     40       40.0
     60       76.0
     80      112.0
    100      148.0
    120      184.0
    140      220.0
    160      256.0
    180      292.0
    200      328.0
    220      364.0
    240      400.0
    260      436.0
    280      472.0
    300      508.0

1.3 La proposición for

Ejercicio 1-5

Modifique el programa de conversión de temperaturas de manera que escriba la tabla en orden inverso, esto es, desde 300 grados hasta 0.

#include 

int main()
{
	float fahr;

	printf(" Fahrenheit\tCelsius\n");
 	printf("=======================\n");

	for(fahr = 300; fahr > 0; fahr = fahr - 20)
		printf("\t%3.0f\t %6.1f\n", fahr, (5.0/9.0)*(fahr-32));
}
 Fahrenheit     Celsius
=======================
        300       148.9
        280       137.8
        260       126.7
        240       115.6
        220       104.4
        200        93.3
        180        82.2
        160        71.1
        140        60.0
        120        48.9
        100        37.8
         80        26.7
         60        15.6
         40         4.4
         20        -6.7

1.4 Constantes simbólicas

1.5 Entrada y salida de caracteres

Ejercicio 1-6

Verifique que la expresión getchar() != EOF es 0 o 1

#include 
/* copia la entrada a la salida*/

int main(void) {
	int c;
	int valor;

	printf("Comienzo programa\n");
	valor = (c=getchar())!=EOF;
	printf("El valor es %d\n", valor);
	/*while((c = getchar()) != EOF){
		putchar(c);
	}*/
	return 0;
}
Comienzo programa
^D
El valor es 1

Ejercicio 1-7

Escriba un programa que imprima el valor de EOF.

#include 
/* copia la entrada a la salida */

int main(void) {
	printf("El valor de fin de fichero es %d",EOF);

	return 0;
}
El valor de fin de fichero es -1

Ejercicio 1-8

Escriba un programa que cuente espacios en blanco, tabuladores y nuevas líneas.

#include 
/* cuenta las líneas de la entrada, los espacios en blanco
   y los tabuladores
*/

int main(void) {
	int c, nl, eb, nt;

	nl = 0;	# nuevas líneas
	eb = 0; # espacios en blanco
	nt = 0; # tabuladores

	while((c = getchar()) != EOF){
		if(c == '\n')
			++nl;
		if(c == ' ')
			++eb;
		if(c == '\t')
			++nt;
	}
	printf("Líneas: %d\nEspacios: %d\nTabuladores: %d\n", nl, eb, nt);
	
	return 0;
}

Ejercicio 1-9

Escriba un programa que copie su entrada a la salida, reemplazando cada cadena de uno o más blancos por un solo blanco.

#include 
/* Escriba un programa que copie su entrada a la 
salida, reemplazando cada cadena de uno o más 
blancos por un solo blanco. */

int main(void) {
	int c, eb;

	eb = 0;
	while((c = getchar()) != EOF){
		if(c==' '){
			++eb;
		} else {
			if(eb>0){
				eb=0;
				putchar(' ');
			}
			putchar(c);
		}
	}
	return 0;
}

Ejercicio 1-10

Escriba un programa que copie su entrada a la salida, reemplazando cada tabulación por \t, cada retroceso por \b y cada diagonal invertida por \\. Esto hace que las tabulaciones y los espacios sean visibles sin confusiones

#include 
/* Escriba un programa que copie su entrada a la salida, 
reemplazando cada tabulación por \t, cada retroceso por 
\b y cada diagonal invertida por \\. Esto hace que las 
tabulaciones y los espacios sean visibles sin confusiones */

int main(void) {
	int c;

	while((c = getchar()) != EOF){
		if(c=='\t'){
			putchar('\\');
			putchar('t');
		}
		else if(c=='\b'){
			putchar('\\');
			putchar('b');
		}
		else if(c=='\\'){
			putchar('\\');
			putchar('\\');
		}
		else
			putchar(c);

	}
	return 0;
}

Ejercicio 1-11

¿Cómo probaría el programa para contar palabras? ¿Qué clase de entrada es la más conveniente para descubrir errores si éstos existen?

#include 

#define IN 1    /* en una palabra */
#define OUT 0   /* fuera de una palabra */

/* cuenta líneas, palabras y caracteres de la entrada */
int 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("Lineas %d, palabras %d, caracteres %d", nl, nw, nc);

    return 0;
}
>type lineas.c | ejer01_11
El sistema no puede encontrar el archivo especificado.
Lineas 0, palabras 0, caracteres 0
>type lineas01.c | ejer01_11
Lineas 13, palabras 33, caracteres 189

Ejercicio 1-12

Escriba un programa que imprima su entrada una palabra por linea.

#include 

#define IN 1
#define OUT 0

int main()
{
    int c, estado;

    estado = OUT;

    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n' || c == '\t') {
            if (estado == IN) {
                putchar('\n');
                estado = OUT;
            }
        } else {
            putchar(c);
            estado = IN;
        }
    }

    return 0;
}

1.6 Arreglos

Ejercicio 1-13

Escriba un programa que imprima el histograma de las longitudes de las palabras de su entrada de forma horizontal.

#include 
#define IN 1
#define OUT 0
#define LARGO_MAXIMO 10 /* máximo tamaño de una palabra */

int main()
{
    int c, i, j, estado, largo;
    int longitudPalabras[LARGO_MAXIMO + 1]; /* array que guarda la frecuencia de cada longitud de palabra */

    for (i = 0; i <= LARGO_MAXIMO; ++i)
        longitudPalabras[i] = 0;

    estado = OUT;
    largo = 0;

    /* contar la frecuencia de cada longitud de palabra */
    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n' || c == '\t') {
            if (estado == IN) {
                if (largo <= LARGO_MAXIMO)
                    ++longitudPalabras[largo];
                else
                    ++longitudPalabras[LARGO_MAXIMO];
                largo = 0;
            }
            estado = OUT;
        } else {
            estado = IN;
            ++largo;
        }
    }

    /* imprimir el histograma */
    for (i = 1; i <= LARGO_MAXIMO; ++i) {
        printf("%2d: ", i);
        for (j = 0; j < longitudPalabras[i]; ++j)
            putchar('*');
        putchar('\n');
    }

    /* imprimir la frecuencia de palabras con longitud mayor que LARGO_MAXIMO */
    if (longitudPalabras[LARGO_MAXIMO] > 0)
        printf("%2d+: %d\n", LARGO_MAXIMO, longitudPalabras[LARGO_MAXIMO]);

    return 0;
}

Ejercicio 1-13b

Escriba un programa que imprima el histograma de las longitudes de las palabras de su entrada. Hacerlo con orientación vertical es un reto más interesante

#include 
#define IN 1
#define OUT 0
#define LARGO_MAXIMO 10 /* máximo tamaño de una palabra */

int main()
{
    int c, i, j, estado, largo;
    int longitudPalabras[LARGO_MAXIMO + 1]; /* array que guarda la frecuencia de cada longitud de palabra */

    for (i = 0; i <= LARGO_MAXIMO; ++i)
        longitudPalabras[i] = 0;

    estado = OUT;
    largo = 0;

    /* contar la frecuencia de cada longitud de palabra */
    while ((c = getchar()) != EOF) {
        if (c == ' ' || c == '\n' || c == '\t') {
            if (estado == IN) {
                if (largo <= LARGO_MAXIMO)
                    ++longitudPalabras[largo];
                else
                    ++longitudPalabras[LARGO_MAXIMO];
                largo = 0;
            }
            estado = OUT;
        } else {
            estado = IN;
            ++largo;
        }
    }

    /* encontrar el máximo número de asteriscos en una barra */
    int maxCount = 0;
    for (i = 1; i <= LARGO_MAXIMO; ++i) {
        if (longitudPalabras[i] > maxCount)
            maxCount = longitudPalabras[i];
    }
    if (longitudPalabras[LARGO_MAXIMO] > maxCount)
        maxCount = longitudPalabras[LARGO_MAXIMO];

    /* imprimir el histograma en vertical */
    for (i = maxCount; i > 0; --i) {
        printf("%2d |", i);
        for (j = 1; j <= LARGO_MAXIMO; ++j) {
            if (longitudPalabras[j] >= i)
                printf(" * ");
            else
                printf("   ");
        }
        if (longitudPalabras[LARGO_MAXIMO] >= i)
            printf(" *");
        putchar('\n');
    }

    /* imprimir la línea de separación */
    printf("   +");
    for (i = 1; i <= LARGO_MAXIMO; ++i)
        printf("---");
    printf("---");
    putchar('\n');

    /* imprimir la etiqueta de cada barra */
    printf("    ");
    for (i = 1; i <= LARGO_MAXIMO; ++i)
        printf("%2d ", i);
    printf("%2d+", LARGO_MAXIMO);
    putchar('\n');

    return 0;
}

1.7 Funciones

Ejercicio 1-15

Escriba de nuevo el programa de conversión de temperatura de la sección 1.2, de modo que utilice una función para la conversión.

#include 
/* Imprime la tabla Fahrenheit-Celsius
   para fahr = 0, 20, ..., 300 
   versión de punto flotante*/

int fahrToCel(int f);

int main()
{
 	float fahr, celsius;
 	float lower, upper, step;

 	printf("Fahrenheit\tCelsius\n");
 	printf("=======================\n");
 	lower = 0;		/* límite inferior de la tabla de temperatura */
 	upper = 300; 	/* límite superior de la tabla de temperatura */
 	step = 20;		/* tamaño del incremento */

 	fahr = lower;
 	while (fahr <= upper) {
 		//celsius = (5.0/9.0) * (fahr - 32);
 		celsius = fahrToCel(fahr);
 		printf("%3.0f\t\t %6.1f\n", fahr, celsius);
 		fahr = fahr + step;
 	}

 	return 0;
}

int fahrToCel(int f)
{
	int c;
	c = (5.0/9.0) * (f - 32);
	return c;
}

1.8 Argumentos - llamadas por valor

 

1.9 Arreglos de caracteres

Ejercicio 1-16

Corrija la rutina principal del programa de la línea más larga de modo que imprima correctamente la longitud de líneas de entrada arbitrariamente largas, y tanto texto como sea posible.

#include 
#define MAXLINE 1000	/* tamaño máximo de la línea de entrada*/
/* Escribir un programa que lea un conjunto de líneas 
   de texto e imprima la de mayor longitud
   Arreglar el programa para que imprima correctamente
   la longitud de líneas de entrada arbitrariamente largas,
   y tanto texto como sea posible.
   */

int getlinea(char line[], int maxline);
void copy(char to[], char from[]);

int main()
{
	int len;				/* longitud actual de la línea */
	int max;				/* máxima longitud vista hasta el momento */
	char line[MAXLINE];		/* línea de entrada actual */
	char longest[MAXLINE];	/* la linea más larga se guarda aquí*/

	max = 0;
	while((len = getlinea(line, MAXLINE)) > 0)
		if(len > max) {
			max = len;
			copy(longest, line);
		}
	if(max > 0){			/* hubo una línea */
		printf("La longitud de la linea es %d\n", max);
		printf("%s\n", longest);
	}

 	return 0;
}

/* getlinea: lee una línea en s, regresa su longitud */
int getlinea(char s[], int lim)
{
	int c, i;

	for(i=0; i < lim-1 && (c = getchar()) != EOF && c != '\n'; ++i)
		s[i] = c;
	if(c == '\n') {
		s[i] = c;
		++i;
	}
	s[i] = '\0';
	return i;
}

/* copy: copia 'from' en 'to'; supone que to es suficientemente grande */
void copy(char to[], char from[])
{
	int i;

	i = 0;
	while((to[i] = from[i]) != '\0')
		++i;
}

Ejercicio 1-17

Escriba un programa que imprima todas las líneas de entrada que sean mayores de 80 caracteres

#include 
#define MAXLINE 1000	/* tamaño máximo de la línea de entrada*/

/* Escriba un programa que imprima todas las líneas de entrada que 
   sean mayores de 80 caracteres.
   */

int getlinea(char line[], int maxline);

int main() {
    int len; /* longitud actual de la línea */
    char line[MAXLINE]; /* línea actual */

    while ((len = getlinea(line, MAXLINE)) > 0) {
        if (len > 80) {         /* si la longitud > 80, imprime */
            printf("%s", line);
        }
    }
    return 0;
}

/* getline: lee una línea en s, regresa su longitud */
int getlinea(char s[], int lim) {
    int c, i;

    for (i = 0; i < lim-1 && (c = getchar()) != EOF && c != '\n'; ++i) {
        s[i] = c;
    }

    if (c == '\n') {
        s[i] = c;
        ++i;
    }
    s[i] = '\0';
    return i;
}

Ejercicio 1-18

Escriba una función reverse(s) que invierta la cadena de caracteres s. Úsela para escribir un programa que invierta su entrada, línea a línea.

#include 
#include 

/* Escribir una funcion reverse(s) ue invierta la cadena de caracteres s. Usarla
   para escribir un programa que invierta su entrada, línea a línea
*/

void reverse(char s[]) {
    int i, j;
    char c;

    for (i = 0, j = strlen(s) - 1; i < j; i++, j--) {
        c = s[i];
        s[i] = s[j];
        s[j] = c;
    }
}

int main() {
    char line[1000];

    while (fgets(line, sizeof(line), stdin)) {
        reverse(line);
        printf("%s\n", line);
    }
    return 0;
}

1.10 Variables externas y alcance

Ejercicio 1-20

Escribir un programa detab en C que reemplace tabuladores de la entrada con el número apropiado de blancos para espaciar hasta el siguiente paro de tabulación. Considerar un conjunto fijo de paros de tabulación, digamos cada n columnas. ¿debería ser n una variable o un parámetro simbólico?

/* Escribir un programa detab en C que reemplace tabuladores de la entrada 
con el número apropiado de blancos para espaciar hasta el siguiente paro 
de tabulación. Considerar un conjunto fijo de paros de tabulación, 
digamos cada n columnas. ¿debería ser n una variable o un parámetro simbólico?
*/
#include 

#define TAB_WIDTH 4

int main() {
    int c;
    int pos = 0;
    
    while ((c = getchar()) != EOF) {
        if (c == '\t') {
            int spaces = TAB_WIDTH - (pos % TAB_WIDTH);
            for (int i = 0; i < spaces; i++) {
                putchar(' ');
                pos++;
            }
        } else if (c == '\n') {
            putchar(c);
            pos = 0;
        } else {
            putchar(c);
            pos++;
        }
    }

    return 0;
}



En respuesta a la pregunta, si el ancho de tabulador debe ser una variable o un parámetro simbólico, la respuesta depende de si se espera que el ancho de tabulador cambie en tiempo de ejecución o si es una constante en el programa. Si el ancho de tabulador es constante, como en este ejemplo, entonces se puede definir como una constante o un macro, como se ha hecho aquí. Si se espera que el ancho de tabulador cambie en tiempo de ejecución, entonces debería ser una variable que se pueda cambiar en algún momento antes de su uso.

Ejercicio 1-21

Escribir un programa en C que reemplace cadenas de blancos por el mínimo número de tabuladores y blancos para obtener el mismo espaciado. Considerar los paros de tabulación de igual manera que en programa anterior. Cuando un tabulador o un simple espacio en blanco fuese suficiente para alcanzar un paro de tabulación, ¿a cuál se le debe dar preferencia?

/* Escribir un programa en C que reemplace cadenas de blancos 
por el mínimo número de tabuladores y blancos para obtener el 
mismo espaciado. Considerar los paros de tabulación de igual 
manera que en programa anterior. Cuando un tabulador o un 
simple espacio en blanco fuese suficiente para alcanzar un 
paro de tabulación, ¿a cuál se le debe dar preferencia?
*/
#include 

#define TAB_WIDTH 4

int main() {
    int c;
    int pos = 0;
    int space_count = 0;
    int tab_count = 0;

    while ((c = getchar()) != EOF) {
        if (c == ' ') {
            space_count++;
            pos++;
        } else {
            if (space_count > 0) {
                int spaces_needed = TAB_WIDTH - (pos % TAB_WIDTH);
                int tabs_needed = spaces_needed / TAB_WIDTH;
                spaces_needed = spaces_needed % TAB_WIDTH;

                if (space_count >= spaces_needed) {
                    tab_count += tabs_needed;
                    space_count -= spaces_needed;
                    putchar('\t');
                }

                while (tab_count > 0) {
                    putchar('\t');
                    tab_count--;
                }

                while (space_count > 0) {
                    putchar(' ');
                    space_count--;
                }
            }

            putchar(c);

            if (c == '\t') {
                pos += TAB_WIDTH - (pos % TAB_WIDTH);
            } else if (c == '\n') {
                pos = 0;
            } else {
                pos++;
            }
        }
    }

    return 0;
}




En este ejemplo, el programa mantiene dos contadores para realizar un seguimiento del número de espacios y pestañas en una cadena de espacios. Cuando se encuentra un carácter que no sea un espacio, el programa verifica si hay una cadena de espacios previa y calcula el número mínimo de pestañas y espacios necesarios para producir el mismo espaciado. Si un solo espacio es suficiente para alcanzar un paro de tabulación, el programa dará preferencia a un espacio en blanco.

En respuesta a la pregunta, si un tabulador o un espacio en blanco es preferible cuando un solo espacio en blanco es suficiente para alcanzar un paro de tabulación, la respuesta depende de la preferencia del usuario o del estándar utilizado. Por ejemplo, en el estándar POSIX, se indica que se debe dar preferencia a los espacios en blanco. Por lo tanto, en este ejemplo, el programa da preferencia a los espacios en blanco en caso de que un solo espacio sea suficiente para alcanzar un paro de tabulación.

Ejercicio 1-22

Escribir un programa en C para "doblar" líneas grandes de entrada en dos o más líneas más cortas después del último carácter no blanco que ocurra antes de la n-ésima columna de entrada. Asegurarse de que el programa se comporte apropiadamente con líneas muy largas y de que no haya blancos o tabuladores antes de la columna especificada.

/* Escribir un programa en C para "doblar" líneas grandes de entrada en dos o más 
líneas más cortas después del último carácter no blanco que ocurra antes de la 
n-ésima columna de entrada. Asegurarse de que el programa se comporte apropiadamente 
con líneas muy largas y de que no haya blancos o tabuladores antes de la columna especificada.
*/

#include 

#define MAX_LINE_LENGTH 1000
#define MAX_LINE_WIDTH 80

int main() {
    int c;
    int line_length = 0;
    char line[MAX_LINE_LENGTH];
    int i = 0;

    while ((c = getchar()) != EOF) {
        if (line_length == MAX_LINE_WIDTH) {
            // La línea ha alcanzado la longitud máxima permitida.
            // Buscamos el último carácter no blanco.
            int last_non_blank = -1;
            for (int j = MAX_LINE_WIDTH - 1; j >= 0; j--) {
                if (line[j] != ' ' && line[j] != '\t') {
                    last_non_blank = j;
                    break;
                }
            }

            if (last_non_blank == -1) {
                // No hay caracteres no blancos en la línea.
                // Simplemente dividimos la línea en dos en la columna especificada.
                for (int j = 0; j < MAX_LINE_WIDTH; j++) {
                    putchar(line[j]);
                }
                putchar('\n');
                for (int j = MAX_LINE_WIDTH; j < line_length; j++) {
                    putchar(line[j]);
                }
                line_length -= MAX_LINE_WIDTH;
            } else if (last_non_blank == MAX_LINE_WIDTH - 1) {
                // El último carácter no blanco está en la última columna.
                // Simplemente dividimos la línea en dos en la columna especificada.
                for (int j = 0; j < MAX_LINE_WIDTH; j++) {
                    putchar(line[j]);
                }
                putchar('\n');
                for (int j = MAX_LINE_WIDTH; j < line_length; j++) {
                    putchar(line[j]);
                }
                line_length -= MAX_LINE_WIDTH;
            } else {
                // Dividimos la línea en dos en el último carácter no blanco.
                for (int j = 0; j <= last_non_blank; j++) {
                    putchar(line[j]);
                }
                putchar('\n');
                for (int j = last_non_blank + 1; j < line_length; j++) {
                    putchar(line[j]);
                }
                line_length -= last_non_blank + 1;
            }
        }

        if (c == '\n') {
            // Nueva línea
            line_length = 0;
            i = 0;
        } else {
            // Agregamos el carácter a la línea actual
            if (line_length < MAX_LINE_LENGTH - 1) {
                line[i++] = c;
                line_length++;
            }
        }
    }

    return 0;
}

Ejercicio 1-23

Escribir un programa en C para eliminar todos los comentarios de un programa en C. No te olvides de manejar apropiadamente las cadenas entre comillas y las constantes de carácter. Los comentarios de C no se anidan.

/* Escribir un programa en C para eliminar todos los comentarios de un programa 
   en C. No te olvides de manejar apropiadamente las cadenas entre comillas 
   y las constantes de carácter. Los comentarios de C no se anidan.
*/

#include 
#include 

int main(void) {
    int c;
    bool inside_string = false; // Indica si estamos dentro de una cadena entre comillas
    bool inside_char = false; // Indica si estamos dentro de una constante de carácter

    while ((c = getchar()) != EOF) {
        if (c == '\"' && !inside_char) {
            inside_string = !inside_string;
            putchar(c);
        } else if (c == '\'' && !inside_string) {
            inside_char = !inside_char;
            putchar(c);
        } else if (!inside_string && !inside_char) {
            if (c == '/') {
                c = getchar();
                if (c == '*') {
                    // Comentario de varias líneas
                    while ((c = getchar()) != EOF) {
                        if (c == '*') {
                            c = getchar();
                            if (c == '/') {
                                break;
                            } else {
                                ungetc(c, stdin);
                            }
                        }
                    }
                } else if (c == '/') {
                    // Comentario de una sola línea
                    while ((c = getchar()) != EOF && c != '\n') {
                        // Descartamos el comentario
                    }
                    if (c != EOF) {
                        putchar('\n');
                    }
                } else {
                    putchar('/');
                    if (c != EOF) {
                        putchar(c);
                    }
                }
            } else {
                putchar(c);
            }
        } else {
            putchar(c);
        }
    }

    return 0;
}

El programa utiliza dos variables booleanas inside_string e inside_char para indicar si se está dentro de una cadena entre comillas o una constante de carácter, respectivamente. Si estamos dentro de una de estas estructuras, el programa simplemente imprime el carácter sin hacer ningún tipo de análisis. Si no estamos dentro de ninguna de estas estructuras, el programa examina el carácter para determinar si es el comienzo de un comentario. Si es así, el programa procesa el comentario de forma apropiada. Si no, el programa simplemente imprime el carácter.

El programa distingue entre comentarios de una sola línea (comenzando con //) y comentarios de varias líneas (comenzando con /* y terminando con */). En ambos casos, el programa descarta todos los caracteres correspondientes al comentario antes de continuar con la entrada.

Es importante notar que este programa no es capaz de manejar comentarios anidados, ya que los comentarios de C no se anidan. Sin embargo, el programa debería ser capaz de manejar apropiadamente todas las situaciones en las que se utilizan comentarios en un programa típico en C.

Ejercicio 1-24

Escribir un programa para revisar los errores de sintaxis rudimentarios de un programa en C, como paréntesis, llaves y corchetes no alineados. No olvidar las comillas ni los apóstrofos, las secuencias de escape y los comentarios.

/* Escribir un programa para revisar los errores de sintaxis rudimentarios de un 
programa en C, como paréntesis, llaves y corchetes no alineados. No olvidar las 
comillas ni los apóstrofos, las secuencias de escape y los comentarios.
*/

#include 

int main() {
    int c, i, line_num;
    int parens, braces, brackets, quotes;
    parens = braces = brackets = quotes = 0;
    line_num = 1;

    while ((c = getchar()) != EOF) {
        if (c == '\n') {
            line_num++;
        } else if (c == '(') {
            parens++;
        } else if (c == ')') {
            parens--;
            if (parens < 0) {
                printf("Error: paréntesis desalineados en línea %d\n", line_num);
                return 1;
            }
        } else if (c == '{') {
            braces++;
        } else if (c == '}') {
            braces--;
            if (braces < 0) {
                printf("Error: llaves desalineadas en línea %d\n", line_num);
                return 1;
            }
        } else if (c == '[') {
            brackets++;
        } else if (c == ']') {
            brackets--;
            if (brackets < 0) {
                printf("Error: corchetes desalineados en línea %d\n", line_num);
                return 1;
            }
        } else if (c == '"' || c == '\'') {
            quotes++;
            i = 1;
            while ((c = getchar()) != EOF) {
                if (c == '\\') {
                    i++;
                    c = getchar();
                } else if (c == '"' || c == '\'') {
                    quotes--;
                    break;
                }
                i++;
            }
            if (quotes < 0) {
                printf("Error: comillas o apóstrofos desalineados en línea %d\n", line_num);
                return 1;
            }
        }
    }
    
    if (parens > 0) {
        printf("Error: paréntesis desalineados en línea %d\n", line_num);
        return 1;
    } else if (braces > 0) {
        printf("Error: llaves desalineadas en línea %d\n", line_num);
        return 1;
    } else if (brackets > 0) {
        printf("Error: corchetes desalineados en línea %d\n", line_num);
        return 1;
    } else if (quotes > 0) {
        printf("Error: comillas o apóstrofos desalineados en línea %d\n", line_num);
        return 1;
    } else {
        printf("Sintaxis correcta\n");
        return 0;
    }
}


Este programa examina cada carácter de la entrada y mantiene un registro de los paréntesis, llaves, corchetes y comillas. Si se encuentra un paréntesis, llave o corchete izquierdo, el contador correspondiente se incrementa, y si se encuentra uno derecho, se decrementa. Si en algún momento el contador es menor que cero, se produce un error de sintaxis y se imprime un mensaje de error.

Por último, se verifica si hay algún carácter no válido fuera de una cadena o comentario, y se imprime un mensaje de error si se encuentra alguno.

Este programa solo cubre errores de sintaxis rudimentarios, por lo que todavía es posible que un programa tenga errores lógicos o de otro tipo incluso si pasa la verificación de sintaxis.