暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

C语言详解:结构体

AKA你的闺蜜 2021-08-26
272

目录

结构体

                结构体类型的声明

                                结构体声明的语法结构

                                声明结构体

                                创建变量示例

                结构体成员的类型

                结构体变量定义及初始化

                结构体成员访问

                结构体传参

结构体

当然本次结构体仍不会讲的特别深入,在后面的自定义类型进阶会更加深入的讲到这部分内容。

结构(体)是一些值的集合,这些值被称为成员变量。结构的每个成员变量可以具有不同类型

我们所学过的类型如:char
,int
,float
,double
等,都只能描述单一变量。但是结构体,顾名思义,是多个变量的集合,其中包含多个单一变量。所以C语言就发明了结构体用于用来描述复杂对象,如:书,人等具有多个特征的变量。

结构体类型的声明

结构体声明的语法结构
struct tag//结构体名称
{

member-list;//成员列表

}variable-list;//变量列表(全局变量)

复制

在结构体声明的{}
后面创建的变量是全局变量,如果我们只想创建局部变量,就在mian
函数里创建。

几个名词需要我们额外注意区分一下。

  1. 结构体(类型)名
struct tag

复制

这个叫结构体类型名,或者叫结构体类型,使用上相当于int
,char
等类型,故叫类型名。

  1. 结构体成员变量
struct S {
char a[20];
char b[5];
float c;
};

复制

因为{}
内部定义的叫成员变量,如例子中a
,b
,c
这样的。定义结构体成员变量也可以顺带初始化。

  1. 结构体变量名
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
也需要使用{}
进行初始化。

结构体成员访问

  1. .
    操作符

结构体成员是通过操作符.
进行访问的,.
操作符具有两个操作数。左边是结构体变量名,右边是结构体成员名。

//就用上面举得嵌套访问的例子
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

  1. ->
    &*
    操作符

当然有的时候有可能我们得到的是一个指向该结构体的指针。这时候我们就需要操作符->
,同样也是两个操作数,如:

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个字节的地址。

点击留言

文章转载自AKA你的闺蜜,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论