#c #optimization #linker #memory-address #gcc7
Вопрос:
Я читаю «Компьютерные системы: взгляд программиста» и наткнулся на следующие файлы на языке Си:
Фу.с:
void f(void); int x = 15213; int y = 15212; int main() { f(); printf("x = 0x%x y = 0x%x n", x, y); return 0; }
Бар.с:
double x; void f() { x = -0.0; }
которые скомпилированы с помощью gcc -o foobar foo.c bar.c и дают следующий вывод:
x = 0x0 y = 0x80000000
Вау. Таким образом, поскольку int равен 4 байтам, а double-8 байтам (в любом случае в моей системе), а сильный символ x находится в Foo.c, x присваивается шестнадцатеричное представление -0.0, что, в свою очередь, тоже перезаписывает y!
Поэтому я хотел бы узнать об этом больше. Почему здесь не применяются стандартные меры предосторожности типа C? Записывается ли 0x0000000080000000 вслепую туда, где x хранится в ELF, а y оказывается рядом с ним и перезаписывается? Не стесняйтесь быть как можно более подробным.
Ответ №1:
Вы заметили, что это предупреждение появляется при компиляции.
/usr/bin/ld: Предупреждение: выравнивание 4 символа «x» в /tmp/cciNZgVG.o меньше, чем 8 в /tmp/cc8pYw6O.o
Это неопределенное поведение. Не игнорируйте предупреждения. И он выдает ответ, основанный на значении, которое вы присваиваете.
Ответ №2:
Вы нарушаете «Правило одного определения«.
Внешнее определение-это внешнее объявление, которое также является определением функции (отличной от встроенного определения) или объекта. Если идентификатор, объявленный с внешней связью, используется в выражении (кроме как часть операнда оператора sizeof или _Alignof, результатом которого является целочисленная константа), где-то во всей программе должно быть ровно одно внешнее определение идентификатора; в противном случае должно быть не более одного.
что приводит к неопределенному поведению.В основном у вас есть 2 определения объекта x
, и объявление также варьируется.В стандарте говорится
Все объявления, которые ссылаются на один и тот же объект или функцию, должны иметь совместимый тип; в противном случае поведение не определено.
Примечание: %x
используется для печати беззнакового int в шестнадцатеричном формате без знака, и попытка распечатать отрицательное число с помощью %x
приводит к неопределенному поведению.
Использование неправильного спецификатора формата приводит к неопределенному поведению.
Комментарии:
1. %x использовался для отображения байтов, составляющих x и y. Дело в том, что y перезаписывается. Попробуйте использовать %d или что-то еще.