目录
结构体
结构体类型的声明
结构体声明的语法结构
声明结构体
创建变量示例
结构体成员的类型
结构体变量定义及初始化
结构体成员访问
结构体传参
结构体
当然本次结构体仍不会讲的特别深入,在后面的自定义类型进阶会更加深入的讲到这部分内容。
结构(体)是一些值的集合,这些值被称为成员变量。结构的每个成员变量可以具有不同类型。
我们所学过的类型如:char
,int
,float
,double
等,都只能描述单一变量。但是结构体,顾名思义,是多个变量的集合,其中包含多个单一变量。所以C语言就发明了结构体用于用来描述复杂对象,如:书,人等具有多个特征的变量。
结构体类型的声明
结构体声明的语法结构
struct tag//结构体名称
{
member-list;//成员列表
}variable-list;//变量列表(全局变量)复制
在结构体声明的{}
后面创建的变量是全局变量,如果我们只想创建局部变量,就在mian
函数里创建。
几个名词需要我们额外注意区分一下。
结构体(类型)名
struct tag
复制
这个叫结构体类型名,或者叫结构体类型,使用上相当于
int
,char
等类型,故叫类型名。
结构体成员变量
struct S {
char a[20];
char b[5];
float c;
};复制
因为
{}
内部定义的叫成员变量,如例子中a
,b
,c
这样的。定义结构体成员变量也可以顺带初始化。
结构体变量名
struct S s = { "abc", "man", 3.1415 };
复制
在声明好结构体后,用结构体去定义的变量,叫结构体变量,不能和成员变量混淆了。上面的
s
就是用S
创建的变量。定义结构体变量和声明结构体,两种说法是两件完全不同的事情。
声明结构体和创建变量示例
结构体用于描述复杂对象的多个属性,必然具有多个成员变量。结构体类型创建如下列示例:
struct book//书
{
char name[20];//书名
char author[15];//作者
float price;//价格
}b1,b2;//全局变量
struct point//坐标
{
int x;
int y;
};
int main()
{
struct book b;//局部变量
return 0;
}复制
当然如果我们把结构体类型声明在main
函数内部也不是不可以,但是如此就没有了全局变量的说法。在公司里更多是大家一起使用。
int main()
{
struct book
{
char name[20];
char author[15];
float price;
}b1, b2;
struct book b;
return 0;
}复制
还有一种方法是使用typedef
对类型重定义,如:
typedef struct human
{
char name[20];
int age;
char sex[10];
char id[20];
}hu;
int main()
{
//1.
struct human man;
//2.
hu man;
return 0;
}复制
这样该结构体,就有了两种创建变量的方式struct human man
和hu man
都行。可以类比tpyedef unsigned int size_t
,如下图:

注意只是将struct human
重新取个名字叫hu
本质上还是类型。
本质上还是类型,是不占用空间的,只有创建变量时才会开辟空间。当然这样就不能在声明结构体的后面直接创建全局变量了。
结构体成员的类型
结构成员可以是标量,数组,指针,也可以是其他结构体。往后会进一步讲解。
结构体变量定义及初始化
可以直接在结构体声明后面定义,甚至在后面可以初始化。
struct book
{
char name[20];
char author[15];
float price;
}b1, b2;
struct book b3 = { 0 };
struct point {
int x;
int y;
};
struct point p1 = { x, y };复制
也可以在main
函数中定义及初始化。
typedef struct human{
char name[20];
int age;
char sex[10];
char id[20];
}hu;
int main() {
struct human man1 = { "张三",20,"女装大佬","20203100" };
point p1 = { 1,2 };
return 0;
}复制
甚至说嵌套定义和初始化。
struct S {
int a;
char c;
double d;
};
struct T {
struct S s;
char name[20];
int num;
};
int main() {
struct T t = { {10, 'x', 1.00}, "yourfriendyo", 21 };
return 0;
}复制
在结构体
T
中使用结构体S
定义了T
的成员变量s
。在初始化的时候同样需要注意的是,s
也需要使用{}
进行初始化。
结构体成员访问
.
操作符
结构体成员是通过操作符.
进行访问的,.
操作符具有两个操作数。左边是结构体变量名,右边是结构体成员名。
//就用上面举得嵌套访问的例子
int main() {
struct T t = { {10, 'x', 1.00}, "yourfriendyo", 21 };
printf("%d %c %lf %s %d\n", t.s.a, t.s.c, t.s.d, t.name, t.num);
return 0;
}复制
s
是t
的成员变量,a
,c
,d
分别是s
的成员变量,所以是t.s.a
,t.s.c
,t.s.d
。
->
&*
操作符
当然有的时候有可能我们得到的是一个指向该结构体的指针。这时候我们就需要操作符->
,同样也是两个操作数,如:
struct T t = { {10, 'x', 1.00}, "yourfriendyo", 21 };
//1.
struct T* pt = &t;
printf("%d %c %lf %s %d\n", (*pt).s.a, (*pt).s.c, (*pt).s.d, (*pt).name, (*pt).num);
//2.
printf("%d %c %lf %s %d\n", pt->s.a, pt->s.c, pt->s.d, pt->name, pt->num);
//3.
struct S* ps = &(t.s);
printf("%d %c %lf %s %d\n", ps->a, ps->c, ps->d, pt->name, pt->num);复制
第一种方法是我们不知道
->
,只能对指针解引用时想到的方法。第二种方法
->
是专门在访问结构体时使用指针的方法,更高效。第三种方法是更加简单粗暴的方法,我们越过了结构体变量
t
,直接创建指针指向(t.s)
.
结构体传参
struct S {
char arr[100];
int num;
float f;
};
void Print(struct S ss) {
printf("%c %c %c %d %f\n", ss.arr[0], ss.arr[1], ss.arr[2], ss.num, ss.f);
}
void Print2(struct S* ps) {
printf("%c %c %c %d %f\n", ps->arr[0], ps->arr[1], ps->arr[2], ps->num, ps->f);
}
int main() {
struct S s = { {'1','2','3','4'}, 22, 3.14};
Print1(s);
Print2(&s);
return 0;
}复制
既然讨论到函数传参的问题,那么就会有传址调用和传址调用的问题。
如上述代码,我们直接把
s
传过去,显然是传值调用。可见的问题是,我们的s
本身就占用了非常大的空间,然后用创建了一份一样大的形参临时拷贝,时间空间上浪费的问题非常严重。这样的话,很明显我们还是传值调用最好,无论
s
的占用有多大,我们只传了4个字节的地址。