2

By Mistake I called a function with less parameters that what it has in its prototype , the code compiled with no warnings, so i did the following experiment:

compiled and ran the following code

#include <stdio.h>

void main()
{
   printf("1 param  %d \n",f(1));
   printf("2 params %d \n",f(2,7));
   printf("3 params %d \n",f(7,8,9));
}

int f(int a, int b)
{
  return (a+b);
}

I compiled it using gcc try.c it compiled without warnings and the results were

1 param  -557760119 
2 params 9 
3 params 15

my question is how come the compiler compiled with no issues, even when i called f() function that gets two parameters either with one or three parameters Thanks,

RDF
  • 51
  • 8
  • 1
    I am a little surprised that compiled at all, much less without warnings, considering that you did not even place a prototype for the function before its first use. – Refugnic Eternium Sep 21 '22 at 12:21
  • 3
    legacy. Long ago, someone thought it would be a good idea to allow implicit function declarations. Turn up the diagnostics on the compiler. – William Pursell Sep 21 '22 at 12:22
  • @RefugnicEternium , the question is a question about principal, if i put a prototype with two parameters before the main() , it would fail on "too few argument" or "too many argument", i was just surprised that without the function declaration it compiled, gave no warning and ran – RDF Sep 21 '22 at 12:29
  • 1
    A permanent employee where I was hired on a temp contract also suppressed compiler warnings because she didn't think they were important... You can imagine the code... _"The compiler helps those who help themselves."_ – Fe2O3 Sep 21 '22 at 12:35
  • 2
    In what version of GCC does that compile with no warnings? – Eric Postpischil Sep 21 '22 at 12:39
  • An empty parameter list in a function prototype means "any number and type of parameters". So, `int f();` as a prototype would fit all 3 calls. In this case the compiler needs `-O2 -Wall` to catch the uninitialized variable `b` in `f` when only 1 parameter is given. https://godbolt.org/z/cz5dY8a46 – mch Sep 21 '22 at 12:40
  • 1
    By the time you call `f()` inside `main()` there is no prototype in scope. You can swap the definitions of the functions (`f()` before `main()`) because a definition also serves as a prototype ... or add the prototype all on its own: `int f(int, int);` – pmg Sep 21 '22 at 12:44
  • @EricPostpischil , originally i tried on an old machine with `GCC 4.7` , then i tried also on newer one with `GNU Tools for Arm Embedded Processors 9-2019 (uses GCC 9.2)` – RDF Sep 21 '22 at 12:47
  • @RuudHelderman , thanks, I searched and missed this answer , this really gives an comprehensive answer – RDF Sep 21 '22 at 12:54

1 Answers1

3

C language is now a rather old language. In the first versions (known as K&R C or pre-ansi C from the 70's) functions prototypes did not exists, and the declaration of a function only declared the return type. As the default return type was int some programmers (many?) used not to declare functions returning int values. And this legacy behaviour is still observed by current compilers: if the compiler sees a function usage with no previous declaration, it assumes that it is a function returning int and taking an arbitrary number of parameters.

So the compiler is right when it compiles your code with no errors.

That being said, calling a function whose definition expects a precise number of parameters with a different number of parameter or with wrong parameter types invokes Undefined Behaviour. That means that per standard no requirement exists on how the program will behave at run time. Said differently never do that in a real world program.

For the actual results, many compilers use the so-called C calling convention: the parameters are put in the stack in reverse order by the caller, the callee uses them using the current position of the stack pointer, and in the end, the stack is cleared by the caller. This is the reason why passing more parameters than expected is thinked to be harmless. Anyway it still explicitely invokes UB, so a different compilation (different compiler or compilation options) could lead to a very different behaviour. And it also explains why passing less parameters than expected just let initialized values (the current content of the stack) for the missing parameters.

But what you should remember is only that passing a wrong number of parameters or using wrong parameter types is explicitely Undefined Behaviour, even if the absence of a definition can prevent the compiler to raise an error.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252