tags:

views:

117

answers:

4
#include<stdio.h>

main()
{
 char str[50] = "Wel %s";
 char dst[50];

 snprintf(dst,50,str,"Come");
 //Now i want to append "*" to dst string ie "Wel Come*" using snprintf() 
 printf("str = %s\n",str);
 printf("dst = %s\n",dst);
}

please suggest is it possible using snprintf()

Thanks Surya

+1  A: 

you can use the %s format for this:

snprintf(dst, 50, "%s*", dst);

EDIT: This seems to have some undefined behaviors. The best thing would be to ask if it is really necessary to use snprintf instead of strncat.

Nathan Fellman
Hi Nathan FellmanThe output of the above operation would result in dst = *-Surya
Surya
@suryak: We assume that you want the output constructed with *two* snprintf's. `snprintf(dst,50,str,"Come");snprintf(dst, 50, "%s*", dst);` will indeed construct "Wel Code*"
Luther Blissett
@surya: I'm assuming you already executed the first snprintf
Nathan Fellman
@Nathan : Thats what i thought. Should work technically but doesn't work here http://codepad.org/ACvWNqKA.
Praveen S
@all: Looks like glibc has a problem with that.
Luther Blissett
This code has undefined behavior because the destination string and one of the arguments to be formatted overlap.
R..
Should change it to: `snprintf(dst+strlen(dst), 50-strlen(dst), "*");`
R..
+2  A: 

The obvious solution:

snprintf(dst,50,"%s*",dst);

is inefficient, because it makes an unnecessary copy of dst (into itself).

invokes undefined behavior as R. pointed out, because the arguments may not overlap (from man snprintf(3) on MacOSX):

"[...]or those routines that write to a user-provided character string, that string and the format strings should not overlap, as the behavior is undefined."

Posix says:

"If copying takes place between objects that overlap as a result of a call to sprintf() or snprintf(), the results are undefined."

snprintf returns the number of characters it has written, so you can do this instead:

 int k=snprintf(dst,50,str,"Come");
 // make sure that we do not pass potential disastrous values to snprintf, because 
 // the size argument is unsigned (size_t, 50-52 is a large positive number!) 
 // and we want 50-k to be in the range 0-50
 // k<0 means output error and k>50 means "output truncated". There is no point in 
 // appending anything in these cases anyway. 
 if (k<0 || k>50) 
 {
  fprintf(stderr,"output error or buffer too small");
 }    
 else k=snprintf(dst+k,50-k,"*");
 // check k for truncation here.

And then there's always strcat...And just in case, you overlooked it. You can have the * attached right in the first place:

main()
{
 char str[50] = "Wel %s*"; //<--!!!
[...]
Luther Blissett
Not only is it inefficient; it has undefined behavior because the destination string and one of the arguments occupy overlapping memory.
R..
`snprintf` **does not** return the number of characters written. It returns the number that **would have been written** if there were sufficient space. Using `dst+k` and `50-k` as you have done is extremely dangerous, and defeats the whole purpose of `snprintf`'s buffer size limit.
R..
"Wel %s" + "Come" was short enough so this could not happen. But I'll be adding a check to make the issue clearer.
Luther Blissett
Nice comments; reverted the -1.
R..
+2  A: 

This should work:

#include<stdio.h>

int main()
{
 char str[50] = "Wel %s";
 char dst[50];
 int len;

 snprintf(dst,50,str,"Come");

 //get size of current string
 len = strlen(dst);

 //add character to the end
 snprintf(dst + len, sizeof(dst) - len, "*");

 printf("str = %s\n",str);
 printf("dst = %s\n",dst);

 return 0;
}
Frank Bollack
On conforming platforms, `snprintf` will return the number of characters written to `dst`, not including the terminating null character.
dreamlax
@dreamlax: Added that, thanks.
Frank Bollack
It does not return the number written. It returns the number that **would have been written**, if the buffer had been sufficiently large to store the whole output, or -1 on error, including if the output size would exceed `INT_MAX`.
R..
Thus, this code has a **major bug**. If `len>sizeof(dst)`, the size argument to the second call will be huge (~4 billion on 32bit platform) and `snprintf` will write past the end of the buffer.
R..
@R: You are correct about `snprintf()`. I reverted the answer to its former version. But besides that, I usually see the answers on SO as sample code. Error checking for this simple scenario is superfluous as all string lengths are known. Handling input data is something different and was not in the scope of the question.
Frank Bollack
The problem isn't so much that **this specific usage** will crash, but that your answer includes dangerous misinformation about the return value of `snprintf`, as well as that people will inevitably copy your example with different data that might have overflow cases.
R..
Changed -1 to +1 because the code is now correct and it's the best answer I know of if you insist on using `snprintf` for concatenation.
R..
Actually this would also work if you define `MIN`: `len=MIN(snprintf(dst, 50, "%s*", dst), sizeof dst-1);` And it saves one `O(n)` operation (`strlen`).
R..
A: 

All the information is already available to you:

snprintf(dst + 8, sizeof(dst) - 8, "%s", "*");

You'd be better off doing:

strncat(dst, "*", sizeof(dst) - strlen(dst) - 1);
Matt Joiner
-1 for misuse of `strncpy`.
R..
oops........ :)
Matt Joiner
This works, but it's still not what `strncat` is intended for; thus the need for the ugly size computation. Use `strlcat` if it's available, or simply implement your own `strlcat` using `snprintf` or by hand..
R..