Страницы

Поиск по вопросам

среда, 18 декабря 2019 г.

Указатель на тип char

#cpp #массивы #строки #указатели


Я как-то давно спрашивал по поводу указателей, разобрался, всем ответившим спасибо.
Но сейчас мне пришлось столкнутся с типом char:

int *a;
char *b;

a = new int(10);
b = "bla bla bla"; // Странно ведь мы записываем в адрес, а не в разыменованный указатель

cout << "a: " << *a << endl; // Выведет значение, а без * адрес (логично и понятно)
cout << "b: " << b << endl; // Выведет значение всего того что мы засунули в "адрес"
указателя, почему так, если приводить аналогию с int то почему тут не как там?


Хотелось бы понять, как работает указатель на тип char (хотелось бы увидеть аргументированные
ответы, почему так различается, и что-то от себя как рекомендации). Шерстил интернет
— по теме указателей в C++ ничего не нашел

P. S.: Прошу прощения, если эта тема всех достала или мое сообщение является дубликатом
(есть темы похожие, но, вроде, это не то, что мне надо).
    


Ответы

Ответ 1



Давайте рассмотрим предложения из вашего примера шаг за шагом. В этом предложении int *a; вы объявили переменную a как указатель на объект с типом int В этом предложении char *b; вы объявили переменную b как указатель на объект с типом char В этом предложении a = new int(10); был создан объект в динамической памяти типа int, который был инициализирован значением 10. В этом предложении b = "bla bla bla"; в левой части выражения с оператором присваивания стоит переменна с типом char *. В правой части этого выражения используется строковый литерал "bla bla bla". Строковые литералы в C++ имеют типы константных символьных массивов. (C++ Стандарт, 2.14.5 String literals) 8 Ordinary string literals and UTF-8 string literals are also referred to as narrow string literals. A narrow string literal has type “array of n const char”, where n is the size of the string as defined below, and has static storage duration (3.7). Строковый литерал "bla bla bla" имеет тип const char [12]. В выражениях массивы неявно преобразуются к указателям на свой первый элемент. Согласно стандарту C++ (4.2 Array-to-pointer conversion) 1 An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The result is a pointer to the first element of the array. Таким образом в приведенном предложении в правой части выражения с оператором присваивания используется значение типа const char *, которое указывает на первый символ строкового литерала "bla bla bla". То есть имеет место попытка указателю типа char * присвоить значение указателя типа const char *. Так как не существует неявного преобразования из типа указателя на константный объект в тип указателя на неконстантный объект, то компилятор должен выдать диагностическое сообщение. Правильно было бы объявить переменную b следующим образом const char *b; Тогда вышеприведенное предложение с присваиванием было бы корректно, и переменной b был бы присвоен адрес первого символа строкового литерала, стоящего в правой части выражения от знака присваивания. Данное предложение b = "bla bla bla"; фактически, эквивалентно следующему предложению b = &"bla bla bla"[0]; В этом предложении cout << "a: " << *a << endl; целочисленное значение, адресуемое указателем a выводится на консоль. Было бы более корректно записать cout << "*a: " << *a << endl; ^^^ В этом предложении cout << "b: " << b << endl; при условии, что компилятор каким-то образом сгенерировал объектный модуль, несмотря на ошибку, о которой я сказал выше, выведет на консоль строковый литерал, так как для указателей типа char * оператор operator << перегружен таким образом, что вместо значения самого указателя, он выводит содержимое памяти, адресуемое этим указателем, как строку. Если вы хотите вывести на консоль именно значение самого указателя, то вам в таком случае следует написать либо cout << "b: " << ( void * )b << endl; либо cout << "b: " << static_cast( b ) << endl; А если переменная b была объявлена с квалификатором const, то cout << "b: " << ( const void * )b << endl; либо cout << "b: " << static_cast( b ) << endl;

Ответ 2



Вы столкнулись со специальным поведением строковых литералов в C и C++. Дело в том, что строковой литерал "bla bla bla" есть константа типа const char* const char [12] [спасибо @Pavel Mayorov за уточнение] (12 получается из 11 символов и дополнительного нуля в конце), и в вашем контексте преобразуется к указателю на первый символ этой самой строки/массива, расположенной компилятором где-то в программе. То есть, тип выражения "bla bla bla" есть const char [12], и неявно преобразуется в const char*. Далее, cout имеет специальное поведение для указателей на символ: он воспринимает их как строки, а не как «обыкновенные» указатели, и выводит все символы за первым аж до финального \0. Это специальное поведение в C++ введено для совместимости с языком C, в котором нету «настоящих» строк. Вообще, char и char* в C++ во многом обладают особыми свойствами, дополнительными к указателям на другие типы. Кстати говоря, странно, что ваш код скомпилировался без диагностики: типы const char* и char* не должны так просто присваиваться друг другу.

Ответ 3



Когда язык C только появлялся, указатели на символ использовались для работы с так называемыми нуль-терминальными строками - и по традиции используются до сих пор. Поэтому в языках C и C++ указатель на символ - это обычно строка. И библиотечные функции, работающие с char*, знают, что они работают со строкой. Посмотрите на первую часть ваших выражений вывода на консоль: cout << "a: " cout << "b: " "a: " и "b: " здесь тоже являются указателями на char. Но если бы вместо этих строк выводились их адреса, вы бы сказали, что это странно, не так ли? Тогда почему точно то же самое, но с использованием промежуточной переменной, удивляет?

Ответ 4



b = "bla bla bla"; /// Странно ведь мы записываем в адрес, а не в разименованный указатель "bla bla bla" не записывается, это константа, b - получает только адрес первого символа этого массива. cout << "b: " << b << endl; /// Выведет значение всего того что мы засунули в "адрес" указателя, почему так, если приводить анологию с int то почему тут не как там? Это связано не с указателем на char, а с объектом cout. Так как char* часто представляет строки, то в cout определено для него именно такое поведение.

Комментариев нет:

Отправить комментарий