💩

programierds

PROGRAMIERDS_OS v1.0.4 // TOPIC: MODULARIDAD_C

Modularización

en C

Subprogramas, Funciones, Procedimientos y el arte de dividir para conquistar.

↓ scroll para continuar


¿Qué es Modularizar?

Dividir un problema grande en partes pequeñas y manejables

✘ El Monolito

// main.c — 500 líneas de terror int main() { // leer datos... // validar entrada... // calcular resultado... // formatear salida... // guardar en archivo... // imprimir por pantalla... // ... 490 líneas más }
Un solo bloque gigante. Si algo falla, todo explota.

✔ Modularizado

leerDatos()
validarEntrada()
calcularResultado()
formatearSalida()
guardarArchivo()
Piezas pequeñas, independientes, reutilizables. Como LEGO.

ANALOGÍA

No esculpís desde una roca entera — construyés con LEGO. Cada pieza tiene forma definida, encaja con otras, y la podés reusar en cualquier construcción.


Subprogramas

En C, todo subprograma es una función. No hay otra cosa.

Anatomía de una Función

TIPO_DE_RETORNO — qué devuelve
NOMBRE — identificador de la función
PARÁMETROS — datos de entrada
CUERPO — lógica e instrucciones
int sumar(int a, int b) {
int resultado = a + b;
return resultado;
}

REUTILIZACIÓN

Una vez definida, la función puede llamarse infinitas veces desde cualquier parte del programa.

ABSTRACCIÓN

No necesitás saber cómo funciona para usarla. Solo qué recibe y qué devuelve.

int x = sumar(3, 7); // x = 10
int y = sumar(100, 200); // y = 300
int z = sumar(x, y); // z = 310

Funciones vs Procedimientos

La diferencia está en si devuelven algo o no

PROCEDIMIENTO

void — ejecuta una acción, no retorna nada

datos IN
imprimirMenu()
❌ sin retorno

Como un trabajador que hace la tarea y ya

void imprimirMenu() {
printf("1. Ingresar\n");
printf("2. Salir\n");
// no hay return
}

FUNCIÓN

con tipo de retorno — calcula y devuelve un valor

a, b IN
sumar(a, b)
resultado OUT

Como un trabajador que trae de vuelta el resultado

int sumar(int a, int b) {
return a + b;
}
int r = sumar(3, 4); // r = 7

Variables Locales

Viven dentro de la función. Cuando termina, desaparecen.

STACK DE EJECUCIÓN

▼ main()
argc = 1
▼ calcular()
base = 10 altura = 5 area = 50
↑ solo existen acá dentro
STACK

Cada llamada apila un nuevo "cajón" con sus vars locales

int calcular(int base, int altura) {
int area = base * altura;
return area;
}

int main() {
int r = calcular(10, 5);
// 'area' NO existe acá
// printf("%d", area); → ERROR
}

⚠ Error de compilación

Intentar usar area fuera de calcular() falla. La variable local no existe fuera de su scope. El compilador te lo dice en la cara.


Variables Globales

Declaradas fuera de toda función. Todos pueden verlas... y modificarlas.

VARIABLE GLOBAL

int contador = 0;
funcionA() puede leer y escribir contador
funcionB() puede leer y escribir contador
main() puede leer y escribir contador
int contador = 0; // GLOBAL

void incrementar() {
contador++;
}

void resetear() {
contador = 0;
}

int main() {
incrementar();
incrementar();
resetear(); // sorpresa!
}

⚠ PELIGRO: Efecto Fantasma

resetear() modifica contador sin que nadie lo espere. Este tipo de bug es una pesadilla para debuggear.

Usá globales solo cuando sea estrictamente necesario.


Locales vs Globales

Y el misterio del shadowing: cuándo una variable oculta a otra

int x = 100; // GLOBAL x

void miFuncion() {
int x = 5; // LOCAL x — OCULTA a la global
printf("%d", x); // imprime 5, no 100
} // la local x desaparece acá

int main() {
printf("%d", x); // imprime 100 (la global)
miFuncion(); // imprime 5
printf("%d", x); // sigue siendo 100
}

LOCAL (cyan)

Existe solo dentro de su bloque. Tiene prioridad sobre la global del mismo nombre dentro de ese scope. Cuando el bloque termina, desaparece del stack.

GLOBAL (amber)

Visible desde cualquier función. Si una función tiene una local del mismo nombre, la global queda oculta (shadowed) dentro de esa función.


Pasaje Por Valor

Se copia el valor. El original queda intacto.

FRAME: main()
0xFF01
x
5
FRAME: inc()
0xFF10
n (copia)
5
1 x vale 5 en el frame de main
2 Se llama inc(x) — el valor 5 se copia en n
3 Dentro de inc(), n pasa a valer 6 (n++)
4 La función termina, el frame de inc() se destruye. n desaparece.
5 x sigue valiendo 5. El original no fue tocado.
void inc(int n) {
n++; // modifica la copia, no el original
}

int main() {
int x = 5;
inc(x); // pasa una COPIA de x
printf("%d", x); // imprime 5 — sin cambios
}

Pasaje Por Referencia

Se pasa la dirección de memoria. El original SÍ se modifica.

FRAME: main()
0xFF01
x
5
FRAME: cambiar()
0xFF20
p (puntero)
0xFF01
1 x vale 5 en dirección 0xFF01
2 Se pasa &x (la dirección). El puntero p recibe 0xFF01
3 *p = 10 sigue la dirección y modifica la memoria en 0xFF01
4 La función termina. p desaparece, pero el daño está hecho.
5 x ahora vale 10. El original fue modificado.
void cambiar(int *p) {
*p = 10; // desreferencia: modifica lo que apunta
}

int main() {
int x = 5;
cambiar(&x); // pasa la DIRECCIÓN de x
printf("%d", x); // imprime 10 — fue modificado
}

Valor vs Referencia

Mismo punto de partida, resultado completamente distinto

POR VALOR

main: 0xFF01
x
5
se copia el valor ↓
func: 0xFF10
n (copia)
6
x sigue = 5 ✔

POR REFERENCIA

main: 0xFF01
x
10 ⚠
se pasa la dirección ↓
func: 0xFF20
p (ptr)
➜ 0xFF01
x ahora = 10 ⚠
Aspecto Por Valor Por Referencia (puntero)
Qué se pasa Copia del valor Dirección de memoria
Modifica original NO ✔ SÍ ⚠
Sintaxis en llamada func(x) func(&x)
Sintaxis en parámetro int n int *p
Cuándo usar Leer datos, calcular Modificar el original

Parámetros Nominales y Efectivos

Definición vs llamada. Placeholder vs valor real.

PARÁMETROS NOMINALES

Los de la DEFINICIÓN — son placeholders, variables locales

int calcular(int base, int altura) {
return base * altura;
}
base altura

Son como variables normales dentro de la función. Se les asigna valor en la llamada.

PARÁMETROS EFECTIVOS

Los de la LLAMADA — los valores reales que se pasan

calcular(10, 20);
10 20

Son los valores concretos (literales, variables o expresiones) que se envían en la llamada.

CORRESPONDENCIA POSICIONAL — el orden importa

calcular(10, 20);
// base=10, altura=20 → resultado = 200

// El orden SÍ importa en muchos casos:
dividir(10, 2); // 10 / 2 = 5
dividir(2, 10); // 2 / 10 = 0 (resultado MUY diferente)

Ejemplo Completo

Un programa que usa TODO lo que vimos

#include <stdio.h>

// GLOBAL: visible en todas las funciones
int llamadas = 0;

// FUNCIÓN: retorna el área (por valor)
int areaRectangulo(int base, int altura) {
llamadas++; // modifica global
int area = base * altura; // LOCAL
return area;
}

// PROCEDIMIENTO: imprime (void, sin retorno)
void imprimirResultado(int valor) {
printf("Resultado: %d\n", valor);
}

// POR REFERENCIA: duplica el original
void duplicar(int *p) {
*p *= 2;
}

int main() {
// nominales: base, altura — efectivos: 5, 10
int area = areaRectangulo(5, 10);
imprimirResultado(area); // 50

duplicar(&area); // pasa dirección — por referencia
imprimirResultado(area); // 100

printf("Llamadas: %d\n", llamadas); // 1
return 0;
}

GLOBAL

llamadas — visible en todas las funciones

LOCAL

area dentro de areaRectangulo — solo ahí

PUNTERO

*p en duplicar — modifica el original


Reglas de Oro

Grabátelas. Para siempre.

01

Modularizá: funciones cortas que hacen UNA cosa

Si tu función necesita un comentario explicando qué hace, es demasiado larga.

02

Preferí variables locales sobre globales

Las globales crean dependencias ocultas y bugs imposibles de rastrear.

03

Paso por valor: seguro, el original no cambia

Usálo cuando solo necesitás leer o calcular algo a partir del dato.

04

Paso por referencia (*punteros): cuando NECESITÁS modificar el original

Sé explícito. Quien lee el código necesita saber que la función tiene efectos secundarios.

05

Parámetros nominales = definición, efectivos = llamada

El orden de los efectivos tiene que coincidir con el de los nominales. Siempre.

06

Cada función = una responsabilidad

Single Responsibility Principle. Vale en C, vale en cualquier lenguaje, vale en cualquier paradigma.


EOF // END_OF_PRESENTATION
>_

SESIÓN FINALIZADA

Ahora sabés modularizar en C. Ahora pensá en módulos.

PROGRAMIERDS — 2026