En mis programas en C, a menudo necesito una forma de hacer una representación en cadena de mis ADT. Incluso si no necesito imprimir la cadena en la pantalla de ninguna manera, es bueno tener ese método para la depuración. Entonces, este tipo de función a menudo surge.
char * mytype_to_string( const mytype_t *t );
Realmente me doy cuenta de que tengo (al menos) tres opciones aquí para manejar la memoria para que la cadena regrese.
Alternativa 1: Almacenar la cadena de retorno en una matriz de caracteres estáticos en la función. No necesito pensar mucho, excepto que la cadena se sobrescribe en cada llamada. Lo cual puede ser un problema en algunas ocasiones.
Alternativa 2: Asignar la cadena en el montón con malloc dentro de la función. Realmente ordenado ya que entonces no necesitaré pensar en el tamaño de un búfer o la sobrescritura. Sin embargo, tengo que recordar liberar () la cadena cuando termine, y luego también necesito asignar una variable temporal para que pueda liberar. y luego la asignación del montón es realmente mucho más lenta que la asignación de la pila, por lo tanto, sea un cuello de botella si esto se repite en un bucle.
Alternativa 3: Pase el puntero a un búfer y deje que la persona que llama asigne ese búfer. Me gusta:
char * mytype_to_string( const mytype_t *mt, char *buf, size_t buflen );
Esto trae más esfuerzo a la persona que llama. También noto que esta alternativa me da otra opción en el orden de los argumentos. ¿Qué argumento debería tener primero y último? (en realidad seis posibilidades)
Entonces, ¿cuál debería preferir? ¿Nada porque? ¿Existe algún tipo de estándar no escrito entre los desarrolladores de C?
fuente
sysctlbyname
en OS X e iOSRespuestas:
Los métodos que más he visto son 2 y 3.
El buffer provisto por el usuario es realmente bastante simple de usar:
Aunque la mayoría de las implementaciones devolverán la cantidad de búfer utilizado.
La opción 2 será más lenta y peligrosa cuando se usan bibliotecas vinculadas dinámicamente donde pueden usar diferentes tiempos de ejecución (y diferentes montones). Por lo tanto, no puede liberar lo que ha sido mal colocado en otra biblioteca. Esto entonces requiere una
free_string(char*)
función para tratarlo.fuente
printf("MyType: %s\n", mytype_to_string( mt, buf, sizeof(buf));
y, por lo tanto, no me gustaría devolver la longitud utilizada, sino el puntero a la cadena. El comentario dinámico de la biblioteca es realmente importante.sizeof(buffer) - 1
para satisfacer al\0
terminador?snprintf
uso.Idea de diseño adicional para el n. ° 3
Cuando sea posible, también proporcione el tamaño máximo necesario para
mytype
el mismo archivo .h quemytype_to_string()
.Ahora el usuario puede codificar en consecuencia.
Orden
El tamaño de las matrices, cuando primero, permite los tipos de VLA.
No es tan importante con una sola dimensión, pero es útil con 2 o más.
Recuerdo que leer tener el tamaño primero es un idioma preferido en la próxima C. Necesito encontrar esa referencia ...
fuente
Como una adición a la excelente respuesta de @ ratchetfreak, señalaría que la alternativa # 3 sigue un paradigma / patrón similar al de las funciones estándar de la biblioteca C.
Por ejemplo,
strncpy
.Seguir el mismo paradigma ayudaría a reducir la carga cognitiva para los nuevos desarrolladores (o incluso su futuro yo) cuando necesiten usar su función.
La única diferencia con lo que tiene en su publicación sería que el
destination
argumento en las bibliotecas C tiende a aparecer primero en la lista de argumentos. Entonces:fuente
Además del hecho de que lo que está proponiendo hacer es un mal olor a código, la alternativa 3 me suena mejor. También pienso como @ gnasher729 que estás usando el idioma incorrecto.
fuente
Para ser honesto, es posible que desee cambiar a un idioma diferente donde devolver una cadena no sea una operación compleja, que requiera mucho trabajo y que sea propensa a errores.
Podría considerar C ++ u Objective-C donde podría dejar el 99% de su código sin cambios.
fuente