El lenguaje C prioriza el control directo del programador sobre el hardware para lograr un alto rendimiento. A diferencia de lenguajes modernos, C ofrece pocas abstracciones, lo que permite traducir el código a muy pocas instrucciones de máquina, ideal para sistemas operativos y controladores.
C confía plenamente en el programador. No verifica límites de arreglos ni punteros automáticamente, lo que brinda gran poder pero exige disciplina. Un error simple puede causar fallos graves (segmentation faults), por lo que aprender buenas prácticas desde el inicio es crucial.
C es de tipado estático: el tipo de cada variable (int, float, char) se define al compilar y no cambia. Esto permite al compilador optimizar el código y detectar errores de incompatibilidad antes de ejecutar el programa.
Como lenguaje estructurado, C utiliza if/else, switch, while y for para controlar el flujo. No existe un tipo booleano nativo clásico; el 0 es falso y cualquier otro valor es verdadero.
En C, la gestión de memoria es manual. Las variables locales viven en el "Stack" y se limpian solas al salir de la función. La memoria dinámica vive en el "Heap" y el programador debe reservarla y liberarla manualmente.
Un puntero es una variable que almacena una dirección de memoria. Usamos "&" para obtener la dirección de una variable y "*" para acceder al valor guardado en esa dirección. Es vital inicializarlos (o usar NULL) para evitar errores graves.
Para datos que deben sobrevivir fuera de una función, usamos "malloc" (reservar) y "free" (liberar). Si no liberamos la memoria con free, creamos fugas de memoria (memory leaks).
C pasa todo por valor (copia). Para modificar una variable original dentro de una función, pasamos su dirección (puntero). Esto simula el "paso por referencia".
Para organizar programas grandes, separamos el código en archivos de cabecera (.h) para declaraciones y archivos fuente (.c) para implementaciones. Esto permite reutilización y compilación separada.
Cada archivo .c se compila a un objeto (.o) independiente. Luego, el "Linker" une todos los objetos para crear el ejecutable final, resolviendo las referencias entre ellos.

Imagen 10. Flujo Compilación y Linker
Diagrama del proceso de compilación separada y enlazado (Linking).
Usamos "#include" para insertar cabeceras y "extern" para compartir variables globales entre archivos. Es fundamental para proyectos complejos.
Las funciones son bloques de código reutilizables fundamentales en la programación estructurada. Permiten dividir problemas complejos en tareas específicas (como printf o main) y evitar la repetición de código.
Es vital distinguir entre la "Declaración" (prototipo), que indica al compilador el nombre y tipos de la función antes de usarla, y la "Definición", que contiene el código real. Es buena práctica poner prototipos en archivos .h.
Por defecto, C usa "paso por valor". Esto significa que la función recibe una COPIA de las variables. Modificar los parámetros dentro de la función NO afecta a las variables originales fuera de ella.
Para que una función modifique variables externas, usamos punteros. Pasamos la dirección de memoria (&variable) y la función opera sobre el valor apuntado (*p). Esto simula el paso por referencia.
Las variables locales mueren al terminar la función. C permite la recursividad: una función que se llama a sí misma. Es útil para problemas matemáticos como el factorial.