【问题标题】:Pointers: initialisation vs. declaration指针:初始化与声明
【发布时间】:2017-11-20 00:19:13
【问题描述】:

我是一个 C++ 菜鸟,我很确定这是一个愚蠢的问题,但我只是不太明白为什么以下代码会出现(不出现)错误:

#include <iostream>
using namespace std;


int main() 
{
int a,*test; 

*test = &a;  // this error is clear to me, since an address cannot be 
             // asigned to an integer  


*(test = &a); // this works, which is also clear
return 0;
}

但为什么这也行得通?

#include <iostream>
using namespace std;

int main() 
{
int a, *test= &a;  // Why no error here?, is this to be read as:
                   // *(test=&a),too? If this is the case, why is the 
                   // priority of * here lower than in the code above?

return 0;
}

【问题讨论】:

  • int *x = y; 表示yx 的初始化器(不是*x)。也不是赋值表达式
  • @M.M 好吧,我必须阅读 int *test = &a as int *(test = &a) ?
  • 不,读作声明了一个名为test的变量,类型为int *,初始化程序为&amp;a
  • * = &amp; 在声明中的含义与在表达式中的含义不同
  • 如您所见,最好避免在一行上声明多个变量。

标签: c++ pointers c++14


【解决方案1】:

这两行的根本区别

*test= &a; // 1
int a, *test= &a; // 2

是第一个是表达式,由具有已知优先级规则的运算符调用组成:

       operator=
          /\
        /    \
      /        \
operator*    operator&
  |             | 
 test           a

而第二个是变量声明和初始化,等价于int a;的声明后跟:

   int*     test    =  &a
// ^^        ^^        ^^
//type    variable    expression giving
//          name        initial value

operator*operator= 都没有在第二行中使用。

标记*=(以及&amp; 以及,)的含义取决于它们出现的上下文:它们所在的表达式内部对于相应的运算符,但在声明中* 通常作为类型的一部分出现(意思是“指向”),= 用于标记(复制)初始化表达式的开始(, 分隔多个声明, &amp; 作为“引用”也是类型的一部分)。

【讨论】:

  • 好答案.. 这一定是最好的。我刚刚删除了我的解释。
  • 加一个用于使用 ASCII 艺术。
  • @isanae 确实,我试图用一句话使其易于理解的尝试太模糊了。我加了一点细节,现在好些了吗?
  • @DanielJour 看起来很棒!
  • @DanielJour 非常感谢您!不知道解引用操作符中的指针声明中的*有区别*
【解决方案2】:
int a, *test= &a;

相当于:

int a;
int* test = &a;

并且当您初始化test 时完全有效,该test 的类型为指向int 的指针,其地址为变量a,其类型为int

【讨论】:

  • Rokyan 但为什么是 int a, *test; *测试 = &a;无效?
  • @maxE 因为在这种情况下您 取消引用 test 的类型为int*,因此您尝试将int* 分配给int,这不是有效。
【解决方案3】:

您混淆了 * 的两种用途。

在您的第一个示例中,您使用它来取消引用指针。 在第二个示例中,您使用它来声明“指向 int 的指针”。

因此,当您在声明中使用 * 时,就表示您正在声明一个指针。

【讨论】:

    【解决方案4】:

    在第一种情况下,您实际上是在进行这样的初始化,

    int *test = &a;
    

    这意味着,你正在初始化一个你提到*的指针,告诉编译器它是一个指针。

    但是在初始化后执行*test带有星号)意味着您正在尝试访问分配给指针test的地址处的值。
    换句话说,执行*test 意味着您将获得a 的值,因为a 的地址存储在指针test 中,只需执行&amp;a
    &amp; 是运算符来获取任何变量的地址。而*是获取地址值的操作符。

    因此,即使星号 * 在这两种情况下都存在,编译器也会以不同的方式推断 initialisationassignment

    【讨论】:

      【解决方案5】:

      您刚刚遇到了两个可怕的语言设计点:将声明压缩到一行中,以及将* 符号重用于不相关的目的。在这种情况下,* 用于声明一个指针(当它用作类型签名 int a,*test; 的一部分时)和引用一个指针(当它用作语句 *test = &amp;a; 时)。好的做法是一次声明一个变量,使用自动类型推导而不是类型复制粘贴,并使用专用的addressof 方法:

      #include <memory> // for std::addressof
      
      int a{};
      auto const p_a{::std::addressof(a)};
      

      【讨论】:

      • 这可能适用于这种情况,但您的代码实际上 更改语义:您的代码使用 直接初始化 而原始代码使用 复制初始化。这在可能的隐式转换序列方面存在差异。
      • @DanielJour 直接初始化的使用还意味着构造函数查找的限制较少(也可以使用显式构造函数)。我的 sn-p 在它使用列表初始化的意义上也有所不同,这可能会导致可能的隐式转换序列的差异(int foo = 1.0f; - 很好,int foo = {1.0f}; - 错误)。
      • 这里的另一个区别是,与 OP 的未初始化 int a 相比,您明确地将 a 默认初始化为其默认值,即 0。虽然最好不要让事情未初始化除非我们可以证明它是合理的(因为有这种情况) - 我会说 IMO 我们应该明确地将初始化程序传递给基本类型,而不仅仅是晦涩的 {}
      • @underscore_d 重点是在所有情况下都使用{} 表示“使用默认内容初始化”,无论是初始化原始类型还是调用类默认构造函数。
      • 当然,我明白了,由此产生的一致性是一个很好的目标。我个人认为,对于值由用户控制的基本类型,明确声明我们想要 0 会更好。我认为我从未写过 int anything{}。但我没有反对它,除了偏好!这肯定比冒着意外使用未初始化变量的风险更好。
      【解决方案6】:

      那里有一个细微的差别。

      当您声明 int a, *test 时,您是在说“将 a 声明为整数,并将 test 声明为指向整数的指针,两者均未初始化。”

      在您的第一个示例中,您在声明之后立即将 *test 设置为 &a。这转换为:“将测试指向的整数(内存地址)设置为 a 的地址。”这几乎肯定会崩溃,因为 test 没有初始化,所以它要么是空指针,要么是乱码。

      在另一个示例中,int a, *test= &a 转换为:“将 a 声明为未初始化的整数,并将 test 声明为初始化为 a 的地址的指针。”这是有效的。更详细地说,它翻译为:

      int a, *test;
      test = &a;
      

      【讨论】:

        猜你喜欢
        • 2011-06-17
        • 1970-01-01
        • 2014-09-08
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-06-22
        • 2020-05-23
        相关资源
        最近更新 更多