C语言中的结构体指针
- 结构体指针概念
- 结构体变量成员访问
- 结构体指针作为函数参数
- 结构体数组指针 5.结构体指针数组
- 结构体的自引用与不完全声明
结构体指针的概念
概念:结构体变量的地址,指向某个结构体变量(同时也是结构体变量中第一个元素的地址) 结构体指针变量中的值是所指向结构体变量的地址 我们可以通过结构体变量可以指向结构体中包含的一些成员
定义一个结构体变量:
struct 结构体名 *结构体指针变量名;
如:struct address *addr;
结构体指针变量初始化:
结构体指针变量名 = &结构体变量;
或者:结构体指针变量名 = &(结构体变量.第一个成员)
注意:结构体指针必须要初始化以后才能够继续使用
结构体变量成员的访问
结构体指针 ->成员名;如addr->country;
(*结构体指针).成员名;(*addr).country;//很少去进行使用,注意必须去使用(),,因为.优先级大于*
结构体变量.成员名 stu.name
注意:如果是结构体指针的话,它是可以指向自己的(引用自己)
####结构体指针作为函数参数 C语言中结构体传递参数,传递的时间,和空间都是很大的,因为其在调用的时候都是拷贝调用,效率是严重降低,因此结构体作为参数传递的话,此时对于效率一块,将大大的节省,严重降低了程序的效率
结构体指针作为函数参数传递,那么会有很大优化:(实参传向形参的只是一个地址)
out_student(struct student *stup);
//将结构体变量的地址传入进去
结构体数组指针
指向结构体数组的指针 1:结构体指针可以指向一个结构体数组,结构体指针变量的值是整个结构体数组的首地址
struct student stus[] = {stu,stu2};
struct student *stup2 = stus;
2:结构体指针可以指向一个结构体数组中的变量,这时结构体指针变量的值就是该结构体数组元素的地址 3:一个结构体指针虽然可以来访问结构体变量或结构体数组,但是不能指向结构体的成员
结构体的指针数组
结构体指针数组的使用对内存开销会大大降低
struct student *stups [] = {stup1,&stu2};
out_students(stups,2);
结构体指针的自引用
一个结构体内部包含一个指向该结构体本身的指针(必须是结构体指针) struct Self{ int a; int b; struct Self *s; //必须是结构体指针,指向自身 }
结构体的不完整声明: 当一个结构体中去引用另外一个结构体指针,但是另一个结构体又去引用了前一个结构体指针,这样就造成了结构体类似死锁的现象,这个时候就需要用结构体不完整声明,从而去避免这种
struct B;
struct A{
struct *B b;
};
struct B{
struct *A a;
};
代码如下:
struct address{
char *country;
char *city;
char *street;
};
struct student{
int xh;
char *name;
int age;
int gender;
struct address *addr;
};
#include<stdio.h>
#include"student2.h"
#include<string.h>
void out_student(struct student *stu);
void out_students(struct student *stups[],int n);
int main(void)
{
struct address addr1 = {"china","shanghai","beijing road"};
struct student stu = {1,"zhangsan",10,1,&addr1};
struct student *stup1 = &stu;
struct address addr2={"jap","dongjing","hefei road"};
struct student stu2 = {2,"lisi",11,2,&addr2};
printf("student name:%s\n",stup1->name);
printf("student street:%s\n",stup1->addr->street);
printf("===================");
out_student(stup1);
out_student(&stu2);
/*定义一个结构体的数据*/
struct student stus[] = {stu,stu2};
struct student *stup2 = stus;
int i ;
for(i = 0 ;i < 2 ; i++)
{
out_student(stup2+i);
printf("---------------------------------");
}
/*定义一个结构体指针数组*/
struct student *stups [] = {stup1,&stu2};
out_students(stups,2);
return 0;
}
void out_student(struct student *stu)
{
printf("student xh:%d\n",stu->xh);
printf("student name:%s\n",stu->name);
printf("student gender:%d\n",stu->gender);
printf("student age:%d",stu->age);
printf("stduent country:%s\n",stu->addr->country);
printf("student city:%s\n",stu->addr->city);
printf("student street:%s\n",stu->addr->street);
printf("*****************************");
printf("size of:%d\n",sizeof(stu));
}
void out_students(struct student *stup[] ,int n)
{
//代码省略
}
欢迎大家的访问,代码可以进行run,因为最近比较忙,所以可能质量有点小下降,后面会修正。谢谢大家的访问 首先结构体做函数参数有三种传递方式
一是传递结构体变量,这是值传递,二是传递结构体指针,这是地址传递,三是传递结构体成员,当然这也分为值传递和地址传递。
以传引用调用方式传递结构比用传值方式传递结构效率高。以传值方式传递结构需要对整个结构做一份拷贝。
下面看一个列子,student结构体中包含该学生的各种信息,我们在change函数中对其进行部分修改,再在主函数中输出其结果
1.下面传递结构体变量
#include<stdio.h>
#include<string.h>
#define format "%d\n%s\n%f\n%f\n%f\n"
struct student
{
int num;
char name[20];
float score[3];
};
void change( struct student stu );
int main()
{
struct student stu;
stu.num = 12345;
strcpy(stu.name, "Tom");
stu.score[0] = 67.5;
stu.score[1] = 89;
stu.score[2] = 78.6;
change(stu);
printf(format, stu.num, stu.name, stu.score[0], stu.score[1],stu.score[2]);
printf("\n");
return 0;
}
void change(struct student stu)
{
stu.score[0] = 100;
strcpy(stu.name, "jerry");
}
可以看到最终输出的值并未改变。。。 2.地址传递
<span style="font-family:Arial Black;font-size:12px;">#include<stdio.h>
#include<string.h>
#define format "%d\n%s\n%f\n%f\n%f\n"
struct student
{
int num;
char name[20];
float score[3];
};
void change( struct student* stu );
int main()
{
struct student stu;
stu.num = 12345;
strcpy(stu.name, "Tom");
stu.score[0] = 67.5;
stu.score[1] = 89;
stu.score[2] = 78.6;
change(&stu);
printf(format, stu.num, stu.name, stu.score[0], stu.score[1],stu.score[2]);
printf("\n");
return 0;
}
void change(struct student* p)
{
p->score[0] = 100;
strcpy(p->name, "jerry");
}</span>
可以看到,通过地址传递修改了结构体内的数据
用&stu做实参,&stu是结构体变量stu的地址。在调用函数时将该地址传送给形参p(p是指针变量)。这样p就指向stu。
在change函数中改变结构体内成员的值,在主函数中就输出了改变后的值
3.结构体成员的地址传递和值传递
这个类似于单一变量的传递,这里也没必要说了,当然是地址传递才能修改。
把一个完整的结构体变量作为参数传递,要将全部成员值一个一个传递,费时间又费空间,开销大。如果结构体类型中的成员很多,或有一些成员是数组,则程序运行效率会大大降低。在这种情况下,用指针做函数参数比较好,能提高运行效率。
链表
链表是一种常见的基础数据结构,结构体指针在这里得到了充分的利用。链表可以动态的进行存储分配,也就是说,链表是一个功能极为强大的数组,他可以在节点中定义多种数据类型,还可以根据需要随意增添,删除,插入节点。链表都有一个头指针,一般以head来表示,存放的是一个地址。链表中的节点分为两类,头结点和一般节点,头结点是没有数据域的。链表中每个节点都分为两部分,一个数据域,一个是指针域。说到这里你应该就明白了,链表就如同车链子一样,head指向第一个元素:第一个元素又指向第二个元素;……,直到最后一个元素,该元素不再指向其它元素,它称为“表尾”,它的地址部分放一个“NULL”(表示“空地址”),链表到此结束。
作为有强大功能的链表,对他的操作当然有许多,比如:链表的创建,修改,删除,插入,输出,排序,反序,清空链表的元素,求链表的长度等等。
初学链表,一般从单向链表开始
--->NULL
head
这是一个空链表。
---->[p1]---->[p2]...---->[pn]---->[NULL]
head p1->next p2->next pn->next
有n个节点的链表。
创建链表
typedef struct student{
int score;
struct student *next;
} LinkList;
一般创建链表我们都用typedef struct,因为这样定义结构体变量时,我们就可以直接可以用LinkList *a;定义结构体类型变量了。
初始化一个链表,n为链表节点个数。
LinkList *creat(int n){
LinkList *head, *node, *end;//定义头节点,普通节点,尾部节点;
head = (LinkList*)malloc(sizeof(LinkList));//分配地址
end = head; //若是空链表则头尾节点一样
for (int i = 0; i < n; i++) {
node = (LinkList*)malloc(sizeof(LinkList));
scanf("%d", &node->score);
end->next = node;
end = node;
}
end->next = NULL;//结束创建
return head;
}
修改链表节点值
修改链表节点值很简单。下面是一个传入链表和要修改的节点,来修改值的函数。
void change(LinkList *list,int n) {//n为第n个节点
LinkList *t = list;
int i = 0;
while (i < n && t != NULL) {
t = t->next;
i++;
}
if (t != NULL) {
puts("输入要修改的值");
scanf("%d", &t->score);
}
else {
puts("节点不存在");
}
}
删除链表节点
删除链表的元素也就是把前节点的指针域越过要删除的节点指向下下个节点。即:p->next = q->next;然后放出q节点的空间,即free(q);
void delet(LinkList *list, int n) {
LinkList *t = list, *in;
int i = 0;
while (i < n && t != NULL) {
in = t;
t = t->next;
i++;
}
if (t != NULL) {
in->next = t->next;
free(t);
}
else {
puts("节点不存在");
}
}
插入链表节点
我们可以看出来,插入节点就是用插入前节点的指针域链接上插入节点的数据域,再把插入节点的指针域链接上插入后节点的数据域。根据图,插入节点也就是:e->next = head->next; head->next = e;
增加链表节点用到了两个结构体指针和一个int数据。
void insert(LinkList *list, int n) {
LinkList *t = list, *in;
int i = 0;
while (i < n && t != NULL) {
t = t->next;
i++;
}
if (t != NULL) {
in = (LinkList*)malloc(sizeof(LinkList));
puts("输入要插入的值");
scanf("%d", &in->score);
in->next = t->next;//填充in节点的指针域,也就是说把in的指针域指向t的下一个节点
t->next = in;//填充t节点的指针域,把t的指针域重新指向in
}
else {
puts("节点不存在");
}
}
输出链表
输出链表很简单,边遍历边输出就行了。
while (h->next != NULL) {
h = h->next;
printf("%d ", h->score);
}