0

I was looking at a project and came across the following code and am unable to figure out what the sprintf is doing in this context and was hoping someone might be able to help me figure it out.

char storage[64];
int loc = 0;
int size = 35;

sprintf(storage+(loc),"A");  //Don't know what this does
loc+=1;
sprintf(storage+(loc),"%i", size);  //Don't know what this does
loc+=4;
sprintf(storage+(loc), "%i", start); //Don't know what this does

start += size;
loc += 3;

The code later does the following in another part

string value;
int actVal;
int index = 0;
for(int j = index+1; j < index+4; j++)
{
    value += storage[j]; 
}
istringstream iss;
iss.str(value);
iss >> actVal; //Don't understand how this now contains size

The examples I have seen online regarding sprintf never covered that the above code was possible, but the program executes fine. I just can't figure out how the "+loc" affects storage in this instance and how the values would be saved/stored. Any help would be appreciated.

K. Fenster
  • 91
  • 3
  • 15
  • 1
    the short answer is any pointer plus an integer returns a pointer advanced that many elements. its basically telling sprintf to start printing `loc` spaces into the buffer. – kmdreko Nov 22 '16 at 02:48
  • 1
    Why would the same codebase use `istringstream` in one part, but not `ostringstream`, thus not needing to use `sprintf`? – PaulMcKenzie Nov 22 '16 at 02:48
  • `sprintf` is a C library function, that is of very little use in modern C++ code, except for creating multiple opportunities of buffer overflows; and serves very little purpose other than being a never-ending source of bugs. – Sam Varshavchik Nov 22 '16 at 02:48
  • You could rewrite `storage+(loc)` as `&(storage[loc])`. – 001 Nov 22 '16 at 02:48
  • 1
    Just hope `size` is between [-999, 9999] – xiaofeng.li Nov 22 '16 at 03:10
  • `storage + (loc)` is equivalent to `&storage[loc]`. Since `storage` is an array, `sprintf(storage+loc, "A")` writes the character `'A' to `storage[loc]` and (since `sprintf()` appends a trailing `'\0'`) a `'\0'` to `storage[loc+1]`. – Peter Nov 22 '16 at 05:51

3 Answers3

0

Ugly code! Regardless, for the first part, storage+(loc) == &storage[loc]. You end up with a string "A35\0<unknown_value>1234\0", assuming start = 1234, or in long form:

sprintf(&storage[0],"A");
sprintf(&storage[1],"%i", size);
sprintf(&storage[5], "%i", start);

For the second part, assuming we have the "A35\0<unknown_value>1234\0" above, we get:

 value += '3';
 value += '5';
 value += '\0';
 value += '<unknown_value>'; // This might technically be undefined behaviour

So now value = "35". [1]

iss.str(value);
iss >> actVal;

This turns the string into an input stream and reads out the first string representing an integer, "35", and converts it into an integer, giving us basically actVal = atoi(value.c_str());.

Finally, according to this page, yes, reading an uninitialised ("indeterminate value" is the official term) array element is undefined behaviour thus should be avoided.

[1] Note that in a usual implementation, there is a theoretical 10/256 chance that the <unknown_value> could contain an ASCII digit, so value could end up being between 350 and 359, which is obviously not a good outcome and is why one shouldn't ignore undefined behaviour.

Community
  • 1
  • 1
Ken Y-N
  • 14,644
  • 21
  • 71
  • 114
0

The function sprintf() works just like printf(), except the result is not printed in stdout, rather it is store in a string variable. I suggest you read the sprintf() man page carefully:

https://linux.die.net/man/3/sprintf

Even if you are not on a Linux, that function is pretty much similar across different platforms, be it Windows, Mac or other animals. That said, this piece of code you have presented seems to be unnecessarily complicated.

The first part could be written as:

sprintf(storage,"A %i %i", size, start);

For a similar-but-not-equal result, but then again, it all depends on what exactly the original programmer intended this storage area to hold. As Ken pointed out, there are some undefined bytes and behaviors coming from this code as-is.

Alexei Z
  • 79
  • 1
  • 3
0

From the standard:

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

Write formatted data to string Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by str.

sprintf(storage+(loc),"A");

writes "A" into a buffer called storage. The storage+(loc) is pointer arithmetic. You're specifying which index of the char array you're writing into. So, storage = "A".

sprintf(storage+(loc),"%i", size);

Here you're writing size into storage[1]. Now storage = "A35\0", loc = 1, and so on.

Your final value of storage = "A35\0<garbage><value of start>\0"

actVal: Don't understand how this now contains size

The for loop goes through storage[1] through storage[5], and builds up value using the contents of storage. value contains the string "35\0<garbage>", and iss.str(value) strips it down to "35\0".

iss >> actVal

If you have come across std::cin, it's the same concept. The first string containing an integer value is written into actVal.

aspen100
  • 965
  • 11
  • 22