Hati-hati; frasa "penunjuk ganda" juga merujuk pada jenisnya double*.
Keith Thompson
1
Catat: jawaban untuk pertanyaan ini berbeda untuk C dan C ++ - jangan tambahkan tag c + untuk pertanyaan yang sangat lama ini.
BЈовић
Jawaban:
479
Jika Anda ingin memiliki daftar karakter (kata), Anda dapat menggunakan char *word
Jika Anda ingin daftar kata-kata (kalimat), Anda dapat menggunakan char **sentence
Jika Anda ingin daftar kalimat (sebuah monolog), Anda dapat menggunakannya char ***monologue
Jika Anda ingin daftar monolog (biografi), Anda dapat menggunakannya char ****biography
Jika Anda ingin daftar biografi (bio-library), Anda dapat menggunakannya char *****biolibrary
Jika Anda ingin daftar bio-libraries (a lol), Anda dapat menggunakan char ******lol
... ...
ya, saya tahu ini mungkin bukan struktur data terbaik
Contoh penggunaan dengan lol yang sangat sangat sangat membosankan
#include<stdio.h>#include<stdlib.h>#include<string.h>int wordsinsentence(char**x){int w =0;while(*x){
w +=1;
x++;}return w;}int wordsinmono(char***x){int w =0;while(*x){
w += wordsinsentence(*x);
x++;}return w;}int wordsinbio(char****x){int w =0;while(*x){
w += wordsinmono(*x);
x++;}return w;}int wordsinlib(char*****x){int w =0;while(*x){
w += wordsinbio(*x);
x++;}return w;}int wordsinlol(char******x){int w =0;while(*x){
w += wordsinlib(*x);
x++;}return w;}int main(void){char*word;char**sentence;char***monologue;char****biography;char*****biolibrary;char******lol;//fill data structure
word = malloc(4*sizeof*word);// assume it worked
strcpy(word,"foo");
sentence = malloc(4*sizeof*sentence);// assume it worked
sentence[0]= word;
sentence[1]= word;
sentence[2]= word;
sentence[3]= NULL;
monologue = malloc(4*sizeof*monologue);// assume it worked
monologue[0]= sentence;
monologue[1]= sentence;
monologue[2]= sentence;
monologue[3]= NULL;
biography = malloc(4*sizeof*biography);// assume it worked
biography[0]= monologue;
biography[1]= monologue;
biography[2]= monologue;
biography[3]= NULL;
biolibrary = malloc(4*sizeof*biolibrary);// assume it worked
biolibrary[0]= biography;
biolibrary[1]= biography;
biolibrary[2]= biography;
biolibrary[3]= NULL;
lol = malloc(4*sizeof*lol);// assume it worked
lol[0]= biolibrary;
lol[1]= biolibrary;
lol[2]= biolibrary;
lol[3]= NULL;
printf("total words in my lol: %d\n", wordsinlol(lol));
free(lol);
free(biolibrary);
free(biography);
free(monologue);
free(sentence);
free(word);}
Hanya ingin menunjukkan bahwa a arr[a][b][c]bukan a ***arr. Pointer pointer menggunakan referensi referensi, sementara arr[a][b][c]disimpan sebagai array biasa dalam urutan utama baris.
MCCCS
170
Salah satu alasannya adalah Anda ingin mengubah nilai pointer yang diteruskan ke fungsi sebagai argumen fungsi, untuk melakukan ini Anda memerlukan pointer ke pointer.
Dengan kata sederhana, Gunakan **ketika Anda ingin menyimpan (ATAU mempertahankan perubahan) Alokasi Memori atau Penugasan bahkan di luar panggilan fungsi. (Jadi, Lewati fungsi tersebut dengan arg pointer ganda.)
Ini mungkin bukan contoh yang sangat baik, tetapi akan menunjukkan kepada Anda penggunaan dasar:
apa yang akan berbeda jika mengalokasikan itu void allocate(int *p)dan Anda menyebutnya sebagai allocate(p)?
ア レ ッ ク ス
@AlexanderSupertramp Ya. Kode tersebut akan segfault. Silakan lihat Jawaban Silviu.
Abhishek
@ Asha apa perbedaan antara mengalokasikan (p) dan mengalokasikan (& p)?
user2979872
1
@ Asha - Bisakah kita mengembalikan pointer? Jika kita harus menjaganya agar tetap kosong, apa gunanya skenario ini?
Shabirmean
91
Katakanlah Anda memiliki pointer. Nilainya adalah alamat.
tetapi sekarang Anda ingin mengubah alamat itu.
Anda bisa. dengan melakukan pointer1 = pointer2, Anda memberi pointer1 alamat pointer2.
tapi! jika Anda melakukan itu dalam suatu fungsi, dan Anda ingin hasilnya bertahan setelah fungsi selesai, Anda perlu melakukan beberapa pekerjaan tambahan. Anda memerlukan pointer3 baru hanya untuk menunjuk ke pointer1. lewat pointer3 ke fungsi.
ini sebuah contoh. lihat output di bawah ini terlebih dahulu, untuk mengerti.
#include<stdio.h>int main(){int c =1;int d =2;int e =3;int* a =&c;int* b =&d;int* f =&e;int** pp =&a;// pointer to pointer 'a'
printf("\n a's value: %x \n", a);
printf("\n b's value: %x \n", b);
printf("\n f's value: %x \n", f);
printf("\n can we change a?, lets see \n");
printf("\n a = b \n");
a = b;
printf("\n a's value is now: %x, same as 'b'... it seems we can, but can we do it in a function? lets see... \n", a);
printf("\n cant_change(a, f); \n");
cant_change(a, f);
printf("\n a's value is now: %x, Doh! same as 'b'... that function tricked us. \n", a);
printf("\n NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a' \n");
printf("\n change(pp, f); \n");
change(pp, f);
printf("\n a's value is now: %x, YEAH! same as 'f'... that function ROCKS!!!. \n", a);return0;}void cant_change(int* x,int* z){
x = z;
printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n", x);}void change(int** x,int* z){*x = z;
printf("\n ----> value of 'a' is: %x inside function, same as 'f', BUT will it be the same outside of this function? lets see\n",*x);}
Inilah hasilnya: ( baca ini dulu )
a's value: bf94c204
b's value: bf94c208
f's value: bf94c20c
can we change a?, lets see
a = b
a's value is now: bf94c208, same as 'b'... it seems we can, but can we do it in a function? lets see...
cant_change(a, f);----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
a's value is now: bf94c208, Doh! same as 'b'... that function tricked us.
NOW! lets see if a pointer to a pointer solution can help us... remember that 'pp' point to 'a'
change(pp, f);----> value of 'a' is: bf94c20c inside function, same as 'f', BUT will it be the same outside of this function? lets see
a's value is now: bf94c20c, YEAH! same as 'f'... that function ROCKS!!!.
Ini adalah jawaban yang bagus dan benar-benar membantu saya memvisualisasikan tujuan dan kegunaan pointer ganda.
Justin
1
@ Justin apakah Anda memeriksa jawaban saya di atas yang ini? lebih bersih :)
Brian Joseph Spinos
10
Jawaban yang bagus, hanya kurang menjelaskan bahwa <code> void cant_change (int * x, int * z) </code> gagal karena parameternya hanyalah pointer lingkup lokal baru yang diinisialisasi juga pointer a dan f (jadi mereka tidak sama dengan a dan f).
Pedro Reis
1
Sederhana? Betulkah? ;)
alk
1
jawaban ini benar-benar menjelaskan salah satu penggunaan pointer ke pointer yang paling umum, terima kasih!
tonyjosi
49
Menambah respons Asha , jika Anda menggunakan satu penunjuk ke contoh di bawah (mis. Alokasi1 ()) Anda akan kehilangan referensi ke memori yang dialokasikan di dalam fungsi.
Alasan itu terjadi seperti ini adalah bahwa di alloc1 pointer dilewatkan oleh nilai. Jadi, ketika dipindahkan ke hasil mallocpanggilan di dalam alloc1, perubahan tidak berkaitan dengan kode dalam cakupan yang berbeda.
Sekarang Anda ingin mengimplementasikan remove_iffungsi, yang menerima kriteria penghapusan rmsebagai salah satu argumen dan melintasi daftar yang ditautkan: jika sebuah entri memenuhi kriteria (seperti rm(entry)==true), simpulnya akan dihapus dari daftar. Pada akhirnya, remove_ifmengembalikan kepala (yang mungkin berbeda dari kepala asli) dari daftar tertaut.
Anda bisa menulis
for(node * prev = NULL,* curr = head; curr != NULL;){
node *const next = curr->next;if(rm(curr)){if(prev)// the node to be removed is not the head
prev->next = next;else// remove the head
head = next;
free(curr);}else
prev = curr;
curr = next;}
sebagai forloop Anda . Pesannya adalah, tanpa pointer ganda, Anda harus mempertahankan prevvariabel untuk mengatur ulang pointer , dan menangani dua kasus yang berbeda.
Tetapi dengan double pointer, Anda sebenarnya bisa menulis
// now head is a double pointerfor(node** curr = head;*curr;){
node * entry =*curr;if(rm(entry)){*curr = entry->next;
free(entry);}else
curr =&entry->next;}
Anda tidak memerlukan yang prevsekarang karena Anda dapat langsung memodifikasi apa yang prev->nextditunjuk .
Agar lebih jelas, ikuti sedikit kode. Selama penghapusan:
if entry == *head: itu akan *head (==*curr) = *head->next- headsekarang menunjuk ke pointer dari node heading baru. Anda melakukan ini dengan mengubah secara langsunghead konten ke pointer baru.
jika entry != *head: sama, *curradalah apa yang prev->nextmenunjuk, dan sekarang menunjuk ke entry->next.
Dalam kasus apa pun, Anda dapat mengatur ulang pointer dengan cara yang sama dengan pointer ganda.
1. char * ch - (disebut penunjuk karakter)
- ch berisi alamat karakter tunggal.
- (* ch) akan merujuk pada nilai karakter ..
2. char ** ch -
'ch' berisi alamat dari Array of character pointer. (seperti pada 1)
'* ch' berisi alamat karakter tunggal. (Perhatikan bahwa ini berbeda dari 1, karena perbedaan dalam deklarasi).
(** ch) akan merujuk pada nilai karakter yang tepat ..
Menambahkan lebih banyak pointer memperluas dimensi tipe data, dari karakter ke string, ke array string, dan seterusnya ... Anda dapat menghubungkannya dengan 1d, 2d, 3d matrix ..
Jadi, penggunaan pointer tergantung pada bagaimana Anda mendeklarasikannya.
Ini kode sederhana ..
int main(){char**p;
p =(char**)malloc(100);
p[0]=(char*)"Apple";// or write *p, points to location of 'A'
p[1]=(char*)"Banana";// or write *(p+1), points to location of 'B'
cout <<*p << endl;//Prints the first pointer location until it finds '\0'
cout <<**p << endl;//Prints the exact character which is being pointed*p++;//Increments for the next string
cout <<*p;}
2. Aplikasi Dua Pointer Lain -
(ini juga mencakup referensi lewat referensi)
Misalkan Anda ingin memperbarui karakter dari suatu fungsi. Jika Anda mencoba yang berikut: -
Sekarang perluas persyaratan ini untuk memperbarui string alih-alih karakter.
Untuk ini, Anda perlu menerima parameter dalam fungsi sebagai penunjuk ganda.
void func(char**str){
strcpy(str,"Second");}int main(){char**str;// printf("%d\n", sizeof(char));*str =(char**)malloc(sizeof(char)*10);//Can hold 10 character pointersint i =0;for(i=0;i<10;i++){
str =(char*)malloc(sizeof(char)*1);//Each pointer can point to a memory of 1 character.}
strcpy(str,"First");
printf("%s\n", str);
func(str);
printf("%s\n", str);}
Dalam contoh ini, metode mengharapkan penunjuk ganda sebagai parameter untuk memperbarui nilai string.
#include <stdio.h> int main() { char *ptr = 0; ptr = malloc(255); // allocate some memory strcpy( ptr, "Stack Overflow Rocks..!!"); printf("%s\n", ptr); printf("%d\n",strlen(ptr)); free(ptr); return 0; } Tetapi Anda dapat melakukannya tanpa menggunakan pointer ganda juga.
kumar
" char ** ch - 'ch' berisi alamat Array of pointer karakter. " Tidak, itu berisi alamat elemen pertama dari array charpointer. Pointer ke array char*akan diketik misalnya seperti ini: char(*(*p)[42])mendefinisikan psebagai pointer ke array dari 42 pointer ke char.
alk
Cuplikan terakhir benar-benar rusak. Sebagai permulaan: Berikut *str = ...strini adalah dereferenced uninitialised meminta perilaku tidak terdefinisi.
alk
Ini malloc(sizeof(char) * 10);tidak mengalokasikan ruang untuk 10 pointer ke chartetapi charhanya untuk 10 ..
alk
Loop ini tidak for(i=0;i<10;i++) { str = ... dapat menggunakan indeks i.
alk
17
Pointer ke pointer juga berguna sebagai "pegangan" ke memori di mana Anda ingin menyampaikan "pegangan" antara fungsi ke memori yang dapat dilokasikan kembali. Itu pada dasarnya berarti bahwa fungsi tersebut dapat mengubah memori yang sedang diarahkan oleh pointer di dalam variabel pegangan, dan setiap fungsi atau objek yang menggunakan pegangan akan menunjuk ke memori yang baru dipindahkan (atau dialokasikan). Perpustakaan suka melakukan ini dengan tipe data "buram", yaitu tipe data yang Anda tidak perlu khawatir tentang apa yang mereka lakukan dengan memori yang ditunjuk lakukan, Anda cukup membagikan "pegangan" antara fungsi perpustakaan untuk melakukan beberapa operasi pada memori itu ...
Misalnya:
#include<stdlib.h>typedefunsignedchar** handle_type;//some data_structure that the library functions would work withtypedefstruct{int data_a;int data_b;int data_c;} LIB_OBJECT;
handle_type lib_create_handle(){//initialize the handle with some memory that points to and array of 10 LIB_OBJECTs
handle_type handle = malloc(sizeof(handle_type));*handle = malloc(sizeof(LIB_OBJECT)*10);return handle;}void lib_func_a(handle_type handle){/*does something with array of LIB_OBJECTs*/}void lib_func_b(handle_type handle){//does something that takes input LIB_OBJECTs and makes more of them, so has to//reallocate memory for the new objects that will be created//first re-allocate the memory somewhere else with more slots, but don't destroy the//currently allocated slots*handle = realloc(*handle,sizeof(LIB_OBJECT)*20);//...do some operation on the new memory and return}void lib_func_c(handle_type handle){/*does something else to array of LIB_OBJECTs*/}void lib_free_handle(handle_type handle){
free(*handle);
free(handle);}int main(){//create a "handle" to some memory that the library functions can use
handle_type my_handle = lib_create_handle();//do something with that memory
lib_func_a(my_handle);//do something else with the handle that will make it point somewhere else//but that's invisible to us from the standpoint of the calling the function and//working with the handle
lib_func_b(my_handle);//do something with new memory chunk, but you don't have to think about the fact//that the memory has moved under the hood ... it's still pointed to by the "handle"
lib_func_c(my_handle);//deallocate the handle
lib_free_handle(my_handle);return0;}
Apa alasan tipe pegangan menjadi unsigned char **? Apakah batal ** berfungsi sama baiknya?
Connor Clark
5
unsigned charsecara khusus digunakan karena kami menyimpan pointer ke data biner yang akan direpresentasikan sebagai byte mentah. Menggunakan voidakan membutuhkan gips di beberapa titik, dan umumnya tidak dapat dibaca dengan maksud apa yang sedang dilakukan.
Jason
7
Contoh sederhana yang mungkin pernah Anda lihat sebelumnya
int main(int argc,char**argv)
Pada parameter kedua Anda memilikinya: pointer ke pointer ke char.
Perhatikan bahwa notasi pointer ( char* c) dan notasi array ( char c[]) dapat dipertukarkan dalam argumen fungsi. Jadi Anda juga bisa menulis char *argv[]. Dengan kata lain char *argv[]dan char **argvdapat dipertukarkan.
Apa yang diwakili di atas sebenarnya adalah array dari urutan karakter (argumen baris perintah yang diberikan kepada suatu program saat startup).
Lihat juga jawaban ini untuk detail lebih lanjut tentang tanda tangan fungsi di atas.
"notasi pointer ( char* c) dan notasi array ( char c[]) dapat dipertukarkan" (dan memiliki arti yang persis sama) dalam argumen fungsi . Mereka berbeda namun di luar argumen fungsi.
sore
6
String adalah contoh yang bagus dari penggunaan pointer ganda. String itu sendiri adalah sebuah pointer, jadi setiap kali Anda perlu menunjuk ke sebuah string, Anda akan membutuhkan pointer ganda.
Poin bagusnya, kehilangan sinyal antara otak dan keyboard. Saya telah mengeditnya agar lebih masuk akal.
Jeff Foster
Mengapa kita tidak dapat melakukan hal berikut ... void safeFree (void * memory) {if (memory) {free (memory); memory = NULL; }}
Peter_pk
@ Peter_pk Menetapkan memori ke nol tidak akan membantu karena Anda telah melewati sebuah pointer dengan nilai, bukan dengan referensi (maka contoh dari sebuah pointer ke sebuah pointer).
Jeff Foster
2
Misalnya jika Anda ingin akses acak ke data yang tidak bersebelahan.
p ->[p0, p1, p2,...]
p0 -> data1
p1 -> data2
- dalam C
T ** p =(T **) malloc(sizeof(T*)* n);
p[0]=(T*) malloc(sizeof(T));
p[1]=(T*) malloc(sizeof(T));
Anda menyimpan sebuah pointer p yang menunjuk ke array pointer. Setiap pointer menunjuk ke sepotong data.
Jika sizeof(T)besar, tidak mungkin untuk mengalokasikan blok yang berdekatan (yaitu menggunakan malloc) sizeof(T) * nbyte.
Satu hal yang saya gunakan untuk terus-menerus adalah ketika saya memiliki array objek dan saya perlu melakukan pencarian (pencarian biner) pada mereka dengan bidang yang berbeda.
Saya menyimpan array asli ...
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
Kemudian buatlah array pointer yang diurutkan ke objek.
Anda dapat membuat sebanyak banyak array pointer yang diurutkan yang Anda butuhkan, kemudian gunakan pencarian biner pada array pointer yang diurutkan untuk mengakses objek yang Anda butuhkan oleh data yang Anda miliki. Array objek asli dapat tetap tidak disortir, tetapi setiap array pointer akan diurutkan berdasarkan bidang yang ditentukan.
Tujuannya adalah untuk mengubah apa yang ditunjuk oleh studentA, menggunakan suatu fungsi.
#include<stdio.h>#include<stdlib.h>typedefstructPerson{char* name;}Person;/**
* we need a ponter to a pointer, example: &studentA
*/void change(Person** x,Person* y){*x = y;// since x is a pointer to a pointer, we access its value: a pointer to a Person struct.}void dontChange(Person* x,Person* y){
x = y;}int main(){Person* studentA =(Person*)malloc(sizeof(Person));
studentA->name ="brian";Person* studentB =(Person*)malloc(sizeof(Person));
studentB->name ="erich";/**
* we could have done the job as simple as this!
* but we need more work if we want to use a function to do the job!
*/// studentA = studentB;
printf("1. studentA = %s (not changed)\n", studentA->name);
dontChange(studentA, studentB);
printf("2. studentA = %s (not changed)\n", studentA->name);
change(&studentA, studentB);
printf("3. studentA = %s (changed!)\n", studentA->name);return0;}/**
* OUTPUT:
* 1. studentA = brian (not changed)
* 2. studentA = brian (not changed)
* 3. studentA = erich (changed!)
*/
Berikut ini adalah contoh C ++ yang sangat sederhana yang menunjukkan bahwa jika Anda ingin menggunakan fungsi untuk mengatur penunjuk ke titik ke objek, Anda memerlukan penunjuk ke penunjuk . Jika tidak, pointer akan tetap kembali ke nol .
(Jawaban C ++, tapi saya percaya itu sama dalam C.)
(Juga, untuk referensi: Google ("lulus dengan nilai c ++") = "Secara default, argumen dalam C ++ dilewatkan oleh nilai. Ketika sebuah argumen dilewatkan oleh nilai, nilai argumen disalin ke dalam parameter fungsi.")
Jadi kami ingin mengatur pointer bsama dengan string a.
"Nilai" dari &main::a(alamat) disalin ke dalam parameter std::string* Function_1::a. Oleh karena itu Function_1::aadalah penunjuk ke (yaitu alamat memori) string main::a.
"Nilai" dari main::b(alamat dalam memori) disalin ke dalam parameter std::string* Function_1::b. Oleh karena itu sekarang ada 2 alamat ini dalam memori, keduanya null pointer. Pada baris tersebut b = a;, variabel lokal Function_1::bkemudian diubah menjadi sama Function_1::a(= &main::a), tetapi variabelnya main::btidak berubah. Setelah panggilan ke Function_1, main::bmasih pointer nol.
Apa yang terjadi di telepon Function_2(&a, &b);?
Perlakuan avariabel adalah sama: di dalam fungsi, Function_2::aadalah alamat string main::a.
Tetapi variabel bsekarang diteruskan sebagai pointer ke pointer. "Nilai" dari &main::b( alamat penunjukmain::b ) disalin ke std::string** Function_2::b. Oleh karena itu dalam Function_2, dereferencing ini sebagai *Function_2::bakan mengakses dan memodifikasi main::b. Jadi garis *b = a;sebenarnya pengaturan main::b(alamat) sama dengan Function_2::a(= alamat main::a) yang kita inginkan.
Jika Anda ingin menggunakan fungsi untuk memodifikasi sesuatu, baik itu objek atau alamat (pointer), Anda harus meneruskan pointer ke hal itu. Hal yang benar - benar Anda lewati tidak dapat dimodifikasi (dalam lingkup panggilan) karena salinan lokal dibuat.
(Pengecualian adalah jika parameter adalah referensi, seperti std::string& a. Tapi biasanya ini const. Umumnya, jika Anda memanggil f(x), jika xadalah objek Anda harus dapat berasumsi bahwa ftidak akan memodifikasi x. Tetapi jika xadalah pointer, maka Anda harus menganggap bahwa fmungkin memodifikasi objek yang ditunjuk oleh x.)
Kode C ++ untuk menjawab pertanyaan C bukanlah ide terbaik.
alk
1
Sedikit terlambat ke pesta, tapi mudah-mudahan ini akan membantu seseorang.
Dalam array C selalu mengalokasikan memori pada stack, sehingga suatu fungsi tidak dapat mengembalikan array (non-statis) karena fakta bahwa memori yang dialokasikan pada stack dibebaskan secara otomatis ketika eksekusi mencapai akhir blok saat ini. Itu benar-benar menjengkelkan ketika Anda ingin berurusan dengan array dua dimensi (yaitu matriks) dan mengimplementasikan beberapa fungsi yang dapat mengubah dan mengembalikan matriks. Untuk mencapai ini, Anda bisa menggunakan pointer-to-pointer untuk mengimplementasikan matriks dengan memori yang dialokasikan secara dinamis:
/* Initializes a matrix */double** init_matrix(int num_rows,int num_cols){// Allocate memory for num_rows float-pointersdouble** A = calloc(num_rows,sizeof(double*));// return NULL if the memory couldn't allocatedif(A == NULL)return NULL;// For each double-pointer (row) allocate memory for num_cols floatsfor(int i =0; i < num_rows; i++){
A[i]= calloc(num_cols,sizeof(double));// return NULL if the memory couldn't allocated// and free the already allocated memoryif(A[i]== NULL){for(int j =0; j < i; j++){
free(A[j]);}
free(A);return NULL;}}return A;}
Double-pointer-to-double-pointer A menunjuk ke elemen pertama A [0] dari blok memori yang elemen-elemennya adalah double-pointer itu sendiri. Anda bisa membayangkan pointer ganda ini sebagai baris dari matriks. Itulah alasan mengapa setiap pointer ganda mengalokasikan memori untuk elemen num_cols bertipe double. Selanjutnya A [i] menunjuk ke baris ke-i, yaitu A [i] menunjuk ke [[]] [0] dan itu hanya elemen-ganda pertama dari blok memori untuk baris ke-i. Akhirnya, Anda dapat mengakses elemen di baris ke-i dan kolom ke-ke-mudah dengan A [i] [j].
Berikut ini adalah contoh lengkap yang menunjukkan penggunaan:
#include<stdio.h>#include<stdlib.h>#include<time.h>/* Initializes a matrix */double** init_matrix(int num_rows,int num_cols){// Allocate memory for num_rows double-pointersdouble** matrix = calloc(num_rows,sizeof(double*));// return NULL if the memory couldn't allocatedif(matrix == NULL)return NULL;// For each double-pointer (row) allocate memory for num_cols// doublesfor(int i =0; i < num_rows; i++){
matrix[i]= calloc(num_cols,sizeof(double));// return NULL if the memory couldn't allocated// and free the already allocated memoryif(matrix[i]== NULL){for(int j =0; j < i; j++){
free(matrix[j]);}
free(matrix);return NULL;}}return matrix;}/* Fills the matrix with random double-numbers between -1 and 1 */void randn_fill_matrix(double** matrix,int rows,int cols){for(int i =0; i < rows;++i){for(int j =0; j < cols;++j){
matrix[i][j]=(double) rand()/RAND_MAX*2.0-1.0;}}}/* Frees the memory allocated by the matrix */void free_matrix(double** matrix,int rows,int cols){for(int i =0; i < rows; i++){
free(matrix[i]);}
free(matrix);}/* Outputs the matrix to the console */void print_matrix(double** matrix,int rows,int cols){for(int i =0; i < rows; i++){for(int j =0; j < cols; j++){
printf(" %- f ", matrix[i][j]);}
printf("\n");}}int main(){
srand(time(NULL));int m =3, n =3;double** A = init_matrix(m, n);
randn_fill_matrix(A, m, n);
print_matrix(A, m, n);
free_matrix(A, m, n);return0;}
Saya telah menggunakan pointer ganda hari ini ketika saya sedang memprogram sesuatu untuk pekerjaan, jadi saya bisa menjawab mengapa kita harus menggunakannya (ini pertama kali saya benar-benar harus menggunakan pointer ganda). Kami harus berurusan dengan pengkodean frame yang terkandung dalam buffer yang merupakan anggota dari beberapa struktur. Dalam encoder kami harus menggunakan pointer ke salah satu struktur itu. Masalahnya adalah bahwa pointer kita sedang diubah untuk menunjuk ke struktur lain dari utas lainnya. Untuk menggunakan struktur saat ini di encoder, saya harus menggunakan pointer ganda, untuk menunjuk ke pointer yang sedang dimodifikasi di thread lain. Awalnya tidak jelas, setidaknya bagi kami, bahwa kami harus mengambil pendekatan ini. Banyak alamat yang dicetak dalam proses :)).
Anda HARUS menggunakan pointer ganda ketika Anda bekerja pada pointer yang diubah di tempat lain aplikasi Anda. Anda mungkin juga menemukan pointer ganda menjadi suatu keharusan ketika Anda berurusan dengan perangkat keras yang kembali dan alamat kepada Anda.
Bandingkan nilai pengubah variabel dengan nilai pengubah pointer :
#include<stdio.h>#include<stdlib.h>void changeA(int(*a)){(*a)=10;}void changeP(int*(*P)){(*P)= malloc(sizeof((*P)));}int main(void){int A =0;
printf("orig. A = %d\n", A);
changeA(&A);
printf("modi. A = %d\n", A);/*************************/int*P = NULL;
printf("orig. P = %p\n", P);
changeP(&P);
printf("modi. P = %p\n", P);
free(P);return EXIT_SUCCESS;}
Ini membantu saya untuk menghindari mengembalikan nilai pointer ketika pointer diubah oleh fungsi yang dipanggil (digunakan dalam daftar tertaut tunggal).
double*
.Jawaban:
Jika Anda ingin memiliki daftar karakter (kata), Anda dapat menggunakan
char *word
Jika Anda ingin daftar kata-kata (kalimat), Anda dapat menggunakan
char **sentence
Jika Anda ingin daftar kalimat (sebuah monolog), Anda dapat menggunakannya
char ***monologue
Jika Anda ingin daftar monolog (biografi), Anda dapat menggunakannya
char ****biography
Jika Anda ingin daftar biografi (bio-library), Anda dapat menggunakannya
char *****biolibrary
Jika Anda ingin daftar bio-libraries (a lol), Anda dapat menggunakan
char ******lol
... ...
ya, saya tahu ini mungkin bukan struktur data terbaik
Contoh penggunaan dengan lol yang sangat sangat sangat membosankan
Keluaran:
sumber
arr[a][b][c]
bukan a***arr
. Pointer pointer menggunakan referensi referensi, sementaraarr[a][b][c]
disimpan sebagai array biasa dalam urutan utama baris.Salah satu alasannya adalah Anda ingin mengubah nilai pointer yang diteruskan ke fungsi sebagai argumen fungsi, untuk melakukan ini Anda memerlukan pointer ke pointer.
Dengan kata sederhana, Gunakan
**
ketika Anda ingin menyimpan (ATAU mempertahankan perubahan) Alokasi Memori atau Penugasan bahkan di luar panggilan fungsi. (Jadi, Lewati fungsi tersebut dengan arg pointer ganda.)Ini mungkin bukan contoh yang sangat baik, tetapi akan menunjukkan kepada Anda penggunaan dasar:
sumber
void allocate(int *p)
dan Anda menyebutnya sebagaiallocate(p)
?pointer1 = pointer2
, Anda memberi pointer1 alamat pointer2.tapi! jika Anda melakukan itu dalam suatu fungsi, dan Anda ingin hasilnya bertahan setelah fungsi selesai, Anda perlu melakukan beberapa pekerjaan tambahan. Anda memerlukan pointer3 baru hanya untuk menunjuk ke pointer1. lewat pointer3 ke fungsi.
ini sebuah contoh. lihat output di bawah ini terlebih dahulu, untuk mengerti.
Inilah hasilnya: ( baca ini dulu )
sumber
Menambah respons Asha , jika Anda menggunakan satu penunjuk ke contoh di bawah (mis. Alokasi1 ()) Anda akan kehilangan referensi ke memori yang dialokasikan di dalam fungsi.
Alasan itu terjadi seperti ini adalah bahwa di
alloc1
pointer dilewatkan oleh nilai. Jadi, ketika dipindahkan ke hasilmalloc
panggilan di dalamalloc1
, perubahan tidak berkaitan dengan kode dalam cakupan yang berbeda.sumber
free(p)
tidak cukup, Anda perluif(p) free(*p)
juga*p
Mengevaluasi nilaiint
holding dari 10, meneruskannyaint
ke free () `adalah ide yang buruk.alloc1()
memperkenalkan kebocoran memori. Nilai pointer yang akan dilewati gratis hilang dengan kembali dari fungsi.Saya melihat contoh yang sangat baik hari ini, dari posting blog ini , seperti yang saya rangkum di bawah ini.
Bayangkan Anda memiliki struktur untuk node dalam daftar yang ditautkan, yang mungkin adalah
Sekarang Anda ingin mengimplementasikan
remove_if
fungsi, yang menerima kriteria penghapusanrm
sebagai salah satu argumen dan melintasi daftar yang ditautkan: jika sebuah entri memenuhi kriteria (sepertirm(entry)==true
), simpulnya akan dihapus dari daftar. Pada akhirnya,remove_if
mengembalikan kepala (yang mungkin berbeda dari kepala asli) dari daftar tertaut.Anda bisa menulis
sebagai
for
loop Anda . Pesannya adalah, tanpa pointer ganda, Anda harus mempertahankanprev
variabel untuk mengatur ulang pointer , dan menangani dua kasus yang berbeda.Tetapi dengan double pointer, Anda sebenarnya bisa menulis
Anda tidak memerlukan yang
prev
sekarang karena Anda dapat langsung memodifikasi apa yangprev->next
ditunjuk .Agar lebih jelas, ikuti sedikit kode. Selama penghapusan:
entry == *head
: itu akan*head (==*curr) = *head->next
-head
sekarang menunjuk ke pointer dari node heading baru. Anda melakukan ini dengan mengubah secara langsunghead
konten ke pointer baru.entry != *head
: sama,*curr
adalah apa yangprev->next
menunjuk, dan sekarang menunjuk keentry->next
.Dalam kasus apa pun, Anda dapat mengatur ulang pointer dengan cara yang sama dengan pointer ganda.
sumber
1. Konsep Dasar -
Ketika Anda menyatakan sebagai berikut: -
1. char * ch - (disebut penunjuk karakter)
- ch berisi alamat karakter tunggal.
- (* ch) akan merujuk pada nilai karakter ..
2. char ** ch -
'ch' berisi alamat dari Array of character pointer. (seperti pada 1)
'* ch' berisi alamat karakter tunggal. (Perhatikan bahwa ini berbeda dari 1, karena perbedaan dalam deklarasi).
(** ch) akan merujuk pada nilai karakter yang tepat ..
Menambahkan lebih banyak pointer memperluas dimensi tipe data, dari karakter ke string, ke array string, dan seterusnya ... Anda dapat menghubungkannya dengan 1d, 2d, 3d matrix ..
Jadi, penggunaan pointer tergantung pada bagaimana Anda mendeklarasikannya.
Ini kode sederhana ..
2. Aplikasi Dua Pointer Lain -
(ini juga mencakup referensi lewat referensi)
Misalkan Anda ingin memperbarui karakter dari suatu fungsi. Jika Anda mencoba yang berikut: -
Outputnya adalah AA. Ini tidak berfungsi, karena Anda telah "Melewati Nilai" ke fungsi.
Cara yang benar untuk melakukan itu adalah -
Sekarang perluas persyaratan ini untuk memperbarui string alih-alih karakter.
Untuk ini, Anda perlu menerima parameter dalam fungsi sebagai penunjuk ganda.
Dalam contoh ini, metode mengharapkan penunjuk ganda sebagai parameter untuk memperbarui nilai string.
sumber
#include <stdio.h> int main() { char *ptr = 0; ptr = malloc(255); // allocate some memory strcpy( ptr, "Stack Overflow Rocks..!!"); printf("%s\n", ptr); printf("%d\n",strlen(ptr)); free(ptr); return 0; }
Tetapi Anda dapat melakukannya tanpa menggunakan pointer ganda juga.char
pointer. Pointer ke arraychar*
akan diketik misalnya seperti ini:char(*(*p)[42])
mendefinisikanp
sebagai pointer ke array dari 42 pointer kechar
.*str = ...
str
ini adalah dereferenced uninitialised meminta perilaku tidak terdefinisi.malloc(sizeof(char) * 10);
tidak mengalokasikan ruang untuk 10 pointer kechar
tetapichar
hanya untuk 10 ..for(i=0;i<10;i++) { str = ...
dapat menggunakan indeksi
.Pointer ke pointer juga berguna sebagai "pegangan" ke memori di mana Anda ingin menyampaikan "pegangan" antara fungsi ke memori yang dapat dilokasikan kembali. Itu pada dasarnya berarti bahwa fungsi tersebut dapat mengubah memori yang sedang diarahkan oleh pointer di dalam variabel pegangan, dan setiap fungsi atau objek yang menggunakan pegangan akan menunjuk ke memori yang baru dipindahkan (atau dialokasikan). Perpustakaan suka melakukan ini dengan tipe data "buram", yaitu tipe data yang Anda tidak perlu khawatir tentang apa yang mereka lakukan dengan memori yang ditunjuk lakukan, Anda cukup membagikan "pegangan" antara fungsi perpustakaan untuk melakukan beberapa operasi pada memori itu ...
Misalnya:
Semoga ini membantu,
Jason
sumber
unsigned char
secara khusus digunakan karena kami menyimpan pointer ke data biner yang akan direpresentasikan sebagai byte mentah. Menggunakanvoid
akan membutuhkan gips di beberapa titik, dan umumnya tidak dapat dibaca dengan maksud apa yang sedang dilakukan.Contoh sederhana yang mungkin pernah Anda lihat sebelumnya
Pada parameter kedua Anda memilikinya: pointer ke pointer ke char.
Perhatikan bahwa notasi pointer (
char* c
) dan notasi array (char c[]
) dapat dipertukarkan dalam argumen fungsi. Jadi Anda juga bisa menulischar *argv[]
. Dengan kata lainchar *argv[]
danchar **argv
dapat dipertukarkan.Apa yang diwakili di atas sebenarnya adalah array dari urutan karakter (argumen baris perintah yang diberikan kepada suatu program saat startup).
Lihat juga jawaban ini untuk detail lebih lanjut tentang tanda tangan fungsi di atas.
sumber
char* c
) dan notasi array (char c[]
) dapat dipertukarkan" (dan memiliki arti yang persis sama) dalam argumen fungsi . Mereka berbeda namun di luar argumen fungsi.String adalah contoh yang bagus dari penggunaan pointer ganda. String itu sendiri adalah sebuah pointer, jadi setiap kali Anda perlu menunjuk ke sebuah string, Anda akan membutuhkan pointer ganda.
sumber
Misalnya, Anda mungkin ingin memastikan bahwa ketika Anda membebaskan memori dari sesuatu yang Anda tetapkan pointer ke nol setelahnya.
Saat Anda memanggil fungsi ini, Anda akan memanggilnya dengan alamat penunjuk
Sekarang
myMemory
diatur ke NULL dan segala upaya untuk menggunakannya kembali akan menjadi sangat salah.sumber
if(*memory)
danfree(*memory);
Misalnya jika Anda ingin akses acak ke data yang tidak bersebelahan.
- dalam C
Anda menyimpan sebuah pointer
p
yang menunjuk ke array pointer. Setiap pointer menunjuk ke sepotong data.Jika
sizeof(T)
besar, tidak mungkin untuk mengalokasikan blok yang berdekatan (yaitu menggunakan malloc)sizeof(T) * n
byte.sumber
Satu hal yang saya gunakan untuk terus-menerus adalah ketika saya memiliki array objek dan saya perlu melakukan pencarian (pencarian biner) pada mereka dengan bidang yang berbeda.
Saya menyimpan array asli ...
Kemudian buatlah array pointer yang diurutkan ke objek.
Anda dapat membuat sebanyak banyak array pointer yang diurutkan yang Anda butuhkan, kemudian gunakan pencarian biner pada array pointer yang diurutkan untuk mengakses objek yang Anda butuhkan oleh data yang Anda miliki. Array objek asli dapat tetap tidak disortir, tetapi setiap array pointer akan diurutkan berdasarkan bidang yang ditentukan.
sumber
Mengapa pointer ganda?
Tujuannya adalah untuk mengubah apa yang ditunjuk oleh studentA, menggunakan suatu fungsi.
sumber
Berikut ini adalah contoh C ++ yang sangat sederhana yang menunjukkan bahwa jika Anda ingin menggunakan fungsi untuk mengatur penunjuk ke titik ke objek, Anda memerlukan penunjuk ke penunjuk . Jika tidak, pointer akan tetap kembali ke nol .
(Jawaban C ++, tapi saya percaya itu sama dalam C.)
(Juga, untuk referensi: Google ("lulus dengan nilai c ++") = "Secara default, argumen dalam C ++ dilewatkan oleh nilai. Ketika sebuah argumen dilewatkan oleh nilai, nilai argumen disalin ke dalam parameter fungsi.")
Jadi kami ingin mengatur pointer
b
sama dengan stringa
.Apa yang terjadi di telepon
Function_1(&a, b);
?"Nilai" dari
&main::a
(alamat) disalin ke dalam parameterstd::string* Function_1::a
. Oleh karena ituFunction_1::a
adalah penunjuk ke (yaitu alamat memori) stringmain::a
."Nilai" dari
main::b
(alamat dalam memori) disalin ke dalam parameterstd::string* Function_1::b
. Oleh karena itu sekarang ada 2 alamat ini dalam memori, keduanya null pointer. Pada baris tersebutb = a;
, variabel lokalFunction_1::b
kemudian diubah menjadi samaFunction_1::a
(=&main::a
), tetapi variabelnyamain::b
tidak berubah. Setelah panggilan keFunction_1
,main::b
masih pointer nol.Apa yang terjadi di telepon
Function_2(&a, &b);
?Perlakuan
a
variabel adalah sama: di dalam fungsi,Function_2::a
adalah alamat stringmain::a
.Tetapi variabel
b
sekarang diteruskan sebagai pointer ke pointer. "Nilai" dari&main::b
( alamat penunjukmain::b
) disalin kestd::string** Function_2::b
. Oleh karena itu dalam Function_2, dereferencing ini sebagai*Function_2::b
akan mengakses dan memodifikasimain::b
. Jadi garis*b = a;
sebenarnya pengaturanmain::b
(alamat) sama denganFunction_2::a
(= alamatmain::a
) yang kita inginkan.Jika Anda ingin menggunakan fungsi untuk memodifikasi sesuatu, baik itu objek atau alamat (pointer), Anda harus meneruskan pointer ke hal itu. Hal yang benar - benar Anda lewati tidak dapat dimodifikasi (dalam lingkup panggilan) karena salinan lokal dibuat.
(Pengecualian adalah jika parameter adalah referensi, seperti
std::string& a
. Tapi biasanya iniconst
. Umumnya, jika Anda memanggilf(x)
, jikax
adalah objek Anda harus dapat berasumsi bahwaf
tidak akan memodifikasix
. Tetapi jikax
adalah pointer, maka Anda harus menganggap bahwaf
mungkin memodifikasi objek yang ditunjuk olehx
.)sumber
Sedikit terlambat ke pesta, tapi mudah-mudahan ini akan membantu seseorang.
Dalam array C selalu mengalokasikan memori pada stack, sehingga suatu fungsi tidak dapat mengembalikan array (non-statis) karena fakta bahwa memori yang dialokasikan pada stack dibebaskan secara otomatis ketika eksekusi mencapai akhir blok saat ini. Itu benar-benar menjengkelkan ketika Anda ingin berurusan dengan array dua dimensi (yaitu matriks) dan mengimplementasikan beberapa fungsi yang dapat mengubah dan mengembalikan matriks. Untuk mencapai ini, Anda bisa menggunakan pointer-to-pointer untuk mengimplementasikan matriks dengan memori yang dialokasikan secara dinamis:
Inilah ilustrasi:
Double-pointer-to-double-pointer A menunjuk ke elemen pertama A [0] dari blok memori yang elemen-elemennya adalah double-pointer itu sendiri. Anda bisa membayangkan pointer ganda ini sebagai baris dari matriks. Itulah alasan mengapa setiap pointer ganda mengalokasikan memori untuk elemen num_cols bertipe double. Selanjutnya A [i] menunjuk ke baris ke-i, yaitu A [i] menunjuk ke [[]] [0] dan itu hanya elemen-ganda pertama dari blok memori untuk baris ke-i. Akhirnya, Anda dapat mengakses elemen di baris ke-i dan kolom ke-ke-mudah dengan A [i] [j].
Berikut ini adalah contoh lengkap yang menunjukkan penggunaan:
sumber
Saya telah menggunakan pointer ganda hari ini ketika saya sedang memprogram sesuatu untuk pekerjaan, jadi saya bisa menjawab mengapa kita harus menggunakannya (ini pertama kali saya benar-benar harus menggunakan pointer ganda). Kami harus berurusan dengan pengkodean frame yang terkandung dalam buffer yang merupakan anggota dari beberapa struktur. Dalam encoder kami harus menggunakan pointer ke salah satu struktur itu. Masalahnya adalah bahwa pointer kita sedang diubah untuk menunjuk ke struktur lain dari utas lainnya. Untuk menggunakan struktur saat ini di encoder, saya harus menggunakan pointer ganda, untuk menunjuk ke pointer yang sedang dimodifikasi di thread lain. Awalnya tidak jelas, setidaknya bagi kami, bahwa kami harus mengambil pendekatan ini. Banyak alamat yang dicetak dalam proses :)).
Anda HARUS menggunakan pointer ganda ketika Anda bekerja pada pointer yang diubah di tempat lain aplikasi Anda. Anda mungkin juga menemukan pointer ganda menjadi suatu keharusan ketika Anda berurusan dengan perangkat keras yang kembali dan alamat kepada Anda.
sumber
Bandingkan nilai pengubah variabel dengan nilai pengubah pointer :
Ini membantu saya untuk menghindari mengembalikan nilai pointer ketika pointer diubah oleh fungsi yang dipanggil (digunakan dalam daftar tertaut tunggal).
LAMA (buruk):
BARU (lebih baik):
sumber