C语言最显著的特点是需要手动管理所有内存:
// 分配内存
ListElmt *new_element = (ListElmt *)malloc(sizeof(ListElmt));
if (new_element == NULL) {
return -1; // 内存分配失败
}
// 使用内存
new_element->data = (void *)data;
// 释放内存(必须手动调用)
free(new_element);
new_element = NULL; // 避免悬空指针内存管理原则:
- 每个malloc必须有对应的free
- 释放后立即置NULL
- 检查malloc返回值是否为NULL
- 避免内存泄漏和重复释放
C语言没有模板,使用void*实现泛型:
typedef struct ListElmt_ {
void *data; // 可以指向任何类型
struct ListElmt_ *next;
} ListElmt;
// 存储整数
int value = 42;
element->data = &value;
// 存储字符串
char *str = "hello";
element->data = str;
// 使用时需要转换
int *ptr = (int *)element->data;
printf("%d\n", *ptr);void指针的限制:
- 不能直接解引用
- 不能进行指针运算
- 使用前必须转换为具体类型
C使用函数指针实现回调:
typedef struct List_ {
int size;
int (*match)(const void *key1, const void *key2); // 比较函数
void (*destroy)(void *data); // 销毁函数
ListElmt *head;
ListElmt *tail;
} List;
// 初始化时传入函数指针
void list_init(List *list, void (*destroy)(void *data)) {
list->destroy = destroy;
// ...
}
// 使用回调函数
if (list->destroy != NULL) {
list->destroy(data);
}// 1. 分配内存
List *list = (List *)malloc(sizeof(List));
if (list == NULL) {
return -1;
}
// 2. 初始化
list_init(list, free_function);
// 3. 使用
// ... 使用list
// 4. 清理
list_destroy(list);
free(list);
list = NULL;// 释放树结构(递归)
static void destroy_left(BisTree *tree, BiTreeNode *node) {
BiTreeNode **position;
if (bitree_size(tree) == 0)
return;
if (node == NULL)
position = &tree->root;
else
position = &node->left;
if (*position != NULL) {
// 先递归释放子树
destroy_left(tree, *position);
destroy_right(tree, *position);
// 释放数据
if (tree->destroy != NULL) {
tree->destroy(((AvlNode *)(*position)->data)->data);
}
// 释放节点
free((*position)->data);
free(*position);
*position = NULL;
tree->size--;
}
}释放顺序很重要:
- 先释放子节点(后序遍历)
- 再释放当前节点
- 最后释放根节点
文件: c/list.c, c/list.h
数据结构:
// 节点
typedef struct ListElmt_ {
void *data;
struct ListElmt_ *next;
} ListElmt;
// 链表
typedef struct List_ {
int size;
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
ListElmt *head;
ListElmt *tail;
} List;插入操作:
int list_ins_next(List *list, ListElmt *element, const void *data) {
ListElmt *new_element;
// 分配新节点
if ((new_element = (ListElmt *)malloc(sizeof(ListElmt))) == NULL)
return -1;
new_element->data = (void *)data;
// 在element后插入
if (element == NULL) {
// 在头部插入
if (list_size(list) == 0)
list->tail = new_element;
new_element->next = list->head;
list->head = new_element;
} else {
// 在中间插入
if (element->next == NULL)
list->tail = new_element;
new_element->next = element->next;
element->next = new_element;
}
list->size++;
return 0;
}文件: c/dlist.c, c/dlist.h
数据结构:
typedef struct DListElmt_ {
void *data;
struct DListElmt_ *prev;
struct DListElmt_ *next;
} DListElmt;
typedef struct DList_ {
int size;
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
DListElmt *head;
DListElmt *tail;
} DList;优势:
- 可以双向遍历
- O(1)时间删除给定节点
文件: c/bistree.c, c/bistree.h
AVL节点定义:
typedef enum AvlNode_ {
AvlNodeLeftHeavy,
AvlNodeBalanced,
AvlNodeRightHeavy
} AvlNodeFactor;
typedef struct AvlNode_ {
void *data;
AvlNodeFactor factor; // 平衡因子
char hidden; // 标记删除
} AvlNode;旋转操作:
// 左旋转
static void rotate_left(BiTreeNode **node) {
BiTreeNode *left, *grandchild;
left = bitree_left(*node);
if (((AvlNode *)bitree_data(left))->factor == AVL_LFT_HEAVY) {
// LL情况:直接左旋
bitree_left(*node) = bitree_right(left);
bitree_right(left) = *node;
((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
((AvlNode *)bitree_data(left))->factor = AVL_BALANCED;
*node = left;
} else {
// LR情况:先右旋左子节点,再左旋
grandchild = bitree_right(left);
bitree_right(left) = bitree_left(grandchild);
bitree_left(grandchild) = left;
bitree_left(*node) = bitree_right(grandchild);
bitree_right(grandchild) = *node;
// 更新平衡因子
switch (((AvlNode *)bitree_data(grandchild))->factor) {
case AVL_LFT_HEAVY:
((AvlNode *)bitree_data(*node))->factor = AVL_RGT_HEAY;
((AvlNode *)bitree_data(left))->factor = AVL_BALANCED;
break;
case AVL_BALANCED:
((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
((AvlNode *)bitree_data(left))->factor = AVL_BALANCED;
break;
case AVL_RGT_HEAY:
((AvlNode *)bitree_data(*node))->factor = AVL_BALANCED;
((AvlNode *)bitree_data(left))->factor = AVL_BALANCED;
break;
}
((AvlNode *)bitree_data(grandchild))->factor = AVL_BALANCED;
*node = grandchild;
}
}文件: c/chtbl.c, c/chtbl.h
链地址法实现:
typedef struct ChtblElmt_ {
void *data;
struct ChtblElmt_ *next;
} ChtblElmt;
typedef struct Chtbl_ {
int buckets;
int (*h)(const void *key); // 哈希函数
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
int size;
ListElmt **table; // 链表数组
} Chtbl;哈希表插入:
int chtbl_insert(Chtbl *htbl, const void *data) {
int bucket, retval;
// 找到bucket
bucket = htbl->h(data) % htbl->buckets;
// 检查是否已存在
if (chtbl_lookup(htbl, data) != NULL)
return 1;
// 插入到链表头部
if ((retval = list_ins_next(htbl->table[bucket], NULL, data)) == 0)
htbl->size++;
return retval;
}文件: c/stack.c, c/queue.c
栈(基于链表):
typedef struct Stack_ {
List list;
} Stack;
// 入栈
int stack_push(Stack *stack, const void *data) {
return list_ins_next(&stack->list, NULL, data);
}
// 出栈
int stack_pop(Stack *stack, void **data) {
return list_rem_next(&stack->list, NULL, data);
}队列(基于链表):
typedef struct Queue_ {
List list;
} Queue;
// 入队
int queue_enqueue(Queue *queue, const void *data) {
return list_ins_next(&queue->list, list_tail(&queue->list), data);
}
// 出队
int queue_dequeue(Queue *queue, void **data) {
return list_rem_next(&queue->list, NULL, data);
}文件: c/heap.c, c/pqueue.c
堆结构:
typedef struct Heap_ {
int size;
int (*compare)(const void *key1, const void *key2);
void (*destroy)(void *data);
void **tree; // 动态数组
} Heap;堆插入(上浮):
int heap_insert(Heap *heap, const void *data) {
void *temp;
int ipos, ppos;
// 插入到最后
if ((heap->tree[heap->size] = malloc(sizeof(void *))) == NULL)
return -1;
heap->tree[heap->size] = (void *)data;
ipos = heap->size;
ppos = (ipos - 1) / 2;
// 上浮
while (ipos > 0 && heap->compare(heap->tree[ppos], heap->tree[ipos]) < 0) {
temp = heap->tree[ppos];
heap->tree[ppos] = heap->tree[ipos];
heap->tree[ipos] = temp;
ipos = ppos;
ppos = (ipos - 1) / 2;
}
heap->size++;
return 0;
}文件: c/set.c, c/set.h
基于链表实现:
typedef struct Set_ {
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
List list;
} Set;
int set_insert(Set *set, const void *data) {
// 检查是否已存在
if (set_is_member(set, data))
return 1;
// 插入
return list_ins_next(&set->list, NULL, data);
}文件: c/graph.c, c/graph.h
邻接表实现:
typedef struct AdjList_ {
List adjacent; // 邻接顶点列表
} AdjList;
typedef struct Graph_ {
int vcount; // 顶点数
int ecount; // 边数
int (*match)(const void *key1, const void *key2);
void (*destroy)(void *data);
List adjlists; // 邻接表
} Graph;#include <stdio.h>
#include <stdlib.h>
#include "list.h"
// 销毁函数
void destroy(void *data) {
free(data);
}
int main() {
List list;
int *data;
// 初始化
list_init(&list, destroy);
// 插入元素
for (int i = 0; i < 10; i++) {
data = (int *)malloc(sizeof(int));
*data = i;
list_ins_next(&list, NULL, data);
}
// 遍历
ListElmt *element = list_head(&list);
while (element != NULL) {
printf("%d ", *(int *)list_data(element));
element = list_next(element);
}
// 销毁
list_destroy(&list);
return 0;
}#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "chtbl.h"
// 哈希函数
int hash(const void *key) {
const char *str = (const char *)key;
int hash = 0;
for (int i = 0; str[i] != '\0'; i++) {
hash = (hash << 5) + str[i];
}
return hash;
}
// 匹配函数
int match(const void *key1, const void *key2) {
return strcmp((const char *)key1, (const char *)key2) == 0;
}
int main() {
Chtbl htbl;
char *key, *value;
// 初始化哈希表
chtbl_init(&htbl, 16, hash, match, free);
// 插入键值对
chtbl_insert(&htbl, "one", "1");
chtbl_insert(&htbl, "two", "2");
chtbl_insert(&htbl, "three", "3");
// 查找
if ((value = chtbl_lookup(&htbl, "two")) != NULL) {
printf("Found: %s\n", value);
}
// 销毁
chtbl_destroy(&htbl);
return 0;
}#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "bistree.h"
// 比较函数
int compare(const void *key1, const void *key2) {
return strcmp((const char *)key1, (const char *)key2);
}
int main() {
BisTree tree;
// 初始化
bistree_init(&tree, compare, free);
// 插入
bistree_insert(&tree, "apple");
bistree_insert(&tree, "banana");
bistree_insert(&tree, "cherry");
// 查找
void *data;
if (bistree_lookup(&tree, &data) == 0) {
printf("Found: %s\n", (char *)data);
}
// 销毁
bistree_destroy(&tree);
return 0;
}使用Valgrind检测内存泄漏:
gcc -g program.c -o program
valgrind --leak-check=full ./program常见内存错误:
// 错误1:内存泄漏
void *ptr = malloc(100);
// 忘记free(ptr);
// 错误2:重复释放
void *ptr = malloc(100);
free(ptr);
free(ptr); // 崩溃!
// 错误3:悬空指针
void *ptr = malloc(100);
free(ptr);
*ptr = 42; // 未定义行为
// 正确做法
void *ptr = malloc(100);
if (ptr != NULL) {
// 使用ptr
free(ptr);
ptr = NULL;
}// 检查指针
if (list == NULL || list->head == NULL) {
return -1;
}
// 检查返回值
if ((new_element = malloc(sizeof(ListElmt))) == NULL) {
return -1;
}
// 使用const修饰不改变的数据
int list_size(const List *list) {
return list->size;
}
// 使用宏定义避免魔法数字
#ifndef MAX_SIZE
#define MAX_SIZE 1000
#endif// 返回0表示成功,-1表示失败
int operation(...) {
if (error_condition) {
return -1;
}
return 0;
}
// 使用时检查
if (operation(...) != 0) {
// 处理错误
}#ifdef DEBUG
#define DEBUG_PRINT(fmt, args...) \
printf("[DEBUG] " fmt "\n", ##args)
#else
#define DEBUG_PRINT(fmt, args...)
#endif
// 使用
DEBUG_PRINT("Current size: %d", list->size);C语言数据结构实现的特点:
优点:
- ✅ 完全掌控内存和性能
- ✅ 理解底层实现原理
- ✅ 可移植性强
- ✅ 适合嵌入式和系统编程
挑战:
⚠️ 手动内存管理容易出错⚠️ 没有泛型,使用void*⚠️ 代码量大,易出错⚠️ 调试困难
适用场景:
- 系统编程
- 嵌入式开发
- 性能关键应用
- 学习底层原理
学习建议:
- 熟练掌握指针和内存管理
- 使用工具检测内存错误
- 理解计算机底层原理
- 多练习,多调试