30.第二阶段x86游戏实战2-遍历周围-C++遍历二叉树(玩家角色基址)

news2024/10/18 23:07:19

免责声明:内容仅供学习参考,请合法利用知识,禁止进行违法犯罪活动!

本次游戏没法给

内容参考于:微尘网络安全

本人写的内容纯属胡编乱造,全都是合成造假,仅仅只是为了娱乐,请不要盲目相信。

工具下载:

链接:https://pan.baidu.com/s/1rEEJnt85npn7N38Ai0_F2Q?pwd=6tw3

提取码:6tw3

复制这段内容后打开百度网盘手机App,操作更方便哦

上一个内容:29.第二阶段x86游戏实战2-遍历周围-花指令与二叉树数据结构(有如何阅读vm代码混淆代码)

上一个内容里找到了附近npc列表,这个列表采用二叉树数据结构存放的,本次就来使用C++遍历这个二叉树

本次要做的事情,通过C++代码把二叉树的数据全部放到列表里,也就是把二叉树的数据改成下图的样子一次排序,排序的顺序以npc和玩家角色的距离来搞,离得近就排前面离得远就排后面

在上一个内容里分析遍历二叉树的那块写的是0是非当前选中怪物1是当前选中怪物,这不怎么准确,应该说循坏结束条件是不等于0的时候,而不是0是非当前1或其它是当前选择,这个0它仅仅是一个结束循环的条件

现在得到的东西,0x74EFA4是通过偏移得到的上一个内容里的二叉树基址

[[[[[[0x74EFA4]+0x54]+0x4]+0x14]+0x148]+0x10]+0x8 血量 [[[[[[0x74EFA4]+0x54]+0x4]+0x14]+0x148]+0x10]+0x2C名字 [[[[0x74EFA4]+0x54]+0x4]+0x14] npc对象地址 [[[[0x74EFA4]+0x54]+0x4]+0x14]+0x3C x坐标 [[[[0x74EFA4]+0x54]+0x4]+0x14]+0x40 z坐标(高度) [[[[0x74EFA4]+0x54]+0x4]+0x14]+0x44 y坐标 [[[0x74EFA4]+0x54]+0x4] 二叉树第一个数据

然后上面的数据怎样找的,首先使用 [[[0x74EFA4]+0x54]+0x4] 得到第一个数据,[[[0x74EFA4]+0x54]+0x4]这个公式是通过上一个内容分析遍历二叉树的代码得到的,然后使用 dd [[[0x74EFA4]+0x54]+0x4]如下图内存区域

然后偏移0x14位置是对象,也就是下图红框位置

然后数据窗口中跟随过去

跟随之后

然后偏移 0x3C、0x40、0x44位置是坐标,使用浮点数查看

如下图,是坐标数据,为了验证可以在游戏总走到下图红框所示的坐标,看看哪里是否有npc,这里走过去看了是有一个怪物的

然后下一个偏移是找名字首先是0x148,首先把内存区域再切换为地址

找到0x148位置进行数据窗口跟随

跟随之后

然后是偏移0x10位置,然后在0x10位置继续数据窗口跟随

跟随之后,使用ASCII数据地址显示内存

如下图可以看到名字了,到这如果找数据就结束了,也就是一个观察数据,怎样就确定某某偏移就是某数据了就是平感觉,就感觉它像,这种感觉分析多了就会有了

为了一个功能接下来要找一下我们角色的对象基址,使用名字、血量等这种属性是无法找到人物角色基址的,我们人物的数据也在上方二叉树里,所以要通过把二叉树遍历出来,然后把名字和对象内存地址打印出来,然后复制内存地址到CE里搜,如下图

这里是通过使用OD对 010F559C 地址下硬件访问断点追到的人物角色基址的,最终得到的公式[0x766A8C]+0xC,只按了一次CTRL+F9就来到下图位置了,所以不详细写过程了

它的偏移,0x5A6A8C

C++代码与逆向代码对应关系

首先是第一个数据

取左右两边的数据

使用递归循环获取二叉树全部数据,递归循环就是函数自己调用自己,如下图

新加遍历周围按钮

CM.cpp文件的修改 :新加遍历周围按钮点击事件处理函数

// CM.cpp: 实现文件
//

#include "pch.h"
#include "tl.h"
#include "CM.h"
#include "afxdialogex.h"


// CM 对话框

IMPLEMENT_DYNAMIC(CM, CDialogEx)

CM::CM(CWnd* pParent /*=nullptr*/)
	: CDialogEx(IDD_DIALOG1, pParent)
	, edi_x(_T(""))
{

}

CM::~CM()
{
}

void CM::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Text(pDX, IDC_EDIT1, edi_x);
	DDX_Text(pDX, IDC_EDIT2, edi_y);
}


BEGIN_MESSAGE_MAP(CM, CDialogEx)
	ON_BN_CLICKED(IDC_BUTTON1, &CM::OnBnClickedButton1)
	ON_BN_CLICKED(IDC_BUTTON2, &CM::OnBnClickedButton2)
	ON_BN_CLICKED(IDC_BUTTON3, &CM::OnBnClickedButton3)
	ON_BN_CLICKED(IDC_BUTTON4, &CM::OnBnClickedButton4)
END_MESSAGE_MAP()


// CM 消息处理程序


void CM::OnBnClickedButton1()
{
	R_人物属性 a;
	a.初始化();
	Call_输出调试信息("人物信息:人物状态%d",a.状态);
}


void CM::OnBnClickedButton2()
{
	UpdateData(TRUE);
	CString str1 = edi_x;
	CString str2 = edi_y;
	// strtol((const char*)CW2A(str1.GetBuffer(0)), NULL, 10);把字符串转成int数字类型
	int x = strtol((const char*)CW2A(str1.GetBuffer(0)), NULL, 10);
	int y = strtol((const char*)CW2A(str2.GetBuffer(0)), NULL, 10);
	Call_xunlu(x, y);
}


void CM::OnBnClickedButton3()
{
	R_遍历背包 a;
	a.遍历背包();// 遍历背包
	CString str;
	str.Format(L"a数量 %d", a.d数量);
	AfxMessageBox(str);

	for (int i = 0; i < a.d数量; i++)
	{
		Call_输出调试信息("tl怀旧   背包信息 dwObject -------------%X----------------\r\n", a.列表[i].dwObject);
		Call_输出调试信息("tl怀旧   背包信息 名字:%s\r\n", a.列表[i].pName.c_str());
		Call_输出调试信息("tl怀旧   背包信息 使用等级:%d\r\n", a.列表[i].p使用等级);
		Call_输出调试信息("tl怀旧   背包信息 简介:%s\r\n", a.列表[i].简介.c_str());
		Call_输出调试信息("tl怀旧   背包信息 数量:%d\r\n", a.列表[i].p数量);


	}

}


void CM::OnBnClickedButton4()
{
	// TODO: 在此添加控件通知处理程序代码
	R_周围遍历 a;

	a.遍历最近怪物();// 让怪物重新排列
	for (int i = 0; i < a.d数量; i++)
	{
		Call_输出调试信息("人物信息:---------------------%s-----%x---------------------", a.列表[i].pName, a.列表[i].dwObject);
		Call_输出调试信息("人物信息:人物id:%x", a.列表[i].id);
		Call_输出调试信息("人物信息:人物X:%f 人物Y:%f", a.列表[i].fX, a.列表[i].fY);
		Call_输出调试信息("人物信息:人物类型:%x  人物距离:%f", a.列表[i].PType, a.列表[i].距离);


	}
}

结构.cpp文件的修改:新加 遍历最近怪物函数、周围二叉数函数、AsmGetMonsterData函数

#include "pch.h"
#include "结构.h"

DWORD R_rwjz = *(DWORD*)((DWORD)GetModuleHandleA("Game.exe") + 0x5A6A8C);//人物基地址
void R_人物属性::初始化()
{
	__try {
		/**
			GetModuleHandleA("Game.exe")返回Game.exe模块的模块基址
		*/
		DWORD 状态基址 = (DWORD)GetModuleHandleA("Game.exe") + 0x59EF6C;
		/**
			(DWORD*)状态基址 的意思是把 状态基址 的值当成内存地址
			*(DWORD*)状态基址 意思把内存地址里的值取出来
		*/
		DWORD 状态偏移 = *(DWORD*)状态基址 + 0x60;
		DWORD 状态偏移1 = *(DWORD*)状态偏移 + 0x14C;
		状态 = *(DWORD*)状态偏移1;

		DWORD RW偏移 = ReadDword(R_rwjz + 0xc);
		M_通用包 = ReadWord(RW偏移 + 0x2c);
		M_走路包 = ReadWord(RW偏移 + 0x30);
		状态 = ReadDword(状态偏移1);
		fX = ReadFloat(RW偏移 + 0x3C);
		fY = ReadFloat(RW偏移 + 0x44);
		pName = "";
		pName = ReadStr((char*)(ReadDword(ReadDword(RW偏移 + 0x148) + 0x10) + 0x2c));
		血量 = ReadFloat(ReadDword(ReadDword(RW偏移 + 0x148) + 0x10) + 0x8);

	}
	__except (1) {
		Call_输出调试信息("读物人物信息异常\r\n");
	}
}

void R_遍历背包::遍历背包()
{
	try {
		DWORD s = 0;
		//DWORD JZ = (DWORD)GetModuleHandleA("Game.exe") + 0x59F458;
		DWORD JZ = (DWORD)GetModuleHandleA("Game.exe") + 0x59F490;
		DWORD JZpy = ReadDword(ReadDword(JZ) + 0x181C4);
		背包[0].背包数量 = ReadDword(ReadDword(JZ) + 0x181C4 + 0x14); // 道具背包数量
		背包[1].背包数量 = ReadDword(ReadDword(JZ) + 0x181C4 + 0x15); // 材料背包数量
		背包[2].背包数量 = ReadDword(ReadDword(JZ) + 0x181C4 + 0x16);// 任务背包数量
		for (int i = 0; i < 3; i++)
		{
			CString str;
			str.Format(L"数量2:%d %d %d", 背包[0].背包数量, 背包[1].背包数量, 背包[2].背包数量);
			//AfxMessageBox(str);
			for (int  j = 0; j < 背包[i].背包数量; j++)
			{
				if (i == 0) {
					s = j;
				}
				if (i == 1)
					s = j + 背包[0].背包数量;
				if (i == 2)
					s = j + 背包[0].背包数量 + 背包[1].背包数量;
				列表[s].dwObject = ReadDword(JZpy + j * 4);
				if (列表[s].dwObject != 0) {
					DWORD 值 = ReadDword(列表[s].dwObject + 0x2C);
					列表[s].pName = ReadStr((char*)ReadDword(值 + 0x18));
					列表[s].p使用等级 = ReadDword(值 + 0x20);
					列表[s].简介 = ReadStr((char*)ReadDword(值 + 0x1c));;
					列表[s].p数量 = (BYTE)ReadByte(ReadDword(列表[s].dwObject + 0x14) + 0x58);
				}
				else
				{

					列表[s].pName = "";
					列表[s].p数量 = 0;
					列表[s].简介 = "";
					列表[s].p使用等级 = 0;
				}

				d数量++;

			}
			JZpy = JZpy + 0x80;// 扩展背包大小,写死0x80,扩展背包大小0x80/0x47=十进制的32
		}
	}
	catch (...)
	{
		Call_输出调试信息("tlhj   返回背包信息异常\r\n");
	}

}

void R_周围遍历::周围二叉数(DWORD Tree, R_人物属性 rw)
{
	try
	{

		int ss = 0;
		ss++;
		if (ss < 5000)
		{


			BYTE data = -1;
			data = ReadByte(Tree + 0xd);
			DWORD 左子树 = ReadDword(Tree + 0x0);
			DWORD 右子树 = ReadDword(Tree + 0x8);
			if (data == 0)
			{
				列表[d数量].dwObject = ReadDword(Tree + 0x14);
				列表[d数量].id = ReadDword(列表[d数量].dwObject + 0x2c);
				列表[d数量].fX = ReadFloat(列表[d数量].dwObject + 0x3c);//坐标X
				列表[d数量].fY = ReadFloat(列表[d数量].dwObject + 0x44);
				列表[d数量].PType = ReadByte(ReadDword(列表[d数量].dwObject + 0x24) + 0x14);

				if ((int)列表[d数量].fX == 100 && (int)列表[d数量].fY == 100)
				{

				}
				else
				{
                    // 计算最近距离的公式,所有游戏通用,看不懂死记住就行,小学的公式
					列表[d数量].距离 = sqrt((rw.fX - 列表[d数量].fX)*(rw.fX - 列表[d数量].fX) + (rw.fY - 列表[d数量].fY)*(rw.fY - 列表[d数量].fY));
					列表[d数量].pName = "";
					//Call_输出调试信息("-----r2-------%x", AA);
					列表[d数量].pName = ReadStr((char *)(ReadDword(ReadDword(列表[d数量].dwObject + 0x148) + 0x10) + 0x2c));

					列表[d数量].血量 = ReadFloat(ReadDword(ReadDword(列表[d数量].dwObject + 0x148) + 0x10) + 0x8);
					d数量++;
				}

				周围二叉数(左子树, rw);
				周围二叉数(右子树, rw);

			}
		}

	}
	catch (...)
	{

		Call_输出调试信息("tlhj   遍历二叉数异常\r\n");
	}
}
DWORD R_blzw = *(DWORD*)((DWORD)GetModuleHandleA("Game.exe") + 0x59EFA4);
void R_周围遍历::AsmGetMonsterData()
{
	Sleep(10);
	try
	{
		
		DWORD dwTreeBase = *(DWORD*)(R_blzw + 0x54);
		dwTreeBase = *(DWORD*)(dwTreeBase + 0x4);
		DWORD dwEnvTreeBase = dwTreeBase;
		Call_输出调试信息("-----q-------");
		if (dwEnvTreeBase) {
			R_人物属性 rw;
			rw.初始化();
			d数量 = 0;
			周围二叉数(dwEnvTreeBase, rw);//遍历二叉树


		}//正确取到二叉树基址

	}
	catch (...)
	{
		Call_输出调试信息("tlhj   得到二叉数根结点异常\r\n");
	}
}

R_人物属性 R_周围遍历::遍历最近怪物()
{
	this->AsmGetMonsterData();
	R_人物属性 a;
	R_人物属性 人物;
	人物.初始化();
	try
	{
		if (this->d数量 > 0)
		{
			for (int i = 0; i < (int)this->d数量; i++)
			{

				for (int j = i + 1; j < (int)this->d数量; j++)
				{
					if (this->列表[i].距离 > this->列表[j].距离)
					{
						a = this->列表[j];
						this->列表[j] = this->列表[i];
						this->列表[i] = a;
					}
				}




			}

			if (d数量 >= 1)
			{
				return this->列表[d数量 - 2];
			}
			else
			{
				return 人物;
			}


		}

		return 人物;

	}
	catch (...)
	{
		Call_输出调试信息("tlhj   返回最近怪物信息异常\r\n");
	}
}

结构.h文件的修改:新加 R_周围遍历结构体,修改了 R_人物属性结构体

#pragma once
#include <string>

#define  BLZW (DWORD)GetModuleHandleA("Game.exe") + 0x59EFA4//遍历周围 和人物状态同一个
using namespace std;
struct R_人物属性
{
	DWORD 状态;
	FLOAT 血量;
	DWORD 最大血量;
	FLOAT fX;
	FLOAT fY;
	DWORD id;
	DWORD dwObject;
	DWORD PType;
	DWORD M_走路包;
	FLOAT 距离;
	DWORD M_通用包;
	PCHAR pName = "";

	void 初始化();
};

struct R_背包属性 {
	DWORD dwObject;
	string pName;
	BYTE p数量;
	string 简介;
	DWORD p使用等级;
};

struct R_背包类
{
	BYTE 背包数量 = 0;
};

struct R_遍历背包 {
	R_背包类 背包[0x3];
	R_背包属性 列表[0x100];
	DWORD d数量 = 0;
	void 遍历背包();
};

struct 坐标 {
	FLOAT x;
	FLOAT y;
};

struct R_周围遍历
{

	R_人物属性 列表[0x100];
	DWORD d数量 = 0;
	void 周围二叉数(DWORD Tree, R_人物属性 dw);
	void AsmGetMonsterData();
	R_人物属性 遍历最近怪物();

};

上方的代码不全,只有手写的代码

完整代码:

链接:https://pan.baidu.com/s/1W-JpUcGOWbSJmMdmtMzYZg?pwd=q9n5

提取码:q9n5

复制这段内容后打开百度网盘手机App,操作更方便哦


img

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2218076.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

衡石分析平台系统分析人员手册-应用查看

应用查看​ 应用创作界面展示了用户可以查看的所有应用。 用户可以使用平铺视图或列表视图查看应用。同时支持通过搜索、过滤、排序等方式快速查找应用。 应用视图​ 应用创作支持平铺视图和列表视图两种展示方式&#xff0c;默认以平铺视图的方式展示应用&#xff0c;用户可…

2024 蚂蚁SEO蜘蛛池对网站收录的帮助

《2024 蜘蛛池对网站收录还有效果吗&#xff1f;》 在网站优化的领域中&#xff0c;蜘蛛池曾经是一个备受关注的工具。然而&#xff0c;随着搜索引擎算法的不断演进&#xff0c;人们对于 2024 年蜘蛛池对网站收录是否还有效果产生了疑问。 一、什么是蜘蛛池&#xff1f; 蜘蛛池…

APQP在制造行业的应用:搭上数字化项目管理平台很nice

APQP&#xff08;Advanced Product Quality Planning&#xff0c;即产品质量先期策划&#xff09;最早由汽车行业引入&#xff0c;并因其在质量管理方面的显著效果而逐渐被其他制造业领域所采纳。 APQP提供了一种从产品设计的最初阶段到生产过程的全面质量管理框架&#xff0c;…

使用fpm工具制作Vim.rpm包

背景&#xff1a;生产环境中的CentOS 7在安全扫描中被扫描出vim存在堆缓冲区溢出&#xff08;CVE-2024-45306&#xff09;等漏洞。根据漏洞说明&#xff0c;需要升级到最新版。 奈何CentOS 7已经停止维护了&#xff0c;所以&#xff0c;想在网上找一个最新版的vim.rpm相当不容易…

数字图像处理:图像复原应用

数字图像处理&#xff1a;图像复原应用 1.1 什么是图像复原&#xff1f; 图像复原是图像处理中的一个重要领域&#xff0c;旨在从退化&#xff08;例如噪声、模糊等&#xff09;图像中恢复出尽可能接近原始图像的结果。图像复原与图像增强不同&#xff0c;复原更多地依赖于图…

ES6 Promise的用法

学习链接&#xff1a;ES6 Promise的用法&#xff0c;ES7 async/await异步处理同步化&#xff0c;异步处理进化史_哔哩哔哩_bilibili 一、同步与异步区别 1.JavaScript代码是单线程的程序&#xff0c;即通过一行一行代码顺序执行&#xff0c;即同步概念。 2.若处理一些简短、…

数据结构部分混淆

1.随机存储和顺序存储&#xff1a; 随机存取&#xff1a;数组&#xff0c;当存储器中的数据被读取或写入时&#xff0c;所需要的时间与该数据所在的物理地址无关 顺序存取&#xff1a;链表&#xff0c;当存储器中的数据被读取或写入时&#xff0c;所需要的时间与该数据所在的物…

力扣之1412.查找成绩处于中游的学生

题目&#xff1a; sql建表语句&#xff1a; Create table If Not Exists Student (student_id int, student_name varchar(30)); Create table If Not Exists Exam (exam_id int, student_id int, score int); Truncate table Student; insert into Student (student_id,…

linux环境下的程序设计与git操作

目录 前言&#xff1a; 进度条小程序&#xff1a; 先介绍几个背景知识 代码实现 Git操作 总结 其他指令 前言&#xff1a; 本文将重点介绍1. linux下的程序设计&#xff0c;并使用linux下的几个函数接口。实现一个简单的小程序 2.本着开源精神&#xff0c;进行git操作。…

OPENSSL-2023/11/10学习记录-C/C++对称分组加密DES

对称分组加密常用算法&#xff1a; DES 3DES AES 国密SM4 对称分组加密应用场景&#xff1a; 文件或者视频加密 加密比特币私钥 消息或者配置项加密 SSL通信加密 对称分组加密 使用异或实现一个简易的对称加密算法 A明文 B秘钥 AB密文AB (AB)B A 密码补全和初始化 数…

在Access中执行SQL语句

1.新建一个查询 2. 关闭弹出的窗口&#xff0c;创建一个空查询 3. 在“设计”页中点击“数据定义”进入输入SQL语句界面 4. “执行”后就能看到执行结果 5.如果想再次执行SQL&#xff0c;则可在MDI窗口标题栏右键&#xff0c;在弹出的菜单中选择“SQL 视图”即可

Vue向上滚动加载数据时防止内容闪动

目前的需求&#xff1a;当前组件向上滚动加载数据&#xff0c;dom加载完后&#xff0c;页面的元素位置不能发生变化 遇到的问题&#xff1a;加载完数据后&#xff0c;又把滚轮滚到之前记录的位置时&#xff0c;内容发生闪动 现在的方案&#xff1a; 加载数据之前记录整体滚动条…

Spring AI 整体介绍_关键组件快速入门_prompt_embedding等

Spring AI&#xff1a;Java开发者的AI集成新利器 在过去&#xff0c;Java开发者在构建AI应用时面临着缺乏统一框架的问题&#xff0c;导致不同AI服务的集成过程复杂且耗时。Spring AI应运而生&#xff0c;旨在为基于Java的应用程序提供一个标准化、高效且易于使用的AI开发平台…

51单片机的超声波视力保护仪【proteus仿真+程序+报告+原理图+演示视频】

1、主要功能 该系统由AT89C51/STC89C52单片机LCD1602显示模块温度传感器光照传感器超声波传感器按键、LED、蜂鸣器等模块构成。适用于视力保护仪、坐姿矫正器、超声波防近视等相似项目。 可实现功能: 1、LCD1602显示温度、光照、距离和学习时间 2、超声波传感器采集头部与探…

如何做好SQL 数据库安全

随着信息技术的迅猛发展&#xff0c;数据库在现代信息系统中的重要性日益凸显。无论是电子商务平台、金融系统还是社交媒体应用&#xff0c;数据库都是其核心组件之一。其中&#xff0c;SQL&#xff08;Structured Query Language&#xff0c;结构化查询语言&#xff09;数据库…

效率飞跃 | SOLIDWORKS 2025:草图与装配体设计再升级!

一年一度的SOLIDWORKS新版本已经发布&#xff0c;快来看看SOLIDWORKS 2025这些让您眼前一亮的新功能&#xff0c;它们将为您的研发过程增添强大助力。 一、草图绘制 1、通过 XYZ 点生成的曲线可以选取坐标系统 新版本可选择曲线起点位置 2、转换实体作为构造几何体 转换实体…

Science Robotics 通过新材料打造FiBa软机器人 可实现四种形态进化

近几年由于材料科学的进步&#xff0c;软机器人相关技术近几年研究成果显著&#xff0c;与传统的刚性机器人相比&#xff0c;软机器人的设计灵感来源于自然界中的生物系统&#xff0c;如蠕虫、章鱼、壁虎和青蛙等。这些生物利用柔软、有弹性的材料&#xff0c;在复杂环境中展现…

微服务基础架构(图)

微服务基础架构是一种现代化的软件架构模式&#xff0c;旨在将大型复杂的应用程序拆分为多个小型、独立的服务。每个微服务专注于特定的业务功能&#xff0c;可独立开发、部署和扩展。 在微服务基础架构中&#xff0c;通常会使用轻量级的通信机制&#xff0c;如 RESTful API 或…

C++简易日志系统:打造高效、线程安全的日志记录工具

目录 引言&#xff1a; 1.日志的基本概念 1.1.什么是日志&#xff1f; 1.2.我们为什么需要日志&#xff1f; 2.自己实现一个简易日志 2.1.日志的等级 2.2日志的格式 2.3.获取时间的方法 2.4.日志的主体实现 参数&#xff1a; 代码解析&#xff1a; 问题&#xff1a…

C++新手入门指南:从基础概念到实践之路

C 继承了 C 语言的高效性和灵活性&#xff0c;同时新增了面向对象编程的特点。这使得 C 既可以进行底层系统编程&#xff0c;又能进行面向对象的软件设计。在面向对象编程方面&#xff0c;C 支持封装、继承和多态三大特性。 &#x1f4af;C 初印象 语言的发展就像是练功打怪…