基本概念
结构是一些值的集合,这些值称为成员变量。结构的每个成员可以是不同类型的变量。
结构体是一种自定义类型
结构体的声明
struct tag
{
member-list;
}variable-list; //分号不能丢
不完全声明—— 匿名结构体
//匿名结构体类型
struct
{
int a;
char b;
float c;
}x;
struct
{
int a;
char b;
float c;
} a[20], *p;
p = &x;
上面的结构在声明的时候省略掉了结构体标签(tag)。
是否可行?
不可行
更改为如下可以使用
struct
{
int a;
char b;
float c;
}x,*p;
p = &x;
结论如下:
结构体的类型是否一致,取决于是否是同一个结构体定义的变量,哪怕两个结构体的成员完全一致,也是两种类型。
结构体自引用
struct Node
{
int data;
struct Node next; //空间不确定
}; //可行否? 如果可以,那sizeof(struct Node)是多少?
类似于递归没有出口的状态
正确的自引用如下所示:
struct Node
{
int data;
struct Node* next;//它的类似是struct Node* 它的空间是可以定义出来的
};
对结构体进行重命名必须要保证结构体本身的类型是确定的,
typedef struct
{
int data;
Node* next; //此处的Node 的类型不明确 程序到这行代码的时候 还没有Node 这样的类型
//就好像是只知道 这是一个指针,但是是 int *?double *? 还是结构体的指针是不确定的。
}Node; //真正是到这里才明确的,所以不可行
//解决方案:
typedef struct node
{
int data;
struct node* next; //而这里事实上是明确了 是一个结构体的指针
}Node;
定义结构体类型
struct stu
{
char name[20];
int age;
char sex[10];
};
```、
//以下定义结构体指针的写法不太推荐,但需要了解。
```c
typedef struct stu
{
char name[20];
int age;
char sex[5];
char id[20];
}stu, *stu_p, stu_arr[10];//
//定义结构体类型
//定义了一个指针类型 类似于int *
//定义了一个数组类型 类似于int arr
int main()
{
stu a = { "zhangsan",18, "man", "001" };
stu_p p = &a; //p 是指针的名字
stu_arr arr1; //arr1 是数组的名字 是有十个结构体变量的集合
下面的写法就一目了然,能够很容易知道是个什么变量,(与上面的是等效的)
stu student1 = { "zhangsan", 29, "man" };
stu *p = &student1;
stu arr[10];
结构体成员可以是标量(int \float\double…),嵌套数组,指针,甚至是其他结构体。
结构体的内存对齐(重要理解)
重命名
typedef struct stu
{
char name[20];
int age;
char sex[10];
}stu_t;
struct stu 叫做结构体类型
就类似于 int float double 这个意思
有的时候觉得写起来太复杂就重命名stu_t,此时struct stu 等于stu_t。
结构体赋值
struct stu student1 = { "zhangsan", 29, "man" };
struct stu student2 = { "lisi",18, "felman" };
student1 、 student2 就相当于变量名。
举个例子 int a=10; a 就相当于这里的student1 、 student2。 花括号里面就是赋值。
强调
结构体和数组一样都是一个聚合类型。聚合类型除了在初始化的时候可以整体赋值,其余时候都不能整体赋值
对于数组,循环赋值。
对于结构体,用. 操作符赋值。
//以下为错误用法
int arr[5];
arr={1,2,3,4,5};
stu a;
a={ "zhangsan",18, "man", "001" };
struct stu s
{
char name[20];
int age;
}
int main()
{
struct stu s;
strcpy(s.name,"zahngsan"); // 错的 name 是一个数组,不能直接赋值 s.name="zhangsan"
s.age=20;
return 0;
}
成员访问
printf("%s %d %s", student1.name, student1.age, student1.sex)
对于一个变量必须提到的三个问题:空间,内容,地址。
下面主要介绍关于地址。
既然是一个变量自然可以取地址
stu_t student1 = { "zhangsan", 29, "man" };
stu_t *p = &student1;//与上面的定义一个指针类型是一样的效果
printf("%s %d %s\n", (*p).name, (*p).age, (*p).sex);//优先级问题
printf("%s %d %s\n", p->name, p->age, p->sex);
->主要是对结构体指针 来引用成员名字。
printf("%s %d %s\n", (*p).name, (*p).age, (*p).sex)
这种写法实在太过于复杂,因而有了-> .
以下报错: 注意优先级问题
事实上 . 的优先级比*高,会先和p 结合,故而报错。
printf("%s %d %s\n", *p.name, *p.age,*p.sex);
结构体传参数,传值 ,拷贝
结论
结构体不谈“退化、降维”问题,结构体要发生对应的硬拷贝(注意:结构体内部的数组也是硬拷贝)
但数组传递参数本身就发生降维问题。
建议
结构体传参的时候,要传结构体的指针
void show(stu_t s)
{
printf("%s %d %s", s.name, s.age,s.sex);
printf("show:%p\n", &s);
}
int main()
{
stu_t student1 = { "zhangsan", 29, "man" };
stu_t *p = &student1;
show(student1);
printf("main:%p\n", &student1)
system("pause");
return 0;
}
//区分以下有什么区别。
void show(stu_t *p)
{
printf("%s %d %s\n", p->name, p->age, p->sex);
printf("show:&*p:%p\n", p);//用的是指针变量p 的内容
printf("show:&p:%p\n", &p);
}
int main()
{
show(&student1);
printf("main:%p\n", &student1);
system("pause");
return 0;
}
//如果是指针其实打印出来的两个地址是一样的
结构体内存对齐
复盘
很重要的一个点就是,学习的过程是由厚到薄。抓住核心,归纳总结。
如果觉得自己越学越难,越学越多,越复杂,可能需要重新找一下方法。理解内在逻辑关系。