I have the following files:
There are 3 global variables that I would need to use: freq, amp and waveforms.
Should I declare those variables in every .h files by using extern and define the variable in one of the .c file OR create a global_variable.h, declare those variables in the global_variable.h and include this global_variable.h in every .c files.
Here is the minimal reproducible example:
change_freq.c
#include "change_freq.h"
void increment_freq(double *frequency)
{
(*frequency)++;
if (*frequency == 1)
{
amp++;
}
}
change_freq.h
#ifndef CHANGE_FREQ_H
#define CHANGE_FREQ_H
#include "global_variables.h"
void increment_freq(double *frequency);
#endif
global_variables.h
#ifndef GLOBAL_VARIABLES_H
#define GLOBAL_VARIABLES_H
double freq;
double amp;
#endif
main.c
#include "global_variables.h"
#include "change_freq.h"
int main()
{
freq = 0;
increment_freq(&freq);
printf("Hello World!\n");
printf("freq %lf\n", freq);
printf("amp %lf\n", amp);
return 0;
}
Here's a script to reproduce your project setup (run in an empty directory):
#!/bin/sh -eu
cat > global_variables.h <<EOF
#ifndef GLOBAL_VARIABLES_H
#define GLOBAL_VARIABLES_H
double freq;
double amp;
#endif
EOF
cat > change_freq.h <<EOF
#ifndef CHANGE_FREQ_H
#define CHANGE_FREQ_H
#include "global_variables.h"
void increment_freq(double *frequency);
#endif
EOF
cat > change_freq.c <<EOF
#include "change_freq.h"
void increment_freq(double *frequency)
{
(*frequency)++;
if (*frequency == 1)
{
amp++;
}
}
EOF
cat > main.c <<EOF
#include "global_variables.h"
#include "change_freq.h"
#include <stdio.h> ///ADD!
int main()
{
freq = 0;
increment_freq(&freq);
printf("Hello World!\n");
printf("freq %lf\n", freq);
printf("amp %lf\n", amp);
return 0;
}
EOF
With it being like this it should compile and run (cc *.c && ./a.out
) if and only if your compiler and linker support and implicitly allow so called common variables, which allow uninitialized global variables (double freq; double amp;
) to be tentatively defined in multiple translation units with the linker then merging them. For gcc/clang you can explicitly request this nonstandard feature with -fcommon
.
(So gcc -fcommon *.c && ./a.out
should work).
A portable solution would be to do
extern double freq, amp;
in the header, and
/*no-extern*/ double freq, amp;
in one of the C files.
(You see, double freq;
double amp;
at global scope without both extern
and initializers are both declarations and tentative definitions. Tentative definitions mean that you can redefine them with initializers once, after which they're finished (and non-tentative redefining attempts become errors), and/or tentatively redefine them. If no initializer is ever given, they're zero-initialized by default and finished by the end of the compilation unit. The (fairly common) -fcommon
extension (also needs support by the linker) allows you to extend this notion of tentative globals to multiple translation units by postponing the finishing of tentative globals from the end of the compilation unit to link time. It is a legacy feature from B, if I remember correctly. It relies on tentative globals being classed as a special type of linker symbol called a common symbol, which works the same as the standard C tentative definition except across all files being linked rather than just a single translation unit.)