Understanding the Difference Between F() and F(Void) in C and C++ Once and for All

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 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.

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++).

`f(void)` meaning no parameters in C++11 or C?

In C++ they both mean the same thing.

In C f(void) is different from f(), becuse f() means "unspecified parameters" - you can legally pass anything (whether the function at receiving the data is happy about that or not is another matter).

Difference between void and float function in C

The purpose of a function is to encapsulate a frequently used calculation. If you code it as returning a value, then you can call it whenever you want inside a bigger program, regardless of whether you want to print out the result or not. Thus the second function is more reusable.

While there is nothing wrong with the way you wrote the function, it assumes that you sit by a keyboard to enter a value, and that you will just want to look at the conversion result.

Now imagine you program a tool which takes a list of temperatures in Celsius (from a spreadsheet), and wants to convert them all into Fahrenheit. Your function wouldn't work here, but the second version can be used with a wrapper around it to read the Celsius value from the spreadsheet, call the function, and then put the converted value somewhere else.

In general it's a good design principle to keep the functionality of a function to a minimum, so that it can be used in more different circumstances.

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 the parameters of a function that takes any number of parameters, in C

int f(void) means a function returning int that takes no parameters.

You can see this example to see the consequence of using int f()

#include <stdio.h>

void f();

int main() {
f(); /* prints some garbage */
f(16); /* prints 16 */
return 0;
}

void f(a1)
int a1;
{
printf("%d\n", a1);
}

And also this:-

#include <stdio.h>
#include <stdlib.h>
int f();
int f(int x) {
return x;
}
int main (int argc, char *argv[]) {
printf ("%d\n", f(atoi(argv[1])));
return 0;
}

pax> gcc -Wall --std=c99 -o qq qq.c
pax> ./qq 42
42

What is the difference between non-type template parameters in C++17 and C++11?

The relevant difference is in the requirements on allowed template arguments (not template parameters) in [temp.arg.nontype].

C++11:

A template-argument for a non-type, non-template template-parameter shall be one of:

  • ...
  • a constant expression that designates the address of an object with static storage duration and external or internal linkage or a function with external or internal linkage, including function templates and function template-ids but excluding non-static class members, expressed (ignoring parentheses) as & id-expression, except that the & may be omitted if the name refers to a function or array and shall be omitted if the corresponding template-parameter is a reference; or
  • ...

C++17:

A template-argument for a non-type template-parameter shall be a converted constant expression of the type of the template-parameter. For a non-type template-parameter of reference or pointer type, the value of the constant expression shall not refer to (or for a pointer type, shall not be the address of):

  • a subobject,
  • a temporary object,
  • a string literal,
  • the result of a typeid expression, or
  • a predefined __func__ variable.

In C++11, the template-argument function is not in the form & id-expression, and the name does not refer to the function something. It refers to a variable of type int (*const)(int, int), whose value points at something. (And do_something<&function> wouldn't help, because now you have a pointer to pointer to function, which won't convert to the pointer to function type.)

In C++17, the syntax requirement is gone, and the restriction is a more relaxed purely semantic requirement on what objects can't be pointed at or referenced.



Related Topics



Leave a reply



Submit