Przechodzenie przez odniesienie w C

Jeśli C nie obsługuje przekazywania zmiennej przez odniesienie, dlaczego to działa?

#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;
}

Wyjście:

$ gcc -std=c99 test.c
$ a.exe
i = 21 
Author: S.S. Anne, 2010-02-09

17 answers

Ponieważ przekazujesz wartość wskaźnika do metody, a następnie dereferujesz ją, aby uzyskać liczbę całkowitą, na którą jest wskazywana.

 329
Author: tvanfosson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 18:23:36

To nie jest pass-by-reference, to jest pass-by-value, jak inni stwierdzili.

Język C jest wartością pass-by-value bez wyjątku. Podanie wskaźnika jako parametr nie oznacza pass-by-reference.

Reguła jest następująca:

Funkcja nie jest w stanie zmienić rzeczywistej wartości parametrów.


Spróbujmy zobaczyć różnice między parametrami skalarnymi i wskaźnikowymi funkcji.

Skalar zmienne

Ten krótki program pokazuje wartość pass-by-value przy użyciu zmiennej skalarnej. {[6] } nazywa się parametrem formalnym, a {[7] } przy wywołaniu funkcji nazywa się parametrem rzeczywistym. Uwaga inkrementacja param w funkcji nie zmienia się variable.

#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;
}

Wynik jest

I've received value 111
variable=111

Iluzja pass-by-reference

Zmieniamy nieco fragment kodu. param jest teraz wskaźnikiem.

#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;
}

Wynik jest

I've received value 111
variable=112

To sprawia, że wierzysz, że parametr został przekazany przez odniesienie. Nie było. Została przekazana przez wartość, wartość param jest adresem. Wartość typu int została zwiększona i to jest efekt uboczny, który sprawia, że myślimy, że było to wywołanie funkcji pass-by-reference.

Pointers-passed-by-value

Jak możemy pokazać / udowodnić ten fakt? Cóż, może spróbujemy pierwszego przykładu zmiennych skalarnych, ale zamiast skalarów używamy adresów (wskaźników). Zobaczmy, czy to może pomóc.

#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;
}

Wynik będzie być, że dwa adresy są równe (nie martw się o dokładną wartość).

Przykładowy wynik:

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

Moim zdaniem dowodzi to wyraźnie, że wskaźniki są przekazywane przez wartość. W przeciwnym razie {[11] } byłoby NULL po wywołaniu funkcji.

 148
Author: Ely,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-12-15 01:10:42

W C, Pass-by-reference jest symulowane przekazując adres zmiennej (wskaźnik) i zdając sobie sprawę, że adres w funkcji do odczytu lub Zapisz rzeczywistą zmienną. To będzie być określane jako " styl C pass-by-reference."

Źródło: www-cs-students.stanford.edu

 72
Author: Tamas Czinege,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 14:56:07

Ponieważ w powyższym kodzie nie ma odniesienia pass-by-reference. Używanie wskaźników (np. void func(int* p)) to pass-by-address. To jest pass-by-reference w C++ (nie będzie działać w C):

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

...
int a;
func(a);
// a is 4 now
 51
Author: Alexander Gessler,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 14:01:08

Twój przykład działa, ponieważ przekazujesz adres zmiennej do funkcji, która manipuluje jej wartością za pomocą operatora dereferencji .

Chociaż C nie obsługuje referencyjnych typów danych , nadal można symulować przekazywanie przez odniesienie poprzez jawne przekazywanie wartości wskaźnika, jak w przykładzie.

Referencyjny typ danych C++ jest mniej wydajny, ale uważany za bezpieczniejszy niż typ wskaźnika odziedziczony po C. To byłby twój przykład, dostosowany do użycia referencje C++ :

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

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

  return 0;
}
 29
Author: Daniel Vassallo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-11 09:56:01

Przekazujesz Wskaźnik (Lokalizacja adresu) przez wartość .

To jak powiedzenie " Oto miejsce z danymi, które chcę, abyś zaktualizował."

 12
Author: antony.trupe,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-10 00:13:48

W C wszystko jest przechodnie. Użycie wskaźników daje nam złudzenie, że przechodzimy przez odniesienie, ponieważ zmienia się wartość zmiennej. Jeśli jednak chcesz wydrukować adres zmiennej kursora, zobaczysz, że nie ma to wpływu. Do funkcji przekazywana jest Kopia z wartości adresu. Poniżej znajduje się fragment, który to ilustruje.

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
 7
Author: Arun Suresh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-07-11 14:00:47

P jest zmienną wskaźnika. Gdy wywołujesz f, przekazujesz wartość p, która jest adresem i.

 6
Author: Anssi,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 15:00:34

Nie pass-by-reference w C, ale p "odnosi się" do i, a Ty przekazujesz wartość p by.

 6
Author: Pavel Radzivilovsky,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 15:28:48

Ponieważ przekazujesz wskaźnik (adres pamięci) do zmiennej p do funkcji f. innymi słowy przekazujesz wskaźnik, a nie referencję.

 4
Author: dave,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 13:55:55

W C, aby przejść przez referencję, używasz operatora address-of &, który powinien być użyty przeciwko zmiennej, ale w Twoim przypadku, ponieważ użyłeś zmiennej wskaźnikowej p, nie musisz jej poprzedzać operatorem address-of. Byłoby to prawdą, gdybyś użył &i jako parametru: f(&i).

Możesz również dodać to do dereference p i zobaczyć, jak ta wartość pasuje i:

printf("p=%d \n",*p);
 4
Author: t0mm13b,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-10 05:08:54

Krótka odpowiedź: Tak, C implementuje parametr przechodzący przez odniesienie za pomocą wskaźników.

Implementując przekazywanie parametrów, projektanci języków programowania stosują trzy różne strategie (lub modele semantyczne): przesyłają dane do podprogramu, odbierają dane z podprogramu lub wykonują oba. Modele te są powszechnie znane jako in mode, out mode i inout mode, odpowiednio.

Kilka modeli zostało opracowanych przez projektantów języków, aby zaimplementować te trzy podstawowe strategie przekazywania parametrów:

Pass-by-Value (w semantyce mode) Pass-by-Result (semantyka trybu wyjścia) Pass-by-Value-Result (semantyka trybu inout) Pass-by-Reference (semantyka trybu inout) Pass-by-Name (semantyka trybu inout)

Pass-by-reference jest drugą techniką przekazywania parametrów w trybie inout. Zamiast kopiować dane między główną funkcją a podprogramem, System runtime wysyła bezpośrednią ścieżkę dostępu do danych dla podprogramu. W tym strategia podprogram ma bezpośredni dostęp do danych, efektywnie udostępniając dane z głównym programem. Główną zaletą tej techniki jest to, że jest ona absolutnie wydajna w czasie i przestrzeni, ponieważ nie ma potrzeby powielania przestrzeni i nie ma operacji kopiowania danych.

Parametr przekazujący implementację w C: C implementuje semantykę pass-by-value oraz pass-by-reference (tryb inout) używając wskaźników jako parametrów. Wskaźnik jest wysyłany do podprogramu i żadne rzeczywiste dane nie są skopiowane w ogóle. Ponieważ jednak wskaźnik jest ścieżką dostępu do danych w rutynie głównej, podprogram może zmienić dane w rutynie głównej. C przyjął tę metodę z ALGOL68.

Parametr przekazujący implementację w C++: C++ implementuje również semantykę pass-by-reference (tryb inout) przy użyciu wskaźników, a także przy użyciu specjalnego rodzaju wskaźnika, zwanego typem odniesienia. Wskaźniki typu odniesienia są niejawnie dereferowane wewnątrz podprogramu, ale ich semantyka jest również pass-by-reference.

Kluczowa koncepcja polega na tym, że pass-by-reference implementuje ścieżkę dostępu do danych zamiast kopiować dane do podprogramu. Ścieżki dostępu do danych mogą być jawnie dereferenced pointers lub auto dereferenced pointers (reference type).

Więcej informacji można znaleźć w książce Concepts of Programming Languages autorstwa Roberta Sebesta, 10th Ed., Rozdział 9.

 4
Author: Francisco Zapata,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-10-22 04:15:18

Nie przekazujesz int przez odniesienie, przekazujesz wskaźnik do int przez wartość. Inna składnia, to samo znaczenie.

 3
Author: xan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 13:57:31

Wskaźniki i odniesienia to dwie różne rzeczy.

Kilka rzeczy, o których nie wspomniałem.

Wskaźnik jest adresem czegoś. Wskaźnik może być przechowywany i kopiowany jak każda inna zmienna. Ma więc rozmiar.

Odniesienie powinno być postrzegane jako ALIAS czegoś. Nie ma rozmiaru i nie można go przechowywać. Musi się odnosić do czegoś, tj. nie może być null ani zmieniona. Cóż, czasami kompilator musi przechowywać odniesienie jako wskaźnik, ale to jest szczegół realizacji.

Z referencjami nie masz problemów ze wskaźnikami, takimi jak obsługa własności, sprawdzanie null, usuwanie odniesień przy użyciu.

 2
Author: user2187033,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-08 08:19:40

'Pass by reference' (za pomocą wskaźników) był w C Od początku. Jak myślisz, dlaczego nie?

 1
Author: Maurits Rijk,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-02-09 13:54:50

Myślę, że C w rzeczywistości wspiera pass by reference.

Większość języków wymaga, aby cukier składniowy przekazywał referencję zamiast wartości. (C++ na przykład wymaga & w deklaracji parametrów).

C wymaga do tego również cukru składniowego. Jest * w deklaracji typu parametru i & na argumencie. Tak * i & jest składnią C dla pass by reference.

Można teraz argumentować, że prawdziwe podanie przez referencję powinno wymagać tylko składni deklaracji parametru, nie po stronie kłótni.

Ale teraz pojawia się C#, który obsługuje poprzez przekazywanie referencji i wymaga cukru składniowego na zarówno po stronie parametru, jak i argumentu.

Argument, że C nie ma przejścia by-ref, ponieważ elementy składniowe wyrażają go, wykazując podstawową implementację techniczną, nie jest argumentem w ogóle, ponieważ odnosi się to mniej więcej do wszystkich implementacji.

Jedynym pozostałym argumentem jest to, że przechodzenie przez ref w C nie jest cecha monolityczna, ale łączy w sobie dwie istniejące cechy. (Weź ref argumentu przez &, oczekuj, że ref napisze przez *.) C# wymaga na przykład dwóch elementów składniowych, ale nie można ich używać bez siebie.

Jest to oczywiście niebezpieczny argument, ponieważ wiele innych funkcji w językach składa się z innych funkcji. (podobnie jak obsługa łańcuchów w C++)

 1
Author: Johannes,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-22 17:58:16

To, co robisz, jest przekazywane przez wartość, a nie przez odniesienie. Ponieważ wysyłasz wartość zmiennej " p "do funkcji" f " (w głównym jako f (p);)

Ten sam program w C z pass by reference będzie wyglądał tak: (!!!ten program daje 2 błędy, ponieważ pass by reference nie jest obsługiwane w C)

#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;
}

Wyjście:-

3:12: error: expected ';', ',' or ')' before '&' token
             void f(int &j);
                        ^
9:3:  warning: implicit declaration of function 'f'
               f(a);
               ^
 1
Author: Sunay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-21 16:54:11