1. 结构体的声明
1.1 结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
(数组是一组相同类型元素的集合)
生活中的对象大多是复杂的,有多种属性,结构体就是用来描述复杂对象的。
1.2 结构的声明
结构体的语法形式:
struct tag//分别是结构体关键字、结构体标签(名字可变)
{
member-list;//成员列表,可以放多个成员,成员之间可以是不同类型
}variable-list;//变量列表
例如声明一个学生的结构体类型:
一种结构体变量的创建(定义)方式
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
float score;//学分
};//分号不能丢
int a = 0;//拿整型这种类型创建了a变量
int b = 0;//拿整型这种类型创建了b变量
//即可以拿类型创建变量
//则现在有了一个结构体类型——学生类型,拿这个类型怎么创建变量呢?
//struct Stu就是结构体类型,如创建一个s变量:
struct Stu s;//定义了一个结构体变量s,是局部变量
//即这里用了struct Stu这种结构体类型创建了(定义)一个结构体变量s
另一种结构体变量的创建(定义)方式
struct Stu
{
char name[20];//名字
int age;//年龄
char sex[5];//性别
char id[20];//学号
float score;//学分
}s1, s2;
//s1和s2是拿这个结构体类型创建的两个结构体变量
//还可以继续创建很多个变量,所以是变量列表。
这里的s1、s2和上面s的创建的效果一模一样;
但是差异是:
这里的s1、s2是全局变量,上面的s是局部变量。
全局变量不初始化默认是0。
1.3 结构成员的类型
结构的成员可以是标量(像int a)、数组、指针,甚至是其他结构体。
1.4 结构体变量的定义和初始化
有了结构体类型(如struct Stu),那如何定义结构体变量呢?
结构体成员的初始化:
#include <stdio.h>
struct Stu
{
char name[20];
int age;
char sex[5];
char id[20];
float score;
};
int main()
{
struct Stu s = { "zhangsan",20,"男","19",95.5f };
printf("%s %d %s %s %.1f\n", s.name, s.age, s.sex, s.id, s.score);
return 0;
}//zhangsan 20 男 19 95.5
结构体里再包含一个结构体的定义和初始化、访问:
#include <stdio.h>
struct S
{
int a;
char c;
double d;
};
struct Stu
{
struct S ss;//这里结构体里包含一个结构体
char name[20];
int age;
char sex[5];
char id[20];
float score;
};
int main()
{
struct Stu s = { {100,'w',3.14},"zhangsan",20,"男","19",95.5f};
//这里把{100,'w',3.14}这些数据给了ss,即给了ss初始化
printf("%d %c %.2lf %s %d %s %s %.1f\n",s.ss.a,s.ss.c,s.ss.d,s.name, s.age, s.sex, s.id, s.score);
return 0;
}//100 w 3.14 zhangsan 20 男 19 95.5
结构体里包含一个结构体访问:结构体里包含一个结构体成员,结构体成员里又找自己的结构成员,用了两层.(两层点)。
注意:
95.5f表明是float类型的值,不写f则编译器会默认这是double类型的值。
打印的时候,f%表明打印的是float类型的数据;lf%表明是double类型的
struct Point
{
int x;
int y;
}p1; //声明类型的同时定义变量p1
struct Point p2; //定义结构体变量p2
//初始化:定义变量的同时赋初值
2. 结构体成员的访问
1、结构体变量访问成员
结构变量的成员是通过点操作符(.)访问的。点操作符接受两个操作数。
例如:
可以看到 s 有成员 name 和 age ;
那如何访问s的成员?
#include <stdio.h>
struct Stu
{
char name[20];
int age;
};
int main()
{
struct Stu s = { "lisi",18 };
printf("%s %d\n", s.name, s.age);
return 0;
}//lisi 18
2、结构体指针访问指向变量的成员
有时候我们得到的不是一个结构体变量,而是指向一个结构体的指针,则用—>操作符。
那该如何访问成员。
如下:
#include <stdio.h>
struct Stu
{
char name[20];
int age;
};
void Print(struct Stu* ps)
{
printf("name=%s age=%d\n", (*ps).name, (*ps).age);
//使用结构体指针访问指向对象的成员
printf("name=%s age=%d\n", ps->name, ps->age);
}
int main()
{
struct Stu s = { "zhangsan",20 };
Print(&s);//结构体地址传参
return 0;
}//打印结果:
//name=zhangsan age=20
//name=zhangsan age = 20
3. 结构体传参
写Print1函数传参:
#include <stdio.h>
struct S
{
int arr[1000];
float f;
char ch[100];
};
void Print1(struct S tmp)
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", tmp.arr[i]);
}
printf("\n");
printf("%.1f\n", tmp.f);
printf("%s\n", tmp.ch);
}
int main()
{
struct S s = { {1,2,3,4,5,6,7,8,9,10},5.5f,"hello world"};
Print1(s);
return 0;
}//输出:
//1 2 3 4 5 6 7 8 9 10
//5.5
//hello world
此代码是值传递,函数调用时是传值调用
当s传给tmp时,传值调用时会再创建一个临时空间,实参传给形参时形参其实是实参的一份临时拷贝,即肯定会拷贝一份空间,本来s(结构体)的空间就很大了,再临时创建一个一样的,大大造成了空间的浪费,数据拷贝也需要时间,系统开销很大。
则重新设计一个函数Print2:
#include <stdio.h>
struct S
{
int arr[1000];
float f;
char ch[100];
};
void Print2(struct S* ps)
{
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", ps->arr[i]);//ps指向对象s中的成员arr数组
}
printf("\n");
printf("%.1f\n", ps->f);//ps指向对象s的成员f;结构体指针->f,因为ps是结构体指针,它里面存的是s的地址所以它指向了找到s的成员f
printf("%s\n", ps->ch);
}
int main()
{
struct S s = { {1,2,3,4,5,6,7,8,9,10},5.5f,"hello world" };
Print2(&s);
return 0;
}//输出:
//1 2 3 4 5 6 7 8 9 10
//5.5
//hello world
上面的 print1 和 print2 函数哪个好些?
答案是:首选print2函数。
原因:函数传参的时候,参数是需要压栈的。
如果传递一个结构体对象的时候,结构体过大,参数压栈的的系统开销比较大,所以会导致性能的下降。
结论:结构体传参的时候,要传结构体的地址。
函数调用传参的过程:(怎么传参的呢?)
任何函数的调用都要在内存中开辟空间。内存中分为栈区、堆区和静态区这3个区域。任何函数的每一次函数的调用都要在内存中的栈区申请空间。