Giriş
Bu mətndə C dilində proqram tərtibindən bəhs olunur . Mətndən
tələbələr, məktəblilər, informatika fənnini tədris edən müəllimlər , sistem
inzibatçıları və ümumilikdə C (si) proqaramlaşdırma dilini örgənmək istəyən kər
kəs istifadə edə bilər. Bu mətndən istifadə edə bilmək üçün ilkin olaraq heç
bir proqramlaşdırma dilini bilmək tələb onunmur.
Kompüterlər, proqramlar,
proqramlaşdırma dilləri, əməliyyat sistemləri.
Proqram anlayışını daxil etməzdən və C dilinin müasir
İnformasiya və Kommunikasiya Texnalogiyalarında yeri barədə söz açmazdan öncə
gəlin əvvəl bugünkü həyatımızada kompüterlərin və proqramların oynadığı rola
sadə bir nəzər salaq. Kompüterlər demək olar ki, hər yerdə, məişətdə, təhsildə,
tibbdə, sənayedə v.s. yerlərdə çox geniş istifadə olunur. Məsələn NASA – da
kosmik gəmilər, süni peyklər, zavodlarda mürəkkəb istehsal prosesləri
kompüterlər vasitəsilə idərə olunur. Milyardlarla insan məlumatları içərisində
axtarış parametrlərinə uyğun gələnini kompüter verilənlər bazasından çox az bir
zaman ərzində(saniyənin kiçik bir hissəsi) tapır. Adi istifadəçilər kompüterlə
müxtəlif oyunlar oynayır, hər-hansı mətn və ya cədvəl üzərində işləyir v.s.
Arxitektura cəhətdən dünyada mövcud olan əksər kompüterlər
arasında elə də böyük fərq yoxdur. Yəni evdə istifadə etdiyimiz komputerlə NASA
– da istifadə olunan kompüterlər bir-birindən əsasən yaddaş və sürətinə görə
fərqlənir. Bəs niyə biz öz kompüterimizlə hansısa kosmik gəmiləri idarə etmirik
? Kompüterlərə bu qədər müxtəlif işlər yerinə yetirməsinə imkan verən nədir?
Niyə hansısa kompüterlə hər-hansı işi gürmək olar digəri ilə yox? Kompüterə
ümumiyyətlə istənilən bir işi görə bilməsinə imkan verən nədir? Əlbəttdə ki
proqram. Məhz proqram kompüterə hansı işlər görməli olduğunu tam detalları ilə
bildirir. Proqramsız kompüter təmamilə yararsız bir dəmir parçasıdır. Proqramı
isə əlbəttdə ki proqramçılar tərtib edir. Bu iş son dərəcə çətin və
məsuliyyətlidir. Proqramın hazırlanma prosesi, onun daxili strukturu və yerinə
yetirilmə prosesi kifayət qədər mürəkkəb məsələlərdir və bu məsələrlə məşğul
olmaq təcrübə tələb edir. Biz hələlik proqramın hazırlanması ilə məşğul
olacayıq. Biz proqramın mətn kodunu yazırıq C dilində , daha sonra xüsusi
proqramlar vasitasilə bizim mətndən icra olunan proqram alınır.
İndi isə bir qədər C dili barəsində danışaq. C dili haqda
Wikipediada kifayət qədər məlumat verilib. Qısa olaraq onu demək olar ki:
1. C dilinin sintaksisi C++, JAVA, PHP dillərinin sintaksisi ilə
eynidir(buradan isə o çıxır ki, bütün bu dillərin sintaksisi eynidir :) ). Bu C
dilini (və ya bu dillərdən hər hansı birini) bilməklə digərlərinə keçməni xeyli
asanlaşdırır.
2. C dilində yazılan proqramlar çox sürətli və təhlükəsiz
olduğundan ciddi proektlər əsasən bu dildə yaradılır. Buraya əməliyyatlar
sistemləri (bütün UNİX-tipli sistemlərin hamısı C dilində yazılıb), Google
axtarış sistemi və bir çox digər proqramları aid etmək olar. Bu siyahını
istənilən qədər uzatmaq olar. Lakin C dilinin bugünkü gündə rolunu anlamaq üçün
"bütün UNİX-tipli əməliyyatlar sistemi anlayışına" nəzər salmaq
kifayətdir.
UNİX-tipli əməliyyatlar sistemlərinin dəqiq siyahısını vermək
mümkün deyil. FreeBSD, OpenBSD SOLARİS, HP_UX, AİX... və xakerlərin sevimlisi
GNU/LİNUX (çünki təmamilə xakerlər tərəfindən yaradılıb) sistemlərini bu
siyahının parlaq nümunələri kimi misal göstərmək olar. UNİX-tipli anlayışına
müəyyən qədər aydınlıq gətirdik, indi isə əməliyyatlar sisteminin nə demək
olduğuna baxaq.
Əməliyyatlar sistemi sadəcə və sadəcə müəyyən qrup proqramlar
yığınıdır. Ancaq proqramlar, ayrı heçnə. Buraya daxildir müxtəlif server
proqramları webserver(appache...), file server(ftp server...), məlumatlar
bazası proqramları( oracle, mysql ...), kompilyator, assembler, linker
proqramları ( proqram tərtib etmək üçün) , mətn redaktorları, shell proqramları
(digər proqramları yükləmək və icra etmək üçün), arxiv proqaramları (tar, gzip
... faylların həcmini kiçiltmək üçün) , 100 -lərlə, 1000 -lərlə digər
proqramlar və əlbəttdə ki ən əsası nüvə (kernel) proqramı. Təsəvvür edin bu
qədər proqramın hamısı C dilində yazılıb.
İndi isə ən maraqlısı – nüvə. Nüvə proqramı kompüter
əməliyyatlar sisteminin ən əsas proqramıdır. Bu proqram sistemin fəaliyyatinə
tam olaraq nəzarət edir. O əməliyyatlar sisteminin digər proqramlarının
fəaliyyəti üçün lazim olan bütün funksiyaları özündə cəmləyir. Xüsusi olaraq
buraya kompüterin qurğularının idarə olunması(driverlər), yaddaşının idarə
olunması(memory management), proseslərin(yerinə yetirilən proqramların ) idarə
olunması, faylların idarə olunması , şəbəkənin idarə olunması(networking),
kompüterin digər resurslarının v.s. idarə olunması daxildir. Beləkliklə C
dilini mükəmməl bilmək mənbə kodları açıq şəkildə verilən bu
proqramları(bəziləri istisna olmaqla – oracle,...) başa düşməyə və müəlliflər
tərəfindən verilmiş imkanlar daxilində (GPL, OSL, v.s. lisenziyaları) bu
proqaramları dəyişib istifadə etməyə imkan verir.
C
proqaramlaşdırma dili.
İlk
proqram.
Bu mövzuda biz ilk proqramı yerinə
yetirmək barədə danışacağıq. Yəni C dilində proqram yazmaq üçün bizə nə
lazımdır, bunları haradan əldə edə və necə işlək vəziyyətə gətirə bilərik.
Sonda hər şeyin qaydasında olduğunu yoxlamaq məqsədilə test proqram yerinə
yetirəcəyik.
C dilində və istənilən digər dildə
proqram yazmaq üçün bizə kompüter, əməliyyatlar sistemi, kompilyator və mətn
redaktoru proaramları lazımdır. Kompüter məsələsi aydındır.
Əməliyyatlar sistemi. ---- Hal-hazırda
geniş yayılmış 2 əməliyyatlar sistemi ailəsi mövcuddur. UNİX tipli sistemlər və
Windows sistemləri ailəsi.
Windows əməliyyatlar sistemi.
Windows istifadəçilər üçün çox rahatdır,
amma Windowsla bağlı bir çox proqramçıların mövqeyi müsbət deyil. Məsələ
ondadır ki bir çox CPU(prosessor) arxitekturaları qoruyucu rejimdə(əsas işçi
rejim) işlədikdə yaddaşı(RAM) seqmentlərə bölür və bir seqmentin proqram
kodunun digər seqmentə müraciət etməsinin qarşısını alır(qoruyur). Belə halda
kompüterdə yerinə yetirilən proqramlar 2 qrupa bölünür.
Yüksək prioritetli seqmentlərə yüklənmiş
proqramlar.
Bu proqram kodlarına aiddir nüvə(kernel)
kodu. Driverlər, yaddaşın, şəbəkənin, fayllar sisteminin, proseslərin idarə
olunması və bir çox digər işləri yerinə yetirən proqram kodları bu qrupa
daxildir. Bu proqramlar kompüterin bütün resurslarına (portlar v.s.) tam
nəzarət edir və onlar yaddaşın istənilən segmentinə istənilən müraciəti edə
bilərlər.
Aşağı prioritetli seqmentlərə yüklənmiş
proqramlar.
Bu proqramlara kompüterə yüklənmiş yerdə
qalan proqramlar -- istifadəçi proqramları aiddir.
Windows sistemi öz nüvəsinin proqram
kodlarını gizli saxlayır.
Bu isə öz proqramlaşdırma biliyini daha
da inkişaf etdirmək istəyən proqramçılara maneə yaradır.
UNIX tipli sistemlər.
GNU/LINUX, FREEBSD, SOLARIS, AIX, ...
Unix sistemləri proqramçılar tərəfindən
yaradılıb və bütün proqram kodları açıqdır. Kompüter arxitekturasını örgənmək
istəyən proqramçılar üçün unix əvəzedilməz əməliyyatlar sistemidir.
Unix sistemləri həm interfeys, həm də
daxili struktur baxımından bir-birinə çox yaxındır. Bu sistemlərin içərisində
ən maraqlısı GNU/LİNUX sistemidir.
Bu mətndə verilmiş bütün proqramlar
GNU/LINUX sistemi üçün nəzərdə tutulub, lakin eyni qayda ilə digər unix
sistemlərində də yerinə yetirilə bilər.
İstənilən halda bu mətndə verilmiş
proqramları Windows sistemində də icra etmək olar, bunun üçün MS Visual C++,
Borland C++ Builder və Delphi kompilyatorlarından istifadə edə bilərsiniz.
Proqram yazmaq üçün bizə lazım olan
digər vasitə MƏTN redaktoru proqramıdır.
Biz gedit proqramından istifadə edəcəyik.
gedit proqramını
yükləmək üçün
Applications->Accessories->Text
Editor
seçimini edin.
Kompilyator proqramı
Kompilyator proqramı olaraq gcc -dən
istifadə edəcəyik və bu kompilyator bütün unix sistemlərində ən çox istifadə
olunan proqramdır və o hər bir sistemin paketinə daxil olduğundan
instalyasiyaya ehtiyac yoxdur.
İlk test proqram ---- Hello world proqramı.
Qeyd edək ki ilk test proqramını yerinə
yetirib lazımi nəticəni almaq həqiqətən əzmkarlıq tələb edir və bir qayda
olaraq həmişə bir neçə uğursuz cəhtdən sonra alınır. İlk olaraq işçi
qovluq(kataloq, papka, folder, directory) anlayışına aydınlıq gətirək.Terminal proqramını yükləyin.
Applications->Accessories->Terminal
Terminal pəncərəsindən aşağıdakı əmri
daxil edin.
pwd
Ekranda sizin işci qovluq cap olunacaq.
user@gnu_linux:~$
user@gnu_linux:~$
user@gnu_linux:~$ pwd
/home/user
user@gnu_linux:~$
user@gnu_linux:~$
ls əmrini
daxil edin
user@gnu_linux:~$
user@gnu_linux:~$ ls
3_20 ch02.pdf f1.txt music Public ubuntu_ins
3_20.cpp Desktop german music_file fhedd Videos
arch documents mozilla.pdf music_file~ ramdisk.img.gz vmlinuz26
a.txt dff mozilla.ps pictures telix wget
boot Examples m.txt prg Templates
user@gnu_linux:~$
user@gnu_linux:~$
bu əmr hal hazırda bizim işci qovluqda
olan faylları və qovluqları göstərir.
mkdir proqs əmrini daxil edirik.
Nəticədə bizim işci qovluqda yeni proqs qovluğu yaranacaq.
cd proqs əmrini daxil edirik.
Nəticədə bizim işci qovluq proqs olacaq.
Bu qovluğu yeni yaratdıq və orada olan faylları yoxlamaq üçün yenidən ls əmrini
daxil edirik.
user@gnu_linux:~$
user@gnu_linux:~$ mkdir progs
user@gnu_linux:~$
user@gnu_linux:~$ cd progs
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ ls
user@gnu_linux:~/progs$
progs qovluğunu yeni yaratdıq və hələlik
onda heç bir fayl yoxdur.
gedit proqramını
yükləyək.
Applications->Accessories->Terminal
Aşağıdakı mətni gedit proqramına köçürüb hello.c adı ilə
yeni yaratdığımız proqs qovluğunda yadda saxlayırıq.
#include<stdio.h>
int main(){
printf("Hello World\n");
}
ls əmrini
daxil edək. Nəticə aşağıdakına bənzər olmalıdır.
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ ls
hello.c
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$
hal-hazırda proqs qovluğunda bizim
yaratmaq istədiyimiz proqramın mətn fayli(çox vaxt buna mətn kodu deyirlər)
var( hello.c ). Bu koddan yerinə yetirilə bilən proqram almaq üçün (mətn kodun
kompilyasiya etmək üçün)
gcc hello.c -o hello
əmrini daxil edək. Əgər siz proqramın
mətn kodunu düzgün köçürmüsünüzsə onda proqramın kompilyasiyası uğurla başa
çatmalıdır. Bunu yoxlamaq üçün yenə ls əmrini daxil edirik. bu dəfə proqs
qovluğunda hello adlı yeni faylın yarandığını görürük. Bu bizim proqramdır.
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ gcc hello.c -o hello
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ ls
hello hello.c
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$
Yeni yaratdığımız hello proqramını
yerinə yetirmək üçün
./hello
əmrini daxil edirik. Əgər ekranda
Hello World
sətri çap olundusa onda sizi təbrik
edirik, siz artıq C dilində ilk proqramı yerinə yetirdiniz.
user@gnu_linux:prg#
user@gnu_linux:prg# gcc hello.c -o hello
user@gnu_linux:prg# ./hello
Hello World
user@gnu_linux:prg#
user@gnu_linux:prg#
Indi isə gəlin proqramın mətn kodu ilə
daha ətraflı tanış olaq. Bunun üçün əvvəl bu proqramın bir qədər dəyişik
variantlarını yerinə yetirək.
prog hello_1.c
#include <stdio.h>
int main(){
printf("Hello World_1\n");
}
proqramın mətnini hello1.c adlı faylda
yadda saxlayırıq və kompilyasiya edirik.
gcc hello1.c -o hello1
və yerinə yetiririk
./hello1
user@gnu_linux:~prg#
user@gnu_linux:~prg# gcc hello1.c -o hello1
user@gnu_linux:~prg#
user@gnu_linux:~prg#
user@gnu_linux:~prg# ./hello1
Hello World_1
user@gnu_linux:~prg#
Bu dəfə ekranda Hello World_1 sətrinin
çap olunduğunu görərik. Yəqin ki oxucuya aydın olur ki, bu proqram yerinə
yetirildikdə printf("....................."); sətrində " və " arasında olan ifadə ekranda çap olunur \n –
dən savayı. hello1.cfaylında
həmin sətri printf("Salam
dünya . Bu mənim C dilində 3-cü proqramımdır\n"); sətri ilə əvəz edək. Proqramı
kompilyasiya ediib yerinə yetirsək ekranda Salam
dünya . Bu mənim C dilində 3-cü proqramımdır sətri çap olunacaq.
Tapşırıq:
Ekranda müxtəlif yazılar çap edən proqramlar yazmalı.
Ekranda müxtəlif yazılar çap edən proqramlar yazmalı.
Beləliklə proqram haqda təcrübə topladıq
, indi isə gəlin hello.c proqramını daha dərin analiz edək. Proqramın ilk sətri
#include <stdio.h>
sətridir. Bu sətrin dəqiq izahı 10-cu
paraqrafda verilir. Hələlik isə onunla kifayətlənək ki, bu sətir proqramda
printf funksiyasından istifadə etməyə imkan verir. Növbəti sətir
int main(){
sətridir. Burada main adlı funksiya
təyin olunur. Funksiyalar barəsində daha ətraflı 5-ci paraqrafda söhbət
açacağıq. Hələlik isə onu qeyd edək ki, main funksiyası bütün C proqaramlarında
olmalıdır və C dilində yazılmış hər bir proqram yerinə yetirilməyə məhz bu
yerdən başlayır. Növbəti sətir
printf("Hello World\n");
sətridir. Bu sətir bizə tanışdır.
Ekranda hər – hansı sətri , rəqəmi, v.s. məlumatı çap etmək üçün istifadə
olunur. printf çox güclü çap funksiyasıdır və konkret yerlərdə bu imkanlarla
tanış olacağıq. İndi isə təcrübə
kimiprintf("%d%s%d=%d\n",3,"+",5,3+5); sətrini proqrama əlavə edib onu yerinə
yetirək. Proqramın son sətri isə } sətridir.
Bu işarə main funksiyasının bitdiyini
bildirir. Printf funksiyasında tez-tez rast gəldiyimiz \n isə yeni sətir
simvolu adlanır. Yəni printf çap edəcəyi sətirdə \n – nə rast gəldikdə yerdə
qalan hissəni növbəti sətirdən çap edir. Aşağıdakı sətri proqrama əlavə edib
bunu yoxlayın.
Dəyişənlər
Əvvəlki paraqrafda biz C dilində necə
proqram yerinə yetirməyi örgəndik. Bu paraqrafda biz C dilində yazılmış
proqramın ən vacib elementlərindən biri – dəyişənlərlə tanış olacağıq. Hər bir
proqram yerinə yetirilərkən müxtəlif məlumatları yadda saxlamalı (yaddaşa
yerləşdirməli) olur. Tutaq ki iki ədədin cəmini hesablayan proqram yazmaq
istəyirik. Bu zaman biz yaddaşda 3 məlumat üçün yer ayırmalıyıq. İki toplanan
və cəm. Proqramlaşdırmada hər hansı məlumatı qəbul etmək, yadda saxlamaq və bu
məlumatın qiymətinə müraciət etmək üçün dəyişənlərdən istifadə olunur. Konkret
olaraq dəyişən adı olan müəyyən bir yaddaş sahəsidir. Bu yaddaşın sahəsini və
adı dəyişəni elan edərkən biz özümüz(proqramçılar) müəyyən edirik.
Yadda saxladığı məlumatın növünə və
həcminə görə dəyişənlər tiplərə ayrılır. Misal üçün tam ədədlər tipi - int, kəsr ədədlər tipi - float, simvol tipi - char, sətir tipi - char [], char *v.s. Bu tiplərə
standart tiplər deyilir. Bundan əlavə C dilində ünvan dəyişənləri tip, struktur
tiplər və siyahılardan da çox geniş istifadə olunur ki, bunlarla da uyğun
olaraq 4, 8 və 9-cu paraqraflarda məşğul olacayıq.
Əgər biz proqramda tam tipdən(int) olan
və adı x olan dəyişən elan etmək istəyiriksə bu zaman
int x;
sətrini
proqrama yerləşdiririk.
Dəyişənlərə istədiyimiz kimi ad verə
bilərik yalnız və yalnız həriflərdən(ingilis əlifbasının) , '_' simvolundan və
rəqəmlərdən istifadə etməklə. Dəyişənin adı mütləq hərflə başlamalıdır, və
operator, tip v.s. adlardan da dəyişən adı kimi istifadə etmək olmaz.
Operatorlarla gələn mövzularda tanış olacayıq.
Beləliklə cəm proqrmında hər iki
toplananı və onların cəmini yerləşdirmək üçün biz tam tipli 3 dəyişən təyin
etməliyik. Gəlin bu dəyişənləri uyğun olaraq top1, top2 və cem kimi adlandıraq.
Bu dəyişənləri təyin etmək üçün proqram kodu aşağıdakı kimi olacaq.
int top1;
int top2;
int cem;
Qeyd
edək ki eyni tipdən olan dəyişənləri vergüllə ayırmaqla bir sətirdə də elan edə
bilərik. Aşağıdakı kimi
int top1,top2,cem;
Burada ; simvoluna
diqqət yetirməyinizi istəyirəm. Bu işarə kompilyatora hər hansısa bir
əməliyyatın (indiki halda dəyişənlərin elanının) bitməsini göstərir.
Gəlin proqramımızı tərtib edək. Hələlik 3 dəyişən elan etmişik(dəyişən elan etmək və ya təyin etmək eyni məna bildirir). Proqramımız belə olacaq.
Gəlin proqramımızı tərtib edək. Hələlik 3 dəyişən elan etmişik(dəyişən elan etmək və ya təyin etmək eyni məna bildirir). Proqramımız belə olacaq.
#include <stdio.h>
int main(){
int top1, top2, cem;
}
Proqramı
cem.c faylında yadda saxlayaq və kompilyasiya edək və yerinə yetirək.
user@gnu_linux:~/prg
user@gnu_linux:~/prg gcc cem.c -o cem
user@gnu_linux:~/prg
user@gnu_linux:~/prg ./cem
user@gnu_linux:~/prg
user@gnu_linux:~/prg
Göründüyü kimi proqram heç bir iş
görmür, sadəcə tam tipli 3 dəyişən elan edir və icrasını bitirir.
İndi gəlin bu elan etdiyimiz
dəyişənlərdə hər hansı məlumat (ədədlər) yerləşdirək(daha doğrusu dəyişənə aid
olan yaddaş sahəsinə). Proqramlaşdırmada hər hansı əməliyyatı yerinə yetirmək
üçün istifadə olunan əmrə operator deyilir. Yaddaşa məlumat yazma əməliyyatı
üçün mənimsətmə operatorundan istifadə olunur. Mənimsətmə operatoru = kimi
işarə olunur. Qətiyyən riyaziyyatda olan bərabərlik simvolu ilə qarışdırmaq
olmaz. Proqramda bu operatordan solda dəyişənin adı , sağda isə ona
mənimsədiləcək qiymət dayanır(və əlbəttdə bütün operatorların sonunda olduğu
kimi sonda ; işarəsi). məsələn
x = 5 ;
Bu operator x dəyişəninə 5 qiymətini
mənimsədir. Bu zaman yaddaşda nəyin baş verdiyinə gəlin nəzər salaq. Əvvəlcə 5ədədi onluq say sistemindən ikilik
say sisteminə çevrilir. Say sistemləri əsasən assembler proqramlaşdırma dilinin
mövzusu olduğundan və bu işlərlə məşğul olmaq böyük proqramlaşdırma təcrübəsi
tələb etdiyindən bu haqda çox dərinə getməyəcəyik.
5 ədədi ikilik say sistemində aşağıdakı kimi göstərilir.
5 ədədi ikilik say sistemində aşağıdakı kimi göstərilir.
00000000000000000000000000000101
x tam tipli olduğundan yaddaşda onun
üçün 4 bayt yer ayrılır. Bir bayt 8 bit – dən ibarət yaddaş sahəsidir. Bir bit
ən kiçik yaddaş sahəsidir və bu sahəyə ancaq 1 və ya 0 yazmaq olar. Komputerin
yaddaşı baytlar ardıcıllığından və o da öz növbəsində bitlərdən ibarət
olduğundan, bitlərdə isə ancaq 0 və ya 1 yerləşdirmək mümkün olduğundan belə
çıxır ki kompüterin yaddaşına yerləşdirilən bütün məlumat orada 0 və 1 -lər
ardıcıllığı şəklində saxlanılır. Yəni bütün bizim mp3 , wav, jpg , doc v.s.
fayllarımız və ümumiyyətlə bütün fayllar yalnız və yalnız 0 və 1 - lər ardıcıllığıdır.
Məişətdə istifadə etdiyimiz bir çox texnikanın (komputer , fotoaparat, ... )
adının rəqəmsal (digital, digit ingilis dilində rəqəm deməkdir)olması da
buradan qaynaqlanır və burada söhbət ancaq 0 və 1 rəqəmlərindən gedir.
Tutaq ki baxdığımız anda proqramın
yaddaşı aşağıdakı şəkildədir.
Shekil1.
Shekil1.
x=5;
Indi
isə yaddaşın vəziyyəti aşağıdakı kimi olar.
şəkil 2.
şəkil 2.
Aşağıdakı operatora baxaq.
x=y;
Bu
halda y -in qiyməti yaddaşdan x-in ünvanına köçürülür.
Sekil3. Shekil4.
Sekil3. Shekil4.
Aşağıdakı hala baxaq.
x=x+1;
Bu
halda əvvəlcə x-in qiyməti yaddaşdan götürülür, onun üzərinə 1 əlavə olunur və
alınmış nəticə yenidən x-ə yazılır. Tutaq ki x – ə 3 qiyməti mənimsədilib.
x=3;
Shekil5.
x=x+1; 3+1 --- 4 --- 00000000000000000000000000000100 Shekil6.
Proqramda Şərhlər
C
dilində yazılmış hər – hansı proqramı şərhsiz təsəvvür etmək olmaz. Şərh
(comment, statement ... ) proqramın bu və ya digər hissəsinin hansı iş
gördüyünü bildirmək üçün proqramın mətn koduna əlavə olunur .Şərhlər ancaq
proqramın işini başa düşmək istəyənlər üçündür. Proqramın real yerinə yetirilən
koduna şəhrlərin heç bir aidiyyəti yoxdur. Belə ki, kompilyator proqramı
kompilyasiya edərkən birinci gordüyü iş şərhləri proqram kodundan silməkdir.
Proqramda şərh elan etmək üçün /*
və */ - dən istifadə olunur.
Bu zaman kompilyator bu işarələr arasında yerləşdirilən mətni şərh kimi üəbul edəcək. Lakin səhvən proqram kodlarını şərh kimi verməyin. Bu zaman kompilyator onları nəzərə almayacaq. Onu da deyim ki, çox vaxt bu qaydadan proqramdakı səhvləri tapmada istifadə olunur (proqramın müəyyən hissəsini şərh kimi verib nəticəni yoxlamaqla).
Bu zaman kompilyator bu işarələr arasında yerləşdirilən mətni şərh kimi üəbul edəcək. Lakin səhvən proqram kodlarını şərh kimi verməyin. Bu zaman kompilyator onları nəzərə almayacaq. Onu da deyim ki, çox vaxt bu qaydadan proqramdakı səhvləri tapmada istifadə olunur (proqramın müəyyən hissəsini şərh kimi verib nəticəni yoxlamaqla).
Dəyişənlərin müqayisəsi və qiymətlərinin dəyişdirilməsi.
Dəyişənlərin qiymələrinin dəyişdirilmə
və müqaisə imkanının olması proqramçıya çox geniş imkanlar verir. Dəyişənərlə
aşağıdakı əməliyyatları aparmaq olar.
*, -, +, /, =, ==, >, < * hasil, - çıxma, + cəm. Bizim üçün lazım olan ən vacib əməliyyat , dəyişənin qiymətinin dəyişdirilməsi əməlidir. Bunun üçün '=' operatorundan istifadə olunur. Bu operatora 'mənimsətmə' operatoru deyirlər. Bu opratoru bərabərlik opratoru kimi qəbul etmək olmaz. C++ dilində bərabərlik opratoru olaraq == istifadə olunur. Aşağıdakı nümunələrə baxaq.
*, -, +, /, =, ==, >, < * hasil, - çıxma, + cəm. Bizim üçün lazım olan ən vacib əməliyyat , dəyişənin qiymətinin dəyişdirilməsi əməlidir. Bunun üçün '=' operatorundan istifadə olunur. Bu operatora 'mənimsətmə' operatoru deyirlər. Bu opratoru bərabərlik opratoru kimi qəbul etmək olmaz. C++ dilində bərabərlik opratoru olaraq == istifadə olunur. Aşağıdakı nümunələrə baxaq.
a = 4*6; /* proqramda bu sətir yerinə yetirildikdən sonra */
/* əvvəlki qiymətindən asılı olmayaraq a-nın qiyməti yeni qiyməti 24 olacaq. */
a = a + 5; /* a -nın yeni qiyməti əvvəlki qiyməti ilə 5 -in cəminə bərabər olur. */
a == 3; /* a -ya heç bir yeni qiymət mənimsədilmir(a-nin qiymeti deyishmir) , sadəcə onun 3 - */
/*ə bərabər olub olmaması yoxlanılır */
Yəqin ki, siz < və >
operatorlarının necə işlədiyini düşünürsünüz. Onlar uyğun olaraq böyükdür və
kiçikdir operatorlarıdır. Məsəslən.
a < 5 /* a -nın 5 -dən kiçik olmasını yoxlayır */
a > 5 /* a -nın 5 -dən böyük olmasını yoxlayır */
a == 5 /* a -nın 5 -ə bərabər olmasını yoxlayır */
proqramlaşdırmanı örgənməyin yeganə yolu
proqram yazmaqdır.
Aşağıda verilən tapşırıqları yerinə yetirən proqramlar yazın.
Aşağıda verilən tapşırıqları yerinə yetirən proqramlar yazın.
Həlli olanlar. (həllərə yalnız öz
cəhdləriniz nəticə vermədikdə müraciət etmək məsləhət görülür.)
1.3 ədədin cəmini hesablayan proqram
tərtib edin.
2. Hər – hansı ədədi və onun kvadratını
hesablayan proqram tərtib edin.
3. Aşağıdakı kod hissəsinin gördüyü işi yoxlamaq üçün proqram
tərtib edin.
int x,y;
x=5;
y=9;
printf("x=%d y=%d",x,y);
Operatorlar
Əvvəlki paraqrafda biz mənimsətmə
operatoru ilə tanış olduq, bu paraqrafda isə şərt(if, switch) və dövr (for,
while, do while) operatorları ilə tanış olacayıq.
Şərt operatorları -- if , switch
Bildiyimiz kimi operator proqramda hansı
əməliyyatın yerinə yetirilməli olduğunu bildirir. Bir çox hallarda bu və ya
digər əməliyyatın yerinə yetirilməsi hansısa şərtdən asılı olur. Bu zaman if operatorundan
istifadə olunur.
if operatoru
if operatorunun sintakisisi aşağıdakı kimidir
if operatoru
if operatorunun sintakisisi aşağıdakı kimidir
if (şərt) {
yerinə yetirilməli əməliyatlar
}
else {
digər əməliyatlar
}
Qeyd edək ki, əgər cəmi bir əməliyyat
yerinə yetirilirsə onda {} mötərizələrinə ehtiyac yoxdur. Izahı:
Əvvəlcə şərt yoxlanılır , əgər doğrudursa onda {} arasında olan əməliyatlar yerinə yetirilir əks halda else – dən sonrakı {} mötərizələri arasında olan əməliyatlar yerinə yetirilir.
Məsələn:
Əvvəlcə şərt yoxlanılır , əgər doğrudursa onda {} arasında olan əməliyatlar yerinə yetirilir əks halda else – dən sonrakı {} mötərizələri arasında olan əməliyatlar yerinə yetirilir.
Məsələn:
int x;
if (x<5)
printf("%d 5 -den kiçikdir",x);
else
printf("%d 5 -den böyükdür",x);
nümunə proqram prog3_1.c :
#include<stdio.h>
int main(int argc, char *argv[]){
int x;
printf("zehmet olmasa x-in qiymetini daxil edin\n");
scanf("%d",&x);
if (x<5)
printf("%d 5 -den kichikdir\n",x);
else
printf("%d 5 -den boyukdur\n",x);
return 0;
}
proqramı kompilyasiya edək və hər dəfə
müxtəlif qiymətlər daxil etməklə bir neçə dəfə yerinə yetirək.
user@gnu_linux:~/progs/3$ gcc prog3_1.c -o prog3_1
user@gnu_linux:~/progs/3$ ./prog3_1
zehmet olmasa x-in qiymetini daxil edin
7
7 5 -den boyukdur
user@gnu_linux:~/progs/3$ ./prog3_1
zehmet olmasa x-in qiymetini daxil edin
3
3 5 -den kichikdir
user@gnu_linux:~/progs/3$ ./prog3_1
zehmet olmasa x-in qiymetini daxil edin
45
45 5 -den boyukdur
user@gnu_linux:~/progs/3$
Dövr operatorları
for, while, do while
Dövr operatorları müəyyən əməliyyatların
bir neçə dəfə təkrar yerinə yetirilməsinə imkan verir. Bu operatorlardan
proqramlaşdırmada çox geniş istifadə olunur.
for operatoru
for operatorunun sintakisisi aşagıdakı
kimidir:
for (sayğacın ilkin qiyməti; dövrün başa çatma şərti; sayğacın dəyişmə qaydası){
əməliyyatlar;
}
nümunə proqram:
/* prog3_2.c */
int main(int argc, char *argv[]){
int k;
for (k=0; k<10; k=k+1)
printf("salam dunya\n");
return 0;
}
programı
kompilyasiya edib yerinə yetirək.
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ gcc -o prog3_2 prog3_2.c
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ ./prog3_2
salam dunya
salam dunya
salam dunya
salam dunya
salam dunya
salam dunya
salam dunya
salam dunya
salam dunya
salam dunya
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$
indi isə proqramda for (k=0; k<10; k=k+1) sərtini for (k=5; k<10; k=k+1) ,for (k=0;
k<3; k=k+1), for (k=0; k<10; k=k+7),
for (k=0; k<10; k=2) şərtləri ilə əvəz edib proqramı icra edək. Hər dəfə müxtəlif nəticələr alacağıq. Sonuncu halda isə ekranda Salam dunya ifadəsi sonsuz çap olunacaq(proqramın icrasını dayandırmaq üçün CTRL + Z düyməsini daxil edin).
Birinci halı təhlil edək.
for (k=0; k<10; k=2) şərtləri ilə əvəz edib proqramı icra edək. Hər dəfə müxtəlif nəticələr alacağıq. Sonuncu halda isə ekranda Salam dunya ifadəsi sonsuz çap olunacaq(proqramın icrasını dayandırmaq üçün CTRL + Z düyməsini daxil edin).
Birinci halı təhlil edək.
for (k=0; k<10; k=k+1)
Sayğac olaraq k dəyişənindən istifadə
olunur və ona başlanğıc qiymət olaraq 0 mənimsədilir. k=0; dövrün
sona çatması şərti kimi k<10; göstərilib. Bu o deməkdir ki nə qədər
ki k < 10 şərti ödənir dövr davam edəcək.
Sayğacın qiymətinin dəyişmə qaydası kimi k=k+1 (! sonda ; simvolunun yoxluğuna diqqət
yetirin) göstərilib, yəni dövr hər dəfə təkrar olunduqda sayğacın qiyməti 1
vahid artır.
Proses aşağıdakı şəkildə baş verir:
Əvvəlcə k dəyişəni 0 qiyməti alır. Sonra
dərhal başa çatma şərti yoxlanılır. k<10 Şərt ödənir, belə ki, 0 < 10
ifadəsi doğru qiymət alır və avtomatik olaraq {} arasında olan əməliyatlar icra
olunur. Bizim halda cəmi bir əməliyyatdan, printf("salam
dunya\n");
istifadə etdiyimizə görə onu {} mötərəzələri arasına almadıq. Daha sonra növbə sayğacın qiymətinin dəyişməsinə gəlir.
Baxdığımız hal üçün ( k -nın qiyməti 0 olan hal ) k-nın yeni qiyməti 1 olur. Dərhal şərt yoxlanılır. 1 < 10 . Hələki hər şey qaydasında, əməliyyatlar yerinə yetirilir, sayğacın qiyməti dəyişdirilir və hər dəfə 1 vahid artaraq 2,3, ... , 9 qiymətlərini alır. k - nın bütün bu qiymətlərində k<10 şərti ödənir və əməliyyatlar( printf("salam dunya\n");) yerinə yetirilir. Növbəti qiymət isə 10 – dur. Bu halda isə artıq 10 < 10 şərti üdənmir və for operatoru sona çatır.
istifadə etdiyimizə görə onu {} mötərəzələri arasına almadıq. Daha sonra növbə sayğacın qiymətinin dəyişməsinə gəlir.
Baxdığımız hal üçün ( k -nın qiyməti 0 olan hal ) k-nın yeni qiyməti 1 olur. Dərhal şərt yoxlanılır. 1 < 10 . Hələki hər şey qaydasında, əməliyyatlar yerinə yetirilir, sayğacın qiyməti dəyişdirilir və hər dəfə 1 vahid artaraq 2,3, ... , 9 qiymətlərini alır. k - nın bütün bu qiymətlərində k<10 şərti ödənir və əməliyyatlar( printf("salam dunya\n");) yerinə yetirilir. Növbəti qiymət isə 10 – dur. Bu halda isə artıq 10 < 10 şərti üdənmir və for operatoru sona çatır.
while opratoru
for operatorunda biz sayğac təyin etdik,
dövrün başa çatması şərtini və sayğacın dəyişmə qaydasını verdik. Bu zaman biz
dövrün neçə dəfə təkrar olunacağını dəqiq bilirik. Bəzən isə elə olur ki,
dövrün başa çatması şərtinin nə vaxt ödənəcəyi əvvəlcədən bilinmir. Bu zaman
while operatorundan istifadə olunur. while operatorunun sisntaksisi aşağıdakı
kimidr.
while(şərt){
əməliyatlar;
}
nümunə:
char x;
x='b';
while (x!='a'){
printf("salam dunya\n");
printf(" yeni simvol daxil edin\n");
scanf("%c",&x);
}
Bu koddan proq3_3.c -də istifadə edilib.
Bu proqramı kompilyasiya edib yerinə yetirək. Göründüyü kimi dövr biz a
simvolunu daxil edənə kimi davam edəcək.
while dövr operatorunun digər nadir
hallarda istifadə olunan forması do while operatorudur. do while operatorunun
sintaksisi belədir:
do{
əməliyyatlar;
} while(şərt);
bu operatorun while opratorundan yeganə
fərqi odur ki, bu halda şərtin nə zaman ödənməsindən asılı olmayaraq
əməliyyatlar ən azı 1 dəfə yerinə yetiriləcək.
switch operatoru
Əgər müəyyən halda proqramın icra
istiqaməti bir neçə şərtdən asılıdırsa bu zaman if opratoru ilə bu şərtlərin
mürəkkəb konfiqurasiyasından istifadə etmək əvəzinə switch operatorundan
istifadə edirlər.
switch operatorunun sintaksisi aşağıdakı
kimidir:
switch ( dəyişən ) {
case qiymət1:
yerinə yetirilməli proqram hissəsi /* əgər dəyişənin qiyməti == qiymət1 */
break;
case qiymət2:
yerinə yetirilməli proqram hissəsi /* əgər dəyişənin qiyməti == qiymət2 */
break;
...
default:
yerinə yetirilməli proqram hissəsi /* yuxarıdakı şərtlərin heç biri ödənmədikdə */
break;
}
switch opratoru
dəyişənin qiymətini yuxarıdan aşağı case ifadəsinin qarşısında dayanan qiymətlə
yoxlayır və bərabər olarsa onda iki nöqtə : - dən sonra gələn bütün operatorları
yerinə yetirir. Növbəti case ifadəsinə qədər. break rast gəlinən yerdə switch opratoru işini dayandırır və proqramda switch -dən sonra gələn oprator yerinə
yetirilir.
switch operatoru ilə bağlı mühüm məqamlardan biri də case ifadələrində qiymət kimi ancaq tam tipli dəyişənlərdən istifadə etmək olar (int) .
default seçimindən istifadə etmək vacib deyil. Əgər qiymətlərdən heç biri dəyişənə bərabər olmasa onda default:seçimində göstərilən operatorlar yerinə yetiriləcək.
nümunə proqram: prog3_4.c
switch operatoru ilə bağlı mühüm məqamlardan biri də case ifadələrində qiymət kimi ancaq tam tipli dəyişənlərdən istifadə etmək olar (int) .
default seçimindən istifadə etmək vacib deyil. Əgər qiymətlərdən heç biri dəyişənə bərabər olmasa onda default:seçimində göstərilən operatorlar yerinə yetiriləcək.
nümunə proqram: prog3_4.c
#include <stdio.h>
int main(int argc, char *argv[]){
int color = 0;
printf("Her hansi reng sechin(qirmizi=1,yashil=2,qara=3):\n");
scanf("%d",&color);
switch(color){
case 1: printf("siz qirmizi rengi seçdiniz\n");
break;
case 2:printf("siz yaşıl rengi seçdiniz\n");
break;
case 3:printf("siz qara rengi seçdiniz\n");
break;
default:printf("siz hech bir reng sechmediniz\n");
}
return 0;
}
Proqramı kompilyasiya edib yerinə
yetirək.
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ gcc -o prog3_4 prog3_4.c
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$ ./prog3_4
Her hansi reng sechin(qirmizi=1,yashil=2,qara=3):
2
siz yaşıl rengi seçdiniz
user@gnu_linux:~/progs$
user@gnu_linux:~/progs$
Ünvan dəyişənləri
Ünvan dəyişənləri proqramlaşdırmada çox
geniş istifadə olunur. C dilində isə ünvan dəyişənləri şahmatda 2 topun
oynadığı rolu oynayır. Vəzir rolu isə əlbəttdə funksiyalara məxsusdur. Bu
imkandan hansı kombinasiyada istifadə etmək isə proqramçının təcrübəsinə
bağlıdır. Ünvan dəyişənlərindən bir qayda olaraq təcrübəli proqramçılar daha
çox istifadə edir. Yaranan fikir belədir ki, təcrübə nə qədər artarsa,
proqramçının ünvan dəyişənlərindən istifadəsi daha çox olur.
Biz qeyd etdik ki, yerinə yetirdiyi
işdən asılı olmayaraq proqram sadəcə və sadəcə hansısa məlumatları müəyyən
mənbədən qəbul edir, yadda saxlayır, ötürür, dəyişdirir. İndiyə kimi baxdığımız
proqramlarda istifadə etdiyimiz adi dəyişənlər vastəsilə bu işin öhdəsindən
asanlıqla gəlmək mümkündür(qəbul,saxlama, ...). Bəs onda ünvan dəyişənləri nəyə
lazımdır?
Biz bildiyimiz adi dəyişənlər qeyd etdiyimiz
işlərin öhdəsindən həqiqətən də əla gəlirlər. Məsələn əgər biz yaddaşda 4 bayt
yer ayırıb, bu yerə 155 ədədini yerləşdirmək istəyiriksə onda int x; x=155;
operatorlarından istifadə edərik. Lakin bu dəyişənlərin böyük çatışmayan bir
cəhəti var. Bu cəhət onların statik olmalarıdır. Yəni əgər yaddaşda hansısa
məqsəd üçün yer ayırmaq tələb olunursa bu yalnız proqramın icraya başladığı
zaman ola bilər. Proqramda elan etdiyimiz bütün dəyişənlər üçün proqram icraya
başladığı zaman yaddaşda yer ayrılır və bu yaddaş sahəsi proqramın icrasına
verilir. Proqram bu sahəyə istənilən məlumatları yaza, dəyişə, ... bilər.
Bəs birdən proqram yerinə yetirilərkən
bizim əlavə yaddaşa ehtiyacımız oldu? Ya da, tutaq ki, biz proqramda 100 000
dənə int tipli dəyişəndən istifadə etmişik. Bu təqribən 1Mb -ta yaxın yaddaş
deməkdir (Qeyd edək ki, bu qədər yaddaş RAM-da ayrılır, Hard diskdə yox. Ona
görə 1 Mb yaddaş çox qiymətlidir). Proqramın icrası davam edir lakin, bu
dəyişənlərə daha müraciət etməyəcəyik. Bu dəyişənlər həmi sahəni boş-boşuna
tutur. Biz bu sahədən o dəyişənləri silib həmin yaddaş sahəsini yenidən
proqramın istifadəsinə verə bilərikmi? İndi təsəvvür edin ki, int tipli
dəyişənlər yox, özümüz tərtib etdiyimiz 100 baytlıq struct tipli 1000 000
dəyişəndən istifadə etmişik və bizə artıq lazım deyil. 100 000 000 bayt yaddaş
proqram başa çatana kimi yaddaşda yer tutacaq, bu yerə həmin struct tipli
dəyişənlərdən başqa dəyişənlər yerləşdirə bilmərik. Bu cür israfçılıqıa 2
proqram işləsə kompüterin bütün yaddaşı tutular və 3-cü proqramı yükləmək üçün
biz bu proqramlardan birini söndürməli olarıq.
Ünvan tipli dəyişənlər dinamik
dəyişənlərdir. Yəni proqramın icrası boyu istənilən vaxt onları yaradıb,
istifadə edə bilərik. İstədiyimiz vaxtda da azad edə bilərik. Beləliklə də, onların
tutduğu sahəni yenidən proqrama qaytara bilərik.
Lakin ünvan dəyişənlərinin özəllikləri
sadəcə bununla bitmir. Adında göründüyü kimi ünvan dəyişənləri özlərində qiymət
olaraq yadaşın müəyyən sahəsinin ünvanını saxlayır, həmin sahəyə istinad edir.
Bu isə proqramçıya yaddaşla güclü manipulyasiya imkanı verir. Bu cəhətə görə
istifadəçi proqramlarının tərtibi üçün nəzərdə tutulmuş bir çox proqramlaşdırma
dilləri, o cümlədən JAVA və PHP təhlükəsizlik məsələlərinə görə(ünvan
dəyişənlərinin qiymətini dəyişməklə başqa istifadəçilərə məxsus məlumatlar
yerləşdirilmiş yaddaş sahəsinə müraciət etmək olar) göstəricilərdən istifadə
etməyə icazə vermir.
Bundan əlavə ünvan dəyişənləri öz
tiplarindən olan ünvan dəyişənlərinə mənimsədilə bilər. Bu imkandan əlaqələnmiş
siyahıların yaradılmasında geniş istifadə edirlər. Əlaqələnmiş siyahılarla 8-ci
paraqrafda məşğul olacağıq. Qeyd edək ki, kompüterdə ötürülən və qəbul olunan
məlumatların böyük hissəsi(bir proqramdan digərinə, driverdən istifadəçi
proqramına, TCP səviyyəsindən İP səviyyəsinə v.s. ) məhs əlaqələnmiş
siyahılarla mümkün olur.
Ünvan dəyişənlərindən istifadə edən sadə
proqrama baxaq(prg_4_1.c).
#include <stdio.h>
int main(int argc, char *argv[]){
int x;
int *y;
x=5;
y=&x;
printf("x-in qiymeti = %d, x-in unvani = %p\n",x,y);
return 0;
}
yerinə yetirək:
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_4_1.c -o prog_4_1
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_4_1
x-in qiymeti = 5, x-in unvani = 0xbfcdae50
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Izah:
Ünvan dəyişəni elan etmək üçün adi
qaydada etdiyimiz kimi dəyişən elan edirik, lakin dəyişənin əvvəlinə * simvolunu
artırırıq.
int x; adi
dəyişən elan edirik. int *y; int tipli y ünvan dəyişənin elan
edirik.
int x; int *y;
y=&x; burada biz y-ə x dəyişəninin yaddaşdakı ünvanını
mənimsədirik. x dəyişəninin yaddaşdakı ünvanını təyin
etmək üçün biz & operatorundan istifadə edirik.
y=&x;
Ünvan dəyişənləri elan etdik və onlara qiymət mənimsədə bildik.
Növbəti məsələ ünvan dəyişənlərinin ünvanladığı yaddaş sahəsindən istifadə
etmək məsələsidir.
Ünvan dəyişənlərinin ünvanladığı yaddaş sahəsinə müaciət etmək
üçün dəyişəni elan edərkən istifadə etdiyimiz * operatorundan istifadə edirik.
nümunə proqrama baxaq(prg_4_2.c).
#include <stdio.h>
int main(int argc, char *argv[]){
int x;
int *y; /* tam tipli unvan deyisheni*/
x=5;
printf("x-in qiymeti = %d\n",x);
y=&x; /* & unvan operatorudur */
*y=155;
printf("y-in qiymeti = %d\n",*y); /* y x-in yaddshina unvanlandigindan(y=&x)
*y – I deyismek birbasha x-i deyishdirir.
Bunu yoxlamaq uchun printf -den istifade edirik */
printf("x-in qiymeti = %d\n",*x);
return 0;
}
yerinə yetirək
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_4_2.c -o prog_4_2
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_4_2
x-in qiymeti = 5
y-in qiymeti = 155
x-in qiymeti = 155
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Beləliklə biz ünvan dəyişənlərinin
unvanlandığı yaddaş sahəsindən istifadə edə bilirik. İdi isə ünvan dəyişənləri
ilə bağlı sonuncu və ən maraqlı məqam. Dinamik yaradılma və yaddaşdan silmə.
prg_4_3.c
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]){
int x;
int *y; /* tam tipli unvan deyisheni*/
x=5;
y=(int *)malloc(sizeof(int)); /* dinamik yaradilma */
*y=176;
printf("y-in qiymeti = %d,*y);
free(y); /* y -e ayrilan yaddash sahesini y-den azad edir
ve bu sahede olan butun melumat silinir*/
return 0;
}
yerinə yetirək
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#gcc prog_4_3.c -o prog_4_3
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#./prog_4_3
y-in qiymeti = 176
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Funksiyalar
C dilinin proqramçılar arasında ən
məşhur dil olmasında rol oynayan 2 ən güclü imkanından biri funksiyalardır.
Digəri isə yaddaş dəyişənləridir. Funksiyalar
proqramın her-hansı bir hissesine ad vermekle bu hisseye proqramın istənilən
yerindən, digər funksiyalardan və eləcə də digər proqramlardan nüraciət etməyə
imkan verir. Bu hissəyə
funksiyanın mətni, bu hissəyə verilən ada isə funksiyanın adı deyilir. Nümunə
proqrama baxmazdan əvvəl funksiyalarla bağlı bir neçə məqama nəzər salaq.
Ola bilsin ki, funksiyanın mətni yerinə yetirildikdən sonra hansısa bir nəticə alınsın və ya bu mətn hissəsi hansısa əməliyyatlar yerinə yetirsin və heç bir nəticə qaytarmasın(void). Digər bir məqam odur odur ki, funksiya mətnində istifadə olunan bəzi dəyişələrin qiyməti funksiya çağırılarkən ona parametr kimi ötürülsün və ya funksiya çağırılarkən ona heç bir parametr ötürülməsin(void).
Indi isə gəlin konkret proqram nümunələrində funksiyalar, onların tərtibi, çağırılma qaydası ilə tanış olaq və bu məqamların hər birini ayrı-ayrı təhlil edək.
Ola bilsin ki, funksiyanın mətni yerinə yetirildikdən sonra hansısa bir nəticə alınsın və ya bu mətn hissəsi hansısa əməliyyatlar yerinə yetirsin və heç bir nəticə qaytarmasın(void). Digər bir məqam odur odur ki, funksiya mətnində istifadə olunan bəzi dəyişələrin qiyməti funksiya çağırılarkən ona parametr kimi ötürülsün və ya funksiya çağırılarkən ona heç bir parametr ötürülməsin(void).
Indi isə gəlin konkret proqram nümunələrində funksiyalar, onların tərtibi, çağırılma qaydası ilə tanış olaq və bu məqamların hər birini ayrı-ayrı təhlil edək.
proq_5_1.c
#include <stdio.h>
int main(int argc, char *argv[]){
printf("Setirlerin ekrana verilmesi\n");
printf("Setir 1\n");
printf("bu gun yagish yagir\n");
printf("Bu gun kulek esir\n");
return 0;
}
programı yerinə yetirək.
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_5_1.c -o prog_5_1
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_5_1
Setirlerin ekrana verilmesi
Setir 1
bu gun yagish yagir
Bu gun kulek esir
user@gnu_linux:~/prg#
proqram bizə tam aydındır. Indi isə gəlin
printf("Setir 1\n");
printf("bu gun yagish yagir\n");
printf("Bu gun kulek esir\n");
proqram hissəsindən ibarət cap_et funksiyası yaradaq və proqramımızda bu
funksiyadan istifadə edək.
prog_5_2.c
#include<stdio.h>
void cap_et(void);
int main(int argc, char *argv[]){
cap_et();
return 0;
}
/*-----------------------cap_et funksiyasi --------------------------------*/
void cap_et(void)
{
printf("Setirlerin ekrana verilmesi\n");
printf("Setir 1\n");
printf("bu gun yagish yagir\n");
printf("Bu gun kulek esir\n");
}
proqramı yerinə yetirək.
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#gcc prog_5_2.c -o prog_5_2
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#./prog_5_2
Setirlerin ekrana verilmesi
Setir 1
bu gun yagish yagir
Bu gun kulek esir
user@gnu_linux:~/prg#
İzahı
proqramda ilk #include<stdio.h> sətrindən sonra gələn void
cap_et(void); sətridir. Bu sətir funksiyanın elanı sətridir. Hər bir funksiyanı
proqramda istifadə etməzdən əvvəl onu elan etmək lazımdır. Bu sətir kompilyatora
funksiyanın adı, qaytaracağı nəticənin tipi və ona ötürüləcək parametrlər
haqqında məlumat verir. Bu sətirdə birinci yerdə gələn söz funksiyanın
qaytaracağı nəticənin tipini bildirir.
Əgər funksiya heç bir nəticə qaytarmırsa onda bu yerdə void yazılır , baxdığımız haldakı kimi. Funksiyanın tipindən sonra funksiyanın adı qeyd olunur – cap_et. Addan sonra () mötərəzələri arasında funksiyanın parametrləri haqda məlumat qeyd olunur. Əgər funksiya heç bir parametr qəbul etmirsə burada void yazırıq.
Əgər funksiya heç bir nəticə qaytarmırsa onda bu yerdə void yazılır , baxdığımız haldakı kimi. Funksiyanın tipindən sonra funksiyanın adı qeyd olunur – cap_et. Addan sonra () mötərəzələri arasında funksiyanın parametrləri haqda məlumat qeyd olunur. Əgər funksiya heç bir parametr qəbul etmirsə burada void yazırıq.
prog_5_3.c -ni yerinə yetirin və onu başa düşməyə çalışın.
prog_5_3.c
#include<stdio.h>
void cap_et(void);
int main(int argc, char *argv[]){
int i;
for(i=0; i<5; ++i)
cap_et();
return 0;
}
/*-----------------------cap_et funksiyasi --------------------------------*/
void cap_et(void)
{
printf("Setirlerin ekrana verilmesi\n");
printf("Setir 1\n");
printf("bu gun yagish yagir\n");
printf("Bu gun kulek esir\n");
}
Digər proqrama baxaq.
prog_5_4.c
#include<stdio.h>
int sahe(int,int);
int main(int argc, char *argv[])
{
int x,y,z;
printf("Duzbucaqlinin enini daxil edin\n");
scanf("%d",&x);
printf("Duzbucaqlinin uzunlugunu daxil edin\n");
scanf("%d",&y);
z=sahe(x,y);
printf("Duzbucaqlinin sahesi =%d\n",z);
return 0;
}
int sahe(int t1, int t2)
{
int sh;
sh=t1*t2;
return sh;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_5_4.c -o prog_5_4
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_5_4
Duzbucaqlinin enini daxil edin
6
Duzbucaqlinin uzunlugunu daxil edin
8
Duzbucaqlinin sahesi =48
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Izahı
Burada biz yeni sahe funksiyası yaradırıq hansı ki int tipli
2 parametr qebul edir və nəticə olaraq
Mötərizələr arasında funksiyaya ötürülən parametrlərin tipləri sadalanır, vergüllə ayrılaraq. Qeyd edək bu zaman her parametrin tipinin qarşısında ad da vere bilerik, kompilyator bunu nəzərə almır, o ancaq tipə əhəmiyyət verir. Yəni əgər yuxarıdakı proqramda sahə funksiyasını elan edərkən biz
Biz proqramda sahe funksiyasını
Biz burada
Sual? Tutaq ki istifadəçi
int tipli
ədəd (düzbucaqlının sahəsini ) qaytarır. int sahe(int,int);Mötərizələr arasında funksiyaya ötürülən parametrlərin tipləri sadalanır, vergüllə ayrılaraq. Qeyd edək bu zaman her parametrin tipinin qarşısında ad da vere bilerik, kompilyator bunu nəzərə almır, o ancaq tipə əhəmiyyət verir. Yəni əgər yuxarıdakı proqramda sahə funksiyasını elan edərkən biz
int sahe(int,int); sətri
əvəzinə int sahe(int teref2,int teref2); də yaza
bilərdik. Sadəcə özümüzə və bu proqrama daha sonra baxan proqramçılara mılumat
məqsədilə. Əgər proqramda bir funksiyada istifadə olunursa bunun elə də
əhəmiyyəti yoxdur, lakin proqramda 1000-lə funksiyadan istifadə olunduqda
(məsələn nüvənin yaddaşın idarə olunması hissəsi və ya şəbəkə hissəsi) bunun
əhəmiyyəti hiss olunur.Biz proqramda sahe funksiyasını
z=sahe(x,y); sətrində
çağırırıq. x və y dəyişənlərinin
qiymətləri scanf funksiyası
vasitəsilə istifadəçidən alınır və sahə funksiyasına parametr kimi ötürülür.
sahe funksiyasında bu 2 dəyişənin hasili hesablanır və qaytarılan nəticə z
dəyişəninə mənimsədilir.sahe funksiyasının
mətnində biz int tipli sh dəyişəni
elan edirik və parametrlərin hasilini hesablayaraq bu dəyişənə mənimsədirik.
Daha sonra return əmri vasitəsilə funksiyanın proqramda çağırılma nöqtəsinə( z=sahe(x,y);)
qaytarılacaq nəticə göstərilir. return əmri
funksiyasiyanın icrasını başa çatdırır və proqramın icrası funksiyanın çağrılma
yerindən davam edir. Biz burada
x,y dəyişələri
daxil etdik və onlara scanf vasitasilə istifadəçinin daxil etdiyi qiymətləri
mənimsətdik, daha sonra sahə funksiyasını çağırdıq. x,y -in
qiymətlərini bu funksiyaya parametr kimi ötürdük. z=sahe(x,y); bu
yerdə proqramın icrası sahə funksiyasına ötürüldü və x,y -in
qiymətlərindən istifadə olunaraq hsablama aparıldı, nəticə zdəyişəninə
mənimsədildi və proqramın icrası davam etdi.Sual? Tutaq ki istifadəçi
5 və 7 ədədlərini
daxil edib.x=5; y=7;. sahə funkdiyası
yerinə yetirildikdən sonra x və y -in
qiyməti dəyişmədiki? Yəni funksiyanın mətnində ona ötürülən arqumentlərə edilən
dəyişiklik funksiya icra olunduqdan sonra öz qüvvəsini saxlayırmı? Bunu
yoxlamaq üçün printf funksiyasından istifadə edək.
prog_5_5.c
#include<stdio.h>
int sahe(int,int);
int main(int argc, char *argv[])
{
int x,y,z;
printf("Duzbucaqlinin enini daxil edin\n");
scanf("%d",&x);
printf("Duzbucaqlinin uzunlugunu daxil edin\n");
scanf("%d",&y);
z=sahe(x,y);
printf("Duzbucaqlinin sahesi =%d\n",z);
printf("x=%d, y= %d\n",x,y);
return 0;
}
int sahe(int t1, int t2)
{
int sh;
sh=t1*t2;
return sh;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_5_5.c -o prog_5_5
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_5_5
Duzbucaqlinin enini daxil edin
6
Duzbucaqlinin uzunlugunu daxil edin
8
Duzbucaqlinin sahesi =48
x=6, y= 8
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Qeyd edək ki bu halda biz sahə funksiyasında ona ötürülən
parametrlərin qiymətlərini dəyişmədik. İdi gəlin digər proqramda sahə
funksiyasında bu qiymətləri dəyişək və nəticəni yenidən yoxlayaq.
proq_5_6.c
#include<stdio.h>
int sahe(int,int);
int main(int argc, char *argv[])
{
int x,y,z;
printf("Duzbucaqlinin enini daxil edin\n");
scanf("%d",&x);
printf("Duzbucaqlinin uzunlugunu daxil edin\n");
scanf("%d",&y);
z=sahe(x,y);
printf("Duzbucaqlinin sahesi =%d\n",z);
printf("x=%d, y= %d\n",x,y);
return 0;
}
int sahe(int t1, int t2)
{
int sh;
sh=t1*t2;
/* funksiyaya oturulen arqumentlerin(t1,t2) qiymtlerini
burada deyishirik */
t1=1; t2=2;
return sh;
}
Gördüyümüz kimi sahə funksiyasında biz sahəni hesabladıqdan
sonra ona ötürülən parametrlərin qiymətlərini dəyişdirdik.
Nəticəni yoxlayaq.
Nəticəni yoxlayaq.
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_5_6.c -o prog_5_6
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_5_6
Duzbucaqlinin enini daxil edin
4
Duzbucaqlinin uzunlugunu daxil edin
7
Duzbucaqlinin sahesi =28
x=4, y= 7
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Gördüyümüz kimi funksiyanın daxilində ona ötürülən parametrlərin
qiymətlərinin dəyişdirilməsi funksiya icra olduqdan sonra öz qüvvəsini itirdi,
Bunun səbəbi odur ki, funksiya çağırılarkən parametr kimi ona əslində
Dəyişənlərin funksiyaya bu cür ötürülməsinin müsbət və mənfi tərəfləri var. Müsbət hal odur ki qiymətə görə ötürülmə zamanı bütün əməliyyatlar dəyişənlərin surətləri üzərində aparıldığından dəyişənlərin qiymətləri hər cür dəyişiklikdən qorunmuş olur.
Mənfi hal odur ki, bu zaman dəyişənlərin surətləri yaradılır və buna proqramçı üçün ən qiymətli resurslar – əməli yaddaş və zaman sərf olunur.
Parametrlərin funksiyaya ötürülməsinin digər variantı da ünvana görə ötürülmədir. Bu zaman, dəyişənlərin surətləri yaradılmır və funksiyaya dəyişənlərin ünvanları ötürülür. Nəticədə funksiyada dəyişənlər üzərində aparılan bütün əməliyyatlar məhz onların əsli üzərində aparılmış olur.
x və y- in
qiymətləri olduğu kimi qaldı. Dəyişənlərin funksiyaya belə ötürülməsinə
deyirlər qiymətə görə ötürmə.Bunun səbəbi odur ki, funksiya çağırılarkən parametr kimi ona əslində
x və y ötürülmür,
bu dəyişənlərin surətləri yaradılır və funksiyaya ötürülür. Funksiyanın mətnində
bu dəyişənlər üzərində aparılan bütün dəyişikliklər əslində onların surətləri
üzərində aparılır.Dəyişənlərin funksiyaya bu cür ötürülməsinin müsbət və mənfi tərəfləri var. Müsbət hal odur ki qiymətə görə ötürülmə zamanı bütün əməliyyatlar dəyişənlərin surətləri üzərində aparıldığından dəyişənlərin qiymətləri hər cür dəyişiklikdən qorunmuş olur.
Mənfi hal odur ki, bu zaman dəyişənlərin surətləri yaradılır və buna proqramçı üçün ən qiymətli resurslar – əməli yaddaş və zaman sərf olunur.
Parametrlərin funksiyaya ötürülməsinin digər variantı da ünvana görə ötürülmədir. Bu zaman, dəyişənlərin surətləri yaradılmır və funksiyaya dəyişənlərin ünvanları ötürülür. Nəticədə funksiyada dəyişənlər üzərində aparılan bütün əməliyyatlar məhz onların əsli üzərində aparılmış olur.
Parametrlərin ünvana görə ötürülməsi
prog_5_7.c -ni yerinə yetirək
prog_5_7.c -ni yerinə yetirək
#include<stdio.h>
int sahe(int *,int *);
int main(int argc, char *argv[])
{
int x,y,z;
printf("Duzbucaqlinin enini daxil edin\n");
scanf("%d",&x);
printf("Duzbucaqlinin uzunlugunu daxil edin\n");
scanf("%d",&y);
z=sahe(&x,&y);
printf("Duzbucaqlinin sahesi =%d\n",z);
printf("x=%d, y= %d\n",x,y);
return 0;
}
int sahe(int *t1, int *t2)
{
int sh;
sh=(*t1)*(*t2);
*t1=1; *t2=2;
return sh;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg# gcc prog_5_7.c -o prog_5_7
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_5_7
Duzbucaqlinin enini daxil edin
3
Duzbucaqlinin uzunlugunu daxil edin
8
Duzbucaqlinin sahesi =24
x=1, y=2
user@gnu_linux:~/prg#
Izahı (to do: ahmed bura izah yaz)
Gördüyümüz kimi funksiayda ona ötürülən
parametrlər üzərində edilən dəyişiklik öz qüvvəsini saxladı. Izahı:
int sahe(int *,int *); sətri ilə sahə funksiyası elan edilir.
Bu sətrdə göstərilir ki, sahə funksiyası nəticə olaraqint tipli qiymət qaytarır. Bizim üçün maraqlı () mötərizələri arasında onaldı hissədir. Qiymətə görə
ötürmə zamanı biz yalnız tipi göstərirdik. Ünavana görə ötürmə zamanı isə
sadəcə tipdən sonra * simvolu yazırıq. Sahə funksiyasına proqramda z=sahe(&x,&y); sətrində müraciət edirik. Burada & operatoru vastəsilə sahə funksiyasına parametr kimix və y dəyişənlərinin yaddaşdakı ünvanları
ötürülür.
Cərgələr
Biz indiyə kimi adi dəyişənlər və ünvan
dəyişələri ilə tanış olduq. Bu dəyişənlər istifadə üçün çox rahatdır və çox
geniş tətbiq olunur. misal üçün 2 tam ədəd elan etmək istəyiriksə onda
Lakin çox vaxt elə olur ki, proqramlda eyni tipli
Cərgələr eyni tipdən olan istənilən sayda elementə eyni adla müraciət etməyə imkan verir. Cərgə (cərgə dəyişəni ) elan etmək üçün aşağıdakı sintaksisdən istifadə edirlər.
məsələn
Bu cərgənin hər bir elementinə
Belə bir proqrama baxaq.
int ed1; ed2; yazırıq.Lakin çox vaxt elə olur ki, proqramlda eyni tipli
100 000 dəyişənə ehtiyac olur . Əgər bu halda biz hər bir
dəyişən üçün bir ad təyin etsək onda proqramda müxtəlif adlı 100 000 dəyişən elan etməliyik. Bu isə
praktik olaraq qeyri mümkündür. Belə hallarda cərgələrdən və əlaqələnmiş
siyahılardan istifadə edirlər.Cərgələr eyni tipdən olan istənilən sayda elementə eyni adla müraciət etməyə imkan verir. Cərgə (cərgə dəyişəni ) elan etmək üçün aşağıdakı sintaksisdən istifadə edirlər.
tip
cergenin_adi [ elementlerin_sayi ] ;məsələn
int x[5]; biz
burada int tipli 6 elementli x cərgəsi
elan etdik.Bu cərgənin hər bir elementinə
x[index] kimi
müraciət edə bilərik. Burada index 0-dan 5-ə kimi
(5 -də
daxil olmaqla) qiymət alır. Belə bir proqrama baxaq.
int tipli 5 elementdən
ibarət cərgə yaradaq. Bu cərgənin elementinə müxtəlif qiymətlər mənimsədək.
Daha sonra bu cərgənin elementlərinin ədədi ortasını (cəmin saya nisbəti)
tapaq.
proqram aşağıdakı kimi olar:
prog_6_1.c
prog_6_1.c
#include <stdio.h>
int main(){
int x[5];
int i,s;
x[0]=1; x[1]=5; x[2]=7;
x[3]=45; x[4]=22;
s=0;
for (i=0; i>5; ++i)
s=s+x[i];
printf("x cergesinin elementleri ashagidakilardir\n");
for(i=0; i>5; ++i)
printf("%d\n",x[i]);
printf("x-in elementlerinin ededi ortasi %d\n", s/5);
return 0;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg# gcc prog_6_1.c -o prog_6_1
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_6_1
x cergesinin elementleri ashagidakilardir
1
5
7
45
22
x-in elementlerinin ededi ortasi 16
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Cərgələr ilə ünvan dəyişənlərinin əlaqəsi.
Tutaq ki, proqramda aşağıdakı kimi elan vermişik.
int y, x[4],*z; Bu
zaman yaddaşda nəyin baş verməsinə nəzər salaq.
Cərgənin adı əslində ünvan dəyişənidir. Fərq yalnız ondadır ki,
unvan dəyişənini (
Əgər
z=&y;
z) yaddaşda istənilən yerə unvanlamaq olar,
cərgənin adı (x)
isə ancaq bir yerə (yaddaşda [4] -ün
başlanğıc addresinə) ünvanlanır(shəkil 1). Əgər
z-tə y-in
ünvanını mənimsətmək istəyiriksə onda ünvan operatorundan (&) istifadə
edirik.z=&y;
Əgər
Lakin biz
z-tə
cərgənin (x)
ünvanını mənimsətmək istəsək onda unvan operatorundan istifadə etmirik. z=x; Başqa
sözlə C kompilyatoru cərgənin adı
ilə ünvan dəyişəninə "eyni gözlə" baxır. Lakin biz
x=z; yaza
bilmərik, çünki x-in qiyməti dəyişməzdir. z-ti x-ə
mənimsətdikdən sonra biz x-in
istənilən elementinə z-lə
müraciət edə bilərik. Tutaq ki, x cərgəsinin
ilk elementini (x[0]) z-lə çap
etmək istəyirik.
Kod belə olar:
z=x; printf(*z);şəkil_6_1.
Biz indi
z-in qiymətini artırıb , azaltmaqla onun ünvanladığı
yeri x cərgəsi boyu sürüşdürə bilərik. Misal üçün z-in qiymətini 1 vahid artıraq z++; bu zaman z artıq x[1]-ə ünvanlayacaq. z=x; printf(*z);
z++;şəkil_6_2.
Proqram icra etməyin vaxtıdır.
prog_6_2.c
#include <stdio.h>
int main(){
int x[5],*y;
int i,s;
x[0]=1; x[1]=5; x[2]=7;
x[3]=45; x[4]=22;
s=0;
y=x;
for (i=0; i<5; ++i){
s=s+(*y);
y++; }
printf("x cergesinin elementleri ashagidakilardir\n");
y=x;
for(i=0; i<5; ++i){
printf("%d\n",*y);
y++;}
printf("x-in elementlerinin ededi ortasi %d\n", s/5);
return 0;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg# gcc prog_6_2.c -o prog_6_2
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_6_2
x cergesinin elementleri ashagidakilardir
1
5
7
45
22
x-in elementlerinin ededi ortasi 16
user@gnu_linux:~/prg#
Izahı
#include
<stdio.h> stdio.h faylını proqrama əlavə edirik. printf v.s. funksiyalardan istifadə edə bilmək üçün.
int
main(){int x[5],*y;
main proqramın əsas funksiyasını elan edirik.
int tipindən olan x cərgəsi və y ünvan dəyişəni elan edirik.
int tipindən olan x cərgəsi və y ünvan dəyişəni elan edirik.
x[0]=1; x[1]=5; x[2]=7;
x[3]=45; x[4]=22;
s=0;
x cərgəsinin elementlərinə qiymətlər mənimsədirik.
s cəminə 0 qiyməti mənimsədirik.
s cəminə 0 qiyməti mənimsədirik.
y=x;
y-ə x-in başlanğıc unvanını mənimsədirik. şəkil_6_1.
for
(i=0; i<5; ++i){
dövr elan edirik
s=s+(*y);
s -ə 0 qiyməti mənimsətmişik. y x -in 1 -ci elementinə istinad
edir, deməli *y x-in ilk elementinin qiymətinə bərabərdir.
y++;
}
y -i x -in növbəti elementi üzərinə sürüşdürürük. şəkil_6_2.
printf("x
cergesinin elementleri ashagidakilardir\n");
ekrana setir çap edirik.
y=x;
for dövrünün sonunda y x-in sonuncu elementi üzərinə istinad
edir. y -i yenidən x-in ilk elementi üzərinə sürüşdürürük.
for(i=0;
i<5; ++i){
dövr elan edirik
printf("%d\n",*y);
y -in istinad etdityi elementi çapa
veririk.
y++;}
y -i x -in növbəti elementi üzərinə
sürüşdürürük. şəkil_6_2.
printf("x-in elementlerinin ededi ortasi %d\n", s/5);
return 0;
}
ədədi ortanı çap edib proqramı bitiririk.
Sətirlər
Sətirlər cərgələrin xüsusi halıdır.
Cərgə elementlərinin tipinin char olduğu hal. char tipi
simvol tipi adlanır. Başqa sözlə sətirlər simvollar cərgəsidir. İlk baxışda
sətirlər elə də əhəmiyyət kəsb etmir və onlarla işləmək üçün bir neçə standart
funksiya təyin olunur.
Sətirlərin tətbiq sahəsi çox genişdir, həm nüvədə, həm də istifadəçi proqramlaşdırmasında sətirlərdən çox geniş istifadə olunur. Misal üçün çat proqramında daxil etdiyimiz her-hansı sətir internet şəbəkəsinə qoşulmuş digər komputerdə yerinə yetirilən çat proqramına ötürülən zaman bir neçə dəfə bir yerdən(yaddaşda) digər yere köçürülür.
İstifadəçinin daxil etdiyi parolu yoxlamaq üçün biz proqramda sətirlərin müqaisəsi funksiyalarından istifadə edirik v.s. Sətirlərlə işləyən zaman diqqət verilməsi lazım gələn məsələlərdən biri də sətirin sonu məsələsidir. Sətirlərin sonunu bildirmək üçün '\0' simvolundan istifadə olunur. Sətir funksiyaları bu simvola rast gəldikdə sətrin yerdə qalan hissəsini inkar edirlər.
Sətirlərin tətbiq sahəsi çox genişdir, həm nüvədə, həm də istifadəçi proqramlaşdırmasında sətirlərdən çox geniş istifadə olunur. Misal üçün çat proqramında daxil etdiyimiz her-hansı sətir internet şəbəkəsinə qoşulmuş digər komputerdə yerinə yetirilən çat proqramına ötürülən zaman bir neçə dəfə bir yerdən(yaddaşda) digər yere köçürülür.
İstifadəçinin daxil etdiyi parolu yoxlamaq üçün biz proqramda sətirlərin müqaisəsi funksiyalarından istifadə edirik v.s. Sətirlərlə işləyən zaman diqqət verilməsi lazım gələn məsələlərdən biri də sətirin sonu məsələsidir. Sətirlərin sonunu bildirmək üçün '\0' simvolundan istifadə olunur. Sətir funksiyaları bu simvola rast gəldikdə sətrin yerdə qalan hissəsini inkar edirlər.
prog_7_1.c
#include <stdio.h>
#include <string.h>
int main(int argc, char *argv[]){
char set[12]; /*12 elementden ibaret setir elan edirik*/
strcpy(set,"Azerbaycan");
printf("%s\n",set);
return 0;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg# gcc prog_7_1.c -o prog_7_1
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_7_1
Azerbaycan
user@gnu_linux:~/prg#
Izahı
Burada biz 12 elementden ibaret set adlı setir elan edirik. Daha sonra strcpy funksiyası vasitəsilə set
sətrinə"Azerbaycan" qiymətini
mənimsədirik. strcpy funksiyasi string.h faylinda təyin olduğundan #include <string.h> vasitəsiləstring.h faylini proqrama əlavə edirik. strcpy funksiyasının sintaksisi aşağıdakı
kimidir.
char * strcpy(char *s1, char *s2);
strcpy funksiyası s1 sətrinə s2 sətrini mənimsədir. Sətirlərlə işləyən digər funksiyalara baxaq. strlen funksiyası.
char * strcpy(char *s1, char *s2);
strcpy funksiyası s1 sətrinə s2 sətrini mənimsədir. Sətirlərlə işləyən digər funksiyalara baxaq. strlen funksiyası.
int strlen(char *s);
s sətrində olan elementlərin sayını qaytarır. '\0' simvolunu hesaba almır.
s sətrində olan elementlərin sayını qaytarır. '\0' simvolunu hesaba almır.
char * strncpy(char *s1, char *s2, int
n);
s2 sətrinin ilk n elementini s1-ə köçürür(s1-in əvvəlindən başlayaraq).
s2 sətrinin ilk n elementini s1-ə köçürür(s1-in əvvəlindən başlayaraq).
int strcmp(char *s1, char *s2);
bu funksiyadan sətirlərin müqaisəsi üçün istifadə edirlər. Əgər s1 sətri s2 sətri ilə eynidirsə onda funksiya 0 qiymətini qaytarır, əgər s1-in elementlərinin sayı s2-den azdırsa onda <0 əks halda >0 qiymətini qaytarır.
bu funksiyadan sətirlərin müqaisəsi üçün istifadə edirlər. Əgər s1 sətri s2 sətri ilə eynidirsə onda funksiya 0 qiymətini qaytarır, əgər s1-in elementlərinin sayı s2-den azdırsa onda <0 əks halda >0 qiymətini qaytarır.
char * strcat (char *s1, char *s2);
bu funksiya s1-in sonuna s2-ni əlavə edir.
bu funksiya s1-in sonuna s2-ni əlavə edir.
Strukt tiplər
İndiyə qədər biz dəyişən elan edərkən
Tutaq ki hər-hansı zavodun
Yeni tip təyin etmək üçün sintaksis aşağıdakı kimidir.
int, double, char, long kimi standart tiplərdən istifadə edirdirk. Əlbətdə bu
tpliər çox əlverişlidir, lakin çox vaxt məsələnin şərtinə uyğun olaraq
proqramçının özü yaratdığı tiplərdən istifadə etmək lazım gəlir.Tutaq ki hər-hansı zavodun
100 000 işçisi var. Bizdən tələb olunur ki, bu zavodun
işçilərinin məlumatlar bazası proqramını yazaq. Hər bir işçi haqqında onun adı,
soyadı, yaşı, maaşı, vəzifesi barədə məlumatlar qeyd edilməlidir. Bunun üçün
yeni struct tipi təyin edək.Yeni tip təyin etmək üçün sintaksis aşağıdakı kimidir.
struct yeni_tipin_adı {
tip dey1;
tip dey2;
tip dey3;
.
.
tip deyn; };
Məsələmizə uyğun təyin etməli olduğumuz
yeni tip belə olar. Gəlin bu yeni yaradacağımız tipə
ishci adını verək. struct ishci {
int yash;
char ad[12];
char soyad[20];
char vezife[20];
double maash; };
Beləliklə biz yeni
Məsələn:
yuxaridakı elanda biz
İndi isə maraqlı məqam. Tutaq ki,
ishci tipi
təyin etdik. Bu elandan sonra biz proqramımızda bu tipdən adi tiplər kimi
dəyişənlər və ünvan dəyişənləri elan edə bilərik, lakin gərək tipin adından (ishici)
əvvəl kompilyatora bu tipi bizim özümüz yaratdığımızı bildirmək üçün struct ifadəsini
yerləşdirək.Məsələn:
struct ishci dey1, ishci1,
*muhendis, yeni_ishciler[100]; yuxaridakı elanda biz
ishci tipindən
olan dey1 və ishci1 dəyişənləri, muhendis unvan
dəyişəni və 100 elementliyeni_ishciler cərgəsi
yatardıq. 100 000 ishci
barəsində məlumat saxlamaq üçün biz yeni yaratdığımız ishci tipindən
olan 100 000 elementli
cərgədən istifadə edə bilərik.struct ishci
ishciler[100000];İndi isə maraqlı məqam. Tutaq ki,
int tipindən
olan x dəyişənimiz
var.int x; Əgər
biz bu dəyişənə 4 qiyməti
mənimsətmək istəyiriksə x=4; yazırıq.
Bəs strukt tipindən
olan dəyişənlərə və ya oların təşkil onlunduqları ayrı-ayrı elementlərə necə
qiymət mənimsədək? strukt tipinin
elementlərinə müraciət etmək üçün ( . ) və ya
( -> )
operatorlarından istifadə olunur. Aşağıdakı
kimi:
struct ishci reis;
reis.yash = 50;
strcpy(reis.ad,"Anar");
... v.s.
Artıq strukt tipindən istifadə etməklə
proqram tərtibinin vaxtı çatıb.
prog_8_1.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* yeni_tip adli struct tipi yaradiriq*/
struct yeni_tip{
char ad[20];
int x; };
int main(int argc, char *argv[]){
/* struct yeni_tip -den dey adli deyishen elan edirik*/
struct yeni_tip dey;
dey.x=50;
memset(dey.ad,0,20); /* memset (stdlib.h)
ad -in butun baytlarina 0 qiymeti yaziriq.
setirlerle ishleyerken bunu etmek vacibdir.*/
strcpy(dey.ad,"Ali");
printf("%s,%d\n",dey.ad,dey.x);
return 0;
}
proqramı yerinə yetirək
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_8_1.c -o prog_8_1
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_8_1
Ali,50
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Strukut tipindən olan ünvan dəyişənləri
strukt tipindən aşağıdakı kimi ünvan
dəyişəni təyin edək:
strukt ishci *muhendis;
Əgər biz
strukt tipdən adi yox unvan dəyişəni təyin ediriksə onda strukt tipinin elementlərinə müraciət zamanı ( .) əvəzinə (->) istifadə edirik. misal üçün:muhendis->yash=45; kimi.
DIQQƏT! ÜNVAN DƏYİŞƏNLƏRİ ÜÇÜN YADDAŞDA
YER AYIRMADAN ONLARIN ELEMENTLƏRİNƏ MÜRACİƏT ETMƏK OLMAZ.
Paraqraf 4-dən bilir ki, hər hansı bir
tipdən olan unvan dəyişəninə yaddaşda yer ayırmaq üçün
tip *dey; dey = (tip *) malloc (sizeof(tip));
sintaksisindən istifadə edirik.
Bu sintaksisə əsasən yuxarıda təyin
etdiyimiz muhendis ünvan dəyişəninin elementlərinə qiymət mənimsədə bilmək üçün
əvvəlcə ona yaddaşda aşağıdakı kimi yer ayırmalıyıq.
muhendis = (struct ishci *)malloc(sizeof(struct
ishci));
Bu operatordan sonra biz artıq muhendis
dəyişəninin istənilən elementinə -> operatoru vasitəsilə müraciət edə
bilərik.
muhendis->yash=33;
strcpy(muhendis->ad,"Rustem");
Proqram nümunəsi:
prog_8_2.c
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* yeni_tip adli struct tipi yaradiriq*/
struct yeni_tip{
char ad[20];
int x; };
int main(int argc, char *argv[]){
struct yeni_tip *dey;
/* malloc yaddashda yer ayirir, bu yerde evvelceden bashqa melumatlar ola biler ve bu bize
gozlenilmeyen problemler yarada biler. Ona gore malloc -la yaddashda yer ayiranda
onun butun baytlarini memset -le 0-ra menumsetmek meslehetdir. */
dey = malloc(sizeof(struct yeni_tip));
memset(dey,0,sizeof(struct yeni_tip));
dey->x=50;
strcpy(dey->ad,"Veli");
printf("%s,%d\n",dey->ad,dey->x);
return 0;
}
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# gcc prog_8_2.c -o prog_8_2
user@gnu_linux:~/prg#
user@gnu_linux:~/prg# ./prog_8_2
Veli,50
user@gnu_linux:~/prg#
user@gnu_linux:~/prg#
Hiç yorum yok:
Yorum Gönder