Указатели и строки С++

Я учу себя С++, и я немного запутался в указателях (в частности, в следующем исходном коде). Но сначала я продолжаю показывать вам то, что знаю (а затем противопоставляя код этому, потому что чувствую, что происходят какие-то противоречия).

Что я знаю:

int Age = 30;
int* pointer = &Age;

cout << "The location of variable Age is: " << pointer << endl;
cout << "The value stored in this location is: " << *pointer << endl;

Указатели содержат адреса памяти. Используя оператор косвенности (разыменования) (*), вы можете получить доступ к тому, что хранится в ячейке памяти указателя. На код в этой книге у меня возникли проблемы с пониманием...

cout << "Enter your name: ";
string name;
getline(cin, name); //gets full line up to NULL terminating character

int CharsToAllocate = name.length() + 1; //calculates length of string input
                          //adds one onto it to adjust for NULL character
char* CopyOfName = new char[CharsToAllocate];
// pointer to char called CopyOfName, is given the memory address of the 
//beginning of a block
//of memory enough to fit CharsToAllocate. Why we added 1? Because char need a 
//NULL terminating character (\0)

strcpy(CopyOfName, name.c_str()); //copies the string name, into a pointer?

cout << "Dynamically allocated buffer contains: " << CopyOfName << endl;
delete[] CopyOfName; //always delete a pointer assigned by new to prevent memory leaks

Вывод:

Enter your name: Adam
Dynamically allocated buffer contains: Adam

Комментарии в приведенном выше коде являются моими комментариями. Моя проблема начинается с strcpy. Почему name.c_str() скопирован в указатель CopyOfName? Означает ли это, что все строки являются важными указателями? Так что   string testing = "Hello world"; На самом деле это указатель, указывающий на место памяти, где хранится "H"?

Затем, почему это делается в инструкции print, используя CopyOfName, а не *CopyOfName? Указатели содержат адреса памяти? Использование *CopyOfName распечатает содержимое ячейки памяти. Я пробовал это в Code:: Blocks, и если входной текст был "Hello World". Использование *CopyOfName в заявлении распечатки просто даст "H". Это имеет смысл, поскольку, когда я объявил, что мне нужен блок памяти с "новой" вещью, это фактически возвращает указатель на первую часть динамически выделенного блока памяти.

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

string testing = "Confused";
cout << testing << endl;

выведет слово "Confused"

Однако, если я попытаюсь скомпилировать

string testing = "Confused";
cout << *testing; 

Появляется сообщение об ошибке.

В принципе, чтобы обобщить мой вопрос, я пытаюсь понять код с помощью strcpy и оператора cout.

Ответ 1

Похоже, вы понимаете, что такое строки C-стиля, но суммировать, это всего лишь массивы символов в памяти, по соглашению, заканчивающимся символом nul \0. Обычно они ссылаются через char*, указывающие на первую букву в строке. Когда они печатаются, как правило, символы строки печатаются начиная с первого, и печать (или копирование и т.д.) Останавливается, когда достигается терминатор \0.

An std::string - это класс, который (обычно) обертывает строку стиля C. Это означает, что объект std::string (обычно) имеет частную строку стиля C, которая используется для реализации ее функциональности. Функция std::string::c_str() возвращает указатель на эту базовую строку C-стиля.

Предположим, что char *str; указывает на строку C-стиля. Если вы попытаетесь запустить cout << *str << endl;, вы заметили, что печатается только первый символ. Это из-за перегрузки функций С++. Тип данных *str равен char, поэтому вызывается char версия cout и верно печатает единственный символ *str. Для совместимости со строками типа C версия cout, которая принимает char* в качестве аргумента, рассматривает указатель как строку стиля C для целей печати. Если вы cout a int*, например, базовый int не будет напечатан.

Изменить: Еще один комментарий:

Причина, по которой ваша попытка разыменовать объект std::string не удалась, заключается в том, что, действительно, это не указатель. Вы можете разыменовать возвращаемое значение std::string::c_str(), и вы вернете первый char строки.

Связано: Как реализовано std::string?.

Ответ 2

В C строки - это просто массивы символов. И массивы распадаются на указатели при использовании в качестве аргумента функции.

В С++ std::string - это класс. Он включает в себя массив символов C-стиля внутри, и это то, что возвращает c_str(). Но сама строка не указатель, поэтому вы не можете ее разыгрывать; вы должны использовать метод c_str(), чтобы получить указатель на содержимое строки.

Ответ 3

Так как string testing = "Hello world"; На самом деле это указатель, указывающий на ячейку памяти, где хранится "H"?

Нет, над вами есть объект с именем string. Это верно для char* testing = "Hello World". Как вы можете видеть, он даже объявлен как указатель и указывает на первый символ в строке - H.

Далее, почему в инструкции print out CopyOfName нет *CopyOfName? Указатели содержат адреса памяти? Использование *CopyOfName распечатает содержимое ячейки памяти. Я пробовал это в блоках кода, и если входной текст был "Hello World". Использование *CopyOfName в инструкции распечатки просто даст "H"

cout принимает указатель на первый символ строки, поэтому CopyOfName прав. В этом случае он будет печатать каждый символ, начиная с H, пока не найдет \0 (нулевой символ). Строки типа "hello" имеют на самом деле 6 символов - "h" 'e' 'l' 'l' 'o' '\ 0' Когда вы пишете *CopyOfName, вы разыгрываете этот указатель, а *CopyOfName на самом деле является только одним символом

Ответ 4

Отвечая на ваши вопросы в порядке:

"Почему имя .c_str() скопировано в указатель CopyOfName? Означает ли это что все строки являются важными указателями? Так, например, тестирование строк =" Привет мир "; На самом деле это указатель, указывающий на расположение памяти где хранится" Н"?

Как указал Юй Хао в своем комментарии, важно понять разницу между строками стиля C++ и строками типа С. В первом случае вы имеете дело с "непрозрачным" объектом, тогда как в последнем случае вы в основном имеете дело с "массивом" символов.

С строковыми объектами С++ вы можете использовать метод c_str(), чтобы получить (указатель на) массив символов C-стиля. В C массив представлен с использованием указателя на начало массива, а затем ссылки достигаются путем подачи смещения (индекса в массив) от этого начального адреса. Таким образом, ответ на последний вопрос в этом пакете "да", указатель на строку C-стиля является указателем на первый символ "H".

"Далее, почему в заявлении для печати указано, что CopyOfName не является * CopyOfName? Указатели имеют адреса памяти?"

Поскольку оператор << перегружен для обработки C-строк. Реализация этого метода "знает, что делать с" указателем.

Ответ 5

Указатели не совпадают с массивами. Строковые литералы неизменяемы, и когда у вас есть указатель на строковый литерал, вы можете проверить его содержимое, но их изменение - это поведение undefined. При использовании этого синтаксиса:

char arr[] = "hi there";

Строковый литерал копируется в массив. Поскольку вы не указываете размер, компилятор автоматически выводит его. Терминатор NUL также автоматически добавляется. Если вы укажете размер, вы должны убедиться, что буфер может содержать терминатор NUL. Поэтому:

char arr[5] = "hello";

- ошибка. Если вы используете синтаксис инициализатора привязки:

char arr[5] = { "h", "e", "l", "l", "o" };

Это ошибка, потому что нет терминатора NUL. Если вы используете strcpy, для вас будет добавлен терминатор NUL.

std::string предоставляет два метода возврата указателя на его содержимое: data и c_str. Pre-С++ 11, единственное отличие - data не включает терминатор NUL. В С++ 11 теперь это делается, поэтому их поведение идентично. Поскольку указатель может быть легко признан недействительным, небезопасно манипулировать этими указателями. Также небезопасно делать char * ptr = str.c_str();, потому что время жизни массива, возвращаемого c_str, умирает в точке с запятой. Вам нужно скопировать его в буфер.

Ответ 6

Вы задаете правильные вопросы как ученик.

Ответы:

  • В С++, string - это объект, c_str() по существу возвращает указатель на первый символ строки (стиль C)
  • Вы правы в строках в C, переменная на самом деле указывает на первый символ строка
  • С++ делает много вещей, основанных на типе переменной. Когда вы передаете объект string cout печатает строку. Кроме того, С++ достаточно умен, чтобы определить, что *testing является незаконным

Ответ 7

Почему имя .c_str() скопировано в указатель CopyOfName?

"name" - это строка STL. Это объект, который отличается от c-строки. С-строка представляет собой набор памяти, который содержит символы и имеет нулевое завершение. Итак, если вы используете STL-строки и хотите превратить их в c-строки, вы используете .c_str() для получения c-строки.

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

cout имеет TON разных вещей, которые вы можете использовать с < <. Похоже, что он может принимать char * (которые являются c-строками) или строками STL. Не похоже, что он может принимать указатели на строки STL.

Я немного смутился, когда вы представили "тестирование", но я думаю, что вы путаетесь между c-строками (которые являются char *) и строками STL, которые являются объектами. Не чувствуй себя плохо или не сдавайся. Этот материал сложный и занимает некоторое время, чтобы добраться.

Я бы рекомендовал попробовать и понять разные термины "c-string", "char *", "stl string" и, возможно, "указатель на stl string".

Ответ 8

В C, где стандартные строки С++ не существовали, char * была так называемой "строкой". Как вы отметили, это массив символов, заканчивающийся символом NULL. Почти любая стандартная библиотечная функция, которая принимает строку C-стиля, примет указатель на указанную строку по двум причинам:

  • Легче думать о строке C-Style в целом, а не о наборе символов, в отличие от других массивов, поэтому с помощью указателя сохраняется эта идея
  • Это самый простой способ взять массив как параметр функции, чтобы просто получить указатель на первый элемент, особенно в случае C-строк, где их можно просто прочитать до символа NULL.

Ответ 9

Я думаю, что вы делаете, а другие, пожалуйста, поправьте меня, если я ошибаюсь, это то, что вы копируете свою строку в динамический массив char. Так что вы не копируете его в указатель. Причина, по которой используется указатель, заключается в том, что динамические массивы требуют указателей, чтобы правильно распределять их память, если я прав.