-3

If I understand correctly, default Arduino versions of printf() and related functions do not support conversion of float type variables, as noted in this thread and many other places. A common suggestion is to use dtostrf() to convert the float variable to a char array variable then use that in printf().

I tried to be clever and make my code more compact by defining a similar function which, rather than modifying a passed-by-reference buffer, generates and returns a pointer to a char array so that I could use the returned char array directly inline inside my printf's. Based on this thread, I prototypes the following (demo):

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

const char* floatToStr(float val, int decimalPlaces);

int main()
{
    float speed[] = {234.5634, 43.234};
    printf("speed0: %s, speed1: %s\n", floatToStr(speed[0],2), floatToStr(speed[1],2));
    return 0;
}

const char* floatToStr(float val, int decimalPlaces){
    char formatStr[12];
    char *outstr = malloc(17);
    if(decimalPlaces==0){
        snprintf(outstr,16,"%i", (int)val );
    } else {
        snprintf(formatStr,10,"%%i.%%0%ii",decimalPlaces);
        snprintf(outstr,16,formatStr, (int)val, ((int)((val>0?1:-1)*val*pow(10,decimalPlaces))%(int)pow(10,decimalPlaces)) );
    }
    return outstr;
}

It works! Or so it seemed. I had overlooked a comment in the second thread noted above regarding needing to free the buffer that was malloc'ed in the floatToStr function. Failing to free the buffer constitutes a memory leak.

I believe the best solution may be to follow the model provided in that thread about making a dedicated char buffer in the calling function and using dtostrf() to write to that buffer before calling printf, and forget about generating the full string purely inline. But before I give up hope on this method...

Question 1: can someone confirm (or guide me on how to confirm this myself) that the buffer that gets generated by floatToStr does not automatically get cleaned up (freed) after the printf is done?

Question 2: Is there some other way to keep the syntax at the caller level short and sweet, i.e. without needing to define the char array buffers outside of the printf call?

Ryan K
  • 1
  • 3
  • 4
    If you use the Arduino framework, remove the C tag, because it is not C. Otherwise, clarify you really use C, not the C++-**based** framework. it might be a simple C question, though, so you can remove the arduino tag completely. And we are not a tutoring/"explain this code I found somewhere" site. Read [ask]. As a sidenote: using dynamic memory allocation on a platform like Arduino is a very bad idea. – too honest for this site Jul 22 '17 at 19:12
  • 1
    Thanks for the tips. Would it not be better to delete the arduino tag? The code snippet is pure C, and the general aim of my questions is about C memory management, although the need for this functionality may be unique to Arduino framework. That's why I figured both tags were relevant. Please also help me understand what is wrong with the question. This is not a "fix this code I found somewhere" question. The code I posted has evolved a few steps beyond what was referenced and my question is specifically about the inline syntax which was not in the scope of the previous threads. – Ryan K Jul 22 '17 at 19:27
  • 4
    In case I was not already clear enough in my first comment: **If you use the Arduino Framework, your code is not C.** If you use a different toolchain, your code **might** be C. Either way, you as the programmer should know. if not, please get a C book and an arduino book. and work through the lessons. Just get the tags correct. (A book about embedded programming might be useful anyway; your code is nothing to be recommended on small platforms. – too honest for this site Jul 22 '17 at 19:32
  • Don't malloc() on a device where the RAM is measured in bytes. Bad idea. – TomServo Jul 22 '17 at 20:20
  • 1
    Whjy don't you use Arduino's String? – Michaël Roy Jul 22 '17 at 21:09
  • `(int)val` is UB should `val` well exceed `INT_MAX/INT_MIN` – chux - Reinstate Monica Jul 23 '17 at 11:47
  • 1
    What's the point of the [tag:arduino] tag, if you are obviously not using the Arduino framework? – gre_gor Jul 23 '17 at 23:33
  • "generating the full string purely inline" --> A solution is possible using C11's compound literal. Is that acceptable? – chux - Reinstate Monica Jul 24 '17 at 01:37

2 Answers2

0

In answer to your first question, no confirmation is needed. There is no such thing in C as automatic cleanup. If you allocate memory, you must free it. End of story. The answer to your second question is self-evident. If you don't have a reference to the memory "in-hand", then there is no way to free it. Either cache the result of the call to floatToStr() in a pointer variable that you can free after the call to printf(), or have the floatToStr() function write to a specified buffer.

In response to your first post on SO, welcome to StackOverflow. I see from your user card that you haven't read the Tour Page. This is generally considered bad form for new users, since it gives the impression (whether justified or not, I leave to you) that one can't be bothered to familiarize oneself with how things work on SO. Another useful resource is the Help Portal.

If the (from the comments)

general aim of my questions is about C memory management

then I would suggest removing the Arduino tag. The tag system is one of the primary methods of organizing the HUGE amount of content on the SO site, and as such, is often rigorously monitored by users who follow given tags. This is especially true for tags like C, which a great many people seem to think is a "catch-all" tag.

Mark Benningfield
  • 2,800
  • 9
  • 31
  • 31
  • Thanks @MarkBenningfield for responding. Good point regarding the need to maintain references to each buffer - that fundamentally is what's wrong with the method I proposed. – Ryan K Jul 24 '17 at 03:50
0

Question 1: can someone confirm (or guide me on how to confirm this myself) that the buffer that gets generated by floatToStr does not automatically get cleaned up (freed) after the printf is done?

Yes, you are right. Memory allocated with malloc needs to be manually deallocated by calling free. Otherwise, the program will leak memory.

Question 2: Is there some other way to keep the syntax at the caller level short and sweet, i.e. without needing to define the char array buffers outside of the printf call?

In standard C your options are fairly limited in that regard. In C++, you have the std::string class, which avoids the manual memory management that you have when using raw char*.

I'm not familiar with Arduino, but according to the documentation it has the String object. From my understanding that could simplify your floatToStr function. It also supports conversions, see the examples in the String constructor documentation.

Philipp Claßen
  • 41,306
  • 31
  • 146
  • 239