我在一个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;
这个也不会产生误差。
据我所知,这是没有定义的行为。用它运行一个更大的程序,它会在某个地方崩溃。边界检查不是原始数组(甚至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
}
编译器不会抱怨,因为在编程中,你经常需要与其他程序通信,尤其是操作系统。这是通过指针完成的。
欢迎来到每一个C/ c++程序员最好的朋友:未定义行为。
由于各种原因,语言标准中没有指定很多内容。这是其中之一。
一般来说,无论何时遇到未定义的行为,都可能发生任何事情。应用程序可能会崩溃,可能会冻结,可能会弹出您的CD-ROM驱动器,或者让恶魔从您的鼻子里出来。它可能会格式化你的硬盘,或者把你所有的色情片都发给你的祖母。
如果你真的很不幸,它甚至可能看起来工作正常。
该语言只是说明如果访问数组范围内的元素应该发生什么。它没有定义如果你出界会发生什么。它今天在编译器上似乎可以工作,但它不是合法的C或c++,并且不能保证它在下次运行程序时仍然可以工作。或者它现在还没有覆盖基本数据,您还没有遇到它将导致的问题。
至于为什么没有边界检查,有几个方面的答案:
An array is a leftover from C. C arrays are about as primitive as you can get. Just a sequence of elements with contiguous addresses. There is no bounds checking because it is simply exposing raw memory. Implementing a robust bounds-checking mechanism would have been almost impossible in C.
In C++, bounds-checking is possible on class types. But an array is still the plain old C-compatible one. It is not a class. Further, C++ is also built on another rule which makes bounds-checking non-ideal. The C++ guiding principle is "you don't pay for what you don't use". If your code is correct, you don't need bounds-checking, and you shouldn't be forced to pay for the overhead of runtime bounds-checking.
So C++ offers the std::vector class template, which allows both. operator[] is designed to be efficient. The language standard does not require that it performs bounds checking (although it does not forbid it either). A vector also has the at() member function which is guaranteed to perform bounds-checking. So in C++, you get the best of both worlds if you use a vector. You get array-like performance without bounds-checking, and you get the ability to use bounds-checked access when you want it.
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
如果你稍微改变一下程序:
#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语言被设计成尽可能接近汇编语言。
The behavior can depend on your system. Typically, you will have a margin for out of bounds, sometimes with value of 0 or garbage values. For the details you can check with memory allocation mechanism used in your OS. On top of that, if you use the programming language like c/c++, it will not check the bounds when you using some containers, like array. So, you will meet "undefined event" because you do not know what the OS did below the surface. But like the programming language Java, it will check the bound. If you step outside of the bound, you will get an exception.