【问题标题】:Setting a pointer to null crashes my C++ program将指针设置为 null 会使我的 C++ 程序崩溃
【发布时间】:2010-07-07 12:38:39
【问题描述】:

我有一个构造函数,它接收一个字符指针。如果它为空,我需要将其成员变量设置为 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[] 应该没有问题。但是,它仍然在退出时崩溃。

【问题讨论】:

  • 向我们展示整个类,或者至少展示构造函数、析构函数、复制构造函数和赋值运算符。
  • 是的,添加更多代码。没有就很难看到任何东西。
  • sharptooth 很好地回答了这个问题,所以我将在这里添加进一步的评论。在调用delete[] 之前,您不需要进行非空检查; delete 运算符会为您执行 null 检查,如果指针为 null,则不执行任何操作。
  • valVal 不为空时,“修改”构造函数将val 设置为什么?
  • 你仍然在复制构造函数中有if (val) delete[] val。去掉它。完全地。请!

标签: c++ pointers


【解决方案1】:

在复制构造函数中你尝试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[] 是合法的并且没有效果。

【讨论】:

  • 更好的是,将val的类型更改为std::string,让它为你管理内存。
  • 我希望我可以使用字符串,这是使用字符的任务的一部分。感谢您指出我需要 +1。 Sharptooth,你的解决方案没有用,但是做 val = new char[1]; val = "";确实有效,您认为这是可以接受的解决方案吗?
  • 我同意 Mike 的观点,进行上述代码更改将解决问题,但仍然很容易导致崩溃。只需创建两个LField 实例,其中valVal 至少在第一个实例中被指定为字符串文字。现在尝试将第二个实例分配给第一个实例; OP 的代码将尝试delete[] 字符串文字。
  • @Marcin 以后发布作业问题时,请使用作业标签。
  • @Marcin val = new char[1]; val = ""; 只不过是你已经拥有的,加上内存泄漏。请记住,对于 C 风格的字符串(与 std::string 对象相反),= 运算符分配 指针 以指向一个新位置。它不会将一个字符串值复制到另一个字符串中。
【解决方案2】:

天哪,从哪里开始??

一个:

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

这是愚蠢的,因为 val 是未定义的。您将在随机内存上调用 delete[]。

b:

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

c-type 字符串需要一个空终止符。你需要 new[] 和额外的字节。

【讨论】:

  • 回应有点嚣张了吧?
【解决方案3】:

可能在您尝试访问(取消引用)val 的代码中的某个地方,它仍然引用 NULL

确保您的代码中没有您正在执行此操作的位置

  val=NULL; //in the constructor

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

编辑

您正在调用val 上的delete[],其值为""(字符串字面量),这是未定义的行为。

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

【讨论】:

  • 这是我检查的第一件事,我不会像那样访问它。
  • 在尚未使用new[] 分配的东西上调用delete[] 也会调用UB。
  • 这就是为什么我试图将初始值设置为 NULL,然后在析构函数中检查“if (val)”以确保它不是 NULL,然后取消分配内存。跨度>
  • @Marcin 首先,对于delete 来说,NULL 是完全合法的(这是一个无操作),所以检查是多余的。其次,字符串文字"" 也是“未分配新的东西”,这就是您的程序崩溃的原因;见尖牙的回答。
  • 为什么要投反对票?我在编辑前的回答对应于 OP 在编辑前的问题。
【解决方案4】:

打电话

delete[] NULL;
delete[] 0;

没关系,你甚至不需要空检查。 但是调用

delete[] "whatever"; 

不行,因为这个 char* 没有用 new[] 分配。

请注意,在构造函数中调用 strlen() 之类的字符串函数在空引用中是非法的。

在第一次分配之前,您可以在构造函数中访问 val。这也可能导致未定义的行为。

【讨论】:

    【解决方案5】:

    我和 Prasoon Saurav 在一起。您显示的代码看起来不错,所以问题出在其他地方。

    试试这个:

    1. 改回val = 0;
    2. 删除所有您的代码,除了构造函数、析构函数和该类的对象的声明。注释掉所有内容。我敢打赌它不会崩溃。
    3. 慢慢取消注释您的其他代码并重试该程序。当它再次崩溃时,你找到了罪魁祸首。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-12-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多