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:
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.
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;
}
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;
}
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;
}
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;
}
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;
}
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.
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;
}
&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;
}
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;
}
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;
}
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.
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
Elemento
Funció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
En división, cambia a a 9 y b a 4. ¿Qué se imprime?
En signo, prueba primero 0 y después 6.
En suma, sustituye i <= 5 por i < 5.
Llama a mayor con -2 y -8.
En el arreglo, cambia 4 por 8.
En cadena, cambia Ana por Sol. ¿Cuánto espacio ocupa?
Introduce 12abc y luego -1000, pulsando Intro en ambos casos.
Usa un archivo con Casa y AAA en dos líneas.
Ver respuestas
Entera: 2; Decimal: 2.25; Resto: 1.
Cero y Positivo, respectivamente.
Total: 10; se acumulan solo 1, 2, 3 y 4.
Devuelve -2, el mayor de los dos negativos.
Cantidad: 4; Promedio: 8.00.
Longitud: 3; Espacio: 4, incluido el terminador nulo.
12abc se rechaza; -1000 se acepta y muestra Valor: -1000.
Letras a: 2. Solo se cuentan las dos a minúsculas de Casa.
Errores frecuentes al empezar con C
Usar comillas tipográficas. El código necesita comillas rectas, no las de un procesador de texto.
Confundir = y ==. El primero asigna; el segundo compara igualdad.
Esperar decimales de una división entera. Convierte un operando antes de dividir y comprueba el divisor.
Olvidar el terminador de una cadena. Reserva espacio para los datos y para \0.
Salirse del arreglo. Si tiene n elementos, el último índice es n − 1.
Dar por válida una entrada. Comprueba conversiones, caracteres sobrantes y rango; que empiece por un número no basta.
Volver a usar memoria liberada. Tras free, el bloque deja de estar disponible para tu programa.
Ignorar las advertencias. Revisa formatos de printf, tipos y declaraciones; que compile no prueba que todos los caminos sean correctos.
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.