web-dev-qa-db-de.com

Referenzübergabe in C

Wenn C die Übergabe einer Variablen als Referenz nicht unterstützt, warum funktioniert das?

#include <stdio.h>

void f(int *j) {
  (*j)++;
}

int main() {
  int i = 20;
  int *p = &i;
  f(p);
  printf("i = %d\n", i);

  return 0;
}

Ausgabe

 $ gcc -std = c99 test.c 
 $ a.exe 
 i = 21 
193
aks

Weil Sie den Wert des Zeigers auf die Methode übergeben und ihn dann dereferenzieren, um die Ganzzahl zu erhalten, auf die verwiesen wird.

305
tvanfosson

Das ist keine Referenz, das ist ein Wert, wie andere angegeben haben.

Die C-Sprache ist ausnahmslos ein Pass-by-Value. Das Übergeben eines Zeigers als Parameter bedeutet nicht das Übergeben eines Verweises.

Die Regel ist die folgende:

Eine Funktion kann den aktuellen Parameterwert nicht ändern.


Lassen Sie uns versuchen, die Unterschiede zwischen Skalar- und Zeigerparametern einer Funktion zu erkennen.

Skalare Variablen

In diesem kurzen Programm wird die Wertübergabe mithilfe einer skalaren Variablen angezeigt. param heißt der Formalparameter und variable beim Funktionsaufruf heißt Aktualparameter. Hinweis: Das Inkrementieren von param in der Funktion ändert variable nicht.

#include <stdio.h>

void function(int param) {
    printf("I've received value %d\n", param);
    param++;
}

int main(void) {
    int variable = 111;

    function(variable);
    printf("variable %d\m", variable);
    return 0;
}

Das Ergebnis ist

I've received value 111
variable=111

Illusion der Referenzübergabe

Wir ändern den Code ein wenig. param ist jetzt ein Zeiger.

#include <stdio.h>

void function2(int *param) {
    printf("I've received value %d\n", *param);
    (*param)++;
}

int main(void) {
    int variable = 111;

    function2(&variable);
    printf("variable %d\n", variable);
    return 0;
}

Das Ergebnis ist

I've received value 111
variable=112

Das lässt Sie glauben, dass der Parameter als Referenz übergeben wurde. Es war nicht. Es wurde als Wert übergeben, wobei der Parameterwert eine Adresse ist. Der Wert für den Int-Typ wurde inkrementiert. Dies ist der Nebeneffekt, der uns den Eindruck erweckt, dass es sich um einen Funktionsaufruf zur Referenzübergabe handelt.

Zeiger - als Wert übergeben

Wie können wir diese Tatsache zeigen/beweisen? Nun, vielleicht können wir das erste Beispiel für Skalarvariablen ausprobieren, aber anstelle von Skalar verwenden wir Adressen (Zeiger). Mal sehen, ob das helfen kann.

#include <stdio.h>

void function2(int *param) {
    printf("param's address %d\n", param);
    param = NULL;
}

int main(void) {
    int variable = 111;
    int *ptr = &variable;

    function2(ptr);
    printf("ptr's address %d\n", ptr);
    return 0;
}

Das Ergebnis ist, dass die beiden Adressen gleich sind (machen Sie sich keine Gedanken über den genauen Wert).

Beispielergebnis:

param's address -1846583468
ptr's address -1846583468

Meiner Meinung nach ist dies ein klarer Beweis dafür, dass Zeiger als Wert übergeben werden. Andernfalls wäre ptr nach dem Funktionsaufruf NULL.

105
Ely

In C wird die Referenzübergabe simuliert, indem die Adresse einer Variablen (ein Zeiger) übergeben und diese Adresse in der Funktion dereferenziert wird, um die tatsächliche Variable zu lesen oder zu schreiben. Dies wird als "C-Stil-Referenzübergabe" bezeichnet.

Quelle: www-cs-students.stanford.ed

69
user195488

Weil der obige Code keine Referenz enthält. Die Verwendung von Zeigern (z. B. void func(int* p)) ist die Übergabe von Adressen. Dies ist eine Referenzübergabe in C++ (funktioniert in C nicht):

void func(int& ref) {ref = 4;}

...
int a;
func(a);
// a is 4 now
48

Ihr Beispiel funktioniert, weil Sie die Adresse Ihrer Variablen an eine Funktion übergeben, die ihren Wert mit dem Operator Dereferenzierung manipuliert.

Obwohl C Referenzdatentypen nicht unterstützt, können Sie die Referenzübergabe dennoch simulieren, indem Sie explizit Zeigerwerte übergeben, wie in Ihrem Beispiel.

Der C++ - Referenzdatentyp ist weniger leistungsfähig, wird aber als sicherer angesehen als der von C geerbte Zeigertyp. Dies wäre Ihr Beispiel, das an die Verwendung von C++ - Referenzen angepasst ist:

void f(int &j) {
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}
27
Daniel Vassallo

Sie übergeben ein Zeiger (Adressposition) nach Wert.

Es ist so, als würde man sagen "Hier ist der Ort mit den Daten, die ich aktualisieren möchte."

12
antony.trupe

p ist eine Zeigervariable. Sein Wert ist die Adresse von i. Wenn Sie f aufrufen, übergeben Sie den Wert von p, was die Adresse von i ist.

6
Anssi

In C ist alles ein Pass-by-Wert. Die Verwendung von Zeigern gibt uns die Illusion, dass wir als Referenz übergeben, weil sich Wert der Variablen ändert. Wenn Sie jedoch die Adresse der Zeigervariablen ausdrucken, werden Sie feststellen, dass sie nicht betroffen ist. Ein Kopie des Wert der Adresse wird an die Funktion übergeben. Unten sehen Sie einen Ausschnitt, der dies veranschaulicht.

void add_number(int *a) {
    *a = *a + 2;
}

int main(int argc, char *argv[]) {
   int a = 2;

   printf("before pass by reference, a == %i\n", a);
   add_number(&a);
   printf("after  pass by reference, a == %i\n", a);

   printf("before pass by reference, a == %p\n", &a);
   add_number(&a);
   printf("after  pass by reference, a == %p\n", &a);

}

before pass by reference, a == 2
after  pass by reference, a == 4
before pass by reference, a == 0x7fff5cf417ec
after  pass by reference, a == 0x7fff5cf417ec
5
Arun Suresh

Keine Referenzübergabe in C, aber p bezieht sich auf i, und Sie übergeben p als Wert.

5

Weil Sie einen Zeiger (Speicheradresse) auf die Variable p in die Funktion f übergeben. Mit anderen Worten, Sie übergeben einen Zeiger, keinen Verweis.

4
dave

Kurze Antwort: Ja, C implementiert die Übergabe von Parametern als Referenz mithilfe von Zeigern.

Bei der Implementierung der Parameterübergabe verwenden Designer von Programmiersprachen drei verschiedene Strategien (oder semantische Modelle): Daten in das Unterprogramm übertragen, Daten aus dem Unterprogramm empfangen oder beides. Diese Modelle werden üblicherweise entsprechend als In-Modus, Out-Modus und In-Out-Modus bezeichnet.

Mehrere Modelle wurden von Sprachdesignern entwickelt, um diese drei elementaren Parameterübergabestrategien zu implementieren:

Pass-by-Value (In-Mode-Semantik) Pass-by-Result (Out-Mode-Semantik) Pass-by-Value-Result (Inout-Mode-Semantik) Pass-by-Reference (Inout-Mode-Semantik) Pass-by-Name (Inout-Mode) Semantik)

Die Referenzübergabe ist die zweite Technik für die Übergabe von Parametern im In-Out-Modus. Anstatt Daten zwischen der Hauptroutine und dem Unterprogramm hin und her zu kopieren, sendet das Laufzeitsystem einen direkten Zugriffspfad auf die Daten für das Unterprogramm. Bei dieser Strategie hat das Unterprogramm direkten Zugriff auf die Daten, wobei die Daten effektiv mit der Hauptroutine geteilt werden. Der Hauptvorteil dieser Technik besteht darin, dass sie absolut effizient in Bezug auf Zeit und Raum ist, da kein Raum dupliziert werden muss und keine Datenkopiervorgänge erforderlich sind.

Die Implementierung der Parameterübergabe in C: C implementiert die Semantik der Wertübergabe und der Referenzübergabe (Inout-Modus) unter Verwendung von Zeigern als Parameter. Der Zeiger wird an das Unterprogramm gesendet und es werden überhaupt keine tatsächlichen Daten kopiert. Da jedoch ein Zeiger ein Zugriffspfad auf die Daten der Hauptroutine ist, kann das Unterprogramm die Daten in der Hauptroutine ändern. C hat diese Methode von ALGOL68 übernommen.

Implementierung der Parameterübergabe in C++: C++ implementiert auch die Semantik der Referenzübergabe (Inout-Modus) unter Verwendung von Zeigern und einer speziellen Art von Zeigern, die als Referenztyp bezeichnet wird. Referenztypzeiger werden implizit innerhalb des Unterprogramms dereferenziert, ihre Semantik wird jedoch auch als Referenz übergeben.

Das Schlüsselkonzept hier ist also, dass Pass-by-Reference einen Zugriffspfad auf die Daten implementiert, anstatt die Daten in das Unterprogramm zu kopieren. Datenzugriffspfade können explizit dereferenzierte Zeiger oder automatisch dereferenzierte Zeiger (Referenztyp) sein.

Weitere Informationen finden Sie in dem Buch Concepts of Programming Languages ​​von Robert Sebesta, 10. Aufl., Kapitel 9.

4

Sie übergeben kein int als Referenz, sondern einen Zeiger auf ein int als Wert. Unterschiedliche Syntax, gleiche Bedeutung.

3
xan

In C verwenden Sie zur Referenzübergabe den Operator address-of &, Der für eine Variable verwendet werden soll. In Ihrem Fall ist dies jedoch nicht der Fall, da Sie die Zeigervariable p verwendet haben muss der Adresse des Operators vorangestellt werden. Es wäre wahr gewesen, wenn Sie &i Als Parameter verwendet hätten: f(&i).

Sie können dies auch hinzufügen, um p zu dereferenzieren und zu sehen, wie dieser Wert mit i übereinstimmt:

printf("p=%d \n",*p);
3
t0mm13b

zeiger und Referenzen sind zwei verschiedene Thigngs.

Ein paar Dinge die ich noch nicht gesehen habe erwähnt.

Ein Zeiger ist die Adresse von etwas. Ein Zeiger kann wie jede andere Variable gespeichert und kopiert werden. Es hat also eine Größe.

Eine Referenz sollte als ALIAS von etwas angesehen werden. Es hat keine Größe und kann nicht gespeichert werden. Es muss etwas verweisen, dh. es kann nicht null sein oder geändert werden. Nun, manchmal muss der Compiler die Referenz als Zeiger speichern, aber das ist ein Implementierungsdetail.

Bei Verweisen gibt es keine Probleme mit Zeigern, wie z. B. Umgang mit Inhabern, Null-Überprüfung und De-Referenzierung bei Verwendung.

2
user2187033

'Referenzübergabe' (unter Verwendung von Zeigern) war von Anfang an in C. Warum denkst du, ist es nicht?

1
Maurits Rijk

Ich denke, C unterstützt tatsächlich die Weitergabe als Referenz.

Die meisten Sprachen benötigen syntaktischen Zucker, um als Referenz statt als Wert zu übergeben. (C++ erfordert beispielsweise & in der Parameterdeklaration).

C benötigt dazu auch syntaktischen Zucker. Es steht * in der Parametertypdeklaration und & im Argument. Also * und & ist die C-Syntax für die Referenzübergabe.

Man könnte jetzt argumentieren, dass eine echte Referenzübergabe nur eine Syntax für die Parameterdeklaration erfordern sollte, nicht für die Argumentation.

Aber jetzt kommt C #, das funktioniert durch Referenzübergabe unterstützt nd syntaktischen Zucker auf beiden Parameter- und Argument-Seiten erfordert.

Das Argument, dass C keine Bypass-Umgehung hat, damit die syntaktischen Elemente die zugrunde liegende technische Implementierung ausdrücken, ist überhaupt kein Argument, da dies mehr oder weniger für alle Implementierungen gilt.

Das einzige verbleibende Argument ist, dass die Übergabe von ref in C kein monolithisches Merkmal ist, sondern zwei vorhandene Merkmale kombiniert. (Nehmen Sie ref des Arguments von &, und erwarten Sie, dass ref durch * eingegeben wird.) C # erfordert beispielsweise zwei syntaktische Elemente, aber sie können nicht ohne einander verwendet werden.

Dies ist offensichtlich ein gefährliches Argument, da viele andere Funktionen in Sprachen aus anderen Funktionen bestehen. (wie Zeichenkettenunterstützung in C++)

1
Johannes

Was Sie tun, ist Wertübergabe, nicht Referenzübergabe. Weil Sie den Wert einer Variablen 'p' an die Funktion 'f' senden (hauptsächlich als f (p);)

Dasselbe Programm in C mit Referenzübergabe sieht folgendermaßen aus (!!! Dieses Programm gibt 2 Fehler aus, da Referenzübergabe in C nicht unterstützt wird.)

#include <stdio.h>

void f(int &j) {    //j is reference variable to i same as int &j = i
  j++;
}

int main() {
  int i = 20;
  f(i);
  printf("i = %d\n", i);

  return 0;
}

Ausgabe:-

 3:12: Fehler: erwartet ';', ',' oder ')' vor '&' Token 
 Ungültig f (int & j); 
 ^ 
 9: 3: Warnung: implizite Deklaration der Funktion 'f' 
 F (a); 
 ^ 
1
Sunay