Structures and Functions in C

Like all other types, we can pass structures as arguments to a function. In fact, we can pass, individual members, structure variables, a pointer to structures etc to the function. Similarly, functions can return either an individual member or structures variable or pointer to the structure.

Let's start with passing individual member as arguments to a function.

Passing structure members as arguments to function

We can pass individual members to a function just like ordinary variables.

The following program demonstrates how to pass structure members as arguments to the function.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct student
{
    char name[20];
    int roll_no;
    int marks;
};

void print_struct(char name[], int roll_no, int marks);

int main()
{
    struct student stu = {"Tim", 1, 78};
    print_struct(stu.name, stu.roll_no, stu.marks);
    return 0;
}

void print_struct(char name[], int roll_no, int marks)
{
    printf("Name: %s\n", name);
    printf("Roll no: %d\n", roll_no);
    printf("Marks: %d\n", marks);
    printf("\n");
}

Expected Output:

Name: Tim
Roll no: 1
Marks: 78

How it works

In lines 7-12, a structure student  is declared with three members namely name, roll_no and marks.

In line 14, a prototype of function print_struct() is declared which accepts three arguments namely name of type pointer to char, roll_no of type int and marks is of type int.

In line 18, a structure variable stu of type struct student is declared and initialized.

In line 19, all the three members of structure variable stu are passed to the print_struct() function. The formal arguments of print_struct() function are initialized with the values of the actual arguments.

From lines 25-27, three printf() statement prints name, roll_no and marks of the student.

The most important thing to note about this program is that stu.name is passed as a reference because name of the array is a constant pointer. So the formal argument of print_struct() function i.e name and stu.name both are pointing to the same array. As a result, any changes made by the function print_struct() will affect the original array. We can verify this fact by making the following amendments to our program.

  • In the main function add the following line after the call to print_struct() function.
    printf("New name: %s", stu.name);
  • In print_struct() function add the following two lines just before the last printf() statement.
    printf("\nChanging name ... \n"); strcpy(name, "Jack");

Now our program should look like this:

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

/*
structure is defined above all functions so it is global.
*/

struct student
{
    char name[20];
    int roll_no;
    int marks;
};

void print_struct(char name[], int roll_no, int marks);

int main()
{
    struct student stu = {"Tim", 1, 78};
    print_struct(stu.name, stu.roll_no, stu.marks);

    printf("New name: %s", stu.name);

    return 0;
}

void print_struct(char name[], int roll_no, int marks)
{
    printf("Name: %s\n", name);
    printf("Roll no: %d\n", roll_no);
    printf("Marks: %d\n", marks);

    printf("\nChanging name ... \n");
    strcpy(name, "Jack");

    printf("\n");
}

Expected Output:

Name: Tim
Roll no: 1
Marks: 78

Changing name ...

New name: Jack

This verifies the fact that changes made by print_struct() function affect the original array.

Passing structure variable as argument to a function

In the earlier section, we have learned how to pass structure members as arguments to a function. If a structure contains two-three members then we can easily pass them to function but what if there are 9-10 or more members ? Certainly passing 9-10 members is a tiresome and error-prone process. So in such cases instead of passing members individually, we can pass structure variable itself.

The following program demonstrates how we can pass structure variable as an argument to the function.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct student
{
    char name[20];
    int roll_no;
    int marks;
};

void print_struct(struct student stu);

int main()
{
    struct student stu = {"George", 10, 69};
    print_struct(stu);
    return 0;
}

void print_struct(struct student stu)
{
    printf("Name: %s\n", stu.name);
    printf("Roll no: %d\n", stu.roll_no);
    printf("Marks: %d\n", stu.marks);
    printf("\n");
}

Expected Output:

Name: George
Roll no: 10
Marks: 69

How it works

In lines 7-12, a structure student is declared with three members namely name, roll_no and marks.

In line 14, the prototype of function print_struct() is declared which accepts an argument of type struct student.

In line 18, a structure variable stu of type struct student is declared and initialized.

In line 19, print_struct() function is called along with argument stu . Unlike arrays, the name of structure variable is not a pointer, so when we pass a structure variable to a function, the formal argument of print_struct() is assigned a copy of the original structure. Both structures reside in different memory locations and hence they are completely independent of each other. Any changes made by function print_struct() doesn't affect the original structure variable in the main() function.

printf() statements from lines 25-27 prints the details of the student.

Passing structure pointers as argument to a function

Although passing structure variable as an argument allows us to pass all the members of the structure to a function there are some downsides to this operation.

1. Recall that a copy of the structure is passed to the formal argument. If the structure is large and you are passing structure variables frequently then it can take quite a bit of time which make the program inefficient.

2. Additional memory is required to save every copy of the structure.

The following program demonstrates how to pass structure pointers as arguments to a function.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct employee
{
    char name[20];
    int age;
    char doj[10]; // date of joining
    char designation[20];
};

void print_struct(struct employee *);

int main()
{
    struct employee dev = {"Jane", 25, "25/2/2015", "Developer"};
    print_struct(&dev);

    return 0;
}

void print_struct(struct employee *ptr)
{
    printf("Name: %s\n", ptr->name);
    printf("Age: %d\n", ptr->age);
    printf("Date of joining: %s\n", ptr->doj);
    printf("Age: %s\n", ptr->designation);
    printf("\n");
}

Expected Output:

Name: Jin
Age: 25
Date of joining: 25/2/2015
Age: Developer

How it works

In lines 7-13, a structure employee is declared with four members namely name, age, doj(date of joining) and designation.

In line 15, the prototype of function print_struct() is declared which accepts an argument of type pointer to struct student.

In line 19, a structure variable dev of type struct employee is declared and initialized.

In line 20, print_struct() is called along with along with the address of variable dev. The formal argument of print_struct() is assigned the address of variable dev. Now ptr is pointing to the original structure, hence any changes made inside the function will affect the original structure.

printf() statements from lines 27-30 prints the details of the developer.

The downside of passing structure pointer to a function is that the function can modify the original structure. If that is what you intentionally want that's fine. However, if don't want functions to modify original structure use the const keyword. Recall that const keyword when applied to a variable makes it read-only. Let's rewrite the previous program using const keyword.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct employee
{
    char name[20];
    int age;
    char doj[10]; // date of joining
    char designation[20];
};

void print_struct(const struct employee *);

int main()
{
    struct employee dev = {"Jane", 25, "25/2/2015", "Developer"};
    print_struct(&dev);

    return 0;
}

void print_struct(const struct employee *ptr)
{
    printf("Name: %s\n", ptr->name);
    printf("Age: %d\n", ptr->age);
    printf("Date of joining: %s\n", ptr->doj);
    printf("Age: %s\n", ptr->designation);

    //ptr->age = 11;

    printf("\n");
}

Now even though we are passing a structure pointer to print_struct() function, any attempt to modify the values of the structure will result in compilation error. Try commenting out code in line 32 and see it yourself.

Array of structures as function arguments

We have already seen how to pass an array of integers to a function. Similarly, we can pass an array of structures to a function.

The following program demonstrates how we can pass an array of structures to a function.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct company
{
    char name[20];
    char ceo[20];
    float revenue; // in $
    float pps; // price per stock in $
};

void print_struct(const struct company str_arr[]);

int main()
{

    struct company companies[3] = {
                           {"Country Books", "Tim Green", 999999999, 1300 },
                           {"Country Cooks", "Jim Green", 9999999, 700 },
                           {"Country Hooks", "Sim Green", 99999, 300 },
                   };
    print_struct(companies);

    return 0;
}

void print_struct(struct company str_arr[])
{
    int i;

    for(i= 0; i<3; i++)
    {
        printf("Name: %s\n", str_arr[i].name);
        printf("CEO: %d\n", str_arr[i].ceo);
        printf("Revenue: %.2f\n", str_arr[i].revenue);
        printf("Price per stock : %.2f\n", str_arr[i].pps);
        printf("\n");
    }
}

Expected Output

Name: Country Books
CEO: 2686660
Revenue: 1000000000.00
Price per stock : 1300.00

Name: Country Cooks
CEO: 2686708
Revenue: 9999999.00
Price per stock : 700.00

Name: Country Hooks
CEO: 2686756
Revenue: 99999.00
Price per stock : 300.00

How it works

In lines 7-13, a structure company is declared with four members namely name, ceo, revenue, pps.

In line 15, the prototype of function print_struct() is declared which accepts an argument of type array of structures.

In lines 20-24, an array of structure called companies of type struct company is declared and initialized.

In line 25, print_struct() is called along with argument companies. Since the name of the array is a constant pointer to the 0th element of the array, the formal argument of print_struct() is assigned the address of variable companies. So now str_arr is pointing to the original array of structure, any changes made inside the function will affect the original structure. If you don't want to call a function to modify the original structure use the keyword const.

In line 32, variable i is declared to control the for loop.

In lines 34-41, a for loop is used to loops through the array of structure and prints the details of each company.

The control then passes to the main() function and the function terminates.

The formal arguments of print_struct() can also be declared as follows:

void print_struct(struct company *str_arr)
{
    int i;
    ...
}

Why ?

Recall that name of the array (companies) is a constant pointer to the 0th element of the array. In this case, 0th element is of type struct company. So the type of companies is a pointer to struct company or (struct company*). That's why str_arr is declared as a pointer to struct company or (struct company*).

Returning structure from function

Just as we can return fundamental types and arrays, we can also return a structure from a function. To return a structure from a function we must specify the appropriate return type in the function definition and declaration. Consider the following example:

struct player check_health(struct player p);
{
    ...
}

This function accepts an argument of type struct player and returns an argument of type struct player.

The following program demonstrates how we can return a structure from a function.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct player
{
    char name[20];
    float height;
    float weight;
    float fees;
};

void print_struct(struct player p);
struct player deduct_fees(struct player p);

int main()
{
    struct player p = {"Joe", 5.9, 59, 5000 };
    print_struct(p);
    p = deduct_fees(p);
    print_struct(p);

    return 0;
}

struct player deduct_fees(struct player p)
{
    p.fees -= 1000;
    return p;
}

void print_struct(const struct player p)
{
    printf("Name: %s\n", p.name);
    printf("Height: %.2f\n", p.height);
    printf("Weight: %.2f\n", p.weight);
    printf("Fees: %.2f\n", p.fees);

    printf("\n");
}

Expected Output:

Name: Joe
Height: 5.90
Weight: 59.00
Fees: 5000.00

Name: Joe
Height: 5.90
Weight: 59.00
Fees: 4000.00

How it works

In lines 7-13, a structure of type player is declared with 4 members namely name, height, weight and fees.

In line 15, the prototype of print_struct() is declared which accepts an argument of type struct player and returns nothing.

In line 16, the prototype of deduct_fees() is declared which accepts an argument of type struct player and returns a structure of type struct player.

In line 20, a structure variable p of type struct player is declared and initialized.

In line 21, the print_struct() function is passed an argument of type struct player. The function prints the details of the player and passes the control back to main() function.

In line 22, deduct_fees() function is called with an argument of type struct player. The function decrements the fees of the player by 1000 using the statement.

p.fees -= 1000;

and then returns the structure variable p to the called function i.e main(), where it is assigned back to the variable p.

In line 23, the print_struct() is called again with the same argument as before to check whether the details have been modified by deduct_fees() or not.

After printing the details of the function the control passes back to main() function and the program terminates.

Returning a structure pointer from the function

In the last section, we have learned that a function can return a structure variable. So it should be no surprise that it can also return a pointer to structure variable.

To return structure pointer from a function all we need to do is to specify the appropriate return type in the function definition and function declaration. For example:

struct *movie add_rating(struct movie *p);
{
    ...
}

This function accepts an argument of type pointer to struct movie and returns and a pointer of type struct movie.

The following program demonstrates how we can return structure pointers from a function.

#include<stdio.h>

/*
structure is defined above all functions so it is global.
*/

struct movie
{
    char title[20];
    char language[20];
    char director[20];
    int year;
    int rating;
};

void print_struct(const struct movie *p);
struct movie *add_rating(struct movie *p);

int main()
{
    struct movie m = {"The Accountant", "English" , "Gavin O'Connor", 2016, 1000};
    struct movie *ptr_m1 = &m, *ptr_m2;

    print_struct(ptr_m1);
    ptr_m2 = add_rating(ptr_m1);
    print_struct(ptr_m2);

    return 0;
}

struct movie *add_rating(struct movie *p)
{
    p->rating++; // increment rating by 1
    return p;
}

void print_struct(const struct movie *p)
{
    printf("Title: %s\n", p->title);
    printf("Language: %s\n", p->language);
    printf("Director: %s\n", p->director);
    printf("Year: %d\n", p->year);
    printf("Rating: %d\n", p->rating);

    printf("\n");
}

Expected Output

Title: The Accountant
Language: English
Director: Gavin O'Connor
Year: 2016
Rating: 1000

Title: The Accountant
Language: English
Director: Gavin O'Connor
Year: 2016
Rating: 1001

How it works

In lines 7-14, a structure movie is declared with 5 members namely title, language, director, year and rating.

In line 16, the prototype of function print_struct() is declared which accepts an argument of type pointer to struct movie and returns nothing.

In line 17, another prototype of function add_rating() is declared which accepts an argument of type pointer to struct movie and also returns a pointer of type struct movie.

In line 21, a struct variable m of type struct movie is declared and initialized.

In line 22, two pointer variables ptr_m1 and ptr_m2 of type struct movie are declared and ptr_m1 is assigned the address of m.

In line 24, print_struct() is called to print the details of the movie.

In line 25, add_rating() function is called along with the address of variable m. The function modifies the value of rating and returns the pointer to the called function where it is assigned to a pointer variable ptr_m2.

In line 26, print_struct() is called again but this time ptr_m2 is passed to it. After printing the details the control is transferred back to main() and the program terminates.