3

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

  • 1
    If I'm not mistaken you cannot nominally access arbitrary elements in the two-dimensional array from the address of the first element. You should receive a warning here. (It will work factually though.) You'll need to pass a pointer the entire line. I'll show some code in a minute. – Peter - Reinstate Monica Dec 10 '22 at 13:19
  • 1
    As an aside, it is also unnecessary to have an array with an element count of one (id, status). – Peter - Reinstate Monica Dec 10 '22 at 13:20
  • As an aside, upvoted for a complete, minimal example and own effort up to the point where you got stuck. – Peter - Reinstate Monica Dec 10 '22 at 13:21
  • 2
    It looks like you are mixing up how your array is allocated. You have defined table as a 2D array of pointers to mystruct. But then when you allocate the items you create `X*Y` storage for the structures. You should either drop the [X][Y] from your table definition and only perform the allocation once, OR you should remove the X*Y from your allocations. – tinman Dec 10 '22 at 13:25
  • `table[X_selected][Y_selected] = (struct mystruct*)malloc(X * Y * sizeof(struct mystruct));` sets *one* element of your 2-d `table` array to point to an array of `X * Y` `struct mystruct` objects. All the other elements of `table` have indeterminate values. Is that what you want? – Andrew Henle Dec 10 '22 at 13:26
  • 1
    Minor point: as a general rule, casting malloc's output is not necessary nor useful. – picchiolu Dec 10 '22 at 13:36
  • Yes, after looking at it a bit closer: If you know the array size in advance, as in this test code, you don't need malloc at all. If you are making a prototype though for cases when the size is only known at run time, you'll need a malloc, but you'll likely want to malloc the structs directly, not just pointers (which would need another round of mallocs to make them point to something). – Peter - Reinstate Monica Dec 10 '22 at 13:36
  • @Peter-ReinstateMonica Thank you, i will see what you will show, there is just one element in this code to make it clear for you, but i will create more than one if i fix this problem, so the specifications are not completely here, i just maked a small code to show what is my problem as you said. I need to keep ``struct mystruct* table[X][Y];`` at the first line of main, i can make this code work if i remove [X][Y], like: ``struct mystruct* table`` and making some changes but i need definitly ``struct mystruct* table[X][Y];`` declaration. – Musa Tombul Dec 10 '22 at 13:36
  • The problem may boil down to the declaration `struct mystruct* table[X][Y];` which is parsed as `struct mystruct* (table[X][Y]);` That is, the indexes come first, and the result (what is stored at each index) is a pointer to struct. – Peter - Reinstate Monica Dec 10 '22 at 13:38
  • What you instead may want is `struct mystruct(* table)[X][Y];`. Table is by force of parentheses de-referenced first, and the result is a 2-d array whose elements are entire structs. – Peter - Reinstate Monica Dec 10 '22 at 13:40
  • The allocation for that would simply be `table = (struct mystruct*)malloc(X * Y * sizeof(struct mystruct));`, which provides the space needed for so many structs. – Peter - Reinstate Monica Dec 10 '22 at 13:42
  • @tinman Yes, tried it, you are right! it is unnecessary to make X*Y storage, removed it. What can you recommend for printing struct elements in this function? – Musa Tombul Dec 10 '22 at 13:42
  • @Peter-ReinstateMonica I know array size but, i will make a memory optimization, soo when table[X][Y] is not selected by the user, the pointer of table[X][Y] = NULL and if selected, malloc create this pointer and i put data in it. – Musa Tombul Dec 10 '22 at 13:46
  • 1
    The code does not make any sense whatsoever. There are many compiler warnings [which are in fact fatal errors](https://stackoverflow.com/questions/57842756/why-should-i-always-enable-compiler-warnings) and there is a zillion ways to fix them, but it is unknown which fix would be compatible with your requirements. Please post the requirements in plain English. – n. m. could be an AI Dec 10 '22 at 13:49
  • @n.m. Yes i put ``**CONDITION**`` text where i need help, if you remove the code lines in the fonction, you can successfully compile and see with red debug point at ``show_struct(table, X_selected, Y_selected);`` line (before launching fonction), data is successfully placed in my 2d struct like the picture. my requirement is: need to keep ``struct mystruct* table[X][Y];`` and find a ``**CONDITION**`` to check ``status`` case of my struct and print ok if 1 or not ok if 0. This is all i want. I can make changes for fonction head and data sending method to the fonction. – Musa Tombul Dec 10 '22 at 14:06
  • Ah, so you do want pointers. Then you'll have to malloc each element individually. Of course you can still use something like my solution below as a base. – Peter - Reinstate Monica Dec 10 '22 at 15:37
  • "if you remove the code lines in the fonction, you can successfully compile" NO YOU CANNOT. Please read the provided link. – n. m. could be an AI Dec 10 '22 at 16:05
  • @n. m. YES I CAN, if you talk about warning, i know there are warnings, after my search on web, cannot find and understand what is the problem so im here to find a solution for this... – Musa Tombul Dec 10 '22 at 17:52
  • @n.m. , Musa: Musa, it is absolutely essential that you compile with all warnings enabled (at least gcc, clang: `-Wall`, Visual C: `/W4`). The prime directive is: **All warnings must be understood fully.** The secondary directive is that the code should be fixed until it compiles without warnings -- but only after understanding them. You must know what you are doing. Compiler warnings are one free tool in a programmer's toolbox that helps you understand the language and how to use it. – Peter - Reinstate Monica Dec 11 '22 at 14:40

2 Answers2

1

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");
   
        }
    }

}
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
0

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
Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62
  • Thank you very much, but i dont understand how can i transform it to ``struct mystruct* table[X][Y];`` declaration type because i need this, i cannot modify this line of code :( – Musa Tombul Dec 10 '22 at 17:33
  • I understand that many different changes are required, but it is absolutely necessary to have this line in my project, is it not possible to just make changes "in" the fonction or "head" of the fonction or "call" of the fonction? – Musa Tombul Dec 10 '22 at 17:35
  • Because, creation of struct or malloc doesnt seem having errors, because when i read memory it at line ``show_struct(table, X_selected, Y_selected);`` , i have exactly what i want (like in the picture https://i.stack.imgur.com/g6vWu.png), so i think there must be a way to do this without editing, main except fonction call and the fonction show_struct – Musa Tombul Dec 10 '22 at 17:39
  • Please can you comment out code lines inside the fonction and compile, add red debug point to ``show_struct(table, X_selected, Y_selected);`` and see the memory? – Musa Tombul Dec 10 '22 at 17:40