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 w
The size of altered
is always zero - by using indexes you are trying to copy values from original
to altered
at indexes altered
does not have. As LogicStuff has said, this is undefined behaviour - it doesn't generate an error because when we use indexes with std::string
we are in fact calling an operator on a std::string
to access the data
field of a string. Using []
operator is defined in the C++ Standard as having no range check - that's why no error was thrown. The safe way to access indexes is to use the at(i)
method: altered.at(i)
will instead throw a range error if altered.size() <= i
However, I'm going to give this as my solution because it's a "Modern C++" approach (plus shorter and complete).
This is the alternative I would do to what has been given above:
string original = "abc";
string altered = original;
for (auto& c : altered) c += 5; // ranged for-loop - for each element in original, increase its value by 5
cout << altered << endl;
Note the significant reduction in code :-)
Even if I were doing it LogicStuff's way, I would still do it like this:
string original = "abc"
string altered = ""; // this is actually what an empty string should be initialised to.
for (auto c : original) altered += (c+5);
However, I actually don't recommend this approach, because of the way push_back()
and string appending / string concatenation work. It's fine in this small example, but what if original
was a string holding the first 10 pages of a book to be parsed? Or what if it's a raw input of a million characters? Then every time the data
field for altered
reaches its limit it needs to be re-allocated via a system call and the contents of altered
are copied and the prior allocation for the data
field is freed. This is a significant performance impediment which grows relative to the size of original
-- it's just bad practice. It would always be more efficient to do a complete copy and then iterate, making the necessary adjustments on the copied string. The same applies to std::vector
.
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.
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>
)
In your program string altered
is empty. It has no elements.
Thus you may not use the subscript operator to access non-existent elements of the string as you are doing
altered[i] = original[i] + 5;
So you can append the string with new characters. There are several ways to do this. For example
altered.push_back( original[i] + 5 );
or
altered.append( 1, original[i] + 5 );
or
altered += original[i] + 5;
As you may not apply the subscript operator for an empty string to assign a value then it is better to use the range-based for loop because the index itself in fact is not used. For example
for ( char c : original ) altered += c + 5;