【C语言复习(二)】struct 与 union 的分析

C语言中,struct 与 union 关键字都是用来定义用户自己的数据类型,struct用来定义结构体,union用来定义共用体;一个结构体所占用的内存空间等于它的成员所占空间的总和;一个共用体所占内存空间等于它的成员中占用空间最大的成员所占的空间大小,同一时刻,只能有一个成员出于有效状态!

1、struct

(1)、思考:一个空的结构体,占用多大内存空间??

如下示例:

#include <stdio.h>
struct D
{
 
};
int main()
{
    struct D d1;
    struct D d2;
 
    printf("空结构体所占内存大小:%d\n",sizeof(struct D));
    printf("变量d1所占内存大小:%d\n",sizeof(d1));
    printf("变量d2所占内存大小:%d\n",sizeof(d2));
    return 0;
}

注意:如上的代码在codeblocks下采用gcc编译

运行结果:


   从上图中可以看出,空结构体所在内存大小为0,也正因为如此,用空结构体定义的两个变量d1和d2所占空间也为0,且起始地址相同!

就这点来说,gcc编译器是严格按照C的标准来实现的,而在C中没有定义一个空结构体应该占用多大的内存空间,所以按照标准来说就为0字节,但是又存在一个问题,用这个空结构体定义的不同变量其实地址会相同,这就有矛盾产生,因此在C++的编译器中将此改善,空结构体的大小定义为1字节;用g++编译器编译运行的结果如下图:


但是在Visual Studio 2012 及其以上的版本中编译上述代码时,会得到如图的提示,编译并不能通过,由此也能看到编译器不断的在尝试修复这个“问题”!

 

(2)、利用结构体实现柔性数组

什么是柔性数组??C语言规定,在定义数组时,必须指定其大小,且不能用变量来定义可变大小的数组。

例如:

int i = 5;
int num[i];//int num[5];

即是说,定义数组必须使用常量表达式,不能使用变量!要想实现此功能,可通过结构体和指针的配合:

  分布实现柔性数组:

A、简单的测试

编写如下代码,运行并测试:

#include <stdio.h>
 
//定义一个结构体,内含两个成员变量,一个是int类型的,另一个是int类型数组
//但没有指定数组的大小
typedef struct _SoftArray
{
int len;
int _Array[];
} SoftArray;
 
int main()
{
printf("结构体所占内存大小:%d\n", sizeof(SoftArray));
return 0;
}

结果如图:

可以看到,这个结构体没有指定成员数组的大小;

稍微修改一下代码:

#include <stdio.h>
 
//定义一个结构体,内含两个成员变量,一个是int类型的,另一个是int类型数组
//但没有指定数组的大小
typedef struct _SoftArray
{
int len;
int _Array[];
} SoftArray;
 
int main()
{
SoftArray sa;
printf("结构体所占内存大小:%d\n", sizeof(SoftArray));
printf("len成员所占大小:%d,\t起始地址:%d\n", sizeof(sa.len),(int)&(sa.len));
printf("_Array成员所占大小:%d,\t起始地址:%d\n", sizeof(sa)-sizeof(sa.len), (int)sa._Array);
return 0;
}

运行结果:


从结果中可见,此时的结构体大小等于第一个成员的大小,因为第二个数组成员“不完整”,编译器给它分配了0字节的大小,相当于只给了它一个起始地址,这就是我们实现柔性数组的关键原理了!


注意:可能会有疑问,此结构体中的第一个成员变量在如下的柔性数组实现过程中,看起来并没有起到什么关键性的作用,那么可否删去不要呢?显然不能删去,否则整个结构体中就只剩一个不完整的成员变量,是无法编译通过的,而这里的第一个成员变量,实际上还算有用,可以用它来指示当前柔性数组的大小!

 

继续改进代码,简单实现柔性数组:
#include <stdio.h>
#include <malloc.h>
//定义一个结构体,内含两个成员变量,一个是int类型的,另一个是int类型数组
//但没有指定数组的大小
typedef struct _SoftArray
{
int len;
int _Array[];
} SoftArray;
 
int main()
{
SoftArray* sa=NULL;
//定义结构体的指针,且已经分配了地址
printf("结构体初始地址为:%d\n",(int)sa);
printf("数组成员初始地址为:%d\n",(int)sa->_Array);
sa=malloc(sizeof(*sa)+sizeof(*(sa->_Array)*10));
//给结构体变量分配空间
//sa->_Array为当前结构体中数组成员的起始地址,通过*运算可以拿到其值,然后sizeof其值的字节大小
//这样做的目的是为了让这里的空间分配不受结构体中数组成员的类型影响,可随意更改数组类型
printf("结构体重新分配空间大小后起始地址:%d,\t大小为:%d\n",(int)sa,sizeof(*sa));
printf("重新分配大小后数组起始地址为:%d\t",(int)sa->_Array);
 
return 0;
}

运行结果:

  由上图可见,利用malloc函数重新分配内存空间后,起始地址变了,但是结构体大小没有改变,还为4个字节;虽然在上面我们为整个结构体分配了44个字节的空间,但是因为定义中没有指明数组成员的大小,因此编译器值分配了4个字节给结构体,这里重新分配后,可以理解为已经超出了结构体的范围,因此超出的部分的字节数没有被计算在结构体内,故还为4字节!


更改代码,尝试使用刚分配的柔性数组:

#include <stdio.h>
#include <malloc.h>
//定义一个结构体,内含两个成员变量,一个是int类型的,另一个是int类型数组
//但没有指定数组的大小
typedef struct _SoftArray
{
int len;
int _Array[];
} SoftArray;
 
int main()
{
    int i=0;
SoftArray* sa=NULL;
sa=malloc(sizeof(*sa)+sizeof(*(sa->_Array)*10));
for(i=0;i<10;i++)
    {
        sa->_Array[i]=i;
    }
    printf("数组值的遍历:\t");
    for(i=0;i<10;i++)
    {
        printf("%d\t",sa->_Array[i]);
    }
return 0;
}

 运行结果:


完善代码,实现斐波拉次数列:

#include <stdio.h>
#include <malloc.h>
 
//用结构体实现软数组
typedef struct _SoftArray
{
    //既然必不可少,那么就用来指示当前软数组的长度
    int len;
    int _Array[];
} SoftArray;
 
//创建软数组,参数size:软数组的大小
SoftArray* CreateArray(int size)
{
    SoftArray* sa;
    sa=malloc(sizeof(*sa)+sizeof(*(sa->_Array))*size);
    //设置数组的长度
    sa->len=size;
    return sa;
}
//生成数列
int func(SoftArray* sa)
{
    int i=0;
    if(NULL!=sa)
    {
        if(1==sa->len)
        {
            sa->_Array[0]=1;
        }
        if(2==sa->len)
        {
            sa->_Array[0]=1;
            sa->_Array[1]=1;
        }
        if(sa->len>2)
        {
            sa->_Array[0]=1;
            sa->_Array[1]=1;
            for(i=2;i<sa->len;i++)
            {
                sa->_Array[i]=sa->_Array[i-1]+sa->_Array[i-2];
            }
        }
        return 1;
    }
    else
    {
        return 0;
    }
}
 
//释放数组空间
void DeleteArray(void* sa)
{
    free(sa);
}
 
int main()
{
    SoftArray* p=NULL;
    int i=0;
    p=CreateArray(10);
    if(1==func(p))
    {
        printf("斐波拉次数列(前十项):\n");
        for(i=0;i<p->len;i++)
        {
            printf("%d\t",p->_Array[i]);
        }
    }
    else
    {
        printf("程序可能出错了!\n");
    }
    DeleteArray(p);
    return 0;
}

运行结果:


2、union

union关键字定义的共用体变量在内存中只分配最大域成员的空间,所有成员共享这个空间,因此union的使用受到系统大小端的影响!所谓的系统大小端,指的是数据在内存中的存放形式,分为大端模式和小端模式;

 

大端模式:低字节的内容存放在内存的高地址内存单元中

小端模式:低字节的内容存放在内存的低字节中;

如下图:

 

可以通过如下的程序测试系统属于哪种模式:

#include <stdio.h>
int main()
{
    union cc
    {
        int i;
        char c;
    };
 
    union cc C;
    C.i=1;
    int i=(int)C.c;
    if(1==i)
    {
        printf("小端模式!");
    }
    else
    {
        printf("大端模式!");
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值