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.
Related
I have two arrays of the alphabet in the following format:
const char plain[26] = {'a','b',....'y','z'} // this is the the full alphabet
const char crypt[26] = {'i','d',....'m','x'}; // this is the the alphabet scrambled
The order of the alphabet in both arrays can change depending on input. This change happens in the main function.
The purpose of this is to match the letters of the string to the second, like an encryption. I compare characters to array values. So it would kind of look like this (simplified)
text[3] = 'yes';
changed[3];
if(text[0] == plain[25]){ //would be done under a for loop so 25 would be a changing integer value
changed[0] = [crypt[25];
}
My code works under perfectly under the main function. I wanted to mention my purpose like this because I was having previous problems simply due to the type of array and formatting. And since the array is moved outside, I will probably/am running into those problems again.
Now I want to make the arrays global. The actual encryption happens in a function that does not take the arrays as a variable. But I want the function to have access to them.
Here's what it looks likes right now
const char plain[26];
const char crypt[26];
int maint(void){
const char plain[26] = {'a','b',....'y','z'} \\values get changed here
const char crypt[26] = {'i','d',....'m','x'} \\and here
While this provides no errors, I dont get an output, I believe the other functions are using a blank array instead of the changed one (if the change even worked).
I've tried different array types, I believe the issue is in initialization or giving the array values.
Edit: To clarify, the two arrays can be in any order. A text file will randomize the order can give it to me in the format:
b,r
m,o
l,s
...
...
...
Both cases the alphabet is randomized. Where the first column would correspond to the first array (plain), second would be to second array (crypt).
If there's a way to read by columns and store in the format
plain = 'bml...'; \\whole alphabet randomized
crypt = 'ros...'; \\whole alphabet randomized
That would also work.
The plain and crypt you have in main aren't the same as the global ones. Since you declare them again, they're new ones that are only visible in main. Thus you're not changing the global ones.
Instead, only declare them once globally and do assignment in the main function:
char plain[26];
int main(void) {
memcpy(plain, "abcdefghijklmnopqrstuvwxyz", 26); //values get changed here
return 0; // indicate successful program execution
}
Also note that there are some syntax errors in
const char plain[26] = {'a','b',....'y','z'} \\values get changed here
Comments start with //, not \\, and you need a ; after a statement. Also, the int main should return an int in C.
Of course, if you don't need to actually change the memory and only assign it to predefined sets of characters, you can do it like this:
const char *plain;
int main(void) {
plain = "abcdefghijklmnopqrstuvwxyz";
return 0;
}
This way you can still read from it with syntax like plain[5], but you can't assign to it with, say plain[5] = 'a';.
Remove "const char" before plain and crypt arrays in main function to see the actual issue.
“Const Char” before plan and crypt arrays actually declare two new local char array constants with same name. As “const” char array can only be initialized at the declaration time therefore initialization in main does not throw error because they are not same global conts arrays.
Instead of using const use Const pointer as suggested in below answer
Another way to look at it is that plain and crypt will both decay to pointers on access (subject to the exceptions in) C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3). So why not just use global pointers to begin with that can then be reassigned as needed throughout your code?
This provides the flexibility of assignment from (1) string literals; (2) constant arrays; or (3) from compound literal initializers. You can #define a constant for the size (number of characters) plain and crypt point to.
For example:
#include <stdio.h>
#define NC 3 /* number of chars (26 for you) */
const char *plain, *crypt; /* global pointers */
void prnchars (const char *arr)
{
for (int i = 0; i < NC; i++)
putchar (arr[i]);
putchar ('\n');
}
int main (void) {
plain = "abc"; /* assigning literals */
crypt = "def";
prnchars (plain);
prnchars (crypt);
crypt = "ghi"; /* reassign at will */
prnchars (crypt);
const char foo[] = "jkl"; /* constant arrays */
crypt = foo;
prnchars (crypt);
crypt = (const char[]){'m', 'n', 'o'}; /* compound literals */
prnchars (crypt);
return 0;
}
Example Use/Output
$ ./bin/global_ptr
abc
def
ghi
jkl
mno
It's just another way of looking at the problem.
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}}
};
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
};
I'm stuck on some homework which isn't graded (its meant for practice).
I have to create a function called find_name that takes 2 arguments. The first argument is a 2D array of names (strings), and the second is a character string which is used to find the name in the 2D array, the function must return 1 if found else 0.
When i call the function (which is empty right now), I get this warning: passing argument 1 of 'find_name' from incompatible pointer type
Here is the important bits.
In Main
char strNameList[][2] = { { "Luca","Daniel"} ,{"Vivan","Desmond"},{"Abdul","Justin"}, {"Nina","Marlene"},{"Donny","Kathlene"} };
char strFindName[] = "\0";
printf("Please enter a name to look for: ");
gets(strFindName);
nSearch = find_name(strNameList, strFindName);
The Function
int find_name(char strNameList[][2], char strLookUp[])
I'm new to C (I'm a student), and I'm completely confused about strings (string arrays etc).
I'm assuming you want a 2D array of char pointers. Your declaration of strNameList is incorrect in both locations in your program. You have:
char strNameList[][2] = { { "Luca","Daniel"} ,{"Vivan","Desmond"},{"Abdul","Justin"}, {"Nina","Marlene"},{"Donny","Kathlene"} };
But char[][N] is declaring a 2D array of chars, not char* Therefore you're being warned by the compiler you're assigning a raft of pointer values to items of type char
Change both your declarations (your variable and your function parameter) to:
const char *strNameList[][2]
which declares an array of unknown length of arrays of two char*, which now matches your initialization lists. Also, the const is added because (a) I'm assuming you are not planning on modify that name list in your function, and (b) writable string literal declarations assigned to char* via initializer is undefined behavior in C, and officially deprecated in C++, so you should not be using it regardless. Likewise, your lookup-name is probably not being modified either, so also declare it const.
Result:
const char * strNameList[][2] = {
{"Luca","Daniel"} ,
{"Vivan","Desmond"},
{"Abdul","Justin"},
{"Nina","Marlene"},
{"Donny","Kathlene"}
};
and in your function:
int find_name(const char * strNameList[][2], const char strLookUp[])
Last but certainly not least, unless you have a crystal ball your find_name() function has no way of knowing with the given information how many names are in the name list being passed. I'd rather you see this now rather than wonder what happened later. you need to either (a) terminate the list with a token-value that find_name() knows about, or (b) pass the number of names in the list to find_name(). To each their own, but I prefer the latter of these:
int find_name(const char * strNameList[][2], size_t nNameListSize, const char strLookUp[])
and invoke it on your caller side by:
find_name(strNameList, sizeof(strNameList)/sizeof(strNameList[0]), strFindName)
Do it this way:
#define STOPPER_NAMELIST NULL
char * strNameList[][2] = {
{ "Luca","Daniel"},
{"Vivan","Desmond"},
{"Abdul","Justin"},
{"Nina","Marlene"},
{"Donny","Kathlene"}
{STOPPER_NAMELIST, STOPPER_NAMELIST}
};
size_t sizeNameList(const char * strNameList[][2])
{
size_t size = 0;
while ((strNameList[size][0] != STOPPER_NAMELIST) &&
(strNameList[size][0] != STOPPER_NAMELIST))
++ size;
return size;
}
int find_name(char * strNameList[][2], char strLookUp[])
{
size_t size = sizeNameList(strNameList);
...
}
...
nSearch = find_name(strNameList, strFindName);
This approach uses an open array ([]) of char * arrays with 2 entries.
Update:
You could add a stopper element to the array carring the names, then there is no need to pass around the array's size along with array itself, as the size could alway be determined by scanning the array members until the stopper is found.
Your function find_name() is looking for a 2-D array of characters ie:
char arr[][2] = { { 'a', 'b'}, ...
if you want to make them strings you need:
char *arr[][2] = { {"John", "Smith"}, ...
Then in the function parameter list you need:
void find_name(char *something[][2])
{
printf("first name: %s, second name: %s\n", something[0][0], something[0][1]);
And in your main() function call it just by:
find_name(arr);