Битовые сдвиги на указателе C?

Я нахожусь в середине этого проекта C, который я хочу сделать очень эффективным с точки зрения памяти. В некоторых случаях я использую void * в структуре динамического массива, которую я написал, чтобы хранить биты. Я хочу использовать все 64 (в данном случае) бита.

Вскоре я понял, что на самом деле вы не можете выполнять какие-либо битовые манипуляции с указателем. Итак, мое решение было следующим:

void *p;
((unsigned long)p) << 4;
((unsigned long)p) & 3;

Это выполняет свою работу, но только потому, что на моем компьютере длинные и указатели равны по размеру. Будет ли это справедливо для всех (или большинства) архитектур?

И мой реальный вопрос: Есть ли более правильный способ манипулирования битами указателя? Я думал, что этот подход в некоторой степени распространен в C (упаковка битов в пустоту *), но я мог бы ошибаюсь ...


person MADgood    schedule 04.12.2009    source источник
comment
Я бы использовал uint64_t вместо stdint.h   -  person sambowry    schedule 04.12.2009
comment
uint64_t будет слишком большим, если на вашей платформе меньше 64-битных указателей. Используйте intptr_t и uintptr_t из одного заголовка.   -  person Todd Gamblin    schedule 04.12.2009
comment
@sambowry - Похоже, вам нужно пройти stdint.h немного ближе.   -  person Chris Lutz    schedule 04.12.2009
comment
Если вы планируете использовать его как 64-битное битовое поле, то компиляция для 32-битной платформы сломает все, независимо от того, к чему вы его применили. Как я уже упоминал в своем ответе ниже, вы хотите объявить объединение указателя с битовым полем любого размера, которое вам нужно, поэтому поле имеет правильный размер независимо от того, на какую платформу вы нацеливаетесь.   -  person Anon.    schedule 04.12.2009
comment
Зачем вообще нужны такие манипуляции с указателями? Их единственное законное использование - для целей выравнивания, и для этого есть обходные пути, которые не требуют преобразования из и в целые числа.   -  person CAFxX    schedule 26.12.2011


Ответы (4)


Если ваш компилятор поддерживает это, заголовок <stdint.h> C99 предоставляет типы intptr_t и uintptr_t, которые должны быть достаточно большими, чтобы содержать указатель в вашей системе, но являются целыми числами, чтобы вы могли манипулировать битами. Это не может быть намного более портативным, чем это, если это то, что вы ищете.

person Chris Lutz    schedule 04.12.2009
comment
В любом случае, вам, вероятно, не нужно полностью портативное решение. Любая перестановка битов, которая пытается сохранить флаги в младших битах указателей, обязательно зависит от платформы, поскольку она полагается на предположения о выравнивании объектов. Таким образом, задавшему вопрос, вероятно, придется проделать некоторую работу (или, по крайней мере, исследовать) при переносе на новую платформу. - person Steve Jessop; 04.12.2009
comment
@Steve - Если он немного вертит указатель, я, конечно, надеюсь, что он не разыменует его позже. Это звучит просто как кошмар портативности. Если бы он делал это, я сомневаюсь, что он спрашивал бы о переносимости своих операций. - person Chris Lutz; 05.12.2009
comment
Не могли бы вы объяснить разницу между intptr_t и uintptr_t? - person DRz; 08.06.2016

Если вам нужно произвести подобные манипуляции с указателями, вы можете преобразовать их в intptr_t и uintptr_t, оба из которых можно найти в _ 3_. Гарантируется, что они будут определены как целочисленные типы, зависящие от платформы, с достаточным количеством битов для хранения указателя.

Там также есть ptrdiff_t, если вам нужно что-то, чтобы удерживать разницу между двумя указателями.

person Todd Gamblin    schedule 04.12.2009
comment
И если ваш компилятор не поддерживает stdint.h (cough Microsoft cough), в следующем SO-ответе и комментариях есть ссылки на несколько, которые вы можете решить использовать: stackoverflow.com / questions / 126279 / - person Michael Burr; 04.12.2009

Я думаю, вы пытаетесь решить не ту проблему. Настоящая проблема здесь:

Я использую void * в структуре динамического массива, которую я написал, чтобы хранить биты.

Не используйте указатели void для хранения битов. Используйте указатели void для хранения указателей. Используйте целые числа без знака для хранения битов.

person Secure    schedule 04.12.2009
comment
Я не уверен, что согласен с тем, что целесообразно переписать всю структуру данных и все связанные с ней функции, чтобы иметь возможность хранить значения без указателей. Опять же, возможно, если бы я написал свою структуру данных более правильно, у меня не было бы этой проблемы. - person MADgood; 04.12.2009
comment
Что ж, вы можете использовать Excel в качестве базы данных, если хотите избежать дополнительной работы по изучению баз данных, но будет ли это хорошей идеей в долгосрочной перспективе? Имейте в виду, что преобразования указателя в int и обратно полностью определены реализацией, и если вы не будете осторожны с обработкой, вы можете закончить неопределенное поведение. Ваша структура предназначена для хранения указателей. Почему бы не написать структуру данных BitStore, специализирующуюся на хранении битов, со всеми этими битами, уже находящимися внутри, так что вам не нужно заботиться о них при каждом вызове? - person Secure; 05.12.2009
comment
-1 за проповедь ОП. Похоже, он знает, чего хочет. - person Heath Hunnicutt; 06.07.2010

Объявите объединение указателя и битового поля.

person Anon.    schedule 04.12.2009
comment
Вам все равно нужно знать, насколько большим нужно сделать битовое поле, поэтому ваше другое поле должно быть intptr_t. Я не уверен, будет ли это или приведение более читаемым. - person Todd Gamblin; 04.12.2009
comment
Вы делаете битовое поле настолько большим, насколько это необходимо для других целей. Затем компилятор гарантирует, что структура достаточно велика, чтобы в нее поместился больший из указателя и битового поля. Именно для этой ситуации существуют профсоюзы. - person Anon.; 04.12.2009