二级 C 语言程序设计
科目 24 · 考点与真题
二级 C 语言不是只背语法就能通过的考试——它考的是 数据类型与运算符 + 三大程序结构 + 数组 + 函数 + 指针 + 字符串/结构体/文件 + 编译预处理 + 公共基础知识。 其中真正决定通过与否的是 指针、数组、函数、循环 的代码能力。 本页按官方大纲组织知识点,并收录 2021–2026 年真题与样题。
选择题 40 分中含 公共基础知识 10 分;操作题 60 分 = 程序填空题 18 分 + 程序修改题 18 分 + 程序设计题 24 分。
C 语言概述
理论 · 选择题为主大纲要求:C 程序的结构、main 函数、注释、标识符命名规则、程序的开发流程。
最小可运行的 C 程序(必须默写)
C#include <stdio.h> /* 预处理命令:引入标准输入输出库 */ int main(void) /* main 函数:程序入口,有且仅有一个 */ { int a = 10, b = 20; /* 变量:先定义,后使用 */ int sum; sum = a + b; /* 每条语句以分号结尾 */ printf("sum = %d\n", sum); /* 注释不参与编译 */ return 0; }
C 程序的开发流程(必背)
必懂概念清单
- 程序从
main函数开始执行 - 一个程序有且仅有一个 main 函数
- 语句以分号
;结尾 - 注释
/* */与//,不能嵌套
- C 程序的基本组成单位是函数
- 函数可以嵌套调用,不能嵌套定义
- C 语言区分大小写
- 每行可以有多条语句
int、case)。printf、scanf
是库函数名,可以作标识符。
数据类型与运算符
选择题高频 · ★★★★★大纲要求:基本数据类型、常量与变量、运算符与表达式、类型转换。这是选择题出题密度最高的一章。
基本数据类型
| 类型 | 关键字 | 字节(常见) | 说明 |
|---|---|---|---|
| 整型 | int | 4 | 还有 short(2)、long、unsigned |
| 字符型 | char | 1 | 本质是小整数,可参与算术运算 |
| 单精度 | float | 4 | 约 7 位有效数字 |
| 双精度 | double | 8 | 约 16 位有效数字 |
常量的写法(易错点集中)
C/* 整型常量:三种进制,注意——没有二进制写法! */ 12 /* 十进制 */ 012 /* 八进制:以 0 开头,等于十进制 10 */ 0x12 /* 十六进制:以 0x 开头,等于十进制 18 */ /* 实型常量 */ 3.14 .5 5. 1.2e3 /* e 前必须有数字,e 后必须是整数 */ /* 字符常量与字符串常量 */ 'a' '\n' '\x41' /* 占 1 字节 */ "abc" /* 占 4 字节:a b c \0 */
'a' 占 1 字节,"a" 占 2 字节;1e2 合法,e2、1e2.5 非法。常用转义字符
| 写法 | 含义 | 写法 | 含义 |
|---|---|---|---|
\n | 换行 | \\ | 反斜杠本身 |
\t | 水平制表 | \' | 单引号 |
\0 | 空字符(串尾) | \" | 双引号 |
\101 | 八进制转义 → 'A' | \x41 | 十六进制转义 → 'A' |
运算符与优先级(速查)
| 类别 | 运算符 | 必考要点 |
|---|---|---|
| 算术 | + - * / % | % 两侧必须为整数;5/2=2;5.0/2=2.5;-5/2=-2(向零取整) |
| 自增自减 | ++ -- | 前置:先加后用;后置:先用后加 |
| 关系 | > < >= <= == != | 结果为 1(真)或 0(假);= 与 == 别混 |
| 逻辑 | ! && || | 短路求值:a && b 中 a 为假则不算 b |
| 位运算 | & | ^ ~ << >> | 按二进制位运算,如 5|3=7、5&3=1、5^3=6 |
| 赋值 | = += -= *= /= %= | 结合性自右向左;a=b=c=5 合法 |
| 条件 | ? : | max = (a>b) ? a : b; |
| 逗号 | , | 依次求值,整个表达式取最后一个的值 |
| 求字节 | sizeof | sizeof(int)=4;是运算符不是函数 |
类型转换
Cint i = 3.9; /* 自动转换:舍去小数 → i = 3(不是四舍五入) */ double d = (double)5 / 2; /* 强制转换 → 2.5 */ /* 自动提升方向:char/short → int → unsigned → long → float → double */
输入输出
printf/scanf · ★★★★★大纲要求:格式化输入输出、字符输入输出。printf 和 scanf 是操作题中出现率最高的库函数,格式符必须一一对应。
printf 格式符速查
| 格式符 | 用途 | 示例 | 输出 |
|---|---|---|---|
%d | 十进制整数 | printf("%d", 123); | 123 |
%f | 小数(默认 6 位) | printf("%f", 3.14); | 3.140000 |
%.2f | 保留 2 位小数 | printf("%.2f", 3.14159); | 3.14 |
%c | 单个字符 | printf("%c", 'A'); | A |
%s | 字符串 | printf("%s", "hi"); | hi |
%o / %x | 八进制 / 十六进制 | printf("%x", 255); | ff |
%u | 无符号十进制 | printf("%u", 10); | 10 |
%% | 输出 % 本身 | printf("100%%"); | 100% |
附加格式:%5d 宽度 5 右对齐(左补空格)、%-5d 左对齐、%5.2f 宽度 5 精度 2。
scanf 注意事项(每年必考)
Cint a; float x; char s[20]; scanf("%d", &a); /* 普通变量前必须加取地址符 & */ scanf("%d%f", &a, &x); /* 输入时用空格分隔:12 3.5 */ scanf("%d,%d", &a, &b); /* 格式串有逗号,输入也必须输逗号 */ scanf("%s", s); /* 数组名本身就是地址,不加 & */
%f 对应 float、%lf 对应 double;
%c 会把空格和回车也读进去(连续输入字符要小心残留回车)。
字符输入输出
Cchar c; c = getchar(); /* 读一个字符(含回车) */ putchar(c); /* 输出一个字符 */ gets(s); /* 读一行字符串(可含空格) */ puts(s); /* 输出字符串并换行 */
亲手试一试 · printf 格式符演示
格式符与输出项个数、类型必须一一对应——这是读程序题的高频出题点。
选择结构
if / switch · ★★★★大纲要求:if 语句的三种形式、嵌套 if、switch 语句、条件运算符。
Cif (score >= 90) printf("A"); else if (score >= 80) printf("B"); else printf("C"); /* 条件运算符(唯一的三目运算符) */ max = (a > b) ? a : b; /* switch:表达式只能是整型或字符型 */ switch (grade) { case 'A': printf("优"); break; case 'B': printf("良"); break; default : printf("中"); }
break 会继续向下"贯穿"执行;③ if(x=5) 是赋值,恒为真;
④ else 总是与它前面最近的、未配对的 if 配对(缩进不代表配对关系)。
循环结构
操作题核心 · ★★★★★大纲要求:for、while、do-while 三种循环,break 与 continue,循环嵌套。所有操作题都建立在循环之上。
三种循环对比
| for | while | do-while | |
|---|---|---|---|
| 执行顺序 | 先判断后执行 | 先判断后执行 | 先执行后判断 |
| 最少执行次数 | 0 次 | 0 次 | 1 次 |
| 适合场景 | 次数已知 | 次数不定 | 至少执行一次 |
Cint i, sum = 0; for (i = 1; i <= 100; i++) /* 初值; 条件; 步进 */ sum += i; /* 1+2+…+100 = 5050 */ i = 1; while (i <= 100) { sum += i; i++; } do { sum += i; i++; } while (i <= 100); /* 注意末尾分号! */
break 与 continue
Cfor (i = 1; i <= 10; i++) { if (i % 2 == 0) continue; /* 跳过本次循环体,进入下一次循环 */ if (i > 7) break; /* 终止整个循环 */ printf("%d ", i); /* 输出:1 3 5 7 */ }
经典必刷题型:判断素数
isPrime —— 操作题、填空题双高频
Cint isPrime(int n) { int i; if (n < 2) return 0; for (i = 2; i * i <= n; i++) /* 只需试除到 √n */ if (n % i == 0) return 0; /* 找到因数 → 不是素数 */ return 1; /* 没有因数 → 是素数 */ }
思路:素数 = 只能被 1 和自身整除。设一个标志位或直接 return,是填空题最爱挖空的位置。
其他必须会写的经典循环
- 累加 ∑ 与累乘 n!(初值 0 / 1)
- Fibonacci 数列:1 1 2 3 5 8…
- 水仙花数:153 = 1³+5³+3³
- 最大公约数:辗转相除法
- 九九乘法表(双重循环)
- 统计位数 / 逆序输出整数
- 求数组最大值 / 最小值 / 平均值
- 判断回文串
do {…} while(条件); 末尾有分号;for(;;) 和 while(1) 都是死循环;循环体内修改循环变量是常见的"输出几行"考题。数组
操作题核心 · ★★★★★大纲要求:一维数组、二维数组的定义/初始化/引用,数组与循环结合的常用算法(排序、查找、逆置、移动)。
定义与初始化
Cint a[5] = {1, 2, 3, 4, 5}; /* 下标范围 0 ~ 4 */ int b[5] = {1, 2}; /* 未初始化的元素自动为 0 */ int c[] = {1, 2, 3}; /* 长度自动确定为 3 */ int m[2][3] = {{1,2,3},{4,5,6}}; /* 二维数组:按行存放 */ m[1][2] = 6; /* 第 2 行第 3 列 */
经典必刷:冒泡排序(填空题最爱)
Cvoid sort(int a[], int n) { int i, j, t; for (i = 0; i < n - 1; i++) /* 共 n-1 趟 */ for (j = 0; j < n - 1 - i; j++) /* 每趟少比一次 */ if (a[j] > a[j + 1]) { /* 前大后小则交换 */ t = a[j]; a[j] = a[j+1]; a[j+1] = t; } }
其他高频算法
- 求最值:
if (a[i] > max) max = a[i]; - 数组逆置:首尾交换
for (i=0; i<n/2; i++) swap(a[i], a[n-1-i]) - 插入 / 删除元素、数组元素移动(操作题常客)
- 杨辉三角、矩阵转置(二维数组)
函数
操作题核心 · ★★★★★大纲要求:函数定义/声明/调用、参数传递、递归、变量的作用域与生存期。操作题的三大题型全部围绕"函数"展开。
定义、声明与调用
Cint add(int x, int y) /* 形参 */ { return x + y; } int main(void) { int s = add(3, 5); /* 实参:把 3、5 的值复制给形参 */ return 0; }
递归(选择题年年考)
Clong fac(int n) /* n 的阶乘 */ { if (n <= 1) return 1; /* 递归出口:必须有! */ return n * fac(n - 1); /* 自己调用自己 */ } /* fac(4) = 4*fac(3) = 4*3*fac(2) = 4*3*2*fac(1) = 24 */
变量的作用域与生存期
| 变量 | 作用域 | 生存期 | 要点 |
|---|---|---|---|
| 局部变量 | 所在函数/复合语句内 | 函数调用期间 | 不同函数中同名互不影响 |
| 全局变量 | 定义处到文件末尾 | 整个程序运行期间 | 与局部变量同名时局部优先 |
static 局部 | 函数内 | 整个程序运行期间 | 只初始化一次,值保留到下次调用 |
void。
指针
重中之重 · ★★★★★大纲要求:指针的定义与引用、指针与数组、指针作函数参数。指针是二级 C 区分度最大的考点,操作题离不开它。
基本用法
Cint a = 10; int *p = &a; /* p 存放 a 的地址;此处 * 表示"指针" */ *p = 20; /* 此处 * 表示"解引用",等价于 a = 20 */ printf("%d %d", a, *p); /* 输出 20 20 */
指针与数组(核心中的核心)
Cint a[5] = {1,2,3,4,5}, *p; p = a; /* 等价于 p = &a[0],数组名 = 首地址 */ /* 以下四种写法完全等价,都表示 a[2] */ a[2] *(a + 2) p[2] *(p + 2)
传地址:让函数真正改变外面的变量
Cvoid swap(int *x, int *y) { int t = *x; *x = *y; *y = t; } /* 调用:swap(&a, &b); —— 传地址,a、b 真正交换 */ /* 错误:swap(a, b); —— 传值,只交换了形参 */
int *p; *p = 10; 是错的——p 没指向任何地址(野指针);
② *p++ 等价于 *(p++):取值后指针后移,a 不变;
③ (*p)++ 才是 a 自增;④ 数组名是地址常量,不能 a++。
字符串
字符数组 · ★★★★大纲要求:字符数组与字符串的关系、字符串结束标志、常用字符串处理函数、手动实现字符串算法。
字符串的存储(结束标志 '\0')
Cchar s[10] = "hello"; /* 总共占 10 字节,有效 5 + \0 */ char t[] = "hello"; /* 长度自动为 6(含 \0) */
| 下标 | 0 | 1 | 2 | 3 | 4 | 5 |
|---|---|---|---|---|---|---|
| 内容 | 'h' | 'e' | 'l' | 'l' | 'o' | '\0' |
常用字符串函数(需 #include <string.h>)
| 函数 | 功能 | 返回值 / 说明 |
|---|---|---|
strlen(s) | 求长度 | 不含 '\0';"hello" → 5 |
strcpy(s1, s2) | 复制 | 把 s2 复制到 s1(s1 必须足够大) |
strcat(s1, s2) | 连接 | 把 s2 接到 s1 末尾 |
strcmp(s1, s2) | 比较 | 相同返回 0;s1>s2 返回正值;s1<s2 返回负值 |
手动实现(操作题高频)
求长度 · 逆序 —— 填空/设计题经典
Cint myLen(char s[]) /* 求字符串长度 */ { int i = 0; while (s[i] != '\0') i++; return i; } void reverse(char s[]) /* 字符串逆置 */ { int i = 0, j = myLen(s) - 1; char t; while (i < j) { t = s[i]; s[i] = s[j]; s[j] = t; i++; j--; } }
strlen("abc\0def") = 3(遇 '\0' 即结束);
对 char s[]="abcdef":sizeof(s)=7 而 strlen(s)=6;
判断字符串相等要用 strcmp,s1 == s2 比较的是地址。
结构体与文件
操作题常考 · ★★★★大纲要求:结构体类型的定义与使用、typedef、链表概念、共用体、文件的基本读写操作。
结构体
Cstruct Student { int num; char name[20]; float score; }; struct Student s1 = {1001, "Tom", 90.5}; struct Student *p = &s1; s1.num /* 结构体变量用 . 访问成员 */ p->num /* 指针用 -> 访问,等价 (*p).num */ typedef struct Student STU; /* 类型别名 */ STU s2;
链表结点(了解结构即可)
Cstruct Node { int data; struct Node *next; /* 指向下一个结点 */ }; /* 删除 head 之后的结点:head->next = head->next->next; */
文件操作
CFILE *fp; fp = fopen("data.txt", "r"); /* 打开失败返回 NULL */ if (fp == NULL) return; /* …… 读写 …… */ fclose(fp); /* 用完必须关闭 */
| 打开方式 | 含义 | 文件不存在时 |
|---|---|---|
"r" | 只读 | 打开失败(返回 NULL) |
"w" | 只写 | 新建;已存在则清空原内容! |
"a" | 追加 | 新建 |
"r+" / "w+" / "a+" | 读写 | 同上对应规则 |
"rb" / "wb" | 二进制读写 | 配 fread / fwrite 使用 |
常用读写函数
fgetc(fp) / fputc(ch, fp)字符fgets(s, n, fp) / fputs(s, fp)字符串
fscanf(fp, "%d", &x) / fprintf(fp, "%d", x)fread / fwrite二进制块读写
feof(fp) 读到文件末尾返回非 0;fgetc 读到末尾返回 EOF(即 -1)。
忘记 fclose 是操作题常见扣分点。
编译预处理
概念与宏 · ★★★大纲要求:宏定义(不带参 / 带参)、文件包含。预处理在编译之前进行纯文本替换。
C#define PI 3.14159 /* 不带参宏:末尾不写分号! */ #define S(x) ((x) * (x)) /* 带参宏:参数和整体都要加括号 */ #include <stdio.h> /* 尖括号:到系统目录找 */ #include "myfun.h" /* 双引号:先到用户当前目录找 */ printf("%f", S(3)); /* 编译前替换为 ((3)*(3)) → 9.000000 */
#define MUL(x) x*x,则 MUL(2+3)
展开为 2+3*2+3 = 11(不是 25!)。宏只做文本替换,不做计算、不管优先级。
规范写法必须给参数加括号:((x)*(x))。
公共基础知识
选择题 10 分 · 别丢虽然只有 10 分,但属于系统复习后比较容易拿到的分数,不要忽略。
数据结构
- 栈(后进先出)/ 队列(先进先出)
- 线性表 / 树 / 二叉树
- 二叉树性质:叶子结点 n0 = n2 + 1
- 查找(二分查找 O(log₂n))/ 排序
程序设计基础
- 算法:有穷性、确定性、可行性
- 时间复杂度 / 空间复杂度
- 结构化设计:顺序、选择、循环
软件工程
- 软件生命周期
- 需求分析 → 概要设计 → 详细设计 → 测试 → 维护
- 黑盒测试 / 白盒测试
数据库基础
- 数据模型 / 关系数据库
- 关系 / 元组 / 属性 / 主键
- E-R 图:实体—联系
按重要性排序复习
不要平均用力| 优先级 | 内容 | 说明 |
|---|---|---|
| ★★★★★ | 指针 · 数组 · 函数 · 循环结构 | 操作题 60 分的核心,程序设计题几乎必考 |
| ★★★★ | 字符串 · 结构体 · 文件 · 输入输出 | 程序填空、程序修改的高频素材 |
| ★★★ | 数据类型与运算符 · 选择结构 · 预处理 | 选择题高频,表达式求值必须练熟 |
| ★★★ | 公共基础知识 | 10 分,系统复习易拿分 |
真题与样题 · 2021–2026
点击选项即判分收录 2021–2024 年考生回忆版真题、2025 年官方样题,以及依据最新大纲编写的 2026 模拟预测题。 选择题点击选项立即判分;操作题点击「查看参考答案」展开解析。
Cdouble fun(int n) { double f = ___(1)___; /* 填:1(或 1.0) */ int i; for (i = 1; ___(2)___; i++) /* 填:i <= n */ ___(3)___; /* 填:f *= i (或 f = f * i) */ return f; }解析:累乘器初值必须是
1(写成 0 结果恒为 0);循环条件 i <= n 保证最后一项不漏;
累乘用 f *= i。这三个空是"初值—条件—累乘"经典三连挖空。
Cdouble fun(int n) { double s = 0.0; int i; for (i = 1; i < n; i++) /* found:应改为 i <= n */ s = s + 1 / i; /* found:应改为 1.0 / i */ return s; }解析:①
1/i 中两边都是 int,结果为整数除法(除 i=1 外全为 0),必须写成
1.0/i 或 (double)1/i;② i < n 漏加了最后一项 1/n,应改为 i <= n。
"整数除法丢小数"与"循环边界差一"是修改题最经典的两种错误。
Cvoid fun(int a, int b, long *c) { *c = (a / 10) * 1000 /* a 的十位 → 千位 */ + (b / 10) * 100 /* b 的十位 → 百位 */ + (a % 10) * 10 /* a 的个位 → 十位 */ + (b % 10); /* b 的个位 → 个位 */ }解析:拆位用整除
/10 取十位、取余 %10 取个位;
注意形参 c 是指针,结果要通过 *c 带回主函数。验证:4000+100+50+2 = 4152 ✓。
int a=3, b=2, c=1;,以下选项中错误的赋值表达式是?(b=4) 是赋值表达式,不能再被赋值。a=b=c+1 因结合性自右向左而合法。int k=0;,以下四个表达式中,与其他三个表达式值不相同的是?int m[]={5,4,3,2,1}, i=4;,下列对 m 数组元素的引用中错误的是?char s1[]="abcdef", s2[]={'a','b','c','d','e','f'};,则下列叙述正确的是?int a=1, *p=&a;,下列语句中不能使 a 的值变为 2 的是?int x=3; do { printf("%d,", x-=2); } while (!(--x));static 局部变量,下列叙述正确的是?struct S { int x; int y; } d[2]={{1,2},{3,4}};,则表达式 d[1].y - d[0].x 的值是?feof(fp) 的返回值是?while(!feof(fp))。Cvoid fun(int a[], int *n) { int i, k = 0; for (i = 1; i <= 1000; i++) if ((i % 7 == 0 || i % 11 == 0) && i % 77 != 0) a[k++] = i; *n = k; }解析:"能被 7 或 11 整除"用
||,"不能同时整除"即不是 77 的倍数 i % 77 != 0;
个数通过指针 *n 带回。"筛选类"程序设计题的通用套路:循环 + 条件筛选 + 计数器。
f(4) 的返回值是?int f(int n) { if (n==1) return 1; return n * f(n-1); }5 | 3 的值是?5&3=1(按位与),5^3=6(按位异或),5<<1=10(左移一位相当于乘 2)。strcmp(s1, s2),当两个字符串相同时,返回值是?strcmp(s1,s2)==0,不能写 s1==s2(那比较的是地址)。#define MUL(x) x*x,则表达式 MUL(2+3) 的值是?#define MUL(x) ((x)*(x))。宏只做文本替换、不做计算——这是预处理章头号陷阱。struct Node { int data; struct Node *next; };,head 指向首结点。要删除 head 之后的那个结点(该结点存在),正确的语句是?int a[3][3]={{1,2,3},{4,5,6},{7,8,9}}, i, s=0;
for (i=0; i<3; i++) s += a[i][2-i];
printf("%d", s);Cint fun(int m) { int k; for (k = 2; k <= m - 1; k++) if (m % k = 0) /* found:应改为 m % k == 0 */ return 0; return; /* found:应改为 return 1; */ }解析:①
= 是赋值,判断相等必须写 ==——这是修改题出现率第一的错误;
② 函数返回类型为 int 且约定"是素数返回 1",空 return 无法返回整数,应写 return 1;。