#include<iostream>
#include <windows.h>
using namespace std;
/************************************************************************/
/* 指针和结构体 */
/************************************************************************/
/*
指针和结构体 ,本篇目录
一、介绍
二、结构体释放问题
三、避免malloc/free开销
四、用指针支持数据结构
五、小结
*/
/*
一、介绍
本章重点:
1、结构体声明,
2、结构体访问
3、结构体内存
*/
/*
声明结构体的两个主要方式:
1、用struct关键字声明
*/
/*
struct _person
{
char * pszFirstName;
char* pszLastName;
char* pszTitle;
unsigned int iAge;
} stPerson;
*/
//2、用struct关键字
typedef struct _person
{
char * pszFirstName;
char* pszLastName;
char* pszTitle;
unsigned int iAge;
}Person;
/************************************************************************/
/* 关于结构体示例的声明 */
/************************************************************************/
// person的示例声明如下
Person person;
// 也可以这样声明一个指针,然后分配内存
Person *ptrPerson =(Person*)malloc(sizeof(Person));
/* 访问方式主要有三个,结构体变量一个,结构体指针两个
1、结构体变量
2、结构体指针
3、结构体指针解引用
*/
void VisitStructWay()
{
// 1、结构体变量
person.pszFirstName = (char*)malloc(strlen("Emily") + 1);
strcpy(person.pszFirstName, "Emily");
person.iAge = 23;
// 2、结构体指针
ptrPerson->pszFirstName = (char*)malloc(strlen("Emily") + 1);
strcpy(ptrPerson->pszFirstName, "Emily");
ptrPerson->iAge = 23;
// 3、结构体指针解引
(*ptrPerson).pszFirstName = (char*)malloc(strlen("Emily") + 1);
strcpy((*ptrPerson).pszFirstName, "Emily");
(*ptrPerson).iAge = 23;
}
/*
为结构体分配内存
涉及到内存对齐的问题,后面详细介绍
*/
/************************************************************************/
/* 二、结构体释放问题 */
/************************************************************************/
// 下面这个函数在结构体的初始化阶段,会为每个字段赋值,对于指针字段,我们
//会从堆上分配内存,并把地址赋值给每个指针
void initializePerson(Person *ptrPerson,const char* pszFn,const char* pszLn,
const char* pszTitle,unsigned int iAge)
{
ptrPerson->pszFirstName = (char*)malloc(strlen(pszFn) + 1);
strcpy(ptrPerson->pszFirstName, pszFn);
ptrPerson->pszLastName = (char*)malloc(strlen(pszLn) + 1);
strcpy(ptrPerson->pszLastName, pszLn);
ptrPerson->pszTitle = (char*)malloc(strlen(pszTitle) + 1);
strcpy(ptrPerson->pszTitle, pszTitle);
ptrPerson->iAge = iAge;
}
// 释放内存
void deallocatePerson(Person *ptrPerson)
{
free(ptrPerson->pszFirstName);
free(ptrPerson->pszLastName);
free(ptrPerson->pszTitle);
}
// 下面这个函数包含了结构体指针的初始化和释放
void processPerson()
{
Person *ptrPerson;
ptrPerson = (Person *)malloc(sizeof(ptrPerson));
initializePerson(ptrPerson,"Peter","UNderwood","Manager",36);
deallocatePerson(ptrPerson);
free(ptrPerson);
}
/************************************************************************/
/* 三、避免malloc/free开销 */
/************************************************************************/
/*
重复分配然后释放结构体会产生一些开销,可能会导致巨大的性能瓶颈。
解决这个问题的技术思想是:
为分配的结构体单独维护一个表,当用户不再需要某个结构体实例的时候,将其返回到结构体池中,
当我们需要某个实例的时候,从结构体池中获取一个对象。如果池中没有可用的元素,我们就动态的
分配一个实例。
这种方法的好处是能够高效地维护一个结构体池,能够按需使用和重复使用内存
*/
// 下面是实现方法
#define D_LIST_SIZE 10
Person *list[D_LIST_SIZE];
// 使用之前先初始化
void initializeList()
{
for(int i = 0;i<D_LIST_SIZE;i++)
{
list[i] = NULL;
}
}
// 下面用两个函数来添加和获取结构体
// 这个函数获取一个非空的结构体
Person *getPerson()
{
for(int i = 0;i < D_LIST_SIZE; i++)
{
if(NULL != list[i])
{
Person *ptr = list[i];
list[i] = NULL;
return ptr; // 返回非空的结构体
}
}
Person *ptrPerson = (Person *)malloc(sizeof(Person));
return ptrPerson;
}
// 这个函数要么将结构体返回表,要么把结构体释放掉
Person *returnPerson(Person *ptrPerson)
{
for(int i = 0; i < D_LIST_SIZE; i++)
{
if(NULL == list[i])
{
list[i] = ptrPerson;
return ptrPerson;
}
}
deallocatePerson(ptrPerson);
free(ptrPerson);
ptrPerson = NULL;
return NULL;
}
// 下面函数说明了表的初始化,以及如何将一个结构体添加到表中
void TestStructPool()
{
initializeList();
Person *ptrPerson;
ptrPerson = getPerson();
initializePerson(ptrPerson, "Mark", "Kevin", "Smith", 35);
returnPerson(ptrPerson);
}
/*
结构体池,有个问题,就是表长度,如果表太短,就需要频繁分配并释放内存
如果表太而没有使用结构体,那么就会浪费大量内存,也不能用在其它地方。
管理表的长度,可以用更复杂的表管理策略来做
*/
/************************************************************************/
/* 四、用指针支持数据结构
/************************************************************************/
/*
本章分为四个部分
1、单链表
2、用指针支持队列
3、用指针支持栈
4、用指针支持树
指针的便利性来自于两个方面:1、动态内存分配,2、自动切换指针引用的便利性
链表是非常有用的数据结构,可以作为实现队列和栈的基础
下面用两个函数和两个函数指针说明下
*/
typedef struct _employee
{
char name[32];
unsigned char age;
}Employee;
// 比较
int CompareEmployee(Employee* ptrEm1, Employee* ptrEm2)
{
int iSize = strcmp(ptrEm1->name, ptrEm2->name);
return iSize;
}
// 输出
void displayEmployee(Employee* ptrEmployee)
{
printf("%s\t%d\n",ptrEmployee->name,ptrEmployee->age);
}
typedef void(*DISPLAY)(void * );
typedef int(*COMPARE)(void* , void*);
void TestPtrStruct()
{
Employee em1;
Employee em2;
strcpy(em1.name, "KAKA");
em1.age = 22;
strcpy(em2.name, "KAKA");
em2.age = 22;
int iResult = CompareEmployee(&em1, &em2);
cout << "iResult = "<< iResult<< endl;
displayEmployee(&em1);
}
//(一)、单链表
/*
链表有很多类型,最简单的是单链表,一个节点到下一个节点只有一个链接
链接从头结点开始,到尾节点结束
循环链表没有尾节点,链表的最后一个节点又指向头节点
双链表用了两个链表,一个向前链接,一个向后链接
下面介绍单链表
*/
// 节点结构体
typedef struct _node
{
void *data; // 持有任意类型数据
struct _node *next; // 指向下一节点的指针
}Node;
// 链表结构体
typedef struct _linkedList
{
Node *pHead;
Node *pTail;
Node *pCurrent;
} LinkedList;
// 初始化链表
void initializeList(LinkedList *pList);
// 给链表头结点添加数据
void addHead(LinkedList *pList,void* pData);
// 给链表尾节点添加数据
void addTail(LinkedList *pList, void* pData);
// 从链表删除节点
void deleteNode(LinkedList *pList, Node* pNode);
// 返回包含指定数据的节点指针,找到指定节点
Node *getNode(LinkedList *pList,COMPARE,void* pData);
// 打印链表
void displayLinkedList(LinkedList *pList,DISPLAY);
void initializeList(LinkedList *pList)
{
pList->pHead = NULL;
pList->pTail = NULL;
pList->pCurrent = NULL;
}
void addHead(LinkedList *pList, void* pData)
{
Node *pNode = (Node*)malloc(sizeof(Node));
pNode->data = pData;
if(NULL == pList->pHead)
{
pList->pTail = pNode;
pNode->next = NULL;
}
else
{
pNode->next = pList->pHead;
}
pList->pHead = pNode;
}
void addTail(LinkedList *pList, void* pData)
{
Node *pNode = (Node*)malloc(sizeof(Node));
pNode->data = pData;
pNode->next = NULL;
if(NULL == pList->pHead)
{
pList->pHead = pNode;
}
else
{
pList->pTail->next = pNode;
}
pList->pTail = pNode;
}
void deleteNode(LinkedList *pList, Node* pNode)
{
if(pNode == pList->pHead)
{
if(NULL == pList->pHead->next)
{
pList->pHead = pList->pTail = NULL;
}
else
{
pList->pHead = pList->pHead->next;
}
}
else
{
Node *pTemp = pList->pHead;
while(pTemp != NULL && pTemp->next != pNode)
{
pTemp = pTemp->next;
}
if(pTemp != NULL)
{
pTemp->next = pNode->next;
}
}
free(pNode);
}
Node * getNode(LinkedList *pList, COMPARE compare, void* pData)
{
Node *pNode = pList->pHead;
while(NULL != pNode)
{
if(0 == compare(pNode->data,pData))
{
return pNode;
}
pNode = pNode->next;
}
return NULL;
}
void displayLinkedList(LinkedList *pList, DISPLAY display)
{
printf("\nLinked List\n");
Node *pCurrent = pList->pHead;
while(NULL != pCurrent)
{
display(pCurrent->data);
pCurrent = pCurrent->next;
}
}
// 链表测试
void TestLink()
{
LinkedList pLinkedList;
Employee *pSamuel = (Employee *)malloc(sizeof(Employee));
strcpy(pSamuel->name, "Samuel");
pSamuel->age = 32;
Employee *pSally = (Employee *)malloc(sizeof(Employee));
strcpy(pSally->name, "Sally");
pSally->age = 22;
Employee *pSusan = (Employee*)malloc(sizeof(Employee));
strcpy(pSusan->name, "Susan");
pSusan->age = 24;
initializeList(&pLinkedList);
addHead(&pLinkedList, pSamuel);
addHead(&pLinkedList, pSally);
addHead(&pLinkedList, pSusan);
Node *pNode = getNode(&pLinkedList, (int(*)(void*, void*))CompareEmployee, pSally);
cout << pNode->data<< endl;
cout << "Test Show"<< endl;
displayLinkedList(&pLinkedList,(DISPLAY)displayEmployee);
deleteNode(&pLinkedList,pNode);
}
/*
(二)、队列
队列的基本操作有两种,enqueue(入队),它是在表的末端(称为队尾)插入一个元素;
dequeue(出队),它是删除(并返回)表的开头的元素
先入先出的数据结构
实现队列经常用到链表,入队操作就是将节点添加链表一端,出队操作就是从链表另一端取节点
,并删除节点
(下面这个实现不是很好)
*/
typedef LinkedList Queue;
// 初始化队列
void initializeQueue(Queue * pQueue)
{
initializeList(pQueue);
}
// 向队列增加一个节点
void enQueue(Queue * pQueue,void *pNode)
{
addHead(pQueue, pNode);
}
// 从队列中取数据,并从队列中删除这个数据
void *deQueue(Queue *pQueue)
{
Node *tmp = pQueue->pHead; // 头指针
void * pData;
if(NULL == pQueue->pHead)
{
pData = NULL;
}
else if (pQueue->pHead == pQueue->pTail)
{
pQueue->pHead = pQueue->pTail = NULL;
pData = tmp->data;
free(tmp);
}
else
{
while(tmp->next != pQueue->pTail)
{
tmp = tmp->next;
}
}
pQueue->pTail = tmp;
tmp = tmp->next;
pQueue->pTail->next = NULL;
pData = tmp->data;
return pData;
free(tmp);
}
// 队列测试方法
void TestQueue()
{
Employee *pJeff = (Employee *)malloc(sizeof(Employee));
strcpy(pJeff->name, "Jeff");
pJeff->age = 32;
Employee *pJames = (Employee *)malloc(sizeof(Employee));
strcpy(pJames->name, "James");
pJames->age = 22;
Employee *pMark = (Employee*)malloc(sizeof(Employee));
strcpy(pMark->name, "Mark");
pMark->age = 24;
Queue qQueueTest;
initializeList(&qQueueTest);
enQueue(&qQueueTest, pJeff);
enQueue(&qQueueTest, pJames);
enQueue(&qQueueTest, pMark);
void *pData = deQueue(&qQueueTest);
char name[20];
strcpy(name, ((Employee*)pData)->name);
cout << name<< endl;
}
/*
(三)、 栈
堆栈的基本操作是push(进栈)和push(出栈),前者相当于插入,后者这是删除最后插入的元素
栈的行为是先进后出
栈同样可以用链表支持操作
*/
typedef LinkedList Stack;
// 初始化栈
void initializeStack(Stack *pStack)
{
initializeList(pStack);
}
// 入栈
void push(Stack *pStack,void *pData)
{
addHead(pStack, pData);
}
/*
出栈,考虑三种情况:
1、栈为空,返回NUNULL
2、栈中有一个元素,如果节点指向尾节点,那么头结点和尾节点是同一个元素。将头结点
和尾节点置为NULL。然后返回数据
3、栈中有多个元素
头结点赋值为链表的下一个元素,然后返回数据
*/
void *pop(Stack *pStack)
{
Node * pNode = pStack->pHead;
if(NULL ==pNode)
{
return NULL;
}
else if(pNode == pStack->pTail)
{
pStack->pHead = pStack->pTail = NULL;
void * pData = pNode->data;
free(pNode);
return pData;
}
else
{
pStack->pHead = pStack->pHead->next;
void * pData = pNode->data;
free(pNode);
return pData;
}
}
void TestStack()
{
Employee *pJeff = (Employee *)malloc(sizeof(Employee));
strcpy(pJeff->name, "Jeff");
pJeff->age = 32;
Employee *pJames = (Employee *)malloc(sizeof(Employee));
strcpy(pJames->name, "James");
pJames->age = 22;
Employee *pMark = (Employee*)malloc(sizeof(Employee));
strcpy(pMark->name, "Mark");
pMark->age = 24;
Stack qStackTest;
initializeStack(&qStackTest);
push(&qStackTest, pJeff);
push(&qStackTest, pJames);
push(&qStackTest, pMark);
Employee *employee;
for(int i = 0;i<4;i++)
{
employee = (Employee*)pop(&qStackTest);
printf("Popped %s\n",employee->name );
}
}
/*
(四)、用指针支持栈
*/
typedef struct _tree
{
void *pData;
struct _tree *pLeft;
struct _tree *pRight;
} TreeNode;
void insertNode(TreeNode **pRoot,COMPARE compare ,void *pData)
{
TreeNode *pNode = (TreeNode*)malloc(sizeof(TreeNode));
pNode->pData = pData;
pNode->pLeft = NULL;
pNode->pRight = NULL;
if(NULL == *pRoot)
{
*pRoot = pNode;
return;
}
while(1)
{
if(compare((*pRoot)->pData,pData)>0)
{
if(NULL != (*pRoot)->pLeft) // 左节点有值
{
*pRoot = (*pRoot)->pLeft;
}
else
{
(*pRoot)->pLeft = pNode;
break;
}
// *pRoot = (*pRoot).pLeft;
}
else
{
if(NULL != (*pRoot)->pRight) // 右节点有值
{
*pRoot = (*pRoot)->pRight;
}
else
{
//(*pRoot) = (*pRoot)->pRight;
(*pRoot)->pRight = pNode;
break;
}
}
}
}
/*
遍历二叉树的方式有三种:前序、中序、后序。
三种遍历方式的技术步骤相同,但是顺序不同
三个步骤:访问节点、往左、往右
访问节点的三种顺序:
中序、先往左,访问节点,再往右
前序、访问节点,往左,再往右
后序、先往左,再往右,最后访问节点
*/
// 前序
void preOrder(TreeNode *pRoot, DISPLAY display)
{
if(NULL != pRoot)
{
display(pRoot->pData);
preOrder(pRoot->pLeft, display);
preOrder(pRoot->pRight, display);
}
}
// 中序;
void inOrder(TreeNode *pRoot,DISPLAY display)
{
if(NULL != pRoot)
{
inOrder(pRoot->pLeft, display);
display(pRoot->pData);
inOrder(pRoot->pRight, display);
}
}
// 后序
void postOrder(TreeNode *pRoot, DISPLAY display)
{
if(NULL != pRoot)
{
postOrder(pRoot->pLeft, display);
postOrder(pRoot->pRight, display);
display(pRoot->pData);
}
}
void TestTree()
{
Employee *pSamuel = (Employee *)malloc(sizeof(Employee));
strcpy(pSamuel->name, "Samuel");
pSamuel->age = 32;
Employee *pSally = (Employee *)malloc(sizeof(Employee));
strcpy(pSally->name, "Sally");
pSally->age = 28;
Employee *pSusan = (Employee*)malloc(sizeof(Employee));
strcpy(pSusan->name, "Susan");
pSusan->age = 45;
TreeNode *pTree = NULL;
insertNode(&pTree, (COMPARE) CompareEmployee, pSamuel);
insertNode(&pTree, (COMPARE)CompareEmployee, pSally);
insertNode(&pTree, (COMPARE)CompareEmployee, pSusan);
// 遍历
preOrder(pTree, (DISPLAY)displayEmployee);
inOrder(pTree, (DISPLAY)displayEmployee);
postOrder(pTree, (DISPLAY)displayEmployee);
}
int main()
{
TestTree();
//TestStack();
//TestQueue();
//TestLink();
//TestPtrStruct();
//TestStructPool();
// const char* pszName = "KAKA";
//
// strcpy(pszName, "JAM");
system("pause");
return 0;
}