内存管理:
1.操作野指针会导致程序崩溃
注意:操作指针时,注意指针指向的空间是否存在?是否可用?
2.避免产生内存溢出:
1.存储越界
当使用strcat、strcpy、strcmp函数时可能会产生内存越界
应该用strncat、strncpy、strncmp函数替代
2.越界访问
数组避免越界访问
字符串缺少\0导致的越界访问
3.堆区空间的使用:
malloc
void *malloc(size_t size);
功能:
申请size个字节的堆区空间
参数:
size:申请空间的大小
返回值:
如果成功,返回指向申请空间的指针
如果失败,返回NULL
free
void free(void *ptr);
功能:
释放堆区空间
参数:
ptr:堆区空间首地址
返回值:
缺省
4.内存泄露:
申请的堆区空间没有被释放
5.malloc使用场景:
1.函数体内部的局部变量会随函数结束被回收,可以考虑存放到堆区空间中避免随函数结束回收
2.C语言中可变长数组可以通过malloc实现
5
10
20
30
40
50
6.内存碎片:
链表:
1.数组:
1.数组是有限个元素的集合
2.数组的空间必须连续
3.数组的插入和删除效率低
7.单向链表
这使用的头插法,比较高效简单
linklist.c 单向链表的代码实现
linklist.h 单向链表头文件
main.c 主函数
#ifndef __LINKLIST_H__
#define __LINKLIST_H__
/* 链表节点类型 */
typedef struct node
{
int data; //数据域
struct node *pnext; //地址域(下一个节点的地址)
}LinkNode;
extern LinkNode *CreateLinkList(void);
extern int InsertHeadLinkList(LinkNode *phead, int tmpdata);
extern void ShowLinkList(LinkNode *phead);
extern int ModifyLinkList(LinkNode *pHead, int OldDate, int NewData);
extern int DeleteLinkList (LinkNode *pHead,int data);
extern int DestroyLinkList(LinkNode **pphead);
#endif
#include <stdlib.h>
#include <stdio.h>
#include "linklist.h"
/******************************************
*函数名:CreateLinkList
*功 能:
* 创建一个空单向有头链表
*参 数:
* 缺省
*返回值:
* 成功返回头结点地址
* 失败返回NULL
*注意事项:
*
******************************************/
LinkNode *CreateLinkList(void)//函数的返回类型为LinkNode *。
{
LinkNode *pTmpNode = NULL;
//1.申请空间
pTmpNode = malloc(sizeof(LinkNode));
if (NULL == pTmpNode)
{
return NULL;
}
//2.对每个成员赋初值
pTmpNode->pnext = NULL;
return pTmpNode;
}
/******************************************
*函数名:InsertHeadLinkList
*功 能:
* 单向链表头插法插入元素
*参 数:
* phead:链表头结点地址
* tmpdata:插入数据
*返回值:
* 成功返回0
* 失败返回-1
*注意事项:
*
******************************************/
int InsertHeadLinkList(LinkNode *phead, int tmpdata)
{
LinkNode *pTmpNode = NULL;
//1.申请存放数据的空间
pTmpNode = malloc(sizeof(LinkNode));
if (NULL == pTmpNode)
{
return -1;
}
//2.数据存进去
pTmpNode->data = tmpdata;
//3.地址域赋值为空白节点的pnext
pTmpNode->pnext = phead->pnext;
//4.空白节点的pnext指向新申请节点
phead->pnext = pTmpNode;
return 0;
}
/******************************************
*函数名:ShowLinkList
*功 能:
* 打印链表中所有节点的数据
*参 数:
* phead:链表头结点地址
*返回值:
* 缺省
*注意事项:
*
******************************************/
void ShowLinkList(LinkNode *phead)
{
LinkNode *p = NULL;
p = phead->pnext;
while (p != NULL)
{
printf("%d ", p->data);
p = p->pnext;
}
printf("\n");
return;
}
/******************************************
*函数名:ModifyLinkList
*功 能:
* 修改链表中节点的数据
*参 数:
* phead:链表头结点地址
OldData:旧的数据
NewData:新的数据
*返回值:
* 修改的数据的个数
*注意事项:
*
******************************************/
int ModifyLinkList(LinkNode *pHead, int OldDate, int NewData)
{
int cnt = 0;
LinkNode *pTmpNode = NULL;
pTmpNode = pHead->pNnext;
while(pTmpNode != NULL)
{
if(pTmpNode->data == OldDate)
{
pTmpNode->data == NewData;
cnt++;
}
pTmpNode = pTmpNode->pNnext;
}
return cnt;
}
/******************************************
*函数名:DeleteLinkList
*功 能:
* 删除链表中节点
*参 数:
* phead:链表头结点地址
data:要删除的数据
*返回值:
* 删除的节点的个数
*注意事项:
*
******************************************/
int DeleteLinkList (LinkNode *pHead,int data)
{
int cnt = 0;
LinkNode *ptmpnode = NULL;
LinkNode *pfrontnode = NULL;
ptmpnode = phead->pnext;
pfrontnode = phead;
while(ptmpnode != NULL)
{
if(ptmpnode->data == data)
{
pfrontnode->pnext = ptmpnode->pnext;
free(ptmpnode);//释放掉申请的空间,此时ptmpnode变为野指针
ptmpnode = pfrontnode->pnex;
cnt++;
}
else
{
ptmpnode = ptmpnode->pnext;
pfrontnode = pfrontnode->pnext;
}
}
return cnt;
}
/******************************************
*函数名:DestroyLinkList
*功 能:
* 销毁列表
*参 数:
* phead:链表头结点地址
*返回值:
* 成功返回0
失败返回-1
*注意事项:
*
******************************************/
int DestroyLinkList(LinkNode **pphead)
{
LinkNode *ptmpnode = NULL;
LinkNode *pfreenode = NULL;
ptmpnode = *phead;
while(ptmpnode != NULL)
{
pfreenode = ptmpnode;
ptmpnode = ptmpnode->pnext;
free(pfreenode);
pfreenode = ptmpnode;
}
**pphead = NULL;//函数想要内改变外面的值需要用指针。
return 0;
}
#include <stdio.h>
#include "linklist.h"
int main(void)
{
LinkNode *plinklist = NULL;
int i = 0;
int ret = 0;
plinklist = CreateLinkList();
if (NULL == plinklist)
{
printf("创建失败\n");
return -1;
}
for (i = 0; i < 10; i++)
{
ret = InsertHeadLinkList(plinklist, i);
if (-1 == ret)
{
printf("%d 插入失败!\n", i);
}
}
ShowLinkList(plinklist);
ModifyLinkList(plinklist,5,500);
ModifyLinkList(plinklist,3,500);
ModifyLinkList(plinklist,2,500);
ShowLinkList(plinklist);
DeleteLinkList(plinklist,500);
ShowLinkList(plinklist);
DestroyLinkList(&plinklist)
return 0;
}
结果如下:
注意:
是所有的节点的遍历。
主要是找最后一个节点。
关于在linux操作系统中粘贴,复制,剪切
进入之后按Esc------输入v是选择------输入y是复制-----d是剪切-------p是粘贴。
8.内核链表
(1).链表节点的定义
单向链表是节点里面包含数据,在定义结构体的时候就已经定义好了要存放的数据类型,这样必须提前要知道存什么类型的数据。不方便使用。
现在内核链表就是数据里面包含节点。定义一个node结构体,这个node结构体只包含了这个节点。用户在使用的时候自己去定义data结构体,这个data结构体成员有前面定义的node结构体,也有自己去定义的数据。
这样可以在不知道数据类型的情况下实现这个功能,就是把存什么数据这个交给使用者,不是代码编写的人。具体的使用如下:
这里链表节点的地址等同于数据包含的小节点的地址,但是要进行强制类型转换。
#ifndef __KERLIST_H__
#define __KERLIST_H__
/* 链表节点类型 */
typedef struct node
{
struct node *pnext; //链表下一个节点的地址
}LinkNode;
extern LinkNode *CreateKerList(void);
extern int InsertHeadKerList(LinkNode *pHead, LinkNode *pNewNode);
extern int ForeachKerList(LinkNode *pHead, int (*pfun)(LinkNode *pTmpNode));
extern LinkNode *SearchKerList(LinkNode *pHead, int (*pcmpfun)(LinkNode *pTmpNode));
extern int DestroyKerList(LinkNode **pHead);
#endif
#include "kerlist.h"
#include <stdlib.h>
/*******************************************************
*函数名: CreateKerList
*功 能:
* 创建一个空内核链表
*参 数:
* 缺省
*返回值:
* 成功返回表头地址
* 失败返回NULL
******************************************************/
LinkNode *CreateKerList(void)
{
LinkNode *pTmpNode = NULL;
pTmpNode = malloc(sizeof(LinkNode));
if (NULL == pTmpNode)
{
return NULL;
}
pTmpNode->pnext = NULL;
return pTmpNode;
}
/*******************************************************
*函数名: InsertHeadKerList
*功 能:
* 头插法插入节点
*参 数:
* pHead:链表头节点地址
* pNewNode:要插入的数据节点地址
*返回值:
* 成功返回0
* 失败返回-1
******************************************************/
int InsertHeadKerList(LinkNode *pHead, LinkNode *pNewNode)
{
if (NULL == pNewNode || NULL == pHead)
{
return -1;
}
pNewNode->pnext = pHead->pnext;
pHead->pnext = pNewNode;
return 0;
}
/*******************************************************
*函数名: InsertTailKerList
*功 能:
* 尾插法插入节点
*参 数:
* pHead:链表头节点地址
* pNewNode:要插入的数据节点地址
*返回值:
* 成功返回0
* 失败返回-1
******************************************************/
int InsertTailKerList(LinkNode *pHead, void *pNewNode)
{
LinkNode *pTmpNode = NULL;
if (NULL == pNewNode || NULL == pHead)
{
return -1;
}
pTmpNode = pHead;
while (pTmpNode->pnext != NULL)
{
pTmpNode = pTmpNode->pnext;
}
((LinkNode *)pNewNode)->pnext = NULL;
pTmpNode->pnext = pNewNode;
return 0;
}
/*******************************************************
*函数名: ForeachKerList
*功 能:
* 遍历链表中每个节点元素
*参 数:
* pHead:链表头节点地址
* pfun:对每个链表节点所做的操作
*返回值:
* 成功返回0
* 失败返回-1
******************************************************/
int ForeachKerList(LinkNode *pHead, int (*pfun)(LinkNode *pTmpNode))
{
LinkNode *pTmpNode = NULL;
int ret = 0;
pTmpNode = pHead->pnext;
while (pTmpNode != NULL)
{
ret = pfun(pTmpNode);
if (-1 == ret)
{
return -1;
}
pTmpNode = pTmpNode->pnext;
}
return 0;
}
/*******************************************************
*函数名: SearchKerList
*功 能:
* 查找链表中的元素
*参 数:
* pHead:链表头节点地址
* pcmpfun:匹配条件
*返回值:
* 成功返回找到节点的地址
* 失败返回NULL
******************************************************/
LinkNode *SearchKerList(LinkNode *pHead, int (*pcmpfun)(LinkNode *pTmpNode))
{
LinkNode *pTmpNode = NULL;
pTmpNode = pHead->pnext;
while (pTmpNode != NULL)
{
if (pcmpfun(pTmpNode))
{
return pTmpNode;
}
pTmpNode = pTmpNode->pnext;
}
return NULL;
}
/*******************************************************
*函数名: DestroyKerList
*功 能:
* 销毁内核链表
*参 数:
* pHead:链表头节点地址
*返回值:
* 成功返回0
* 失败返回-1
******************************************************/
int DestroyKerList(LinkNode **pHead)
{
if (*pHead != NULL)
{
free(*pHead);//只需要销毁头节点,因为后面的是用户自己创建的需要用户自己去销毁
}
*pHead = NULL;
return 0;
}
#include "kerlist.h"
#include <stdio.h>
typedef struct data
{
LinkNode node;
int data;
}datatype;
int PrintData(LinkNode *pTmpNode)
{
datatype *pTmp = (datatype *)pTmpNode;//将pTmpNode强制类型转换并赋值给pTmp
printf("%d ", pTmp->data);
return 0;
}
int compare(LinkNode *pTmpNode)
{
datatype *ptmp = (datatype *)pTmpNode;
if (ptmp->data == 2)
{
return 1;
}
return 0;
}
int main(void)
{
LinkNode *phead = NULL;
LinkNode *pnode = NULL;
datatype data[10];
int i = 0;
phead = CreateKerList();
for (i = 0; i < 10; i++)
{
data[i].data = i+1;
InsertHeadKerList(phead, (LinkNode *)&data[i]);
}
ForeachKerList(phead, PrintData);
pnode = SearchKerList(phead, compare);
printf("pnode->data: %d\n", ((datatype *)pnode)->data);
DestroyKerList(&phead);
return 0;
}
#include "kerlist.h"
#include <stdio.h>
typedef struct student
{
LinkNode node;
char name[32];
char sex;
int age;
int score;
}stu_t;
int PrintStuInfo(void *pdata)
{
stu_t *ptmp = pdata;
printf("姓名:%s\n", ptmp->name);
printf("性别:%c\n", ptmp->sex);
printf("年龄:%d\n", ptmp->age);
printf("成绩:%d\n", ptmp->score);
return 0;
}
int compare(void *pnode)
{
stu_t *pstu = pnode;
if (pstu->score > 90)
{
return 1;
}
return 0;
}
int main(void)
{
stu_t a = {
{NULL}, "zhangsan", 'm', 19, 100};
stu_t b = {
{NULL}, "lisi", 'm', 19, 70};
stu_t c = {
{NULL}, "wanger", 'f', 19, 90};
LinkNode *pHead = NULL;
LinkNode *pTmpNode = NULL;
pHead = CreateKerList();
InsertTailKerList(pHead, &a);
InsertTailKerList(pHead, &b);
InsertTailKerList(pHead, &c);
ForeachKerList(pHead, PrintStuInfo);
pTmpNode = SearchKerList(pHead, compare);
printf("%s\n", ((stu_t *)pTmpNode)->name);
DestroyKerList(&pHead);
return 0;
}