#cpp
Я написал такой код:
char * str = "abcdefg";
cout << str << " " << &str << " " << (void *) str;
Выводит следующее:
abcdefg 0x22feac 0x488000
Я сейчас читаю книгу "Язык программирования C++ (Прата С. 6-е издание)" и там написано
примерно следующее: чтобы вывести адрес строки с помощью cout нужно применить такой
синтаксис: cout << (void *) str;
Объясните пожалуйста почему &str и (void *) str выводят разные адреса и что вообще
значит этот синтаксис: (void *) str?
Ответы
Ответ 1
Я совсем далёк от C++ нынче, но, если мне память не изменяет, то... (void *) str - адрес, где строка лежит. &str - это адрес указателя, тоже самое, что char **. То есть, это не одно и то же. Ну а (void *) это просто каст без привязки к типу. Если бы у вас был int *, то вы точно так же бы могли сделать каст (void *), чтоб получать адрес. Зачем это делается? Ну, скажем, вместо создания различных методов для int, char: void myMethodForChar(char * arg){ ...} void myMethodForInt(int * arg){ ...} Вы можете создать один метод: void myMethodForChar(void * arg){ ...} И передавать туда, хоть указатель на int, хоть на char, предварительно преобразовав в void *. Пускай знающие люди поправят, если неправ (:Ответ 2
Во-первых, строковый литерал должен быть присвоен типу const char* (или хотя бы char[], если нужна модификация строки в дальнейшем), т.к. его нельзя изменить без возможности нарваться на UB. То, что компилятор позволяет написать без const оставлено для совместимости с Си-кодом. В C++ это будет неверным: warning: ISO C++11 does not allow conversion from string literal to 'char *' [-Wwritable-strings] Далее, operator<< (сдвиг влево) перегружен для потоковых (stream) классов в качестве операции вывода в поток. Перегрузка реализована для различных типов данных: целых, вещественных, строк, адресов ... Для пользовательского класса можно реализовать свою перегрузку. Какая из перегрузок operator<< будет вызвана зависит от типа правостороннего операнда. Для обычных указателей выводится адрес (т.е. значение, которое хранит указатель), но для строковых типов (а char* по соглашению является именно Си-строкой, а не просто указателем) работает перегрузка, которая выводит именно Си-строку, расположенную по переданному адресу. Т.о. чтобы явно вывести адрес строки (адрес ее первого символа), а не саму строку, нужно для operator<< преобразовать правосторонний операнд к типу обычного указателя. void* при этом является обобщенным указателем, т.е. может легально указывать на любые типы данных. И преобразование вида (void *) str делает свое дело. Следующий момент в том, что преобразование такого вида (T) v является преобразованием в Си-стиле, что по сути сводится к цепочке других преобразований. Для C++ варианта преобразование строки к обобщенному указателю должно быть написано в иной форме: static_cast(str). Теперь к самим аргументам. str является указателем и содержит адрес строки, а значит &str это адрес, по которому расположен сам указатель str (ведь указатель тоже должен где-то храниться), содержащий адрес строки. Т.о. указатели имеют разные адреса, т.к. указывают на разные сущности. Итоговый код будет выглядеть следующим образом: #include int main() { const char * str = "abcdefg"; std::cout << str << " " << &str << " " << static_cast (str); } Вывод: abcdefg 0x7fff74013938 0x400915 Из которого, кстати, хорошо видно, что адрес указателя (&str) и адрес строки указывают в совершенно разные области памяти. Указатель расположен на стеке, а строка в защищенной для записи области памяти (скорее всего). Ответ 3
(void *) - Это си-стайл приведения типов (C-style cast). Данной операцией ты str приводишь к void * собственно потому и вывод отличается от вывода &str.Ответ 4
cout << str; выведет строку, на которую указывает str, a cout << (void *) str; выведет значение самого указателя str (адрес).
Комментариев нет:
Отправить комментарий