Из стандарта C99 6.2.3:
Если объявление идентификатора объекта имеет область действия файла и не содержит спецификатора класса хранения, его связь является внешней.
и 6,7
Объявление определяет интерпретацию и атрибуты набора идентификаторов. Определение идентификатора — это объявление для этого идентификатора, которое:
— for an object, causes storage to be reserved for that object; — for a function, includes the function body;99) — for an enumeration constant or typedef name, is the (only) declaration of the identifier.
К сожалению, я не нашел дальнейшего описания того, когда компилятор должен рассматривать внешнее объявление как определение (что означает, что тип должен быть полным и вычисляется размер хранилища).
Поэтому я провел несколько экспериментов. Сначала я заметил, что:
struct A a;
int main() {
}
недействителен, gcc говорит, что тип A неполный и не знает, как выделить память для a. Однако, что интересно, у нас есть следующий правильный код:
struct A a;
int main() {
}
struct A {int x;};
Это также разумно, так как тип A завершается в конце файла. Из двух приведенных выше примеров мы можем сделать вывод, что внешнее объявление проверяется в конце области действия файла. (Все еще не знаю, где об этом говорится в стандарте)
Однако объявление массива является исключительным. Измененный код больше не действителен:
struct A a[1];
int main() {
}
struct A {int x;};
И стандарт C99 говорит об этом, он говорит, что элементы массива должны быть завершенного типа. Итак, возникает вопрос: является ли struct A a[1]
определением или декларацией? Не спешите отвечать на него. Проверьте следующие примеры.
Здесь у нас есть два файла: a.c
и b.c
. В a.c
:
#include <stdio.h>
int arr[10];
void a_arr_info() {
printf("%lu at %lx\n", sizeof arr, (size_t)arr);
}
в то время как в b.c
:
#include <stdio.h>
int arr[20];
void b_arr_info() {
printf("%lu at %lx\n", sizeof arr, (size_t)arr);
}
int main() {
a_arr_info();
b_arr_info();
}
Результат потрясающий. Вывод показывает, что arr
в обоих файлах относится к одному и тому же адресу. Это можно понять, потому что arr
оба находятся в области действия файла, поэтому они являются внешней связью. Проблема в том, что они имеют разный размер. В каком файле компилятор принял объявление как определение и выделил память?
Почему я спрашиваю об этом? Потому что я работаю над проектом упрощенного компилятора C (домашняя работа по курсу). Так что мне может быть важно это выяснить. Хотя домашнее задание не заходит так далеко, мне очень любопытно, и я хотел бы узнать больше. Спасибо!
a.x
изmain()
. - person M.M   schedule 08.04.2014a.c
иb.c
представляют собой совершенно отдельную проблему от предварительных определений ранее, возможно, измените это на два разных сообщения с вопросами. Вa.c
иb.c
поведение не определено, потому что у вас есть два внешне видимых объекта с одинаковыми именами. - person M.M   schedule 08.04.2014