C语言经典面试题深度解析

本文汇总了C语言面试中最常见的问题,包括指针、内存管理、字符串处理、算法实现等核心知识点。

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
#include <stdio.h>

void pointerVsArray() {
printf("=== 指针与数组的区别 ===\n");

char arr[] = "Hello";
char *ptr = "World";

printf("数组大小:%zu\n", sizeof(arr)); // 6 (包含'\0')
printf("指针大小:%zu\n", sizeof(ptr)); // 8 (64位系统)

// 数组名是常量,不能修改
// arr = ptr; // 编译错误

// 指针可以重新赋值
ptr = arr;
printf("指针重新赋值后:%s\n", ptr);

// 数组内容可以修改(如果不是字符串字面量)
arr[0] = 'h';
printf("修改数组后:%s\n", arr);

// 指向字符串字面量的指针内容不能修改
char *literal = "Test";
// literal[0] = 't'; // 运行时错误
}

题目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
#include <stdio.h>
#include <stdlib.h>

void multiLevelPointers() {
printf("\n=== 多级指针示例 ===\n");

int value = 42;
int *ptr1 = &value;
int **ptr2 = &ptr1;
int ***ptr3 = &ptr2;

printf("原始值:%d\n", value);
printf("通过一级指针:%d\n", *ptr1);
printf("通过二级指针:%d\n", **ptr2);
printf("通过三级指针:%d\n", ***ptr3);

// 修改值
***ptr3 = 100;
printf("修改后的值:%d\n", value);

// 地址分析
printf("\n地址分析:\n");
printf("value的地址:%p\n", (void*)&value);
printf("ptr1的值:%p\n", (void*)ptr1);
printf("ptr1的地址:%p\n", (void*)&ptr1);
printf("ptr2的值:%p\n", (void*)ptr2);
printf("ptr2的地址:%p\n", (void*)&ptr2);
printf("ptr3的值:%p\n", (void*)ptr3);
}

题目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
#include <stdio.h>

// 错误的交换函数
void wrongSwap(int a, int b) {
int temp = a;
a = b;
b = temp;
}

// 正确的交换函数
void correctSwap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}

// 使用异或的交换(无需临时变量)
void xorSwap(int *a, int *b) {
if (a != b) { // 防止同一地址的情况
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}

void swapFunctions() {
printf("\n=== 交换函数示例 ===\n");

int x = 10, y = 20;
printf("初始值:x = %d, y = %d\n", x, y);

wrongSwap(x, y);
printf("错误交换后:x = %d, y = %d\n", x, y);

correctSwap(&x, &y);
printf("正确交换后:x = %d, y = %d\n", x, y);

xorSwap(&x, &y);
printf("异或交换后:x = %d, y = %d\n", x, y);
}

2. 字符串处理面试题

题目4:字符串反转

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
#include <stdio.h>
#include <string.h>

// 原地字符串反转
void reverseString(char *str) {
if (str == NULL) return;

int len = strlen(str);
for (int i = 0; i < len / 2; i++) {
char temp = str[i];
str[i] = str[len - 1 - i];
str[len - 1 - i] = temp;
}
}

// 递归字符串反转
void reverseStringRecursive(char *str, int start, int end) {
if (start >= end) return;

char temp = str[start];
str[start] = str[end];
str[end] = temp;

reverseStringRecursive(str, start + 1, end - 1);
}

void stringReverseTest() {
printf("\n=== 字符串反转 ===\n");

char str1[] = "Hello World";
char str2[] = "Programming";

printf("原始字符串1:%s\n", str1);
reverseString(str1);
printf("反转后:%s\n", str1);

printf("\n原始字符串2:%s\n", str2);
reverseStringRecursive(str2, 0, strlen(str2) - 1);
printf("递归反转后:%s\n", str2);
}

题目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
#include <stdio.h>
#include <string.h>

// 简单的字符串匹配
char* simpleStrstr(const char *haystack, const char *needle) {
if (*needle == '\0') return (char*)haystack;

for (const char *h = haystack; *h != '\0'; h++) {
const char *h_temp = h;
const char *n_temp = needle;

while (*h_temp != '\0' && *n_temp != '\0' && *h_temp == *n_temp) {
h_temp++;
n_temp++;
}

if (*n_temp == '\0') {
return (char*)h;
}
}

return NULL;
}

// KMP算法实现
void computeLPS(const char *pattern, int *lps, int m) {
int len = 0;
lps[0] = 0;
int i = 1;

while (i < m) {
if (pattern[i] == pattern[len]) {
len++;
lps[i] = len;
i++;
} else {
if (len != 0) {
len = lps[len - 1];
} else {
lps[i] = 0;
i++;
}
}
}
}

char* kmpSearch(const char *text, const char *pattern) {
int n = strlen(text);
int m = strlen(pattern);

if (m == 0) return (char*)text;

int *lps = (int*)malloc(m * sizeof(int));
computeLPS(pattern, lps, m);

int i = 0; // text index
int j = 0; // pattern index

while (i < n) {
if (pattern[j] == text[i]) {
i++;
j++;
}

if (j == m) {
free(lps);
return (char*)(text + i - j);
} else if (i < n && pattern[j] != text[i]) {
if (j != 0) {
j = lps[j - 1];
} else {
i++;
}
}
}

free(lps);
return NULL;
}

void substringSearchTest() {
printf("\n=== 子串查找 ===\n");

const char *text = "ABABDABACDABABCABCABCABCABC";
const char *pattern = "ABABCABCABCABC";

char *result1 = simpleStrstr(text, pattern);
char *result2 = kmpSearch(text, pattern);

printf("文本:%s\n", text);
printf("模式:%s\n", pattern);

if (result1) {
printf("简单算法找到位置:%ld\n", result1 - text);
} else {
printf("简单算法未找到\n");
}

if (result2) {
printf("KMP算法找到位置:%ld\n", result2 - text);
} else {
printf("KMP算法未找到\n");
}
}

3. 内存管理面试题

题目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
#include <stdio.h>
#include <stdlib.h>

// 模拟内存泄漏的错误代码
void memoryLeakExample() {
printf("\n=== 内存泄漏示例 ===\n");

// 错误1:分配后忘记释放
int *ptr1 = (int*)malloc(100 * sizeof(int));
if (ptr1 != NULL) {
// 使用内存
for (int i = 0; i < 100; i++) {
ptr1[i] = i;
}
// 忘记调用 free(ptr1); - 内存泄漏!
}

// 错误2:条件分支中的泄漏
char *buffer = (char*)malloc(256);
if (buffer != NULL) {
if (/* 某个条件 */ 1) {
printf("条件满足,提前返回\n");
// return; // 如果这里返回,buffer没有被释放
}
free(buffer); // 只有在条件不满足时才会执行
}
}

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

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

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

// 打印前几个值
printf("前5个值:");
for (int i = 0; i < 5; i++) {
printf("%d ", ptr[i]);
}
printf("\n");

// 正确释放内存
free(ptr);
ptr = NULL; // 防止悬空指针

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

题目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
#include <stdio.h>

// 危险的递归函数(可能导致栈溢出)
int dangerousRecursion(int n) {
if (n <= 0) return 0;
return n + dangerousRecursion(n - 1);
}

// 安全的递归函数(带深度限制)
int safeRecursion(int n, int depth, int max_depth) {
if (depth > max_depth) {
printf("警告:递归深度超过限制 %d\n", max_depth);
return -1;
}

if (n <= 0) return 0;
return n + safeRecursion(n - 1, depth + 1, max_depth);
}

// 迭代版本(避免栈溢出)
int iterativeSum(int n) {
int sum = 0;
for (int i = 1; i <= n; i++) {
sum += i;
}
return sum;
}

void stackOverflowTest() {
printf("\n=== 栈溢出测试 ===\n");

int n = 1000;

// 测试安全递归
int result1 = safeRecursion(n, 0, 500);
if (result1 != -1) {
printf("安全递归结果:%d\n", result1);
}

// 测试迭代版本
int result2 = iterativeSum(n);
printf("迭代版本结果:%d\n", result2);

// 数学公式验证
int expected = n * (n + 1) / 2;
printf("数学公式结果:%d\n", expected);
}

4. 算法实现面试题

题目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
#include <stdio.h>
#include <stdlib.h>

typedef struct ListNode {
int data;
struct ListNode *next;
} ListNode;

// 创建新节点
ListNode* createNode(int data) {
ListNode *node = (ListNode*)malloc(sizeof(ListNode));
if (node != NULL) {
node->data = data;
node->next = NULL;
}
return node;
}

// 打印链表
void printList(ListNode *head) {
while (head != NULL) {
printf("%d -> ", head->data);
head = head->next;
}
printf("NULL\n");
}

// 迭代方式反转链表
ListNode* reverseListIterative(ListNode *head) {
ListNode *prev = NULL;
ListNode *current = head;
ListNode *next = NULL;

while (current != NULL) {
next = current->next;
current->next = prev;
prev = current;
current = next;
}

return prev;
}

// 递归方式反转链表
ListNode* reverseListRecursive(ListNode *head) {
if (head == NULL || head->next == NULL) {
return head;
}

ListNode *newHead = reverseListRecursive(head->next);
head->next->next = head;
head->next = NULL;

return newHead;
}

// 释放链表内存
void freeList(ListNode *head) {
while (head != NULL) {
ListNode *temp = head;
head = head->next;
free(temp);
}
}

void linkedListReverseTest() {
printf("\n=== 链表反转 ===\n");

// 创建测试链表:1 -> 2 -> 3 -> 4 -> 5
ListNode *head = createNode(1);
head->next = createNode(2);
head->next->next = createNode(3);
head->next->next->next = createNode(4);
head->next->next->next->next = createNode(5);

printf("原始链表:");
printList(head);

// 迭代反转
head = reverseListIterative(head);
printf("迭代反转后:");
printList(head);

// 递归反转(恢复原状)
head = reverseListRecursive(head);
printf("递归反转后:");
printList(head);

freeList(head);
}

题目9:二分查找

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
#include <stdio.h>

// 迭代版本二分查找
int binarySearchIterative(int arr[], int n, int target) {
int left = 0, right = n - 1;

while (left <= right) {
int mid = left + (right - left) / 2;

if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return -1;
}

// 递归版本二分查找
int binarySearchRecursive(int arr[], int left, int right, int target) {
if (left > right) {
return -1;
}

int mid = left + (right - left) / 2;

if (arr[mid] == target) {
return mid;
} else if (arr[mid] < target) {
return binarySearchRecursive(arr, mid + 1, right, target);
} else {
return binarySearchRecursive(arr, left, mid - 1, target);
}
}

// 查找第一个出现的位置
int findFirst(int arr[], int n, int target) {
int left = 0, right = n - 1;
int result = -1;

while (left <= right) {
int mid = left + (right - left) / 2;

if (arr[mid] == target) {
result = mid;
right = mid - 1; // 继续在左半部分查找
} else if (arr[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}

return result;
}

void binarySearchTest() {
printf("\n=== 二分查找 ===\n");

int arr[] = {1, 2, 3, 4, 5, 5, 5, 6, 7, 8, 9};
int n = sizeof(arr) / sizeof(arr[0]);
int target = 5;

printf("数组:");
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");

int pos1 = binarySearchIterative(arr, n, target);
int pos2 = binarySearchRecursive(arr, 0, n - 1, target);
int first_pos = findFirst(arr, n, target);

printf("查找目标:%d\n", target);
printf("迭代查找位置:%d\n", pos1);
printf("递归查找位置:%d\n", pos2);
printf("第一次出现位置:%d\n", first_pos);
}

5. 位操作面试题

题目10:位操作技巧

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
#include <stdio.h>

// 检查数字是否为2的幂
int isPowerOfTwo(int n) {
return n > 0 && (n & (n - 1)) == 0;
}

// 计算二进制中1的个数
int countBits(unsigned int n) {
int count = 0;
while (n) {
count++;
n &= (n - 1); // 清除最低位的1
}
return count;
}

// 交换两个数(不使用临时变量)
void swapWithoutTemp(int *a, int *b) {
if (a != b) {
*a ^= *b;
*b ^= *a;
*a ^= *b;
}
}

// 找出数组中唯一出现一次的数字(其他都出现两次)
int findSingle(int arr[], int n) {
int result = 0;
for (int i = 0; i < n; i++) {
result ^= arr[i];
}
return result;
}

// 反转二进制位
unsigned int reverseBits(unsigned int n) {
unsigned int result = 0;
for (int i = 0; i < 32; i++) {
result = (result << 1) | (n & 1);
n >>= 1;
}
return result;
}

void bitOperationsTest() {
printf("\n=== 位操作技巧 ===\n");

// 测试2的幂
int numbers[] = {1, 2, 3, 4, 8, 15, 16, 32};
int count = sizeof(numbers) / sizeof(numbers[0]);

printf("2的幂测试:\n");
for (int i = 0; i < count; i++) {
printf("%d 是2的幂:%s\n", numbers[i],
isPowerOfTwo(numbers[i]) ? "是" : "否");
}

// 测试位计数
printf("\n位计数测试:\n");
for (int i = 0; i < count; i++) {
printf("%d 的二进制中有 %d 个1\n", numbers[i], countBits(numbers[i]));
}

// 测试交换
printf("\n交换测试:\n");
int x = 25, y = 30;
printf("交换前:x = %d, y = %d\n", x, y);
swapWithoutTemp(&x, &y);
printf("交换后:x = %d, y = %d\n", x, y);

// 测试找单独数字
printf("\n找单独数字:\n");
int arr[] = {1, 2, 3, 2, 1, 4, 4};
int single = findSingle(arr, 7);
printf("数组中唯一的数字:%d\n", single);

// 测试位反转
printf("\n位反转测试:\n");
unsigned int num = 0x12345678;
unsigned int reversed = reverseBits(num);
printf("原数字:0x%08X\n", num);
printf("反转后:0x%08X\n", reversed);
}

6. 主函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main() {
printf("C语言经典面试题解析\n");
printf("======================\n");

pointerVsArray();
multiLevelPointers();
swapFunctions();
stringReverseTest();
substringSearchTest();
correctMemoryManagement();
stackOverflowTest();
linkedListReverseTest();
binarySearchTest();
bitOperationsTest();

return 0;
}

总结

这些面试题涵盖了C语言的核心概念:

  1. 指针操作:理解指针与数组、多级指针、参数传递
  2. 字符串处理:反转、查找、匹配算法
  3. 内存管理:避免泄漏、栈溢出、正确的分配释放
  4. 算法实现:链表操作、查找算法、递归与迭代
  5. 位操作:高效的位运算技巧和应用

掌握这些知识点和解题思路,能够帮助你在C语言面试中脱颖而出。记住:理解原理比记住答案更重要。

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