strncpy documentation question
Asked Answered
M

6

6

At the following regarding strncpy: http://www.cplusplus.com/reference/clibrary/cstring/strncpy/, it mentions the following:

No null-character is implicitly appended to the end of destination, so destination will only be null-terminated if the length of the C string in source is less than num.

What is meant by this sentence?

Myramyrah answered 22/1, 2011 at 14:8 Comment(6)
Long story short: avoid using strncpy as a secure strcpy, it's a different function for a different purpose, that exposes to other security risks.Gottuard
Is this tagged C++ intentionally? If you are using C++ I would avoid the C string functions entirely and use the C++ Std::String functions. Otherwise, I would remove the C++ tag...Encouragement
I disagree with removing the tag, @James, although not with your advice to use "proper" strings. However, while the vast majority of cases would probably be better off with C++ strings, there are still situations where the old C strings may be useful and they are part of the C++ standard, after all.Reliance
cplusplus.com is a site full of badly-worded, misleading, and often plain wrong information. I would avoid it and read the relevant standards documents instead.Fleece
I second the disrecommendation for cplusplus.com. For anything that's in C, prefer search results from opengroup.org to cplusplus.com. Comparing them on this example, cplusplus.com neglects to mention that behavior is undefined for overlapping source/destination. The Opengroup text is from Posix and more or less reproduces the C standard. For C++, good information in web page form is hard to find, so web search for the names of the classes/functions you need and then look them up in a book. Or PDF of the standard.Schism
@James. Yes, actually the C++ tag was intentionally used.Myramyrah
R
13

It means that, for example, if your source string is 20 characters plus a null terminator and your strncpy specifies less than 21 characters, the target string will not have a null appended to it.

It's because of the way it works: strncpy guarantees that it will write exactly N bytes where N is the length value passed in.

If the length of the source string (sans null byte) is less than that, it will pad the destination area with nulls. If it's equal or greater, you won't get a null added to the destination.

That means it may not technically be a C string that you get. This can be solved with code like:

char d[11];          // Have enough room for string and null.
strncpy (d, s, 10);  // Copy up to 10 bytes of string, null the rest.
d[10] = '\0';        // Then append null manually in case s was too long.

You allocate 11 bytes (array indexes 0..10), copy up to 10 (indexes 0..9) then set the 11th (index 10) to null.

Here's a diagram showing the three possibilities for writing various-sized strings to a 10-character area with strncpy (d, s, 10) where . represents a null byte:

s              d
-------------  ----------
Hello.         Hello.....
Hello Fred.    Hello Fred
Hello George.  Hello Geor

Note that in the second and third case, no null byte is written so, if you treat d as a string, you're likely to be disappointed in the outcome.

Reliance answered 22/1, 2011 at 14:13 Comment(6)
"you're likely to be disappointed in the outcome" - yeah, how come UB is always stupid nasal demons, and never for example a cake? Non-nasal cake, that is.Schism
@Steve: ha ha, good one. Actually, that may be the basis for a whole question (though, even as CW, it'd probably be shut down): "Has anyone ever experienced undefined behaviour that was good? No, not just that it did what you expected, but that it went above and beyond the call of duty."Reliance
@Steve, even when UB produces a cake, it tends to be demon-flavored, I'm afraidDamnedest
@bdonlan: or nasal-flavoured.Schism
@paxdiablo. Thanks for your reply. Can you just explain this line "d[10] = '\0';"? Especially that when "s" is too long, location "10" will already be filled? How can I append a "null" then? Thanks.Myramyrah
@SWEngineer: d[10] actually won't be filled in that case. Copying 10 bytes will fill d[0] through d[9] inclusive, leaving d[10] for the null. That's why you declare it int d[11] (0 through 10). Updated answer to clarify.Reliance
S
5

The string "foo" has 3 characters + 1 null-terminator (it's stored as "foo\0") giving a total length of 4. If you call strncpy with n=3 (or fewer) it won't append a null-terminator to the end of the target string, but will copy only "foo". Attempting to print the resulting string will result in undefined behaviour due to the lack of a null-terminator which signals the end of the string.

You have to be very careful of this and either pass n one greater than the maximum source or add the null-terminator yourself.

Sinfonietta answered 22/1, 2011 at 14:12 Comment(0)
G
4

That means that it copies the terminating null of the source string, but doesnt add a terminating null if the source string doesnt fit into the destination.

Goddord answered 22/1, 2011 at 14:11 Comment(0)
P
4

In C strings stored as arrays of char's and they are null-terminated, which means they have an extra 0 appended at the end, which marks the end of the string and could be used later to figure out the string's length. So the string "hello" looks like this in memory:

char hello[] = {'h', 'e', 'l', 'l', 'o', 0};

Normally, when you copy a string, the null character should be copied as well. So the memory needed for the string buffer is its length + 1 (e.g. (strlen(hello) + 1) * sizeof(char)).

Function strncpy allows you to copy only as many characters as it's possible to fit in the provided buffer. In case the buffer you provided is not big enough to hold that extra null, it won't be added. Or if the string is cut, it won't be null-terminated.

char hello[] = "hello"; // 5 characters, 6 bytes long
char hel[3];
strncpy(hel, hello, 3); // hel is {'h', 'e', 'l'}

You should be always careful after calling strncpy, since the result might not be a valid C string. If the string is not null-terminated it's impossible to know its length and most of the string manipulation functions would fail or would do something unexpected.

Partizan answered 22/1, 2011 at 14:23 Comment(2)
Please don't ever * sizeof(char), it reduces the life of your keyboard with extra keystrokes and your monitor with extra electrons needed :-)Reliance
Ok, maybe it should have been sizeof(hello[0]).Partizan
D
1

It means that only num bytes will be copied from the source buffer to the destination buffer; so if the source string length if upper or equal to num, the terminating NULL byte will not be copied, and the result will not have a NULL terminating byte, which is dangerous.

It is recommended to use strlcpy instead.

Dottydoty answered 22/1, 2011 at 14:17 Comment(1)
Recommended by who? Certainly not the glibc bods :-) My recommendation is to learn all the foibles of the tools you're using rather than relying on crutches. People who want that level of safety should leave the wonderful land of C and return to Visual Basic :-) And, please, note the smileys! I can't tell you how many arguments my dry sense of humor has started here :-)Reliance
N
0

The semantics of strncpy(), even when precisely explained as they are in the C++ reference above, are widely misunderstood. The behavior of this function is counterintuitive and error prone.

To avoid problems when using it or further down the development process, when the maintainer will misread the code and add more subtile bugs, there is a simple solution: NEVER EVER USE THIS FUNCTION.

You can read further details about this in this article by Bruce Dawson.

To answer your question: if the source string is longer than the size passed as a third argument (usually corresponding to the size of the destination buffer), the function will copy size characters to the destination and no null byte will be present among these. Calling strlen(destination); will then invoke undefined behavior because it will attempt to read beyond the end of the array until it finds a null terminator. This specific behavior is what makes strncpy so error prone.

Nonresident answered 9/12, 2016 at 21:41 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.