C: поведение массивов при назначении указателям

#include  main() { char * ptr; ptr = "hello"; printf("%p %s" ,"hello",ptr ); getchar(); } 

Привет, Я пытаюсь понять, как массивы могут присваиваться указателям. Я замечаю, когда вы назначаете массив символов для указателя символов ptr="hello"; массив распадается на указатель, но в этом случае я назначаю char массивов, которые не находятся внутри переменной, а не переменная, содержащая их », делает ли этот способ присвоения адрес памяти специально для "Hello" (что очевидно ), и можно ли изменить значение каждого элемента в «Hello», которое содержится в адресе памяти, в котором хранится этот массив. Для сравнения, мне хорошо назначить указатель с массивом, например, ints что-то такое расплывчатое, как это int_ptr = 5,3,4,3; а значения 5,3,4,3 попадают в адрес памяти как «Hello». И если нет, то почему это возможно только со строками? в продвинутых.

"hello" – строковый литерал. Это безымянный немодифицируемый объект типа char [6] . Это массив, и он ведет себя так же, как и любой другой массив. Тот факт, что это безымянность, ничего не меняет. Вы можете использовать его с помощью оператора [] например, как в "hello"[3] и так далее. Как и любой другой массив, он может и будет распадаться на указатель в большинстве контекстов.

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

Аналогичная функциональность существует для других типов массивов посредством синтаксиса составного литерала

 int *p = (int []) { 1, 2, 3, 4, 5 }; 

В этом случае правая часть является безымянным объектом типа int [5] , который распадается на int * указатель. Компонентные литералы изменяются, но это означает, что вы можете сделать p[3] = 8 и, таким образом, заменить 4 на 8 .

Вы также можете использовать синтаксис сложного литерала с массивами char и делать

 char *p = (char []) { "hello" }; 

В этом случае правая часть является модифицируемым безымянным объектом типа char [6] .

Первое, что вам нужно сделать, это прочитать раздел 6 чата comp.lang.c.

Строковый литерал "hello" является выражением типа char[6] (5 символов для «hello» плюс один для завершающего '\0' ). Он ссылается на анонимный объект массива со статической продолжительностью хранения, инициализированный при запуске программы, чтобы содержать эти 6 символов.

В большинстве контекстов выражение типа массива неявно преобразуется в указатель на первый элемент массива; Исключение составляют:

  • Когда это аргумент sizeof ( sizeof "hello" дает 6, а не размер указателя);
  • Когда это аргумент _Alignof (новая функция в C11);
  • Когда это аргумент унарного & ( &arr дает адрес всего массива, а не его первого элемента, то же место памяти, другого типа); а также
  • Когда это строковый литерал в инициализаторе, используемом для инициализации объекта массива ( char s[6] = "hello"; копирует весь массив, а не только указатель).

Ни одно из этих исключений не относится к вашему коду:

 char *ptr; ptr = "hello"; 

Таким образом, выражение "hello" преобразуется в («decays» to) указатель на первый элемент ( 'h' ) этого анонимного объекта массива, о котором я упоминал выше.

Итак *ptr == 'h' , и вы можете продвигать ptr через память для доступа к другим символам: 'e' , 'l' , 'l' , 'o' и '\0' . Это то, что делает printf() , когда вы даете ему формат "%s" .

Этот анонимный объект массива, связанный с строковым литералом, доступен только для чтения, но не const . Это означает, что любая попытка изменить этот массив или любой из его элементов имеет неопределенное поведение (поскольку стандарт явно говорит об этом), но компилятор не обязательно предупреждает вас об этом. (C ++ делает строковые литералы const а то же самое в C нарушало бы существующий код, который был написан до того, как const был добавлен на язык.) Нет, вы не можете изменять элементы "hello" – или, по крайней мере, вы не следует пытаться. И чтобы компилятор предупредил вас, если вы попытаетесь, вы должны объявить указатель как const :

 const char *ptr; /* pointer to const char, not const pointer to char */ ptr = "hello"; 

(gcc имеет опцию -Wwrite-strings , что заставляет обрабатывать строковые литералы как const . Это заставит его предупредить о некотором C-коде, который является законным в отношении стандарта, но такой код, вероятно, должен быть изменен для использования const ).

 #include  main() { char * ptr; ptr = "hello"; //instead of above tow lines you can write char *ptr = "hello" printf("%p %s" ,"hello",ptr ); getchar(); } 

Здесь вы назначили строковый литерал "hello" на ptr это означает, что строковый литерал хранится в памяти только для чтения, поэтому вы не можете его изменить. Если вы объявите char ptr[] = "hello"; , то вы можете изменить массив.

Чего-чего?

Ваш код выделяет 6 байтов памяти и инициализирует его значениями «h», «e», «l», «l», «o» и «\ 0».

Затем он выделяет указатель (количество байтов для указателя зависит от реализации) и устанавливает значение указателя в начало упомянутых ранее 5 байтов.

Вы можете изменять значения массива с помощью синтаксиса, такого как ptr[1] = 'a' .

Синтаксически строки являются особым случаем. Поскольку C не имеет определенного типа строки, о котором можно говорить, он предлагает некоторые ярлыки для их объявления и т. Д. Но вы можете легко создать тот же тип структуры, что и для строки с использованием int , даже если синтаксис должен быть немного иным.