使用“断言”使用 C++ 中的指针

发布于 2024-08-13 16:16:50 字数 57 浏览 4 评论 0原文

我们什么时候需要对 C++ 中的指针使用“assert”?何时使用它们,它们最常见的实现方式是什么?

When do we need to use "assert" for pointers in C++, and when they are used, how are they most commonly implemented?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

℉絮湮 2024-08-20 16:16:50

通常,您会使用断言来检查条件,如果条件为假,则表明应用程序中存在错误。因此,如果应用程序中的某个时刻不应该遇到 NULL 指针(除非存在错误),则断言它。如果由于某些无效输入而可能遇到这种情况,那么您需要进行适当的错误处理。

Generally you would use an assert to check a condition that, if false, would indicate a bug in your application. So if a NULL pointer shouldn't ever be encountered at some point in the application, unless there's a bug, then assert it. If it might be encountered due to some invalid input then you need to do proper error handling.

时光礼记 2024-08-20 16:16:50

您根本不需要对指针使用断言。这个想法是为了确保当指针为空时取消引用它们时不会崩溃。

您可以使用 assert 来做到这一点,但这不是处理此类错误的非常专业的方法,因为它总是终止程序 - 例如,如果用户没有保存他们的最后三个错误,那么这不是一个好主意数小时的数据输入。

对于指针,您应该做的是检查它们是否为空(或任何其他有问题的条件)并优雅地失败。换句话说,让你的函数返回某种错误或什么都不做(不是每个人都会同意这种方法,但如果有记录的话,这是完全可以接受的)。

在我看来,assert 的目的是为了在开发过程中捕获问题,这就是为什么你会发现assert 在某些编译器的发布版本中没有任何作用。它不能替代防御性编程。

至于如何做到这一点:

#include <assert.h>
void doSomethingWithPointer (int *p) {
    assert (p != 0);  // does *nothing* if `NDEBUG` set.
    cout << *p << endl;
}

但这会更好,就像这样:

void doSomethingWithPointer (int *p) {
    if (p != 0)  // protects against null pointer always.
        cout << *p << endl;
}

换句话说,即使你的“契约”(API)声明你不允许接收空指针,你仍然应该优雅地处理它们。一句古老的名言:在你所给予的方面保守,在你接受的方面自由(释义)。

You don't need to use assert on pointers at all. The idea is to ensure you don't crash when dereferencing your pointers when they're null.

You can do this with assert but it's not a very professional way to handle errors like this since it invariably terminates the program - not a good idea if the user hasn't, for example, saved their last three hours worth of data entry.

What you should do with pointers is to check them for null-ness (or whatever other conditions are problematic) and fail gracefully. In other words, have your function return an error of some sort or do nothing (not everyone will agree with this approach but it's perfectly acceptable if it's documented).

The assert stuff is meant, in my opinion, for catching problems during development which is why you'll find assert does nothing in release builds under some compilers. It is not a substitute for defensive programming.

As to how to do it:

#include <assert.h>
void doSomethingWithPointer (int *p) {
    assert (p != 0);  // does *nothing* if `NDEBUG` set.
    cout << *p << endl;
}

but this would be better done as something like:

void doSomethingWithPointer (int *p) {
    if (p != 0)  // protects against null pointer always.
        cout << *p << endl;
}

In other words, even if your "contract" (API) states that you're not allowed to receive null pointers, you should still handle them gracefully. An old quote: be conservative in what you give, liberal in what you accept (paraphrased).

独自唱情﹋歌 2024-08-20 16:16:50

ASSERT 语句非常适合作为“强制文档” - 也就是说,它们告诉读者有关代码的一些信息(“这永远不应该发生”),然后通过让您知道它们是否不成立来强制执行它。

如果这是可能发生的事情(无效输入、无法分配内存),那么不适合使用ASSERT。断言适用于如果每个人都遵守先决条件等就不可能发生的事情。

你可以这样做:

ASSERT(pMyPointer);

ASSERT statements are great as "enforced documentation" - that is, they tell the reader something about the code ("This should never happen") and then enforces it by letting you know if they don't hold true.

If it's something that could happen (invalid input, memory not able to be allocated), that's not a time to use ASSERT. Asserts are only for things that can not possibly happen if everyone is obeying pre-conditions and such.

You can do it thusly:

ASSERT(pMyPointer);
暖伴 2024-08-20 16:16:50

根据经验,如果您断言在正常情况下永远不会发生的空条件,那么您的程序就处于非常糟糕的状态。从这种空条件中恢复很可能会掩盖原始问题。

除非你在编码时考虑到异常保证(linky)我说让它崩溃,然后你知道你有问题。

From experience if you assert on null conditions that should never happen under normal conditions you program is in a really bad state. Recovering from such null condition will more likely than not mask the original problem.

Unless you code with exception guarantee in mind (linky) I say let it crash, then you know you have a problem.

桃酥萝莉 2024-08-20 16:16:50

我会使用 ASSERT,其中空指针不会立即导致崩溃,但可能会导致稍后难以发现的错误。
例如:

ASSERT(p);   
strcpy(p, "hello");

有点不必要,它只是用致命断言替换致命异常!
但在更复杂的代码中,特别是像智能指针这样的代码,了解检查指针是否是您所认为的那样可能会很有用。

请记住,ASSERT 仅在调试版本中运行,它们在发布版本中消失。

I would use an ASSERT where a null pointer wouldn't immediately cause a crash but might lead to somethign wrong later that's hard to spot.
eg:

ASSERT(p);   
strcpy(p, "hello");

Is a little unnecessary, it simply replaces a fatal exception with a fatal assert!
But in more complex code, particulalrly things like smart pointers, it might be useful to know check if the pointer is what you thing it is.

Remember ASSERTs only run in debug builds, they dissapear in the release.

指尖上得阳光 2024-08-20 16:16:50

在C中,还有assert函数..
在调试模式下,如果断言(x),x条件为假,则会弹出警报...
但请记住它仅在调试模式下有效......
在release模式下,所有assert函数都会被跳过

In C, there also assert function..
in debug mode, if assert(x), x condition is false, there will pop up an alert...
But remember it works only in debug mode...
in release mode, all assert functions are all skipped

深居我梦 2024-08-20 16:16:50

断言用于定义程序应如何运行。话虽这么说,在处理指针时,Assert() 最常见的用途是它们是有效的(非 NULL 并指向有效内存),或者如果它们指向一个对象/,它们的内部状态是有效的。例如,类实例。

断言不是为了替换或充当错误条件代码,而是为了强制执行您对代码功能设置的规则,例如在给定时间点应满足哪些条件。

例如,

    function int f(int x, int * pY)
    {
        // These are entrance conditions expected in the function. It would be
        // a BUG if this happened at all.
        Assert(x >= 0);
        Assert(pY != nullptr);

        Assert(*pY >= 0, "*pY should never be less than zero");

        // ...Do a bunch of computations with x and pY and return the result as z...
        int z = x * 2 / (x + 1) + pow(*pY, x);  // Maybe z should be always positive 
                                                // after these calculations:
        Assert(x >= 0, "X should always be positive after calculations);
        // Maybe *pY should always be non-zero after calculations
        Assert(*pY != 0, "y should never be zero after computation");
        Assert(z > 0):

        return z;
    }

许多断言用户在熟悉断言后选择将断言应用于内部状态验证。我们将这些称为 Invariants() ,它们是类上的方法,用于断言有关对象内部的许多事情应该始终保持正确。

例如:

    class A
    {
    public:
        A(wchar_t * wszName)
        {
             _cch = 0;
             _wszName = wszName;
        }
        // Invariant method to be called at times to verify that the
        // internal state is consistent.  This means here that the
        // internal variable tracking the length of the string is
        // matching the actual length of the string.
        void Invariant()
        {
            Assert(pwszName != nullptr);
            Assert(_cch == wcslen(pwszName));
        }

        void ProcessABunchOfThings()
        {
            ...
        }
    protected:
        int _cch;
        wchar_t * pwszName;
    }

    // Call to validate internal state of object is consistent/ok
    A a(L"Test Object");
    a.Invariant();
    a.ProcessABunchOfThings();
    a.Invariant();

要记住的重要一点是,这是为了确保当错误确实发生时,这意味着程序无法按您的预期运行,那么错误的影响尽可能接近代码中发生的位置为了使调试更容易。我在自己的代码中以及在 Microsoft 期间广泛使用了断言,我对它们发誓,因为它们为我节省了很多调试时间,甚至知道存在缺陷。

Assertions are used to to define how the program should function. That being said, the most common use of Assert()s when dealing with pointers is going to either be that they are valid (non-NULL and point towards valid memory) or that their internal state is valid if they point to an object/class instance, for example.

Assertions are not for replacing or acting as error condition code, but instead to enforce rules that you are placing on the functioning of your code, such as what conditions should be at given points in time.

For example,

    function int f(int x, int * pY)
    {
        // These are entrance conditions expected in the function. It would be
        // a BUG if this happened at all.
        Assert(x >= 0);
        Assert(pY != nullptr);

        Assert(*pY >= 0, "*pY should never be less than zero");

        // ...Do a bunch of computations with x and pY and return the result as z...
        int z = x * 2 / (x + 1) + pow(*pY, x);  // Maybe z should be always positive 
                                                // after these calculations:
        Assert(x >= 0, "X should always be positive after calculations);
        // Maybe *pY should always be non-zero after calculations
        Assert(*pY != 0, "y should never be zero after computation");
        Assert(z > 0):

        return z;
    }

Many users of Asserts choose to apply Assertions to internal state validation once they become familiar with them. We call these Invariants() which are methods on a class that assert many things about the internals of the object that should always hold true.

For example:

    class A
    {
    public:
        A(wchar_t * wszName)
        {
             _cch = 0;
             _wszName = wszName;
        }
        // Invariant method to be called at times to verify that the
        // internal state is consistent.  This means here that the
        // internal variable tracking the length of the string is
        // matching the actual length of the string.
        void Invariant()
        {
            Assert(pwszName != nullptr);
            Assert(_cch == wcslen(pwszName));
        }

        void ProcessABunchOfThings()
        {
            ...
        }
    protected:
        int _cch;
        wchar_t * pwszName;
    }

    // Call to validate internal state of object is consistent/ok
    A a(L"Test Object");
    a.Invariant();
    a.ProcessABunchOfThings();
    a.Invariant();

The important thing to remember is that this is to make sure that when bugs do happen that mean the program is not working as you would expect, then the effect of the bug happens as close to where it happened in the code as possible in order to make debugging easier. I have used Asserts extensively in my own code and while at Microsoft and I swear by them since they have saved me so much time in debugging and even knowing the defect is there.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文