-1

I'm so confused, I'm sorry if this obvious, but:

int main()
{
    char stringDest[20];
    char stringSource[20];

    strcpy_s(stringDest, stringSource);

    return 0;
}

Throws the exception "Buffer is too small". Whereas:

char stringSource[20];

int main()
{
    char stringDest[20];

    strcpy_s(stringDest, stringSource);

    return 0;
}

Works fine.

Furthermore, I thought the point of the safe strcpy_s(dest, size, source) was that you specify the number of bytes that are copied, however when I do this:

int main()
{
    char stringDest[20];
    char stringSource[20];

    strcpy_s(stringDest, 1, stringSource);

    return 0;
}

I get a "Buffer is too small exception". I am so confused. Why does declaring the variable outside main() make a difference? And why is it wrong to specify 1 byte to copy?

fluter
  • 13,238
  • 8
  • 62
  • 100
Zebrafish
  • 11,682
  • 3
  • 43
  • 119
  • 1
    It is undefined behaviour to read unininitialized variables. – Kerrek SB May 12 '16 at 12:45
  • 1
    I know you want an answer to this but instead of dealing with it all you can just use `std::string`s and use `std::string foo = "foo"; std::string bar; bar = foo;` and now `bar` has `"foo"` in it. – NathanOliver May 12 '16 at 12:46
  • 1
    `errno_t strcpy_s( char *strDestination, size_t numberOfElements, const char *strSource );` – BLUEPIXY May 12 '16 at 12:48
  • Where are the library includes? This code should throw compilation errors/warnings. `strcpy_s` takes [3 parameters](http://en.cppreference.com/w/c/string/byte/strcpy), not 2. – user694733 May 12 '16 at 12:55
  • @user694733 Yes, I left out the headers for clarity, maybe it was a bad decision. The strcpy_s is overloaded for two arguments I believe also. – Zebrafish May 12 '16 at 12:57
  • @user694733: Microsoft's `strcpy_s` has a template overload which takes two arguments. The first as a reference to an array, where the size is deduced, so you don't need to specify it. – Benjamin Lindley May 12 '16 at 12:58
  • @TitoneMaurice In that case this is strictly C++ code. You should remove C tag from the post then, and mention compiler as this is not standard code. Also in the future always provide complete code examples ([mcve]). – user694733 May 12 '16 at 13:17
  • @TitoneMaurice **Never** leave out headers if you're tagging this as `C`. A missing header file influences how a C program behaves. – PaulMcKenzie May 12 '16 at 13:19
  • 1
    @PaulMcKenzie Not really, missing header files will just give compiler errors for missing prototype, unless you are using some old dinosaur compiler. – Lundin May 12 '16 at 13:45
  • @Lundin -- C89 is a language standard and is still relevant (note that the tag is just `C`). Also, Visual Studio is not a dinosaur compiler, but AFAIK still compiles 'C' code as C89. Leaving out prototypes is perfectly valid and the code will compile. There was a post last week about this where the poster left out the header files and couldn't understand why certain functions were not returning types correctly (other than `int`). – PaulMcKenzie May 12 '16 at 15:14
  • @PaulMcKenzie It is an obsolete standard withdrawn 17 years ago. Note the C tag wiki which explicitly says that if the OP doesn't mention a specific version of the standard, always assume the current ISO standard. And I'm sorry, but Visual Studio 2015 is a very old, completely outdated dinosaur compiler, which doesn't even conform to C89 very well, let alone C99 or ISO C. If you paid money for it you got scammed, contact the police. – Lundin May 12 '16 at 17:34
  • I mostly write C++ style, but marked this question as C as these were pure C functions (I think), at least in the documentation the headers are and . I hope VS isn't THAT bad, as it's what I use. I guess I like it cause I haven't used anything else. Would anyone happen to know why the compiler allows me to use the two argument overload strcpy_s(dest, source) for a char array, but insists on the three argument function if I pass char pointers, or char arrays in classes? – Zebrafish May 12 '16 at 18:09

2 Answers2

6

Gives me an unhandled RangeChecks exception, whereas:

C's arrays are zero indexed, that means for char stringSource[20];, the elements are from 0 to 19, when you do stringSource[20] = '\0';, the access is out of array bounds, which results undefined behavior.

Throws the exception "Buffer is too small". Whereas:

That's because stringSource is of automatic storage and is not initialized to a valid string, use of it cause undefined behavior.

While the second case, when you put char stringSource[20]; out of the function, the array is static storage, it is by default initialized with zero value, it is effectively the same as char stringSource[20] = "";, that's why this case the strcpy succeeded.

fluter
  • 13,238
  • 8
  • 62
  • 100
2

You are invoking undefined behaviour here, because you are not initializing the source string.

In most cases, there will be garbage, and the first 0 byte is most likely after the length of 20, which is why the compiler/runtime complaines.

char stringSource[20] = {0};

or alternatively

char stringSource[20] = "";

Whatever suits best.

Depending on your environment, in the debug version, the compiler can intentionally fill the variables with values like 0xff so that the first 0-byte will always be beyond the limit.

If you put char stringDest[20] outside the function it will be a global variable, which are always iniatlized to 0 by the CRT startup.

In your last example, you should take a look at the description of strcpy_s.

This variant will cause an error if

destsz is less or equal strnlen_s(src, destsz); in other words, truncation would occur

strcpy_s(stringDest, 1, stringSource); means that 1 is smaller than your uninitialized stringlen, so it gives an error.

Note, that this parameter does not tell the function how many characters it should copy, but how big the destination is supposed to be. You don't tell it to copy only 1 character, you tell it, that it can copy at most 1 character ( which would be needed for the 0 byte anyway).

Devolus
  • 21,661
  • 13
  • 66
  • 113
  • Why does the *destination* string need to be initialized? – Scott Hunter May 12 '16 at 12:49
  • In this example it's not strictly neccessary, but it usually is good practice to initialize variables. – Devolus May 12 '16 at 12:50
  • @Devoius: Your answer does not distinguish between the initialization that is *required* and the one that is not. – Scott Hunter May 12 '16 at 12:52
  • @Scott Hunter I believe Fluter answered that bit, outside the main() function it's considered static and is initialised automatically. Though, it doesn't have the static keyword, that's something else... not sure. – Zebrafish May 12 '16 at 12:56
  • @Devious: No, that answer says *nothing* about the *destination*, because there is nothing wrong with it being uninitialized. – Scott Hunter May 12 '16 at 12:58
  • @ScottHunter, I removed that bit anyway as it is not relevant here. – Devolus May 12 '16 at 13:03
  • "because you are not initializing the source *and destination* strings" – Scott Hunter May 12 '16 at 13:10
  • @Devolus Funny, you'd think the size of the string would be more important than the size of the destination. I'm probably wrong, I'll need to read up on strcpy_s() – Zebrafish May 12 '16 at 13:12
  • @TitoneMaurice, that's the quote from the reference page. If I would want to copy N bytes, I would use `strncpy` instead (which also has some subtle pitfalls). – Devolus May 12 '16 at 13:13
  • @Devolus Yeah, just reading now, says: "destsz - maximum number of characters to write, typically the size of the destination buffer". That's strange. So specifying 1 doesn't copy one, I'm guessing it runs through all the source string until it hits zero, then why specify the size? It's ok, I'm just thinking to myself. – Zebrafish May 12 '16 at 13:19
  • @TitoneMaurice, I suppose the '_s' means that it does additional validations, like checking if the string even fits into the target. With `strncpy` you can get a similar functionality, without the hassle of not happening if the size doesn't fit. – Devolus May 12 '16 at 13:21