How can I write these char arrays in a nicer way? - c

Right now I have an array of char arrays, which I'm using to store font data:
const char * const FONT[] = {
"\x48" "a44448", //0
"\x27" "m\x48" "m\x40", //1
"\x06" "a46425" "m\x00" "m\x80", //2
"\x06" "a46425" "a42425", //3
"\x83" "m\x03" "m\x68" "m\x60", //4
"\x88" "m\x08" "m\x04" "m\x44" "a42424" "m\x00", //5
"\x02" "a42428" "a84842", //6
"\x08" "m\x88" "m\x20", //7
"\x44" "A46428" "a42428", //8
"\x86" "a46428" "m\x60", //9
...
Is there a way to write this in a more readable way, but still have it computed at compile time?
For example, something like:
#define start(x,y) //somehow create '\x<x><y>'. start(3,4) -> '\x34'
#define arc(x,y,rx,ry,a) //evaluate to {'a','<x>','<y>','<rx>','<ry>','<a>'}. arc(1,2,3,4,5) -> {'a','1','2','3','4','5'}
const char * const FONT[] = {
start(4,8) arc(4,4, 4,4, 8) "", //somehow concatenate them
...
Also, why can I use string literals but not char array literals:
(This doesn't work)
const char * const FONT[] = {
{'\x48','a','4','4','4','4','8','\0'}, //0
But this works:
const char X[] = {'\x48','a','4','4','4','4','8','\0'};
const char * const FONT[] = {
X,
...

This set of macro's should do what you want :
#define str(s) #s
#define start(px,py) str(\x##px##py)
#define arc(x,y,rx,ry,pa) str(a##x##y##rx##ry##pa)
const char * const FONT[] = {
start(4,8) arc(4,4, 4,4, 8),
}
This makes use of the # and ## operators (aka stringization resp. concatenation operators).
And results in the following pre-compiler output :
const char * const FONT[] = {
"\x48" "a44448",
}

Is there a way to write this in a more readable way, but still have it
computed at compile time? For example, something like:
#define start(x,y) //somehow create '\x<x><y>'. start(3,4) -> '\x34'
#define arc(x,y,rx,ry,a) //evaluate to {'a','<x>','<y>','<rx>','<ry>','<a>'}. arc(1,2,3,4,5) -> {'a','1','2','3','4','5'}
const char * const FONT[] = {
start(4,8) arc(4,4, 4,4, 8) "", //somehow concatenate them
...
You can implement your start() macro with use of the preprocessor's stringification (#) and token-pasting (##) operators. You need to be a little careful with these, however, to account for the fact that their arguments are not first macro-expanded. Where you do want macro expansion, you can achieve it by interposing an extra layer of macro. For example:
// Stringify the argument (without expansion)
#define STRINGIFY(x) #x
// Expand the argument and stringify the result
#define STRINGIFY_EXPANSION(x) STRINGIFY(x)
// Assumes that the arguments should not themselves be expanded
#define MAKE_HEX(x, y) \ ## x ## y
#define start(x,y) STRINGIFY_EXPANSION(MAKE_HEX(x,y))
Similarly, you can implement your arc() macro as
// No macro expansion wanted here, neither at this level nor before stringifying
#define arc(x,y,rx,ry,a) STRINGIFY(x ## y ## rx ## ry ## a)
(Technically, that creates a string literal token that implies a null terminator, not the unterminated char array you described, but that's what you really want anyway.)
Also, why can I use string literals but not char array literals: (This
doesn't work)
const char * const FONT[] = {
{'\x48','a','4','4','4','4','8','\0'}, //0
But this works:
const char X[] = {'\x48','a','4','4','4','4','8','\0'};
const char * const FONT[] = {
X,
...
Largely because those are not array literals. They are plain initializers. Initializers provide a sequence of values used to initialize an object being declared; when that object is a compound one, such as an array or struct, multiple values presented in its initializer provide initial values for some or all of its members. The members of your array FONT are of type char *, and those pointers are what an initializer provides values for. They furthermore have no deeper structure, so no nested braces are expected.
An array literal might look like this:
(const char[]) {'\x48','a','4','4','4','4','8','\0'}
And, because arrays decay to pointers in initializers, too, just as they do most everywhere else, you indeed can use array literals to initialize your array of pointers:
const char * const FONT[] = {
(const char[]) {'\x48','a','4','4','4','4','8','\0'},
// ...
};

A nicer way would be to write it all in hexadecimal notation even for printable characters. But given the different length that would still be a mess.
How about loading the font from a file at runtime or linking it in as binary blob from a file? There isn't really a good way of making binary data look good in source.

You have almost got the reason. You declare an array of pointers because you have rows of different sizes. So in const char * const FONT[] = {..., FONT is an array of const pointers to arrays of const chars. A litteral string is a const char array so it will decay to a pointer that will be used for the initialization of FONT. If you first declare a char array and use its name, things go well too, because here again the array decays to a pointer.
But in C {'\x48','a','4','4','4','4','8','\0'} is not by itself an array but only an initialization list that can only be used to initialize a character array. For example:
char arr_ok[] = { '1', '2', '3', '\0' }; // correct initialization of a char[4]
char *ptr_ko = { '1', '2', '3', '\0' }; // wrong initialization of a char* (should not compile)
That means that the initialization list is not an array and cannot decay to a pointer.
Things would be different for a 2D array:
char arr2D[][9] = { { '1', '2', '3' }, { '4', '5', '6' }, { '7', '8', '9'} };
This line initializes the 3 sub arrays with resp. '1','2','3' '4','5','6 and '7','8','9'. But it cannot be used for an array of pointers

Related

Create string in C where first character is string length

This question is related to Concatenate string literal with char literal, but is slightly more complex.
I would like to create a string literal, where the first character of the string is the length of the string, and the second character is a constant. This is how it is being done currently:
const char myString[] =
{
0x08,
SOME_8_BIT_CONSTANT,
'H',
'e',
'l',
'l',
'o',
0x00
};
Ideally, I would like to replace it with something like:
const char myString[] = BUILD_STRING(0xAA, "Hello");
I tried implementing it like this:
#define STR2(S) #S
#define STR(S) STR2(S)
#define BUILD_STRING(C, S) {(sizeof(S)+2), C, S}
const char myString[] = BUILD_STRING(0xAA, "Hello");
but it expands to:
const char myString[] = {(sizeof("Hello")+2), 0xAA, "Hello"};
and the compiler doesn't seem to like mixing numbers and strings.
Is there any way to do this?
You could in-place define a structure to hold the prefix and the rest, conveniently initialize it, and then treat the whole struct as a char array (not a strict-aliasing violation because standard C lets you treat any object as a char array).
Technically, you're not guaranteed that the compiler won't insert padding between the prefix and the rest, but in practice you can count on it.
#define BUILD_STRING(C, S) \
((char const*)&(struct{ char const p[2]; char const s[sizeof(S)]; })\
{ {(sizeof(S)+2), C}, S})
const char *myString = BUILD_STRING(0xAA, "Hello");
#include <stdio.h>
int main()
{
printf("%d, %#hhX, '%s'\n", myString[0], myString[1], myString+2);
//PRINTS: 8, 0XAA, 'Hello'
}
Edit:
If you're paranoid about the possibility of padding, here's one way to statically assert none is inserted:
#define BUILD_STRING__(S) \
char const p[2]; char const s[sizeof(S)]
#define BUILD_STRING(C, S) \
((char const*)&(struct{BUILD_STRING__(S); \
_Static_assert(sizeof(S)+2== \
sizeof(struct{BUILD_STRING__(S);_Static_assert(1,"");}),""); \
}){ {sizeof(S)+2, C}, S})
Alternatively, using the first version with the (nonstandard)
__attribute((__packed__)) should do the same.

Designators in c89

C99 allows array initializers (among others) to specify which element of the array is being set with a positive integer designator ($6.7.8.6, $6.7.8.17), for example like so:
const char *foo[] = {[2] = "foo", [1] = "bar", [0] = "baz"};
I have previously used this to make an enum-to-string table like so:
enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {
[THING_FOO] = "foo",
[THING_BAR] = "bar",
[THING_BAZ] = "baz"
}
However, I am now working under the requirement that my code is c89 compliant.
I have looked into preprocessor magic (as in here, for example) but I need the strings to be arbitrary, not copies of the enum symbols.
It isn't sufficient to just do
enum {THING_FOO = 0, THING_BAR, THING_BAZ};
const char *table[] = {"foo", "bar", "baz"};
because I will need to add enum elements in the future. Using the c99 method, this would result in NULL pointers in the table which are acceptably easy to debug if they become problems. If I forgot to update the string table using this method, I'd get segfaults which are harder to debug. Also it defeats the point of having symbols if I have to remember offsets anyway.
If the declaration were in a function, I could achieve the desired effect like this:
enum {THING_FOO = 0, THING_BAR, THING_BAZ, NUM_THINGS};
void foo(void)
{
static const char *table[NUM_THINGS];
table[THING_FOO] = "foo";
table[THING_BAR] = "bar";
table[THING_BAZ] = "baz";
/* ... */
}
However, at least with gcc, this does not get optimized.
Is there any way of declaring such a string table in c89?
(It's no problem in assembly.)
What about the simple, old fashioned
const char* table[] = { "foo", "bar", "baz" };
In other words, just put them in the correct order.
char *foo_string = table[FOO];
Of course, that only works for simple enums like the above, not for enums in the style
enum { FOO = 13; BAR = 15, BAZ = 312 };
But for that, you would have to create an array with at least 313 elements, most of which are NULL anyway, which would be a pretty wasteful construct. In such cases, the compiler can optimize this for you, when you use a switch construct.
Also take a look at the S.O. question #Leandros pointed to: How to convert enum names to string in c. The answer there uses macros to generate the array, which ensures the entries are in the correct order.
Or, as that answer says:
#define enum_str(s) #s
Which gets rid of the array altogether.
#define DEF_FOO_ENUM(E0, S0, E1, S1, E2, S2) \
enum foo { E0, E1, E2 }; \
const char *foo_str = { S0, S1, S2 };
DEF_FOO_ENUM(THING_FOO, "foo",
THING_BAR, "bar",
THING_BAZ, "baz");
The symbols and strings are paired. You're not easily going to add a new symbol without a string, or vice versa. To add an element, you have to two new arguments to the macro—E3, S3—and so on. There is nothing to keep in sync there, just that the enum has all the E-s and the array has all the S-s. This is almost impossible to screw up.
You can keep them together by using X-Macros:
#define MYXMACRO(OP) \
OP(ENUM_FOO, "foo") \
OP(ENUM_BAR, " bar") \
OP(ENUM_BAZ, "baz")
/* use the first parameter to set up your enums*/
enum {
#define AS_ENUMS(x,y) x,
MYXMACRO(AS_ENUMS)
#undef AS_ENUMS /*not required, just playing nice*/
NUMTHINGS
};
/* use the 2nd parameter to set up your strings*/
const char *strings[] = {
#define AS_STRINGS(x,y) y,
MYXMACRO(AS_STRINGS)
#undef AS_STRINGS
};
#undef MYXMACRO
Now your new data can be added as a set of enum and string. If you later decided to add something else based on the enums it's easily extended with a 'z' parameter to OP() or even ... and __VA_ARGS__ for multiple but varying number of parameters.
After trying a few different techniques, this one is easiest to maintain:
const char *table[] = {
#define FOO 0
"foo",
#define BAR (FOO + 1)
"bar",
#define BAZ (BAR + 1)
"baz"
}
Here all the information about an entry is clustered. To insert an element you only have to modify stuff right around it. For example to insert qux:
const char *table[] = {
#define FOO 0
"foo",
#define QUX (FOO + 1) /* new */
"qux", /* new */
#define BAR (QUX + 1) /* modified */
"bar",
#define BAZ (BAR + 1)
"baz"
}
It's a bit ugly (kind of endearing, you know?) but it works good.

Array of macros in C

Can I do array of macros
I am trying to define array of macros, Please check the below code and let me know can I do it like this:
#include <stdio.h> 
struct da
{
    unsigned char d1:1;
    unsigned char d2:1;
};
struct da stDataVar;
#define DATA_1 stDataVar.d1
#define DATA_2 stDataVar.d2 = 1
unisgned char arrChar[2] = {DATA_1, DATA_2};
main()
{
 
printf("data = %d\n",arrChar[0]);
}
It doesn't make any sense to have "an array of macros". In your case, the macros probably just obfuscate the code. In particular, you shouldn't hide side effects inside macros that you are using for initialization.
Is there any reason why you can't do like this?
// elsewhere in the code:
stDataVar.d2 = 1;
...
unsigned char arrChar[2] =
{
stDataVar.d1,
stDataVar.d2
};
arrChar[0] is the 1st element of arrChar[2] i.e. DATA_1 which is a macro that gets textually replaced by the preprocessor as stDataVar.d1 which is structure stDataVar(of type struct da)'s d1 bit field which is zero or garbage (depends on compiler if that initializes a character by default)

C: make, pass and access const array of pointers to const strings

For some reason this isn't working:
const char * str_reset_command = "\r\nReset";
const char * str_config_command = "\r\nConfig";
const char *commands[2] = {
&str_reset_command,
&str_config_command
};
Is this correct? (Is it code elsewhere that must be causing the problem?)
(I'm running it on a baseline 8bit microcontroller and have very limited debugging capabilities)
Clarification:
This is what I want to achieve:
const char * str_reset_command = "\r\nReset";
const char * str_config_command = "\r\nConfig";
const char * str_start_command = "\r\nStart";
const char * str_end_command = "\r\nEnd";
const char * const group0[1] = {
str_reset_command
}
const char * const group1[2] = {
str_reset_command,
str_start_command
}
const char * const group2[3] = {
str_reset_command,
str_start_command,
str_end_command
}
void doStuffToCharacter(unsigned char the_char){
// ....
}
void doStuffToGroup(char ** group, unsigned char num_strings){
for (unsigned char s=0; s < num_strings; s++){
unsigned char idx = 0;
while (group[s][idx]){
doStuffToCharacter(group[s][idx]);
}
}
}
void main (void){
doStuff(group0, 1);
doStuff(group1, 2);
doStuff(group2, 3);
}
As well as corrections, any neater suggestions of doing the above would be welcome.
Various combinations of the command strings need to be sent to a function for processing. All the strings will be in ROM, as will the groups of pointers to them.
You created an array of pointers, but you are passing the addresses of pointers (meaning, a pointer to a pointer). what you should do is this-
const char* commands[2]= {str_reset_command, str_config_command};
Doesn't directly answer your question, but why not try this:
typedef enum {
RESET_CMD,
CONFIG_CND
} cmd_id;
const char *commands[2] = {
"\r\nReset",
"\r\nConfig"
};
And than use it like this:
commands[RESET_CMD]
EDIT
I'll rewrite your clarification to match this method:
typedef enum {
RESET_CMD,
CONFIG_CND,
START_CMD,
END_CMD
} cmd_id;
const char *commands[4] = {
"\r\nReset",
"\r\nConfig",
"\r\nStart",
"\r\nEnd"
};
const cmd_id group0[1] = {
RESET_CMD
};
const cmd_id group1[2] = {
RESET_CMD,
START_CMD
};
const cmd_id group2[3] = {
RESET_CMD,
START_CMD,
END_CMD
};
void doStuffToCharacter(unsigned char the_char){
// ....
}
void doStuffToGroup(const cmd_id group[], unsigned char num_cmds){
for (unsigned char s=0; s < num_cmds; s++) {
unsigned char idx = 0;
while (commands[group[s]][idx]) {
doStuffToCharacter(commands[group[s]][idx++]);
}
}
}
void main (void){
doStuff(group0, 1);
doStuff(group1, 2);
doStuff(group2, 3);
}
You have an array of const char pointers, and you try to store in it pointers to const char pointers. Remove & operators and it should work.
If you have variables declared outside of function your initializers must be constant. Const doesn't count in this case. You will make an array of pointers to pointers and then use the address of the char* variables.
const char * str_reset_command = "\r\nReset";
const char * str_config_command = "\r\nConfig";
const char **commands[2] = {
&str_reset_command,
&str_config_command
};
Don't forget to dereference the pointer when you use the array.
const char* string = *commands[1] ; //actually it is *(commands[1]) but the [] operator has higher precedence anyway
Try this. It's not quite what you want, but it does the job.
#define RESET_COMMAND "Reset"
#define CONFIG_COMMAND "Config"
const char *commands[2] = {
RESET_COMMAND,
CONFIG_COMMAND,
};
int
main() {
...
}
You are using global variables. Are you really sure you need this approach?
By following your code as is, you defined str_reset_command as a const char *. This means that is a pointer to char, which also has the qualifier const. You have not to confuse "const" with const. :)
The compiler consider "const" to every expression build from expression involving literals.
On the other hand, the const qualifier means that you are defining a "variable" whose value cannot be modified, except in the very moment of its definition.
The expression "\r\nReset" is a "const" because is a string literal.
However const char * str_reset_command is not a "const", but a variable not able to be modified.
The difference is that the literals are "constants" for the compiler, because it can calculate their value in compiling time. However a const object is held as a variable, because its value may be determined in execution time. For example, consider the const parameters of several functions in <string.h>.
Your variable group0 is defined as a pointer to an array of char.
Since it is defined as a global variable, its initializer has to be a "const" (in compile time sense) value. But you have provided str_reset_command, which is not a literal (or an expression involving only literals). Thus, its value cannot be determined in compiling time and it cannot be used as initialize there.
Maybe you could try to write an initializer function:
const char * str_reset_command = "\r\nReset";
const char * str_config_command = "\r\nConfig";
const char * str_start_command = "\r\nStart";
const char * str_end_command = "\r\nEnd";
char * group0[1];
char * group1[2];
char * group2[3];
void initGroups(void) {
group2[0] = group1[0] = group0[0] = str_reset_command;
group2[1] = group1[1] = str_start_command;
group2[2] = str_end_command;
}
int main(void) {
initGroups();
// Do stuff...
}
I dropped the const qualifier since now we need to modify the pointers.
Anyway, the use of global variables has some side effects.
Try to modify this aspect, if possible.
EDIT
2nd TRY
I was thinking in your program, and I has changed completely your approach.
First of all, I don't understand why you are using all that "group[]" arrays.
Since you want to optimize memory resources, this is not optimal.
On the other hand, your "constant strings" seems to be only a few.
By assuming that they are not more than 8, the information of the strings involved in a given group can be hold in 1 byte.
I mean, you can define a "group" or a "set" by means of the bits of a byte, thus putting the bit 1 (on) if a given member belongs to the set, and 0 else.
Besides, since you accept the use of arrays of pointers to constant chars, I think that all your strings can be held in an array of constant char at the beggining of the code. This can be held in ROM, by means of a const declaration.
You have used names as str_reset, str_start, and so on.
It seems that you need this information to be clear for yourself.
This information can be preserved in the code by means of compiler-constants and/or enumerations, which have not any cost in the compiled program.
I have designed a code that use bit-masking.
This let you use up to 8 strings.
Since a bit-mask is a power of 2, this could not be used as an array index.
However, one can define in a very consequent way a list of enumaration constants with names going "in parallel" with the bit-mask constants.
IN particular, you will be capable or change the order of the enumeration, but your code will keep working.
To complete the picture, I have used a feature of C99/C11 standard (that you seems to use, because the way you are declaring the for statements), that allows us to initialize indidivual members of an array by writting the desired index.
Since the indexes now will have names given by an enumeration, you can trust in this technique, without pay attention to the actual indexes that are been used.
I have used <stdio.h> and printf() just to test the program. You can erase these lines.
#include <stdio.h>
#define bit0 0x01u /* Binary 0000 0001 */
#define bit1 0x02u /* Binary 0000 0010 */
#define bit2 0x04u /* Binary 0000 0100 */
#define bit3 0x08u /* Binary 0000 1000 */
#define bit4 0x10u /* Binary 0001 0000 */
#define bit5 0x20u /* Binary 0010 0000 */
#define bit6 0x40u /* Binary 0100 0000 */
#define bit7 0x80u /* Binary 1000 0000 */
enum {reset_command = 0, config_command, start_command, end_command};
#define bit_reset (1u << reset_command) /* Equal to bit0 */
#define bit_config (1u << config_command) /* Equal to bit1 */
#define bit_start (1u << start_command) /* Equal to bit2 */
#define bit_end (1u << end_command) /* Equal to bit3 */
const char * const str[] = {
[reset_command] = "\r\nReset",
[config_command] = "\r\nConfig",
[start_command] = "\r\nStart",
[end_command] = "\r\nEnd"
};
const unsigned char bitgroup0 = bit_reset;
const unsigned char bitgroup1 = bit_reset | bit_start;
const unsigned char bitgroup2 = bit_reset | bit_start | bit_end;
void doStuffToCharacter(unsigned char the_char){
printf("%c", the_char);
}
void doStuff(const char * const * str, unsigned char bitgroup){
printf("\n\nGroup: %hu\n", bitgroup);
for (unsigned char b=bitgroup, j=0; b; b >>= 1u, j++){
if (b & 1u) {
for(unsigned char idx = 0; str[j][idx]; idx++) {
doStuffToCharacter(str[j][idx]);
}
}
}
}
int main (void){
doStuff(str, bitgroup0);
doStuff(str, bitgroup1);
doStuff(str, bitgroup2);
}
As you can see, each "group" now needs only 1 byte. (Your approach used at least 1, 2 and 3 bytes).
The number of iterations in the for() statement does not exceed the greatest bit "on" in the groupbit parameter.
The requirement of having const char object held in const pointers is also fullfilled.
The size of the array is automatically determined by the compiler to hold the maximum index.
The for() loop iterates over a "byte" initialized to the "group" you passed as a parameter.
The last bit is tested against 1.
If this bit is 1, then some operation is done.
Else, this is skipped in it goes to the next iteration.
The last bit is dropped with the assignment b >>= 1u.
With each iteration, we need to walk away the index j of the array str.
Thus, the j-th bit is 1 if and only if the j-string is processed.
Next, every is repeated, until b has not more bits 1.
If you use "groups" whose bits 1 are all contiguous, then the program works in the same way that you expected in your example.
Now you are able to choose the operations you want, just switching the appropiated bits.
Your array declaration is inconsistent with the prototype of the function that uses it:
const char *commands[2] = { // <- declared as array of 2 pointers to char
doStuffToGroup(char ** group // <- used as pointer to pointer to char
It is a common C/C++ pitfall (though vectors used as a remplacement for vanilla arrays make that far less common in C++).
See this answer for a more detailed explanation.

Copying an array in a designated initializer

I'm trying to initialize a const struct with a designated initializer. However, one of the struct elements is a fixed-width array. I already have the contents I would like to initialize the array with in another fixed-width array of appropriate size.
Is there any way to do this with a designated initializer? A simple (failing example) of what I'm trying to accomplish is demonstrated below.
struct foo {
uint8_t array1[4];
uint8_t array2[4];
}
uint8_t array[4] = {
1, 2, 3, 4
};
struct foo const bar = {
.array1 = array, // incompatible pointer to integer conversion
.array2 = { *array } // only copies the first element
};
Short answer: you can't. C does not copy arrays (without the use of (standard library-)functions). The warnings come from the fact that you cannot assign an array as a whole, even when they are static or constant. When an array is used as an r-value in an assignment it decays to a pointer and thus cannot be assigned to another array (as a whole).
The easiest way to go would be to use memcpy, but obviously that must be inside a function.
If bar has global scope, or is declared static, then you won't be able use designated initializers to initialize from non-immediate values, regardless of whether or not the members in question are arrays.
However, if:
bar is declared on the stack of some function, and
Your fixed-size array really does only have 4 elements,
then you might be able to get away with something like this:
#include <stdio.h>
#include <stdint.h>
struct foo {
uint8_t array1[4];
uint8_t array2[4];
};
#define ARRAY_INIT(a) { a[0], a[1], a[2], a[3] }
int main (int argc, char **argv) {
uint8_t arr_init[4] = {
1, 2, 3, 4
};
struct foo const bar = {
.array1 = ARRAY_INIT(arr_init),
.array2 = ARRAY_INIT(arr_init),
};
printf("%d, %d\n", bar.array1[0], bar.array2[3]);
return (0);
}
The initializer array must appear before what is being initialized in the stack frame. Or it may come from a function parameter.
Of course if your array is much bigger than this, then using a macro like this will get very messy indeed.
While you may not be able to initialize the array by copying from another array, it may be helpful to use a preprocessor macro:
#define ARRAY_INIT {1, 2, 3, 4}
struct foo const bar = {
.array1 = ARRAY_INIT,
.array2 = ARRAY_INIT
};

Resources