Я пытаюсь понять указатели в C, но я в настоящее время путаю со следующим:
char *p = "hello"
Это указатель на символ, указывающий на массив символов, начиная с h .
char p[] = "hello"
Это массив, который хранит привет .
Какая разница, когда я передаю обе эти переменные в эту функцию?
void printSomething(char *p)
{
printf("p: %s",p);
}
char p[3] = "hello";
строка инициализатора слишком длинна для размера объявленного вами массива. Опечатка?char p[]="hello";
будет достаточно!char
конкретным.Ответы:
char*
иchar[]
это разные типы , но это не сразу видно во всех случаях. Это связано с тем, что массивы распадаются на указатели , что означает, что если выражение типаchar[]
предоставлено там, гдеchar*
ожидается один из типов , компилятор автоматически преобразует массив в указатель на его первый элемент.Ваша примерная функция
printSomething
ожидает указатель, поэтому, если вы попытаетесь передать ей массив следующим образом:Компилятор делает вид, что вы написали это:
источник
printf
обрабатывается%s
строка формата: начинайте с указанного адреса и продолжайте до тех пор, пока не встретите нулевой терминатор. Если вы хотите напечатать только один символ, вы можете использовать%c
, например, строку формата.char *p = "abc";
символ NULL,\0
как в случае с массивом char []?char *name; name="123";
но могу сделать то же самое сint
типом? И после использования%c
для печатиname
, вывод нечитаемой строка:�
?Посмотрим:
foo * и foo [] - это разные типы, и они обрабатываются компилятором по-разному (указатель = адрес + представление типа указателя, массив = указатель + необязательная длина массива, если он известен, например, если массив размещен статически ), подробности можно найти в стандарте. И на уровне времени выполнения нет никакой разницы между ними (в ассемблере, ну почти, см. Ниже).
Кроме того, есть связанный вопрос в C FAQ :
источник
Тяга C99 N1256
Существует два различных варианта использования строковых литералов символов:
Инициализировать
char[]
:Это «больше волшебства», и описано в 6.7.8 / 14 «Инициализация»:
Так что это просто ярлык для:
Как и любой другой обычный массив,
c
может быть изменен.Везде: генерирует:
Поэтому, когда вы пишете:
Это похоже на:
Обратите внимание на неявное приведение от
char[]
кchar *
, которое всегда допустимо.Затем, если вы измените
c[0]
, вы также измените__unnamed
, что является UB.Это описано в 6.4.5 «Строковые литералы»:
6.7.8 / 32 «Инициализация» приводит прямой пример:
GCC 4.8 x86-64 реализация ELF
Программа:
Компилировать и декомпилировать:
Выход содержит:
Вывод: GCC хранит
char*
его в.rodata
разделе, а не в.text
.Если мы сделаем то же самое для
char[]
:мы получаем:
поэтому он сохраняется в стеке (относительно
%rbp
).Однако обратите внимание, что скрипт компоновщика по умолчанию помещает
.rodata
и.text
в тот же сегмент, который имеет исполняемый файл, но не имеет разрешения на запись. Это можно наблюдать с:который содержит:
источник
Вы не можете изменять содержимое строковой константы, на что
p
указывает первая . Второйp
- это массив, инициализированный строковой константой, и вы можете изменить его содержимое.источник
Для подобных случаев эффект тот же: вы в конечном итоге передаете адрес первого символа в строке символов.
Заявления, очевидно, не то же самое, хотя.
Далее выделяется память для строки, а также указатель символа, а затем инициализируется указатель, указывающий на первый символ в строке.
В то время как следующее выделяет память только для строки. Так что на самом деле он может использовать меньше памяти.
источник
Насколько я помню, массив - это фактически группа указателей. Например
это верное утверждение
источник
*(arr + 1)
сводит вас ко второму членуarr
. Если*(arr)
указывает на 32-битный адрес памяти, напримерbfbcdf5e
, тогда*(arr + 1)
указывает наbfbcdf60
(второй байт). Следовательно, почему выход из области видимости массива приведет к странным результатам, если ОС не перестанет работать. Если онint a = 24;
находится по адресуbfbcdf62
, то доступarr[2]
может вернуться24
, при условии, что вначале не произошла ошибка.От APUE , раздел 5.14:
Цитируемый текст соответствует объяснениям @Ciro Santilli.
источник
char p[3] = "hello"
? следуетchar p[6] = "hello"
помнить, что в конце строки в C. есть символ '\ 0'в любом случае, массив в C - это просто указатель на первый объект настраиваемых объектов в памяти. единственные различия в семантике. в то время как вы можете изменить значение указателя, чтобы оно указывало на другое место в памяти, массив после создания всегда будет указывать на одно и то же место.
также при использовании массива «new» и «delete» автоматически выполняются за вас.
источник