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
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
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
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
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
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
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
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
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 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 Şək.1 Göstərici vasitəsilə dəyişənə daxil olmaq Ümumilikdə, göstəricilərə münasibətdə * iki halda istifadə olunur: 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 Şəkil 2 Göstəricinin sıfırlanması Ev C dili nümunələri ilə 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ə 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 { 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. 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. 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: 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. 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. Ə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. 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. 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. 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. 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. Göstərici yaddaş yerinin ü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 #daxildir 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. 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. #daxildir Göstəriləcək Ə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. Onları yerinə yetirmək üçün ölçüsünü bilməlisiniz. #daxildir 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. #daxildir Göstəricilər bərabərdirsə, onlar eyni yaddaş sahəsinə işarə edirlər. 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 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 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. 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. 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). 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 İndi göstəricilərlə işləməyin bəzi nümunələri #daxildir 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 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 Bu nümunədə siz dəyişənlərin növünü dəyişə bilərsiniz a Və b 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 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. 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əh Və t- 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. 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. 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). İ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çü. 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. 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 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ə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əsələn: Сhar c; // dəyişən char *p; // göstərici p = // p = ünvan c 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ə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 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 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. 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. 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. 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: 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. 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:C-də funksiyalar
C-də funksiyalar nə üçündür?
C-də funksiyanın sadə nümunəsi
puts("C-də funksiyalar");
EXIT_SUCCESS qaytarın;
}C-də bir funksiyadan başqa bir funksiyanı necə çağırmaq olar?
C-də funksiya prototipi
C-də funksiya elanı C-də funksiya tərifindən nə ilə fərqlənir?
geri qaytarma bəyanatı
C dilində göstəricilər
Funksiya arqumentlərinin dəyərinə görə ötürülməsi
C funksiyası göstəricilərini ötürmək
Eclipse-də C/C++
Yol göstəriciləri
Tərif
Mövzu 7. C dilində göstəricilər.
Göstəricilərlə işləmək üçün iki əsas operator & ünvan operatoru və * dereference operatorudur. Sadə bir misala baxaq.
Məzmunu dəyişdirmək üçün yazın
İndi başqa bir vacib nümunə
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ı
İkincisi, göstəricilər arifmetik əməliyyatları dəstəkləyir.
Ə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.
+ 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ə.Göstəriciyə göstərici
Göstəricilər və tip tökmə
NULL göstərici - boş göstərici
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.
Bu o deməkdir ki, bu vəziyyətdəNümunələr
1. Massivdən keçək və bütün cüt elementləri tapaq.C-də göstəricilər.
Qeydlər və fərziyyələr
Göstərici bəzi verilənlər elementinin (dəyişən, sabit, funksiya, struktur) ünvanını ehtiva edən dəyişəndir.
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.
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);
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.
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 sintaksisi
*ObyektAdı yazın; Göstəricinin elan edilməsi, dəyişənin ünvanının alınması
Göstəriciyə göstərici
Göstəricilər və tip tökmə
NULL göstərici - boş göstərici
Sual
Cavab verin
Dinamik paylama
Yol göstəriciləri