2009-11-24 2 views
1

Как работает следующий пример использования указателя extern.Использование спецификатора класса «extern» класса хранения в C

Мы имеем глобальную переменную Int х и в файлах one.c и two.c Мы хотим, чтобы использовать их в three.c так объявили эту переменную в качестве three.c

extern int x;

Что произойдет, когда мы скомпилируем и соединим эти файлы?

Я отвечаю: компиляция всех этих файлов должна быть успешной, однако компоновщик должен помечать ошибку при связывании из-за нескольких объявлений x. Будет ли какая-либо разница в поведении на C++?

Является ли это любым способом ссылаться на int x (in three.c) одновременно из обоих файлов в C и C++. В C++, я думаю, мы можем использовать пространства имен, чтобы добиться этого. Правильно?

+0

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

+0

См. Также: http://stackoverflow.com/questions/1433204/what-are-extern-variables-in-c/ –

ответ

6

По умолчанию глобальные переменные имеют внешнюю связь , что означает, что они могут использоваться другими исходными файлами (или «единицами перевода»). Если вы вместо этого объявите свои глобальные переменные с ключевым словом static, у них будет внутренняя ссылка, то есть они не будут использоваться другими исходными файлами.

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

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

C++ - это точно то же самое, за исключением добавления пространств имен. Если глобальные переменные помещаются внутри пространства имен, они могут иметь одно и то же имя без ошибок компоновщика при условии, что они находятся в разных пространствах имен. Конечно, все ссылки на эти переменные затем должны либо ссылаться на полное имя namespace::var_name, либо использовать объявление using, чтобы установить локальный контекст пространства имен.

C++ также имеет анонимные пространства имен, которые полностью эквивалентны использованию ключевого слова static для глобальных переменных в C: все переменные и функции, объявленные внутри анонимного пространства имен, имеют внутреннюю связь.

Таким образом, чтобы ответить на ваш первоначальный вопрос, вы правы - компиляция будет успешной, но связь потерпит неудачу из-за несколько определений переменной x с внешним связыванием (в частности, от единиц перевода one.c и two.c).

От three.c, нет возможности одновременно обращаться к обеим переменным x. Вам нужно будет переименовать x в один или оба модуля или переключиться на C++ и поместить хотя бы один x в пространство имен.

+0

Отличный ответ Адам. Благодарю. – Ankur

+0

Как линкер знает, какие «единицы перевода» должны искать внешнюю переменную, является ли какая-то настройка или что-то еще? – marchinram

+0

@marchinram: Линкер знает, потому что во время ссылки компоновщик загружает все объектные файлы и библиотеки, которые одновременно связаны в память, а затем проходит через них и исправляет ссылки extern. Если он найдет 'var_name' в' a.o' и затем увидит, что 'b.o' ссылается на' var_name', он исправит заполнители в 'b.o', чтобы указать фактическое' var_name' в 'a.o'. Если 'var_name' не существует нигде, это ошибка (обычно называемая« нерешенной внешней ссылкой »или somesuch). –

0

Чтобы избежать генерации повторяющихся символов, вы должны объявить extern int x; в одном файле заголовка (а .h файл), а затем все .c файлы, которые будут использовать х #include, что заголовок файла и определить или инициализировать int x; в одном .c файлов.

+1

То есть, предполагая, что должна быть только одна переменная. Я думаю, что OP хочет две глобальные переменные с тем же именем, что довольно глупо, и я предполагаю теоретическое упрощение более сложного ошибочного дизайна. –

+0

Крис это не слишком необычно в производственном коде, где вы в конечном итоге используете код от стороннего поставщика или нескольких команд, работающих над продуктом с несколькими подсистемами. – Ankur

+1

Проблема такая же: у вас есть две глобальные переменные с внешней связью с тем же именем, и вам нужно использовать оба из них. И ответ на C, вы не можете. В C++ вы можете с пространствами имен, но тогда у них действительно нет одинакового имени, так почему бы просто не дать им новые имена и не беспокоиться о хлопотах в первую очередь? –

0

Возможно, вас могут заинтересовать ответы на this question.

Резюме: компоновщик может или не может связать файл. Это зависит от инициализации переменной. Это, безусловно, не удастся, если переменная имеет разные инициализации в разных файлах.

1

В C, вы можете сделать это:

// one.c 
static int x; 
int *one_x = &x; 

// two.c 
static int x; 
int *two_x = &x; 

// three.c 
extern int *one_x; 
extern int *two_x; 

Теперь вы можете обратиться однозначно к x в файле one.c или в x в файле two.c из файла three.c.

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

0

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

Смежные вопросы