“Const static” vs “#define” vs “enum”

¿Cuál es mejor usar entre los enunciados siguientes en C?

static const int var = 5; 

o

 #define var 5 

o

 enum { var = 5 }; 

Generalmente hablando:

 static const 

Porque respeta el scope y es seguro para el tipo.

La única advertencia que pude ver: si desea que la variable sea posiblemente definida en la línea de comando. Todavía hay una alternativa:

 #ifdef VAR // Very bad name, not long enough, too general, etc.. static int const var = VAR; #else static int const var = 5; // default value #endif 

Siempre que sea posible, en lugar de macros / elipsis, use una alternativa de tipo seguro.

Si realmente NECESITA ir con una macro (por ejemplo, quiere __FILE__ o __LINE__ ), entonces será mejor que le __FILE__ MUY cuidadoso a su macro: en su convención de nomenclatura Boost recomienda todas las mayúsculas, comenzando por el nombre del proyecto ( aquí BOOST_), mientras examina la biblioteca observará que esto es (generalmente) seguido del nombre del área particular (biblioteca) y luego con un nombre significativo.

Por lo general, hace nombres largos 🙂

Depende de para qué necesitas el valor. Usted (y todos los demás hasta el momento) omitieron la tercera alternativa:

  1. static const int var = 5;
  2. #define var 5
  3. enum { var = 5 };

Ignorando los problemas sobre la elección del nombre, entonces:

  • Si necesita pasar un puntero, debe usar (1).
  • Como aparentemente (2) es una opción, no necesita pasar punteros.
  • Ambos (1) y (3) tienen un símbolo en la tabla de símbolos del depurador, que facilita la depuración. Es más probable que (2) no tenga un símbolo, dejándote preguntándote qué es.
  • (1) no se puede usar como una dimensión para matrices de scope global; ambos (2) y (3) pueden.
  • (1) no se puede utilizar como una dimensión para matrices estáticas en el scope de la función; ambos (2) y (3) pueden.
  • En C99, todos estos se pueden usar para matrices locales. Técnicamente, usar (1) implicaría el uso de un VLA (matriz de longitud variable), aunque la dimensión referenciada por ‘var’ se fijaría por supuesto en el tamaño 5.
  • (1) no se puede usar en lugares como declaraciones de cambio; ambos (2) y (3) pueden.
  • (1) no se puede usar para inicializar variables estáticas; ambos (2) y (3) pueden.
  • (2) puede cambiar el código que no deseó cambiar porque lo usa el preprocesador; ambos (1) y (3) no tendrán efectos secundarios inesperados como ese.
  • Puede detectar si (2) se ha configurado en el preprocesador; ni (1) ni (3) lo permite.

Entonces, en la mayoría de los contextos, prefieren la ‘enumeración’ sobre las alternativas. De lo contrario, es probable que los primeros y los últimos puntos sean los factores de control, y debe pensar más si necesita satisfacer ambos a la vez.

Si estuviera preguntando sobre C ++, entonces usaría la opción (1) – la constante estática – cada vez.

En C, específicamente? En C, la respuesta correcta es: use #define (o, si corresponde, enum )

Si bien es beneficioso tener las propiedades de delimitación y tipado de un objeto const , en realidad los objetos const en C (a diferencia de C ++) no son constantes verdaderas y, por lo tanto, generalmente son inútiles en la mayoría de los casos prácticos.

Entonces, en C, la elección debe estar determinada por cómo planeas usar tu constante. Por ejemplo, no puede usar un objeto const int como una etiqueta de case (mientras que una macro funcionará). No puede usar un objeto const int como un ancho de campo de bits (mientras que una macro funcionará). En C89 / 90 no puede usar un objeto const para especificar un tamaño de matriz (mientras que una macro funcionará). Incluso en C99 no puede usar un objeto const para especificar un tamaño de matriz cuando necesita una matriz que no sea VLA .

Si esto es importante para usted, entonces determinará su elección. La mayoría de las veces, no tendrá más opción que usar #define en C. Y no se olvide de otra alternativa, que produce constantes verdaderas en C- enum .

En C ++ los objetos const son verdaderas constantes, por lo que en C ++ casi siempre es mejor preferir la variante const (sin necesidad de static explícito en C ++).

La diferencia entre static const y #define es que el primero usa la memoria y el último no usa la memoria para el almacenamiento. En segundo lugar, no puede pasar la dirección de un #define mientras que puede pasar la dirección de una static const . De hecho, depende de en qué circunstancias nos encontremos, necesitamos seleccionar uno de estos dos. Ambos son mejores en diferentes circunstancias. Por favor, no asums que uno es mejor que el otro … 🙂

Si ese hubiera sido el caso, Dennis Ritchie habría mantenido al mejor en paz … jajaja … 🙂

En C #define es mucho más popular. Puede usar esos valores para declarar tamaños de matriz, por ejemplo:

 #define MAXLEN 5 void foo(void) { int bar[MAXLEN]; } 

ANSI C no le permite usar static const en este contexto, hasta donde yo sé. En C ++, debe evitar las macros en estos casos. Puedes escribir

 const int maxlen = 5; void foo() { int bar[maxlen]; } 

e incluso dejar fuera static porque la vinculación interna está implícita en const ya [solo en C ++].

Otro inconveniente de const en C es que no puede usar el valor para inicializar otro const .

 static int const NUMBER_OF_FINGERS_PER_HAND = 5; static int const NUMBER_OF_HANDS = 2; // initializer element is not constant, this does not work. static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND * NUMBER_OF_HANDS; 

Incluso esto no funciona con un const ya que el comstackdor no lo ve como una constante:

 static uint8_t const ARRAY_SIZE = 16; static int8_t const lookup_table[ARRAY_SIZE] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant! 

Me encantaría usar const tipado en estos casos, de lo contrario …

Si puede salirse con la suya, static const tiene muchas ventajas. Obedece a los principios de scope normal, es visible en un depurador y generalmente obedece las reglas que las variables obedecen.

Sin embargo, al menos en el estándar C original, no es realmente una constante. Si usa #define var 5 , puede escribir int foo[var]; como una statement, pero no se puede hacer eso (excepto como una extensión de comstackción “con static const int var = 5; Este no es el caso en C ++, donde la versión static const se puede usar en cualquier lugar donde la #define versión puede, y creo que este también es el caso con C99.

Sin embargo, nunca nombre una constante #define con un nombre en minúscula. Anulará cualquier uso posible de ese nombre hasta el final de la unidad de traducción. Las constantes macro deben estar en lo que es efectivamente su propio espacio de nombres, que tradicionalmente son todas mayúsculas, quizás con un prefijo.

#define var 5 le causará problemas si tiene cosas como mystruct.var .

Por ejemplo,

 struct mystruct { int var; }; #define var 5 int main() { struct mystruct foo; foo.var = 1; return 0; } 

El preprocesador lo reemplazará y el código no se comstackrá. Por esta razón, el estilo de encoding tradicional sugiere que todos los #define constantes #define letras mayúsculas para evitar conflictos.

Escribí un progtwig de prueba rápido para demostrar una diferencia:

 #include  enum {ENUM_DEFINED=16}; enum {ENUM_DEFINED=32}; #define DEFINED_DEFINED 16 #define DEFINED_DEFINED 32 int main(int argc, char *argv[]) { printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED); return(0); } 

Esto comstack con estos errores y advertencias:

 main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED' enum {ENUM_DEFINED=32}; ^ main.c:5:7: note: previous definition is here enum {ENUM_DEFINED=16}; ^ main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined] #define DEFINED_DEFINED 32 ^ main.c:8:9: note: previous definition is here #define DEFINED_DEFINED 16 ^ 

Tenga en cuenta que enum da un error cuando define da una advertencia.

La definición

 const int const_value = 5; 

no siempre define un valor constante. Algunos comstackdores (por ejemplo, tcc 0.9.26 ) simplemente asignan la memoria identificada con el nombre “const_value”. Usando el identificador “const_value” no puede modificar esta memoria. Pero aún puedes modificar la memoria usando otro identificador:

 const int const_value = 5; int *mutable_value = (int*) &const_value; *mutable_value = 3; printf("%i", const_value); // The output may be 5 or 3, depending on the compiler. 

Esto significa que la definición

 #define CONST_VALUE 5 

es la única forma de definir un valor constante que no se puede modificar de ninguna manera.

SIEMPRE es preferible usar const, en lugar de #define. Esto se debe a que el comstackdor trata de const y #define el preprocesador. Es como #define en sí mismo no es parte del código (hablando en términos generales).

Ejemplo:

 #define PI 3.1416 

El nombre simbólico PI nunca puede ser visto por los comstackdores; puede ser eliminado por el preprocesador antes de que el código fuente llegue a un comstackdor. Como resultado, es posible que el nombre PI no se ingrese en la tabla de símbolos. Esto puede ser confuso si obtiene un error durante la comstackción que implica el uso de la constante, porque el mensaje de error puede referirse a 3.1416, no a PI. Si PI se definió en un archivo de encabezado que no escribió, no tendría idea de dónde salió ese 3.1416.

Este problema también puede surgir en un depurador simbólico, porque, de nuevo, el nombre con el que está progtwigndo puede no estar en la tabla de símbolos.

Solución:

 const double PI = 3.1416; //or static const... 

No creo que haya una respuesta para “lo que siempre es mejor”, pero, como dijo Matthieu

static const

es tipo seguro. Mi mayor preocupación con #define , sin embargo, es que al depurar en Visual Studio no se puede ver la variable. Da un error que el símbolo no se puede encontrar.

Por cierto, una alternativa a #define , que proporciona un scope adecuado pero se comporta como una constante “real”, es “enum”. Por ejemplo:

 enum {number_ten = 10;} 

En muchos casos, es útil definir tipos enumerados y crear variables de esos tipos; si eso se hace, los depuradores pueden mostrar variables de acuerdo con su nombre de enumeración.

Una advertencia importante al hacer eso, sin embargo: en C ++, los tipos enumerados tienen una compatibilidad limitada con los enteros. Por ejemplo, de forma predeterminada, no se puede realizar una aritmética sobre ellos. Encuentro que es un curioso comportamiento predeterminado para las enumeraciones; aunque hubiera sido agradable tener un tipo de “enum estricto”, dado el deseo de tener C ++ generalmente compatible con C, creo que el comportamiento predeterminado de un tipo “enum” debería ser intercambiable con enteros.

Aunque la pregunta era sobre enteros, vale la pena señalar que #define y enumeraciones son inútiles si necesita una estructura o cadena constante. Por lo general, ambos se pasan a funciones como punteros. (Con cuerdas es obligatorio, con estructuras es mucho más eficiente).

En cuanto a los enteros, si se encuentra en un entorno incrustado con memoria muy limitada, es posible que deba preocuparse por dónde se almacena la constante y cómo se comstackn los accesos a ella. El comstackdor puede agregar dos consts en tiempo de ejecución, pero agrega dos #defines en tiempo de comstackción. Una constante #define se puede convertir en una o más instrucciones MOV [inmediatas], lo que significa que la constante se almacena efectivamente en la memoria del progtwig. Una constante constante se almacenará en la sección .const en la memoria de datos. En sistemas con una architecture de Harvard, podría haber diferencias en el rendimiento y el uso de memoria, aunque probablemente serían pequeños. Podrían ser importantes para la optimización de núcleos internos.

Una simple diferencia:

En el tiempo de preprocesamiento, la constante se reemplaza por su valor. Por lo tanto, no podría aplicar el operador de desreferencia a una definición, pero puede aplicar el operador de desreferencia a una variable.

Como supondrás, definir es más rápido que const estático.

Por ejemplo, tener:

 #define mymax 100 

no puedes hacer printf("address of constant is %p",&mymax); .

Pero teniendo

 const int mymax_var=100 

puedes hacer printf("address of constant is %p",&mymax_var); .

Para ser más claros, la definición se reemplaza por su valor en la etapa de preprocesamiento, por lo que no tenemos ninguna variable almacenada en el progtwig. Solo tenemos el código del segmento de texto del progtwig donde se utilizó la definición.

Sin embargo, para const estático tenemos una variable que se asigna en alguna parte. Para gcc, las const estáticas se asignan en el segmento de texto del progtwig.

Más arriba, quería hablar sobre el operador de referencia, así que reemplace la desreferencia con referencia.

Observamos el código ensamblador producido en el MBF16X … Ambas variantes dan como resultado el mismo código para operaciones aritméticas (ADD Inmediato, por ejemplo).

Por lo tanto, const int es preferible para la comprobación de tipo, mientras que #define es de estilo antiguo. Tal vez es específico del comstackdor. Por lo tanto, verifique su código ensamblador producido.