🎉 Celebrating 25 Years of GameDev.net! 🎉

Not many can claim 25 years on the Internet! Join us in celebrating this milestone. Learn more about our history, and thank you for being a part of our community!

C++: how does strncpy_s work?

Started by
6 comments, last by _Silence_ 5 years, 3 months ago

Hi, 

I'm having trouble to understand the behavior of strncpy_s. So I have char* dst which is later defined as an array of char with the size of 1024 (max possible length). I tried to fill with chars from char* src with the length of 442. So I use strncpy_s like this:


strncpy_s(dst, 442,src, 442);

It always returns an error (L'Buffer is too small). I don't get it since the destination length and source is the same, and dst is actually larger than src. But when I changed the code to this


strncpy_s(dst, 1024,src, 442);

the code runs without any error. Of course, I can simply put the maximum number (1024) on the destination length, but I wonder how it actually works. Can anyone help me with this?

Thank you

Advertisement

You need to take the string-terminator \0 into account. Also there are some special rules for strncpy_s() vs strncpy().

See: https://en.cppreference.com/w/c/string/byte/strncpy

Why are you manipulating zero-terminated char buffers, instead of using std::string?

 

I couldn't care less about the reputation system on these forums, but it would be better if the people who are downvoting my question could explain what they think is wrong with it. That way we can have a conversation.

The point of the question is in part to suggest a different approach to the OP, and in part it's an honest question: There might be advantages to this way of handling strings that I am not aware of. We could all learn something from the discussion.

 

I decided to keep using this one:


strncpy_s(dst, 1024,src, 442);

I've read somewhere that this is probably the best solution since there is no performance hiccup. I only need to make sure that the source does not exceed the length of the destination. Thanks for the reply :)

On 3/27/2019 at 8:06 PM, jt.tarigan said:

It always returns an error (L'Buffer is too small). I don't get it since the destination length and source is the same, and dst is actually larger than src. But when I changed the code to this

If there are 442 characters in a string (the length of actual characters, not including the null terminator), then the destination buffer needs to have enough space to store 443 characters so that it can write a null terminator at the end of the string. Some string handling functions would simply truncatthe string in this situation (drop the last character and replace it with the null terminator), while others would simple not write the null terminator at all and leave you in a very dangerous situation, but strncpy_s is specified to do nothing and return an error when the destination buffer is too small. The docs are here: https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strncpy-s-strncpy-s-l-wcsncpy-s-wcsncpy-s-l-mbsncpy-s-mbsncpy-s-l?view=vs-2017

When doing manual C-style string manipulation, it's important to know which of these functions deals with length in characters including the terminator, which ones work with length in characters without the terminator, and which ones deal with length in bytes of a memory allocation. Always check the documentation.

1 hour ago, jt.tarigan said:

I decided to keep using this one:



strncpy_s(dst, 1024,src, 442);

I've read somewhere that this is probably the best solution

To be frank, that's voodoo coding / cargo cult coding, and doing random things with raw byte array management that seems to work without deep understanding of why, is not a recipe for stable software. Memory corruption lies that way...

If dst is actually a static size of 1kb though, then yeah, this is correct -- "copy a C string of length 442 (not counting the terminator) into this 1kb allocation".

If dst is a fixed size array, like "char dst[1024]", then you can avoid human error by calling "strncpy_s(dst, src, length)" and the destination size of 1025 will be inferred automatically.

18 hours ago, alvaro said:

Why are you manipulating zero-terminated char buffers, instead of using std::string?

I couldn't care less about the reputation system on these forums, but it would be better if the people who are downvoting my question could explain what they think is wrong with it. That way we can have a conversation.

Sorry, I downvoted this because it's a straightforward question of 'how does this account function work', and that function has many uses in places where std::string is a shitty solution.

But, I didn't notice the OP has tagged this as a C++ question and is going to just arbitrarily tweak numbers to make it work, so, yeah, using std::string is entirely appropriate here ?

2 hours ago, Hodgman said:

Sorry, I downvoted this because it's a straightforward question of 'how does this account function work', and that function has many uses in places where std::string is a shitty solution.

While I agree with you globally (this might be faster, and you know exactly what you do and when you do it), it is also worth mentioning that keeping using low-level functions to play with strings will definitely lead to security issues. It might not be the case of the OP thought, but anyone has to keep in mind that 90% of network breaches are due to buffer overflow and other misuses of buffers. And since nowadays most games have any network / internet connection, I believe that the safest advice to give to new comers and not very experienced programmer is to use safe tools.

That's just my two cents.

This topic is closed to new replies.

Advertisement