Home > Blockchain >  How can i use pointer to 2D struct array in a fonction C
How can i use pointer to 2D struct array in a fonction C

Time:12-12

Here is my code:

#include <stdio.h>
#include <stdlib.h>

#define X 5       
#define Y 7 

struct mystruct {
    char name[10];
    int id[1];
    int status[1];
};

void show_struct(struct mystruct* table, int X_selected, int Y_selected);


void main(void)
{
    struct mystruct* table[X][Y];

    int X_selected = 3;
    int Y_selected = 4;

    table[X_selected][Y_selected] = (struct mystruct*)malloc(sizeof(struct mystruct));

    strcpy(table[X_selected][Y_selected]->name, "NAME");
    *table[X_selected][Y_selected]->id = 0;
    *table[X_selected][Y_selected]->status = 1;

    show_struct(table, X_selected, Y_selected);
}

void show_struct(struct mystruct* table, int X_selected, int Y_selected)
{
    if (**CONDITION**) {
        printf("OK , STATUS IS 1");
    }
    else {
        printf("ERROR , STATUS IS NOT 1");
    }
}

I need help to find the CONDITION in my code to check if status = 1

When i debug at line show_struct(table, X_selected, Y_selected); before going in fonction, i can see status=1 is successfully placed on table:

Memory Table

CodePudding user response:

OK, now that you specified what you are trying to do, let's simply start with your code. Let's use the godbolt compiler explorer. I'll only link to versions of your code so that I don't have to copy so much code into this answer.

If we run your original code (with 0 instead of your condition marker) through a recent gcc (12.2.) on godbolt we get a number of warnings and, of course, the error where the condition should be (version 0). Let's fix the first warnings because they are simple:

<source>:16:6: warning: return type of 'main' is not 'int' [-Wmain]
   16 | void main(void)
      |      ^~~~
<source>: In function 'main':
<source>:25:5: warning: implicit declaration of function 'strcpy' [-Wimplicit-function-declaration]
   25 |     strcpy(table[X_selected][Y_selected]->name, "NAME");
      |     ^~~~~~
<source>:3:1: note: include '<string.h>' or provide a declaration of 'strcpy'
    2 | #include <stdlib.h>
      | #include <string.h>
    3 | 
<source>:25:5: warning: incompatible implicit declaration of built-in function 'strcpy' [-Wbuiltin-declaration-mismatch]
   25 |     strcpy(table[X_selected][Y_selected]->name, "NAME");
      |     ^~~~~~
<source>:25:5: note: include '<string.h>' or provide a declaration of 'strcpy'
  • OK, main should return an int. Googling that points us to an SO answer pointing out that int main(void) is OK, and not having a return statement is OK, too. Got it.
  • strcpy must be declared. gcc already suggests to include the header string.h. Got it.
  • The next warning is harder:
<source>: In function 'main':
<source>:30:17: warning: passing argument 1 of 'show_struct' from incompatible pointer type [-Wincompatible-pointer-types]
   30 |     show_struct(table, X_selected, Y_selected);
      |                 ^~~~~
      |                 |
      |                 struct mystruct * (*)[7]
<source>:14:35: note: expected 'struct mystruct *' but argument is of type 'struct mystruct * (*)[7]'
   14 | void show_struct(struct mystruct* table, int X_selected, int Y_selected);
      |                  ~~~~~~~~~~~~~~~~~^~~~~

In particular, what is struct mystruct * (*)[7], and why is the compiler thinking we are passing that when in fact we pass a 2-dimensional array? Before I suggest a solution that is simpler and avoids complicated types, I'll give short explanation.

A 2-dimensional array in C is, in fact, an array of (1-dimensional) arrays. It's easy to understand a 1-dimensional array for a type T (T may be struct mystruct * like in your program, but the argument is universal). T table[7]; defines an array of 7 Ts. Now I can define an array of 5 of those arrays; because square brackets are evaluated left-to-right, I have to write the new dimension to the left of the old one (think "I first index the 2-dimensional array, obtaining an element — which is a 1-dimensional array, which I index again to obtain the j-th T" — left to right): T table[5][7]. 5 one-dimensional arrays with 7 Ts each.

The crucial point is to remember what happens when you pass an array to a function: It is "adjusted" to a pointer to its first element. The elements of a T table[5][7] are arrays of 7 T, or T [7], a pointer to that is a T (*)[7]. We must put the * in parentheses because otherwise, per operator precedence, the index operation would come first, resulting in a pointer to T, which is something different: We don't have an array of seven pointers to T — we have a pointer to an array of seven T. The order of "pointer" and "array" in these two sentences reflects the order of evaluation, which is enforced by the parentheses in the second case. Now, in our case T is struct mystruct *, so that the argument actually passed to the function is what gcc reports: struct mystruct * (*)[7]. It is a pointer to an array of seven pointers to mystruct. That is not the same as a pointer to mystruct.

The easiest way to implement a function that prints one mystruct object would actually be to define the function to simply take a pointer to that mystruct object. The caller is responsible for providing the right pointer. This makes the function more general: Perhaps we want to print mystructs which are not in the table?

Pointers have a nice reserved value to show that they don't point to anything, which is NULL in C. We'll test whether the pointer is NULL to check whether it was initialized. Note that we cannot test e.g. `if(myStructPtr->id == 0) because if myStructPtr is not initialized the program will be faulty and likely crash. But we can and often must examine the actual pointer value.

void show_struct(struct mystruct* myStructPtr)
{
    if (myStructPtr != NULL) {
        printf("OK , initialized");
    }
    else {
        printf("ERROR, uninitialized");
    }
}

Passing such a pointer is relatively straightforward: You index the table properly! The obtained element is a pointer, after all:

    show_struct(table[X_selected][Y_selected]);

Now we must make sure that the pointers stored in the table are actually null. We can do that with brace initialization. We don't actually need to initialize all elements, missing ones will be filled with zeroes by default:

    struct mystruct* table[X][Y] = {{0}};

Now we have everything together for a working program:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define X 2       
#define Y 3 

struct mystruct {
    char name[10];
    int id[1];
    int status[1];
};

void show_struct(struct mystruct* myStructPtr)
{
    if (myStructPtr != NULL) {
        printf("OK , initialized\n");
        printf("Status: %d\n", *myStructPtr->status);
    }
    else {
        printf("ERROR, uninitialized\n");
    }
}

int main(void)
{
    struct mystruct* table[X][Y] = {{0}};
    int X_selected = 1;
    int Y_selected = 1;

    table[X_selected][Y_selected] = (struct mystruct*)malloc(sizeof(struct mystruct));

    strcpy(table[X_selected][Y_selected]->name, "NAME");
    *table[X_selected][Y_selected]->id = 0;
    *table[X_selected][Y_selected]->status = 1;

    printf("Selected: ");
    show_struct(table[X_selected][Y_selected]);
    printf("\n");

    for(int xInd = 0; xInd < X; xInd  )
    {
        for(int yInd = 0; yInd < Y; yInd  )
        {
            printf("%d, %d:\n", xInd, yInd);
            show_struct(table[xInd][yInd]);
            printf("****************************************\n");
   
        }
    }

}

CodePudding user response:

Here is a solution that is probably a little more than you asked for. I created a new structure for the array of structures which carries the size information with it, and a few functions that operate on that "array structure" instead on raw pointers.

My solution assumes that you eventually want to enter array sizes at run time (your prototype has defines for the array sizes; in that case you could simply define proper arrays of sizes known at compile time).

The "array structure", myTable, holds only a pointer to the actual data. This makes it so small that it can be passed around by value. That copying is shallow: A copy points to the same data as the original. (This way we can fill a table with a function that gets it by value, as in fill_tbl(struct myTable tbl).

The program has a header file myStructArray.h with the structure definitions and function declarations, an implementation file myStructArray.c with the function implementations, and a main.c file.

myStructArray.h

#ifndef MY_STRUCT_ARRAY_H
#define MY_STRUCT_ARRAY_H

#include <stdint.h> // size_t

struct mystruct {
    char name[10];
    int id;
    int status;
};

struct myTable
{
    size_t mXSz;
    size_t mYSz;

    struct mystruct* mStructs;
};

/// <summary>
/// return a table with a pointer to the dynamically allocated array 
/// </summary>
struct myTable createTable(size_t szX, size_t szY);

/// <summary>
/// get a pointer to an element in the given table
/// </summary>
struct mystruct* getStructPtr(struct myTable tbl, size_t x, size_t y);

/// <summary>
/// print  single struct
/// </summary>
/// <param name="s"></param>
void show_struct(struct mystruct* s);
/// <summary>
/// print the entire table
/// </summary>
/// <param name="tbl"></param>
void show_table(struct myTable tbl);

void destroy_table(struct myTable tbl);

#endif

myStructArray.c

#include <stdint.h> // size_t
#include <stdio.h> // printf
#include <string.h> // memset
#include <stdlib.h> // malloc
#include "myStructArray.h"
#include <assert.h>

void show_struct(struct mystruct *s)
{
    if (s->id) {
        printf("OK, STATUS IS 1\n");
        printf("name: ->%s<-\n", s->name? s->name : "Null");
        printf("status: %i\n", s->status);
    }
    else {
        printf("ERROR, STATUS IS NOT 1");
    }
}

struct mystruct* getStructPtr(struct myTable tbl, size_t x, size_t y)
{
    assert(x < tbl.mXSz&& y < tbl.mYSz);
    return &tbl.mStructs[y * tbl.mXSz   x];
}

void show_table(struct myTable tbl)
{
    for (size_t y = 0; y < tbl.mYSz; y  )
    {
        for (size_t x = 0; x < tbl.mXSz; x  )
        {
            printf("*************** x: %zu, y: %zu******\n", x, y);
            show_struct(getStructPtr(tbl, x, y));
        }
        printf("\n");
    }
}

struct myTable createTable(size_t szX, size_t szY)
{
    struct myTable newTbl = {szX, szY, 0};
    
    size_t byteSz = szX * szY * sizeof(struct mystruct);
    newTbl.mStructs = (struct mystruct *) malloc(byteSz);
    memset(newTbl.mStructs, 0, byteSz); // make sure all ids are 0
    return newTbl; // yes, by value (two numbers and a pointer)
}

void destroy_table(struct myTable tbl)
{
    free(tbl.mStructs); // ok if null
}

main.c

#define _CRT_SECURE_NO_WARNINGS // for visual C

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "myStructArray.h"

void fill_tbl(struct myTable tbl)
{
    for (size_t y = 0; y < tbl.mYSz; y  )
    {
        for (size_t x = 0; x < tbl.mXSz; x  )
        {
            struct mystruct* sp = getStructPtr(tbl, x, y);
            sprintf(sp->name, "%zu/%zu", x, y);
            sp->id = 1  y*tbl.mXSz   x;
            sp->status = 1;
        }
    }
}

void main(void)
{
    int size_x, size_y;
    printf("size_x size_y: ");
    scanf("%i %i", &size_x, &size_y);
    struct myTable tbl = createTable(size_x, size_y);

    fill_tbl(tbl);

    int x_selected, y_selected;
    printf("x_selected y_selected: ");
    scanf("%i %i", &x_selected, &y_selected);


    struct mystruct *sPtr = getStructPtr(tbl, x_selected, y_selected);
    strcpy(sPtr->name, "NAME");
    sPtr->id = 1234;
    sPtr->status = 1;

    show_struct(getStructPtr(tbl, x_selected, y_selected));
    show_table(tbl);
}

Sample session

"3 2" and "0 1" in the first two lines is user input for the sizes and indexes. The rest is output.

size_x size_y: 3 2
x_selected y_selected: 0 1
OK, STATUS IS 1
name: ->NAME<-
status: 1
*************** x: 0, y: 0******
OK, STATUS IS 1
name: ->0/0<-
status: 1
*************** x: 1, y: 0******
OK, STATUS IS 1
name: ->1/0<-
status: 1
*************** x: 2, y: 0******
OK, STATUS IS 1
name: ->2/0<-
status: 1

*************** x: 0, y: 1******
OK, STATUS IS 1
name: ->NAME<-
status: 1
*************** x: 1, y: 1******
OK, STATUS IS 1
name: ->1/1<-
status: 1
*************** x: 2, y: 1******
OK, STATUS IS 1
name: ->2/1<-
status: 1
  •  Tags:  
  • c
  • Related