滴水三期:day19.1-变量前加星号或地址符的使用

一、类型转换

能不能转是看编译器是如何设定的

  • 基本类型之间可以相互转换

  • 并不是所有的数据类型之间都可以相互转换,比如: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*类型,编译器认得出来,所以不用再加这个强转,直接简写就可以

  • 下面来通过反汇编来看看究竟地址符做了什么操作

    012E4A7AA29FC227AB75535C006B6215

    所以&就是将变量的地址取出来,光取出来没有意义,我们要把这个值赋给一个容器,用什么类型的容器存储呢?就是这个变量的数据类型加一个*

三、带*类型变量前加*

  • 类型:在带*类型的变量前面加*类型其原来的类型减去一个*

  • 作用:在带*类型的变量前加*的作用:带*类型的变量,可以通过在其变量前加*来获取其指向内存中存储的值

    记住:只能在*类型的变量前面加*,不能在一个普通类型的变量前面加*,不然编译器会报错!

  • 宽度:在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变量
    
    4D4CA02514AF1D7D89D03E940D603001

    所以在带*类型的变量前加*,可以来获取其指向内存中存储的值

    image-20211212214944564

四、*号操作数组

下面其实就是用指针操作数组!

  • 总结:

    1. *(arr+i) = arr[i](arr是数组名)
    2. 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;
      
      5A8F35A4519A28E9B1843D871AAADF29
    • 通过上述的启发,我们既然可以通过*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变量的地址
    }
    
    image-20211213145152602
  • 列出每一行的反汇编代码:

    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;
    }
    
    image-20211213152127068

    可以发现作为中间跳板的寄存器就是按照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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值