cstructfunction-pointersgcc6

Why function inside an struct works C language


As shown in this small script.

#include <stdio.h>

struct student{
    short count;
    void (*addCount)();
};

void student_addCount(struct student a){
    a.count++;
}

int main(){
    struct student student;
    student.addCount = student_addCount;
    student.count = 0;
    student.addCount();
    student.addCount();
    student.addCount();
    student.addCount();
    student.addCount();
    printf("%d\n",student.count);
}

I have added a pointer to a function inside a struct, but I do not know why this works as the function 'addCount' is not receiving any arguments and it actually adds up the number of times specified.

I am compiling this code with GCC 6.3.0 in different environments such as ideone.com, wandbox.org and the compiler in WSL.

here is the proof that it works with ideone. https://ideone.com/Cam4xY


Solution

  • That's not a function inside a structure, it's a function pointer inside a structure.

    void (*addCount)();
    

    defines addCount as a pointer to a function. The function returns void and takes an unspecified number of arguments.

    That's what the empty parentheses mean. That's an old-style non-prototype function declaration. There is rarely, if ever, a good reason to use non-prototype function declarations or definitions. If you want to specify tht a function takes no arguments, use (void) rather than ().

    student.addCount = student_addCount;
    

    The function student_addCount takes a struct student argument, but its type is still compatible with the type of your pointer member. Assigning it like this essentially disables checking on calls. This is why old-style function declarations are a bad idea.

    student.addCount();
    

    This is an indirect call via the function pointer. Since the function pointer type doesn't specify how many arguments are expected, the call is legal. Since the actual function being called requires a single argument of type struct student, the behavior is undefined. With old-style function declarations, it's entirely up to you, the programmer, to get the arguments right; the compiler won't help you.

    Since you're getting what appear to be valid results, it's likely that the argument you expected to be passed happened to be in the right place in memory, perhaps on top of the stack. The function is called and it assumes, quite reasonably, that you've passed a proper argument value. It looks for that argument in memory, or in a register, and finds ... something.

    Here's a version of your program without the undefined behavior:

    #include <stdio.h>
    
    struct student{
        short count;
        void (*addCount)(struct student a);
    };
    
    void student_addCount(struct student a){
        a.count++;
    }
    
    int main(void){
        struct student student;
        student.addCount = student_addCount;
        student.count = 0;
        student.addCount(student);
        student.addCount(student);
        student.addCount(student);
        student.addCount(student);
        student.addCount(student);
        printf("%d\n",student.count);
    }
    

    The output is 0 -- because the count being incremented is a member of the parameter, which is a local object.

    Here's a version that does what you probably want. It passes a pointer to the structure, so the count member of your original struct student object is incremented by the function. The output is 5.

    #include <stdio.h>
    
    struct student{
        short count;
        void (*addCount)(struct student *a);
    };
    
    void student_addCount(struct student *a){
        a->count++;
    }
    
    int main(void){
        struct student student;
        student.addCount = student_addCount;
        student.count = 0;
        student.addCount(&student);
        student.addCount(&student);
        student.addCount(&student);
        student.addCount(&student);
        student.addCount(&student);
        printf("%d\n",student.count);
    }