C'de çift dolaylı aktarma ne zaman kullanılmalıdır? Herkes bir örnekle açıklayabilir mi?
Bildiğim şey, bir çift dolaylamanın bir işaretçiye bir işaretçi olduğudur. Neden bir işaretçiye bir işaretçiye ihtiyacım var?
C'de çift dolaylı aktarma ne zaman kullanılmalıdır? Herkes bir örnekle açıklayabilir mi?
Bildiğim şey, bir çift dolaylamanın bir işaretçiye bir işaretçi olduğudur. Neden bir işaretçiye bir işaretçiye ihtiyacım var?
Yanıtlar:
Bir karakter listesi (bir kelime) almak istiyorsanız, char *word
Kelimelerin bir listesini (cümle) istiyorsanız, char **sentence
Cümle listesi (bir monolog) istiyorsanız, kullanabilirsiniz char ***monologue
Monologların (biyografi) bir listesini istiyorsanız, char ****biography
Bir biyografi listesi (bir biyo kütüphane) istiyorsanız, char *****biolibrary
Biyo-kütüphanelerin bir listesini istiyorsanız (a ?? lol), char ******lol
... ...
evet, bunların en iyi veri yapıları olmayabileceğini biliyorum
Çok çok çok sıkıcı bir lol ile kullanım örneği
#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);
}
Çıktı:
lolimdeki toplam kelime sayısı: 243
arr[a][b][c]
olmadığını belirtmek istedim ***arr
. İşaretçi işaretçisi referansların referanslarını kullanır arr[a][b][c]
, sıra ana sırada sıralı bir dizi olarak saklanır.
Bunun bir nedeni, bir işleve geçirilen işaretçinin değerini işlev bağımsız değişkeni olarak değiştirmek istemenizdir, bunu yapmak için işaretçiye işaretçi gerekir.
Basit bir ifadeyle, bir işlev çağrısının dışında bile Bellek Ayırma veya Atamayı korumak (VEYA değişikliği tutmak) istediğinizde kullanın **
. (Yani, böyle bir işlevi çift pointer arg ile geçirin.)
Bu çok iyi bir örnek olmayabilir, ancak size temel kullanımı gösterecektir:
void allocate(int** p)
{
*p = (int*)malloc(sizeof(int));
}
int main()
{
int* p = NULL;
allocate(&p);
*p = 42;
free(p);
}
void allocate(int *p)
ve onu şöyle çağırdın allocate(p)
?
pointer1 = pointer2
, pointer1'e pointer2 adresini verirsiniz.fakat! bunu bir işlev içinde yaparsanız ve işlev tamamlandıktan sonra sonucun devam etmesini istiyorsanız, fazladan bir iş yapmanız gerekir. yalnızca pointer1'i işaret etmek için yeni bir pointer3'e ihtiyacınız var. pointer3 işlevine iletir.
işte bir örnek. anlamak için önce aşağıdaki çıktıya bakın.
#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);
}
İşte çıktı: ( önce bunu okuyun )
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!!!.
Ekleme Asha'nın Eğer örnek, aşağıda gösterilen tek işaretçi kullanırsanız, yanıt (örn alloc1 ()) Eğer işlevi içinde ayrılan belleğe başvuru kaybedecektir.
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;
}
Bunun gibi olmasının nedeni alloc1
, işaretçideki değerin iletilmesidir. Bu nedenle, malloc
içindeki çağrı sonucuna yeniden atandığında alloc1
, değişiklik farklı bir kapsamdaki kodla ilgili değildir.
free(p)
yetmez yapmanız gerekenler if(p) free(*p)
de
*p
değerlendirir int
, int
bunu free () `ye geçirmek kötü bir fikirdir.
alloc1()
bir bellek sızıntısı ortaya çıkar. Serbest bırakılacak işaretçi değeri işlevden dönülerek kaybolur.
Aşağıda, aşağıda özetlediğim gibi, bu blog yayınından çok güzel bir örnek gördüm .
Bağlantılı bir listedeki düğümler için bir yapınız olduğunu düşünün.
typedef struct node
{
struct node * next;
....
} node;
Şimdi remove_if
bir kaldırma ölçütünü rm
bağımsız değişkenlerden biri olarak kabul eden ve bağlantılı listeyi geçen bir işlev uygulamak istiyorsunuz : bir girdi ölçütü karşılarsa (benzer bir şey rm(entry)==true
), düğümü listeden kaldırılır. Sonunda, remove_if
bağlı listenin başını (orijinal başından farklı olabilir) döndürür.
Yazabilirsiniz
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;
}
senin kadar for
döngü. İleti, çift işaretçiler olmadanprev
, işaretçileri yeniden düzenlemek ve iki farklı durumu ele almak için bir değişken tutmanız gerekir .
Ancak çift işaretçilerle, aslında
// 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;
}
prev
Şimdi bir şeyeprev->next
ihtiyacınız yok çünkü işaret ettiğiniz şeyi doğrudan değiştirebilirsiniz .
İşleri daha net hale getirmek için kodu biraz izleyelim. Kaldırma işlemi sırasında:
entry == *head
: olacak *head (==*curr) = *head->next
- head
şimdi yeni başlık düğümünün işaretçisini gösteriyor. Bunu doğrudan head
içeriğini yeni bir işaretçiye değiştirerek yaparsınız .entry != *head
: benzer şekilde, işaret eden ve şimdi işaret eden *curr
şeydir .prev->next
entry->next
Bu durumda, işaretçileri çift işaretçilerle birleştirilmiş bir şekilde yeniden düzenleyebilirsiniz.
1. Temel Kavram -
Aşağıdaki gibi beyan ettiğinizde: -
1. char * ch - (karakter işaretçisi olarak adlandırılır)
- ch, tek bir karakterin adresini içerir.
- (* ch) karakterin değerine bağlı olacaktır.
2. char ** ch -
'ch', bir karakter işaretçisi dizisinin adresini içerir. (1'deki gibi)
'* ch' tek bir karakterin adresini içerir. (Beyandaki farktan dolayı 1'den farklı olduğunu unutmayın).
(** ch), karakterin tam değerine bağlı olacaktır.
Daha fazla işaretçi eklemek, bir veri tipinin boyutunu karakterden dizeye, dizelerden oluşan diziye kadar genişletir ... 1d, 2d, 3d matrisle ilişkilendirebilirsiniz.
Dolayısıyla, işaretçinin kullanımı onu nasıl bildirdiğinize bağlıdır.
İşte basit bir kod ..
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. Çift İşaretçilerin Başka Bir Uygulaması -
(bu aynı zamanda referans ile geçişi de kapsayacaktır)
Bir fonksiyondan bir karakteri güncellemek istediğinizi varsayalım. Aşağıdakileri denerseniz: -
void func(char ch)
{
ch = 'B';
}
int main()
{
char ptr;
ptr = 'A';
printf("%c", ptr);
func(ptr);
printf("%c\n", ptr);
}
Çıkış AA olacaktır. Bu, işlev için "Değere Göre Geçti" olarak çalışmaz.
Bunu yapmanın doğru yolu -
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);
}
Şimdi karakter yerine bir dizeyi güncellemek için bu gereksinimi uzatın.
Bunun için işlevdeki parametreyi çift işaretçi olarak almanız gerekir.
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);
}
Bu örnekte, yöntem, bir dizenin değerini güncellemek için parametre olarak bir çift işaretçi bekler.
#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; }
Ancak çift işaretçiyi kullanmadan da yapabilirsiniz.
char
işaretçi dizisinin 1. öğesinin adresini içerir . Bir dizinin işaretçisi char*
örneğin şöyle yazılabilir: 42 işaretçi dizisinin işaretçisi olarak char(*(*p)[42])
tanımlar . p
char
*str = ...
str
tanımlanmamış davranışı çağıran kayıttan çıkarılmış, başlatılmamış.
malloc(sizeof(char) * 10);
10 işaretçiye yer ayırmaz, sadece char
10'a yer char
for(i=0;i<10;i++) { str = ...
dizini kullanmak için özlüyor i
.
İşaretçilerden işaretleyicilere, yeniden konumlandırılabilir belleğe işlevler arasında bir "tutamacın" etrafından geçmek istediğiniz bellekte "tutamaçlar" olarak da kullanışlı olurlar. Bu, temel olarak işlevin tanıtıcı değişkenin içindeki işaretçiyle gösterilen belleği değiştirebileceği anlamına gelir ve tanıtıcıyı kullanan her işlev veya nesne yeni taşınan (veya ayrılan) belleği düzgün bir şekilde gösterecektir. Kütüphaneler bunu "opak" veri türleriyle yapmayı severler, yani veri türleri, işaret edilen bellekle ne yaptıklarından endişelenmenize gerek kalmadıysa, o bellekte bazı işlemleri gerçekleştirmek için kütüphanenin fonksiyonları ...
Örneğin:
#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;
}
Bu yardımcı olur umarım,
Jason
unsigned char
özellikle kullanılır, çünkü ham bayt olarak temsil edilecek ikili verilere bir işaretçi depolarız. Kullanmak void
bir noktada alçı gerektirir ve genellikle ne yapıldığının amacı kadar okunabilir değildir.
int main(int argc, char **argv)
İkinci parametrede: işaretçi - işaretçi.
İşaretçi gösteriminin ( char* c
) ve dizi gösteriminin ( char c[]
) işlev bağımsız değişkenlerinde değiştirilebilir olduğunu unutmayın. Böylece yazabilirsiniz char *argv[]
. Başka bir deyişle char *argv[]
ve char **argv
değiştirilebilir.
Yukarıdakilerin gösterdiği şey aslında bir dizi karakter dizisidir (başlangıçta bir programa verilen komut satırı bağımsız değişkenleri).
Ayrıca bkz bu cevabı yukarıdaki fonksiyon imza ile ilgili daha fazla ayrıntı için.
char* c
char c[]
işlev argümanlarında "işaretçi gösterimi ( ) ve dizi gösterimi ( ) değiştirilebilir" (ve aynı anlama sahiptir) . Ancak, işlev argümanlarının dışında farklıdırlar.
Örneğin, bir şeyin belleğini boşalttığınızda işaretçiyi daha sonra null değerine ayarladığınızdan emin olmak isteyebilirsiniz.
void safeFree(void** memory) {
if (*memory) {
free(*memory);
*memory = NULL;
}
}
Bu işlevi çağırdığınızda, bir işaretçi adresiyle çağırırsınız
void* myMemory = someCrazyFunctionThatAllocatesMemory();
safeFree(&myMemory);
şimdi myMemory
NULL olarak ayarlanmıştır ve yeniden kullanma girişimleri çok yanlış olacaktır.
if(*memory)
vefree(*memory);
Örneğin bitişik olmayan verilere rasgele erişim istiyorsanız.
p -> [p0, p1, p2, ...]
p0 -> data1
p1 -> data2
- C cinsinden
T ** p = (T **) malloc(sizeof(T*) * n);
p[0] = (T*) malloc(sizeof(T));
p[1] = (T*) malloc(sizeof(T));
Bir işaretçi depolarsınız p
dizisini gösteren . Her işaretçi bir veri parçasını gösterir.
Eğer sizeof(T)
büyük bunun (yani malloc kullanarak) bitişik bir bloğun yerleştirilmesi mümkün olmayabilir sizeof(T) * n
bayt.
Onları sürekli olarak kullandığım bir şey, bir dizi nesneye sahip olduğum ve farklı alanlar tarafından aramaları (ikili arama) gerçekleştirmem gerektiğinde.
Orijinal diziyi saklıyorum ...
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
Ardından nesnelere sıralı işaretçiler dizisi yapın.
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);
İhtiyacınız olan sayıda sıralanmış işaretçi dizisi yapabilir, ardından ihtiyacınız olan nesneye sahip olduğunuz verilere erişmek için sıralanmış işaretçi dizisinde ikili bir arama kullanabilirsiniz. Orijinal nesne dizisi ayrılmadan kalabilir, ancak her işaretçi dizisi belirtilen alanlarına göre sıralanır.
Neden çift işaretçiler?
Amaç, bir işlevi kullanarak studentA'nın işaret ettiği şeyi değiştirmektir.
#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!)
*/
Aşağıda, bir nesneyi gösterecek bir işaretçi ayarlamak için bir işlev kullanmak istiyorsanız, bir işaretçiye bir işaretçiye ihtiyacınız olduğunu gösteren çok basit bir C ++ örneğidir . Aksi takdirde, işaretçi null değerine geri dönmeye devam eder .
(C ++ yanıtı, ancak C'de aynı olduğuna inanıyorum.)
(Ayrıca, başvuru için: Google ("c ++ değerine göre geç") = "Varsayılan olarak, C ++ içindeki bağımsız değişkenler değere göre iletilir. Bir bağımsız değişken değere göre iletildiğinde, bağımsız değişkenin değeri işlevin parametresine kopyalanır.")
Bu yüzden işaretçiyi b
dizeye eşit ayarlamak istiyoruz 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
Hatta ne olur Function_1(&a, b);
?
&main::a
Bir adresin "değeri" parametreye kopyalanır std::string* Function_1::a
. Bu nedenle Function_1::a
, dizenin işaretçisi (yani bellek adresi) main::a
.
main::b
(Bellekteki bir adres) öğesinin "değeri" parametreye kopyalanır std::string* Function_1::b
. Bu nedenle, bellekte bu adreslerden 2 tanesi var, her ikisi de boş gösterici. Satırda b = a;
, yerel değişken Function_1::b
daha sonra eşittir Function_1::a
(= &main::a
), ancak değişken main::b
değişmez. Çağrısının ardından Function_1
, main::b
yine bir boş göstericidir.
Hatta ne olur Function_2(&a, &b);
?
a
Değişkenin işlenmesi aynıdır: fonksiyon Function_2::a
içinde, dizenin adresidir main::a
.
Ancak değişken b
şimdi bir işaretçiye işaretçi olarak aktarılıyor. Öğesinin "değeri" &main::b
( işaretçinin adresi main::b
) içine kopyalanır std::string** Function_2::b
. Bu nedenle, Function_2 içinde *Function_2::b
erişim ve değişiklik olarak bu kayıttan çıkarılması main::b
. Yani çizgi *b = a;
aslında main::b
(adres) Function_2::a
(= adres ) 'e eşit main::a
olanı istiyoruz.
Bir şeyi değiştirmek için bir işlev kullanmak istiyorsanız, nesne veya adres (işaretçi) olsun, o öğeye bir işaretçi iletmeniz gerekir. Yerel bir kopya yapıldığı için, gerçekte ilettiğiniz şey değiştirilemez (arama kapsamında).
(Parametre referans gibi, eğer bir istisna oluşturur std::string& a
. Ama genelde bunlar const
. Genellikle, ararsanız f(x)
eğer x
bir nesnedir sen varsaymak gerekir f
olmayacaktır değiştirmek x
. Ama eğer x
bir gösterici, o zaman gerekir varsaymak f
olabilir işaret ettiği nesneyi değiştirmek x
.)
Partiye biraz geç, ama umarım bu birine yardım eder.
C dizilerinde her zaman yığına bellek ayırır, bu nedenle bir işlev (statik olmayan) bir dizi döndüremez, çünkü yürütme geçerli bloğun sonuna ulaştığında yığına ayrılan belleğin otomatik olarak boşalması sağlanır. İki boyutlu dizilerle (yani matrislerle) uğraşmak ve matrisleri değiştirebilen ve geri döndürebilen birkaç işlevi uygulamak istediğinizde bu gerçekten sinir bozucu. Bunu başarmak için, dinamik olarak ayrılmış belleğe sahip bir matris uygulamak için bir işaretçi-işaretçi kullanabilirsiniz:
/* 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;
}
İşte bir örnek:
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] |
------------- ---------------------------------------------------------
Çift-işaretçi-çift-işaretçi A, öğeleri çift işaretçi olan bir bellek bloğunun ilk elemanını A [0] işaret eder. Bu çift işaretçileri matrisin satırları olarak hayal edebilirsiniz. Her çift işaretçinin double türündeki num_cols öğeleri için bellek ayırmasının nedeni budur. Ayrıca A [i] i-sıraya işaret eder, yani A [i] A [i] [0] 'ya işaret eder ve bu, i-sıraya ait bellek bloğunun sadece ilk çift elemanıdır. Son olarak, i [d] ve j dördüncü sütundaki öğeye A [i] [j] ile kolayca erişebilirsiniz.
Kullanımı gösteren tam bir örnek:
#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;
}
İş için bir şey programlarken bugün çift işaretçiler kullandım, bu yüzden onları neden kullanmak zorunda olduğumuzu cevaplayabilirim (aslında ilk kez çift işaretçiler kullanmak zorunda kaldım). Bazı yapıların üyesi olan tamponlarda bulunan karelerin gerçek zamanlı kodlanmasıyla uğraşmak zorunda kaldık. Kodlayıcıda bu yapılardan birine bir işaretçi kullanmak zorunda kaldık. Sorun, işaretçimizin başka bir iş parçacığından diğer yapılara işaret edecek şekilde değiştirilmesiydi. Kodlayıcıdaki mevcut yapıyı kullanmak için, başka bir iş parçacığında değiştirilen işaretçiye işaret etmek için bir çift işaretçi kullanmak zorunda kaldım. İlk başta, en azından bizim için bu yaklaşımı benimsememiz gerektiği belli değildi. Bu süreçte çok sayıda adres basıldı :)).
Uygulamanızın diğer yerlerinde değiştirilen işaretçiler üzerinde çalışırken çift işaretçiler KULLANMALISINIZ. Ayrıca, size geri dönen ve adreslenen donanımlarla uğraşırken çift işaretçiler de bulunmalıdır.
Modifiye karşılaştır değişkeninin değerini değiştirerek karşılık işaretçi değerini :
#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;
}
Bu, işaretçi çağrılan işlevle (tekli bağlantılı listede kullanılır) değiştirildiğinde işaretçi değerini döndürmeme engel oldu.
ESKİ (kötü):
int *func(int *P)
{
...
return P;
}
int main(void)
{
int *pointer;
pointer = func(pointer);
...
}
YENİ (daha iyi):
void func(int **pointer)
{
...
}
int main(void)
{
int *pointer;
func(&pointer);
...
}
double*
.