preparado - ¿Por qué está int x[n] mal donde n es un valor constante?
dom preparado (5)
No puedo entender por qué hacer esto está mal:
const int n = 5;
int x[n] = { 1,1,3,4,5 };
a pesar de que n
ya es un valor constante.
Si bien esto parece ser el correcto para el compilador de GNU:
const int n = 5;
int x[n]; /*without initialization*/
Conozco la función VLA de C99 y creo que está relacionada con lo que está sucediendo, pero solo necesito aclarar qué está pasando en segundo plano.
¿Por qué
int x[n]
está mal donden
es un valorconst
?
n
no es una constante. const
solo promete que n
es una variable de "solo lectura" que no debe modificarse durante la ejecución del programa.
Tenga en cuenta que en c , a diferencia de c ++ , las variables const
calificadas no son constantes. Por lo tanto, la matriz declarada es una matriz de longitud variable.
No puede usar la lista de inicializadores para inicializar matrices de longitud variable.
C11-§6.7.9 / 3:
El tipo de entidad que se inicializará será una matriz de tamaño desconocido o un tipo de objeto completo que no sea de tipo de matriz de longitud variable .
Puede usar #define
o enum
para hacer n
una constante
#define n 5
int x[n] = { 1,1,3,4,5 };
Aunque n
es un const
, no puede usarlo para definir el tamaño de una matriz a menos que desee crear un VLA. Sin embargo, no puede usar una lista de inicializadores para inicializar un VLA.
Use una macro para crear una matriz de tamaño fijo.
#define ARRAY_SIZE 5
int x[ARRAY_SIZE] = { 1,1,3,4,5 };
Es su código semánticamente diferente de myfunc()
aquí:
void myfunc(const int n) {
int x[n] = { 1,1,3,4,5 };
printf("%d/n", x[n-1]);
*( (int *) &n) = 17; // Somewhat less "constant" than hoped...
return ;
}
int main(){
myfunc(4);
myfunc(5);
myfunc(6); // Haven''t actually tested this. Boom? Maybe just printf(noise)?
return 0;
}
¿Realmente n
es tan constante? ¿Cuánto espacio cree que debe asignar el compilador para x[]
(ya que es el trabajo del compilador hacerlo)?
Como han señalado otros, la const
cv-qualifier no significa "un valor que es constante durante la compilación y para todos los tiempos posteriores". Significa "un valor que el código local no debe cambiar (aunque puede)".
La clave para recordar es que const
y "constante" significan dos cosas bastante diferentes.
La palabra clave const
significa realmente "solo lectura". Una constante es un literal numérico, como 42
o 1.5
(o una enumeración o constante de caracteres). Una expresión constante es un tipo particular de expresión que se puede evaluar en tiempo de compilación, como 2 + 2
.
Entonces, con una declaración:
const int n = 5;
la expresión n
refiere al valor del objeto , y no se trata como una expresión constante. Un compilador típico optimizará una referencia a n
, reemplazándola por el mismo código que usaría para un literal 5
, pero eso no es necesario, y las reglas para determinar si una expresión es constante están determinadas por el lenguaje, no por la inteligencia de el compilador actual.
Un ejemplo de la diferencia entre const
(solo lectura) y constante (evaluada en tiempo de compilación) es:
const size_t now = time(NULL);
La palabra clave const
significa que no está permitido modificar el valor de now
después de su inicialización, pero el valor de time(NULL)
claramente no se puede calcular hasta el tiempo de ejecución.
Así que esto:
const int n = 5;
int x[n];
no es más válido en C de lo que sería sin la palabra clave const
.
El lenguaje podría (y en mi humilde opinión probablemente debería) evaluar n
como una expresión constante; simplemente no está definido de esa manera. (C ++ tiene una regla así, vea el estándar de C ++ o una referencia decente para los detalles sangrientos).
Si desea una constante con nombre con el valor 5
, la forma más común es definir una macro:
#define N 5
int x[N];
Otro enfoque es definir una constante de enumeración:
enum { n = 5 };
int x[n];
Las constantes de enumeración son expresiones constantes, y siempre son de tipo int
(lo que significa que este método no funcionará para tipos distintos de int
). Y es discutible un abuso del mecanismo enum
.
Comenzando con el estándar de 1999, una matriz se puede definir con un tamaño no constante; esto es un arreglo VLA o de longitud variable. Tales matrices están permitidas solo en el alcance del bloque, y pueden no tener inicializadores (dado que el compilador no puede verificar que el inicializador tenga la cantidad correcta de elementos).
Pero dado su código original:
const int n = 5;
int x[n] = { 1,1,3,4,5 };
puede dejar que el compilador infiera la longitud del inicializador:
int x[] = { 1,1,3,4,5 };
Y luego puede calcular la longitud del tamaño de la matriz:
const int x_len = sizeof x / sizeof x[0];
Si está inicializando exhaustivamente una matriz, entonces es más fácil, más seguro y más fácil de mantener que el compilador infiera el tamaño de la matriz:
int x[] = { 1,1,3,4,5 };
const int n = sizeof(x) / sizeof(*x) ;
Luego, para cambiar el tamaño de la matriz, solo tiene que cambiar el número de inicializadores, en lugar de cambiar el tamaño y la lista de inicializadores para que coincida. Especialmente útil cuando hay muchos inicializadores.