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.
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");
For example, I have this block:
int nFirst, nSecond;
char sInput[10];
printf("Which variable to change to 10?");
scanf("%s", &sInput);
// BAD - inflexible and unmaintainable
if(strcmp(sInput,"nFirst") ==0){
nFirst = 10;
}
else if (strcmp(sInput,"nSecond")==0) {
nSecond =10;
}
Is there a nice way to do this? like treat a string as if its a variable name?
No, there is no "nice" way of doing this in C. Variable names (typically) aren't preserved in the generated machine code, except to support debugging. C doesn't have a built-in mechanism for translating a string value into a reference to a variable of the same name.
You would have to map variable names to variables manually. You could build a lookup table, associating a string value with the address of the corresponding variable:
struct vn {
char *varname;
void *addr;
Typeinfo t;
};
where Typeinfo is some enumeration or other mechanism for encoding the type of the variable, giving you something to the effect of
int foo;
double bar;
char *blurga;
struct vn varsByName[] = { {"foo", &foo, IntType},
{"bar", &bar, DoubleType},
{"blurga", blurga, CharPtrType} };
I don't recommend doing this.
Another, platform-dependent approach is to put all your variables into a shared library and then have access to them by names. Have a look at dlsym/dlopen functions.
void* handle = dlopen("mysymbols.so", RTLD_LOCAL | RTLD_LAZY);
int* var = (int*) dlsym(handle, user_typed_name);
*var = 10; /* modify the variable */
You could implement something like a dictionary or a two-dimensional array which contains the "variable name" and the value. Then this comes down to setting an array element to a new value.
Other than that: C# and other object oriented languages would allow this through reflection, but as C itself isn't object oriented, you can not do that (C++ support for this seems to be very limited).
You can do it with a macro:
#define MAYBESET(name) if (strcmp(sInput, #name) ==0 ){ name = 10; }
#name is the real value of name changed to a string literal.
For a small number of variables then your algorithm should perform well. If there are many variables that could be changed, rather than just two, then another algorithm should be considered. Making this pretty and clear isn't exactly easy in C.
If you really wanted this to be faster you could either do a hash table or use a switch/case like:
int First, Second; // Note that I got rid of your leading n
char sInput[10];
printf("Which variable to change to 10?");
scanf("%s", &sInput);
// BAD - inflexible and unmaintainable
// referring to character array overflow potential, I assume
switch (sInput[0])
{
case 'F':
if (0 == strcmp("irst", sInput+1) )
{
First = 10;
} else
{
// error
}
break;
case 'S':
if (0 == strcmp("econd", sInput+1) )
{
Second = 10;
} else
{
// error
}
break;
default:
// error
break;
}
If you don't like the way that this looks then you could use macros (#define) to make it less big looking, but it would turn out the same. Another option that you could employ would be to write a small program that output the source code of this program which would handle all of the repetitive and tedious parts.
Another way to do this, if all of the variables are of the same type, would be to create an array of them and input their index in rather than a name, but then you have to add code to check against inputting an index out of range of the size of the array.
I have multiple void functions that relies on the each individual output of the functions since there are multiple variables (that are the same throughout the code), where each functions' output will be "stored" to them and be passed to another.
So, I decided to make those variables into global variables by making them static .... right after all the necessary #include... codes.
I was able to utilize all functions (14 functions in total,all void) by only calling four of them (Each functions, after processing its own function, passes the result into another function and after series of passing, only four of them are needed to be called in int main())
Now, I created another void function that requires the global variables as its parameter since that void function relies on the data that all the other functions "copied and put" into the global variables declared earlier. (Which I found is not working, since I heard that storing data into global variables is not possible.)
Can anyone teach me if there is any other way to create series of functions which requires output of each individual functions?
I checked if the variables were stored properly, so I tried using printf method right after the #3 process. I found out nothing gets printed when I expected a value from the struct data to be printed.
Ex:
typedef struct database{
//... variables
}data;
typedef struct itembase{
//... variables
}item;
static data user1;
static data user2;
static data *pointer[10000];
static item *pointer2[10000];
static item current[10000]; //Shares same value of the bracket with *pointer2
static data sectionA[1][10000];
static data sub_section[3][10000];
static int datacounter = 0; //..will be put inside the bracket of *pointer
static int itemcounter = 0; //..will be put inside the bracket of *pointer2
static int typenum = 0; ..will be put inside the first bracket of all the sections and subsections
static int section_count = 0; //..will be put inside the second bracket of all sections
static int sub_section_count[3] = {0}; //..will be put inside the second bracket of all sub_sections. The [3] will be the value of the typenum.
void load_data() // Accepts User's input and store them into struct data's variable using singly-linked list
{
//.... All data will be stored to *pointer[datacounter]
binarycheck(pointer[datacounter]->encoding,*pointer,datacounter);
//.... The `typedef struct` of data contains 12 variables. After storing 12 variables, datacounter will be ++ and the program will still continue to accept input from the user
}
void load_item()
{
//.... All item will be stored to *pointer2[itemcounter]
memcpy(¤t[itemcounter],pointer2[itemcounter],sizeof(item));
}
void binarycheck(data encoding,data *pointer,int datacounter)
{
if ((encoding&128)==128){
typenum = 3;
memcpy(§ionA[typenum][section_count],pointer,sizeof(data));
sub_sectionA[typenum][sub_section_count[typenum]] = sectionA[typenum[section_count];
section_count++;
sub_section_count++;
}
}
void askitem(data user)
{
// Tried putting `printf(" %s User1 Data#1",user1.firstdata);` and it works perfectly fine.
// Ask for user's selection on item
// If the item is found, then the content of that item will modify the data of the variable of `user`
}
void askinput(data user)
{
int whattype = 0;
int whatsub = 0;
printf("What type do you want?: \n);
scanf("%d",&whattype);
if (whattype == 1)
{typenum = 1;}
printf("What Sub type do you want?: \n);
scanf("%d",&whatsub);
if (whatsub == 1)
{ user = sub_sectionA[typenum][sub_section_count[typenum]];}
askitem(user);
}
void final_print(data user, data user2)
{
printf("%d\n",user.Adata);
printf("%d\n",user2.Adata);
}
int main()
{
load_data();
load_item();
askinput(user1);
//Tried putting `printf(" %s User1 Data#1",user1.firstdata);` but nothing shows.
askinput(user2);
//Nothing shows
final_print(user1,user2); //Nothing shows
}
Take a look at this function:
void askinput(data user)
Here you pass user by value to the function. When you pass by value, the function receives a copy of the variable. Changes that you make inside the body of that function only affect the copy. They are not visible to the caller's variable.
Instead you need to pass a reference. In C that means passing a pointer to a variable:
void askinput(data *user)
Inside the body of the function you need to de-reference the pointer to access members. So you use -> rather than . to refer to members.
And when you call the function you need to pass a pointer to the variable. So the call becomes:
askinput(&user1);
Frankly I do not understand why you are using global variables here at all. It's generally preferable to pass parameters otherwise you do find yourself struggling to keep track of which different version of the variable you are meant to be working on.
Finally, you have written your entire program and trying to debug this specific problem in the context of the entire program is confusing you. You really should have cut this down to a 10 or 20 line simple reproduction. Being able to do that in the future will make life much easier for you.
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.
Perhaps this bridge likely has been crossed many times and in many ways... reading a simple text .conf file and acting upon its entries.
In my case, the file format is simple.. a series of tokens and assignments, as in:
token_name_1 value
with a tab character as the field delimiter and a unix line-ending for each the record.
The .conf file directly alters certain program configurations, all of them stored in a single structure. Variables of types Integer, float, char[], and *char are represented in the structure.
A quick but boring approach involves, for example:
if (strcasecmp(token,"token_name_1")==0)
token_name_1=value;
But I determined that it would be sweet to do the deed in a nice tight loop. In C.
So it seemed best to construct an array which provides pointers to each of the structure variables I wish to expose; another that provides the name of the variable; and a third which describes the stored data type and a desired default value.
These look like this:
const char* allowed_tokens[] =
{
"loglevel",
"debugecho",
"errorSqlDisable",
"ClearErrorDbOnExit",
"\0" // terminates list
}
int *varpointers[] =
{
&appinfo.nLogLevel,
&appinfo.debugEcho,
&appinfo.OWFSLogLevel,
&appinfo.OWFSLogEchoToDisplay,
0 // terminates list
};
char *varDatatypes_defaults[] =
{
"I|6", // for LOG_INFO
"B|false",
"I|0",
"B|true",
"\0" // terminates list
};
The loop looks like this (pseudocode):
row=0;
while (read a line of the .conf file into cLine)
{
get the token_name and value from cLine
check if allowed_tokens[row]==0 and if true, exit the loop
// example cLine= "debugecho false"
find match to "debugecho" in allowed_tokens. This provides an offset into varpointers and varDatatypes.
get the default data type and default value tokens from varDattypes_defaults[row]
Do the assignment. For example, if the data type=="I":
*varpointers[row]=atoi(value);
++row;
}
This technique works fine, but there are two problems.
It would be preferable to combine the three arrays into a single array. Is there a best practice here?
The array of pointers (varpointers[]) is defined as *int. I did so as I want it to hold pointers. However if the variable pointed to is not an integer data type, warning: initialization from incompatible pointer type is triggered. Of course, char * and int * cannot be mixed... so how otherwise could this be done such that a single pointer array is used?
I realize I can do all this in c++. This luxury is not an option at this point.
You can combine them into one array by structs, e.g.
typedef struct { char *token; void *var; char *defaults; } the_type;
the_type the_data[] = { { "loglevel", (void*)&appinfo.nLogLevel, "I|6" },
{ "debugecho", (void*)&appinfo.debugEcho, "B|false" },
...
};
The generic pointer type is void *. Your code has to ensure you use the correct type when actually writing to the variable being pointed to, e.g. *(int*)the_data[0] = 42;.
I would use an enumeration to specify the types so you don't have to parse a string. The values could be stored in an union.
typedef enum {
BOOLEAN,
INTEGER,
} type_t;
typedef union value {
bool boolean;
int integer;
} value_t;
typedef struct token {
char *name;
type_t type;
value_t value;
} token_t;
Now you can define your defaults like so:
token_t data[] = {
{ "loglevel", INTEGER, { 6 } },
{ "debugecho", BOOLEAN, { false } },
{ "errorSqlDisable", INTEGER, { 0 } },
{ "ClearErrorDbOnExit", BOOLEAN, { true } },
{ 0 }
};
This will get pretty cumbersome if the number of configuration keys gets large. You might want to think about storing the configuration in a hash table or a tree.
Here is a short example that seems to accomplish what you want.
If we are talking about the same data type, use double pointers (you get an array of arrays)
Declare a struct holding your pointers, then use a pointer to your struct to work on.
For declaring a general pointer, you can use void instead of int. But then every time you have to cast the pointer to use it properly.