前言
看到这里了,我感觉其实再看到这些新的名词也不害怕了,比如上节笔记其实就已经提到了指针数组,那结构体指针终究还是一个指针呗,一个指向结构体的指针,这个如果有些 php 以及 python 的开发基础那就更好懂了,就和操作一个类一样
课堂
指针数组
-
一个数组,其元素的类型为指针类型,即带*类型,这种数组就称为指针数组
-
指针数组一个元素占 4 字节,因为指针(带*类型)类型宽度都是 4 字节,无论多少个
*
char* arr[5] = {0}; //占4 * 5 = 20字节
arr[0] = (char*)1; //可以将int强转成char*类型,就可以存储在char*类型的数组中
char a1 = 'A';
char a2 = 'B';
char a3 = 'C';
char a4 = 'D';
char a5 = 'E';
char* p1 = &a1; //因为一个char类型变量加&,表示取此变量的地址,类型为char加一个*号,即char*类型
char* p2 = &a2;
char* p3 = &a3;
char* p4 = &a4;
char* p5 = &a5;
char* arr1[5] = {p1,p2,p3,p4,p5}; //char*类型就可以直接赋值
指针数组存储字符串首地址
因为字符串存储在常量区中,如果使用指针指向常量区字符串,那么这个这个指针就是char*
类型的,自然可以存在指针数组中。按照这个道理,也可以直接将字符串存储在char*
类型的指针数组中
上面这点是不是就是上篇文章所涉及的
char* str[] = {"if","while","a"};
上面这段代码其实就等价于
char* str1 = "if";
char* str2 = "while";
char* str3 = "a";
char* str[] = {str1,str2,str3};
结构体指针
又引入了一个新的类型,指针这块每引入一个新的类型,咱们就从像第一节探测带星号的类型一样开始学它,就能把它给学透彻
由于之前就说过所有带星号的类型的宽度都是 4 个字节,这里就不再多说了,那就直接从运算开始说起
- 探测结构体指针做++,–
#include "stdafx.h"
struct Student{ //根据字节对齐,可以判断此结构体宽度为(2+2) + 4 + (1+3) = 12
short a;
int b;
char c;
};
int main(int argc,char* argv[]){
Student stu;
printf("%d",sizeof(stu)); //12
Student* pstu = (Student*)100; //正常情况下使用Student* p = &stu,用来获取结构体变量stu的首地址,这里为了方便查看++结果
pstu++; 100 + 12 = 112
return 0;
}
- 探测结构体指针做加整数或减整数
#include "stdafx.h"
struct Student{ //根据字节对齐,可以判断此结构体宽度为(2+2) + 4 + (1+3) = 12
short a;
int b;
char c;
};
int main(int argc,char* argv[]){
Student stu;
Student* pstu = (Student*)100; //强转。这就是结构体指针
pstu = pstu + 10; //100 + 12 * 10 = 220
printf("%d",pstu); //220
return 0;
}
- 探测结构体指针相减
#include "stdafx.h"
struct Student{ //根据字节对齐,可以判断此结构体宽度为(2+2) + 4 + (1+3) = 12
short a;
int b;
char c;
};
int main(int argc,char* argv[]){
Student stu;
Student* pstu = (Student*)100; //强转
Student* pstu2 = (Student*)200;
printf("%d",pstu2 - pstu); //(200 - 100) // 12 = 8
return 0;
}
通过结构体指针操作数据
这个其实就可以类比操作一个一维数组,这里还不能将它和数组指针类比,因为数组指针指向的是一个数组,而这个数组其实是一个地址,所有还得指一次才能到数据,而结构体指针就不一样了,指一次就到数据了,这两者的比较咱们仔细想想,其实早就在函数传参那里就有所体现出不一样了
结构体变量前加&
,得到结构体的首地址,类型为结构体*
。所以定义结构体指针去接收这个结果,那么此指针中存的就是此结构体变量的首地址,通常我们说指针指向结构体变量
那么这个结构体指针就可以通过p->结构体中变量
的方式来读取结构体中的变量(原理是根据宽度从结构体起始地址偏移,而不是根据变量的名字来查找的!!!)
#include "stdafx.h"
struct Student{ //根据字节对齐,可以判断此结构体宽度为(2+2) + 4 + (1+3) = 12
short a;
int b;
char c;
};
int main(int argc,char* argv[]){
//创建结构体并赋值
Student stu;
stu.a = 1;
stu.b = 2;
stu.c = 3;
//现在声明并赋值结构体指针。即结构体*类型的变量
Student* pstu = &stu; //pstu中存储的就是stu结构体首地址!!!
//通过 pstu-> 的方式来访问结构体中的变量的值
printf("%d %d %d\n",pstu->a,pstu->b,pstu->c); //1 2 3
//通过结构体指针来修改结构体中变量的值
pstu->a = 10;
pstu->b = 20;
pstu->c = 30;
printf("%d %d %d",pstu->a,pstu->b,pstu->c); //10 20 30
return 0
}
咱们看一下它的反汇编,可以发现这个并不是通过比如 ebp 减多少多少去操作的,而是保留了一个基地址,然后根据内存对齐的知识去改它
这里再回顾一下基地址吧,我写了个比较易懂的代码
你看,编译器还是分的很清楚的嘛,有 8 有 4 有 3,所有结构体指针通过这种基地址加偏移来读取数值还是很准确的
作业
1.
#include "stdafx.h"
int main(int argc,char* argv[]){
int a,b,c,d,e;
int* arr[5] = {0};
*arr = &a;
*(arr + 1) = &b;
*(arr + 2) = &c;
*(arr + 3) = &d;
*(arr + 4) = &e;
return 0;
}
//或者
int main(int argc, char* argv[])
{
int num[5] = {1,2,3,4,5};
int* arr[5] = {num,&num[1],&(*(num+2)),num+3,&num[4]};
for(int i = 0;i < 5;i++){
printf("%x ",*(arr+i)); //打印arr数组中的值
}
printf("\n");
for(int j = 0;j < 5;j++){
printf("%d ",**(arr+j)); //打印arr数组中的地址值对应的内存单元中的数值,即num中的值
}
return 0;
}
2.这个还是好好想想吧,当时一做完这个小项目,就感觉这不就和数组指针差不多嘛,都是指向了一个数组,不过数组指针的话,多加了一个可以定义的数组的长度,这样在操作数据的时候就更加灵活了,比如*p++就是加数组的数据宽度,*p 相当于得到了数组的首地址,那么**p 就能读取数据了,但不同就在这里,这里的*p 就能得到数据了,**p 可以继续得数据,下面会验证
// 1208.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
int main(int argc,char* argv[]){
char* keyword[] = {
"auto", //每个常量区中的字符串结尾都有'\0'
"short",
"int",
"long",
"float",
"double",
"char",
"struct",
"union",
"enum",
"typedef",
"const",
"unsigned",
"signed",
"extern",
"register",
"static",
"volatile",
"void",
"if",
"else",
"switch",
"case",
"for",
"do",
"while",
"goto",
"continue",
"break",
"default",
"sizeof",
"return",
0
};
char** p = keyword;
while(*p != 0){
printf("%c\n",*p);
p++;
}
return 0;
}
咱们在此基础上做个小实验哈,证明它和数组指针还是有那么一丝丝联系的,不同的可能就是*p++的宽度不一样,数组指针是可以自定义的,所有它操作数据也就有更多的手段,我们上面代码的 char **p = keyword 何尝不是 p 指向一个数组,然后*p 得到了具体的字符串存储的地址(这步在数组指针得**P 才能做到),然后**p 得具体值
先看原本的**p,就直接得到了常量区的值,这里不能用%s 打印,我就用%c 意思一下
然后再看数组指针的,我稍微改了一下代码
#include "stdafx.h"
int main(int argc,char* argv[]){
char* keyword[] = {
"auto", //每个常量区中的字符串结尾都有'\0'
"short",
"int",
"long",
"float",
"double",
"char",
"struct",
"union",
"enum",
"typedef",
"const",
"unsigned",
"signed",
"extern",
"register",
"static",
"volatile",
"void",
"if",
"else",
"switch",
"case",
"for",
"do",
"while",
"goto",
"continue",
"break",
"default",
"sizeof",
"return",
0
};
int (*p)[1];
p = (int(*)[1])keyword;
printf("%s\n",**p);
printf("-------------------");
while(**p != 0){
printf("%s\n",**p);
p++;
}
return 0;
}
然后下面是运行结果
欸怎么说还是得对%s 理解深刻,它是给个地址,然后开始从这个地址开始打印,大家一定要注意
3.这个还是模拟 CE 查找的功能,但是明显这两个数据是不一样的,所以定义一个结构体指针最合适不过,然后把数据的首地址给这个指针,那么就可以很随心所欲的操作数据了
下面给出我的代码
char data[100] = {
0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07,0x07,0x09,
0x00,0x20,0x10,0x03,0x03,0x0C,0x00,0x00,0x44,0x00,
0x00,0x33,0x01,0x00,0x00,0x08,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x02,0x64,0x00,0x00,0x00,0xAA,0x00,
0x00,0x00,0x64,0x01,0x00,0x00,0x00,0x08,0x00,0x00,
0x00,0x00,0x02,0x00,0x74,0x0F,0x41,0x00,0x00,0x00,
0x01,0x00,0x00,0x00,0x05,0x00,0x00,0x00,0x0A,0x00,
0x00,0x02,0x57,0x4F,0x57,0x00,0x06,0x08,0x00,0x00,
0x00,0x00,0x00,0x64,0x00,0x0F,0x00,0x00,0x0D,0x00,
0x00,0x00,0x23,0x00,0x00,0x64,0x00,0x00,0x64,0x00
};
typedef struct figure{
int id;
int level;
}Player;
void search_struct(int id,int level){
Player* search;
for(int i=0;i<100-7;i++){
search = (Player*)(data+i);
if(search->id == id&&search->level == level){
printf("%x\t%d\t%d",search,id,level);
}
}
}
int main(int argc,char* argv[]){
Player player;
player.id = 1;
player.level = 2;
search_struct(player.id,player.level);
return 0;
}
作者奔溃
啊啊啊啊好累,明天要早睡啊啊啊啊啊啊啊,呜呜呜裂开,讨厌 study
奔溃了