20160210.CCPP体系具体解释(0020天)

时间:2021-04-13 19:16:43

程序片段(01):01.二级指针.c

内容概要:二级指针

#include <stdio.h>
#include <stdlib.h> //01.二级指针:
// 1.使用场景:
// (1).跨函数改动一级指针变量的数据-->DLL注入技术!
// (2).指针数组作为函数形參,将会转化为二级指针-->函数形參!
// 2.使用规律:
// 怎样高速定位取值运算符所操作的数据是什么?
// 指针变量(定义级数-取值级数)<==>訪问级数!
int main01(void)
{
int a = 10;
int b = 15;
int * p = &a;
int ** pp = &b;
*pp = &b;
**pp = 3;
//a-->b-->*p-->**pp system("pause");
}

程序片段(02):01.反外挂.c

内容概要:游戏反外挂

#include <stdio.h>
#include <stdlib.h>
#include <Windows.h> //01.关于赋值运算符的使用特点:
// 赋值运算符针对于普通变量具备自己主动类型转换特性;
// 赋值运算符针对于指着变量不具备自己主动类型转换的特性
//02.指针类型与指针变量类型的重要性!
// 仅仅要涉及到指针的操作,一定要时刻注意类型的重要性
// 注:数据类型<=>解析(步长+方式)
//03.数组名以及指向数组首元素的指针差别:
// 1.数组名和指向数组首元素的指针的数值层面一样
// 2.数组名和指向数组首元素的指针都存在内存实体
// 数组名:内存实体不可改动
// 常量指针变量的指向不可改变,可是指向的数据能够改变
// 指向数组首元素的指针(分变量指针和常量指针的情况)
// 视指针变量的const修饰关键字使用情况而定
//04.通过一级指针变量来模拟反外挂原理:
// 一级指针变量其中的指针指向数组其中的某个血量刻度;
// 通过改动一级指针变量的指针指向来获取详细的血量刻度;
// 从而实现血量不可改动,仅仅能改动指向(实现血量的仅仅读权限)
int main01(void)
{
double arr[100];
for (int i = 0; i < 100; ++i)
{
arr[i] = i + 1;//1.0~100.0
}
double * p = arr + 60;//类型非常重要,类型决定了(解析步长+解析方式)-->直接的一个数组名仅仅是具备该数组首元素的内存首地址(首个字节)
printf("&p = %p \n", &p);
printf("&arr = %p \n", arr);
printf("%f \n", *p);
while (1)
{
printf("此时的血=%lf \n", *p);
Sleep(1000);
} system("pause");
}

程序片段(03):01.挂.c

内容概要:

_declspec(dllexport) go01()
{
double * p1 = 0x2ef4a4;
double * p2 = 0x2ef4bc;
//p1 = p2 + 92;//整数倍
} //01.动态库函数的导出接口:
// _declspec(dllexport)
//02.二级指针变量的用途:
// 注入技术改动二级指针变量的数据
//03.区分数值意义层面的整数
// 没有数据类型的特点
//04.指针变量的指针假设仅仅是加上
// 一个普通的数值,那么最好是该指针所
// 指向内存块儿的内存字节整数倍!
// 避免数据解析失败!
//05.不管是自己主动类型转换还是强制类型转换
// 都仅仅是在对内存其中的二进制数据副本进行操作;
// 原本数据不会被改动!
_declspec(dllexport) go()
{
double ** pp = 0x0018FAE4;//这里的pp+1<=>pp+1*(所存储的一级指针变量所占用的内存尺寸)
//double * p = 0xB9F5D4;//p+1不论什么指针类型的变量都能够存储一个地址,地址本身无类型的情况之下,仅仅能当做一个普通数据
//*pp = 0xB9F5D4 + 0x50;//p+3普通的整数没有类型可言,仅仅是一个数值层面的意义
//*pp = *pp + 2;//改动方式1(明白类型)
//*pp = 0x0018F794 + 3 * sizeof(double);//改动方式2(不明类型)-->错误,由于sizeof();会返回有类型的整型数值
*pp = 0x0018FAFC + 3 * 8;//改动方式2(正确方式)
//int a = 10;
}

程序片段(04):01.指针类型.c

内容概要:指针类型

#include <stdio.h>
#include <stdlib.h> //01.指针的类型以及指针变量的类型重要性:
// 1.数据类型:决定了(解析方式+解析步长)
// 2.关于数据类型所决定的解析方式一共同拥有三大类:
// (有符号整型+无符号整型+浮点型),这三种类型
// 即使解析步长一样,假设解析方式不一样,解析结果照样异常!
// 3.指针的类型决定了通过取值运算符进行取值运算的时候
// 须要通过指针的数据(等同于内存首地址),向前解析的内存字节数
// 4.printf();函数不会进行数据类型的转换,仅仅是依据格式控制符所描写叙述
// 的解析方式对内存其中的二进制数据进行相应的解析
// 注1:赋值号对于指针变量没有类型转换的意义,仅仅有数值层面的意义!-->数值意义(地址数值,没有类型概念)
// 赋值号仅仅对于普通变量才存在着类型转换的意义!-->类型意义
// 注2:指针变量的类型决定了取值运算符对指针变量其中所存储的指针的解析细节!
int main01(void)
{
int num = 10;
int * p1 = &num;
void * p3 = &num;//空类型的指针变量仅仅是用于存储具备地址意义的数值,不含解析细节!
double * p2 = &num;
printf("%p, %p \n", p1, p2);//指针变量所存储的地址数值都一样
printf("%lf, %d \n", *p2, *p1);//可是指针变量的类型决定了取值运算福对指针变量其中的指针解析方式 system("pause");
} //02.不论什么类型的变量进行赋值的时候,
// 都应当注意数据类型是否一致!
int main02(void)
{
int a = 10;
int * p1 = &a;
int * p2 = p1;//类型一致的指针变量指针能够进行指针的赋值操作
printf("%p, %p \n", p1, p2);//格式控制符仅仅是描写叙述对于内存二进制数据的解析方式
printf("%d, %d, %d \n", *p1, *p2, a);
//*p1 = 4;
a = 2;
printf("%d, %d, %d \n", *p1, *p2, a); system("pause");
} //03.仅仅要指针变量的类型和指针类型不一致
// 就绝对不能正确的解析到结果
int main03(void)
{
int num = 10;
double db = 10.9;
int * p1 = &num;
double * p2 = p1;//获取p2这个指针所指向的数据的时候,由于该指针变量(p2)的所描写叙述的解析(方式+步长)不对,因此解析出来的结果不可预料
//赋值号,指针除了指针变量以外的其它变量,都会存在自己主动类型转换机制,只有指针类型的变量特殊(仅仅有地址意义的数值)
//int a = 10.9;
void *px = p1;
px = p2;//空类型的指针变量仅仅是用于暂时存储地址意义的变量
printf("%d \n", (int)db);//printf();不会进行自己主动类型转换,会导致解析某些数据失败
printf("%lf \n", (float)num); //double * p = &num;
//printf("%lf \n", *p);
//*p = &db;
//p = &db;
//printf("%lf \n", *p);
int *p = &db;
printf("%d \n", *p); system("pause");
} //04.sizeof关键字用于求取实际占用的内存字节数!
// 返回内存字节数类型为unsigned int
//05.严格注意:
// 是详细给指针变量赋值还是给普通变量赋值
// 注:通过指针变量+取值运算符訪问到的可能是普通内存实体!
// 赋值运算符针对于普通内存实体也是具备自己主动类型转换机制的!
//06.关于自己主动类型转换机制:
// 两种处理方式
// 同为整型:直接截断(图形化操作二进制位)
// 不同类型:依据解析方式+解析步长进行决断!
int main04(void)
{
int num = 100;
int * p = &num;//sizeof(int)--int **pp-->sizeof(int *)
*p = 10.9;
printf("%d \n", num);
printf("%u \n", sizeof(int *));
printf("%d \n", sizeof(double *)); system("pause");
} //07.三个不同类型的指针变量所存储的地址是一样的
// 可是由于指针变量的类型不一致,也就导致了指针类型不一致
// 因此,对于同一个地址所相应的内存实体的解析方式也就不一样!
//08.对局部变量的查询方式:
// 局部变量的值+依据局部变量的值进行解析后的结果
int main05(void)
{
int num = -1;
int * p1 = &num;
unsigned int * p2 = &num;
float * p3 = &num; system("pause");
}

程序片段(05):01.Test.cpp+02.指针.c

内容概要:指针的运算

///01.Test.cpp
#include <stdio.h>
#include <stdlib.h> //01.区分:变量指针和常量指针以及指针常量!
// 变量指针:能够改动的指针变量
// 常量指针:不能够改动的指针变量,不能够改动指向,比如数组名
// 指着常量:具备地址层面意义的地址数值!
//02.数组名的本质:
// 假设数组是一维数组,那就是指向数组其中收个元素的常量指针
// 假设数组是高维数组(含二维),那就首先将该数组看作为一个一维数组
// 而该数组名的本质就是指向(看做成一维数组)的首个元素的常量指针
int main01(void)
{
int arr[5] = { 1, 2, 3, 4, 5 };
int * p = arr;//变量指针=常量指针
int * const px = arr;//数组名的本质就是数组其中第一个元素(全部数组都看作为一维数组进行处理!)的指针常量(地址常量|数值常量)
*(arr + 3);
//arr = 1;//常量指针
//px = 1;//const所修饰的常量指针,其指向不能够进行直接改动! system("pause");
return 1;
}
///02.指针.c
#include <stdio.h>
#include <stdlib.h> //01.指针和指针变量都必须严格注重数据类型
//02.指针变量的运算必须在同一个数组的情况之下,才具有意义!
// 1.指针变量+|-一个整数:
// 在数组其中向前或者向后移动几个数组元素
// 2.指针变量-指针变量:
// 求取两个指针变量之间相差了多少个数组元素
// 注:指针变量没有乘法和乘法方面的运算,由于运算结果毫无意义!
int main02(void)
{
int a = 10;
int b = 20;
int * p1 = &a;
int * p2 = &b;
p1 = p2;//同一个t类型的一级指in针变量之间能够进行相互赋值
int arr[5];
p1 = arr;
p2 = &arr[2];//指针变量赋值运算,必须注意类型的统一性(并且:指针变量的赋值操作仅仅具备数值层面的操作意义)
p1 - p2;//得到的结果是在同一个数组其中的两个数组元素之间所相差的数组元素个数 system("pause");
} //03.C语言和C++语言的编译器特点:
// C语言属于"弱类型"语言;C++语言属于"强类型"语言
// 注:强弱类型就是对数据类型是否精准匹配的严格校对!(编译时期校对)
//04.关于指针变量和整型变量之间的赋值:
// 1.类型不匹配,C语言同意
// 2.给指针变量赋予的整型变量的数据具有地址层面的意义
// 注:不能够对指针变量进行随意赋值操作!(唯恐赋予不规范内存地址)
int main03(void)
{
int * p = 0x123123;//指针变量一般不同意直接赋值常规整数,由于不可预知(由于该地址所相应的内存块儿不知道是否有系统进行维护!)
*p = 1;//该指针变量其中的指针指向了操作系统所使用的内存,因此发生内存訪问错误情况!
int num = p;
//指针变量和整型变量之间能够进行赋值,能够编译,可是整数表示的是具备地址意义层面的数值!
printf("%x \n", num); system("pause");
} void select(int const * p)
{
//*p = 123;
printf("银行有%d元! \n", *p);
} int main04(void)
{
int arr[5] = { 1, 2, 3, 4, 5 };
int const * p;//指针变量的指向能够改变,可是指向的数据数据不能够改变!
int num1 = 101;//银行的钱
select(&num1);
int num2 = 102;//银行的钱
select(&num2); system("pause");
} int main05(void)
{
int num1 = 101;//银行的钱
int num2 = 102;//银行的钱
const int * p = &num1;
p = &num2;
//*p = 10; system("pause");
} int main06(void)
{
int num1 = 101;//银行的钱
int num2 = 102;//银行的钱
int * const p = &num1;
*p += 3;
//p = &num2;
//int * const p;//指针变量的指向能够改动,可是所指向的数据不能改动 system("pause");
} //04.关于const关键字做权限控制的几种情况:
// const在*右边:跨函数数据仅仅读权限,账户訪问权限
// 指针变量的指向不可改动,但所指向的数据能够改动
// const在*左边:跨函数账户仅仅读权限,数据訪问权限
// 指着变量的指向能够改动,但所指向的数据不可改动
// 双const情况:跨函数账户不可改动,数据不可改动
// 指针变量的指向不可改动,所指向的数据不可改动
// 注:严格区分账户情况和数据情况
int main07(void)
{
int num = 10;
int num2 = 100;
const int * const p = &num;
//*p = 101;
//p = &num2; system("pause");
} //05.关于const做权限控制的解决方式:
// const在*右边:
// 1.指向变量数据的常量指针
// 2.账户仅仅读权限,数据訪问权限
// const在*左边:
// 1.指向常量数据的变量指针
// 2.账户訪问权限,数据仅仅读权限
// const在双边:
// 1.指向常量数据的常量指针
// 2.账户仅仅读权限,数据仅仅读权限(代理查询)
// 注:函数形參控制账户+函数实体控制数据

程序片段(06):01.Run.c

内容概要:算数运算以及其它运算

#include <stdio.h>
#include <stdlib.h> //01.针对于数组的两种经常使用遍历方式:
// 索引遍历+指针遍历
// 注:编译实质arr[i]<=>*(arr+i)
int main01(void)
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
//arr[i]的编译实质:*(arr+i)
for (int i = 0; i < 10; ++i)
{//下标遍历
printf("%p, %p, %d \n", &arr[i], arr + i, arr[i]);
}
for (int * p = arr; p < arr + 10; ++p)
{//指针遍历
printf("%p, %d \n", p, *p);
} system("pause");
} //02.运算符的优先级:
// 1.接触生效原理(谁先接触谁优先)
// 2.自变运算符(++)的优先级大于取值运算符(星号:"*")
int main02(void)
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int * p = &arr[3];
p += 3;
//printf("%d \n", *p++);//7
//printf("%d \n", *(p++));//7
//printf("%d \n", ++p);//8
//printf("%d \n", *(++p));//8 system("pause");
} //03.指针变量的相等与不等比較特点!
int main03(void)
{
int num = 10;
int * p1 = &num;
int * p2 = &num + 1;
if (p1 == p2)
{
printf("情敌! \n");
}
else
{
printf("非情敌! \n");
} system("pause");
} //04.指针变量的关系运算:
// 1.数组内:索引前后关系
// 2.数组外:栈内存所属位置
int main04(void)
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
int * p1 = arr;
int * p2 = arr + 3;
//p2>p1:p2的下标比p1的下标大,数组内部能够说明排序 system("pause");
} //05.编译器的两种编译模式:
// Debug模式:栈结构的开口向上
// Debug情况之下:VC和GCC两个编译器的结果同样
// Release模式:栈结构的开口向下
// Release情况之下:存在着代码优化机制
// 注:(代码优化机制:VC和GCC编译器都会依据变量的使用频率,自己主动优化该变量在栈结构其中的所属位置
// 某变量的使用频率越高,那么该变量在Release模式下越有几率被存放于栈结构开口处!)
//06.几种进栈实质区分:
// 1.代码进栈
// 2.函数形參:形參变量进栈
// 3.函数实体:局部变量进栈
int main05(void)
{
int a;
int b;
int c;
printf("&a = %p, &b = %p, c = %p \n", &a, &b, &c); system("pause");
}

程序片段(07):01.Test.c+02.Run.c

内容概要:指针运算的优先级

///01.Test.c
#include <stdio.h>
#include <stdlib.h> int main01(void)
{
char * p;
printf("%d \n", sizeof(p));//全部指针类型享有共同内存尺寸,仅仅是区分编译器位数直接决定 system("pause");
}
///02.Run.c
#include <stdio.h>
#include <stdlib.h> //01.两个指针变量的减法说明:
// 1.必需在同一个数组其中两个指针变量进行减法操作才具有实际意义!
// 2.随意两个指针变量的减法结果推导!
// 减法结果=((指针变量1的指针所属地址数值)-(指针变量2的指针所属地址数值))/sizeof(指针变量1的所属类型);
// 注:编译器不会报错!可是实际开发过程其中严谨这样进行操作!
int main02(void)
{
int arr[5] = { 1, 2, 3, 4, 5 };
int * p = arr;
int * p1 = &arr[4];
printf("%d \n", p1 - p);//相减等同于相差的数组元素个数
//double * px = &arr[4];//2 system("pause");
} //02.内容概要:
// 1.变量指针+常量指针
// 2.指针减法=(指针1-指针2)/sizeof(*指针1);
//03.指针变量的优先级和结合性:
// 1.接触生效
// 2.++的优先级高于*
int main03(void)
{
int arr[5] = { 1, 2, 3, 4, 5 };
int * p = arr;
int * px = &arr[4];
printf("%d \n", px - p); char * px1 = arr + 3;
char * px2 = arr + 4;//数组名arr的数据类型int * const p;
printf("%d \n", px1 - px2);//4=(地址1-地址2)sizeof(char) *p++;//++的优先级比*的优先级高
(*p)++;
++*p;//优先级接触生效
for (int i = 0; i < 5; ++i)
{
printf("%d \n", arr[i]);
} system("pause ");
}

程序片段(08):01.Point.c

内容概要:指针与数组

///01.Point.c
#include <stdio.h>
#include <stdlib.h> //01.数组名+&数组名+*数组名:
// 数组名:
// 将全部数组看做为一维数组进行处理
// 然后数组名就是指向该一维数组的常量指针
// &数组名:
// 数组指针常量,是一个具有(地址+类型)意义的数值
// *数组名:
// 依据数组名的特点进行区分
//02.各种维度的指针差别:
// 0维指针:指向一列
// 1维指针:指向一行
// 2维指针:指向一面
// 3维指针:指向一体
int main01(void)
{
int intArr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 , 0 };
int intArrArr[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 };
printf("intArr = %p, &intArr = %p \n", intArr, &intArr);
printf("intArr+1 = %p, &intArr + 1 = %p \n", intArr + 1, &intArr + 1);
//intArr作为一个常量指针,指向数组其中的首个元素
//&intArr作为一个指针常量,指向整个数组
prinrf("intArrArr = %p, &intArrArr = %p, *intArrArr \n", intArrArr, &intArrArr, *intArrArr);
printf("intArrArr+1 = %p, &intArrArr+1 = %p, *intArrArr+1 = %p \n",intArrArr + 1, &intArrArr + 1, *intArrArr + 1);
//intArrArr作为一个常量指针,指向数组其中的首个元素(将全部数组当做为一维数组进行看待的处理结果)
//&intArrArr作为一个指针常量,指向一个面儿
//*intArrArr作为一个指针常量,指向一个列 system("pause");
}

程序片段(09):01.P.cpp

内容概要:指针数组与数组指针

#include <stdio.h>
#include <stdlib.h> //01.指针数组与数组指针
// 指针数组:
// 1.是一个数组
// 2.存储的都是变量指针
// 数组指针:垂直指向
// 1.是一个指针
// 2.存储的是一维数组的首地址
// 注:凡是对数组名进行取地址操作,所获取到的
// 都是指针都是数组指针(指针常量)
int main01(void)
{
int arr[10];
int * pArr[10];//40-->指针数组:用于存储指针变量的数组
int (*p)[10];//4-->数组指针:指向含有10个数组元素的的数组的指针!
printf("%d, %d \n", sizeof(pArr), sizeof(p)); system("pause");
return 1;
} //0 9 2 8 3 7 4 5 5 10
//0 9 2 8 3 7 4 6 10 5
//10 9 2 8 3 7 4 6 1 5
//02.指针数组的常见用途:
// 1.批量管理指针(内存地址)
// 2.便于分块数组模型的构建
//注:指针数组所存储的指针类型通常都是(char*)类型
// 由于便于进行内存检索!+字符串存储于代码区常量池
//03.赋值运算符针对于指针变量不具备类型转换的特性
// 通常仅仅是具备地址层面意义的数值
//04.比較指针变量所存储的指针所指向数据实体
// 交换指针变量所存储的指针!-->防止改动原始存储数据顺序!
int main02(void)
{
int arr[10] = { 0, 9, 2, 8, 3, 7, 4, 6, 5, 10 };
int * pArr[10];//指针数组:该数组其中的每一个变量都是指针变量,用于批量管理内存地址[指针]
for (int i = 0; i < 10; ++i)
{
pArr[i] = arr + i;//变量指针存储常量指针的地址数值
}
//正向的冒泡排序算法
//for (int i = 0; i < 10 - 1; ++i)
//{
// for (int j = 0; j < 10 - 1 - i; ++j)
// {
// if (*pArr[j] < *pArr[j + 1])
// {
// int * pTemp = pArr[j];
// pArr[j] = pArr[j + 1];
// pArr[j + 1] = pTemp;
// }
// }
//}
//反向的冒泡排序算法
for (int i = 10 - 1; i > 0; --i)
{
for (int j = 10 - 1; j > 10 - 1 - i; --j)
{
if (*pArr[j] < *pArr[j - 1])
{
int * pTemp = pArr[j];
pArr[j] = pArr[j - 1];
pArr[j - 1] = pTemp;
}
}
}
for (int i = 0; i < 10; ++i)
{
printf("%d, %d \n", *pArr[i], arr[i]);
} system("pause");
return 1;
} int main03(void)
{
int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
for (int * p = arr; p < arr + 10; ++p)
{//指针遍历
printf("%3d", *p);
}
printf("%p, %p \n", arr, &arr);
printf("%p , %p \n", arr + 1, &arr + 1);
int (*p)[10];//p-->p+1-->40//&一维数组名的本质
p = &arr;
int * px = arr;//px-->px+1-->4 system("pause");
}

程序片段(10):01.批量挂.c

内容概要:指针数组挂

#include <Windows.h>

//01.通过指针数组实现批量处理指针变量:
// 跨进程改动内存实体-->须要内存实体的所属地址!
_declspec(dllexport) go()
{
int * pArr[5];
pArr[0] = 0xadfc70;
pArr[1] = 0xae0720;
pArr[2] = 0xae0850;
pArr[3] = 0xae05f0;
pArr[4] = 0xae04c0; while (1)
{
for (int i = 0; i < 5; ++i)
{
if (*pArr[i] < 100)
{
*pArr[i] = 101;
}
}
Sleep(1000);
}
}

程序片段(11):01.二维数组.c+02.二维数组轮询.c

内容概要:指针与二维数组

///01.二维数组.c
#include <stdio.h>
#include <stdlib.h> //01.关于线性存储的数组的解析方式:
// 1.能够採用指针变量的遍历方式
// 2.该指针变量直接从数组的首个数组元素開始进行遍历!
// 线性遍历:遍历的指针尺寸是数组其中的不可切割的数据类型!
// 3.线性数组的奥数操作方式!
// 外层变化快+内层变化慢!
//注:能够直接将二维数组其中的每一个数组元素看做为单个列指针变量的
// 指针所指向!
int main01(void)
{
int arrArr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0 };
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 5; ++j)
{
printf("%4d", arrArr[i][j]);
}
printf("\n");
}
for (int * p = &arrArr[0][0]; p < *arrArr + 15; ++p)
{//线性排列-->线性遍历
printf("%4d, %p \n", *p, p);
}
printf("\n\n");
for (int i = 0; i < 15; ++i)
{//索引遍历-->性能优化
printf("%4d, %p \n", arrArr[i / 5][i % 5], &arrArr[i / 5][i % 5]);
//00 01 02 03 04
//10 11 12 13 14
//20 21 22 23 24
} system("pause");
} //02.关于一维数组二维数组的概念差别:
// 一维数组:int arr[5]
// arr:int * const-->常量指针-->有内存实体
// &arr:int (*pArr)[5]-->数组指针-->没有内存实体-->属于指针常量
// *arr:int-->零维指针-->有内存实体-->获取数值
// 二维数组:int arrArr[4][5]
// arrArr:int (*pArr)[5]-->数组类型的常量指针-->行指针-->有内存实体
// &arrArr:int (*pArr)[4][5]-->数组类型的指针-->面指针-->没有内存实体-->属于指针常量
// *arrArr:int *-->普通变量的指针-->列指针-->有内存实体
int main02(void)
{
//int arr[5];
int arrArr[3][5];
printf("%p, %p \n", arrArr, arrArr + 1);//20个字节
printf("%p, %p \n", *arrArr, *arrArr + 1);//4个字节
//int ** p = arrArr;//数据类型不匹配
int(*parrArr)[5] = arrArr + 0;//二维数组名的本质:将二维数组看做为一维数组之后,指向其中每一个元素的常量指针类型
//arrArr = 1;//数组名一定是常量指针,绝对不能够进行改变
int * px = *(arrArr + 0);//*arrArr-->行指针转化为列指针(就是指向普通变量的一级一级指针)-->仅仅是对内存数据的转换
int(*py)[3][5] = &arrArr;//&a-->一个指向固定面积的二维数组的指着变量,二维数组指针 system("pause");
}
///02.二维数组轮询.c
#include <stdio.h>
#include <stdlib.h> //01.关于二维数组的指针操作方式:
// 比如:int arrArr[3][4]
// arrArr:行指针
// arrArr+i<=>&arrArr[i]:行指针
// arrArr[i]+j<=>*(arrArr+i)+j;
// *(arrArr[i]+j)<=>*(*(arrArr+i)+j)
// 注:
// 数组名的本质:
// 将数组看做为一维数组,数组名
// 就是指向该一维数组的常量指针
// *行指针<=>列指针
// 列指针的实质就是一级常量指针
int main03(void)
{
int arrArr[3][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 , 0, 0, 0, 0, 0 };
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 5; ++j)
{
printf("%4d, %p", arrArr[i][j], &arrArr[i][j]);
}
printf("\n");
}
printf("\n\n");
for (int i = 0; i < 3; ++i)
{
printf("arrArr + %d = %p, %p \n", i, arrArr + i, &arrArr[i]);
}
for (int j = 0; j < 5; ++j)
{
printf("%p, %p \n", arrArr[0] + j, *(arrArr + 0) + j);
printf("%p, %p \n", *(arrArr[0] + j), *(*(arrArr + 0) + j));
} system("pause");
}

程序片段(12):01.Show.c

内容概要:WebShow二维数组

#include <stdio.h>
#include <stdlib.h> int main(void)
{
printf("Content-Type:text/html \n\n");//01.必须使用一个明显的空行(须要两个"\n\n")进行表示
int arrArr[3][5];
int num = 0;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 5; ++j)
{
arrArr[i][j] = num;
++num;
printf("%3d",*(*(arrArr+i)+j));
printf("%3d", arrArr[i][j] = ++num);
}
printf("<br />");//02.html换行标签,不能使用C语言的换行!
} //system("pause");//03.不能使用系统函数
}

程序片段(13):01.动态数组.c

内容概要:动态数组

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h> //01.动态数组採用什么类型的指针变量进行接收:
// 就表明动态数组名称属是什么类型!(变量指针|)
// 简而言之:接收动态数组的指针变量就是动态数组名的本质!
//注:严格区分变量指针和常量指针
//02.区分释放内存和回收内存之间的差别:
// 个人观点:释放内存+数据清除=回收内存
// 仅仅的释放内存:不会自己主动发生数据清除操作!
int main01(void)
{
//arr[N];
int N;
scanf("%d", &N); int * p;
p = (int *)malloc(N * sizeof(int));
for (int i = 0; i < N; ++i)
{
p[i] = i;
printf("%d \n", p[i]);
}
free(p);//回收内存
//free(p);//释放内存,针对于同一片儿内存的指针,对该指针进行释放的操作不可反复进行! system("pause");
} //02.在堆内存开辟模拟栈内存的二维数组的关键点在于:
// 指针变量的类型-->决定对内存块儿的解析方式!
//注:能够对动态数组的数组名运行相似于栈内存二维数组的操作!
int main02(void)
{
int arrArr[3][10];//开辟堆内存动态数组模拟这个栈内存二维数组,关键在于指针
int(*p)[10] = malloc(30 * sizeof(int));//p就是堆内存其中的动态数组数组名
int num = 0;
for (int i = 0; i < 3; ++i)
{
for (int j = 0; j < 10; ++j)
{
printf("%3d", p[i][j] = num++);//原理:p[i][j]<=>*(*(p+i)+j));
}
printf("\n");
} system("pause");
} //03.关于动态内存分配的注意事项:
// 1.參数依照无符号类型进行解析的!
// 因此不能使用特殊值-1-->会解析为极大的数据
// 无符号类型解析方式:内存二进制全部看做为补码进行逆向解析
// 2.取特殊内存字节数0能够分配成功,可是并没有
// 实际意义!
//注:解析方式尤其重要!
int main03(void)
{
//int * p = malloc(-1);//分配失败为0,-1补码-->所分配的内存空间比較大
int * p = malloc(0);
printf("%p \n", p);//0能分配成功,可是没有实际意义! system("pause");
} //04.动态内存开辟诀窍:
// 数组首元素的指针!
// 注:回收内存的操作仅仅能运行一次,为了软件规范
// 仅仅要对指针变量其中所存储的指针运行了回收操作
// 就应当置为空指针,防止反复回收内存错误!
int main04(void)
{
int N;
scanf("%d", &N); int * p;
p = malloc(N * sizeof(N));
printf("p = %p \n", p);
for (int i = 0; i < N; ++i)
{
p[i] = i;
printf("%d", p[i]);
}
free(p);//内存回收!
printf("p = %p \n", p);
//free(p);回收内存,回收内存的操作仅仅能运行一次,不能够进行反复回收动作
p = NULL;//被妹子回收了,为了防止迷途指针的产生,须要设定指针变量为空
free(p);//回收空指针不会出错!
//*p = 123; system("pause");
} //05.一定要在存储指针的指针变量消亡之前
// 运行指针所指向内存区块儿回收操作,否则会导致内存泄露
void run()
{
void * p = malloc(10);//指针变量p本身存储与栈内存
//内存泄露,存储地址的指针消亡,就无法进行回收动作了
free(p);
} //06.怎样定义一个三维动态数组? // 採用面儿指针<=>指向二维数组的指针
//注:定义N维数组须要(N-1)维的数组的指针
// 不论什么N维数组都能够看做为一个一维数组,内部的(N-1)维数组仅仅是一个元素!
int main05(void)
{
int(*p)[3][5] = malloc(sizeof(int) * 15 * 2);
int num = 0;
for (int z = 0; z < 2; ++z)//纵坐标-->控制面
{
for (int x = 0; x < 3; ++x)//横坐标-->控制行
{
for (int y = 0; y < 5; ++y)//纵坐标-->控制列
{
printf("%3d", p[z][x][y] = num++);
}
printf("\n");
}
printf("\n\n");
} system("pause");
}

程序片段(14):01.动态内存.c

内容概要:动态内存

#include <stdio.h>
#include <stdlib.h> //01.静态数组:
// 位于栈内存:在编译时期就已经确定了该数组的所含有的元素个数
int main01(void)
{
int arr[10];//栈内存-->静态数组-->在编译时期就已经确定了数组的元素个数 system("pause");
} //02.动态数组:
// 位于堆内存:在运行时期动态确定该数组所占有的内存尺寸!
int main02(void)
{
int n;
scanf("%d", &n); int * p = malloc(n * sizeof(int));
for (int i = 0; i < n; ++i)
{
p[i] = i;
printf("%d \n", p[i]);
} system("pause");
} //03.区分软訪问与硬訪问:
// 软訪问和硬訪问的默认最小单位是字节
// 软訪问能够实现的极限最下单位是二进制位
int main03(void)
{
while (1)
{
void * p = malloc(1024 * 1024 * 10);
} system("pause");
} //04.使用动态内存的原因:
// 静态内存(栈内存)使用尺寸过小!
int main04(void)
{
//int a[1024 * 1024 * 1024];//栈内存的大小,不能太大,设置非常大不但耗费CPU,还耗费内存
int a; system("pause");
}

程序片段(15):main.c

内容概要:Stack

#include <stdio.h>
#include <stdlib.h> //01.内存地址的高低必须区分模式:
// 模式:Debug+Release
//02.不同的模式:
// 栈内存的开口方向不一样
// 程序优化程度不一样(比如:经常訪问的变量会定义在栈口!)
int main01()
{
int a;
int b;
int c;
printf("a=%p\nb=%p\nc=%p", &a, &b, &c);
system("pause");
return 0;
}
()