struct#
For putting many values with different data types together in a single variable.
General syntax#
struct TagName {
Type member1;
Type member2;
};
Tagged struct#
struct Shape {
int x, y;
char *name;
}; // struct definition is similar to `enum`
struct Shape s = {.x = 0, .y = 0, .name = "banana"};
// Pay attention to `.`s
// Alternatively: {0, 0, "banana"} in short
int main() {
s.x = 5;
// Access elements with `.`
struct Shape s2 = {};
// Can be initialized similar to arrays
}
Activity 54 (Comparing structs)
Define a
structnamedPersonwith two members:agegender
Create two variables using the
struct:p1,p2.Write a function called
are_equal(a, b)that checks whether the two have the same data.
Note
As a convention, I use PascalCase for tags (and also typedef, enum, union, etc). It seems to be more common.
You will see also snake_case and _t suffix in other code. The most important is to stick to the conventions of a code base or project.
Anonymous struct#
We can also declare a variable directly like an enum, but then reusing it becomes more difficult:
Above example using an anonymous struct and typeof:
struct {
int x, y;
char *name;
} s = {.x = 0, .y = 0, .name = "banana"};
int main() {
s.x = 5;
typeof(s) s2 = {};
}
Tip
Anonymous struct is useful if you plan not to use the struct for further variables.
Typically you can stick to the tagged version.
pass-by-?#
Activity 55 (Passing a struct to a function)
The code below stores same values in an array and a struct. Analyze the following code and its output. Why are a and s not the same in the end?
#include <stdio.h>
#include <string.h>
int a[] = {4, 2};
struct {
int a, b;
} s = {4, 2};
void set_first_element_to_zero_array(typeof(a) a) { a[0] = 0; }
void set_first_element_to_zero_struct(typeof(s) s) { s.a = 0; }
void are_values_of_a_and_s_equal() {
puts(memcmp(a, &s, sizeof(a)) ? "a != b" : "a == b");
}
int main() {
are_values_of_a_and_s_equal();
puts("set_first_element_to_zero()");
set_first_element_to_zero_array(a);
set_first_element_to_zero_struct(s);
are_values_of_a_and_s_equal();
}
Output
a == b
set_first_element_to_zero()
a != b
Tip
Pay attention when you are passing large structs. Structs should be typically passed by address.
Activity 56 (Modifying struct members)
Modify set_first_element_to_zero_struct so the struct is passed by address.
Accessing members of a struct pointer using ->#
-> is the arrow operator.
struct Shape {
int x, y;
char *name;
};
struct Shape s;
typeof(s) *sp = &s;
int main() {
(*sp).x = 1; // ❌ this is silly
sp->x = 1; // ✅
}
Activity 57
Modify your code again to use the dereference operator.
Self-referential struct#
struct can point to itself. This is a superpower 💪:
#include <stdio.h>
#include <string.h>
struct node {
char *data;
struct node *next;
};
struct node tail = {"tej", nullptr}, b = {"pos", &tail}, a = {"ver", &b},
head = {"le", &a};
char puzzle[20], puzzle2[20];
int main() {
printf("puzzle: ");
strcat(puzzle, head.data);
strcat(puzzle, head.next->data); // a
strcat(puzzle, head.next->next->data); // b
strcat(puzzle, head.next->next->next->data); // tail
puts(puzzle);
// Alternatively through traversing the chain
printf("puzzle2: ");
struct node *cursor = &head;
while (cursor) {
strcat(puzzle2, cursor->data);
cursor = cursor->next;
}
puts(puzzle2);
}
puzzle: leverpostej
puzzle2: leverpostej
Fig. 23 A linked list. Each node contains data (e.g., 12) and a pointer to the next node.
Public domain. By Vectorization: Lasindi. Source: Wikimedia Commons#
We can build dynamic linked lists using this superpower.
- linked list
a linear collection of data elements whose order is not given by their physical placement in memory.
We will visit linked lists in a future chapter again.
How can we create an endless string using the linked list above?