Page 1 of 1

Cross compiler swears on implicit declaration

Posted: Wed Nov 27, 2019 2:16 pm
by mrjbom
Hello.

Source code and method of compilation posted here: https://github.com/mrjbom/OS

When trying to compile kmain.c, the compiler says that there is an implicit declaration.

Code: Select all

./source/kmain.c: In function ‘kmain’:
./source/kmain.c:7:5: warning: implicit declaration of function ‘initVGAMemory’ [-Wimplicit-function-declaration]
     initVGAMemory();
     ^
./source/kmain.c:8:5: warning: implicit declaration of function ‘clearVGAMemory’ [-Wimplicit-function-declaration]
     clearVGAMemory(0xFE01AC);
     ^
./source/kmain.c:9:5: warning: implicit declaration of function ‘writePixelToVGA’ [-Wimplicit-function-declaration]
     writePixelToVGA(VGA_HEIGHT - 1, VGA_WIDTH - 1, 0x00FFFFFF);
I tried to eliminate these warnings by adding to kmain.c

Code: Select all

#include "vgamemory.h"
However now I get this error:

Code: Select all

vgamem.o:(.bss+0x0): multiple definition of `VGAFRAMEBUFFER_ADDRESS'
kmain.o:(.bss+0x0): first defined here
The VGAFRAMEBUFFER_ADDRESS variable is declared in vgamemory.h

How can I solve this problem?

I hope I could explain everything. I apologize if something is unclear to you.

Re: Cross compiler swears on implicit declaration

Posted: Wed Nov 27, 2019 2:54 pm
by Schol-R-LEA
TL;DR: Move the variable definition

Code: Select all

uint32_t* VGAFRAMEBUFFER_ADDRESS = NULL;
into vgamemory.c and replace the line in vgamemory.h with

Code: Select all

extern uint32_t* VGAFRAMEBUFFER_ADDRESS;
The problem is in where you are defining the variable, as opposed to where you are declaring it; in this case, they are both on line 6 of vgamemory.h. This is a problem, since it means that, because of how the #include directive works, it means you actually defining and declaring it in both kmain.c and vgamemory.c, even though you intended it to be declared in only the header.

This is basically the same issue discussed in the wiki page Why function implementations shouldn't be put In header files, and the solution is similar: use an extern declaration in the header file (which corresponds to a funtion prototype for functions), and put the variable definition proper in one of the source files. In this case, vgamemory.c is probably the best choice for this.

It's the sort of thing which can drive even experienced C and C++ programmers to drink, as on the surface, it seems reasonable to put variable declarations in headers, until something like this crops up. It's all too easy to forget the need to make header declarations extern if you haven't had to add any in a while.

Re: Cross compiler swears on implicit declaration

Posted: Wed Nov 27, 2019 3:02 pm
by mrjbom
Schol-R-LEA wrote:TL;DR: Move the variable definition

Code: Select all

uint32_t* VGAFRAMEBUFFER_ADDRESS = NULL;
into vgamemory.c and replace the line in vgamemory.h with

Code: Select all

extern uint32_t* VGAFRAMEBUFFER_ADDRESS;
The problem is in where you are defining the variable...
When declaring function prototypes in .h should I do that too?

Re: Cross compiler swears on implicit declaration

Posted: Wed Nov 27, 2019 4:02 pm
by mallard
Schol-R-LEA wrote:It's the sort of thing which can drive even experienced C and C++ programmers to drink, as on the surface, it seems reasonable to put variable declarations in headers, until something like this crops up.
Although us C++ programmers get to "cheat" in this particular case and just declare the variable "inline" (a C++17 feature) and it works exactly as we'd like. :D
mrjbom wrote:When declaring function prototypes in .h should I do that too?
You can if you like, but it's already the default for function prototypes. The language can already tell that it's a declaration and not a definition by the lack of a function body.

Re: Cross compiler swears on implicit declaration

Posted: Wed Nov 27, 2019 5:12 pm
by Schol-R-LEA
mrjbom wrote:
Schol-R-LEA wrote:TL;DR: Move the variable definition

Code: Select all

uint32_t* VGAFRAMEBUFFER_ADDRESS = NULL;
into vgamemory.c and replace the line in vgamemory.h with

Code: Select all

extern uint32_t* VGAFRAMEBUFFER_ADDRESS;
When declaring function prototypes in .h should I do that too?
Not quite; the prototype syntax (the function signature ended with a semicolon, and without a function body) is automatically recognized as a declaration rather than a definition, so the extern keyword isn't necessary. While this is an inconsistency in the syntax, it is one which the ANSI C committee thought acceptable when they introduced prototypes of this form in 1988 (previously, prototypes only had the type and name of the function and the types of each argument, and they could even skip the arguments and type under some circumstances; indeed, IIRC, the very earliest C compilers didn't require prototypes at all for functions of one int argument and an int return type, though they were treated as mandatory by most compilers by around 1982, I think).

The only reason it becomes a problem is because it is easy to start seeing header files as their own thing, rather than as a chunk of raw text which gets inserted into the input stream by the pre-processor before the compiler even sees it. It is easy to forget about things like #include guards and extern declarations if you start forgetting that, as far as the lexical analyzer and parser are concerned, the source code and the headers #includeed into it are a single text stream.