Об адресах и указателях в Си

Сегодня на хабре появилась статья о работе с регистрами внешних устройств на языке Си. Такая статья наверняка полезна начинающим Си-программистам, а особенно хороша для людей, переползающих с ассемблера.

Хотелось бы добавить пять копеек. Возьмем последний пример кода из статьи:


#define IO_DATA_ADRESS  0x40000004
typedef struct  {
   uint32_t data;
   uint32_t status;
} IO_DEVICE;
IO_DEVICE * const pio_device = (IO_DEVICE *) (IO_DATA_ADRESS);
while (pio_device->status==0) {};
pio_device->data=3;


Не придираясь к стилистике (ну раз уж uint32_t, то почему не IO_DEVICE_t? И для чего верхний регистр, если это не константа?) обратим внимание на адрес и его применение:

#define IO_DATA_ADRESS  0x40000004
IO_DEVICE * const pio_device = (IO_DEVICE *) (IO_DATA_ADRESS);

Автор намеренно поместил адрес в скобки при приведении — это хороший тон и помогает избежать ошибок при работе с адресами и адресной арифметикой. Но так почему бы не поместить все дефайны сразу в скобки, чтобы избежать лишних проблем? Мне думается, что хорошо иметь привычку писать так:

#define IO_DATA_ADRESS  (0x40000004)
IO_DEVICE * const pio_device = (IO_DEVICE *) (IO_DATA_ADRESS);

Пара «лишних» скобок поможет избежать трудноуловимых проблем.

Однако в этой части кода присутствует и самая основная ошибка программистов встраиваемых систем. Компилятор взглянет на строчку:
while (pio_device->status==0) {};

И произведет оптимизацию либо в while(1), либо в while(0). Так что обязательно указать volatile ко всей структуре регистров переферии:

typedef volatile struct  {
   uint32_t data;
   uint32_t status;
} IO_DEVICE;
  • +3
  • 28 апреля 2014, 15:35
  • becopt

Комментарии (24)

RSS свернуть / развернуть
volatle надо добавлять не к структуре целиком, а к каждому регистру отдельно.
0
  • avatar
  • amx
  • 28 апреля 2014, 23:00
0
Вот жеж придумали язык. Вместо того, чтоб на нем писать, его всю жизнь изучать надо :)
+1
Впрочем как и любой другой язык, например, Русский язык. :)
+1
О! Это вы приплюснутый «С» не изучали. А по теме: различные компиляторы, различные приёмы эффективного кода.
+1
Да кстати, не подскажите где можно почитать про грамотное оформление кода, типа что константы дефайнятся в верхнем регистре и ...??
0
Например, здесь:

Как писать программы без ошибок

или в этом документе:

Quantum Leaps Coding Standard
0
Как писать программы без ошибок
Много толковых рекомендаций, но насчет планирования автор несколько погорячился. Точнее, это его представление о том, как правильно (по его мнению), к реальной жизни это имеет не очень много отношения. Попытка реализации этого подхода либо ничего не даст (в лучшем случае), либо приведет к еще большему бардаку и потере времени (просто потому, что «гладко было на бумаге, да забыли про овраги»).
+1
Я лично, категорически не согласен с Вами за одним случаем: когда проект является радиолюбительской поделкой или несет в себе минимальный функционал.
В остальных случаях грамотный руководитель прорисовывает программу (алгоритм, конечный автомат, UML'ки), разделяет разработку по модулям и реализует связку Логика+HAL+драйвер. И требует прогонки по Unit Test'ам каждого модуля.
Я успел поработать и там, где реализован описанный выше подход, и где каждый разработчик кодит во что горазд. Как итог — во втором случае процесс разработки растягивается, поддержка кода и передача его усложняется очень сильно, переносимость практически отсутствует.
Хотя, как правило, (к сожалению) всем этим сильно пренебрегают «для ускорения разработки». Что выливается в большие костыли и потерю времени. Но людей не переубедишь.
0
Подозреваю, что evsi говорит о том, что описанный в книге подход (водопад) в реальной жизни не так эффективен, как более гибкие (agile) методологии. И здесь я с ним полностью согласен.

Никто не спорит, что принцип «каждый разработчик кодит во что горазд» — это зло.
+2
(водопад) в реальной жизни не так эффективен, как более гибкие (agile) методологии
Именно.
Никто не спорит, что принцип «каждый разработчик кодит во что горазд» — это зло.
Да, это другая крайность, которая отнюдь не является альтернативой водопаду.
0
Подозреваю, что он говорит об этом :)
0
Я лично, категорически не согласен с Вами за одним случаем: когда проект является радиолюбительской поделкой или несет в себе минимальный функционал.
Строго наоборот: чем сложнее и функционально насыщенее проект, тем больше расхождение между первоначальным планом и тем, что в итоге получается. А то, что в итоге получается, как раз и есть то, что на самом деле хотел заказчик.
Я успел поработать и там, где реализован описанный выше подход, и где каждый разработчик кодит во что горазд.
Кодить кто во что горазд это вторая крайность. Оптимум находится на стыке этих подходов, то есть есть изначальный высокоуровневый план, который по ходу работы уточняется и, при необходимости, части программы переделываются. Вобщем, RTFM на тему agile methods.
+1
А где в исходной статье отрицается итеративный подход? Или где его отрицаю я? :) Там вся мысль гораздо проще: подумай, прежде чем делать.
0
Ссылка «encoding without errors» уже радует.
0
Я бы отталкивался от MISRA C (Сейчас не так актуально, но задаёт хороший тон) и глядел бы, например, на вот эту книгу Embedded C Coding Standard.
Хотя после прочтения книги Кернигана и Ричи уже должно быть сформировано достаточное видение грамотного написания/оформления кода.
И, разумеется, стоит копаться в библиотеках переферии на контроллер, RTOS'ки и готовых проектах на GIT'e.
Также не стоит смешивать в голове рекомендации к оформлению кода на С и на С++.
0
И, разумеется, стоит копаться в библиотеках переферии на контроллер, RTOS'ки и готовых проектах на GIT'e.
Угу. Копаться стоит. Чтобы понимать как делать не надо. В подавляющем большинстве случаев. Со временем надоедает

так «учиться»
0
Угу. Копаться стоит.

Зря Вы так. Копаться в чужом коде полезно.

Чтобы понимать как делать не надо.

Да, и по этой причине в том числе. Плюс, это очень полезный навык — уметь быстро сориентироваться/разобраться в чужом коде.

Вообще, я считаю, что работа в хорошей команде (на ранних этапах «становления» программиста) дает очень сильную «прокачку скила». Со временем эффективность «прокачки» падает. Но на первых этапах, общение с «живыми» специалистами и совместная работа над проектом дает огромный толчок, несоизмеримый с чтением книг, и другими средствами самостоятельного изучения.

Менее эффективный вариант — давать свой код на ревью (попросить знакомого отревьювить код или, например, выложить код в паблик с просьбой прокомментировать)
0
Еще более эффективно — парное программирование.
+1
ну раз уж uint32_t, то почему не IO_DEVICE_t?
Имена типов, оканчивающиеся на "_t", зарезервированы.
0
  • avatar
  • John
  • 29 апреля 2014, 23:07
Я так понимаю, что _t зарезервированы толлко для POSIX-совместимых систем.
Вполне вероятно Вы правы и стоит использовать некий свой префикс/постфикс для создания персонального пространства имён и избегания коллизий.
0
uint32_t и прочие подобные типы являются стандартными (начиная с древнего стандарта C99) и рекомендуются к использованию там, где важна разрядность переменных. Определены в файле <stdint.h>. Тех, кто этим не пользуется и изобретает свои собственные u8, u16 и u32 — надо бить по рукам, я считаю!

Занимательный факт: Microsoft Visual C++ до версии 2010 не имел stdint.h, и его приходилось подкладывать вручную. Все остальные компиляторы давным-давно этот файл имеют «из коробки».
+2
Не придираясь к стилистике (ну раз уж uint32_t, то почему не IO_DEVICE_t? И для чего верхний регистр, если это не константа?) обратим внимание на адрес и его применение:

дальше не читал.
0
PS. Статья на хабре — бредятина полная. Достаточно глянуть 2 примера в конце и вот это вот:
причем быстродействие опять-таки не пострадало, а даже и чуть выросло, поскольку компилятор держит указатель в регистре для и для второй команды его не загружает.

Довольно дилетантское рассуждение о компиляторах и о том, как там используются указатели.
0
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.