Uso del operador de flecha (->) en C

257

Estoy leyendo un libro llamado "Teach Yourself C in 21 Days" (ya aprendí Java y C #, así que me estoy moviendo a un ritmo mucho más rápido). Estaba leyendo el capítulo sobre punteros y el operador-> (flecha) apareció sin explicación. Creo que se usa para llamar a miembros y funciones (como el equivalente del operador (punto), pero para punteros en lugar de miembros). Pero no estoy completamente seguro..

¿Podría obtener una explicación y un ejemplo de código?

Mohit Deshpande
fuente
90
Consigue un mejor libro. norvig.com/21-days.html
joshperry
9
qrdl es correcto: los libros "Aprender X en Y días" son generalmente basura. Además de K&R, también recomendaría "C Primer Plus" de Prata, que profundiza más que K&R.
J. Taylor
3
@Steve Esa pregunta trata sobre C ++. Llamarlo me causó cierta confusión cuando comencé a leer sobre la sobrecarga del operador en esa otra respuesta, que no es relevante en C.
Johann
1
@Belton Las series difíciles son malas, el tipo dice cosas que ni siquiera eran relevantes cuando escribió el libro y no le importan las buenas prácticas.
Bálint
1
El enlace de Peter Norvig a "Teach Yourself Programming in Ten Years" es genial, uno de mis favoritos. Aquí está la versión cómica que explica cómo hacer esto en 21 días, que lamentablemente recordé como un XKCD pero me equivoqué: abstrusegoose.com/249
Ted

Respuestas:

462

foo->bares equivalente a (*foo).bar, es decir, obtiene el miembro llamado bardesde la estructura que fooapunta.

sepp2k
fuente
51
Vale la pena señalar que si el operador de desreferenciación se hubiera hecho postfix, como en Pascal, el ->operador no habría sido necesario en absoluto, ya que habría sido equivalente al mucho más legible foo*.bar. También se habría evitado todo el desorden de las funciones de definición de texto con todos los paréntesis adicionales.
Marqués de Lorne
1
Entonces, ¿ foo*.bary (*foo).barambos serían equivalentes a foo->bar? ¿Qué hay de Foo myFoo = *foo; myFoo.bar?
Aaron Franke el
9
No, es sólo decir SI a los creadores de C habrían hecho el operador eliminar la referencia como operador de sufijo en lugar de prefijo entonces habría sido más fácil. Pero ES un operador de prefijo en C.
reichhart
@ user207421 Por favor, ¿puede dar una breve descripción o un enlace a las "funciones de tipografía con todos los paréntesis adicionales" que menciona? Gracias.
RoG
1
@ user207421 nah, causaría más padres ... hasta ahora, hay prioridad de () y [] a la derecha arriba * a la izquierda. si todos están de un lado, habrás puesto más padres. Lo mismo en expresiones, debido al conflicto con el operador de multiplicación. Pascal ^ podría ser una opción, pero estaba reservado para la operación de bits, aún más padres.
Swift - Friday Pie
130

Si eso es.

Es solo la versión de punto cuando desea acceder a elementos de una estructura / clase que es un puntero en lugar de una referencia.

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;

¡Eso es!

Jack
fuente
3
Dado que pvar no está inicializado, ¿cómo lo inicializaría si quisiera que pvar apuntara a una nueva estructura, que no es así pvar = &var?
CMCDragonkai
La pregunta era específicamente sobre C, que no tiene clases o variables de referencia.
Roble
1
hmm no deberías hacer un malloc antes de escribir en pvar struct foo * pvar; ?? ¡pvar-> y escribe en el espacio no asignado!
Zibri
inicialización de pvar: Inicialice todos los miembros manualmente a algunos valores predeterminados que desee tener o use algo como calloc () si el relleno cero sería adecuado para usted.
Reichhart
2
¿No debería ser: pvar = malloc (sizeof (struct foo)) o malloc (sizeof (* pvar)) ??
Yuri Aps
33

a->bes corto para (*a).btodos los sentidos (lo mismo para funciones: a->b()es corto para (*a).b())

Peter Alexander
fuente
1
¿Existe documentación que diga que también funciona de esa manera para los métodos?
AsheKetchum
28

Simplemente agregaría a las respuestas el "¿por qué?".

.es un operador de acceso de miembro estándar que tiene mayor prioridad que el *operador de puntero.

Cuando intentas acceder a las partes internas de una estructura y la escribiste como *foo.barentonces, el compilador pensaría querer un elemento 'bar' de 'foo' (que es una dirección en memoria) y obviamente esa mera dirección no tiene ningún miembro.

Por lo tanto, debe pedirle al compilador que primero desreferencia con (*foo)y luego acceda al elemento miembro: (*foo).barque es un poco torpe para escribir, por lo que la gente buena ha creado una versión abreviada: foo->barque es una especie de acceso de miembro por operador de puntero.

Lukasz Matysiak
fuente
19

foo->bares solo una abreviatura de (*foo).bar. Eso es todo al respecto.

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

Aquí el acceso a los valores de iy jque se puede utilizar la variable ay el puntero pde la siguiente manera: a.i, (*p).iy p->ison todos iguales.

Aquí .hay un "Selector directo" y ->es un "Selector indirecto".

Jayghosh Wankar
fuente
2

Bueno, tengo que agregar algo también. La estructura es un poco diferente de la matriz porque la matriz es un puntero y la estructura no lo es. ¡Así que ten cuidado!

Digamos que escribo este 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;

}

Aquí puntero ptr apunta a la dirección ( ! ) De la variable de estructura, audipero al lado de la estructura de la dirección también hay una porción de datos ( ! )! El primer miembro de la porción de datos tiene la misma dirección que la estructura misma y puede obtener sus datos solo haciendo referencia a un puntero como este *ptr (sin llaves) .

Pero Si desea acess cualquier otro miembro que el primero, tiene que añada un indicador como .km, .kph, .kgque no es más que compensa a la dirección base del son parte de los datos ...

Pero debido a la precedencia, no puede escribir *ptr.kgya que el operador de acceso .se evalúa antes que el operador de desreferencia *y obtendría lo *(ptr.kg)que no es posible ya que el puntero no tiene miembros. Y el compilador lo sabe y, por lo tanto, emitirá un error, por ejemplo:

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

En su lugar, usa esto (*ptr).kgy fuerza al compilador a primero desreferenciar el puntero y habilitar el acceso a la porción de datos y segundo agregar un desplazamiento (designador) para elegir el miembro.

Mira esta imagen que hice:

ingrese la descripción de la imagen aquí

Pero si hubiera miembros anidados, esta sintaxis se volvería ilegible y, por ->lo tanto, se introdujo. Creo que la legibilidad es la única razón justificable para usarlo, ya que ptr->kges mucho más fácil de escribir que (*ptr).kg.

Ahora permítanos escribir esto de manera diferente para que vea la conexión más claramente. (*ptr).kg(*&audi).kgaudi.kg. Aquí utilicé por primera vez el hecho de que ptres una "dirección de audi", es decir, &audiy el hecho de que "referencia" & y "desreferencia" * operadores de cancelan entre sí.

71GA
fuente
1

Tuve que hacer un pequeño cambio en el programa de Jack para que funcionara. Después de declarar el puntero de estructura pvar, apúntelo a la dirección de var. Encontré esta solución en la página 242 de la Programación de Stephen Kochan en 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;
}

Ejecute esto en vim con el siguiente comando:

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

Saldrá:

5 - 14.30
6 - 22.40
Rich Vogt
fuente
3
Consejo vim: se usa %para representar el nombre de archivo actual. Así:!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
fuente
1

El ->operador hace que el código sea más legible que el *operador en algunas situaciones.

Tales como: (citado del proyecto 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;

};

La _EFI_BLOCK_IO_PROTOCOLestructura contiene 4 miembros de puntero de función.

Supongamos que tiene una variable struct _EFI_BLOCK_IO_PROTOCOL * pStructy desea utilizar el *operador antiguo para llamar a su puntero de función miembro. Terminarás con un código como este:

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

Pero con el ->operador, puedes escribir así:

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

¿Cuál se ve mejor?

smwikipedia
fuente
1
Creo que el código sería más legible si no está en mayúsculas como lo escribieron los adolescentes en el chat de AOL de los años 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);
}

la salida es 5 5 5

prashanth
fuente
0

Dot es un operador de desreferencia y se utiliza para conectar la variable de estructura para un registro particular de estructura. P.ej :

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

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

De esta manera, podemos usar un operador de puntos para acceder a la variable de estructura

divya
fuente
66
¿Qué valor agrega esto? El ejemplo es un poco pobre en comparación con las otras respuestas que realmente lo comparan ->. También esta pregunta ha sido respondida por 4.5 años ya.
EWit