首页
实用工具
我的旅程
在线壁纸
更多
✒️ 问题反馈
📦 文章统计
🌍 国内镜像
🎬 次元视界
📒 流水账本
🎨 在线 PS
推荐
🕵️ 开源情报
🌆 图片压缩
🍭 资产清洗
💡 我的作品
👤 关于站长
⚔️ 次 元 剑
搜索
1
【工具分享】逆向工具箱 - 次元剑
89,537 阅读
2
【技术分享】PE文件结构分析 ( RVA转FOA )
7,350 阅读
3
【技术分享】NASM x86 Assembly Language
5,982 阅读
4
【技术分享】CK竞技之王游戏辅助制作
3,725 阅读
5
【每日随记】天涯明月刀无限飞修改思路
2,816 阅读
技术分享
CTF解题
英语笔记
数学笔记
网络通信
每日随记
攻防技术
工具分享
Search
标签搜索
Windows
Web安全
Python3
Linux
逆向工程
CTF
红队技术
人工智能
C/C++
黑客工具
Go
密码学
二进制安全
数学
漏洞挖掘
Android
eNSP
渗透测试
蓝队技术
黑客大会
发光的神
累计撰写
160
篇文章
累计收到
103
条评论
首页
栏目
技术分享
CTF解题
英语笔记
数学笔记
网络通信
每日随记
攻防技术
工具分享
页面
实用工具
我的旅程
在线壁纸
✒️ 问题反馈
📦 文章统计
🌍 国内镜像
🎬 次元视界
📒 流水账本
🎨 在线 PS
推荐
🕵️ 开源情报
🌆 图片压缩
🍭 资产清洗
💡 我的作品
👤 关于站长
⚔️ 次 元 剑
搜索到
8
篇与
的结果
2024-11-20
【技术分享】CK竞技之王游戏辅助制作
简介以下教程是自我总结出的,我会很详细的进行编写,配详细的步骤和视频教程,本文章用到的工具会有 Cheat engine、x64dbg,本文章只用C语言编写代码,用单机游戏 CK竞技之王 进行辅助的制作,该游戏是CS进行魔改仿穿越火线,在此声明并不是穿越火线游戏,做这个教程目的只有一个让你们明白它的原理和搜法,在游戏中不管是网络游戏还是单机游戏,他们的共同点都是一样的,能够被本地修改达到开挂的目的(在此声明只是作为学习交流)。基本了解游戏它是程序,因为运行程序就会创建进程,通过进程就可以访问程序中的内存地址,通过地址可以访问到存储的数据,举个简单的例子:比如游戏开发者创建了个变量 float speed = 2.5; 来定义人物速度,那么就会在内存中开辟一个空间,然后会有一个内存地址像 0x12345 这样的(只是打比方),我们可以通过这个地址来访问到里面的数据(知道C语言指针的就很好理解),也就是 2.5,通过微软给的系统 API 接口 kernel32.dll 里的 ReadProcessMemory 函数就能办到,然后就是写内存数值了,可以通过 WriteProcessMemory 函数,要用到的API函数我都会写在下面的。取窗口句柄HWND FindWindow( LPCTSTR lpClassName, LPCTSTR lpWindowName );取窗口句柄因为 ReadProcessMemory,第一个参数 hProcess 要的是进程句柄(注意了 FindWindow 取的是窗口句柄而不是进程句柄哦)。取进程线程DWORD GetWindowThreadProcessId( HWND hWnd, LPDWORD lpdwProcessId );通过 GetWindowThreadProcessId,来获取进程 PID 当然你直接获取进程 PID 就省去了找窗口。取进程句柄HANDLE OpenProcess( DWORD dwDesiredAccess, BOOL bInheritHandle, DWORD dwProcessId );通过 OpenProcess,打开进程来取进程句柄,这样就拿到进程句柄了。读内存数据BOOL ReadProcessMemory( HANDLE hProcess, LPCVOID lpBaseAddress, LPVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesRead );通过 ReadProcessMemory 来读进程内存数据。写内存数据BOOL WriteProcessMemory( HANDLE hProcess, LPVOID lpBaseAddress, LPCVOID lpBuffer, SIZE_T nSize, SIZE_T *lpNumberOfBytesWritten );通过 WriteProcessMemory 来写进程内存数据,以上是简单介绍让大家有个基本了解(注意以上只能对 32 程序进程操作)。32位、64位内存读写内核级进程内存读NTSTATUS ZwWow64ReadVirtualMemory64( HANDLE Process, PVOID64 BaseAddress, PVOID Buffer, ULONG64 BufferSize, PULONG64 NumberOfBytesRead );内核级进程内存写NTSTATUS ZwWow64WriteVirtualMemory64( HANDLE Process, PVOID64 BaseAddress, PVOID Buffer, ULONG64 BufferSize, PULONG64 NumberOfBytesWritten );内核级别的进程内存读写,可以用在32和64位程序上,微软不公开的但是可以用,这两个函数不属于动态链接库,感兴趣的可以自己去研究。完整代码(读写子弹数量)// 读写内存简单例子: #include <Windows.h> // 包含头文件 Windows.h #include <stdio.h> int main() { DWORD processid; // 保存进程PID HANDLE hProcess; // 保存进程句柄 HWND hwnd = FindWindow("Valve001", NULL); // 取窗口句柄 GetWindowThreadProcessId(hwnd, &processid); // 取进程PID hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid); // 取进程句柄 if (hProcess == NULL) { printf("打开进程失败\n"); return 1; } int values; // 返回读取数据 SIZE_T bytesRead; // 存读取字节数 LPVOID Addresses = (LPVOID)0x7000FA4; // 临时地址读取 ReadProcessMemory(hProcess, Addresses, &values, sizeof(int), &bytesRead); // 读取内存地址数据 printf("子弹数量:%d\n", values); values = 100; SIZE_T bytesWritten; WriteProcessMemory(hProcess, Addresses, &values, sizeof(int), &bytesWritten); // 写内存地址数据 100 CloseHandle(hProcess); // 关闭句柄 return 0; }以上函数都在这个目录下:C:\Windows\System32\,这里面是存放的是 32 位动态链接库,上面的函数是 kernel32.dll 里面的,常用的 dll 我写在下面了,感兴趣的可以自己去看看。user32.dll // 提供了用户界面方面的功能,包括窗口、菜单、鼠标、键盘等。 kernel32.dll // 包含了许多核心的操作系统功能,如进程和线程管理、内存管理、文件操作等。 advapi32.dll // 提供了高级的系统和安全功能,如访问控制、认证等。 gdi32.dll // 提供了图形设备接口相关的功能,包括绘图和字体操作。 shell32.dll // 提供了与 Windows shell 相关的功能,如文件和文件夹操作、图标处理等。 comdlg32.dll // 提供了用于打开和保存对话框等通用的常见对话框。人物自瞄(C语言)#include <stdio.h> #include <math.h> #include <windows.h> #define PI 3.1415926 float GetDistance(float x1, float y1, float x2, float y2) { float distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); // 计算二维距离 return distance / 20.0f; } int main() { DWORD processid; HANDLE hProcess; HWND hwnd = FindWindow("Valve001", NULL); GetWindowThreadProcessId(hwnd, &processid); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid); if (hProcess == NULL) { printf("打开进程失败\n"); return 1; } LPVOID myAddr[] = { // 临时地址 (LPVOID)0x0B466A44, // 自己x (LPVOID)0x0B466A4C, // 自己y (LPVOID)0x0B466A48, // 自己z }; LPVOID emAddr[] = { // 临时地址 (LPVOID)0x0B466D68, // 敌人x (LPVOID)0x0B466D70, // 敌人y (LPVOID)0x0B466D6C, // 敌人z }; LPVOID aimAddr[] = { // 临时地址 (LPVOID)0x02de10c8, // 准星 x (LPVOID)0x02de10c4, // 准星 y }; SIZE_T bytesRead; float Aimvalues[2]; float myvalues[3]; float envalues[3]; float differ_x, differ_y, distan; float AiMBot_x, AiMBot_y, differ; while (TRUE) { for (int i = 0; i < 3; i++) { ReadProcessMemory(hProcess, aimAddr[i], &Aimvalues[i], sizeof(float), &bytesRead); ReadProcessMemory(hProcess, myAddr[i], &myvalues[i], sizeof(float), &bytesRead); ReadProcessMemory(hProcess, emAddr[i], &envalues[i], sizeof(float), &bytesRead); } differ_x = envalues[0] - myvalues[0]; // 计算敌我坐标差 differ_y = envalues[2] - myvalues[2]; distan = sqrt(pow(differ_x, 2) + pow(differ_y, 2)); differ = GetDistance(myvalues[0], myvalues[2], envalues[0], envalues[2]); // 计算二维距离 AiMBot_y = atan((myvalues[1] - envalues[1]) / distan) / PI * 180 + 3; // 计算y轴方向上的角度 if (differ_x > 0 && differ_y > 0) // 判断第一象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI; // 计算x轴方向上的角度 } if (differ_x < 0 && differ_y > 0) // 判断第二象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI + 180; } if (differ_x < 0 && differ_y < 0) // 判断第三象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI + 180; } if (differ_x > 0 && differ_y < 0) // 判断第四象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI; } WriteProcessMemory(hProcess, aimAddr[0], &AiMBot_x, sizeof(float), &bytesRead); // 将计算出的敌人位置x写入准星x WriteProcessMemory(hProcess, aimAddr[1], &AiMBot_y, sizeof(float), &bytesRead); // 将计算出的敌人位置y写入准星y } CloseHandle(hProcess); return 0; }{dplayer src="https://www.52tt.pro/usr/uploads/2023/08/Aim.mp4"/}自瞄代码详解1.首先通过 Cheat Engine 获取人物在游戏中的 x,y,z 坐标地址,有了这些数据才能为后期做准备。Tips:使用CE搜未知初始值浮点数,人物坐标是浮点数,人物往左移动 也就是 x 轴 那么就是减少了,往右走就是增加了,数轴的基本常识左减右增,来来回回过滤用二分排除法很快就能找到x坐标了。Tips:一旦找到 x 坐标那么 y,z 就都找到了,x坐标的内存地址是 0x2458fa44 只要 0x2458fa44 + 4 就是 z 坐标 + 8 就是 y 坐标,这样就全部拿到了,可以看到我额外标出HP,因为这里是跟人物相关的数据一般都在附近,就像一个对象或者结构体里面的数据都会存放在一起的。2.计算 x,y 轴方向的差值(水平与垂直距离),假设自己的位置是 (0, 0),敌人的位置是 (3, 5),那么 x 轴方向的差值是 3,y 轴方向的差值是 5。√(3^2 + 5^2) = √34 ≈ 5.83 // 勾股定理水平距离:3(敌人的 x 坐标)- 0(自己 x 坐标) = 3垂直距离:5(敌人的 y 坐标)- 0(自己 y 坐标) = 5float differ_x = enemy_x - self_x float differ_y = enemy_y - self_y3.计算两点之间的直线距离,可以使用勾股定理计算,本教程中我使用了欧氏距离计算。d = √((x2 - x1)^2 + (y2 - y1)^2) // 二维欧氏距离// 计算二维欧氏距离 float GetDistance(float x1, float y1, float x2, float y2) { float distance = sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); return distance / 20.0f; }4.计算自己与敌人之间的角度(相对于 x 轴)用反正切函数来计算角度。θ = arctan(5 / 3)AiMBot_y = atan((myvalues[1] - envalues[1]) / distan) / PI * 180 + 3; // 计算y轴方向上的角度5.使用 atan 函数计算 y 轴方向上的角度,作为左后自瞄的 y 角度。if (differ_x > 0 && differ_y > 0) // 判断第一象限 { AiMBot_x = atan(differ_y / differ_x) * 180 / PI; // 计算 x 轴方向上的角度 } // 代码省略AiMBot_x = atan(differ_y / differ_x) * 180 / PI; // 计算x轴方向上的角度6.通过差值正负判断象限,用 atan 函数计算 x 轴方向上的角度,作为左后自瞄的 x 角度。WriteProcessMemory(hProcess, aimAddr[0], &AiMBot_x, sizeof(float), &bytesRead); // 准星x写入计算出的敌人位置x WriteProcessMemory(hProcess, aimAddr[1], &AiMBot_y, sizeof(float), &bytesRead); // 准星y写入计算出的敌人位置y7.获取游戏准星 x,y 移动坐标,最终配合 x 角度与 y 角度,用来移动准星实现自瞄效果。(以上方式是最原始的自瞄,用三角函数进行实现,只是单个人自瞄哦,多人只要遍历敌人数据x,y,z,加入到数组中通过距离判断优先级再自瞄,这样就完成了暂时不做比较懒。)无限急速雷思路很简单,先找雷数量右键看谁访问了这个地址,就可以追踪到访问它的代码段,这里面就会有很多对手雷的功能操作。(比如投掷距离,投掷速度,引爆时间。)看到这个地方,有 #fire_in_the_hole 这里就是调用扔雷的声音,再往下就是扔雷的操作。我分析过了,扔雷的速度就在这个反汇编代码,引用的这个地址附近,通过访问内存就能看到 + 4 的位置是 1.75 修改它扔雷速度就无冷却。{dplayer src="https://www.52tt.pro/usr/uploads/2023/08/bom.mp4"/}方框透视(C语言)绘制方框透视的方式也就两种,内部绘制 和 外部绘制,内部就是嵌入到了游戏窗口内,本文章用的是外部绘制,也就是说创建了一个窗口,设置透明并覆盖在游戏窗口上方,读游戏中的数据计算出来再进行绘制。我这里用的是 Windows API 进行绘制,会存在一点小问题,只是提供个思路给你们,在C语言也可以用 D3D9、SDL、Imgui 等等。#include <Windows.h> #include <tlhelp32.h> // 根据进程ID和模块名获取模块句柄 HMODULE GetModuleHandleFromProcess(DWORD processId, const char *moduleName) { HANDLE hSnapshot = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processId); MODULEENTRY32 moduleEntry; moduleEntry.dwSize = sizeof(moduleEntry); if (Module32First(hSnapshot, &moduleEntry)) { do { if (stricmp(moduleEntry.szModule, moduleName) == 0) { CloseHandle(hSnapshot); return moduleEntry.hModule; } } while (Module32Next(hSnapshot, &moduleEntry)); } CloseHandle(hSnapshot); return NULL; } // 窗口消息处理函数 LRESULT CALLBACK MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case WM_DESTROY: PostQuitMessage(0); return 0; case WM_SIZING: return TRUE; case WM_NCHITTEST: return HTTRANSPARENT; default: return DefWindowProc(hWnd, msg, wParam, lParam); } } // 绘制矩形 void DrawRectangle(HDC hdc, int x, int y, int width, int height) { RECT rect = {x, y, x + width, y + height}; HBRUSH hBrush = (HBRUSH)GetStockObject(NULL_BRUSH); HPEN hPen = CreatePen(PS_SOLID, 2, RGB(255, 0, 0)); HGDIOBJ hOldPen = SelectObject(hdc, hPen); HGDIOBJ hOldBrush = SelectObject(hdc, hBrush); Rectangle(hdc, x, y, x + width, y + height); SelectObject(hdc, hOldBrush); SelectObject(hdc, hOldPen); DeleteObject(hPen); } int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) { // 获取进程ID和句柄 DWORD processid; HANDLE hProcess; HWND Gamehwnd = FindWindow(NULL, "Compete King"); GetWindowThreadProcessId(Gamehwnd, &processid); hProcess = OpenProcess(PROCESS_ALL_ACCESS, FALSE, processid); // 获取模块句柄 const char *moduleName = "amxmodx_mm.dll"; HMODULE hModule = GetModuleHandleFromProcess(processid, moduleName); // 注册窗口类 WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L, GetModuleHandle(NULL), NULL, NULL, NULL, NULL, TEXT("D3D9"), NULL}; RegisterClassEx(&wc); // 创建窗口 RECT rects; GetWindowRect(Gamehwnd, &rects); int left = rects.left; int top = rects.top; int right = rects.right; int bottom = rects.bottom; int sWidth = right - left; int sHeight = bottom - top; HWND hWnd = CreateWindowEx( WS_EX_LAYERED | WS_EX_TRANSPARENT, wc.lpszClassName, TEXT("D3D9"), WS_POPUP, left, top, sWidth - 6, sHeight - 3, NULL, NULL, wc.hInstance, NULL); SetLayeredWindowAttributes(hWnd, RGB(0, 0, 0), 0, LWA_COLORKEY); ShowWindow(hWnd, SW_SHOWDEFAULT); UpdateWindow(hWnd); MSG msg; float MatData; float Matarray[4][4]; HDC hdc = GetDC(hWnd); ZeroMemory(&msg, sizeof(msg)); while (msg.message != WM_QUIT) { if (PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE)) { TranslateMessage(&msg); DispatchMessage(&msg); } else { // 获取游戏窗口大小 if (!GetWindowRect(Gamehwnd, &rects)) { PostQuitMessage(0); break; } int left = rects.left; int top = rects.top; int right = rects.right; int bottom = rects.bottom; int sWidth = right - left; int sHeight = bottom - top; SetWindowPos(hWnd, HWND_TOPMOST, left + 3, top, sWidth, sHeight, SWP_NOSIZE); int initNumber = -4; for (int i = 0; i < 4; i++) // 遍历存储 4 x 4 矩阵 { for (int j = 0; j < 4; j++) { initNumber += 4; LPVOID Addresses = (LPVOID)0x2C20100 + initNumber; ReadProcessMemory(hProcess, Addresses, &MatData, sizeof(float), NULL); Matarray[i][j] = MatData; } } int GameWinWidth = 1030 / 2; int GameWinHeight = 797 / 2; float vorx, vory, vorz, vory2, FanHeight, FanWidth, row; long long offset = 0xFFFFFFFFFFFFFCDC; RECT clientRect; GetClientRect(hWnd, &clientRect); FillRect(hdc, &clientRect, (HBRUSH)GetStockObject(BLACK_BRUSH)); for (int enemy_num = 0; enemy_num < 2; enemy_num++) // 遍历敌人数组 { offset += 0x324; LPVOID baseAddress = (LPVOID)((DWORD_PTR)hModule + 0x001CFE08); int baseaddr; ReadProcessMemory(hProcess, baseAddress, &baseaddr, sizeof(int), NULL); float enemyHP, enemyX, enemyY, enemyZ; ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x1E0 + offset, &enemyHP, sizeof(float), NULL); // enemyHP ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x88 + offset, &enemyX, sizeof(float), NULL); // enemyX ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x8C + offset, &enemyY, sizeof(float), NULL); // enemyY ReadProcessMemory(hProcess, (LPVOID)baseaddr + 0x90 + offset, &enemyZ, sizeof(float), NULL); // enemyZ /* 三维图形的投影算法 */ vorz = Matarray[0][2] * enemyX + Matarray[1][2] * enemyY + Matarray[2][2] * enemyZ + Matarray[3][2]; row = 1 / vorz; vorx = GameWinWidth + (Matarray[0][0] * enemyX + Matarray[1][0] * enemyY + Matarray[2][0] * enemyZ + Matarray[3][0]) * row * GameWinWidth; vory = GameWinHeight - (Matarray[0][1] * enemyX + Matarray[1][1] * enemyY + Matarray[2][1] * (enemyZ - 50) + Matarray[3][1]) * row * GameWinHeight; vory2 = GameWinHeight - (Matarray[0][1] * enemyX + Matarray[1][1] * enemyY + Matarray[2][1] * (enemyZ + 30) + Matarray[3][1]) * row * GameWinHeight; if (vorz < 0) { continue; } FanHeight = vory - vory2; FanWidth = FanHeight * 0.6; DrawRectangle(hdc, vorx - FanWidth / 2, vory2, (int)FanWidth, (int)FanHeight); // 绘制方框矩形 } ReleaseDC(hWnd, hdc); } } UnregisterClass(wc.lpszClassName, wc.hInstance); return (int)msg.wParam; }gcc main.c -s -lgdi32 -ld3d9 -mwindows // 编译成 exe 并隐藏 DOS 窗口方框透视详解先理一下思路,要用到的功能有,通过窗口句柄取进程ID,打开进程句柄,读游戏敌人 x y z 内存坐标数据,本文章代码用到的是外部窗口绘制,所以找个可以创建图形窗口能画图的库就行,设置窗口无边框,设置窗口透明,设置鼠标穿透,设置窗口置顶和实时位置覆盖,这样就能绘制了。三维投影变换1.主要用到三维图形的投影算法,把游戏里的数据进行计算后把敌人位置绘制到屏幕上显示,所以要在游戏里找到相机矩阵。在游戏里能影响到相机的方向的只有视角,用 CE 搜浮点数未知初始值,人物不动鼠标转动视角,再次搜变动了数值,以此类推一个一个进去看用内存视图观察特征,当然嫌麻烦可以找矩阵验证工具,可以自己网上找这里就不提供了。常见相机矩阵有 4 x 4,其他的还有 4 x 3 等等,如果矩阵有变化那么三维图像投影变换算法也需要进行更改。 矩阵特征:规律总结1:跳动时,有三个数据在变动规律总结2:晃动鼠标时,有一个数据保持 0 不变规律总结3:存在3对两两相同2.当我在游戏里人物跳起来的时候,可以发现只有三个值不停的发送变化,其他的都没变而且存在3对两两相同数:-0.11 -0.11 -0.99 -0.99 0.04 0.043.当我在游戏里旋转游戏视角的时候,可以发现有一个 0 不变。相机z = 矩阵[0][2] x 敌人x坐标 + 矩阵[1][2] x 敌人y坐标 + 矩阵[2][2] x 敌人z坐标 + 矩阵[3][2] 比例 = 1 / 相机z 相机x = 游戏窗口宽度 + (矩阵[0][0] x 敌人x坐标 + 矩阵[1][0] x 敌人y坐标 + 矩阵[2][0] x 敌人z坐标 + 矩阵[3][0]) x 比例 x 游戏窗口宽度 相机y = 游戏窗口高度 - (矩阵[0][1] x 敌人x坐标 + 矩阵[1][1] x 敌人y坐标 + 矩阵[2][1] x (敌人z - 50) + 矩阵[3][1]) x 比例 x 游戏窗口高度 相机y2 = 游戏窗口高度 - (矩阵[0][1] x 敌人x坐标 + 矩阵[1][1] x 敌人y坐标 + 矩阵[2][1] x (敌人z + 30) + 矩阵[3][1]) x 比例 x 游戏窗口高度投影公式相机 Z 计算:比例因子:屏幕上的 X 坐标:屏幕上的 Y 坐标:这里还有Python3的版本:Python3 透视自瞄外挂制作 ,仅供学习参考。2024-12-20 09:58:10 星期五 更新过
2024年11月20日
3,725 阅读
1 评论
764 点赞
2021-04-13
【技术分享】C语言转换汇编代码
简介我会介绍下在Windows环境下使用gcc生成的汇编代码,gcc环境请自行安装好,汇编有好几种格式最常用的也就是 x86-64 Intel格式 和 x86-64 AT&T 语法格式,以下我都会演示,你一定想问为什么要生成汇编,在我看来的确很有用它会让你明白代码在底层是如何进行运作的,在逆向中能懂汇编代码会有很大的帮助。C源代码#include <stdio.h> #include <Windows.h> int main(){ puts("Hello"); system("pause"); return 0; }生成obj文件gcc -c .\test.cIntel 语法格式objdump -M intel -d .\test.o objdump -M intel -d -S .\test.o1.用objdump工具对.o文件进行生成Intel语法格式汇编代码。AT&T 语法格式objdump -d .\test.o2.用objdump工具对.o文件进行生成AT&T语法格式汇编代码。gcc -c .\test.c && objdump -S .\test.o 1.gcc中的 -c参数只激活预处理 编译和汇编生成.o 的obj文件,这里我用了&&符号这是cmd里的并执行的意思,它会先执行 gcc 然后再执行 objdump 让它生成汇编代码。gcc -c .\test.c && objdump -S .\test.o > 1.txt2.然后将内容指定输出到1.txt文本文档里,桌面就会生成1.txt文本文档打开就可以看到AT&T语法格式的汇编代码了。
2021年04月13日
54 阅读
0 评论
3 点赞
2021-04-12
【技术分享】C++ strcpy strncpy 函数
简介strncpy 与 strcpy 差不多都是字符串复制,strncpy函数就多了一个参数可以指定复制的字符数。dest -- 指向用于存储复制内容的目标数组。src -- 要复制的字符串。n -- 要从源中复制的字符数。char *strncpy(char *dest, const char *src, size_t n)#include <iostream> #include <cstring> using namespace std; int main() { string str = "act"; // 创建字符串变量 str char _dst1[1024]; // 创建char _dst2 char _dst2[1024]; // 创建char _dst2 memset(_dst1, '\0', sizeof(_dst1)); // 使用函数 memset() 来清除内存位置 strcpy(_dst1, str.c_str()); // 直接复制str里的字符串 strncpy(_dst2, str.c_str(), 2); // 复制字符串 指定两个字符 cout << _dst1 << ","<< _dst2; return 0; }输出:act,ac1.这个函数要注意,很有可能会导致内存溢出,我给他的buf是 _dst1[1024],如果要复制的内容超出1024那么就会导致溢出。
2021年04月12日
11 阅读
0 评论
2 点赞
2021-04-11
【技术分享】C++ find rfind 语句
简介在C++里 查找字符串位置,可以用到 find 和 rfind,一个从头开找,另个是从尾找。简单例子#include <iostream> using namespace std; int main() { string str = "Hello, World!"; unsigned long f = str.find("l", 4); // 从头偏移4位 就找到World里的l了 unsigned long r = str.rfind("l", 4);// 从尾偏移4位 就找到Hello里的l了 cout << f << "," << r << endl; return 0; }输出:10,3
2021年04月11日
17 阅读
0 评论
1 点赞
2021-04-10
【技术分享】C++ 调用cmd命令
简介个人感觉:能结合外部系统命令或程序是很关键的,在开发中也会用到很多的库和系统提供的api接口,这样才能使我们的程序功能更加的强大,在C++中能执行CMD命令的函数,有system和popen第一个只会执行命令并不能把返回结果传递到变量上,而第二个正好是可以。system函数#include <iostream> int main() { system("ping 127.0.0.1"); return 0; }输出: 正在 Ping 127.0.0.1 具有 32 字节的数据: 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 127.0.0.1 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 0ms,最长 = 0ms,平均 = 0ms1.system只能执行,返回的内容不能保存在变量里。popen函数#include <iostream> #include <stdlib.h> #include <stdlib.h> using namespace std; string cmd(const char *cmd, const int buffer) { char _str[buffer]; string cmd_str; FILE *f = popen("ping 127.0.0.1", "r"); while (fgets(_str, buffer, f) != NULL) { cmd_str += _str; } return cmd_str; } int main() { cout << cmd("dir", 1024); return 0; }输出: 正在 Ping 127.0.0.1 具有 32 字节的数据: 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 来自 127.0.0.1 的回复: 字节=32 时间<1ms TTL=128 127.0.0.1 的 Ping 统计信息: 数据包: 已发送 = 4,已接收 = 4,丢失 = 0 (0% 丢失), 往返行程的估计时间(以毫秒为单位): 最短 = 0ms,最长 = 0ms,平均 = 0ms1.popen返回的内容可以保存到变量里。
2021年04月10日
30 阅读
0 评论
1 点赞
2021-04-09
【技术分享】C++ 指针引用
简介首先了解下什么是指针:我的理解:在我们创建一个变量时,运行的时候就会在内存中开辟一块空间用来存储数据,指针是内存地址,指针变量是用来存放内存地址的变量,当我们需要这个地址的值时,只需要指向这块内存地址就可以获取到地址里的数据了。指针在初始化可为空,指针指向的对象可以改变,引用不能为空,必须和引用对象绑定,之后就不能改变了。指针和引用并没有本质区别,引用的底层就是指针,只是做了一层封装。简单例子#include <iostream> using namespace std; int main() { int x = 10; // 创建一个整数变量 就会在内存开辟一个地址空间 int *i = &x; // &x 是取 x 的地址 再存放到空指针里 那么i就是x的地址了 int &j = x; // 取 x 的地址数据 赋值给 j cout << i << endl; cout << j << endl; }输出: i:0x61fe0c j:10#include <iostream> using namespace std; void change(int &x, int &y) { int temp; temp = x; x = y; y = temp; } int main() { int i = 10; int j = 20; change(i, j); cout << i << "," << j << endl; }输出:20,10
2021年04月09日
33 阅读
0 评论
4 点赞
2021-04-09
【技术分享】C++ 命名空间
简介1.为什么要有命名空间? 其实很简单在开发中我们会创建函数比如 test(),在另一个可用的库中也存在一个相同的test()函数,这个时候编译器就无法判断你使用的是哪一个函数。2.因此,我们要引入命名空间,它是用来专门解决这个问题的,它是用来区分不同库中的相同名称的函数、类、变量等。 使用命名空间就可以定义上下文。3.简单来说,命名空间就是定义一个范围,以下例子可以让你很清楚的了解到。用命名空间前#include <iostream> int main() { std::cout << "Hello, World!" << std::endl; // 没用前发现要接std::才可以使用 return 0; }用命名空间后#include <iostream> using namespace std; // 使用命名空间后 int main() { cout << "Hello, World!" << endl; // 发现不需要std::了 return 0; }命名空间前后关系#include <iostream> int main() { cout << "Hello, World!" << endl; using namespace std; // 关键的来了 要是把位置放在这里 那么上面的语句就找不到会报错,只会认识下面的。 cout << "Hello, World!" << endl; return 0; }
2021年04月09日
56 阅读
0 评论
2 点赞
2021-04-08
【技术分享】C语言学习笔记
简介很早以前就学过C语言了,那个时候Python刚热门起来也学了下来,毕竟轮子多嘛方便,后面不怎么用C了慢慢的就忘记了,现在整理下复习笔记,方便自己查阅资料。C语言环境安装Mingw下载地址: https://sourceforge.net/projects/mingw/files/Installer/mingw-get-setup.exe1.下载完后运行Mingw,到这个界面后点击左上角的 Basic Setup,在右边面板寻找 mingw32-gcc-g++,鼠标右击并点击 Mark for Installation,之后再点击左上角的 Installation > Apply Changes。2.下载完后关掉所有界面,右击"此电脑" > 属性 > 高级系统设置 > 环境变量 > 在下面的系统变量中双击Path,如果之前没有改过安装路径,就添加 C:\MinGW\bin,按情况来添加。gcc --version3.添加好后输入 gcc --version 回车,能看见正常显示那就没问题了,只要配置了gcc的环境变量,那么vscode也可以运行。VSCode运行C代码1.到vscode应用商店安装,Code Runner 有了这个插件就很方便了,直接在vscode里运行代码。gcc编译器运行C程序gcc a.c -o a && a // C g++ a.cpp -o a && a // C++1.通过终端输入以上gcc命令,也是可以执行成功的。C语言中的关键字1.在C语言中,有32个关键字,它们具有预定义的含义,不能用作变量名,这些词也被称为 "保留词",最好避免将这些关键字用作变量名。if,else,switch,case,default – 用于决策控制编程结构。 break – 用于任何循环或者switch-case。 int,float,char,double,long – 这些是数据类型,在变量声明期间使用。 for,while – C 中的循环结构类型。 void – 其中一种返回类型。 goto – 用于重定向执行流程。 auto,signed,const,extern,register,unsigned – 定义一个变量。 return – 该关键字用于返回值。 continue – 它通常与for,while和do-while循环一起使用,当编译器遇到此语句时,它执行循环的下一次迭代,跳过当前迭代的其余语句。 enum – 常量集。 sizeof – 用于了解尺寸。 struct, typedef – 结构中使用的这两个关键字(单个记录中的数据类型分组)。 union – 它是一组变量,它们共享相同的内存位置和内存存储。 volatile编写第一个C程序先来编写一个经典的开场,那就是你好世界啦,C语言是强类编译型语言,文件后缀是以.c结尾,c++是以.cpp结尾。好了先创建一个 a.c 文件,将以下代码写入 a.c 文件里,然后再运行。#include <stdio.h> // 预处理器指令,告诉C编译器在实际编译之前要包含 stdio.h 文件 int main() { // main主函数,程序从这里开始执行 printf("Hello, World!"); // 输出函数 会在终端上显示内容 return 0; // 终止 main() 函数,并返回值 0 }输出结果:Hello, World!代码注释1.在日常的开发中,代码只会越写越多,而且不止是你一人在写,在团队中要想别人看的懂你的代码,就要用到注释了。// 单行注释 /* 多行注释 */ 2.内容写在注释里,编译器会忽略掉注释里的内容,这样就会方便我们在做开发的时候阅读代码。if-else语句if else语句的语法:如果条件返回true,则执行if正文内的语句,并跳过else正文内的语句。如果条件返回false,则跳过if正文中的语句,并执行else中的语句。if(condition) { // Statements inside body of if } else { //Statements inside body of else }#include <stdio.h> int main() { int par = 1; if (par == 1) { // 判断变量par 是否为真 printf("up"); // 成立就执行对的 }else{ printf("down"); // 否则就不成立 } return 0; }输出:upfor循环C语言中最常用的循环。步骤 1:首次初始化发生,计数器变量初始化。步骤 2:在第二步中检查条件,其中计数器变量由给定条件测试,如果条件返回true则执行for循环体内的 C 语句,如果条件返回false,则for循环终止,控制流退出循环。步骤 3:成功执行循环体内语句后,计数器变量会递增或递减,具体取决于操作(++或--)。for (initialization; condition test; increment or decrement) { //Statements to be executed repeatedly }#include <stdio.h> int main() { int i; for (i=1; i<=3; i++) { printf("%d\n", i); } return 0; }输出: 1 2 3C语言中的各种形式的for循环,我在以下所有例子中使用变量num作为计数器。1.这里使用num = num + 1而不是num ++。for (num=10; num<20; num=num+1)2.初始化部分可以从循环中跳过,如下所示,计数器变量在循环之前声明。int num=10; for (;num<20;num++)注意:即使我们可以跳过初始化部分但是分号(;)必须保留,否则你将得到编译错误。3.与初始化一样,您也可以跳过增量部分,如下所示。在这种情况下,分号(;)必须在条件逻辑之后。在这种情况下,增量或减量部分在循环内完成。for (num=10; num<20; ) { //Statements num++; } 4.这也是可能的,计数器变量在循环之前初始化并在循环内递增。int num=10; for (;num<20;) { //Statements num++; }5.如上所述,计数器变量也可以递减。在下面的示例中,每次循环运行时变量都会递减,直到条件num > 10返回false。for(num=20; num>10; num--)6.C语言中的嵌套for循环循环嵌套也是可能的。让我们举个例子来理解这个:#include <stdio.h> int main() { for (int i=0; i<2; i++) { for (int j=0; j<4; j++) { printf("%d, %d\n",i ,j); } } return 0; }输出: 0, 0 0, 1 0, 2 0, 3 1, 0 1, 1 1, 2 1, 37.在上面的例子中,我们在另一个for循环中有一个for循环,这称为循环嵌套。我们使用嵌套for循环的示例之一是二维数组。在 C 中循环内部进行多次初始化我们可以在for循环中进行多次初始化,如下所示。for (i=1,j=1;i<10 && j<10; i++, j++)上面的循环和简单的for循环有什么区别?它正在初始化两个变量。注意:两者都用逗号(,)分隔。它有使用 与(&&)逻辑运算符连接在一起的两个测试条件。注意:您不能使用以逗号分隔的多个测试条件,您必须使用逻辑运算符,例如&&或||连接条件。增量部分有两个变量。注意:应以逗号分隔。8.具有多个测试条件的for循环的示例#include <stdio.h> int main() { int i,j; for (i=1,j=1 ; i<3 || j<5; i++,j++) { printf("%d, %d\n",i ,j); } return 0; }输出: 1 2 3while循环1.循环用于重复执行语句块,如果条件返回true,则执行while循环体内的语句,否则控制退出循环。while (condition test) { //Statements to be executed repeatedly // Increment (++) or Decrement (--) Operation }#include <stdio.h> int main() { int count=1; while (count <= 4) { printf("%d ", count); count++; } return 0; }输出:1 2 3 42.while循环中使用逻辑运算符,就像关系运算符(< > <= >= == !=)一样,我们也可以在while循环中使用逻辑运算符。while(num1<=10 && num2<=10)while(num1<=10||num2<=10)while(num1!=num2 &&num1 <=num2)while(num1!=10 ||num2>=num1)3.在while循环中使用逻辑运算符测试多个条件#include <stdio.h> int main() { int i=1, j=1; while (i <= 4 || j <= 3) { printf("%d %d\n",i, j); i++; j++; } return 0; }输出: 1 1 2 2 3 3 4 4break语句1.break是用来退出循环的。#include <stdio.h> int main() { int num =0; while(num<=100) { printf("value of variable num is: %d\n", num); if (num==2) { break; } num++; } printf("Out of while-loop"); return 0; }输出: value of variable num is: 0 value of variable num is: 1 value of variable num is: 2 Out of while-loop2.在for循环中使用break#include <stdio.h> int main() { int var; for (var =100; var>=10; var --) { printf("var: %d\n", var); if (var==99) { break; } } printf("Out of for-loop"); return 0; }输出: var: 100 var: 99 Out of for-loop3.在switch-case中使用break语句#include <stdio.h> int main() { int num; printf("Enter value of num:"); scanf("%d",&num); switch (num) { case 1: printf("You have entered value 1\n"); break; case 2: printf("You have entered value 2\n"); break; case 3: printf("You have entered value 3\n"); break; default: printf("Input value is other than 1,2 & 3 "); } return 0; }输出: Enter value of num:2 You have entered value 2continue语句当在循环内遇到 continue语句时,控制流跳转到循环的开头以进行下一次迭代,跳过当前迭代循环体内语句的执行。#include <stdio.h> int main() { for (int j=0; j<=8; j++) { if (j==4) { continue; } printf("%d ", j); } return 0; }输出:0 1 2 3 5 6 7 8switch-case语句1.当我们有多个选项时,使用C语言switch-case语句,我们需要为每个选项执行不同的任务。switch (variable or an integer expression) { case constant: //C Statements ; case constant: //C Statements ; default: //C Statements ; }2.在switch-case中使用数字#include <stdio.h> int main() { int num=2; switch(num+2) { case 1: printf("Case1: Value is: %d", num); case 2: printf("Case1: Value is: %d", num); case 3: printf("Case1: Value is: %d", num); default: printf("Default: Value is: %d", num); } return 0; }输出:Default: value is: 23.在switch-case中使用字符#include <stdio.h> int main() { char ch='b'; switch (ch) { case 'd': printf("CaseD "); break; case 'b': printf("CaseB"); break; case 'c': printf("CaseC"); break; case 'z': printf("CaseZ "); break; default: printf("Default "); } return 0; }输出:CaseB4.switch的有效表达式switch(1+2+23) switch(1*2+3%4)5.无效的switch表达式switch(ab+cd) switch(a+b+c)goto语句goto label_name; .. .. label_name: C-statements#include <stdio.h> int main() { int sum=0; for(int i = 0; i<=10; i++){ sum = sum+i; if(i==5){ goto addition; } } addition: printf("%d", sum); return 0; }输出:15C语言按引用函数讨论按引用函数调用之前,让我们理解我们将在解释这个时使用的术语。实际参数:函数调用中出现的参数。形式参数:函数声明中出现的参数。int sum(int a, int b);int s = sum(10, 20); //Here 10 and 20 are actual parameters or int s = sum(n1, n2); //Here n1 and n2 are actual parameters按引用函数调用的示例#include <stdio.h> void increment(int *var) { *var = *var+1; } int main() { int num=20; increment(&num); printf("Value of num is: %d", num); return 0; }输出:Value of num is: 21在调用swapnum()函数后,变量的值已经更改,因为交换发生在变量num1和num2的地址上#include <stdio.h> void swapnum ( int *var1, int *var2 ) { int tempnum ; tempnum = *var1 ; *var1 = *var2 ; *var2 = tempnum ; } int main( ) { int num1 = 35, num2 = 45 ; printf("Before swapping:"); printf("\nnum1 value is %d", num1); printf("\nnum2 value is %d", num2); swapnum( &num1, &num2 ); printf("\nAfter swapping:"); printf("\nnum1 value is %d", num1); printf("\nnum2 value is %d", num2); return 0; }输出: Before swapping: num1 value is 35 num2 value is 45 After swapping: num1 value is 45 num2 value is 35文件I/O操作1.在上面的程序中,我们在r模式下打开文件newfile.txt,读取文件内容并在控制台上显示。#include <stdio.h> int main() { FILE *fp1; char c; fp1= fopen ("C:\\myfiles\\newfile.txt", "r"); while(1) { c = fgetc(fp1); if(c==EOF) break; else printf("%c", c); } fclose(fp1); return 0; }打开文件fopen()函数用于打开文件。FILE pointer_name = fopen ("file_name", "Mode");pointer_name可以是您选择的任何东西。file_name是您要打开的文件的名称。在此处指定完整路径,如C:\myfiles\newfile.txt。打开文件时,需要指定模式。我们用来读取文件的模式是r,它是 "只读模式"。FILE *fp; fp = fopen("C:\\myfiles\\newfile.txt", "r");第一个字符的地址存储在pointer fp中。如何检查文件是否已成功打开?如果文件未成功打开,则指针将被赋予NULL值,因此您可以编写如下逻辑:此代码将检查文件是否已成功打开。如果文件未打开,则会向用户显示错误消息。FILE fpr; fpr = fopen("C:\\myfiles\\newfile.txt", "r"); if (fpr == NULL) { puts("Error while opening file"); exit(); }文件打开模式使用fopen()函数打开文件,打开时可以根据需要使用以下任何一种模式。模式r:这是一种只读模式,这意味着如果文件在r模式下打开,它将不允许您编写和修改它的内容。当fopen()成功打开文件时,它返回文件第一个字符的地址,否则返回NULL。模式w:这是一种只写模式。fopen()函数在指定文件不存在时创建新文件,如果无法打开文件,则返回NULL。模式a:使用此模式,内容可以附加在现有文件的末尾。与模式w类似,如果文件不存在,fopen()会创建一个新文件。在打开不成功时,它返回NULL。文件指针指向:文件的最后一个字符。模式r+:此模式与模式r相同;但是,您可以对在此模式下打开的文件执行各种操作。您可以读取,写入和修改以r+模式打开的文件内容。文件指针指向:文件的第一个字符。模式w+:与可以执行的操作相同的模式w相同;可以在此模式下读取,写入和修改文件。模式a+:与模式a相同;您可以在文件中读取和附加数据,但在此模式下不允许进行内容修改。读取文件要读取文件,我们必须首先使用任何模式打开它,例如,如果您只想读取文件,然后以r模式打开它。根据文件打开期间选择的模式,我们可以对文件执行某些操作。fgetc():该函数从当前指针的位置读取字符,成功读取后,将指针移动到文件中的下一个字符。一旦指针到达文件的末尾,该函数返回 EOF(文件结束)。我们在程序中使用了 EOF 来确定文件的结尾。#include <stdio.h> int main() { FILE *fp1; char c; fp1 = fopen ("C:\\myfiles\\newfile.txt", "r"); while(1) { c = fgetc(fp1); if(c==EOF) break; else printf("%c", c); } fclose(fp1); return 0; }写入文件要写入文件,我们必须以支持写入的模式打开文件。例如,如果以r模式打开文件,则无法写入文件,因为r是只允许读取的只读模式。#include <stdio.h> int main() { char ch; FILE *fpw; fpw = fopen("C:\\newfile.txt","w"); if(fpw == NULL) { printf("Error"); exit(1); } printf("Enter any character: "); scanf("%c",&ch); fprintf(fpw,"%c",ch); fclose(fpw); return 0; }关闭文件fclose(fp);fclose()函数用于关闭打开的文件。作为参数,您必须提供指向要关闭的文件的指针。#include <stdio.h> int main() { char ch; FILE *fpr, *fpw; fpr = fopen("C:\\file1.txt", "r"); if (fpr == NULL) { puts("Input file cannot be opened"); } fpw= fopen("C:\\file2.txt", "w"); if (fpw == NULL) { puts("Output file cannot be opened"); } while(1) { ch = fgetc(fpr); if (ch==EOF) break; else fputc(ch, fpw); } fclose(fpr); fclose(fpw); return 0; }二进制文件的文件I/O如果文件是二进制文件(例如.exe文件)。上述程序不适用于二进制文件,但处理二进制文件时有一些细微的变化。主要区别在于文件名和模式。 让我们在一个例子的帮助下理解这一点。可以说我有两个二进制文件bin1.exe和bin2.exe– 我想将bin1.exe的内容复制到bin2.exe#include <stdio.h> int main() { char ch; FILE *fpbr, *fpbw; fpbr = fopen("bin1.exe", "rb"); if (fpbr == NULL) { puts("Input Binary file is having issues while opening"); } fpbw= fopen("bin2.exe", "wb"); if (fpbw == NULL) { puts("Output binary file is having issues while opening"); } while(1) { ch = fgetc(fpbr); if (ch==EOF) break; else fputc(ch, fpbw); } fclose(fpbr); fclose(fpbw); return 0; }指针的概念指针是存储另一个变量的地址的变量。与保存某种类型值的其他变量不同,指针保存变量的地址。例如,整数变量保存(或者可以说是存储)整数值,但整数指针保存整数变量的地址。在本指南中,我们将在示例的帮助下讨论C 编程中的指针。在这个程序中,我们有一个int类型的变量。num的值是 10,这个值必须存储在内存中的某个地方,对吧?为保存该变量值的变量分配一个内存空间,该内存空间有一个地址。例如,我们住在一所房子里,我们的房子有一个地址,帮助其他人找到我们的房子。同样,变量的值存储在内存地址中,这有助于 C 程序在需要时找到该值。因此,假设分配给变量num的地址是0x7fff5694dc58,这意味着我们应该将赋给num的任何值存储在以下位置:0x7fff5694dc58。#include <stdio.h> int main() { int num = 10; printf("Value of variable num is: %d", num); printf("\nAddress of variable num is: %p", &num); return 0; }输出: Value of variable num is: 10 Address of variable num is: 0x7fff5694dc58打印数组元素地址的简单示例#include <stdio.h> int main( ) { int val[7] = { 11, 22, 33, 44, 55, 66, 77 } ; for ( int i = 0 ; i < 7 ; i++ ) { printf("val[%d]: value is %d and address is %d\n", i, val[i], &val[i]); } return 0; }输出: val[0]: value is 11 and address is 1423453232 val[1]: value is 22 and address is 1423453236 val[2]: value is 33 and address is 1423453240 val[3]: value is 44 and address is 1423453244 val[4]: value is 55 and address is 1423453248 val[5]: value is 66 and address is 1423453252 val[6]: value is 77 and address is 1423453256C语言中的数组和指针示例#include <stdio.h> int main( ) { int *p; int val[7] = { 11, 22, 33, 44, 55, 66, 77 } ; p = &val[0]; for ( int i = 0 ; i<7 ; i++ ) { printf("val[%d]: value is %d and address is %p\n", i, *p, p); p++; } return 0; }输出: val[0]: value is 11 and address is 0x7fff51472c30 val[1]: value is 22 and address is 0x7fff51472c34 val[2]: value is 33 and address is 0x7fff51472c38 val[3]: value is 44 and address is 0x7fff51472c3c val[4]: value is 55 and address is 0x7fff51472c40 val[5]: value is 66 and address is 0x7fff51472c44 val[6]: value is 77 and address is 0x7fff51472c48数组的使用数组是相同数据类型的分组(或集合)。例如,int数组包含int类型的元素,而float数组包含float类型的元素。C 中声明数组int num[35]; /* An integer array of 35 elements */ char ch[10]; /* An array of characters for 10 elements */C语言中访问数组的元素您可以使用数组下标(或索引)来访问存储在数组中的任何元素。下标从 0 开始,这意味着arr[0]代表数组arr中的第一个元素。通常,arr[n-1]可用于访问数组的第n个元素。其中n是任何整数。int mydata[20]; mydata[0] /* first element of array mydata*/ mydata[19] /* last (20th) element of array mydata*/#include <stdio.h> int main() { int avg = 0; int sum =0; int x=0; int num[4]; for (x=0; x<4;x++) { printf("Enter number %d \n", (x+1)); scanf("%d", &num[x]); } for (x=0; x<4;x++) { sum = sum+num[x]; } avg = sum/4; printf("Average of entered number is: %d", avg); return 0; }输出: Enter number 1 10 Enter number 2 10 Enter number 3 20 Enter number 4 40 Average of entered number is: 20这里我们将数组从 0 迭代到 3,因为数组的大小是 4。在循环内部,我们向用户显示一条消息以输入值。使用scanf函数将所有输入值存储在相应的数组元素中。输入数据到数组for (x=0; x<4;x++) { printf("Enter number %d \n", (x+1)); scanf("%d", &num[x]); }数组中读出数据for (x=0; x<4;x++) { printf("num[%d]\n", num[x]); }初始化数组的不同方式int arr[5] = {1, 2, 3, 4 ,5}; int arr[] = {1, 2, 3, 4, 5};简单的二维数组#include<stdio.h> int main() { int disp[2][3]; int i, j; for(i=0; i<2; i++) { for(j=0;j<3;j++) { printf("Enter value for disp[%d][%d]:", i, j); scanf("%d", &disp[i][j]); } } printf("Two Dimensional array elements:\n"); for(i=0; i<2; i++) { for(j=0;j<3;j++) { printf("%d ", disp[i][j]); if(j==2){ printf("\n"); } } } return 0; }输出: Enter value for disp[0][0]:1 Enter value for disp[0][1]:2 Enter value for disp[0][2]:3 Enter value for disp[1][0]:4 Enter value for disp[1][1]:5 Enter value for disp[1][2]:6 Two Dimensional array elements: 1 2 3 4 5 6 二维数组的初始化int disp[2][4] = { {10, 11, 12, 13}, {14, 15, 16, 17} }; int disp[2][4] = { 10, 11, 12, 13, 14, 15, 16, 17};虽然上述两个声明都是有效的,但我建议您使用第一个方法,因为它更具可读性,因为您可以在此方法中可视化 2d 数组的行和列。指针和二维数组#include <stdio.h> int main() { int abc[5][4] ={ {0,1,2,3}, {4,5,6,7}, {8,9,10,11}, {12,13,14,15}, {16,17,18,19} }; for (int i=0; i<=4; i++) { printf("%d ",abc[i]); } return 0; }数组传递给函数#include <stdio.h> void disp( char ch) { printf("%c ", ch); } int main() { char arr[] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'}; for (int x=0; x<10; x++) { disp (arr[x]); } return 0; }输出:a b c d e f g h i j按引用传递数组给函数#include <stdio.h> void disp( int *num) { printf("%d ", *num); } int main() { int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; for (int i=0; i<10; i++) { disp (&arr[i]); } return 0; }输出:1 2 3 4 5 6 7 8 9 0将整个数组作为参数传递给函数#include <stdio.h> void myfuncn( int *var1, int var2) { for(int x=0; x<var2; x++) { printf("Value of var_arr[%d] is: %d \n", x, *var1); var1++; } } int main() { int var_arr[] = {11, 22, 33, 44, 55, 66, 77}; myfuncn(var_arr, 7); return 0; }输出: Value of var_arr[0] is: 11 Value of var_arr[1] is: 22 Value of var_arr[2] is: 33 Value of var_arr[3] is: 44 Value of var_arr[4] is: 55 Value of var_arr[5] is: 66 Value of var_arr[6] is: 77 函数的概念函数是执行特定任务的语句块。假设您正在使用 C语言构建应用,并且在某个程序中,您需要多次执行相同的任务。1.预定义的标准库函数 – 如puts(),gets(),printf(),scanf()等,这些函数已经在头文件中有定义(.h文件如stdio.h),所以我们只要在需要使用它们时调用它们。2.用户定义函数 – 我们在程序中创建的函数称为用户定义函数。为什么需要函数1.提高代码的可读性。2.提高代码的可重用性,可以在任何程序中使用相同的函数,而不是从头开始编写相同的代码。3.如果使用函数,代码的调试会更容易,因为错误很容易被跟踪。4.减少代码的大小,重复的语句集被函数调用替换。return_type function_name (argument list) { Set of statements – Block of code }#include <stdio.h> int addition(int num1, int num2) { int sum; sum = num1+num2; return sum; } int main() { int var1, var2; printf("Enter number 1: "); scanf("%d",&var1); printf("Enter number 2: "); scanf("%d",&var2); int res = addition(var1, var2); printf ("输出: %d", res); return 0; }常见的字符串处理函数strcat函数函数用于字符串连接,它在另一个指定字符串的末尾连接指定的字符串。char *strcat(char *str1, const char *str2)此函数将两个指针作为参数,并在连接后返回指向目标字符串的指针。str1 – 指向目标字符串的指针。str2 – 指向附加到目标字符串的源字符串的指针。#include <stdio.h> #include <string.h> int main () { char str1[50], str2[50]; strcpy(str1, "This is my initial string"); strcpy(str2, ", add this"); strcat(str1, str2); printf("String after concatenation: %s", str1); return 0; }输出:String after concatenation: This is my initial string, add thisstrncat函数类似的strncat()函数,它与 strcat()相同,只是 strncat()只将指定数量的字符附加到目标字符串。char *strncat(char *str1, const char *str2, size_t n)str1 – 目标字符串。str2 – 附加在目标字符串str1末尾的源字符串。n – 需要追加的源字符串str2的字符数。例如,如果这是 5,则只有源字符串str2的前 5 个字符将附加在目标字符串str1的末尾。返回值:该函数返回指向目标字符串str1的指针。#include <stdio.h> #include <string.h> int main () { char str1[50], str2[50]; strcpy(str1, "This is my initial string"); strcpy(str2, ", add this"); printf("String after concatenation: %s\n", strncat(str1, str2, 5)); printf("Destination String str1: %s", str1); return 0; }输出: String after concatenation: This is my initial string, add Destination String str1: This is my initial string, addstrchr函数给定字符串中搜索指定字符的出现,并返回指向它的指针。char *strchr(const char *str, int ch)str – 在其中搜索字符的字符串。ch – 在字符串str中搜索的字符。#include <stdio.h> #include <string.h> int main () { const char str[] = "This is just a String"; const char ch = 'u'; char *p; p = strchr(str, ch); printf("String starting from %c is: %s", ch, p); return 0; }输出: String starting from u is: ust a Stringstrcmp函数int strcmp(const char *str1, const char *str2)str1 – 第一个字符串 str2 – 第二个字符串 0:如果两个字符串相等 > 0:如果字符串str1的第一个不匹配字符的 ASCII 值大于字符串str2中的字符 < 0:如果字符串str1的第一个不匹配字符的 ASCII 值小于字符串str2中的字符#include <stdio.h> #include <string.h> int main () { char str1[20]; char str2[20]; int result; strcpy(str1, "hello"); strcpy(str2, "hEllo"); result = strcmp(str1, str2); if(result > 0) { printf("ASCII value of first unmatched character of str1 is greater than str2"); } else if(result < 0) { printf("ASCII value of first unmatched character of str1 is less than str2"); } else { printf("Both the strings str1 and str2 are equal"); } return 0; }输出:ASCII value of first unmatched character of str1 is greater than str2strncmp函数strncmp()函数,它与strcmp()相同,但strncmp()比较仅限于函数调用期间指定的字符数。例如,strncmp(str1, str2, 4)仅比较字符串str1和str2的前四个字符。int strncmp(const char *str1, const char *str2, size_t n)str1 – 第一个字符串 str2 – 第二个字符串 n – 需要比较的字符数。 0,如果字符串str1和str2都相等 > 0,如果str1的第一个不匹配字符的 ASCII 值大于str2 < 0,如果str1的第一个不匹配字符的 ASCII 值小于str2#include <stdio.h> #include <string.h> int main () { char str1[20]; char str2[20]; int result; strcpy(str1, "hello"); strcpy(str2, "helLO WORLD"); result = strncmp(str1, str2, 3); if(result > 0) { printf("ASCII value of first unmatched character of str1 is greater than str2"); } else if(result < 0) { printf("ASCII value of first unmatched character of str1 is less than str2"); } else { printf("Both the strings str1 and str2 are equal"); } return 0; }输出:Both the strings str1 and str2 are equalstrcoll函数strcmp()函数,它比较两个字符串并根据比较结果返回一个整数。int strcoll(const char *str1, const char *str2)str1 – 第一个字符串 str2 – 第二个字符串 > 0:如果字符串str1中第一个不匹配字符的 ASCII 值大于str2。 < 0:如果字符串str1中第一个不匹配字符的 ASCII 值小于str2。 = 0:如果两个字符串相等#include <stdio.h> #include <string.h> int main () { char str1[20]; char str2[20]; int result; strcpy(str1, "HELLO"); strcpy(str2, "hello world!"); result = strcoll(str1, str2); if(result > 0) { printf("ASCII value of first unmatched character of str1 is greater than str2"); } else if(result < 0) { printf("ASCII value of first unmatched character of str1 is less than str2"); } else { printf("Both the strings str1 and str2 are equal"); } return 0; }输出:ASCII value of first unmatched character of str1 is less than str2strcpy函数strcpy()函数将一个字符串复制到另一个字符串char *strcpy(char *str1, const char *str2)str1 – 这是复制其他字符串str2的值的目标字符串。函数的第一个参数。str2 – 这是源字符串,该字符串的值被复制到目标字符串。这是函数的第二个参数。#include <stdio.h> #include <string.h> int main () { char str1[20]; char str2[20]; strcpy(str1, "Apple"); printf("String str1: %s\n", str1); strcpy(str2, "Banana"); printf("String str2: %s\n", str2); strcpy(str1, str2); printf("String str1: %s\n", str1); return 0; }输出: String str1: Apple String str2: Banana String str1: Bananastrncpy函数类似于strcpy()函数,不同之处在于它只从源字符串复制指定数量的字符到目标字符串。char *strncpy(char *str1, const char *str2, size_t n)str1 – 目标字符串。复制源字符串str2的前n个字符到其中的字符串。str2 – 源字符串n – 需要复制的源字符串的字符数。#include <stdio.h> #include <string.h> int main () { char str1[20]; char str2[25]; strcpy(str2, "welcome to beginnersbook.com"); strncpy(str1, str2, 7); printf("String str1: %s\n", str1); printf("String str2: %s\n", str2); return 0; }输出: String str1: welcome String str2: welcome to beginnersbook.comstrrchr函数给定字符串中搜索指定字符的最后一次出现。此函数与函数strchr()完全相反,后者在字符串中搜索字符的第一次出现。char *strrchr(const char *str, int ch)#include <stdio.h> #include <string.h> int main () { const char str[] = "This-is-just-a-test-string"; const char ch = '-'; char *p, *p2; p = strrchr(str, ch); printf("String starting from last occurrence of %c is: %s\n", ch, p); p2 = strrchr(str, 'i'); printf("String starting from last occurrence of 'i' is: %s\n", p2); return 0; }输出: String starting from last occurrence of - is: -string String starting from last occurrence of 'i' is: ingstrspn函数给定字符串中搜索指定的字符串,并返回给定字符串中匹配的字符数。size_t strspn(const char *str1, const char *str2)#include <stdio.h> #include <string.h> int main () { int len; const char str1[] = "abcdefgh"; const char str2[] = "abXXcdeZZh"; len = strspn(str1, str2); printf("Number of matched characters: %d\n", len ); return 0; }输出:Number of matched characters: 5strstr函数指定主字符串中搜索给定字符串,并返回指向给定字符串第一次出现的指针。str – 要搜索的字符串。searchString – 我们需要在其中搜索字符串str的字符串char *strstr(const char *str, const char *searchString)#include <stdio.h> #include <string.h> int main () { const char str[20] = "Hello, how are you?"; const char searchString[10] = "you"; char *result; result = strstr(str, searchString); printf("The substring starting from the given string: %s", result); return 0; }输出:The substring starting from the given string: you?strcspn函数在主字符串中扫描给定字符串,并返回主字符串中从开头到第一个匹配字符的字符数。str1 – 要搜索的主字符串str2 – 在主字符串中搜索此字符串的字符,直到找到第一个匹配的字符#include <stdio.h> #include <string.h> int main () { const char str[20] = "aabbccddeeff"; const char searchString[10] = "dxz"; int loc; loc = strcspn(str, searchString); printf("The first matched char in string str1 is at: %d", (loc+1)); return 0; }输出:The first matched char in string str1 is at: 7strlen函数返回给定字符串的长度(字符数)。size_t strlen(const char *str)str – 这是我们需要计算长度的给定字符串#include <stdio.h> #include <string.h> int main () { char str[50]; int length; strcpy(str, "Welcome to Beginnersbook.com"); length = strlen(str); printf("Length of string - %s is: %d", str, length); return 0; }输出:Length of string - Welcome to Beginnersbook.com is: 28隐藏DOS窗口gcc main.c -s -mwindows // 添加 mwindows 参数即可隐藏自定义图标生成 icon 资源文件:dot2 ICON "00.ico"00.ico和C源文件,demo.rc文件均在同一路径下,行如下命令生成资源文件:windres demo.rc demo.o执行成功后,会生成demo.o文件gcc -s main.c demo.o -o main.exe
2021年04月08日
96 阅读
0 评论
23 点赞
0:00