Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

why it is allowed to pass insufficient number of parameters when calling a function in C?

I know that function prototyping is mandatory in C++ if the function is defined after the main() function, but it is optional (but recommended) in C. I recently wrote a simple program that performs addition of 2 numbers, but by mistake used the dot operator instead of a comma when passing arguments.

#include <stdio.h>
int main()
{
    printf("sum is: %d",add(15.30)); // oops, uses dot instead of comma
}

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

In the above program if the add(int,int) function is defined before the main() function then the program will surely fail to compile. This is because fewer parameters are passed when calling the function than what is required.

But, why does the above program compile and run fine - giving some large garbage value as an output? What is the reason? Is it better to use function prototyping so that the compiler detects the type mismatch and any other errors associated with function invocation?

Is this undefined behavior?

like image 237
Destructor Avatar asked May 28 '15 16:05

Destructor


People also ask

How many parameters a function can pass in C?

Neither the C nor C++ standard places an absolute requirement on the number of arguments/parameters you must be able to pass when calling a function, but the C standard suggests that an implementation should support at least 127 parameters/arguments (§5.2.

How many parameters can be passed to function?

Except for functions with variable-length argument lists, the number of arguments in a function call must be the same as the number of parameters in the function definition. This number can be zero. The maximum number of arguments (and corresponding parameters) is 253 for a single function.

What should be pass in parameters when function does not require any parameters?

9. What should be passed in parameters when function does not require any parameters? Explanation: When we does not want to pass any argument to a function then we leave the parameters blank i.e. func() – function without any parameter. 10.

Can a function have too many parameters?

You can define as many parameters as you may need, but too many of them will make your routine difficult to understand and maintain. Of course, you could use a structured variable as a workaround: putting all those variables in a single struct and passing it to the routine.


2 Answers

In the earliest days of C, when methods looked like:

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

given int x; a statement add(3,5); would generate code like:

push 5
push 3
call _add
store r0,_x

The compiler would not need to know anything about the add method beyond the return type (which it could guess as int) in order to generate the above code; if the method turned out to exist, that would be discovered at link time. Code which supplied more parameters than a routine expected would push those values on the stack where they would be ignored until the code popped them some time later.

Problems would arise if code passed too few parameters, parameters of the wrong types, or both. If code passed a fewer parameters than a routine was expecting, this would often have no effect if the called code didn't happen to use the later parameters. If code read parameters that weren't passed, they would generally read 'garbage' values (though there's no guarantee that the attempt to read those variables wouldn't have other side-effects). On many platforms, the first non-passed parameter would be the return address, though not always. If code wrote to parameters that weren't passed, that would generally mess up the return stack and lead to weird goofy behavior.

Passing incorrect-type parameters would often cause the called function to look in the wrong place for its arguments; if the passed types were smaller than what was expected, that could cause the called method to access stack variables it didn't own (as with the case of passing too few parameters).

ANSI C standardized the use of function prototypes; if the compiler sees a definition or declaration like int add(int a, int b) before it sees a call to add, then it will ensure that it passes two arguments of type int, even if the caller supplies something else (e.g. if the caller passes a double, its value will be coerced to type int before the call). An attempt to pass too few parameters to a method the compiler has seen will result in an error.

In the code above, the call to add is made before the compiler has any clue what the method is expecting. While newer dialects would not allow such usage, older ones would have the compiler assume by default that add is a method that will take whatever arguments the caller supplies and return int. In the above case, the called code would likely be expecting two words to be pushed on the stack, but the double constant would probably be pushed as two or four words. Thus, the method would probably receive for a and b two words from the binary representation of the double value 15.3.

BTW, even compilers which do accept the indicated syntax will almost always squawk if a function whose type was implicitly assumed is declared as returning something other than int; many will also squawk if such a function is defined using anything other than old-style syntax. While the first C compilers always pushed function arguments on the stack, newer ones generally expect them in registers. Thus, if one were to declare

 /* Old-style declaration for function that accepts whatever it's given
    and returns double */

double add();

then the code add(12.3,4.56) would push two double values on the stack and call the add function, but if one had instead declared:

double add(double a, double b);

then on many systems the code add(12.3,4.56) would load floating-point register 0 with 12.3 and floating-point register 1 with 4.56 before the call (not pushing anything). As a consequence, the two styles of declaration are not compatible. On such systems, attempting to call a method without declaring it, and subsequently declaring it within the same compilation unit using new-style syntax will yield a compilation error. Further, some such systems prefix the names of functions that take register arguments with two underscores and those that don't with one underscore, so if the method was defined using new-style syntax but called without the compiler having seen a prototype, the compiler would try to call _add even though the function was called __add. This would cause the linker to reject the program rather than generating an executable that wouldn't work.

like image 179
supercat Avatar answered Nov 13 '22 14:11

supercat


If C sees you calling a function that hasn't been declared yet, it will generate an implicit declaration, taking any number of arguments, and assuming a return of int. In your example it is creating the implicit declaration of int add(); This is wrong, of course.

At runtime, the add function will receive a single double on its stack, and try to interpret the bytes in a confusing and undefined way.

C99 changed this rule and forbids calling undeclared functions, but most compilers still let you do it, albeit with a warning.

The printf is irrelevant.

like image 21
Ryan Haining Avatar answered Nov 13 '22 15:11

Ryan Haining