dynamically allocating array of struct

Walter L. Peterson, Jr. wlp at calmasd.Prime.COM
Thu Apr 6 15:42:01 AEST 1989


In article <3658 at uhccux.uhcc.hawaii.edu>, cs411s03 at uhccux.uhcc.hawaii.edu (Cs411s03) writes:
> 
> I am having difficulty writing a program which dynamically allocates
> an array of structs via malloc() and casting. The program seems to
> compile OK, but at run time I am addressing the SAME struct over and
> over, I want to step thru them with an index. The program goes like
> so:
> 
> /* malloc struct test */
> 
> #include <stdio.h>
> #include <malloc.h>
> 
> #define NUMRECS 60
> 
> struct ttst {
> 	int num;
> };
> 
> struct ttst (*tptr)[];
> 
> main()
> {
> 	int i, j;
> 
> 	if ((tptr = (struct ttst (*)[]) \
> 	malloc(sizeof(struct ttst) * NUMRECS)) == (struct ttst (*)[]) 0) {
> 		perror("malloc");
> 		exit(1);
> 	}
> 
> 	printf("sizeof = %d\n", sizeof(struct ttst) * NUMRECS);
> 
> 	for (i = j = 0; i < NUMRECS; i++) {
> 		tptr[i]->num = ++j;
> 		printf("Rec: %x %d %d\n", tptr[i], i, tptr[i]->num);
> 	}
> 
> 	printf("---------------\n");
> 
> 	for (i = 0; i < NUMRECS; i++)
> 		printf("Rec: %x %d %d\n", tptr[i], i, tptr[i]->num);
> 
> 	free(tptr);
> 	exit(0);
> }
> 
> The program will do much more than this once completed, this program
> was just to test the basic concept of dynamically allocating the
> structures rather than just declaring a fixed length array ...
> 
> The first loop looks as if it is working, but the second loop proves it
> is not. It seems that the same structure is being written to over and
> over. I must be missing something fundamental here ...
> 
> cs411s03!uhccux



Yes, you are missing something, but don't feel bad, this DOES seem the
intuitive way to do it.  I don't know what system you are on, but I have
tried your original code on Pyramid (BSD & SYSV), SUN (SunOS (mostly SYSV))
and TURBO-C (Messy-DOS).  None of these have malloc.h, so that had to
be changed to start.  Sun gave the compile warning, zero-length array
element on each line that referenced tptr[i], Pyramid compiled OK and 
TURBO-C, which is pretty much ANSI, had fatal errors and failed to
compile at all.  It did not like the tptr[i]->num.

On both SUN and Pyramid it behaived as you said; the first loop looked
OK, but the second didn't.  Which is just what I would expect. The
solution to this problem is simple once you know a few things about
the way C treats arrays and subscripts. The resulting code is also,
IMHO, "cleaner" and very easy to read.

First: the declaration of the pointer is simpler; just declare a
pointer to the structure:

       struct ttst *tptr;

no blank subscripts or other jazz.

Second: I would suggest using calloc rather than malloc for several
reasons. Reason one is that calloc's arguments make what you are
trying to do clearer. The first arg is the number of objects for which
you are allocating space and the second arg is the size of one of
those objects.  The second reason is that calloc will "zero-out" the
memory space that it allocates; malloc dosn't (typically). Calloc
returns the value of the memory location at the start of the block of
memory allocated, which must be cast to the type of object being
pointed to; that is:

    tptr = (struct ttst *)calloc(NUMRECS, sizeof(struct ttst));

This gives you a pointer, tptr, to a block of initialized memory that
is NUMRECS * sizeof(struct ttst) bytes long and ensures that C now
knows that the pointer tptr points to things that are sizeof(struct ttst) 
long.

It is at this point that an understanding of the way C handles arrays
and their subscripts comes in.  In C the UNSUBSCRIPTED name of an
array is in fact a POINTER TO THE BEGINING OF THE ARRAY.  For example,
if I declare foo to be an array of 20 integers, then the unsubscripted
name foo points to the start of the array.

C then uses the subscripts as offsets from the array's starting
address, each offset being as long as the type of the array elements,
to find each array element.  Since in this case we are dealing with a
structure we will also need an additional offset to get to each
element of the structure at each array element; that is done using the
standard structure dot notation, NOT the structure pointer ->
notation. The reason for this is (C super-wizards no flames please if
this explaination is not *STRICTLY* "by-the-book"; I'm recalling this
off the top of my head.) that, since you are using the subscript
notation, tptr[i], C is interpreting tptr as the name (and thus
address) of an array, each ELEMENT of which is of type struct ttst
and not as a pointer to a struct ttst.  This means that the notation:

         tptr[i]->num

is wrong (TURBO-C refused to compile using this notation) and should
be:

         tptr[i].num


The following code compiles and runs on SUN, Pyramid and TURBO-C and
provides the expected output:
      


/* malloc struct test */

#include <stdio.h>

#define NUMRECS 60

struct ttst {
	int num;
};

main()
{
    int i;
    struct ttst *tptr;

    if ((tptr = (struct ttst *)calloc(NUMRECS, sizeof(struct ttst))) == NULL){
        perror("malloc");
	exit(1);
    }

    printf("sizeof = %d\n", sizeof(struct ttst) * NUMRECS);

    for (i = 0; i < NUMRECS; i++) {
        tptr[i].num = i;
	printf("Rec: %x %d %d\n", tptr[i], i, tptr[i].num);
    }

    printf("---------------\n");

    for (i = 0; i < NUMRECS; i++)
        printf("Rec: %x %d %d\n", tptr[i], i, tptr[i].num);

    free(tptr);
    exit(0);
}


The only complaint that any of the compilers give is a "warning:
structure passed by value" for the printing of tptr[i], using the %x
format. I'm not too certain what you were expecting here; you DONT get
the address.  What you DO get in this example is the hex value of 
tptr[i].num.

Also, notice the change in the first loop. There is no need for your
variable "j".

I hope all this helps.


---------------------------------------

Walt Peterson

wlp at calmasd.Prime.COM
-- 
Walt Peterson.  Prime - Calma San Diego R&D (Object and Data Management Group)
"The opinions expressed here are my own and do not necessarily reflect those
Prime, Calma nor anyone else.
...{ucbvax|decvax}!sdcsvax!calmasd!wlp



More information about the Comp.lang.c mailing list