双链表冒泡排序

This commit is contained in:
Wang Bo Yang 2025-04-21 15:42:27 +08:00
parent 1559d5941c
commit 2960ca4fd8
23 changed files with 1270 additions and 0 deletions

BIN
ex17/db1.dat Normal file

Binary file not shown.

BIN
ex17/ex17

Binary file not shown.

264
ex17/ex17_copy.c Normal file
View File

@ -0,0 +1,264 @@
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#define MAX_DATA 512
#define MAX_ROWS 100
struct Address {
int id;
int set;
char name[MAX_DATA];
char email[MAX_DATA];
};
struct Database {
struct Address rows[MAX_ROWS];
};
struct Connection {
FILE *file;
struct Database *db;
};
void die(const char *message)
{
if (errno)
{
perror(message);
}
else
{
printf("ERROR: %s\n", message);
}
exit(1);
}
void Address_print(struct Address *addr)
{
printf("%d %s %s\n", addr->id, addr->name, addr->email);
}
void Database_load(struct Connection *conn)
{
int rc = fread(conn->db, sizeof(struct Database), 1, conn->file);
if (rc != 1 )
{
die("Failed to load database.");
}
}
struct Connection *Database_open(const char *filename, char mode)
{
struct Connection *conn = malloc(sizeof(struct Connection));
if (!conn)
{
die("Memory error");
}
conn->db = malloc(sizeof(struct Database));
if (!conn->db)
{
die("Memory error");
}
if (mode == 'c')
{
conn->file = fopen(filename, "w");
}
else
{
conn->file = fopen(filename, "r+");
if (conn->file)
{
Database_load(conn);
}
}
if (!conn->file)
{
die("Failed to open the file");
}
return conn;
};
void Database_close(struct Connection *conn)
{
if (conn)
{
if (conn->file)
{
fclose(conn->file);
}
if (conn->db)
{
free(conn->db);
}
free(conn);
}
}
void Database_write(struct Connection *conn)
{
rewind(conn->file);
int rc = fwrite(conn->db, sizeof(struct Database), 1, conn->file);
if (rc != 1)
{
die("Failed to write database.");
}
rc = fflush(conn->file);
if (rc == -1)
{
die("Cannot flush database.");
}
}
void Database_create(struct Connection *conn)
{
int i = 0;
for (i = 0; i < MAX_ROWS; i++)
{
// make a prototype to initialize it
struct Address addr = {.id = i, .set = 0};
// then just assign it
conn->db->rows[i] = addr;
}
}
void Database_set(struct Connection *conn, int id, const char *name, const char *email)
{
struct Address *addr = &conn->db->rows[id];
if (addr->set)
{
die("Already set, delete it first");
}
addr->set = 1;
// WARNING: bug, read the "How To Break It" and fix this
char *res = strncpy(addr->name, name, MAX_DATA);
// demonstrate the strncpy bug
if (!res)
{
die("Name copy failed");
}
res = strncpy(addr->email, email, MAX_DATA);
if (!res)
{
die("Email copy failed");
}
}
void Database_get(struct Connection *conn, int id)
{
struct Address *addr = &conn->db->rows[id];
if (addr->set)
{
Address_print(addr);
}
else
{
die("ID is not set");
}
}
void Database_delete(struct Connection *conn, int id)
{
struct Address addr = {.id = id, .set = 0};
conn->db->rows[id] = addr;
}
void Database_list(struct Connection *conn)
{
int i = 0;
struct Database *db = conn->db;
for (i = 0; i < MAX_ROWS; i++)
{
struct Address *cur = &db->rows[i];
if (cur->set)
{
Address_print(cur);
}
}
}
int main(int argc, char *argv[])
{
if (argc < 3)
{
die("USAGE: ex17 <dbfile><action> [action params]");
}
char *filename = argv[1];
char action = argv[2][0];
struct Connection *conn = Database_open(filename, action);
int id = 0;
if (argc > 3)
{
id = atoi(argv[3]);
}
if (id >= MAX_ROWS)
{
die("There's not that many records.");
}
switch(action)
{
case 'c':
Database_create(conn);
Database_write(conn);
break;
case 'g':
if (argc != 4)
{
die("Need an id to get");
}
Database_get(conn, id);
break;
case 's':
if (argc != 6)
{
die("Need id, name, email to set");
}
Database_set(conn, id, argv[4], argv[5]);
Database_write(conn);
break;
case 'd':
if (argc != 4)
{
die("Need id to delete");
}
Database_delete(conn, id);
Database_write(conn);
break;
case 'l':
Database_list(conn);
break;
default:
die("Invalid action: c=create, g=get, s=set, l=list");
}
Database_close(conn);
return 0;
}

0
ex33/liblcthw/LICENSE Normal file
View File

55
ex33/liblcthw/Makefile Normal file
View File

@ -0,0 +1,55 @@
CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)
LIBS=-ldl $(OPTLIBS)
PREFIX?=/usr/local
SOURCES=$(wildcard src/**/*.c src/*.c)
HEADERS=$(wildcard src/**/*.h src/*.h)
OBJECTS=$(patsubst %.c,%.o,$(SOURCES))
TEST_SRC=$(wildcard tests/*_tests.c)
TESTS=$(patsubst %.c,%,$(TEST_SRC))
TARGET=build/liblcthw.a
SO_TARGET=$(patsubst %.a,%.so,$(TARGET))
# The Target Build
all: $(TARGET) $(SO_TARGET) tests
dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)
dev: all
$(TESTS): $(TARGET) $(SO_TARGET)
$(TARGET): CFLAGS += -fPIC
$(TARGET): build $(OBJECTS)
ar rcs $@ $(OBJECTS)
ranlib $@
$(SO_TARGET): $(TARGET) $(OBJECTS)
$(CC) -shared -o $@ $(OBJECTS)
build:
@mkdir -p build
@mkdir -p bin
# The Unit Tests
.PHONY: tests
tests: CFLAGS += $(TARGET)
tests: $(TESTS)
sh ./tests/runtests.sh
# The Cleaner
clean:
rm -rf build $(OBJECTS) $(TESTS)
rm -f tests/tests.log
find . -name "*.gc*" -exec rm {} \;
rm -rf 'find . -name "*.dSYM" -print'
# The Install
install: all
install -d $(DESTDIR)/$(PREFIX)/lib/
indtall $(TARGET) $(DESTDIR)/$(PREFIX)/lib/
# The Checker
check:
@echo Files with potentially dangerous functions.
@egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|a?sn?printf|byte_)' $(SOURCES) || true

0
ex33/liblcthw/README.md Normal file
View File

Binary file not shown.

BIN
ex33/liblcthw/build/liblcthw.so Executable file

Binary file not shown.

View File

@ -0,0 +1,37 @@
#ifndef __dbg_h__
#define __dbg_h__
#include <stdio.h>
#include <errno.h>
#include <string.h>
#ifdef NDEBUG
#define ZED_DEBUG_MACRO(M, ...)
#else // 打印文件名,行号
#define ZED_DEBUG_MACRO(M, ...) fprintf(stderr, "DEBUG %s:%d: " M "\n", __FILE__, __LINE__, ##__VA_ARGS__)
#endif
#define ZED_CLEAN_ERRNO_MACRO() (errno == 0 ? "None" : strerror(errno))
// 打印错误的提示信息
#define ZED_LOG_ERR_MACRO(M, ...) fprintf(stderr, "[ERROR] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, ZED_CLEAN_ERRNO_MACRO(), ##__VA_ARGS__)
// 打印警告的提示信息
#define ZED_LOG_WARN_MACRO(M, ...) fprintf(stderr, "[WARN] (%s:%d: errno: %s) " M "\n", __FILE__, __LINE__, ZED_CLEAN_ERRNO_MACRO(), ##__VA_ARGS__)
// 打印提示信息
#define ZED_LOG_INFO_MACRO(M, ...) fprintf(stderr, "[INFO] (%s:%s:%d) " M "\n", __FUNCTION__, __FILE__, __LINE__, ##__VA_ARGS__)
// 如果A为假则打印错误提示
#define ZED_CHECK_MACRO(A, M, ...) if(!(A)) {ZED_LOG_ERR_MACRO(M, ##__VA_ARGS__); errno=0; goto error;}
// 根据输入参数判断运行是否成功
#define ZED_SENTINEL_MACRO(M, ...) {ZED_LOG_ERR_MACRO(M, ##__VA_ARGS__); errno=0; goto error;}
// 判断内存分配是否成功
#define ZED_CHECK_MEM_MACRO(A) ZED_CHECK_MACRO((A), "Out of memory.")
// 如果A为假显示调试信息
#define ZED_CHECK_DEBUG_MACRO(A, M, ...) if(!(A)) {ZED_DEBUG_MACRO(M, ##__VA_ARGS__); errno=0; goto error;}
#endif

View File

@ -0,0 +1,261 @@
#include "lcthw/list.h"
#include "lcthw/dbg.h"
List *List_create()
{
return calloc(1, sizeof(List));
}
void List_destroy(List *list)
{
ZED_CHECK_MACRO(list != NULL, "list is NULL!");
ZED_LIST_FOREACH_MACRO(list, first, next, cur) {
if (cur -> prev) {
free(cur->prev);
}
}
free(list->last);
free(list);
error:
return;
}
void List_clear(List *list)
{
ZED_CHECK_MACRO(list != NULL, "list is NULL!");
ZED_LIST_FOREACH_MACRO(list, first, next, cur) {
cur->value = NULL;
}
error:
return;
}
void List_clear_destroy(List *list)
{
List_clear(list);
List_destroy(list);
}
void List_push(List *list, char *value)
{
// puts("hello");
ListNode *node = calloc(1, sizeof(ListNode));
ZED_CHECK_MEM_MACRO(node);
node->value = value;
if (NULL == list->last) {
list->first = node;
list->last = node;
} else {
list->last->next = node;
node->prev = list->last;
list->last = node;
}
++(list->count);
error:
return;
}
char *List_pop(List *list)
{
ZED_CHECK_MACRO(list != NULL, "list is NULL!");
ListNode *node = list->last;
return node != NULL ? List_remove(list, node) : NULL;
error:
return NULL;
}
void List_unshift(List *list, char *value)
{
ListNode *node = calloc(1, sizeof(ListNode));
ZED_CHECK_MEM_MACRO(node);
node -> value = value;
if (NULL == list->first) {
list->first = node;
list->last = node;
} else {
node->next = list->first;
list->first->prev = node;
list->first = node;
}
++(list->count);
error:
return;
}
char *List_shift(List *list)
{
ZED_CHECK_MACRO(list != NULL, "list is NULL!");
ListNode *node = list->first;
return node != NULL ? List_remove(list, node) : NULL;
error:
return NULL;
}
char *List_remove(List *list, ListNode *node)
{
void *result = NULL;
ZED_CHECK_MACRO(list != NULL, "list is NULL!");
ZED_CHECK_MACRO(list->first && list->last, "List is empty.");
ZED_CHECK_MACRO(node, "node can't be NULL");
if (node == list->first && node == list->last) {
list->first = NULL;
list->last = NULL;
} else if (node == list->first) {
list->first = node->next;
ZED_CHECK_MACRO(list->first != NULL, "Invalid list, somehow got a first that is NULL.");
list->first->prev = NULL;
} else if (node == list->last) {
list->last = node->prev;
ZED_CHECK_MACRO(list->last != NULL, "Invalid list, somehow got a next that is NULL.");
list->last->next = NULL;
} else {
ListNode *after = node->next;
ListNode *before = node->prev;
after->prev = before;
before->next = after;
}
--(list->count);
result = node->value;
free(node);
error:
return result;
}
List *List_copy(List *list)
{
List *list_copy = NULL;
ListNode *cur = NULL;
if (NULL == list)
{
return list;
}
else
{
cur = list->first;
list_copy = List_create();
ZED_CHECK_MACRO(list != NULL, "Failed to create list.");
// 循环复制
while (cur != NULL)
{
List_push(list_copy, cur -> value);
cur = cur -> next;
}
}
error:
return list_copy;
}
// 将递增有序的双链表lista和listb合并成一个递增有序的双链表listc
// 处理完后lista和listb将被销毁
List *List_merge(List* lista, List* listb)
{
List *listc = NULL;
char* value = NULL;
ZED_CHECK_MACRO(lista != NULL && listb != NULL, "one or both of the lists are NULL");
ZED_CHECK_MACRO(ZED_LIST_COUNT_MACRO(lista) != 0 && ZED_LIST_COUNT_MACRO(listb) != 0, "one or both of the lists don't have a node");
listc = List_create();
// 当两个表中均未处理完时将值较小的结点插入到新表listc中
while ((ZED_LIST_COUNT_MACRO(lista) > 0) && (ZED_LIST_COUNT_MACRO(listb) > 0))
{
if (ZED_LIST_FIRST_MACRO(lista) <= ZED_LIST_FIRST_MACRO(listb))
{
value = List_shift(lista);
List_push(listc, value);
}
else
{
value = List_shift(listb);
List_push(listc, value);
}
}
// 若表lista未完
while (ZED_LIST_COUNT_MACRO(lista) > 0)
{
value = List_shift(lista);
List_push(listc, value);
}
// 若表listb未完
while (ZED_LIST_COUNT_MACRO(listb) > 0)
{
value = List_shift(listb);
List_push(listc, value);
}
// 记得在主函数处销毁
// List_clear_destroy(lista);
// List_clear_destroy(listb);
error:
return listc;
}
List *List_split(List *list)
{
List *list_front = NULL;
int node_count = 0;
int i = 0;
char *value;
ZED_CHECK_MACRO(list != NULL, "list is NULL");
ZED_CHECK_MACRO(ZED_LIST_COUNT_MACRO(list) > 1, "list length <=1");
node_count = ZED_LIST_COUNT_MACRO(list);
list_front = List_create();
if (0 != node_count%2)
{
++node_count;
}
while (i < node_count/2)
{
value = List_shift(list);
List_push(list_front, value);
++i;
}
error:
return list_front;
}
int List_count(List *list)
{
return list -> count;
}

View File

@ -0,0 +1,69 @@
#ifndef lcthw_List_h
#define lcthw_List_h
#include <stdlib.h>
// struct ListNode;
// 双链表结点结构体,包含指向前后结点和所存储元素的指针
typedef struct ListNode {
struct ListNode *next;
struct ListNode *prev;
char *value;
} ListNode;
// 双链表结构体,包含结点个数,指向第一个结点和最后一个结点的指针
typedef struct List {
int count;
ListNode *first;
ListNode *last;
} List;
// 创建空的双链表
List *List_create();
// 销毁双链表
void List_destroy(List *list);
// 释放结点的值,而非结点本身。
void List_clear(List *list);
// 销毁双链表
void List_clear_destroy(List *list);
// 获取链表结点个数
#define ZED_LIST_COUNT_MACRO(A) ((A)->count)
int List_count(List *list);
// 获取链表第一个结点所存储的值
#define ZED_LIST_FIRST_MACRO(A) ((A)->first != NULL ? (A)->first->value : NULL)
// 获取链表最后一个结点所存储的值
#define ZED_LIST_LAST_MACRO(A) ((A)->last != NULL ? (A)->last->value : NULL)
// 在链表的结尾添加一个新元素
void List_push(List *list, char *value);
// 删除表中最后一个元素,并返回最后一个元素的值
char *List_pop(List *list);
// 在链表的前面添加一个新元素
void List_unshift(List *list, char *value);
// 与List_pop一样删除表中最后一个元素并返回它的值
char *List_shift(List *list);
// 删除node结点
char *List_remove(List *list, ListNode *node);
// 遍历双链表
#define ZED_LIST_FOREACH_MACRO(LIST, FIRST, NEXT, CUR)\
ListNode *CUR = NULL;\
for(CUR = LIST->FIRST; CUR != NULL; CUR = CUR -> NEXT)
// 复制双链表
List *List_copy(List *list);
// 将递增有序的双链表lista和listb合并成一个递增有序的双链表listc
List *List_merge(List *lista, List *listb);
// 从中间n/2或(n+1)/2位置处将list一分为二
// 输入待分割双链表list
// 输出分割后的链表前半部分通过返回值返回后半部分保留在输入的双链表list中。
List *List_split(List *list);
#endif

Binary file not shown.

View File

@ -0,0 +1,53 @@
#include "lcthw/list_algos.h"
#include "lcthw/dbg.h"
#include <stdlib.h>
#include <string.h>
int List_bubble_sort(List *list, List_compare cmp)
{
ListNode *p = NULL; // 工作指针,指向前一个结点
ListNode *q = NULL; // 工作指针,指向后一个结点
int flag = 0;
char *tmp = NULL;
ListNode *end = NULL; // 交换终止位置
ZED_CHECK_MACRO(NULL != list, "list is null");
ZED_CHECK_MACRO(ZED_LIST_COUNT_MACRO(list) > 1, "list length is less than 2");
p = list -> first;
q = list -> first -> next;
end = list -> last;
while (list -> first != end -> prev)
{
// 一趟交换
while (q != end -> next)
{
flag = strcmp(p -> value, q -> value);
if (flag > 0)
{
// 交换两个结点的值,先删除前一个结点,再将前一个结点插入到后一个结点之后
tmp = p -> value;
p -> value = q -> value;
q -> value = tmp;
}
p = q;
q = q -> next;
}
end = end -> prev;
}
return 0;
error:
return -1;
}
List *List_merge_sort(List *list, List_compare cmp)
{
return list;
}

View File

@ -0,0 +1,14 @@
#ifndef lcthw_List_algos_h
#define lcthw_List_algos_h
#include <lcthw/list.h>
typedef int (*List_compare) (const void *a, const void *b);
// 冒泡排序
int List_bubble_sort(List *list, List_compare cmp);
// 归并排序
List *List_merge_sort(List *list, List_compare cmp);
#endif

Binary file not shown.

95
ex33/liblcthw/tags Normal file
View File

@ -0,0 +1,95 @@
!_TAG_FILE_FORMAT 2 /extended format; --format=1 will not append ;" to lines/
!_TAG_FILE_SORTED 1 /0=unsorted, 1=sorted, 2=foldcase/
!_TAG_OUTPUT_EXCMD mixed /number, pattern, mixed, or combineV2/
!_TAG_OUTPUT_FILESEP slash /slash or backslash/
!_TAG_OUTPUT_MODE u-ctags /u-ctags or e-ctags/
!_TAG_PATTERN_LENGTH_LIMIT 96 /0 for no limit/
!_TAG_PROC_CWD /home/boyang/文档/Project/C/lcthw/lcthw-ex33/liblcthw/ //
!_TAG_PROGRAM_AUTHOR Universal Ctags Team //
!_TAG_PROGRAM_NAME Universal Ctags /Derived from Exuberant Ctags/
!_TAG_PROGRAM_URL https://ctags.io/ /official site/
!_TAG_PROGRAM_VERSION 5.9.0 //
$(SO_TARGET) Makefile /^$(SO_TARGET): $(TARGET) $(OBJECTS)$/;" t
$(TARGET) Makefile /^$(TARGET): CFLAGS += -fPIC$/;" t
$(TARGET) Makefile /^$(TARGET): build $(OBJECTS)$/;" t
$(TESTS) Makefile /^$(TESTS): $(TARGET) $(SO_TARGET)$/;" t
CFLAGS Makefile /^CFLAGS=-g -O2 -Wall -Wextra -Isrc -rdynamic -DNDEBUG $(OPTFLAGS)$/;" m
CFLAGS Makefile /^dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)$/;" m
HEADERS Makefile /^HEADERS=$(wildcard src\/**\/*.h src\/*.h)$/;" m
LIBS Makefile /^LIBS=-ldl $(OPTLIBS)$/;" m
List src/lcthw/list_algos.h /^typedef struct List {$/;" s
List src/lcthw/list_algos.h /^} List;$/;" t typeref:struct:List
ListNode src/lcthw/list_algos.h /^typedef struct ListNode {$/;" s
ListNode src/lcthw/list_algos.h /^} ListNode;$/;" t typeref:struct:ListNode
List_bubble_sort src/lcthw/list.c /^int List_bubble_sort(List *list, List_compare cmp)$/;" f typeref:typename:int
List_clear src/lcthw/list.c /^void List_clear(List *list)$/;" f typeref:typename:void
List_clear_destroy src/lcthw/list.c /^void List_clear_destroy(List *list)$/;" f typeref:typename:void
List_compare src/lcthw/list_algos.h /^typedef int (*List_compare) (const void *a, const void *b);$/;" t typeref:typename:int (*)(const void * a,const void * b)
List_copy src/lcthw/list.c /^List *List_copy(List *list)$/;" f typeref:typename:List *
List_create src/lcthw/list.c /^List *List_create()$/;" f typeref:typename:List *
List_destroy src/lcthw/list.c /^void List_destroy(List *list)$/;" f typeref:typename:void
List_merge src/lcthw/list.c /^List *List_merge(List* lista, List* listb)$/;" f typeref:typename:List *
List_merge_sort src/lcthw/list.c /^List *List_merge_sort(List *list, List_compare cmp)$/;" f typeref:typename:List *
List_pop src/lcthw/list.c /^char *List_pop(List *list)$/;" f typeref:typename:char *
List_push src/lcthw/list.c /^void List_push(List *list, char *value)$/;" f typeref:typename:void
List_remove src/lcthw/list.c /^char *List_remove(List *list, ListNode *node)$/;" f typeref:typename:char *
List_shift src/lcthw/list.c /^char *List_shift(List *list)$/;" f typeref:typename:char *
List_split src/lcthw/list.c /^List *List_split(List *list)$/;" f typeref:typename:List *
List_unshift src/lcthw/list.c /^void List_unshift(List *list, char *value)$/;" f typeref:typename:void
NUM_VALUES tests/list_algos_tests.c /^#define NUM_VALUES /;" d file:
OBJECTS Makefile /^OBJECTS=$(patsubst %.c,%.o,$(SOURCES))$/;" m
PREFIX Makefile /^PREFIX?=\/usr\/local$/;" m
SOURCES Makefile /^SOURCES=$(wildcard src\/**\/*.c src\/*.c)$/;" m
SO_TARGET Makefile /^SO_TARGET=$(patsubst %.a,%.so,$(TARGET))$/;" m
TARGET Makefile /^TARGET=build\/liblcthw.a$/;" m
TESTS Makefile /^TESTS=$(patsubst %.c,%,$(TEST_SRC))$/;" m
TEST_SRC Makefile /^TEST_SRC=$(wildcard tests\/*_tests.c)$/;" m
ZED_CHECK_DEBUG_MACRO src/lcthw/dbg.h /^#define ZED_CHECK_DEBUG_MACRO(/;" d
ZED_CHECK_MACRO src/lcthw/dbg.h /^#define ZED_CHECK_MACRO(/;" d
ZED_CHECK_MEM_MACRO src/lcthw/dbg.h /^#define ZED_CHECK_MEM_MACRO(/;" d
ZED_CLEAN_ERRNO_MACRO src/lcthw/dbg.h /^#define ZED_CLEAN_ERRNO_MACRO(/;" d
ZED_DEBUG_MACRO src/lcthw/dbg.h /^#define ZED_DEBUG_MACRO(/;" d
ZED_LIST_COUNT_MACRO src/lcthw/list_algos.h /^#define ZED_LIST_COUNT_MACRO(/;" d
ZED_LIST_FIRST_MACRO src/lcthw/list_algos.h /^#define ZED_LIST_FIRST_MACRO(/;" d
ZED_LIST_FOREACH_MACRO src/lcthw/list_algos.h /^#define ZED_LIST_FOREACH_MACRO(/;" d
ZED_LIST_LAST_MACRO src/lcthw/list_algos.h /^#define ZED_LIST_LAST_MACRO(/;" d
ZED_LOG_ERR_MACRO src/lcthw/dbg.h /^#define ZED_LOG_ERR_MACRO(/;" d
ZED_LOG_INFO_MACRO src/lcthw/dbg.h /^#define ZED_LOG_INFO_MACRO(/;" d
ZED_LOG_WARN_MACRO src/lcthw/dbg.h /^#define ZED_LOG_WARN_MACRO(/;" d
ZED_MU_ASSERT_MACRO tests/minunit.h /^#define ZED_MU_ASSERT_MACRO(/;" d
ZED_MU_RUN_TEST_MACRO tests/minunit.h /^#define ZED_MU_RUN_TEST_MACRO(/;" d
ZED_MU_SUITE_START_MACRO tests/minunit.h /^#define ZED_MU_SUITE_START_MACRO(/;" d
ZED_RUN_TESTS_MACRO tests/minunit.h /^#define ZED_RUN_TESTS_MACRO(/;" d
ZED_SENTINEL_MACRO src/lcthw/dbg.h /^#define ZED_SENTINEL_MACRO(/;" d
__dbg_h__ src/lcthw/dbg.h /^#define __dbg_h__$/;" d
_minunit_h tests/minunit.h /^#define _minunit_h$/;" d
all Makefile /^all: $(TARGET) $(SO_TARGET) tests$/;" t
all_tests tests/list_algos_tests.c /^char *all_tests()$/;" f typeref:typename:char *
all_tests tests/list_tests.c /^char *all_tests()$/;" f typeref:typename:char *
build Makefile /^build:$/;" t
check Makefile /^check:$/;" t
clean Makefile /^clean:$/;" t
count src/lcthw/list_algos.h /^ int count;$/;" m struct:List typeref:typename:int
create_words tests/list_algos_tests.c /^List *create_words()$/;" f typeref:typename:List *
dev Makefile /^dev: CFLAGS=-g -Wall -Isrc -Wall -Wextra $(OPTFLAGS)$/;" t
dev Makefile /^dev: all$/;" t
first src/lcthw/list_algos.h /^ ListNode *first;$/;" m struct:List typeref:typename:ListNode *
install Makefile /^install: all$/;" t
is_sorted tests/list_algos_tests.c /^int is_sorted(List *words)$/;" f typeref:typename:int
last src/lcthw/list_algos.h /^ ListNode *last;$/;" m struct:List typeref:typename:ListNode *
lcthw_List_h src/lcthw/list_algos.h /^#define lcthw_List_h$/;" d
next src/lcthw/list_algos.h /^ struct ListNode *next;$/;" m struct:ListNode typeref:struct:ListNode *
prev src/lcthw/list_algos.h /^ struct ListNode *prev;$/;" m struct:ListNode typeref:struct:ListNode *
test_bubble_sort tests/list_algos_tests.c /^char *test_bubble_sort()$/;" f typeref:typename:char *
test_create_destroy tests/list_tests.c /^char *test_create_destroy()$/;" f typeref:typename:char *
test_merge tests/list_tests.c /^char *test_merge()$/;" f typeref:typename:char *
test_merge_sort tests/list_algos_tests.c /^char *test_merge_sort()$/;" f typeref:typename:char *
test_push_copy tests/list_tests.c /^char *test_push_copy()$/;" f typeref:typename:char *
test_push_pop1 tests/list_tests.c /^char *test_push_pop1()$/;" f typeref:typename:char *
test_split tests/list_tests.c /^char *test_split()$/;" f typeref:typename:char *
test_unshift_remove_shift tests/list_tests.c /^char *test_unshift_remove_shift()$/;" f typeref:typename:char *
tests Makefile /^tests: $(TESTS)$/;" t
tests Makefile /^tests: CFLAGS += $(TARGET)$/;" t
tests_run_globel tests/minunit.h /^int tests_run_globel = 0;$/;" v typeref:typename:int
value src/lcthw/list_algos.h /^ char *value;$/;" m struct:ListNode typeref:typename:char *
values tests/list_algos_tests.c /^char *values[] = {"XXXX", "1234", "abcd", "xjvef", "NDSS"};$/;" v typeref:typename:char * []

Binary file not shown.

View File

@ -0,0 +1,84 @@
#include "minunit.h"
#include "lcthw/list_algos.h"
#include <assert.h>
#include <string.h>
#define NUM_VALUES 5
char *values[] = {"XXXX", "1234", "abcd", "xjvef", "NDSS"};
List *create_words()
{
int i = 0;
List *words = List_create();
for (i = 0; i < NUM_VALUES; ++i)
{
List_push(words, values[i]);
}
return words;
}
int is_sorted(List *words)
{
ZED_LIST_FOREACH_MACRO(words, first, next, cur) {
if (cur -> next && strcmp(cur -> value, cur -> next -> value) > 0) {
ZED_DEBUG_MACRO("%s %s", (char *)cur -> value, (char *)cur -> next -> value);
return 0;
}
}
return 1;
}
char *test_bubble_sort()
{
List *words = create_words();
// should work on a list that needs sorting
int rc = List_bubble_sort(words, (List_compare)strcmp);
ZED_MU_ASSERT_MACRO(rc != 0, "Bubble sort failed.");
ZED_MU_ASSERT_MACRO(is_sorted(words), "Words are not sorted after bubble sort.");
// should work on an already sorted list
rc = List_bubble_sort(words, (List_compare)strcmp);
ZED_MU_ASSERT_MACRO(0 != rc, "Bubble sort of already sorted failed.");
ZED_MU_ASSERT_MACRO(is_sorted(words), "Words should be sort if already bubble sorted.");
List_clear_destroy(words);
return NULL;
}
char *test_merge_sort()
{
List *words = create_words();
// should work on a list that needs sorting
List *res = List_merge_sort(words, (List_compare)strcmp);
ZED_MU_ASSERT_MACRO(is_sorted(res), "Words are not sorted after merge sort.");
List *res2 = List_merge_sort(res, (List_compare) strcmp);
ZED_MU_ASSERT_MACRO(is_sorted(res), "Should still be sorted after merge sort.");
List_destroy(res2);
List_destroy(res);
List_destroy(words);
return NULL;
}
char *all_tests()
{
ZED_MU_SUITE_START_MACRO();
ZED_MU_RUN_TEST_MACRO(test_bubble_sort);
ZED_MU_RUN_TEST_MACRO(test_merge_sort);
return NULL;
}
ZED_RUN_TESTS_MACRO(all_tests);

BIN
ex33/liblcthw/tests/list_tests Executable file

Binary file not shown.

View File

@ -0,0 +1,278 @@
#include "minunit.h"
#include "lcthw/list_algos.h"
#include <assert.h>
char *test_create_destroy()
{
// test create
List *list = List_create();
ZED_MU_ASSERT_MACRO(NULL == list, "Failed to create list_globel.");
// test destroy
List_clear_destroy(list);
return NULL;
}
char *test_push_pop1()
{
// test create
List *list = List_create();
char *test1 = "test1 data";
char *test2 = "test2 data";
char *test3 = "test3 data";
char *test4 = "test4 data";
ZED_MU_ASSERT_MACRO(NULL == list, "Failed to create list_globel.");
// test push
ZED_MU_ASSERT_MACRO(list->count<0, "invalid list");
ZED_MU_ASSERT_MACRO(list->count>0&&(NULL == ZED_LIST_FIRST_MACRO(list)), "invalid list");
List_push(list, test1);
ZED_MU_ASSERT_MACRO(test1 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
List_push(list, test2);
ZED_MU_ASSERT_MACRO(test2 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
List_push(list, test3);
ZED_MU_ASSERT_MACRO(test3 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
ZED_MU_ASSERT_MACRO(3 != ZED_LIST_COUNT_MACRO(list), "Wrong count on push.");
List_push(list, test4);
ZED_MU_ASSERT_MACRO(test4 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
// test pop
char *val = List_pop(list);
ZED_MU_ASSERT_MACRO(test4 != val, "Wrong value on pop.");
val = List_pop(list);
ZED_MU_ASSERT_MACRO(test3 != val, "Wrong value on pop.");
val = List_pop(list);
ZED_MU_ASSERT_MACRO(test2 != val, "Wrong value on pop.");
val = List_pop(list);
ZED_MU_ASSERT_MACRO(test1 != val, "Wrong value on pop.");
ZED_MU_ASSERT_MACRO(0 != ZED_LIST_COUNT_MACRO(list), "Wrong count after pop.");
// test destroy
List_clear_destroy(list);
return NULL;
}
char *test_unshift_remove_shift()
{
// test create
List *list = List_create();
char *test1 = "test1 data";
char *test2 = "test2 data";
char *test3 = "test3 data";
ZED_MU_ASSERT_MACRO(NULL == list, "Failed to create list_globel.");
// test unshift
List_unshift(list, test1);
ZED_MU_ASSERT_MACRO(test1 != ZED_LIST_FIRST_MACRO(list), "Wrong first value.");
List_unshift(list, test2);
ZED_MU_ASSERT_MACRO(test2 != ZED_LIST_FIRST_MACRO(list), "Wrong first value.");
List_unshift(list, test3);
ZED_MU_ASSERT_MACRO(test3 != ZED_LIST_FIRST_MACRO(list), "Wrong first value.");
ZED_MU_ASSERT_MACRO(3 != ZED_LIST_COUNT_MACRO(list), "Wrong count on unshift.");
// we only need to test the middle remove case since push/shift
// already tests the other cases
// test remove
char *val = List_remove(list, list->first->next);
ZED_MU_ASSERT_MACRO(test2 != val, "Wrong removed element.");
ZED_MU_ASSERT_MACRO(2 != ZED_LIST_COUNT_MACRO(list), "Wrong count after remove.");
ZED_MU_ASSERT_MACRO(test3 != ZED_LIST_FIRST_MACRO(list), "Wrong first after remove.");
ZED_MU_ASSERT_MACRO(test1 != ZED_LIST_LAST_MACRO(list), "Wrong last after remove.");
// test shift
ZED_MU_ASSERT_MACRO(0 == ZED_LIST_COUNT_MACRO(list), "Wrong count before shift.");
char *val1 = List_shift(list);
ZED_MU_ASSERT_MACRO(test3 != val1, "Wrong value on shift.");
val1 = List_shift(list);
ZED_MU_ASSERT_MACRO(test1 != val1, "Wrong value on shift.");
ZED_MU_ASSERT_MACRO(0 != ZED_LIST_COUNT_MACRO(list), "Wrong count after shift.");
// test destroy
List_clear_destroy(list);
return NULL;
}
char *test_push_copy()
{
// test create
List *list = List_create();
List *list_copy = NULL;
char *test1 = "test1 data";
char *test2 = "test2 data";
char *test3 = "test3 data";
ZED_MU_ASSERT_MACRO(NULL == list, "Failed to create list.");
// test push
ZED_MU_ASSERT_MACRO(list->count < 0, "invalid list");
ZED_MU_ASSERT_MACRO((list->count > 0) && (NULL == ZED_LIST_FIRST_MACRO(list)), "invalid list");
List_push(list, test1);
ZED_MU_ASSERT_MACRO(test1 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
List_push(list, test2);
ZED_MU_ASSERT_MACRO(test2 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
List_push(list, test3);
ZED_MU_ASSERT_MACRO(test3 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
// TEST LIST_COPY
list_copy = List_copy(list);
char *val = List_pop(list_copy);
ZED_MU_ASSERT_MACRO(val != test3, "Wrong value on pop.");
val = List_pop(list_copy);
ZED_MU_ASSERT_MACRO(val != test2, "Wrong value on pop.");
val = List_pop(list_copy);
ZED_MU_ASSERT_MACRO(val != test1, "Wrong value on pop.");
ZED_MU_ASSERT_MACRO(ZED_LIST_COUNT_MACRO(list_copy) != 0, "Wrong count after pop.");
// test destroy
List_clear_destroy(list);
List_clear_destroy(list_copy);
return NULL;
}
char *test_merge()
{
List *list1 = List_create();
List *list2 = List_create();
List *list_merge = NULL;
char *test1 = "test1 data";
char *test2 = "test2 data";
char *test3 = "test3 data";
char *test4 = "test4 data";
char *test5 = "test5 data";
char *test6 = "test6 data";
ZED_MU_ASSERT_MACRO(NULL == list1 || NULL == list2, "Failed to create list");
List_push(list1, test1);
ZED_MU_ASSERT_MACRO(test1 != ZED_LIST_LAST_MACRO(list1), "Wrong last values.");
List_push(list1, test3);
ZED_MU_ASSERT_MACRO(test3 != ZED_LIST_LAST_MACRO(list1), "Wrong last values.");
List_push(list1, test5);
ZED_MU_ASSERT_MACRO(test5 != ZED_LIST_LAST_MACRO(list1), "Wrong last values.");
ZED_MU_ASSERT_MACRO(3 != ZED_LIST_COUNT_MACRO(list1), "Wrong list count.");
List_push(list2, test2);
ZED_MU_ASSERT_MACRO(test2 != ZED_LIST_LAST_MACRO(list2), "Wrong last values.");
List_push(list2, test4);
ZED_MU_ASSERT_MACRO(test4 != ZED_LIST_LAST_MACRO(list2), "Wrong last values.");
List_push(list2, test6);
ZED_MU_ASSERT_MACRO(test6 != ZED_LIST_LAST_MACRO(list2), "Wrong last values.");
ZED_MU_ASSERT_MACRO(3 != ZED_LIST_COUNT_MACRO(list2), "Wrong list count.");
list_merge = List_merge(list1, list2);
char* val = List_pop(list_merge);
// puts(val);
ZED_MU_ASSERT_MACRO(val != test6, "Wrong value on pop.");
val = List_pop(list_merge);
ZED_MU_ASSERT_MACRO(val != test5, "Wrong value on pop.");
val = List_pop(list_merge);
ZED_MU_ASSERT_MACRO(val != test4, "Wrong value on pop.");
val = List_pop(list_merge);
ZED_MU_ASSERT_MACRO(val != test3, "Wrong value on pop.");
val = List_pop(list_merge);
ZED_MU_ASSERT_MACRO(val != test2, "Wrong value on pop.");
val = List_pop(list_merge);
ZED_MU_ASSERT_MACRO(val != test1, "Wrong value on pop.");
ZED_MU_ASSERT_MACRO(0 != ZED_LIST_COUNT_MACRO(list_merge), "Wrong value on pop.");
List_clear_destroy(list_merge);
List_clear_destroy(list1);
List_clear_destroy(list2);
return NULL;
}
char *test_split()
{
List *list = List_create();
List *list_front = NULL;
char *test1 = "test1 data";
char *test2 = "test2 data";
char *test3 = "test3 data";
ZED_MU_ASSERT_MACRO(NULL == list, "Failed to create list create");
List_push(list, test1);
ZED_MU_ASSERT_MACRO(test1 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
List_push(list, test2);
ZED_MU_ASSERT_MACRO(test2 != ZED_LIST_LAST_MACRO(list), "Wrong last values");
List_push(list, test3);
ZED_MU_ASSERT_MACRO(test3 != ZED_LIST_LAST_MACRO(list), "Wrong last values.");
ZED_MU_ASSERT_MACRO(3 != ZED_LIST_COUNT_MACRO(list), "Wrong list count.");
list_front = List_split(list);
char *val = List_pop(list_front);
ZED_MU_ASSERT_MACRO(test2 != val, "Wrong value on pop.");
val = List_pop(list_front);
ZED_MU_ASSERT_MACRO(test1 != val, "Wrong value on pop.");
ZED_MU_ASSERT_MACRO(0 != ZED_LIST_COUNT_MACRO(list_front), "Wrong list count");
val = List_pop(list);
ZED_MU_ASSERT_MACRO(test3 != val, "Wrong value on pop.");
ZED_MU_ASSERT_MACRO(0 != ZED_LIST_COUNT_MACRO(list), "Wrong list count");
List_clear_destroy(list_front);
List_clear_destroy(list);
return NULL;
}
char *all_tests()
{
ZED_MU_SUITE_START_MACRO();
ZED_MU_RUN_TEST_MACRO(test_create_destroy);
ZED_MU_RUN_TEST_MACRO(test_push_pop1);
ZED_MU_RUN_TEST_MACRO(test_unshift_remove_shift);
ZED_MU_RUN_TEST_MACRO(test_push_copy);
ZED_MU_RUN_TEST_MACRO(test_merge);
ZED_MU_RUN_TEST_MACRO(test_split);
return NULL;
}
ZED_RUN_TESTS_MACRO(all_tests);

View File

@ -0,0 +1,37 @@
#undef NDEBUG
#ifndef _minunit_h
#define _minunit_h
#include <stdio.h>
#include "lcthw/dbg.h"
#include <stdlib.h>
// 设置消息为空
#define ZED_MU_SUITE_START_MACRO() char *message = NULL
// 如果测试失败,显示错误信息
#define ZED_MU_ASSERT_MACRO(test, message) if (test) {\
ZED_LOG_ERR_MACRO(message); return message; }
// 运行一个测试,如果测试失败,则返回提示信息
#define ZED_MU_RUN_TEST_MACRO(test) ZED_DEBUG_MACRO("\n-----%s", " " #test); \
message = test(); ++tests_run_globel; if (message) return message;
// 测试主函数
#define ZED_RUN_TESTS_MACRO(name) int main(int argc, char *argv[]) {\
ZED_DEBUG_MACRO("----- RUNNING: %s", argv[0]); \
printf("----\n%d test RUNNING: %s\n", argc, argv[0]); \
char *result = name(); \
if (result != 0) { \
printf("FAILED: %s\n", result); \
} \
else { \
printf("ALL TESTS PASSED\n"); \
} \
printf("Function tests run: %d.\n", tests_run_globel); \
exit(result != 0); \
}
int tests_run_globel = 0;
#endif

View File

@ -0,0 +1,19 @@
echo "Running unit tests:"
for i in tests/*_tests
do
if test -f $i
then
if $VALGRIND ./$i 2>> tests/tests.log
then
echo $1 PASS
else
echo "ERROR in test $i: here's tests/tests.log"
echo "------"
tail tests/tests.log
exit 1
fi
fi
done
echo ""

View File

@ -0,0 +1,4 @@
DEBUG tests/list_algos_tests.c:84: ----- RUNNING: ./tests/list_algos_tests
DEBUG tests/list_algos_tests.c:78:
----- test_bubble_sort
Segmentation fault (core dumped)