一文帶你徹底理清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
繁體地址,請注明出處:http://hk.pswp.cn/web/81394.shtml
英文地址,請注明出處:http://en.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…