(C语言完结)结构体在汇编中的表现形式

一丶了解什么是结构体,以及计算结构体成员的对其值以及总大小(类也是这样算)

结构体的特性

  1.结构体(struct)是由一系列具有相同类型或不同类型的数据构成的数据集合

  2.在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。

  1. 结构体可以被声明为变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用名字访问。[1]
    高级代码:

struct TagList
{
char ch;
int number1;
short int number2;
double dbl;
float flt;
};

上面就是一个简单的结构体,那么我们这个结构体在内存中的偏移要怎么计算.

公式:

下面是推理,如果不想看可以直接跳到总结去看总结.

  成员偏移量的公式

alg 设alg是编译器的对其值,offset为结构体首地址的偏移,从0开始.

Member offset % min(alg,sizeof(member type) == 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的位置

那么现在的offset = 1,继续代入公式

1 % min(4,4) == 0,不成立,偏移继续++

2%min(4,4) == 0,不成立,偏移继续++

…..

一直到偏移为4的时候满足,所以 偏移为4的地方,放number1             +4 4

计算 number2所在的偏移

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) == 0,不成立

12%(4,8) == 0;成立,所以在                           +12 8

计算float的位置

offset % min(4,sizeof(member type)) == 0;

20 % min(4,4) == 0; 成立                          +20 4

那么各成员的偏移已经计算出来了.

其中float成员位与结构体的 +20偏移,占4个字节大小.

计算结构体总体大小

  公式:

  sizeof(struct) % min( Max type size,alg);

结构体的大小我们上面计算出来了,是 24个字节

MAX type,是结构体中最大成员的数据类型大小, 现在是double,也就是8个字节

alg是编译器对齐值,现在是4

所以代入公式得到

  24 % 4 == 6…0

所以总体的大小是24个字节.

总结:

  编译器对齐值,设置为 alg, MeMber offset 从0开始计算, 其中Member offset 要每次代入公式之后,加上自己成员所占的字节大小,继续参与下次运算.

  设置或者查看编译器对其值, VC6.0版本 Project (工程) -> Settings(设置) -> C/C++ -> Category(种类) -> Code Generation(代码生成) -> Struct Member alignment(结构体对齐值)

  结构体成员偏移计算公式: MeMber offset % min(alg,sizeof(Member type)) == 0

  结构体总大小计算公式:   sizeof(struct) % min(Max type size,alg) == 0;

程序内存查看.

根据内存窗口赋值,可以得出结构体成语位与结构体的偏移是多少

第一个成员, +0 偏移位置, 占1个字节

第二个成员, +4 偏移位置, 占4个字节

第三个成员 +8 偏移位置, 占2个字节

第四个成员 +12偏移位置,占8个字节

PS: 其中成员的Member offset 从零开始,当计算完毕之后,需要加上自己所占的字节大小,然后继续参与运算,如果运算不成立,则偏移继续增加,一直到偏移成立

比如:

  比如我们计算第二个成员位置的偏移

公式:

  Member offset % min(alg,sizeof(member type size) == 0;

  0 % 1 == 0 +0 放第一个成员

Member offset = Mmeber offset + 占的字节大小,(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("%drn",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

Release下的汇编

main函数调用传递结构体地址的时候,只需要三行汇编

lea eax,[esp + 20h + Var_20]
push eax
call MyFun
上面都是流水线优化的汇编

看下MyFun内部

其结构和Debug差不多

1.获得结构体的首地址

2.+0偏移位置赋值字符

3.+4偏移位置,赋值为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("%drn",text.number1);
return 0;
}

Debug下的汇编

传参之前的操作

很明显

1.先抬栈

2.循环6次,每次4个字节4个字节的拷贝

3.获得结构体的首地址

4.将栈顶赋值给edi,意思就是说,从栈顶开始复制.

5.执行串操作指令,rep movsd 将 esi的内容复制到栈顶位置处,

因为要复制 24个字节,所以栈顶要+24所以这一段就是存储结构体成员的.

MyFun内部

经过传参之后,esp的位置为数组首地址的,也就是+0位置偏移处
2.进入函数后压入返回地址,那么栈 esp -4, 然后push ebp,继续esp -4

3.mov ebp,esp,保存寻址,现在的ebp + 8正好是外面我们进行串拷贝的时候的结构体的首地址.

4.mov byte ptr[ebp +8],62h,相当于就是给我们结构体成员的 +0成员赋值

5.mov dword ptr[ebp + 0ch],2 则正好是我们的第二个成员.

所以为了解释这两句汇编代码,需要通过外面传参的栈环境来看.

Release下的汇编

和Debug下一样,也是要进行串拷贝

MyFun函数内部

发现我们没有使用,所以直接给优化了.

三丶函数返回值为结构体的时候

1.返回为指针的时候,直接放到eax中

返回值,为结构体的情况

三种情况

1.当结构体大小小于(4这个数不确定)个字节,直接用eax返回

2.当结构大小小于(8这个数不确定)个字节,直接用 edx,eax返回

3.当结构体大小大于 8个字节以上(不确定,视编译器而决定).

最后一种的高级代码:

struct TagList MyFun()
{
struct TagList text =
{
‘a’,
1,
2,
3.0,
4.0,
};
return text;
}
int main(int argc, char* argv[])
{
struct TagList text;
text = MyFun();
printf(“%crn”,text.ch);
return 0;
}

Debug下的汇编代码

1.我们的函数没有参数,但是Debug会生成上面的代码,传入进入, 为什么? 因为返回值eax等等都装不下了,所以利用这块内存区域当做返回值

2.函数退出之前,也会对它进行串操作指令,因为要返回这块内存区域,所以写入内存.

3.返回值以前会把首地址给 eax保存

4.看外面是否使用eax,如果使用可以可以判断返回的是一个对象,(当然这一步可以省略,但是上面的三步少一步都不是返回对象)

参数问题:

  它会默认给我们生成一个参数传入,那么我们有了参数,则会跟在后面.

Release汇编代码一样.

更多优质文章请访问博悦 hongshulin001.com

时间: 2024-09-15 12:04:51

(C语言完结)结构体在汇编中的表现形式的相关文章

c语言,对结构体中的整型数组进行赋值.....

问题描述 c语言,对结构体中的整型数组进行赋值..... c语言中,在结构体里声明整型数组,想对整型数组赋值,只能用循环吗?如果我想这样呢..... typedef struct Data { int arr[10]; }Data; int main() { Data data; data = (Data)malloc(sizeof(Data)); data->arr[10]={1,3,2,4,5,6,7,8,9,0}; . ... ... ... } 这样可以吗? 解决方案 绝对不可以.数组作

c程序设计-C语言截结构体指针中的变量又是另一个结构体指针,怎么用,代码运行不了

问题描述 C语言截结构体指针中的变量又是另一个结构体指针,怎么用,代码运行不了 #include #include #include #include #define LIST_INIT_SIZE 100//线性表存储空间的初始分配量 #define LISTINCREMENT 10//线性表存储空间的分配增量 typedef struct { int No; char name[10]; }Student; typedef struct { Student *elem;//存储空间基址 int

C#调用C语言结DLL 结构体不会封装,怎么在C#中对应C语言的结构体呢

问题描述 //决策树节点typedefstruct{char*RecordData[1000][100];//训练数据intRecord_Number;/*训练数据的行数*/intAttribute_Number;/*训练数据的列数*/}TrainDataNode;typedefstructDecisionTreeNode{intkeynum;charSelectedAttributeName[30];//选择的属性名charAttributeValue[30];//属性值intRecord_M

详解C语言的结构体中成员变量偏移问题_C 语言

c语言中关于结构体的位置偏移原则简单,但经常忘记,做点笔记以是个记忆的好办法 原则有三个: a.结构体中的所有成员其首地址偏移量必须为器数据类型长度的整数被,其中第一个成员的首地址偏移量为0, 例如,若第二个成员类型为int,则其首地址偏移量必须为4的倍数,否则就要"首部填充":以此类推 b.结构体所占的总字节数即sizeof()函数返回的值必须是最大成员的长度的整数倍,否则要进行"末尾填充": c.若结构体A将结构体B作为其成员,则结构体B存储的首地址的偏移量必须

【Go语言】【12】GO语言的结构体

原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.http://qingkechina.blog.51cto.com/5552198/1671463        当我第一次接触到C语言时,就对结构体投入了极大的兴趣,认为这个东西以后大有作为,后来接触Java.C++,面向对象编程中的对象进入我的视线,经过了这么多年的磨练,回过头来再看结构体依旧是那么亲切:同时从另一个角度上看结构体与面向对象中的成员对象是多么的相像 :) 一.结构体元素 结

C语言的结构体和C++结构体的区别

关于C++中声明结构体中需要使用构造器创建实例对象的语法: <C++的结构体构造方法的基本概念:结构体的构造方法需要和结构体的名字相同,并且无返回值,也不要void关键字,这样的方法就是构造器的初始化方法> 接着下面两个代码截图(一个是C源码,一个是C++源码)对比你就初步体会到C语言的结构体和C++结构体的区别了:        对于右边的C++结构体的使用类似Java,C++,Swift中的类,类中有构造器方法,然后构造器创建这个类的实例对象. 当然Swift中也有一样用法的结构体.毕竟S

c语言-一道C语言用结构体排序的题目 大神快来帮我理解下人家的代码~~~

问题描述 一道C语言用结构体排序的题目 大神快来帮我理解下人家的代码~~~ 描述 小王是公司的仓库管理员,一天,他接到了这样一个任务:从仓库中找出一根钢管.这听起来不算什么,但是这根钢管的要求可真是让他犯难了,要求如下: 1. 这根钢管一定要是仓库中最长的: 2. 这根钢管一定要是最长的钢管中最细的: 3. 这根钢管一定要是符合前两条的钢管中编码最大的(每根钢管都有一个互不相同的编码,越大表示生产日期越近). 相关的资料到是有,可是,手工从几百份钢管材料中选出符合要求的那根-- 要不,还是请你编

C++ 语言关于结构体的排序的功能

问题描述 C++ 语言关于结构体的排序的功能 结构体由多个字段构成,要求按照先后次序对字段排序,用C++语言实现,怎么才能简单高效可靠 解决方案 参考:http://blog.csdn.net/lethic/article/details/7781203 解决方案二: GO语言为结构体排序c++ 结构体排序结构体优先队列排序 解决方案三: 根据结构体中的某个字段排序?

struct-C语言关于结构体赋值的问题,请教

问题描述 C语言关于结构体赋值的问题,请教 现在有一内存结构 如下: typedef struct { char classname[128]; char name[32]; unsigned int id; char X[128]; int X_len; }class_t; 现在有很多组数据要向该结构体赋值.但是 X[128] 这个变量是不可见字符 如0x20,0x03,0x04等登构成的. 那么我现在按照 class_t Temp[] = { {"One", "OneNa