4

So, I'm new to C++ and I can't figure why this is happening. I have a string with all the alphabets and I copied 10 characters from it into a new string s2, character by character using a for loop as follows and when I execute it the cout function is printing a blank line.

#include <iostream>

using namespace std;

int main(){
    string s = "abcdefghijklmnopqrstuvwxyz";
    string s2;
    for(int i=0; i<10; i++){
        s2[i] = s[i];
    }
    cout << s2 << endl;
    return 0;
}

But when I print this string s2 character by character I got the correct output

#include <iostream>

using namespace std;

int main(){
    string s = "abcdefghijklmnopqrstuvwxyz";
    string s2;
    for(int i=0; i<10; i++){
        s2[i] = s[i];
    }
    
    for(int i=0; i<10; i++){
        cout << s2[i];
    }
    return 0;
}

Any help would be appreciated!

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • 1
    `ios::sync_with_stdio(false); cin.tie(0);` - Why all this nonsense? – Jesper Juhl Jul 18 '20 at 15:05
  • 1
    @JesperJuhl • (deadpan) So the program is performant. Otherwise it will be too slow. (/deadpan) – Eljay Jul 18 '20 at 15:11
  • 1
    You did not `#include `, but you added all sorts of `sync_with_stdio` stuff for no reason.. – PaulMcKenzie Jul 18 '20 at 15:12
  • 1
    You tagged the question with c-strings but there aren't any c-strings in your code. – Thomas Sablik Jul 18 '20 at 15:13
  • @Eljay Well, the program does hardly anything other than output, so the synccing overhead might actually be significant enough to be measurable. – eerorika Jul 18 '20 at 15:14
  • @Eljay If those statements make a difference, then the program has other issues. Those statements *can* make sense if you are trying to shave off every last ms, but for most (real) applications they won't matter one iota. – Jesper Juhl Jul 18 '20 at 15:14
  • @JesperJuhl forgot to mention I encountered this when I was solving a problem on codeforces and `ios::sync_with_stdio` will help in fast I/O – iwrestledthebeartwice Jul 18 '20 at 15:17
  • @eerorika Maybe if it was outputting megabytes of data, but a few characters? – Shawn Jul 18 '20 at 15:17
  • @PaulMcKenzie it didn't work when I include `` – iwrestledthebeartwice Jul 18 '20 at 15:18
  • @JesperJuhl I'm pretty sure Eljay was being just a little bit sarcastic. – Shawn Jul 18 '20 at 15:20
  • 1
    *it didn't work when I include * -- That's a relief -- now you're forced to actually include the correct headers. – PaulMcKenzie Jul 18 '20 at 15:22
  • 2
    [Why should I not `#include `](https://stackoverflow.com/questions/31816095/why-should-i-not-include-bits-stdc-h) – Shawn Jul 18 '20 at 15:23
  • @jaychandra "it didn't work when I include " - You should *never* do that. Why would you ever want to include a non-standard and non-portable header in the first place? That header is internal to the implementation, you are not supposed to include it. And if you do include it you've just made sure your code won't compile with any other compiler and you've also just dragged in *all* of the standard library, which hurts your compile times and increases the risk of name collisions. *Why* would you *ever* do that? – Jesper Juhl Jul 18 '20 at 15:24
  • Thank you @Shawn for that article, I'll try to use it less from now – iwrestledthebeartwice Jul 18 '20 at 15:25
  • 1
    It's somehow funny to use `ios::sync_with_stdio` to optimize performance but to copy a string without preallocating memory. – Thomas Sablik Jul 18 '20 at 15:26
  • Thanks for the info @JesperJuhl I will stop using `` from now :) – iwrestledthebeartwice Jul 18 '20 at 15:27
  • @jaychandra Currious; what caused you to include it in the first place? – Jesper Juhl Jul 18 '20 at 15:28
  • I've been practicing competitive programming and found that most people use `` so I started using it. – iwrestledthebeartwice Jul 18 '20 at 15:38
  • 1
    That's called https://en.wikipedia.org/wiki/Cargo_cult_programming. Most people in competitive are bad programmers. You shouldn't learn from them. – Thomas Sablik Jul 18 '20 at 15:40
  • ah sure thing, I'll keep that in mind @ThomasSablik – iwrestledthebeartwice Jul 19 '20 at 06:32

3 Answers3

10

Both your code have undefined behavior. string s2; just initializes s2 as an empty std::string which contains no elements. Trying to access element as s2[i] leads to UB, means anything is possible. (Even it appears to give the correct output in your 2nd code snippet.)

You can use push_back to append element as

for(int i=0; i<10; i++){
    s2.push_back(s[i]);
}

Or

for(int i=0; i<10; i++){
    s2 += s[i];
}

Or you can make s2 containing 10 elements in advance.

string s2(10, '\0'); // or... string s2; s2.resize(10);
for(int i=0; i<10; i++){
    s2[i] = s[i];
}
songyuanyao
  • 169,198
  • 16
  • 310
  • 405
4

An empty string s2 is created. s2[i] = s[i]; won't resize the string but it causes undefined behavior. One way to solve the problem is to append each character. This will change the size and can involve multiple memory allocations.

Another way is to create a string with correct size or reserve memory with:

  1. Create "empty" string with size 10:

     string s2(10, '\0');
     for(int i=0; i<10; i++){
         s2[i] = s[i];
     }
    
  2. Reserve memory

     string s2;
     s2.reserve(10);
     for(int i=0; i<10; i++){
         s2 += s[i];
     }
    
  3. Initialize the string with a substring

     string s2 = s.substr(0, 10);
    

    or

     string s2(s.begin(), s.begin() + 10);
    

    or

     string s2(s, 0, 10);
    
Thomas Sablik
  • 16,127
  • 7
  • 34
  • 62
3

One might be confused, because

for(int i=0; i<10; i++){
    cout << s2[i];
}

delivers the correct output. The std::string is a class with functions and overloaded operators. The []-operator of the std::string shows a similar behaviour as a char-array. The "array-elements" are defined with this code snipped:

for(int i=0; i<10; i++){
        s2[i] = s[i];
    }

If the code of the question is executed several times, the undefined behaviour of this snipped can be seen.

If you try to use a string iterator you will see, that the string s2 is still an empty string.

string::iterator si;
    for (si=s2.begin(); si!=s2.end(); si++)
    {
        cout << *si ;
    }
UweJ
  • 447
  • 3
  • 10