I want to debug my microcontroller program in C by sending strings via UART.
Now I'm facing a problem joining debug strings together, as I have to work with c strings.
I need to have debugMessageOnStack
on the stack.
Consider following example: I need a string like "My favorite color is mountbatten pink"
const char debugMessageStart[] = "My favorite color is ";
const char* colors[MAX_COLORS] = {[GREEN] = "green", [ORANGE] = "orange", [PINK] = "mountbatten pink"};
char debugMessageOnStack[DEBUG_MESSAGE_LENGTH];
I know it is pretty easy to calculate DEBUG_MESSAGE_LENGTH
by hand, but i want it to be calculated dynamically, so I don't have to recalculate it by hand everytime I add a new color string that's longer than all existing color strings.
Theoretically it should be possible as every string length in colors
is known before compiling, but I don't know how I can do it.
Thanks!
CodePudding user response:
First of all, "but i want it to be calculated dynamically" doesn't make much sense since your array must be large enough to handle the worst case scenario. Your code can't run off into the woods with stack overflow during the worst case scenario, so you could simply allocate the array large enough for that one.
It doesn't make sense to "save some stack space" when not under the worst case scenario, since this is just a local variable anyhow and will get deallocated after use no matter size.
Instead of an array of strings, use an array of structs:
typedef struct
{
size_t length;
const char* str;
} color_t;
const color_t colors[] =
{
[GREEN] = { sizeof("green")-1, "green" },
...
};
That way everything is stored in flash and computed at compile-time. If you are pedantic about reducing code repetition, then you could add a mysterious macro (generally not recommended):
#define COLOR_INIT(str) { sizeof(str)-1, str }
...
[GREEN] = COLOR_INIT("green"),
CodePudding user response:
Generally one should avoid using a variable sized object on stack. First, there is a risk that the size of string can grow too much to be kept on stack. The second, stack allocation with automatic VLAs or alloca()
is usually less efficient than for a fixed size object (though still faster than malloc()
).
Anyway, to deal with a unknown size string on the stack you could use snprintf
function. The function works like printf()
but it stores the characters to a string. However, if one passes NULL
as a buffer and 0 as size it returns a number of characters that would be stored to a buffer.
The string would be produced in two passes:
size_t n = snprintf(NULL, 0, "My favorite color is %s", colors[ORANGE]);
char msg[n 1]; // 1 for the terminator
snprintf(msg, n 1, "My favorite color is %s", colors[ORANGE]);
It may be a good idea to wrap the message generation into a helper to avoid repeating:
size_t mk_msg(char *msg, size_t n, int color) {
return snprintf(msg, n, "My favorite color is %s", colors[color]);
}
The advantage of this methodology is that it is very generic and reusable.
CodePudding user response:
If your compiler supports VLAs the you can use this:
#include <stdio.h>
#include <string.h>
#define MAX_COLORS 3
#define GREEN 0
#define ORANGE 1
#define PINK 2
const char debugMessageStart[] = "My favorite color is ";
const char* colors[] = { [GREEN] = "green",[ORANGE] = "orange", [PINK] = "mountbatten pink" };
int main(void)
{
const char* color = colors[ORANGE];
int length = strlen(debugMessageStart) strlen(color) 1;
char debugMessageOnStack[length];
strcpy(debugMessageOnStack, debugMessageStart);
strcat(debugMessageOnStack, color);
printf("%s\n", debugMessageOnStack);
}