В чем разница между 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' генерирует ошибку времени выполнения:)