Uso do operador de seta (->) em C

257

Estou lendo um livro chamado "Ensine-se C em 21 dias" (eu já aprendi Java e C #, por isso estou me movendo em um ritmo muito mais rápido). Eu estava lendo o capítulo sobre ponteiros e o operador-> (seta) surgiu sem explicação. Eu acho que é usado para chamar membros e funções (como o equivalente do operador (ponto), mas para ponteiros em vez de membros). Mas não tenho muita certeza..

Posso obter uma explicação e um exemplo de código?

Mohit Deshpande
fonte
90
Obtenha um livro melhor. norvig.com/21-days.html
joshperry
9
qrdl está correto - os livros "Aprenda X em Y dias" geralmente são lixo. Além da K&R, eu também recomendaria o "C Primer Plus" da Prata, que é mais profundo que a K&R.
31411 Taylor
3
@ Steve Essa pergunta lida com C ++. Chamando-a causou alguma confusão para mim quando eu comecei a ler sobre sobrecarga de operador em que outra resposta, que não é relevante em C.
Johann
1
@Belton As séries da maneira mais difícil são ruins, o cara diz coisas que nem eram relevantes quando ele escreveu o livro e ele não se importa com as boas práticas.
Bálint
1
O link de Peter Norvig para "Aprenda a programar em dez anos" é ótimo, um dos meus favoritos. Aqui está a versão em quadrinhos que explica como fazer isso em 21 dias, que infelizmente me lembrei como um XKCD, mas eu estava errado: abstrusegoose.com/249
Ted

Respostas:

462

foo->baré equivalente a (*foo).bar, ou seja, obtém o membro chamado barda estrutura que fooaponta para.

sepp2k
fonte
51
Vale a pena notar que, se o operador de desreferência tivesse sido feito pós-fixado, como em Pascal, o ->operador não seria necessário, pois seria equivalente ao muito mais legível foo*.bar. Toda a confusão de funções de digitação com todos os parênteses extras também teria sido evitada.
Marquês de Lorne
1
Então, foo*.bare (*foo).barambos seriam equivalentes a foo->bar? Que tal Foo myFoo = *foo; myFoo.bar?
Aaron Franke
9
Não, ele está apenas dizendo que se os criadores de C tivessem feito o operador de desreferência como operador POSTfix em vez de PREfix, seria mais fácil. Mas é um operador de prefixo em C.
reichhart
@ user207421 Você pode fornecer uma breve descrição ou link para as "funções de digitação com todos os parênteses extras" que você mencionou? Obrigado.
285 RoG
1
@ user207421 nah, isso causaria mais pais .. até agora, há prioridade de () e [] à direita acima * à esquerda. se todos estiverem de um lado, você terá colocado mais pais. O mesmo nas expressões, devido ao conflito com o operador de multiplicação. Pascal ^ poderia ser uma opção, mas estava reservado para operações de bits, ainda mais pais.
Swift - Friday Pie
130

Sim é isso.

É apenas a versão do ponto quando você deseja acessar elementos de uma estrutura / classe que é um ponteiro em vez de uma referência.

struct foo
{
  int x;
  float y;
};

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

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

É isso aí!

Jack
fonte
3
Como o pvar é não inicializado, como você o inicializaria se quisesse que o pvar apontasse para uma nova estrutura, não é pvar = &var?
precisa saber é o seguinte
A questão era especificamente sobre C, que não possui classes ou variáveis ​​de referência.
carvalho
1
hmm, você não deve fazer um malloc antes de escrever para pvar struct foo * pvar; ?? pvar-> y escreve para espaço não alocado!
Zibri
Inicialização pvar: inicialize todos os membros manualmente para alguns padrões que você deseja ter ou use algo como calloc () se zero preenchimento for adequado para você.
Reichhart 16/03/19
2
não deveria ser: pvar = malloc (sizeof (struct foo)) ou malloc (sizeof (* pvar)) ??
Yuri Aps
33

a->bé apenas uma abreviação de (*a).btodas as maneiras (o mesmo para funções: a->b()é abreviação de (*a).b()).

Peter Alexander
fonte
1
existe documentação que diz que também funciona dessa maneira para métodos?
AsheKetchum 06/07
28

Eu apenas adicionaria às respostas o "por quê?".

.é um operador de acesso de membro padrão que tem uma precedência mais alta que o *operador de ponteiro.

Quando você está tentando acessar as partes internas de uma estrutura e a escreveu da mesma forma *foo.bar, o compilador pensaria querer um elemento 'bar' de 'foo' (que é um endereço na memória) e, obviamente, esse mero endereço não possui nenhum membro.

Portanto, você precisa pedir ao compilador que primeiro desreferencie com (*foo)e depois acesse o elemento membro:, (*foo).barque é um pouco desajeitado de escrever, para que as pessoas boas tenham uma versão abreviada: foo->barque é uma espécie de acesso de membro pelo operador ponteiro.

Lukasz Matysiak
fonte
19

foo->baré apenas uma abreviação de (*foo).bar. É tudo o que há para isso.

Matti Virkkunen
fonte
10
struct Node {
    int i;
    int j;
};
struct Node a, *p = &a;

Aqui o para acessar os valores de ie jpodemos usar a variável ae o ponteiro pda seguinte forma: a.i, (*p).ie p->isão todos iguais.

Aqui .está um "Seletor Direto" e ->um "Seletor Indireto".

Jayghosh Wankar
fonte
2

Bem, eu tenho que adicionar algo também. Estrutura é um pouco diferente de matriz, porque matriz é um ponteiro e estrutura não. Por isso tem cuidado!

Digamos que eu escreva este pedaço de código inútil:

#include <stdio.h>

typedef struct{
        int km;
        int kph;
        int kg;
    } car;

int main(void){

    car audi = {12000, 230, 760};
    car *ptr = &audi;

}

Aqui o ponteiro ptraponta para o endereço ( ! ) Da variável de estrutura, audimas ao lado da estrutura de endereço também há um pedaço de dados ( ! )! O primeiro membro do pedaço de dados tem o mesmo endereço que a própria estrutura e você pode obtê-los apenas desreferenciando um ponteiro como este *ptr (sem chaves) .

Mas se você quer acesso a qualquer outro membro do que o primeiro, você tem que adicionar um designador como .km, .kph, .kgque são nada mais do que compensa para o endereço base do bloco de dados ...

Mas, devido à precedência, você não pode escrever *ptr.kgcomo o operador de acesso .é avaliado antes do operador de desreferência *e você obteria o *(ptr.kg)que não é possível, pois o ponteiro não possui membros! E o compilador sabe disso e, portanto, emitirá um erro, por exemplo:

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

Em vez de utilizar este (*ptr).kge você forçar compilador para cancelar o ponteiro e permitir acesso ao bloco de dados e segundo você adicionar um offset (designador) para escolher o membro.

Confira esta imagem que fiz:

insira a descrição da imagem aqui

Mas se você tivesse membros aninhados, essa sintaxe se tornaria ilegível e, portanto, ->foi introduzida. Eu acho que a legibilidade é a única razão justificável para usá-lo, pois ptr->kgé muito mais fácil escrever do que isso (*ptr).kg.

Agora, vamos escrever isso de forma diferente, para que você veja a conexão mais claramente. (*ptr).kg(*&audi).kgaudi.kg. Aqui, usei pela primeira vez o fato de que ptré um "endereço de audi", isto é, &audie o fato de que os operadores de "referência"& e "desreferência" se * cancelam.

71GA
fonte
1

Eu tive que fazer uma pequena alteração no programa de Jack para executá-lo. Após declarar o ponteiro de estrutura pvar, aponte-o para o endereço de var. Encontrei esta solução na página 242 de Programação de Stephen Kochan em 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;
}

Execute isso no vim com o seguinte comando:

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

Saída:

5 - 14.30
6 - 22.40
Rich Vogt
fonte
3
dica do vim: use %para representar o nome do arquivo atual. Assim:!gcc % && ./a.out
jibberia
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;
}
Gopal Rao
fonte
1

O ->operador torna o código mais legível do que o *operador em algumas situações.

Tais como: (citado no projeto 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;

};

A _EFI_BLOCK_IO_PROTOCOLestrutura contém 4 membros de ponteiro de função.

Suponha que você tenha uma variável struct _EFI_BLOCK_IO_PROTOCOL * pStructe deseje usar o bom e velho *operador para chamá-lo de ponteiro de função de membro. Você terminará com um código como este:

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

Mas com o ->operador, você pode escrever assim:

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

Qual parece melhor?

smwikipedia
fonte
1
Acho que o código seria mais legível se não estivesse em letras maiúsculas, como foi digitado por adolescentes no bate-papo da AOL dos anos 90.
thang
1
#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);
}

saída é 5 5 5

prashanth
fonte
0

Dot é um operador de desreferência e usado para conectar a variável de estrutura a um registro específico da estrutura. Por exemplo :

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

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

Dessa forma, podemos usar um operador de ponto para acessar a variável de estrutura

divya
fonte
6
Que valor isso acrescenta? O exemplo é um pouco ruim em comparação com as outras respostas que realmente o comparam ->. Além disso, esta pergunta já foi respondida por 4,5 anos.
EWit