文章

滴水-day16-指针常量-指针函数

day16_01

模仿CE搜索内存功能

// char字节
	char* pChar = (char*)data;
	for (int i = 0; i < sizeof(data) / sizeof(data[0]); i++)
	{
		if (*(pChar + i) == 0x64)
		{
			printf("Addr-->%p value-->%x index-->%d\\\\n", (pChar + i), *(pChar + i), i);
		}
	}

	printf("\\\\n-------------------------------------------------\\\\n");

	// short字节
	for (int i = 0; i < sizeof(data) - 1; i++) {
		uint16_t value = (uint16_t)data[i] |
			((uint16_t)data[i + 1] << 8);

		if (value == 0x0064) {
			printf("Addr-->%p value-->%x index-->%d\\\\n", &data[i], data[i], i);
		}
	}

	printf("\\\\n-------------------------------------------------\\\\n");

	// int字节
	int* p = (int*)data;
	for (int i = 0; i < sizeof(data) - 3; i++) {
		uint32_t value = (uint32_t)data[i] |
			((uint32_t)data[i + 1] << 8) |
			((uint32_t)data[i + 2] << 16) |
			((uint32_t)data[i + 3] << 24);

		if (value == 0x00000064) {
			printf("Addr-->%p value-->%x index-->%d\\\\n", &data[i], data[i], i);
		}
	}

指针常量

指针指向的字符串是常量,不可更改字符串,但可以更改指针。

char* data = (char*)"AAAA";

即使强制类型转换也无法修改data地址指向的数据。

数组作为参数

数组作为参数实际上传递的是数组的首地址,

在传递数组的时候通常会把数组的长度传递过去,因为函数只接受了数组的首地址并不知道数组有多长。

数组名就相当于一个指针,他指向数组的首个元素。

008A585F  mov         dword ptr [ebp-14h],1  // 数组首地址
008A5866  mov         dword ptr [ebp-10h],2
008A586D  mov         dword ptr [ebp-0Ch],3
008A5874  mov         dword ptr [ebp-20h],3  // main函数定义的数组
008A587B  mov         eax,dword ptr [ebp-20h]
008A587E  push        eax
008A587F  lea         ecx,[ebp-14h]  // 取数组首地址赋值ecx
008A5882  push        ecx  			 // ecx传入函数
008A5883  call        008A12EE

指针函数

指针函数就是返回的是一个地址,需要同数据类型取接受或者强制类型转换

STRCAT

char* strcat_s(char* dest, char* src)
{
	char* ptr = dest;
	while (*dest !='\\\\0')
	{
		dest++;
	}
	while (*src!='\\\\0')
	{
		*dest = *src;
		dest++;
		src++;
	}
	*dest = '\\\\0';
	return ptr;
}
char data3[200] = "China";
char data4[] = "Nice";
printf("%s", strcat_s(data3, data4));

一定要对目标数组进行大小初始化,否则会发生堆栈破坏。

char* newarray = new char[strlen(dest) + strlen(src)+1];
char* ptr = newarray;
while (*dest != '\\\\0')
{
	*newarray ++= *dest++;
}
while (*src != '\\\\0')
{
	*newarray++ = *src++;
}
*newarray = '\\\\0';
return ptr;

但是通过新的指针存放追加完成之后的数组,这样就不会发生堆栈破坏了,也不用指定目标数组的大小了。

STRCPY

// strcpy
char* strcpy_s(char* dest, char* src)
{
	if (*dest == NULL || *src == NULL)
	{
		return NULL;
	}
	HWND hwnd = GetForegroundWindow();
	if (strlen(src) > strlen(dest))
	{
		MessageBoxA(hwnd, "目标空间太小", "错误", MB_OK);
		return NULL;
	}

	char* ptr = dest;
	while (*src != '\\\\0')
	{
		*dest++ = *src++;
	}
	return ptr;
}

day16.cpp

day16_1

指针数组

普通数组

int array[] = { 1,2,3,4,5 };

00D45402  mov         dword ptr [array],1
00D45409  mov         dword ptr [ebp-18h],2
00D45410  mov         dword ptr [ebp-14h],3
00D45417  mov         dword ptr [ebp-10h],4
00D4541E  mov         dword ptr [ebp-0Ch],5

for (int i = 0; i < arrayLength; i++)
{
	printf("%d ", *(array + i));
}
00D45433  jmp         __$EncStackInitStart+62h (0D4543Eh)
00D45435  mov         eax,dword ptr [ebp-34h]
00D45438  add         eax,1
00D4543B  mov         dword ptr [ebp-34h],eax
00D4543E  mov         eax,dword ptr [ebp-34h]
00D45441  cmp         eax,dword ptr [arrayLength]
00D45444  jge         __$EncStackInitStart+81h (0D4545Dh)
00D45446  mov         eax,dword ptr [ebp-34h]
00D45449  mov         ecx,dword ptr array[eax*4]
00D4544D  push        ecx
00D4544E  push        offset string "%d " (0D47B30h)
00D45453  call        _printf (0D410CDh)
00D45458  add         esp,8
00D4545B  jmp         __$EncStackInitStart+59h (0D45435h)

指针数组

int a = 10, b = 20, c = 30, d = 40, e = 50;
00D4545D  mov         dword ptr [ebp-40h],0Ah
00D45464  mov         dword ptr [ebp-4Ch],14h
00D4546B  mov         dword ptr [ebp-58h],1Eh
00D45472  mov         dword ptr [ebp-64h],28h
00D45479  mov         dword ptr [ebp-70h],32h

int* arr[] = { &a, &b, &c, &d, &e};
00D45480  lea         eax,[a]
00D45483  mov         dword ptr [arr],eax
00D45489  lea         eax,[b]
00D4548C  mov         dword ptr [ebp-88h],eax
00D45492  lea         eax,[c]
00D45495  mov         dword ptr [ebp-84h],eax
00D4549B  lea         eax,[d]
00D4549E  mov         dword ptr [ebp-80h],eax
00D454A1  lea         eax,[e]
00D454A4  mov         dword ptr [ebp-7Ch],eax

不难理解,什么样的数组就存储什么样的数据,指针数组当然存储指针类型的数据咯。

指针数组遍历数据

for (int i = 0; i < arrLength; i++)
{
	printf("%d ", *(arr[i]));
}

使用\*取值就可以了,arr数组保存的是地址。

还有一种写法

for (int i = 0; i < arrLength; i++)
{
	printf("%d ", *(*(arr + i)));
}

也不难理解,arr本身也是一个指针,他指向数组的第一个元素的地址,通过\*取出该地址的值,此时这个值是另一个数据的地址,在使用\*取出值就完成了。这相当于是二级取值吧。类似于这样

0x12345678----->0x33221144----->10

// int类型的指针,存储的是int类型的指针(听君一席话如听一席话......)
int a = 10, b = 20, c = 30, d = 40, e = 50;
int* arr[] = { &a, &b, &c, &d, &e}; // 存储的是a,b,c,d,e的地址。这些地址分别指向了10,20,30,40,50
int arrLength = sizeof(arr) / sizeof(arr[0]);
for (int i = 0; i < arrLength; i++)
{
	printf("%p---->%p---->%d ", (arr + i), *(arr + i), *(*(arr + i)));
}

// 最常用的指针数组:
	// p1已经是char指针了,不需要使用取值符&了。
	char* p1 = (char*)"if";
	char* p2 = (char*)"for";
	char* p3 = (char*)"while";
	char* p4 = (char*)"switch";

	char* keyword[] = { p1,p2,p3,p4 };
	// keyword保存的已经是这些字符串的地址了,通过*[keyword[i]]取到的是字符串的首字符。
	// 切记不可再通过*[keyword[i]]去取值,因为这个时候取到的是字符串,可能会造成内存无法访问。
	// 这里与上面那个int不同的就是,int我是通过两个变量去给指针数组进行的赋值,才会导致有两个地址
	// 而这里的char是直接把字符串的地址放到了指针数组中所有只会有一层地址。
	// p1的数据直接指向了字符串"if"再把这个p1的数据放到keyword中
	for (int i = 0; i < sizeof(keyword) / sizeof(keyword[0]); i++)
	{
		printf("%p---->%s", keyword[i], (keyword[i]));
	}

0062552F  mov         dword ptr [p1],offset string "if" (0627E24h)  // p1存储的是if的地址,而不是if本身
00625539  mov         dword ptr [p2],offset string "if" (0627BD4h)
00625543  mov         dword ptr [p3],offset string "for" (0627BD8h)
0062554D  mov         dword ptr [p4],offset string "while" (0627BE0h)
00625557  mov         eax,dword ptr [p1]  // 读取p1指向的数据,也就是if的地址。
0062555D  mov         dword ptr [keyword],eax
00625563  mov         eax,dword ptr [p2]
00625569  mov         dword ptr [ebp-0E8h],eax
0062556F  mov         eax,dword ptr [p3]
00625575  mov         dword ptr [ebp-0E4h],eax
0062557B  mov         eax,dword ptr [p4]
00625581  mov         dword ptr [ebp-0E0h],eax
00625587  mov         dword ptr [ebp-0F8h],0

// 这个和上面那个是等价的
	char* keyword2[] =
	{
		(char*)"if",
		(char*)"for",
		(char*)"while",
		(char*)"switch"
	};
	for (int i = 0; i < 4; i++)
	{
		printf("%p---->%s\\\\n", keyword2[i],keyword2[i]);
	}

同理。

结构体指针

void structArry()
{
	stu* s1;
	s1 = (stu*)20; // s1保存的是0x00000014这个地址(20-->0x14)
	printf("s1----->%p\\\\n", s1);

	s1++; // 28
	printf("s1++--->%p\\\\n", s1);

	stu s2;
	s2.x = 10;
	s2.y = 20;
	printf("\\\\n--------------------------------------\\\\n");

	// ptrS2保存了s2的地址,就是结构体第一个成员的地址
	stu* ptrS2 = &s2;
	printf("%d\\\\n", ptrS2->x);
	printf("%d\\\\n", ptrS2->y);
}

License:  CC BY 4.0