Хранение целого числа без знака в указателе

Я создаю структуру данных октодерева, и, чтобы сохранить память для конечных узлов, я хотел бы хранить значения непосредственно в указателе, а не создавать объект, предназначенный для хранения 8 дочерних элементов.

Мой тип данных — uint32_t, что означает, что указатель имеет достаточно битов, чтобы удерживать его на x86 или amd64.

Итак, как мне сохранить 32-битное целое число без знака в указателе x86 или amd64?

псевдокод:

uint32_t i = 123;
Octree* ptr = i;
uint32_t ii = ptr;
std::cout << ii << std::endl; //Prints 123

Как это возможно?


person KaareZ    schedule 13.12.2016    source источник
comment
uint32_t i = 123; Octree* ptr = reinterpret_cast‹Octree*›(i); uint32_t ii = реинтерпретировать_cast‹uint32_t›(ptr);   -  person FamZ    schedule 14.12.2016
comment
@FamZ: хотя на x86 это должно работать, технически это незаконно, и есть некоторые странные платформы, на которых это приведет к сбою (даже просто ссылка на недопустимые указатели вызывает аппаратные исключения на этих процессорах).   -  person Matteo Italia    schedule 14.12.2016


Ответы (2)


Хранение целого числа без знака прямо в указателе переносимым образом не разрешено, но вы можете:

  • сделайте обратное: вы можете сохранить свой указатель в беззнаковом целом числе; в частности, стандарт явно гарантирует, что uintptr_t будет достаточно большим, чтобы указатели могли пережить циклический обход;
  • используйте union:

    union NodePtr {
        Octree *child;
        uint32_t value;
    }
    

    здесь child и value используют одну и ту же ячейку памяти, и вам разрешено читать только из той, где вы в последний раз писали; когда вы находитесь в конечном узле, вы используете value, в противном случае используйте child.

person Matteo Italia    schedule 13.12.2016
comment
Есть ли какие-либо накладные расходы на использование союза? Будет ли союз выше занимать то же место, что и необработанный указатель? - person KaareZ; 14.12.2016
comment
@KaareZ: union практически бесплатны для любой приличной реализации - они просто сообщают компилятору, что вы собираетесь обращаться к этой памяти с несколькими типами, поэтому применяются самые строгие требования к выравниванию; размер объединения достаточен для хранения самого большого из его элементов данных (и на практике он обычно равен его самому большому члену). - person Matteo Italia; 14.12.2016
comment
Хорошо, тогда это отличное решение, сэр. Спасибо. - person KaareZ; 14.12.2016

Ну, вы можете хранить int как указатель с приведениями:

uint32_t i = 123;
Octree* ptr = reinterpret_cast<Octree*>(i);
uint32_t ii = reinterpret_cast<uint32_t>(ptr);
std::cout << ii << std::endl; //Prints 123

Но если вы сделаете это таким образом, я не понимаю, как вы обнаружите, что данное Octree* действительно хранит данные и не является указателем на другое Octree.

person Dima Ogurtsov    schedule 13.12.2016
comment
У меня был бы логический флаг для этого. - person KaareZ; 14.12.2016
comment
Как сказано выше, хотя это будет работать на x86 и других распространенных архитектурах, технически это не переносимо (указателям разрешено иметь представление ловушки). - person Matteo Italia; 14.12.2016