-2

Basically the compiler complains of unknown type name 'Song_t' since the datatype definition happens after it is first referenced.

struct Cell { Song_t song;
            struct Cell *pnext;
};

typedef struct Song Song_t;

If I place the typedef before, it works. Is it therefore generally advised to place typedefs at the very beginning of each file?

  • 1
    How can the compiler know what is Somg_t in the structure definition?! – Vlad from Moscow Oct 07 '20 at 21:31
  • Read [*Modern C*](https://modernc.gforge.inria.fr/) and see [this C reference](https://en.cppreference.com/w/c) – Basile Starynkevitch Oct 07 '20 at 21:31
  • Provide some [mre] in your next question. Read also the documentation of your C compiler (e.g. [GCC](http://gcc.gnu.org/), and study for inspiration the source code of *existing* C software on [github.com](http://github.com/) etc... – Basile Starynkevitch Oct 07 '20 at 21:35
  • @BasileStarynkevitch: This is not a debugging question and has no need of a minimal reproducible example. The question is sufficiently posed. – Eric Postpischil Oct 07 '20 at 21:35
  • 2
    @VladfromMoscow: A compiler **could** examine the entire source file and, if possible, resolve types given that complete information of the file. It does not because C is largely designed to be compiled in one pass. But that is key information that a student may not yet know, so the rhetorical question you posed is an inadequate answer. – Eric Postpischil Oct 07 '20 at 21:37
  • I wrote an answer regarding typedefs here. Maybe it could be considered a duplicate. https://stackoverflow.com/a/54752982/6699433 – klutt Oct 07 '20 at 21:49

3 Answers3

3

You can't refer to a typedef name before it's defined.

Either move the typedef before the struct Cell definition, or use struct Song in the struct Cell definition.

Barmar
  • 741,623
  • 53
  • 500
  • 612
2

Is it therefore generally advised to place typedefs at the very beginning of each file?

Not necessarily, and you could avoid typedef -s by coding

struct Cell { 
    struct Song song;
    struct Cell *pnext;
};

Of course, struct Song should be defined "before" (take into account the C preprocessor).

You could also code

typedef struct Song Song_t;
typedef struct Cell Cell_t;

and use later only Song_t etc. You do have to provide (later) a definition of struct Song (not just a forward declaration). For details, read Modern C, see this C reference website, and the C11 standard n1570.

Look for inspiration into the source code of the Linux kernel or of a simple C compiler, such as nwcc, or of the GTK toolkit. All these are coded (mostly) in C. Look also for examples on github. Study also the source code of GCC. It is a popular C compiler (and old versions of it -e.g. GCC 4.4- have been coded in C mostly).

Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
0

The C compiler parses the preprocessed code in a single pass from top to bottom.

That means that any object or type must first be defined before it is used.

dbush
  • 205,898
  • 23
  • 218
  • 273
  • Not *exactly* true, because of the [C preprocessor](https://en.wikipedia.org/wiki/C_preprocessor). Read the C11 standard [n1570](https://web.cs.dal.ca/~vlado/pl/C_Standard_2011-n1570.pdf) – Basile Starynkevitch Oct 07 '20 at 21:32
  • @BasileStarynkevitch The processor usually feeds tokens right into the parser. As such, it won't need a separate phase. – Petr Skocik Oct 07 '20 at 21:37
  • @PSKocik: It depends upon what you call a phase. In [GCC](http://gcc.gnu.org/) that word does not even appear (and GCC is a C compiler). See [documentation of GCC internals](https://gcc.gnu.org/onlinedocs/gccint/). AFAIK, GCC has several hundred optimization passes. See its file `gcc-10.*/gcc/passes.def` – Basile Starynkevitch Oct 07 '20 at 21:44
  • 1
    C translation of course requires retrograde changes to previously scanned code, but most of that is handled by the linker. E.g., in `extern int x; int foo(void) { return x; } int x = 3;`, the instructions for `foo` cannot be fully resolved while `foo` is being scanned and `int x` has not been scanned. But even disregarding symbol resolution, there are aspects of C that require some look-ahead or retrograde work. For example, `switch` statements are not properly compiled, at least not well, without information about the `case` labels. – Eric Postpischil Oct 07 '20 at 21:48
  • @BasileStarynkevitch I should have used the word pass (i.e., a completed scan from beginning to end). I trust you understand what I mean. – Petr Skocik Oct 07 '20 at 21:48
  • 1
    And of course, optimizers throw all order out the window. Typical compilers build flow graphs and other data structures describing the program and then perform various transformations on them that have no bearing to source code order. A compiler with such processing cannot be said to operate solely in a single pass from top to bottom. – Eric Postpischil Oct 07 '20 at 21:49
  • Again, GCC has more than two hundred [passes](https://gcc.gnu.org/onlinedocs/gccint/Passes.html). Many of them operate on [GIMPLE](https://gcc.gnu.org/onlinedocs/gccint/GIMPLE.html)s, others on [RTL](https://gcc.gnu.org/onlinedocs/gccint/RTL.html). Your [GCC plugin](https://gcc.gnu.org/onlinedocs/gccint/Plugins.html) can add more passes – Basile Starynkevitch Oct 07 '20 at 21:49
  • @BasileStarynkevitch As Eric Postpischil has pointed out, the point is that C compilers *can* run in a single pass (with some backpatching). I think tinycc mostly does that. But yeah, the 1st sentence of this answer is indeed a bit misleading. – Petr Skocik Oct 07 '20 at 21:53
  • @PSkocik Updated to address parsing specifically. – dbush Oct 07 '20 at 21:55