admin 管理员组文章数量: 887021
C开始的学习之路
记不住的一些函数
#define _CRT_SECURE_NO_WARNINGS
关闭不安全函数的提示
#字符串和字符串函数
strncat(字符串,字符串,长度)
strncat()拼接字符串的函数
strcmp(用户响应的, 已存储的)
strcmp()把用户响应与已存储的字符串作比较
char str1[] = "123";
char str2[] = "1234";
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(const char*s,int c)
如果s字符串中包含c字符,该函数返回指向,s字符串首位置的指针,如果未找到,则返回空指针
char* strpbrk(const char*s1,const char*s2)
如果s1字符中包含s1字符串中的任意字符,函数返回指向s1字符首位置的指针,如果未找到,则返回空字符
toupper()
处理字符串中的每个字符,把整个字符串转换成大写
memset(数组名,要清理内存使用的数据,清理的字节数)
清空字符串 字符串初始化函数
char str1[10] = "china";
memset(str1, 'a', 10);
strlen()求字符串长度
fclose()关闭指定的文件,必要时刷新缓冲区
变量的作用域和生命周期
作用域:变量的作用范围(在何处能够访问到变量)
全局变量:定义在所有函数之外的变量,定义之后都可以访问,而且数据共享(内存只有一块),从定义开始到程序执行结束
局部变量:在函数或者代码块里面定义的变量,从定义开始到函数或者代码块结束
#define _CRT_SECURE_NO_WARNINGS
#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 表示外部变量 这个一般用在多文件中 申明使用外部变量
Test2.cpp
int test_a = 50;
Test1.cpp
extern int test_a;
int main(void)
{
printf("%d", test_a);
return 0;
}
动态内存分配
动态内存是相对静态内存而言的,所谓动态和静态就是指内存的分配方式,动态内存是指在堆上分配的内存,而静态内存是指在栈上分配的内存
静态内存:比如局部变量、形参等
动态内存分配的意义
1、C语言中一切操作都是基于内存的
2、变量和数组都是内存的别名
3、定义数组时必须指定数组的大小,使用动态分配可以在运行时调整大小
4、函数结束之后,不希望变量的内存被释放
malloc
#define _CRT_SECURE_NO_WARNINGS
#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 == NULL)
{
printf("no\n");
return -1;
}
*pn = 66;
printf("%d", *pn);
//释放,要设置他为NULL,以免后面不小心还调用
pn = NULL;
free(pn);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(void)
{
int* parr = (int*)malloc(sizeof(int) * 5);
if (!parr)//!parr==NULL
{
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 = NULL;
return 0;
}
结构体
结构体数组
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
struct stStudent
{
int age;
int level;
};
//代表有5个stStudent结构体的数组
struct stStudent arr[5] = { {0,0},{1,1},{2,2},{3,3},{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_NO_WARNINGS
#include <iostream>
struct stStudent
{
int Age;
char name[0x20];
};
struct stStudent arr[3];
void init()
{
arr[0].Age = 20;
printf("%s", strcpy(arr[0].name, "china"));
}
int main()
{
init();
return 0;
}
数组指针
int main(int argc, char* argv[])
{
int arr[]={1,2,3,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* argv[])
{
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; //0003
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
0x3c DWORD e_lfanew; //000000F8 *PE头相对于文件的偏移,用于定位PE文件
PE头标志(4): //00005045
NT头 struct _IMAGE_NT_HEADERS 0x3c DWORD e_lfanew;
↓
标准PE头 struct _IMAGE_FILE_HEADER(20)
0x00 WORD Machine; //014C *程序运行的CPU型号:0x0任何处理器//0x14C 386及后续处理器
0x02 WORD NumberOfSections; //0004 *文件中存在的节的总数,如果要新增节或者合并节 就要修改这个值
0x04 DWORD TimeDateStamp; //4DCEB17D *时间戳:文件的创建时间(和操作系统的创建时间无关),编译器填写的.
0x08 DWORD PointerToSymbolTable; //00000000
0x0c DWORD NumberOfSymbols; //00000000
0x10 WORD SizeOfOptionalHeader; //00E0 *可选PE头的大小,32位PE文件默认E0h(16*14) 64位PE文件默认为F0h 大小可以自定义.
0x12 WORD Characteristics; //010F *每个位有不同的含义,可执行文件值为10F 即0 1 2 3 8位置1(特征勾选重定位信息、文件可执行、行号符号移去和32位机器是010F)
( 用二进制从右至左看
数据位 常量符号 为1时的定义
0 IMAGE_FILE_RELOCS_STRIPPED 文件中不存在重定位信息
1 IMAGE_FILE_EXECUTABLE_IMAGE 文件是可执行的
2 IMAGE_FILE_LINE_NUMS_STRIPPED 不存在行信息
3 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_32BIT_MACHINE 只在 32 位平台上运行
9 IMAGE_FILE_DEBUG_STRIPPED 不但含调试信息
10 IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 不能从可移动盘运行
11 IMAGE_FILE_NET_RUN_FROM_SWAP 不能从网络运行
12 IMAGE_FILE_SYSTEM 系统文件(如驱动程序),不能直接运行
13 IMAGE_FILE_DLL 这是一个 DLL 文件
14 IMAGE_FILE_UP_SYSTEM_ONLY 文件不能在多处理器计算机上运行
15 IMAGE_FILE_BYTES_REVERSED_HI 大尾方式
)
可选PE头 struct _IMAGE_OPTIONAL_HEADER(224)
0x00 WORD Magic; //010B *说明文件类型:10B 32位下的PE文件 20B 64位下的PE文件
0x02 BYTE MajorLinkerVersion; //06
0x03 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;
0x30 WORD MajorSubsystemVersion;
0x32 WORD MinorSubsystemVersion;
0x34 DWORD Win32VersionValue;
0x38 DWORD SizeOfImage; //0006E000 *内存中整个PE文件的映射的尺寸,可以比实际的值大,但必须是SectionAlignment的整数倍
0x3c 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 NumberOfRvaAndSizes; //00000010
0x60 _IMAGE_DATA_DIRECTORY DataDirectory[16];
节表 _IMAGE_SECTION_HEADER(在标准PE头NumberOfSections查看节的总数)
(.text节)(.data节)(.rsrc节)(.reloc节)(...)
0x00 BYTE Name[IMAGE_SIZEOF_SHORT_NAME]; //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 PointerToRelocations;
0x1c DWORD PointerToLinenumbers;
0x20 WORD NumberOfRelocations;
0x22 WORD NumberOfLinenumbers;
0x24 DWORD Characteristics; *节的属性(可读、可写、可执行)
读取PE头
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define filepath "C:\\Users\\Administrator\\Desktop\\ipmsg.exe"
LPVOID ReadPEFile(LPCSTR lpszFile);
VOID PrintNTHeaders();
int main(int argc, char* argv[])
{
PrintNTHeaders();
return 0;
}
LPVOID ReadPEFile(LPCSTR lpszFile)
{
FILE* pFile = NULL;
DWORD fileSize = 0;
LPVOID pFileBuffer = NULL;
//打开文件
pFile = fopen(lpszFile, "rb+");
if (!pFile)
{
printf("无法打开文件!\n");
return NULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//开辟内存空间、分配缓冲区
pFileBuffer = malloc(fileSize);
if (!pFileBuffer)
{
printf("分配空间失败!\n");
fclose(pFile);
return NULL;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf("读取数据失败!\n");
free(pFileBuffer);
fclose(pFile);
return NULL;
}
//关闭文件
fclose(pFile);
return pFileBuffer;
}
VOID PrintNTHeaders()
{
LPVOID pFileBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOpHeader = NULL;
PIMAGE_SECTION_HEADER pSeHeader = NULL;
pFileBuffer = ReadPEFile(filepath);
if (!pFileBuffer)
{
printf("文件读取失败!\n");
return;
}
//判断是否是有效的MZ标志
if ((*(PWORD)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
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);
//打印NT头
printf("**********NT头**********\n");
pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
printf("NT头:%x\n", pNtHeaders->Signature);
//打印标准PE头
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders+4);
printf("Machine:%x\n", pFileHeader->Machine);
printf("NumberOfSections:%x\n", pFileHeader->NumberOfSections);
printf("TimeDateStamp:%x\n", pFileHeader->TimeDateStamp);
printf("PointerToSymbolTable:%x\n", pFileHeader->PointerToSymbolTable);
//打印可选PE头
pOpHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader+20);
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_END 将读写位置指向文件末尾
SEEK_SET 将读写位置指向文件开头
SEEK_CUR 将读写位置指向当前位置
ftell()取得目前读写的位置
fread() fwrite() 将文件内容读取到内存
LPVOID 是一个没有类型的指针,也就是说你可以将任意类型的指针赋值给LPVOID类型的变量,在使用的时候转换回来
BYTE char 1字节
WORD short 2字节
DWORD long 4字节
PWORD 是指向一个单词的指针,在32位下是4字节在64位下是8字节
size_t 用于表示对象的大小或数组的索引,是计算对象大小并存储,无符号整数类型,sizeof操作符返回的结果类型(通常用于:1、计算数组的长度或元素数量 2、在内存分配函数如malloc、calloc中表示分配的内存大小 3、在函数参数中用作索引如memset、memcpy)
读取节表
#define _CRT_SECURE_NO_WARNINGS
//#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define filepath "C:\\Users\\Administrator\\Desktop\\ipmsg.exe"
LPVOID ReadPEFile(LPCSTR lpszFile);
void PrintNTHeaders();
int main(int argc, char* argv[])
{
PrintNTHeaders();
return 0;
}
LPVOID ReadPEFile(LPCSTR lpszFile)
{
FILE* pFile = NULL;
DWORD fileSize = 0;
LPVOID pFileBuffer = NULL;
//打开文件
pFile = fopen(lpszFile, "rb+");
if (!pFile)
{
printf("无法打开文件!\n");
return NULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//开辟内存空间、分配缓冲区
pFileBuffer = malloc(fileSize);
if (!pFileBuffer)
{
printf("分配空间失败!\n");
fclose(pFile);
return NULL;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf("读取数据失败!\n");
free(pFileBuffer);
fclose(pFile);
return NULL;
}
//关闭文件
fclose(pFile);
return pFileBuffer;
}
void PrintNTHeaders()
{
LPVOID pFileBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOpHeader = NULL;
PIMAGE_SECTION_HEADER pSeHeader = NULL;
pFileBuffer = ReadPEFile(filepath);
if (!pFileBuffer)
{
printf("文件读取失败!\n");
return;
}
//判断是否是有效的MZ标志
if ((*(WORD*)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
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);
//打印NT头
printf("**********NT头**********\n");
pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer+pDosHeader->e_lfanew);
printf("NT头:%x\n", pNtHeaders->Signature);
//打印标准PE头
printf("**********标准PE头**********\n");
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders+4);
printf("Machine:%x\n", pFileHeader->Machine);
printf("NumberOfSections:%x\n", pFileHeader->NumberOfSections);
printf("TimeDateStamp:%x\n", pFileHeader->TimeDateStamp);
printf("PointerToSymbolTable:%x\n", pFileHeader->PointerToSymbolTable);
printf("SizeOfOptionalHeader:%x\n", pFileHeader->SizeOfOptionalHeader);
//打印可选PE头
printf("**********可选PE头**********\n");
pOpHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader+20);
printf("Magic:%x\n",pOpHeader->Magic);
printf("SizeOfCode:%x\n", pOpHeader->SizeOfCode);
//打印节表
pSeHeader = IMAGE_FIRST_SECTION(pNtHeaders);
for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++)
{
printf("**********第%d个节表**********\n", i + 1);
printf("Name:%.*s\n", IMAGE_SIZEOF_SHORT_NAME, pSeHeader->Name);
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("PointerToRelocations:0x%.x\n", pSeHeader[i].PointerToRelocations);
printf("PointerToLinenumbers:0x%.x\n", pSeHeader[i].PointerToLinenumbers);
printf("NumberOfRelocations:0x%.x\n", pSeHeader[i].NumberOfRelocations);
printf("NumberOfLinenumbers:0x%.x\n", pSeHeader[i].NumberOfLinenumbers);
printf("Characteristics:0x%.x\n", pSeHeader[i].Characteristics);
}
//释放内存
free(pFileBuffer);
}
#define _CRT_SECURE_NO_WARNINGS
//#include "stdafx.h"
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#define filepath "C:\\Users\\Administrator\\Desktop\\ipmsg.exe"
LPVOID ReadPEFile(LPCSTR lpszFile);
void PrintNTHeaders();
int main(int argc, char* argv[])
{
PrintNTHeaders();
return 0;
}
LPVOID ReadPEFile(LPCSTR lpszFile)
{
FILE* pFile = NULL;
DWORD fileSize = 0;
LPVOID pFileBuffer = NULL;
//打开文件
pFile = fopen(lpszFile, "rb+");
if (!pFile)
{
printf("无法打开文件!\n");
return NULL;
}
//读取文件大小
fseek(pFile, 0, SEEK_END);
fileSize = ftell(pFile);
fseek(pFile, 0, SEEK_SET);
//开辟内存空间、分配缓冲区
pFileBuffer = malloc(fileSize);
if (!pFileBuffer)
{
printf("分配空间失败!\n");
fclose(pFile);
return NULL;
}
//将文件数据读取到缓冲区
size_t n = fread(pFileBuffer, fileSize, 1, pFile);
if (!n)
{
printf("读取数据失败!\n");
free(pFileBuffer);
fclose(pFile);
return NULL;
}
//关闭文件
fclose(pFile);
return pFileBuffer;
}
void PrintNTHeaders()
{
LPVOID pFileBuffer = NULL;
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeaders = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOpHeader = NULL;
PIMAGE_SECTION_HEADER pSeHeader = NULL;
pFileBuffer = ReadPEFile(filepath);
if (!pFileBuffer)
{
printf("文件读取失败!\n");
return;
}
//判断是否是有效的MZ标志
if ((*(WORD*)pFileBuffer) != IMAGE_DOS_SIGNATURE)
{
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);
//打印NT头
printf("**********NT头**********\n");
pNtHeaders = (PIMAGE_NT_HEADERS)((DWORD)pFileBuffer + pDosHeader->e_lfanew);
printf("NT头:%x\n", pNtHeaders->Signature);
//打印标准PE头
printf("**********标准PE头**********\n");
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeaders + 4);
printf("Machine:%x\n", pFileHeader->Machine);
printf("NumberOfSections:%x\n", pFileHeader->NumberOfSections);
printf("TimeDateStamp:%x\n", pFileHeader->TimeDateStamp);
printf("PointerToSymbolTable:%x\n", pFileHeader->PointerToSymbolTable);
printf("SizeOfOptionalHeader:%x\n", pFileHeader->SizeOfOptionalHeader);
//打印可选PE头
printf("**********可选PE头**********\n");
pOpHeader = (PIMAGE_OPTIONAL_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_SECTION(pNtHeaders);
pSeHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOpHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 0; i < pNtHeaders->FileHeader.NumberOfSections; i++)
{
printf("**********第%d个节表**********\n", i + 1);
printf("Name:%.*s\n", IMAGE_SIZEOF_SHORT_NAME, pSeHeader[i].Name);
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("PointerToRelocations:0x%.x\n", pSeHeader[i].PointerToRelocations);
printf("PointerToLinenumbers:0x%.x\n", pSeHeader[i].PointerToLinenumbers);
printf("NumberOfRelocations:0x%.x\n", pSeHeader[i].NumberOfRelocations);
printf("NumberOfLinenumbers:0x%.x\n", pSeHeader[i].NumberOfLinenumbers);
printf("Characteristics:0x%.x\n", pSeHeader[i].Characteristics);
}
//释放内存
free(pFileBuffer);
}
RVA与FOA的转换
RVA:相对虚拟地址
FOA:文件偏移地址
1、得到RVA的值:内存地址-ImageBase
2、判断RVA是否位于PE头重,如果是:FOA==RVA
3、判断RVA位于哪个节(条件必须同时成立的):
RVA>=节.VirtualAddress
RVA<=节.VirtualAddress+节.VirtualSize
差值A=RVA-节.VirtualAddress
文件中的地址=差值A+PointRawData
RVA = 全局变量 - ImageBase
FOA:
1、判断RVA是否在头部 在的话直接返回
FOA=RVA
2、如不在就判断这个值在哪个节里
RVA>=节.VirtualAddress
RVA<=节.VirtualAddress+当前节内存对齐后的大小
差值=RVA-节.VirtualAddress
FOA=节.PointerToRawData+差值
PE加载的过程
1、根据SizeOfImage的大小,开辟一块缓冲区(ImageBuffer).
2、根据SizeOfHeader的大小,将头信息从FileBuffer拷贝到ImageBuffer
3、根据节表中的信息循环讲FileBuffer中的节拷贝到ImageBuffer中.
4、Misc.VirtualSize 和 SizeOfRawData谁大?
5、FileBuffer与ImageBuffer谁大?
PE模拟exe加载到内存(FileBuffer-ImageBuffer)
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
//变量声明
PIMAGE_DOS_HEADER pDosHeader = NULL;
PIMAGE_NT_HEADERS pNtHeader = NULL;
PIMAGE_FILE_HEADER pFileHeader = NULL;
PIMAGE_SECTION_HEADER pSeHeader = NULL;
PIMAGE_OPTIONAL_HEADER pOpHeader = NULL;
//exe文件地址
#define filepath "C:\\Users\\Administrator\\Desktop\\ipmsg.exe"
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* argv[])
{
LPVOID pFileBuffer = NULL;
LPVOID* ppFileBuffer = &pFileBuffer;
LPVOID pImageBuffer = NULL;
LPVOID* ppImageBuffer = &pImageBuffer;
DWORD SizeOfFileBuffer = 0;
DWORD SizeOfImageBuffer = 0;
DWORD SizeOfBuffer = 0;
LPVOID pBuffer = NULL;
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("调用ImageToNewFile失败!\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_END);
pFileSize = ftell(file);
fseek(file, 0, SEEK_SET);
if (pFileSize == NULL)
{
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_SIGNATURE)
{
printf("不是有效的MZ标志!\n");
return 0;
}
pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)ppFileBuffer + pDosHeader->e_lfanew);
if (pNtHeader->Signature != IMAGE_NT_SIGNATURE)
{
printf("不是有效的PE标志!\n");
return 0;
}
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeader + 4);
pOpHeader = (PIMAGE_OPTIONAL_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_SECTION_HEADER)((DWORD)pOpHeader + pFileHeader->SizeOfOptionalHeader);
for (int i = 1; i <= pFileHeader->NumberOfSections; 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;
pNtHeader = (PIMAGE_NT_HEADERS)((DWORD)pppImageBuffer + pDosHeader->e_lfanew);
pFileHeader = (PIMAGE_FILE_HEADER)((DWORD)pNtHeader + 4);
pOpHeader = (PIMAGE_OPTIONAL_HEADER)((DWORD)pFileHeader + 20);
pSeHeader = (PIMAGE_SECTION_HEADER)((DWORD)pOpHeader + pFileHeader->SizeOfOptionalHeader);
//得到FileBuffer的大小
for (int i = 1; i < pFileHeader->NumberOfSections; 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_SECTION_HEADER)((DWORD)pOpHeader + pFileHeader->SizeOfOptionalHeader);
for (int j = 1; j <= pFileHeader->NumberOfSections; 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\\ipmsg2.exe", "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 = NULL;
printf("success\n");
return TRUE;
}
硬编码
代码节空白区添加代码(文件注入)
MessageBoxA:0x76780660
Function();
0040D478 E8 8D 3B FF FF call @ILT+5(Function) (0040100a)
//MessageBox(0,0,0,0);
return 0;
0040D47D 33 C0 xor eax,eax
E9 01 00 00 00 jmp Function (00401010)
CALL真正要跳转的地址 = E8这条指令的下一行地址 + X
X = 真正要跳转的地址0040100a - E8这条指令的下一行地址0040D47D = 8D3BFFFF
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是填(EntryPoint+imagebase)-(E9最后一个00的地址)
最后把原来EntryPoint改成从什么地址开始添加的代码就改成什么地址
call = E8
jmp = E9
push = 6A
KR笔记
cl /c Cdemo.c 产生.obj文件
.obj就是产生的代码支撑、数据支撑,二进制代码的中间文件,可以交给其他的编译器使用
link Cdemo.c 产生.exe可执行文件
#include <stdio.h> <>尖括号仅是找环境变量 ""双引号是找当前目录
存进去的地址从左至右是从高位置到低位置,就是小段方式
#include <stdio.h>
#include "Text1.c"
int main(int argc, char *argv[])
{
int n = 999;
printf("Hello, world:%p:%d\n",&n,n);
foo();
getchar();
return 0;
}
随机数 反码、补码
#define _CRT_SECURE_NO_WARNINGS
#include <windows.h>
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <time.h>
int main(int argc, char* argv[])
{
int n = 0;
srand((unsigned)time(NULL));
while (n < 10)
{
printf("%d\r\n", rand());
n++;
}
return 0;
}
为什么到80000000出来了?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
int n = 1;
while (n > 0)
{
n++;
}
printf("%x\r\n", n);
return 0;
}
求补运算是一种运算
补码是一种编码
补码规定了数据的读写双方必须做到以下几点
1、最高有效位是符号位,0表示正、1表示负
2、当数据是正数的时候,其余各位直接存储其数值
3、当数据为负数的时候,其余各位存储其求补后的值
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
int n = 52;
int m = -52;
printf("%x\r\n", &n);
return 0;
}
+52
110100 最高位补位后
00110100
看成2进制就是
0011 0100
3 4在内存中就是34 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 *argv[])
{
//36, -36
//0x86单字节有符号数的十进制真值是什么?
//10000110
//1 0000110
//- 1111001 取反+1
//- 1111010
// 0111 1010
// 7 A
//7A=7*16+10=-122
//0x80呢?
//1000 0000
//-0111 1111 取反+1
//-10000000 = -128
return 0;
}
异常报错笔记
C05:内存访问异常:访问了保留地址、或不存在的地址、或没有权限的地址
Scanf 用户输出
#define _CRT_SECURE_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
//一定要对变量进行初始化,否则上次用n变量的还会有残留数据
int i1 = 0;
char c1 = '\0';
float f1 = 0.0f;
double d1 = 0.0;
char name[10] = { 0 };
int* lpTest = NULL;
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); //000003e7
//printf("%08x\r\n",m); //00000000
//等号左边必须是一个变量
char ch1='a';
char ch2='b';
char ch3='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_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
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_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
/*
6 *
5 * *
4 * *
3 * *
2 * *
1 * *
0 *
0 1 2 3 4 5 6
ax + c = y
0a + c = 3
3a + c = 6
c = 3
a = 1
x + 3 == y
3a + c = 6
6a + c = 3
a = -1
c = 9
-x + 9 == y
6a + c = 3
3a + c = 0
a = 1
c = -3
x - 3 == y
3a + c = 0
0a + c = 3
a = -1
c = 3
-x + 3 == y
*/
int main(int argc, char* argv[])
{
int x = 0;
int y = 0;
for (x = 0;x < 7; x++)
{
for (y = 0;y < 7;y++)
{
if (x + 3 == y || -x + 9 == y || x - 3 == y || -x + 3 == y)
{
printf("* ");
}
else
{
printf(" ");
}
}
printf("\n");
}
system("pause");
return 0;
}
地址 指令的内容 指令
00401037 5F pop edi
00401038 5E pop esi
00401039 5B pop ebx
0040103A 83 C4 40 add esp,40h
0040103D 3B EC cmp ebp,esp
0040103F E8 2C 03 00 00 call __chkesp (00401370)
00401044 8B E5 mov esp,ebp
00401046 5D pop ebp
if…else 可以控制命中率
switch…case 最好是大于3个分支在用
#define _CRT_SECURE_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main(int argc, char* argv[])
{
int n = 3;
/*if(n == 1)
{
printf("n == 1\n");
}
else if (n == 2)
{
printf("n == 2\n");
}
else if (n == 3)
{
printf("n == 3\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 3:
printf("%d\n",n);
break;
case 4:
printf("%d\n",n);
break;
}
system("pause");
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* argv[])
{
int n = 15000;
double nBitCount = 0.0;
int i = 1;
for (int i = 1; i <= n; i++)
{
nBitCount += log10(i);
}
/*
DO_BEGIN:
nBitCount += log10(i);
i++;
if (i<=n)
{
goto DO_BEGIN;
}*/
//ceil函数用于向上取整,即使不小于参数的最小整数,例如ceil(4.3),ceil(4.7)也将返回5
printf("%d\n",(int)ceil(nBitCount));
system("pause");
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* argv[])
{
int nHp = 0;
srand( (unsigned)time( NULL ) );
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_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* argv[])
{
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_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* argv[])
{
int n = 100;
if (n<1)
{
//error info
return -1;
}
printf("sum(%d) = %d \r\n",n,(1+n)*(n/2));
system("pause");
return 0;
}
猴子吃桃
#define _CRT_SECURE_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
int main(int argc, char* argv[])
{
int nCount = 1;
for (int i =0;i<9;i++)
{
nCount=(nCount+1)*2;
}
printf("%d\r\n",nCount);
system("pause");
return 0;
}
斐波那契数列(每一项都是前两项的和)
#define _CRT_SECURE_NO_WARNINGS
#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* argv[])
{
/* 斐波那契数列数组循环
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 nFib3 = 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_NO_WARNINGS
#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=nFib1+nFib2;
nFib1=nFib2;
nFib2=nFib;
}
return nFib;
}
int main(int argc, char* argv[])
{
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.参数的传输媒介(栈传参、寄存器传参)
3.函数返回值的位置
4.释放参数空间的负责方,有且仅有一方去释放参数空间
__cdecl:参数使用栈空间传递,从右往左传递,函数返回值在寄存器,由调用方负责释放参数空间
__stdcall:参数使用栈空间传递,从右往左传递,函数返回值在寄存器,由被调用方负责释放参数空间
__fastcall:左数前两个参数使用寄存器传递,其他参数使用栈空间传递,从右往左传递,函数返回值在寄存器,由被调用方负责释放参数空间
2、保存返回地址:在函数调用时,需要将下一条指令的地址(即返回地址)保存在栈上,以便在函数执行结束后返回到调用方 19 15 40 00
3、保存调用方的栈信息(栈底) 在开始执行被调用函数之前,需要保存调用方的栈信息,即栈底的地址 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、 其他约定,取出当前栈顶作为返回的流程地址,同时由被调方清理参数空间后才返回
E3 10 40 00 01 00 00 00 00 00 00 00 03 00 00 00 30 14 40 00 30 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…:申请的局部变量空间
30 14 40 00 30 14 40 00 00 10 2E 00:保存的寄存器空间
code | |||
---|---|---|---|
data | Inited | 常量区 | |
可读可写区 | |||
Uninit | |||
stack | |||
heap |
微软C/C++编译器选项
-优化- | ||
---|---|---|
/O1 | 最小化空间 | minimize space |
/Op[-] | 改善浮点数一致性 | improve floating-pt consistency |
/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 optimizations (default) |
/Ox | 最大化选项。(/Ogityb2 /Gs) | maximum opts. (/Ogityb1 /Gs) |
/Og | 启用全局优化 | enable global optimization |
/Oy[-] | 启用框架指针省略 | enable frame pointer omission |
/Oi | 启用内建函数 | enable intrinsic functions |
-代码生成- | ||
/G3 | 为 80386 进行优化 | optimize for 80386 |
/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 functions 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 extensions (implies /Op) |
/noBool | 禁用“bool”关键字 | disable “bool” keyword |
/Ze | 启用扩展(默认) | enable extensions (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 options and libraries] |
经典题目:迷宫
0x00000000FD:栈溢出
project settings----link----Reserve(保留值,能用的空间)改为0x10000000 Commit(提交值,现在给你用的空间)改为0x01000000
#define _CRT_SECURE_NO_WARNINGS
#include "StdAfx.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <MATH.H>
#include <STDLIB.H>
/*
随堂作业:迷宫
$终点,1表示墙,0表示路
*/
void Show(char szMaze[][13],int nRow)
{
for (int i = 0;i<nRow;i++)
{
puts(szMaze[i]);
}
return;
}
void Maze(char szMaze[][13],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[x+1][y]=='0'||szMaze[x+1][y]=='$')
{
Maze(szMaze,x+1,y);
}
if (szMaze[x][y+1]=='0'||szMaze[x][y+1]=='$')
{
Maze(szMaze,x,y+1);
}
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* argv[])
{
char szMaze[][13] = {
"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_NO_WARNINGS
#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,3,4,5 };
/*
连续且一致
第0个元素的地址常量
int n = .....;
type arr[M] = ....;
arr[n] address;
(int)arr + sizeof(type)*n
*/
int n = 2;
//printf("%p\r\n", &arr[3]); //4
//printf("%p\r\n", (int)arr + sizeof(int) * 3);
//printf("%p\r\n", &arr[n]); //3
//printf("%p\r\n", &arr[n - 3]); //下标越界的地址
//printf("%p\r\n", &arr[n + 3]); //下标越界的地址
//printf("%p\r\n", &n[arr]); //3
//printf("%p\r\n", arr[(0x00400000 - (int)arr) / sizeof(int)]);
//foo(3);
//foo(arr);
int m = 5;
foo(arr);
return 0;
}
二维数组
寻址公式
//多维数组是特殊的一维数组
//N维数组的元素是N-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*x+y)
#define _CRT_SECURE_NO_WARNINGS
#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() {
//多维数组是特殊的一维数组
//N维数组的元素是N-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*x+y)
int arr[3][5] =
{
{1,2,3,4,5},
{2,2,3,4,5},
{3,2,3,4,5}
};
/*int arr[3][4] =
{
{1,2,3,4},
{2,2,3,4},
{3,2,3,4}
};*/
ShowArr(&arr[0][0], 3, 5);
int x = 2;
int y = 3;
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_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <stdlib.h>
//自己实现的标准库函数strcpy
void MyStrCpy(char szBuf[], const 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_NO_WARNINGS
#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;
}
作用域 | 生命期 | |
---|---|---|
函数 | 变量定义开始到函数结束 | 进入函数分配空间,退出函数释放空间 |
块(受约束的局部变量) | 变量定义开始到块结束 | 同函数 |
全局变量 | 工程作用域 |
编译期间检查 运行期间检查
贪吃蛇小游戏
MyGameStart.c
#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();
MySnake.c
#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 SNAKE 2 //蛇
#define FOOD 3 //食物
//贪吃蛇数组
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] == SNAKE)
{
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] = SNAKE;
//保存蛇头的坐标
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] == SNAKE)
{
return SNAKE;
}
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 == SNAKE)
{
return SNAKE;
}
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] = SNAKE;
}
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 == SNAKE)
{
return SNAKE;
}
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] = SNAKE;
}
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 == SNAKE)
{
return SNAKE;
}
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] = SNAKE;
}
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 == SNAKE)
{
return SNAKE;
}
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] = SNAKE;
}
if (ShowUI() != 0)
{
return -1;
}
return 0;
}
//游戏引擎文件
int StartGame()
{
srand((unsigned)time(NULL));
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;
}
C++ 指针和地址
type* ptr = ....;
int n = .....;
prt + n = (int)ptr + sizeof(type)*n 运算结果为type * const同类指针常量
= (type * const)((int)ptr + sizeof(type)*n)
数组名是数组第0个元素的指针常量
type ary[M] = ....;
ptr = ary;
ptr[n] = *(type * const)((int)ptr + sizeof(type)*n)
int _tmain() {
int a = 8;
int* pa = NULL;
pa = &a;
*pa = 999;
int ary[5] = { 1,2,3,4,5 };
int* pAry = NULL;
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,3,4,5 };
int* pAry = NULL;
int n = 2;
int* pn = NULL;
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 = NULL;
memcpy(目标,原,大小)
int _tmain() {
int ary[5] = { 1,2,3,4,5 };
char aryDest[5 * sizeof(int)];
int* pAry = NULL;
int n = 2;
int* pn = NULL;
pn = &n;
pAry = ary;
float flt = 3.14f;
printf("%f", flt);
//没有解释方式,只有地址
void* pv = NULL;
/*
块操作
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,调用约定
3,返回值类型
void (_cdecl*pfnSort)(int [], int);
void SortA(int arr[], int nCount)
{
puts("冒泡");
}
void SortB(int arr[], int nCount)
{
puts("选择");
}
//typedef 起别名,定义一个新类型,增加可读性、可移植性
typedef void(_cdecl* PFNSORT)(int[], int);
int _tmain() {
int arr[54];
//void(_cdecl * pfnSort)(int[], int) = NULL;
PFNSORT pfnSort = NULL;
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* PFNSORT)(int[], int);
void PlayCard(int arr[], int nCount, PFNSORT 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 *argv[],char* envp[]) {
char* arrPoint[3] = {
"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[3][5] = {
{1,2,3,4,5},
{10,20,30,40,50},
{100,200,300,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)[3][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[3][4]的元素是int[4]
//arr是int[4]类型的指针常量
printf("%p\r\n", arr);
//*arr得到int[4]一维数组
//*arr是int类型的指针常量
printf("%p\r\n", *arr);
//任何类型的变量取地址得到该类型的指针
//&arr取地址得到int[3][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[3][4]类型的指针
//*arr + 1 <==> (int)&arr + sizeof(int[3][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]类型的指针常量
//*(arr+1)得到一维数组int[4]
//数组名是第0个元素的指针常量
//*(arr+1)是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(memberN 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 szName[5]; //5->8
int nAge; //4
float fHeight; //4
double dblWeight; //8
unsigned short int wID; //2
char nGender; //上面的2和1补到8
//sizeof() == 32
};
//恢复对齐值
#pragma pack(pop)
/*
struct tagType obj;
obj.member address is:
(member type*)((int)&obj + member offset)
obj.member = *(member type*)(obj.member address)
*/
int _tmain(int argc, char* argv[], 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.szName);
struct tagStudent* pStu = NULL;
pStu = &stu;
printf("%d\r\n", &*(double*)((int)pStu + 8));
//想取出偏移量,又不想取出结构体变量
#define GetOffset(s,m) (size_t)(((s*)NULL)->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->dob.nYear);
_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, const 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* argv[], 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));
usc1.chLevel = 'a';
*(char*)usc2.szText = 'a';
*
usc1.fPoint = 3.14f;
*(char*)usc2.szText = 3.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 sc3;
SetScore(&sc3, "GOOD");
GetScore(&sc3);
_tsystem(_T("pause"));
return 0;
}
堆
#ifdef _DEBUG
#define malloc(n) _malloc_dbg(n, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
int _tmain(int argc, char** argv, 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, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
void ErrorProc() {}
int _tmain(int argc, char **argv, char *envp[]) {
int *pA = (int *)malloc(sizeof(int));
if (pA == NULL) {
ErrorProc();
return -1;
}
*pA = 999;
char *pb1;
float *pb2;
if (argc % 2 == 0) {
pb1 = (char *)malloc(strlen("Hello") + sizeof(char));
if (pb1 == NULL) {
free(pA);
ErrorProc();
return -1;
}
strcpy(pb1, "Hello");
} else {
pb2 = (float *)malloc(strlen("Hello") + sizeof(float));
if (pb2 == NULL) {
free(pA);
ErrorProc();
return -1;
}
*pb2 = 3.14f;
}
double *pC = (double *)malloc(sizeof(double));
if (pC == NULL) {
free(pA);
ErrorProc();
return -1;
}
*pC = 0.618;
_tsystem(_T("pause"));
return 0;
}
C指针的使用规范、位运算
#ifdef _DEBUG
#define malloc(n) _malloc_dbg(n, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
void ErrorProc() {}
int _tmain(int argc, char **argv, char *envp[]) {
// 多资源使用规范
// 1.引用资源的变量在作用域开始定义,并初始化为错误值
int *pA = NULL;
char *pB1 = NULL;
float *pB2 = NULL;
double *pB3 = NULL;
// 2.申请资源后,必须检查是否资源有效,无效则处理错误
pA = (int *)malloc(sizeof(int));
if (pA == NULL) {
ErrorProc();
// 3.处理完错误后,转移到统一退出位置
goto EXIT_PROC;
}
*pA = 999;
if (argc > 3) {
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 = 3.14f;
}
pB3 = (double *)malloc(sizeof(double));
if (pB3 == nullptr) {
ErrorProc();
goto EXIT_PROC;
}
*pB3 = 0.618;
_tsystem(_T("pause"));
EXIT_PROC:
#define SAFE_FREE(p) \
if (p) { \
free(p); \
(p) = NULL; \
}
SAFE_FREE(pB1);
SAFE_FREE(pB2);
SAFE_FREE(pB3);
// 4.释放资源前,必须检查资源是否有效,无效则不处理
if (pA != NULL) {
free(pA);
// 5.释放资源后,必须将引用资源的变量重置为错误值
// pA = NULL;
}
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, _NORMAL_BLOCK, __FILE__, __LINE__)
#endif
int myabs(int n) {
// 具体数学
// if n >= 0, i = 0; else i = 0xffffffff = -1
int i = n >> 31;
// 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** argv, 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:\\1.txt", "wb");
if (fp == NULL) {
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_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main() {
char player_name[32] = { "没穿裤子" };
int player_lv = 99;
float player_money = 35.5f;
// 文件读取
// 二进制打开
FILE* fp = fopen("D:\\1.txt", "rb");
if (fp == NULL) {
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_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main() {
char player_name[32] = { "没穿裤子" };
int player_lv = 99;
float player_money = 35.5f;
//文本方式写入
FILE* fp = fopen("D:\\1.txt", "w");
fprintf(fp, "%s\r\n%d\r\n%.2f", player_name, player_lv, player_money);
fclose(fp);
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char player_name[32] = {"没穿裤子"};
int player_lv = 99;
float player_money = 35.5f;
//字符串分割
/*char str[] = { "hello123world123test" };
char str2[200];
strcpy(str2, str);
char* token = strtok(str, "123");
while (token != NULL)
{
printf("%s\n", token);
token = strtok(NULL, "123");
}
puts(str);*/
文本方式写入
FILE *fp = fopen("D:\\1.txt", "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[32] = {0};
int player_lv2 = 0;
float player_money2 = 0;
FILE *fp2 = fopen("D:\\1.txt", "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++ cin cout输入输出
C++完全兼容C语言的语法
二进制 >> 汇编 >> 高级语言 C >> 面向对象(C++、java、C#)
版权声明:本文标题:(持续更新)逆向学习(KR 41笔记含随堂作业) 内容由网友自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.freenas.com.cn/jishu/1726377255h948171.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论