В настоящее время я изучаю C, читая хорошую книгу начинающих под названием "Teach Yourself C через 21 день" (я уже изучил Java и С#, поэтому я продвигаюсь гораздо быстрее). Я читал главу о указателях, и оператор → (стрелка) подошел без объяснений. Я думаю, что он используется для вызова членов и функций (например, эквивалент оператора. (Dot), но для указателей вместо членов). Но я не совсем уверен. Могу ли я получить объяснение и образец кода?
foo->bar
эквивалентен (*foo).bar
, т.е. получает член, называемый bar
из структуры, на которую указывает foo
.
->
вообще не понадобился бы, поскольку он был бы эквивалентен гораздо более разборчивому foo*.bar
. Также можно было бы избежать всей путаницы функций определения типов со всеми дополнительными скобками.
foo*.bar
и (*foo).bar
эквивалентны foo->bar
? Что насчет Foo myFoo = *foo; myFoo.bar
?
Да, это так.
Это просто точечная версия, когда вы хотите получить доступ к элементам структуры/класса, которые являются указателем, а не ссылкой.
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
Это!
pvar = &var
?
a->b
всего лишь коротко для (*a).b
во всех отношениях (то же самое для функций: a->b()
является коротким для (*a).b()
).
foo->bar
является только сокращением для (*foo).bar
. Это все, что нужно.
Я просто добавлю к ответам "почему?".
.
является стандартным оператором доступа к члену, который имеет более высокий приоритет, чем оператор *
.
Когда вы пытаетесь получить доступ к внутренним структурам, и вы написали их как *foo.bar
, тогда компилятор подумает, что нужен элемент "bar" из "foo" (который является адресом в памяти) и, очевидно, что простой адрес не имеют членов.
Таким образом, вам нужно попросить компилятор сначала разыменовать (*foo)
, а затем получить доступ к элементу-члену: (*foo).bar
, который немного неудобно писать, поэтому хорошие люди придумали сокращенную версию: foo->bar
который является типом доступа членов оператором указателя.
struct Node {
int i;
int j;
};
struct Node a, *p = &a;
Здесь для доступа к значениям i
и j
мы можем использовать переменную a
и указатель p
следующим образом: a.i
, (*p).i
и p->i
все одинаковы.
Здесь .
- "Прямой селектор", а ->
- "Непрямой селектор".
Оператор ->
делает код более читаемым, чем оператор *
в некоторых ситуациях.
Такие как: (цитируется из проект 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...)
.
Что выглядит лучше?
#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;
}
Мне пришлось внести небольшое изменение в программу Джека, чтобы заставить его работать. После объявления указателя 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
%
для представления текущего имени файла. !gcc % && ./a.out
#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
Dot - это оператор разыменования и используется для связывания структурной переменной для конкретной записи структуры. Например:
struct student
{
int s.no;
Char name [];
int age;
} s1,s2;
main()
{
s1.name;
s2.name;
}
Таким образом, мы можем использовать оператор точки для доступа к структурной переменной
->
. Также на этот вопрос уже 4,5 года.