首先,了解结构是什么,并计算结构成员的值和总大小(类也是如此)
结构特征
1. 结构是由一系列相同或不同类型的数据组成的数据集
2. 在C语言中,struct是指数据结构,它是C语言中的一种聚合数据类型.
3. 可以将结构声明为变量,指针或数组等,以实现更复杂的数据结构. 结构也是元素的集合. 这些元素称为结构的成员,并且这些成员可以具有不同的类型. 通常按名称访问成员.
高级代码:
structTagList
{charch;intnumber1;short intnumber2;doubledbl;floatflt;
};
上面是一个简单的结构,然后介绍如何计算我们的结构在内存中的偏移量.
公式:
以下是原因,如果您不想看到它,可以跳至摘要以查看摘要.
成员补偿的公式
alg设alg为编译器的值,offset是结构的第一个地址的偏移量,从0开始.
成员偏移量%min(alg,sizeof(成员类型)== 0;该公式用于在结构的第一个地址处查找成员的偏移量
例如,要计算成员的flt位与结构的第一个地址之间的偏移量,我们必须首先从第一个成员进行计算
将alg对齐值设置为4
offset%min(4,sizeof(ch))== 0;
0%min(4,1)== 0结论是ch变量位于结构的第一个地址的偏移量为0处,占1个字节+0 1
offset%min(4,sizeof(number1))== 0
因为上面计算出ch的大小,它占用了1个字节,所以偏移量+1变成了1的位置
然后现在偏移量= 1,继续替换公式
1%min(4,4)== 0,不是,偏移继续++
2%min(4,4)== 0,不是,偏移继续++
.....
直到偏移量为4为止都满足,因此偏移量为4时,放置number1 + 4 4
计算数字2的偏移量
offset%min(4,sizeof(member type))== 0;
8%min(4,2)== 0,建立+8 2
计算dbl的位置
offset%min(4,sizeof(member type))== 0;
10%(4,8)== 0,不是真的
11%(4,8)== 0c 计算结构体大小,不是真的
12%(4,8)== 0;确定,所以在+12 8
计算浮点位置
offset%min(4,sizeof(member type))== 0;
20%min(4,4)== 0;正确+20 4
然后计算每个成员的偏移量.
float成员位从结构中偏移+20,并占用4个字节.
计算结构的总体尺寸
公式:
sizeof(struct)%min(最大字体大小,alg);
我们上面计算的结构大小为24个字节
MAX类型是结构中最大成员的数据类型大小,现在是两倍,即8个字节
alg是编译器对齐值,现在为4
所以用公式代替
24%4 == 6 ... 0
因此,总大小为24个字节.
摘要:
编译器对齐值设置为alg,MeMber偏移量从0开始计算,其中每次需要将成员偏移量替换为公式,再加上其自身成员占用的字节大小,继续参与下一个操作.
设置或查看VC6.0版本编译器的值项目->设置-> C / C ++->类别(类型)->代码生成(代码生成)->结构成员对齐(结构对齐值)<
结构成员偏移的计算公式: MeMber偏移%min(alg,sizeof(成员类型))== 0
用于计算结构总大小的公式: sizeof(struct)%min(最大类型大小,alg)== 0;
查看程序存储器.
根据内存窗口分配,您可以获取结构成语与结构之间的偏移量
第一个成员,偏移位置为+0,占1个字节
第二个成员,+ 4偏移位置,4个字节
第三个成员+8偏移位置,占用2个字节
第四个成员+12个偏移位置,占8个字节
PS: 成员的成员偏移从零开始. 计算完成时,需要增加占用的字节大小,然后继续参与操作. 如果未建立操作,则偏移量会继续增加,直到建立偏移量为止.
例如:
例如,我们计算第二个会员位置的偏移量
公式:
成员偏移量%min(alg,sizeof(成员类型大小)== 0;
0%1 == 0 +0输入第一个成员
成员偏移=成员偏移+字节大小,(1)
寻求第二位成员的职位
1%4 == 0;当偏移量为1时,不正确,偏移量会继续增加
2%4 == 0,如果没有建立,则继续增加
3%4 == 0,如果没有建立,则继续增加
4%4 == 0;建立,因此在+4位置放4个字节,这是第二个成员位置.
二,当结构作为参数传递并且是指针时
void MyFun(struct TagList *pThis)
{
pThis->ch = 'b';
}int main(int argc, char*argv[])
{struct TagList text ={'a',1,2,3.14,0.0};
MyFun(&text);
printf("%d\r\n",text.number1);return 0;
}
在Debug下汇编代码
生成寻址公式,其中eax是数组的第一个地址,ebp +8是参数,外部是结构的第一个地址,因此ebp +8是数组的第一个
所以ebp +8是结构的第一个地址
mov byte ptr [eax],这句话62h直接产生+0位置偏移,并且为内容分配了字符
mov ecx,[ebp + 8]
mov dword ptr [ecx +4],2这句话产生+4的偏移量分配2,因此可以确定
1. 结构第一个地址ebp + 8(参数1)
2. 结构+0的第一个成员的偏移量被分配了一个字符
3. 结构+4的第二个成员的偏移量分配为2
根据发布进行编译
当主函数调用结构地址时,只需要三行汇编
lea eax,[esp + 20h +Var_20]
push eax
call MyFun
以上是流水线的优化
看看MyFun内部
结构类似于Debug
1. 获取结构的第一个地址
2. +0偏移位置分配字符
3. + 4偏移位置c 计算结构体大小,分配值为2
三,将结构作为参数传递时,就结构本身而言
高级代码:
void MyFun(struct TagList pThis) //这个地方变了.不是指针了
{
pThis.ch= 'b';
pThis.number1= 2;
}int main(int argc, char*argv[])
{struct TagList text ={'a',1,2,3.14,0.0};
MyFun(text);//传参不用取地址了
printf("%d\r\n",text.number1);return 0;
}
在调试下进行组装
传递参数之前的操作
很明显
1. 首先提起纸叠
2. 循环6次,每次4字节4字节复制
3. 获取结构的第一个地址
4. 将堆栈顶部分配给edi,这意味着从堆栈顶部进行复制.
5. 执行字符串操作指令,rep movsd将esi的内容复制到堆栈的顶部位置,
由于要复制24个字节,因此堆栈的顶部必须为+24,因此此部分是存储结构的成员.
在MyFun内部
1. 传递参数后,esp的位置是数组的第一个地址,即+0 position的偏移量
2. 进入函数后按返回地址,然后堆栈esp -4,然后按ebp,继续esp -4
3.mov ebp,esp,保存寻址,当前的ebp + 8只是在外部进行字符串复制时结构的第一个地址.
4.mov字节ptr [ebp +8],62h,相当于为我们的结构成员分配+0个成员
5.mov dword ptr [ebp + 0ch],2只是我们的第二个成员.
因此,为了解释这两行汇编代码,您需要查看在外部传递参数的堆栈环境.
根据发布进行编译
像调试一样,字符串复制也是必需的
MyFun内部功能
我发现我们没有使用它,因此我们直接对其进行了优化.
三,当函数返回值是结构时
1. 作为指针返回时,请将其直接放在eax中
返回值是结构的情况
三种情况
1. 当结构的大小小于(4个数量不确定)字节时,请使用eax直接返回
2. 当结构的大小小于(8个数量不确定)字节时,直接使用edx,eax返回
3. 如果结构的大小大于8个字节(不确定,取决于编译器).
最后一种高级代码:
structTagList MyFun()
{struct TagList text ={'a',1,2,3.0,4.0,
};returntext;
}int main(int argc, char*argv[])
{structTagList text;
text=MyFun();
printf("%c\r\n",text.ch);return 0;
}
在Debug下汇编代码
1. 我们的函数没有参数,但是Debug会生成上面的代码并传入,为什么?由于无法安装返回值eax等,因此请将该存储区域用作返回值
2. 在函数退出之前,它还将在其上执行字符串操作指令,因为它需要返回到该存储区,因此将其写入内存.
3. 返回值之前,第一个地址将保存到eax
4. 查看eax是否在外部使用,如果可以使用,则可以判断返回的对象是一个对象(当然,可以省略此步骤,但是上面的三个步骤和一个步骤都不是返回的对象)
参数问题:
默认情况下,它将为我们生成一个参数,然后,如果我们有一个参数,它将紧随其后.
发布的汇编代码是相同的.
转载于:
作者: IBinary
来源:
本文来自电脑杂谈,转载请注明本文网址:
http://www.pc-fly.com/a/jisuanjixue/article-244953-1.html