#1
  1. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2012
    Posts
    2
    Rep Power
    0

    C - Question with Struct and Pointer


    Hi All,

    This is my scenario:
    I have a struct which is like this:
    Code:
    typedef struct {
            int row;
            int column;
    } Position;
    I have an another struct which is like this:
    Code:
    typedef struct {
            int maxRow;
            int maxCol;
            Position *position;
            //Position (*position)[][]
    } Grid;
    In my Grid struct, I would like a pointer points to array of the Position. Because I do not know upfront the array size of Position I am going to create. Therefore ( the pointer that I comment off) would not be possible.

    So I point to the first element of the array and then access the element just by increment the pointer, which is like this:
    Code:
    //create an array of Position
    int i,j;
    Position positionArray[2][2];
    for(i=0;i<2;i++)
     for(j=0;j<2;j++)
      positionArray = {i,j};
    
    GameBoard gb = {2,2};
    GameBoard *ptgb = &gb;
    
     ptgb->position = &positionArray[0][0];
           printf("row: %d, column: %d\n",ptgb->position->row,ptgb->position->column);
           ptgb->position++;
           printf("row: %d, column: %d\n",ptgb->position->row,ptgb->position->column);
           ptgb->position++;
           printf("row: %d, column: %d\n",ptgb->position->row,ptgb->position->column);
           ptgb->position++;
           printf("row: %d, column: %d\n",ptgb->position->row,ptgb->position->column);
    But the problem is because this is a double array, by simply increment the pointer,makes me a little bit difficult to locate array element. For example, if I want to retrieve position[1][1],
    I have to do some calculation to know how many the pointer should increment.

    What is the downside of the current approach?
    What is the better way to approach this?

    Thank you for all your help. I hope I can learn something new by any of your advice.
  2. #2
  3. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,325
    Rep Power
    2228
    As I understand the implementation of two-dimensional arrays in C, it's an array of arrays. Namely, the array indexed by row contains pointers to each row, which are indexed by column. Once that's set up, you should be able to simply use standard 2-D array notation.

    So, first you declare position to be Position** . Then you malloc or calloc to position the number of rows, each element in position being sizeof(Position*), since each element of position will be a pointer to a one-dimensional array of Position. Then you iterate through position and malloc or calloc to each element the number of columns times sizeof(Position). After that, you should have your 2-D array of Position and be able to access each element with standard [][] notation.

    Mind you I've never tried it, but that fits what I've heard and read. See how that works for you.

    PS
    I just now jotted down this test program:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include <malloc.h>
    
    #define  ROW  2
    #define  COL  2
    
    typedef struct {
            int row;
            int column;
    } Position;
    
    
    int main()
    {
        Position **position;
        int     i;
        
        position = (Position**)malloc(sizeof(Position*)*ROW);
        
        for (i=0; i<ROW; i++)
        {
            position[i] = (Position*)malloc(sizeof(Position)*COL);
        }
        
        position[1][1].row = 42;
        
        printf("position[1][1].row = %d\n", position[1][1].row);
        
        return 0;
    }
    Using MinGW gcc on WinXP Pro with warnings set to "all", it compiled clean and ran successfully.
    Last edited by dwise1_aol; April 5th, 2012 at 02:42 PM.
  4. #3
  5. No Profile Picture
    Registered User
    Devshed Newbie (0 - 499 posts)

    Join Date
    Apr 2012
    Posts
    2
    Rep Power
    0

    Smile


    Originally Posted by dwise1_aol
    As I understand the implementation of two-dimensional arrays in C, it's an array of arrays. Namely, the array indexed by row contains pointers to each row, which are indexed by column. Once that's set up, you should be able to simply use standard 2-D array notation.

    So, first you declare position to be Position** . Then you malloc or calloc to position the number of rows, each element in position being sizeof(Position*), since each element of position will be a pointer to a one-dimensional array of Position. Then you iterate through position and malloc or calloc to each element the number of columns times sizeof(Position). After that, you should have your 2-D array of Position and be able to access each element with standard [][] notation.

    Mind you I've never tried it, but that fits what I've heard and read. See how that works for you.

    PS
    I just now jotted down this test program:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include <malloc.h>
    
    #define  ROW  2
    #define  COL  2
    
    typedef struct {
            int row;
            int column;
    } Position;
    
    
    int main()
    {
        Position **position;
        int     i;
        
        position = (Position**)malloc(sizeof(Position*)*ROW);
        
        for (i=0; i<ROW; i++)
        {
            position[i] = (Position*)malloc(sizeof(Position)*COL);
        }
        
        position[1][1].row = 42;
        
        printf("position[1][1].row = %d\n", position[1][1].row);
        
        return 0;
    }
    Using MinGW gcc on WinXP Pro with warnings set to "all", it compiled clean and ran successfully.
    Thank you so much for your advice.
  6. #4
  7. Contributing User
    Devshed Supreme Being (6500+ posts)

    Join Date
    Jan 2003
    Location
    USA
    Posts
    7,325
    Rep Power
    2228
    Never able to leave well enough alone, I decided to print out a hex dump of the array to see just exactly how it was organized in memory. For test data, I assigned each element's array row and column to the element's Position row and column fields, respectively. When you read the hex dump, keep in mind that Intel is little-endian, which means that it stores multi-byte values with the LSB first and the MSB last.

    Generating the array dynamically gives us this:
    Code:
    C:TEST>a
    Rows = 4, Cols = 5, sizeof Position = 8, sizeof int = 4
    03E2468:  80 24 3E 00 B0 24 3E 00 E0 24 3E 00 10 25 3E 00   .$>..$>..$>..%>.
    03E2478:  06 00 03 00 06 01 08 00 00 00 00 00 00 00 00 00   ................
    03E2488:  00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00   ................
    03E2498:  00 00 00 00 03 00 00 00 00 00 00 00 04 00 00 00   ................
    03E24A8:  06 00 06 00 1C 01 08 00 01 00 00 00 00 00 00 00   ................
    03E24B8:  01 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00   ................
    03E24C8:  01 00 00 00 03 00 00 00 01 00 00 00 04 00 00 00   ................
    03E24D8:  06 00 06 00 12 01 08 00 02 00 00 00 00 00 00 00   ................
    03E24E8:  02 00 00 00 01 00 00 00 02 00 00 00 02 00 00 00   ................
    03E24F8:  02 00 00 00 03 00 00 00 02 00 00 00 04 00 00 00   ................
    03E2508:  06 00 06 00 28 01 08 00 03 00 00 00 00 00 00 00   ....(...........
    03E2518:  03 00 00 00 01 00 00 00 03 00 00 00 02 00 00 00   ................
    03E2528:  03 00 00 00 03 00 00 00 03 00 00 00 04 00 00 00   ................
    03E2538:  7E 00 06 00 46 00 65 00 68 05 3E 00 68 05 3E 00   ~...F.e.h.>.h.>.
    The first line in the dump is the list of memory addresses that the rows are stored. There are 8 bytes of padding between each row.

    Declaring the array statically gives us this:
    Code:
    C:TEST>a
    Rows = 4, Cols = 5, sizeof Position = 8, sizeof int = 4
    022FEE0:  00 00 00 00 00 00 00 00 00 00 00 00 01 00 00 00   ................
    022FEF0:  00 00 00 00 02 00 00 00 00 00 00 00 03 00 00 00   ................
    022FF00:  00 00 00 00 04 00 00 00 01 00 00 00 00 00 00 00   ................
    022FF10:  01 00 00 00 01 00 00 00 01 00 00 00 02 00 00 00   ................
    022FF20:  01 00 00 00 03 00 00 00 01 00 00 00 04 00 00 00   ................
    022FF30:  02 00 00 00 00 00 00 00 02 00 00 00 01 00 00 00   ................
    022FF40:  02 00 00 00 02 00 00 00 02 00 00 00 03 00 00 00   ................
    022FF50:  02 00 00 00 04 00 00 00 03 00 00 00 00 00 00 00   ................
    022FF60:  03 00 00 00 01 00 00 00 03 00 00 00 02 00 00 00   ................
    022FF70:  03 00 00 00 03 00 00 00 03 00 00 00 04 00 00 00   ................
    022FF80:  A0 FF 22 00 D3 11 40 00 01 00 00 00 58 24 3E 00   .."...@.....X$>.
    022FF90:  58 2B 3E 00 B3 11 40 00 08 04 00 00 00 E0 FD 7F   X+>...@.........
    Different format. No addresses of rows, but rather it's each row of data stored one after the other.

    For the statically declared array, you could take the row, column, number of columns per row, and the size of each element and use those to calculate the offset into the array where you could find a given array element.

    For the dynamically declared array, you would need to get the pointer to the row and then calculate the offset into that row in order to access the element you want.

    And yet, both types of arrays can use the exact same position[][] notation. Which makes this an object lesson that, even though the C language treats the two as equivalent, they are still implemented differently; the code makes it look like they're the same, but they are not.

    Here is my test program with a hex dump function I had written years ago:
    Code:
    #include <stdlib.h>
    #include <stdio.h>
    #include <malloc.h>
    
    #define  TEST_POINTERS  // #define for dynamic array creation, #undef for static array declaration
    
    #define  ROWS  4
    #define  COLS  5
    
    typedef struct {
            int row;
            int column;
    } Position;
    
    Position ** CreatePositionArray(int rowsize, int colsize);
    void HexDump(unsigned char *buffer,int len,unsigned long ulAddr);
    
    int main()
    {
    #ifdef TEST_POINTERS
        Position **position;
    #else
        Position position[ROWS][COLS];
    #endif    
        int     i, j;
        int     dumpSize;
        
    #ifdef TEST_POINTERS
        position = CreatePositionArray(ROWS, COLS);
    #endif    
    
        // Test Pattern:  save each element's row and column in the array into 
        //      the Position struct's row and column fields, respectively     
        for (i=0; i<ROWS; i++)
            for (j=0; j<COLS; j++)
            {
                position[i][j].row = i;
                position[i][j].column = j;
            }
        
        // Display the test's parameters and type sizes
        printf("Rows = %d, Cols = %d, sizeof Position = %d, sizeof int = %d\n", 
                    ROWS, COLS, sizeof(Position), sizeof(int));
                    
        // Generate the hex dump of the array
        
        // In the dynamic array, there is padding between the rows, so add a 
        //      line of padding for each row, which should be more than enough.
        // The static array's size does not need to be padded.
    #ifdef TEST_POINTERS
        dumpSize = sizeof(Position)*ROWS*COLS + sizeof(Position*)*ROWS + 16*ROWS;
    #else
        dumpSize = sizeof(Position)*ROWS*COLS; // + 32;
    #endif    
        HexDump((unsigned char*)position, dumpSize, (unsigned long)position);
        
        return 0;
    }
    
    
    Position ** CreatePositionArray(int rowsize, int colsize)
    {
        Position **pos;
        int     i;
        
        pos = (Position**)malloc(sizeof(Position*)*rowsize);
        
        for (i=0; i<rowsize; i++)
        {
            pos[i] = (Position*)malloc(sizeof(Position)*colsize);
        }
        
        return pos;
    }
    
    
    /* routine for displaying one line (16 bytes) of data as a hex dump */
    void WriteHexLine(int nBytes, unsigned long ulAddr, unsigned char a[])
    {
    #define ADDR_LEN     7
    #define HEX_START   10
    #define ASCII_START 60
    #define S           (ASCII_START+16)
        char s[S+1];
        char *hp;
        char *ap;
        unsigned char   ch;
        int    i, x;
    
        sprintf(s,"%0*lX:",ADDR_LEN,ulAddr);
        
        for (i=ADDR_LEN+1; i<S; i++)
            s[i] = ' ';
        s[S] = '\0';
    
        hp = &(s[HEX_START]);
        ap = &(s[ASCII_START]);
    
        for (i=0; i<nBytes; i++)
        {
            ch = (unsigned char)a[i];
            x = ch / 16;
            if (x < 10)
                *hp++ = x + '0';
            else 
                *hp++ = (x-10) + 'A';
    
            x = ch & 0x000F;
            if (x < 10)
                *hp++ = x + '0';
            else 
                *hp++ = (x-10) + 'A';
    
            hp++;
            if ( (ch > 31) && (ch < 127) )
                *ap++ = ch;
            else
                *ap++ = '.';
        }
    
        printf("%s\n",s);
    }
    
    /* ******************************************** */
    
    /* controls the printing of the entire hex dump */
    void HexDump(unsigned char *buffer,int len,unsigned long ulAddr)
    {
        int  i, n, u;
        unsigned char *cp;
    
        cp = buffer;
        n = len / 16;  /* number of complete lines */
        u = len % 16;  /* length of the partial line (the last one) */
    
        /* display all the complete lines */
        for (i = 0; i < n; i++)
        {
            WriteHexLine(16, ulAddr, cp);
            cp += 16;
            ulAddr += 16UL;
        }
    
        /* now finish with the partial line, if there is one */
        if (u)
            WriteHexLine(u, ulAddr, cp);
    }
    Share and enjoy!

    Comments on this post

    • RAJ_55555 agrees : Your posts are always worth reading..

IMN logo majestic logo threadwatch logo seochat tools logo