前言
基于自我理解的角度来讲结构体;
一、结构体的基础知识
结构是一些值的集合,这些值称为成员变量;结构的每个成员可以是不同类型的变量;
注:数组是相同元素的集合;而结构体中可以放不同类型的数据,例如想要做一个表达一个复杂对象的数据;比如说是描述一个人,就得说这个人的姓名、年龄,性别、身高、电话等,想要准确存储这些信息就得使用不同的变量,把这些不同类型的变量整合在另个一变量中,便就是结构体变量;
二、结构体的初始化
当我们想要初始化一个整型变量,int a = 4; --> 需要变量的类型+变量名+ 初始化的值;
所以首先我们要声明其类型:
1、结构体类型声明:
下图是结构体类型声明的模板:
其中 struct tag 是结构体的类型,stract 不可以更改,tag 可以更改;number-list 就是成员列表; variable-list 是变量列表,在声明的过程中可要可不要; 列表就是不止一个的意思;
方式一:声明时省略variable-list(通常是这样的方式) :
这里就仅仅声明了一个结构体类型
方式二:声明时不省略variable-list :
利用结构体类型创建了变量p1、p2 , 此处的结构体类型用来描述一个人的信息,那么p1、p2 也可以理解为被描述的两个人;
其中p1、p2 为全局的结构体变量(一般不推荐用全局变量),这也就是不常用此方法的原因。
注:结构体类型的声明通常是在main函数外进行的;当然也可以在主函数内部进行声明,只不过此时的结构体类型就为局部变量,只能在main 函数的内部使用;此处并不是全局变量与局部变量,请注意。
重点分析:在对结构体类型进行声明的时候,并不会向内存申请空间;当只有创建一个变量时才会向内存申请空间;可以将结构体类型当作建房子的图纸,图纸并会占据你要建房地基的空间,只有当你真正意义上开始建房子(创建变量)的时候,才会占据空间;同理。
2、结构体成员的类型
结构体成员可以是标量、数组、指针,甚至是其他结构体。
代码如下:
#include<stdio.h>
struct Peo
{
char name[20];
int age;
char sex[10];
int high;
char tel[10];
struct home s;
}p5;
struct home
{
char name[20];
int age;
char sex[10];
};
struct Peo p2,p3,p4; //结构体变量的创建
int main()
{
struct Peo p1; //结构体变量的创建
return 0;
}
其中p1为局部变量,p2、p3、p4为全局变量,同时p5 也是全局变量;结构体成员的类型可以是标量、数组、指针、结构体;
3、结构体变量的初始化:
在结构体变量创建时再给它一个初始值就是结构体变量的初始化。
注:在创建结构体变量赋值时,至少为一个结构体成员赋值;故而分为完全初始化与不完全初始化;
代码如下:
#include<stdio.h>
struct Peo
{
char name[20];
int age;
char sex[10];
int high;
char tel[20];
}p1,p2;
int main()
{
struct Peo p1 = { "zhangsan",19,"保密",180,"123456789" };
return 0;
}
而若是对结构体中成员是结构体的这种情况是该如何初始化呢?
代码如下:
#include<stdio.h>
struct home
{
char name[20];
int age;
char sex[10];
};
struct Peo
{
char name[20];
int age;
char sex[10];
int high;
char tel[10];
struct home s;
};
int main()
{
struct Peo p1 = { "zhangsan",19,"保密",185,"1234567",{"lisi",11,"女"} };
return 0;
}
如上,按照所创建变量的类型,按照类型声明时的顺序进行初始化就行,成员之间的数据用 ' , ' 隔开,如果遇到结构体的成员中有结构体,就将另一结构体的数据放入 { } 中,结构体的成员中的结构体的成员之间的数据也是用 ' , ' 隔开。
同理,在数组中若初始化时的数据个数小于数组元素的个数,那么就存在未初始化的数组元素,这就是未完全初始化;请看一下代码:
#include<stdio.h>
struct home
{
char name[20];
int age;
char sex[10];
};
struct Peo
{
char name[20];
int age;
char sex[10];
int high;
char tel[10];
struct home s;
};
int main()
{
struct Peo p1 = { "zhangsan",19,"保密",185,"1234567",{"lisi"} };
return 0;
}
代码调试结果如下:
分析:可以发现,我们此处未对 struct home s 中后两个成员进行赋值;在数组中,初始化时未被赋值的元素会默认为 ’\0‘ (因 '\0' 的ASCII码值为0,故而可以将 '\0' 就当作0);对结构体变量进行初始化本质上是对结构体中的成员进行初始化,而其成员就是我们平时接触的内置类型、数组(无非其成员可能会是结构体变量,本质上就是嵌套结构体,一层一层进行赋值即可),若是结构体变量未初始化相对应的内置类型表现为随机值,而数组就是 ’\0‘;
三、结构体成员访问
会用到两个操作符 . 点操作符 -> 箭头操作符
二者访问结构体变量成员的运用:
. 点操作符: 结构体变量 . 成员名
-> 箭头操作符 : 结构体变量指针 -> 成员名
注:结构体变量 = * 结构体变量指针,故而存在 ( * 结构体变量指针 ). 成员名
以下代码将展示这三种结构体成员的访问:
代码如下:
#include<stdio.h>
struct home
{
char name[20];
int age;
char sex[10];
};
struct Peo
{
char name[20];
int age;
char sex[10];
int high;
char tel[10];
struct home s;
};
void print(struct Peo* s)
{
printf("%s %d %s %d %s %s %d %s\n", (*s).name, (*s).age, (*s).sex, (*s).high, (*s).tel, (*s).name, (*s).age, (*s).sex);
printf("%s %d %s %d %s %s %d %s\n", s->name, s->age, s->sex, s->high, s->tel, s->name, s->age, s->sex);
}
int main()
{
struct Peo p = { "zhangsan",19,"保密",185,"1234567",{"lisi",11,"保密"}};
printf("%s %d %s %d %s %s %d %s\n", p.name, p.age, p.sex, p.high, p.tel, p.name, p.age, p.sex);
print(&p);
return 0;
}
代码运行结果如下:
四、结构体传参
在学习函数的时候,我们知道有传值传参和传址传参;
传值传参,形参是对实参的一份临时拷贝;传址传参就是将其变量的地址传过去,即对存在内存中的数据进行操作;结构体变量也存在传值传参和传址传参,且道理与函数传参相同;
我们来看一下这两种传参方式的例子:
代码如下:
#include<stdio.h>
struct Peo
{
char name[10];
};
void print1(struct Peo ps)
{
printf("%s\n", ps.name);
}
void print2(struct Peo* s)
{
printf("%s\n", s->name);
}
int main()
{
struct Peo p = { "zhangsan" };
print1(p);//传值传参
print2(&p); //传址传参
return 0;
}
代码运行结果如下::
思考:若是讨论传参用来打印的话,传址传参比传值传参更好,因为如果结构体对象很大即其所占的空间很大,那么传值传参就会浪费空间来进行拷贝,并且在拷贝的时候也会废时,这就会导致其性能降低;
当实参进行传值传参时,形参是对实参的一份临时拷贝,此时形参会在栈帧(即为本次 printf() 所开辟于函数栈帧中找的空间) 中存放 ;若结构体在内存空间中所占的空间很大,栈帧就会因形参的拷贝而增大,于是乎就浪费了很多空间;
专业描述原因:
1、函数传参时,参数需要压栈,会有时间和空间上的开销;
2、如果传递的一个结构体变量,其在空间中所占的内存很大时,参数压栈的系统开销比较大,所以会导致性能的下降;
所以:结构体传参时,最好传其地址;
总结
1、结构是一些值的集合,这些值称为成员变量;结构的每个成员可以是不同类型的变量;
2、结构体类型的声明通常是在main函数外进行的;当然也可以在主函数内部进行声明,只不过此时的结构体类型就为局部变量,只能在main 函数的内部使用;此处并不是全局变量与局部变量,请注意。
3、在结构体变量创建时再给它一个初始值就是结构体变量的初始化;在创建结构体变量赋值时,至少为一个结构体成员赋值;故而分为完全初始化与不完全初始化;
4、. 点操作符: 结构体变量 . 成员名
-> 箭头操作符 : 结构体变量指针 -> 成员名
注:结构体变量 = * 结构体变量指针,故而存在 ( * 结构体变量指针 ). 成员名
5、函数传参时,参数需要压栈,会有时间和空间上的开销;如果传递的一个结构体变量,其在空间中所占的内存很大时,参数压栈的系统开销比较大,所以会导致性能的下降;
所以:结构体传参时,最好传其地址;