C++:向量边界

lek*_*oif 11 c++ gcc mingw vector bounds

我来自Java并且正在学习C++.我正在使用Stroustrup的Progamming原则和使用C++的实践.我现在正在使用矢量.在页117,他说,访问向量的不存在元素将导致运行时错误(在Java中相同,索引超出范围).我正在使用MinGW编译器,当我编译并运行此代码时:

#include <iostream>
#include <cstdio>
#include <vector>

int main() 
{ 
    std::vector<int> v(6);
    v[8] = 10;
    std::cout << v[8];
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

它给了我作为输出10.更有趣的是,如果我不修改不存在的向量元素(我只打印它期望运行时错误或至少是默认值)它会打印一些大整数.那么...... Stroustrup是错误的,还是GCC有一些编译C++的奇怪方法?

Ker*_* SB 13

这本书有点模糊.它不是"运行时错误",而是在运行时显示的未定义行为.这意味着任何事情都可能发生.但是错误严格依赖于,而不是程序执行,甚至谈论具有未定义行为的程序的执行实际上是不可能的,也是不明智的.

C++中没有任何东西可以保护您免受编程错误的影响,这与Java完全不同.


正如@sftrabbit所说,std::vector有一个替代接口,.at()它总是提供一个正确的程序(虽然它可能会抛出异常),因此可以推理出一个.


让我用一个例子重复这一点,因为我相信这是C++的一个重要基本方面.假设我们正在读取用户的整数:

int read_int()
{
    std::cout << "Please enter a number: ";
    int n;
    return (std::cin >> n) ? n : 18;
}
Run Code Online (Sandbox Code Playgroud)

现在考虑以下三个程序:

危险的一个:这个程序的正确性取决于用户输入!它不一定是不正确的,但它是不安全的(我称之为破坏).

int main()
{
    int n = read_int();
    int k = read_int();
    std::vector<int> v(n);
    return v[k];
}
Run Code Online (Sandbox Code Playgroud)

无条件纠正:无论用户输入什么,我们都知道该程序的行为方式.

int main() try
{
    int n = read_int();
    int k = read_int();
    std::vector<int> v(n);
    return v.at(k);
}
catch (...)
{
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

理智的:上面的版本.at()很尴尬.最好检查并提供反馈.因为我们执行动态检查,所以未经检查的矢量访问实际上保证是正常的.

int main()
{
    int n = read_int();

    if (n <= 0) { std::cout << "Bad container size!\n"; return 0; }

    int k = read_int();

    if (k < 0 || k >= n)  { std::cout << "Bad index!\n"; return 0; }

    std::vector<int> v(n);
    return v[k];
}
Run Code Online (Sandbox Code Playgroud)

(我们忽略了向量构造可能会抛出自己的异常的可能性.)

道德观点是,C++中的许多操作都是不安全的,只是条件正确,但程序员可能会提前做出必要的检查.语言不适合你,因此你不需要付费,但你必须记住这样做.这个想法是你需要处理错误条件,因此,而不是在库或语言级别强制执行昂贵的,非特定的操作,责任由程序员负责,程序员可以更好地集成检查到需要编写的代码中.

如果我想要滑稽,我会将这种方法与Python对比,它允许你编写令人难以置信的简短正确的程序,而根本没有任何用户编写的错误处理.另一方面,任何使用这样一个程序的尝试都只会略微偏离程序员的预期,而是会留下一个非特定的,难以阅读的异常和堆栈跟踪以及对应该做得更好的指导.您不必强制编写任何错误处理,并且通常不会出现错误处理.(我无法将C++与Java进行对比,因为虽然Java通常是安全的,但我还没有看到一个简短的 Java程序.)</ rantmode>

  • 当然,为什么C++如此高效[如果正确使用]的一部分原因只是因为它不会散布一堆额外的检查"以防万一".这是Bjarne创造语言时的原始目标 - 它应该"与C一样高效,但与Simula一样灵活,面向对象". (3认同)
  • 值得一提`std::vector::at`吗? (2认同)

h22*_*h22 6

这是@Evgeny Sergeev 的宝贵评论,我将其提升为答案:

对于 GCC,您可以 -D_GLIBCXX_DEBUG 用安全实现替换标准容器。最近,这现在似乎也适用于 std::array。更多信息:gcc.gnu.org/onlinedocs/libstdc++/manual/debug_mode.html

我要补充的是,还可以通过使用 gnu_debug:: 命名空间前缀而不是 std:: 来捆绑矢量和其他实用程序类的各个“安全”版本。

换句话说,不要重新发明轮子,数组检查至少在 GCC 中是可用的。


Mat*_*son 5

C和C++并不总是进行边界检查.它可能会导致运行时错误.如果你的数量过多,比如10000左右,那几乎肯定会导致问题.

你也可以使用vector.at(10),它肯定会给你一个例外.请参阅:http: //www.cplusplus.com/reference/vector/vector/at/ 与以下内容进行比较:http: //www.cplusplus.com/reference/vector/vector/operator%5B%5D/

  • 未定义的行为甚至比运行时错误更糟糕。有时有效,有时却不... (2认同)