【问题标题】:What if I need to differentiate 0 from NULL in C++?如果我需要在 C++ 中区分 0 和 NULL 怎么办?
【发布时间】:2015-06-02 21:58:14
【问题描述】:

** 请不要批评代码本身的目的。它来自 Pat Morin 的 Open Data Structures 书。不是我的第一选择,它指定的阅读/练习。我只是想知道是否有区分的方法,或者更好的方法来解决这个问题。教科书-->http://opendatastructures.org/ods-cpp/**

** 另一个注意事项:我来自 Java,这是允许的。我的代码仍然可以编译,它只是“修复”它**

我很惊讶以前没有出现过这样的问题,因为这似乎是一个如此简单的问题。也许它被埋没了,或者我没有使用正确的术语。

我有一个遍历向量中数据的 for 循环。如果找到,我需要返回正在搜索的值。如果找不到怎么办?这是我的代码。

int find(int x) {
    for(int i=0;i<bag.size();i++){
        // if x is equal to data, return data
        if (bag[i]==x){
            return bag[i]; // ends loop as soon as one instance is found
        }
    }
    // if we made it this far, no match was found.
    return NULL;

}

很简单。假设 0 是我可能需要记录和搜索的有效值之一。事实上,它实际上返回 0,而不是“NULL”。研究表明它是一回事。如何指定或区分?除了返回一个不会出现在程序中的晦涩数字之外,因为我们可能并不总是拥有那种奢侈(如 -1 或 -9999999)。例如,搜索您的帐户余额。没有数字是不可能的。

【问题讨论】:

  • 如果找到 x(作为参数传递),返回作为参数传递的相同值有什么意义?为什么不使用 bool 作为返回值呢?
  • NULL 并不意味着是 int => 代码无论如何都是错误的。作为搜索方法的错误值,-1 是常用的。
  • 我是否理解正确,如果找到该值,该函数将返回与输入相同的值?如果是,您只需返回任何其他号码,调用代码将能够知道是否找到它。或者更好的是,让它返回 true 或 false。
  • 如果你阅读我的笔记 deviantfan,你会发现我已经解决了这个问题。您可以在其他一些语言中返回 null,因此为了清楚起见,我将其保留为原样。 @tobi303 - 它来自教科书中的一个练习题。不要让我从教科书开始。这就是为什么我在这里而不是在我的 uni 论坛上问的原因。
  • 这是一个从其他语言迁移的人可能会提出的问题,合理地说,没有理由反对

标签: c++ vector null return-value function-definition


【解决方案1】:

为什么要从 find 函数返回要搜索的值?您已经知道值,它是您传递给函数的值。而是返回找到的元素的位置,因为此信息更有用。当未找到该值时,您可以返回一个特殊位置,例如 -1。或者您可以按照标准库的模型并返回结束迭代器,这表示一个位置超出范围的末尾。

【讨论】:

  • 返回optional&lt;position_t&gt; 比发明一个特殊的位置值要好。
  • @Puppy 假设他使用 Boost,这根本不是微不足道的。
  • @Puppy:不,不是。当调用者已经知道值时,为什么要不必要地构造一个对象?
  • @Puppy:仍然没有更好。这是一个完全无用的信息。位置是调用者可以实际使用的东西。
  • @Benjamin:没有明确说明,但我说的是optional&lt;position_t&gt;。我的意思是,无论返回位置、迭代器或值,可选性的机制都是相同的。
【解决方案2】:

您可以通过多种方式编写函数

bool find( int x ) 
{
    std::vector<int>::size_type i = 0;

    while (  i < bag.size() && bag[i] != x ) i++;

    return i != bag.size();
}

或者

std::vector<int>::size_type find( int x ) 
{
    std::vector<int>::size_type i = 0;

    while (  i < bag.size() && bag[i] != x ) i++;

    return i;
}

或者

#include <algorithm>

//...

std::vector<int>::iterator find( int x ) 
{
    return std::find( beg.begin(), bag.end(), x );
}

并通过以下方式对应使用功能

if ( find( x ) ) { /*...*/ }

if ( find( x ) != bag.size() ) { /*...*/ }

if ( find( x ) != bag.end() ) { /*...*/ }

关于你在帖子标题中的一般性问题

如果我需要在 C++ 中区分 0 和 NULL 怎么办?

那么你需要使用nullptr而不是NULL来区分0和NULL。:)

【讨论】:

  • 首先展示算法的方式来鼓励使用 stl 而不是编写自己的循环是有意义的
  • @Glenn Teitelbaum 也许现在对他来说更重要的是自己编写函数作为练习。:)
【解决方案3】:
#define XOR_MSB(x) (x^0x80000000)

int find(bool found) {
    return found ? XOR_MSB(0) : NULL;
}

int main()
{
    int value = find(false);
    if (value == NULL) printf("not found\n");
    else printf("%d\n", XOR_MSB(value));
    value = find(true);
    if (value == NULL) printf("not found\n");
    else printf("%d\n", XOR_MSB(value));
    return 0;
}

【讨论】:

    【解决方案4】:

    您说的是一种邪恶的做法,来自 Java 和 C# 开发人员,您可以将 null 作为无效结果返回。

    好吧,你不能在 C++ 中做到这一点。 Java 和 C# 声明了堆上的几乎所有内容,并且访问对象始终由引用完成。这意味着您始终可以从函数返回一个指针,并且您始终可以返回 null 作为返回值。

    当函数不返回指针时,这在 C++ 中是不可能的。例如函数std::string returnString()不能返回null作为返回值。

    此外,您不应该将 null 作为无效输出返回。即使在 C# 和 Java 中,这在很多层面上都是错误的!如果您的函数失败,只需抛出一个异常,或者将您的返回值作为引用参数传递,并使函数返回 true 或 false。

    您还可以找到不那么激进的解决方案,例如返回 -1(如 javascript 中的 indexOf())或返回类似 std::string::nposstd::vector:end 之类的东西,它们更加 C++ STL 调整。

    【讨论】:

    • 这些都不是真正的正确答案。根据您的用例(在 Java 或 C# 中),为“未找到”返回 null 可能没问题。像indexOf()std::string::npos 和朋友这样的东西都很糟糕,因为它们不是通用的。 std::vector::end 稍微不那么糟糕,因为它是通用的,但只是以特定的方式。
    【解决方案5】:

    这就是boost::optional&lt;T&gt; 的用途。实际上,此类型表示“可能有T,但可能没有T”。用户可以检查是否有T。理想情况下,您不会依赖T 的任何特殊值,而是使用optional&lt;T&gt;optional&lt;T&gt; 是完全通用的,可能适用于您可能需要使用的几乎任何 T,它不依赖于 T 的某些特殊值或状态。

    boost::optional<int> find(int x) {
        for(int i=0;i<bag.size();i++){
            // if x is equal to data, return data
            if (bag[i]==x){
                return bag[i]; // ends loop as soon as one instance is found
            }
        }
        // if we made it this far, no match was found.
        return boost::none;    
    }
    int main() {
        if (auto opt = find(5))
            std::cout << *opt;
    }
    

    永远不要使用NULL,这是一件可怕的事情。始终使用nullptr,这实际上更安全,它更安全的原因之一是您的错误代码无法编译。

    【讨论】:

    • 我觉得你歌颂boost::optional 很有趣,但很容易忘记它会在呼叫站点产生“开销”,而你在main 中没有考虑到这一点,从而导致潜在的错误代码。
    • 尝试使用基础而不是其他包含 boost::optional 关键字类型的库。
    猜你喜欢
    • 2021-09-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-17
    • 1970-01-01
    • 2014-10-19
    • 2012-06-20
    • 1970-01-01
    相关资源
    最近更新 更多