Aunque no se recomienda, esto es seguro, el alcance de una variable estática permanece vivo incluso cuando finaliza el alcance de la función. Esta función no es muy segura para subprocesos en absoluto. Una mejor función le haría pasar un char* buffer y un maxsize Para el GetString() Función para llenar.
En particular, esta función no se considera una función reentrante porque las funciones reentrantes no deben, entre otras cosas, devolver la dirección a datos estáticos (globales) no constantes. Ver funciones reentrantes.
Segundo ejemplo: Completamente inseguro
char* const GetString()
{
return "Test";
}
Esto sería seguro si hicieras un const char *. Lo que diste no es seguro. El motivo es que los literales de cadena se pueden almacenar en un segmento de memoria de solo lectura y permitir que se modifiquen generará resultados indefinidos.
char* const (puntero const) significa que no puede cambiar la dirección a la que apunta el puntero. const char * (puntero a const) significa que no puede cambiar los elementos a los que apunta este puntero.
Conclusión:
Debes considerar:
1) Si tiene acceso al código, modifique el GetString tomar un parámetro de un char* buffer para llenar y un maxsize usar.
2) Si no tiene acceso al código, pero debe llamarlo, envuelva este método en otra función que esté protegida por un mutex. El nuevo método es como se describe en 1.
extraño
static Las variables (en una función) son como variables globales con ámbito. En general, deben evitarse (al igual que las variables globales, causan problemas de reingreso), pero a veces son útiles (algunas funciones de biblioteca estándar las usan). Puede devolver punteros a variables globales, por lo que puede devolver punteros a static variables también.
“En general, deben evitarse” puede ser demasiado fuerte, pero es seguro que debe ser consciente de los riesgos y limitaciones. +1 para aclarar por qué está bien.
– dmckee — gatito ex-moderador
17 de enero de 2009 a las 18:15
Estoy de acuerdo con dmckee, los problemas de reingreso se deben al diseño de estática que está vivo en las llamadas a funciones. no es mal comportamiento. pero debe ser consciente del riesgo, de hecho.
– Johannes Schaub – litb
17 de enero de 2009 a las 18:18
Depende de lo que entiendas por seguro. Hay un par de problemas que puedo ver de inmediato:
Has devuelto un char * const, que permitirá a las personas que llaman cambiar la cadena en esta ubicación. Potencial desbordamiento del búfer. ¿O quisiste decir un const char *?
Es posible que tenga un problema con la reentrada o con la concurrencia.
int a = do_something();
int b = do_something_else();
if (a != 0 && b != 0)
{
fprintf(stderr,
"do_something failed (%s) AND do_something_else failed (%s)\n",
format_error_message(a), format_error_message(b));
}
…¿Qué se va a imprimir?
Lo mismo para enhebrar.
jonathan leffler
Fundamentalmente, sí, es seguro en el sentido de que el valor durará indefinidamente porque es estático.
No es seguro en el sentido de que ha devuelto un puntero constante a datos variables, en lugar de un puntero variable a datos constantes. Es mejor si las funciones de llamada no pueden modificar los datos:
En el caso simple que se muestra, no es necesario preocuparse por los desbordamientos del búfer, aunque mi versión del código sí se preocupa y garantiza una terminación nula. Una alternativa sería usar la función TR24731 strcpy_s en lugar de:
Más importante aún, ambas variantes devuelven un puntero (variable) a datos constantes, por lo que el usuario no debe modificar la cadena y (probablemente) pisotear fuera del rango de la matriz. (Como @strager señala en los comentarios, devolver un const char * no es una garantía de que el usuario no intentará modificar los datos devueltos. Sin embargo, tienen que emitir el puntero devuelto para que no sea constante y luego modificar los datos; esto invoca un comportamiento indefinido y todo es posible en ese punto).
Una ventaja del retorno literal es que el compilador y el sistema operativo generalmente pueden hacer cumplir la promesa de no escribir. La cadena se colocará en el segmento de texto (código) del programa, y el sistema operativo generará una falla (violación de segmentación en Unix) si el usuario intenta modificar los datos a los que apunta el valor devuelto.
[At least one of the other answers notes that the code is not re-entrant; that is correct. The version returning the literal is re-entrant. If re-entrancy is important, the interface needs to be fixed so that the caller provides the space where the data is stored.]
Johannes Schaub – litb
Sí, es perfectamente seguro. La vida útil de la estática local es la de la ejecución completa del programa en C. Por lo tanto, puede devolverle un puntero, ya que la matriz estará activa incluso después de que la función regrese, y el puntero devuelto puede desreferenciarse válidamente.
Nuclear
Es muy útil, ya que puede usar la función directamente como parámetro printf. Pero, como se mencionó, varias llamadas a la función dentro de una sola llamada causarán un problema, porque la función usa el mismo almacenamiento y llamarla dos veces sobrescribirá la cadena devuelta. Pero probé este fragmento de código y parece funcionar: puede llamar de forma segura a una función, donde se usa givemestring en la mayoría de las veces MAX_CALLS y se comportará correctamente.
El único problema es la seguridad de subprocesos, pero esto se puede resolver con variables locales de subprocesos (palabra clave __thread de gcc)
jonathan leffler
Sí, esto se usa con frecuencia para devolver la parte de texto de alguna búsqueda, es decir, para traducir algún número de error en una cadena amigable para los humanos.
Es aconsejable hacer esto en los casos en los que:
fprintf(stderr, "Error was %s\n", my_string_to_error(error_code));
Si my_string_to_error() devolvió una cadena asignada, su programa se filtraría dado el uso (muy) común anterior de dicha función.
Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos.
Configurar y más información