¿Cómo puedo asignar memoria y devolverla (a través de un parámetro puntero) a la función que llama?


Tengo algún código en un par de funciones diferentes que se ve algo como esto:

void someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (data);

  useData (data);

  return 0;
}

someFunction () y useData () se definen en módulos separados (*.archivos c).

El problema es que, mientras malloc funciona bien, y la memoria asignada es utilizable en someFunction, la misma memoria no está disponible una vez que la función ha regresado.

Se puede ver un ejemplo de ejecución del programa aquí, con una salida que muestra las diversas direcciones de memoria.

¿Puede alguien por favor explicar para mí lo que estoy haciendo mal aquí, y cómo puedo hacer que este código funcione?


EDITAR: Así que parece que necesito usar punteros dobles para hacer esto - ¿cómo voy a hacer lo mismo cuando realmente necesito usar punteros dobles? Así, por ejemplo, los datos son

int **data = NULL; //used for 2D array

¿Entonces necesito usar punteros triples en las llamadas a funciones?

Author: a_m0d, 2009-09-09

10 answers

Desea utilizar un puntero a puntero:

void someFunction (int **data) {
  *data = malloc (sizeof (int));
}

void useData (int *data) {
  printf ("%p", data);
}

int main () {
  int *data = NULL;

  someFunction (&data);

  useData (data);

  return 0;
}

¿Por qué? Bueno, quieres cambiar tu puntero data en la función principal. En C, si desea cambiar algo que se pasa como parámetro (y que ese cambio se muestre en la versión de la persona que llama), debe pasar un puntero a lo que quiera cambiar. En este caso, ese "algo que quieres cambiar" es un puntero so así que para poder cambiar ese puntero, tienes que usar un puntero a puntero...

Tenga en cuenta que en la parte superior de su problema principal, había otro error en el código: sizeof(data) le da el número de bytes necesarios para almacenar el puntero (4 bytes en un sistema operativo de 32 bits o 8 bytes en un sistema operativo de 64 bits), mientras que realmente desea el número de bytes necesarios para almacenar lo que el puntero apunta a (un int, es decir, 4 bytes en la mayoría de los sistemas operativos). Porque normalmente sizeof(int *)>=sizeof(int), esto probablemente no habría causado un problema, pero es algo que hay que tener en cuenta. He corregido esto en el código anterior.

Aquí hay algunas preguntas útiles sobre punteros a punteros:

¿Cómo funciona el puntero a punteros en C?

Usos para múltiples niveles de desreferencias de puntero?

 51
Author: Martin B,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-05-23 11:33:14

Un escollo común, especialmente si se movió de Java a C / C++

Recuerde que cuando pasa un puntero, es pasar por valor, es decir, el valor del puntero se copia. Es bueno para hacer cambios a los datos apuntados por el puntero, pero cualquier cambio en el puntero en sí es solo local, ya que es una copia!!

El truco es usar pasar el puntero por referencia ya que desea cambiarlo, es decir, malloc, etc.

* * pointer will > asustará a un programador noobie C;)

 8
Author: Sid Sarasvati,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-22 02:18:56

Debe pasar un puntero al puntero si desea modificarlo.

Ie. :

void someFunction (int **data) {
  *data = malloc (sizeof (int)*ARRAY_SIZE);
}

Editar : Agregado ARRAY_SIZE, en algún momento tienes que saber cuántos enteros quieres asignar.

 4
Author: Ben,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 09:56:15

Esto se debe a que los datos del puntero se pasan por valor a someFunction.

int *data = NULL;
//data is passed by value here.
someFunction (data); 
//the memory allocated inside someFunction  is not available.

Puntero a puntero o devolver el puntero asignado resolvería el problema.

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (data));
}


int*  someFunction (int *data) {
  data = (int *) malloc (sizeof (data));
return data;
}
 2
Author: aJ.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 08:31:35

SomeFunction() toma su parámetro como int*. Así que cuando lo llamas desde main (), se crea una copia del valor que pasaste. Lo que sea que esté modificando dentro de la función es esta copia y, por lo tanto, los cambios no se reflejarán fuera. Como otros sugirieron, puede usar int * * para que los cambios se reflejen en los datos. Otra forma de hacerlo es devolver int * desde someFunction ().

 2
Author: Naveen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 08:34:29

Aparte de usar la técnica doublepointer, si solo hay 1 retorno param necesario reescribir es como sigue:

 int *someFunction () {
   return (int *) malloc (sizeof (int *));
 }

Y úsalo:

 int *data = someFunction ();
 2
Author: Toad,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 08:35:03

Aquí está el patrón general para asignar memoria en una función y devolver el puntero a través del parámetro:

void myAllocator (T **p, size_t count)
{
  *p = malloc(sizeof **p * count);
}
...
void foo(void)
{
  T *p = NULL;
  myAllocator(&p, 100);
  ...
}

Otro método es hacer que el puntero sea el valor de retorno de la función (mi método preferido):

T *myAllocator (size_t count)
{
  T *p = malloc(sizeof *p * count);
  return p;
}
...
void foo(void)
{
  T *p = myAllocator(100);
  ...
}

Algunas notas sobre la gestión de la memoria:

  1. La mejor manera de evitar problemas con la gestión de la memoria es evitar la gestión de la memoria; no estropee la memoria dinámica a menos que realmente la necesite.
  2. No lanzar el resultado de malloc() a menos que esté utilizando una implementación anterior al estándar ANSI de 1989 o tenga la intención de compilar el código como C++. Si olvidó incluir stdlib.h o de lo contrario no tienen un prototipo para malloc() en el alcance, lanzar el valor devuelto suprimirá un valioso diagnóstico del compilador.
  3. Use el tamaño del objeto que se asigna en lugar del tamaño del tipo de datos (es decir, sizeof *p en lugar de sizeof (T)); esto le ahorrará algo de acidez si el tipo de datos tiene que cambiar (por ejemplo, de int a long o float a double). También hace que el código lea un poco mejor IMO.
  4. Aislar las funciones de gestión de memoria detrás de las funciones de asignación y desasignación de nivel superior; estas pueden manejar no solo la asignación, sino también la inicialización y los errores.
 1
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 16:24:44

Aquí está tratando de modificar el puntero, es decir, de "data == Null" a "data == 0xabcd"alguna otra memoria que haya asignado. Para modificar los datos que necesita, pase la dirección de los datos, es decir, & datos.

void someFunction (int **data) {
  *data = (int *) malloc (sizeof (int));
}
 0
Author: Learner,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 08:46:12

Respondiendo a su pregunta adicional que editó en:

'*' denota un puntero a algo. Así '**' sería un puntero a un puntero a algo, '***' un puntero a un puntero a un puntero a algo, etc.

La interpretación habitual de 'int **data' (si data no es un parámetro de función) sería un puntero a la lista de matrices int (por ejemplo, 'int a [100][100]').

Así que primero necesitas asignar tus arrays int (estoy usando una llamada directa a malloc () por el bien de simplicidad):

data = (int**) malloc(arrayCount); //allocate a list of int pointers
for (int i = 0; i < arrayCount; i++) //assign a list of ints to each int pointer
   data [i] = (int*) malloc(arrayElemCount);
 0
Author: karx11erx,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-09-09 10:02:43

En lugar de usar doble puntero, podemos asignar un nuevo puntero y simplemente devolverlo, sin necesidad de pasar doble puntero porque no se usa en ninguna parte de la función.

Return void * so se puede utilizar para cualquier tipo de asignación.

void *someFunction (size_t size) {
    return  malloc (size);
}

Y usarlo como:

int *data = someFunction (sizeof(int));
 0
Author: GG.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-06-29 16:33:53