Best way to share multiple global variables between compilation units in C - c

I am trying to 'gather' global cmdOps spread in multiple files into lookup table for convenient usage in main. I did as shown below, but as pointed out this approach does not guarantee file1.c and file2.c to use the same variables, but I really want to avoid using extern declarations in file1.c as there will be tens of them if not more.
main.c:
#include "file1.h"
int getLutIdx(int argc, char *argv[]);
extern myLUT *LUT;
int main(int argc, char *argv[])
{
int idx = getLutIdx(argc, argv);
myArgs args;
LUT[idx].ops->parseArgs(argc, argv, &args);
LUT[idx].ops->validateArgs(&args);
LUT[idx].ops->executeCmd(&args);
}
file1.h:
typedef struct myArgs {
union {
cmd1Args_t cmd1Args;
cmd2Args_t cmd2Args;
...
}
}myArgs;
typedef int (*op1)(int argc, char *argv[], myArgs *args);
typedef int (*op2)(myArgs *args);
typedef int (*op3)(myArgs *args);
typedef struct cmdOps {
op1 parseArgs;
op2 validateArgs;
op3 executeCmd;
} cmdOps;
typedef struct myLUT {
char *cmdName;
cmdOps *ops;
}myLUT;
file1.c:
#include "file1.h"
#include "file2.h"
#include "file3.h"
myLUT LUT[CMD_NUM] {
{ "CMD1", &cmd1Ops },
{ "CMD2", &cmd2Ops },
...
}
file2.h:
int cmd1ParseArgs(int argc, char *argv[], myArgs *args);
int cmd1ValidateArgs(myArgs *args);
int cmd1Execute(myArgs *args);
int cmd2ParseArgs(int argc, char *argv[], myArgs *args);
int cmd2ValidateArgs(myArgs *args);
int cmd2Execute(myArgs *args);
cmdOps cmd1Ops;
cmdOps cmd2Ops;
file2.c
#include "file1.h"
#include "file2.h"
myOps cmd1Ops = {
.parseArgs= cmd1ParseArgs,
.validateArgs = cmd1ValidateArgs,
.executeCmd = cmd1Execute
}
myOps cmd2Ops = {
.parseArgs= cmd2ParseArgs,
.validateArgs = cmd2ValidateArgs,
.executeCmd = cmd2Execute
}
...
Whole question edited, thanks for previous comments.
Goal is for user to invoke:
./myProg <cmd_name> <cmd_args>
and each command (or sets of commands) can accept different parameters

The "best way" would be to not have any global variables (or at least, as few as possible), since global variables are likely to make your program difficult to understand and debug as it gets larger and more complex.
If you must have global variables, however, I suggest declaring the global variables in just one .c file:
myOps file2Ops; // in somefile.c
... and placing any necessary extern declarations in a .h file that other files can include:
extern myOps file2Ops; // in someheader.h

But it baffles me why don't I get any error or warning about duplicated declaration…
At file scope, myOps file2Ops; is a tentative definition, which is not actually a definition, directly. If there is no definition for it by the end of the translation unit, the behavior is as if there were a definition myOps file20ps = {0};. (Having an initializer would make the declaration a proper definition instead of a tentative definition.)
Then, because myOps file20ps; appears in file2.h and that is included in both file1.c and file2.c, your program has multiple external definitions of file20ps. The C standard does not define the behavior of this because it violates the constraint in C 2018 6.9 5:
… If an identifier declared with external linkage is used in an expression (other than as part of the operand of a sizeof or _Alignof operator whose result is an integer constant), somewhere in the entire program there shall be exactly one external definition for the identifier; otherwise, there shall be no more than one.
Historically, some C implementations have defined the behavior of external identifiers resulting from tentative definitions to act as “common” symbols. When linking the object modules, a definition originating from a tentative definition will be coalesced with other definitions, so there will be only one definition in the final program. Your C implementation (notably the compiler and the linker) appears to be doing this, and that would be why you did not get an error message.
(This was the default behavior for GCC prior to version 10. You can request the old behavior with -fcommon or the non-coalescing behavior with -fno-common. Some additional information is here and here.)
… and I also don't know if this approach would be considered 'good practice'
You have not shown much context for what you are doing or why. Generally, external identifiers for objects should be avoided, but there can be appropriate uses.

Related

How does linking work in C with regards to opaque pointers?

So, I've been having a bit of confusion regarding linking of various things. For this question I'm going to focus on opaque pointers.
I'll illustrate my confusion with an example. Let's say I have these three files:
main.c
#include <stdio.h>
#include "obj.h" //this directive is replaced with the code in obj.h
int main()
{
myobj = make_obj();
setid(myobj, 6);
int i = getid(myobj);
printf("ID: %i\n",i);
getchar();
return 0;
}
obj.c
#include <stdlib.h>
struct obj{
int id;
};
struct obj *make_obj(void){
return calloc(1, sizeof(struct obj));
};
void setid(struct obj *o, int i){
o->id = i;
};
int getid(struct obj *o){
return o->id;
};
obj.h
struct obj;
struct obj *make_obj(void);
void setid(struct obj *o, int i);
int getid(struct obj *o);
struct obj *myobj;
Because of the preprocessor directives, these would essentially become two files:
(I know technically stdio.h and stdlib.h would have their code replace the preprocessor directives, but I didn't bother to replace them for the sake of readability)
main.c
#include <stdio.h>
//obj.h
struct obj;
struct obj *make_obj(void);
void setid(struct obj *o, int i);
int getid(struct obj *o);
struct obj *myobj;
int main()
{
myobj = make_obj();
setid(myobj, 6);
int i = getid(myobj);
printf("ID: %i\n",i);
getchar();
return 0;
}
obj.c
#include <stdlib.h>
struct obj{
int id;
};
struct obj *make_obj(void){
return calloc(1, sizeof(struct obj));
};
void setid(struct obj *o, int i){
o->id = i;
};
int getid(struct obj *o){
return o->id;
};
Now here's where I get a bit confused. If I try to make a struct obj in main.c, I get an incomplete type error, even though main.c has the declaration struct obj;.
Even if I change the code up to use extern, It sill won't compile:
main.c
#include <stdio.h>
extern struct obj;
int main()
{
struct obj myobj;
myobj.id = 5;
int i = myobj.id;
printf("ID: %i\n",i);
getchar();
return 0;
}
obj.c
#include <stdlib.h>
struct obj{
int id;
};
So far as I can tell, main.c and obj.c do not communicate structs (unlike functions or variables for some which just need a declaration in the other file).
So, main.c has no link with struct obj types, but for some reason, in the previous example, it was able to create a pointer to one just fine struct obj *myobj;. How, why? I feel like I'm missing some vital piece of information. What are the rules regarding what can or can't go from one .c file to another?
ADDENDUM
To address the possible duplicate, I must emphasize, I'm not asking what an opaque pointer is but how it functions with regards to files linking.
Converting comments into a semi-coherent answer.
The problems with the second main.c arise because it does not have the details of struct obj; it knows that the type exists, but it knows nothing about what it contains. You can create and use pointers to struct obj; you cannot dereference those pointers, not even to copy the structure, let alone access data within the structure, because it is not known how big it is. That's why you have the functions in obj.c. They provide the services you need — object allocation, release, access to and modification of the contents (except that the object release is missing; maybe free(obj); is OK, but it's best to provide a 'destructor').
Note that obj.c should include obj.h to ensure consistency between obj.c and main.c — even if you use opaque pointers.
I'm not 100% what you mean by 'ensuring consistency'; what does that entail and why is it important?
At the moment, you could have struct obj *make_obj(int initializer) { … } in obj.c, but because you don't include obj.h in obj.c, the compiler can't tell you that your code in main.c will call it without the initializer — leading to quasi-random (indeterminate) values being used to 'initialize' the structure. If you include obj.h in obj.c, the discrepancy between the declaration in the header and the definition in the source file will be reported by the compiler and the code won't compile. The code in main.c wouldn't compile either — once the header is fixed. The header files are the 'glue' that hold the system together, ensuring consistency between the function definition and the places that use the function (references). The declaration in the header ensures that they're all consistent.
Also, I thought the whole reason why pointers are type-specific was because the pointers need the size which can vary depending on the type. How can a pointer be to something of unknown size?
As to why you can have pointers to types without knowing all the details, it is an important feature of C that provides for the interworking of separately compiled modules. All pointers to structures (of any type) must have the same size and alignment requirements. You can specify that the structure type exists by simply saying struct WhatEver; where appropriate. That's usually at file scope, not inside a function; there are complex rules for defining (or possibly redefining) structure types inside functions. And you can then use pointers to that type without more information for the compiler.
Without the detailed body of the structure (struct WhatEver { … };, where the braces and the content in between them are crucial), you cannot access what's in the structure, or create variables of type struct WhatEver — but you can create pointers (struct WhatEver *ptr = NULL;). This is important for 'type safety'. Avoid void * as a universal pointer type when you can, and you usually can avoid it — not always, but usually.
Oh okay, so the obj.h in obj.c is a means of ensuring the prototype being used matches the definition, by causing an error message if they don't.
Yes.
I'm still not entirely following in terms of all pointers having the same size and alignment. Wouldn't the size and alignment of a struct be unique to that particular struct?
The structures are all different, but the pointers to them are all the same size.
And the pointers can be the same size because struct pointers can't be dereferenced, so they don't need specific sizes?
If the compiler knows the details of the structure (there's a definition of the structure type with the { … } part present), then the pointer can be dereferenced (and variables of the structure type can be defined, as well as pointers to it, of course). If the compiler doesn't know the details, you can only define (and use) pointers to the type.
Also, out of curiosity, why would one avoid void * as a universal pointer?
You avoid void * because you lose all type safety. If you have the declaration:
extern void *delicate_and_dangerous(void *vptr);
then the compiler can't complain if you write the calls:
bool *bptr = delicate_and_dangerous(stdin);
struct AnyThing *aptr = delicate_and_dangerous(argv[1]);
If you have the declaration:
extern struct SpecialCase *delicate_and_dangerous(struct UnusualDevice *udptr);
then the compiler will tell you when you call it with a wrong pointer type, such as stdin (a FILE *) or argv[1] (a char * if you're in main()), etc. or if you assign to the wrong type of pointer variable.

C - problem with const - error: initializer element is not constant

I try add global variable in my buttons.c file, but have a error - initializer element is not constant. Example headers.h file
struct MainStruct {
GtkEntryBuffer *buffer;
GtkWidget *entry;
GtkWidget *label;
};
extern struct MainStruct *p;
extern const char *text_entry;
void lower_button_clicked(GtkWidget *lowerbutton);
and when file main.c calls file buttons.c, I cannot define the variable text_entry. What am I doing wrong?
buttons.c
#include <gtk/gtk.h>
#include "headers.h"
const char *text_entry = gtk_entry_buffer_get_text(p -> buffer); // is not constant, why?
void lower_button_clicked(GtkWidget *lowerbutton)
{
printf("%s\n", text_entry);
}
I saw a lot of similar questions that talk about static, but
static const char *text_entry = gtk_entry_buffer_get_text(p -> buffer);
not working.
How do I define this variable to be global? to avoid duplication in similar functions
From the C Standard (6.7.9 Initialization)
4 All the expressions in an initializer for an object that has static
or thread storage duration shall be constant expressions or string
literals.
And in this declaration
const char *text_entry = gtk_entry_buffer_get_text(p -> buffer);
the initializer is not a constant expression. So the compiler issues an error.
Also pay attention to that these declarations
extern const char *text_entry;
and that follows it
static const char *text_entry = /*...*/;
contradict each other. The first declaration declares the variable text_entry as having external linkage while the second declaration declares the variable as having internal linkage.

C: Passing variables between files

I'm trying to pass a local variable (in func1) to a function (func2) in another file, but func2 requires that as a global variable. To explain things better, here are the two files:
file1.c:
#include <something.h>
extern void func2();
void func1(){
int a=0;
func2();
}
file2.c:
#include <something.h>
extern int a; //this will fail
void func2(){
printf("%d\n",a);
}
The variable int a can't be declared as global in file1, as func1 is called recursively. Is there a better way to do this?
In file1.c:
#include <something.h>
#include "file1.h"
int a;
void func1(){
a = 0;
}
In file1.h
extern int a;
In file2.c:
#include <something.h>
#include "file1.h"
void func2(){
printf("%d\n",a);
}
So:
The variable is in file1.c
file1.h allows others to know that it exists and its type is int.
file2.c includes file1.h so that the compiler knows about var a existence before file2.c tries to use it.
Sorry I can’t comment on Ciro Pedrini’s answer, so I would say: do as he says, but also:
Make the argument list of func1 explicitly (void) (and set your compiler to complain about empty argument lists), because () just means you are not specifying what the arguments are, at least in C, which your question is tagged as.
Declare void func1(void); as well as a in file1.h.
#include file1.h in file1.c, so the compiler checks that the definitions (in file1.c) of a and func1 are consistent with their declarations (in file1.h).
But, although you may have to do this as an exercise, try to avoid passing information in global variables: arguments are usually more reliable, as it is easier to ensure that no other part of the programme alters them. And you talk about passing information to a recursive function through a global variable: in that case you must be especially careful to pick up and save the value in func1 before a recursive call alters the value - passing an argument is so much easier and more reliable!
Ciro's solution would have worked if I am only using func1 once; however func1 is called recursively so variable a cannot be global in file1 (i.e. the variable has to live on the stack and not on the heap). Here's the solution I ended up using:
File1.c:
#include <something.h>
extern void func2_new(int b);
void func1(){
int a=0;
func2_new(a);
}
File2.c:
#include <something.h>
int a;
void func2(){
printf("%d\n",a);
}
void func2_new(int b){
a=b;
func2();
}
As many have pointed out, the only answer would be to refactor func2 completely. I welcome more input to this solution.

variable between files [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I share variables between different .c files?
If I have two source files, and one header: file1.c, file2.c, and header.h, and:
--header.h--
int i;
--file1.c--
#include <header.h>
i = 10;
int main() {
func();
return 0;
}
--file2.c--
#include <header.h>
void func() {
printf("i = %d\n", i);
return;
}
I get the warning that i defaults to an int. What could I do if I want to have i as a float for instance?
Make it
extern int i;
in the header and
int i = 10;
in file1.c.
The warning means that for the (incomplete) declaration i = 10; in file1.c, the "implicit int" rule is applied, in particular, that line is interpreted as a declaration (since an assignment cannot appear outside function scope).
You have a couple of errors in your code. The first is that you define the variable i in the header file, which means that it will be defined in all source files that include the header. Instead you should declare the variable as extern:
extern int i;
The other problem is that you can't just assign to variables in the global scope in file1.c. Instead it's there that you should define the variable:
int i = 10;
Declare it as extern in the header (this means memory for it is reserved somewhere else):
/* header.h */
extern int i;
Then define it in only one .c file, i.e. actually reserve memory for it:
/* file1.c */
int i = <initial value>;
In the header use
extern int i;
in either file1.c or file2.c have
int i = 20;
If you want float just change int to float
In 99.9% of all cases it is bad program design to share non-constant, global variables between files. There are very few cases when you actually need to do this: they are so rare that I cannot come up with any valid cases. Declarations of hardware registers perhaps.
In most of the cases, you should either use (possibly inlined) setter/getter functions ("public"), static variables at file scope ("private"), or incomplete type implementations ("private") instead.
In those few rare cases when you need to share a variable between files, do like this:
// file.h
extern int my_var;
// file.c
#include "file.h"
int my_var = something;
// main.c
#include "file.h"
use(my_var);
Never put any form of variable definition in a h-file.

C extern struct pointer dynamic allocation

I'm trying to allocate dynamically a global struct in c, but something makes my c file not being able to find the reference to the extern variable.
The log:
main.c:18: undefined reference to `gate_array'
extern.h
#ifndef EXTERN_H_
#define EXTERN_H_
typedef struct gate_struct {
int out;
} gate;
extern gate *gate_array;
#endif /* EXTERN_H_ */
main.c:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "extern.h"
int main(int argc, char *argv[]){
gate_array = (gate*) malloc (2* sizeof(gate));
return 0;
}
Thanks!
There is no definition of gate_array due to extern. In this case, you can just remove the extern qualifier. However, if extern.h was used in multiple translation units (#include in several .c files) then this approach would result in multiple definition errors. Consider adding another .c file that would contain the definiton of gate_array (and any future variables), ensuring there is exactly one definition of gate_array.
The extern gate *gate_array tells the compiler that there is a variable called gate_array, but it is defined somewhere else. But there is no definition of gate_array in the posted code.
Also, you may wish to read
Do I cast the result of malloc?
This is probably what you meant to do:
#ifndef EXTERN_H_
#define EXTERN_H_
typedef struct gate_struct {
int out;
} gate;
typedef gate *gate_array;
#endif /* EXTERN_H_ */
This typedefs gate * to gate_array. Then in your main.c you want to do:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include "extern.h"
int main(int argc, char *argv[]){
gate_array name_of_array = malloc (2* sizeof(gate));
free(name_of_array);
return 0;
}
Previously you were missing a variable name. Furthermore, it is bad practice to cast the return of malloc.
Pointer to gate i.e. gate_array was typedef not declared so you were doing something like this:
typedef int *IXX;
IXX = (int*) malloc(2*sizeof(int));
Do something like this:
IXX ix = (int*) malloc(2*sizeof(int));

Resources