DWade64
DWade64

Reputation: 427

Pointers and Strings C++

I'm teaching myself C++ and I'm a bit confused about pointers (specifically in the following source code). But first, I proceed with showing you what I know (and then contrasting the code against this because I feel as if there are some contradictions going on).

What I know:

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;

Pointers hold memory addresses. Using the indirection (dereference) operator (the *), you can access what is stored in memory location of the pointer. Onto the code in this book I'm having trouble understanding...

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's 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's 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

Output:

Enter your name: Adam
Dynamically allocated buffer contains: Adam

The comments in the above code are my comments. My problem begins with strcpy. Why is name.c_str() copied into a pointer CopyOfName? Does this mean that all strings are essential pointers? So like string testing = "Hello world"; Is actually a pointer pointing to the memory location where "H" is stored?

Next, why is it in the print out statement using CopyOfName and not *CopyOfName? Pointers hold memory addresses? Using *CopyOfName would print out the contents of the memory location. I tried this in Code::Blocks and if the input text was "Hello World." Using *CopyOfName in the print out statement would just give an "H". This makes sense since when I declared that I needed a memory block with the 'new' thing, this actually returns a pointer to the first part of the dynamically allocated memory block.

The only way I can reconcile this is if a string is actually a pointer.

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

would print out the word "Confused"

However, if I try to compile

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

I get an error message.

Basically, to summarize my question, I'm trying to understand the code with strcpy and the cout statement.

Upvotes: 16

Views: 77445

Answers (9)

Brian Stinar
Brian Stinar

Reputation: 1109

Why is name.c_str() copied into a pointer CopyOfName?

"name" is an STL string. This is an object, which is different than a c-string. A c-string is a collection of memory, that holds characters, and has a null termination. So, if you're using STL strings and want to turn them into c-strings, you use .c_str() on it to get the c-string.

CopyOfName contains enough memory to hold name, since it was allocated to hold it.

cout has a TON of different things you can use with <<. It looks like it can take char *'s (which are c-strings) or STL strings. It does not look like it can take pointers to STL strings.

I got kind of confused when you introduced "testing" but I think that you are getting confused between c-strings (which are char *'s) and STL strings, which are objects. Don't feel bad, or give up. This stuff is tricky and takes a while to get.

I would reccomend to try and understand the different between the terms "c-string", "char *", "stl string" and maybe "pointer to stl string" too.

Upvotes: 2

Sudhee
Sudhee

Reputation: 714

You are asking the right questions as a learner.

Answers:

  1. In C++, string is an object, the c_str() essentially returns a pointer to the first character of the string (C Style)
  2. You are right about strings in C, the variable actually points to first character of the string
  3. C++ does lot of things based on the type of variable. When you pass a string object cout prints the string. Also, C++ is smart enough to determine *testing is illegal

Upvotes: 2

user1508519
user1508519

Reputation:

Pointers are not the same as arrays. String literals are immutable, and when you have a pointer to a string literal, you can inspect its contents, but modifying them are undefined behavior. When using this syntax:

char arr[] = "hi there";

The string literal is copied into the array. Because you do not specify a size, the compiler automatically deduces it. The NUL terminator is automatically added as well. If you specify a size, you must make sure that the buffer can hold the NUL terminator. Therefore:

char arr[5] = "hello";

is a mistake. If you use the brace initializer syntax:

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

This is a mistake because there is no NUL terminator. If you use strcpy, a NUL terminator will be added for you.

std::string provides two methods of returning a pointer to its underlying contents: data and c_str. Pre-C++11, the only difference is data does not include the NUL terminator. In C++11, it now does, so their behavior is identical. Because the pointer can easily be invalidated, is not safe to manipulate those pointers. It is also not safe to do char * ptr = str.c_str(); because the lifetime of the array returned by c_str dies at the semi-colon. You need to copy it into a buffer.

Upvotes: 5

Andrey Mishchenko
Andrey Mishchenko

Reputation: 4206

It seems like you understand what C-style strings are, but to summarize, they are just arrays of characters in memory, by convention terminated by a nul character \0. Usually they are referenced via a char* pointing to the first letter in the string. When they are printed, typically the characters of the string are printed starting from the first, and printing (or copying, etc.) stops when the \0 terminator is reached.

An std::string is a class that (typically) wraps a C-style string. This means that a std::string object (typically) has a private C-style string that is used to implement its functionality. The function std::string::c_str() returns a pointer to this underlying C-style string.

Let's suppose that char *str; points to a C-style string. If you attempt to run cout << *str << endl;, you noticed that only the first character is printed. That is because of C++'s function overloading. The data type of *str is char, so the char version of cout is called and faithfully prints the single character *str. For compatibility with C-style strings, the version of cout that takes a char* as an argument treats the pointer as a C-style string for printing purposes. If you cout an int*, for example, the underlying int will not be printed.

Edit: Another comment:

The reason that your attempt to dereference an std::string object failed is that, indeed, it is not a pointer. You could dereference the return value of std::string::c_str(), and you would get back the first char of the string.

Related: How is std::string implemented?.

Upvotes: 19

janepe
janepe

Reputation: 99

So like string testing = "Hello world"; Is actually a pointer pointing to the memory location where "H" is stored?

No, above you are have object called string. It would be true for char* testing = "Hello World". As you can see it is even declared as pointer and it points to first character in string - H.

Next, why is it in the print out statement that CopyOfName is not *CopyOfName? Pointers hold memory addresses? Using *CopyOfName would print out the contents of the memory location. I tried this in code blocks and if the input text was "Hello World." Using *CopyOfName in the print out statement would just give an "H"

cout take pointer to first character of the string so CopyOfName is right. In this case it will print every character starting from H until it finds \0 (null character). Strings like "hello" have actually 6 characters - 'h' 'e' 'l' 'l' 'o' '\0' When you write *CopyOfName you are dereferencing this pointer and *CopyOfName is actually only one character

Upvotes: 3

Turix
Turix

Reputation: 4490

Taking your questions in order:

"Why is name.c_str() copied into a pointer CopyOfName? Does this mean that all strings are essential pointers? So like string testing = "Hello world"; Is actually a pointer pointing to the memory location where "H" is stored?"

As Yu Hao pointed out in his/her comment, it is important to understand the difference between C++-style strings and C-style strings. In the first case, you are dealing with an "opaque" object, whereas in the latter case you are basically dealing with an "array" of characters.

With C++ string objects, you can use the c_str() method to get a (pointer to a) C-style array of characters. In C, an array is represented using the pointer to the beginning of the array and then references are achieved by supplying an offset (the index into the array) from that starting address. So the answer to the last question in this bundle is "yes", the pointer to the C-style string is the pointer to the first character, 'H'.

"Next, why is it in the print out statement that CopyOfName is not *CopyOfName? Pointers hold memory addresses?"

Because the operator << is overloaded to handle C-strings. The implementation of this method "knows what to do with" the pointer.

Upvotes: 3

jundl77
jundl77

Reputation: 476

I think what you are doing, and others please correct me if I'm wrong, is that you're copying your string into a dynamic char array. So no you are not copying it into a pointer. The reason a pointer is involved there is because dynamic arrays require pointers to allocate their memory correctly if I am right.

Upvotes: 1

IllusiveBrian
IllusiveBrian

Reputation: 3214

In C, where C++ standard strings didn't exist, char* was the so-called "string." As you noted, it is an array of characters ending in a NULL character. Just about any standard library function which takes a C-style string will take a pointer to said string, for two reasons:

  1. It's easier to think of a C-Style string as a whole rather than a bunch of characters, unlike other arrays, so taking the pointer keeps that idea
  2. It's the easiest way to take an array as a function parameter to just get a pointer to the first element, especially in the case of C-strings where they can just be read up to the NULL character.

Upvotes: 1

Barmar
Barmar

Reputation: 780818

In C, strings are simply arrays of characters. And arrays decay to pointers when used as the argument to a function.

In C++, std::string is a class. It includes a C-style character array within it, and this is what c_str() returns. But the string itself is not a pointer, so you can't dereference it; you have to use the c_str() method to get a pointer to the string contents.

Upvotes: 5

Related Questions