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 = &num;

	//自动申请到堆区,成功返回申请到的首地址,失败返回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从右至左入栈自身清理堆栈
_fastcallECX/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 的二进制表示可以通过以下步骤得到:

  1. 首先,将 52 的绝对值转换为二进制。
  2. 然后,将得到的二进制数取反,得到其反码。
  3. 最后,将得到的反码加 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
dataInited常量区
可读可写区
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++ RTTIenable 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
/EHcextern“C”默认为 nothrowextern “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}禁用/启用 vtordispdisable/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 类型是 unsigneddefault char type is unsigned
/nologo取消显示版权消息suppress copyright message
/WX将警告视为错误treat warnings as errors
/Tc将文件编译为 .ccompile file as .c
/Yc[file]创建 .PCH 文件create .PCH file
/Tp将文件编译为 .cppcompile file as .cpp
/Yd将调试信息放在每个 .OBJ 中put debug info in every .OBJ
/TC将所有文件编译为 .ccompile all files as .c
/TP将所有文件编译为 .cppcompile 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创建 .DLLCreate .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 含随堂