[comp.lang.c] how do I initialize a function pointer to be NULL ?

wei@tiger.Princeton.EDU (P Wei) (09/11/87)

If p is declared as :
struct x
{ int (*p)(); };
Now I want to initialize a variable y which is a structure x.
struct x y = { NULLFUNC };
The question is what should I put into the #define NULLFUNC .... ?
(I am using Microsoft C 4.0). Thanks for your help.
HP  Wei     princeton!tiger!wei

guy@sun.uucp (Guy Harris) (09/11/87)

> If p is declared as :
> struct x
> { int (*p)(); };
> Now I want to initialize a variable y which is a structure x.
> struct x y = { NULLFUNC };
> The question is what should I put into the #define NULLFUNC .... ?
> (I am using Microsoft C 4.0). Thanks for your help.

Unless Microsoft C 4.0 is badly broken, "#define NULLFUNC 0" should suffice;
the compiler knows enough to coerce the "0" to a pointer of the appropriate
type, and should do so in this context if it claims to implement C.  NULL
should also suffice, if it is defined as 0 or (sigh) 0L.

If you want to pass a null pointer of the same type as the member of "x" to a
function, and there is no prototype for that function in scope, you have to
use a cast, as the compiler *doesn't* know enough to coerce "0" to a pointer
of the appropriate type in that context.  In that case, the properly-cast
pointer is "(int (*)())0".
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

platt@emory.UUCP (09/12/87)

In article <27742@sun.uucp> guy@sun.uucp (Guy Harris) writes:
>> If p is declared as :
>> struct x
>> { int (*p)(); };
>> Now I want to initialize a variable y which is a structure x.
>> struct x y = { NULLFUNC };
>> The question is what should I put into the #define NULLFUNC .... ?
>> (I am using Microsoft C 4.0). Thanks for your help.
>
>Unless Microsoft C 4.0 is badly broken, "#define NULLFUNC 0" should suffice;
>the compiler knows enough to coerce the "0" to a pointer of the appropriate
>type, and should do so in this context if it claims to implement C.  NULL
>should also suffice, if it is defined as 0 or (sigh) 0L.
>-- 
>	Guy Harris
>	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
>	guy@sun.com (or guy@sun.arpa)

Actually, depending on the model (the IBM machines have a segmented
addressing scheme), NULL is defined as 0 if the memory is in small or
medium models, and is defined as 0L in compact and large models.  
Since a module may need to be compiled several ways, rather than 
checking the compile flags in the preprocessor, it's easiest to 
just define it as #define NULLFUNC NULL, and including either
<stdio.h> or <stdlib.h> (either having NULL defined) before the other
define.

As far as the function is concerned, it doesn't care what kind of
pointer is passed to it; it just passes an address.  The declaration
in the function where the routine is passed defines what the 
function is expected to do (whether the address is to be read/write, or
called). 

Dan

guy@sun.uucp (Guy Harris) (09/13/87)

> Actually, depending on the model (the IBM machines have a segmented
> addressing scheme), NULL is defined as 0 if the memory is in small or
> medium models, and is defined as 0L in compact and large models.  
> Since a module may need to be compiled several ways,

The model in which the module is compiled is *completely irrelevant* in this
case, unless the compiler is broken.  The compiler is required to coerce 0
(yes, a 16-bit 0) into a large-model function pointer in a context where this
is called for by the language specification; an initialization counts as such
a context.
-- 
	Guy Harris
	{ihnp4, decvax, seismo, decwrl, ...}!sun!guy
	guy@sun.com (or guy@sun.arpa)

greg@utcsri.UUCP (09/13/87)

In article <2236@emory.uucp> platt@emory.UUCP (Dan Platt) writes:
>In article <27742@sun.uucp> guy@sun.uucp (Guy Harris) writes:
>>> If p is declared as :
>>> struct x
>>> { int (*p)(); };
>>> Now I want to initialize a variable y which is a structure x.
>>> struct x y = { NULLFUNC };
>>> The question is what should I put into the #define NhULLFUNC .... ?
>>> (I am using Microsoft C 4.0). Thanks for your help.#
>>
>>Unless Microsoft C 4.0 is badly broken, "#define NULLFUNC 0" should suffice;
>>the compiler knows enough to coerce the "0" to a pointer of the appropriate
>>type, and should do so in this context if it claims to implement C.  NULL
>>should also suffice, if it is defined as 0 or (sigh) 0L.
>>-- 
>>	Guy Harris
>
>Actually, depending on the model (the IBM machines have a segmented
>addressing scheme), NULL is defined as 0 if the memory is in small or
>medium models, and is defined as 0L in compact and large models.  

Unfortunately for this 0L rubbish, function pointers differ in size from
data pointers in compact and medium models. Thus it trips over its own feet,
and is guaranteed to give you the wrong result for say,
function_expecting_possibly_null_function_pointer( NULL );

>Since a module may need to be compiled several ways, rather than 
>checking the compile flags in the preprocessor, it's easiest to 
>just define it as #define NULLFUNC NULL, and including either
><stdio.h> or <stdlib.h> (either having NULL defined) before the other
>define.
( you don't need NULL defined when you #define SOMETHING NULL, by the way).

It's best to use 0 and then cast that where appropriate (i.e. in function
call parameters). Writing code that compiles under various memory muddles
is really no different from writing code that compiles for various
machines. We got along fine with #define NULL 0 for all kinds of machines.

If a function prototype is available, the cast for a NULL function
parameter is unnecessary, but I leave them in, at least until I feel
I will always be able to get an ANSI compiler.
I find it immensely useful to make the Turbo C compiler
warn me if a function call is made with no prototype. I wish it could
also warn me if the prototype is causing a cast that would not be
made without the prototype ( as in Root2 = sqrt(2), which works with
a prototype but not without ).

>
>As far as the function is concerned, it doesn't care what kind of
>pointer is passed to it; it just passes an address.  The declaration
>in the function where the routine is passed defines what the 
>function is expected to do (whether the address is to be read/write, or
>called). 
>
>Dan

Pagan tripe. A pointer is more than just an address (and an 'address'
may well be more than 'just an address'.) Firstly you can't
read or write through a function pointer, and you can't call through a
data pointer. The two are effectively in separate spaces. The pragmatisms
of C allow you declare a function as expecting an (int *) and then pass
that function a function pointer, but then you are just being very Wrong.

-- 
----------------------------------------------------------------------
Greg Smith     University of Toronto      UUCP: ..utzoo!utcsri!greg
Have vAX, will hack...

chris@mimsy.UUCP (Chris Torek) (09/14/87)

In article <2236@emory.uucp> platt@emory.uucp (Dan Platt) writes:
>Actually, depending on the model (the IBM machines have a segmented
>addressing scheme), NULL is defined as 0 if the memory is in small or
>medium models, and is defined as 0L in compact and large models.  

This is a bogosity intended to make broken code work.  It is
unnecessary and potentially confusing.

>As far as the function is concerned, it doesn't care what kind of
>pointer is passed to it; it just passes an address.

Here is the essence of the confusion.  This is *not* *true*.
Despite the relative weakness of its typing, C is a typed language.
Functions do not `just pass addresses' to other functions; functions
pass `pointer to <object>'s, such as `pointer to function returning
int'.  In small models, IBM PC pointers-to-functions-returning-int
occupy 16 bits; in large models, these pointers occupy 32 bits.
That `0' and `0L' happen to exactly correspond to those 16 and 32
bit pointer-to-nil-function-returning-int values is what might be
called an accident of design.  If you write type-correct code in
the first place, you will not depend upon this carefully engineered
accident, and hence will not be burned when a different C implementation
cannot do this for you.

[We used to hear `all the world's a Vax'; is it now to be `all the
world is PC compatible'?]
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7690)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

devine@vianet.UUCP (Bob Devine) (09/15/87)

In article <2236@emory.uucp>, platt@emory.uucp (Dan Platt) writes:
> Actually, depending on the model (the IBM machines have a segmented
> addressing scheme), NULL is defined as 0 if the memory is in small or
> medium models, and is defined as 0L in compact and large models.  
> Since a module may need to be compiled several ways, rather than 
> checking the compile flags in the preprocessor, it's easiest to 
> just define it as #define NULLFUNC NULL, and including either
> <stdio.h> or <stdlib.h> (either having NULL defined) before the other
> define.

  This is not right.  You are confusing the NULL defined for *data*
pointers with *code* pointers.  The silly memory models can be shown
as a 2-by-2 matrix :

                        # of code segments

                       one         multiple
                   +------------+------------+
             one   |   SMALL    |   MEDIUM   |
   # of data       | 16bit code | 32bit code |
                   | 16bit data | 16bit data |
    segments       +------------+------------+
             mult. | "un-named" |   LARGE    |
                   | 16bit code | 32bit code |
                   | 32bit data | 32bit data |
                   +------------+------------+

  So, using NULL will only give one column of the matrix.  Let the compiler
do the appropriate assignment.  Or, if all functions are the same, you can
use a define like:

#define NULLFUNC (void(*)())0

Bob Devine

[ Note that MSC does not name the commonly called "compact" variation.]

gwyn@brl-smoke.ARPA (Doug Gwyn ) (09/21/87)

#define	NULL	0	/* universally correct */

Just don't attempt to pass this as a parameter to a function without
supplying the proper cast.
	func( stuff, (int (*)())NULL );	/* for example */

If you make extensive usage of such parameters, then it might save
some typing to define shorthand such as

#define	NULL_DIRENTP	((struct dirent *)0)

This would be particularly useful if the type involved might change
in a future revision of the code.

Ignore all babble about defining NULL as 0L etc.