C语言常见面试题:指针与内存管理

指针和内存管理是C语言的核心特性,也是技术面试中的重点考查内容。本文系统整理了相关的常见面试题,帮助求职者深入理解和掌握这些关键概念。

1. 指针基础

1.1 指针概念与操作

面试题1:什么是指针?指针和变量的区别是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
#include <stdio.h>
#include <stdlib.h>

// 指针基础演示
void demonstrate_pointer_basics(void) {
printf("=== 指针基础演示 ===\n");

// 变量和指针的声明
int value = 42; // 普通变量
int *ptr = &value; // 指针变量,存储value的地址
int **ptr_to_ptr = &ptr; // 指向指针的指针

printf("变量和指针信息:\n");
printf("value = %d, 地址 = %p\n", value, (void*)&value);
printf("ptr = %p, 指向的值 = %d\n", (void*)ptr, *ptr);
printf("ptr的地址 = %p\n", (void*)&ptr);
printf("ptr_to_ptr = %p, 指向的指针 = %p, 最终值 = %d\n",
(void*)ptr_to_ptr, (void*)*ptr_to_ptr, **ptr_to_ptr);

// 指针运算
printf("\n指针运算:\n");
int arr[] = {10, 20, 30, 40, 50};
int *arr_ptr = arr; // 数组名就是指向首元素的指针

printf("数组元素访问:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d, *(arr_ptr + %d) = %d\n",
i, arr[i], i, *(arr_ptr + i));
}

// 指针自增
printf("\n指针自增演示:\n");
int *moving_ptr = arr;
for (int i = 0; i < 5; i++) {
printf("地址: %p, 值: %d\n", (void*)moving_ptr, *moving_ptr);
moving_ptr++; // 指针向前移动一个int的大小
}
}

// 指针类型和void指针
void demonstrate_pointer_types(void) {
printf("\n=== 指针类型演示 ===\n");

int i = 10;
float f = 3.14f;
char c = 'A';

// 不同类型的指针
int *int_ptr = &i;
float *float_ptr = &f;
char *char_ptr = &c;

printf("不同类型指针:\n");
printf("int_ptr: %p, 值: %d, 大小: %zu\n",
(void*)int_ptr, *int_ptr, sizeof(int_ptr));
printf("float_ptr: %p, 值: %.2f, 大小: %zu\n",
(void*)float_ptr, *float_ptr, sizeof(float_ptr));
printf("char_ptr: %p, 值: %c, 大小: %zu\n",
(void*)char_ptr, *char_ptr, sizeof(char_ptr));

// void指针(通用指针)
void *void_ptr;

printf("\nvoid指针演示:\n");
void_ptr = &i;
printf("指向int: %p, 值: %d\n", void_ptr, *(int*)void_ptr);

void_ptr = &f;
printf("指向float: %p, 值: %.2f\n", void_ptr, *(float*)void_ptr);

void_ptr = &c;
printf("指向char: %p, 值: %c\n", void_ptr, *(char*)void_ptr);
}

// 指针与数组
void demonstrate_pointer_array_relationship(void) {
printf("\n=== 指针与数组关系 ===\n");

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 等价于 int *ptr = &arr[0]

printf("数组和指针的等价性:\n");
printf("arr == &arr[0]: %s\n", (arr == &arr[0]) ? "true" : "false");
printf("arr == ptr: %s\n", (arr == ptr) ? "true" : "false");

// 不同的访问方式
printf("\n不同访问方式:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d, *(arr+%d) = %d, ptr[%d] = %d, *(ptr+%d) = %d\n",
i, arr[i], i, *(arr+i), i, ptr[i], i, *(ptr+i));
}

// 指针运算
printf("\n指针运算:\n");
int *p1 = &arr[1];
int *p2 = &arr[4];

printf("p2 - p1 = %ld (元素个数差)\n", p2 - p1);
printf("p2 > p1: %s\n", (p2 > p1) ? "true" : "false");

// 数组大小 vs 指针大小
printf("\n大小比较:\n");
printf("sizeof(arr) = %zu (整个数组)\n", sizeof(arr));
printf("sizeof(ptr) = %zu (指针本身)\n", sizeof(ptr));
}

面试题2:指针和数组的区别是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
// 指针和数组的深入比较
void demonstrate_pointer_vs_array(void) {
printf("=== 指针 vs 数组详细比较 ===\n");

// 1. 内存分配
int arr[5] = {1, 2, 3, 4, 5}; // 栈上分配连续内存
int *ptr = malloc(5 * sizeof(int)); // 堆上分配内存

// 初始化动态数组
for (int i = 0; i < 5; i++) {
ptr[i] = i + 1;
}

printf("内存分配:\n");
printf("数组arr地址: %p (栈上)\n", (void*)arr);
printf("指针ptr指向: %p (堆上)\n", (void*)ptr);
printf("指针ptr地址: %p (栈上)\n", (void*)&ptr);

// 2. 修改性
printf("\n修改性测试:\n");
// arr = ptr; // 错误:数组名不能重新赋值
ptr = arr; // 正确:指针可以重新赋值
printf("ptr重新指向arr后: %p\n", (void*)ptr);

// 3. 大小计算
int local_arr[10] = {0};
printf("\n大小计算:\n");
printf("sizeof(local_arr) = %zu\n", sizeof(local_arr));
printf("sizeof(ptr) = %zu\n", sizeof(ptr));

// 4. 作为函数参数
printf("\n作为函数参数时的区别:\n");
printf("数组作为参数时退化为指针\n");
printf("无法通过sizeof获取原始数组大小\n");

// 释放动态内存
free(ptr);
ptr = NULL; // 避免悬空指针
}

// 函数参数中的数组和指针
void print_array_info(int arr[], int size) {
printf("函数内sizeof(arr) = %zu (实际是指针大小)\n", sizeof(arr));
printf("需要额外传递size参数: %d\n", size);
}

void test_array_parameter(void) {
printf("\n=== 数组参数测试 ===\n");

int test_arr[8] = {1, 2, 3, 4, 5, 6, 7, 8};
printf("调用前sizeof(test_arr) = %zu\n", sizeof(test_arr));

print_array_info(test_arr, 8);
}

1.2 多级指针

面试题3:什么是多级指针?如何使用?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
// 多级指针演示
void demonstrate_multi_level_pointers(void) {
printf("=== 多级指针演示 ===\n");

int value = 100;
int *ptr1 = &value; // 一级指针
int **ptr2 = &ptr1; // 二级指针
int ***ptr3 = &ptr2; // 三级指针

printf("多级指针访问:\n");
printf("value = %d\n", value);
printf("*ptr1 = %d\n", *ptr1);
printf("**ptr2 = %d\n", **ptr2);
printf("***ptr3 = %d\n", ***ptr3);

printf("\n地址信息:\n");
printf("&value = %p\n", (void*)&value);
printf("ptr1 = %p, &ptr1 = %p\n", (void*)ptr1, (void*)&ptr1);
printf("ptr2 = %p, &ptr2 = %p\n", (void*)ptr2, (void*)&ptr2);
printf("ptr3 = %p, &ptr3 = %p\n", (void*)ptr3, (void*)&ptr3);

// 通过多级指针修改值
printf("\n通过多级指针修改值:\n");
***ptr3 = 200;
printf("修改后 value = %d\n", value);
}

// 指针数组 vs 数组指针
void demonstrate_pointer_array_vs_array_pointer(void) {
printf("\n=== 指针数组 vs 数组指针 ===\n");

// 指针数组:数组的每个元素都是指针
int a = 10, b = 20, c = 30;
int *ptr_array[3] = {&a, &b, &c}; // 指针数组

printf("指针数组:\n");
for (int i = 0; i < 3; i++) {
printf("ptr_array[%d] = %p, 值 = %d\n",
i, (void*)ptr_array[i], *ptr_array[i]);
}

// 数组指针:指向数组的指针
int arr[3] = {40, 50, 60};
int (*array_ptr)[3] = &arr; // 数组指针

printf("\n数组指针:\n");
printf("array_ptr = %p\n", (void*)array_ptr);
printf("*array_ptr = %p (等于arr)\n", (void*)*array_ptr);

for (int i = 0; i < 3; i++) {
printf("(*array_ptr)[%d] = %d\n", i, (*array_ptr)[i]);
}

// 二维数组和指针
printf("\n二维数组和指针:\n");
int matrix[2][3] = {{1, 2, 3}, {4, 5, 6}};
int (*row_ptr)[3] = matrix; // 指向行的指针

for (int i = 0; i < 2; i++) {
for (int j = 0; j < 3; j++) {
printf("matrix[%d][%d] = %d, (*(row_ptr+%d))[%d] = %d\n",
i, j, matrix[i][j], i, j, (*(row_ptr+i))[j]);
}
}
}

// 字符串指针数组
void demonstrate_string_pointer_array(void) {
printf("\n=== 字符串指针数组 ===\n");

// 字符串指针数组
char *languages[] = {
"C",
"C++",
"Python",
"Java",
"JavaScript"
};

int count = sizeof(languages) / sizeof(languages[0]);

printf("编程语言列表:\n");
for (int i = 0; i < count; i++) {
printf("%d. %s (地址: %p)\n",
i+1, languages[i], (void*)languages[i]);
}

// 字符数组 vs 字符指针
printf("\n字符数组 vs 字符指针:\n");
char str_array[] = "Hello"; // 字符数组,可修改
char *str_ptr = "World"; // 字符指针,指向字符串常量

printf("str_array = %s, 地址 = %p\n", str_array, (void*)str_array);
printf("str_ptr = %s, 地址 = %p\n", str_ptr, (void*)str_ptr);

// 修改测试
str_array[0] = 'h'; // 可以修改
printf("修改后 str_array = %s\n", str_array);

// str_ptr[0] = 'w'; // 错误:不能修改字符串常量
str_ptr = "Universe"; // 可以重新指向
printf("重新指向后 str_ptr = %s\n", str_ptr);
}

2. 动态内存分配

2.1 malloc、calloc、realloc、free

面试题4:malloc、calloc、realloc的区别是什么?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
#include <string.h>

// 动态内存分配函数比较
void demonstrate_memory_allocation_functions(void) {
printf("=== 动态内存分配函数比较 ===\n");

// 1. malloc - 分配未初始化内存
printf("1. malloc演示:\n");
int *malloc_ptr = (int*)malloc(5 * sizeof(int));
if (malloc_ptr == NULL) {
printf("malloc分配失败\n");
return;
}

printf("malloc分配的内存内容(未初始化):");
for (int i = 0; i < 5; i++) {
printf("%d ", malloc_ptr[i]); // 可能是随机值
}
printf("\n");

// 手动初始化
for (int i = 0; i < 5; i++) {
malloc_ptr[i] = i + 1;
}
printf("手动初始化后:");
for (int i = 0; i < 5; i++) {
printf("%d ", malloc_ptr[i]);
}
printf("\n");

// 2. calloc - 分配并初始化为0
printf("\n2. calloc演示:\n");
int *calloc_ptr = (int*)calloc(5, sizeof(int));
if (calloc_ptr == NULL) {
printf("calloc分配失败\n");
free(malloc_ptr);
return;
}

printf("calloc分配的内存内容(自动初始化为0):");
for (int i = 0; i < 5; i++) {
printf("%d ", calloc_ptr[i]);
}
printf("\n");

// 3. realloc - 重新分配内存大小
printf("\n3. realloc演示:\n");
printf("原始malloc_ptr大小:5个int\n");

// 扩大内存
malloc_ptr = (int*)realloc(malloc_ptr, 10 * sizeof(int));
if (malloc_ptr == NULL) {
printf("realloc扩大失败\n");
free(calloc_ptr);
return;
}

printf("realloc扩大后,原有数据:");
for (int i = 0; i < 5; i++) {
printf("%d ", malloc_ptr[i]);
}
printf("\n");

// 初始化新增部分
for (int i = 5; i < 10; i++) {
malloc_ptr[i] = i + 1;
}
printf("完整数据:");
for (int i = 0; i < 10; i++) {
printf("%d ", malloc_ptr[i]);
}
printf("\n");

// 缩小内存
malloc_ptr = (int*)realloc(malloc_ptr, 3 * sizeof(int));
if (malloc_ptr == NULL) {
printf("realloc缩小失败\n");
free(calloc_ptr);
return;
}

printf("realloc缩小后:");
for (int i = 0; i < 3; i++) {
printf("%d ", malloc_ptr[i]);
}
printf("\n");

// 4. free - 释放内存
printf("\n4. 释放内存\n");
free(malloc_ptr);
free(calloc_ptr);
malloc_ptr = NULL; // 避免悬空指针
calloc_ptr = NULL;
printf("内存已释放\n");
}

// 内存分配错误处理
void demonstrate_memory_allocation_errors(void) {
printf("\n=== 内存分配错误处理 ===\n");

// 1. 检查分配是否成功
size_t huge_size = SIZE_MAX; // 尝试分配极大内存
void *ptr = malloc(huge_size);

if (ptr == NULL) {
printf("大内存分配失败(预期行为)\n");
perror("malloc");
} else {
printf("意外:大内存分配成功\n");
free(ptr);
}

// 2. 正常大小分配
int *normal_ptr = (int*)malloc(100 * sizeof(int));
if (normal_ptr == NULL) {
printf("正常内存分配失败\n");
return;
}

printf("正常内存分配成功\n");

// 3. realloc特殊情况
printf("\nrealloc特殊情况:\n");

// realloc(NULL, size) 等价于 malloc(size)
int *realloc_ptr = (int*)realloc(NULL, 5 * sizeof(int));
if (realloc_ptr != NULL) {
printf("realloc(NULL, size) 成功,等价于malloc\n");

// realloc(ptr, 0) 等价于 free(ptr)
realloc_ptr = (int*)realloc(realloc_ptr, 0);
printf("realloc(ptr, 0) 执行,等价于free\n");
// 注意:realloc_ptr现在可能是NULL
}

free(normal_ptr);
normal_ptr = NULL;
}

// 动态二维数组分配
void demonstrate_dynamic_2d_array(void) {
printf("\n=== 动态二维数组分配 ===\n");

int rows = 3, cols = 4;

// 方法1:分配指针数组,然后为每行分配内存
printf("方法1:指针数组方式\n");
int **matrix1 = (int**)malloc(rows * sizeof(int*));
if (matrix1 == NULL) {
printf("分配行指针失败\n");
return;
}

for (int i = 0; i < rows; i++) {
matrix1[i] = (int*)malloc(cols * sizeof(int));
if (matrix1[i] == NULL) {
printf("分配第%d行失败\n", i);
// 释放已分配的内存
for (int j = 0; j < i; j++) {
free(matrix1[j]);
}
free(matrix1);
return;
}
}

// 初始化和使用
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
matrix1[i][j] = i * cols + j + 1;
}
}

printf("matrix1内容:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%3d ", matrix1[i][j]);
}
printf("\n");
}

// 方法2:分配连续内存块
printf("\n方法2:连续内存块方式\n");
int *matrix2 = (int*)malloc(rows * cols * sizeof(int));
if (matrix2 == NULL) {
printf("分配连续内存失败\n");
// 释放matrix1
for (int i = 0; i < rows; i++) {
free(matrix1[i]);
}
free(matrix1);
return;
}

// 使用宏或函数访问元素
#define MATRIX2(i, j) matrix2[(i) * cols + (j)]

for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
MATRIX2(i, j) = (i + 1) * (j + 1);
}
}

printf("matrix2内容:\n");
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
printf("%3d ", MATRIX2(i, j));
}
printf("\n");
}

// 释放内存
// 方法1需要逐行释放
for (int i = 0; i < rows; i++) {
free(matrix1[i]);
}
free(matrix1);

// 方法2只需一次释放
free(matrix2);

printf("\n内存释放完成\n");
}

2.2 内存泄漏与检测

面试题5:什么是内存泄漏?如何避免和检测?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
// 内存泄漏示例和避免方法

// 错误示例:内存泄漏
void memory_leak_example(void) {
printf("=== 内存泄漏示例 ===\n");

// 泄漏1:分配后忘记释放
int *leak1 = (int*)malloc(100 * sizeof(int));
if (leak1 != NULL) {
// 使用内存...
printf("分配了内存但忘记释放\n");
// 忘记调用 free(leak1);
}

// 泄漏2:重复分配同一指针
int *leak2 = (int*)malloc(50 * sizeof(int));
if (leak2 != NULL) {
printf("第一次分配成功\n");
leak2 = (int*)malloc(100 * sizeof(int)); // 丢失了第一次分配的地址
if (leak2 != NULL) {
printf("第二次分配成功,但第一次分配的内存泄漏了\n");
free(leak2); // 只能释放第二次分配的内存
}
}

// 泄漏3:条件分支中的泄漏
int *leak3 = (int*)malloc(75 * sizeof(int));
if (leak3 != NULL) {
int condition = 1;
if (condition) {
printf("条件为真,提前返回\n");
return; // 忘记释放leak3
}
free(leak3);
}
}

// 正确的内存管理
void proper_memory_management(void) {
printf("\n=== 正确的内存管理 ===\n");

int *ptr = NULL;

// 1. 检查分配是否成功
ptr = (int*)malloc(100 * sizeof(int));
if (ptr == NULL) {
printf("内存分配失败\n");
return;
}

printf("内存分配成功\n");

// 2. 使用内存
for (int i = 0; i < 100; i++) {
ptr[i] = i;
}

// 3. 及时释放内存
free(ptr);
ptr = NULL; // 避免悬空指针

printf("内存正确释放\n");
}

// RAII风格的内存管理(使用函数封装)
typedef struct {
int *data;
size_t size;
size_t capacity;
} IntArray;

IntArray* int_array_create(size_t initial_capacity) {
IntArray *arr = (IntArray*)malloc(sizeof(IntArray));
if (arr == NULL) {
return NULL;
}

arr->data = (int*)malloc(initial_capacity * sizeof(int));
if (arr->data == NULL) {
free(arr);
return NULL;
}

arr->size = 0;
arr->capacity = initial_capacity;
return arr;
}

void int_array_destroy(IntArray *arr) {
if (arr != NULL) {
free(arr->data);
free(arr);
}
}

int int_array_push(IntArray *arr, int value) {
if (arr == NULL) {
return 0;
}

if (arr->size >= arr->capacity) {
// 扩容
size_t new_capacity = arr->capacity * 2;
int *new_data = (int*)realloc(arr->data, new_capacity * sizeof(int));
if (new_data == NULL) {
return 0; // 扩容失败
}
arr->data = new_data;
arr->capacity = new_capacity;
}

arr->data[arr->size++] = value;
return 1;
}

void demonstrate_raii_style_management(void) {
printf("\n=== RAII风格内存管理 ===\n");

IntArray *arr = int_array_create(5);
if (arr == NULL) {
printf("数组创建失败\n");
return;
}

printf("数组创建成功,初始容量:%zu\n", arr->capacity);

// 添加元素
for (int i = 0; i < 10; i++) {
if (int_array_push(arr, i * i)) {
printf("添加元素 %d\n", i * i);
} else {
printf("添加元素失败\n");
break;
}
}

printf("最终大小:%zu,容量:%zu\n", arr->size, arr->capacity);

// 打印内容
printf("数组内容:");
for (size_t i = 0; i < arr->size; i++) {
printf("%d ", arr->data[i]);
}
printf("\n");

// 自动清理
int_array_destroy(arr);
arr = NULL;

printf("数组已销毁\n");
}

// 内存检测工具使用示例
void demonstrate_memory_debugging(void) {
printf("\n=== 内存调试技巧 ===\n");

// 1. 使用调试宏
#ifdef DEBUG_MEMORY
#define DEBUG_MALLOC(size) debug_malloc(size, __FILE__, __LINE__)
#define DEBUG_FREE(ptr) debug_free(ptr, __FILE__, __LINE__)
#else
#define DEBUG_MALLOC(size) malloc(size)
#define DEBUG_FREE(ptr) free(ptr)
#endif

printf("内存调试技巧:\n");
printf("1. 使用valgrind检测内存泄漏\n");
printf(" 命令:valgrind --leak-check=full ./program\n");
printf("2. 使用AddressSanitizer\n");
printf(" 编译:gcc -fsanitize=address -g program.c\n");
printf("3. 使用静态分析工具\n");
printf(" 如:cppcheck, clang-static-analyzer\n");
printf("4. 自定义内存跟踪\n");
printf(" 记录每次malloc/free的位置和大小\n");

// 简单的内存跟踪示例
static int malloc_count = 0;
static int free_count = 0;

// 模拟分配和释放
void *ptrs[5];
for (int i = 0; i < 5; i++) {
ptrs[i] = malloc(100);
if (ptrs[i] != NULL) {
malloc_count++;
printf("分配 #%d\n", malloc_count);
}
}

for (int i = 0; i < 5; i++) {
if (ptrs[i] != NULL) {
free(ptrs[i]);
free_count++;
printf("释放 #%d\n", free_count);
}
}

printf("分配次数:%d,释放次数:%d\n", malloc_count, free_count);
if (malloc_count == free_count) {
printf("内存管理平衡✓\n");
} else {
printf("可能存在内存泄漏!\n");
}
}

3. 指针常见陷阱

3.1 悬空指针和野指针

面试题6:什么是悬空指针和野指针?如何避免?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
// 悬空指针和野指针演示
void demonstrate_dangling_wild_pointers(void) {
printf("=== 悬空指针和野指针演示 ===\n");

// 1. 悬空指针(Dangling Pointer)
printf("1. 悬空指针示例:\n");

int *dangling_ptr;
{
int local_var = 42;
dangling_ptr = &local_var; // 指向局部变量
printf("局部变量值:%d\n", *dangling_ptr);
} // local_var超出作用域,内存被回收

// 此时dangling_ptr成为悬空指针
printf("局部变量超出作用域后,指针变为悬空指针\n");
// printf("悬空指针值:%d\n", *dangling_ptr); // 危险!未定义行为

// 动态内存的悬空指针
int *heap_ptr = (int*)malloc(sizeof(int));
if (heap_ptr != NULL) {
*heap_ptr = 100;
printf("堆内存值:%d\n", *heap_ptr);

free(heap_ptr); // 释放内存
// heap_ptr现在是悬空指针
printf("内存释放后,指针变为悬空指针\n");
// printf("悬空指针值:%d\n", *heap_ptr); // 危险!

heap_ptr = NULL; // 正确做法:置为NULL
printf("指针已置为NULL\n");
}

// 2. 野指针(Wild Pointer)
printf("\n2. 野指针示例:\n");

int *wild_ptr; // 未初始化的指针
printf("野指针地址:%p\n", (void*)wild_ptr);
// printf("野指针值:%d\n", *wild_ptr); // 危险!随机地址

// 正确做法:初始化指针
int *safe_ptr = NULL;
printf("安全指针(初始化为NULL):%p\n", (void*)safe_ptr);

// 使用前检查
if (safe_ptr != NULL) {
printf("指针有效,值:%d\n", *safe_ptr);
} else {
printf("指针为NULL,不能解引用\n");
}
}

// 指针安全使用模式
void demonstrate_safe_pointer_patterns(void) {
printf("\n=== 指针安全使用模式 ===\n");

// 模式1:初始化为NULL
int *ptr = NULL;

// 模式2:分配前检查
ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
printf("内存分配失败\n");
return;
}

// 模式3:使用前检查
if (ptr != NULL) {
*ptr = 42;
printf("安全赋值:%d\n", *ptr);
}

// 模式4:释放后置NULL
free(ptr);
ptr = NULL;

// 模式5:再次使用前检查
if (ptr != NULL) {
printf("指针有效\n");
} else {
printf("指针为NULL,安全\n");
}

// 模式6:函数参数检查
safe_function(NULL); // 测试NULL参数

int value = 100;
safe_function(&value); // 测试有效参数
}

// 安全的函数设计
void safe_function(int *param) {
// 参数检查
if (param == NULL) {
printf("safe_function: 参数为NULL\n");
return;
}

printf("safe_function: 参数值 = %d\n", *param);
*param *= 2;
printf("safe_function: 修改后 = %d\n", *param);
}

// 双重释放错误
void demonstrate_double_free_error(void) {
printf("\n=== 双重释放错误 ===\n");

int *ptr = (int*)malloc(sizeof(int));
if (ptr == NULL) {
printf("内存分配失败\n");
return;
}

*ptr = 42;
printf("分配并初始化:%d\n", *ptr);

// 第一次释放
free(ptr);
printf("第一次释放完成\n");

// 错误:双重释放
// free(ptr); // 危险!可能导致程序崩溃

// 正确做法:释放后置NULL
ptr = NULL;

// 安全:释放NULL指针是安全的
free(ptr); // 这是安全的,free(NULL)什么都不做
printf("释放NULL指针是安全的\n");
}

// 指针别名问题
void demonstrate_pointer_aliasing(void) {
printf("\n=== 指针别名问题 ===\n");

int value = 10;
int *ptr1 = &value;
int *ptr2 = ptr1; // ptr2是ptr1的别名

printf("初始值:%d\n", value);
printf("ptr1指向:%d\n", *ptr1);
printf("ptr2指向:%d\n", *ptr2);

// 通过ptr1修改
*ptr1 = 20;
printf("\n通过ptr1修改后:\n");
printf("value:%d\n", value);
printf("ptr1指向:%d\n", *ptr1);
printf("ptr2指向:%d\n", *ptr2);

// 释放其中一个指针指向的内存(如果是动态分配的)
int *heap_ptr1 = (int*)malloc(sizeof(int));
int *heap_ptr2 = heap_ptr1; // 别名

if (heap_ptr1 != NULL) {
*heap_ptr1 = 100;
printf("\n堆内存别名:\n");
printf("heap_ptr1:%d\n", *heap_ptr1);
printf("heap_ptr2:%d\n", *heap_ptr2);

free(heap_ptr1);
heap_ptr1 = NULL;

// 现在heap_ptr2是悬空指针
printf("heap_ptr1释放后,heap_ptr2成为悬空指针\n");
// printf("heap_ptr2:%d\n", *heap_ptr2); // 危险!

heap_ptr2 = NULL; // 也要置为NULL
}
}

3.2 指针运算陷阱

面试题7:指针运算有哪些注意事项?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// 指针运算陷阱演示
void demonstrate_pointer_arithmetic_pitfalls(void) {
printf("=== 指针运算陷阱 ===\n");

// 1. 不同类型指针的运算
printf("1. 不同类型指针运算:\n");

int arr_int[5] = {1, 2, 3, 4, 5};
char arr_char[5] = {'a', 'b', 'c', 'd', 'e'};

int *int_ptr = arr_int;
char *char_ptr = arr_char;

printf("int指针递增:\n");
for (int i = 0; i < 3; i++) {
printf("地址:%p,值:%d\n", (void*)int_ptr, *int_ptr);
int_ptr++; // 移动sizeof(int)字节
}

printf("\nchar指针递增:\n");
for (int i = 0; i < 3; i++) {
printf("地址:%p,值:%c\n", (void*)char_ptr, *char_ptr);
char_ptr++; // 移动sizeof(char)字节
}

// 2. 数组边界检查
printf("\n2. 数组边界问题:\n");

int small_arr[3] = {10, 20, 30};
int *ptr = small_arr;

printf("正常访问:\n");
for (int i = 0; i < 3; i++) {
printf("arr[%d] = %d\n", i, ptr[i]);
}

printf("\n越界访问(危险):\n");
// printf("arr[3] = %d\n", ptr[3]); // 越界!未定义行为
// printf("arr[-1] = %d\n", ptr[-1]); // 越界!未定义行为
printf("越界访问被注释掉以避免未定义行为\n");

// 3. 指针减法的陷阱
printf("\n3. 指针减法:\n");

int *start = &small_arr[0];
int *end = &small_arr[2];

ptrdiff_t diff = end - start;
printf("指针差值:%ld(元素个数)\n", diff);
printf("字节差值:%ld\n", diff * sizeof(int));

// 错误:不同数组的指针相减
int other_arr[3] = {40, 50, 60};
int *other_ptr = other_arr;

// ptrdiff_t wrong_diff = end - other_ptr; // 未定义行为!
printf("不同数组指针相减是未定义行为(已避免)\n");

// 4. void指针运算
printf("\n4. void指针运算:\n");

void *void_ptr = small_arr;
printf("void指针地址:%p\n", void_ptr);

// void_ptr++; // 错误:void指针不能进行算术运算
printf("void指针不能直接进行算术运算\n");

// 需要转换为具体类型
int *converted_ptr = (int*)void_ptr;
converted_ptr++;
printf("转换后可以运算:%p\n", (void*)converted_ptr);
}

// 指针比较陷阱
void demonstrate_pointer_comparison_pitfalls(void) {
printf("\n=== 指针比较陷阱 ===\n");

int arr[5] = {1, 2, 3, 4, 5};
int *ptr1 = &arr[1];
int *ptr2 = &arr[3];

// 1. 同一数组内的指针比较
printf("1. 同一数组内指针比较:\n");
printf("ptr1 < ptr2: %s\n", (ptr1 < ptr2) ? "true" : "false");
printf("ptr1 == ptr2: %s\n", (ptr1 == ptr2) ? "true" : "false");
printf("ptr2 - ptr1 = %ld\n", ptr2 - ptr1);

// 2. 不同数组的指针比较
printf("\n2. 不同数组指针比较:\n");
int other_arr[5] = {6, 7, 8, 9, 10};
int *other_ptr = &other_arr[2];

printf("不同数组指针比较结果未定义\n");
// 比较结果是未定义的,但通常基于地址
printf("ptr1地址:%p\n", (void*)ptr1);
printf("other_ptr地址:%p\n", (void*)other_ptr);

// 3. NULL指针比较
printf("\n3. NULL指针比较:\n");
int *null_ptr = NULL;

printf("ptr1 == NULL: %s\n", (ptr1 == NULL) ? "true" : "false");
printf("null_ptr == NULL: %s\n", (null_ptr == NULL) ? "true" : "false");
printf("ptr1 != NULL: %s\n", (ptr1 != NULL) ? "true" : "false");

// 4. 函数指针比较
printf("\n4. 函数指针比较:\n");

void (*func_ptr1)(void) = demonstrate_pointer_basics;
void (*func_ptr2)(void) = demonstrate_pointer_basics;
void (*func_ptr3)(void) = demonstrate_pointer_types;

printf("相同函数指针相等: %s\n",
(func_ptr1 == func_ptr2) ? "true" : "false");
printf("不同函数指针相等: %s\n",
(func_ptr1 == func_ptr3) ? "true" : "false");
}

// 指针转换陷阱
void demonstrate_pointer_casting_pitfalls(void) {
printf("\n=== 指针转换陷阱 ===\n");

// 1. 不同类型指针转换
printf("1. 类型转换:\n");

int value = 0x12345678;
int *int_ptr = &value;
char *char_ptr = (char*)int_ptr;

printf("原始int值:0x%X\n", value);
printf("通过char指针访问字节:\n");

for (int i = 0; i < sizeof(int); i++) {
printf("字节%d:0x%02X\n", i, (unsigned char)char_ptr[i]);
}

// 2. 对齐问题
printf("\n2. 对齐问题:\n");

char buffer[10] = {0};

// 可能导致对齐问题的转换
int *misaligned_ptr = (int*)(buffer + 1); // 可能未对齐

printf("buffer地址:%p\n", (void*)buffer);
printf("misaligned_ptr地址:%p\n", (void*)misaligned_ptr);
printf("地址对齐检查:%s\n",
((uintptr_t)misaligned_ptr % sizeof(int) == 0) ? "对齐" : "未对齐");

// 在某些架构上,访问未对齐的指针可能导致性能下降或崩溃
// *misaligned_ptr = 42; // 可能有问题

// 3. 函数指针转换
printf("\n3. 函数指针转换:\n");

void (*void_func)(void) = demonstrate_pointer_basics;
void *generic_ptr = (void*)void_func;
void (*restored_func)(void) = (void(*)(void))generic_ptr;

printf("原始函数指针:%p\n", (void*)void_func);
printf("通用指针:%p\n", generic_ptr);
printf("恢复的函数指针:%p\n", (void*)restored_func);

// 调用恢复的函数指针(在这个例子中是安全的)
if (restored_func == void_func) {
printf("函数指针转换成功\n");
}
}

4. 综合面试题

4.1 实战编程题

面试题8:实现一个简单的动态数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
// 动态数组实现
typedef struct {
int *data; // 数据指针
size_t size; // 当前元素个数
size_t capacity; // 容量
} DynamicArray;

// 创建动态数组
DynamicArray* dynamic_array_create(size_t initial_capacity) {
if (initial_capacity == 0) {
initial_capacity = 1;
}

DynamicArray *arr = (DynamicArray*)malloc(sizeof(DynamicArray));
if (arr == NULL) {
return NULL;
}

arr->data = (int*)malloc(initial_capacity * sizeof(int));
if (arr->data == NULL) {
free(arr);
return NULL;
}

arr->size = 0;
arr->capacity = initial_capacity;
return arr;
}

// 销毁动态数组
void dynamic_array_destroy(DynamicArray *arr) {
if (arr != NULL) {
free(arr->data);
free(arr);
}
}

// 扩容
static int dynamic_array_resize(DynamicArray *arr, size_t new_capacity) {
if (arr == NULL || new_capacity == 0) {
return 0;
}

int *new_data = (int*)realloc(arr->data, new_capacity * sizeof(int));
if (new_data == NULL) {
return 0; // 扩容失败
}

arr->data = new_data;
arr->capacity = new_capacity;
return 1;
}

// 添加元素
int dynamic_array_push_back(DynamicArray *arr, int value) {
if (arr == NULL) {
return 0;
}

// 检查是否需要扩容
if (arr->size >= arr->capacity) {
size_t new_capacity = arr->capacity * 2;
if (!dynamic_array_resize(arr, new_capacity)) {
return 0; // 扩容失败
}
}

arr->data[arr->size++] = value;
return 1;
}

// 删除最后一个元素
int dynamic_array_pop_back(DynamicArray *arr) {
if (arr == NULL || arr->size == 0) {
return 0;
}

arr->size--;

// 如果使用率过低,可以考虑缩容
if (arr->size > 0 && arr->size <= arr->capacity / 4) {
size_t new_capacity = arr->capacity / 2;
if (new_capacity > 0) {
dynamic_array_resize(arr, new_capacity);
}
}

return 1;
}

// 获取元素
int dynamic_array_get(DynamicArray *arr, size_t index, int *value) {
if (arr == NULL || index >= arr->size || value == NULL) {
return 0;
}

*value = arr->data[index];
return 1;
}

// 设置元素
int dynamic_array_set(DynamicArray *arr, size_t index, int value) {
if (arr == NULL || index >= arr->size) {
return 0;
}

arr->data[index] = value;
return 1;
}

// 插入元素
int dynamic_array_insert(DynamicArray *arr, size_t index, int value) {
if (arr == NULL || index > arr->size) {
return 0;
}

// 检查是否需要扩容
if (arr->size >= arr->capacity) {
size_t new_capacity = arr->capacity * 2;
if (!dynamic_array_resize(arr, new_capacity)) {
return 0;
}
}

// 移动元素
for (size_t i = arr->size; i > index; i--) {
arr->data[i] = arr->data[i - 1];
}

arr->data[index] = value;
arr->size++;
return 1;
}

// 删除元素
int dynamic_array_remove(DynamicArray *arr, size_t index) {
if (arr == NULL || index >= arr->size) {
return 0;
}

// 移动元素
for (size_t i = index; i < arr->size - 1; i++) {
arr->data[i] = arr->data[i + 1];
}

arr->size--;

// 考虑缩容
if (arr->size > 0 && arr->size <= arr->capacity / 4) {
size_t new_capacity = arr->capacity / 2;
if (new_capacity > 0) {
dynamic_array_resize(arr, new_capacity);
}
}

return 1;
}

// 打印数组信息
void dynamic_array_print(DynamicArray *arr) {
if (arr == NULL) {
printf("数组为NULL\n");
return;
}

printf("动态数组信息:\n");
printf("大小:%zu,容量:%zu\n", arr->size, arr->capacity);
printf("元素:[");

for (size_t i = 0; i < arr->size; i++) {
printf("%d", arr->data[i]);
if (i < arr->size - 1) {
printf(", ");
}
}
printf("]\n");
}

// 测试动态数组
void test_dynamic_array(void) {
printf("=== 动态数组测试 ===\n");

// 创建数组
DynamicArray *arr = dynamic_array_create(2);
if (arr == NULL) {
printf("数组创建失败\n");
return;
}

printf("创建成功,初始容量:%zu\n", arr->capacity);

// 添加元素
printf("\n添加元素:\n");
for (int i = 0; i < 8; i++) {
if (dynamic_array_push_back(arr, i * i)) {
printf("添加 %d\n", i * i);
}
}
dynamic_array_print(arr);

// 插入元素
printf("\n在索引2插入99:\n");
dynamic_array_insert(arr, 2, 99);
dynamic_array_print(arr);

// 删除元素
printf("\n删除索引2的元素:\n");
dynamic_array_remove(arr, 2);
dynamic_array_print(arr);

// 修改元素
printf("\n修改索引0为100:\n");
dynamic_array_set(arr, 0, 100);
dynamic_array_print(arr);

// 获取元素
int value;
if (dynamic_array_get(arr, 3, &value)) {
printf("\n索引3的元素:%d\n", value);
}

// 删除多个元素测试缩容
printf("\n删除多个元素测试缩容:\n");
for (int i = 0; i < 5; i++) {
dynamic_array_pop_back(arr);
dynamic_array_print(arr);
}

// 销毁数组
dynamic_array_destroy(arr);
printf("\n数组已销毁\n");
}

**面试题9:实现字符串操作函数**

```c
// 字符串长度(不使用strlen)
size_t my_strlen(const char *str) {
if (str == NULL) {
return 0;
}

size_t len = 0;
while (str[len] != '\0') {
len++;
}
return len;
}

// 字符串复制(不使用strcpy)
char* my_strcpy(char *dest, const char *src) {
if (dest == NULL || src == NULL) {
return NULL;
}

char *original_dest = dest;
while ((*dest++ = *src++) != '\0') {
// 复制字符直到遇到空字符
}
return original_dest;
}

// 字符串连接(不使用strcat)
char* my_strcat(char *dest, const char *src) {
if (dest == NULL || src == NULL) {
return NULL;
}

char *original_dest = dest;

// 找到dest的末尾
while (*dest != '\0') {
dest++;
}

// 复制src到dest末尾
while ((*dest++ = *src++) != '\0') {
// 复制字符
}

return original_dest;
}

// 字符串比较(不使用strcmp)
int my_strcmp(const char *str1, const char *str2) {
if (str1 == NULL && str2 == NULL) {
return 0;
}
if (str1 == NULL) {
return -1;
}
if (str2 == NULL) {
return 1;
}

while (*str1 && (*str1 == *str2)) {
str1++;
str2++;
}

return *(unsigned char*)str1 - *(unsigned char*)str2;
}

// 字符串查找(不使用strstr)
char* my_strstr(const char *haystack, const char *needle) {
if (haystack == NULL || needle == NULL) {
return NULL;
}

if (*needle == '\0') {
return (char*)haystack; // 空字符串匹配任何位置
}

while (*haystack) {
const char *h = haystack;
const char *n = needle;

// 比较子串
while (*h && *n && (*h == *n)) {
h++;
n++;
}

if (*n == '\0') {
return (char*)haystack; // 找到匹配
}

haystack++;
}

return NULL; // 未找到
}

// 字符串反转
void my_strrev(char *str) {
if (str == NULL) {
return;
}

size_t len = my_strlen(str);
if (len <= 1) {
return;
}

char *start = str;
char *end = str + len - 1;

while (start < end) {
// 交换字符
char temp = *start;
*start = *end;
*end = temp;

start++;
end--;
}
}

// 测试字符串函数
void test_string_functions(void) {
printf("\n=== 字符串函数测试 ===\n");

// 测试字符串长度
const char *test_str = "Hello, World!";
printf("字符串 \"%s\" 长度:%zu\n", test_str, my_strlen(test_str));

// 测试字符串复制
char buffer[50];
my_strcpy(buffer, test_str);
printf("复制结果:\"%s\"\n", buffer);

// 测试字符串连接
char concat_buffer[100] = "Hello";
my_strcat(concat_buffer, ", World!");
printf("连接结果:\"%s\"\n", concat_buffer);

// 测试字符串比较
const char *str1 = "apple";
const char *str2 = "banana";
const char *str3 = "apple";

printf("\n字符串比较:\n");
printf("\"%s\" vs \"%s\": %d\n", str1, str2, my_strcmp(str1, str2));
printf("\"%s\" vs \"%s\": %d\n", str1, str3, my_strcmp(str1, str3));
printf("\"%s\" vs \"%s\": %d\n", str2, str1, my_strcmp(str2, str1));

// 测试字符串查找
const char *haystack = "Hello, World! Welcome to C programming.";
const char *needle1 = "World";
const char *needle2 = "Python";

printf("\n字符串查找:\n");
char *result1 = my_strstr(haystack, needle1);
char *result2 = my_strstr(haystack, needle2);

printf("在 \"%s\" 中查找 \"%s\": %s\n",
haystack, needle1, result1 ? result1 : "未找到");
printf("在 \"%s\" 中查找 \"%s\": %s\n",
haystack, needle2, result2 ? result2 : "未找到");

// 测试字符串反转
char rev_buffer[] = "Hello";
printf("\n字符串反转:\n");
printf("原字符串:\"%s\"\n", rev_buffer);
my_strrev(rev_buffer);
printf("反转后:\"%s\"\n", rev_buffer);
}

**面试题10:实现简单的内存池**

```c
// 简单内存池实现
#define POOL_SIZE 1024
#define BLOCK_SIZE 32
#define MAX_BLOCKS (POOL_SIZE / BLOCK_SIZE)

typedef struct {
char pool[POOL_SIZE]; // 内存池
int free_blocks[MAX_BLOCKS]; // 空闲块索引
int free_count; // 空闲块数量
size_t block_size; // 块大小
size_t total_blocks; // 总块数
} MemoryPool;

// 初始化内存池
void memory_pool_init(MemoryPool *pool) {
if (pool == NULL) {
return;
}

pool->block_size = BLOCK_SIZE;
pool->total_blocks = MAX_BLOCKS;
pool->free_count = MAX_BLOCKS;

// 初始化空闲块列表
for (int i = 0; i < MAX_BLOCKS; i++) {
pool->free_blocks[i] = i;
}

printf("内存池初始化完成:%zu块,每块%zu字节\n",
pool->total_blocks, pool->block_size);
}

// 从内存池分配内存
void* memory_pool_alloc(MemoryPool *pool) {
if (pool == NULL || pool->free_count == 0) {
return NULL; // 无可用块
}

// 获取一个空闲块
int block_index = pool->free_blocks[--pool->free_count];
void *ptr = pool->pool + (block_index * pool->block_size);

printf("分配块 %d,地址:%p\n", block_index, ptr);
return ptr;
}

// 释放内存到内存池
void memory_pool_free(MemoryPool *pool, void *ptr) {
if (pool == NULL || ptr == NULL) {
return;
}

// 检查指针是否在池范围内
if (ptr < (void*)pool->pool ||
ptr >= (void*)(pool->pool + POOL_SIZE)) {
printf("错误:指针不在内存池范围内\n");
return;
}

// 计算块索引
size_t offset = (char*)ptr - pool->pool;
if (offset % pool->block_size != 0) {
printf("错误:指针未对齐到块边界\n");
return;
}

int block_index = offset / pool->block_size;

// 检查是否已经在空闲列表中
for (int i = 0; i < pool->free_count; i++) {
if (pool->free_blocks[i] == block_index) {
printf("警告:重复释放块 %d\n", block_index);
return;
}
}

// 添加到空闲列表
if (pool->free_count < MAX_BLOCKS) {
pool->free_blocks[pool->free_count++] = block_index;
printf("释放块 %d\n", block_index);
}
}

// 获取内存池状态
void memory_pool_status(MemoryPool *pool) {
if (pool == NULL) {
return;
}

printf("\n内存池状态:\n");
printf("总块数:%zu\n", pool->total_blocks);
printf("空闲块数:%d\n", pool->free_count);
printf("已用块数:%zu\n", pool->total_blocks - pool->free_count);
printf("使用率:%.1f%%\n",
(double)(pool->total_blocks - pool->free_count) /
pool->total_blocks * 100);
}

// 测试内存池
void test_memory_pool(void) {
printf("\n=== 内存池测试 ===\n");

MemoryPool pool;
memory_pool_init(&pool);
memory_pool_status(&pool);

// 分配一些内存块
printf("\n分配内存块:\n");
void *ptrs[10];
for (int i = 0; i < 10; i++) {
ptrs[i] = memory_pool_alloc(&pool);
if (ptrs[i] == NULL) {
printf("分配失败\n");
break;
}
}

memory_pool_status(&pool);

// 释放一些内存块
printf("\n释放部分内存块:\n");
for (int i = 0; i < 5; i++) {
memory_pool_free(&pool, ptrs[i]);
ptrs[i] = NULL;
}

memory_pool_status(&pool);

// 再次分配
printf("\n再次分配:\n");
for (int i = 0; i < 3; i++) {
void *new_ptr = memory_pool_alloc(&pool);
if (new_ptr != NULL) {
printf("重新分配成功:%p\n", new_ptr);
}
}

memory_pool_status(&pool);

// 释放剩余内存
printf("\n释放剩余内存:\n");
for (int i = 5; i < 10; i++) {
if (ptrs[i] != NULL) {
memory_pool_free(&pool, ptrs[i]);
}
}

memory_pool_status(&pool);
}

总结

指针与内存管理是C语言面试中的重点考查内容,主要包括:

核心概念

  • 指针基础:指针的定义、声明、初始化和操作
  • 指针类型:不同类型指针的特点和使用场景
  • 指针运算:地址运算、指针算术、指针比较
  • 多级指针:二级指针、多级指针的应用

内存管理

  • 动态内存分配:malloc、calloc、realloc、free的使用
  • 内存泄漏:常见原因和预防方法
  • 野指针:产生原因和避免策略
  • 内存池:高效内存管理技术

实际应用

  • 数据结构实现:链表、树、图等动态数据结构
  • 字符串处理:字符串操作函数的实现
  • 系统编程:底层内存操作和优化
  • 嵌入式开发:受限环境下的内存管理

面试要点

  1. 理论基础:深入理解指针的本质和内存模型
  2. 代码实现:能够手写常见的指针操作和内存管理代码
  3. 问题分析:能够分析和解决内存相关的bug
  4. 最佳实践:掌握安全的内存管理技巧

学习建议

  • 多练习指针操作,加深对内存地址的理解
  • 学会使用调试工具(如valgrind)检测内存问题
  • 阅读优秀的开源代码,学习内存管理技巧
  • 实践中总结经验,形成良好的编程习惯

掌握这些知识点,能够帮助你在C语言面试中展现扎实的基础功底和实际编程能力。

完整测试程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 这里包含上面所有的函数实现...

int main(void) {
printf("=== C语言指针与内存管理面试题测试 ===\n\n");

// 测试基础指针操作
test_pointer_basics();

// 测试指针与数组
test_pointer_array_relationship();

// 测试多级指针
test_multi_level_pointers();

// 测试指针数组与数组指针
test_pointer_arrays();

// 测试字符串指针数组
test_string_pointer_arrays();

// 测试动态内存分配
test_dynamic_memory();

// 测试链表实现
test_linked_list();

// 测试动态数组
test_dynamic_array();

// 测试字符串函数
test_string_functions();

// 测试内存池
test_memory_pool();

printf("\n=== 所有测试完成 ===\n");
return 0;
}

版权所有,如有侵权请联系我