[comp.lang.c] ptrs and arrays

janm@eliot.UUCP (Jan Morales/Development) (11/10/89)

In article <304@jhereg.Minnetech.MN.ORG> Mark H. Colburn writes:
>In article <2640@dogie.macc.wisc.edu> Ross Yahnke writes:
>>Is there a more graceful way to do the following? I allocate
>>a chunk 'o' mem and keep a ptr to it, I want to access the
>>mem as an integer array and inc different elements of the array.

If I read Ross' problem correctly, all you really need is:

foo()
{
	int *aPtr;
	char *calloc();

	aPtr = (int *) calloc(NUM_OF_INTS_IN_ARRAY, sizeof(int));
	aPtr[10]++;
}

That last line of code will increment the eleventh integer in the
"chunk" of memory (aPtr[0] being the first).  Keep in mind that you
should check for a NULL pointer from calloc meaining there's not
enough memory for your request.

Jan
-- 
{uunet,pyramid}!pyrdc!eliot!janm

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

>In article <2640@dogie.macc.wisc.edu> yahnke@vms.macc.wisc.edu
>(Ross Yahnke, MACC) writes:
>>... I want to [malloc and] access [a chunk of memory]
>>as an integer array and inc different elements of the array.

In article <304@jhereg.Minnetech.MN.ORG> mark@jhereg.Minnetech.MN.ORG
(Mark H. Colburn) writes:
>What you really want is something like this:
>
>   { 
>	int	**aPtr;
>	char     *calloc();
>
>	aPtr = (int **)calloc(10, sizeof(int));
>	aPtr[10]++;
>   }

Mark, you should know better than this (especially since we just went
through this in this very newsgroup).  For what Ross described, he
wants something like

	int i, n;
	int *b; /* base of the chunk of memory */

	n = how_ever_many_we_want();
	b = (int *)malloc(n * sizeof(int));
	/*
	 * or
	b = (int *)calloc(n, sizeof(int));
	 */
	if (b == NULL) {
		help, allocation failed, call in the Marines,
		call the President, dump core!
	}
	/*
	 * The following is unnecessary with calloc, but
	 * only for certain kinds of data (bytes and things
	 * made up of integral bytes, as opposed to weird
	 * bytes like those in pointers or float or double).
	 */
	for (i = 0; i < n; i++)
		b[i] = 0;
	/*
	 * now `b[k]++' is legal for all k in [0..n).
	 */

>Using the "int **" for the data type, you are allocating space for an
>array of ints, and acessing it as such...

malloc (and calloc, which is simply a wrapper for malloc+memset)
simply obtains a `lump' of memory.  In principle, the cast, or if
there is no cast, the variable, to the left of the call to malloc
gives that lump its shape.  That shape must match up, in some way,
to the number of bytes requested.

To do the matching, take the type---(int **), (int *), or whatever;
no matter what, it will be some sort of `pointer to T'---and take
out the `pointer to' part.  If the call is, for instance,

	p = (thistype *) malloc( ...

the type is `pointer to thistype', so take out the `pointer to' and
get `thistype'.  Now look at the argument to malloc, or the second
argument to calloc.  It had better be `sizeof(T)', or some multiple
of sizeof(T):

	p = (thistype *) malloc( sizeof(thistype) ); /* right */
	p = (thistype *) calloc( 1, sizeof(thistype) ); /* right */
	p = (thistype *) malloc( n * sizeof(thistype) ); /* right */
	p = (thistype *) calloc( n, sizeof(thistype) ); /* right */
	p = (thistype *) calloc( 1, n * sizeof(thistype) ); /* a bit weird,
							but otherwise OK */
	p = (thistype *) malloc( 123 );	/* wrong */
	p = (thistype *) malloc( sizeof(othertype) ); /* wrong */
	p = (thistype *) malloc( sizeof(thistype *) ); /* wrong */

This applies no matter how many stars there are:

	p = (foo **) malloc( ...

Here the type is `pointer to pointer to foo', so take out the (first)
`pointer to', and get `pointer to foo'.  The argument to malloc had
better be sizeof(foo *), or some multiple thereof:

	p = (foo **) malloc( n * sizeof(foo *) ); /* right */
	p = (foo **) malloc( n * sizeof(foo) ); /* wrong */

If the type and size do not match up in this fashion, the run-time
behaviour of the code is undefined.  (Your computer might call in the
Marines, then dump core all over them; this might make them rather mad
at you, and you probably would not want that. :-) )

But that is not the only problem with this code:

>	aPtr = (int **)calloc(10, sizeof(int));

The type and size do not match up.  If we had

	aPtr = (int **)calloc(10, sizeof(int *));

they would, and aPtr[i] would have type=`int *' and value=junk; or
if we changed the declaration of `aPtr' to `int *aPtr' and changed
the code to

	aPtr = (int *)calloc(10, sizeof(int));

they would again, and aPtr[i] would have type=`int' and value=0.
But:

>	aPtr[10]++;

aPtr would only point to the first of ten `int's, and aPtr[10] names
the eleventh such `int'.  (The first 10 are numbered 0 through 9.)
Again, the behavour is undefined, and who knows what mean old Mr.
Computer would do this time, like maybe call the CIA and report you
for selling secrets to the Russians....

Oops, I see it is time for my medication :-)
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

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

In article <0156@sheol.UUCP> throopw@sheol.UUCP (Wayne Throop) writes:
>As is (essentially) invariably the case, the argument Chris presents
>against the usefulness of utterable array types of unknown size is
>both cogent and correct.  I think it misses a small point, however.

I did this deliberately:

>The point is controlled information hiding.

I have a (perhaps weak) argument against this particular usage.  I was
going to include it, but cannot think of any decent way to phrase it
(this is one of the reasons I cannot tell whether it is really any good).
All I can do is point out something about the wording here:

>Such types (it seems to me) are perfectly appropriate in header files,
>where the decision of actual array size can be delayed until link time
>safely,

In other words, we have something like

	extern sometype somearray[];	/* unknown size */

This much is justifiably necessary---among other things, it is the right
way to refer to variables such as `sys_siglist' and `etext' (these are
specific to Unix, but no doubt there are other examples).

>and yet provide for some operations (encapsulated in macros
>defined in the same header file preferably) on the element type of the
>array.

But look at what you just said: operations on the *element type* of the
array: not operations on the array as a whole.  Operations on these
elements need pointers to the elements, not a pointer to the array.

To put it another way (in another weak argument), I have never seen a
situation where I could justify `sometype (*p)[]', even when dealing
with someone else's array, whose type I knew, but size I did not.
Moreover, I have had to know that something was an array type, even
when its elements were hidden.  For instance, `jmp_buf' must be an
array type.  The `array-ness' of an object shows through, because only
arrays can be modified by a call that does not have an `&':

	f(x);	/* x is never modified, but x[i] could be */
	f(&x);	/* x could be modified (and x[i] could be as well) */

Arrays often get short shrift in C, and I think this applies to information
hiding as well.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

throopw@sheol.UUCP (Wayne Throop) (11/11/89)

> chris@mimsy.umd.edu (Chris Torek)
> But look at what you just said: operations on the *element type* of the
> array: not operations on the array as a whole.  Operations on these
> elements need pointers to the elements, not a pointer to the array.

Yes.  But the manipulations are hidden inside procedures or macros.
The manufacture of the address of the entity to be manipulated might
also be encapsulated, but if the "&" unary operator is to be used in
any reasonable way (which is what I meant by *partial* information
hiding... the addressability is not abstracted), then one must be
able to utter the type of the resulting expression, so that it can
(for example) properly be passed to routines which will manipulate
it (and which know the correct size).

I admit that there are ways of avoiding this problem.  For example,
encapsulating the address-of operation, and having it yeild void or
pointer-to-element-type.  For another, have the interface specify
a pointer-to-void in a prototype, so that a cast will occur automagically.

I admit this as I say... But I also think it would be cleaner to be
able to simply utter the "correct" type.  Marginally cleaner, but
cleaner nonetheless.  (Or maybe it IS "theless", but it's *still*
cleaner notwhithstanding.)      (or should that be irregardless....)

Just think of how often people wish the jmp_buf type were cleaner
in just this sort of way.


> Arrays often get short shrift in C, and I think this applies to information
> hiding as well.

Very definitely agreed.  And we all know how painful *that* can be.
--
Wayne Throop <backbone>!mcnc!rti!sheol!throopw or sheol!throopw@rti.rti.org