0

It is clearly written for strcat, e.g. 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

Duck Dodgers
  • 3,409
  • 8
  • 29
  • 43
  • @user694733, wooops! my bad! I will edit and correct it. One second. – Duck Dodgers Sep 08 '20 at 15:12
  • 1
    @user694733, In the original test-code I had here on the PC I had used `strlen`. Then I wanted to test with `sizeof` and forgot to undo that change before pasting here. Better now? – Duck Dodgers Sep 08 '20 at 15:15
  • The incorrect usage of `snprintf` (similar to `strcat`) is `snprintf(buf, buflen, "%s%s", buf, more_stuff);`. That will "work" with some C library implementations and not with others, but it's Undefined Behaviour according to the standard. – rici Sep 08 '20 at 15:53
  • @rici, ok. I understand. The source and the destination cannot be overlapping, as you show in the snippet in your comment. – Duck Dodgers Sep 08 '20 at 18:55

2 Answers2

2

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.)

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • `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` --> that was exactly my question. Thanks for the quick answer. – Duck Dodgers Sep 08 '20 at 15:19
0

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.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • `When you write %s to snprintf, it copies the string to the target string without any trailing null.` --> that was exactly my question. `So, the actual answer to your question is no, because the null was never written after first` --> thanks for clearing that up. – Duck Dodgers Sep 08 '20 at 15:18