所以,我可以看到strtok似乎是一个备受鄙视的函数,但它非常适合我在这个特定实例中的需求,并且我想避免在可能的情况下重写整个函数。当然,我愿意接受strtok必须要去的地方。
无论如何,这是我遇到的问题。此函数从配置文件中读取用户指定的字符串(这是第一行中发生的情况)。该字符串是逗号分隔的列表,包含用冒号分隔的数字对,如下所示:
int:float, int:float, int:float
我希望以某种方式存储这些值,将它们相互映射,其中int是键,浮点数是值。我编写的代码只是按照我想要的方式工作,只要第一个int只有一个数字,或者存在多个int:float对。如果字符串只有一个int:float对,第一个int是两个数字,那么函数将执行几次没有问题,但最终垃圾将被读入index_token和ratios_token字符串,程序将出现seg-fault。如果我在valgrind中运行程序,这不会发生,所以它必定是某种内存错误。每次执行此函数时,都会从文件中读取字符串。当我打印出const_ratios和比率时,它们就像每次都应该一样。
这是我的代码:
const char * const_ratios = m_world->GetConfig().NON_1_RESOURCE_RATIOS.Get();
cout << "Const_ratios: " << const_ratios;
char * ratios = new char[strlen(const_ratios)]; #make non const version of ratios
strcpy(ratios, const_ratios); #so that I can use strtok
cout << ", Ratios: " << ratios;
map<int, float> ratioMap;
char * ratio_tokens = strtok((char *)ratios, ",:");
while (ratio_tokens != NULL){
char * index_token = new char[strlen(ratio_tokens)];
strcpy(index_token, ratio_tokens);
cout <<", Index token: " << index_token;
ratio_tokens = strtok(NULL, ",:");
char * value_token = new char[strlen(ratio_tokens)];
strcpy(value_token, ratio_tokens);
cout << ", Value token: " << value_token << endl;
ratioMap[atoi(index_token)] = atof(value_token);
ratio_tokens = strtok(NULL, ",:");
有谁知道为什么会这样?我认为它必须与strtok(可能与strcpy联合)有关,但我看不出我错过了什么。
答案 0 :(得分:1)
你没有分配足够的内存。您分配strlen(ratio_tokens)
但是然后再复制一个字节。这是C风格字符串的烦恼之一 - C风格总是比字符串中的字符数大一个字节。由于您使用C ++进行编码,为什么不使用std::string
?
答案 1 :(得分:1)
对于C字符串,它需要字符串末尾的终止空字符,因此代码为:
char * ratios = new char[strlen(const_ratios)];
strcpy(ratios, const_ratios);
终止null char未附加字符串'ratios',这将导致其他函数出现问题。例如,函数'strcpy()',如果检查函数的实现,它会检查源字符串的终止null char以确定复制过程是否完成。因此,如果没有终止null char,它将导致内存错误。
所以上面的代码应该是这样的:
int n = strlen(const_ratios) +1
char * ratios = new char[n];
strcpy(ratios, const_ratios);
ratios[n-1] = '\0'