Подтвердить что ты не робот

Глобальная реализация переменных

Когда я пишу следующую программу:

файл 1:

#include <stdio.h>    
int global;    
void print_global1() {
        printf("%p\n", &global);
}

файл 2:

#include <stdio.h>
char global;    
void print_global2() {
        printf("%p\n", &global);
}

файл 3:

void print_global1();
void print_global2();
int main()
{
        print_global1();
        print_global2();

        return 0;
}

выход:

$ ./a.out
0x804a01c
0x804a01c

Вот мой вопрос:

  • Почему компоновщик реализует "int global" и "char global" как одну и ту же глобальную переменную:
  • Почему компилятор не жалуется (не самое маленькое предупреждение с -Wall -Wextra -ansi...)
  • Как управляется размер управляемой глобальной переменной (размер int и char различаются)

PS: Второй вопрос связан с архитектурой/компилятором, поэтому давайте возьмем gcc или Visual С++ (для C) с размером int 32 бита

EDIT: ЭТО НЕ ВОПРОС ДЛЯ С++, но для C!

Я использую gcc версии 4.4.1 и на Ubuntu 9.10. Вот вывод консоли компиляции:

$ ls
global_data1.c  global_data2.c  global_data.c

$ gcc -Wall -Wextra -ansi global_data*.c
$ ./a.out
0x804a01c
0x804a01c
or 
$ gcc -Wall -Wextra -ansi -c global_data*.c
$ gcc -Wall -Wextra -ansi global_data*.o
$ ./a.out
0x804a01c
0x804a01c
4b9b3361

Ответ 1

gcc не сообщает об ошибках/предупреждениях. Но g++ делает.

EDIT:

Похоже, C позволяет предварительные определения для переменной.

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

Измените файл2 на:

char global = 1; // no more tentative...but explicit.

Теперь, если вы скомпилируете, как и раньше, пробный def в файле1 будет проигнорирован.

Сделайте def def явным:

int global = 1; // in file1

char global = 1; // in file2

теперь ничто не может быть проигнорировано, и мы получаем множественную ошибку def.

Ответ 2

Это связано с тем, что называется "предварительным определением" в C. Сначала, если вы назначаете global в файлах file1 и file2, вы получите сообщение об ошибке C. Это связано с тем, что global не определяется заранее в file1 и file2, это действительно определено.

Из стандарта C (ударный удар):

Объявление идентификатора для объекта с областью файлов без инициализатора и без спецификатора класса хранения или с помощью статического элемента класса хранения представляет собой предварительное определение. Если единица перевода содержит одно или несколько предварительных определений для идентификатора, а единица перевода не содержит внешнего определения для этого идентификатора, то поведение в точности совпадает с тем, что единица перевода содержит объявление области файла этого идентификатора, причем составной тип как конца блока перевода, причем инициализатор равен 0.

В вашем случае "единица перевода" (в основном) каждый исходный файл.

О "составных типах":

Идентификатор с внутренней или внешней связью, объявленный в области, в которой предшествующий объявление этого идентификатора видимо, если в предыдущем объявлении указаны внутренние или внешняя связь, тип идентификатора в более позднем объявлении становится составным тип.

Подробнее о предварительных определениях см. этот вопрос и его ответы.

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

GNU ld имеет опцию --warn-common, которая предупреждает вас о нескольких предварительных определениях (общий символ - это имя компоновщика для условно определенных переменных):

$ gcc -Wl,--warn-common file*.c
/tmp/ccjuPGcq.o: warning: common of `global' overridden by larger common
/tmp/ccw6nFHi.o: warning: larger common is here

Из руководство:

Если для переменной есть только (один или несколько) общих символов, то она попадает в неинициализированную область данных выходного файла. Компилятор объединяет несколько общих символов для одной и той же переменной в один символ. Если они имеют разные размеры, он выбирает самый большой размер. Компонент превращает общий символ в объявление, если есть определение той же переменной.

Параметр --warn-common может выдавать пять видов предупреждений. Каждое предупреждение состоит из пары строк: первый описывает только что встреченный символ, а второй описывает предыдущий символ, встречающийся с тем же именем. Один или оба этих символа будут общим символом.

Ответ 3

Какой компилятор вы используете. Что такое платформа? С g++ я получаю

/tmp/cc8Gnf4h.o:(.bss+0x0): multiple definition of `global'
/tmp/ccDQHZn2.o:(.bss+0x0): first defined here
/usr/bin/ld: Warning: size of symbol `global' changed from 4 in a.o to 1 in b.o

AFAIR, в С++ переменные в разных единицах перевода имеют точно такое же объявление для работы.

Ответ 4

Компонент позволяет использовать дубликаты внешних данных (хотя я удивлен, что разные типы не создают проблемы). Какой из них вы получаете, зависит от порядка ваших объектных файлов в командной строке ссылки.