一、类型转换
能不能转是看编译器是如何设定的
-
基本类型之间可以相互转换
-
并不是所有的数据类型之间都可以相互转换,比如:int型不能转换成结构体类型
-
带*类型可以相互转换!(强转)(逆向经常会用到)
#include "stdafx.h" struct Student{ int x; short y; }; void Test(){ char* a; //声明 int* b; short** c; Student* s; a = (char*)1; //赋值 b = (int*)2; c = (short**)3; s = (Student*)4; a = (char*)b; //int*可以强转成char* b = (int*)a; //char*可以强转成int* c = (short**)a; //int*可以强转成short** s = (Student*)b; //int*可以强转成Student* //... } int main(int argc,char* argv[]){ Test(); return 0; }
二、地址符&
-
类型:
&
是地址符,任何类型变量前面加上&
,就成了一个新的类型变量,类型是:&
后面的类型加上一个*
-
在任何类型变量加
&
的作用:任何变量都可以使用&来获取地址,但不能用在常量上char a = 10; short b = 20; int c = 30; int* d = (int*)40; double**** e = (double****)50; char* pa = &a; //a是char类型,在前面加上&,&a就变成了char*类型;此时pa中存的就是a变量的地址值 short* pb = &b; //b是short类型,在前面加上&,&b就变成了short*类型 int* pc = &c; //c是int类型,在前面加上&,&c就变成了int*类型;此时pc中存的就是c变量的内存地址值 int** pd = &d; //d是int*类型,在前面加上&,&d就变成了int**类型 double***** pe = &e; //e是double****类型,在前面加上&,&e就变成了double*****类型
其实本来完整的写法应该是:
char* pa = (char*)&a
。但是我们知道了这是强转的意思,现在&a本来就是char*
类型,编译器认得出来,所以不用再加这个强转,直接简写就可以 -
下面来通过反汇编来看看究竟地址符做了什么操作
所以
&
就是将变量的地址取出来,光取出来没有意义,我们要把这个值赋给一个容器,用什么类型的容器存储呢?就是这个变量的数据类型加一个*
三、带*
类型变量前加*
-
类型:在带
*
类型的变量前面加*
,类型是其原来的类型减去一个*
-
作用:在带
*
类型的变量前加*
的作用:带*
类型的变量,可以通过在其变量前加*来获取其指向内存中存储的值记住:只能在带
*
类型的变量前面加*
,不能在一个普通类型的变量前面加*
,不然编译器会报错! -
宽度:在char类型变量前加
*
,则在内存中取的数据宽度是从此变量中存的地址值开始往后取一个字节内存;在short类型变量前加*
,则在内存中取从此变量中存的地址值开始往后取2字节内存宽度,这2字节内存中的数是多少,结果就是多少;如果是int类型,则要从起始地址开始往后取4字节内存,这4个内存中的数才是结果//定义并赋值4个带*类型的变量 int* pa = (int*)1; char** pa2 = (char**)2; int*** pa3 = (int***)3; short**** pa4 = (short****)4; //接着在这些带*类型变量前加*号:得到的新类型就是原来的类型减一个* int newpa = *pa; char* newpa2 = *pa2; //*pa2的值是一个char*类型的,所以现在把*pa2的值取出来赋给一个char*类型的变量 newpa2 int** newpa3 = *pa3; short*** newpa4 = *pa4; //不能在普通类型的变量前加* //int a = 1; 不能出现*a
-
那在带
*
类型的变量前加*
到底取出来的是什么,我们通过反汇编分析一下int a = 1; //先定义一个普通的整型变量a,赋值为1 int* pa = &a; //在变量a的前面加&表示取a的地址,取出来的值是一个int*类型的,所以赋给int*类型的pa变量 int newa = *pa; //在int*类型的变量pa前加*,表示把pa中存的值作为内存地址值,将此地址中存的值取出来,这个值 的类型为int型,所以赋给int类型的newa变量
所以在带
*
类型的变量前加*,可以来获取其指向内存中存储的值
四、*号操作数组
下面其实就是用指针操作数组!
-
总结:
*(arr+i) = arr[i]
(arr是数组名)arr = &arr[0]
-
&arr[0]
代表取数组中第一个元素的地址,可以省略为数组名arr
。即arr
等价于&arr[0]
char arr[5] = {1,2,3,4,5}; char* pa = &arr[0]; //获取arr数组的首地址,也就是arr数组中第一个元素的首地址,这个值的类型为char* char* pa2 = arr; //可以直接简写为arr,同样也是获取arr数组第一个元素的首地址
-
查询数组中元素的值除了通过
arr[下标]
的方式来获取;还可以通过*
的方式获取,如下:-
通过上面我们知道
arr
表示取数组中第一个元素的地址,赋给char*类型的变量来存储,那么此时如果在这个变量前加*
,就表示取这个变量中存的地址中的值,即把数组中第一个元素又取出来了,类型为char类型char arr[5] = {1,2,3,4,5}; char* p = arr; char n1 = *p;
-
通过上述的启发,我们既然可以通过
*p
得到第一个元素的值,那么能否通过对*p
做加法,得到arr数组的第二个、第三个等地址中的值呢?可以的!因为我们前面学过带星号类型的变量p加一个整数N,即表示带*
类型变量p的值 + N * (去掉一个*
后类型的宽度),而此时为char类型数组,所以(p + 1)就表示p中存的数组第一个元素的地址值加1,(P + 2)就表示p中存的数组第一个元素的地址值加2,依次类推。所以可以通过这种方式得到任意位置的数组元素的值char arr[5] = {1,2,3,4,5}; char* p = arr; char n0 = *(p); //1 char n1 = *(p + 1); //2 char n2 = *(p + 2); //3 char n3 = *(p + 3); //4 char n4 = *(p + 4); //5
所以可以总结出:如果想得到数组中第n个下标的值,则使用
*(数组名 + n)
可以获取到 -
那么再举个例子,现在数组元素类型为int类型,也可以通过
*(数组名 + n)
来获取指定下标的元素值int arr[5] = {1,2,3,4,5}; int n0 = *arr; //arr表示数组第一个元素地址值,那么加一个*号就表示取这个地址中存的值,即数组中第一 个元素的值1 int n1 = *(arr + 1); //2 这里其实就是取数组首地址+1*4,取这个新地址中存的数,类型为int*减一个 *,即int类型。即第二个元素的值 int n2 = *(arr + 2); //3 首地址+2*4,取这个地址中存的值,即第三个元素的值 int n3 = *(arr + 3); //4 int n4 = *(arr + 4); //5 //使用for循环加*号的方式遍历数组中的元素 for(int i = 0;i < 5;i++){ printf("%d ",*(arr + i)); }
我的小糊涂:
*(数组名 + n)
这个表达式其实是当变量使用,相当于arr[n],所以这个表达式是一个数组元素类型的变量,而不是一个常量!!!,之所以能使用int n1 = *(arr + n)
,就相当于变量a = 变量b,即把变量b的值赋给变量a,那么这个也同理,把首地址+n * 元素宽度得到的新的地址中的值赋给变量n1,这个表达式类型是int类型,而n1也是int型,所以可以直接赋值。而不能理解为*(arr + n)
是一个纯纯的常量像1,2,3一样 -
所以同样也可以通过
*(数组名 + n) = 数
来给数组中元素赋值short arr[10]; short* p = arr; for(int i = 0;i < 10;i++){ *(p+i) = i*2; } for(int k=0;k<10;k++){ printf("%d\n",*(p+k)); }
-
五、作业
-
列出每一行的反汇编代码:
void Test(){ char a = 10; short b = 20; int c = 30; char* pa = &a; //pa中存的是变量a的地址 short* pb = &b; //pb中存的是变量b的地址 int* pc = &c; //bc中存的是变量c的地址 char** ppa = &pa; //ppa变量中存的是pa变量的地址 short** ppb = &pb; //ppb变量中存的是pb变量的地址 int** ppc = &pc; //ppc变量中存的是bc变量的地址 }
-
列出每一行的反汇编代码:
void Test(){ int p = 10; int******* p7; int****** p6; int***** p5; int**** p4; int*** p3; int** p2; int* p1; p1 = &p; //将p变量内存地址赋给p1,因为p为int类型,所以&p为int*类型,而p1也是int*类型,所以 可以赋值(不用强转) p2 = &p1; //后面的都跟第一个同理 p3 = &p2; p4 = &p3; p5 = &p4; p6 = &p5; p7 = &p6; }
可以发现作为中间跳板的寄存器就是按照eax,ecx,edx,eax…的顺序循环使用的
-
完成代码,实现数组值的互换
#include "stdafx.h" void Func(){ int arr[5] = {1,2,3,4,5}; //..此处添加代码,使用指针,将数组的值倒置 int temp = 0; for(int i = 0,j = 4;i < j;i++,j--){ temp = *(arr + i); *(arr + i) = *(arr + j); *(arr + j) = temp; } for(int k = 0;k < 5;k++){ printf("%d ",*(arr+k)); } } int main(int argc,char* argv[]){ Func(); return 0; }
通用数组代码:
#include "stdafx.h" //实现数组值的互换 void exchange_arr(int* arr,int length){ int* p = arr; //头指针 int* pend = arr + length - 1; //尾指针 for(int i = 0;i < length / 2;i++){ int temp = *(p + i); *(p + i) = *(pend - i); *(pend - i) = temp; } } void print(int* arr,int length){ //打印数组函数 for(int i = 0;i < length;i++){ printf("%d ",*(arr + i)); } } int main(int argc, char* argv[]) { int arr1[5] = {1,2,3,4,5}; int length1 = sizeof(arr1) / sizeof(arr1[0]); int arr2[4] = {1,2,3,4}; int length2 = sizeof(arr2) / sizeof(arr2[0]); exchange_arr(arr1,length1); exchange_arr(arr2,length2); //验证 print(arr1,length1); printf("\n"); print(arr2,length2); getchar(); return 0; }