Fundamental C - Basic Structs
Written by Harry Fairhead   
Monday, 29 April 2019
Article Index
Fundamental C - Basic Structs
Value Semantics
Pointers to Structs

Value Semantics

Defining a type for a struct is the first surprise, but if you have followed how the array works so well with reference semantics, you might be even more surprised to learn that structs use value semantics.

For example, if me and person are both instances of myStruct then:

person=me;

copies the fields of me into the fields of person.

To see this in action try:

struct myStruct me;
struct myStruct person;
me.age=19;
strcpy(me.name,"harry");
person=me;
person.age=21;
printf("%d %d\n",me.age,person.age);

You will see that me.age and person.age are two different fields in two different structs. A struct variable isn’t a pointer to struct but a value like an int.

When you assign a struct to another struct of the same type the compiler generates code that copies the fields from the source struct to the destination struct.

It is important to realize at this early stage that struct assignment only performs a shallow copy. What this means is that the block of storage assigned to the struct is copied to the destination struct. This is usually all that is needed, but if the block contains a pointer to another block of memory then the pointer will be copied, but the block will not. That is, for structs that contain a pointer, the pointer is copied, but not what it points at. This is a shallow copy. A deep copy would make a copy of the pointer and what it pointed at.

You might be wondering what is happening with the name field – surely this is a pointer to a string, i.e. a char array and a shallow copy would fail to copy the data?

Does this mean that me and person share the same copy of the name string? If you try:

me.name[0]=’X’;

just before you print person you will discover that changing me.name doesn’t change person.name. The reason is that the char array is declared within the struct and the 25 characters are actually stored in the struct. The constant pointer, i.e. the array variable me.name, references the 25 characters stored in the struct. When the me struct is assigned to the person struct the whole block of data including the 25 chars of the array are copied – hence they do not share the string.

Value semantics for structs doesn’t seem to be better than reference semantics, which seem more natural.

Why are structs implemented in this way?

It is a matter of preference, but one reason why structs are value types is that they are often small and more like extensions of the basic integer types which use value semantics.

For example, a common use of a struct is to implement a coordinate type:

struct point{
    int x;
    int y;
}

This takes just twice the memory that a single int takes and it is convenient to treat it like an over-sized int type. This makes working with small structs easier:

struct point a = {0, 0};
struct point b;
b=a;

which assigns 0,0 to b. It is arguable that this is a more reasonable interpretation for this sort of struct than reference semantics, which would result in a and b pointing to the same data.

This said, many programmers are of the opinion that you should always avoid using value semantics with structs by using pointers to structs, see later.

Struct Parameters and Return

Value semantics also apply when you pass a struct to a function.

For example:

void addToAge(struct myStruct person) {
    person.age = person.age + 1;
}

In this case adding one to age has no effect on any struct in the main program and the function doesn’t actually do anything useful. When you call it using:

addToAge(me);

it is as if the first thing that happens within the function is the equivalent of:

person = me;

and all of the fields of me are copied to person, i.e. value semantics.

The fact that structs use value semantics also means that you are safe to return a struct from a function. If the struct is local then it is destroyed when the function ends, but in this case it is not the struct that is returned, but the struct’s value, which is copied into whatever variable in the main program the function is assigned to.

For example:

struct myStruct makePerson(char *name,int age){
    struct myStruct person;
    strncpy(person.name,name,25);
    person.age=age;
    return person;
}

This function creates and initializes a struct which it returns. It can be used in the obvious way:

struct myStruct me = makePerson("harry",19);

Notice that what happens is that the new struct me is set to harry, 19 by the return value of the person struct in the function.

The original struct is not returned, only its values are.

Finally, it is important to keep in mind that the copy involved in value semantics in a return is “shallow”. The name field is stored within the struct and so it is copied along with the rest of the struct when the function returns. Any pointers to data outside of the struct would be copied, but not the data they point to.



Last Updated ( Monday, 27 May 2019 )