11

As we all know, strcpy_s is a safety version of strcpy.

But I wonder how it works ...

let's see some examples.

strpy_s's declaration:
errno_t strcpy_s(_CHAR *_DEST, size_t _SIZE, const _CHAR *_SRC)

eg1

char dest[5];
char* src = "abcdefg";
strcpy_s(dest,5,src);

It will return an assertion.
I think I can understand this, use _SIZE to make sure we can't copy more characters than _SIZE

But.. I can't understand this:

char dest[5];
char* src = "abcdefg";
strcpy_s(dest,10,src);

we can still get a assertion, how did that happened?

ps,error was:

Debug Assertion Failed
expression : (L"Buffer is too small "&&0)


In VS2013

will strcpy_s checks the size of dest inside its body?? and if it's true , how? how to check a pointer like _DEST?

Joey
  • 228
  • 1
  • 2
  • 11
  • Read the documentation carefully: [MSDN](http://msdn.microsoft.com/en-us/library/td1esda9.aspx) – Jens Apr 26 '14 at 06:00
  • Your second example is wrong. The _SIZE parameter must correctly give the size of the buffer. If you give 10 for _SIZE but your buffer is only 5, then the result is undefined. There is no way strcpy_s can catch this error. – Brandin Apr 26 '14 at 11:46
  • actually there was an assertion . – Joey Apr 26 '14 at 23:17

4 Answers4

8

This is actually how to get the size of a stack array at run time without decaying it to a pointer:

template<typename T, size_t N> 
size_t arrSize(T (&array)[N])  
{
  return N;
}

You send it as a template reference, and the template mechanism deduces the size. So, you can do something like

int myArray[10];
cout << arrSize(myArray); // will display 10

So my guess is that this is how the "safe" MS strcpy_s is checking the sizes. Otherwise, if you pass just a pointer, there is NO STANDARD-COMPLIANT way of getting the size.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
4

In DEBUG mode, MicroSoft APIs fill the buffer with 0xfd, so they can check for an overflow.

This function doesn't truncate the copied string, but raises an exception!

It's always a pain to specify the size of the dest buffer (use _countof rather than sizeof), mostly when you use a pointer!

I have more problems with those "_s" APIs than with the standards ones!!

Romain
  • 3,586
  • 7
  • 31
  • 52
Ananké
  • 219
  • 2
  • 2
2

MSDN Says "The strcpy_s function copies the contents in the address of strSource, including the terminating null character, to the location that's specified by strDestination. The destination string must be large enough to hold the source string and its terminating null character. The behavior of strcpy_s is undefined if the source and destination strings overlap."

Santosh Dhanawade
  • 1,756
  • 14
  • 29
  • so it was not that "safe" ? – Joey Apr 26 '14 at 06:16
  • What is your definition of safe? – Lee White Apr 26 '14 at 06:16
  • when we try use this function like strcpy_s(dest,10,src); we will get exception or will do nothing – Joey Apr 26 '14 at 06:23
  • Considering that Microsoft created this function, shouldn't Microsoft be defining what "safe" means and explaining it in the documentation? If they want us to use it, then shouldn't they be explaining why it is "safer"? – shawn1874 Oct 11 '16 at 17:08
  • It's safe in the sense that undefined behavior can be prevented by avoiding buffer overruns and issues with null pointers. In the case of overlapping memory, undefined behavior can still occur. I'm not sure that Microsoft defined safe very well. It seems more about providing defined behavior for an error condition. If strcpy_s is used with a destination buffer that is too small, then the default behavior is still to abort the program but the buffer overrun is avoided. – shawn1874 Oct 11 '16 at 18:37
1

dest cannot hold more than 5 chars, that's why you get the error. It is not because of _SIZE. If dest was char* then you need to make sure you allocate enough memory for it, you won't get any compile error. But in your program dest has a fixed size, and strcpy_s, unlike strcpy, checks the size of the destination buffer (if it can, and in this case it can as its size is defined at compile time). Read this

http://www.cplusplus.com/forum/beginner/118771/

Basically strcpy_s is the "safe" version of strcpy, it doesn't allow you to overflow. From the standard: C (2011) and ISO/IEC WDTR 24731 - strcpy_s: a variant of strcpy that checks the destination buffer size before copying. Internally, probably strcpy_s asserts sizeof(dest)<SIZE.

vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • how to check the destination buffer size? – Joey Apr 26 '14 at 06:10
  • with `sizeof`, I just edited my comment :) But it will only work for statically defined arrays, not for pointers, for which the compiler does not know how much memory (if any) you have allocated to. – vsoftco Apr 26 '14 at 06:11
  • so we can't get the size of dest? – Joey Apr 26 '14 at 06:13
  • no, it can be converted to a pointer, however `sizeof(char[5])` is 5, and not `sizeof(char*)`, which is usually 32bit (or 64 bit if you have a 64 bit OS). Just do a `printf("%d ",sizeof(dest));` to convince yourself :) `sizeof` on any statically defined array will return the size of the array. – vsoftco Apr 26 '14 at 06:15
  • thank you.. I know this, but when we call a function ,use char[5] as an argument,it will become a pointer.. so can't get the size of it, was that correct? – Joey Apr 26 '14 at 06:18
  • I see... you're right actually... Not sure now how `strcpy_s` checks the size of `dest` inside its body... – vsoftco Apr 26 '14 at 06:27
  • I think it's some internal compiler enhancement done by Microsoft, somehow the functions have access to the sizes of static arrays, at least in Debug mode. If you compile in Release mode you won't probably get any error and you'll overflow the buffer. If I'm not wrong, I think that in standard C there is no way of determining the size of the array passed to a function inside the function. I can not think of any other explanation. – vsoftco Apr 26 '14 at 06:57
  • ah.. in Release mode .. still the same – Joey Apr 26 '14 at 08:24
  • @Joey you're not supposed to get the size of `dest`; you're supposed to pass it along with `dest` -- or, better yet, use a `std::string` instead. – AnotherParker Jul 24 '14 at 00:37