💩

programierds

PROGRAMIERDS_OS v1.0.4 // TOPIC: C_MEMORY

> Paso por Valor

y Referencia

En C, todo se pasa por valor. Los punteros hacen que parezca magia, pero siguen siendo copias de direcciones.

VALOR
copia directa
REFERENCIA
dirección de memoria

↓ scroll para continuar


FUNDAMENTO

Stack vs Heap

Primero entendé cómo C organiza la memoria. Sin esto, nada de lo que viene tiene sentido.

STACK

Variables locales y referencias. LIFO.

int x 42
boolean b true
int[] nums → 0x4A2F
Persona p → 0x8C1D

Primitivos: el valor vive acá.
Objetos: solo la dirección (referencia).

HEAP

Objetos reales. Gestionado por el GC.

0x4A2F
int[ ]
[ 1, 2, 3 ]
0x8C1D
Persona
nombre: "Ana"
0x3F0B
char *
"Hola"

Los objetos reales viven acá.
El Stack guarda la dirección para llegar.

LA REGLA FUNDAMENTAL

Cuando C pasa algo a una función, lo que viaja es el contenido de la variable en el Stack. Para primitivos eso es el valor directo. Para objetos eso es la dirección de memoria (la referencia). En AMBOS casos: se copia.


CASO 01

Paso por Valor

Con primitivos (int, double, boolean, char...)

void cambiar(int x) {
  x = 99;
  // x es una COPIA local
}

int a = 5;
cambiar(a);
printf("%d\n", a); // 5 ← no cambió

POR QUÉ

C copia el valor 5 en una nueva variable x. Cualquier cambio a x es local al método. a nunca se toca.

VISUALIZACIÓN

a
5
───> copia
x (copia)
5

x cambia a 99...

...pero a sigue siendo 5  


CASO 02 — EL TRICKY

Objetos: La Referencia se Copia

Ambas variables apuntan al MISMO objeto en el heap

void modificar(int[] arr) {
  arr[0] = 99;
  // arr apunta al MISMO objeto
}

int[] nums = {1,2,3};
modificar(nums);
printf("%d\n", nums[0]); // 99!

POR QUÉ

arr recibe una COPIA de la referencia (la dirección 0x4A2F). Ambas apuntan al mismo array en el heap. Modificar arr[0] modifica el objeto real, y nums lo ve.

DIAGRAMA DE MEMORIA

STACK
nums → 0x4A2F
arr (copia) → 0x4A2F
HEAP
0x4A2F
[ 1, 2, 3 ]
arr[0] = 99 → nums[0] == 99

CASO 03 — LA PRUEBA

Reasignar la Referencia

Esto prueba QUE C copia los punteros por valor

void reasignar(int[] arr) {
  arr = new int[]{99,99,99};
  // arr ahora apunta a OTRO objeto
  // nums NO se entera
}

int[] nums = {1,2,3};
reasignar(nums);
printf("%d\n", nums[0]); // 1 ← no cambió

LA CLAVE

arr = new int[]... solo cambia a dónde apunta la COPIA local. La referencia original nums en el Stack del llamador no se modifica. Sigue apuntando al objeto original. Esta es la prueba de que en C los punteros también se pasan POR VALOR.

DIAGRAMA

STACK
nums → 0x4A2F
arr (copia) → 0x4A2F
HEAP
0x4A2F
[1, 2, 3]
← nums apunta acá
0x9E3C
[99,99,99]
← arr reasignado
nums[0] sigue siendo 1

CASO 04

Clase Persona

Modificar campos vs reasignar el objeto

✓ Modifica el campo

class Persona {
  char nombre[20];
}

void cambiarNombre(Persona p) {
  p.nombre = "Nuevo";
}

Persona ana = new Persona();
ana.nombre = "Ana";
cambiarNombre(ana);
// ana.nombre == "Nuevo" ✓

RESULTADO

p apunta al mismo objeto que ana. Modificar p.nombre cambia el objeto compartido.

✗ Reasigna la referencia

void reemplazar(Persona p) {
  p = new Persona();
  p.nombre = "Otro";
  // p apunta a un objeto nuevo
}

Persona ana = new Persona();
ana.nombre = "Ana";
reemplazar(ana);
// ana.nombre == "Ana" (sin cambios)

RESULTADO

La copia local p apunta a otro objeto. ana sigue apuntando al original.


CASO ESPECIAL

Strings en C: arreglos mutables

En C, un arreglo de char se puede modificar carácter por carácter si tenés memoria escribible.

void cambiar(char s[]) {
  s[0] = 'Z';
  // modifica el arreglo original
}

char nombre[] = "Ana";
cambiar(nombre);
printf("%s\n", nombre); // "Zna"

INMUTABILIDAD

char[] en C es mutable si la memoria es escribible. s[0] = 'Z' modifica el mismo arreglo recibido. La función escribe directamente sobre la misma memoria compartida.

ARREGLO DE CHARS

Los literales suelen ser de solo lectura; si querés mutarlos, usá un arreglo de chars propio. Usá strcmp() para comparar contenido, no compares direcciones si querés saber si dicen lo mismo.

QUÉ PASA EN MEMORIA

STACK
nombre → 0xA1B2  ✓
s (copia) → 0xC3D4 (new)
HEAP
0xA1B2 (buffer char[])
"Ana"
original, inmutable
0xC3D4
"Nuevo"
objeto nuevo
nombre sigue siendo "Ana"

SNAPSHOT

Diagrama de Memoria Completo

Todo junto. Primitivos y referencias en acción.

STACK
int edad 25
double sueldo 3500.0
boolean activo true
char nombre[] → 0xA1B2
int[] scores → 0x4A2F
Persona p → 0x8C1D
primitivos: valor directo   objetos: dirección
HEAP
0xA1B2
char[]
"Ana"
0x4A2F
int[]
[90, 85, 78]
0x8C1D
Persona
nombre: → 0xA1B2
edad: 25

Los objetos no viven en el Stack.
Solo la dirección para encontrarlos.

NOTA SOBRE PRIMITIVOS

Los primitivos (int, double, boolean, char, long, float, short, byte) son los ÚNICOS que viven directamente en el Stack. Todo lo demás — incluyendo buffers dinámicos, arrays reservados en heap y structs gestionados con punteros — vive en el Heap.


SÍNTESIS

Reglas de Oro

Grabátelas. Son inamovibles.

01

C SIEMPRE pasa por valor

Sin excepciones. Los punteros no cambian esta regla: solo copian direcciones.

02

Para primitivos: se copia el VALOR

El método recibe una copia. Cambiarla no afecta al original.

03

Para objetos: se copia la REFERENCIA

La copia contiene la misma dirección de memoria. Ambas apuntan al mismo objeto.

04

Modificar el objeto a través de la copia: SÍ afecta al original

arr[0] = 99, p.nombre = "X" — ambas referencias ven el cambio.

05

Reasignar la referencia copiada: NO afecta al original

arr = new int[]..., p = new Persona() — solo mueve la copia local.


ESTADO: SESION_COMPLETADA

SESION FINALIZADA

Ahora sabés la diferencia entre paso por valor y uso de punteros en C.
No la vayas a confundir con "paso por referencia" de otros lenguajes.

PRIMITIVOS

int, double, boolean, char, long, float, short, byte

→ copia del valor

OBJETOS

punteros, arrays, structs

→ copia de la referencia

FIN DE SESION // DATOS PROCESADOS CORRECTAMENTE

← VOLVER A PRESENTACIONES