C语言进阶:文件操作、七大排序与Git实战

一、C语言文件操作详解

1.1 文件操作基本流程

C语言中操作文件遵循"三步走"原则:

复制代码
// 1. 打开文件
FILE *fp = fopen("filename", "mode");

// 2. 读写操作
// ...

// 3. 关闭文件
fclose(fp);

1.2 文件打开模式

模式 含义 文件不存在 文件存在
"r" 只读(文本) 返回NULL 从头读
"w" 只写(文本) 创建新文件 清空内容
"a" 追加(文本) 创建新文件 末尾追加
"rb" 只读(二进制) 返回NULL 从头读
"wb" 只写(二进制) 创建新文件 清空内容
"r+" 读写(文本) 返回NULL 从头读写

1.3 常用读写函数

字符读写
复制代码
// 写入字符
fputc('A', fp);

// 读取字符(注意返回int,用于判断EOF)
int ch = fgetc(fp);
while ((ch = fgetc(fp)) != EOF) {
    putchar(ch);
}
字符串读写
复制代码
// 写入字符串
fputs("Hello World\n", fp);

// 按行读取
char buffer[256];
while (fgets(buffer, sizeof(buffer), fp) != NULL) {
    printf("%s", buffer);
}
格式化读写
复制代码
// 格式化写入
fprintf(fp, "姓名:%s,年龄:%d\n", name, age);

// 格式化读取
fscanf(fp, "姓名:%s,年龄:%d", name, &age);
数据块读写(二进制)
复制代码
typedef struct {
    int id;
    char name[50];
    float score;
} Student;

Student stu = {1001, "张三", 92.5};

// 写入结构体
fwrite(&stu, sizeof(Student), 1, fp);

// 读取结构体
Student stu2;
fread(&stu2, sizeof(Student), 1, fp);

1.4 文件定位

复制代码
// 移动文件指针
fseek(fp, offset, origin);
// origin: SEEK_SET(开头), SEEK_CUR(当前), SEEK_END(末尾)

// 获取当前位置
long pos = ftell(fp);

// 重置到开头
rewind(fp);

1.5 实战:文件复制程序

复制代码
#include <stdio.h>
#include <stdlib.h>

int copyFile(const char *src, const char *dst) {
    FILE *fSrc = fopen(src, "rb");
    if (fSrc == NULL) {
        perror("打开源文件失败");
        return -1;
    }
    
    FILE *fDst = fopen(dst, "wb");
    if (fDst == NULL) {
        perror("创建目标文件失败");
        fclose(fSrc);
        return -1;
    }
    
    char buffer[1024];
    size_t bytesRead;
    while ((bytesRead = fread(buffer, 1, sizeof(buffer), fSrc)) > 0) {
        fwrite(buffer, 1, bytesRead, fDst);
    }
    
    fclose(fSrc);
    fclose(fDst);
    printf("文件复制成功!\n");
    return 0;
}

int main() {
    copyFile("source.txt", "dest.txt");
    return 0;
}

1.6 常见错误与注意事项

  1. 忘记检查文件指针是否为NULL
复制代码
// ❌ 错误
FILE *fp = fopen("test.txt", "r");
fscanf(fp, "%d", &num);  // fp可能为NULL

// ✅ 正确
FILE *fp = fopen("test.txt", "r");
if (fp == NULL) {
    perror("打开文件失败");
    return -1;
}
  1. 忘记关闭文件 → 资源泄露

  2. 文本模式 vs 二进制模式:Windows下文本模式会自动转换换行符


二、七大排序算法

2.1 算法对比一览

排序算法 平均时间 最好情况 最坏情况 空间 稳定性
冒泡排序 O(n²) O(n) O(n²) O(1)
选择排序 O(n²) O(n²) O(n²) O(1)
插入排序 O(n²) O(n) O(n²) O(1)
希尔排序 O(n log n)~O(n²) O(n^1.3) O(n²) O(1)
归并排序 O(n log n) O(n log n) O(n log n) O(n)
快速排序 O(n log n) O(n log n) O(n²) O(log n)
堆排序 O(n log n) O(n log n) O(n log n) O(1)

2.2 完整代码实现

1. 冒泡排序
复制代码
void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int swapped = 0;
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = 1;
            }
        }
        if (!swapped) break;  // 优化:提前结束
    }
}
2. 选择排序
复制代码
void selectionSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;
        for (int j = i + 1; j < n; j++) {
            if (arr[j] < arr[minIdx]) {
                minIdx = j;
            }
        }
        if (minIdx != i) {
            int temp = arr[i];
            arr[i] = arr[minIdx];
            arr[minIdx] = temp;
        }
    }
}
3. 插入排序
复制代码
void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
}
4. 希尔排序
复制代码
void shellSort(int arr[], int n) {
    for (int gap = n / 2; gap > 0; gap /= 2) {
        for (int i = gap; i < n; i++) {
            int temp = arr[i];
            int j = i;
            while (j >= gap && arr[j - gap] > temp) {
                arr[j] = arr[j - gap];
                j -= gap;
            }
            arr[j] = temp;
        }
    }
}
5. 归并排序
复制代码
void merge(int arr[], int left, int mid, int right) {
    int n1 = mid - left + 1;
    int n2 = right - mid;
    int L[n1], R[n2];
    
    for (int i = 0; i < n1; i++) L[i] = arr[left + i];
    for (int j = 0; j < n2; j++) R[j] = arr[mid + 1 + j];
    
    int i = 0, j = 0, k = left;
    while (i < n1 && j < n2) {
        if (L[i] <= R[j]) arr[k++] = L[i++];
        else arr[k++] = R[j++];
    }
    while (i < n1) arr[k++] = L[i++];
    while (j < n2) arr[k++] = R[j++];
}

void mergeSort(int arr[], int left, int right) {
    if (left < right) {
        int mid = left + (right - left) / 2;
        mergeSort(arr, left, mid);
        mergeSort(arr, mid + 1, right);
        merge(arr, left, mid, right);
    }
}
6. 快速排序
复制代码
int partition(int arr[], int low, int high) {
    int pivot = arr[high];
    int i = low - 1;
    
    for (int j = low; j < high; j++) {
        if (arr[j] < pivot) {
            i++;
            int temp = arr[i];
            arr[i] = arr[j];
            arr[j] = temp;
        }
    }
    
    int temp = arr[i + 1];
    arr[i + 1] = arr[high];
    arr[high] = temp;
    return i + 1;
}

void quickSort(int arr[], int low, int high) {
    if (low < high) {
        int pi = partition(arr, low, high);
        quickSort(arr, low, pi - 1);
        quickSort(arr, pi + 1, high);
    }
}
7. 堆排序
复制代码
void heapify(int arr[], int n, int i) {
    int largest = i;
    int left = 2 * i + 1;
    int right = 2 * i + 2;
    
    if (left < n && arr[left] > arr[largest]) largest = left;
    if (right < n && arr[right] > arr[largest]) largest = right;
    
    if (largest != i) {
        int temp = arr[i];
        arr[i] = arr[largest];
        arr[largest] = temp;
        heapify(arr, n, largest);
    }
}

void heapSort(int arr[], int n) {
    for (int i = n / 2 - 1; i >= 0; i--) {
        heapify(arr, n, i);
    }
    
    for (int i = n - 1; i > 0; i--) {
        int temp = arr[0];
        arr[0] = arr[i];
        arr[i] = temp;
        heapify(arr, i, 0);
    }
}

2.3 如何选择排序算法

场景 推荐算法
数据量小或基本有序 插入排序
追求稳定排序 归并排序
数据量大,追求速度 快速排序(注意规避最坏情况)
内存受限 堆排序
数据量大且要求稳定 归并排序

三、Git版本控制操作

3.1 Git基础概念

Git是分布式版本控制系统,核心概念包括:

  • 工作区(Working Directory):你当前修改的文件

  • 暂存区(Staging Area/Index):准备提交的文件

  • 本地仓库(Local Repository):提交历史存储

  • 远程仓库(Remote Repository):托管在服务器的仓库

3.2 基本操作流程

复制代码
工作区 → git add → 暂存区 → git commit → 本地仓库 → git push → 远程仓库

3.3 常用命令速查

配置

bash

复制代码
# 设置用户信息
git config --global user.name "Your Name"
git config --global user.email "your@email.com"

# 查看配置
git config --list
创建与克隆

bash

复制代码
# 初始化本地仓库
git init

# 克隆远程仓库
git clone https://github.com/user/repo.git
基本操作

bash

复制代码
# 查看状态
git status

# 添加文件到暂存区
git add file.txt      # 添加单个文件
git add .             # 添加所有文件

# 提交到本地仓库
git commit -m "提交说明"

# 查看提交历史
git log               # 完整历史
git log --oneline     # 简洁版本
远程操作

bash

复制代码
# 添加远程仓库
git remote add origin https://github.com/user/repo.git

# 推送到远程
git push origin main

# 拉取远程更新
git pull origin main

# 获取远程更新但不合并
git fetch origin
分支操作

bash

复制代码
# 查看分支
git branch            # 本地分支
git branch -a         # 所有分支(含远程)

# 创建分支
git branch feature

# 切换分支
git checkout feature
# 或使用新版命令
git switch feature

# 创建并切换
git checkout -b feature

# 合并分支
git checkout main
git merge feature

# 删除分支
git branch -d feature

3.4 解决冲突

当合并产生冲突时:

bash

复制代码
# 1. 手动编辑冲突文件,保留需要的代码
# 2. 标记为已解决
git add filename
# 3. 完成合并
git commit -m "解决冲突"

3.5 .gitignore文件

创建 .gitignore 文件忽略不需要版本控制的文件:

gitignore

复制代码
# 编译输出
*.o
*.exe
*.out

# IDE配置
.vscode/
.idea/

# 日志文件
*.log

# 临时文件
*.tmp

3.6 Git工作流示例

bash

复制代码
# 1. 克隆项目
git clone https://github.com/user/project.git
cd project

# 2. 创建功能分支
git checkout -b feature-login

# 3. 修改代码
vim login.c

# 4. 提交更改
git add login.c
git commit -m "实现登录功能"

# 5. 同步主分支最新代码
git checkout main
git pull origin main

# 6. 合并回主分支
git merge feature-login

# 7. 推送到远程
git push origin main

# 8. 删除功能分支
git branch -d feature-login

3.7 常用操作技巧

bash

复制代码
# 撤销暂存
git reset HEAD file.txt

# 撤销本地修改
git checkout -- file.txt

# 修改最后一次提交信息
git commit --amend -m "新的提交信息"

# 查看差异
git diff                 # 工作区 vs 暂存区
git diff --staged        # 暂存区 vs 上次提交

# 查看某行代码的修改历史
git blame file.txt

# 暂存当前工作
git stash
git stash pop            # 恢复暂存

四、综合练习:将排序结果写入文件

复制代码
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

// 此处放入七大排序函数...

void saveToFile(int arr[], int n, const char *filename) {
    FILE *fp = fopen(filename, "w");
    if (fp == NULL) {
        perror("打开文件失败");
        return;
    }
    
    fprintf(fp, "排序结果(共%d个元素):\n", n);
    for (int i = 0; i < n; i++) {
        fprintf(fp, "%d ", arr[i]);
        if ((i + 1) % 10 == 0) {
            fprintf(fp, "\n");
        }
    }
    fprintf(fp, "\n");
    fclose(fp);
    printf("结果已保存到 %s\n", filename);
}

int main() {
    // 生成随机数组
    int arr[100];
    srand(time(NULL));
    for (int i = 0; i < 100; i++) {
        arr[i] = rand() % 1000;
    }
    
    // 使用快速排序
    quickSort(arr, 0, 99);
    
    // 保存到文件
    saveToFile(arr, 100, "sorted_result.txt");
    
    return 0;
}

结语

本文涵盖了C语言开发中三个重要的技能领域:

  1. 文件操作:数据的持久化存储

  2. 排序算法:数据处理的核心算法

  3. Git操作:团队协作的版本控制

这三个技能组合起来,足以应对大部分C语言开发场景。建议读者动手实践每个代码示例,加深理解。