深入浅出c 语言核心精髓指针 3000 行代码的核心知识点解析- 附4k行手写代码调试过程

一、知识点全景表格(覆盖我这3千行代码的全部内容!

知识领域具体知识点代码示例工业实践建议
数组基础一维数组初始化与遍历里面的testArrayInitialization()函数,演示显式初始化、隐式补零、自动计算长度初始化时避免数组越界,建议用sizeof计算长度,如ARRAY_LEN(arr)
数组越界风险与调试demonstrateArrayOutOfBounds()故意越界访问,触发未定义行为-fsanitize=address编译选项检测越界,配合gdb追踪地址访问错误
二维数组内存布局与转置zhuanzhi()函数实现矩阵转置,通过行优先遍历交换元素二维数组传参时用行指针int arr[][col],避免数组退化为一级指针导致的列数丢失
排序算法冒泡排序优化(含有序标记)bubbleSort()基础实现,可优化添加isSorted标志提前终止循环大规模数据用qsort库函数,小规模用插入排序,冒泡仅用于教学场景
选择排序的原地交换逻辑selectionSort()通过最小索引交换元素,时间复杂度 O (n²)不稳定排序,适合不要求稳定性的场景(如哈希表冲突处理)
快速排序与分区策略quick_Sort()结合partition()实现降序分区,输出每次分区结果三数取中法优化枢轴选择,避免最坏情况 O (n²),建议用shuffle预处理数组
指针深度指针与数组的等价性arr1_ptr()int*指针遍历数组,验证arr[i] == *(ptr+i)数组名作为参数时退化为指针,务必同时传递长度参数,如printArray(arr, len)
多级指针与结构体指针数组Node结构体中childrenstruct Node**二级指针,动态管理子节点数组多级指针初始化时需parent->children = NULL,扩容用realloc并校验返回值
void 指针的通用转换arrtoPtrInt()char*偏移实现任意类型指针运算,返回void*避免void*直接解引用,必须强转类型并确保对齐,如*(int*)ptr
内存管理动态内存分配与性能测试dy_alloc_mem()分配 100 万元素数组,用clock()测试初始化耗时大块内存用mmap替代malloc,用valgrind --tool=massif分析内存峰值
柔性数组与结构体设计代码未显式实现,但可扩展struct Node添加柔性数组children[]节省内存柔性数组需保证最后一个元素为变长部分,如struct { int len; int data[]; } Array;
数据结构单向链表的头插法实现insert_atFirst()创建新节点并更新头指针,print_List()遍历链表链表节点用dummy head简化边界处理,销毁时需递归释放节点
顺序栈的临界条件处理stack结构体实现入栈pushStack、出栈popStack,包含空栈 / 满栈判断栈顶索引从 - 1 开始,入栈先自增再赋值,出栈先取值再自减,避免竞态条件
结构体嵌套与内存对齐Person_16嵌套Address结构体,用#pragma pack(1)取消对齐验证内存占用工业代码用__attribute__((packed))声明紧凑结构体,避免对齐导致的空间浪费
字符串处理指针遍历实现字符串逆序string_reverse()用双指针交换字符,时间复杂度 O (n)处理多字节字符(如 UTF-8)需按编码单元操作,避免截断字符
安全字符串拼接connect_char()strlen计算长度后追加,避免缓冲区溢出生产环境用snprintf替代手动拼接,指定目标缓冲区大小如connect_char(a, b, sizeof(a))
预处理与宏数组长度计算宏ARRAY_LEN(arr)sizeof计算数组元素个数,避免硬编码长度宏定义加括号(sizeof(arr)/sizeof((arr)[0])),防止表达式优先级问题
类型别名简化结构体声明typedef struct Node简化链表节点声明,相比struct Node减少代码冗余复杂结构体用typedef封装,如typedef struct { ... } ListNode;
函数与指针函数指针实现计算器代码注释中保留的计算器逻辑,通过int (*func_ptr)(int, int)指向add/substract用函数指针数组实现命令分发,如int (*ops[])(int, int) = {add, sub, mul, div};
可变参数与日志系统代码注释中的DEBUG(fmt, ...)宏,可扩展为带文件 / 行号的日志输出va_list实现可变参数函数,如void log(const char* fmt, ...)

二、核心模块深度解析

1. 数组与指针:内存操作的双刃剑

1.1 数组越界的致命陷阱
void demonstrateArrayOutOfBounds() {
    int arr[6] = {1, 2, 3, 4, 5, 6};
    for (int i = 0; i <= 8; i++) { // 访问arr[6]~arr[8]触发越界
        printf("arr[%d]=%d\n", i, arr[i]); // 可能输出随机值或崩溃
    }
}

  • 风险分析:越界访问会覆盖相邻内存,可能导致:
    • 栈帧破坏引发段错误(如覆盖函数返回地址)
    • 堆内存元数据损坏导致malloc崩溃
    • 静默数据错误(如误改其他变量值)
  • 工业方案
    • -fsanitize=address编译,运行时检测越界
    • 封装安全数组结构体:

      typedef struct {
          int *data;
          size_t len;
          size_t cap;
      } SafeArray;
      int safe_array_get(SafeArray *arr, size_t index, int *val) {
          if (index >= arr->len) return -1; // 越界返回错误码
          *val = arr->data[index];
          return 0;
      }
      
1.2 二维数组转置的指针优化

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;
        }
    }
}

  • 内存布局:二维数组在内存中按行优先存储,arr[i][j]等价于*(arr[i] + j)
  • 行指针传参:函数参数int arr[3][3]等价于int (*arr)[3],表示指向含 3 个元素的一维数组的指针
  • 扩展应用:动态二维数组用指针数组实现:

    int **alloc_2dArr(int row, int col) {
        int **a = malloc(row * sizeof(int*));
        for (int i=0; i<row; i++) {
            a[i] = malloc(col * sizeof(int));
        }
        return a; // 调用后需逐层free
    }
    

2. 排序算法:从 O (n²) 到 O (n log n) 的性能跃迁

2.1 快速排序的分区与递归优化

int partition(int arr[], int left, int right) {
    int pivot = arr[right]; // 选择最后一个元素为枢轴
    int i = left - 1;
    for (int j = left; j < right; j++) {
        if (arr[j] > pivot) { // 降序分区:左侧元素 > 枢轴
            swap(&arr[++i], &arr[j]); // 交换到左侧
        }
    }
    swap(&arr[i+1], &arr[right]); // 枢轴归位
    return i + 1; // 枢轴索引即第i+1大元素位置
}

  • 时间复杂度:平均 O (n log n),最坏 O (n²)(有序数组需三数取中法优化)
  • 递归深度控制:对大区间用迭代实现,小区间用插入排序:

    void quick_sort_iterative(int arr[], int left, int right) {
        struct Stack { int l, r; } stack[100]; // 手动模拟栈
        int top = -1;
        push(&stack, &top, left, right);
        while (top >= 0) {
            struct Stack node = pop(&stack, &top);
            if (node.l < node.r) {
                int pi = partition(arr, node.l, node.r);
                // 先压入小区间,后压入大区间,减少栈深度
                if (pi - node.l < node.r - pi) {
                    push(&stack, &top, pi+1, node.r);
                    push(&stack, &top, node.l, pi-1);
                } else {
                    push(&stack, &top, node.l, pi-1);
                    push(&stack, &top, pi+1, node.r);
                }
            }
        }
    }
    

3. 动态内存与数据结构:工业级设计要点

3.1 链表节点的安全管理

struct Node *insert_atFirst(struct Node *head, int data) {
    struct Node *n_node = malloc(sizeof(struct Node)); // 分配节点
    if (!n_node) { perror("malloc failed"); exit(1); } // 错误处理
    n_node->data = data;
    n_node->next = head; // 新节点指向原头节点
    return n_node; // 返回新头节点
}

  • 内存泄漏预防
    • 插入失败时确保已分配内存被释放
    • 销毁链表时递归释放:

      void free_list(struct Node *head) {
          while (head) {
              struct Node *temp = head;
              head = head->next;
              free(temp);
          }
      }
      
  • 线程安全:多线程环境需加锁,如用pthread_mutex_t保护链表操作
3.2 顺序栈的临界条件测试

int popStack(struct stack *s) {
    if (isEmpty(s)) {
        return -1; // 空栈返回特殊值
    }
    return s->data[s->top--]; // 先取值,后减栈顶索引
}

  • 测试用例
    • 空栈弹出:验证返回值是否为 - 1
    • 满栈压入:调用isFull后验证是否拒绝
    • 边界值测试:栈满时top == maxL-1,而非maxL(原代码 bug)
  • 扩展功能:支持动态扩容栈:

    void resize_stack(struct stack *s, int new_size) {
        int *new_data = realloc(s->data, new_size * sizeof(int));
        if (new_data) {
            s->data = new_data;
            s->cap = new_size; // 新增容量字段
        }
    }
    

4. 字符串与指针:从底层到应用

4.1 安全字符串拼接的工业实现

void connect_char(char *a, char *b) {
    size_t a_len = strlen(a);
    size_t b_len = strlen(b);
    if (a_len + b_len + 1 > sizeof(a)) { // 假设a为固定大小数组
        fprintf(stderr, "buffer overflow risk!\n");
        return;
    }
    strcpy(a + a_len, b); // 等价于手动遍历
}

  • 缺陷分析:原代码未校验目标缓冲区大小,可能导致溢出
  • 正确实现

    void safe_strcat(char *dest, const char *src, size_t dest_size) {
        size_t dest_len = strlen(dest);
        size_t src_len = strlen(src);
        if (dest_len + src_len + 1 >= dest_size) {
            errno = ERANGE; // 设置错误码
            return;
        }
        memcpy(dest + dest_len, src, src_len + 1);
    }
    
4.2 void 指针的跨类型应用

void *arrtoPtrInt(void *x) {
    return (char *)x + 2; // 偏移2字节,适用于char*指针
}

  • 应用场景
    • 协议解析:跳过协议头 2 字节,如uint8_t header[2]; uint8_t data = *(uint8_t*)arrtoPtrInt(header);
    • 内存对齐:确保指针偏移后仍对齐,如(int*)((char*)ptr + (sizeof(int)-1)) & ~(sizeof(int)-1)

三、未覆盖知识点与扩展方向(基于代码注释)

知识领域缺失知识点工业实现建议
指针进阶const 限定符与指针权限const int*防止指针修改数据,用int* const防止指针重定向
函数指针数组与回调机制实现命令模式,如int (*ops[])(int, int) = {add, sub, mul};
内存管理内存池设计预分配大块内存,用链表管理空闲块,减少malloc/free系统调用开销
数据结构二叉树与哈希表用平衡二叉树实现快速查找,哈希表处理键值对存储(如用户信息管理)
预处理条件编译与带副作用宏#ifdef DEBUG包裹调试代码,宏定义避免副作用:#define MIN(a,b) ((a)<(b)?(a):(b))
标准库扩展qsort/bsearch 与安全字符串函数qsort(arr, len, sizeof(int), cmp)排序,strncpy替代strcpy防止溢出

四、我这4千行代码的核心函数解析  :我自己写的15 个典型案例

1. testArrayInitialization() - 数组初始化与内存布局

void testArrayInitialization() {
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5] = {1, 2, 3}; // 自动补0
    int arr3[] = {1, 3, 4, 5, 6}; // 自动计算长度
    
    printf("一维数组初始化测试:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr1[%d]=%d, arr2[%d]=%d, arr3[%d]=%d\n", 
               i, arr1[i], i, arr2[i], i, arr3[i]);
    }
}

知识点剖析

  • 显式初始化arr1完全初始化,内存布局为连续的 5 个整数
  • 隐式补零arr2部分初始化,未显式赋值的元素自动初始化为 0
  • 自动长度计算arr3省略长度,编译器根据初始化列表推断长度为 5
  • 内存对齐:数组元素按类型自然对齐,int通常占 4 字节(取决于平台)

工业实践

  • 避免越界访问:初始化时明确长度,或用sizeof(arr)/sizeof(arr[0])计算
  • 全局数组默认初始化为 0,局部数组需显式初始化
  • 多维数组初始化:int arr[2][3] = {{1,2,3},{4,5,6}}等价于按行展开的一维数组

2. demonstrateArrayOutOfBounds() - 数组越界的未定义行为

void demonstrateArrayOutOfBounds() {
    int arr[6] = {1, 2, 3, 4, 5, 6};
    printf("越界访问示例(危险!):\n");
    for (int i = 0; i <= 8; i++) { // 注意这里会访问arr[6]导致越界
        printf("arr[%d]=%d\n", i, arr[i]);
    }
}

风险分析

  • 栈内存布局:数组arr后的内存可能存储函数返回地址、局部变量
  • 越界后果
    • 读取随机值(访问未初始化内存)
    • 覆盖相邻变量(如i被修改导致死循环)
    • 段错误(访问非法地址,如arr[1000]

调试技巧

  • 编译选项:-fsanitize=address(AddressSanitizer)在运行时检测越界
  • GDB 断点:在循环内设置断点,观察iarr[i]的值变化
  • Valgrind:用--tool=memcheck检测内存访问错误

3. test2DArray() - 二维数组的内存布局与访问

void test2DArray() {
    int arr[2][3] = {{12, 3, 4}, {12, 22, 33}};
    printf("\n二维数组演示:\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("arr[%d][%d]=%d\t", i, j, arr[i][j]);
        }
        printf("\n");
    }
}

内存模型

  • 二维数组在内存中按行优先存储,等价于一维数组{12,3,4,12,22,33}
  • arr[i][j]的地址计算:&arr[0][0] + i*3 + j
  • 行指针与列指针:
    • arr是指向第一行的指针(类型为int (*)[3]
    • arr[i]是指向第 i 行首元素的指针(类型为int*

传参陷阱

  • 函数参数必须指定列数:void func(int arr[][3], int rows)
  • 若列数不确定,需用动态二维数组(二级指针或数组指针)

4. printArray(int arr[], int size) - 数组作为函数参数

void printArray(int arr[], int size) {
    printf("\n数组作为参数传递:\n");
    for (int i = 0; i < size; i++) {
        printf("arr[%d]=%d\n", i, arr[i]);
    }
}

参数退化

  • int arr[]等价于int* arr,数组名退化为指针
  • 丢失数组长度信息,必须显式传递size参数
  • sizeof(arr)的区别:函数内sizeof(arr)返回指针大小(通常 8 字节)

替代方案

  • 用结构体封装数组和长度:

    typedef struct {
        int *data;
        size_t size;
    } Array;
    void printArray(Array arr) { ... }
    
  • 用宏计算长度:#define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0]))

5. bubbleSort(int arr[], int size) - 冒泡排序与优化

void bubbleSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

算法分析

  • 时间复杂度:O (n²),稳定排序
  • 优化点:添加有序标记,提前终止循环

    void optimizedBubbleSort(int arr[], int size) {
        int swapped;
        for (int i = 0; i < size - 1; i++) {
            swapped = 0;
            for (int j = 0; j < size - i - 1; j++) {
                if (arr[j] > arr[j + 1]) {
                    swap(&arr[j], &arr[j + 1]);
                    swapped = 1;
                }
            }
            if (!swapped) break; // 已排序,提前退出
        }
    }
    

工业应用

  • 小规模数据排序(n<100)
  • 部分有序数据的适应性排序

6. selectionSort(int arr[], int size) - 选择排序的实现

void selectionSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < size; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

算法特性

  • 时间复杂度:O (n²),不稳定排序
  • 比较次数:固定为 n (n-1)/2 次,交换次数最多 n-1 次
  • 内存访问模式:每次选择最小值需遍历未排序部分,缓存不友好

适用场景

  • 交换成本高的场景(如链表排序)
  • 数据规模较小且对稳定性无要求的排序

7. reverseArray(int arr[], int size) - 数组逆序的双指针技巧

void reverseArray(int arr[], int size) {
    printf("\n数组逆序前:\n");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    
    int start = 0;
    int end = size - 1;
    while (start < end) {
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
    
    printf("\n数组逆序后:\n");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

算法分析

  • 时间复杂度:O (n/2),空间复杂度 O (1)
  • 双指针技巧:首尾指针向中间移动,每次交换元素
  • 边界条件:奇数长度数组中间元素无需交换(start < end而非start <= end

扩展应用

  • 字符串反转:reverseString(char* str)
  • 链表反转:需修改指针指向关系
  • 旋转数组:先整体反转,再局部反转

     

8. zhuanzhi(int arr[3][3]) - 矩阵转置的内存操作

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;
        }
    }
}

实现要点

  • 只遍历上三角:避免重复交换(如arr[0][1]arr[1][0]被交换两次)
  • 原地转置:无需额外空间,时间复杂度 O (n²)
  • 非方阵转置:需创建新矩阵,无法原地操作

内存优化

  • 缓存优化:按行优先遍历,减少缓存失效
  • 分块转置:大规模矩阵按块处理,提高缓存命中率

9. dy_alloc_mem() - 动态内存分配与性能测试

static void dy_alloc_mem() {
    int *arr1 = (int *)malloc(1000000 * sizeof(int));
    if (arr1 == NULL) {
        printf("内存分配失败!\n");
        return;
    }
    for (int i = 0; i < 1000000; i++) {
        *(arr1 + i) = i;
    }
    clock_t start, end;
    start = clock();
    int i;
    for (i = 0; i < 100; i++) {
        printf("%d \n", *(arr1 + i));
    }
    end = clock();
    double t = (double)(end - start);
    printf("打印这些%d用了%10f毫秒\n", i, t);
}

内存管理

  • malloc返回 void*,需显式转换类型(C++ 要求,C 可选)
  • 内存分配失败返回 NULL,必须检查返回值
  • clock()计算 CPU 时间,gettimeofday()获取更精确的挂钟时间

性能分析

  • 内存访问模式:顺序访问,缓存友好
  • 预取优化:现代 CPU 自动预取连续内存,加速遍历
  • 内存碎片:频繁分配释放可能导致堆碎片,可用内存池解决

10. string_reverse(char *str, int len) - 字符串反转的指针操作

void string_reverse(char *str, int len) {
    char *start = str;
    char *end = start + len - 1;
    while (start < end) {
        char temp = *start;
        *start = *end;
        *end = temp;
        start++;
        end--;
    }
}

指针技巧

  • 指针运算:end = start + len - 1定位到字符串末尾
  • 解引用操作:*start*end交换字符
  • 双指针相向移动:避免使用索引,提高效率

安全考虑

  • 传入长度参数避免strlen重复计算
  • 需确保len不超过字符串实际长度(不含 '\0')
  • 处理多字节字符(如 UTF-8)需特殊处理,避免截断

11. insert_atFirst(struct Node *head, int data) - 链表头插法

struct Node *insert_atFirst(struct Node *head, int data) {
    struct Node *n_node = (struct Node *)malloc(sizeof(struct Node));
    n_node->data = data;
    n_node->next = head;
    return n_node;
}

数据结构设计

  • 节点包含数据域和指向下一节点的指针
  • 头插法时间复杂度 O (1),无需遍历链表
  • 返回新头指针,调用者需更新头指针

内存管理

  • 节点创建必须用malloc动态分配
  • 避免内存泄漏:不再使用的节点需free
  • 线程安全:多线程环境需加锁保护插入操作

12. quick_Sort(int arr[], int low, int high) - 快速排序的递归实现

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);
    }
}

算法分析

  • 分治策略:通过分区将数组分为两部分,递归排序
  • 时间复杂度:平均 O (n log n),最坏 O (n²)(有序数组)
  • 空间复杂度:递归栈深度 O (log n)(平均)

优化点

  • 三数取中法选择枢轴:int pivot = median(arr[low], arr[mid], arr[high])
  • 小规模数据用插入排序(n<10)
  • 尾递归优化:先处理较小分区,减少栈深度

13. connect_char(char* a, char* b) - 字符串拼接的指针遍历

void connect_char(char* a, char* b) {
    char* p1 = a + strlen(a);
    printf("这是实验18里面的callback,a的长度是:%d ,b的长度是:%d\n", strlen(a), strlen(b));

    while(*b!='\0'){
        *p1 = *b;
        p1++;b++;
    }
    *p1 = '\0';
}

实现细节

  • strlen(a)计算原字符串长度,O (n) 时间复杂度
  • p1指向a的末尾,逐字符复制b
  • 手动添加终止符\0

安全隐患

  • 未检查目标缓冲区大小,可能导致溢出
  • a空间不足,建议返回错误码而非静默失败
  • 改进版本:int connect_safe(char* a, char* b, size_t max_len)

14. initStack(struct stack* s) - 栈的初始化

void initStack(struct stack* s) {
    s->top = -1;
}

数据结构

  • 顺序栈用数组实现,top为栈顶索引
  • 初始化为 - 1 表示空栈
  • 入栈操作:s->data[++(s->top)] = data
  • 出栈操作:return s->data[(s->top)--]

边界条件

  • 空栈判断:top == -1
  • 栈满判断:top == maxL - 1(原代码错误为top == maxL

15. findKthlarge(int a[], int left, int right, int k) - 第 k 大元素查找

int findKthlarge(int a[], int left, int right, int k) {
    if (left == right)
        return a[left];

    int pivot = partition(a, left, right);
    int rank = pivot - left + 1;

    if (rank == k)
        return a[pivot];
    else if (rank > k)
        return findKthlarge(a, left, pivot - 1, k);
    else
        return findKthlarge(a, pivot + 1, right, k - rank);
}

算法思想

  • 基于快速排序的分区思想,每次排除一半元素
  • 时间复杂度:平均 O (n),最坏 O (n²)
  • 空间复杂度:递归栈深度 O (log n)

优化方向

  • 随机化分区:随机选择枢轴,减少最坏情况概率
  • 尾递归优化:用迭代替代递归,降低栈溢出风险
  • 应用场景:Top-K 问题、中位数查找

五、我自己的一点小总结-献丑了

1 核心知识点与易错点总结表格

知识模块核心知识点典型代码示例易错点 / 关键提醒
数组基础一维 / 二维数组初始化、越界风险testArrayInitialization()- 隐式补零规则:未初始化元素默认 0,但局部数组需显式初始化
- 二维数组传参必须指定列数,否则退化为一级指针
指针深度指针与数组等价性、多级指针、void 指针arr1_ptr()Node.children- 数组名作为参数时退化为指针,必须搭配长度参数
- void 指针需强转类型解引用,避免内存对齐错误
动态内存malloc/free 配对、内存性能测试dy_alloc_mem()- 忘记检查 malloc 返回 NULL 导致程序崩溃
- 大块内存分配用 realloc 扩容,避免重复申请释放
排序算法冒泡 / 选择 / 快排实现与优化bubbleSort()quick_Sort()- 快排枢轴选择不当导致 O (n²),建议用三数取中法
- 冒泡排序优化:添加有序标记提前终止循环
数据结构链表头插法、顺序栈临界条件处理insert_atFirst()popStack()- 链表插入后未更新头指针导致内存泄漏
- 栈满判断应为top == maxL-1,而非maxL
字符串处理指针遍历反转、安全拼接string_reverse()connect_char()- 手动拼接未校验缓冲区大小导致溢出,需用snprintf替代
- 多字节字符反转需按编码单元处理,避免截断
调试工具AddressSanitizer、Valgrind、GDB无(贯穿代码优化)- 数组越界用-fsanitize=address检测
- 内存泄漏用valgrind --tool=memcheck定位

2、我的真实感悟与给新手的肺腑之言

记得刚开始学指针的时候,总觉得这玩意儿既神秘又酷炫。看着老师在黑板上写int *p = &a;,觉得指针不过是存个地址而已,能有多难?直到自己写第一个链表程序,因为没给新节点的next指针赋NULL,程序一运行就卡在遍历那里,Debug 了整整 3 个小时 —— 当时对着黑屏的终端疯狂敲键盘,差点把笔记本砸了的心都有。后来才明白,野指针就像埋在代码里的地雷,你以为只是忘了初始化一个指针,结果它能让整个程序炸得粉碎。现在我写代码时,只要定义指针,第一反应就是给它赋值NULL或者指向合法内存,这习惯都是当年被段错误逼出来的。
 

3、给新手的 5 条实操建议

  1. 动手比看视频更重要
    别光看华清老师演示代码,自己敲一遍并故意制造错误(如越界、野指针),观察程序反应。我在学习指针时,故意写了int *p = NULL; *p = 1;,程序直接崩溃,从此记住空指针解引用的危害。

  2. 用项目驱动学习
    别停留在写单个函数,尝试做个小项目(如学生管理系统):

    • 用数组存储学生信息
    • 用链表实现课程选修关系
    • 用快排对成绩排序
      遇到问题再查资料,比单纯刷题印象深 10 倍。
  3. 掌握 3 个调试工具

    • gcc -Wall -Wextra:开启警告,很多隐藏错误会暴露(如未初始化变量)
    • gdb:学会设置断点、查看变量、回溯栈帧
    • valgrind:每周至少用一次检测内存问题,养成良好习惯
  4. 建立错题本
    把每次遇到的错误分类记录,比如:

    • 野指针:5 次(链表插入未初始化 next)
    • 内存泄漏:3 次(动态数组忘记 free)
    • 越界访问:7 次(循环条件写反)
      每周复习一次,避免重复踩坑。
  5. 别怕问问题,但先学会自己解决
    遇到问题先自己查资料、调试,卡 3 小时再问人。我曾被链表反转卡住,查了 2 小时资料,尝试了 3 种写法后终于成功,从此再也没忘记双指针反转的逻辑。

4 给新手的真心话:多练多试错!

C 语言难吗?难。别怕写烂代码,别怕遇到 bug。每一个指针错误都是提醒你更深入理解内存的机会,每一次内存泄漏都是让你学会对资源负责的教训。
记得多动手敲代码,多调试,多记录自己的错误就完事儿!

附录:5千行手写代码合集
 

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// D9:多级指针、void指针、const
typedef struct Node
{
    int value;
    struct Node **children; // #self 多级指针:指向结构体指针的数组
    int child_count;
} Node;

void add_child(Node *parent, Node *child)
{
    // realloc函数,动态扩展函数 !!!vip 节省空间
    parent->children = realloc(parent->children, (parent->child_count + 1) * sizeof(Node *)); // #self vip 动态扩展二级指针数组
    parent->children[parent->child_count++] = child;
}

void *arrtoPtrInt(void *x)
{                           // #self vip void指针通用类型转换
    return (char *)(x) + 2; // #self 指针运算:char*偏移2字节
}

// D10:函数的基本用法及传参
void printArr(int arr[], int len)
{ // #self 数组传参退化为指针
    for (int i = 0; i < len; i++)
        printf("元素%d的值: %d\n", i, arr[i]);
}

// D11:函数的传参
void arrSort(char *arr[], int n)
{ // #self 指针数组排序:字符指针比较
    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;
            }
}

// D12:指针函数
int *dy_alloc(int n)
{ // #self vip 指针函数返回动态数组
    int *res = (int *)malloc(n * sizeof(int));
    for (int i = 0; i < n; i++)
        res[i] = i;
    return res;
}

// D14:#define和typedef
#define ARRAY_LEN(arr) (sizeof(arr) / sizeof(arr[0])) // #self 宏定义计算数组长度

// D15:变量的作用域和生命周期
// (原代码未体现,保留为示例位置)

// D16:字符串处理函数
int getStrLen(char *a)
{ // #self 字符指针遍历
    char *p = a;
    while (*p != '\0')
        p++;
    return p - a; // #self 指针差值计算长度
}

char *strCpyFn(char *dest, char *src)
{ // #self 指针函数:返回目标字符串指针
    char *result = dest;
    while (*src)
        *dest++ = *src++;
    *dest = '\0';
    return result;
}

int main()
{
    // D9:多级指针测试
    Node root = {1, NULL, 0};
    Node child1 = {2, NULL, 0};
    add_child(&root, &child1); // #self 多级指针添加子节点
    printf("节点%d的子节点值: %d\n", root.value, root.children[0]->value);

    // D9:void指针测试
    char arr[] = {'a', 'b', 'c', 'd'};
    char *ptr = arrtoPtrInt(arr);
    printf("void指针偏移后的值: %c\n", *ptr);

    // D10:函数传参测试
    int arr1[] = {1, 2, 3, 4, 5};
    printArr(arr1, ARRAY_LEN(arr1)); // #self 使用宏定义计算数组长度

    // D11:指针数组排序
    char *arr6[] = {"apple", "banana", "cherry"};
    arrSort(arr6, 3);
    printf("排序后的字符串: %s, %s, %s\n", arr6[0], arr6[1], arr6[2]);

    // D12:指针函数测试
    int *dyn_arr = dy_alloc(5);
    printf("动态分配数组: %d, %d, %d, %d, %d\n", dyn_arr[0], dyn_arr[1], dyn_arr[2], dyn_arr[3], dyn_arr[4]);
    free(dyn_arr);

    // D16:字符串处理测试
    char src[] = "Hello";
    char dest[10];
    strCpyFn(dest, src);
    printf("字符串拷贝结果: %s (长度: %d)\n", dest, getStrLen(dest));

    // #self ai分析 !!!vip  进度:截至25年5月20号
    /* 目录逻辑:基础→指针→函数/数据→预处理/内存→字符串/算法
     * D1-D7:二维数组、指针基础、字符串操作
     * D8-D9:指针数组、多级指针、void/const
     * D10-D13:函数传参、指针函数、递归/回调
     * D14-D15:宏/类型定义、变量作用域
     * D16:字符串算法(KMP等)
     */
    /* 已覆盖:
     * - 指针操作(一/二级指针、动态数组)
     * - 内存管理(malloc/realloc)
     * - 数据结构(链表、栈、快排)
     * - 函数传参(指针传参、简单函数指针)
     */

    /* 未覆盖但必需:
     * 1. 指针:三维数组、void指针、const限定
     * 2. 回调:函数指针数组、异步回调
     * 3. 内存:Valgrind检测、内存池设计
     * 4. 预处理:带副作用宏、条件编译
     * 5. 数据结构:二叉树、哈希表
     * 6. 算法:递归转迭代、双指针技巧
     * 7. 标准库:qsort/bsearch、安全字符串函数
     */

    /* 大厂高频考点:
     * - 野指针、内存对齐
     * - 缓冲区溢出防范
     * - 段错误调试技巧
     */

    //  #self
    // 所有知识点详细目录:

    // 1二维数组的定义及初始化试听
    // 二维数组的应用案例试听
    // 二维数组应用-矩阵转置试听
    // 二维数组应用-杨辉三角形试听
    // 今日作业

    // 2字符数组和字符串试听
    // 字符数组和字符串-应用案例试听
    // 二维字符数组试听
    // 字符串输入输出函数

    // 3指针的基本用法
    // 指针的作用及学习方法05'24"
    // 内存、地址、变量08'27"
    // 指针和指针变量13'11"
    // 指针的目标和“解引用”08'45"
    // 指针的赋值14'25"
    // 指针的大小11'36"
    // 空指针10'05"
    // 野指针及成因16'12"
    // 野指针的危害是什么11'28"
    // 如何避免野指针

    // 4指针的运算
    // 指针的算术运算17'48"
    // 指针的算术运算-自增自减08'31"
    // 指针算术运算符-自增自减应用109'07"
    // 指针算术运算符-自增自减应用204'00"
    // 指针的算术运算-应用08'32"
    // 指针的算术运算-注意事项12'22"
    // 指针的关系运算

    // 5指针与数组
    // 指针与数组基本用法11'57"
    // 指针与数组使用注意事项09'49"
    // 指针与数组相关笔试题

    // 6指针与二维数组
    // 一级指针与二维数组07'57"
    // 数组指针与二维数组10'06"
    // 数组指针应用12'33"
    // 数组指针笔试题

    // 7字符指针与字符串
    // 字符指针与字符串基本用法09'15"
    // 字符指针与字符串常量08'34"
    // 字符指针与字符串应用

    // 8指针数组
    // 指针数组的基本用法14'06"
    // 指针数组与二维数组11'41"
    // 数组指针和指针数组的区别

    // 9多级指针 void指针 const
    // 多级指针基本用法10'36"
    // 多级指针的运算13'45"
    // 多级指针和指针数组06'09"
    // void指针及用法11'38"
    // const变量、const指针14'31"
    // main函数的参数

    // 10函数的基本用法及传参
    // 函数的基本用法10'44"
    // 函数的调用07'29"
    // 函数的声明07'16"
    // 函数的应用08'34"
    // 函数传参-全局变量06'01"
    // 函数的参数传递-值传递07'34"
    // 函数的参数传递-值传递 -应用12'40"
    // 函数的参数传递-指针传递

    // 11函数的传参
    // 函数传参-应用-字符串统计14'17"
    // 一维数组在函数间传参14'26"
    // 一维数组在函数间传参-总结04'00"
    // 字符数组传参09'28"
    // 二维数组在函数间传参-一级指针05'47"
    // 二维数组在函数间传参 -行指针05'39"
    // 二维数组在函数间传参 -

    // 12指针函数
    // 指针函数及用法11'44"
    // 指针函数的返回值08'30"
    // 指针函数-编写字符串拷贝函数13'17"
    // 指针函数-编写字符串连接函数11'44"
    // 指针函数案例

    // 13递归函数和函数指针
    // 递归函数及用法09'54"
    // 递归函数应用04'37"
    // 函数指针的基本用法16'07"
    // 函数指针数组04'35"
    // qsort函数的用法

    // 14#define 和 typedef
    // 预处理指令#define09'58"
    // 没有值的宏定义15'34"
    // 宏定义和 const 常量区别04'57"
    // typedef的用法15'45"
    // #define和typedef的比较06'27"
    // 宏函数的使用

    // 15变量的作用域和生命周期
    // 变量存储类型-auto04'03"
    // 变量存储类型-register07'03"
    // 变量存储类型-static05'40"
    // 变量存储类型-extern

    // 16字符串处理函数
    // 字符串函数上11'28"
    // 字符串函数下08'48"
    // strncpy函数09'12"
    // strncat函数04'38"
    // strncmp函数04'46"
    // strchr函数04'09"
    // strstr函数


    

    return 0;
}















#include <stdio.h>
#include <stdlib.h>

// 转置 动态分配内存、k大算法->脱胎与快排法


// 转置矩阵函数
void zhuanzhi()
{
    // 定义并初始化一个3x3的矩阵
    int a[][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 ", a[i][j]);
        }
        printf("\n");
    }

    // 转置矩阵
    printf("现在开始转置:\n");
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < i; j++)
        {
            int temp = a[i][j];
            a[i][j] = a[j][i];
            a[j][i] = temp;
        }
    }

    // 输出转置后的矩阵
    printf("转置之后:\n");
    for (int i = 0; i < 3; i++)
    {
        for (int j = 0; j < 3; j++)
        {
            printf("%d ", a[i][j]);
        }
        printf("\n");
    }
}

// 动态分配一维数组函数
int *alloc_1dArr()
{
    // 分配100个整数的内存空间
    int *arr = (int *)malloc(100 * sizeof(int));

    // 初始化数组元素
    for (int i = 0; i < 100; i++)
    {
        arr[i] = i;
    }

    return arr;
}

// 动态分配二维数组函数
int **alloc_2dArr(int row, int col)
{
    // 分配row个指针的内存空间,用于存储每一行的地址
    int **a1 = (int **)malloc(row * sizeof(int *));

    // 为每一行分配col个整数的内存空间,并初始化元素
    for (int i = 0; i < row; i++)
    {
        a1[i] = (int *)malloc(col * sizeof(int));
        for (int j = 0; j < col; j++)
        {
            a1[i][j] = i * col + j;
        }
    }

    // 输出二维数组
    printf("动态分配的二维数组:\n");
    for (int i = 0; i < row; i++)
    {
        for (int j = 0; j < col; j++)
        {
            printf("%4d ", a1[i][j]);
        }
        printf("\n");
    }

    return a1;
}

// 交换两个整数的函数
void swap(int *a, int *b)
{
    int temp = *a;
    *a = *b;
    *b = temp;
}

// 分区函数(移除冗余的k参数)
int partition(int a[], int left, int right)
{
    int pivot = a[right]; // 选择最右元素作为枢轴
    int i = left - 1;     // 标记大于枢轴区域的末尾

    for (int j = left; j <= right - 1; j++)
    {
        if (a[j] > pivot)
        {                       // 降序分区:左侧元素 > 枢轴
            i++;                // 扩大左侧区域
            swap(&a[i], &a[j]); // 交换到正确位置
        }
    }
    swap(&a[i + 1], &a[right]); // 枢轴归位
    return i + 1;               // 返回枢轴索引(第rank大元素的位置)
}

// 查找第k大元素函数
int findKthlarge(int a[], int left, int right, int k)
{
    if (left == right)
        return a[left];

    int pivot = partition(a, left, right);
    int rank = pivot - left + 1; // 计算枢轴排名

    if (rank == k)
    { // 找到第k大
        return a[pivot];
    }
    else if (rank > k)
    { // 左半部分查找
        return findKthlarge(a, left, pivot - 1, k);
    }
    else
    { // 右半部分查找,调整k
        return findKthlarge(a, pivot + 1, right, k - rank);
    }
}

int main(void)
{
    // 标记当前是实验1
    printf("实验1:矩阵转置\n");
    zhuanzhi();

    // 标记当前是实验2
    printf("\n实验2:动态分配一维数组\n");
    int *a1 = alloc_1dArr();
    for (int i = 0; i < 100; i++)
    {
        printf("%d ", a1[i]);
    }
    printf("\n");

    // 标记当前是实验3
    printf("\n实验3:动态分配二维数组\n");
    int m = 10, n = 15;
    int **a2 = alloc_2dArr(m, n);

    // 标记当前是实验4
    printf("\n实验4:找到第k大的数字\n");
    int arr4[] = {34, 78, 12, 56, 89, 23, 45, 67, 90, 11};
    int len4 = sizeof(arr4) / sizeof(arr4[0]);
    printf("这里的arr4的长度是:%d\n", len4);
    int k = 8;
    int res4 = findKthlarge(arr4, 0, len4 - 1, k);
    printf("数组arr4的元素:\n");
    for (int i = 0; i < len4; i++)
    {
        printf("%d ", arr4[i]);
    }
    printf("\n第k大(第4大)的数字就是:%d\n\n", res4);

    // 实验5:指针遍历二维数组
    // 这里可以添加一个新的函数来演示指针遍历二维数组

    // 实验6

    // 实验7

    return 0;
}




























// #self init statement
// 这里说明清楚:原来的代码有五百行
// 自己根据AI和华清园圈的课程写的现在底薪的一部分是AI重新给我优化生成的但是
// 之前写的我也不会给他删掉做一个备份?

// #include <stdio.h>
// #include <string.h>
// #include <stdlib.h>
// #include <time.h>

// // 指针访问数组中的元素
// void arr1_ptr()
// {
//     int arr[] = {1, 2, 4, 5, 6, 56, 67, 8, 9};
//     int *ptr = arr;
//     int len = sizeof(arr) / sizeof(arr[0]);
//     for (int i = 0; i < len; i++)
//     {
//         printf("指针访问:%d,数组访问:%d\n ", *(ptr + i), arr[i]);
//     }
// }

// // 转置操作
// // #self 数组没办法获取数组大小 :sizeof(arr)这个会失效!!!
// 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;
//         }
//     }
// }

// static void dy_alloc_mem()
// {
//     int *arr1 = (int *)malloc(1000000 * sizeof(int));
//     if (arr1 == NULL)
//     {
//         printf("内存分配失败!\n");
//         return;
//     }
//     for (int i = 0; i < 1000000; i++)
//     {
//         *(arr1 + i) = i;
//     }
//     // 时间戳1
//     clock_t start, end;
//     start = clock();
//     int i;
//     for (i = 0; i < 100; i++)
//     {
//         printf("%d \n", *(arr1 + i));
//     }
//     end = clock();
//     double t = (double)(end - start);
//     printf("打印这些%d用了%10f毫秒\n", i, t);
//     //
//     // 时间戳2
// }

// void string_reverse(char *str, int len)
// {

//     char *start = str;
//     char *end = start + len - 1;
//     while (start < end)
//     {
//         char temp = *start;
//         *start = *end;
//         *end = temp;
//         start++;
//         end--;
//     }
// }

// int add(int a, int b)
// {
//     return a + b;
// }

// int substract(int a, int b)
// {
//     return a - b;
// }

// struct Person
// {

//     char name[10];
//     int age;
// };

// struct Node
// {
//     int data;
//     struct Node *next;
// };

// struct Node *insert_atFirst(struct Node *head, int data)
// {
//     // self
//     // vip!!!
//     //  在末尾插入!
//     struct Node *n_node = (struct Node *)malloc(sizeof(struct Node));
//     n_node->data = data;
//     n_node->next = head;
//     return n_node;
// }

// void print_List(struct Node *node)
// {
//     struct Node *l = node;
//     while (l != NULL)
//     {
//         printf("这是打印的节点:!!! %d \n", l->data);
//         l = l->next;
//     }
//     return;
// }

// void swap(int *a, int *b)
// {
//     int temp = *a;
//     *a = *b;
//     *b = temp;
// }

// int cnt11 = 0;

// 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]);
//     printf("\n\n---现在是partition%d次调用\n,pi是%d\n", cnt11++, pi);

//     for (int i = 0; i < high; i++)
//     {
//         printf(" %d-", arr[i]);
//     }
//     printf("---现在是partition%d次调用\n\n", cnt11++);

//     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);
//     }
// }

// struct Address{
//     char street[50];
//     int house_number;
// };

// struct Person_16{
//     char name[20];
//     struct Address address;
// };

// void Print_address(struct Person_16* p){
//     printf("%s \n",p->name);
//     printf("%s \n",p->address.street);
//     printf("%d \n",p->address.house_number);
// }

// //#self vip !!!!
// //不自己写 根本就不知道有这么多不懂的内容!
// void connect_char(char* a,char* b){
//     char* p1 = a + strlen(a);
//     printf("这是实验18里面的callback,a的长度是:%d ,b的长度是:%d\n",strlen(a),strlen(b));

//     // for(int i=0;i<strlen(b);i++){
//     //     a[i] = b[i];

//     // }
//     while(*b!='\0'){
//         *p1 = *b;
//         p1++;b++;
//     }
//     *p1 = '\0';
// }

// #define maxL 100

// struct stack{
//     int data[maxL] ;
//     int top ;
// };

// void initStack(struct stack* s ){
//     s->top=-1;
// }

// int isEmpty(struct stack* s ){
//     if(s->top==-1){
//         return 1;
//     }
//     else{
//         return -100;
//     }
// }

// int isFull(struct stack* s ){
//     if(s->top==maxL){
//         return 1;
//     }
//     else{
//         return 0;
//     }
// }

// void pushStack(struct stack* s ,int data ){
//     //判断栈是不是满了?
//     if(s->top==maxL){
//         return  ;
//     }
//     else{
//         s->data[++(s->top)] = data;
//     }
// }

// int  popStack(struct stack* s ){
//     //判断是不是空?空了就返回-1
//     if(isEmpty(s)){
//         return -1;
//     }
//     else{
//         int res  = s->data[s->top--];
//     }
// }

// int peekTop(struct stack* s ){
//     return s->data[s->top];
// }

// int main()
// {

//     // 访问数组元素
//     // arr1_ptr();
//     // printf("1 访问数组元素结束!\n");

//     // // 多维数组的内存布局
//     // // 把一个二维数组转置操作一下
//     // // #self 数组没办法获取数组大小 :sizeof(arr)这个会失效!!!
//     // int arr[3][3] = {
//     //     {1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
//     // zhuanzhi(arr);
//     // for (int i = 0; i < 3; i++)
//     // {
//     //     for (int j = 0; j < 3; j++)
//     //     {
//     //         printf("%d ", arr[i][j]);
//     //     }
//     //     printf("\n");
//     // }
//     // printf("\n\n实验转置操作\n");

//     // // 动态分配内存打印
//     // printf("第3实验开始!\n");
//     // dy_alloc_mem();
//     // printf("第3实验结束!\n");

//     // printf("\n\n第四个实验开始\n\n");
//     // char charS[100] = {'0', '1', '2', '3', '4', '5', '\0'};
//     // int char_len = strlen(charS);
//     // printf("这个字符数组的长度:%d \n", char_len);
//     // printf("但是强行从0-100打印就是:\n");
//     // for (int i = 0; i < 5; i++)
//     // {
//     //     printf("%c \n", charS[i]);
//     // }

//     // string_reverse(charS, char_len);
//     // for (int i = 0; i < char_len; i++)
//     // {
//     //     printf("%c \n", charS[i]);
//     // }
//     // printf("第4个实验结束\n\n");

//     // // 第5个实验:函数指针
//     // // 手动实现一个计算器
//     // printf("\n第5个实验开始\n\n");
//     // int a = 100, b = 50;
//     // int (*func_ptr)(int a, int b);

//     // printf("告诉我你要的操作:\n");
//     // char op;
//     // scanf("%c", &op);
//     // switch (op)
//     // {
//     // case '+':
//     //     func_ptr = add;
//     //     {
//     //         int res = func_ptr(a, b);
//     //         printf("%d\n", res);
//     //         break;
//     //     }
//     // case '-':
//     //     func_ptr = substract;
//     //     {
//     //         int res = func_ptr(a, b);
//     //         printf("%d\n", res);
//     //         break;
//     //     }
//     // }
//     // printf("\n第5个实验结束\n\n");

//     // printf("\n第六个实验,二维数组动态分配\n");

//     // int m =3,n =4 ;
//     // int **arr_6 =(int**)malloc(m*sizeof(int*));
//     // for(int i= 0;i<m;i++){
//     //     arr_6[i] = (int*)malloc(n*sizeof(int));
//     // }
//     // for(int i =0;i<m;i++){
//     //     for(int j =0;j<n;j++){
//     //         arr_6[i][j] = i*n+j;
//     //         printf("\n%d = %d 行 %d列",arr_6[i][j],i,j);
//     //     }
//     //     printf("\n");
//     // }
//     // printf("\n第六个实验,二维数组动态分配\n");

//     printf("实验7 指针数组用法\n");
//     char *string[] = {"jibamao", "abc", "123", "why", "你是神秘嘉宾", "\0"};
//     int len7 = sizeof(string) / sizeof(string[0]);
//     for (int i = 0; i < len7; i++)
//     {
//         printf("%s\n", string[i]);
//     }
//     char string7[] = "123123";

//     printf("%d\n", sizeof(string7));
//     for (int i = 0; i < sizeof(string7); i++)
//     {
//         printf("%c", string7[i]);
//     }
//     // #self vip
//     // 特别重要 字符串数组和字符指针数组

//     // 结构体指针8 实验8 结构体修改结构体内值
//     //
//     struct Person person = {"xiaomin", 18};
//     printf("\n%s-%d\n", person.name, person.age);

//     struct Person *p = &person;
//     strcpy(p->name, "jibamao");
//     // p->name = "jibamao";
//     printf("改过之后:%s\n", p->name);
//     // #self vip!
//     // 结构体指针的作用

//     // #self
//     // 实验9 链表节点的插入
//     printf("\n\n实验9 链表的插入\n");
//     struct Node *head_node = NULL;
//     // struct Node node = {123,NULL};
//     head_node = insert_atFirst(head_node, 123);
//     printf("\n第一个节点:%d\n  ", head_node->data);

//     head_node = insert_atFirst(head_node, 4);
//     printf("\n第二个节点 : %d\n", head_node->data);
//     print_List(head_node);

//     // #self  vip!!!
//     // 实验10 冒泡排序优化
//     printf("实验10 冒泡排序优化!\n");

//     // 11 快排法
//     // #self vip!!!
//     printf("\n\n实验11 快速排序!!开始!!!\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("快排打印的结果是");
//         printf("%d\n", arr11[i]);
//     }
//     printf("\n\n实验11 快排结束!!!\n");

//     // 13实验 文件读写操作
//     //

//     // 14整数阶乘

//     // 15指针数组与二维数组区别:
//     // #self vip
//     printf("\n\n实验15开始 \n\n");
//     char *str15[] = {"ab", "123", "efg", "jkl"};
//     char str15new[3][10] = {"baname", "apple", "milk"};
//     int len1 = sizeof(strlen(str15))/sizeof(str15[0]);
//     for (int i = 0; i <len1; i++)
//     {
//         printf("%s \n", str15[i]);
//     }
//     printf("\n\n");
//     for (int i = 0; i < strlen(str15new); i++)
//     {
//         printf("%s \n", str15new[i]);
//     }

//     //实验16 结构体嵌套
//     printf("\n\n实验16开始 \n\n");

//     struct Person_16 p16 = {"xiaom",{"123abc",100}};
//     Print_address(&p16);
//     printf("\n\n实验16结束 \n\n");

//     //实验17 位运算实现假发

//     //shyan 18 指针实现字符串拼接
//     //告诉两个指针的地址,直接把连个地址相加即可
//     printf("\n\n实验18开始 ---------\n\n");

//     char char1 []= {'1','2','3','4','5','a','\0'};
//     char char2 []= {'b','9','\0'};
//     connect_char(char1,char2);
//     int len18 = strlen(char1);
//     printf("\n18实验的长度是:%d \n\n",len18);

//     for(int i =0;i<len18;i++){
//         printf("%c \n",char1[i]);
//     }
//     printf("\n\n实验18结束 ---------------\n\n");

//     //实验19
//     //later
//     //检测是否链表有环形结构

//     //实验20
//     //自己实现一个栈结构
//     //#self vip!!!
//     //栈要实现哪些功能?
//     //1 初始化 2入栈  3出栈   4查看栈顶元素  5空了?   6满了?
//     struct stack s;
//     initStack(&s);

//     printf("初始了,是不是空?%d\n",isEmpty(&s));
//     pushStack(&s,1);
//     printf("插入了1,是不是空?\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("栈顶是:%d\n",peekTop(&s));

//     return 0;
// }

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <time.h>

// 指针数组、结构体指针、链表插入、快排、栈

int cnt11 = 0;
// ====================== 提前结构体定义 ======================
struct Person
{
    char name[10];
    int age;
};

struct Node
{
    int data;
    struct Node *next;
};

#define maxL 100
struct stack
{
    int data[maxL];
    int top;
};

// ====================== 函数前置声明 ======================
void arr1_ptr();
void zhuanzhi(int arr[3][3]);
static void dy_alloc_mem();
void string_reverse(char *str, int len);
int add(int a, int b);
int substract(int a, int b);
struct Node *insert_atFirst(struct Node *head, int data);
void print_List(struct Node *node);
void swap(int *a, int *b);
int partition(int arr[], int low, int high);
void quick_Sort(int arr[], int low, int high);
void connect_char(char *a, char *b);
void initStack(struct stack *s);
int isEmpty(struct stack *s);
int isFull(struct stack *s);
void pushStack(struct stack *s, int data);
int popStack(struct stack *s);
int peekTop(struct stack *s);

// ====================== 原始函数实现 ======================
// 指针访问数组中的元素
void arr1_ptr()
{
    int arr[] = {1, 2, 4, 5, 6, 56, 67, 8, 9};
    int *ptr = arr;
    int len = sizeof(arr) / sizeof(arr[0]);
    for (int i = 0; i < len; i++)
    {
        printf("指针访问:%d,数组访问:%d\n ", *(ptr + i), arr[i]);
    }
}

// 转置操作
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;
        }
    }
}

static void dy_alloc_mem()
{
    int *arr1 = (int *)malloc(1000000 * sizeof(int));
    if (arr1 == NULL)
    {
        printf("内存分配失败!\n");
        return;
    }
    for (int i = 0; i < 1000000; i++)
    {
        *(arr1 + i) = i;
    }
    clock_t start, end;
    start = clock();
    int i;
    for (i = 0; i < 100; i++)
    {
        printf("%d \n", *(arr1 + i));
    }
    end = clock();
    double t = (double)(end - start);
    printf("打印这些%d用了%10f毫秒\n", i, t);
}

void string_reverse(char *str, int len)
{
    char *start = str;
    char *end = start + len - 1;
    while (start < end)
    {
        char temp = *start;
        *start = *end;
        *end = temp;
        start++;
        end--;
    }
}

int add(int a, int b)
{
    return a + b;
}

int substract(int a, int b)
{
    return a - b;
}

struct Node *insert_atFirst(struct Node *head, int data)
{
    struct Node *n_node = (struct Node *)malloc(sizeof(struct Node));
    n_node->data = data;
    n_node->next = head;
    return n_node;
}

void print_List(struct Node *node)
{
    struct Node *l = node;
    while (l != NULL)
    {
        printf("这是打印的节点:!!! %d \n", l->data);
        l = l->next;
    }
}

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]);
    printf("\n\n---现在是partition%d次调用\n,pi是%d\n", cnt11++, pi);

    for (int i = 0; i < high; i++)
    {
        printf(" %d-", arr[i]);
    }
    printf("---现在是partition%d次调用\n\n", cnt11++);

    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 connect_char(char *a, char *b)
{
    char *p1 = a + strlen(a);
    printf("这是实验18里面的callback,a的长度是:%d ,b的长度是:%d\n", strlen(a), strlen(b));

    while (*b != '\0')
    {
        *p1 = *b;
        p1++;
        b++;
    }
    *p1 = '\0';
}

void initStack(struct stack *s)
{
    s->top = -1;
}

int isEmpty(struct stack *s)
{
    if (s->top == -1)
    {
        return 1;
    }
    else
    {
        return -100;
    }
}

int isFull(struct stack *s)
{
    if (s->top == maxL)
    { // 注意:这里可能存在越界风险,原始逻辑未修正
        return 1;
    }
    else
    {
        return 0;
    }
}

void pushStack(struct stack *s, int data)
{
    if (s->top == maxL)
    {
        return;
    }
    else
    {
        s->data[++(s->top)] = data;
    }
}

int popStack(struct stack *s)
{
    if (isEmpty(s))
    {
        return -1;
    }
    else
    {
        int res = s->data[s->top--];
        return res; // 原始代码遗漏return
    }
}

int peekTop(struct stack *s)
{
    return s->data[s->top];
}

// 指针数组、结构体指针、链表插入、快排、栈

// ====================== 主函数 ======================
int main()
{
    // 实验7 指针数组用法
    printf("实验7 指针数组用法\n");
    char *string[] = {"qibamao", "abc", "123", "why", "你是神秘嘉宾", "\0"};
    int len7 = sizeof(string) / sizeof(string[0]);
    for (int i = 0; i < len7; i++)
    {
        printf("%s\n", string[i]);
    }
    char string7[] = "123123";
    printf("%d\n", sizeof(string7));
    for (int i = 0; i < sizeof(string7); i++)
    {
        printf("%c", string7[i]);
    }

    // 结构体指针实验
    struct Person person = {"xiaomin", 18};
    printf("\n%s-%d\n", person.name, person.age);
    struct Person *p = &person;
    strcpy(p->name, "jibamao");
    printf("改过之后:%s\n", p->name);

    // 链表实验
    printf("\n\n实验9 链表的插入\n");
    struct Node *head_node = NULL;
    head_node = insert_atFirst(head_node, 123);
    printf("\n第一个节点:%d\n  ", head_node->data);
    head_node = insert_atFirst(head_node, 4);
    printf("\n第二个节点 : %d\n", head_node->data);
    print_List(head_node);

    // 快速排序实验
    printf("\n\n实验11 快速排序!!开始!!!\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]);
    }

    // 栈结构实验
    struct stack s;
    initStack(&s);
    printf("\n初始了,是不是空?%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));

    return 0;
}







































#include <stdio.h>

//数组 、二维数组 、排序算法

// 1. 一维数组初始化与遍历
void testArrayInitialization() {
    int arr1[5] = {1, 2, 3, 4, 5};
    int arr2[5] = {1, 2, 3}; // 自动补0
    int arr3[] = {1, 3, 4, 5, 6}; // 自动计算长度
    
    printf("一维数组初始化测试:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr1[%d]=%d, arr2[%d]=%d, arr3[%d]=%d\n", 
               i, arr1[i], i, arr2[i], i, arr3[i]);
    }
}

// 2. 数组越界演示(危险操作,仅用于教学)
void demonstrateArrayOutOfBounds() {
    int arr[6] = {1, 2, 3, 4, 5, 6};
    printf("越界访问示例(危险!):\n");
    for (int i = 0; i <= 8; i++) { // 注意这里会访问arr[6]导致越界
        printf("arr[%d]=%d\n", i, arr[i]);
    }
}

// 3. 二维数组操作
void test2DArray() {
    int arr[2][3] = {{12, 3, 4}, {12, 22, 33}};
    printf("\n二维数组演示:\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("arr[%d][%d]=%d\t", i, j, arr[i][j]);
        }
        printf("\n");
    }
}

// 4. 数组作为函数参数
void printArray(int arr[], int size) {
    printf("\n数组作为参数传递:\n");
    for (int i = 0; i < size; i++) {
        printf("arr[%d]=%d\n", i, arr[i]);
    }
}

// 5. 冒泡排序
void bubbleSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        for (int j = 0; j < size - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

// 6. 选择排序
void selectionSort(int arr[], int size) {
    for (int i = 0; i < size - 1; i++) {
        int minIndex = i;
        for (int j = i + 1; j < size; j++) {
            if (arr[j] < arr[minIndex]) {
                minIndex = j;
            }
        }
        if (minIndex != i) {
            int temp = arr[i];
            arr[i] = arr[minIndex];
            arr[minIndex] = temp;
        }
    }
}

// 7. 数组逆序
void reverseArray(int arr[], int size) {
    printf("\n数组逆序前:\n");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    
    int start = 0;
    int end = size - 1;
    while (start < end) {
        int temp = arr[start];
        arr[start] = arr[end];
        arr[end] = temp;
        start++;
        end--;
    }
    
    printf("\n数组逆序后:\n");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

int main() {
    // 测试数组初始化
    testArrayInitialization();
    
    // 测试数组越界(注意:实际编程中应避免!)
    demonstrateArrayOutOfBounds();
    
    // 测试二维数组
    test2DArray();
    
    // 测试数组作为参数
    int arr[] = {1, 3, 5, 7, 9};
    printArray(arr, 5);
    
    // 测试排序算法
    int sortArr[] = {5, 3, 8, 4, 2};
    int size = sizeof(sortArr) / sizeof(sortArr[0]);
    
    printf("\n选择排序前:\n");
    printArray(sortArr, size);
    
    selectionSort(sortArr, size);
    
    printf("\n选择排序后:\n");
    printArray(sortArr, size);
    
    // 测试数组逆序
    reverseArray(sortArr, size);
    
    return 0;
}


这还不值得你的关注点赞收藏吗?hhhh

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值