Page 1 of 1
#Include problems in C
Posted: Fri Mar 14, 2003 10:24 am
by Scalpel
When I started writing my OS I put all my prototypes as well as my entire functions in my .h files. Now I've begun the work of extracting all the functions to separate files (printf.c, clear_screen.c, freeze.c and so on), but have run into some problems.
I get errors when one .c file wants to use the functions from another .c file.
I'll show you what I mean with this example.
My freeze(); function is a simple routine that writes "Kernel Frozen" to the screen, and runs a for-loop forever.
When I extracted the function to freeze.c I got errors about "implicit declaration of function printf". So I tried to #include <io.h> in the freeze.file, but then I got "multiple declaration"-errors.
Can anyone explain the proper way of using #include, or point me in the direction of a good web-page?
Re:#Include problems in C
Posted: Fri Mar 14, 2003 10:35 am
by Pype.Clicker
Kernigan & Ritchie ask for the developer to split their work into .C files (implementation) and .h files (module interface: structures description, function prototypes, etc.)
The usual structure is
DISPLAY.H
Code: Select all
void printchar(char);
void clearscreen();
KEYBOARD.H
CONSOLE.C
Code: Select all
#include "keyboard.h"
#include "display.h"
void readline(char *buffer) {
char last=0;
while (last!='\n') {
waitKey();
last=readKey();
printchar(last);
*buffer++=last;
}
return
}
display.c will contain the implementation of the display module. Each module consider other modules as a blackbox providing the interface described by the .h file. It shouldn't call functions that aren't in .h, it shouldn't access variables that aren't
externed by the .h file, etc.
Now, when your code grow, it may happen that you have #include statements in your .h file as well, for instance because module X needs structures declared by module Y to declare its own structs.
To avoid multiple definition, you can then use the following technique:
DISPLAY.H
Code: Select all
#ifndef __DISPLAY_H
#define __DISPLAY_H
#include <global.h>
// other includes
// local declarations
#endif
the same way, global.h will have a #ifndef __GLOBAL_H, etc.
if your C file is now carrying something like
Code: Select all
#include <global.h>
#include <display.h>
the #include global.h within display.h will be dropped silently
Re:#Include problems in C
Posted: Fri Mar 14, 2003 1:34 pm
by _mark
A very good explaination above. In simple terms, every .H file should have multiple-include-protection.
I always use the following stratagy:
#ifndef __filename_H__
#define __filename_H__
// all code goes here.
#endif
So my stdio.h file would look something like:
#ifndef __STDIO_H__
#define __STDIO_H__
void printf(...);
void gets(...);
#endif
Note the double underscores in front and behind the H filename. This pretty much garentees I do not try to use this definition for anything else.
Also Note that the #define XXXX does not have to be defined as anything such as
#define MY_DEF some_other_thing
The fact that the symbol was defined to be nothing is sufficant for the compile to ignore the header file if it had already been included once.
_mark()
Re:#Include problems in C
Posted: Sat Mar 15, 2003 3:08 am
by Scalpel
Ok, thanks for your good replies. I'll try or code, but I'm sure it works
Btw. sub-question:
What is the difference between using <io.h> and "io.h" ?
Re:#Include problems in C
Posted: Sat Mar 15, 2003 4:07 am
by Pype.Clicker
"io.h" is looked up in the current directory, while <io.h> is looked in the Include path (set with -I )
maybe "io.h" falls back to the include path if it doesn't find the header file in the current directory, i never tried it ...
Re:#Include problems in C
Posted: Sat Mar 15, 2003 6:00 am
by Whatever5k
maybe "io.h" falls back to the include path if it doesn't find the header file in the current directory
Yep, that's it. If the compiler cannot find the specified file in the current directory, it continues searching in the include path.
Re:#Include problems in C
Posted: Sun Mar 16, 2003 4:03 pm
by Scalpel
Ok, I've tried to use the Inclusion guards in my .h files, but I still seem to have a problem with the linking.
Here is the short version of my kernel.c:
Code: Select all
//kernel.c
#include <io.h>
#include <cpu.h>
clear_screen();
freeze();
Code: Select all
//io.h
#ifndef __IO_H
#define __IO_H
#include <strings.h>
int global_cursor=0;
//Prototypes
void clear_screen(int); //function exported to own file (./sources/libc/clear_screen.c)
int set_cursor (int, int); //function exported to own file (./sources/libc/set_cursor.c)
void iprint(int, char);
void sprintf(char*, int, int);
//Functions
(..cut away functions iprint and sprintf..)
#endif
Code: Select all
#ifndef __CPU_H //Inclusion guard
#define __CPU_H //
#ifndef __IO_H
#include <io.h>
#endif
//Prototypes
void freeze();
#endif
Code: Select all
//freeze.c
#include <cpu.h>
#include <text_colors.h>
void freeze()
{
sprintf ("****************", TEXT_BLACK + BACKGROUND_RED , set_cursor(1, 62));
sprintf ("*Kernel frozen!*", TEXT_BLACK + BACKGROUND_RED , set_cursor(2, 62));
sprintf ("****************", TEXT_BLACK + BACKGROUND_RED , set_cursor(3, 62));
for (;;);
};
Hope thats not too much code for the forum :-[
When I compile and link this I get the error:
Code: Select all
Cerberus:~/projects/OS/HelmsDeep# make
nasm -f aout -o sources/kernel/asmkernel.o sources/kernel/asmkernel.asm
g++ -c -ffreestanding -fasm -O2 -I./includes -o sources/kernel/kernel.o sources/kernel/kernel.c
g++ -c -ffreestanding -fasm -O2 -I./includes -o sources/libc/clear_screen.o sources/libc/clear_screen.c
g++ -c -ffreestanding -fasm -O2 -I./includes -o sources/libc/set_cursor.o sources/libc/set_cursor.c
g++ -c -ffreestanding -fasm -O2 -I./includes -o sources/libc/strcmp.o sources/libc/strcmp.c
g++ -c -ffreestanding -fasm -O2 -I./includes -o sources/libc/freeze.o sources/libc/freeze.c
ld -T ./linking/kernel.lnk -o kernel.bin ./sources/kernel/asmkernel.o ./sources/kernel/kernel.o ./sources/libc/clear_screen.o
./sources/libc/set_cursor.o ./sources/libc/strcmp.o ./sources/libc/freeze.o
./sources/libc/freeze.o: In function `convert_int_to_string(int, char *, char)':
./sources/libc/freeze.o(.text+0x0): multiple definition of `convert_int_to_string(int, char *, char)'
./sources/kernel/kernel.o(.text+0x0): first defined here
./sources/libc/freeze.o: In function `strlen(char *)':
./sources/libc/freeze.o(.text+0x8c): multiple definition of `strlen(char *)'
./sources/kernel/kernel.o(.text+0x8c): first defined here
./sources/libc/freeze.o(.data+0x0): multiple definition of `global_cursor'
./sources/kernel/kernel.o(.data+0x0): first defined here
./sources/libc/freeze.o: In function `iprint(int, char)':
./sources/libc/freeze.o(.text+0xac): multiple definition of `iprint(int, char)'
./sources/kernel/kernel.o(.text+0xac): first defined here
./sources/libc/freeze.o: In function `sprintf(char *, int, int)':
./sources/libc/freeze.o(.text+0x14c): multiple definition of `sprintf(char *, int, int)'
./sources/kernel/kernel.o(.text+0x14c): first defined here
make: *** [kernel.bin] Error 1
Does anyone see what the problem is?
Re:#Include problems in C
Posted: Mon Mar 17, 2003 2:13 am
by Pype.Clicker
did you by any chance put the code of some function (like strlen, for instance) in a .h file ? if you did, every .c that include the .h is going to have its own copy of the function and when you attempt to link things together, you have the same function (say, strlen) present in several modules, so the linker can't tell which one is the good one.
Once again, the good practice is:
* no function body in a .h file (except if you explicitly declared it static inline)
* no #include <file.c> from a file.h
* no #include <file.c> from a file2.c except if you can make sure file2.c is the very only one file to include <file.c>
Re:#Include problems in C
Posted: Mon Mar 17, 2003 3:02 am
by Scalpel
Yes, you are right. There are a few functions in my .h files. I am exporting one function after another to their own .c file now. These errors came after I moved freeze() to it's own freeze.c file.
But perhaps I should try to move all the remaining functions out of the .h-files, and then see if it works.
I thought it wouldn't be a problem because the remaining functions are placed inside the inclusion guards like this:
Code: Select all
io.h
#ifndef __IO_H
#define __IO_H
#include <strings.h>
int global_cursor=0;
//Prototypes
void clear_screen(int); //function exported to own file (./sources/libc/clear_screen.c)
int set_cursor (int, int); //function exported to own file (./sources/libc/set_cursor.c)
void iprint(int, char);
void sprintf(char*, int, int);
//Functions
(Functions placed HERE)
#endif
Re:#Include problems in C
Posted: Mon Mar 17, 2003 4:18 am
by Scalpel
Ok, that seemed to work.
It now seems that the only errors I get is from a multiple declared 'int global_cursor' variable.
The 'global_cursor' is declared in my io.h, and is used by most of the functions declared in io.h. I've tried to use 'extern int global_cursor' in the .c files, but it doesn't seem to help.
How do I then set the global variable?
Re:#Include problems in C
Posted: Mon Mar 17, 2003 6:27 am
by Pype.Clicker
you must replace the "int global_cursor=0" from file.h into "extern int global_cursor;" and have only one C file declaring "int global_cursor=0".
BTW, i suggest you read more about C programming language before you continue OS programming -- recommended reading : "C Language, ANSI from Brian W. Kernighan and Denis M. Ritchie" (no ISBN because i fear my french translated copy will not have the proper ISBN
)
Re:#Include problems in C
Posted: Mon Mar 17, 2003 6:35 am
by Scalpel
Thanks
I've allready ordered my copy of Kernighans and Ritchies "The C Programming Language", and looking forward to reading it ;D