C语言----链表理论(高阶篇三十六)
动态内存分配
程序1
sizeof与结构
//36-1 sizeof与结构.c #include <stdio.h> #pragma pack(1) //字节对齐 struct student //定义结构类型 { char name[9]; char sex[3]; int age; }*pstu, stu = {"王什","男",46}; main() { double d, *pd = &d; //双精度浮点型 printf("%d\n", sizeof(struct student)); //结构内存 printf("%d\n", sizeof(stu)); //结构变量内存 printf("%d\n", sizeof(pstu)); //结构指针内存 printf("%d\n", sizeof(double)); //双精度浮点型内存 printf("%d\n", sizeof(d)); //双精度浮点型变量内存 printf("%d\n", sizeof(pd)); //双精度浮点型变量内存 }
※在sizeof中使用变量和类型效果一样,如果计算的是变量,它只会计算出变量对应类型所占的字节数。
普通的变量定义,在定义时分配内存,函数结束时释放内存,这一切都由系统自动完成。在C语言中,程序员也可以自由地,不受系统控制地手动分配内存。若要手动分配内存,需要用到以下知识点:
※使用sizeof函数计算变量或类型的大小
※使用malloc函数、free函数分配内存
※使用指针指向已分配的内存。
程序2
无名变量
// 36-2无名变量.c #include <stdio.h> main() { int *p1, *p2, *p3; //定义三个整型指针 p1 = (int*)malloc(sizeof(int)); //p1指针分配4字节内存空间 p2 = (int*)malloc(sizeof(int)); //p2指针分配4字节内存空间 p3 = (int*)malloc(sizeof(int)); //p3指针分配4字节内存空间 printf("变量地址:%d,%d,%d\n", p1, p2, p3); //分配的地址 *p1 = 10; *p2 = 20; *p3 = 30; printf("变量内容:%d,%d,%d\n", *p1, *p2, *p3); //内容 free(p1); //释放内存 free(p2); free(p3); printf("变量内容:%d,%d,%d\n", *p1, *p2, *p3); //释放后的内容 }
※malloc和free通常配对使用,malloc的内存如果没有用free释放,会造成“内存泄露”。
链表原理
使用数组存储数据时,所有的数据在内存中连成一片,需要取出某个数据,只要报上它在内存中的房间编号(数组下标)即可马上取出。
数组也有缺点,它必须预先分配好一些内存,如果这些内存未全部使用,其它的就浪废了。这在字符串的使用中尤为明显:
char str[100]=”hello!”;
程序员定义这个字符串最大能接受99个字符。但是在这里因为实际需求只用了前面7个字符,后面的93个字节就浪废了,浪废率达到90%。
于是软件工程师们就想出“链表”这种存储方法,它的内存结构如下:
在C语言中,一般用结构表示链表:
struct student
{
char name[9]; /*姓名*/
int age; /*年龄*/
struct student *pNext; /*下一结点*/
};
※老师带小朋友游玩时,老师用手拉着第一个小朋友,第一个小朋友又拉着后面的小朋友,以此类推……
这里的“头指针”可以比作老师的手,后面的“指针”就像小朋友的手,最后一个小朋友后面没有拉人,所以它设置为NULL。
链表这种结构,就像锁链一样,一环扣着一环,有一个环坏了,后面所有的结点都会丢失,这就是链表的特点。
程序3
手拉手的小朋友
// 36-3手拉手小盆友(链表简单使用).c #include <stdio.h> struct kid //小盆友结构变量 { char name[9]; int age; struct kid *pHand; //指针指向下一个小盆友 }; main() { struct kid k1 = { "小名",3 }; struct kid k2 = { "小红",4 }; struct kid k3 = { "小冰",6 }; k1.pHand = &k2; //第一个小朋友拉着第二个小盆友的手 k2.pHand = &k3; //第二个小朋友拉着第三个小盆友的手 k3.pHand = NULL; printf("%s拉着的小盆友是:%s\n", k1.name, k1.pHand->name); //换种写法: (*k1.pHand).name printf("%s拉着的小盆友是:%s\n", k2.name, k2.pHand->name); }
名词解释
结点(Node):
在《数据结构》中,结点通常表示一个数据单位,比如在数组中,数组元素可称为结点。在“树”结构中,每个分支元素也称为结点。如:设系为根结点,班是系的子结点,学生是班的子结点。反推过去,系是班的父结点,班是学生的父结点,系是所有子结点的祖宗结点。
链表和数组都不是用来描述父子关系的,它们通常用来描述“兄弟关系”,所以结点与结点的关系不像“树”结构这么复杂。
作业
// 36-4练习.c #include <stdio.h> fut(int **s, int p[2][3]) { **s = p[1][1]; //指针与二维数组中 **s = *(*(s+0)+0) 取内容 } main() { int a[2][3] = { 1,3,5,7,9,11 }, *p; p = (int*)malloc(sizeof(int)); //定义一个无名变量 fut(&p, a); printf("%d\n", *p); //结果是 : 9 }