Sorry for the newbie thread.
I searched online, but I still have some questions, of which I'm guessing the answer to all of them is "yes":
- Is it like that because of legacy reasons? Namely, those old compilers were single-pass back in the day?
- Even if redundant, is it fair to say that the behavior is valuable for non-trivial programs that rely on header files?
- Will this behavior be around for a while because the people working on C2x have bigger things to work on?
Here’s what I know, but I would appreciate clarification or examples:
A function declaration specifies the return type, function name, and parameters. It ends with a semicolon and tells the compiler about the function's signature but doesn’t contain the implementation. For example:
int add(int num1, int num2);A function definition actually implements the function with the code inside curly braces. For example: c int add(int a, int b) { return a + b; }
Some specific questions I have:
Why is it sometimes okay to declare a function without parameter names but you must always specify parameter types?
Can a function declaration and definition differ in the way parameters are named?
What is the practical benefit of separating declaration and definition in bigger projects?
Are there any common mistakes beginners make regarding declaration vs definition?
Thanks in advance for your help!
Videos
here I am talking about c++/c language
Yes, they are different.
In the first example, you are just telling the compiler about the name and return type of the function and nothing of its expected arguments.
In the second example you are telling the compiler the full signature of the functions, both return type and expected arguments, prior to calling them.
The second form is pretty much universally better as it helps you compiler do a better job warning you when you have the wrong type or number of arguments when calling the function.
Also note int function() in C is a function that can accept any arguments, not a function that accepts no arguments. For that you need an explicit void, i.e int function(void). This mostly trips up those coming to C from C++.
See also: Why does a function with no parameters (compared to the actual function definition) compile?
To demonstrate why the first, antiquated form is bad in modern C, the following program compiles without warning with gcc -Wall -ansi -pedantic or gcc -Wall -std=c11.
Copy#include<stdio.h>
int foo();
int main(int argc, char**argv)
{
printf("%d\n", foo(100));
printf("%d\n", foo(100,"bar"));
printf("%d\n", foo(100,'a', NULL));
return 0;
}
int foo(int x, int y)
{
return 10;
}
UPDATE: M&M brought to my attention that my example uses int not float for the functions. I think we can all agree that declaring int function1() is bad form, but my statement that this declaration accepts any arguments is not quite correct. See Vlad's answer for relevant spec section explaining why that is the case.
The difference is that then there is a function prototype as in the second code snippet then the compiler checks that the number and types of arguments correspond to the number and types of the parameters. The compiler can issue an error at the compilation-time if it will find an inconsistence.
If there is no function prototype as in the first code snippet then the compiler performs default argument promotions on each argument that includes the integer promotions and conversion of expressions of type float to type double. If after these operations the number and types of promoted arguments do not correspond to the number and types of parameters the behaviour is undefined. The compiler can be unable to issue an error because the function definition can be in some other compilation unit.
here are relevant quotes from the C Standard (6.5.2.2 Function calls)
2 If the expression that denotes the called function has a type that includes a prototype, the number of arguments shall agree with the number of parameters. Each argument shall have a type such that its value may be assigned to an object with the unqualified version of the type of its corresponding parameter.
6 If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:
— one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;
— both types are pointers to qualified or unqualified versions of a character type or void.
As for your code snippets then if the second parameter had type double then the code would be well-formed. However as the second parameter has type float but the corresponding argument will be promoted to type double then the first code snippet has undefined behaviour.
In case of function, storage allocation is done when a function called first time. Definition itself doesn't make a storage allocation.
You should also note that, making function body empty is valid when the return type of function is void. Therefore, compiler should raise a warning against
int bar(int a) {};
[Warning] control reaches end of non-void function [-Wreturn-type]
[Warning] ISO C does not allow extra ';' outside of a function [-pedantic]
Function declaration helps the compiler to check whether the call you make for the function obeys the same signature or not, for syntactical correctness in terms of return type, number and type of arguments and order of arguments too. A function definition is required by the linker for cross-referencing the call (use) and the definition.
When you declare a function, no storage is allocated, when you define a function, function created on relocatable reloadable address space, size depends on the machine language translation of the function, varies from platform to platform
A declaration introduces an identifier and describes its type, be it a type, object, or function. A declaration is what the compiler needs to accept references to that identifier. These are declarations:
extern int bar;
extern int g(int, int);
double f(int, double); // extern can be omitted for function declarations
class foo; // no extern allowed for type declarations
A definition actually instantiates/implements this identifier. It's what the linker needs in order to link references to those entities. These are definitions corresponding to the above declarations:
int bar;
int g(int lhs, int rhs) {return lhs*rhs;}
double f(int i, double d) {return i+d;}
class foo {};
A definition can be used in the place of a declaration.
An identifier can be declared as often as you want. Thus, the following is legal in C and C++:
double f(int, double);
double f(int, double);
extern double f(int, double); // the same as the two above
extern double f(int, double);
However, it must be defined exactly once. If you forget to define something that's been declared and referenced somewhere, then the linker doesn't know what to link references to and complains about a missing symbols. If you define something more than once, then the linker doesn't know which of the definitions to link references to and complains about duplicated symbols.
Since the debate what is a class declaration vs. a class definition in C++ keeps coming up (in answers and comments to other questions) , I'll paste a quote from the C++ standard here.
At 3.1/2, C++03 says:
A declaration is a definition unless it [...] is a class name declaration [...].
3.1/3 then gives a few examples. Amongst them:
[Example: [...]
struct S { int a; int b; }; // defines S, S::a, and S::b [...]
struct S; // declares S
—end example
To sum it up: The C++ standard considers struct x; to be a declaration and struct x {}; a definition. (In other words, "forward declaration" a misnomer, since there are no other forms of class declarations in C++.)
Thanks to litb (Johannes Schaub) who dug out the actual chapter and verse in one of his answers.
From the C++ standard section 3.1:
A declaration introduces names into a translation unit or redeclares names introduced by previous declarations. A declaration specifies the interpretation and attributes of these names.
The next paragraph states (emphasis mine) that a declaration is a definition unless...
... it declares a function without specifying the function’s body:
void sqrt(double); // declares sqrt
... it declares a static member within a class definition:
struct X
{
int a; // defines a
static int b; // declares b
};
... it declares a class name:
class Y;
... it contains the extern keyword without an initializer or function body:
extern const int i = 0; // defines i
extern int j; // declares j
extern "C"
{
void foo(); // declares foo
}
... or is a typedef or using statement.
typedef long LONG_32; // declares LONG_32
using namespace std; // declares std
Now for the big reason why it's important to understand the difference between a declaration and definition: the One Definition Rule. From section 3.2.1 of the C++ standard:
No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.