Like 1980s Internet protocol features the rationale for weird things in C is more often "That's how Unix works" than "This is actually a clever safety feature".
Like 1980s Internet protocol features the rationale for weird things in C is more often "That's how Unix works" than "This is actually a clever safety feature".
... AND where you want to pad the remaining space with zero bytes, so that you don't leak uninitialized memory onto the disk, or network.
The null byte padding behavior of strncpy makes it clear what the intended use was.
Also, the way C initializes character arrays from literals has strncpy-like behavior, because the entire aggregate is initialized, so the extra bytes are all zero:
char a[4] = "a"; // like strncpy(a, "a", 4);
char b[4] = "abcd"; // like strncpy(a, "abcd", 4);
the compiler could literally emit strncpy calls to do these initializations, so we might say that strncpy is a primitive that is directly relevant for run-time support for a C declaration feature.If you look at this function assuming it's a safety feature, that's a huge surprise, and indeed if you were skimming you might miss what it does because (in the context of "it's a safety feature") this is an insane choice. "Why would you do that?". Well, because it's not a safety feature.
The perf cost isn't what you'd expect from a safety feature either. Suppose we have a 1MB buffer, and we strncpy "THIS" into it using n = 1024. That's just four bytes right? Nope. strncpy() will write "THIS" and then 1020 zero bytes.
Calling a bespoke byte-sequence data structure a "string" is inaccurate. Treating strncpy() as a string function is erroneous and can easily lead to memory corruption.
Not only that, but (because of its actual purpose) it also fills the buffer with nuls, which is a complete waste of resources.
So yes, the original intent of strncpy() germane to the GPs comment, because it makes strncpy actively dangerous and complete shit when working with C strings.
It was completely clear, and can easily be inferred from its specified behaviour.
It's just completely useless nowadays, because its purpose is essentially obsolete, because the data type it works with is almost never used anymore.
strncpy works with fixed-size nul-padded fields as you'd find in e.g. mainframe-type software. That is why it:
- fills the destination buffer with NULs if the source is shorter
- does not nul-terminate if the source is the same size or longer than the destination
strncpy is essentially equivalent to zero-ing a buffer of size `n` then copying the first `n` bytes of src (up to the first nul) in the target
(I don't use strncpy and don't defend its functionality, just want to know what was intended when it was introduced.)
[1] "UNIX Implementation" by Ken Thompson, _The Bell System Technical Journal_, July-August 1978, Vol 57, No 6, Part 2, pg 1942.
This is mostly relevant to write into existing memory or memory-mapped records.
And on embedded devices you are often memory constrained so you might reuse an existing structure.
Some typo on the buffer limits and the same hazards as always.
Only fix is hardware memory tagging.
Systems which do/did include Burroughs Large Systems (now Unisys ClearPath MCP), IBM System/38 and AS/400 and IBM i (the RISC versions of which used PowerPC AS Tagged Memory Extensions), ARM MTE, SPARC ADI, and CHERI/ARM Morello.
What I meant by optimization is not treating them same as other structs, but e.g. guaranteeing pass-by-register like other primitive types, spelled out explicitly in the ABI. The choice of length field type would be size_t, obviously.
no-nul is not the goal of strncpy, it's the effect of strncpy.
strncpy is designed to work on fixed-size, nul-padded strings. That's why it fills the destination buffer with nuls if the source is too short, and it doesn't guarantee nul-termination (if the source is exactly the size of the destination).