Zaiping Bie
Zaiping Bie

Reputation: 378

C Function with parameter without type indicator still works?

The code is as following:

int func(param111)
{
    printf("%d\n", param111);
    return param111;
}

int main()
{
    int bla0 = func(99);
    int bla1 = func(10,99);
    int bla2 = func(11111110,99,10001);
    printf("%d, %d, %d\n", bla0, bla1, bla2);
}

Compile result:

zbie@ubuntu:~$ gcc -Wall -g -std=c99 -O2 zeroparam.c

zeroparam.c: In function ‘func’:

zeroparam.c:2: warning: type of ‘param111’ defaults to ‘int’

Run result:

zbie@ubuntu:~$ ./a.out

99

10

11111110

99, 10, 11111110

I know the code should be ok if the func with zero parameters, such as int func() which will accept any inputs. But how does this code get compiled and ran successfully?

Upvotes: 23

Views: 9001

Answers (7)

Keith Thompson
Keith Thompson

Reputation: 263477

If you don't get any warnings for that code, it's because your compiler is not enforcing C99 rules (calling printf, or any function, without a visible declaration is a constraint violation). You can probably get at least some warnings by passing the right options to your compiler. If you're using gcc, try gcc -std=c99 -pedantic -Wall -Wextra.

So-called K&R C, the language described by the 1978 1st edition of Kernighan and Ritchie's classic book The C Programming Language, did not have function prototypes. (A prototype is a function declaration that specifies the types of its parameters.) A function definition still had to define its parameters (perhaps implicitly), but a declaration did not -- and typical compilers did not check for correct matching of arguments (in a function call) to parameters (in a function definition).

It wasn't entirely clear just what happens if you called a function with the wrong number and/or types of arguments. In modern terms, it was undefined behavior, but older compilers commonly let you play tricks.

The 1989 ANSI C standard (republished as the 1990 ISO C standard) introduced prototypes (borrowed from early C++), but did not require them. But it did state explicitly that calling a function with the wrong number or types of arguments causes undefined behavior; the compiler is not required to warn you about it, but the program can do quite literally anything when you run it.

The 1999 ISO C standard dropped the "implicit int" rule and made it illegal (a constraint violation) to call a function with no visible declaration -- but it still permitted old-style function declarations and definitions. So under K&R1 and C89/C90 rules, your function definition:

int func(param111)
{
    printf("%d\n", param111);
    return param111;
}

is valid, and param111 is of type int. Under C99 rules, it's invalid, but this:

int func(param111)
int param111;
{
    printf("%d\n", param111);
    return param111;
}

is still legal (and remains legal even under the 2011 standard).

As of C99 and C11, if you call a function whose visible declaration is not a prototype, it's entirely up to you to get the arguments right; the compiler isn't required to warn you if you get it wrong.

Which is why you should always use prototypes for all function declarations and definitions. The need to write code that compiles with pre-ANSI compilers is practically nonexistent these days; it's difficult to find a compiler that doesn't support at least C89/C90.

Oh, and you need to add

#include <stdio.h>

to the top of the source file because you're calling printf. Under C89/C90 rules, calling printf with no visible declaration has undefined behavior (because printf takes a variable number of arguments). Under C99 and later, it's a constraint violation, requiring a compile-time diagnostic.

I've been nit-picking about the missing parameter declaration. A slightly altered variant of your program:

#include <stdio.h> /* add this line */

int func(param111)
int param111;      /* add this line */
{
    printf("%d\n", param111);
    return param111;
}

int main(void)     /* add "void" */
{
    int bla0 = func(99);
    int bla1 = func(10,99);
    int bla2 = func(11111110,99,10001);
    printf("%d, %d, %d\n", bla0, bla1, bla2);
}

does not violate any rules that require a compile-time diagnostic in C90, C99, or C11 -- but the second and third calls to func have undefined behavior.

Note that the compiler actually has enough information to warn you that your calls to func are incorrect. It's just seen the definition of func, and it should know that any call that doesn't pass exactly 1 argument of a type that's implicitly convertible to int is invalid. No warning is required, but compilers can always print whatever extra warnings they like. Apparently the authors of gcc (and of whatever compiler you're using) felt that it wasn't worth the effort to warn about mismatched calls to functions with old-style declarations and/or definitions.

Upvotes: 1

Prof. Falken
Prof. Falken

Reputation: 24917

This behaviour is to provide backwards compatibility with older versions of the language, the K&R version of the language. When GCC encounters an "old style" function, it conforms to the old K&R C behaviour which implies no warnings in this situation.

Indeed, if you change the function to: int func(int param111), you do get the expected warnings:

x.c: In function ‘main’:
x.c:11:5: error: too many arguments to function ‘func’
x.c:2:5: note: declared here
x.c:12:5: error: too many arguments to function ‘func’
x.c:2:5: note: declared here
x.c:14:1: warning: control reaches end of non-void function [-Wreturn-type]

(Tested with GCC 4.7.3 and "gcc -std=c99 -Wall x.c && ./a.out")

Or to quote JeremyP from the comments: "In K&R C it was perfectly fine to call a function with as many arguments as you like, because the ellipsis notation wasn't invented then.".

Note that a compiler can show as many extra warnings it wants and still conform to the standard. For instance Apple's compiler warns about this code.

Upvotes: 21

X Zhang
X Zhang

Reputation: 307

The func function in your code only has a function definition but not a function declarator. In C99 6.5.2.2(Function Calls), it stats:

"No other conversions are performed implicitly; in particular, the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator."

When func(10,99) and func(11111110, 99, 10001) is called, the complier will not compare the number and the types of arguments with the parameters in function definition. You can even call it through func("abc"). However, if you add the following function declaration of func in your code:

int func(int);

(int fun(int) is declared because the C99 standard will implicitly promote para111 to int type), the compiler would send the following errors:

zeroparam.c: In function ‘main’:
zeroparam.c:15:13: error: too many arguments to function ‘func’
zeroparam.c:6:5: note: declared here
zeroparam.c:16:17: error: too many arguments to function ‘func’

BTW: I do not think this is an "K&R program" problem, as you explicitly specify "-std=c99" in your command.

Upvotes: 1

nneonneo
nneonneo

Reputation: 179552

The function declaration is being interpreted as a K&R style function declaration because it lacks types. In standardese, this is called a function declaration with an identifier list, as opposed to a parameter type list as in the usual declaration.

According to the C99 specification, 6.9.1/7, only function definitions with a parameter type list are considered to be function prototypes. The K&R style uses an identifier list instead, and so is not considered to have a prototype.

Function calls to functions without prototypes are not checked for parameter counts or types (per 6.5.2.2/8, "the number and types of arguments are not compared with those of the parameters in a function definition that does not include a function prototype declarator"). Thus, it is legal to call a function declared in the K&R style with any number and type of arguments, but per 6.5.2.2/9 a call with invalid types will produce undefined behaviour.

As an illustration, the following will compile without any warnings (on gcc -Wall -Wextra -pedantic -std=c99 -O):

#include <stdio.h>

void *func(param111)
char *param111;
{
    printf("%s\n", param111);
    return param111;
}

int main()
{
    void *bla0 = func();
    void *bla1 = func(99);
    void *bla2 = func(11111110,99);
    printf("%p, %p, %p\n", bla0, bla1, bla2);
    return 0;
}

despite obviously having incorrect parameter types and counts.

Upvotes: 10

Yu Hao
Yu Hao

Reputation: 122433

It's interpreted as K&R C, as others have explained. It's worth noting that it's undefined behavior in ANSI C:

C11 6.9.1 Function definitions Section 9

If a function that accepts a variable number of arguments is defined without a parameter type list that ends with the ellipsis notation, the behavior is undefined.

So a variable number arguments function has to end with ... as parameter, like printf:

int printf( const char *format ,...);

Upvotes: 2

Lyth
Lyth

Reputation: 2211

I can explain, why this works, but not why compiler does not warn about it.

There are a few calling conventions, which specify how arguments are ordered and where they are placed. C calling convention allows passing extra parameters without side effects, because caller cleans them up, not the function called, and they are all passed on stack:

For your case with func(10, 99), "main" pushes values onto stack in the following order (right-to-left):

99
10

"func" only knows about one value, and it takes them from the end, so param111 == 10.

Then "main", knowing that two arguments were pushed, retrieves them back, thus cleaning the stack.

Upvotes: 1

Some programmer dude
Some programmer dude

Reputation: 409356

If you check the warnings when compiling, you see this message:

zeroparam.c:2: warning: type of ‘param111’ defaults to ‘int’

That tells you that an argument without a type will by default by an integer. Just the same as defining a function without return type, it will default to int as well.

Upvotes: -2

Related Questions