0

I was simply using copy function in c++ for copying a string into an array. But the result shown for the following code contain garbage values:

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s;
    char t[10];

    s = "Hello";
    s.copy(t, s.length());
    cout << t;

    return 0;
}

Output: Hellov._Sï

Whereas, if I do the same thing in a different way. I get the right output.

#include <iostream>
#include <string>
using namespace std;

    int main()
    {
        string s = "Hello";
        char t[10];

        s.copy(t, s.length());
        cout << t;

        return 0;
    }

Output: Hello

Can someone explain why is this happening ? I am relatively new to c++.

Chinmay Vemuri
  • 179
  • 2
  • 9
  • Does this answer your question? [What is a null-terminated string?](https://stackoverflow.com/questions/2037209/what-is-a-null-terminated-string) – Yksisarvinen Jan 31 '20 at 14:20
  • *I was simply using copy function in c++ for copying a string into an array* -- You do know that this is not necessary, and you can just use the data within the `std::string` as-is, without copying anything? – PaulMcKenzie Jan 31 '20 at 14:22
  • @Yksisarvinen I didn't quite understand how it is relevant for my question. Is there a problem with the initialization of string object in my code? – Chinmay Vemuri Jan 31 '20 at 14:25
  • @PaulMcKenzie I am aware of that but I just wanted to know how str.copy() works. – Chinmay Vemuri Jan 31 '20 at 14:26
  • @ChinmayVemuri The `copy` did exactly what it is supposed to do. There were `s.length()` number of characters copied to `t`. It will not do any more or any less than that. The `copy` has no idea what to do with `t` after the copy is done. In short, your array has uninitialized values since it is a local variable, no different than any other variable type declared locally that is not initialized. – PaulMcKenzie Jan 31 '20 at 14:29
  • @PaulMcKenzie I am still quite confused. Why did it work the second time ? – Chinmay Vemuri Jan 31 '20 at 14:35
  • Undefined behavior. "Working" is one thing that can happen when the behavior is undefined. Change compiler, compiler options, etc., and that "working" code may no longer work. The array is uninitialized, meaning any junk could be in that array when you use it, including null characters. – PaulMcKenzie Jan 31 '20 at 14:36
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/206982/discussion-between-chinmay-vemuri-and-paulmckenzie). – Chinmay Vemuri Jan 31 '20 at 15:37

1 Answers1

4

The issue is that std::string::length() does not include the string's NUL terminator. So s.copy(t, s.length()); copies 'H', 'e', 'l', 'l', 'o' into t, but not the '\0' that follows. The following elements of that array are not zeroed (they may contain some zeroes by chance).

But std::cout, when passed a char*, expects a nul-terminated string. It will continue to print until it finds a 0 byte or something else happens (overflowing t is undefined behaviour).

You could "fix" it by nul-terminating t:

t[s.length()] = '\0';
juanchopanza
  • 223,364
  • 34
  • 402
  • 480
  • is it possible that the string::length() functionality depends on the compiler also? Like some compiler will take care of the NULL terminator and @Chinmay Vemuri's compiler does lack that functionality? – Santosh Sahu Jan 31 '20 at 14:26
  • @SantoshSahu Not really for an `std::string` constructed from a string literal. But a `std::string` can actually contain nul-terminators, it just has to be constructed in a different way. Here, there just happen to be zero bytes lying around. – juanchopanza Jan 31 '20 at 14:27
  • But my question remains. Why did it print the correct output second time ? Is it that for some other input, the behaviour maybe different if done in the second way? – Chinmay Vemuri Jan 31 '20 at 14:40
  • @juanchopanza, it works fine [here](https://wandbox.org/permlink/nOrMGP9lVcl82jVQ), besides why would `string s;` `s = "Hello";` be any different than `string s = "Hello";` ? – anastaciu Jan 31 '20 at 14:41
  • @ChinmayVemuri because char array unitialized data randomly has '\0' at that place – Slava Jan 31 '20 at 14:41
  • 3
    C++ has something called "undefined behavior". You are plain lucky that a null was in the right place. To ensure that the behavior is defined, *you* have to put the null there. – PaulMcKenzie Jan 31 '20 at 14:42
  • 2
    @anastaciu unfortunately in C++ getting expected output not enough to say your program works. – Slava Jan 31 '20 at 14:42
  • I was convinced that, at least after C++11, any assignment of a string literal would be automatically null-terminated, it make littlle sense that it's not. – anastaciu Jan 31 '20 at 14:48
  • 2
    @anastaciu, a `string` would be null-terminated since C++11, yes, but `s.length()` does **not** include the `\0` in its count, so the copy would still not work properly. – ChrisMM Jan 31 '20 at 14:52
  • 1
    @anastaciu assignment of a string literal has nothing to do here, it just happens that uninitialized char array `t` have 0 byte at that location or not. `s.length()` does not include `\0` so `std::string::copy()` will not copy it no matter if it is there or not. – Slava Jan 31 '20 at 14:52
  • I see, thanks guys, maybe next time I should read the answer more carefully. – anastaciu Jan 31 '20 at 14:57
  • 2
    @anastaciu -- If a programmer asks for 5 bytes to be copied, then they only want 5 bytes copied, not 6. Having `std::copy` do extra work that the programmer didn't ask for goes against C++ philosophy of getting only what you are paying for. – PaulMcKenzie Jan 31 '20 at 15:00