Блок кода, который должен вызвать ошибку шины, выполняется нормально

В настоящее время я читаю Expert C Programmign - Deep C Secrets. На странице 164, где автор объяснял ошибку шины и ошибку сегментации, он показал эту строку кода

union { 
  char a[10];
  int i;
} u ;

int * p = ( int * ) &(u.a[1]);
*p = 17; /* the misaligned addr in p causes a bus error */

Предполагается, что приведенный выше код вызывает ошибку шины, но когда я его запустил, он работал нормально, без ошибок. Автор дал ниже объяснение

Это вызывает ошибку шины, потому что объединение массива / int гарантирует, что массив символов "a" также находится по разумно выровненному адресу для целого числа, поэтому "a+1" определенно нет. Затем мы пытаемся сохранить 4 байта в адресе, который выровнен только для однобайтового доступа. Хороший компилятор предупредит о смещении, но не сможет определить все случаи.

Насколько я понимаю вышеизложенное, char 1 байт, и мы пытаемся разместить int что составляет 4 байта по индексу char a[10] следовательно, произойдет ошибка шины (я не уверен, правильно ли я понимаю или нет)

У меня вопрос, почему приведенный выше код не вызывает ошибку шины.

Примечание: я не студент CS, простые объяснения помогут.

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

3 ответа

Решение

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

Насколько я понимаю из вышеприведенного утверждения, char - это 1 байт, и мы пытаемся поместить int, равный 4 байта, в индекс char a[10], следовательно, произойдет ошибка шины (я не уверен, верно ли мое понимание или неправильно)

Проблема не в размере char или же int, но их расклад. Как правило, архитектуры очень требовательны к адресам, с которых вы загружаете данные / код, например, вы можете загружать только 16-битное целое число с адреса, кратного 16-битному, или функция всегда должна начинаться с 4-байтового граница.

Если вы не заметите этого, процессор может прочитать неверные данные или наказать вас с исключением. Затем ОС может эмулировать его, используя несколько выровненных обращений, или передать его как SIGBUS в пользовательском приложении. Последнее то, что автор, вероятно, испытывает на своей установке.

Как все это связано с C?

То, что у вас есть, это неопределенное поведение. Взаимодействие процессора, контроллера памяти, компилятора, ОС и носовых демонических появлений в вашем районе будет влиять на то, как это повлияет (если вообще будет). На вашем компьютере возможно, что процессор изначально поддерживает не выровненный доступ, поэтому он работал, но все же это то, на что вы не можете положиться: он просто не определен. (Особенно с оптимизацией, эти вещи могут вернуться, чтобы укусить вас. Работы для меня ™ не достаточно хороши, чтобы написать четко определенный C-код!)

Данные, которые вы пытаетесь извлечь, пересекают 32-битную границу, поэтому необходимы 2 выборки из памяти (но компилятор не обязательно знает это во время компиляции).

Примечание: книга очень старая и рассказывает о 32-битных процессорах. Для 64-битной может потребоваться изменить int * p = ( int * ) &(u.a[1]); в int * p = ( int * ) &(u.a[5]); так что все необходимые данные не могут быть получены в одной выборке из памяти с выровненного адреса.

По причинам обратной совместимости большинство процессоров Intel (и их производных, таких как AMD) автоматически обрабатывают ошибки выравнивания памяти на уровне инструкций, поэтому вы не заметите ничего плохого (упрощенно автоматически добавляется дополнительное чтение, блокирующее шину, чтобы убедиться, что между чтениями ничего не меняется)).

На многих других архитектурах ЦП (ARM, PowerPC, MIPS, более новые архитектуры Intel) пример кода вызвал бы проблемы, как описано, но теперь некоторые операционные системы, такие как Linux, могут быть настроены на автоматическое обнаружение ошибки и выполнение "исправления", позволяющего В программе для неосведомленности возникла проблема. В большинстве программ это, вероятно, останется незамеченным для пользователя, но займет много времени и вызовет реальные проблемы с программным обеспечением и драйверами в реальном времени.

Критический по времени код часто условно компилируется, чтобы выполнять или не выполнять невыровненный доступ в соответствии с архитектурой процессора, для которого он компилируется. В linux псевдо-файл "/proc/cpu/alignment" может использоваться для управления поведением ядра и просмотра статистики о количестве "исправлений".

Другие вопросы по тегам