Been a loooooong time since I've actually coded straight c (not even C++ but c) and I know how to use the extern keyword to share a variable between separate .c files, but what I can't remember is how to share constant data between files?
For example, say I have this... (note, this is not c code (or if it is, its an accident) but rather pseudo-code to show what I want):
const char const * WEEKDAYS[] = {
"Sunday",
"Monday",
"Tuesday"
}
Now I'm trying to create an array of char pointers that point to the data. Again, this is constant data so I'd like to just define it in a header directly, but that's where I can't figure out how to do it, or if that isn't how you should do it anyway and you should still declare it in the c file, then use extern in the header you include elsewhere.
Again, been a long time since I've had to deal with this thanks to the newer, more modern languages, but hoping you can help.
It's the same as for variables:
// header
extern const char * const WEEKDAYS[3];
// implementation
const char * const WEEKDAYS[3] = {
"Sunday",
"Monday",
"Tuesday"
};
Also you probably want const char * const, not const char const * which is invalid syntax.
Related
If I have a bunch of error codes in my application header.h like:
enum errors {
ERROR_NONE,
ERROR_TOTO,
ERROR_TATA,
ERROR_TUTU,
ERROR_MAX,
};
Should I define the string associated with each code in the header like this:
static const char * const errors_strings[ERROR_MAX] = {
"ERROR_NONE",
"Something happened with toto",
"Tata is wrong",
"Pasta or pizza?",
};
or directly in the printing function:
void print_error(int error)
{
char* array[ERROR_MAX] = {
"ERROR_NONE",
"Something happened with toto",
"Tata is wrong",
"Pasta or pizza?"
};
printf("%s\n", errors_strings[error]);
}
Which is the better practice?
First of all, make the error enum a named type with typedef. Ideally we shouldn't mix int and enum types but treat enum as distinct types, even though the weak type system in C doesn't provide much help there. (However, check out How to create type safe enums? for some tips & tricks.)
As for if you should have the string table outside a function or inside one, it entirely depends on if one or several functions are using it. If only one function is using it, then it's a good idea to place the table inside it, to reduce its scope.
Also since the string array has size ERROR_MAX it cannot hold more initialized items than that, but nothing prevents it from having less. Therefore to guarantee integrity between the enum and the table, always do this:
static const char * const errors_strings[] = { ... }; // no size specified
_Static_assert(sizeof errors_strings / sizeof *errors_strings == ERROR_MAX,
"helpful error message here");
I need to define some strings and an array initialized with those strings available to use by different pieces of the software. I thought in defining them in a header file like this:
//.h file
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
const char *serviceStateKindScheduledMaintenanceDown = "ScheduledMaintenance (down)";
const char *serviceStateKindScheduledMaintenanceAvailable = "ScheduledMaintenance (available)";
const char *serviceStateKindMajorIncidentInProgress = "MajorIncidentInProgress";
const char *serviceStateKindPartialService = "PartialService";
const char *serviceStateKindOverloaded = "Overloaded";
const char *serviceStateKindGoingDown = "GoingDown";
const char *serviceStateKindDown = "Down";
const char *serviceStateKind[9] = {
serviceStateKindNormal,
serviceStateKindUnmanned,
serviceStateKindScheduledMaintenanceDown,
serviceStateKindScheduledMaintenanceAvailable,
serviceStateKindMajorIncidentInProgress,
serviceStateKindPartialService,
serviceStateKindOverloaded,
serviceStateKindGoingDown,
serviceStateKindDown
};
but the compiler shows
error: initializer element is not constant
serviceStateKindNormal
what exactly is the problem here and what choices do I have to define my variables?
Everything you put in an intialized of a variable that is declared at file scope must be a constant expression or a string literal, from initialization. There is a list what is a constant expression and variable value is not among them. So:
// there is an array of char[2]{'a',\0'} somewhere in the memory
// we store the pointer value to that array inside the variable a
static const char *a = "a";
// now we want to statically initialize variable b with the same value
static const char *b = a;
will not work, because b is initialized with the pointer a value, which is not a constant expression. You need a constant expression when initializing a variable with static storage duration.
What can you do? The following:
The good way: Why do you store the pointers to the string literals "Normal"? Why not store the data itself inside the array? Happily, variable address is a constant expression, so we can use it in initialization! Note that (almost) all use cases and semantics stay the same, except for some corner usage like sizeof(serviceStateKindNormal) operator results.
const char serviceStateKindNormal[] = "Normal";
const char serviceStateKindUnmanned[] = "Unmanned";
const char *serviceStateKind[] = {
serviceStateKindNormal,
serviceStateKindUnmanned,
};
A strange way: store the pointers to the pointers to strings inside the array. As variable addresses are constant expressions, this will work. Note that makes the serviceStateKind a three star variable. You need to double dereference the array on usage. The serviceStateKind is an array of pointers of pointers to strings. Note that it is very confusing on how to use such array, so I suggest you go with a structure then.
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
const char **serviceStateKind[] = {
&serviceStateKindNormal,
&serviceStateKindUnmanned,
};
int main() {
// question which one is correct?
printf("%s\n", *serviceStateKind[1]);
printf("%s\n", (*serviceStateKind)[1]);
printf("%s\n", serviceStateKind[0][1]);
}
but as i don't think of myself as a three star programmer, I would try to make it at least to two stars:
const char *serviceStateKindNormal = "Normal";
const char *serviceStateKindUnmanned = "Unmanned";
struct {
// points to a string
const char **pnt;
// an array of pointers to string
} serviceStateKind[] = {
&serviceStateKindNormal,
&serviceStateKindUnmanned,
};
int main() {
// more verbose
printf("%s\n", *serviceStateKind[0].pnt);
}
The old fashioned way - macros. Don't do it. And what is bad about it, you can abuse preprocessor concatenation of string literals, to concatenate them together.
#define serviceStateKindNormal "Normal"
#define serviceStateKindUnmanned "Unmanned"
const char *serviceStateKind[] = {
serviceStateKindNormal,
serviceStateKindUnmanned,
};
int main() {
# magic
printf(serviceStateKindNormal " " serviceStateKindUnmanned "\n");
}
I thought in defining them in a header file like this:
If you are defining the variables in a header file, you want to mark them with static, so that you will not get duplicate symbol errors on linking with different .c files that use that .h file. Also it's nice to mark the variable as const as a hint to other programmers that they are somewhat not changable.
In C language constant refers to literal constants, like (3.14, 2.718, etc).
Const-qualified objects (of any type) are not constants in C language terminology.
For creating constants in C language terminology use #define directive, for example:
#define ServiceStateKindNormal "Normal"
etc.
Let's say I'm making a C program which display information about a precious stone when the user enter the stone's name.
The stones names never change and there are less than 20 of them so reading them from a external file at runtime seems a bit overkill.
I also need to access those names from multiple files (let's say two).
What I usually do is I declare a global char * array like so:
char *g_rsc_names[] = {"linemate", "deraumere", "sibur", "mendiane", "phiras", "thystane"};
in the file I need to use those. This look pretty fine to me but this only allow me to access the names in the file where I declare this global array.
In that case, what is the best/cleanest way to store the stones names?
You can wrap your array with a function which returns the const char* to the appropriate stone name and put that declaration into your global header file you include in multiple files:
const char* get_stone_name(size_t id);
Inside a source file define the get_stone_name like:
const char* get_stone_name(size_t id) {
static const char* const names[] = {
"linemate", "deraumere", "sibur", "mendiane", "phiras", "thystane"
};
return (id < (sizeof(names) / sizeof(names[0])) ? names[id] : "");
}
Decaring names as static will guarantee that won't be initialized more than once, and as a side option you can prevent the user to index your array out of bounds.
Define an enum with symbolic names for each stone. You would then store the enum value in the file instead of the name.
You could then define a function which will return the associated string for the given enum.
enum stones {
STONE_LINEMATE,
STONE_DERAMERE,
...
};
const char *stone_name(int stone)
{
switch (stone) {
case STONE_LINEMATE:
return "linemate";
case STONE_DERAMERE:
return "deraumere";
...
default:
return "";
}
Put a public declaration of the stones' data type in a header file, say stones.h:
// This lets any file that includes stones.h know that you've defined
// a variable named g_rsc_names.
extern char const *const g_rsc_names[];
The variable is defined as an array containing some number of character (string) pointers that will never change that each point to characters (strings) that will never change. This is to prevent programmer errors later and gives some hints to your compiler for optimization.
Then, in another file, stones.c:
// Populate the array.
char const *const g_rsc_names[] = {
"linemate", "deraumere", "sibur", "mendiane", "phiras", "thystane"
};
Now, include stones.h wherever you want to reference the array. Make sure to compile the stones.c file and link it to your binary.
I am creating a C program with some hardcoded parameters. (This is by design, lets not worry about that.)
//global constants
const char * const USERNAME = "username";
const int USERNAME_LEN = strlen(USERNAME);
Of course, this errors out because strlen isn't a constant apparently. Is there a way to do this? or should I just not care and pass strncmp the result direct from strlen?
If you really need your USERNAME as a const char * const object (do you?), then one way to it is
#define USERNAME_LITERAL "username"
const char * const USERNAME = USERNAME_LITERAL;
const int USERNAME_LEN = sizeof USERNAME_LITERAL - 1;
It is as elegant as one might wish, but does keep the code in "self-maintaining" shape. I.e. you only have to edit the literal, the rest will update itself automatically.
You can also do
const char USERNAME[] = "username";
const int USERNAME_LEN = sizeof USERNAME - 1;
or even
#define USERNAME "username"
const int USERNAME_LEN = sizeof USERNAME - 1;
but in this case USERNAME is not const char * const anymore, which is why I'm asking whether you really need it as such.
If you have to initialize a pointer for the literal, you can use the following:
const char un_str[] = "...", *un_ptr = un_str;
const size_t un_len = sizeof(un_str);
Note: sizeof() returns a size_t, which is unsigned.
You can pack that into a macro:
#define STR_LEN_PAIR(name, value) const char name##_str[] = value; \
const size_t name##_len = sizeof(name##_str)
This uses argument concatenation. Note the missing ; after the last line which allows to use standard syntax on invocation. Be careful to use it only as intended:
// at file level
STR_LEN_PAIR(un,"...");
The fields can be accessed like un_len, un_str, ...
Note that the pointer itself is not const, so there is not much use in it (you quite likely do not want to have a modifyable pointer to each such string). You can simply omit its definitions as I already did in the *macro.
Compatibility note:
C differs in the semantics of const. Actually, C does not have "constants" as C++ has. objects defined as const are actually "unmodifyable variables": they can only be initialized at compiler-time, but they consume space as any other variable (however, they might be located in unchangeable memory) and the compiler will generate read accesses as for any other variable. In C++, un_len for instance will be replaced by its value at compile-time and might consume no memory in the final program - as long as its address is not taken.
What comes next to real constants in C are either enum constants, or `#define'ed names. The latter are processed at a very different level (textual replacement by the pre-processor), however.
I want to be able to create const structs with specific info easily, so have decided to declare and initialise them "one-line-at-a-time", so I can simply add new const structs as I need them. This works fine, but how can I create some kind of array to access each of these const structs? I tried the following, but it doesn't work.
typedef struct
{
int numOfNotes;
char *arpName;
double freqRatios[12];
} ARPINFO;
const ARPINFO majorArp = {3,"Major Arpeggio",{0.0,JUST_MAJ_3,JUST_PERF_5}};
const ARPINFO minorArp = {3,"Minor Arpeggio",{0.0,JUST_MIN_3,JUST_PERF_5}};
const ARPINFO arpInfoArray[2] = {majorArp,minorArp}; // ERROR HERE
If I can use this way of organising my structs, I'll only have to change the size of the array and add the new const struct to the array, each time I create a new const struct.
Or am I way off track here? Would enums or MACROS help me out?
EDIT: The freqRatios are defined with macros and I understand that the initial 0.0 is likely to be redundant...
Variables with static storage duration have to have compile-time constant initializers in C. And another variable does not count as a compile-time constant (even if that variable is const).
You could write:
#define MAJOR_ARP {3,"Major Arpeggio",{0.0,JUST_MAJ_3,JUST_PERF_5}}
#define MINOR_ARP {3,"Minor Arpeggio",{0.0,JUST_MIN_3,JUST_PERF_5}}
const ARPINFO majorArp = MAJOR_ARP;
const ARPINFO minorArp = MINOR_ARP;
const ARPINFO arpInfoArray[2] = { MAJOR_ARP, MINOR_ARP };
Note that this has two copies of your data. If you're OK with having one copy of the data and the other static variables referencing it, you could instead do:
const ARPINFO *const arpInfos[2] = { &majorArp, &minorArp };
You should describe the error you get. Trying to compile it myself resulted in:
main.cpp:14:33: error: initializer element is not a compile-time constant
const ARPINFO arpInfoArray[2] = {majorArp,minorArp}; // ERROR HERE
^~~~~~~~~~~~~~~~~~~
To which the solution is:
const ARPINFO arpInfoArray[2] = {
{3,"Major Arpeggio",{0.0,JUST_MAJ_3,JUST_PERF_5}},
{3,"Minor Arpeggio",{0.0,JUST_MIN_3,JUST_PERF_5}}
};