malloc, free, append
This commit is contained in:
@@ -17,6 +17,20 @@ Los literales como `"1.0.0"` viven en **memoria de solo lectura**. Si
|
||||
intentas escribir en ellos, el comportamiento es indefinido; en la práctica,
|
||||
el programa se cierra.
|
||||
|
||||
## `'A'` frente a `"A"`
|
||||
|
||||
| Literal | Qué es | Tipo | Ocupa |
|
||||
|---|---|---|---|
|
||||
| `'A'` (comillas simples) | Un **carácter**: el número 65 | `int` (cabe en un `uint8_t`) | 1 byte guardado en un `uint8_t` |
|
||||
| `"A"` (comillas dobles) | Una **cadena**: los bytes `'A'` y `'\0'` en memoria de solo lectura | `char *`: un puntero a esos bytes | 2 bytes, más el puntero |
|
||||
|
||||
**En Go** es la misma diferencia que entre `'A'` (un `rune`) y `"A"` (un
|
||||
`string`).
|
||||
|
||||
Pasar `"A"` donde se espera un byte es pasar **una dirección** donde se
|
||||
espera **un número**. gcc lo detecta: `makes integer from pointer without a
|
||||
cast` o `differ in signedness`.
|
||||
|
||||
## `const char *`
|
||||
|
||||
Una función que "devuelve una cadena" en realidad devuelve **la dirección de
|
||||
@@ -44,6 +58,87 @@ Si `p` apunta a `"1.0.0"`, `*p` es solo el `'1'`.
|
||||
|
||||
**En Go**: es lo mismo que `var p *T` frente a `*p`.
|
||||
|
||||
### Regla práctica: ¿quiero la dirección o la cosa?
|
||||
|
||||
Un puntero es un papel con la dirección de una casa:
|
||||
|
||||
- `buffer` es **el papel**, la dirección.
|
||||
- `*buffer` es **la casa**, el struct que está en esa dirección.
|
||||
- `buffer->len` es "ve a la casa y coge `len`". La flecha ya va a la casa,
|
||||
así que no lleva `*`. Equivale a `(*buffer).len`.
|
||||
- `&x` es lo contrario de `*`: "dame la dirección de `x`".
|
||||
|
||||
Antes de escribir, pregúntate: **¿esta función o esta operación necesita la
|
||||
dirección o la cosa?**
|
||||
|
||||
Aplicado a `buffer.c`:
|
||||
|
||||
| Código | ¿`*`? | Por qué |
|
||||
|---|---|---|
|
||||
| `escpos_buffer *buffer = ...` | Sí | Declaración: dice que `buffer` **es** un puntero |
|
||||
| `malloc(sizeof *buffer)` | Sí | Quiero el tamaño **de la casa** (el struct, 24 bytes). `sizeof buffer` sería el tamaño **del papel** (el puntero, 8 bytes) |
|
||||
| `if (buffer == NULL)` | No | Compruebo si el papel tiene una dirección válida |
|
||||
| `buffer->data = NULL` | No | `->` ya va a la casa |
|
||||
| `return buffer;` | No | Devuelvo el papel; la función devuelve `escpos_buffer *` |
|
||||
| `free(buffer)` | No | `free` necesita la dirección del bloque |
|
||||
|
||||
**En Go** las reglas son las mismas, pero Go oculta casi todos los `*`:
|
||||
`p.X` desreferencia solo, y los métodos con receptor puntero también. Por eso en
|
||||
Go casi solo ves `*` en los tipos (`*Buffer`) y rara vez en expresiones
|
||||
(`*p = v`). En C la diferencia entre papel y casa siempre se escribe.
|
||||
|
||||
## Dónde va el `*`: siempre con el tipo
|
||||
|
||||
Los espacios alrededor del `*` dan igual. Estas tres líneas son idénticas:
|
||||
|
||||
```c
|
||||
escpos_buffer* escpos_buffer_new(void);
|
||||
escpos_buffer * escpos_buffer_new(void);
|
||||
escpos_buffer *escpos_buffer_new(void);
|
||||
```
|
||||
|
||||
En todas, el tipo de retorno es `escpos_buffer *` ("puntero a
|
||||
`escpos_buffer`"). El `*` **no** es de la función, es del tipo que devuelve.
|
||||
En Go se lee igual: `func New() *Buffer`.
|
||||
|
||||
La costumbre en C es pegarlo al nombre por esta trampa:
|
||||
|
||||
```c
|
||||
int *a, b; /* a es int *, pero b es int, NO un puntero */
|
||||
```
|
||||
|
||||
En una declaración múltiple, el `*` solo afecta al nombre que tiene al lado.
|
||||
Pegarlo al nombre lo deja a la vista. Mejor aún: una variable por línea.
|
||||
|
||||
## Nombres de parámetros en las declaraciones
|
||||
|
||||
En una **declaración** (el prototipo de la cabecera), el nombre del parámetro
|
||||
es opcional: `void escpos_buffer_free(escpos_buffer *);` es válido. El
|
||||
compilador solo necesita los tipos.
|
||||
|
||||
En la **definición** (en el `.c`) sí hace falta, porque el cuerpo lo usa.
|
||||
|
||||
Aun así, conviene ponerlo también en la cabecera: es documentación. Con
|
||||
`void copy(char *, const char *)` no sabes cuál es el destino;
|
||||
con `void copy(char *dst, const char *src)` sí.
|
||||
|
||||
**En Go** pasa lo mismo en los tipos función y en las interfaces:
|
||||
`func(int) error` es válido sin nombres.
|
||||
|
||||
## Paso por valor: un struct entero frente a un puntero
|
||||
|
||||
C copia siempre los argumentos, igual que Go. Si un parámetro es
|
||||
`escpos_buffer` (sin `*`), la función recibiría **una copia del struct
|
||||
entero**. Eso tiene dos problemas:
|
||||
|
||||
1. Con un tipo opaco ni siquiera compila: el struct está incompleto fuera de
|
||||
su `.c` y no se sabe cuánto copiar.
|
||||
2. Liberar una copia no sirve de nada: el bloque que reservó `malloc` es el
|
||||
original.
|
||||
|
||||
Por eso las funciones de un tipo opaco reciben siempre un **puntero**. Es lo
|
||||
mismo que los métodos con receptor puntero de Go (`func (b *Buffer) Free()`).
|
||||
|
||||
## `printf` y el formato
|
||||
|
||||
El primer argumento de `printf` es el **formato**. Nunca pases ahí una cadena
|
||||
|
||||
Reference in New Issue
Block a user