Link to home
Start Free TrialLog in
Avatar of Stephen Kairys
Stephen KairysFlag for United States of America

asked on

sprintf: same variable for source and target

Using C....

I have a varible containing a 7-digit phone number e.g. 5551212 that can hold up to 10 chars. My system has a default area code of 3 digits e.g. 631.  I need to prefix the phone number with the areacode, giving me a resultant string of 6315551212.

Is the following legal

sprintf(phonenum, %3.3s%7.7s, default_area_code, phonenum);

in other words having phonenum as both the source and target.

Or, doI need to use an intermediate variable e.g.

sprintf(temp_phonenum, %3.3s%7.7s, default_area_code, phonenum);
strcpy(phonenum_temp_phonenum);

Please advise.
Thanks.



Avatar of phoffric
phoffric

Below is my preference to avoid any possibility about overwriting phonenum. You probably know about the extra terminating null byte so that phonenum has to be a 10+1 char array.
sprintf(temp_phonenum, "%3s%7s", default_area_code, phonenum);
strcpy(phonenum, temp_phonenum);

Open in new window

Avatar of Stephen Kairys

ASKER

Phoffric-
phonenum is indeed declaed as
char phonenum[11];

Gvein that, do you still have the same concern about possibly overwriting phonenum?
Thanks.


>> do you still have the same concern about possibly overwriting phonenum?
With the temp_phonenum variable, you are definitely safe.

With your first choice, it appears to me that default_area_code gets written into the first 3 chars of phonenum, thereby clobbering phonenum. However, it will only take you a minute to verify. The temp approach is definitely cleaner looking to me.

Except for certain cases I try to avoid having the same variable used in one statement. Know good cases are statements like:  a = a+ y (or equivalently, a+= y).
On a side topic, I don't think you need the precision .3 in the sprintf format, but if you think a string could have a leading 0, you should have "%03s%07s".
Just to be safe, I prefer using %3.3s and %7.7s. That way there is no risk of string overflow.
I want to accept your solution with the %3s%7s but would feel more comfortable if the 3.3 and 7.7 were used. Wouild you be willing to repost that solution with my ammendment to it? Then, I can close out this question andwe can both move on to other things. :)

Thanks
Well, OK, but overflows? Hopefully your area code and your 7 digit phone numbers could never have overflows, and if they ever could, I would hope that you would have a way to identify this before going into the sprintf call. So, before the sprintf, you should have assertions about the length of the two variables to protect against hiding extra digits in the numbers. Since the precision does prevent buffer overflow, I can see why you want to keep the precision in the format.

// add assertions to verify that the area code and phonenum strings
//   have the correct string lengths
sprintf(temp_phonenum, "%03.3s%07.7s", default_area_code, phonenum);
strcpy(phonenum, temp_phonenum);

Open in new window

Better, but my data never would have leading zeroes. (And when I tried %03.3s, it did not display with a leading zero for the value "12"; rather, it displayed "<blank>12".  I'm guesing that the leading 0 does notg work for strings, so maybe we should take it out and just go with 3.3s and 7.7s. Thanks.
Just to be absolutely clear :

>> sprintf(phonenum, "%3.3s%7.7s", default_area_code, phonenum);

has undefined behavior, and will very likely not do what you intended, due to the way the sprintf function commonly works internally.
Don't do this :)

What you can do instead, is this :
memmove(phonenum + 3, phonenum, 8);
strncpy(phonenum, default_area_code, 3);

/* or alternatively (without using a temporary buffer) : */
int i = 0;
for (i = 7; i >= 0; --i) {
  phonenum[i + 3] = phonenum[i];
}
strncpy(phonenum, default_area_code, 3);

Open in new window

OK,
Here's the output if I intentionally try to overflow. So, you are protected.

1231234567

   char temp_phonenum[11];
   char default_area_code[] = "1234";
   char phonenum[] = "123456789";
   sprintf(temp_phonenum, "%3.3s%7.7s", default_area_code, phonenum);
   strcpy(phonenum, temp_phonenum);
   printf(phonenum);

Open in new window

ASKER CERTIFIED SOLUTION
Avatar of phoffric
phoffric

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
If you made sure that the lengths of the input strings are ok, you could do

  if (strlen(phonenum) <= 7 && strlen(default_area_code) == 3)
  {
      memmove(&phonenum[3], phonenum, 8);
      memcpy(phonenum,  default_area_code, 3);
  }

Note, with memmove you can copy within one buffer while with memcpy the buffers may not overlap.
Phoffric - this looks good; I was surprised that you did not include a format specifier for the final printf, but it seems to work....I never knew that you could get away w/o havinga format specifier. What's the deal with that? Thanks.
>> I never knew that you could get away w/o having a format specifier. What's the deal with that?
No magic. The format specifier is a required field. It is defined to be a string with option translation tokens beginning with % that correspond to optional parameters to be embedded in the output stream, right?

printf("Hello World\n");
The format specifier is "Hello World\n".

char hiThere[ ]  = "Hello World\n";
printf( hiThere );

No difference. Either way the string format specifier is provided. This means that you can dynamically create format specifiers, if need be.
OK, phoffric. I am going to accept your solution, while noting that printf(phonenum(; will not include a new line (\n).

itsmeandnobodyelse: and Infinity08 - thanks for the input, but as you guys came in after phoffric, and I don't have time to try out your soln's,, I'm going with phoffric. Thanks for your efforts to help,though.

>> itsmeandnobodyelse: and Infinity08 - thanks for the input, but as you guys came in after phoffric, and I don't have time to try out your soln's,, I'm going with phoffric. Thanks for your efforts to help,though.

No worries. The only reason I posted, was to make it clear beyond doubt that the code from the question has undefined behavior, and should thus not be used.
In case you were interested in a solution without a temporary buffer, I also included alternative approaches.
Thanks, Infinity08. I apprecaite it.
No worries from my side either.

The sprintf is a valid (and commonly used) solution. My code using memmove and memcpy only should make clear that the operations made by sprintf would not allow to use the same buffer for input and output and that the only way to achieve that is to manipulate the phonenum buffer yourself.
OK - thanks for the input.