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

智能大模型理解有小偏差,但整体符合两种编程特征。
用 KUKA 移动机械臂搬运木箱案例,通俗区分 C++ 面向过程 & 面向对象
场景任务:让图里的库卡移动机械臂,完成「移动到木箱旁→抓取木箱→平移运走→松开放置」整套搬运工作
一、面向过程:只关心「一步步做什么」(C 式线性步骤思维)
核心思路:盯着完整操作流程,把所有动作拆成顺序步骤,数据和动作完全分开
生活化理解
你是现场操作工,全程按清单逐条执行,所有工具、物体只是一堆孤立数据,没有自主能力:
- 给机械臂底盘输入坐标,向前移动到木箱侧边
- 读取木箱尺寸、高度数据,计算机械臂关节旋转角度
- 控制机械臂各关节转动,夹爪闭合夹紧木箱
- 抬高机械臂抬升木箱离地
- 底盘向后平移一段距离
- 夹爪打开,放下木箱
- 机械臂复位收回
全程只有顺序动作函数,没有 "机械臂""木箱" 这种独立个体:
- 单独存变量:
木箱长宽高、机械臂各关节角度、底盘位置(纯数据) - 单独写函数:
移动底盘()、旋转关节()、夹爪夹紧()、抬升臂杆()(纯操作) 数据和操作互不绑定,谁都能随便修改数据。
简易 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;
}
面向过程缺点对应这个场景
- 项目变大后步骤堆砌,流程缠成乱麻;再加第二个木箱、第二台机械臂,要重复复制一堆变量和函数,极易写错。
- 数据无保护:任何人都能直接修改
arm_j1关节角度,可能出现机械臂撞木箱、过载损坏的逻辑漏洞。 - 不符合现实认知:现实里 "库卡机械臂" 是一个完整设备,这里却拆得四分五裂。
二、面向对象:先划分「实体对象」,让对象自己拥有属性和能力(C++ 核心思维)
核心思路:先识别场景里独立事物,每个事物封装「自身特征(属性)+ 自身能做的动作(方法)」,我们只需要下达指令,不用管内部细节
生活化理解
你是车间调度员,不用亲自操作每一步;先定义两个独立 "东西":KUKA 移动机械臂 、木箱,每个东西自带自己的信息和功能:
- 对象 1:KUKA 机械臂
- 属性(自身特征):底盘坐标、各关节角度、夹爪开合状态、最大承重
- 自带能力(自己能完成的动作):底盘移动、关节旋转、夹爪抓取、抬升货物、复位
- 对象 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;
}
面向对象对应场景的优势
- 贴合现实世界:画面里就是「机械臂」和「木箱」两个物体,代码结构和肉眼看到的场景一一对应,可读性极强。
- 高度复用:新增第二台 KUKA 机械臂
KukaRobot kuka2;、第二个木箱Box box2;,直接调用transportBox就能复用整套搬运逻辑,不用重复写步骤。 - 数据安全:可以把属性设为私有,外部不能随意乱改机械臂关节、木箱位置,避免撞机 bug(封装特性)。
- 方便扩展:后续想升级机械臂,增加「避障」「视觉识别箱子」功能,只需要在
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.csrc/main.csrc/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 进程与线程操作 分步实验流程
前置准备
-
打开蓝桥云课平台,进入本次实验对应的 Linux 在线环境,打开终端窗口。
-
执行基础命令确认环境: bash
运行
pwd # 查看当前工作目录 mkdir exp6 # 新建实验文件夹 cd exp6 # 进入实验目录,所有代码在此目录编写运行
任务一:测试 wait.cpp,对比有无 wait()、wait(NULL) 的运行差异
步骤 1:编写无 wait () 版本代码
-
使用文本编辑器创建文件: bash
运行
vim wait.cpp -
按
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;
}
- 按
Esc,输入:wq保存并退出 vim。
步骤 2:编译、运行,观察结果
-
编译代码: bash
运行
g++ wait.cpp -o wait -
运行程序: bash
运行
./wait -
现象记录 : 父进程先执行并退出,2 秒后子进程才输出内容,父子进程执行顺序混乱、异步执行。
步骤 3:修改代码,添加 wait(NULL)
-
重新编辑文件: bash
运行
vim wait.cpp -
在父进程代码中添加头文件与
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;
}
:wq保存退出。
步骤 4:重新编译运行,对比结果
-
重新编译(代码修改后必须重编): bash
运行
g++ wait.cpp -o wait -
运行: bash
运行
./wait -
现象记录 : 父进程执行到
wait(NULL)后阻塞,等待子进程完全运行结束,父进程才继续执行,输出顺序固定。
任务一总结
- 无
wait():父子进程并发执行,顺序随机; - 有
wait(NULL):父进程阻塞,实现进程同步,保证子进程先退出。
任务二:编写 Zombie.cpp,产生并解决僵尸进程
步骤 1:编写产生僵尸进程的原始代码
-
新建文件: bash
运行
vim Zombie.cpp -
写入代码:
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;
}
:wq保存退出。
步骤 2:编译运行,查看僵尸进程
-
编译: bash
运行
g++ Zombie.cpp -o zombie -
终端第一个窗口 运行程序:
bash
运行
./zombie -
立刻新开一个终端窗口 ,执行查看僵尸进程命令:
bash
运行
ps aux | grep Z -
现象记录 : 可以查询到状态标记为
Z(僵尸)的进程,僵尸进程产生。 -
等待 10 秒,原程序运行结束,僵尸进程自动消失。
步骤 3:修改代码,使用 waitpid() 解决僵尸进程
-
回到原目录,编辑代码: bash
运行
vim Zombie.cpp -
添加头文件与
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;
}
:wq保存退出。
步骤 4:再次测试,验证僵尸进程已消除
-
重新编译: bash
运行
g++ Zombie.cpp -o zombie -
运行程序,同时新开终端执行: bash
运行
./zombie ps aux | grep Z -
现象记录 : 查询不到
Z状态进程,僵尸进程被彻底解决。
任务二总结
- 僵尸进程成因:子进程退出,父进程未调用
wait/waitpid回收资源; - 解决方案:父进程调用
wait()或waitpid()回收子进程。
任务三:改造 pthread_con.cpp,新增 2 个线程,四线程有序输出 1~36
步骤 1:编写最终四线程代码
-
新建文件: bash
运行
vim pthread_con.cpp -
写入完整代码(含 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;
}
: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,输出有序、无乱序、无重复。
整体收尾步骤
-
整理每一步运行截图:分别截取三个程序的代码界面、编译命令、运行输出、僵尸进程查询结果。
-
对照实验现象,填写实验报告的实验现象、结果分析、总结部分。
-
可选清理(环境恢复): bash
运行
rm -f wait zombie pthread_con # 删除可执行文件
关键操作速记(易错点提醒)
- 编辑文件:
vim 文件名,i编辑,Esc+:wq保存退出; - 多线程编译必须加
-lpthread,否则编译报错; - 查看僵尸进程命令:
ps aux | grep Z; 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
核心跳动原理说明
- 缩放控制
scale = 0.6 + 0.7 * sin(t)sin(t)取值范围 -1,1,所以 scale 区间0.6 ~ 1.3- t 不断累加,scale 周期性变大变小,画面爱心同步缩放,实现 "蹦跳" 效果
- 清屏转义码
\033[2J\033[H:每次刷新清空屏幕,无残影 - 编译必须带
-lm:链接数学库pow/sin,否则编译报错 - 调速:修改
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 本身的技术优势(企业偏爱它的原因)
- 开源免费 无版权费、可自由修改内核,中小企业、大厂不用支付系统授权成本,Windows Server 商用需要高额授权。
- 稳定性极强 服务器常年连续运行数年不用重启,宕机概率极低;多用户、高并发承载能力远超 Windows。
- 资源占用低 Minimal 最小化安装仅占用几十 MB 内存,低配机器也能搭建服务,云服务器成本更低。
- 安全漏洞更少 权限模型严谨(用户、组、文件权限、防火墙 selinux),病毒木马极少,适合对外提供公网服务。
- 高度可定制 可按需裁剪内核、安装最小组件,适配服务器、嵌入式、超级计算机各种场景;全球 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 能提升你的底层计算机思维
-
理解操作系统本质 Windows 封装太深,看不到底层;Linux 一切皆文件(硬件、进程、网络、磁盘都是文件),能看懂:进程调度、内存管理、文件系统、权限机制、网络协议栈,真正搞懂操作系统原理。
-
学会自动化思维(Shell 脚本) 重复部署、日志清理、定时任务用 shell 一键自动化,告别鼠标点点,提升工作效率,也是高级工程师核心能力。
-
掌握排错、性能调优能力 线上卡顿、接口超时、磁盘爆满、内存泄漏、端口占用,全部靠 Linux 命令定位问题,培养独立解决线上故障的能力,是区分初级和资深工程师的关键。
-
标准行业通用能力 无论去哪家互联网、政企、制造业,服务器操作逻辑统一,学会一套命令通吃所有企业,不存在换公司就要重新学系统的情况。
五、个人提升、副业与进阶优势
- 自建个人服务器:搭建博客、网盘、代理、小型网站、私有云,低成本折腾;
- 自学技术门槛降低:绝大多数开源软件、工具优先支持 Linux;
- 进阶学习必经之路:学 Docker/K8s、微服务、大数据、内核开发,都要先夯实 Linux 基础;
- 副业接单:帮中小企业部署网站、维护云服务器、搭建集群,接单增收。
六、对比不学 Linux 的劣势
- 只能做简单桌面办公、前端纯页面开发,无法接触后端、服务器、云原生等高价值业务;
- 求职选择面窄,同等能力下薪资比会 Linux 的人低一截;
- 遇到线上环境问题完全无从下手,只能依赖运维,职场竞争力弱。
简单总结一句话
Linux 是互联网、云计算、AI、嵌入式的底层操作系统,是 IT 行业通用基础技能;学会它既能打开绝大多数高薪技术岗位大门,又能真正吃透计算机底层原理,是程序员、运维、数据、安全从业者绕不开的必修课。
实验七 CMakeLists.txt 编写 完整实验报告
学号:________ 姓名:________ 分数:________
一、实验目的
- 掌握 Linux 线程
pthread_create()、pthread_join()创建与回收线程; - 掌握互斥锁
pthread_mutex_lock()/pthread_mutex_unlock()解决多线程资源竞争; - 熟练编写
CMakeLists.txt,实现可执行程序、动态链接库分离输出(bin 存放程序、lib 存放 so 库); - 拓展:复用 CMake 知识完成 SDL2 地牢游戏项目 DungeonRushDemo 构建脚本解析与部署。
二、实验设备
- 个人笔记本电脑;
- 蓝桥云课 Linux 开发环境;
- 实验素材:
mytest.zip源码包。
三、实验原理
1. Linux 多线程基础
pthread_create(&tid, nullptr, func, arg):创建子线程,绑定执行函数;pthread_join(tid, &ret):主线程阻塞等待子线程结束,回收资源;pthread_mutex_t互斥锁:多线程同时读写共享计算结果时,加锁保证数据线程安全,防止数据错乱。
2. CMake 核心知识点
add_library(xxx SHARED):编译生成.so动态库;add_executable():生成可执行程序;CMAKE_RUNTIME_OUTPUT_DIRECTORY:控制可执行文件输出到bin;CMAKE_LIBRARY_OUTPUT_DIRECTORY:控制动态库输出到lib;target_link_libraries:链接自定义动态库 + pthread 线程库;- 跨平台依赖查找、头文件目录配置、资源拷贝等(拓展 DungeonRush 项目使用)。
四、实验内容
- 封装
Calculator计算类,使用Result结构体统一存储四则运算结果; - 编写加、减、乘、除四类线程执行函数,使用互斥锁保护共享结果结构体;
- 在
main函数批量创建四则运算线程,等待所有线程执行完毕; - 补全项目
CMakeLists.txt缺失代码,实现:- 编译自定义四则运算动态库;
- 编译链接动态库的主程序;
- 程序输出至
build/bin,动态库输出至build/lib;
- 使用
cmake + make编译运行,查看多线程计算输出; - 拓展任务:基于同套 CMake 语法,完成 DungeonRushDemo 地牢游戏项目完整部署、脚本解析、一键编译运行。
五、实验步骤
步骤 1:源码补全(mytest.zip 工程)
- 解压
mytest.zip,打开头文件、源文件,填充所有TODO标记代码; - 完善减法、乘法、除法线程函数,复用加法线程逻辑;
- 主线程依次创建加法、减法、乘法、除法 4 个线程,调用
pthread_join等待全部线程结束; - 全局互斥锁初始化,在线程函数读写共享 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 地牢游戏项目
- 克隆源码仓库,安装 SDL2 全套图形依赖;
- 逐行解析项目自带 SDL2 专用 CMakeLists.txt;
- 执行 cmake 编译,自动复制游戏资源文件夹 res;
- 运行
build/bin/dungeon_rush启动地牢冒险游戏; - 对比线程实验 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. 线程实验运行截图描述
- 编译目录结构:
build/lib/libcalc.so生成动态库;build/bin/calc_main生成可执行程序;
- 终端输出:依次打印加法、减法、乘法、除法计算结果,多线程无序输出但数值无错乱(互斥锁生效);
- 截图要点:目录树 + 终端运行输出。
2. DungeonRushDemo 游戏运行截图描述
- 编译后
build/bin/存在dungeon_rush程序 +res资源文件夹; - 执行程序弹出 SDL2 游戏窗口,地牢闯关游戏正常运行,支持单人 / 多人联机;
- 截图要点:编译命令、文件夹结构、游戏窗口画面。
八、实验分析与问题总结
1. 线程实验常见问题
- 编译报错
undefined reference to pthread_create解决:CMakeLists.txt 链接pthread库; - 多线程结果混乱、数值错误 解决:读写共享结构体前后添加互斥锁,避免并发抢占;
- 动态库找不到,运行提示
error while loading shared libraries解决:export LD_LIBRARY_PATH=./lib指定动态库搜索路径。
2. CMake 通用问题
CMAKE_RUNTIME_OUTPUT_DIRECTORY不生效 确认变量写在add_executable之前;- 第三方库 SDL2 找不到 系统安装对应 dev 开发包,或补充 FindSDL2.cmake 模块文件;
- 游戏运行提示图片缺失
file(COPY res)未执行,资源未复制到 bin 输出目录。
3. 两个项目 CMake 对比
表格
| 对比项 | 线程计算实验 | DungeonRush 游戏项目 |
|---|---|---|
| 语言 | C++ | C 语言 |
| 产物 | 动态库 + 主程序 | 单一可执行程序 |
| 依赖 | pthread 系统线程库 | SDL2 图形多媒体全套库 |
| 特殊逻辑 | 无资源拷贝 | 自动复制图片音效资源 |
| 模块查找 | 系统默认路径 | 自定义 cmake/sdl2 查找脚本 |
九、实验心得
- 多线程并发操作共享变量必须使用互斥锁,否则会出现数据竞争、结果异常;
pthread_create创建线程后必须pthread_join回收资源防止内存泄漏; - CMake 实现了跨平台统一构建,通过变量统一控制输出目录,区分动态库、可执行文件存放路径,工程结构更整洁;
find_package可以自动化查找第三方开发库,配合target_link_libraries一键链接依赖,相比手写 Makefile 大幅简化跨平台项目维护;- 拓展地牢游戏项目加深了对图形类 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环境下的开发技能。