2010-08-05 3 views
6

Im unten stehenden C-Programm verstehe ich nicht, warum buf [0] = 'A', nachdem ich foo aufgerufen habe. Ist nicht foo Pass-by-Value?C Programmierung - Pass-by-Referenz

#include <stdio.h> 
#include <stdlib.h> 

    void foo(char buf[]) 
    { 
     buf[0] = 'A'; 
    } 

    int main(int argc, char *argv[]) 
    { 
     char buf[10]; 

     buf[0] = 'B'; 
     printf("before foo | buf[0] = %c\n", buf[0]); 
     foo(buf); 
     printf("after foo | buf[0] = %c\n", buf[0]); 

     system("PAUSE"); 
     return 0; 
     } 

Ausgang:

before foo | buf[0] = 'B' 
after foo | buf[0] = 'A' 

Antwort

12
void foo(char buf[]) 

ist die gleiche wie

void foo(char* buf) 

Wenn Sie es nennen, foo(buf) Sie einen Zeiger nach Wert übergeben, so dass eine Kopie des Zeigers gemacht wird.

Die Kopie des Zeigers verweist auf dasselbe Objekt wie der ursprüngliche Zeiger (oder in diesem Fall auf das ursprüngliche Element des Arrays).

C hat keine Referenz-Semantik in dem Sinne, dass C++ die Referenz-Semantik erfüllt. Alles in C wird nach Wert übergeben. Zeiger werden verwendet, um eine Referenzsemantik zu erhalten.

+0

Danke an alle, besonders James, für die Antwort. Warum sollte jemand Call by Value verwenden, wenn er den Wert des Inputs nicht ändert? –

+0

Weil Sie einen Wert von etwas benötigen, aber nicht ändern müssen. – Javier

+2

@Kevin: Weil die Funktion nur den Wert benötigt. Funktionen ohne Nebenwirkungen sind leichter zu verstehen (nicht dass Sie mit C als funktionaler Programmiersprache sehr weit kommen würden), und die Weitergabe nach Werten entfernt mögliche Nebenwirkungen. –

1

* char buf [] bedeutet eigentlich char ** so sind vorbei Sie durch Zeiger/Referenz. Das gibt Ihnen, dass buf ein Zeiger ist, sowohl in der Haupt() und foo() -Funktion.

1

Weil Sie einen Zeiger auf buf übergeben (nach Wert). Der Inhalt, auf den buf zeigt, wurde geändert.

4

ein Array ist nur eine schicke Möglichkeit, einen Zeiger zu verwenden. Wenn Sie die Funktion buf an die Funktion übergeben, übergeben Sie einen Zeiger als Wert, aber wenn Sie den Zeiger dereferenzieren, verweisen Sie immer noch auf die Zeichenfolge, auf die er zeigt.

0

Um diese nach Wert zu übergeben, müsste die Funktion die Größe des Arguments kennen. In diesem Fall übergeben Sie nur einen Zeiger.

1

Mit Zeigern ist es anders; Sie übergeben den Wert, aber was Sie übergeben, ist der Wert des Zeigers, der nicht mit dem Wert des Arrays übereinstimmt.

Also der Wert des Zeigers ändert sich nicht, aber Sie ändern, was es zeigt.

2

Array als Funktionsparameter auf einen Zeiger äquivalent, so dass die Erklärung

void foo(char buf[]); 

die gleiche ist wie

void foo(char* buf); 

Das Argument Array wird dann auf sein erstes Element auf den Zeiger abgeklungen .

0

Sie sind hier als Referenz übergeben. In diesem Beispiel können Sie das Problem lösen, indem Sie ein einzelnes Zeichen am Index des gewünschten Arrays übergeben.

Wenn Sie den Inhalt des ursprünglichen Arrays beibehalten möchten, können Sie die Zeichenfolge in den temporären Speicher in der Funktion kopieren.

edit: Was würde passieren, wenn Sie Ihr char-Array in eine Struktur eingepackt und die Struktur übergeben?Ich glaube, das könnte auch funktionieren, obwohl ich nicht weiß, welche Art von Overhead auf der Compiler-Ebene entstehen könnte.

0

Bitte beachten Sie eine Sache,

Erklärung

void foo(char buf[]) 

sagt, dass [] Notation wird. Nicht welches Element des Arrays du benutzen willst.

, wenn Sie darauf hinweisen möchten, wollen Sie etwas bestimmten Wert erhalten, dann sollten Sie diese Funktion als

void foo(char buf[X]); //where X would be a constant. 

Natürlich erklären es nicht möglich ist, weil es nutzlos (Funktion für den Betrieb sein würde, am n-ten Element der Anordnung?). Sie müssen nicht aufschreiben, welches Element des Arrays Sie erhalten möchten. Alles, was Sie brauchen, ist einfache Erklärung:

voi foo(char value); 

so ...

void foo(char buf[]) 

ist eine Erklärung, die besagt, welche Schreibweise Sie verwenden möchten ([] - Teil), und es enthält auch Zeiger auf Daten.

Außerdem ... was würden Sie erwarten ... Sie Funktion foo

foo(buf); 

einen Namen Array gesendet, die zu & buf entspricht [0]. Also ... das ist ein Zeiger.

0

Arrays in C werden nicht als Wert übergeben. Sie sind nicht einmal legitime Funktionsparameter. Stattdessen erkennt der Compiler, dass Sie versuchen, ein Array zu übergeben und es auf den Zeiger zu reduzieren. Es macht das still, weil es böse ist. Es mag auch, Welpen zu treten. Das Verwenden von Arrays in Funktionsparametern ist eine gute Möglichkeit, Ihren API-Benutzern zu signalisieren, dass dieses Ding ein Speicherblock sein sollte, der in n-Byte große Segmente unterteilt ist, aber nicht erwarten, dass Compiler sich darum kümmern, wenn Sie char foo[] oder in Funktionsparameter. Sie werden nicht.

2

Arrays werden anders als andere Typen behandelt;

Außer wenn es der Operanden der: man kann nicht ein Feld "durch den Wert" in C.

Online C99 standard (draft n1256), Abschnitt 6.3.2.1, "Lvalues, Arrays und Funktion Bezeich" Absatz 3 passieren sizeof operator oder der unäre Operator & oder ist ein String-Literal, das zum Initialisieren eines Arrays verwendet wird. Ein Ausdruck vom Typ '' Array vom Typ '' ist konvertiert in einen Ausdruck mit dem Typ '' Zeiger auf den Typ '', auf den verweist Das Anfangselement von das Array-Objekt und ist kein Lvalue. Wenn das Array-Objekt über eine Speicherklasse für Register verfügt, ist das Verhalten undefiniert.

Im Aufruf

foo(buf); 

der Array Ausdruck buf ist nicht der Operand sizeof oder &, noch ist es ein Stringliteral ein Array zu initialisieren, verwendet wird, so ist es implizit konvertiert wird ("Zerfälle ") vom Typ" 10-Elemente-Array von char "bis" Zeiger auf char ", und die -Adresse des ersten Elements wird an foo übergeben. Daher wird alles, was Sie an buf in foo() tun, in buf Array in main() widergespiegelt werden. Da Array-Subskriptionen definiert sind, können Sie einen Subscript-Operator für einen Zeigertyp verwenden, so dass er wie aussieht, als ob Sie mit einem Array-Typ arbeiten, aber nicht.

Im Rahmen einer Funktion Parameterdeklaration, T a[] und T a[N] sind mit T *a auch, aber das ist nur Fall, dass das wahr ist.

1

Arrays und Zeiger sind (fast) das Gleiche.

int* foo = malloc(...) 

foo[2] ist die gleiche wie *(foo+2*sizeof(int))

Anekdote: Sie

schrieb Rechts
int main(int argc, char *argv[]) 

es ist auch (die gleiche kompilieren und arbeiten)

int main(int argc, char **argv) 

zu schreiben und auch

int main(int argc, char argv[][]) 

sie sind effektiv gleich. es ist etwas komplizierter als das, weil ein Array weiß, wie viele Elemente es hat, und ein Zeiger nicht. aber sie werden gleich verwendet.