да я и сам сейчас смотрю в map-файл, и вижу, что одна причем если static делать в обоих модулях, то получается две. если static только в одном - одна... но если без static попробовать проинициализировать разными числами - вылетает ошибка линкера "multiple definition"... странно все это...
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
_________________ Разработал: -BLDC -ФУОЗ/МПСЗ -SMART BMS -ECU/EDC на STM32F4(43%)+CPLD(57%) -Моноинжектор на ATSAMD20G16 -контроллер эффектов для RGB LED ленты -умные часы/обратный счет/секундомер -устройство измерения емкости АКБ
"Так можно делать в С, это tentative definition, объект обявленный таким образом попадает в область общих данных. При линковке одноимённые объекты в этой области становятся одним объектом. В С++ так делать нельзя."
tentative definition гуглится, вопросы такого планы уже были. Например вот.
Цитата:
The ANSI C standard supports the concept of the tentative definition. Any external data declaration that has no storage class specifier and no initializer is considered a tentative definition. If the identifier declared appears in a later definition, then the tentative definition is treated as if the extern storage class specifier were present. In other words, the tentative definition becomes a simple referencing declaration.
If the end of the translation unit is reached and no definition has appeared with an initializer for the identifier, then the tentative definition becomes a full definition, and the object defined has uninitialized (zero-filled) space reserved for it.
Unlike ANSI C, C++ doesn't have the concept of a tentative declaration; an external data declaration without a storage class specifier is always a definition.
Карма: 67
Рейтинг сообщений: 1060
Зарегистрирован: Чт сен 18, 2008 12:27:21 Сообщений: 19745 Откуда: Столица Мира Санкт-Петербург
Рейтинг сообщения:0 Медали: 1
А теперь суммируя всё вышеизложенное, можно по-русски и для чайников — чего-как правильно надо делать и какие могут быть подводные камни?
_________________ [ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ] Измерить нннада?
Карма: 16
Рейтинг сообщений: 197
Зарегистрирован: Вс дек 02, 2012 16:58:33 Сообщений: 922 Откуда: от туда
Рейтинг сообщения:1
Для использования глобальных переменных в разных файлах делаем так: В файле sample.h:
Код:
extern char s1;
в файле main.c:
Код:
#include "sample.h" char s1 = INIT_S1;
в других файлах:
Код:
#include "sample.h" s1 = 2;
Указание типа с extern без присваивания -> объявление переменной в том файле, где применяете, без выделения памяти. Указание переменной с типом и присвоением значения -> определение переменной (выделение памяти). Использование переменной дальше без ограничений. Лишний extern в main.c никак не мешает выделять память при определении переменной.
Карма: 67
Рейтинг сообщений: 1060
Зарегистрирован: Чт сен 18, 2008 12:27:21 Сообщений: 19745 Откуда: Столица Мира Санкт-Петербург
Рейтинг сообщения:0 Медали: 1
любом.... месте?
_________________ [ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ] Измерить нннада?
Вообще говоря, в принципе, эти глобальные переменные общие для нескольких модулей и основной части программы, нафиг не нужны. Без них можно легко обходится. И даже принципы объектного программирования требуют в хедере описывать только интерфейс, а данные в .с файлах. Т.е. переменные, с которыми работает модуль, описываются в модуле, можно с модификатором static, а изменяются только за счет функций, объявленных в его хедере. В современных языках C# или Java понятия глобальных переменных вообще нет. В С++ их количество сведено до минимума. В объекто-ориентированном программировании это называют инкапсуляцией, данные и детали реализации скрываются в неком "черном ящике", а наружу предоставляется только интерфейс (публичные функции). Поэтому, не объявляйте в хедере переменные вообще никак, ни с extern, ни без, и это не будет ошибкой, а будет хорошим заделом на будущее. И как следствие не объявляйте глобальные переменные дважды в разных .c-файлах, ни с extern, ни без extern (в расчете на tentative definition).
В современных языках C# или Java понятия глобальных переменных вообще нет.
Воу воу воу, полегче. А как же статический класс с публичным свойством или шаблон "одиночка"? Все они представляют некое глобальное состояние, как и глобальная переменная в Си.
ks0 писал(а):
Т.е. переменные, с которыми работает модуль, описываются в модуле, можно с модификатором static, а изменяются только за счет функций, объявленных в его хедере.
Не можно static, а нужно static, это является хорошим тоном, но всё же мы имеем дело с Си, поэтому...
ks0 писал(а):
не объявляйте в хедере переменные вообще никак
...всё же не стоит принимать это как закон. В некоторых ситуациях глобальные переменные (как и злополучный goto кстати) могут не только упростить реализацию, но и сделать её более изящной и понятной.
Здравствуйте, помню что где-то тут было обсуждения на тему что вместо глобальный переменных для передачи параметра из одного модуля в другой лучше использовать функции. что-то типа такого
Код:
u08 test(void) { return data; }
Но пока не особо понимаю в чём преимущество такой передачи ? и какие могут быть ошибки если вывод из модуля делать через глобальные переменные.
1. Если из модуля необходимо предоставить данные, доступные только для чтения, то в случае использования глобальной переменной кто-то может случайно изменить эту переменную, она ведь никак не защищена от записи. В этом случае использование функции (так называемого getter) не позволит записывать что-либо в эту переменную.
2. Обычно, перед передачей или чтением каких-либо данных в/из модуля, возникает необходимость некоторых действий по преобразованию данных. Например, при задании скорости работы UART может понадобиться перевести значение из бод/с в период таймера, а при чтении соответственно наоборот. Иногда может понадобиться атомарный доступ к переменной, тогда перед чтением/записью придется использовать примитивы синхронизации или запрещать прерывания. Всё это очень легко сделать если доступ осуществляется через getter и setter.
3. Даже если сейчас ничего из вышеперечисленного не требуется, то это может потребоваться в будущем. При использовании функций можно будет просто добавить в них требуемый функционал, снаружи интерфейс модуля останется прежним. Иначе, изменения в интерфейсе модуля потребуют изменений во всех местах, где он используется. То есть в данном случае с помощью функций доступа мы скрываем внутреннюю реализацию модуля и все особенности его работы, это называется инкапсуляция.
4. Тем не менее, мы говорим о языке C и низкоуровневом программировании, поэтому в некоторых ситуациях, когда известны все подводные камни, использование глобальных переменных может не только не навредить архитектуре приложения, но и сделать её более простой и понятной. Поэтому нельзя однозначно говорить, что глобальные переменные - зло, просто нужно использовать их с умом. В противоположность этому, новички обычно их используют везде, где надо и не надо.
static обозначает то, что переменная при выходе из функции, в которой она используется, будет все равно сохраняться в памяти. т.е. под нее резервируется место "глобально". но из внешних функций эта переменная не видна. это все мое личное имхо на моем личном небогатом опыте. псевдокод:
Код:
static delta = 10; void main (void) { uint8_t temp; for (uint8_t i = 0; i < 10; i++) { temp = up(); } }
uint8_t up (void) { static up = 0; up++; return up; }
в данном случае переменная delta будет видна только из файла, в котором находится функция main, а переменная temp в цикле будет получать последовательно значения от 1 до 10.
_________________ Осилит дорогу идущий ---------- Пишу на Си за еду
Карма: 67
Рейтинг сообщений: 1060
Зарегистрирован: Чт сен 18, 2008 12:27:21 Сообщений: 19745 Откуда: Столица Мира Санкт-Петербург
Рейтинг сообщения:0 Медали: 1
slavokhire5 писал(а):
последовательно значения от 1 до 10.
Эээ... Разве? Она не будет обнуляться при каждом вызове функции?
_________________ [ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ] Измерить нннада?
не будет. локальные static-переменные получают начальное значение единожды перед стартом main, как и все прочие (глобальные) static-переменные. при входах в функцию инициализация переменной уже не происходит, используется предыдущее значение.
_________________ если рассматривать человека снизу, покажется, что мозг у него глубоко в жопе при взгляде на многих сверху ничего не меняется...
Карма: 67
Рейтинг сообщений: 1060
Зарегистрирован: Чт сен 18, 2008 12:27:21 Сообщений: 19745 Откуда: Столица Мира Санкт-Петербург
Рейтинг сообщения:0 Медали: 1
Хитро. А что про "@" и следущие за ней значения? Это адреса ОЗУ принудительно задаются?
_________________ [ Всё дело не столько в вашей глупости, сколько в моей гениальности ] [ Правильно заданный вопрос содержит в себе половину ответа ] Измерить нннада?
Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4
Вы не можете начинать темы Вы не можете отвечать на сообщения Вы не можете редактировать свои сообщения Вы не можете удалять свои сообщения Вы не можете добавлять вложения