Pointer di C: kapan harus menggunakan ampersand dan tanda bintang?

298

Saya baru memulai dengan pointer, dan saya sedikit bingung. Saya tahu &berarti alamat suatu variabel dan yang *dapat digunakan di depan variabel pointer untuk mendapatkan nilai objek yang ditunjuk oleh pointer. Tetapi berbagai hal bekerja secara berbeda ketika Anda bekerja dengan array, string atau ketika Anda memanggil fungsi dengan salinan pointer variabel. Sulit untuk melihat pola logika di dalam semua ini.

Kapan saya harus menggunakan &dan *?

Pieter
sumber
5
Tolong ilustrasikan bagaimana Anda melihat sesuatu kadang-kadang bekerja secara berbeda. Kalau tidak, kami harus menebak apa yang membingungkan Anda.
Drew Dormann
1
Setuju dengan Neil Butterworth. Mungkin akan mendapatkan lebih banyak info untuk mendapatkannya langsung dari sebuah buku, dan penjelasan K&R cukup jelas.
Tom
145
Saya tidak setuju dengan Anda semua yang mengatakan bahwa itu bukan ide yang baik untuk menanyakan jenis pertanyaan ini pada SO. SO telah menjadi sumber daya nomor 1 saat mencari di Google. Anda tidak memberikan kredit yang cukup untuk jawaban ini. Baca tanggapan Dan Olson. Jawaban ini benar-benar mendalam dan sangat bermanfaat bagi pemula. RTFMtidak membantu, dan terus terang sangat kasar. Jika Anda tidak punya waktu untuk menjawab, maka hormatilah mereka yang meluangkan waktu untuk menjawab pertanyaan-pertanyaan ini. Saya berharap saya bisa @ ini ke "anon" tapi jelas dia tidak punya waktu untuk berkontribusi dengan cara yang berarti.
SSH
18
Apa yang dikatakan SSH Ini benar sekali. Beberapa orang berteriak "Hanya Google itu", tetapi saat ini sebaliknya: "Lihat saja StackOverflow." Pertanyaan ini bermanfaat bagi banyak orang. (Karena itu upvotes dan tidak ada downvotes.)
MC Emperor

Jawaban:

610

Anda memiliki petunjuk dan nilai:

int* p; // variable p is pointer to integer type
int i; // integer value

Anda mengubah pointer menjadi nilai dengan *:

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to

Anda mengubah nilai menjadi pointer dengan &:

int* p2 = &i; // pointer p2 will point to the address of integer i

Sunting: Dalam hal array, mereka diperlakukan seperti pointer. Jika Anda menganggapnya sebagai pointer, Anda akan menggunakan *untuk mendapatkan nilai-nilai di dalamnya seperti yang dijelaskan di atas, tetapi ada juga cara lain yang lebih umum menggunakan []operator:

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

Untuk mendapatkan elemen kedua:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

Jadi []operator pengindeksan adalah bentuk khusus dari *operator, dan berfungsi seperti ini:

a[i] == *(a + i);  // these two statements are the same thing
Dan Olson
sumber
2
Kenapa ini tidak berhasil? int aX[] = {3, 4}; int *bX = &aX;
Pieter
5
Array adalah khusus dan dapat dikonversi ke pointer secara transparan. Ini menyoroti cara lain untuk mendapatkan dari pointer ke nilai, saya akan menambahkannya ke penjelasan di atas.
Dan Olson
4
Jika saya mengerti ini dengan benar ... contohnya int *bX = &aX;tidak berfungsi karena aXsudah mengembalikan alamat aX[0](yaitu &aX[0]), jadi &aXakan mendapatkan alamat dari alamat yang tidak masuk akal. Apakah ini benar?
Pieter
6
Anda benar, meskipun ada kasus di mana Anda sebenarnya ingin alamat itu. Dalam hal ini Anda akan mendeklarasikannya sebagai int ** bX = & aX, tapi ini adalah topik yang lebih maju.
Dan Olson
3
@ Dan, diberikan int aX[] = {3,4};, int **bX = &aX;adalah kesalahan. &aXadalah tipe "pointer to array [2] dari int", bukan "pointer to pointer to int". Secara khusus, nama array tidak diperlakukan sebagai penunjuk ke elemen pertama untuk unary& . Anda dapat melakukan:int (*bX)[2] = &aX;
Alok Singhal
28

Ada pola ketika berhadapan dengan array dan fungsi; awalnya agak sulit dilihat.

Ketika berhadapan dengan array, ada baiknya untuk mengingat hal-hal berikut: ketika ekspresi array muncul di sebagian besar konteks, jenis ekspresi secara implisit dikonversi dari "N-element array T" ke "pointer ke T", dan nilainya diatur untuk menunjuk ke elemen pertama dalam array. Pengecualian untuk aturan ini adalah ketika ekspresi array muncul sebagai operan dari &atausizeof operator, atau ketika itu adalah string literal digunakan sebagai initializer dalam deklarasi.

Jadi, ketika Anda memanggil fungsi dengan ekspresi array sebagai argumen, fungsi tersebut akan menerima pointer, bukan array:

int arr[10];
...
foo(arr);
...

void foo(int *arr) { ... }

Inilah sebabnya mengapa Anda tidak menggunakan &operator untuk argumen yang terkait dengan "% s" di scanf():

char str[STRING_LENGTH];
...
scanf("%s", str);

Karena konversi tersirat, scanf()menerima char *nilai yang menunjuk ke awal strarray. Ini berlaku untuk setiap fungsi yang disebut dengan ekspresi array sebagai argumen (hampir semua str*fungsi, *scanfdan *printffungsi, dll.).

Dalam praktiknya, Anda mungkin tidak akan pernah memanggil fungsi dengan ekspresi array menggunakan &operator, seperti pada:

int arr[N];
...
foo(&arr);

void foo(int (*p)[N]) {...}

Kode seperti itu sangat tidak umum; Anda harus mengetahui ukuran array dalam deklarasi fungsi, dan fungsi hanya bekerja dengan pointer ke array ukuran tertentu (pointer ke array 10-elemen T adalah jenis yang berbeda dari pointer ke array 11-elemen dari T).

Ketika ekspresi array muncul sebagai operan ke &operator, jenis ekspresi yang dihasilkan adalah "pointer ke array elemen-N dari T", atau T (*)[N], yang berbeda dari array pointer ( T *[N]) dan pointer ke tipe dasar (T * ).

Ketika berhadapan dengan fungsi dan pointer, aturan yang harus diingat adalah: jika Anda ingin mengubah nilai argumen dan memilikinya tercermin dalam kode panggilan, Anda harus meneruskan pointer ke hal yang ingin Anda modifikasi. Sekali lagi, array melemparkan sedikit kunci pas ke dalam karya, tetapi kita akan berurusan dengan kasus normal terlebih dahulu.

Ingatlah bahwa C lewat semua argumen fungsi dengan nilai; parameter formal menerima salinan nilai dalam parameter aktual, dan setiap perubahan pada parameter formal tidak tercermin dalam parameter aktual. Contoh umum adalah fungsi swap:

void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);

Anda akan mendapatkan output berikut:

sebelum swap: a = 1, b = 2
setelah swap: a = 1, b = 2

Parameter formal xdan yobjek berbeda dari adan b, sehingga berubah menjadi xdan ytidak tercermin dalam adan b. Karena kami ingin memodifikasi nilai adan b, kami harus meneruskan pointer ke fungsi swap:

void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);

Sekarang output Anda akan menjadi

sebelum swap: a = 1, b = 2
setelah swap: a = 2, b = 1

Perhatikan bahwa, dalam fungsi swap, kami tidak mengubah nilai xdan y, tetapi nilai apa xdan y menunjuk ke . Menulis ke *xberbeda dari menulis ke x; kami tidak memperbarui nilai xitu sendiri, kami mendapatkan lokasi dari xdan memperbarui nilai di lokasi itu.

Ini sama benarnya jika kita ingin memodifikasi nilai pointer; jika kita menulis

int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);

maka kita memodifikasi nilai parameter input stream, bukan stream menunjuk ke apa , jadi mengubah streamtidak berpengaruh pada nilai in; agar ini berfungsi, kita harus meneruskan sebuah pointer ke pointer:

int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);

Sekali lagi, array melemparkan sedikit kunci pas ke dalam karya. Saat Anda meneruskan ekspresi array ke suatu fungsi, fungsi yang diterima adalah sebuah pointer. Karena bagaimana array subscript didefinisikan, Anda bisa menggunakan operator subskrip pada pointer dengan cara yang sama Anda bisa menggunakannya pada array:

int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}

Perhatikan bahwa objek array mungkin tidak ditugaskan; yaitu, Anda tidak dapat melakukan sesuatu seperti

int a[10], b[10];
...
a = b;

jadi Anda ingin berhati-hati ketika berhadapan dengan pointer ke array; sesuatu seperti

void (int (*foo)[N])
{
  ...
  *foo = ...;
}

tidak akan bekerja

John Bode
sumber
16

Sederhananya

  • &berarti alamat-dari , Anda akan melihat bahwa di placeholder untuk fungsi untuk memodifikasi variabel parameter seperti dalam C, variabel parameter dilewatkan oleh nilai, menggunakan ampersand berarti untuk melewati referensi.
  • *berarti dereferensi variabel penunjuk, artinya mendapatkan nilai variabel penunjuk itu.
int foo(int *x){
   *x++;
}

int main(int argc, char **argv){
   int y = 5;
   foo(&y);  // Now y is incremented and in scope here
   printf("value of y = %d\n", y); // output is 6
   /* ... */
}

Contoh di atas menggambarkan cara memanggil fungsi foodengan menggunakan pass-by-reference, bandingkan dengan ini

int foo(int x){
   x++;
}

int main(int argc, char **argv){
   int y = 5;
   foo(y);  // Now y is still 5
   printf("value of y = %d\n", y); // output is 5
   /* ... */
}

Berikut ilustrasi penggunaan dereference

int main(int argc, char **argv){
   int y = 5;
   int *p = NULL;
   p = &y;
   printf("value of *p = %d\n", *p); // output is 5
}

Di atas menggambarkan bagaimana kita mendapatkan alamat- y dan menetapkannya ke variabel pointer p. Kemudian kami melakukan dereferensi p dengan melampirkan bagian *depannya untuk mendapatkan nilai p, yaitu *p.

t0mm13b
sumber
10

Ya itu bisa sangat rumit karena *digunakan untuk berbagai keperluan di C / C ++.

Jika *muncul di depan variabel / fungsi yang sudah dideklarasikan, itu berarti bahwa:

  • a) *memberikan akses ke nilai variabel itu (jika jenis variabel itu adalah tipe pointer, atau *operator yang kelebihan beban ).
  • b) *memiliki arti operator pengganda, dalam hal ini, harus ada variabel lain di sebelah kiri*

Jika *muncul dalam deklarasi variabel atau fungsi itu berarti bahwa variabel itu adalah pointer:

int int_value = 1;
int * int_ptr; //can point to another int variable
int   int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer as well which points to the first int of the array
//int   int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5};  // these two
int int_array4[5] = {1,2,3,4,5}; // are identical

void func_takes_int_ptr1(int *int_ptr){} // these two are identical
void func_takes int_ptr2(int int_ptr[]){}// and legal

Jika &muncul dalam deklarasi variabel atau fungsi, itu umumnya berarti bahwa variabel tersebut adalah referensi ke variabel jenis itu.

Jika &muncul di depan variabel yang sudah dideklarasikan, itu mengembalikan alamat variabel itu

Selain itu Anda harus tahu, bahwa ketika meneruskan array ke suatu fungsi, Anda harus selalu melewati ukuran array dari array itu juga, kecuali ketika array tersebut adalah sesuatu seperti cstring (char array) yang diakhiri 0.

smerlin
sumber
1
@akmozo s / func_takes int_ptr2 / func_takes_int_ptr2 / (ruang tidak valid)
PixnBits
4

Ketika Anda mendeklarasikan variabel pointer atau parameter fungsi, gunakan *:

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}

NB: setiap variabel yang dideklarasikan memerlukan * sendiri.

Saat Anda ingin mengambil alamat suatu nilai, gunakan &. Saat Anda ingin membaca atau menulis nilai dalam sebuah pointer, gunakan *.

int a;
int *b;
b = f(&a);
a = *b;

a = *f(&a);

Array biasanya hanya diperlakukan seperti pointer. Ketika Anda mendeklarasikan parameter array dalam suatu fungsi, Anda dapat dengan mudah mendeklarasikannya sebagai pointer (artinya sama). Ketika Anda melewatkan sebuah array ke suatu fungsi, Anda sebenarnya meneruskan sebuah pointer ke elemen pertama.

Pointer fungsi adalah satu-satunya hal yang tidak cukup mengikuti aturan. Anda dapat mengambil alamat suatu fungsi tanpa menggunakan &, dan Anda dapat memanggil pointer fungsi tanpa menggunakan *.

Jay Conrod
sumber
4

Saya sedang melihat semua penjelasan bertele-tele jadi alih-alih beralih ke video dari University of New South Wales untuk penyelamatan. Berikut adalah penjelasan sederhana: jika kita memiliki sel yang memiliki alamat xdan nilai 7, cara tidak langsung untuk meminta alamat nilai 7adalah &7dan cara tidak langsung untuk meminta nilai di alamat xadalah . *xJadi (cell: x , value: 7) == (cell: &7 , value: *x). Cara lain untuk melihatnya: Johnduduk di 7th seat. *7th seatAkan menunjuk ke Johndan &Johnakan memberikan address/ lokasi 7th seat. Penjelasan sederhana ini membantu saya dan berharap itu akan membantu orang lain juga. Berikut tautan untuk video luar biasa: klik di sini.

Ini adalah contoh lain:

#include <stdio.h>

int main()
{ 
    int x;            /* A normal integer*/
    int *p;           /* A pointer to an integer ("*p" is an integer, so p
                       must be a pointer to an integer) */

    p = &x;           /* Read it, "assign the address of x to p" */
    scanf( "%d", &x );          /* Put a value in x, we could also use p here */
    printf( "%d\n", *p ); /* Note the use of the * to get the value */
    getchar();
}

Add-on: Selalu inisialisasi pointer sebelum menggunakannya. Jika tidak, pointer akan menunjuk ke apa pun, yang dapat mengakibatkan crash program karena sistem operasi akan mencegah Anda mengakses memori yang ia tahu tidak Anda miliki. p = &x;, kami menetapkan penunjuk lokasi tertentu.


sumber
3

Sebenarnya, Anda sudah memilikinya, tidak ada lagi yang perlu Anda ketahui :-)

Saya hanya akan menambahkan bit-bit berikut:

  • dua operasi adalah ujung yang berlawanan dari spektrum. &mengambil variabel dan memberi Anda alamat, *mengambil alamat, dan memberi Anda variabel (atau konten).
  • array "turunkan" ke pointer ketika Anda meneruskannya ke fungsi.
  • Anda sebenarnya dapat memiliki beberapa level tanpa tipuan ( char **pberarti itu padalah pointer ke pointer ke char.

Mengenai hal-hal yang bekerja secara berbeda, tidak juga:

  • array, seperti yang telah disebutkan, menurunkan ke pointer (ke elemen pertama dalam array) ketika diteruskan ke fungsi; mereka tidak menyimpan informasi ukuran.
  • tidak ada string dalam C, hanya array karakter yang, secara konvensi, mewakili string karakter yang diakhiri oleh karakter zero ( \0).
  • Saat Anda meneruskan alamat suatu variabel ke suatu fungsi, Anda bisa menghilangkan referensi pointer untuk mengubah variabel itu sendiri (biasanya variabel dilewatkan oleh nilai (kecuali untuk array)).
paxdiablo
sumber
3

Saya pikir Anda agak bingung. Anda harus membaca tutorial / buku petunjuk yang bagus.

Ini tutorial sangat baik untuk pemula (jelas menjelaskan apa &dan *yang). Dan ya jangan lupa membaca buku Pointers in C karya Kenneth Reek.

Perbedaan antara &dan *sangat jelas.

Contoh:

#include <stdio.h>

int main(){
  int x, *p;

  p = &x;         /* initialise pointer(take the address of x) */
  *p = 0;         /* set x to zero */
  printf("x is %d\n", x);
  printf("*p is %d\n", *p);

  *p += 1;        /* increment what p points to i.e x */
  printf("x is %d\n", x);

  (*p)++;         /* increment what p points to i.e x */
  printf("x is %d\n", x);

  return 0;
}
Prasoon Saurav
sumber
1

Oke, sepertinya posting Anda sudah diedit ...

double foo[4];
double *bar_1 = &foo[0];

Lihat bagaimana Anda bisa menggunakan &untuk mendapatkan alamat awal struktur array? Pengikut

Foo_1(double *bar, int size){ return bar[size-1]; }
Foo_2(double bar[], int size){ return bar[size-1]; }

akan melakukan hal yang sama.

wheaties
sumber
Pertanyaannya telah ditandai C bukan C ++.
Prasoon Saurav
1
Dan saya telah menghapus cout yang menyinggung <<
wheaties