В чем разница между char a [] =? String ?;и char * p =? string?;? - PullRequest
47 голосов
/ 27 февраля 2012

Как гласит заголовок, в чем разница между

char a[] = ?string?; and 
char *p = ?string?;  

Этот вопрос был задан мне в интервью. Я даже не понимаю утверждения.

char a[] = ?string?

Вот что такое ? оператор? Это часть строки или она имеет какое-то конкретное значение?

Ответы [ 8 ]

90 голосов
/ 09 марта 2012

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


Оба явно различны, для начала:

  1. Первый создает указатель.
  2. Второй создает массив.

Читайте дальше для более подробного объяснения:

Версия Array:

char a[] = "string";  

Создает массив, достаточно большой для размещения строкового литерала "string", включая его терминатор NULL. Массив string инициализируется строковым литералом "строка". Массив можно изменить позже . Кроме того, размер массива известен даже во время компиляции, поэтому для определения его размера можно использовать оператор sizeof.


Версия указателя:

char *p  = "string"; 

Создает указатель для указания строкового литерала "строка". Это быстрее, чем версия массива, , но строка, указанная указателем, не должна изменяться , потому что она находится в памяти, доступной только для чтения. Изменение такого строкового литерала приводит к неопределенному поведению .

На самом деле C ++ 03 не поддерживает [Ref 1] использование строкового литерала без ключевого слова const. Таким образом, декларация должна быть:

const char *p = "string";

Кроме того, необходимо использовать функцию strlen(), а не sizeof, чтобы найти размер строки , поскольку оператор sizeof просто даст вам размер переменной-указателя. 1049 *


Какая версия лучше, и какую мне использовать?

Зависит от использования.

  • Если вам не нужно вносить какие-либо изменения в строку, используйте версию указателя.
  • Если вы намереваетесь изменить данные, используйте версию массива.

Примечание: Это не C ++, но это специфично для C.

Обратите внимание, что использование строкового литерала без ключевого слова const вполне допустимо в C. Однако изменение строкового литерала все еще остается неопределенным поведением в C [Ref 2] .

Это поднимает интересный вопрос,
В чем разница между char * и const char * при использовании строковых литералов в C?


Для фанатов Standerdese:
[Ссылка 1] C ++ 03 Стандарт: § 4.2 / 2

Строковый литерал (2.13.4), который не является широким строковым литералом, может быть преобразован в значение типа «указатель на символ»; широкий строковый литерал может быть преобразован в значение типа «указатель на wchar_t». В любом случае результатом является указатель на первый элемент массива. Это преобразование рассматривается только при наличии явного соответствующего целевого типа указателя, а не при общей необходимости преобразования из lvalue в rvalue. [ Примечание: это преобразование устарело . См. Приложение D.] В целях ранжирования по разрешению перегрузки (13.3.3.1.1) это преобразование считается преобразованием массива в указатель, за которым следует преобразование квалификации (4.4). [Пример: «abc» преобразуется в «указатель на const char» как преобразование массива в указатель, а затем в «указатель на char» как преобразование квалификации. ]

C ++ 11 просто удаляет приведенную выше цитату, которая подразумевает, что это недопустимый код в C ++ 11.

[Ref 2] Стандарт C99 6.4.5 / 5 "Строковые литералы - семантика":

В переводеНа этапе 7 к каждой многобайтовой символьной последовательности добавляется байт или код нулевого значения, которые являются результатом строкового литерала или литералов.Последовательность многобайтовых символов затем используется для инициализации массива статической длительности хранения и длины, достаточной только для того, чтобы содержать последовательность.Для строковых литералов символов элементы массива имеют тип char и инициализируются отдельными байтами многобайтовой последовательности символов;для широких строковых литералов элементы массива имеют тип wchar_t и инициализируются последовательностью широких символов ...

Не определено, различаются ли эти массивы при условии, что их элементы имеют соответствующие значения. Если программа пытается изменить такой массив, поведение не определено.

84 голосов
/ 09 марта 2012

Первый - это массив, другой - указатель.

Объявление массива char a[6]; требует, чтобы было выделено пространство для шести символов, которое будет известно под именем a.То есть есть место с именем a, в котором могут сидеть шесть символов.Объявление указателя char *p;, с другой стороны, запрашивает место, которое содержит указатель.Указатель должен быть известен под именем p и может указывать на любой символ (или непрерывный массив символов) где угодно.

Операторы

 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

12 голосов
/ 27 февраля 2012
char a[] = "string";

Распределяет строку в стеке.

char *p = "string";

Создает указатель в стеке, который указывает на литерал в сегменте данных процесса.

? тот, кто написал это, не зная, что они делают.

7 голосов
/ 22 июня 2012

Стек, куча, сегмент данных (и 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' разрешена, потому что теперь мы пишем в куче.

6 голосов
/ 27 февраля 2012

char *p = "string"; создает указатель на постоянную память, в которой хранится строковый литерал "string".Попытка изменить строку, на которую указывает p, приводит к неопределенному поведению.

char a[] = "string"; создает массив и инициализирует его содержимое, используя строковый литерал "string".

3 голосов
/ 24 октября 2012

Они различаются относительно того, где хранится память. В идеале второй должен использовать 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
}
1 голос
/ 27 февраля 2012

a объявляет массив char значений - массив char s, который завершается.

p объявляет указатель, который ссылается на неизменяемую завершенную строку C, чье точное место хранения определяется реализацией.Обратите внимание, что это должно быть const -качественным (например, const char *p = "string";).

Если вы распечатаете его, используя std::cout << "a: " << sizeof(a) << "\np: " << sizeof(p) << std::endl;, вы увидите различия в их размерах (примечание: значения могут различаться в зависимости от системы):

a: 7
p: 8

Вот что?оператор?Это часть строки или она имеет какое-то конкретное значение?

char a[] = ?string?

Я предполагаю, что они когда-то были двойными кавычками "string", которые потенциально были преобразованы в "умные"кавычки ", то не могли быть представлены как таковые по пути, и были преобразованы в ?.

0 голосов
/ 19 сентября 2014

C и C ++ имеют очень похожие отношения Pointer to Array ...

Я не могу говорить о точных местах памяти двух операторов, о которых вы спрашиваете, но я нашел их статьи интересными и полезными для понимания некоторых различий между объявлением char Pointer и объявлением char Array.

Для наглядности:

C Отношение указатель и массив

C ++ Указатель на массив

Я думаю, важно помнить, что массив в C и C ++ является постоянным указателем на первый элемент массива. И, следовательно, вы можете выполнять арифметику указателей на массиве.

char * p = "строка"; <--- Это указатель, который указывает на первый адрес символьной строки. </p>

возможно также следующее:

char *p;
char a[] = "string";

p = a; 

В этот момент p теперь ссылается на первый адрес памяти a (адрес первого элемента)

и так * p == 's'

* (p ++) == 't' и так далее. (или * (p + 1) == 't')

и то же самое будет работать для a: * (a ++) или * (a + 1) также будет равно 't'

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...