Применение правила наложения указателя (указатель на свою личность)

Я недавно столкнулся с неприятным schrödinbug. При попытке загрузить файл в плоское представление памяти автор написал такой код:

class Line final { public:
int stuff[3];
char* data;
}

//...

Line* line = /*...*/;
//Trying to treat line->data like an array.  This is *wrong*.
line->data = reinterpret_cast<char*>(line) + 3*sizeof(int);

//...

line->data[0] = /*...*/
line->data[1] = /*...*/
//...
line->data[n] = /*...*/ //"line->data" changes because of this line!

Итак, происходит то, что первые строки кода по существу установлены line->data равно &line->data, Это ошибка, потому что любые изменения в значениях, на которые указывает line->data также может изменить то, что line->data сама указывает на!

Тогда мне показалось любопытным, что проблема возникла так долго. Насколько я понимаю, если не квалифицирован с restrict (или для g ++ / MSVC __restrict) компилятор должен предполагать, что указатели являются псевдонимами. Так что, если я установлю line->data[0] быть чем-то, то это будет видно для следующего доступа, line->data[1]и почти наверняка будет недействительным. Однако в отладчике изменения не были видны намного позже, и записи продолжались долго и счастливо.

Я предполагаю, что компилятор (в данном случае MSVC 2013) не считал возможным использование псевдонимов. Это разрешено?

0

Решение

Насколько я понимаю, что, если только он не квалифицирован как restrict (или для g ++ / MSVC __restrict), компилятор должен предполагать, что указатели являются псевдонимами.

Это неверно Компилятору разрешается предполагать, что указатели указывают только псевдонимы, указывающие на один и тот же тип, или указатели на char,

class X;
class Y;
X *ptr_x = ...;
Y *ptr_y = ...;
char *ptr_char = ...;

Здесь компилятор может предположить, что ptr_x не псевдоним ptr_y, Он не может делать предположения о ptr_char, тем не мение.

0

Другие решения

Трудно точно понять, в чем проблема. Я уже давно решил эту проблему, и сейчас я готовлю несколько проектов. Оглядываясь назад, кажется, что комментарии к исходному вопросу были наиболее успешными в предоставлении подсказок для объяснения поведения:

Возможно из-за заполнения, в зависимости от разрядности системы.

а также:

Ну, одна вещь, которая сразу приходит на ум, это выравнивание. Это работает на 64-битной платформе? Если это так, вычисление в арифметике указателя не учитывает отступы.

На 64-битной архитектуре, которая действительно была скомпилирована, я предполагаю, что класс в исходном вопросе будет размещен в памяти следующим образом (типы, скорректированные для ясности):

int32_t stuff_0;
int32_t stuff_1;
int32_t stuff_2;
//4 bytes of empty space
char* data;

Заполнение происходит потому, что char* указатель должен быть 8-байт выровнен. С первых трех intвзять 3*32/8=96/8=12 байт, чтобы получить это выравнивание, компилятор должен вставить дополнительный 4 байты, чтобы привести накладные расходы на раунд 16 байт.

когда data инициализирован, он был неправильно инициализирован, чтобы указывать на начало пустого пространства. Итак, пишет data[n], 0<=n<4 ударил обивка. Это только при доступе data[4] что у нас проблема.

Я говорю «самый успешный» с тех пор, пока вопрос делал в основном происходят около пятого доступа, к моей памяти иногда возникала проблема, даже при отладке. И, как я уже писал, это было schrödinbug (то есть ошибка, которая должна была произойти, но не произошла — и теперь, когда она наблюдалась, всегда есть). У меня нет данных о предыдущем типе выполняемых данных, но возможно, что логика заставила критический диапазон указателя не быть затронутым.

0

По вопросам рекламы [email protected]