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

Я знаю одно: при объявлении массива скажите:

int arr[2] {3, 5};

arr будет содержать значение первого элемента в массиве, поэтому попытка распечатать это (cout << arr) дает, очевидно, адрес arr[0]. Даже если подумать, что моя программа использует указатели, она все еще похожа.

У меня вопрос: почему я могу напечатать h и получить на выходе bonjour, но не могу сделать то же самое с p?

Также похоже, что когда я увеличиваю h++ и снова печатаю, я получаю onjour. Чем указатели отличаются от char?

#include <iostream>
#include <string>

int main()
{
    char* h = "bonjour";
    int k[4]{3, 4, 5, 6};
    int * p = k;

    std::cout << "Hello, "<< h << "!\n";
}
4
Bobby 27 Окт 2015 в 22:57

4 ответа

Лучший ответ

При потоковой передаче h вы используете эту перегрузку:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
                                        const char* s );

Но когда вы транслируете p, вы используете этот:

basic_ostream& operator<<( const void* value );

Первый будет печатать каждый байт, пока не дойдет до \0, второй просто напечатает адрес. То есть - есть просто особый случай для const char*, которым вы пользуетесь.

9
Barry 27 Окт 2015 в 20:01

Я думаю, что это больше всего связано с operator << для char* и int*. Программисты на C привыкли распечатывать строки с завершающим нулем, поэтому был создан метод для их печати. int s могут быть напечатаны напрямую. char не сможет распечатать всю строку.

0
Ziezi 27 Окт 2015 в 20:08

char* может быть особенным при некоторых обстоятельствах, потому что C решил использовать его для представления строк. В этом случае char* относится к массиву символов с завершающим нулем, также известному как C-строка. Причина, по которой вы можете печатать h, но не p, заключается в том, что существует функция, которая выполняет специальную обработку для char*:

 std::ostream::operator<<(std::ostream& os, const char*);

Вы можете предоставить свою собственную перегрузку для печати целочисленных массивов с завершающим нулем:

std::ostream& operator<<(std::ostream& os, const int* arr) {
   while (int i = *arr++) {
      os << i;
      if (*arr) os << ", ";
   }
   return os;
}

int main()
{
  const int arr[] = {1, 2 ,3, 4, 0};
  std::cout << arr << "\n";
}

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

1
Florian 27 Окт 2015 в 20:41

почему я могу напечатать h и получить bonjour в качестве вывода, а я не могу сделать то же самое с p? также похоже, что когда я увеличиваю h++ и снова печатаю, я получаю onjour. чем указатели отличаются от char?

Стандартная библиотека предоставляет две перегрузки функции operator<< для работы с указателями.

Перегрузка функции-члена:

basic_ostream& operator<<( const void* value );

И перегрузка функции, не являющейся членом:

template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,  
                                        const char* s );

Первый вызывается, когда cout << ptr используется для всех указателей, которые не относятся к типу char* или char const*. Второй используется для всех указателей типа char* или char const*. Второй выводит все, вплоть до завершающих нулевых символов, а первый выводит только значение указателя.

Это объясняет результат.

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

Язык и стандартные библиотеки делают исключения для строк с завершающим нулем, которые представлены char* и char const*. При работе со строками нулевой символ ('\0') действует как контрольное значение, которое отмечает конец строки. Для других типов такого значения нет. Следовательно, int* нельзя рассматривать так же, как char*.

2
R Sahu 28 Окт 2015 в 15:23