Is F(Void) Deprecated in Modern C and C++

Is f(void) deprecated in modern C and C++?

In C, the declaration int f(void) means a function returning int that takes no parameters. The declaration int f() means a function returning int that takes any number of parameters. Thus, if you have a function that takes no parameters in C, the former is the correct prototype.

In C++, I believe int f(void) is deprecated, and int f() is preferred, as it specifically means a function that takes no parameters.

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

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.

Is there a difference between foo(void) and foo() in C++ or C?

In C:

  • void foo() means "a function foo taking an unspecified number of arguments of unspecified type"
  • void foo(void) means "a function foo taking no arguments"

In C++:

  • void foo() means "a function foo taking no arguments"
  • void foo(void) means "a function foo taking no arguments"

By writing foo(void), therefore, we achieve the same interpretation across both languages and make our headers multilingual (though we usually need to do some more things to the headers to make them truly cross-language; namely, wrap them in an extern "C" if we're compiling C++).

Is it better to use C void arguments void foo(void) or not void foo() ?

void foo(void);

That is the correct way to say "no parameters" in C, and it also works in C++.

But:

void foo();

Means different things in C and C++! In C it means "could take any number of parameters of unknown types", and in C++ it means the same as foo(void).

Variable argument list functions are inherently un-typesafe and should be avoided where possible.

Use of 'void' in a function parameter

The two canonical forms of main are, according to the standard (see C99 section 5.1.2.2.2 "Program startup"):

int main (void);
int main (int argc, char *argv[]); // or equivalent such as char **argv

Others are specifically allowed but those are the required ones.

As to the preferred form between fn(void) and fn(), I prefer the former since I like to explicitly state that there are no parameters.

There is also a subtle difference. C99 section 6.7.5.3 "Function declarators", paragraph 10, states:

The special case of an unnamed parameter of type void as the only item in the list
specifies that the function has no parameters.

Paragraph 14 of that same section shows the only difference:

An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied.

In other words, it means the same as void in the function definition but does not mean that in a standalone declarator (i.e., the prototype). int fn(); means that no information on the parameters is yet known but int fn(void); means there are no parameters.

That means that:

int fn();
int fn (int x) { return x; }
int main (void) { return fn(0); }

is valid but:

int fn(void);
int fn (int x) { return x; }
int main (void) { return fn(0); }

is not.

What does void mean in C, C++, and C#?

Basically it means "nothing" or "no type"

There are 3 basic ways that void is used:

  1. Function argument: int myFunc(void)
    -- the function takes nothing.

  2. Function return value: void myFunc(int)
    -- the function returns nothing

  3. Generic data pointer: void* data
    -- 'data' is a pointer to data of unknown type, and cannot be dereferenced

Note: the void in a function argument is optional in C++, so int myFunc() is exactly the same as int myFunc(void), and it is left out completely in C#. It is always required for a return value.

Why add void to method parameter list

The C++03 standard says (emphasis mine):

8.3.5.2

The parameter-declaration-clause determines the arguments that can be
specified, and their processing, when the function is called. [Note:
the parameter-declaration-clause is used to convert the arguments
specified on the function call; see 5.2.2. ] If the
parameter-declaration-clause is empty, the function takes no
arguments
.

This means that if you are talking to the compiler it's just a matter of taste.

If you are writing code that will be read by others, then the C++ way of doing things is

void foo();

The other form remains valid only for reasons of compatibility with C, where there was a difference among the two signatures.



Related Topics



Leave a reply



Submit