funciones que devuelven un puntero de caracteres

6 minutos de lectura

Encontré muchas funciones que devolvían punteros de caracteres en una aplicación heredada. Algunos de ellos devuelven punteros a matrices de caracteres locales. Parece estar causando bloqueos después de varias invocaciones (¡no inmediatamente!). Consulte el uso a continuación.

char *f1(){
  char buff[20];
  char *ptr;

  ----
  ----
  ptr=buff;
 return ptr;
}

---
---

f2(f1());

f1() devuelve una variable local de puntero y luego la pasa a otra función. Obtuve el bloqueo directamente cuando se compila usando el modo _DEBUG en MS DEV. Pero en el modo de liberación, no provoca un bloqueo inmediato, pero puede ocurrir después de realizar muchas de esas llamadas.

Cuando modifiqué el uso como se muestra a continuación, funciona sin problemas. ¿Es seguro el siguiente uso?

strcpy(arr,f1()); /* arr is fixed char array*/
f2(arr);

No, es un comportamiento indefinido. Simplemente funciona en su caso, pero puede dejar de funcionar en cualquier momento.

  • Y la razón por la que funciona es que en el x86, la matriz de mejora se completa desde las direcciones más bajas a las más altas y probablemente no se verá afectada por el uso de la pila de strcpy. No lo haga de todos modos: el uso de la pila puede variar debido a eventos asincrónicos como señales e interrupciones.

    –Richard Pennington

    26 de febrero de 2010 a las 12:57

No, eso no es seguro. Simplemente llamar a strcpy puede modificar la pila lo suficiente como para causar problemas más adelante porque la dirección de retorno y los parámetros pueden sobrescribir la matriz.

las soluciones malloc son interesantes, excepto que la memoria debería estar libre después del uso. (fuera de la función). De lo contrario, habría una pérdida de memoria.

La función f1 devuelve un (buff) temporal que se libera cuando la función regresa. Debe usar malloc() dentro de la función.

Nunca devuelva un puntero a la variable local. Puede funcionar en algunas situaciones, pero en general tendrás muchos problemas con él. En lugar de:

  • documente su función que devuelve un puntero a la memoria asignada y que la persona que llama debe liberar el búfer devuelto
  • agregue un búfer y un argumento de tamaño, y complete el búfer (esto es lo que generalmente se hace en la API de Win32)
  • si usa C++, use std::string

  • Creo que dos soluciones para esto. 1. use malloc dentro de f1(). En este caso, todas las personas que llamen deberían liberar memoria correctamente después de su uso. Deben evitarse usos como f2(f1()), porque no vuelve a ejecutar el identificador para liberar memoria. 2. pasar un ptr asignado a la función, para que f1() pueda copiar el texto en él. los usos existentes como f2(f1()) tampoco son posibles en este caso. ¿Qué solución prefieres? De todos modos, requiere muchos cambios en la aplicación existente. porque tales patrones existen en varios lugares. algunos de ellos asignan memoria, pero no la liberan. Existen usos como f1(f2(f3())). 2.

    – cobp

    26 de febrero de 2010 a las 13:06

  • Si por “nunca” quiere decir “a menos que la variable local sea estática”, entonces estoy de acuerdo.

    – William Pursell

    26 de febrero de 2010 a las 13:53

  • @William, si la variable local es estática, funcionará (al menos durante más tiempo). Pero esto no significa que sea una buena práctica. Si la función se vuelve a ejecutar antes de realizar una copia personal del búfer devuelto, seguirá teniendo problemas. Similar para aplicaciones de subprocesos múltiples (aunque podría usar variables Thread-Local-Storage para eso).

    – Patricio

    26 de febrero de 2010 a las 14:44

funciones que devuelven un puntero de caracteres
leonel

¿Es seguro el siguiente uso?

No.

Si su función devuelve un puntero a algo, asegúrese de que asigne un área de memoria y le devuelva el puntero:

char *safe_f1(void) {
    char *ptr;
    ptr = (char *) malloc(20 * sizeof(char));
    ...
    return ptr;
}

  • Creo que dos soluciones para esto. 1. use malloc dentro de f1(). En este caso, todas las personas que llamen deberían liberar memoria correctamente después de su uso. Deben evitarse usos como f2(f1()), porque no vuelve a ejecutar el identificador para liberar memoria. 2. pasar un ptr asignado a la función, para que f1() pueda copiar el texto en él. los usos existentes como f2(f1()) tampoco son posibles en este caso. ¿Qué solución prefieres? De todos modos, requiere muchos cambios en la aplicación existente. porque tales patrones existen en varios lugares. algunos de ellos asignan memoria, pero no la liberan. Existen usos como f1(f2(f3())). 2.

    – cobp

    26 de febrero de 2010 a las 13:06

  • Si por “nunca” quiere decir “a menos que la variable local sea estática”, entonces estoy de acuerdo.

    – William Pursell

    26 de febrero de 2010 a las 13:53

  • @William, si la variable local es estática, funcionará (al menos durante más tiempo). Pero esto no significa que sea una buena práctica. Si la función se vuelve a ejecutar antes de realizar una copia personal del búfer devuelto, seguirá teniendo problemas. Similar para aplicaciones de subprocesos múltiples (aunque podría usar variables Thread-Local-Storage para eso).

    – Patricio

    26 de febrero de 2010 a las 14:44

No es seguro. La razón es simple:

Cualquier variable en una función se asignará en la pila cuya memoria se libera después de que la función regrese. El hecho de que se libere la memoria no significa que se cambie su contenido.

Esto significa que el contenido de la memoria que puso en la variable char buff[20] todavía está en el buff o ptr (ya que ptr=buff) posición de memoria. Cada vez que llame a otra función (o ejecute otro bloque), sus variables de función/bloque también irán a la pila, creando la posibilidad de cambiar el contenido de la memoria para cuya posición ptr puntos a.

En el strcpy ejemplo que escribiste, tuviste la suerte de que las variables de la función strcpy no se colocaron en la pila en una posición que estaba dentro del antiguo buff formación. Esta es la razón por la que tuvo la impresión de que era seguro.

La conclusión es que no hay forma de garantizar que el contenido de la memoria liberada de la pila no cambie entre dos llamadas de función.

La solución es usar malloc porque malloc no asigna memoria en la pila sino en el montón. La memoria del montón no se desasigna a menos que elija hacerlo (mediante una llamada gratuita).

Este enfoque garantizaría que la memoria apuntada por ptr es seguro para ser utilizado por cualquier otra función.

El inconveniente de esta solución es intrínseco: una vez que la memoria no se desasigna a menos que lo haga programáticamente, si olvida liberar esta memoria y pierde el contenido de ptr, esta memoria estará allí, asignada para su programa pero nunca alcanzable, perdida mientras su programa se ejecute. Este recuerdo se convertirá en una fuga de memoria 🙂

Esta es una de las razones por las que algunos idiomas tienen recolectores de basura… pero esta es otra historia 🙂

PD.: Creo que es seguro (si su programa es de un solo hilo), aunque no recomiendo hacer algo así:

{
  char safe_buffer[20];
  char *unsafe_ptr;
  int i;
  unsafe_ptr = f1();
  /*Copy the buffer without calling any function
    not to change the stack content
  */
  for(i=0;i<20 && *(unsafe_ptr + i) != 0;i++)
  {
    *(safe_buffer + i) = *(unsafe_ptr + i);
  }
  *(safe_buffer + i) = 0;
  f2(safe_buffer);
}

¿Ha sido útil esta solució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
Privacidad