Simple string assignment causing a segmentation fault?

3k Views Asked by At

I've tracked this problem through a lot my classes, and it surprised me, that the source of this error is a simple std::string = std::string operation.

I won't post the whole code, just the functions that are executed in sequence. I still think that there is too much code for SO standards, but I see no other option.

Some context notes:

  • OrderedPair is a public struct { int y, int x } in a separate lib.inc file.
  • Leading underscore marks a variable declared in the function header
  • Trailing underscore marks a class member variable

ncefm.cc -- basically the main file

std::vector<std::string> juststring = {"teststring", "another string", "foobar"};
List* list0 = new List(w0_, juststring); // Source of the error

list.cc -- constructor List() is called here

Just ignore the optional variables, they aren't called anyway

List::List(Frame* const _parent, std::vector<std::string> &_list,
  const OrderedPair &_pos = {0, 0}, const unsigned int &_spacing = 1,
  const unsigned int &_maxsize = 0) {
  pos_ = _pos;
  size_ = _list.size();
  spacing_ = _spacing;
  maxsize_ = _maxsize;
  parent_ = _parent;

  Fill(_list); //Source of the error

  Redraw();

  parent_->AddWidget(this);
}

list.cc -- Member function Fill()

list_ is a member variable of type std::vector

void List::Fill(std::vector<std::string> &_list) {
  for (unsigned int loop = size_; loop < (size_ + _list.size()); loop++) {
    list_.push_back(new Label(parent_, _list[loop], {pos_.y + (loop * spacing_),
      pos_.x}, maxsize_)); // source of the error (the constructor Label() )
  }
}

label.cc -- constructor Label() is called here

Label::Label(Frame* const _parent, std::string &_text,
  const OrderedPair &_pos = {0,0}, const unsigned int &_maxsize = 0) {
  pos_ = _pos;
  maxsize_ = _maxsize;
  parent_ = _parent;

  SetText(_text); // Source of the error

  parent_->AddWidget(this);
}

list.cc -- Member function SetText()

Here we finally are, the source of the error is...

void Label::SetText(std::string& _text) {
  if (maxsize_ != 0 && _text.length() > maxsize_) _text.resize(maxsize_);
  text_ = _text; // THIS?!
  size_ = text_.length();

  Redraw();
}

If I just comment out this line, the error dissapears, of course, that defeats the functionality. text_.assign(_text); doesn't work either.

label.h -- To show the header file and definition of some variable text_

class Label {
private:
  OrderedPair pos_;

  unsigned int size_;
  unsigned int maxsize_;
  std::string text_;
  Frame* parent_;

public:
  Label(Frame* const _parent, std::string &_text, const OrderedPair &_pos,
    const unsigned int &_maxsize);
  ~Label();

  inline const Frame* GetParent();
  inline unsigned int GetSize();

  inline std::string Text();
  void SetText(std::string&);

  void Move(const OrderedPair &_pos);
  void RMove(const OrderedPair &_pos);

  void Redraw();
  void Clear();
};

If this is too messy, or you think you need more information about my classes, ask me to add them here, or look at my GitHub repo (public) on the dev branch here.

3

There are 3 best solutions below

0
On BEST ANSWER
for (unsigned int loop = size_; loop < (size_ + _list.size()); loop++) 
{
    [...] _list[loop] [...]
}

You set size_ to _list.size() in the list constructor, so it means you start iterating your list from the end, and _list[loop] is out of bounds.

Didn't you mean this instead ?

for (unsigned int loop = 0 ; loop < _list.size() ; loop++) 
{
    [...] _list[loop] [...]
}
0
On

Looks like you are accessing elements outside vector's range (_list[loop]):

for (unsigned int loop = size_; loop < (size_ + _list.size()); loop++) {
  list_.push_back(new Label(parent_, _list[loop], {pos_.y + (loop * spacing_), pos_.x}, maxsize_));
0
On

Vendors of C++ implementations tend to apply a lot of effort to eliminating obvious flaws from their implementation of the standard library. So the odds of the actual cause of your problem being a simple std::string assignment are vanishingly small (approximately zero).

What will almost certainly be happening is that some code in your program - executed BEFORE the assignment occurs - will be exhibiting undefined behaviour. This might include falling off the end of an array (e.g. accessing x[3] where x is an array or a std::vector with less than three elements), dereferencing a null pointer, using an uninitialised variable.

One of the quainter properties of undefined behaviour is that the effect may not be immediate .... only for a crash to occur in some completely unrelated code that is subsequently executed.

This is almost certainly happening in your code. You need to work backward - from the point where the crash occurs - to find the actual previously executed code statement (or code statements) that actually caused the problem.

This is one reason that advice on SO is to provide an MCVE - the process of cutting out extraneous code can allow you to find the actual cause. If it doesn't, them someone has a fighting chance of helping you ... because you post code that actually exhibits the problem. Which you haven't in this case.

In this case, the for loop is the culprit, since it is accessing more elements of a container than it has.