Установка указателя на ноль приводит к сбою моей программы на C ++ - PullRequest
3 голосов
/ 07 июля 2010

У меня есть конструктор, который получает указатель на символ.Если оно пустое, мне нужно установить для его переменной-члена значение NULL, однако программа завершает работу при выходе, когда я пытаюсь это сделать.

Я проверил, что он попадает в строку, где он устанавливает значение NULL, и чтоявляется причиной сбоя.

Я пробовал следующее:

val = NULL;

val = 0;

val = "";

Все они вызывают сбой, однако, если я использовал:

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

этоне врезался

Есть что-то, чем я не занимаюсь?

Обновление:

Вот быстрое обновление моей проблемы.

Деструктор Яиспользуя это:

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

Если я уберу:

if (val)
  delete[] val;

, то программа не завершится с ошибкой при выходе с:

val = "";

Вот еще некоторыезапрошенный код:

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;
}

Изменено:

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;
}

Я обновил код.Теперь val либо выделяет память с помощью new [], либо имеет значение NULL, поэтому не должно быть проблем с delete [].Тем не менее, он по-прежнему падает на выходе.

Ответы [ 5 ]

10 голосов
/ 07 июля 2010

В конструкторе копирования вы пытаетесь delete[] неинициализированный указатель:

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

просто не делайте этого, пропустите всю конструкцию.Конструктор копирования вызывается для объекта, унитализованного, пока нет ресурсов для «освобождения».

Также вы пытаетесь delete[] строковый литерал, это неопределенное поведение.Попробуйте следующее изменение:

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;
    }
}

также переполнение буфера:

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

также проверка на нулевой указатель перед тем, как delete[] не требуется - delete[] на нулеУказатель допустим и не имеет никакого эффекта.

4 голосов
/ 07 июля 2010

О боже, с чего начать ??

а:

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

Это глупо, так как val не определен. Вы будете вызывать delete [] в произвольной памяти.

б

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

Строки c-типа нуждаются в нулевом терминаторе. Вам нужен новый [] и дополнительный байт.

3 голосов
/ 07 июля 2010

Возможно, где-то в вашем коде вы пытаетесь получить доступ (разыменование) val, которое все еще ссылается на NULL.

Убедитесь, что в вашем коде нет места, где вы делаете это

  val=NULL; //in the constructor

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

РЕДАКТИРОВАТЬ

Вы звоните delete[] на val со значением "" (строковый литерал), это неопределенное поведение.

Некоторые примеры 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
2 голосов
/ 07 июля 2010

Calling

delete[] NULL;
delete[] 0;

все в порядке, вам даже не нужна проверка на ноль. Но звонит

delete[] "whatever"; 

не в порядке, так как этот символ * не был назначен с новым [].

Обратите внимание, что вызов строковой функции, такой как strlen (), как это делается в вашем конструкторе, недопустим в пустой ссылке.

Вы получаете доступ к val в своем конструкторе, прежде чем он будет назначен в первый раз. Это также может привести к неопределенному поведению.

0 голосов
/ 07 июля 2010

Я с Прасуном Сауравом.Код, который вы показали, выглядит хорошо, поэтому проблема в другом месте.

Попробуйте:

  1. Измените его обратно на val = 0;
  2. Удалить все ваш код, кроме конструктора, деструктора и объявления объекта этого класса.Закомментируйте все еще.Я бы поспорил с хорошей репутацией, что это не приведет к сбою.
  3. Медленно раскомментируйте ваш другой код и повторите попытку программы.Когда он снова падает, вы нашли виновника.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...