Как избежать глобалов в этом случае (встроенный C)

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

Например, монитор батареи, в этом случае есть 4 функции, которым нужно прочитать или изменить переменную. У меня все эти функции используют переменную LowVoltage.

void Check_Voltage(){
  checks current voltage against LowVoltage
}

void Menu_Voltage(){
  a menu on the LCD screen to set the value of LowVoltage
}

void Save_LowVoltage(){
 runs after the settings menu is finished to save LowVoltage to EEPROM
}

void Load_LowVoltage(){
 reads EEPROM and sets LowVoltage at startup 
}
  • Check_Voltage() и Save_LowVoltage() должны читать LowVoltage.
  • Load_LowVoltage() нужно написать LowVoltage.
  • Menu_Voltage() должен читать и записывать LowVoltage.

Как я могу заставить это работать, не делая LowVoltage глобальным?? Нужно ли мне делать другую функцию для чтения или записи LowVoltage? Что-то вроде этого:

unsigned int Low_Voltage(short Get, unsigned int Value){
  static unsigned int LowVoltage;

  if(Get) return LowVoltage;
  else LowVoltage= Value;
}

Или есть лучшие способы сделать это? Я думаю, должны быть :) Я недавно читал о структурах, но, честно говоря, я не совсем понимаю их, и я даже не уверен, что это поможет мне в таких случаях?


person Benno    schedule 30.03.2015    source источник
comment
эти функции используются одновременно?   -  person moffeltje    schedule 30.03.2015
comment
Не путайте глобальные переменные, которые являются переменными области видимости файла, доступными для всей программы, с переменными области видимости частного файла, которые видны только в файле, в который вы их помещаете. большую часть времени хорошая практика программирования.   -  person Lundin    schedule 30.03.2015
comment
@moffeltje, нет, они никогда не используются одновременно   -  person Benno    schedule 30.03.2015


Ответы (3)


Существует несколько вариантов совместного использования переменной между функциями:

  • Разместите вашу переменную в статической памяти — это в значительной степени то, что делает ваш код. У вас есть два варианта: статическая функция, статическая единица перевода и глобальная.
  • Передать указатель на переменную в качестве параметра функции. Этот вариант требует передачи указателя в той или иной форме.
  • Использовать локальное хранилище потока с продуманной инициализацией. Этот вариант обычно недоступен при работе с микроконтроллерами; Я перечисляю его здесь для полноты.

В вашем случае я думаю, что использование статической переменной единицы перевода было бы уместно. Поместите реализации четырех функций в один файл C и объявите LowVoltage вверху как статическую переменную:

static unsigned int LowVoltage;

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

  • Все функции внутри модуля C "видят" эту переменную и могут свободно ею манипулировать.
  • Никакие другие функции за пределами модуля C не могут получить доступ к этой переменной. Они могут объявить свою собственную переменную LowVoltage, придав ей совершенно другое значение.
person Sergey Kalinichenko    schedule 30.03.2015
comment
Сделать его статической переменной области видимости файла и поместить все эти функции в модуль кода battery.h/battery.c, похоже, именно то, что должен делать OP. - person Lundin; 30.03.2015
comment
Спасибо dasblinkenlight! Что ж, это действительно то, о чем я думал, но когда у меня были одинаковые имена статических переменных в двух разных модулях C, они каким-то образом находились в одной области. Поскольку я только недавно узнал о статической переменной и ее области действия, я подумал, что неправильно ее понял. Но теперь я начинаю понимать, почему я часто вижу жалобы на MikroC... Я включил файл .c с помощью #include Может быть, если я включу файл .c с помощью менеджера проектов, он будет работать как надо... - person Benno; 30.03.2015

Два решения, которые я могу придумать

  1. Сделайте сигнатуры функций такими

    unsigned int Load_LowVoltage(unsigned int lowVoltage);
    

    а затем передать LowVoltage и присвоить ему возвращаемое значение, например

    LowVoltage = Load_LowVoltage(LowVoltage);
    
  2. Измените LowVoltage внутри функции и передайте указатель на исходный LowVoltage вот так

    void LowVoltage(unsigned int *lowVoltage)
     {
        *lowVoltage = modifiedValue;
     }
    

    тогда вы можете использовать его так

    Load_LowVoltage(&LowVoltage);
    

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

person Iharob Al Asimi    schedule 30.03.2015
comment
Однако это не дает частной инкапсуляции. Совершенно очевидно, что он кодирует какой-то драйвер батареи, и вызывающему абоненту не следует беспокоиться о внутренностях этого драйвера, которым, по-видимому, является эта переменная LowVoltage. - person Lundin; 30.03.2015

Вы можете создать структуру, содержащую все параметры батареи, например:

typedef struct {
  int low_voltage; 
  int voltage;
  int capacity;
  int temperature;
  ...
} BatteryData

В начале вашей программы вы выделяете для нее память и инициализируете члены некоторыми начальными значениями:

BatteryData *battery = malloc(sizeof(BatteryData));
battery->low_voltage = 0;
...

Затем вы передаете указатель на всю структуру функциям, которые устанавливают или считывают отдельные значения, например:

void Load_LowVoltage(BatteryData *battery){
 //reads EEPROM and sets LowVoltage at startup 
 int eeprom_val = get_low_voltage_from_eeprom();
 battery->low_voltage = eeprom_val;
}

Освободите структуру, когда она не нужна:

free(battery);
person baf    schedule 30.03.2015
comment
Я думаю, это плохая идея. Во встроенных системах лучше избегать использования динамического размещения. Однако, если вы должны использовать его, просто выделите для него память один раз и не освобождайте ее. Освобождение и повторное создание выделения много раз приведет к очень быстрому повреждению кучи в зависимости от обстоятельств. - person pfabri; 16.02.2017