C primer快看完了,今天自己试着实现书上简单列表的例子,遇到了一些坑,稍微总结一下。
在C里面,数组是内存块,通过下标访问这些内存块。因此,在C里面的数组只能使用整数且有序的键值,像PHP中那样简单便捷的关联数组在C中是不存在的。所以,想要处理复杂的数据集的时候,就需要使用数据结构,链表就是其中之一。
链表
虽然C提供了struct(结构)这种数据类型,可以使用结构数组来处理复杂的数据集,如果事先声明了结构数组的大小,那么存少了浪费空间,存多了又会不够,如果用的时候再使用malloc来申请内存空间,又不能保证内存连续,所以,使用结构数组不够灵活。
#define TSIZE 45
struct film{
char title[TSIZE];
int rating;
struct film * next; //存放下一个元素的地址。
};
如上结构,就是一个链表的简单结构,每次拿到链表中的一个元素,就可以根据next属性拿到下一个元素。
也可以增加一个pre属性,用于存储该元素上一个元素的地址,即双向链表。
#define TSIZE 45
struct film{
char title[TSIZE];
int rating;
struct film * pre; //上一个元素的地址
struct film * next; //下一个元素的地址
};
抽象数据类型(ADT),从链表到列表
一个列表需要有以下方法:
1. 初始化列表
2. 向列表末尾添加元素
3. 判断列表是否为空
4. 获取列表元素数量
5. 访问列表中每个项目来做操作
6. 在列表中任意位置插入元素
7. 从列表中删除一个元素
8. 取出列表中的一个元素
9. 替换列表中的一个元素
10. 在列表中搜索一个元素
需要注意的
- 在处理列表时,需要用一个临时变量存储当前的元素
- 列表的第一个元素的地址需要一直记录着,如果直接操作原始变量,会导致指针混乱
最后
PHP的数组功能强大,使用起来简单便捷,这些都是底层C的功劳,学好C,是通往PHP大神的必经之路!
代码
list.h头文件
#ifndef LIST_H_
#define LIST_H_
#include <stdbool.h>
#define LSIZE 45
struct film{
char title[LSIZE];
int rating;
};
typedef struct film Item;
typedef struct node{
Item item;
struct node * next;
}Node;
typedef Node * List;
void initList(List * plist);
bool listIsEmpty(List * plist);
bool listIsFull(List * plist);
bool addItem(List * plist, Item item);
void traverse(List * plist, void (*func)(Item item, int index));
unsigned int itemCount(List * plist);
void emptyList(List * plist);
Item * getItem(List * plist, int index);
#endif
list.c接口实现
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include "list.h"
static void copyItemToNode(Item item, Node * pnode);
void initList(List * plist){
*plist = NULL;
}
bool listIsEmpty(List * plist){
if(*plist == NULL){
return true;
}
return false;
}
bool listIsFull(List * plist){
Node * pt;
pt = (Node *) malloc(sizeof(Node));
if(pt == NULL){
free(pt);
return true;
}else{
free(pt);
return false;
}
}
bool addItem(List * plist, Item item){
Node * pnode;
Node * current = *plist;
pnode = (Node *)malloc(sizeof(Node));
if(pnode == NULL){
return false;
}
copyItemToNode(item, pnode);
pnode->next = NULL;
if(*plist == NULL){ //如果列表中没有项目,则添加的是第一个项目
*plist = pnode;
}else{
//如果不是第一个项目,则找到列表中节点为空的项目,将新项目的地址存进去
while(current->next != NULL){
current = current->next;
}
current->next = pnode;
}
return true;
}
void traverse(List * plist, void (*func)(Item item, int index)){
Node * current = *plist;
int count = 0;
while(current != NULL){
(*func)(current->item, count + 1);
current = current->next;
count++;
}
}
unsigned int itemCount(List * plist){
unsigned int count = 0;
Node * current = *plist;
while(current != NULL){
count++;
current = current->next;
}
return count;
}
Item * getItem(List * plist, int index){
Node * current = *plist;
int count = 0;
while((current != NULL)){
if(count == index) {
return ¤t->item;
}
current = current->next;
count++;
}
return NULL;
}
void emptyList(List * plist){
Node * temp;
while((*plist) != NULL){
temp = *plist;
free(*plist);
*plist = temp->next;
}
}
static void copyItemToNode(Item item, Node * pnode){
pnode->item = item;
}
main.c主文件
#include <stdio.h>
#include <stdlib.h>
#include "list.h"
void showItem(Item item, int index);
int main(){
List film_list;
Item film;
initList(&film_list);
if(listIsFull(&film_list)){
fprintf(stderr, "列表已满!\n");
exit(1);
}
puts("请输入电影名称:");
while(gets(film.title) != NULL && film.title[0] != '\0'){
puts("请输入电影评分<1-10>:");
scanf("%d", &film.rating);
while(getchar() != '\n'){
continue;
}
if(!addItem(&film_list, film)){
fprintf(stderr, "添加失败!\n");
break;
}
if(listIsFull(&film_list)){
fprintf(stderr, "列表已满!\n");
break;
}
puts("下一个电影的名称(输入空行退出):");
}
if(listIsEmpty(&film_list)){
puts("电影列表为空!");
}else{
puts("当前的电影列表:");
traverse(&film_list,showItem);
}
printf("当前电影数量:%u\n", itemCount(&film_list));
showItem(*getItem(&film_list, 2), 3);
emptyList(&film_list);
return 0;
}
void showItem(Item item, int index){
printf("第%d部:<%s> 评分:%d\n", index, item.title, item.rating);
}