Difference between revisions of "Printing to Strings"

From Nutwiki
Jump to: navigation, search
m (Test Environments)
 
m (1 revision imported)
 
(No difference)

Latest revision as of 17:02, 27 October 2016

Test Environments

Hardware Comments Nut/OS
4.8.7
Ethernut 1.3 H OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 2.1 B OK
Binaries
Compiler: AVR-GCC 4.3.2
Ethernut 3.0 E OK
Binaries
Compiler: ARM-GCC 4.3.3

Overview

This example shows 2 easy methods for printing to strings. You may need this when outputting data for the user or other devices.

Please note that while the methods demonstrated here do work, they were chosen for ease of use. String handling is a pretty resource intensive task. If your application shifts loads of strings around, you may want to develop faster methods of generating your strings.

Source Code

<source lang="c">

  1. include <dev/board.h>
  2. include <stdio.h>
  3. include <io.h>

void PrintSomeMore(char *format, ...) {

   char yet_another_string[256];
   va_list argument_list;
   va_start(argument_list, format);
   vsprintf(yet_another_string, format, argument_list);
   va_end(argument_list);
   puts(yet_another_string);

}

int main(void) {

   unsigned long baud = 115200;
   char text[256];
   CONST char *some_other_text = "world";
   NutRegisterDevice(&DEV_UART, 0, 0);
   freopen(DEV_UART_NAME, "w", stdout);
   freopen(DEV_UART_NAME, "r", stdin);
   _ioctl(_fileno(stdout), UART_SETSPEED, &baud);
   puts("\nNut/OS Printing to strings example");
   /* using printf with a fixed number of arguments */
   sprintf(text, "Hello %s!", some_other_text);
   puts(text);
   /* using vsprintf with an argument list */
   PrintSomeMore("Some numbers: %d, %d, %d", 21, 99, 4431);
   for (;;);
   return 0;

} </source>

Details

The first thing you will notice is probably the "PrintSomeMore" function. Let us forget about it for a moment, though, and look at the main function first.

<source lang="c"> unsigned long baud = 115200; char text[256]; CONST char *some_other_text = "world";

NutRegisterDevice(&DEV_UART, 0, 0); freopen(DEV_UART_NAME, "w", stdout); freopen(DEV_UART_NAME, "r", stdin); _ioctl(_fileno(stdout), UART_SETSPEED, &baud);

puts("\nNut/OS Printing to strings example"); </source>

Along with our usual variables we declare a string text which can hold up to 255 characters (plus the terminating \0 makes 256). Second thing we declare is the constant string some_other_text which, as the name suggests, holds some other text.

As you can see, we already filled the first string. Not actually printed of course, but for strings that never change it is the most efficient way of creating them.

The rest is pretty standard stuff. We open the UART so we can actually print stuff and let the application output a title.

<source lang="c"> /* using printf with a fixed number of arguments */ sprintf(text, "Hello %s!", some_other_text); puts(text); </source>

Now here we start the actual printing. In case you never used printf or sprintf yet, here's the rundown:

sprintf takes at least 2 parameters. The first one is a string to print to. This is where the function will output its results to, so make sure the string you provide has enough room for that. The second parameter is the format. It is a string itself but no complete one. It is a format string, containing the basic layout of the string you want to create and placeholders for variables that you are going to insert.

Here's the catch with sprintf: It can take any number of arguments as long as it has at least the 2 required ones. All arguments that follow after the format string are treated as values that you want to place inside the string, where the placeholders are.

You see the %s in there? That's a placeholder for a string, right there. It will be replaced by the first argument passed to sprintf after the format string. There are many more, for a detailed breakdown check out the Output Format Specifiers example.

sprintf returns the number of characters written or a negative value in case of an error.

What good would a string be if we never used it? Right. After creating the string we output it using puts.

<source lang="c"> /* using vsprintf with an argument list */ PrintSomeMore("Some numbers: %d, %d, %d", 21, 99, 4431); </source>

Now it gets a little more tricky. This looks similar to the previous call to sprintf but it is only to a certain extent.

Remember the PrintSomeMore function we ignored earlier? Now is the time to get back to it.

<source lang="c"> void PrintSomeMore(char *format, ...) </source>

While you probably already wrote some functions, the ... may be new to you. It means: "Here follows a variable list of arguments". Since we don't know the names or even the amount of arguments, we'll need some trickery to work with that.

<source lang="c"> char yet_another_string[256]; va_list argument_list; </source>

We're going to be using these two variables. The first is a string like the one we used before, only local to the function. The second variable is an argument_list.

Argument lists have the type va_list and allow us to manage arguments that we don't have information about.

<source lang="c"> va_start(argument_list, format); </source>

In order to start using the argument list, we have to initialize it using va_start. The first parameter here is the list we are starting with, the second is the argument the function takes right before the start of the argument list. Using some "magic" (well, not really, but at least not something in the scope of this example) the application now figures out how the argument list should be created and does that for us.

<source lang="c"> vsprintf(yet_another_string, format, argument_list); </source>

Now this is the meat of this function. It is pretty similar to sprintf, only that it doesn't take a variable number of arguments but the argument list we just created. In case you're wondering: Yes, this is about the same as sprintf does internally.

<source lang="c"> va_end(argument_list); </source>

Please do never forget va_end! Your application uses some trickery behind the scenes to make argument lists work so if you forget to end them properly, you will get all kinds of strange errors later on. Also, do not return from the function before calling va_end as that will have similar side-effects.

<source lang="c"> puts(yet_another_string); </source>

Finally, we again output the string we just created.

There are many more ways of manipulating strings. The ones shown here are very easy to use but also sap a lot of CPU time. In time critical situations, you may require faster ways of putting strings together.

Output

<source lang="text">

Nut/OS Printing to strings example Hello world! Some numbers: 21, 99, 4431

</source>

See also