Deep C Dives: Value Structs |
Written by Mike James | |||||
Wednesday, 27 August 2025 | |||||
Page 2 of 4
Value SemanticsStructs, unlike arrays, apply value semantics. What this means is that the variable associated with the struct behaves as if the struct was its value. Contrast this to the variable associated with an array behaving as if it was a reference i.e. a pointer to the array. You cannot assign one array to another because the variables only hold references to arrays, but you can assign an array to a pointer: int myArray1[10]; myArray1[0] = 42; int *myArray2 = myArray1; myArray2[0] = 43; In this case, changing myArray2[0] also changes myArray1[0] as there is only one array that both variables reference. This is reference semantics at work. Compare this to: myStructType myStruct1; myStruct1.myfield1=42; myStructType myStruct2=myStruct1; myStruct2.myfield1=43; In this case, the contents of myStruct1 are copied to the new myStruct2. If you now change myStruct2 it has no effect on myStruct1. This is value semantics at work. Put simply, unless you explicitly use a pointer, any operations involving a struct generally involve making a copy of all of the struct’s data. When you pass an array to a function then it is a pointer which is passed and the function works with the same copy of the array as the calling program. When you pass a struct to a function, a copy of the struct is made on the stack for the function to work with in isolation, i.e. it is passed by value. What is more a struct is returned by value. That is, if you create a struct within a function as an auto then it is created on the stack. If it is returned to the calling program then its value is copied to the calling program’s stack and it is safe to use, even though the original is destroyed. This is not the case with arrays. For more about functions, value and reference see Dive 13. It comes as a surprise to many programmers that structs are value types for the simple reason that a struct can be large and making copies of large amounts of data isn’t very efficient. The solution to this problem is to make use of a pointer to a struct and pass this instead. As a C program becomes more sophisticated, the tendency to use pointer to struct makes a struct look more like a reference type. It’s Just a Block of MemoryAt this point it is worth starting to think of the struct type as defining how a block of memory is divided up into subtypes. For example, when you write: struct myStructType{ int myfield1; float myfield2; }; you are saying that any block of memory that this struct is applied to will have sizeof(int) bytes as an int followed by sizeof(float) bytes as a float. When you create such a struct, the compiler allocates the correct amount of memory to store the field. This means that struct has to have a known size at compile time. What this means in practice is that you can’t use a VLA in a struct and any types you use have to be fully defined at compile time, although see later for how to include a dynamic array. The memory for the struct is allocated according to where the declaration is. If it is at file-level then the memory that the struct needs is compiled into the program. It if is within a function, then it is allocated on the stack. You can also create a struct on the heap by simply allocating the memory and casting to the type: struct myStructType *myStruct= (struct myStructType *) malloc(sizeof(struct myStructType)); All of the usual lifetime rules apply to structs. Notice that a pointer to a struct can be used in the usual way: (*myStruct).myfield1 = 42; but you do need the parentheses as the . operator has a higher priority than the * operator. To make this easier you can also use the -> operator to dereference and access the field: myStruct->myfield1 = 42; No dereferencing or parentheses needed. |
|||||
Last Updated ( Wednesday, 27 August 2025 ) |