Является ли #define запрещенным в отраслевых стандартах?
Я студент первого курса информатики и мой профессор сказал #define
запрещено в отраслевых стандартах наряду с #if
,#ifdef
,#else
, и несколько других директив препроцессора. Он использовал слово "запрещен" из-за неожиданного поведения.
это точно? Если да, то почему?
существуют ли на самом деле какие-либо стандарты, запрещающие использование этих директив?
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
vsenum
для обсуждения вопросов нет.это хорошая идея, чтобы свести к минимуму использование препроцессора - он часто злоупотребляют по крайней мере столько, сколько он используется (см. 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++)
, что плохая примета.некоторые стандарты могут препятствовать или запрещать использование
#ifdef
S для условной компиляции. Например, следующий код использует условную компиляцию для правильной печати из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
чтобы избежать глупых ошибок, таких как присвоение одного и того же числового значения дважды.обратите внимание, что даже в этой ситуации, иногда лучше использовать
#define
s чем перечисления, например, если вы полагаетесь на числовые значения, обмениваемые с другими системами, и фактические значения должны оставаться одинаковыми, даже если вы добавляете / удаляете константы (для совместимость.)