# Punteros y cadenas ## En C no hay tipo `string` En Go, `string` es un tipo propio: por dentro guarda un puntero y una longitud, y lo manejas como un valor. En C, una cadena es solo una secuencia de bytes en memoria terminada en `'\0'`. La longitud **no se guarda en ningún sitio**: para saberla hay que recorrer la cadena hasta encontrar el `'\0'`. ``` "1.0.0" → '1' '.' '0' '.' '0' '\0' (6 bytes, no 5) ``` 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 su primer byte**: ```c const char *version = escpos_version(); ``` - `char`: en esa dirección hay caracteres. - `*`: `version` es un puntero; guarda una dirección, no un carácter. - `const`: a través de este puntero no se pueden modificar los caracteres. `char` es un **entero de 1 byte**. Si guardas un puntero en un `char`, pierdes la dirección. Por eso el error dice `makes pointer from integer`. ## Los dos significados de `*` | Dónde aparece | Qué significa | |---|---| | En una **declaración**: `const char *p` | "`p` es un puntero" | | En una **expresión**: `*p` | "el valor que hay en la dirección de `p`" (desreferenciar) | 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 variable: ```c printf(s); /* mal */ printf("%s\n", s); /* bien */ ``` Si `s` contuviera un `%`, `printf` iría a leer argumentos que no existen, lo que es comportamiento indefinido y un agujero de seguridad clásico (*format string*). `%s` significa "imprime los bytes desde esta dirección hasta el `'\0'`".