滴水-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_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