1

So the case was simple: I needed to add a character in front of a c string.

So I had my code:

char *a = "2233b";
char output[100];   
char toAdd = '-';

strcpy(output, a);
printf("\noutput=%s", output);
sprintf(output, "%c%s",  toAdd, a);
printf("\noutput=%s", output);

And the output was as expected:

output=2233b
output=-2233b

Ok, so far so good. Now the case changed and I wanted to add more characters before a c string, so I configured the code like so:

char *a = "2233b";
char output[100];   
char toAdd = '-';

strcpy(output, a);
printf("\noutput=%s", output);
sprintf(output, "%c%s",  toAdd, a);
printf("\noutput=%s", output);
sprintf(output, "%c%s",  toAdd, output);
printf("\noutput=%s", output);

I expected the output to be:

output=2233b
output=-2233b
output=--2233b

But this was not the case and the following output was printed on my screen:

output=2233b
output=-2233b
output=-------

Check working example here

Why is does output contain this value?
Because the format is just the character (%c) toAdd ('-') and a string (%s) which is output and contains ("-2233b").

So why isn't the last output containing "--2233b"? And how come the characters of output are all converted to '-'?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
moffeltje
  • 4,521
  • 4
  • 33
  • 57

3 Answers3

3

I believe the issue is in

 sprintf(output, "%c%s",  toAdd, output);

Here, you're reading from and writing to output, in a single sequenced statement. This invokes undefined behaviour.

To quote C11, chapter §7.21.6.6, the sprintf() function

If copying takes place between objects that overlap, the behavior is undefined.

Sourav Ghosh
  • 133,132
  • 16
  • 183
  • 261
1

This is invoking undefined behavior as is mentioned on the corresponding man page on *nix systems and in the C and also in the POSIX standard.

NOTES
   Some programs imprudently rely on code such as the following

       sprintf(buf, "%s some further text", buf);

   to append text to buf.  However, the standards explicitly  note  that  the
   results are undefined if source and destination buffers overlap when call‐
   ing sprintf(), snprintf(), vsprintf(), and vsnprintf().  Depending on  the
   version  of  gcc(1) used, and the compiler options employed, calls such as
   the above will not produce the expected results.

   The glibc implementation of the functions snprintf() and vsnprintf()  con‐
   forms  to  the  C99  standard,  that is, behaves as described above, since
   glibc version 2.1.  Until glibc 2.0.6, they would return -1 when the  out‐
   put was truncated.
lord.garbage
  • 5,884
  • 5
  • 36
  • 55
1

The standard prohibits passing buffers that overlap for input and output, so the behavior is undefined.

If you are interested in finding out why you get multiple dashes, here is what's going on: the problem is that when you pass output as an input to sprintf, the function behaves like a dog chasing its own tail: it writes the first character into output before reading its first character, and then its writing "front-runs" its reading as it tries, unsuccessfully, to process the %s format: new characters get added to the output right at the point where the function is reading.

To fix this problem, make a temporary copy of output before passing it to sprintf.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Well, that makes sense actually. Did you just assume this is the case (because what else could it be, huh?) or did you take a look at the source code? – moffeltje Jul 06 '15 at 13:58
  • 1
    @moffeltje I did not look up the source code, I just tried to think through implementing `sprintf` myself, and how my implementation would behave in a situation like this. – Sergey Kalinichenko Jul 06 '15 at 14:02