Wskaźniki W C: Kiedy używać ampersand i gwiazdkę?

Zaczynam od wskazań i jestem trochę zdezorientowany. Wiem, że & oznacza adres zmiennej i że * może być użyty przed zmienną wskaźnika, aby uzyskać wartość obiektu, na który wskazuje wskaźnik. Ale sprawy działają inaczej, gdy pracujesz z tablicami, łańcuchami lub gdy wywołujesz funkcje z kopią wskaźnika zmiennej. Trudno dostrzec w tym wszystkim schemat logiki.

Kiedy należy stosować & i *?

 219
Author: Pieter, 2010-01-19

9 answers

Masz wskaźniki i wartości:

int* p; // variable p is pointer to integer type
int i; // integer value

Zamieniasz wskaźnik na wartość za pomocą *:

int i2 = *p; // integer i2 is assigned with integer value that pointer p is pointing to

Zamieniasz wartość w wskaźnik za pomocą &:

int* p2 = &i; // pointer p2 will point to the address of integer i

Edytuj: W przypadku tablic są one traktowane bardzo podobnie jak wskaźniki. Jeśli myślisz o nich jak o wskaźnikach, będziesz używać *, aby uzyskać wartości wewnątrz nich, jak wyjaśniono powyżej, ale istnieje również inny, bardziej powszechny sposób użycia operatora []:

int a[2];  // array of integers
int i = *a; // the value of the first element of a
int i2 = a[0]; // another way to get the first element

Aby uzyskać drugi element:

int a[2]; // array
int i = *(a + 1); // the value of the second element
int i2 = a[1]; // the value of the second element

Operator indeksujący [] jest specjalną formą operatora * i działa tak:

a[i] == *(a + i);  // these two statements are the same thing
 486
Author: Dan Olson,
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
2015-11-21 11:53:13

Istnieje wzorzec, gdy mamy do czynienia z tablicami i funkcjami; na początku jest tylko trochę trudno go zobaczyć.

Podczas pracy z tablicami, warto pamiętać, co następuje: gdy wyrażenie tablicowe pojawia się w większości kontekstów, typ wyrażenia jest domyślnie konwertowany z "tablicy N-elementów T" na "wskaźnik do T", a jego wartość jest ustawiona na wskaż pierwszy element w tablicy. Wyjątkami od tej reguły są sytuacje, gdy wyrażenie array pojawia się jako operand albo Operatory & lub sizeof, lub gdy jest to literał Łańcuchowy używany jako inicjalizator w deklaracji.

Tak więc, gdy wywołasz funkcję z wyrażeniem tablicy jako argumentem, funkcja otrzyma wskaźnik, a nie tablicę:

int arr[10];
...
foo(arr);
...

void foo(int *arr) { ... }

Dlatego nie używaj operatora & dla argumentów odpowiadających "%s " w scanf():

char str[STRING_LENGTH];
...
scanf("%s", str);

Z powodu niejawnej konwersji, scanf() otrzymuje char * wartość, która wskazuje na początek str / align = "left" / Dotyczy to każdej funkcji wywołanej z wyrażeniem tablicy jako argumentem (prawie każdej z funkcji str*, *scanf i *printf, itd.).

W praktyce prawdopodobnie nigdy nie wywołasz funkcji z wyrażeniem tablicy używając operatora &, jak w:

int arr[N];
...
foo(&arr);

void foo(int (*p)[N]) {...}

Taki kod nie jest zbyt powszechny; musisz znać rozmiar tablicy w deklaracji funkcji, a funkcja działa tylko ze wskaźnikami do tablic o określonych rozmiarach (wskaźnik do 10-elementowa tablica T jest innego typu niż wskaźnik do 11-elementowej tablicy T).

Gdy wyrażenie tablicy pojawia się jako operand dla operatora &, Typ wynikowego wyrażenia to "wskaźnik do tablicy N-elementowej T" lub T (*)[N], który różni się od tablicy wskaźników (T *[N]) i wskaźnika do typu bazowego (T *).

Gdy mamy do czynienia z funkcjami i wskaźnikami, regułą do zapamiętania jest: jeśli chcesz zmienić wartość argumentu i mieć ją odzwierciedlone w kodzie wywołującym, musisz przekazać wskaźnik do rzeczy, którą chcesz zmodyfikować. Ponownie, tablice rzucają trochę Małpiego klucza w prace, ale najpierw zajmiemy się normalnymi przypadkami.

Pamiętaj, że C przekazuje wszystkie argumenty funkcji według wartości; parametr formalny otrzymuje kopię wartości w parametrze rzeczywistym, a wszelkie zmiany w parametrze formalnym nie są odzwierciedlane w parametrze rzeczywistym. Częstym przykładem jest swap funkcja:

void swap(int x, int y) { int tmp = x; x = y; y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(a, b);
printf("after swap: a = %d, b = %d\n", a, b);

Otrzymasz następujące wyjście:

before swap: a = 1, b = 2
after swap: a = 1, b = 2

Parametry formalne x i y są odrębnymi obiektami od a i b, więc zmiany na x i y nie są odzwierciedlane w a i b. Ponieważ chcemy zmodyfikować wartości a i b, musimy przekazać wskaźniki do nich do funkcji swap:

void swap(int *x, int *y) {int tmp = *x; *x = *y; *y = tmp; }
...
int a = 1, b = 2;
printf("before swap: a = %d, b = %d\n", a, b);
swap(&a, &b);
printf("after swap: a = %d, b = %d\n", a, b);

Teraz twój wynik będzie

before swap: a = 1, b = 2
after swap: a = 2, b = 1

Zauważ, że w funkcji swap nie zmieniamy wartości x i y, ale wartości tego, co x i y wskaż . Zapis do *x różni się od zapisu do x; nie aktualizujemy samej wartości w x, otrzymujemy lokalizację z x i aktualizujemy wartość w tej lokalizacji.

Jest to równie prawdziwe, jeśli chcemy zmodyfikować wartość wskaźnika; jeśli napiszemy

int myFopen(FILE *stream) {stream = fopen("myfile.dat", "r"); }
...
FILE *in;
myFopen(in);

Następnie modyfikujemy wartość parametru wejściowego stream, a nie co stream wskazuje na , więc zmiana stream nie ma wpływu na wartość in; aby to zadziałało, musimy przekazać wskaźnik do wskaźnika:

int myFopen(FILE **stream) {*stream = fopen("myFile.dat", "r"); }
...
FILE *in;
myFopen(&in);
/ Align = "center" bgcolor = "# e0ffe0 " / cesarz chin / / align = center / Kiedy przekazujesz wyrażenie tablicy do funkcji, funkcja otrzymuje wskaźnik. Ze względu na sposób definiowania array subscripting, można użyć operatora indeksu dolnego na wskaźniku w taki sam sposób, jak można go używać na tablicy:
int arr[N];
init(arr, N);
...
void init(int *arr, int N) {size_t i; for (i = 0; i < N; i++) arr[i] = i*i;}

Zauważ, że obiekty tablicy mogą nie być przypisane; np. nie możesz jak

int a[10], b[10];
...
a = b;

Więc chcesz być ostrożny, gdy masz do czynienia ze wskaźnikami do tablic; coś w rodzaju

void (int (*foo)[N])
{
  ...
  *foo = ...;
}
Nie zadziała.
 22
Author: John Bode,
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-09-17 15:58:25

Po prostu

  • & oznacza adres - z , zobaczysz, że w miejscach zastępczych dla funkcji modyfikujących zmienną parametru, jak w C, zmienne parametrów są przekazywane przez wartość, używając ampersand means, aby przekazać przez odniesienie.
  • * oznacza dereferencję zmiennej wskaźnikowej, co oznacza uzyskanie wartości tej zmiennej wskaźnikowej.
int foo(int *x){
   *x++;
}

int main(int argc, char **argv){
   int y = 5;
   foo(&y);  // Now y is incremented and in scope here
   printf("value of y = %d\n", y); // output is 6
   /* ... */
}

Powyższy przykład ilustruje, jak wywołać funkcję {[5] } za pomocą pass-by-reference, porównaj z tym

int foo(int x){
   x++;
}

int main(int argc, char **argv){
   int y = 5;
   foo(y);  // Now y is still 5
   printf("value of y = %d\n", y); // output is 5
   /* ... */
}

Oto ilustracja użycia dereferencji

int main(int argc, char **argv){
   int y = 5;
   int *p = NULL;
   p = &y;
   printf("value of *p = %d\n", *p); // output is 5
}

Powyższe ilustruje, w jaki sposób otrzymaliśmy adres-z y i przypisał go do zmiennej wskaźnika p. Wtedy dereferencja p poprzez dołączenie * do przodu, aby uzyskać wartość p, tj. *p.

 9
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
2015-08-03 10:08:31

Tak, to może być dość skomplikowane, ponieważ * jest używany do wielu różnych celów w C / C++.

Jeśli * pojawia się przed zadeklarowaną zmienną / funkcją, oznacza to, że:

  • a) * daje dostęp do wartości tej zmiennej (jeśli Typ tej zmiennej jest typem wskaźnika, lub przeciążony operatorem *).
  • b) * ma znaczenie operatora mnożenia, w takim przypadku musi być inna zmienna po lewej stronie *

Jeśli * pojawia się w deklaracji zmiennej lub Funkcji, Oznacza to, że zmienna jest wskaźnikiem:

int int_value = 1;
int * int_ptr; //can point to another int variable
int   int_array1[10]; //can contain up to 10 int values, basically int_array1 is an pointer aswell which points to the first int of the array
//int   int_array2[]; //illegal, without initializer list..
int int_array3[] = {1,2,3,4,5};  // these two
int int_array4[5] = {1,2,3,4,5}; // are indentical

void func_takes_int_ptr1(int *int_ptr){} // these two are indentical
void func_takes int_ptr2(int int_ptr[]){}// and legal

Jeśli & pojawia się w zmiennej lub deklaracji funkcji, oznacza to, że zmienna ta jest odniesieniem do zmiennej tego typu.

Jeśli & pojawia się przed zadeklarowaną zmienną, zwraca adres tej zmiennej

Dodatkowo powinieneś wiedzieć, że przekazując tablicę do funkcji, zawsze będziesz trzeba również przekazać rozmiar tablicy tej tablicy, z wyjątkiem sytuacji, gdy tablica jest czymś w rodzaju zakończonej 0 tablicy CString (tablica znaków).

 9
Author: smerlin,
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
2016-06-04 21:47:39

Kiedy deklarujesz zmienną wskaźnika lub parametr funkcji, Użyj*:

int *x = NULL;
int *y = malloc(sizeof(int)), *z = NULL;
int* f(int *x) {
    ...
}

NB: każda zadeklarowana zmienna potrzebuje własnego *.

Jeśli chcesz przyjąć adres wartości, użyj &. Gdy chcesz odczytać lub zapisać wartość w wskaźniku, użyj *.

int a;
int *b;
b = f(&a);
a = *b;

a = *f(&a);

Tablice są zwykle traktowane jak wskaźniki. Kiedy deklarujesz parametr tablicy w funkcji, możesz równie łatwo zadeklarować, że jest wskaźnikiem(oznacza to to samo). Kiedy przekazujesz tablicę do funkcji, w rzeczywistości przekazujesz wskaźnik do pierwszego elementu.

Wskaźniki funkcji są jedynymi rzeczami, które nie do końca przestrzegają zasad. Możesz przyjąć adres funkcji bez użycia &, a wskaźnik funkcji można wywołać bez użycia *.

 4
Author: Jay Conrod,
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-01-19 15:51:43

Właściwie to masz to w dół pat, nic więcej nie musisz wiedzieć : -)

Dodałbym tylko następujące bity:

  • obie operacje są przeciwnymi końcami widma. & pobiera zmienną i podaje adres, * pobiera adres i podaje zmienną (lub zawartość).
  • tablice "degradują" się do wskaźników po przekazaniu ich do funkcji.
  • możesz mieć wiele poziomów na indrection (char **p oznacza, że p jest wskaźnikiem do wskaźnik do char.

Co do rzeczy działających inaczej, nie do końca:

  • tablice, jak już wspomniano, degradują się do wskaźników (do pierwszego elementu tablicy) po przekazaniu do funkcji; nie zachowują informacji o rozmiarze.
  • W C nie ma ciągów znaków, tylko tablice znaków, które zgodnie z konwencją reprezentują ciąg znaków zakończony znakiem zero (\0).
  • kiedy przekazujesz adres zmiennej do funkcji, możesz odwołać się wskaźnik do zmiany samej zmiennej (normalnie zmienne są przekazywane przez wartość (z wyjątkiem tablic)).
 3
Author: paxdiablo,
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-01-19 15:52:09

Myślę, że jesteś trochę zdezorientowany. Powinieneś przeczytać dobry samouczek/książkę o wskaźnikach.

Ten tutorial jest bardzo dobry na początek (jasno wyjaśnia czym są & i *). I tak nie zapomnij przeczytać książkiPointers in C autorstwa Kennetha Reeka.

Różnica między & i * jest bardzo wyraźna.

Przykład:

#include <stdio.h>

int main(){
  int x, *p;

  p = &x;         /* initialise pointer(take the address of x) */
  *p = 0;         /* set x to zero */
  printf("x is %d\n", x);
  printf("*p is %d\n", *p);

  *p += 1;        /* increment what p points to i.e x */
  printf("x is %d\n", x);

  (*p)++;         /* increment what p points to i.e x */
  printf("x is %d\n", x);

  return 0;
}
 3
Author: Prasoon Saurav,
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-01-19 15:55:28

Przeglądałem wszystkie słowne wyjaśnienia, więc zamiast tego zwróciłem się do filmu z University of New South Wales na ratunek.Oto proste wyjaśnienie: jeśli mamy komórkę, która ma Adres x i wartość 7, pośrednim sposobem na zapytanie o adres wartości 7 jest &7, A pośrednim sposobem na zapytanie o wartość pod adresem x jest *x. więc (cell: x , value: 7) == (cell: &7 , value: *x).Inny sposób przyjrzenia się temu: John siada na 7th seat.*7th seat wskaże John, a &John poda address/położenie 7th seat. This simple Wyjaśnienie pomogło mi i mam nadzieję, że pomoże również innym. Oto link do doskonałego Filmu: Kliknij tutaj.

Oto kolejny przykład:

#include <stdio.h>

int main()
{ 
    int x;            /* A normal integer*/
    int *p;           /* A pointer to an integer ("*p" is an integer, so p
                       must be a pointer to an integer) */

    p = &x;           /* Read it, "assign the address of x to p" */
    scanf( "%d", &x );          /* Put a value in x, we could also use p here */
    printf( "%d\n", *p ); /* Note the use of the * to get the value */
    getchar();
}

Add-on: zawsze inicjalizuj wskaźnik przed ich użyciem.Jeśli nie, wskaźnik wskaże cokolwiek, co może spowodować awarię programu, ponieważ system operacyjny uniemożliwi dostęp do pamięci, o której wie, że nie posiadasz.Ale po prostu umieszczając p = &x;, przypisujemy wskaźnik a miejsce.

 2
Author: ,
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
2016-07-14 14:34:34

Ok, wygląda na to, że twój post został edytowany...

double foo[4];
double *bar_1 = &foo[0];

Zobacz jak możesz użyć & aby uzyskać adres początku struktury tablicy? Następujące

Foo_1(double *bar, int size){ return bar[size-1]; }
Foo_2(double bar[], int size){ return bar[size-1]; }
Zrobi to samo.
 1
Author: wheaties,
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-01-19 15:52:06