Разница между char * и int *

В чем разница между char* и int*? Конечно, они имеют разные типы, но как я могу писать

char* s1="hello world";

а

"hello world"

это не один символ, это массив символов, и я не могу написать

*s1

а

char* s1 = {'h','e','l','l','o',' ','w','o','r','l','d'};

и

int* a = {2,3,1,45,6};

В чем разница?

Ответ 1

Это довольно просто: строковый литерал, т.е. "foobar", скомпилирован в массив символов, который хранится в статическом разделе вашей программы (т.е. где хранятся все константы) и завершается нуль. Затем, присваивая это переменной, просто присваивает переменную переменную указателю на эту память. Например, const char* a = "foo"; назначит адрес, где "foo" хранится в a.

Короче говоря, строковая константа уже содержит память, в которой она должна храниться вместе с ней.

Напротив, инициализация указателя с помощью списка инициализаторов (т.е. списка элементов внутри фигурных скобок) не определена для указателей. Неформально проблема с списком инициализаторов - в отличие от строкового литерала - заключается в том, что он не "приносит свою собственную память". Поэтому мы должны предоставить память, в которой список инициализаторов может хранить свои символы. Это делается путем объявления массива вместо указателя. Это прекрасно компилируется:

char s1[11]={'h','e','l','l','o',' ','w','o','r','l','d'}

Теперь мы предоставили пространство, в котором символы должны быть сохранены, объявив s1 в качестве массива.

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

char* c2 = {nullptr};

Однако, хотя синтаксис кажется равным, это нечто совершенно другое, которое называется равномерной инициализацией и просто инициализирует c2 с помощью nullptr.

Ответ 2

В вашем первом случае строковый литерал распадается на указатель на const char. Хотя s1 действительно должен быть const char *, несколько компиляторов разрешают другую форму как расширение:

const char* s1 = "hello world" ;

Лист литерала является массивом const char, это видно из черновик проекта С++ 2.14.5 Строковые литералы, в котором говорится (акцент мой вперед):

Также указываются обычные строковые литералы и строковые литералы UTF-8 как узкие строковые литералы. Узкий строковый литерал имеет массив type n const char ",, где n - размер строки, как определено ниже, и имеет статическую продолжительность хранения (3.7).

Преобразование массива в указатель описано в разделе 4.2 Преобразование массива в указатель, в котором говорится:

[...] выражение, которое имеет тип '' массив типа, преобразуется в выражение с типом '' указатель на тип, указывающий на начальный элемент объекта массива и не является lvalue. [...]

Ваши другие случаи не работают, потому что скаляр, который может быть арифметическим типом, типами перечисления или типом указателя, может быть инициализирован только одним элементом внутри фигурных скобок, который описан в черновом стандартном разделе С++ 5.17 Операторы присваивания и составного присваивания 8.5.1 Пункт 3 инициализации списка, в котором говорится:

Список-инициализация объекта или ссылки типа T определяется как следующим образом:

а затем перечисляет различные случаи, единственное, что применимо к правой стороне для этого случая, - это следующая марка:

В противном случае, если в списке инициализаторов есть один элемент типа E и либо T не является ссылочным типом, либо его ссылочный тип ссылка, связанная с E, объект или ссылка инициализируются из этот элемент; если сужение конверсии (см. ниже) требуется для преобразуйте элемент в T, программа плохо сформирована.

который требует, чтобы список имел один элемент, в противном случае применяется конечная марка:

В противном случае программа плохо сформирована.

В двух случаях, даже если вы уменьшили инициализатор до одной переменной, типы неверны h является char и 2 является int, который не будет преобразован в указатель.

Назначение может быть выполнено для работы, назначив результаты массиву, например:

  char s1[] = { 'h', 'e', 'l', 'l', 'o',' ', 'w', 'o', 'r', 'l', 'd' } ;
  int  a[]  = { 2, 3, 1, 45, 6 } ;

Это будет описано в разделе 8.5.1 Агрегаты, в котором говорится:

Массив неизвестного размера, инициализированный скобкой список инициализаторов, содержащий n инициализатор-предложений, где n должно быть больше нуля, определяется как имеющее n элементов (8.3.4). [Пример:

int x[] = { 1, 3, 5 };

объявляет и инициализирует x как одномерный массив, который имеет три элементов, поскольку размер не указан, и есть три инициализатора. -end example] Пустой список инициализаций {} не должен использоваться как initializer-clause для массива неизвестной границы .104

Примечание:

Неверно сказать, что список указателей скобок не определен для указателей, он отлично подходит для указателей:

int x   = 10 ;
int *ip =  &x ;
int *a  = {nullptr} ;
int *b  = {ip} ;