Tengo el siguiente código:
char* get_address_string(PACKAGE* pkg){ char *c; sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]); return c; }El código funciona bien. Sin embargo, sé que esta no es la forma correcta de devolver una cadena en C. Recibo la advertencia "c se usa sin inicializar en esta función".
¿Cuál es la forma correcta de escribir esta función en C?
"La forma correcta de devolver una cadena en C" no es realmente posible. En C, una cadena es una matriz de caracteres (hasta el carácter nulo incluido) y las matrices, por sí mismas, no se pueden devolver desde una función.
Una función puede devolver punteros . Entonces, el método habitual de "devolver una cadena" es:
Devuelve un puntero. char *foo1(...) como char *strdup()
Pase un puntero a una matriz de caracteres y modifique su contenido. void foo2(char *,...) como int sprintf(char *dest, const char *format, ...)
Combina 1 y 2 char *foo3(char *, ...) como char *strcpy(char *dest, char *src)
Pase la dirección de un puntero y actualícelo. foo4(char **ptr) como ssize_t getline(char **lineptr, size_t *n, FILE *stream)
La clave es que la memoria asociada con el puntero debe ser válida después de que se complete la función. Devolver un puntero a la memoria no estática de una función es un comportamiento indefinido. Los métodos exitosos incluyen hacer que el código de llamada pase en el puntero, o que la función lo proporcione a través de la asignación de memoria del puntero a algún valor persistente como una variable global o una constante de cadena.
¿Cuál es la forma correcta de escribir esta función en C?
La práctica de diseño actual fomenta funciones como la n.° 2 y la n.° 3 anteriores para que también suministren un size_t size para que la función conozca las limitaciones de la memoria disponible.
char *foo2(char *s, size_t size, const pkg_T *pkg) { int result = snprintf(s, size, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]); // encoding error or not enough room if (result < 0 || result >= size) return NULL; return s; } Otro método asignaría memoria (aunque estoy a favor de lo anterior). Esto obliga al código de llamada a free() la memoria.
#define UINT_MAX_WIDTH (sizeof(unsigned)*CHAR_BIT/3 + 3) char *foo2alloc(char *s, size_t size, const pkg_T *pkg) { char buf[(UINT_MAX_WIDTH+3)*6 + 1]; int result = snprintf(buf, sizeof buf, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]); // encoding error or not enough room if (result < 0 || result >= size) return NULL; return strdup(buf); }c es un puntero, pero no se asigna memoria. El valor de retorno está bien, así es como se puede hacer en C.
Pero necesita asignar memoria.
Dado que c no está inicializado, sprintf escribe en una ubicación de memoria desconocida, lo que conduce a un comportamiento no especificado. Podría bloquearse inmediatamente, podría no bloquearse en absoluto o podría bloquearse en alguna línea de código completamente no relacionada.
Debe inicializar el puntero asignándole memoria con malloc .
char* get_address_string(PACKAGE* pkg){ char *c = malloc(20); // enough room for output as 00:11:22:33:44:55 plus null terminator if (c == null) { perror("malloc failed"); exit(1); } sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]); return c; }Tenga en cuenta que, aunque sabe de antemano cuánta memoria necesita, no puede reservarla en el momento de la compilación a través de una matriz. Esto está mal:
char* get_address_string(PACKAGE* pkg){ char c[20]; // allocated on the stack, contents unspecified on return sprintf(c, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]); return c; }Como es esto:
char* get_address_string(PACKAGE* pkg){ char c[20]; // allocated on the stack, contents unspecified on return char *p = c; sprintf(p, "%02x:%02x:%02x:%02x:%02x:%02x", pkg->address[0], pkg->address[1], pkg->address[2], pkg->address[3], pkg->address[4], pkg->address[5]); return p; } Dado que c se asigna en la pila, cuando get_address_string devuelve los contenidos no están especificados, lo que lleva nuevamente a un comportamiento no especificado.