这相当于一种数据管理,你通过采集获得了一堆数据,这些数据你该怎么处理,提取出你需要的信息。
线性表就是其中的一种管理数据的形式:N个有相同特性的数据元素的有限序列。
(例如:顺序表(数组),链表(指针),栈,队列,字符串等)
从物理地址头开始连续的存储单元。
#define N 100
#typedef int SLDataType
typedef struct Seqlist
{
SLDataType a[N];
int size;
}SL;
这很像以前学的结构体,这样的信息汇总才更方便处理数据。
其中令人学习的点:
1)除了数据参数化,数据类型是不是也可以参数化?我可以用SLDataType 去选择我需要的数据类型。
这些天在一家公司的技术部里经常听到“接口”这个词,现在大体明白所谓接口函数,是你开发出来了功能后让客户去使用的地方就是接口。你输入数据,得到你想要的结果。
程序代码就是为了完成需求的。
1)客户想给数据表后端添加数据:
void SeqlistPushBack(SL* p, SLDatatype x);//程序员就给你开发这个功能,然后给你这个数据x的接口去输入数据x
按需分配是首选的。
#define N 100
#typedef int SLDataType
typedef struct Seqlist
{
SLDataType* a; 指定动态分配的起始地址
int size;
int capacity; 没有固定的内存大小,需要有个变量存储容量大小
}SL;
项目文件结构:
1)头文件定义一个顺序表(结构体)
2)test.c测试文件定义一个顺序表变量,并把顺序表起始地址传给定义文件
(我之前写的时候是把顺序表变量写在了定义文件中,现在回想,确实该写在test.c测试文件里(你要测试这个变量及其功能是否能实现),实现功能的定义文件只需要实现功能,你的顺序表变量相当于客户的需求(客户想改变修改他们给的变量))
3)Seqlist.c定义文件使用test.c传来的地址实现功能。
测试文件中,当遇到接口函数较多时,要单独写出测试函数分批次测试(算是个好习惯)
(说实话,这些小细节的好习惯能有人讲一下是真的好,回想我的学习经历,能把这些经验教授予人的很少,要么是自己悟,要么别人不屑或是不想讲给你)
例如:
void TestSeqlist1()
{
接口函数1;
接口函数2;
}
1)顺序表本身是空的
2)你在执行操作后,内存空间不够了(改变了空间就需要考虑)
3)空间是足够的
if (p->size == p->capacity)
{
int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;空的置4,不够翻倍
p->capacity = newcapacity;
SeqDataType* tmp = (SeqDataType*)realloc(p->a, p->capacity * sizeof(SeqDataType));
if (tmp == 0) 开辟空间成功否?
{
printf("error");
exit(-1);
}
p->a = tmp; 成功传给a
}
上述值得学习的几点:
1)int newcapacity = p->capacity == 0 ? 4 : p->capacity * 2;
这句话就体现了代替if的“? :”表达式的优点,对这种一句话里的判断简洁明了。
除此,容量成倍增长,这种思路在TCP的congestion control里见过,指数增到collusion发生,再对半减,再加法增去尽快获得最恰当的大小。(这算算法吗?似乎是的,咱们作为使用者看起来没啥,从0到1又谈何容易?)(我个人极其反感中英文夹杂表达)
2)SeqDataType* tmp = (SeqDataType*)realloc(p->a, p->capacity * sizeof(SeqDataType));
最开始,我在想为什么不直接用“p->a”来开辟一段动态内存,反而用tmp来开辟。因为如果开辟失败了,p->a=NULL;你就直接丢失了原来的地址,这太可怕了,回不去了。所以要用tmp一个临时变量来试错,确认没错,再传给p->a。
(这里利用了realooc的memblock为Null等同于malloc的特性)
3)exit(-1)就是退出,之前是perror()。