当前位置: 首页 > article >正文

一文带你彻底理清C 语言核心知识 与 面试高频考点:从栈溢出到指针 全面解析 附带笔者手写2.4k行代码加注释

引言:C 语言的魅力与挑战

从操作系统内核到嵌入式系统,从高性能计算到网络编程,C 语言高效、灵活和贴近硬件的特性,始终占据着不可替代的地位。然而,C 语言的强大也伴随着较高的学习曲线,尤其是指针、内存管理和复杂数据结构的操作  经常搞得人晕.....

哥们这次特地基于 700 余行实战代码,系统梳理 C 语言的核心知识点,从基础语法到高级应用,从内存模型到算法实现,帮助读者建立完整的 C 语言知识体系。

第一部分:笔者搜集的 csdn牛客里扣github博客园开源中国等头部技术论坛的大厂考点实战:

一、基础概念与内存模型

1. 指针与数组的本质区别

题目:以下关于指针与数组的说法错误的是( )
A. 数组名在多数情况下会衰退为指针
B. sizeof(arr)返回数组总字节数,sizeof(ptr)返回指针字节数
C. 指针可以进行算术运算,数组名不能
D. 数组元素存储在堆上,指针变量存储在栈上

解析
答案 D。数组元素存储位置取决于定义方式:局部数组在栈上,全局 / 静态数组在数据段,动态分配的数组在堆上。指针变量本身是变量,存储位置由定义位置决定(局部指针在栈,全局指针在数据段)。

关键点

  • 数组名衰退规则:除sizeof(arr)&arr外,数组名会衰退为指向首元素的指针
  • 指针算术运算本质是地址偏移,步长由指向类型决定

2. 指针大小与平台相关性

题目:在 64 位 Linux 系统中,以下指针类型的大小分别是( )

int *p1; 
void **p2; 
char (*p3)[10]; 
int (*p4)(int, int);

A. 8,8,8,8 B. 4,8,8,4 C. 8,16,8,8 D. 4,4,4,4

解析
答案 A。在 64 位系统中,所有指针类型(包括函数指针、多级指针、数组指针)的大小均为 8 字节,与指向类型无关。

关键点

  • 指针大小仅由操作系统位数决定:32 位 4 字节,64 位 8 字节
  • 函数指针、数组指针本质仍是指针,遵循相同大小规则

3. 野指针成因与危害

题目:以下代码会产生野指针的是( )

A. int *p; *p = 10;
B. int *p = (int*)malloc(sizeof(int)); free(p); p = NULL;
C. int arr[5], *p = arr; p += 5;
D. int *p = &(int){10};

解析
答案 A、C、D。

  • A:未初始化的指针直接解引用,是典型野指针
  • C:指针超出数组边界,指向无效内存
  • D:临时变量地址在表达式结束后失效,形成野指针

关键点
野指针常见成因:

  1. 未初始化的指针
  2. 释放后未置 NULL 的指针
  3. 越界访问的指针
  4. 指向临时变量的指针

二、指针与数组高级操作

4. 二维数组与指针运算

题目:对于二维数组int arr[3][4],以下表达式值为arr[1][2]的是( )
A. *(arr + 1 + 2)
B. *(arr [1] + 2)
C. ((arr + 2) + 1)
D. arr[1] + 2

解析
答案 B。

  • arr是指向int[4]的指针,arr[1]等价于*(arr+1),类型为int*
  • arr[1]+2是指向arr[1][2]的指针,解引用后得到值

关键点
二维数组在内存中按行存储,arr[i][j]等价于*(*(arr+i)+j)

5. 指针数组与数组指针辨析

题目:定义int (*p)[5]int *p[5],以下说法正确的是( )
A. 两者都是指针数组,存储 5 个 int * 指针
B. 前者是数组指针,后者是指针数组
C. 前者指针指向 5 个 int,后者数组存储 5 个指针
D. 两者没有区别

解析
答案 B。

  • int (*p)[5]:数组指针,指向包含 5 个 int 的数组
  • int *p[5]:指针数组,包含 5 个 int * 指针

关键点

  • 括号优先级:*p[5][]优先级高于*,先成数组
  • (int (*)[5])中括号改变优先级,先成指针

6. 字符串指针与数组的陷阱

题目:分析以下代码的输出:

void string_trap() {char str[] = "hello";char *ptr = "world";str[0] = 'H';ptr[0] = 'W';
}

A. 编译错误
B. 运行时错误(段错误)
C. 正常运行,str 变为 "Hello",ptr 变为 "World"
D. str 变为 "Hello",ptr 指向的字符串不变

解析
答案 B。

  • str是数组,存储在栈上,可以修改
  • ptr指向字符串常量,存储在代码段,修改会导致段错误

关键点

  • 字符串字面量存储在只读区,不能修改
  • 数组名作为左值时可修改元素

三、内存管理与指针操作

7. 动态内存分配与指针

题目:以下代码存在的问题是( )

c

运行

void memory_bug() {int *p = (int*)malloc(10 * sizeof(int));for (int i=0; i<10; i++) p[i] = i;int *q = (int*)realloc(p, 20 * sizeof(int));free(p);
}

A. 没有问题
B. realloc 后未检查返回值
C. free (p) 释放了已重新分配的内存
D. 内存泄漏

解析
答案 B、C。

  • realloc 可能失败,需检查返回值
  • realloc 成功时,p 的地址可能改变,原 p 被 q 覆盖后释放 q 才正确

关键点
realloc 使用规范:

void *new_ptr = realloc(old_ptr, new_size);
if (new_ptr) {old_ptr = new_ptr;
}

8. 指针与结构体对齐

题目:已知结构体:

struct Data {char c;int i;double d;
};

在 64 位系统中,sizeof(struct Data)的结果是( )
A. 13 B. 16 C. 24 D. 32

解析
答案 B。

  • 64 位系统默认对齐为 8 字节
  • char c占 1,补 3 到 4 字节
  • int i占 4,累计 8 字节
  • double d占 8,累计 16 字节

关键点
结构体对齐规则:

  1. 每个成员按自身大小和对齐参数取最小对齐
  2. 整体大小为最大对齐参数的整数倍

9. 多级指针操作

题目:执行以下代码后,**pp的值是( )

int a = 10, b = 20;
int *p = &a, **pp = &p;
p = &b;

A. 10 B. 20 C. 编译错误 D. 运行时错误

解析
答案 B。

  • pp是指向p的指针,p先指向a,后指向b
  • **pp等价于*p,即b的值

关键点
多级指针本质是指针的指针,解引用次数等于级别数

四、函数指针与回调

10. 函数指针数组实现计算器

题目:用函数指针数组实现四则运算计算器,要求支持+-*/,并处理除零错误。

解析

#include <stdio.h>
#include <stdlib.h>// 函数指针类型
typedef int (*OpFunc)(int, int);// 四则运算函数
int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }
int divide(int a, int b) {if (b == 0) {printf("错误:除数不能为零\n");exit(1);}return a / b;
}int main() {// 函数指针数组OpFunc ops[4] = {add, subtract, multiply, divide};char op;int a, b;printf("输入运算(+ - * /): ");scanf(" %c", &op);printf("输入两个操作数: ");scanf("%d %d", &a, &b);// 根据操作符选择函数int idx = -1;switch (op) {case '+': idx = 0; break;case '-': idx = 1; break;case '*': idx = 2; break;case '/': idx = 3; break;default: printf("不支持的操作符\n"); return 1;}printf("结果: %d\n", ops[idx](a, b));return 0;
}

关键点

  • 函数指针数组实现多态操作
  • 类型安全检查与错误处理

五、算法与数据结构中的指针应用

11. 链表逆序(指针操作)

题目:用指针操作实现单链表的逆序,要求时间复杂度 O (n),空间复杂度 O (1)。

解析

struct Node {int data;struct Node *next;
};struct Node* reverseList(struct Node* head) {struct Node *prev = NULL;struct Node *current = head;struct Node *next = NULL;while (current != NULL) {next = current->next;    // 保存下一个节点current->next = prev;   // 反转指针prev = current;          // 移动prevcurrent = next;          // 移动current}return prev;  // 新的头节点
}

关键点


三指针法:prevcurrentnext配合实现指针反转

12. 快速排序中的指针应用

题目:用指针操作实现快速排序,要求使用void*指针实现通用排序。

解析

#include <stdio.h>
#include <stdlib.h>// 交换函数
void swap(void *a, void *b, size_t size) {char temp[size];memcpy(temp, a, size);memcpy(a, b, size);memcpy(b, temp, size);
}// 分区函数
int partition(void *base, int low, int high, size_t size, int (*cmp)(const void*, const void*)) {void *pivot = (char*)base + high * size;int i = low - 1;for (int j = low; j < high; j++) {void *elem = (char*)base + j * size;if (cmp(elem, pivot) <= 0) {i++;swap((char*)base + i * size, elem, size);}}swap((char*)base + (i+1) * size, pivot, size);return i + 1;
}// 快速排序
void quickSort(void *base, int n, size_t size, int (*cmp)(const void*, const void*)) {if (n > 1) {int pi = partition(base, 0, n-1, size, cmp);quickSort(base, pi, size, cmp);quickSort((char*)base + (pi+1)*size, n - pi - 1, size, cmp);}
}// 测试
int intCmp(const void *a, const void *b) {return *(int*)a - *(int*)b;
}int main() {int arr[] = {3, 1, 4, 1, 5, 9, 2};int n = sizeof(arr)/sizeof(arr[0]);quickSort(arr, n, sizeof(int), intCmp);for (int i=0; i<n; i++) {printf("%d ", arr[i]);}return 0;
}

关键点

  • void*指针实现通用排序
  • 内存操作函数memcpy实现任意类型交换

六、综合应用与陷阱

13. 指针与数组传参陷阱

题目:以下函数中sizeof(arr)的结果是( )

void func(int arr[10]) {printf("%zu\n", sizeof(arr));
}int main() {int arr[5];func(arr);return 0;
}

A. 20 B. 8 C. 40 D. 编译错误

解析
答案 B。数组作为函数参数时衰退为指针,sizeof(arr)返回指针大小(64 位系统 8 字节)

关键点
数组传参本质是传递指针,无法在函数内获取原始数组大小

14. 指针运算与类型转换

题目:计算以下表达式的值(64 位系统):

char *p = "hello";
int *q = (int*)p;
q += 1;

q - p的值是( )
A. 1 B. 4 C. 8 D. 编译错误

解析
答案 B。int*指针 + 1 偏移 4 字节(int 大小),char*指针偏移 1 字节,差值为 4

关键点
指针算术运算的步长由指向类型决定

15. 内存泄漏检测

题目:找出以下代码中的内存泄漏:

void leak_demo() {int *p1 = (int*)malloc(10*sizeof(int));int *p2 = (int*)realloc(p1, 20*sizeof(int));if (p2 == NULL) return;free(p1);
}

解析
realloc成功时,p1指向的内存已被重新分配,原p1地址可能改变,直接free(p1)会释放新分配的内存,导致原内存泄漏。应改为:

int *p2 = realloc(p1, 20*sizeof(int));
if (p2) {p1 = p2; // 更新指针
}

七、编程题(大厂算法题)

16. 指针实现字符串拷贝(模拟 strcpy)

题目:用指针操作实现strcpy函数,要求处理边界情况。

解析

char* my_strcpy(char* dest, const char* src) {char* res = dest;if (dest == NULL || src == NULL) return NULL;while (*src) {*dest++ = *src++;}*dest = '\0';return res;
}

17. 指针实现链表环检测(Floyd 判圈算法)

题目:用指针操作实现链表环检测,要求时间复杂度 O (n),空间复杂度 O (1)。

解析

int hasCycle(struct Node *head) {if (head == NULL || head->next == NULL) return 0;struct Node *slow = head;struct Node *fast = head->next;while (slow != fast) {if (fast == NULL || fast->next == NULL) return 0;slow = slow->next;fast = fast->next->next;}return 1;
}

18. 指针与内存池设计

题目:设计一个简单内存池,要求:

  1. 预分配一块大内存
  2. 实现内存分配与释放
  3. 避免碎片

解析

#include <stdio.h>
#include <stdlib.h>
#include <string.h>#define POOL_SIZE 1024*1024  // 1MB内存池typedef struct {char* memory;          // 内存池起始地址char* current;         // 当前分配位置int size;              // 内存池大小
} MemoryPool;// 初始化内存池
MemoryPool* initMemoryPool(int size) {MemoryPool* pool = (MemoryPool*)malloc(sizeof(MemoryPool));if (pool == NULL) return NULL;pool->memory = (char*)malloc(size);if (pool->memory == NULL) {free(pool);return NULL;}pool->current = pool->memory;pool->size = size;return pool;
}// 从内存池分配内存
void* allocFromPool(MemoryPool* pool, int size) {if (pool == NULL || size <= 0) return NULL;if (pool->current + size > pool->memory + pool->size) {printf("内存池不足\n");return NULL;}void* res = pool->current;pool->current += size;return res;
}// 释放内存池
void freeMemoryPool(MemoryPool* pool) {if (pool == NULL) return;free(pool->memory);free(pool);
}

八、系统级指针操作

19. 指针与类型转换(内存复用)

题目:用指针操作实现 int 与 float 的内存互转,要求不使用联合。

解析

20. 指针与位操作(内存映射)

题目:用指针操作实现将整数的第 3 位和第 7 位取反。

解析

int flipBits(int num) {// 第3位和第7位掩码int mask = (1 << 3) | (1 << 7);return num ^ mask;
}// 指针版本
void flipBitsPtr(int *num) {int mask = (1 << 3) | (1 << 7);*num ^= mask;
}

面试题解析方法论

大厂指针题核心考点归纳:

  1. 指针本质:地址操作、类型系统、衰退规则
  2. 内存模型:栈堆数据段、对齐规则、生命周期
  3. 算法应用:链表 / 数组操作、排序 / 搜索中的指针技巧
  4. 系统编程:内存管理、函数指针、类型转换
  5. 安全问题:野指针、内存泄漏、越界访问

解题思路:

  1. 画图分析:指针操作时画出内存布局
  2. 类型推导:从定义推导指针类型(如int (*)[5]是数组指针)
  3. 边界测试:空指针、越界、类型转换等边界情况
  4. 内存跟踪:动态分配时跟踪指针变化

这些题目覆盖了腾讯、阿里等大厂面试中指针相关的核心考点,从基础概念到系统级编程,结合算法与数据结构,适合进阶学习和面试准备。建议在理解原理的基础上,动手实现并调试代码,加深对指针本质的理解。

第二部分:相关知识点详解

一、C 语言基础数据结构与内存模型

1.1 结构体:数据组织的基石

结构体是 C 语言中组织复杂数据的核心机制,它允许我们将不同类型的数据组合成一个有机整体。在实际项目中,结构体的设计直接影响程序的效率和可维护性。

// 多用途节点结构体,适用于链表和树结构
struct Node {int value;              // 节点存储的值struct Node *next;      // 链表中的下一个节点struct Node **children; // 树结构中的子节点数组int child_count;        // 子节点数量
};// 人员信息结构体
struct Person {char name[10];          // 姓名int age;                // 年龄
};// 栈数据结构实现
struct stack {int data[100];          // 栈存储数组int top;                // 栈顶指针
};

上述代码定义了三种常用结构体:多用途节点结构体Node、人员信息结构体Person和栈结构体stack。其中Node结构体的设计体现了 C 语言的灵活性 —— 通过next指针可以构建链表,通过children指针数组可以构建树结构,这种 "一结构多用途" 的设计在实际开发中非常常见。

结构体使用实战:栈的实现与应用

栈是计算机科学中最基础的数据结构之一,下面我们通过stack结构体实现一个完整的栈,并演示其基本操作:

#define maxL 99 // 栈的最大容量// 初始化栈
void initStack(struct stack *s) {s->top = -1;
}// 判断栈是否为空
int isEmpty(struct stack *s) {return s->top == -1 ? 1 : -100;
}// 判断栈是否已满
int isFull(struct stack *s) {return s->top == maxL ? 1 : 0;
}// 入栈操作
void pushStack(struct stack *s, int value) {if (!isFull(s)) {s->data[++(s->top)] = value;}
}// 出栈操作
int popStack(struct stack *s) {if (!isEmpty(s)) {return s->data[(s->top)--];}return -1;
}// 获取栈顶元素
int peekTop(struct stack *s) {return s->data[s->top];
}// 栈操作测试
void stackTest() {struct stack s;initStack(&s);printf("初始化后,栈是否为空: %d\n", isEmpty(&s));pushStack(&s, 1);printf("压入 1 后,栈是否为空: %d\n", isEmpty(&s));pushStack(&s, 2);pushStack(&s, 3);printf("栈顶元素: %d\n", peekTop(&s));pushStack(&s, 4);printf("栈顶元素: %d\n", peekTop(&s));popStack(&s);printf("弹出元素后,栈顶元素: %d\n", peekTop(&s));
}

这段代码完整实现了栈的基本操作:初始化、判空、判满、入栈、出栈和获取栈顶元素。在stackTest函数中,我们演示了栈的基本使用流程。需要注意的是,这里使用数组实现栈,属于顺序栈,其优点是访问效率高,缺点是容量固定。在实际项目中,当需要动态调整栈大小时,可以考虑使用链表实现栈结构。

1.2 指针:C 语言的灵魂

指针是 C 语言的核心特性,也是让许多初学者望而生畏的难点。理解指针,本质上是理解计算机内存的工作原理。

// 野指针成因演示
void wild_pointer_cause() {int a = 10;int *new_ptr = &a;  // 合法指针,指向变量aint *heap_ptr = (int *)malloc(sizeof(int)); // 在堆上分配内存free(heap_ptr);  // 释放内存,但指针未置为NULL// heap_ptr现在成为野指针int arr[5] = {1, 2, 3, 4, 5};int *arr_ptr = arr;arr_ptr = arr_ptr + 10;  // 指针越界,成为野指针
}// 指针关系运算演示
void pointer_relation() {int arr[5] = {1, 2, 3, 4, 5};int *p1 = arr + 1;  // 指向arr[1]int *p2 = arr + 3;  // 指向arr[3]printf("p1 < p2: %s\n", (p1 < p2) ? "true" : "false");  // 指针地址比较printf("p1 == arr+1: %s\n", (p1 == arr + 1) ? "true" : "false");  // 指针相等比较
}// 指针算术运算演示
void pointer_arithmetic_application() {int arr[5] = {1, 2, 3, 4, 5};int *p = arr;for (int i = 0; i < 5; i++) {printf("arr[%d] = %d\n", i, *(p + i));  // 通过指针算术访问数组元素}
}
指针与数组的爱恨情仇

在 C 语言中,指针与数组的关系极为密切,但又有着本质区别。许多初学者容易混淆两者,导致程序出现难以试的错误。

// 数组与指针区别演示
void array_pointer_question() {int arr[5] = {1, 2, 3, 4, 5};printf("sizeof(arr) = %zu\n", sizeof(arr));       // 输出数组总字节数:5*4=20printf("sizeof(arr+0) = %zu\n", sizeof(arr + 0)); // 输出指针字节数:8(64位系统)
}// 数组指针应用:二维数组操作
void array_ptr_application() {int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};int (*p)[3] = arr;  // 定义指向包含3个int的数组的指针for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {// 两种等价的访问方式printf("1 the num: %d-%d:%d\n", i, j, *(p + i)[j]);printf("2 the num: %d-%d:%d\n", i, j, p[i][j]);}}
}

1.3 C 语言内存模型:从栈到堆的全景图

理解 C 语言的内存分布是写出高效、稳定程序的关键。C 程序的内存通常分为以下几个区域:

// 内存分布演示
void memory_distribution_demo() {// 栈区:存储局部变量int localVar = 10;char str[20] = "hello";// 堆区:动态分配的内存int *heapPtr = (int *)malloc(sizeof(int));if (heapPtr == NULL) {printf("内存分配失败\n");return;}*heapPtr = 20;// 数据段:存储全局变量和静态变量static int staticVar = 30;// 代码段:存储可执行代码和字符串常量const char *strConst = "world";// 释放堆内存free(heapPtr);
}

二、字符串处理:从基础函数到自定义实现

字符串是 C 语言中最常用的数据类型之一,熟练掌握字符串处理是 C 程序员的基本素养。标准库提供了丰富的字符串函数,但了解其实现原理能帮助我们更好地使用它们。

2.1 字符串处理函数的自定义实现

c

// 自定义strstr函数:查找子字符串
char *my_strstr(const char *haystack, const char *needle) {assert(haystack != NULL && needle != NULL);// 空字符串特殊处理if (*needle == '\0') {return NULL;}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;
}// 自定义strncpy函数:复制指定长度的字符串
char *my_strncpy(char *dest, const char *src, size_t n) {assert(dest != NULL && src != NULL);size_t i = 0;char *tempPtr = dest;// 复制src中的字符,直到n个或遇到'\0'while (i < n && src[i]) {dest[i] = src[i];i++;}// 填充剩余位置为'\0'while (i < n) {dest[i] = '\0';i++;}return tempPtr;
}// 自定义strncat函数:连接指定长度的字符串
char *my_strncat(char *dest, const char *src, size_t n) {assert(dest != NULL && src != NULL);size_t len = strlen(dest);char *res = dest;size_t i;// 连接src中的字符,直到n个或遇到'\0'for (i = 0; i < n && src[i] != '\0'; i++) {dest[len + i] = src[i];}dest[i + len] = '\0';return res;
}// 自定义strncmp函数:比较指定长度的字符串
int my_strncmp(const char *s1, const char *s2, size_t n) {assert(s1 != NULL && s2 != NULL);for (size_t i = 0; i < n; i++) {char c1 = s1[i] ? s1[i] : '\0';char c2 = s2[i] ? s2[i] : '\0';if (c1 != c2) {return c1 - c2;}}return 0;
}// 自定义strchr函数:查找字符
char *my_strchr(const char *str, int c) {assert(str != NULL);const char *p = str;while (*p) {if (*p == (char)c) {return (char *)p;}p++;}// 处理查找'\0'的情况return (char)c == '\0' ? (char *)p : NULL;
}

2.2 字符串处理实战:从复制到拼接

c

// 字符串复制函数
char *str_copy(const char *src) {// 分配内存,+1用于存储'\0'char *dest = (char *)malloc(strlen(src) + 1);assert(dest != NULL);char *p = dest;while (*src) {*p++ = *src++;}*p = '\0';return dest;
}// 字符串拼接演示
void char_ptr_application() {char str1[20] = "Hello";char *str2 = ", World!";char *p1 = str1 + strlen(str1);char *p2 = str2;// 手动拼接字符串while (*p2) {*p1++ = *p2++;}*p1 = '\0';printf("拼接后的字符串: %s\n", str1);
}// 字符串处理函数测试
void string_functions_test() {printf("字符串复制测试:\n");char *copy = str_copy("test");printf("复制后的字符串: %s\n", copy);free(copy);printf("\nstrncpy测试:\n");char dest[10] = {0};my_strncpy(dest, "hello", 3);printf("复制结果: %s\n", dest);printf("\nstrncat测试:\n");char dest_cat[10] = {'1', '2', '\0'};char src_cat[] = {'3', '4', 'a', '\0'};my_strncat(dest_cat, src_cat, 3);printf("拼接结果: %s\n", dest_cat);printf("\nstrncmp测试:\n");char s1[] = {'1', '2', '3', '5'};char s2[] = {'1', '2', '3'};printf("比较结果: %d\n", my_strncmp(s1, s2, 4));printf("\nstrchr测试:\n");char test[] = {'1', '2', 'a', 'b', 'c', 'f', 'l', '6', '\0'};char *res = my_strchr(test, 'a');if (res) {printf("找到字符 'a',位置: %ld\n", res - test);} else {printf("未找到字符\n");}printf("\nstrstr测试:\n");char haystack[] = {'1', 'a', '2', 'b', 'c', '\0'};char needle[] = {'b', 'c', '\0'};char *strstr_res = my_strstr(haystack, needle);if (strstr_res) {printf("找到子字符串,位置: %ld\n", strstr_res - haystack);} else {printf("未找到子字符串\n");}
}

三、算法与数据结构:从排序到递归

3.1 排序算法:快速排序的实现与优化

快速排序是实践中常用的高效排序算法,其平均时间复杂度为 O (n log n)。

c

// 交换函数
void swap(int *a, int *b) {int temp = *a;*a = *b;*b = temp;
}// 快速排序的分区函数
int partition(int arr[], int low, int high) {int pi = arr[high];  // 选择最后一个元素作为基准int i = low - 1;     // 小于基准的元素的边界for (int j = low; j <= high - 1; j++) {// 如果当前元素小于等于基准if (arr[j] <= pi) {i++;swap(&arr[i], &arr[j]);}}swap(&arr[i + 1], &arr[high]);return i + 1;
}// 快速排序主函数
void quick_Sort(int arr[], int low, int high) {if (low < high) {// 分区操作,返回基准的正确位置int pi = partition(arr, low, high);// 递归排序基准左侧和右侧quick_Sort(arr, low, pi - 1);quick_Sort(arr, pi + 1, high);}
}// 快速排序测试
void quick_sort_test() {int arr[] = {4, 5, 6, 7, 15, 234, 46, 698, 238, 258, 45, 2, 36, 26, 123, 77, 5, 48, 45, 2, 5, 325, 32, 1, 6};int len = sizeof(arr) / sizeof(arr[0]);printf("排序前数组:\n");for (int i = 0; i < len; i++) {printf("%d ", arr[i]);}printf("\n");quick_Sort(arr, 0, len - 1);printf("排序后数组:\n");for (int i = 0; i < len; i++) {printf("%d ", arr[i]);}printf("\n");
}

3.2 递归算法:斐波那契数列的实现

递归是一种强大的算法思想,常用于解决可以分解为相似子问题的场景。

c

// 斐波那契数列递归实现
int fibonacci(int n) {if (n <= 1) {return n;}return fibonacci(n - 1) + fibonacci(n - 2);
}// 斐波那契数列测试
void fibonacci_test() {printf("fib(5) = %d\n", fibonacci(5));   // 输出5printf("fib(10) = %d\n", fibonacci(10)); // 输出55
}// 递归优化思路:记忆化搜索
int fibonacci_memo(int n, int *memo) {if (n <= 1) {return n;}// 如果已经计算过,直接返回结果if (memo[n] != -1) {return memo[n];}// 计算并存储结果memo[n] = fibonacci_memo(n - 1, memo) + fibonacci_memo(n - 2, memo);return memo[n];
}// 记忆化搜索测试
void fibonacci_memo_test() {int n = 20;int *memo = (int *)calloc(n + 1, sizeof(int));for (int i = 0; i <= n; i++) {memo[i] = -1;}printf("fib_memo(20) = %d\n", fibonacci_memo(20, memo));free(memo);
}

四、高级主题:从函数指针到内存管理

4.1 函数指针:C 语言的回调机制

函数指针是 C 语言中实现回调机制的基础,也是许多高级特性的基石。

c

// 加法函数
int add(int a, int b) {return a + b;
}// 减法函数
int subtract(int a, int b) {return a - b;
}// 函数指针数组演示
void func_ptr_array_demo() {// 定义函数指针类型typedef int (*OpFunc)(int, int);// 创建函数指针数组OpFunc ops[] = {add, subtract};printf("5+3=%d\n", ops[0](5, 3));printf("5-3=%d\n", ops[1](5, 3));
}// 回调函数示例:排序比较函数
int compare_asc(const void *a, const void *b) {return *(int *)a - *(int *)b;
}// qsort函数使用演示
void qsort_demo() {int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};int n = sizeof(arr) / sizeof(arr[0]);printf("排序前:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");// 使用qsort排序,传入比较函数qsort(arr, n, sizeof(int), compare_asc);printf("排序后:\n");for (int i = 0; i < n; i++) {printf("%d ", arr[i]);}printf("\n");
}

4.2 内存管理:从 malloc 到野指针防护

c

// 动态内存分配演示
int *dy_alloc(int n) {int *res = (int *)malloc(n * sizeof(int));if (res == NULL) {printf("内存分配失败\n");exit(1);}// 初始化分配的内存for (int i = 0; i < n; i++) {res[i] = i;}return res;
}// 内存管理最佳实践
void memory_management_best_practices() {// 分配内存int *ptr1 = (int *)malloc(10 * sizeof(int));if (ptr1 == NULL) {printf("内存分配失败\n");return;}// 使用内存for (int i = 0; i < 10; i++) {ptr1[i] = i;}// 重新分配内存int *ptr2 = (int *)realloc(ptr1, 20 * sizeof(int));if (ptr2 == NULL) {free(ptr1);printf("内存重新分配失败\n");return;}ptr1 = ptr2; // 更新指针// 继续使用内存for (int i = 10; i < 20; i++) {ptr1[i] = i;}// 释放内存free(ptr1);ptr1 = NULL; // 置为NULL,避免野指针
}// 野指针防护策略
void wild_pointer_prevention() {int *ptr = NULL; // 初始化为NULL// 分配内存ptr = (int *)malloc(sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return;}// 使用内存*ptr = 10;// 释放内存free(ptr);ptr = NULL; // 释放后立即置为NULL// 安全检查if (ptr != NULL) {// 不会执行到这里}
}

五、综合实战:从链表到矩阵操作

5.1 链表操作:从创建到遍历

c

// 在链表头部插入节点
struct Node *insert_atFirst(struct Node *head, int data) {struct Node *n_node = (struct Node *)malloc(sizeof(struct Node));if (n_node == NULL) {printf("内存分配失败\n");return head;}n_node->value = data;n_node->next = head;n_node->children = NULL;n_node->child_count = 0;return n_node;
}// 打印链表
void print_List(struct Node *node) {struct Node *l = node;while (l != NULL) {printf("链表节点值: %d\n", l->value);l = l->next;}
}// 链表操作测试
void linked_list_test() {struct Node *head_node = NULL;head_node = insert_atFirst(head_node, 123);printf("第一个节点值: %d\n", head_node->value);head_node = insert_atFirst(head_node, 4);printf("第二个节点值: %d\n", head_node->value);print_List(head_node);
}

5.2 矩阵操作:转置与拼接

c

// 矩阵转置
void zhuanzhi(int arr[3][3]) {for (int i = 0; i < 3; i++) {for (int j = i + 1; j < 3; j++) {int temp = arr[i][j];arr[i][j] = arr[j][i];arr[j][i] = temp;}}
}// 矩阵转置测试
void matrix_transpose_test() {int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};printf("转置前矩阵:\n");for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {printf("%d ", arr[i][j]);}printf("\n");}zhuanzhi(arr);printf("转置后矩阵:\n");for (int i = 0; i < 3; i++) {for (int j = 0; j < 3; j++) {printf("%d ", arr[i][j]);}printf("\n");}
}// 字符串拼接
void connect_char(char *a, char *b) {char *p1 = a + strlen(a);while (*b) {*p1 = *b;p1++;b++;}*p1 = '\0';
}// 字符串拼接测试
void string_concatenation_test() {char char1[] = {'1', '2', '3', '4', '5', 'a', '\0'};char char2[] = {'b', '9', '\0'};printf("拼接前char1: %s, char2: %s\n", char1, char2);connect_char(char1, char2);printf("拼接后char1: %s\n", char1);
}

六、C 语言编程最佳实践与常见陷阱

6.1 编程规范与最佳实践

c

// 命名规范演示
#define MAX_STACK_SIZE 100 // 宏定义使用全大写
typedef struct {int data[MAX_STACK_SIZE];int top;
} Stack; // 结构体类型使用大写开头// 函数命名使用小写加下划线
Stack* stack_create() {Stack *s = (Stack *)malloc(sizeof(Stack));if (s == NULL) {return NULL;}s->top = -1;return s;
}// 注释规范
int calculate_sum(int a, int b) {// 计算两个整数的和return a + b;
}// 代码缩进与格式
if (n > 0) {for (int i = 0; i < n; i++) {if (arr[i] > 0) {process(arr[i]);}}
} else {printf("n 必须为正数\n");
}

6.2 常见陷阱与解决方案

c

// 数组越界陷阱
void array_overflow_trap() {int arr[5] = {1, 2, 3, 4, 5};int *ptr = arr;// 危险操作:越界访问for (int i = 0; i < 10; i++) {printf("%d ", ptr[i]); // 访问越界,行为未定义}
}// 内存泄漏陷阱
void memory_leak_trap() {while (1) {int *ptr = (int *)malloc(sizeof(int));// 忘记调用free(ptr)}
}// 空指针解引用陷阱
void null_pointer_dereference() {int *ptr = NULL;*ptr = 10; // 空指针解引用,程序崩溃
}// 解决方案:防御性编程
void defensive_programming() {int *ptr = (int *)malloc(sizeof(int));if (ptr == NULL) {printf("内存分配失败\n");return;}*ptr = 10;// 使用指针前检查是否为NULLif (ptr != NULL) {printf("ptr的值: %d\n", *ptr);}free(ptr);ptr = NULL; // 释放后置为NULL
}

七、总结+本人vscode本地编辑的源码:


 

1 附录1 指针知识代码源码:

2  附录代码2 :指针知识点总结
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>// 结构体定义
// 定义 Node 结构体,用于多级指针操作和链表
struct Node
{int value;              // 节点值struct Node *next;      // 指向下一个节点的指针(用于链表)struct Node **children; // 子节点数组(用于多级指针)int child_count;        // 子节点数量
};// 定义 Person 结构体,包含姓名和年龄
struct Person
{char name[10];int age;
};// 定义栈结构体
struct stack
{int data[100]; // 假设栈最大容量为 100int top;
};#define maxL 99 // 栈的最大容量// 函数声明
// 栈操作函数
void initStack(struct stack *s);
int isEmpty(struct stack *s);
int isFull(struct stack *s);
void pushStack(struct stack *s, int value);
int popStack(struct stack *s);
int peekTop(struct stack *s);// 链表操作函数
struct Node *insert_atFirst(struct Node *head, int data);
void print_List(struct Node *node);// 排序函数
void quick_Sort(int arr[], int low, int high);
int partition(int arr[], int low, int high);
void swap(int *a, int *b);// 字符串处理函数
char *my_strstr(const char *haystack, const char *needle);
char *my_strncpy(char *dest, const char *src, size_t n);
char *my_strncat(char *dest, const char *src, size_t n);
int my_strncmp(const char *s1, const char *s2, size_t n);
char *my_strchr(const char *str, int c);
char *str_copy(const char *src);// 其他函数
void wild_pointer_cause();
void pointer_relation();
void pointer_arithmetic_application();
void array_pointer_question();
void array_ptr_application();
void char_ptr_application();
void const_pointer();
void value_pass_application();
void pass_row_ptr(int arr[][3], int rows);
void qsort_demo();
void func_ptr_array();
void testFuncPtr();
int fibonacci(int n);
void test_fibonacci();
void typedef_usage();
void dachangmianshi3();
void test_strncpy();
void test_str_cat();
void test_strncmp();
void test_strchar();
void test_strstr();
void arrSort(char *arr[], int n);
int *dy_alloc(int n);
void add_child(struct Node *parent, struct Node *child);
void *arrtoPtrInt(void *x);
int getStrLen(char *a);
char *strCpyFn(char *dest, char *src);
void zhuanzhi(int arr[3][3]);
void connect_char(char *a, char *b);// 加法函数
int add(int a, int b);
// 减法函数
int subtract(int a, int b);
// 测试字符串复制函数
void test_str_copy();// 栈操作函数实现
// 初始化栈
void initStack(struct stack *s)
{s->top = -1;
}// 判断栈是否为空
int isEmpty(struct stack *s)
{return s->top == -1 ? 1 : -100;
}// 判断栈是否已满
int isFull(struct stack *s)
{return s->top == maxL ? 1 : 0;
}// 入栈操作
void pushStack(struct stack *s, int value)
{if (!isFull(s)){s->data[++(s->top)] = value;}
}// 出栈操作
int popStack(struct stack *s)
{if (!isEmpty(s)){return s->data[(s->top)--];}return -1;
}// 获取栈顶元素
int peekTop(struct stack *s)
{return s->data[s->top];
}// 链表操作函数实现
// 在链表头部插入节点
struct Node *insert_atFirst(struct Node *head, int data)
{struct Node *n_node = (struct Node *)malloc(sizeof(struct Node));n_node->value = data;    // 修改为正确的成员名 valuen_node->next = head;     // 使用正确的成员名 nextn_node->children = NULL; // 初始化子节点数组n_node->child_count = 0; // 初始化子节点数量return n_node;
}// 打印链表节点数据
void print_List(struct Node *node)
{struct Node *l = node;while (l != NULL){printf("当前打印的节点:数据为 %d \n", l->value); // 修改为正确的成员名 valuel = l->next;                                      // 使用正确的成员名 next}
}// 排序函数实现
// 快速排序
void quick_Sort(int arr[], int low, int high)
{if (low < high){int pi = partition(arr, low, high);quick_Sort(arr, low, pi - 1);quick_Sort(arr, pi + 1, high);}
}// 分区函数
int partition(int arr[], int low, int high)
{int pi = arr[high];int i = low - 1;for (int j = low; j <= high - 1; j++){if (arr[j] <= pi){i++;swap(&arr[i], &arr[j]);}}swap(&arr[i + 1], &arr[high]);return i + 1;
}// 交换两个整数的值
void swap(int *a, int *b)
{int temp = *a;*a = *b;*b = temp;
}// 字符串处理函数实现
// 自定义 strstr 函数,查找子字符串
char *my_strstr(const char *haystack, const char *needle)
{assert(haystack != NULL && needle != NULL);if (*needle == '\0'){return NULL;}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;
}// 自定义 strncpy 函数,复制指定长度的字符串
char *my_strncpy(char *dest, const char *src, size_t n)
{assert(dest != NULL && src != NULL);size_t i = 0;char *tempPtr = dest;while (i < n && src[i]){dest[i] = src[i];i++;}while (i < n){dest[i] = '\0';i++;}return tempPtr;
}// 自定义 strncat 函数,连接指定长度的字符串
char *my_strncat(char *dest, const char *src, size_t n)
{assert(dest != NULL && src != NULL);size_t len = strlen(dest);char *res = dest;size_t i;for (i = 0; i < n && src[i] != '\0'; i++){dest[len + i] = src[i];}dest[i + len] = '\0';return res;
}// 自定义 strncmp 函数,比较指定长度的字符串
int my_strncmp(const char *s1, const char *s2, size_t n)
{assert(s1 != NULL && s2 != NULL);for (size_t i = 0; i < n; i++){char c1 = s1[i] ? s1[i] : '\0';char c2 = s2[i] ? s2[i] : '\0';if (c1 != c2){return c1 - c2;}}return 0;
}// 自定义 strchr 函数,查找字符
char *my_strchr(const char *str, int c)
{printf("\n___>>>>> in the strchar func!!\n");assert(str != NULL);const char *p = str;while (*p){if (*p == (char)c){return (char *)p;}p++;}return (char)c == '\0' ? (char *)p : NULL;
}// 自定义字符串复制函数
char *str_copy(const char *src)
{char *dest = (char *)malloc(strlen(src) + 1);assert(dest != NULL);char *p = dest;while (*src){*p++ = *src++;}*p = '\0';return dest;
}// 其他函数实现
// 演示野指针的成因
void wild_pointer_cause()
{int a = 10;int *new_ptr = &a;int *heap_ptr = (int *)malloc(sizeof(int));free(heap_ptr);int arr[5] = {1, 2, 3, 4, 5};
}// 演示指针的关系运算
void pointer_relation()
{int arr[5] = {1, 2, 3, 4, 5};int *p1 = arr + 1;int *p2 = arr + 3;printf("p1 < p2: %s\n", (p1 < p2) ? "true" : "false");printf("p1 == arr+1: %s\n", (p1 == arr + 1) ? "true" : "false");
}// 演示指针的算术运算
void pointer_arithmetic_application()
{int arr[5] = {1, 2, 3, 4, 5};int *p = arr;for (int i = 0; i < 5; i++){printf("arr[%d] = %d\n", i, *(p + i));}
}// 演示数组指针相关问题
void array_pointer_question()
{int arr[5] = {1, 2, 3, 4, 5};printf("sizeof(arr) = %zu\n", sizeof(arr));printf("sizeof(arr+0) = %zu\n", sizeof(arr + 0));
}// 演示数组指针的应用
void array_ptr_application()
{int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};int (*p)[3] = arr;for (int i = 0; i < 3; i++){for (int j = 0; j < 3; j++){printf("1 the numn :%d-%d:%d\n", i, j, *(p + i)[j]);printf("2 the num: %d -%d : %d\n", i, j, p[i][j]);}}printf("\n");
}// 演示字符指针的应用
void char_ptr_application()
{char str1[20] = "Hello";char *str2 = ", World!";char *p1 = str1 + strlen(str1);char *p2 = str2;while (*p2){*p1++ = *p2++;}*p1 = '\0';printf("拼接后的字符串: %s\n", str1);
}// 演示 const 指针的使用
void const_pointer()
{int val = 10;const int *cp1 = &val;int *const cp2 = &val;
}// 演示值传递的应用
void value_pass_application()
{printf("-->>\nin hte 21 value-pass-func:\n---->>\n");int x = 5;void value_pass(int *);value_pass(&x);printf("x的值不变: %d\n", x);
}// 传递二维数组的行指针
void pass_row_ptr(int arr[][3], int rows)
{for (int i = 0; i < rows; i++){for (int j = 0; j < 3; j++){printf("%d ", arr[i][j]);}printf("\n");}
}// 比较函数,用于 qsort
int compare_asc(const void *a, const void *b)
{return *(int *)a - *(int *)b;
}// 演示 qsort 函数的使用
void qsort_demo()
{int arr[] = {3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5};int n = sizeof(arr) / sizeof(arr[0]);qsort(arr, n, sizeof(int), compare_asc);for (int i = 0; i < n; i++){printf("%d ", arr[i]);}printf("\n");
}// 演示函数指针数组的使用
int add(int a, int b);
int subtract(int a, int b); // 修正函数声明void func_ptr_array()
{typedef int (*OpFunc)(int, int);OpFunc ops[] = {add, subtract};printf("5+3=%d\n", ops[0](5, 3));printf("5-3=%d\n", ops[1](5, 3));
}// 测试函数指针
void testFuncPtr()
{// 示例代码可根据实际需求添加
}// 斐波那契数列递归实现
int fibonacci(int n)
{if (n <= 1)return n;return fibonacci(n - 1) + fibonacci(n - 2);
}// 测试斐波那契数列
void test_fibonacci()
{printf("fib(5) = %d\n", fibonacci(5));
}// 演示 typedef 的使用
void typedef_usage()
{typedef int IntArray[5];IntArray arr;arr[0] = 10;printf("typedef 使用示例: %d\n", arr[0]);
}// 大厂面试相关代码
void dachangmianshi3()
{
#define PTR_INT int *typedef int *ptr_int;PTR_INT a, b;ptr_int x, y;printf("--->>> %s %s \n", "int*", "int");printf("--->>> %s %s \n", "int*", "int*");
}// 测试 strncpy 函数
void test_strncpy()
{char dest[10] = {0};my_strncpy(dest, "hello", 3);printf("strncpy---复制结果: %s\n", dest);
}// 测试 strncat 函数
void test_str_cat()
{printf("\n---->>>>>\n strcat 函数测试\n");char dest[10] = {'1', '2', '\0'};char src[] = {'3', '4', 'a', '\0'};char *res = my_strncat(dest, src, 3);printf("--->>> \n str cat func --->>> \n%s", res);
}// 测试 strncmp 函数
void test_strncmp()
{char s1[] = {'1', '2', '3', '5'};char s2[] = {'1', '2', '3'};printf("\n--->>>\nstrnCmp 函数测试结果:\n%d", my_strncmp(s1, s2, 4));
}// 测试 strchr 函数
void test_strchar()
{char test[] = {'1', '2', 'a', 'b', 'c', 'f', 'l', '6', '\0'};char *res = my_strchr(test, '7');if (!res){printf("not found!!\n");}else{printf("founded !!!! res is %c  \n\n", *res);}
}// 测试 strstr 函数
void test_strstr()
{char haystack[] = {'1', 'a', '2', 'b', 'c', '\0'};char needle[] = {'b', 'c', '\0'};char *res = my_strstr(haystack, needle); // 使用自定义的 my_strstr 函数if (res){printf("-->>>>>>\n now in strstr func, res is :%c \n", *res);}else{printf("-->>>>>>\n now in strstr func, not found \n");}
}// 对字符串数组进行排序
void arrSort(char *arr[], int n)
{for (int i = 0; i < n - 1; i++)for (int j = 0; j < n - 1 - i; j++)if (strcmp(arr[j], arr[j + 1]) > 0){char *temp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = temp;}
}// 动态分配数组
int *dy_alloc(int n)
{int *res = (int *)malloc(n * sizeof(int));for (int i = 0; i < n; i++)res[i] = i;return res;
}// 添加子节点
void add_child(struct Node *parent, struct Node *child)
{parent->children = realloc(parent->children, (parent->child_count + 1) * sizeof(struct Node *));parent->children[parent->child_count++] = child;
}// void 指针通用类型转换
void *arrtoPtrInt(void *x)
{return (char *)(x) + 2;
}// 获取字符串长度
int getStrLen(char *a)
{char *p = a;while (*p)p++;return p - a;
}// 字符串拷贝函数
char *strCpyFn(char *dest, char *src)
{char *result = dest;while (*src)*dest++ = *src++;*dest = '\0';return result;
}// 矩阵转置
void zhuanzhi(int arr[3][3])
{for (int i = 0; i < 3; i++){for (int j = i + 1; j < 3; j++){int temp = arr[i][j];arr[i][j] = arr[j][i];arr[j][i] = temp;}}
}// 连接两个字符串
void connect_char(char *a, char *b)
{char *p1 = a + strlen(a);while (*b){*p1 = *b;p1++;b++;}*p1 = '\0';
}// 加法函数
int add(int a, int b)
{return a + b;
}// 减法函数
int subtract(int a, int b)
{return a - b;
}// 测试字符串复制函数
void test_str_copy()
{char *copy = str_copy("test");printf("复制后的字符串: %s\n", copy);free(copy);
}
void value_pass(int *p)
{printf("在 value_pass 函数中:原值 = %d\n", *p);*p = *p + 10;printf("在 value_pass 函数中:新值 = %d\n", *p);
}// 主函数
int main()
{printf("=== C 语言知识点综合测试 ===\n\n");// 野指针相关测试printf("野指针相关测试:\n");wild_pointer_cause();printf("\n");// 指针关系和算术运算测试printf("指针关系和算术运算测试:\n");pointer_relation();pointer_arithmetic_application();printf("\n");// 数组指针测试printf("数组指针测试:\n");array_pointer_question();array_ptr_application();printf("\n");// 字符指针测试printf("字符指针测试:\n");char_ptr_application();printf("\n");// const 指针测试printf("const 指针测试:\n");const_pointer();printf("\n");// 值传递测试printf("值传递测试:\n");value_pass_application();printf("\n");// 二维数组行指针传递测试printf("二维数组行指针传递测试:\n");int arr2d[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};pass_row_ptr(arr2d, 3);printf("\n");// 字符串复制测试printf("字符串复制测试:\n");test_str_copy(); // 确保函数已声明和定义printf("\n");// qsort 函数测试printf("qsort 函数测试:\n");qsort_demo();func_ptr_array();testFuncPtr();printf("\n");// 斐波那契数列测试printf("斐波那契数列测试:\n");test_fibonacci();printf("\n");// typedef 使用测试printf("typedef 使用测试:\n");typedef_usage();printf("\n");// 大厂面试相关代码测试printf("大厂面试相关代码测试:\n");dachangmianshi3();printf("\n");// 字符串处理函数测试printf("字符串处理函数测试:\n");test_strncpy();test_str_cat();test_strncmp();test_strchar();test_strstr();printf("\n");// 栈操作测试printf("栈操作测试:\n");struct stack s;initStack(&s);printf("初始化后,栈是否为空: %d\n", isEmpty(&s));pushStack(&s, 1);printf("压入 1 后,栈是否为空: %d\n", isEmpty(&s));pushStack(&s, 2);pushStack(&s, 3);printf("栈顶元素: %d\n", peekTop(&s));pushStack(&s, 4);printf("栈顶元素: %d\n", peekTop(&s));popStack(&s);printf("弹出元素后,栈顶元素: %d\n", peekTop(&s));printf("\n");// 链表操作测试printf("链表操作测试:\n");struct Node *head_node = NULL;head_node = insert_atFirst(head_node, 123);printf("第一个节点: %d\n", head_node->value); // 修改为正确的成员名 valuehead_node = insert_atFirst(head_node, 4);printf("第二个节点: %d\n", head_node->value); // 修改为正确的成员名 valueprint_List(head_node);printf("\n");// 快速排序测试printf("快速排序测试:\n");int arr11[] = {4, 5, 6, 7, 15, 234, 46, 698, 238, 258, 45, 2, 36, 26, 123, 77, 5, 48, 45, 2, 5, 325, 32, 1, 6};int len = sizeof(arr11) / sizeof(arr11[0]);quick_Sort(arr11, 0, len - 1);for (int i = 0; i < len; i++){printf("排序后输出的结果: %d\n", arr11[i]);}printf("\n");printf("=== 测试结束 ===\n");return 0;
}

第三部分:本人结合AI所总结的相关c语言知识点总结

 

知识点总结表格:

考点分类具体知识点常见题型解题关键点易错点
基础概念指针与数组的本质区别选择题(如数组名衰退规则)数组名在sizeof&操作外衰退为指针,数组存储位置由定义方式决定混淆数组与指针的本质,认为数组名始终是指针
指针大小与平台相关性选择题(64 位系统指针大小)指针大小仅由操作系统位数决定(32 位 4 字节,64 位 8 字节)误以为指针大小与指向类型有关
野指针成因与危害代码找错题未初始化、释放未置 NULL、越界访问、指向临时变量是野指针主因忽视临时变量地址失效问题
数组与指针二维数组与指针运算表达式求值题(如arr[1][2]的指针表示)二维数组按行存储,arr[i][j]等价于*(*(arr+i)+j)错误计算指针偏移量
指针数组与数组指针辨析定义辨析题int (*p)[5]是数组指针,int *p[5]是指针数组混淆括号优先级导致类型判断错误
字符串指针与数组陷阱代码运行结果题字符串字面量存储在只读区,数组可修改,指针指向不可修改修改字符串常量导致段错误
内存管理动态内存分配与 realloc 陷阱代码找错题realloc 需检查返回值,成功时原指针可能改变直接释放原指针导致内存泄漏
指针与结构体对齐sizeof 计算题结构体对齐规则:成员按自身大小对齐,整体为最大对齐参数整数倍忽视对齐导致结构体大小计算错误
多级指针操作指针解引用题多级指针解引用次数等于级别数,注意指针指向的指针层级解引用次数不足或过多导致访问错误
函数指针函数指针数组实现计算器编程题定义函数指针类型,通过数组索引调用对应函数函数指针类型定义错误
通用排序中的 void * 指针算法实现题使用void*memcpy实现任意类型排序,需传入比较函数类型转换时未考虑内存对齐问题
算法应用链表逆序(指针操作)数据结构题三指针法:prevcurrentnext配合反转指针指针更新顺序错误导致链表断裂
快速排序中的指针应用算法题用指针偏移实现任意类型分区,void*配合memcpy交换元素分区时偏移量计算错误
系统级操作指针与数组传参陷阱sizeof 计算题数组作为参数衰退为指针,无法获取原始大小在函数内用sizeof(arr)获取数组大小
指针运算与类型转换表达式求值题指针算术步长由指向类型决定,不同类型指针相减得到元素个数忽视指针类型不同导致偏移量错误
内存泄漏检测与预防代码分析题动态内存分配后需正确释放,realloc后更新指针遗漏realloc后的指针更新导致内存泄漏
指针实现字符串拷贝编程题指针遍历源字符串,逐个字符复制,处理NULL输入未处理源字符串或目标字符串为NULL的情况
链表环检测(Floyd 判圈算法)算法题快慢指针法:慢指针每次走 1 步,快指针每次走 2 步,相遇则有环边界条件处理不当(空链表或单节点链表)
指针与内存池设计系统编程题预分配大内存,用指针跟踪分配位置,避免碎片内存池边界检查缺失导致越界
指针与类型转换(内存复用)底层编程题通过强制类型转换实现不同类型内存复用,如intfloat互转违反类型安全原则导致未定义行为
指针与位操作(内存映射)位运算题用指针定位内存

逻辑关系图:

指针知识体系
│
├── 基础概念
│   ├── 指针本质(地址操作、类型系统)
│   ├── 指针与数组的关系
│   │   ├── 数组名衰退规则
│   │   ├── 二维数组内存布局
│   │   └── 指针数组 vs 数组指针
│   ├── 指针大小(平台相关性)
│   └── 野指针
│       ├── 成因(未初始化、释放未置NULL、越界、临时变量)
│       └── 危害(段错误、数据破坏)
│
├── 内存管理
│   ├── 动态分配(malloc/realloc/free)
│   │   ├── realloc陷阱(返回值检查、指针更新)
│   │   └── 内存泄漏检测
│   ├── 结构体对齐
│   │   ├── 对齐规则(成员对齐、整体对齐)
│   │   └── sizeof计算
│   └── 多级指针操作(解引用层级)
│
├── 函数指针
│   ├── 函数指针定义与调用
│   ├── 函数指针数组(回调机制)
│   └── 通用编程(void*指针+比较函数)
│
├── 算法与数据结构
│   ├── 链表操作
│   │   ├── 链表逆序(三指针法)
│   │   └── 环检测(Floyd算法)
│   ├── 排序算法
│   │   ├── 快速排序(指针分区)
│   │   └── 通用排序实现
│   └── 字符串处理(指针实现strcpy等)
│
└── 系统级编程├── 指针传参与数组衰退├── 指针运算(类型转换、偏移量)├── 内存池设计(预分配与指针跟踪)└── 底层操作(位运算、内存复用)

最后:   
                    觉得我写得不错的,还请各位给一个点赞收藏关注!


 

相关文章:

一文带你彻底理清C 语言核心知识 与 面试高频考点:从栈溢出到指针 全面解析 附带笔者手写2.4k行代码加注释

引言&#xff1a;C 语言的魅力与挑战 从操作系统内核到嵌入式系统&#xff0c;从高性能计算到网络编程&#xff0c;C 语言高效、灵活和贴近硬件的特性&#xff0c;始终占据着不可替代的地位。然而&#xff0c;C 语言的强大也伴随着较高的学习曲线&#xff0c;尤其是指针、内存管…...

【Redis】第1节|Redis服务搭建

一、Redis 基础概念 核心功能 内存数据库&#xff0c;支持持久化&#xff08;RDB/AOF&#xff09;、主从复制、哨兵高可用、集群分片。常用场景&#xff1a;缓存、分布式锁、消息队列、计数器、排行榜等。 安装环境 依赖 GCC 环境&#xff08;C语言编译&#xff09;&#xff0…...

数据结构第5章 树与二叉树(竟成)

第 5 章 树与二叉树 【考纲内容】 1.树的基本概念 2.二叉树 &#xff08;1&#xff09;二叉树的定义及其主要特征 &#xff08;2&#xff09;二叉树的顺序存储结构和链式存储结构 &#xff08;3&#xff09;二叉树的遍历 &#xff08;4&#xff09;线索二叉树的基本概念和构造 …...

# 深入解析BERT自然语言处理框架:原理、结构与应用

深入解析BERT自然语言处理框架&#xff1a;原理、结构与应用 在自然语言处理&#xff08;NLP&#xff09;领域&#xff0c;BERT&#xff08;Bidirectional Encoder Representations from Transformers&#xff09;框架的出现无疑是一个重要的里程碑。它凭借其强大的语言表示能…...

ai学习--python部分-1.变量名及命名空间的存储

初学代码时总有一个问题困扰我&#xff1a;a 10 # a指向地址0x1234&#xff08;存储10&#xff09; 变量a的值10存储在0x1234&#xff0c;那么变量a需要存储吗&#xff1f;a又存储在什么地址呢 目录 1. ​​命名空间的本质​​ 2. ​​命名空间的内存占用​​ 3. ​​…...

Cadence学习笔记之---PCB过孔替换、封装更新,DRC检查和状态查看

目录 01 | 引 言 02 | 环境描述 03 | 过孔替换 04 | 封装更新 05 | PCB状态查看 06 | DRC检查 07 | 总 结 01 | 引 言 终于终于来到了Cadence学习笔记的尾声&#xff01; 在上一篇文章中&#xff0c;讲述了如何布线、如何铺铜&#xff0c;以及布线、铺铜过程中比较重要…...

Java基础 Day21

一、Stream 流 思想&#xff1a;流水线式思想 1、获取流对象&#xff08;将数据放到流中&#xff09; &#xff08;1&#xff09;集合获取 Stream 流对象 使用Collection接口的默认方法 default Stream<E> stream() 获取当前集合对象的 Stream 流&#xff08;单列集…...

系统开发和运行知识

软件生存周期 软件生存周期包括可行性分析与项目开发计划、需求分析、概要设计、详细设计、编码和单元测试、综合测试及维护阶段。 1、可行性分析与项目开发计划 主要任务是确定软件的开发目标及可行性。该阶段应该给出问题定义、可行性分析和项目开发计划。 2、需求分析 需求…...

Elasticsearch 分片驱逐(Shard Exclusion)方式简析:`_name`、`_ip`、`_host`

在日常运维 Elasticsearch 集群过程中&#xff0c;常常需要将某个节点上的分片迁移出去&#xff0c;例如下线节点、腾出资源或进行维护操作。Elasticsearch 提供了简单直观的 shard exclusion 参数来实现这一目的&#xff0c;主要通过以下三个配置项&#xff1a; cluster.rout…...

【C++高级主题】异常处理(四):auto_ptr类

目录 一、auto_ptr 的诞生&#xff1a;为异常安全的内存分配而设计 1.1 传统内存管理的痛点 1.2 auto_ptr 的核心思想&#xff1a;RAII 与内存绑定 1.3 auto_ptr 的基本定义&#xff08;简化版&#xff09; 二、auto_ptr 的基本用法&#xff1a;将指针绑定到智能对象 2.1…...

STM32CubeMX配置使用通用定时器产生PWM

一、定时器PWM功能简介 定时器&#xff0c;顾名思义&#xff0c;就是定时的功能&#xff0c;定时器在单片机中算是除GPIO外最基本的外设。在ST中&#xff0c;定时器分为几种&#xff0c;基础定时器&#xff0c;通用定时器&#xff0c;高级定时器和低功耗定时器。其中定时器除了…...

WebSphere Application Server(WAS)8.5.5教程第十四讲:JPA

一、JPA 以下是对 JPA&#xff08;Java Persistence API&#xff09; 的深入详解&#xff0c;适用于具备一定 Java EE / Jakarta EE 背景的开发者&#xff0c;尤其是对数据持久化机制感兴趣的人员。 1、什么是 JPA&#xff1f; Java Persistence API&#xff08;JPA&#xf…...

Linux系统调用深度剖析

Linux系统调用深度剖析与实践案例 目录 Linux系统调用深度剖析与实践案例 一、Linux系统调用概述 二、进程管理相关系统调用 1. fork():进程克隆与多任务处理 2. exec系列:程序加载与替换 3. wait/waitpid:进程状态同步 三、文件操作相关系统调用 1. 文件描述符操作…...

动态规划-918.环形子数组的最大和-力扣(LeetCode)

一、题目解析 听着有点复杂&#xff0c;这里一图流。 将环形问题转化为线性问题。 二、算法原理 1.状态表示 2.状态转移方程 详细可以移步另一篇博客&#xff0c;53. 最大子数组和 - 力扣&#xff08;LeetCode&#xff09; 3.初始化 由于计算中需要用到f[i-1]和g[i-1]的值&…...

Docker 镜像标签(Tag)规范与要求

Docker 镜像标签(Tag)规范与要求 背景 目前主流云厂商&#xff0c;如阿里云、百度云和腾讯云&#xff0c;均提供租户使用的镜像仓库服务。 各个厂商要求可能不太一样&#xff0c;比如华为&#xff1a;https://doc.hcs.huawei.com/zh-cn/usermanual/swr/swr_faq_0017.html 样…...

STM32:Modbus通信协议核心解析:关键通信技术

知识点1【 Modbus通信】 1、Modbus的概述 Modbus是OSI模型第七层的应用层报文传输协议 协议&#xff1a;说明有组包和解包的过程 2、通信机制 Modelbus是一个请求/应答协议 通信机制&#xff1a;主机轮询&#xff0c;从机应答的机制。每个从设备有唯一的地址&#xff0c;主…...

线程封装与互斥

目录 线程互斥 进程线程间的互斥相关背景概念 互斥量mutex 互斥量的接口 初始化互斥量有两种方法&#xff1a; 销毁互斥量 互斥量加锁和解锁 改进售票系统 互斥量实现原理探究 互斥量的封装 线程互斥 进程线程间的互斥相关背景概念 临界资源&#xff1a;多线程执行流共…...

鸿蒙OSUniApp 开发实时天气查询应用 —— 鸿蒙生态下的跨端实践#三方框架 #Uniapp

使用 UniApp 开发实时天气查询应用 —— 鸿蒙生态下的跨端实践 在移动互联网时代&#xff0c;天气应用几乎是每个人手机中的"标配"。无论是出行、旅游还是日常生活&#xff0c;实时获取天气信息都极为重要。本文将以"实时天气查询应用"为例&#xff0c;详…...

第十一天 5G切片技术在车联网中的应用

前言 在自动驾驶汽车每天产生4TB数据的时代&#xff0c;传统的移动网络已难以满足车联网的海量连接需求。中国移动2023年实测数据显示&#xff0c;某智能网联汽车示范区在传统5G网络下&#xff0c;紧急制动指令的传输延迟高达65ms&#xff0c;而5G网络切片技术将这个数值降低到…...

Spring AI 系列之一个很棒的 Spring AI 功能——Advisors

1. 概述 由AI驱动的应用程序已成为我们的现实。我们正在广泛地实现各种RAG应用程序、提示API&#xff0c;并利用大型语言模型&#xff08;LLM&#xff09;创建项目。借助 Spring AI&#xff0c;我们可以更快速地完成这些任务。 在本文中&#xff0c;我们将介绍一个非常有价值…...

Vue3 + TypeScript + el-input 实现人民币金额的输入和显示

输入人民币金额的参数要求&#xff1a; 输入要求&#xff1a; 通过键盘&#xff0c;只允许输入负号、小数点、数字、退格键、删除键、方向左键、方向右键、Home键、End键、Tab键&#xff1b;负号只能在开头&#xff1b;只保留第一个小数点&#xff1b;替换全角输入的小数点&a…...

2.1 C++之条件语句

学习目标&#xff1a; 理解程序的分支逻辑&#xff08;根据不同条件执行不同代码&#xff09;。掌握 if-else 和 switch 语句的用法。能编写简单的条件判断程序&#xff08;如成绩评级、游戏选项等&#xff09;。 1 条件语句的基本概念 什么是条件语句&#xff1f; 程序在执…...

ZYNQ实战:可编程差分晶振Si570的配置与动态频率切换

为什么需要可编程差分晶振? 在现代FPGA和嵌入式系统中,高速串行通信(如GTP/GTX收发器)对参考时钟的精度和灵活性要求极高。例如,1G以太网需要125MHz时钟,SATA协议需120MHz,而DisplayPort则需135MHz。传统固定频率晶振无法满足多协议动态切换需求,而Si570凭借其10MHz~8…...

Linux `ls` 命令深度解析与高阶应用指南

Linux `ls` 命令深度解析与高阶应用指南 一、核心功能解析1. 基本作用2. 与类似命令对比二、选项系统详解1. 常用基础选项2. 进阶筛选选项三、高阶应用技巧1. 组合过滤查询2. 格式化输出控制3. 元数据深度分析四、企业级应用场景1. 存储空间监控2. 安全审计3. 自动化运维五、特…...

【MPC控制 - 从ACC到自动驾驶】5. 融会贯通:MPC在ACC中的优势总结与知识体系构建

【MPC控制 - 从ACC到自动驾驶】融会贯通&#xff1a;MPC在ACC中的优势总结与知识体系构建 在过去的四天里&#xff0c;我们一起经历了一段奇妙的旅程&#xff1a; Day 1: 我们认识了自适应巡航ACC这位“智能领航员”&#xff0c;并初见了模型预测控制MPC这位“深谋远虑的棋手…...

Day3 记忆内容:map set 高频操作

以下是 第三天 的详细学习内容&#xff0c;聚焦 map和set的高效应用&#xff0c;重点突破查找类题型和去重逻辑&#xff0c;助你提升代码效率&#xff01; &#x1f4da; Day3 记忆内容&#xff1a;map & set 高频操作 1. map 核心操作&#xff08;手写3遍&#xff09; /…...

初等数论--Garner‘s 算法

0. 介绍 主要通过混合积的表示来逐步求得同余方程的解。 对于同余方程 { x ≡ v 0 ( m o d m 0 ) x ≡ v 1 ( m o d m 1 ) ⋯ x ≡ v k − 1 ( m o d m k − 1 ) \begin{equation*} \begin{cases} x \equiv v_0 \quad (\ \bmod \ m_0)\\ x \equiv v_1 \quad (\ \bmod \ m_1)…...

NV211NV212美光科技颗粒NV219NV220

NV211NV212美光科技颗粒NV219NV220 技术架构解析&#xff1a;从颗粒到存储系统 近期美光科技发布的NV211、NV212、NV219、NV220系列固态颗粒&#xff0c;凭借其技术突破引发行业关注。这些颗粒基于176层QLC堆叠工艺&#xff0c;单Die容量预计在2026年可达1Tb&#xff0c;相当…...

SQL解析工具JSQLParser

目录 一、引言二、JSQLParser常见类2.1 Class Diagram2.2 Statement2.3 Expression2.4 Select2.5 Update2.6 Delete2.7 Insert2.8 PlainSelect2.9 SetOperationList2.10 ParenthesedSelect2.11 FromItem2.12 Table2.13 ParenthesedFromItem2.14 SelectItem2.15 BinaryExpressio…...

Wave Terminal + Cpolar:SSH远程访问的跨平台实战+内网穿透配置全解析

文章目录 前言1. Wave Terminal安装2. 简单使用演示3. 连接本地Linux服务器3.1 Ubuntu系统安装ssh服务3.2 远程ssh连接Ubuntu 4. 安装内网穿透工具4.1 创建公网地址4.2 使用公网地址远程ssh连接 5. 配置固定公网地址 前言 各位开发者朋友&#xff0c;今天为您介绍一款颠覆性操…...