struct 订阅
struct即结构体,亦被直接称为“结构”。实际编程时,经常需要用相关的不同类型的数据来描述一个数据对象。例如,描述学生的综合信息时,需要使用学生的学号、姓名、性别、成绩以及家庭住址等不同类型的数据。但是,用相关的不同类型的数据来描述一个数据对象会使编程极为不便。因此,C语言提供了一种称为结构体(struct)的数据类型,以描述需要不同类型数据的数据对象 [1]  。 展开全文
struct即结构体,亦被直接称为“结构”。实际编程时,经常需要用相关的不同类型的数据来描述一个数据对象。例如,描述学生的综合信息时,需要使用学生的学号、姓名、性别、成绩以及家庭住址等不同类型的数据。但是,用相关的不同类型的数据来描述一个数据对象会使编程极为不便。因此,C语言提供了一种称为结构体(struct)的数据类型,以描述需要不同类型数据的数据对象 [1]  。
信息
别    称
结构
类    别
C/C++关键词
构    成
由若干“成员”组成
释    义
一种构造数据类型
中文名
结构体
外文名
Structure type
struct构成
“结构”是一种构造类型,它是由若干“成员”组成的。 每一个成员可以是一个基本数据类型或者又是一个构造类型。 结构即是一种“构造”而成的数据类型, 那么在说明和使用之前必须先定义它,也就是构造它。如同在说明和调用函数之前要先定义一样。  “结构体”是由一些逻辑相关的数据栏或称为“字段”(Field)所构成。例如,一位学生的学号、姓名、生日……即是一条记录,该结构体拥有“姓名”栏、“语文”栏、“英文”栏,而一群学生的记录集合就是一个结构体数组 [2]  。
收起全文
精华内容
下载资源
问答
  • C语言结构体(struct)常见使用方法

    万次阅读 多人点赞 2014-04-14 01:51:57
    今天复习一下struct,顺便挖掘一下以前没注意的小细节: 基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法...

    注意:盗版是不会得到修正和更新的!

    今天复习一下struct,顺便挖掘一下以前没注意的小细节:

    基本定义:结构体,通俗讲就像是打包封装,把一些有共同特征(比如同属于某一类事物的属性,往往是某种业务相关属性的聚合)的变量封装在内部,通过一定方法访问修改内部变量。具体一点说,结构体是让一些很散的数据变得很整,不管是网络传输,还是函数传参,还是为了便于你肉眼管理。

    一个函数,你想传入一个参数void func(),就需要改一下函数定义,加一个数据类型和数据名void func(int i);又想加一个参数,又改一遍void func(int i,double b);如此往复。但是用一个结构体(或者类对象)传入,这个函数定义就可以不改动了,只改结构体就好了,比如一个游戏,你的人物属性有成百上千,你只需要修改你的类与结构体成员就好了。

    (因为C++和C有共通之处,但是在结构体上的某些机制又有所不同,所以后边提了一下C++得东西,不喜欢可以略过,但是2021年了,用纯C的人估计要消失了吧,尤其新人)

    结构体声明与定义:

    第一种:只有结构体定义

    struct stuff{
            char job[20];
            int age;
            float height;
    };

    第二种:附加该结构体类型的“结构体变量”的初始化的结构体定义

    //直接带变量名Huqinwei
    struct stuff{
            char job[20];
            int age;
            float height;
    }Huqinwei;

    也许初期看不习惯容易困惑,其实这就相当于两步合并一步:先定义结构体stuff,再定义变量Huqinwei

    struct stuff{
            char job[20];
            int age;
            float height;
    };
    struct stuff Huqinwei;
    

    第三种:如果该结构体你只用一个变量Huqinwei,而不再需要用

    struct stuff yourname;

    去定义第二个变量。

    那么,附加变量初始化的结构体定义还可进一步简化出第三种

    把结构体名称去掉,用匿名结构体直接定义一个结构体对象(习惯用对象这词了,大家都要习惯,没纯C了),这样更简洁,不过也不能定义其他同类型结构体变量了——除非用typeof再逆向找到这个类型。

    struct{
            char job[20];
            int age;
            float height;
    }Huqinwei;

    第三种附加:使用typeof重新利用HU的结构体定义HU3

    并且定义指针ptr1,ptr2

    #include <stdio.h>
    
    struct
    {
            char a;
            short b;
            int c;
    }HU;
    
    struct
    {
            char a;
            short b;
            int c;
    }HU2;
    
    int main(){
    
            printf("%ld\n",sizeof(HU));
    
            typeof(HU) HU3;
            printf("%ld\n",sizeof(HU3));
            printf("%ld\n",sizeof(HU2));
            typeof(HU) *ptr1 = &HU;
            typeof(HU) *ptr2 = &HU3;
            ptr2->b = 444;
            printf("%d\n",ptr2->b);
            ptr1 = ptr2;
            printf("%d\n",ptr1->b);
    
    
    }
    

    同样的写法,再定义一个结构体成员HU2,他们的“类型”不同,因为如果类型相同,肯定会报错了,实际并没有报。

    不过内存操作角度,HU2和HU应该没有任何区别,也可以用指针强行更改,前提是确认安全,比如没有不同文件不同平台对齐不兼容这种问题,所以C很万能,也很危险

    结构体变量及其内部成员变量的定义及访问:

    绕口吧?要分清结构体变量和结构体内部成员变量的概念。

    就像刚才的第二种提到的,结构体变量的声明可以用:

    struct stuff yourname;

    其成员变量的定义可以随声明进行:

       struct stuff Huqinwei = {"manager",30,185};
    

    也可以考虑结构体之间的“赋值”(拷贝构造):

            struct stuff faker = Huqinwei;
    //或    struct stuff faker2;
    //      faker2 = faker;
    打印,可见结构体的每一个成员变量一模一样
    

    如果不使用上边两种方法,那么成员数组的操作会稍微麻烦(用for循环可能好点)

            Huqinwei.job[0] = 'M';
            Huqinwei.job[1] = 'a';
            Huqinwei.age = 27;
            Huqinwei.height = 185;
    

    结构体成员变量的访问除了可以借助符号".",还可以用"->"访问(下边会提)。

    引用(C++)、指针和数组:

    首先是引用和指针:

    int main()
    {
            struct stuff Huqinwei;
    
            struct stuff &ref = Huqinwei;
            ref.age = 100;
            printf("Huqinwei.age is %d\n",Huqinwei.age);
            printf("ref.age is %d\n",ref.age);
    
            struct stuff *ptr = &Huqinwei;
            ptr->age = 200;
            printf("Huqinwei.age is %d\n",Huqinwei.age);
            printf("ptr->age is %d\n",ptr->age);
    //既然都写了,把指针引用也加上吧
            struct stuff *&refToPtr = ptr;
            refToPtr->age = 300;
            printf("Huqinwei.age is %d\n",Huqinwei.age);
            printf("refToPtr->age is %d\n",refToPtr->age);
    
    
    }
    

    更正:之前给引用的初始化语句写错了,而且没注明引用是纯C中没有的东西(在这么个以C为幌子的博客中)。

    引用是C++特有的一个机制,必须靠编译器支撑,至于引用转换到C中本质是什么,我有个帖子写过
     

    结构体也不能免俗,必须有数组:

    struct test{
            int a[3];
            int b;
    };
    //对于数组和变量同时存在的情况,有如下定义方法:
            struct test student[3] =      {{{66,77,55},0},
                                            {{44,65,33},0},
                                            {{46,99,77},0}};
    //特别的,可以简化成:
            struct test student[3] =       {{66,77,55,0},
                                            {44,65,33,0},
                                            {46,99,77,0}};
    

    变长结构体

    可以变长的数组

    #include <stdio.h>
    #include <malloc.h>
    #include <string.h>
    typedef struct changeable{
            int iCnt;
            char pc[0];
    }schangeable;
    
    main(){
            printf("size of struct changeable : %d\n",sizeof(schangeable));
    
            schangeable *pchangeable = (schangeable *)malloc(sizeof(schangeable) + 10*sizeof(char));
            printf("size of pchangeable : %d\n",sizeof(pchangeable));
    
            schangeable *pchangeable2 = (schangeable *)malloc(sizeof(schangeable) + 20*sizeof(char));
            pchangeable2->iCnt = 20;
            printf("pchangeable2->iCnt : %d\n",pchangeable2->iCnt);
            strncpy(pchangeable2->pc,"hello world",11);
            printf("%s\n",pchangeable2->pc);
            printf("size of pchangeable2 : %d\n",sizeof(pchangeable2));
    }

    运行结果

    size of struct changeable : 4
    size of pchangeable : 4
    pchangeable2->iCnt : 20
    hello world
    size of pchangeable2 : 4

    如上,本例中变长结构体本身长度就是一个int的长度(这个int值通常只为了方便表示后边的数组长度),而后边的数组长度不计算在内,但是该数组可以直接使用。

    (说后边是个指针吧?指针也占长度!这个是不占的!原理很简单,这个东西完全是数组后边的尾巴,malloc开辟的是一片连续空间。其实这不应该算一个机制,感觉应该更像一个技巧吧

    20191113:这块可能有点抽象?建议去了解一下手动开辟空间malloc和指针相关知识,所谓“变长结构体”,不是一个你理解的结构体!至少不是按正常结构体用的,他像是一个逻辑性的概念,空间是malloc开辟的,结构体是以指针形式存在的“虚拟”的概念,简单说,这个“结构体”不在栈空间!

    20160405补充:

    非弹性数组不能用"char a[]"这种形式定义弹性(flexible)变量,必须明确大小。

    弹性数组在结构体中,下面的形式是唯一允许的:

    struct s
    {
            int a;
            char b[] ;
    };

    顺序颠倒会让b和a数据重合,会在编译时不通过。

    char b[] = "hell";也不行(C和C++都不行)

    少了整型变量a又会让整个结构体长度为0,compiler不允许编译通过!不同的是,其实C++形式上是允许空结构体的,本质上是通过机制避免了纯空结构体和类对象,自动给空结构体对象分配一个字节(sizeof()返回1)方便区分对象,避免地址重合!所以呢,C如果有空结构体,定义两个(或一打,或干脆一个数组)该结构体的变量(对象),地址是完全一样的!·!!!!!!!!调试看程序运行,这些语句其实都被当屁放了,根本没有运行,没有实际意义,C压根不支持空结构体这种东西(或者说我也没想好什么场合有用)

    struct s2
    {
    //      char a[]  = "hasd" ;
    //      int c;
    };
    int main()
    {
            struct s2 s22;
            struct s2 s23;
            struct s2 s24;
            struct s2 s25;
    }
    


    例外的是,C++唯独不给带弹性数组的结构体分配空间(可能怕和变长结构体机制产生某种冲突,比如大小怎么算):

    struct s
    {
            char b[] ;
    };

    struct s
    {
    //        char b[] ;
    };

    C++中两者是不一样的,空的结构体反而“大”(sizeof()返回1)
     

    20160321补充:这个机制利用了一个非常重要的特性——组和指针的区别!数组和指针在很多操作上是一样的,但是本质不一样。最直观的,指针可以改指向,数组不可以,因为数组占用的每一个内存地址都用来保存变量或者对象,而指针占用的内存地址保存的是一个地址,数组没有单独的保存指向地址的这样一个结构。数组的位置是固定的,正如指针变量自身的位置也是固定的,改的是指针的值,是指向的目标地址,而因为数组不存储目标地址,所以改不了指向。企图把地址强制赋值给数组的话,也只是说把指针赋值给数组,类型不兼容。

    结构体嵌套:

    结构体嵌套其实没有太意外的东西,只要遵循一定规律即可:

    //对于“一锤子买卖”,只对最终的结构体变量感兴趣,其中A、B也可删,不过最好带着
    struct A{ 
            struct B{
                 int c;
            }
            b;
    }
    a;
    //使用如下方式访问:
    a.b.c = 10; 
    

    特别的,可以一边定义结构体B,一边就使用上:

    struct A{
            struct B{
                    int c;
            }b;
            struct B sb;
    }a;
    

    使用方法与测试:

            a.b.c = 11;
            printf("%d\n",a.b.c);
            a.sb.c = 22;
            printf("%d\n",a.sb.c);
    结果无误。 

    但是如果嵌套的结构体B是在A内部才声明的,并且没定义一个对应的对象实体b,这个结构体B的大小还是不算进结构体A中。

    结构体与函数:

    关于传参,首先,把结构体中的int成员变量当做和普通int变量一样的东西来使用(当做函数参数),是不用脑子就想到的一种方法,如下:

    void func(int);
    func(a.b.c);

    另外的主要用法就是传递副本和指针了 :

    //20210805更新:用了能完整跑通的代码,降低了文章前后所需的连贯性,避免读者拼接代码编译不过——https://mp.csdn.net/mp_blog/creation/editor/23625823
    struct A {
        struct B {
            int c;
        }b;
        struct B sb;
    }a;
    
    //设立了两个函数,分别传递struct A结构体和其指针。
    void func1(struct A a) {//复制结构体
        printf("%d\n", a.b.c);
    }
    void func2(struct A* a) {//传递结构体指针
        printf("%d\n", a->b.c);
    }
    void func3(struct A& a) {//进阶:传递结构体引用,效用上近似结构体指针,但访问形式不同于指针
        printf("%d\n", a.b.c);
    }
    int main() {
        a.b.c = 112;
        struct A * pa;
        pa = &a;
        func1(a);
        func2(&a);
        func2(pa);
        func3(a);
    }

    注意:盗版是得不到更新迭代的(手动滑稽)https://blog.csdn.net/huqinweI987/article/details/23625823

    占用内存空间:

    struct结构体,在结构体定义的时候不能申请内存空间,不过如果是结构体变量,声明的时候就可以分配——两者关系就像C++的类与对象,对象才分配内存(不过严格讲,作为代码段,结构体定义部分“.text”真的就不占空间了么?当然,这是另外一个范畴的话题)。

    结构体的大小通常(只是通常)是结构体所含变量大小的总和,下面打印输出上述结构体的size:

            printf("size of struct man:%d\n",sizeof(struct man));
            printf("size:%d\n",sizeof(Huqinwei));
    结果毫无悬念,都是28:分别是char数组20,int变量4,浮点变量4. 

    下边说说不通常的情况:

    对于结构体中比较小的成员,可能会被强行对齐,造成空间的空置,这和读取内存的机制有关,为了效率。通常32位机按4字节对齐,小于的都当4字节,有连续小于4字节的,可以不着急对齐,等到凑够了整,加上下一个元素超出一个对齐位置,才开始调整,比如3+2或者1+4,后者都需要另起(下边的结构体大小是8bytes),相关例子就多了,不赘述。

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

    相应的,64位机按8字节对齐。不过对齐不是绝对的,用#pragma pack()可以修改对齐,如果改成1,结构体大小就是实实在在的成员变量大小的总和了。
    补一个代码,压入1字节对齐,定义s,然后弹出,使用默认,定义s2,两个结构体大小分别为7和8

    #include <stdio.h>
    #pragma pack(push,1)
    struct s
    {
            char a;
            short b;
            int c;
    };
    #pragma pack(pop)
    struct s2
    {
            char a;
            short b;
            int c;
    };
    
    
    
    int main(){
    
            printf("%ld\n",sizeof(struct s));
            printf("%ld\n",sizeof(struct s2));
    
    }
    
    $ ./a.out
    7
    8
    

    和C++的类不一样,结构体不可以给结构体内部变量初始化,。

    如下,为错误示范:

    #include<stdio.h>
    //直接带变量名Huqinwei
    struct stuff{
    //      char job[20] = "Programmer";
    //      char job[];
    //      int age = 27;
    //      float height = 185;
    }Huqinwei;
    

    PS:结构体的声明也要注意位置的,作用域不一样。

    C++的结构体变量的声明定义和C有略微不同,说白了就是更“面向对象”风格化,要求更低。

    那么熟悉了常用方法,都要注意哪些常犯错误呢,见C语言结构体常见错误

    =========================================================================

    如果感兴趣C++、计算机视觉、深度学习和SLAM与三维重建,可以关注作者学习交流。

    展开全文
  • struct和typedef struct彻底明白了

    万次阅读 多人点赞 2019-09-07 19:43:36
    C/C++语法知识:typedef struct 用法详解 typedef struct OLNode {  int i,j;  int data;  OLNode *right,*down; }OLNode,*OLink;//结构的对象OLNode, 指向结构的指针*OLink, 可以这样写 struct OLNode{}; ...
    更详细和准确内容参见:
    C/C++语法知识:typedef struct 用法详解
    typedef struct OLNode
    {
      int i,j;
      int data;
      OLNode *right,*down;
    }OLNode,*OLink;//结构的对象OLNode, 指向结构的指针*OLink,
    可以这样写
    struct OLNode{};
    OLNode OLNode, *OLink;
    
     

     

     

    分三块来讲述:
      1 首先://注意在C和C++里不同
        在C中定义一个结构体类型要用typedef:
        typedef struct Student
        {
        int a;
        }Stu;
        于是在声明变量的时候就可:Stu stu1;(如果没有typedef就必须用struct Student stu1;来声明)
        这里的Stu实际上就是struct Student的别名。Stu==struct Student
        另外这里也可以不写Student(于是也不能struct Student stu1;了,必须是Stu stu1;)
        typedef struct
        {
        int a;
        }Stu;
        但在c++里很简单,直接
        struct Student
        {
        int a;
        };    
        于是就定义了结构体类型Student,声明变量时直接Student stu2;
    ======================================================================================

      2.其次:
        在c++中如果用typedef的话,又会造成区别:
        struct   Student   
        {   
        int   a;   
        }stu1;//stu1是一个变量  

     
        typedef   struct   Student2   
        {   
        int   a;   
        }stu2;//stu2是一个结构体类型=struct Student  

     
        使用时可以直接访问stu1.a
        但是stu2则必须先   stu2 s2;
        然后               s2.a=10;
    ======================================================================================

      3 掌握上面两条就可以了,不过最后我们探讨个没多大关系的问题
        如果在c程序中我们写:
        typedef struct  
        {
        int num;
        int age;
        }aaa,bbb,ccc;
        这算什么呢?
        我个人观察编译器(VC6)的理解,这相当于
        typedef struct  
        {
        int num;
        int age;
        }aaa;
        typedef aaa bbb;
        typedef aaa ccc;
        也就是说aaa,bbb,ccc三者都是结构体类型。声明变量时用任何一个都可以,在c++中也是如此。但是你要注意的是这个在c++中如果写掉了typedef关键字,那么aaa,bbb,ccc将是截然不同的三个对象。

        //此处不是很理解。

       

     

     

        typedef struct和struct的区别:

     

     

        typedef struct tagMyStruct
        { 
         int iNum;
         long lLength;
        } MyStruct;

        上面的tagMyStruct是标识符,MyStruct是变量类型(相当于(int,char等))。

     

     

        这语句实际上完成两个操作:

          1) 定义一个新的结构类型

        struct tagMyStruct
        {   
         int iNum; 
         long lLength; 
        };

      分析:tagMyStruct称为“tag”,即“标签”,实际上是一个临时名字,不论是否有typedefstruct 关键字和tagMyStruct一起,构成了这个结构类型,这个结构都存在。

      我们可以用struct tagMyStruct varName来定义变量,但要注意,使用tagMyStruct varName来定义变量是不对的,因为struct 和tagMyStruct合在一起才能表示一个结构类型。

      2) typedef为这个新的结构起了一个名字,叫MyStruct。

        typedef struct tagMyStruct MyStruct;

      因此,MyStruct实际上相当于struct tagMyStruct,我们可以使用MyStruct varName来定义变量。

      2.

        typedef struct tagMyStruct
        { 
         int iNum;
         long lLength;
        } MyStruct;

        在C中,这个申明后申请结构变量的方法有两种:

        (1)struct tagMyStruct 变量名

        (2)MyStruct 变量名

        在c++中可以有

        (1)struct tagMyStruct 变量名

        (2)MyStruct 变量名

        (3)tagMyStruct 变量名

    展开全文
  • 结构体定义 typedef struct 用法详解和用法小结

    万次阅读 多人点赞 2018-07-31 22:25:48
    typedef struct 是为了使用这个结构体方便。 具体区别在于: 若struct node{ }这样来定义结构体的话。在定义 node 的结构体变量时,需要这样写:struct node n; 若用typedef,可以这样写:typedef struct node{}NODE;...

    typedef是类型定义的意思。typedef struct 是为了使用这个结构体方便。

    具体区别在于: 
    若struct node{ }这样来定义结构体的话。在定义 node 的结构体变量时,需要这样写:struct node n; 
    若用typedef,可以这样写:typedef struct node{}NODE; 。在申请变量时就可以这样写:NODE n;其实就相当于 NODE 是node 的别名。区别就在于使用时,是否可以省去struct这个关键字。

    首先:

    在C中定义一个结构体类型时如果要用typedef:

    typedef struct Student
    {
       int no;
       char name[12];
    }Stu,student;

    于是在声明变量的时候就可:Stu stu1;或者:student stu2;(Stu 和student 同时为Student的别名) 
    如果没有typedef即:

    struct Student
    {
       int no;
       char name[12];
    }Stu;

    就必须用struct Student stu1;或者struct Stu stu1;来声明 
    另外这里也可以不写Student(于是也不能struct Student stu1;了)

    typedef struct
    {
       int no;
       char name[12];
    }Stu;

    其次:

    在c++中如果用typedef的话,又会造成区别:

    struct Student
    {
       int no;
       char name[12];
    }stu1;//stu1是一个变量
    typedef struct Student2
    {
       int no;
       char name[12];
    }stu2;//stu2是一个结构体类型,即stu2是Student2的别名

    使用时可以直接访问stu1.no 
    但是stu2则必须先定义 stu2 s2; 
    然后 s2.no=10;

    展开全文
  • mapstruct使用的正确姿势

    万次阅读 多人点赞 2020-04-22 09:29:29
    我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以其他工程想...所以阿淼今天就要给大家安利一款叫 mapstruct 的插件,它就...

    我们都知道,随着一个工程的越来越成熟,模块划分会越来越细,其中实体类一般存于 domain 之中,但 domain 工程最好不要被其他工程依赖,所以其他工程想获取实体类数据时就需要在各自工程写 model,自定义 model 可以根据自身业务需要映射相应的实体属性。这样一来,这个映射工程貌似并不简单了。阿森差点就犯难了……

    所以阿淼今天就要给大家安利一款叫 mapstruct 的插件,它就是专门用来处理 domin 实体类与 model 类的属性映射的,我们只需定义 mapper 接口,mapstruct 在编译的时候就会自动的帮我们实现这个映射接口,避免了麻烦复杂的映射实现。

    那可能有的小伙伴就要问了?为啥不用 BeanUtilscopyProperties 方法呢?不也照样可以实现属性的映射么?

    这个啊,阿淼我开始也是好奇,所以就和 BeanUtils 深入交流了一番,最后才发现,BeanUtils 就是一个大老粗,只能同属性映射,或者在属性相同的情况下,允许被映射的对象属性少;但当遇到被映射的属性数据类型被修改或者被映射的字段名被修改,则会导致映射失败。而 mapstruct 就是一个巧媳妇儿了,她心思细腻,把我们可能会遇到的情况都给考虑到了(要是阿淼我也能找一个这样的媳妇儿该多好,内心笑出了猪声)

    如下是这个插件的开源项目地址和各种例子:

    • Github地址:https://github.com/mapstruct/mapstruct/
    • 使用例子:https://github.com/mapstruct/mapstruct-examples

    一、准备工作

    接下来,阿淼将和大家一起去解开这个巧媳妇儿的真正面纱,所以我们还需要做一点准备工作。

    1.1、了解@Mapper 注解

    从 mybatis3.4.0 开始加入的 @Mapper 注解,目的就是为了不再写mapper映射文件。

    我们只需要在 dao 层定义的接口上使用注解就可以实现sql语句的编写,例如:

    @Select("select * from user where name = #{name}")
    public User find(String name);
    
    

    如上就是一个简单的使用,虽然简单,但也确实体现出了这个注解的优越性,至少少写了一个xml文件。

    但阿淼我今天可不是想跟你探讨 @Mapper 注解,我主要是想去看我的巧媳妇儿 mapstruct ,所以我就只是想说下 @Mapper 注解的 componentModel 属性,componentModel 属性用于指定自动生成的接口实现类的组件类型,这个属性支持四个值:

    • default: 这是默认的情况,mapstruct 不使用任何组件类型, 可以通过Mappers.getMapper(Class)方式获取自动生成的实例对象。
    • cdi: the generated mapper is an application-scoped CDI bean and can be retrieved via @Inject
    • spring: 生成的实现类上面会自动添加一个@Component注解,可以通过Spring的 @Autowired方式进行注入
    • jsr330: 生成的实现类上会添加@javax.inject.Named 和@Singleton注解,可以通过 @Inject注解获取

    1.2、依赖包

    首先需要把依赖包导入,主要由两个包组成:

    • org.mapstruct:mapstruct:包含了一些必要的注解,例如@Mapping。r若我们使用的JDK版本高于1.8,当我们在pom里面导入依赖时候,建议使用坐标是:org.mapstruct:mapstruct-jdk8,这可以帮助我们利用一些Java8的新特性。
    • org.mapstruct:mapstruct-processor:注解处理器,根据注解自动生成mapper的实现。
        <dependency>
            <groupId>org.mapstruct</groupId>
            <!-- jdk8以下就使用mapstruct -->
            <artifactId>mapstruct-jdk8</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>1.2.0.Final</version>
        </dependency>
    

    好了,准备工作做完了,接下来我们就看看巧媳妇儿巧在什么地方吧。

    二、先简单玩一把

    2.1、定义实体类以及被映射类

    // 实体类
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class User {
        private Integer id;
        private String name;
        private String createTime;
        private LocalDateTime updateTime;
    }
    
    // 被映射类VO1:和实体类一模一样
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class UserVO1 {
        private Integer id;
        private String name;
        private String createTime;
        private LocalDateTime updateTime;
    }
    
    // 被映射类VO1:比实体类少一个字段
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class UserVO2 {
        private Integer id;
        private String name;
        private String createTime;
    
    }
    

    2.2、定义接口:

    当实体类和被映射对象属性相同或者被映射对象属性值少几个时:

    @Mapper(componentModel = "spring")
    public interface UserCovertBasic {
        UserCovertBasic INSTANCE = Mappers.getMapper(UserCovertBasic.class);
    
        /**
         * 字段数量类型数量相同,利用工具BeanUtils也可以实现类似效果
         * @param source
         * @return
         */
        UserVO1 toConvertVO1(User source);
        User fromConvertEntity1(UserVO1 userVO1);
    
        /**
         * 字段数量类型相同,数量少:仅能让多的转换成少的,故没有fromConvertEntity2
         * @param source
         * @return
         */
        UserVO2 toConvertVO2(User source);
    }
    

    从上面的代码可以看出:接口中声明了一个成员变量INSTANCE,母的是让客户端可以访问 Mapper 接口的实现。

    2.3、使用

    @RestController
    public class TestController {
    
        @GetMapping("convert")
        public Object convertEntity() {
            User user = User.builder()
                    .id(1)
                    .name("张三")
                    .createTime("2020-04-01 11:05:07")
                    .updateTime(LocalDateTime.now())
                    .build();
            List<Object> objectList = new ArrayList<>();
    
            objectList.add(user);
    
            // 使用mapstruct
            UserVO1 userVO1 = UserCovertBasic.INSTANCE.toConvertVO1(user);
            objectList.add("userVO1:" + UserCovertBasic.INSTANCE.toConvertVO1(user));
            objectList.add("userVO1转换回实体类user:" + UserCovertBasic.INSTANCE.fromConvertEntity1(userVO1));
            // 输出转换结果
            objectList.add("userVO2:" + " | " + UserCovertBasic.INSTANCE.toConvertVO2(user));
            // 使用BeanUtils
            UserVO2 userVO22 = new UserVO2();
            BeanUtils.copyProperties(user, userVO22);
            objectList.add("userVO22:" + " | " + userVO22);
    
            return objectList;
        }
    }
    

    2.4、查看编译结果

    通过IDE的反编译功能查看编译后自动生成 UserCovertBasic 的实现类 UserCovertBasicImpl ,内容如下:

    @Component
    public class UserCovertBasicImpl implements UserCovertBasic {
        public UserCovertBasicImpl() {
        }
    
        public UserVO1 toConvertVO1(User source) {
            if (source == null) {
                return null;
            } else {
                UserVO1 userVO1 = new UserVO1();
                userVO1.setId(source.getId());
                userVO1.setName(source.getName());
                userVO1.setCreateTime(source.getCreateTime());
                userVO1.setUpdateTime(source.getUpdateTime());
                return userVO1;
            }
        }
    
        public User fromConvertEntity1(UserVO1 userVO1) {
            if (userVO1 == null) {
                return null;
            } else {
                User user = new User();
                user.setId(userVO1.getId());
                user.setName(userVO1.getName());
                user.setCreateTime(userVO1.getCreateTime());
                user.setUpdateTime(userVO1.getUpdateTime());
                return user;
            }
        }
    
        public UserVO2 toConvertVO2(User source) {
            if (source == null) {
                return null;
            } else {
                UserVO2 userVO2 = new UserVO2();
                userVO2.setId(source.getId());
                userVO2.setName(source.getName());
                userVO2.setCreateTime(source.getCreateTime());
                return userVO2;
            }
        }
    }
    

    2.5、浏览器查看结果

    好了,一个流程就走完了,是不是感觉贼简单呢?

    而且呀,阿淼温馨提醒:
    如果是要转换一个集合的话,只需要把这里的实体类换成集合就行了,例如:

        List<UserVO1> toConvertVOList(List<User> source);
    

    三、不简单的情况

    上面已经把整个流程都给过了一遍了,相信大家对 mapstruct 也有了一个基础的了解了,所以接下来的情况我们就不展示全部代码了,毕竟篇幅也有限,所以就直接上关键代码(因为不关键的和上面内容一样,哈哈)

    3.1、类型不一致

    实体类我们还是沿用 User;被映射对象 UserVO3 改为:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class UserVO3 {
        private String id;
        private String name;
        // 实体类该属性是String
        private LocalDateTime createTime;
        // 实体类该属性是LocalDateTime
        private String updateTime;
    }
    

    那么我们定义的接口就要稍稍修改一下了:

        @Mappings({
                @Mapping(target = "createTime", expression = "java(com.java.mmzsblog.util.DateTransform.strToDate(source.getCreateTime()))"),
        })
        UserVO3 toConvertVO3(User source);
    
        User fromConvertEntity3(UserVO3 userVO3);
    

    上面 expression 指定的表达式内容如下:

    public class DateTransform {
        public static LocalDateTime strToDate(String str){
            DateTimeFormatter df = DateTimeFormatter.ofPattern("yyy-MM-dd HH:mm:ss");
            return LocalDateTime.parse("2018-01-12 17:07:05",df);
        }
    
    }
    

    通过IDE的反编译功能查看编译后的实现类,结果是这样子的:

    从图中我们可以看到,编译时使用了expression中定义的表达式对目标字段 createTime 进行了转换;然后你还会发现 updateTime 字段也被自动从 LocalDateTime 类型转换成了 String 类型。

    阿淼小结

    当字段类型不一致时,以下的类型之间是 mapstruct 自动进行类型转换的:

    • 1、基本类型及其他们对应的包装类型。
      此时 mapstruct 会自动进行拆装箱。不需要人为的处理
    • 2、基本类型的包装类型和string类型之间

    除此之外的类型转换我们可以通过定义表达式来进行指定转换。

    3.2、字段名不一致

    实体类我们还是沿用 User;被映射对象 UserVO4 改为:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class UserVO4 {
        // 实体类该属性名是id
        private String userId;
        // 实体类该属性名是name
        private String userName;
        private String createTime;
        private String updateTime;
    }
    

    那么我们定义的接口就要稍稍修改一下了:

        @Mappings({
                @Mapping(source = "id", target = "userId"),
                @Mapping(source = "name", target = "userName")
        })
        UserVO4 toConvertVO(User source);
        
        User fromConvertEntity(UserVO4 userVO4);
    

    通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:

    很明显, mapstruct 通过读取我们配置的字段名对应关系,帮我们把它们赋值在了相对应的位置上,可以说是相当优秀了,但这也仅仅是优秀,而更秀的还请继续往下看:

    阿淼小结

    当字段名不一致时,通过使用 @Mappings 注解指定对应关系,编译后即可实现对应字段的赋值。

    3.3、属性是枚举类型

    实体类我们还是改用 UserEnum:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class UserEnum {
        private Integer id;
        private String name;
        private UserTypeEnum userTypeEnum;
    }
    

    被映射对象 UserVO5 改为:

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @Builder
    public class UserVO5 {
        private Integer id;
        private String name;
        private String type;
    }
    

    枚举对象是:

    @Getter
    @AllArgsConstructor
    public enum UserTypeEnum {
        Java("000", "Java开发工程师"),
        DB("001", "数据库管理员"),
        LINUX("002", "Linux运维员");
        
        private String value;
        private String title;
    
    }
    

    那么我们定义的接口还是照常定义,不会受到它是枚举就有所变化:

        @Mapping(source = "userTypeEnum", target = "type")
        UserVO5 toConvertVO5(UserEnum source);
    
        UserEnum fromConvertEntity5(UserVO5 userVO5);
    

    通过IDE的反编译功能查看编译后的实现类,编译后的结果是这样子的:

    很明显, mapstruct 通过枚举类型的内容,帮我们把枚举类型转换成字符串,并给type赋值,可谓是小心使得万年船啊。看来这巧媳妇儿不仅仅优秀还心细啊……

    源码地址:

    文章中的所有例子已上传github:https://github.com/mmzsblog/mapstructDemo

    展开全文
  • typedef structstruct区别

    万次阅读 多人点赞 2018-05-26 18:02:47
    typedef structstruct区别
  • C语言中的struct用法

    万次阅读 多人点赞 2019-06-24 15:01:52
    参考自C语言中的struct用法 在c语言中结构体(struct)跟面向对象编程(如java等)里面的类是非常相似的。不过像C++里面对结构体进行了扩展,c++里面的结构体是可以包含方法的,但是C语言里面是不能够的。 结构...
  • struct typedef struct 区别
  • struct files_structstruct fdtable

    千次阅读 2018-05-30 18:11:09
    struct files_structstruct fdtable的初始化我们先来列出struct files_structstruct fdtable的定义,为了讨论方面,下面的定义中略去了很少一部分的锁成员,下面的代码均摘自linux 2.6.24。struct files_struct...
  • MapStruct - 一篇就能上手 MapStruct

    万次阅读 多人点赞 2021-02-22 17:45:25
    MapStruct是满足JSR269规范的一个Java注解处理器,用于为Java Bean生成类型安全且高性能的映射。它基于编译阶段生成get/set代码,此实现过程中没有反射,不会造成额外的性能损失。 您所要做的就是定义一个mapper...
  • C++struct继承struct

    千次阅读 2016-11-02 20:16:32
    #include using namespace std;...struct A { int a; int b; }; struct B : A { int c; }; int main() { struct B stB; stB.a = 1; cout; return 0; } c++ 里面结构体是可以继承
  • python struct 结构体

    万次阅读 多人点赞 2018-07-12 20:44:57
    import struct 有的时候需要用python处理二进制数据,比如,存取文件,socket操作时.这时候,可以使用python的struct模块来完成.可以用 struct来处理c语言中的结构体. struct模块中最重要的三个函数是pack(), ...
  • struct timespec 和 struct timeval

    千次阅读 2019-10-17 14:15:53
    struct timespec 提供秒和纳秒级的时间精度 struct timeval 提供秒和微秒级的时间精度
  • C语言之struct 和 typedef struct

    千次阅读 2020-10-07 17:21:34
    struct 用法 struct tag { member-list member-list member-list ... } variable-list ; 实例 struct Books { int book_id; char *bookname; } 说明: (1)上面声明了一个结构的“变量类型”,我们...
  • 结构体定义struct和typedef struct的区别

    千次阅读 多人点赞 2018-03-20 13:37:54
    1.结构体的定义: 允许用户自己建立由不同类型数据组成的组合型的数据结构,它称为...2.下面以一个结构体实例来说明一下struct的用法: structos_tcb { OS_STK*OSTCBStkPtr; OS_STK*OSTCBStkBottom; INT3...
  • typedef structstruct的使用

    千次阅读 2018-08-24 18:14:43
    typedef structstruct的使用 //以下student是标识符(标识符是用户编程时使用的名字,对于变量、常量、函数、语句块也有名 字;),stu则为变量类型(类比int和char等),pstu相当于(int*)。 typedef struct...
  • Python中struct.pack()和struct.unpack()用法详细说明

    万次阅读 多人点赞 2018-05-21 18:30:57
    python中的struct主要是用来处理C结构数据的,读入时先转换为Python的字符串类型,然后再转换为Python的结构化类型,比如元组(tuple)啥的~。一般输入的渠道来源于文件或者网络的二进制流。1.struct.pack()和struct....
  • struct与typedef struct的区别

    万次阅读 多人点赞 2016-08-21 17:03:25
    typedef struct 是为了使用这个结构体方便。 具体区别在于: 若struct node {}这样来定义结构体的话。在申请node 的变量时,需要这样写,struct node n; 若用typedef,可以这样写,typedef struct node{}NODE; 。在...
  • struct vm_structstruct vm_area_struct

    千次阅读 2014-03-29 19:35:44
    内存映射信息放在vma参数中,注意,这里的vma的数据类型是struct vm_area_struct,它表示的是一块连续的虚拟地址空间区域,在函数变量声明的地方,我们还看到有一个类似的结构体struct vm_struct,这个数据结构也是...
  • 一、struct timespec 定义: typedef long time_t; #ifndef _TIMESPEC #define _TIMESPEC struct timespec { time_t tv_sec; // seconds long tv_nsec; // and nanoseconds }; #endif struct timespec有两个...
  • 【C/C++面试必备】struct和class的区别

    万次阅读 多人点赞 2021-07-23 07:47:53
    ???? 作者:Linux猿 ???? 简介:CSDN博客专家?...,C/C++、面试、刷题、算法尽管咨询我,关注我,有...首先,注意本文讨论的是 C++ 中 struct 和 class 的区别,因为 C 中 struct 和 class 的区别已经很明显了! 先说下
  • struct与typedef struct

    千次阅读 2014-10-02 17:17:40
    一、struct和typedef struct区别 1)在C中定义一个结构体类型要用typedef: typedef struct Student {  int a;  }Stu; // 写法1 于是在声明变量的时候就可:Stu stu1;(如果没有typedef就必须用struct Student stu1;...
  • python之struct详解

    万次阅读 多人点赞 2018-05-23 18:20:29
    用处按照指定格式将Python数据转换为...处理二进制数据,如果用struct来处理文件的话,需要用’wb’,’rb’以二进制(字节流)写,读的方式来处理文件;处理c语言中的结构体;struct模块中的函数函数returnexplainpack(fmt,...
  • struct和typedef struct的用法和区别

    千次阅读 多人点赞 2017-10-15 14:50:01
    struct和typedef struct的用法和区别
  • struct用法

    万次阅读 多人点赞 2018-07-09 19:06:02
    关键字struct能定义各种类型的变量集合,称为结构(structure),并把它们视为一个单元。1.struct的简单例子下面是一个struct的简单的声明例子:struct horse { int age; int height; } Silver; 这个例子声明了一个...
  • struct cdev与struct file_operations的关系 各种数据结构之间的关系struct file:(表示已打开的文件)(路径:linux-3.13.10\include\linux\fs.h)(描述进程中打开的文件,进程中只要调用了open就有一个该对象。...
  • struct1与struct2的区别

    千次阅读 2017-03-16 20:14:07
    struct1与struct2的区别
  • struct和typedef struct

    千次阅读 2014-07-25 10:36:58
    1 首先://注意在C和C++里不同 ... typedef struct Student  {  int a;  }Stu;  于是在声明变量的时候就可:Stu stu1;(如果没有typedef就必须用struct Student stu1;来声明)  这里的Stu实际上就是struct Stud
  • Linux下常用的时间类型有4个:time_t,struct timeb, struct timeval,struct timespec,clock_t, struct tm. (1) time_t是一个长整型,一般用来表示用1970年以来的秒数. 该类型定义在中. 一般通过 time_t ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 2,249,033
精华内容 899,613
关键字:

struct