【问题标题】:Arrow operator (->) usage in CC 中的箭头运算符 (->) 用法
【发布时间】:2011-02-04 05:25:53
【问题描述】:

我正在阅读一本名为“21 天内自学 C”的书(我已经学习了 Java 和 C#,所以我的进度要快得多)。我正在阅读关于指针的章节,->(箭头)运算符 没有解释就出现了。我认为它用于调用成员和函数(类似于.(点)运算符,但用于指针而不是成员)。但我不完全确定。

我可以得到一个解释和代码示例吗?

【问题讨论】:

  • 买一本更好的书。 norvig.com/21-days.html
  • qrdl 是正确的——“在 Y 天内学习 X”的书通常是垃圾。除了 K&R,我还推荐 Prata 的“C Primer Plus”,它比 K&R 更深入。
  • @Steve 该问题涉及 C++。当我开始在另一个答案中阅读有关运算符重载的信息时,称它为 a 给我带来了一些困惑,这在 C 中不相关。
  • @Belton 艰难的方式系列很糟糕,这家伙说的东西在他写这本书时甚至都不相关,他也不关心良好的做法。
  • 他没有说这是一本好书!这只是他出于某种原因碰巧在读的那本书。也许他从中得到了很多,他计划在完成之后再读一本更好的书。像这样的评论很烦人

标签: c pointers syntax


【解决方案1】:

foo->bar 等价于(*foo).bar,即它从foo 指向的结构中获取名为bar 的成员。

【讨论】:

  • 值得注意的是,如果取消引用操作符是后缀,就像在 Pascal 中一样,-> 操作符根本就不需要,因为它等同于更易读的 @ 987654326@。带有所有额外括号的类型定义函数的整个混乱也将被避免。
  • 那么foo*.bar(*foo).bar 是否都等同于foo->barFoo myFoo = *foo; myFoo.bar 呢?
  • 不,他只是说 IF C 的创建者会将取消引用运算符设为 POSTfix 运算符而不是 PREfix,这样会更容易。但它是 C 中的前缀运算符。
  • @user207421 您能否提供一个简短的描述或链接到您提到的“带有所有额外括号的类型定义函数”?谢谢。
  • @user207421 不,这会导致更多的父母.. 到目前为止,() 和 [] 的优先级在右边 * 到左边。如果他们都在一边,你就会有更多的父母。表达式相同,因为与乘法运算符冲突。 Pascal ^ 可能是一个选项,但它是为位操作保留的,还有更多的父母。
【解决方案2】:

是的,就是这样。

当您想要访问作为指针而不是引用的结构/类的元素时,这只是点版本。

struct foo
{
  int x;
  float y;
};

struct foo var;
struct foo* pvar;
pvar = malloc(sizeof(struct foo));

var.x = 5;
(&var)->y = 14.3;
pvar->y = 22.4;
(*pvar).x = 6;

就是这样!

【讨论】:

  • 由于 pvar 未初始化,如果您希望 pvar 指向一个新结构,而不是 pvar = &var,您将如何初始化它?
  • 这个问题是关于 C 的,它没有类或引用变量。
  • hmm 在写入 pvar struct foo* pvar; 之前你不应该做一个 malloc 吗? ?? pvar->y 写入未分配的空间!
  • pvar 初始化:手动将所有成员初始化为您想要拥有的一些默认值,或者如果零填充对您来说可以使用类似 calloc() 的东西。
  • 不应该是:pvar = malloc(sizeof(struct foo)) 还是 malloc(sizeof(*pvar))??
【解决方案3】:

我只想在答案中添加“为什么?”。

. 是标准成员访问运算符,其优先级高于* 指针运算符。

当您尝试访问结构的内部并将其写为*foo.bar 时,编译器会认为需要 'foo' 的 'bar' 元素(这是内存中的地址),显然只是地址没有任何成员。

因此,您需要让编译器首先取消引用 (*foo),然后访问成员元素:(*foo).bar,这写起来有点笨拙,所以好人想出了一个速记版本:foo->bar这是指针运算符的一种成员访问。

【讨论】:

    【解决方案4】:

    a->b 在各个方面都是 (*a).b 的缩写(函数相同:a->b()(*a).b() 的缩写)。

    【讨论】:

    • 是否有文档表明它也适用于方法?
    【解决方案5】:

    foo->bar 只是(*foo).bar 的简写。仅此而已。

    【讨论】:

      【解决方案6】:
      struct Node {
          int i;
          int j;
      };
      struct Node a, *p = &a;
      

      这里要访问ij的值,我们可以使用变量a和指针p如下:a.i(*p).ip->i都是一样的。

      这里. 是“直接选择器”,-> 是“间接选择器”。

      【讨论】:

        【解决方案7】:

        好吧,我也必须添加一些东西。结构与数组有点不同,因为数组是指针而结构不是。所以要小心!

        假设我写了这段无用的代码:

        #include <stdio.h>
        
        typedef struct{
                int km;
                int kph;
                int kg;
            } car;
        
        int main(void){
        
            car audi = {12000, 230, 760};
            car *ptr = &audi;
        
        }
        

        这里的指针ptr指向结构变量audi的地址(!),但地址结构旁边还有一个数据块()! 数据块的第一个成员与结构本身具有相同的地址,您可以通过取消引用像这样的指针*ptr (没有大括号)来获取它的数据。

        但是如果你想访问除第一个成员之外的任何其他成员,你必须添加一个指示符,如.km.kph.kg,它们只不过是 基地址的偏移量数据块...

        但是由于preceedence,你不能写*ptr.kg,因为访问运算符.在取消引用运算符*之前被评估,你会得到*(ptr.kg),这是不可能的,因为指针没有成员!编译器知道这一点,因此会发出错误,例如:

        error: ‘ptr’ is a pointer; did you mean to use ‘->’?
          printf("%d\n", *ptr.km);
        

        相反,您使用此 (*ptr).kg 并强制编译器 1st 取消引用指针并启用对 数据块2nd 的访问您添加一个偏移量(指示符)来选择成员。

        查看我制作的这张图片:

        但是如果你有嵌套的成员,这个语法就会变得不可读,因此引入了-&gt;。我认为可读性是使用它的唯一正当理由,因为 ptr-&gt;kg(*ptr).kg 更容易编写。

        现在让我们以不同的方式写出来,以便您更清楚地看到联系。 (*ptr).kg(*&amp;audi).kgaudi.kg。在这里,我首先使用了ptraudi“地址”&amp;audi 的事实以及“reference” &amp; “取消引用” * 运营商相互抵消。

        【讨论】:

        • 你的回答太棒了!
        【解决方案8】:

        我必须对 Jack 的程序进行一些小改动才能使其运行。声明完struct指针pvar后,指向var的地址。我在 Stephen Kochan 的 Programming in C 第 242 页找到了这个解决方案。

        #include <stdio.h>
        
        int main()
        {
          struct foo
          {
            int x;
            float y;
          };
        
          struct foo var;
          struct foo* pvar;
          pvar = &var;
        
          var.x = 5;
          (&var)->y = 14.3;
          printf("%i - %.02f\n", var.x, (&var)->y);
          pvar->x = 6;
          pvar->y = 22.4;
          printf("%i - %.02f\n", pvar->x, pvar->y);
          return 0;
        }
        

        使用以下命令在 vim 中运行:

        :!gcc -o var var.c && ./var
        

        将输出:

        5 - 14.30
        6 - 22.40
        

        【讨论】:

        • vim 提示:使用% 表示当前文件名。像这样:!gcc % &amp;&amp; ./a.out
        【解决方案9】:
        #include<stdio.h>
        
        int main()
        {
            struct foo
            {
                int x;
                float y;
            } var1;
            struct foo var;
            struct foo* pvar;
        
            pvar = &var1;
            /* if pvar = &var; it directly 
               takes values stored in var, and if give  
               new > values like pvar->x = 6; pvar->y = 22.4; 
               it modifies the values of var  
               object..so better to give new reference. */
            var.x = 5;
            (&var)->y = 14.3;
            printf("%i - %.02f\n", var.x, (&var)->y);
        
            pvar->x = 6;
            pvar->y = 22.4;
            printf("%i - %.02f\n", pvar->x, pvar->y);
        
            return 0;
        }
        

        【讨论】:

          【解决方案10】:

          在某些情况下,-&gt; 运算符使代码比 * 运算符更具可读性。

          如:(引自EDK II project

          typedef
          EFI_STATUS
          (EFIAPI *EFI_BLOCK_READ)(
            IN EFI_BLOCK_IO_PROTOCOL          *This,
            IN UINT32                         MediaId,
            IN EFI_LBA                        Lba,
            IN UINTN                          BufferSize,
            OUT VOID                          *Buffer
            );
          
          
          struct _EFI_BLOCK_IO_PROTOCOL {
            ///
            /// The revision to which the block IO interface adheres. All future
            /// revisions must be backwards compatible. If a future version is not
            /// back wards compatible, it is not the same GUID.
            ///
            UINT64              Revision;
            ///
            /// Pointer to the EFI_BLOCK_IO_MEDIA data for this device.
            ///
            EFI_BLOCK_IO_MEDIA  *Media;
          
            EFI_BLOCK_RESET     Reset;
            EFI_BLOCK_READ      ReadBlocks;
            EFI_BLOCK_WRITE     WriteBlocks;
            EFI_BLOCK_FLUSH     FlushBlocks;
          
          };
          

          _EFI_BLOCK_IO_PROTOCOL 结构包含 4 个函数指针成员。

          假设您有一个变量struct _EFI_BLOCK_IO_PROTOCOL * pStruct,并且您想使用旧的* 运算符来调用它的成员函数指针。你最终会得到这样的代码:

          (*pStruct).ReadBlocks(...arguments...)

          但是使用-&gt; 运算符,你可以这样写:

          pStruct-&gt;ReadBlocks(...arguments...).

          哪个更好看?

          【讨论】:

            【解决方案11】:
            #include<stdio.h>
            struct examp{
            int number;
            };
            struct examp a,*b=&a;`enter code here`
            main()
            {
            a.number=5;
            /* a.number,b->number,(*b).number produces same output. b->number is mostly used in linked list*/
               printf("%d \n %d \n %d",a.number,b->number,(*b).number);
            }
            

            输出为 5 5 5

            【讨论】:

              【解决方案12】:

              点是一个解引用运算符,用于连接结构变量以获取结构的特定记录。 例如:

              struct student
                  {
                    int s.no;
                    Char name [];
                    int age;
                  } s1,s2;
              
              main()
                  {
                    s1.name;
                    s2.name;
                  }
              

              这样我们就可以使用点运算符来访问结构体变量了

              【讨论】:

              • 这增加了什么价值?与实际将其与-&gt; 进行比较的其他答案相比,该示例有点差。这个问题也已经回答了 4.5 年了。
              猜你喜欢
              • 2012-12-04
              • 2012-10-12
              • 1970-01-01
              • 1970-01-01
              • 2014-01-30
              • 1970-01-01
              • 1970-01-01
              • 2011-08-16
              相关资源
              最近更新 更多