Ошибка в инициализаторе массива строк: элемент инициализатора не является константой

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

//.h file
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
const char *serviceStateKindScheduledMaintenanceDown = "ScheduledMaintenance (down)";
const char *serviceStateKindScheduledMaintenanceAvailable = "ScheduledMaintenance (available)";
const char *serviceStateKindMajorIncidentInProgress = "MajorIncidentInProgress";
const char *serviceStateKindPartialService = "PartialService";
const char *serviceStateKindOverloaded = "Overloaded";
const char *serviceStateKindGoingDown = "GoingDown";
const char *serviceStateKindDown = "Down";

const char *serviceStateKind[9] = {
    serviceStateKindNormal,
    serviceStateKindUnmanned,
    serviceStateKindScheduledMaintenanceDown,
    serviceStateKindScheduledMaintenanceAvailable,
    serviceStateKindMajorIncidentInProgress,
    serviceStateKindPartialService,
    serviceStateKindOverloaded,
    serviceStateKindGoingDown,
    serviceStateKindDown
};

но компилятор показывает

error: initializer element is not constant
     serviceStateKindNormal

в чем именно заключается проблема, и какой выбор у меня есть, чтобы определить мои переменные?

2 ответа

Решение

В языке C константа относится к буквальным константам, вроде (3.14, 2.718, так далее). Константные объекты (любого типа) не являются константами в терминологии языка Си.

Для создания констант в терминологии языка C используйте #define директива, например:

#define ServiceStateKindNormal "Normal"

и т.п.

Все, что вы помещаете в инициализированную переменную, объявленную в области видимости файла, должно быть константным выражением или строковым литералом, начиная с инициализации. Есть список того, что является константным выражением, и значение переменной среди них отсутствует. Так:

 // there is an array of char[2]{'a',\0'} somewhere in the memory
 // we store the pointer value to that array inside the variable a
 static const char *a = "a";
 // now we want to statically initialize variable b with the same value
 static const char *b = a;

не будет работать, потому что b инициализируется указателем a значение, которое не является постоянным выражением. Вам нужно постоянное выражение при инициализации переменной со статической продолжительностью хранения.

Что ты можешь сделать? Следующие:

  1. Хороший способ: почему вы храните указатели на строковые литералы "Normal"? Почему бы не хранить сами данные внутри массива? К счастью, адрес переменной является константным выражением, поэтому мы можем использовать его при инициализации! Обратите внимание, что (почти) все варианты использования и семантика остаются прежними, за исключением некоторого углового использования, такого как sizeof(serviceStateKindNormal) Результаты оператора.

    const char serviceStateKindNormal[] = "Normal";
    const char serviceStateKindUnmanned[] = "Unmanned";
    
    const char *serviceStateKind[] = {
        serviceStateKindNormal,
        serviceStateKindUnmanned,
    };
    
  2. Странный способ: хранить указатели на указатели на строки внутри массива. Поскольку переменные адреса являются константными выражениями, это будет работать. Обратите внимание, что делает serviceStateKind переменная три звезды. Вам необходимо дважды разыменовать массив при использовании. serviceStateKind это массив указателей на указатели на строки. Обратите внимание, что использование такого массива очень запутанно, поэтому я предлагаю вам перейти к структуре.

    const char *serviceStateKindNormal = "Normal";
    const char *serviceStateKindUnmanned = "Unmanned";
    
    const char **serviceStateKind[] = {
        &serviceStateKindNormal,
        &serviceStateKindUnmanned,
    };
    
    int main() {
        // question which one is correct?
        printf("%s\n", *serviceStateKind[1]);
        printf("%s\n", (*serviceStateKind)[1]);
        printf("%s\n", serviceStateKind[0][1]);
    }
    

но поскольку я не считаю себя трехзвездным программистом, я бы попытался сделать это как минимум до двух звезд:

    const char *serviceStateKindNormal = "Normal";
    const char *serviceStateKindUnmanned = "Unmanned";

    struct {
        // points to a string
        const char **pnt;
    // an array of pointers to string
    } serviceStateKind[] = {
        &serviceStateKindNormal,
        &serviceStateKindUnmanned,
    };

    int main() {
        // more verbose
        printf("%s\n", *serviceStateKind[0].pnt);
    }
  1. По старинке - макросы. Не делай этого. И что в этом плохого, вы можете использовать препроцессорную конкатенацию строковых литералов, чтобы объединить их вместе.

    #define serviceStateKindNormal  "Normal"
    #define serviceStateKindUnmanned  "Unmanned"
    
    const char *serviceStateKind[] = {
        serviceStateKindNormal,
        serviceStateKindUnmanned,
    };
    
    int main() { 
       # magic
       printf(serviceStateKindNormal " " serviceStateKindUnmanned "\n");
    }
    

Я думал, определив их в заголовочном файле следующим образом:

Если вы определяете переменные в заголовочном файле, вы хотите пометить их static, так что вы не получите повторяющихся ошибок символов при связывании с различными .c файлы, которые используют это .h файл. Также приятно пометить переменную как const как подсказка другим программистам, что они несколько не меняются.

Другие вопросы по тегам