C语言核心语法

C语言核心语法:

基础1:

  • 1.程序结构
    • (1)导入库(头文件)
      • stdio.h 标准输入输出库 (在代码中使用的scanf printf等函数都是这个库中的)
c 复制代码
#include <stdio.h>
int main(){
	printf("Hello World!\n");
	
	int num=0;
	scanf("%d",&num);
	printf("输入的数字为%d",num);
	 
} 

结果:

复制代码
    - stdlib.h 动态内存分配 malloc 数值类型转换
        - malloc 用于在堆区动态分配内存,返回 void* 指针,需要强转为目标类型
        - 数值类型转换:使用 (类型名) 进行强制类型转换,如 (int*)、(double*) 等
c 复制代码
            #include <stdio.h>
            #include <stdlib.h>   // malloc 所在库

            int main() {
                // 1. 动态分配 3 个 int 大小的内存,并强转为 int*
                int *p = (int*)malloc(3 * sizeof(int));
                if (p == NULL) {
                    printf("内存分配失败\n");
                    return 1;
                }

                // 2. 通过指针赋值
                p[0] = 10;
                p[1] = 20;
                p[2] = 30;

                // 3. 打印结果
                for (int i = 0; i < 3; i++) {
                    printf("p[%d] = %d\n", i, p[i]);
                }

                // 4. 释放内存
                free(p);
                return 0;
            }
复制代码
        - 运行结果:

                p[0] = 10
                p[1] = 20
                p[2] = 30

        - 数值类型转换示例:
c 复制代码
                int *ip = (int*)malloc(sizeof(int));       // 分配 int 大小
                double *dp = (double*)malloc(sizeof(double)); // 分配 double 大小
                char *cp = (char*)malloc(sizeof(char));     // 分配 char 大小
复制代码
        - 要点:
            - malloc 返回 void*,必须强转为目标指针类型
            - 分配后要检查是否为 NULL,防止分配失败
            - 使用完必须 free 释放,避免内存泄漏




    - math.h 数学函数库 (提供 sqrt 开方、pow 幂运算、abs 绝对值等数学函数)
c 复制代码
            #include <stdio.h>
            #include <math.h>   // 数学函数所在库

            int main() {
                double x = 9.0;
                double y = 2.0;

                // 1. sqrt 开平方
                printf("sqrt(%.1f) = %.2f\n", x, sqrt(x));

                // 2. pow 幂运算:2 的 3 次方
                printf("pow(%.1f, %.1f) = %.2f\n", y, 3.0, pow(y, 3.0));

                // 3. fabs 浮点数绝对值
                printf("fabs(-5.5) = %.2f\n", fabs(-5.5));

                // 4. ceil 向上取整、floor 向下取整
                printf("ceil(3.2) = %.0f, floor(3.2) = %.0f\n", ceil(3.2), floor(3.2));

                return 0;
            }

运行结果:

复制代码
                sqrt(9.0) = 3.00
                pow(2.0, 3.0) = 8.00
                fabs(-5.5) = 5.50
                ceil(3.2) = 4, floor(3.2) = 3
  • 要点:

    • 使用 math.h 中的函数时,编译需要链接数学库,Linux 下加 -lm 参数

    • sqrt 返回 double 类型,接收时用 double 变量

    • 常用函数:sqrt 开方、pow 幂、fabs 绝对值、ceil 向上取整、floor 向下取整

      复制代码
      - string.h 字符串处理库 (提供 strlen 求长度、strcpy 复制、strcat 拼接、strcmp 比较等函数)
c 复制代码
            #include <stdio.h>
            #include <string.h>   // 字符串函数所在库

            int main() {
                char s1[20] = "Hello";
                char s2[20] = "World";

                // 1. strlen 求字符串长度(不含结尾的 \0)
                printf("strlen(s1) = %d\n", (int)strlen(s1));

                // 2. strcpy 复制:把 s2 复制到 s1
                strcpy(s1, s2);
                printf("strcpy 后 s1 = %s\n", s1);

                // 3. strcat 拼接:把 s2 追加到 s1 末尾
                strcat(s1, "!");
                printf("strcat 后 s1 = %s\n", s1);

                // 4. strcmp 比较:相等返回 0,s1<s2 返回负数,s1>s2 返回正数
                char a[] = "abc";
                char b[] = "abd";
                int cmp = strcmp(a, b);
                printf("strcmp(a, b) = %d\n", cmp);

                return 0;
            }
  • 运行结果:

    复制代码
                  strlen(s1) = 5
                  strcpy 后 s1 = World
                  strcat 后 s1 = World!
                  strcmp(a, b) = -1
  • 要点:

    • strlen 返回的是 size_t 类型,打印时建议强转为 int

    • strcpy 目标数组要足够大,否则会越界

    • strcat 拼接时目标数组要预留足够空间,且以 \0 结尾

    • strcmp 按字典序比较,常用于字符串排序和查找

    • (2)主函数: int main(){ } 程序的入口,在其中可以调用主函数之前定义的常量 函数等
      -2. 输出语句:printf 格式化输出语句
    • 格式: ("输出的内容与对应的占位符",数据参数,...)
  • 3.数据类型与变量

    -(1) 基本类型: 占用空间 与 取值范围

    • 整数型:int long long

    • 浮点型:float double

    • 字符型:char

    • (2)字符串: char 类型的数组,经过了特殊的处理,值可以使用双引号括起来 char xx字符数+1="值";
    • (3)数组: 可以存储多个同类型元素的数据结构 ,在内存中是一整块空间,可以使用下标操作
    • (4)指针: 指针变量用来存储其他变量的内存地址
    • (5)结构体:是自己定义的一个数据类型,可以将多个数据放在一起。比如:学生(姓名 年龄 学号 班级)

结构体:

  • 结构体与面向对象中的类非常相似,它允许我们自定义一种数据类型,将多个不同类型的数据变量组合在一起

  • 创建结构体变量之后,可以通过结构体变量访问并对其中的成员进行赋值等操作

  • 结构体实战示例:定义一个学生结构体,包含姓名、年龄、学号、班级等字段,演示如何创建结构体变量并赋值、打印,以及结构体数组和结构体指针的用法

    c 复制代码
    #include <stdio.h>
    #include <string.h>
    // 1. 定义结构体类型 Student
    struct Student {
        char name[20];      // 姓名
        int age;            // 年龄
        int id;             // 学号
        char class[20];     // 班级
    };
    int main() {
        // 2. 创建结构体变量并赋值
        struct Student stu1;
        strcpy(stu1.name, "张三");
        stu1.age = 18;
        stu1.id = 1001;
        strcpy(stu1.class, "计算机1班");
        // 3. 通过结构体变量访问成员并打印
        printf("姓名:%s\n", stu1.name);
        printf("年龄:%d\n", stu1.age);
        printf("学号:%d\n", stu1.id);
        printf("班级:%s\n", stu1.class);
        // 4. 结构体数组:存储多个学生
        struct Student stuArr[3] = {
            {"李四", 19, 1002, "计算机1班"},
            {"王五", 20, 1003, "计算机2班"},
            {"赵六", 18, 1004, "计算机2班"}
        };
        // 5. 遍历结构体数组
        printf("\n--- 结构体数组遍历 ---\n");
        for (int i = 0; i < 3; i++) {
            printf("第%d个学生:%s,%d岁,学号%d,%s\n",
                   i + 1, stuArr[i].name, stuArr[i].age,
                   stuArr[i].id, stuArr[i].class);
        }
        // 6. 结构体指针:通过指针访问成员
        struct Student *p = &stu1;
        printf("\n--- 结构体指针访问 ---\n");
        printf("通过指针访问姓名:%s\n", p->name);
        printf("通过指针访问年龄:%d\n", p->age);
        // 7. 通过指针修改成员
        p->age = 19;
        printf("修改后年龄:%d\n", stu1.age);
        return 0;
    }
    • 运行结果:

      复制代码
      姓名:张三
      年龄:18
      学号:1001
      班级:计算机1班
      --- 结构体数组遍历 ---
      第1个学生:李四,19岁,学号1002,计算机1班
      第2个学生:王五,20岁,学号1003,计算机2班
      第3个学生:赵六,18岁,学号1004,计算机2班
      --- 结构体指针访问 ---
      通过指针访问姓名:张三
      通过指针访问年龄:18
      修改后年龄:19
    • 要点总结:

      • 结构体用 struct 结构体名 { 成员列表 }; 定义,末尾分号不能省略
      • 访问成员用 .(点运算符),结构体指针访问成员用 ->(箭头运算符)
      • 结构体数组可以批量管理多个同类型对象,常用于学生、商品等场景
      • 结构体指针常用于函数传参,避免复制整个结构体,提高效率

--- 0-12的二进制码

0000 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100

  • 二进制码解读:

    • 每一组 4 位二进制数对应一个十六进制数字,方便阅读和记忆
    • 例如:0000 对应 0,0001 对应 1,0010 对应 2,0011 对应 3,以此类推
    • 从右往左每一位的权重依次是 1、2、4、8、16、32、64、128......(即 2 的 0 次方、1 次方、2 次方......)
    • 例如 0111 从右往左:1×1 + 1×2 + 1×4 + 0×8 = 7,所以 0111 表示十进制 7
  • 位移运算与二进制的关系:

    • 右移一位(>> 1)相当于把所有位向右移动一位,最右边的一位被丢弃,最左边补 0,效果等价于整除 2
      • 例如:7 >> 1,7 的二进制是 0111,右移一位变成 0011,即十进制 3
    • 左移一位(<< 1)相当于把所有位向左移动一位,最左边的一位被丢弃,最右边补 0,效果等价于乘以 2
      • 例如:3 << 1,3 的二进制是 0011,左移一位变成 0110,即十进制 6
    • 位移运算在底层硬件中执行速度极快,常用于性能敏感的场景,如乘除 2 的幂、位掩码操作等
  • 逻辑语句:控制程序的执行流程,让代码能够根据条件做出不同选择,或重复执行某段代码

  • 条件判断语句:根据条件的真假,决定执行哪一段代码

    • if-else:最常用的条件判断语句
      • 格式:if (条件) { 条件为真时执行的代码 } else { 条件为假时执行的代码 }

      • 条件可以是比较表达式(如 a > b)、逻辑表达式(如 a > 0 && b > 0)或任意非零值

      • 示例:

        c 复制代码
        int score = 85;
        if (score >= 60) {
            printf("及格\n");
        } else {
            printf("不及格\n");
        }
      • 支持 else if 进行多分支判断,例如根据分数划分优秀、良好、及格、不及格

    • switch case:匹配语句,根据表达式的值精确匹配到对应的分支
      • 格式:switch (表达式) { case 常量: 语句; break; default: 语句; }

      • 表达式的结果必须是整数类型(int、char 等),不能是浮点数或字符串

      • 每个 case 末尾通常要加 break;,否则会继续向下执行(称为「穿透」)

      • default 分支在所有 case 都不匹配时执行,可省略

      • 示例:

        c 复制代码
        int day = 3;
        switch (day) {
            case 1: printf("星期一\n"); break;
            case 2: printf("星期二\n"); break;
            case 3: printf("星期三\n"); break;
            default: printf("无效输入\n");
        }
      • 适用场景:当判断的分支较多且都是等值比较时,switch 比 if-else 更清晰高效

  • 循环语句:让同一段代码重复执行多次,直到满足退出条件

    • for:适合已知循环次数的场景

      • 格式:for (初始化; 条件; 更新) { 循环体 }

      • 三个部分都可以省略,但分号不能省;全部省略时是死循环

      • 示例:计算 1 到 100 的和

        c 复制代码
        int sum = 0;
        for (int i = 1; i <= 100; i++) {
            sum += i;
        }
        printf("1到100的和 = %d\n", sum);
    • while:适合循环次数未知、只根据条件判断的场景

      • 格式:while (条件) { 循环体 }

      • 先判断条件,条件为真才进入循环体;条件一开始为假,循环体一次都不执行

      • 示例:不断读取输入直到输入 0 为止

        c 复制代码
        int num;
        scanf("%d", &num);
        while (num != 0) {
            printf("你输入了 %d\n", num);
            scanf("%d", &num);
        }
    • do-while:先执行一次循环体,再判断条件

      • 格式:do { 循环体 } while (条件);

      • 与 while 的区别:do-while 至少执行一次循环体,适合「先做再判断」的场景

      • 示例:菜单选择,至少显示一次菜单

        c 复制代码
        int choice;
        do {
            printf("1. 查询  2. 退出\n");
            scanf("%d", &choice);
        } while (choice != 2);
    • 循环控制关键字:

      • break:立即跳出当前循环(或 switch)
      • continue:跳过本次循环剩余部分,直接进入下一次循环
      • 示例:打印 1 到 10 中所有奇数
c 复制代码
            for (int i = 1; i <= 10; i++) {
                if (i % 2 == 0) {
                    continue;   // 偶数跳过
                }
                printf("%d ", i);
            }
  • 要点总结:
    • if-else 适合范围判断和多分支逻辑,switch 适合等值匹配
    • for 适合已知次数,while 适合条件控制,do-while 保证至少执行一次
    • 循环中注意更新条件,避免死循环;break 跳出循环,continue 跳过本次

数组:

  • 数组的定义:数组是一种可以存储多个同类型元素的数据结构,在内存中是一整块连续的空间,可以通过下标(索引)快速访问其中的任意元素

  • 数组的类型设定:数组中的元素必须是同一种数据类型,可以是 int、float、double、char 等基本类型,也可以是结构体等自定义类型

    • 定义格式:类型名 数组名[长度];,例如 int scores[5]; 表示定义一个能存放 5 个 int 类型元素的数组
    • 数组的长度必须是常量,不能是变量(C99 之前);定义后长度不可改变
  • 数组的初始化:

    • 定义时直接赋值:int arr[5] = {10, 20, 30, 40, 50};
    • 部分初始化:int arr[5] = {1, 2}; 未指定的元素自动补 0
    • 省略长度初始化:int arr[] = {1, 2, 3, 4, 5}; 编译器根据元素个数自动确定长度
    • 全部初始化为 0:int arr[5] = {0};
  • 数组的长度是固定的:一旦定义,数组能存储的元素个数就固定了,不能像链表那样动态增删

    • 数组名本质是一个常量指针,指向数组首元素的地址,定义之后不能修改
    • 如果需要动态改变存储空间大小,可以使用 malloc 动态分配内存(见指针一节)
  • 数组的下标不要超过范围:

    • 数组下标从 0 开始,到 长度 - 1 结束,例如长度为 5 的数组,下标范围是 0 到 4
    • 访问越界下标(如 arr[5])属于未定义行为,可能读取到脏数据,甚至导致程序崩溃
    • 遍历数组时,循环条件通常写成 i < 长度,避免越界
  • 数组的遍历:使用 for 循环配合下标,可以依次访问数组中的每一个元素

    c 复制代码
    #include <stdio.h>
    int main() {
        int arr[5] = {10, 20, 30, 40, 50};
        // 遍历并打印每个元素
        for (int i = 0; i < 5; i++) {
            printf("arr[%d] = %d\n", i, arr[i]);
        }
        return 0;
    }
    • 运行结果:

      复制代码
      arr[0] = 10
      arr[1] = 20
      arr[2] = 30
      arr[3] = 40
      arr[4] = 50
  • 数组的求和与平均值:

    c 复制代码
    #include <stdio.h>
    int main() {
        int scores[5] = {85, 90, 78, 92, 88};
        int sum = 0;
        // 遍历求和
        for (int i = 0; i < 5; i++) {
            sum += scores[i];
        }
        double avg = (double)sum / 5;   // 强转为 double,避免整数除法丢失小数
        printf("总分 = %d\n", sum);
        printf("平均分 = %.2f\n", avg);
        return 0;
    }
    • 运行结果:

      复制代码
      总分 = 433
      平均分 = 86.60
  • 数组的查找:遍历数组,逐个比较元素是否等于目标值,找到后记录下标

    c 复制代码
    #include <stdio.h>
    int main() {
        int arr[5] = {10, 20, 30, 40, 50};
        int target = 30;
        int found = -1;   // 初始化为 -1,表示未找到
        for (int i = 0; i < 5; i++) {
            if (arr[i] == target) {
                found = i;   // 记录找到的下标
                break;
            }
        }
        if (found != -1) {
            printf("找到 %d,下标为 %d\n", target, found);
        } else {
            printf("未找到 %d\n", target);
        }
        return 0;
    }
    • 运行结果:

      复制代码
      找到 30,下标为 2
  • 数组的排序:以冒泡排序为例,通过相邻元素两两比较并交换,把最大(或最小)的元素逐步「冒泡」到末尾

    c 复制代码
    #include <stdio.h>
    int main() {
        int arr[5] = {50, 10, 40, 20, 30};
        // 冒泡排序(升序)
        for (int i = 0; i < 5 - 1; i++) {
            for (int j = 0; j < 5 - 1 - i; j++) {
                if (arr[j] > arr[j + 1]) {
                    int temp = arr[j];
                    arr[j] = arr[j + 1];
                    arr[j + 1] = temp;
                }
            }
        }
        // 打印排序结果
        printf("排序后:");
        for (int i = 0; i < 5; i++) {
            printf("%d ", arr[i]);
        }
        printf("\n");
        return 0;
    }
    • 运行结果:

      复制代码
      排序后:10 20 30 40 50
  • 要点总结:

    • 数组元素类型必须一致,长度固定,下标从 0 开始,访问时注意不要越界
    • 遍历数组常用 for 循环,配合 i < 长度 作为循环条件
    • 求和、平均值、查找、排序等操作都建立在遍历的基础上,先掌握遍历再学这些操作
    • 数组名是常量指针,不能重新赋值;需要动态大小请使用 malloc

函数:

  • 格式: 返回值类型 函数名(参数类型 参数名){ 函数体 }
    • 返回值:
      • 定义函数时,设定在函数执行完成之后需要返回什么数据,返回值类型就设定为该数据的类型,在函数执行结束的地方,使用return
        返回一个匹配类型的数据、带数据的变量
      • 调用带返回值的函数时,在函数的前面定义一个与返回值类型一样的变量 等于 这个函数式调用
        • int a = abs(-100);
    • 参数:
      • 定义函数时,预设一组变量,知道类型,不知道具体的值,只用于完成函数中逻辑代码编写
      • 调用函数时,一定要根据参数设定来传入对应匹配的数据、带数据的变量 按照匹配的类型 顺序 数量传入
  • 定义函数的优点,缺点:
    • 优点:
      • 将程序中一些重复执行的代码定义出来,然后通过调用函数的形式复用代码
      • 提高代码的简洁性,可重用性,可维护性
    • 缺点:
      • 函数中属于一个局部空间,将数据作为参数传入时,要考虑作用域的问题

指针:

  • 使用一个指针变量存储其他的变量的地址编码,在变量传递的时候比较方便

  • 将数据变量,作为参数传入函数时,如果需要在函数中修改该数据变量的值,通常需要使用指针类型作为参数,传入该变量的地址进去

  • 指针是地址操作,与数组下标通用 ,在我们实现动态数组中比较需要

    • 通过使用内存分配空间来替代数据,做到存储数据空间替换 ,因为普通的数组名本质是一个常量,定义之后不能改
  • 指针实战示例:使用 malloc 动态创建数组,通过指针遍历并修改元素,最后 free 释放内存

    c 复制代码
    #include <stdio.h>
    #include <stdlib.h>   // malloc / free 所在库
    
    int main() {
        int n = 5;
        // 1. 动态分配 n 个 int 大小的连续内存
        int *arr = (int*)malloc(n * sizeof(int));
        if (arr == NULL) {          // 分配失败时返回 NULL
            printf("内存分配失败\n");
            return 1;
        }
    
        // 2. 通过指针遍历数组并赋值
        for (int i = 0; i < n; i++) {
            *(arr + i) = i * 10;    // 等价于 arr[i] = i * 10
        }
    
        // 3. 通过指针遍历并修改元素(把每个元素加 1)
        for (int i = 0; i < n; i++) {
            arr[i] += 1;            // 下标写法,与 *(arr+i) 等价
        }
    
        // 4. 打印结果
        for (int i = 0; i < n; i++) {
            printf("arr[%d] = %d\n", i, arr[i]);
        }
    
        // 5. 释放内存,避免内存泄漏
        free(arr);
        return 0;
    }
    • 运行结果:

      复制代码
      arr[0] = 1
      arr[1] = 11
      arr[2] = 21
      arr[3] = 31
      arr[4] = 41
    • 要点总结:

      • malloc 返回 void* 指针,需要强转为目标类型(如 int*)
      • 分配后必须检查返回值是否为 NULL,防止内存分配失败
      • 指针算术 *(arr + i) 与下标 arr[i] 完全等价
      • 使用完 malloc 分配的内存后,必须调用 free 释放,否则会造成内存泄漏

结构体:

  • 与类与对象的类非常像, 可以通过设定一个结构类型,其中自定义多个不同类型的数据变量

  • 结构体变量创建之后,可以使用结构体变量调用其中的数据变量进行赋值 等使用场景

  • 结构体实战示例:定义一个学生结构体,包含姓名、年龄、学号、班级,创建结构体变量并赋值、打印,再演示结构体数组和结构体指针

    c 复制代码
    #include <stdio.h>
    #include <string.h>
    
    // 1. 定义结构体类型 Student
    struct Student {
        char name[20];      // 姓名
        int age;            // 年龄
        int id;             // 学号
        char class[20];     // 班级
    };
    
    int main() {
        // 2. 创建结构体变量并赋值
        struct Student stu1;
        strcpy(stu1.name, "张三");
        stu1.age = 18;
        stu1.id = 1001;
        strcpy(stu1.class, "计算机1班");
    
        // 3. 通过结构体变量访问成员并打印
        printf("姓名:%s\n", stu1.name);
        printf("年龄:%d\n", stu1.age);
        printf("学号:%d\n", stu1.id);
        printf("班级:%s\n", stu1.class);
    
        // 4. 结构体数组:存储多个学生
        struct Student stuArr[3] = {
            {"李四", 19, 1002, "计算机1班"},
            {"王五", 20, 1003, "计算机2班"},
            {"赵六", 18, 1004, "计算机2班"}
        };
    
        // 5. 遍历结构体数组
        printf("\n--- 结构体数组遍历 ---\n");
        for (int i = 0; i < 3; i++) {
            printf("第%d个学生:%s,%d岁,学号%d,%s\n",
                   i + 1, stuArr[i].name, stuArr[i].age,
                   stuArr[i].id, stuArr[i].class);
        }
    
        // 6. 结构体指针:通过指针访问成员
        struct Student *p = &stu1;
        printf("\n--- 结构体指针访问 ---\n");
        printf("通过指针访问姓名:%s\n", p->name);
        printf("通过指针访问年龄:%d\n", p->age);
    
        // 7. 通过指针修改成员
        p->age = 19;
        printf("修改后年龄:%d\n", stu1.age);
    
        return 0;
    }
    • 运行结果:

      复制代码
      姓名:张三
      年龄:18
      学号:1001
      班级:计算机1班
      
      --- 结构体数组遍历 ---
      第1个学生:李四,19岁,学号1002,计算机1班
      第2个学生:王五,20岁,学号1003,计算机2班
      第3个学生:赵六,18岁,学号1004,计算机2班
      
      --- 结构体指针访问 ---
      通过指针访问姓名:张三
      通过指针访问年龄:18
      修改后年龄:19
    • 要点总结:

      • 结构体用 struct 结构体名 { 成员列表 }; 定义,末尾分号不能省略
      • 访问成员用 .(点运算符),结构体指针访问成员用 ->(箭头运算符)
      • 结构体数组可以批量管理多个同类型对象,常用于学生、商品等场景
      • 结构体指针常用于函数传参,避免复制整个结构体,提高效率

链表:

  • 链表的定义:链表是一种动态的数据结构,由一系列节点(Node)通过指针连接而成,每个节点在内存中不一定连续,而是通过指针字段指向下一个节点,从而形成一条「链」

  • 组成结构:节点结构体变量,每个节点都包含了一个数据变量和下一个节点指针变量

    • 数据域:存放该节点要保存的数据,可以是 int、char 等基本类型,也可以是结构体等自定义类型

    • 指针域:存放下一个节点的地址,通过它把各个节点串联起来;最后一个节点的指针指向 NULL,表示链表结束

    • 节点结构体定义示例:

      c 复制代码
      struct Node {
          int data;          // 数据域:存放数据
          struct Node *next; // 指针域:指向下一个节点
      };
  • 链表与数组的区别:

    • 数组长度固定,链表长度可以动态增长或缩减
    • 数组在内存中连续,链表节点在内存中分散,靠指针连接
    • 数组按下标随机访问很快,链表只能从头开始逐个遍历
    • 数组插入/删除元素需要移动大量数据,链表只需修改指针指向,效率更高
  • 创建链表:通过 malloc 动态分配节点内存,再用指针把节点逐个连接起来

    c 复制代码
    #include <stdio.h>
    #include <stdlib.h>
    
    struct Node {
        int data;
        struct Node *next;
    };
    
    int main() {
        // 1. 创建三个节点并分配内存
        struct Node *n1 = (struct Node*)malloc(sizeof(struct Node));
        struct Node *n2 = (struct Node*)malloc(sizeof(struct Node));
        struct Node *n3 = (struct Node*)malloc(sizeof(struct Node));
    
        // 2. 给节点赋值
        n1->data = 10;
        n2->data = 20;
        n3->data = 30;
    
        // 3. 通过指针把节点连接成链表
        n1->next = n2;
        n2->next = n3;
        n3->next = NULL;   // 最后一个节点指向 NULL
    
        // 4. 从头节点开始遍历链表
        struct Node *p = n1;
        while (p != NULL) {
            printf("%d ", p->data);
            p = p->next;   // 移动到下一个节点
        }
        printf("\n");
    
        // 5. 释放内存
        free(n1);
        free(n2);
        free(n3);
        return 0;
    }
    • 运行结果:

      复制代码
      10 20 30
  • 链表的遍历:通过节点中下一个节点的指针可以进行从头到尾的循环遍历操作

    • 从「头节点」开始,用一个临时指针 p 指向当前节点
    • 每访问完一个节点,就让 p 指向 p->next,即下一个节点
    • 当 p 为 NULL 时,说明已经走到链表末尾,遍历结束
    • 遍历是链表最基础的操作,查找、统计长度、打印等都要基于遍历实现
  • 链表的插入:在指定位置插入一个新节点,核心是修改相邻节点的指针指向

    c 复制代码
    #include <stdio.h>
    #include <stdlib.h>
    
    struct Node {
        int data;
        struct Node *next;
    };
    
    int main() {
        // 创建初始链表:10 -> 20 -> 30
        struct Node *head = (struct Node*)malloc(sizeof(struct Node));
        struct Node *n2 = (struct Node*)malloc(sizeof(struct Node));
        struct Node *n3 = (struct Node*)malloc(sizeof(struct Node));
        head->data = 10; head->next = n2;
        n2->data = 20;   n2->next = n3;
        n3->data = 30;   n3->next = NULL;
    
        // 在 20 之后插入一个新节点 25
        struct Node *newNode = (struct Node*)malloc(sizeof(struct Node));
        newNode->data = 25;
        newNode->next = n2->next;   // 新节点指向 30
        n2->next = newNode;         // 20 指向新节点
    
        // 遍历打印
        struct Node *p = head;
        while (p != NULL) {
            printf("%d ", p->data);
            p = p->next;
        }
        printf("\n");
    
        // 释放内存
        while (head != NULL) {
            struct Node *temp = head;
            head = head->next;
            free(temp);
        }
        return 0;
    }
    • 运行结果:

      复制代码
      10 20 25 30
  • 链表的删除:删除指定节点,核心是让前一个节点的指针跳过被删除的节点

    c 复制代码
    #include <stdio.h>
    #include <stdlib.h>
    
    struct Node {
        int data;
        struct Node *next;
    };
    
    int main() {
        // 创建链表:10 -> 20 -> 30
        struct Node *head = (struct Node*)malloc(sizeof(struct Node));
        struct Node *n2 = (struct Node*)malloc(sizeof(struct Node));
        struct Node *n3 = (struct Node*)malloc(sizeof(struct Node));
        head->data = 10; head->next = n2;
        n2->data = 20;   n2->next = n3;
        n3->data = 30;   n3->next = NULL;
    
        // 删除节点 20:让 10 直接指向 30
        struct Node *toDelete = n2;
        head->next = n2->next;   // 10 跳过 20,指向 30
        free(toDelete);          // 释放被删除节点的内存
    
        // 遍历打印
        struct Node *p = head;
        while (p != NULL) {
            printf("%d ", p->data);
            p = p->next;
        }
        printf("\n");
    
        // 释放内存
        while (head != NULL) {
            struct Node *temp = head;
            head = head->next;
            free(temp);
        }
        return 0;
    }
    • 运行结果:

      复制代码
      10 30
  • 要点总结:

    • 链表节点由数据域和指针域组成,指针域存放下一个节点的地址
    • 链表通过 malloc 动态分配节点,长度可以灵活变化,不受固定大小限制
    • 遍历链表必须从头节点开始,借助 p = p->next 逐个移动
    • 插入和删除只需修改指针指向,不需要移动大量数据,效率高
    • 使用完链表后要逐个 free 释放每个节点,避免内存泄漏

总结:

本文系统梳理了 C 语言的核心语法,从最基础的程序结构出发,逐步深入到数据类型、数组、函数、指针、结构体与链表等进阶内容,帮助读者建立起完整的 C 语言知识框架。

  • 程序结构 :C 程序由头文件导入、主函数入口和各类语句组成。头文件提供了标准库函数(stdio.h 输入输出、stdlib.h 动态内存分配、math.h 数学运算、string.h 字符串处理),主函数 int main() 是程序执行的起点。
  • 数据类型与变量:基本类型包括整数型(int、long、long long)、浮点型(float、double)和字符型(char);字符串本质是 char 数组;数组、指针、结构体则是在基本类型之上构建的复合数据形式。
  • 数组:在内存中连续存储同类型元素,通过下标快速访问。遍历、求和、查找、排序等操作都建立在 for 循环遍历的基础上;数组长度固定,需要动态大小时应改用 malloc。
  • 函数:通过「返回值类型 + 函数名 + 参数 + 函数体」的格式定义,实现代码复用,提高可维护性;注意参数传递时的作用域问题。
  • 指针:存储其他变量的内存地址,与数组下标通用,是实现动态数组和函数内修改外部变量的关键;使用 malloc 分配的内存必须用 free 释放。
  • 结构体 :自定义数据类型,将多个不同类型成员组合在一起,类似面向对象中的类;用 . 访问成员、-> 通过指针访问,结构体数组可批量管理对象。
  • 链表:由节点通过指针连接而成的动态数据结构,节点包含数据域和指针域。与数组相比,链表长度可动态变化,插入删除只需修改指针指向,效率更高;遍历、插入、删除是链表最核心的操作。

学习建议:先掌握程序结构与基本数据类型,再依次攻克数组、函数、指针,最后学习结构体与链表。指针和动态内存分配是 C 语言的难点,务必亲手敲代码验证 malloc/free 的配对使用,避免内存泄漏。链表作为指针与结构体的综合应用,是理解后续数据结构(栈、队列、树)的重要基石。

相关推荐
ttwuai43 分钟前
Go开源后台管理系统推荐:README、源码和License应该怎么验
开发语言·golang·开源
Java后端的Ai之路1 小时前
Python 进阶探索30 - Python中的装饰器
开发语言·人工智能·python·文件处理·装饰器模式
沫璃染墨1 小时前
《从零入门Linux系统篇(五十八):线程篇·十一——线程安全与死锁详解:从可重入到多锁管理》
linux·服务器·开发语言·c++·驱动开发·安全·架构
海宇AI1 小时前
Java数据工程:利用海宇婚恋风险报告优化高端婚恋实名与涉诉核验合规体验
java·人工智能
我命由我123451 小时前
Photoshop - 了解 Photoshop
学习·职场和发展·产品运营·求职招聘·职场发展·学习方法·photoshop
CEZ1 小时前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·开源
黑妹天下第一乖1 小时前
第 05 讲:阿加犀 AidCV 图像处理加速与 OpenCV 一致开发实战
开发语言·图像处理·人工智能·嵌入式硬件·数码相机·opencv·计算机视觉
言乐61 小时前
Python基于关键词分拣快递(适用于电商与货运代理等)
开发语言·python·django·virtualenv·pygame
Wang's Blog1 小时前
Java框架 SpringCloud 快速入门: Gateway 路由的过滤器配置
java·spring cloud·gateway