это - const char*




В чем разница между char s[] и char*s? (8)

В C можно использовать строковый литерал в следующем объявлении:

char s[] = "hello";

или вот так:

char *s = "hello";

Так в чем же разница? Я хочу знать, что на самом деле происходит с точки зрения продолжительности хранения, как при компиляции, так и во время выполнения.


В качестве дополнения учтите, что для целей только для чтения использование обоих одинаково, вы можете получить доступ к символу путем индексирования с помощью формата [] или *(<var> + <index>) :

printf("%c", x[1]);     //Prints r

А также:

printf("%c", *(x + 1)); //Prints r

Очевидно, что если вы попытаетесь сделать

*(x + 1) = 'a';

Вероятно, вы получите ошибку сегментации, поскольку вы пытаетесь получить доступ к памяти только для чтения.


В свете комментариев здесь должно быть очевидно, что: char * s = "hello"; Это плохая идея, и ее следует использовать в очень узком пространстве.

Это может быть хорошей возможностью указать на то, что «const correctness» - это «хорошая вещь». Когда бы и где бы вы ни находились, используйте ключевое слово «const» для защиты своего кода, от «расслабленных» абонентов или программистов, которые обычно наиболее «расслаблены», когда указатели вступают в игру.

Достаточно мелодрамы, вот что можно достичь, когда украшающие указатели с «const». (Примечание: каждый должен читать декларации указателей справа налево.) Вот три разных способа защитить себя при игре с указателями:

const DBJ* p means "p points to a DBJ that is const" 

- то есть объект DBJ не может быть изменен с помощью p.

DBJ* const p means "p is a const pointer to a DBJ" 

- то есть вы можете изменить объект DBJ через p, но вы не можете изменить указатель p.

const DBJ* const p means "p is a const pointer to a const DBJ" 

- то есть вы не можете изменить указатель p самостоятельно, и вы не можете изменить объект DBJ через p.

Ошибки, связанные с попытками мутаций const-ant, попадают во время компиляции. Для состязания нет времени выполнения или скорости.

(Предположительно, вы используете компилятор C ++, конечно?)

--DBJ


Во-первых, в аргументах функций они в точности эквивалентны:

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

В других контекстах char * выделяет указатель, а char [] выделяет массив. Где строка идет в первом случае, спросите вы? Компилятор тайно выделяет статический анонимный массив для хранения строкового литерала. Так:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

Обратите внимание, что вы никогда не должны пытаться модифицировать содержимое этого анонимного массива с помощью этого указателя; эффекты не определены (часто это означает сбой):

x[1] = 'O'; // BAD. DON'T DO THIS.

Использование синтаксиса массива напрямую выделяет его в новую память. Таким образом, модификация безопасна:

char x[] = "Foo";
x[1] = 'O'; // No problem.

Однако массив работает только до тех пор, пока его область охвата, поэтому, если вы делаете это в функции, не возвращайте и не strdup() указатель на этот массив - вместо этого сделайте копию с помощью strdup() или аналогичного. Разумеется, если массив выделен в глобальной области, проблем нет.


Просто добавьте: вы также получите разные значения для своих размеров.

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

Как упоминалось выше, для массива '\0' будет выделен конечный элемент.


Учитывая декларации

char *s0 = "hello world";
char s1[] = "hello world";

предположим следующую гипотетическую карту памяти:

                    0x01  0x02  0x03  0x04
        0x00008000: 'h'   'e'   'l'   'l'
        0x00008004: 'o'   ' '   'w'   'o'
        0x00008008: 'r'   'l'   'd'   0x00
        ...
s0:     0x00010000: 0x00  0x00  0x80  0x00
s1:     0x00010004: 'h'   'e'   'l'   'l'
        0x00010008: 'o'   ' '   'w'   'o'
        0x0001000C: 'r'   'l'   'd'   0x00

Строковый литерал "hello world" представляет собой 12-элементный массив char ( const char на C ++) со статической продолжительностью хранения, что означает, что память для него выделяется при запуске программы и остается выделенной до тех пор, пока программа не завершится. Попытка изменить содержимое строкового литерала вызывает неопределенное поведение.

Линия

char *s0 = "hello world";

определяет s0 как указатель на char с длительностью автоматического хранения (что означает, что переменная s0 существует только для области, в которой она объявлена) и копирует адрес строкового литерала ( 0x00008000 в этом примере). Заметим, что поскольку s0 указывает на строковый литерал, он не должен использоваться в качестве аргумента для любой функции, которая попытается ее модифицировать (например, strtok() , strcat() , strcpy() и т. Д.).

Линия

char s1[] = "hello world";

определяет s1 как 12-элементный массив char (длина берется из строкового литерала) с длительностью автоматического хранения и копирует содержимое литерала в массив. Как видно из карты памяти, у нас есть две копии строки "hello world" ; разница в том, что вы можете изменить строку, содержащуюся в s1 .

s0 и s1 взаимозаменяемы в большинстве контекстов; вот исключения:

sizeof s0 == sizeof (char*)
sizeof s1 == 12

type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char

Вы можете переназначить переменную s0 чтобы указать на другой строковый литерал или на другую переменную. Вы не можете переназначить переменную s1 чтобы указать на другой массив.


Это заявление:

char s[] = "hello";

Создает один объект - массив char размером 6, называемый s , инициализированный значениями 'h', 'e', 'l', 'l', 'o', '\0' . Если этот массив выделен в памяти и как долго он живет, зависит от того, где появляется объявление. Если декларация находится в пределах функции, она будет жить до конца блока, в котором она объявлена, и почти наверняка будет выделена в стеке; если он находится вне функции, он, вероятно, будет храниться в «инициализированном сегменте данных», который загружается из исполняемого файла в записываемую память при запуске программы.

С другой стороны, это заявление:

char *s ="hello";

Создает два объекта:

  • массив, доступный только для чтения, из 6 char s, содержащий значения 'h', 'e', 'l', 'l', 'o', '\0' живет на всю жизнь программы); а также
  • переменная типа pointer-to-char, называемая s , которая инициализируется с расположением первого символа в этом неназванном, доступном только для чтения массиве.

Неименованный доступный только для чтения массив обычно находится в сегменте «текст» программы, что означает, что он загружается с диска в постоянную память вместе с самим кодом. Расположение переменной указателя s в памяти зависит от того, где появляется объявление (как в первом примере).


char *str = "Hello";

Вышеупомянутое устанавливает str для указания на буквальное значение «Hello», которое жестко закодировано в двоичном изображении программы, которое помечено как доступное только для чтения в памяти, означает, что любое изменение в этом строковом литерале является незаконным, и это приведет к возникновению ошибок сегментации.

char str[] = "Hello";

копирует строку в новую выделенную память в стеке. Таким образом, любые изменения в нем разрешены и законны.

means str[0] = 'M';

изменит str на «Mello».

Для получения более подробной информации, пожалуйста, перейдите по аналогичному вопросу:

Почему возникает ошибка сегментации при записи в строку, инициализированную символом «char * s», но не «char s []»?


char s[] = "Hello world";

Здесь s - массив символов, который мы можем переписать, если хотим.

char *s = "hello";

Строковый литерал используется для создания этих символьных блоков где-то в памяти, на которую указывает этот указатель s . Мы можем здесь переназначить объект, на который он указывает, изменив это, но пока он указывает на строковый литерал, блок символов, на который он указывает, не может быть изменен.





constants