Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Change string by index

Tags:

c++

string

I am a beginner in C++ and I am currently working with strings.

My question is why when compiling the code I'm providing below, I can get the string's characters when I use index notation, but cannot get the string itself using cout?

This is the code:

#include <iostream>
#include <string>

using namespace std;

int main()
{
    string original; // original message
    string altered; // message with letter-shift

    original = "abc";
    cout << "Original : " << original << endl; // display the original message

    for(int i = 0; i<original.size(); i++)
        altered[i] = original[i] + 5;

    // display altered message
    cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;
    cout << "altered : " << altered << endl;

    return 0;
}

When I run this, the characters in the string altered are displayed correctly with this line:

cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;

But the string itself is not displayed with this line:

cout << "altered : " << altered << endl;

I would like to know why this happens.

like image 721
theodoros Avatar asked Nov 22 '15 21:11

theodoros


1 Answers

You have not resized your altered string to fit the length of the original string before the loop, thus your code exhibits undefined behavior:

altered[i] = original[i] + 5; // UB -  altered is empty

To fix this, resize altered before the loop:

altered.resize(original.size());

Or use std::string::operator+= or similar to append to altered:

altered += original[i] + 5;

This way, it can be empty before the loop, it will automatically resize itself to contain appended characters.


Explanation

The way UB is happening here, is that you're succeeding in writing the data in the static array, which std::string uses for short string optimization (std::string::operator[] does no checks if you're accessing this array past the std::string::size()), but std::string::size() remains 0, as well as std::string::begin() == std::string::end().

That's why you can access the data individually (again, with UB):

cout << altered[0] << " " << altered[1] << " " << altered[2] << endl;

but cout << aligned does not print anything, considering simplified operator<< definition for std::string looks functionally like this:

std::ostream &operator<<(std::ostream &os, std::string const& str)
{
    for(auto it = str.begin(); it != str.end(); ++it) // this loop does not run
        os << *it;

    return os;
}

In one sentence, std::string is not aware of what you did to its underlying array and that you meant the string to grow in length.


To conclude, <algoritm> way of doing this transformation:

std::transform(original.begin(), original.end(),
    std::back_inserter(altered), // or altered.begin() if altered was resized to original's length
    [](char c)
    {
        return c + 5;
    }

(required headers: <algorithm>, <iterator>)

like image 58
LogicStuff Avatar answered Sep 30 '22 18:09

LogicStuff