0

In C++, I created a new string (string class) by copying characters index wise from another string (string class) which was already initialized.

But I am unable to print this new string to the screen using cout. Using c_str() I am able to print it using cout. But isn't c_str() needed only when using printf() because it needs a c type string?

#include <cstring>
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;

int main() {
    int i;
    string a,b;
    cin>>a;
    for(i=0;a[i]!='\0';i++){
        b[i]=a[i];
    }
    cout<<b;
    return 0;
}

EDIT: Thanks for the help! But I might not have been quite clear in my question, so these are the main problems I have. PLease read the following and if you could help me further, that would be great! (Also, I understand b=a; is the easiest way to assign but I am trying to understand strings and hence the question.)

a) I dont know when a cpp string is null terminated and when it is not but in this case the a string after initialisation was null terminated because the loop ended and it ended after the last character of the string a because on coming out of the loop and doing cout<<a[i]; the last chararcter of a is printed.

b) Within the loop, after the assignment when I include cout<<b[i]; it does print out the value that we expect was just assigned to b[i]. So b[i] does exist for some odd reason.

c) Outside the for loop, at the end of the program, when I cout<<b[2]; it does print the third char of the string. And if I do cout<<b.c_str(); it prints out the entire string. Its only if I do cout<< b; that nothing gets printed. Why is this?

Zag
  • 693
  • 1
  • 5
  • 9
  • A std::string is not(!) a null terminated C-string It may or may not contain a '\0' (even in the middle), –  Sep 28 '16 at 13:49
  • ... your loop is wrong –  Sep 28 '16 at 13:50
  • 4
    You mistyped `b = a;`. – LogicStuff Sep 28 '16 at 13:51
  • You need to allocate space in `b` before you start overwriting its characters using `b[n]`. So `b.resize(...)` would be needed. Otherwise you need to add each char to the end using `b += a[i];`. Its probably worth trying to find a string tutorial. – Galik Sep 28 '16 at 13:53

3 Answers3

3

You may have noticed that std::string doesn't behave like a normal C-style string. It's important to notice that.

The first thing is that direct assignment like that doesn't work. The real reason is that this not change the size of the string. You can find it out yourself by outputting b.size(), or checking that both b.start() and b.end() refer to the same char, b[0].

Your experiment would work if you first resized b to the same size as a.

b.resize(a.size());

Secondly, have a look in your loop. Don't compare to \0 as if it was a C-style string. Instead, check if i < a.size(), as there's no guarantee a std::string will be null (\0) terminated.

What you really should do, although, is simply assign a to b, and b will have a copy of a as you intend.

b = a;
2

You need to allocate space in b before you start overwriting its characters using b[n]. So b.resize(...) would be needed. Otherwise you need to add each char to the end using b += a[i];.

Also std::string are not null terminated so please don't check for \0,use their size():

Consider this:

int main()
{
    int i;

    string a, b; // these are both empty

    std::cout << "a.size() = " << a.size() << '\n';
    std::cout << "b.size() = " << b.size() << '\n';

    cin >> a; // now a contains something

    std::cout << "a.size() = " << a.size() << '\n';
    std::cout << "b.size() = " << b.size() << '\n';

    // for(i = 0; a[i] != '\0'; i++) // no don't test for null
    // {
    //  b[i] = a[i]; // no b has no spaces
    // }

    for(std::size_t i = 0; i < a.size(); ++i)
        b += a[i]; // increases b's size by one each time

    std::cout << "b.size() = " << b.size() << '\n';

    cout << b;
}

To answer the additional questions:

a) It's an internal implementation detail that std::string is null terminated. The fact is that the null terminating character is outside the legal bounds of the public interface and it is technically "undefined behavior" to access it. The correct way to find the end of the std::string is using its size() function or its end() iterator.

b) It is pure luck if there is memory allocated beyond the end of the string. Even if there is memory there it is not part of the string and will not be copied during a legal string operation like a = b;.

c) It's completely undefined behavior. Likely std::cout << b; looks at the size() of the string which is zero and decides not to print anything because the size() is zero (see my example). However c_str() simply returnes the memory address of the beginning of the string. From the memory address alone std::cout is unable to tell how big the string that it comes from is supposed to be. Therefore it just keeps printing characters until it finds a null. It is pure luck if it prints what you are hoping for.

Galik
  • 47,303
  • 4
  • 80
  • 117
1

b is empty. Assigning to b[i] has undefined behaviour for any value of i.

a[i]!='\0' condition is only valid if your compiler supports C++11 or later. Prior to C++11 str[str.size()] has undefined behaviour.


You can fix first problem by first resizing b. If you have C++11 support, a range-based loop would be much simpler. If you don't have C++11, then following is a valid option:

b.resize(a.size());
for(i=0; i < a.size(); i++)

Writing a loop to copy a string is a bit silly though. You can simply use assignment:

b = a;

a) I dont know when a cpp string is null terminated and when it is not but in this case the a string after initialisation was null terminated because the loop ended and it ended after the last character of the string a because on coming out of the loop and doing cout<<a[i]; the last chararcter of a is printed.

(Assuming you aren't compiling with C++11 enabled) That happens to be what you observed when you compiled with your compiler on your operating system and ran the program on your cpu at that particular time. There is no guarantee that the behaviour is the same if any factor changes.

b) Within the loop, after the assignment when I include cout<<b[i]; it does print out the value that we expect was just assigned to b[i]. So b[i] does exist for some odd reason

It doesn't mean that b[1] "exists". The behaviour is undefined.

c) Outside the for loop, at the end of the program, when I cout<<b[2]; it does print the third char of the string. And if I do cout<<b.c_str(); it prints out the entire string. Its only if I do cout<< b; that nothing gets printed. Why is this?

This is because the behaviour is undefined.

eerorika
  • 232,697
  • 12
  • 197
  • 326