Göstəricilərlə əməliyyatlar. Göstəricilərlə əməliyyatlar C-də aşağıdakı göstərici növləri fərqləndirilir:

Teqlər: C göstəriciləri. Göstərici üçün göstərici. Göstərici növü. Göstərici arifmetik. Göstəricilərin müqayisəsi.

Yol göstəriciləri

Bu, bəlkə də bütün kursun ən çətin və ən vacib mövzusudur. Göstəriciləri başa düşmədən C dilini daha da öyrənmək mənasız olacaq. Yol nişanları çox sadə anlayışdır, çox məntiqlidir, lakin təfərrüata diqqət tələb edir.

Tərif

İndeks yaddaş sahəsinin ünvanını saxlayan dəyişəndir. Göstərici, dəyişən kimi, bir növü var. Göstəriciləri elan etmək üçün sintaksis

<тип> *<имя>;

Məsələn
float *a;
uzun uzun *b;
Göstəricilərlə işləmək üçün iki əsas operator & ünvan operatoru və * dereference operatorudur. Sadə bir misala baxaq.

#daxildir #daxildir void main() ( int A = 100; int *p; //A dəyişəninin ünvanını alın p = //A dəyişəninin ünvanını çıxarın printf("%p\n", p); //Məzmununu çıxarın dəyişən A printf("% d\n", *p); getch();

Kodu yenidən diqqətlə nəzərdən keçirək

Int A = 100;

adlı dəyişən A. O, yaddaşda hansısa ünvanda yerləşir. 100 dəyəri bu ünvanda saxlanılır.

Növ göstəricisi yaradıldı int.

İndi dəyişən səh dəyişənin ünvanını saxlayır A. * operatorundan istifadə edərək dəyişənin məzmununa daxil oluruq A.
Məzmunu dəyişdirmək üçün yazın

*p = 200;

Bundan sonra dəyər A eyni yaddaş sahəsinə işarə etdiyi üçün də dəyişdi. Mürəkkəb bir şey yoxdur.
İndi başqa bir vacib nümunə

#daxildir #daxildir void main() ( int A = 100; int *a = ikiqat B = 2.3; double *b = printf("%d\n", sizeof(A)); printf("%d\n", sizeof(a) )); printf("%d\n", sizeof(B))("%d\n", sizeof(b));

Göstəriləcək
4
4
8
4
Dəyişənlərin müxtəlif növləri və ölçüləri olsa da, onlara işarə edənlər eyni ölçüyə malikdir. Həqiqətən, əgər göstəricilər ünvanları saxlayırsa, onda onlar tam ədəd olmalıdır. Düzdür, göstərici özü kimi dəyişəndə ​​saxlanılır size_t(və həmçinin ptrdiff_t), bu, tam ədəd kimi davranan bir növdür, lakin onun ölçüsü sistemin bit tutumundan asılıdır. Əksər hallarda onların arasında heç bir fərq yoxdur. Niyə göstəriciyə tip lazımdır?

Göstərici arifmetikası

Birincisi, göstəriciyə istinad etmə əməliyyatının (ünvana görə məzmunun qəbulu) düzgün işləməsi üçün bir növ lazımdır. Göstərici dəyişənin ünvanını saxlayırsa, bütün dəyişəni əldə etmək üçün həmin ünvandan başlayaraq neçə bayt götürməli olduğunuzu bilməlisiniz.
İkincisi, göstəricilər arifmetik əməliyyatları dəstəkləyir. Onları yerinə yetirmək üçün ölçüsünü bilməlisiniz.
+N əməliyyatı göstəricini N*sizeof(type) bayt irəli aparır.
Məsələn, əgər göstərici int *p; CC02 ünvanını saxlayır, sonra p += 10; o, CC02 + sizeof(int)*10 = CC02 + 28 = CC2A ünvanını saxlayacaq (Bütün əməliyyatlar onaltılıq formatda həyata keçirilir). Massivin əvvəlinə bir göstərici yaradaq. Bundan sonra biz fərdi elementlərə daxil olaraq bu massivdə "hərəkət edə" bilərik.

#daxildir #daxildir void main() ( int A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int *p; p = A; printf("%d\n", *p) p++; printf("%d\n", *p = p + 4; "%d\n", *p);

Diqqət yetirin, serialın birinci elementinin ünvanını necə əldə etmişik

Massiv mahiyyətcə göstəricinin özüdür, ona görə də & operatorundan istifadə etməyə ehtiyac yoxdur. Məsələni fərqli şəkildə yenidən yaza bilərik

Birinci elementin ünvanını əldə edin və ona nisbətdə massivdən keçin.
+ və - operatorlarına əlavə olaraq göstəricilər müqayisə əməliyyatlarını dəstəkləyir. Əgər a və b iki göstəricimiz varsa, onda a > b a-nı saxlayan ünvan b-ni saxlayan ünvandan böyükdürsə.

#daxildir #daxildir void main() ( int A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int *a, *b; a = b = printf("&A == %p\ n", a); printf("&A == %p\n", b); əgər (a< b) { printf("a < b"); } else { printf("b < a"); } getch(); }

Göstəricilər bərabərdirsə, onlar eyni yaddaş sahəsinə işarə edirlər.

Göstəriciyə göstərici

İndeks yaddaş sahəsinin ünvanını saxlayır. Siz göstəriciyə göstərici yarada bilərsiniz, sonra o, göstəricinin ünvanını saxlayacaq və onun məzmununa daxil ola bilər. Göstəriciyə göstərici kimi müəyyən edilir

<тип> **<имя>;

Aydındır ki, heç bir şey sizə bir göstəriciyə bir göstərici yaratmağınıza mane olmur və bir göstərici üçün bir göstərici və s. İki ölçülü və çoxölçülü massivlərlə işləyərkən buna ehtiyacımız olacaq. Göstəriciyə göstərici ilə necə işləyə biləcəyinizin sadə bir nümunəsidir.

#daxildir #daxildir #define SIZE 10 void main() ( int A; int B; int *p; int **pp; A = 10; B = 111; p = pp = printf("A = %d\n", A); *p = 20; printf("A = %d\n", A); //burada mötərizə yoxdur printf("A = %d\n", A); ("B = %d\n", *p **pp = 333 printf("B = %d", B);

Göstəricilər və tip tökmə

Göstərici ünvanı saxladığından, onu başqa bir növə köçürə bilərsiniz. Bu, məsələn, dəyişənin bir hissəsini götürmək istəsək və ya dəyişənin bizə lazım olan növü saxladığını bilsək lazım ola bilər.

#daxildir #daxildir #ölçüsü 10-u müəyyən et void main() ( int A = 10; int *intPtr; char *charPtr; intPtr = printf("%d\n", *intPtr); printf("----------- ----------\n"); charPtr = (char*)intPtr; printf("%d ", *charPtr); charPtr++; printf("%d ", *charPtr); charPtr++; printf ("%d ", *charPtr printf("%d ", *charPtr);

Bu nümunədə biz ölçü növündən faydalanırıq int 4 baytdır və char 1 bayt. Bunun sayəsində ilk baytın ünvanını aldıqdan sonra nömrənin qalan baytlarını keçib onların məzmununu göstərə bilərsiniz.

NULL göstərici - boş göstərici

Başlamadan əvvəl indeks hər hansı digər dəyişən kimi zibil saxlayır. Ancaq eyni zamanda, bu "zibil" etibarlı bir ünvan ola bilər. Məsələn, bir göstəricimiz var. Onun işə salınıb-başlanılmadığını necə öyrənə bilərəm? Ümumiyyətlə, yol yoxdur. Bu problemi həll etmək üçün stdlib kitabxanasının NULL makrosu təqdim edilmişdir.
Göstərici müəyyən edilərkən, əgər o, müəyyən bir dəyərlə işə salınmayıbsa, onu NULL-ə bərabərləşdirmək adətdir.

Int *ptr = NULL;

Standarta görə, bu halda göstəricinin bərabər olmasına zəmanət verilir NULL, və sıfırdır və boolean dəyər kimi istifadə edilə bilər yalan. Baxmayaraq ki, həyata keçirilməsindən asılı olaraq NULL 0-a bərabər ola bilməz (bu mənada bit şəklində təmsildə sıfıra bərabər deyil, məsələn: int və ya üzmək).
Bu o deməkdir ki, bu vəziyyətdə

Int *ptr = NULL; əgər (ptr == 0) ( ... )

olduqca düzgün əməliyyat və halda

Int a = 0; əgər (a == NULL) ( ... )

davranış müəyyən edilmir. Yəni göstəricini sıfırla və ya ilə müqayisə etmək olar NULL, amma edə bilməzsən NULL tam və ya üzən nöqtə tipli dəyişənlə müqayisə edin.

#daxildir #daxildir #daxildir void main() ( int *a = NULL; işarəsiz uzunluq, i; printf("Massivin uzunluğunu daxil edin: "); scanf("%d", &length); if (uzunluq > 0) ( //Yaddaş ayrıldıqda , göstərici qaytarır //Əgər yaddaş ayrılmayıbsa, onda ((a = (int*) malloc(length * sizeof(int))) != NULL) ( (i = 0; i) olduqda NULL qaytarılır.< length; i++) { a[i] = i * i; } } else { printf("Error: can"t allocate memory"); } } //Если переменая была инициализирована, то очищаем её if (a != NULL) { free(a); } getch(); }

Nümunələr

İndi göstəricilərlə işləməyin bəzi nümunələri
1. Massivdən keçək və bütün cüt elementləri tapaq.

#daxildir #daxildir void main() ( int A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int cüt; int evenCounter = 0; int *iter, *end; //iter massivin birinci elementini ünvanlayın //end massivin sonuncudan sonra növbəti "elementinin" ünvanını saxlayır (iter = A, end = iter< end; iter++) { if (*iter % 2 == 0) { even = *iter; } } //Выводим задом наперёд чётные числа for (--evenCounter; evenCounter >= 0; evenCounter--) ( printf("%d ", hətta); ) getch(); )

2. Elementləri çeşidləyərkən çox vaxt onları köçürməli oluruq. Bir obyekt çox yer tutursa, iki elementin dəyişdirilməsi bahalı olacaq. Bunun əvəzinə, siz orijinal elementlərə bir sıra göstəricilər yarada və onu çeşidləyə bilərsiniz. Göstəricilərin ölçüsü hədəf massivin elementlərinin ölçüsündən kiçik olduğundan, çeşidləmə daha sürətli olacaq. Bundan əlavə, massiv dəyişdirilməyəcək, bu çox vaxt vacibdir.

#daxildir #daxildir #define SIZE 10 void main() ( ikiqat çeşidlənməmiş = (1.0, 3.0, 2.0, 4.0, 5.0, 6.0, 8.0, 7.0, 9.0, 0.0); double *p; double *tmp; char flag = 1; unsigned i; printf("çeşidlənməmiş massiv\n" üçün (i = 0; i< SIZE; i++) { printf("%.2f ", unsorted[i]); } printf("\n"); //Сохраняем в массив p адреса элементов for (i = 0; i < SIZE; i++) { p[i] = &unsorted[i]; } do { flag = 0; for (i = 1; i

3. Daha maraqlı bir nümunə. Char tipinin ölçüsü həmişə 1 bayt olduğundan, dəyişdirmə əməliyyatını həyata keçirmək üçün istifadə edilə bilər - iki dəyişənin məzmununu dəyişdirmək.

Bu saytda AdBlock-u dayandırın.

Göstərici kompüter yaddaşında obyektin ünvanını saxlayan dəyişəndir, məsələn, başqa dəyişən. Biz daha əvvəl scanf funksiyasını öyrənəndə dəyişən ünvanlarla qarşılaşmışıq.

Beləliklə, sıra ilə gedək. Göstərici bəyannaməsi.

Göstərici bəyannaməsi dəyişən elanından yalnız növ adından sonra * simvolu əlavə etməklə fərqlənir. Nümunələr:

Siyahı 1.

int * p_g; // int double * p_f tipli dəyişənə göstərici; // double tipli dəyişənə göstərici

Siz təyinetmə operatorundan istifadə edərək göstəriciyə ünvan təyin edə bilərsiniz. Nümunələr:

Siyahı 2.

int n = 100; ikiqat PI = 3,1415926; int * p_k; // int double * p_pi tipli dəyişənə göstərici; // double p_k tipli dəyişənə göstərici = // n dəyişənin ünvanını əldə edin və onu p_k göstəricisinə təyin edin p_pi = // PI dəyişəninin ünvanını alın və onu p_pi göstəricisinə təyin edin

Göstərici dəyərini ekranda göstərmək üçün printf funksiyasında %p dəyişdiricisindən istifadə etməlisiniz. Misal:

Siyahı 3.

printf("adres peremennoi PI %p\n", p_pi);

Göstəricidə saxlanılan dəyişənin ünvanından istifadə edərək, həmin dəyişənin dəyərini dəyişə bilərsiniz. Bunu etmək üçün, referensiya əməliyyatından istifadə edin *. Bir nümunəyə nəzər salaq:

Siyahı 4.

#daxildir int main(void) ( int a = 100; int * p_a = // a dəyişəninin ünvanını göstəricidə saxlayın printf("a = %d\n", a); // dəyişənin qiymətini əldə etməyin standart yolu a printf("a = %d\n", *p_a // a dəyişəninin dəyərini ona göstərici vasitəsilə əldə edin // p_a göstəricisindən istifadə edərək, a *p_a = 50 dəyişəninə başqa qiymət yazın; a = %d\n", *p_a ); 0 qaytarın; )

Şək.1 Göstərici vasitəsilə dəyişənə daxil olmaq

Ümumilikdə, göstəricilərə münasibətdə * iki halda istifadə olunur:

  • göstəricini elan edərkən onun göstərici olduğunu göstərmək;
  • göstəricinin göstərdiyi dəyişənə daxil olmaq istəsək.

Null pointerNULL deyilən də var. Null göstərici heç nəyə istinad etmir. Göstəriciləri sıfıra sıfırlamaq üçün istifadə olunur. Məsələnə baxın.

Siyahı 5.

#daxildir int main(void) ( int a = 100; int * p_a = // a dəyişəninin ünvanını göstəricidə saxlayın printf("a = %d\n", a); // dəyişənin qiymətini əldə etməyin standart yolu a printf("a = %d\n", *p_a // a dəyişənin dəyərini ona göstərici vasitəsilə əldə edin // p_a göstəricisindən istifadə edərək, a *p_a = 50 dəyişəninə başqa bir dəyər yazın; a = %d\n", *p_a ); printf("%p\n", p_a); p_a = NULL; printf("%p\n", p_a);

Şəkil 2 Göstəricinin sıfırlanması

Ev

C dili nümunələri ilə

C-də funksiyalar

C-də funksiyalar nə üçündür?

C-də funksiyalar ümumi proqram çərçivəsində konkret hərəkətləri yerinə yetirmək üçün istifadə olunur. Funksiyalarda hansı hərəkətlərin göstəriləcəyinə proqramçı özü qərar verir. Təkrarlanan hərəkətlər üçün funksiyalardan istifadə etmək xüsusilə rahatdır.

C-də funksiyanın sadə nümunəsi

C-də funksiya nümunəsi:

Bu çox sadə bir C proqramıdır. Sadəcə olaraq "C-də funksiyalar" xəttini çap edir. Proqramın əsas adlı bir funksiyası var. Bu funksiyanı ətraflı nəzərdən keçirək. Funksiya başlığında, yəni. sırada

int funksiyanın qaytarılması növüdür;

main funksiyanın adıdır;

(void) funksiya arqumentlərinin siyahısıdır. Void sözü funksiyanın heç bir arqumentinin olmadığını göstərir;

return funksiyanın icrasını dayandıran və funksiyanın nəticəsini funksiyanın çağırıldığı nöqtəyə qaytaran ifadədir;

EXIT_SUCCESS sıfıra bərabər dəyərdir. stdlib.h faylında müəyyən edilmişdir;

başlıqdan sonra funksiyanın bir hissəsi, əyri mötərizələrə daxil edilir

{
puts("C-də funksiyalar");
EXIT_SUCCESS qaytarın;
}

funksiyanın bədəni adlanır.

Deməli, biz funksiya ilə işləyərkən funksiyanın adını göstərməliyik, bizim üçün bu, əsasdır, funksiyanın qaytardığı dəyərin növü, bizim üçün int, mötərizədən sonra arqumentlərin siyahısını verin. funksiyanın adı, bizim heç bir arqumentimiz yoxdur, ona görə də biz void yazırıq, funksiyanın gövdəsində bəzi hərəkətləri yerinə yetiririk (bunun üçün funksiya yaradılmışdır) və return ifadəsindən istifadə edərək funksiyanın nəticəsini qaytarırıq. C dilində funksiyalar haqqında bilməli olduğunuz əsaslar buradadır.

C-də bir funksiyadan başqa bir funksiyanı necə çağırmaq olar?

Gəlin C-də funksiyaların çağırılması nümunəsinə baxaq:

Biz onu işə salırıq və əldə edirik:

Bu nümunə iki tam ədəd əlavə edən və nəticəni qaytaran cəmi funksiyası yaradır. Bu funksiyanın strukturuna ətraflı nəzər salaq.

cəm funksiyasının başlığı:

int cəmi(int a, int b)

burada int funksiyanın qaytarılması növüdür;

sum funksiyanın adıdır;

(int a, int b) - mötərizədə funksiya adından sonra onun arqumentlərinin siyahısı verilir: birinci arqument int a, ikinci arqument int b. Arqument adları formaldır, yəni. Funksiyaya zəng edərkən a və b adlı dəyişənlərin qiymətlərini bu funksiyaya arqument kimi göndərməyimiz tələb olunmur. Əsas funksiyada biz cəm funksiyasını belə adlandırırıq: sum(d, e);. Lakin funksiyaya ötürülən arqumentlərin funksiyada elan edilən tipə uyğun olması vacibdir.

Cəm funksiyasının gövdəsində, yəni. Funksiya başlığından sonra əyri mötərizələrin içərisində int c lokal dəyişəni yaradırıq, ona a plus b cəminin qiymətini veririk və onu return ifadəsi ilə funksiyanın nəticəsi kimi qaytarırıq.

İndi baxaq görək cəm funksiyası əsas funksiyadan necə çağırılır.

Budur əsas funksiya:

Əvvəlcə iki int dəyişəni yaradırıq

int d = 1; int e = 2;

Onları arqument dəyərləri kimi cəmi funksiyasına keçirəcəyik.

int f = cəmi(d, e);

onun dəyəri cəmi funksiyasının nəticəsi olacaq, yəni. f dəyişəninə təyin etdiyimiz int dəyərini qaytaracaq sum funksiyasını çağırırıq. Biz d və f-ni arqument kimi keçirik. Ancaq funksiya başlığında cəmi

int cəmi(int a, int b)

arqumentlər a və b adlanır, onda niyə biz d və f-i keçirik? Çünki formal arqumentlər funksiyanın başlığında yazılır, yəni. Arqumentlərin adları vacib DEYİL, lakin onların növləri vacibdir. Sum funksiyası int tipli hər iki arqumentə malikdir, yəni bu funksiyanı çağırarkən siz int tipli iki arqumenti istənilən adla ötürməlisiniz.

Daha bir incəlik. Funksiya ilk çağırılmadan əvvəl elan edilməlidir. Bizim nümunəmizdə belə oldu: əvvəlcə cəm funksiyası elan edilir, sonra isə onu əsas funksiyadan çağırırıq. Əgər funksiya çağırıldığı yerdən sonra elan edilirsə, o zaman funksiya prototipindən istifadə edilməlidir.

C-də funksiya prototipi

Gəlin C-də bir funksiya nümunəsinə baxaq:

Bu misalda, cəm funksiyası əsas funksiyada çağırıldığı yerdə aşağıda müəyyən edilmişdir.

Bu halda siz cəm funksiyasının prototipindən istifadə etməlisiniz. Prototipimiz əsas funksiyanın üstündə elan edilmişdir:

int cəmi(int a, int b);

Prototip nöqtəli vergüllə bitən funksiya başlığıdır. Prototip aşağıda müəyyən ediləcək funksiyanın elanıdır. Məhz bunu etdik: biz bir funksiya prototipini elan etdik

int f = cəmi(d, e);

və əsas funksiyanın altında biz əvvəllər prototipdə elan edilmiş cəmi funksiyasını təyin edirik:

C-də funksiya elanı C-də funksiya tərifindən nə ilə fərqlənir?

Bir funksiya prototipi yazdıqda, məsələn, bu kimi:

int cəmi(int a, int b);

sonra funksiya elan edirik.

Və biz funksiyanı həyata keçirəndə, yəni. Biz yalnız başlığı deyil, həm də funksiyanın gövdəsini yazırıq, məsələn:

sonra funksiyanı təyin edirik.

geri qaytarma bəyanatı

Qaytarma ifadəsi C-də funksiyanı dayandırır və onun işinin nəticəsini çağırış nöqtəsinə qaytarır. Misal:

Bu funksiya sadələşdirilə bilər:

burada qaytarma ifadəsi a + b cəminin dəyərini qaytaracaq.

Bir funksiyada bir neçə qaytarma ifadəsi ola bilər. Misal:

Əgər nümunədə a arqumentinin dəyəri ikidən böyükdürsə, o zaman funksiya sıfırı (birinci hal) və “// Birinci hal;” şərhinin altındakı hər şeyi qaytaracaq. icra olunmayacaq.

C dilində göstəricilər

Əgər a ikidən azdırsa, lakin b sıfırdan azdırsa, funksiya öz işini və “// İkinci hal;” şərhinin altındakı hər şeyi tamamlayacaq. icra olunmayacaq.

Və yalnız əvvəlki hər iki şərt yerinə yetirilmədikdə, proqramın icrası son qaytarma ifadəsinə çatacaq və a + b cəmi qaytarılacaqdır.

Funksiya arqumentlərinin dəyərinə görə ötürülməsi

Arqumentlər qiymətə görə C funksiyasına ötürülə bilər. Misal:

Nümunədə əsas funksiyada int d = 10 dəyişəni yaradırıq. Bu dəyişəni qiymətə görə sum(d) funksiyasına keçirik. Cəm funksiyasının daxilində dəyişənin qiyməti 5 artırılır.Lakin əsas funksiyada d-nin qiyməti dəyişməyəcək, çünki o, dəyərlə keçib. Bu o deməkdir ki, dəyişənin özü deyil, dəyişənin dəyəri ötürülür. Bunu proqramın nəticəsi sübut edir:

olanlar. cəm funksiyasından qayıtdıqdan sonra d-nin qiyməti dəyişmədi, cəm funksiyasının daxilində isə dəyişdi.

C funksiyası göstəricilərini ötürmək

Bu dəyişənə göstəricini dəyişənin dəyəri əvəzinə funksiyaya arqument kimi ötürsəniz, bu dəyişənin dəyəri dəyişə bilər. Məsələn, proqramı bir qədər dəyişdirərək əvvəlki bölmədən götürürük:

Proqramın bu versiyasında mən arqumentin dəyər üzrə ötürülməsindən göstəricinin dəyişənə ötürülməsinə keçdim. Gəlin bu məqama daha yaxından nəzər salaq.

printf("cəm = %d\n", cəmi(&d));

Cəm funksiyasına ötürülən d dəyişəninin 10-a bərabər qiyməti deyil, bu dəyişənin ünvanıdır, məsələn:

İndi isə cəmi funksiyasına baxaq:

Onun arqumenti int üçün göstəricidir. Biz bilirik ki, göstərici hansısa obyektin ünvanı olan dəyişəndir. D dəyişəninin ünvanı cəmi funksiyasına göndərilir:

Cəmin daxilində int *a göstəricisinə istinad edilir. Bu, bizə göstəricidən göstəricimizin işarə etdiyi dəyişənin özünə keçməyə imkan verir. Və bizim vəziyyətimizdə bu, d dəyişənidir, yəni. ifadə

ifadəsinə bərabərdir

Nəticə: cəm funksiyası d dəyişəninin qiymətini dəyişir:

Bu dəfə d dəyəri cəmdən qayıtdıqdan sonra dəyişir, biz arqumenti dəyər üzrə ötürəndə əvvəlki abzasda müşahidə edilməmişdi.

Eclipse-də C/C++

Bu məqalə üçün bütün nümunələri Eclipse-də etdim. Eclipse-də C/C++ ilə necə işləməyi burada tapa bilərsiniz. Fərqli bir mühitdə işləyirsinizsə, nümunələr orada da işləyəcək.

Teqlər: C göstəriciləri. Göstərici üçün göstərici. Göstərici növü. Göstərici arifmetik. Göstəricilərin müqayisəsi.

Yol göstəriciləri

Bu, bəlkə də bütün kursun ən çətin və ən vacib mövzusudur. Göstəriciləri başa düşmədən C dilini daha da öyrənmək mənasız olacaq. Yol nişanları çox sadə anlayışdır, çox məntiqlidir, lakin təfərrüata diqqət tələb edir.

Tərif

Göstərici yaddaş yerinin ünvanını saxlayan dəyişəndir.

Mövzu 7. C dilində göstəricilər.

Göstərici, dəyişən kimi, bir növü var. Göstəriciləri elan etmək üçün sintaksis

<тип> *<имя>;

Məsələn
Göstəricilərlə işləmək üçün iki əsas operator & ünvan operatoru və * dereference operatorudur. Sadə bir misala baxaq.

#daxildir #daxildir void main() ( int A = 100; int *p; //A dəyişəninin ünvanını alın p = //A dəyişəninin ünvanını çıxarın printf("%p\n", p); //Məzmununu çıxarın dəyişən A printf("% d\n", *p); //A dəyişəninin məzmununu dəyişdirin printf("%d", *p);

Kodu yenidən diqqətlə nəzərdən keçirək

adlı dəyişən A. O, yaddaşda hansısa ünvanda yerləşir. 100 dəyəri bu ünvanda saxlanılır.

Növ göstəricisi yaradıldı int.

İndi dəyişən səh dəyişənin ünvanını saxlayır A. * operatorundan istifadə edərək dəyişənin məzmununa daxil oluruq A.
Məzmunu dəyişdirmək üçün yazın

Bundan sonra dəyər A eyni yaddaş sahəsinə işarə etdiyi üçün də dəyişdi. Mürəkkəb bir şey yoxdur.
İndi başqa bir vacib nümunə

#daxildir #daxildir void main() ( int A = 100; int *a = ikiqat B = 2.3; double *b = printf("%d\n", sizeof(A)); printf("%d\n", sizeof(a) )); printf("%d\n", sizeof(B))("%d\n", sizeof(b));

Göstəriləcək
Dəyişənlərin müxtəlif növləri və ölçüləri olsa da, onlara işarə edənlər eyni ölçüyə malikdir. Həqiqətən, əgər göstəricilər ünvanları saxlayırsa, onda onlar tam ədəd olmalıdır. Düzdür, göstərici özü kimi dəyişəndə ​​saxlanılır size_t(və həmçinin ptrdiff_t), bu, tam ədəd kimi davranan bir növdür, lakin onun ölçüsü sistemin bit tutumundan asılıdır. Əksər hallarda onların arasında heç bir fərq yoxdur. Niyə göstəriciyə tip lazımdır?

Göstərici arifmetikası

Əvvəla, istinadın ləğvi əməliyyatının (ünvana görə məzmunun əldə edilməsi) düzgün işləməsi üçün göstəriciyə bir növ lazımdır. Göstərici dəyişənin ünvanını saxlayırsa, bütün dəyişəni əldə etmək üçün həmin ünvandan başlayaraq neçə bayt götürməli olduğunuzu bilməlisiniz.
İkincisi, göstəricilər arifmetik əməliyyatları dəstəkləyir.

Onları yerinə yetirmək üçün ölçüsünü bilməlisiniz.
Əməliyyat göstəricini bayt irəli aparır.
Məsələn, əgər göstərici int *p; CC02 ünvanını saxlayır, sonra p += 10; o, CC02 + sizeof(int)*10 = CC02 + 28 = CC2A ünvanını saxlayacaq (Bütün əməliyyatlar onaltılıq formatda həyata keçirilir). Massivin əvvəlinə bir göstərici yaradaq. Daha sonra fərdi elementlərə daxil olaraq bu massivdə “hərəkət edə” bilərik.

#daxildir #daxildir void main() ( int A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int *p; p = A; printf("%d\n", *p) p++; printf("%d\n", *p = p + 4; "%d\n", *p);

Diqqət yetirin, serialın birinci elementinin ünvanını necə əldə etmişik

Massiv mahiyyətcə göstəricinin özüdür, ona görə də & operatorundan istifadə etməyə ehtiyac yoxdur. Məsələni fərqli şəkildə yenidən yaza bilərik

Birinci elementin ünvanını əldə edin və ona nisbətdə massivdən keçin.
+ və - operatorlarına əlavə olaraq göstəricilər müqayisə əməliyyatlarını dəstəkləyir. Əgər a və b iki göstəricimiz varsa, onda a > b a-nı saxlayan ünvan b-ni saxlayan ünvandan böyükdürsə.

#daxildir #daxildir void main() ( int A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int *a, *b; a = b = printf(“&A == %p\ n", a); printf("&A == %p\n", b);< b) { printf(«a < b»); } else { printf(«b < a»); } getch(); }

Göstəricilər bərabərdirsə, onlar eyni yaddaş sahəsinə işarə edirlər.

Göstəriciyə göstərici

Göstərici yaddaş sahəsinin ünvanını saxlayır. Siz göstəriciyə göstərici yarada bilərsiniz, sonra o, göstəricinin ünvanını saxlayacaq və onun məzmununa daxil ola bilər. Göstəriciyə göstərici kimi müəyyən edilir

<тип> **<имя>;

Aydındır ki, heç bir şey sizə bir göstəriciyə bir göstərici yaratmağınıza mane olmur və bir göstərici üçün bir göstərici və s. İki ölçülü və çoxölçülü massivlərlə işləyərkən buna ehtiyacımız olacaq. Göstəriciyə göstərici ilə necə işləyə biləcəyinizin sadə bir nümunəsidir.

#daxildir #daxildir #define SIZE 10 void main() ( int A; int B; int *p; int **pp; A = 10; B = 111; p = pp = printf("A = %d\n", A); *p = 20; printf("A = %d\n", A = printf("B = %d", *p = 333; B);

Göstəricilər və tip tökmə

Göstərici ünvanı saxladığından, onu başqa bir növə köçürə bilərsiniz.

Bu, məsələn, dəyişənin bir hissəsini götürmək istəsək və ya dəyişənin bizə lazım olan növü saxladığını bilsək lazım ola bilər.

#daxildir #daxildir #ölçüsü 10-u müəyyən et void main() ( int A = 10; int *intPtr; char *charPtr; intPtr = printf(“%d\n”, *intPtr); printf(“———————\n” charPtr = (char*)intPtr("%d ", *charPtr++ ("%d ", *charPtr);

Bu nümunədə biz ölçü növündən faydalanırıq int 4 baytdır və char 1 bayt. Bunun sayəsində ilk baytın ünvanını aldıqdan sonra nömrənin qalan baytlarını keçib onların məzmununu göstərə bilərsiniz.

NULL göstərici - boş göstərici

Başlamadan əvvəl göstərici hər hansı digər dəyişən kimi zibil saxlayır. Ancaq eyni zamanda, bu "zibil" etibarlı bir ünvan ola bilər. Məsələn, bir göstəricimiz var. Onun işə salınıb-başlanılmadığını necə öyrənə bilərəm? Ümumiyyətlə, yol yoxdur. Bu problemi həll etmək üçün stdlib kitabxanasının NULL makrosu təqdim edilmişdir.
Göstərici müəyyən edilərkən, əgər o, müəyyən bir dəyərlə işə salınmayıbsa, onu NULL-ə bərabərləşdirmək adətdir.

int *ptr = NULL;

Standarta görə, bu halda göstəricinin bərabər olmasına zəmanət verilir NULL, və sıfırdır və boolean dəyər kimi istifadə edilə bilər yalan. Baxmayaraq ki, həyata keçirilməsindən asılı olaraq NULL 0-a bərabər ola bilməz (bu mənada bit şəklində təmsildə sıfıra bərabər deyil, məsələn: int və ya üzmək).
Bu o deməkdir ki, bu vəziyyətdə

int *ptr = NULL; əgər (ptr == 0) (… )

olduqca düzgün əməliyyat və halda

int a = 0; əgər (a == NULL) (… )

davranış müəyyən edilmir. Yəni göstəricini sıfırla və ya ilə müqayisə etmək olar NULL, amma edə bilməzsən NULL tam və ya üzən nöqtə tipli dəyişənlə müqayisə edin.

#daxildir #daxildir #daxildir void main() ( int *a = NULL; işarəsiz uzunluq, i; printf("Massivin uzunluğunu daxil edin: "); scanf("%d", &length); if (uzunluq > 0) ( //Yaddaş ayrıldıqda , göstərici qaytarır //Əgər yaddaş ayrılmayıbsa, onda ((a = (int*) malloc(length * sizeof(int))) != NULL) ( (i = 0; i) olduqda NULL qaytarılır.< length; i++) { a[i] = i * i; } } else { printf(«Error: can’t allocate memory»); } } //Если переменая была инициализирована, то очищаем её if (a != NULL) { free(a); } getch(); }

Nümunələr

İndi göstəricilərlə işləməyin bəzi nümunələri
1. Massivdən keçək və bütün cüt elementləri tapaq.

#daxildir #daxildir void main() ( int A = (1, 2, 3, 4, 5, 6, 7, 8, 9, 10); int cüt; int evenCounter = 0; int *iter, *end; //iter massivin birinci elementini ünvanlayın //end massivin sonuncudan sonra növbəti “elementinin” ünvanını saxlayır (iter = A, end = iter< end; iter++) { if (*iter % 2 == 0) { even = *iter; } } //Выводим задом наперёд чётные числа for (—evenCounter; evenCounter >= 0; evenCounter—) ( printf("%d ", hətta); ) getch(); )

2. Elementləri çeşidləyərkən çox vaxt onları köçürməli oluruq. Bir obyekt çox yer tutursa, iki elementin dəyişdirilməsi bahalı olacaq. Bunun əvəzinə, siz orijinal elementlərə bir sıra göstəricilər yarada və onu çeşidləyə bilərsiniz. Göstəricilərin ölçüsü hədəf massivin elementlərinin ölçüsündən kiçik olduğundan, çeşidləmə daha sürətli olacaq. Bundan əlavə, massiv dəyişdirilməyəcək, bu çox vaxt vacibdir.

#daxildir #daxildir #define SIZE 10 void main() ( ikiqat çeşidlənməmiş = (1.0, 3.0, 2.0, 4.0, 5.0, 6.0, 8.0, 7.0, 9.0, 0.0); double *p; double *tmp; char flag = 1; unsigned i; printf("çeşidlənməmiş massiv\n" üçün (i = 0; i< SIZE; i++) { printf(«%.2f «, unsorted[i]); } printf(«\n»); //Сохраняем в массив p адреса элементов for (i = 0; i < SIZE; i++) { p[i] = &unsorted[i]; } do { flag = 0; for (i = 1; i

3. Daha maraqlı bir nümunə. Char tipinin ölçüsü həmişə 1 bayt olduğundan, dəyişdirmə əməliyyatını həyata keçirmək üçün istifadə edilə bilər - iki dəyişənin məzmununu dəyişdirmək.

#daxildir #daxildir #daxildir void main() ( int uzunluğu; char *p1, *p2; char tmp; float a = 5.0f; float b = 3.0f; printf(“a = %.3f\n”, a); printf(“b = %.3f\n", b); p1 = (char*) p2 = (char*) // Hərəkət etmək üçün neçə bayt tapın length = sizeof(float) ( // Dəyişənlərin məzmununu dəyişdirin bayt bayt = * p1; *p1 = *p2; p1++;

Bu nümunədə siz dəyişənlərin növünü dəyişə bilərsiniz ab haqqında ikiqat və ya hər hansı digər (çıxışda və zəngdə müvafiq dəyişikliklə sizeof), biz hələ də iki dəyişənin baytlarını dəyişdirəcəyik.

4. #include göstəricisindən istifadə edərək istifadəçinin daxil etdiyi sətir uzunluğunu tapın #daxildir void main() ( char bufer; char *p; işarəsiz uzunluq = 0; scanf("%127s", bufer); p = bufer; while (*p != '\0') ( p++; uzunluq++; ) printf( "uzunluq = %d", getch();

Kod bölməsinə diqqət yetirin

isə (*p != ‘\0’) ( p++; uzunluq++; )

yenidən yazmaq olar

while (*p != 0) ( p++; length++; ) və ya while (*p) ( p++; length++; )

və ya vəziyyətdəki artımı silməklə

isə (*p++) (uzunluq++; )

ru-Cyrl18-dərslikSypachev [email protected]

Göstərici başqa dəyişənin ünvanını saxlayan xüsusi dəyişəndir. Göstərici aşağıdakı kimi elan edilir: tip* dəyişən; Harada növü- istənilən etibarlı sadə və ya mürəkkəb əsas göstərici növü.

Məsələn, tutaq ki, müntəzəm dəyişən elan edilib int t; Bəyannamə və inisializasiya int* p= aşağıdakı deməkdir. Dəyişəndə səh Saxlanılacaq şey proqram tərəfindən işlənmiş tam ədəd deyil (tələbə qiyməti, istehsal olunan məhsulların sayı və s.), lakin müəyyən edilmiş tip (tam ədəd) məlumatını ehtiva edən xananın ünvanıdır. Ünvan dedikdə dəyişən üçün ayrılmış RAM bölməsinin birinci baytının nömrəsi nəzərdə tutulur. Göstərici olmayan dəyişənlər üçün əlavə bəyannamə olmadan ünvan sistem tərəfindən də yadda saxlanılır və ondan istifadə etməklə əldə edilə bilər. və əməliyyatlar (addressing), Məsələn , &t. Bəzən “ünvan alma” adlanan bu vahid əməliyyat dəyişənin dəyəri ilə heç nə etmir. t.

İlk istifadədən əvvəl göstərici dəyişəni olmalıdır işə salındı. Göstəricinin dəyərini təyin edənə qədər, o, yaddaşda təsadüfi bir şeyə istinad edir və onun istifadəsi gözlənilməz nəticələrə səbəb ola bilər.

Metodlardan biri yuxarıda göstərilmişdir və dəyişəndə ​​olan deməkdir səh hüceyrə ünvanı yerləşdirilir t. Bunu başa düşmək vacibdir int* p= ekvivalent int* p; p=&t; və yox *p=&t; Bu, göstəricilərin öyrənilməsinin ilkin mərhələsinin çətinliklərindən biridir. Bu mövzu eyni simvolun olması ilə daha da mürəkkəbləşir. & ” istinad tipli dəyişəni elan edərkən istifadə olunur.

C-də göstəricilər.

Burada bu simvol müəyyən edir ünvan alma əməliyyatı dəyişən üçün və istinad növü ilə heç bir əlaqəsi yoxdur.

Qeyd edək ki, göstəriciləri elan edərkən boşluqların yerləşdirilməsi pulsuzdur. Aşağıdakı girişlər də məqbuldur: int * p= &t; int *p= Paraqrafın əvvəlindəki yazılara üstünlük verilməlidir, onlardan indeksin mənasını başa düşmək daha asandır. Dəyişən elan edilir səh, yox *səh, və əlavə olaraq, növüdür int*, yox int.

Əgər eyni vaxtda bir neçə göstərici elan edilirsə, onda hər dəyişəndən əvvəl “*” simvolu yazılmalıdır: float* q1, *q2;

Ünvanı olan xananın məzmunu səh, proqramın mətnində ilə göstərilir əməliyyatlar istinaddan imtina . O, göstərici dəyişənini elan edərkən eyni “*” simvolundan istifadə edir. Bu birlik əməliyyat müəyyən ünvanda yerləşən dəyişənin dəyərini qaytarır. Buna görə *səhünvanı göstərici dəyişənində olan xanada yerləşən proqram tərəfindən işlənmiş tam ədəddir səh. başlatma nəzərə alınmaqla ( p = &t) *səht- bu eyni mənadır. Bu o deməkdir ki, istifadə edirsinizsə cin>>t; məsələn, 2 rəqəmini daxil edək və icra edək *p*=5; və ya *p=*p*5; sonra dəyəri dəyişəcək t, heç bir açıq-aydın dəyişiklik görünməsə də. Buna görə də operator cout<< t; 10 rəqəmini çıxaracaq (2*5). Və əksinə, dəyişir t(Məsələn, t++;), bununla da dəyəri dəyişəcəyik *səh.İstifadə etməklə cout<<(*p); 11 çap edək.

Yuxarıdakıları aşağıdakı kimi ifadə edəcəyik:

səh(və ya &t) *səh(və ya t)

“Sol düzbucaqlı” (yaddaş xanası) ünvan, “sağ” xana isə emal olunan tam ədədi ehtiva edir.

Burada müzakirə edilən “&” və “*” əməliyyatları birdir və oxşar ikili əməliyyatlar “bit və” və arifmetik vurma əməliyyatlarından daha yüksək prioritetə ​​malikdir.

üçün *səh Eyni əməliyyatlar müəyyən edilmiş tipli dəyişən üçün, lakin tam ədədlər üçün müəyyən edilir. Buna görə də, məsələn, aşağıdakı operatorlar məqbuldur: a) cin>>(*p); b) int r; r=*p*2; c) əgər (*p%2)…; d) cout<<(*p);.

Siz həmçinin göstərici dəyişəninin dəyərini göstərə bilərsiniz. cout<Ünvanı onaltılıq notasiyada göstərir. Bununla belə, eyni proqram təkrar-təkrar icra edildikdə, bu, mütləq eyni olmayacaq.

⇐ Əvvəlki567891011121314Sonrakı ⇒

Dərc tarixi: 2015-02-18; Oxunub: 526 | Səhifənin müəllif hüquqlarının pozulması

Studopedia.org - Studopedia.Org - 2014-2018 (0,001 s)…

— İşçiyə göstərici. Bu göstəriciyə bir ayrılmış obyekt və ya sizin vəziyyətinizdə bir neçə (massiv sintaksisi ilə) təyin edə bilərsiniz. Beləliklə, bir sıra işçilərə işarə edir.

Siz bu göstəriciyə istinad etdiniz.

Qeydlər və fərziyyələr

Bir sıra (birdən çox) işçiyə işarə etdiyi üçün ilk girişə də işarə edir. Daha sonra siz hələ də mümkün olan tam üzv dəyişəninə daxil olursunuz. Lakin sonra siz tam dəyərdə massiv indeksi operatorundan() istifadə etməyə çalışırsınız, bu mümkün deyil.

Yəqin ki, siz ayrılmış massivinizin ci girişinin üzv dəyişəninə daxil olmaq istəyirdiniz. Beləliklə, siz bunu çevirməlisiniz: əvvəlcə massiv indeksi operatorundan istifadə edin, sonra həmin işçinin üzvünə daxil olun.

aşağı səviyyəli sözlər deməkdir: göstərici götürün, göstərilən növün ölçüsünü əlavə edin (belə ki, o, --ci girişə işarə etsin) və həmin ünvana istinad edin göstərici).

Sonra həmin işçinin üzvünə daxil olmaq istəyirsiniz.

Əgər o, hələ də göstərici idisə, siz ox operatorundan istifadə etməli olacaqsınız, lakin siz massiv indeksi operatorundan() istifadə etdiyinizə görə artıq onun istinadını ləğv etmisiniz, deməli, nöqtə operatoru düzgündür:

C dilini öyrənərkən, yeni başlayanların tez-tez göstəricilərlə bağlı sualları olur, düşünürəm ki, hər kəsin təxminən eyni sualları var, ona görə də mənim üçün yarananları təsvir edəcəyəm.

Göstərici nə üçündür?

Niyə həmişə “tip göstəricisi” yazılır və tip göstəricisi nədir? uint16_t tip göstəricidən fərqlidir uint8_t?

Bəs indeksi kim hazırladı?

Bu suallara cavab verməzdən əvvəl göstəricinin nə olduğunu xatırlayaq.
Göstərici bəzi verilənlər elementinin (dəyişən, sabit, funksiya, struktur) ünvanını ehtiva edən dəyişəndir.

Dəyişənləri göstərici kimi elan etmək üçün onun adından əvvəl yazmalısınız * , və dəyişənin ünvanını almaq üçün istifadə olunur & (bir ünvan alma operatoru).
char a = "a"; char *p =
Bu halda p a dəyişəninin ünvanını ehtiva edir. Amma maraqlısı budur göstərici ilə sonrakı iş üçün ulduz yazmağa ehtiyac yoxdur, yalnız elan edərkən lazımdır.
char a = "a"; char b = "b"; char *p = p =
Bu halda p, b dəyişəninin ünvanını ehtiva edir, lakin bu ünvanda yerləşən dəyəri əldə etmək istəyiriksə, dereference operatorundan istifadə etməliyik, eyni ulduz *.
char new_simbol = 0; char a = "a"; char *p = new_simbol = *p;
Beləliklə, new_simbol dəyişəni "a" simvolunun ascii kodunu ehtiva edəcəkdir.

İndi isə birbaşa göstəricinin nə üçün lazım olduğu suallarına keçək. Təsəvvür edin ki, funksiyada işləmək istədiyimiz massivimiz var. Massivi funksiyaya ötürmək üçün onu, yəni MK-da onsuz da az olan yaddaşı nüsxələmək lazımdır, ona görə də daha düzgün həll massivi kopyalamaq deyil, onun ünvanını ötürmək olardı. birinci element və ölçü.
m =(1,2,3...);
Bunu belə edə bilərsiniz
void foo(char *m, uint8_t ölçüsü) ( )
və ya belə
void foo(char m, uint8_t ölçüsü) ( )
Massivin adı onun ilk elementinin ünvanını ehtiva etdiyi üçün o, göstəricidən başqa bir şey deyil. Sadə arifmetik əməliyyatlardan istifadə edərək massivdə hərəkət edə bilərsiniz, məsələn, massivin beşinci elementinin qiymətini əldə etmək üçün massivin ünvanına (birinci elementin ünvanı) 4 əlavə etməli və qeyd operatorunu tətbiq etməlisiniz. .
m = *(m + 4);

Və dərhal sual yaranır ki, niyə hər yerdə göstəricidən əvvəl tip yazırlar? Hər şey sadədir, massivin birinci elementinin ünvanını və massivin ölçüsünü keçərək deyirik: Buradan (göstərici) 10 deşik (massivin ölçüsü) qazırıq, iki saata çatırıq və güman edilənlər traktor deyilən çuxurları qazmağa və çuxur qazırlar. Bu vəziyyətə düşməmək üçün bənzətməmizdə çuxurun ölçüsünü müəyyən etmək lazım idi, göstəricinin növü bir dəyişənin yaddaşda neçə bayt tutacağını müəyyənləşdirir;

Beləliklə, göstərici tipini göstərərək kompilyatora deyirik, burada massivin başlanğıcının ünvanıdır, massivin bir elementi 2 bayt tutur, massivdə 10 belə element var, ona görə nə qədər yaddaş ayırmalıyıq bu massiv? 20 bayt - kompilyator cavab verir. Aydınlıq üçün götürək void tipli göstəricidir, onun nə qədər yer tutduğu müəyyən edilmir- bu sadəcə bir ünvandır, onu müxtəlif tipli göstəricilərə endirək və yönləndirmə əməliyyatını yerinə yetirək.


Siz həmçinin funksiyaya struktura göstərici ötürə bilərsiniz. Strukturun işarələnməsi məlum olduğu üçün bizə yalnız onun başlanğıcının ünvanını ötürmək kifayətdir və kompilyator özü onu sahələrə böləcək.

Yaxşı, son sual bu göstəricini kimin icad etdiyidir. Bu məsələni başa düşmək üçün bir assemblerə müraciət etməliyik, məsələn AVR və orada təlimatlar tapacağıq
st X, r1 ;r1 məzmununu SRAM-da X ünvanında saxlayın, burada X r26, r27 ld r1,X registrləri cütüdür; SRAM məzmununu X ünvanında r1-ə yükləyin, burada X r26, r27 cüt registrləridir
X ehtiva etdiyi aydın olur göstərici(ünvan) və belə çıxır ki, hər kəsi aldatmaq üçün bir göstərici ilə gələn heç bir pis adam yoxdur, göstəricilərlə (ünvanlarla) işləmək MK kernel səviyyəsində dəstəklənir.

Göstərici obyektin ünvanını ehtiva edən dəyişəndir. Göstərici obyektin məzmunu haqqında məlumat daşımır, lakin obyektin harada yerləşdiyi barədə məlumatları ehtiva edir.

Göstəricilər yaddaşdakı yerlərə istinad edən etiketlər kimidir. Onların da ünvanı var və onların dəyəri başqa dəyişənin ünvanıdır. Göstərici kimi elan edilən dəyişən RAM-da 4 bayt tutur (kompilyatorun 32-bit versiyası olduqda).

Göstərici sintaksisi

*ObyektAdı yazın;

Göstəricinin növü, ünvanı ehtiva edən dəyişənin növüdür. C dilində göstəricilərlə işləmək üçün iki əməliyyat müəyyən edilir:

  • əməliyyat * (ulduz işarəsi) - obyektin qiymətini onun ünvanı ilə əldə etməyə imkan verir - göstəricidə olan ünvanda olan dəyişənin qiymətini təyin edir;
  • əməliyyat & (ampersand) - dəyişənin ünvanını təyin etməyə imkan verir.

Məsələn:

Сhar c; // dəyişən char *p; // göstərici p = // p = ünvan c

Göstəricinin elan edilməsi, dəyişənin ünvanının alınması

Bir dəyişənə istinad edəcək göstərici elan etmək üçün əvvəlcə həmin dəyişənin ünvanını əldə etməlisiniz. Dəyişənin yaddaş ünvanını əldə etmək üçün dəyişənin adından əvvəl “&” işarəsindən istifadə etməlisiniz. Bu, dəyişənin dəyərinin saxlandığı yaddaş xanasının ünvanını öyrənməyə imkan verir. Bu əməliyyat ünvan alma əməliyyatı adlanır:

Int var = 5; // ilkin başlatma int *ptrVar ilə dəyişənin sadə elanı; // göstərici elan etdi, lakin o, hələ heç nəyə işarə etmir ptrVar = // indi göstəricimiz yaddaşda 5 rəqəminin saxlandığı ünvana istinad edir.

Göstəriciyə göstərici

Göstərici yaddaş sahəsinin ünvanını saxlayır. Siz göstəriciyə göstərici yarada bilərsiniz, sonra o, göstəricinin ünvanını saxlayacaq və onun məzmununa daxil ola bilər. Göstəriciyə göstərici aşağıdakı kimi müəyyən edilir:

<тип> **<имя>;

Göstəriciyə göstəricinin necə işləməsinə bir nümunə:

#daxildir #daxildir #define SIZE 10 void main() ( int A; int B; int *p; int **pp; A = 10; B = 111; p = pp = printf("A = %d\n", A); *p = 20; printf("A = %d\n", A); //burada mötərizə yoxdur printf("A = %d\n", A); ("B = %d\n", *p **pp = 333 printf("B = %d", B);

Göstəricilər və tip tökmə

Göstərici ünvanı saxladığından, onu başqa bir növə köçürə bilərsiniz. Əgər dəyişənin bir hissəsini götürməli olsaq və ya dəyişənin bizə lazım olan növü saxladığını bilsək, bu lazım ola bilər.

Aşağıdakı misalda int tipinin ölçüsünün 4 bayt, char isə 1 bayt olmasından istifadə edirik. Bunun sayəsində ilk baytın ünvanını aldıqdan sonra nömrənin qalan baytlarını keçib onların məzmununu göstərə bilərsiniz.

#daxildir #daxildir #ölçüsü 10-u müəyyən et void main() ( int A = 10; int *intPtr; char *charPtr; intPtr = printf("%d\n", *intPtr); printf("----------- ----------\n"); charPtr = (char*)intPtr; printf("%d ", *charPtr); charPtr++; printf("%d ", *charPtr); charPtr++; printf ("%d ", *charPtr printf("%d ", *charPtr);

NULL göstərici - boş göstərici

Başlamadan əvvəl göstərici hər hansı digər dəyişən kimi zibil saxlayır. Ancaq eyni zamanda, bu "zibil" etibarlı bir ünvan ola bilər. Məsələn, bir göstərici var. Onun işə salınıb-başlanılmadığını necə öyrənə bilərəm? Ümumiyyətlə, yol yoxdur. Bu problemi həll etmək üçün stdlib kitabxanasının NULL makrosu təqdim edilmişdir.

Göstərici müəyyən edilərkən, əgər o, müəyyən bir dəyərlə işə salınmayıbsa, onu NULL-ə bərabərləşdirmək adətdir.

Int *ptr = NULL;

Standart zəmanət verir ki, bu halda göstərici NULL olub, sıfıra bərabərdir və Boolean false dəyəri kimi istifadə edilə bilər. Baxmayaraq ki, həyata keçirilməsindən asılı olaraq NULL 0-a bərabər olmaya bilər. Yəni göstərici sıfırla və ya NULL ilə müqayisə oluna bilər, lakin NULL tam ədədli və ya üzən nöqtə tipli dəyişənlə müqayisə edilə bilməz.

Əksər proqramçılar obyektlər və onlara işarə edənlər arasındakı fərqi başa düşsələr belə, bəzən obyektə daxil olmaq üçün hansı yolun seçilməli olduğu tam aydın olmur. Aşağıda bu suala cavab verməyə çalışdıq.

Sual

Diqqət etdim ki, kodunu gördüyüm proqramçılar tez-tez bu obyektlərin özlərindən daha çox obyektlərə göstəricilərdən istifadə edirlər, məsələn, aşağıdakı konstruksiyadan istifadə edirlər:

Obyekt *myObject = yeni Obyekt;

Obyekt myObject;

Metodlarla eyni. Bunun əvəzinə niyə:

MyObject.testFunc();

bunu yazmalıyıq:

MyObject->testFunc();

Anladığım kimi, bu, sürət qazandırır, çünki... yaddaşa birbaşa daxil oluruq. Düzdür? P.S. Java-dan keçdim.

Cavab verin

Yeri gəlmişkən, qeyd edək ki, Java-da göstəricilər açıq şəkildə istifadə edilmir, yəni. Proqramçı koddakı obyektə ona işarə edən bir işarə vasitəsilə daxil ola bilməz. Bununla belə, reallıqda Java-da əsas növlərdən başqa bütün növlər istinad tipləridir: onlara istinadla daxil olurlar, baxmayaraq ki, istinadla parametri açıq şəkildə ötürmək mümkün deyil. Həm də qeyd edək ki, C++ və Java və ya C#-da yeniliklər tamamilə fərqli şeylərdir.

C++-da hansı göstəricilərin olması barədə bir az fikir vermək üçün burada iki oxşar kod fraqmenti var:

Obyekt obyekt1 = yeni Obyekt(); // Yeni obyekt Obyekt obyekti2 = yeni Obyekt(); // Başqa bir yeni obyekt obyekt1 = obyekt2 // Hər iki dəyişən əvvəllər obyekt2 tərəfindən istinad edilən obyektə istinad edir // Əgər obyekt1 tərəfindən istinad edilən obyekt dəyişirsə, // obyekt2 də dəyişəcək, çünki onlar eyni obyektdir.

C++ dilində ən yaxın ekvivalent:

Obyekt * obyekt1 = yeni Obyekt(); // Yaddaş yeni obyekt üçün ayrılıb // Bu yaddaş obyekt1 tərəfindən istinad edilir Obyekt * obyekt2 = new Object(); // İkinci obyekt silmək obyekt1 ilə eyni; // C++-da zibil toplama sistemi yoxdur, ona görə də bu edilməsə, // proqram artıq bu yaddaşa daxil ola bilməyəcək, // ən azı proqram yenidən işə salınana qədər // Buna yaddaş sızması obyekti deyilir1 = obyekt2; // Java-da olduğu kimi, obyekt1 obyekt2 ilə eyni yerə işarə edir

Ancaq bu tamamilə fərqli bir şeydir (C++):

Obyekt obyekt1; // Yeni obyekt Obyekt obyekti2; // Başqa bir obyekt1 = obyekt2 // Göstəricini yenidən təyin etmək əvəzinə, obyekt2-ni obyekt1-ə tam surətdə köçürmək çox bahalı əməliyyatdır

Bəs biz birbaşa yaddaşa daxil olmaqla sürət qazanacağıq?

Düzünü desək, bu sual iki fərqli sualı birləşdirir. Birincisi: dinamik yaddaş bölgüsündən nə vaxt istifadə etməlisiniz? İkincisi: göstəricilərdən nə vaxt istifadə etməlisiniz? Təbii ki, burada ümumi sözlər olmadan edə bilmərik ki, həmişə iş üçün ən uyğun aləti seçmək lazımdır. Əl ilə dinamik ayırmadan istifadə etməkdən daha yaxşı tətbiq demək olar ki, həmişə var (dinamik bölgü) və/və ya xam göstəricilər.

Dinamik paylama

Sualın mətni obyekt yaratmağın iki yolunu təqdim edir. Və əsas fərq onların ömrüdür (saxlama müddəti) proqram yaddaşında. MyObject Obyektindən istifadə; , siz avtomatik ömür boyu aşkarlanmasına etibar edirsiniz və obyekt əhatə dairəsini tərk edən kimi məhv ediləcək. Lakin Obyekt *myObject = yeni Obyekt; sil əmri ilə onu yaddaşdan əl ilə silənə qədər obyekti canlı saxlayır. Sonuncu variantdan yalnız həqiqətən lazım olduqda istifadə edin. Və buna görə də Həmişə mümkünsə obyektin saxlama müddətini avtomatik müəyyən etməyi seçin.

Bir qayda olaraq, məcburi ömür boyu təyini aşağıdakı hallarda istifadə olunur:

  • Obyektin əhatə dairəsini tərk etdikdən sonra da mövcud olması lazımdır- məhz bu obyekt, məhz bu yaddaş sahəsində, onun surəti deyil. Əgər bu sizin üçün vacib deyilsə (əksər hallarda belədir), ömür boyu avtomatik təyinata etibar edin. Bununla belə, burada bir situasiya nümunəsi var ki, siz obyektə onun əhatə dairəsindən kənarda daxil olmanız lazım ola bilər, lakin siz bunu açıq şəkildə saxlamadan edə bilərsiniz: Obyekti vektora yazmaqla siz obyektin özü ilə “əlaqəni poza” bilərsiniz - əslində o (və onun surəti deyil) vektordan çağırıldıqda mövcud olacaq.
  • Çox yaddaş istifadə etməlisiniz, bu da yığını daşdıra bilər. Əgər belə bir problemlə qarşılaşmasanız (və nadir hallarda rastlaşırsınızsa) əladır, çünki bu, C++ dilinin “səriştəsindən kənar”dır, lakin təəssüf ki, bəzən bu problemi də həll etməli olursunuz.
  • Məsələn, istifadə etməli olduğunuz massivin ölçüsünü dəqiq bilmirsiniz. Bildiyiniz kimi, C++-da massivlər müəyyən edildikdə sabit ölçüyə malikdir. Bu, məsələn, istifadəçi daxiletməsini oxuyarkən problemlər yarada bilər. Göstərici yaddaşda yalnız massivin başlanğıcının yazılacağı sahəni, təxmini desək, ölçüsünü məhdudlaşdırmadan müəyyən edir.

Dinamik ayırmadan istifadə etmək lazımdırsa, onu ağıllı göstərici (məqaləmizdə oxuya bilərsiniz) və ya "Resurs əldə etmək işə salınır" deyimini dəstəkləyən başqa bir növdən istifadə edərək əhatə etməlisiniz (standart konteynerlər bunu dəstəkləyir - bu idiomdur. hansı resursa görə: blok yaddaşı, faylı, şəbəkə bağlantısı və s. - qəbul edildikdə konstruktorda işə salınır və sonra dağıdıcı tərəfindən diqqətlə məhv edilir). Ağıllı göstəricilər, məsələn, std::unique_ptr və std::shared_ptr dir.

Yol göstəriciləri

Bununla belə, göstəricilərin istifadəsi yalnız dinamik yaddaşın ayrılması baxımından əsaslandırıldığı hallar var, lakin demək olar ki, həmişə göstəricilərdən istifadə etmədən alternativ bir yol var, onu seçməlisiniz. Əvvəlki kimi deyək: göstəricilərdən istifadə etmək üçün xüsusi ehtiyac olmadığı halda həmişə alternativi seçin.

Göstəricilərdən istifadənin mümkün variant kimi nəzərdən keçirildiyi hallara aşağıdakılar daxildir:

  • Referensial semantika. Bəzən bir obyektə daxil olmaq lazım ola bilər (yaddaşın necə ayrılmasından asılı olmayaraq), çünki siz onun surətinə deyil, bu obyektdəki funksiyalara daxil olmaq istəyirsiniz - yəni. həyata keçirmək lazım olduqda istinadla keçin. Lakin əksər hallarda burada göstərici deyil, linkdən istifadə etmək kifayətdir, çünki linklər məhz bunun üçün yaradılır. Qeyd edək ki, bunlar yuxarıda 1-ci bənddə təsvir edilənlərdən bir qədər fərqli şeylərdir. Amma əgər siz obyektin surətinə daxil ola bilirsinizsə, onda istinaddan istifadə etməyə ehtiyac yoxdur (lakin nəzərə alın ki, obyektin surətinin çıxarılması bahalı əməliyyatdır).
  • Polimorfizm. Polimorfizm (dinamik obyekt sinfi) daxilində funksiyaları çağırmaq istinad və ya göstəricidən istifadə etməklə mümkündür. Yenə də istinadlardan istifadəyə üstünlük verilir.
  • Könüllü obyekt. Bu halda obyektin buraxıldığını göstərmək üçün nullptr-dən istifadə edə bilərsiniz. Əgər bu funksiya arqumentidirsə, onu defolt arqumentlər və ya həddindən artıq yükləmə ilə həyata keçirmək daha yaxşıdır. Alternativ olaraq, boost::optional (C++14 std::optional-da dəyişdirilmiş) kimi bu davranışı əhatə edən növdən istifadə edə bilərsiniz.
  • Kompilyasiya sürətinin yaxşılaşdırılması. Sizə kompilyasiya vahidlərini ayırmaq lazım ola bilər (tərtib vahidləri). Göstəricilərin effektiv istifadələrindən biri əvvəlcədən bəyannamədir (çünki obyektdən istifadə etmək üçün ilk növbədə onu müəyyən etməlisiniz). Bu, kompilyasiya vahidlərini boş yerə ayırmağa imkan verəcək ki, bu da tərtib müddətlərinin sürətləndirilməsinə müsbət təsir göstərə bilər, bu prosesə sərf olunan vaxtı əhəmiyyətli dərəcədə azaldır.
  • Kitabxana ilə qarşılıqlı əlaqəC və ya C kimi. Burada ən son anda yaddaşı onlardan azad edərək xam göstəricilərdən istifadə etməli olacaqsınız. Ağıllı göstəricidən xam göstərici əldə edə bilərsiniz, məsələn, get əməliyyatı ilə. Kitabxana sonradan əl ilə azad edilməli olan yaddaşdan istifadə edirsə, siz dağıdıcını ağıllı göstəricidə çərçivəyə sala bilərsiniz.