Explorar temas

INFORMÁTICA

12 ejemplos de lenguaje C con código y resultados

Empieza mostrando un mensaje y avanza hasta validar una entrada, manejar memoria y leer un archivo. Cada ejemplo es un programa completo que puedes compilar por separado; incluye la salida esperada y una explicación del procedimiento.

C es un lenguaje de programación de propósito general que permite organizar instrucciones en funciones y trabajar con datos, direcciones de memoria y bibliotecas. Estos ejemplos usan C17 y la biblioteca estándar de C; no requieren C++.

Cómo compilar y ejecutar los ejemplos

Los doce programas se han compilado y ejecutado en Windows con GCC 9.2.0 (MinGW.org), modo C17, sin advertencias con las opciones indicadas. Usa una terminal con GCC disponible y comprueba la versión mediante gcc --version. La versión se indica para que puedas reproducir la prueba. [1]

Guarda el primer programa como saludo.c. En Windows:

gcc -std=c17 -Wall -Wextra -Wpedantic saludo.c -o saludo.exe
.\saludo.exe

Con GCC disponible en Linux o macOS, los comandos equivalentes son:

gcc -std=c17 -Wall -Wextra -Wpedantic saludo.c -o saludo
./saludo

Compila un archivo cada vez, cambiando saludo por el nombre de su ejemplo. Todos tienen su propio main: no los juntes en un único programa. Los mensajes de consola usan caracteres sin tildes para reducir diferencias de codificación. El punto es el separador decimal de las salidas mostradas.

Descargar los doce programas y palabras.txt. Incluye instrucciones y resultados; no contiene compilador ni ejecutables.

12 ejemplos de C para practicar

Los primeros casos trabajan con datos fijos. El décimo recibe texto de teclado y el último necesita el archivo que se proporciona. Puedes desplazar el código horizontalmente en móvil; con teclado, enfoca el bloque y usa las flechas.

EJEMPLO 01 · Salida

Mostrar un mensaje y un salto de línea

Archivo: saludo.c

#include <stdio.h>

int main(void) {
    printf("Hola visitante a Ejemplode.com\n");
    return 0;
}

Descargar saludo.c

Salida

Hola visitante a Ejemplode.com

stdio.h declara printf. La ejecución comienza en main; return 0 indica terminación correcta. La secuencia \n produce el salto de línea y no se imprime como dos caracteres.

El texto entre comillas puede cambiarse; conserva las comillas rectas y el punto y coma.

EJEMPLO 02 · Tipos y operaciones

Comparar división entera y decimal

Archivo: division.c

#include <stdio.h>

int main(void) {
    int a = 7, b = 2;
    if (b == 0) {
        puts("No se puede dividir por cero");
        return 1;
    }
    printf("Entera: %d\n", a / b);
    printf("Decimal: %.2f\n", (double)a / b);
    printf("Resto: %d\n", a % b);
    return 0;
}

Descargar division.c

Salida

Entera: 3
Decimal: 3.50
Resto: 1

Con dos int, la división descarta la parte fraccionaria hacia cero. Convertir a double antes de dividir cambia la operación; convertir el resultado de 7 / 2 llegaría demasiado tarde. El operador % calcula el resto entero.

Este caso usa enteros pequeños. No sirve como división general para todos los límites de int: el cociente también debe ser representable. double aproxima muchos decimales; no garantiza aritmética monetaria exacta.

EJEMPLO 03 · Condiciones

Distinguir positivos, negativos y cero

Archivo: signo.c

#include <stdio.h>

int main(void) {
    int numero = -4;
    if (numero > 0) {
        puts("Positivo");
    } else if (numero < 0) {
        puts("Negativo");
    } else {
        puts("Cero");
    }
    return 0;
}

Descargar signo.c

Salida

Negativo

Solo se ejecuta una rama. Prueba también 0 y 6: obtendrás Cero y Positivo. Las llaves agrupan las instrucciones que pertenecen a cada condición.

EJEMPLO 04 · Repetición

Acumular los primeros cinco enteros positivos

Archivo: suma.c

#include <stdio.h>

int main(void) {
    int total = 0;
    for (int i = 1; i <= 5; i++) {
        total += i;
    }
    printf("Total: %d\n", total);
    return 0;
}

Descargar suma.c

Salida

Total: 15

El acumulador comienza en cero. El bucle visita 1, 2, 3, 4 y 5; cambiar <= por < excluiría el 5 y daría 10.

Los límites pequeños evitan desbordamientos; no amplíes el bucle indefinidamente sin revisar la capacidad del tipo.

EJEMPLO 05 · Funciones

Reutilizar una función que devuelve el mayor

Archivo: funcion.c

#include <stdio.h>

int mayor(int a, int b) {
    return a > b ? a : b;
}

int main(void) {
    printf("Mayor: %d\n", mayor(8, 3));
    printf("Iguales: %d\n", mayor(5, 5));
    return 0;
}

Descargar funcion.c

Salida

Mayor: 8
Iguales: 5

La función recibe copias de dos valores y devuelve uno. El operador condicional elige a cuando a > b y b en otro caso; si son iguales, cualquiera representa el mismo resultado.

La definición está antes de main, por lo que la declaración de la función ya es visible al llamarla.

EJEMPLO 06 · Colecciones

Calcular el promedio de un arreglo

Archivo: arreglo.c

#include <stdio.h>

int main(void) {
    int datos[] = {6, 8, 10, 4};
    size_t n = sizeof datos / sizeof datos[0];
    int suma = 0;
    for (size_t i = 0; i < n; i++) {
        suma += datos[i];
    }
    printf("Cantidad: %zu\n", n);
    printf("Promedio: %.2f\n", (double)suma / n);
    return 0;
}

Descargar arreglo.c

Salida

Cantidad: 4
Promedio: 7.00

Los índices van de 0 a n - 1. El arreglo contiene cuatro elementos, de modo que el divisor no es cero. size_t es el tipo que devuelve sizeof; %zu permite mostrarlo.

La fórmula de sizeof calcula la cantidad aquí, donde datos es un arreglo. No obtiene la longitud de un arreglo recibido como parámetro de función: allí el parámetro se ajusta a un puntero. La suma de estos cuatro valores cabe en int.

EJEMPLO 07 · Cadenas

Medir un texto sin contar el terminador

Archivo: cadena.c

#include <stdio.h>
#include <string.h>

int main(void) {
    char nombre[] = "Ana";
    printf("Texto: %s\n", nombre);
    printf("Longitud: %zu\n", strlen(nombre));
    printf("Espacio: %zu\n", sizeof nombre);
    return 0;
}

Descargar cadena.c

Salida

Texto: Ana
Longitud: 3
Espacio: 4

El arreglo guarda A, n, a y el terminador nulo \0. strlen recorre la cadena hasta ese terminador; sizeof cuenta todo el espacio del arreglo.

Con texto multibyte, la longitud en bytes no equivale necesariamente al número de letras visibles. strlen requiere una cadena correctamente terminada; no comprueba por sí sola el tamaño del arreglo.

EJEMPLO 08 · Punteros

Modificar una variable mediante su dirección

Archivo: puntero.c

#include <stdio.h>

void asignar(int *destino, int valor) {
    if (destino != NULL) {
        *destino = valor;
    }
}

int main(void) {
    int puntos = 3;
    asignar(&puntos, 9);
    asignar(NULL, 20);
    printf("Puntos: %d\n", puntos);
    return 0;
}

Descargar puntero.c

Salida

Puntos: 9

&puntos obtiene la dirección de una variable que sigue existiendo. Dentro de asignar, *destino accede a esa variable. La segunda llamada no hace nada gracias a la comprobación de NULL.

Un puntero distinto de NULL no es automáticamente válido. El contrato de esta función exige NULL o la dirección de un int vivo y modificable. No se deben inventar direcciones ni usar punteros a objetos que ya dejaron de existir.

EJEMPLO 09 · Estructuras

Reunir campos de un producto

Archivo: estructura.c

#include <stdio.h>

struct Producto {
    char nombre[20];
    int unidades;
    int centavos;
};

int main(void) {
    struct Producto p = {"Cuaderno", 3, 250};
    int total = p.unidades * p.centavos;
    printf("%s: %d unidades\n", p.nombre, p.unidades);
    printf("Total: %d.%02d\n", total / 100, total % 100);
    return 0;
}

Descargar estructura.c

Salida

Cuaderno: 3 unidades
Total: 7.50

struct reúne campos relacionados; el punto permite acceder a cada uno. El precio se representa en centavos enteros y el total es 750: 7 unidades monetarias y 50 centavos.

El caso usa cantidades no negativas y un total pequeño. Antes de convertirlo en un sistema de ventas habría que validar rangos, moneda y posibles desbordamientos; el formato mostrado no resuelve importes negativos.

EJEMPLO 10 · Entrada y errores

Leer y validar un entero entre −1000 y 1000

Archivo: entrada.c

Entrada: escribe 42 y pulsa Intro. La salida de abajo excluye el eco del teclado.

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>

int main(void) {
    char linea[64];
    char *fin;
    if (fgets(linea, sizeof linea, stdin) == NULL) {
        puts("No se recibio una linea");
        return 1;
    }
    if (strchr(linea, '\n') == NULL) {
        puts("Linea demasiado larga o sin Intro");
        return 1;
    }
    errno = 0;
    long valor = strtol(linea, &fin, 10);
    if (fin == linea || errno == ERANGE) {
        puts("Entero no valido");
        return 1;
    }
    while (isspace((unsigned char)*fin)) {
        fin++;
    }
    if (*fin != '\0' || valor < -1000 || valor > 1000) {
        puts("Usa un entero entre -1000 y 1000");
        return 1;
    }
    printf("Valor: %ld\n", valor);
    return 0;
}

Descargar entrada.c

Salida

Valor: 42

Escribe 42 y pulsa Intro. fgets limita la lectura; strtol convierte en base diez y deja en fin dónde terminó. Se comprueba que haya dígitos, que no exista desbordamiento y que el resto solo tenga espacios. Después se aplica el rango del ejercicio.

Admite signo y espacios alrededor. Rechaza 12abc, 4.5 y valores fuera del rango. Esta versión exige el salto de línea: admite hasta 62 caracteres antes de Intro y termina ante una línea más larga o entrada sin Intro. Está pensada para texto de consola, no datos binarios con bytes nulos.

EJEMPLO 11 · Memoria dinámica

Reservar, usar y liberar memoria

Archivo: memoria.c

#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t n = 3;
    int *datos = malloc(n * sizeof *datos);
    if (datos == NULL) {
        puts("No se pudo reservar memoria");
        return 1;
    }
    for (size_t i = 0; i < n; i++) {
        datos[i] = (int)i + 1;
    }
    printf("Valores: %d %d %d\n",
           datos[0], datos[1], datos[2]);
    free(datos);
    return 0;
}

Descargar memoria.c

Salida

Valores: 1 2 3

malloc reserva espacio para tres int, pero no inicializa sus valores. El bucle los escribe antes de leerlos. Si la reserva falla se termina sin acceder al bloque; free lo libera al terminar.

n vale 3 y es fijo en este ejemplo. Si viniera de una entrada habría que validar n y comprobar que n * sizeof *datos no desborde size_t. Después de free no se puede volver a acceder al bloque ni liberarlo otra vez.

EJEMPLO 12 · Archivos

Contar la letra a en un archivo de texto

Archivo: archivo.c

Contenido de palabras.txt:

casa
mapa

Descargar palabras.txt

#include <stdio.h>
#include <limits.h>

int main(void) {
    FILE *f = fopen("palabras.txt", "r");
    if (f == NULL) {
        puts("No se pudo abrir palabras.txt");
        return 1;
    }
    int c;
    unsigned long total = 0;
    while ((c = fgetc(f)) != EOF) {
        if (c == 'a') {
            if (total == ULONG_MAX) {
                fclose(f);
                puts("Conteo fuera de rango");
                return 1;
            }
            total++;
        }
    }
    int error_lectura = ferror(f);
    int error_cierre = fclose(f);
    if (error_lectura || error_cierre != 0) {
        puts("Error al leer o cerrar el archivo");
        return 1;
    }
    printf("Letras a: %lu\n", total);
    return 0;
}

Descargar archivo.c

Salida

Letras a: 4

Guarda palabras.txt en el directorio desde el que ejecutas el programa. Se abre para lectura: no modifica su contenido. fgetc devuelve int para poder representar un carácter o EOF. Se distingue el fin del archivo de un error de lectura.

Cuenta solo la a minúscula sin tilde, no A ni á. Un archivo vacío produce cero. Si no existe o no puede abrirse, se muestra el error y se devuelve 1.

Cómo leer un programa en C

Elementos del primer ejemplo
ElementoFunción
#include <stdio.h>Incorpora las declaraciones de entrada y salida.
int main(void)Define el punto de entrada de estos programas de consola, sin parámetros.
{ … }Delimita un bloque de instrucciones.
printf(...);Llama a una función para escribir una salida con formato.
return 0;Termina main con un resultado de éxito.

Del archivo .c al ejecutable

Con GCC, el proceso puede recorrer cuatro etapas: preprocesado, compilación, ensamblado y enlazado. El preprocesador resuelve directivas como #include; después se traduce el programa, se genera el objeto y se enlaza con las funciones necesarias. Un único comando puede dirigir toda la secuencia. [2]

Incluir una cabecera no incorpora por sí solo la implementación de todas sus funciones. Las bibliotecas aportan operaciones reutilizables; el enlazado resuelve las referencias que utiliza el programa.

Tipos, conversiones y memoria

C permite algunas conversiones implícitas: por ejemplo, en 2 + 0.5 el entero se convierte para realizar una suma decimal. Otras conversiones se escriben expresamente, como (double)a. No todas conservan la información: convertir 3.9 a int produce 3 si el valor es representable. Esto no significa que cualquier mezcla de tipos sea válida.

Un puntero permite referirse a un objeto, pero C no da permiso universal para leer o escribir memoria. Acceder fuera de un arreglo, usar una variable sin inicializar o desreferenciar un puntero inválido puede producir comportamiento indefinido. En sistemas con protección de memoria, el sistema operativo restringe los accesos entre procesos y a zonas privilegiadas; los detalles dependen de la plataforma. [5]

Los ejemplos distinguen tres situaciones: una variable local que sigue existiendo, un arreglo con límites conocidos y un bloque reservado con malloc. Comprobar NULL es necesario cuando corresponde, pero también hay que respetar el tamaño, el tipo y el tiempo de vida del objeto. [4]

Origen, usos y diferencia con C++

Dennis Ritchie desarrolló C a partir de B en Bell Labs entre 1971 y 1973; 1972 fue un año especialmente importante. Su desarrollo estuvo ligado a Unix, que pasó de una implementación inicial en ensamblador a una reescritura de gran parte del sistema en C. [3]

C se utiliza en software de sistemas, bibliotecas y programas cercanos al hardware, además de aplicaciones generales. La eficiencia depende del programa, el compilador y la plataforma. La etiqueta «nivel medio» es una descripción informal de su combinación de abstracciones y operaciones cercanas a la máquina; no es una categoría normativa que garantice control sobre discos, impresoras o cualquier memoria. Ese control requiere interfaces y permisos del entorno.

C y C++ son lenguajes distintos. Comparten muchas construcciones, pero difieren en sus reglas y bibliotecas. Aquí se usan archivos .c, printf y el compilador de C; no se mezclan cout, clases, new ni cabeceras de C++.

8 ejercicios para comprobar lo aprendido

  1. En división, cambia a a 9 y b a 4. ¿Qué se imprime?
  2. En signo, prueba primero 0 y después 6.
  3. En suma, sustituye i <= 5 por i < 5.
  4. Llama a mayor con -2 y -8.
  5. En el arreglo, cambia 4 por 8.
  6. En cadena, cambia Ana por Sol. ¿Cuánto espacio ocupa?
  7. Introduce 12abc y luego -1000, pulsando Intro en ambos casos.
  8. Usa un archivo con Casa y AAA en dos líneas.
Ver respuestas
  1. Entera: 2; Decimal: 2.25; Resto: 1.
  2. Cero y Positivo, respectivamente.
  3. Total: 10; se acumulan solo 1, 2, 3 y 4.
  4. Devuelve -2, el mayor de los dos negativos.
  5. Cantidad: 4; Promedio: 8.00.
  6. Longitud: 3; Espacio: 4, incluido el terminador nulo.
  7. 12abc se rechaza; -1000 se acepta y muestra Valor: -1000.
  8. Letras a: 2. Solo se cuentan las dos a minúsculas de Casa.

Errores frecuentes al empezar con C

Referencias

  1. [1] GCC 9.2 · Selección de dialecto C17
  2. [2] GCC 9.2 · Etapas y opciones de compilación
  3. [3] Dennis M. Ritchie · The Development of the C Language
  4. [4] WG14 · N1570, borrador de C11: tipos, expresiones y biblioteca estándar
  5. [5] Microsoft · Espacio de direcciones virtuales de un proceso

Para las reglas de tipos y biblioteca se consultaron apartados de N1570, borrador público de C11, especialmente 6.3, 6.5, 7.21 y 7.22. La ejecución declarada usa C17; la documentación de GCC explica la relación entre ambas revisiones.