|
Использование глобальных переменных |
|
|
2 страниц
1 2 >
|
 |
Ответов
(1 - 28)
|
Jan 9 2017, 20:28
|
Частый гость
 
Группа: Свой
Сообщений: 113
Регистрация: 25-10-07
Из: Краснодар
Пользователь №: 31 725

|
Цитата(demiurg1978 @ Jan 9 2017, 21:26)  Хочу вывести переменные из static в глобальные. Ваши за и против. Плохая практика. Программа быстро превращается в это
|
|
|
|
|
Jan 9 2017, 20:33
|
Местный
  
Группа: Участник
Сообщений: 333
Регистрация: 19-12-13
Из: Новосибирск
Пользователь №: 79 709

|
Цитата(desh @ Jan 10 2017, 02:28)  ... Скажу честно, с английским настолько не дружу.
|
|
|
|
|
Jan 10 2017, 01:59
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(demiurg1978 @ Jan 9 2017, 20:26)  Хочу вывести переменные из static в глобальные. Ваши за и против. В Вашем случае, когда Вы сам себе хозяин это не имеет никакого значения, кроме личных предпочтений. Само ключевое слово static появилось, при появлении второго программиста. Первый не хотел, чтобы другой использовал его глобальные переменные, вот и прятал их посредством статика. Что касается функций установки и чтения значений переменных, то у меня такая же история: терпения на их написание не хватает и я обычно в приватную секцию вставляю "public:". Но даже в этом случае для тех переменных, тип или значения которых могут измениться в будущем, функции (или псевдофункции) прописываю сразу.
Сообщение отредактировал aiwa - Jan 10 2017, 02:00
|
|
|
|
|
Jan 10 2017, 03:18
|
Местный
  
Группа: Свой
Сообщений: 279
Регистрация: 2-07-08
Из: Новосибирск
Пользователь №: 38 699

|
Цитата(demiurg1978 @ Jan 10 2017, 01:26)  static переменные. использование в других модулях посредством set_value (); get_value ();. Но в нынешнем проекте у меня много параметров. И если честно, я заколебался на каждую переменную писать свои функции установки и получения переменных. Смысла в простых get/set функциях, тупо устанавливающих значения локальных переменных нет. Проще использовать глобальные переменные. get/set функции нужны в том случае, если производится какая-то дополнительная работа, помимо установки/возврата переменной. Например Код int set_age(int age) { if (age>=0 && age <=100) { local_age = age; return 0; }
perror("incorrect age!"); return -1; } К вопросу о "много параметров", может есть смысл объединить их в структуры?
|
|
|
|
|
Jan 10 2017, 06:30
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(demiurg1978 @ Jan 9 2017, 21:26)  Хочу узнать мнение насчет использования глобальных переменных. Спрашиваю вот почему: неоднократно слышал, что использование глобальных переменных нужно максимально минимизировать. Откуда пошло это? В данный момент пишу проект. Свои проекты всегда стараюсь разделить на модули. static переменные. использование в других модулях посредством set_value (); get_value ();. Но в нынешнем проекте у меня много параметров. И если честно, я заколебался на каждую переменную писать свои функции установки и получения переменных. Хочу вывести переменные из static в глобальные. Ваши за и против. Проблема в связности. Если у вас все модули знают все про все другие модули - то связность высокая. Сложно понять, кто именно меняет данную переменную и по каким позывам. Сложно поменять алгоритм работы какого-то одного модуля - при этом придется менять все другие. Часто нельзя просто взять и записать в переменную значение - иногда это можно сделать только в определенных состояниях, иногда такая запись должна быть защищена критической секцией, иногда нужно проверить значение или выполнить какие-то действия до/после записи. Если все это размазать по разным модулям - очень легко где-то что-то забыть. Просто механически каждую переменную оборачивать в пару функций смысла нет. Но если модулю 1 нужны прям вот все переменные модуля 2, то у вас что-то не так с разделением на модули. Обычно модуль дает какой-то интерфейс, куда входят функции, и, возможно, какой-то набор глобальных переменных. Опять же, вот пусть у нас есть модуль "передатчик по порту". Если у нас появятся два порта и понадобятся два передатчика - как мы будем выкручиваться? Поэтому обычно тут так или иначе используют ООП - все переменные, которые требуются "передатчику" собираются в структуру, а каждая функция получает указатель на эту структуру (или к примеру числовой дескриптор). А вообще, дайте конкретный пример  По нему уже можно дать какие-то рекомендации Цитата(zltigo @ Jan 9 2017, 23:57)  К чему эти глупые страшилки не по делу. Это не то, чтобы страшилки. Обычно, если в коде много GOTO или функции на тысячи строк или все "потроха" торчат наружу, то с ним что-то не так  Хотя конечно возможны какие-то ситуации, когда все это оправдано
|
|
|
|
|
Jan 10 2017, 07:11
|
Местный
  
Группа: Участник
Сообщений: 333
Регистрация: 19-12-13
Из: Новосибирск
Пользователь №: 79 709

|
Цитата(Непомнящий Евгений @ Jan 10 2017, 12:30)  А вообще, дайте конкретный пример  По нему уже можно дать какие-то рекомендации Редактирование параметров в меню.
|
|
|
|
|
Jan 10 2017, 08:17
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(demiurg1978 @ Jan 10 2017, 10:11)  Редактирование параметров в меню. Можно сделать так, как выше предложил Сергей. У меня обычно параметры редактируются не только в меню, но и по интерфейсу (причем протоколы могут быть разные). Кроме того, на разных устройствах разные способы организации меню (разные дисплеи, где-то семисегментное табло). Поэтому у меня каждый параметр завернут в функцию доступа (для упрощения сделан мини-язык на макросах) и затем параметры всех модулей добавляются в общий массив (код параметра + функция доступа). Функция доступа получает специальный поток (откуда читается или куда пишется значение, значение может быть разных типов, а некоторые параметры имеют несколько значений) и требуемое действие (прочитать / записать / проверить допустимость значения). Возвращает успех или код ошибки. И далее уже имея такой массив, несложно организовать редактирование / отображение параметров через меню или по интерфейсу. Можно сделать перебор всех параметров, их сохранение в какой-то промежуточный формат и т.п.
|
|
|
|
|
Jan 10 2017, 11:24
|
Местный
  
Группа: Участник
Сообщений: 333
Регистрация: 19-12-13
Из: Новосибирск
Пользователь №: 79 709

|
Цитата(Непомнящий Евгений @ Jan 10 2017, 14:17)  ... Спасибо всем за ответы.Народ, не поделитесь примерами? Я всегда старался посматривать, как делают другие. Брать лучшее из примеров. Не постесняюсь сказать, что многому научился как раз на форумах. На ответах на мои вопросы и приведенных примерах.
Сообщение отредактировал demiurg1978 - Jan 10 2017, 11:26
|
|
|
|
|
Jan 11 2017, 06:05
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(demiurg1978 @ Jan 10 2017, 14:24)  Спасибо всем за ответы.Народ, не поделитесь примерами? Я всегда старался посматривать, как делают другие. Брать лучшее из примеров. Не постесняюсь сказать, что многому научился как раз на форумах. На ответах на мои вопросы и приведенных примерах. Готовый код я дать не могу - он писался для дяди, на плюсах и довольно запутан. Основная мысль, если переложить ее на Си, выглядит так ИнтерфейсCODE
enum ParamType { paramInt, paramUint, paramFloat, paramBytes, };
// Поток для чтения и записи значений // В простейшем случае это просто буфер, размер и тип значения в буфере // Если параметр может иметь несколько значений, то придется усложнить (скажем сделать односвязный список таких структур) // Если хочется иметь возможность делать произвольные действия (писать/читать сразу в формате протокола и т.п.), то тут должны быть указатели на функции struct ParamStream { void *buf; uint16_t size; ParamType type; bool error; // произошла ошибка чтения/записи? };
// это интерфейс к ParamStream, который позволяет читать/писать значение // конкретная реализация зависит от вида ParamStream uint8_t paramStream_readUI8(ParamStream *s) { // конвертирует содержимое потока в uint8_t; если это невозможно - устанавливает s->error } uint16_t paramStream_readUI16(ParamStream *s); ... uint16_t paramStream_readBytes(ParamStream *s, byte *buf, byte bufSize);
void paramStream_writeUI8(ParamStream *s, uint8_t v); ... void paramStream_writeBytes(ParamStream *s, const byte *buf, byte bufSize);
enum ParamResult { parres_OK, parres_BadValue, parres_NoParam, parres_NoAction, ... };
enum ParamAction { paract_Get, paract_Set, paract_Check, // проверить значение на допустимость без установки - бывает полезно в меню };
typedef ParamResult (*ParamAccessor)(ParamStream *s, ParamAction act);
struct ParamInfo { uint16_t code; ParamAccessor accessor; };
/// Эта функция для пользователей данной системы. Она ищет в глобальном массиве ParamInfo параметр с данным кодом (бинарный поиск к примеру) и вызывает его accessor ParamResult paramAccessor(uint16_t code, ParamStream *s, ParamAction act);
ИспользованиеКод // получить значение параметра 42 unsigned value; ParamStream stream = {.buf=&value, .size = sizeof(value), .type=paramUint}; paramAccessor(42, &stream, paract_Get);
// установить значение параметра 55 byte buf[10] = {1, 2, 3}; ParamStream stream = {.buf=&buf, .size = sizeof(buf), .type=paramBytes}; paramAccessor(55, &stream, paract_Set); Модуль 1Код int a, b;
// вычисляемый параметр только для чтения ParamResult param_42(ParamStream *s, ParamAction act) { switch(act) { case paract_Get: paramStream_writeUI8(s, a+b); return parres_OK; default: return parres_NoAction; } } Модуль 2Код byte buf[20];
// чтение/запись ParamResult param_55(ParamStream *s, ParamAction act) { switch(act) { case paract_Get: paramStream_writeBytes(s, buf, 20); return parres_OK; case paract_Set: paramStream_readBytes(s, buf, 20); return parres_OK; default: return parres_NoAction; } } Где-то нужен файл, который все функции доступа поместит в один массив (и в этот файл надо включить хидеры всех модулей с параметрами). Код ParamInfo paramInfo[] = { {42, param_42}, {55, param_55}, {0, 0} // терминатор Теперь у нас есть обобщенный механизм доступа к параметру любого типа по коду. Дальше все в наших руках - можно написать адаптер для modbus / etc, доступ к произвольному параметру через меню, выплевывание всех параметров по интерфейсу и т.п.
|
|
|
|
|
Jan 11 2017, 08:25
|
Местный
  
Группа: Участник
Сообщений: 333
Регистрация: 19-12-13
Из: Новосибирск
Пользователь №: 79 709

|
Цитата(Непомнящий Евгений @ Jan 11 2017, 12:05)  ИспользованиеКод // получить значение параметра 42 unsigned value; ParamStream stream = {.buf=&value, .size = sizeof(value), .type=paramUint}; paramAccessor(42, &stream, paract_Get);
// установить значение параметра 55 byte buf[10] = {1, 2, 3}; ParamStream stream = {.buf=&buf, .size = sizeof(buf), .type=paramBytes}; paramAccessor(55, &stream, paract_Set); В си разве возможна такая запись: ".type" ?
|
|
|
|
|
Jan 11 2017, 08:31
|
Гуру
     
Группа: Свой
Сообщений: 3 020
Регистрация: 7-02-07
Пользователь №: 25 136

|
Цитата(aiwa @ Jan 10 2017, 04:59)  Само ключевое слово static появилось, при появлении второго программиста. Первый не хотел, чтобы другой использовал его глобальные переменные, вот и прятал их посредством статика. Неправда. static позволяет не засорять глобальное пространство имён. Скажем, если в программе два десятка модулей, и в каждом - десяток своих переменных, то это уже 200 глобальных переменных. Кому нужен этот бардак? Цитата(demiurg1978 @ Jan 11 2017, 11:25)  В си разве возможна такая запись: ".type" ? Погуглите на тему "что нового в C99 по сравнению с C90". Там немало приятных плюшек добавилось.
|
|
|
|
|
Jan 11 2017, 09:50
|
Профессионал
    
Группа: Свой
Сообщений: 1 123
Регистрация: 8-03-09
Из: Днепр
Пользователь №: 45 848

|
Цитата(Сергей Борщ @ Jan 10 2017, 10:33)  Собрать все параметры в структуру (наверняка это уже сделано для удобства сохранения/чтения да и вообще для повышения читабельности кода). Передавать в редактор указатель на эту структуру. Можно создавать копию этой структуры, передавать в редактор указатель на копию и переписывать обратно из копии в основную структуру только после того, как пользователь подтвердит, что он действительно в своем уме. Тоже хотел квакнуть, но это уже сделали. Это промежуточный вариант к ООП. Каждый модуль как-бы "псевдо-клас" со своими данными в виде струкутуры, которая extern и видна глобально через 1 указатель. Позволяет сильно сэкономить на написании "extern".
|
|
|
|
|
Jan 11 2017, 12:52
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (demiurg1978 @ Jan 10 2017, 13:24)  Народ, не поделитесь примерами? примерно так: CODE bool cfg_editor::do_edit(bool from_last) { // return true to edit field auto always = [](cfg_cache &) { return true; }; auto dhcp_disabled = [](cfg_cache & cfg) { return cfg->Localhost.DHCP_enabled == false; };
struct { bool (*is_item_active)(cfg_cache &); cfg_editor_ui::result (*function)(cfg_editor *); } const Table[] = { #define HANDLER(edit_func, param_name, ...) [](cfg_editor * editor) { return editor-> edit_func (param_name , editor->Cfg-> __VA_ARGS__); } { always, HANDLER(edit_MAC, "MAC address ", Localhost.MAC_address) }, { always, HANDLER(choose_YN, "DHCP enabled " , Localhost.DHCP_enabled) }, { dhcp_disabled, HANDLER(edit_IP, "IP address ", Localhost.IP_address.addr) }, { dhcp_disabled, HANDLER(edit_IP, "Netmask ", Localhost.Netmask.addr) }, { dhcp_disabled, HANDLER(edit_IP, "Gateway ", Localhost.Gateway.addr) }, #if LWIP_DNS { dhcp_enabled, HANDLER(edit_IP, "DNS1 ", Localhost.DNS[0].addr) }, { dhcp_enabled, HANDLER(edit_IP, "DNS2 ", Localhost.DNS[1].addr) }, #endif { always, HANDLER(edit, "Hostname ", Localhost.Hostname) },
{ always, HANDLER(edit, "Telnet port ", Telnet.Port) }, { always, HANDLER(edit_password, "Telnet password (space = disabled)", Telnet.Password) },
{ always, HANDLER(edit, "Serial port baudrate ", Serial.Baudrate, 600, 115200) }, ......... };
size_t const Last_index = sizeof(Table) / sizeof(Table[0]) - 1; size_t Index = from_last ? Last_index : 0;
for(;;) { Console.new_line(); Repeat: switch(Table[Index].function(this)) { case result::CONSOLE_CLOSED: case result::TIMEOUT: return false;
case result::ABORTED: return true;
case result::KEY_UP: if(Index == 0) { Console.send('\r'); goto Repeat; } while(--Index && !Table[Index].is_item_active(Cfg)) ; break;
case result::JUST_ENTER: case result::OK: if(Index == Last_index) return true; while(++Index < Last_index && !Table[Index].is_item_active(Cfg)) ;
if(!Table[Index].is_item_active(Cfg)) // if last item inactive return true; break;
case result::KEY_DOWN: if(Index == Last_index) { Console.send('\r'); goto Repeat; } while(++Index < Last_index && !Table[Index].is_item_active(Cfg)) ; // last index inactive, go back to active if(Index == Last_index && !Table[Index].is_item_active(Cfg)) { while(--Index && !Table[Index].is_item_active(Cfg)) ; Console.send('\r'); goto Repeat;
} break; } } return true; // make eclipse parser happy }
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 12 2017, 12:39
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(scifi @ Jan 11 2017, 10:31)  Неправда. static позволяет не засорять глобальное пространство имён. Скажем, если в программе два десятка модулей, и в каждом - десяток своих переменных, то это уже 200 глобальных переменных. Кому нужен этот бардак? Ну бардак все-таки не хаос. Переменные видимы в определенном порядке и избыточное количество не критично: пусть лучше уж валяется в глобальном списке, чем тратить на нее семиразовое нажатие клавиш. И реальная необходимость возникла не из-за "засорять", а именно "спрятать" от второго. Но это уже флейм, ибо Вы обрезали главное: один программист - сам себе хозяин. И перевод из static в глобальную особого значения не имеет: насколько я понимаю имелись ввиду переменные static исключительно внутри функции. Но чтобы следовать упомянутому ТС принципу "использование глобальных переменных нужно максимально минимизировать" перевода из static в глобальные недостаточно, нужно перевести эту переменную в параметры. Этот принцип никак не регламентирует количество глобальных переменных. Было: void func() { static int value =0; // модифицирующий код value; } Стало: int value =0; void func(int* value) { // модифицирующий код value; }
|
|
|
|
|
Jan 14 2017, 10:53
|

Гуру
     
Группа: Модераторы
Сообщений: 8 455
Регистрация: 15-05-06
Из: Рига, Латвия
Пользователь №: 17 095

|
QUOTE (aiwa @ Jan 12 2017, 14:39)  Переменные видимы в определенном порядке и избыточное количество не критично: пусть лучше уж валяется в глобальном списке, чем тратить на нее семиразовое нажатие клавиш. И реальная необходимость возникла не из-за "засорять", а именно "спрятать" от второго. Но это уже флейм, ибо Вы обрезали главное: один программист - сам себе хозяин. И перевод из static в глобальную особого значения не имеет Из соседней ветки: QUOTE (Mister_DSP @ Jan 13 2017, 08:13)  Создаю библиотеку (lib) в Keil по исходникам из множества файлов. Затем получившуюся библиотеку пристыковываю к другому проекту (главному).
При линковке выдаёт ошибку: найдены одинаковые имена в libfile.o и module.o
Исходные тексты программ не моего авторства, около 100 имён совпадает, так что переименовывать не вариант. Попробуйте убедить его, что он сам себе хозяин и static значения не имеет
--------------------
На любой вопрос даю любой ответ"Write code that is guaranteed to work, not code that doesn’t seem to break" ( C++ FAQ)
|
|
|
|
|
Jan 14 2017, 16:29
|
Гуру
     
Группа: Свой
Сообщений: 2 702
Регистрация: 14-07-06
Пользователь №: 18 823

|
Цитата(Сергей Борщ @ Jan 11 2017, 15:52)  примерно так: У этого автора не так все легко с абстракциями, вряд ли вы ему помогли. Нужно начинать с чего-нибудь попроще. Вот, например, два датчика Холла, описанные в заголовочном файле для доступа снаружи Код #define HALLS_CHANNELS_QTY 2
typedef struct { int position; unsigned char cur; //current_inputs; unsigned char prev; //previous_inputs unsigned char prev_prev; //before previous inputs unsigned char init; } hall_type;
extern hall_type hall[HALLS_CHANNELS_QTY]; extern hall_type old_hall[HALLS_CHANNELS_QTY];
--------------------
Уходя, оставьте свет...
|
|
|
|
|
Jan 14 2017, 18:03
|
Частый гость
 
Группа: Участник
Сообщений: 197
Регистрация: 8-07-16
Пользователь №: 92 484

|
Цитата(demiurg1978 @ Jan 9 2017, 19:26)  Хочу узнать мнение насчет использования глобальных переменных. Спрашиваю вот почему: неоднократно слышал, что использование глобальных переменных нужно максимально минимизировать. Откуда пошло это? В данный момент пишу проект. Свои проекты всегда стараюсь разделить на модули. static переменные. использование в других модулях посредством set_value (); get_value ();. Но в нынешнем проекте у меня много параметров. И если честно, я заколебался на каждую переменную писать свои функции установки и получения переменных. Хочу вывести переменные из static в глобальные. Ваши за и против. Не один из принципов нельзя возводить в абсолют. Если нельзя, но очень хочется и это сильно упрощает жизнь - то можно. Вон про GOTO тоже написано, что "правильные пасаны" GoTO не используют. А тем не менее ИМЕННО GOTO в ряде случаев упрощает код и делает его более читабельным
|
|
|
|
|
Jan 16 2017, 08:26
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Сергей Борщ @ Jan 14 2017, 12:53)  Попробуйте убедить его, что он сам себе хозяин и static значения не имеет  Как раз в приведенном примере из соседней ветки static значения не имеет: static имеет целью сделать переменную недоступной извне, что совершенно не подходит топикстартеру. Там конфликт имен в чистом виде. Цитата(Непомнящий Евгений @ Jan 16 2017, 07:14)  У вас очень трепетное отношение к нажатию клавиш  Опять же, имена глобальных переменных обычно должны быть длинными и осмысленными, часто это больше 7 символов  7-кратное нажатие - это написание "static"+пробел перед глобальными переменными, которые желательно сделать недоступными вне файла. В случае "сам себе хозяин" без них можно обойтись.
|
|
|
|
|
Jan 16 2017, 10:14
|
Знающий
   
Группа: Свой
Сообщений: 771
Регистрация: 16-07-07
Из: Волгодонск
Пользователь №: 29 153

|
Цитата(aiwa @ Jan 16 2017, 11:26)  7-кратное нажатие - это написание "static"+пробел перед глобальными переменными, которые желательно сделать недоступными вне файла. В случае "сам себе хозяин" без них можно обойтись. Я как раз про нажатия и говорю. Глобальная переменная - это тип, название и комментарий, причем название должно быть достаточно длинным. Т.е. несколько десятков символов точно. На этом фоне экономия 7 символов - это ни о чем. Можно конечно давать имена вроде xz42 и комментарии не писать Опять же, для значительной части программ собственно набор текста занимает сравнительно малый процент времени. Поэтому рекомендация не использовать static из-за затрат на его написание смотрится странновато  Но если экономить символы, то ничто не мешает написать #define S static - это минус 5 нажатий
|
|
|
|
|
Jan 17 2017, 02:54
|
Местный
  
Группа: Участник
Сообщений: 301
Регистрация: 13-12-15
Из: Харьков
Пользователь №: 89 682

|
Цитата(Непомнящий Евгений @ Jan 16 2017, 12:14)  Поэтому рекомендация не использовать static из-за затрат на его написание смотрится странновато  Как раз ровно наоборот: я одобряю решение топикстартера затратить лишние нажатия клавиш на удаление уже написанного static. Что бы не писать для доступа к переменным функции-обертки (или макросы). Для единоличника считаю это допустимым и вполне разумным. С небольшой поправкой: для переменных, которые могут изменить свою структуру в будущем, не полениться и написать доступ через обертки. Цитата(Непомнящий Евгений @ Jan 16 2017, 12:14)  Но если экономить символы, то ничто не мешает написать #define S static - это минус 5 нажатий  Не, такой хоккей нам не нужен. Конечно, это дело личных предпочтений, но как по мне "S" вместо static смотрится еще хуже переменной xz42.
|
|
|
|
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0
|
|
|