Matriz modificada de forma variable en el ámbito del archivo

86

Quiero crear una matriz estática constante para usar en todo mi archivo de implementación de Objective-C similar a algo como esto en el nivel superior de mi archivo ".m":

static const int NUM_TYPES = 4;
static int types[NUM_TYPES] = { 
  1,
  2, 
  3, 
  4 };

Planeo usarlo NUM_TYPESmás adelante en el archivo, así que quería ponerlo en una variable.

Sin embargo, cuando hago esto, aparece el error

"'Tipos' modificados de forma variable en el ámbito del archivo"

Entiendo que esto puede tener algo que ver con que el tamaño de la matriz sea una variable (no recibo este mensaje cuando pongo un literal entero allí, como static int types[4]).

Quiero arreglar esto, pero tal vez lo esté haciendo mal ... Tengo 2 objetivos aquí:

  1. Tener una matriz accesible en todo el archivo
  2. Para encapsular NUM_TYPESen una variable para que no tenga el mismo literal disperso en diferentes lugares en mi archivo

¿Alguna sugerencia?

[EDITAR] Encontré esto en C Faq: http://c-faq.com/ansi/constasconst.html

Sam
fuente
2
¿Qué pasa si lo haces como una definición en su lugar? #define kNUM_TYPES 4?
Jorge Israel Peña
Eso funciona ... por alguna razón, estaba tratando de evitar usar el preprocesador porque pensé que recordaba haberlo leído en alguna parte, pero investigué un poco más y no pude encontrar una buena razón para no usarlo en este caso. Creo que puede ser menos deseable si estoy creando objetos en el preprocesador (como @"An NSString literal"). El único problema con su fragmento de código es que no es necesario el punto y coma.
Sam
Ah, sí, gracias por avisarme, y me alegro de poder ayudar.
Jorge Israel Peña

Respuestas:

63

El motivo de esta advertencia es que const en c no significa constante. Significa "solo lectura". Por lo tanto, el valor se almacena en una dirección de memoria y podría modificarse potencialmente mediante el código de la máquina.

larsr
fuente
3
La modificación de un objeto definido const(por ejemplo, alejando constun puntero y almacenando un valor) es un comportamiento indefinido; por lo tanto, el valor de dicho objeto es una constante de tiempo de compilación o de tiempo de ejecución (dependiendo de la duración del almacenamiento). El valor no se puede usar en una expresión constante simplemente porque el estándar C no dice que pueda serlo. (Reparto de distancia consty el almacenamiento de un valor se permite si el objeto de destino se define sin consto dinámicamente asignada; literales de cadena no están const, pero no pueden ser escritos a.)
Jilles
3
@jilles "potencialmente podría ser cambiado por código de máquina" no significa que el autor de esta respuesta quisiera decir "potencialmente podría ser cambiado por código C". Además, esto tiene otra muy buena razón: puede haber externconstantes en diferentes TU cuyo valor no se conoce al compilar la TU actual.
14
Una forma de mejorar esta respuesta sería mostrar cómo resolver este problema.
George Stocker
32

Si va a usar el preprocesador de todos modos, según las otras respuestas, puede hacer que el compilador determine el valor de NUM_TYPESautomágicamente:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
coste y flete
fuente
Vaya, eso es realmente genial ... No sabía que era posible. Supongo que el costo de este cálculo es insignificante. ¿Puedo asumir también que un compilador podría optimizar esto a un valor estático?
Sam
2
Sí, el resultado de sizeofen objetos como ese es una constante en tiempo de compilación.
caf
21
#define NUM_TYPES 4
Jim Buck
fuente
11

También es posible utilizar la enumeración.

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
Dave L Delaney
fuente
4

Como ya se explicó en otras respuestas, consten C simplemente significa que una variable es de solo lectura. Sigue siendo un valor en tiempo de ejecución. Sin embargo, puede usar an enumcomo una constante real en C:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};
CygnusX1
fuente
3

En mi humilde opinión, esto es un defecto en muchos compiladores de c. Sé a ciencia cierta que los compiladores con los que trabajé no almacenan una variable "const estática" en una dirección, sino que reemplazan el uso en el código por la constante. Esto se puede verificar, ya que obtendrá la misma suma de comprobación para el código producido cuando use una directiva #define de preprocesadores y cuando use una variable estática const.

De cualquier manera, debe usar variables estáticas const en lugar de #defines siempre que sea posible, ya que la constante estática es segura para los tipos.

Hans Lepoeter
fuente
Eso suena bastante mal, ya que puede tomar la dirección de una static constvariable. El comportamiento que está describiendo puede ser una optimización válida, pero ciertamente no es algo que siempre funcione.
relajarse el
En realidad está bien. Está bien que el compilador de C reemplace los usos individuales de las variables globales const con el valor constante siempre que sea posible. Si todas las referencias a una variable se convierten en constantes, el compilador puede eliminarla por completo. Si usa la dirección en cualquier lugar, no se eliminará. Nada de eso cambia que, según el estándar del lenguaje, C no permite matrices globales con una variable como tamaño, ya sea que la variable sea constante o no.
Evan