© Mikael Olsson 2019
Mikael OlssonModern C Quick Syntax Referencehttps://doi.org/10.1007/978-1-4842-4288-9_13

13. Structs

Mikael Olsson1 
(1)
Hammarland, Länsi-Suomi, Finland
 
A struct or structure is a user-defined type used for grouping a collection of related variables under a single name. To define a structure you use the struct keyword, followed by an optional identifier and a code block containing variable declarations. The definition of this new type ends with a semicolon.
struct point {
  int x, y;
};

Unlike arrays, structs allow data items of different kinds to be combined. Structs may contain variables, pointers, arrays, or other user-defined types. In contrast to C++, structs in C may not contain functions.

Struct Objects

To declare a variable of a struct type, the struct keyword is followed by the type name and the variable identifier. Variables of struct type are commonly referred to as objects or instances.
int main(void) {
  struct point p; /* object declaration */
}
Objects may also be created when the struct is defined, by placing the object names before the final semicolon. This position is called the declarator list. If the optional struct identifier is left out, this becomes the only way to create objects of the struct type. Such a struct without an identifier is called an unnamed struct and provides a way for programmers to prevent any more instances of the type from being created.
struct /* unnamed struct */
{
  int x, y;
} a, b; /* object declarations */
It is common in C to use typedef when defining structures. This aliasing removes the need to include the struct keyword when declaring objects of the struct type, resulting in the shorter syntax used in C++.
typedef struct point point;
struct point {
  int x, y;
};
int main(void) {
  point p; /* struct omitted */
}

Member Access

Variables of a struct type are called fields or members. These fields are accessed using the member of operator (.) prefixed by the object name. Fields of an object are by default undefined, so it is important to assign them a value before they are read.
int main(void) {
  point p;
  p.x = 1;
  p.y = 2;
}
Similar to an array, struct objects may also be initialized when they are declared by enclosing the values in curly brackets. The values are then assigned in order based on the corresponding members of the struct. This way of assigning values to a composite type is known as aggregate initialization.
struct point {
  int x, y;
} r = { 1, 2 }; /* assigns x and y */
int main(void) {
  point p = { 1, 2 };
}
C99 introduced designated initializers, which allow structures to be initialized in any order by specifying the names of the fields. Any omitted fields will be automatically initialized to 0.
int main(void) {
  point p = { .y = 2, .x = 1 };
}

Struct Pointers

A struct is a value type, not a reference type like an array. As such, any assignment or argument passing for objects will copy the field values and not the object reference. This is different from many modern languages where composite types are automatically assigned and passed by reference.
int main(void) {
  point p = { 1, 2 };
  point r = p; /* copies field values */
}
For large structures the performance cost of this copy operation may be significant. Therefore it is common to use pointers when passing objects to functions, to avoid having to copy and return the whole object.
void init_struct(point* a) {
  (*a).x = 1;
  (*a).y = 2;
}
int main(void) {
  point p;
  init_struct(&p);
}
As shown in this example, the pointer must be dereferenced before the member of the operator can be used to access the fields. Since this operation is so common, there is a syntactical shortcut available, known as the infix operator (->), which automatically dereferences the pointer.
point p;
point* r = &p;
r->x = 1; /* same as (*r).x = 1; */
 

Bit Fields

The C programming language offers a way to optimize memory use within a struct type by allowing the bit length of integer fields to be specified. Such a field is called a bit field , and its length is set by placing a colon after the field name followed by the number of bits. The length must be less than or equal to the bit length of the specified type.
struct my_bits
{
  unsigned short f1 : 1;
  unsigned short f2 : 1;
  unsigned short id : 10;
} a;
Bit fields are packed as compactly as possible, while keeping in mind that the size of an object needs to be a multiple of the size of the types it contains. In this case the needed 12 bits will require 16 bits (two bytes) to be reserved for the object, as that is the size of the short type. Had bit fields not been used, the three shorts and consequently the struct would occupy 48 bits instead.
int main(void) {
  printf("%d bytes", sizeof(a)); /* "2 bytes" */
}

This feature is useful when programming for embedded systems, where hardware resources may be very constrained.