Массив с переменным изменением в области файла

Я хочу создать постоянный статический массив, который будет использоваться во всем моем файле реализации 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


person Sam    schedule 11.11.2009    source источник
comment
Что произойдет, если вы сделаете это вместо определения? #define kNUM_TYPES 4 ?   -  person Jorge Israel Peña    schedule 11.11.2009
comment
Это работает... по какой-то причине я пытался держаться подальше от использования препроцессора, потому что мне казалось, что я читал это где-то, но я просто провел дополнительное исследование и не смог найти веской причины не использовать его в этом случае. Я думаю, что это может быть менее желательно, если я создаю объекты в препроцессоре (например, @"An NSString literal"). Единственное, что не так с вашим фрагментом кода, это отсутствие необходимости в точке с запятой.   -  person Sam    schedule 11.11.2009
comment
Ах да, спасибо за голову, и рад, что я мог помочь.   -  person Jorge Israel Peña    schedule 11.11.2009


Ответы (5)


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

person larsr    schedule 19.02.2011
comment
Изменение объекта, определенного const (например, удаление const из указателя и сохранение значения) является поведением undefined; поэтому значение такого объекта является константой времени компиляции или времени выполнения (в зависимости от продолжительности хранения). Значение нельзя использовать в постоянном выражении просто потому, что стандарт C не говорит, что это возможно. (Отбрасывание const и сохранение значения разрешено, если целевой объект определен без const или выделяется динамически; строковые литералы не являются const, но в них нельзя записывать.) - person jilles; 29.12.2012
comment
@jilles потенциально может быть изменен машинным кодом, это не означает, что автор этого ответа имел в виду, что он потенциально может быть изменен кодом C. Кроме того, у этого есть еще одна очень веская причина: в разных TU может быть extern констант, значение которых неизвестно при компиляции текущей TU. - person ; 30.11.2013
comment
Способ улучшить этот ответ — показать, как решить эту проблему. - person George Stocker; 16.05.2016

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

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

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

typedef enum {
    typeNo1 = 1,
    typeNo2,
    typeNo3,
    typeNo4,
    NumOfTypes = typeNo4
}  TypeOfSomething;
person Dave L Delaney    schedule 11.11.2009

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

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

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

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

person hans lepoeter    schedule 16.08.2014
comment
Звучит довольно плохо, поскольку вы можете взять адрес переменной static const. Поведение, которое вы описываете, может быть действительной оптимизацией, но это, конечно, не то, что всегда может работать. - person unwind; 19.10.2016
comment
На самом деле все в порядке. Для компилятора C допустимо заменять отдельные случаи использования глобальных переменных const постоянными значениями везде, где это возможно. Если все ссылки на переменную преобразуются в константы, то компилятор может полностью ее удалить. Если вы используете адрес где-либо, он не будет удален. Ничто из этого не меняет того, что в соответствии со стандартом языка C не допускает глобальных массивов с переменной в качестве размера, независимо от того, является ли переменная константой или нет. - person Evan; 09.05.2017