Если мы рассмотрим общие сценарии, в которых может быть выбрано это исключение, доступ к свойствам с объектом вверху.
Пример:
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
Они имеют тип char[N]
, где N
- количество символов, включая завершающий \0
. Поэтому да, вы можете назначить их char*
, но вы все равно не можете писать им (эффект будет неопределенным).
Wrt argv
: указывает на массив указателей на строки. Эти строки явно изменяемы. Вы можете изменить их, и они должны удерживать последнее сохраненное значение.
(Извините, я только что заметил, что этот вопрос отмечен как 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 *
литералом.
Ответ Йоханнеса верен относительно типа и содержания. Но в дополнение к этому да, это неопределенное поведение для изменения содержимого строкового литерала.
Что касается вашего вопроса о argv
:
Параметры argc и argv и строки, на которые указывает массив argv, могут быть модифицированы программой и сохраняют свои последние сохраненные значения между запуском программы и завершением программы.
blockquote>
В C89 и C99 строковые литералы имеют тип char *
(по историческим причинам, насколько я понимаю). Вы правы, что пытаетесь изменить один результат в неопределенном поведении. GCC имеет специальный предупреждающий флаг, -Wwrite-strings (который не является частью -Wall
), который предупредит вас, если вы попытаетесь это сделать.
Что касается argv
, аргументы копируются в адресное пространство вашей программы и могут быть безопасно изменены в вашей функции main()
.
EDIT: Упс, если -Wno-write-strings
скопирован случайно. Обновлен с правильной (положительной) формой предупреждающего флага.
Это const char *, но есть специальное исключение для назначения их char * для устаревшего кода, существовавшего до того, как const сделал. И аргументы командной строки определенно не являются буквальными, они создаются во время выполнения.
Строковые литералы имеют формальный тип char []
, но семантический тип const char []
. Пуристы ненавидят его, но это вообще полезно и безвредно, за исключением того, что приносят много новичков к SO с «ПОЧЕМУ МОЙ ПРОГРАММИРУЮЩИЙ ПРОГРАММА?!?!» вопросы.
С помощью опции -Wwrite-strings
вы получите:
warning: initialization discards qualifiers from pointer target type
Независимо от этой опции GCC помещает литералы в раздел памяти только для чтения, если не сказать иначе, используя -fwritable-strings
(однако эта опция был удален из недавних версий GCC).
Параметры командной строки не являются константами, они обычно живут в стеке.
Для полноты проекта C99 проект стандарта ( C89 и C11 имеет схожую формулировку ) в разделе 6.4.5
. Строковые литералы, параграф 5 , гласят:
[...] байт или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая возникает из строкового литерала или литералов. Последовательность многобайтовых символов затем используется для инициализации массива статической продолжительности хранения и длины, достаточной для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов, [...]
blockquote>Таким образом, это говорит строковый литерал имеет статическую продолжительность хранения ( длится время жизни программы ), и это тип
char[]
(неchar *
), а его длина - это размер строкового литерала с добавленным нулем , * Параграф 6 говорит:Если программа пытается изменить такой массив, поведение не определено.
blockquote>Поэтому попытка изменить Строковый литерал является неопределенным поведением , независимо от того, что они не являются
const
.В отношении
argv
в разделе5.1.2.2.1
Программа в пункте 2 запуска говорится:Если они объявлены, параметры главной функции должны подчиняться следующим ограничениям:
[...]
. Параметры argc и argv и строки, на которые указывает массив argv, могут быть модифицированы программой и сохраняют свои последние сохраненные значения между запуском программы и завершением программы.
blockquote>Таким образом,
argv
не считается массивом строковых литералов, и нормально модифицировать содержимоеargv
.