Задумывались, в чем разница между объявлением переменной с помощью [] или *? На мой взгляд:
char *str = new char[100];
char str2[] = "Hi world!";
.. должно быть основным отличием, хотя я не уверен, что вы можете сделать что-то вроде
char *str = "Hi all";
.. поскольку указатель должен ссылаться на статический член, я не знаю, может ли он?
В любом случае, что меня действительно беспокоит, так это то, что я знаю разницу между:
void upperCaseString(char *_str) {};
void upperCaseString(char _str[]) {};
Итак, был бы очень признателен, если бы кто-нибудь мог сказать мне разницу? У меня есть подозрение, что оба могут быть скомпилированы одинаково, за исключением некоторых особых случаев?
Ty





Первый вариант динамически выделяет 100 байт.
Второй вариант статически выделяет 10 байтов (9 для строки + нулевой символ).
Ваш третий пример не должен работать - вы пытаетесь статически заполнить динамический элемент.
Что касается вопроса upperCaseString(), после того, как C-строка была выделена и определена, вы можете перебирать ее либо путем индексации массива, либо с помощью обозначения указателя, потому что массив на самом деле является просто удобным способом обернуть арифметику указателя в C.
(Это простой ответ - я ожидаю, что кто-то другой получит авторитетный и сложный ответ вне спецификации :))
Думаю, это зависит от вашего компилятора ... или от установленного вами уровня ошибок: в прошлый раз, когда я сделал №3, он выдал множество предупреждений и несколько ошибок.
# 3 предупреждает в GCC с помощью -Wwrite-strings, но это не входит ни в -Wall, ни в -Wextra. literal -> non-const char *, я думаю, устарел, поэтому предупреждение является разумным.
У меня довольно высокий уровень ошибок / предупреждений, так что это может быть поведение, которое, как я вижу, не по умолчанию :)
Три разных объявления позволяют указателю указывать на разные сегменты памяти:
char* str = new char[100];
позволяет str указывать на кучу.
char str2[] = "Hi world!";
кладет строку в стек.
char* str3 = "Hi world!";
указывает на сегмент данных.
Две декларации
void upperCaseString(char *_str) {};
void upperCaseString(char _str[]) {};
равны, компилятор жалуется на функцию, уже имеющую тело, когда вы пытаетесь объявить их в той же области.
Давайте посмотрим на это (обратите внимание, что char const и const char одинаковы в C++):
"hello" - это массив из 6 константных символов: char const[6]. Как и любой массив, он может неявно преобразовываться в указатель на свой первый элемент: char const * s = "hello"; Для совместимости с кодом C C++ допускает еще одно преобразование, которое в противном случае было бы некорректным: char * s = "hello"; удаляет константу !. Это исключение, позволяющее компилировать этот C-образный код, но не рекомендуется указывать 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. Но обратите внимание, если вы хотите передать ему строковый литерал (скажем, он не меняет свой аргумент), тогда вы должны изменить параметр на char const* _str, чтобы не делать устаревшие вещи.
Знаете, я никогда не тратил время на то, чтобы понять сложность объявления массива [] и указателя. Я всегда просто использовал массивы или указатели с явной длиной. Спасибо!
В дополнение к уже полученным ответам вам следует прочитать C FAQ относительно массивов и указателей. Да, это FAQ по C, а не FAQ по C++, но в этой области между двумя языками нет существенной разницы.
Также, в качестве примечания, избегайте именования переменных с подчеркиванием в начале. Это зарезервировано для символов, определенных компилятором и стандартной библиотекой.
Это не правда. Символы с начальным подчеркиванием, за которым следует заглавная буква, зарезервированы для реализации. Подчеркивание с последующим нижним регистром - это нормально, по крайней мере, в C++. Я не проверял стандарт C, но он резервирует всевозможные вещи, включая имена функций, начинающиеся с s или w IIRC.
Хорошо, чтобы прояснить это правило, похоже, что ведущие подчеркивания, за которыми следует заглавная буква, всегда зарезервированы, а ведущие подчеркивания, за которыми следует строчная буква, зарезервированы только в std :: и в глобальном пространстве имен, поэтому ведущее подчеркивание в член на самом деле в порядке.
"зарезервировано только в std :: and ::". Я не совсем понимаю это, поскольку вы все равно не можете добавлять символы в std :: (то есть все пространство имен зарезервировано для реализации).
Хорошо, я оставил два отрицательных комментария. Это не очень полезно; Я их удалил.
char *str = new char[100];
Этот блок можно освободить с помощью delete [].
char [] str2 = "Hi world!";
Этот массив можно без проблем модифицировать, что приятно. Так
str2[0] = 'N';
cout << str2;
должен выводить Ni world! на стандартный вывод, заставляя некоторых рыцарей чувствовать себя очень неуютно.
char *str = "Hi all";
str[0] = 'N'; // ERROR!
void upperCaseString(char *_str) {};
void upperCaseString(char [] _str) {};
выглядите так же, как мне, и в вашем случае (вы хотите ввести строку в верхний регистр на месте) это действительно не имеет значения.
Однако все это вызывает вопрос: почему вы используете char * для выражения строк на C++?
Пожалуйста, также взгляните на http://c-faq.com/aryptr/aryptr2.html. C-FAQ может оказаться интересным для чтения сам по себе.
Третий пример работает, он будет указывать на постоянную память, содержащую строку. Однако если вы попытаетесь его изменить, у вас возникнут проблемы.