Может ли две структуры в C99 / C11 псевдоним? - PullRequest
3 голосов
/ 28 апреля 2020

Предположим, у нас есть две структуры

struct hello {  
  float a;
  float b;
};

и

struct world {
  float c;
  float d;
};

и функция

void func(struct hello* h, struct world* w);

Это тот случай, когда два аргумента могут иметь псевдоним, так как они оба содержат float и, следовательно, компилятор должен сгенерировать код, который обеспечивает корректность? Если я уверен, что w и h не хранятся в одной и той же ячейке памяти (или не перекрываются каким-либо образом), достигну ли я ускорения, изменив функцию на следующую?

void func(struct hello *restrict h, struct world *restrict w);

Кроме того, имеет ли примитивный тип float какое-либо влияние на это или те же правила применяются к int?

Ответы [ 3 ]

6 голосов
/ 28 апреля 2020

Ваш пример очень близок к созданию двух структур совместимого типа (C11 6.2.7). Но чтобы быть совместимыми, они должны иметь одинаковых членов с одинаковыми именами, а теги struct также должны быть одинаковыми.

У вас этого нет, поэтому (TL; DR) структуры в вопросе не могут быть псевдонимами.

Еще одна вещь, с которой можно поиграться, - это трюк, называемый common начальная последовательность (C11 6.5.2.3), где вы можете поместить обе структуры в объединение, которое видно в блоке перевода. После этого вам будет разрешено проверять первую последовательность членов каждого типа структуры, пока они не перестанут быть совместимыми. Вы можете сделать это:

typedef union
{
  struct hello h;
  struct world w;
} hack_t;

Затем получить доступ к отдельным членам любой структуры. К сожалению, это правило немного exoti c, и компиляторы, очевидно, не всегда хорошо его поддерживают - правило подчинялось некоторым отчетам о дефектах (DR). Я не уверен в его статусе в текущем C17.

Но независимо от этого трюка, объединение все еще позволяет присвоить доступ к struct hello или struct world через hack_t, так как тип объединения, который включает совместимый тип среди своих членов (C11 6.5 / 7). Полагаю, что это немного полезно.

Помимо этих случаев, вы не можете дико приводить типы указателей от одного типа указателя к другому и отменять ссылки. Было бы строгое нарушение псевдонимов (C11 6.5 / 7), даже если все отдельные члены являются совместимыми типами. (Однако, конечно, вы можете получить доступ к любому отдельному члену float, сняв ссылку на указатель float на этого члена.)

Ваша оптимизация restrict не применяется, поскольку структуры не могут иметь псевдоним, если только они действительно совместимые типы. Таким образом, компилятор будет предполагать, что они всегда находятся в разных областях памяти.

Неважно, используете ли вы float или любой другой примитивный тип данных, они все ведут себя одинаково, за исключением типов символов. Указатель на тип символа может использоваться для доступа к любым данным без строгого нарушения псевдонимов (но не наоборот, чтение типа символа через другой несовместимый тип).

1 голос
/ 28 апреля 2020

Если я уверен, что w и h не хранятся в одной и той же ячейке памяти (или не перекрываются каким-либо образом), могу ли я добиться ускорения, изменив функцию на следующую?

void func(struct hello *restrict h, struct world *restrict w);

Мое понимание стандарта C11 (читай n1570 ) состоит в том, что некоторые реализации (на практике некоторые оптимизирующие компиляторы, когда их просят достаточно оптимизировать, например, недавний ) G CC вызывается как gcc -O3) может добиться ускорения благодаря ключевому слову restrict.

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

Наконец, ваш пример - типичный случай, когда обычная практика и ожидания программистов и стандарт C могут расходиться. Подумайте об использовании инструментов анализа исходных данных stati c, таких как Frama- C или Clang stati c анализаторы (или переключитесь на лучший язык, например Rust ).

0 голосов
/ 29 апреля 2020

Авторы C89, похоже, не рассматривали вопрос о том, когда lvalue формы, такой как aggregatePtr->member или aggregatePtr->arrayTypeMember[index], может быть использован для доступа к произвольному хранилищу типа элемента или элемента-элемента, или будет ограниченный доступ к элементам, которые фактически содержатся в совокупности, и ни одна версия с тех пор должным образом не решала эту проблему. Clang и g cc, похоже, предполагают, что операции с различными типами структур не могут взаимодействовать, даже если структуры содержат одни и те же элементы, если не сконфигурированы для -fno-strict-aliasing, но кодируют с использованием restrict без выполнения каких-либо сравнений между restrict квалифицированные указатели и другие указатели могут получить хорошую производительность, даже когда -fno-strict-aliasing используется для обеспечения полезности общих начальных последовательностей. Ничто в Обосновании не может предложить какого-либо намерения запретить сравнения между restrict указателями и другими указателями, но они заставляют и clang, и g cc вести себя бессмысленно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...