tags:

views:

285

answers:

9

I have a function in C which generates a number of hours from a rtc peripheral, which I then want to fill an array within a struct object. The array is set up to take 5 digits, but I need to prepend leading zeros to the number when it is less than 5 digits.

Could anyone advise on an easy way of achieving this?

+12  A: 
char array[5];
snprintf(array, 5, "%05d", number)
Aaron
%05d would add the leading zeros
rikh
@rikh - Yeah... I re-edited that right after hitting 'post'. Still too early in the morning...
Aaron
The user wants to get 5 digits, so we need a buffer of 6, not 5 (including the NULL-byte) Additionally it should print leading zeros.char array[6];snprintf(array, 6, "%05d", 1234);
johannes
He didn't specify that he was using it as a C string - he said he was filling in an array which takes 5 digits - that's exactly what this does.
Aaron
... before corrupting the stack.
Thomas
@Aaron: To emphasize what Thomas said, you are writing six characters (five digits and a null-byte terminator) into an array of five characters. The null-byte terminator is going somewhere, and so one byte outside the array will be zeroed out. Exactly what that will do is up for question in any particular instance (it's undefined behavior), but it's very likely not to be good.
David Thornley
@David Thornley and @Thomas - you are absolutely wrong. Note the '5' in the snprintf(). From the snprintf() docs: "If len > count, then count characters are stored in buffer, no null-terminator is appended, and a negative value is returned." 5 characters will be stored in the buffer with no null-terminator.
Aaron
@Aaron - you're quoting from docs for MSVC's very broken implementation of `_snprintf()`, not the real `snprint()`, which says: "output characters beyond the n-1st are discarded rather than being written to the array, and a null character is written at the end of the characters actually written into the array". You need to add space for the '\0' terminator. Don't use MSVC's `_snprintf()`. As I said, it's broken - use something like http://www.jhweiss.de/software/snprintf.html instead
Michael Burr
@Michael Burr - Based on your quoted doc (and from a test on linux I just tried) I agree that the result will not be what I wanted (on linux array[4] == '\0'). However - this will not corrupt the stack nor write outside the array as @Thomas and @David Thornley asserted. From the linux man page on snprintf(): "The functions snprintf() and vsnprintf() do not write more than size bytes (including the trailing '\0')."
Aaron
@Aaron - I agree that there's no buffer overflow. It's just that MS's `_snprintf()` behaves subtly different (in a very bad way in my opinion) than everyone else's `snprintf()`. So it's a good idea to just not use it. Even in programs that are intended only for Windows, I think.
Michael Burr
Oh. Right. Silly me. Sorry!
Thomas
@Michael Burr - fair point.
Aaron
A: 

Initialize every element in the array to 0 before inserting the digits into the array. That way you're guaranteed to always have 5 digits with leading zeroes.

Welbog
A: 

If the type is int, then no. Set the type to string?

BenB
+1  A: 

like @Aaron's solution, the only way to do it in C is to treat it like a character instead of a number. Leading zeros are ignored when they occur as a value and indicate an octal constant when appearing in code.

int a = 0000015; // translates to decimal 13.
Kelly French
You've been using Twitter waaayyy too much.
Daniel Bingham
@Alcon Nahh.. 213 char in his main text is waaayyy too long for a twit.
lsc
@Alcon, you must have waaayyy more followers than I do. :)
Kelly French
A: 

try this :

    char source[5+1] = { '1','2', 0, 0, 0, 0 };
    char dest[5+1];
    int nd = strlen(source) ;
    memset ( dest, '0', 5 - nd );
    sprintf ( dest+nd+1, "%s", source );
lsalamon
A: 

Doesn't simple total bzero() or something like this and then filling with new value suit you? Could you please describe more details if not?

I'm not joking, I have seen several cases when total filling with 0 is faster then tricks to add 0s, especially if memory was cached and bzero() had some burst write support.

Roman Nikitchenko
A: 
int newNumber [5]  = 0;

for ( int i=0; givenNumber != 0 && i < 5 ; i++ ) {
    newNumber[i] = givenNumber%10;
    givenNumber = givenNumer/10;
}
A: 

apologies for the lack of detail. My code generates a 32-bit integer, which I am expecting to only get to 5 decimal digits in size. I then call the function:

numArray = numArrayFill(12345, buf, sizeof(buf));

(uint32_t number, char *buffer, int size) 
{
    char *curr = &buffer[size];       
    *--curr = 0;          
    do 
    {
        *--curr = (number % 10) + '0';
        number /= 10;
    } 
    while (number != 0);

    if (curr == buffer && number != 0)   
    return NULL;

    return curr;
}

The problem comes when I put in a less than 5 digit number and I get random behaviour. What I need to do is to append zeros to the front so that it is always a 5 digit number.

Thanks Dave

Like I said in my answer above - you can do this much easier with snprintf(). In this example instead of calling numArrayFill() you can just call snprintf(buf, sizeof(buf), "%0*d", sizeof(buf), 12345) directly.
Aaron
the only problem with that is that I don't have printf available to me. This is an embedded target with limited resources
That would have been a useful thing to put in the original question... :)
Aaron
Since you don't have printf() after your original while() loop you need to add something like: while(cur > buffer) *--cur = '0';
Aaron
@unknown - you should edit your answer to include this instead of putting it in an answer.
Michael Burr
+1  A: 

A couple little routines that'll do what you want without having a printf() library hanging around. Note that there's not a way for the routines to tell you that the int you've passed in is too big for the buffer size passed in - I'll leave adding that as an exercise for the reader. But these are safe against buffer overflow (unless I've left a bug in there).

void uint_to_str_leading( char* dst, unsigned int i, size_t size )
{
    char* pCurChar;

    if (size == 0) return;

    pCurChar = dst + size;  // we'll be working the buffer backwards

    *--pCurChar = '\0';   // take this out if you don't want an ASCIIZ output
                          //   but think hard before doing that...

    while (pCurChar != dst) {        
        int digit = i % 10;
        i = i /10;

        *--pCurChar = '0' + digit;
    }

    return;
}


void int_to_str_leading( char* dst, int i, size_t size )
{
    if (size && (i < 0)) {
        *dst++ = '-';
        size -= 1;

        i *= -1;
    }

    uint_to_str_leading( dst, i, size);

    return;
}

Note that these routines pass in the buffer size and terminate with a '\0', so your resulting strings will have one less character than the size you've passed in (so don't just pass in the field size you're looking for).

If you don't want the terminating '\0' character because you're dealing with a fixed char[] array that's not terminated, it's easy enough to take out that one line of code that does the termination (but please consider terminating the char[] array - if you don't, I'll bet you'll see at least one bug related to that sometime over the next 6 months).

Edit to answer some questions:


dst is a pointer to a destination buffer. The caller is responsible to have a place to put the string that's produced by the function, much like the standard library function strcpy().

The pointer pCurChar is a pointer to the location that the next digit character that's produced will go. It's used a little differently than most character pointers because the algorithm starts at the end of the buffer and moves toward the start (that's because we produce the digits from the 'end' of the integer). Actually, pCurChar points just past the place in the buffer where it's going to put the next digit. When the algorithm goes to add a digit to the buffer, it decrements the pointer before dereferencing it. The expression:

*--pCurChar = digit;

Is equivalent to:

pCurChar = pCurChar-1;  /* move to previous character in buffer */
*pCurChar = digit;

It does this because the test for when we're done is:

while (pCurChar == dst) {   /* when we get to the start of the buffer we're done */

The second function is just a simple routine that handles signed ints by turning negative numbers into positive ones and letting the 1st function do all the real work, like so:

  • putting a '-' character at the start of the buffer,
  • adjusting the buffer pointer and size, and
  • negating the number to make it positive
  • passing that onto the function that converts an unsigned int to do the real work

An example of using these functions:

char buffer[80];

uint_to_str_leading( buffer, 0, 5); 
printf( "%s\n", buffer);

uint_to_str_leading( buffer, 123, 6); 
printf( "%s\n", buffer);

uint_to_str_leading( buffer, UINT_MAX, 14); 
printf( "%s\n", buffer);

int_to_str_leading( buffer, INT_MAX, 14); 
printf( "%s\n", buffer);

int_to_str_leading( buffer, INT_MIN, 14); 
printf( "%s\n", buffer);

Which produces:

0000
00123
0004294967295
0002147483647
-002147483648
Michael Burr
please bear with me if these questions seem ridiculously basic, but I am fairly new to C, having just got my head around pass pointers by reference..In your first function, what is the purpose of pCurChar, is that the char array which I pass back to my main function? Also, what does dst do? Your second function is very much beyond me..
I've updated the answer to address your questions.
Michael Burr
thanks for the answer and the detailed explanation - I really appreciate it. Problem solved!