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 int
1 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.
It is okay to pass
char *
to a function that expectsvoid *
, and vice versa.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 functionfoo
taking an unspecified number of arguments of unspecified type"void foo(void)
means "a functionfoo
taking no arguments"
In C++:
void foo()
means "a functionfoo
taking no arguments"void foo(void)
means "a functionfoo
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
Writing a Matrix into a Single Txt File with Mpi
Qt MACro Keywords Cause Name Collisions
Inserting Elements in Multidimensional Vector
Why Should I Avoid MACros in C++
C++ Convert from Lpctstr to Const Char *
Scope Resolution Operator Being Used Twice
How to Pass a Std::Function Object to a Function Taking a Function Pointer
Very Simple Application Fails with "Multiple Target Patterns" from Eclipse
Print 2-D Array in Clockwise Expanding Spiral from Center
Covariant Return Type and Type Conversion
Speed Difference Between If-Else and Ternary Operator in C...
Can a Variable Be Declared Both Static and Extern
Why Does a Virtual Function Get Hidden