C ++: change row by index

I am new to C ++ and currently I am working with strings. My question is why when compiling the script that I provide below, I can get string characters when I use index notation, but can't get a string 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 script, the characters in the β€œchanged” line are displayed correctly with this line:

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

But the line itself is not displayed with this line:

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

I would like to know why this is happening.

+5
source share
3 answers

You have not resized your altered line to fit the length of the original line before the loop, so your code shows 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 adding to altered :

 altered += original[i] + 5; 

Thus, it may be empty before the loop, it will automatically resize to contain the added characters.


Explanation

The way UB happens here is that you can write data to a static array that std::string uses to optimize short strings ( std::string::operator[] does not check if you access this array by std::string::size() ), but std::string::size() remains 0 , and also std::string::begin() == std::string::end() .

To do this, you can access the data separately (again, using UB):

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

but cout << aligned does not print anything, since the definition of a simplified operator<< for std::string looks functionally as follows:

 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 does not know what you did with its base array, and what you meant that the string should increase in length.


In conclusion, the <algoritm> way to do this conversion is:

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

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

+5
source

Your altered program altered empty. He has no elements. Thus, you cannot use the index operator to access non-existent elements of a row, as you do

  altered[i] = original[i] + 5; 

So you can add a line with new characters. There are several ways to do this. for instance

  altered.push_back( original[i] + 5 ); 

or

  altered.append( 1, original[i] + 5 ); 

or

  altered += original[i] + 5; 

Since you cannot use the index operator for an empty string to assign a value, then it is better to use a range-based loop because the index itself is not actually used. for instance

 for ( char c : original ) altered += c + 5; 
+2
source

The altered size altered always zero β€” with the indexes you are trying to copy from original to altered , the altered does not exist. As LogicStuff said, this behavior is undefined - it does not generate an error, because when we use indexes with std::string , we actually call the operator on std::string to access the data field of the string. Using the [] operator is defined in the C standard ++ as without range checking - therefore no errors have been made. A safe way to access indexes is to use the at(i) method: altered.at(i) instead use a range error if altered.size() <= i

However, I am going to give this as my solution, because it is a Modern C ++ approach (plus shorter and more complete).

This is an alternative that I would do with what was 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; 

Please note the significant code reduction :-)

Even if I did this LogicStuff, 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, in fact, I do not recommend this approach because of the push_back() method and string merging / string concatenation. This is great in this small example, but what if original was a line containing the first 10 pages of the book to be parsed? Or what if it's a raw contribution to a million characters? Then, each time the data field for altered reaches its limit, it must be redistributed using a system call, and the contents of altered will be copied, and the preliminary selection for the data field will be freed. This is a significant performance hurdle that is growing relative to the size of the original - it's just bad practice. It would always be more efficient to perform a full copy and then iterate through the necessary adjustments to the copied string. The same goes for std::vector .

+1
source

Source: https://habr.com/ru/post/1236571/


All Articles