C语言自定义SimpleMap结构体值丢失问题及解决方法
C语言简易Map结构体值丢失问题解决
问题现象
用C语言实现的简易Map(字典)出现值意外丢失的情况:执行测试函数test_simple_map时,首次遍历能正常输出键值对,但后续遍历和查询时数据全部错乱或丢失,测试输出如下:
0 -> 5 1 -> 7 2 -> 3 -------------------------------------- 40 -> 0 0 -> 0 0 -> 0 -------------------------------------- Value for key 'apple': 0 Is a not found zero: FALSE Value for key 'banana': 0 Is a not found zero: TRUE Value for key 'orange': 0 Is a not found zero: TRUE Value for key 'grape': 0 Is a not found zero: TRUE
原实现代码
#include <stdio.h> #include <stdbool.h> typedef struct { unsigned char key; unsigned char value; } KeyValuePair; typedef struct { KeyValuePair *pairs; unsigned char max_size; unsigned char size; bool not_found; } SimpleMap; /** * Initializes a given "SimpleMap" struct with given size * * @param map Pointer to simple map struct * @param size Your desired size if this simple map */ void simple_map_init(SimpleMap* map, const unsigned char size) { KeyValuePair empty_pairs[size]; map->pairs = empty_pairs; map->max_size = size; map->size = 0; map->not_found = false; } /** * Searches through the map array, for a given key. If key is found, the corresponding value will be returned. * If key is NOT found, zero will be returned return. * * NOTE: Since zero is an absolutely valid value within a map, you may look up the "not_found" attribute * of your map, to distinguish between a zero as value or a "NOT FOUND" zero! * * @param map Pointer to simple map struct * @param key Specific key you are looking for * @return Value if key was found, 0 otherwise */ const unsigned char simple_map_get(SimpleMap* map, const unsigned char key) { for(unsigned short i = 0; i < map->size; i++) { if(map->pairs[i].key == key) { map->not_found = false; return map->pairs[i].value; } } map->not_found = true; return 0; } /** * Adds/Overwrites given key with given value * * @param map Pointer to simple map struct * @param key Key * @param value Value * @return TRUE if given key/value pair was added successfully, FALSE otherwise */ bool simple_map_put(SimpleMap* map, const unsigned char key, const unsigned char value) { for(unsigned short i = 0; i < map->size; i++) { if(map->pairs[i].key == key) { map->pairs[i].value = value; return true; } } if (map->size < map->max_size) { KeyValuePair pair = {key, value}; map->pairs[map->size++] = pair; return true; } else { printf("Map is full!\n"); return false; } } /** * Removes a key/value pair with given key, by removing the target key pair & shifting * remaining elements of the array to "position - 1", and reducing the size by 1. * * @param map Pointer to simple map struct * @param key Key to remove * @return TRUE if key was found and removed, FALSE otherwise */ bool simple_map_remove(SimpleMap* map, const unsigned short key) { bool found_key = false; unsigned short i; for(i = 0; i < map->size; i++) { KeyValuePair current_pair = map->pairs[i]; // printf("%d == %d\n", current_pair.key, key); if (current_pair.key == key) { found_key = true; break; } } if(found_key) { i++; for(; i < map->size; i++) { map->pairs[i-1] = map->pairs[i]; printf("Overwrite index from %d with index %d\n", i-1, i); } map->--size; return true; } else { return false; } } void test_simple_map() { SimpleMap map; simple_map_init(&map, 8); unsigned char key_apple = 0x00; unsigned char key_banana = 0x01; unsigned char key_orange = 0x02; unsigned char key_grape = 0x03; simple_map_put(&map, key_apple, 5); simple_map_put(&map, key_banana, 7); simple_map_put(&map, key_orange, 3); for(short i=0; i < map.size; i++) { printf("%d -> %d\n", map.pairs[i].key, map.pairs[i].value); } printf("\n--------------------------------------\n\n"); for(short i=0; i < map.size; i++) { printf("%d -> %d\n", map.pairs[i].key, map.pairs[i].value); } printf("\n--------------------------------------\n\n"); printf("Value for key 'apple': %d\n", simple_map_get(&map, key_apple)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); printf("Value for key 'banana': %d\n", simple_map_get(&map, key_banana)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); printf("Value for key 'orange': %d\n", simple_map_get(&map, key_orange)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); printf("Value for key 'grape': %d\n", simple_map_get(&map, key_grape)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); }
问题根源
原simple_map_init函数中,empty_pairs是栈上的局部数组。栈内存的生命周期仅限于函数执行期间,当simple_map_init返回后,该数组占用的栈空间会被系统回收并重新分配给其他函数使用,此时map->pairs指向的是一块无效内存。后续对map->pairs的读写操作都会访问非法内存,导致数据错乱或丢失。
修复方案
改用动态内存分配(malloc)在堆上分配内存,堆内存的生命周期由开发者手动控制,只要不调用free,内存就会保持有效,与Map对象的生命周期一致。同时需要新增内存释放函数,避免内存泄漏。
修改后的初始化函数
#include <stdlib.h> // 引入stdlib.h以使用malloc和free void simple_map_init(SimpleMap* map, const unsigned char size) { map->pairs = malloc(size * sizeof(KeyValuePair)); map->max_size = size; map->size = 0; map->not_found = false; }
新增内存释放函数
void simple_map_del(SimpleMap* map) { free(map->pairs); map->pairs = NULL; // 置空指针,避免悬空指针 map->size = 0; map->max_size = 0; }
补充修复其他小问题
simple_map_remove函数中的map->--size是语法错误,应改为map->size--。simple_map_remove的参数key类型为unsigned short,与结构体中的unsigned char类型不统一,建议改为unsigned char。
修复后的测试函数
需要在测试结束后调用simple_map_del释放内存:
void test_simple_map() { SimpleMap map; simple_map_init(&map, 8); unsigned char key_apple = 0x00; unsigned char key_banana = 0x01; unsigned char key_orange = 0x02; unsigned char key_grape = 0x03; simple_map_put(&map, key_apple, 5); simple_map_put(&map, key_banana, 7); simple_map_put(&map, key_orange, 3); for(short i=0; i < map.size; i++) { printf("%d -> %d\n", map.pairs[i].key, map.pairs[i].value); } printf("\n--------------------------------------\n\n"); for(short i=0; i < map.size; i++) { printf("%d -> %d\n", map.pairs[i].key, map.pairs[i].value); } printf("\n--------------------------------------\n\n"); printf("Value for key 'apple': %d\n", simple_map_get(&map, key_apple)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); printf("Value for key 'banana': %d\n", simple_map_get(&map, key_banana)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); printf("Value for key 'orange': %d\n", simple_map_get(&map, key_orange)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); printf("Value for key 'grape': %d\n", simple_map_get(&map, key_grape)); printf("Is a not found zero: %s\n", map.not_found ? "TRUE" : "FALSE"); simple_map_del(&map); // 释放内存 }
验证结果
修复后再次执行测试函数,两次遍历和查询都会输出正确的键值对,不会出现数据丢失的情况。
内容的提问来源于stack exchange,提问作者SimpleJack
相关产品推荐
相关产品推荐

