【C语言->数据结构与算法】->线性表->线性表工具库的创建

在做线性表工具之前,先明确何为线性表。

线性表性质:

1.多个数据,有且仅有一个数据没有前件;
2.多个数据,有且仅有一个数据没有后件;
3.其他数据,有且仅有一个前件和一个后件;
4.在任意删除或增加一个数据后,剩余数据仍然满足以上三个条件。

线性表的表示:

*1.可以用线性存储结构表示: 数组
2.可以用非线性存储结构表示:链表 *

在这里我们使用数组实现。
但是类似于int arr[10],是一个线性表吗?
答案是 是,但并不具有工具性。 所谓工具性是指,能处理各种类型的数据,不能只处理int类型的。
工具性也指,应该提供一整套数据结构(数据类型)和操作(函数),使得在使用这个工具时,不再额外编写关于工具的整理、规范等等有关工具管理的代码。

线性表工具实现的分析:
1.这个线性表应该有一段数据存储空间
2.这个空间应该存在一系列有关这个空间的管理的数据
比如:空间容量(capacity),有效元素个数(count)。根据capacity和count,可以判断线性表的空和满。
所以我们需要建立一个表头,来控制整个线性表。

以下为linear.h头文件中的内容

#ifndef _TYZ_LINEAR_H_
#define _TYZ_LINEAR_H_

typedef struct LINEAR {
	USER_TYPE *data;
	int capacity;
	int count;
}LINEAR;

#endif

现在我们需要解决USER_TYPE类型的问题,如何实现让用户能存储任何类型的数据呢?
在此我给出第一种方法,下一个方法会在我的堆栈工具中实现。

我们只需要再定义一个开放给用户的头文件,即userType.h,在里面用户可以根据自己的需要而改变USER_TYPE的类型。
以下为userType.h的代码。

#ifndef _USER_TYPE_H_
#define _USER_TYPE_H_

typedef int USER_TYPE;  //用户在此可以更改需要的数据类型

#endif

在此我们先将USER_TYPE定义为int类型。
解决了这个问题,我们可以开始着手做线性表工具了。

我们之前定义了一个表头,表头里还有个指向数据的指针,因此我们需要先申请空间给表头及数据,所以第一个函数我们需要写初始化线性表的函数。

#include 
#include 

#include "userType.h"
#include "tyz.h"
#include "linear.h"

boolean initLinear(LINEAR **head, int capacity) {
	if (NULL != *head) {
		return FALSE;
	}

	*head = (LINEAR *) calloc(sizeof(LINEAR),1);
	(*head)->capacity = capacity;
	(*head)->count = 0;
	(*head)->data = (USER_TYPE *) calloc(sizeof(USER_TYPE),capacity);

	return TRUE;
}

//boolean本质的类型是unsigned cha,其定义在我编写的tyz.h的头文件中

以下为tyz.h中的代码

#ifndef _TYZ_H_
#define _TYZ_H_

#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1

typedef unsigned char boolean;

int skipBlank(const char *str);
boolean isRealStart(int ch);


#endif

如果 *head == NULL,说明该线性表没有被初始化过,这个判断是为了防止用户对同一个线性表进行重复初始化,造成内存泄漏。我们编写工具的时候,一定要把用户当成敌人,尽可能考虑到所有他有可能摧毁我们程序的操作。

编写完申请空间,接下来一定要考虑释放空间,内存泄漏是编程的大忌
以下为销毁线性表的函数,即释放所申请的空间。

void destoryLinear(LINEAR **head) {
	if (NULL == head || NULL == *head) {
		return;
	}

	free((*head)->data);
	free(*head);

	*head = NULL;
}

销毁函数不需要知道它有没有成功,所以返回值类型用void.

现在我们从最简单的开始,先编写判断线性表空与满的函数。我们之前分析的时候说过,要判断空和满,需要根据capacity和count来判断,所以在判断之前,我们需要得到线性表的capacity和count。

int getCapacity(LINEAR *head) {
	return head->capacity;
}

int getCount(LINEAR *head) {
	return head->count;
}

得到了capacity和count,我们可以进行空和满的判断了。

boolean isLinearFull(LINEAR *head) {
	return head->count >= head->capacity;
}

boolean isLinearEmpty(LINEAR *head) {
	return head->count <= 0;
}

接下来我们需要完善插入、修改、追加、删除等基本操作,我们先完成修改操作。

boolean setElementAt(LINEAR *head, int index, USER_TYPE data) {
	if (NULL == head || index < 0 || index >= head->count) {
		return FALSE;
	}

	head->data[index] = data;

	return TRUE;
}

修改的函数便完成了。在完成其他操作之前,我们可以先做一个将元素取出的函数。

boolean getElement(LINEAR *head, int index, USER_TYPE *data) {
	if (NULL == head || index < 0 || index >= head->count) {
		return FALSE;
	}

	*data = head->data[index];

	return TRUE;
}

接下来需要完成插入的函数。

boolean insertElementAt(LINEAR *head, int index, USER_TYPE data) {
	int i;

	if (NULL == head || isLinearFull(head)
	     || index < 0 || index >= head->count) {
		return FALSE;
	}

	for (i = head->count; i > index; i--) {
		head->data[i] = head->data[i - 1];
	}
	head->data[index] = data;
	++head->count;

	return TRUE;
}

只有线性表没有满的时候,才能插入元素,需要注意这个判断。插入元素后,一定要记得将count++,否则访问会出错。
完成了插入的函数,末尾追加的函数就很简单了。

boolean appendElement(LINEAR *head, USER_TYPE data) {
	return insertElementAt(head, head->count, data);
}

最后进行删除函数的编写。

boolean removeElement(LINEAR *head, int index, USER_TYPE *data) {
	if (NULL == head || isLinearEmpty(head)
		 || index < 0 || index >= head->count) {
		return FALSE;
	}
	*data = head->data[index];

	for (; index < head->count; index++) {
		head->data[index] = head->data[index + 1];
	}
	head->count--;

	return TRUE;
}

*data可以将要删除的元素保存,让用户知道删除了哪个元素。

接下来还需要一个清空线性表的功能,即用户想重新录入数据,之前的数据不需要了,此时需要一个清空的功能。

void clearLinear(LINEAR *head) {
	head->count = 0;
}

我们只需要将count变成0,就已经完成了。之前的数据会变成垃圾数据被覆盖,不会再访问到。这同时也是其他电子设备删除信息的原理,你的信息并没有被移除,只是你无法访问到,它们变成了垃圾数据而已。

最后,我们需要一个将元素下标取出的函数。这个函数将会引出一个重要的知识点,即指向函数的指针

为什么需要指向函数的指针呢?

在开始我们用USER_TYPE定义了数据类型,并且这个数据类型是由用户决定的,我们并不知道它是什么类型的数据,所以我们用的比较元素的方法可能会造成语法错误。在找出我们想要的元素的下标时,我们需要将两个数据进行相等比较,但是数组和数组,结构体和结构体单纯的比较都可能会造成错误,所以我们是无法进行元素的相等比较的。
由此我们可以定义一个指向函数的指针,这个指针指向用户定义的相等比较的函数,也就是这个指针是指向未来的
以下为提取元素下标的函数

int getIndexOfElement(LINEAR *head, USER_TYPE data,
		boolean (*eq)(USER_TYPE,USER_TYPE)) {
	int index;

	if (NULL == head || isLinearEmpty(head)) {
		return NOT_FOUND;
	}

	for (index = 0; index < count; index++) {
		if (eq(head->data[index],data)) {
			return index;
		}
	}

	return NOT_FOUND;
}

其中,boolean (*eq)(USER_TYPE,USER_TYPE) 定义了一个指向函数的指针,即eq。函数的名称本质上其实是一个指令的首地址,在我用汇编代码分析函数调用的博客中会详细讲解。

eq指向了一个返回值为boolean, 参数类型为(USER_TYPE,USER_TYPE)的函数,该函数将由使用该工具的用户进行编写,根据用户需要的数据类型,编写适用的函数。用户定义的函数返回值类型必须是boolean,参数必须是(USER_TYPE,USER_TYPE)。

关于eq,可以这样理解: boolean((USER_TYPE,USER_TYPE)) *eq
我们进行函数声明的时候,格式是 返回值类型 函数名称(形参),比如
void clearLinear(LINEAR *head) ,定义了一个函数名称为clearLinear,返回值类型为void,参数为(LINEAR *head)的函数,根据这个推导,可以更好地理解指向函数的指针。

线性表的完整代码如下:
1.linear.h

#ifndef _TYZ_LINEAR_H_
#define _TYZ_LINEAR_H_

#include "tyz.h"
#include "userType.h"

typedef struct LINEAR {
	USER_TYPE *data;
	int capacity;
	int count;
}LINEAR;

boolean initLinear(LINEAR **head, int capacity);
void destoryLinear(LINEAR **head);
int getCapacity(LINEAR *head);
int getCount(LINEAR *head);
boolean isLinearFull(LINEAR *head);
boolean isLinearEmpty(LINEAR *head);
boolean setElementAt(LINEAR *head, int index, USER_TYPE data);
boolean getElement(LINEAR *head, int index, USER_TYPE *data);
boolean insertElementAt(LINEAR *head, int index, USER_TYPE data);
boolean appendElement(LINEAR *head, USER_TYPE data);
boolean removeElement(LINEAR *head, int index, USER_TYPE *data);
void clearLinear(LINEAR *head);
int getIndexOfElement(LINEAR *head, USER_TYPE data,
	boolean (*eq)(USER_TYPE,USER_TYPE));

#endif

2.userType.h

#ifndef _USER_TYPE_H_
#define _USER_TYPE_H_

typedef int USER_TYPE;  //用户在此可以更改需要的数据类型

#endif

3.tyz.h

#ifndef _TYZ_H_
#define _TYZ_H_

#define TRUE 1
#define FALSE 0
#define NOT_FOUND -1

typedef unsigned char boolean;

int skipBlank(const char *str);
boolean isRealStart(int ch);


#endif

4.linear.c

#include 
#include 

#include "userType.h"
#include "tyz.h"
#include "linear.h"

boolean initLinear(LINEAR **head, int capacity) {
	if (NULL != *head) {
		return FALSE;
	}

	*head = (LINEAR *) calloc(sizeof(LINEAR),1);
	(*head)->capacity = capacity;
	(*head)->count = 0;
	(*head)->data = (USER_TYPE *) calloc(sizeof(USER_TYPE),capacity);

	return TRUE;
}

void destoryLinear(LINEAR **head) {
	if (NULL == head || NULL == *head) {
		return;
	}

	free((*head)->data);
	free(*head);

	*head = NULL;
}

int getCapacity(LINEAR *head) {
	return head->capacity;
}

int getCount(LINEAR *head) {
	return head->count;
}

boolean isLinearFull(LINEAR *head) {
	return head->count >= head->capacity;
}

boolean isLinearEmpty(LINEAR *head) {
	return head->count <= 0;
}

boolean setElementAt(LINEAR *head, int index, USER_TYPE data) {
	if (NULL == head || index < 0 || index >= head->count) {
		return FALSE;
	}

	head->data[index] = data;

	return TRUE;
}

boolean getElement(LINEAR *head, int index, USER_TYPE *data) {
	if (NULL == head || index < 0 || index >= head->count) {
		return FALSE;
	}

	*data = head->data[index];

	return TRUE;
}

boolean insertElementAt(LINEAR *head, int index, USER_TYPE data) {
	int i;

	if (NULL == head || isLinearFull(head)
	     || index < 0 || index >= head->count) {
		return FALSE;
	}

	for (i = head->count; i > index; i--) {
		head->data[i] = head->data[i - 1];
	}
	head->data[index] = data;
	++head->count;

	return TRUE;
}

boolean appendElement(LINEAR *head, USER_TYPE data) {
	return insertElementAt(head, head->count, data);
}

boolean removeElement(LINEAR *head, int index, USER_TYPE *data) {
	if (NULL == head || isLinearEmpty(head)
		 || index < 0 || index >= head->count) {
		return FALSE;
	}
	*data = head->data[index];

	for (; index < head->count; index++) {
		head->data[index] = head->data[index + 1];
	}
	head->count--;

	return TRUE;
}

void clearLinear(LINEAR *head) {
	head->count = 0;
}

int getIndexOfElement(LINEAR *head, USER_TYPE data,
		boolean (*eq)(USER_TYPE,USER_TYPE)) {
	int index;

	if (NULL == head || isLinearEmpty(head)) {
		return NOT_FOUND;
	}

	for (index = 0; index < count; index++) {
		if (eq(head->data[index],data)) {
			return index;
		}
	}

	return NOT_FOUND;
}

当用户使用时,我们只需要提供给用户linear.h,userType.h,tyz.h三个头文件以及编译后的linear.o文件,用户就可以使用了,不用担心我们的工具代码会被用户修改。
经PowerShell编译验证,无错误。
【C语言->数据结构与算法】->线性表->线性表工具库的创建_第1张图片

你可能感兴趣的:(数据结构与算法,线性表工具库的创建,消费未来-指向函数的指针)