Arrow operator (->) penggunaan dalam C


257

Saya membaca buku berjudul "Teach Yourself C in 21 Days" (Saya sudah belajar Java dan C # jadi saya bergerak dengan kecepatan yang jauh lebih cepat). Saya membaca bab tentang petunjuk dan operator-> (panah) muncul tanpa penjelasan. Saya pikir itu digunakan untuk memanggil anggota dan fungsi (seperti yang setara dengan operator (titik), tetapi untuk pointer bukan anggota). Tetapi saya tidak sepenuhnya yakin..

Bisakah saya mendapatkan penjelasan dan contoh kode?


90
Dapatkan buku yang lebih baik. norvig.com/21-days.html
joshperry

9
qrdl benar - buku "Learn X in Y days" pada umumnya sampah. Selain K&R, saya juga merekomendasikan "C Primer Plus" Prata, yang lebih mendalam dari K&R.
J. Taylor

3
@Steve Pertanyaan itu berkaitan dengan C ++. Menyebutnya menyebabkan kebingungan bagi saya ketika saya mulai membaca tentang kelebihan operator pada jawaban lain itu, yang tidak relevan dalam C.
Johann

1
@Belton Seri yang sulit itu buruk, pria itu mengatakan hal-hal yang bahkan tidak relevan ketika dia menulis buku dan dia tidak peduli dengan praktik yang baik.
Bálint

1
Tautan Peter Norvig ke "Teach Yourself Programming in Ten Years" sangat bagus, salah satu favorit saya. Inilah versi komik yang menjelaskan bagaimana melakukan ini dalam 21 hari, yang sayangnya saya ingat sebagai XKCD tetapi saya salah: abstrusegoose.com/249
Ted

Jawaban:


462

foo->barsetara dengan (*foo).bar, yaitu mendapat anggota dipanggil bardari struct yang foomenunjuk ke.


51
Perlu dicatat bahwa jika operator dereference telah dibuat postfix, seperti pada Pascal, ->operator tidak akan diperlukan sama sekali, karena itu akan setara dengan jauh lebih terbaca foo*.bar. Seluruh kekacauan fungsi mengetik dengan semua tanda kurung tambahan akan dihindari juga.
Marquis of Lorne

1
Jadi akan foo*.bardan (*foo).barkeduanya setara dengan foo->bar? Bagaimana dengan Foo myFoo = *foo; myFoo.bar?
Aaron Franke

9
Tidak, dia hanya mengatakan JIKA pencipta C akan menjadikan operator dereference sebagai operator POSTfix alih-alih PREfix maka itu akan lebih mudah. Tapi itu ADALAH operator awalan di C.
reichhart

@ user207421 Bisakah Anda memberikan uraian singkat atau tautan ke "fungsi mengetik dengan semua tanda kurung tambahan" yang Anda sebutkan? Terima kasih.
RoG

1
@ user207421 nah, itu akan menyebabkan lebih banyak orang tua .. sejauh ini, ada prioritas () dan [] ke kanan di atas * ke kiri. jika mereka semua di satu sisi, Anda akan menempatkan lebih banyak orang tua. Sama dalam ekspresi, karena konflik dengan operator perkalian. Pascal ^ bisa menjadi pilihan tetapi dicadangkan untuk operasi bit, masih lebih banyak orang tua.
Swift - Friday Pie

130

Ya itu saja.

Itu hanya versi titik ketika Anda ingin mengakses elemen struct / kelas yang merupakan pointer bukan referensi.

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;

Itu dia!


3
Karena pvar tidak diinisialisasi, bagaimana Anda menginisialisasi jika Anda ingin pvar menunjuk ke struct baru, itu tidak pvar = &var?
CMCDragonkai

Pertanyaannya secara khusus tentang C, yang tidak memiliki kelas atau variabel referensi.
Oak

1
hmm seharusnya Anda tidak melakukan malloc sebelum menulis ke pvar struct foo * pvar; ?? pvar-> y menulis ke ruang yang tidak terisi!
Zibri

inisialisasi pvar: Inisialisasi semua anggota secara manual ke beberapa default yang Anda ingin miliki atau gunakan sesuatu seperti calloc () jika nol isi akan baik-baik saja untuk Anda.
reichhart

2
bukankah seharusnya: pvar = malloc (sizeof (struct foo)) atau malloc (sizeof (* pvar)) ??
Yuri Aps

33

a->bhanya kependekan dari (*a).bsegala hal (sama untuk fungsi: a->b()kependekan (*a).b()).


1
apakah ada dokumentasi yang mengatakan itu juga berfungsi seperti itu untuk metode?
AsheKetchum

28

Saya hanya menambahkan jawaban "mengapa?".

.adalah operator akses anggota standar yang memiliki prioritas lebih tinggi daripada *operator pointer.

Ketika Anda mencoba mengakses internal struct dan Anda menulisnya *foo.barmaka kompiler akan berpikir untuk menginginkan elemen 'bar' dari 'foo' (yang merupakan alamat dalam memori) dan jelas bahwa alamat belaka tidak memiliki anggota.

Jadi Anda perlu meminta kompiler untuk dereferensi pertama dengan (*foo)dan kemudian mengakses elemen anggota:, (*foo).baryang agak canggung untuk menulis sehingga orang-orang baik telah datang dengan versi singkat: foo->baryang merupakan semacam akses anggota oleh operator pointer.



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

Berikut untuk mengakses nilai-nilai idan jkita dapat menggunakan variabel adan pointer psebagai berikut: a.i, (*p).idan p->isemua sama.

Inilah ."Pemilih Langsung" dan ->"Pemilih Tidak Langsung".


2

Yah saya harus menambahkan sesuatu juga. Struktur sedikit berbeda dari array karena array adalah pointer dan struktur tidak. Jadi hati-hati!

Katakanlah saya menulis kode yang tidak berguna ini:

#include <stdio.h>

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

int main(void){

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

}

Di sini pointer ptrmenunjuk ke alamat ( ! ) Dari variabel struktur auditetapi di samping struktur alamat juga memiliki sejumlah data ( ! )! Anggota pertama dari chunk data memiliki alamat yang sama dari struktur itu sendiri dan Anda bisa mendapatkan datanya dengan hanya mereferensikan pointer seperti ini *ptr (tanpa kawat gigi) .

Tapi Jika Anda ingin acess setiap anggota lain dari yang pertama, Anda harus menambahkan penanda seperti .km, .kph, .kgyang tidak lebih dari offset ke alamat dasar potongan data ...

Tetapi karena prioritas Anda tidak dapat menulis *ptr.kgkarena operator akses .dievaluasi sebelum operator dereferensi *dan Anda akan mendapatkan *(ptr.kg)yang tidak mungkin karena pointer tidak memiliki anggota! Dan kompiler mengetahui hal ini dan karena itu akan mengeluarkan kesalahan misalnya:

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

Alih-alih Anda menggunakan ini (*ptr).kgdan Anda memaksa compiler ke 1 dereference pointer dan mengaktifkan acess ke potongan data dan 2 Anda menambahkan offset (penunjuk) untuk memilih anggota.

Periksa gambar yang saya buat ini:

masukkan deskripsi gambar di sini

Tetapi jika Anda memiliki anggota bersarang, sintaks ini akan menjadi tidak dapat dibaca dan oleh karena ->itu diperkenalkan. Saya pikir keterbacaan adalah satu-satunya alasan dibenarkan untuk menggunakannya karena ini ptr->kgjauh lebih mudah untuk ditulis daripada (*ptr).kg.

Sekarang mari kita tuliskan ini secara berbeda sehingga Anda melihat hubungannya lebih jelas. (*ptr).kg(*&audi).kgaudi.kg. Di sini saya pertama kali menggunakan fakta yang ptrmerupakan "alamat audi" yaitu &audidan fakta bahwa operator "referensi" & dan "dereferensi" * membatalkan satu sama lain.


1

Saya harus membuat perubahan kecil pada program Jack untuk menjalankannya. Setelah mendeklarasikan struct pointer pvar, arahkan ke alamat var. Saya menemukan solusi ini di halaman 242 dari Programming Stephen Kochan di 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;
}

Jalankan ini dalam vim dengan perintah berikut:

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

Akan menghasilkan:

5 - 14.30
6 - 22.40

3
vim tip: gunakan %untuk mewakili nama file saat ini. Seperti itu:!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;
}

1

The ->Operator membuat kode lebih mudah dibaca daripada *operator dalam beberapa situasi.

Seperti: (dikutip dari proyek 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;

};

The _EFI_BLOCK_IO_PROTOCOLstruct berisi 4 anggota fungsi pointer.

Misalkan Anda memiliki variabel struct _EFI_BLOCK_IO_PROTOCOL * pStruct, dan Anda ingin menggunakan *operator lama yang baik untuk memanggil itu pointer fungsi anggota. Anda akan berakhir dengan kode seperti ini:

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

Tetapi dengan ->operator, Anda dapat menulis seperti ini:

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

Mana yang terlihat lebih baik?


1
Saya pikir kode akan lebih mudah dibaca jika tidak dalam huruf besar semua seperti yang diketik oleh remaja pada obrolan AOL dari tahun 90-an.
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);
}

output adalah 5 5 5


0

Dot adalah operator dereference dan digunakan untuk menghubungkan variabel struktur untuk catatan struktur tertentu. Misalnya:

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

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

Sedemikian rupa kita dapat menggunakan operator titik untuk mengakses variabel struktur


6
Nilai apa yang ditambahkan ini? Contohnya agak buruk dibandingkan dengan jawaban lain yang benar-benar membandingkannya ->. Pertanyaan ini sudah dijawab selama 4,5 tahun.
EWit
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.