Сбивает с толку символ* в c

#c #pointers #char

Вопрос:

тестовый код:

 #include <iostream>
using namespace std;
int main()
{
    const char* b="str";
    cout << b << endl;
    cout << *b << endl;
    cout << amp;b << endl;
    cout << *(amp;b) << endl;
    
    
    return 0;
}
 

Результат:

 str
s
0x7ffdf39c27f0
str
 

Я запускаю свой код в веб-компиляторе runoob online

Почему я получаю эти результаты? Я посмотрел несколько вопросов о char*, но недостаточно, чтобы понять. Кто-нибудь может мне это объяснить? Фотографии лучше всего.

Я хочу узнать об этом больше с помощью рекомендованных книг или блогов.

Кстати, используя char b[] вместо const char* , я получаю те же результаты.


Большое спасибо за всех вас.

Я просто хочу знать, почему значение указателя символа не является адресом.

Я думаю, что адрес похож на 0x7ffdf39c27f0. адрес в памяти.

Но const char* b = "str" . б справедлив str .

И я обнаружил, что *b это то же *("str") самое, что и .

Итак, я хочу знать, что произошло в памяти? почему значение указателя символа не является адресом?

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

1. Что в этих результатах сбивает с толку? Вы печатаете строку с окончанием 0, первый символ этой строки, ее адрес, а затем снова строку.

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

3. Смущенный. Является b ли указатель или строка? Основываясь на определении, я бы подумал b , что вернет указатель.

Ответ №1:

Чтобы понять, что выводит код, необходимо понимать, что выходные потоки C (объекты с таким типом, как std::ostream ) и, следовательно, объекты (такие как std::cout ) имеют ряд перегрузок operator<<() . Вызываемая перегрузка зависит от типа предоставленного аргумента.

Я объясню ваш второй пример, но объяснение для первого примера почти идентично.

 const char* b="str";
cout << b << endl;
cout << *b << endl;
cout << amp;b << endl;
cout << *(amp;b) << endl;
 

cout << b расширяется до cout.operator<<(b) места, где b есть тип const char * . Эта перегрузка операторной функции ПРЕДПОЛАГАЕТ, что аргумент указывает на (первый символ) строку, заканчивающуюся нулем, которая представлена в памяти в виде массива char , заканчивающегося char значением '' (ноль). Функция оператора выводит каждый найденный символ, пока он не достигнет определенного '' символа. Первый '' найденный-это тот , который ВЫ явно вставили после 't' , поэтому вывод st будет получен. Тот факт, что в вашей строке есть секунда '' после 'r' , не имеет значения, так как функция оператора останавливается на первой, которую она находит.

cout << *b расширяется до вызова другой перегрузки operator<<() , которая принимает один char и выводит его char . *b является значением первого символа в строке, представленной b . Таким s образом, получается результат.

В cout << amp;b , amp;b имеет тип const char ** или (эквивалентно) char const ** . Нет перегрузки выходного потока operator<<() , который принимает a const char ** , но есть перегрузка, которая принимает a const void * . Поскольку любой указатель (кроме указателя на элемент или указателей на функции) может быть неявно преобразован void * , это преобразование выполняется (компилятором), поэтому перегрузка соответствует и вызывается. Эта конкретная перегрузка operator<<() печатает адрес в памяти.

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

В последнем утверждении *(amp;b) это эквивалентно b . Это происходит потому amp; , что оператор адреса в этом коде и оператор * разыменования (который является обратным оператору адреса). Таким образом, последнее утверждение выдает тот же результат, cout << b что и .

Ответ №2:

 cout << b << endl;
 

Вы печатаете строку b

 cout << *b << endl;
 

Вы печатаете указатель, указывающий на первый символ b ., так что это то же самое, что:

 cout << b[0] << endl;
 
 cout << amp;b << endl;
 

amp;b является адресом памяти b , что означает адресную память для хранения b в компьютере.

 cout << amp;b << endl;
 

Итак, вы печатаете адрес памяти b здесь. Компьютер хранит b адрес в памяти 0x7ffdf39c27f0 , так что вот что вы получаете.

 cout << *(amp;b) << endl;
 

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

 cout << b << endl;
 

изменить: Указатель содержит адрес, который (обычно, он может указывать на функцию, например) представляет местоположение объекта, и для печати указателя (обычно) выводится значение этого адреса памяти. Поскольку char * он тесно связан со строками, заканчивающимися нулем, существует специальная перегрузка для указателей на символы для печати указанной строки.

Переменная указателя по-прежнему является переменной и будет иметь собственный адрес, поэтому amp;b в результате получается указатель на указатель, char ** в данном случае a , и поскольку он больше не является a char * , cout << amp;b; выводится адрес b , а не адрес, на который указывает b или строка, на которую указывает b .

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

1. Часто важно, чтобы было ясно, что b это строка с нулевым завершением и не имеет ни одного из колоколов и свистков a std::string . amp;b является ли адрес памяти b , который означает адресную память для хранения b в компьютере. на 100% правильный, но не думайте, что это будет понятно кому-то, кто придет холодным и не осознает разницу между указателем и указываемым на нас. Может быть, еще одна фраза, чтобы забить лошадь до смерти.

2. Намного лучше, но не возражаешь, если я слегка подправлю?

3. @user4581301 Конечно! Определенно, можешь.

4. Потребовалось немного больше, чтобы завершить все это, чем я ожидал. Не стесняйтесь возвращаться, если я забрел слишком далеко от ваших намерений.