简体   繁体   中英

Does snprintf/sprintf overwrite the terminating-null of the penultimate argument, just like strcat does?

It is clearly written for strcat , eg here and here that, in case,

char *strcat(char *s1, const char *s2);

then,

The initial character of s2 overwrites the null character at the end of s1.

But apparently searching a little here for "concatenating strings/literals in C", I stumbled upon this , which states,

Avoid using strcat in C code. The cleanest and, most importantly, the safest way is to use snprintf:

So, is the same also true for snprintf/sprintf that the first character of the next argument overwrites the null-termination of the previous argument ? I can see no such reference in the documentation .

Empirically evidence seems to suggest, that both strcat & snprintf act the same way. Or is my assumption wrong?

#include <string.h>
#include <stdio.h>

int main(int argc, char const *argv[])
{
    printf( "Test Program Started\n");
    const char* first = "a";
    const char* second = "b";
    const char* third = "c";
    const int merged_length = (strlen(first) + strlen(second) + strlen(third) + 1); // +1 for null-termination

    char* catResult;
    catResult = malloc( merged_length * sizeof(char));
    strcpy(catResult, first);
    strcat(catResult, second);
    strcat(catResult, third);
    catResult[merged_length] = '\0';
    printf("catResult:%s \tstrlen(catResult):%d \t sizeof(catResult):%d\n", 
            catResult, 
            strlen(catResult), 
            sizeof(catResult));
    free(catResult);

    char* snprintfResult;
    snprintfResult = malloc( merged_length * sizeof(char));
    snprintf(snprintfResult, merged_length, "%s%s%s", first, second, third);
    // catResult[merged_length] = '\0'; // not necessary as per documentation
    printf("snprintfResult:%s \tstrlen(snprintfResult):%d \tsizeof(snprintfResult):%d\n", 
            snprintfResult, 
            strlen(snprintfResult), 
            sizeof(snprintfResult));
    free(snprintfResult);
} 

Test Program Started
catResult:abc strlen(catResult):3 sizeof(catResult):4
snprintfResult:abc strlen(snprintfResult):3 sizeof(snprintfResult):4

snprintf and sprintf do not append to a prior string the way strcat does. They start writing at the beginning of the buffer passed to them.

When writing multiple strings in a single call, as with the format string "%s%s%s" , they will write the strings consecutively, with no null characters between them, and ending with a null character.

If you want them to append to an existing string in a buffer named buffer , then determine the length of the string, say n , and pass buffer + n as the first argument instead of buffer . (For snprintf , note that n should also be subtracted from the second argument, which specifies how many bytes are available in the buffer.)

You cannot use sizeof like that. While sizeof("string") works as you expect, sizeof(string pointer) always returns the same value on any given platform (usually 4 or 8).

const int merged_length = (sizeof(first) + sizeof(second) + sizeof(third) + 1);

should be

const int merged_length = (strlen(first) + strlen(second) + strlen(third) + 1);

When you write %s to snprintf , it copies the string to the target string without any trailing null. When the null terminator at the end of the format string is reached, the output string is also null terminated.

So, the actual answer to your question is no, because the null was never written after first , but the final effect is more like if we answered yes, because both those code fragments do the same thing.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM