Когда можно использовать глобальную переменную в C?

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

Итак.

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

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

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

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

-Adam

Ответ 1

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

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

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

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

Ответ 2

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

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

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

Bonus.

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

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

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

Ответ 3

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

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

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

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

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

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

Ответ 4

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

Ответ 5

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

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

Ответ 6

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

Ответ 7

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

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

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

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

Ответ 8

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

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

Ответ 9

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

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

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

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

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

Ответ 10

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

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

Ответ 11

Когда вы объявляете константы.

Ответ 12

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

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

#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.

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

Ответ 13

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

Ответ 14

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

Ответ 15

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

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

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