Когда можно использовать глобальную переменную в C? - PullRequest
44 голосов
/ 07 октября 2008

Очевидно, что существует множество разных мнений, начиная от " Никогда! Всегда инкапсулируйте (даже если это просто макрос!) " до " Это не страшно - используйте их, когда это удобнее, чем нет."

Зв

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

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

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

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

-Adam

Ответы [ 15 ]

45 голосов
/ 07 октября 2008

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

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

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

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

15 голосов
/ 07 октября 2008

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

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

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

Бонус.

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

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

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

11 голосов
/ 07 октября 2008

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

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

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

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

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

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

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

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

8 голосов
/ 07 октября 2008

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

4 голосов
/ 07 октября 2008

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

3 голосов
/ 03 декабря 2010

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

Ошибочно полагать, что глобальные переменные являются обязательством в многопоточных приложениях, поскольку ЛЮБАЯ переменная, независимо от области действия, является потенциальной ответственностью, если она подвержена изменениям в более чем одном потоке.

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

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

2 голосов
/ 07 октября 2008

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

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

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

Я могу придумать несколько причин:

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

#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, но это еще один сценарий, в котором могут быть полезны глобальные переменные (чисто математические функции, которые должны хранить данные, чтобы избежать бесконечного использования)

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

globals полезны как типизированные константы, где функция где-то требует * int вместо int.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...