Zmieniona tablica w zakresie pliku

86

Chcę utworzyć stałą tablicę statyczną, która będzie używana w całym moim pliku implementacji Objective-C, podobną do tego na najwyższym poziomie mojego pliku „.m”:

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

Planuję użyć NUM_TYPESpóźniej w pliku, więc chciałem umieścić go w zmiennej.

Jednak kiedy to robię, pojawia się błąd

„Zmienione„ typy ”w zakresie pliku”

Rozumiem, że może to mieć coś wspólnego z rozmiarem tablicy jako zmienną (nie dostaję tego komunikatu, gdy umieszczam tam literał liczby całkowitej static int types[4]).

Chcę to naprawić, ale może wszystko źle robię ... Mam tutaj 2 cele:

  1. Posiadać tablicę, która jest dostępna w całym pliku
  2. Aby zamknąć NUM_TYPESw zmiennej, aby nie mieć tego samego literału rozproszonego w różnych miejscach w moim pliku

Jakieś sugestie?

[EDYTUJ] Znalezione w C Faq: http://c-faq.com/ansi/constasconst.html

Sam
źródło
2
Co się stanie, jeśli zamiast tego zrobisz to jako definicja? #define kNUM_TYPES 4?
Jorge Israel Peña
To działa ... z jakiegoś powodu starałem się trzymać z daleka od używania preprocesora, ponieważ myślałem, że gdzieś go czytałem, ale po prostu przeprowadziłem więcej badań i nie mogłem znaleźć dobrego powodu, aby go nie używać w tym przypadku. Myślę, że może być mniej pożądane, jeśli tworzę obiekty w preprocesorze (np. @"An NSString literal") Jedyną wadą twojego fragmentu kodu jest to, że nie ma potrzeby używania średnika.
Sam
Ach tak, dzięki za ostrzeżenia i cieszę się, że mogłem pomóc.
Jorge Israel Peña

Odpowiedzi:

63

Powodem tego ostrzeżenia jest to, że const w c nie oznacza stałej. To znaczy „tylko do odczytu”. Zatem wartość jest przechowywana pod adresem pamięci i może zostać zmieniona przez kod maszynowy.

larsr
źródło
3
Modyfikacja zdefiniowanego obiektu const(na przykład przez odrzucenie constwskaźnika i zapisanie wartości) jest niezdefiniowanym zachowaniem; dlatego wartość takiego obiektu jest stałą czasu kompilacji lub czasu wykonywania (w zależności od czasu przechowywania). Wartość nie może być używana w wyrażeniu stałym tylko dlatego, że standard C nie mówi, że może być. (Odrzucanie consti przechowywanie wartości jest dozwolone, jeśli obiekt docelowy jest zdefiniowany bez constlub dynamicznie przydzielony; literały łańcuchowe nie są, constale nie mogą być zapisywane.)
Jilles
3
@jilles „może potencjalnie zostać zmieniony przez kod maszynowy” nie oznacza, że ​​autor tej odpowiedzi miał na myśli „potencjalnie zmieniony przez kod C”. Co więcej, ma to inny bardzo dobry powód: mogą istnieć externstałe w różnych jednostkach tłumaczeniowych, których wartość nie jest znana podczas kompilowania bieżącej jednostki tłumaczeniowej.
14
Sposobem na ulepszenie tej odpowiedzi byłoby pokazanie, jak rozwiązać ten problem.
George Stocker
32

Jeśli i tak zamierzasz używać preprocesora, zgodnie z innymi odpowiedziami, możesz zmusić kompilator do określenia wartości NUM_TYPESautomagicznie:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
kawiarnia
źródło
Wow, to naprawdę fajne ... Nie wiedziałem, że to możliwe. Zakładam, że koszt tego obliczenia jest znikomy. Czy mogę założyć, że kompilator może zoptymalizować to do wartości statycznej?
Sam
2
Tak, wynikiem sizeofon obiektów jest stała czasu kompilacji.
kawiarnia
11

Możliwe jest również użycie wyliczenia.

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
Dave L Delaney
źródło
4

Jak już wyjaśniono w innych odpowiedziach, constw języku C oznacza jedynie, że zmienna jest tylko do odczytu. Nadal jest to wartość czasu wykonywania. Możesz jednak użyć enumjako rzeczywistej stałej w C:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};
CygnusX1
źródło
3

Imho, to jest wada w wielu kompilatorach C. Wiem na pewno, że kompilatory, z którymi pracowałem, nie przechowują zmiennej „static const” pod adresem, ale zastępują użycie w kodzie bardzo stałą. Można to zweryfikować, ponieważ uzyskasz tę samą sumę kontrolną dla wygenerowanego kodu, gdy używasz dyrektywy #define preprocesorów i gdy używasz statycznej zmiennej const.

Tak czy inaczej, należy używać statycznych zmiennych const zamiast #defines, gdy tylko jest to możliwe, ponieważ statyczna stała jest bezpieczna dla typu.

hans lepoeter
źródło
Brzmi to bardzo źle, ponieważ możesz wziąć adres static constzmiennej. Zachowanie, które opisujesz, może być prawidłową optymalizacją, ale z pewnością nie jest czymś, co zawsze może działać.
odpoczynek
Właściwie jest w porządku. Kompilator C może zastąpić indywidualne zastosowania zmiennych globalnych const stałą wartością, jeśli tylko jest to możliwe. Jeśli wszystkie odwołania do zmiennej zostaną przekonwertowane na stałe, kompilator może je całkowicie usunąć. Jeśli użyjesz adresu w dowolnym miejscu, nie zostanie on usunięty. Nic z tego nie zmienia faktu, że zgodnie ze standardem języka C nie zezwala na globalne tablice ze zmienną jako rozmiarem, niezależnie od tego, czy zmienna jest stała, czy nie.
Evan,