Есть ли недостатки в объявлении переменных в файлах заголовков без ключевого слова extern?

Недавно я начал понимать, что использование ключевого слова extern очень приветствуется. Таким образом, я начал задаваться вопросом, есть ли что-нибудь не так с текущим (без внешнего) способом использования файлов заголовков:

main.c:

#include "main.h"
#include "function.h"

int main(void){
    globalvariable = 0;

    testfunction();
    return 0;
}

main.h:

#ifndef MAIN_H_
#define MAIN_H_

int globalvariable;

#endif /* MAIN_H_ */

function.c:

#include "main.h"
#include "function.h"

void testfunction(){
    globalvariable++;
    return;
}

function.h:

#ifndef FUNCTION_H_
#define FUNCTION_H_

void testfunction(void);

#endif /* FUNCTION_H_ */

Таким образом, каждый новый исходный файл, которому требуется доступ к globalvariable, просто должен включать main.h.

Одним из очевидных недостатков этого метода являются массивы: вы не можете использовать форматирование {element0, element1, ...} для присвоения значений массиву после его объявления.

Кстати, когда я даю globalvariable начальное значение, равное нулю, я определяю его в этот момент? Или память уже выделена раньше?

Кроме того, есть ли официальный термин для метода, который я использую?


person SharpHawk    schedule 09.04.2012    source источник
comment
Ключевое слово extern не приветствуется; вы должны использовать его только в случае крайней необходимости (чего, надеюсь, никогда не будет).   -  person Oliver Charlesworth    schedule 10.04.2012
comment
Глобальные переменные плохие.   -  person Fred Larson    schedule 10.04.2012
comment
Интересный. Из предыдущих обсуждений у меня возникло ощущение, что extern был де-факто способом делать что-то.   -  person SharpHawk    schedule 10.04.2012
comment
Что ж, цитата из верхнего cnicutar, связанного с (который я прочитал перед тем, как опубликовать свой вопрос), гласит: Хотя есть и другие способы сделать это, чистый и надежный способ объявления и определения глобальных переменных - использовать файл заголовка file3 .h, чтобы содержать внешнее объявление переменной. Оли, вы говорите, что есть лучший способ объявления и определения глобальных переменных?   -  person SharpHawk    schedule 10.04.2012
comment
@SharpHawk: Нет (нет). Я имею в виду, что вам следует избегать глобальных переменных, совместно используемых несколькими единицами перевода ...   -  person Oliver Charlesworth    schedule 10.04.2012
comment
Ах хорошо. Отсутствие классов в C было основной причиной, по которой я использовал глобальные переменные, которые я использовал.   -  person SharpHawk    schedule 10.04.2012


Ответы (5)


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

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

Чтобы ответить на ваш вопрос о распределении, память для всех глобальных объектов выделяется до запуска программы. Глобальная переменная занимает место, даже если она используется только в части кода, который никогда не запускается. Ваша строка globalvariable = 0; на самом деле не дает переменной начальное значение. Компилятор C гарантирует, что все неинициализированные глобальные переменные автоматически инициализируются нулем при загрузке программы. Ваш код технически переназначает значение переменной. Если вы хотите убедиться, что глобал инициализирован конкретным значением, добавьте инициализатор к определению, например int globalvariable = 42;.

person bta    schedule 09.04.2012
comment
Я разрывался между этим ответом и ответом Р. Этот выиграл благодаря ответам на все части моего исходного сообщения. - person SharpHawk; 10.04.2012

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

person Chris Dodd    schedule 09.04.2012

extern int globalvariable;

это декларация

int globalvariable;

одновременно декларация и предварительное определение.

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

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

person R.. GitHub STOP HELPING ICE    schedule 09.04.2012
comment
Что вы подразумеваете под использованием последнего в файле заголовка, который включен более чем в одну единицу перевода? Будет ли количество файлов заголовков включать в себя несколько исходных файлов? Если да, то именно это я и делаю, и, как вы заявили в последней строке, компилятор не жалуется. Фактически, он даже не выдает предупреждения. - person SharpHawk; 10.04.2012
comment
Правильно. Компилятор не может жаловаться, потому что каждая отдельная единица перевода действительна; только компоновщик может жаловаться, и это не так, потому что это принято в unix. Это все еще не делает его действительным C, и он не делает то, что вы хотите. (Он сворачивает несколько определений переменной в одно во время компоновки, а не объявляет, что вы хотите получить доступ к переменной, определенной в другой единице перевода.) - person R.. GitHub STOP HELPING ICE; 10.04.2012

Недостатком вашего кода является то, что вы, вероятно, получите ошибку компоновщика. 1


1. Где "вероятно" основано на идее построения вашего кода на большом количестве различных наборов инструментов.

person Oliver Charlesworth    schedule 09.04.2012
comment
Вы действительно думаете, что я бы продолжил так поступать, если бы это вызвало ошибки компоновщика? - person SharpHawk; 10.04.2012
comment
@SharpHawk: Вы пробовали, скажем, MSVC? - person Oliver Charlesworth; 10.04.2012
comment
Нет, я использовал его только в AVR Studio 5 (по общему признанию, ужасная программа). - person SharpHawk; 10.04.2012

Стандарт ANSI C говорит

Если объявление идентификатора для объекта имеет область действия файла и не имеет спецификатора класса хранения, его связь является внешней.

person Inquisitive    schedule 20.07.2014