Как я должен определить / объявить строковые константы - PullRequest
0 голосов
/ 04 марта 2019

Я всегда использовал строковые константы в C как одно из следующих

char *filename = "foo.txt";
const char *s = "bar";    /* preferably this or the next one */
const char * const s3 = "baz":

Но после прочтения this , теперь мне интересно, должен ли я объявить свою строкуконстанты как

const char s4[] = "bux";

?

Обратите внимание, что связанный вопрос, предложенный как дубликат, отличается, потому что этот вопрос специально задает о константе строках.Я знаю, как разные типы и как они хранятся.Версия массива в этом вопросе не const.Это был простой вопрос относительно того, должен ли я использовать константный массив для константных строк по сравнению с версией указателя, которую я использовал.Ответы здесь ответили на мой вопрос, когда два дня поиска по SO и Google не дали точного ответа.Благодаря этим ответам я узнал, что компилятор может выполнять особые действия, когда массив помечен const, и действительно (хотя бы один) случай, когда я сейчас буду использовать версию массива.

Ответы [ 3 ]

0 голосов
/ 04 марта 2019

В связанной статье исследуется небольшая искусственная ситуация, и продемонстрированная разница исчезает, если вставить const после * в const char *ptr = "Lorum ipsum"; (протестировано в Apple LLVM 10.0.0 с clang-1000.11.45.5).

Тот факт, что компилятор должен был загрузить ptr, возник полностью из-за того, что его можно было изменить в каком-то другом модуле, невидимом для компилятора.Создание указателя const устраняет это, и компилятор может подготовить адрес строки напрямую, без загрузки указателя.

Если вы собираетесь объявить указатель на строку и никогда не изменять указатель, тогдаобъявите его как static const char * const ptr = "string";, и компилятор может с радостью предоставить адрес строки всякий раз, когда используется значение ptr.На самом деле ему не нужно загружать содержимое ptr из памяти, поскольку оно никогда не изменится и, как известно, будет указывать, куда компилятор решит сохранить строку.Тогда это то же самое, что и static const char array[] = "string"; - всякий раз, когда требуется адрес массива, компилятор может предоставить его из своих знаний о том, где он решил хранить массив.

Кроме того, с помощью спецификатора static, ptr не может быть известно за пределами модуля перевода (файла, который компилируется), поэтому компилятор может удалить его во время оптимизации (если вы не взяли его адрес, возможно, при передаче его другой подпрограмме вне модуля перевода).В результате не должно быть различий между методом указателя и методом массива.

Практическое правило. Сообщите компилятору столько, сколько вам известно о вещах: если он никогда не изменится, отметьте его const.Если он является локальным для текущего модуля, отметьте его static.Чем больше информации имеет компилятор, тем больше он может оптимизировать.

0 голосов
/ 04 марта 2019

Указатель и массивы разные.Определение строковых констант как указателей или массивов соответствует различным целям.

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

const char product_name[] = "The program version 3";

Определение его как const char *product_name = "The program version 3"; фактически определяет 2 объекта: саму строковую константу, которая будет находиться в сегменте константы, и указатель, который можно изменить, чтобы он указывал на другую строку или установил на NULL.

И наоборотопределение строковой константы как локальной переменной лучше сделать в виде локальной переменной-указателя типа const char *, инициализированной адресом строковой константы:

int main() {
    const char *s1 = "world";
    printf("Hello %s\n", s1);
    return 0;
}

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

Обратите также внимание, чтоconst char const *s3 = "baz"; является избыточной формой const char *s3 = "baz";.Он отличается от const char * const s3 = "baz";, который определяет постоянный указатель на постоянный массив символов.

Наконец, строковые константы являются неизменяемыми и поэтому должны иметь тип const char [].Стандарт C специально позволяет программистам сохранять свои адреса в неконстантных указателях, как в char *s2 = "hello";, чтобы избежать выдачи предупреждений для унаследованного кода.В новом коде настоятельно рекомендуется всегда использовать указатели const char * для манипулирования строковыми константами.Это может заставить вас объявить аргументы функции как const char *, когда функция не изменяет содержимое строки.Этот процесс известен как constification и позволяет избежать незначительных ошибок.

Обратите внимание, что некоторые функции нарушают это const распространение: strchr() не изменяет полученную строку, объявленную как const char *,но возвращает char *.Таким образом, можно сохранить указатель на строковую константу в простом указателе char * следующим образом:

char *p = strchr("Hello World\n", 'H');

Эта проблема решается в C ++ с помощью перегрузки.Программисты C должны иметь дело с этим как с недостатком.Еще более раздражающая ситуация - strtol(), когда передается адрес char * и требуется преобразование для сохранения правильной константности.

0 голосов
/ 04 марта 2019

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

Однако я бы сказал, что const char s3[] = "bux"; лучшесемантическая перспектива, потому что тип правой стороны ближе к типу левой стороны.По этой причине я думаю, что имеет смысл объявить строковые константы с синтаксисом массива.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...