[comp.os.minix] Minix/ST <stdio.h> problems.

meulenbr@cstw01.UUCP (Frans Meulenbroeks) (10/21/88)

Hi!

I've encountered a number of problems using Minix/ST <stdio.h>.
The major problem is that it defines NULL as 0.
This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
Solution: change the 0 in (char *)0

Some other problems are encountered when a program tries to access the
private part of a FILE struct (e.g. the count field, which is normally
called cnt), or when someone refers to _iob (which is _io_table on
Minix). I have a quick and dirty fix using defines, which fixes most of
these problems. The question is: should I post it (is there interest),
and can I post it (context diffs will be greater than the complete file;
however, I'm also a little reluctant to post the complete file without
permission of Dr. Tanenbaum).

A side note: don't expect that _doprintf uses the parameter order you're
used to. It doesn't. Of course decent programs don't call _doprintf
directly (but dirty ones like the curses lib do.

-- 
Frans Meulenbroeks        (meulenbr@cst.prl.philips.nl)
	Centre for Software Technology
	( or try: ...!mcvax!philmds!prle!cst!meulenbr)

chris@mimsy.UUCP (Chris Torek) (10/22/88)

In article <249@cstw01.UUCP> meulenbr@cstw01.UUCP (Frans Meulenbroeks) writes:
>I've encountered a number of problems using Minix/ST <stdio.h>.
>The major problem is that it defines NULL as 0.
>This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
>Solution: change the 0 in (char *)0

Do NOT do this.  In the absence of `void *' as a type, the only two
correct and realistic ways to define NULL are `0' and `0L'.  (If you
read the draft ANSI C standard, you should see why I say this.)  Instead,
be sure to cast NULL whenever it is used as an argument to a function.

>Some other problems are encountered when a program tries to access the
>private part of a FILE struct . . . .

Not surprising; you are not supposed to do this, but people will do
all sorts of things....

>A side note: don't expect that _doprintf uses the parameter order you're
>used to. It doesn't. Of course decent programs don't call _doprintf
>directly (but dirty ones like the curses lib do.

Curses should use vsprintf.  vsprintf is like sprintf but gets a
`va_list' argument, i.e., a pointer into the call stack, and vsprintf
is basically a wrapper for the internal printf routine---sprintf is
a bit more than a wrapper since it provides the call stack to which
the va_list pointer will point.

(Actually, curses should use a more general stdio, but this may be more
than Minix needs as yet.  Stdio currently calls read, write, lseek,
and close on open FILEs.  Rather than calling read, write, lseek, and
close directly, it should call `a read routine', `a write routine', `a
seek routine', and `a close routine'.  Normally these would be the same
ones, but another routine would `open' four functions rather than a
file descriptor.  Curses would then use this to `open' a curses window
for writing, where the write function would put characters to the
window.  Then there are no magic buffer size limits like `no more than
127 characters printed at a time'.  Note that the various routines must
be given an argument so that, e.g., a curses window writer can tell to
which window to print.  My approach was to allow one pointer argument.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

dtynan@sultra.UUCP (Der Tynan) (10/25/88)

In article <249@cstw01.UUCP>, meulenbr@cstw01.UUCP (Frans Meulenbroeks) writes:
> 
> This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
> Solution: change the 0 in (char *)0
> 
> Frans Meulenbroeks        (meulenbr@cst.prl.philips.nl)

Hmmm.  I've always been partial to the idea that sizeof(char *) == sizeof(int).
In fact, I've seen a lot of code (not necessarily my own), which assumes this.
If one needs sizeof(int) == 16, then use short.  It's my belief that if you
have a true 32-bit address space (lucky you), then there's no reason to keep
ints to a small size.  Of course, one should always use casts with NULL anyway.
I don't know, offhand, of any compiler that requires this, but it's good
practice, as shown by your problem.  NULL is best left as 0, and for instance,
	if ((memptr = malloc(BUFSIZE)) == (char *)NULL)
		etc();

Hope this helps!
						- Der
-- 
Reply:	dtynan@sultra.UUCP		(Der Tynan @ Tynan Computers)
	{mips,pyramid}!sultra!dtynan
	Cast a cold eye on life, on death.  Horseman, pass by...    [WBY]

FYS-EH%FINTUVM.BITNET@cunyvm.cuny.edu (Esa Heinonen) (10/27/88)

In article <249@cstw01.UUCP>, meulenbr@cstw01.UUCP (Frans Meulenbroeks) writes:
>
> This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
> Solution: change the 0 in (char *)0
>

(32*8)-bit pointers and (16*8)-bit ints, WOW  :-)
(just to be consistent with my signature)

Finally, floats have become unnecessary (well, almost)... a 128-bit
signed integer would have a range of -1.7E38 .. 1.7E38. Now all we
need is a way to have fractions as well ?|

You obviously meant "sizeof(char *) == 4 and sizeof(int) == 2" .

------------------------------------------------
|  Esa Heinonen, University of Turku, Finland  |
------------------------------------------------
|  Bitnet:   FYS-EH at FINTUVM                 |
|  Internet: FYS-EH at FINTUVM.UTU.FI          |
------------------------------------------------
|  "Minix? Just say WOW|"                      |
------------------------------------------------

siebren@cwi.nl (Siebren van der Zee) (10/27/88)

In article <249@cstw01.UUCP> meulenbr@cstw01.UUCP writes:
>Hi!
>
>I've encountered a number of problems using Minix/ST <stdio.h>.
>The major problem is that it defines NULL as 0.

The major problem is that you forgot that Minix is modelled after V7 unix.

>This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
>Solution: change the 0 in (char *)0

It has been a killer ever since 32-bit architectures showed up, but that's
not much of a problem: it kills braindamaged programmers. I consider this
a nice side-effect.

>Some other problems are encountered when a program tries to access the
>private part of a FILE struct (e.g. the count field, which is normally
>called cnt), or when someone refers to _iob (which is _io_table on
>Minix). I have a quick and dirty fix using defines, which fixes most of
			    ^^^^^
>these problems. The question is: should I post it (is there interest),

No! Please!!

	Siebren van der Zee, CWI Amsterdanm
	"A kernel a day keeps the users away."

henry@utzoo.uucp (Henry Spencer) (10/29/88)

In article <2598@sultra.UUCP> dtynan@sultra.UUCP (Der Tynan) writes:
>... I've always been partial to the idea that sizeof(char *) == sizeof(int).
>In fact, I've seen a lot of code (not necessarily my own), which assumes this.

Such code is broken.  Unfortunately, there *is* a lot of it about.

>If one needs sizeof(int) == 16, then use short.  It's my belief that if you
>have a true 32-bit address space (lucky you), then there's no reason to keep
>ints to a small size...

Can you say "performance"?  Ints are supposed to be the "natural" width
for the machine, which is often taken to mean the width that performs
best.  On the 16-bit 680x0s, there is a sticky decision to be made:
16-bit ints are considerably faster, 32-bit ints are more useful and
cause less trouble with poorly-written software.  Not an easy choice,
and there is no unanimity among C compilers on it.

>Of course, one should always use casts with NULL anyway.

No, the only place where NULL must be cast is as a function parameter.
But casting it *there* is very important.

>	if ((memptr = malloc(BUFSIZE)) == (char *)NULL)

This is silly; any compiler that isn't totally brain-dead will do exactly
the same thing with or without the cast.  Problems arise only when the
compiler cannot tell what the type is supposed to be, and function calls
are the only place where that really crops up.

(Note, a machine with 16-bit ints and 32-bit pointers has a related
problem with return values:  the calling function *must* know the correct
type of the return value.  Many programs are sloppy about this, since
the default type -- int -- happens to work right for pointer-valued
functions on VAXen and the like.  Not so when sizeof(int) != sizeof(char*).)
-- 
The dream *IS* alive...         |    Henry Spencer at U of Toronto Zoology
but not at NASA.                |uunet!attcan!utzoo!henry henry@zoo.toronto.edu

mem@zinn.MV.COM (Mark E. Mallett) (10/31/88)

In article <2598@sultra.UUCP> dtynan@sultra.UUCP (Der Tynan) writes:
>Hmmm.  I've always been partial to the idea that sizeof(char *) == sizeof(int).
>In fact, I've seen a lot of code (not necessarily my own), which assumes this.
>If one needs sizeof(int) == 16, then use short.  It's my belief that if you
>have a true 32-bit address space (lucky you), then there's no reason to keep
>ints to a small size.

Of course it doesn't matter what you're partial to your what you're
beliefs are, what matters is the language definition and the compiler
implementation.  The only assumption you should make about an int is that
it is a size that is optimally handled by your hardware, and sometimes
that assumption is at the whim of the compiler writer.  If you really
need to make assumptions -- any assumptions -- about the size and
class of your declarations and their modifiers, use typedefs and/or
defines to devise your own metatypes, and use those metatypes where
storage characteristics must match some predetermined criteria.


> Of course, one should always use casts with NULL anyway.

One should only have to use casts with NULL if NULL is improperly
defined, and even then using the cast won't necessarily help you.

-mm-
-- 
Mark E. Mallett  Zinn Computer Co/ PO Box 4188/ Manchester NH/ 03103 
Bus. Phone: 603 645 5069    Home: 603 424 8129
uucp: mem@zinn.MV.COM  (  ...{decvax|elrond|harvard}!zinn!mem   )
BIX: mmallett

chris@mimsy.UUCP (Chris Torek) (10/31/88)

I suppose I had best post this again....

From: chris@mimsy.UUCP (Chris Torek)
Newsgroups: comp.lang.c
Subject: Re: NULL etc.
Message-ID: <12290@mimsy.UUCP>
Date: 2 Jul 88 20:36:44 GMT
References: <MWlAyzy00hIE82Q1Qc@cs.cmu.edu> <6966@cup.portal.com> <3458@rpp386.UUCP>
Organization: U of Maryland, Dept. of Computer Science, Coll. Pk., MD 20742
Lines: 91

C's untyped nil pointer, which MUST be given a type before it can be
used correctly, is written as `0' (and `0L', and possibly using
constant integer expressions, depending on whose language definition you use;
but `0' must work).  After it has been given a type (`(char *)0') it
becomes a nil pointer of that type.  Once it has a type (if we ignore
some fine points in the draft proposed American National Standard for
C, many of which are unlikely to be implemented in current C compilers)
it may not be used as a nil pointer of another type.  Hence (char *)0
is a nil pointer to char, and as such may not be used as a nil pointer
to int, or a nil pointer to struct tcurts, or indeed as anything other
than a pointer to char.  It may work---indeed, it is more likely to
work than to fail---but it is incorrect and unportable, and should (and
does in PCC) draw at least a warning from the compiler.

There are only two ways that the untyped nil pointer can acquire a
type, namely assignment and comparison.  Casts are a special case of
assignment, as are arguments to functions that have prototypes in
scope.  Where this causes the most trouble is in arguments to functions
that do not have prototypes in scope, or for which the prototype does
not specify a type for that argument: e.g., execl():

	f() {
		void execl(char *, ...);

		execl("prog", "prog", "arg1", "arg2", ___);
	}

The only correct way to fill in the blank is with (char *)0 (or
possibly (char *)0L and similar tricks; outside of obfuscated C
contests, these tricks are not worth considering).  If NULL is
defined as 0 (or 0L or (void *)0), (char *)NULL is equivalent and
hence also correct.

The dpANS has at present one more instance of an `untyped' nil pointer,
namely `(void *)0'.  This is an anomaly in the type system, and, while
it has some beneficial properties, I believe that overall it makes the
situation worse, not better.  The differences between using `0' and
`(void *)0' as a `generic nil' are, first, that while 0 is also an
integer constant, (void *)0 is not, and second, that (void *)0 is also
a typed nil pointer (ouch!---more below).

Suppose that NULL is defined as either `0' or `(void *)0'---one of the
two untyped nil pointers---but that we do not know which one.  Which of
the following calls are correct?

	/* defintions before the fragments (note lack of prototypes) */
	void f1(cp) char *cp; { <code> }
	void f2(ip) int *ip; { <code> }
	void f3(vp) void *vp; { <code> }

	...
		f1(NULL);		/* call 1 */
		f1((char *)NULL);	/* call 2 */
		f2(NULL);		/* call 3 */
		f2((int *)NULL);	/* call 4 */
		f3(NULL);		/* call 5 */
		f3((void *)NULL);	/* call 6 */

It is easy to see that calls 2, 4, and 6 (which cast their arguments
and hence provide types) are correct.  The surprise is that while calls
1, 3, and 5 are all wrong if NULL is defined as `0', calls 1 and 5 are
both correct, or at least will both work, if NULL is defined as
`(void *)0'.  Call 3 is wrong in any case.

We can get away with `f1((void *)0)' only because of a technicality:
the dpANS says that (void *) and (char *) must have the same
representation (which more or less means `must be the same type'), and
because (void *) is a valid pointer type, (void *)0 must be a valid nil
pointer of type `void *', and thus must also be a valid nil pointer of
type `char *'.  (Actually, this argument glosses over a subsidiary
technicality, in that there is no guarantee that there is only ONE
valid nil pointer of any given type, but that way lies madness.  There
are more arguments about whether `same representation' implies
`indistinguishable'; these, too, are best left untouched.)

There are no ANSI-conformant C compilers, for there is as yet no ANSI C
standard.  One should therefore assume that code may have to run under
a compiler where NULL is defined as `0', not as `(void *)0', and should
therefore avoid calls like 1, 3, and 5 above.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

dal@midgard.UUCP (Dale Schumacher) (11/01/88)

In article <7683@boring.cwi.nl> siebren@cwi.nl (Siebren van der Zee) writes:
|In article <249@cstw01.UUCP> meulenbr@cstw01.UUCP writes:
|>Hi!
|>
|>I've encountered a number of problems using Minix/ST <stdio.h>.
|>The major problem is that it defines NULL as 0.
[...]
|>This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
|>Solution: change the 0 in (char *)0
|
|It has been a killer ever since 32-bit architectures showed up

The X3J11 C standard proposal discusses this problem.  It states that NULL
may be defined as 0, 0L or ((void *) 0) as needed by an implementation.
Therefore, is makes sense for NULL to be 0 (maybe) on the PC version, and
0L on the ST.  Other liberties, like assuming sizeof(int)==sizeof(char *)
and failing to declare pointer return values, are simply NON-PORTABLE and
should not be used in Minix-related code or anything else pretending to be
portable.  I think it is very desirable to have code which compiles under
Minix-PC work properly under Minix-ST with NO CHANGES, and visa-versa.  I
don't think this is unreasonable to expect.

P.S.:  When we get a more ansi-conformal compiler for Minix, possibly from
Sozobon, NULL would change to the ((void *) 0) that it SHOULD be.

randy@umn-cs.CS.UMN.EDU (Randy Orrison) (11/02/88)

In article <486@midgard.UUCP> dal@syntel.UUCP (Dale Schumacher) writes:
|In article <7683@boring.cwi.nl> siebren@cwi.nl (Siebren van der Zee) writes:
||In article <249@cstw01.UUCP> meulenbr@cstw01.UUCP writes:
||>Hi!
||>I've encountered a number of problems using Minix/ST <stdio.h>.
||>The major problem is that it defines NULL as 0.
	THIS IS NOT A PROBLEM.
||>This is a killer on an ST since sizeof(char *) == 32 and sizeof(int) == 16.
||>Solution: change the 0 in (char *)0
   SOLUTION:  cast NULL in function calls

|The X3J11 C standard proposal discusses this problem.  It states that NULL
|may be defined as 0, 0L or ((void *) 0) as needed by an implementation.
|Therefore, is makes sense for NULL to be 0 (maybe) on the PC version, and
|0L on the ST.

If used properly, the definition of NULL is irrelevant.

In the context of an assignment or comparison, the compiler can figure
out the appropriate type and will do the cast for you.  In the context
of a parameter to a function, there is NO DEFINITION that will work
universally, and so it MUST BE CAST to the appropriate type.
(Exception:  In ANSI C, function prototypes will allow the compiler to
figure out the type and do the cast for you. (in which case the
definition again doesn't matter.))

|Other liberties, like assuming sizeof(int)==sizeof(char *)
|and failing to declare pointer return values, are simply NON-PORTABLE and
|should not be used in Minix-related code or anything else pretending to be
|portable.  I think it is very desirable to have code which compiles under
|Minix-PC work properly under Minix-ST with NO CHANGES, and visa-versa.  I
|don't think this is unreasonable to expect.

Agreed, whole heartedly.

|P.S.:  When we get a more ansi-conformal compiler for Minix, possibly from
|Sozobon, NULL would change to the ((void *) 0) that it SHOULD be.

This really doesn't matter...

Summary:

For any definition of NULL, and any foo_type or mumble:

	foo_type	*a;
	a = NULL;			/*  IS portable  */
	if (a == NULL)			/*  IS portable  */
	my_func (NULL);			/*  is NOT portable
					    (unless there's a prototype)  */
	my_func ((mumble *)NULL);	/*  IS portable, and REQUIRED for
					    portability under pre-ANSI C  */
--
Randy Orrison, Chemical Computer Thinking Battery  --  randy@cctb.mn.org
(aka randy@{ux.acss.umn.edu, umn-cs.uucp, umnacca.bitnet, halcdc.uucp})
If you think last Tuesday was a drag, wait till you see what happens
tomorrow!

kirkenda@psu-cs.UUCP (Steve Kirkendall) (11/03/88)

In article <397@zinn.MV.COM> mem@zinn.MV.COM (Mark E. Mallett) writes:
>In article <2598@sultra.UUCP> dtynan@sultra.UUCP (Der Tynan) writes:
>>Hmmm.  I've always been partial to the idea that sizeof(char *) == sizeof(int).
>>In fact, I've seen a lot of code (not necessarily my own), which assumes this.
>>If one needs sizeof(int) == 16, then use short.  It's my belief that if you
>>have a true 32-bit address space (lucky you), then there's no reason to keep
>>ints to a small size.
>
>Of course it doesn't matter what you're partial to your what you're
>beliefs are, what matters is the language definition and the compiler
>implementation.  The only assumption you should make about an int is that
>it is a size that is optimally handled by your hardware, and sometimes
>that assumption is at the whim of the compiler writer

Sure, *I* write good, portable code.  Anybody who does a lot of MINIX coding
will have to learn to be careful, and that ain't all bad.  Unfortunately,
there are some people in this world who don't... so some highly desirable
programs are going to be tough to port to MINIX/ST.

One painful example: GCC.  The author (Stallman) allowed a lot pointer/int
confusion to creep into his code, so now, to get gcc running under MINIX/ST
somebody is going to have to dig through (I'm guessing here) about 70,000
lines of code.

BTW, there's more to the issue of pointer/int confusion than just the size of
the data objects.  You also have to worry about the convention that the
compiler uses for returning values from functions.  For example, ints might
be returned in D0 while pointers are returned in A0.  If your code confuses
pointers and integers, you are assuming not only that they are the same size, 
but also that they use the same register for returning values.

THELBEKK%NORUNIT.BITNET@cunyvm.cuny.edu (11/04/88)

With regard to portability problems:  It was mentioned that some pointer
versus int assumptions had "been allowed to creep into" GNU CC.  This is
nothing new to me...  I've had occasion to work with several programs
by Stallman & Co, and while admitting freely that RMS is something of a
genius, and FSF just what the doctor ordered, it seems that the assumtion
that (sizeof(int) == sizeof(int *) == 32

THELBEKK%NORUNIT.BITNET@cunyvm.cuny.edu (11/04/88)

With regard to portability problems:  It was mentioned that some pointer
versus int assumptions had "been allowed to creep into" GNU CC.  This is
nothing new to me...  I've had occasion to work with several programs
by Stallman & Co, and while admitting freely that RMS is something of a
genius, and FSF just what the doctor ordered, it seems that the assumtion
that (sizeof(int) == sizeof(int *) == 32) is pretty much accepted.  In
fact, parameters to functions and return values of functions are often
explicitly declared as int.  I ported GNU Bison to Microsoft C under DOS,
and it was a pretty tough job.  Must have rewritten pointer arithmetic
in some 30 or 40 places from what it was to what it should have been, in
addition to all the other little fixes necessary.  It was worth it, the
program now compiles with no warnings at max warning level, and works
perfectly -- it has already generated compilers successfully under DOS.

Anyway, beware of GNU software on 16-bit machines; it will usually need
quite a bit of work to get it running...

-tih

egisin@watmath.waterloo.edu (Eric Gisin) (11/05/88)

In article <1187@psu-cs.UUCP>, kirkenda@psu-cs.UUCP (Steve Kirkendall) writes:
< there are some people in this world who don't... so some highly desirable
< programs are going to be tough to port to MINIX/ST.
< 
< One painful example: GCC.  The author (Stallman) allowed a lot pointer/int
< confusion to creep into his code, so now, to get gcc running under MINIX/ST
< somebody is going to have to dig through (I'm guessing here) about 70,000
< lines of code.

No, just compile GCC and the libraries with 32-bit ints.
You will have to re-write the system call library to
convert between the application's 32-bit ints and the
kernel's 16-bit ints. That's how the ST port of GCC was done.

dtynan@sultra.UUCP (Der Tynan) (11/05/88)

In article <5181@louie.udel.EDU>, THELBEKK%NORUNIT.BITNET@cunyvm.cuny.edu writes:
> With regard to portability problems:  It was mentioned that some pointer
> versus int assumptions had "been allowed to creep into" GNU CC.  This is
> nothing new to me...  I've had occasion to work with several programs
> by Stallman & Co, and while admitting freely that RMS is something of a
> genius, and FSF just what the doctor ordered, it seems that the assumtion
> that (sizeof(int) == sizeof(int *) == 32

Ok, for the benefit of those who may have missed earlier postings by me, on the
subject of GNU, I'm enclosing a small section from the GNU Programming
Standards Manual.  Note that this is not trying to tell us how to program in
C, but how to write code for the GNU project.  Perhaps this will finally clear
up the comments about FSF, and sixteen-bit machines...

	Portability standards:

	Much of what is called "portability" in the Unix world refers to
	porting to different Unix versions.  This is not relevant to GNU
	software, because its purpose is to run on top of one and only
	one kernel, the GNU kernel, compiled with one and only one C
	compiler, the GNU C compiler.  The amount and kinds of variation
	among GNU systems on different cpu's will be like the variation
	among Berkeley 4.3 systems on different cpu's.

			[Portions deleted for brevity]

	It remains necessary to worry about differences among cpu types, such
	as the difference in byte ordering and alignment restrictions.
	However, I don't expect 16-bit machines ever to be supported by GNU,
	so there is no point in spending any time to consider the possibility
	that an int will be less than 32 bits.

	You can assume that it is reasonable to use a meg of memory.  Don't
	strain to reduce memory usage unless it can get to that level.  If
	your program creates complicated data structures, just make them in
	core and give a fatal error if malloc returns zero.

Reprinted without permission.
I hope this dispells some of the myths about using GNU software on a PC.  It
*may* be possible now, but that doesn't mean that the *next* version will
fit in a 64K segment.  Of course, on the other hand, GNUUCP *will* work on a
PC (probably an oversight :-), and I hope to surface a MINIX version soon...
						- Der
-- 
	dtynan@Tynan.COM  (Dermot Tynan @ Tynan Computers)
	{apple,mips,pyramid,uunet}!zorba.Tynan.COM!dtynan

 ---  God invented alcohol to keep the Irish from taking over the planet  ---

bill@uhccux.uhcc.hawaii.edu (William J. King) (11/11/88)

anyone have Andy Tannenbaum's e-mail address?