2014-09-26 6 views
0

Недавно я столкнулся с неприятным 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

Так вы в основном задаете вопрос: «Почему это не произошло на' line-> data [1] = ... '?"? –

+0

Да. Запись через 'line-> data [0]' должна быть видна последующей загрузке 'line-> data' при вычислении' line-> data [1] '- безгласное сглаживание не включает самоиализацию. – imallett

+0

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

ответ

0

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

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

и:

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

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

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

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

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

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

0

Мое понимание состоит в том, что, если у него нет ограничений (или для g ++/MSVC __restrict), компилятор должен предположить, что указатели сглажены.

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

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

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

+0

Последнее, что я проверил. MSVC не поддерживает строгое сглаживание, поэтому я предполагаю, что он должен предположить, что все указатели сглажены. В любом случае указатель здесь задает _is_ указатель 'char'. – imallett

+0

@IanMallett: Действительно. Возможно, что это ошибка компилятора. Однако неопределенное поведение должно быть сначала удалено из программы. –

 Смежные вопросы

  • Нет связанных вопросов^_^