结体的定义与使用
- 结构体是一种构造数据类型
- 把不同类型的数据组合成一个整体
- c语言使用结构体变量进一步加强了表示数据的能力。
定义形式:struct 结构体名{
结构体包含的基本类型
};
比如我们打游戏,会有人物的名称,血量,蓝量,经验等等,如果我们把这些数据都用一个人物数据的结构体来表示的话
struct Game_person{
char name[];
int hp;
int mp;
double experience;
};
它里面包含了多个变量或数组,它们的类型可以相同,也可以不同,每个这样的变量或数组都称为结构体的成员(Member).
上述是我们已经创建了一个基本的结构体模板(结构体类型),它指明了结构体的存储方式(),如果我们想真正创建一个结构体对象。就要实例化结构体
struct Game_person user1;
可以这么理解,(char name[50])
如此,我们就实例化了一个结构体对象;有了实例对象,就可以对结构体对象进行初始化了
结构体的初始化
初始化方法和数组差不多,只不过数组可以在创建时进行初始化,而结构体必须要实例化之后才可以进行初始化。
struct Game_person user1={"韩信",100,100,1300};
或者我们也可以
struct Game_person{
char name[];
int hp;
int mp;
double experience;
}user1;
<结构体类型变量名>.<成员名>进行初始化;.
其结合性是自左至右的,它在所有的运算符中优先级是最高的;
这里涉及到一个之前的知识点,如果我将user1.name = "韩信";这样是否可以?答案是不可以
因为user1.name表示这个字符串的首地址,而"韩信"会作为常量存储在常量区 有自己单独的地址,数组作为指针常量,其指向的地址无法被改变,所以这种定义方法是错误的,正确的是用字符串操作函数strcpy
把字符串拷贝到数组中去。
strcpy(user1.name,"韩信");
user1.hp = 100;
user1.mp = 100;
user1.experience = 1300;
需要注意的是,结构体是一种自定义的数据类型,是创建变量的模板,不占用内存空间;结构体变量才包含了实实在在的数据,需要内存空间来存储。
结构体嵌套
1.子引用:就是在结构体内部包含指向自身类型结构体的指针。
我们在后面会见到这个类型的引用,比如链表
struct Node_{
int value;
struct Node *link;
}Node,*PNode;
注意事项
我们可以先通过typedef定义结构体,然后再进行子引用
typedef struct Node_ Node;
struct Node_{
int value;
Node*link;
};
C++或者可以直接在结构体直接创建一个示例
typedef struct Node_{
int value;
struct Node_*link;
};
C++不要没有提前声明就使用typedef定义的结构体声明
比如
typedef Node_{
int value;
Node*link;
}Node;
C++这种情况编译是不通过的,因为使用了之后定义的结构体变量
用typedef定义结构体
我们会发现 如果我们通过结构体定义并初始化 ,代码会很长,这时候我们可以通过 typedef
来定义结构体。
之后我会单独写一篇笔记介绍typedef
这里仅对结构体定义阐述
typedef struct Game_person
{
member-list;
}Gp;
C++这里的GP不是实例化结构体,而是对struct的重命名。之后我们既可以通过Gp创建也可以通过struct Game_person定义结构体。
结构体作用域
关于其struct声明的位置,也就是这段代码要放到哪里。同样这也是具有作用域的。
这种声明如果放在任何函数的外面,那么则可选标记可以在本文件中,该声明的后面的所有函数都可以使用。如果这种声明在某个函数的内部,则它的标记只能咋内部使用,并且在其声明之后;
结构体指针
声明结构体指针 struct Game_person * puser;
该指针可以指向任何Game_person结构体 puser = &user[0];
有两种方式可以通过结构体指针访问结构体成员
1.指针
puser->name
2.解引用指针
(*puser).name(因为.的优先级最高,所以要加括号)
结构体作为函数参数
1.结构体指针
#include<stdio.h>
typedef struct Game_person {
char name[50];
int hp;
int mp;
double experience;
}Gp;
void Changestrcut(struct Game_person* puserchange)
{
puserchange->hp = 55;
}
int main()
{
Gp testuser = { "钟馗",100,100,1300 };
Changestrcut(&testuser);
printf("%s %d %d %0.2f\n", testuser.name, testuser.hp, testuser.mp, testuser.experience);
}
C++上述代码通过结构体指针传参修改结构体成员内容,这是被允许的,之前我们也讲过一些不被允许的情况,应该是在指针那几章,具体请等我后面详细复习一期函数传参的文章。
运行截图:
可以看到通过结构体的地址,我们可以找到数据的真实地址,进而修改真实数据
- testuser结构体内的数据发生了变化
- testuser结构体的原地址没有发生变化
2.结构体传参
这种传参方法只可以访问结构体成员,不能修改结构体成员(原因在指针那几篇 ) 正因为无法
#include<stdio.h>
typedef struct Game_person {
char name[50];
int hp;
int mp;
double experience;
}Gp;
void getinfo(struct Game_person puserchange)
{
printf("address of getinfo %p\n", &puserchange);
puserchange.hp = 55;
}
int main()
{
Gp testuser = { "钟馗",100,100,1300 };
printf("address of main %p\n", &testuser);
getinfo(testuser);
printf("%s %d %d %0.2f\n", testuser.name, testuser.hp, testuser.mp, testuser.experience);
}
C++运行截图:
3.修改结构体地址
我们将传入函数的结构体地址,重新分配一块新的内存这个指针,以达到修改的目的,但是我们发现没这样并不会影响原地址数据
因为是结构体指针,当他指向其他地址进行修改时,并不会影响原结构体数据
大致的内存图就是这样的,可以看到并不会影响原来的结构体
#include<stdio.h>
typedef struct Game_person {
char name[50];
int hp;
int mp;
double experience;
}Gp;
Gp* getinfo(Gp* user01)
{
printf("01puserchange = %p\n", user01);
printf("01puserchange->hp = %d\n", user01->hp);
user01 = (Gp*)malloc(sizeof(Gp));
user01->hp = 55;
printf("02address of newaddress %p\n", &user01);
printf("02puserchange->hp = %d\n", user01->hp);
return user01;
}
int main()
{
Gp user = {"李鑫",150,150,1300};
Gp* testuser;
printf("address of main %p\n", &user);
testuser = getinfo(&user);
printf("newaddress 数据 %d\n", testuser->hp);
}
C++双指针传参
跟之前指针传参差不多,也是可以修改结构体地址和结构体成员。再分析一下。
可以看到我们通过在函数内部新开辟的结构体修改了原结构体的地址和结构体成员的值
#include<stdio.h>
typedef struct Game_person {
char name[50];
int hp;
int mp;
double experience;
}Gp;
Gp* getinfo(Gp** user01)
{
printf("getinfo\n");
printf("address of getinfo %p\n", *user01);
printf("value of getinfo %d\n", (*user01)->hp);
*user01 = (Gp*)malloc(sizeof(Gp));
(*user01)->hp = 55;
printf("02address of newaddress %p\n", &user01);
printf("02puserchange->hp = %d\n", (*user01)->hp);
return user01;
}
int main()
{
Gp tests;
Gp *user2 = &tests;
Gp** user = &user2;
(*user)->hp = 100;
Gp** testuser;
printf("main\n");
printf("address of main %p\n", *user);
printf("address of main %d\n", (*user)->hp);
testuser = getinfo(user);
printf("newaddress 数据 %p\n", *testuser);
printf("newaddress 数据 %d\n", (*testuser)->hp);
printf("user 数据 %p\n", *user);
printf("user 数据 %d\n", (*user)->hp);
}
C++对比上一标题,会发现两幅图大同小异,只是多了一个指针,之前我们只是修改了指针的指向,比如指向标被风刮跑了,那你家难道也跟着跑吗?二级指针就比如是你家户主,你家地址在哪由他说了算,他说搬家就要搬家,拥有绝对的权限。
如果还有迷惑