Passando uma matriz dinâmica em que as funções em C
Pergunta
Eu estou tentando criar uma função que leva uma matriz como um argumento, adiciona valores a ele (aumentando seu tamanho, se necessário) e retorna a contagem de itens. Até agora eu tenho:
int main(int argc, char** argv) {
int mSize = 10;
ent a[mSize];
int n;
n = addValues(a,mSize);
for(i=0;i<n;i++) {
//Print values from a
}
}
int addValues(ent *a, int mSize) {
int size = mSize;
i = 0;
while(....) { //Loop to add items to array
if(i>=size-1) {
size = size*2;
a = realloc(a, (size)*sizeof(ent));
}
//Add to array
i++;
}
return i;
}
Isso funciona se msize é grande o suficiente para conter todos os elementos potenciais da matriz, mas se ele precisa redimensionar, recebo uma falha de segmentação.
Eu também tentei:
int main(int argc, char** argv) {
...
ent *a;
...
}
int addValues(ent *a, int mSize) {
...
a = calloc(1, sizeof(ent);
//usual loop
...
}
sem sucesso.
Eu assumo isso é porque quando eu chamar realloc, a cópia de 'a' está em outro lugar pontas - como é possível modificar isso para que 'a' sempre aponta para o mesmo local
Am I ir sobre isso corretamente? Existem melhores formas de lidar com estruturas dinâmicas em C? Devo ser a implementação de uma lista ligada para lidar com isso?
Solução
O principal problema aqui é que você está tentando usar realloc com uma matriz alocada-stack. Você tem:
ent a[mSize];
Isso é atribuição automática na pilha. Se você quisesse usar realloc () sobre isso mais tarde, você iria criar a matriz na pilha usando malloc (), assim:
ent *a = (ent*)malloc(mSize * sizeof(ent));
Assim que a biblioteca malloc (e, portanto, realloc (), etc.) sabe sobre o seu array. Dos olhares isso, você pode ser confuso matrizes C99 de comprimento variável com a verdadeira matrizes dinâmicas , por isso certifique-se entender a diferença que existe antes de tentar corrigir isso.
Realmente, porém, se você está escrevendo matrizes dinâmicas em C, você deve tentar usar o projeto OOP-ish à informação encapsular sobre suas matrizes e escondê-lo a partir do usuário. Você deseja consolidar informações (por exemplo, ponteiro e tamanho) sobre sua matriz em uma estrutura e operações (por exemplo, alocação, adicionando elementos, removendo elementos, libertando, etc.) em funções especiais que o trabalho com a sua estrutura. Assim que você pode ter:
typedef struct dynarray {
elt *data;
int size;
} dynarray;
E você pode definir algumas funções para trabalhar com dynarrays:
// malloc a dynarray and its data and returns a pointer to the dynarray
dynarray *dynarray_create();
// add an element to dynarray and adjust its size if necessary
void dynarray_add_elt(dynarray *arr, elt value);
// return a particular element in the dynarray
elt dynarray_get_elt(dynarray *arr, int index);
// free the dynarray and its data.
void dynarray_free(dynarray *arr);
Desta forma, o usuário não tem que se lembrar exatamente como alocar as coisas ou o tamanho do array é atualmente. Esperança de que se você começou.
Outras dicas
Tente retrabalhar-lo para um apontador para um apontador para a matriz é passado em, isto é ent **a
. Então, você será capaz de atualizar o chamador sobre a nova localização da matriz.
Esta é uma boa razão para usar OOP. sim, você pode fazer OOP em C, e ainda parece bom, se feito corretamente.
Neste caso simples você não precisa herança nem polimorfismo, apenas os conceitos de encapsulamento e métodos:
- definir uma estrutura com um comprimento e um ponteiro de dados. talvez um tamanho de elemento.
- funções de gravação de apanhador / definidor que operam em ponteiros para que struct.
- a 'crescer' modifica função o ponteiro de dados dentro da estrutura, mas nenhum estadias ponteiro struct válido.
Se você alterou a declaração de variável no principal para ser
ent *a = NULL;
o código funcionaria mais como você imaginou, não liberando uma matriz alocada-stack. Definir um para NULL funciona porque trata realloc isso como se o usuário chamado malloc (tamanho). Tenha em mente que, com esta alteração, o protótipo para AddValue precisa mudar para
int addValues(ent **a, int mSize)
e que as necessidades de código para lidar com o caso de falhar realloc. Por exemplo
while(....) { //Loop to add items to array
tmp = realloc(*a, size*sizeof(ent));
if (tmp) {
*a = tmp;
} else {
// allocation failed. either free *a or keep *a and
// return an error
}
//Add to array
i++;
}
Eu esperaria que a maioria das implementações de realloc irá alocar internamente duas vezes mais memória, se as necessidades buffer atual redimensionamento fazendo do código original
size = size * 2;
desnecessário.
Você está passando o ponteiro matriz por valor. O que isto significa é:
int main(int argc, char** argv) {
...
ent *a; // This...
...
}
int addValues(ent *a, int mSize) {
...
a = calloc(1, sizeof(ent); // ...is not the same as this
//usual loop
...
}
para alterar o valor de um na função addValues
não altera o valor de um em principal. Para alterar o valor de um em principal que você precisa para passar uma referência a ele para addValues
. No momento, o valor de uma está sendo copiado e passado para addValues
. Para passar uma referência a um uso:
int addValues (int **a, int mSize)
e chamá-lo assim:
int main(int argc, char** argv) {
...
ent *a; // This...
...
addValues (&a, mSize);
}
No addValues
, acesso os elementos de um como este:
(*a)[element]
e realocar a matriz como este:
(*a) = calloc (...);
Xahtep explica como o interlocutor possa lidar com o fato de que realloc () pode mover a matriz para um novo local. Contanto que você fizer isso, você deve ser fino.
realloc () pode ficar caro se você começar a trabalhar com grandes matrizes. Isso é quando é hora de começar a pensar em usar outras estruturas de dados -. Uma lista ligada, uma árvore binária, etc
Como afirmado você deve passar ponteiro para ponteiro para atualizar o valor do ponteiro.
Mas gostaria de sugerir redesenho e evitar esta técnica, na maioria dos casos ele pode e deve ser evitado. Sem saber o que exatamente você está tentando alcançar é difícil sugerir projeto alternativo, mas estou 99% certo de que é factível outra maneira. E, como Javier triste - pensar orientada a objeto e você sempre terá um código melhor.
Você realmente necessário para usar C? Este seria um ótimo aplicativo de C ++ 's 'std :: vector', que é precisamente uma matriz dinâmica de tamanho (facilmente resizeble com uma única chamada você não tem que escrever e depurar-se).