Como tudo o que parece mais assustador a princípio do que era depois, a melhor maneira de superar o medo inicial é mergulhar no desconforto do desconhecido ! Afinal, é em momentos como o que mais aprendemos.
Infelizmente, existem limitações. Enquanto você ainda está aprendendo a usar uma função, não deve assumir o papel de professor, por exemplo. Frequentemente leio respostas de quem aparentemente não sabe usar realloc
(ou seja, a resposta atualmente aceita! ) Dizendo aos outros como usá-la incorretamente, ocasionalmente sob o pretexto de que eles omitiram o tratamento de erros , mesmo que essa seja uma armadilha comum que precisa ser mencionado. Aqui está uma resposta explicando como usar realloc
corretamente . Observe que a resposta está armazenando o valor de retorno em uma variável diferente para executar a verificação de erros.
Toda vez que você chama uma função e toda vez que usa uma matriz, você está usando um ponteiro. As conversões estão ocorrendo implicitamente, o que, se algo deve ser ainda mais assustador, pois são as coisas que não vemos que geralmente causam mais problemas. Por exemplo, vazamentos de memória ...
Operadores de matriz são operadores de ponteiro. array[x]
é realmente um atalho para *(array + x)
, que pode ser dividido em: *
e (array + x)
. É mais provável que isso *
seja o que o confunde. Podemos eliminar ainda mais a adição do problema assumindo x
que 0
, assim, array[0]
se torna *array
porque a adição 0
não altera o valor ...
... e assim podemos ver que *array
é equivalente a array[0]
. Você pode usar um onde deseja usar o outro e vice-versa. Operadores de matriz são operadores de ponteiro.
malloc
, realloc
e amigos não inventam o conceito de ponteiro que você usa o tempo todo; eles simplesmente usam isso para implementar algum outro recurso, que é uma forma diferente de duração do armazenamento, mais adequada quando você deseja mudanças drásticas e dinâmicas no tamanho .
É uma pena que a resposta atualmente aceita também seja contrária a alguns outros conselhos bem fundamentados sobre o StackOverflow e, ao mesmo tempo, perca a oportunidade de introduzir um recurso pouco conhecido que brilha exatamente nesse caso de uso: matriz flexível membros! Essa é realmente uma resposta bastante quebrada ... :(
Ao definir seu struct
, declare sua matriz no final da estrutura, sem nenhum limite superior. Por exemplo:
struct int_list {
size_t size;
int value[];
};
Isso permitirá que você unir seu array int
na mesma alocação que o seu count
, e tê-los vinculados dessa maneira pode ser muito útil !
sizeof (struct int_list)
agirá como se value
tivesse um tamanho 0, por isso informa o tamanho da estrutura com uma lista vazia . Você ainda precisa adicionar ao tamanho passado realloc
para especificar o tamanho da sua lista.
Outra dica útil é lembrar que isso realloc(NULL, x)
é equivalente a malloc(x)
, e podemos usá-lo para simplificar nosso código. Por exemplo:
int push_back(struct int_list **fubar, int value) {
size_t x = *fubar ? fubar[0]->size : 0
, y = x + 1;
if ((x & y) == 0) {
void *temp = realloc(*fubar, sizeof **fubar
+ (x + y) * sizeof fubar[0]->value[0]);
if (!temp) { return 1; }
*fubar = temp; // or, if you like, `fubar[0] = temp;`
}
fubar[0]->value[x] = value;
fubar[0]->size = y;
return 0;
}
struct int_list *array = NULL;
A razão pela qual escolhi usar struct int_list **
como o primeiro argumento pode não parecer imediatamente óbvio, mas se você pensar no segundo argumento, quaisquer alterações feitas no value
interior push_back
não seriam visíveis para a função da qual estamos chamando, certo? O mesmo vale para o primeiro argumento, e precisamos ser capazes de modificar o nosso array
, não apenas aqui, mas possivelmente também em qualquer outra função / s que passamos para ...
array
começa apontando para o nada; é uma lista vazia. Inicializar é o mesmo que adicionar a ele. Por exemplo:
struct int_list *array = NULL;
if (!push_back(&array, 42)) {
// success!
}
PS Lembre-se defree(array);
quando terminar!