💩

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 lines of terror int main() { // read data... // validate input... // calculate result... // format output... // save to file... // print to screen... // ... 490 more lines }
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' DOES NOT exist here
// 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(); // surprise!
}

⚠ 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 — HIDES the global
printf("%d", x); // prints 5, not 100
} // the local x disappears here

int main() {
printf("%d", x); // prints 100 (the global)
miFuncion(); // prints 5
printf("%d", x); // still 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 inc(x) se llama — 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++; // modifies the copy, not the original
}

int main() {
int x = 5;
inc(x); // passes a COPY of x
printf("%d", x); // prints 5 — unchanged
}

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 &x (la dirección) se pasa. 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; // dereference: modifies what it points to
}

int main() {
int x = 5;
cambiar(&x); // passes the ADDRESS of x
printf("%d", x); // prints 10 — was modified
}

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 still = 5 ✔

POR REFERENCIA

main: 0xFF01
x
10 ⚠
se pasa la dirección ↓
func: 0xFF20
p (ptr)
➜ 0xFF01
x now = 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 → result = 200

// Order DOES matter in many cases:
dividir(10, 2); // 10 / 2 = 5
dividir(2, 10); // 2 / 10 = 0 (VERY different result)

Ejemplo Completo

Un programa que usa TODO lo que vimos

#include <stdio.h>

// GLOBAL: visible in all functions
int llamadas = 0;

// FUNCTION: returns the area (by value)
int areaRectangulo(int base, int altura) {
llamadas++; // modifies global
int area = base * altura; // LOCAL
return area;
}

// PROCEDURE: prints (void, no return)
void imprimirResultado(int valor) {
printf("Resultado: %d\n", valor);
}

// BY REFERENCE: duplicates the original
void duplicar(int *p) {
*p *= 2;
}

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

duplicar(&area); // passes address — by reference
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