1.定义和功能
用于存储包含不同类型数据,的数据类型
2.声明(模板作用)
struct person{
char lasename[20];
char firstname[20];
int age;
int tel[11];
...
};
a.以上声明只是告诉编译器有这么一个模板,并没有在内存中实际分配空间
b.结构中的项可以是c的任意一种数据类型(int,double,array,struct...)
c.声明时注意变量的作用域,若在函数体内声明则作用域限制在函数内部,反之则相反
3.定义结构变量(内存中实际分配空间)
struct person suhu; //第一种(此处表示结构在以前已经声明过了
struct person{.......} suhu; //第二种(表示在声明结构的时候同时定义变量)
//还有一种不常用的无名结构形式
这样会在内存里面分配一个struct里面成员数据大小的总宽度,或者比其更大,因为不同的编译器在在处理定义命令的时候可能会产生“内存间隙”
4.初始化结构
类似于数组和字符串两种初始化方式
struct person suhu={
"su",
"hu",
"24",
"11011011011"
}; //第一种
struct person suhu={
.lastname="su",
.firstname="hu",
.age=24,
.tel=11011011011
}; //第二种结构初始化器
a.若结构在函数外定义则为静态变量,初始化的时候必须是都是常量表达式
5.利用结构对象访问结构成员
suhu.lastname
suhu.firstname
//成员的类型即是定义时候成员的类型
6.结构数组(即用数组存储结构, 数组里面的每一项都是一个结构)
struct person suhu[2]
//定义一个两个结构的数组,数组成的每一项的类型都是struct person
//"suhu"依然表示数组的首地址,但是结构名suhu[0],suhu[1]不表示地址
a.结构数组访问对象成员为 suhu[0].lastname
若出现suhu[0].lastname[2]表示的是lastname里面的第3个字符
7.嵌套结构
即一个结构里面的成员可以是另外一个结构
下面给出声明的方式以及赋值方法
struct name {
char firstname[20];
char lastname[20];
}; //声明第一个结构,作为另外一个结构的成员
struct person{
char name suhu; //使用struct name 类型定义变量suhu作为成员
int age;
int tel;
}
声明嵌套结构struct person类型的变量跟初始化没有嵌套的结构的方法大同小异,只是要在声明作为结构的成员时额外加一个花括号
struct person sha={
{"su","hu"}, //这里实际上就等于suhu.lastname="su",suhu.firstname="hu"
24,
11011011011
};
8.指向结构的指针
重点在与将声明了的结构看成是一个自己定义的数据类型,如:
struct person{.....};
则将struct person 看成是一个数据类型,在声明指针的时候类似于声明c元数据类型。
struct person suhu[2];
struct person * p; //声明p是指向struct person这种类型的指针
p=&suhu[0]; // 因为结构不像数组,结构变量名不能表示地址,p此时指向suhu[0]这个结构
p++ //表示p指向suhu[1]
p+1//表示p指向suhu[1]
尽管每个指针变量存储的都是内存中的地址,但是指针也是有类型的例如
int arr[10];
int p=arr;
这个语句会告诉编译器,p这个指针指向的是一个int类型的元素,即指向arr[0],所以当p+1或者p++时,结果会自动增加指针指向类型的大小的字节数。结构数组在这里也是一样的,在64位的系统里面做个实验如下
//input:
struct person{
char name[20];
int age;
};
struct person suhu[2];
struct person *p=&suhu[0];
printf("sizeof char =%lu\n",sizeof(char));//打印int所占字节数
printf("sizeof int =%lu\n",sizeof(int));//打印char所占字节数
printf("p=%p\n",p);
printf("p+1=%p\n",p+1);
//output:
sizeof char =1
sizeof int =4
000000000022FE10
000000000022FE28
看输出的两个指针值,p和p+1之间相差18(16进制)个字节,即24个字节(10进制),刚好是20个char的长度加上1个int长度(根据不同的编译器个操作系统,两个指针的差值可能会大于24个字节,这是因为可能会存在间隙)。
所以当一个指针p指向结构数组中的一个对象时,p+1表示它会指向数组中的下一个结构。指针在数组里面的用法都是这样的(注意数组名,数组名可以看成一个常量指针,但实际上并没有一个地址来存储数组名这个地址,这个是在编译器层面上实现的,原来这个问题困扰了我三四天!!!)。
9.用结构指针访问成员
第一种方法:
struct person{
char name[20];
int age;
};
struct person suhu[2];
struct person *p;
如果p==&suhu[0],
则p->name 表示suhu[0].name,
p->age 表示suhu[0].age
如果p==&suhu[1],
则p->name 表示suhu[1].name
同理p->age表示suhu[1].age
p表示的是指针,p->表示的是访问p此时指向的对象里面的某个成员
第二种方法:
struct person{
char name[20];
int age;
};
struct person suhu[2];
struct person *p=&suhu[0]; //此时p指向suhu数组里面第一个结构
(*suhu).name //表示suhu[0].name
(*suhu).age //表示suhu[1].age //实际上就是先对指针解引得到结构,再用结构的.运算符取结构中成员数据
10.结构信息在函数中的传递
结构信息能在函数中传递,前提是结构一定要定义为静态存储,即定义在函数之外,且每个函数的原型声明和函数定义都必须要在结构声明之后。
结构信息在函数中的传递有三种方式,分别是只传递结构成员项、传递结构、传递结构指针
a、传递结构成员项
int func(int,int); //声明函数原型
int func(int a,int b); //定义函数
//在这种状态下被调函数func其实不管要处理的是什么信息,他只要求接受两个int值,返回一个int值。这样的方式跟以前定义常规函数没区别
b、传递结构
struct name getinfo(struct name); //原型声明
sturct name getinfo(strict name temp); //函数定义头
//在这种形式中,调用函数getinfo()会需要传入一个结构,也会返回一个结构,这个函数执行时会创建一个strict name类型的temp变量,这个变量完全是实参的副本,所以在当结构较大时,优先考虑传递结构指针,节省内存开销
c、传递结构指针
struct name * getinfo(struct name *); //原型声明
struct name * getinfo(struct name * p); //函数定义头
//在这种形式中,背调函数操作的是主调函数中实际的结构而不是被调函数自己创建的本地变量,所以如果主调函数中的结构若不想修改,则应在参数声明时添加const,如:
struct name *getinfo(const struct name *p) //这样的话,在被调函数中就不可能通过p改变主调函数中结构的值