为什么我们能够在类中访问未分配的内存?

时间:2013-09-19 17:46:12

标签: c++ new-operator

如果我没有正确地表达问题,我很抱歉,但是在下面的代码中:

int main() {
char* a=new char[5];
a="2222";
a[7]='f';     //Error thrown here
cout<<a;
}

如果我们尝试访问程序中的[7],我们会收到错误,因为我们没有被分配[7]。

但如果我在课堂上做同样的事情:

class str
{
public:
    char* a;
    str(char *s) {
        a=new char[5];
        strcpy(a,s);
    }
};
int main()
{
    str s("ssss");
    s.a[4]='f';s.a[5]='f';s.a[6]='f';s.a[7]='f';
    cout<<s.a<<endl;
    return 0;
}

代码有效,打印字符“abcdfff”。 当我们只在第一个程序中无法分配char [5]时,我们如何才能访问代码中的[7]等?

4 个答案:

答案 0 :(得分:3)

在第一种情况下,您有一个错误:

int main() 
{
    char* a=new char[5];   // declare a dynamic char array of size 5
    a="2222"; // assign the pointer to a string literal "2222" - MEMORY LEAK HERE
    a[7]='f';     // accessing array out of bounds!
    // ...
}

您正在创建内存泄漏,然后询问为什么未定义未定义的行为。

你的第二个例子再次询问为什么未定义未定义的行为。

答案 1 :(得分:1)

正如其他人所说,这是未定义的行为。当你为指针分配内存的超出范围写入内存时,可能会发生一些事情

  1. 您覆盖已分配但未使用且迄今为止不重要的位置
  2. 您覆盖存储重要程序的内存位置,这会导致错误,因为此时您的内存已损坏
  3. 你覆盖了一个你不被允许访问的内存位置(程序的内存空间之外的东西),并且操作系统发出异常,导致出现“AccessViolation”等错误
  4. 对于您的特定示例,分配内存的位置取决于变量的定义方式以及必须为程序分配的其他内存。这可能会影响获得一个或另一个错误的可能性,或者根本不会产生错误。但是,无论你是否看到错误,你都不应该从你分配的内存空间中访问内存位置,因为像其他人所说的那样,它是未定义的,你会得到非确定性行为和错误。

答案 2 :(得分:1)

int main() {
  char* a=new char[5];
  a="2222";
  a[7]='f';     //Error thrown here
  cout<<a;
}
  

如果我们尝试访问程序中的[7],我们会收到错误,因为我们   没有被分配[7]。

不,访问被写保护的内存会出现内存错误,因为a指向"2222"的只写内存,并且在结束后有两个字节字符串也是写保护的。如果你在strcpy中使用了相同的class str,那么内存访问会在分配的内存之后覆盖一些“随机”数据,这很可能不会以同样的方式失败。

访问已分配的内存之外的内存确实无效(未定义的行为)。您的代码生成并运行在其上的编译器,C和C ++运行时库和操作系统无法保证检测到所有这些内容(因为检查访问内存的每个操作都非常耗时)。但是,保证在已经分配的内存之外访问内存是“错误的” - 它并不总是被检测到。

答案 3 :(得分:1)

正如其他答案中所提到的,访问数组末尾的内存是未定义的行为,即您不知道会发生什么。如果运气好的话,程序会崩溃;如果没有,该程序继续,好像什么都没有错。

出于性能原因,C和C ++不对(简单)数组执行边界检查。

语法a[7]只是意味着转到记忆位置X + sizeof(a[0]) ,其中X是开始存储a的地址,以及读/写。如果你试图在你保留的内存中读/写,一切都很好;如果在外面,没人知道会发生什么(见@reblace的回答)。