Change string by index

前端 未结 3 1512
借酒劲吻你
借酒劲吻你 2021-01-13 04:38

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

相关标签:
3条回答
  • 2021-01-13 05:20

    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.

    0 讨论(0)
  • 2021-01-13 05:32

    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>)

    0 讨论(0)
  • 2021-01-13 05:37

    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;
    
    0 讨论(0)
提交回复
热议问题