Kapan tipuan ganda digunakan dalam C? Adakah yang bisa menjelaskan dengan contoh?
Yang saya tahu adalah bahwa tipuan ganda adalah pointer ke pointer. Mengapa saya membutuhkan pointer ke pointer?
Kapan tipuan ganda digunakan dalam C? Adakah yang bisa menjelaskan dengan contoh?
Yang saya tahu adalah bahwa tipuan ganda adalah pointer ke pointer. Mengapa saya membutuhkan pointer ke pointer?
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
#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);
}
Keluaran:
total kata dalam lol saya: 243
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.
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:
void allocate(int** p)
{
*p = (int*)malloc(sizeof(int));
}
int main()
{
int* p = NULL;
allocate(&p);
*p = 42;
free(p);
}
void allocate(int *p)
dan Anda menyebutnya sebagai allocate(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.
#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);
return 0;
}
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!!!.
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.
void alloc2(int** p) {
*p = (int*)malloc(sizeof(int));
**p = 10;
}
void alloc1(int* p) {
p = (int*)malloc(sizeof(int));
*p = 10;
}
int main(){
int *p = NULL;
alloc1(p);
//printf("%d ",*p);//undefined
alloc2(&p);
printf("%d ",*p);//will print 10
free(p);
return 0;
}
Alasan itu terjadi seperti ini adalah bahwa di alloc1
pointer dilewatkan oleh nilai. Jadi, ketika dipindahkan ke hasil malloc
panggilan di dalam alloc1
, perubahan tidak berkaitan dengan kode dalam cakupan yang berbeda.
free(p)
tidak cukup, Anda perlu if(p) free(*p)
juga
*p
Mengevaluasi nilai int
holding dari 10, meneruskannya int
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
typedef struct node
{
struct node * next;
....
} node;
Sekarang Anda ingin mengimplementasikan remove_if
fungsi, yang menerima kriteria penghapusan rm
sebagai 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_if
mengembalikan 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 for
loop Anda . Pesannya adalah, tanpa pointer ganda, Anda harus mempertahankan prev
variabel untuk mengatur ulang pointer , dan menangani dua kasus yang berbeda.
Tetapi dengan double pointer, Anda sebenarnya bisa menulis
// now head is a double pointer
for (node** curr = head; *curr; )
{
node * entry = *curr;
if (rm(entry))
{
*curr = entry->next;
free(entry);
}
else
curr = &entry->next;
}
Anda tidak memerlukan yang prev
sekarang karena Anda dapat langsung memodifikasi apa yang prev->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 yang prev->next
menunjuk, dan sekarang menunjuk ke entry->next
.Dalam kasus apa pun, Anda dapat mengatur ulang pointer dengan cara yang sama dengan pointer ganda.
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 ..
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: -
void func(char ch)
{
ch = 'B';
}
int main()
{
char ptr;
ptr = 'A';
printf("%c", ptr);
func(ptr);
printf("%c\n", ptr);
}
Outputnya adalah AA. Ini tidak berfungsi, karena Anda telah "Melewati Nilai" ke fungsi.
Cara yang benar untuk melakukan itu adalah -
void func( char *ptr) //Passed by Reference
{
*ptr = 'B';
}
int main()
{
char *ptr;
ptr = (char *)malloc(sizeof(char) * 1);
*ptr = 'A';
printf("%c\n", *ptr);
func(ptr);
printf("%c\n", *ptr);
}
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 pointers
int 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.
char
pointer. Pointer ke array char*
akan diketik misalnya seperti ini: char(*(*p)[42])
mendefinisikan p
sebagai pointer ke array dari 42 pointer ke char
.
*str = ...
str
ini adalah dereferenced uninitialised meminta perilaku tidak terdefinisi.
malloc(sizeof(char) * 10);
tidak mengalokasikan ruang untuk 10 pointer ke char
tetapi char
hanya untuk 10 ..
for(i=0;i<10;i++) { str = ...
dapat menggunakan indeks i
.
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>
typedef unsigned char** handle_type;
//some data_structure that the library functions would work with
typedef struct
{
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);
return 0;
}
Semoga ini membantu,
Jason
unsigned char
secara khusus digunakan karena kami menyimpan pointer ke data biner yang akan direpresentasikan sebagai byte mentah. Menggunakan void
akan membutuhkan gips di beberapa titik, dan umumnya tidak dapat dibaca dengan maksud apa yang sedang dilakukan.
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 **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.
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.
Misalnya, Anda mungkin ingin memastikan bahwa ketika Anda membebaskan memori dari sesuatu yang Anda tetapkan pointer ke nol setelahnya.
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
Saat Anda memanggil fungsi ini, Anda akan memanggilnya dengan alamat penunjuk
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
Sekarang myMemory
diatur ke NULL dan segala upaya untuk menggunakannya kembali akan menjadi sangat salah.
if(*memory)
danfree(*memory);
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) * n
byte.
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.
int compare_object_by_name( const void *v1, const void *v2 ) {
OBJECT *o1 = *(OBJECT **)v1;
OBJECT *o2 = *(OBJECT **)v2;
return (strcmp(o1->name, o2->name);
}
OBJECT **object_ptrs_by_name = malloc(sizeof(OBJECT *)*num_objects);
int i = 0;
for( ; i<num_objects; i++)
object_ptrs_by_name[i] = original_array+i;
qsort(object_ptrs_by_name, num_objects, sizeof(OBJECT *), compare_object_by_name);
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.
Mengapa pointer ganda?
Tujuannya adalah untuk mengubah apa yang ditunjuk oleh studentA, menggunakan suatu fungsi.
#include <stdio.h>
#include <stdlib.h>
typedef struct Person{
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);
return 0;
}
/**
* 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 b
sama dengan string a
.
#include <iostream>
#include <string>
void Function_1(std::string* a, std::string* b) {
b = a;
std::cout << (b == nullptr); // False
}
void Function_2(std::string* a, std::string** b) {
*b = a;
std::cout << (b == nullptr); // False
}
int main() {
std::string a("Hello!");
std::string* b(nullptr);
std::cout << (b == nullptr); // True
Function_1(&a, b);
std::cout << (b == nullptr); // True
Function_2(&a, &b);
std::cout << (b == nullptr); // False
}
// Output: 10100
Apa yang terjadi di telepon Function_1(&a, b);
?
"Nilai" dari &main::a
(alamat) disalin ke dalam parameter std::string* Function_1::a
. Oleh karena itu Function_1::a
adalah 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::b
kemudian diubah menjadi sama Function_1::a
(= &main::a
), tetapi variabelnya main::b
tidak berubah. Setelah panggilan ke Function_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 string main::a
.
Tetapi variabel b
sekarang diteruskan sebagai pointer ke pointer. "Nilai" dari &main::b
( alamat penunjuk main::b
) disalin ke std::string** Function_2::b
. Oleh karena itu dalam Function_2, dereferencing ini sebagai *Function_2::b
akan 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 x
adalah objek Anda harus dapat berasumsi bahwa f
tidak akan memodifikasi x
. Tetapi jika x
adalah pointer, maka Anda harus menganggap bahwa f
mungkin memodifikasi objek yang ditunjuk oleh x
.)
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-pointers
double** A = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(A == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols floats
for(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 memory
if(A[i] == NULL){
for(int j = 0; j < i; j++){
free(A[j]);
}
free(A);
return NULL;
}
}
return A;
}
Inilah ilustrasi:
double** double* double
------------- ---------------------------------------------------------
A ------> | A[0] | ----> | A[0][0] | A[0][1] | A[0][2] | ........ | A[0][cols-1] |
| --------- | ---------------------------------------------------------
| A[1] | ----> | A[1][0] | A[1][1] | A[1][2] | ........ | A[1][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[i] | ----> | A[i][0] | A[i][1] | A[i][2] | ........ | A[i][cols-1] |
| --------- | ---------------------------------------------------------
| . | .
| . | .
| . | .
| --------- | ---------------------------------------------------------
| A[rows-1] | ----> | A[rows-1][0] | A[rows-1][1] | ... | A[rows-1][cols-1] |
------------- ---------------------------------------------------------
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-pointers
double** matrix = calloc(num_rows, sizeof(double*));
// return NULL if the memory couldn't allocated
if(matrix == NULL) return NULL;
// For each double-pointer (row) allocate memory for num_cols
// doubles
for(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 memory
if(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);
return 0;
}
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).
LAMA (buruk):
int *func(int *P)
{
...
return P;
}
int main(void)
{
int *pointer;
pointer = func(pointer);
...
}
BARU (lebih baik):
void func(int **pointer)
{
...
}
int main(void)
{
int *pointer;
func(&pointer);
...
}
double*
.