2

I am attempting to define a set of global variables which will configure my device, about 10 in a dedicated .c file, that will be changed on a regular basis at compile time (as per device requirements), I want these to be in this separate file so they can all be changed easily. These variables are then called throughout my program in various files. And are never changed only read. The problem is that my compiler (XC8 for PIC MCU's) doesn't define the variable, as it can only see that one use of the variable in the file, even though it is called with extern throughout the program.

config.h

unsigned int Global_A;
unsigned int Global_B;
void config(void);

config.c

void config(void)
{
   unsigned int Global_A=987;
   unsigned int Global_B=123;
}

prog_a.h

extern unsigned int Global_A;
extern unsigned int Global_B;
unsigned int var_A;
void prog_a(void);

prog_a.c

unsigned int var_A=0;
void prog_a(void);
{
   var_A=Global_A+Global_B;
}

main.c

#include config.h
#include prog_a.h

void main(void)
{
   while(1)
   {
      config();
      prog_a();
   }
}

as a result, the equivalent var_A is always 0, as the compiler has done away with config.c as it cannot see the variable called again.

I'm assuming the answer is very obvious to those more versed, but I can't find anything online. I have not had any trouble with using extern and globals before when they are used in the file the are defined in. But I could just be using it wrong fundamentally, so feel free to berate me.

Thanks in advance.

p.s if it wasn't obvious this is an example code to illustrate my problem.

  • 4
    I do not really understand the question, but I can tell that *defining* variables in `h` files is not a good idea in general. You can declare them with `extern` in the headers though. – Eugene Sh. Dec 01 '20 at 21:17
  • Do you actually build with the `config.c` source file, and link to the object file created from it? How *do* you build your program? – Some programmer dude Dec 01 '20 at 21:19
  • MPLABX automatically builds and generates the linker – Josh Gudgin Dec 01 '20 at 21:21

3 Answers3

4

Your function config declares two new variables in the scope of the function (their names hide those of the global variables). They don't exist anywhere outside of it, and assigning a value to them does nothing. If your goal was for it to initialize the globals, you need do this:

// config.h
extern unsigned int Global_A;
extern unsigned int Global_B;
void config(void);

// config.c
unsigned int Global_A;
unsigned int Global_B;
void config(void) {
   Global_A=987;
   Global_B=123;
}
Enno
  • 1,736
  • 17
  • 32
  • Sure. I was assuming that this is a stripped down example, and that the values are actually passed into config as arguments, or calculated. Or that there's some code before the call to config() that relies on them not being initialized yet. – Enno Dec 01 '20 at 21:42
2

With extern keyword it it necessary to declare the variable once (preferably in a header file) define it once in a .c file that has visibility to the declaration statement. That is it. Where the extern defined variable is necessary, #include the header file in which the declaration statement occurred.

Also note it is important also to define extern variable(s) in global scope (i.e. not in a function).

config.h:

#include "prog_a.h"
//unsigned int Global_A;
//unsigned int Global_B;
void config(void);

main.c

#include config.h
#include prog_a.h
...
//suggest defining these here:
unsigned int Global_A=0;//define outside of function
unsigned int Global_B=0;
...

config.c

#include "prog_a.h"
...

void config(void)
{
   Global_A=987;
   Global_B=123;
}
ryyker
  • 22,849
  • 3
  • 43
  • 87
  • Okay yes, that was the problem, I just removed the declaration in "config.c" so it was declared in config.h as is, and purely defined in config.h, by just removing the "unsigned int", what curious is it did not flag a multiple definition error at all, compiled without warnings. – Josh Gudgin Dec 01 '20 at 21:31
  • @Josh - compiler warnings turned on? – ryyker Dec 01 '20 at 21:32
  • The definitions in `config.c` will not cause multiple-definition warnings or errors because they define new identifiers in block scope. – Eric Postpischil Dec 01 '20 at 21:35
  • Compiler is XC8 for PIC, compiler warnings are turned on. – Josh Gudgin Dec 01 '20 at 21:38
  • @EricPostpischil - removed the erroneous statement. Thanks – ryyker Dec 01 '20 at 21:41
  • 2
    The definitions in `config.h`, which appear in multiple translation units since `config.h` is included in multiple source files, are actually *tentative definitions*, which the C standard allows C implementations to resolve as they choose. Many Unix-affiliated compilers and linkers, including GCC by default until recent versions, merged such tentative definitions into a single definition. So I would not expect an error message from such tools. – Eric Postpischil Dec 01 '20 at 21:43
0

Your global variables are all of type int, so why not just use #define?

Change file config.h to

#define Global_A 789    
#define Global_B 123

You do not actually have to declare any int variables to hold those values, plus, they will be const and unmodifiable.


That's they way that we did it "back in the day", but in the last decade or two, I see more and more actually storing these configuration values in an external text file

It can be a .INI, or XML or JSON, etc, etc, that is up to you.

You just create different files, let's say Singapore.ini and Paris.ini and Auckland.ini, etc, each containing a key/value pair.

e.g

time_zone = X
population = Y

etc, etc

Then, at the start of your main, read the file in and store the values -but not in globals, which are frowned upon, these days. Read them into variables which are local to config.c, and have config.c/h provode methods to read their values, e.g GetTimeZone() and GetPoulation(), etc

Don't worry about any code size or run time impact of this, as any decent compiler will in-line these function calls.

One advantage to reading "global" configuration values in an external text file is that you only need to build your software once. You do not need to rebuild and have an executable for each configuration, which the road that you are currently heading down.

Firstly, this makes it easier to test your software (especially automated test), by merely editing the text file, or supplying a new one.

Also, since you only have a single executable, you can ship that to all of your users/customers, and give each a tailored config file. You can totally control & change the functionality of your software just by changing the config file. You might want to think about that.

Mawg says reinstate Monica
  • 38,334
  • 103
  • 306
  • 551