Является ли #define запрещенным в отраслевых стандартах?


Я студент первого курса информатики и мой профессор сказал #define запрещено в отраслевых стандартах наряду с #if,#ifdef,#else, и несколько других директив препроцессора. Он использовал слово "запрещен" из-за неожиданного поведения.

это точно? Если да, то почему?

существуют ли на самом деле какие-либо стандарты, запрещающие использование этих директив?

13 68

13 ответов:

впервые слышу об этом.

нет; #define и так далее широко используются. Иногда слишком широко используется, но используется. Есть места, где стандарт C требует использования макросов - вы не можете легко избежать их. Например, §7.5 ошибки <errno.h> говорит:

макросы

     EDOM
     EILSEQ
     ERANGE

которые расширяются до целочисленных константных выражений с типом int, определенные положительные значения, и которые соответствующие для используйте в #if директивы препроцессора; ...

один стандарт, вы можете проверить МИСРА C (2012) стандарт; это имеет тенденцию запрещать вещи, но даже это признает, что #define иногда требуется et al (раздел 8.20, правила 20.1-20.14 охватывают препроцессор C).

NASA GSFC (Goddard Space Flight Center)C Стандарты Кодирования просто сказать:

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

обсуждение после этого вступительного заявления иллюстрирует приемлемое использование функциональных макросов.

The CERT C стандарт кодирования имеет ряд рекомендаций по использованию препроцессора и подразумевает, что вы должны минимизировать использование препроцессора, но не запрещать его использование.

Stroustrup хотел бы сделать препроцессор не имеет значения в C++, но это еще не произошло. Как ПетрПримечания, некоторые стандарты C++, такие как стандарты кодирования JSF AV C++ (Совместный Ударный Истребитель, Воздушная Машина) примерно с 2005 года диктуют минимальное использование препроцессора C. По сути, правила JSF AV C++ ограничивают его #include и #ifndef XYZ_H/#define XYZ_H / .../#endif танец, который предотвращает множественные включения одного заголовка. C++ имеет некоторые параметры, которые не являются доступно в C-в частности, улучшена поддержка типизированных констант, которые затем могут использоваться в местах, где C не позволяет их использовать. Смотрите также static const vs #define vs enum для обсуждения вопросов нет.

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

резюме

препроцессор является неотъемлемой частью C и #define и #if и т. д. не может быть полностью избежать. Утверждение профессора в вопросе не является общепринятым: #define запрещено в отраслевых стандартах наряду с #if,#ifdef,#else, и несколько других макросов в лучшем случае является чрезмерным заявлением, но может быть поддержано с явной ссылкой на конкретные отраслевые стандарты (но стандарты в вопрос не включает ISO / IEC 9899:2011 - стандарт C).


отметим, что Дэвид Hammen и предоставлена информация об одном конкретном стандарте кодирования C-the стандарт кодирования JPL C - это запрещает много вещей, которые многие люди используют в C, включая ограничение использования препроцессора C (и ограничение использования динамического выделения памяти, и запрещение рекурсии-прочитайте его, чтобы понять, почему, и решить, являются ли те причины имеют отношение к вам).

нет, использование макросов не запрещено.

на самом деле, использование #include guards в заголовочных файлах является одним из распространенных методов, который часто является обязательным и поощряется принятыми руководящими принципами кодирования. Некоторые люди утверждают, что #pragma once - это альтернатива, но проблема в том, что #pragma once - по определению, поскольку прагмы являются крючком, предоставляемым стандартом для конкретных расширений компилятора,-это нестандартно, даже если он поддерживается рядом компиляторов.

что там сказал есть ряд отраслевых рекомендаций и рекомендуемых практик, которые активно препятствуют использованию всех макросов, кроме #include охранники из-за проблем, которые вводят макросы (не соблюдая область действия и т. д.). В разработке на языке C++ использование макросов осуждается еще сильнее, чем в разработке на языке C.

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

некоторые стандарты кодирования могут препятствовать или даже запрещают использование #define создать функциональные макросы которые принимают аргументы, как

#define SQR(x) ((x)*(x))

потому что а) такие макросы не являются типобезопасными, и б) кто-то неизбежно написать SQR(x++), что плохая примета.

некоторые стандарты могут препятствовать или запрещать использование #ifdefS для условной компиляции. Например, следующий код использует условную компиляцию для правильной печати из size_t значение. Ибо C99 и потом, вы используете %zu спецификатор преобразования; для C89 и ранее, вы используете %lu и приведите значение к unsigned long:

#if __STDC_VERSION__ >= 199901L
#  define SIZE_T_CAST
#  define SIZE_T_FMT "%zu"
#else
#  define SIZE_T_CAST (unsigned long)
#  define SIZE_T_FMT "%lu"
#endif
...
printf( "sizeof foo = " SIZE_T_FMT "\n", SIZE_T_CAST sizeof foo );

стандарты мая поручите, чтобы вместо этого вы реализовали модуль дважды, один раз для C89 и ранее, один раз для C99 и позже:

/* C89 version */
printf( "sizeof foo = %lu\n", (unsigned long) sizeof foo );

/* C99 version */
printf( "sizeof foo = %zu\n", sizeof foo );

а потом пусть сделать (или АНТ, или любой другой инструмент сборки вы использование) занимается компиляцией и связыванием правильной версии. Для этого примера это было бы смешно перебор, но я видел код, который был неотслеживаемым крысиным гнездом #ifdefчто должны этот условный код был разложен на отдельные файлы.

однако я не знаю ни одной компании или отраслевой группы, которая имеет запрещен использование операторов препроцессора напрямую.

макросы не могут "забанить". Это утверждение-полная чушь. Буквально.

например, раздел 7.5 ошибки <errno.h> на Стандарт Cтребует использование макросов:

1 заголовок <errno.h> определяет несколько макросов, все из которых относятся к сообщению об ошибках.

2 макросы

EDOM
EILSEQ
ERANGE

которые расширяются до целочисленных константных выражений с типом int, отчетливый положительные значения, и которые подходят для использования в #if предобработки директивы; и

errno

который расширяется до изменяемого значения lvalue, которое имеет тип int и нить длительность локального хранения, значение которой установлено в положительную ошибку число по нескольким библиотечным функциям. Если определение макроса подавляется для доступа к фактическому объекту, или программа определяет идентификатор с именем errno поведение не определено.

итак, не только макросы a требуются часть C, в некоторых случаях не используя их приводит к неопределенному поведению.

нет, #define не запретили. Злоупотребление #define, однако, можно нахмуриться.

например, вы можете использовать

#define DEBUG

в вашем коде, так что позже, вы можете назначить части вашего кода для условной компиляции с помощью #ifdef DEBUG, только для отладки. Я не думаю, что кто-то в здравом уме захочет запретить что-то подобное. Макросы, определенные с помощью #define также широко используются в портативных программах, чтобы включить / отключить компиляция специфичного для платформы кода.

, если вы используете что-то вроде

#define PI 3.141592653589793

ваш учитель может справедливо указать, что гораздо лучше объявить PI как константа с соответствующим типом, например,

const double PI = 3.141592653589793;

как это позволяет компилятору делать проверку типа, когда это.

аналогично (как упоминалось выше Джоном Боде), использование функциональных макросов может быть не одобряется, особенно в C++ , где можно использовать шаблоны. Так что вместо

#define SQ(X) ((X)*(X))

рассмотрите возможность использования

double SQ(double X) { return X * X; }

или, в C++, еще лучше,

template <typename T>T SQ(T X) { return X * X; }

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

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

это совершенно неверно, макросы широко используются в C. Новички часто используют их плохо, но это не причина, чтобы запретить их в промышленности. Классическое плохое использование -#define succesor(n) n + 1. Если вы ожидаете 2 * successor(9) чтобы дать 20, то вы ошибаетесь, потому что это выражение будет переведено как 2 * 9 + 1 т. е. 19 не 20. Используйте скобки, чтобы получить ожидаемый результат.

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

нет ваш профессор не прав или вы что-то ослышались.

#define является макросом препроцессора, а макросы препроцессора необходимы для условной компиляции и некоторых соглашений, которые не просто построены на языке C. Например, в недавнем стандарте C, а именно C99, была добавлена поддержка булевых значений. Но он поддерживается не" родным " языком, а препроцессором #defineы. Вижу это ссылка на stdbool.h

макросы довольно сильно используются в GNU land C, и без условных команд препроцессора нет способа правильно обрабатывать несколько включений одних и тех же исходных файлов, так что они кажутся мне существенными языковыми функциями.

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

вопреки всем ответам на сегодняшний день, использование директив препроцессора часто запрещено в вычислениях с высокой надежностью. Есть два исключения из этого правила, использование которых разрешено в таких организациях. Это #include директива и использование Include guard в заголовочном файле. Эти виды запретов более вероятны в C++, а не в C.

вот только один пример: 16.1.1 используйте препроцессор только для реализации include guards, и в том числе заголовочные файлы с include guards.

другой пример, на этот раз для C, а не c++: стандарт институционального кодирования JPL для языка программирования C . Этот стандарт кодирования C не заходит так далеко, чтобы полностью запретить использование препроцессора, но он приближается. В частности, он говорит

правило 20 (использование препроцессора) Использование препроцессора C должны ограничиться файлом включение и простые макросы. [Сила десяти Правило 8].


я не оправдываю и не осуждаю эти стандарты. Но говорить, что они не существуют, смешно.

Если вы хотите, чтобы ваш код C взаимодействовал с кодом C++, вы захотите объявить свои внешне видимые символы, такие как объявления функций, в extern "C" пространство имен. Это часто делается с помощью условной компиляции:

#ifdef __cplusplus
extern "C" {
#endif

/* C header file body */

#ifdef __cplusplus
}
#endif

посмотрите на любой файл заголовка, и вы увидите что-то вроде этого:

#ifndef _FILE_NAME_H
#define _FILE_NAME_H
//Exported functions, strucs, define, ect. go here
#endif /*_FILE_NAME_H */

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

в компилятор также будет использовать define as seen здесь с gcc это позволит вам проверить такие вещи, как версия компилятора, которая очень полезна. В настоящее время я работаю над проектом, который должен компилироваться с avr-gcc, но у нас есть среда тестирования, в которой мы также запускаем наш код. Чтобы предотвратить запуск определенных файлов и регистров avr, мы делаем что-то вроде этого:

#ifdef __AVR__
//avr specific code here
#endif

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

если бы вы только что упомянули #define, Я бы подумал, может быть, он намекал на его использование для перечислений, которые лучше использовать enum чтобы избежать глупых ошибок, таких как присвоение одного и того же числового значения дважды.

обратите внимание, что даже в этой ситуации, иногда лучше использовать #defines чем перечисления, например, если вы полагаетесь на числовые значения, обмениваемые с другими системами, и фактические значения должны оставаться одинаковыми, даже если вы добавляете / удаляете константы (для совместимость.)