
F() vs. F(void) in C vs. C++ - headalgorithm
http://nickdesaulniers.github.io/blog/2019/05/12/f-vs-f-void-in-c-vs-c-plus-plus/
======
kazinator
Prefer T foo(void) in C because that's the declaration that actually
introduces, into the scope, information about the parameter list.

T foo() is a deprecated old style from before ANSI C 1989 which declares _foo_
in a way that is mum about how many arguments there are and what their types
are.

(void) is actually an ANSI C invention. C++ adopted this for better C
compatibility. (Yes, C++ did that once upon a time!) Then some C++ coders
started doing silly things like myclass::myclass(void): something that would
never be processed with a C compiler.

(void) is ugly; it would be a productive change in C to do away with K&R
declarations and make () equivalent to (void) like C++ did some three decades
ago.

~~~
saagarjha
> it would be a productive change in C to do away with K&R declarations and
> make () equivalent to (void) like C++ did some three decades ago.

But this would mean you no longer have a nice way of specifying a function
that takes parameters that you can't list :(

~~~
dlbucci
How often do you need to do that? I feel like I never have, and I think feel
like it's a rare enough use as that it shouldn't be the default behavior, but
maybe I haven't written enough C.

~~~
mkehrt
int main() { return 0; }

Main takes argc and argv, but it's idiomatic to omit them if unused.

~~~
shawxe
Why not just use int main(void)?

~~~
mkehrt
I'm confused. main doesn't take void--it takes two arguments. The f() syntax
means that the definition doesn't specify how many arguments the function will
be called with, while f(void) means it takes no arguments.

~~~
pksadiq
As per the standard, main() can take no argument [ie, main(void)] or 2
arguments [main (int, char asterisk asterisk) or equivalent] or some
implementation defined manner. See 5.1.2.2.1 in C2x working draft[0].

[0] [http://www.open-
std.org/jtc1/sc22/wg14/www/docs/n2346.pdf](http://www.open-
std.org/jtc1/sc22/wg14/www/docs/n2346.pdf)

~~~
tropo
Wow, it is painful to see that they still haven't accepted case ranges. They
are supported by gcc, clang, and icc. Like so:

case 123 ... 456:

Switching on strings is another thing people have been wanting for half a
century. It would seep up many programs, because most programmers don't bother
to generate a perfect hash or a carefully-balanced tree of "if".

------
userbinator
For those who are still wondering the actual reason for the extra instruction
after reading all that, it has to do with the calling convention: when calling
a variadic function in SysV AMD64, AL holds the number of vector registers
used for parameters. I believe the Microsoft x64 one doesn't do that.

Also, a xor r32, r32 is 2 bytes, not 1.

~~~
saagarjha
> a xor r32, r32 is 2 bytes, not 1

Does the article imply otherwise?

~~~
biesnecker
Yes.

> So Clang can potentially save you a single instruction (xorl %eax, %eax)
> whose encoding is only 1B, per function call to functions declared in the
> style f(), but only IF the definition is in the same translation unit and
> doesn’t differ from the declaration, and you happen to be targeting x86_64.

~~~
saagarjha
I couldn't seem to find that bit, thanks for pointing it out to me.

~~~
ndesaulniers
I just updated the article, too, sorry if that caused confusion.

------
pdpi
> Is an error in C, but surprisingly C++ is less strict here, not only
> allowing it but also taking the semantics of the definition. (Spooky)

Aren't these just declaring one overload of foo and defining a different
overload?

~~~
ndesaulniers
Ah! That's it, thanks! edited the article

~~~
J-Kuhn
Came here to say something that I suspect operator overloadíng. I read your
comment, pressed refresh, and it was already fixed.

------
maxdamantus
I feel like basically any "explanation" of fails to sufficiently explain
`f()`.

`void f(){}` is just a special case of the old style of defining functions.
Another case of this is `void f(a, b) int a, b; {}`.

Using the old style, functions are defined without "prototypes"; that is, the
type of the function does not specify its arguments.

Since changing the meaning of `void f(){}` would have broken backwards
compatibility, they added `void f(void){}` as the way of specifying "no
arguments" in the new style.

Edit: to further demonstrate, this is a perfectly valid C program:

> void f(a, b) int a, b; {}

> int main(void) { if(0) f("oops"); }

As the type of `f` does not specify its arguments, there is no constraint
violation (compilation error), and as the call is never actually performed
(`if(0) ..`), the program does not invoke the undefined behaviour that would
result in performing the invalid call.

------
mises
Thank you to this guy for not burying the lead. I hope more people put a
simple conclusion at the beginning when writing such a blog post.

Also, any one else notice that the favicon is a gif (nyan cat)? I didn't know
that was supported.

~~~
_Microft
This might blow your mind :)

[https://news.ycombinator.com/item?id=19712167](https://news.ycombinator.com/item?id=19712167)

------
IAmLiterallyAB
I've always wondered why EAX was getting cleared; I see it all the time in
disassembly. Very cool stuff!

------
korethr
In the conclusion the author says he considers T f() to be prettier than T
f(void). I personally prefer the look of T f(void) to T f(); the former is
explicit to both the programmer and the compiler that this function is not
supposed to take any arguments. If I can cause the compiler to not be
helpfully clever when it doesn't need to be, as well as make it clear to my
future selves, or any other poor soul condemned to read my code what my actual
intention was, I see that as a Good Thing.

------
johannkokos
> If we change foo2 to a declaration (such as would be the case if it was
> defined in an external translation unit, and its declaration included via
> header), then Clang can no longer observe whether foo2 definition differs or
> not from the declaration.

I tried enabling link time optimization on clang-8, it does see through and
eliminate redundant xor instruction in foo2(). gcc fails to do that, however.
I inspect the result by calling objdump on generated object code.

~~~
ndesaulniers
Great! Thanks for confirming; I suspected LTO would be able to do this
optimization as well. I should add that to the post.

------
Animats
That should have been disallowed in C11, but apparently it wasn't. You do have
to have function prototypes now, but they can still be empty.

------
tapirl
I found another difference between C and C++ recently. C++ compiles the code
in the following post ok, but C doesn't.

[https://old.reddit.com/r/C_Programming/comments/bn04w0/confu...](https://old.reddit.com/r/C_Programming/comments/bn04w0/confusion_on_array_parameters/)

In short, for "typedef struct bar bar;", C views "bar" as an incomplete type,
but C++ doesn't.

~~~
dearrifling
That doesn't sound right. I think the difference is that C++ doesn't treat
that as an array declaration so it doesn't require a complete type.

~~~
tapirl
What is the root cause for the difference?

------
selimthegrim
Ah, the days of void main(void)

