C ++ строки: [] против * - PullRequest
       44

C ++ строки: [] против *

18 голосов
/ 21 ноября 2008

Задумывались, в чем разница между объявлением переменной с помощью [] или *? Как я это вижу:

char *str = new char[100];
char str2[] = "Hi world!";

.. должно быть главное отличие, хотя я не уверен, что вы можете сделать что-то вроде

char *str = "Hi all";

.. поскольку указатель должен ссылаться на статический член, который я не знаю, может ли он?

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

void upperCaseString(char *_str) {};
void upperCaseString(char _str[]) {};

Так, было бы очень признательно, если бы кто-нибудь мог сказать мне разницу? У меня есть догадка, что оба могут быть скомпилированы одинаково, за исключением некоторых особых случаев?

Ty

Ответы [ 6 ]

41 голосов
/ 21 ноября 2008

Давайте разберемся с ним (обратите внимание, что char const и const char одинаковы в C ++):

Строковые литералы и символы *

"hello" - это массив из 6 константных символов: char const[6]. Как и любой массив, он может неявно преобразовывать указатель на свой первый элемент: char const * s = "hello"; Для совместимости с кодом C, C ++ допускает еще одно преобразование, которое в противном случае было бы некорректным: char * s = "hello"; удаляет const !. Это исключение, позволяющее компилировать этот C-ish-код, но не рекомендуется указывать char * на строковый литерал. Итак, что мы имеем для char * s = "foo";?

"foo" -> array-to-pointer -> char const* -> qualification-conversion -> char *. Строковый литерал доступен только для чтения и не будет размещен в стеке. Вы можете свободно указать на них указатель и вернуть его из функции, без сбоя :).

Инициализация массива с использованием строкового литерала

Теперь, что такое char s[] = "hello";? Это целое другое дело. Это создаст массив символов и заполнит его строкой "hello". Буква не указана. Вместо этого он копируется в массив символов. И массив создается в стеке . Вы не можете корректно вернуть указатель на него из функции.

Типы параметров массива.

Как вы можете заставить вашу функцию принимать массив в качестве параметра? Вы просто объявляете свой параметр как массив:

void accept_array(char foo[]); 

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

void accept_array(char * foo);

Экскурсия: многомерные массивы

Замените char любым типом, включая сами массивы:

void accept_array(char foo[][10]);

принимает двумерный массив, последнее измерение которого имеет размер 10. Первый элемент многомерного массива - это его первый подмассив следующего измерения ! Теперь давайте изменим это. Это будет указатель на его первый элемент снова. Таким образом, фактически он примет указатель на массив из 10 символов: (удалите [] в голове, а затем просто сделайте указатель на тип, который вы видите в своей голове):

void accept_array(char (*foo)[10]);

Поскольку массивы неявно преобразуются в указатель на их первый элемент, вы можете просто передать в него двумерный массив (последний размер измерения которого равен 10), и он будет работать. Действительно, это относится к любому n-мерному массиву, включая особый случай n = 1;

Заключение

void upperCaseString(char *_str) {}; 

и

void upperCaseString(char _str[]) {};

такие же, как первый - просто указатель на символ. Но учтите, что если вы хотите передать ему строковый литерал (скажем, он не меняет свой аргумент), то вы должны изменить параметр на char const* _str, чтобы не делать устаревшие вещи.

12 голосов
/ 21 ноября 2008

Три разных объявления позволяют указателю указывать на разные сегменты памяти:

char* str = new char[100];

позволяет str указывать на кучу.

char str2[] = "Hi world!";

помещает строку в стек.

char* str3 = "Hi world!";

указывает на сегмент данных.

Две декларации

void upperCaseString(char *_str) {};
void upperCaseString(char _str[]) {};

равны, компилятор жалуется на функцию, уже имеющую тело, когда вы пытаетесь объявить их в той же области.

2 голосов
/ 21 ноября 2008

Хорошо, я оставил два отрицательных комментария. Это не очень полезно; Я удалил их.

  • Следующий код инициализирует указатель на символ, указывающий на начало динамически выделяемой части памяти (в куче).

char *str = new char[100];

Этот блок может быть освобожден с помощью delete [].

  • Следующий код создает массив символов в стеке, инициализированный значением, указанным строковым литералом.

char [] str2 = "Hi world!";

Этот массив может быть изменен без проблем, что приятно. Так


str2[0] = 'N';
cout << str2;

должен печатать Ni world! на стандартный вывод, что заставляет некоторых рыцарей чувствовать себя очень некомфортно.

  • Следующий код создает указатель на символ в стеке, указывающий на строковый литерал ... Указатель может быть переназначен без проблем, но указанный блок не может быть изменен (это неопределенное поведение; segfaults под Linux, например.)

char *str = "Hi all";
str[0] = 'N'; // ERROR!
  • следующие две декларации

void upperCaseString(char *_str) {};
void upperCaseString(char [] _str) {};

выглядят одинаково для меня , и в вашем случае (вы хотите прописать строку на месте) это действительно не имеет значения.

Однако все это вызывает вопрос: почему вы используете char * для выражения строк в C ++?

0 голосов
/ 21 ноября 2008

Пожалуйста, взгляните также на http://c -faq.com / aryptr / aryptr2.html C-FAQ может показаться интересным сам по себе.

0 голосов
/ 21 ноября 2008

В качестве дополнения к уже даным ответам вы должны прочитать FAQ C относительно массивов и указателей . Да, это часто задаваемые вопросы по C, а не по C ++, но между этими двумя языками в этой области нет существенной разницы.

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

0 голосов
/ 21 ноября 2008

Первый параметр динамически распределяет 100 байтов.

Второй вариант статически выделяет 10 байтов (9 для строки + нулевой символ).

Ваш третий пример не должен работать - вы пытаетесь статически заполнить динамический элемент.

Что касается вопроса upperCaseString(), то после того, как C-строка была выделена и определена, вы можете перебирать ее либо с помощью индексации массива, либо с помощью записи указателя, поскольку массив на самом деле является просто удобным способом переноса арифметики указателя в C.


(Это простой ответ - я ожидаю, что кто-то другой получит авторитетный, сложный ответ из спецификации:))

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