结构体【重要】

继续进行C语言学习之旅,现在已经快进入了C的核心了,所以需更加细心地学习,言归正传,开始学习:

什么是结构体呢?

 

定义结构体类型:
 
注意:结构体最后的分号不能省略
这时编译就出错了:
 
结构体变量:
当我们定义好了一个结构体类型之后,就可以使用它,类似于基本类型一样,但是:会有一些差别
①使用方式一【用得较少】:
 
注意:struct关键字不能省略,代表该变量是一种结构体类型:
 
这时编译就出错了:
②使用方式二【用得较少】:
 我们可以将结构体声明在函数体内,声明之时就给出变量:
③使用方式三【通常用这种方式】:
使用方式一中可以看出,使用起来不是那么方便,有没有解决办法呢?答案当然有,也就是第三种方式,用typedef为现有类型创建一个新的名字,或称为类型别名
 
 
另外,我们可以简化这种方式,在定义结构体类型之后,就给出别名,还不用单独多出一行来处理【在编程中能用少的代码来实现同样的功能,是我一直追求的!!】
 
编译也是成功的:
 
提示:对于用tydef给一个类型定义别名,一般命名是以_t结尾:
 
 
结构体中可以嵌套结构体:
#include <stdio.h>

struct point
{
    int x;
    int y;
};

typedef struct point point_t;

typedef struct circle
{
    point_t center;//嵌套结构体
    int radius;
} circle_t;

int main(void)
{    
    return 0;
}
 
结构体变量初始化:
①第一种初始化:
#include <stdio.h>

struct point
{
    int x;
    int y;
};

typedef struct point point_t;

typedef struct circle
{
    point_t center;
    int radius;
} circle_t;

int main(void)
{

    circle_t c1 = {{1, 2}, 3};

    printf("(%d,%d) %d\n", c1.center.x, c1.center.y, c1.radius);

    return 0;
}

说明:访问结构体里面的元素,采用“.”:

②第二种初始化:
 
#include <stdio.h>

struct point
{
    int x;
    int y;
};

typedef struct point point_t;

typedef struct circle
{
    point_t center;
    int radius;
} circle_t;

int main(void)
{

    circle_t c1 = {1, 2, 3};

    printf("(%d,%d) %d\n", c1.center.x, c1.center.y, c1.radius);

    return 0;
}

③第三种初始化:
 
#include <stdio.h>

struct point
{
    int x;
    int y;
};

typedef struct point point_t;

typedef struct circle
{
    point_t center;
    int radius;
} circle_t;

int main(void)
{

    point_t p1 = {1, 2};
    circle_t c1 = {p1, 3};

    printf("(%d,%d) %d\n", c1.center.x, c1.center.y, c1.radius);

    return 0;
}

 

我们知道数组是不能整体赋值的,这个在http://www.cnblogs.com/webor2006/p/3452229.html有说明,下面再回忆一下:
 
 
要真的想把一个数组的值赋给另外一个数组,只能采用遍历的方式:
 
 
 而对于字符数组,也是无法直接整体赋值的:
编译,这时会出错:
如果要从一个字符数组的内容拷贝到另外一个数组,则需要利用库函数来实现:
编译,运行:
但是结构体中的数组是可以相互赋值的:
#include <stdio.h>

typedef struct test
{
    int a[3];
    int b;
    char c[10];
} test_t;


int main(void)
{

    test_t t = {{4, 5, 6}, 100, "hello"};
    test_t t2;
    t2 = t;//结构体整体赋值

    printf("%d %d %d %d %s\n", t2.a[0], t2.a[1], t2.a[2], t2.b, t2.c);

    return 0;
}

 
结构体数组:
由于结构体也是一种数据类型,所以很容易地可以想到它可以有数组:
 
结构体内存对齐【这个概念重要】:
 看如下图:
 
 
 
从结果来看,并非按我们推算的来计算结构体的大小,这是因为结构体里面的数据是按对齐的方式来存放的,把数据地址对齐到2、4、8(这里称为对齐模数,不同数据类型的对齐模数是不一样的)的整个倍,这就是所谓的“结构体内存对齐”
可能有些抽象,至于怎么对齐,下面会详细介绍,这阶段先初步解释上程序中16的字节数是怎么得来的
 
 
 下面通过一个图来说明意义:
先讨论对齐的方式存放:
 
而如果不以对齐的方式存放呢?
可见,这种方式的效率远不如对齐的,所以对齐的重要意义为:
 
这是关键点,先说下规则:
①:第一个数据成员对齐至偏移值为0的地方。
②:接下来的数据成员对齐至对齐模数的整数倍,每个数据成员都有自己的对齐模数。
③:对齐模数取数据成员的大小与param pack(n)(对齐值,gcc默认为4,微软的vc默认是8)的较小值。
 
④:整个结构体的大小对齐至最大对齐模数的整数倍。
 
 
下面通过设置param pack(n)来看下,结构体的大小是否发生了变化:
运行结果:
这时,我们来算一下,这个值是怎么得来的:
 
如果我们将对齐值改为1呢?
运行结果:
通过上面的分析就可以很清楚地推出为啥输出是10个字节的大小了
如果将对齐值改为8呢?
运行结果:
 
这是为什么呢?原因是因为gcc中的对齐值只能是1、2、4,不支持8,只有在vc上才会支持
 
结构体位字段:
 
 
 
 
 
 
联合体:
“联合”与“结构”有一些相似之处。但两者有本质上的不同。在结构中各成员有各自的内存空间,一个结构变量的总长度是各成员长度之和。而在“联合”中,各成员共享一段内存空间,一个联合变量的长度等于各成员中最长的长度。【来自于百度百科】
 
 
 
下面用一个实例来体会一下联合体:验证一下机器是小端字节序还是大端字节序
先介绍一下什么是“小端字节序”和“大端字节序
接着用联合体来编程验证:
 
 
好了,结构体这一章是非常重要的,涉及到的细节也是挺多的,需好好消化。

posted on 2013-12-06 23:02  cexo  阅读(522)  评论(0编辑  收藏  举报

导航