Understanding the difference between f() and f(void) in C and C++ once and for all

C++CFunctionVoid

C++ Problem Overview


Ok, so I have heard different opinions on this subject and just want to make sure I understand it correctly.

For C++

Declarations void f(); and void f(void); mean precisely the same thing, the function f does not take any parameters. Ditto for definitions.

For C

Declaration void f(void); means that f does not take any parameters.

Declaration void f(); means that function f may or may not have parameters, and if it does, we don't know what kind of parameters those are, or how many there is of them. Note that it is NOT the same as ellipsis, we can't use va_list.

Now here is where things get interesting.

Case 1

Declaration:

void f();

Definition:

void f(int a, int b, float c)
{
   //...
}
Case 2

Declaration:

void f();

Definition:

void f()
{
   //...
}
Question:

What happens at compile time in cases 1 and 2 when we call f with the correct arguments, wrong arguments and no arguments at all? What happens at run time?

Additional question:

If I declare f with arguments, but define it without them, will it make a difference? Should I be able to address the arguments from the function body?

C++ Solutions


Solution 1 - C++

More terminology (C, not C++): a prototype for a function declares the types of its arguments. Otherwise the function does not have a prototype.

void f();                      // Declaration, but not a prototype
void f(void);                  // Declaration and prototype
void f(int a, int b, float c); // Declaration and prototype

Declarations that aren't prototypes are holdovers from pre-ANSI C, from the days of K&R C. The only reason to use an old-style declaration is to maintain binary compatibility with old code. For example, in GTK 2 there is a function declaration without a prototype -- it is there by accident, but it can't be removed without breaking binaries. The C99 standard comments:

> 6.11.6 Function declarators

>The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

Recommendation: I suggest compiling all C code in GCC/Clang with -Wstrict-prototypes and -Wmissing-prototypes, in addition to the usual -Wall -Wextra.

What happens

void f(); // declaration
void f(int a, int b, float c) { } // ERROR

The declaration disagrees with the function body! This is actually a compile time error, and it's because you can't have a float argument in a function without a prototype. The reason you can't use a float in an unprototyped function is because when you call such a function, all of the arguments get promoted using certain default promotions. Here's a fixed example:

void f();

void g()
{
    char a;
    int b;
    float c;
    f(a, b, c);
}

In this program, a is promoted to int1 and c is promoted to double. So the definition for f() has to be:

void f(int a, int b, double c)
{
    ...
}

See C99 6.7.6 paragraph 15,

> If one type has a parameter type list and the other type is specified by a function declarator that is not part of a function definition and that contains an empty identifier list, the parameter list shall not have an ellipsis terminator and the type of each parameter shall be compatible with the type that results from the application of the default argument promotions.

Answer 1

> What happens at compile time in cases 1 and 2 when we call f with the correct arguments, wrong arguments and no arguments at all? What happens at run time?

When you call f(), the parameters get promoted using the default promotions. If the promoted types match the actual parameter types for f(), then all is good. If they don't match, it will probably compile but you will definitely get undefined behavior.

"Undefined behavior" is spec-speak for "we make no guarantees about what will happen." Maybe your program will crash, maybe it will work fine, maybe it will invite your in-laws over for dinner.

There are two ways to get diagnostics at compile-time. If you have a sophisticated compiler with cross-module static analysis capabilities, then you will probably get an error message. You can also get messages for un-prototyped function declarations with GCC, using -Wstrict-prototypes -- which I recommend turning on in all your projects (except for files which use GTK 2).

Answer 2

> If I declare f with arguments, but define it without them, will it make a difference? Should I be able to address the arguments from the function body?

It shouldn't compile.

Exceptions

There are actually two cases in which function arguments are allowed to disagree with the function definition.

  1. It is okay to pass char * to a function that expects void *, and vice versa.

  2. It is okay to pass a signed integer type to a function that expects the unsigned version of that type, or vice versa, as long as the value is representable in both types (i.e., it is not negative, and not out of range of the signed type).

Footnotes

1: It is possible that char promotes to unsigned int, but this is very uncommon.

Solution 2 - C++

The whole thing is really a moot point if you're using C99 or later (and, unless you're stuck on an old embedded system or something like that, you probably should be using something more modern).

C99/C11 section 6.11.6 Future language directions, Function declarators states:

> The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

Hence you should avoid using things like void f(); altogether.

If it takes parameters, list them, forming a proper prototype. If not, use void to indicate definitively that it doesn't take any parameters.

Solution 3 - C++

In C++, f() and f(void) are same.

In C, they are different and any number of arguments can be passed while calling the f() function, but no argument can be passed in f(void).

Solution 4 - C++

In pure C, this results in the error: error C2084: function 'void __cdecl f(void )' already has a body

void f(void);
void f();

int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(void) {

}

void f() {

}

> fvoid.c line(19) : error C2084: function 'void __cdecl f(void )' already has a body

But in pure C++, it will compile without errors.

Overloading functions (C++ only, C has no overloading)

You overload a function name f by declaring more than one function with the name f in the same scope. The declarations of f must differ from each other by the types and/or the number of arguments in the argument list. When you call an overloaded function named f, the correct function is selected by comparing the argument list of the function call with the parameter list of each of the overloaded candidate functions with the name f.

Example:

#include <iostream>
using namespace std;

void f(int i);
void f(double  f);
void f(char* c);


int main() {
  f(10);
  f(10.10);
  f("ten");

  return 0;
}

void f(int i) {
  cout << " Here is int " << i << endl;
}
void f(double  f) {
  cout << " Here is float " << f << endl;
}

void f(char* c) {
  cout << " Here is char* " << c << endl;
}

Output:

Here is int 10
Here is float 10.1
Here is char* ten

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
Questionuser500944View Question on Stackoverflow
Solution 1 - C++Dietrich EppView Answer on Stackoverflow
Solution 2 - C++paxdiabloView Answer on Stackoverflow
Solution 3 - C++rs2012View Answer on Stackoverflow
Solution 4 - C++Software_DesignerView Answer on Stackoverflow