You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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;
}

补充修复其他小问题

  1. simple_map_remove函数中的map->--size是语法错误,应改为map->size--。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 11:23:11