C++向<string>量元素传递到函数会导致 SIGSEGV 崩溃



正如标题所说,问题是当我试图将第二个向量元素传递给函数"solveSide"时。这个向量包含两个字符串标记,它们是从一个用户定义函数的较大字符串中获得的。

程序的工作方式如下:

  • 将文件中的一行读取为字符串
  • 调用"stringSplit"函数从该字符串中获取两个标记,并将它们放入向量中
  • 为矢量中的第一个元素调用"solveSide">
  • 为矢量中的第二个元素调用"solveSide">

问题出现在对"solveSide"的第二次调用中,因为程序在进入函数体之前崩溃。

如果我删除对"solveSide"函数的第二次调用,程序运行得很好。如果我试图在不删除该行的情况下输出向量的两个元素,它不会输出任何内容,但删除了它后,它会输出两个元素。

在调试模式下,当它调用"std::string::_M_mutate(unsigned int,unsigned整型,unsignedint)"(如调用堆栈所示)时,它会抛出SIGSEGV。

碰撞代码:

ifstream fin("ecuatii.in");
string ecuatie;
vector<string> sideSplit;
fin>>ecuatie;
sideSplit = stringSplit(ecuatie, "=");
fout<<sideSplit[0]<<"="<<sideSplit[1]<<"n";
solveSide(sideSplit[0], leftCoeffSum, leftSum);
solveSide(sideSplit[1], rightCoeffSum, rightSum);

stringSplit函数:

vector<string> stringSplit(string str, const char *delim){
char *strCopy = new char [str.size()+1];
char *token;
char **tokens = new char* [(str.size()+1)*2];
unsigned tokenCount = 0;
strcpy(strCopy, str.c_str());
token = strtok(strCopy, delim);
while(token != NULL){
tokens[tokenCount++] = token;
token = strtok(NULL, delim);
}
vector<string> splits(tokenCount);
for(unsigned i = 0; i < tokenCount; ++i){
splits[i] = "";
splits[i] += tokens[i];
}
delete[] strCopy;
for(unsigned i = 0; i < tokenCount; ++i){
delete[] tokens[i];
}
delete[] tokens;
return splits;
}

stringChunkToFloat函数(现在我知道了,应该使用"substr()"):

float stringChunkToFloat(string str, unsigned posStart, unsigned posEnd){
char numberStr[20];
for(unsigned i = posStart; i <= posEnd; ++i){
numberStr[i-posStart] = str[i];
}
numberStr[posEnd-posStart+1] = '';
float number = atof(numberStr);
return number;

}

solveSide函数:

void solveSide(string &side, float &coeffSum, float &sum){
coeffSum = 0;
sum = 0;
unsigned posStart = side.find_first_of("+-", 1);
unsigned posEnd;
float currentNumber;
if(side.size() == 1){
if(side[0] == 'x'){
coeffSum = 1;
}else{
sum = atof(side.c_str());
}
return;
}
if(side[0] == 'x'){
coeffSum += 1;
}else if(side[1] == 'x' && side[0] == '-'){
coeffSum -= 1;
}else if(side[posStart-1] == 'x'){
currentNumber = stringChunkToFloat(side, 0, posStart-2);
coeffSum += currentNumber;
}else{
currentNumber = stringChunkToFloat(side, 0, posStart-1);
sum += currentNumber;
}
while(posStart != string::npos && posStart != side.size()){
posEnd = side.find_first_of("+-", posStart+1);
if(posEnd == string::npos){
posEnd = side.size();
}
if(side[posStart+1] == 'x'){
if(side[posStart] == '+'){
coeffSum += 1;
}else{
coeffSum -= 1;
}
}else if(side[posEnd-1] == 'x'){
currentNumber = stringChunkToFloat(side, posStart, posEnd-2);
coeffSum += currentNumber;
}else{
currentNumber = stringChunkToFloat(side, posStart, posEnd-1);
sum += currentNumber;
}
posStart = posEnd;
}
}

输入字符串:2.0x-4+5x+300=98x

这个块不仅没有必要,而且还为您带来了未定义行为的乐趣:

for(unsigned i = 0; i < tokenCount; ++i){
delete[] tokens[i];
}

想想看:您为tokens分配了内存,而不是为单个令牌本身分配内存。毕竟,它们是指向标记化字符串的指针(std::strtok具有破坏性)。

更好的是,如果可以避免的话,根本不要使用原始内存:

vector<string> stringSplit(const string& str, const char *delim){
// Note: if you use C++11, you can use std::string::data() 
// and don't need a copy to a vector, just take the string by value
vector<char> strCopy(str.begin(), str.end());
strCopy.push_back('');
vector<char*> tokens;
{        
char * token = strtok(strCopy.data(), delim);
while(token != NULL){
tokens.push_back(token);
token = strtok(NULL, delim);
}
}
vector<string> splits(tokens.size();
for(unsigned i = 0; i < tokens.size(); ++i){
splits[i] = "";
splits[i] += tokens[i];
}
return splits;
}

我发现了这个问题,它更多的是一个思考错误:

当函数solveSide以值为98x的第二个字符串作为自变量调用时,它会在搜索startPos = npos之后搜索"+""-"符号,因为它找不到它,并且它会在while:之前进入这种情况

}else{
currentNumber = stringChunkToFloat(side, 0, posStart-1);
sum += currentNumber;
}

当它试图获取从位置0npos的数字时,它抛出SIGSEGV(分段故障),因为它试图从传递给stringChunkToFloat的字符串中访问未分配的内存

使用vector更安全的方法是执行splits.prushback()方法和size更安全,如spits.size()。在分配给vector'成员之前,始终检查vector.size(。

最新更新