1

I can access the char value of the nested struct, but the int one it shows memory address only, how I can dereference the pointer value of an int one ? here is a sample code:

#include <stdio.h>


    struct Class {
        char name[10];
        int cap;
    };

    struct Student {
        char firstname[10];
        char lastname[10];
        struct Class *class[5];
    };
    


     void printStudents(struct Student *student) {
        struct Class *class;
        class = student->class[2];
        printf("Capacity => %d\n", &class->cap);
        printf("Class => %s\n", &class->name);
    }



int main(void) {
    
struct Class class[] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

    struct Student student = {"Ian", "saeidi", (struct Class *) &class};

struct Class class[] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

    struct Student student = {"Ian", "saeidi", &class};
    
        printStudents(&student);
    
        return 0;
    }

Output:


Expected Output:

Capacity => 33
Class => java

I have tried the below initialization still does not worked, and the editor says that it is element 0, while I have added the entire struct array:

struct Class class[] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

    struct Student student = {"Ian", "saeidi", (struct Class *) &class};

struct Class class[] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

    struct Student student = {"Ian", "saeidi", &class};

enter image description here

enter image description here

Moss
  • 378
  • 7
  • 18
  • 3
    That initialization is wrong, `{"rust", 24}` cannot be converted to a `struct Class*` – UnholySheep Apr 13 '22 at 14:32
  • 3
    *And* you are passing an `int*` to `printf("Capacity => %d\n", &class->cap);` - you should be removing the `&` here. All these things should be at least warnings emitted by your compiler – UnholySheep Apr 13 '22 at 14:35
  • I have updated the name (added length), and I have removed the & from calss->cap, but still does not got the value of cap! , would you let me know what is the correct way of initialize the struct please ? – Moss Apr 13 '22 at 14:40
  • You need to first create a `struct Class` object so that you can take its address. One possible way of doing that is to have an extra variable, e.g.: `struct Class c = {"rust", 24};` and then `struct Student student = {"Ian", "saeidi", &c};`. There's also some other options but I believe this to be the most suitable for your use case – UnholySheep Apr 13 '22 at 14:44
  • Save yourself from some unnecessary head ache by following this: [What compiler options are recommended for beginners learning C?](https://software.codidact.com/posts/282565) – Lundin Apr 13 '22 at 14:44
  • I consider myself very experienced in C, and I don't have a slightest idea on what's going on here. I'd be happy if someone answered this question (can't set a bounty yet). – anatolyg Apr 13 '22 at 14:46
  • 1
    @anatolyg Initialising a pointer (scalar) using a brace-enclosed list is allowed in C. As a gcc non-standard extension, this brace-enclosed list may contain more items than objects to initialize, which standard C doesn't allow. Check out this old post where I was equally confused at first: https://stackoverflow.com/questions/35266987/int-nums-5-2-1-4-causes-a-segmentation-fault – Lundin Apr 13 '22 at 14:50
  • @anatolyg If you want I could cook up an answer here, but the linked post is kind of a duplicate to this one I guess. As for why gcc allows this, I suppose it could be some old remain from pre-C99 compound literals. GNU C served as a playground for a lot of new experimental features that later made it into C99. – Lundin Apr 13 '22 at 14:51
  • Yes, that linked post explains everything, no need to repeat it here. – anatolyg Apr 13 '22 at 14:54

3 Answers3

2

You have several problems:

  1. You're not allocating any space for class, so trying to initialize it with {"rust", 24} is incorrect.
  2. Even if that was ok, name is a single char and cannot hold nor point to a string.
  3. Since the initialization is wrong, class = student->class; assigns class to invalid memory, and dereferencing it (class->) invokes undefined behavior.
  4. "%d" printf format specifier expects an int, but you pass it an int*
  5. "%s" printf format specifier expect a char*, but you pass it a char (*)[10] (pointer to a 10 size char array).

One possible fix:

// flip the order of the structs so you can declare a `struct Class class` in
// `struct Student` (no pointer)
struct Class {
    // change this from a pointer to array
    char name[10];
    int cap;
};

struct Student {
    char firstname[10];
    char lastname[10];
    struct Class class;
};

 void printStudents(struct Student *student) {
    // get a pointer to the address of student->class if you want
    struct Class *class;
    class = &(student->class);

    // now things print appropriately
    // pass an `int` to %d
    printf("Capacity => %d\n", class->cap);
    // pass a `char*` to %s. `class->name` in this context "decays" to a
    // pointer to its first element, a `char`
    printf("Class => %s\n", class->name);
}

int main(void) {
    // now your initialization is valid
    struct Student student = {"Ian", "saeidi", {"rust", 24}};

    printStudents(&student);

    return 0;
}

You could also use dynamic memory allocation instead of arrays.

Demonstration


EDIT after OP changed the question, another possible implementation. See explanation comments below:

struct Class {
    char name[10];
    int cap;
};

struct Student {
    char firstname[10];
    char lastname[10];
    // Not exactly sure what you're trying to do, but I've changed your
    // pointer here to be a pointer to an array of three `struct Class`es
    // (since that's what you're creating in main, and so this is the only
    // compatible pointer that can point to such an object).
    struct Class (*class)[3];
};

void printStudent(struct Student *student) {
    struct Class (*class)[3];
    class = (*student).class;
    
    // Take note of the pointer arithmetic here. class+1 advances the
    // pointer the size of three `struct Class`es. From main, there's
    // only one object of this type (three `struct Class`es), so you
    // have to dereference the pointer at that first object only
    // (eg, `(*class)` ), then apply the index for `cap` and `name`.
    // If you do `class[i]`, for `i>0` you point to the `i`th object of
    // 3 `struct Class`es, which of course don't exist. There's only one.
    for (size_t i=0; i<3; i++)
    {
        printf("Capacity => %d\n", (*class)[i].cap);
        printf("Class => %s\n", (*class)[i].name);
    }
}

int main(void) {
    // here you're creating three `struct Class`es
    struct Class class[] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

    // pass the address of `class` to your struct. This type is a
    // struct Class (*)[3]. That is, a pointer to a 3-size array of
    // struct Class type.  If you added or subtracted another entry
    // to your class definition above, then you'd have to change the
    // pointer dimension in struct Student as well.
    struct Student student = {"Ian", "saeidi", &class};
    printStudent(&student);
    return 0;
}

New Demonstration

yano
  • 4,827
  • 2
  • 23
  • 35
  • https://godbolt.org/z/cTWe8sE74 – Moss Apr 14 '22 at 11:51
  • @mos I assume you're asking why the code in your link crashes? I've updated my answer with another possible implementation, based on the 3-sized `struct Class` array you've created in `main`. There's no black and white rules of course, but be cautious editing your original code. Too many changes and you create a moving target, where already-supplied answers become invalidated (as was the case here). In such cases you're better off asking a new question. – yano Apr 14 '22 at 15:06
2

struct Student student = {"Ian", "saeidi", {"rust", 24}}; is incorrect. The {"rust", 24} part needs to be changed to a pointer to a struct Class. That struct Class could be stored as a variable like this:

struct Class class = {"rust", 24};
struct Student student = {"Ian", "saeidi", &class};

Alternatively, that struct Class could be stored as a compound literal like this:

struct Student student = {"Ian", "saeidi", &(struct Class){"rust", 24}};

A compound literal behaves rather like an initialized variable, but is anonymous.


The printStudents function is passing the wrong arguments to printf(). It is passing the address of class->cap instead of the value of class->cap. And it is also passing the address of class->name instead of the the address of class->name[0]. That can be corrected by removing the & from both calls to printf:

    printf("Capacity => %d\n", class->cap);
    printf("Class => %s\n", class->name);

N.B. class->name is the same value as &class->name[0].


EDIT after OP changed the question

OP changed struct Student member class from struct Class *class to struct Class *class[5]. The previous initialization should still work, but student.class[1] through to student.class[4] will have the default initialization of NULL.

With OP's array of 3 classes, student can be initialized as follows:

struct Class class[] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

struct Student student = {"Ian", "saeidi",
                           { &class[0], &class[1], &class[2] } };

Full working code:

#include <stdio.h>

#define ARRAY_LEN(x) (sizeof (x) / sizeof *(x))

struct Student {
    char firstname[10];
    char lastname[10];
    struct Class *class[5];
};

struct Class {
    char name[10];
    int cap;
};

void printStudents(struct Student *student) {
    printf("First name => %s\n", student->firstname);
    printf("Last name => %s\n", student->lastname);
    for (int i = 0; i < ARRAY_LEN(student->class); i++) {
        struct Class *class = student->class[i];
        if (class) {
            printf("Class %d =>\n", i);
            printf("    Capacity => %d\n", class->cap);
            printf("    Name => %s\n", class->name);
        }
    }
}

int main(void) {
    struct Class class[] = {
        {"rust",   8},
        {"python", 22},
        {"java",   33}
    };
    struct Student student = {"Ian", "saeidi",
                               { &class[0], &class[1], &class[2] } };

    printStudents(&student);

    return 0;
}
Ian Abbott
  • 15,083
  • 19
  • 33
0

I have found the problem, the class array length should be determine during initialization:

#include <stdio.h>


    struct Class {
        char name[10];
        int cap;
    };

    struct Student {
        char firstname[10];
        char lastname[10];
        struct Class *class;
    };
    


     void printStudents(struct Student *student) {
        struct Class *class;
        class = student->class[2];
        printf("Capacity => %d\n", &class->cap);
        printf("Class => %s\n", &class->name);
    }



int main(void) {

// here I added the length of the class struct    
struct Class class[5] = {
            {"rust",   8},
            {"python", 22},
            {"java",   33}
    };

    struct Student student = {"Ian", "saeidi", &class};
    
        printStudents(&student);
    
        return 0;
    }

@ian abbott and other guys, Thanks for participating

Moss
  • 378
  • 7
  • 18
  • [This doesn't compile](https://godbolt.org/z/8W6P6a3qo), and you should always pay attention to the warnings. `&class` in `main` is a `struct Class (*)[5]`. That is, a pointer to a 5-sized array of `struct Class`es. That's not the same as `struct Class*`, which is simply a pointer to a `struct Class`. If you leave out the 5, the compiler correctly deduces it's sized 3, that's the only difference. And you still have the same incorrect arguments for `printf` as before. At least use `-Wall -Wextra` to turn on a healthy selection of warning checks when building. – yano Apr 14 '22 at 15:26
  • I got the expected result, And it’s compiled successfully, – Moss Apr 15 '22 at 05:37