Achtung; Der Ausdruck "Doppelzeiger" bezieht sich auch auf den Typ double*.
Keith Thompson
Beachten Sie: Die Antwort auf diese Frage ist für C und C ++ unterschiedlich. Fügen Sie dieser sehr alten Frage kein c + -Tag hinzu.
BЈовић
Antworten:
479
Wenn Sie eine Liste von Zeichen (ein Wort) haben möchten, können Sie verwenden char *word
Wenn Sie eine Liste von Wörtern (einen Satz) möchten, können Sie verwenden char **sentence
Wenn Sie eine Liste von Sätzen (einen Monolog) möchten, können Sie verwenden char ***monologue
Wenn Sie eine Liste von Monologen (eine Biografie) möchten, können Sie verwenden char ****biography
Wenn Sie eine Liste von Biografien (eine Biobibliothek) möchten, können Sie diese verwenden char *****biolibrary
Wenn Sie eine Liste von Biobibliotheken (a ?? lol) möchten, können Sie verwenden char ******lol
... ...
Ja, ich weiß, dass dies möglicherweise nicht die besten Datenstrukturen sind
Anwendungsbeispiel mit einem sehr sehr sehr langweiligen lol
#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);}
Ich wollte nur darauf hinweisen, dass a arr[a][b][c]kein a ist ***arr. Zeiger von Zeigern verwenden Referenzen von Referenzen, während arr[a][b][c]sie als übliches Array in Zeilenreihenfolge gespeichert werden.
MCCCS
170
Ein Grund ist, dass Sie den Wert des Zeigers ändern möchten, der als Funktionsargument an eine Funktion übergeben wird. Dazu benötigen Sie einen Zeiger auf einen Zeiger.
Mit einfachen Worten: Verwenden **Sie diese Option, wenn Sie die Speicherzuordnung oder -zuweisung auch außerhalb eines Funktionsaufrufs beibehalten (ODER Änderungen beibehalten) möchten. (Übergeben Sie also eine solche Funktion mit dem Doppelzeiger arg.)
Dies ist möglicherweise kein sehr gutes Beispiel, zeigt Ihnen jedoch die grundlegende Verwendung:
Was wäre anders, wenn Allokation wäre void allocate(int *p)und Sie es so nannten allocate(p)?
ス レ ッ ク ス
@ AlexanderSupertramp Ja. Der Code wird fehlerhaft sein. Bitte lesen Sie Silvius Antwort.
Abhishek
@Asha was ist der Unterschied zwischen allocate (p) und allocate (& p)?
user2979872
1
@Asha - Können wir nicht einfach den Zeiger zurückgeben? Wenn wir es für nichtig erklären müssen, was ist dann ein praktischer Anwendungsfall für dieses Szenario?
Shabirmean
91
Angenommen, Sie haben einen Zeiger. Sein Wert ist eine Adresse.
Aber jetzt möchten Sie diese Adresse ändern.
du könntest. Auf diese Weise pointer1 = pointer2geben Sie Zeiger1 die Adresse von Zeiger2.
aber! Wenn Sie dies innerhalb einer Funktion tun und möchten, dass das Ergebnis nach Abschluss der Funktion erhalten bleibt, müssen Sie zusätzliche Arbeit leisten. Sie benötigen einen neuen Zeiger3, um auf Zeiger1 zu zeigen. Übergeben Sie den Zeiger3 an die Funktion.
Hier ist ein Beispiel. Schauen Sie sich zuerst die Ausgabe unten an, um zu verstehen.
#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);}
Hier ist die Ausgabe: (zuerst lesen )
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!!!.
Dies ist eine großartige Antwort und hat mir wirklich geholfen, den Zweck und die Nützlichkeit eines Doppelzeigers zu visualisieren.
Justin
1
@ Justin hast du meine Antwort über dieser überprüft? es ist sauberer :)
Brian Joseph Spinos
10
Gute Antwort, es fehlt nur zu erklären, dass <code> void cant_change (int * x, int * z) </ code> fehlschlägt, weil seine Parameter nur neue Zeiger mit lokalem Gültigkeitsbereich sind, die ebenfalls mit a- und f-Zeigern initialisiert werden (also nicht) das gleiche wie a und f).
Pedro Reis
1
Einfach? "Ja wirklich?" ;)
alk
1
Diese Antwort erklärt wirklich eine der häufigsten Verwendungen von Zeigern auf Zeiger, danke!
Tonyjosi
48
Wenn Sie zusätzlich zu Ashas Antwort einen einzelnen Zeiger auf das Beispiel unten verwenden (z. B. alloc1 ()), verlieren Sie den Verweis auf den in der Funktion zugewiesenen Speicher.
Der Grund dafür ist, dass im alloc1Zeiger ein Wert übergeben wird. Wenn es also dem Ergebnis des mallocAufrufs innerhalb von zugewiesen wird, alloc1bezieht sich die Änderung nicht auf Code in einem anderen Bereich.
Was passiert, wenn p ein statischer Ganzzahlzeiger ist? Segmentierungsfehler erhalten.
Kapilddit
free(p)ist nicht genug, müssen Sie if(p) free(*p)auch
Shijing Lv
@ShijingLv: Nein. Wird *pmit einem intWert von 10 bewertet. Dies intan free () zu übergeben, ist eine schlechte Idee.
Alk
Die in vorgenommene Zuordnung alloc1()führt zu einem Speicherverlust. Der frei zu übergebende Zeigerwert geht durch die Rückkehr von der Funktion verloren.
Alk
Keine (!) Notwendigkeit, das Ergebnis von malloc in C.
Jetzt möchten Sie eine remove_ifFunktion implementieren , die ein Entfernungskriterium rmals eines der Argumente akzeptiert und die verknüpfte Liste durchläuft: Wenn ein Eintrag das Kriterium erfüllt (so etwas wie rm(entry)==true), wird sein Knoten aus der Liste entfernt. Am Ende wird remove_ifder Kopf (der sich möglicherweise vom ursprünglichen Kopf unterscheidet) der verknüpften Liste zurückgegeben.
Du darfst schreiben
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;}
als deine forSchleife. Die Nachricht lautet, dass Sie ohne Doppelzeiger eine prevVariable verwalten müssen, um die Zeiger neu zu organisieren und die beiden verschiedenen Fälle zu behandeln.
Aber mit Doppelzeigern können Sie tatsächlich schreiben
// 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;}
Sie brauchen kein prevJetzt, da Sie direkt ändern können, worauf prev->nextverwiesen wird .
Um die Dinge klarer zu machen, folgen wir dem Code ein wenig. Während der Entfernung:
if entry == *head: it be *head (==*curr) = *head->next- zeigt headjetzt auf den Zeiger des neuen Steuerkursknotens. Sie tun dies, indem Sie den headInhalt direkt in einen neuen Zeiger ändern .
if entry != *head: ähnlich *currist es, worauf prev->nexthingewiesen wurde und jetzt zeigt entry->next.
Unabhängig davon, in welchem Fall Sie die Zeiger mit Doppelzeigern einheitlich neu organisieren können.
1. char * ch - (als Zeichenzeiger bezeichnet)
- ch enthält die Adresse eines einzelnen Zeichens.
- (* ch) wird auf den Wert des Zeichens dereferenziert.
2. char ** ch -
'ch' enthält die Adresse eines Arrays von Zeichenzeigern. (wie in 1)
'* ch' enthält die Adresse eines einzelnen Zeichens. (Beachten Sie, dass es sich aufgrund der unterschiedlichen Deklaration von 1 unterscheidet.)
(** ch) wird auf den genauen Wert des Zeichens dereferenziert.
Durch Hinzufügen weiterer Zeiger wird die Dimension eines Datentyps von Zeichen zu Zeichenfolge, zu Array von Zeichenfolgen usw. erweitert. Sie können ihn auf eine 1d-, 2d-, 3d-Matrix beziehen.
Die Verwendung des Zeigers hängt also davon ab, wie Sie ihn deklarieren.
Hier ist ein einfacher Code ..
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. Eine weitere Anwendung von Doppelzeigern -
(dies würde auch die Referenzübergabe abdecken)
Angenommen, Sie möchten ein Zeichen aus einer Funktion aktualisieren. Wenn Sie Folgendes versuchen: -
Erweitern Sie nun diese Anforderung zum Aktualisieren einer Zeichenfolge anstelle eines Zeichens.
Dazu müssen Sie den Parameter in der Funktion als Doppelzeiger erhalten.
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);}
In diesem Beispiel erwartet die Methode einen Doppelzeiger als Parameter, um den Wert einer Zeichenfolge zu aktualisieren.
#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; } Sie können dies aber auch ohne Doppelzeiger tun.
Kumar
" char ** ch - 'ch' enthält die Adresse eines Arrays von Zeichenzeigern. " Nein, es enthält die Adresse des ersten Elements eines Arrays von charZeigern. Ein Zeiger auf ein Array von char*würde beispielsweise wie folgt eingegeben: char(*(*p)[42])Definiert pals Zeiger auf ein Array von 42 Zeiger auf char.
Alk
Das letzte Snippet ist komplett kaputt. Für den Anfang: Hier *str = ...strwird dereferenziert, nicht initialisiert, und undefiniertes Verhalten aufgerufen.
Alk
Dies malloc(sizeof(char) * 10);reserviert keinen Platz für 10 Zeiger auf, charsondern nur für 10 char..
alk
In dieser Schleife wird for(i=0;i<10;i++) { str = ... der Index nicht verwendet i.
Alk
17
Zeiger auf Zeiger sind auch nützlich als "Handles" für den Speicher, bei denen Sie ein "Handle" zwischen Funktionen an den neu lokalisierbaren Speicher weitergeben möchten. Dies bedeutet im Grunde, dass die Funktion den Speicher ändern kann, auf den der Zeiger in der Handle-Variablen zeigt, und jede Funktion oder jedes Objekt, das das Handle verwendet, ordnungsgemäß auf den neu verschobenen (oder zugewiesenen) Speicher verweist. Bibliotheken tun dies gerne mit "undurchsichtigen" Datentypen, dh Datentypen, bei denen Sie sich keine Gedanken darüber machen müssen, was sie mit dem Speicher tun, auf den gezeigt wird. Sie geben einfach das "Handle" zwischen den Datentypen weiter Funktionen der Bibliothek, um einige Operationen an diesem Speicher auszuführen ...
Zum Beispiel:
#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;}
Was ist der Grund dafür, dass der Handle-Typ ein Zeichen ohne Vorzeichen ist **? Würde void ** genauso gut funktionieren?
Connor Clark
5
unsigned charwird speziell verwendet, weil wir einen Zeiger auf Binärdaten speichern, die als Rohbytes dargestellt werden. Die Verwendung voiderfordert irgendwann eine Besetzung und ist im Allgemeinen nicht so lesbar wie die Absicht, was getan wird.
Jason
7
Ein einfaches Beispiel, das Sie wahrscheinlich schon oft gesehen haben
int main(int argc,char**argv)
Im zweiten Parameter haben Sie es: Zeiger auf Zeiger auf Zeichen.
Beachten Sie, dass die Zeigernotation ( char* c) und die Arraynotation ( char c[]) in Funktionsargumenten austauschbar sind. Du könntest also auch schreiben char *argv[]. Mit anderen Worten char *argv[]und char **argvsind austauschbar.
Was das Obige darstellt, ist in der Tat ein Array von Zeichenfolgen (die Befehlszeilenargumente, die einem Programm beim Start gegeben werden).
Siehe auch diese Antwort für weitere Details zur obigen Funktionssignatur.
"Zeigernotation ( char* c) und Array-Notation ( char c[]) sind austauschbar" (und haben dieselbe exakte Bedeutung) in Funktionsargumenten . Sie unterscheiden sich jedoch außerhalb von Funktionsargumenten.
PMG
6
Strings sind ein gutes Beispiel für die Verwendung von Doppelzeigern. Die Zeichenfolge selbst ist ein Zeiger. Wenn Sie also auf eine Zeichenfolge zeigen müssen, benötigen Sie einen Doppelzeiger.
Guter Punkt, Signalverlust zwischen Gehirn und Tastatur. Ich habe es bearbeitet, um ein bisschen mehr Sinn zu machen.
Jeff Foster
Warum können wir nicht Folgendes tun ... void safeFree (void * memory) {if (memory) {free (memory); Speicher = NULL; }}
Peter_pk
@Peter_pk Das Zuweisen von Speicher zu Null würde nicht helfen, da Sie einen Zeiger nach Wert und nicht nach Referenz übergeben haben (daher das Beispiel eines Zeigers auf einen Zeiger).
Jeff Foster
2
Zum Beispiel, wenn Sie zufälligen Zugriff auf nicht zusammenhängende Daten wünschen.
p ->[p0, p1, p2,...]
p0 -> data1
p1 -> data2
- in C.
T ** p =(T **) malloc(sizeof(T*)* n);
p[0]=(T*) malloc(sizeof(T));
p[1]=(T*) malloc(sizeof(T));
Sie speichern einen Zeiger p, der auf ein Array von Zeigern zeigt. Jeder Zeiger zeigt auf ein Datenelement.
Wenn sizeof(T)es groß ist, ist es möglicherweise nicht möglich, einen zusammenhängenden Block (dh mit malloc) von sizeof(T) * nBytes zuzuweisen .
Keine (!) Notwendigkeit, das Ergebnis von Malloc in C.
Alk
2
Eine Sache, für die ich sie ständig benutze, ist, wenn ich eine Reihe von Objekten habe und sie anhand verschiedener Felder nachschlagen (binäre Suche) muss.
Ich behalte das ursprüngliche Array ...
int num_objects;
OBJECT *original_array = malloc(sizeof(OBJECT)*num_objects);
Erstellen Sie dann ein Array sortierter Zeiger auf die Objekte.
Sie können so viele sortierte Zeigerarrays erstellen, wie Sie benötigen, und dann eine binäre Suche im sortierten Zeigerarray verwenden, um auf das Objekt zuzugreifen, das Sie mit den vorhandenen Daten benötigen. Das ursprüngliche Array von Objekten kann unsortiert bleiben, aber jedes Zeigerarray wird nach dem angegebenen Feld sortiert.
Ziel ist es, mithilfe einer Funktion zu ändern, worauf studentA zeigt.
#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!)
*/
Keine (!) Notwendigkeit, das Ergebnis von malloc in C.
alk
2
Das folgende einfache C ++ - Beispiel zeigt, dass Sie einen Zeiger auf einen Zeiger benötigen , wenn Sie eine Funktion zum Setzen eines Zeigers auf ein Objekt verwenden möchten . Andernfalls wird der Zeiger immer wieder auf Null zurückgesetzt .
(Eine C ++ - Antwort, aber ich glaube, dass es in C dasselbe ist.)
(Als Referenz: Google ("Übergeben des Werts c ++") = "Standardmäßig werden Argumente in C ++ als Wert übergeben. Wenn ein Argument als Wert übergeben wird, wird der Wert des Arguments in den Parameter der Funktion kopiert.")
Wir wollen also den Zeiger bgleich dem String setzen a.
Der "Wert" von &main::a(einer Adresse) wird in den Parameter kopiert std::string* Function_1::a. Daher Function_1::aist ein Zeiger auf (dh die Speicheradresse von) der Zeichenfolge main::a.
Der "Wert" von main::b(eine Adresse im Speicher) wird in den Parameter kopiert std::string* Function_1::b. Daher befinden sich jetzt 2 dieser Adressen im Speicher, beide Nullzeiger. In der Zeile b = a;wird die lokale Variable Function_1::bdann in gleich Function_1::a(= &main::a) geändert , die Variable main::bbleibt jedoch unverändert. Nach dem Aufruf Function_1, main::bist immer noch ein Null - Zeiger.
Was passiert an der Leitung Function_2(&a, &b);?
Die Behandlung der aVariablen ist dieselbe: Innerhalb der Funktion Function_2::abefindet sich die Adresse der Zeichenfolge main::a.
Die Variable bwird jetzt jedoch als Zeiger auf einen Zeiger übergeben. Der "Wert" von &main::b(die Adresse des Zeigersmain::b ) wird kopiert std::string** Function_2::b. Daher wird in Function_2 eine Dereferenzierung vorgenommen, um darauf *Function_2::bzuzugreifen und Änderungen vorzunehmen main::b. Die Zeile *b = a;setzt also tatsächlich main::b(eine Adresse) gleich Function_2::a(= Adresse von main::a), was wir wollen.
Wenn Sie eine Funktion zum Ändern eines Objekts verwenden möchten, sei es ein Objekt oder eine Adresse (Zeiger), müssen Sie einen Zeiger auf dieses Objekt übergeben. Das Objekt, das Sie tatsächlich übergeben, kann nicht geändert werden (im aufrufenden Bereich), da eine lokale Kopie erstellt wird.
(Eine Ausnahme ist, wenn der Parameter eine Referenz ist, wie z std::string& a. B.. In der Regel sind dies jedoch const. Wenn Sie ein Objekt aufrufen f(x), xsollten Sie im Allgemeinen davon ausgehen können, dass fes sich nicht ändert x. Wenn xes sich jedoch um einen Zeiger handelt, sollten Sie dies tun Angenommen, dies fkönnte das Objekt ändern, auf das von gezeigt wird x.)
C ++ - Code zur Beantwortung einer C-Frage ist nicht die beste Idee.
Alk
1
Ein bisschen zu spät zur Party, aber hoffentlich hilft das jemandem.
In C-Arrays wird immer Speicher auf dem Stapel zugewiesen, daher kann eine Funktion kein (nicht statisches) Array zurückgeben, da der auf dem Stapel zugewiesene Speicher automatisch freigegeben wird, wenn die Ausführung das Ende des aktuellen Blocks erreicht. Das ist wirklich ärgerlich, wenn Sie sich mit zweidimensionalen Arrays (dh Matrizen) befassen und einige Funktionen implementieren möchten, die Matrizen ändern und zurückgeben können. Um dies zu erreichen, können Sie einen Zeiger zu Zeiger verwenden, um eine Matrix mit dynamisch zugewiesenem Speicher zu implementieren:
/* 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;}
Der Doppelzeiger-zu-Doppelzeiger A zeigt auf das erste Element A [0] eines Speicherblocks, dessen Elemente selbst Doppelzeiger sind. Sie können sich diese Doppelzeiger als Zeilen der Matrix vorstellen. Aus diesem Grund reserviert jeder Doppelzeiger Speicher für num_cols-Elemente vom Typ double. Außerdem zeigt A [i] auf die i-te Zeile, dh A [i] zeigt auf A [i] [0] und das ist nur das erste Doppelelement des Speicherblocks für die i-te Zeile. Schließlich können Sie mit A [i] [j] einfach auf das Element in der i-ten Zeile und j-ten Spalte zugreifen.
Hier ist ein vollständiges Beispiel, das die Verwendung demonstriert:
#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;}
Ich habe heute Doppelzeiger verwendet, als ich etwas für die Arbeit programmiert habe, damit ich antworten kann, warum wir sie verwenden mussten (es ist das erste Mal, dass ich tatsächlich Doppelzeiger verwenden musste). Wir mussten uns mit der Echtzeitcodierung von Frames befassen, die in Puffern enthalten sind, die Mitglieder einiger Strukturen sind. Im Encoder mussten wir einen Zeiger auf eine dieser Strukturen verwenden. Das Problem war, dass unser Zeiger geändert wurde, um auf andere Strukturen eines anderen Threads zu verweisen. Um die aktuelle Struktur im Encoder zu verwenden, musste ich einen Doppelzeiger verwenden, um auf den Zeiger zu zeigen, der in einem anderen Thread geändert wurde. Zumindest für uns war zunächst nicht klar, dass wir diesen Ansatz wählen mussten. Dabei wurden viele Adressen gedruckt :)).
Sie sollten doppelte Zeiger verwenden, wenn Sie an Zeigern arbeiten, die an anderen Stellen Ihrer Anwendung geändert wurden. Möglicherweise sind Doppelzeiger auch ein Muss, wenn Sie sich mit Hardware befassen, die zurückgibt und an Sie adressiert.
Vergleichen Sie den Änderungswert der Variablen mit dem Änderungswert des Zeigers :
#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;}
Dies hat mir geholfen, die Rückgabe des Zeigerwerts zu vermeiden, wenn der Zeiger durch die aufgerufene Funktion (die in einer einfach verknüpften Liste verwendet wird) geändert wurde.
double*
.Antworten:
Wenn Sie eine Liste von Zeichen (ein Wort) haben möchten, können Sie verwenden
char *word
Wenn Sie eine Liste von Wörtern (einen Satz) möchten, können Sie verwenden
char **sentence
Wenn Sie eine Liste von Sätzen (einen Monolog) möchten, können Sie verwenden
char ***monologue
Wenn Sie eine Liste von Monologen (eine Biografie) möchten, können Sie verwenden
char ****biography
Wenn Sie eine Liste von Biografien (eine Biobibliothek) möchten, können Sie diese verwenden
char *****biolibrary
Wenn Sie eine Liste von Biobibliotheken (a ?? lol) möchten, können Sie verwenden
char ******lol
... ...
Ja, ich weiß, dass dies möglicherweise nicht die besten Datenstrukturen sind
Anwendungsbeispiel mit einem sehr sehr sehr langweiligen lol
Ausgabe:
quelle
arr[a][b][c]
kein a ist***arr
. Zeiger von Zeigern verwenden Referenzen von Referenzen, währendarr[a][b][c]
sie als übliches Array in Zeilenreihenfolge gespeichert werden.Ein Grund ist, dass Sie den Wert des Zeigers ändern möchten, der als Funktionsargument an eine Funktion übergeben wird. Dazu benötigen Sie einen Zeiger auf einen Zeiger.
Mit einfachen Worten: Verwenden
**
Sie diese Option, wenn Sie die Speicherzuordnung oder -zuweisung auch außerhalb eines Funktionsaufrufs beibehalten (ODER Änderungen beibehalten) möchten. (Übergeben Sie also eine solche Funktion mit dem Doppelzeiger arg.)Dies ist möglicherweise kein sehr gutes Beispiel, zeigt Ihnen jedoch die grundlegende Verwendung:
quelle
void allocate(int *p)
und Sie es so nanntenallocate(p)
?pointer1 = pointer2
geben Sie Zeiger1 die Adresse von Zeiger2.aber! Wenn Sie dies innerhalb einer Funktion tun und möchten, dass das Ergebnis nach Abschluss der Funktion erhalten bleibt, müssen Sie zusätzliche Arbeit leisten. Sie benötigen einen neuen Zeiger3, um auf Zeiger1 zu zeigen. Übergeben Sie den Zeiger3 an die Funktion.
Hier ist ein Beispiel. Schauen Sie sich zuerst die Ausgabe unten an, um zu verstehen.
Hier ist die Ausgabe: (zuerst lesen )
quelle
Wenn Sie zusätzlich zu Ashas Antwort einen einzelnen Zeiger auf das Beispiel unten verwenden (z. B. alloc1 ()), verlieren Sie den Verweis auf den in der Funktion zugewiesenen Speicher.
Der Grund dafür ist, dass im
alloc1
Zeiger ein Wert übergeben wird. Wenn es also dem Ergebnis desmalloc
Aufrufs innerhalb von zugewiesen wird,alloc1
bezieht sich die Änderung nicht auf Code in einem anderen Bereich.quelle
free(p)
ist nicht genug, müssen Sieif(p) free(*p)
auch*p
mit einemint
Wert von 10 bewertet. Diesint
an free () zu übergeben, ist eine schlechte Idee.alloc1()
führt zu einem Speicherverlust. Der frei zu übergebende Zeigerwert geht durch die Rückkehr von der Funktion verloren.Ich habe heute ein sehr gutes Beispiel aus diesem Blog-Beitrag gesehen , wie ich unten zusammenfasse.
Stellen Sie sich vor, Sie haben eine Struktur für Knoten in einer verknüpften Liste, was wahrscheinlich der Fall ist
Jetzt möchten Sie eine
remove_if
Funktion implementieren , die ein Entfernungskriteriumrm
als eines der Argumente akzeptiert und die verknüpfte Liste durchläuft: Wenn ein Eintrag das Kriterium erfüllt (so etwas wierm(entry)==true
), wird sein Knoten aus der Liste entfernt. Am Ende wirdremove_if
der Kopf (der sich möglicherweise vom ursprünglichen Kopf unterscheidet) der verknüpften Liste zurückgegeben.Du darfst schreiben
als deine
for
Schleife. Die Nachricht lautet, dass Sie ohne Doppelzeiger eineprev
Variable verwalten müssen, um die Zeiger neu zu organisieren und die beiden verschiedenen Fälle zu behandeln.Aber mit Doppelzeigern können Sie tatsächlich schreiben
Sie brauchen kein
prev
Jetzt, da Sie direkt ändern können, woraufprev->next
verwiesen wird .Um die Dinge klarer zu machen, folgen wir dem Code ein wenig. Während der Entfernung:
entry == *head
: it be*head (==*curr) = *head->next
- zeigthead
jetzt auf den Zeiger des neuen Steuerkursknotens. Sie tun dies, indem Sie denhead
Inhalt direkt in einen neuen Zeiger ändern .entry != *head
: ähnlich*curr
ist es, woraufprev->next
hingewiesen wurde und jetzt zeigtentry->next
.Unabhängig davon, in welchem Fall Sie die Zeiger mit Doppelzeigern einheitlich neu organisieren können.
quelle
1. Grundkonzept -
Wenn Sie wie folgt deklarieren: -
1. char * ch - (als Zeichenzeiger bezeichnet)
- ch enthält die Adresse eines einzelnen Zeichens.
- (* ch) wird auf den Wert des Zeichens dereferenziert.
2. char ** ch -
'ch' enthält die Adresse eines Arrays von Zeichenzeigern. (wie in 1)
'* ch' enthält die Adresse eines einzelnen Zeichens. (Beachten Sie, dass es sich aufgrund der unterschiedlichen Deklaration von 1 unterscheidet.)
(** ch) wird auf den genauen Wert des Zeichens dereferenziert.
Durch Hinzufügen weiterer Zeiger wird die Dimension eines Datentyps von Zeichen zu Zeichenfolge, zu Array von Zeichenfolgen usw. erweitert. Sie können ihn auf eine 1d-, 2d-, 3d-Matrix beziehen.
Die Verwendung des Zeigers hängt also davon ab, wie Sie ihn deklarieren.
Hier ist ein einfacher Code ..
2. Eine weitere Anwendung von Doppelzeigern -
(dies würde auch die Referenzübergabe abdecken)
Angenommen, Sie möchten ein Zeichen aus einer Funktion aktualisieren. Wenn Sie Folgendes versuchen: -
Die Ausgabe ist AA. Dies funktioniert nicht, da Sie "Passed By Value" an die Funktion übergeben haben.
Der richtige Weg, dies zu tun, wäre -
Erweitern Sie nun diese Anforderung zum Aktualisieren einer Zeichenfolge anstelle eines Zeichens.
Dazu müssen Sie den Parameter in der Funktion als Doppelzeiger erhalten.
In diesem Beispiel erwartet die Methode einen Doppelzeiger als Parameter, um den Wert einer Zeichenfolge zu aktualisieren.
quelle
#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; }
Sie können dies aber auch ohne Doppelzeiger tun.char
Zeigern. Ein Zeiger auf ein Array vonchar*
würde beispielsweise wie folgt eingegeben:char(*(*p)[42])
Definiertp
als Zeiger auf ein Array von 42 Zeiger aufchar
.*str = ...
str
wird dereferenziert, nicht initialisiert, und undefiniertes Verhalten aufgerufen.malloc(sizeof(char) * 10);
reserviert keinen Platz für 10 Zeiger auf,char
sondern nur für 10char
..for(i=0;i<10;i++) { str = ...
der Index nicht verwendeti
.Zeiger auf Zeiger sind auch nützlich als "Handles" für den Speicher, bei denen Sie ein "Handle" zwischen Funktionen an den neu lokalisierbaren Speicher weitergeben möchten. Dies bedeutet im Grunde, dass die Funktion den Speicher ändern kann, auf den der Zeiger in der Handle-Variablen zeigt, und jede Funktion oder jedes Objekt, das das Handle verwendet, ordnungsgemäß auf den neu verschobenen (oder zugewiesenen) Speicher verweist. Bibliotheken tun dies gerne mit "undurchsichtigen" Datentypen, dh Datentypen, bei denen Sie sich keine Gedanken darüber machen müssen, was sie mit dem Speicher tun, auf den gezeigt wird. Sie geben einfach das "Handle" zwischen den Datentypen weiter Funktionen der Bibliothek, um einige Operationen an diesem Speicher auszuführen ...
Zum Beispiel:
Hoffe das hilft,
Jason
quelle
unsigned char
wird speziell verwendet, weil wir einen Zeiger auf Binärdaten speichern, die als Rohbytes dargestellt werden. Die Verwendungvoid
erfordert irgendwann eine Besetzung und ist im Allgemeinen nicht so lesbar wie die Absicht, was getan wird.Ein einfaches Beispiel, das Sie wahrscheinlich schon oft gesehen haben
Im zweiten Parameter haben Sie es: Zeiger auf Zeiger auf Zeichen.
Beachten Sie, dass die Zeigernotation (
char* c
) und die Arraynotation (char c[]
) in Funktionsargumenten austauschbar sind. Du könntest also auch schreibenchar *argv[]
. Mit anderen Wortenchar *argv[]
undchar **argv
sind austauschbar.Was das Obige darstellt, ist in der Tat ein Array von Zeichenfolgen (die Befehlszeilenargumente, die einem Programm beim Start gegeben werden).
Siehe auch diese Antwort für weitere Details zur obigen Funktionssignatur.
quelle
char* c
) und Array-Notation (char c[]
) sind austauschbar" (und haben dieselbe exakte Bedeutung) in Funktionsargumenten . Sie unterscheiden sich jedoch außerhalb von Funktionsargumenten.Strings sind ein gutes Beispiel für die Verwendung von Doppelzeigern. Die Zeichenfolge selbst ist ein Zeiger. Wenn Sie also auf eine Zeichenfolge zeigen müssen, benötigen Sie einen Doppelzeiger.
quelle
Beispielsweise möchten Sie möglicherweise sicherstellen, dass Sie den Zeiger anschließend auf null setzen, wenn Sie den Speicher von etwas freigeben.
Wenn Sie diese Funktion aufrufen, rufen Sie sie mit der Adresse eines Zeigers auf
Jetzt
myMemory
ist NULL eingestellt und jeder Versuch, es wiederzuverwenden, ist offensichtlich falsch.quelle
if(*memory)
undfree(*memory);
Zum Beispiel, wenn Sie zufälligen Zugriff auf nicht zusammenhängende Daten wünschen.
- in C.
Sie speichern einen Zeiger
p
, der auf ein Array von Zeigern zeigt. Jeder Zeiger zeigt auf ein Datenelement.Wenn
sizeof(T)
es groß ist, ist es möglicherweise nicht möglich, einen zusammenhängenden Block (dh mit malloc) vonsizeof(T) * n
Bytes zuzuweisen .quelle
Eine Sache, für die ich sie ständig benutze, ist, wenn ich eine Reihe von Objekten habe und sie anhand verschiedener Felder nachschlagen (binäre Suche) muss.
Ich behalte das ursprüngliche Array ...
Erstellen Sie dann ein Array sortierter Zeiger auf die Objekte.
Sie können so viele sortierte Zeigerarrays erstellen, wie Sie benötigen, und dann eine binäre Suche im sortierten Zeigerarray verwenden, um auf das Objekt zuzugreifen, das Sie mit den vorhandenen Daten benötigen. Das ursprüngliche Array von Objekten kann unsortiert bleiben, aber jedes Zeigerarray wird nach dem angegebenen Feld sortiert.
quelle
Warum Doppelzeiger?
Ziel ist es, mithilfe einer Funktion zu ändern, worauf studentA zeigt.
quelle
Das folgende einfache C ++ - Beispiel zeigt, dass Sie einen Zeiger auf einen Zeiger benötigen , wenn Sie eine Funktion zum Setzen eines Zeigers auf ein Objekt verwenden möchten . Andernfalls wird der Zeiger immer wieder auf Null zurückgesetzt .
(Eine C ++ - Antwort, aber ich glaube, dass es in C dasselbe ist.)
(Als Referenz: Google ("Übergeben des Werts c ++") = "Standardmäßig werden Argumente in C ++ als Wert übergeben. Wenn ein Argument als Wert übergeben wird, wird der Wert des Arguments in den Parameter der Funktion kopiert.")
Wir wollen also den Zeiger
b
gleich dem String setzena
.Was passiert an der Leitung
Function_1(&a, b);
?Der "Wert" von
&main::a
(einer Adresse) wird in den Parameter kopiertstd::string* Function_1::a
. DaherFunction_1::a
ist ein Zeiger auf (dh die Speicheradresse von) der Zeichenfolgemain::a
.Der "Wert" von
main::b
(eine Adresse im Speicher) wird in den Parameter kopiertstd::string* Function_1::b
. Daher befinden sich jetzt 2 dieser Adressen im Speicher, beide Nullzeiger. In der Zeileb = a;
wird die lokale VariableFunction_1::b
dann in gleichFunction_1::a
(=&main::a
) geändert , die Variablemain::b
bleibt jedoch unverändert. Nach dem AufrufFunction_1
,main::b
ist immer noch ein Null - Zeiger.Was passiert an der Leitung
Function_2(&a, &b);
?Die Behandlung der
a
Variablen ist dieselbe: Innerhalb der FunktionFunction_2::a
befindet sich die Adresse der Zeichenfolgemain::a
.Die Variable
b
wird jetzt jedoch als Zeiger auf einen Zeiger übergeben. Der "Wert" von&main::b
(die Adresse des Zeigersmain::b
) wird kopiertstd::string** Function_2::b
. Daher wird in Function_2 eine Dereferenzierung vorgenommen, um darauf*Function_2::b
zuzugreifen und Änderungen vorzunehmenmain::b
. Die Zeile*b = a;
setzt also tatsächlichmain::b
(eine Adresse) gleichFunction_2::a
(= Adresse vonmain::a
), was wir wollen.Wenn Sie eine Funktion zum Ändern eines Objekts verwenden möchten, sei es ein Objekt oder eine Adresse (Zeiger), müssen Sie einen Zeiger auf dieses Objekt übergeben. Das Objekt, das Sie tatsächlich übergeben, kann nicht geändert werden (im aufrufenden Bereich), da eine lokale Kopie erstellt wird.
(Eine Ausnahme ist, wenn der Parameter eine Referenz ist, wie z
std::string& a
. B.. In der Regel sind dies jedochconst
. Wenn Sie ein Objekt aufrufenf(x)
,x
sollten Sie im Allgemeinen davon ausgehen können, dassf
es sich nicht ändertx
. Wennx
es sich jedoch um einen Zeiger handelt, sollten Sie dies tun Angenommen, diesf
könnte das Objekt ändern, auf das von gezeigt wirdx
.)quelle
Ein bisschen zu spät zur Party, aber hoffentlich hilft das jemandem.
In C-Arrays wird immer Speicher auf dem Stapel zugewiesen, daher kann eine Funktion kein (nicht statisches) Array zurückgeben, da der auf dem Stapel zugewiesene Speicher automatisch freigegeben wird, wenn die Ausführung das Ende des aktuellen Blocks erreicht. Das ist wirklich ärgerlich, wenn Sie sich mit zweidimensionalen Arrays (dh Matrizen) befassen und einige Funktionen implementieren möchten, die Matrizen ändern und zurückgeben können. Um dies zu erreichen, können Sie einen Zeiger zu Zeiger verwenden, um eine Matrix mit dynamisch zugewiesenem Speicher zu implementieren:
Hier ist eine Illustration:
Der Doppelzeiger-zu-Doppelzeiger A zeigt auf das erste Element A [0] eines Speicherblocks, dessen Elemente selbst Doppelzeiger sind. Sie können sich diese Doppelzeiger als Zeilen der Matrix vorstellen. Aus diesem Grund reserviert jeder Doppelzeiger Speicher für num_cols-Elemente vom Typ double. Außerdem zeigt A [i] auf die i-te Zeile, dh A [i] zeigt auf A [i] [0] und das ist nur das erste Doppelelement des Speicherblocks für die i-te Zeile. Schließlich können Sie mit A [i] [j] einfach auf das Element in der i-ten Zeile und j-ten Spalte zugreifen.
Hier ist ein vollständiges Beispiel, das die Verwendung demonstriert:
quelle
Ich habe heute Doppelzeiger verwendet, als ich etwas für die Arbeit programmiert habe, damit ich antworten kann, warum wir sie verwenden mussten (es ist das erste Mal, dass ich tatsächlich Doppelzeiger verwenden musste). Wir mussten uns mit der Echtzeitcodierung von Frames befassen, die in Puffern enthalten sind, die Mitglieder einiger Strukturen sind. Im Encoder mussten wir einen Zeiger auf eine dieser Strukturen verwenden. Das Problem war, dass unser Zeiger geändert wurde, um auf andere Strukturen eines anderen Threads zu verweisen. Um die aktuelle Struktur im Encoder zu verwenden, musste ich einen Doppelzeiger verwenden, um auf den Zeiger zu zeigen, der in einem anderen Thread geändert wurde. Zumindest für uns war zunächst nicht klar, dass wir diesen Ansatz wählen mussten. Dabei wurden viele Adressen gedruckt :)).
Sie sollten doppelte Zeiger verwenden, wenn Sie an Zeigern arbeiten, die an anderen Stellen Ihrer Anwendung geändert wurden. Möglicherweise sind Doppelzeiger auch ein Muss, wenn Sie sich mit Hardware befassen, die zurückgibt und an Sie adressiert.
quelle
Vergleichen Sie den Änderungswert der Variablen mit dem Änderungswert des Zeigers :
Dies hat mir geholfen, die Rückgabe des Zeigerwerts zu vermeiden, wenn der Zeiger durch die aufgerufene Funktion (die in einer einfach verknüpften Liste verwendet wird) geändert wurde.
ALT (schlecht):
NEU (besser):
quelle