News for hackers. Stuff that matters.
Re:C Internals by _kst_ on Tuesday June 02, 2020 @01:31AM (#23387798)
http://www.avabodh.com/cin/programstructure.html

    ...
    void main()
    ...
That's where I stopped reading.

Since this comment is getting downvotes, I'll explain. The "void" keyword was introduced in the 1989 ANSI C standard. That same standard specified that the valid definitions for the main functions are:

    int main(void) { /* ... */ }
and

    int main(int argc, char *argv[]) { /* ... */ }
or equivalent (or implementations can support other forms). There has never been a version of C in which "void main()" is a valid way to define the main function.

It's a small detail, and yes, many compilers will let you get away with it (the language doesn't require a diagnostic), but anyone writing about C should be aware of this, and should set a good example by writing correct code.

Maybe the site is OK other than that, but it doesn't inspire confidence.

Re:C Internals by fanf2 on Tuesday June 02, 2020 @05:27AM (#23389248)
The void keyword predates ANSI C by several years. It was introduced in pcc in the early 1980s during the effort to improve the portability of Unix, which included improving the type safety of C. Steve Johnson, author of pcc and lint, recently wrote about the origin of void - https://minnie.tuhs.org/pipermail/tuhs/2020-May/021034.html
Re:C Internals by _kst_ on Tuesday June 02, 2020 @11:41AM (#23392221)
Good point. "void" does not appear in K&R1 (1978), and there was no definitive manual for the language between that and the ANSI standard, but it did start appearing in implementations.

Still, I'm not aware of any implementation that explicitly supported void as the return type of main. (Many compilers would accept just about any return type without complaint.)

Somehow some authors got the idea that "void main()" was a good idea. I find it to be a good way to detect authors who don't know the language very well.

Re:C Internals by MaxBarraclough on Tuesday June 02, 2020 @05:31AM (#23389274)
According to [0], it's legal to use a void return type, but the value returned to the host environment is unspecified. Whereas if you use an int return type and allow control to reach the closing brace of the main function, it's as if you put return 0; (as of C99)

It seems like a bad idea to use void return type (presumably you want the program to always return 0), but the standard permits it.

edit Wikipedia seems less convinced that it's ok [1]

[0] https://en.cppreference.com/w/c/language/main_function

[1] https://en.wikipedia.org/wiki/Entry_point#C_and_C++

Re:C Internals by scatters on Tuesday June 02, 2020 @07:44AM (#23389976)
It's legal if implementation-specified. The clause you're looking at says how implementations that add extra signatures for main are required to map the return value (if any) to an exit status.

You can't just make up extra signatures for main and expect your compiler to deal with them. Of course, if your compiler vendor says that a particular non-standard signature is available, you're free to use it.

Re:C Internals by throwaway894345 on Tuesday June 02, 2020 @10:46AM (#23391578)
Man, I don't miss the amount of spec lawyering that exists in C. All of these pedantic details that don't contribute a single lick to writing software...
Re:C Internals by _kst_ on Tuesday June 02, 2020 @11:34AM (#23392128)
All those "pedantic details" let you know how you can write code that a future version of your compiler won't quietly break.
Re:C Internals by throwaway894345 on Tuesday June 02, 2020 @12:37PM (#23392971)
You missed the obvious point: other languages don’t allow you to write code that will break in future versions (obviously provided that the spec doesn’t change in a breaking way, but this is the same for C). The compiler is your lawyer.
Re:C Internals by MaxBarraclough on Tuesday June 02, 2020 @01:17PM (#23393469)
That's a good point. If your Java code compiles with the Eclipse compiler, you can be pretty confident it will compile with the javac compiler. If it doesn't, it's likely to be a compiler bug somewhere. Not so for C.

Put another way, C doesn't let you get away with the try it and see approach the way many other languages do.

Re:C Internals by throwaway894345 on Tuesday June 02, 2020 @01:25PM (#23393557)
> Put another way, C doesn't let you get away with the try it and see approach the way many other languages do.

Exactly, and I don't see any upside to this "tradeoff". It seems strictly worse, but maybe I'm missing something.

Re:C Internals by MaxBarraclough on Tuesday June 02, 2020 @05:16PM (#23396407)
In this particular case, part of the complexity is that C explicitly supports 'freestanding' programs, like kernels, where the parameters and return-value of main are inapplicable.

More generally it's because the C language standard is steered only partly by the interests of C programmers: 1) the C standard aims to easily support many different platforms and compilers, 2) it aims to remain a compact and stable (slowly changing) language, and 3) it aims for maximal performance. None of those 3 aligns with programmer convenience.

Point 1 ties in to the origins of the C standard itself, which was deliberately designed not to nail down every last detail. This was to incorporate different compilers and platforms. e.g. Where Java mandates two's complement, C doesn't. C was carefully designed so that platforms that don't use two's complement, don't have to jump through (many) hoops in order to comply with the standard. Neither should they be tempted to just break the standard for their own convenience/performance.

Point 2 means there's tremendous inertia in the C language. There are considerable upsides to the language being slow to change, though. Although mistakes in the language are slower to be fixed, they're also much less likely to make it into the standard in the first place, compared to a fast-moving language like D. It's also a tremendous advantage for compiler engineers - they can spend their time improving their compiler rather than keeping up with the standard.

(Related trivia: C++20 will break with tradition and mandate two's complement for signed integer types. I don't think C will do this though.)

And finally Point 3. C's weird and wonderful rules on undefined behaviour permit compilers to make strange optimisations and to omit runtime checks, but they require the programmer to have an eagle eye, and undefined behaviour can manifest in peculiar ways that are hard to hunt down. There are endless horror stories of bizarre undefined behaviour.

That said, I'm not sure that performance (on modern platforms) is really such a factor in C's quirks. I believe that these days (with modern compilers), C's performance is generally about the same as that of other similar languages that lack broad undefined behaviour, such as Ada.

Honourable mention: in at least one instance, undefined behaviour was deliberately introduced into the standard to permit trap-based error-reporting that was convenient on one particular hardware platform. [0] (Previously the relevant action would merely produce an indeterminate value.) I believe this unusual though even for C.

[0] http://blog.frama-c.com/index.php?post/2013/03/13/indetermin... (ctrl-f for Itanium)

Re:C Internals by DoingIsLearning on Tuesday June 02, 2020 @04:19AM (#23388874)
Just for some context, the standard specifies an int return type for the purpose of returning a meaningful value back to a hosting operating system.

However, in the specific context of stand-alone or freestanding programs, the "void main()" definition would be absolutely nonconsequential, since there is no host to return a value to.

Re:C Internals by tom_mellior on Tuesday June 02, 2020 @05:42AM (#23389324)
> However, in the specific context of stand-alone or freestanding programs, the "void main()" definition would be absolutely nonconsequential

One possible consequence I can think of: If your program is safety-critical (or even otherwise) you might be interested in running static analysis or verification tools on it. These tools implement the C standard, so they would emit a diagnostic on the use of void main.

Other than that, sure, the language police will not come and break down your door. If your compiler's docs say that it accepts this construct with the meaning you want, it is indeed an inconsequential, though also completely unnecessary, deviation from the standard.

Re:C Internals by _kst_ on Tuesday June 02, 2020 @11:26AM (#23392009)
For a freestanding implementation, the program entry point is entirely implementation-defined. It may or may not be called "main". In that kind of environment, "void main()" (or, better, "void main(void)") might be correct. You need to read the implementation's documentation.

For hosted implementations, "void main()" might be valid, but "int main(void)" is always valid.

A tutorial should not suggest "void main()" without mentioning any of this.

Reference: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf sections 5.1.2.1 (freestanding environments) and 5.1.2.2.1 (hosted environments).

Re:C Internals by ken on Tuesday June 02, 2020 @12:12PM (#23392656)
This sounds like pointless nitpicking. Has there ever been any real C compiler on any OS (not the DeathStation 9000) where this distinction has caused any issue?

FWIW, I learned "ANSI C" from a book older than 1989. I've written C programs that ran on 8-bit to 64-bit machines, big- and little-endian, on a dozen OSs (most of which no longer exist), and even no OS. I've never even heard of this being a problem.

Re:C Internals by commandlinefan on Tuesday June 02, 2020 @08:56PM (#23398464)
You got further than me - I gave up after “for writing softwares” and then “which can directly be run by CPU”.
Re:C Internals by boblivion on Tuesday June 02, 2020 @04:17AM (#23388862)
Which is funny because the void keyword specifies that the function receives no arguments. Otherwise you could call it with any amount of arguments you want. However, the linker doesn't care about type-signatures, so in the end this main function still gets called with argc, argv, envp.
Re:C Internals by _kst_ on Tuesday June 02, 2020 @11:37AM (#23392179)
The "any amount of arguments you want" applies to C function call. The mechanism for invoking your "main" function might use a different mechanism.

The C standard specifies (for hosted implementations) two ways to define "main", and allows implementations to document and support more. "int main(void)" is one of them. "int main()" is not. So, strictly speaking, using "int main()" makes your program's behavior undefined.

On the other hand, as far as I know every implementation actually allows "int main()" with no problem. This was necessary to support pre-ANSI C code, which couldn't use the "void" keyword. It's still better to be explicit and use "int main(void)" rather than "int main()". (It can also affect recursive calls to main, which are legal but almost certainly a bad idea.)

Re:C Internals by pjmlp on Wednesday June 03, 2020 @01:37AM (#23400182)
Which is why C++ makes use of name mangling, to achieve link time safety like in Mesa / Modula-2 derived languages, while relying on primitive UNIX linkers built for C semantics.
Re:C Internals by doomjunky on Tuesday June 02, 2020 @09:52PM (#23398839)
Where did the author commit to a certain standard? Maybe the author knows all of this but isn't -Wpedantic. Please don't jump to conclusions and assume worst.
Re:C Internals by Y_Y on Tuesday June 02, 2020 @03:36AM (#23388619)
It's legal to declare main as returning an int, And then not return any value, but it's not legal to declare it as not returning any value? I agree that in many contexts it's useful and important to have a good return value, but to be honest, I almost never end up doing it in my own programs.
Re:C Internals by fanf2 on Tuesday June 02, 2020 @05:21AM (#23389218)
The declared return type affects the calling convention, and if you get it wrong weird things can happen even if the function does not return.
Re:C Internals by Koshkin on Tuesday June 02, 2020 @07:41AM (#23389960)
This could only be true if the returned value is placed on the stack (where the size of the reserved space is determined by the expected type), but usually a register suffices, and then it does not matter as much.
Re:C Internals by Y_Y on Tuesday June 02, 2020 @07:09AM (#23389748)
It would be cool if someone could give an example of this behaviour.
Re:C Internals by fanf2 on Tuesday June 02, 2020 @08:10AM (#23390124)
Consider a hypothetical flavour of the Deathstation 9000 architecture in which the ABI uses call-by-reference for return values, and for void functions there is no return value argument. If you get the declaration wrong, the arguments will be placed in the wrong registers.

These kinds of problems might not happen with common ABIs, but if you try to write C on the assumption that it will be compiled in a reasonable way based on your knowledge of how the plaform works, then modern compilers will punish you for your presumption.

Re:C Internals by badsectoracula on Tuesday June 02, 2020 @09:06AM (#23390559)
> if you try to write C on the assumption that it will be compiled in a reasonable way based on your knowledge of how the plaform works, then modern compilers will punish you for your presumption.

Which is really a bug that everyone has decided to look the other way around because it wins compiler benchmarks despite coming against what C originally was meant for.

Specifically (from the C89 rationale[0], that i'm certain most people who think those benchmarks are good haven't read):

> C code can be non-portable. Although it strove to give programmers the opportunity to write truly portable programs, the Committee did not want to force programmers into writing portably, to preclude the use of C as a ``high-level assembler'': the ability to write machine-specific code is one of the strengths of C. It is this principle which largely motivates drawing the distinction between strictly conforming program and conforming program (§1.7).

Also in the same rationale it mentions how "the spirit of C" is to do operations in the way the machine would do it instead of forcing some abstract rule - yet that exactly is what happens later when everyone goes all language lawyer about C's abstract machine and how you should not rely on what you think the target machine would do.

[0] http://www.lysator.liu.se/c/rat/a.html#1-1