What is the difference between passing a structure by pointer or by value in this case?

Viewed 55

So I am working through the C Primer Plus book and I came across a programming assignment on structures and files. The main goal is to make a seat reservation program for a plane.

I defined the following structures,

typedef struct {
    unsigned short identification_number;
    unsigned short status;
    char last_name[MAX_NAM_LEN];
    char first_name[MAX_NAM_LEN];
} Seat;

typedef struct {
    unsigned short empty_seats;
    Seat seats[]; // Flexible Array Member
} Plane;

and I have the following function that prints the identification numbers of the empty seats,

/*
 * Function:  print_empty_seats_ID
 * -------------------------
 * Prints a list of empty seats identification numbers of the given plane.
 *
 */
void print_empty_seats_ID (Plane* plane, int num_seats) {

    // Clear the screen.
    system("clear");

    printf("Identification number of empty seats:");

    for (int i = 0; i < num_seats; i++) {
        if (plane->seats[i].status == EMPTY)
            printf(" %d", plane->seats[i].identification_number);
    }
    puts("");

    // Redirect the user to the main menu.
    puts("Redirecting in 4 seconds...");
    sleep(4);
}

which is called from another function this way, print_empty_seats_ID (plane, num_seats); such that plane is a pointer to the plane structure defined previously on the heap and num_seats is the total number of seats on the plane.

This function works well, but if I change it to use call by value instead of pointers it prints garbage values as illustrated next.

/*
 * Function:  print_empty_seats_ID
 * -------------------------
 * Prints a list of empty seats identification numbers of the given plane.
 *
 */
void print_empty_seats_ID (Plane plane, int num_seats) {

    // Clear the screen.
    system("clear");

    printf("Identification number of empty seats:");

    for (int i = 0; i < num_seats; i++) {
        if (plane.seats[i].status == EMPTY)
            printf(" %d", plane.seats[i].identification_number);
    }
    puts("");

    // Redirect the user to the main menu.
    puts("Redirecting in 4 seconds...");
    sleep(4);
}

In this case the function is called this way print_empty_seats_ID (*plane, num_seats); and outputs the following nonsense Identification number of empty seats: 64 0 0.

My question is why does the first function works and the second one doesn't ?

2 Answers

The problem is that you're using a so-called flexible array member, about which the spec states:

In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

So, when you pass it by value, the seats themselves are not copied. When you pass it by pointer, the pointer points to the original Plane which does have seats, so it works..

The problem is that your Plane has a flexible array member seats, i.e. a trailing array with no specified size. These are defined in C11 6.7.2.1p18:

18 As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

The example 6.7.2.1p25 clarifies this:

25 The assignment:

*s1 = *s2;

[where both are pointers to structures with a flexible array member] only copies the [non-flexible members]; if any of the array elements are within the first sizeof (struct s) bytes of the structure, they might be copied or simply overwritten with indeterminate values.

And function arguments in C are passed as if by assignment, therefore this applies to plane being passed by value.

Related