总结了一些指针易出错的常见问题(四)
指针与结构体
简介:我们可以使用C的结构体来表示数据结构元素,比如链表或树的节点,指针是把这些元素联系到一起的纽带。
typedef struct _person{ char* firstName; char* lastName; char* title; unsigned int age; } Person;
/*用点表示法初始化*/
Person person;
person.firstName=(char*)malloc(strlen("Emily")+1);
stcpy(person.firstName,"Emily");
person.age=23;
/*****结构体指针初始化*/
Person *ptrperson;
ptrperson=(Person*)malloc(sizeof(Person));
ptrperson->firstName=(char*)malloc(strlen("Emily")+1);
strcpy(ptrperson->firstName,"Emily");
ptrperson->age=23; // (*ptrperson).age = 23;
为结构体分配内存,分配的内存大小至少是各个字段的长度之和。不过,实际长度会大于这个和,结构体的各字段之间可能会有填充。结构体数组各元素之间会有填充。
结构体释放问题:
用结构体变量和指向结构体的指针函数参数
1.用结构体变量的成员作参数。(用法和普通变量相同)
2.用结构体变量作实参。形参也必须是同类型的结构体变量。调用期间形参也要占用内存。(空间和时间上开销较大),较少使用该方法。
3.用指向结构体变量(或数组)的指针作实参,将结构体变量(数组)的地址传给形参。
用指针处理链表
链表是一种动态地进行存储分配的一种数据结构。 链表是C语言编程中常用的数据结构,比如我们要建一个整数链表,一般可能这么定义:
struct int_node {
int val;
struct int_node *next;
};
假设有五个学生某一门功课的成绩分别为A、B、C、D和E,这五个数据在内存中的存储单元地址分别为1248、1488、1366、1022和1520,其链表结构如图所示。
链表有一个“头指针”变量,图中以 head表示,它存放一个地址,该地址指向链表中第一个结点,第一个结点又指向第二个结点……直到最后一个结点。该结点不再指向其他结点,它称为“表尾”,它的地址部分存放一个“NULL”(表示“空地址”),链表到此结束。链表中每个结点都包括两个部分:用户需要用的实际数据和下一个结点的地址。
链表每个结点中只有一个指向后继结点的指针,该链表称为单链表。其实结点中可以有不止一个用于链接其他结点的指针。如果每个结点中有两个用于链接其他结点的指针,一个指向前趋结点(称前趋指针),另一个指向后继结点(称后继指针),则构成双向链表。双向链表如图所示。
/*单链表的例子(静态链表:因为所有节点在程序中定义的,不是临时开辟的,也不能用完后释放)*/ #include<stdio.h> //#define NULL 0 struct student { int num; int score; struct student *Next; }; int main() { struct student a,b,*head,*p; a.num=10012; a.score=99; b.num=10013; b.score=81; head=&a; a.Next=&b; b.Next=NULL; p=head; do { printf("%5d %ld\n",p->num,p->score); p=p->Next; }while(p!=NULL); }
问题:结构体的变量名可以当做地址赋给指针吗?没有头指针head行不行?p起了什么作用?没它可以吗?
处理动态链表用到的函数 calloc/malloc/free
malloc函数原型: void *malloc(unsigned int size);其作用是在内存的动态存储区分配一个长度为size的连续空间。此函数的值(返回值)是一个分配域的起始地址(void类型);若函数未成功执行,则返回空指针(NULL)。
calloc函数原型: void *calloc(unsigned n,unsigned size);其作用是在内存的动态存储区分配n个长度为size的连续空间。函数返回一个指向分配域起始位置的指针;否则,返回NULL。
free 函数原型: void free(void *p);释放由p指向的动态存储区。free无返回值。
建立动态链表(知难而进)
/*写一函数建立一个有3名学生数据的单向动态链表*/ #include<stdio.h> #include<malloc.h> //#define NULL 0 #define LEN sizeof(struct student) struct student { long num; float score; struct student *next; }; int n; struct student *creat(void) { struct student *head; struct student *p1,*p2; n=0; p1=p2=(struct student*)malloc(LEN); scanf("%ld,%f",&p1->num,&p1->score); head=NULL; while(p1->num!=0) { n=n+1; if(n==1) head=p1; else p2->next=p1; p2=p1; p1=(struct student*)malloc(LEN); scanf("%ld,%f",&p1->num,&p1->score); } p2->next=NULL; return(head); } int main() { creat(); }
建立链表首先要定义一个包含数据域和指针域的的结构类型,然后建立指向表头节点的头指针head,最后通过malloc函数动态申请一块内存作为表头节点。
typedef struct node { int data; /*信息*/ struct node *link; /*指针*/ }NODE; /*定义节点*/ NODE *head; /*定义头指针head */
定义结构类型和头节点之后,我们要建立不包含数据的表头节点,可以按下列语句进行操作。
NODE *p; /*说明一个指向节点的指针变量p */ p=(NODE*) malloc(sizeof(NODE)); /*申请表头节点*/ p->link = NULL; /*将表头节点的link置为NULL */ head=p; /*head指向表头节点p*/
由于此时链表中只有一个表头节点,没有数据节点,所以称为空链表。
p=(NODE*) malloc(sizeof(NODE)); /*申请一个数据节点*/ 为了在链表中保存数据,可以从表头位置将数据节点插入到链表中,例如,插入一个数据节点:
p=(NODE*) malloc(sizeof(NODE)); /*申请一个数据节点*/ gets(p ->data); /*输入一个新的数据*/ p->link=head->link; /*建立链接关系。将表头节点的link存入p 的link中*/ head->link=p; /*将数据节点插在表头节点之后成为第一个数据节点*/
插入第一个数据节点后链表,然后继续插入下一个数据节点。
create(NODE *head,int n) 根据上面的链表建立过程,可以写出函数create建立有n个数据节点的链表。
create(NODE *head,int n) { NODE *p; for(; n>0;n--) { p=(NODE*) malloc(sizeof(NODE)); if(p==NULL) exit(0); gets(p->data); p->link = head->link; head->link = p; } }