ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

C指针之六:指针和结构体

2019-07-11 14:04:14  阅读:279  来源: 互联网

标签:NULL void pRoot 之六 pData pNode Employee 指针 结构



#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;
}

 

标签:NULL,void,pRoot,之六,pData,pNode,Employee,指针,结构
来源: https://blog.csdn.net/Rbaggio92/article/details/95472080

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有