1.概述
在C语言中,结构体(struct)指的是一种数据结构,是C语言中聚合数据类型(aggregate data type)的一类。可以声明结构体类型的变量、指针或数组等,用以实现较复杂的数据结构。结构体同时也是一些元素的集合,这些元素称为结构体的成员(member),且这些成员可以为不同的类型,成员一般用结构体类型对象访问。
2.结构体的定义和声明
结构体的定义如下所示,struct为结构体关键字,tag为结构体的类型名,member-list为结构体成员列表,其必须列出其所有成员;variable-list为此结构体声明的变量。
struct tag { member-list } variable-list ;
在一般情况下,tag、member-list、variable-list这3部分至少要出现2个。以下为示例:
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其类型名,这种用法不利于后续定义该类型的对象
struct
{
int a;
char b;
double c;
} s1;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE另外声明了变量t1、t2、t3
//在C编译器里一般独立声明变量必须使用全称struct SIMPLE,C++中通常省略struct和class关键字
struct SIMPLE t1, t2[20],*t3;
//也可以用typedef创建新类型,此时的结构体标记可要可不要
//如果结构体成员包括指向自身的指针时,必须要结构体标记
//在结构体里面还不能使用typedef定义的类型别名
typedef struct
{
int a;
char b;
double c;
Simple2* next; //这是不可以的,因为在这里标示符Simple2还是不可见的
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20],*u3;
在上面的声明中,第一个和第二声明被编译器当作两个完全不同的类型,即使他们的成员列表是一样的,如果令t3=&s1,则是非法的。
结构体的成员可以包含其他结构体,也可以包含指向自己结构体类型的指针,而通常这种指针的应用是为了实现一些更高级的数据结构如链表和树等。
//此结构体的声明包含了其他的结构体
struct COMPLEX
{
char string[100];
struct SIMPLE a;
};
//此结构体的声明包含了指向自己类型的指针
struct NODE
{
char string[100];
struct NODE* next_node;
};
如果两个结构体互相包含,则需要对其中一个结构体进行不完整声明,如下所示:
struct B;//对结构体B进行不完整声明
//结构体A中包含指向结构体B的指针
struct A
{
struct B*partner;
//other members;
};
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
struct A*partner;
//other members;
};
2.结构体成员访问
结构体成员依据结构体变量类型的不同,一般有2种访问方式,一种为直接访问,一种为间接访问。直接访问应用于普通的结构体变量,间接访问应用于指向结构体变量的指针。直接访问使用结构体变量名.成员名,间接访问使用(*结构体指针名).成员名或者使用结构体指针名->成员名。相同的成员名称依靠不同的变量前缀区分。
struct SIMPLE
{
int a;
char b;
};
//声明结构体变量s1和指向结构体变量的指针s2
struct SIMPLE s1,*s2;
//给变量s1和s2的成员赋值,注意s1.a和s2->a并不是同一成员
s1.a=5;
s1.b=6
s2->a=3;
s2->b=4;
3.结构体变量存储
在内存中,编译器按照成员列表顺分别为每个结构体变量成员分配内存,当存储过程中需要满足边界对齐的要求时,编译器会在成员之间留下额外的内存空间。如果想确认结构体占多少存储空间,则使用关键字sizeof,如果想得知结构体的某个特定成员在结构体的位置,则使用offsetof宏(定义于stddef.h)。
struct SIMPLE{int a;char b;};
//获得SIMPLE类型结构体所占内存大小
int size_simple=sizeof(struct SIMPLE);
//获得成员b相对于SIMPLE储存地址的偏移量
int offset_b=offsetof(struct SIMPLE, b);
4.链表
链表的基本内容在百度百科中写得很不错,链接如下:http://baike.baidu.com/view/549479.htm
以下为我个人的一点总结:
(1)相对于线性表的顺序结构,链表比较方便插入和删除操作。(例如数组的插入和删除操作,需要进行复制和赋值操作,还需要分配临时内存)。
(2)链表分为单链表,双向链表和循环链表,他们各有优势,有自己适合的场合,但用法的本质是一样的,操纵指针的指向。
5.几个问题------值得思考一下
1)在结构体中若存在指向自身类型的指针,typdef怎么使用,后面还要跟结构体类型名吗?::::::::::>必须,typedef定义的标记不能用在结构体定义体里。
2) 结构体里如果再定义另一个结构体类型的成员,初始化怎么初始化?::::::::::::>可以,非动态创建的结构体对象初始化时可以用类似于数组初始化的方式初始化结构体成员:
#include <malloc.h>
#include <string>
#include <iostream>
using namespace std;
//日期
typedef struct ST_Data
{
int year;
int month;
int day;
}data;
//员工
typedef struct
{
string name;
int age;
data birthday; //data类型的对象
}stuff;
int main()
{
data BirthDay={1989,8,10};
stuff s1={"Yin",23,BirthDay}; //甚至还可以直接写成:stuff s1={"Yin",23,1989,8,10}:
s1.name="MingXiao"; //重新给成员赋值也是可以的
cout<<s1.name<<s1.age<<s1.birthday.year<<s1.birthday.month<<s1.birthday.day<<endl;
system("pause");
return 0;
}
注意:动态创建链表时malloc函数和new运算符是有区别的:见另一篇博文:http://blog.csdn.net/miss_acha/article/details/7279915
当结构体成员有非内置类型时,只能使用new运算符,而不能使用malloc函数!!