0

I am currently working on a personal AVR project and wrote a function for transmitting a double to my serial LCD screen using UART. For this, I convert a double into a char array and then transmit it char by char, serially.

What confuses me is what the length of the char buffer array has to be. The length of the array does not seem to correspond to the size of a double. Funnily enough, I can set the the length of the array to 0, and it will still work, like this:

    void USART_Transmit_Double(double doubleNumber) {

    char aNumberAsString[0];
    dtostrf(doubleNumber, 3, 0, aNumberAsString);

    USART_Transmit_String(aNumberAsString);
}

Here I am printing number 1234 in the main loop and it appears on my LCD just fine. If I change the precision to 1 however, the entire thing goes haywire.

I tested the same function on my Arduino and keep getting completely different results, on it each character seems to correspond to each element of the array, so if I wanted to print 1234.2, I would have to set the buffer length to 6.

So what's going on here?

Shibalicious
  • 288
  • 2
  • 4
  • 14
  • Can you please put the code segment shown _[here](https://i.stack.imgur.com/n35oJ.png)_ directly into the post? It will be easier to see in the context of your other content. – ryyker Jan 09 '18 at 14:37
  • Regarding _...to print 1234.2, I would have to set the buffer length to 6._: A buffer needed to contain the string `1234.2` needs to have room for 7 characters. A `C string` is an array of char terminated with the NULL character. – ryyker Jan 09 '18 at 14:39
  • @ryyker I'm not using strings though, I am iterating through a char array and transmitting char by char via serial. What's even weirder is that it works fine with the buffer having 0 length. – Shibalicious Jan 09 '18 at 14:43
  • 2
    Welcome to the world of *undefined behavior* – Kevin Jan 09 '18 at 14:43
  • @ryyker " A buffer needed to contain the string 1234.2 needs to have room for 7 characters", that's right.. But now I am even more confused, as I tried the same procedure with integers and itoa function, and it was one to one mapping, for example, to display 255, I would need to have the buffer length of 1. – Shibalicious Jan 09 '18 at 14:51
  • "so if I wanted to print 1234.2, I would have to set the buffer length to 6." A binary `double` cannot have the value of `1234.2`, just something close to it. "what the length of the char buffer array has to be." --> Suggest [Printf width specifier to maintain precision of floating-point value](https://stackoverflow.com/q/16839658/2410359) – chux - Reinstate Monica Jan 09 '18 at 14:53
  • You may be confusing the value necessary to create a buffer containing a single value, with the index used to access that value once the variable is created. i.e. `char aNumberAsString[1];` will create a variable that can contain a single char value. – ryyker Jan 09 '18 at 14:59
  • @Hypomania you can't assume that because it "works" this time it will work every time. Something as simple as calling another function could (but doesn't have to) break it. Undefined behavior means that anything can happen. Think about what the function does. It has to store those digits somewhere, so you need to have space for it. – Kevin Jan 09 '18 at 15:00
  • @ryyker okay, so if we go back to int, what should be my buffer length to create to display -128? On Arduino it works with 4, on my custom AVR it works with 1. As far as I know they both use the same itoa implementation. – Shibalicious Jan 09 '18 at 15:04
  • @Kevin I understand that, I just don't understand how something like an array with 0 elements can be created in the first place, and if it can, what does that mean? – Shibalicious Jan 09 '18 at 15:05
  • @Hypomania you need 1 byte per character plus 1 bytes for the null terminator. So in this case you need 5: `'-'`, `'1'`, `'2'`, `'8'`, and `'\0'`. – Kevin Jan 09 '18 at 15:06
  • @Kevin okay, that makes sense, I am still able to see the -128 on the Arduino with only 4 elements, but that's probably because null terminator is not just being skipped. – Shibalicious Jan 09 '18 at 15:11

1 Answers1

3

You are running into undefined behavior. In particular, in this link, the phrase: Examples of undefined behavior are memory accesses outside of array bounds,... would likely apply to your described scenario. It may work a thousand times if the memory you are attempting to access (but do not own) does not happen to violate another variable's memory space. The first time it does, your program will crash.

The following statement, although legal, creates a useless variable, and is likely the reason you are seeing odd results:

char aNumberAsString[0];

Addressing your comment:
You may be confusing the index value necessary to create a buffer containing a single value, with the index value necessary to access that value once the variable is created. i.e.

char aNumberAsString[1] = 'A'; //creates an array of 1 char ([1])

will create a variable that can contain a single char value.

char value = aNumberAsString[0]; // accesses the only value in array ([0])

EXAMPLE:
See an example of how to use the dtostrf function here.
Note the size of the variables this example is using:

static float f_val = 123.6794;
static char outstr[15];

to support the following call:

 dtostrf(f_val,7, 3, outstr);

Which produces the following output:

123.679
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Addressing the second part of your comment - no, I declaring a new char array with the length of 0. – Shibalicious Jan 09 '18 at 15:07
  • @Hypomania - Yes, that is the problem. An array with length == 0is useless. Either create a simple char variable, or create a char array with space for at least 1 value. – ryyker Jan 09 '18 at 15:11
  • Makes sense, but makes no difference anyway. There is no direct relationship between number of characters and my buffer length on my custom board (works fine on Arduino). – Shibalicious Jan 09 '18 at 15:15
  • 1
    @Hypomania - You are not getting it. If you are using a zero length array to pass even a single `char` value, you are invoking undefined behavior. It may appear to work hundreds of iterations, then all of the sudden, for no _apparent_ reason, your program will crash. It does not matter what system it works on now. Listen to the comments Kevin has made, and re-read my post. Both provide good advice to follow, even if it does not make sense right now. – ryyker Jan 09 '18 at 15:17
  • No, what I am trying to say is that even if I use 2 or 3 for a 5-6 digit number, it will still work. – Shibalicious Jan 09 '18 at 15:21
  • 1
    @Hypomania - Do you at least agree that `char aNumberAsString[0];` although a legal statement, is useless to use? – ryyker Jan 09 '18 at 15:22
  • I do. I also might have an idea why my other project behaves differently.. It seems like itoa and dtostfr are converting the numbers I'm passing into them directly to their ASCII equivalent, rather than the ASCII values that have one to one mapping.. So for example, if I pass 255 into itoa and store the result in an unsigned char array[1], that will work, as 255 is 0b1111111, exactly one byte, and passing 256 doesn't work. But that doesn't explain how I am able to see 255 on my screen instead of some weird ASCII character. – Shibalicious Jan 09 '18 at 15:30
  • @Hypomania - _[char data range](https://www.tutorialspoint.com/cprogramming/c_data_types.htm)_ is: char 1 byte -128 to 127 or 0 to 255 for unsigned. 256 not in range. And undefined behavior can do anything, including making something unexplainable show up on your screen. Sorry, gotta go. Good luck. – ryyker Jan 09 '18 at 15:36
  • Exactly, so if I perform this with an unsigned char array[1] and convert 255, it works, if I do it with 256 it doesn't.. Whereas on Arduino I would need unsigned char array[4] to make 255 work. Makes me think that Arduino converts them directly into ASCII characters, so 053048 for ASCII "2", whereas in my project it's 50 for ASCII "2" – Shibalicious Jan 09 '18 at 15:43