sprintf no longer assign properly ?

Go To Last Post
16 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

considering this partial function code:

void display_Float ( float* NUMBER){ // number = 234.8765432
    char BUFFER[20]     ;
    const char* form    ;
    form = "%8.8f"	;
    sprintf ( BUFFER , form , NUMBER ) ;
display_String( (unsigned char*) form ) ; // This one display OK -> %8.8f
display_String( (unsigned char*) BUFFER ) ; // This one ouput '       ?'  7 spaces and ?
...

As a mean to debug the whole code I am trying to find out why 'display_String' will work OK trying to display a constant string " %8.8f "

and yet will not display the output of sprintf() function. 'display_String' function is totally functional and as been for many other situations.

The NUMBER value as been verified using the Debug feature in Studio 7. When the code pause at sprintf() the value is correct.

Can anyone see a problem in calling 'sprintf ( BUFFER , form , NUMBER ) ; ' as shown above ?

For some reason BUFFER is not receiving the proper output from sprintf().

 

According to cppreference.com sprintf should be called according to format: int sprintf( char* buffer, const char* format, some numbers... );

I'M stuck, what am I doing wrong ?

 

 

 

 

 

 

This topic has a solution.
Last Edited: Tue. Dec 14, 2021 - 09:06 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

AVR printf() does not support floating point output unless you add the "libprintf_flt" library to the link command.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

If you don't do the two things needed to use the float version of printf you will just see "?" for any %f. 

 

Luckily the authors had the foresight to write a manual... 

 

https://www.nongnu.org/avr-libc/user-manual/group__avr__stdio.html#gaa3b98c0d17b35642c0f3e4649092b9f1

(specifically from "Since the full implementation..." onwards but ignore "-lm", the manual is out of date, that bit is done automatically anyway now) 

 

If using Studio 7 there is a tick box in project properties to do the -Wl,-u,vfprintf and under the libraries section in the project is a tick ow for libprintf_flt.a 

Last Edited: Sat. Dec 11, 2021 - 01:11 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FredCailloux wrote:

sprintf ( BUFFER , form , NUMBER ) ;

 

Should be:

  sprintf(BUFFER, form, *NUMBER);

 

Letting the smoke out since 1978

 

 

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0


Here is what I found in my version of Studio 7:

 

And as for " libraries section in the project is a tick ow for libprintf_flt.a " , I cannot find exactly that 'tick'  ( see screenshot below )

Is that the correct location ?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Points well taken, thanks for the education.

I've got many other questions concerning this topic and comments I read.

I prefer to post questions limited to one particular subject. It keeps the content better organised in my view.

Otherwise things get mixed up and the whole topic gets into a maze of subjects that seems more difficult to follow ( at least for me)

 

But to follow on the subject of utilizing *NUMBER instead NUMBER ( byRef .vs. byVal )

I was under the impression that passing by reference utilize less memory. Pls correct me if I'm wrong on this one.

Also, I want to become more proficient at utilizing pointers, an area for which I need more understanding.

The compiler errors and warnings forces me to rethink how to use pointers properly and help me understand the pitfalls. 

 

Also, I am making substantial usage of the website: Online GDB for practice, but in the case of sprintf() it is a function that I need in my AVR project .

Basically the idea is to build myself a driver for this 8 digits display so I can use it for debugging purposes.

Hence the attempt to send Strings, FLoats, Ints and Char anytime I need throughout a project construction.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

What is the difference between these two arguments lists ( if any ) ?

 vfprintf_P (FILE *__stream, const char *__fmt, va_list __ap )
  fprintf   (FILE *__stream, const char *__fmt, ...          )

vfprintf_P last argument is 'va_list __ap'

  fprintf    last argument is        '...'

Is 'va_list __ap' the same as '...' but just a different way of saying the same thing ?

Or is '...' a totally different beast altogether ?

Reading these function definitions ( and all others )  can I assume that '...' can be replaced by 'va_list __ap'  ?

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

First any AVR-LibC function suffixed _P implies one or more input is in PROGMEM (ie flash) and needs to be LPMd not LDd.

 

As for by ref or by value. It's true the (on AVR) by ref sually means a 2 byte pointer whereas a by value float is 32bits so passes four bytes. But don't forget that if a pointer is passed it has to be dereferenced.

 

On the whole don't write C with premature optimisation. Write what will be clearest and most obvious to the maintainer. On the whole clear code is efficient code anyway. 

 

(and if you were that fussed about optimization you wouldn't be using "float" in the first place!) 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

clawson wrote:
Equally... 

 

FredCailloux wrote:

 

const char* form    ;
    form = "%8.8f"	;

 

 

.. you can't do this either.!

 

The first line only creates a 2 byte pointer to char it does NOT allocate any storage to hold characters. Even if you malloc() to allocate some storage for the pointer to point to you can't just do form = "%8.8f" either. In C you need to use str(n)cpy to copy some characters to a pointer address.

The quoted code is valid: the name of an array of (possibly const) char assigned to a pointer to const char.

'Tain't even rare.  'Tis part of the traditional one-liner for copying a named string.

 

At one time, perhaps still, the type of a quoted string was array of non-const char

that nevertheless one was not allowed to change.

Moderation in all things. -- ancient proverb

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
Luckily the authors had the foresight to write a manual

is kind of let down by

the manual is out of date

frown

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FredCailloux wrote:
What is the difference between these two arguments lists ( if any )

The 'va_list __ap' is how the '...' variable-length parameter list is implemented.

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
Last Edited: Sun. Dec 12, 2021 - 07:57 PM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

FredCailloux wrote:

void display_Float ( float* NUMBER){ // number = 234.8765432
    char BUFFER[20]     ;

note that it's conventional to reserve ALL UPPERCASE names for preprocessor #defines ...

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

FredCailloux wrote:
But to follow on the subject of utilizing *NUMBER instead NUMBER ( byRef .vs. byVal )

actually, C only ever passes by value - you pass the value of a pointer, and use it as a reference

 

FredCailloux wrote:
I was under the impression that passing by reference utilize less memory

Not necessarily.

 

eg, a pointer to a single byte is likely to be bigger than just the value of that single byte - plus, as clawson said, you then have the added overhead of dereferencing the pointer.

 

On the other hand, you probably wouldn't want to pass a large struct by value ...

 

 I want to become more proficient at utilizing pointers

pointers are, indeed, a key tool in C programming - so this is a noble goal.

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
This reply has been marked as the solution. 
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 2

FredCailloux wrote:

What is the difference between these two arguments lists ( if any ) ?

 vfprintf_P (FILE *__stream, const char *__fmt, va_list __ap )
  fprintf   (FILE *__stream, const char *__fmt, ...          )

vfprintf_P last argument is 'va_list __ap'

  fprintf    last argument is        '...'

Is 'va_list __ap' the same as '...' but just a different way of saying the same thing ?

Or is '...' a totally different beast altogether ?

 

No, they are not the same, although they are intended to work in concert, i.e provide different interface for exactly the same functionality. This is an idiom, a gentleman's agreement that is normally carefully followed in professional code that provides variadic functions:

 

Every time you want to write a `...` function, e.g.

 

void my_function(int some_params, ...)

you should implement it in accordance with the following general idiomatic pattern:

 

int vmy_function(int some_param, va_list va)
{
  // Here you implemement your actual functionality
  // In order to access variadic parameters you use `va_arg(va)`
  // You do that under assumption/expectation that `va_start` has already
  // been applied to `va` by the calling code
  // And you don't call `va_end` in this function
}

int my_function(int some_param, ...)
{
  va_list va;
  va_start(va, some_param);
  int result = vmy_function(some_params, va);
  va_end(va);
  return result;
}

I.e. split your function's implementation into two tiers. Both functions should be available to the users of your code.

 

The reason you have to do it that way is to support forwarding of `...` parameters. The problem is that in C it is impossible to forward `...` parameters from the calling function to a callee with `...` parameters.

 

Let's say you wanted to write your own wrapper function for `printf`. Say, to add some custom prologue and epilogue code:

int my_printf_wrapper(const char *format, ...)
{
  // Do some custom prologue actions

  // Now you want to delegate the call to `printf` with the same arguments
  int result = printf(format, ???);
  // But how? What do you put in place of `???` above?

  // Do some custom epilogue actions

  return result;
}

How would you implement the wrapped call to `printf`? The answer is: you can't. It is not possible to forward `...` parameters of your function to `printf`. The language offers no such functionality. And this is where `v...` functions with `va_list` parameters come to the rescue. It not possible to delegate the call to `printf`, but is perfectly possible to properly delegate the call to `vprintf` using the technique shown previously. 

 

For this reason you should always strive to follow the aforementioned idiom: every time you implement a `...` function, provide a `v...` version of that function with a `va_list` parameter instead of `...`(of course, you don't  have to name it as `v...`, but you get the idea). That way you allow the users of your function to forward their `...` parameters to your function. There's no other way to support such forwarding.

 

Of course, if this is your internal code and you are sure you will never need to make such forwarding calls to your function, you can just implement the `...` version. But quite often it pays to stick to the two-tiered approach shown above from the very beginning.

Dessine-moi un mouton

Last Edited: Fri. Dec 17, 2021 - 02:03 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 1

Getting to know stdio for avr will be beneficial- the source code to avr-libc is easy enough to read and figure out.

 

A simple example from another thread to take advantage of vfprintf-

 

https://www.avrfreaks.net/commen...

 

 

In this case, there are no var args in use so can let fprintf handle calling vfprintf-

 

https://github.com/vancegroup-mi...

 

so your code could eliminate the buffer and use of sprintf if you have a function that 'prints' one char at a time. If your 'display' is an lcd for example, and you want to be able to embed codes for cls/new line then you can simply use \n and \t as one way to do this. The below example is an abbreviated version to write to an lcd, but really makes no difference where it ends up- your put function is in control of what happens to each char output by vfprintf.

 

 

int display_char(const char c, FILE* fp){

    //if c == '\t' then cls/home

    //else if c == '\n' then advance to next line (keeping track of which line you are on)

    //else write char to display here

    //can keep track of row/col to keep chars inside the display (ignore if outside the display boundaries)

    return 0;

}

 

void display_float (float n){

    FILE f; f.flags = __SWR; f.put = &display_char;

    return fprintf( &f, "%8.8f", n );

}

 

 

You will notice someone has to setup a va_list to eventually get to vfprintf (the code that does the real work), so if you do not need var args then let someone else do it (fprintf), but if your function takes var args then its up to you to do that and the fprintf source shows you how, and of course you then need to call vfprintf. The first link above shows where va args are in use and vfprintf is called 'directly'.

 

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

To be clear, va_list, va_start and va_end are not the idiom.

They are standard.

The idiom is the two-tiered approach.

Moderation in all things. -- ancient proverb