结构体的定义和使用

     C语言提供了基本数据类型,如char,short,int,float...等类型,我们称之为内置类型。

     程序设计人员可以使用结构体来封装一些属性,设计处新的类型,在C语言中称之为结构体类型。

一、结构体类型的设计

     在C语言中,结构体是又程序开发者自己设计的一种数据类型。可以使用结构体(struct)来存放一组不同类型的数据,结构体的定义形式如下:

struct 结构体名
{
成员列表(可以是基本数据类型,指针,数组或其他结构类型)
};

示例:设计一个学生类型,要求有学生学号,学生姓名,性别以及年龄。

struct Student
{
   int id;
   char name[10];
   char sex[8];
   int age;

};

      在这个结构体中,学号id及年龄用整型存储,姓名及性别用字符型数组存储。

      在后面要使用这个Student结构体时,在.c文件中必须输入struct Student,非常的不方便,所以我们可以对其进行重命名:

typedef struct Student
{
    int id;
    char name[10];
    char sex[8];
    int age;
}Student;

     重命名后,在使用过程中就可直接输入Student来使用此结构体。

     但是在.cpp文件中就可直接输入Student来直接使用。

二、结构体变量初始化

       结构体设计完成后,就要对其中的变量进行初始化,我们在主函数中对其变量进行初始化,以上面定义的Student类型为例,我们对其进行初始化,在初始化结构体时,要注意char型要用双引号标注,不同的类型中间用“,”隔开:

//在一般情况下,我们可以用如下形式初始化结构体
typedef struct Student
{
    int id;
    char name[10];
    char sex[8];
    int age;
}Student;

int main()
{
    Student stu1={2401,"zs","male",20};

    return 0;
}


//我们也可以用以下方式初始化结构体
// 在声明时初始化成员
struct Student student2 = {2401,"lisi", female,21};

// 使用花括号初始化
struct Student student3 = {.name="Charlie", .age=25, .gpa=3.2};

三、结构体的嵌套

     在结构体的使用中,我们也可以在结构体内引用其他结构体。

     例如:我们要定义一个学生类,其中要有学生的姓名,年龄,以及家庭信息,这里的家庭信息就需要定义另外一个结构体,在家庭信息中要包含联系电话,家庭成员数以及家庭住址。

typedef struct Home
{
    char phone[11];   //联系电话
    int  member;      //家庭成员数
    char adress[20];  //家庭住址
}Home;

typedef struct Student
{
    char name[10];    //学生姓名
    int  age;         //年龄
    Home home;        //家庭信息
}Student;

       在这里要注意,定义"Student"这个结构体时,因为其中有嵌套的结构体"Home",要将“Home",结构体定义在"Student"之上,否则编译器会报错;或者在使用"Student"时,在"Student"前面声明"Home"。

四、结构体成员的访问

     结构体变量的成岩用 . 访问。

     获取结构体变量成员的一般格式为:结构体变量.成员名.

例如:要修改Student类中的id
      stu1.id=2402;
      对于这种非字符型成员,可直接修改


但是字符型类型就要用到string函数进行修改,
Student stu1={2401,"zs","male",20};
strcpy(stu1.name,"lisi");       //将lisi拷贝给stu1中的name
memcpy(stu1.name,"lisi",5);     //将lisi拷贝给stu1中的name,拷贝5B

五、结构体数组

       一个结构体变量中可以存放一组有关联的数据,如果有10个学生的数要参加运算,我们就要用到数组,这就是结构体数组

       以上面的Student结构体为例,我们来定义一个结构体数组:

typedef struct Student
{
    char name[10];
    int age;

}Student;

int main()
{

    Student stus[3];   //定义一个有3个学生的数组

    return 0;
}

         将结构体内存初始化为0:

 //指针方法:  
    char* p=(char*) stus;
    for(int i=0;i<3*24,i++)
    {
       *(p+i) =0 ;
    }

//memset函数:
    memset(stus,0,3*sizeof(Student));

       将stus[3]数组的值拷贝给stus2[3]:

Student stus2[3];
//for循环遍历赋值:
for(int i=0;i < 3;i++)
{
   stus2[i] = stus[i];

}

//memcpy拷贝:
memcpy(stus2, stus, 3*sizeof(Student));

六、结构体与指针

      当涉及到结构体指针时,我们将结构体的地址存储在指针变量中。我们就可以通过指针来操作和访问结构体内的成员。我们可以通过指针来访问结构体的成员,从而避免了对整个结构体的拷贝,这样就可以减少内存的使用量。

     结构体指针使用 `->`(指向符) 运算符来访问结构体的成员,与通过 `.` 运算符来访问普通结构体变量的成员相似。

      在使用中,为了避免在函数调用中复制整个结构体,我们可以将结构体指针作为参数传递给函数。这允许函数直接访问和修改结构体的内容。

#include <stdio.h>

struct Student 
{
    char name[10];
    int age;
   
} Student;                          // 定义结构体

int main() 
{
    Student stu={"zs",20};           //初始化结构体成员
    Student *pstu=&stu;              //定义结构体指针变量
    
    (*pstu).age=30;                 
    pstu->age=30;                    //通过指针变量将学生年龄改为30
    strcpy(pstu->name,"lisi");       //将学生姓名改为"lisi"

    return 0;
}

 七、结构体大小

1.字节对齐方式

      内存大小的基本单位是字节(byte),理论上讲,可以从任意地址访问变量,但是CPU是以2,4或8的倍数的字节来读写内存,所以,基本数据类型的地址必须是2,4或8的倍数。那么就要求各种数据类型按照一定规则在空间上排列,这就是对齐。

       假设在32位系统中,一个int型存放在偶地址开始的地方,那么一个周期就可以读出这个32bit;如果存放在奇地址开始的地方,就要读两个周期。

指定对齐值:

      #pragma pack(n)可以改变默认对齐数。n可取1,2,4,8,16.

      VS中默认值为8

2.结构体内存大小

      结构体变量的首地址,必须是MIN{“结构体最大基本类型数据类型成员所占字节数”,指定对齐方式}大小的整数倍;

      结构体每个成员相对于结构体首地址的偏移量,都是MIN{基本数据类型成员,指定对齐方式}大小的整数倍;

      结构体的总大小,为MIN{结构体“最大基本数据类型成员所占字节数”(包括嵌套结构体内的基本类型),指定对齐方式}大小的整数倍。

      下面我们举例说明:
 

struct A
{
    int a;    //4B
    short s;  //2B+2B(偏移量,b类型是int,前方应当是int的整数倍)
    int b;    //4B

};            
    sizeof(struct A);    //12B

      如果我们对A结构体中的数据类型重新进行排列,它的总大小会不会改变呢?

​
struct A
{
    int a;    //4B
    int b;    //4B
    short s;  //2B
              
};
   sizeof(struct A);    //10B+2B=12B
   //这里加2B是因为MIN{最大基本数据类型,8}
   //最大基本类型是int,4B,10不是4的整数倍,所以要+2​

       从以上结果来看,struct A的总大小不会发生改变,那么我们再看看下面这个例子:

struct B
{
    char a;     //1B+3B
    int b;      //4B
    char c;     //1B+1B
    short d;    //2B

};
    sizeof(struct B);  12B

//下面对B中数据类型按从大到小重新排列

struct B
{
    char a;    //1B
    char c;    //1B
    short d;   //2B
    int b;     //4B
};
    sizeof(struct B);  //8B

      从上面的例子可以看出,对struct B中的基本数据类型进行了重新排列后,结构体的大小明显小了4字节,这也告诉我们,在定义结构体时,应该想办法减小内存的使用,毕竟留给我们的空间是有限的。

      用#pragma pack(n)改变默认对齐方式:

#pragma pack(1)
struct A
{
    char c;    //1B
    short s;   //2B    前方偏移量MIN{2,1}
    char d;    //1B    


};
    sizeof(struct A);    //4B



#pragma pack(2)
struct B
{
    int a;        //4B
    short b;      //2B
    long long c;  //8B

};
    sizeof(struct B);   //14B   MIN{8,2}

八、枚举

   1.枚举的定义

      枚举(enum),顾名思义,就是一一列举出来所有可能出现的情况,并且给它们取一个名字。enum是一个新的关键字,在C语言中的唯一用途就是定义枚举类型。最后的;不能少。

      用代码写出就是以下这种方式:

enum typename{valuName1,valuName2,valuName3,......};



//列出一个星期有几天

enum week{Mon,Tues,Wed,Thurs,Fir,Sat,Sun};

   2.枚举值

     枚举值默认从1开始,往后逐渐递增。

     比如,week中的Mon,Tues......Sun对应的值就是0、1、······6.

     但是,当我们给其中的某个值赋值后,后面的值就会从前面所给定的值来递增。

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

//这样,white的值就是5

   3.枚举变量

      我们也可以通过枚举类型定义枚举变量:

      enum week a,b,c;

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

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

     有了枚举变量后,我们就可以将列表中的值赋给它:

写法一:
enum week{Mom=1,Tues,Wed,Thurs,Fir,Sat,Sun};
enum week a=Mon,b=Wed,c=Sta;


写法二:
enum week{Mon=1,Tues,Wed,Thurs,Fir,Sat,Sun} a=Mon,b=Wed,c=Sta;

//可以分两行写,也可以在一行写

九、位段

      有些数据在存储时不需要占用一个完整的字节,只要占用一个或几个二进制位。所以C语言提供了一种叫做位域的数据结构。

      在定义结构体时,我们可以指定某个成员变量所占的二进制位数(Bit),这就是位域。

      我们用"类型名 : n"来决定该成员所占的位数,示例如下:

struct A
{
    unsigned char id : 4;        //id占无符号char类型的四个位
    unsigned char level : 4;     //level占无符号char类型的四个位

};

int main()
{
    A a;
    a.id=0x8;
    a.level=0x9;
    printf("%d",sizeof(A));      //输出结构体A所占的内存大小
    return 0;

}

    该代码的运行结果如下: 

      可见结构体struct A只占了一个字节的内存。   

      在使用时要注意,只有有限的几种数据类型可以使用位段;同时,位域的宽度不能超过所依附的数据类型的长度,也就是说,char类型:后面的数字不能超过8,int类型:后面的数字不能超过32。

十、联合体

      联合体就是在同一个内存空间中存储不同的数据类型,这几种数据类型共用这一块区域。

      联合体也叫共同体,在C语言中联合体的关键字是union

      比如说,我们在学校,学生有学号,老师有工号,这样我们就可以定义一个联合体,使学号和工号共用一块区域:

union  ID
{
    int stuid;    //学号
    int teachid;  //工号
};

      之后,我们要访问学号和工号,就可以通过.访问不同的类型。如下:

struct School
{
    char name[10];    //姓名
    int age;          //年龄
    union ID id;      //学号或工号
};

int main()
{
    School p={"zs",20,1};
    p.id.stuid=2;
    return 0;
}

全局结构体定义使用是一种在程序中定义使用全局的自定义数据类型的方式。结构体是一种可以包含不同类型数据成员的数据结构,它允许我们将多个相关的数据项组合在一起。 要定义一个全局结构体,可以在函数外部或文件的顶部使用关键字 `struct` 来定义结构体类型,并在结构体内部定义各个数据成员。例如: ```c struct Person { char name[50]; int age; }; ``` 上面的代码定义了一个名为 `Person` 的结构体,它包含两个数据成员:一个 `name` 字符数组和一个 `age` 整数。 要使用全局结构体,可以在程序的任何地方声明结构体变量,并为其成员赋值。例如: ```c #include <stdio.h> struct Person { char name[50]; int age; }; struct Person person1; // 声明一个全局结构体变量 int main() { // 对结构体变量进行赋值 strcpy(person1.name, "John"); person1.age = 25; // 输出结构体变量的值 printf("Name: %s\n", person1.name); printf("Age: %d\n", person1.age); return 0; } ``` 上面的代码中,我们在 `main` 函数外部定义了一个名为 `person1` 的全局结构体变量,并在 `main` 函数内部给它的成员赋值。然后,我们通过 `printf` 函数输出结构体变量的值。 需要注意的是,全局结构体变量可以在程序的任何地方访问,但是在访问之前必须先进行声明或定义。此外,全局结构体变量的作用域是整个程序,可以被多个函数共享。 希望这能帮到你!如果你还有其他问题,请继续提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值