精华内容
下载资源
问答
  • C语言是一门很简单的语言,但是并没有多少人敢说自己完全懂这个语言。今天,我们来仔细了解一下结构体和联合,看看它是否还是那个为我们所熟知的语言。

    【C语言进阶】结构体与联合

    本文内容整理自 ISO/IEC 9899:2017

    2021-3-21:完成初稿。

    1. 说明符

    1.1. 说明符 - 定义

    下面的语法定义不做过多介绍,因为这些东西介绍起来价值很低——懂的不需要介绍,需要介绍的完全可以找到更适合、更成熟的教程。

    struct-or-union-specifier:
        struct-or-union identifier[opt] { struct-declaration-list }
        struct-or-union identifier
    struct-or-union:
        struct
        union
    struct-declaration-list:
        struct-declaration
        struct-declaration-list struct-declaration
    struct-declaration:
        specifier-qualifier-list struct-declarator-list[opt] ;
        static_assert-declaration
    specifier-qualifier-list:
        type-specifier specifier-qualifier-list[opt]
        type-qualifier specifier-qualifier-list[opt]
        alignment-specifier specifier-qualifier-list[opt]
    struct-declarator-list:
        struct-declarator
        struct-declarator-list , struct-declarator
    struct-declarator:
        declarator
        declarator[opt] : constant-expression
    

    1.2. 说明符 - 高级用法

    这里介绍一些结构体 / 联合的高级用法,相信大家应该都已经用得挺熟练的了。这里定义并介绍这些用法,好让大家交流的时候看起来更专业一点。

    1.2.1. 匿名结构体及联合(anonymous structure/union)

    一个未指定结构体/联合名、也未命名自身的结构体/联合成员,即为匿名结构体/联合。匿名结构体/联合中的成员可在保持其原本布局的同时,被视作包含该匿名结构体/联合的结构体/联合的成员。

    struct v {
        union { // anonymous union
            struct { int i, j; }; // anonymous structure
            struct { long k, l; } w;
            struct z { char x, y; }; // invalid: declaration does not declare anything
        };
        int m;
    } v1;
    
    v1.i = 2;   // valid
    v1.k = 3;   // invalid: inner structure is not anonymous
    v1.w.k = 5; // valid
    

    具名结构体 v 中存在匿名联合,该匿名联合中同时存在两个结构体,其中一个指定了声明符 w。如上例所示,可直接访问匿名结构体中的字段,但是却必须通过声明符访问具名成员中的字段。

    1.2.2. 柔性数组成员(flexible array member)

    起码拥有一个具名成员的结构体,其末尾的成员可拥有不完全数组类型(incomplete type),也就是说它的大小可以是不确定的,这种成员就是柔性数组成员。

    NOTE1:柔性数组成员不占用任何内存,但可能导致结构体尾部填充额外 padding。填充的结果为:结构体的大小看起来就像柔性数组成员中每一项(基本类型)的整数倍。
    NOTE2:更多有关上述 padding 的例子,可以参考 《ISO/IEC 9899:2017》§ 6.7.2.1

    比如声明了下述结构体 s:

    struct s { int n; double d[]; };
    

    struct s 中有一个柔性数组成员 d,此时通常这样使用 s:

    int m = /* some value */;
    struct s *p = malloc(sizeof (*p) + sizeof (double [m]));
    

    上面这个结构体和使用方式,看起来就像等价于下面这个表达式一样,除了 d 的偏移可能并不一致:

    struct { int n; double d[m]; } *p;
    

    1.2.3. 仅包含匿名结构体/联合和柔性数组成员的结构体

    匿名结构体、联合中的成员可直接视作被包含的结构体、联合中的成员,因此,下述结构体 s 可视为拥有一个具名成员的结构体,其末尾成员,a,自然可定义为柔性数组成员。

    struct s {
        struct { int i; };
        int a[];
    };
    

    2. 初始化

    2.1. 初始化 - 定义

    同样的,语法定义不做过多介绍。这看起来会有些复杂,但只是一些递归的逻辑。

    initializer:
        assignment-expression
        { initializer-list }
        { initializer-list , }
    initializer-list:
        designation[opt] initializer
        initializer-list , designation[opt] initializer
    designation:
        designator-list =
    designator-list:
        designator
        designator-list designator
    designator:
        [ constant-expression ]
        . identifier
    

    2.2. 初始化 - 一些零碎的知识点

    2.2.1. 结构体的匿名成员,其值在结构体初始化之后是不确定的

    2.2.2. 未指定指派符时,初始化列表将按顺序初始化结构体/联合

    struct s {int a[3]; int b; union {int c1; char c2;};};
    
    struct s s1 = {{1}, 2, 3}; // 依次初始化 s.a[0], s.b, s.c1
    struct s s2 = {.b = 2, .a = {1}, 3}; // 依次初始化 s.b, s.a[0], s.c1
    

    2.2.3. 初始化列表中表达式的计算顺序未作约束,因此尽量避免从中使用可能产生边际效应的做法

    表达式的计算顺序可能并不等于成员的初始化顺序,不要作出任何假设!

    struct s s1 = {{a += 1}, b = a, a++}; // 不要这样玩
    

    2.3. 初始化 - 关于自动填零的坑

    2.3.1. 拥有自动存储周期(Storage Duration)的对象,若未显式初始化,则其值是不确定的;反之,拥有静态或线程存储周期的对象将被置零

    struct s {int f;};
    
    struct s s1;               // 静态存储周期,s1.f == 0 
    static struct s s2;        // 静态存储周期,s2.f == 0 
    extern struct s s3;        // 静态存储周期,s3.f == 0 
    _Thread_local struct s s4; // 线程存储周期,s4.f == 0 (c11)
    
    {
        struct s s5;           // 自动存储周期,s5.f == ?
    }
    
    

    2.3.2. 若初始化列表中项的个数少于结构体/联合/数组中项的总数,或给定字符串的字符数少于数组总大小,则其余项被隐式地填为零

    • eg1

      struct { int a[3], b; } w[] = { { 1 }, 2 };
      

      上述表达式将会把 w[0].a[0] 初始化为 1,把 w[1].a[0] 初始化为 2,而其余元素将默认填为 0。

    • eg2

      struct T {
          int k;
          int l;
      };
      
      struct S {
          int i;
              struct T t;
      };
      
      struct T x = {.l = 43, .k = 42, };
      
      void f(void)
      {
          struct S l = { 1, .t = x, .t.l = 41, };
      }
      

      注意 l.t.k d 的值是 42,因为隐式初始化无法覆盖显示初始化。

    • eg3

      union u
      {
          uint32_t output;
          struct
          {
              uint32_t aa : 31;
              uint32_t ac : 1;
          };
          struct
          {
              uint32_t ba : 16;
              uint32_t bb : 15;
              uint32_t bc : 1;
          };
          struct
          {
              uint32_t __low_bits_holder : 31;
              uint32_t cc : 1;
          };
      };
      
      union u u = {
          .aa = 1,
          .cc = 1,
      };
      

      u.output == 0x80000000,而不是 0x80000001。

    展开全文
  • C语言中,可以使用结构体(Struct)来存放一组类型不同的数据。 定义格式: struct 结构体名{ 成员 }; 结构体所包含的变量或数组称为结构体成员(Member)。结构体成员的定义方式与变量数组的定义方式相同...

    目录

    一、结构体

    结构体变量

    成员的获取和赋值

    结构体数组

     结构体指针

    二、枚举类型

    枚举变量

    三、联合类型

    共用体的应用


    一、结构体

    在C语言中,可以使用结构体(Struct)来存放一组类型不同的数据。

    定义格式

    struct 结构体名{
        成员
    };

    结构体所包含的变量或数组称为结构体成员(Member)。结构体成员的定义方式与变量和数组的定义方式相同,只是不能初始化。

    大括号后面的分号;不能少,这是一条完整的语句。

    结构体是一种自定义的数据类型,是一种创建变量的模板,编译器不会为它分配内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。无法去获取一个结构体名的地址,也不能将它赋值给其他变量。

    结构体变量

    结构体是一种数据类型,可以用它来定义变量

     struct 结构体名 变量名;

    也可以在定义结构体的同时定义结构体变量

    struct 结构体名{
    ...
    } 变量名;

    在编译器的具体实现中,结构体的各个成员在内存中不是连续存储的,各个成员之间可能会存在缝隙

    结构体变量的初始化

    struct stu {
        char *name;  // 姓名
        int num;  // 学号
    } A = {"Li ping", 1};

    当对数组中全部元素赋值时,也可不给出数组长度

    成员的获取和赋值

    结构体和数组类似,也是一组数据的集合,整体使用没有太大的意义。数组使用下标[ ]获取单个元素,结构体使用点号.获取单个成员。

    格式:

    结构体变量名.成员名;

    可以通过获取成员对成员进行逐一赋值,也可以在定义时整体赋值。不过整体赋值仅限于定义结构体变量的时候,在使用过程中只能对成员逐一赋值,这和数组的赋值非常类似。

    结构体数组

    结构体数组,是指数组中的每个元素都是一个结构体。在实际应用中,结构体数组常被用来表示一个拥有相同数据结构的群体,比如一个班的学生等。

    定义格式:

    struct 结构体名 数组名[数组长度];

    在定义的时候对数组中全部元素赋值时,也可以不给出数组长度。

    使用:

    数组名[下标].成员名;

     结构体指针

    指针也可以指向一个结构体。

    定义格式:

    struct 结构体名 *变量名 = & 结构体变量名;

    也可以在定义结构体的同时定义结构体指针:

    struct 结构体名{
    ...
    } 结构体变量名 = { …… }, *变量名 = &结构体变量名;

    通过结构体指针可以获取结构体成员,一般形式为:

    (*pointer).memberName 
    或  
    pointer -> memberName

    第一种写法中,.的优先级高于*,(*pointer)两边的括号不能少。如果去掉括号写作*pointer.memberName,那么就等效于*(pointer.memberName),这样意义就完全不对了。

    第二种写法中,->是一个新的运算符,习惯称它为“箭头”,有了它,可以通过结构体指针直接取得结构体成员;这也是->在C语言中的唯一用途。

    两种写法是等效的,我们通常采用后面的写法,这样更加直观。

    二、枚举类型

    在实际编程中,有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便在后续代码中使用,比如一个星期只有七天。我们可以使用#define命令来给每天指定一个名字,但会导致宏名过多,代码松散。C语言提供了一种枚举(Enum)类型,能够列出所有可能的取值,并给它们取一个名字。

    定义格式:

    enum typeName{ valueName1, valueName2, valueName3, ... };

    enum 专门用来定义枚举类型,这也是它在C语言中的唯一用途;

    typeName是枚举类型的名字;

    valueName1, valueName2, valueName3, ......是每个值对应的名字的列表。

    注意最后的 ; 不能少。

    枚举值默认从 0 开始,往后逐个加 1(递增)。我们也可以给每个名字都指定一个值,更为简单的方法是只给第一个名字指定值。

    枚举变量

    枚举是一种类型,通过它可以定义枚举变量:

    enum week a, b, c;

    也可以在定义枚举类型的同时定义变量:

    enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a, b, c;

    有了枚举变量,就可以把列表中的值赋给它:

    enum week{ Mon = 1, Tues, Wed, Thurs, Fri, Sat, Sun } a = Mon, b = Wed, c = Sat;

    枚举类型变量需要存放的是一个整数,所以它的长度和 int 相同。

    枚举列表中的 Mon 等标识符的作用范围不能再定义与它们名字相同的变量。

    Mon 等标识符都是常量,不能对它们赋值,只能将它们的值赋给其他的变量。

    枚举和宏其实非常类似,宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。我们可以将枚举理解为编译阶段的宏。Mon 等名字都被替换成了对应的数字。这意味着,Mon、Tues、Wed 等都不是变量,它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是直接被编译到命令里面,放到代码区,所以不能用&取得它们的地址。这就是枚举的本质。

    三、联合类型

    在C语言中,还有另外一种和结构体非常类似的语法,叫做共用体(Union)。共用体有时也被称为联合或者联合体。

    定义格式

    union 共用体名{
        成员列表
    };

    结构体的各个成员会占用不同的内存,互相之间没有影响;而共用体的所有成员占用同一段内存,修改一个成员会影响其余所有成员。结构体占用的内存大于等于所有成员占用的内存的总和(成员之间可能会存在缝隙),共用体占用的内存等于最长的成员占用的内存。共用体使用了内存覆盖技术,同一时刻只能保存一个成员的值,如果对新的成员赋值,就会把原来成员的值覆盖掉。

    共用体也是一种自定义类型,可以通过它来创建变量。 可以先定义共用体,再创建变量,也可以在定义共用体的同时创建变量。

    union 里的成员共享内存,哪个成员长度最长,内存大小就由它决定。另外,编译器考虑“内存对齐”,32位机上考虑按32位对齐,也就是4字节对齐。需要的最大长度不为4的整数倍数时,给它分配多几个字节,凑成4的整数倍数。

    共用体成员之间会相互影响,修改一个成员的值会影响其他成员。

    共用体的应用

    共用体在一般的编程中应用较少,在单片机中应用较多。对于 PC 机,经常使用到的一个实例是: 现有一张关于学生信息和教师信息的表格。

    Name

    Num

    Sex

    Profession

    Score / Course

    HanXiaoXiao

    501

    famale

    student

    89.5

    YanWeiMin

    1011

    male

    teacher

    math

    LiuZhenTao

    109

    famale

    teacher

    English

    ZhaoFeiYan

    982

    male

    student

    95.0

    学生和教师所包含的数据是不同的。学生信息包括姓名、编号、性别、职业、分数,教师的信息包括姓名、编号、性别、职业、教学科目。现在要求把这些信息放在同一个表格中,并设计程序输入人员信息然后输出。

    如果把每个人的信息都看作一个结构体变量的话,那么教师和学生的前 4 个成员变量是一样的,第 5 个成员变量可能是 score 或者 course。当第 4 个成员变量的值是 student 的时候,第 5 个成员变量就是 score;当第 4 个成员变量的值是 teacher 的时候,第 5 个成员变量就是 course。所以我们可以设计一个包含共用体的结构体。

    #include <stdio.h>
    #include <stdlib.h>
    #define TOTAL 4 //人员总数
    struct{
        char name[20];
        int num;
        char sex;
        char profession;
        union{
            float score;
            char course[20];
        } sc;
    } bodys[TOTAL];
    int main(){
        int i;
        //输入人员信息
        for(i=0; i<TOTAL; i++){
            printf("Input info: ");
            scanf("%s %d %c %c",bodys[i].name,&(bodys[i].num),&(bodys[i].sex),&(bodys[i].profession));
            if(bodys[i].profession == 's'){ //如果是学生
                scanf("%f", &bodys[i].sc.score);
            }else{ //如果是老师
                scanf("%s", bodys[i].sc.course);
            }
            fflush(stdin);
        }
        //输出人员信息
        printf("\nName\t\tNum\tSex\tProfession\tScore / Course\n");
        for(i=0; i<TOTAL; i++){
            if(bodys[i].profession == 's'){ //如果是学生
                printf("%s\t%d\t%c\t%c\t\t%f\n", bodys[i].name,bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.score);
            }else{ //如果是老师
                printf("%s\t%d\t%c\t%c\t\t%s\n", bodys[i].name, bodys[i].num, bodys[i].sex, bodys[i].profession, bodys[i].sc.course);
            }
        }
        return 0;
    }

     

     

     

     

    展开全文
  • 结构体是不同数据类型组成的数据联合结构体也可以像数组一样整体使用,也可以对结构体成员单个使用。 结构体的成员变量不仅可以包含基本数据类型和数组,也可以嵌套结构体。例如定义了一个结构体a,定义了一个...

    结构体

    在实际的编程过程中,往往一组数据中包含有不同的数据类型。而简单的数据类型和数组都不能存储一组不同数据类型的数据,为了解决这一问题。C语言提供了复杂类型数据结构。复杂数据结构包括结构体和联合体。这是一个可以由用户自己定义的一个数据结构。

    结构体是不同数据类型组成的数据联合,结构体也可以像数组一样整体使用,也可以对结构体成员单个使用。

    结构体的成员变量不仅可以包含基本数据类型和数组,也可以嵌套结构体。例如定义了一个结构体a,定义了一个结构体b,结构体a可以作为结构体b的成员存在。

    1 结构体和结构体变量的定义

    结构体有三种定义方式:

    1)常见的先定义结构体,这后定义结构体变量

    例如 :

    struct  std{
        char name[20];
        int nub;
    };
    struct std std1,std2;

    这种方式是首先定义一个结构体std,在需要使用结构体变量的时候通过struct std 作为结构体类型,定义结构体变量std1和std2。

     

    2)在定义结构体类型的时候顺便定义结构体变量

    例如:

    struct  std{
        char name[20];
        int nub;
    } std1,std2;

    这种方式是在定义结构体类型的时候顺便定义了结构体变量std1和std2.在后面需要使用结构体的时候不需要定义,直接使用std1和std2即可。

    3)直接说明结构体变量

    例如:

    ​​​​​​struct  {
        char name[20];
        int nub;
    } std1,std2;

    这种方式定义的结构体只能通过std1和std2两个结构体变量进行操作,而不能在定义其他的结构体变量

    2 结构体的初始化

    结构体变量和其他的变量一样可以在定义的时候初始化,根据结构体的定义不同,结构体初始也有几种不同的初始化方式。

    1)在定义完结构体后,定义结构体变量的时候初始化

    例如 :

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std std1,std2 = {“ zhangsan”,100};

    2)在定义结构体时顺便定义结构体变量的时候初始化

    例如:

    struct  std{
        char name[20];
        int nub;
    } std1,std2 = {“ zhangsan”,100};

    直接说明定义结构体变量的定义方式也可以使用第二种初始化方式进行初始化。

    3)这种初始方式不区分定义的方式,针对结构体内部元素的初始化。我们可以顺序对结构中的元素一一进程初始化,这种初始化方式可以使用结构体中的成员名,也可以不使用结构体中的成员名。

    例如:

    struct  std{
        char name[20];
        int nub;
        double dd;
    };
    
    truct std std2 = {“ zhangsan”,100,88.8};//不带成员名初始化
    
    truct std std2 = {name =“ zhangsan”,nub = 100,dd = 88.8}; //带成员名初始化

     而在结构体初始化的时候我们也可以初始化结构体中的部分成员,那么这种初始化的时候就需要带成员变量初始化了。

    例如

    struct  std{
        char name[20];
        int nub;
        double dd;
    };
    
    truct std std2 = {name =“ zhangsan”,dd = 88.8}; //带成员名部分初始化

    如果不带成员名称,那么初始化的时候会默认根据结构体中成员的顺序进行初始化,如果数据类型对应不上则会报错。

    3 结构体的使用

    结构体处理在文件传输的时候、初始化和相同类型的结构变量相互赋值时候是整体使用的。其他在获取结构体中成员值和对结构体的成员赋值的时候一般是单独使用结构体成员。而结构体和结构体成员之间用哪个”.”连接来操作结构体成员。

    使用方法如下:

    struct  std{
        char name[20];
        int nub;
        double dd;
    };
    
    truct std std2 = {“ zhangsan”,100,88.8};
    
    //获取结构体中的成员值
    char name1[20] = std2.name;
    int aa = std2.nub;
    double  cc = std2.dd;
    
    //以上操作方式是将结构体中的name、nub的dd中的赋值给name1、aa和cc。
    
    
    
    //给结构体中的成员赋值
    char name1[20] = “zhangsan”;
    std2.name = name1;
    int aa = 100;
    std2.nub = aa;
    double  cc = 88.8;
    td2.nub = cc;
    //以上操作方式是给结构体中的name、nub的dd中的赋值name1、aa和cc。

     

     

    4 结构体数组

    结构体数组指的是存储相同数据类型的结构体变量的集合。这个集合中每一个元素都是结构体类型的元素。

    1) 结构体数组的定义:

    结构体数组的定义和结构体变量定义一样,

    (1)是常见的先定义结构体,这后定义结构体数组并初始化

    例如 :

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std std1[5];//定义一个结构体类型为std的结构体数组std1,std1中可以存储个元素。

    (2)在定义结构体类型的时候顺便定义结构体数组并初始化

    例如:

    struct  std{
        char name[20];
        int nub;
    } std1[5];//定义一个结构体类型为std的结构体数组std1,std1中可以存储个元素。

     

    (3)直接说明结构体数组并初始化

    例如:

    struct  {
        char name[20];
        int nub;
    } std1[5];//定义一个结构体类型为std的结构体数组std1,std1中可以存储 //个元素。

    1) 结构体数组的初始化

    结构体数组的初始化也是根据结构体数组的定义有不同的方式:

    1)常见的先定义结构体,这后定义结构体数组

    例如 :

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std std1[5] ={{“zhangsan”,100},
    
                            {“lisi”,101},
    
                            {“wangmazi”,102},
    
                            {“zhaowu”,103},
    
                            {“chengliu”,104}
    );//定义一个结构体类型为std的结构体数组std1并初始化。

    (2)在定义结构体类型的时候顺便定义结构体变量

    例如:

    struct  std{
        char name[20];
        int nub;
    } std1[5] ={{“zhangsan”,100},
    
                {“lisi”,101},
    
                {“wangmazi”,102},
    
                {“zhaowu”,103},
    
                {“chengliu”,104}};//定义一个结构体类型为std的结构体数组std1并初始化。

     

    (3)直接说明结构体变量

    例如:

    struct  {
    char name[20];
    int nub;
    } std1[5]{{“zhangsan”,100},
    
              {“lisi”,101},
    
              {“wangmazi”,102},
    
              {“zhaowu”,103},
    
              {“chengliu”,104}};//定义一个结构体类型为std的结构体数组std1并初始化。

     

     

    2)结构体数组的使用

    结构体数组的使用和其他数组的使用方式一样,首先将数组中的结构体元素通过下标找到。之后找到的结构体元素的变量,将结构体变量和成员之间用”.”连接。

    例如:

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std std1[5] ={
               {“zhangsan”,100},
               {“lisi”,101},
               {“wangmazi”,102},
               {“zhaowu”,103},
               {“chengliu”,104}
    };

     如上例子,先定义一个结构体,之后在定义一个结构体数组std1。如果需要使用std1结构体数组。

    std1[0].name = “zhangsan”;//将结构体数组中的第一个结构体变量的成员name赋    //值为zhangsan.

    std1[0].nub = 100; //将结构体数组中的第一个结构体变量的成员nub赋值为100

    std1[1].name = “lisi”;//将结构体数组中的第二个结构体变量的成员name赋    //值为lisi.

    std1[1].nub = 101; //将结构体数组中的第二个结构体变量的成员nub赋值为101

    char  na[20] ;

    strcpy(na,std1[0].name); //将结构体变量中的第一个结构体变量的name赋值给字符数组na;

    Int   nn = std1[0].nub; //将结构体变量中的第一个结构体变量的nub赋值个nn。

    因此获得第二个结构体变量中成员的值的话只需要通过下标找到对应的结构体变量,之后在通过”.”获取结构体成员的值

    int   nn1 = std1[1].nub;

     

    5 结构体指针

    结构体指针指的是一个指向结构体变量的指针变量。他和数组指针、函数指针一样都是指向数据集合的首地址。

    在使用结构体的时候可以通过结构体指针间接的操作结构体中的成员。

    1)结构体指针的定义

    结构指针的定义格式如下:

    struct 结构体名 *结构体指针变量名;

    struct 代表定义的是一个结构体指针

    结构体名为要定义的结构体类型的名称

    * 代表的是定义一个指针变量

    结构体指针变量名为定义的结构体变量的名称

    例如:

    struct std  *std1;//这就代表定义一个结构体类型为std的结构体指针,结构体指针的变量名为std1。

    2)构体指针的初始化

    结构体指针变量在使用的时候必须赋值才能使用,因此在定义结构体指针变量的时候可以进行初始化,初始化的方式和其他指针一样,只需要在定义的时候给结构体指针变量赋一个结构体变量的地址即可。

    例如:

    struct std  std1;
    struct std  *std2 = &std1;//这是将结构体变量std1的地址赋值给定义的std2结构体指针变量。

    3)结构体指针的使用

    结构体指针变量的使用和结构体变量一样,可以单独对结构体中的成员进行赋值。其访问的一般形式为:

     (*结构指针变量).成员名

       或为:

    结构指针变量->成员名

            

    第一种访问形式很明确,首先是通过“.”访问结构体成员,之后将结构体指针变量中的地址通过*取出地址的值。

    第二种是直接哟结构体指针变量访问结构体成员。之间是使用”->”符号连接。而使用”->”连接之后的的成员名是指向次成员所在地址的首地址。

    例如:

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std  std1;
    struct std  *std2 = &std1;

    第一种通过点取出成员的值

    char na[20] = std.name ;//通过结构体变量取出结构体成员的值

    char na[20] = *(std1.name) ;//通过结构体指针变量取出结构体中成员的值

    char na[20] = std->name ;//通过结构体指针变量取出结构体中成员的值

    以上这三种方式取出的成员的值是一样的,因此可以根据不同的需求对结构体中的成员进行操作。对结构体成员赋值的方式只需要将其逆置就可以了。

     

     

    6 指向结构体数组的指针

    指针变量可以指向一个结构数组,这时结构指针变量的值是整个结构数组的首地址。结构指针变量也可指向结构数组的一个元素,这时结构指针变量的值是该结构数组元素的首地址。

    设p为指向结构数组的指针变量,则ps也指向该结构数组的0号元素,ps+1指向1号元素,ps+i则指向i号元素。这与普通数组的情况是一致的。

    指向结构体数组的指针的使用和其他的指针结构体指针变量一样,只是结构体数组指针指向的地址是数组中第一个结构体元素的地址。如果要使用数组中的第二个结构体元素的值的话只需要将指向结构体数组的指针加1即可。依次类推就可以操作整个结构体数组中的值。

    例如:

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std std1[5] ={
                {“zhangsan”,100},
                {“lisi”,101},
                {“wangmazi”,102},
                {“zhaowu”,103},
                {“chengliu”,104}
    };

    struct std  *std2 = &std1;//std指向结构体数组中的首地址。

    char na[20] = std2->name;//通过指向结构体数组的指针获取结构体数组的第一个结构体成员的name元素的值。

    std2 ++;将指向结构体数组的指针的值加1,表示std2指向结构体数组中的第二个结构体成员地址。等价于std2 = &std1[1]。

    char na1[20] = std2->name;//通过指向结构体数组中第二个结构体变量中name成员的值。

    7 结构体类型作为函数参数和返回值

    1)在C语言中结构体类型的变量也可以作为函数参数或者函数的返回值使用。使用结构体变量作为函数参数会将C语言中的所有成员传递过去通过传递的形参名通过“.”调用结构体中的成员

    例如:

    #include <stdio.h>
    #include <stdlib.h>
    
    struct  std{
        char name[20];
        int nub;
    };
    
    void ave(struct std ps);
    main()
    {
        struct std std1 = { "zhangsan", 100 };//定义一个结构体变量并初始化
        ave(std1);//将结构体变量作为实参传递给其他函数。
    }
    void ave(struct std ps)
    {
        char na[20] = ps.name;//通过指向结构体数组的指针获取结构体数组的第一个结构体成员的name元素的值
        printf("%s\n", ps.name);
    }

    2)结构体成员作为函数的返回值同样跟其他变量作为函数返回值一样,只不过返回的是一个结构体类型的变量,这个变量包含了返回结构体中所有的成员变量,可以通过返回的结构体获取其中的值将函数处理之后的数据传递到其他函数。

    例如:

    #include <stdio.h>
    #include <stdlib.h>
    struct  std{
        char name[20];
        int nub;
    };
    struct std ave(struct std ps);
    struct std std1 = { "zhangsan",100 };//定义一个结构体变量并初始化
    main()
    {
        struct std std2 =  ave(std1);//将结构体变量作为实参传递给其他函数。
        printf("name = %s\n", std2.name); //输出返回结构体中name中的值
        printf("nub= %d\n", std2.nub); //输出返回结构体中的name中的值
        system("pause");
    }
    struct std ave(struct std ps)
    {
        strcpy(ps.name, "lisi");//修改传递过来的std1结构体中的name,将其修改为lisi
        ps.nub = 101; //修改传递过来的std1结构体中的nub,将其修改为101构体成员的name元素的值
        return ps;//返回修改之后的结构体
    }

    输出结构为:

    name = lisi
    nub = 101

    7 结构体指针作为函数参数和返回值

    1)在ANSI C标准中允许用结构变量作函数参数进行整体传送。但是这种传送要将全部成员逐个传送,特别是成员为数组时将会使传送的时间和空间开销很大,严重地降低了程序的效率。因此最好的办法就是使用指针,即用指针变量作函数参数进行传送。这时由实参传向形参的只是地址,从而减少了时间和空间的开销。

    结构体指针作为函数参数进行传递和普通的指针变量传递一样,传递过去的地址形参使用的时候和结构体指针的使用一样。

    例如:

    #include <stdio.h>
    #include <stdlib.h>
    struct  std{
        char name[20];
        int nub;
    };
    void ave(struct stu *ps);
    main()
    {
        struct std std1 = { "zhangsan", 100 };//定义一个结构体变量并初始化
        struct std *ps;//定义一个结构体指针
        ps = &std1;//将结构体变量std1的地址赋值给结构体指针变量pa
        ave(ps);//将结构体指针变量作为实参传递给其他函数。
    }
    void ave(struct std *ps)
    {
        char na[20] = ps->name;//通过指向结构体数组的指针获取结构体数组的第一个结构体成员的name元素的值
        printf("%s", ps->name);
    }

    如以上例子所示,首先定义一个std的结构体,之后在主函数中定一个结构体变量和结构体指针变量。再将结构体中变量的地址赋值给结构体指针。通过结构体指针将std1中的数据传递给ave函数。在ave函数中通过传递的结构体指针获取std1中name成员的值。最后na中存储的值为zhangsan。

     

    2)结构体指针作为函数的返回值。结构体指针作物函数的返回值和结构体变量作为函数的返回值一样,只不过是在使用返回的结构体指针变量的时候,不能通过“.”来获取结构体中成员值,而是使用“->”来获取结构体中的成员值。

    例如:

    #include <stdio.h>
    #include <stdlib.h>
    struct  std{
        char name[20];
        int nub;
    };
    struct std *ave(struct std *ps);
    struct std std1 = { "zhangsan",100 };//定义一个结构体变量并初始化
    main()
    {
        struct std *std2 =  ave(&std1);//将结构体变量作为实参传递给其他函数。
        printf("name = %s\n", std2->name); //输出返回结构体中name中的值
        printf("nub = %d\n", std2->nub); //输出返回结构体中的name中的值
        system("pause");
    }
    struct std *ave(struct std *ps)
    {
        strcpy(ps->name, "lisi");//修改传递过来的std1结构体中的name,将其修改为lisi
        ps->nub = 101; //修改传递过来的std1结构体中的nub,将其修改为101
                        //构体成员的name元素的值
        return ps;//返回修改之后的结构体指针    
    }

    输出结果为:

    name = lisi
    nub = 101

     

    联合体

    在介绍结构体的时候介绍了联合体,联合体又被称为共用体,也是程序可以自己定义的一种复杂数据类型。联合体和结构体都可以存储不同数据类型的数据,但是联合体在使用的时候是根据联合体中最大的数据类型而进行空间开辟的。而结构体是根据结构体中所有成员占有空间的大小开辟的。联合体和结构体在使用上也不同,结构体可以对每一个成员进行赋值。而结构体在使用的时候只能对其中的某一个成员赋值,如果对已经赋值的联合体再次进行赋值,那么以前对联合体赋的值将被覆盖。

    1 联合体的定义

    联合体(共用体)的定义:union  共用体名称 { 成员列表  }共用体变量名;

    联合体的定义方式和结构体一样有三种方式:

    1)常见的先定义联合体,这后定义联合体变量

    例如 :

    union  std{
        char name[20];
        int nub;
    };
    
    union  std std1,std2;

    这种方式是首先定义一个联合体std,在需要使用联合体变量的时候通过struct std 作为联合体类型,定义联合体变量std1和std2。

     

    第二种方式是在定义联合体类型的时候顺便定义联合体变量

    例如:

    union  std{
        char name[20];
        int nub;
    } std1,std2;

    这种方式是在定义联合体类型的时候顺便定义了联合体变量std1和std2.在后面需要使用联合体的时候不需要定义,直接使用std1和std2即可。

    3)直接说明联合体变量

    例如:

    union  {
        char name[20];
        int nub;
    } std1,std2;

    这种方式定义的联合体只能通过std1和std2两个联合体变量进行操作,而不能在定义其他的联合体变量

    2 联合体的初始化

    联合体的出初始向相比结构体来说相对简单,联合体只能初始化联合体中第一个成员的值,其他成员的值无法进行初始化。初始化的方式和结构体一样更具联合体变量不同的定义方式,联合体的初始化方式不一样。

    1)定义完联合体后,定义联合体变量的时候初始化

    例如 :

    struct  std{
        char name[20];
        int nub;
    };
    
    struct std std1,std2 = {“ zhangsan”};

    第二种是在定义联合体时顺便定义联合体变量的时候初始化

    例如:

    struct  std{
        char name[20];
        int nub;
    } std1,std2 = {“ zhangsan”};

    直接说明定义结构体变量的定义方式也可以使用第二种初始化方式进行初始化。

    2 联合体的使用

    联合体的使用和结构体一样,联合体和成员之间使用“.”连接。

    union std{
        char name[20];
        int nub;
        double dd;
    };
    
    union uct std std2 = {“ zhangsan”};

     

    1)  获取联合体中的成员值

    char name1[20] = std2.name;
    int aa = std2.nub;
    double  cc = std2.dd;

    以上操作方式是将联合体中的name、nub的dd中的赋值给name1、aa和cc。

    2) 给联合体中的成员赋值

    char name1[20] = “zhangsan”;
    std2.name = name1;
    int aa = 100;
    std2.nub = aa;
    double  cc = 88.8;
    td2.nub = cc;

    以上操作方式是给联合体中的name、nub的dd中的赋值name1、aa和cc。

    注意:联合体中最后一次写入的值是最准确的,之前的值将会不准确。这是因为后写入的数据可能架构之前写入的数据部分覆盖了。

     

    枚举

    枚举类型是一个集合类型,将枚举变量的类型限制在一个范围之内。枚举类型的元素取值不能超过定义的范围。需要注意的是枚举类型是一种基本数据类型,而不是一种构造类型,因为它不能再分解为任何基本类型。枚举集合中的元素是一些命名的整形变量,元素之间用逗号隔开。

    枚举一般用于流程控制,一般和switch联合使用。

    枚举在使用的时候必须要赋值,类型必须为枚举定义的类型

     

    1 枚举的定义

    枚举类型的定义格式如下:enum 枚举名{ 枚举值表 };

    例如:

    enum color{red,yellow,blue};//这是定义一个关于颜色的枚举类型,元素为red、 //yellow和blue.

    枚举变量的定义:

    和结构体、联合体的定义一样,都可以在枚举定义之后或者枚举定义时定义枚举变量。

    例如:

    1)在枚举定义之后定义枚举变量

    enum color{red,yellow,blue};
    enum color a,b,c;//定义枚举变量a,b,c

    2)在定义枚举的时候定义枚举变量

    enum color{red,yellow,blue} a,b,c;//定义枚举变量a,b,c

    可以手动设定枚举成员的值,从而自定义摸一个范围内的整数。第一个枚举成员的默认值为整形的0,如果第一个成员设置了值,那后面的枚举成员的值都在前一个成员基础上加1。

    例如:

    enum color{red,yellow,blue};

    以上例子而言,没有对举类型的成员变量定义一个整形数值,那么red的值为0,yellow的值为1,blue的值为2.

    enum color{red = 3,yellow,blue};

    以上例子而言,没有对举类型的成员变量red定义一个值3,那么red的值为3,yellow的值为4,blue的值为5.

    2 枚举的使用

    前面说过枚举类型一般用于流程控制,和switch一起使用。

    例如:

    #include <stdio.h>
    #include <stdlib.h>
    enum color
    {
        rad,yellow,blue
    };
    
    int  main()
    {
        enum color col;
        col = rad;//注意,在使用枚举变量之前一定要对枚举变量赋值,如果不赋值则会报错。
                  //col = 0;和col = red;等价
        switch (col)
        {
        case rad:
            break;
        case yellow:
            break;
        case blue:
            break;
        default:
            break;
        }
        printf("%f\n", sdt1.b);
        system("pause");
        return 0;
    }

    typedef类型

    typedef类型为一种数据类型(基本类型或自定义数据类型包括结构体或者联合体)定义一个新名字,不能创建新类型。

    typedef定义的一般形式为:

    typedef 原类型名  新类型名

    使用方法如下:

    例如:

    有整型量a,b,其说明如下:

        int a;

    其中int是整型变量的类型说明符。int的完整写法为integer,为了增加程序的可读性,可把整型说明符用typedef定义为:

    例如:

     

    typedef int INTEGER
    
    这以后就可用INTEGER来代替int作整型变量的类型说明了。  
     INTEGER a,b;    它等效于:   int a,b;

     

    typedef char NAME[20];    表示NAME是字符数组类型,数组长度为20。然后可用NAME 说明变量,如:    
    NAME a1; 完全等效于: char a1[20];

     

     typedef struct str  {
            char name[20];
            int age;
    } STU;
    
    定义STU表示stu的结构类型,然后可用STU来说明结构变量:
    
    STU body1;

     

     

    展开全文
  • 在联合体中,存储一个int整型四个char类型时,只开辟四个字节的空间,而这时intchar其实是公用这四个字节的空间,char就是int,int就是char,所以通过ji将一个数存进int类型后,再将这个数以char类型能拿来,...

           在每一个计算机中都有他自己的IP地址,而IP地址是如何在计算机中存储的是一直以来我们都不知道的。

           在计算机中,IP地址的存放是由10进制数组组成的,而计算机将IP地址呈现给我们时则是以点分十进制表示的。

           例如  IP地址为 57.163.109.40  ,其实他在计算机中存储的是  678273849,而计算机就是利用这个十进制数字将IP地址以点分十进制进行表示,打印在我们的面前。

           在联合体内部将十进制数字存储起来,得到的是一个占有四个字节的整形,而最终要将每个字节用点隔开将每个字节中的二进制数字打印出来,形成我们所看见的形式。在联合体中,存储一个int整型和四个char类型时,只开辟四个字节的空间,而这时int和char其实是公用这四个字节的空间,char就是int,int就是char,所以通过ji将一个数存进int类型后,再将这个数以char类型能拿来,这时拿出来的就是每个字节的数了,然后又以10进制进行打印,让IP地址呈现在我们眼前。

           当然,打印出来的IP地址会存在大小端的问题,视情况而定。

    #include<stdio.h>
    union Ip
    {
    	unsigned int num;
    	struct
    	{
    		unsigned char c1;
    		unsigned char c2;
    		unsigned char c3;
    		unsigned char c4;
    	}ip;
    };
    int main()
    {
    	union Ip my_ip;
    	my_ip.num = 678273849;
    	printf("%d.%d.%d.%d", my_ip.ip.c1, my_ip.ip.c2, my_ip.ip.c3, my_ip.ip.c4);
    	system("pause");
    		return 0;
    }

     

    展开全文
  • 联合结构类似,但是共享一个存储空间(每次只能存储一个成员) 枚举:是一种整形类型结构体结构体的声明结构标记声明struct part { int number; char name[256]; };struct part part1;//不能去掉struct//也...
  •  联合说明和联合变量定义 联合也是一种新的数据类型, 它是一种特殊形式的变量。 联合说明和联合变量定义与结构十分相似。其形式为: union 联合名{ 数据类型 成员名; 数据类型 成员名; ... } 联合变量名...
  • 第十章 结构体与共用体 第三节 共用体,枚举类型,typedef 1. 共用体 又叫联合 把几种不同类型的变量存放到同一段内存单元(同一个内存地址开始的单元中),几个变量共同占用同一段内存的结构,就叫共用体 定义形式:...
  • 一、联合的定义 定义一个联合类型的一般形式为: union 联合名 { 成员表 }; 成员表中含有若干成员,成员的一般形式为: 类型说明符 成员名 成员名的命名应符合标识符的规定。 例如: union perdata { int ...
  • 本程序中定义了一个结构mem,它有两个成员namephone 用来表示姓名电话号码。在主函数中定义man为具有mem 类型的结构数组。在for语句中,用gets函数分别输入各个元素中两个成员的值。然后又在for语句中用printf...
  • C语言-结构体和联合

    2021-05-09 11:11:11
    C语言中提供了两种类型的聚合类型,数组结构。数组是相同类型元素的集合,它的每个元素是通过"下标"引用或“指针”间接访问来选择;结构是一些值的集合,这些值被称为结构的成员,各个成员可能具有不同的类型,...
  • C语言结构体联合和枚举

    千次阅读 2015-08-20 09:47:44
    结构体是C语言中一种非常重要的数据结构,与数组不同,它的成员(结构体称元素)可以具有不同的类型,并...联合和结构体相似,但联合的所有成员都共享同一存储空间,每次只能存储一个成员。枚举是命名的一组整数值。
  • C语言结构体联合

    2021-03-26 17:34:14
    C语言结构体和联合 一、结构体的介绍 结构体的概念 在实际问题处理中,一组数据往往具有不同的数据类型。 例如在学生登记表中,姓名-字符型,学号-整型或字符型,年龄-整型,性别-字符型,成绩-整型或实型 数组...
  • 1、联合的用法、语法结构与结构非常相似,但联合中所有成员分配的内存是同一块;(也就是只能保存一个成员信息,联合的空间以最大成员所占的空间为值); 2、联合可以用一块内存对应多种数据类型; 3、联合与...
  • C语言结构体是不同数据类型的组合,可以实现对数据的整合,可以定义自己的数据集,结构体的定义如下struct 结构体名 { 类型 成员名1; 类型 成员名2; ......};例如我们定义一个结构体代表用户struct user { ...
  • 一、结构体  1、什么是结构体  2、结构体语法格式  3、结构体所占内存空间  4、结构体成员赋值  二、联合  1、什么是联合  2、联合语法格式  三、枚举  1、什么是枚举  2、枚举语法格式 一、...
  •  在C语言中,结构体是一种数据结构,是C提供的聚合类型(C提供了两种聚合类型:数组结构)的一种。结构体与数组的区别是:数组是相同类型的集合,而结构体可能具有不同的类型。 结构体也可以被声明为变量,数组...
  • C语言中构造类型分为数组,结构体联合和枚举四种类型,其中,结构体是机多种数据类型于一身的一种构造类型。 声明结构体类型 通常在主函数外部进行声明(全局区) struct Stu { char name[10]; int age; ...
  • 5.2 联合体共用体 5.2.1 共用体及其定义成员的访问 1 联合的概念 联合表示几个变量共用一段内存位置, 在不同的时间保存不同的类型数据 不同长度的变量 2 联合的定义 union 联合名 成员1定义 成员2定义 成员n定义...
  • 结构体 ...联合和结构体巧妙使用 结构体 结构是一些值的集合,这些值成为成员变量。结构的每个成员可以是不同类型的变量。 结构体的声明 struct tag //结构体类型名 { mem...
  • 目录: 结构体 枚举 联合 结构体 ...这里的ab就是结构体的成员变量,s1就是结构体的名字。 当我们在声明结构体时,可以忽略掉结构体标签s,进行不完全声明 struct { char a; int b; }s2...
  • 一、结构体 结构体是一些值得集合,这些值成为成员变量。结构体每个成员可以是不同成员。 1、结构体类型的创建 结构体的声明: struct tag//关键字+标签 { member-list;//成员列表 }variable-list;//变量 ...
  • 匿名联合与匿名结构体: http://moosewoler.blog.163.com/blog/static/698660520105264486311/ C++的使用参看: http://blog.csdn.net/kunshan_shenbin/article/details/5747063
  • C语言结构体和联合体

    2011-05-16 20:23:00
    结构与联合 结构类型定义结构变量说明  在实际问题中,一组数据往往具有不同的数据类型。例如, 在学生登记表中,姓名应为字符型;学号可为整型或字符型; 年龄应为整型;性别应为字符型;成绩可为整型...
  • 一、结构体  概述:简单来说结构体就是一些值的集合,这些值是它的成员,只不过各个成员可能具有不同的类型。  结构体的声明:一种不完全声明,一种采用重命名typedef,再就是标准命名。  不完全声明:不声明...
  • 结构体 struct 类型定义点...与结构体类似的类型1、联合作用 :存放不同类型的数据 (共同体) 同事只能存储一个数据关键字 :union 和结构体类似联合与结构体的区别: 结构体: 是每个成员有地址分配 即有单独的...

空空如也

空空如也

1 2 3 4 5 ... 14
收藏数 270
精华内容 108
关键字:

c语言联合和结构体

c语言 订阅