一文带你彻底理清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等)
│
└── 系统级编程├── 指针传参与数组衰退├── 指针运算(类型转换、偏移量)├── 内存池设计(预分配与指针跟踪)└── 底层操作(位运算、内存复用)

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


 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.pswp.cn/web/81394.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

GitHub 趋势日报 (2025年05月22日)

本日报由 TrendForge 系统生成 https://trendforge.devlive.org/ &#x1f310; 本日报中的项目描述已自动翻译为中文 &#x1f4c8; 今日整体趋势 Top 10 排名项目名称项目描述今日获星总星数语言1microsoft/WSLLinux的Windows子系统⭐ 2524⭐ 26627C2HeyPuter/puter&#x1…

AI智能混剪核心技术解析(一):字幕与标题生成的三大支柱-字幕与标题生成-优雅草卓伊凡

AI智能混剪核心技术解析&#xff08;一&#xff09;&#xff1a;字幕与标题生成的三大支柱-字幕与标题生成-优雅草卓伊凡 引言&#xff1a;文字到画面的桥梁工程 在AI视频混剪系统中&#xff0c;字幕与标题生成是连接语言表达与视觉呈现的核心枢纽。优雅草卓伊凡团队将该功能拆…

如何通过PHPMyadmin对MYSQL数据库进行管理?

管理MySQL数据库时&#xff0c;使用PHPMyAdmin是一种常见且方便的方式。PHPMyAdmin是一个基于Web的数据库管理工具&#xff0c;提供了许多功能&#xff0c;如数据库创建、表管理、数据查询、用户权限设置等。本文将介绍如何通过PHPMyAdmin对MySQL数据库进行管理&#xff0c;包括…

如何解决大模型返回的JSON数据前后加上```的情况

环境说明 springboot 应用使用dashscope-sdk-java对接阿里百练 deepseek v3模型 问题表现 已经指定了输出json格式&#xff0c;但指令不明确&#xff0c;输出JSON格式的写法如下 注&#xff1a;提示词一开始是能正常功能的&#xff0c;但过了几天就出现了异常&#xff0c;原…

uniapp实现H5、APP、微信小程序播放.m3u8监控视频

目录 1.APP播放.m3u8监控视频 2.H5播放.m3u8监控视频 3.微信小程序播放.m3u8监控视频 最近在写一个uniapp实现h5、app、微信小程序兼容三端的播放监控视频功能&#xff0c;我原本以为一套代码多处运行&#xff0c;但事实并非如此&#xff0c;h5可以运行&#xff0c;微信小程…

萤石云实际视频实时接入(生产环境)

萤石云视频接入 本示例可用于实际接入萤石云开放平台视频&#xff0c;同时支持音频输入和输出。 实际优化内容 1.动态获取token 2.切换各公司和车间时&#xff0c;自动重新初始化播放器 let EZUIKit null; // 第三方库引用 let EZUIKitPlayers []; // 播放器实例数组 le…

【Dify平台】使用Dify API 实现网页内嵌式AI助手

使用 Dify API 实现网页内嵌式 AI 助手 一. 引言二. Dify API 概述三. 实现网页内嵌式 AI 助手的技术架构四. 前端实现五. 后端实现六. 功能扩展与优化七. 测试与部署一. 引言 随着 AI 技术的不断发展,越来越多的企业希望将智能助手集成到自己的网页中,实现用户自动接待、问…

mysql8配置文件my.ini讲解,原汁原味直接拷贝再讲解

文章目录 一、原英文版本&#xff0c;不带注释二、由原版逐字翻译成的中文版&#xff08;行行对应&#xff09;三、最常用的配置 一、原英文版本&#xff0c;不带注释 # Other default tuning values # MySQL Server Instance Configuration File # -------------------------…

Go语言中内存释放 ≠ 资源释放

// QueryUserFileMetas : 批量获取用户文件信息 func QueryUserFileMetas(username string, limit int) ([]UserFile, error) {stmt, err : mydb.DBConn().Prepare("select file_sha1,file_name,file_size,upload_at," "last_update from tbl_user_file where u…

win11+vs2022 安装opencv 4.11.0图解教程

1. 下载opencv opencv官网下载地址&#xff1a;Releases - OpenCV 2. 双击运行该exe&#xff0c;即可进行安装&#xff0c;安装文件夹可自行选择 安装后目录如下&#xff1a; 3. 配置环境变量 使用win键搜索环境变量&#xff0c;选中系统变量中的Path&#xff0c;然后点击编辑…

【Linux】进程 信号的产生

&#x1f33b;个人主页&#xff1a;路飞雪吖~ &#x1f320;专栏&#xff1a;Linux 目录 一、掌握Linux信号的基本概念 &#x1f320;前台进程 VS 后台进程 &#x1f320; 小贴士&#xff1a; &#x1fa84;⼀个系统函数 --- signal() &#x1fa84;查看信号 --- man 7 sign…

Python 网络编程入门

目录 一、前言 二、网络通信基础12&#xff1a;TCP 与 UDP 协议解析 2.1 TCP 协议&#xff1a;可靠的面向连接通信 2.2 UDP 协7议&#xff1a;无连接的快速通信 2.3 Sock12et&#xff1a;网络通信的基石 三、TCP 编程实15战&#xff1a;从单工通信到双向聊天 3.1 TCP 客…

Django压缩包形式下载文件

通过web将minio上的文件以压缩包-文件夹-文件的形式下载到本地 import os from bx_mes import settings from io import BytesIO import zipfile from django.http import StreamingHttpResponse class FileRemote(GenericAPIView):def post(self,request):# 压缩包名folder_n…

Enhancing Relation Extractionvia Supervised Rationale Verifcation and Feedback

Enhancing Relation Extraction via Supervised Rationale Verification and Feedback| Proceedings of the AAAI Conference on Artificial Intelligencehttps://ojs.aaai.org/index.php/AAAI/article/view/34631 1. 概述 关系抽取(RE)任务旨在抽取文本中实体之间的语义关

【RAG】ragflow源码亮点:文档embedding向量化加权融合

引言&#xff1a; 最近在看ragflow源码&#xff0c;其中有一个较为巧妙地设计&#xff1a;分别将 文字 、 标题 行向量化 之后&#xff0c;直接根据权重&#xff0c;进行加法运算&#xff0c;得到向量融合&#xff0c;增强了文本向量化的表示能力&#xff0c;这里开始讨论一下…

限流系列:sentinel

目录 滑动窗口算法 Sentinel 数据模型 示例 大致流程 ​​​​​​​entry ​​​​​​​entryWithPriority ​​​​​​​FlowSlot.entry ​​​​​​​checkFlow ​​​​​​​canPass ​​​​​​​avgUsedTokens ​​​​​​​passQps ​​​​​​​pa…

Java 访问者模式深度重构:从静态类型到动态行为的响应式设计实践

一、访问者模式的本质与核心价值 在软件开发的漫长演进中&#xff0c;设计模式始终是架构师手中的利刃。当我们面对复杂对象结构上的多种操作需求时&#xff0c;访问者模式&#xff08;Visitor Pattern&#xff09;犹如一把精密的手术刀&#xff0c;能够优雅地分离数据结构与作…

UE 5 C++设置物体位置和旋转,初始化虚幻引擎样条线、加载引用虚幻编辑器中的蓝图、设置虚幻编辑器中Actor大小

一、设置物体位置和旋转 UE.cpp文件中代码&#xff1a; Mesh->SetWorldLocationAndRotation(FVector(50.0f, 50.0f, 50.0f),FRotator(0,-90,0)); vs代码编辑器中旋转信息顺序&#xff08;yzx&#xff09;&#xff1a; Pitch、 Yaw、 Roll UE编辑器中旋转信息顺序&#xf…

【文本分类】KG-HTC 知识图谱提升分类准确率

最近看到一篇论文“KG-HTC: Integrating Knowledge Graphs into LLMs for Effective Zero-shot Hierarchical Text Classification”&#xff0c;介绍了文本分类的技巧&#xff0c;这篇文航主要利用了知识图谱大模型的思路&#xff0c;实验效果不错&#xff0c;里面的一些论述也…

三大微调技术对比:Prompt/Prefix/P-Tuning

Prompt Tuning、Prefix Tuning和P - Tuning的区别 概念方面: Prompt Tuning:在输入序列前添加可训练的额外Token以适配下游任务,预训练语言模型参数不变。比如在文本分类中,在句子前加特定Token如“(OPINION)”,让模型理解是对观点进行分类的任务。Prefix Tuning:在每层T…