2

I was trying to learn smart pointer as i came across this.

{ //Example 1
    //String to be copied
    std::string strHello = "HelloWorld1";
    int size = strHello.length();

    //Creating Ptr using make_unique
    std::unique_ptr<char[]> pstrText;
    pstrText = make_unique<char[]>(size + 1); // Allocating +1 for \0

    //Copying values to the new pointer
    int r = memcpy_s(pstrText.get(), size + 1, strHello.c_str(), size);

    //Printing
    std::cout << pstrText.get() << std::endl;
}

{ //Example 2
    //String to be copied
    std::string strHello = "HelloWorld2";
    int size = strHello.length();

    //Creating Ptr using make_unique
    std::unique_ptr<char[]> pstrText;
    pstrText = make_unique<char[]>(size);

    //Copying values to the new pointer
    int r = memcpy_s(pstrText.get(), size, strHello.c_str(), size);

    //Printing
    std::cout << pstrText.get() << std::endl;
}
{//Example 3

    //String to be copied
    std::string strHello = "HelloWorld3";
    int size = strHello.length();

    //Creating Ptr using make_unique
    std::unique_ptr<char[]> pstrText;
    pstrText = make_unique<char[]>(size + 1); //Allocating + 1

    //Copying values to the new pointer
    int r = memcpy_s(pstrText.get(), size, strHello.c_str(), size);

    //Printing
    std::cout << pstrText.get() << std::endl;
}

{//Example 4

    //String to be copied
    std::string strHello = "HelloWorld4";
    int size = strHello.length();

    //Creating Ptr using make_unique
    std::unique_ptr<char[]> pstrText;
    pstrText = make_unique<char[]>(size);

    //Copying values to the new pointer
    int r = memcpy_s(pstrText.get(), size + 1, strHello.c_str(), size); 

    //Printing
    std::cout << pstrText.get() << std::endl;
}

The Output is as Follows using VS 2013 Debug(Win32):

HelloWorld1
HelloWorld2²²²²½½½½½½½½■
HelloWorld3
HelloWorld4²²²²½½½½½½½½■
  1. If Example 1 is correct why does the Example 3 also give the correct output?
  2. What Does the garbage values at the end signify?
  3. Why didn't the compiler throw any error or exception?
  4. In Example 4 Why is there no error when we had tried to copy a value at the end but was not initialized
  5. How do we properly initialize a smart pointer in this case? Will anything change if it a BYTE array instead of char;
Kiran Thilak
  • 443
  • 5
  • 15
  • 2
    I recommend using `std::copy` rather than `memcpy` and variants. It is as fast and is safer. – Galik Feb 04 '20 at 09:22
  • @Galik for the specific case of copying `char`s, it isn't any safer – Caleth Feb 04 '20 at 10:00
  • @Caleth Avoiding `memcpy` altogether makes coding safer. If you use `memcpy` in the wrong place it is a problem. You can avoid that possibility by simply not using it. – Galik Feb 04 '20 at 10:13

2 Answers2

4

Two things you need to know:

First is that in C++ memcpy_s is a Microsoft VC++ CRT specific extension. It exists in C (added in the C11 standard), but even then the Microsoft implementations of the safe functions are often not following the C standard.

The second thing, and which is the cause of your problem, is that the last argument is the number of bytes to copy. Since you only specify size there, your call will not copy the string null-terminator. You need to copy size + 1 bytes:

int r = memcpy_s(pstrText.get(), size + 1, strHello.c_str(), size + 1);
//                                                               ^^^^
//                    Copy plus one, to also copy the null-terminator
Some programmer dude
  • 400,186
  • 35
  • 402
  • 621
3

If Example 1 is correct why does the Example 3 also give the correct output?

make_unique<char[]>(size + 1); initialises all the chars to 0. Both example 1 and 3 don't overwrite this, because they copy respectively the smaller of size + 1 and size; and the smaller of size and size. I.e. they both copy size chars

What Does the garbage values at the end signify?

That your program has undefined behaviour. std::ostream::operator<<(char *) in this case reads bytes until it sees a 0.

Why didn't the compiler throw any error or exception?

Because C++ is not safe. Rather than defining a particular behaviour (such as throwing an exception) when you access outside an array, the whole program isn't bound by the rules of C++. This is called "Undefined Behaviour".

In Example 4 Why is there no error when we had tried to copy a value at the end but was not initialized

Because C++ is not safe

How do we properly initialize a smart pointer in this case? Will anything change if it a BYTE array instead of char;

Either example 1 or example 3 is fine. However, why not just use std::string?

Caleth
  • 52,200
  • 2
  • 44
  • 75
  • I have a problem with "C++ is not safe" part - is a Train unsafe because somebody died on the tracks? Is a car unsafe because somebody crashed taking a corner too fast? Or is alcohol unsafe, because somebody lost consciousness from drinking too much? In C++ the safe construct is to use a `vector` (or `array`) with `at` accessor. – Robert Andrzejuk Feb 04 '20 at 12:14
  • @RobertAndrzejuk it's a statement of fact. "Certain other operations are described in this document as undefined (for example, the effect of attempting to modify a const object). [ Note: This document imposes no requirements on the behavior of programs that contain undefined behavior. — end note ]" – Caleth Feb 04 '20 at 12:30
  • It's not a fact - it's an opinion. – Robert Andrzejuk Feb 04 '20 at 12:32
  • 2
    @RobertAndrzejuk and yes, those things *are* unsafe. I'm not saying that C++ is *bad*. A more apt comparison is saying that a table-saw is unsafe, you can cut limbs off if you don't use it correctly – Caleth Feb 04 '20 at 12:47
  • "_Because C++ is not safe_", "_if you don't use it correctly_" ? – Robert Andrzejuk Feb 04 '20 at 13:42
  • 1
    @RobertAndrzejuk In a safer language, "not using it correctly" would have a defined result at runtime. In a safe language, "not using it correctly" would be a build-time error. – Caleth Feb 04 '20 at 14:16
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/207189/discussion-between-caleth-and-robert-andrzejuk). – Caleth Feb 04 '20 at 14:18
  • "Example 1 copies the string's terminating 0, Example 3 keeps the one from the initialisation" Neither of these examples copies the terminating byte. Both of these examples specify `size` as their last parameter instead of `size + 1` and thus only copy up to the length of the string. Example 1 and 3, in the end, perform the exact same set of operations. – Darinth Feb 04 '20 at 15:48