文章

滴水-day15-指针-类型转换

day15_0

指针

PS:在操作指针时尽量保持数据类型一致

宽度

任何数据类型加上一个后的数据宽度是4字节,不管有多少个都是4字节。

stu* stu1;
int* a;
char* b;
short* c;
a = (int*)1;
b = (char*)2;
c = (short*)3;
stu1 = (stu*)4;

00D31E85  mov         dword ptr [a],1
00D31E8C  mov         dword ptr [b],2
00D31E93  mov         dword ptr [c],3
00D31E9A  mov         dword ptr [stu1],4

char与short加上了*符号以后都是以四字节为准分配空间的。

声明

int* a;
char* b;
short* c;

任何类型后面都可以加一个*符号,加完这个符号就表示为一个新的类型。

符号尽量加在数据类型后面而不是变量名称前面。

赋值

对于带*符号的数据类型进行赋值的时候不能省略类型,比如

int* a;
a = 10 // 这是不允许的
a = (int*)a; // 这才可以

运算

++与--运算

int* a = (int*)10;
char* b = (char*)20;
a++;
b++;

00B81815  mov         dword ptr [ebp-8],0Ah
00B8181C  mov         dword ptr [ebp-14h],14h
00B81823  mov         eax,dword ptr [ebp-8]
00B81826  add         eax,4
00B81829  mov         dword ptr [ebp-8],eax
00B8182C  mov         eax,dword ptr [ebp-14h]
00B8182F  add         eax,1
00B81832  mov         dword ptr [ebp-14h],eax

int** a = (int**)10;
char** b = (char**)20;
a++;
b++;

00771815  mov         dword ptr [ebp-8],0Ah
0077181C  mov         dword ptr [ebp-14h],14h
00771823  mov         eax,dword ptr [ebp-8]
00771826  add         eax,4
00771829  mov         dword ptr [ebp-8],eax
0077182C  mov         eax,dword ptr [ebp-14h]
0077182F  add         eax,4
00771832  mov         dword ptr [ebp-14h],eax

通过反汇编可以发现当对int* a进行++的时候实际上加的不是1而是4,char* b++的时候是1。

当有两个*的时候,a++的还是4,但b++的却也是4了。

总结:带有数据类型做++--运算的时候,会先去掉一个后看自身数据宽度是多少就加多少

比如说:int a = (int)10;a++,去掉一个*后的宽度是4,那么a++的就是a+4;**

char b = (char)20;b++,去掉一个*后的宽度是1,那么b++的就是b+1;**

对于多个的情况,在宽度里讲到,不管有多少个一律视为4字节,所以带有两个*的数据类型进行++--运算

例如:char** a = (char**)20;a++去掉一个*后的数据宽度是4,就是a+4

int* a = (int*)10;
int* b = (int*)20;
char* c = (char*)30;
a = a + 5;
b = b + 5;
printf("%d\\\\n", a);
printf("%d\\\\n", b);
printf("%d\\\\n", c);

加上一个整数的运算

首先会去掉一个*后计算自身数据宽度,再以这个宽度乘以整数后加上赋值

a = a+5;就是4*5+10=30;

b = b+5就是4*5+20=40;

对与cha*的结果是

1*5+30=35

只能加整数不能加浮点数,不然报错:

表达式必须具有整数或未区分范围的枚举类型

求差值

对于两个带有*号的数据类型进行差值求会有什么不一样的呢

int* a = (int*)10;
char* b = (char*)20;
int x = a - b;

这种事不被允许的,因为int和char是两个不同的数据类型无法做减法操作,但可以强制类型转换.

所有在带有*号做差值运算的时候尽量保持数据类型一致

int* a = (int*)20;
int* b = (int*)10;
int x = a - b;
printf("%d\\\\n", x);

x的结果为2,这是为何,20-10不是等于10吗?

在带有的数据类型做差值的时候,计算的结果要除以去掉一个号后的数据类型的宽度.

20-10=10,去掉*后是int类型4字节的所以要除以4,10/4=2;

作比较

/*数据类型不同无法作比较
int* a = (int*)10;
char* b = (char*)20;
if (a > b)
{
	printf("1");
}
else
{
	printf("2");
}
*/

int* a = (int*)10;
int* b = (int*)20;
if (a > b)
{
	printf("1");
}
else
{
	printf("2");
}

005C1805  mov         dword ptr [ebp-8],0Ah
005C180C  mov         dword ptr [ebp-14h],14h  d
005C1813  mov         eax,dword ptr [ebp-8]
005C1816  cmp         eax,dword ptr [ebp-14h]
005C1819  jbe         005C182A  //第一个参数小于等于第二个参数就跳转,显然10是小于20的,
005C181B  push        5C7BCCh
005C1820  call        005C13AC
005C1825  add         esp,4
005C1828  jmp         005C1837
005C182A  push        5C7BD0h
005C182F  call        005C13AC
005C1834  add         esp,4

练习

1、char类型占几字节?char*类型占几字节?int*****占几字节?

char类型占1个字节,char*类型占4个字节,int*****占4个字节

2、char** arr[10] 占多少个字节?

40个字节

day15.xlsx

day15.cpp

day15_1

类型转换

int* a = (int*)10;
char* b = (char*)20;
a = (int*)b;

把b的类型转为了int*类型.

struct student
{
	int x;
	int y;
};

void test()
{
	int ss = 20;
	student aa = (student)ss;
}

这是不允许的,即使使用了强制类型转换.

&的使用

&符号可以加载任何变量的前面,表示为取这个变量的内存地址,这个变量是什么类型那么&取得的结果就是该类型的指针.

例如:int a = 10;

使用&符号取a的地址就是:

int* aAddr = &a;

也就是说&取完之后的类型就是该变量的类型加一个*符号.

int x = 100;
int* a = &x; // 取x的地址保存到a中

此时a保存的是x变量的地址而不是变量的值.

变量前的*符号使用

&符号是用来取变量的地址的,那么变量前的就是用来取地址中的数据的,这里的与指针声明时候的*可以理解为两个不同的东西.

总结:*在数据类型后面是意思声明一该类型的指针指向一块内存

  • 在变量名前面时意思是取出该变量所保存的地址指向的数据(前提是该变量是一个指针);

int x = 100;
int* a = &x; // 取x的地址保存到a中
int resu = *(a); //取a地址指向的数据保存到resu中。

通过&符号把x的地址保存到a变量中,此时a保存的是x的地址而不是x的值,在通过*(a)把a保存地址指向的数据取出来放到resu中.

通过以上代码可以看到:对指针变量进行取值后所返回的结果就是该变量去掉一个的数据类型.

int** uuu = (int**)200;
int*** ss = &(uuu);
int** re = *(ss);
printf("%p\\\\n", re);

这里也更好的论证了&与的使用,&返回的数据类型就是该变量自身类型加上一个

返回的类型就是该变量减去一个的类型.


int** uuu = (int**)200;
int* re = *(uuu);
printf("%d\\\\n", re);


这样写是不行的,因为200保存到uuu地址后,是0xC8,在从0xC8中取值肯定是取不到的.

在打印指针时候尽量使用%p,虽然是打印的十六进制.但是指针保存的地址不都是十六进制嘛

练习

1、列出每一行的反汇编代码:

char a = 10; // mov dword ptr ds:[ebp-0x4],Ah

short b = 20; // mov dword ptr ds:[ebp-0x8],0x14

int c = 30; // mov dword ptr ds:[ebp-0xc],0x1E

char* pa = &a; // lea eax,dword ptr ds:[ebp-0x4];mov dword ptr ds:[ebp-0x10],eax

short* pb = &b; // lea ecx,dwrod ptr ds:[ebp-0x8];mov dword ptr ds:[ebp-0x14],ecx

int* pc = &c; // lea edx,dword ptr ds:[ebp-0xc];mov dword ptr ds:[ebp-0x18],edx

char** ppa = &pa; //lea eax,dword ptr ds:[ebp-0x10];mov dword ptr ds:[ebp-0x1C],eax

short** ppb = &pb; // lea ecx,dword ptr ds:[ebp-0x14];mov dword ptr ds:[ebp-0x20],ecx

int** ppc = &pc; // lea edx,dword ptr ds:[ebp-0x18];mov dwrod ptr ds:[ebp-0x24],edx;

数据类型在分配空间的时候是按照4字节分的,但是在使用的时候是按照自身宽度使用的.

1、列出每一行的反汇编代码:

char a = 10; // mov byte ptr ds:[ebp-0x4],0xa

short b = 20; // mov word ptr ds:[ebp-0x8],0x14

int c = 30; // mov dword ptr ds:[ebp-0xc],0x1E

char* pa = &a; // lea eax,[ebp-4];mov dword ptr ds:[ebp-0x10],eax

short* pb = &b;// lea ecx,[ebp-0x8];mov dword ptr ds:[ebp-0x14],ecx

int* pc = &c;// lea edx,[ebp-0xc];mov dword ptr ds:[ebp-0x18],edx

char** ppa = &pa; // lea eax,[ebp-0x10];mov dword ptr ds:[ebp-0x1c],eax

short** ppb = &pb;// lea ecx,[ebp-0x14];mov dword ptr ds:[ebp-0x20],ecx

int** ppc = &pc;// lea edx,[ebp-0x18];mov dword ptr ds:[ebp-0x24],eds

2、列出每一行的反汇编代码:

int p = 10; mov dword ptr ds:[ebp-0x4],0xa

int******* p7;

int****** p6;

int***** p5;

int**** p4;

int*** p3;

int** p2;

int* p1;

p1 = &p; //lea eax,[ebp-0x4];mov dword ptr ds:[ebp-0x8],eax;ebp-0x20

p2 = &p1; // lea ecx,[ebp-0x20];mov dword ptr ds:[ebp-0xc],ecx;ebp-0x1C

p3 = &p2; // lea edx,[ebp-0x1c];mov dword ptr ds:[ebp-0x10],edx;ebp-0x18

p4 = &p3; // lea ecx,[ebp-0x18];mov dword ptr ds:[ebp-0x14],eax

p5 = &p4; // lea eax,[ebp-0x14];mov dword ptr ds:[ebp-0x10],eax

p6 = &p5; // lea edx,[ebp-0x10];mov dword ptr ds:[ebp-0xC],edx

p7 = &p6; // lea ecx,[ebp-0xC];mov dword ptr ds:[ebp-0x8],ecx

存储变量地址的时候尽量局部变量最大位开始存储,ds段寄存器默认情况下可以省略

4、完成代码,实现数组值的互换

void Function()
{
	int arr[5] = {1,2,3,4,5};

	//..此处添加代码,使用指针,将数组的值倒置

	//打印数组值的代码已经写完,不需要修改
	for(int k=0;k<5;k++)
	{
		printf("%d\\\\n",*(p+k));
	}
}

void Function()
{
	/*int arr[] = { 1,2,3,4,5,6,7,8 };*/
	int arr[] = { 2,3,5,1,2,4,7,9,7 };
	int length = sizeof(arr) / sizeof(arr[0]);
	int tempLength = 0;

	/*if (length % 2 !=0)
	{
		tempLength = length + 1;
	}
	else {
		tempLength = length;
	}*/

	/*printf("tempLength---->%d\\\\n", tempLength);
	printf("length-------->%d\\\\n", sizeof(arr) / sizeof(arr[0]));*/

	//..此处添加代码,使用指针,将数组的值倒置
	int* begin = (arr);
	int* end = (arr + (length - 1));

	/*printf("begin--------->%d\\\\n", *(begin));
	printf("end----------->%d\\\\n", *(end));*/

	int i = 0;
	/*while (i < (tempLength / 2))
	{
		int temp = *(begin);
		*(begin)= *(end);
		*(end )= temp;
		++begin;
		--end;
		++i;
	}*/

	while (begin < end)
	{
		int temp = *(begin);
		*(begin) = *(end);
		*(end) = temp;
		++begin;
		--end;
		++i;
	}
	printf("------------------------\\\\n");

	int* p = arr;
	//打印数组值的代码已经写完,不需要修改
	/*for (int k = 0; k < 5; k++)
	{
		printf("%d\\\\n", *(p + k));
	}*/
	for (int k = 0; k < length; k++)
	{
		printf("%d ", *(p + k));
	}
}

day15_1.cpp

day15.cpp

day15.xlsx

License:  CC BY 4.0