Why must a pointer to a char array need strcpy to assign characters to its array and double quotes assignment will not work?
Asked Answered
J

9

10

The first example does not work when you go to delete the pointer. The program either hangs when I add the null terminator or without it I get:

Debug Assertion Failed Expression: _BLOCK_TYPE_IS_VALID(pHead->nBlockUse) from Visual Studio 2008

//Won't work when deleting pointer:
    char *at = new char [3];
    at = "tw"; //   <-- not sure what's going on here that strcpy does differently
    at[2] = '\0'; // <-- causes program to hang
    delete at;

//Works fine when deleting pointer:
    char *at = new char [3];
    strcpy(at,"t");
    at[1] = 'w';
    at[2] = '\0';
    delete at;

So what's going on when I use double quotes instead of strcpy? Both of them will cout the string perfectly and debugger does not show anything different.

Jellaba answered 16/10, 2009 at 12:29 Comment(3)
I assume you meant delete[]Barter
You've answered your own question. strcpy() assigns characters to the array. = assigns a new array. The debugger does indeed show something different. The value of 'at' changes in one case and not in the other.Downhill
FYI, doing at = "tw"; and then at[2] = '\0'; is redundant. "tw" creates a string literal that is already null-terminated. The memory for the string "tw" looks like [ 't' | 'w' | '\0' ]. Not only that, but at[2] = '\0'; will also result in undefined behavior; "tw" creates a string literal, which is a read-only string which is unwritable, so writing to this read-only string literal will invoke undefined behavior. To actually assign something this way, you'd have to do const char *at = "tw"; which will create a string literal and have at point to the same location.Huguenot
P
12

Because a char* isn't a string. It's just a pointer to some character, with the convention that there might be more characters to follow and that after the last one there is a '\0'.

A string literal in C (and thus in C++) like "abc" is just an array of characters, with the compiler silently adding a '\0'. When you assign an array to a pointer, the array silently converts a pointer to the first element. The result is that

at = "tw";

means, the pointer at is assigned the address of the first character in the string literal "tw". By this, it will lose its old value. Since this was the address of a dynamically allocated character array, you are leaking this array.

When you later assign to a character in the array at now points to, you are assigning a new value to some character in the string literal. That's invoking undefined behavior and the program hanging or crashing immediately is probably the best that could happen to you when you do this. (On many platforms you're writing to read-only memory doing so.)

Later you pass at to delete[] (and not delete, since you called new[], not new). In doing so, you pass it the address of the string literal, instead of the allocated character array. This will, of course, mess up the heap manager. (VC's runtime library catches this in Debug mode.)

std::strcpy, on the other hand, copies a string character by character from one array to another array. No pointers will be changed, only pieces of memory are copied. The pointer to the target array still points to the target array afterwards, only the data in that array has changed.

Let me add this: As a beginner in C++, you should use std::string, rather than C strings. That does all the dirty work for you and has sane semantics.

Patellate answered 16/10, 2009 at 12:46 Comment(0)
C
14

When you do

char *at = ...;

at = "hello";

You're basically overwriting the pointer value (i.e., the address of the memory allocated for you by new[]) with the address of a static constant string. This means that when you later delete that memory, you're passing delete a pointer not previously returned by new.

That is a bad thing to be doing.

In C and C++, assignments to pointers typically don't do anything to the memory being pointed at, they change the pointer itself. This might be confusing if you're used to a language where strings are more of "first class citizens".

Also, you should use delete[] if you used new[].

Conduct answered 16/10, 2009 at 12:34 Comment(2)
So would I be correct in assuming strcpy(var,"string") loops through each individual character in "string" and assigns it to the correct index in var?Jellaba
@Omar: Yes, strcpy() will write one character at a time, up to and including the terminating NIL character.Conduct
P
12

Because a char* isn't a string. It's just a pointer to some character, with the convention that there might be more characters to follow and that after the last one there is a '\0'.

A string literal in C (and thus in C++) like "abc" is just an array of characters, with the compiler silently adding a '\0'. When you assign an array to a pointer, the array silently converts a pointer to the first element. The result is that

at = "tw";

means, the pointer at is assigned the address of the first character in the string literal "tw". By this, it will lose its old value. Since this was the address of a dynamically allocated character array, you are leaking this array.

When you later assign to a character in the array at now points to, you are assigning a new value to some character in the string literal. That's invoking undefined behavior and the program hanging or crashing immediately is probably the best that could happen to you when you do this. (On many platforms you're writing to read-only memory doing so.)

Later you pass at to delete[] (and not delete, since you called new[], not new). In doing so, you pass it the address of the string literal, instead of the allocated character array. This will, of course, mess up the heap manager. (VC's runtime library catches this in Debug mode.)

std::strcpy, on the other hand, copies a string character by character from one array to another array. No pointers will be changed, only pieces of memory are copied. The pointer to the target array still points to the target array afterwards, only the data in that array has changed.

Let me add this: As a beginner in C++, you should use std::string, rather than C strings. That does all the dirty work for you and has sane semantics.

Patellate answered 16/10, 2009 at 12:46 Comment(0)
U
9

There are 3 things to understand:

1) char *at; is just a pointer variable.
A pointer variable simply means that it holds a memory address.

2) new char[3] returns the starting address of the memory allocated on the heap.

3) "hello" returns the address of the string literal.

char *at = new char [3];
//at now contains the address of the memory allocated on the heap


at = "hello";
//at now contains the address of the static string. 
// (and by the way you just created a 3 byte memory leak)


delete[] at; 
//WOOPS!!!! you can't do that because you aren't deleting 
// the original 3 chars anymore which were allocated on the heap!
//Since at contains the string literal's memory address you're 
// trying to delete the string literal.

A note about modifying read only memory:

Also you should never be modifying a string literal. I.e. this should never be done:

char *at = "hello";
at[2] = '\0'; 

The memory for string literals must be read only and if you change it, the results are undefined by the C++ language.

Since you're using C++:

Since you're using C++ please consider using the std::string type instead.

#include <string>

using namespace std;

int main(int argc, char **argv)
{
  string s = "hello";
  s += " world!";

  //s now contains "hello world!"

  s = "goodbye!";

  //Everything is still valid, and s contains "goodbye!"


  //No need to cleanup s. 

  return 0;
}
Ung answered 16/10, 2009 at 12:33 Comment(0)
U
5

Do not forget to use

delete []

whenever you are allocating something with [].

Urethrectomy answered 16/10, 2009 at 12:34 Comment(0)
B
4

A pointer holds an address. The = operator for a pointer changes the address held.

at = "tw";

Makes at point to the array "tw" (an array created by the compiler to hold the characters tw), it no longer points to the array you created with new. created in the file.

at[2] = '\0';

Adds a NULL to the end of the complier array.

Burne answered 16/10, 2009 at 12:35 Comment(1)
"at[2] = '\0'; Adds a NULL to the end of the compiler array"--this is invoking UB as string literals (the array created by the compiler that you are referring to) are read-only and should never be modified. But I'm assuming you knew that; I just wanted to point it out for others reading your answer.Huguenot
F
0

In the first example you are chaning the value at, in the second you are changing the value of what at points to. Assigning a char * to a double quoted string assigns it to a static const pointer.

In particular, in the first example at now points a different location in memory.

Feathered answered 16/10, 2009 at 12:33 Comment(1)
In the example const char *s = "hello world;, s is a pointer to a const char, not a const pointer to a char. There are so many typos in your answer... Also, I'm assuming you might have meant to say "assigning a double quoted string to a char * rather than the other way around? As you can't assign anything to a string literal, i.e., a string literal can never be on the LHS?Huguenot
B
0

In your first example you are allocating some memory and pointing to it with the "at" variable. When you do

at = "tw"

you are effectively re-pointing the char * to a constant character string. This causes you to leak memory. When you go on to delete "at" you are attempting to delete stack memory.

strcpy goes through each character and copies their values to the new memory you allocate. This is also known as a deep copy.

Benge answered 16/10, 2009 at 12:36 Comment(0)
R
0

In the first example, you have caused a memory leak.

Your variable at is a pointer to a memory address, not the string itself. When you assign the address of "tw" to the pointer, you have lost the original address that you got with new. at now points to an address that you did not allocate with new, so you cannot delete it.

If you think of pointers as integers, it will probably make more sense. I've assigned arbitrary numbers as addresses for the sake of discussion.

char *at = new char[3];    // 0x1000
at = "tw";                 // 0x2000
at[2] = '\0';              // set char at 0x2002 to 0
delete at;                 // delete 0x2000 (whoops, didn't allocate that!)
Ragsdale answered 16/10, 2009 at 12:43 Comment(0)
T
0

You mistake two things: making pointer point to something different (this is what assignment does) and copying some data to a place pointed by pointer.

at = "tw";

this code makes at point to a literal "tw" created somewhere in read-only memory. Trying to write to it is an undefined behaviour.

char *at = new char [3];
strcpy(at,"t");

this code allocates memory for three chars and makes at point to this part of memory (line 1) and then copies some data to memory pointed by at.

And remember, that memory allocated with new[] should be deallocated with delete[], not delete

I advice you to learn more about pointers. This discussion covers this.

Tomasz answered 16/10, 2009 at 12:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.