C语言二级考试通关笔记

前言:值不值得考?

在所有二级证书中,C语言是我最推荐的一个,因为它会逼迫你去试着理解编程思维内存管理。准确来说,只要你真的学习c语言,就无法避开这些,哪怕是二级考试这种简单的考试,因而它一定程度上会逼迫你去理解和学习,虽然二级证书整体专业性一般,但是这已经是大学生除了蓝桥杯等io竞赛外最容易拿到的专业证书,而考虑到蓝桥杯这些比赛,如果希望拿奖,准备时间不会低于两个月,只需要14天就能稳定通过的二级考试性价比可以说极高。

在大厂实习期间我发现,面试官特别喜欢问C语言相关的底层问题。即使你应聘的是Java开发岗位,他们也会问指针、内存泄漏、段错误等概念,因为这些反映了你对计算机系统的理解深度。

备考用到的题库:c.code2ji.cn


第一章:C语言基础语法

1.1 C语言的特点

C语言有几个显著特点:

  • 高效简洁:代码简短,执行效率高
  • 贴近硬件:可以直接操作内存和硬件
  • 可移植性强:在不同平台上都能运行
  • 功能强大:系统编程的首选语言

1.2 数据类型 - 精确控制内存

C语言数据类型分类:

// 基本数据类型
char         // 字符类型,1字节
int          // 整数类型,通常4字节
float        // 单精度浮点数,4字节
double       // 双精度浮点数,8字节

// 修饰符
short        // 短整型,2字节
long         // 长整型,4或8字节
signed       // 有符号(默认)
unsigned     // 无符号

// 空类型
void         // 空类型

数据类型详细说明:

#include <stdio.h>
#include <limits.h>   // 整数限制
#include <float.h>    // 浮点数限制

int main() {
    // 字符类型
    char c1 = 'A';              // 字符常量
    char c2 = 65;               // ASCII值
    signed char sc = -128;      // 有符号字符:-128到127
    unsigned char uc = 255;     // 无符号字符:0到255
    
    // 整数类型
    short s = 32767;            // 短整型:-32768到32767
    int i = 2147483647;         // 整数:-2^31到2^31-1
    long l = 2147483647L;       // 长整型,常数后加L
    unsigned int ui = 4294967295U; // 无符号整数:0到2^32-1
    
    // 浮点数类型
    float f = 3.14159f;         // 单精度,常数后加f
    double d = 3.141592653589793; // 双精度
    long double ld = 3.141592653589793L; // 扩展精度
    
    // 输出信息
    printf("字符: c1='%c'(%d), c2='%c'(%d)\n", c1, c1, c2, c2);
    printf("整数: short=%d, int=%d, long=%ld\n", s, i, l);
    printf("浮点: float=%.6f, double=%.15f\n", f, d);
    
    // 数据类型大小
    printf("\n数据类型大小:\n");
    printf("char: %zu 字节\n", sizeof(char));
    printf("short: %zu 字节\n", sizeof(short));
    printf("int: %zu 字节\n", sizeof(int));
    printf("long: %zu 字节\n", sizeof(long));
    printf("float: %zu 字节\n", sizeof(float));
    printf("double: %zu 字节\n", sizeof(double));
    
    // 数据类型范围
    printf("\n数据类型范围:\n");
    printf("int: %d 到 %d\n", INT_MIN, INT_MAX);
    printf("unsigned int: 0 到 %u\n", UINT_MAX);
    printf("float: %e 到 %e\n", FLT_MIN, FLT_MAX);
    
    return 0;
}

变量声明和初始化:

// 变量声明语法
数据类型 变量名;
数据类型 变量名 = 初始值;

// 实例
int age;                    // 只声明
age = 20;                   // 赋值
int score = 85;             // 声明同时初始化

// 多个变量声明
int a, b, c;                // 声明多个变量
int x = 1, y = 2, z = 3;    // 声明并初始化

// 常量声明
const int MAX_SIZE = 100;   // const关键字声明常量
#define PI 3.14159          // 宏定义常量

// 存储类型
auto int local_var = 10;    // 自动变量(局部)
static int count = 0;       // 静态变量
register int fast_var = 5;  // 寄存器变量
extern int global_var;      // 外部变量声明

类型转换:

#include <stdio.h>

int main() {
    // 自动类型转换(隐式转换)
    int i = 10;
    float f = i;        // int 自动转换为 float
    double d = f;       // float 自动转换为 double
    
    char c = 'A';
    int ascii = c;      // char 自动转换为 int
    
    printf("自动转换: i=%d, f=%.1f, d=%.1f, ascii=%d\n", i, f, d, ascii);
    
    // 强制类型转换(显式转换)
    double d1 = 3.14159;
    int i1 = (int)d1;           // 强制转换,丢失小数部分
    float f1 = (float)d1;       // double 转 float
    char c1 = (char)65;         // int 转 char
    
    printf("强制转换: d1=%.5f, i1=%d, f1=%.5f, c1='%c'\n", d1, i1, f1, c1);
    
    // 运算中的类型提升
    char a = 10, b = 20;
    int result = a + b;         // char 自动提升为 int
    
    float x = 3.5f;
    double y = 2.1;
    double sum = x + y;         // float 自动提升为 double
    
    printf("类型提升: result=%d, sum=%.2f\n", result, sum);
    
    return 0;
}

🔥 真题实战:去除偶数生成新数

在这里插入图片描述

这道题考查数字的位操作和指针传递:

题目要求: 从输入的正整数中提取所有奇数位,组成新整数

#include <stdio.h>

void fun(unsigned long *n) {
    unsigned long x = 0, i;    
    int t;
    i = 1;
    while(*n) {
        t = *n % 10;           // 取个位数字
        if(t % 2 != 0) {       // 判断是否为奇数
            x = x + t * i;     // 构建新数字
            i = i * 10;        // 位权递增
        }
        *n = *n / 10;          // 去掉个位
    }
    *n = x;                    // 将结果存回原变量
}

int main(void) {
    unsigned long n = -1;
    // 输入验证循环
    while(n > 99999999 || n < 0) {
        printf("Please input(0<n<100000000): "); 
        scanf("%ld", &n); 
    }
    fun(&n);                   // 传递地址
    printf("\nThe result is: %ld\n", n);
    return 0;
}

知识点解析:

  1. 指针传递fun(&n) 传递变量地址,*n 解引用访问值
  2. 数字分解n % 10 取个位,n / 10 去掉个位
  3. 奇偶判断t % 2 != 0 判断是否为奇数
  4. 数字构建:用位权i重新组合数字
  5. 输入验证:while循环确保输入在有效范围内

记忆技巧:

  • 指针:&取地址,*取值
  • 数字操作:%10取个位,/10去个位
  • 位权概念:个位权重1,十位权重10,百位权重100…

指针和数字处理常见考点:

  • 数字的位操作和分解
  • 指针传递和解引用
  • 条件判断和循环控制
  • 数字构造和组合

第二章:函数与文件操作 - 模块化编程

2.1 函数的定义与调用

C函数语法结构:

// 函数定义的完整语法
返回类型 函数名(参数类型1 参数1, 参数类型2 参数2, ...) {
    函数体;
    return 返回值;  // 可选
}

// 函数声明(原型)
返回类型 函数名(参数类型1, 参数类型2, ...);

函数定义示例:

#include <stdio.h>

// 函数声明(放在main前面)
int add(int a, int b);               // 有参数有返回值
void printMessage(void);             // 无参数无返回值
float calculateAverage(int arr[], int size); // 数组参数
int max(int x, int y);               // 比较函数
void swap(int *a, int *b);           // 指针参数

int main() {
    // 函数调用示例
    int result = add(5, 3);
    printf("加法结果: %d\n", result);
    
    printMessage();
    
    int numbers[] = {85, 92, 78, 96, 88};
    float avg = calculateAverage(numbers, 5);
    printf("平均分: %.2f\n", avg);
    
    int maxVal = max(15, 23);
    printf("最大值: %d\n", maxVal);
    
    int x = 10, y = 20;
    printf("交换前: x=%d, y=%d\n", x, y);
    swap(&x, &y);
    printf("交换后: x=%d, y=%d\n", x, y);
    
    return 0;
}

// 函数定义实现

// 1. 有参数有返回值的函数
int add(int a, int b) {
    int result = a + b;
    return result;
}

// 2. 无参数无返回值的函数
void printMessage(void) {
    printf("这是一个无参数无返回值的函数\n");
}

// 3. 数组参数函数
float calculateAverage(int arr[], int size) {
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    return (float)sum / size;
}

// 4. 条件返回函数
int max(int x, int y) {
    if (x > y) {
        return x;
    } else {
        return y;
    }
    // 可以简化为: return (x > y) ? x : y;
}

// 5. 指针参数函数(实现参数交换)
void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

函数参数传递方式:

#include <stdio.h>

// 值传递(传入参数的副本)
void changeValue(int x) {
    x = 100;  // 只改变副本,不影响原变量
    printf("函数内x = %d\n", x);
}

// 地址传递(传入参数的地址)
void changePointer(int *x) {
    *x = 100;  // 通过指针修改原变量的值
    printf("函数内*x = %d\n", *x);
}

// 数组传递(传入数组名,实际上是传入数组首元素的地址)
void modifyArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;  // 直接修改原数组元素
    }
}

int main() {
    // 测试值传递
    int a = 10;
    printf("调用前 a = %d\n", a);
    changeValue(a);
    printf("调用后 a = %d\n\n", a);  // a仍为10
    
    // 测试地址传递
    int b = 20;
    printf("调用前 b = %d\n", b);
    changePointer(&b);
    printf("调用后 b = %d\n\n", b);  // b变为100
    
    // 测试数组传递
    int numbers[] = {1, 2, 3, 4, 5};
    printf("修改前数组: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    modifyArray(numbers, 5);
    
    printf("修改后数组: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    return 0;
}

函数的递归调用:

#include <stdio.h>

// 递归计算阶乘
long factorial(int n) {
    if (n <= 1) {
        return 1;  // 递归的终止条件
    }
    return n * factorial(n - 1);  // 递归调用
}

// 递归计算斐波那契数列
long fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n - 1) + fibonacci(n - 2);
}

// 递归计算最大公约数
int gcd(int a, int b) {
    if (b == 0) {
        return a;
    }
    return gcd(b, a % b);
}

int main() {
    // 测试阶乘
    int n = 5;
    printf("%d! = %ld\n", n, factorial(n));
    
    // 测试斐波那契数列
    printf("斐波那契数列前10项: ");
    for (int i = 0; i < 10; i++) {
        printf("%ld ", fibonacci(i));
    }
    printf("\n");
    
    // 测试最大公约数
    int a = 48, b = 18;
    printf("gcd(%d, %d) = %d\n", a, b, gcd(a, b));
    
    return 0;
}

2.2 文件操作 - 数据的持久化

文件操作就像是操作现实中的文件柜,需要打开、读写、关闭三个步骤:

#include <stdio.h>

int main() {
    FILE *fp;
    
    // 打开文件进行写入
    fp = fopen("data.txt", "w");
    if (fp == NULL) {
        printf("文件打开失败!\n");
        return 1;
    }
    
    // 写入数据
    fprintf(fp, "Hello, C语言!\n");
    fprintf(fp, "数字: %d\n", 42);
    
    // 关闭文件
    fclose(fp);
    
    return 0;
}

🔥 真题实战:实型数四舍五入

在这里插入图片描述

这道题综合考查函数定义和文件操作:

题目要求: 实现浮点数四舍五入并进行文件批处理

#include <stdio.h>

// 四舍五入函数实现
float fun(float h) {
    // 核心算法:乘100,加0.5,取整,再除100
    return (int)(h * 100 + 0.5) / 100.0;
}

void NONO(void);   // 函数声明

int main(void) {
    float a;
    printf("Enter  a:  ");
    if (scanf("%f", &a) != 1) return 0;  // 输入检查

    printf("The original data is :   %f\n\n", a);
    printf("The result :  %f\n", fun(a));

    NONO();  // 调用文件处理函数
    return 0;
}

// 文件批处理函数
void NONO(void) {
    int i;
    float a;
    FILE *rf = fopen("in.txt", "r");     // 打开输入文件
    FILE *wf = fopen("out.txt", "w");    // 打开输出文件

    if (!rf || !wf) {                    // 检查文件是否成功打开
        printf("文件打开失败!\n");
        return;
    }

    // 读取并处理20个数字
    for (i = 0; i < 20 && fscanf(rf, "%f", &a) == 1; i++) {
        fprintf(wf, "%f\n", fun(a));     // 处理后写入文件
    }

    fclose(rf);  // 关闭文件
    fclose(wf);
}

知识点解析:

  1. 四舍五入算法(int)(h * 100 + 0.5) / 100.0
    • 乘100:将小数点后两位移到整数部分
    • 加0.5:实现四舍五入
    • 取整:去掉多余小数
    • 除100.0:恢复原来的小数位置
  2. 文件操作fopen() 打开,fscanf()/fprintf() 读写,fclose() 关闭
  3. 错误检查:检查文件是否成功打开,检查输入是否成功
  4. 函数分离:主函数处理交互,NONO函数处理文件批量操作

记忆技巧:

  • 四舍五入:×100 → +0.5 → 取整 → ÷100
  • 文件操作:打开 → 读写 → 关闭
  • 错误检查:每个操作都要检查返回值

函数和文件常见考点:

  • 函数的定义、声明和调用
  • 文件的打开、读写、关闭操作
  • 数学算法的实现(四舍五入等)
  • 批量数据处理和错误检查

第三章:数组与字符串 - 数据的批量处理

3.1 一维数组

数组就像是一排编了号的储物柜,每个柜子存放一个数据:

#include <stdio.h>

int main() {
    int scores[5] = {85, 92, 78, 96, 88};  // 定义并初始化
    int sum = 0;
    
    // 计算总分
    for (int i = 0; i < 5; i++) {
        sum += scores[i];
    }
    
    // 计算平均分
    double average = (double)sum / 5;
    printf("平均分: %.2f\n", average);
    
    return 0;
}

数组声明和初始化语法:

// 一维数组声明语法
数据类型 数组名[数组大小];
数据类型 数组名[数组大小] = {初始值列表};

// 实例
int numbers[5];                    // 声明5个整数的数组
int scores[5] = {85, 92, 78, 96, 88};  // 声明并初始化
int data[] = {1, 2, 3, 4};         // 自动推导大小

// 二维数组声明
int matrix[3][4];                  // 3行4列的二维数组
int grid[2][3] = {{1,2,3}, {4,5,6}}; // 声明并初始化

// 字符数组(字符串)
char str[20];                      // 声明字符数组
char name[20] = "Hello";           // 声明并初始化
char message[] = "Welcome";        // 自动推导大小

数组操作示例:

#include <stdio.h>

int main() {
    // 数组的基本操作
    int numbers[5] = {10, 20, 30, 40, 50};
    int sum = 0;
    
    // 遍历数组
    printf("数组元素: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", numbers[i]);
        sum += numbers[i];
    }
    printf("\n总和: %d\n", sum);
    
    // 修改数组元素
    numbers[0] = 100;
    printf("修改后第一个元素: %d\n", numbers[0]);
    
    // 二维数组操作
    int matrix[3][3] = {
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9}
    };
    
    printf("二维数组:\n");
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            printf("%d ", matrix[i][j]);
        }
        printf("\n");
    }
    
    return 0;
}

3.2 字符串操作 - 字符数组的特殊应用

字符串基本语法:

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

// 字符串声明和初始化
char str1[20];                    // 声明字符数组
char str2[20] = "Hello";          // 初始化字符串
char str3[] = "World";            // 自动推导大小
char str4[20] = {'H','e','l','l','o','\0'}; // 字符数组形式

// 字符串常量
char *ptr = "Hello World";        // 指向字符串常量的指针

字符串函数详解:

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

int main() {
    char name[50] = "张三";
    char greeting[100];
    char copy_str[50];
    char search_str[] = "Hello World Programming";
    
    // strlen() - 计算字符串长度
    int len = strlen(name);
    printf("字符串长度: %d\n", len);
    
    // strcpy() - 字符串复制
    strcpy(greeting, "你好, ");
    strcpy(copy_str, name);
    printf("复制结果: %s\n", copy_str);
    
    // strcat() - 字符串连接
    strcat(greeting, name);
    printf("连接结果: %s\n", greeting);  // 输出:你好, 张三
    
    // strcmp() - 字符串比较
    int result = strcmp("apple", "banana");
    if (result < 0) {
        printf("apple < banana\n");
    } else if (result > 0) {
        printf("apple > banana\n");
    } else {
        printf("apple == banana\n");
    }
    
    // strchr() - 查找字符
    char *pos = strchr(search_str, 'W');
    if (pos != NULL) {
        printf("找到字符W,位置: %ld\n", pos - search_str);
    }
    
    // strstr() - 查找子字符串
    char *sub_pos = strstr(search_str, "World");
    if (sub_pos != NULL) {
        printf("找到子字符串World,位置: %ld\n", sub_pos - search_str);
    }
    
    // strncpy() - 安全的字符串复制(指定长度)
    char safe_copy[10];
    strncpy(safe_copy, "Very Long String", 9);
    safe_copy[9] = '\0';  // 手动添加结束符
    printf("安全复制: %s\n", safe_copy);
    
    return 0;
}

字符串输入输出:

#include <stdio.h>

int main() {
    char input[100];
    
    // 使用scanf读取字符串(遇到空格停止)
    printf("请输入一个单词: ");
    scanf("%s", input);  // 注意:数组名就是地址,不需要&
    printf("输入的单词: %s\n", input);
    
    // 清空输入缓冲区
    while (getchar() != '\n');
    
    // 使用fgets读取整行(包括空格)
    printf("请输入一行文本: ");
    fgets(input, sizeof(input), stdin);
    printf("输入的文本: %s", input);  // fgets保留换行符
    
    // 使用gets读取整行(不安全,不推荐)
    // gets(input);  // 已废弃,容易缓冲区溢出
    
    return 0;
}

字符串常见考点:

  • 字符串的声明和初始化
  • strlen、strcpy、strcat、strcmp等函数
  • 字符串的遍历和处理
  • 字符数组和指针的关系

第四章:指针 - C语言的精髓

4.1 指针的基本概念和语法

指针声明语法:

// 指针声明的基本语法
数据类型 *指针名;
数据类型 *指针名 = &变量名;

// 实例
int *ptr;              // 声明一个指向int的指针
float *fptr;           // 声明一个指向float的指针
char *cptr;            // 声明一个指向char的指针

int num = 42;
int *p = &num;         // 声明并初始化指针

// 多级指针
int **pp = &p;         // 指向指针的指针
int ***ppp = &pp;      // 指向指针的指针的指针

指针基本操作:

#include <stdio.h>

int main() {
    int num = 42;      // 定义变量
    int *ptr = &num;   // 定义指针,指向num的地址
    
    printf("=== 指针基本信息 ===\n");
    printf("num的值: %d\n", num);
    printf("num的地址: %p\n", &num);
    printf("ptr的值(存储的地址): %p\n", ptr);
    printf("ptr的地址: %p\n", &ptr);
    printf("ptr指向的值: %d\n", *ptr);
    
    printf("\n=== 通过指针修改值 ===\n");
    *ptr = 100;        // 通过指针修改num的值
    printf("修改后num的值: %d\n", num);
    
    printf("\n=== 指针运算 ===\n");
    int arr[5] = {10, 20, 30, 40, 50};
    int *arr_ptr = arr;  // 数组名就是指向第一个元素的指针
    
    printf("数组首地址: %p\n", arr);
    printf("指针指向: %p\n", arr_ptr);
    
    // 指针算术运算
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d, 地址: %p\n", i, *(arr_ptr + i), arr_ptr + i);
    }
    
    printf("\n=== 多级指针 ===\n");
    int **pp = &ptr;   // 指向指针的指针
    printf("pp的值(ptr的地址): %p\n", pp);
    printf("*pp的值(ptr的值): %p\n", *pp);
    printf("**pp的值(num的值): %d\n", **pp);
    
    return 0;
}

指针与不同数据类型:

#include <stdio.h>

int main() {
    // 整型指针
    int x = 10;
    int *int_ptr = &x;
    printf("整型: *int_ptr = %d\n", *int_ptr);
    
    // 浮点型指针
    float y = 3.14f;
    float *float_ptr = &y;
    printf("浮点型: *float_ptr = %.2f\n", *float_ptr);
    
    // 字符指针
    char c = 'A';
    char *char_ptr = &c;
    printf("字符型: *char_ptr = %c\n", *char_ptr);
    
    // 字符串指针
    char *str_ptr = "Hello World";
    printf("字符串: %s\n", str_ptr);
    
    // void指针(通用指针)
    void *void_ptr;
    void_ptr = &x;
    printf("void指针指向整数: %d\n", *(int*)void_ptr);
    
    void_ptr = &y;
    printf("void指针指向浮点数: %.2f\n", *(float*)void_ptr);
    
    return 0;
}

空指针和野指针:

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

int main() {
    // 空指针
    int *null_ptr = NULL;  // 初始化为NULL
    
    if (null_ptr == NULL) {
        printf("指针为空,不能解引用\n");
    }
    
    // 检查指针是否为空(安全编程)
    if (null_ptr != NULL) {
        printf("指针值: %d\n", *null_ptr);
    } else {
        printf("指针为空,无法访问\n");
    }
    
    // 动态分配内存
    int *dynamic_ptr = (int*)malloc(sizeof(int));
    if (dynamic_ptr != NULL) {
        *dynamic_ptr = 100;
        printf("动态分配的值: %d\n", *dynamic_ptr);
        free(dynamic_ptr);     // 释放内存
        dynamic_ptr = NULL;    // 避免野指针
    }
    
    return 0;
}

4.2 指针与数组的关系

数组和指针的等价性:

#include <stdio.h>

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int *ptr = numbers;  // 数组名就是指向第一个元素的指针
    
    printf("=== 数组访问方式比较 ===\n");
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);    // 数组下标方式
        printf("*(numbers+%d) = %d\n", i, *(numbers+i)); // 指针运算方式
        printf("ptr[%d] = %d\n", i, ptr[i]);           // 指针下标方式
        printf("*(ptr+%d) = %d\n", i, *(ptr+i));       // 指针运算方式
        printf("\n");
    }
    
    return 0;
}

指针数组和数组指针:

#include <stdio.h>

int main() {
    // 指针数组:存放指针的数组
    char *names[3] = {"张三", "李四", "王五"};
    printf("=== 指针数组 ===\n");
    for (int i = 0; i < 3; i++) {
        printf("names[%d] = %s\n", i, names[i]);
    }
    
    // 数组指针:指向数组的指针
    int matrix[2][3] = {{1,2,3}, {4,5,6}};
    int (*array_ptr)[3] = matrix;  // 指向包含3个int的数组
    
    printf("\n=== 数组指针 ===\n");
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("matrix[%d][%d] = %d\n", i, j, array_ptr[i][j]);
        }
    }
    
    return 0;
}

函数参数中的数组和指针:

#include <stdio.h>

// 数组作为参数的三种等价写法
void printArray1(int arr[], int size) {
    printf("方式1 - arr[]: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

void printArray2(int *arr, int size) {
    printf("方式2 - *arr: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", *(arr + i));
    }
    printf("\n");
}

void printArray3(int arr[5], int size) {  // 数字5会被忽略
    printf("方式3 - arr[5]: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 修改数组元素
void modifyArray(int *arr, int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;  // 将每个元素翻倍
    }
}

int main() {
    int numbers[] = {1, 2, 3, 4, 5};
    int size = sizeof(numbers) / sizeof(numbers[0]);
    
    printf("原始数组: ");
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n\n");
    
    printArray1(numbers, size);
    printArray2(numbers, size);
    printArray3(numbers, size);
    
    printf("\n修改数组后: ");
    modifyArray(numbers, size);
    for (int i = 0; i < size; i++) {
        printf("%d ", numbers[i]);
    }
    printf("\n");
    
    return 0;
}

第五章:结构体 - 自定义数据类型

5.1 结构体的定义和语法

结构体定义语法:

// 结构体定义的基本语法
struct 结构体名 {
    数据类型1 成员名1;
    数据类型2 成员名2;
    // ...
};

// 实例:学生结构体
struct Student {
    int id;           // 学号
    char name[20];    // 姓名
    float score;      // 成绩
    char grade;       // 年级
};

// 结构体变量声明方式
struct Student stu1;                    // 声明一个变量
struct Student stu2, stu3;              // 声明多个变量
struct Student students[10];            // 声明结构体数组

// 结构体类型定义(typedef)
typedef struct {
    int x;
    int y;
} Point;                                // 直接使用Point作为类型名

Point p1, p2;                           // 不需要struct关键字

结构体初始化方式:

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

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

int main() {
    // 方式1:分别赋值
    struct Student stu1;
    stu1.id = 1001;
    strcpy(stu1.name, "张三");
    stu1.score = 85.5;
    
    // 方式2:初始化列表
    struct Student stu2 = {1002, "李四", 92.0};
    
    // 方式3:指定初始化(C99标准)
    struct Student stu3 = {
        .id = 1003,
        .name = "王五",
        .score = 78.5
    };
    
    // 方式4:部分初始化
    struct Student stu4 = {1004};  // 只初始化第一个成员
    strcpy(stu4.name, "赵六");
    stu4.score = 88.0;
    
    printf("学生信息:\n");
    printf("ID: %d, Name: %s, Score: %.1f\n", stu1.id, stu1.name, stu1.score);
    printf("ID: %d, Name: %s, Score: %.1f\n", stu2.id, stu2.name, stu2.score);
    printf("ID: %d, Name: %s, Score: %.1f\n", stu3.id, stu3.name, stu3.score);
    printf("ID: %d, Name: %s, Score: %.1f\n", stu4.id, stu4.name, stu4.score);
    
    return 0;
}

结构体数组和指针:

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

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

// 处理结构体数组的函数
void printStudents(struct Student students[], int count) {
    for (int i = 0; i < count; i++) {
        printf("学号: %d, 姓名: %s, 成绩: %.1f\n", 
               students[i].id, students[i].name, students[i].score);
    }
}

// 使用指针处理结构体
void updateScore(struct Student *stu, float newScore) {
    stu->score = newScore;  // 指针访问结构体成员的语法
}

int main() {
    // 结构体数组初始化
    struct Student students[3] = {
        {1001, "张三", 85.5},
        {1002, "李四", 92.0},
        {1003, "王五", 78.5}
    };
    
    printf("原始成绩:\n");
    printStudents(students, 3);
    
    // 使用指针修改成绩
    updateScore(&students[1], 95.0);
    
    printf("\n修改后成绩:\n");
    printStudents(students, 3);
    
    // 结构体指针数组
    struct Student *ptr_array[3];
    for (int i = 0; i < 3; i++) {
        ptr_array[i] = &students[i];
    }
    
    printf("\n通过指针数组访问:\n");
    for (int i = 0; i < 3; i++) {
        printf("学号: %d, 姓名: %s, 成绩: %.1f\n", 
               ptr_array[i]->id, ptr_array[i]->name, ptr_array[i]->score);
    }
    
    return 0;
}

嵌套结构体和联合:

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

// 嵌套结构体
struct Date {
    int year;
    int month;
    int day;
};

struct Person {
    char name[20];
    int age;
    struct Date birthday;  // 嵌套结构体
};

// 联合(union)
union Data {
    int i;
    float f;
    char c;
};  // 所有成员共享同一块内存

int main() {
    // 嵌套结构体使用
    struct Person p1;
    strcpy(p1.name, "张三");
    p1.age = 25;
    p1.birthday.year = 1998;
    p1.birthday.month = 5;
    p1.birthday.day = 15;
    
    printf("个人信息:\n");
    printf("姓名: %s\n", p1.name);
    printf("年龄: %d\n", p1.age);
    printf("生日: %d-%d-%d\n", p1.birthday.year, p1.birthday.month, p1.birthday.day);
    
    // 联合使用
    union Data data;
    data.i = 10;
    printf("联合中的整数: %d\n", data.i);
    
    data.f = 3.14f;
    printf("联合中的浮点数: %.2f\n", data.f);
    printf("此时整数值变为: %d(因为共享内存)\n", data.i);
    
    printf("联合大小: %zu 字节\n", sizeof(union Data));
    printf("结构体大小: %zu 字节\n", sizeof(struct Person));
    
    return 0;
}

第六章:动态内存分配 - 灵活管理内存

6.1 malloc和free

动态内存分配就like是向银行借钱,用完要记得还:

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

int main() {
    int n;
    printf("请输入数组大小: ");
    scanf("%d", &n);
    
    // 动态分配内存
    int *arr = (int*)malloc(n * sizeof(int));
    if (arr == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }
    
    // 初始化数组
    for (int i = 0; i < n; i++) {
        arr[i] = i + 1;
    }
    
    // 使用数组
    for (int i = 0; i < n; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
    
    // 释放内存
    free(arr);
    arr = NULL;  // 避免野指针
    
    return 0;
}

第七章:预处理与编译 - 程序的构建过程

7.1 预处理指令

#include <stdio.h>     // 包含标准库
#define PI 3.14159     // 定义常量
#define MAX(a,b) ((a)>(b)?(a):(b))  // 定义宏

#ifdef DEBUG
    #define PRINT(x) printf(x)
#else
    #define PRINT(x)
#endif

int main() {
    printf("圆周率: %f\n", PI);
    printf("最大值: %d\n", MAX(5, 3));
    return 0;
}

第八章:考试技巧与注意事项

7.2 文件操作语法

文件操作函数:

#include <stdio.h>

// 文件操作基本函数
FILE* fopen(const char* filename, const char* mode);  // 打开文件
int fclose(FILE* stream);                            // 关闭文件
int fgetc(FILE* stream);                             // 读取字符
int fputc(int c, FILE* stream);                      // 写入字符
char* fgets(char* str, int n, FILE* stream);         // 读取字符串
int fputs(const char* str, FILE* stream);            // 写入字符串
int fscanf(FILE* stream, const char* format, ...);   // 格式化读取
int fprintf(FILE* stream, const char* format, ...);  // 格式化写入

// 文件位置操作
long ftell(FILE* stream);                            // 获取当前位置
int fseek(FILE* stream, long offset, int whence);     // 设置文件位置
void rewind(FILE* stream);                           // 重置到文件开头

// 文件状态检查
int feof(FILE* stream);                              // 检查是否到达文件末尾
int ferror(FILE* stream);                            // 检查文件错误
void clearerr(FILE* stream);                         // 清除错误标志

文件打开模式:

// 文件打开模式说明
"r"    // 只读模式,文件必须存在
"w"    // 只写模式,创建新文件或覆盖现有文件
"a"    // 附加模式,在文件末尾写入
"r+"   // 读写模式,文件必须存在
"w+"   // 读写模式,创建新文件或覆盖现有文件
"a+"   // 读写模式,在文件末尾附加
"rb"   // 二进制只读模式
"wb"   // 二进制只写模式
"ab"   // 二进制附加模式

文件操作完整示例:

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

// 写入文件示例
void write_file_example() {
    printf("=== 文件写入示例 ===\n");
    
    FILE *fp = fopen("student_data.txt", "w");
    if (fp == NULL) {
        printf("无法打开文件进行写入!\n");
        return;
    }
    
    // 写入学生信息
    fprintf(fp, "学号,姓名,性别,成绩\n");
    fprintf(fp, "1001,张三,男,85.5\n");
    fprintf(fp, "1002,李四,女,92.0\n");
    fprintf(fp, "1003,王五,男,78.5\n");
    
    fclose(fp);
    printf("学生数据已写入文件\n\n");
}

// 读取文件示例
void read_file_example() {
    printf("=== 文件读取示例 ===\n");
    
    FILE *fp = fopen("student_data.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件进行读取!\n");
        return;
    }
    
    char line[100];
    int line_num = 1;
    
    // 逐行读取文件
    while (fgets(line, sizeof(line), fp) != NULL) {
        printf("第%d行: %s", line_num, line);
        line_num++;
    }
    
    fclose(fp);
    printf("\n");
}

// 格式化读取示例
void formatted_read_example() {
    printf("=== 格式化读取示例 ===\n");
    
    FILE *fp = fopen("numbers.txt", "w");
    if (fp != NULL) {
        fprintf(fp, "10 20.5 30\n");
        fprintf(fp, "40 50.5 60\n");
        fprintf(fp, "70 80.5 90\n");
        fclose(fp);
    }
    
    fp = fopen("numbers.txt", "r");
    if (fp == NULL) {
        printf("无法打开文件!\n");
        return;
    }
    
    int a, c;
    float b;
    
    // 格式化读取
    while (fscanf(fp, "%d %f %d", &a, &b, &c) == 3) {
        printf("读取到: a=%d, b=%.1f, c=%d\n", a, b, c);
    }
    
    fclose(fp);
    printf("\n");
}

// 文件位置操作示例
void file_position_example() {
    printf("=== 文件位置操作示例 ===\n");
    
    FILE *fp = fopen("position_test.txt", "w+");
    if (fp == NULL) {
        printf("无法打开文件!\n");
        return;
    }
    
    // 写入一些数据
    fputs("Hello World!", fp);
    
    // 获取当前位置
    long pos = ftell(fp);
    printf("当前文件位置: %ld\n", pos);
    
    // 重置到文件开头
    rewind(fp);
    
    // 读取数据
    char buffer[20];
    fgets(buffer, sizeof(buffer), fp);
    printf("从文件开头读取: %s\n", buffer);
    
    // 移动到文件中间
    fseek(fp, 6, SEEK_SET);  // 从开头第6个字节
    fgets(buffer, 6, fp);
    printf("从位置6开始读取: %s\n", buffer);
    
    fclose(fp);
    printf("\n");
}

// 错误处理示例
void error_handling_example() {
    printf("=== 文件错误处理示例 ===\n");
    
    FILE *fp = fopen("nonexistent.txt", "r");
    if (fp == NULL) {
        perror("打开文件失败");  // perror会打印系统错误信息
        return;
    }
    
    // 尝试读取数据
    int num;
    if (fscanf(fp, "%d", &num) != 1) {
        if (feof(fp)) {
            printf("到达文件末尾\n");
        }
        if (ferror(fp)) {
            printf("文件读取发生错误\n");
            clearerr(fp);  // 清除错误标志
        }
    }
    
    fclose(fp);
}

int main() {
    write_file_example();
    read_file_example();
    formatted_read_example();
    file_position_example();
    error_handling_example();
    
    return 0;
}

8.1 C程序的标准结构

#include <stdio.h>        // 头文件包含

#define MAXSIZE 100       // 宏定义

// 函数声明
int add(int a, int b);

// 全局变量
int global_var = 0;

// 主函数
int main() {
    // 局部变量
    int local_var = 10;
    
    // 程序逻辑
    printf("Hello, C!\n");
    
    return 0;
}

// 函数定义
int add(int a, int b) {
    return a + b;
}

8.2 常见错误避免

语法错误:

  • 忘记分号
  • 括号不匹配
  • 变量未声明就使用
  • 数组下标越界

逻辑错误:

  • 指针未初始化就使用
  • 内存泄漏(malloc后忘记free)
  • 字符串操作越界
  • 文件操作后忘记关闭

8.3 调试技巧

#include <stdio.h>

int main() {
    int a = 5, b = 3;
    
    // 使用printf调试
    printf("DEBUG: a=%d, b=%d\n", a, b);
    
    int result = a + b;
    printf("DEBUG: result=%d\n", result);
    
    return 0;
}

结语:C语言学习的进阶之路

恭喜你掌握了C语言二级考试的核心知识!🎉

C语言的学习不应止步于考试,掌握了这些基础后,你可以:

  • 深入学习数据结构与算法
  • 探索操作系统原理
  • 学习嵌入式开发
  • 为学习C++打下坚实基础

最后推荐一下我使用的题库,三端可用【支持苹果,安卓,pc】在手机上写操作题,特别是指针可视化和内存管理功能,让抽象的概念变得具体可见!和考试环境一致的评分系统也能让你提前适应考试节奏。

在这里插入图片描述

祝愿每位同学都能在C语言的世界里找到编程的乐趣! 💪


Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐