文章

滴水-day13-结构体-字节对齐

结构体

定义结构体

struct name
{
    int a;
    char b;
    short c;
    char s[20];
}

结构体与int,short,char....是同级的,只是这些内置的数据类型无法满足需求。

结构体中可以存储除自身外的任何数据类型。

结构体的使用

name AA; // 声明一个 name 类型的数据
AA.a = 10; // 给AA中的a属性赋值
AA.b = 20;
AA.c = 30;
AA.s[0] = 1; // 给AA中s属性的第一个元素赋值

结构体传参数

// 结构体定义
struct coord
{
	int x;
	int y;
	int h;
};

void testStructParr(coord cd)
{

}

// 传递参数
coord cd;
cd.x = 10;
cd.y = 20;
cd.h = 30;
testStructParr(cd);

002E1A7F  mov         dword ptr [ebp-10h],0Ah
002E1A86  mov         dword ptr [ebp-0Ch],14h
002E1A8D  mov         dword ptr [ebp-8],1Eh
002E1A94  sub         esp,0Ch
002E1A97  mov         eax,esp
002E1A99  mov         ecx,dword ptr [ebp-10h]
002E1A9C  mov         dword ptr [eax],ecx
002E1A9E  mov         edx,dword ptr [ebp-0Ch]
002E1AA1  mov         dword ptr [eax+4],edx
002E1AA4  mov         ecx,dword ptr [ebp-8]
002E1AA7  mov         dword ptr [eax+8],ecx
002E1AAA  call        002E13C5

结构体传递参数,当我向coord结构体传递三个参数的时候反汇编是这样的。

首先在main函数中为这个三个参数分配了空间,可以看作是局部变量。

esp减去了c个字节,也就是将esp向上提升了c个字节,这些字节刚好够放下参数。

把esp赋值给了eax,也就是新的栈顶

使用mov依次对eax内存地址进行赋值,每赋值一次eax就+4直到参数传递完毕。

!https://cdn.nlark.com/yuque/0/2024/jpeg/39210623/1721894575502-3ef26679-b489-4395-8a33-a21007e357f2.jpeg

这样就完成了参数的传递,虽然没有push指令。

结构体返回值

coord testScructRet()
{
	coord cd;
	cd.x = 10;
	cd.y = 20;
	cd.h = 30;
	return cd;
}

coord cd2 = testScructRet();

005E5432  lea         eax,[ebp-10Ch]
005E5438  push        eax
005E5439  call        testScructRet (05E13CAh)
005E543E  add         esp,4
005E5441  mov         ecx,dword ptr [eax]
005E5443  mov         dword ptr [ebp-0F8h],ecx
005E5449  mov         edx,dword ptr [eax+4]
005E544C  mov         dword ptr [ebp-0F4h],edx
005E5452  mov         eax,dword ptr [eax+8]
005E5455  mov         dword ptr [ebp-0F0h],eax
005E545B  mov         ecx,dword ptr [ebp-0F8h]
005E5461  mov         dword ptr [cd2],ecx
005E5464  mov         edx,dword ptr [ebp-0F4h]
005E546A  mov         dword ptr [ebp-20h],edx
005E546D  mov         eax,dword ptr [ebp-0F0h]
005E5473  mov         dword ptr [ebp-1Ch],eax

005E5432 lea eax,[ebp-10Ch]

005E5438 push eax

首先在main函数中通过ebp-10C去地址编号放到eax中,在之前的学习中可以知道ebp-??是存放局部变量的地方。把eax压入栈中,这里很明显是在准备局部变量空间用来接收数据。10C是局部变量最大存储位置

testScructRet函数

005E41C5  mov         dword ptr [ebp-10h],0Ah
005E41CC  mov         dword ptr [ebp-0Ch],14h
005E41D3  mov         dword ptr [ebp-8],1Eh
005E41DA  mov         eax,dword ptr [ebp+8]
005E41DD  mov         ecx,dword ptr [ebp-10h]
005E41E0  mov         dword ptr [eax],ecx
005E41E2  mov         edx,dword ptr [ebp-0Ch]
005E41E5  mov         dword ptr [eax+4],edx
005E41E8  mov         ecx,dword ptr [ebp-8]
005E41EB  mov         dword ptr [eax+8],ecx
005E41EE  mov         eax,dword ptr [ebp+8]

三个局部变量0AH,14H,1EH。取ebp+8放到eax,这里的ebp+8其实就是刚才push的eax,也就是main函数局部变量最大空间的地址。

接下来就是通过大量的mov 把testScructRet的 局部变量放到寄存器中,再通过寄存器放到eax中。这样就完成了的了对main局部变量的赋值,也就相当于返回值了。

day13.cpp

字节对齐,结构体数组

Sizeof关键字

sizeof可以获取数据类型的大小,可以通过数据类型名称或者是变量名称获取。

sizeof是关键字不是函数,只是用起来像一个函数。

字节对齐

在结构体中分配空间的依据是跟进字节对齐的大小进行分配的

默认字节对齐是8字节,也就是说以8字节对齐为标准

可以使用:#pragma pack(n)

结构体

\#pragma pack

n可以是1,2,4,8.中的任意一个

结构体的顺序不同,分配空间大小也不同。

原则一:数据成员对齐规则:结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储).

原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。 (struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储.)

原则四:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准.

也就是说,结构体成员的偏移量应该取二者的最小值.

例如:

struct aa
{
	int a;
	char b;
	short c;
}

默认是8字节:

a a a a

b 0 c c

1字节:

a a a a

b c c

2字节:

a a a a

b 0 c c

4字节:

a a a a

b 0 c c

Typedef

typedf关键字是对一种数据类型取一个新的名字,包括内置的数据类型。

typedef unsigned int DWORD
typedef unsigned char BYTE
typedef unsigned short WORD

typedef struct student
{
	int a;
	int	b;
	int c;
}stu;

字符数组赋值

char arr[10]={0};

arr[0]='a';

arr[1]='b';

arr[2]='c';

arr[3]='\0';

通过内置函数可以快速进行赋值

strcpy(arr,"abc");

结构体数组

就是定义一个结构体类型的数组罢了,每个下标表示一个结构,通常用来存储大量信息且有重复的数据。

typedef struct coordinate // 8
{
	int x;
	int y;
}coord;

typedef struct Monster // 44
{
	int id;
	int hp;
	int gongji;
	int yidong;
	char monName[20];
	coord cd;
}mons;

// 2、声明一个Monster类型的数组,长度为10.
mons monsInfo[10];

练习

typedef struct coordinate // 8

{
int x; // 4
int y; // 4
}coord;
typedef struct Monster // 44 20+8+16=44
{
int id; // 4
int hp; // 4
int gongji; // 4
int yidong; // 4
char monName[20]; 16 + 44440000 = 44444444 = 16+8
coord cd; // 8-4 = 4
}mons;

// 2、声明一个Monster类型的数组,长度为10.
mons monsInfo[10];

License:  CC BY 4.0