(持续更新)逆向学习(KR 41笔记含随堂作业)
C开始的学习之路
#define _CRT_SECURE_O_WARIGS
关闭不安全函数的提示
#字符串和字符串函数
strncat(字符串,字符串,长度)
strncat()拼接字符串的函数
strcmp(用户响应的, 已存储的)
strcmp()把用户响应与已存储的字符串作比较
char str1[] = 12;
char str2[] = 124;
int result = strcmp(str1, str2);
if (result > 0)
{
printf(1>2);
}
else
{
printf(2>1);
}
strcpy_s(需拷贝的数组,原拷贝的数组)
strcpy()拷贝整个字符串到目标数组里
strncpy_s(数组,目标数组,长度)
strncpy()拷贝整个字符串到目标数组里更安全
sprintf(目标数组,打印内容,写入的条件1,条件2....)
sprintf()把数据写入字符串
char* strchr(ct char*s,int c)
如果s字符串中包含c字符,该函数返回指向,s字符串首位置的指针,如果未到,则返回空指针
char* strpbrk(ct char*s1,ct char*s2)
如果s1字符中包含s1字符串中的任意字符,函数返回指向s1字符首位置的指针,如果未到,则返回空字符
toupper()
处理字符串中的每个字符,把整个字符串转换成大写
memset(数组名,要清理内存使用的数据,清理的字节数)
清空字符串 字符串初始化函数
char str1[10] = china;
memset(str1, a , 10);
strlen()求字符串长度
fclose()关闭指定的文件,必要时刷新缓冲区
作用域:变量的作用范围(在何处能够访问到变量)
全局变量:定义在所有函数之外的变量,定义之后都可以访问,而且数据共享(内存只有一块),从定义开始到程序执行结束
局部变量:在函数或者代码块里面定义的变量,从定义开始到函数或者代码块结束
#define _CRT_SECURE_O_WARIGS
#include<stdio.h>
int num = 90;
void fun();
int main(void)
{
num = 5;
printf(%d\n, g_num);
fun();
return 0;
}
void fun()
{
printf(%d\n, num);
}
根据变量的作用域和生命周期分类
全局变量和局部变量
静态变量和动态变量
栈区(stack) | 存放函数的参数,局部变量等,由编译器自动释放 |
---|---|
堆区(heap) | 动态申请的内存存放在堆区,若不释放, |
全局区(static)静态 | 全局变量和静态变量存储是放一起的,里面细分有一个常量区,字符串常量和其他常量也存放在此,该区域在程序结束后又系统释放 |
代码区 | 存放函数体的二进制代码 |
自动变量:没有任何存储类别修饰的变量,都是自动变量
静态变量:用static修饰的变量,就是静态变量,不会自动释放内存,而是程序结束之后系统自动回收,加了static生命周期延长了,作用域不变
register 表示吧变量放在寄存器(CPU)里面,注意:这个只是说给编译器建议,但是编译器不一定会采纳
extern 表示外部变量 这个一般用在多文件中 申明使用外部变量
int test_a = 50;
extern int test_a;
int main(void)
{
printf(%d, test_a);
return 0;
}
动态内存是相对静态内存而言的,所谓动态和静态就是指内存的分配方式,动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存
静态内存:比如局部变量、形参等
动态内存分配的意义
1、C语言中一切操作都是基于内存的
2、变量和数组都是内存的别名
、定义数组时必须指定数组的大小,使用动态分配可以在运行时调整大小
4、函数结束之后,不希望变量的内存被释放
malloc
#define _CRT_SECURE_O_WARIGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
//栈区
int num = 20;
int* p = #
//自动申请到堆区,成功返回申请到的首地址,失败返回null
int* pn = (int*)malloc(sizeof(int)); //堆区地址
//防御性编程
if (pn == ULL)
{
printf(no\n);
return -1;
}
*pn = 66;
printf(%d, *pn);
//释放,要设置他为ULL,以免后面不小心还调用
pn = ULL;
free(pn);
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
int* parr = (int*)malloc(sizeof(int) * 5);
if (!parr)//!parr==ULL
{
return -1;
}
//把parr这片内存全部初始化为0
memset(parr, 0, sizeof(int) * 5);
for (int i = 0; i < 5; i)
{
//printf(%d, *(parr i));
//printf(%d, *(parr)); error,错误,会越界
}
free(parr);
parr = ULL;
return 0;
}
结构体数组
#define _CRT_SECURE_O_WARIGS
#include <iostream>
struct stStudent
{
int age;
int level;
};
//代表有5个stStudent结构体的数组
struct stStudent arr[5] = { {0,0},{1,1},{2,2},{,},{4,4} };
void MyPrint()
{
int a;
int b;
for (int i = 0; i < 5; i)
{
a = arr[i].age;
b = arr[i].level;
printf(%d %d\n, a, b);
}
};
int main()
{
MyPrint();
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include <iostream>
struct stStudent
{
int Age;
char name[0x20];
};
struct stStudent arr[];
void init()
{
arr[0].Age = 20;
printf(%s, strcpy(arr[0].name, china));
}
int main()
{
init();
return 0;
}
int main(int argc, char* ar[])
{
int arr[]={1,2,,4,5,6,7,8,9,10};
int (*px)[2][2]=(int (*)[2][2])arr;
printf(%d\n,(*px)[1][1]);//4
px; //2*2*4,现在位置到5开始
printf(%d\n,(*px)[1][1]);//8
return 0;
}
调用约定 | 参数压栈顺序 | 平衡堆栈 |
---|---|---|
_cdecl | 从右至左入栈 | 调用者清理栈 |
_stdcall | 从右至左入栈 | 自身清理堆栈 |
_fastcall | ECX/EDX传送前两个剩下:从右至左入栈 | 自身清理堆栈 |
返回类型(调用约定 *变量名)(参数列表)
如:
int(_cdecl *pFun)(int,int);
int main(int argc, char* ar[])
{
int(_stdcall *pFun)(int,int,int,int);
pFun=(int(_stdcall *)(int,int,int,int))0x76A70660;
MessageBox(0,0,0,0);
MessageBox(0,0,0,0);
pFun(0,0,0,0);
return 0;
}
指令 | 用途 |
---|---|
#define | 定义宏 |
#undef | 取消已定义宏 |
#if | 如果给定条件为真,则编译下面代码 |
#elif | 如果前面的#if给定条件不为真,当前条件为真,则编译下面代码 |
#else | 同else |
#endif | 结束一个#if…#else条件编译块 |
#ifdef | 如果宏已经定义,则编译下面代码 |
#ifndef | 如果宏没有定义,则编译下面代码 |
#include | 包含文件 |
滴水PE开始作业笔记
什么是可执行文件
可执行文件 executable file指的是可以由操作系统进行加载执行的文件
可执行文件的格式:
Windows平台:
PE(Portable Executable)文件结构
Linux平台:
ELF(Executable and Linking Format)文件结构
解析PE文件
1 DOS头 struct _IMAGE_DOS_HEADER(64)
0x00 WORD e_magic; //5A4D *MZ标记用于判断是否为可执行文件
0x02 WORD e_cblp; //0090
0x04 WORD e_cp; //000
0x06 WORD e_crlc; //0000
0x08 WORD e_cparhdr; //0004
0x0a WORD e_minalloc; //0000
0x0c WORD e_maxalloc; //FFFF
0x0e WORD e_ss; //0000
0x10 WORD e_sp; //00B8
0x12 WORD e_csum; //0000
0x14 WORD e_ip; //0000
0x16 WORD e_cs; //0000
0x18 WORD e_lfarlc; //0040
0x1a WORD e_ovno; //0000
0x1c WORD e_res[4]; //0000000000000000
0x24 WORD e_oemid; //0000
0x26 WORD e_oeminfo; //0000
0x28 WORD e_res2[10]; //0000 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
0xc DWORD e_lfanew; //000000F8 *PE头相对于文件的偏移,用于定位PE文件
PE头标志(4): //00005045
T头 struct _IMAGE_T_HEADERS 0xc DWORD e_lfanew;
↓
标准PE头 struct _IMAGE_FILE_HEADER(20)
0x00 WORD Machine; //014C *程序运行的CPU型号:0x0任何处理器//0x14C 86及后续处理器
0x02 WORD umberOfSecti; //0004 *文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值
0x04 DWORD TimeDateStamp; //4DCEB17D *时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的.
0x08 DWORD PointerToSymbolTable; //00000000
0x0c DWORD umberOfSymbols; //00000000
0x10 WORD SizeOfOptionalHeader; //00E0 *可选PE头的大小,2位PE文件默认E0h(16*14) 64位PE文件默认为F0h 大小可以自定义.
0x12 WORD Characteristics; //010F *每个位有不同的含义,可执行文件值为10F 即0 1 2 8位置1(特征勾选重定位信息、文件可执行、行号符号移去和2位机器是010F)
( 用二进制从右至左看
数据位 常量符号 为1时的定义
0 IMAGE_FILE_RELOCS_STRIPPED 文件中不存在重定位信息
1 IMAGE_FILE_EXECUTABLE_IMAGE 文件是可执行的
2 IMAGE_FILE_LIE_UMS_STRIPPED 不存在行信息
IMAGE_FILE_LOCAL_SYMS_STRIPPED 不存在符号信息
4 IMAGE_FILE_AGGRESSIVE_WS_TRIM 询整工作集
5 IMAGE_FILE_LARGE_ADDRESS_AWARE 应用程序可处理大于 2GB 的地址
6 此标志保留
7 IMAGE_FILE_BYTES_REVERSED_LO 小尾方式
8 IMAGE_FILE_2BIT_MACHIE 只在 2 位平台上运行
9 IMAGE_FILE_DEBUG_STRIPPED 不但含调试信息
10 IMAGE_FILE_REMOVABLE_RU_FROM_SWAP 不能从可移动盘运行
11 IMAGE_FILE_ET_RU_FROM_SWAP 不能从网络运行
12 IMAGE_FILE_SYSTEM 系统文件(如驱动程序),不能直接运行
1 IMAGE_FILE_DLL 这是一个 DLL 文件
14 IMAGE_FILE_UP_SYSTEM_OLY 文件不能在多处理器计算机上运行
15 IMAGE_FILE_BYTES_REVERSED_HI 大尾方式
)
可选PE头 struct _IMAGE_OPTIOAL_HEADER(224)
0x00 WORD Magic; //010B *说明文件类型:10B 2位下的PE文件 20B 64位下的PE文件
0x02 BYTE MajorLinkerVersion; //06
0x0 BYTE MinorLinkerVersion; //00
0x04 DWORD SizeOfCode; //00045000 *所有代码节的和,必须是FileAlignment的整数倍 编译器填的 没用
0x08 DWORD SizeOfInitializedData; //00028000 *已初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用
0x0c DWORD SizeOfUninitializedData; //00000000 *未初始化数据大小的和,必须是FileAlignment的整数倍 编译器填的 没用
0x10 DWORD AddressOfEntryPoint;(OEP) //000441EC *程序入口
0x14 DWORD BaseOfCode; //00001000 *代码开始的基址,编译器填的 没用
0x18 DWORD BaseOfData; //00046000 *数据开始的基址,编译器填的 没用
0x1c DWORD ImageBase; //00400000 *****内存镜像基址
0x20 DWORD SectionAlignment; *内存对齐 1000h
0x24 DWORD FileAlignment; *文件对齐 200h
0x28 WORD MajorOperatingSystemVersion;
0x2a WORD MinorOperatingSystemVersion;
0x2c WORD MajorImageVersion;
0x2e WORD MinorImageVersion;
0x0 WORD MajorSubsystemVersion;
0x2 WORD MinorSubsystemVersion;
0x4 DWORD Win2VersionValue;
0x8 DWORD SizeOfImage; //0006E000 *内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍
0xc DWORD SizeOfHeaders; *所有头节表按照文件对齐后的大小,否则加载会出错
0x40 DWORD CheckSum; *校验和,一些系统文件有要求.用来判断文件是否被修改.
0x44 WORD Subsystem;
0x46 WORD DllCharacteristics;
0x48 DWORD SizeOfStackReserve; *初始化时保留的堆栈大小
0x4c DWORD SizeOfStackCommit; *初始化时实际提交的大小
0x50 DWORD SizeOfHeapReserve; *初始化时保留的堆大小
0x54 DWORD SizeOfHeapCommit; *初始化时实践提交的大小
0x58 DWORD LoaderFlags;
0x5c DWORD umberOfRvaAndSizes; //00000010
0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16];
节表 _IMAGE_SECTIO_HEADER(在标准PE头umberOfSecti查看节的总数)
(.text节)(.data节)(.rsrc节)(.reloc节)(...)
0x00 BYTE ame[IMAGE_SIZEOF_SHORT_AME]; //0747865742E *8个字节 一般情况下是以\0结尾的ASCII吗字符串来标识的名称,内容可以自定义.
union {
0x08 DWORD PhysicalAddress;
0x08 DWORD VirtualSize;
} Misc;(被干掉也不妨碍程序) //000440A2 *双字 是该节在没有对齐前的真实尺寸,该值可以不准确。
0x0c DWORD VirtualAddress; //00001000 *节区在内存中的偏移地址。加上ImageBase才是在内存中的真正地址.
0x10 DWORD SizeOfRawData; //00045000 *节在文件中对齐后的尺寸.
0x14 DWORD PointerToRawData; //00001000 *在文件中的偏移 开始地址(在文件中存在哪里)
0x18 DWORD PointerToRelocati;
0x1c DWORD PointerToLinenumbers;
0x20 WORD umberOfRelocati;
0x22 WORD umberOfLinenumbers;
0x24 DWORD Characteristics; *节的属性(可读、可写、可执行)
读取PE头
#define _CRT_SECURE_O_WARIGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define filepath C:\\Users\\Administrator\\Desktop\\
LPVOID ReadPEFile(LPCSTR lpszFile);
VOID PrintTHeaders();
int main(int argc, char* ar[])
{
PrintTHeaders();
return 0;
}
LPVOID ReadPEFile(LPCSTR lpszFile)
{
FILE* pFile = ULL;
DWORD fileSize = 0;
LPVOID pFileBuffer = ULL;
//打开文件
pFile = fopen(lpszFile, rb);
if (!pFile)
{
printf(无法打开文件!\n);
return ULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_ED);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//开辟内存空间、分配缓冲区
pFileBuffer = malloc(fileSize);
if (!pFileBuffer)
{
printf(分配空间失败!\n);
fclose(pFile);
return ULL;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf(读取数据失败!\n);
free(pFileBuffer);
fclose(pFile);
return ULL;
}
//关闭文件
fclose(pFile);
return pFileBuffer;
}
VOID PrintTHeaders()
{
LPVOID pFileBuffer = ULL;
PIMAGE_DOS_HEADER pDosHeader = ULL;
PIMAGE_T_HEADERS ptHeaders = ULL;
PIMAGE_FILE_HEADER pFileHeader = ULL;
PIMAGE_OPTIOAL_HEADER pOpHeader = ULL;
PIMAGE_SECTIO_HEADER pSeHeader = ULL;
pFileBuffer = ReadPEFile(filepath);
if (!pFileBuffer)
{
printf(文件读取失败!\n);
return;
}
//判断是否是有效的MZ标志
if ((*(PWORD)pFileBuffer) != IMAGE_DOS_SIGATURE)
{
printf(不是有效的MZ标志!\n);
free(pFileBuffer);
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//以下要往前加位置才能往后读取
//打印DOS头
printf(**********DOS头**********\n);
printf(MZ标志:%x\n, pDosHeader->e_magic);
printf(PE偏移 定位PE:%x\n, pDosHeader->e_lfanew);
//打印T头
printf(**********T头**********\n);
ptHeaders = (PIMAGE_T_HEADERS)((DWORD)pFileBufferpDosHeader->e_lfanew);
printf(T头:%x\n, ptHeaders->Signature);
//打印标准PE头
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ptHeaders4);
printf(Machine:%x\n, pFileHeader->Machine);
printf(umberOfSecti:%x\n, pFileHeader->umberOfSecti);
printf(TimeDateStamp:%x\n, pFileHeader->TimeDateStamp);
printf(PointerToSymbolTable:%x\n, pFileHeader->PointerToSymbolTable);
//打印可选PE头
pOpHeader = (PIMAGE_OPTIOAL_HEADER)((DWORD)pFileHeader20);
printf(Magic:%x\n,pOpHeader->Magic);
printf(SizeOfCode:%x\n, pOpHeader->SizeOfCode);
//释放内存
free(pFileBuffer);
}
fopen()打开待读取的文件
r 打开只读文件,该文件必须存在
r 打开可读写的文件,该文件必须存在
rb 读写打开一个二进制文件,只允许读写数据
rt 读写打开一个文本文件,允许读和写
w 打开只写文件,若文件存在则文件长度清为0,即该文件内容会消失。若文件不存在则建立该文件。
w 打开可读写文件,若文件存在则文件长度清为零,即该文件内容会消失。若文件不存在则建立该文件。
a 以附加的方式打开只写文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾,即文件原先的内容会被保留。(EOF符保留)
a 以附加方式打开可读写的文件。若文件不存在,则会建立该文件,如果文件存在,写入的数据会被加到文件尾后,即文件原先的内容会被保留。 (原来的EOF符不保留)
wb 只写打开或新建一个二进制文件;只允许写数据。
wb 读写打开或建立一个二进制文件,允许读和写。
wt 读写打开或着建立一个文本文件;允许读写。
at 读写打开一个文本文件,允许读或在文本末追加数据。
ab 读写打开一个二进制文件,允许读或在文件末追加数据。
fseek()用来读取文件流的读写位置
SEEK_ED 将读写位置指向文件末尾
SEEK_SET 将读写位置指向文件开头
SEEK_CUR 将读写位置指向当前位置
ftell()取得目前读写的位置
fread() fwrite() 将文件内容读取到内存
LPVOID 是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量,在使用的时候转换回来
BYTE char 1字节
WORD short 2字节
DWORD long 4字节
PWORD 是指向一个单词的指针,在2位下是4字节在64位下是8字节
size_t 用于表示对象的大小或数组的索引,是计算对象大小并存储,无符号整数类型,sizeof操作符返回的结果类型(通常用于:1、计算数组的长度或元素数量 2、在内存分配函数如malloc、calloc中表示分配的内存大小 、在函数参数中用作索引如memset、memcpy)
读取节表
#define _CRT_SECURE_O_WARIGS
//#include stdafx.h
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define filepath C:\\Users\\Administrator\\Desktop\\
LPVOID ReadPEFile(LPCSTR lpszFile);
void PrintTHeaders();
int main(int argc, char* ar[])
{
PrintTHeaders();
return 0;
}
LPVOID ReadPEFile(LPCSTR lpszFile)
{
FILE* pFile = ULL;
DWORD fileSize = 0;
LPVOID pFileBuffer = ULL;
//打开文件
pFile = fopen(lpszFile, rb);
if (!pFile)
{
printf(无法打开文件!\n);
return ULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_ED);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//开辟内存空间、分配缓冲区
pFileBuffer = malloc(fileSize);
if (!pFileBuffer)
{
printf(分配空间失败!\n);
fclose(pFile);
return ULL;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf(读取数据失败!\n);
free(pFileBuffer);
fclose(pFile);
return ULL;
}
//关闭文件
fclose(pFile);
return pFileBuffer;
}
void PrintTHeaders()
{
LPVOID pFileBuffer = ULL;
PIMAGE_DOS_HEADER pDosHeader = ULL;
PIMAGE_T_HEADERS ptHeaders = ULL;
PIMAGE_FILE_HEADER pFileHeader = ULL;
PIMAGE_OPTIOAL_HEADER pOpHeader = ULL;
PIMAGE_SECTIO_HEADER pSeHeader = ULL;
pFileBuffer = ReadPEFile(filepath);
if (!pFileBuffer)
{
printf(文件读取失败!\n);
return;
}
//判断是否是有效的MZ标志
if ((*(WORD*)pFileBuffer) != IMAGE_DOS_SIGATURE)
{
printf(不是有效的MZ标志!\n);
free(pFileBuffer);
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//以下要往前加位置才能往后读取
//打印DOS头
printf(**********DOS头**********\n);
printf(MZ标志:%x\n, pDosHeader->e_magic);
printf(PE偏移 定位PE:%x\n, pDosHeader->e_lfanew);
//打印T头
printf(**********T头**********\n);
ptHeaders = (PIMAGE_T_HEADERS)((DWORD)pFileBufferpDosHeader->e_lfanew);
printf(T头:%x\n, ptHeaders->Signature);
//打印标准PE头
printf(**********标准PE头**********\n);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ptHeaders4);
printf(Machine:%x\n, pFileHeader->Machine);
printf(umberOfSecti:%x\n, pFileHeader->umberOfSecti);
printf(TimeDateStamp:%x\n, pFileHeader->TimeDateStamp);
printf(PointerToSymbolTable:%x\n, pFileHeader->PointerToSymbolTable);
printf(SizeOfOptionalHeader:%x\n, pFileHeader->SizeOfOptionalHeader);
//打印可选PE头
printf(**********可选PE头**********\n);
pOpHeader = (PIMAGE_OPTIOAL_HEADER)((DWORD)pFileHeader20);
printf(Magic:%x\n,pOpHeader->Magic);
printf(SizeOfCode:%x\n, pOpHeader->SizeOfCode);
//打印节表
pSeHeader = IMAGE_FIRST_SECTIO(ptHeaders);
for (int i = 0; i < ptHeaders->FileHeader.umberOfSecti; i)
{
printf(**********第%d个节表**********\n, i 1);
printf(ame:%.*s\n, IMAGE_SIZEOF_SHORT_AME, pSeHeader->ame);
printf(VirtualSize:0x%.x\n, pSeHeader[i].Misc.VirtualSize);
printf(VirtualAddress:0x%.x\n, pSeHeader[i].VirtualAddress);
printf(SizeOfRawData:0x%.x\n, pSeHeader[i].SizeOfRawData);
printf(PointerToRawData:0x%.x\n, pSeHeader[i].PointerToRawData);
printf(PointerToRelocati:0x%.x\n, pSeHeader[i].PointerToRelocati);
printf(PointerToLinenumbers:0x%.x\n, pSeHeader[i].PointerToLinenumbers);
printf(umberOfRelocati:0x%.x\n, pSeHeader[i].umberOfRelocati);
printf(umberOfLinenumbers:0x%.x\n, pSeHeader[i].umberOfLinenumbers);
printf(Characteristics:0x%.x\n, pSeHeader[i].Characteristics);
}
//释放内存
free(pFileBuffer);
}
#define _CRT_SECURE_O_WARIGS
//#include stdafx.h
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define filepath C:\\Users\\Administrator\\Desktop\\
LPVOID ReadPEFile(LPCSTR lpszFile);
void PrintTHeaders();
int main(int argc, char* ar[])
{
PrintTHeaders();
return 0;
}
LPVOID ReadPEFile(LPCSTR lpszFile)
{
FILE* pFile = ULL;
DWORD fileSize = 0;
LPVOID pFileBuffer = ULL;
//打开文件
pFile = fopen(lpszFile, rb);
if (!pFile)
{
printf(无法打开文件!\n);
return ULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_ED);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//开辟内存空间、分配缓冲区
pFileBuffer = malloc(fileSize);
if (!pFileBuffer)
{
printf(分配空间失败!\n);
fclose(pFile);
return ULL;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf(读取数据失败!\n);
free(pFileBuffer);
fclose(pFile);
return ULL;
}
//关闭文件
fclose(pFile);
return pFileBuffer;
}
void PrintTHeaders()
{
LPVOID pFileBuffer = ULL;
PIMAGE_DOS_HEADER pDosHeader = ULL;
PIMAGE_T_HEADERS ptHeaders = ULL;
PIMAGE_FILE_HEADER pFileHeader = ULL;
PIMAGE_OPTIOAL_HEADER pOpHeader = ULL;
PIMAGE_SECTIO_HEADER pSeHeader = ULL;
pFileBuffer = ReadPEFile(filepath);
if (!pFileBuffer)
{
printf(文件读取失败!\n);
return;
}
//判断是否是有效的MZ标志
if ((*(WORD*)pFileBuffer) != IMAGE_DOS_SIGATURE)
{
printf(不是有效的MZ标志!\n);
free(pFileBuffer);
return;
}
pDosHeader = (PIMAGE_DOS_HEADER)pFileBuffer;
//以下要往前加位置才能往后读取
//打印DOS头
printf(**********DOS头**********\n);
printf(MZ标志:%x\n, pDosHeader->e_magic);
printf(PE偏移 定位PE:%x\n, pDosHeader->e_lfanew);
//打印T头
printf(**********T头**********\n);
ptHeaders = (PIMAGE_T_HEADERS)((DWORD)pFileBuffer pDosHeader->e_lfanew);
printf(T头:%x\n, ptHeaders->Signature);
//打印标准PE头
printf(**********标准PE头**********\n);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ptHeaders 4);
printf(Machine:%x\n, pFileHeader->Machine);
printf(umberOfSecti:%x\n, pFileHeader->umberOfSecti);
printf(TimeDateStamp:%x\n, pFileHeader->TimeDateStamp);
printf(PointerToSymbolTable:%x\n, pFileHeader->PointerToSymbolTable);
printf(SizeOfOptionalHeader:%x\n, pFileHeader->SizeOfOptionalHeader);
//打印可选PE头
printf(**********可选PE头**********\n);
pOpHeader = (PIMAGE_OPTIOAL_HEADER)((DWORD)pFileHeader 20);
printf(Magic:%x\n, pOpHeader->Magic);
printf(SizeOfCode:%x\n, pOpHeader->SizeOfCode);
printf(SizeOfHeaders:%x\n, pOpHeader->SizeOfHeaders);
//打印节表
//pSeHeader = IMAGE_FIRST_SECTIO(ptHeaders);
pSeHeader = (PIMAGE_SECTIO_HEADER)((DWORD)pOpHeader pFileHeader->SizeOfOptionalHeader);
for (int i = 0; i < ptHeaders->FileHeader.umberOfSecti; i)
{
printf(**********第%d个节表**********\n, i 1);
printf(ame:%.*s\n, IMAGE_SIZEOF_SHORT_AME, pSeHeader[i].ame);
printf(VirtualSize:0x%.x\n, pSeHeader[i].Misc.VirtualSize);
printf(VirtualAddress:0x%.x\n, pSeHeader[i].VirtualAddress);
printf(SizeOfRawData:0x%.x\n, pSeHeader[i].SizeOfRawData);
printf(PointerToRawData:0x%.x\n, pSeHeader[i].PointerToRawData);
printf(PointerToRelocati:0x%.x\n, pSeHeader[i].PointerToRelocati);
printf(PointerToLinenumbers:0x%.x\n, pSeHeader[i].PointerToLinenumbers);
printf(umberOfRelocati:0x%.x\n, pSeHeader[i].umberOfRelocati);
printf(umberOfLinenumbers:0x%.x\n, pSeHeader[i].umberOfLinenumbers);
printf(Characteristics:0x%.x\n, pSeHeader[i].Characteristics);
}
//释放内存
free(pFileBuffer);
}
RVA与FOA的转换
RVA:相对虚拟地址
FOA:文件偏移地址
1、得到RVA的值:内存地址-ImageBase
2、判断RVA是否位于PE头重,如果是:FOA==RVA
、判断RVA位于哪个节(条件必须同时成立的):
RVA>=节.VirtualAddress
RVA<=节.VirtualAddress节.VirtualSize
差值A=RVA-节.VirtualAddress
文件中的地址=差值APointRawData
RVA = 全局变量 - ImageBase
FOA:
1、判断RVA是否在头部 在的话直接返回
FOA=RVA
2、如不在就判断这个值在哪个节里
RVA>=节.VirtualAddress
RVA<=节.VirtualAddress当前节内存对齐后的大小
差值=RVA-节.VirtualAddress
FOA=节.PointerToRawData差值
PE加载的过程
1、根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer).
2、根据SizeOfHeader的大小,将头信息从FileBuffer拷贝到ImageBuffer
、根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.
4、Misc.VirtualSize 和 SizeOfRawData谁大?
5、FileBuffer与ImageBuffer谁大?
PE模拟exe加载到内存(FileBuffer-ImageBuffer)
#define _CRT_SECURE_O_WARIGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
//变量声明
PIMAGE_DOS_HEADER pDosHeader = ULL;
PIMAGE_T_HEADERS ptHeader = ULL;
PIMAGE_FILE_HEADER pFileHeader = ULL;
PIMAGE_SECTIO_HEADER pSeHeader = ULL;
PIMAGE_OPTIOAL_HEADER pOpHeader = ULL;
//exe文件地址
#define filepath C:\\Users\\Administrator\\Desktop\\
DWORD ReadFile(LPVOID* pFileBuffer);
DWORD CopyFileBufferToImageBuffer(LPVOID ppFileBuffer, LPVOID* ppImageBuffer);
DWORD CopyImageBufferToFileBuffer(LPVOID pppImageBuffer, LPVOID* pppBuffer);
BOOL MemeryToFile(LPVOID ppppBuffer, DWORD SizeOfBuffer);
int main(int argc, char* ar[])
{
LPVOID pFileBuffer = ULL;
LPVOID* ppFileBuffer = &pFileBuffer;
LPVOID pImageBuffer = ULL;
LPVOID* ppImageBuffer = &pImageBuffer;
DWORD SizeOfFileBuffer = 0;
DWORD SizeOfImageBuffer = 0;
DWORD SizeOfBuffer = 0;
LPVOID pBuffer = ULL;
LPVOID* ppBuffer = &pBuffer;
//调用readFile
SizeOfFileBuffer = ReadFile(ppFileBuffer);
if (!SizeOfFileBuffer)
{
printf(调用ReadFile失败\n);
return 0;
}
pFileBuffer = *ppFileBuffer;
//调用CopyFileBufferToImageBuffer
SizeOfBuffer = CopyFileBufferToImageBuffer(pFileBuffer, ppImageBuffer);
if (!SizeOfBuffer)
{
printf(调用FileToImage失败!\n);
return 0;
}
//调用CopyImageBufferToFileBuffer
SizeOfBuffer = CopyImageBufferToFileBuffer(pImageBuffer, ppBuffer);
pBuffer = *ppBuffer;
if (!SizeOfBuffer)
{
printf(调用ImageToewFile失败!\n);
return 0;
}
//调用MemeryToFile
if (MemeryToFile(pBuffer, SizeOfBuffer) == FALSE)
{
printf(end\n);
return 0;
}
return 0;
}
DWORD ReadFile(LPVOID* pFileBuffer)
{
FILE* file;
DWORD pFileSize = 0;
//打开文件
file = fopen(filepath, rb);
if (!file)
{
printf(打开文件失败!\n);
return 0;
}
//读取文件大小
fseek(file, 0, SEEK_ED);
pFileSize = ftell(file);
fseek(file, 0, SEEK_SET);
if (pFileSize == ULL)
{
printf(读取文件大小失败!\n);
return 0;
}
//分配内存
*pFileBuffer = malloc(pFileSize);
if (!*pFileBuffer)
{
printf(分配内存失败!\n);
fclose(file);
return 0;
}
//将文件内容读取到缓冲区
size_t n = fread(*pFileBuffer, pFileSize, 1, file);
if (!n)
{
printf(复制数据失败!\n);
fclose(file);
free(*pFileBuffer);
return 0;
}
fclose(file);
return pFileSize;
}
DWORD CopyFileBufferToImageBuffer(LPVOID ppFileBuffer, LPVOID* ppImageBuffer)
{
if (!ppFileBuffer)
{
printf(ppFileBuffer调用失败!\n);
return 0;
}
printf(CopyFileBufferToImageBuffer里ppFileBuffer刚开始的值:%p\n, ppFileBuffer);
//判断是否是PE文件
pDosHeader = (PIMAGE_DOS_HEADER)ppFileBuffer;
if (pDosHeader->e_magic != IMAGE_DOS_SIGATURE)
{
printf(不是有效的MZ标志!\n);
return 0;
}
ptHeader = (PIMAGE_T_HEADERS)((DWORD)ppFileBuffer pDosHeader->e_lfanew);
if (ptHeader->Signature != IMAGE_T_SIGATURE)
{
printf(不是有效的PE标志!\n);
return 0;
}
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ptHeader 4);
pOpHeader = (PIMAGE_OPTIOAL_HEADER)((DWORD)pFileHeader 20);
//开辟空间
*ppImageBuffer = malloc(pOpHeader->SizeOfImage);
if (!*ppImageBuffer)
{
printf(CopyFileBufferToImageBuffer开辟imagebuffer空间失败!\n);
return 0;
}
//将内存清零
memset(*ppImageBuffer, 0, pOpHeader->SizeOfImage);
//复制数据
memcpy(*ppImageBuffer, pDosHeader, pOpHeader->SizeOfHeaders);
//循环复制节表
pSeHeader = (PIMAGE_SECTIO_HEADER)((DWORD)pOpHeader pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->umberOfSecti; i, pSeHeader)
{
memcpy((LPVOID)((DWORD)*ppImageBuffer pSeHeader->VirtualAddress), (LPVOID)((DWORD)ppFileBuffer pSeHeader->PointerToRawData), pSeHeader->SizeOfRawData);
printf(%d\n, i);
}
printf(CopyFileBufferToImageBuffer拷贝成功!\n);
return pOpHeader->SizeOfImage;
}
DWORD CopyImageBufferToFileBuffer(LPVOID pppImageBuffer, LPVOID* pppBuffer)
{
if (!pppImageBuffer)
{
printf(调用pppImageBuffer失败!\n);
return 0;
}
pDosHeader = (PIMAGE_DOS_HEADER)pppImageBuffer;
ptHeader = (PIMAGE_T_HEADERS)((DWORD)pppImageBuffer pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)ptHeader 4);
pOpHeader = (PIMAGE_OPTIOAL_HEADER)((DWORD)pFileHeader 20);
pSeHeader = (PIMAGE_SECTIO_HEADER)((DWORD)pOpHeader pFileHeader->SizeOfOptionalHeader);
//得到FileBuffer的大小
for (int i = 1; i < pFileHeader->umberOfSecti; i, pSeHeader)
{
printf(%d\n, i);
}
//循环到最后一个节表
DWORD SizeOfBuffer = pSeHeader->PointerToRawData pSeHeader->SizeOfRawData;
//开辟空间
*pppBuffer = malloc(SizeOfBuffer);
if (!*pppBuffer)
{
printf(开辟buffer空间失败!\n);
return 0;
}
//缓冲区清零
memset(*pppBuffer, 0, SizeOfBuffer);
memcpy(*pppBuffer, pppImageBuffer, pOpHeader->SizeOfHeaders);
//循环复制节表
pSeHeader = (PIMAGE_SECTIO_HEADER)((DWORD)pOpHeader pFileHeader->SizeOfOptionalHeader);
for (int j = 1; j <= pFileHeader->umberOfSecti; j, pSeHeader)
{
memcpy((LPVOID)((DWORD)*pppBuffer pSeHeader->PointerToRawData), (LPVOID)((DWORD)pppImageBuffer pSeHeader->VirtualAddress), pSeHeader->SizeOfRawData);
printf(%d\n, j);
}
printf(CopyImageBufferToFileBuffer拷贝成功!\n);
return SizeOfBuffer;
}
BOOL MemeryToFile(LPVOID ppppBuffer, DWORD SizeOfBuffer)
{
FILE* file = fopen(C:\\Users\\Administrator\\Desktop\\, wb);
if (!file)
{
printf(file error\n);
return 0;
}
if (fwrite(ppppBuffer, 1, SizeOfBuffer, file) == 0)
{
printf(file fwrite fail\n);
return 0;
}
fclose(file);
file = ULL;
printf(success\n);
return TRUE;
}
硬编码
代码节空白区添加代码(文件注入)
MessageBoxA:0x76780660
Function();
0040D478 E8 8D B FF FF call @ILT5(Function) (0040100a)
//MessageBox(0,0,0,0);
return 0;
0040D47D C0 xor eax,eax
E9 01 00 00 00 jmp Function (00401010)
CALL真正要跳转的地址 = E8这条指令的下一行地址 X
X = 真正要跳转的地址0040100a - E8这条指令的下一行地址0040D47D = 8DBFFFF
X = 401010 - 40100F = 1 (JMP就是E9 01)
要跳转的地方 = E8当前的地址 5 X
X = 要跳转的地址 - (E8的地址 5)
---------------------------------------------------------------------
(放在任意一个节后面的空白区)
6A 00 6A 00 6A 00 6A 00 (MeassageBoxA的硬编码push函数)
E8 00 00 00 00 E9 00 00 00 00 (call地址消息函数)
E8 00 00 00 00 后面0是填dbg到的MeassageBoxA基址-E8最后一个00后一位的地址
E9 00 00 00 00 后面0是填(EntryPointimagebase)-(E9最后一个00的地址)
最后把原来EntryPoint改成从什么地址开始添加的代码就改成什么地址
call = E8
jmp = E9
push = 6A
KR笔记
cl /c 产生.obj文件
.obj就是产生的代码支撑、数据支撑,二进制代码的中间文件,可以交给其他的编译器使用
link 产生.exe可执行文件
#include <stdio.h> <>尖括号仅是环境变量 双引号是当前目录
存进去的地址从左至右是从高位置到低位置,就是小段方式
#include <stdio.h>
#include
int main(int argc, char *ar[])
{
int n = 999;
printf(Hello, world:%p:%d\n,&n,n);
foo();
getchar();
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
int main(int argc, char* ar[])
{
int n = 0;
srand((unsigned)time(ULL));
while (n < 10)
{
printf(%d\r\n, rand());
n;
}
return 0;
}
为什么到80000000出来了?
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* ar[])
{
int n = 1;
while (n > 0)
{
n;
}
printf(%x\r\n, n);
return 0;
}
求补运算是一种运算
补码是一种编码
补码规定了数据的读写双方必须做到以下几点
1、最高有效位是符号位,0表示正、1表示负
2、当数据是正数的时候,其余各位直接存储其数值
、当数据为负数的时候,其余各位存储其求补后的值
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* ar[])
{
int n = 52;
int m = -52;
printf(%x\r\n, &n);
return 0;
}
52
110100 最高位补位后
00110100
看成2进制就是
0011 0100
4在内存中就是4 00 00 00
-52
-52 的二进制表示可以通过以下步骤得到:
- 首先,将 52 的绝对值转换为二进制。
- 然后,将得到的二进制数取反,得到其反码。
- 最后,将得到的反码加 1,得到补码。
首先,52 的绝对值为 00110100(因为 52 的二进制表示为 00110100)。
然后,将其取反得到其反码:11001011。
最后,将得到的反码加 1。这一步骤是为了确保补码的符号位正确表示负数。在二进制补码表示中,最高位(即最左边的位)表示符号位。如果是负数,符号位为 1;如果是正数,符号位为 0。因此,将反码加 1 可以确保符号位正确。在这种情况下,11001011 加 1 为 11001100。
所以,-52 的二进制表示为 11001100。
CC 00 00 00
#include <stdio.h>
int main(int argc, char *ar[])
{
//6, -6
//0x86单字节有符号数的十进制真值是什么?
//10000110
//1 0000110
//- 1111001 取反1
//- 1111010
// 0111 1010
// 7 A
//7A=7*1610=-122
//0x80呢?
//1000 0000
//-0111 1111 取反1
//-10000000 = -128
return 0;
}
C05:内存访问异常:访问了保留地址、或不存在的地址、或没有权限的地址
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* ar[])
{
//一定要对变量进行初始化,否则上次用n变量的还会有残留数据
int i1 = 0;
char c1 = \0 ;
float f1 = 0.0f;
double d1 = 0.0;
char name[10] = { 0 };
int* lpTest = ULL;
char szBuf[20] = { 0 };
//写长度不要造成越界,不然很容易被攻击
//scanf(%s, szBuf, 20);
//short int ary[2] = { 666,999 };
//printf(%x\n, &ary[0]);
//scanf(%d, ary);
/*随堂作业*/
//int n = 0x19FEF4;
//int m = 0;
//scanf(%d,n); //输入999
//printf(%08x\r\n,n); //00000e7
//printf(%08x\r\n,m); //00000000
//等号左边必须是一个变量
char ch1= a ;
char ch2= b ;
char ch= c ;
char ch4= d ;
printf(%p:%d\r\n,&ch1,sizeof(ch1));
printf(%p:%d\r\n,&ch2,sizeof(ch2));
system(pause);
return 0;
}
**程序效率高不高跟语言没关系,和写程序的人有关系,人决定8成以上,剩下看编译器
99乘法表
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* ar[])
{
for (int i = 1;i<=9;i)
{
for (int j = 1;j<=i;j)
{
printf(%d*%d=%d ,j,i,i*j);
}
printf(\n);
}
system(pause);
return 0;
}
四角星作业
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/*
6 *
5 * *
4 * *
* *
2 * *
1 * *
0 *
0 1 2 4 5 6
ax c = y
0a c =
a c = 6
c =
a = 1
x == y
a c = 6
6a c =
a = -1
c = 9
-x 9 == y
6a c =
a c = 0
a = 1
c = -
x - == y
a c = 0
0a c =
a = -1
c =
-x == y
*/
int main(int argc, char* ar[])
{
int x = 0;
int y = 0;
for (x = 0;x < 7; x)
{
for (y = 0;y < 7;y)
{
if (x == y || -x 9 == y || x - == y || -x == y)
{
printf(* );
}
else
{
printf( );
}
}
printf(\n);
}
system(pause);
return 0;
}
地址 指令的内容 指令
0040107 5F pop edi
0040108 5E pop esi
0040109 5B pop ebx
004010A 8 C4 40 add esp,40h
004010D B EC cmp ebp,esp
004010F E8 2C 0 00 00 call __chkesp (0040170)
00401044 8B E5 mov esp,ebp
00401046 5D pop ebp
if…else 可以控制命中率
switch…case 最好是大于个分支在用
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* ar[])
{
int n = ;
/*if(n == 1)
{
printf(n == 1\n);
}
else if (n == 2)
{
printf(n == 2\n);
}
else if (n == )
{
printf(n == \n);
}
else if (n == 4)
{
printf(n == 4\n);
}
*/
switch(n)
{
case 0:
printf(%d\n,n);
break;
case 1:
printf(%d\n,n);
break;
case 2:
printf(%d\n,n);
break;
case :
printf(%d\n,n);
break;
case 4:
printf(%d\n,n);
break;
}
system(pause);
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* ar[])
{
int n = 15000;
double nBitCount = 0.0;
int i = 1;
for (int i = 1; i <= n; i)
{
nBitCount = log10(i);
}
/*
DO_BEGI:
nBitCount = log10(i);
i;
if (i<=n)
{
goto DO_BEGI;
}*/
//ceil函数用于向上取整,即使不小于参数的最小整数,例如ceil(4.),ceil(4.7)也将返回5
printf(%d\n,(int)ceil(nBitCount));
system(pause);
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* ar[])
{
int nHp = 0;
srand( (unsigned)time( ULL ) );
while(nHp<100)
{
int nFood = rand()%100;
if (nFood<1000)
{
printf(吃了一口,体力恢复5\r\n);
nHp=5;
}
else if (nFood>=700 && nFood <850)
{
printf(吃了一口蔬菜,体力恢复10\r\n);
nHp=10;
}
else if (nFood>=850 && nFood <950)
{
printf(吃了一口肉,体力恢复20\r\n);
nHp=20;
}
else if (nFood>=950 && nFood <980)
{
printf(吃到头发不吃了\r\n);
continue;
}
else if (nFood>=980 && nFood <999)
{
printf(吃到蟑螂不吃了!\r\n);
break;
}
if (nFood>=1000)
{
printf(吃饱了,下次来\r\n);
}
else if (nHp<0)
{
printf(没吃饱\r\n);
}
else
{
printf(太恶心了 报警\r\n);
}
}
system(pause);
return 0;
}
判断是否是质数
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* ar[])
{
for (int n = 2;n<=100;n)
{
int isPrime = 1;
//sqrt是返回一个平方根
for (int i = 2; i < (int)sqrt(n)1;i)
{
if (n%i==0)
{
isPrime=0;
break;
}
}
if (isPrime)
{
printf(%d ,n);
}
}
system(pause);
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* ar[])
{
int n = 100;
if (n<1)
{
//error info
return -1;
}
printf(sum(%d) = %d \r\n,n,(1n)*(n/2));
system(pause);
return 0;
}
猴子吃桃
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* ar[])
{
int nCount = 1;
for (int i =0;i<9;i)
{
nCount=(nCount1)*2;
}
printf(%d\r\n,nCount);
system(pause);
return 0;
}
斐波那契数列(每一项都是前两项的和)
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int DiGuiFib(int n)
{
if (n==1||n==2)
{
return 1;
}
return DiGuiFib(n-2)DiGuiFib(n-1);
}
int main(int argc, char* ar[])
{
/* 斐波那契数列数组循环
int arr[50]={1,1};
for (int i = 2;i < 40;i)
{
arr[i]=arr[i-1]arr[i-2];
printf(%-15u%f\r\n,arr[i],(double)arr[i-1]/arr[i]);
}
*/
int nFib1 = 1;
int nFib2 = 1;
int nFib = 0;
for (int i = 2;i < 47;i)
{
nFib1=(double)DiGuiFib(i-1);
nFib2=DiGuiFib(i);
printf(%-15u%f\r\n,nFib2,(double)nFib1/nFib2);
}
system(pause);
return 0;
}
函数调用
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int DiGuiFib(int n)
{
int nFib1 = 1;
int nFib2 = 1;
int nFib = 0;
if (n==1||n==2)
{
return 1;
}
for (int i = 2;i<=n;i)
{
nFib=nFib1nFib2;
nFib1=nFib2;
nFib2=nFib;
}
return nFib;
}
int main(int argc, char* ar[])
{
unsigned int nFib1 = DiGuiFib(1);
unsigned int nFib2 = DiGuiFib(1);
for (int i = 2;i < 47;i)
{
nFib1=DiGuiFib(i);
printf(%-15u%f\r\n,nFib1,(double)nFib2/nFib1);
nFib2=nFib1;
}
system(pause);
return 0;
}
函数调用约定
描述的地方:函数和返回值之间,默认是cdecl调用约定
1、调用约定:调用方(caller)需要和被调方(callee)作出以下约定: 01 00 00 00 传参
1.参数的传递方向
2.参数的传输媒介(栈传参、寄存器传参)
.函数返回值的位置
4.释放参数空间的负责方,有且仅有一方去释放参数空间
__cdecl:参数使用栈空间传递,从右往左传递,函数返回值在寄存器,由调用方负责释放参数空间
__stdcall:参数使用栈空间传递,从右往左传递,函数返回值在寄存器,由被调用方负责释放参数空间
__fastcall:左数前两个参数使用寄存器传递,其他参数使用栈空间传递,从右往左传递,函数返回值在寄存器,由被调用方负责释放参数空间
2、保存返回地址:在函数调用时,需要将下一条指令的地址(即返回地址)保存在栈上,以便在函数执行结束后返回到调用方 19 15 40 00
、保存调用方的栈信息(栈底) 在开始执行被调用函数之前,需要保存调用方的栈信息,即栈底的地址 70 FF 19 00
4、更新当前栈到栈顶(把当前栈顶作为被调方的栈底) 将当前栈顶作为被调用方的栈底,以便在函数执行期间分配局部变量空间70 FF 19 00
5、为局部变量申请空间(抬高栈顶) 在栈上为函数的局部变量分配内存空间 CCCCCCCCCCCC …
6、保存寄存器环境(把即将使用的寄存器原值保存在栈里) 如果函数需要使用寄存器来存储临时变量或者函数参数,那么在函数调用前需要保存这些寄存器的原值
*7、如果编译选项有/ZI /Zi (带调试信息),则将局部变量初始化为如果编译选项中包含调试信息,则局部变量可能会被初始化为特定的值(例如 0xCCCCCCCC
)0xCCCCCCCC
8、执行函数体 执行函数的实际代码逻辑。
9、恢复寄存器环境 在函数执行结束后,需要将之前保存的寄存器值恢复到原始状态。
10、释放局部变量的空间 在函数执行结束后,需要释放为局部变量分配的内存空间。
11、恢复调用方的栈信息(栈底) 在函数执行结束后,需要将保存的调用方栈信息恢复,即将栈底的地址恢复到之前保存的值。
12、
12A、如果是__cdecl,取出当前栈顶作为返回的流程地址,返回到调用方后,由调用方清理参数空间
12B、 其他约定,取出当前栈顶作为返回的流程地址,同时由被调方清理参数空间后才返回
E 10 40 00 01 00 00 00 00 00 00 00 0 00 00 00 0 14 40 00 0 14 40 00 00 10 2E 00
CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC CC
70 FF 19 00 19 15 40 00 01 00 00 00 28 1C 85 00 B0 1C 85 00
01 00 00 00:命令行个数(字符型指针数组)
28 1C 85 00:命令行列表(字符型指针数组)
B0 1C 85 00:环境变量列表(整型)
19 15 40 00:main函数的返回地址
70 FF 19 00:main函数调用方的栈信息,同时也是栈底,访问局部变量以这个地址作减法访问,访问参数是加法
CCC…:申请的局部变量空间
0 14 40 00 0 14 40 00 00 10 2E 00:保存的寄存器空间
code | |||
---|---|---|---|
data | Inited | 常量区 | |
可读可写区 | |||
Uninit | |||
stack | |||
heap |
微软C/C编译器选项
-优化- | ||
---|---|---|
/O1 | 最小化空间 | minimize space |
/Op[-] | 改善浮点数一致性 | improve floating-pt cistency |
/O2 | 最大化速度 | maximize speed |
/Os | 优选代码空间 | favor code space |
/Oa | 假设没有别名 | assume no aliasing |
/Ot | 优选代码速度 | favor code speed |
/Ob | 内联展开(默认 n=0) | inline expansion (default n=0) |
/Ow | 假设交叉函数别名 | assume cross-function aliasing |
/Od | 禁用优化(默认值) | disable optimizati (default) |
/Ox | 最大化选项。(/Ogityb2 /Gs) | maximum opts. (/Ogityb1 /Gs) |
/Og | 启用全局优化 | enable global optimization |
/Oy[-] | 启用框架指针省略 | enable frame pointer omission |
/Oi | 启用内建函数 | enable intrinsic functi |
-代码生成- | ||
/G | 为 8086 进行优化 | optimize for 8086 |
/G4 | 为 80486 进行优化 | optimize for 80486 |
/GR[-] | 启用 C RTTI | enable C RTTI |
/G5 | 为 Pentium 进行优化 | optimize for Pentium |
/G6 | 为 Pentium Pro 进行优化 | optimize for Pentium Pro |
/GX[-] | 启用 C 异常处理(与 /EHsc 相同) | enable C EH (same as /EHsc) |
/EHs | 启用同步 C 异常处理 | enable synchronous C EH |
/GD | 为 Windows DLL 进行优化 | optimize for Windows DLL |
/GB | 为混合模型进行优化(默认) | optimize for blended model (default) |
/EHa | 启用异步 C 异常处理 | enable asynchronous C EH |
/Gd | __cdecl 调用约定 | __cdecl calling convention |
/EHc | extern“C”默认为 nothrow | extern “C” defaults to nothrow |
/Gr | __fastcall 调用约定 | __fastcall calling convention |
/Gi[-] | 启用增量编译 | enable incremental compilation |
/Gz | __stdcall 调用约定 | __stdcall calling convention |
/Gm[-] | 启用最小重新生成 | enable minimal rebuild |
/GA | 为 Windows 应用程序进行优化 | optimize for Windows Application |
/Gf | 启用字符串池 | enable string pooling |
/QIfdiv[-] | 启用 Pentium FDIV 修复 | enable Pentium FDIV fix |
/GF | 启用只读字符串池 | enable read-only string pooling |
/QI0f[-] | 启用 Pentium 0x0f 修复 | enable Pentium 0x0f fix |
/Gy | 分隔链接器函数 | separate functi for linker |
/GZ | 启用运行时调试检查 | enable runtime debug checks |
/Gh | 启用钩子函数调用 | enable hook function call |
/Ge | 对所有函数强制堆栈检查 | force stack checking for all funcs |
/Gs[num] | 禁用堆栈检查调用 | disable stack checking calls |
-输出文件- | ||
/Fa[file] | 命名程序集列表文件 | name assembly listing file |
/Fo | 命名对象文件 | name object file |
/FA[sc] | 配置程序集列表 | configure assembly listing |
/Fp | 命名预编译头文件 | name precompiled header file |
/Fd[file] | 命名 .PDB 文件 | name .PDB file |
/Fr[file] | 命名源浏览器文件 | name source browser file |
/Fe | 命名可执行文件 | name executable file |
/FR[file] | 命名扩展 .SBR 文件 | name extended .SBR file |
/Fm[file] | 命名映射文件 | name map file |
-预处理器- | ||
/FI | 命名强制包含文件 | name forced include file |
/C | 不吸取注释 | don’t strip comments |
/U | 移除预定义宏 | remove predefined macro |
/D{=|#} | 定义宏 | define macro |
/u | 移除所有预定义宏 | remove all predefined macros |
/E | 将预处理定向到标准输出 | preprocess to stdout |
/I | 添加到包含文件的搜索路径 | add to include search path |
/EP | 将预处理定向到标准输出,不要带行号 | preprocess to stdout, no #line |
/X | 忽略“标准位置” | ignore “standard places” |
/P | 预处理到文件 | preprocess to file |
-语言- | ||
/Zi | 启用调试信息 | enable debugging information |
/Zl | 忽略 .OBJ 中的默认库名 | omit default library name in .OBJ |
/ZI | 启用调试信息的“编辑并继续”功能 | enable Edit and Continue debug info |
/Zg | 生成函数原型 | generate function prototypes |
/Z7 | 启用旧式调试信息 | enable old-style debug info |
/Zs | 只进行语法检查 | syntax check only |
/Zd | 仅要行号调试信息 | line number debugging info only |
/vd{0|1} | 禁用/启用 vtordisp | disable/enable vtordisp |
/Zp[n] | 在 n 字节边界上包装结构 | pack structs on n-byte boundary |
/vm | 指向成员的指针类型 | type of pointers to members |
/Za | 禁用扩展(暗指 /Op) | disable extensi (implies /Op) |
/noBool | 禁用“bool”关键字 | disable “bool” keyword |
/Ze | 启用扩展(默认) | enable extensi (default) |
- 杂项 - | ||
/?, /help | 打印此帮助消息 | print this help message |
/c | 只编译,不链接 | compile only, no link |
/W | 设置警告等级(默认 n=1) | set warning level (default n=1) |
/H | 最大化外部名称长度 | max external name length |
/J | 默认 char 类型是 unsigned | default char type is unsigned |
/nologo | 取消显示版权消息 | suppress copyright message |
/WX | 将警告视为错误 | treat warnings as errors |
/Tc | 将文件编译为 .c | compile file as .c |
/Yc[file] | 创建 .PCH 文件 | create .PCH file |
/Tp | 将文件编译为 .cpp | compile file as .cpp |
/Yd | 将调试信息放在每个 .OBJ 中 | put debug info in every .OBJ |
/TC | 将所有文件编译为 .c | compile all files as .c |
/TP | 将所有文件编译为 .cpp | compile all files as .cpp |
/Yu[file] | 使用 .PCH 文件 | use .PCH file |
/V | 设置版本字符串 | set version string |
/YX[file] | 自动的 .PCH 文件 | automatic .PCH |
/w | 禁用所有警告 | disable all warnings |
/Zm | 最大内存分配(默认为 %) | max memory alloc (% of default) |
-链接- | ||
/MD | 与 MSVCRT.LIB 链接 | link with MSVCRT.LIB |
/MDd | 与 MSVCRTD.LIB 调试库链接 | link with MSVCRTD.LIB debug lib |
/ML | 与 LIBC.LIB 链接 | link with LIBC.LIB |
/MLd | 与 LIBCD.LIB 调试库链接 | link with LIBCD.LIB debug lib |
/MT | 与 LIBCMT.LIB 链接 | link with LIBCMT.LIB |
/MTd | 与 LIBCMTD.LIB 调试库链接 | link with LIBCMTD.LIB debug lib |
/LD | 创建 .DLL | Create .DLL |
/F | 设置堆栈大小 | set stack size |
/LDd | 创建 .DLL 调试库 | Create .DLL debug libary |
/link | [链接器选项和库] | [linker opti and libraries] |
经典题目:迷宫
0x00000000FD:栈溢出
project settings----link----Reserve(保留值,能用的空间)改为0x10000000 Commit(提交值,现在给你用的空间)改为0x01000000
#define _CRT_SECURE_O_WARIGS
#include StdAfx.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
#include <STDLIB.H>
/*
随堂作业:迷宫
$终点,1表示墙,0表示路
*/
void Show(char szMaze[][1],int nRow)
{
for (int i = 0;i<nRow;i)
{
puts(szMaze[i]);
}
return;
}
void Maze(char szMaze[][1],int x,int y)
{
system(cls);
Show(szMaze,8);
system(pause);
if (szMaze[x][y]== $ )
{
printf(过关!\r\n);
system(pause);
exit(0);
}
//走过的地方设标志
szMaze[x][y]= \x1 ;
if (szMaze[x1][y]== 0 ||szMaze[x1][y]== $ )
{
Maze(szMaze,x1,y);
}
if (szMaze[x][y1]== 0 ||szMaze[x][y1]== $ )
{
Maze(szMaze,x,y1);
}
if (szMaze[x][y-1]== 0 ||szMaze[x][y-1]== $ )
{
Maze(szMaze,x,y-1);
}
if (szMaze[x-1][y]== 0 ||szMaze[x-1][y]== $ )
{
Maze(szMaze,x-1,y);
}
//走过的地方设标志
szMaze[x][y]= \x2 ;
system(cls);
Show(szMaze,8);
system(pause);
return;
}
int main(int argc, char* ar[])
{
char szMaze[][1] = {
111111111111,
1010000001$1,
101011011101,
101000011101,
101011011101,
100011011101,
100011000001,
111111111111,
};
Show(szMaze,8);
Maze(szMaze,1,1);
return 0;
}
强内聚,低耦合
强内聚:指模块或组件内部各部分之间功能联系紧密,完成单一任务或目标的程度。在强内聚的模块中,相关功能应该彼此关联,并且一致地服务于某一目标。强内聚的模块通常具有更好的可维护性、可重用性和可理解性,因为其功能单一、清晰明确。
低耦合:指模块或组件之间的依赖关系较弱,彼此之间的联系较少、相对独立。低耦合的设计使得系统中的各个部分能够更灵活、更容易地独立修改、测试、重用和理解。通过降低模块之间的耦合性,可以减少对系统的修改造成的影响范围,并且提高系统的可扩展性和可维护性。
常见的就是:界面和逻辑分离、业务和算法分离
寻址公式:(int)arr sizeof(type)*n
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
void foo(int arr[5])
{
//n;
//arr;
/*int temp = 0;
printf(%d %d\r\n, x, y);
temp = x;
x = y;
y = temp;
建议不用
x = x y;
y = x - y;
x = x - y;
printf(%d %d\r\n, x, y);*/
int temp = arr[0];
arr[0] = arr[1];
arr[1] = temp;
}
int main() {
int arr[5] = { 1,2,,4,5 };
/*
连续且一致
第0个元素的地址常量
int n = .....;
type arr[M] = ....;
arr[n] address;
(int)arr sizeof(type)*n
*/
int n = 2;
//printf(%p\r\n, &arr[]); //4
//printf(%p\r\n, (int)arr sizeof(int) * );
//printf(%p\r\n, &arr[n]); //
//printf(%p\r\n, &arr[n - ]); //下标越界的地址
//printf(%p\r\n, &arr[n ]); //下标越界的地址
//printf(%p\r\n, &n[arr]); //
//printf(%p\r\n, arr[(0x00400000 - (int)arr) / sizeof(int)]);
//foo();
//foo(arr);
int m = 5;
foo(arr);
return 0;
}
寻址公式
//多维数组是特殊的一维数组
//维数组的元素是-1维数组
// (int)arr sizeof(type[][M]) * x sizeof(type) * y //&arr[x] //&arr[x][y]
// 等于下面的
// (int)arr sizeof(type[M]) * x sizeof(type) * y
// 推导
//(int)arr sizeof(type) * M * x sizeof(type) * y
//(int)arr sizeof(type)*(M*xy)
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
// 能适用所有的二维数组(整型)
void ShowArr(int arr[], int nMaxX, int nMaxY)
{
for (int i = 0; i < nMaxX; i)
{
for (int j = 0; j < nMaxY; j)
{
printf(%d\t, arr[nMaxY * i j]);
}
printf(\r\n);
}
}
// 常量阶
// 线性阶
// 指数阶
// 对数阶
int main() {
//多维数组是特殊的一维数组
//维数组的元素是-1维数组
// (int)arr sizeof(type[][M]) * x sizeof(type) * y //&arr[x] //&arr[x][y]
// 等于下面的
// (int)arr sizeof(type[M]) * x sizeof(type) * y
// 推导
//(int)arr sizeof(type) * M * x sizeof(type) * y
//(int)arr sizeof(type)*(M*xy)
int arr[][5] =
{
{1,2,,4,5},
{2,2,,4,5},
{,2,,4,5}
};
/*int arr[][4] =
{
{1,2,,4},
{2,2,,4},
{,2,,4}
};*/
ShowArr(&arr[0][0], , 5);
int x = 2;
int y = ;
printf(%p\r\n, &arr[x][y]);
printf(%p\r\n, (int)arr sizeof(int[5]) * x sizeof(int) * y);
printf(%p\r\n, (int)arr sizeof(int) * (5 * x y));
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
//自己实现的标准库函数strcpy
void MyStrCpy(char szBuf[], ct char szSrc[])
{
int i = 0;
while (szSrc[i] != \0 )
{
szBuf[i] = szSrc[i];
i;
}
szBuf[i] = \0 ;
//第二种写法,可读性太差
//while (szBuf[i] = szSrc[i]);
}
int main() {
char szBuf[20];
MyStrCpy(szBuf, hello,world);
printf(%s\n, szBuf);
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
#include <string.h>
int IsSplit(char ch)
{
char arrSplit[] = { , , . , , \n , - };
for (int i = 0; i < sizeof(arrSplit); i)
{
if (ch == arrSplit[i])
{
return 1;
}
}
return 0;
}
int main() {
char szBuf[] = I like C,and Java.ha-ha\nwhat a u like? ;
int isSplit = 1;
int isCount = 0;
size_t nLength = strlen(szBuf);
for (int i = 0; i < nLength; i)
{
if (!isSplit && IsSplit(szBuf[i]))
{
isSplit = 1;
}
//判断有几组单词
if (isSplit && !IsSplit(szBuf[i]))
{
isSplit = 0;
isCount;
}
}
printf(word count is : %d\r\n, isCount);
return 0;
}
作用域 | 生命期 | |
---|---|---|
函数 | 变量定义开始到函数结束 | 进入函数分配空间,退出函数释放空间 |
块(受约束的局部变量) | 变量定义开始到块结束 | 同函数 |
全局变量 | 工程作用域 |
编译期间检查 运行期间检查
#include <stdio.h>
#include MySnake.h
#include <stdlib.h>
void main()
{
//接收游戏的返回值
int nRet = 0;
nRet = StartGame();
//游戏正常退出(撞墙或咬自己了)
if (nRet == 2)
{
printf(很遗憾,游戏挑战失败!\r\n);
}
//游戏过关
else if (nRet == 1)
{
printf(恭喜,游戏挑战成功!\r\n);
}
//游戏出Bug
else if (nRet == -1)
{
printf(游戏维护中...\r\n);
}
system(pause);
}
MySnake.h
int StartGame();
#include MySnake.h
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>
#define ROW 8 //行
#define COL 16 //列
#define WALL 1 //墙
#define SAKE 2 //蛇
#define FOOD //食物
//贪吃蛇数组
char g_ArySnake[ROW][COL] = { 0 };
//蛇坐标数组
char g_ArySnakeRowAndCol[((ROW - 2) * (COL - 2) 1)][2] = { 0 };
//蛇的长度
int g_nSnakeLength = 0;
//蛇行坐标
int g_nSnakeRow = 0;
//蛇列坐标
int g_nSnakeCol = 0;
//食物行坐标
int g_nFoodRow = 0;
//食物列坐标
int g_nFoodCol = 0;
//初始化墙
int InitWall()
{
int i = 0, j = 0;
for (i = 0; i < ROW; i)
{
for (j = 0; j < COL; j)
{
if (((i == 0) || i == (ROW - 1)) || ((j == 0) || j == (COL - 1)))
{
g_ArySnake[i][j] = WALL;
}
}
}
return 0;
}
//显示UI
int ShowUI()
{
int i = 0, j = 0;
system(cls);
for (i = 0; i < ROW; i)
{
for (j = 0; j < COL; j)
{
if (g_ArySnake[i][j] == WALL)
{
printf(■);
}
else if (g_ArySnake[i][j] == SAKE)
{
printf(÷);
}
else if (g_ArySnake[i][j] == FOOD)
{
printf(⊙);
}
else
{
printf( );
}
}
printf(\r\n);
}
return 0;
}
//创建蛇
int CreateSnake()
{
g_nSnakeRow = (rand() % (ROW - 2)) 1;
g_nSnakeCol = (rand() % (COL - 2)) 1;
g_ArySnake[g_nSnakeRow][g_nSnakeCol] = SAKE;
//保存蛇头的坐标
g_ArySnakeRowAndCol[0][0] = g_nSnakeRow;
g_ArySnakeRowAndCol[0][1] = g_nSnakeCol;
//蛇的长度1
g_nSnakeLength;
return 0;
}
//创建食物
int CreateFood()
{
//创建食物不能产生在蛇身上
g_nFoodRow = (rand() % (ROW - 2)) 1;
g_nFoodCol = (rand() % (COL - 2)) 1;
g_ArySnake[g_nFoodRow][g_nFoodCol] = FOOD;
return 0;
}
//移动后的结果
int IsMove(int nSnakeRow, int nSnackCol)
{
if (g_ArySnake[nSnakeRow][nSnackCol] == WALL)
{
return WALL;
}
else if (g_ArySnake[nSnakeRow][nSnackCol] == SAKE)
{
return SAKE;
}
else if (g_ArySnake[nSnakeRow][nSnackCol] == FOOD)
{
return FOOD;
}
return 0;
}
//清除原蛇坐标
int ClearSnake()
{
int i = 0;
int nSnakeRow = 0;
int nSnakeCol = 0;
for (i = 0; i < g_nSnakeLength; i)
{
nSnakeRow = g_ArySnakeRowAndCol[i][0];
nSnakeCol = g_ArySnakeRowAndCol[i][1];
g_ArySnake[nSnakeRow][nSnakeCol] = 0;
}
return 0;
}
//向上移动
int MoveUp()
{
int nRet = 0;
int i = 0;
int nSnakeRow = 0;
int nSnakeCol = 0;
g_nSnakeRow--;
nRet = IsMove(g_nSnakeRow, g_nSnakeCol);
//撞墙了或者咬到自己了
if (nRet == WALL || nRet == SAKE)
{
return SAKE;
}
else if (nRet == FOOD)
{
//如果蛇的长度==最长长度,就返回过关
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
for (i = g_nSnakeLength; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nFoodRow;
g_ArySnakeRowAndCol[0][1] = g_nFoodCol;
g_nSnakeRow = g_nFoodRow;
g_nSnakeCol = g_nFoodCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
//蛇长大一节
g_nSnakeLength;
//重新创建食物
if (CreateFood() != 0)
{
return -1;
}
}
else
{
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
//更新蛇的坐标
for (i = g_nSnakeLength - 1; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nSnakeRow;
g_ArySnakeRowAndCol[0][1] = g_nSnakeCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
}
//更新大数组中蛇的坐标
for (i = 0; i < g_nSnakeLength; i)
{
nSnakeRow = g_ArySnakeRowAndCol[i][0];
nSnakeCol = g_ArySnakeRowAndCol[i][1];
g_ArySnake[nSnakeRow][nSnakeCol] = SAKE;
}
if (ShowUI() != 0)
{
return -1;
}
return 0;
}
//向下移动
int MoveDwon()
{
int nRet = 0;
int i = 0;
int nSnakeRow = 0;
int nSnakeCol = 0;
g_nSnakeRow;
nRet = IsMove(g_nSnakeRow, g_nSnakeCol);
//撞墙了或者咬到自己了
if (nRet == WALL || nRet == SAKE)
{
return SAKE;
}
else if (nRet == FOOD)
{
//如果蛇的长度==最长长度,就返回过关
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
for (i = g_nSnakeLength; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nFoodRow;
g_ArySnakeRowAndCol[0][1] = g_nFoodCol;
g_nSnakeRow = g_nFoodRow;
g_nSnakeCol = g_nFoodCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
//蛇长大一节
g_nSnakeLength;
//重新创建食物
if (CreateFood() != 0)
{
return -1;
}
}
else
{
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
//更新蛇的坐标
for (i = g_nSnakeLength - 1; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nSnakeRow;
g_ArySnakeRowAndCol[0][1] = g_nSnakeCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
}
//更新大数组中蛇的坐标
for (i = g_nSnakeLength - 1; i >= 0; i--)
{
nSnakeRow = g_ArySnakeRowAndCol[i][0];
nSnakeCol = g_ArySnakeRowAndCol[i][1];
g_ArySnake[nSnakeRow][nSnakeCol] = SAKE;
}
if (ShowUI() != 0)
{
return -1;
}
return 0;
}
//向左移动
int MoveLeft()
{
int nRet = 0;
int i = 0;
int nSnakeRow = 0;
int nSnakeCol = 0;
g_nSnakeCol--;
nRet = IsMove(g_nSnakeRow, g_nSnakeCol);
//撞墙了或者咬到自己了
if (nRet == WALL || nRet == SAKE)
{
return SAKE;
}
else if (nRet == FOOD)
{
//如果蛇的长度==最长长度,就返回过关
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
for (i = g_nSnakeLength; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nFoodRow;
g_ArySnakeRowAndCol[0][1] = g_nFoodCol;
g_nSnakeRow = g_nFoodRow;
g_nSnakeCol = g_nFoodCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
//蛇长大一节
g_nSnakeLength;
//重新创建食物
if (CreateFood() != 0)
{
return -1;
}
}
else
{
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
//更新蛇的坐标
for (i = g_nSnakeLength - 1; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nSnakeRow;
g_ArySnakeRowAndCol[0][1] = g_nSnakeCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
}
//更新大数组中蛇的坐标
for (i = 0; i < g_nSnakeLength; i)
{
nSnakeRow = g_ArySnakeRowAndCol[i][0];
nSnakeCol = g_ArySnakeRowAndCol[i][1];
g_ArySnake[nSnakeRow][nSnakeCol] = SAKE;
}
if (ShowUI() != 0)
{
return -1;
}
return 0;
}
//向右移动
int MoveRight()
{
int nRet = 0;
int i = 0;
int nSnakeRow = 0;
int nSnakeCol = 0;
g_nSnakeCol;
nRet = IsMove(g_nSnakeRow, g_nSnakeCol);
//撞墙了或者咬到自己了
if (nRet == WALL || nRet == SAKE)
{
return SAKE;
}
else if (nRet == FOOD)
{
//如果蛇的长度==最长长度,就返回过关
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
for (i = g_nSnakeLength; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nFoodRow;
g_ArySnakeRowAndCol[0][1] = g_nFoodCol;
g_nSnakeRow = g_nFoodRow;
g_nSnakeCol = g_nFoodCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
//蛇长大一节
g_nSnakeLength;
//重新创建食物
if (CreateFood() != 0)
{
return -1;
}
}
else
{
//清除大数组中原蛇的坐标
if (ClearSnake() != 0)
{
return -1;
}
//更新蛇的坐标
for (i = g_nSnakeLength - 1; i >= 0; i--)
{
if (i == 0)
{
g_ArySnakeRowAndCol[0][0] = g_nSnakeRow;
g_ArySnakeRowAndCol[0][1] = g_nSnakeCol;
}
else
{
g_ArySnakeRowAndCol[i][0] = g_ArySnakeRowAndCol[i - 1][0];
g_ArySnakeRowAndCol[i][1] = g_ArySnakeRowAndCol[i - 1][1];
}
}
}
//更新大数组中蛇的坐标
for (i = 0; i < g_nSnakeLength; i)
{
nSnakeRow = g_ArySnakeRowAndCol[i][0];
nSnakeCol = g_ArySnakeRowAndCol[i][1];
g_ArySnake[nSnakeRow][nSnakeCol] = SAKE;
}
if (ShowUI() != 0)
{
return -1;
}
return 0;
}
//游戏引擎文件
int StartGame()
{
srand((unsigned)time(ULL));
char szKey = \0 ;
int nRet = 0;
//初始化墙
if (InitWall() != 0)
{
return -1;
}
//创建蛇
if (CreateSnake() != 0)
{
return -1;
}
//创建食物
if (CreateFood() != 0)
{
return -1;
}
//显示UI
if (ShowUI() != 0)
{
return -1;
}
//方向控制
while (1)
{
szKey = _getch();
switch (szKey)
{
case W :
case w :
{
nRet = MoveUp();
if (nRet != 0)
{
return nRet;
}
break;
}
case S :
case s :
{
{
nRet = MoveDwon();
if (nRet != 0)
{
return nRet;
}
break;
}
break;
}
case A :
case a :
{
{
nRet = MoveLeft();
if (nRet != 0)
{
return nRet;
}
break;
}
break;
}
case D :
case d :
{
{
nRet = MoveRight();
if (nRet != 0)
{
return nRet;
}
break;
}
break;
}
}
}
return 0;
}
type* ptr = ....;
int n = .....;
prt n = (int)ptr sizeof(type)*n 运算结果为type * ct同类指针常量
= (type * ct)((int)ptr sizeof(type)*n)
数组名是数组第0个元素的指针常量
type ary[M] = ....;
ptr = ary;
ptr[n] = *(type * ct)((int)ptr sizeof(type)*n)
int _tmain() {
int a = 8;
int* pa = ULL;
pa = &a;
*pa = 999;
int ary[5] = { 1,2,,4,5 };
int* pAry = ULL;
pAry = ary;
int n = 2;
printf(%d\r\n, pAry[2]);
printf(%d\r\n, ary[2]);
printf(%d\r\n, *(pAry n));
_tsystem(_T(pause));
return 0;
}
int _tmain() {
int ary[5] = { 1,2,,4,5 };
int* pAry = ULL;
int n = 2;
int* pn = ULL;
pn = &n;
pAry = ary;
//这里pAry是pAry[0]的首地址,pn的地址-pAry[0]地址的差值除以数据类型的大小sizeof(type)
printf(%d\r\n, pn - pAry);
_tsystem(_T(pause));
return 0;
}
//没有解释方式,只有地址
void* pv = ULL;
memcpy(目标,原,大小)
int _tmain() {
int ary[5] = { 1,2,,4,5 };
char aryDest[5 * sizeof(int)];
int* pAry = ULL;
int n = 2;
int* pn = ULL;
pn = &n;
pAry = ary;
float flt = .14f;
printf(%f, flt);
//没有解释方式,只有地址
void* pv = ULL;
/*
块操作
memcpy memcmp memstr memchr
*/
_tsystem(_T(pause));
return 0;
}
void myswap(int* pn1, int* pn2)
{
int nTmp = *pn1;
*pn1 = *pn2;
*pn2 = nTmp;
}
int _tmain() {
int n1 = 8;
int n2 = 10;
printf(%d %d\r\n, n1, n2);
myswap(&n1, &n2);
printf(%d %d\r\n, n1, n2);
_tsystem(_T(pause));
return 0;
}
int* max(int* x, int* y)
{
int* q;
if (*x > *y)
{
q = x;
}
else
{
q = y;
}
return q;
}
int _tmain() {
int a, b, * p;
scanf(%d %d, &a, &b);
p = max(&a, &b);
printf(%d %d,max is%d\r\n, a, b, *p);
_tsystem(_T(pause));
return 0;
}
typedef 起别名,定义一个新类型,增加可读性、可移植性
函数的类型
1,参数序列(包括参数个数,类型,顺序)
2,调用约定
,返回值类型
void (_cdecl*pfnSort)(int [], int);
void SortA(int arr[], int nCount)
{
puts(冒泡);
}
void SortB(int arr[], int nCount)
{
puts(选择);
}
//typedef 起别名,定义一个新类型,增加可读性、可移植性
typedef void(_cdecl* PFSORT)(int[], int);
int _tmain() {
int arr[54];
//void(_cdecl * pfnSort)(int[], int) = ULL;
PFSORT pfnSort = ULL;
pfnSort = SortB;
pfnSort(arr, 54);
_tsystem(_T(pause));
return 0;
}
void SortA(int arr[], int nCount)
{
puts(冒泡);
}
void SortB(int arr[], int nCount)
{
puts(选择);
}
//typedef 起别名,定义一个新类型,增加可读性、可移植性
typedef void(_cdecl* PFSORT)(int[], int);
void PlayCard(int arr[], int nCount, PFSORT pfnSort)
{
//洗牌
//发牌
//排序
pfnSort(arr, nCount);
//叫地主
//抢地主
}
int _tmain() {
int arr[54];
//算法和业务逻辑的分离
PlayCard(arr, 54, SortA);
PlayCard(arr, 54, SortB);
_tsystem(_T(pause));
return 0;
}
void FunA()
{
puts(进货);
}
void FunB()
{
puts(库存);
}
void FunC()
{
puts(加工);
}
void FunD()
{
puts(销售);
}
int _tmain() {
//函数指针数组
void(*pfnArr[])() = {
FunA,FunB,FunC,FunD
};
//循环遍历出四个void
for (int i = 0; i < sizeof(pfnArr) / sizeof(pfnArr[0]); i)
{
pfnArr[i]();
}
_tsystem(_T(pause));
return 0;
}
*和&
void GetValue(int *nOut)
{
*nOut=8;
}
int g_nTest = 999;
//一级使用指针
void GetPoint1(int *pnOut)
{
pnOut=&g_nTest;
}
//二级使用指针
void GetPoint2(int **pnOut)
{
*pnOut=&g_nTest;
}
int _tmain(int argc,char *ar[],char* envp[]) {
char* arrPoint[] = {
Hello,
Wolrd,
C
};
//数组名是第0个元素的指针常量
//char* arrPoint是一级指针需要二级指针接收
char** ppab = arrPoint;
printf(%s\r\n,arrPoint[0]);
printf(%c\r\n,arrPoint[0][0]);
printf(%c\r\n,**arrPoint);
int arr[][5] = {
{1,2,,4,5},
{10,20,0,40,50},
{100,200,00,400,500}
};
int n = 6;
GetValue(&n);
int* pn = &n;
//一级使用指针
GetPoint1(pn);
printf(%p\r\n,*pn);
//二级使用指针
GetPoint2(&pn);
printf(%p\r\n,*pn);
int** ppn = &pn;
printf(%p\r\n, ppn);
printf(%p\r\n, *ppn);
printf(%p\r\n, **ppn);
printf(%p\r\n, ppn[0]);
printf(%p\r\n, ppn[0][0]);
//数组指针类型吻合
int(*p)[5] = arr;
int(*pa)[][5] = &arr;
printf(%p\r\n, p);
printf(%p\r\n, *p);
printf(%p\r\n, pa);
printf(%p\r\n, *pa);
printf(%p\r\n, **pa);
printf(%p\r\n, p 1);
printf(%p\r\n, *p 1);
printf(%p\r\n, pa 1);
printf(%p\r\n, *pa 1);
printf(%p\r\n, **pa 1);
//数组名是第0个元素的指针常量
//二位数组的元素是一堆数组
//arr[][4]的元素是int[4]
//arr是int[4]类型的指针常量
printf(%p\r\n, arr);
//*arr得到int[4]一维数组
//*arr是int类型的指针常量
printf(%p\r\n, *arr);
//任何类型的变量取地址得到该类型的指针
//&arr取地址得到int[][4]类型的指针
printf(%p\r\n, &arr);
//arr是int[4]类型的指针常量
//arr 1 <==> (int)arr sizeof(int[4])*1
printf(%p\r\n, arr);
//*arr是int类型的指针常量
//*arr 1 <==> (int)*arr sizeof(int[4])*1
printf(%p\r\n, *arr 1);
//&arr取地址得到int[][4]类型的指针
//*arr 1 <==> (int)&arr sizeof(int[][4])*1
printf(%p\r\n, &arr 1);
//二位数组的元素是一堆数组
//arr[0]是int[4]类型的指针常量
//arr[0]是int类型的指针常量
//arr 1 <==> (int)arr[0] sizeof(int[4])*1
printf(%p\r\n, arr[0] 1);
//指针加整型得到同类型的指针常量
//arr是int[4]类型的指针常量
//arr 1 得到int[4]类型的指针常量
//*(arr1)得到一维数组int[4]
//数组名是第0个元素的指针常量
//*(arr1)是int类型的指针常量
//对int*做[1]运算得到int
printf(%p\r\n, (*(arr 1))[1]);
_tsystem(_T(pause));
return 0;
}
/*
设编译对齐值为 Zp
设结构体成员的地址和结构体首地址之差为offset
每个结构体成员的偏移量offset
设结构体成员类型为member type
必须满足:
offset % min (Zp,sizeof(member type)) == 0
定义结构体自身的对齐值为StructAlig
StructAlig = max(sizeof(member1 type),sizeof(member2 type)...sizeof(member type))
设整个结构体的空间长度为size
必须满足:
StructAlig = min(Zp,StructAlig)
*/
// /Zp2
struct tagDOB
{
int nYear;
char cMonth;
short int wDay;
};
//保存当前对齐值
#pragma pack(push)
#pragma pack(1)
struct tagStudent
{
struct tagDOB dob;
char szame[5]; //5->8
int nAge; //4
float fHeight; //4
double dblWeight; //8
unsigned short int wID; //2
char nGender; //上面的2和1补到8
//sizeof() == 2
};
//恢复对齐值
#pragma pack(pop)
/*
struct tagType obj;
address is:
(member type*)((int)&obj member offset)
= *(member type*)( address)
*/
int _tmain(int argc, char* ar[], char* envp[]) {
struct tagStudent stu = {
{
1999,
9,
9
},
Jack,
25,
185.0f,
80.0,
9527,
m
};
printf(%d\r\n, sizeof(stu));
printf(%s\r\n, stu.szame);
struct tagStudent* pStu = ULL;
pStu = &stu;
printf(%d\r\n, &*(double*)((int)pStu 8));
//想取出偏移量,又不想取出结构体变量
#define GetOffset(s,m) (size_t)(((s*)ULL)->m)
//printf(%d\r\n, GetOffset(tagStudent, dblWeight));
printf(%d\r\n, &pStu->dblWeight);
printf(%d\r\n, &pStu->dob.nYear);
//printf(%d\r\n, pStu->);
_tsystem(_T(pause));
return 0;
}
//enum eType test = TYPE_CHAR or TYPE_FLOAT or TYPE_TEXT;
//枚举变量,变量只能赋值为对应枚举类型中的枚举常量值之一
//enum eType test = TYPE_CHAR;
struct unScore2
{
char szText[8];
};
//变体的前身
struct tagScore
{
//枚举类型
enum
{
TYPE_CHAR,
//枚举常量
TYPE_FLOAT,
TYPE_TEXT
}nType;
union unScore
{
char chLevel;
float fPoint;
char szText[8];
}score;
//union unScore tScore;
};
void SetScore(struct tagScore* pScore, char chLevel)
{
pScore->score.chLevel = chLevel;
pScore->nType = tagScore::TYPE_CHAR;
}
void SetScore(struct tagScore* pScore, float chPoint)
{
pScore->score.fPoint = chPoint;
pScore->nType = tagScore::TYPE_FLOAT;
}
void SetScore(struct tagScore* pScore, ct char* chText)
{
strcpy(pScore->score.szText, chText);
pScore->nType = tagScore::TYPE_TEXT;
}
void GetScore(struct tagScore* pScore)
{
switch (pScore->nType)
{
case tagScore::TYPE_CHAR:
printf(%c\r\n, pScore->score.chLevel);
break;
case tagScore::TYPE_FLOAT:
printf(%f\r\n, pScore->score.fPoint);
break;
case tagScore::TYPE_TEXT:
printf(%s\r\n, pScore->score.szText);
break;
default:
printf(error\r\n);
}
}
int _tmain(int argc, char* ar[], char* envp[]) {
argc = tagScore::TYPE_TEXT;
//改善语法的可读性
//union联合结构体占用内存小
/*union unScore usc1;
printf(%d\r\n, sizeof(usc1));
struct unScore2 usc2;
printf(%d\r\n, sizeof(usc2));
= a ;
*(char*)usc2.szText = a ;
*
usc1.fPoint = .14f;
*(char*)usc2.szText = .14f;
strcpy(usc1.szText, hello);
strcpy((char*)usc2.szText, hello);*/
tagScore sc1;
SetScore(&sc1, A );
GetScore(&sc1);
tagScore sc2;
SetScore(&sc2, 56.9f);
GetScore(&sc2);
tagScore sc;
SetScore(&sc, GOOD);
GetScore(&sc);
_tsystem(_T(pause));
return 0;
}
#ifdef _DEBUG
#define malloc(n) _malloc_dbg(n, _ORMAL_BLOCK, __FILE__, __LIE__)
#endif
int _tmain(int argc, char** ar, char* envp[]) {
int* p = (int*)malloc(4);
*p = 999;
char* psz = (char*)malloc(20);
strcpy(psz, hello);
char* psz2 = (char*)realloc(p, 1);
*psz2 = ( a );
p = (int*)realloc(psz2, 8);
p[0] = 0x6666;
p[1] = 0x8888;
free(p);
free(psz2);
_tsystem(_T(pause));
return 0;
}
#ifdef _DEBUG
#define malloc(n) _malloc_dbg(n, _ORMAL_BLOCK, __FILE__, __LIE__)
#endif
void ErrorProc() {}
int _tmain(int argc, char **ar, char *envp[]) {
int *pA = (int *)malloc(sizeof(int));
if (pA == ULL) {
ErrorProc();
return -1;
}
*pA = 999;
char *pb1;
float *pb2;
if (argc % 2 == 0) {
pb1 = (char *)malloc(strlen(Hello) sizeof(char));
if (pb1 == ULL) {
free(pA);
ErrorProc();
return -1;
}
strcpy(pb1, Hello);
} else {
pb2 = (float *)malloc(strlen(Hello) sizeof(float));
if (pb2 == ULL) {
free(pA);
ErrorProc();
return -1;
}
*pb2 = .14f;
}
double *pC = (double *)malloc(sizeof(double));
if (pC == ULL) {
free(pA);
ErrorProc();
return -1;
}
*pC = 0.618;
_tsystem(_T(pause));
return 0;
}
#ifdef _DEBUG
#define malloc(n) _malloc_dbg(n, _ORMAL_BLOCK, __FILE__, __LIE__)
#endif
void ErrorProc() {}
int _tmain(int argc, char **ar, char *envp[]) {
// 多资源使用规范
// 1.引用资源的变量在作用域开始定义,并初始化为错误值
int *pA = ULL;
char *pB1 = ULL;
float *pB2 = ULL;
double *pB = ULL;
// 2.申请资源后,必须检查是否资源有效,无效则处理错误
pA = (int *)malloc(sizeof(int));
if (pA == ULL) {
ErrorProc();
// .处理完错误后,转移到统一退出位置
goto EXIT_PROC;
}
*pA = 999;
if (argc > ) {
pB1 = (char *)malloc(strlen(hello) sizeof(char));
if (pB1 == nullptr) {
ErrorProc();
goto EXIT_PROC;
}
} else {
pB2 = (float *)malloc(sizeof(float));
if (pB2 == nullptr) {
ErrorProc();
goto EXIT_PROC;
}
*pB2 = .14f;
}
pB = (double *)malloc(sizeof(double));
if (pB == nullptr) {
ErrorProc();
goto EXIT_PROC;
}
*pB = 0.618;
_tsystem(_T(pause));
EXIT_PROC:
#define SAFE_FREE(p) \
if (p) { \
free(p); \
(p) = ULL; \
}
SAFE_FREE(pB1);
SAFE_FREE(pB2);
SAFE_FREE(pB);
// 4.释放资源前,必须检查资源是否有效,无效则不处理
if (pA != ULL) {
free(pA);
// 5.释放资源后,必须将引用资源的变量重置为错误值
// pA = ULL;
}
return 0;
}
A and 1 = A
A or 0 = A
A and 0 = 0
A or 1 = 1
A xor A = 0
A xor 0 = A
A xor 1 = not A
A and not A = 0
A or not A = 1
#ifdef _DEBUG
#define malloc(n) _malloc_dbg(n, _ORMAL_BLOCK, __FILE__, __LIE__)
#endif
int myabs(int n) {
// 具体数学
// if n >= 0, i = 0; else i = 0xffffffff = -1
int i = n >> 1;
// if i = 0, n = n; else i = 0xffffffff, n = ~n
n = n ^ i;
//if i = 0, n - i = n; else i = -1, n - i = n 1
return n - i;
}
int _tmain(int argc, char** ar, char* envp[]) {
int n = myabs(-5);
_tsystem(_T(pause));
return 0;
}
文件访问 模式字符串 | 意义 | 解释 | 如果文件 已存在,则执行操作 | 如果文件 不存在,则执行操作 |
---|---|---|---|---|
“r” | 读 | 打开文件进行读取 | 从头开始阅读 | 打不开 |
“w” | 写 | 创建用于写入的文件 | 销毁内容 | 新建 |
“a” | 附加 | 追加到文件 | 写到结束 | 新建 |
“r” | 阅读扩展 | 打开文件进行读/写 | 从头开始阅读 | 错误 |
“w” | 写扩展 | 创建用于读/写的文件 | 销毁内容 | 新建 |
“a” | 追加扩展 | 打开文件进行读/写 | 写到结束 | 新建 |
文件操作
打开/创建 fopen
文件指针 ftell(查看当前指针在哪) fseek(将当前指针移动到文件中的指定位置) feof(检查文件结束符)
关闭fclose
显示错误的原因 perror
文本方式
fputc
fputs
fgetc
fgets
二进制方式
读取数据 fread
写入数据 fwrite
写入
/*----保存数据----*/
//二进制打开
FILE *fp = fopen(D:\\, wb);
if (fp == ULL) {
perror(fopen\r\n);
return 0;
}
printf(open file ok fp:%p\r\n, fp);
//写入数据
size_t size = fwrite(player_name, 1, sizeof(player_name), fp);
if (size <= 0) {
perror(fwrite\r\n);
return 0;
} else {
printf(write player_name ok size:%d\r\n, size);
}
//变量要取地址
//移动文件指针
if (fseek(fp, 16, SEEK_SET) != 0) {
perror(fseek\r\n);
fclose(fp);
return 0;
}
size = fwrite(&player_lv, 1, sizeof(player_lv), fp);
if (size <= 0) {
perror(fwrite\r\n);
return 0;
} else {
printf(write player_lv ok size:%d\r\n, size);
}
size = fwrite(&player_money, 1, sizeof(player_money), fp);
if (size <= 0) {
perror(fwrite\r\n);
return 0;
} else {
printf(write player_money ok size:%d\r\n, size);
}
fclose(fp);
读取
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
int main() {
char player_name[2] = { 没穿裤子 };
int player_lv = 99;
float player_money = 5.5f;
// 文件读取
// 二进制打开
FILE* fp = fopen(D:\\, rb);
if (fp == ULL) {
perror(Failed to open file);
return 1;
}
size_t size = fread(player_name, 1, sizeof(player_name), fp);
if (size != sizeof(player_name)) {
perror(Failed to read player_name);
fclose(fp);
return 1;
}
printf(fread player_name ok size:%zu\r\n, size);
printf(player_name: %s\r\n, player_name);
//移动文件指针
if (fseek(fp, 16, SEEK_SET) != 0) {
perror(fseek\r\n);
fclose(fp);
return 0;
}
size = fread(&player_lv, 1, sizeof(player_lv), fp);
if (size != sizeof(player_lv)) {
perror(Failed to read player_lv);
fclose(fp);
return 1;
}
printf(fread player_lv ok size:%zu\r\n, size);
printf(player_lv: %d\r\n, player_lv);
size = fread(&player_money, 1, sizeof(player_money), fp);
if (size != sizeof(player_money)) {
perror(Failed to read player_money);
fclose(fp);
return 1;
}
printf(fread player_money ok size:%zu\r\n, size);
printf(player_money: %.2f\r\n, player_money);
fclose(fp);
return 0;
}
文本方式写入
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
int main() {
char player_name[2] = { 没穿裤子 };
int player_lv = 99;
float player_money = 5.5f;
//文本方式写入
FILE* fp = fopen(D:\\, w);
fprintf(fp, %s\r\n%d\r\n%.2f, player_name, player_lv, player_money);
fclose(fp);
return 0;
}
#define _CRT_SECURE_O_WARIGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char player_name[2] = {没穿裤子};
int player_lv = 99;
float player_money = 5.5f;
//字符串分割
/*char str[] = { hello12world12test };
char str2[200];
strcpy(str2, str);
char* token = strtok(str, 12);
while (token != ULL)
{
printf(%s\n, token);
token = strtok(ULL, 12);
}
puts(str);*/
////文本方式写入
FILE *fp = fopen(D:\\, w);
fprintf(fp, player_name:%s\r\n player_lv:%d\r\n player_money%.2f\r\n, player_name, player_lv, player_money);
fclose(fp);
//文本方式读取
char player_name2[2] = {0};
int player_lv2 = 0;
float player_money2 = 0;
FILE *fp2 = fopen(D:\\, rt);
//fscanf(fp, player_name2:%s\r\n player_lv2:%d\r\n player_money2%.2f\r\n, player_name2, &player_lv2, &player_money2);
char buf[260] = {0};
while (!feof(fp2)) {
//fgets(buf, sizeof(buf), fp2);
//sscanf(buf, player_name:%s, player_name2);
fscanf(fp2, player_name:%s\n, player_name2);
fscanf(fp2, player_lv:%d\n, &player_lv2);
fscanf(fp2, player_money:%f\n, &player_money2);
}
fclose(fp2);
return 0;
}
C完全兼容C语言的语法
二进制 >> 汇编 >> 高级语言 C >> 面向对象(C、java、C#)
#感谢您对电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格的认可,转载请说明来源于"电脑配置推荐网 - 最新i3 i5 i7组装电脑配置单推荐报价格
推荐阅读
留言与评论(共有 5 条评论) |
本站网友 二月初二龙抬头 | 29分钟前 发表 |
5 }; int* pAry = ULL; int n = 2; int* pn = ULL; pn = &n; pAry = ary; //这里pAry是pAry[0]的首地址,pn的地址-pAry[0]地址的差值除以数据类型的大小sizeof(type) printf(%d\r\n | |
本站网友 美瞳颜色 | 30分钟前 发表 |
\\ | |
本站网友 宁波同仁医院 | 12分钟前 发表 |
%x\n | |
本站网友 电梯事件 | 15分钟前 发表 |
b); } }; int main() { MyPrint(); return 0; } #define _CRT_SECURE_O_WARIGS #include <iostream> struct stStudent { int Age; char name[0x20]; }; struct stStudent arr[]; void init() { arr[0].Age = 20; printf(%s |