Eu tenho um programa que lê uma lista "bruta" de entidades no jogo e pretendo criar uma matriz contendo um número de índice (int) de um número indeterminado de entidades, para processar várias coisas. Gostaria de evitar o uso de muita memória ou CPU para manter esses índices ...
Uma solução rápida e suja que utilizo até agora é declarar, na principal função de processamento (foco local), o array com o tamanho máximo de entidades do jogo e outro número inteiro para acompanhar quantas foram adicionadas à lista. Isso não é satisfatório, pois todas as listas contêm mais de 3.000 matrizes, o que não é muito, mas parece um desperdício, pois é possível usar a solução para 6-7 listas para funções variadas.
Não encontrei nenhuma solução específica em C (não em C ++ ou C #) para conseguir isso. Posso usar ponteiros, mas tenho um pouco de medo de usá-los (a menos que seja a única maneira possível).
As matrizes não deixam o escopo da função local (elas devem ser passadas para uma função e depois descartadas), caso isso mude.
Se os ponteiros são a única solução, como posso acompanhá-los para evitar vazamentos?
fonte
Respostas:
Se você precisar de uma matriz dinâmica, não poderá escapar dos ponteiros. Por que você está com medo? Eles não vão morder (contanto que você seja cuidadoso). Não há matriz dinâmica embutida em C, você apenas precisará escrever uma. No C ++, você pode usar a
std::vector
classe interna. C # e quase todas as outras linguagens de alto nível também têm uma classe semelhante que gerencia matrizes dinâmicas para você.Se você planeja escrever o seu próprio, aqui está algo para começar: as implementações mais dinâmicas da matriz funcionam começando com uma matriz de algum tamanho padrão (pequeno) e, sempre que você ficar sem espaço ao adicionar um novo elemento, duplique o tamanho da matriz. Como você pode ver no exemplo abaixo, não é muito difícil: (omiti as verificações de segurança por questões de brevidade)
Usá-lo é tão simples:
fonte
removeArray
método que se livre do último elemento também seria interessante. Se você permitir, eu o adicionarei ao seu exemplo de código.Existem algumas opções em que posso pensar.
array[100]
sem ter que percorrer1-99
primeiro. E pode não ser tão útil para você usar também.É difícil dizer qual opção seria melhor na sua situação. Simplesmente criar uma grande variedade é, naturalmente, uma das soluções mais fáceis e não deve gerar muitos problemas, a menos que seja realmente grande.
fonte
realloc
com o nº 3 - aloque o tamanho normal da matriz e depois aumente sempre que acabar.realloc
tratará de copiar seus dados, se necessário. Quanto à pergunta do OP sobre gerenciamento de memória, você só precisa fazermalloc
uma vez no início,free
outra no final erealloc
toda vez que ficar sem espaço. Não é tão ruim.7 * 3264 * 32 bit
parece91.39 kilobytes
.realloc
retornoNULL
:a->array = (int *)realloc(a->array, a->size * sizeof(int));
... Talvez tivesse sido melhor escrito como:int *temp = realloc(a->array, a->size * sizeof *a->array); a->array = temp;
... Dessa forma, seria óbvio que o que quer que aconteça precisa acontecer antes oNULL
valor é atribuído aa->array
(se é que existe).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 usarrealloc
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 assumindox
que0
, assim,array[0]
se torna*array
porque a adição0
não altera o valor ...... e assim podemos ver que
*array
é equivalente aarray[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:Isso permitirá que você unir seu array
int
na mesma alocação que o seucount
, e tê-los vinculados dessa maneira pode ser muito útil !sizeof (struct int_list)
agirá como sevalue
tivesse um tamanho 0, por isso informa o tamanho da estrutura com uma lista vazia . Você ainda precisa adicionar ao tamanho passadorealloc
para especificar o tamanho da sua lista.Outra dica útil é lembrar que isso
realloc(NULL, x)
é equivalente amalloc(x)
, e podemos usá-lo para simplificar nosso código. Por exemplo: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 novalue
interiorpush_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 nossoarray
, 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:PS Lembre-se de
free(array);
quando terminar!fonte
array[x]
é realmente um atalho para*(array + x)
, [...]" Você tem certeza disso ???? Veja uma exposição de seus diferentes comportamentos: eli.thegreenplace.net/2009/10/21/… .array[index]
é realmenteptr[index]
disfarçado ... "A definição do operador subscrito[]
se queE1[E2]
é idêntico ao(*((E1)+(E2)))
" Você não pode refutar a stdint main(void) { unsigned char lower[] = "abcdefghijklmnopqrstuvwxyz"; for (size_t x = 0; x < sizeof lower - 1; x++) { putchar(x[lower]); } }
... Você provavelmente precisará#include <stdio.h>
e<stddef.h>
... Você vê como eu escrevix[lower]
(x
sendo o tipo inteiro) em vez delower[x]
? O compilador C não se importa, porque*(lower + x)
é o mesmo valor que*(x + lower)
elower[x]
é o primeiro onde-comox[lower]
é o último. Todas essas expressões são equivalentes. Testá-los ... ver por si mesmo, se você não pode tomar a minha palavra ...gcc
ouclang
por toda a sua compilação C, porque você vai descobrir que há tantos pacotes que adotaram C99 apresenta ...Com base no design de Matteo Furlans , quando ele disse que " as implementações mais dinâmicas da matriz funcionam começando com uma matriz com algum tamanho (pequeno) padrão, sempre que você ficar sem espaço ao adicionar um novo elemento, dobre o tamanho da matriz ". A diferença no " trabalho em andamento " abaixo é que ele não dobra de tamanho, visa usar apenas o necessário. Também omiti as verificações de segurança por simplicidade ... Também com base na idéia de brimboriums , tentei adicionar uma função de exclusão ao código ...
O arquivo storage.h se parece com isso ...
O arquivo storage.c fica assim ...
O main.c fica assim ...
Ansiosos pelas críticas construtivas a seguir ...
fonte
malloc()
antes de tentar usar a alocação. Na mesma linha, é um erro atribuir diretamente o resultado dorealloc()
ponteiro à memória original que está sendo realocada; serealloc()
falhar,NULL
é retornado e o código é deixado com um vazamento de memória. É muito mais eficiente dobrar a memória ao redimensionar do que adicionar um espaço por vez: menos chamadas pararealloc()
.int
, etc.) de cada vez. Dobrar é uma solução típica, mas não acho que exista uma solução ideal que atenda a todas as circunstâncias. Eis por que duplicar é uma boa idéia (algum outro fator, como 1,5, também seria bom): se você começar com uma alocação razoável, talvez não seja necessário realocar. Quando mais memória é necessária, a alocação razoável é dobrada e assim por diante. Dessa forma, você provavelmente precisará de apenas uma ou duas chamadasrealloc()
.Quando você está dizendo
você está basicamente dizendo que está usando "ponteiros", mas um que é um ponteiro local para toda a matriz em vez de ponteiro para toda a memória. Como você já está conceitualmente usando "ponteiros" (ou seja, números de identificação que se referem a um elemento em uma matriz), por que você não usa ponteiros regulares (ou seja, números de identificação que se referem a um elemento na maior matriz: toda a memória )
Em vez de seus objetos armazenarem números de identificação de recurso, você pode fazê-los armazenar um ponteiro. Basicamente a mesma coisa, mas muito mais eficiente, pois evitamos transformar "array + index" em um "ponteiro".
Os ponteiros não são assustadores se você os considera como índice de matriz para toda a memória (que é o que realmente são)
fonte
Para criar uma matriz de itens ilimitados de qualquer tipo:
e como usá-lo:
Esse vetor / matriz pode conter qualquer tipo de item e é totalmente dinâmico em tamanho.
fonte
Bem, acho que se você precisar remover um elemento, fará uma cópia da matriz desprezando o elemento a ser excluído.
Assume-se que
getElement2BRemove()
,copy2TempVector( void* ...)
efillFromTempVector(...)
são métodos auxiliares para lidar com o vector de temperatura.fonte