1

I have read the post sprintf format specifier replace by nothing, and others related, but have not seen this addressed specifically.

Until today, I have never seen sprintf used with only 2 arguments.
The prototype my system uses for sprintf() is:

int sprintf (char Target_String[], const char Format_String[], ...);

While working with some legacy code I ran across this: (simplified for illustration)

char toStr[30];
char fromStr[]={"this is the in string"};
sprintf(toStr, fromStr);

My interpretation of the prototype is that the second argument should be comprised of a const char[], and accepting standard ansi C format specifiers such as these.

But the above example seems to work just fine with the string fromStr as the 2nd argument.
Is it purely by undefined behavior that this works?, or is this usage perfectly legal?

I a working on Windows 7, using a C99 compiler.

Community
  • 1
  • 1
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • For a different reason, I'd say code is near a bug - I'd expect `char fromStr[]={"this is the in string"};` should be `char fromStr[]="this is the in string";` (no `{}`). Code does compile as I am sure OP intends, but to me it look like `char fromStr[]={"this is the in string"};` should make an array of 1 `char` with the value of a pointer converted to a `char`. I guess it is right, but looks wrong – chux - Reinstate Monica Feb 27 '16 at 21:47
  • @chux - Interesting. I was looking kind of down the same path trying to discover what failure modes, if any I could find. (kind of related to the _format string attack_, mentioned in comments below). I tried with `={"%this is the in string"};` which errored out with _unknown specifier_, then again with `={"%cthis is the in string"};`, which because %c is a known specifier, errored out with _Not enough parameters_. both of which seem to support the truth of the answers below. As for using the `{"..."};` brackets, they do not cause a problem for me. – ryyker Feb 27 '16 at 22:32

6 Answers6

2

Perfectly legal. The variadic arguments are optional.

In this case the printf serves as strcpy but parses the fmt string for % specifiers.

I'd write sprintf(toStr,"%s",fromStr); so it doesn't have to parse that long string.

BitWhistler
  • 1,439
  • 8
  • 12
1

The behavior you are observing is correct, a format string is not required to have any conversion specifiers. In this case the variable-length argument list, represented by ..., has length of zero. This is perfectly legal, although it's definitely less efficient than its equivalent

strcpy(toStr, fromStr);
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • 1
    @ryyker The two reasons why `sprintf` like that would fail is that the destination buffer has insufficient length, or the source is not properly terminated. Both situations would cause `strcpy` to fail as well, though. – Sergey Kalinichenko Feb 27 '16 at 21:11
  • You should make sure you don't have buffer overruns in all these sprintfs and strcpys. Better to use the **n** variants of these functions. – BitWhistler Feb 27 '16 at 21:13
1

It's perfectly legal code, but

  1. If you just want to copy a string, use strcpy() instead.
  2. If you are working with user input, you could be making yourself vulnerable to a format string attack.
Community
  • 1
  • 1
r3mainer
  • 23,981
  • 3
  • 51
  • 88
1

Synopsis for sprintf is:

int sprintf(char *str, const char *format, ...);

That means 2 arguments are legal option.

4pie0
  • 29,204
  • 9
  • 82
  • 118
1

It works because you have no further parameter (ie no control format %) to print.

It's no difference than printf without second parameter:

int printf ( const char * format, ... );

It also works if you don't have any second parameter:

printf(fromStr);
artm
  • 17,291
  • 6
  • 38
  • 54
1

the second argument should be comprised of a const char[]

A const specifier of a function argument guarantees that the function does not change the value of that argument (given it can change it which is the case on arrays because they are passed by address to the function). It does not require that a const value to be used on the actual call.

The code you posted do not use a const string as the second argument to sprintf() but the conversion from non-const to const is implicit; there is no need to worry there.

accepting standard ansi C format specifiers

"accepting" does not mean "requiring". The format string you specified does not contain any format specifier. Accordingly, the function is called with only 2 arguments (no values to format). A third argument would be ignored by sprinf() anyway and many modern compilers would issue an warning about it.


Update: I don't want to start a debate about which compilers are modern and which are not.

It happens that I'm using the default compiler on OSX 10.11 and this what it outputs:

axiac: ~/test$ cc -v
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.3.0
Thread model: posix
axiac: ~/test$ cc -o 1 1.c
1.c:8:25: warning: data argument not used by format string [-Wformat-extra-args]
    sprintf(x, "abc\n", n);
               ~~~~~~~  ^
axiac
  • 68,258
  • 9
  • 99
  • 134
  • I added an update to my answer. Couldn't add it as a comment because of the formatting. – axiac Feb 27 '16 at 21:23
  • I just tested with `gcc version 4.6.3 (Ubuntu/Linaro 4.6.3-1ubuntu5)`. It has the flag `-Wformat` that *"Check calls to "printf" and "scanf", etc., to make sure that the arguments supplied have types appropriate to the format string specified, and that the conversions specified in the format string make sense."*. Maybe your compiler also performs such checks but they are not enabled by default and you need to dig after the options into the documentation :-) – axiac Feb 27 '16 at 21:39