Eu quero armazenar tipos de dados mistos em uma matriz. Como alguém poderia fazer isso?
c
arrays
variant
mixed-type
chanzerre
fonte
fonte
Respostas:
Você pode transformar os elementos da matriz em uma união discriminada, também conhecida como união marcada .
O
type
membro é usado para manter a escolha de qual membro dounion
deve ser usado para cada elemento da matriz. Portanto, se você deseja armazenar umint
no primeiro elemento, faça:Quando você deseja acessar um elemento da matriz, deve primeiro verificar o tipo e, em seguida, usar o membro correspondente da união. Uma
switch
declaração é útil:É responsabilidade do programador garantir que o
type
membro sempre corresponda ao último valor armazenado nounion
.fonte
Use uma união:
Você precisará acompanhar o tipo de cada elemento.
fonte
Os elementos da matriz precisam ter o mesmo tamanho, por isso não é possível. Você pode contornar isso criando um tipo de variante :
O tamanho do elemento da união é o tamanho do elemento maior, 4.
fonte
Há um estilo diferente de definir a união de tags (por qualquer nome) que a IMO torne muito mais agradável o uso , removendo a união interna. Esse é o estilo usado no sistema X Window para coisas como eventos.
O exemplo da resposta de Barmar dá o nome
val
à união interna. O exemplo na resposta de Sp. Usa uma união anônima para evitar precisar especificar o.val.
sempre que você acessar o registro de variante. Infelizmente, estruturas e uniões internas "anônimas" não estão disponíveis em C89 ou C99. É uma extensão do compilador e, portanto, inerentemente não portátil.Uma maneira melhor da IMO é inverter toda a definição. Torne cada tipo de dado sua própria estrutura e coloque a tag (especificador de tipo) em cada estrutura.
Em seguida, envolva-os em uma união de nível superior.
Agora, pode parecer que estamos nos repetindo, e nós são . Mas considere que é provável que essa definição seja isolada em um único arquivo. Mas eliminamos o ruído de especificar o intermediário
.val.
antes de você chegar aos dados.Em vez disso, vai no final, onde é menos desagradável. : D
Outra coisa que isso permite é uma forma de herança. Editar: esta parte não é padrão C, mas usa uma extensão GNU.
Up-casting e down-casting.
Edit: Uma dica para você estar ciente é se você estiver construindo um desses com inicializadores C99 designados. Todos os inicializadores de membros devem passar pelo mesmo membro do sindicato.
O
.tag
inicializador pode ser ignorado por um compilador de otimização, porque o.int_
inicializador que segue aliases a mesma área de dados. Mesmo que nós sabemos o layout (!), E ele deve estar ok. Não, não é. Use a tag "internal" (sobrepõe a tag externa, exatamente como queremos, mas não confunde o compilador).fonte
.int_.val
não alias a mesma área porque o compilador sabe que.val
está em um deslocamento maior que.tag
. Você tem um link para uma discussão mais aprofundada sobre esse suposto problema?Você pode fazer uma
void *
matriz, com uma matriz separada desize_t.
Mas você perde o tipo de informação.Se você precisar manter o tipo de informação de alguma forma, mantenha uma terceira matriz de int (onde int é um valor enumerado). Em seguida, codifique a função que lança dependendo do
enum
valor.fonte
A união é o caminho padrão a seguir. Mas você tem outras soluções também. Uma delas é apontada como ponteiro , o que envolve o armazenamento de mais informações nos bits "livres" de um ponteiro.
Dependendo das arquiteturas, você pode usar os bits mais altos ou mais baixos, mas a maneira mais segura e portátil é usar os bits mais baixos não utilizados , aproveitando a vantagem da memória alinhada. Por exemplo, em sistemas de 32 e 64 bits, os ponteiros
int
devem ter múltiplos de 4 (assumindo queint
é um tipo de 32 bits) e os 2 bits menos significativos devem ser 0, portanto, você pode usá-los para armazenar o tipo de seus valores . Obviamente, você precisa limpar os bits da tag antes de remover a referência do ponteiro. Por exemplo, se seu tipo de dados é limitado a 4 tipos diferentes, você pode usá-lo como abaixoSe você pode ter certeza de que os dados são 8 bytes alinhados (como por ponteiros em sistemas de 64 bits, ou
long long
euint64_t
...), você vai ter mais um pouco para o tag.Isso tem uma desvantagem: você precisará de mais memória se os dados não tiverem sido armazenados em uma variável em outro local. Portanto, caso o tipo e o intervalo dos seus dados sejam limitados, você pode armazenar os valores diretamente no ponteiro. Essa técnica foi usada na versão de 32 bits do mecanismo V8 do Chrome , onde verifica o bit menos significativo do endereço para ver se isso é um ponteiro para outro objeto (como números grandes, inteiros, string ou algum objeto), ou um 31 valor assinado de bits (chamado
smi
- pequeno inteiro ). Se for umint
, o Chrome simplesmente faz um deslocamento aritmético à direita 1 bit para obter o valor, caso contrário, o ponteiro é desreferenciado.Na maioria dos sistemas atuais de 64 bits, o espaço de endereço virtual ainda é muito mais estreito que 64 bits; portanto, os bits mais significativos também podem ser usados como tags . Dependendo da arquitetura, você tem maneiras diferentes de usá-las como tags. O ARM , 68k e muitos outros podem ser configurados para ignorar os bits superiores , permitindo que você os use livremente sem se preocupar com segfault ou qualquer coisa. No artigo vinculado da Wikipedia acima:
No x86_64, você ainda pode usar os bits altos como tags com cuidado . Claro que você não precisa usar todos esses 16 bits e pode deixar alguns bits para prova futura
Nas versões anteriores do Mozilla Firefox, eles também usam pequenas otimizações de números inteiros, como a V8, com os 3 bits baixos usados para armazenar o tipo (int, string, objeto ... etc.). Mas desde o JägerMonkey, eles seguiram outro caminho ( a nova representação de valor JavaScript da Mozilla , link de backup ). O valor agora é sempre armazenado em uma variável de precisão dupla de 64 bits. Quando o
double
é normalizado , pode ser usado diretamente nos cálculos. No entanto, se os 16 bits altos forem todos 1s, que indicam um NaN , os 32 bits baixos armazenarão o endereço (em um computador de 32 bits) no valor ou no valor diretamente, os 16 bits restantes serão usados para armazenar o tipo. Esta técnica é chamada NaN-boxingou freira. Também é usado no JavaScriptCore do WebKit de 64 bits e no SpiderMonkey da Mozilla, com o ponteiro sendo armazenado nos 48 bits baixos. Se o seu tipo de dados principal é de ponto flutuante, esta é a melhor solução e oferece desempenho muito bom.Leia mais sobre as técnicas acima: https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
fonte