查看: 573|回复: 0

[讨论] 工程师讲解:C语言如何节省存储空间?

[复制链接]
  • TA的每日心情

    2018-11-20 13:41
  • 签到天数: 3 天

    连续签到: 1 天

    [LV.2]偶尔看看I

    发表于 2019-10-16 09:14:46 | 显示全部楼层 |阅读模式
    分享到:
    C语言的共用体union
    共用体是一种特殊的数据类型,允许您在相同的内存位置存储不同的数据类型。什么意思呢,就是在同一块内存存储可以定义多个数据类型,但是在使用的时候,只有一个变量有效。
    这里就有一个问题,变量有大有小呀,对的,所以这个时候共用体的空间为内部变量最大占用空间的值。

    如此这般,共用体就可以通过共享存储空间,来避免当前没有被使用的变量所造成的存储空间的浪费。

    共用体的成员可以使用任何数据类型,但是一个共用体所占用的存储空间的字节总数,必须保证至少足以能够容纳其占用空间字节数最大的成员。并且共用体每次只允许访问一个成员,也就是一种数据类型,确保按照正确的数据类型来访问共用体中的数据,就是你的责任了。

    先看看union的格式:


    union [tag]{member definition;member definition;...member definition;} [variables];
    其中:

    • union为类型变量;
    • tag为共用体的标记;
    • member definition为变量的定义;

    举个例子:


    union test{int i;float f;double d;char  str[20];} data;
    通过这个例子可以看到,这个结构体的大小是多少呢?可以通过程序来确认一下。

    OK,这次我们来聊聊结构体。
    任务来了,我想让你给学生建立一个数据库,该怎么来做。
    这个学生包含的信息如下:
    • ID:也就是学号,唯一区别码,用整型表示
    • Name:姓名,用字符串表示
    • Age:年龄,用整型表示
    • Sex:性别,用字符串表示

    按照目前学过的知识我们的代码如下,比如先来一个李雷同学的吧:

    #include <stdio.h>#include <string.h> union test{int i;float f;double d;char  str[20];}; int main( ){union test data; printf( "data size : %d\n", sizeof(data)); return 0;}
    C语言 共用体的访问
    共用体的访问与结构体类似,也是有2中类型,我们只看看成员访问运算符.。
    所以按照通用的赋值方式,来看一下是否按照我们预定的方式运行。

    /*beginner/union/union2.c*/#include <stdio.h>#include <string.h>union Data {int i;float f;char str[20];};int main(){union Data data;    data.i = 123;    data.f = 456.0;strcpy(data.str, "Hello World");printf("data.i : %d\n", data.i);printf("data.f : %f\n", data.f);printf("data.str : %s\n", data.str);return 0;}
    从结果上来看,what are you弄啥嘞,感觉什么跟什么呀,只有最后的字符串是正确的,这也就间接证明了共用体使用相同的存储空间,其他类型的赋值会破坏原先的赋值,正常情况下只有最后一次的赋值才会保证正确结果。

    所以我们需要在每次赋值后直接查看结果,是可以保证结果正确的:

    /*beginner/union/union3.c*/#include <stdio.h>#include <string.h>union Data {int i;float f;char str[20];};int main(){union Data data;    data.i = 123;printf("data.i : %d\n", data.i);    data.f = 456.0;printf("data.f : %f\n", data.f);strcpy(data.str, "Hello World");printf("data.str : %s\n", data.str);return 0;}
    再次运行,可以看到结果就按照预想的进行了。
    编译运行

    #beginner/union/MakefileALL : union1 union2 union3
    union1: union1.cgcc -o union1 union1.c
    union2: union2.cgcc -o union2 union2.c
    union3: union3.cgcc -o union3 union3.c
    .PHONY : clean
    clean:rm -f union1 union2 union3
    输出结果

    $ ./union2data.i : 1819043144data.f : 1143139122437582505939828736.000000data.str : Hello World
    $ ./union3data.i : 123data.f : 456.000000data.str : Hello World
    除了共用体还有什么可以节省存储
    C语言的结构体位域
    前面可以看到,使用unoin共用体可以节省数据的存储空间。
    同样,在结构体或者共用体中,使用位域也可以达到这个效果。
    先看看什么时候可以使用位域,这个特点大多数人都不会用到,用到的大部分人都基本跟底层打交道,比如驱动开发、单片机开发等。
    先看一个最简单的例子,比如我们的红绿灯系统,先定义一个结构体:

    typedef struct{unsigned int red;unsigned int green;unsigned int yellow;} TrafficLight;
    此时如果看一下TrafficLight结构体的大小,应该是12个字节

    是我们知道对于这几种灯而言,只有2中状态,开和关,也就是1和0,也就是1个bit其实就能表达,所以针对这种情况,有了位域的概念,先看一下位域的声明:


    typedef struct{    type name : width;}
    • type:整数类型
    • name:为位域的名称
    • width:为位域中位的数量,其值需要小于等于type指定的类型大小

    所以交通灯的结构体使用位域的概念就如下所示:

    typedef struct{unsigned int red : 1;unsigned int green : 1;unsigned int yellow : 1;} TrafficLight1;
    三色红绿灯加起来一共需要3个bit,所以一个无符号整型就可以容纳这些值了,此时看一下这个结构体的长度,应该为4。
    总结一下:
    当结构体或共用体中有无符号整型或有符号整型成员时,C语言允许用户指定这些成员所占用的存储位数,即位域。通过将数据存储在它们所需的最小数目的存储位内,位域能够有效地提供存储空间的利用率,但是,要注意,位域成员必须被声明为有符号整型或无符号整型。
    代码如下:

    /*beginner/struct/struct6.c*/#include <stdio.h>
    int main(){
    typedef struct    {unsigned int red;unsigned int green;unsigned int yellow;    } TrafficLight;
        TrafficLight trafficlight;
    printf("The size of TrafficLight %d\n", sizeof(trafficlight));
    typedef struct    {unsigned int red : 1;unsigned int green : 1;unsigned int yellow : 1;    } TrafficLight1;
        TrafficLight1 trafficlight1;
    printf("The size of TrafficLight1 %d\n", sizeof(trafficlight1));
    return 0;}
    编译运行
    直接输入make就可以了。

    #beginner/struct/Makefile
    struct6: struct6.cgcc -o struct6 struct6.c
    运行输出如下:


    $ ./struct6The size of TrafficLight 12The size of TrafficLight1 4
    扩展
    既然位域指定了长度位,所以就涉及到万一赋值超过了会发生什么情况,可以通过给红绿灯赋一个大值看看。

    比如复制一个2,那么会得到如下警告:

    warning: implicit truncation from ‘int’ to bit-field changes value from 2 to 0 [-Wbitfield-constant-conversion]
    编译有警告,不过还是生成了可执行文件,运行下看看结果吧。


    回复

    使用道具 举报

    您需要登录后才可以回帖 注册/登录

    本版积分规则

    关闭

    站长推荐上一条 /2 下一条

    手机版|小黑屋|与非网

    GMT+8, 2024-3-29 20:23 , Processed in 0.107784 second(s), 17 queries , MemCache On.

    ICP经营许可证 苏B2-20140176  苏ICP备14012660号-2   苏州灵动帧格网络科技有限公司 版权所有.

    苏公网安备 32059002001037号

    Powered by Discuz! X3.4

    Copyright © 2001-2020, Tencent Cloud.