1

I've encountered a problem that I cannot seem to resolve in which a global variable is claimed to be redeclared on the only line where it is declared. I have the following code:

test_regs.h:

#define    TEST_REGS_BASE_ADDRESS       0xA0080000

typedef struct {
    union {
        unsigned int data;
        struct {
            unsigned int RESERVED     : 16;
            unsigned int CHAR1        : 8;
            unsigned int CHAR0        : 8;
        };
    };
} TEST_REG_STRUCT;

typedef struct {
    TEST_REG_STRUCT                     TEST_REG;
} *TEST_REGS;

tasks.h:

#ifndef TASKS_H
#include "test_regs.h"

volatile TEST_REGS              TST; // This line throws an error
volatile int                    ok_global;

void func();

#define TASKS_H
#endif

tasks.c:

#include "tasks.h"

void func() {
    TST->TEST_REG.CHAR1 = 0x52;
    TST->TEST_REG.CHAR0 = 0x51;
    ok_global++;
}

main.c:

#include "tasks.h"

main() {
    TST = (TEST_REGS) TEST_REGS_BASE_ADDRESS;
    ok_global = 0;
    func();
}

I attempt to compile the above code with the following command (using a minimal version of GCC developed for the Leon3 processor):

sparc-elf-gcc -msoft-float -c -g -O2 -o test.o tasks.c main.c

That compile attempt produces the following errors:

tasks.h:4: error: conflicting types for 'TST'

tasks.h:4: error: previous declaration of 'TST' was here

Notably, the global variable, ok_global does not pose any problem; only the variable, the type having been declared in test_regs.h, TST produces the above error. This means that the error cannot be due to the header tasks.h somehow getting declared multiple times. Does anyone have any idea why my code as written is apparently illegal?

I'd note that, if I get rid of all headers, except for test_regs.h, and make the declaration in one unified C file, the problem goes away. Also, I really must have the test_regs.h header separated from the tasks.h header, test_regs.h is machine-generated while tasks.h is not, and will change depending on the usage.


Okay, since this apparently isn't sinking in for moderator-minded folks, this is not a duplicate question. I can structure my code in order to meet the suggestions in the existing post as follows (even sucking in the header, test_regs.h):

tasks.h:

#ifndef TASKS_H
#define TASKS_H
#define    TEST_REGS_BASE_ADDRESS       0xA0080000

typedef struct {
    union {
        unsigned int data;
        struct {
            unsigned int RESERVED     : 16;
            unsigned int CHAR1        : 8;
            unsigned int CHAR0        : 8;
        };
    };
} TEST_REG_STRUCT;

typedef struct {
    TEST_REG_STRUCT                     TEST_REG;
} *TEST_REGS;

extern volatile TEST_REGS              TST;
volatile int                    ok_global;

void func();

#endif

tasks.c:

#include "tasks.h"

volatile TEST_REGS TST;

void func() {
    TST->TEST_REG.CHAR1 = 0x52;
    TST->TEST_REG.CHAR0 = 0x51;
    ok_global++;
}

main.c:

#include "tasks.h"

main() {
    TST = (TEST_REGS) TEST_REGS_BASE_ADDRESS;
    ok_global = 0;
    func();
}

Compile command:

sparc-elf-gcc -msoft-float -c -g -O2 -o test.o tasks.c main.c

Result:

tasks.h:20: error: conflicting types for 'TST'

tasks.c:3: error: previous declaration of 'TST' was here

There is something specific to TST that is breaking global sharing; this is not just a general "how do I share global variables" question.

user3570982
  • 559
  • 1
  • 6
  • 14
  • Move your `#define TASKS_H` to the top of the file, right after `#ifndef TASKS_H`. – e0k Dec 28 '16 at 02:08
  • 1
    Do not `typedef` pointers, that's a terrible idea. Except perhaps if they are 100% opaque types. – Iharob Al Asimi Dec 28 '16 at 02:12
  • 1
    e0k, thanks for the suggestion, but that didn't address the issue. Additionally, I don't think that the `test_regs.h` can be sourced multiple times as is, as your suggestion would seem to imply, since the same redeclaration error does not occur for `ok_global`. – user3570982 Dec 28 '16 at 03:57
  • 1
    iharob, thank you for the style advice, but that's not relevant to the issue posted. Also, these are in fact opaque types. – user3570982 Dec 28 '16 at 03:59
  • 1
    Thanks Charlotte! That appears to have been the issue. I didn't even think to look at that. Thanks again for taking the time to understand what was at issue, and what wasn't. – user3570982 Dec 28 '16 at 16:10

1 Answers1

3

The variables TST and ok_global are defined in tasks.h. Since both main.c and tasks.c both include this header, those variables are defined in both modules. When those modules are then linked together, you get an error for multiple definitions.

Global variables should be defined in exactly one .c file. Any .c file that needs to reference it should include a header file that has a declaration of this global. A declaration says "this variable exists somewhere" but doesn't say exactly where.

In tasks.h, you declare the variables like this:

extern volatile TEST_REGS              TST;
extern volatile int                    ok_global;
void func();

Then you define them in tasks.c:

#include "tasks.h"

volatile TEST_REGS              TST;
volatile int                    ok_global;

void func() {
    TST->TEST_REG.CHAR1 = 0x52;
    TST->TEST_REG.CHAR0 = 0x51;
    ok_global++;
}

Notice that you're already doing this with the func function. The difference is that variable declarations require the extern keyword, while function declarations do not.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • But you *can* declare the functions `extern`, too, if you want that consistency. It just happens to be redundant to do so for them. – John Bollinger Dec 28 '16 at 02:19
  • Respectfully, that's not what's going on. That ignores the macro defines wrapped around the `tasks.h` header file to preclude multiple declarations, and it ignores the fact that `ok_global` compiles without error. This problem is specific to `TST`. Additionally, when I declare `TST` in `tasks.h` with an `extern`, and then declare it without the `extern` in `tasks.c`, I still get the "previous declaration" error; it just indicates that the previous declaration was in `tasks.c` instead of `tasks.h`. – user3570982 Dec 28 '16 at 03:48
  • @user3570982 The macro guards defend against a given header being included in more than once in a given source module, but it does **not** defend against multiple source modules including that header, and that header contains a definition like you have. As for `ok_global`, it's possible that the linker may only be catching the first instance of a duplicate variable. Give the above changes a try and see how it works. – dbush Dec 28 '16 at 04:11
  • 1
    Thanks for the explanation. I tried your suggestion, but encountered essentially the same errors: `tasks.h:4: error: conflicting types for 'TST' tasks.c:3: error: previous declaration of 'TST' was here` – user3570982 Dec 28 '16 at 04:18