C专升本计算机基础与程序设计专业

绪论

特点

  1. 关键字:32+5
  2. 9个控制语句,34个运算符
  3. 大小写严格区分
  4. 运费限制不严格
  5. 可访问物理地址
  6. 代码质量高,效率高
  7. 移植性好

头文件

1
2
3
#include<stdio.h>         //在系统路径下查找头文件
#include"stdio.h" //先在当前目录下查找头文件,没有再在系统路径下查找头文件
#include<define.h>

main函数

程序的入口,有且只有一个

程序的基本单位是函数

1
2
3
int main(){
.....
}

注释

  1. 单行注释

    1
    // 描述信息
  2. 多行注释

    1
    /* 描述信息 */

    提示:编译器在编译代码时,会忽略注释的内容,可以等于空格

程序的执行过程

编写.c文件

预处理:处理.h等文件

编译:变成二进制的.obj文件,会检查语法报错

链接:打包成.exe程序

算法

特性:

  1. 有穷性
  2. 确定性
  3. 有零个或者多个输入
  4. 有一个或者多个输出
  5. 有效性(可行性)

数据类型

基本类型:

1字节=8bit

整型

  • 基本整型:int(2或者4字节)

    unsigned int 无符号整形

  • 短整型:short int(2字节)

  • 长整型:long int(4字节)

  • 字符型:char

  • 双长整型:long long int

  • 布尔型:bool

浮点型(实型)

  • 单精度浮点型:float(4字节)在数字后面加f为单精度
  • 双精度浮点型:double(8字节)默认为双精度
  • 长双精度浮点型:long double(8字节或者16字节)

枚举型:enum

空类型:void

派生类型

指针类型:*

数组类型:[]

结构体类型:struct

共用体类型:union

常量

  1. 整型常量

    【长整型的后缀为L(l),无符号整型(正数)后缀为U(u),LU或者UL是长整型无符号整型】

    八进制整型常量:0开头

    十进制整型常量:无前缀

    十六进制整型常量:0x或者0X开头123456789abcdef(ABCDEF)

  2. 实型常量

    常数型

    .1 ==0.1

    -1. == -1.0

    -.1==-0.1

    .-2==0.-2 是错误的

    指数型(e前e后要有数,e后必须是整数)

    123.62 == 1.2362e2(E2)

    123456.78 ==1.2345678E5

  3. 字符型常量

    字符型

    作用:字符型变量用于显示单个字符

    语法:char ch = 'a';

    注意1:在显示字符型变量时,用单引号将字符括起来,不要用双引号

    注意2:单引号内只能有一个字符,不可以是字符串

    • C和C++中字符型变量只占用1个字节。
    • 字符型变量并不是把字符本身放到内存中存储,而是将对应的ASCII编码放入到存储单元
    • ‘0’==48 ‘A’==65 ‘a’==97 差32

    转义字符

    作用:用于表示一些不能显示出来的ASCII字符

    现阶段我们常用的转义字符有: \n \\ \t

    转义字符 含义 ASCII码值(十进制)
    \a 警报 007
    \b 退格(BS) ,将当前位置移到前一列 008
    \f 换页(FF),将当前位置移到下页开头 012
    \n 换行(LF) ,将当前位置移到下一行开头 010
    \r 回车(CR) ,将当前位置移到本行开头 013
    \t 水平制表(HT) (跳到下一个TAB位置) 009
    \v 垂直制表(VT) 011
    \\ 代表一个反斜线字符”” 092
    \’ 代表一个单引号(撇号)字符 039
    \“ 代表一个双引号字符 034
    ? 代表一个问号 063
    \0 数字0 000
    \ddd 8进制转义字符,d范围0~7 3位8进制
    \xhh 16进制转义字符,h范围09,af,A~F 3位16进制
  4. 字符串常量

    作用:用于表示一串字符,由’\0’字符结束

    两种风格

    1. C风格字符串: char 变量名[] = "字符串值"
    2. C++风格字符串: string 变量名 = "字符串值"

    注意:C++风格字符串,需要加入头文件#include

    字节==字符数量+1

  5. 符号常量

    作用:通过一个符号表示一个常量

    格式:#define PI 3.1415

    编译前会预处理将代码中PI变成3.1415

    不会占用内存

二进制转化十进制

八进制转化十进制

十六进制转化十进制

同理

标识符命名规则

作用:C规定给标识符(变量、常量)命名时,有一套自己的规则

  • 标识符不能是关键字
  • 标识符只能由字母、数字、下划线组成
  • 第一个字符必须为字母或下划线
  • 标识符中字母区分大小写

常变量

const 类型 变量名 = 值

如const int a =13

那么a不可以被修改,会占用内存

变量的定义

1
2
3
4
5
int main() {
int a,b,c;//没有赋值则为随机值
printf(%d %d %d ,a,b,c);
return 0;
}

sizeof关键字

作用:利用sizeof关键字可以统计数据类型所占内存大小

语法: sizeof( 数据类型 / 变量)

示例:

1
2
3
4
5
6
7
8
9
10
11
12
#include<iostream>
using namespace std;
int main4()
{
//sizeof可以求出数据类型占用内存的大小
//语法:sizoef (数据类型或者变量)
int a = 40;
cout << "a所占用的空间为" << sizeof(a) << endl;

system("pause");
return 0;
}

不同类型运算

都会转化为double类型运算

运算符

优先级

逗号运算符,优先级最低,最右边为值

1
2
3
c=2;
printf (a,b,c++,c);//3
y=a,b;//=的优先级大于逗号,所以y=a

输入和输出

stdio.h文件中包含输入和输出函数

  1. putchar(输出字符)

    1
    putchar(a)//a
  2. getchar(输入字符)

    是函数调用,没有参数,需要回车,保留前面的,剩下的还在缓冲区,下次才保存,保存为ASCll码

    1
    2
    3
    4
    5
    6
    a=getchar()//123
    printf("%d",a)//49
    printf("%c",a)//1
    getchar()//把2拿走
    b=getchar()
    printf("%c",b)//3
  3. printf(格式输出)

    使用printf(格式控制字符串,输出列表)

    %d(i) == 有符号的十进制整数

    %c ==字符

    %f ==浮点数 (默认6为小数)%6.3f是 6为总格,3为小数位的格,-1.2345 =-1.235(四舍五入),-号和 . 也算一位,不够会补0,%0.3f为整数全部,小数3位

    %s ==字符串

    %e == 指数

    %o ==八进制整数

    %x == 十六进制整数(无前缀)

    %#x == 十六进制整数(有前缀)

    1
    2
    3
    4
    5
    6
    //格式控制字符串 =" 格式声明符% 和格式字符 " 如"%d,%d "
    printf("这是%d,这是%d",a,b);
    //没有使用控制字符则不用输出列表,直接为字符串
    printf("这是");
    //d前面可以加数字,表示占格,如果为3的话,输出5,则为 空格空格5,-1则为-1空格空格,2888为2888
    printf("这是%3d,这是%d。",5,-1);//这是 5,这是-1 。
  4. scanf(格式输入)

    格式:scanf(格式控制字符串,地址列表)

    %c可以连续输入,因为只能保存一个字符,会自动切换,如果回车,回车也是一个字符

    1
    2
    3
    4
    5
    scanf ("%c%c%c%c",&a,&c,&v,&b);//qw回车e
    a==a
    c==w
    v==回车
    b==e

    同样可以 %3d控制输入的数量,要四舍五入,%mf则m不能为小数

    1
    2
    scanf ("%3f",&a);//3.1415
    a==3.1//小数点也是

    输入要门当户对,如变量是long float或者double,则需要%lf才能输入

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    例子:scanf ("a=%d%d%d",&s,&b,&d);
    //输入 a=5 回车 2 回车 0 回车结束
    scanf ("a=%d,%d,%d",&s,&b,&d);
    //输入 a=5,2,0 回车 有逗号可以不用回车分开
    s==5
    b==2
    d==0
    输入的内容要和字符串一样,不然会错
    scanf ("a=%2d%d",&s,&b);
    //输入 a=5203 回车 s获得2位,b和一位,要四舍五入
    s==52
    b=0

    long float v;
    double v;
    scanf ("%lf",&v);//3
    printf("%f",v)//3.000000
    如果输入和变量不匹配则输入失败,为0
    scanf ("%f",&v);//3
    printf("%f",v)//0.000000

    注意:遇到空格或者回车或者TAB键会停止当前的占位的控制字符输入

    如果坑多,但是地址不够时,则全部输入失败

    1
    2
    scanf ("%c,%c,%c",&s);//qwe
    //空

    坑少,地址多时,后面的地址的变量会为随机数

    1
    2
    3
    scanf ("%c",&s,&d);//q
    s==q
    d==随机
  5. puts(输出字符串)

  6. gets(输入字符串)

逻辑运算符的短路

&&时,如果前面的为假,则后面的不会计算

1
2
3
int a=0,b=10;
printf("%d,(a&&b++)")//0
printf("%d,b")//10

||时,如果前面的为真,则后面的不会计算

1
2
3
int a=1,b=10;
printf("%d,(a||b++)")//1
printf("%d,b")//10c

表达式等价

1
2
3
4
if (x){}
和
if(x!=0){}
是一样的

原码,反码,补码

注意:正数的原码,反码,补码都是原本的二进制数(8位)

原码:数的二进制,正数第一位为0,负数第一位为1

反码:负数原码第一位不变,其他取反

补码:负数反码+1

按位与,按位或,按位异或,按位取反

按位与 & :将左右的值转化为二进制补码,然后进行与的比较,得到的值(优先级8)

1
2
3
4
5&3 ==1
5:00000101
3:00000011
&:00000001

按位或 |:将左右的值转化为二进制补码,然后进行或的比较,得到的值(优先级10)

1
2
3
4
5|3 ==7
5:00000101
3:00000011
|:00000111

按位异或^:将左右的值转化为二进制补码,然后进行的比较,相同的为0,不同的为1(优先级9)

1
2
3
4
5^3 ==6
5:00000101
3:00000011
^:00000110

按位取反~:将右边数的二进制原码全部区反(补码),再-1(反码),除第一位,其他取反(原码)

1
2
3
4
5
~6 == -7
00000110
11111001
11111000
10000111//-7

按位左移,按位右移

按位左移<<:将左边的数的原码左移右边的数的位,正数补0,负数补1(优先级5)

1
2
3
4
5
5<<2 ==20  ==5*2^2
|00000101|
|000101
|00010100|
5<<3 ==40 ==5*2^3

按位右移>>:将左边的数的原码右移右边的数的位,正数补0,负数补1(优先级5)

1
2
3
4
5>>2 ==1  ==5/2^2
|00000101|
| 000001
|00000001|

条件表达式

条件 ? 表达式1 :表达式2:条件为真则执行表达式1,假为表达式2,(可以嵌套)

1
2
3
a>b ? a : a>c ? a : c
等于
a>b ? a : (a>c ? a : c)

switch语句

如果没有没有break,当a=1时,输出一之后会输出3和2

如果a=3时,会default开始往下执行,输出3和2

当a=2时,只输出2

1
2
3
4
5
6
Switch(a){
case 1: printf("1");break;
default: printf("3");
case 2:printf("2");
}

注意:a只能是整型和表达式,case后面只能是整型

文件操控

文件:存储在外部介质上的数据集合。

文件分类

  • 文本文件:ASCII 码存放(人能看懂)
  • 二进制文件:按内存原样存放(看不懂)

缓冲文件系统:C 语言用的是缓冲文件系统。

文件指针:FILE *fp;

  • FILE 是系统定义的结构体类型。

文件打开与关闭(必考)

打开文件 fopen

1
fp = fopen("文件名", "打开方式");

打开方式(必须背熟)

  • r:只读,文件必须存在
  • w:只写,文件不存在则新建,存在则清空
  • a:追加,在文件末尾写
  • r+:读写,文件必须存在
  • w+:读写,新建 / 清空
  • a+:读写,追加

带 b 是二进制:rb、wb、ab

打开失败

  • 失败返回 NULL
  • 必须判断:
1
2
3
4
if(fp == NULL) {
printf("打开失败");
return 0;
}

关闭文件 fclose

1
fclose(fp);

作用:释放缓冲区,防止数据丢失。

打开后一定要关闭。

文件读写函数(大题核心)

. 字符读写

  • fgetc(fp):从文件读一个字符
  • fputc(ch, fp):向文件写一个字符
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <stdio.h>

int main() {
FILE *fp;
char ch;

// 1. 用fputc写字符到文件
fp = fopen("char.txt", "w"); // 只写模式打开,不存在则新建
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
fputc('A', fp); // 写入字符'A'
fputc('B', fp); // 写入字符'B'
fclose(fp); // 写完必须关闭

// 2. 用fgetc从文件读字符
fp = fopen("char.txt", "r"); // 只读模式打开
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
// 循环读,直到文件结束(feof判断)
while (!feof(fp)) {
ch = fgetc(fp); // 读一个字符
printf("%c", ch); // 输出:AB
}
fclose(fp);

return 0;
}

运行结果

控制台输出:AB,同时生成 char.txt 文件,内容是 AB。

2. 字符串读写

  • fgets(buf, n, fp):读一行字符串
  • fputs(str, fp):写一行字符串
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
#include <stdio.h>

int main() {
FILE *fp;
char buf[100]; // 存储读取的字符串

// 1. 用fputs写字符串到文件
fp = fopen("str.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
fputs("广东专升本\n", fp); // 写入字符串,加\n换行
fputs("C语言文件操作\n", fp);
fclose(fp);

// 2. 用fgets读字符串
fp = fopen("str.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
// 循环读每一行,fgets读不到内容时返回NULL
while (fgets(buf, 100, fp) != NULL) {
printf("%s", buf); // 输出:广东专升本 换行 C语言文件操作 换行
}
fclose(fp);

return 0;
}

考试注意点

fgets(buf, n, fp) 中 n 是缓冲区大小,实际最多读 n-1 个字符(留 1 个存\0),专升本常考这个细节。

3. 格式化读写(最常考)(读写结构体数据,专升本高频大题)

  • fscanf(fp, "%d%s", &a, str); 从文件读
  • fprintf(fp, "%d %s", a, str); 写入文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <stdio.h>

// 定义学生结构体(考试常考)
struct Student {
int id; // 学号
char name[20];// 姓名
float score; // 分数
};

int main() {
FILE *fp;
struct Student s1 = {101, "张三", 95.5}, s2;

// 1. 用fprintf写结构体数据到文件
fp = fopen("student.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
// 按格式写入:学号 姓名 分数
fprintf(fp, "%d %s %.1f", s1.id, s1.name, s1.score);
fclose(fp);

// 2. 用fscanf读结构体数据
fp = fopen("student.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
// 按格式读取,存到s2中
fscanf(fp, "%d %s %f", &s2.id, s2.name, &s2.score);
// 输出验证:学号:101 姓名:张三 分数:95.5
printf("学号:%d 姓名:%s 分数:%.1f\n", s2.id, s2.name, s2.score);
fclose(fp);

return 0;
}

运行结果

控制台输出:学号:101 姓名:张三 分数:95.5,student.txt 内容:101 张三 95.5。

4. 二进制读写(会考)

  • fread(buffer, size, count, fp); 读
  • fwrite(buffer, size, count, fp); 写
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>

struct Student {
int id;
char name[20];
float score;
};

int main() {
FILE *fp;
struct Student s1 = {102, "李四", 88.0}, s2;

// 1. 二进制写(注意打开方式加b)
fp = fopen("student.bin", "wb");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
// 参数:数据地址、单个数据大小、个数、文件指针
fwrite(&s1, sizeof(struct Student), 1, fp);
fclose(fp);

// 2. 二进制读(打开方式加b)
fp = fopen("student.bin", "rb");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
fread(&s2, sizeof(struct Student), 1, fp);
// 输出:学号:102 姓名:李四 分数:88.0
printf("学号:%d 姓名:%s 分数:%.1f\n", s2.id, s2.name, s2.score);
fclose(fp);

return 0;
}

考试注意点

二进制读写必须用 wb/rb 打开方式,sizeof(结构体) 是专升本必考参数。

文件定位函数(填空选择)

1
rewind(fp)

作用:文件指针回到开头

1
fseek(fp, 偏移量, 起始点)

作用:移动文件指针

起始点:

  • SEEK_SET 0 开头
  • SEEK_CUR 1 当前
  • SEEK_END 2 末尾
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <stdio.h>

int main() {
FILE *fp;
char ch;

// 先写入测试内容:"ABCDEFGHIJK"(共11个字符,每个字符占1字节)
fp = fopen("fseek.txt", "w");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}
fputs("ABCDEFGHIJK", fp);
fclose(fp);

// 打开文件演示fseek
fp = fopen("fseek.txt", "r");
if (fp == NULL) {
printf("文件打开失败!\n");
return 0;
}

// 场景1:从开头偏移4字节(SEEK_SET=0)
fseek(fp, 4, SEEK_SET); // 开头→跳过A(0)、B(1)、C(2)、D(3),到E(4)
ch = fgetc(fp);
printf("从开头偏移4字节:%c\n", ch); // 输出:E

// 场景2:从当前位置偏移2字节(SEEK_CUR=1)
fseek(fp, 2, SEEK_CUR); // 当前在E→跳过F(5)、G(6),到H(7)
ch = fgetc(fp);
printf("从当前位置偏移2字节:%c\n", ch); // 输出:H

// 场景3:从末尾向前偏移3字节(SEEK_END=2)
fseek(fp, -3, SEEK_END); // 末尾→向前跳K(10)、J(9),到I(8)
ch = fgetc(fp);
printf("从末尾偏移-3字节:%c\n", ch); // 输出:I

fclose(fp);
return 0;
}
1
long pos = ftell(fp); // 返回值是long型(专升本易错点:别用int)

作用:返回当前位置距离文件开头的字节数

文件结束判断

  • feof(fp)
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26

    文件结束:返回非 0

    没结束:返回 0

    ## 广东专升本必考模板(直接套)

    ```c
    #include <stdio.h>

    int main() {
    FILE *fp;
    fp = fopen("test.txt", "r");
    if(fp == NULL) {
    printf("打不开");
    return 0;
    }

    // 读写操作
    int a;
    fscanf(fp, "%d", &a);
    fprintf(fp, "%d", a);

    fclose(fp);
    return 0;
    }

字符串函数

头文件

1
#include<string.h>

strlen(求长度)

功能:数字符串的有效字符数,**不含末尾的 \0**。

关键考点:和 sizeof 区分 ——sizeof 算总字节数(含 \0、数组长度),strlen 只算有效字符。

例子:

1
2
3
char s[] = "hello";  // 实际存:h e l l o \0
strlen(s); // 结果:5(只数到o)
sizeof(s); // 结果:6(连\0一起算)

strcpy(复制)

功能:把后面的字符串连 \0 一起复制到前面的数组里。

关键考点:前面的数组必须足够大,装得下后面的字符串(不然会越界报错)。

例子:

1
2
char s1[10] = "abc";  // s1空间够大
strcpy(s1, "hello"); // 复制后s1变成:h e l l o \0

strcat(拼接)

功能:把后面的字符串拼到前面字符串的末尾(覆盖前面的 \0,最后加新 \0)。

关键考点:前面的数组必须足够大,能装下拼接后的总长度。

例子:

1
2
char s1[20] = "hello";  // 空间够大
strcat(s1, " world"); // 拼接后s1变成:hello world\0

strcmp(比较)

功能:按 ASCII 码逐位比两个字符串,返回值:

  • 相同 → 返回 0;
  • 前面大 → 返回正数;
  • 前面小 → 返回负数。

关键考点:比的是 ASCII 码(比如 'A' 65 < 'a' 97),不是长度。

1
2
strcmp("abc", "abc");  // 结果:0(完全一样)
strcmp("abc", "abd"); // 结果:负数('c' < 'd')

strchr(找单个字符)

功能:在字符串里从前往后找某个字符第一次出现的位置,返回指向该位置的指针;找不到返回 NULL。

关键考点:返回指针后,用 printf("%s", 指针) 会从找到的字符开始输出到末尾。

例子:

1
2
3
char s[] = "hello";
char *p = strchr(s, 'l'); // 找第一个'l'
printf("%s", p); // 结果:llo(从'l'开始输出)

strstr(找子串)

功能:在字符串里找某个子串第一次出现的位置,返回指向该位置的指针;找不到返回 NULL。

关键考点:找的是 “一串字符”,不是单个字符,用法和 strchr 类似。

例子:

1
2
3
char s[] = "hello world";
char *p = strstr(s, "world"); // 找子串"world"
printf("%s", p); // 结果:world(从"world"开始输出)

数组

一维数组(基础必考)

  • 定义格式:

    1
    类型名 数组名[长度];

    例:

    1
    int a[5];

    (定义一个能存 5 个整数的数组,长度必须是常量)

  • 下标规则:从 0 开始,最大下标是 长度-1

    例:

    1
    a[0]是第 1 个元素,a[4]是第 5 个元素,不能用 `a[5]`(越界)
  • 初始化:

    • 全部初始化:int a[5] = {1,2,3,4,5};
    • 部分初始化:int a[5] = {1,2}; (剩下的元素自动为 0)
    • 不指定长度:int a[] = {1,2,3}; (长度自动为 3)

二维数组(高频考点)

  • 定义格式:

    1
    类型名 数组名[行数][列数];

    例:

    1
    int a[2][3];(2 行 3 列,共 6 个元素)
  • 存储顺序:行优先(先存第 1 行,再存第 2 行……)

  • 元素引用:

    1
    a[行下标][列下标],下标同样从 0 开始

    例:

    1
    a[0][1]是第 1 行第 2 列的元素
  • 初始化:

    • 按行初始化:int a[2][3] = {{1,2,3}, {4,5,6}};
    • 省略行长度:int a[][3] = {1,2,3,4,5,6}; (行数自动为 2)

数组与指针(核心难点)

  • 数组名的本质:数组名是数组的首地址,等价于 &数组名[0]

    例:

    1
    int a[5];,则 a就是 &a[0](第 1 个元素的地址)
  • 指针移动:

    1
    a+1是第 2 个元素的地址(&a[1]),*(a+1)等价于 a[1]

    例:

    1
    2
    int a[5] = {1,2,3,4,5};
    printf("%d", *(a+2)); // 结果:3(等价于a[2])

    数组作为函数参数(程序阅读常考)

    • 形参写法:可以写成 int arr[] 或 int *arr(本质都是指针)

    • 传递规则:传递的是数组的首地址,函数里修改数组元素会影响实参

    • 例子:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      void fun(int arr[], int n) {
      arr[0] = 100; // 修改会影响实参
      }
      int main() {
      int a[5] = {1,2,3,4,5};
      fun(a, 5);
      printf("%d", a[0]); // 结果:100
      return 0;
      }

一句话记牢

  • 一维数组下标从 0 开始,部分初始化剩下的为 0;
  • 二维数组行优先存储,数组名是首地址;
  • 数组传参传地址,函数里修改会影响实参。

顺序表

线性表的定义

1
2
3
4
5
#define MaxSize 100  // 最大容量,固定写100就行
typedef struct {
int data[MaxSize]; // 存数据的数组
int length; // 当前有效元素个数
} SqList;

初始化操作

1
2
3
void initList(SqList *L) {
L->length = 0; // 空表标志:长度设为0
}

✅记忆锚点:空表就是 length == 0

插入操作

核心需求:在顺序表 L 的第 i 个下标位置,插入元素 e

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int listInsert(SqList *L, int i, int e) {
// 1. 判边界:满了不能插,下标不合法不能插(length最多99,所以i也是)
if (L->length == MaxSize || i < 0 || i > L->length) return 0;

// 2. 后移给位置:从最后一个元素开始,挨个往后挪
for (int j = L->length - 1; j >= i; j--) {
L->data[j+1] = L->data[j];
}

// 3. 放元素
L->data[i] = e;

// 4. 改长度
L->length++;

return 1; // 插入成功返回1
}

✅ 记忆锚点:完全对应口诀「满了不能插,后移给位置,放完加长度」

⚠️ 考试必写:j 从 length-1 开始,从后往前挪,别写反!

删除操作

核心需求:删除顺序表 L 中下标为 i 的元素,用 e 带回被删的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int listDelete(SqList *L, int i, int *e) {
// 1. 判边界:空了不能删,下标不合法不能删
if (L->length == 0 || i < 0 || i > L->length - 1) return 0;

// 2. 带回被删值(可选,题目要就写)
*e = L->data[i];

// 3. 前移补空位:从i+1开始,挨个往前挪
for (int j = i + 1; j < L->length; j++) {
L->data[j-1] = L->data[j];
}

// 4. 改长度
L->length--;

return 1; // 删除成功返回1
}

✅ 记忆锚点:对应口诀「空了不能删,前移补空位,删完减长度」

⚠️ 考试必写:j 从 i+1 开始,从前往后挪,别写反!

按下标查找 / 修改

查找:直接返回下标 i 的元素

1
2
3
int getElem(SqList L, int i) {
return L.data[i]; // 随机访问,一步到位
}

修改:直接把下标 i 的元素改成 e

1
2
3
void setElem(SqList *L, int i, int e) {
L->data[i] = e;
}

✅ 记忆锚点:靠「随机访问」特性,和数组 a[i] 用法完全一样

遍历操作

1
2
3
4
5
6
void printList(SqList L) {
for (int i = 0; i < L.length; i++) {
printf("%d ", L.data[i]);
}
printf("\n");
}

✅ 记忆锚点:循环从 0 到 length-1,挨个输出

单链表

定义

1
2
3
4
5
// 节点结构体(核心中的核心)
typedef struct LNode {
int data; // 节点存的数据
struct LNode *next;// 创建一个节点结构体的指针next,可用来保存指向下一个节点的地址
} LNode, *LinkList; // 重命名LNode=节点结构体类型,LinkList=重命名节点结构体指针类型

初始化(空链表)

LinkList本身是一个指针,但是没有指向,LinkList *L保存了外面指针的地址,解L可找到外面的指针的地址,并将初始化的头节点的地址保存到外面的指针中

1
2
3
4
5
// 初始化空链表(带头节点)
void initList(LinkList *L) {//这是二级指针
*L = (LNode *)malloc(sizeof(LNode)); // 申请头节点内存
(*L)->next = NULL; // 头节点next指向NULL,空表
}

头插法建表(高频,简单易记)

1
2
3
4
5
6
7
// 头插法插入元素e
void headInsert(LinkList L, int e) {
LNode *p = (LNode *)malloc(sizeof(LNode)); // 新建节点
p->data = e; // 存数据
p->next = L->next; // 第一步:新节点连头节点的next
L->next = p; // 第二步:头节点连新节点
}

尾插法建表(最常用,编程题必考)

1
2
3
4
5
6
7
8
// 尾插法插入元素e(L是头节点,tail是尾节点指针)
void tailInsert(LinkList L, int e, LNode *tail) {
LNode *p = (LNode *)malloc(sizeof(LNode));
p->data = e;
p->next = NULL; // 新节点是尾节点,next=NULL
tail->next = p; // 原尾节点连新节点
tail = p; // 更新尾节点
}

按位置删除节点(核心难点,记指针修改顺序)

1
2
3
4
5
6
// 删除节点p的后继节点(p是前驱节点)
void deleteNode(LNode *p) {
LNode *q = p->next; // q是要删的节点
p->next = q->next; // 前驱连后继的后继,跳过q
free(q); // 释放内存
}

题型 2:综合题(尾插法 + 删除节点,编程题核心)

题目要求

编写 C 语言程序,完成以下功能:

  1. 初始化一个带头节点的单链表;
  2. 用尾插法依次插入元素 5、15、25、35、45;
  3. 删除链表中第 3 个位置的节点(下标从 0 开始,第 3 个节点是 25);
  4. 遍历并输出最终的链表元素;
  5. 考点:尾插法正序特性、删除节点的指针操作、尾指针的使用。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include<stdio.h>
#include<stdlib.h>

//定义
typedef struct LNode{
int data;
struct LNode *next;
}LNode, *LinkList;

//初始化
void AAA(LinkList *L) {
*L = (LNode*)malloc(sizeof(LNode));
(*L)->next = NULL;

}

//头插法
void BBB(LinkList L,int i) {
LNode* P = (LNode*)malloc(sizeof(LNode));
P->data = i;
P->next = L->next;
L->next = P;
}

//尾插法
void DDD(LinkList L, int i, LNode** t) {
LNode* P = (LNode*)malloc(sizeof(LNode));
P->data = i;
P->next = NULL;
(*t)->next = P;
*t = P;
}
//删除
int EEE(LinkList L,int i) {
LNode* P = L;
int len = 1;
while (P != NULL&&len<i) {
P = P->next;
len++;
};
if (P == NULL || P->next == NULL)
{
return 0;
};
LNode* Q = P->next;

P->next = Q->next;
free(Q);
return 1;


}

//遍历
void CCC(LinkList L) {

LNode *P=L->next;
while (P != NULL)
{
printf("%d\n", P->data);
P = P->next;
}

}

int main() {
LinkList L;
AAA(&L);
LNode* T = L;
DDD(L,5,&T);
DDD(L, 15, &T);
DDD(L, 25, &T);
DDD(L, 35, &T);
DDD(L, 45, &T);
EEE(L, 3);
CCC(L);



}

栈和队列

栈(Stack):后进先出的 “弹夹”

1. 核心定义与特性(选择填空必考)

  • 定义:只能在栈顶(Top) 进行插入(入栈)和删除(出栈)操作的线性表;
  • 核心特性:后进先出(LIFO, Last In First Out);
  • ✅ 生活类比:弹夹 —— 最后压进去的子弹,第一个打出来。

2. 顺序栈的存储结构(专升本只考顺序栈)

用数组实现,定义两个变量:

  • data[MaxSize]:存栈中元素的数组;
  • top:栈顶指针,指向当前栈顶元素的位置(初始为-1,代表空栈)。

3. 顺序栈核心操作(极简代码,记逻辑)

(1)初始化

1
2
3
4
5
6
7
8
9
#define MaxSize 100
typedef struct {
int data[MaxSize];
int top; // 栈顶指针
} SqStack;

void initStack(SqStack *S) {
S->top = -1; // 空栈标志:top=-1
}

(2)入栈(Push)

1
2
3
4
5
6
int push(SqStack *S, int e) {
if (S->top == MaxSize - 1) return 0; // 栈满
S->top++; // 栈顶指针上移
S->data[S->top] = e; // 元素入栈
return 1;
}

✅ 记忆口诀:先上移,再放元素

(3)出栈(Pop)

1
2
3
4
5
6
int pop(SqStack *S, int *e) {
if (S->top == -1) return 0; // 栈空
*e = S->data[S->top]; // 取出栈顶元素
S->top--; // 栈顶指针下移
return 1;
}

✅ 记忆口诀:先取元素,再下移

4. 栈的典型应用(选择题高频)

  • 括号匹配:判断()[]{}是否合法;
  • 表达式求值:中缀转后缀、后缀表达式计算;
  • 递归调用:函数调用栈;
  • 数制转换:十进制转二进制。

二、队列(Queue):先进先出的 “排队”

1. 核心定义与特性(选择填空必考)

  • 定义:只能在队尾(Rear) 插入(入队),在队头(Front) 删除(出队)的线性表;
  • 核心特性:先进先出(FIFO, First In First Out);
  • ✅ 生活类比:排队买饭 —— 先来的先买,后来的站后面。

2. 循环队列(专升本只考循环队列,解决假溢出)

用数组实现,定义两个指针:

  • data[MaxSize]:存队列元素的数组;
  • front:队头指针,指向队头元素的前一个位置(初始为0);
  • rear:队尾指针,指向队尾元素的位置(初始为0)。

3. 循环队列核心考点(超级高频,记死公式)

(1)队空 / 队满判断(必考选择填空)

  • 队空:front == rear;
  • 队满:(rear + 1) % MaxSize == front(牺牲一个位置,避免和队空混淆);
  • ✅ 记忆口诀:队空头尾相等,队满尾 + 1 模 Max 等于头

(2)入队 / 出队操作(记指针变化)

  • 入队:rear = (rear + 1) % MaxSize;(队尾指针后移,取模防越界);
  • 出队:front = (front + 1) % MaxSize;(队头指针后移,取模防越界)。

4. 队列的典型应用(选择题高频)

  • 广度优先搜索(BFS):图 / 树的层序遍历;
  • 系统缓冲区:打印机队列、消息队列;
  • 排队场景:银行叫号、食堂排队。

三、栈 vs 队列 对比记忆(表格,考前看一眼)

维度 栈(Stack) 队列(Queue)
核心特性 后进先出(LIFO) 先进先出(FIFO)
操作位置 仅栈顶(Top) 队尾入、队头出
空标志 top == -1(顺序栈) front == rear(循环队列)
满标志 top == MaxSize-1 (rear+1)%MaxSize == front
典型应用 括号匹配、递归 BFS、缓冲区

树与二叉树

一、树的基本概念(选择填空)

  1. 树:一对多的层次结构。
  2. 结点:树上的一个元素。
  3. 度:一个结点有几个孩子,度就是几。
  4. 叶子结点:度 = 0 的结点(没有孩子)。
  5. 树的度:树中最大的结点度。
  6. 深度 / 高度:
    • 根在第 1 层,深度 = 层数。

二、二叉树(考试 90% 都考它)

1.定义

  • 每个结点最多 2 个孩子(左孩子、右孩子)
  • 左、右孩子不能颠倒(有序树)

2. 二叉树 5 大性质(必考!背下来!)

  1. 第 i 层最多有:2^(i-1) 个结点

  2. 深度 k 的二叉树最多:2^k - 1 个结点

  3. 叶子结点数 n0 = n2 + 1

    (叶子 = 度为 2 的结点数 + 1)

  4. n 个结点的二叉树,最小深度:⌈log₂(n+1)⌉例如:n=5,2^2=4 <5+1 <2^3=8,h=3

  5. 完全二叉树,双亲下标:i/2,左孩子:2i,右孩子:2i+1

三、两种特殊二叉树(必认)

1. 满二叉树

  • 每一层都满
  • 总结点:2^k - 1

2. 完全二叉树

  • 只有最后一层不满
  • 缺的结点都靠右
  • 前面每一层都满

四、二叉树遍历(超级重点!必考!)

给你最简单、最不会错的记法:

  • 先序:根 → 左 → 右

  • 中序:左 → 根 → 右

  • 后序:左 → 右 → 根

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    // 二叉树节点结构体(必写)
    typedef struct BiTNode {
    int data; // 节点的值(盒子里的纸条)
    struct BiTNode *lchild; // 左孩子(左盒子)
    struct BiTNode *rchild; // 右孩子(右盒子)
    } BiTNode, *BiTree;

    // 1. 先序遍历(根→左→右)
    void PreOrder(BiTree T) {
    if (T != NULL) {
    printf("%d ", T->data); // 先拆自己(根)
    PreOrder(T->lchild); // 再拆左盒子
    PreOrder(T->rchild); // 最后拆右盒子
    }
    }

    // 2. 中序遍历(左→根→右)
    void InOrder(BiTree T) {
    if (T != NULL) {
    InOrder(T->lchild); // 先拆左盒子
    printf("%d ", T->data); // 再拆自己(根)
    InOrder(T->rchild); // 最后拆右盒子
    }
    }

    // 3. 后序遍历(左→右→根)
    void PostOrder(BiTree T) {
    if (T != NULL) {
    PostOrder(T->lchild); // 先拆左盒子
    PostOrder(T->rchild); // 再拆右盒子
    printf("%d ", T->data); // 最后拆自己(根)
    }
    }

考试必考结论

  1. 先序 + 中序 可以唯一确定二叉树
  2. 后序 + 中序 可以唯一确定二叉树
  3. 先序 + 后序 不能唯一确定二叉树

五、哈夫曼树(选择题必考)

1. 构造方法

每次选两个最小权值的结点合并,生成新结点。

2. 性质

  • 叶子结点:n 个
  • 总结点:2n - 1
  • 没有度为 1 的结点

3. WPL 带权路径长度

每个叶子的 权值 × 路径长度 全部加起来。