-2

I want to convert a number into a not zero-terminated char array without any predifined C / C++ function (e.g itoa). I don't have much space left (working on an embedded application with 5KB pgm space total of which I'm allready using 4.862KB) and my output function doesn't accept zero-terminated char arrays; only an array and the length.

EDIT 1: I'm not working for a company :P

EDIT 2: Doesn't accept means that for no reason it won't send if there's a 0-byte inside the array.

EDIT 3: I solved it using a modified version of the method from 'Vlad from Moscow' below. Still thanks to all of you, who tried helping me :)

EDIT 4: If anybody cares: The project is setting an AVR based alarm clock using bluetooth.

Benedikt M.
  • 145
  • 1
  • 13
  • 4
    Ok, did you try anything? – Oliver Charlesworth Jun 21 '14 at 17:09
  • Use (integer) division to get the digits, and put the corresponding `char` values for your output, that's it. – πάντα ῥεῖ Jun 21 '14 at 17:13
  • Can you please give me an example? – Benedikt M. Jun 21 '14 at 17:17
  • How can a function not accept zero-terminated arrays in this scenario? – mafso Jun 21 '14 at 17:21
  • 1
    A hint to get you started, ``i + '0'`` where ``i`` is a single base-10 digit will get you the ASCII/UTF8 character for that digit. – aruisdante Jun 21 '14 at 17:24
  • 2
    @mafso most embedded system's UART output hardware/interfaces work on bytes, not C-strings. So it's logical that the low-level output function you write for them takes in an arbitrary byte-array and a length, not a string. This lets you pass it ints, floats, strings, whatever as long as it's a byte-array. Your interpretation function on the receiving end still needs to know what the incoming data represents of course. – aruisdante Jun 21 '14 at 17:28
  • @aruisdante You're right, the function sends the char array through an UART Bluetooth module. The packets are max. 32 bytes in size, and the length will be transmitted as the first byte. So why should I use zero-termination? – Benedikt M. Jun 21 '14 at 17:29
  • 1
    @muessigb If you designed this protocol, for what reason in heaven you have chosen to transfer numbers in text format?!? That was probably a bad decision :/ ... Ah, the cheaper people gave some answers BTW ... – πάντα ῥεῖ Jun 21 '14 at 17:32
  • 2
    @πάνταῥεῖ is correct. I can understand converting to string if you're sending mixed-format data and you're always going to be printing it out in a terminal and thus it needs to be a string. But if it needs to be a string, it's going to need to be zero-terminated at some point. If it's not being received by something that is going to assume a c-string, then for any number > 999 (since you have a 1-byte header overhead), sending the value directly as a number is more space-efficient. Representing the maximum 32bit-int as text takes 10 bytes instead of 4. – aruisdante Jun 21 '14 at 17:39
  • @aruisdante: I totally understand why there are reasons to make a function not _expect_ zero-terminated arrays. But nonetheless, a function getting an array and a length shouldn't care for what's after that length, if accessing the byte after the last of the array is undefined behavior or zero, don't make a difference. – mafso Jun 21 '14 at 17:41
  • @mafso I imagine the point of his statement was more *I need to be able to track the length of the text, zero-terminating isn't sufficient* rather than saying that something would fail if the array was zero terminating. – aruisdante Jun 21 '14 at 17:42
  • @aruisdante; Yes, probably you're right. I just stumbled when I read "doesn't accept". – mafso Jun 21 '14 at 17:48
  • Have you tried `snprintf(buffer, buffer_size, "%d", 1234);`? – Thomas Matthews Jun 21 '14 at 18:16
  • There is also `std::ostringstream`, which you should try. – Thomas Matthews Jun 21 '14 at 18:17
  • @ThomasMatthews Seriously?!? This would surely overrun the OP's remaining code text space!! – πάντα ῥεῖ Jun 21 '14 at 18:20
  • @πάνταῥεῖ: Which version has overrun? The `snprintf`, note the 'n' after the `s`, is a function to print to the buffer with the limit as the 2nd parameter. We use this all the time safely in our embedded systems. The `ostringstream` uses the `string` data type which expands as needed. I don't see any buffer overrun here. – Thomas Matthews Jun 21 '14 at 18:24
  • @ThomasMatthews I'm not talking about buffer overruns, but code bloat! Would you say you can link s.th. like `snprintf()` using the left over approx. 120K?!? – πάντα ῥεῖ Jun 21 '14 at 18:27
  • 1
    Maybe codereview.SE would be a better place, since you already have working code, but need it optimized? I suggest you explain, in some detail, the rational for your code thus far; since you are asking here because of specific size limits that govern your necessary solution. Focus on reframing the question precisely to the well-described limits. – New Alexandria Jun 21 '14 at 18:31
  • @ThomasMatthews Sorry, meant leftover approx. 120 **bytes** of course! – πάντα ῥεῖ Jun 21 '14 at 18:38
  • @πάνταῥεῖ: Sorry, I didn't read the part about code running on a constrained system. – Thomas Matthews Jun 21 '14 at 18:39
  • @ThomasMatthews De Nada! I've been experiencing that even an (unintentionally left over from debugging) `#include ` statement could kill you on a small Cortex-M3 ;) ... – πάντα ῥεῖ Jun 21 '14 at 18:43
  • You should edit the title to clarify that you want to convert the number to its decimal string representation, I thought that you want to get the bytes of the integer to a byte array at first. Also, if you want to transfer via UART, why don't just directly trasmit those bytes? Or at least tramsit the hexadecimal number, it'll much faster than converting to decimal and don't need much code because it's just a direct mapping of the bits – phuclv Jun 22 '14 at 02:07

2 Answers2

2

As my hourly rate is equal to zero $ (I am unemployed) then I will show a possible approach.:)

In my opinion the simplest way is to write a recursive function. For example

#include <iostream>

size_t my_itoa( char *s, unsigned int n )
{
    const unsigned base = 10; 
    unsigned digit = n % base;
    size_t i = 0;

    if ( n /= base ) i += my_itoa( s, n );

    s[i++] = digit + '0';

    return i;
} 

int main() 
{
    unsigned x = 12345;
    char s[10];

    std::cout.write( s, my_itoa( s, x ) );

    return 0;
}

The output is

12345

Though I used unsigned int you can modify the function that it would accept objects of type int.

If you need to allocate the character array in the function then it will be even simpler and can be non-recursive.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • Thanks. I'll try yours. I just wrote a function for that, but yours seems to be smaller. – Benedikt M. Jun 21 '14 at 17:45
  • 1
    @vlad-from-moscow If you're currently unemployed, that would be an even better reason, to ask for money doing this. But well, you already know what our finally differences about this site are. I wish you good luck that the rep you gain here, somehow helps to boost your career. – πάντα ῥεῖ Jun 21 '14 at 17:59
  • Thanks again, I got your function to work in my Project :) It's nice that you helped for free; it's just a little project thats (mostly) for me and in any case I won't get a single buck out of it, so why invest one ;) – Benedikt M. Jun 22 '14 at 00:22
  • +1 for nice answer and use of `size_t`. Minor: could use `if ( n /= base ) i = my_itoa( s, n );`. `=` vs. `+=`. – chux - Reinstate Monica Jun 22 '14 at 12:42
0

A general algorithm:

Personally I can't recommend a recursive algorithm unless I knew the stack limits the embedded system imposes (PICs for example have a very limited stack depth) and what your current stack usage is.

orignumber = 145976

newnumber = orignumber 

newnumber = new number / 10
remainder = new number % 10    answer: 6
digit = remainder + 30 hex -> store into array  answer:0x36  ascii 6
increment array location

newnumber = new number / 10
remainder = new number % 10    answer: 7
digit = remainder + 30 hex -> store into array  answer:0x37  ascii 7
increment array location

newnumber = new number / 10
remainder = new number % 10    answer: 9
digit = remainder + 30 hex -> store into array  answer:0x39  ascii 9
increment array location

repeat these 4 steps while new number > 0  

Array will contain: 0x36 0x37 0x39 0x35 0x34 0x31
null terminal array, 

null termination allows easy calculation of string length and string reversal. Another option would be to fill the array from the end by decrementing a pointer to avoid reversing the string.

Array will contain: 0x36 0x37 0x39 0x35 0x34 0x31 0x00
finally reverse array

Array will contain: 0x31 0x34 0x35 0x39 0x37 0x36 0x00
user3750385
  • 13
  • 1
  • 4
  • You don't need to *calculate* the string length; you can simply pointer-subtract the end from the start to find out how many digits your loop stored. Or instead of reversing, store digits starting at the *end* of a buffer, like in the C and x86 asm functions in [How do I print an integer in Assembly Level Programming without printf from the c library?](https://stackoverflow.com/a/46301894) (most useful if you're going to copy somewhere else anyway, so can use the string regardless of where it starts.) – Peter Cordes Nov 29 '20 at 20:22