linux_C++记录备份-2026

面向对象和面向过程的差异

智能大模型理解有小偏差,但整体符合两种编程特征。

用 KUKA 移动机械臂搬运木箱案例,通俗区分 C++ 面向过程 & 面向对象

场景任务:让图里的库卡移动机械臂,完成「移动到木箱旁→抓取木箱→平移运走→松开放置」整套搬运工作

一、面向过程:只关心「一步步做什么」(C 式线性步骤思维)

核心思路:盯着完整操作流程,把所有动作拆成顺序步骤,数据和动作完全分开

生活化理解

你是现场操作工,全程按清单逐条执行,所有工具、物体只是一堆孤立数据,没有自主能力:

  1. 给机械臂底盘输入坐标,向前移动到木箱侧边
  2. 读取木箱尺寸、高度数据,计算机械臂关节旋转角度
  3. 控制机械臂各关节转动,夹爪闭合夹紧木箱
  4. 抬高机械臂抬升木箱离地
  5. 底盘向后平移一段距离
  6. 夹爪打开,放下木箱
  7. 机械臂复位收回

全程只有顺序动作函数,没有 "机械臂""木箱" 这种独立个体:

  • 单独存变量:木箱长宽高、机械臂各关节角度、底盘位置(纯数据)
  • 单独写函数:移动底盘()、旋转关节()、夹爪夹紧()、抬升臂杆()(纯操作) 数据和操作互不绑定,谁都能随便修改数据。

简易 C++ 代码示例(面向过程写法)

cpp

运行

复制代码
// 分开存放:纯粹数据
double box_x, box_y, box_h;    // 木箱位置、高度
double arm_j1, arm_j2, arm_j3;// 机械臂各关节角度
double car_x, car_y;           // 移动底盘坐标

// 分开存放:纯粹动作函数
void moveCar(double target_x, double target_y) {
    // 控制底盘移动逻辑
}
void rotateJoint(int joint_id, double angle) {
    // 旋转指定机械臂关节
}
void gripperClose() {
    // 夹爪夹紧
}
void gripperOpen() {
    // 夹爪松开
}

int main() {
    // 完整搬运流程,一步接一步硬写死
    moveCar(box_x - 1, box_y);  // 1.开到木箱旁边
    rotateJoint(2, 45);         // 2.调整小臂角度对准木箱
    gripperClose();             // 3.夹紧木箱
    rotateJoint(1, 30);         // 4.抬升机械臂
    moveCar(car_x + 2, car_y);  // 5.向后运走
    gripperOpen();              // 6.放下木箱
    return 0;
}

面向过程缺点对应这个场景

  1. 项目变大后步骤堆砌,流程缠成乱麻;再加第二个木箱、第二台机械臂,要重复复制一堆变量和函数,极易写错。
  2. 数据无保护:任何人都能直接修改arm_j1关节角度,可能出现机械臂撞木箱、过载损坏的逻辑漏洞。
  3. 不符合现实认知:现实里 "库卡机械臂" 是一个完整设备,这里却拆得四分五裂。

二、面向对象:先划分「实体对象」,让对象自己拥有属性和能力(C++ 核心思维)

核心思路:先识别场景里独立事物,每个事物封装「自身特征(属性)+ 自身能做的动作(方法)」,我们只需要下达指令,不用管内部细节

生活化理解

你是车间调度员,不用亲自操作每一步;先定义两个独立 "东西":KUKA 移动机械臂木箱,每个东西自带自己的信息和功能:

  1. 对象 1:KUKA 机械臂
    • 属性(自身特征):底盘坐标、各关节角度、夹爪开合状态、最大承重
    • 自带能力(自己能完成的动作):底盘移动、关节旋转、夹爪抓取、抬升货物、复位
  2. 对象 2:木箱
    • 属性:长宽高、当前坐标、重量、是否被抓取
    • 自带能力:被抓取后跟随移动、落地放置

你只需要下达一句指令:机械臂.搬运木箱(目标位置),内部走到哪、怎么抓、抬多高全是机械臂自己内部实现,不用你手动拆分步骤。

简易 C++ 代码示例(面向对象写法,class 封装)

cpp

运行

复制代码
#include <string>
using namespace std;

// 第一类对象:木箱,封装自身数据和状态
class Box {
public:
    // 属性:木箱自身信息
    double x, y, height;
    double weight;
    bool isGrasped; // 是否被夹住
};

// 第二类对象:KUKA移动机械臂,封装底盘、关节、全部操作能力
class KukaRobot {
public:
    // 属性:机械臂自身硬件数据
    double car_x, car_y;
    double j1, j2, j3; // 三个关节角度
    bool gripper_state;// true夹紧 / false松开

    // 机械臂自带功能:底盘移动
    void moveBase(double tar_x, double tar_y) {
        car_x = tar_x;
        car_y = tar_y;
    }
    // 机械臂自带功能:旋转关节
    void rotateArm(int joint, double angle) {
        if(joint == 1) j1 = angle;
        if(joint == 2) j2 = angle;
    }
    // 机械臂自带功能:抓取物体
    void graspBox(Box &box) {
        moveBase(box.x - 1, box.y); // 自动开到箱子旁
        rotateArm(2, 45);           // 自动调整角度对准
        gripper_state = true;
        box.isGrasped = true;
    }
    // 封装完整一键搬运功能
    void transportBox(Box &box, double target_x, double target_y) {
        graspBox(box);
        rotateArm(1, 30); // 抬升
        moveBase(target_x, target_y); // 运到目标点
        gripper_state = false; // 松开
        box.isGrasped = false;
    }
};

int main() {
    // 创建真实场景里的实体
    KukaRobot kuka;  // 生成图中的库卡机械臂
    Box woodBox;     // 生成图里的木质箱子
    woodBox.x = 5.2; woodBox.y = 3.1; // 箱子初始坐标

    // 只需要一条指令,完成全部搬运,不用拆分细碎步骤
    kuka.transportBox(woodBox, 8.0, 3.1);
    return 0;
}

面向对象对应场景的优势

  1. 贴合现实世界:画面里就是「机械臂」和「木箱」两个物体,代码结构和肉眼看到的场景一一对应,可读性极强。
  2. 高度复用:新增第二台 KUKA 机械臂KukaRobot kuka2;、第二个木箱Box box2;,直接调用transportBox就能复用整套搬运逻辑,不用重复写步骤。
  3. 数据安全:可以把属性设为私有,外部不能随意乱改机械臂关节、木箱位置,避免撞机 bug(封装特性)。
  4. 方便扩展:后续想升级机械臂,增加「避障」「视觉识别箱子」功能,只需要在KukaRobot类里新增方法,不会打乱原有搬运逻辑。

三、同一搬运场景直观对比总结

表格

维度 面向过程(搬箱子) 面向对象(搬箱子)
关注重点 操作步骤:先开车、再转臂、再夹紧... 实体事物:KUKA 机械臂、木箱两个对象
思维逻辑 我手动一步步完成搬运 我指挥机械臂,让它自己完成搬运
数据与动作 箱子位置、机械臂关节数据 和 移动 / 抓取函数完全分开 机械臂自带坐标、关节参数,自带移动抓取功能,绑定在一起
代码扩展 多一台机械臂就要复制全套变量函数,臃肿难维护 直接新建 KukaRobot 实例,一键调用搬运方法
适配场景 单次简单搬运、超简短小程序 机器人仿真、工业自动化这类复杂大型项目(如图里的机械臂仿真)

四、终极通俗一句话(结合这张图)

  • 面向过程:你手动一步步控制底盘走位、掰机械臂关节、开关夹爪,全程盯着每一个操作动作;
  • 面向对象:你找到画面里的「库卡机械臂」这个设备,告诉它 "把这个木箱搬到指定位置",剩下怎么走、怎么抓全交给机械臂自己处理。

补充:C++ 是混合范式,写小型机械臂单步调试可以用面向过程;做整套机器人仿真、多机械臂协同任务时,面向对象是最优写法。


绘制图形

排错案例,多次出错如何修正

报错原因 & 解决方案

报错 graphics.h:没有那个文件或目录EGE/graphics.h 是 Windows 专用图形库,蓝桥云课 Ubuntu 环境默认没有这个头文件 ,不能用原来的 EGE 代码。 提供两种蓝桥云课可用方案:SDL2 窗口绘图(可视化弹窗)、Gnuplot 生成图片(无弹窗,直接出 png)

方案一:SDL2 绘图(推荐,弹出窗口显示五角星)

1. 先在终端安装依赖

bash

运行

复制代码
sudo apt update
sudo apt install libsdl2-dev -y

2. star.cpp 完整代码(沿用波浪五星参数公式)

cpp

运行

复制代码
#include <SDL2/SDL.h>
#include <cmath>
#define PI 3.1415926535
#define W 800
#define H 800

int main()
{
    SDL_Init(SDL_INIT_VIDEO);
    SDL_Window* win = SDL_CreateWindow("WaveStar", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, W, H, 0);
    SDL_Renderer* ren = SDL_CreateRenderer(win, -1, SDL_RENDERER_ACCELERATED);

    // 背景白色
    SDL_SetRenderDrawColor(ren, 255,255,255,255);
    SDL_RenderClear(ren);
    // 线条蓝色
    SDL_SetRenderDrawColor(ren, 30,80,180,255);

    int cx = W/2, cy = H/2;
    double baseR = 220;
    double waveA = 22;
    int layer = 6;
    double dr = 18;

    for(int k=0;k<layer;k++){
        double R = baseR - k*dr;
        int preX,preY;
        for(double th=0; th<=2*PI; th+=0.005){
            double r = R + waveA*sin(5*th);
            int x = cx + r*cos(th);
            int y = cy - r*sin(th);
            if(th==0){
                preX=x; preY=y;
            }else{
                SDL_RenderDrawLine(ren, preX, preY, x, y);
                preX=x; preY=y;
            }
        }
    }

    SDL_RenderPresent(ren);
    SDL_Event e;
    while(SDL_WaitEvent(&e) && e.type!=SDL_QUIT); // 等待关闭窗口

    SDL_DestroyRenderer(ren);
    SDL_DestroyWindow(win);
    SDL_Quit();
    return 0;
}

3. 编译 & 运行命令

bash

运行

复制代码
g++ star.cpp -o star -lSDL2 -lm
./star

运行后直接弹出绘图窗口,和原图嵌套波浪五星一致。


方案二:Gnuplot 方案(无图形窗口,代码输出数据生成 png 图片,蓝桥云课万能兼容)

1. 安装绘图工具

bash

运行

复制代码
sudo apt install gnuplot -y

2. star.cpp(输出坐标数据,调用 gnuplot 生成图片)

cpp

运行

复制代码
#include <iostream>
#include <cmath>
#define PI 3.1415926535
using namespace std;

int main()
{
    int cx=400, cy=400;
    double baseR=220, waveA=22, dr=18;
    int layer=6;

    FILE *fp = fopen("data.txt","w");
    for(int k=0;k<layer;k++){
        double R = baseR - k*dr;
        for(double th=0;th<=2*PI;th+=0.005){
            double r = R + waveA*sin(5*th);
            int x = cx + r*cos(th);
            int y = cy - r*sin(th);
            fprintf(fp,"%d %d\n",x,y);
        }
        fprintf(fp,"\n\n"); // 分层分隔
    }
    fclose(fp);

    // 调用gnuplot生成star.png
    system("gnuplot <<EOF
set terminal png size 800,800
set output 'star.png'
set xrange [0:800]
set yrange [0:800]
set style data lines lw 1 lc rgb '#2050b0'
plot 'data.txt'
EOF");
    cout<<"生成图片 star.png"<<endl;
    return 0;
}

3. 编译运行

bash

运行

复制代码
g++ star.cpp -o star -lm
./star

运行后目录生成star.png图片,下载即可查看图案。


参数微调说明(和原图匹配)

  • layer=6:嵌套线条总数(原图 6 圈)
  • baseR=220:整体大小,数值变大图形放大
  • waveA=22:波浪凹凸幅度,越大尖角起伏越夸张
  • dr=18:相邻线条间距,数值越小线条越紧凑

cmake-Linux 环境下的 C/C++ 编程法宝-2026-CSDN博客


定制标题栏加入学号

你这一整条命令是一次性克隆、装依赖、编译、运行这个 DungeonRushDemo SDL2 游戏,思路是对的,但有几个细节我直接帮你改好、并附上 "改标题栏加学号" 的完整流程,你可以直接复制照着做。


一、你这条命令拆解(确认你在 Ubuntu 下执行)

bash

运行

复制代码
# 1. 拉代码
git clone https://gitcode.com/ZhangRelay1/DungeonRushDemo.git

# 2. 安装 SDL2 全套依赖(正确)
sudo apt install libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev -y

# 3. 进目录、建 build、cmake、make、运行
cd DungeonRushDemo
mkdir build && cd build
cmake ..
make
cd bin
./dungeon_rush

这条命令整体没问题,但容易踩坑:

  • 没装 git / cmake / gcc → 先装:

    bash

    运行

    复制代码
    sudo apt update
    sudo apt install git cmake gcc -y
  • 编译报错找不到 SDL → 依赖安装那一步要sudo apt update 后再执行

  • 运行黑屏 / 没窗口 → 加环境变量(后面给你最终命令)


二、改标题栏 + 学号(核心操作)

1. 先进入项目根目录

bash

运行

复制代码
cd ~/DungeonRushDemo   # 假设你在家目录 clone 的

2. 搜索窗口创建代码

bash

运行

复制代码
grep -r "SDL_CreateWindow" src/

大概率会定位到类似:

  • src/render.c
  • src/main.c
  • src/ui.c

打开那个文件:

bash

运行

复制代码
vim src/render.c

找到类似:

c

运行

复制代码
SDL_Window* window = SDL_CreateWindow(
    "Dungeon Rush",   # <--- 原来标题
    SDL_WINDOWPOS_CENTERED,
    SDL_WINDOWPOS_CENTERED,
    800, 600,
    SDL_WINDOW_SHOWN
);

改成(带上你的学号):

c

运行

复制代码
SDL_Window* window = SDL_CreateWindow(
    "Dungeon Rush - 学号:20260001",
    SDL_WINDOWPOS_CENTERED,
    SDL_WINDOWPOS_CENTERED,
    800, 600,
    SDL_WINDOW_SHOWN
);

保存退出(vim 按 Esc → 输入 :wq → 回车)


三、重新编译运行(最终一键命令)

bash

运行

复制代码
cd ~/DungeonRushDemo/build
make clean
cmake ..
make
cd bin
SDL_VIDEODRIVER=x11 ./dungeon_rush

SDL_VIDEODRIVER=x11 是为了防止 Wayland 下窗口出不来 / 黑屏。


四、我直接给你一条 "从 0 到运行 + 带学号标题" 的完整脚本

你可以直接复制进终端执行:

bash

运行

复制代码
sudo apt update
sudo apt install git cmake gcc libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev -y

git clone https://gitcode.com/ZhangRelay1/DungeonRushDemo.git
cd DungeonRushDemo

# 自动替换标题为:Dungeon Rush - 学号:20260001(你可改下面字符串)
sed -i 's/"Dungeon Rush"/"Dungeon Rush - 学号:20260001"/g' $(grep -rl "Dungeon Rush" src/)

mkdir build && cd build
cmake ..
make
cd bin
SDL_VIDEODRIVER=x11 ./dungeon_rush

执行完,窗口标题就直接带学号了。



结合蓝桥云课 Linux 环境,为你整理分步可操作的完整实验流程,包含环境操作、代码编写、编译、运行、现象观察、问题解决,严格对应实验三大任务,可直接照着完成实验并填写报告。

实验六 Linux 进程与线程操作 分步实验流程

前置准备

  1. 打开蓝桥云课平台,进入本次实验对应的 Linux 在线环境,打开终端窗口。

  2. 执行基础命令确认环境: bash

    运行

    复制代码
    pwd          # 查看当前工作目录
    mkdir exp6   # 新建实验文件夹
    cd exp6      # 进入实验目录,所有代码在此目录编写运行

任务一:测试 wait.cpp,对比有无 wait()wait(NULL) 的运行差异

步骤 1:编写无 wait () 版本代码

  1. 使用文本编辑器创建文件: bash

    运行

    复制代码
    vim wait.cpp
  2. i 进入编辑模式,粘贴以下代码:

cpp

运行

复制代码
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;

int main()
{
    pid_t pid = fork();  // 创建子进程
    if(pid == -1)
    {
        cout << "fork error!" << endl;
        return -1;
    }
    else if(pid == 0)
    {
        // 子进程
        cout << "子进程运行,PID:" << getpid() << endl;
        sleep(2);
        cout << "子进程执行完毕" << endl;
        exit(0);
    }
    else
    {
        // 父进程:未调用 wait()
        cout << "父进程运行,PID:" << getpid() << endl;
        cout << "父进程执行完毕" << endl;
    }
    return 0;
}
  1. Esc,输入 :wq 保存并退出 vim。

步骤 2:编译、运行,观察结果

  1. 编译代码: bash

    运行

    复制代码
    g++ wait.cpp -o wait
  2. 运行程序: bash

    运行

    复制代码
    ./wait
  3. 现象记录 : 父进程先执行并退出,2 秒后子进程才输出内容,父子进程执行顺序混乱、异步执行

步骤 3:修改代码,添加 wait(NULL)

  1. 重新编辑文件: bash

    运行

    复制代码
    vim wait.cpp
  2. 在父进程代码中添加头文件与 wait(NULL),修改后核心代码:

cpp

运行

复制代码
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>   // 新增 wait 函数头文件
using namespace std;

int main()
{
    pid_t pid = fork();
    if(pid == -1)
    {
        cout << "fork error!" << endl;
        return -1;
    }
    else if(pid == 0)
    {
        cout << "子进程运行,PID:" << getpid() << endl;
        sleep(2);
        cout << "子进程执行完毕" << endl;
        exit(0);
    }
    else
    {
        cout << "父进程运行,PID:" << getpid() << endl;
        wait(NULL);  // 阻塞父进程,等待子进程结束
        cout << "父进程执行完毕" << endl;
    }
    return 0;
}
  1. :wq 保存退出。

步骤 4:重新编译运行,对比结果

  1. 重新编译(代码修改后必须重编): bash

    运行

    复制代码
    g++ wait.cpp -o wait
  2. 运行: bash

    运行

    复制代码
    ./wait
  3. 现象记录 : 父进程执行到 wait(NULL) 后阻塞,等待子进程完全运行结束,父进程才继续执行,输出顺序固定。

任务一总结

  • wait():父子进程并发执行,顺序随机;
  • wait(NULL):父进程阻塞,实现进程同步,保证子进程先退出。

任务二:编写 Zombie.cpp,产生并解决僵尸进程

步骤 1:编写产生僵尸进程的原始代码

  1. 新建文件: bash

    运行

    复制代码
    vim Zombie.cpp
  2. 写入代码:

cpp

运行

复制代码
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
using namespace std;

int main()
{
    pid_t pid = fork();
    if(pid == 0)
    {
        // 子进程立刻退出
        cout << "子进程退出,PID:" << getpid() << endl;
        exit(0);
    }
    else
    {
        // 父进程长时间休眠,不回收子进程
        sleep(10);
        cout << "父进程执行结束" << endl;
    }
    return 0;
}
  1. :wq 保存退出。

步骤 2:编译运行,查看僵尸进程

  1. 编译: bash

    运行

    复制代码
    g++ Zombie.cpp -o zombie
  2. 终端第一个窗口 运行程序:

    bash

    运行

    复制代码
    ./zombie
  3. 立刻新开一个终端窗口 ,执行查看僵尸进程命令:

    bash

    运行

    复制代码
    ps aux | grep Z
  4. 现象记录 : 可以查询到状态标记为 Z(僵尸)的进程,僵尸进程产生

  5. 等待 10 秒,原程序运行结束,僵尸进程自动消失。

步骤 3:修改代码,使用 waitpid() 解决僵尸进程

  1. 回到原目录,编辑代码: bash

    运行

    复制代码
    vim Zombie.cpp
  2. 添加头文件与 waitpid() 函数,修改后代码:

cpp

运行

复制代码
#include <iostream>
#include <unistd.h>
#include <stdlib.h>
#include <wait.h>   // 等待函数头文件
using namespace std;

int main()
{
    pid_t pid = fork();
    if(pid == 0)
    {
        cout << "子进程退出,PID:" << getpid() << endl;
        exit(0);
    }
    else
    {
        // 回收指定子进程,解决僵尸进程
        waitpid(pid, NULL, 0);
        sleep(10);
        cout << "父进程执行结束" << endl;
    }
    return 0;
}
  1. :wq 保存退出。

步骤 4:再次测试,验证僵尸进程已消除

  1. 重新编译: bash

    运行

    复制代码
    g++ Zombie.cpp -o zombie
  2. 运行程序,同时新开终端执行: bash

    运行

    复制代码
    ./zombie
    ps aux | grep Z
  3. 现象记录 : 查询不到 Z 状态进程,僵尸进程被彻底解决

任务二总结

  • 僵尸进程成因:子进程退出,父进程未调用 wait/waitpid 回收资源;
  • 解决方案:父进程调用 wait()waitpid() 回收子进程。

任务三:改造 pthread_con.cpp,新增 2 个线程,四线程有序输出 1~36

步骤 1:编写最终四线程代码

  1. 新建文件: bash

    运行

    复制代码
    vim pthread_con.cpp
  2. 写入完整代码(含 4 个线程、互斥锁 + 条件变量,有序输出 1~36):

cpp

运行

复制代码
#include <iostream>
#include <pthread.h>
#include <unistd.h>
using namespace std;

pthread_mutex_t mutex;
pthread_cond_t cond;
int num = 1;
int thread_flag = 0;  // 标记:0=r1,1=r2,2=r3,3=r4

// 线程1
void *r1(void *arg)
{
    while(num <= 36)
    {
        pthread_mutex_lock(&mutex);
        while(thread_flag != 0)
            pthread_cond_wait(&cond, &mutex);

        if(num <= 36)
            cout << "线程r1:" << num << endl;
        num++;
        thread_flag = 1;

        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

// 线程2
void *r2(void *arg)
{
    while(num <= 36)
    {
        pthread_mutex_lock(&mutex);
        while(thread_flag != 1)
            pthread_cond_wait(&cond, &mutex);

        if(num <= 36)
            cout << "线程r2:" << num << endl;
        num++;
        thread_flag = 2;

        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

// 新增线程3
void *r3(void *arg)
{
    while(num <= 36)
    {
        pthread_mutex_lock(&mutex);
        while(thread_flag != 2)
            pthread_cond_wait(&cond, &mutex);

        if(num <= 36)
            cout << "线程r3:" << num << endl;
        num++;
        thread_flag = 3;

        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

// 新增线程4
void *r4(void *arg)
{
    while(num <= 36)
    {
        pthread_mutex_lock(&mutex);
        while(thread_flag != 3)
            pthread_cond_wait(&cond, &mutex);

        if(num <= 36)
            cout << "线程r4:" << num << endl;
        num++;
        thread_flag = 0;

        pthread_cond_broadcast(&cond);
        pthread_mutex_unlock(&mutex);
    }
    pthread_exit(NULL);
}

int main()
{
    pthread_t t1, t2, t3, t4;
    // 初始化锁和条件变量
    pthread_mutex_init(&mutex, NULL);
    pthread_cond_init(&cond, NULL);

    // 创建四个线程
    pthread_create(&t1, NULL, r1, NULL);
    pthread_create(&t2, NULL, r2, NULL);
    pthread_create(&t3, NULL, r3, NULL);
    pthread_create(&t4, NULL, r4, NULL);

    // 等待所有线程执行完毕
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    pthread_join(t3, NULL);
    pthread_join(t4, NULL);

    // 释放资源
    pthread_mutex_destroy(&mutex);
    pthread_cond_destroy(&cond);
    return 0;
}
  1. :wq 保存退出。

步骤 2:编译多线程程序(必须加 -lpthread 链接库)

bash

运行

复制代码
g++ pthread_con.cpp -o pthread_con -lpthread

步骤 3:运行程序,观察输出

bash

运行

复制代码
./pthread_con

现象记录 : 线程按 r1 → r2 → r3 → r4 循环顺序,依次输出 1、2、3、4 ... 36,输出有序、无乱序、无重复。


整体收尾步骤

  1. 整理每一步运行截图:分别截取三个程序的代码界面、编译命令、运行输出、僵尸进程查询结果。

  2. 对照实验现象,填写实验报告的实验现象、结果分析、总结部分。

  3. 可选清理(环境恢复): bash

    运行

    复制代码
    rm -f wait zombie pthread_con  # 删除可执行文件

关键操作速记(易错点提醒)

  1. 编辑文件:vim 文件名i 编辑,Esc + :wq 保存退出;
  2. 多线程编译必须加 -lpthread,否则编译报错;
  3. 查看僵尸进程命令:ps aux | grep Z
  4. wait(NULL) / waitpid() 用于回收子进程,消除僵尸进程。



AI对大学生C/C++编程思维及编程方式影响的思考

学号: 姓名: 分数:

1. 引言

1.1 研究背景

1.1.1 AI技术的发展现状

随着人工智能技术快速发展,智能编程助手已全面融入编程学习与开发领域,彻底革新了传统编程模式。以GitHub Copilot、CodeGeeX为代表的AI工具,可实现代码补全、算法生成、漏洞检测、代码优化等多功能服务,覆盖C/C++等主流编程语言。依托海量训练数据,AI编程工具大幅降低了编程入门门槛,提升了开发效率,现已从基础代码辅助,升级为具备项目架构搭建、算法设计、逻辑纠错能力的综合性工具,成为编程学习的重要辅助手段。

C/C++是计算机类专业核心底层编程语言,凭借高效、稳定、兼容性强的特点,广泛应用于嵌入式开发、操作系统、算法竞赛等领域,也是高校必修核心课程。该语言学习十分注重严谨的逻辑思维、扎实的语法基础和算法设计能力。在AI普及背景下,大学生C/C++编程学习模式发生颠覆性改变,AI工具既为学习提供了高效助力,也对传统编程思维培养和基础能力训练带来全新挑战。

1.1.2 高校C/C++编程学习现状

在高校教学体系中,C/C++程序设计是培养学生逻辑推理、算法思维和实操能力的核心课程。传统学习模式依靠学生课堂听课、手动敲写代码、上机调试纠错、反复优化迭代来积累经验,逐步建立结构化编程思维。但该模式存在效率低、难点突破困难、调试耗时久的问题,指针、内存管理、递归等重难点知识,往往需要学生花费大量时间摸索,学习门槛较高。

目前,绝大多数大学生都会借助AI工具完成编程作业、算法练习和课程设计,AI已成为编程学习的常用辅助工具。这种模式既解决了传统编程学习效率低、难点难突破的痛点,也滋生了学生代码依赖、思维惰性、基础薄弱等问题,对大学生C/C++编程能力培养形成双向影响。

1.2 研究目的与意义

1.2.1 研究目的

本文聚焦AI技术对大学生C/C++编程学习的影响,从编程思维和编程方式两大维度,结合实际学习案例,分析AI工具应用的积极作用与现存弊端。针对学生学习中存在的思维惰性、过度依赖AI、基础不扎实等问题,提出切实可行的改进策略,引导大学生规范使用AI工具,规避学习误区,高效提升C/C++编程核心能力。

1.2.2 研究意义

理论层面,本文细化了AI对高校C/C++编程教学的影响维度,丰富了人工智能赋能编程教育的相关研究,为高校优化智能化编程教学模式提供理论参考。实践层面,本文总结的优劣影响与应对策略,可指导大学生科学使用AI工具,平衡工具辅助与自主学习的关系,夯实编程基础、锤炼编程思维,同时为教师优化教学内容、改进教学方式提供实践借鉴。

2. AI对大学生C/C++编程学习的双向影响分析

2.1 对编程思维的影响

编程思维是C/C++学习的核心,主要包括逻辑思维、算法思维、模块化思维,是学生独立分析和解决编程问题的关键能力。AI工具的普及对大学生编程思维培养利弊并存,既能够助力思维快速养成,也会带来思维弱化、惰性滋生的问题。

2.1.1 积极影响:赋能编程思维高效养成

首先,助力学生快速建立严谨的逻辑思维。C/C++语法严谨,指针操作、循环嵌套、内存分配等内容易出现逻辑漏洞,传统人工排查错误耗时费力、效率低下。而AI工具可快速识别数组越界、野指针使用、循环条件错误等常见问题,精准梳理代码执行逻辑、标注错误原因,为学生提供实时的逻辑指导。

例如学生编写冒泡排序代码时,常出现循环边界错误、排序逻辑颠倒等问题,传统模式需逐行调试排查,效率极低。AI可直接定位逻辑漏洞,拆解双层循环的核心运行逻辑,帮助学生快速理解嵌套循环原理,养成规范、严谨的逻辑思维,大幅缩短思维养成周期。

其次,拓宽学生算法思维维度。递归、二分查找、动态规划等算法抽象难懂,仅靠课堂理论讲解,学生难以吃透核心思想。AI可结合学生知识基础,将复杂算法拆解为通俗步骤,提供多种实现思路,打破单一学习局限。

以二分查找为例,学生掌握基础写法后,可借助AI对比迭代与递归写法的优劣,学习边界优化、重复元素处理等进阶思路。在课程设计和算法练习中,AI可引导学生拆解问题、匹配最优算法,培养学生问题拆解、效率优化的核心算法思维。

最后,培养模块化编程思维。C/C++强调结构化、模块化开发,AI生成的代码普遍结构规范、函数分工清晰、复用性强。学生长期参考优质AI代码,可潜移默化养成"功能拆分、模块独立、代码复用"的编程习惯,摒弃新手常见的代码堆砌问题。

2.1.2 潜在挑战:滋生思维惰性,弱化独立思考能力

AI工具带来的核心问题是滋生学生编程思维惰性,弱化独立思考能力。传统学习中,学生遇到难题需自主梳理需求、构思逻辑、调试代码,这个过程是锤炼编程思维的关键。而AI可一键生成完整代码,部分学生遇到基础算法题、课后作业、小型项目时,直接照搬AI代码,跳过自主思考环节。

例如在链表操作、动态内存分配作业中,很多学生直接复制AI运行代码,不理解链表增删改查逻辑和malloc内存分配原理。长期如此,学生将丧失问题拆解、逻辑设计的能力,看似完成了学习任务,实则编程思维无法得到锻炼,面对复杂编程项目时极易无从下手。

同时,AI会弱化学生的细节纠错思维。C/C++对语法细节、内存操作规范要求极高,人工调试的过程能让学生深刻记忆各类错误成因与解决方法。而AI自动修正所有漏洞,学生无需自主排查问题,久而久之对代码细节、程序漏洞的敏感度大幅降低,编程思维粗糙不严谨,难以掌握底层核心逻辑。

2.2 对编程方式的影响

编程方式涵盖代码编写、程序调试、代码优化、团队协作等实操环节。AI技术彻底改变了传统手动编程、人工调试的模式,大幅提升编程实操效率,但也造成了学生实操能力弱化、独立开发能力不足等问题。

2.2.1 积极影响:优化编程流程,提升实操效率

第一,简化编码流程,降低入门门槛。C/C++语法繁琐、格式规范严格,新手常因基础语法错误、框架书写不规范浪费大量时间。AI的代码补全、模板生成功能,可快速生成基础代码框架,规范代码格式,减少基础性语法错误。

例如编写C++类与对象程序时,AI可快速生成类声明、构造函数、析构函数等基础框架,让学生专注核心功能逻辑开发。面对文件操作、多线程等复杂场景,AI标准化模板也能帮助学生快速上手项目开发。

第二,高效辅助调试与代码优化。调试是C/C++学习的难点,人工排查内存泄漏、野指针、逻辑死循环等隐性错误耗时费力。AI可快速定位编译、运行、逻辑三类错误,精准给出修改方案,大幅提升调试效率。

除此之外,AI能够对学生编写的冗余代码、低效代码进行优化,简化代码结构、提升程序运行效率、规范代码风格。例如学生编写的循环冗余、变量重复定义、算法复杂度较高的代码,AI可在不改变功能的前提下,优化代码逻辑、降低时间与空间复杂度,帮助学生学习高效的代码编写方式,提升代码质量。

第三,助力编程协作学习。在课程设计、小组编程项目中,AI可作为团队协作辅助工具,帮助学生拆分开发任务、统一代码规范、对接模块接口。团队成员可通过AI梳理项目开发流程、解决模块对接漏洞、统一代码编写风格,降低团队协作的沟通成本与技术门槛,提升小组项目开发效率。同时,AI可生成代码注释、项目说明文档,帮助团队成员快速理解他人代码,提升协作学习效果。

2.2.2 潜在挑战:弱化实操能力,滋生学术不端

首先,学生自主编码与调试能力严重弱化。长期依赖AI生成代码、自动调试,学生逐渐丧失手动敲写代码、自主排查错误的能力。很多学生能够依靠AI完成复杂的C/C++程序开发,但脱离AI工具后,连基础的循环语句、条件语句、结构体定义都无法独立编写,简单的编译错误也无法自主排查,出现"AI依赖型编程"的问题,实操基础极其薄弱。C/C++编程能力的提升依赖大量手动实操积累,而AI的过度替代,直接剥夺了学生的实操锻炼机会,导致学生"看得懂、想不出、写不出"。

其次,滋生抄袭、敷衍的不良学习风气,引发学术不端问题。部分学生将AI作为作业"代写工具",课后作业、课程设计、期末大作业完全依赖AI生成,不进行任何自主修改与思考,直接提交AI生成代码。AI生成的代码同质化较高,大量学生提交相似代码,不仅违背编程学习的初衷,也构成了轻微的学术不端行为。同时,部分学生过度依赖AI,缺乏自主创新意识,代码编写、项目开发完全照搬AI思路,无法形成自己的编程风格与开发思路,创新能力严重不足。

最后,忽视底层原理学习。AI可以直接生成可运行代码,但不会主动讲解底层原理,很多学生只关注代码能否运行,不深究代码背后的内存机制、编译原理、算法逻辑。例如,学生使用AI生成动态内存分配代码,却不了解malloc与free的匹配原理、内存泄漏的危害;使用AI生成指针操作代码,却不理解指针的本质与地址操作逻辑。长期下来,学生只会机械使用代码,不懂底层核心原理,无法应对深层次的编程问题,编程基础浮于表面。

3. 大学生应对AI编程影响的有效策略

3.1 转变学习理念,树立正确的AI使用认知

大学生首先要彻底转变对AI编程工具的认知,明确AI的核心定位是"辅助工具"而非"替代工具",摒弃"依赖AI偷懒、依靠AI速成"的错误学习理念,树立"自主学习为主、AI辅助为辅"的学习原则。在C/C++编程学习中,必须明确核心学习目标是锤炼编程思维、夯实语法基础、提升独立开发能力,而非单纯完成作业、实现程序功能。

日常学习中,主动杜绝直接复制AI完整代码、完全依赖AI完成作业的行为。将AI工具的功能限定为答疑解惑、思路启发、错误修正、代码优化,而非全程代做。遇到编程难题时,遵循"自主思考优先、AI辅助兜底"的流程,先独立分析问题、构思代码逻辑、尝试编写调试,经过自主思考仍无法解决时,再借助AI获取思路、排查错误,从根源上杜绝思维惰性。

3.2 优化学习方法,强化编程思维自主锤炼

针对AI带来的思维弱化问题,学生需要优化自身学习方法,针对性强化逻辑思维、算法思维与模块化思维的自主锻炼。一方面,夯实基础语法与底层原理,主动梳理C/C++核心知识点,重点攻克指针、内存管理、递归、结构体、类与对象等重难点内容,深入理解代码底层运行原理,避免只会用代码、不懂原理的问题。

另一方面,建立"先思后查、先写后改"的学习习惯。练习算法题目、编写程序时,先自主绘制逻辑流程图、设计算法思路、手写基础代码,完成初步编写后,再借助AI对比思路差异、优化代码逻辑、排查漏洞。同时,主动总结AI给出的优质思路与优化方案,将AI的优质思维转化为自身知识储备,逐步提升独立分析问题、解决问题的能力。此外,主动进行算法拓展练习,脱离AI辅助完成基础算法、经典编程案例的开发,刻意锻炼独立算法设计与逻辑构建能力。

3.3 规范工具使用,提升自主实操与创新能力

学生需要制定规范的AI工具使用准则,合理把控使用场景与使用限度,全面提升自主实操能力。在基础学习阶段,严格限制AI的使用,课后基础作业、语法练习、简单程序编写全程自主完成,依靠手动敲写代码、自主调试报错积累实操经验,夯实编程基础。在进阶学习与项目开发阶段,合理利用AI的辅助优势,借助AI梳理项目架构、优化代码结构、排查复杂漏洞、补充开发思路。

同时,杜绝直接照搬AI代码,对AI生成的代码必须进行逐行研读、理解原理、自主修改优化,结合自身开发需求调整代码逻辑,融入自己的设计思路,避免代码同质化,培养编程创新能力。此外,主动脱离AI完成综合性课程设计、期末大作业等核心任务,全程独立完成需求分析、逻辑设计、代码编写、调试优化全流程,全方位锻炼独立开发能力。

3.4 高校协同引导,优化智能化编程教学模式

除了学生自主调整外,高校与教师也需协同发力,适配AI时代的编程教学趋势,引导学生合理使用AI工具。一方面,教师优化教学内容与考核方式,增加底层原理讲解、编程思路推导、现场编码实操的教学环节,减少机械性代码抄写作业,增加开放性、创新性编程项目,倒逼学生独立思考、自主开发。同时,调整考核标准,将代码原创性、思路完整性、原理掌握程度纳入考核,杜绝AI代写、抄袭作业的现象。

另一方面,教师在课堂中正向引导学生使用AI工具,讲解AI工具的合理使用场景与使用误区,教会学生利用AI查漏补缺、优化代码、拓展思路,而非完全依赖AI。同时,高校可开设AI编程辅助相关选修课,帮助学生树立科学的工具使用理念,让AI真正服务于编程能力提升,而非阻碍能力成长。

4. 结论

人工智能技术的普及,为大学生C/C++编程学习带来了颠覆性的变革,呈现出显著的双向影响。从积极层面来看,AI编程工具能够启发学生编程思维、降低编程学习门槛、优化代码开发与调试流程、提升编程学习效率,为学生攻克C/C++重难点知识、拓宽算法思路提供了极大助力,适配了智能化教育的发展趋势。从消极层面来看,AI工具的过度使用与不当依赖,会滋生学生的编程思维惰性,弱化独立思考、自主编码与调试的核心能力,导致学生编程基础薄弱、底层原理缺失、创新能力不足,甚至引发学术不端等问题,严重阻碍编程核心素养的养成。

在AI赋能教育的新时代,人工智能与编程学习的融合是必然趋势,完全摒弃AI工具、固守传统学习模式不符合时代发展,而过度依赖AI、丧失自主能力则会本末倒置。作为新时代大学生,必须正视AI的双向影响,精准定位AI工具的辅助价值,树立"自主学习为核心、AI工具为辅助"的正确理念,主动规避工具使用误区,在借助AI高效学习的同时,坚持自主思考、手动实操、深耕原理、大胆创新,持续锤炼逻辑思维、算法思维等核心编程能力。

未来,大学生应主动适配智能化学习环境,合理借力AI工具弥补自身学习短板,同时坚守编程学习的核心本质,夯实底层编程基础、培养独立开发能力、提升编程创新素养,真正实现从"工具依赖"向"能力自主"的转变,借助AI技术助力自身编程能力全面提升,成长为具备扎实功底、创新思维、实操能力的复合型编程人才。




动态跳动爱心(缩放脉动效果,一键复制运行)

解决你当前静态红心无跳动问题,实现从小到大、从大到小持续脉动,红色渐变立体心跳动画,单行一键生成编译运行:

bash

运行

复制代码
rm -rf ~/heartbeat && mkdir ~/heartbeat && cd ~/heartbeat && cat > beat.c <<EOF
#include <stdio.h>
#include <math.h>
#include <unistd.h>

int main() {
    float t = 0;
    // 无限循环实现持续跳动
    while (1) {
        // 清屏+光标置顶
        printf("\033[2J\033[H");
        // scale 利用正弦函数实现0.6~1.3之间缩放,模拟心跳忽大忽小
        float scale = 0.6 + 0.7 * sin(t);
        // 纵向遍历
        for (float y = 1.4f; y > -1.4f; y -= 0.08f) {
            // 横向遍历
            for (float x = -1.4f; x < 1.4f; x += 0.04f) {
                // 缩放坐标
                float sx = x / scale;
                float sy = y / scale;
                // 爱心公式 (x²+y²-1)³ - x²y³ < 0
                float val = pow(sx*sx + sy*sy - 1, 3) - sx*sx * pow(sy, 3);
                if (val < 0) {
                    // 亮红色爱心
                    printf("\033[1;31m*\033[0m");
                } else {
                    printf(" ");
                }
            }
            printf("\n");
        }
        // 增加时间,改变缩放比例
        t += 0.08f;
        // 控制跳动速度,数值越大跳动越慢
        usleep(60000);
    }
    return 0;
}
EOF
gcc beat.c -o beat -lm && ./beat

核心跳动原理说明

  1. 缩放控制 scale = 0.6 + 0.7 * sin(t)
    • sin(t) 取值范围 -1,1,所以 scale 区间 0.6 ~ 1.3
    • t 不断累加,scale 周期性变大变小,画面爱心同步缩放,实现 "蹦跳" 效果
  2. 清屏转义码 \033[2J\033[H:每次刷新清空屏幕,无残影
  3. 编译必须带 -lm:链接数学库pow/sin,否则编译报错
  4. 调速:修改usleep(60000),数字变小跳动更快(如 30000 极速心跳)

退出程序

按下 Ctrl + C 终止动画,回到终端

进阶彩色渐变心跳(多色轮换爱心,更炫酷)

bash

运行

复制代码
rm -rf ~/colorbeat && mkdir ~/colorbeat && cd ~/colorbeat && cat > cbeat.c <<EOF
#include <stdio.h>
#include <math.h>
#include <unistd.h>

int main() {
    float t = 0;
    int color[] = {31,35,36,33,32}; // 红/紫/青/黄/绿
    int c_idx = 0;
    while (1) {
        printf("\033[2J\033[H");
        float scale = 0.6 + 0.7 * sin(t);
        for (float y = 1.4f; y > -1.4f; y -= 0.08f) {
            for (float x = -1.4f; x < 1.4f; x += 0.04f) {
                float sx = x / scale;
                float sy = y / scale;
                float val = pow(sx*sx + sy*sy - 1, 3) - sx*sx * pow(sy, 3);
                if (val < 0) {
                    printf("\033[1;%dm*\033[0m", color[c_idx]);
                } else printf(" ");
            }
            printf("\n");
        }
        t += 0.08f;
        // 切换颜色
        if(fmod(t, 1.5) < 0.08) c_idx = (c_idx + 1) % 5;
        usleep(60000);
    }
    return 0;
}
EOF
gcc cbeat.c -o cbeat -lm && ./cbeat



为什么要学习 Linux?全方位讲清价值、用途、就业与底层逻辑

一、Linux 是互联网世界的底层基石(无处不用)

1. 服务器几乎全是 Linux

全球 90% 以上的云服务器、网站后端、小程序、APP 后台、电商平台、游戏服务器都跑 Linux(CentOS、Ubuntu、Debian、Rocky)。

  • 阿里云、腾讯云、华为云、AWS 主流实例默认 Linux;
  • 百度、抖音、淘宝、微信、B 站后台全基于 Linux; Windows Server 只占极小份额,企业生产环境优先 Linux。

2. 云、容器、大数据、AI 底层依赖 Linux

  • 容器 Docker、编排工具 K8s(kubernetes)只能跑在 Linux
  • 大数据 Hadoop、Spark、Flink,数据库 MySQL/Redis/MongoDB 标准运行环境是 Linux;
  • AI 深度学习框架 PyTorch、TensorFlow 训练服务器清一色 Linux;
  • 云计算、云原生是当下高薪赛道,不懂 Linux 完全入门不了。

3. 智能设备、嵌入式全是 Linux

手机安卓内核 = Linux;路由器、智能家居、机顶盒、车载系统、工业机器人、监控摄像头、无人机底层都是裁剪版 Linux。做嵌入式、物联网开发必须学。

4. 开发、运维、测试必备环境

后端开发(Java/Go/Python/PHP)本地写完代码,上线要部署到 Linux,不会操作服务器无法上线项目; 自动化测试、性能测试、运维、安全渗透都离不开 Linux 命令。

5. 个人电脑、开源圈主流系统

Ubuntu、Fedora、Deepin 等桌面 Linux:稳定、流畅、无广告、不吃配置、适合编程;程序员很多直接用 Linux 当本机开发环境。

二、Linux 本身的技术优势(企业偏爱它的原因)

  1. 开源免费 无版权费、可自由修改内核,中小企业、大厂不用支付系统授权成本,Windows Server 商用需要高额授权。
  2. 稳定性极强 服务器常年连续运行数年不用重启,宕机概率极低;多用户、高并发承载能力远超 Windows。
  3. 资源占用低 Minimal 最小化安装仅占用几十 MB 内存,低配机器也能搭建服务,云服务器成本更低。
  4. 安全漏洞更少 权限模型严谨(用户、组、文件权限、防火墙 selinux),病毒木马极少,适合对外提供公网服务。
  5. 高度可定制 可按需裁剪内核、安装最小组件,适配服务器、嵌入式、超级计算机各种场景;全球 TOP500 超算 100% Linux。

三、职业就业核心刚需(不学直接少大半岗位)

1. 运维 / 云原生岗位(核心技能)

运维工程师、DevOps、K8s 运维、云平台运维、数据库运维,招聘第一条要求精通 Linux,不会直接简历淘汰。 薪资:一线城市运维普遍 10k--30k,资深云原生 25k--50k。

2. 后端开发(必加分项,甚至硬性要求)

Java、Go、Python、PHP 后端工程师: 日常需要登录服务器、查看日志、部署项目、排查报错、调优服务,不懂 Linux 只能做本地开发,无法独立负责线上项目。大厂面试必考 Linux 命令、进程、磁盘、网络排查。

3. 大数据 / AI 算法工程师

数据集、分布式集群、模型训练全部部署 Linux,需要会脚本、文件操作、集群调度。

4. 网络安全 / 渗透测试 / 等保

漏洞扫描、渗透、抓包、应急响应、搭建靶机环境全部基于 Linux,黑客工具(kali)就是定制 Linux。

5. 嵌入式 / IoT 开发

车载、智能家居、工控开发岗位,笔试面试大量 Linux 内核、驱动、shell 脚本题目。

6. 测试开发、自动化测试

线上测试环境、接口自动化、性能压测工具都运行在 Linux,需要日志排查、环境搭建。

总结:IT 行业 80% 高薪岗位,都把 Linux 列为基础门槛技能,只会 Windows 可选岗位极少。

四、学习 Linux 能提升你的底层计算机思维

  1. 理解操作系统本质 Windows 封装太深,看不到底层;Linux 一切皆文件(硬件、进程、网络、磁盘都是文件),能看懂:进程调度、内存管理、文件系统、权限机制、网络协议栈,真正搞懂操作系统原理。

  2. 学会自动化思维(Shell 脚本) 重复部署、日志清理、定时任务用 shell 一键自动化,告别鼠标点点,提升工作效率,也是高级工程师核心能力。

  3. 掌握排错、性能调优能力 线上卡顿、接口超时、磁盘爆满、内存泄漏、端口占用,全部靠 Linux 命令定位问题,培养独立解决线上故障的能力,是区分初级和资深工程师的关键。

  4. 标准行业通用能力 无论去哪家互联网、政企、制造业,服务器操作逻辑统一,学会一套命令通吃所有企业,不存在换公司就要重新学系统的情况。

五、个人提升、副业与进阶优势

  1. 自建个人服务器:搭建博客、网盘、代理、小型网站、私有云,低成本折腾;
  2. 自学技术门槛降低:绝大多数开源软件、工具优先支持 Linux;
  3. 进阶学习必经之路:学 Docker/K8s、微服务、大数据、内核开发,都要先夯实 Linux 基础;
  4. 副业接单:帮中小企业部署网站、维护云服务器、搭建集群,接单增收。

六、对比不学 Linux 的劣势

  • 只能做简单桌面办公、前端纯页面开发,无法接触后端、服务器、云原生等高价值业务;
  • 求职选择面窄,同等能力下薪资比会 Linux 的人低一截;
  • 遇到线上环境问题完全无从下手,只能依赖运维,职场竞争力弱。

简单总结一句话

Linux 是互联网、云计算、AI、嵌入式的底层操作系统,是 IT 行业通用基础技能;学会它既能打开绝大多数高薪技术岗位大门,又能真正吃透计算机底层原理,是程序员、运维、数据、安全从业者绕不开的必修课。




实验七 CMakeLists.txt 编写 完整实验报告

学号:________ 姓名:________ 分数:________

一、实验目的

  1. 掌握 Linux 线程 pthread_create()pthread_join() 创建与回收线程;
  2. 掌握互斥锁 pthread_mutex_lock() / pthread_mutex_unlock() 解决多线程资源竞争;
  3. 熟练编写 CMakeLists.txt,实现可执行程序、动态链接库分离输出(bin 存放程序、lib 存放 so 库);
  4. 拓展:复用 CMake 知识完成 SDL2 地牢游戏项目 DungeonRushDemo 构建脚本解析与部署。

二、实验设备

  1. 个人笔记本电脑;
  2. 蓝桥云课 Linux 开发环境;
  3. 实验素材:mytest.zip 源码包。

三、实验原理

1. Linux 多线程基础

  • pthread_create(&tid, nullptr, func, arg):创建子线程,绑定执行函数;
  • pthread_join(tid, &ret):主线程阻塞等待子线程结束,回收资源;
  • pthread_mutex_t 互斥锁:多线程同时读写共享计算结果时,加锁保证数据线程安全,防止数据错乱。

2. CMake 核心知识点

  1. add_library(xxx SHARED):编译生成 .so 动态库;
  2. add_executable():生成可执行程序;
  3. CMAKE_RUNTIME_OUTPUT_DIRECTORY:控制可执行文件输出到 bin
  4. CMAKE_LIBRARY_OUTPUT_DIRECTORY:控制动态库输出到 lib
  5. target_link_libraries:链接自定义动态库 + pthread 线程库;
  6. 跨平台依赖查找、头文件目录配置、资源拷贝等(拓展 DungeonRush 项目使用)。

四、实验内容

  1. 封装 Calculator 计算类,使用 Result 结构体统一存储四则运算结果;
  2. 编写加、减、乘、除四类线程执行函数,使用互斥锁保护共享结果结构体;
  3. main 函数批量创建四则运算线程,等待所有线程执行完毕;
  4. 补全项目 CMakeLists.txt 缺失代码,实现:
    • 编译自定义四则运算动态库;
    • 编译链接动态库的主程序;
    • 程序输出至 build/bin,动态库输出至 build/lib
  5. 使用 cmake + make 编译运行,查看多线程计算输出;
  6. 拓展任务:基于同套 CMake 语法,完成 DungeonRushDemo 地牢游戏项目完整部署、脚本解析、一键编译运行。

五、实验步骤

步骤 1:源码补全(mytest.zip 工程)

  1. 解压 mytest.zip,打开头文件、源文件,填充所有 TODO 标记代码;
  2. 完善减法、乘法、除法线程函数,复用加法线程逻辑;
  3. 主线程依次创建加法、减法、乘法、除法 4 个线程,调用 pthread_join 等待全部线程结束;
  4. 全局互斥锁初始化,在线程函数读写共享 Result 前后加锁、解锁。

步骤 2:补全 CMakeLists.txt(实验核心)

需求:

  • 源码编译为动态库 ,输出到 build/lib
  • main 主程序链接动态库,可执行文件输出到 build/bin
  • 链接 pthread 线程库;
  • 设置最低 CMake 版本,项目名、头文件搜索路径。

参考完整实验 CMakeLists.txt:

cmake

复制代码
# 最低cmake版本要求
cmake_minimum_required(VERSION 3.10)
# 定义项目名
project(CalcThreadDemo LANGUAGES C CXX)

# 1. 设置输出目录:程序→bin,动态库→lib
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib)

# 2. 收集库源码、主程序源码
file(GLOB LIB_SRC src/calculator.cpp src/result.cpp)
file(GLOB MAIN_SRC main.cpp)

# 3. 编译动态库 libcalc.so
add_library(calc SHARED ${LIB_SRC})
# 指定头文件目录
target_include_directories(calc PUBLIC ${CMAKE_SOURCE_DIR}/include)

# 4. 编译主程序
add_executable(calc_main ${MAIN_SRC})
# 链接自定义动态库 + pthread线程库
target_link_libraries(calc_main calc pthread)

步骤 3:编译运行流程

bash

运行

复制代码
# 1. 创建编译目录
mkdir build && cd build
# 2. cmake生成构建文件
cmake ..
# 3. 多线程编译
make -j$(nproc)
# 4. 进入bin运行程序
cd bin
./calc_main

步骤 4:拓展任务 DungeonRushDemo 地牢游戏项目

  1. 克隆源码仓库,安装 SDL2 全套图形依赖;
  2. 逐行解析项目自带 SDL2 专用 CMakeLists.txt;
  3. 执行 cmake 编译,自动复制游戏资源文件夹 res;
  4. 运行 build/bin/dungeon_rush 启动地牢冒险游戏;
  5. 对比线程实验 CMake 与游戏 CMake 的异同(动态库、第三方库查找、资源拷贝)。

六、核心代码注释说明

1. 多线程计算核心伪代码(带注释)

cpp

运行

复制代码
#include <pthread.h>
#include <iostream>
#include "calculator.h"

// 全局共享结果结构体
Result res;
// 全局互斥锁
pthread_mutex_t mutex;

// 加法线程函数
void* add_thread(void* arg)
{
    // 加锁,独占共享资源
    pthread_mutex_lock(&mutex);
    Calculator calc;
    res.add_ret = calc.add(100, 20);
    std::cout << "加法结果:" << res.add_ret << std::endl;
    // 解锁,释放资源
    pthread_mutex_unlock(&mutex);
    return nullptr;
}

// 减法线程
void* sub_thread(void* arg)
{
    pthread_mutex_lock(&mutex);
    Calculator calc;
    res.sub_ret = calc.sub(100, 20);
    std::cout << "减法结果:" << res.sub_ret << std::endl;
    pthread_mutex_unlock(&mutex);
    return nullptr;
}

// 乘法、除法线程逻辑同上...

int main()
{
    // 初始化互斥锁
    pthread_mutex_init(&mutex, nullptr);
    pthread_t t1, t2, t3, t4;

    // 创建四则运算线程
    pthread_create(&t1, nullptr, add_thread, nullptr);
    pthread_create(&t2, nullptr, sub_thread, nullptr);
    pthread_create(&t3, nullptr, mul_thread, nullptr);
    pthread_create(&t4, nullptr, div_thread, nullptr);

    // 等待所有线程执行完毕
    pthread_join(t1, nullptr);
    pthread_join(t2, nullptr);
    pthread_join(t3, nullptr);
    pthread_join(t4, nullptr);

    // 销毁互斥锁
    pthread_mutex_destroy(&mutex);
    return 0;
}

2. DungeonRushDemo CMakeLists.txt 逐行解析(拓展代码)

cmake

复制代码
# 指定最低CMake版本,SDL2现代目标语法要求3.5.1以上
cmake_minimum_required(VERSION 3.5.1)
# 游戏项目名称
project(DungeonRush)

# 将项目内cmake/sdl2加入模块搜索路径,用于查找SDL2库
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/sdl2)

# 设置可执行文件输出到build/bin
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)

# 查找SDL2全套图形、音频、网络、字体依赖,找不到直接报错
find_package(SDL2 REQUIRED)
find_package(SDL2_image REQUIRED)
find_package(SDL2_net REQUIRED)
find_package(SDL2_mixer REQUIRED)
find_package(SDL2_ttf REQUIRED)

# 定义程序名变量,统一管理
set(BIN_NAME dungeon_rush)

# 自动扫描src下所有C语言源码
file(GLOB SRC src/*.c)

# 根据源码生成游戏可执行程序
add_executable(${BIN_NAME} ${SRC})

# 添加头文件搜索目录:项目src目录 + SDL2系统头文件
target_include_directories(${BIN_NAME} PUBLIC src)
target_include_directories(${BIN_NAME} PUBLIC ${SDL2_INCLUDE_DIR})

# 链接SDL2所有扩展库 + Linux数学库libm
target_link_libraries(${BIN_NAME} SDL2::Main SDL2::Net SDL2::Image SDL2::Mixer SDL2::TTF m)

# 复制游戏资源文件夹res到bin目录,运行时读取图片音效
file(COPY res DESTINATION ${CMAKE_RUNTIME_OUTPUT_DIRECTORY})

七、运行结果截图说明

1. 线程实验运行截图描述

  1. 编译目录结构:
    • build/lib/libcalc.so 生成动态库;
    • build/bin/calc_main 生成可执行程序;
  2. 终端输出:依次打印加法、减法、乘法、除法计算结果,多线程无序输出但数值无错乱(互斥锁生效);
  3. 截图要点:目录树 + 终端运行输出。

2. DungeonRushDemo 游戏运行截图描述

  1. 编译后 build/bin/ 存在 dungeon_rush 程序 + res 资源文件夹;
  2. 执行程序弹出 SDL2 游戏窗口,地牢闯关游戏正常运行,支持单人 / 多人联机;
  3. 截图要点:编译命令、文件夹结构、游戏窗口画面。

八、实验分析与问题总结

1. 线程实验常见问题

  1. 编译报错 undefined reference to pthread_create 解决:CMakeLists.txt 链接 pthread 库;
  2. 多线程结果混乱、数值错误 解决:读写共享结构体前后添加互斥锁,避免并发抢占;
  3. 动态库找不到,运行提示 error while loading shared libraries 解决:export LD_LIBRARY_PATH=./lib 指定动态库搜索路径。

2. CMake 通用问题

  1. CMAKE_RUNTIME_OUTPUT_DIRECTORY 不生效 确认变量写在 add_executable 之前;
  2. 第三方库 SDL2 找不到 系统安装对应 dev 开发包,或补充 FindSDL2.cmake 模块文件;
  3. 游戏运行提示图片缺失 file(COPY res) 未执行,资源未复制到 bin 输出目录。

3. 两个项目 CMake 对比

表格

对比项 线程计算实验 DungeonRush 游戏项目
语言 C++ C 语言
产物 动态库 + 主程序 单一可执行程序
依赖 pthread 系统线程库 SDL2 图形多媒体全套库
特殊逻辑 无资源拷贝 自动复制图片音效资源
模块查找 系统默认路径 自定义 cmake/sdl2 查找脚本

九、实验心得

  1. 多线程并发操作共享变量必须使用互斥锁,否则会出现数据竞争、结果异常;pthread_create 创建线程后必须 pthread_join 回收资源防止内存泄漏;
  2. CMake 实现了跨平台统一构建,通过变量统一控制输出目录,区分动态库、可执行文件存放路径,工程结构更整洁;
  3. find_package 可以自动化查找第三方开发库,配合 target_link_libraries 一键链接依赖,相比手写 Makefile 大幅简化跨平台项目维护;
  4. 拓展地牢游戏项目加深了对图形类 C 项目 CMake 配置的理解,学会资源文件自动拷贝、多媒体库链接等实战技巧,掌握 Linux 下 C/C++ 项目完整开发、编译、运行流程。

附:DungeonRushDemo 完整一键部署命令(拓展实验执行脚本)

bash

运行

复制代码
# 更新软件源,安装SDL2全套依赖与编译工具
sudo apt update && sudo apt install git cmake gcc make libsdl2-dev libsdl2-image-dev libsdl2-mixer-dev libsdl2-net-dev libsdl2-ttf-dev -y
# 克隆游戏源码
git clone https://gitcode.com/ZhangRelay1/DungeonRushDemo.git
cd DungeonRushDemo
# 创建编译目录并构建
mkdir build && cd build
cmake ..
make -j$(nproc)
# 启动游戏
cd bin
./dungeon_rush

本文摘要主要介绍了面向对象和面向过程编程的区别,以及Linux系统在计算机编程和IT行业中的重要性。面向过程编程以步骤为中心,数据和操作分离,适合简单任务;面向对象编程以实体为中心,封装数据和操作,适合复杂项目。Linux作为互联网底层基石,广泛应用于服务器、云服务、嵌入式等领域,是IT从业者必备技能。文章还提供了多线程编程实验、CMake构建工具使用以及动态爱心代码等实用技术内容,帮助读者掌握编程核心概念和Linux环境下的开发技能。