Гарантирует ли объединение указателя и массива указателей, что один указатель имеет тот же адрес, что и первый элемент массива? - PullRequest
2 голосов
/ 20 июня 2019

Со структурой:

struct stree {
    union {
        void *ob;
        struct stree *strees[256];
    };
};

Гарантируют ли стандарты C (особенно C11), что ob является псевдонимом stree[0]?

1 Ответ

1 голос
/ 20 июня 2019

Может быть. Стандарт C гарантирует только следующее, 6.3.2.3:

Указатель на void может быть преобразован в или из указателя на любой тип объекта. Указатель на любой тип объекта может быть преобразован в указатель на void и обратно; результат должен сравнить, равный исходному указателю.

Теоретически / формально, void* не обязательно должен иметь то же представление памяти, что и указатель на объект. Всякий раз, когда он сталкивается с таким преобразованием, он теоретически может делать некоторые трюки, такие как отслеживание исходного значения, чтобы восстановить его позже.

На практике void* и указатели на объекты имеют одинаковый размер и формат в любой здравомыслящей системе, потому что это самый простой способ удовлетворить вышеуказанное требование. Существуют системы с расширенными режимами адресации для объектов, но они не работают так, как предполагал стандарт C. Вместо этого они используют нестандартные расширения, изобретая квалификаторы указателей near и far, которые затем можно применить либо к void*, либо к указателю объекта. (Некоторые примеры этого - микроконтроллеры низкого уровня и старая MS DOS.)

Итог: проектирование для переносимости в безумные или вымышленные системы - огромная трата времени. Вбросить static_assert(sizeof(void*) == sizeof(int*), "...") и этого должно быть достаточно. Потому что на практике вы не найдете систему с магическим или загадочным форматом void*. Если да, то разберись с этим.

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

Итак, что касается вашего union, void* и первый указатель объекта гарантированно будут размещены, начиная с одного и того же адреса, но опять-таки их соответствующие размеры и внутренние форматы теоретически могут быть разными. На практике вы можете предположить, что они одинаковы, и поскольку вы используете union, вы не нарушаете строгий псевдоним.

Что касается лучшей практики, то это, во-первых, избегать void*, так как там очень мало мест, где они вам действительно нужны. В тех немногих случаях, когда вам нужно передать какой-то общий указатель, часто лучше использовать uintptr_t и сохранять результат как целое число.

...