Почему переменные типа char * cstring = & ldquo; myString & rdquo; невозможно изменить, но переменные определены char [] cstring = & ldquo; mystring & rdquo; можно изменить? [Дубликат]

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

Пример:

string postalcode=Customer.Address.PostalCode; 
//if customer or address is null , this will through exeption

здесь, если адрес имеет значение null, то вы получите NullReferenceException.

Итак, в качестве практики мы всегда должны использовать проверку нуля, прежде чем обращаться к свойствам в таких объектах (особенно в общих)

string postalcode=Customer?.Address?.PostalCode;
//if customer or address is null , this will return null, without through a exception
18
задан marcog 20 December 2010 в 20:41
поделиться

8 ответов

Они имеют тип char[N], где N - количество символов, включая завершающий \0. Поэтому да, вы можете назначить их char*, но вы все равно не можете писать им (эффект будет неопределенным).

Wrt argv: указывает на массив указателей на строки. Эти строки явно изменяемы. Вы можете изменить их, и они должны удерживать последнее сохраненное значение.

18
ответ дан Johannes Schaub - litb 27 August 2018 в 15:07
поделиться

(Извините, я только что заметил, что этот вопрос отмечен как c, а не c++. Возможно, мой ответ не так важен для этого вопроса!) [/ ​​g3]

Строковые литералы не совсем const или not-const, существует специальное странное правило для литералов.

(Резюме: литералы могут быть взяты с помощью ссылки на массив как foo( const char (&)[N]) и не могут быть приняты как не-const. Они предпочитают распадаться до const char *. Пока что это похоже на то, что они const. Но существует специальное правило, разрешает литералы распадаться на char *. См. эксперименты ниже.)

(После экспериментов, выполненных на clang3.3 с -std=gnu++0x. Возможно, это проблема C ++ 11 или конкретная для clang? Кстати, что-то странное происходит.)

Сначала литералы выглядят как const:

void foo( const char  * ) { std::cout << "const char *" << std::endl; }
void foo(       char  * ) { std::cout << "      char *" << std::endl; }

int main() {
        const char arr_cc[3] = "hi";
        char arr_c[3] = "hi";

        foo(arr_cc); // const char *
        foo(arr_c);  //       char *
        foo("hi");   // const char *
}

Эти два массива ведут себя так, как ожидалось, демонстрируя, что foo может указать нам, является ли указатель const или нет. Затем "hi" выбирает const версию foo. Кажется, что это решает: литералы const ... не так ли?

Но , если вы удалите void foo( const char * ), тогда это становится странным. Во-первых, вызов foo(arr_c) не выполняется с ошибкой во время компиляции. Это ожидается. Но буквальный вызов (foo("hi")) работает через неконстантный вызов.

Итак, литералы «больше const», чем arr_c (потому что они предпочитают распадаться на const char *, в отличие от arr_c. Но литералы «меньше const», чем arr_cc, потому что они желают распадаться на char *, если это необходимо.

(Кланг дает предупреждение, когда он распадается на char *).

Но как насчет разложения? Давайте избежим этого для простоты.

Давайте возьмем массивы по ссылке в foo вместо этого. Это дает нам более «интуитивные» результаты:

void foo( const char  (&)[3] ) { std::cout << "const char (&)[3]" << std::endl; }
void foo(       char  (&)[3] ) { std::cout << "      char (&)[3]" << std::endl; }

Как и раньше, литерал и массив const (arr_cc) используют версию const, а версия non-const используется arr_c. И если мы удалим foo( const char (&)[3] ), тогда мы получим ошибки с обоими foo(arr_cc); и foo("hi");. Короче говоря, если мы избежим распада указателя и будем использовать ссылку на массив, литералы будут вести себя так, как если бы они были const.

Шаблоны?

В шаблонах система выведет const char * вместо char *, и вы «застряли» с этим.

template<typename T>
void bar(T *t) { // will deduce   const char   when a literal is supplied
    foo(t);
}

Итак, буквально ведет себя как const на ll раз, за ​​исключением конкретного случая, когда вы непосредственно инициализируете char * литералом.

3
ответ дан Aaron McDaid 27 August 2018 в 15:07
поделиться

Ответ Йоханнеса верен относительно типа и содержания. Но в дополнение к этому да, это неопределенное поведение для изменения содержимого строкового литерала.

Что касается вашего вопроса о argv:

Параметры argc и argv и строки, на которые указывает массив argv, могут быть модифицированы программой и сохраняют свои последние сохраненные значения между запуском программы и завершением программы.

2
ответ дан Jens Gustedt 27 August 2018 в 15:07
поделиться

В C89 и C99 строковые литералы имеют тип char * (по историческим причинам, насколько я понимаю). Вы правы, что пытаетесь изменить один результат в неопределенном поведении. GCC имеет специальный предупреждающий флаг, -Wwrite-strings (который не является частью -Wall), который предупредит вас, если вы попытаетесь это сделать.

Что касается argv, аргументы копируются в адресное пространство вашей программы и могут быть безопасно изменены в вашей функции main().

EDIT: Упс, если -Wno-write-strings скопирован случайно. Обновлен с правильной (положительной) формой предупреждающего флага.

1
ответ дан Justin Spahr-Summers 27 August 2018 в 15:07
поделиться

Это const char *, но есть специальное исключение для назначения их char * для устаревшего кода, существовавшего до того, как const сделал. И аргументы командной строки определенно не являются буквальными, они создаются во время выполнения.

1
ответ дан Puppy 27 August 2018 в 15:07
поделиться

Строковые литералы имеют формальный тип char [], но семантический тип const char []. Пуристы ненавидят его, но это вообще полезно и безвредно, за исключением того, что приносят много новичков к SO с «ПОЧЕМУ МОЙ ПРОГРАММИРУЮЩИЙ ПРОГРАММА?!?!» вопросы.

1
ответ дан R.. 27 August 2018 в 15:07
поделиться

С помощью опции -Wwrite-strings вы получите:

warning: initialization discards qualifiers from pointer target type

Независимо от этой опции GCC помещает литералы в раздел памяти только для чтения, если не сказать иначе, используя -fwritable-strings (однако эта опция был удален из недавних версий GCC).

Параметры командной строки не являются константами, они обычно живут в стеке.

5
ответ дан rsp 27 August 2018 в 15:07
поделиться

Для полноты проекта C99 проект стандарта ( C89 и C11 имеет схожую формулировку ) в разделе 6.4.5 . Строковые литералы, параграф 5 , гласят:

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

Таким образом, это говорит строковый литерал имеет статическую продолжительность хранения ( длится время жизни программы ), и это тип char[] (не char *), а его длина - это размер строкового литерала с добавленным нулем , * Параграф 6 говорит:

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

Поэтому попытка изменить Строковый литерал является неопределенным поведением , независимо от того, что они не являются const.

В отношении argv в разделе 5.1.2.2.1 Программа в пункте 2 запуска говорится:

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

[...]

. Параметры argc и argv и строки, на которые указывает массив argv, могут быть модифицированы программой и сохраняют свои последние сохраненные значения между запуском программы и завершением программы.

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

5
ответ дан Toby Speight 27 August 2018 в 15:07
поделиться
Другие вопросы по тегам:

Похожие вопросы: