C ld ошибка времени связи с глобальными переменными

#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 или что-то еще.