David Morris
David Morris

Reputation: 41

Problems with structures with pointers to arrays of other structures in C

I am attempting to tackle college worksheet on C programming (no marking for it, just to improve our learning). What we're meant to do is get a few details about shipping docks. I decided to use structures for this.

My code is below, what I need help with is to print out the information (to see if its working) of whats at the location of the shipyards .run.

Everything compiles and according to the debugger shipyard1.run and shipyard2.run point to different locations, but I can not see the values.

int main(int argc, char** argv)
{
    typedef struct dockInfo
    {
        int dockCode;
        int dockLength;
    }dckDetails;

    typdef struct shipyard
    {
        char dockName[20];

        /* however big this number is thats how many dockInfo structs are needed.*/

        int numOfDocks;             
        dckDetails *run; //points to the array of dockInfo structs

    };

    struct dockInfo *arrayD; // the array to hold all the dockInfo structs
    struct dockInfo tempo; // the temporary dockInfo struct to take in the details
    struct shipyard shipyard1;
    struct shipyard shipyard2;

    /**
    * the variables for shipyard1 and shipyard2 are then assigned
    **/

    int i;
    for (i=0;i<shipyard1.numOfDocks;i++)
    {

    arrayD=calloc(shipyard1.numOfDocks,100); // allocate a new bit of memory for arrayD
    tempo.dockCode=45*i;
    tempo.dockLength=668*i;
    arrayD[i]=tempo; //the element of arrayD becomes tempo.

    }
    shipyard1.run=arrayD; //make shipyard1.run point to the location of arrayD.

    for (i=0;i<shipyard2.numOfDocks;i++)
    {

    arrayD=calloc(shipyard2.numOfDocks,100); // allocate a new bit of memory for arrayD
    tempo.dockCode=1234*i;
    tempo.dockLength=1200*i;
    arrayD[i]=tempo; //the element of arrayD becomes tempo.

    }
    shipyard2.run=arrayD; //make shipyard2.run point to the new location of arrayD.

    int elementTest1; // need element1test to be shipyard1.run[0].dockLength;
    int elementTest2; // need element2test to be shipyard2.run[1].dockCode;

return (EXIT_SUCCESS);
}

It should be noted that I have left a lot of code out because I have yet to write it. I have used static examples for the moment (shipyard1 and shipyard2) but in the future I am going to implment a 'load info from file' feature.

Any help would be greatly appreciated and please excuse my English if it's poor, English is not my first language.

Upvotes: 0

Views: 627

Answers (4)

Bill IV
Bill IV

Reputation: 205

I shortened the variable names and re-wrote this to do what I think you are interested in. I also added display of the addresses the data is stored in. Generally, when I try to understand something in the arrays and pointers world, I make the simple case work - an embedded array (my yard1) and then do the pointer thing after that (yard2, yard3) You'll note each set of data has different start points, two add i for each point, one multiplies by i for each point.

#include <libc.h>
#include <stdlib.h>
#include <stdio.h>
#define MAX_DOCKS 100

int main(int argc, char** argv)
{

    struct dock
    {
        int code;
        int length;
    };

    struct yard
    {
        char name[20];
        int numDocks;             
        struct dock arDocks[MAX_DOCKS]; //an array of dock structs
    };

    struct yard_2 
    {
        char name[20];
        int numDocks;             
        struct dock *run; //points to the array of dock structs
    };

/* data within main function */

struct dock *arrayD; // pointer to dock structs
struct yard yard1;
struct yard_2 yard2;
struct yard_2 yard3;

int i;
char temp[] = "2 draY";
strcpy( yard2.name, temp );  /* temp is only persistant in main... */
strcpy( yard1.name, "Yard 1");  
strcpy( yard3.name, "3 y 3 a 3 r 3 d 3");
yard1.numDocks = MAX_DOCKS; /* or so I guess.. */
yard2.numDocks = MAX_DOCKS; /* or so I guess.. */
yard3.numDocks = MAX_DOCKS; /* or so I guess.. */


/* get some memory, init it to 0 */
arrayD = calloc( yard2.numDocks, sizeof( struct dock ) );
    /* connect to the yard2 struct via "run", a pointer to struct dock */
yard2.run = arrayD;    

/* without middleman... get more memory, init it to 0 */
yard3.run = calloc( yard3.numDocks, sizeof( struct dock ) );

/* at this point arrayD could be re-used to get another hunk.. */

/* fill in and display data .. */

for (i=0;i<yard1.numDocks;i++)
    {
    /* This sets  all the memory for yard 1... */
yard1.arDocks[i].code = 45 + i;
yard1.arDocks[i].length = 668 + i;

/* so here are some ways to display the data */

    printf("%d,    %d    %x   %d   %x   -   ", 
       i, yard1.arDocks[i].code, &(yard1.arDocks[i].code),
       (yard1.arDocks[i].length), &(yard1.arDocks[i].length) );

    /* This sets the memory for yard 2... */
yard2.run[i].code = 45 * i;
yard2.run[i].length = 668 * i;

/* Display through a pointer to a calloc'ed array of structs is the
 same syntax as the embedded array of structs. The addresses of the 
 array are completely different - 0xbffff704 vs 0x800000  on my Intel-based iMac... */

    printf("%d    %x   %d   %x   -   ",
       yard2.run[i].code, &(yard2.run[i].code),
       yard2.run[i].length, &(yard2.run[i].length) );

yard3.run[i].code = 100 + i;
yard3.run[i].length = 2000 + i;

/* see where second calloc got its memory... */

    printf("%d    %x   %d   %x\n", 
       yard3.run[i].code, &(yard3.run[i].code),
       yard3.run[i].length, &(yard3.run[i].length) );
    }

/* data all filled in, more demos of how to get it back: */

printf( "%s,     : 1\n", yard1.name );
printf( "%d,     : numOfDocs \n", yard1.numDocks );
printf( "0x%x,     : arDocks \n", yard1.arDocks );


int elementTest1 = yard1.arDocks[0].length;
int elementTest2 = yard1.arDocks[1].code;
int elementTest3 = yard2.run[0].length;
int elementTest4 = yard3.run[1].code;

printf( "elementTest1:  yard1.arDocks[0].length  %d\n", elementTest1 );
printf( "elementTest2:  yard1.arDocks[1].code  %d\n", elementTest2 );
printf( "elementTest3:  yard2.run[0].length  %d\n", elementTest3 );
printf( "elementTest4:  yard3.run[1].code;  %d\n", elementTest4 );

for (i=0; i< yard2.numDocks; i++ ) {
printf("%d   %d   %d    _   ", i, yard2.run[i].length, yard2.run[i].code);
printf(" %d   %d \n",  yard3.run[i].length, yard3.run[i].code);
}


return (EXIT_SUCCESS);
}

Here's an edited example of the output, compile/build via cc, cmd line a.out:

Macintosh-6:interview Bill4$ cc

dockyard.c Macintosh-6:interview

Bill4$ a.out 

0  45  bffff6f8  668 bffff6fc - 0   800000  0   800004 -  100 800400  2000 800404 
1  46  bffff700  669 bffff704 - 45  800008  668 80000c -  101 800408  2001 80040c
2  47  bffff708  670 bffff70c - 90  800010 1336 800014 -  102 800410  2002 800414 
:
Yard 1,     : 1
100,     : numOfDocs 
0xbffff6f8,     : arDocks 
elementTest1:  yard1.arDocks[0].length  668
elementTest2:  yard1.arDocks[1].code   46
elementTest3:  yard2.run[0].length  0
elementTest4:  yard3.run[1].code;  101
 0   0   0    _    2000   100 
 1   668   45    _    2001   101 
 2   1336   90    _    2002   102 
 3   2004   135    _    2003   103  
 :
 99   66132   4455    _    2099   199 

Macintosh-6:interview Bill4$

Upvotes: 0

Alok Singhal
Alok Singhal

Reputation: 96121

Others have made some very good points, and you should fix your code according to them. So far, no one seems to have seen that the call to calloc() is wrong. Instead of:

arrayD=calloc(shipyard1.numOfDocks,100);

it should be:

arrayD = calloc(shipyard1.numOfDocks, sizeof *arrayD);

You want shipyard1.numOfDocks objects, each of size equal to sizeof *arrayD.

In fact, as mentioned below, you don't need to set the memory allocated to all-zeros, so you can replace calloc() by malloc():

arrayD = malloc(shipyard1.numOfDocks * sizeof *arrayD);

(Be sure to #include <stdlib.h>, whether you call calloc() or malloc().)

I have some minor comments about style:

  1. you don't need the typedef. You can write struct dockInfo instead of dckDetails. If you do keep the typedef, you should be consistent, and use the typedef name everywhere. You use struct dockInfo most of the time, and then use dckDetails once. Your usage suggests that you probably weren't comfortable declaring a pointer to the struct. However, struct dockInfo *run is a completely valid declaration.
  2. you don't need the tempo object. You can instead do: arrayD[i].dockCode = 45*i; arrayD[i].dockLength = 668*i;
  3. Unless you're running C99, you can't declare variables after statements in a block. So you should move the declarations for elementTest1 and elementTest2 to the top of main(), with other declarations.
  4. return is a statement, not a function, so the parentheses are not needed.
  5. Since you overwrite the memory allocated immediately, and don't need it to be zero, you can replace calloc() call by a suitable call to malloc().

As I said, these are minor comments. Your main problems lie with the wrong use of calloc, etc.

Upvotes: 1

catchmeifyoutry
catchmeifyoutry

Reputation: 7359

Some feedback:

  • The memory allocation parts with calloc should occur outside the loop. Now you allocate it, and then loose track of it in the next iteration because new memory is allocated and assigned.
  • memory you allocate should be freed somewhere with free
  • shipyard1.numOfDocks (same for shipyard2) is unitialized when you use it, it may be a random number (which means you have an undefined number of loop iterations, and allocate an undefined amount of memory).

Good luck!

Upvotes: 1

pmg
pmg

Reputation: 108978

You have calloc() inside a for loop twice. Both times you're losing the address returned.

for () {
    addr = calloc();
    addr[i] = ...
}

the second time through the loop, the addr you got on the first time is gone (you got yourself a memory leak), the value you saved there is gone too.

Move the calloc() outside the loop ... and remember to free() the memory when you no longer need it

addr = calloc();
for () {
    addr[i] = ...
}
free(addr);

Upvotes: 3

Related Questions