Home > OS >  Why the order in which you write declare and use functions in C# doesn't matter contrary to C
Why the order in which you write declare and use functions in C# doesn't matter contrary to C

Time:12-12

I was wondering, when I write code in C# Unity for my games, I can declare voids in the code after using them, and the code still works. When I write pure C code on the other hand, I need to declare my functions before using them, and I am very curious why is that difference present?

CodePudding user response:

I was wondering, when I write code in C# Unity for my games, I can declare voids in the code after using them, and the code still works. When I write pure C code on the other hand, I need to declare my functions before using them, and I am very curious why is that difference present?

Short answer:

  • Because C and C# are completely different programming languages with their own entirely separate backstories and compilers.
  • Because C was built on-top of C and inherited its compilation process.
  • ...and C was designed at a time when compilers only did 1 pass through source-code with minimal RAM usage which necessitates forward-declarations, while C# was designed when compilers could do "multi-pass" runs and build large in-memory program models which obviated the need for forward-declaration.

Longer answer:

Note that by "multi-pass" I don't mean that the compiler actually re-parses and processes source-files from-scratch multiple times; what it means is that after the C# compiler has parsed the source-code (which only happens once), it can extract out all the symbols for things (like types, methods, etc) that are actually used by a program and store that in a list in memory (list-of-used-things) and only complain about missing/broken references after it's built-up a list of everything that is fully defined by the program (list-of-defined-things) and compared the two lists. Whereas back in the 1970s and early 1980s computers simply didn't have enough memory to store those lists-of-things, hence why C requires it.

Now, today in 2021, arguably it is possible to have a C (and even a C compiler) that doesn't need forward-declarations, however that's another topic concerned with too many reasons I won't go into (though the main reason is probably because there simply isn't any demand: all existing C and C programs already have forward-declarations and no-one will write a C or C program without forward-declarations just because a single compiler supports it. The ISO C and C language design committees could introduce it, but because forward-declarations are fundamental to C's language design it would be a truly massive breaking-change to the wider C and C ecosystem and everyone would complain.

C was built on-top of C, and so it inherited its requirement for forward-declaration. C 's compatibility with C source-code was a huge positive factor for C 's popularity compared to other OOP languages at the time (like Smalltalk and Object Pascal) which would require existing C programs to be either completely ported-over to a new language or require C programs to be linked in binary form, which complicates everything.

Objective-C is another OOP language built on-top of C, and it too, inherits C's requirement for forward-declaration.

Arguably C could have been designed to be able to be compiled without forward-declarations however that would add a ton of complexity w.r.t. its compatibility with C source files, as well as almost certainly delaying the release date of C itself because hammering out the specification for exactly how C-compatible-compilation-without-forward-declaration should work would take months or even years to complete.

Flash forward to the mid-1990s with Java (and eventually C#) and these modern programming languages are not intended to be source-compatible with C programs at all (the shared curly-brace syntax notwithstanding), which means those languages don't need to be designed around limitations imposed by contemporaneous hardware limitations.

This isn't to say that the ergonomic design of the Java language wasn't compromised by its compiler design: Java still requires 1-type-per-file and the CLASSPATH/SOURCEPATH bollocks. I understand these restrictions were necessary for Java's then very fast and simple compiler, and it does mean that all Java projects will have a predictable layout, but 25 years later these restrictions are increasingly seen as imposing a very tedious burden on programmers and are overall silly restrictions with little benefit. However compare that to post-Java languages like C#, Rust, Kotlin, Go, Swift which entirely decouple a project's source-code arrangement from the compiled output.


I assume you've read this: What are forward declarations in C ? - if you haven't read it, then you should read it first.

Now, consider this table and spot the correlation:

Language C C Java C#
Vintage 1972 1985-ish 1995 2001
Exemplary contemporaneous typical workstation PDP-11 HP 200 Series SPARCstation 10 Beige box
RAM Cost: USD per MiB N/A $1,000 $32 $4
Typical RAM in said contemporaneous typical workstation 56 KiB 512 KiB 32 MiB 128 MiB
Single-pass compiler Yes Yes No No
Requires forward declarations Yes Yes No No
Language ergonomics Basic Awful Good Very good

In conclusion: the more RAM a compiler can use means the more ergonomic a language can be, because the fundamental design of a programming language doesn't need to be compromised by limitations imposed on the parser/compiler/linker from having insufficient memory.

  •  Tags:  
  • c# c
  • Related