2008-10-06 4 views
36

По-видимому, существует много разнообразия в мнениях, начиная от «Никогда! Всегда инкапсулируйте (даже если это просто макрос!)« до »Это неважно - используйте их, когда это удобнее, чем нет "Когда можно использовать глобальную переменную в C?

So.

Конкретные, конкретные причины (желательно с примером)

  • Почему глобальные переменные опасны
  • Когда глобальные переменные должны быть использованы вместо альтернатив
  • Какие альтернативы существуют для тех, кто соблазн неправильно использовать глобальные переменные

Хотя это субъективно, я выберу один анвс er (что для меня лучше всего представляет отношения любви/ненависти, которые каждый разработчик должен иметь с глобалами), и сообщество будет голосовать за них чуть ниже.

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

-Adam

ответ

39

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

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

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

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

7

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

0

Я здесь, в лагере «никогда»; если вам нужна глобальная переменная, по крайней мере используйте singleton pattern. Таким образом, вы пожинаете плоды ленивого экземпляра, и вы не загромождаете глобальное пространство имен.

+2

Хотя я не согласен, имейте в виду, что здесь сосредоточен C - нет классов. Можете ли вы показать, как реализовать синглтон в C? Одним из основных преимуществ программирования OO является C-тип такого дизайна ... – 2008-10-06 20:53:34

+0

Синглтоны не являются ответом на глобальные переменные, они позволяют программистам изменять стиль декларации в то же время ленивый (плохой) с пожизненное управление. Синглтоны вызывают у меня больше проблем, чем глобальные переменные. – Torlack 2008-10-06 20:56:59

+0

@Adam Davis: функция, которая возвращает экземпляр, который хранится в глобальной переменной с областью файлов? Хотя я не уверен, что мне это нравится лучше, чем простая глобальная переменная. – aib 2011-08-04 16:23:43

1

Это инструмент, как и любой другой, обычно злоупотребляющий, но я не думаю, что они злые.

Например, у меня есть программа, которая действительно действует как онлайн-база данных. Данные хранятся в памяти, но другие программы могут манипулировать им. Существуют внутренние процедуры, которые действуют как хранимые процедуры и триггеры в базе данных.

Эта программа содержит сотни глобальных переменных, но если вы думаете об этом, то что такое база данных, но огромное количество глобальных переменных.

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

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

2

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

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

4

Я приехал из лагеря «никогда», пока не начал работать в оборонной промышленности. Существуют некоторые отраслевые стандарты, которые требуют, чтобы программное обеспечение использовало глобальные переменные вместо динамического (malloc в памяти C). Мне нужно переосмыслить свой подход к распределению динамической памяти для некоторых из проектов, над которыми я работаю. Если вы можете защитить глобальную память с помощью соответствующих семафоров, потоков и т. Д., То это может быть приемлемым подходом к управлению вашей памятью.

1

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

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

5

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

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

10

Рассмотрите этот коан: «если область достаточно узкая, все глобально».

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

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

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

На самом деле я мог смело утверждать, что если программа не такая маленькая, то глобальные переменные должны быть незаконными.

  • Если переменная никогда не изменится, то это константа, а не переменная.
  • Если переменная требует универсального доступа, то для ее получения и настройки должны существовать две подпрограммы, и они должны быть синхронизированы.
  • Если программа запускается небольшим образом и может быть больше позже, тогда введите код, как если бы программа была большой сегодня и отменила глобальные переменные. Не все программы будут расти! (Хотя, конечно, предполагается, что программист захочет иногда выбросить код.)
15

Единственный способ заставить глобальные переменные работать, чтобы дать им имена, которые гарантируют, что они уникальны.

Это имя обычно имеет префикс, связанный с некоторым «модулем» или набором функций, для которых глобальная переменная особенно сфокусирована или имеет смысл.

Это означает, что переменная «принадлежит» этим функциям - это их часть. Действительно, глобальный обычно может быть «завернут» с небольшой функцией, которая сочетается с другими функциями - в том же .h файл с одним и тем же именем.

Бонус.

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

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

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

0

Глобальные константы полезны - вы получаете больше безопасности по типу, чем препроцессорные макросы, и все равно так же легко изменить значение, если вы решите, что вам нужно.

Глобальные переменные имеют некоторые виды использования, например, если операция многих частей программы зависит от конкретного состояния на конечной машине. Пока вы ограничиваете количество мест, которые могут ИЗМЕНИТЬ ошибки отслеживания переменных, связанные с этим, не так уж плохо.

Глобальные переменные становятся опасными почти сразу после создания нескольких потоков.В этом случае вам действительно необходимо ограничить область (не более) файлом global (путем объявления его статической) переменной и методами getter/setter, которые защищают его от множественного доступа, если это может быть опасно.

3

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

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

Используйте глобальные переменные экономно. Структуры данных должны использоваться по возможности для организации и изоляции использования глобального пространства имен.

Переменная область использует программистов очень полезную защиту - но это может стоить. Сегодня я стал писать о глобальных переменных, потому что я опытный программист Objective-C, который часто разочаровывается в объектно-ориентированных местах барьеров при доступе к данным. Я бы сказал, что антиглотулярное фанатизм происходит в основном от более молодых программистов, ориентированных на теорию, которые испытывают главным образом объектно-ориентированные API в изоляции без глубокого практического опыта API-интерфейсов уровня и их взаимодействия в разработке приложений. Но я должен признать, что я расстраиваюсь, когда поставщики используют пространство имен вяло. Несколько дистрибутивов Linux имели, например, «PI» и «TWOPI», например, которые сильно нарушили мой личный код.

1

Я могу назвать несколько причин: целей

отладки/тестирования (предупреждение - не проверял этот код):

#include <stdio.h> 
#define MAX_INPUT 46 
int runs=0; 
int fib1(int n){ 
    ++runs; 
    return n>2?fib1(n-1)+fib1(n-2):1; 
}; 
int fib2(int n,int *cache,int *len){ 
    ++runs; 
    if(n<=2){ 
     if(*len==2) 
      return 1; 
     *len=2; 
     return cache[0]=cache[1]=1; 
    }else if(*len>=n) 
     return cache[n-1]; 
    else{ 
     if(*len!=n-1) 
      fib2(n-1,cache,len); 
     *len=n; 
     return cache[n-1]=cache[n-2]+cache[n-3]; 
    }; 
}; 
int main(){ 
    int n; 
    int cache[MAX_INPUT]; 
    int len=0; 
    scanf("%i",&n); 
    if(!n||n>MAX_INPUT) 
     return 0; 
    printf("fib1(%i)==%i",n,fib1(n)); 
    printf(", %i run(s)\n",runs); 
    runs=0; 
    printf("fib2(%i)==%i",n,fib2(n,&cache,&len)); 
    printf(", %i run(s)\n",runs); 
    main(); 
}; 

Я использовал контекстные переменные для fib2, но это еще один сценарий, в котором глобальные переменные могут быть полезны (чистые математические функции, которые должны хранить данные, чтобы избежать их навсегда).

программы, которые используются только один раз (например, для участия в конкурсе), или когда необходимо время разработки укоротить

Глобалы полезны в качестве типизированных констант, где функция где-то требует * Int вместо междунар.

Я вообще избегаю глобальных координат, если намерен использовать программу более одного дня.

1
  • Если не использовать: Глобальные переменные опасны, потому что единственный способ когда-либо знать, как глобальная переменная изменена, чтобы проследить весь исходный код внутри .c файла, в течение которого они были объявлены (или, все .c файлы, если это также extern). Если ваш код неисправен, вам нужно искать весь исходный файл (ы), чтобы увидеть, какие функции меняют его и когда. Это кошмар для отладки, когда он идет не так. Мы часто воспринимаем как должное изобретательности позади концепции локальных переменных грациозно, выходящих за рамки - легко проследить
  • Когда использовать: глобальные переменные должны использоваться, когда его использование не слишком замаскированы и где стоимость использования локальных переменных является чрезмерно сложным до того момента, когда он ставит под угрозу удобочитаемость.Под этим я подразумеваю необходимость добавления дополнительного параметра для аргументов функции и возврата и прохода указателей вокруг, среди прочего. Три классических примера: когда я использую pop и push stack - это разделяется между функциями. Конечно, я мог бы использовать локальные переменные, но тогда мне пришлось бы передавать указатели в качестве дополнительного параметра. Второй классический пример можно найти в K & R «Язык программирования C», где они определяют функции getch() и ungetch(), которые совместно используют массив массивов символов. Еще раз, нам не нужно делать его глобальным, но добавленная сложность стоит того, когда его довольно сложно испортить использование буфера? Третий пример - это то, что вы найдете во встроенном пространстве среди любителей Arduino. Множество функций в основной петле функции все разделяют функцию millis(), которая является мгновенным временем срабатывания функции. Поскольку тактовая частота не бесконечна, millis() будет отличается от в пределах одного цикла. Чтобы сделать его постоянным, сделайте снимок времени до для каждого цикла и сохраните его в глобальной переменной. Теперь моментальный снимок будет таким же, как при доступе к множеству функций.
  • Альтернативы: Не так много. Придерживайтесь локального охвата как можно больше, особенно в начале проекта, а не наоборот. По мере роста проекта и если вы чувствуете, что сложность может быть снижена с использованием глобальных переменных, сделайте это, но только если она удовлетворяет требованиям пункта 2. И помните, что использование локальной области и более сложный код - это меньшее зло по сравнению с безответственным использованием глобальных переменных.