Como posso alocar memory e devolvê-lo (através de um parâmetro de ponteiro) para a function de chamada?

Eu tenho alguns códigos em algumas funções diferentes que se parecem com isso:

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 () e useData () são definidos em módulos separados (arquivos * .c).

O problema é que, enquanto o malloc funciona bem, e a memory alocada é utilizável em someFunction , a mesma memory não fica disponível depois que a function é retornada.

Um exemplo de execução do programa pode ser visto aqui , com saída mostrando os vários endereços de memory.

Alguém pode me explicar o que estou fazendo de errado aqui e como posso fazer esse código funcionar?


EDIT: Assim, parece que eu preciso usar pointers duplos para fazer isso – como eu iria fazer a mesma coisa quando eu realmente preciso usar pointers duplos? Então, por exemplo, os dados são

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

Eu preciso usar pointers triplos em chamadas de function?

Você quer usar um ponteiro para ponteiro:

 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ê? Bem, você quer mudar seus data ponteiro na function principal. Em C, se você quiser alterar algo que é passado como um parâmetro (e que a mudança apareça na versão do chamador), você deve passar um ponteiro para o que você deseja alterar. Nesse caso, “algo que você deseja alterar” é um ponteiro – para poder alterar esse ponteiro, você deve usar um ponteiro para ponteiro …

Observe que, além de seu problema principal, havia outro bug no código: sizeof(data) fornece o número de bytes necessários para armazenar o ponteiro (4 bytes em um sistema operacional de 32 bits ou 8 bytes em um sistema operacional de 64 bits ), enquanto você realmente quer o número de bytes necessários para armazenar para o qual o ponteiro aponta (um int , ou seja, 4 bytes na maioria dos sistemas operacionais). Porque normalmente sizeof(int *)>=sizeof(int) , isso provavelmente não teria causado um problema, mas é algo que deve estar ciente. Eu corrigi isso no código acima.

Aqui estão algumas perguntas úteis sobre pointers para pointers:

Como o ponteiro para pointers funciona em C?

Usos para vários níveis de dereferências de ponteiro?

Uma armadilha comum, especialmente se você moveu o Java para C / C ++

Lembre-se quando você passar um ponteiro, é passar por valor, ou seja, o valor do ponteiro é copiado. É bom fazer mudanças nos dados apontados pelo ponteiro, mas qualquer mudança no próprio ponteiro é apenas local, já que é uma cópia !!

O truque é usar passar o ponteiro por referência, já que você quer mudá-lo, por exemplo, malloc etc.

** ponteiro -> vai assustar um programador noobie C;)

Você precisa passar um ponteiro para o ponteiro se quiser modificar o ponteiro.

ie. :

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

Editar: Adicionado ARRAY_SIZE, em algum momento você tem que saber quantos números inteiros você deseja alocar.

Isso porque os dados do ponteiro são passados ​​por valor para someFunction .

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

Ponteiro para ponteiro ou retornar o ponteiro alocado resolveria o problema.

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

someFunction () usa seu parâmetro como int *. Então, quando você chama de main (), uma cópia do valor que você passou criou. O que quer que você esteja modificando dentro da function é esta cópia e, portanto, as mudanças não serão refletidas externamente. Como outros sugeriram, você pode usar int ** para obter as alterações refletidas nos dados. Outra maneira de fazer isso é retornar int * de someFunction ().

Além de usar a técnica de doublepointer, se houver apenas 1 parâmetro de retorno, a reescrita necessária é a seguinte:

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

e usá-lo:

  int *data = someFunction (); 

Aqui está o padrão geral para alocar memory em uma function e retornar o ponteiro via parâmetro:

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

Outro método é tornar o ponteiro o valor de retorno da function (meu método preferido):

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

Algumas notas sobre gerenciamento de memory:

  1. A melhor maneira de evitar problemas com o gerenciamento de memory é evitar o gerenciamento de memory; não muck com memory dinâmica, a menos que você realmente precise.
  2. Não converta o resultado de malloc (), a menos que você esteja usando uma implementação anterior à norma ANSI de 1989 ou que pretenda compilar o código como C ++. Se você esquecer de include stdlib.h ou não tiver um protótipo para malloc () no escopo, a conversão do valor de retorno suprimirá um valioso diagnóstico do compilador.
  3. Use o tamanho do object sendo alocado em vez do tamanho do tipo de dados (isto é, sizeof *p vez de sizeof (T) ); Isto irá poupar alguma azia se o tipo de dados tiver que mudar (digamos de int para long ou float para double). Ele também faz o código ler um pouco melhor IMO.
  4. Isole as funções de gerenciamento de memory por trás das funções de alocação e desalocação de alto nível; eles podem lidar não apenas com alocação, mas também com boot e erros.

Aqui você está tentando modificar o ponteiro, ou seja, de “data == Null” para “data == 0xabcd” alguma outra memory que você alocou. Então, para modificar os dados que você precisa passar o endereço de dados ou seja, e dados.

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

Respondendo a sua pergunta adicional que você editou em:

‘*’ denota um ponteiro para algo. Então, ‘**’ seria um ponteiro para um ponteiro para algo, ‘***’ um ponteiro para um ponteiro para um ponteiro para algo, etc.

A interpretação usual de ‘int ** data’ (se os dados não forem um parâmetro de function) seria um ponteiro para a lista de int arrays (por exemplo, ‘int a [100] [100]’).

Então você precisa primeiro alocar seus arrays int (estou usando uma chamada direta para malloc () por uma questão de simplicidade):

 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); 

Em vez de usar o ponteiro duplo, podemos simplesmente alocar um novo ponteiro e apenas retorná-lo, não é necessário passar o ponteiro duplo porque ele não é usado em nenhum lugar da function.

Retorna void * então pode ser usado para qualquer tipo de alocação.

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

e usá-lo como:

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