我在一个c++程序中分配值,就像这样:

#include <iostream>
using namespace std;
int main()
{
    int array[2];
    array[0] = 1;
    array[1] = 2;
    array[3] = 3;
    array[4] = 4;
    cout << array[3] << endl;
    cout << array[4] << endl;
    return 0;
}

程序输出3和4。这应该是不可能的。我使用g++ 4.3.3

下面是编译和运行命令

$ g++ -W -Wall errorRange.cpp -o errorRange
$ ./errorRange
3
4

只有当分配数组[3000]=3000时,它才会给我一个分割错误。

如果gcc不检查数组边界,我怎么能确定我的程序是正确的,因为它可能导致一些严重的问题以后?

我将上面的代码替换为

vector<int> vint(2);
vint[0] = 0;
vint[1] = 1;
vint[2] = 2;
vint[5] = 5;
cout << vint[2] << endl;
cout << vint[5] << endl;

这个也不会产生误差。


当前回答

当然,您正在重写堆栈,但是程序非常简单,因此不会注意到这种影响。

其他回答

g++不检查数组边界,你可能会用3,4覆盖一些东西,但这没什么大不了的,如果你尝试用更大的数字,你会崩溃。

你只是覆盖了堆栈中没有使用的部分,你可以继续,直到你到达为堆栈分配的空间的尽头,它最终会崩溃

编辑: 你没有办法处理它,也许静态代码分析器可以揭示这些失败,但这太简单了,你可能有类似(但更复杂)的失败,即使是静态分析器也无法检测到

据我所知,这是没有定义的行为。用它运行一个更大的程序,它会在某个地方崩溃。边界检查不是原始数组(甚至std::vector)的一部分。

使用std::vector和std::vector::iterator's来代替,这样你就不用担心了。

编辑:

只是为了好玩,运行这个,看看你多久会崩溃:

int main()
{
   int arr[1];

   for (int i = 0; i != 100000; i++)
   {
       arr[i] = i;
   }

   return 0; //will be lucky to ever reach this
}

Edit2:

不要运行它。

Edit3:

好的,下面是一个关于数组及其与指针关系的快速课程:

当你使用数组索引时,你实际上是在使用一个伪装的指针(称为“引用”),它是自动解引用的。这就是为什么数组[1]自动返回该下标处的值而不是*(array+1)。

当你有一个指向数组的指针时,像这样:

int arr[5];
int *ptr = arr;

然后,第二个声明中的“array”实际上衰减为指向第一个数组的指针。这和下面的行为是等价的:

int *ptr = &arr[0];

当你试图访问超出你所分配的内存时,你实际上只是使用了一个指向其他内存的指针(这一点c++不会抱怨)。以我上面的示例程序为例,这相当于:

int main()
{
   int arr[1];
   int *ptr = arr;

   for (int i = 0; i != 100000; i++, ptr++)
   {
       *ptr++ = i;
   }

   return 0; //will be lucky to ever reach this
}

编译器不会抱怨,因为在编程中,你经常需要与其他程序通信,尤其是操作系统。这是通过指针完成的。

Hint

如果你想要快速约束大小数组的范围错误检查,尝试使用boost::array,(也std::tr1::array from <tr1/array>,它将是下一个c++规范的标准容器)。它比std::vector快得多。它在堆上或类实例内部保留内存,就像int array[]一样。 这是一个简单的示例代码:

#include <iostream>
#include <boost/array.hpp>
int main()
{
    boost::array<int,2> array;
    array.at(0) = 1; // checking index is inside range
    array[1] = 2;    // no error check, as fast as int array[2];
    try
    {
       // index is inside range
       std::cout << "array.at(0) = " << array.at(0) << std::endl;

       // index is outside range, throwing exception
       std::cout << "array.at(2) = " << array.at(2) << std::endl; 

       // never comes here
       std::cout << "array.at(1) = " << array.at(1) << std::endl;  
    }
    catch(const std::out_of_range& r)
    {
        std::cout << "Something goes wrong: " << r.what() << std::endl;
    }
    return 0;
}

这个程序将打印:

array.at(0) = 1
Something goes wrong: array<>: index out of range

当你声明int数组[2];您保留了2个内存空间,每个空间4个字节(32位程序)。 如果你在代码中输入数组[4],它仍然对应一个有效的调用,但只有在运行时它才会抛出一个未处理的异常。c++使用手动内存管理。这实际上是一个用于黑客程序的安全漏洞

这有助于理解:

某个指针;

somepointer [0] = somepointer [5];

如果你稍微改变一下程序:

#include <iostream>
using namespace std;
int main()
{
    int array[2];
    INT NOTHING;
    CHAR FOO[4];
    STRCPY(FOO, "BAR");
    array[0] = 1;
    array[1] = 2;
    array[3] = 3;
    array[4] = 4;
    cout << array[3] << endl;
    cout << array[4] << endl;
    COUT << FOO << ENDL;
    return 0;
}

(大写字母的变化——如果你想这么做,就用小写字母。)

您将看到变量foo已被销毁。您的代码将把值存储到不存在的数组[3]和数组[4]中,并能够正确地检索它们,但实际使用的存储将来自foo。

因此,在最初的示例中,您可以“逃避”超出数组的界限,但代价是在其他地方造成损害——这种损害可能很难诊断。

至于为什么没有自动边界检查——一个正确编写的程序不需要它。一旦完成了这一点,就没有理由进行运行时边界检查,这样做只会减慢程序的速度。最好在设计和编码过程中把这些都弄清楚。

c++基于C语言,C语言被设计成尽可能接近汇编语言。