Изменяемый массив в области видимости файла

86

Я хочу создать постоянный статический массив, который будет использоваться во всем моем файле реализации Objective-C, похожий на что-то вроде этого на верхнем уровне моего файла ".m":

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

Я планирую использовать NUM_TYPESпозже в файле, поэтому я хотел поместить его в переменную.

Однако когда я это делаю, я получаю сообщение об ошибке

"Изменяемые" типы "в области файлов"

Я так понимаю, что это может иметь какое-то отношение к тому, что размер массива является переменной (я не получаю это сообщение, когда помещаю туда целочисленный литерал, например static int types[4]).

Я хочу исправить это, но, возможно, я все делаю неправильно ... У меня здесь 2 цели:

  1. Чтобы иметь массив, доступный во всем файле
  2. Чтобы инкапсулировать NUM_TYPESв переменную, чтобы у меня не было одного и того же литерала, разбросанного в разных местах в моем файле

Какие-либо предложения?

[EDIT] Нашел это в C Faq: http://c-faq.com/ansi/constasconst.html

Сэм
источник
2
Что произойдет, если вместо этого вы сделаете это как определение? #define kNUM_TYPES 4?
Хорхе Исраэль Пенья
Это работает ... по какой-то причине я старался держаться подальше от использования препроцессора, потому что мне показалось, что я где-то его читал, но я просто провел еще несколько исследований и не смог найти вескую причину не использовать его в этом случае. Я думаю, что это может быть менее желательно, если я создаю объекты в препроцессоре (например @"An NSString literal"). Единственное, что не так с вашим фрагментом кода, это то, что нет необходимости в точке с запятой.
Сэм
Ах да, спасибо за внимание и рад, что смог помочь.
Хорхе Исраэль Пенья

Ответы:

63

Причина этого предупреждения в том, что const в c не означает константу. Это означает «только чтение». Таким образом, значение хранится по адресу памяти и потенциально может быть изменено машинным кодом.

ларср
источник
3
Изменение определенного объекта const(например, отведение constот указателя и сохранение значения) является неопределенным поведением; следовательно, значение такого объекта является константой времени компиляции или времени выполнения (в зависимости от продолжительности хранения). Значение нельзя использовать в постоянном выражении просто потому, что стандарт C не говорит, что это может быть. (Отбрасывание constи сохранение значения разрешены, если целевой объект определен без constвыделения или динамически выделен; строковые литералы не constмогут быть записаны в них.)
jilles
3
@jilles «потенциально может быть изменен с помощью машинного кода» не означает, что автор этого ответа имел в виду «потенциально может быть изменен с помощью кода C». Более того, у этого есть еще одна очень веская причина: externв разных TU могут быть константы, значение которых не известно при компиляции текущего TU.
14
Способ улучшить этот ответ - показать, как решить эту проблему.
Джордж Стокер
32

Если вы все равно собираетесь использовать препроцессор, в соответствии с другими ответами, вы можете заставить компилятор определять значение NUM_TYPESавтоматически:

#define NUM_TYPES (sizeof types / sizeof types[0])
static int types[] = { 
  1,
  2, 
  3, 
  4 };
кафе
источник
Вау, это действительно круто ... Я не знал, что это возможно. Я предполагаю, что стоимость этого вычисления ничтожна. Могу ли я также предположить, что компилятор может оптимизировать это до статического значения?
Sam
2
Да, результат для таких sizeofобъектов является константой времени компиляции.
caf
11

Также возможно использование перечисления.

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
Дэйв Л. Делани
источник
4

Как уже объяснялось в других ответах, constв C просто означает, что переменная доступна только для чтения. Это по-прежнему значение времени выполнения. Однако вы можете использовать enumв C как реальную константу:

enum { NUM_TYPES = 4 };
static int types[NUM_TYPES] = { 
  1, 2, 3, 4
};
CygnusX1
источник
3

Имхо, это недостаток многих компиляторов c. Я точно знаю, что компиляторы, с которыми я работал, не хранят переменную "static const" по адресу, а заменяют использование в коде самой константой. Это можно проверить, поскольку вы получите одинаковую контрольную сумму для созданного кода при использовании директивы препроцессоров #define и при использовании статической переменной const.

В любом случае вы должны использовать статические константные переменные вместо #defines, когда это возможно, поскольку статическая константа безопасна по типу.

Ханс Лепоэтер
источник
Звучит довольно плохо, поскольку вы можете взять адрес static constпеременной. Описываемое вами поведение может быть допустимой оптимизацией, но, конечно, не всегда может работать.
расслабьтесь
На самом деле это нормально. Для компилятора C нормально заменять отдельные случаи использования глобальных переменных const постоянным значением везде, где это возможно. Если все ссылки на переменную преобразованы в константы, компилятор может удалить ее полностью. Если вы используете адрес где угодно, он не будет удален. Ничто из этого не меняет того, что в соответствии со стандартом языка C не допускает использование глобальных массивов с переменной в качестве размера, независимо от того, является ли переменная константой или нет.
Эван