将C ++中的字符串分解为单独的行和参数(作为2D向量)时,在尝试解析函数之间的向量时,可能会引起有趣的访问冲突问题。 在代码示例中,已经进行了很多尝试来确保数据与函数之间的传递是独立的对象,绝不是引用。
segregate.hpp
#pragma once
#include <vector>
#include <string>
/*
Purpose:
To to take a whole file as a string,
and break it up into individual words
*/
namespace Segregate {
// Module types
typedef std::vector< std::string > ParamArray;
struct StrCommand{
unsigned long line;
ParamArray param;
};
typedef std::vector< StrCommand > StrCommands;
bool IsParamBreak(char val);
bool IsLineBreak(char val);
ParamArray Parameterize(std::string str);
StrCommands Fragment(std::string str);
}
#include "./segregate.cpp"
segregate.cpp
#include "./segregate.hpp"
namespace Segregate{
bool IsParamBreak(char val){
if (val == ' '){
return true;
}else if (val == '\t'){
return true;
}
return false;
};
bool IsLineBreak(char val){
if (val == '\n'){
return true;
}
return false;
};
// Splits a single line into individual parameters
ParamArray Parameterize(std::string str){
str.append(" "); // Ensures that the loop will cover all segments
unsigned long length = str.size();
unsigned long comStart = 0;
ParamArray res;
// Ignore carrage returns
// Windows artifact
if (str[0] == '\r'){
comStart = 1;
}
// Ignore indentation
// Find the start of actual content
while (comStart < length && IsParamBreak(str[comStart])){
comStart++;
}
// Count the number of parameters
unsigned long vecLen = 0;
for (unsigned long i=comStart; i<length; i++){
if ( IsParamBreak(str[i]) ){
vecLen++;
}
}
res.reserve(vecLen);
// Scan will fail if there is no data
if (length == 0){
return res;
}
// Slice the the string into parts
unsigned long toIndex = 0;
unsigned long cursor = comStart;
for (unsigned long i=cursor; i<length; i++){
if (IsParamBreak(str[i]) == true){
// Transfer the sub-string to the vector,
// Ensure that the data is it's own, and not a reference
res[toIndex].reserve(i-cursor);
// Error here
res[toIndex].assign( str.substr(cursor, i-cursor) );
cursor = i+1;
toIndex++;
}
}
return res;
};
StrCommands Fragment(std::string str){
str.append("\n"); // Ensures that the loop will cover all segments
unsigned long length = str.size();
// Result
StrCommands res;
// Count lines
// Ignoring empty lines
unsigned long vecLen = 1;
for (unsigned long i=0; i<length; i++){
if (IsLineBreak(str[i])){
vecLen++;
}
}
res.reserve(vecLen);
// Ignore 'empty' strings as they may cause errors
if (vecLen == 0){
return res;
}
// Read lines
unsigned long toIndex = 0;
unsigned long cursor = 0;
for (unsigned long i=0; i<length; i++){
if (IsLineBreak(str[i])){
// Error here
res[toIndex].param = ParamArray( Parameterize( std::string(str.substr(cursor, i-cursor)) ) );
res[toIndex].line = i+1;
// Ignore blank lines
if (res[toIndex].param.size() == 0){
vecLen--;
}else{
toIndex++;
}
cursor = i+1;
}
}
// Shrink the result due to undersizing for blank lines
res.reserve(vecLen);
return res;
};
}
内存访问冲突通常发生在第66和108行(当元素数据本地存储在向量中时)。它似乎是在赋值阶段发生的,这是通过在解析之后使用一个中间临时变量直接存储结果来推断的。 该错误也可能在vector :: reserve()期间发生,但发生频率较低。
注意::在Windows上没有直接的错误消息:
在fibre.exe中的0x00A20462处引发异常:0xC0000005:访问 违反读取位置0xBAADF009。
仅在使用“ Visual Studio Code的C / C ++扩展”调试时才能看到,而不是在常规终端执行中看到。
但是在Ubuntu上它输出:
分段错误(核心转储)
答案 0 :(得分:4)
您正在对引导程序调用{{1}},该引导程序分配了用于存储对象的内存,但没有构造它们。然后,当您尝试使用尚未构造的对象的方法时,很可能会崩溃。
有2种可能的解决方案,要么调用reserve
而不是resize
,要么调用reserve
在向量的末尾构造新对象。