Это займет некоторое время.
Давайте начнем с основ. В C string является последовательностью символьных значений, включая терминатор с 0 значениями. IOW, строка "hello"
представляется как последовательность {'h', 'e', 'l', 'l', 'o', 0}
. Строки хранятся в массивах char
(или wchar_t
для "широких" строк, о которых мы не будем здесь говорить). Сюда входят строковые литералы , такие как "Bla"
- они хранятся в массивах char
, так что они доступны в течение всего жизненного цикла программы.
В большинстве случаев выражение типа "массив N-элементов из T
" будет преобразовано ("распад") в выражение типа "указатель на T
", поэтому большинство времени, когда мы имеем дело со строками, мы на самом деле имеем дело с выражениями типа char *
. Однако это не означает, что выражение типа char *
представляет собой строку - char *
может указывать на первый символ строки или на первый символ в последовательности, которая не является строкой (без терминатора) или может указывать на один символ, который не является частью большой последовательности.
A char *
также может указывать на начало динамически распределяемого буфера , который был выделен malloc
, calloc
или realloc
.
Следует также отметить, что оператор индекса []
определен в терминах арифметики указателей - выражение a[i]
определено как *(a + i)
- с учетом значения адреса a
(преобразованного из типа массива, как описано выше), смещение i
элементов ( не байтов ) от этого адреса и разыменование результата.
Еще одна важная вещь, которую следует отметить, это то, что =
не определен для копирования содержимого одного массива в другой. Фактически, выражение массива не может быть целью оператора =
.
Тип CS50 string
на самом деле является typedef
(псевдоним) для типа char *
. Функция get_string()
выполняет много магии за кулисами, чтобы динамически распределять и управлять памятью для содержимого строки, и делает обработку строки в C на гораздо более высоком уровне, чем на самом деле. Я и несколько других людей считаем, что это плохой способ учить С, по крайней мере, в отношении струн. Не поймите меня неправильно, это чрезвычайно полезная утилита, просто если у вас нет cs50.h и вам нужно начать выполнять свою собственную обработку строк, вы окажетесь в море какое-то время.
Итак, что все эти глупости связаны с вашим кодом? В частности, линия
s = "Bla";
Происходит следующее: вместо копирования содержимого строкового литерала "Bla"
в память, на которую указывает s
, адрес строкового литерала записывается в s
, перезаписывая предыдущее значение указателя. Вы не можете использовать оператор =
для копирования содержимого одной строки в другую; вместо этого вам придется использовать библиотечную функцию, такую как strcpy
:
strcpy( s, "Bla" );
Причина, по которой s[0] = A
работал так, как вы ожидали, заключается в том, что оператор индекса []
определен в терминах арифметики указателей. Выражение a[i]
оценивается как *(a + i)
- с учетом адреса a
(либо указатель, либо выражение массива, которое "распалось" на указатель, как описано выше), смещение i
элементов ( не байт! ) с этого адреса и разыменовывает результат. Таким образом, s[0]
указывает на первый элемент строки, которую вы прочитали.