Predefined types are not enough when data is complex and must be structured according to your own criteria. C/C++ lets the programmer define new types, tailored exactly to the application's needs.
1Lab objectives
- Renaming types with
typedeffor more readable code - Defining and using enumerations
- Building structures and accessing members
- Understanding memory alignment and padding bytes
- Using bit fields and unions
2Overview
| Mechanism | What it does | When it is used |
|---|---|---|
typedef | renames an existing type | simplifying complicated declarations |
enum | defines a set of named integer constants | states, options, error codes |
struct | groups data of different types under one name | records: student, product, point |
| bit fields | members allocated at bit level | hardware registers, compact flags |
union | several members that share the same zone | memory savings, reinterpreting data |
In addition to C, the C++ language offers the definition of classes - types that follow the principles of object-oriented programming (covered in Laboratory 13).
3The typedef declaration
typedef unsigned char byte; // byte becomes a synonym for unsigned char typedef unsigned int uint; typedef int Vector[10]; // Vector is "an array of 10 integers" typedef int (*Operation)(int, int); // pointer to a function byte mask = 0xF0; uint counter = 0; Vector v; // equivalent to int v[10]; Operation op; // equivalent to int (*op)(int,int);
int (*table[5])(int, int);
is hard to read. With typedef it becomes Operation table[5]; - the same
meaning, but understandable at a glance.4Enumeration
enum <enum_type_id> { elem_id <= const>, ... } <var_id_list>;
enum Color { RED, GREEN, BLUE }; // 0, 1, 2 by default
enum Code { OK = 0, ERROR = -1, WARNING = 10, SEVERE }; // SEVERE becomes 11
enum boolean { false, true }; // false=0, true=1
typedef enum { false, true } boolean; // equivalent declaration
| Rule | Detail |
|---|---|
| Default value | the first element is 0, each next one increases by 1 |
| Explicit values | can be assigned; the following elements continue from that value |
| Name uniqueness | identifiers must be distinct within their scope |
| Underlying type | compatible with int |
if (state == 2) with
if (state == CONNECTED). The second form explains itself, and if the numeric values
change, the code stays correct with no change at all.5Structures
struct Student {
char name[50];
int age;
float average;
}; // note: semicolon is mandatory
struct Student s1; // in C the word struct must be repeated
Student s2; // in C++ it is not needed
typedef struct { // typedef variant, common in C
char name[50];
int age;
float average;
} Student_t;
Student_t s3 = {"John Smith", 20, 9.15}; // initialization at declaration
s3.age = 21; // access through the dot
Student_t *p = &s3;
p->average = 9.40; // access through the arrow, when we have a pointer
| Operation | Syntax | Notes |
|---|---|---|
| Member access | s.member | for ordinary variables |
| Access through a pointer | p->member | equivalent to (*p).member |
| Assignment | s2 = s1; | allowed - all members are copied |
| Comparison | s1 == s2 | forbidden - must be compared member by member |
== operator is
not defined for structures, precisely because of the padding bytes, whose contents are
undefined. Two structures with the same values can have different binary representations.6Simulator: memory alignment
The compiler inserts padding bytes so that each member starts at a suitable address. Compare the two structures below: they contain exactly the same members, but in a different order - and they take up different amounts of space.
double, then int, then short, then char). In
an array of a million structures, the savings become significant.7Bit fields
A structure's members can be allocated at bit level rather than byte level. The number of bits is specified after the member's name:
struct ControlRegister {
unsigned int enabled : 1; // 1 bit -> values 0 or 1
unsigned int mode : 3; // 3 bits -> values 0..7
unsigned int priority : 4; // 4 bits -> values 0..15
unsigned int reserved : 8; // 8 unused bits
}; // total 16 bits, that is, 2 bytes
struct ControlRegister reg = {1, 5, 12, 0};
| Advantage | Disadvantage |
|---|---|
| Considerable memory savings | access is slower (requires shifts and masks) |
| Direct correspondence with hardware registers | bit order depends on the compiler |
| More readable code than manual masks | the address of a field cannot be taken with & |
8Unions
A union looks like a structure, but all members share the same memory zone. The union's size is that of its largest member, and only one member is valid at any given time.
struct S { int a; float b; char c; }; // sizeof = 12 (4 + 4 + 1 + padding)
union U { int a; float b; char c; }; // sizeof = 4 (the largest member)
union U u;
u.a = 65; // written as an integer
printf("%c\n", u.c); // read as a character: 'A'
// ...because it is the SAME memory zone
enum-typed field.9Source code
#include <stdio.h>
#include <string.h>
typedef struct {
char name[50];
int age;
float average;
} Student;
void print(const Student *s) // we receive the address, not a copy
{
printf("%-20s %3d years average %.2f\n", s->name, s->age, s->average);
}
int main(void)
{
Student group[3] = {
{"John Smith", 20, 9.15f},
{"Mary Johnson", 21, 8.70f},
{"Radu Georgescu",20, 9.60f}
};
printf("Group list:\n");
for (int i = 0; i < 3; i++) print(&group[i]);
// computing the overall average
float sum = 0;
for (int i = 0; i < 3; i++) sum += group[i].average;
printf("\nOverall average: %.3f\n", sum / 3);
// finding the student with the highest average
int pos = 0;
for (int i = 1; i < 3; i++)
if (group[i].average > group[pos].average) pos = i;
printf("Highest average: %s\n", group[pos].name);
printf("\nsizeof(Student) = %zu bytes\n", sizeof(Student));
return 0;
}
#include <stdio.h>
typedef enum {
STOPPED, // 0
STARTING, // 1
RUNNING, // 2
ERROR = 99
} State;
const char* stateName(State s)
{
switch (s) {
case STOPPED: return "STOPPED";
case STARTING: return "STARTING";
case RUNNING: return "RUNNING";
case ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
int main(void)
{
State s = STOPPED;
printf("Initial state: %s (%d)\n", stateName(s), s);
s = STARTING; printf("-> %s (%d)\n", stateName(s), s);
s = RUNNING; printf("-> %s (%d)\n", stateName(s), s);
s = ERROR; printf("-> %s (%d)\n", stateName(s), s);
return 0;
}
#include <stdio.h>
#include <stddef.h>
struct Unfavorable { char c; int x; char d; };
struct Optimized { int x; char c; char d; };
int main(void)
{
printf("Unfavorable: sizeof = %zu\n", sizeof(struct Unfavorable));
printf(" offset c = %zu\n", offsetof(struct Unfavorable, c));
printf(" offset x = %zu\n", offsetof(struct Unfavorable, x));
printf(" offset d = %zu\n", offsetof(struct Unfavorable, d));
printf("\nOptimized: sizeof = %zu\n", sizeof(struct Optimized));
printf(" offset x = %zu\n", offsetof(struct Optimized, x));
printf(" offset c = %zu\n", offsetof(struct Optimized, c));
printf(" offset d = %zu\n", offsetof(struct Optimized, d));
printf("\nSame members, different space!\n");
return 0;
}
#include <stdio.h>
union Converter {
unsigned int value;
unsigned char bytes[4];
};
int main(void)
{
union Converter c;
c.value = 0x12345678;
printf("Value: 0x%X\n", c.value);
printf("Bytes in memory: ");
for (int i = 0; i < 4; i++) printf("%02X ", c.bytes[i]);
printf("\n");
// on Intel/AMD processors (little-endian), 78 56 34 12 is printed
if (c.bytes[0] == 0x78) printf("Little-endian system\n");
else printf("Big-endian system\n");
printf("\nsizeof(union) = %zu bytes\n", sizeof(union Converter));
return 0;
}
10Code workshop
Structures group data of different types under a single name. In the panel on the right, a structure appears with all of its fields, and the address shows where it begins in memory.
11Work tasks
- Define a
Productstructure (code, name, price, stock) and an array of products. - Write functions to print the list, compute the total stock value, and search by code.
- Sort the array of structures by price, using an exchange sort.
- Use
sizeofandoffsetofto check where padding bytes appear. - Reorder the structure's members and measure the space savings obtained.
- Define an enumeration for the days of the week and print the day's name from a number.
- Build a structure with bit fields describing an 8-bit control register.
- Use a union to determine whether the system is little-endian or big-endian.
12Extended application
enum-typed member for the student's status (funded, fee-paying,
expelled) and a bit field that stores the results of eight exams. Compare the size of the
structure before and after optimizing the order of its members.