Способ избежать глобальных переменных в C

#c #global-variables

#c #глобальные переменные

Вопрос:

Предположим, у меня есть переменная counter . Если мне нужно:

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

является ли это решение адекватным или существуют более эффективные / более чистые способы сделать это?

 int counter_access(int value) {
    static int counter = 0;

    if (value > 100) {
        printf("there is a bug in the code");
        return counter;
    }

    counter  = value;
    return counter;
}
 

А затем, когда мне нужно изменить переменную:

 counter_access(10); /* increase counter by 10 */
counter_access(-2); /* decrease counter by 2 */
 

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

 if (counter_access(0) == 100) do_something();
 

Это решение кажется мне довольно запутанным. Однако я не могу придумать очень много хороших способов сделать это. Я мог бы использовать глобальные переменные (которые вызывают ошибки). Я мог бы передать адрес counter функциям, которые в нем нуждаются, но это не гарантирует, что переменная не будет изменена неправильным образом (в приведенном выше примере, если counter значение увеличивается более чем на 100, возникает ошибка).

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

Комментарии:

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

2. Ваши примеры показывают только константу , переданную counter_access() . Всегда ли передаваемое значение является константой?

Ответ №1:

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

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

У вас может быть что-то вроде:

 #define COUNT_OVERFLOW            -1
#define COUNT_UNDERFLOW           -2

#define counter_get()    counter_add(0)

int counter_add(int incr) 
{ 
    static int counter = 0;
    int counter_temp;

    counter_temp = counter  incr;

    if (counter_temp < 0)    return COUNT_UNDERFLOW;
    if (counte_temp > 100)   return COUNT_OVERFLOW;

    counter = counter_temp;
    return counter;
}
 

Теперь, чтобы обнаружить ошибку, вы можете проверить, является ли возвращаемое значение <0:

 cnt = counter_add(x);
if (cnt < 0) {
  fprintf(stderr,"There is a bug in the coden");
}
....
if (counter_get() == 100) {
  printf("DONE!n");
}
 

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

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

Как упоминалось ранее, в более сложных случаях вам не следует использовать статические переменные (или, что эквивалентно, глобальные переменные). В этих случаях правильный способ — иметь a struct , который устанавливается для каждого потока и сохраняет переменные, которые имеют отношение к этому состоянию потока. Вам нужно будет передать указатель на эту структуру и заставить counter_access() функцию принимать ее в качестве параметра.

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