使用看似相同字符串键访问std::map时at()抛出std::out_of_range异常
std::map使用const char*作为键的访问异常问题解析
问题现象
使用const char*作为std::map的键时,字符串字面量可以正常调用at()访问,但std::string::c_str()的返回值调用时会抛出std::out_of_range异常。已验证两者字符串内容、长度、strcmp结果完全一致,但问题仍存在。
示例代码
#include <map> #include <string> #include <string.h> #include <stdio.h> typedef enum { CODING_RATE_4_5, // Modem default CODING_RATE_4_6 } lora_modem_cfg_coding_rate_t; static std::map<const char*, lora_modem_cfg_coding_rate_t> CreateCodingRateMap() { std::map<const char*, lora_modem_cfg_coding_rate_t> newMap; newMap["CODING_RATE_4_5"] = CODING_RATE_4_5; newMap["CODING_RATE_4_6"] = CODING_RATE_4_6; return newMap; } int main(void) { std::map<const char*, lora_modem_cfg_coding_rate_t> codingRateMap = CreateCodingRateMap(); const char* coding_rate_selection1 = "CODING_RATE_4_5"; std::string coding_rate_selection2_string = "CODING_RATE_4_5"; const char* coding_rate_selection2 = coding_rate_selection2_string.c_str(); printf("coding_rate_selection1: %s\n", coding_rate_selection1); printf("coding_rate_selection2: %s\n", coding_rate_selection2); printf("coding_rate_selection1 strlen: %ld\n", strlen(coding_rate_selection1)); printf("coding_rate_selection2 strlen: %ld\n", strlen(coding_rate_selection2)); printf("strcmp: %d\n", strcmp(coding_rate_selection1, coding_rate_selection2)); for (uint8_t idx = 0; idx < 20; idx++) printf("%2d %02X %02X\n", idx, coding_rate_selection1[idx], coding_rate_selection2[idx]); printf("\n"); lora_modem_cfg_coding_rate_t codingRate; codingRate = codingRateMap.at(coding_rate_selection1); printf("%s %d\n", __FILE__, __LINE__); fflush(stdout); codingRate = codingRateMap.at(coding_rate_selection2); printf("%s %d\n", __FILE__, __LINE__); fflush(stdout); return 0; }
运行输出
coding_rate_selection1: CODING_RATE_4_5 coding_rate_selection2: CODING_RATE_4_5 coding_rate_selection1 strlen: 15 coding_rate_selection2 strlen: 15 strcmp: 0 0 43 43 1 4F 4F 2 44 44 3 49 49 4 4E 4E 5 47 47 6 5F 5F 7 52 52 8 41 41 9 54 54 10 45 45 11 5F 5F 12 34 34 13 5F 5F 14 35 35 15 00 00 16 43 0A 17 4F D7 18 44 39 19 49 C4 map_test.cpp 47 terminate called after throwing an instance of 'std::out_of_range' what(): map::at Aborted (core dumped)
原因解析
std::map默认使用std::less<Key>作为比较器,当Key是const char*时,比较的是指针的内存地址,而非字符串内容:
- 字符串字面量(如
"CODING_RATE_4_5")会被编译器存入程序的常量字符串池,相同内容的字面量会复用同一个内存地址,因此coding_rate_selection1的地址和CreateCodingRateMap中插入的字面量地址一致,at()能找到对应条目。 std::string::c_str()返回的是指向std::string内部堆内存缓冲区的指针,这个地址和常量池中的字面量地址完全不同,map会判定这是一个不存在的键,进而抛出std::out_of_range异常。
解决方案
方案1:将map的键类型改为std::string(推荐)
std::string的std::less比较器会按字符串内容进行比较,完全符合预期,且无需额外处理:
// 修改map的键类型 static std::map<std::string, lora_modem_cfg_coding_rate_t> CreateCodingRateMap() { std::map<std::string, lora_modem_cfg_coding_rate_t> newMap; newMap["CODING_RATE_4_5"] = CODING_RATE_4_5; newMap["CODING_RATE_4_6"] = CODING_RATE_4_6; return newMap; } int main(void) { std::map<std::string, lora_modem_cfg_coding_rate_t> codingRateMap = CreateCodingRateMap(); // 其余代码无需修改,std::string会自动兼容const char*类型的键 }
方案2:为map自定义字符串内容比较器
如果必须使用const char*作为键,可以自定义比较器让map按字符串内容比较:
struct CStrCompare { bool operator()(const char* a, const char* b) const { return strcmp(a, b) < 0; } }; // 使用自定义比较器声明map static std::map<const char*, lora_modem_cfg_coding_rate_t, CStrCompare> CreateCodingRateMap() { std::map<const char*, lora_modem_cfg_coding_rate_t, CStrCompare> newMap; newMap["CODING_RATE_4_5"] = CODING_RATE_4_5; newMap["CODING_RATE_4_6"] = CODING_RATE_4_6; return newMap; } int main(void) { std::map<const char*, lora_modem_cfg_coding_rate_t, CStrCompare> codingRateMap = CreateCodingRateMap(); // 其余代码不变 }
注意:使用const char*作为键时,必须确保所有插入的键指针指向的内存不会被释放或修改,否则会导致map行为异常。
内容的提问来源于stack exchange,提问作者nabelekt
相关产品推荐
相关产品推荐

