QuaternionsRock
QuaternionsRock

Reputation: 922

What is the advantage of using `strncpy` over `snprintf`?

I just realized that I am in the habit of using snprintf for taking slices of strings, which now seems unconventional to me for several reasons (chiefly, the need to #include <stdio.h>). But after looking at strncpy more closely, I'm struggling to see why anyone would prefer it over snprintf.

To be clear, I'm comparing strncpy(dest, src, count) with snprintf(dest, count + 1, "%s", src). snprintf(dest, count + 1, src) obviously causes problems whenever src contains '%'.

What stood out to me most was how much more snprintf acts like an str- function than strncpy, which by comparison feels more like a mem- function to me.

Compare all of this to snprintf:

Why on earth is strncpy this way?

Upvotes: 0

Views: 994

Answers (2)

Steve Summit
Steve Summit

Reputation: 48010

What is the advantage of using strncpy over snprintf?

Virtually none. The only advantage of strncpy (over anything) is if you want to create what the C FAQ list calls a "now-obsolete data structure, the fixed-length, not-necessarily-\0-terminated 'string.'"

If your goal is to copy the string src to the destination dst, as a proper (but possibly truncated) string, and without overflowing dst's size dstsize, then

snprintf(dst, dstsize, "%s", src);

is actually a pretty good way to do it. (Not the only way, and not a bad way, but perhaps overkill.)

On the other hand, strncpy(dst, src, dstsize), is no solution to this problem at all.

Other possible solutions to this problem are

*dst = '\0'; strncat(dst, src, dstsize);

strlcpy(dst, src, dstsize);

strcpy_s(dst, dstsize, src);

Why on earth is strncpy this way?

In the early days of C and Unix, it was somewhat common to use short, fixed-length pseudo-strings which were not necessarily null-terminated, but which were padded out to their "full length" with extra \0's if necessary. This meant that you could efficiently compare them using the equivalent of memcmp; you didn't have to check for \0 at all. See also the footnote to the aforementioned C FAQ list entry.

strncpy is pretty much obsolete today; many programmers recommend avoiding it entirely.

Upvotes: 2

4386427
4386427

Reputation: 44329

What is the advantage of using strncpy over snprintf?

Well, it doesn't really make sense to compare two functions that are really intended to do different things.

strncpy is designed to write exactly n characters to dst. Some or all (but at least 1) of these n characters may be copied from the string src and the remaining characters (if any) will be null characters. Unfortunately many C programmers think that strncpy is a "safe" version of strcpy but it's not and it's not what was intended. The name strncpy is probably what is misleading.

snprintf is designed for a completely different task, i.e. formatted printing to a string.

Special use cases of sprintf like sprintf(dst, n, "%s", src) may seem equivalent to strncpy(dst, src, n) because the result dst when viewed as a "traditional C style" string looks the same. But it's not truely identical calls. They do different things to generate the result dst and dst may be different when looking at all array elements (see example in the end of this answer).

Why on earth is strncpy this way?

strncpy is handy when working with strings stored in fixed size arrays where no termination character is used and null padding is used for strings shorter than the array size.

Example of snprintf appearing to do the same as strncpy while it's not

int main ()
{
    char src[] = "Hi";
    char dst[6];
    strcpy(dst, "Hello");
    puts(dst);
    strncpy(dst, src, 6);
    puts(dst);
    puts("------------------");
    strcpy(dst, "Hello");
    puts(dst);
    snprintf(dst, 6, "%s", src);
    puts(dst);    
    return 0;
}

Output

Hello
Hi
------------------
Hello
Hi

From the output it seems that the strncpy call and the snprintf call did the same, i.e. changed dst from "Hello" to "Hi".

However, a closer look at dst - like

void pp(const char* p)
{
    for (int i=0; i < 6; ++i) printf("%02X ", p[i]);
    puts("");
}

int main ()
{
    char src[] = "Hi";
    char dst[6];
    strcpy(dst, "Hello");
    puts(dst);
    strncpy(dst, src, 6);
    puts(dst);
    pp(dst);
    puts("------------------");
    strcpy(dst, "Hello");
    puts(dst);
    snprintf(dst, 6, "%s", src);
    puts(dst);    
    pp(dst);
    return 0;
}

Output

Hello
Hi
48 69 00 00 00 00 
------------------
Hello
Hi
48 69 00 6C 6F 00 

shows that the two calls actually resulted in different dst values.

Upvotes: 3

Related Questions