[comp.lang.c] Help me cast this!

wrd3156@fedeva.UUCP (Bill Daniels) (05/03/88)

How do I cast the malloc() in line 12 of the following program to avoid 
the lint cries of "warning: illegal pointer combination" et al?
My system is a Perkin-Elmer 3210 running SYS V rel. 2.  I have tried 
"all but one" of every combination of casts known to mankind. Help me find
the only one left, the one that works!


#include <stdio.h>

main()	
{
	char *malloc();
	int i;
	struct outfile {
		int j;
		int k;
	} (*output)[];

	output = malloc(sizeof(struct outfile) * 3);

	(*output)[0].j = 1;
	(*output)[0].k = 2;

	i = printf("%d   %d\n",(*output)[0].j,(*output)[0].k);

	return(i);
}

THIS IS MY LINT'S OUTPUT:

trial.c
==============
warning: illegal pointer combination
    (12)  	
warning: possible pointer alignment problem
    (12)  	


Thanks for any assistance!
-- 
bill daniels
federal express, memphis, tn
{hplabs!csun,gatech!emcard}!fedeva!wrd3156

friedl@vsi.UUCP (Stephen J. Friedl) (05/03/88)

In article <294@fedeva.UUCP>, wrd3156@fedeva.UUCP (Bill Daniels) writes:
>
> [nonessential lines deleted here]
> 
> How do I cast the malloc() in line 12 of the following program to avoid 
> the lint cries of "warning: illegal pointer combination" et al?
> 
> main()	
> {
> char	*malloc();
>
> 	struct outfile {
> 		int j;
> 		int k;
> 	} (*output)[];
> 
> 	output = malloc(sizeof(struct outfile) * 3);

Some casts defy obvious attempts, and this is one of them.

First, declare an abstract variable (I always use "foo") of the
same type you want to cast.  We want to say "foo is a pointer to
an array of struct outfile", so it would be:

	struct outfile (*foo)[]

This is just an ordinary variable, and it's the same declaration
as "output" above.  To turn this into a cast, drop the "foo" and
put parens around the whole thing:

	(struct outfile (*)[])
                         ^^_________ foo *was* between here

This is the "obvious" proper cast.  Think how this works on others:

	the variable is...	abstract decl	  cast
	------------------	-------------	--------
	int			int foo		(int)
	ptr to char		char *foo	(char *)
	ptr to fcn rtn int	int (*foo)()	(int (*)())

In any valid cast, there is exactly one place where the abstract
variable would fit, and after a while you can get the hang of
where it goes.

-- 
Steve Friedl      V-Systems, Inc. (714) 545-6442       3B2-kind-of-guy
friedl@vsi.com      {backbones}!vsi.com!friedl      attmail!vsi!friedl

ajmyrvold@violet.waterloo.edu (Alan Myrvold) (05/03/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>
>How do I cast the malloc() in line 12 of the following program to avoid 
>the lint cries of "warning: illegal pointer combination" et al?
...
>	struct outfile {
>		int j;
>		int k;
>	} (*output)[];
>
>	output = malloc(sizeof(struct outfile) * 3);

Did you try :
	output = (struct outfile **) malloc(sizeof(struct outfile) * 3);

This goes through lint here, althougth lint complains that
        sizeof(struct outfile) * 3
is long, and should be unsigned.

So 
	output = (struct outfile **) malloc((unsigned)
                    sizeof(struct outfile) * 3);
should clear everything up.


-------------------------------------------------------------------
Si je t'aime? Bien sur que je t'aime! Ne suis-je pas en train de
te le prouver encore une fois, dans ce lit? 
-------------------------------------------------------------------
Alan Myrvold     ajmyrvold@violet.waterloo.edu
-------------------------------------------------------------------

gwyn@brl-smoke.ARPA (Doug Gwyn ) (05/03/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>How do I cast the malloc() in line 12 of the following program to avoid 
>the lint cries of "warning: illegal pointer combination" et al?

General, SVR2 "lint" will complain about casting the (char *) returned
by malloc() into other pointer types, and there seems to be no way around
it.  The best you can do is obtain a "possible pointer alignment problem"
warning.  That's because "lint" does not know that the pointer returned by
malloc() is guaranteed to point to properly aligned storage for any use.

chris@mimsy.UUCP (Chris Torek) (05/03/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>	char *malloc();
>	struct outfile {
>		int j;
>		int k;
>	} (*output)[];
>	output = malloc(sizeof(struct outfile) * 3);

First, note that output is not a proper variable:

	% echo 'declare output as pointer to array of struct outfile' | cdecl
	Warning: Unsupported in C -- Pointer to array of unspecified dimension
	struct outfile (*output)[]
	% 

In particular, your compiler will let you write (*output)[i], or
output[0][i], but try output[j][i] and <boom>!  I have argued this
point here before; I will only say now that this is not what C wants
you to say to create a dynamic array 3 of struct outfile: you should
be using a simple pointer.  Nonetheless, it happens to work.

On to the cast:  The syntax of a cast is exactly the same as
the syntax of a declaration, minus the variable name.  Hence to
cast malloc to the same (unsupported) type:

	output = (struct outfile (*)[])malloc(...);
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

hender@prandtl.nas.nasa.gov.nas.nasa.gov (Bob Henderson) (05/04/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>
>How do I cast the malloc() in line 12 of the following program to avoid 
>the lint cries of "warning: illegal pointer combination" et al?
>My system is a Perkin-Elmer 3210 running SYS V rel. 2.  I have tried 
>"all but one" of every combination of casts known to mankind. Help me find
>the only one left, the one that works!
>...


Lint is complaining about the assignmemt, not because of the casting, but
because it is not sure the byte address returned by malloc will allign the
structure correctly. (It most likely will, but link cannot be sure).

So the "trick" is to force the alignment to a word boundry.  Assuming that
"int" is the natural size for your machine; you might code the cast like:

	struct foo {
		long count;
		char byte;
		int junk;
	} foo, *pfoo;

	char *malloc(), *cp;

	/* step 1: allocate n-1 extra bytes for alignment	*/

	cp = malloc(sizeof(foo)+sizeof(int)-1);

	/* step 2: align address to natural boundry 		*/

	pfoo = (struct foo *)(( (int)cp +sizeof(int)-1) & ~(sizeof(int)-1) );

In step 1, the space is allocated, along with extra space for the allignment.
In step 2, the address malloc returned (a byte address) is adjusted to point to
the next byte address (inclusive) that is also a int address. 

If double words or some large address type is involved, then use that type 
instead of int.
-----------------------------------------------------------------------------
Bob Henderson
NASA Ames Research Center
arpa: hender@prandtl.nas.nasa.gov
uucp: {ihnp4,hplabs,ucbcad,tektronix,allegra}!ames!prandtl!hender
  "I like work;  it fascinates me;  I can sit and look at it for hours."

carlp@iscuva.ISCS.COM (Carl Paukstis) (05/04/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
=
=How do I cast the malloc() in line 12 of the following program to avoid 
=the lint cries of "warning: illegal pointer combination" et al?
=
=#include <stdio.h>
=
=main()	
={
=	char *malloc();
=	int i;
=	struct outfile {
=		int j;
=		int k;
=	} (*output)[];
=
=	output = malloc(sizeof(struct outfile) * 3);
=
=	(*output)[0].j = 1;
=	(*output)[0].k = 2;
=
=	i = printf("%d   %d\n",(*output)[0].j,(*output)[0].k);
=
=	return(i);
=}
=-- 
=bill daniels
=federal express, memphis, tn
={hplabs!csun,gatech!emcard}!fedeva!wrd3156

	output = (struct outfile **)malloc(sizeof(struct outfile) * 3);

works on Ultrix 2.something, a Sys V "compliant" type system complains of
"possible pointer alignment problem", presumably because "char" requires
only byte alignment (returned from malloc), while "struct outfile *"
requires halfword alignment (on 68010 systems).  Maybe my lint is too
good... I can't find a combination that passes clean on the Sys V system,
either.
-- 
Carl Paukstis    +1 509 927 5600 x5321  |"I met a girl who sang the blues
                                        | and asked her for some happy news
UUCP:     carlp@iscuvc.ISCS.COM         | but she just smiled and turned away"
          ...uunet!iscuva!iscuvc!carlp  |                    - Don MacLean

limes@sun.uucp (Greg Limes) (05/04/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>How do I cast the malloc() in line 12 of the following program to avoid 
>the lint cries of "warning: illegal pointer combination" et al?
>
>
>#include <stdio.h>
>
>main()	
>{
>	char *malloc();
>	int i;
>	struct outfile {
>		int j;
>		int k;
>	} (*output)[];
>
>	output = malloc(sizeof(struct outfile) * 3);
>
>	(*output)[0].j = 1;
>	(*output)[0].k = 2;
>
>	i = printf("%d   %d\n",(*output)[0].j,(*output)[0].k);
>
>	return(i);
>}

Several quick points:

    1) this looks like a job for typedef; if its too complex to
    figure out the cast, it may be too complex to read the cast. A
    translation of your code literally into typedefs gives the following
    results for declaration and cast:

	typedef struct { int j, k; } outrec;
	typedef outrec outfile[];
	outfile *output;
	output = (outfile *) malloc (3 * sizeof (outrec));

    2) Pointers to indefinite sized arrays are normally kept simply as a
    pointer to the beginning of the array. This removes one level of
    indirection in the code, making it easier to read: all your
    "(*ptr)[rec].field" constructs change into "ptr[rec].field", giving
    somewhat easier to write, read, and maintain code. Expanding your
    program a bit shows one way of using this:

        /* sortecho: echo parameters, sorted shortest first */
        char *malloc ();
        typedef struct { int len; char *ap; } outrec;

        int cmp_rec (p, q) outrec *p, *q; { return p->len - q->len; }

        main (ac, av) char **av; {
            int nrec;
            outrec *output;

            nrec = ac - 1;
            output = (outrec *) malloc (nrec * sizeof (outrec));
            for (i = 0; i < nrec; ++i) {
                output[i].ptr = av[i + 1];
                output[i].len = strlen (av[i + 1]);
            }
            qsort (output, nrec, sizeof (outrec), cmp_rec);
            for (i = 0; i < nrec; ++i)
                printf ("%6d: %s\n", output[i].len, output[i].ptr);
        }
-- 
   Greg Limes [limes@sun.com]			Illigitimi Non Carborundum

chris@mimsy.UUCP (Chris Torek) (05/04/88)

In article <51875@sun.uucp> limes@sun.uucp (Greg Limes) writes:
>2) Pointers to indefinite sized arrays are normally kept simply as a
>   pointer to the beginning of the array. This removes one level of
>   indirection in the code, making it easier to read: all your
>   "(*ptr)[rec].field" constructs change into "ptr[rec].field", giving
>   somewhat easier to write, read, and maintain code.

I would (indeed, I did) put it more strongly: it makes it correct.
This is the right idea, in any case.  There are some who claim that
(*p)[i] is somehow more indicative that there is an indefinitely sized
array around somewhere, but I disagree, and side with Greg:  p[i] is
clearer.

One small fix:
>	typedef struct { int len; char *ap; } outrec;
>	int cmp_rec (p, q) outrec *p, *q; { return p->len - q->len; }
	...
>		qsort (output, nrec, sizeof (outrec), cmp_rec);

A shiny new dpANS-conformant compiler will complain here, if you
have written a `#include <stddef.h>' as well, for qsort's final
argument is not a function with two structure pointer arguments,
but rather a function with two (char *) or (void *) arguments
(depending on who you ask: K&R 1st ed., or dpANS).

	int cmp_rec(p, q)
		char *p, *q;
	{

		return (((outrec *)p)->len - ((outrec *)q)->len);
	}

If you do otherwise, on a DG MV series machine, your code will work
about as well as the 4.3BSD `ls' does---which is to say, not at all.
(The Data General has Word Pointers and Byte Pointers, and never the
twain should meet, save by a Cast.)

(And if we *really* get picky we can worry about the call to malloc
when nrec==0. :-) )
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

walter@hpcllz2.HP.COM (Walter Murray) (05/04/88)

Bill Daniels writes:

> How do I cast the malloc() in the following program to avoid 
> the lint cries of "warning: illegal pointer combination" et al?

[program abbreviated]

> #include <stdio.h>
> main()	
> {
> char *malloc();
> struct outfile {
>         int j;
>         int k;
>       } (*output)[];
> output = malloc(sizeof(struct outfile) * 3);
> }

> THIS IS MY LINT'S OUTPUT:

> trial.c
> ==============
> warning: illegal pointer combination
> (12)  	
> warning: possible pointer alignment problem
> (12)  	

To avoid the "illegal pointer combination", try:

   output = (struct outfile (*)[])malloc(sizeof(struct outfile) * 3);

The "possible pointer alignment problem" warning comes from the
fact that malloc(3) is declared to return char*, even though we
know that the alignment of its return pointer is such that it can
be used for anything.  To get rid of that warning, try:

   output = (struct outfile (*)[])(int)malloc(sizeof(struct outfile) * 3);

Conversions of pointers to integral types and back are implementation-
defined, but this should work if int on your machine is wide enough
to hold a pointer.  If not, try long instead.

		 --Walter Murray

lmiller@venera.isi.edu (Larry Miller) (05/04/88)

In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>
>How do I cast the malloc() in line 12 of the following program to avoid 
>the lint cries of "warning: illegal pointer combination" et al?
>
>main()	
>{
>	char *malloc();
>	int i;
>	struct outfile {
>		int j;
>		int k;
>	} (*output)[];
>
>	output = malloc(sizeof(struct outfile) * 3);


Probably seen 100s of these by now, but here goes:


% cdecl
cast malloc into  pointer to array of struct outfile
Warning: Unsupported in C -- Pointer to array of unspecified dimension
(struct outfile (*)[])malloc(sizeof(struct outfile) * 3);

NOTE the warning!!
And note that the cast is almost the same as the definition of output, only
the name is removed.

Larry Miller				lmiller@venera.isi.edu (no uucp)
USC/ISI					213-822-1511
4676 Admiralty Way
Marina del Rey, CA. 90292

henry@utzoo.uucp (Henry Spencer) (05/05/88)

> Lint is complaining about the assignmemt, not because of the casting, but
> because it is not sure the byte address returned by malloc will allign the
> structure correctly. (It most likely will, but link cannot be sure).

Malloc in fact is required to, so there is no need for elaborate measures
to ensure it.  The problem is that lint does not know that malloc does in
fact guarantee alignment.
-- 
NASA is to spaceflight as            |  Henry Spencer @ U of Toronto Zoology
the Post Office is to mail.          | {ihnp4,decvax,uunet!mnetor}!utzoo!henry

njk@diku.dk (Niels J|rgen Kruse) (05/05/88)

In article <7822@brl-smoke.ARPA>, gwyn@brl-smoke.ARPA (Doug Gwyn ) writes:
> In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
> >How do I cast the malloc() in line 12 of the following program to avoid
> >the lint cries of "warning: illegal pointer combination" et al?
> (..)
> it.  The best you can do is obtain a "possible pointer alignment problem"
> warning.  That's because "lint" does not know that the pointer returned by
> malloc() is guaranteed to point to properly aligned storage for any use.
                                     ^^^^^^^^^^^^^^^^

    I have been wondering why malloc() returns a (char *), when
it is guaranteed to satisfy all alignment requirements. It
would seem to be more proper to return (maxalign *), where
maxalign is a type with the strictest alignment requirements of
all types. This would inform lint that the cast is safe.
In addition, passing (char *) is inefficient on some
architectures, whereas passing (maxalign *) would be efficient on
all architectures.

    I dont know anything about ANSI-C, other than what i have
picked up in this newsgroup. I gather that malloc has been
redefined as void *malloc(). Is a (void *) guaranteed to
satisfy all alignment requirements?
If so i would hope that this is reflected in the
representation, so that passing a (void *) is the most
efficient way to pass a pointer.
Changing declarations of malloc in existing code would be a
small price to pay for getting this cleaned up.


        Niels J|rgen Kruse
        stud. scient. at DIKU.
        (DIKU ~= CS dept. of University of Copenhagen)

jfh@rpp386.UUCP (John F. Haugh II) (05/06/88)

In article <7822@brl-smoke.ARPA> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes:
>In article <294@fedeva.UUCP> wrd3156@fedeva.UUCP (Bill Daniels) writes:
>>How do I cast the malloc() in line 12 of the following program to avoid 
>>the lint cries of "warning: illegal pointer combination" et al?
>
>General, SVR2 "lint" will complain about casting the (char *) returned
>by malloc() into other pointer types, and there seems to be no way around
>it.

my answer didn't make it out due to hardware trouble.  however, while
banging on the problem i tried solving the lint noise.  what i learned
was that if i declared

void	*malloc ();

then lint shut up.  this would seem to be the correct behavior for an
ANSI conforming lint.  i did get errors concerning the use of malloc,
but those could be corrected by properly declaring malloc (as above)
in llib-c.

well, karl, what you got to say???

- john.
-- 
John F. Haugh II                 | "You see, I want a lot.  Perhaps I want every
River Parishes Programming       | -thing.  The darkness that comes with every
UUCP:   ihnp4!killer!rpp386!jfh  | infinite fall and the shivering blaze of
DOMAIN: jfh@rpp386               | every step up ..." -- Rainer Maria Rilke

henry@utzoo.uucp (Henry Spencer) (05/06/88)

>     I have been wondering why malloc() returns a (char *), when
> it is guaranteed to satisfy all alignment requirements. It
> would seem to be more proper to return (maxalign *), where
> maxalign is a type with the strictest alignment requirements of
> all types. This would inform lint that the cast is safe.

If you accept the need to cast all results of malloc, there is nothing
inherently very wrong with this.  Note, however, that the type of maxalign
is implementation-dependent.  On some systems I'm not sure, actually, that
there is a specific type that expresses the maximal alignment constraint.
(Since I carefully avoid these silly machines that have segments, I'm not
intimate with the details of their flaws.)

> ...redefined as void *malloc(). Is a (void *) guaranteed to
> satisfy all alignment requirements?

No.  A "void *" is guaranteed to be able to hold any other pointer intact.
(Well, any other data pointer.)  This means that it has pretty much the
same characteristics as "char *", and in fact I think it is required to
use the same representation.  The purpose of "void *" is to be a real, true
pointer-to-anything, so that "char *" can go back to being specifically
pointer-to-char.  The properties of "void *" are also defined in such a way
that many conversions between it and other pointer representations do not
require explicit casts.

There is still no fully portable way to say "maximally aligned pointer".
If somebody wants to implement one and try it out, the next incarnation of
X3J11 would probably be interested in hearing about it when the time comes
to revise the C standard, several years from now.  I don't think there is
any actual implementation experience to justify trying to put it in now.
I'm not even quite sure how I would add this to the language:  one wants
something that shares "void *"'s property of not needing explicit casts,
while also being maximally aligned (which may not be expressible as an
ordinary C type).  Perhaps "struct *"?  (Ugh.)
-- 
NASA is to spaceflight as            |  Henry Spencer @ U of Toronto Zoology
the Post Office is to mail.          | {ihnp4,decvax,uunet!mnetor}!utzoo!henry

pedersen@acf3.NYU.EDU (paul pedersen) (05/06/88)

Walter Murray writes:
<The "possible pointer alignment problem" warning comes from the
<fact that malloc(3) is declared to return char*, even though we
<know that the alignment of its return pointer is such that it can
<be used for anything.  To get rid of that warning, try:
<
<   output = (struct outfile (*)[])(int)malloc(sizeof(struct outfile) * 3);
<
<Conversions of pointers to integral types and back are implementation-
<defined, but this should work if int on your machine is wide enough
<to hold a pointer.  If not, try long instead.


This is terrible, outrageous, and disgusting.  In order to get 'lint' to
shut up, this mechanism sacrifices portability!  The whole point of running
'lint' in the first place is to detect constructions that may work but are
not portable (I know there are other reasons for 'lint'-ing, but this is
the >primary< reason).

Better just to eat the lint complaints automatically with grep -v, as someone
else has already suggested.

gwyn@brl-smoke.ARPA (Doug Gwyn ) (05/07/88)

In article <3802@diku.dk> njk@diku.dk (Niels J|rgen Kruse) writes:
>... I gather that malloc has been redefined as void *malloc().
>Is a (void *) guaranteed to satisfy all alignment requirements?

No.  There are two distinct properties of the pointer returned by
malloc(): one is that it is a "generic" pointer, capable of being
safely converted to a pointer to any object type; the other is that
it is guaranteed to be properly aligned so that this conversion is
meaningful.  These two properties are NOT the same and should not
be bundled into a single language element.  The "super-aligned"
property is only true of pointers returned by malloc() and
therefore didn't seem to need a specific notion in the C language.
The "generic" property is essentially what ANSI C "void *" is
intended for.

ok@quintus.UUCP (Richard A. O'Keefe) (05/07/88)

In article <691@acf3.NYU.EDU>, pedersen@acf3.NYU.EDU (paul pedersen) writes:
> Better just to eat the lint complaints automatically with grep -v, as someone
> else has already suggested.

There is a slightly better way still.

#ifdef	lint
#define	Malloc(Type,Amount) ((void)malloc(Amount), (Type)(0))
#else
#define	Malloc(Type,Amount) (Type)malloc(Amount)
#endif
#define Talloc(Type) Malloc(Type*,sizeof (Type))

	typedef ..... Thing;
	Thing *p;

	p = Talloc(Thing);

lint #defines lint, so lint will see
	p = ((void)malloc(sizeof (Thing)), (Thing*)(0));
but the C compiler will see
	p = (Thing*)malloc(sizeof (Thing));

This shuts lint up about _only_ pointer alignment problems involving malloc(),
which is what you really want, as the others may be genuine.  When you find
other generic cases, you can fix them the same way.  That's what #ifdef lint
is for!

Another common use for #ifdef lint is for things like
	#ifndef lint
	static char copyr[] =
		"@(#)Copyright (C) 1988 Muppet Labs, Incorporated.";
	#endif
where lint would complain about copyr not being used.

thorinn@diku.dk (Lars Henrik Mathiesen) (05/11/88)

In article <1988May6.165221.536@utzoo.uucp> henry@utzoo.uucp (Henry Spencer) writes:
>There is still no fully portable way to say "maximally aligned pointer".
>...
>I'm not even quite sure how I would add this to the language:  one wants
>something that shares "void *"'s property of not needing explicit casts,
>while also being maximally aligned (which may not be expressible as an
>ordinary C type).  Perhaps "struct *"?  (Ugh.)

How about some `prior art' : teach lint about /*MAXALIGN*/, it knows about so
many special cases already. After all, it's not a language feature, it's a
feature of malloc(), so llib-lc is a good place for the information.
  The next problem is with compiler writers who insist on warning about such
things on compilation. They will have to put in support for /*MAXALIGN*/, and
put that in <malloc.h> (if it exists). Or use a #pragma. Or their users will
have to live with the warnings (most probable?).
--
Lars Mathiesen, DIKU, U of Copenhagen, Denmark      [uunet!]mcvax!diku!thorinn
Institute of Datalogy -- we're scientists, not engineers.

karl@haddock.ISC.COM (Karl Heuer) (05/14/88)

In article <3817@diku.dk> thorinn@diku.dk (Lars Henrik Mathiesen) writes:
>The next problem is with compiler writers who insist on warning about
>[possible alignment problems] on compilation.  They will have to put in
>support for /*MAXALIGN*/...  Or use a #pragma.  Or their users will have to
>live with the warnings (most probable?).

If they're going to add the warning message, they'd better make damn sure that
"int *ip = (int *)malloc(sizeof(int));", which is perfectly valid, not produce
the warning.  "Live with the warnings" is unacceptable.

If it's true that #pragma is not allowed to change the semantics of a program,
then this may be an excellent example of what it *is* good for.

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