Я хочу хранить смешанные типы данных в массиве. Как можно это сделать?
c
arrays
variant
mixed-type
chanzerre
источник
источник
Ответы:
Вы можете сделать элементы массива распознаваемым объединением, или теговым объединением .
Элемент
type
используется для хранения выбора элемента, которыйunion
должен использоваться для каждого элемента массива. Так что если вы хотите сохранитьint
в первом элементе, вы должны сделать:Когда вы хотите получить доступ к элементу массива, вы должны сначала проверить тип, а затем использовать соответствующий член объединения.
switch
Заявление полезно:Программист должен убедиться, что
type
элемент всегда соответствует последнему значению, сохраненному вunion
.источник
Используйте союз:
Вы должны будете отслеживать тип каждого элемента.
источник
Элементы массива должны иметь одинаковый размер, поэтому это невозможно. Вы можете обойти это, создав тип варианта :
Размер элемента объединения равен размеру самого большого элемента 4.
источник
Существует другой стиль определения объединения тегов (с любым именем), который IMO делает его более приятным для использования , удаляя внутреннее объединение. Этот стиль используется в X Window System для таких вещей, как События.
Пример в ответе Бармара дает название
val
внутреннему союзу. Пример в ответе Sp. Использует анонимное объединение, чтобы не указывать.val.
каждый раз, когда вы обращаетесь к варианту записи. К сожалению, «анонимные» внутренние структуры и союзы недоступны в C89 или C99. Это расширение компилятора, и, следовательно, изначально не переносимое.Лучший способ ИМО - инвертировать все определение. Сделайте каждый тип данных своей собственной структурой и поместите тег (спецификатор типа) в каждую структуру.
Затем вы заключаете их в союз высшего уровня.
Теперь может показаться, что мы повторяем себя, и мы это делаем . Но учтите, что это определение, вероятно, будет выделено в один файл. Но мы устранили шум указания промежуточного звена,
.val.
прежде чем вы получите данные.Вместо этого это идет в конце, где это менее неприятно. : D
Другая вещь, которую это позволяет, является формой наследования. Редактировать: эта часть не стандартная Си, но использует расширение GNU.
Повышение и понижение.
Редактировать: один момент, о котором нужно знать, - это если вы создаете один из них с помощью инициализированных C99 инициализаторов Все инициализаторы должны быть через одного и того же члена объединения.
.tag
Инициализатор может быть проигнорирован оптимизирующего компилятора, потому что.int_
инициализатор , который следует псевдонимами той же области данных. Хотя мы знаем макет (!), И все должно быть в порядке. Нет, это не так. Вместо этого используйте «внутренний» тег (он накладывается на внешний тег, как мы и хотим, но не путает компилятор).источник
.int_.val
не псевдоним той же области, потому что компилятор знает, что.val
с большим смещением, чем.tag
. У вас есть ссылка для дальнейшего обсуждения этой предполагаемой проблемы?Вы можете сделать
void *
массив с отдельным массивомsize_t.
Но вы потеряете тип информации.Если вам нужно каким-то образом сохранить тип информации, сохраните третий массив int (где int - это перечисляемое значение). Затем закодируйте функцию, которая приводится в зависимости от
enum
значения.источник
Союз - это стандартный путь. Но у вас есть и другие решения. Одним из них является теговый указатель , который включает в себя хранение дополнительной информации в «свободных» битах указателя.
В зависимости от архитектуры вы можете использовать младшие или старшие биты, но самый безопасный и переносимый способ - использовать неиспользуемые младшие биты , используя преимущество выровненной памяти. Например, в 32-разрядных и 64-разрядных системах указатели
int
должны быть кратны 4 (при условии,int
что это 32-разрядный тип), а 2 младших значащих бита должны быть 0, поэтому их можно использовать для хранения типа ваших значений. , Конечно, вам нужно очистить биты тега перед разыменованием указателя. Например, если ваш тип данных ограничен 4 различными типами, вы можете использовать его, как показано нижеЕсли вы можете убедиться, что данные выровнены по 8 байтов (например, для указателей в 64-битных системах или
long long
иuint64_t
...), у вас будет еще один бит для тега.Это имеет один недостаток: вам понадобится больше памяти, если данные не были сохранены в другой переменной. Поэтому в случае, если тип и диапазон ваших данных ограничен, вы можете хранить значения непосредственно в указателе. Этот метод использовался в 32-битной версии движка Chrome V8 , где он проверяет младший значащий бит адреса, чтобы определить, является ли это указателем на другой объект (например, double, большие целые числа, строку или некоторый объект) или 31 -значное знаковое значение (называемое
smi
- маленькое целое ). Если этоint
, Chrome просто выполняет арифметическое смещение вправо на 1 бит, чтобы получить значение, в противном случае указатель разыменовывается.В большинстве современных 64-разрядных систем виртуальное адресное пространство все еще намного уже 64-разрядных, поэтому старшие старшие биты также можно использовать в качестве тегов . В зависимости от архитектуры у вас есть разные способы использовать их в качестве тегов. ARM , 68k и многие другие могут быть сконфигурированы так, чтобы игнорировать верхние биты , что позволяет вам свободно использовать их, не беспокоясь о segfault или чем-либо еще. Из приведенной выше статьи в Википедии:
На x86_64 вы все равно можете осторожно использовать старшие биты в качестве тегов . Конечно, вам не нужно использовать все эти 16 битов, и вы можете оставить некоторые биты для будущего
В предыдущих версиях Mozilla Firefox они также использовали небольшие целочисленные оптимизации, такие как V8, с 3 младшими битами, используемыми для хранения типа (int, string, object ... и т. Д.). Но начиная с JägerMonkey они пошли другим путем ( Новое представление значений JavaScript в Mozilla , ссылка для резервного копирования ). Значение теперь всегда сохраняется в 64-битной переменной двойной точности. Когда
double
это нормализованное один, он может быть непосредственно использован в расчетах. Однако, если все старшие 16 битов равны 1, это означает NaN , младшие 32 бита сохранят адрес (в 32-битном компьютере) непосредственно в значении или значении, остальные 16 бит будут использованы хранить тип. Эта техника называется NaN-боксомили монахиня-бокс. Он также используется в JavaScript-ядре 64-битного WebKit и SpiderMonkey от Mozilla, причем указатель хранится в младших 48 битах. Если ваш основной тип данных с плавающей запятой, это лучшее решение и обеспечивает очень хорошую производительность.Узнайте больше о вышеупомянутых методах: https://wingolog.org/archives/2011/05/18/value-representation-in-javascript-implementations
источник