Глобальные переменные

Глобальная переменная — это переменная, объявленная вне функции. Время её жизни совпадает со временем работы программы. Этим глобальная переменная отличается и от локальной (обычной) переменной функции, которая существует только до окончания работы функции, так и от динамических переменных, созданных вызовом типа malloc, которые доступны до вызова free.

Как использовать

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

    int global_counter = 0; /* Объявление */

    void process_new_request() {
        global_counter += 1; /* увеличили счетчик запросов */

        /*
        ** что-то делаем
        */
    }    

Важно! В функции не должно быть объявлено локальной ("обычной") переменной с тем же именем, что и глобальная переменная. Если это сделать, то локальная переменная "скроет" глобальную.

    int global_counter = 0;

    void process_unncounted_request() {
        int global_counter = 120;
        global_counter += 1; /* Изменяется значение ЛОКАЛЬНОЙ переменной! */
    }
Следует учесть, что в такой ситуации компилятор не выдает сообщений об ошибке или предупреждение.

Начальное значение глобальной переменной задаётся при её объявлении.

Глобальные переменные доступны по всех функциях, удовлетворяющих следующим условиям:

  1. функция реализована в том же файле, где объявлена переменная, но ниже объявления;
  2. перед функцией, или "внутри" функции, есть декларация вида extern typename varname;.
Пример первого варианта мы уже видели выше. Обычно глобальные переменные объявляются в верхней части файла, после директив #include, а значит доступны во всех функциях файла.

Декларация нужна при раздельной компиляции. Например, в одном файле, пусть он называется process.c, реализована функция process_new_request, а в файле data.c объявлены переменные. Тогда файл process.c может выглядеть следующим образом.

    extern int global_counter;

    void process_new_request() {
        global_counter += 1;
    }    
Декларация extern аналогична прототипу функции и означает, что где-то (в каком-то .с файле) будет объявлена переменна типа int с именем global_counter.

Файл data.c может содержать одну строчку.

int global_counter = 0;

Еще немного про инициализацию

Если начальное значение глобальной переменной является числовой константой, или каким-то другим "простым" значением, то её объявление имеет простую форму. А можно в качестве начального значения использовать результат вычисления функции? Например, так:


    #include <math.h>
      
    double sin_at_one = sin(1.0);
    
Можно.

В какой момент происходит присвоение начального значения переменной? До её первого использования. То есть функция, вызов которой стоит в правой части объявления глобальной переменной, может быть выполнена в любой момент между запуском программы и первым обращением к глобальной переменной. Возможно, что функция будет вызвана до начала main!

Обязательно ли присваивать начальное значение при объявлении? Нет. Но тогда нужно быть уверенным, что переменной было присвоено значение до её первого использования. Это можно сделать в функции main, или же написать специальную функцию init, которая вызывается в подходящий момент (да-да, в main).

Глобальные переменные для одного файла

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

Рассмотрим такой пример.


    #include <stdio.h>
      
    static int global_counter;

    void process_new_request() {
        global_counter += 1;
    }

    void show_requests_statistics(FILE *stream) {
       fprintf(stream, "We have processed %d requests so far!\n", global_counter);
    }
    
При таком объявлении переменной global_counter её имя недоступно в других модулях трансляции (хорошо-хорошо, это просто другие файлы). Статические переменные, как и статические функции, доступны только в том файле, в котором они объявлены и реализованы.

Опасность глобальных переменных

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

"Глобальные переменные" функции

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

    #include <stdio.h>

    void useless_function(FILE *stream) {
       static int counter = 0; 
       fprintf(stream, "We have been called %d times!\n", ++counter);
    }    
При каждом новом вызове функции useless_function будет выводится сообщение с количеством вызовов.

Важно! Если функция рекурсивная, то все её "копии" работают с одной переменной.

Советы