0

In C++ string literals "Hello" are const and are immutable. I wanted to make a custom string class whose strings are not const chars, so they can be changeable

Here is a snippet of code that might illustrate what I'm trying to do:

#include <iostream>

class String {
  public: 
    char * p_start;

    String(char * strSourc) // Constructor
      {
        p_start = strSourc;
      }
};

int main() 
{
  String myString("Hello");  
// Create object myString, send "Hello" string literal as argument

  std::cout << myString.p_start << std::endl; 
// Prints "Hello"

  *myString.p_start = 'Y'; 
// Attempt to change value at first byte of myString.p_start

  std::cout << myString.p_start << std::endl; 
// Prints "Hello" (no change)

  myString.p_start = "Yellow"; 
// Assigning a string literal to p_start pointer 

  std::cout << myString.p_start << std::endl; 
// Prints Yellow, change works.  I thought myString "Hello" was const chars, immutable

  return 0;
}

So, I'm confused. I've looked everywhere and it says that string literals, like "Hello", are immutable, each of their char bytes are unchangeable. Though I managed to assign Yellow to the p_start pointer, changing the first letter. Though changing the single letter H to a Y through dereferencing the H pointer didn't do anything.

Any insights would help me, thanks.

5
  • 1
    Could you show your current ctor? And what error did you encounter? Commented Oct 3, 2015 at 7:47
  • I've changed the question to hopefully better illustrate the issue, including sample code. Commented Oct 3, 2015 at 11:02
  • 1
    Yes, you cannot change the content of "Hello" by your pointer p_start. You can make a copy of it in the ctor, then you can modify the chars, which being managed by the class. Commented Oct 3, 2015 at 11:27
  • Thanks for that. The thing I don't get is why it let me say p_start = "Yellow" if the string literal is null terminated array of const char, but it wouldn't let me say *p_start = 'Y'. Also, like you said, I'll have to pass it the string literal, then do a one to one copy into my own array, but this brings up the question, I heard string literals are static and last forever during the program until the program stops. Wouldn't this be a huge waste of memory? Basically having two copies of everything? As far as I know you can't destroy string literals, that's what I read. Commented Oct 3, 2015 at 12:58
  • I am unsure how this differs from string s = "Hello"; if you are trying to make "Hello" mutable, you cannot — there must always be a copy. Commented Feb 8, 2022 at 4:36

2 Answers 2

1

I think you're confusing about pointer and pointee.

p_start = "Yellow", you're changing the value of pointer, to point to "Yellow". *p_start = 'Y', you're changing the value of pointee, the content p_start points to, not itself. As you said, "Yellow" are const chars, so the behaviour try to modify them is UB.

You can make a copy of it in the ctor, then you can modify the chars, which are managed by the class. Yes, it will be a new copy, but it won't be a waste of memory. And you have no choice if you want them to be changable.

Sign up to request clarification or add additional context in comments.

9 Comments

I upvoted your answer, thanks. You're right, I did confuse the pointer and the pointee. If you make a pointer to an int, assign it to an address of an int, and then cout << the pointer, you'll get the memory address pointed to by the pointer. If on the other hand, as I did here, you create an array of chars, and then make a pointer pointing to the array of chars, and cout << the pointer, you won't get the address that the pointer points to, you'll get the entire null-terminated string. Why is that? Different rules? Also, how can I assign "Yellow" to a 4 byte pointer?
I just found out that when I did p_start = "Yellow", it didn't assign the value of "Yellow" to p_start, rather it created a new block of memory with the string literal "Yellow" and gave the address of it to p_start. Anyway, I have to say I find that string literals being immutable and static a pain in the arse. I'm sure they have their reasons, but if I want to manipulate a string I have to either copy it from the string literal and have two copies of it (cause the original is static and is never destroyed), or create an array and send it to my class in two steps. Anyone think this is silly?
@TitoneMaurice "Different rules?" Yes, operator<<() overload for char * to print out the content of string, so the behaviour is different from int * or float * and so on. "How can I assign "Yellow" to a 4 byte pointer?" You don't assign the string to the pointer, just the first address of the string, i.e. the address of "Y" in "Yellow".
@TitoneMaurice Because the string literal can't be modified, the complier is allowed to do some optimization on it, such as save it in ROM, and only one copy in memory for all string literals with same value, and so on.
@TitoneMaurice Comments can't be string literals. They can't be decided at compile time. const char[] comment1 = "???";
|
0

not sure if anyone finds this helpful after so long but since I am learning myself I took your above code and made it work by copying. It now has a member variable for size and cleans up using delete when you assign a new string literal to it (const char*).

#include <iostream>

class String {
public: 
char * p_start;
int m_size;

String(const char * strSourc) // Constructor
  {
    //The following will get the size of the parameter.
     int SizeParameter=0;
     while (*(strSourc+SizeParameter) != '\0')
     {
         SizeParameter++;
     }
     // size of string saved.
     m_size = SizeParameter; 
     // allocate enough memory so we can copy strSourc
     p_start = new char[SizeParameter+1];
     //copy the contents strSourc
    for(int i=0; i<SizeParameter+1; i++)
    {
    *(p_start+i) = *(strSourc+i);
    }
  }
  //Handle change of string value.
  char* AssignNewString (const char* newtext)
  {
      //clean
      delete p_start; 
      
      int SizeParameter=0;
     while (*(newtext+SizeParameter) != '\0')
     {
         SizeParameter++;
     }
     // size of string saved.
     m_size = SizeParameter; 
     // allocate enough memory so we can copy strSourc
     p_start = new char[SizeParameter+1];
     //copy the contents strSourc
    for(int i=0; i<SizeParameter+1; i++)
    {
    *(p_start+i) = *(newtext+i);
    }
    return p_start;
  }
  char* operator=(const char* newtext)
  {
  AssignNewString(newtext);
  }
  };



 int main() 
 {
 String myString("Hello");  
 // Create object myString, send "Hello" string literal as argument
 std::cout << "string size: " << myString.m_size << std::endl;

 std::cout << myString.p_start << std::endl; 
// Prints "Hello"

*myString.p_start = 'Y'; 
// Attempt to change value at first byte of myString.p_start

std::cout << myString.p_start << std::endl; 
// Prints "Hello" (no change)

myString = "yellow"; 
// Assigning a string literal to p_start pointer 
myString = "THIS IS A LONGER STRING";

std::cout << myString.p_start << std::endl; 

std::cout << "string size: " << myString.m_size << std::endl;
return 0;
}

Note I am learning myself so do let me know if I am doing something wrong. But so far, it seems to work.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.