Məzmuna keç
Educora
Orta18 dəq6 / 8

Göstəricilər

Dəyişənin yaddaşdakı ünvanını al, göstərici vasitəsilə qiyməti oxu və dəyiş, göstərici arifmetikasını və göstəricilərlə massivlərin əlaqəsini anla, swap funksiyasını yaz.

Özünü yoxla
Bu dərsdə öyrənəcəksən
  • & operatoru ilə ünvan almaq və * ilə göstəricinin göstərdiyi qiyməti oxumaq və dəyişmək
  • Funksiyaya göstərici ötürərək çağıran tərəfin dəyişənlərini dəyişmək
  • Göstərici arifmetikasını və arr[i] ilə *(arr + i) ifadələrinin eyni olduğunu izah etmək
  • NULL və ilkin qiymət verilməmiş göstəricilərin təhlükəsini tanımaq

Dostuna evinin yerini göstərmək üçün evi götürüb aparmırsan — sadəcə ünvanı deyirsən: «Bakı, Nizami küçəsi, 25». Ünvanı bilən adam evi tapa, içəri girə və hətta orada nəyisə dəyişə bilər. Kompüterin yaddaşı da belədir: hər dəyişən müəyyən ünvanda yaşayır. Ünvanı saxlayan dəyişənə göstərici deyilir. Göstəricilər C-nin ən güclü və eyni zamanda ən çox qorxulan mövzusudur, amma ideyası elə bu ünvan qədər sadədir.

Ünvan və göstərici

Tərif
Göstərici

Qiyməti başqa dəyişənin yaddaşdakı ünvanı olan dəyişən. int *p; — «p int tipli qiymətin ünvanını saxlayır» deməkdir.

YazılışMənası
int *p;int-ə göstərici elan edir
&xx dəyişəninin ünvanı
p = &x;p indi x-i göstərir
*pp-nin göstərdiyi ünvandakı qiymət (müraciət, ingiliscə dereference)
NULLheç nəyi göstərməyən göstərici
C
#include <stdio.h>

int main(void) {
    int x = 10;
    int *p = &x;
    printf("x = %d\n", x);
    printf("*p = %d\n", *p);
    *p = 25;
    printf("x = %d\n", x);
    printf("Same address? %d\n", p == &x);
    return 0;
}
Gözlənilən nəticə
x = 10
*p = 10
x = 25
Same address? 1
*p = 25; x-in adını çəkmədən onun qiymətini dəyişdi, çünki p onun ünvanını saxlayır.

Ünvanın özünü %p spesifikatoru ilə çap etmək olar. O, adətən 0x7ffd... kimi on altılıq ədəd şəklində görünür və proqram hər dəfə işə salınanda fərqli ola bilər:

C
int x = 10;
int *p = &x;
printf("Address of x: %p\n", (void *) &x);
printf("Value of p:   %p\n", (void *) p);

Funksiyaya göstərici ötürmək: swap

Xatırla: C-də funksiya arqumentlərin surətini alır, ona görə adi parametrlə iki dəyişənin qiymətini dəyişdirmək mümkün deyil. Həll yolu — funksiyaya dəyişənlərin ünvanlarını vermək. Funksiya ünvan vasitəsilə orijinal dəyişənlərə çatır. scanf("%d", &age) məhz buna görə & tələb edir.

C
#include <stdio.h>

void swap(int *a, int *b) {
    int temp = *a;
    *a = *b;
    *b = temp;
}

int main(void) {
    int x = 3, y = 8;
    printf("Before: x = %d, y = %d\n", x, y);
    swap(&x, &y);
    printf("After:  x = %d, y = %d\n", x, y);
    return 0;
}
Gözlənilən nəticə
Before: x = 3, y = 8
After:  x = 8, y = 3

Göstəricilər funksiyaya bir neçə nəticə «qaytarmağa» da imkan verir: return yalnız bir qiymət qaytarır, amma funksiya nəticələri ona verilmiş ünvanlara yaza bilər. Aşağıdakı funksiya Bakıda bir həftənin temperaturları arasında ən kiçik və ən böyük qiyməti tapır:

C
#include <stdio.h>

void min_max(const int *arr, int n, int *min, int *max) {
    *min = arr[0];
    *max = arr[0];
    for (int i = 1; i < n; i++) {
        if (arr[i] < *min) *min = arr[i];
        if (arr[i] > *max) *max = arr[i];
    }
}

int main(void) {
    int temps[5] = {18, 25, 21, 30, 16};
    int low, high;
    min_max(temps, 5, &low, &high);
    printf("Min: %d, max: %d\n", low, high);
    return 0;
}
Gözlənilən nəticə
Min: 16, max: 30

Göstərici arifmetikası və massivlər

Massivin adı ifadələrdə onun ilk elementinin ünvanına çevrilir, ona görə int *p = arr; yazmaq olar. Göstəriciyə 1 əlavə edəndə o, 1 bayt yox, bir element irəli gedir: int * üçün adətən 4 bayt. Deməli, *(p + 2) üçüncü elementdir, arr[i] yazılışı isə sadəcə *(arr + i) ifadəsinin qısa formasıdır. Funksiyaya massiv ötürəndə əslində ilk elementin göstəricisi ötürülür — funksiyanın orijinal massivi dəyişə bilməsinin və parametrdə sizeof fəndinin işləməməsinin səbəbi budur.

C
#include <stdio.h>

int main(void) {
    int arr[4] = {10, 20, 30, 40};
    int *p = arr;
    printf("%d\n", *p);
    printf("%d\n", *(p + 2));
    p++;
    printf("%d\n", *p);
    printf("%d\n", arr[3] == *(arr + 3));
    int sum = 0;
    for (int *q = arr; q < arr + 4; q++) {
        sum += *q;
    }
    printf("Sum: %d\n", sum);
    return 0;
}
Gözlənilən nəticə
10
30
20
1
Sum: 100
p++ göstəricini növbəti elementə keçirdi. Son dövr massivi indeks əvəzinə göstərici ilə gəzir.

Təhlükəli göstəricilər

  • İlkin qiyməti verilməmiş göstərici təsadüfi ünvanı saxlayır. Ona yazmaq başqa məlumatı korlaya bilər. Göstəriciyə həmişə ilkin qiymət olaraq ya dəyişənin ünvanını, ya da NULL ver.
  • **NULL göstəricisinə müraciət** (*p, p isə NULL-dur) adətən proqramı dərhal çökdürür. İstifadədən əvvəl yoxla: if (p != NULL).
  • Asılı qalan göstərici artıq mövcud olmayan dəyişəni göstərir. Məsələn, funksiya öz lokal dəyişəninin ünvanını qaytarsa, funksiya bitəndə o dəyişən yox olur.
C
int *make_number(void) {
    int local = 5;
    return &local;
}
SƏHV nümunəsi: local funksiya bitəndə yox olur, qaytarılan ünvan isə «boşluğu» göstərir. Nəticə gözlənilməzdir.

Əsas fikirlər

  • Göstərici ünvan saxlayır: int *p = &x; — p x-in ünvanıdır.
  • & ünvanı verir, * ünvandakı qiyməti oxuyur və ya dəyişir.
  • Funksiya göstərici alanda çağıran tərəfin dəyişənlərini dəyişə bilər (swap, scanf).
  • p + 1 bir element irəli gedir; arr[i] — *(arr + i) deməkdir.
  • Göstəriciyə həmişə ilkin qiymət ver, NULL-u yoxla və lokal dəyişənin ünvanını qaytarma.

Özünü yoxla

10 sual. Hər düzgün cavab XP qazandırır.

1 / 10
int x = 4; int *p = &x; *p = 9; sətirlərindən sonra x neçədir?