[comp.lang.c] A pointer to a 3-D array, or an array of ptrs to 2-D arrays?

MARWK@levels.sait.edu.au (11/15/89)

Why does (1) work but (2) does not?

(1) p = (int (*) [13][2][64]) malloc (sizeof(int) * 13 * 2 * 64)
    
    *p[i][j][k] = 6

(2) p = (int (*) [2][64]) malloc (sizeof(int) * 13 * 2 * 64)

    p[i][j][k] = 6

These are both supposed to be ways of dynamically creating a 3-dimensional
array for accessing as such, but (1) causes a segmentation error when run
on the ENCORE.
Thanks in advance.

Ray

chris@mimsy.umd.edu (Chris Torek) (11/15/89)

In article <2765@levels.sait.edu.au> MARWK@levels.sait.edu.au writes:
>Why does (1) work but (2) does not?
>(1) p = (int (*) [13][2][64]) malloc (sizeof(int) * 13 * 2 * 64)
>    *p[i][j][k] = 6
>(2) p = (int (*) [2][64]) malloc (sizeof(int) * 13 * 2 * 64)
>    p[i][j][k] = 6

(I think you have this backwards: (1) fails and (2) works)

>These are both supposed to be ways of dynamically creating a 3-dimensional
>array for accessing as such, but (1) causes a segmentation error when run
>on the ENCORE.

(Aha, you think you have it backwards too :-) )

You need to read my recent treatises on `pointers and arrays'.

The short explanation for the failure of (1):

	*p[i][j][k]

is the same as

	*(((p[i])[j])[k])

which is the same as

	p[i][j][k][0]

which means to add i*sizeof(*p) to p, which will add i*13*2*64*sizeof(int)
bytes to whatever value is in p, which, if i is more than 0, will be
outside of the region allocated by malloc.

The code in (2) is much more `C-like': p is a pointer to an array 2
of array 64 of int, which points to the first of 13 consecutive such
things.  In (1), p is a pointer to an array 13 of array 2 of array 64
of int, and it points to the first of one such thing, so you must always
write (*p) or (p[0]) in order to get to that thing before you can
do anything useful with it.

(All of the above assumes p is properly declared.)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

cpcahil@virtech.uucp (Conor P. Cahill) (11/15/89)

In article <2765@levels.sait.edu.au>, MARWK@levels.sait.edu.au writes:
> Why does (1) work but (2) does not?
> 
> (1) p = (int (*) [13][2][64]) malloc (sizeof(int) * 13 * 2 * 64)
>     
>     *p[i][j][k] = 6
> 
> (2) p = (int (*) [2][64]) malloc (sizeof(int) * 13 * 2 * 64)
> 
>     p[i][j][k] = 6

This stuff depends upon how you declare p not really on how you cast
the return from malloc().

if p is declared as

	int  (*p) [13][2][64];

then p is a pointer to an array[13][2][4] of ints.  Therefore to 
place a value into position i,j,k you need to:

	(*p)[i][j][k] = 6;

which says place a 6 into element i,j,k of the array to wich p points.

	p[i][j][k] = 6;

says place a 6 into the i,j,k'th occurance of such an array starting
at the location p.

	*p[i][j][k] = 6;

says place a 6 into the location pointed to by the data at the i,j,k'th
occurance of such an array starting at location p.

The following program will show the compilers interpretation of those
statements:

	main()
	{
		int	(*p) [13][2][4];

		p = (int (*) [13][2][4]) malloc(sizeof(*p));

		(*p) [12][1][3] = 5;

		printf("%d\n", p);
		printf("%d\n", sizeof(p));
		printf("%d\n", sizeof(*p));
		printf("%d\n", p[0][0][1]);
		printf("%d\n", (*p)[12][1][3]);
	}


	4202744		<== value of pointer p
	4		<== size of p
	416		<== size of what p points to
	4202760		<== value of p[0][0][1]      **
	5		<== correct value at 12,1,13

** note that p[0][0][1] is equal to the p (4202744) + sizeof(*p) (416).  Which
is what you should expect.

Also note that *p[12][1][3] caused a core dump since it is way beyond the end
of address space for this program.
-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

jnh@ecemwl.ncsu.edu (Joseph N. Hall) (11/15/89)

As an aside, I've found it useful to dynamically allocate matrices like
this (hastily paraphrased, it might have a typo or two in it):

	double **NewDoublem(int isize, int jsize)
	{
		int i;
		double **doublem, *doublev;

		doublem = (double **) malloc(sizeof(double *) * isize);
		doublev = (double *) malloc(sizeof(double) * isize * jsize);

		for (i = 0; i < isize; i++) {
			doublem[i] = doublev + i * jsize;
		}

		return doublem;
	}

This has the advantage over the more frequently-seen approach (allocate a
separate vector for each row) that you can free such a "matrix" without
knowing its size:

	free((void *) *doublem);
	free((void *) doublem);

...and, of course, you make fewer calls to malloc.

Hope this is a useful and not tiresomely obvious suggestion for some of you.



v   v sssss|| joseph hall                      || 4116 Brewster Drive
 v v s   s || jnh@ecemwl.ncsu.edu (Internet)   || Raleigh, NC  27606
  v   sss  || SP Software/CAD Tool Developer, Mac Hacker and Keyboardist
-----------|| Disclaimer: NCSU may not share my views, but is welcome to.

peter@ficc.uu.net (Peter da Silva) (11/16/89)

In article <2765@levels.sait.edu.au> MARWK@levels.sait.edu.au writes:
> (1) p = (int (*) [13][2][64]) malloc (sizeof(int) * 13 * 2 * 64)
>    *p[i][j][k] = 6

Try !(*p)[i][j][k]!. This is why I wish indirection was postfix. In
"My Ideal C" this would be !p^[i][j][k]!.

(I'm going to start a C-FUTURES mailing list soon, for discussions of
this nature. Mail to peter@ficc.uu.net if you want to join.)
-- 
`-_-' Peter da Silva <peter@ficc.uu.net> <peter@sugar.hackercorp.com>.
 'U`  --------------  +1 713 274 5180.
"*Real* wizards don't whine about how they paid their dues"
	-- Quentin Johnson quent@atanasoff.cs.iastate.edu

chris@mimsy.umd.edu (Chris Torek) (11/20/89)

In article <1989Nov15.145302.3906@virtech.uucp> cpcahil@virtech.uucp
(Conor P. Cahill) writes:
>if p is declared as
>	int  (*p) [13][2][64];
>then p is a pointer to an array[13][2][4] of ints.

More precisely, p is a pointer that can point to zero or more contiguous
such objects, and:

>... to place a value into position i,j,k you need to:
>	(*p)[i][j][k] = 6;
>which says place a 6 into element i,j,k of the array to which p points.

this puts a 6 into the i/j/k element of the first (0th) array to which
p points.

>	p[i][j][k] = 6;
>says place a 6 into the i,j,k'th occurance of such an array starting
>at the location p.

I think this is confusing.  p[i] names the i'th object to which p points;
this is an `array 13,2,4 of int'; then the [j] names the j'th object to
which the pointer one gets by The Famous Rule points, and this is an
`array 2,4 of int'; the [k] then names the k'th object to which the
pointer one gets by The Rule points, which is an `array 4 of int'.  This
is not a modifiable object, so the assignment is not legal.

>	*p[i][j][k] = 6;
>says place a 6 into the location pointed to by the data at the i,j,k'th
>occurance of such an array starting at location p.

Since the binding is *(p[i][j][k]), the resolution of p[i][j][k] is as
above, but the result is in a value context, not an object context.  It
therefore becomes a pointer (by The Rule) and *(the-pointer-we-just-got)
names the 0'th element of that k'th array of that j'th array of that
i'th array of objects-to-which-p-points.  This *is* a modifiable object
(assuming it has not been declared `const') and the assignment is
legal, providing that p really points to at least i contiguous objects,
and providing that j and k are in bounds.

>Also note that *p[12][1][3] caused a core dump since it is way beyond
>the end of address space for this program.

Right: p only points to one object, and p[12] asks for the thirteenth
(p[0] being the first).
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris