5 ms·
We can still get away with those sins today if you change C's implicit type from int to long. I modified chibicc to do just that it was a 1 LOC patch. Suddenly
by jart 3y ago
We can still get away with those sins today if you change C's implicit type from int to long. I modified chibicc to do just that it was a 1 LOC patch. Suddenly I didn't need prototypes anymore and everything just worked without #include lines.
- duskwuff 3y ago> Suddenly I didn't need prototypes anymore Under some very old C standards, maybe. But C99 requires that you at least declare a function before calling it.
- fuzztester 3y agoPascal had a forward declaration from way back, even before Object Pascal: https://en.m.wikipedia.org/wiki/Forward_declaration https://en.m.wikipedia.org/wiki/Forward_declaration https://en.m.wikipedia.org/wiki/Pascal_(programming_language) https://en.m.wikipedia.org/wiki/Pascal_(programming_language... https://en.m.wikipedia.org/wiki/Object_Pascal https://en.m.wikipedia.org/wiki/Object_Pascal https://www.thedelphigeek.com/2017/03/forward-record-declaration.html?m=1 https://www.thedelphigeek.com/2017/03/forward-record-declara...
- billfruit 3y agoBut why does it have to be that way though, can't the compiler scan the rest of the code/files and see if a defenition/declaration is present somewhere? There is no such requirement of a declaration before first call in Java for example?
- fuzztester 3y agoGood question.
- rezonant 3y agoIt's because the C compiler was designed to be single pass, so as to be speedy and require less memory on the systems of it's time.
- billfruit 3y agoIs that a requirement that is relevant presently? I would say not so much. Do modern C compilers also make only a single pass?
- hulitu 3y agoYes. Gcc throws errors like hell with c files which have the definitions after functions.
- rezonant 3y agoModern compilers still have to follow the rules of the language.
- billfruit 3y agoYes, but is the constraint that compiler shall make only a single pass, a rule of the language?
- trealira 3y agoPrior to C99 (i.e., in C90), there was a rule that any undeclared identifier id that was followed by a function call parenthesis would be treated as though there were an implicit declaration in the scope in which it was used like this: extern int id(); The empty parentheses meant it could take any number of arguments, since it was declared in the traditional style, where the argument types weren't specified first. This implicit declaration meant that if it was later defined in the same file as returning a type other than int, the compiler wasn't permitted to go back up in the file and treat the function call as though it returned a type other than int. This requirement was removed in C99, but in practice, compilers kept doing it for backwards compatibility, even when not invoked in C90 mode.
- fuzztester 3y agoLike early Pascal compilers, or maybe all of them. Don't know about the latter case (all).
- Gibbon1 3y agoWhat I understand is the preprocessor causes issues where you could do that maybe 99.9% of the time, especially with well written code. But it'd fail that 0.1% of the time. You do have analyses like ctags. In theory the compiler could use ctags to find definitions in source code. My experience is ctags can get confused. It's possible in a set of files to have multiple implementations of a function and ctags can't tell which one is used. And I see cases where it can't find definitions. Personally I'd be happy if the compiler tried to find a missing definition using ctags and issued a warning. I have wondered if adding funct, public, private keywords to the language might allow the compiler to reliably find function and stuct definitions in source files.
- reactordev 3y agoThe fact that I'm calling a function, it must exist, otherwise the compiler will throw an error ("undefined reference to function"). So forward declarations is just needless typing.
- saagarjha 3y agoThe compiler needs to see it to know how to call it.
- reactordev 3y agoIn which case, it needs to keep parsing the include tree until it finds it. I know why it exists. I’m just not happy about duplicating code. Other compilers are smarter than this.
- duskwuff 3y agoIf you're using a modern IDE, clangd can be configured to automatically insert #include statements (--header-insertion=iwyu).
- saagarjha 3y agoThe thing I hate about this is that it really has no idea what a “public” header is. If I have it on I’ll use something like uintptr_t and get a random include for <__bits/std/uintptr.h> or something that I don’t want. I assume there’s like some manual auditing one can do to go “don’t peek through stdint.h” but either nobody does this reliably or it doesn’t work.
- duskwuff 3y ago> The fact that I'm calling a function, it must exist That's not necessarily true. It's possible that the symbol is a function pointer; calling a function pointer requires slightly different code generation. Compare the generated code between: void fn(void); void call_fn(void) { fn(); } and void (*fn_ptr)(void); void call_fn_ptr(void) { fn_ptr(); } In practice, it's probably a function, and that's what the compiler assumes if there's no declaration to go off of. But we all know what happens when you make assumptions.
- jart 3y ago> C99 requires that you at least declare a function before calling it Where is that written? Please quote the standard. My reading of ISO/IEC 9899:TC3 is that Foreword says (paraphrasing) "hey, we removed implicit int and implicit prototypes from the standard". So they simply stopped specifying it, but as far as I can tell, the standard says nothing about forbidding them. From my point of view, that means compilers are free to still implement implicit types as a compiler extension to the language, and everyone does, because that extension is needed in order to support the older c89 standard. It would only impact people who want to be able to claim their codebase is c99 standard compliant, because you can't do that if you depend on compiler extensions.
- kevin_thibedeau 3y agoPrototypes are required in C23 as part of the expungement of K&R syntax. This also brings harmonization with C++ where an empty parameter list is implicitly void.
- duskwuff 3y ago> Where is that written? ISO/IEC 9899:TC3 [1] §6.5.1 ¶2: "An identifier is a primary expression, provided it has been declared as designating an object (in which case it is an lvalue) or a function (in which case it is a function designator)." There's even a footnote to underscore this point: "79) Thus, an undeclared identifier is a violation of the syntax." [1]: https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf https://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf
- eVeechu7 3y agoA little bit of advice like that is a tremendous blessing to those of us who just need to touch C occasionally.