recently I decide to debug my application with valgrind.
I've resolved a lot of errors, but can't this one.
==12205== Invalid read of size 8
==12205== at 0x37E1864C40: std::_Rb_tree_increment(std::_Rb_tree_node_base*) (in /usr/lib64/libstdc++.so.6.0.8)
==12205== by 0x40393C: readConfig(std::string) (stl_tree.h:257)
==12205== by 0x4058BE: main (application.cpp:42)
==12205== Address 0x5589b88 is 24 bytes inside a block of size 48 free'd
==12205== at 0x4A05A33: operator delete(void*) (vg_replace_malloc.c:346)
==12205== by 0x4067AD: std::_Rb_tree<std::string, std::pair<std::string const, std::string>, std::_Select1st<std::pair<std::string const, std::string> >, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >::erase(std::_Rb_tree_iterator<std::pair<std::string const, std::string> >, std::_Rb_tree_iterator<std::pair<std::string const, std::string> >) (new_allocator.h:94)
==12205== by 0x406841: std::_Rb_tree<std::string, std::pair<std::string const, std::string>, std::_Select1st<std::pair<std::string const, std::string> >, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >::erase(std::string const&) (stl_tree.h:1215)
==12205== by 0x403934: readConfig(std::string) (stl_map.h:461)
==12205== by 0x4058BE: main (application.cpp:42)
Part of my code:
string config_file;
if (strlen(getParam("config") . c_str()) > 0)
{
config_file = getParam("config");
}
else
config_file = string("default.conf");
if (access(config_file . c_str(), 0) == -1)
{
printf("Config file \"%s\" not exists\n", config_file . c_str());
exit(1);
}
if (!readConfig(config_file))
{
printf("Application error: read config file\n");
exit(1);
}
String #42:
if (!readConfig(config_file))
Please try to help me.
Thanks in advance!
Update #1:
I apologize for so large function :(
bool readConfig(string filename)
{
time_t rawtime;
struct tm * timeinfo;
time ( &rawtime );
timeinfo = localtime ( &rawtime );
map<string,string> tmp_buff;
ifstream ifs( filename.c_str() );
string temp,tmp;
int i;
unsigned int MAX_MATCH = 40;
regex_t re;
char *pattern = "([^=]+)=(.+)";
const char* s;
map<int,string> matches_tmp;
map<string,string>::const_iterator it;
char s1[1024];
size_t rm;
regmatch_t pmatch[MAX_MATCH];
regcomp(&re, pattern, REG_ICASE|REG_EXTENDED|REG_NOSUB);
if ((rm = regcomp (&re, pattern, REG_EXTENDED)) != 0)
{
printf("Invalid expression:'%s'\n",pattern);
return false;
}
int start[2]={0},end[2]={0},current[2]={0};
char * b;
string substr;
bool start_time=false,inside_time=false,error=false;
while( getline( ifs, temp ) )
{
tmp=trim(temp);
tmp=temp;
if(strlen(tmp.c_str())==0) continue;
s=tmp.c_str();
if(!regexec(&re, s, MAX_MATCH, pmatch, 0))
{
for(i=1;i<=2;i++)
{
strncpy (s1, s + pmatch[i].rm_so, pmatch[i].rm_eo - pmatch[i].rm_so);
s1[pmatch[i].rm_eo - pmatch[i].rm_so] = '\0';
matches_tmp[i]=trim((string)s1);
}
if(matches_tmp[1]==string("start-time"))
{
substr=matches_tmp[2].substr(0,2);
b=new char[substr.length()+1];
strcpy(b, substr.c_str() );
if(strlen(b)!=2) continue;
start[0]=atoi(b);
//free(b);
substr=matches_tmp[2].substr(3,2);
b=new char[substr.length()+1];
strcpy(b, substr.c_str() );
if(strlen(b)!=2) continue;
start[1]=atoi(b);
start_time=true;
continue;
}
if(matches_tmp[1]==string("end-time"))
{
start_time=false;
substr=matches_tmp[2].substr(0,2);
b=new char[substr.length()+1];
strcpy(b, substr.c_str() );
if(strlen(b)!=2) error=true;
end[0]=atoi(b);
substr=matches_tmp[2].substr(3,2);
b=new char[substr.length()+1];
strcpy(b, substr.c_str() );
if(strlen(b)!=2) error=true;
end[1]=atoi(b);
if(error)
{
printf("ERROR1\n");
error=false;
continue;
}
current[0]=timeinfo->tm_hour;
current[1]=timeinfo->tm_min;
if(end[0]<start[0])
{
if(
(current[0]<start[0] && current[0]>end[0]) ||
(current[0]==start[0] && current[1]<start[1]) ||
(current[0]==end[0] && current[1]>end[1])
)
{
error=true;
}
}else
{
if(
(current[0]<start[0]) ||
(current[0]>start[0] && current[0]>end[0]) ||
(current[0]==start[0] && current[1]<start[1]) ||
(current[0]==end[0] && current[1]>end[1])
)
{
error=true;
}
}
if(error)
{
error=false;
continue;
}
for (it = tmp_buff.begin(); it != tmp_buff.end(); ++it)
{
if(config.find( it->first ) != config.end()) config.erase(it->first);
config[it->first]=it->second;
tmp_buff.erase(it->first);
}
}
if(strlen(matches_tmp[1].c_str())==0) continue;
if(start_time)
{
tmp_buff[matches_tmp[1]]=matches_tmp[2];
}
else
config[matches_tmp[1]]=matches_tmp[2];
}
}
}
I suppose you are incrementing an invalid std::set or std::map iterator. This incorrect program produces a similar valgrind error:
#include <set>
int main () {
std::set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
for(std::set<int>::iterator it = s.begin(); it != s.end(); ++it) {
if(*it == 2) s.erase(it);
}
}
EDIT: Yep, you are doing exactly what I said:
for (it = tmp_buff.begin(); it != tmp_buff.end(); ++it)
{
if(config.find( it->first ) != config.end()) config.erase(it->first);
config[it->first]=it->second;
tmp_buff.erase(it->first);
}
The call to tmp_buff.erase(it->first) invalidates it. But, you subsequently increment it: ++it. This is not allowed.
Also, there is no reason to call config.erase. The entry in config will be implicity destroyed when it is overwritten in the next line. Try:
for (it = tmp_buff.begin(); it != tmp_buff.end(); ++it)
{
config[it->first]=it->second;
}
tmp_buff.clear();
Related
I need to write a program that uses a stack to verify if a string expression is balanced, in regards to the parenthesis, brackets, and curly braces contained in it. The string's are to be inputted by the user, and all errors (i.e. mismatched parenthesis, brackets, and curly braces) need to be pointed out by a caret on the next line, directly under it, like this:
(a bit hard to show here...)
(()
^
In my "balanced" function, I am taking the current index of the loop, and assigning it to either "unmatchedRightPosition" or "unmatchedLeftPosition," whichever one is needed, at the time. I think that a lot of my program works already, but I'm having problems with placing the carets under the errors. My professor suggested that I may choose to use a stack class that holds structs, where each struct contains both a char and the char's position, but I am a bit puzzled by that.
Thanks for looking
#include <iostream>
#include <string>
#include <stdlib.h>
#include <sstream>
using namespace std;
struct Stack{
static const unsigned MAX_SIZE = 5;
char data[ MAX_SIZE ];
unsigned size;
};
struct Stack2{
unsigned unmatchedLeftPos, unmatchedRightPos;
};
void initialize( Stack & stack );
void show( const Stack & stack );
unsigned getSize( const Stack & stack );
void push( Stack & stack, char c );
char pop( Stack & stack );
char top( const Stack & stack );
bool die( const string & msg );
bool balanced (unsigned & unmatchedLeftPos, unsigned & unmatchedRightPos, const string & expr);
int main(){
Stack2 s2;
cout << "\nPlease enter your expression - enter a blank line to quit. \n";
for(;;){
string line;
getline(cin, line);
if( line.size() == 0 ) break;
if (balanced(s2.unmatchedLeftPos, s2.unmatchedRightPos, line) == 1){
cout << "OK\n";
}
else if (balanced(s2.unmatchedLeftPos, s2.unmatchedRightPos, line) == 0){
cout << string(s2.unmatchedLeftPos, ' ') << '^';
cout << string(s2.unmatchedRightPos, ' ') << '^';
}
}
return 0;
}
void initialize( Stack & stack ){
stack.size = 0;
}
void show( const Stack & stack ){
cout <<"[" << stack.size <<"]:";
for( unsigned i = 0; i < stack.size; i++ )
cout <<stack.data[i];
cout <<endl;
} // show
unsigned getSize( const Stack & stack ) {return stack.size;}
void push( Stack & stack, char c ){
if( stack.size == Stack::MAX_SIZE ) die( "push: overflow" );
stack.data[stack.size++] = c;
} // push
char pop( Stack & stack ){
if( stack.size == 0 ) die( "pop: underflow" );
return stack.data[--stack.size];
} // pop
char top( const Stack & stack ){
if( stack.size == 0 ) die( "top: underflow" );
return stack.data[stack.size-1];
} // pop
bool die( const string & msg ){
cerr <<endl <<"Fatal error: " << msg <<endl;
exit( EXIT_FAILURE );
}
bool balanced (unsigned & unmatchedLeftPos, unsigned & unmatchedRightPos, const string & expr){
Stack s;
initialize(s);
unsigned i;
for (i = 0; i < expr.size(); i++){
char c = expr[i];
if( expr.size() == Stack::MAX_SIZE) {
die( "push: overflow" );
}
if (c == '(')
{
push(s, c);
}
else if (c == '['){
push(s, c);
}
else if (c == '{'){
push(s, c);
}
if (s.size == 0 && (c == ')' || c == ']' || c == '}'))
{
unmatchedRightPos = i;
return false;
}
else if (c == ')' && top(s) == '('){
pop(s);
}
else if (c == ']' && top(s) == '['){
pop(s);
}
else if (c == '}' && top(s) == '{'){
pop(s);
}
}
if (s.size == 0){
return true;
}
else if (top(s) == '(' || top(s) == '[' || top(s) == '{'){
unmatchedLeftPos = i;
return false;
}
}
You are currently using stack, with an array of characters:
char data[ MAX_SIZE ];
Instead, you would go for a struct, that holds both character and position in the input string
struct info {
char data;
int pos;
};
info data[ MAX_SIZE ];
So at the end, you just check your stack, and in addition to invalid characters, you also have the position in the input string.
Hope that helps.
You can move your main to the bottom to avoid forward function declarations. You also don't need to use another stack for the errors (actually I think it's easier if you don't). You just need to hold both the bracket and its position on a single stack, i.e.
struct Item
{
char bracket;
size_t position;
}
std::stack<Item> st;
As well as either an array or, better a string initialized to the same length as the input string with all spaces which you change to '^' upon encountering an error, i.e.
std::string errorString(input.size(), ' ');
if ( /* brackets don't match */ )
{
errorString[st.top().position] = '^';
}
In case you cannot use STL stack, you need to modify your own to hold Item objects instead of char (i.e. Item data[ MAX_SIZE ];). Your code looks very much like C thought and it would be better if you made use of std::string and std::stack instead.
I need to read parameters from a txt file for my program in Linux. But the result is that some of the parameters read from the txt file have the correct value, but some of them have a wrong value. Somebody has met this problem? I have translated the format of the txt in windows into Linux with the command dos2unix. I need your help, Thanks.
The read function is as follows:
template <class T>int ReadFileVar(ifstream *inClientFile, const char var_name[], T *var)
{
//inClientFile - pointer to the previously opened File stream
//var_name - contains the name of the variable
//var - pointer to a long, the function will return the value of the variable in this
int length_var_name = (int) strlen(var_name);
char line[512];
int i, j;
while (inClientFile->getline(line,512))
{
if (line[0] != '/' && line[1] != '/')
{
i = 0;
while (line[i] != '\0')
{
if (!strncmp(&line[i],var_name,length_var_name))
{
j = i + length_var_name;
while (line[j] != '\0')
{
if ( line[j] >= '0' && line[j] <= '9')
{
*var = (T) atof(&line[j]);
inClientFile->seekg( 0, ios_base::beg ); //back to the beginning of the file
return 0;
}
j++;
}
}
i++;
}
}
}
cerr << var_name << " - cannot be found" << endl;
throw "error reading input data from: ";
return 1; //the specified variable was not found in the file
}
For example:
the parameters in the txt are as follows:,the type of them are long,
nx=100;
ny=100;
nz=100;
ipro=1;
jpro=1;
kpro=1;
but after reading the txt in my program I get these,
nx=100;
ny=100;
nz=15;
ipro=1;
jpro=1;
kpro=100;
I have tested the program under Windows, there it works!
Your code works for me, you must have an error somewhere else or an undefined behavior I didn't spot.
May I suggest a more C++ way to do exactly the same thing :
template <class T>
T ReadFileVar(ifstream& inClientFile, string var_name)
{
string line;
while (getline(inClientFile, line))
{
if (line[0] != '/' && line[1] != '/')
{
size_t pos = line.find(var_name);
if( pos != string::npos) {
pos = line.find('=', pos + 1);
if(pos == string::npos) {
throw std::exception();
}
istringstream iss(line.substr(pos + 1));
T result;
iss >> result;
inClientFile.seekg( 0, ios_base::beg );
return result;
}
}
}
throw std::exception();
}
You could also parse the whole file and store the result in a map instead of searching the whole file for each variable :
map<string, string> ParseFile(ifstream& inClientFile) {
map<string, string> result;
string line;
while (getline(inClientFile, line))
{
if (line[0] != '/' && line[1] != '/')
{
size_t pos = line.find('=');
if(pos == string::npos) {
throw std::exception();
}
string var_name = line.substr(0, pos);
string var_value = line.substr(pos + 1);
result[var_name] = var_value;
}
}
return result;
}
template <class T>
T ReadVar(map<string, string> data, string var_name)
{
map<string, string>::iterator it = data.find(var_name);
if(it == data.end()) {
throw exception();
}
string value = it->second;
istringstream iss(value);
T result;
iss >> result;
return result;
}
I have the code below:
std::string myName = "BLABLABLA";
//check if there are illegal characters
for (unsigned int i = 0; i < myName.length(); i++)
{
const char& c = myName[i];
if (!(isalnum(c) || (c == '_') || (c == '-')))
{
return 0;
}
}
This is the output of valgrind at line "const char& c = myName[i];"
==17249== 51 bytes in 1 blocks are possibly lost in loss record 116 of 224
==17249== at 0x4C2714E: operator new(unsigned long) (vg_replace_malloc.c:261)
==17249== by 0x602A498: std::string::_Rep::_S_create(unsigned long, unsigned long,
std::allocator<char> const&) (in /usr/lib64/libstdc++.so.6.0.16)
==17249== by 0x602A689: std::string::_M_mutate(unsigned long, unsigned long,
unsigned long) (in /usr/lib64/libstdc++.so.6.0.16)
==17249== by 0x602AFB5: std::string::_M_leak_hard() (in
/usr/lib64/libstdc++.so.6.0.16)
==17249== by 0x602B0A4: std::string::operator[](unsigned long) (in /
/usr/lib64/libstdc++.so.6.0.16)
I do not see anything wrong with this...
Yes, it's the horrible COW implementation!
You can also force use of the const (and therefore non-mutating) overloads like so:
std::string const myName = "BLABLABLA";
//check if there are illegal characters
for (unsigned int i = 0; i < myName.length(); i++)
{
const char& c = myName[i];
if (!(isalnum(c) || (c == '_') || (c == '-')))
{
return 0;
}
}
or (if you don't want to modify the original string type):
std::string myName = "BLABLABLA";
std::string const &cref = myName;
//check if there are illegal characters
for (unsigned int i = 0; i < myName.length(); i++)
{
const char& c = cref[i];
if (!(isalnum(c) || (c == '_') || (c == '-')))
{
return 0;
}
}
etc.
COW reference, because I knew I'd written something about it somewhere.
I have the following function which inserts pair to STL map. Do I need to to allocate memory using new before insert?
char* foo(char* lnumber)
{
char* sData = “A,B,C”;
Char delim[] = “,”;
typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
TStrStrMap tMap;
if(strstr(sData,delim) != 0)
{
tok = strtok( sData, delim);
while( ( tok != NULL))
{
int bytes = strlen(tok)+1;
char* ll = new char[bytes];
memset(ll,0,bytes);
strcpy(ll,tok);
ll[bytes] = '\0';
int bytes1 = strlen("yes")+1;
char* ll1 = new char[bytes1];
memset(ll1,0,bytes1);
strcpy(ll1,”yes”);
ll1[bytes1] = '\0';
tMap.insert(TStrStrPair(ll,ll1));
tok = strtok( NULL, delim);
}
}
std::string strValue = tMap[lnumber];
return(strdup(strValue.c_str()));
}
To answer your specific question - no, you do not need to allocate the memory yourself given the declarations you have shown. std::string will manage the memory for the string values, std::pair will handle the memory of its std::string values, and std::map will handle the memory for its std::pair values.
Your current code is leaking every char[] buffer that you are allocating with 'new[]'. Your std::string values are making copies of the data, so you need to delete[] them whenyou are done with them, eg:
char* foo(char* lnumber)
{
char sData[] = "A,B,C";
char *delim = ",";
typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
TStrStrMap tMap;
if(strstr(sData, delim) != 0)
{
char *tok = strtok(sData, delim);
while (tok != NULL)
{
int bytes = strlen(tok)+1;
char* ll = new char[bytes];
strcpy(ll, tok);
int bytes1 = strlen("yes")+1;
char* ll1 = new char[bytes1];
strcpy(ll1, "yes");
tMap.insert(TStrStrPair(ll,ll1));
delete[] ll; // <-- here
delete[] ll1; // <-- here
tok = strtok( NULL, delim);
}
}
std::string strValue = tMap[lnumber];
return strdup(strValue.c_str());
}
With that said, since std::string has a constructor that accepts char* input, your loop code can be greatly simplified to the following:
// you really should be using std::string instead
// of char* for the function's input and output...
//
char* foo(char* lnumber)
{
char sData[] = "A,B,C";
char *delim = ",";
typedef std::map<std::string, std::string> TStrStrMap;
typedef std::pair<std::string, std::string> TStrStrPair;
TStrStrMap tMap;
char *tok = strtok(sData, delim);
while (tok != NULL)
{
tMap.insert(TStrStrPair(tok, "yes"));
tok = strtok(NULL, delim);
}
std::string strValue = tMap[lnumber];
return strdup(strValue.c_str());
}
I have the following code (C++0x):
const set<char> s_special_characters = { '(', ')', '{', '}', ':' };
void nectar_loader::tokenize( string &line, const set<char> &special_characters )
{
auto it = line.begin();
const auto not_found = special_characters.end();
// first character special case
if( it != line.end() && special_characters.find( *it ) != not_found )
it = line.insert( it+1, ' ' ) + 1;
while( it != line.end() )
{
// check if we're dealing with a special character
if( special_characters.find(*it) != not_found ) // <----------
{
// ensure a space before
if( *(it-1) != ' ' )
it = line.insert( it, ' ' ) + 1;
// ensure a space after
if( (it+1) != line.end() && *(it+1) != ' ' )
it = line.insert( it+1, ' ');
else
line.append(" ");
}
++it;
}
}
with the crash pointing at the indicated line. This results in a segfault with this gdb backtrace:
#0 0x000000000040f043 in std::less<char>::operator() (this=0x622a40, __x=#0x623610, __y=#0x644000)
at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c++/4.5.2/bits/stl_function.h:230
#1 0x000000000040efa6 in std::_Rb_tree<char, char, std::_Identity<char>, std::less<char>, std::allocator<char> >::_M_lower_bound (this=0x622a40, __x=0x6235f0, __y=0x622a48, __k=#0x644000)
at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c++/4.5.2/bits/stl_tree.h:1020
#2 0x000000000040e840 in std::_Rb_tree<char, char, std::_Identity<char>, std::less<char>, std::allocator<char> >::find (this=0x622a40, __k=#0x644000)
at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c++/4.5.2/bits/stl_tree.h:1532
#3 0x000000000040e4fd in std::set<char, std::less<char>, std::allocator<char> >::find (this=0x622a40, __x=#0x644000)
at /usr/lib/gcc/x86_64-unknown-linux-gnu/4.5.2/../../../../include/c++/4.5.2/bits/stl_set.h:589
#4 0x000000000040de51 in ambrosia::nectar_loader::tokenize (this=0x7fffffffe3b0, line=..., special_characters=...)
at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:146
#5 0x000000000040dbf5 in ambrosia::nectar_loader::fetch_line (this=0x7fffffffe3b0)
at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:112
#6 0x000000000040dd11 in ambrosia::nectar_loader::fetch_token (this=0x7fffffffe3b0, token=...)
at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:121
#7 0x000000000040d9c4 in ambrosia::nectar_loader::next_token (this=0x7fffffffe3b0)
at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:72
#8 0x000000000040e472 in ambrosia::nectar_loader::extract_nectar<std::back_insert_iterator<std::vector<ambrosia::target> > > (this=0x7fffffffe3b0, it=...)
at ../../ambrosia/Library/Source/Ambrosia/nectar_loader.cpp:43
#9 0x000000000040d46d in ambrosia::drink_nectar<std::back_insert_iterator<std::vector<ambrosia::target> > > (filename=..., it=...)
at ../../ambrosia/Library/Source/Ambrosia/nectar.cpp:75
#10 0x00000000004072ae in ambrosia::reader::event (this=0x623770)
I'm at a loss, and have no clue where I'm doing something wrong. Any help is much appreciated.
EDIT: the string at the moment of the crash is
sub Ambrosia : lib libAmbrosia
UPDATE:
I replaced the above function following suggestions in comments/answers. Below is the result.
const string tokenize( const string &line, const set<char> &special_characters )
{
const auto not_found = special_characters.end();
const auto end = line.end();
string result;
if( !line.empty() )
{
// copy first character
result += line[0];
char previous = line[0];
for( auto it = line.begin()+1; it != end; ++it )
{
const char current = *it;
if( special_characters.find(previous) != not_found )
result += ' ';
result += current;
previous = current;
}
}
return result;
}
Another guess is that line.append(" ") will sometimes invalidate it, depending on the original capacity of the line.
You don't check that it != line.end() before the first time you dereference it.
I could not spot the error, I would suggest iterating slowly with the debugger since you have identitied the issue.
I'll just that in general, modifying what you are iterating over is extremely prone to failure.
I'd recommend using Boost Tokenizer, and more precisely: boost::token_iterator combined with boost::char_separator (code example included).
You could then simply build a new string from the first, and return the new string from the function. The speed up on computation should cover the memory allocation.