Этот ответ хороший, но не совсем полный.
char * test = "abcdefghijklmnopqrstuvwxyz";
A строковый литерал относится к объекту анонимного массива типа char[N]
со статической продолжительностью хранения (то есть он существует для всего выполнения программы), где N
- длина строки плюс один за окончание '\0'
. Этот объект не const
, но любая попытка изменить его имеет неопределенное поведение. (Реализация может сделать строковые литералы доступными для записи, если она выберет, но большинство современных компиляторов этого не делают.)
Объявление выше создает такой анонимный объект типа char[27]
и использует адрес первого элемента этого объекта для инициализации test
. Таким образом, присваивание типа test[5] = 'x'
пытается изменить массив и имеет неопределенное поведение; как правило, это приведет к краху вашей программы. (При инициализации используется адрес, потому что литерал является выражением типа массива, который в большинстве контекстов неявно преобразуется в указатель на первый элемент массива.)
Обратите внимание, что в C ++ строковые литералы на самом деле const
, и приведенное выше объявление будет недопустимым. В C или C ++ лучше всего объявить test
как указатель на const char
:
const char *test = "abcdefghijklmnopqrstuvwxyz";
, поэтому компилятор предупредит вас, если вы попытаетесь изменить массив с помощью test
.
(Строковые литералы C не являются const
по историческим причинам. До стандарта ANSI C 1989 года ключевое слово const
не существовало. Требовать его использования в объявлениях, аналогичных вашим, было бы сделано для более безопасного кода, но это потребовалось бы изменить существующий код, чего комитет ANSI пытался избежать. Вам следует притвориться , что строковые литералы равны const
, даже если это не так. Если вы используете gcc, Опция -Wwrite-strings
заставит компилятор обрабатывать строковые литералы как const
- что делает gcc несоответствующим.)
Если вы хотите изменить строку, на которую ссылается test
, вы можете определить ее следующим образом:
char test[] = "abcdefghijklmnopqrstuvwxyz";
Компилятор смотрит на инициализатор, чтобы определить, насколько большим должен быть test
. В этом случае test
будет иметь тип char[27]
. Строковый литерал все еще ссылается на анонимный объект массива, доступный только для чтения, но его значение скопировано в test
. (Строковый литерал в инициализаторе, который используется для инициализации объекта массива, является одним из контекстов, в которых массив не «затухает» по указателю; другие - когда это операнд унарного &
или sizeof
.) дальнейших ссылок на анонимный массив нет, компилятор может его оптимизировать.
В этом случае test
представляет собой массив, содержащий 26 символов, которые вы указали, плюс терминатор '\0'
. Время жизни этого массива зависит от того, где объявлено test
, что может иметь или не иметь значения. Например, если вы сделаете это:
char *func(void) {
char test[] = "abcdefghijklmnopqrstuvwxyz";
return test; /* BAD IDEA */
}
звонящий получит указатель на то, что больше не существует. Если вам нужно обратиться к массиву вне области действия, в которой определен test
, вы можете определить его как static
или выделить его с помощью malloc
:
char *test = malloc(27);
if (test == NULL) {
/* error handling */
}
strcpy(test, "abcdefghijklmnopqrstuvwxyz";
, поэтому массив будет существовать до тех пор, пока вы не вызовете free()
. Нестандартная функция strdup()
делает это (она определяется POSIX, но не ISO C).
Обратите внимание, что test
может быть указателем или массивом, в зависимости от того, как вы его объявили. Если вы передадите test
в строковую функцию или любую функцию, которая принимает char*
, это не имеет значения, но что-то вроде sizeof test
будет вести себя очень по-разному в зависимости от того, является ли test
указателем или массивом. .
comp.lang.c FAQ отлично. Раздел 8 охватывает символы и строки, и вопрос 8.5 указывает на вопрос 1.32, в котором рассматривается ваш конкретный вопрос. Раздел 6 посвящен часто сбивающим с толку отношениям между массивами и указателями.