Перейти к содержанию
    

Стиль программирования на Си

Попробуй таки найти текстовым поиском "#define IN" и "#define OUT"

или "отбросить" их как нечто ненужное;)

#define IN

#define OUT

у меня, например, в кейле используется

#define __flash

чтобы код из IAR не трогать

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Попробуй таки найти текстовым поиском "#define IN" и "#define OUT"

или "отбросить" их как нечто ненужное;)

#define IN

#define OUT

у меня, например, в кейле используется

#define __flash

чтобы код из IAR не трогать

А вот на такое безобразие препроцессор сругаться должен. Вообще-то #undef для этих целей существует ;)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вот на такое безобразие препроцессор сругаться должен

Вот и найдётся;)

А #undef существует для случаев когда точно знаешь, что нужно нечто переопределить. Сложнее переопределить чего-нить типа

typedef enum bool{

false = 0,

true = -1

};

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А #undef существует для случаев когда точно знаешь, что нужно нечто переопределить.
Дык

#ifdef __flash
  #undef __flash
#endif

и всех делов :)

Сложнее переопределить чего-нить типа
Вот именно что "сложнее", потому и надежнее.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Гы;) Мне код перебивать не нужно. Мне как раз единожды написанное под яром нужно не править - у меня текст один для кейла и яра, а ненужные квалификаторы просто выбрасываются после разруливания;)

потому и надежнее.

Тут в примере особо изощренный случай. Такое "надёжное" лучше бы не попадалось вовсе. Пример наглого использования умолчаний Си

bool is_leap(int year){

if(!(year%400))return true;

if(!(year%100))return false;

if(!(year%4))return true;

return false;

}

int Get_Days_In_Month(int month, int year){

return(Days_Array[month]+(month==2)?is_leap(year):0);

}

при таких объявлениях bool просто веселит:)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

препроцессором не рекомендуется пользоваться именно из-за проблем с поиском ошибок (в основном), так как во что разворачивается макроопределение выяснить практически нереально (достоверно), отлаживаться и искать ошибки крайне неудобно. Поэтому были введены в язык средства, позволяющие практически полностью исключить использование препроцессора.

Список случаев, когда препроцессор оправдан уже был озвучен.

 

Правда, я лично использую его неправильно, надо отучаться...

Для маленьких проектов, например, для микроконтроллеров типа АВР, разницы никакой, но привычка переносится и на большие, а там проявляются все эти кажущиеся надуманными проблемы.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

препроцессором не рекомендуется пользоваться

Простите, но кем и где не рекомендуется? В стандарте?

во что разворачивается макроопределение выяснить практически нереально (достоверно)

ИМХО, это больше зависит от стиля написания и принятого механизма проверок.

ЗЫ Во встраиваемых устройствах до недавнего времени довольно часто приходилось экономить ОЗУ при достаточном объёме памяти программ. Грамотное использование препроцессора позволяет более рационально использовать память программ, высвобождая ОЗУ.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Так там рекомендуется использовать препроцессор только тогда, когда это необходимо. Объясняется все просто: код, активно использующий препроцессор сложно читать, сопровождать и отлавливать ошибки.

 

Пишем, пишем...

Объявляем трехэтажный мат #define func_type_macro(arg1,arg2,arg3) .....

Применяем объявленный трехэтажный в 10-ти этажной конструкции нужное количество раз. В куске кода, размер которого никого не испугает.

..........................................

Отменяем #undef func_type_macro

 

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Просветите, плз, откуда такая категоричная постановка?

Уже неоднократно на эту тему проходились, повторять не охота, воспользуйтесь поиском.

 

Вкратце. Макроподстановка таит в себе опасность из-за того, что делается тупо, без контроля типов и контекста, в котром она производится. При этом пользователь видит одно, а получает другое. Наглядный пример тому - соседняя ветка про вычисления в макросах, когда пара лишних скобок меняла контекст выражения и вычисление выражения с этапа компиляции переносилось на этап выполнения программы, что предъявляло совершенно другие требования по потреблению стека, в частности. Причем все это происходит скрытно от пользователя, в этом и подлость.

 

Отсутствие анализа контекста несет еще косяки - недавно обсуждали: например, фирма IAR ввела для некоторых новых чипов AVR макроопределения в заголовочных файлах ioxxx.h типа

 

#define N 2
#define C 0

 

Теперь, если пользователь использует где-то внутри, в недрах функций (да хоть в списке аргументов прототипов) простое короткое имя N или C, то все, привет. Причем, в ряде случаев весьма неочевидно, в чем ошибка. И хорошо, если еще компилятор заругается на что-то, а то ведь может и прокатить компиляция, а в железе не работает. Вот тут попляшешь с бубном. А все потому, что препроцессор работает тупо и кладет на пространства имен, контекст выражения и делает свою работу скрытно.

 

Некоторое время назад попался прикол (возможно байка, но все равно забавно): в исходниках, оставшихся от недавно уволенного программиста после долгой и болезненной отладки обнаужили такую сточку в одном из заголовочных файлов:

 

#define TRUE FALSE // счастливой отладки, суки

:lol:

 

Список примеров можно продолжать, то повторять не хочется, уже обсуждалось не раз.

 

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

 

Почему-то приходится применять гораздо шире. Где не вижу тонкой грани?

А вот мне не приходится. И все получается. Приходится применять только в рамках вышеописанного - для условной компиляции и простых подстановок при портировании.

 

Псевдонимы типов прекрасно решаются с помощью typedef, о чем уже сказали.

 

Макросы-литералы замечательно заменяются настоящими типизированными константами. При этом никакого оверхеда не возникает - если нет попытки взять адрес константного объекта или завести ссылку на него, то компилятор не будет его размещать в памяти. Правда, это относится к С++, где у констант по умолчанию внутреннее связывание. Но, думается, сегодня уже нет проблем использовать плюсатый компилятор. В случае с С для получения такого же поведения константные объекты придется обзывать дополнительно static.

 

Макросы-функции без проблем заменяются встраиваемыми (inline) функциями. Это на сегодняшний день штатно умеют уже не только С++, но и С компиляторы. По Стандарту.

 

Если требуются какие-то нетривиальные вычисления и надо, чтобы они гарантировано производились на этапе компиляции (во избежание оверхеда), то существует мнение (и я его придерживаюсь), что такие вещи лучше вообще выполнять внешними инструментами и подсовывать компилятору уже готовые значения (потому как компилятор - не калькулятор). Через опцию -D, либо вообще поместив их в отдельный файл, который включается в проект. При использовании систем сборки (make, scons) технических трудностей не возникает никаких. Да и с оболочками тоже проблем во многих случаях нет. В любом случае не слишком разумно заставлять пыжиться целочисленный препроцессор с его 32-битной арифметикой - он отомстит (что часто и происходит). :)

 

Если все это вас не убедило, поищите в сети мнение авторитетного дяденьки Б.Страуструпа (автора языка С++), возможно, он подробнее объяснит. :)

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

ПОКАЖИ

 

typedef struct TYPE{

......

......

......

} TYPE

 

TYPE var;

......

 

Да, конечно, создание структуры созданием нового типа назвать нельзя.

Правильнее говорить "структура типа type" вот что я имел ввиду.

sKWO спросил, насколько я понял, "что может быть если где-то встречается нстнадартный тип"

Изменено пользователем Axxel

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Уже неоднократно на эту тему проходились, повторять не охота, воспользуйтесь поиском.

Простите, но что искать? По слову макрос? Я тут довольно часто, но мимио видно всё интересное по теме пробежало.

Макроподстановка таит в себе опасность

Я не спорю, что пользоваться нужно уметь, а тем более таких глупых вещей, как примере от IAR, не допускать (с примерчиком познакомился ранее просматривая хедеры).

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

А вот тут начинается полный субъективизм. Обратный пример - неинициализированные элементы структур и, как самые приятные;) варианты, неинициализированные указатели на функции и использование указателей на структуры в качестве формальных параметров функции. Необходимость выдерживать порядок инициализации приводит к ещё более "подлым" ошибкам, чем жесткое конфигурирование с использованием макроподстановок (ну нет шаблонов в Си). Но, ИМХО, всё это имеет право на использование, если это не самоцель.

Макросы-литералы замечательно заменяются настоящими типизированными константами. При этом никакого оверхеда не возникает - если нет попытки взять адрес константного объекта или завести ссылку на него, то компилятор не будет его размещать в памяти. Правда, это относится к С++, где у констант по умолчанию внутреннее связывание.

К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная.

то существует мнение (и я его придерживаюсь)

опять же всё субъективно. Если не понимать ограничений препроцессора, то можно и в самом простом ошибиться. (Я, например, для редких случаев сложных константных выражений применяю тесты на интерпретаторе Ch)

 

Но, думается, сегодня уже нет проблем использовать плюсатый компилятор

К сожалению, есть.

 

Некоторое время назад попался прикол (возможно байка

Аналогично

#define else ;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Пример winavr

eeprom_read_block(&my_variable,&my_param,sizeof(my_variable));

 

Ну не хочу я стока писАть !!!

 

#define eeread(Var,Param) eeprom_read_block(&Var,&Param,sizeof(Var))
..........................
eeread(my_variable,my_param);

 

Интересует субъективное мнение:

оправдано ли в данном случае применение макро?

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Простите, но что искать? По слову макрос? Я тут довольно часто, но мимо видно всё интересное по теме пробежало.

 

Я расскажу интересное! :) Не далее как вчера час искал отсутствие фигурных скобок в макросе из двух команд, который использовался в if /else. На фоне какой-то непонятной потери синхронизации с JTAGом повеселился по полной программе. Сам себе клоун. :(

 

Тем не менее сложные макросы использую, вариант с функциями inline не перекрывает всех возможных ситуаций. Использую один раз созданную конструкцию для организации доступа к переменным в протоколе обмена и переношу из проекта в проект, один раз отладив стиснув зубы :)

 

Но создавать такое в каждом проекте - упаси Бог.

 

Для того, чтобы было понятно, о чем разговор, пример текста описания переменных.

В поддержку протокола в объект 'system' включаются 4 переменных со своими именами, и функциями, вызываемыми при чтении и записи. Все это разворачивается в массивы строк и указателей на функции.

Добавление переменной в протокол происходит за минуту максимум.

 

IMPL_PROLOG(system)
  IMPL_VARIABLE(system, 0, "HTI 16V hose pumps",
                IMPL_GET dpr->tx_value=(long)COUNT_ELEMENTS(system_names); return;,
                IMPL_SET READ_ONLY
                           )
  IMPL_VARIABLE(system, 1, "Firmware version",
                IMPL_GET dpr->name=version; return;,
                IMPL_SET READ_ONLY
                )
  IMPL_VARIABLE(system, 2, "Objects quantity",
                IMPL_GET dpr->tx_value=(long)OBJECTS_QUANTITY; return;,
                IMPL_SET READ_ONLY
                )
  IMPL_VARIABLE(system, 3, "Heartbeat Period,s",
                IMPL_GET GET_HEARTBEAT,
                IMPL_SET SET_HEARTBEAT
                )
IMPL_EPILOG()

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная.

 

Еще добавлю примерчик

static PROGMEM char array[3] = {'A','B','C'};
................................
char a;
a = array[0];

 

Winavr сделает a='A' , поскольку array[0] рассматривается как константное выражение.

Сделают ли то же самое компилеры с услужливым сервисом "чтения флеша" - не уверен.

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

А вот тут начинается полный субъективизм. Обратный пример - неинициализированные элементы структур и, как самые приятные;) варианты, неинициализированные указатели на функции и использование указателей на структуры в качестве формальных параметров функции. Необходимость выдерживать порядок инициализации...

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

 

К сожалению, это, IMHO, более желаемое, чем действительное. const переменная это все-таки переменная.

Вернее - это констатный объект. Если нет необходимости размещать его в памяти (не берется адрес и нет ссылки на него), то никакой вменяемый компилятор этого делать не будет - именно для этой возможности в том же С++ специально ввели правило, согласно которому константные объекты имеют внутреннее связывание... Хорошо, не верите, давайте проверим. Поскольку тут форум по AVR, то на нем и будем тренироваться. Код:

 

// file: slon.cpp

const int a = 10;

int f(int x)
{
    return x + a;
}

 

Комилятор - какой-то от IAR. v4.хх. Не суть важно. Результат:

 

          const int a = 10;                                     
                                                                       
                                                                        
                              In segment CODE, align 2, keep-with-next 
          int f(int x)                                                 
                  ??f:                                                 
          {                                                            
              return x + a;                                            
00000000   5F06               SUBI    R16, 246                         
00000002   4F1F               SBCI    R17, 255                         
00000004   9508               RET                                      
          }

 

Как видим, все замечательно получилось, ничего в памяти не размещается, никаких обращений к памяти, ессно, нет. Это в режиме ++. Отключаем его - в С режиме результат:

 

                                                                   
                     In segment NEAR_I, align 1, keep-with-next    
                     REQUIRE `?<Segment init: NEAR_I>`             
const int a = 10;                                                 
         a:                                                        
                     DS 2                                          
                     REQUIRE `?<Initializer for a>`                
                                                                   
                                                                   
                     In segment CODE, align 2, keep-with-next      
int f(int x)                                                      
         f:                                                        
{                                                                 
     return x + a;                                                 
  5F06               SUBI    R16, 246                              
  4F1F               SBCI    R17, 255                              
  9508               RET                                           
}                                                                 
                                                                   
                     In segment NEAR_ID, align 1, keep-with-next   
         `?<Initializer for a>`:                                   
  000A               DW 10

 

Да, тут, как и положено, константа 'a' размещена в памяти (и инициализатор тоже съест свою часть в сегменте инициализаторов), потому что компилятор не имеет права этого не сделать - в С константные объекты имеют по умолчанию внешнее связывание и к ним может производиться обращение из других единиц компиляции. В этом случае линкер будет материться на отсутствие объекта.

 

Но даже в этом случае компилятор, видя значение констатнты, не стал городить обращение к ней, а сгенерил более короткий эффективный код.

 

Поведение можно сделать как в ++ варианте, снабдив объявление констатны квалификатором static:

 

static const int a = 10;

 

Результат аналогичен первому варианту:

 

 static const int a = 10;                                      
                                                               
                                                               
                     In segment CODE, align 2, keep-with-next  
int f(int x)                                                  
         f:                                                    
{                                                             
     return x + a;                                             
  5F06               SUBI    R16, 246                          
  4F1F               SBCI    R17, 255                          
  9508               RET                                       
}

 

Думается, что написать ключевое слово 'static' не является слишком обременительным.

 

Кстати, сделать в С++ поведение констант аналогичным С-шному можно сделать с помощью объявления их как 'extern'. Результат компиляции приводить не буду, поверьте, оно точно соотвествует вышеприведенному С без 'static'.

 

К сожалению, есть.

И в чем они состоят? Для AVR хоть коммерческий IAR, хоть безплатный AVR-GCC давно уже поддерживают почти все фичи С++ (за исключением тяжелых, которые реально применять на AVR смысла нет).

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить в этой теме...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

×
×
  • Создать...