1.1 Analysis of Data Types
1.1.1 Array Parameters Decay to Pointers
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
// When an array is passed as a function parameter, it decays to a pointer.
// The size inside the brackets for the array parameter is ignored, but cannot be negative or zero.
// void print_array(int a[1], int n)
// void print_array(int a[], int n)
void print_array(int *a, int n) {
int i = 0;
printf("Array elements: ");
for (i = 0; i < n; i++) {
printf("%d ", a[i]);
}
printf("\n");
// 'a' is treated as a pointer. On a 32-bit compiler, pointers occupy 4 bytes.
int m = sizeof(a) / sizeof(a[0]); // number of elements
printf("After decay, m = %d\n", m);
}
int main(void) {
int arr[] = {10, 7, 1, 9, 4, 6, 7, 3, 2, 0};
int n = 10;
int m = sizeof(arr) / sizeof(arr[0]);
printf("Original array element count m = %d\n", m);
print_array(arr, n);
system("pause");
return 0;
}

1.1.2 Essence of Data Types
- A data type can be seen as a mold for creating variables: it is an alias for a fixed memory size.
- Purpose of data types: the compiler estimates the memory space to allocate for an object (variable).
- Note: data types are just molds; the compiler does not allocate space until a variable (entity) is created based on the type.
#include <stdio.h>
int main(void) {
int a = 10; // instructs compiler to allocate 4 bytes
int b[10]; // instructs compiler to allocate 4 * 10 = 40 bytes
printf(" b: %d\n b+1: %d\n &b: %d\n &b+1: %d\n", b, b + 1, &b, &b + 1);
// b+1 and &b+1 differ because 'b' and '&b' have different data types.
// 'b' represents the address of the first element.
// '&b' represents the address of the entire array, which has the same value as the first element's address.
return 0;
}

1.1.3 Size of Data Types
#include <stdio.h>
int main(void) {
int a = 10; // allocate 4 bytes
int b[10]; // allocate 40 bytes
printf("sizeof(a): %d\n", sizeof(a));
printf("sizeof(int *): %d\n", sizeof(int *));
printf("sizeof(b): %d\n", sizeof(b));
printf("sizeof(b[0]): %d\n", sizeof(b[0]));
printf("sizeof(*b): %d\n", sizeof(*b));
printf("*b: %d\n", *b);
return 0;
}

1.1.4 Typedef for Data Types
#include <stdio.h>
struct Person {
char name[64];
int age;
};
typedef struct Person {
char name[64];
int age;
} person_t;
typedef unsigned int u32; // alias for unsigned int
int main(void) {
struct Person p1;
person_t p2;
u32 a;
p1.age = 10;
p2.age = 11;
a = 10;
return 0;
}
1.2 Essence of Variables
- Programs use variables to request and name memory space:
int a = 0; - Access memory space through variable names.
- Methods to modify a variable:
- Directly
- Indirectly
#include <stdio.h>
#include <stdlib.h>
int main(void) {
int i = 0;
// Directly modify memory via variable
i = 10;
int *p = &i;
printf("&i: %d\n", &i);
printf("p: %d\n", p);
// Indirectly modify memory via pointer
*p = 100;
printf("i = %d, *p = %d\n", i, *p);
system("pause");
return 0;
}
1.3 Memory Four-Area Model

1.3.1 Global Area
#include <stdio.h>
char *getStr1() {
char *p1 = "abcdefg2"; // string literal in constant area
return p1;
}
char *getStr2() {
char *p2 = "abcdefg2"; // same literal, same address
return p2;
}
int main(void) {
char *p1 = NULL;
char *p2 = NULL;
p1 = getStr1();
p2 = getStr2();
printf("p1: %s, p2: %s\n", p1, p2);
printf("p1: %p, p2: %p\n", p1, p2);
return 0;
}
Since both strings are identical, the compiler allocates only one memory location.

1.3.2 Stack Area
Stack space is allocated and reclaimed by the system.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *get_str() {
char str[] = "abdefhgj"; // stack area
return str;
}
int main(void) {
char buf[128] = {0};
strcpy(buf, get_str());
printf("buf = %s\n", buf); // garbage or unpredictable
printf("\n");
system("pause");
return 0;
}

In this compiler, the string is copied before the stack memory is freed, so buf gets a determinate value. Different compilers may behave differently.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *get_str() {
char str[] = "abdefhgj"; // stack area
printf("str = %s\n", str);
return str;
}
int main(void) {
char buf[128] = {0};
char *p = NULL;
p = get_str();
printf("p = %s\n", p); // junk
printf("\n");
system("pause");
return 0;
}

Here, p points to the now-invalid stack memory, leading to undefined behavior.
1.3.3 Heap Area
Heap space is allocated and freed by the programmer.
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
char *get_str2() {
// Allocate 100 bytes on the heap, return its address.
// Cast to char* and assign to a char* pointer.
// If allocation fails, malloc returns NULL.
char *tmp = (char *)malloc(100);
if (tmp == NULL) {
return NULL;
}
strcpy(tmp, "absfsgjghgu");
return tmp;
}
int main(void) {
char *p = NULL;
p = get_str2();
if (p != NULL) {
printf("p = %s\n", p);
free(p);
p = NULL;
}
system("pause");
return 0;
}

1.4 Function Call Model

2. Pointers
2.1 Strengthening Pointers
2.1.1 Pointer as a Data Type
- A pointer variable occupies memory and stores an address. Test its size.
*poperates on memory:- In declaratino,
*indicates a pointer. - In usage,
*dereferences the pointer to access the value at the pointed memory.*pon the left side of=writes to memory; on the right side, it reads from memory.
- In declaratino,
- The pointer variable and the memory it points to are separate concepts.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *p = NULL;
char buf[] = "abcdef";
printf("p1 = %d\n", p);
p = buf;
printf("p2 = %d\n", p);
p = p + 1; // change pointer value, i.e., change where it points
printf("p3 = %d\n", p);
printf("buf = %s\n", buf);
printf("*p = %c\n", *p); // single character
printf("*p = %s\n", p); // string from new position
printf("Changing the content pointed to does not affect the pointer's value\n");
buf[1] = '2';
printf("p3 = %d\n", p);
printf("buf2 = %s\n", buf);
*p = 'm';
printf("p4 = %d\n", p);
printf("buf3 = %s\n", buf);
system("pause");
return 0;
}

Note: When writing to memory, ensure it is writable.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char *str_literal = "abcsdefh"; // string literal, read-only
// str_literal[2] = '3'; // error: writing to read-only memory
char arr[] = "sbgdjdgj";
arr[1] = '3'; // OK
printf("arr = %s\n", arr);
system("pause");
return 0;
}
- A pointer's type determines the step size when incrementing (
p++is equivalent to(unsigned char*)p + sizeof(*p)). - Assigning to a pointer changes its value (where it points), unrelated to the pointed memory.
- Do not copy memory to NULL or unknown illegal addresses.
char buf[100] = "absfdghh";
int i;
for (i = 0; i < strlen(buf); i++) {
p = &buf[i]; // or p = buf + i;
printf("p = %d, %c\n", p, *p);
}
2.1.2 Modifying Arguments via Pointers
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int get_a() {
int a = 10;
return a;
}
void get_a2(int a) {
a = 22; // local copy, does not affect original
}
void get_a3(int *p) {
*p = 33; // modify through pointer
}
void get_a4(int *a1, int *a2, int *a3, int *a4) {
*a1 = 1;
*a2 = 2;
*a3 = 3;
*a4 = 4;
}
int main(void) {
int a = get_a();
printf("a = %d\n", a);
get_a2(a);
printf("a2 = %d\n", a); // unchanged
get_a3(&a);
printf("a3 = %d\n", a); // changed
int a1, a2, a3, a4;
get_a4(&a1, &a2, &a3, &a4);
printf("a1=%d, a2=%d, a3=%d, a4=%d\n", a1, a2, a3, a4);
system("pause");
return 0;
}

2.1.3 Indirect Assignment via Double Pointers
int *p = 0x1122;
int **q = &p; // q is a pointer to pointer p
// *q gets the value of p (the address it holds)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void fun(int *p) {
p = (int*)0xaabb;
printf("fun: p = %p\n", p);
}
void fun2(int **p) {
*p = (int*)0xeeff;
printf("fun2: *p = %p\n", *p);
}
int main(void) {
int *p = (int*)0x1122;
printf("p = %p\n", p);
fun(p); // pass by value: p unchanged
printf("fun: p1 = %p\n", p);
fun2(&p); // pass address: p changed
printf("fun2: p2 = %p\n", p);
system("pause");
return 0;
}

How to declare a pointer of appropriate type: To store the address of a variable, add a * to its type.
int b;
int *q = &b; // q can store address of b
int **t = &q; // t can store address of q
2.1.4 Understanding Double Pointers
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
int a = 10;
int *p = &a;
printf("hex p = %p\n", p);
printf("hex &a = %p\n", &a);
printf("dec p = %d\n", p);
printf("dec &a = %d\n", &a);
printf("&p = %d\n", &p);
printf("*p = %d\n", *p);
printf("a = %d\n", a);
int *q = (int*)0x1122;
printf("dec q = %d\n", q);
printf("&q = %d\n", &q);
// printf("*q = %d\n", *q); // undefined
int **q1 = &p;
printf("q1 = %d\n", q1); // value of q1 is address of p
printf("*q1 = %d\n", *q1); // value at q1 is value of p (address of a)
system("pause");
return 0;
}

2.1.5 Pointer Parameters: Input and Output Characteristics
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void fun(char *p /* in */) {
strcpy(p, "abcdefg");
}
void fun2(char *p) {
if (p == NULL) return;
strcpy(p, "abcdefg");
}
void fun3(char **p /* out */, int *len) {
if (p == NULL) return;
char *tmp = (char*)malloc(100);
if (tmp == NULL) return;
strcpy(tmp, "abcdefg");
*p = tmp;
*len = strlen(tmp);
}
int main(void) {
// Input: caller allocates memory
char buf[100] = {0};
fun(buf);
printf("buf = %s\n", buf);
char *str = NULL;
fun2(str); // dangerous: copying to NULL or unknown address
printf("str = %d\n", str);
// Output: callee allocates memory, pass by address
char *p = NULL;
int len = 0;
fun3(&p, &len);
if (p != NULL) {
printf("p = %s, len = %d\n", p, len);
}
system("pause");
return 0;
}

3. Strings
3.1 Basic String Operations
3.1.1 String Initialization
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char buf[] = {'a', 'b', 'c'}; // no null terminator, prints garbage
printf("buf = %s\n", buf);
char buf1[100] = {'a', 'b', 'c'}; // rest filled with 0
printf("buf1 = %s\n", buf1);
char buf2[100] = {0};
printf("buf2 = %s\n", buf2);
char buf3[2] = {'1', '2'}; // overflow
printf("buf3 = %s\n", buf3);
char buf4[50] = {'1', 'a', 'b', '0', '7'};
printf("buf4 = %s\n", buf4);
char buf5[50] = {'1', 'a', 'b', 0, '7'};
printf("buf5 = %s\n", buf5);
char buf6[50] = {'1', 'a', 'b', '\0', '7'};
printf("buf6 = %s\n", buf6);
char buf7[] = "asbfhjh"; // commonly used
printf("strlen = %d, sizeof = %d\n", strlen(buf7), sizeof(buf7));
char buf8[100] = "asbfhjh";
printf("strlen = %d, sizeof = %d\n", strlen(buf8), sizeof(buf8));
system("pause");
return 0;
}

3.1.2 Accessing Strings with Pointers and Arrays
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char buf[] = "gasdfgjkp";
int i = 0;
int n = strlen(buf);
// Using array subscript
for (i = 0; i < n; i++) {
printf("%c", buf[i]);
}
printf("\n");
// Using pointer
char *p = buf;
for (i = 0; i < n; i++) {
printf("%c", p[i]);
}
printf("\n");
// p[i] is equivalent to *(p+i)
for (i = 0; i < n; i++) {
printf("%c", *(p + i));
}
printf("\n");
for (i = 0; i < n; i++) {
printf("%c", *(buf + i));
}
// buf and p are not entirely equivalent: p++ is legal, buf++ is not (buf is constant)
printf("\n");
system("pause");
return 0;
}
3.1.3 String Copy
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void my_strcpy(char *dst, char *str) {
int i;
for (i = 0; str[i] != '\0'; i++) {
dst[i] = str[i]; // equivalent to *(dst+i) = *(str+i)
}
dst[i] = 0; // null-terminate
}
void my_strcpy2(char *dst, char *str) {
while (*str != 0) {
*dst = *str;
dst++;
str++;
}
*dst = 0;
}
void my_strcpy3(char *dst, char *str) {
while (*str != 0) {
*dst++ = *str++; // post-increment: *dst = *str; then increment
}
*dst = 0;
}
void my_strcpy4(char *dst, char *str) {
while ((*dst++ = *str++) != 0) {
// copy and check
}
}
void my_strcpy5(char *dst, char *str) {
while (*dst++ = *str++) {
// assignment is the condition; ends when null character copied
}
}
int main(void) {
char src[] = "asdfghjkl";
char dest[100];
my_strcpy5(dest, src);
printf("%s\n", dest);
system("pause");
return 0;
}
Improved version with safety checks:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int my_strcpy6(char *dst, const char *src) {
// Returns 0 on success, non-zero on failure
if (dst == NULL || src == NULL) {
return -1;
}
char *to = dst;
const char *from = src;
while (*to++ = *from++) {
// copy until null
}
printf("my_strcpy6: dst = %s\n", dst);
return 0;
}
int main(void) {
char src[] = "asdfghjkl";
char dest[100] = {0};
int ret = my_strcpy6(dest, src);
printf("%s\n", dest);
if (ret != 0) {
printf("my_strcpy6 err: %d\n", ret);
return ret;
}
system("pause");
return 0;
}
3.1.4 strstr with while and do-while Models
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int count_substr_do_while(const char *str) {
int count = 0;
const char *p = str;
do {
p = strstr(p, "abcd");
if (p != NULL) {
count++;
p += strlen("abcd");
} else {
break;
}
} while (*p != '\0');
return count;
}
int count_substr_while(const char *str) {
int count = 0;
const char *p = str;
while ((p = strstr(p, "abcd")) != NULL) {
p += strlen("abcd");
count++;
if (*p == '\0') break;
}
return count;
}
int my_count_strstr(const char *p, int *n) {
const char *tmp = p;
int i = 0;
while ((tmp = strstr(tmp, "abcd")) != NULL) {
tmp += strlen("abcd");
i++;
if (*tmp == 0) break;
}
*n = i;
return 0;
}
int main(void) {
const char *str = "11abcd456abcd7533abcd44abcd56";
int n = 0;
int ret = my_count_strstr(str, &n);
if (ret == 0) {
printf("%d\n", n);
}
system("pause");
return 0;
}
3.1.5 Two-End Trimming Model
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int trim_space(const char *input, char *output) {
if (input == NULL || output == NULL) return -1;
int begin = 0;
int end = strlen(input) - 1;
while (isspace(input[begin]) && input[begin] != 0) {
begin++;
}
while (isspace(input[end]) && end > 0) {
end--;
}
if (end < begin) return -2; // all whitespace
int n = end - begin + 1;
strncpy(output, input + begin, n);
output[n] = '\0';
return 0;
}
int main(void) {
const char *p = " abcdefg ";
int n;
// Just compute length of trimmed string
// encapsulated in function
char buf[100] = {0};
int ret = trim_space(p, buf);
if (ret == 0) {
printf("Trimmed: '%s'\n", buf);
}
system("pause");
return 0;
}
Extract even/odd characters:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int extract_even_odd(const char *src, char *even, char *odd) {
if (src == NULL || even == NULL || odd == NULL) return -1;
int len = strlen(src);
for (int i = 0; i < len; i++) {
if (i % 2 == 0) {
*even++ = src[i];
} else {
*odd++ = src[i];
}
}
*even = '\0';
*odd = '\0';
return 0;
}
int main(void) {
const char *str = "a1b2c3d4";
char buf1[50] = {0};
char buf2[50] = {0};
int ret = extract_even_odd(str, buf1, buf2);
if (ret == 0) {
printf("Even: %s\n", buf1);
printf("Odd: %s\n", buf2);
}
system("pause");
return 0;
}
Extract key-value pairs and trim spaces:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int trim_space(const char *in, char *out) {
if (in == NULL || out == NULL) return -1;
int begin = 0;
int end = strlen(in) - 1;
while (isspace(in[begin]) && in[begin] != 0) begin++;
while (isspace(in[end]) && end > 0) end--;
if (end < begin) return -2;
int n = end - begin + 1;
strncpy(out, in + begin, n);
out[n] = '\0';
return 0;
}
int get_key_value(const char *key_value_str, const char *key, char *value, int *value_len) {
if (key_value_str == NULL || key == NULL || value == NULL || value_len == NULL) return -1;
const char *p = strstr(key_value_str, key);
if (p == NULL) return -2;
p += strlen(key);
p = strstr(p, "=");
if (p == NULL) return -3;
p += strlen("=");
char trimmed[256];
int ret = trim_space(p, trimmed);
if (ret != 0) return ret;
strcpy(value, trimmed);
*value_len = strlen(value);
return 0;
}
int main(void) {
const char *line = "key4= value4";
const char *key = "key4";
char value[100] = {0};
int len = 0;
int ret = get_key_value(line, key, value, &len);
if (ret == 0) {
printf("Value: '%s', length: %d\n", value, len);
} else {
printf("Error: %d\n", ret);
}
system("pause");
return 0;
}

3.1.6 const Usage
// Look from left to right, skip the type, see which character is modified.
// If it's '*', the pointed memory cannot be changed.
// If it's the pointer variable, the pointer's value (where it points) cannot be changed.
const char *p = buf; // equivalently: char const *p1 = buf;
char * const p2 = buf; // equivalently: char * const p2 = buf;
const char * const p3 = buf; // both read-only
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct {
int a;
int b;
} MyStruct;
void fun(MyStruct *p) {
// pointer can change; pointed content can change
// p = NULL; // OK
// p->a = 10; // OK
}
void fun1(const MyStruct *p) {
// p = NULL; // OK
// p->a = 10; // error
}
void fun2(MyStruct * const p) {
// p = NULL; // error
// p->a = 10; // OK
}
void fun3(const MyStruct * const p) {
// read-only
// MyStruct tmp;
// tmp.a = p->a; // only reading
}
int main(void) {
system("pause");
return 0;
}
3.1.7 Pointers as Function Parameters (Pointer as Output)
Pass by value:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int get_mem(char *p) {
p = (char*)malloc(100);
if (p == NULL) return -1;
strcpy(p, "ajlgjdslga");
printf("%s\n", p);
return 0;
}
int main(void) {
char *p = NULL;
int ret = get_mem(p);
if (ret != 0) {
printf("get_mem err: %d\n", ret);
return ret;
}
printf("p = %s\n", p); // still NULL, memory leak
system("pause");
return 0;
}

Pass by address (double pointer as output):
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
int get_mem2(char **p, int size) {
if (p == NULL) return -1;
char *tmp = (char*)malloc(size);
if (tmp == NULL) return -1;
strcpy(tmp, "ajlgjdslga");
*p = tmp;
return 0;
}
int main(void) {
char *p = NULL;
int ret = get_mem2(&p, 100);
if (ret == 0) {
printf("p = %s\n", p);
free(p);
p = NULL;
}
system("pause");
return 0;
}

4. Double Pointers
4.1 Double Pointers as Input
For an array of pointers *p[], the formal parameter should be **p.
Input: caller allocates memory. Output: callee allocates memory.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void sort_strings(char **arr, int n) {
char *tmp;
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (strcmp(arr[i], arr[j]) > 0) {
tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
}
}
}
void print_strings(char **arr, int n) {
for (int i = 0; i < n; i++) {
printf("%s, ", arr[i]);
}
printf("\n");
}
int main(void) {
char *strings[] = {"111111111", "00000000", "bbbbbbbbb", "aaaaaaaaa"};
int n = sizeof(strings) / sizeof(strings[0]);
printf("Before sorting:\n");
print_strings(strings, n);
sort_strings(strings, n);
printf("After sorting:\n");
print_strings(strings, n);
system("pause");
return 0;
}
4.2 Two-Dimensional Arrays
4.2.1 Size of Two-Dimensional Arrays
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char arr[4][30] = {"22222", "11111", "aaaaa", "bbbbb"};
printf("sizeof(arr) = %d\n", sizeof(arr));
printf("sizeof(arr[0]) = %d\n", sizeof(arr[0]));
system("pause");
return 0;
}

4.2.2 Essence of Two-Dimensional Arrays

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void) {
char arr[4][30] = {"22222", "11111", "aaaaa", "bbbbb"};
printf("arr = %d\n", arr); // address of first row (same as first element)
printf("&arr = %d\n", &arr); // address of entire 2D array
printf("arr[0] = %d\n", arr[0]); // address of first element of first row
printf("&arr[0][0] = %d\n", &arr[0][0]); // same
for (int i = 0; i < 4; i++) {
printf("%s\n", arr[i]); // arr+i or *(arr+i) also work
}
system("pause");
return 0;
}

4.2.3 Double Pointer as Input (Second Model: 2D Array)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void sort_strings_2d(char arr[][30], int n) {
// If using **arr, pointer arithmetic would change (step of 4 bytes)
char tmp[30];
for (int i = 0; i < n - 1; i++) {
for (int j = i + 1; j < n; j++) {
if (strcmp(arr[i], arr[j]) > 0) {
strcpy(tmp, arr[i]);
strcpy(arr[i], arr[j]);
strcpy(arr[j], tmp);
}
}
}
}
void print_strings_2d(char arr[][30], int n) {
for (int i = 0; i < n; i++) {
printf("%s, ", arr[i]);
}
printf("\n");
}
int main(void) {
char arr[4][30] = {"22222", "11111", "aaaaa", "bbbbb"};
int n = sizeof(arr) / sizeof(arr[0]);
print_strings_2d(arr, n);
sort_strings_2d(arr, n);
print_strings_2d(arr, n);
system("pause");
return 0;
}
4.3 Pointer to Array (Array Pointer)

4.3.1 Two-Dimensional Array Stored as One-Dimensional
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void print_array(int *arr, int n) {
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
int main(void) {
int arr[][4] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12};
print_array((int*)arr, sizeof(arr) / sizeof(arr[0][0]));
system("pause");
return 0;
}

4.3.1.1 Array Pointer with 2D Arrays

5. Structures
5.1 Structure with Nested Pointer (Single Level)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char *name; // no memory allocated initially
int age;
} Teacher;
int main(void) {
// Method 1: struct variable on stack
Teacher t1;
t1.name = (char*)malloc(30);
strcpy(t1.name, "lily");
t1.age = 22;
printf("name=%s, age=%d\n", t1.name, t1.age);
free(t1.name);
t1.name = NULL;
// Method 2: struct pointer on heap
Teacher *p = (Teacher*)malloc(sizeof(Teacher));
p->name = (char*)malloc(30);
strcpy(p->name, "lily");
p->age = 22;
printf("name=%s, age=%d\n", p->name, p->age);
free(p->name);
free(p);
p = NULL;
// Method 3: array of structs on heap
Teacher *arr = (Teacher*)malloc(sizeof(Teacher) * 3);
for (int i = 0; i < 3; i++) {
arr[i].name = (char*)malloc(30);
sprintf(arr[i].name, "name%d%d%d", i, i, i);
arr[i].age = 20 + i;
}
for (int i = 0; i < 3; i++) {
printf("%s, %d\n", arr[i].name, arr[i].age);
}
for (int i = 0; i < 3; i++) {
free(arr[i].name);
arr[i].name = NULL;
}
free(arr);
arr = NULL;
system("pause");
return 0;
}

5.1.2 Structure as Function Parameter (Encapsulated)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char *name;
int age;
} Teacher;
void show_teachers(const Teacher *teachers, int n) {
for (int i = 0; i < n; i++) {
printf("%s, %d\n", teachers[i].name, teachers[i].age);
}
}
void free_teachers(Teacher *teachers, int n) {
for (int i = 0; i < n; i++) {
if (teachers[i].name) {
free(teachers[i].name);
teachers[i].name = NULL;
}
}
if (teachers) {
free(teachers);
teachers = NULL;
}
}
Teacher* create_teachers(int n) {
Teacher *teachers = (Teacher*)malloc(sizeof(Teacher) * n);
for (int i = 0; i < n; i++) {
teachers[i].name = (char*)malloc(30);
sprintf(teachers[i].name, "name%d%d%d", i, i, i);
teachers[i].age = 20 + i;
}
return teachers;
}
int create_teachers2(Teacher **teachers, int n) {
if (teachers == NULL) return -1;
Teacher *tmp = (Teacher*)malloc(sizeof(Teacher) * n);
if (!tmp) return -2;
for (int i = 0; i < n; i++) {
tmp[i].name = (char*)malloc(30);
sprintf(tmp[i].name, "name%d%d%d", i, i, i);
tmp[i].age = 20 + i;
}
*teachers = tmp;
return 0;
}
int main(void) {
Teacher *p = NULL;
int ret = create_teachers2(&p, 3);
if (ret == 0) {
show_teachers(p, 3);
free_teachers(p, 3);
p = NULL;
}
system("pause");
return 0;
}

5.2 Structure with Nested Double Pointer
Scenario: One mentor has multiple students.
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char **students; // 2D memory: each student is a string
} Mentor;
int main(void) {
// Allocate array of 3 pointers
char **names = (char**)malloc(3 * sizeof(char*));
for (int i = 0; i < 3; i++) {
names[i] = (char*)malloc(30);
strcpy(names[i], "lily");
}
for (int i = 0; i < 3; i++) {
printf("%s\n", names[i]);
}
for (int i = 0; i < 3; i++) {
free(names[i]);
names[i] = NULL;
}
free(names);
// Mentor with 3 students
Mentor m;
m.students = (char**)malloc(3 * sizeof(char*));
for (int i = 0; i < 3; i++) {
m.students[i] = (char*)malloc(30);
strcpy(m.students[i], "lily");
}
for (int i = 0; i < 3; i++) {
printf("%s\n", m.students[i]);
}
for (int i = 0; i < 3; i++) {
free(m.students[i]);
m.students[i] = NULL;
}
free(m.students);
// Mentor pointer on heap
Mentor *p = (Mentor*)malloc(sizeof(Mentor));
p->students = (char**)malloc(3 * sizeof(char*));
for (int i = 0; i < 3; i++) {
p->students[i] = (char*)malloc(30);
strcpy(p->students[i], "lily");
}
for (int i = 0; i < 3; i++) {
printf("%s\n", p->students[i]);
}
for (int i = 0; i < 3; i++) {
free(p->students[i]);
p->students[i] = NULL;
}
free(p->students);
free(p);
p = NULL;
// Array of 3 mentors, each with 3 students
Mentor *mentors = (Mentor*)malloc(sizeof(Mentor) * 3);
for (int i = 0; i < 3; i++) {
mentors[i].students = (char**)malloc(3 * sizeof(char*));
for (int j = 0; j < 3; j++) {
mentors[i].students[j] = (char*)malloc(30);
sprintf(mentors[i].students[j], "name_%d_%d", i, j);
}
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
printf("%s ", mentors[i].students[j]);
}
printf("\n");
}
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
free(mentors[i].students[j]);
mentors[i].students[j] = NULL;
}
free(mentors[i].students);
mentors[i].students = NULL;
}
free(mentors);
mentors = NULL;
system("pause");
return 0;
}

5.2.1 Strengthening: Encapsulated Functions
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char **students;
} Mentor;
int create_mentors(Mentor **mentors, int num_mentors, int num_students) {
if (mentors == NULL) return -1;
Mentor *tmp = (Mentor*)malloc(sizeof(Mentor) * num_mentors);
if (!tmp) return -2;
for (int i = 0; i < num_mentors; i++) {
tmp[i].students = (char**)malloc(sizeof(char*) * num_students);
for (int j = 0; j < num_students; j++) {
tmp[i].students[j] = (char*)malloc(30);
sprintf(tmp[i].students[j], "name_%d_%d", i, j);
}
}
*mentors = tmp;
return 0;
}
void show_mentors(const Mentor *mentors, int num_mentors, int num_students) {
for (int i = 0; i < num_mentors; i++) {
for (int j = 0; j < num_students; j++) {
printf("%s ", mentors[i].students[j]);
}
printf("\n");
}
}
void free_mentors(Mentor **mentors, int num_mentors, int num_students) {
if (mentors == NULL || *mentors == NULL) return;
Mentor *tmp = *mentors;
for (int i = 0; i < num_mentors; i++) {
for (int j = 0; j < num_students; j++) {
if (tmp[i].students[j]) {
free(tmp[i].students[j]);
tmp[i].students[j] = NULL;
}
}
if (tmp[i].students) {
free(tmp[i].students);
tmp[i].students = NULL;
}
}
free(tmp);
*mentors = NULL;
}
int main(void) {
Mentor *mentors = NULL;
int ret = create_mentors(&mentors, 3, 3);
if (ret == 0) {
show_mentors(mentors, 3, 3);
free_mentors(&mentors, 3, 3);
}
system("pause");
return 0;
}
5.3 Deep Copy vs Shallow Copy of Structures
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
typedef struct {
char *name;
int age;
} Person;
int main(void) {
Person p1;
p1.name = (char*)malloc(30);
strcpy(p1.name, "lily");
p1.age = 22;
Person p2;
// Shallow copy: p2 = p1; // both point to same memory; double free later
// Deep copy: allocate separate memory
p2.name = (char*)malloc(30);
strcpy(p2.name, p1.name);
p2.age = p1.age;
printf("p2: name=%s, age=%d\n", p2.name, p2.age);
if (p1.name) {
free(p1.name);
p1.name = NULL;
}
if (p2.name) {
free(p2.name);
p2.name = NULL;
}
system("pause");
return 0;
}
