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

Они по существу одинаковы, но using обеспечивает alias templates, что весьма полезно. Один хороший пример, который я мог найти, следующий:

namespace std {
 template<typename T> using add_const_t = typename add_const<T>::type;
}

Итак, мы можем использовать std::add_const_t<T> вместо typename std::add_const<T>::type

488
задан StoryTeller 22 December 2017 в 09:04
поделиться

8 ответов

Разница в том, что

char *s = "Hello world";

поместит «Hello world» в части памяти, доступные только для чтения , и сделает ] s указатель, который делает любую операцию записи в эту память незаконной.

При выполнении:

char s[] = "Hello world";

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

s[0] = 'J';

становится законным.

533
ответ дан 22 November 2019 в 22:39
поделиться

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

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

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

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.

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

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

- DBJ

-1
ответ дан 22 November 2019 в 22:39
поделиться

В случае:

char *x = "fred";

x - это lvalue - ему можно присвоить. Но в случае:

char x[] = "fred";

x не является l-значением, это r-значение - вы не можете присвоить ему значение.

0
ответ дан 22 November 2019 в 22:39
поделиться
char s[] = "hello";

объявляет s как массив char , который достаточно длинный для хранения инициализатора (5 + 1 char s ) и инициализирует массив, копируя элементы данного строкового литерала в массив.

char *s = "hello";

объявляет s как указатель на один или несколько (в данном случае более) char s и указывает его прямо на фиксированное (доступное только для чтения) место, содержащее литерал "hello" .

15
ответ дан 22 November 2019 в 22:39
поделиться

Учитывая объявления

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 , чтобы она указывала на другой массив.

59
ответ дан 22 November 2019 в 22:39
поделиться

Это объявление:

char s[] = "hello";

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

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

char *s ="hello";

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

  • доступный только для чтения массив из 6 символов , содержащий значения 'h', 'e', ​​'l', 'l', 'o', '\ 0' , который не имеет имени и имеет статический срок хранения (что означает, что он живет в течение всего срока службы программы); и
  • переменная типа «указатель на символ», называемая s , которая инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения.

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

\ 0 ', который не имеет имени и имеет статический срок хранения (что означает, что он живет в течение всего срока жизни программы); и
  • переменная типа «указатель на символ», называемая s , которая инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения.
  • Безымянный доступ только для чтения. Обычно массив располагается в «текстовом» сегменте программы, что означает, что он загружается с диска в постоянную память вместе с самим кодом. Расположение переменной-указателя s в памяти зависит от того, где появляется объявление (как и в первом примере).

    \ 0 ', который не имеет имени и имеет статический срок хранения (что означает, что он живет в течение всего срока жизни программы); и
  • переменная типа «указатель на символ», называемая s , которая инициализируется местоположением первого символа в этом безымянном массиве, доступном только для чтения.
  • Безымянный доступ только для чтения. Обычно массив располагается в «текстовом» сегменте программы, что означает, что он загружается с диска в постоянную память вместе с самим кодом. Расположение переменной-указателя s в памяти зависит от того, где появляется объявление (как и в первом примере).

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

    Безымянный массив, доступный только для чтения, обычно располагается в "текстовом" сегменте программы, Это означает, что он загружается с диска в постоянную память вместе с самим кодом. Местоположение переменной-указателя s в памяти зависит от того, где появляется объявление (как и в первом примере).

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

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

    70
    ответ дан 22 November 2019 в 22:39
    поделиться

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

    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 () или аналогичный. Если массив размещен в глобальной области видимости, конечно, нет проблем.

    147
    ответ дан 22 November 2019 в 22:39
    поделиться
    char s[] = "Hello world";
    

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

    char *s = "hello";
    

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

    4
    ответ дан 22 November 2019 в 22:39
    поделиться
    Другие вопросы по тегам:

    Похожие вопросы: