2

I'm working in C and this is the first time I've coded in C in quite a while. I'm having trouble finding out why I'm getting a multiple definition error on compile. Here's some example code, which is just enough code to reproduce the error.

file: main.c

#include <stdio.h>
#include "struct.h"

int main()
{
    parse();
}

file: struct.h

#ifndef STRUCT_H
#define STRUCT_H

#define plantot 4

void parse();

struct planettype
{
    char name[30]; // Name of planet

};

struct planettype planname[plantot] = // List of planet types
{   // Name
    {"None"},
    {"Gas Giant"},
    {"Earth-like"},
    {"Uninhabitable"}
};

#endif // STRUCT_H

file: parse.c

#include <stdio.h>
#include "struct.h"

void parse() {

    printf("I'm not parsing a thing!\n");

};

I try to compile that code and I get:

mingw32-gcc.exe -Wall -g  -c C:\Projects\TW-test\parse.c -o obj\Debug\parse.o
mingw32-g++.exe  -o bin\Debug\TW-test.exe obj\Debug\main.o obj\Debug\parse.o   
obj\Debug\parse.o:parse.c:(.data+0x0): multiple definition of `planname'
obj\Debug\main.o:main.c:(.data+0x0): first defined here
collect2.exe: error: ld returned 1 exit status

I have header guards in place which should prevent this exact thing from happening. Why is it happening anyway? I've spent hours trying to figure this out and can't. In my actual full project, I'm getting this on several structures in struct.h, but I figure if I can fix it for this one, it'll fix it for all of them. If it makes any difference, I'm using Code::Blocks to write and compile the code.

What am I missing? Why won't this code compile?

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • You are violating the one definition rule, because `planname` is not a pure declaration, but a definition, since it includes an initializer. So, each inclusion will cause another definition to be created. – jxh Aug 06 '21 at 16:40
  • 2
    Because you define variables in a header and include it in 2 compilation units. You should only keep declarations in your header and move definitions to the C files. – Gerhardh Aug 06 '21 at 16:41
  • @jxh this is not 100% correct. The reason for `planname` to be a definition is the fact that it is not `extern`. Even without the initialization it still would be a definition. – SergeyA Aug 06 '21 at 16:43
  • @SergeyA: True. C allows non-`extern` uninitialized declarations to be treated as definitions if there is no other definition present. – jxh Aug 06 '21 at 16:45

1 Answers1

4

In your project, both "main.c" and "parse.c" have #include "struct.h" in them, so both those source files end up containing this:

struct planettype planname[plantot] = // List of planet types
{   // Name
    {"None"},
    {"Gas Giant"},
    {"Earth-like"},
    {"Uninhabitable"}
};

Thus, when it comes to linking the component object modules into an executable, there are two definitions for the planname array of structures.

To resolve the issue, convert the definition of plannname (in the header file) to a simple declaration, like so:

extern struct planettype planname[plantot]; // No initializer = just a declaration!

Then, add the full definition (i.e. with the initializer list) in one (and only one) of your source files: either "main.c" or "parse.c".

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
  • I see. That explains why I remember always having problems along these lines when I used to code in C more regularly, as I've always done arrays that way. Things would always break if I tried to move code around too much. Thank you! – Steven Reeves Aug 07 '21 at 14:10