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

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.

Related

Converting any variable from big to little endian - how to avoid void pointers?

I'm working on an application that needs to convert any type of the variable from big to little-endian.
My system works with different variable types (16, 32, and 64 bits wide), and I need to be able to change the endianness with a single function. I wrote a function that manages to swap bytes in any variable however, I'm not happy with it. It works, but it requires dereferencing void pointers, which are prone to error with the double star...
Is there any better way to approach the problem?
Is there any way to avoid void pointers as return value? I was thinking about switch-case loop (eg. case 4 bytes -> return int32) however, I don't know how to write a function prototype for a function that returns different values.
My function:
void* swapBytes(void* number, int bytes_num){
void* swapped;
unsigned __int8* single_byte_ptr;
swapped = malloc(bytes_num * sizeof(__int8));
for (int i = 0; i<bytes_num; i++){
single_byte_ptr =((unsigned __int8*)number)+i; //get current byte
*( (__int8*)(swapped)+((bytes_num-1)-i)) = (unsigned __int8)*single_byte_ptr; //save the byte in new position
}
return swapped;
}
the way I call this function
__int64 big_number = 35169804487071;
big_number = *(__int64*)(swapBytes(&big_number, 8));
One problem you have is that you're leaking memory. You return a pointer to malloc'ed memory, but you're not saving the pointer when you return.
Given that you're assigning the result back to the same value, you're better off updating the existing variable, swapping the current byte with a byte on the "opposite" side.
You also don't need to use a void * anyplace other than the parameter type. Inside of the function, just use a pointer to an unsigned char or unsigned __int8 to work through the bytes.
void swapBytes(void* number, int bytes_num)
{
unsigned __int8* ptr = number;
for (int i = 0; i<bytes_num/2; i++) {
unsigned __int8 tmp = ptr[i];
ptr[i] = ptr[bytes_num-1-i];
ptr[bytes_num-1-i] = tmp;
}
}
Then call it like this:
swapBytes(&big_number, sizeof(big_number));
Your solution is very over-engineered and also entirely unsuitable for embedded systems such as MPC57xx.
Any integer type can get safely iterated across using a pointer to character. Assuming uint8_t* is a character type for your compiler, it's as simple as this:
void little_to_big16 (uint8_t big [sizeof(uint16_t)],
const uint8_t little [sizeof(uint16_t)])
{
big[0] = little[1];
big[1] = little[0];
}
Then write big_to_little16, big_to_little32 etc etc as needed. Such functions can and should probably be inlined too.
Example of use:
#include <stdio.h>
#include <inttypes.h>
void little_to_big16 (uint8_t big [sizeof(uint16_t)],
const uint8_t little [sizeof(uint16_t)])
{
big[0] = little[1];
big[1] = little[0];
}
int main (void)
{
uint16_t little = 0xAABB;
uint16_t big;
little_to_big16((uint8_t*)&big, (uint8_t*)&little);
printf("%"PRIx16, big);
}
Output on x86 little endian:
bbaa

Is it possible to simulate C99 lvalue array initialization in C90?

Context:
I am experimenting with functional programming patterns in C90.
Goal:
This is what I'm trying to achieve in ISO C90:
struct mut_arr tmp = {0};
/* ... */
struct arr const res_c99 = {tmp};
Initializing a const struct member of type struct mut_arr with a lvalue (tmp).
#include <stdio.h>
enum
{
MUT_ARR_LEN = 4UL
};
struct mut_arr
{
unsigned char bytes[sizeof(unsigned char const) * MUT_ARR_LEN];
};
struct arr {
struct mut_arr const byte_arr;
};
static struct arr map(struct arr const* const a,
unsigned char (*const op)(unsigned char const))
{
struct mut_arr tmp = {0};
size_t i = 0UL;
for (; i < sizeof(tmp.bytes); ++i) {
tmp.bytes[i] = op(a->byte_arr.bytes[i]);
}
struct arr const res_c99 = {tmp};
return res_c99;
}
static unsigned char op_add_one(unsigned char const el)
{
return el + 1;
}
static unsigned char op_print(unsigned char const el)
{
printf("%u", el);
return 0U;
}
int main() {
struct arr const a1 = {{{1, 2, 3, 4}}};
struct arr const a2 = map(&a1, &op_add_one);
map(&a2, &op_print);
return 0;
}
This is what I tried in C90:
#include <stdio.h>
#include <string.h>
enum {
MUT_ARR_LEN = 4UL
};
struct mut_arr {
unsigned char bytes[sizeof(unsigned char const) * MUT_ARR_LEN];
};
struct arr {
struct mut_arr const byte_arr;
};
struct arr map(struct arr const* const a,
unsigned char (*const op)(unsigned char const))
{
struct arr const res = {0};
unsigned char(*const res_mut_view)[sizeof(res.byte_arr.bytes)] =
(unsigned char(*const)[sizeof(res.byte_arr.bytes)]) & res;
struct mut_arr tmp = {0};
size_t i = 0UL;
for (; i < sizeof(tmp.bytes); ++i) {
tmp.bytes[i] = op(a->byte_arr.bytes[i]);
}
memcpy(res_mut_view, &tmp.bytes[0], sizeof(tmp.bytes));
return res;
}
unsigned char op_add_one(unsigned char const el) { return el + 1; }
unsigned char op_print(unsigned char const el) {
printf("%u", el);
return 0U;
}
int main() {
struct arr const a1 = {{{1, 2, 3, 4}}};
struct arr const a2 = map(&a1, &op_add_one);
map(&a2, &op_print);
return 0;
}
All I do is to create an "alternate view" (making it essentially writable). Hence, I cast the returned address to unsigned char(*const)[sizeof(res.byte_arr.bytes)].
Then, I use memcpy, and copy the contents of the tmp to res.
I also tried to use the scoping mechanism to circumvent initializing in the beginning.
But it does not help, since there cannot be a runtime evaluation.
This works, but it is not anything like the C99 solution above.
Is there perhaps a more elegant way to pull this off?
PS: Preferably, the solution should be as portable as possible, too. (No heap allocations, only static allocations. It should remain thread-safe. These programs above seem to be, as I only use stack allocation.)
Union it.
#include <stdio.h>
#include <string.h>
enum {
MUT_ARR_LEN = 4UL
};
struct mut_arr {
unsigned char bytes[sizeof(unsigned char) * MUT_ARR_LEN];
};
struct arr {
const struct mut_arr byte_arr;
};
struct arr map(const struct arr *a, unsigned char (*op)(unsigned char)) {
union {
struct mut_arr tmp;
struct arr arr;
} u;
size_t i = 0;
for (; i < sizeof(u.tmp.bytes); ++i) {
u.tmp.bytes[i] = op(a->byte_arr.bytes[i]);
}
return u.arr;
}
unsigned char op_add_one(unsigned char el) {
return el + 1;
}
unsigned char op_print(unsigned char el) {
printf("%u", el);
return 0U;
}
int main() {
const struct arr a1 = {{{1, 2, 3, 4}}};
const struct arr a2 = map(&a1, &op_add_one);
map(&a2, &op_print);
return 0;
}
Let's throw some standard stuffs from https://port70.net/~nsz/c/c89/c89-draft.html .
One special guarantee is made in order to simplify the use of unions: If a union contains several structures that share a common initial sequence, and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them. Two structures share a common initial sequence if corresponding members have compatible types for a sequence of one or more initial members.
Two types have compatible type if their types are the same.
For two qualified types to be compatible, both shall have the identically qualified version of a compatible type;
The idea is that "common initial sequence" of mut_arr and arr is unsigned char [sizeof(unsigned char) * MUT_ARR_LEN]; so you can access one using the other.
However, as I read it now, it is unspecified if "initial sequence if corresponding members" includes nested struct members or not. So technically to be super standard compliant, you would:
struct arr map(const struct arr *a, unsigned char (*op)(unsigned char)) {
struct mutmut_arr {
struct mut_arr byte_arr;
};
union {
struct mutmut_arr tmp;
struct arr arr;
} u;
size_t i = 0;
for (; i < sizeof(u.tmp.bytes); ++i) {
u.tmp.byte_arr.bytes[i] = op(a->byte_arr.bytes[i]);
}
return u.arr;
}
#subjective I do want to note two things.
The placement of const type qualifier in your code is very confusing. It's typical in C to write const <type> not <type> const. It's typical to align * to the right with space on the left. I was not able to read your code efficiently at all. I removed almost all const from the code above.
Creating such interface as presented will be pain with no great benefits, with a lot of edge cases with lurking undefined behaviors around the corner. In C programming language, trust the programmer - it's one of the principles of C programming language. Do not prevent the programmer to do what has to be done (initializing a structure member). I would advise making the member mutable and have one structure definition and call it day. const qualified structure members usually are just hard to deal with, with no big benefits.
My answer might sound outrageous at first glance. It is
STOP WHAT YOU ARE DOING, NOW!
I will take my time to explain and give you a glimpse into your future (which is dim, if you pursue this idea) and try to convince you. But the gist of my answer is the bold line above.
Your prototype omits crucial parts to have some lasting solution to your "functional programming in C" approach. For example, you only have arrays of bytes (unsigned char). But for a "real" solution for "real" programmers, you need to consider different types. If you go to hoogle (Haskells online type and function browser engine thingy), you will notice, that fmap, which is the functional feature you try to achieve in C is defined as:
fmap :: Functor f => (a -> b) -> f a -> f b
This means, the mapping is not always from type a to type a. It's a monadic thingy, you try to offer your C programming fellows. So, an array of type element type a needs to be mapped to an array of element type b. Hence, your solution needs to offer not just arrays of bytes.
In C, arrays can reside in different types of memory and we cannot hide this very well. (In real functional languages, memory management is kind of abstracted away for the larger part and you just do not care. But in C, you must care. The user of your library must care and you need to allow them to dutifully care. Arrays can be global, on the stack, on the heap, in shared memory, ... and you need to offer a solution, allowing all that. Else, it will always just be a toy, propagating an illusion, that "it is possible and useful".
So, with just allowing arrays of different, custom types (someone will want arrays of arrays of a type as well, mind you!) and to be aware of memory management, how could a header file of your next evolution look like. Here is what I came up with:
#ifndef __IMMUTABLE_ARRAY_H
#define __IMMUTABLE_ARRAY_H
#include <stdint.h>
#include <stdlib.h>
#include <stdatomic.h>
// lacking namespaces or similar facilities in C, we use
// the prefix IA (Immutable Array) in front of all the stuff
// declared in this header.
// Wherever you see a naked `int`, think "bool".
// 0 -> false, 1 -> true.
// We do not like stdbool.h because sometimes trouble
// ensues in mixed C/C++ code bases on some targets, where
// sizeof(C-bool) != sizeof(C++-bool) o.O. So we cannot use
// C-bool in headers...
// We need storage classes!
// There are arrays on heap, static (global arrays),
// automatic arrays (on stack, maybe by using alloca),
// arrays in shared memory, ....
// For those different locations, we need to be able to
// perform different actions, e.g. for cleanup.
// IAStorageClass_t defines the behavior for a specific
// storage class.
// There is also the case of an array of arrays to consider...
// where we would need to clean up each member of the array
// once the array goes out of scope.
struct IAArray_tag;
typedef struct IAArray_tag IAArray_t;
typedef struct IAStorageClass_tag IAStorageClass_t;
typedef int (*IAArrayAllocator) (IAStorageClass_t* sclass,
size_t elementSize,
size_t capacity,
void* maybeStorage,
IAArray_t* target);
typedef void (*IAArrayDeleter) (IAArray_t* arr);
typedef void (*IAArrayElementDeleter) (IAArray_t* arr);
typedef int64_t (*IAArrayAddRef) (IAArray_t* arr);
typedef int64_t (*IAArrayRelease) (IAArray_t* arr);
typedef struct IAStorageClass_tag {
IAArrayAllocator allocator;
IAArrayDeleter deleter;
IAArrayElementDeleter elementDeleter;
IAArrayAddRef addReffer;
IAArrayRelease releaser;
} IAStorageClass_t;
enum IAStorageClassID_tag {
IA_HEAP_ARRAY = 0,
IA_STACK_ARRAY = 1,
IA_GLOBAL_ARRAY = 2,
IA_CUSTOM_CLASSES_BEGIN = 100
};
typedef enum IAStorageClassID_tag IAStorageClassID_t;
// creates the default storage classes (for heap and automatic).
void IAInitialize();
void IATerminate();
// returns a custom and dedicated identifier of the storage class.
int32_t
IARegisterStorageClass
(IAArrayAllocator allocator,
IAArrayDeleter deleter,
IAArrayElementDeleter elementDeleter,
IAArrayAddRef addReffer,
IAArrayRelease releaser);
struct IAArray_tag {
const IAStorageClass_t* storageClass;
int64_t refCount;
size_t elementSize; // Depends on the type you want to store
size_t capacity;
size_t length;
void* data;
};
// to make sure, uninitialized array variables are properly
// initialized to a harmless state.
IAArray_t IAInitInstance();
// allows to check if we ran into some uninitialized instance.
// In C++, this would be like after default constructor.
// See IAInitInstance().
int IAIsArray(IAArray_t* arr);
int
IAArrayCreate
(int32_t storageClassID,
size_t elementSize, // the elementSize SHALL be padded to
// a system-acceptable alignment size.
size_t capacity,
size_t size,
void* maybeStorage,
IAArray_t* target);
typedef
int
(*IAInitializerWithIndex_t)
(size_t index,
void* elementPtr);
int
IAArrayCreateWithInitializer
(int32_t storageClassID,
size_t elementSize,
size_t capacity,
void* maybeStorage,
IAInitializerWithIndex_t initializer,
IAArray_t* target);
IAArray_t* IAArrayAddReference(IAArray_t* arr);
void IAArrayReleaseReference(IAArray_t* arr);
// The one and only legal way to access elements within the array.
// Shortcutters, clever guys and other violators get hung, drawn
// and quartered!
const void * const IAArrayAccess(IAArray_t* arr, size_t index);
typedef void (*IAValueMapping_t)
(size_t index,
void* sourceElementPtr,
size_t sourceElementSize,
void* targetElementPtr,
size_t targetElementSize);
size_t IAArraySize(IAArray_t* arr);
size_t IAArrayCapacity(IAArray_t* arr);
size_t IAArrayElementSize(IAArray_t* arr);
// Because of reasons, we sometimes want to recycle
// an array and populate it with new values.
// This can only be referentially transparent and safe,
// if there are no other references to this array stored
// anywhere. i.e. if refcount == 1.
// If our app code passed the array around to other functions,
// some nasty ones might sneakily store themselves a pointer
// to an array and then the refcount > 1 and we cannot
// safely recycle the array instance.
// Then, we have to release it and create ourselves a new one.
int IACanRecycleArray(IAArray_t* arr);
// Starship troopers reporter during human invasion
// of bug homeworld: "It is an ugly planet, a bug planet!"
// This is how we feel about C. Map needs some noisy extras,
// just because C does not allow to build new abstractions with
// types. Yes, we could send Erich Gamma our regards and pack
// all the noise into some IAArrayFactory * :)
int
IAArrayMap(IAValueMapping_t mapping,
IAArray_t* source,
int32_t targetStorageClassID,
size_t targetElementSize,
void* maybeTargetStorage,
IAArray_t* target);
#endif
Needless to say, that I did not bother to implement my cute immutable-array.h in my still empty immutable-array.c, yes?
But once we did it, the joy woulds begin and we could write robust, functional C programs, yes? No! This is how well written functional C application code using those arrays might look like:
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stdatomic.h>
#include <math.h>
#include <assert.h>
#include "immutable-array.h"
typedef struct F64FloorResult_tag {
double div;
double rem;
} F64FloorResult_t;
void myFloor(double number, F64FloorResult_t* result) {
if (NULL != result) {
result->div = floor(number);
result->rem = number - result->div;
}
}
int randomDoubleInitializer(size_t index, double* element) {
if (NULL != element) {
*element = ((double)rand()) / (double)RAND_MAX;
return 1;
}
return 0;
}
void
doubleToF64FloorMapping
(size_t index,
double* input,
size_t inputElementSize,
F64FloorResult_t *output,
size_t outputElementSize) {
assert(sizeof(double) == inputElementSize);
assert(sizeof(F64FloorResult_t) == outputElementSize);
assert(NULL != input);
assert(NULL != output);
myFloor(*input, output);
}
int main(int argc, const char* argv[]) {
IAInitialize();
{
double sourceData[20];
IAArray_t source = IAInitInstance();
if (IAArrayCreateWithInitializer
((IAStorageClassID_t)IA_STACK_ARRAY,
sizeof(double),
20,
&sourceData[0],
(IAInitializerWithIndex_t)randomDoubleInitializer,
&source)) {
IAArray_t result = IAInitInstance();
F64FloorResult_t resultData[20];
if (IAArrayMap
((IAValueMapping_t)doubleToF64FloorMapping,
&source,
(int32_t)IA_STACK_ARRAY,
sizeof(F64FloorResult_t),
&result)) {
assert(IAArraySize(&source) == IAArraySize(&result));
for (size_t index = 0;
index < IAArraySize(&source);
index++) {
const double* const ival =
(const double* const)IAArrayAccess(&source, index);
const F64FloorResult_t* const oval =
(const F64FloorResult_t* const)
IAArrayAccess(&result,index);
printf("(%g . #S(f64floorresult_t :div %g :rem %g))\n",
*ival, oval->div, oval->rem);
}
IAArrayReleaseReference(&result);
}
IAArrayReleaseReference(&source);
}
}
IATerminate();
return 0;
}
I see already the knives coming out of the satchels of your colleagues if you try to impose such a monstrosity upon them. They will hate you, you will hate yourself. Eventually, you will hate that you ever had the idea to even try.
Especially, if in a more suitable language, the same code might look like this:
(map 'list #'(lambda (x) (multiple-value-list (floor x)))
(loop repeat 20
for x = (random 1.0)
collecting x))

Can I access variables using a list of their names dynamically?

I have a list of variables char [][20] ls = {"var_1", "var_2", ... , ""}
which are the names of the fields of a struct struct {char var1[10], ...} my_struct;
The variables inside the struct are all char[] with changing lengths.
The list itself is const and should not change mid-run-time.
I want to access those variables in a loop in a somewhat generic way. Instead of calling myfunc(my_struct.var1); myfunc(my_struct.var2); and so on, I would much rather have:
for (char * p = ls[0]; *p; p += sizeof(ls[0]))
{
myfunc(my_struct.{some magic that would put var_1 / var_2 here});
}
But I guess this is impossible due to fact that the loop is executed in run-time, and the variable name needs to be available in compile-time.
Am I correct or is there something that can be done here? (not have to be this way, just wants to know if I can pack this routine into a nice loop)
Since all members are arrays of the same type, you can create an array of addresses to each member and loop through that:
char *my_struct_addrs[] = { my_struct.var1, my_struct.var2, ... };
int i;
for (i=0; i < sizeof(my_struct_addrs) / sizeof(my_struct_addrs[0]); i++) {
myfunc(my_struct_addrs[i]);
}
Since the size of each of these arrays is different however, you'll need to take care not to pass the bounds of each one. You can address this by keeping track of the size of each field and passing that to the function as well:
struct addr_list {
char *addr;
int len;
};
struct addr_list my_struct_addrs[] = {
{ my_struct.var1, sizeof(my_struct.var1) },
{ my_struct.var2, sizeof(my_struct.var2) },
...
};
int i;
for (i=0; i < sizeof(my_struct_addrs) / sizeof(my_struct_addrs[0]); i++) {
myfunc(my_struct_addrs[i].addr, my_struct_addrs[i].len);
}
Assuming you have something like
const char* ls[] = {"var_1", "var_2", ""};
where this list is not tightly-coupled to the struct data (if so you can use the answer by dbush), but is a separate item for whatever reason.
Then the slightly hacky, but well-defined version would be to use look-up tables. Create two lookup tables, one with strings, one with offsets:
#include <stddef.h>
typedef struct
{
int var_1;
int var_2;
} my_struct_t;
static const char* VAR_STRINGS[] =
{
"var_1",
"var_2",
""
};
static const size_t VAR_OFFSET[] =
{
offsetof(my_struct_t, var_1),
offsetof(my_struct_t, var_2),
};
Then do something like index = search_in_VAR_STRINGS_for(ls[i]); to get an index. (Loop through all items, or use binary search etc). The following code is then actually legal and well-defined:
unsigned char* ptr = (unsigned char*)&my_struct;
ptr += VAR_OFFSET[index];
int var_1 = *(int*)ptr;
This takes padding in account and the pointer arithmetic is guaranteed to be OK by C11 6.3.2.3/7:
When a pointer to an object is converted to a pointer to a character type,
the result points to the lowest addressed byte of the object. Successive increments of the
result, up to the size of the object, yield pointers to the remaining bytes of the object.
And since what's really stored at that address (effective type) is indeed an int, the variable access is guaranteed to be OK by C11 6.5/7 ("strict aliasing"):
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
— a type compatible with the effective type of the object,
But various error handling obviously needs to be in place to check that something doesn't go out of bounds.

using #define for defining struct objects

I came across this simple program somewhere
#include<stdio.h>
#include<stdlib.h>
char buffer[2];
struct globals {
int value;
char type;
long tup;
};
#define G (*(struct globals*)&buffer)
int main ()
{
G.value = 233;
G.type = '*';
G.tup = 1234123;
printf("\nValue = %d\n",G.value);
printf("\ntype = %c\n",G.type);
printf("\ntup = %ld\n",G.tup);
return 0;
}
It's compiling (using gcc) and executing well and I get the following output:
Value = 233
type = *
tup = 1234123
I am not sure how the #define G statement is working.
How G is defined as an object of type struct globals ?
First, this code has undefined behavior, because it re-interprets a two-byte array as a much larger struct. Therefore, it is writing past the end of the allocated space. You could make your program valid by using the size of the struct to declare the buffer array, like this:
struct globals {
int value;
char type;
long tup;
};
char buffer[sizeof(struct globals)];
The #define is working in its usual way - by providing textual substitutions of the token G, as if you ran a search-and-replace in your favorite text editor. Preprocessor, the first stage of the C compiler, finds every entry G, and replaces it with (*(struct globals*)&buffer).
Once the preprocessor is done, the compiler sees this code:
int main ()
{
(*(struct globals*)&buffer).value = 233;
(*(struct globals*)&buffer).type = '*';
(*(struct globals*)&buffer).tup = 1234123;
printf("\nValue = %d\n",(*(struct globals*)&buffer).value);
printf("\ntype = %c\n",(*(struct globals*)&buffer).type);
printf("\ntup = %ld\n",(*(struct globals*)&buffer).tup);
return 0;
}
The macro simply casts the address of the 2-character buffer buf into a pointer to the appropriate structure type, then de-references that to produce a struct-typed lvalue. That's why the dot (.) struct-access operator works on G.
No idea why anyone would do this. I would think it much cleaner to convert to/from the character array when that is needed (which is "never" in the example code, but presumably it's used somewhere in the larger original code base), or use a union to get rid of the macro.
union {
struct {
int value;
/* ... */
} s;
char c[2];
} G;
G.s.value = 233; /* and so on */
is both cleaner and clearer. Note that the char array is too small.

Comparing constants

Suppose I have the following constants:
const char EASY = 'E';
const char NORMAL = 'N';
const char HARD = 'H';
const char LUNATIC = 'L';
I want LUNATIC to be greater than HARD, which is greater than NORMAL, which is greater than EASY.
How do I define them as such where the following will work:
int main(){
char diff1 = LUNATIC;
char diff2 = NORMAL;
if (diff1 > diff2){
printf("Lunatic is harder than normal");
}
return 0;
}
Use enumeration:
enum Difficulty
{
EASY,
NORMAL,
HARD,
LUNATIC
};
int main(){
char diff1 = LUNATIC;
char diff2 = NORMAL;
if (diff1 > diff2){
printf("Lunatic is harder than normal");
}
return 0;
}
Use enum { EASY, NORMAL, HARD, LUNATIC }; instead of your constant definitions and your code would start to work.
But, one thing that I am unable to understand is your need to compare the constants. You know the values as they are compile time constants!
There's several ways. The cleaner solution is to use enumerators, which are guaranteed to have increasing integral values:
typedef enum Difficulty {
Easy,
Normal,
Hard,
Lunatic
} Difficulty;
This can be used like this:
void foo(Difficulty a, Difficulty b)
{
if (a > b) {
// ...
} else
// ...
}
// ...
Difficulty a = Easy;
Difficulty b = Hard;
foo(a, b);
Other approaches are possible too. You can just use macros with increasing values:
#define EASY 0
#define NORMAL 1
#define HARD 2
#define LUNATIC 3
Your current approach works too, but there's no reason for you to use actual characters for the constants. You can just use numeric values instead:
const char EASY = 0;
const char NORMAL = 1;
const char HARD = 2;
const char LUNATIC = 3;
And of course there's no real reason as far as I can see that would require you to use char constants. You can just use int instead.
The first approach (using an enumerator) is usually preferred, as it gives you an actual typedef that you can use, which helps make code a bit more readable; if you see a variable of type Difficulty, then you immediately know what kind of values it's expected to hold.
One thing to keep in mind with enumerators is that they're compatible with integers. That means they don't really give you any type safety since you can assign any numerical value to them; it's mostly about writing cleaner code, not type safety.

Resources