¿Cuál es mejor usar entre las siguientes declaraciones en C?
static const int var = 5;
o
#define var 5
o
enum { var = 5 };
¿Cuál es mejor usar entre las siguientes declaraciones en C?
static const int var = 5;
o
#define var 5
o
enum { var = 5 };
Respuestas:
Depende de para qué necesita el valor. Usted (y todos los demás hasta ahora) omitieron la tercera alternativa:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignorando problemas sobre la elección del nombre, entonces:
Entonces, en la mayoría de los contextos, prefiera la 'enumeración' sobre las alternativas. De lo contrario, es probable que el primer y el último punto sean los factores de control, y debe pensar más si necesita satisfacer ambos a la vez.
Si estuviera preguntando sobre C ++, usaría la opción (1), la constante estática, cada vez.
enum
es que se implementan como int
([C99] 6.7.2.2/3). A le #define
permite especificar sin signo y largo con U
y L
sufijos, y le const
permite dar un tipo. enum
puede causar problemas con las conversiones de tipo habituales.
enum
ni #define
utiliza ni espacio extra, per se. El valor aparecerá en el código del objeto como parte de las instrucciones en lugar de que se le asigne almacenamiento en el segmento de datos o en el montón o en la pila. Tendrá un espacio asignado para el static const int
, pero el compilador podría optimizarlo si no toma una dirección.
enum
s (y static const
): no se pueden cambiar. a define
puede ser #undefine
'd donde an enum
y static const
se fijan al valor dado.
Generalmente hablando:
static const
Porque respeta el alcance y es de tipo seguro.
La única advertencia que pude ver: si desea que la variable se defina posiblemente 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 / puntos suspensivos, use una alternativa de tipo seguro.
Si realmente NECESITA ir con una macro (por ejemplo, quiere __FILE__
o __LINE__
), será mejor que nombre su macro MUY cuidadosamente: en su convención de nomenclatura Boost recomienda todo en mayúsculas, comenzando por el nombre del proyecto (aquí BOOST_ ), al examinar la biblioteca, notará que esto es (generalmente) seguido del nombre del área particular (biblioteca) y luego con un nombre significativo.
Generalmente hace nombres largos :)
static
deben permanecer aquellos cuya dirección esté tomada; y si se toma la dirección, uno no podría haber usado un #define
o enum
(sin dirección) ... así que realmente no veo qué alternativa podría haberse usado. Si puede eliminar la "evaluación del tiempo de compilación", podría estar buscando en su extern const
lugar.
#if
podría ser preferible a #ifdef
las banderas booleanas, pero en este caso se haría imposible definir var
como 0
desde la línea de comandos. Entonces, en este caso, #ifdef
tiene más sentido, siempre que 0
sea un valor legal para var
.
En C, específicamente? En C la respuesta correcta es: uso #define
(o, si corresponde,enum
)
Si bien es beneficioso tener las propiedades de alcance y escritura de un const
objeto, en realidadconst
objetos 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 planea usar su constante. Por ejemplo, no puede usar un const int
objeto como case
etiqueta (mientras que una macro funcionará). No puede usar un const int
objeto como un ancho de campo de bits (mientras que una macro funcionará). En C89 / 90 no puede usar un const
objeto para especificar un tamaño de matriz (mientras que una macro funcionará). Incluso en C99 no puede usar un const
objeto para especificar un tamaño de matriz cuando necesita una matriz que no sea VLA .
Si esto es importante para usted, determinará su elección. La mayoría de las veces, no tendrá más remedio que usarlo #define
en C. Y no olvide otra alternativa, que produce constantes verdaderas en C - enum
.
En C ++, los const
objetos son constantes verdaderas, por lo que en C ++ casi siempre es mejor preferir la const
variante (sin static
embargo, no es necesario explícito en C ++).
const int
objetos en etiquetas de caso es ilegal en todas las versiones del lenguaje C. (Por supuesto, su compilador es gratuito para
const
significa solo lectura. const int r = rand();
Es perfectamente legal.
constexpr
en comparación con const
especialmente con los stl
contenedores como array
o bitset
.
switch()
declaración, no en case
una. También me atraparon en este ☺
La diferencia entre static const
y #define
es que el primero usa la memoria y el segundo 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 unstatic const
. En realidad, dependiendo de las circunstancias en que nos encontremos, debemos seleccionar uno de estos dos. Ambos están en su mejor momento bajo diferentes circunstancias. Por favor, no asuma que uno es mejor que el otro ... :-)
Si ese hubiera sido el caso, Dennis Ritchie habría mantenido el mejor solo ... jajaja ... :-)
const
usa memoria. GCC (probado con 4.5.3 y algunas versiones más nuevas) optimiza fácilmente const int
en un literal directo en su código cuando se utiliza -O3. Entonces, si realiza un desarrollo embebido con poca RAM (por ejemplo, AVR), puede usar con seguridad C consts si usa GCC u otro compilador compatible. No lo he probado, pero espero que Clang haga lo mismo por cierto.
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
s 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 de lado static
porque el enlace interno ya está implícito const
[solo en C ++].
const int MY_CONSTANT = 5;
en un archivo y acceder a él extern const int MY_CONSTANT;
en otro. No pude encontrar ninguna información en el estándar (al menos C99) sobre cómo const
cambiar el comportamiento predeterminado "6.2.2: 5 Si la declaración de un identificador para un objeto tiene un alcance de archivo y ningún especificador de clase de almacenamiento, su enlace es externo".
bar
es un VLA (matriz de longitud variable); Es probable que el compilador genere código como si su longitud fuera constante.
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 una constante, ya que el compilador 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 mecanografiado const
en estos casos, de lo contrario ...
static uint8_t const ARRAY_SIZE = 16;
de repente ya no compila puede ser un poco desafiante, especialmente cuando #define ARRAY_SIZE 256
está enterrado a diez capas de profundidad en una red enmarañada de encabezados. Ese nombre en mayúsculas ARRAY_SIZE
está pidiendo problemas. Reserve ALL_CAPS para macros y nunca defina una macro que no esté en forma ALL_CAPS.
const
. ¡Esto podría ser votado más!
Si puede salirse con la suya, static const
tiene muchas ventajas. Obedece a los principios del alcance normal, es visible en un depurador y generalmente obedece las reglas que obedecen las variables.
Sin embargo, al menos en el estándar C original, en realidad no es una constante. Si lo usa #define var 5
, puede escribir int foo[var];
como una declaración, pero no puede hacerlo (excepto como una extensión del compilador "con static const int var = 5;
. Este no es el caso en C ++, donde la static const
versión se puede usar en cualquier lugar que la #define
versión pueda, y creo que esto Es también el caso de C99.
Sin embargo, nunca nombre una #define
constante con un nombre en minúscula. Anulará cualquier posible uso de ese nombre hasta el final de la unidad de traducción. Las constantes macro deberían estar en lo que efectivamente es su propio espacio de nombres, que tradicionalmente son todas letras mayúsculas, quizás con un prefijo.
const
en C99 todavía no es una constante real. Puede declarar el tamaño de la matriz con a const
en C99, pero solo porque C99 admite matrices de longitud variable. Por esta razón, solo funcionará donde se permitan los VLA. Por ejemplo, incluso en C99, aún no puede usar a const
para declarar el tamaño de una matriz de miembros en a struct
.
const int
tamaño como si fuera una constante C ++ o una macro. Si desea depender de esta desviación de GCC del estándar, por supuesto, es su elección, yo personalmente iría con él a menos que realmente pueda evitar usar otro compilador que GCC o Clang, este último tiene la misma característica aquí (probado con Clang 3.7)
SIEMPRE es preferible usar const, en lugar de #define. Eso es porque const es tratado por el compilador y #define por el preprocesador. Es como si #definir en sí mismo no formara parte del código (más o menos).
Ejemplo:
#define PI 3.1416
El nombre simbólico PI nunca puede ser visto por los compiladores; puede ser eliminado por el preprocesador incluso antes de que el código fuente llegue a un compilador. 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 compilación que implica el uso de la constante, porque el mensaje de error puede referirse a 3.1416, no a PI. Si PI se definiera en un archivo de encabezado que no escribió, no tendría idea de dónde vino ese 3.1416.
Este problema también puede surgir en un depurador simbólico, porque, nuevamente, el nombre con el que está programando puede no estar en la tabla de símbolos.
Solución:
const double PI = 3.1416; //or static const...
#define var 5
te causará problemas si tienes 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 compilará. Por esta razón, el estilo de codificación tradicional sugiere que todas las constantes #define
usan letras mayúsculas para evitar conflictos.
Escribí un programa de prueba rápida para demostrar una diferencia:
#include <stdio.h>
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 compila 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 compiladores (por ejemplo, tcc 0.9.26 ) simplemente asignan memoria identificada con el nombre "const_value". Usando el identificador "const_value" no puede modificar esta memoria. Pero aún podría modificar la memoria utilizando 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 la definición
#define CONST_VALUE 5
es la única forma de definir un valor constante que no puede modificarse de ninguna manera.
#define
también puedes modificarlo editando el código de la máquina.
5
. Pero no se puede modificar #define
porque es una macro de preprocesador. No existe en el programa binario. Si uno quería modificar todos los lugares donde CONST_VALUE
se usaba, tenía que hacerlo uno por uno.
#define CONST 5
, entonces if (CONST == 5) { do_this(); } else { do_that(); }
, y el compilador elimina la else
rama. ¿Cómo propone editar el código de la máquina para cambiar CONST
a 6?
#define
no es a prueba de balas.
#define
. La única forma real de hacerlo es editar el código fuente y volver a compilar.
Aunque la pregunta era sobre enteros, vale la pena señalar que #define y las enumeraciones son inútiles si necesita una estructura o cadena constante. Ambos se pasan generalmente a funciones como punteros. (Con cadenas es necesario; con estructuras es mucho más eficiente).
En cuanto a los enteros, si está en un entorno incrustado con memoria muy limitada, es posible que deba preocuparse sobre dónde se almacena la constante y cómo se compilan los accesos a ella. El compilador puede agregar dos consts en tiempo de ejecución, pero agrega dos #defines en tiempo de compilación. Una constante #define puede convertirse en una o más instrucciones MOV [inmediatas], lo que significa que la constante se almacena efectivamente en la memoria del programa. Una constante constante se almacenará en la sección .const en la memoria de datos. En sistemas con una arquitectura de Harvard, podría haber diferencias en el rendimiento y el uso de la memoria, aunque probablemente serían pequeños. Pueden ser importantes para la optimización de núcleo duro de los bucles internos.
No creo que haya una respuesta para "lo que siempre es mejor" pero, como dijo Matthieu
static const
Es de tipo seguro. Sin #define
embargo, mi mayor motivo de molestia es cuando 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 alcance 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.
Sin embargo, una advertencia importante para hacerlo: en C ++, los tipos enumerados tienen compatibilidad limitada con los enteros. Por ejemplo, por defecto, uno no puede realizar operaciones aritméticas sobre ellos. Me parece un comportamiento curioso por defecto para las enumeraciones; si bien hubiera sido bueno tener un tipo "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.
int
, por lo que el "enum hack" no se puede usar con otros tipos enteros. (El tipo de enumeración es compatible con algún tipo de entero definido por la implementación, no necesariamente int
, pero en este caso el tipo es anónimo, por lo que no importa)
int
a una variable de enumeración (que los compiladores pueden hacer) y uno intenta asignar a dicha variable un miembro de su propia enumeración. Deseo que los comités de normas agreguen formas portátiles de declarar tipos enteros con semántica especificada. CUALQUIER plataforma, independientemente de su char
tamaño, debería poder, por ejemplo, declarar un tipo que envolverá el mod 65536, incluso si el compilador tiene que agregar muchas AND R0,#0xFFFF
o instrucciones equivalentes.
uint16_t
, aunque, por supuesto, ese no es un tipo de enumeración. Sería bueno permitir que el usuario especifique el tipo de entero usado para representar un tipo de enumeración dado, pero puede lograr el mismo efecto con un typedef
for uint16_t
y una serie de #define
s para valores individuales.
2U < -1L
como verdaderas y otras como falsas, y ahora estamos atrapados con el hecho de que algunas plataformas implementarán una comparación entre uint32_t
y int32_t
como se firmó y algunos como sin firmar, pero eso no significa que el Comité no pueda definir un sucesor de C compatible hacia arriba que incluya tipos cuya semántica sea coherente en todos los compiladores.
Una simple diferencia:
En el tiempo de preprocesamiento, la constante se reemplaza por su valor. Por lo tanto, no puede aplicar el operador de referencia a una definición, pero puede aplicar el operador de referencia a una variable.
Como es de suponer, definir es más rápido que la constante estática.
Por ejemplo, tener:
#define mymax 100
No puedes hacer printf("address of constant is %p",&mymax);
.
Pero teniendo
const int mymax_var=100
que puede hacer printf("address of constant is %p",&mymax_var);
.
Para ser más claro, la definición se reemplaza por su valor en la etapa de preprocesamiento, por lo que no tenemos ninguna variable almacenada en el programa. Solo tenemos el código del segmento de texto del programa donde se utilizó la definición.
Sin embargo, para const estática tenemos una variable que se asigna en algún lugar. Para gcc, las constantes estáticas se asignan en el segmento de texto del programa.
Arriba, quería contar sobre el operador de referencia, así que reemplace la desreferencia con referencia.
const
calificador. C no tiene constantes simbólicas que no sean constantes enum . A const int
es una variable. También confunde el lenguaje y las implementaciones específicas. No hay ningún requisito sobre dónde colocar el objeto. Y ni siquiera es cierto para gcc: generalmente coloca const
variables calificadas en la .rodata
sección. Pero eso depende de la plataforma de destino. Y te refieres a la dirección del operador &
.
Observamos el código de ensamblador producido en el MBF16X ... Ambas variantes dan como resultado el mismo código para operaciones aritméticas (ADD Inmediato, por ejemplo).
Por const int
lo tanto, se prefiere para la verificación de tipo, mientras que #define
es de estilo antiguo. Tal vez es específico del compilador. Así que verifique su código de ensamblador producido.
No estoy seguro de tener razón, pero en mi opinión, llamar al #define
valor d es mucho más rápido que llamar a cualquier otra variable normalmente declarada (o valor constante). Es porque cuando el programa se está ejecutando y necesita usar alguna variable normalmente declarada, necesita saltar al lugar exacto en la memoria para obtener esa variable.
Al contrario, cuando usa el #define
valor d, el programa no necesita saltar a ninguna memoria asignada, solo toma el valor. Si #define myValue 7
y el programa llama myValue
, se comporta exactamente igual que cuando solo llama 7
.