haker_fox 60 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Здравствуйте! До недавнего времени я использовал только си. В настоящее время я стараюсь писать максимально на языке си++, правда мои старания пока ограничиваются классами и некоторыми другими возможностями этого языка. Суть моего вопроса: каким образом может сказаться использование только одних #define, вместо рекомендуемых const? Просто для меня лично более понятно это: /* Valve's pin */ #define VLV_TURN_LEFT_PIN 0 #define VLV_TURN_RIGHT_PIN 1 #define VLV_UP_PIN 2 #define VLV_DOWN_PIN 3 #define VLV_FORWARD_PIN 4 #define VLV_BACK_PIN 5 #define VLV_OPEN_PIN 6 #define VLV_CLOSE_PIN 7 Чем примерно такое: const int VLV_TURN_LEFT_PIN 0 и т.д. В данном случае конечно можно использовать (как я правильно понял) enum. Но мне нравятся дефайны. Может ли иметь это какие-либо неприятные последствия в будущем? Заранее спасибо за ответы и сорри за ламерский вопрос... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
DRUID3 0 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Чивото я совсем не понял ничего в Вашем вопросе :07: . #define - это работа с препроцессором (ну даже не знаю как назвать, автоматическим текстовым редактором исходного кода программы, что ли). Т.е. используя такую директиву, Вы просто заменяете в "отправляемом на компиляцию" тексте программы VLV_TURN_LEFT_PIN на 0. Ни подо что не отводится память, подстановка никак не влияет и на время работы исполняемой программы. А const int VLV_TURN_LEFT_PIN = 0; это переменная типа int с именем VLV_TURN_LEFT_PIN и равная "0" которую нельзя изменять. Она лежит в памяти (как во Flash так и в RAM во время исполнения). Хотя странно что Вы пропустили знак "равно", может это какой-нить "неклассический" компилятор. :07: Но вобщем #define и const не кружатся в вечном танце диалектической борьбы, это как "овальный" и "серый" :) да и по-моему, в С const есть очень давно. А уж enum, да, это злобный агент C++. В "+ы" перечислениям ставят то, что при инициализации констант компилятор может выполнить проверку типов(1). А еще, поскольку перечисления - это типы, определенные пользователем то для них можно перегрузить операции (2)(по умолчанию как для int) и тем самым рискнуть попасть под расправу людей работающих над этим же проектом ... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
haker_fox 60 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Чивото я совсем не понял ничего в Вашем вопросе :07: . #define - это работа с препроцессором (ну даже не знаю как назвать, автоматическим текстовым редактором исходного кода программы, что ли). Т.е. используя такую директиву, Вы просто заменяете в "отправляемом на компиляцию" тексте программы VLV_TURN_LEFT_PIN на 0. Ни подо что не отводится память, подстановка никак не влияет и на время работы исполняемой программы. А const int VLV_TURN_LEFT_PIN = 0; это переменная типа int с именем VLV_TURN_LEFT_PIN и равная "0" которую нельзя изменять. Она лежит в памяти (как во Flash так и в RAM во время исполнения). Хотя странно что Вы пропустили знак "равно", может это какой-нить "неклассический" компилятор. :07: Но вобщем #define и const не кружатся в вечном танце диалектической борьбы, это как "овальный" и "серый" :) да и по-моему, в С const есть очень давно. А уж enum, да, это злобный агент C++. В "+ы" перечислениям ставят то, что при инициализации констант компилятор может выполнить проверку типов(1). А еще, поскольку перечисления - это типы, определенные пользователем то для них можно перегрузить операции (2)(по умолчанию как для int) и тем самым рискнуть попасть под расправу людей работающих над этим же проектом ... Знак равно пропустил по невнимательности, конечно же он там должен быть... Я понимаю, что #define это работа с препроцессором, т.е. идет подстановка вместо макроса, того значения, которое определяет последний. Просто я читал в рекомендациях к этому языку, что следует использовать именно const. Только не понятно, какой в этом смысл... Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
Losik 1 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба основной смысл в том, что >>при инициализации констант компилятор может выполнить проверку типов(1). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
scifi 1 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба До недавнего времени я использовал только си. В настоящее время я стараюсь писать максимально на языке си++, правда мои старания пока ограничиваются классами и некоторыми другими возможностями этого языка. Я где-то читал, что если не использовать виртуальные функции, то всё, что делается на Си++, можно сделать и на Си. Так что стоит ли огород городить? В данном случае конечно можно использовать (как я правильно понял) enum. Но мне нравятся дефайны. Может ли иметь это какие-либо неприятные последствия в будущем? Моё личное мнение - никаких проблем с #define я тут не вижу. Как верно сказал DRUID3, enum хорош тем, что возможна проверка типов, но при этом придётся его явно преобразовывать к int там, где это нужно. Конечно, пуристы из лагеря Си++ скажут, что #define - это тяжёлое наследие Си, и что в Си++ есть средства получше. Но только Вам решать, что использовать, а что нет. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 34 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Здравствуйте! До недавнего времени я использовал только си. В настоящее время я стараюсь писать максимально на языке си++, правда мои старания пока ограничиваются классами и некоторыми другими возможностями этого языка. Суть моего вопроса: каким образом может сказаться использование только одних #define, вместо рекомендуемых const? Просто для меня лично более понятно это: /* Valve's pin */ #define VLV_TURN_LEFT_PIN 0 ... #define VLV_CLOSE_PIN 7 Чем примерно такое: const int VLV_TURN_LEFT_PIN 0 и т.д. В данном случае конечно можно использовать (как я правильно понял) enum. Но мне нравятся дефайны. Может ли иметь это какие-либо неприятные последствия в будущем? Заранее спасибо за ответы и сорри за ламерский вопрос... Дефайн - и в С, и в С++ это просто тупой макрос, тупой в том смысле, что когда он встречается, то производится просто тупая макроподстановка безо всякого анализа того, что получается в результате. Это зачастую является источником труднообнаруживаемых багов - в исходном коде одно, а компилятор видит другое. Константный же объект - это объект, обладающий типом, поэтому он прямо попадает под контроль типов компилятора, что делает его использование безопасным и однозначным. С точки зрения реализации констатны в С++ нечем не уступают препроцессорным макросам (дефайнам). Макросы по большому счету к месту использовать только для нужд условной компиляции. А const int VLV_TURN_LEFT_PIN = 0; это переменная типа int с именем VLV_TURN_LEFT_PIN и равная "0" которую нельзя изменять. Она лежит в памяти (как во Flash так и в RAM во время исполнения). Это в С она лежит в памяти. Из-за того, что у константных объектов в С по умолчанию внешнее связывание. А в С++ у константных объектов по умолчанию внутреннее связывание, что позволяет компилятору оптимизировать константу и не размещать ее в памяти - все приличные компиляторы сегодня это умеют. В памяти констатный объект обязан быть размещен только в двух случаях: 1. Явно указано внешнее связывание этого объекта - с помощью ключевого слова extern. 2. В коде присутствует взятие адреса этого константного объекта. В обоих этих случах компилятору ничего не остается, как разместить объект памяти. В других случаех это не не обязательно, чем и пользуется оптимизатор компилятора. Итого, использование того же // file.h int a = 10; ... void f(int x); // file.cpp ... f(a); не приведет ни к какому размещению константы 'a' в памяти - будет точно такая же подстановка, как и в случае с макросом. Но уже с контролем типов. А уж enum, да, это злобный агент C++. В "+ы" перечислениям ставят то, что при инициализации констант компилятор может выполнить проверку типов О какой проверке типов идет речь? (1). А еще, поскольку перечисления - это типы, определенные пользователем то для них можно перегрузить операции Типами, определяемыми пользователем, в С++ являются классы и только они. Для них, действительно, можно перегрузить операции. Перечислымый тип enum не является типом, определяемым пользователем, и для него никакие операции перегрузить нельзя. Единственным назначением перечислимых типов является ограничение принимаемых значений с возможностью задания им сивмолических имен. Перечислимый тип даже не вводит своего пространства имен (scope) и имена его значений видны в том пространстве имен, где он объявлен. Кстати, перечислимый тип в нормальном виде присутствует только в С++ - он именно вводит перечень принимаемых значений. В С enum штука почти вообще безполезная - с объектами этого типа можно делать все, что угодно - присваивать им любые значения, производить арифметические действия. Словом, он по сути не отлчичается от обычных интегральных встроенных типов. В С++ такие финты не проходят - там область значений задана определением перечислимого типа, и пихнуть туда левое значение можно только принудительно (через ручное преобразование типов). Я где-то читал, что если не использовать виртуальные функции, то всё, что делается на Си++, можно сделать и на Си. Так что стоит ли огород городить? И виртуальные функции - точнее, функциональность, реализуемую ими, можно достаточно просто реализовать на С. И вообще, любую программу можно написать на ассемблере. Моё личное мнение - никаких проблем с #define я тут не вижу. Например, такую проблему Вы не видите? #define DOUBLE(x) x+x int a = 3*DOUBLE(4); Что ожидаем получить? Ожидаем получить 3*8 = 24. А на деле что получим? А получим 3*4+4 = 16. Конечно, этот известный, описанный в учебниках эффект легко обходится с помощью применения скобок: #define DOUBLE(x) (x+x) Но там и кроме этого приколов хватает. По той же причине - скрытая от программиста подстановка значений безо всякого контроля со сторны компилятора. Именно по этой причине препроцессор в кодогенерации маст дай. Как верно сказал DRUID3, enum хорош тем, что возможна проверка типов, но при этом придётся его явно преобразовывать к int там, где это нужно. Какая именно провека типов имеется в виду? Конечно, пуристы из лагеря Си++ скажут, что #define - это тяжёлое наследие Си, и что в Си++ есть средства получше. Но только Вам решать, что использовать, а что нет. Дефайны - это наследине С. Насколько тяжелое, не берусь судить. Иногда весьма тяжеловатое. Дабы избежать пробем с дефайнами и макропостановкой, надо просто всякую вещь использовать к месту - макросы в условной компиляции, тут им замены пока нет, а в качестве констант использовать константные объекты, в качестве макрофункций - встраевыемые (inline) функции, к качестве генераторов кода - шаблоны. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба В С enum штука почти вообще безполезная - с объектами этого типа можно делать все, что угодно - присваивать им любые значения, производить арифметические действия. Словом, он по сути не отлчичается от обычных интегральных встроенных типов. Ну не перегибайте палку :). В абсолютно подавляющем большинстве случаев и в 'C' enum нормально выполняет свою функцию. Да его можно постараться обмануть, да многое отдано на откуп компиляторам и некоторые компиляторы на, например, арифметические действия с enum и wanings не выдадут.... Но "почти вообще бесполезная" это явный перебор - настоятельно рекомендую пользоваться и не ограничиваться банальными #define. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
HSC 0 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Практически все что было сказано выше в этой ветке верно. От себя хочу добавить пример который иллюстрирует преимущество констант перед define'ами. Представьте себе что вы имеете #define MY_ID 123. Если при написании кода вы допустили ошибку связанную с MY_ID, и при компиляции вы получите ошибку компилятора, то имени MY_ID в описании этой ошибки не будет. Связано это с тем, что еще до компилятора препроцессор заменил MY_ID на 123. И поэтому в сообщении об ошибке будет фигурировать именно 123. А если define в код ввели не вы, а кто-то другой, то на отлов ошибки уйдет масса времени. И прочитайте кстати правило номер 1 из книги Скота Майерса "Эффективное использование С++, 50 рекомендаций". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Представьте себе что вы имеете #define MY_ID 123. Если при написании кода вы допустили ошибку связанную с MY_ID, и при компиляции вы получите ошибку компилятора, то имени MY_ID в описании этой ошибки не будет. Ну это самая ничножная из всех проблем - строчку компилятор покажет, на идентификатор выругается и дальше, если даже несколько констант, но по тэгам в нормальном редакторе найти битую буквально несколько секунд. Вот с макроподстановками функций дело может оказаться действительно запутанее, но рецепт прост :) пишите их внимательнее :), если конечно inline нельзя использовать. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 34 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Ну не перегибайте палку :). В абсолютно подавляющем большинстве случаев и в 'C' enum нормально выполняет свою функцию. Да его можно постараться обмануть, да многое отдано на откуп компиляторам и некоторые компиляторы на, например, арифметические действия с enum и wanings не выдадут.... Но "почти вообще бесполезная" это явный перебор - Вы хотите сказать, что на: enum { SLON = 10, MAMONT= 15, KOT } a; a = 12; компилятор С дает ошибку? Мой опыт говорит, что не дает, и по Стандарту не должен - это не запрещенное действие. Максимум компилятор может выдать предупреждение. IAR для AVR, например, дает на этот код предупреждение: "slon.cpp",15 Warning[Pe188]: enumerated type mixed with another type типа, смешение типов. А на а++; даже и предупреждения не дает (не обязан). Хотя тут арифметика - т.е. преобразование к целому, инкремент, присваивание. На a = SLON + KOT; тоже дает то же предупреждение, что и в первом примере. В общем, как хочет, так и предупреждает. Но ошибки тут нет. Хотя все эти операции приводят к семантическим ошибкам - объект перечислимого типа принимает значения, которых в типе нет. Именно поэтому в С enum и безполезен именно как перечислимый тип. А в С++ режиме ни один из этих номеров не проходит. О чем и шла речь. В С++ enum - это отдельный тип и неявных преобразований в него нет. А в С - это некий объект интегрального типа, а не именно тип с перечисленными значениями. настоятельно рекомендую пользоваться и не ограничиваться банальными #define. Мне, к счастью, С компиляторами пользоваться не приходится, у меня везде С++, поэтому перечислимыми типами пользуюсь с удовольствим и к месту - они свою роль (описанную в предыущем посте) успешно выполняют. Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
xvr 12 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба Хочу добавить свои 5 коп. к всему вышеизложенному: const и enum - типы C++ и они подчиняются правилам видимости (scope), т.е. если у вас есть const ABC на уровне файла, то вам никто не мешает завести int ABC где нибудь в классе, с define такой номер не пройдет. Здесь можно возразить, что нефиг заводить ABC в классе если уже есть ABC на глобальном уровне (и это отчасти правильно), однако не всегда можно вспомнить где и какие глобальные константы были заведены (особенно исли их количество перевалит за пару тысяч, а файлов с их описаниями - за пару сотен), а с другой стороны, если мы поместим константы и класс в разные пространства имен (namespace), то тут все будет идеологически правильно по всем канонам OO, что все равно не избавит нас от большого облома с define'ами. И еще один плюс к enum - их значения вместе с идентификаторами видят debugger'ы, в отличие от define'ов Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба компилятор С дает ошибку? Мой опыт говорит, что не дает, и по Стандарту не должен - это не запрещенное действие. С опциями типа 'warnings are errors' - да :) Мне достаточно warning, ибо warnig для меня лично потенциальная ошибка и следует эту проблему решать растолковывая компилятору, что от него требуетя. В крайнем случае индивидуальное подавление прагмой. Наличе warnings и подавление их оптом не приемлю категорически. А на а++; даже и предупреждения не дает (не обязан). Хотя тут арифметика - т.е. преобразование к целому, инкремент, присваивание. Не выдает не обязан и не может - формально отсутсвуюет поминание enum-ов :(. Практически во всех остальных случаях, коих много больше, предупреждения есть. Мне, к счастью, С компиляторами пользоваться не приходится, у меня везде С++, поэтому перечислимыми типами пользуюсь с удовольствим и к месту - они свою роль (описанную в предыущем посте) успешно выполняют. Я обычно даже 'С' (с легкой доработкой напильником) исходники плюсовым компилятором компилю из-за большей его привередливости и небольших приятных фич, но тем не менее настаиваю на полезности использования enum и в "C". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 34 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба С опциями типа 'warnings are errors' - да :) Мне достаточно warning, ибо warnig для меня лично потенциальная ошибка и следует эту проблему решать растолковывая компилятору, что от него требуетя. В крайнем случае индивидуальное подавление прагмой. Наличе warnings и подавление их оптом не приемлю категорически. Я тоже не сторонник подавлять предупреждения скопом и никогда так не делаю. Но тем не менее предупреждение - это совсем не ошибка. Это указание на место потенциальной ошибки, не более. Не выдает не обязан и не может - формально отсутсвуюет поминание enum-ов :(. Практически во всех остальных случаях, коих много больше, предупреждения есть. Как это отсутствует? 'a' - это объект перечислимого типа, вот и поминание. К нему применяется операция инкремента, которая приводит значение объекта к величине, отсутствующей в типе этого объекта. Это есть не что иное, как семантическая ошибка! Объект 'a' перечислимого типа на деле перестает таковым быть! И в чем тогда смысл использования перечисления? Вот в С++ правила строго защищают эту ситуацию - перечислимый тип является таковым, любая попытка нарушить целостность типа объекта пресекается ошибкой. Это и делает enum перечислимым типом. Это и обуславливает целесообразность использования. В С этого нет, поэтому enum в С штука почти бесполезная - годится только для описания именованных литералов, не более. но тем не менее настаиваю на полезности использования enum и в "C". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
zltigo 0 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба В С этого нет, поэтому enum в С штука почти бесполезная - годится только для описания именованных литералов, не более. :) Как известно, всегда существуют 100% эффективные средства для решения проблемы. Например, проблему перхоти хорошо решает гильотитна, но почему при наличии гильотины не пользоваться не столь эффективным шампунем против перхоти я не занаю :). Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться
dxp 34 22 июня, 2007 Опубликовано 22 июня, 2007 · Жалоба :) Как известно, всегда существуют 100% эффективные средства для решения проблемы. Например, проблему перхоти хорошо решает гильотитна, но почему при наличии гильотины не пользоваться не столь эффективным шампунем против перхоти я не занаю :). Пример забавный :), но не в кассу - у гильотины побочный эффект нехороший. В перечислимом типе С++ никаких таких побочных эффектов нет. А в С от перечислимого типа только название. Думается, что Вы не будете в восторге от использования для мытья волос воды, даже если на ней написно "Шампунь". Цитата Поделиться сообщением Ссылка на сообщение Поделиться на другие сайты Поделиться