【问题标题】:Order of incrementing and dereferencing pointer in C++C++中递增和取消引用指针的顺序
【发布时间】:2015-10-15 23:05:37
【问题描述】:

我辅导 C++ 的学生,最近遇到了一个涉及数组名称的指针运算的问题。我最困惑的是声明

T min_value = *begin++; 

Cplusplus 告诉我 ++ 运算符的优先级高于 * 取消引用运算符,因此我假设 begin 首先递增然后取消引用。此外,this 站点确认,当您将数组的名称传递给函数时,它会变成指向第一个元素(元素 [0])地址的指针。但是,当我在 Visual Studio 中运行下面的代码时,看起来 min_value 一开始就设置为 1.5,这似乎与我认为的操作顺序相矛盾。

我认为应该是:

  1. 增加指向 [1] 元素(数组中的第二个)的开始指针
  2. 取消引用指针值
  3. 将 min_value 设置为等于数组中的第二个元素。

但是,我的实验似乎表明正在发生一些不同的事情:

  1. 取消引用指针值
  2. 设置 min_value 等于数组的第一个元素
  3. 增加指向下一个元素的指针

有人可以澄清一下吗?

// Problem #3: Please write the implementation of min() function and max() function..

#include <iostream> 
using namespace std; 
template<typename T> 

T min(T* begin, T* end) 
{ 
        T min_value = *begin++; 
        while(begin != end) // You can use for-loop too. 
        { 
                if( *begin < min_value) 
                        min_value = *begin; 
                begin++; 
        } 
        return min_value; 
} 
template<typename T> 
T max(T* begin, T* end) 
{ 
        T max_value = *begin++; 
        while(begin != end) 
        { 
                if( *begin > max_value) 
                        max_value = *begin; 
                begin++; 
        } 
        return max_value; 
} 
int main() 
{ 
        double arr[] = {    1.5, 4.5, 3.5, 2.5, 5.5 }; 
        int values[] = {    1, 2, 3, 4, -1, 5 }; 
        cout << "min of arr[] is : " << min(arr, arr + 5) << endl; 
        cout << "min of values[] is : " << min(values, values + 6) << endl; 
        cout << "max of arr[] is : " << max(arr, arr + 5) << endl; 
        cout << "max of values[] is : " << max(values, values + 6) << endl; 
}

【问题讨论】:

  • 顺便说一句,如果他们是初学者,你应该教你的学生不要使用using namespace。否则,他们最终将在标头中的全局范围内使用它。此外,minmax 是名称冲突的特别热门候选,因为 &lt;algorithm&gt; 中潜伏着 std::minstd::max 函数。

标签: c++ arrays algorithm pointers post-increment


【解决方案1】:

这个表达式

T min_value = *begin++; 

可以想象如下方式

auto temp = begin;
T min_value = *temp;
++begin;

根据 C++ 标准(5.2.6 递增和递减)

1 后缀 ++ 表达式的值是 它的值 操作数。 [注:得到的值是原值的副本 ——尾注] ...++表达式的值计算是有序的 在修改操作对象之前。

一般情况下,函数定义是错误的,因为beginend 指定的范围可以为空,并且begin 可以指向有效范围之外。在这种情况下,您既不能增加 begin 也不能取消引用它。

因此,例如以下方式编写会更正确

template <typename T>
T * min( T* begin, T* end ) 
{
    T *min_value = begin;
 
    if ( begin != end )
    {
        while( ++begin != end )
        { 
            if( *begin < *min_value ) min_value = begin; 
        } 
    }

    return min_value; 
} 

在这种情况下,函数的调用看起来像

cout << "min of arr[] is : " << *min(arr, arr + 5) << endl;
                               ^^^

【讨论】:

  • 这很有道理,你能解释一下为什么会这样吗?具体来说,为什么取消引用和递增似乎是独立发生的?
  • @wyverniv:它们不会独立发生。只是后缀++ 不会only 递增,它还会复制。所以顺序是复制->递增->返回复制->取消引用复制。
  • @wyverniv 查看我更新的帖子,带有后缀自增运算符的表达式的值是应用自增之前操作数的值。
【解决方案2】:

您不应混淆运算符的返回值和优先级。

第一个处理操作符返回的内容,第二个处理发生事件的时间。

如果你有:

T min_value = *begin++;

这是它的工作原理:

  1. operator++ - 它增加指针,但返回最初存在的指针。
  2. operator* - 取消引用之前返回的指针,返回它指向的 T。
  3. operator= 将左侧存储到右侧,返回右侧。

您不使用最后一个返回值,但理论上可以。

请注意,在 #2 中,您使用 #1 的返回值,而不是再次访问指针。

【讨论】:

    【解决方案3】:

    优先级只是如何解析代码的规则。 ++ 排在第一位,* 排在第二位。但是当代码执行时,你必须考虑操作符实际上是做什么的

    在您的情况下,会发生以下情况:

    1. 制作了begin 的副本。
    2. 原件增加。
    3. 副本被退回。
    4. 副本被取消引用
    5. 副本分配给min_value

    这就是后自增运算符的工作原理,也是当您为自己的类型重载它时编写运算符的方式:

    T operator++(int)
    {
        T copy = *this;
        ++(*this);
        return copy;
    }
    

    实际上,在内置后自增运算符的情况下,自增不一定是第 2 步。它也可能在稍后发生,只要可观察的行为相同。例如,没有什么能阻止编译器在返回副本后递增原始值。当然,您不能在自己的重载运算符中执行这样的操作。

    【讨论】:

    • 第 2 步可能随时发生,不一定按此顺序进行
    • @MattMcNabb:你是对的。我编辑了答案以包含这些信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-26
    • 2010-10-25
    • 1970-01-01
    • 1970-01-01
    • 2019-05-20
    相关资源
    最近更新 更多