speed - pointers in c
“Static const” vs “#define” vs “enum” (17)
Depende de para qué necesitas el valor. Usted (y todos los demás hasta ahora) omitió la tercera alternativa:
-
static const int var = 5;
-
#define var 5
-
enum { var = 5 };
Ignorando temas sobre la elección del nombre, entonces:
- Si necesita pasar un puntero, debe usar (1).
- Ya que (2) es aparentemente una opción, no es necesario que pases los punteros.
- Tanto (1) como (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, lo que te hace preguntarte qué es.
- (1) no se puede utilizar como una dimensión para matrices en el ámbito global; Ambos (2) y (3) pueden.
- (1) no se puede utilizar como una dimensión para matrices estáticas en el ámbito de la función; Ambos (2) y (3) pueden.
- Bajo C99, todos estos se pueden usar para arreglos locales. Técnicamente, el uso de (1) implicaría el uso de un VLA (matriz de longitud variable), aunque la dimensión a la que hace referencia ''var'' se fijará, por supuesto, en el tamaño 5.
- (1) no se puede utilizar en lugares como declaraciones de cambio; Ambos (2) y (3) pueden.
- (1) no se puede utilizar para inicializar variables estáticas; Ambos (2) y (3) pueden.
- (2) puede cambiar el código que no deseaba cambiar porque lo utiliza el preprocesador; tanto (1) como (3) no tendrán efectos secundarios inesperados como ese.
- Puede detectar si (2) se ha configurado en el preprocesador; ni (1) ni (3) permite eso.
Por lo tanto, en la mayoría de los contextos, prefiera el ''enum'' 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 por C ++, entonces usaría la opción (1) - la constante estática - cada vez.
¿Cuál es mejor usar entre las siguientes afirmaciones en C?
static const int var = 5;
o
#define var 5
o
enum { var = 5 };
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 utilizar static const
en este contexto, que yo sepa. En C ++ debes evitar las macros en estos casos. Puedes escribir
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
e incluso omitir la static
porque la vinculación interna está implícita en const
ya [solo en C ++].
En C, específicamente? En C, la respuesta correcta es: use #define
(o, si corresponde, enum
)
Si bien es beneficioso tener las propiedades de alcance y escritura de un objeto const
, en realidad los objetos const
en C (a diferencia de C ++) no son constantes verdaderas y, por lo tanto, suelen ser inútiles en la mayoría de los casos prácticos.
Por lo tanto, en C, la elección debe determinarse según la forma en que planea usar su constante. Por ejemplo, no puede usar un objeto const int
como una etiqueta de case
(mientras que una macro funcionará). No puede utilizar 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 remedio 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 constantes verdaderas, por lo que en C ++ casi siempre es mejor preferir la variante const
(aunque no es necesario que exista static
explícita en C ++).
Escribí el 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 se 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 la enumeración da un error cuando define da una advertencia.
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 comandos. 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 segura para el tipo.
Si realmente NECESITA ir con una macro (por ejemplo, quiere __FILE__
o __LINE__
), entonces debería nombrar su macro MUY __FILE__
: en su convención de nombres, Boost recomienda todas las mayúsculas, comenzando por el nombre del proyecto ( aquí, BOOST_), mientras lee detenidamente la biblioteca, notará que (generalmente) le sigue el nombre del área en particular (biblioteca) y luego un nombre significativo.
Por lo general hace para nombres largos :)
Incidentalmente, una alternativa a #define
, que proporciona un alcance adecuado pero se comporta como una constante "real", es "enumeración". Por ejemplo:
enum {number_ten = 10;}
En muchos casos, es útil definir tipos enumerados y crear variables de esos tipos; si se hace eso, los depuradores pueden mostrar variables según 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, por defecto, uno no puede realizar aritmética sobre ellos. Me parece que es un curioso comportamiento por defecto para las enumeraciones; aunque hubiera sido bueno tener un tipo de "enumeración estricta", dado el deseo de tener C ++ generalmente compatible con C, creo que el comportamiento predeterminado de un tipo de "enumeración" debería ser intercambiable con números enteros.
La definición
const int const_value = 5;
No siempre se 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 puedes modificar esta memoria. Pero todavía podrías 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 la definición
#define CONST_VALUE 5
Es la única forma de definir un valor constante que no puede modificarse de ninguna manera.
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
. En realidad, dependiendo de las circunstancias en las que nos encontremos, debemos seleccionar una de estas dos. Ambos están en su mejor momento en diferentes circunstancias. Por favor, no asumas que uno es mejor que el otro ... :-)
Si ese hubiera sido el caso, Dennis Ritchie habría mantenido solo al mejor ... jajaja ... :-)
No estoy seguro de estar en lo cierto, pero en mi opinión, llamar al valor #define
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, tiene que saltar al lugar exacto en la memoria para obtener esa variable.
Al contrario, cuando usa el valor #define
d, el programa no necesita saltar a ninguna memoria asignada, solo toma el valor. Si #define myValue 7
y el programa que llama a myValue
, se comporta exactamente igual que cuando solo llama a 7
.
No piense que hay una respuesta para "lo que siempre es mejor" pero, como Matthieu dijo
static const
es de tipo seguro. Sin embargo, mi mayor molestia con #define
es que al depurar en Visual Studio no puedes ver la variable. Da un error que el símbolo no se puede encontrar.
Observamos el código de ensamblador producido en el MBF16X ... Ambas variantes dan como resultado el mismo código para las operaciones aritméticas (ADD Inmediato, por ejemplo).
Por lo tanto, se prefiere const int
para la comprobación de tipo mientras que #define
es de estilo antiguo. Tal vez es específico del compilador. Así que revise su código de ensamblador producido.
Otro inconveniente de const
en C es que no se puede usar el valor al inicializar otra 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!
Estaré encantado de utilizar const
en estos casos, de lo contrario ...
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 #define en sí mismo no es parte del código (en términos generales).
Ejemplo:
#define PI 3.1416
El nombre simbólico PI nunca puede ser visto por compiladores; puede ser eliminado por el preprocesador 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 recibe un error durante la compilación que involucra el uso de la constante, ya que el mensaje de error puede referirse a 3.1416, no a PI. Si se definiera PI en un archivo de encabezado que no escribió, no tendría idea de dónde provino 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...
Si bien la pregunta era sobre los enteros, vale la pena señalar que #define y enums son inútiles si necesita una estructura o cadena constante. Estos se suelen pasar a las funciones como punteros. (Con cuerdas se requiere; con estructuras es mucho más eficiente.)
En cuanto a los enteros, si se encuentra en un entorno integrado con memoria muy limitada, es posible que deba preocuparse por 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 agregar dos #defines en tiempo de compilació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 programa. Una constante constante se almacenará en la sección .const en la memoria de datos. En los sistemas con una arquitectura de Harvard, podría haber diferencias en el rendimiento y el uso de la memoria, aunque probablemente sean pequeñas. Pueden ser importantes para la optimización de núcleo de los bucles internos.
Si puede salirse con la suya, static const
tiene muchas ventajas. Obedece 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 usa #define var 5
, puede escribir int foo[var];
como una declaración, pero no se puede hacer eso (excepto como una extensión del compilador "con static const int var = 5;
Este no es el caso en C ++, donde la versión static const
puede usarse en cualquier lugar que pueda la versión #define
, y creo que este es también el caso de 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 efectivamente es su propio espacio de nombres, que tradicionalmente es mayúsculas, quizás con un prefijo.
Una simple diferencia:
En el tiempo de preprocesamiento, la constante se reemplaza con su valor. Por lo tanto, no puede aplicar el operador de desreferencia a una definición, pero puede aplicar el operador de desreferencia 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 puede hacer printf("address of constant is %p",&mymax);
.
Pero teniendo
const int mymax_var=100
puede 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 programa. Tenemos solo el código del segmento de texto del programa donde se usó la definición.
Sin embargo, para const constantes tenemos una variable que está asignada en algún lugar. Para gcc, las constantes estáticas se asignan en el segmento de texto del programa.
Anteriormente, quería hablar sobre el operador de referencia, así que reemplace la desreferencia por referencia.
#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.