2016-12-07 4 views
3

Следующий код компилируется отлично, если я удаляю статический классификатор, но я не понимаю, почему он не работает в противном случае. Я вижу строку в разделе .rodata (при компиляции без статического классификатора), а конечная сборка просто загружает указатели надлежащим образом перед вызовом printf.Инициализатор не постоянный ... Я знаю, но я чувствую, что это должно работать, почему это не так.

#include <stdio.h> 
#define S "Testing" 
#define C 't' 
typedef struct { 
    char const * const s; 
    char const c; 
} foo; 
int main(int argc, char **argv) 
{ 
    static foo const f = { 
     .s = S, 
     .c = S[3], 
    }; 
    printf("s = %s, c = %c\n", f.s, f.c); 
    return 0; 
} 

Кроме того, он отлично компилируется со статическим классификатором, если я использую:

.c = C, 

Интересно, что сборка выглядит идентично (да, НКУ угоняет «т» из «Тестирование»)

Этот фрагмент просто демонстрирует конкретный пример, с которым я сталкиваюсь в гораздо более сложной проблеме. Почему компилятор не может рассматривать строковый литерал [] как константу, когда это ясно?

BTW, скомпилирован с GCC 5.4 только с -O2

Edit: Для того, чтобы уточнить, проблема заключается в инициализации f.c

Edit: Спасибо за Insights всех. Таким образом, препроцессор не может ничего делать со строками, что является хромым. Если я действительно реализую то, что я пытаюсь сделать в области действия функции, компилятор (с оптимизацией) в конечном итоге переработает все до констант в разделе .rodata, но я не могу сделать то же самое в глобальной области, к сожалению ,

Решение: Написать пользовательский питон скрипт (или любой другой ваш любимый язык сценариев) для генерации кода, или использовать C++

+0

Для уточнить, проблема заключается в инициализации fc –

+0

Редактирование вашего разъяснения в вопросе, а не комментарий. – Barmar

ответ

4

В объектах С языка со статической продолжительностью хранения требуется постоянные выражения как инициализаторах. S[3] не является постоянным выражением в C. Вам в основном не разрешено «читать из памяти» в постоянных выражениях, то есть вам не разрешено использовать унарный оператор * и, следовательно, не разрешено использовать оператор [] для чтения элементов массива.

Между тем, 't' является постоянной константой, поэтому ваш код компилируется при замене S[3] на C.

+0

Почему же? Если S является выражением const, то как трудно рассматривать S [i] как выражение const. Это буквально * ((& S) + sizeof (S [0]) * i) –

+0

@Scott Valentine: Да, вы правы, не должно быть сложно компилятору выяснить, что «Тестирование» [3] ' в этом конкретном случае. Однако общие правила констант C констант запрещают оператор '[]' в этом контексте. Исключение не делается для «очевидных» ситуаций. – AnT

+0

ну, я думаю, что меня вторгаются тогда ... Я проголосовал за ваш ответ, но у меня нет репутации. –

-5

Ты проблема заключается в том, что определить не инициализирует используется только в препроцессора. Вам потребуется:

static const char *S = "Testing/0"; 
static const char C = 't'; 
+0

Нет, то, что я на самом деле пытаюсь выполнить, заключается в использовании препроцессора для компиляции данных вплоть до значения истинной статической константы (.rodata), и моя проблема блокировки заключается в том, что она не обрабатывает элементы массива как постоянные выражения, но она обрабатывает массив как постоянное выражение ... Ваш пример никогда не может использоваться как статический инициализатор для чего-то другого, а «\ 0» всегда неявный (ну, если вы не используете размер фиксированного массива). –

+0

Исправлено: он должен быть const! – koper89

+0

Я думаю, что вам не хватает точки ... У меня нет проблем с инициализацией f.s, проблема в f.c. В вашем примере * S отлично, конечно, но он никогда не может использоваться как статический инициализатор для чего-то другого. –

2

С C11 6.7.9/4 (Инициализация - Ограничения):

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

Определение постоянное выражение содержится в 6.6. В частности, 6.6/7:

Для постоянных выражений в инициализаторах допускается использование большей широты.Такой постоянное выражение должно быть, или вычисляться, одно из следующих действий:

  • арифметического выражения-константы,
  • пустого указателя константы,
  • адреса постоянная или
  • адреса константы полный тип объекта плюс или минус целочисленное постоянное выражение.

S[3] Однако пока ни один из них. Арифметическое константное выражение разрешено иметь только операнды, которые являются константами арифметического типа (вкратце), однако S[3] означает *(S + 3), который имеет операнд указателя.

Использование 't' в качестве инициализатора в порядке, то есть символьная константа и, следовательно, выражение постоянной арифметики.

Примечание: постоянное выражение имеет совершенно отдельный смысл для "const -qualified variable". Константные выражения никогда не являются const -qualified и являются непересекающимися множествами из переменных и объектов.


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

Обратите внимание, что строковые литералы не являются постоянными выражениями (хотя их адрес есть). Это массивы неконстантных char. Существует также правило, что это неопределенное поведение для изменения строкового литерала. Поэтому переносимый код должен обрабатывать строковый литерал, как если бы он был const, хотя технически это не так. Исторически некоторые компиляторы разрешили модификацию строковых литералов.

+0

Более значительным, чем тот факт, что некоторые компиляторы имеют «разрешенную» модификацию строковых литералов, является тот факт, что некоторые компиляторы имеют документальное поведение в таких случаях, даже если стандарт не требует от них этого. – supercat

0

Я не уверен, какова ваша конечная цель, но, возможно, это небольшое изменение в вашем struct может помочь. В соответствии со стандартом:

Массив-индекс [] и член доступа . и -> операторы, адрес & и косвенное предоставление * унарные операторы, и указатель слепков могут быть использованы при создании адреса постоянного, но значение объекта не должно быть доступны использование этих операторов.
ISO/IEC 9899: 2011 6,6/9

В коде из вашего примера, вы пытаетесь использовать значение S[3] в инициализаторе в качестве постоянного выражения.Приведенные выше показывает, почему это невозможно, но вы можете использовать адрес из S[3] постоянного выражения:

#include <stdio.h> 
#define S "Testing" 
#define C 't' 
typedef struct { 
    char const * const s; 
    char const * const c; 
} foo; 
int main(int argc, char **argv) 
{ 
    static foo const f = { 
     .s = S, 
     .c = &S[3], 
    }; 
    printf("s = %s, c = %c\n", f.s, *(f.c)); 
    return 0; 
} 

Когда я скомпилировать и запустить этот код, выход:

s = Testing, c = t 
Смежные вопросы