Programación · n.º 11 entre los lenguajes más usados

Aprende C desde cero

El lenguaje de los sistemas operativos: cerca del hardware y muy eficiente.

  • 10 lecciones
  • 10 ejercicios interactivos
  • 14 conceptos clave
  • 21,8 % lo usa*

Qué es C y para qué sirve

C es un lenguaje pequeño y muy cercano a la máquina. El núcleo de Linux, gran parte de Windows, las bases de datos o el firmware de muchos aparatos están escritos en C. Aprenderlo te enseña cómo funciona de verdad la memoria de un ordenador, aunque también te deja cometer errores que otros lenguajes impiden.

  • Sistemas operativos y drivers
  • Microcontroladores y dispositivos
  • Bibliotecas que usan otros lenguajes

Para empezar: Instala GCC o Clang (en Windows, con WSL o MSYS2) y compila cada ejemplo con gcc main.c -o main.

Nivel 1 Básico

Lección 1 · Básico

Tu primer programa

Todo programa en C empieza en main. #include <stdio.h> incluye las declaraciones de entrada y salida, entre ellas printf, que escribe texto con formato.

El compilador convierte el archivo .c en un ejecutable; \n es el salto de línea.

Ejemplo práctico
/* main.c */
#include <stdio.h>

int main(void) {
    printf("¡Hola, mundo!\n");
    printf("%d\n", 2 + 3);   /* 5 */
    return 0;
}

/* gcc -Wall main.c -o main && ./main */

Ejercicio

Completa la función que escribe texto con formato.

("Hola\n");
Ver la solución

Respuesta: printf. printf escribe en la salida estándar sustituyendo los marcadores de formato.

Lección 2 · Básico

Variables, tipos y formatos

Los tipos básicos son int (enteros), double (decimales) y char (un carácter). Cada variable se declara con su tipo y conviene darle un valor inicial.

En printf, cada tipo tiene su marcador: %d para int, %f para double (con %.2f para dos decimales), %c para un carácter y %s para una cadena.

Ejemplo práctico
#include <stdio.h>

int main(void) {
    int unidades = 3;
    double precio = 19.99;
    char talla = 'M';

    printf("%d unidades de talla %c: %.2f euros\n", unidades, talla, unidades * precio);
    /* 3 unidades de talla M: 59.97 euros */
    return 0;
}

Ejercicio

¿Qué marcador de printf muestra un double con dos decimales?

Ver la solución

Respuesta: %.2f. %f es para decimales y .2 limita la salida a dos cifras tras la coma.

Lección 3 · Básico

Condiciones y bucles

if/else, switch, for, while y do…while controlan el flujo. En C no existe un tipo booleano clásico: 0 es falso y cualquier otro valor es verdadero (desde C99 puedes usar bool con <stdbool.h>).

Ojo con confundir = (asignar) con == (comparar) dentro de un if: el compilador con -Wall te avisará.

Ejemplo práctico
#include <stdio.h>

int main(void) {
    for (int i = 1; i <= 10; i++) {
        if (i % 3 == 0) {
            printf("%d es múltiplo de 3\n", i);
        }
    }
    return 0;
}

Ejercicio

En C, ¿qué valor se considera «falso» en una condición?

Ver la solución

Respuesta: 0. Solo el 0 es falso; cualquier otro número cuenta como verdadero.

Lección 4 · Básico

Arrays y cadenas de texto

Un array es un bloque de elementos del mismo tipo con tamaño fijo. C no comprueba los límites: leer o escribir fuera del array es un error grave que puede colgar el programa o abrir un fallo de seguridad.

Una cadena es un array de char que termina con el carácter nulo '\0'. Las funciones de <string.h>, como strlen, cuentan hasta ese carácter.

Ejemplo práctico
#include <stdio.h>
#include <string.h>

int main(void) {
    int notas[3] = {7, 8, 9};
    int suma = 0;
    for (int i = 0; i < 3; i++) suma += notas[i];
    printf("Media: %.1f\n", suma / 3.0);   /* 8.0 */

    char nombre[] = "Ana";                  /* 'A' 'n' 'a' '\0' */
    printf("%s tiene %zu letras\n", nombre, strlen(nombre));
    return 0;
}

Ejercicio

¿Cuántos char ocupa la cadena "Ana" en memoria?

Ver la solución

Respuesta: 4. Las tres letras más el carácter nulo '\0' que marca el final.

Nivel 2 Intermedio

Lección 5 · Intermedio

Funciones

Una función se declara con su tipo de retorno, su nombre y sus parámetros. Si la usas antes de definirla, escribe antes su prototipo (la primera línea terminada en punto y coma).

En C los argumentos siempre se pasan por valor: la función recibe una copia.

Ejemplo práctico
#include <stdio.h>

double con_iva(double precio, double iva);   /* prototipo */

int main(void) {
    printf("%.2f\n", con_iva(100, 21));      /* 121.00 */
    return 0;
}

double con_iva(double precio, double iva) {
    return precio * (1 + iva / 100);
}

Ejercicio

¿Para qué sirve el prototipo de una función?

Ver la solución

Respuesta: Para que el compilador la conozca antes de su definición. El prototipo anuncia el nombre, los parámetros y el tipo de retorno antes de que aparezca el cuerpo.

Conceptos clave

Lección 6 · Intermedio

Punteros

Un puntero guarda una dirección de memoria. &x da la dirección de x y *p accede al valor al que apunta p.

Pasando punteros, una función puede modificar variables de quien la llama, algo imposible con la copia normal.

Ejemplo práctico
#include <stdio.h>

void intercambiar(int *a, int *b) {
    int tmp = *a;
    *a = *b;
    *b = tmp;
}

int main(void) {
    int x = 1, y = 2;
    intercambiar(&x, &y);
    printf("x=%d y=%d\n", x, y);   /* x=2 y=1 */
    return 0;
}

Ejercicio

Completa para pasar la dirección de x.

intercambiar(x, &y);
Ver la solución

Respuesta: &. &x es la dirección de memoria de x, que es lo que espera un parámetro int *.

Conceptos clave

Lección 7 · Intermedio

Estructuras (struct)

Un struct agrupa varios datos relacionados bajo un nombre, como los campos de una ficha. Accedes a cada campo con un punto, o con -> si tienes un puntero a la estructura.

Con typedef le das un nombre corto al tipo.

Ejemplo práctico
#include <stdio.h>

typedef struct {
    char nombre[32];
    double precio;
    int stock;
} Producto;

void reponer(Producto *p, int unidades) {
    p->stock += unidades;
}

int main(void) {
    Producto teclado = {"Teclado", 25.90, 2};
    reponer(&teclado, 5);
    printf("%s: %d unidades\n", teclado.nombre, teclado.stock);   /* 7 */
    return 0;
}

Ejercicio

Si p es un puntero a un Producto, ¿cómo lees su stock?

Ver la solución

Respuesta: p->stock. p->stock es la forma corta de (*p).stock.

Nivel 3 Avanzado

Lección 8 · Avanzado

Memoria dinámica: malloc y free

Cuando no sabes el tamaño hasta que el programa se ejecuta, pides memoria con malloc (de <stdlib.h>). Devuelve NULL si no hay memoria, así que compruébalo siempre.

Lo que reservas con malloc hay que liberarlo con free exactamente una vez. Olvidarlo deja la memoria ocupada (fuga), y usarla después de liberarla es un error grave.

Ejemplo práctico
#include <stdio.h>
#include <stdlib.h>

int main(void) {
    size_t n = 5;
    int *cuadrados = malloc(n * sizeof *cuadrados);
    if (cuadrados == NULL) return 1;

    for (size_t i = 0; i < n; i++) cuadrados[i] = (int)(i * i);
    printf("%d\n", cuadrados[4]);   /* 16 */

    free(cuadrados);
    cuadrados = NULL;
    return 0;
}

Ejercicio

Completa la función que libera la memoria reservada.

(cuadrados);
Ver la solución

Respuesta: free. free devuelve al sistema la memoria obtenida con malloc.

Lección 9 · Avanzado

Leer y escribir archivos

fopen abre un archivo y devuelve un FILE * (o NULL si falla). Con fprintf escribes y con fgets lees línea a línea; fclose lo cierra.

El modo "w" crea o vacía el archivo, "a" añade al final y "r" lo abre para leer.

Ejemplo práctico
#include <stdio.h>

int main(void) {
    FILE *f = fopen("notas.txt", "w");
    if (f == NULL) { perror("fopen"); return 1; }
    fprintf(f, "Comprar pan\nLlamar al médico\n");
    fclose(f);

    char linea[128];
    f = fopen("notas.txt", "r");
    if (f == NULL) { perror("fopen"); return 1; }
    while (fgets(linea, sizeof linea, f) != NULL) printf("- %s", linea);
    fclose(f);
    return 0;
}

Ejercicio

¿Qué modo de fopen añade texto al final sin borrar el contenido?

Ver la solución

Respuesta: "a". "a" (append) abre para añadir al final; "w" vacía el archivo.

Lección 10 · Avanzado

El preprocesador y varios archivos

Antes de compilar, el preprocesador procesa las líneas que empiezan por #: #include pega el contenido de otro archivo y #define crea constantes o macros.

Los programas grandes se reparten en archivos .c con sus cabeceras .h, que solo contienen declaraciones. Las guardas #ifndef evitan incluir dos veces la misma cabecera.

Ejemplo práctico
/* matematicas.h */
#ifndef MATEMATICAS_H
#define MATEMATICAS_H
#define PI 3.14159265358979
double area_circulo(double radio);
#endif

/* matematicas.c */
#include "matematicas.h"
double area_circulo(double radio) { return PI * radio * radio; }

/* main.c */
#include <stdio.h>
#include "matematicas.h"
int main(void) { printf("%.2f\n", area_circulo(1)); return 0; }   /* 3.14 */

/* gcc main.c matematicas.c -o main */

Ejercicio

¿Qué contiene normalmente un archivo .h?

Ver la solución

Respuesta: Declaraciones que comparten varios archivos .c. Las cabeceras declaran funciones, tipos y constantes; las definiciones van en los .c.

¿Has terminado? Sigue practicando

Repasa los conceptos en el diccionario, practica con estas herramientas o prueba otro lenguaje: las ideas que has aprendido (variables, condiciones, funciones, objetos…) te servirán en cualquiera.

Fuentes consultadas

* Porcentaje de personas que dijeron haber usado C en la Stack Overflow Developer Survey 2026: Technology (pregunta sobre lenguajes de programación, de scripts y de marcado; todas las respuestas). El progreso de las lecciones se guarda solo en este navegador.