Со структурой:
struct stree {
union {
void *ob;
struct stree *strees[256];
};
};
Гарантируют ли стандарты C (особенно C11), что ob
является псевдонимом stree[0]
?
если бы это был struct stree *ob;
, это была бы «общая начальная последовательность»
@ØysteinSchønning-Johansen strees[0] всегда доступен только как указатель void *. Доступ к массиву улиц осуществляется только через индексы 1-255. Использование объединения позволяет мне сохранить размер как степень двойки, что позволяет избежать использования (void *) в нескольких местах.
Может быть. Стандарт C гарантирует только следующее, 6.3.2.3:
A pointer to
void
may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer tovoid
and back again; the result shall compare equal to the original pointer.
Теоретически/формально void*
не обязательно иметь то же представление памяти, что и указатель на объект. Всякий раз, когда он сталкивается с таким преобразованием, он теоретически может делать некоторые трюки, такие как отслеживание исходного значения, чтобы восстановить его позже.
На практике void*
и указатели объектов имеют одинаковый размер и формат в любой здравомыслящей системе, потому что это, безусловно, самый простой способ удовлетворить вышеуказанное требование. Существуют системы с расширенными режимами адресации для объектов, но они не работают так, как предполагал стандарт C. Вместо этого они используют нестандартные расширения, изобретая квалификаторы указателя near
и far
, которые затем можно применять либо к void*
, либо к указателю объекта. (Некоторыми примерами этого являются недорогие микроконтроллеры и старая MS DOS.)
Итог: проектирование для переносимости на безумные или вымышленные системы — это огромная трата времени. Добавьте static_assert(sizeof(void*) == sizeof(int*), "...")
и этого должно быть достаточно. Потому что на практике вы не найдете систему с волшебным или таинственным форматом void*
. Если да, то разберитесь с этим.
Однако вы должны иметь в виду, что void*
сам по себе является другим типом, поэтому вы обычно не можете получить доступ к памяти, где хранится void*
, через другой тип указателя (строгий псевдоним указателя). Но есть несколько исключений из строгого правила алиасинга, одно из них — каламбур через союзы.
Итак, что касается вашего union
, void*
и первый указатель объекта гарантированно будут выделены, начиная с одного и того же адреса, но опять же их соответствующие размеры и внутренние форматы теоретически могут быть разными. На практике вы можете предположить, что они одинаковы, и поскольку вы используете union
, вы не нарушаете строгое сглаживание.
Что касается наилучшей практики, то в первую очередь следует избегать void*
, так как очень мало мест, где они действительно нужны. В тех немногих случаях, когда вам нужно передать какой-то общий указатель, часто лучше использовать uintptr_t
и сохранять результат как целое число.
Может ли какая-нибудь система разместить ob по тому же адресу, что и streetes[255]?
@fadedbee они находятся по одному и тому же адресу во всех системах
@fadedbee Элементы массивов и структур гарантированно размещаются сверху вниз, причем первый член находится на самом низком адресе. Это 100% переносимое предположение, на которое не влияет порядок байтов и т. д. Члены союза гарантированно размещаются на одном и том же адресе.
near
и far
не имеют ничего общего с тем, почему разные типы указателей могут иметь разные представления в строго соответствующем коде C или коде в вопросе. near
и far
являются расширениями, но стандартные char *
и int *
могут иметь разные представления, и утверждение, что их размеры одинаковы, не будет проверять это.
@EricPostpischil near
и far
— единственный реальный пример указателей разных форматов, которые существуют на обычных компьютерах. Другие формы внутренних форматов магических указателей не существуют в нормальных системах, несмотря на то, что говорит стандарт.
Нельзя сделать так, чтобы квадратный штифт помещался в круглое отверстие, заявляя, что круглых штифтов нет: заявления об отсутствии других примеров не устраняют дефект, заключающийся в том, что near
и far
не являются примерами разных типов указателей, имеющих разные представления в строго соответствующем коде C. или код в вопросе. Указатели near
и far
просто не имеют отношения к тому, имеет ли void *
то же представление, что и struct stree *
. Квадратный стержень языковых расширений не помещается в круглое отверстие строго соответствующего кода.…
… Они не имеют отношения к проблеме и представляют собой отступление, которое сбивает этот ответ с рельсов. Кроме того, не нужно проверять размеры указателей, чтобы узнать, являются ли они проблемой: это можно узнать, потому что исходный код содержит near
или far
.
Можно легко привести примеры систем, в которых адреса имеют или имели разные представления для разных типов объектов, включая HP 3000 (который сегодня имеет постоянную стороннюю поддержку, в том числе для GCC), PDP-10 компании Digital, некоторые машины Cray, некоторые машины Prime. и серия Eclipse MV компании Data General. Это послужит информированию людей о том, почему эти лицензии были предоставлены в стандарте C, и позволит им принимать обоснованные решения, основанные на соответствующих фактах, а не на неуместных отступлениях.
Мой вопрос возвращается к вам: почему бы не использовать
void *
напрямую?