CS100 Lecture 9
struct
, Recursion
Contents
struct
-
Recursion
-
Factorial
- Print a non-negative integer
- Selection-sort
struct
struct
type
The name of the type defined by a struct
is struct Name
.
- Unlike C++, the keyword
struct
here is necessary.
struct Student stu; // `stu` is an object of type `struct Student`
struct Point3d polygon[1000]; // `polygon` is an array of 1000 objects,
// each being of type `struct Point3d`.
struct TreeNode *pNode; // `pNode` is a pointer to `struct TreeNode`.
* The term "object" is used interchangeably with "variable".
- Objects often refer to variables of
struct
(orclass
in C++) types. - But in fact, there's nothing wrong to say "an
int
object".
Members of a struct
Use obj.mem
, the member-access operator .
to access a member.
struct Student stu;
stu.name = "Alice";
stu.id = "2024533000";
stu.entrance_year = 2024;
stu.dorm = 8;
printf("%d\n", student.dorm);
++student.entrance_year;
puts(student.name);
Dynamic allocation
Create an object of struct
type dynamically: Just allocate sizeof(struct Student)
bytes of memory.
struct Student *pStu = malloc(sizeof(struct Student));
Member access through a pointer: ptr->mem
, or (*ptr).mem
(not *ptr.mem
!).
pStu->name = "Alice";
pStu->id = "2024533000";
(*pStu).entrance_year = 2024; // equivalent to pStu->entrance_year = 2024;
printf("%d\n", pStu->entrance_year);
puts(pStu->name);
As usual, don't forget to free
after use.
free(pStu);
Size of a struct
struct Student {
const char *name;
const char *id;
int entrance_year;
int dorm;
};
struct Student *pStu = malloc(sizeof(struct Student));
What is the value of sizeof(struct Student)
?
Size of struct
It is guaranteed that
The inequality is due to memory alignment requirements, which is beyond the scope of CS100.
Implicit initialization
What happens if an object of struct
type is not explicitly initialized?
struct Student gStu;
int main(void) {
struct Student stu;
}
Implicit initialization
What happens if an object of struct
type is not explicitly initialized?
struct Student gStu;
int main(void) {
struct Student stu;
}
- Global or local
static
: "empty-initialization", which performs member-wise empty-initialization. - Local non-
static
: every member is initialized to indeterminate values (in other words, uninitialized).
Explicit initialization
Use an initializer list:
struct Student stu = {"Alice", "2024533000", 2024, 8};
Use C99 designators: (highly recommended)
struct Student stu = {.name = "Alice", .id = "2024533000",
.entrance_year = 2024, .dorm = 8};
The designators greatly improve the readability.
[Best practice] Use designators, especially for struct
types with lots of members.
Compound literals
struct Student *student_list = malloc(sizeof(struct Student) * n);
for (int i = 0; i != n; ++i) {
student_list[i].name = A(i); // A, B, C and D are some functions
student_list[i].id = B(i);
student_list[i].entrance_year = C(i);
student_list[i].dorm = D(i);
}
Use a compound literal to make it clear and simple:
struct Student *student_list = malloc(sizeof(struct Student) * n);
for (int i = 0; i != n; ++i) {
student_list[i] = (struct Student){.name = A(i), .id = B(i),
.entrance_year = C(i), .dorm = D(i)};
}
struct
-typed parameters
The semantic of argument passing is copy:
void print_student(struct Student s) {
printf("Name: %s, ID: %s, dorm: %d\n", s.name, s.id, s.dorm);
}
print_student(student_list[i]);
In a call print_student(student_list[i])
, the parameter s
of print_student
is initialized as follows:
struct Student s = student_list[i];
The copy of a struct
-typed object: Member-wise copy.
struct
-typed parameters
In a call print_student(student_list[i])
, the parameter s
of print_student
is initialized as follows:
struct Student s = student_list[i];
The copy of a struct
-typed object: Member-wise copy. It is performed as if
s.name = student_list[i].name;
s.id = student_list[i].id;
s.entrance_year = student_list[i].entrance_year;
s.dorm = student_list[i].dorm;
Return a struct
-typed object
Strictly speaking, returning is also a copy:
struct Student fun(void) {
struct Student s = something();
some_operations(s);
return s;
}
student_list[i] = fun();
The object s
is returned as if
student_list[i] = s;
But in fact, the compiler is more than willing to optimize this process. We will talk more about this in C++.
Array member
struct A {
int array[10];
// ...
};
Although an array cannot be copied, an array member can be copied.
The copy of an array is element-wise copy.
int a[10];
int b[10] = a; // Error!
struct A a;
struct A b = a; // OK
Summary
A struct
is a type consisting of a sequence of members.
- Member access:
obj.mem
,ptr->mem
(equivalent to(*ptr).mem
, but better) -
sizeof(struct A)
, no less than the sum of size of every member. -
But not necessarily equal, due to memory alignment requirements.
- Implicit initialization: recursively performed on every member.
- Initializer-lists, designators, compound literals.
- Copy of a
struct
: member-wise copy. - Argument passing and returning: copy.
Recursion
Problem 1. Calculate \(n!\)
int factorial(int n) {
return n == 0 ? 1 : n * factorial(n - 1);
}
This is perfectly valid and reasonable C code!
- The function
factorial
recursively calls itself.Problem 2. Print a non-negative integer
If we only have getchar
, how can we read an integer?
- We have solved this in recitations.
If we only have putchar
, how can we print an integer?
- Declared in
<stdio.h>
. putchar(c)
prints a characterc
. That's it.
For convenience, suppose the integer is non-negative (unsigned).
Print a non-negative integer
To print \(x\):
- If \(x < 10\), just print the digit and we are done.
- Otherwise (\(x\geqslant 10\)), we first print \(\displaystyle\left\lfloor\frac{x}{10}\right\rfloor\), and then print the digit on the last place.
void print(unsigned x) {
if (x < 10)
putchar(x + '0'); // Remember ASCII?
else {
print(x / 10);
putchar(x % 10 + '0');
}
}
Simplify the code
To print \(x\):
- If \(x\geqslant 10\), we first print \(\displaystyle\left\lfloor\frac{x}{10}\right\rfloor\). Otherwise, do nothing.
- Print \(x\bmod 10\).
void print(unsigned x) {
if (x >= 10)
print(x / 10);
putchar(x % 10 + '0');
}
Print a non-negative integer
To print \(x\):
- If \(x\geqslant 10\), we first print \(\displaystyle\left\lfloor\frac{x}{10}\right\rfloor\). Otherwise, do nothing.
- Print \(x\bmod 10\).
void print(unsigned x) {
if (x >= 10)
print(x / 10);
putchar(x % 10 + '0');
}
Design a recursive algorithm
Suppose we are given a problem of scale \(n\).
- Divide the problem into one or more subproblems, which are of smaller scales.
- Solve the subproblems recursively by calling the function itself.
- Generate the answer to the big problem from the answers to the subproblems.
* Feels like mathematical induction?
Problem 3. Selection-sort
How do you sort a sequence of \(n\) numbers? (In ascending order)
Do it recursively.
How do you sort a sequence of \(n\) numbers \(\langle a_0,\cdots,a_{n-1}\rangle\)? (In ascending order)
Do it recursively: Suppose we are going to sort \(\langle a_k,a_{k+1},\cdots,a_{n-1}\rangle\), for some \(k\).
- If \(k=n-1\), we are done.
- Otherwise (\(k<n-1\)):
- Find the minimal number \(a_m=\min\left\{a_k,a_{k+1},\cdots,a_{n-1}\right\}\).
- Put \(a_m\) at the first place by swapping it with \(a_k\).
- Now \(a_k\) is the smallest number in \(\langle a_k,\cdots,a_{n-1}\rangle\). All we have to do is to sort the rest part \(\langle a_{k+1},\cdots,a_{n-1}\rangle\) recursively.
void sort_impl(int *a, int k, int n) {
if (k == n - 1) return;
int m = k;
for (int i = k + 1; i < n; ++i)
if (a[i] < a[m]) m = i;
swap(&a[m], &a[k]); // the "swap" function we defined in previous lectures
sort_impl(a, k + 1, n); // sort the rest part recursively
}