简体   繁体   中英

Why can't I have an inner structure when two files include each other?

For context, I'm writing an operating system:

I have a struct vt_device_s and a struct __vt_device_s which is architecture specific and lives inside of vt_device_s like so:

struct
vt_device_s
{
    struct __vt_device_s __device;
    size_t cursor_x;
    size_t cursor_y;
};

Now for the architectural struct:

struct
__vt_device_s
{
    uint16_t *memory;
    size_t memory_len;
};

The header <dev/vt.h> knows about __vt_device_s defined in <sys/_vt.h> because it is included, yet I get this error:

error: field '__device' has incomplete type
   48 |  struct __vt_device_s __device;
      |

I realise this is because both files rely on each other (the whole conflict is caused by _vt.c including _vt.h including vt.h including _vt.h ) but I don't understand how it is a compile problem. I have include guards in both files!

PS: I understand this would be a non-issue if I used a pointer but as it's an operating system, this driver needs to function before paging is set up (that is, malloc and free don't exist yet).

Here are the three files in question:

dev/vt.h

#ifndef _DEV_VT_H_
#define _DEV_VT_H_ 1

#include <stddef.h>

#include <sys/_vt.h>

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

struct
vt_device_s
{
    struct __vt_device_s __device;
    size_t cursor_x;
    size_t cursor_y;
};

void vt_init(struct vt_device_s *);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* _DEV_VT_H_ */

sys/_vt.h

#ifndef _I386__VT_H_
#define _I386__VT_H_ 1

#include <stddef.h>
#include <stdint.h>

#include <dev/vt.h>

#define __VT_WIDTH  80
#define __VT_HEIGHT 25
#define __VT_MEMOFF 0xb8000

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

struct
__vt_device_s
{
    uint16_t *memory;
    size_t memory_len;
};

void __vt_init(struct vt_device_s *);

#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* _I386__VT_H_ */

sys/_vt.c

#include <sys/_vt.h>

void
__vt_init(struct vt_device_s *device)
{
    device->__device.memory = (uint16_t *) __VT_MEMOFF;
    device->__device.memory_len = __VT_WIDTH * __VT_HEIGHT;
}

Your double include guards prevent the one file from including itself when it is re-included by the other. The only way to fix this is you have got to break the cycle. Decide which header is "higher" and will include the "lower" and don't try to include the higher one from the lower one. The lower one must be valid on its own.

The reason for this is that the pre-processor has to transform multiple files into one linear sequence of lines for the compiler. The compiler has to see one set of file contents before the other.

If you have cyclic includes like this you make it up to the final user of the code which one comes first. If they include file A, then it will include file B, which will try to include file A again but it will be prevented by the include guards, so the contents of B will be parsed first. On the other hand if the final user includes B first, then the contents of A will be seen first by the compiler.

So if you leave it like it is, then it is effectively random which file gets included first. If you break the cycle, you get to decide yourself which is included first.

Once you have decided, you can fix the compiler errors about incomplete types by making whichever file you choose to put first able to stand on its own and then letting the one that is second use definitions from the first.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM