macro define c c-preprocessor preprocessor-directive

macro - #define c++



¿Está#define prohibido en los estándares de la industria? (13)

Soy un estudiante de primer año de ciencias de la computación y mi profesor dijo que #define está prohibido en los estándares de la industria junto con #if , #ifdef , #ifdef y algunas otras directivas de preprocesador. Usó la palabra "prohibido" debido a un comportamiento inesperado.

¿Es esto exacto? Si es así, ¿por qué?

¿Hay, de hecho, alguna norma que prohíba el uso de estas directivas?


Algunos estándares de codificación pueden desalentar o incluso prohibir el uso de #define para crear macros similares a funciones que toman argumentos, como

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

porque a) tales macros no son de tipo seguro, yb) alguien inevitablemente escribirá SQR(x++) , que es un mal juju.

Algunos estándares pueden desalentar o prohibir el uso de #ifdef s para la compilación condicional. Por ejemplo, el siguiente código usa compilación condicional para imprimir correctamente un valor size_t . Para C99 y C99 posteriores, utiliza el especificador de conversión %zu ; para C89 y C89 anteriores, utiliza %lu y C89 el valor a 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 );

Algunos estándares pueden exigir que, en lugar de hacerlo, implemente el módulo dos veces, una vez para C89 y versiones anteriores, una vez para C99 y versiones posteriores:

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

y luego deje que Make (o Ant , o cualquier herramienta de compilación que esté usando) se ocupe de compilar y vincular la versión correcta. Para este ejemplo, sería una exageración ridícula, pero he visto un código que era un nido de #ifdef de #ifdef s que no se #ifdef y que debería haber tenido ese código condicional factorizado en archivos separados.

Sin embargo, no tengo conocimiento de ninguna compañía o grupo industrial que haya prohibido el uso de declaraciones de preprocesadores directamente.


Contrariamente a todas las respuestas hasta la fecha, el uso de directivas de preprocesador a menudo está prohibido en la informática de alta confiabilidad. Hay dos excepciones a esto, cuyo uso es obligatorio en tales organizaciones. Estas son la directiva #include , y el uso de un protector de inclusión en un archivo de encabezado. Este tipo de prohibiciones es más probable en C ++ que en C.

Aquí hay solo un ejemplo: 16.1.1 Use el preprocesador solo para implementar incluir guardias e incluir archivos de encabezado con incluir guardias .

Otro ejemplo, esta vez para C en lugar de C ++: Estándar de codificación institucional JPL para el lenguaje de programación C. Este estándar de codificación C no llega tan lejos como para prohibir completamente el uso del preprocesador, pero se acerca. Específicamente, dice

Regla 20 (uso del preprocesador) El uso del preprocesador C se limitará a la inclusión de archivos y macros simples. [Poder de los Diez Regla 8].

No estoy tolerando ni denunciando esos estándares. Pero decir que no existen es ridículo.


Eso es completamente falso, las macros se usan mucho en C. Los principiantes a menudo las usan mal, pero esa no es una razón para prohibirlas en la industria. Un mal uso clásico es #define succesor(n) n + 1 . Si espera que 2 * successor(9) dé 20, entonces está equivocado porque esa expresión se traducirá como 2 * 9 + 1 es decir, 19 no 20. Use paréntesis para obtener el resultado esperado.


Las macros no pueden ser "prohibidas". La afirmación no tiene sentido. Literalmente.

Por ejemplo, la sección 7.5 Errores <errno.h> del Estándar C requiere el uso de macros:

1 El encabezado <errno.h> define varias macros, todas relacionadas con el informe de condiciones de error.

2 Las macros son

EDOM EILSEQ ERANGE

que se expanden a expresiones constantes enteras con tipo int , valores positivos distintos, y que son adecuados para su uso en #if directivas de preprocesamiento; y

errno

que se expande a un valor l modificable que tiene una duración de almacenamiento local tipo int e hilo, cuyo valor se establece en un número de error positivo por varias funciones de la biblioteca. Si se suprime una definición de macro para acceder a un objeto real, o un programa define un identificador con el nombre errno , el comportamiento es indefinido.

Por lo tanto, no solo las macros son una parte necesaria de C, en algunos casos no usarlas da como resultado un comportamiento indefinido.


Las macros se usan bastante en GNU land C, y sin los comandos de preprocesador condicional no hay forma de manejar adecuadamente las inclusiones múltiples de los mismos archivos fuente, por lo que me parecen funciones de lenguaje esenciales.

Tal vez su clase esté realmente en C ++, que a pesar de que muchas personas no lo hayan hecho, debería distinguirse de C, ya que es un lenguaje diferente y no puedo hablar por macros allí. O tal vez el profesor quiso decir que los está prohibiendo en su clase. De todos modos, estoy seguro de que la comunidad SO estaría interesada en escuchar de qué estándar está hablando, ya que estoy bastante seguro de que todos los estándares C admiten el uso de macros.


Mire cualquier archivo de encabezado y verá algo como esto:

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

Estas definiciones no solo están permitidas, sino que son de naturaleza crítica ya que cada vez que se hace referencia al archivo de encabezado en los archivos, se incluirá por separado. Esto significa que sin la definición, está redefiniendo todo lo que está entre los guardias varias veces, el mejor de los casos no se compila y el peor de los casos le deja rascándose la cabeza más tarde por qué su código no funciona de la manera deseada.

El compilador también usará definir como se ve aquí con gcc que le permite probar cosas como la versión del compilador que es muy útil. Actualmente estoy trabajando en un proyecto que necesita compilarse con avr-gcc, pero tenemos un entorno de prueba en el que también ejecutamos nuestro código. Para evitar que los archivos y registros específicos de avr impidan que se ejecute nuestro código de prueba, hacemos algo como esto:

#ifdef __AVR__ //avr specific code here #endif

Al usar esto en el código de producción, el código de prueba complementario puede compilarse sin usar avr-gcc y el código anterior solo se compila usando avr-gcc.


No, #define no está prohibido. El mal uso de #define , sin embargo, puede estar mal visto.

Por ejemplo, puedes usar

#define DEBUG

en su código para que luego pueda designar partes de su código para compilación condicional usando #ifdef DEBUG , solo para propósitos de depuración. No creo que nadie en su sano juicio quiera prohibir algo como esto. Las macros definidas usando #define también se usan ampliamente en programas portátiles, para habilitar / deshabilitar la compilación de código específico de la plataforma.

Sin embargo, si está usando algo como

#define PI 3.141592653589793

su maestro puede señalar con razón que es mucho mejor declarar PI como una constante con el tipo apropiado, por ejemplo,

const double PI = 3.141592653589793;

ya que permite que el compilador realice verificaciones de tipo cuando se usa PI .

De manera similar (como mencionó anteriormente John Bode), el uso de macros de función puede ser desaprobado, especialmente en C ++ donde se pueden usar plantillas. Entonces en lugar de

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

Considere usar

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

o, en C ++, mejor aún,

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

Una vez más, la idea es que al usar las facilidades del lenguaje en lugar del preprocesador, permita que el compilador escriba check y también (posiblemente) genere un mejor código.

Una vez que tenga suficiente experiencia en codificación, sabrá exactamente cuándo es apropiado usar #define . Hasta entonces, creo que es una buena idea para su maestro imponer ciertas reglas y estándares de codificación, pero preferiblemente ellos mismos deberían saber y poder explicar las razones. Una prohibición general de #define tiene sentido.


No, el uso de macros no está prohibido.

De hecho, el uso de #include guardias en los archivos de encabezado es una técnica común que a menudo es obligatoria y alentada por las pautas de codificación aceptadas. Algunas personas afirman que #pragma once es una alternativa a eso, pero el problema es que #pragma once , por definición, ya que los pragmas son un gancho proporcionado por el estándar para extensiones específicas del compilador, no es estándar, incluso si es compatible por varios compiladores.

Dicho esto, hay una serie de pautas de la industria y prácticas alentadas que desalientan activamente todo uso de macros que no sean #include guardias debido a los problemas que presentan las macros (sin respetar el alcance, etc.). En el desarrollo de C ++, el uso de macros está mal visto aún más que en el desarrollo de C.

Desalentar el uso de algo no es lo mismo que prohibirlo, ya que todavía es posible usarlo legítimamente, por ejemplo, documentando una justificación.


No, tu profesor está equivocado o escuchaste algo mal.

#define es una macro de preprocesador, y se necesitan macros de preprocesador para la compilación condicional y algunas convenciones, que no se crean simplemente en el lenguaje C. Por ejemplo, en un estándar C reciente, a saber, C99, se había agregado soporte para booleanos. Pero el lenguaje no lo admite "nativo", sino el preprocesador #define s. Ver esta referencia a stdbool.h


No. No está prohibido. Y a decir verdad, es imposible hacer un código multiplataforma no trivial sin él.


Primero he oído hablar de eso.

No; #define y demás son ampliamente utilizados. A veces se usa demasiado, pero definitivamente se usa. Hay lugares donde el estándar C exige el uso de macros; no puede evitarlos fácilmente. Por ejemplo, §7.5 Errores <errno.h> dice:

Las macros son

EDOM EILSEQ ERANGE

que se expanden a expresiones constantes enteras con tipo int , valores positivos distintos, y que son adecuados para su uso en #if directivas de preprocesamiento; ...

Dado esto, está claro que no todos los estándares de la industria prohíben el uso de las macro directivas del preprocesador C. Sin embargo, existen estándares de ''mejores prácticas'' o ''pautas de codificación'' de varias organizaciones que prescriben límites en el uso del preprocesador C, aunque ninguno prohíbe su uso por completo; es una parte innata de C y no se puede evitar por completo. A menudo, estos estándares son para personas que trabajan en áreas críticas para la seguridad.

Un estándar que puede consultar el estándar MISRA C (2012); eso tiende a proscribir cosas, pero incluso eso reconoce que a veces se necesitan #define et al (sección 8.20, las reglas 20.1 a 20.14 cubren el preprocesador C).

Los estándares de codificación C GSFC (Centro de vuelo espacial Goddard) de la NASA simplemente dicen:

Las macros deben usarse solo cuando sea necesario. El uso excesivo de macros puede hacer que el código sea más difícil de leer y mantener porque el código ya no lee o se comporta como el estándar C.

La discusión después de esa declaración introductoria ilustra el uso aceptable de las macros de funciones.

El estándar de codificación CERT C tiene una serie de pautas sobre el uso del preprocesador, e implica que debe minimizar el uso del preprocesador, pero no prohíbe su uso.

A Stroustrup le gustaría hacer que el preprocesador sea irrelevante en C ++, pero eso aún no ha sucedido. Como notes Peter , algunos estándares C ++, como los estándares de codificación JSF AV C ++ ( Joint Strike Fighter, Air Vehicle ) de alrededor de 2005, dictan un uso mínimo del preprocesador C. Esencialmente, las reglas JSF AV C ++ lo restringen a #include y el #ifndef XYZ_H / #define XYZ_H / ... / #endif dance que evita múltiples inclusiones de un solo encabezado. C ++ tiene algunas opciones que no están disponibles en C, en particular, un mejor soporte para constantes escritas que luego se pueden usar en lugares donde C no permite que se usen. Vea también static const vs #define vs enum para una discusión de los problemas allí.

Es una buena idea minimizar el uso del preprocesador: a menudo se abusa de él al menos tanto como se usa (consulte la ''biblioteca'' del preprocessor Boost para obtener ilustraciones de hasta dónde puede llegar con el preprocesador C).

Resumen

El preprocesador es una parte integral de C y #define y #if etc. no se pueden evitar por completo. La declaración del profesor en la pregunta generalmente no es válida: #define está prohibido en los estándares de la industria junto con #if , #ifdef , #ifdef , y algunas otras macros es una declaración excesiva en el mejor de los casos, pero podría ser compatible con referencia explícita a estándares específicos de la industria (pero los estándares en cuestión no incluyen ISO / IEC 9899: 2011 - el estándar C).

Tenga en cuenta que David Hammen ha proporcionado información sobre un estándar de codificación C específico, el Estándar de codificación JPL C , que prohíbe muchas cosas que muchas personas usan en C, incluida la limitación del uso del preprocesador C (y la limitación del uso de memoria dinámica asignación y prohibición de recurrencia: léalo para ver por qué y decida si esos motivos son relevantes para usted).


Si desea que su código C interopere con el código C ++, deberá declarar sus símbolos visibles externamente, como las declaraciones de funciones, en el espacio de nombres extern "C" . Esto a menudo se realiza mediante compilación condicional:

#ifdef __cplusplus extern "C" { #endif /* C header file body */ #ifdef __cplusplus } #endif


Si hubiera mencionado #define , habría pensado que tal vez aludía a su uso para enumeraciones, que es mejor usar enum para evitar errores estúpidos, como asignar el mismo valor numérico dos veces.

Tenga en cuenta que incluso para esta situación, a veces es mejor usar #define s que enumeraciones, por ejemplo, si confía en los valores numéricos intercambiados con otros sistemas y los valores reales deben permanecer iguales incluso si agrega / elimina constantes (por compatibilidad) .

Sin embargo, agregar que #if , #ifdef , etc. tampoco deberían usarse es simplemente extraño. Por supuesto, probablemente no deberían ser abusados, pero en la vida real hay docenas de razones para usarlos.

Lo que pudo haber querido decir es que (cuando corresponda), no debe codificar el comportamiento en la fuente (lo que requeriría una nueva compilación para obtener un comportamiento diferente), sino más bien utilizar alguna forma de configuración de tiempo de ejecución.

Esa es la única interpretación que podría pensar que tendría sentido.