1

I've been going through the Minix 1.1 source and noticed that a struct type is being referenced before it has been defined or declared. For example, main.c contains the following includes :

#include "file.h"
#include "fproc.h"
#include "glo.h"
#include "inode.h"
#include "param.h"
#include "super.h"

file.h contains the following :

EXTERN struct filp {
  mask_bits filp_mode;      /* RW bits, telling how file is opened */
  int filp_count;       /* how many file descriptors share this slot? */
  struct inode *filp_ino;   /* pointer to the inode */
  file_pos filp_pos;        /* file position */
} filp[NR_FILPS];

inode.h contains the following :

EXTERN struct inode {
  unshort i_mode;       /* file type, protection, etc. */
  uid i_uid;            /* user id of the file's owner */
  file_pos i_size;      /* current file size in bytes */
  real_time i_modtime;      /* when was file data last changed */
  gid i_gid;            /* group number */
  links i_nlinks;       /* how many links to this file */
  zone_nr i_zone[NR_ZONE_NUMS]; /* zone numbers for direct, ind, and dbl ind */

  /* The following items are not present on the disk. */
  dev_nr i_dev;         /* which device is the inode on */
  inode_nr i_num;       /* inode number on its (minor) device */
  short int i_count;        /* # times inode used; 0 means slot is free */
  char i_dirt;          /* CLEAN or DIRTY */
  char i_pipe;          /* set to I_PIPE if pipe */
  char i_mount;         /* this bit is set if file mounted on */
  char i_seek;          /* set on LSEEK, cleared on READ/WRITE */
} inode[NR_INODES];

Notice that struct filp contains a member filp_ino which is a pointer to type inode; however, type inode is not defined until later.

I was under the assumption that you either need to forward declare the type or define it before usage but obviously I'm wrong. Can someone point me in the right direction in understanding 1) why this is legal and 2) how the compiler is able to parse struct filp without advance knowledge of the inode type.

alex
  • 479,566
  • 201
  • 878
  • 984
Anthony
  • 604
  • 6
  • 18

1 Answers1

1

You can create pointers to incomplete types (such as structure or union types) without knowing the size of the type, so you can use:

struct anything *p;

before you define the internals of struct anything (where anything is spelled inode in your example code). You can't dereference an incomplete type, but you can create pointers to it. If you don't need to access the internals of the incomplete type, you can pass the pointers around without ever defining the internals. This can help you create opaque types in C.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • Oh wow... so is a pointer to these incomplete types the only exception to the rule or are there others? Also, I'm assuming that a pointer to an incomplete union or enum is equally valid correct? Would you happen to know where this behavior is defined in the standard? – Anthony Jun 04 '14 at 00:44
  • 1
    Structures and unions are OK. I tested a one line C file containing `enum xyz *pqr = 0;` and it compiled to object code with GCC 4.9.0 set to very fussy (somewhat to my surprise). _[...time passes...]_ However, when I made GCC even fussier, it reported: `xx.c:1:6: error: ISO C forbids forward references to ‘enum’ types [-Werror=pedantic]` (sometimes, it takes a lot of persuading to make GCC give up its extensions; it also makes it dangerous to test with). – Jonathan Leffler Jun 04 '14 at 00:49
  • I'll work out where the construct is allowed in the standard; it may take a while (and there's a journey home to worry about too). – Jonathan Leffler Jun 04 '14 at 00:50
  • To be clear: Doing `struct inode *p;` also counts as a forward declaration of `struct inode` - you don't need a separate declaration. This is actually annoying sometimes when you mis-spell a struct tag; it declares a type which is never defined, instead of immediately giving an error. – M.M Jun 04 '14 at 00:53
  • @Anthony an example of this in the standard library is `FILE *`. Your implementations' stdio.h et al. isn't required to actually define the `FILE` struct anywhere that you can see it. – M.M Jun 04 '14 at 00:54
  • Yes, that's interesting... I wonder why they allowed that. I'll search for it in the standard when I get home. – Anthony Jun 04 '14 at 01:28
  • Two related questions: [Which part of the C standard allows this code to compile?](http://stackoverflow.com/questions/12200096/which-part-of-the-c-standard-allows-this-code-to-compile) and [Does the C standard consider that there are one or two `struct uperms_entry` types in this header?](http://stackoverflow.com/questions/11697705/does-the-c-standard-consider-that-there-are-one-or-two-struct-uperms-entry-typ), especially the second which has quotes from the standard aplenty covering this general topic. – Jonathan Leffler Jun 04 '14 at 01:33
  • Note that in general the `FILE` type is complete (though it is not _required_ to be complete) so that the macro implementations of functions such as `getchar()` and `getc()` or `putchar()` and `putc()` can make use of the internals of the `FILE` type. (Further, the macro implementations of the `getc()` and `putc()` functions are allowed to reference the `FILE *` argument more than once — a permission not normally granted to macro implementations of standard library functions. – Jonathan Leffler Jun 04 '14 at 01:38