Использование оператора стрелки (->) в C

195

В настоящее время я изучаю C, читая хорошую книгу начинающих под названием "Teach Yourself C через 21 день" (я уже изучил Java и С#, поэтому я продвигаюсь гораздо быстрее). Я читал главу о указателях, и оператор → (стрелка) подошел без объяснений. Я думаю, что он используется для вызова членов и функций (например, эквивалент оператора. (Dot), но для указателей вместо членов). Но я не совсем уверен. Могу ли я получить объяснение и образец кода?

  • 73
    Получить лучшую книгу. norvig.com/21-days.html
  • 38
    Просто примечание: книги под названием «научи себя ХХХ через ХХХ дней / недель / часов» обычно не годятся. Получить копию K & R.
Показать ещё 5 комментариев
Теги:
pointers
syntax

11 ответов

383
Лучший ответ

foo->bar эквивалентен (*foo).bar, т.е. получает член, называемый bar из структуры, на которую указывает foo.

  • 42
    Стоит отметить, что если бы оператор разыменования был сделан постфиксным, как в Pascal, оператор -> вообще не понадобился бы, поскольку он был бы эквивалентен гораздо более разборчивому foo*.bar . Также можно было бы избежать всей путаницы функций определения типов со всеми дополнительными скобками.
  • 0
    Значит, foo*.bar и (*foo).bar эквивалентны foo->bar ? Что насчет Foo myFoo = *foo; myFoo.bar ?
Показать ещё 1 комментарий
109

Да, это так.

Это просто точечная версия, когда вы хотите получить доступ к элементам структуры/класса, которые являются указателем, а не ссылкой.

struct foo
{
  int x;     // 5
  float y;
};

struct foo var;
struct foo* pvar;

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

Это!

  • 3
    Поскольку pvar не инициализирован, как бы вы его инициализировали, если бы хотели, чтобы pvar указывал на новую структуру, то есть это не pvar = &var ?
  • 0
    Вопрос был конкретно о C, который не имеет классов или ссылочных переменных.
Показать ещё 2 комментария
26

a->b всего лишь коротко для (*a).b во всех отношениях (то же самое для функций: a->b() является коротким для (*a).b()).

  • 1
    Есть ли документация, в которой говорится, что это также работает для методов?
17

foo->bar является только сокращением для (*foo).bar. Это все, что нужно.

13

Я просто добавлю к ответам "почему?".

. является стандартным оператором доступа к члену, который имеет более высокий приоритет, чем оператор *.

Когда вы пытаетесь получить доступ к внутренним структурам, и вы написали их как *foo.bar, тогда компилятор подумает, что нужен элемент "bar" из "foo" (который является адресом в памяти) и, очевидно, что простой адрес не имеют членов.

Таким образом, вам нужно попросить компилятор сначала разыменовать (*foo), а затем получить доступ к элементу-члену: (*foo).bar, который немного неудобно писать, поэтому хорошие люди придумали сокращенную версию: foo->bar который является типом доступа членов оператором указателя.

9
struct Node {
    int i;
    int j;
};
struct Node a, *p = &a;

Здесь для доступа к значениям i и j мы можем использовать переменную a и указатель p следующим образом: a.i, (*p).i и p->i все одинаковы.

Здесь . - "Прямой селектор", а -> - "Непрямой селектор".

1

Оператор -> делает код более читаемым, чем оператор * в некоторых ситуациях.

Такие как: (цитируется из проект EDK II)

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...)

Но с оператором -> вы можете написать вот так:

pStruct->ReadBlocks(...arguments...).

Что выглядит лучше?

  • 1
    Я думаю, что код был бы более читабельным, если бы он был не во всех заглавных буквах, как это было напечатано подростками в чате AOL из 90-х.
1
#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;
}
1

Мне пришлось внести небольшое изменение в программу Джека, чтобы заставить его работать. После объявления указателя struct pvar укажите его на адрес var. Я нашел это решение на стр. 242 программирования Stephen Kochan на C.

#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
  • 2
    Совет vim: используйте % для представления текущего имени файла. !gcc % && ./a.out
0
#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

0

Dot - это оператор разыменования и используется для связывания структурной переменной для конкретной записи структуры. Например:

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

main()
    {
      s1.name;
      s2.name;
    }

Таким образом, мы можем использовать оператор точки для доступа к структурной переменной

  • 6
    Какую ценность это добавляет? Пример немного плох по сравнению с другими ответами, которые фактически сравнивают его с -> . Также на этот вопрос уже 4,5 года.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню