F() vs. F(void) in C vs. C++
nickdesaulniers.github.io
nickdesaulniers.github.io
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.
But this would mean you no longer have a nice way of specifying a function that takes parameters that you can't list :(
If we stick to standard C, all calls are statically determined, so your only option is to switch among different call expressions, like:
switch (nargs) {
case 0: return f();
case 1: return f(a[0]);
case 2: return f(a[0], a[1]);
...
}
Here, if the f pointer is under-declared, it saves you some casting.You can use a union:
struct fun {
int nargs;
union {
int (*ptr0)(void);
int (*ptr1)(valtype);
int (*ptr2)(valtype, valtype);
...
} fn;
}
Then: switch (f->nargs) {
case 0: return f->fn.ptr0();
case 1: return f->fn.ptr1(arg[0]);
...
}
Now there is a modicum of type checking. When you create the "struct fun" object, you may be able to take the address of a function that is compatible with one of the ptr's, and assing it to the correct union member without having to use a cast. If you record the number of arguments correctly, it won't be misused.POSIX generaly has very little to say about C matters; for the most part it defers to ISO C by normative reference.
Off the top of my head, because POSIX specifies dlopen and dlsym, that pretty much requires function pointers have to have a common representation convertible between void * and back.
One of my coworkers could defeat any idea for a new optimization with "well, what about unions?" which would typically complicate things quickly...
I'm not sure I follow: they would thwart your attempts at optimizing things by talking about unions?
Say we have a really contrived function that works with two pointers A and B to the same union type. The compiler cannot prove that A and B are distinct. The function evaluates A->x = expr, and also B->y in several places. Since A and B might be the same pointer, the assignment has to be regarded as clobbering B->y, which interferes with CSE of B->y and register caching.
As of C99, a possible solution here would be to declare the pointers restrict; then the compiler assumes they don't overlap: A->x has nothing to do with B->y.
If a struct is used, then x and y are different members and have nothing to do with each other for that reason, needless to say, even if A and B are the same pointer.
Most of the code surrounding this won't contain assignments into the union, so it won't impact optimization.
In an interpreted language I wrote this kind of union is initialized when a function object comes to life, and then not mutated again.
The union is necessary because a struct would blow up the size of the object significantly (and then it wouldn't fit into the GC heap cell size, requiring an additional piece of malloced memory).
int foo()
int x;
int y;
{ return x + y; }
Perfectly legal C, if not an old archaic and seldom used syntax. I saw it in some proprietary code, so, no, I cannot provide a link. It was odd, but worked for what it was trying to do.It pays to know the language you work in, not to write things like this, but to understand on the off chance you encounter it in the wild.
Edit, I may be wrong on the syntax, it might be:
int foo(x, y)
int x;
int y;
{ return x + y; }
I dont have the ISO spec in front of me, but one of these should work.Rather it should look like this:
int foo(x, y)
int x;
int y;
{ return x + y; }
x and y would be assumed to be int in the absence of the type specifiers, so in this case they're optional. foo(x, y) { return x + y; }Main takes argc and argv, but it's idiomatic to omit them if unused.
[0] http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2346.pdf
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".
It works because with the way the C ABI works on all platforms I'm aware of, extra arguments passed to the function can be completely ignored by the function with no ill effects, so the fact that _start actually invokes main with argc and argv can be ignored by main.
Which is to say, you could also write this as
int main(void) { return 0; }
and that would work equally as well. int WINAPI WinMain(HINSTANCE hInst, HINSTANCE hPrevInst, LPSTR, int)
(where WINAPI is just a macro for __stdcall)I also found a reference saying you can in fact use `int main()` if you want to, in a GUI subsystem Windows app, by using the Microsoft linker options /subsystem:windows /ENTRY:mainCRTStartup, but in that case you're not writing a stdcall function anymore.
EDIT: I guess I should clarify, my previous comment was talking about cdecl functions, i.e. the default calling convention in C.
So perhaps foo(args) means foo takes a list of arguments unspecified.
result = foo(&(struct foo_args){.arg1 = 4, .arg7 = "2"})I think what I want is llvm's blocks. Which isn't available in gcc.
varargs functions require at least one non-vararg parameter.
(As int_19h said:) On some architectures, vararg functions have their own distinct ABI, so (...) as a declaration is not compatible with any definition that doesn't also use (...).
int foo(int x, y, z, float a, b, c) { ...
instead of int foo(int x, int y, int z, float a, float b, float c) { ...
Yes, you can pack things like that into a struct sometimes, but not always. From talking to folks who were on the ANSI committee at the time, there was some reason why the first example wouldn't work. Some parsing ambiguity, but I forget the details.It's distressing to see new languages (like D) adopt this verbose style as if it were some well thought out idea. It wasn't, it was just a consequence of the new declaration style that the ANSI C committee adopted.
Also, a xor r32, r32 is 2 bytes, not 1.
Does the article imply otherwise?
> 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.
sorry, I must've misread the output from godbolt from too many window splits. Will update the article. Thanks for pointing it out. :)
Aren't these just declaring one overload of foo and defining a different overload?
This is how we "deleted" functions, especially otherwise auto generated ones such as a default or copy constructor and assignment operator pre C++11.
`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.
Also, any one else notice that the favicon is a gif (nyan cat)? I didn't know that was supported.
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.
https://old.reddit.com/r/C_Programming/comments/bn04w0/confu...
In short, for "typedef struct bar bar;", C views "bar" as an incomplete type, but C++ doesn't.