Array modificado de forma variável no escopo do arquivo

86

Eu quero criar uma matriz estática constante para ser usada em todo o meu arquivo de implementação Objective-C semelhante a algo assim no nível superior do meu arquivo ".m":

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

Pretendo usar NUM_TYPESmais tarde no arquivo, então queria colocá-lo em uma variável.

No entanto, quando faço isso, recebo o erro

"'Tipos' modificados de forma variável no escopo do arquivo"

Percebi que isso pode ter algo a ver com o tamanho do array ser uma variável (não recebo esta mensagem quando coloco um literal inteiro lá, como static int types[4]).

Quero consertar isso, mas talvez esteja fazendo tudo errado ... Tenho 2 objetivos aqui:

  1. Para ter uma matriz que é acessível em todo o arquivo
  2. Para encapsular NUM_TYPESem uma variável para que eu não tenha o mesmo literal espalhado por diferentes lugares em meu arquivo

Alguma sugestão?

[EDITAR] Encontrado no C Faq: http://c-faq.com/ansi/constasconst.html

Sam
fonte
2
O que acontece se você fizer isso como um definir em vez disso? #define kNUM_TYPES 4?
Jorge Israel Peña
Isso funciona ... por algum motivo, eu estava tentando evitar o uso do pré-processador porque pensei ter lido isso em algum lugar, mas acabei de fazer mais pesquisas e não consegui encontrar um bom motivo para não usá-lo neste caso. Acho que pode ser menos desejável se eu estiver criando objetos no pré-processador (como @"An NSString literal"). A única coisa errada com o seu código é que não há necessidade de ponto-e-vírgula.
Sam
Sim, obrigado pelo aviso e fico feliz por poder ajudar.
Jorge Israel Peña

Respostas:

63

A razão para esse aviso é que const em c não significa constante. Significa "somente leitura". Portanto, o valor é armazenado em um endereço de memória e pode ser potencialmente alterado por código de máquina.

larsr
fonte
3
Modificar um objeto definido const(como lançar longe constde um ponteiro e armazenar um valor) é um comportamento indefinido; portanto, o valor de tal objeto é um tempo de compilação ou constante de tempo de execução (dependendo da duração do armazenamento). O valor não pode ser usado em uma expressão constante simplesmente porque o padrão C não diz que pode ser. (A rejeição conste o armazenamento de um valor são permitidos se o objeto de destino for definido sem constou alocado dinamicamente; literais de string não são, constmas não podem ser gravados.)
jilles
3
@jilles "poderia ser potencialmente alterado por código de máquina" não significa que o autor desta resposta quisesse dizer "poderia ser potencialmente alterado por código C". Além disso, isso tem outra razão muito boa: pode haver externconstantes em diferentes TUs cujo valor não é conhecido ao compilar a TU atual.
14
Uma maneira de melhorar essa resposta seria mostrar como resolver esse problema.
George Stocker
32

Se for usar o pré-processador de qualquer maneira, conforme as outras respostas, você pode fazer o compilador determinar o valor de NUM_TYPESautomagicamente:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
caf
fonte
Nossa isso é muito legal ... Eu não sabia que era possível. Presumo que o custo desse cálculo seja insignificante. Posso também supor que um compilador poderia otimizar isso para um valor estático?
Sam
2
Sim, o resultado de sizeofobjetos on como esse é uma constante de tempo de compilação.
caf
21
#define NUM_TYPES 4
Jim Buck
fonte
11

Também é possível usar enumeração.

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

Como já foi explicado em outras respostas, constem C significa apenas que uma variável é somente leitura. Ainda é um valor de tempo de execução. No entanto, você pode usar um enumcomo uma constante real em C:

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

Na verdade, isso é uma falha em muitos compiladores c. Eu sei que os compiladores com os quais trabalhei não armazenam uma variável "const estática" em um endereço, mas substituem o uso no código pela própria constante. Isso pode ser verificado, pois você obterá a mesma soma de verificação para o código produzido quando usar uma diretiva #define de pré-processador e quando usar uma variável const estática.

De qualquer forma, você deve usar variáveis ​​const estáticas em vez de #defines sempre que possível, pois const estático é seguro para o tipo.

Hans Lepoeter
fonte
Isso soa muito ruim, já que você pode pegar o endereço de uma static constvariável. O comportamento que você está descrevendo pode ser uma otimização válida, mas certamente não é algo que sempre funcionará.
relaxe
É realmente bom. É normal que o compilador C substitua os usos individuais de variáveis ​​globais const pelo valor constante sempre que possível. Se todas as referências a uma variável forem convertidas em constantes, o compilador poderá removê-la inteiramente. Se você usar o endereço em qualquer lugar, ele não será removido. Nada disso muda o fato de que, de acordo com o padrão da linguagem, C não permite arrays globais com uma variável como tamanho, seja a variável constante ou não.
Evan