name: c-data-structures user-invocable: false description: 当需要使用基础C数据结构,包括数组、结构体、链表、树和哈希表,并实现内存高效实现时使用。 allowed-tools:
- Read
- Write
- Edit
- Grep
- Glob
- Bash
C 数据结构
C语言中的数据结构需要手动内存管理和谨慎的指针操作。理解和实现基础数据结构对于构建高效的C应用程序至关重要。本技能涵盖数组、结构体、链表、树和哈希表。
数组和动态数组
数组提供连续内存存储,具有O(1)的访问时间。动态数组提供灵活性,但代价是偶尔的重新分配。
静态数组
#include <stdio.h>
#include <string.h>
// 使用静态数组
void array_operations(void) {
int numbers[5] = {1, 2, 3, 4, 5};
// 数组长度(仅适用于静态数组)
size_t length = sizeof(numbers) / sizeof(numbers[0]);
// 遍历数组
for (size_t i = 0; i < length; i++) {
printf("%d ", numbers[i]);
}
printf("
");
// 数组作为函数参数(退化为指针)
int sum = 0;
for (size_t i = 0; i < length; i++) {
sum += numbers[i];
}
printf("Sum: %d
", sum);
}
动态数组
#include <stdlib.h>
#include <string.h>
typedef struct {
int *data;
size_t size;
size_t capacity;
} DynamicArray;
// 初始化动态数组
DynamicArray *array_create(size_t initial_capacity) {
DynamicArray *arr = malloc(sizeof(DynamicArray));
if (!arr) return NULL;
arr->data = malloc(initial_capacity * sizeof(int));
if (!arr->data) {
free(arr);
return NULL;
}
arr->size = 0;
arr->capacity = initial_capacity;
return arr;
}
// 向数组追加元素
int array_push(DynamicArray *arr, int value) {
if (arr->size >= arr->capacity) {
size_t new_capacity = arr->capacity * 2;
int *new_data = realloc(arr->data, new_capacity * sizeof(int));
if (!new_data) return -1;
arr->data = new_data;
arr->capacity = new_capacity;
}
arr->data[arr->size++] = value;
return 0;
}
// 释放数组内存
void array_free(DynamicArray *arr) {
if (arr) {
free(arr->data);
free(arr);
}
}
结构体和数据建模
结构体将相关数据组合在一起,实现复杂的数据建模和组织。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 定义结构体
typedef struct {
char name[50];
int age;
float salary;
} Employee;
// 创建和初始化结构体
Employee *employee_create(const char *name, int age, float salary) {
Employee *emp = malloc(sizeof(Employee));
if (!emp) return NULL;
strncpy(emp->name, name, sizeof(emp->name) - 1);
emp->name[sizeof(emp->name) - 1] = '\0';
emp->age = age;
emp->salary = salary;
return emp;
}
// 结构体嵌套结构体
typedef struct {
int x;
int y;
} Point;
typedef struct {
Point top_left;
Point bottom_right;
} Rectangle;
// 计算矩形面积
int rectangle_area(const Rectangle *rect) {
int width = rect->bottom_right.x - rect->top_left.x;
int height = rect->bottom_right.y - rect->top_left.y;
return width * height;
}
链表
链表提供动态插入和删除,对于已知位置的操作具有O(1)的时间复杂度。
#include <stdio.h>
#include <stdlib.h>
// 单链表节点
typedef struct Node {
int data;
struct Node *next;
} Node;
typedef struct {
Node *head;
size_t size;
} LinkedList;
// 创建新链表
LinkedList *list_create(void) {
LinkedList *list = malloc(sizeof(LinkedList));
if (!list) return NULL;
list->head = NULL;
list->size = 0;
return list;
}
// 在开头插入
int list_prepend(LinkedList *list, int data) {
Node *node = malloc(sizeof(Node));
if (!node) return -1;
node->data = data;
node->next = list->head;
list->head = node;
list->size++;
return 0;
}
// 在末尾插入
int list_append(LinkedList *list, int data) {
Node *node = malloc(sizeof(Node));
if (!node) return -1;
node->data = data;
node->next = NULL;
if (!list->head) {
list->head = node;
} else {
Node *current = list->head;
while (current->next) {
current = current->next;
}
current->next = node;
}
list->size++;
return 0;
}
// 移除第一个出现的值
int list_remove(LinkedList *list, int data) {
if (!list->head) return -1;
// 移除头节点
if (list->head->data == data) {
Node *temp = list->head;
list->head = list->head->next;
free(temp);
list->size--;
return 0;
}
// 移除其他节点
Node *current = list->head;
while (current->next) {
if (current->next->data == data) {
Node *temp = current->next;
current->next = current->next->next;
free(temp);
list->size--;
return 0;
}
current = current->next;
}
return -1; // 未找到
}
// 释放整个链表
void list_free(LinkedList *list) {
if (!list) return;
Node *current = list->head;
while (current) {
Node *next = current->next;
free(current);
current = next;
}
free(list);
}
双向链表
双向链表允许双向遍历,具有前向和后向指针。
#include <stdlib.h>
// 双向链表节点
typedef struct DNode {
int data;
struct DNode *prev;
struct DNode *next;
} DNode;
typedef struct {
DNode *head;
DNode *tail;
size_t size;
} DoublyLinkedList;
// 创建新双向链表
DoublyLinkedList *dlist_create(void) {
DoublyLinkedList *list = malloc(sizeof(DoublyLinkedList));
if (!list) return NULL;
list->head = NULL;
list->tail = NULL;
list->size = 0;
return list;
}
// 在末尾插入(使用尾指针为O(1))
int dlist_append(DoublyLinkedList *list, int data) {
DNode *node = malloc(sizeof(DNode));
if (!node) return -1;
node->data = data;
node->next = NULL;
node->prev = list->tail;
if (list->tail) {
list->tail->next = node;
} else {
list->head = node;
}
list->tail = node;
list->size++;
return 0;
}
// 从末尾移除(使用尾指针为O(1))
int dlist_pop(DoublyLinkedList *list, int *data) {
if (!list->tail) return -1;
*data = list->tail->data;
DNode *node = list->tail;
if (list->tail->prev) {
list->tail = list->tail->prev;
list->tail->next = NULL;
} else {
list->head = NULL;
list->tail = NULL;
}
free(node);
list->size--;
return 0;
}
二叉树
二叉树以层次结构组织数据,实现高效的搜索、插入和遍历操作。
#include <stdio.h>
#include <stdlib.h>
// 二叉树节点
typedef struct TreeNode {
int data;
struct TreeNode *left;
struct TreeNode *right;
} TreeNode;
// 创建新树节点
TreeNode *tree_node_create(int data) {
TreeNode *node = malloc(sizeof(TreeNode));
if (!node) return NULL;
node->data = data;
node->left = NULL;
node->right = NULL;
return node;
}
// 插入到二叉搜索树
TreeNode *bst_insert(TreeNode *root, int data) {
if (!root) {
return tree_node_create(data);
}
if (data < root->data) {
root->left = bst_insert(root->left, data);
} else if (data > root->data) {
root->right = bst_insert(root->right, data);
}
return root;
}
// 在二叉搜索树中搜索
TreeNode *bst_search(TreeNode *root, int data) {
if (!root || root->data == data) {
return root;
}
if (data < root->data) {
return bst_search(root->left, data);
}
return bst_search(root->right, data);
}
// 中序遍历(对于BST为排序顺序)
void tree_inorder(TreeNode *root) {
if (!root) return;
tree_inorder(root->left);
printf("%d ", root->data);
tree_inorder(root->right);
}
// 释放整个树
void tree_free(TreeNode *root) {
if (!root) return;
tree_free(root->left);
tree_free(root->right);
free(root);
}
哈希表
哈希表使用哈希函数和冲突解决提供O(1)平均情况的插入、删除和查找。
#include <stdlib.h>
#include <string.h>
#define HASH_TABLE_SIZE 100
// 哈希表条目
typedef struct Entry {
char *key;
int value;
struct Entry *next; // 用于冲突链
} Entry;
// 哈希表
typedef struct {
Entry *buckets[HASH_TABLE_SIZE];
size_t size;
} HashTable;
// 哈希函数(djb2)
unsigned long hash(const char *str) {
unsigned long hash = 5381;
int c;
while ((c = *str++)) {
hash = ((hash << 5) + hash) + c;
}
return hash % HASH_TABLE_SIZE;
}
// 创建哈希表
HashTable *hashtable_create(void) {
HashTable *table = malloc(sizeof(HashTable));
if (!table) return NULL;
for (int i = 0; i < HASH_TABLE_SIZE; i++) {
table->buckets[i] = NULL;
}
table->size = 0;
return table;
}
// 插入或更新键值对
int hashtable_set(HashTable *table, const char *key, int value) {
unsigned long index = hash(key);
Entry *entry = table->buckets[index];
// 检查键是否存在
while (entry) {
if (strcmp(entry->key, key) == 0) {
entry->value = value;
return 0;
}
entry = entry->next;
}
// 创建新条目
Entry *new_entry = malloc(sizeof(Entry));
if (!new_entry) return -1;
new_entry->key = strdup(key);
if (!new_entry->key) {
free(new_entry);
return -1;
}
new_entry->value = value;
new_entry->next = table->buckets[index];
table->buckets[index] = new_entry;
table->size++;
return 0;
}
// 通过键获取值
int hashtable_get(HashTable *table, const char *key, int *value) {
unsigned long index = hash(key);
Entry *entry = table->buckets[index];
while (entry) {
if (strcmp(entry->key, key) == 0) {
*value = entry->value;
return 0;
}
entry = entry->next;
}
return -1; // 未找到
}
// 释放哈希表
void hashtable_free(HashTable *table) {
if (!table) return;
for (int i = 0; i < HASH_TABLE_SIZE; i++) {
Entry *entry = table->buckets[i];
while (entry) {
Entry *next = entry->next;
free(entry->key);
free(entry);
entry = next;
}
}
free(table);
}
栈和队列
栈(LIFO)和队列(FIFO)是基础的抽象数据类型,具有许多应用。
#include <stdlib.h>
#include <stdbool.h>
// 使用动态数组的栈
typedef struct {
int *data;
size_t size;
size_t capacity;
} Stack;
// 创建栈
Stack *stack_create(size_t initial_capacity) {
Stack *stack = malloc(sizeof(Stack));
if (!stack) return NULL;
stack->data = malloc(initial_capacity * sizeof(int));
if (!stack->data) {
free(stack);
return NULL;
}
stack->size = 0;
stack->capacity = initial_capacity;
return stack;
}
// 压栈
int stack_push(Stack *stack, int value) {
if (stack->size >= stack->capacity) {
size_t new_capacity = stack->capacity * 2;
int *new_data = realloc(stack->data,
new_capacity * sizeof(int));
if (!new_data) return -1;
stack->data = new_data;
stack->capacity = new_capacity;
}
stack->data[stack->size++] = value;
return 0;
}
// 出栈
int stack_pop(Stack *stack, int *value) {
if (stack->size == 0) return -1;
*value = stack->data[--stack->size];
return 0;
}
// 查看栈顶
int stack_peek(Stack *stack, int *value) {
if (stack->size == 0) return -1;
*value = stack->data[stack->size - 1];
return 0;
}
// 检查栈是否为空
bool stack_is_empty(Stack *stack) {
return stack->size == 0;
}
// 释放栈
void stack_free(Stack *stack) {
if (stack) {
free(stack->data);
free(stack);
}
}
最佳实践
- 始终将指针初始化为NULL,以防止访问未初始化的内存
- 在使用分配的内存之前检查malloc/calloc/realloc的返回值
- 以分配顺序的反序释放所有分配的内存
- 使用sizeof(变量)而不是sizeof(类型)以提高可维护性
- 跟踪数据结构大小以避免缓冲区溢出
- 当函数不应修改数据时使用const指针
- 显式初始化所有结构体成员以避免未定义行为
- 为数据结构操作记录时间和空间复杂度
- 使用typedef为结构体和指针创建更清晰的类型名称
- 为所有自定义数据结构实现销毁/释放函数
常见陷阱
- 忘记释放内存,导致长期运行程序中的内存泄漏
- 访问已释放的内存(释放后使用),导致未定义行为
- 多次释放同一内存(双重释放),导致堆损坏
- malloc后未检查NULL,导致段错误
- 使用未初始化的指针,访问随机内存位置
- 由于未跟踪数组边界导致的缓冲区溢出
- 返回指向局部变量的指针,这些变量会超出作用域
- 插入/删除操作后未更新大小计数器
- 链式结构中的循环引用没有适当清理
- 需要深拷贝时使用浅拷贝,共享可变状态
何时使用C数据结构
使用C数据结构当需要:
- 对内存布局和访问模式的最大控制
- 没有垃圾收集的最小内存开销
- 实时系统的可预测性能特性
- 与硬件或低级系统接口的集成
- 构建其他语言使用的基础库
- 理解高级抽象的内部工作原理
- 用于学习算法和复杂性分析的教育目的
- 资源有限的嵌入式系统需要效率
- 性能关键代码,每个字节都很重要
- 需要维护或扩展的遗留代码库