394

I've just come across someone's C code that I'm confused as to why it is compiling. There are two points I don't understand.

  1. The function prototype has no parameters compared to the actual function definition.

  2. The parameter in the function definition does not have a type.


#include <stdio.h>

int func();

int func(param)
{
    return param;
}

int main()
{
    int bla = func(10);    
    printf("%d", bla);
}

Why does this work? I have tested it in a couple of compilers, and it works fine.

Federico Baù
  • 2,100
  • 3
  • 10
  • 20
AdmiralJonB
  • 1,896
  • 2
  • 18
  • 25
  • 78
    It's K&R C. We wrote code like this in the 1980s before there were full function prototypes. – hughdbrown Dec 19 '12 at 18:27
  • surprised there were no warnings.. – nycynik Dec 19 '12 at 19:09
  • 6
    gcc does warn with `-Wstrict-prototypes` for both the `int func()` and `int main()`: x.c:3: warning: function declaration isn't a prototype. You should declare `main()` as `main(void)` as well. – Jens Dec 19 '12 at 21:59
  • Kinda sleepy. But "function definition doesn't have a type". Where? – CppLearner Dec 19 '12 at 22:00
  • 3
    @Jens Why did you edit the question? You seem to have missed the point... – dlras2 Dec 19 '12 at 22:02
  • 1
    I just made the implicit int explicit. How does that miss the point? I believe the point is why `int func();` is compatible with `int func(arglist) { ... }`. – Jens Dec 19 '12 at 22:04
  • @Jens Part of the reason this code was so confusing (to me) was the implicit `int`. Also note the part of the question that says "Secondly, the parameter in the function definition doesn't have a type." – Phil Dec 19 '12 at 22:06
  • BTW, someone edited the question to become bogus: it says prototype when the point is that it is **NOT A PROTOTYPE**. – Jens Dec 19 '12 at 22:11
  • Technically, main(void) is not valid. It works in most compilers, but it's incorrect. it is either `int main(int argc, char **argv)` or `int main()` if you don't care about argc and argv. – Mats Petersson Dec 20 '12 at 18:11
  • 3
    @MatsPetersson This is wrong. C99 5.1.2.2.1 expressly contradicts your claim and states that the no argument version is `int main(void)`. – Jens Dec 20 '12 at 22:47
  • 1
    @Jens if you have a better title, edit the title. C is not my first, second, or even third language. It's a book on my shelf, sadly. – George Stocker Feb 02 '13 at 02:10

11 Answers11

273

All the other answers are correct, but just for completion

A function is declared in the following manner:

  return-type function-name(parameter-list,...) { body... }

return-type is the variable type that the function returns. This can not be an array type or a function type. If not given, then int is assumed.

function-name is the name of the function.

parameter-list is the list of parameters that the function takes separated by commas. If no parameters are given, then the function does not take any and should be defined with an empty set of parenthesis or with the keyword void. If no variable type is in front of a variable in the paramater list, then int is assumed. Arrays and functions are not passed to functions, but are automatically converted to pointers. If the list is terminated with an ellipsis (,...), then there is no set number of parameters. Note: the header stdarg.h can be used to access arguments when using an ellipsis.

And again for the sake of completeness. From C11 specification 6:11:6 (page: 179)

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

Krishnabhadra
  • 33,632
  • 30
  • 110
  • 164
  • 2
    ***"If no variable type is in front of a variable in the parameter list, then int is assumed."*** I see this in yours provided link, but I can not find it in any standard c89, c99... Can you provide another source? – godaygo Jan 22 '18 at 08:31
  • "If no parameters are given, then the function does not take any and should be defined with an empty set of parenthesis" sounds contradictory to [Tony The Lion's answer](https://stackoverflow.com/a/13950669/2828064) but I just learned that Tony The Lion's answer is correct the hard way. – jakun May 22 '20 at 16:17
161

In C func() means that you can pass any number of arguments. If you want no arguments then you have to declare as func(void). The type you're passing to your function, if not specified defaults to int.

Tony The Lion
  • 57,181
  • 57
  • 223
  • 390
  • 3
    Actually there's no single type defaulting to int. In fact, all args default to int (even the return type). It is perfectly ok to call `func(42,0x42);` (where *all* calls must use the two args form). – Jens Dec 19 '12 at 14:20
  • 1
    In C it is quite common for unspecified types to default to *int* like for example when you declare a variable: unsigned x; What type is the variable x? It turns out its *unsigned int* – Alex Bitek Dec 19 '12 at 22:32
  • 4
    I'd rather say implicit int **was** common in the olden days. It certainly isn't any longer and in C99 it was removed from C. – Jens Dec 20 '12 at 09:09
  • 2
    It may be worthwhile noting that this answer applies to function prototypes with empty parameter lists, but *not* function definitions with empty parameter lists. – autistic May 10 '13 at 19:19
  • @mnemonicflow that's not an "unspecified type defaulting to int"; `unsigned` is just another name for the same type as `unsigned int`. – hobbs Feb 08 '15 at 05:52
  • @hobbs Sure, but it's the same old reasoning used for function parameters, if no type is specified int is assumed. Just saying "unsigned" doesn't tell me anything about what is the actual type (e.g. is it unsigned short or unsigned int64?, etc.) – Alex Bitek Feb 08 '15 at 12:35
  • you can also declare a variable like `static x;`, `const x;` or `auto x;` and it'll default to `int` – osvein Oct 22 '17 at 18:55
58

int func(); is an obsolescent function declaration from the days when there was no C standard, i.e. the days of K&R C (before 1989, the year the first "ANSI C" standard was published).

Remember that there were no prototypes in K&R C and the keyword void was not yet invented. All you could do was to tell the compiler about the return type of a function. The empty parameter list in K&R C means "an unspecified but fixed" number of arguments. Fixed means that you must call the function with the same number of args each time (as opposed to a variadic function like printf, where the number and type can vary for each call).

Many compilers will diagnose this construct; in particular gcc -Wstrict-prototypes will tell you "function declaration isn't a prototype", which is spot on, because it looks like a prototype (especially if you are poisoned by C++!), but isn't. It's an old style K&R C return type declaration.

Rule of thumb: Never leave an empty parameter list declaration empty, use int func(void) to be specific. This turns the K&R return type declaration into a proper C89 prototype. Compilers are happy, developers are happy, static checkers are happy. Those mislead by^W^Wfond of C++ may cringe, though, because they need to type extra characters when they try to exercise their foreign language skills :-)

Jens
  • 61,963
  • 14
  • 104
  • 160
  • 1
    Please don't relate this to C++. It is *common sense* that empty argument list means *no parameters*, and people of the world are not interested to deal with mistakes made by K&R guys some 40 years ago. But committee pundits keep dragging contranatural choices along - into C99, C11. – pfalcon May 03 '14 at 23:17
  • 1
    @pfalcon *Common sense* is quite subjective. What's common sense to one person is plain insanity to others. Professional C programmers know that empty parameter lists indicate an unspecified but fixed number of arguments. Changing that would likely break many implementations. Blaming committees for avoiding silent changes is barking up the wrong tree, IMHO. – Jens May 04 '14 at 10:46
53
  • The empty parameter list means "any arguments", so the definition isn't wrong.
  • The missing type is assumed to be int.

I would consider any build that passes this to be lacking in configured warning/error level though, there's no point in being this allowing for actual code.

Jens
  • 61,963
  • 14
  • 104
  • 160
unwind
  • 364,555
  • 61
  • 449
  • 578
30

It's K&R style function declaration and definition. From C99 Standard (ISO/IEC 9899:TC3)

Section 6.7.5.3 Function Declarators (including prototypes)

An identifier list declares only the identifiers of the parameters of the function. An empty list in a function declarator that is part of a definition of that function specifies that the function has no parameters. The empty list in a function declarator that is not part of a definition of that function specifies that no information about the number or types of the parameters is supplied. (If both function types are "old style", parameter types are not compared.)

Section 6.11.6 Function declarators

The use of function declarators with empty parentheses (not prototype-format parameter type declarators) is an obsolescent feature.

Section 6.11.7 Function definitions

The use of function definitions with separate parameter identifier and declaration lists (not prototype-format parameter type and identifier declarators) is an obsolescent feature.

Which the old style means K&R style

Example:

Declaration: int old_style();

Definition:

int old_style(a, b)
    int a; 
    int b;
{
     /* something to do */
}
Jens
  • 61,963
  • 14
  • 104
  • 160
Lei Mou
  • 2,422
  • 1
  • 19
  • 28
15

C assumes int if no type is given on function return type and parameter list. Only for this rule following weird things are possible.

A function definition looks like this.

int func(int param) { /* body */}

If its a prototype you write

int func(int param);

In prototype you can only specify the type of parameters. Parameters' name is not mandatory. So

int func(int);

Also if you dont specify parameter type but name int is assumed as type.

int func(param);

If you go farther, following works too.

func();

Compiler assumes int func() when you write func(). But dont put func() inside a function body. That'll be a function call

Jens
  • 61,963
  • 14
  • 104
  • 160
Shiplu Mokaddim
  • 52,462
  • 12
  • 127
  • 180
11

As stated @Krishnabhadra, all previous responses from other users, have a correct interpretation, and I just want to make a more detailed analysis of some points.

In the Old-C as in ANSI-C the "untyped formal parameter", take the dimencion of your work register or instruction depth capability (shadow registers or instruction cumulative cycle), in an 8bit MPU, will be an int16, in a 16bit MPU and so will be an int16 an so on, in the case 64bit architectures may choose to compile options like: -m32.

Although it seems simpler implementation at high level, For pass multiple parameters, the work of the programmer in the control dimencion data type step, becomes more demanding.

In other cases, for some microprocessors architectures, the ANSI compilers customized, leveraged some of this old features to optimize the use of the code, forcing the location of these "untyped formal parameters" to work within or outside the work register, today you get almost the same with the use of "volatile" and "register".

But it should be noted that the most modern compilers, not make any distinction between the two types of parameters declaration.

Examples of a compilation with gcc under linux:

main.c

main2.c

main3.c  
In any case the statement of the prototype locally is of no use, because there is no call without parameters reference to this prototype will be remiss. If you use the system with "untyped formal parameter", for an external call, proceed to generate a declarative prototype data type.

Like this:

int myfunc(int param);
RTOSkit
  • 1,161
  • 1
  • 11
  • 23
  • 5
    I have taken the trouble to optimize images for that the size in bytes of the images, was the minimum possible. I think the pictures can get a quick view of the lack of difference in the generated code. I've been testing out the code, generating real documentation of what it claimed and not just theorizing about the problem. but not all see the world the same way. I'm terribly sorry that my attempt to provide more information to the community to have bothered you so much. – RTOSkit Dec 20 '12 at 13:13
  • 1
    I'm pretty certain that an unspecified return type is always `int`, which is generally either the work-register size or 16 bits, whichever is smaller. – supercat Jul 12 '13 at 18:17
  • @supercat +1 Perfect deduction, you are right! This is exactly what I discuss above, a compiler designed by default for a specifies architecture , always reflect the size of the work-register of the CPU / MPU in question, So in an embedded enviroment, there are various re-typing strategy on a realtime OS, into a compiler layer (stdint.h), or into a portability layer, that makes portable the same OS in many others arquitectures, and obtained by alignment the CPU / MPU specific types (int, long, long long, etc) with the generic system types u8, u16, u32. – RTOSkit Jul 12 '13 at 23:18
  • @supercat Without control types offered by an operating system or by specific compiler, you need have a bit attencion in development time, and, you make that all "untyped assignments" are align with your application design, before have the surprise of finding a "16bit int" and not a "32bit int". – RTOSkit Jul 12 '13 at 23:39
  • @RTOSkit: Your answer suggests that the default type on an 8-bit processor will be an Int8. I do recall a couple C-ish compilers for the PICmicro brand architecture where that was the case, but I don't think anything remotely resembling a C standard has ever allowed an `int` type which was not capable both of holding all values in the range -32767 to +32767 (note -32768 is not required) and also capable of holding all `char` values (meaning that if `char` is 16 bits, either it must be signed or `int` must be bigger). – supercat Jul 13 '13 at 03:07
  • @supercat ahh! ok now I understand your comment, excellent observation! it is true all standard compilers guarantee for an int 16bits and there customizations where you might find a int8 as datatype, also in 8bits data micros with 14,16,18bits instructions is guaranteed a 16-bit integer. I modified the answer thanks for taking the time to comment. – RTOSkit Jul 13 '13 at 04:37
5

Regarding parameter type, there are already correct answers here but if you want to hear it from the compiler you can try adding some flags (flags are almost always a good idea anyways).

compiling your program using gcc foo.c -Wextra I get:

foo.c: In function ‘func’:
foo.c:5:5: warning: type of ‘param’ defaults to ‘int’ [-Wmissing-parameter-type]

strangely -Wextra doesn't catch this for clang (it doesn't recognize -Wmissing-parameter-type for some reason, maybe for historical ones mentioned above) but -pedantic does:

foo.c:5:10: warning: parameter 'param' was not declared, 
defaulting to type 'int' [-pedantic]
int func(param)
         ^
1 warning generated.

And for prototype issue as said again above int func() refers to arbitrary parameters unless you exclicitly define it as int func(void) which would then give you the errors as expected:

foo.c: In function ‘func’:
foo.c:6:1: error: number of arguments doesn’t match prototype
foo.c:3:5: error: prototype declaration
foo.c: In function ‘main’:
foo.c:12:5: error: too many arguments to function ‘func’
foo.c:5:5: note: declared here

or in clang as:

foo.c:5:5: error: conflicting types for 'func'
int func(param)
    ^
foo.c:3:5: note: previous declaration is here
int func(void);
    ^
foo.c:12:20: error: too many arguments to function call, expected 0, have 1
    int bla = func(10);
              ~~~~ ^~
foo.c:3:1: note: 'func' declared here
int func(void);
^
2 errors generated.
none
  • 10,753
  • 9
  • 46
  • 81
3

If the function declaration has no parameters i.e. empty then it is taking unspecified number of arguments. If you want to make it take no arguments then change it to:

int func(void);
P.P
  • 106,931
  • 18
  • 154
  • 210
  • 1
    _"If the function prototype has no parameters"_ That's not a function prototype, just a function declaration. – effeffe Dec 19 '12 at 12:10
  • @effeffe fixed that. I didn't realize this question would get a lot of attention ;) – P.P Dec 19 '12 at 12:48
  • 3
    Isn't "function prototype" just an alternate (possibly old-fashioned) expression for "function declaration"? – Giorgio Dec 19 '12 at 14:25
  • @Giorgio IMO, both are correct. "function prototype" should match "function definition". I probably think effeffe meant declaration in the question and what I meant is in my answer. – P.P Dec 19 '12 at 15:15
  • @Giorgio no, function declaration are made by the return type value, the identifier and an _optional_ parameter list; function prototypes are function declaration with a parameter list. – effeffe Dec 20 '12 at 01:16
  • @KingsIndian function prototypes and function definitions are unrelated, a definition is made by a declaration and a body for the function, but there could be a prototype without a definition and a definition without a prototype. But we're talking about poor practice. – effeffe Dec 20 '12 at 01:19
  • @effeffe For example, `void func(int);` can considered as both declaration in the old forms that's what giorgio says, which is mentioned in the quoted standard. – P.P Dec 20 '12 at 07:44
  • @KingsIndian If you're talking about old K&R C, I just don't know how things are named there. :) – effeffe Dec 20 '12 at 11:04
  • @effeffe Function prototypes were not there in K&R C. The "protypes" you say were added only after C89. – P.P Dec 20 '12 at 11:08
  • @KingsIndian Yes I knew that, I just don't know if people used to call things differently before standardization process. However, I think it doesn't make much sense to talk about these details without a standardization. Thanks however for pointing out that things used to be different. – effeffe Dec 20 '12 at 11:25
0

This is why I typically advise people to compile their code with:

cc -Wmissing-variable-declarations -Wstrict-variable-declarations -Wold-style-definition

These flags enforce a couple of things:

  • -Wmissing-variable-declarations: It is impossible to declare a non-static function without getting a prototype first. This makes it more likely that a prototype in a header file matches with the actual definition. Alternatively, it enforces that you add the static keyword to functions that don't need to be visible publicly.
  • -Wstrict-variable-declarations: The prototype must properly list the arguments.
  • -Wold-style-definition: The function definition itself must also properly list the arguments.

These flags are also used by default in a lot of Open Source projects. For example, FreeBSD has these flags enabled when building with WARNS=6 in your Makefile.

Ed Schouten
  • 396
  • 1
  • 4
0

In the old-style declarator,

the identifier list must be absent unless the declarator is used in the head of a function definition (Par.A.10.1). No information about the types of the parameters is supplied by the declaration. For example, the declaration

int f(), *fpi(), (*pfi)();

declares a function f returning an integer, a function fpi returning a pointer to an integer, >and a pointer pfi to a function returning an integer. In none of these are the parameter types >specified; they are old-style.

In the new-style declaration

int strcpy(char *dest, const char *source), rand(void);

strcpy is a function returning int, with two arguments, the first a character pointer, and the second a pointer to constant characters

SOURCE:- K&R book

I hope it cleared your doubt..