I'm trying to initialize an array of strings in C. I want to set one of the elements of the array from a variable, but I'm getting a compiler error. What's wrong with this?
char * const APP_NAME = "test_app";
char * const array_of_strings[4] = {
APP_NAME,
"-f", "/path/to/file.txt",
NULL
};
The error is error: initializer element is not constant.
The standard distinguishes const-qualified variables and compile time constants.
Evaluating a variable (APP_NAME) is not considered to be a compile time constant in the sense of the C standard. This
char const app_name[] = "test_app";
char const*const array_of_strings[4] = {
&app_name[0],
"-f", "/path/to/file.txt",
0,
};
would be allowed, since this is not evaluating app_name but only taking its address.
Also, you always should treat string literals as if they had type char const[]. Modifying them has undefined behavior, so you should protect yourself from doing so.
I was able to get it to compile with this syntax using gcc 4.6.3:
char* const APP_NAME = "test_app";
char* const array_of_strings[4] = {
APP_NAME,
"-f", "/path/to/file.txt",
NULL
};
You could also try casting to a const (at your own risk) if the compiler rejects everything else:
char* const APP_NAME = "test_app";
char* const array_of_strings[4] = {
(char* const)APP_NAME,
"-f", "/path/to/file.txt",
NULL
};
Related
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.
The Google searches I have tried lead me to people with bugs in their programs. I'd rather just know how to do it properly than backtrack from faulty code.
I have an array, it's const int.
In my class I want to initialise a different array but with the same number of elements.
After initialising the first array, in the class I've tried:
int array[array.length()];
But the compiler moans it isn't a constant expression.
Even when I
const string thestring = "Dummy example";
const static int strlen = (int)thestring.length();
Then later in the class I:
class dostuff {
int newstring[strlen];
);
The compiler Still complains at me.
This leaves me trying first do declare, eg:
const string thestring = "Dummy";
Then in the class, just count the elements by hand:
class Enigmatise {
int duplicatelengthstring[5]; // Just counted by hand. :-(
);
The compiler is happy now it has a constant expression, but I'm not happy because if I change the definition of the principle string to "More characters", it's then down to me to count them out by hand, or cout them with .length(), and then use a new constant numerical expression in the class, doing it all by hand. This looks accident prone.
Therefore, if I have a
const string thestring = "Dummy example";
How do I then declare, in a class, another array of equal length to the dummy within class?
You could use dynamic allocation;
class anotherclass {
const std::string thestring;
int * const otherArray;
anotherclass() : thestring("some string"),
otherArray(new int[(int)thestring.length()]){}
~anotherclass(){delete[] otherArray;}
};
EDIT: This compiles without warnings
class anotherclass {
static const std::string thestring;
static const int strlen;
public:
void dosomething( ){int g[strlen]; }
};
const std::string anotherclass::thestring = "mystring";
const int anotherclass::strlen = anotherclass::thestring.length();
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 program a application with mikroC PRO for AVR on the Hardware mikromedia for XMEGA.
I have different languages to show on a TFT Display.
I must switch the languages at runtime.
This works the following way, as example for the languages german and english.
I have two Pointer Arrays for the languages:
char* deu_hilfe[] = {
"START = Startet die Messung",
"STOPP = Stoppt die Messung",
"SETUP = Aufruf des Setups",
};
char *eng_hilfe[] = {
"START = Start the measurement",
"STOPP = Stop the measurment",
"SETUP = Open the setup",
};
I define a pointer to a pointer:
char **sprache_hilfe;
Then i allocate the pointer to the german or english array:
sprache_hilfe = eng_hilfe;
sprache_hilfe = deu_hilfe;
And use it:
TEXT_HI_HITXT_1.Caption = sprache_hilfe[SP_HILFE_ALG_BUTTON];
This works fine, but my problem is, that the arrays are in the RAM and my RAM is full now.
So i tried to make the arrays CONST.
But when i cast my array to the pointer with:
char **sprache_hilfe = (char*)eng_hilfe;
I see no text on the display.
I think something is wrong with my cast.
Has anyone a better solution or knows what is wrong with my code?
Greetings from Germany
Patrick
To move the arrays and strings into ROM, I would declare them as follows
const char * const deu_hilfe[] = {
"START = Startet die Messung",
"STOPP = Stoppt die Messung",
"SETUP = Aufruf des Setups",
};
const char * const eng_hilfe[] = {
"START = Start the measurement",
"STOPP = Stop the measurment",
"SETUP = Open the setup",
};
These declarations specify that the strings are const, and the array of pointers is also const. You can then access the strings with code like this
const char * const *sprache_hilfe;
sprache_hilfe = eng_hilfe;
printf( "%s\n", sprache_hilfe[1] );
sprache_hilfe = deu_hilfe;
printf( "%s\n", sprache_hilfe[1] );
If that still doesn't work, then you'll have to look at the MAP output of the linker, to see where the linker decided to put the strings and the arrays. Then use the debugger to look at those memory addresses to verify that the strings and arrays were properly burned into the read-only memory.
The actual characters of the string literals end up in flash anyway. Where you can save RAM is the two arrays, which occupy 24 bytes (2*3 pointers) in case of a 32-bit controller. So insert the const qualifiers like this. GCC has moved the arrays to the .rodata section after this.
char *const deu_hilfe[] = {
"START = Startet die Messung",
"STOPP = Stoppt die Messung",
"SETUP = Aufruf des Setups",
};
char *const eng_hilfe[] = {
"START = Start the measurement",
"STOPP = Stop the measurment",
"SETUP = Open the setup",
};
Your original code should work now, without any casting. You probably get 2 warnings from the compiler. GCC complains:
warning: assignment discards ‘const’ qualifier from pointer target type
To avoid that, you can either do a cast at assignment:
sprache_hilfe = (char**)eng_hilfe;
Or declare sprache_hilfe to point to const arrays only:
char *const *sprache_hilfe;
You original assignment
sprache_hilfe = (char*)eng_hilfe;
is wrong, as eng_hilfe points to char pointers, not chars.
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}}
};