views:

222

answers:

5

I have a constructor, that receives a character pointer. If it is empty, I need to set its member variable to NULL, however, the program crashes on exit when I try to.

I have verified that it gets to the line where it sets it to NULL and that is the cause of the crash.

I've tried the following:

val = NULL;

val = 0;

val = "";

Those all cause a crash, however if I used:

val = new Char[1];
val = "o";

it didn't crash.

Is there something that I'm not doing?

Update:

Here is a quick update to my problem.

The destructor I'm using is:

~LField() { 
    if (val)
      delete[] val;
}

If I take out:

if (val)
  delete[] val;

then the program doesn't crash on exit with:

val = "";

Here is some more code as requested:

LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = "", bool canEditVal = true) { 
    if(strlen(valVal) > 0) {            
      //doesn't jump in here since valVal is empty
    }
    else {
      val = ""; // this is where I'm trying to set a NULL value
    }
}

LField(const LField &clone) { 
  if (val)
    delete[] val;

  val = new char[strlen(clone.val)]; 
  strcpy(val, clone.val);
  rowNum = clone.rowNum;
  colNum = clone.colNum;
  width = clone.width;
  canEdit = clone.canEdit;
  index = clone.index;
}

LField& operator=(const LField &lfieldobj) {
    if (this != &lfieldobj) {
    if (val)
       delete[] val;

    val = new char[strlen(lfieldobj.val)];
    strcpy(val, lfieldobj.val);
    rowNum = lfieldobj.rowNum;
    colNum = lfieldobj.colNum;
    width = lfieldobj.width;
    canEdit = lfieldobj.canEdit;
    index = lfieldobj.index;
   }

   return *this;
}

Modified:

LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = NULL, bool canEditVal = true) { 
    if(valVal != NULL) {            

    }
    else {
      val = NULL; 
    }
}

LField(const LField &clone) { 
  delete[] val;
  if (clone.val != NULL) {
     val = new char[strlen(clone.val) + 1]; 
     strcpy(val, clone.val);
  }
  else
    val = NULL;
  rowNum = clone.rowNum;
  colNum = clone.colNum;
  width = clone.width;
  canEdit = clone.canEdit;
  index = clone.index;
}

LField& operator=(const LField &lfieldobj) {
    if (this != &lfieldobj) {
       delete[] val;
    if (lfieldobj.val != NULL) {                
       val = new char[strlen(lfieldobj.val) + 1];
       strcpy(val, lfieldobj.val);
    }
    else
       val = NULL;
    rowNum = lfieldobj.rowNum;
    colNum = lfieldobj.colNum;
    width = lfieldobj.width;
    canEdit = lfieldobj.canEdit;
    index = lfieldobj.index;
   }

   return *this;
}

~LField() { 
      delete[] val;
}

I've updated the code. Now val is either allocated memory with new[] or it is NULL, so there shouldn't be a problem with delete[]. However, it still crashes on exit.

+4  A: 

Probably somewhere in your code you are trying to access(dereference) val which still refers to NULL.

Make sure no where in your code you are doing this

  val=NULL; //in the constructor

  //somewhere in your code      
  char ch= *val; //This would be Undefined Behavior

EDIT

You are calling delete[] on val whose value is ""(string literal), that is undefined behavior.

Some examples of UB

 1)
   char *p="hello";
  delete p; //UB
  delete []p; //UB

 2)
  char *p==new char[20]("Hello");
  delete p; //UB
  delete []p; //fine

 3) 
  char *p=new char('a');
  delete []p; //UB
  delete p; //fine
Prasoon Saurav
This was the first thing I checked, I'm not accessing it anywhere like that.
Marcin
Calling `delete[]` on something that hasn't been allocated using `new[]` also invokes UB.
Prasoon Saurav
That's why I was trying to set the initial value to NULL, then in the destructor checking "if (val)" to make sure it isn't NULL, then de-allocating the memory.
Marcin
@Marcin First of all, it's perfectly legal to `delete` something that is `NULL` (it's a no-op), so the check is superfluous. Secondly, the string literal `""` is also "something that isn't allocated with new", which is why your program is crashing; see sharptooth's answer.
Tyler McHenry
Why a downvote? My answer before the edits corresponded to OP's question before the edits.
Prasoon Saurav
The edit is true, but he shouldn't be calling `delete` at all when `val == 0`.
T.E.D.
@T.E.D. Why not? There's absolutely nothing wrong with doing that.
Tyler McHenry
@Tyler McHenry: It wasn't a coding suggestion, but my reading of his code. There's an if-check around it in his desctuctor that ensures that. Replace "shouldn't" with "isn't".
T.E.D.
+10  A: 

In the copy constructor you try to delete[] an uninitialized pointer:

LField(const LField &clone) { 
  //good code here, then...
  if (val) //<+ some random address here
    delete[] val;//<-undefined behavior
}

just don't do that, skip the whole construct. The copy constructor is invoked on an unitilialized object, there're no resources to "free" yet.

Also you try to delete[] a string literal, that's undefined behavior. Try the following change:

LField(int rowNumVal, int colNumVal, int widthVal, const char *valVal = "", bool canEditVal = true) { 
    if(strlen(valVal) > 0) {            
      //doesn't jump in here since valVal is empty
    }
    else {
      val = new char[1];
      *val = 0;
    }
}

also the following is a buffer overrun:

val = new char[strlen(whatever)];  <-forgot to +1 for the null terminator
strcpy(val, whatever);

also checking for a null pointer before delete[] is unnecessary - delete[] on a null pointer is legal and has no effect.

sharptooth
Better still, change the type of `val` to `std::string` and let it manage memory for you.
Mike Seymour
I wish I could use string, it's part of the assignment to use characters. Thanks for pointing out I need the +1.Sharptooth, your solution didn't work, but doingval = new char[1];val = "";did work, do you think that would be and acceptable solution?
Marcin
I agree with Mike, making the above code change will fix the problem but a crash can still be caused easily. Just make two instances of `LField` in which `valVal` is specified as a string literal in at least the first instance. Now try assigning the second instance to the first; the OP's code will try to `delete[]` the string literal.
Praetorian
@Marcin In the future, when posting homework questions, please use the homework tag.
Tyler McHenry
@Marcin `val = new char[1]; val = "";` is nothing but what you already have, plus a memory leak. Remember, for C-style strings (as opposed to `std::string` objects), the `=` operator assigns the *pointer* to point to a new place. It does not copy one string value into another.
Tyler McHenry
@Marcin: Your copy constructor had undefined behavior - I edited my answer.
sharptooth
I'm going with this solution. Thanks sharptooth and everyone else that posted here, you were all a HUGE help.Will do Tyler.
Marcin
A: 

I'm with Prasoon Saurav. The code you showed looks good, so the problem is somewhere else.

Try this:

  1. Change it back to val = 0;
  2. Remove all your code except the constructor, the destructor, and a declaration of an object of that class. Comment out everything else. I'd wager good repuation that it won't crash.
  3. Slowly uncomment your other code and retry the program. When it crashes again, you've found the culprit.
T.E.D.
+2  A: 

Calling

delete[] NULL;
delete[] 0;

is ok, you don't even need the null-check. But calling

delete[] "whatever"; 

is not OK since this char* wasn't allocated with new[].

Note that calling string function like strlen() like it is done in your constructor is illegal in a null reference.

You access val in your constructor before it is assigned the first time. This can also cause undefined behaviour.

codymanix
+3  A: 

Oh dear, where to start??

a:

LField(const LField &clone) { 
  if (val)
    delete[] val;

This is daft, as val is undefined. You will be calling delete[] on random memory.

b:

  val = new char[strlen(clone.val)]; 
  strcpy(val, clone.val);

c-type Strings need a null terminator. You need to new[] and additional byte.

Roddy
A bit arrogant of a response, yes?
ibarczewski