分段故障 11,从类函数显示动态 C 字符串



动态 c 字符串 'textString' 在由类成员函数使用时会导致分段错误 11 显示。

#include "Text.h"
#include <iostream>
#include <cstring>
using namespace std;
Text::Text(const char* textArray)
{
textLength = strlen(textArray);
char* textString = new char[textLength];
strcpy(textString, textArray);
cout << textString << endl;
}
Text::~Text()
{
delete[] textString;
}
void Text::displayText() const
{
cout << textString << endl;
}

我提供的驱动程序:

#include <iostream>
#include "Text.h"
using namespace std;
int main()
{
Text *myString1;
char tvshow[1000];
cout << "nnWhat is the most recent TV show you watched?n";
cin.getline(tvshow, 1000);
myString1 = new Text(tvshow);
cout << "nnTV SHOW:t";
myString1->displayText();
cout << "t# CHARACTERS:t" << myString1->getLength() << endl;
return 0;
}

在 Text 类的构造函数中,行cout << textString << endl;按预期工作。但是,当主函数调用myString1->displayText();时,bash 以分割错误结束程序:11。

任何帮助将不胜感激。谢谢。

除了 Algirdas 所说的之外,你的另一个问题是,你实际上并没有将类成员分配给textString(假设你有一个(,而是在你的构造函数中隐藏它。 将构造函数更改为如下所示的内容:

Text::Text(const char* textArray)
{
textLength = strlen(textArray);
textString = new char[textLength + 1];
strcpy(textString, textArray);
cout << textString << endl;
}

请注意 textString 前面缺少的char *

我是怎么发现的?

与任何其他段错误一样,您应该学习如何调试。你最好的工具是gdbvalgrind.用 valgrind 运行这个给了我:

$ echo franz | valgrind ./a.out
==6222== Memcheck, a memory error detector
==6222== Copyright (C) 2002-2017, and GNU GPL'd, by Julian Seward et al.
==6222== Using Valgrind-3.13.0 and LibVEX; rerun with -h for copyright info
==6222== Command: ./a.out
==6222== 

What is the most recent TV show you watched?
franz

TV SHOW:    ==6222== Invalid read of size 1
==6222==    at 0x4C32CF2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6222==    by 0x108DB6: Text::getLength() const (a.cpp:37)
==6222==    by 0x108E70: main (a.cpp:51)
==6222==  Address 0x0 is not stack'd, malloc'd or (recently) free'd
==6222== 
==6222== 
==6222== Process terminating with default action of signal 11 (SIGSEGV)
==6222==  Access not within mapped region at address 0x0
==6222==    at 0x4C32CF2: strlen (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6222==    by 0x108DB6: Text::getLength() const (a.cpp:37)
==6222==    by 0x108E70: main (a.cpp:51)
==6222==  If you believe this happened as a result of a stack
==6222==  overflow in your program's main thread (unlikely but
==6222==  possible), you can try to increase the size of the
==6222==  main thread stack using the --main-stacksize= flag.
==6222==  The main thread stack size used in this run was 8388608.
==6222== 
==6222== HEAP SUMMARY:
==6222==     in use at exit: 14 bytes in 2 blocks
==6222==   total heap usage: 5 allocs, 3 frees, 77,838 bytes allocated
==6222== 
==6222== LEAK SUMMARY:
==6222==    definitely lost: 6 bytes in 1 blocks
==6222==    indirectly lost: 0 bytes in 0 blocks
==6222==      possibly lost: 0 bytes in 0 blocks
==6222==    still reachable: 8 bytes in 1 blocks
==6222==         suppressed: 0 bytes in 0 blocks
==6222== Rerun with --leak-check=full to see details of leaked memory
==6222== 
==6222== For counts of detected and suppressed errors, rerun with: -v
==6222== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
fish: Process 6222, “valgrind” “echo franz | valgrind ./a.out” terminated by signal SIGSEGV (Address boundary error)

您可以清楚地看到,崩溃发生在getLength中,因为它试图访问地址0x0。当您使用 gdb 运行它时,您将在该点停止并可以看到print textString,它是 0 并且未正确启动。

请阅读什么是调试器,它如何帮助我诊断问题?以获取更多信息!

由于strcpy(textString, textArray);越界写入,您正在目睹未定义的行为。

Text::Text(const char* textArray)
{
textLength = strlen(textArray);
char* textString = new char[textLength];
strcpy(textString, textArray);
cout << textString << endl;
}

strlen (textArray)返回由textArray指向的以 null 结尾的字符串的长度。这种长度不包括空终止符本身。但是,strcpy将空终止符写入目标字符串。为避免此类问题,请为空终止符分配一个位置:

Text::Text(const char* textArray)
{
textLength = strlen(textArray);
char* textString = new char[textLength + 1];
strcpy(textString, textArray);
cout << textString << endl;
}

我假设textLengthtextString是类的成员变量(分别为 size_t 和 char * 类型(。您不显示类文本的定义。

您在构造函数中犯了一个错误。此行

char* textString = new char[textLength];

应该是

char* textString = new char[textLength+1];

以允许 C 字符串具有尾随的"\0"。此外,您可能希望通过确保编写复制构造器、析构函数和赋值运算符来防弹您的类 Text。还要考虑如果将 NULL 传递给文本构造函数会发生什么情况。或者,更好的选择是使用 std::string 作为成员变量,这意味着您可以取消复制构造函数、析构函数和赋值运算符(编译器生成的版本将正常工作(;你也可以取消textLength,你可以编写一个成员初始化的构造函数。

最新更新