progress
This commit is contained in:
@@ -196,6 +196,70 @@ uint8_t init[] = { 0x1B, 0x40 }; /* ESC @ */
|
||||
**En Go** `[2]byte{0x1B, 0x40}` es un array con su longitud dentro del tipo.
|
||||
Se pasa por copia, y para pasarlo sin copiar se usa un slice: `init[:]`.
|
||||
|
||||
## Aritmética de punteros: `[]` también lleva un `*` escondido
|
||||
|
||||
Con dibujos interactivos y preguntas: [02-pointers-visual.html](02-pointers-visual.html).
|
||||
|
||||
Sumar un entero a un puntero da **otra dirección**: `p + i` avanza `i`
|
||||
**elementos**, es decir, `i * sizeof *p` bytes. No va a la casa. Solo calcula
|
||||
qué casa es.
|
||||
|
||||
`p[i]` es una abreviatura de `*(p + i)`: calcula la dirección y va a ella.
|
||||
Igual que `->`, los corchetes llevan el `*` dentro.
|
||||
|
||||
La cadena completa con el buffer:
|
||||
|
||||
| Escribes | Equivale a | Tipo | Qué es |
|
||||
|---|---|---|---|
|
||||
| `buffer` | | `escpos_buffer *` | El papel (8 bytes) |
|
||||
| `*buffer` | | `struct escpos_buffer` | La casa (24 bytes) |
|
||||
| `buffer->data` | `(*buffer).data` | `uint8_t *` | Un papel guardado dentro de la casa, con la dirección del bloque de bytes |
|
||||
| `buffer->data + i` | `&buffer->data[i]` | `uint8_t *` | La dirección de la casilla `i` |
|
||||
| `buffer->data[i]` | `*(buffer->data + i)` | `uint8_t` | El byte de la casilla `i` |
|
||||
|
||||
Para saber si una expresión lleva `*`, cuenta: `*`, `->` y `[]` desreferencian
|
||||
una vez cada uno, y `&` quita una desreferencia.
|
||||
|
||||
### Qué viaja cuando llamas a una función
|
||||
|
||||
En C, a una función le llega siempre **una copia** del valor que le pasas.
|
||||
|
||||
- `f(buffer)`: viaja una copia del papel, 8 bytes. La función llega a la misma
|
||||
casa, así que lo que cambie en ella lo ves tú.
|
||||
- `f(*buffer)`: viaja una copia de la casa, 24 bytes. Lo que cambie la función
|
||||
se queda en su copia.
|
||||
- `f(data)`, con `data` un array de 300 bytes: el array decae a la dirección
|
||||
de su primera casilla. Viaja un papel de 8 bytes, no los 300 bytes. Dentro de
|
||||
la función, `sizeof *data` es 1, el tamaño de una casilla.
|
||||
|
||||
### Restar direcciones
|
||||
|
||||
Las direcciones se escriben en hexadecimal, en base 16: cada posición vale 16
|
||||
veces la de su derecha, y `a`–`f` valen 10–15.
|
||||
|
||||
| Hex | Decimal |
|
||||
|---|---|
|
||||
| `0x08` | 8 |
|
||||
| `0x10` | 16 |
|
||||
| `0x18` | 24 |
|
||||
| `0x20` | 32 |
|
||||
| `0x40` | 64 |
|
||||
| `0x80` | 128 |
|
||||
| `0x100` | 256 |
|
||||
|
||||
`0x20` es 2 × 16 + 0 = 32. Para no hacer la cuenta a mano:
|
||||
|
||||
- En gdb: `p 0xd210 - 0xd1f0` da `32`, y `p/x 32` da `0x20`.
|
||||
- En bash: `echo $((0xd210 - 0xd1f0))`.
|
||||
|
||||
Si restas dos **punteros** del mismo tipo (`p &b - &a`), el resultado no son
|
||||
bytes: son **elementos**, igual que en la suma. Dos `escpos_buffer *` separados
|
||||
32 bytes dan `4`, porque cada uno ocupa 8.
|
||||
|
||||
**En Go** pasa lo mismo, todo se copia: un `*Buffer` copia el puntero y un
|
||||
slice copia la cabecera (puntero, len y cap). La diferencia es que en el slice
|
||||
viaja la longitud, y en C tienes que pasarla tú.
|
||||
|
||||
## `printf` y el formato
|
||||
|
||||
El primer argumento de `printf` es el **formato**. Nunca pases ahí una cadena
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -52,6 +52,30 @@ propios gestores sobre `mmap` y pueden no tener `[heap]` en absoluto.
|
||||
Comprobado con el proceso principal de VS Code: tiene `[stack]` pero no
|
||||
`[heap]`.
|
||||
|
||||
### Ejemplo: dónde vive cada parte del buffer
|
||||
|
||||
Medido con gdb en `hello` (Fedora, 2026-10-06), en el punto de parada de
|
||||
`main`:
|
||||
|
||||
| Qué | Expresión en gdb | Dirección | Zona |
|
||||
|---|---|---|---|
|
||||
| El papel: la variable local `buffer` de `main` | `p &buffer` | `0x7fffffffd520` | `[stack]` |
|
||||
| La casa: el struct de 24 bytes | `p buffer` | `0x4052b0` | `[heap]` |
|
||||
| Los bytes: el bloque de 128 | `p buffer->data` | `0x4052d0` | `[heap]` |
|
||||
|
||||
- Son **tres sitios**: el puntero está en la pila, y los dos bloques de
|
||||
`malloc`/`realloc` están en el montón, separados entre sí.
|
||||
- Los campos del struct sí van juntos y en orden: `&buffer->len` es
|
||||
`0x4052b8`, 8 bytes después de `data`.
|
||||
- El struct y los datos están a 32 bytes y no a 24: `malloc` redondea el tamaño
|
||||
y guarda delante de cada bloque una cabecera con información para `free`.
|
||||
- `info proc mappings` en gdb (o `/proc/<pid>/maps`) dice a qué zona pertenece
|
||||
cada dirección.
|
||||
|
||||
**En Go** la variable `b := &Buffer{}` también puede vivir en la pila y el
|
||||
struct en el montón, pero lo decide el compilador (*escape analysis*) y no lo
|
||||
ves.
|
||||
|
||||
### Memoria virtual frente a memoria real
|
||||
|
||||
`/proc/<pid>/status` muestra dos cifras:
|
||||
@@ -243,6 +267,39 @@ Comparado con el `copy(dst, src)` de Go:
|
||||
- Go copia `min(len(dst), len(src))` y devuelve cuántos copió: nunca se sale.
|
||||
`memcpy` no puede comprobar nada porque no conoce los tamaños.
|
||||
|
||||
### `restrict`: la promesa de que no se solapan
|
||||
|
||||
Desde C99 la firma real lleva `restrict`:
|
||||
|
||||
```c
|
||||
void *memcpy(void *restrict dst, const void *restrict src, size_t n);
|
||||
```
|
||||
|
||||
`restrict` en un puntero es una **promesa del programador al compilador**:
|
||||
mientras dura la función, a la memoria que se toca por ese puntero no se accede
|
||||
por ningún otro. Aquí quiere decir que `dst` y `src` no se solapan.
|
||||
|
||||
- **Para qué sirve:** sabiendo que no se solapan, el compilador puede copiar en
|
||||
bloques de 16 o 32 bytes, reordenar y no volver a leer `src` después de cada
|
||||
escritura en `dst`. Sin la promesa, tendría que suponer que escribir en `dst`
|
||||
puede cambiar `src`.
|
||||
- **Quién lo comprueba:** nadie. Si rompes la promesa (con bloques que se
|
||||
solapan), es comportamiento indefinido. Para bloques solapados está `memmove`,
|
||||
que no lleva `restrict`.
|
||||
- **Para quien llama no cambia nada:** se llama igual que en C89.
|
||||
|
||||
`man 3 memcpy` en Fedora lo escribe como `void dest[restrict .n]`. Es una
|
||||
notación de la página de manual para decir que el bloque mide `n`. No es C
|
||||
válido.
|
||||
|
||||
Versión del estándar: el `Makefile` compila con `-std=c17`, así que
|
||||
`restrict` existe. Para comprobar qué estándar usa el compilador:
|
||||
`echo | gcc -std=c17 -dM -E - | grep __STDC_VERSION__` da `201710L` (C17). Sin
|
||||
`-std`, gcc 15 usa C23 (`202311L`).
|
||||
|
||||
**En Go** no existe. El compilador tiene que suponer lo peor cuando dos slices
|
||||
pueden compartir memoria, y `copy` funciona aunque se solapen, como `memmove`.
|
||||
|
||||
**Por qué usarlo en vez de un bucle:** glibc implementa `memcpy` en ensamblador
|
||||
optimizado, con instrucciones que copian 16 o 32 bytes de una vez (SIMD). A
|
||||
veces gcc incluso convierte un bucle de copia en una llamada a `memcpy`. Y
|
||||
|
||||
+5
-3
@@ -9,9 +9,11 @@ Ordenados por tema, en el orden en que se ven en el proyecto.
|
||||
flags, qué ve quien recibe los binarios, `nm` y errores del enlazador.
|
||||
2. [Punteros y cadenas](02-pointers-and-strings.md): cadenas terminadas en
|
||||
`'\0'`, `const char *`, los dos significados de `*`, dónde va el `*`,
|
||||
nombres de parámetros, paso por valor frente a puntero, un puntero no sabe
|
||||
nombres de parámetros, paso por valor frente a puntero, aritmética de
|
||||
punteros (`p + i`, `p[i]`) y qué viaja al llamar a una función, un puntero no sabe
|
||||
cuántos elementos hay, `const` en parámetros, arrays y el formato de
|
||||
`printf`.
|
||||
`printf`. Versión interactiva con dibujos:
|
||||
[02-pointers-visual.html](02-pointers-visual.html) (ábrela en el navegador).
|
||||
3. [La memoria de un proceso](03-process-memory.md): memoria virtual, fallos
|
||||
de página, modo usuario y modo núcleo, llamadas al sistema, cómo se lee la
|
||||
memoria de otro proceso, y por qué el peligro real está en la tuya.
|
||||
@@ -26,7 +28,7 @@ Ordenados por tema, en el orden en que se ven en el proyecto.
|
||||
detección de Windows/Linux.
|
||||
7. [Memoria dinámica](07-dynamic-memory.md): pila y montón, *escape
|
||||
analysis* de Go, `malloc`/`free`/`calloc`, `sizeof *p`, `realloc` y
|
||||
estrategia de crecimiento, `memcpy` y desbordamiento al calcular tamaños, fugas, liberar en caminos
|
||||
estrategia de crecimiento, `memcpy` y `restrict`, desbordamiento al calcular tamaños, fugas, liberar en caminos
|
||||
de error sin `defer` (`goto cleanup`), propiedad de
|
||||
los punteros y Valgrind.
|
||||
8. [Structs](08-structs.md): definir frente a declarar, tipos incompletos y
|
||||
|
||||
@@ -31,6 +31,7 @@ Si juntas dos flags de parada (`-E -S`), gana la que para antes.
|
||||
| `xxd fichero.bin` | Volcado hexadecimal (para revisar los bytes ESC/POS). |
|
||||
| `tail -c 5 fichero \| xxd` | Muestra los últimos bytes: sirve para ver si el fichero acaba en `0a` (`\n`). |
|
||||
| `man 3 memcpy` | Documentación de una función de la librería de C: cabecera, firma y comportamiento. `man 2` para llamadas al sistema. |
|
||||
| `echo \| gcc -std=c17 -dM -E - \| grep __STDC_VERSION__` | Qué versión del estándar de C usa el compilador: `201710L` es C17, `202311L` es C23. |
|
||||
| `echo $?` | Código de salida del último comando: `0` es éxito. |
|
||||
|
||||
## Memoria
|
||||
@@ -57,6 +58,8 @@ Si juntas dos flags de parada (`-E -S`), gana la que para antes.
|
||||
| `run` / `bt` / `print x` | Dentro de gdb: ejecutar, ver la pila de llamadas, ver una variable. |
|
||||
| `Ctrl+C` (dentro de gdb, con el programa en marcha) | Detiene el programa donde esté. Para encontrar bucles infinitos. |
|
||||
| `break f.c:42` / `next` / `step` / `continue` | Dentro de gdb: punto de parada, siguiente línea, entrar en la función, seguir. |
|
||||
| `p &x` / `p *p` / `p/x *p@n` | Dentro de gdb: dirección de `x`, lo que hay en la dirección de `p`, y `n` elementos seguidos desde `p` en hexadecimal. |
|
||||
| `info proc mappings` | Dentro de gdb: mapa de memoria del proceso (`[heap]`, `[stack]`, librerías). Dice en qué zona cae una dirección. |
|
||||
|
||||
### VS Code
|
||||
|
||||
|
||||
Reference in New Issue
Block a user