que - ¿Cómo puedo asignar memoria y devolverla(a través de un puntero-parámetro) a la función de llamada?
punteros como parametros de funciones en c (10)
Tengo un código en un par de funciones diferentes que se ve así:
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 que malloc funciona bien, y la memoria asignada se puede usar en someFunction
, la misma memoria no está disponible una vez que la función ha regresado.
here se puede ver una ejecución de ejemplo del programa, y el resultado muestra las distintas direcciones de memoria.
¿Puede alguien explicarme por favor lo que estoy haciendo mal aquí, y cómo puedo hacer que funcione este código?
EDITAR: Parece que necesito utilizar dos punteros para hacer esto: ¿cómo haré lo mismo cuando realmente necesite utilizar punteros dobles? Entonces, por ejemplo, los datos son
int **data = NULL; //used for 2D array
¿Debo utilizar punteros triples en las llamadas a funciones?
Además de usar la técnica de doble puntero, si solo hay 1 parámetro de retorno necesario, la reescritura es la siguiente:
int *someFunction () {
return (int *) malloc (sizeof (int *));
}
y úsala:
int *data = someFunction ();
Aquí está el patrón general para asignar memoria en una función y devolver el puntero a través de un 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 convertir el puntero en 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:
- La mejor manera de evitar problemas con la administración de memoria es evitar la administración de la memoria; no ensucie con memoria dinámica a menos que realmente lo necesite.
- No eche el resultado de malloc () a menos que esté usando una implementación que sea anterior al estándar ANSI de 1989 o tenga la intención de compilar el código como C ++. Si olvida incluir stdlib.h o no tiene un prototipo para malloc () en el alcance, el lanzamiento del valor de retorno suprimirá un valioso diagnóstico del compilador.
- Utilice el tamaño del objeto asignado en lugar del tamaño del tipo de datos (es decir,
sizeof *p
lugar desizeof (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. - Aislar las funciones de administración de memoria detrás de las funciones de asignar y desasignar de mayor nivel; estos pueden manejar no solo la asignación sino también la inicialización y los errores.
Aquí está tratando de modificar el puntero, es decir, de "data == Null" a "data == 0xabcd" alguna otra memoria que haya asignado. Entonces, para modificar los datos que necesita, pase la dirección de los datos, es decir, los datos.
void someFunction (int **data) {
*data = (int *) malloc (sizeof (int));
}
Debe pasar un puntero al puntero si desea modificar el puntero.
es decir. :
void someFunction (int **data) {
*data = malloc (sizeof (int)*ARRAY_SIZE);
}
editar: Agregado ARRAY_SIZE, en algún momento debe saber cuántos enteros desea asignar.
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, quiere cambiar los data
su puntero en la función principal. En C, si desea cambiar algo que se transfiere como parámetro (y que ese cambio aparezca en la versión de la persona que llama), debe pasar un puntero a lo que quiera cambiar. En este caso, ese "algo que quiere cambiar" es un puntero, de modo que para poder cambiar ese puntero, debe usar un puntero a puntero ...
Tenga en cuenta que además de su problema principal, había otro error en el código: sizeof(data)
le da la cantidad de bytes necesarios para almacenar el puntero (4 bytes en un sistema operativo de 32 bits u 8 bytes en un sistema operativo de 64 bits ), mientras que realmente desea la cantidad de bytes necesarios para almacenar lo que señala el puntero (un int
, es decir, 4 bytes en la mayoría de los sistemas operativos). Debido a que típicamente sizeof(int *)>=sizeof(int)
, esto probablemente no habría causado un problema, pero es algo a tener en cuenta. He corregido esto en el código de arriba.
Aquí hay algunas preguntas útiles sobre punteros a punteros:
En lugar de utilizar un puntero doble, podemos asignar un puntero nuevo y simplemente devolverlo, sin necesidad de pasar el puntero doble porque no se usa en ninguna parte de la función.
Retorno void *
por lo que 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));
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 al 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;
}
Respondiendo a su pregunta adicional editada en:
''*'' denota un puntero a algo. Entonces ''**'' 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 los datos no son un parámetro de función) sería un puntero a la lista de arreglos int (por ejemplo, ''int a [100] [100]'').
Por lo tanto, primero debe asignar sus matrices en int (estoy utilizando una llamada directa a malloc () por 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);
Una trampa común, especialmente si se mueve de Java a C / C ++
Recuerde que cuando pasa un puntero, pasa por valor, es decir, se copia el valor del puntero. Es bueno para hacer cambios en 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 quiere cambiarlo, es decir, malloc, etc.
** puntero -> asustará a un programador noobie C;)
someFunction () toma su parámetro como int *. Entonces 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 en el exterior. Como otros sugirieron, puede usar int ** para obtener los cambios reflejados en los datos. Otra forma de hacerlo es devolver int * desde algunaFunción ().