[comp.lang.c] Prototypes

ark@alice.UUCP (03/20/88)

Here is one sensible way to deal with prototypes:

Suppose you are writing a function to be used by others.
Let's call it foo().  Declare it in foo.h:

	extern double foo (int, char *, long);

When you write foo.c, be sure to include foo.h:

	#include "foo.h"

	double foo (int n, char *p, long size)
	{
		/* stuff */
	}

Indeed, you have stated foo's signature twice.
However, the compiler should reject an attempt to
compile foo.c if the two instances do not match.

Now a user who includes foo.h will automatically
get the right declaration.

henry@utzoo.uucp (Henry Spencer) (03/27/88)

> ... Declare it in foo.h... When you write foo.c, be sure to include foo.h...
> Indeed, you have stated foo's signature twice.
> However, the compiler should reject an attempt to
> compile foo.c if the two instances do not match.

There is still one headache with this, a more general flaw of foo.h schemes:
significant information is still present in two different files, and keeping
them in step can be a headache (even if failures are detected at compile
time).  I've been experimenting with a simple solution to this that works
moderately well.  It's a variant on the idea of automatically generating
function prototypes from the function code (which isn't simple to do and
doesn't address the other things that one might want to put in a header).
The idea is to imbed the foo.h information in foo.c, marked so that it can
be extracted automatically.  This still means you have to *write* it twice,
but the two instances can be *together*, which makes synchronized changes
much easier.

The particular marking convention I use is based on an extension of another
notation I already use:  beginning all lines of a multi-line comment with
" *" except for a line in the comment at the head of each function describing
the function, which begins with " -".  (This isn't original, although I no
longer remember exactly who gave me the idea.)  So I use "=" to mark a line
meant for foo.h.  This may be obscure without an example, so here's how I
might start an implementation of strcpy() (ignore the leading tabs):

	/*
	 - strcpy - copy string from b to a
	 = extern char *strcpy(char *a, char *b);
	 *
	 * Using algorithm XYZ to run fast on machine ABC.
	 */
	char *
	strcpy(a, b)
	char *a;
	char *b;
	{
		/* ... */

A simple "egrep '^ -'" will give me the "definition" lines for all the
functions, and a slightly more complex bit of code (see below) will build
a suitable header file.  Note that I can put *anything* into the header
file this way, not just function prototypes.  Note also that I can write
ANSI prototypes even for a non-ANSI compiler; my header-file builder takes
an "old compiler" option that comments out the parameter list.  (It also
turns C++-style // comments in the = lines into C comments, because otherwise
it's hard to get C comments onto such lines.)  Without further ado, here's
what I call "mkh"; I recommend it.  (Oh, ignore the -p option, it's still
experimental.)

-----------
X# mkh - pull headers out of C source
XPATH=/bin:/usr/bin ; export PATH
X
Xpeel='	/^ ==*[ 	]/	/\/\//s;//\(.*\);/*\1 */;
X	s/^ ==*[ 	]//'
X
Xegrep='^ =[ 	]'
X
Xfiles=
Xfor a
Xdo
X	case "$a"
X	in
X		-o)		# old (pre-function-prototype) compiler
X		peel='/^ ==*[ 	][^#]/{	/^\([^\/]*[a-zA-Z0-9_)]\)(\(.*\))/s;;\1(/*\2*/);
X			}
X			'"$peel"
X		echo '#ifdef __STDC__'
X		echo '#error "header file prepared with mkh -o"'
X		echo '#endif'
X		;;
X
X		-p)		# include private declarations
X		egrep='^ ==*[ 	]'
X		;;
X
X		*)
X		files="$files $a"
X		;;
X	esac
Xdone
X
Xfor f in $files
Xdo
X	egrep "$egrep" $f | sed "$peel"
Xdone
-----------
-- 
"Noalias must go.  This is           |  Henry Spencer @ U of Toronto Zoology
non-negotiable."  --DMR              | {allegra,ihnp4,decvax,utai}!utzoo!henry

rang@cpsin3.cps.msu.edu (Anton Rang) (04/29/89)

Is it possible to declare a prototype with an "optional" final
argument?  I'm trying to do a prototype for BSD's "open", which takes
either 2 or 3 arguments.  At the moment, I have:

	extern int open(char *, int, ...);

Is there any way to limit this to only take 2 or 3 arguments (as
opposed to 2 or more)?

+---------------------------+------------------------+-------------------+
| Anton Rang (grad student) | "VMS Forever!"         | VOTE on	         |
| Michigan State University | rang@cpswh.cps.msu.edu | rec.music.newage! |
+---------------------------+------------------------+-------------------+
| Send votes for/against rec.music.newage to "rang@cpswh.cps.msu.edu".   |
+---------------------------+------------------------+-------------------+

karl@haddock.ima.isc.com (Karl Heuer) (04/30/89)

In article <2775@cps3xx.UUCP> rang@cpswh.cps.msu.edu (Anton Rang) writes:
>I'm trying to do a prototype for BSD's "open"

BSD?  I thought AT&T was to blame for that mistake.

>	extern int open(char *, int, ...);
>Is there any way to limit this to only take 2 or 3 arguments (as
>opposed to 2 or more)?

No.  The prototype you gave is the best possible under the circumstances.
(Except that the first arg should be declared with "const".)

Karl W. Z. Heuer (ima!haddock!karl or karl@haddock.isc.com), The Walking Lint

rdw2030@venus.tamu.edu (06/29/90)

With all this talk about prototypes lately, I must admit... though I've been
programming in C for years, I've never understood why they exist!  Programs
run without them!

My question... why prototypes?  I know they are part of the ANSI standard, but
what is their purpose!?

Mark C. Lowe - KB5III

henry@zoo.toronto.edu (Henry Spencer) (07/08/90)

In article <55550@lanl.gov> rdw2030@venus.tamu.edu writes:
>... I've never understood why they exist!  Programs run without them!
>My question... why prototypes? ...

They make it possible for the compiler to do type checking that formerly
had to be done by the programmer or lint (both error-prone, the former
because of human frailty and the latter because too many people either
don't have it or won't use it).

They also give the compiler more information about how functions are
called, which permits optimized calling sequences of various kinds.
Notably, it is no longer necessary for the compiler to assume that
any function might be a varargs function, so non-varargs calls need
not be constrained by the tricky requirements of varargs.  Also of
note for the numerical community is the ability to pass floats as
floats and not have them widened to double, which can be costly.
-- 
"Either NFS must be scrapped or NFS    | Henry Spencer at U of Toronto Zoology
must be changed."  -John K. Ousterhout |  henry@zoo.toronto.edu   utzoo!henry

david@csource.oz.au (david nugent) (07/09/90)

In <55550@lanl.gov> rdw2030@venus.tamu.edu writes:

>With all this talk about prototypes lately, I must admit... though I've been
>programming in C for years, I've never understood why they exist!  Programs
>run without them!

Sure.
     

>My question... why prototypes?  I know they are part of the ANSI standard, but
>what is their purpose!?

Simple - type checking, and the benefit of implied type casting.  The latter
is particularly good in a memory segment (ugh!) environment, and takes care
of near/far pointer conversion.

By using prototypes, you lessen the need to ever need to use LINT; for basic
maintenance, anyway.  They've saved me many hours of frustrating bug-busting
by allowing the compiler to check types passed as parameters.

Converting from a situation where you use none to using prototypes should
a) be annoying as hell, and b) be rewarding in the long run.


david

-- 
_______________________________________________________________________________
 Unique Computing Pty Ltd  Melbourne  Australia  -  Communications Specialists 
        david@csource.oz.au    3:632/348@fidonet    28:4100/1@signet           

stever@Octopus.COM (Steve Resnick ) (07/10/90)

In article <559@csource.oz.au> david@csource.oz.au (david nugent) writes:
>In <55550@lanl.gov> rdw2030@venus.tamu.edu writes:
>
>>With all this talk about prototypes lately, I must admit... though I've been
>>programming in C for years, I've never understood why they exist!  Programs
>>run without them!
>
>>what is their purpose!?
>

[I know this is referenced wrong but I missed the original]

There are a few "ansi" compilers around which are "broken" without prototypes.

In Turbo C, for instance, calling malloc without the prototype doesn't work
in large memory model (eg returns a 32bit pointer) becuase it (correctly) 
assumes that the function returns an int. (I found this out by getting my
pointer value returned with the upper 16 bits set to zero and pointing into
the interrupt vector table on my PC - real nice "feature" (-8). Turbo C
has also made assumptions about function arguments being ints as well. I have
also encountered this on Microsoft C. The bottom line is that when you provide
your compiler with a prototype, the compiler shouldn't make any assumptions
about what's being passed or returned and you get much better error checking.
(Even though it's a pain in the arse to get used to doing at first!)

There are a few compilers which will generate the prototypes for you, too!
(Microsoft for instance) 

Adding my $.02 to the pot, now deal the cards! :)
Steve


-- 
--------------------------------------------------------------------------------
Steve Resnick -<stever@octopus.COM apple!octopus!stever sun!vsi1!octopus!stever>
408/241-1533 Process Scientific, Inc.
"0x2B|~0x2B THAT is the question!"

gordon@osiris.cso.uiuc.edu (John Gordon) (07/11/90)

	The purpose of a prototype is to explicitly declare what type the 
function returns (if any) and what types the function takes as args (if
any).  This makes it easier to detect errors involving incorrect returns
and arg passing to and from functions.


---
John Gordon
Internet: gordon@osiris.cso.uiuc.edu        #include <disclaimer.h>
          gordon@cerl.cecer.army.mil       #include <clever_saying.h>
GEnie:    j.gordon14                  

peter@msinc.msi.com (Peter Blemel) (08/24/90)

It has been noted that the linker *should* resolve symbols to local code before
resolving them from a library. Two problems arise from this assumption in my
personal experience:

1) 	I decided to rewrite a function, but I changed the order of the arguments:
	If the system I am porting to has ansi prototypes the compiler will bomb
	on the code declaring that my arguments are incorrect.
	
	I had used index as a global int within a file (properly declaring it, and
	referencing it), but when I tried to port the code to a machine with 
	prototypes, the compiler choked on my use of index because of the
	prototype definition, effectively making index a reserved word. Should
	functions in the standard c library be forced as reserved words?

2)	In an even worse oversight, Apollo has implimented shared libraries
	including the lex library libl.a. My application needed a very large
	yytext[] array, so I declared it myself and the Sun/VAX/IBM linkers
	correctly used my array because I do not include -ll in the arguments
	to ld. The Apollo linker automagically loads all of the shared libraries,
	so that even though I do *not* specify -ll at link time, it is still
	loaded and a multiply defined symbol yytext is produced. My code (under
	these conditions) can not be ported without a rewrite becuase I need the
	expanded yytext buffer.

Peter Blemel
----------------
unmvax.cs.unm.edu!bbx!yenta!msinc!peter