sprintf with width parameter, WTF?

For the life of me I can’t get sprintf working with a width parameter. Small test code follows

static	char	temp_string[50];

void	HexDump () {
   for (int i = 0; i < 16; i++) {
        Serial.print (temp_string[i], HEX);
        Serial.print (" ");
    }    
    Serial.println ("");
}
void setup () {
 
  Serial.begin (115200);
  Serial.println ("Start");
  
  sprintf (temp_string, "%03X", 0xAA);  // works
  Serial.println (temp_string);
  HexDump();

  sprintf (temp_string, "%0*X", 3, 0xAA); // doesn't work
  Serial.println (temp_string);
  HexDump();
  
}
    
void loop () {};

Output as follows

Start
0AA
30 41 41 0 0 0 0 0 0 0 0 0 0 0 0 0

0 41 41 0 0 0 0 0 0 0 0 0 0 0 0 0

Note that the second sprintf uses a width parm, it prints nothing and that’s because bit0 of the string has been dorked with a \0.

Surely something this simple can’t be a compiler bug, so can any C experts tell me what I’ve stuffed up?


Rob

I've tested your code on codepad.org http://codepad.org/oZ4iDLCq

int main()
{
  char temp_string[256];
  sprintf(temp_string, "%03X", 0xAA); // your version 2
  printf("%s\r\n", temp_string);
  sprintf(temp_string, "%0*X", 3, 0xAA); // your version 2
  printf("%s\r\n", temp_string);
  sprintf(temp_string, "%*X", 3, 0xAA); // fixed version
  printf("%s\r\n", temp_string);
  return 0;
}

outputted

0AA
0AA
 AA

The zero before the star is causing the bug.

Could this be related to the fact that only a subset of sprintf is linked? If you try “%f” you won’t get anything either since floating point printing is not linked in by default.

@frank But that's a different platform eh? I've tried without 0 but same problem.

@liudr Could be.


Rob

I googled "printf" and this was the first result:

http://www.cplusplus.com/reference/clibrary/cstdio/printf/

According to that:

Flags: ... 0 Left-pads the number with zeroes (0) instead of spaces, where padding is specified (see width sub-specifier).

(my emphasis)

Slightly further down:

width description (number) Minimum number of characters to be printed. If the value to be printed is shorter than this number, the result is padded with blank spaces. * The width is not specified in the format string, but as an additional integer value argument preceding the argument that has to be formatted.

(my emphasis again)

I read this as that the zero-padding only applies for specified padding, not *-padding.

I admit the documentation is obscure, and what you have possibly found is a library bug, rather than an error in your interpretation.

I believe this is the official source code for AVR LIBC... http://download.savannah.gnu.org/releases/avr-libc/

I cannot find support for asterisk in the printf family. I'm afraid it isn't a bug but simply a lack of code.

Thanks guys, guess I can feel a work-around coming on.


Rob

You probably should get a second opinion. I could have been in the wrong place or looked at the wrong source files. I think this was the third time ever I've been to the AVR LIBC website.

At a minimum, try this...

  // Your code included here as a place-marker
  sprintf (temp_string, "%0*X", 3, 0xAA); // doesn't work
  Serial.println (temp_string);
  HexDump();

  // So you know where to put this snippet
  sprintf (temp_string, "%z", 3, 0xAA); // illegal specifier
  Serial.println (temp_string);
  HexDump();

If the output is identical to the "%0*X" format string then there is a good chance I'm correct.

Produces

Start
0AA
30 41 41 0 0 0 0 0 0 0 0 0 0 0 0 0

0 41 41 0 0 0 0 0 0 0 0 0 0 0 0 0

0 41 41 0 0 0 0 0 0 0 0 0 0 0 0 0

Rob