2009-09-09 15 views
23

que tienen algo de código en un par de diferentes funciones que se ve algo como esto:¿Cómo puedo asignar memoria y devolverla (a través de un puntero-parámetro) a la función de llamada?

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 independientes (archivos * .c).

El problema es que, aunque 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.

Se puede ver una ejecución de ejemplo del programa here, con salida que muestra las distintas direcciones de memoria.

¿Puede alguien explicarme por favor lo que estoy haciendo mal aquí, y cómo puedo conseguir que este código funcione?


EDIT: Por lo que parece que tengo que utilizar punteros dobles de hacer esto - ¿cómo voy a ir haciendo la misma cosa cuando realmente se necesita para el uso punteros dobles? Por ejemplo, los datos son

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

¿Necesito utilizar punteros triples en las llamadas a funciones?

+0

Sí, se necesitaría triples punteros luego – vpram86

Respuesta

45

Usted 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 su puntero data 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 desea 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 principal problema, 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 el número de bytes requerido para almacenar lo que indica el puntero a (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.

Estas son algunas preguntas útiles sobre punteros-a-punteros:

How do pointer to pointers work in C?

Uses for multiple levels of pointer dereferences?

+2

Sugiero que no se lo llame "doble puntero", que se confunde fácilmente con un "puntero al doble". – Nefrubyr

+0

"doble puntero" y "triple puntero" son en realidad términos bastante comunes –

+0

Gracias por los comentarios, aunque estoy de acuerdo en que el "doble puntero" es bastante común, lo he eliminado para evitar confusiones. –

4

Tiene que pasar un puntero al puntero si desea modificar el puntero.

es decir. :

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

edición: Agregado ARRAY_SIZE, en algún momento usted tiene que saber cuántos números enteros que desea asignar.

+2

El 'sizeof' debe ser' sizeof (** data) '(o simplemente' sizeof (int) '); este error ya estaba en el código original. –

+0

no arroje el valor de retorno de malloc - http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?id=1043284351&answer=1047673478 – gnud

+0

derecha y derecha, corregido. – Ben

2

Eso se debe a que los datos de puntero se pasa por valor de 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

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().

2

Aparte de utilizar la técnica de doublepointer, si sólo hay 1 retorno parámetro necesaria reescritura es la siguiente:

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

y utilizarla:

int *data = someFunction(); 
+0

No, simplemente lo hice para simplificar las cosas, no puedo devolverlo. –

+0

Estaba adivinando tanto ... pero nunca se sabe. ; ^) – Toad

0

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

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

En respuesta a su pregunta adicional que ha editado 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 usual de 'int ** data' (si data no es un parámetro de función) sería un puntero a la lista de matrices en int (por ejemplo, 'int a [100] [100]').

Así que es necesario asignar primero las matrices INT (estoy usando una llamada directa a malloc() en aras de la 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

En lugar de utilizar doble puntero sólo se puede asignar una nuevo puntero y simplemente devolverlo, no es necesario pasar el puntero doble porque no se usa en ninguna parte de la función.

Devuelve void * por lo que se puede utilizar para cualquier tipo de asignación.

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

y utilizarlo como:

int *data = someFunction (sizeof(int)); 
1

Aquí está el patrón general para la asignación de 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 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 memoria:

  1. La mejor manera de evitar problemas con la gestión de memoria es evitar la gestión de memoria; no ensucie con memoria dinámica a menos que realmente lo necesite.
  2. No eche el resultado de malloc() a menos que esté utilizando 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.
  3. Utilice el tamaño del objeto asignado 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 administración de memoria detrás de las funciones de asignación y desasignación de nivel superior; estos pueden manejar no solo la asignación sino también la inicialización y los errores.
+0

Una ventaja del enfoque de puntero doble indirecto es que puede devolver información de estado más allá de aprobado/no aprobado, y si los punteros siempre son 'nulos' cuando no son válidos, puede implementar semánticas de" asignar si es necesario ". Desafortunadamente, incluso en plataformas donde todos los punteros tienen el mismo código de representación necesitarán usar llamadas feas 'malloc' para evitar que los compiladores obtusos se desconecten al alias (un compilador sano debería reconocer que una llamada como' doSomething ((void **) & ptr) 'es probable que modifique ptr) incluso si' ptr' es algo distinto de 'void *'. – supercat

7

Un error común, especialmente si nos cambiaron la forma de Java a C/C++

Recuerde cuando pasa un puntero, que es paso 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 utilizar pasar el puntero por referencia ya que quiere cambiarlo es decir que malloc etc.

** puntero -> va a asustar a un programador de C noobie;)

+0

Hola, no entiendo la parte "pasar por valor". Un puntero es una dirección, ¿verdad? así que cuando pasas una dirección a la función, ¿cómo podría ser una copia? – hadesMM

+3

Un puntero es un tipo cuyo valor es una dirección de memoria. Usamos este tipo de puntero para apuntar a otros objetos en la memoria. Digamos que pasa un puntero p {val: 0x1234} a una función. (Recuerde que los punteros son objetos también, así que también tienen una dirección, digamos 0x8888) - La función obtendrá el puntero y podrá acceder al objeto en 0x1234, sin embargo, el puntero que apunta a esta dirección no es el mismo puntero que el puntero 0x8888, es una copia y solo tiene el mismo valor. - Esto es similar a pasar un int por valor. Se copia. –

+0

ohh es mucho más claro muchas gracias! – hadesMM

Cuestiones relacionadas