function prototype vs function declaration

C

CBFalconer

Ravishankar said:
.... snip ...


Please recommend me a good book, other than K&R which covers
these points. Thank you.

Please do not snip attributions for material your quote.
Attributions are the "joe wrote:" initial lines.

I recommend reading the C standard, which should keep you out of
mischief for a while. You can get a text version of the last draft
of C99 (small, compressed with bzip2) at:

<http://cbfalconer.home.att.net/download/N869_txt.bz2>

or a more recent, and much larger (factor of 15), draft at:

<http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1256.pdf>

There are only small detail differences between the two, of little
importance.
 
G

George Peter Staplin

Ravishankar said:
Point taken. Though this not of the same stature are of writing "void main"
and "fflush(stdin)".


Please recommend me a good book, other than K&R which covers these points.
Thank you.

If I am not mistaken a void* must be capabale of storing any pointer
(generic pointer) including
a pointer to a function...

Unfortunately that's not true, and there is a correction for K&R
regarding that. I have a printing of the 2nd edition with that mistake.

"On page 199, beginning of §A6.8, ``Any pointer may be converted to type
void *...'' is changed to ``Any pointer to an object may be converted to
type void *...''." --
http://cm.bell-labs.com/cm/cs/cbook/2ediffs.html

While the C standard is limited in that way, the fact is most popular
systems *do* store function pointers in void *. POSIX is one example,
with regard to the way dlsym works(), and Windows is another.

C isn't very useful without other standards, and some of them conflict.
POSIX for instance makes certain restrictions for pthreads that limit
the optimizations that some compilers may do for C.


George
 
K

Keith Thompson

Richard Heathfield said:
James Kuyper said:

Brace yourself, then, as you read pp93, 103, 120 and 199, none of which
make it clear that function pointers are not included in the void * magic.
The closest approach is in the tiny-type text on page 199, which
incorrectly singles out object pointers (and thus ignores incomplete
pointers).

The errata page, at
<http://cm.bell-labs.com/cm/cs/cbook/2ediffs.html>, says:

On page 199, beginning of <section>A6.8, ``Any pointer may be
converted to type void *...'' is changed to ``Any pointer _to an
object_ may be converted to type void *...''.

I don't have my copy of K&R2 handy; it sounds like your copy has that
(incomplete) erratum already incorporated.
 
K

Keith Thompson

santosh said:
Ravishankar S wrote: [...]
If I am not mistaken a void* must be capabale of storing any pointer
(generic pointer) including a pointer to a function...

No. A void pointer can only store pointers to object and incomplete
types. Conversion between function and void pointer types is undefined.
Also there is no "generic" function pointer type.

There is no signle "generic" function pointer type. On the other
hand, *any* function pointer type (such as void (*)(void)) may be used
as a generic function pointer type. (It's generic in the sense that
you can losslessly convert any function pointer to and from it; unlike
with void*, such conversions require explicit casts.)
 
K

Keith Thompson

Ravishankar S said:
Dear C Experts,
While prepating a content for a C course,I made section on function
prototypes.
Could you kindly provide me your comments on its correctness. Thank you !

Q12: What is the difference between a function prototype and a function
declaration?
[snip]

C99 6.2.1p2:

A _function prototype_ is a declaration of a function that
declares the types of its parameters.
 
R

Richard Heathfield

Keith Thompson said:

The errata page, at
<http://cm.bell-labs.com/cm/cs/cbook/2ediffs.html>, says:

On page 199, beginning of <section>A6.8, ``Any pointer may be
converted to type void *...'' is changed to ``Any pointer _to an
object_ may be converted to type void *...''.

I don't have my copy of K&R2 handy; it sounds like your copy has that
(incomplete) erratum already incorporated.

Actually, no, it doesn't. In my copy, the paragraph starts with the
unamended text you quote above:

"Any pointer may be converted to type void * without loss of information."
The paragraph continues with a few words about the properties of void *
that are irrelevant to this discussion, before going on to say, in an
indented small-font paragraph:

"This interpretation of void * pointers is new; previously,
char * pointers played the role of generic pointer. The ANSI
standard specifically blesses the meeting of void * pointers
with object pointers in assignments and relationals, while
requiring explicit casts for other pointer mixtures."

....which seems utterly to disregard incomplete types.
 
P

pete

Richard said:
Keith Thompson said:



Actually, no, it doesn't. In my copy, the paragraph starts with the
unamended text you quote above:

"Any pointer may be converted to type void *
without loss of information."
The paragraph continues with a few words about the properties of void *
that are irrelevant to this discussion, before going on to say, in an
indented small-font paragraph:

"This interpretation of void * pointers is new; previously,
char * pointers played the role of generic pointer. The ANSI
standard specifically blesses the meeting of void * pointers
with object pointers in assignments and relationals, while
requiring explicit casts for other pointer mixtures."

...which seems utterly to disregard incomplete types.

That depends on what you think "object pointers" means.


ISO/IEC 9899:1999 (E)

7.18.1.4 Integer types capable of holding object pointers

1 The following type designates a signed integer type
with the property that any valid pointer to void
can be converted to this type,
then converted back to pointer to void,
and the result will compare equal to the original pointer:
intptr_t
The following type designates an unsigned integer type
with the property that any valid pointer to void
can be converted to this type,
then converted back to pointer to void,
and the result will compare equal to the original pointer:
uintptr_t
These types are optional.

7.18.1.5 Greatest-width integer types
 
P

pete

pete said:
That depends on what you think "object pointers" means.

Sometime back,
I got it into my head that there are only two kinds of pointers:
1 object pointers
2 function pointers
and that pointers to incomplete types, were object pointers.

I'm not able to supply a definitive reference at this time.
 
J

James Kuyper

pete wrote:
....
Sometime back,
I got it into my head that there are only two kinds of pointers:
1 object pointers
2 function pointers
and that pointers to incomplete types, were object pointers.

I'm not able to supply a definitive reference at this time.

I can supply a definitive counter-reference - Section 6.2.5p1 says:

"... Types are partitioned into object types (types that fully describe
objects), function types (types that describe functions), and incomplete
types (types that describe objects but lack information needed to
determine their sizes)."

If the base types are so partitioned, then the same partitioning must
apply to types derived from the base types, including pointer types.
Since there's no overlap between object types and incomplete types,
there can be no overlap between pointers to object types and pointers to
incomplete types.
 
P

pete

James said:
pete wrote:
...

I can supply a definitive counter-reference - Section 6.2.5p1 says:

"... Types are partitioned into object types
(types that fully describe objects),
function types (types that describe functions),
and incomplete types
(types that describe objects but lack information needed to
determine their sizes)."

If the base types are so partitioned, then the same partitioning must
apply to types derived from the base types, including pointer types.
Since there's no overlap between object types and incomplete types,
there can be no overlap between pointers
to object types and pointers to
incomplete types.

None of what you wrote
prevents the definition of the term "object pointers"
from meaning collectively,
pointers to object types and pointers to incompletes types.

A pointer declared as a pointer to an incomplete type
in a source file,
can become a pointer to an object type in a translation unit,
and that doesn't invlove anything
that can be described as a type conversion.

If you consider new.c,
ptr is pointer to an incomplete type in the source file.
Depending on whether or not struct s is defined in in "inc.h"
ptr can either be a pointer to an object type in the translation unit,
or it can remain a pointer to an incomplete type.

/* BEGIN new.c */

#include <stdio.h>

#include "inc.h"

extern struct s;

int main(void)
{
struct s *ptr = NULL;

return 0;
}

/* END new.c */
 
J

James Kuyper

pete wrote:
....
None of what you wrote
prevents the definition of the term "object pointers"
from meaning collectively,
pointers to object types and pointers to incompletes types.

Agreed; I didn't argue against that possibility because I didn't see it
as a possible interpretation. Now that you've raised that possibility, I
still don't see it as a possible interpretation.
 
P

phao

Hi, i was reading this post and i'd like to know if is wrong doing
this:

/* begin of code */
datatype func_name(arglist) { ... }

.... list of functions implementations ...

int main(int argc, char **argv) { ... }
/* end of code */

suposing datatype is a valid datatype and arglist is a list of
arguments with valid datatypes and names, and func_name is a valid
name and { ... } for each function is a valid block of code.

is this valid? i mean, i do not define the function before
implementing it.

/*begin of code*/
type func_name(arglist);

.... main function implementation here ...

.... func_name implementation here ...
/*end of code */

i do not do this.

is that wrong? not doing this pre-definition of the function before
implementing it. is it wrong?
 
S

santosh

phao said:
Hi, i was reading this post and i'd like to know if is wrong doing
this:

/* begin of code */
datatype func_name(arglist) { ... }

... list of functions implementations ...

int main(int argc, char **argv) { ... }
/* end of code */

suposing datatype is a valid datatype and arglist is a list of
arguments with valid datatypes and names, and func_name is a valid
name and { ... } for each function is a valid block of code.

is this valid? i mean, i do not define the function before
implementing it.

/*begin of code*/
type func_name(arglist);

... main function implementation here ...

... func_name implementation here ...
/*end of code */

i do not do this.

is that wrong? not doing this pre-definition of the function before
implementing it. is it wrong?

Your compiler should emit a diagnostic for calling a function with no
prototype in scope. If it does not, please read your compiler's
documentation and supply the appropriate switches to make it do so.
Almost all compilers can diagnose this code feature.

A declaration must be in scope for all functions that you call, at or
before the point of call. For Standard library functions, you need to
include the appropriate headers. For your functions, you either need to
define them before using them, or provide a declaration before the
point of first use in a translation unit. This is commonly done by
placing a prototype declaration of the function near the top of the
translation unit, either directly, or via an header inclusion. Using
the prototype style is preferable over the obsolete K&R style
declaration which provides no parameter checking.
 
J

Joachim Schmitz

phao said:
Hi, i was reading this post and i'd like to know if is wrong doing
this:

/* begin of code */
datatype func_name(arglist) { ... }

... list of functions implementations ...

int main(int argc, char **argv) { ... }
/* end of code */

suposing datatype is a valid datatype and arglist is a list of
arguments with valid datatypes and names, and func_name is a valid
name and { ... } for each function is a valid block of code.

is this valid? i mean, i do not define the function before
implementing it.

/*begin of code*/
type func_name(arglist);

... main function implementation here ...

... func_name implementation here ...
/*end of code */

i do not do this.

is that wrong? not doing this pre-definition of the function before
implementing it. is it wrong?
It's not a matter of when you implement it, but when you use it.
You should declare it before first use. If you define/implement it before
the first use like in your first example, everything is fine, as the
definition/implementation contains the declaration. If you define/implement
it after first use, you'd need to declare it first. Well, for varadic
functions you'd have to (to prevent UB), for other you should (to prevent
warning and allow the compiler to do proper type checking).

Bye, Jojo
 
P

phao

ahh ok, suposing

'fa', 'fb' and 'main' as beeing 3 valid functions

if I implement fa before fb before main, in the 'global scope' then i
can call fa inside fb, call fa and fb inside main, but i cannot call
fb inside fa, because fb is implemented after fa, ok, understood.

the main factors here is the order and scope.
 
S

santosh

phao said:
ahh ok, suposing

'fa', 'fb' and 'main' as beeing 3 valid functions

if I implement fa before fb before main, in the 'global scope' then i
can call fa inside fb, call fa and fb inside main, but i cannot call
fb inside fa, because fb is implemented after fa, ok, understood.

the main factors here is the order and scope.

Yes. To get around this problem place the declarations for 'fa' and 'fb'
at the top of the source file.

/* main.c */

RETURN TYPE fa(PARAMETER LIST);
RETURN TYPE fb(PARAMETER LIST);

/* fa definition */
/* fb definition */
/* main definition */

/* or in any other order too */

If you need to call 'fa' and 'fb' from multiple files in your project
then place the declarations in a header and include that header in all
the necessary files.

/* fa_fb.h */
RETURN TYPE fa(PARAMETER LIST);
RETURN TYPE fb(PARAMETER LIST);

/* file1.c */
#include "fa_fb.h"
/* ... */

/* file2.c */
#include "fa_fb.h"
/* ... */

and so on.
 
R

Richard Heathfield

phao said:
ahh ok, suposing

'fa', 'fb' and 'main' as beeing 3 valid functions

if I implement fa before fb before main, in the 'global scope' then i
can call fa inside fb, call fa and fb inside main, but i cannot call
fb inside fa, because fb is implemented after fa, ok, understood.

the main factors here is the order and scope.


To free yourself from such foolish restrictions, you can declare functions
without (immediately) defining them. Here's an example which actually
requires a forward declaration of one of its functions:


#include <stdio.h>
#include <stdlib.h>

unsigned long bar(unsigned long n);
unsigned long foo(unsigned long n)
{
if(n % 2 == 0)
{
n /= 2;
}
else if(n != 1)
{
n = bar(n);
}
return n;
}

unsigned long bar(unsigned long n)
{
if(n % 2 == 1)
{
if(n > 1)
{
n *= 3;
++n;
}
}
else
{
n = foo(n);
}
return n;
}

int main(int argc, char **argv)
{
unsigned long c = argc > 1 ? strtoul(argv[1], NULL, 10) : 23;
if(c > 0)
{
do
{
c = foo(c);
printf("%lu\n", c);
}
while(c > 1);
}
return 0;
}
 
J

Joachim Schmitz

phao said:
ahh ok, suposing

'fa', 'fb' and 'main' as beeing 3 valid functions

if I implement fa before fb before main, in the 'global scope' then i
can call fa inside fb, call fa and fb inside main, but i cannot call
fb inside fa, because fb is implemented after fa, ok, understood.
You can, but in that case should have declared it first.
It isn't alway possible to define all funktions in the right order...
And if using different source files, it is even impossible.
the main factors here is the order and scope.
Yes

Bye, Jojo
 
C

Chris Torek

I can supply a definitive counter-reference - Section 6.2.5p1 says:

"... Types are partitioned into object types (types that fully describe
objects), function types (types that describe functions), and incomplete
types (types that describe objects but lack information needed to
determine their sizes)."

As an aside, most incomplete types can later be completed, turning
them into object types (a "function type" is never itself incomplete).
(The one special incomplete type that can never be completed is "void".)
If the base types are so partitioned, then the same partitioning must
apply to types derived from the base types, including pointer types.

Yes. However:
Since there's no overlap between object types and incomplete types,
there can be no overlap between pointers to object types and pointers to
incomplete types.

Suppose T is an incomplete type, e.g.:

typedef struct incomplete T;

Then a pointer to T is an object type, not an incomplete type:

typedef T *PT;

There is a simple demonstration of this fact, in that we can create
an array of such pointers:

PT arr[10];

while the Standard says that we can*not* create an array of incomplete
types -- and indeed, an attempt such as:

T arr2[10];

fails.

Hence, given some arbitrary type X, if we are subsequently told that
X is a name for a type that is also spelled "pointer to ...", then
-- regardless of what goes in the "..." part -- we know that X is not
an incomplete type. That means X must be one of an "object type" or
a "function type". Since X is not itself a function type, that
leaves only "object type" as a possibility.

None of this changes the objection to the K&R2 correction wording,
since they say "*pointer to* object type": they really need to say
"pointer to object or incomplete type". But Pete's observation is
correct if interpreted one way, since "pointer to incomplete type"
is itself an "object type". (I am not sure what he meant by the
phrase "object pointers" though.)
 
J

James Kuyper

Chris Torek wrote:
....
As an aside, most incomplete types can later be completed, turning
them into object types (a "function type" is never itself incomplete).
(The one special incomplete type that can never be completed is "void".)


Yes. However:


Suppose T is an incomplete type, e.g.:

typedef struct incomplete T;

Then a pointer to T is an object type, not an incomplete type:

O, certainly. I didn't mean to suggest that pointers inherited the
partition of the type they pointed at. Rather, I'm saying that derived
types have derived partitions. A pointer to an object type is never
simultaneously a pointer to an incomplete type. As you pointed out, an
incomplete type can be completed, but at any given point in the code it
is either incomplete or complete; it's never both at the same place in
the code.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,581
Members
45,057
Latest member
KetoBeezACVGummies

Latest Threads

Top