Чи глобальні змінні в C / C ++ такі погані, як вважає мій професор?
Чи глобальні змінні в C / C ++ такі погані, як вважає мій професор?
Відповіді:
Проблема глобальних змінних полягає в тому, що оскільки кожна функція має доступ до них, стає важче зрозуміти, які функції насправді читають і записують ці змінні.
Щоб зрозуміти, як працює програма, вам доведеться враховувати кожну функцію, яка модифікує глобальний стан. Це можна зробити, але в міру того, як додаток зростає, це стає важче до практично неможливого (або, принаймні, повної трати часу).
Якщо ви не покладаєтесь на глобальні змінні, ви можете переходити стан між різними функціями за потребою. Таким чином, ви маєте набагато більше шансів зрозуміти, що робить кожна функція, оскільки вам не потрібно враховувати глобальну державу.
Важливо - пам’ятати загальну мету: чіткість
Правило "без глобальних змінних" існує тому, що більшість часу глобальні змінні роблять значення коду менш зрозумілим.
Однак, як і багато правил, люди пам’ятають правило, а не те, що було призначено.
Я бачив програми, які, здається, подвоюють розмір коду, передаючи величезну кількість параметрів навколо, щоб уникнути зла глобальних змінних. Зрештою, використання глобальних програм зробило б програму зрозумілішою для тих, хто її читає. Нерозумно дотримуючись слова правила, початковий програміст провалив наміри правила.
Так, так, глобалісти часто погані. Але якщо ви відчуваєте, що зрештою, наміри програміста з’ясуються зрозумілішими завдяки використанню глобальних змінних, тоді йдіть далі. Однак пам’ятайте про падіння чіткості, яке автоматично настає, коли ви змушуєте когось отримати доступ до другого фрагмента коду (глобальних), щоб зрозуміти, як працює перший фрагмент.
Мій професор казав щось на кшталт: використання глобальних змінних добре, якщо ви правильно їх використовуєте. Я не думаю, що я коли-небудь добре отримував їх від використання правильно, тому рідко їх взагалі використовував.
static
глобальні змінні, мова - це C. Отримавши обмеження на відносно невеликі одиниці перекладу, вони починають нагадувати змінні класи об’єктів C ++.
program lifetime, file scope variables
. І вони стають досить глобальними, як тільки ви переходите вказівник на змінну до зовнішнього світу (що неможливо з автоматичними змінними) ..
static
глобальні змінні обмежують сферу застосування однією і тією ж одиницею перекладу. Але вони мають життя до кінця програми, як будь-яка глобальна змінна.
Глобальні змінні слід використовувати лише тоді, коли у вас немає альтернативи. І так, сюди входить Сінглтон. У 90% часу глобальні змінні вводяться для економії витрат на проходження параметра. І тоді відбувається багатопотокове тестування / тестування / кодування технічного обслуговування, і у вас є проблеми.
Так, так, у 90% ситуацій глобальні змінні є поганими. Винятки, швидше за все, ви не побачите у студентські роки. Один виняток, про який я можу придумати верхню частину голови, - це робота з суттєво глобальними об'єктами, такими як таблиці переривань. Такі речі, як підключення до БД, здаються глобальними, але ні.
Проблема, яку створюють глобальні змінні для програміста, полягає в тому, що він розширює міжкомпонентну поверхню зв'язку між різними компонентами, які використовують глобальні змінні. Це означає, що зі збільшенням кількості компонентів, що використовують глобальну змінну, складність взаємодій також може зростати. Таке посилене з'єднання, як правило, полегшує введення дефектів у систему під час внесення змін, а також ускладнює діагностику та виправлення дефектів. Це збільшення зв'язку також може зменшити кількість доступних варіантів при внесенні змін, і це може збільшити зусилля, необхідні для змін, оскільки часто потрібно простежити різні модулі, які також використовують глобальну змінну, щоб визначити наслідки змін.
Метою інкапсуляції , яка в основному є протилежною використанню глобальних змінних, є зменшення зв'язку, щоб зробити розуміння та зміну джерела простішим, безпечнішим та легшим для перевірки. Набагато простіше використовувати тестування одиниць, коли глобальні змінні не використовуються.
Наприклад, якщо у вас є проста глобальна ціла змінна, яка використовується в якості перерахованого індикатора, який різні компоненти використовують як машину стану, а ви потім вносите зміни, додаючи новий стан для нового компонента, ви повинні простежити всі інші компоненти, щоб гарантувати, що зміни не торкнуться їх. Прикладом можливої проблеми може стати те, що в різних місцях використовується switch
оператор для перевірки значення глобальної змінної перерахунку з case
операторами для кожного з поточних значень, і так трапляється, що в деяких з switch
операторів немає default
справи для обробки несподіване значення для глобального раптом у вас не визначене поведінка, що стосується програми.
З іншого боку, використання спільної області даних може бути використано, щоб містити набір глобальних параметрів, на які посилається у всій програмі. Цей підхід часто використовується для вбудованих додатків з невеликими розмірами пам'яті.
При використанні глобальних змінних у подібних додатках, як правило, відповідальність за запис у область даних покладається на один компонент, а всі інші компоненти бачать область як const
та прочитану з неї, ніколи не пишучи на неї. Такий підхід обмежує проблеми, які можуть розвиватися.
Кілька проблем із глобальних змінних, над якими потрібно вирішити
Коли джерело для такої глобальної змінної, як структура, модифікується, все, що використовується, повинно бути перекомпільовано, щоб все, що використовувало змінну, знало її справжній розмір і шаблон пам'яті.
Якщо більш ніж один компонент може змінити глобальну змінну, ви можете зіткнутися з проблемами з непослідовними даними, що знаходяться в глобальній змінній. За допомогою програми з багатопотоковою передачею вам, ймовірно, потрібно буде додати якусь блокувальну чи критичну область, щоб забезпечити спосіб, щоб лише один потік одночасно міг змінити глобальну змінну, і коли потік змінює змінну, усі зміни завершені і зроблено раніше, ніж інші потоки можуть запитувати змінну або змінювати її.
Налагодження багатопотокової програми, яка використовує глобальну змінну, може бути складніше. Ви можете зіткнутися з умовами перегонів, які можуть створити дефекти, які важко повторити. Коли декілька компонентів спілкуються через глобальну змінну, особливо в багатопотоковому додатку, бути в змозі зрозуміти, який компонент змінює змінну, коли і як вона змінює змінну, може бути дуже важко зрозуміти.
Зіткнення імен може бути проблемою при використанні глобальних змінних. Локальна змінна, яка має те саме ім'я, що і глобальна змінна, може приховати глобальну змінну. Ви також стикаєтеся з проблемою з умовами іменування під час використання мови програмування C. Робота полягає в тому, щоб розділити систему на підсистеми з глобальними змінними для певної підсистеми, починаючи з тих самих перших трьох літер (див. Це на вирішенні зіткнень простору імен у цілі C ). C ++ надає простори імен, і за допомогою C ви можете обійти це, створивши глобально видиму структуру, членами якої є різні елементи даних та покажчики на дані та функції, які надаються у файл як статичний, а також видимість файлу лише для того, щоб на них можна було посилатися лише через глобально видима структура.
У деяких випадках вихідний намір програми змінюється таким чином, що глобальні змінні, що забезпечували стан для одного потоку, змінюються, щоб дозволити запускати декілька повторюваних потоків. Прикладом може бути проста програма, призначена для одного користувача, що використовує глобальні змінні для стану, а потім від управління надходить запит на додавання інтерфейсу REST, щоб віддалені програми могли діяти як віртуальні користувачі. Отже, тепер у вас виникає необхідність дублювати глобальні змінні та інформацію про їх стан, щоб у одного користувача, а також у кожного віртуального користувача з віддалених додатків був свій унікальний набір глобальних змінних.
Використання C ++ namespace
та struct
методики C
Для мови програмування C ++ ця namespace
директива є величезною допомогою для зменшення шансів зіткнення імені. namespace
поряд із class
різними ключовими словами доступу ( private
, protected
і public
) надають більшість інструментів, необхідних для інкапсуляції змінних. Однак мова програмування на C не дає цієї директиви. Ця публікація stackoverflow, простори імен у C , пропонує деякі методи для C.
Корисна методика полягає в тому, щоб мати єдину область даних резидентної пам’яті, яка визначається як a, struct
яка має глобальну видимість, і всередині цього struct
є вказівниками на різні глобальні змінні та функції, які піддаються впливу. Фактичні визначення глобальних змінних задаються обсягом файлів за допомогою static
ключового слова. Якщо ви використовуєте const
ключове слово, щоб вказати лише те, що читаються, компілятор може допомогти вам застосувати доступ лише для читання.
Використання struct
методики також може інкапсулювати глобальний, щоб він став своєрідним пакетом або компонентом, який, як правило, є глобальним. Маючи такий компонент, стає легше керувати змінами, які впливають на глобальний та функціональний за допомогою глобального.
Однак в той час namespace
або struct
метод може допомогти управляти конфліктом імен, що лежить в основі проблема межкомпонентное зв'язку яких використання глобал вводять особливо в сучасних багатопотокових застосуваннях, все ще існує.
Так, але ви не несете вартості глобальних змінних, поки не перестанете працювати в коді, який використовує глобальні змінні, і не почнете писати щось інше, що використовує код, який використовує глобальні змінні. Але вартість все ж є.
Іншими словами, це довгострокова непряма вартість, і тому такі люди вважають, що це не погано.
Якщо можливо, ваш код буде піддаватися інтенсивному перегляду під час судового розгляду у Верховному суді , тоді ви хочете уникнути глобальних змінних.
Дивіться цю статтю: Код повітряного дихального засобу Баггі відображає важливість огляду джерела
Були деякі проблеми зі стилем коду, які були визначені обома дослідженнями. Одним із стилістичних питань, що хвилювали рецензентів, було широке використання незахищених глобальних змінних . Це вважається поганою формою, оскільки це збільшує ризик того, що стан програми стане непослідовним, або що значення будуть ненавмисно змінені або перезаписані. Дослідники також висловили певне занепокоєння з приводу того, що десяткова точність не підтримується послідовно у всьому коді.
Людина, я думаю, що розробники хочуть, щоб вони не використовували глобальні змінні!
Глобальні змінні настільки ж погані, як і ти їх робиш, не менше.
Якщо ви створюєте повністю капсульовану програму, ви можете використовувати глобальні програми. Користуватися глобальними гріхами - це "гріх", але програмування гріхів є філософською філософією.
Якщо ви перевірите L.in.oleum , ви побачите мову, змінні якої є виключно глобальними. Це неможливо, тому що всі бібліотеки не мають іншого вибору, крім використання глобальних.
Однак, якщо у вас є вибір і ви можете ігнорувати філософію програміста, глобалісти не все так погано.
Ні Готос, ні якщо ви правильно їх використовуєте.
Велика "погана" проблема полягає в тому, що, якщо ви їх неправильно використовуєте, люди кричать, марсовий земля розбивається, і світ вибухає .... або щось подібне.
Я відповів би на це запитання ще одним питанням: Чи використовуєте Ви сінгелтони / Чи сингелтони погані?
Тому що (майже все) використання сингелтону - це прославлена глобальна змінна.
Справа менше в тому, що вони погані , і тим більше, що вони небезпечні . У них є свої плюси і мінуси, і бувають ситуації, коли вони є найбільш ефективним або єдиним способом досягнення певного завдання. Однак їх дуже легко зловживати, навіть якщо ви вживаєте заходів, щоб завжди правильно їх використовувати.
Кілька плюсів:
Кілька мінусів:
Зауважте, якщо ви хочете, що перші два плюси та перші два мінуси, які я перерахував, - це абсолютно те саме, лише з різними формулюваннями. Це тому, що особливості глобальної змінної дійсно можуть бути корисними, але самі функції, які роблять їх корисними, є джерелом усіх їхніх проблем.
Кілька потенційних варіантів вирішення деяких проблем:
Globals
або GlobalVars
) або використовувати стандартизовану конвенцію іменування глобальних змінних (наприклад, global_[name]
або g_module_varNameStyle
(як зазначено underscore_d у коментарях) )). Це одночасно документує їх використання (ви можете знайти код, який використовує глобальні змінні, шукаючи простір імен / ім’я структури) та мінімізуючи вплив на глобальний простір імен.extern
у відповідному заголовку, щоб їх використання могло бути обмежене одиницями компіляції, які потребують доступу до них. Якщо ваш код покладається на безліч глобальних змінних, але кожен блок компіляції потребує доступу лише до декількох файлів, ви можете розглянути їх сортування в декілька вихідних файлів, тому простіше обмежити доступ кожного файлу до глобальних змінних.Хороші вони чи погані, залежить від того, як ви їх використовуєте. Більшість, як правило, погано їх використовує, звідси загальна обережність щодо них. Якщо їх правильно використовувати, вони можуть стати головним благом; якщо використовується погано, проте, вони можуть і будуть повертатися , щоб вкусити вас , коли і як ви менше всього цього чекаєте.
Хороший спосіб поглянути на те, що вони самі не погані, але вони дозволяють поганий дизайн і можуть помножувати наслідки поганого дизайну в експоненціальній формі.
Навіть якщо ви не збираєтесь ними користуватися, краще знати, як безпечно ними користуватися, і не бажати, ніж не користуватися ними, оскільки ви не знаєте, як безпечно ними користуватися. Якщо ви коли-небудь опинитесь у ситуації, коли вам потрібно підтримувати наявний код, який спирається на глобальні змінні, ви можете зіткнутися з труднощами, якщо не знаєте, як правильно їх використовувати.
g_module_varNameStyle
ідеально розбірливим. Щоб було зрозуміло, я не використовую глобалістів, якщо я можу це легко уникнути - ключове слово легко , тому що, оскільки я перестала вірити, що їх треба уникати - а точніше - заплутано - будь-якою ціною, я проводжу набагато кращий час, і мій code is (shock!) набагато акуратніше
Як хтось сказав (я перефразую) в іншій темі "Такі правила не слід порушувати, поки ви повністю не зрозумієте наслідки цього".
Бувають випадки, коли потрібні глобальні змінні або, принаймні, дуже корисні (наприклад, робота із зворотними дзвінками, визначеними системою). З іншого боку, вони також дуже небезпечні з усіх причин, про які вам сказали.
Існує багато аспектів програмування, які, ймовірно, слід залишити експертам. Іноді вам потрібен дуже гострий ніж. Але ви не отримаєте використовувати його, поки не будете готові ...
Глобальні змінні, як правило, погані, особливо якщо інші люди працюють над тим самим кодом і не хочуть витрачати 20 хвилин на пошук усіх місць, на які вказана змінна. А додавання ниток, що змінюють змінні, приносить абсолютно новий рівень головних болів.
Глобальні константи в анонімному просторі імен, використовуваному в одній одиниці перекладу, є чудовими та всюдисущими в професійних додатках та бібліотеках. Але якщо дані є мутаційними та / або їх потрібно ділити між декількома ТУ, ви можете захопити їх інкапсуляцією - якщо не заради дизайну, то заради налагодження чи роботи з вашим кодом.
Використання глобальних змінних на зразок схоже на підмітання бруду під килим. Це швидко виправити та набагато простіше за короткий термін, ніж дістати пилосос або пилосос для його очищення. Однак якщо ви коли-небудь пізніше пересунете килимок, під вами буде великий безлад.
Я думаю, що ваш професор намагається зупинити шкідливу звичку до того, як вона навіть розпочнеться.
Глобальні змінні мають своє місце, і як багато хто казав, знати, де і коли їх використовувати, може бути складним. Тож я думаю, а не вникати в нітратну глибокість того, чому, як, коли і де глобальних змінних ваш професор вирішив просто заборонити. Хто знає, він може заборонити їх у майбутньому.
Абсолютно не. Зловживання ними, хоча ... це погано.
Бездумно їх видаляти заради цього - це просто безглуздо. Якщо ви не знаєте передових і недоліків, найкраще керуватися чітко та робити так, як вас вчили / вчили, але з глобальними змінними немає нічого неявного. Коли ви зрозумієте плюси і мінуси, краще прийміть власне рішення.
Глобальні змінні чудові в невеликих програмах, але жахливі, якщо використовувати їх однаково у великих.
Це означає, що ви можете легко звикнути ними користуватися під час навчання. Це те, що ваш професор намагається захистити вас.
Коли ти досвідченіший, то буде легше вчитися, коли їм добре.
Ні, вони зовсім не погані. Вам потрібно подивитися на (машинний) код, виданий компілятором, щоб зробити це визначення, іноді набагато гірше використовувати локальний, ніж глобальний. Також зауважте, що нанесення "статичного" на локальну змінну в основному робить її глобальною (і створює інші потворні проблеми, які вирішить справжній глобальний). "місцеві глобали" особливо погані.
Глобали дають вам чіткий контроль над використанням пам’яті, що щось набагато складніше зробити з місцевими жителями. Сьогодні це має значення лише у вбудованих середовищах, де пам'ять досить обмежена. Що потрібно знати, перш ніж припустити, що вбудована така ж, як і в інших середовищах, і припустити, що правила програмування є однаковими для всіх.
Добре, що ви ставите під сумнів правила, які навчають, більшість з них не з тих причин, про які вам кажуть. Хоча найважливіший урок - це не те, що це правило, яке потрібно носити з собою назавжди, але це правило, необхідне для шанування, щоб пройти цей клас і рухатися вперед. У житті ви побачите, що для компанії XYZ у вас будуть інші правила програмування, які вам, зрештою, доведеться дотримуватися, щоб продовжувати отримувати зарплату. В обох ситуаціях можна сперечатися з цим правилом, але я думаю, у вас буде набагато більше удачі на роботі, ніж у школі. Ви просто ще один із багатьох студентів, ваше місце буде замінено незабаром, професори не зможуть, на роботі ви є одним з невеликої команди гравців, які повинні переглянути цей продукт до кінця, і в цьому середовищі правила, розроблені, є для вигода членів команди, а також продукту та компанії, тож, якщо всі мають однодумство або якщо для конкретного продукту є вагомі інженерні причини, щоб порушити щось, що ви дізналися в коледжі чи якусь книгу про загальне програмування, тоді продайте свою ідею команді та запишіть її як дійсний, якщо не кращий метод . Все - це чесна гра в реальному світі.
Якщо ви будете дотримуватися всіх правил програмування, викладених вам у школі чи книгах, ваша кар’єра програмування буде вкрай обмеженою. Ви, ймовірно, можете вижити і мати плідну кар’єру, але широта і ширина доступних вам середовищ будуть вкрай обмеженими. Якщо ви знаєте, як і чому це правило існує, і ви можете його захищати, це добре, якщо ви тільки причина "тому, що мій учитель сказав так", то це не так добре.
Зауважте, що такі теми часто сперечаються на робочому місці і надалі залишатимуться, оскільки компілятори та процесори (і мови) розвиваються так, як такі правила і не захищаючи свою позицію і, можливо, викладаєте урок хтось із іншою думкою, яку ви не хочете рухайся вперед.
Тим часом, тоді просто роби все, що каже той, хто говорить найгучніше чи несе найбільшу палицю (до тих пір, поки ти не той, який кричить найгучніший і несе найбільшу палицю).
Я хотів би заперечувати проти того, що в цій нитці висловлюється думка про те, що це робить багатопотокові складніші або неможливіші самі по собі. Глобальні змінні є загальним станом, але альтернативи глобальним (наприклад, передача покажчиків навколо) також можуть поділяти стан. Проблема з багатопотоковою ниткою полягає в тому, як правильно використовувати спільний стан, а не те, чи стане цей стан спільним через глобальну змінну чи щось інше.
Більшу частину часу, коли ви робите багатопотокові, вам потрібно чим-небудь поділитися. Наприклад, у шаблоні виробник-споживач, ви можете поділити певну чергу, безпечну для потоків, яка містить робочі блоки. І вам дозволяється ділитися ним, оскільки ця структура даних є безпечною для потоків. Незалежно від того, чи така черга є глобальною чи ні, це абсолютно не має значення, коли мова йде про безпеку потоку.
Поточна надія, висловлена у цій нитці, що перетворення програми з однопотокової в багатопотокову буде простішою, коли не використовувати глобальні наївні. Так, глобалісти полегшують себе в ногу, але існує маса способів, як стріляти в себе.
Я не виступаю за глобальні передумови, оскільки інші точки все ще стоять, моя думка полягає лише в тому, що кількість потоків у програмі не має нічого спільного із змінною сферою застосування.
Так, тому що якщо ви дозволяєте некомпетентним програмістам їх використовувати (читайте 90%, особливо науковці), ви отримуєте 600+ глобальну змінну, що поширюється на 20+ файлів, і проект із 12 000 рядків, де 80% функцій втрачає чинність, повертає недійсність і працює повністю на глобальному стані.
Швидко стає неможливо зрозуміти, що відбувається в будь-який момент, якщо ви не знаєте весь проект.
Використання глобальних змінних фактично залежить від вимог. Його перевага полягає в тому, що він скорочує накладні витрати на передачу значень багаторазово.
Але ваш професор має рацію, оскільки він викликає проблеми безпеки, тому слід максимально уникати використання глобальних змінних. Глобальні змінні також створюють проблеми, які іноді важко налагодити .
Наприклад:-
Ситуації, коли значення змінних змінюються під час виконання . На той момент важко визначити, яка частина коду його модифікує та на яких умовах.
Глобальні хороші, коли справа стосується конфігурації . Коли ми хочемо, щоб наша конфігурація / зміни мали глобальний вплив на весь проект .
Таким чином, ми можемо змінити одну конфігурацію, і зміни спрямовані на весь проект . Але мушу застерегти, що вам доведеться бути дуже розумними, щоб користуватися глобалами.
Рано чи пізно вам потрібно буде змінити спосіб встановлення цієї змінної або що відбувається під час доступу до неї, або вам просто потрібно знайти місце, де вона змінена.
Практично завжди краще не мати глобальних змінних. Просто напишіть способи отримання греблі та встановіть методи, і будьте вам, коли вам знадобляться через день, тиждень чи місяць.
Зазвичай я використовую глобалі для значень, які рідко змінюються, як одиночні кнопки або покажчики функцій, на функції в динамічно завантаженій бібліотеці. Використання змінних глобальних мереж у багатопотокових програмах, як правило, призводить до важкої відслідковування помилок, тому я намагаюся уникати цього як загальне правило.
Використання глобального замість передачі аргументу часто швидше, але якщо ви пишете багатопотокове додаток, яке часто робите в наші дні, воно, як правило, не дуже добре (ви можете використовувати нитки-статики, але тоді підвищення продуктивності сумнівний) .
У веб-додатках в рамках підприємства можуть використовуватися для проведення сеансу / вікна / потоку / конкретних даних користувача на сервері з міркувань оптимізації та для захисту від втрати роботи, коли з'єднання нестабільне. Як уже згадувалося, гоночні умови потребують поводження. Ми використовуємо один екземпляр класу для цієї інформації, і вона ретельно управляється.
Зрештою, програма чи додаток все ще можуть працювати, але справа в тому, щоб бути охайним та мати повне розуміння того, що відбувається. Якщо ви поділите значення змінної між усіма функціями, може бути важко відстежити, яка функція змінює значення (якщо функція робить це) і робить налагодження в мільйон разів складніше
безпека менше означає, що будь-хто може маніпулювати змінними, якщо вони оголошені глобальними, для цього можна пояснити, наприклад, якщо у вашій банківській програмі баланс як глобальна змінна, функція користувача може це маніпулювати, а також банківський співробітник також може маніпулювати. у цьому виникає проблема. Тільки користувачеві слід надати лише функцію читання та зняття, але діловод банку може додати суму, коли користувач особисто видає готівку на стіл. Це так, як це працює
У багатопотоковому додатку використовуйте локальні змінні замість глобальних змінних, щоб уникнути перегонів.
Умова перегонів виникає, коли декілька потоків отримують доступ до спільного ресурсу, при цьому щонайменше один потік має доступ для запису до даних. Тоді результат програми не передбачуваний і залежить від порядку доступу до даних різними потоками.
Більше про це тут, https://software.intel.com/en-us/articles/use-intel-parallel-inspector-to-find-race-conditions-in-openmp-based-multithreaded-code