Правильно ли я понимаю строгий псевдоним C / C ++? - PullRequest
6 голосов
/ 06 сентября 2011

Я прочитал эту статью о строгом псевдониме в C / C ++ . Я думаю, что то же самое относится к C ++.

Как я понимаю, строгий псевдоним используется для перестановки кода для оптимизации производительности. Вот почему два указателя разных (и не связанных в случае C ++) типов не могут ссылаться на одну и ту же область памяти.

Значит ли это, что проблемы могут возникнуть только при изменении памяти? Помимо возможных проблем с выравниванием памяти .

Например, обработка сетевого протокола или десериализация. У меня есть байтовый массив, динамически распределяемый и структура пакета правильно выровнена. Могу ли я reinterpret_cast это к моей структуре пакета?

char const* buf = ...; // dynamically allocated
unsigned int i = *reinterpret_cast<unsigned int*>(buf + shift); // [shift] satisfies alignment requirements

Ответы [ 2 ]

7 голосов
/ 06 сентября 2011

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

Во-первых, можно использовать псевдоним между char, signed char или unsigned char и любым другим другим типом (в вашем случае unsigned int. Это позволяет вам написать свой собственный циклы копирования в память, если они определены с использованием типа char. Это разрешено следующим языком в C99 (§6.5):

6. Эффективным типом объекта для доступа к его сохраненному значению является объявленный тип объекта, если таковой имеется. [Сноска. Выделенные объекты не имеют объявленного типа] [...] Если значение копируется в объект, не имеющий объявленного типа, используя memcpy или memmove, или копируется как массив символьного типа, затем эффективный тип измененного объекта для этого доступа и для последующих доступов, которые не изменяют значение - это эффективный тип объекта, из которого копируется значение, если оно есть. За при всех других доступах к объекту, не имеющему объявленного типа, эффективный тип объекта просто тип lvalue, используемого для доступа.

7. Объект должен иметь свое сохраненное значение, доступ к которому осуществляется только через выражение lvalue, имеющее один из следующих типов: [Сноска. Цель этого списка - указать те обстоятельства, при которых объект может или не может иметь псевдоним. ]

  • тип, совместимый с эффективным типом объекта,
  • [...]
  • тип символа.

Подобный язык можно найти в черновике C ++ 0x N3242 §3.11 / 10, хотя не так ясно, когда назначается «динамический тип» объекта (я был бы признателен за любые дальнейшие ссылки на то, что динамический тип массива char, в который был скопирован объект POD в виде массива char с правильным выравниванием).

Таким образом, псевдонимы здесь не проблема. Однако строгое чтение стандарта указывает на то, что реализация C ++ обладает большой свободой при выборе представления unsigned int.

В качестве одного случайного примера, unsigned int s может быть 24-разрядным целым числом, представленным в четырех байтах, с перемежением 8 битов заполнения; если какой-либо из этих битов заполнения не соответствует определенному (постоянному) шаблону, он рассматривается как представление прерывания, и разыменование указателя приведет к сбою. Это вероятная реализация? Возможно нет. Но исторически существовали системы с битами четности и другими странностями, и поэтому прямое чтение из сети в unsigned int, при строгом чтении стандарта не является кошерным.

Теперь, проблема заполнения битов является в основном теоретической проблемой на большинстве систем сегодня, но это стоит отметить. Если вы планируете использовать аппаратное обеспечение ПК, вам не нужно об этом беспокоиться (но не забывайте, что ваши ntohl s - порядковый номер все еще остается проблемой!)

Структуры, конечно, еще хуже - представления выравнивания зависят от вашей платформы. Я работал над встроенной платформой, в которой все типы имеют выравнивание 1 - никакие отступы когда-либо не вставляются в структуры. Это может привести к несоответствиям при использовании одних и тех же определений структуры на нескольких платформах. Вы можете либо вручную обработать смещения байтов для элементов структуры данных и напрямую ссылаться на них, либо использовать специфическую для компилятора директиву выравнивания для управления заполнением.

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

0 голосов
/ 06 сентября 2011

На самом деле этот код уже имеет UB в момент, когда вы разыменовываете целочисленный указатель reinterpret_cast ed без необходимости вызывать правила строгого наложения имен.Мало того, но если вы не будете достаточно осторожны, реинтерпретация непосредственно к вашей структуре пакета может вызвать всевозможные проблемы в зависимости от упаковки структуры и порядка байтов.Я подозреваю, что это «сработает» на нескольких компиляторах, и вы можете принять этот (возможно измеримый) риск.

...