七,结构体
-
结构体类型的声明
-
结构体初始化
-
结构体成员
-
结构体传参
0.前置知识
关于typedef
在C语言中,使用 typedef 关键字可以创建类型别名,也就是为一个已有的数据类型(包括基本类型、结构体、枚举等)赋予一个新的名称。这个新的名称可以用来代替原有的类型名,从而使代码更加清晰、易读和可维护。
通过创建类型别名,我们可以为某个类型提供一个更加具有描述性的名称,或者将复杂的类型声明简化为一个更简单、易于理解的名称。这在代码编写和阅读过程中非常有用,特别是在处理复杂的数据结构、函数指针或回调函数时。
下面是一些示例,展示了如何使用 typedef 创建类型别名:
cCopy codetypedef int MyInt; // 创建一个类型别名 MyInt,代表 int 类型
typedef struct
{
char name[20];
int age;
} Person; // 创建一个类型别名 Person,代表结构体类型
typedef void (*Callback)(int); // 创建一个类型别名 Callback,代表函数指针类型
typedef enum
{
RED,
GREEN,
BLUE
} Color; // 创建一个类型别名 Color,代表枚举类型
在这些示例中,通过 typedef 关键字为不同的数据类型创建了新的类型别名。这样,我们可以在后续的代码中直接使用这些别名,而不需要再写出完整的类型名。这有助于提高代码的可读性和可维护性。
例如,使用了类型别名的代码可能会是这样的:
cCopy codeMyInt num = 10;
Person p;
Callback cb;
Color c = RED;
通过使用类型别名,我们可以更加直观地理解代码中的数据类型,并且减少了重复编写冗长类型名的工作。
1.结构体的声明
1.1 结构的基础知识
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量
1.2结构的声明
struct tag//tag是名字
{
member - list;//成员列表(1个或者多个)
}variable-list;//变量列表
举例说明:
struct Stu
{
//成员变量,是用来描述结构体对象的相关属性的
char name[20];
int age;
char sex[5];//男 女 保密
} s2,s3,s4; //s2,s3,s4 就是结构体变量 - 全局变量
//这个调用的时候只能写struct Stu
typedef struct Stu1
{
//成员变量,是用来描述结构体对象的相关属性的
char name[20];
int age;
char sex[5];//男 女 保密
}shiyong;
//这个调用的时候(typedef)定义了结构体struct Stu1为shiyong,所以之后的代码可以写shiyong xx
int main()
{
//int a = 10;
struct Stu s1;//局部变量
shiyong s2;
return 0;
}
1.3结构成员的类型
结构的成员可以是标量、数组、指针,甚至是其他结构体。
1.4结构体变量的定义和初始化
范例:
struct Point//声明类型
{
int x;
int y;
}p1;//同时定义变量p1
struct Point p2;//定义结构体变量p2
struct Point p3 = { x,y };
2.结构体成员的访问
当谈到结构体的访问方式时,通常有两种常见的方式:
通过点操作符(.)
通过指针操作符(->)
点操作符(.):
点操作符用于直接访问结构体的成员变量。通过结构体变量名和点操作符,可以访问结构体内部的成员变量。
例如,假设我们有一个名为person的结构体,其中包含了name和age两个成员变量。我们可以使用点操作符来访问这些成员变量:
struct Person {
char name[20];
int age;
};
int main()
{
struct Person person1;
strcpy(person1.name, "John");
person1.age = 25;
printf("Name: %s\n", person1.name);
printf("Age: %d\n", person1.age);
return 0;
}
输出结果:
makefileCopy codeName: John
Age: 25
指针操作符(->):
指针操作符用于通过指向结构体的指针来访问结构体的成员变量**。如果我们有一个指向结构体的指针,我们可以使用指针操作符来访问结构体的成员。**
以下是一个使用指针操作符访问结构体成员的示例:
struct Person
{
char name[20];
int age;
};
int main()
{
struct Person person1;
struct Person *ptr = &person1;
strcpy(ptr->name, "John");
ptr->age = 25;
printf("Name: %s\n", ptr->name);
printf("Age: %d\n", ptr->age);
return 0;
}
输出结果与前一个示例相同:
makefileCopy codeName: John
Age: 25
在这个示例中,我们创建了一个指向结构体person1的指针ptr。通过指针操作符**->**,我们可以使用指针来访问结构体的成员变量。
无论是通过点操作符还是指针操作符,两种方式都允许我们访问结构体的成员变量。选择哪种方式取决于我们使用结构体的方式,以及结构体的访问情况(通过变量还是指针)来决定使用哪种操作符。
例1:.访问
#include<stdio.h>
struct S
{
int a;
char arr[6];
int* p;
}s1 = {100, "world", NULL};
struct S s2 = {98, "hehe", NULL};
struct B
{
char ch[10];
struct S s;
double d;
};
int main()
{
struct S s3 = {.arr="hello", .p = NULL, .a = 1};//. 结构成员访问操作符
printf("%d %s %p\n", s3.a, s3.arr, s3.p);//. 结构成员访问操作符
struct B sb = { "hello", {20, "qqq", NULL}, 3.14};
printf("%s %d %s %p %.2lf\n", sb.ch, sb.s.a, sb.s.arr, sb.s.p, sb.d);
return 0;
}
例2:思考为什么这里输出的结果为0?
#include <stdio.h>
#include <string.h>
struct Stu
{
char name[20];
int age;
};
void set_stu(struct Stu t)
{
t.age = 20;
//t.name = "张三";//err
strcpy(t.name, "张三");//字符串拷贝
}
void print_stu(struct Stu t)
{
printf("%s %d\n", t.name, t.age);
}
int main()
{
struct Stu s = {0};
set_stu(s);//设置
print_stu(s);//呈现
return 0;
}
答:
- 在C语言中,字符串不能通过简单的赋值运算符来赋值给字符数组。字符串是以字符数组的形式表示的,而在C语言中,字符数组是一种特殊的数据类型,不能直接通过赋值运算符来进行字符串的赋值。
2. 在这段代码中,set_stu函数接受一个struct Stu类型的参数t,然后尝试修改t的age和name成员变量。然而,函数中对t的修改不会影响到main函数中的s结构体变量,因为在set_stu函数中,参数t是按值传递的。
在函数调用时,传递给set_stu函数的是s的一个副本,而不是s本身。因此,函数中对t的修改不会影响到原始的s结构体变量。
当set_stu函数结束后,对t的任何修改都会被丢弃,而原始的s结构体变量保持不变。
因此,在print_stu函数中打印s时,s的成员变量仍然是初始值,即0。
改:
#include <stdio.h>
#include <string.h>
struct Stu
{
char name[20];
int age;
};
void set_stu(struct Stu *t)
{
t->age = 20;//(*t).age = 20
strcpy(t->name, "张三");//strcpy((*t).name, "张三")
}
void print_stu(struct Stu t)
{
printf("%s %d\n", t.name, t.age);
}
int main()
{
struct Stu s = {0};
set_stu(&s);
print_stu(s);
return 0;
}
3.结构体传参
总结:结构体传参的时候,要传结构体的地址
原因:
函数传参的时候,参数是需要压栈的。
如果传递一个结构体对象的时候,结构体过大,参数压栈的时候系统开销比较大,所以会导致性能下降。
上代码示例:
#include <stdio.h>
struct S
{
int data[1000];
int num;
};
struct S s1 = { {1,2,3,4,5},1000 };//全局变量,不会被销毁
//结构体传参方式1
void print1(struct S s11)
{
printf("%d\n", s11.num);
}
//结构传参方式2(地址传参)
void print2(struct S *ps1)
{
printf("%d\n", ps1->num);
}
int main()
{
print1(s1);
print2(&s1);
return 0;
}