[net.unix] allocating arrays

allbery@ncoast.UUCP (Brandon Allbery) (05/08/86)

Expires:

Quoted from <200@pyuxv.UUCP> ["Microsoft 'C' - Strange behaviour with doubles"], by cim2@pyuxv.UUCP (Robert L. Fair)...
+---------------
| Consider an array of 15 pointers to arrays of doubles:
| 
| 	double (*parray[15])[];
| 
| The following code to 'malloc' the actual double array barfs on Microsoft 'C',
| with a 'different levels of indirection' warning on the '='.
| The code passes through LINT, and compiles OK on UN*X 5.2
| 
| 	char	*malloc();
| 
| 	parray[0] = (double*)malloc((unsigned)sizeof(double)*75);
| 
| Microsoft produces the same error if the coercion is (double**), (double),
| or nothing at all !
| 
| Any ideas ?
+---------------

double (*parray[15])[]; means:

	an indefinitely-sized array of (or a pointer to)
		an array of 15
			(double *)

You want to allocate an array[15] of (double *)?  ``double *parray[15];''
                  ...an array of 15 (double)?  ``double parray[15][];''

My compiler (pcc) won't even TAKE ``double (*parray[15])[];'' unless it's
initialized or external; apparently sys5 converts it to the declaration
``double *(*parray[15]);'' -- ptr to array[15] of (double *) -- automatically.

--Brandon
-- 
decvax!cwruecmp!ncoast!allbery  ncoast!allbery@Case.CSNET  ncoast!tdi2!brandon
(ncoast!tdi2!root for business) 6615 Center St. #A1-105, Mentor, OH 44060-4101
Phone: +01 216 974 9210      CIS 74106,1032      MCI MAIL BALLBERY (part-time)
PC UNIX/UNIX PC - which do you like best?  See <1129@ncoast.UUCP> in net.unix.

throopw@dg_rtp.UUCP (Wayne Throop) (05/10/86)

Ever alert to save the world from mistaken C code examples that could
have been caught by simple use of lint or other commonly available
tools, our eagle eyed hero finds a particularly silly example.  Let's
listen in....

> allbery@ncoast.UUCP (Brandon Allbery)

> | Consider an array of 15 pointers to arrays of doubles:
> |     double (*parray[15])[];
> | The following code to 'malloc' the actual double array barfs on Microsoft 'C',
> | with a 'different levels of indirection' warning on the '='.
> | The code passes through LINT, and compiles OK on UN*X 5.2
> |     char    *malloc();
> |     parray[0] = (double*)malloc((unsigned)sizeof(double)*75);

> double (*parray[15])[]; means:
>       an indefinitely-sized array of (or a pointer to)
>               an array of 15
>                       (double *)

Wrong.  It means just what the original poster said it meant.  It is an
array of 15 pointers to arrays of double.  What the original poster is
mistaken about is that lint doesn't complain about the example.  In
particular, given this example

        void f(){
            double (*a[15])[];
            char *malloc();
            a[0] = (double *)malloc((unsigned)sizeof(double)*75);
        }
lint (on our system at least) says
        warning: illegal pointer combination
            (4)

I grant you, this isn't very informative, but lint *doesn't* like it,
that much is certain.  Let's run a more blabbermouth tool over it.

        #1052 4 inconsistent types discovered
              (=
                (:AREF
                  (:IDENTIFIER a :AUTO ... )
                  (:OCT_INT 0))
                (:CAST
                  (:POINTER_TO
                    (:DOUBLE))
                  (:FUNCALL
                    (:IDENTIFIER malloc :EXTERN ... )
                    (*
                      (:CAST
                        (:UNSIGNED)
                        (:SIZEOF
                           ... ))
                      (:DEC_INT 75)))))
            Types are:
              (:POINTER_TO
                (:ARRAY_OF
                  (:DOUBLE)
                  () ()))
            and:
              (:POINTER_TO
                (:DOUBLE))

As you can see, this is an attempt to assign a pointer-to-double to a
pointer-to-array-of-double.  In this case, it is easy to tell that this
is what is going on even without blabbermouth typecheckers.  The
declaration of a is of the form (*[])[], making the type of a[0] (*)[],
a pointer to an array.  The cast of the return value of malloc was
simply *, that is, a simple pointer.  If you want your compiler to shut
up, you should make the cast read (double (*)[]).  When the cast is
changed in this way, lint no longer complains.  Changing the cast to
(double **) naturally still causes complaint.

The problem as I see it is that the C compiler led the original poster
astray with a misleading error message.  The problem isn't with a wrong
level of indirection, but with an assignment of a value of type
pointer-to-scalar to an lvalue of type pointer-to-array.  The compiler
in question seems to have the common misconception that
pointers == arrays in C... they do not.

Now, with all that said, I suspect that what was really wanted was this:

        void f(){
            double *a[15];
            char *malloc();
            a[0] = (double *)malloc((unsigned)sizeof(double)*75);
        }

This is type-correct, and is probably what is wanted.  We have here an
array of 15 pointers to (implicitly, variable sized arrays of) double.
To reference the second double in the implicit array pointed to by the
third pointer in a, one would say a[2][1].  To say the same thing with
the original definitions would be (*a[2])[1].

> My compiler (pcc) won't even TAKE ``double (*parray[15])[];'' unless it's
> initialized or external; apparently sys5 converts it to the declaration
> ``double *(*parray[15]);'' -- ptr to array[15] of (double *) -- automatically.

Yes, the pcc seems to be another beastie with odd (and in some cases
incorrect) ideas about pointers and arrays.  Sigh.
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

david@ukma.UUCP (David Herron, NPR Lover) (05/14/86)

[My mailer doesn't know anything about dg_rtp.UUCP, could you please
let uucpmap@cbosgd know of your existance????]

In article <350@dg_rtp.UUCP> you write:
>> allbery@ncoast.UUCP (Brandon Allbery)
>
>> | Consider an array of 15 pointers to arrays of doubles:
>> |     double (*parray[15])[];
>> | The following code to 'malloc' the actual double array barfs on Microsoft 'C',
>> | with a 'different levels of indirection' warning on the '='.
>> | The code passes through LINT, and compiles OK on UN*X 5.2
>> |     char    *malloc();
>> |     parray[0] = (double*)malloc((unsigned)sizeof(double)*75);
>
>> double (*parray[15])[]; means:
>>       an indefinitely-sized array of (or a pointer to)
>>               an array of 15
>>                       (double *)
>
>Wrong.  It means just what the original poster said it meant.  It is an
>array of 15 pointers to arrays of double.  What the original poster is
>mistaken about is that lint doesn't complain about the example.  In
>particular, given this example

I respectively submit that you're full of it and that Brandon is correct.

Using []'s in a declaration is SIMILAR to declaring a pointer, 'cept
that the pointer is an internal constant in the compiler, and if you 
fill in a value inside the []'s it allocates some space.  A stumbling
point is when a [] declaration is an arg to a function, THAT gets
translated to a * declaration...

Some examples:

	char a[] = "this is a character string\n";
	char b[];

(oh, they're both external decl's)  Tell me tho, what USE is a 
the declaration of b[]????  Translating the decl to *b isn't
doing what I SAY, and (on Unix anyway) what I SAY is what I MEAN.
Anyway, compiling I get:

	Undefined:
	_main
	_b

_main is undefined because I don't have a main() obviously.
_b is undefined because it knew that somewhere there's a b[]
and made a reference to it.  

Modifying it to read:

char a[] = "this is a character string\n";
main()
{
	char b[], *c;
	char *malloc();

	printf("b = 0x%x\n", b);
	/* b = */ c = malloc(5);
	printf("b = 0x%x, c = 0x%x\n", b, c);
}

produces:

	b = 0x7fffe880
	b = 0x7fffe880, c = 0x2800

(BTW, this is a Vax running 4.2BSD, and we have the "hated" pcc,
a value like the one for b is a stack location)

Originally I had the b[] decl by itself and the compiler swallowed it.
(I have no idea what the compiler thought I wanted to do with a b[]...)
But it didn't like it when I had the b = uncommented...  (which is
what I'd expect).  Looking at the assembler output, b[] is a pointer
to the top of the stack but there's no space allocated to it.  There's
only space allocated for the *c.  

>
>        void f(){
>            double (*a[15])[];
>            char *malloc();
>            a[0] = (double *)malloc((unsigned)sizeof(double)*75);
>        }
>lint (on our system at least) says
>        warning: illegal pointer combination
>            (4)

Right.  the pcc says "illegal lhs" or something to that effect.  Which
is correct... there's no space allocated to your a.

>
>I grant you, this isn't very informative, but lint *doesn't* like it,
>that much is certain.  Let's run a more blabbermouth tool over it.
>
	... [deleted, for the sake of brevity, some very interesting output]
>
>As you can see, this is an attempt to assign a pointer-to-double to a
>pointer-to-array-of-double.  In this case, it is easy to tell that this
>is what is going on even without blabbermouth typecheckers.  The
>declaration of a is of the form (*[])[], making the type of a[0] (*)[],
>a pointer to an array.  The cast of the return value of malloc was
>simply *, that is, a simple pointer.  If you want your compiler to shut
>up, you should make the cast read (double (*)[]).  When the cast is
>changed in this way, lint no longer complains.  Changing the cast to
>(double **) naturally still causes complaint.

er... where did you get that program?  I agree with it somewhat EXCEPT
it's not making a distinction about CONSTANT pointers a la arrays. It's
an interesting program nonetheless...

I agree with you that the compilers need to produce better error messages.


-- 
David Herron,  cbosgd!ukma!david, david@UKMA.BITNET, david@uky.csnet

throopw@dg_rtp.UUCP (Wayne Throop) (05/22/86)

> david@ukma.UUCP (David Herron)
>> throopw@dg_rtp.UUCP (Wayne Throop)
>>> allbery@ncoast.UUCP (Brandon Allbery)
>>> | ???

> [My mailer doesn't know anything about dg_rtp.UUCP, could you please
> let uucpmap@cbosgd know of your existance????]

[It has been done, long ago.  We are (finally!) on the latest maps.
 Also, my .signature shows a path to dg_rtp from mcnc, and mcnc is quite
 well known (being part of the backbone, I beleive).]

>>> | Consider an array of 15 pointers to arrays of doubles:
>>> |     double (*parray[15])[];

>>> double (*parray[15])[]; means:
>>>       an indefinitely-sized array of (or a pointer to)
>>>               an array of 15
>>>                       (double *)

>>Wrong.  It means just what the original poster said it meant.  It is an
>>array of 15 pointers to arrays of double.

> I respectively submit that you're full of it and that Brandon is correct.

I exasperatedly submit that I'm quite empty of it, and that Brandon is
as wrong as wrong can be.  Also, after saying I'm wrong, your examples
go on to support my position strongly, so you've succeeded in puzzling
me no end, as well as prolonging this discussion beyond it's natrual
span.

But meanwhile, long ago, in an address space far, far away....

                                  POINTER WARS

                                The Evil Empire
                                has attempted to
                             perpetuate the fallacy
                           that pointers are the same
                          thing as arrays.  The brave,
                       outnumbered, noble, correct, long-
                 suffering rebel alliance has always maintained
               that the Force keeps pointers and arrays distinct.
              Our hero (moi) has lived a peaceful existance on the
             out-of-the-way node dg_rtp for years, until finally an
            empire star destroyer (which insists that stars ("*") in
         declarations can be replaced by brackets( "[]")) finds dg_rtp
                    via the news network, and opens fire....

                    Let's listen in, as the battle begins...

> [Shhhh-Pfoooo...      Shhhh-Pfoooo...]

[You can't fool me, Darth Vader!
 I know that's you behind the life-mask!]

> Using []'s in a declaration is SIMILAR to declaring a pointer, 'cept
> that the pointer is an internal constant in the compiler, and if you
> fill in a value inside the []'s it allocates some space.  A stumbling
> point is when a [] declaration is an arg to a function, THAT gets
> translated to a * declaration...

Yeah, right, [] is just like a pointer, except that it isn't, really.
Give me a break.  A [] declaration always declares an array, and arrays
and pointers are *NOT* (I'll repeat for those who have trouble
understanding this concept: *NOT*) the same thing.  The fact that array
formal arguments must "actually" be pointers is no more significant than
the fact that character formal arguments must "actually" be integers.

And while declaring *anything* is to some degree similar to declaring
anything else, pointers and arrays are not really very similar.

> Some examples:
>
>         char a[] = "this is a character string\n";
>         char b[];
>
> (oh, they're both external decl's)  Tell me tho, what USE is a
> the declaration of b[]????  Translating the decl to *b isn't
> doing what I SAY, and (on Unix anyway) what I SAY is what I MEAN.

What's the big deal?  This says that "a" is an array 28 characters long
(unless I've counted wrong... which is the reason for this shorthand in
the first place), and storage for "a" is allocated in this file, and
initialized to the given string.  In addition, you are saying that "b"
is an array of unknown size, and storage for it is *not* allocated in
this file (that's why it can be of unknown size... the storage will be
allocated somewhere else). (What use is "b"?  My good sir, what use is a
baby?)  Given all this, is it hardly surprising that

> Anyway, compiling I get:
>         Undefined: _b

since you implicitly promised to allocate space for "b" somewhere else,
and then didn't do it.  So where's the strangeness?  How does all this
flapdoodle support your contention that "double (*parray[15])[]" isn't
an array of pointers to arrays of double?

[   Let me be clear about what "shorthand" I'm talking about above.  In
    C, the declaration
                char a[] = "ab";
    is shorthand for
                char a[3] = {'a', 'b', '\0'};

    (though there is some debate as to whether that "'\0'" should be "0"
    instead, or exactly what '\0' means (does it mean zero or ascii
    null?))

    This is *NOT* the same as the declaration
                char *a = "ab";
    which isn't shorthand for anything, and which behaves quite
    differently.  The array declaraction allocates storage of size 3,
    and initializes those three characters to the values given.  The
    pointer declaration allocates storage for one pointer (on our
    machine, of size 4), and initializes it to point at the first
    character of a constant array of size 3.  As I say, quite different.
]

> [another example, simplified:
>       f(){
>                char b[];
>                printf( ... b ...);
>       }   ]
> Looking at the assembler output, b[] is a pointer
> to the top of the stack but there's no space allocated to it.

Actually, the declaration of b is illegal, and our compiler complains
bitterly about it.  I assume your compiler treated it as a zero-sized
array.  I doubt that it treated it as a pointer, as you suggest.
(Interestingly, lint doesn't complain about it.  It certainly ought to.)

>> [verbose typechecker output, deleted]
> er... where did you get that program?  I agree with it somewhat EXCEPT
> it's not making a distinction about CONSTANT pointers a la arrays. It's
> an interesting program nonetheless...

Well, the program is a typechecker that was locally written to check the
rules as laid out in Harbison and Steele, sort of the new testament to
K&R's old.  Sadly, I'm not currently at liberty to release it.

It is making all the distinctions it should.  It was created in essence
by going through H&S (as well as K&R), and translating the type rules
found there into checks performed against the parse tree of the input C
program.  Each error message is in fact related back to where H&S
justifies calling the construct an error.  I trust it a fair amount more
than I do lint (though, to my utter dismay, it didn't complain about
"f(){char b[];}" either... if anybody can justify why that should be
legal (as I say, our compiler complains bitterly), I'd appreciate a
message explaining why).

> I agree with you that the compilers need to produce better error messages.

Good.  I hope you now also agree that compilers should keep the
differences between pointers and arrays straight.  Keep away from the
Dark side of the Force, and you can't go wrong.

I stress again, the declaration

        int (*a)[];

is *NOT* (i say) *NOT* (i say again) *NOT* the same as the declaration

        int **a;

and any entity that thinks they *are* the same thing is,
quite simply, mistaken.  Trust me folks... I'm right about this.  If you
think these two are the same, you simply haven't thought about it
enough.

--
"Luke... use the Force!"
--
"He's turned his targeting computer off!"
--
Stay tuned for the next exciting episode,

        The Umpire Strikes Out

Coming soon to a netnode near you!
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

throopw@dg_rtp.UUCP (Wayne Throop) (05/23/86)

> david@ukma.UUCP (David Herron)
>> throopw@dg_rtp.UUCP (Wayne Throop)

Oh yeah, about that example the pcc says "illegal lhs" about...
(Don't know how I let this slip by before.  Oh well, I can fix it now.)

>>        void f(){
>>            double (*a[15])[];
>>            char *malloc();
>>            a[0] = (double *)malloc((unsigned)sizeof(double)*75);
>>        }
>>lint (on our system at least) says
>>        warning: illegal pointer combination
>>            (4)
>
> Right.  the pcc says "illegal lhs" or something to that effect.  Which
> is correct... there's no space allocated to your a.

Wrongo.  The array "a" is an array of 15 pointers to arrays of double
(is anybody getting bored with this?), and thus has 15 pointer-sizes
allocated for it, on the stack in this case.  Take this program for
example:

        void main(){
            double (*a[15])[];
            printf( "elements in a = %d\n",
                sizeof(a) / sizeof( double (*)[] ) );
        }

Now, let's examine this program with a debugger.  Positioning to this
module, and asking it to describe "a", we get

        ARRAY [ 0..14 ] OF POINTER TO ARRAY [ 0..0 ] OF FLOAT BOUND 64

That is, this is an array of pointers to arrays of doubles, just as I've
been trying to *tell* you.  In particular, there are 15 pointers in this
array (that is, the bounds are 0 to 14).  But let's see how much space
the compiler has allocated for this array on the local stack frame.  We
position to main, and disassemble the first instruction (the allocation
of stack space), and find

         WSAVR   17

OK.  That's 15 (octal 17) 32-bit words of storage allocated to "a".
Finally, let's run the blasted thing, and see how many elements *IT*
thinks it allocated for "a":

    elements in a = 15

If the pcc says that the assignment is illegal, that's fine with me
since it (sort of) is.  But it had better be able to accept

        a[0] = (double (*)[])malloc((unsigned)sizeof(double)*75);

Now *this* assignment is perfectly legal, since a[0] is a pointer to
an array of double (that is, (double (*)[])).

Fairly conclusive, I'd say.  Are y'all convinced?

--
   "I've seen things you people wouldn't believe. Attack ships on fire
off the shoulder of Orion. I watched C-Beams glitter in the dark near
the Tanhauser Gate. All those moments will be lost in time, like tears
in rain. Time to die."
                         Roy Baty, N6MAA10816, Nexus6, Combat Model
--
   "I've seen blunders you people wouldn't believe. Pointers confused
with arrays as arguments. I watched C-hackers make stupid mistakes on
net.lang.c.  All of these moments will be lost in time, like a posting
on net.philosophy.  Time to logout."
                        Wayne Throop, throopw@dg_rtp, Comedy Model
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw