zoukankan      html  css  js  c++  java
  • 结构体的字节对齐(跨语言传参时尤为重要)

    在项目开发时,结构体作为一个数据结构,非常适合用语存储某一设备或某一类事务的信息,自然的,将其用作参数也是必然的事。然而在将结构体作为参数生成DLL后,用其他语言调用时,则会有一些问题。比如在用C#调用C++的DLL时,结构体的大小就会有不一样,导到调用出问题。

    基于以前的BCB开发的产品,需要增加一些接口以实现新的功能,于是将相应的信息封装到了结构体中,然后传出。但是当我用C#调用时,发现怎么调都不对,后来用了C#调用C++DLL传递结构体数组的终极解决方案所说的,用最蛮力的办法,开了一个大的byte数组去接收(数组最好能基于DLL的结构体估算一下大小),数据接收成功了,也分析得到了想要的数据。之后再细加分析,发现原来BCB导出DLL时调用的结构体大小和我们用C#的Marsh弄出来的大小有差异,C#的小了2个字节,于是我就强行补了2个字节进去,最终算是达到了目的。

    不过想想就这样,还是觉得有问题。因为这个接口最终可能会发布,供第三方调用。要是他们用其他的语言呢?第三方可不一定会知道是这个原因的,所以想着有没有办法可以解决。经过测试分析,发现是结构体的字节对齐问题。以下结构体是基于win7 32下BCB编写与测试的。

    struct Info
    {
        int OrderNO;
        float CpuPercent;
        char UniqueCode[33];    
    };
    
    void __fastcall TForm1::btn1Click(TObject *Sender)
    {
         int len=sizeof(Info);
         ShowMessage(len);
    }

    测试Info的长度是44,可是我们自己一个一个变量计算时,发现并不是。OrderNO是int为4字节,CpuPercent是float为4字节,UniqueCode为char数组共33个字节,合起来是41个字节。那多出来的3个字节是哪多出来的呢?

    经过查找资料,提到说是字节对齐的问题。所谓字节对齐,就是每个变量在内存中的以多少个字节作为数据块存储。现在测出来是44个字节,那只能是1、2、4、11的倍数,11的倍数显然不可能,那会不会是4的倍数呢?

    若以4字节对齐,那么OrderNO是4个字节,正好可以存储。CpuPercent也是4个字节,也正好可以存储。而UniqueCode是33字节,以4字节为块进行存储,那么需要9个块。而9个块则有4*9=36个字节,比char数组分配的33个字节多了3个字节。这么说来,这多出来的3个字节就是这里多出来的。那又要如何去证实想法呢?

    有了!我们不妨把UniqueCode变成36的char数组,看是不是也是44个字节。结构体修改如下。

    struct Info
    {
        int OrderNO;
        char UniqueCode[36];
        float CpuPercent;
    };

    经测试,新的Info结构体确实是44个字节。那么依此类推,如果UniqueCode开辟的数组为33、34、35、36时,其Info结构的字节大小都是44字节。经过测试,确实是如此。所以最终证明,这多出来的3个字节,其实是因为数据块是以4字节来分配的。

    那为什么又要以4字节来分配呢?难道是因为结构体中的变量第一个是整型,所以用第一个作为字节对齐的标准吗?这么想了之后,还是得证明。那不妨把UniqueCode和OrderNO的顺序对换一下,新的结构体如下。

    struct Info
    {
        char UniqueCode[33];
        int OrderNO;
        float CpuPercent;
    };

    经测试,Info结构体还是44个字节。奇怪了,难道不是以第一个类型作为参照吗?那会是以什么?难道是以数组中最多字节的数据类型作为参照?那有什么数据类型是比int的字节要多的?有了,double类型,果断改之。

    struct Info
    {
        char UniqueCode[33];
        double OrderNO;
        float CpuPercent;
    };

    经测试,Info结构体大小为56个字节,正好是double所占8字节的倍数。我们分析分析,UniqueCode是33字节,按8字节对齐,需要5个块,即5*8=40字节。OrderNO是8个字节。CpuPercent虽然是float为4个字节,但要按8字节对齐,所以还是占了8字节。合起来40+8+8=56字节,这么说BCB编译器是默认以结构体中所占字节最多的为字节对齐的。

    这时可能有人要问了,那UniqueCode是char数组,不是33个字节,要比double的8个字节要大吗?为什么不以UniqueCode作为字节对齐的参照?是的,我也曾有这种疑惑。不过再想想,问题就不难了。int、float、double类型是一个整体,如果要把数据给完整的保留,那么就得分配连续的空间来存储。而char数组虽说为数组,但其本质上是以char的1字节存储的,所以在结构体中占字节最多时,char数组只是以1字节计算的。若是还觉得怀疑,可 将CpuPercent改成数组,比如。

    struct Info
    {
        char UniqueCode[33];
        double OrderNO;
        float CpuPercent[4];
    };

    测出的长度为64,UniqueCode点了40字节和OrderNO占了8字节,CpuPercent占了16字节。因为CpuPercent每个都是float为4字节,一个8字节的数组块可以存上两个float。所以会发现,BCB是以结构体中的基本数据类型所占字节最多的作为字节对齐参照的。

    那有没有什么方式可以让空间就如我们所写的那样占字节呢。比如

    struct Info
    {
        int OrderNO;
        float CpuPercent;
        char UniqueCode[33];
    };

    我希望Info所占的字节数是OrderNO为4个,CpuPercent为4个,UniqueCode为33个,合起来是4+4+33=41个。答案是有的。只要我给结构体强制指定一个字节对齐的字节数即可,代码如下。

    #pragma pack(push,1)
    struct Info
    {
        int OrderNO;
        float CpuPercent;
        char UniqueCode[33];
    };
    #pragma pack(pop)

    该结构体测试后,大小为41字节,正是我们所要的。其中#pragma pack(push,1)中的1是表示对齐字节的方式,但必须是系统所能存储的,比如3就会报错。#pragma pack(pop)是表示结束了。

    当结构体的字节如我们所定义各个变量所计算的字节之和时,那么对于跨语言调用,就可以消除不同语言不同编译器调用结构体参数时大小不同的障碍,很多问题也就可以随之解决了。

    转载请注明出处http://blog.csdn.net/xxdddail/article/details/11808253

  • 相关阅读:
    sqlplus时报Linux-x86_64 Error: 13: Permission denied
    thrift之TTransport层的缓存传输类TBufferedTransport和缓冲基类TBufferBase
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 新建Microsoft world文档
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 快乐司机
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 队列操作
    Java实现 蓝桥杯 算法提高 文本加密
    Java实现 蓝桥杯 算法提高 合并石子
  • 原文地址:https://www.cnblogs.com/james1207/p/3328894.html
Copyright © 2011-2022 走看看