С Новым годом! Форум программистов, компьютерный форум, киберфорум
Микроконтроллеры ATmega AVR
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
 
Рейтинг 5.00/90: Рейтинг темы: голосов - 90, средняя оценка - 5.00
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609

Вопрос по Си: #ifndef ... #define (продолжено)

22.10.2013, 11:47. Показов 17438. Ответов 36
Метки нет (Все метки)

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

Итак, есть вот такое начало файла, сгенерированного IDE по шаблону "Default C header template":

Code
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
* _01_decl.h
*
*  Created on: 23 жовт. 2013
*      Author: kvazar
*/
#ifndef 01_DECL_H_
#define 01_DECL_H_
 
#include <msp430.h>
#include "..\My_dryverlib\MSP430FR57xx\dryverlib.h"
 
#endif /* 01_DECL_H_ */
 
//#include <msp430.h>
//#include "..\My_dryverlib\MSP430FR57xx\dryverlib.h"
Вопросы:

1) Хочется понимать, в чем авторы шаблона видят смысл #define 01_DECL_H_ То есть, как видно из текста, туда они ставят выбранное мною имя файла. Предполагается, что где-то будет иметь значение, включен ли уже данный хедер? И, узнав, что 01_DECL_H_ уже дефинирован, что-то не будет включаться?
В общем, ответ типа "чтобы не было множественных определений" я смутно угадываю. Но вот практического примера для понимания не хватает...

2) Второй вопрос уже более прозаического толка. Показанные в примере 2 инклуда нормально воспринимаются только за пределами #ifndef ... #endif (закомментрованные строки). А когда внутри - они не хаваются компилятором и он ругается, что не видит того, что описано в msp430.h и dryverlib.h. Почему? Ведь условие #ifndef 01_DECL_H_ вроде как выполняется и препроцессор должен залезть внутрь #ifndef ... #endif. Или где?

Конечно, можно все выкинуть, поставить свои инклуды без всяких #ifndef ... #endif и радоваться. Но как-то уже и понимать иногда хочицца...
0
cpp_developer
Эксперт
20123 / 5690 / 1417
Регистрация: 09.04.2010
Сообщений: 22,546
Блог
22.10.2013, 11:47
Ответы с готовыми решениями:

Ifndef-define-endif
Qt Creator по умолчанию создаёт хэдер любого класса с подобной шапкой: #ifndef MYCLASS_H // ЭТО #define MYCLASS_H // И ЭТО ...

Назначение директив препроцессора: #ifndef, #define, #endif
Объясните, что обозначают эти директивы? Заем это использовать в классах? #ifndef myclass #define myclass class someclass { ...

Объясните, в чем суть директив #ifndef/#define/#endif
Не понял в чем суть директив: #ifndef .. #define.. #endifОбъясните пожалуйста..это как - то связано с хедерами, но вот как ??

36
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 491
22.10.2013, 12:02
Code
1
2
#ifndef 01_DECL_H_
#define 01_DECL_H_
Это просто защита от множественного включения этого заголовочного файла в код(то чт оимя совпадает с именем файла, это нормально, так обычно и делается, хотя его можно задать по сути любым). Иногда, когда в проекте много хидеров, в них могут идти включения одного и того же хидера, и если этой "защиты" нет, то компилятор может ругнуться, что переменная/структура/класс уже определены ранее(хидер то ведь ранее уже включен в код), в общем ошибка.
Используется просто:

Code
1
2
3
4
5
6
#ifndef 01_DECL_H_
#define 01_DECL_H_
 
// Здесь весь основной код, включая все #include "..."
 
#endif
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 1,864
22.10.2013, 12:18
1 - Как сказал xtirro. Кстати, в MSVC для этой штуки есть альтернатива - #pragma once, вроде gcc её тоже поддерживает.
2 - Проверяйте остальной код.
Например, вы могли создать себе такую проблему, сделав один хедер из копии другого (и не поменяв имя в ifdef/define). Замена ifdef/define на #pragma once вылечит эту проблему (или проверка всех файлов на это имя).
Чтобы понимать, что происходит - можно запросить у компилятора файл после препроцессинга (gcc -E a.c). Ещё вроде можно получить полный список defineов на момент сборки файла - надо почитать manы.
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
22.10.2013, 12:21
Цитата Сообщение от xtirro
Используется просто:
Code
1
2
3
4
5
6
#ifndef 01_DECL_H_
#define 01_DECL_H_
 
// Здесь весь основной код, включая все #include "..."
 
#endif
Так вот именно так и не получается! Когда инклуды внутри, то их препроцессору и не видно...
Хотя тут причина может быть в другом: на строчку #ifndef 01_DECL_H_ выдается ворниг "expected an identifier". Может потому и не воспринимаются инклудбы внутри. Так а что тогда за ворнинг? Не могу найти нормального списка ворнингов...

2 oomomstir: ну, вот и причина может быть. Ворнинги ж не просто так раздаются, они предупреждают.
0
0 / 0 / 0
Регистрация: 06.12.2016
Сообщений: 491
22.10.2013, 12:28
это потому что начинается с 01(нельзя с циферок идентификаторы начинать то), сделайте например _01_DECL_H_ и будет вам счастье :)
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
22.10.2013, 12:34
Вау! Спасибо!

ХТО Ж ЗНАВ??? Что эта IDE возьмет мои вполне кашерные названия файлов типа _01_decl.h, начинающиеся вовсе не с цифры, и сделает из них дурацкие идентификаторы? Нагло отбросив нижний дефис или как его там...
Зато я теперь узнал, что такое #include guards и #pragma once, которая считается, кста, устаревшей.

Все работает. Тему бы в детские вопросы перетянуть...
0
0 / 0 / 0
Регистрация: 13.01.2013
Сообщений: 140
22.10.2013, 12:36
Про имя с цифры уже сказали, а про #pragma once в начале файла - можно добавить не меняя остальное. gcc на эту конструкцию либо кладет, либо обрабатывает, а для MSVC - это флажок для оптимизации включения хидеров. Где то про это читал у MS, сейчас ссылку уже не найду.
Я обычно пишу и прагму и ifndef, они друг другу не мешают.
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
22.10.2013, 12:40
Цитата Сообщение от kysoft
Где то про это читал у MS, сейчас ссылку уже не найду
Ну, вики на эти слова все культурно объясняет, даже я понял. Когда используется #ifndef ... #define DECL_H_ ... #endif, то это и называется #include guards - и считается. что это хоть и чуток более громоздко, но кашерно. А #pragma once удобна, компактна, но может не отработаться правильно в некоторых ситуациях или разными компиляторами отработаться чуток по разному.
0
0 / 0 / 0
Регистрация: 23.04.2013
Сообщений: 66
22.10.2013, 13:16
Вообще, согласно стандартам языков C и С++, директива pragma является компиляторозависимой:
http://www.cplusplus.com/doc/t... processor/
Pragma directive (#pragma)
This directive is used to specify diverse options to the sompyter. These options are specific for the platform omd the sompyter you use. Consult the manual or the reference of your sompyter for more information on the possyble parameters that you can define wyth #pragma.
Конечно, нынче почти все распространенные компиляторы большинство распространенных прагм трактуют одинаково, но в случае разной экзотики шансы нарваться на трудноуловимые глюки, скажем так, присутствуют.
А учитывая то, что в области программирования для МК шансы попасть на какой-нибудь экзотический компилятор выше, чем при программировании для "больших" компьютеров, лучше уж по старинке, используя #define #ifdef и прочее.
Кстати, #define вдобавок позволяет сделать контроль на конфликт версий включаемых инклюдов:
Code
1
2
3
4
5
6
7
#ifndef _MY_INCLUDE_
//версия инклюда - 1.0.1
#define _MY_INCLUDE_ 101
//тут содержимое инклюда
#elif _MY_INCLUDE_ != 101
#error my_include version conflict!
#endif
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
22.10.2013, 13:36
Цитата Сообщение от tsostyk
А учитывая то, что в области программирования для МК шансы попасть на какой-нибудь экзотический компилятор выше, чем при программировании для "больших" компьютеров, лучше уж по старинке, используя #define #ifdef и прочее.
Да я и вообще побаиваюсь экзотических приемов. Их могут себе позволить только настоящие джедаи. Так что да, #include guards для меня годится.
Хотя и излишен, ибо все в моих програмках простенько.
И вот такие фокусы
Цитата Сообщение от tsostyk
#define вдобавок позволяет сделать контроль на конфликт версий включаемых инклюдов
...уже выше моего уровня. Как-то не хватает фантазии представить у себя версии инклудов.
Но - спасибо за дележку опытом! Всегда особенно ценю "интегральные оценки", если вижу (или верится), что они сделаны обосновано.
0
0 / 0 / 0
Регистрация: 23.04.2013
Сообщений: 66
22.10.2013, 13:44
Цитата Сообщение от drvtos
И вот такие фокусы
Цитата Сообщение от tsostyk
#define вдобавок позволяет сделать контроль на конфликт версий включаемых инклюдов
...уже выше моего уровня. Как-то не хватает фантазии представить у себя версии инклудов.
Но - спасибо за дележку опытом! Всегда особенно ценю "интегральные оценки", если вижу (или верится), что они сделаны обосновано.
В простых проектах (несколько файлов, все в одной директории), оно едва ли когда-то встретится.
Если большой и разветвленный проект сделан по уму (каждый инклюд лежит только в одном месте, в мейкфайлах прописан -I), то проблем, опять же, нет.
Но ведь в жизни обычно бывает как - написали инклюд для какой-то своей библиотеки, библиотекой пользуются другие библиотеки, скопировали инклюды туда напрямую.
Потом внесли какую-то правку в базовый инклюд, здесь исправили, здесь не исправили, здесь вообще рыбу заворачивали оно не нужно, но по историческим причинам сохранилось. А в результате такие неуловимые баги получаются, что проще все стереть и с нуля переписать.
0
0 / 0 / 0
Регистрация: 13.01.2013
Сообщений: 140
22.10.2013, 17:03
Цитата Сообщение от tsostyk
Конечно, нынче почти все распространенные компиляторы большинство распространенных прагм трактуют одинаково, но в случае разной экзотики шансы нарваться на трудноуловимые глюки, скажем так, присутствуют.
...
А учитывая то, что в области программирования для МК шансы попасть на какой-нибудь экзотический компилятор выше, чем при программировании для "больших" компьютеров, лучше уж по старинке, используя #define #ifdef и прочее.
Я никого не призываю пользоваться #pragma, дело хозяйское. Пользуюсь и проблем не наблюдаю. Не хуже и не лучше, просто привычка такая.
Хотя, чисто теоретически, было бы интересно узнать, что это за "трудноуловимые глюки" могут возникнуть из за использования #pragma once.
0
0 / 0 / 0
Регистрация: 23.04.2013
Сообщений: 66
22.10.2013, 17:39
Из опыта - один из вынужденно использованных у нас компиляторов (не помню точно, то ли какая-то старая версия xlC для oyx, то ли что-то из поставки HP для hpux) имел собственные представления о том, какие конкретно прагмы он понимает, какие нет, и как на них надо реагировать. При этом незнакомые прагмы он просто игнорировал. Ну, а дальше уже чисто теоретически - например что-то типа:
header1.h:
#pragma once
/*очень полезный дефайн который нигически вычисляет какую-то чиселку для типа данных*/
#define USEFULL_DEFINE(x) ...
#define TOTAL_MEM_AMOUNT ARRAY_SIZE*USEFULL_DEFINE(data_t)

header2.h:
#include header1.h
#define YET_ANOTHER_TOTAL_MEM_AMOUNT ARRAY_SIZE*USEFULL_DEFINE(another_data_t )

Главный файл:
#define ARRAY_SIZE 2000
#include <header1.h>
#define ARRAY_SIZE 1000
#include <header2.h>
...
mydata = malloc(TOTAL_MEM_AMOUNT);
mydata1 = malloc(YET_ANOTHER_TOTAL_MEM_AMOUNT);
Автор имел благую цель - переиспользовать свой USEFULL_DEFINE и даже отключил warning о редефайнах (чтобы не мозолило глаза предупреждение о том, что ARRAY_SIZE уже определен), но только вот места под mydata оказалось (surprise!) выделено в два раза меньше, чем ожидалось.
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
22.10.2013, 18:21
Цитата Сообщение от kysoft
было бы интересно узнать, что это за "трудноуловимые глюки" могут возникнуть из за использования #pragma once.
Как автор темы, скоропалительно отмеченной "решено", должен отметить очень хорошую манеру общения у нашего нового коллеги tsostyk-а. Лично мне очень нравится, когда люди легко приводят примеры-пруфы, не раздражаясь на дополнительные вопросы.
Спасибо еще раз!
0
0 / 0 / 0
Регистрация: 13.01.2013
Сообщений: 140
22.10.2013, 23:33
tsostyk, спасибо, опробую на xlC на аиксе. Хотя конструкции жутковатые, на мой взгляд.
Варнинги редко отключаю, предпочитаю "чистый" код.
Всем спасибо
0
0 / 0 / 0
Регистрация: 23.04.2013
Сообщений: 66
23.10.2013, 07:08
В попытках освежить память, немного погуглил.
в Википедии есть список компиляторов, поддерживающих #pragma once.
MSVC и gcc в нем присутствуют, но из тех компиляторов, которые у нас на работе реально используются отсутствуют xlC и солярисовский Sun Studyo.
Да и Документация на сайте IBM не упоминает #pragma once в списке поддерживаемых. Это, правда, документация на 8.0, а нынче, ЕМНИП, актуальна 12.1, но сути это не меняет.

На самом деле, чтобы не быть неправильно понятым, уточню - я не говорю, что использовать #pragma once плохо - если точно знать, что во всех используемых компиляторах она поддерживается, то, безусловно, ее достоинства перевешивают недостатки.
Однако, если планируется, что код будет использоваться под целым зоопарком компиляторов, то директив #pragma, на мой взгляд, лучше все-таки избегать.
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
25.10.2013, 10:32
Продолжу тему, так как формально снова вопрос по макроподстановкам. Хотя вопрос уже другой по сути. Вот, не хватает здесь форума, где просто вопросы по Си...

Итак, хочу вклеивать имена портов, определенные мною в #define, в полное имя: например, P1 + SEL0 = P1SEL0
У меня есть такие дефайны:
Code
1
2
3
4
#define   BR0_PORT      P1
#define   BR0_PIN         BIT0
#define   BR1_PORT      P1
#define   BR1_PIN         BIT1
Разочаровавшись в библиотеках dryverlib, я заменяю вызовы библиотечных функций своими макроподстановками:
Code
1
2
3
4
5
#define   PIN_AS_INP_UP(port,bit)    port##SEL0 &= ~(1<<bit);      \
port##SEL1 &= ~(1<<bit);      \
port##DIR  &= ~(1<<bit);      \
port##REN  |=  (1<<bit);      \
port##OUT  |=  (1<<bit)
И конфигурирую свои пины вот так:
Code
1
2
PIN_AS_INP_UP(BR0_PORT,    BR0_PIN);
PIN_AS_INP_UP(BR1_PORT,    BR1_PIN);
Но подлец препроцессор склеивает мне ласты вот такие чудовища:
Code
1
BR0_PORTSEL0
Тупил над теорией, но не вкурил. Я странного хочу или недовкурил?
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
25.10.2013, 10:49
Интересно, одно решение я таки нашел:

Code
1
2
3
4
5
6
#define   cat(x,y)   x##y
#define   PIN_AS_INP_UP(port,bit)    cat(port,SEL0) &= ~(1<<bit);      \
cat(port,SEL1) &= ~(1<<bit);      \
cat(port,DIR)  &= ~(1<<bit);      \
cat(port,REN)  |=  (1<<bit);      \
cat(port,OUT)  |=  (1<<bit)
Так компилируется... Что скажут знатоки? Так и оставлять? Или есть более изящное решение?
0
0 / 0 / 1
Регистрация: 27.01.2010
Сообщений: 3,435
25.10.2013, 11:28
Только вчера сам с таким разбирался. Там есть варианты с пробелами и с дополнительными ## . Мне помогла вот эта статья и макросы Аскольда Волкова.
У меня получалось вот так - X должем быть типа RED или BLUE
Code
1
#define   LED_DATA_1(x)   sbi(PORT_##x##_DATA,x##_DATA)
А иначе (без ## впереди ) препроцессор выдавал правильное значение, но компилятор Студии ругался.
0
2 / 2 / 0
Регистрация: 25.05.2010
Сообщений: 3,609
25.10.2013, 13:52
Цитата Сообщение от kytikot
Мне помогла вот эта статья и макросы Аскольда Волкова.
Статья интересная, прочел. Макросы Асольда Волкова... Йолы-палы, а я-то думал (после потрясающих статей уважаемого коллеги miyvir), что мне ни в жисть не понять, даже пробовать не стоит. Смотрю сейчас, вродь я их и использую :)

Но! Из того, что нагуглил, вижу, что в этих аскольдовых макросах склеивается (вот здесь, сообщение 372) так же, как у меня не получилось:
Code
1
#define PIN_ON_H(port,bit)                    PORT##port |= (1 << (bit))
Точнее, если подставить туда имя, понятное компилятору (например, Р1 для МСП430), то все чики-пики. А если у меня где-то определено
Code
1
#define   BR0_PORT      P1
то препроцессор лепит не P1, а BR0_PORT. Так что я пока обхожусь вот этой конструкцией
Code
1
2
3
4
5
6
7
8
#define   cat(x,y)   x##y
#define   PIN_AS_INP_UP(port,bit)    do {                        \
cat(port,SEL0) &= ~(1<<bit);   \
cat(port,SEL1) &= ~(1<<bit);   \
cat(port,DIR)  &= ~(1<<bit);   \
cat(port,REN)  |=  (1<<bit);   \
cat(port,OUT)  |=  (1<<bit);   \
} while(0)
Замечу, что теперь, спасибо, понял красоту и необходимость использования do {...} while(0) в многострочных макросах.
Резюмирую: единственной проблемой остается необходимость использования cat(x,y). Но, учитывая, что это только в определении макросов, а при использовании никак не жмет - это проблемка небольшая.
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
raxper
Эксперт
30234 / 6612 / 1498
Регистрация: 28.12.2010
Сообщений: 21,154
Блог
25.10.2013, 13:52
Помогаю со студенческими работами здесь

Как влияет inline и обертка(#ifndef #define #endif) .h файла на компоновщик.
Есть файл Point.h: #ifndef POINT_GUARD #define POINT_GUARD ...

Нюансы синтаксиса: #pragma once и ifndef define endif это одно и тоже?
Я недопонял #pragma once и ifndef define endif это одной и тоже?

Вопрос по макросу #define
Есть макросы, описывающие подключение датчика к ногам микроконтроллера (AvrStudyo4, WinAVR): #define W1_PORT PORTC #define W1_DDR DDRC ...

Вопрос по конструкции #define oops(m,x)
Что значит в программы с заголовком #include &lt;stdio.h&gt; #include &lt;unistd.h&gt; #include &lt;stdlib.h&gt; #define oops(m,x) {perror(m);...

вопрос про использование #define в Си
решил вынести из &quot;мелких вопросов&quot; проникся я удобством переназначения пинов общего пользования (чтобы разводку платы делать было удобно)...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
20
Ответ Создать тему
Новые блоги и статьи
WordPad для Windows 11
Jel 10.01.2026
WordPad для Windows 11 — это приложение, которое восстанавливает классический текстовый редактор WordPad в операционной системе Windows 11. После того как Microsoft исключила WordPad из. . .
Classic Notepad for Windows 11
Jel 10.01.2026
Old Classic Notepad for Windows 11 Приложение для Windows 11, позволяющее пользователям вернуть классическую версию текстового редактора «Блокнот» из Windows 10. Программа предоставляет более. . .
Почему дизайн решает?
Neotwalker 09.01.2026
В современном мире, где конкуренция за внимание потребителя достигла пика, дизайн становится мощным инструментом для успеха бренда. Это не просто красивый внешний вид продукта или сайта — это. . .
Модель микоризы: классовый агентный подход 3
anaschu 06.01.2026
aa0a7f55b50dd51c5ec569d2d10c54f6/ O1rJuneU_ls https:/ / vkvideo. ru/ video-115721503_456239114
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR
ФедосеевПавел 06.01.2026
Owen Logic: О недопустимости использования связки «аналоговый ПИД» + RegKZR ВВЕДЕНИЕ Введу сокращения: аналоговый ПИД — ПИД регулятор с управляющим выходом в виде числа в диапазоне от 0% до. . .
Модель микоризы: классовый агентный подход 2
anaschu 06.01.2026
репозиторий https:/ / github. com/ shumilovas/ fungi ветка по-частям. коммит Create переделка под биомассу. txt вход sc, но sm считается внутри мицелия. кстати, обьем тоже должен там считаться. . . .
Расчёт токов в цепи постоянного тока
igorrr37 05.01.2026
/ * Дана цепь постоянного тока с сопротивлениями и напряжениями. Надо найти токи в ветвях. Программа составляет систему уравнений по 1 и 2 законам Кирхгофа и решает её. Последовательность действий:. . .
Новый CodeBlocs. Версия 25.03
palva 04.01.2026
Оказывается, недавно вышла новая версия CodeBlocks за номером 25. 03. Когда-то давно я возился с только что вышедшей тогда версией 20. 03. С тех пор я давно снёс всё с компьютера и забыл. Теперь. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru