0

I am having problem of multiple declaration during compilation of multiple dependent c files.

I had been including the source files in main.c and compile it. It worked fine. Lately I was advised by my colleague not to do in that way. So I tried to change my code and compile it making different .o files and finally linking them. But it did not work.

Basically what I am trying to do is similar to this:

essentials.h

#ifndef ESSENTIALS
#define ESSENTIALS
//C headers
#include <stdio.h>
#include <conio.h>

//My Assets
#include <new.h>
#include <global.h>
#endif

This header includes all the basic files any source file may need. Also it includes header file where the global variables are declared.


The other header and source files are such as:

global.h

#ifndef GLOBAL
#define GLOBAL

int glob_int=0;

#endif

new.h

#ifndef NEW
#define NEW

int thisFunc();

#endif

new.c

#include <essentials.h>

int thisFunc(){
    glob_int++;
    printf("This is new Function %d\n",glob_int);
    return 0;
}

main.c

#include <essentials.h>

/*  include other headers*/

int main(){
    printf("Main Function Start %d\n",glob_int);

    thisFunc();
    return 0;
}

I then proceed to compile the following,

F:\Codes\prog> gcc -c ./src/*.c -I"F:\Codes\prog\include"

<<<< Compiles separate obj files >>>>

F:\Codes\prog> gcc *.o -o ./bin/main.exe
c:/my_assets/mingw64/bin/../lib/gcc/x86_64-w64-mingw32/12.2.0/../../../../x86_64-w64-
mingw32/bin/ld.exe: 
new.o:new.c:(.bss+0x0): multiple definition of `glob_int'; main.o:main.c:(.bss+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

I have to use glob_int in both the source files. What am I missing about the linking phase?And are there any suitable ways to compile this code.

I have searched various such examples but nothing has worked for my case. The thing is without the glob_int variable, the program compiles and runs too without showing any multiple declaration error for the thisFunc() function. But it does not compile with the variable.

I also have tried putting <global.h> in each .c files instead of in essentials header file. But that too didn't work.

Please avoid giving me hints for using extern. That will be the last thing I would use.

ALSO: Please I would like to know why including source files in main.c with guard headers and compiling it is usually condemned by many C programmers. If you instead can describe about this please help by answering.

Thanks Sammy1410

Sammy1410
  • 47
  • 5
  • Does this answer your question? [Why aren't my compile guards preventing multiple definition inclusions?](https://stackoverflow.com/questions/249701/why-arent-my-compile-guards-preventing-multiple-definition-inclusions) – tinman Oct 09 '22 at 11:52
  • 1
    *This header includes all the basic files any source file may need. Also it includes header file where the global variables are declared.* Both of these things are bad practice. Why not start doing it properly instead? – super Oct 09 '22 at 11:54
  • Why don't you want to use `extern`? – klutt Oct 09 '22 at 12:55
  • @klutt I went to great lengths in my projects by including source files in main.c and compiling it. So the project has been somewhat big and using extern in such mess would just consume my time and also will make it difficult to read. Right now there is just one declaration for each and guard headers so it was easy. Need similar approach instead of having to extern all the variables present there. – Sammy1410 Oct 09 '22 at 13:18
  • Just trying to change the way I code to what many say is "the way" to do it. Does including source files in one main.c file that bad? – Sammy1410 Oct 09 '22 at 13:20
  • `glob_int` symbol present in both `main.o` and `new.o`. How linker can know which of them to use? – dimich Oct 09 '22 at 13:53
  • @Sammy1410 If you don't care about not using globals, and you want to avoid `extern` which is what is used to deal with globals, then why not simply ignore the advice of not including source files? TBH, the code base you're working on seem to be a mess anyway. – klutt Oct 09 '22 at 15:54
  • @klutt That's not what I am specifying. I said I need global variables and they have to be accessed by all src files. And also not use that include all source files in main.c approach. So if I ignore source files I am asking how must I include all the global variables such that linker distinguishes between them (and not throw any multiple definition error). Also since most of code has been written with compile guards, I need a way other than extern so that they compile to one executable file. ALSO, it is not messy and is quite readable since every variable is declared(written in code) once. – Sammy1410 Oct 09 '22 at 16:09

1 Answers1

1

Non-extern solution - leave off the initializer in the declaration for glob_int.

The presence of the initializer tells the compiler that this is a definition of glob_int, and you can only have one of those in the entire program. Leaving the initializer off creates a tentative definition, and the linker can sort it out.

Since it's declared at file scope, glob_int will be implicitly initialized to zero, so the initializer isn't necessary.

Of course, the right way to do this is to not use a global at all; instead, only define it in one source file and provide getter and setter functions for it:

Header:

/**
 * glob.h
 */
#ifndef GLOB_H
#define GLOB_H

void glob_reset( void ) // resets glob_int to 0
int glob_val( void )    // returns current value of glob_int
int glob_inc( void )    // increments glob_int and returns its new value

#endif

Source file:

/**
 * glob.c
 */
#include "glob.h"

static int glob_int; // glob_int is only visible to this source file

void glob_reset( void )
{
  glob_int = 0;
}

int glob_val( void )
{
  return glob_int;
}

int glob_inc( void )
{
  return ++glob_int;
}

Also, your essentials.h is a bad idea - source files should only include the headers they actually need, not a whole smorgasbord of unnecessary or potentially redundant headers. It creates maintenance headaches and can potentially lead to circular dependencies.

EDIT

My getter/setter example above was tossed off on little to no sleep; it's the right thing to do for a singleton object, but not for what you're describing.

From the comments:

so that I don't have to rewrite all the library headers again and again in each .c file.

This isn't anywhere near the burden you think it is, and I guarantee you for a large enough project it will cause problems. I've been in this movie, several times. It ends the same way every time. In the end it just creates more work because you have to disentangle dependencies.

Only include the files you know you need to include for each .h and .c file.

These global variables include keyboard key states, mouse position, various buffers which I must have as global. Is there any different non-extern way?

Don't use globals.

Seriously, don't use globals - maintain that state in main (or wherever your main event loop resides) and communicate that information via function arguments and return values:

void eventLoop( void )
{
  struct pos { int x, y; } curPos, lastPos; 
  buffer_t myBuffers[NUM_BUFFERS];
  ...

  while ( !done )
  {
    curPos = getReadPosition();
    readFrom( curPos, myBuffers[RD_BUF] );
    ...
  }

Making everything global tightly couples your code to itself, which makes it hard to test pieces in isolation and hard to upgrade or maintain.

Again, I've been in this movie. I've worked on code that made everything global, and it was impossible to fix. I worked on one system that was failing to hit its performance requirements, and because everything was global (and because it used goto for all control structures) we could not make any change without breaking something. It was fantastically brittle.

Functions should only communicate via parameters, return values, and exceptions. They should not share state via global variables. C doesn't support structured exception handling, but you can fake it using signal and raise or setjmp and longjmp.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • Well I am not suggesting essentials.h to have unnecessary headers just headers like math.h stdio.h, etc. so that I don't have to rewrite all the library headers again and again in each .c file. Also this approach seems ok when there is just one or two variables. Now think how many such functions must be created to access (which in my case is about 53) different global variables by each source files. These global variables include keyboard key states, mouse position, various buffers which I must have as global. Is there any different non-extern way? – Sammy1410 Oct 09 '22 at 13:40
  • Thank you @John Bode for your help. I took your advice and worked on changing the code. Took me a whole day to change my code. But still, I am getting multiple definitions errors when linking. Is it compulsory to use extern?? Using extern for each variable I supposed global has compiled. Its messy but is working. – Sammy1410 Oct 10 '22 at 17:32
  • But my main problem is this. I am trying to learn to code microprocessors. I started with beginner-friendly Arduino. It was all fun playing with functions. Lately, I am working on microprocessors having limited stack memory. That is why my first choice for each function accessing important data(as I mentioned --- not keyboard and mouse but others) was to keep it updated in one location so that any function may come and peek at it (fake read-only memory). ROM is also smaller so needs smaller code. Can you suggest some other ways? I am trying to code using MinGW pushing to the limit. – Sammy1410 Oct 10 '22 at 17:36