В чем разница между char a [] =? String?; и char * p =? string?;?
Как говорится в заголовке: В чем разница между
char a[] = ?string?; and
char *p = ?string?;
Этот вопрос был задан мне в интервью.
Я даже не понимаю этого утверждения.
char a[] = ?string?
Вот что такое оператор ?
? Является ли это частью строки или имеет определенный смысл?
Ответы
Ответ 1
Первый - это массив, другой - указатель.
Объявление массива char a[6];
требует, чтобы пространство для шести символов было отложено, чтобы быть известным под именем a
. То есть, есть место с именем a
, в котором могут сидеть шесть символов. Объявление указателя char *p;
, с другой стороны, запрашивает место, в котором содержится указатель. Указатель должен быть известен под именем p
и может указывать на любой char (или непрерывный массив символов) где угодно.
Утверждения
char a[] = "string";
char *p = "string";
приведет к созданию структур данных, которые могут быть представлены следующим образом:
+---+---+---+---+---+---+----+
a: | s | t | r | i | n | g | \0 |
+---+---+---+---+---+---+----+
+-----+ +---+---+---+---+---+---+---+
p: | *======> | s | t | r | i | n | g |\0 |
+-----+ +---+---+---+---+---+---+---+
Важно понимать, что ссылка типа x[3]
генерирует другой код в зависимости от того, является ли x
массивом или указателем. Учитывая вышеприведенные объявления, когда компилятор видит выражение a[3]
, он испускает код для начала в местоположении a
, перемещает три элемента мимо него и выбирает там символ. Когда он видит выражение p[3]
, он испускает код для начала в месте p
, извлекает там значение указателя, добавляет три элемента к указателю и, наконец, извлекает символ, на который указывает. В приведенном выше примере обе a[3]
и p[3]
оказываются символом l
, но компилятор попадает туда по-другому.
Источник: comp.lang.c Часто задаваемые вопросы · Вопрос 6.2
Ответ 2
?
кажется опечаткой, это не семантически допустимо. Таким образом, ответ предполагает, что ?
является опечаткой и объясняет, что, возможно, интервьюер действительно хотел спросить.
Оба они совершенно разные, для начала:
- Первый создает указатель.
- Второй создает массив.
Читайте дальше для более подробного объяснения:
Версия массива:
char a[] = "string";
Создает массив, достаточно большой, чтобы содержать строковый литерал "string" , включая его терминатор NULL
. Массив string
инициализируется строковым литералом "string" . Массив может быть изменен позднее. Кроме того, размер массива известен даже во время компиляции, поэтому для определения его размера можно использовать оператор sizeof
.
Версия указателя:
char *p = "string";
Создает указатель, указывающий на строковый литерал "string" . Это быстрее, чем версия массива, , но строка, указанная указателем, не должна быть изменена, поскольку она находится в памяти, определенной только для чтения. Изменение такого строкового литерала приводит к Undefined Поведение.
Фактически С++ 03 осуждает [Ref 1] использование строкового литерала без ключевого слова const
. Таким образом, декларация должна быть:
const char *p = "string";
Кроме того, вы должны использовать функцию strlen()
, а не sizeof
, чтобы найти размер строки, поскольку оператор sizeof
просто даст вам размер переменной указателя.
Какая версия лучше и какой из них я должен использовать?
Зависит от использования.
- Если вам не нужно вносить какие-либо изменения в строку, используйте версию указателя.
- Если вы намерены изменить данные, используйте версию массива.
Примечание: Это не С++, но это C специфично.Суб >
Обратите внимание, что использование строкового литерала без ключевого слова const
отлично действует в C.
Однако изменение строкового литерала по-прежнему является Undefined Поведение в C [Ссылка 2].
Это вызывает интересный вопрос,
В чем разница между char * и const char * при использовании со строковыми литералами в C?
Для поклонников Standerdese:
[Ref 1] С++ 03 Стандарт: §4.2/2
Строковый литерал (2.13.4), который не является широким строковым литералом, может быть преобразован в rvalue типа "указатель на char"; широкий строковый литерал может быть преобразован в rvalue типа "указатель на wchar_t". В любом случае результатом является указатель на первый элемент массива. Это преобразование рассматривается только тогда, когда существует явный соответствующий целевой тип указателя, а не когда требуется общая конвертация из lvalue в rvalue. [ Примечание. Это преобразование устарело. См. Приложение D.] Для целей ранжирования в разрешении перегрузки (13.3.3.1.1) это преобразование считается преобразованием от массива к указателю с последующим квалификационным преобразованием (4.4). [Пример: "abc" преобразуется в "указатель на const char" как преобразование от массива к указателю, а затем к "указателю на char" в качестве преобразования квалификации. ]
С++ 11 просто удаляет приведенную выше цитату, которая подразумевает, что это незаконный код в С++ 11.
[Ref 2] C99 standard 6.4.5/5 "Строковые литералы - семантика":
В фазу 7 перевода байта или код нулевого значения добавляется к каждой многобайтовой последовательности символов, которая получается из строкового литерала или литералов. Последовательность многобайтовых символов затем используется для инициализации массива статической продолжительности хранения и длины, достаточной для того, чтобы содержать последовательность. Для символьных строковых литералов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов; для широких строковых литералов элементы массива имеют тип wchar_t и инициализируются с помощью последовательности широких символов...
Не указано, являются ли эти массивы различными, если их элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение undefined.
Ответ 3
char a[] = "string";
Это выделяет строку в стеке.
char *p = "string";
Это создает указатель на стек, который указывает на литерал в сегменте данных процесса.
?
тот, кто написал это, не зная, что они делают.
Ответ 4
Стек, куча, данные (и BSS) и текстовое разделение - это четыре сегмента памяти процесса. Все локальные переменные определены в стеке. Динамически распределенная память с использованием malloc
и calloc
будет находиться в куче. Все глобальные и статические переменные будут находиться в сегменте данных. Текстовый сегмент будет иметь ассемблерный код программы и некоторые константы.
В этих 4 сегментах сегмент текста является сегментом READ ONLY
, а во всех остальных трех для READ
и WRITE
.
char a[] = "string";
- этот statemnt будет выделять память для 7 байтов в стеке (потому что локальная переменная), и в конце будет содержать все 6 символов (s, t, r, i, n, g
) плюс символ NULL (\0
).
char *p = "string";
- этот оператор будет выделять память в 4 байта (если это 32-разрядная машина) в стеке (потому что это также локальная переменная) и будет содержать указатель строки константы, значение которой "string"
, Этот 6 байт постоянной строки будет в текстовом сегменте. Это постоянное значение. Переменная указателя p
просто указывает на эту строку.
Теперь a[0]
(индекс может быть от 0 до 5) означает, что он получит доступ к первому символу этой строки, которая находится в стеке. Поэтому мы можем писать и на этой должности. a[0] = 'x'
. Эта операция разрешена, потому что у нас есть READ WRITE
доступ в стеке.
Но p[0] = 'x'
приведет к сбою, потому что у нас есть только READ
доступ к текстовому сегрегации. Ошибка сегментации произойдет, если мы сделаем запись в текстовом сегменте.
Но вы можете изменить значение переменной p
, так как его локальная переменная в стеке. как ниже
char *p = "string";
printf("%s", p);
p = "start";
printf("%s", p);
Это разрешено. Здесь мы меняем адрес, хранящийся в переменной-указателе p
, на адрес строки start
(снова start
также является данными только для чтения в текстовом сегменте). Если вы хотите изменить значения, присутствующие в *p
, это означает, что вы хотите использовать динамически выделенную память.
char *p = NULL;
p = malloc(sizeof(char)*7);
strcpy(p, "string");
Теперь операция p[0] = 'x'
разрешена, потому что теперь мы пишем в кучу.
Ответ 5
char *p = "string";
создает указатель на постоянную память, где хранится строковый литерал "string"
. Попытка изменить строку, которая указывает p
на поведение undefined.
char a[] = "string";
создает массив и инициализирует его содержимое с помощью строкового литерала "string"
.
Ответ 6
Они отличаются тем, где хранится память. В идеале второй должен использовать const char *.
Первый
char buf[] = "hello";
создает автоматический буфер, достаточно большой, чтобы удерживать символы и копировать их (включая нулевой терминатор).
Второй
const char * buf = "hello";
должен использовать const и просто создает указатель, указывающий на память, обычно хранящуюся в статическом пространстве, где это незаконно изменять.
Обратное (из того, что вы можете изменить первое безопасно, а не второе) состоит в том, что безопасно возвращать второй указатель из функции, но не первый. Это связано с тем, что второй останется действительным указателем памяти вне области действия функции, первая не будет.
const char * sayHello()
{
const char * buf = "hello";
return buf; // valid
}
const char * sayHelloBroken()
{
char buf[] = "hello";
return buf; // invalid
}
Ответ 7
a
объявляет массив значений char
- массив char
, который завершается.
p
объявляет указатель, который ссылается на неизменяемую, завершенную строку C, точное место хранения которой определяется реализацией. Обратите внимание, что это должно быть const
-qualified (например, const char *p = "string";
).
Если вы распечатаете его с помощью std::cout << "a: " << sizeof(a) << "\np: " << sizeof(p) << std::endl;
, вы увидите различия в их размерах (обратите внимание: значения могут отличаться в зависимости от системы):
a: 7
p: 8
Вот что такое? оператор? Является ли это частью строки или имеет определенный смысл?
char a[] = ?string?
Я предполагаю, что они были когда-то двойными кавычками "string"
, которые потенциально были преобразованы в "умные кавычки", затем не могли быть представлены как таковые на этом пути и были преобразованы в ?
.
Ответ 8
C и С++ имеют очень похожие указатели на отношения Array...
Я не могу говорить о точках памяти двух утверждений, о которых вы спрашиваете, но я нашел их статьи интересными и полезными для понимания некоторых различий между объявлением указателя char и массивом char декларация.
Для наглядности:
C Отношения указателя и массива
С++ Указатель на массив
Я думаю, что важно помнить, что массив в C и С++ является константным указателем для первого элемента массива. И, следовательно, вы можете выполнить арифметику указателя на массиве.
char * p = "string"; < --- Это указатель, указывающий на первый адрес символьной строки.
возможно также следующее:
char *p;
char a[] = "string";
p = a;
В этот момент p теперь ссылается на первый адрес памяти (адрес первого элемента)
и поэтому * p == 's'
* (p ++) == 't' и т.д. (или * (p + 1) == 't')
и то же самое будет работать для a: * (a ++) или * (a + 1) также будет равно 't'
Ответ 9
char [] arr = "hello";
char * p = "hello";
Теперь мы можем сделать arr [2] = 'x';
Но p [2] = 'c' генерирует ошибку времени выполнения:)