20160206.CCPP体系详解(0016天)
代码片段(01):.指针.c+02.间接赋值.c
内容概要:内存
///01.指针
#include <stdio.h>
#include <stdlib.h>
//01.取地址操作符(&)详解:
// 1.操作对象:变量名(实质:内存实体|数据实体)
// 2.操作特点:属于寄存器操作(操作结果不是内存实体)
// (1).取地址操作(&变量名)是在CPU的寄存器区域所完成的操作;
// (2).地址数据不占用内存,内存地址是在CPU核心构成组件寄存器产生的,
// 内存地址的数值和内存存储没有实质关系;
// 3.计算机地址总线:连接CPU与硬件内存的总线
// (1).总线数目与CPU位数+操作系统位数有关
// (2).地址数值作为一个常量恒久不变
// (3).地址作为产量恒值并不会占用内存
// 内存地址是在CPU的寄存器当中产生的,因此不具备内存实体,也就不会占用内存
// (4).总线的个数决定者位数的不同:
// 32条总线对应于32位操作系统;64条总线对应于64位操作系统
//02.对指针变量的解释:
// P一个指针变量,用于存储普通变量的地址数值,然后通过操作指针变量的方式间接操作普通变量
//03.对于指针所占用的字节数分析:
// Win32平台所编译的程序的指针类型占用4个字节
// Win64平台所编译的程序的指针类型占用8个字节
int main01(void)
{
int num = 10;
int data = 20;
//P是一个指针变量,可以存储相同类型的不同变量的内存地址,常用于做间接访问变量本身(内存实体)操作
int * pNum = #//0x12312312
int * pData = &data;
printf("%d \n", sizeof(pNum));//指针变量所占用的内存尺寸为4个字节(Win3平台所编译的程序)
printf("%p \n", pNum);//直接打印指针变量(pNum)的数值,相当于间接打印了普通变量(num)的地址
printf("%p \n", &pNum);//表示获取"指针"变量的地址,类型为int **
system("pause");
}
//04.指针变量内容详解:
// 1.类型:
// 既决定了内存实体步长,也决定了内存实体解析方式
// 2.数值:
// 就是一个地址数值
// 3.场景:(携带*号的情况)
// 赋值号左边:
// 给内存实体写入数据
// 赋值号右边:
// 从内存实体读取数据
int main02(void)
{
int num1 = 1100;
int num2 = 1200;//变量num都分配了内存,内存实体(数据实体)的数值代表变量的数据
int * pNum = &num1;//pNum1是int类型的一级指针变量,&num1是一级指针真常量数值(含有类型含义)
pNum = &num2;//pNum的值是num2这个变量(内存实体|数据实体)的内存地址,该内存地址统一占用4个字节,int * 地址,指针变量的长度统一为4
//int * 对称于pNum,指针类型既决定了内存实体的访问字节步长,也决定了内存实体的解析方式,特殊点:浮点型数据的特殊存储形式,既是指数形式存储还涉及到无符号类型(存储实质阶码)
*pNum = 12;//通过指针变量间接的访问了普通变量的内存实体//根据指针数值找到内存地址,根据指针类型既决定了存储步长也决定了存储方式
printf("%p \n", &pNum);//&pNum表示获取一级指针变量pNum的内存地址,该地址数值存储于CPU核心组件寄存器区域,完全不会占用内存存储
printf("%d \n", num2);
printf("num2 = %d \n", num2);
system("pause");
}
//05.野指针:就是没有进行初始化操作的指针变量
// VC:编译检测,GCC编译不检测
//06.解析方式的不同含义:
// 有无符号:signed/unsigned
// 存储原理:补码原理/阶码原理
//07.指针变量所占用的内存尺寸只跟编译平台有关:
// Win32平台:4字节-->Win64平台:8字节
int main03(void)
{
//int * p;//没有对象的野指针,定义指针变量的时候必须进行指针变量的初始化操作
//printf("%p \n", p);//打印指针变量当中所存储的内存地址-->编译不通过
//解析方式的不同特点:
// signed/unsigned(只针对于10进制的整数)+float指数形式存储(阶码实质存储)
int num1 = 10;
int num2 = 20;
//float * pNum;
//pNum = &num1;
//printf("%f", *pNum);//指针变量的类型决定了指针变量对其所存储的地址数值的解析步长(字节尺寸)以及解析方式(补码阶码)
int * pNum;
pNum = &num1;
printf("%d \n", *pNum);//对内存地址数值的正确解析步长和正确解析方式
printf("%d \n", sizeof(pNum));//指针变量所占用的字节长度在Win32平台之下统一占用4个字节的内存尺寸,任何类型的地址都一样采用int类型的统一字节尺寸存储
printf("%d \n", sizeof(*pNum));//尺寸,方式
system("pause");
}
//08.指针认识规律:
// *pNum<=>num
int main04(void)
{
int num = 20;
int data = 30;
int * pNum = #//int类型的一级指针变量存储了int类型的普通变量的内存地址
//&num = 1;//内存地址不允许操作,由于&num这个表达式是在CPU的寄存器当中所进行的操作,然而C语言是不能够直接修改寄存器当中的数据的
//pNum = &data;
num = 3;
printf("%d,%d \n", *pNum, num);//*pNum和num是完全等价的
*pNum = 7;
printf("%d,%d \n", *pNum, num);
system("pause");
}
///02.间接赋值.c
#include <stdio.h>
#include <stdlib.h>
//01.函数形参具有副本机制:
// 1.传值和传指的数据层面意思都一样;
// 只不过对同样的数值有着不同的解析方式!
// 1.函数的副本机制总是存在;
// 无论是传值还是传地!
void change05(int num)//函数形参具有副本机制
{
//函数形参具有副本机制,因此,无论是传值还是传指,都具备这样的特性:
// 传值传递的是副本,传指传递的是地址,因此出现不一样的修改效果
// 实质:都是副本,只不过对待同样的数值解析方式不同而已
num = 3;
printf("change05 = %d \n", num);
}
int main05(void)
{
int num = 10;
change05(num);
printf("%d \n", num);
system("pause");
}
void change06(int * pNum)
{
*pNum = 3;
}
int main07(void)
{
int num = 10;
change06(&num);//&num-->表达式-->没有内存实体-->位于寄存器-->C语言不具备修改寄存器的权限
printf("%d \n", num);
system("pause");
}
程序片段(02):01.Run.c
内容概要:挂
#include <stdio.h>
#include <stdlib.h>
//01.外挂修改数值原理
// 1.注入DLL模块儿
// 2.根据指针进行跨函数访问内存
_declspec(dllexport) void go()
{
int * p = (int *)0xae0720;
*p = 77;
}
//02.遍历内存查找原理:
// 1.单字节内存遍历
// 2.待查找尺寸读取
_declspec(dllexport) void run()
{
typedef char * Char;
for (Char pStart = 0xae000000,pEnd = 0xae100000; pStart < pEnd; ++pStart)//单字节遍历指定内存范围
{
int * p = (int *)p;//指针类型转换,待查找尺寸读取数据进行匹配,以便于相同数值进行内存检索
if (77 == *p)
{
*p = 86;
}
}
}
程序片段(03):01.void.c+02.空指针.c
内容概要:Void与空类型指针
///01.void.c
#include <stdio.h>
#include <stdlib.h>
//01.空类型与空类型指针:
// 空类型:void
// 空类型指针:void *
//02.指针类型:
// 解析步长+解析方式
//03.空类型指针变量:
// 可以存储任何类型的指针
int main01(void)
{
//error C2182:"a":非法引用"void"类型 void
int num = 10;
double db = 19.3;
//void num;//不合法-->没有明确数据类型-->编译器既不知道分配多大步长的内存块儿,也不知道按照何种方式进行分配!
void * vp = #//合法->Win平台(32/64)编译器明确指针所应使用的内存字节尺寸-->不需要明确解析方式-->因为只是一个数值意义的地址
vp = &db;//空类型的指针可以存储任何类型变量(普通变量,一级指针变量,二级指针变量,三级指针变量,多级指针变量等等...)的地址,因为编译器决定了地址存储尺寸
//printf("%d \n", *vp);//:error C2100:非法的间接寻址(问题原因通常是采用空类型的指针对该指针所存储的地址进行解析)->既不明确解析尺寸,也不明确解析方式,所以出错
//空类型的指针既不可以间接取值,也不可以间接赋值(也就是只能存储内存地址,而不能根据内存地址进行间接访问操作)
//*vp = 10;
printf("%f \n", *((double *)vp));//将空类型的指针转化为具体指针类型,然后就达到了既明确了指针的解析步长,也明确了解析方式
system("pause");
}
///02.空指针.c
#include <stdio.h>
#include <stdlib.h>
//01.结构体变量:
// 用于组织多种不同类型的变量
struct MyStruct//包含四根指针,花心的妹子,可以进行动态的扩充
{//一个由多个一级指针变量所构成的单个普通结构体变量
int *p1;
int *p2;
int *p3;
int *p4;
};
//02.空指针(NULL):
// 实质:(void *)0
// 作用:标识任何指针变量没有存储内存地址
// 特点:空指针所指向的内存实体不允许访问,否则出现访问异常
int main02(void)
{
//标记指针变量是否存储内存地址
double db = 10.9;
double 刘海华 = 10.9;
double * pDb = NULL;//空指针(NULL):标识任何指针变量没有存储内存地址
while (NULL == pDb)
{
//炮之
pDb = &刘海华;
}
*pDb = 1;//空指针所指向的内存实体不允许进行操作
system("pause");
}
程序片段(04):01.指针的声明.c
内容概要:指针的声明
#include <stdio.h>
#include <stdlib.h>
//01.声明指针变量:
// 数据类型+指针级数+指针变量
int main01(void)
{
int * p1;
int * p2;
system("pause");
}
//02.特殊声明格式:
// 格式:数据类型 * p, num;
// 特点:p是指针变量,num是普通变量
int main02(void)
{
int numA, numB, numC;
double * pNumA, pNumB, pNumC;
printf("%d \n", sizeof(pNumA));//在Win32平台之下,所有类型,所以级数的指针变量都占用4个字节的内存尺寸
printf("%d \n", sizeof(pNumB));
system("pause");
}
//03.宏定义(宏替换)和别名定义的区别:
// 宏定义:第二整块儿替换后置
// 别名定义:最后整块儿替换前置
#define 整数 int
typedef int INT;
int main03(void)
{
整数 x = 3;//替换后置
INT y = 3;//替换前置
printf("%d, %d \n", x, y);
system("pause");
}
//04.别名定义规律:
// 变量定义:int * pNum
// 添关键字:typedef int * pNum;
// 特点:变量名变为类型别名
#define 指针 double *
typedef double * DBP;
int main04(void)
{
指针 p1;
DBP p2;
//printf("%p, %p \n", p1, p2);
printf("%d, %d \n", sizeof(p1), sizeof(p2));
system("pause");
}
//05.特别注意:
// 1.宏定义(宏替换)和别名定义之间的区别
// 2.预编译与真实别名
int main05(void)
{
指针 p1, p2;//实质:double *p1, p2, p3;//4,8
p1 = 0x123;
p2 = 0x123;
DBP p3, p4;//doube *类型
p3 = 0x123;
p4 = 0x123;
printf("%d, %d \n", sizeof(p1), sizeof(p2));
printf("%d, %d \n", sizeof(p3), sizeof(p4));
system("pause");
}
程序片段(05):01.银行.c+02.指针数组.c
内容概要:数据地址映射
///01.银行.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
//01.C语言编译器特点:
// 未初始化的普通变量->编译通过
// 未初始化的指针变量->编译不过
//02.数据地址映射使用:
// 1.不允许改变原始内存实体的情况,实现排序
// 2.比较数据实体,交换指针变量
// 注:
// 1.只有整型数据才能使用位运算
// 2.升序的反面成立,既需要进行交换
int main01(void)
{
int num1;//内存实体
int num2;
scanf("%d%d", &num1, &num2);//从从小到大
int * p1 = &num1;//映射指针
int * p2 = &num2;
if (*p1 > *p2)//升序的反面成立,就需要进行交换
{
//p1 = p1 ^p2;//只有整型才能使用位运算
int * ptemp = p1;
p1 = p2;
p2 = ptemp;
}
printf("%d, %d \n", *p1, *p2);
system("pause");
}
//03.指针变量声明与const关键字使用:
// 规律:const在指针变量名称前面,也就是星号("*")的右边
// 就表明该指针变量本身是一个伪常量,也就是该指针变量
// 不能直接进行修改,但是可以通过间接方式进行修改
// 特点:const int * p<=>int const * p
// 只需要关注const与星号("*")之间的相对位置
// 用处:限定访问权限
// 读取权限和修改权限的综合配对
//void accessControl(const int * p);
void accessControl(int const * p)//报表权限:限定访问权限,间接操作的内存实体只能读取,不能进行修改
{//const在指针当中所能起到的作用
p = NULL;//通过指针变量间接访问的内存实体是个真常量,不允许进行间接修改,但是当前指针变量可以进行修改
printf("*p = %d \n", *p);//不能根据空指针进行内存实体访问,既不能读取也不能写入(操作系统规定)-->编译不报错,但是运行报错
//*p = 10;//编译报错
}
int main02(void)
{
int num = 100;
int * pNum = #
accessControl(pNum);
printf("num = %d \n", num);
system("pause");
}
///02.指针数组.c
#include <stdio.h>
#include <stdlib.h>
//int a[8];
//const int *p[8];
int a[8] = { 1,8,2,7,3,6,4,5 };//int类型的数组
const int * p[8] = { &a[0],&a[1],&a[2],a + 3,a + 4,a + 5,a + 6,a + 7 };//指针数组
void main()
{
printf("原来的数组数据:\n");
for (int i = 0; i < 8; i++)
{
printf("%d\n", a[i]);
}
printf("原来的指针数组指向数据:\n");
for (int i = 0; i < 8; i++)
{
printf("%d\n", *p[i]);
}
for (int i = 0; i < 8 - 1; i++)//最后一个数据不需要冒泡
{
for (int j = 0; j < 8 - 1 - i; j++)//冒泡数与与i形成对应关系
{
//指针数组当中存储的是指针,然后通过指针进行数据的获取,通过比较指针所对应的数据,然后进行数据交换
if (*p[j]>*p[j+1])//这里使用指针数组进行比较数据,用数组指针进行数据获取比较,然后进行数据交换
{//指针交换
int *ptemp = p[j];
p[j] = p[j + 1];
p[j + 1] = ptemp;
}
}
}
printf("数组数据:\n");
for (int i = 0; i < 8; i++)
{
printf("%d\n", a[i]);
}
printf("指针数组指向数据:\n");
for (int i = 0; i < 8; i++)
{
printf("%d\n", *p[i]);
}
system("pause");
}
程序片段(06):01.输入指针.c
内容概要:地址输入
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
int main01(void)
{
int num = 10;
int data = 20;
printf("%p, %p \n", &num, &data);
int * pNum = &pNum;
//对指针变量进行手动地址输入:
// 格式:scanf("%p", &pNum);
// 特点:不需要携带0x前缀,而且必须是大写英文字母
scanf("%p", pNum);//地址类型数组需要使用%p这种输入格式控制符+指针变量的地址
*pNum = 20 + 3;//操作系统所使用的地址,胡乱进行访问的时候容易出现系统问题
printf("num = %d, data = %d \n", num, data);
system("pause");
}
程序片段(07):01.Screen.c
内容概要:屏幕输出图片
#include <stdio.h>
#include <stdlib.h>
#include <Windows.h>
int main(void)
{
int i = 0;
while (1)
{
HWND win = GetDesktopWindow();
HDC winDc = GetWindowDC(win);//获取指定窗口的输出接口
HDC memDc = CreateCompatibleDC(0);//获取内存存储的操作接口
HBITMAP bitMap = (HBITMAP)LoadImage(win, TEXT("1.bmp"), IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION);
SelectObject(memDc, bitMap);//选定图形图像设备-->设置显示什么
BitBlt(winDc, 0, 0, i++, i++, memDc, i++, i++, SRCCOPY);
Sleep(100);//模拟0.1秒
++i;
if (3 == i)//到达3次
{
i = 0;//重置0次
}
}
system("pause");
}
程序片段(08):test.c
内容概要:Test1
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define EN 1024
int flag = 0;
char * filePath = "E:\\Resource\\TestData\\BigDB\\Test.txt";
//数组赋值
int intArr[EN];
void initArr(int intArr[EN])
{
//time_t te;
//unsigned int seed = (unsigned int)(time(&te));
//srand(seed);
srand((unsigned int)(time(NULL)));
for (int i = 0; i < EN; ++i)
{
intArr[i] = rand() % EN + 1;
}
}
//数组显示
void showArr(int intArr[EN])
{
for (int i = 0; i < EN; ++i)
{
printf("%4d \n", intArr[i]);
}
}
//冒泡排序
void bubbleSortArr(int intArr[EN])
{
for (int i = 0; i < EN - 1; ++i)
{
for (int j = 0; j < EN - 1 - i; ++j)
{
if (intArr[j] > intArr[j + 1])
{
intArr[j] = intArr[j] ^ intArr[j + 1];
intArr[j + 1] = intArr[j] ^ intArr[j + 1];
intArr[j] = intArr[j] ^ intArr[j + 1];
}
}
}
}
//选择排序
void selectSortArr(int intArr[EN])
{
int minIndex = 0;
int minNum = 0;
for (int i = 0; i < EN - 1; ++i)
{
minIndex = i;
minNum = intArr[i];
for (int j = i + 1; j < EN; ++j)
{
if (minNum > intArr[j])
{
minIndex = j;
minNum = intArr[j];
}
}
if (i != minIndex)
{
intArr[i] = intArr[i] ^ intArr[minIndex];
intArr[minIndex] = intArr[i] ^ intArr[minIndex];
intArr[i] = intArr[i] ^ intArr[minIndex];
}
}
}
//插入排序
void insertSortArr(int intArr[EN])
{
int currentIndex = 0;
int currentValue = 0;
for (int i = 1; i < EN; ++i)
{
currentIndex = i;
currentValue = intArr[currentIndex];
while (0 < currentIndex && intArr[currentIndex - 1] > currentValue)
{
intArr[currentIndex] = intArr[currentIndex - 1];
--currentIndex;
}
intArr[currentIndex] = currentValue;
}
}
//二分查找
int binarySearch(int intArr[EN], int value)
{
int minIndex = 0;
int midIndex = 0;
int maxIndex = EN - 1;
while (minIndex <= maxIndex)
{
midIndex = (minIndex + maxIndex) / 2;
if (value == intArr[midIndex])
{
return midIndex;
}
else if (value < intArr[midIndex])
{
maxIndex = midIndex - 1;
}
else
{
minIndex = midIndex + 1;
}
}
return -1;
}
//拉格朗日查找
int lagrangeSearch(int intArr[EN], int value)
{
int minIndex = 0;
int ratioIndex = 0;
int maxIndex = EN - 1;
while (minIndex <= maxIndex)
{
//midIndex = minIndex + (maxIndex - minIndex) / 2;
ratioIndex = minIndex + (maxIndex - minIndex)*(value - intArr[minIndex]) / (intArr[maxIndex] - intArr[minIndex]);
if (value == intArr[ratioIndex])
{
return ratioIndex;
}
else if (value < intArr[ratioIndex])
{
maxIndex = ratioIndex - 1;
}
else
{
minIndex = ratioIndex + 1;
}
}
return -1;
}
int main01(void)
{
initArr(intArr);
showArr(intArr);
insertSortArr(intArr);
printf("binSearch Index = %d \n", binarySearch(intArr, 880));
printf("lagrangeSearch index = %d \n", lagrangeSearch(intArr, 880));
system("pause");
}