结构体【C语言从入门到精通系列(十)】

C语言提供了结构体,来管理不同类型的数据组合。
如,针对某学生,存在姓名,性别,年龄,身高等不同类型的数据,可以使用结构体将这些信息定义为一个变量。


1.结构体的声明

定义结构体变量之前,需要先声明一个结构体类型。声明一个student结构体的代码示例如下:

struct student
{
    char name[20];
    char gender[10];
    int age;
    float height; 
};

(声明结构体类型,末尾是必须加分号的。)


2.结构体的定义

定义结构体对象的时候,要避免结构体变量的变量名与结构体类型名相同,不然可能带来风险。

此外,对于结构体对象的访问,必须一个成员、一个成员地访问,不支持同时访问。

# include <stdio.h>

struct student
{
    char name[20];
    char gender[10];
    int age;
    float height; 
};


int main(){
    struct student s001={"Tom", "male", 18, 170.5};
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.age,s001.height);
    return 0;
}

程序输出结果如下:

Tom,male,18,170.500000


也可以使用输入函数的方式,示例如下:

# include <stdio.h>

struct student
{
    char name[20];
    char gender[10];
    int age;
    float height; 
};


int main(){
    struct student s001;
    scanf("%s %s %d %f",&s001.name,&s001.gender,&s001.age,&s001.height);
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.age,s001.height);
    return 0;
}

输入:

Tom male 18 170.5

输出:

Tom,male,18,170.500000


3. 结构体数组

结构体数组是数组,不是结构体。
结构体数组,可以理解为,元素是结构体的数组

# include <stdio.h>

int main(){
    struct student{
    char name[20];
    char gender[10];
    int age;
    float height; 
    }stu01[5]={{"a","male",18,165.3},
               {"b","female",17,160.6},
               {"c","male",18, 178.2},
               {"d","male",16,163.5},
               {"e","female",20,177.6}    

};// 也可以在声明结构体类型的时候,一并定义变量
    int i;
    for(i=0;i<5;i++){
        printf("%s,%s,%d,%f\n",stu01[i].name,stu01[i].gender,stu01[i].age,stu01[i].height);
        }
    return 0;
}

输出结果:

a,male,18,165.300003
b,female,17,160.600006
c,male,18,178.199997
d,male,16,163.500000
e,female,20,177.600006


4.修改值

要修改结构体中的某个值也是支持的。参考:

stu01[0].age=20;

5.结构体对齐

结构体的大小必须是其最大成员的整数倍。(最大成员不包含数组类型)。
此即结构体的对齐。
对齐的目的是:为了让CPU高效地去取内存上的数据。

下边用一段代码来说明:

# include <stdio.h>

int main(){
    struct student
{
    char name[20];
    char gender[10];
    int age;
    float height; 
};
    struct student s001={"Tom","male",20,175.6};
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.age,s001.height);
    printf("%d %d %d %d\n",sizeof(s001.name),sizeof(s001.gender),sizeof(s001.age),sizeof(s001.height));
    printf("%d\n",sizeof(s001));
    return 0;
}

程序输出结果如下:

Tom,male,20,175.600006
20 10 4 4
40

四个元素的大小分别是20字节,10字节,4字节,4字节。
最终得到结构体的大小为40字节。计算方法为:
先对每个元素的大小进行求和,即20+10+4+4的结果为38字节。然后在此基础上,不考虑数组元素的大小,只考虑除数组以外元素的大小。这里需要考虑的是后两个元素,都占了4字节。则在38的基础上,再加上2,即满足了为最大成员大小4的整数倍。(不考虑数组大小)此即结构体的对齐。

如果将float height改为double height,则20+10+4+8的结果为42,最终结构体的大小为48字节。(大多数时候都是4或8的倍数)


此外,假设已知最大成员的大小,此时还有两个更小的成员,这两个更小的成员的字节大小的和小于最大成员大小。则可以将这两个更小的成员写在一起以节省空间。
如:

# include <stdio.h>

int main(){
    struct student
{
    char name[20];
    char gender[10];
    double height; //8字节
    short age; //2字节
    char level; 
};
    struct student s001={"Tom","male",175.6,20,"A"};
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.height,s001.level);
    printf("%d %d %d %d %d\n",sizeof(s001.name),sizeof(s001.gender),sizeof(s001.height),sizeof(s001.age),sizeof(s001.level));
    printf("%d\n",sizeof(s001));
    return 0;
}

输出结果:

Tom,male,858993459,0.000000
20 10 8 2 1
48

空间分析:

非数组的最大成员double大小为8字节
先考虑数组,name和gender的空间大小的和为20+10=30,修正为8的倍数,为32字节。

再考虑非数组,heightagelevel分别为8,2,1个字节。
此时height独占8个字节,agelevel的和小于8个字节,则可以结合起来占8个字节。
所以该结构体一共占了32+8+8=48字节。


6.结构体指针

一个结构体变量的指针,即该结构体变量所占用内存段的起始地址。

在下边的代码中,先定义一个结构体类型student,然后定义一个结构体对象s001并打印之。
然后再定义一个结构体指针变量p,指向结构体s001。并尝试通过指针变量p来访问每个成员。

# include <stdio.h>


int main(){
    struct student
    {
        char name[20];
        char gender[10];
        int age;
        float height; 
    };
    
    struct student s001={"Tom","male",18,172.5};
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.age,s001.height);
    struct student *p;
    p=&s001;
    printf("%s,%s,%d,%f\n",(*p).name,(*p).gender,(*p).age,(*p).height); //不推荐的访问方法
    printf("%s,%s,%d,%f\n",p->name,p->gender,p->age,p->height);  //推荐的访问方法
    return 0;
}

程序输出结果:

Tom,male,18,172.500000
Tom,male,18,172.500000
Tom,male,18,172.500000


(*p).name为例,访问结构体的每个成员时,不能写作*p.name,因为点.的优先级高于*,不加括号的话则会被解读为向指针变量取成员,这样的逻辑是错误的。此外,即便加了括号,这样的写法也是不推荐的。因该写法过于繁琐,c语言为此提供了->符号来实现对结构体指针成员的访问。通过指针变量p访问成员name则可写作p->name。日常实践和应用中通常建议使用此种方法。(*p).name的写法可能会让人觉得困惑,一般要避免使用,了解即可。


7. typedef方法

7.1 给结构体取别名

定义结构体类型的时候,使用typedef来起别名,可以让代码更加简洁。

# include <stdio.h>

int main(){
    typedef struct student
    {
        char name[20];
        char gender[10];
        int age;
        float height; 
    }stu;
    stu s001={"Tom","male",18,172.5};
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.age,s001.height);
    return 0;
}

输出:

Tom,male,18,172.500000

在该示例中,通过typedef方法,给定义的student类型起了一个别名stu。然后再定义每一个该类别的结构体变量时,代码都会更为简洁。(此时依然支持struct student s001={"Tom","male",18,172.5};但不会再采用。)


7.2 给结构体指针取别名

同理,对于结构体指针类型,我们也可以通过typedef方法来起别名pstu,然后起到简化代码的作用。

# include <stdio.h>

int main(){
    typedef struct student
    {
        char name[20];
        char gender[10];
        int age;
        float height; 
    }stu,*pstu;
    stu s001={"Tom","male",18,172.5};
    printf("%s,%s,%d,%f\n",s001.name,s001.gender,s001.age,s001.height);
    pstu p001=&s001;
    printf("%s,%s,%d,%f\n",p001->name,p001->gender,p001->age,p001->height);
    return 0;
}

输出:

Tom,male,18,172.500000
Tom,male,18,172.500000

(其中stu即等价于struct studentpstu即等价于struct student *
pstu p001=&s001;也可以写作stu *p001=&s001。)


7.3 其他用途

取别名不仅仅是为了简化代码,也可以起到一定的筛选作用。
在特定的地方,比如某个局部空间内,可以通过给某一个变量起别名,比如给int类型起别名为ingeter,来实现对在区域内所以int类型的筛选。比如若我们存在需求:将该区域内别名为ingeterint变量转变为浮点类型,则只需要修改该别名对应的对象即可。而该区域内,直接使用int定义的整型变量则不会受其影响。

# include <stdio.h>

int main(){
    typedef int ingerter;
    int a = 1;
    ingerter b=2;
    ingerter c=3;
    return 0;
}

如若将typedef int ingerter;中的int修改为其他类型,则变量a将不受影响,变量b和变量c将会同时改变。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值