Professional Documents
Culture Documents
C Kitap PDF
C Kitap PDF
NDEKLER
Programlama ve C
Say Sistemleri
Genel Kavramler ve Terimler
Bir C Program Oluturmak
Veri Trleri
Bildirim ve Tanmlama
Deimezler
levler
leler
Bilinirlik Alan ve mr
Kontrol Deyimleri
if Deyimi
lev Bildirimleri
Tr Dnmleri
Dng Deyimleri
Koul leci
nilemci Komutlar - 1
Switch Deyimi
goto Deyimi
Rastgele Say retimi ve Kontrol Deyimlerine likin Genel Uygulamalar
Diziler
char Trden Diziler ve Yazlar
sizeof leci
Gstericiler
Gsterici leleri
Yazlarla lgili lem Yapan Standart levler
Gsterici Hatalar
void Trden Gstericiler
Dizgeler
Gsterici Dizileri
Gstericiyi Gsteren Gsterici
ok Boyutlu Diziler
exit, abort atexit levleri
Dinamik Bellek Ynetimi
Belirleyiciler ve Niteleyiciler
Yaplar
Tr simleri Bildirimleri ve typedef Belirleyicisi
Tarih ve Zaman ile lgili lem Yapan Standart levler
Birlikler
Numaralandrmalar
Bitsel leler
Bit Alanlar
Komut Satr Argmanlar
Dosyalar
Makrolar
nilemci Komutlar - 2
lev Gstericileri
zyinelemeli levler
Deiken Sayda Arguman Alan levler
Kaynaklar
1/529
3
15
25
31
33
39
45
53
73
93
101
102
119
127
137
159
165
175
185
189
199
215
226
229
235
265
280
283
291
301
307
311
321
323
341
359
387
397
411
421
427
441
445
451
485
495
505
519
525
529
PROGRAMLAMA ve C
Yazlm Nedir
Donanm Nedir
Yazlmn Snflandrlmas
3/529
Seviye
4/529
"compiler" derleyici ismi verildi. (Grace Hopper ayn zamanda Cobol dilini gelitiren
ekipten biridir, bug(bcek) szcn ilk olarak Grace Hopper kullanmtr.) Artk
programclar, simgesel szcklerden oluan Assembly programlama dillerini kullanyor,
yazdklar programlar derleyici tarafndan makine koduna dntrlyor ve makine kodu
eski hzndan bir ey yitirmeksizin tam hzla alyordu. Assembly diller 2. kuak diller
olarak tarihte yerini ald.
Assembly dillerinin kullanlmaya balamasyla bilgisayar kullanm hzla artt. Ancak en
basit ilemlerin bile bilgisayara yaptrlmas iin birok komut gerekmesi, programlama
srecini hzlandrma ve kolaylatrma araylarn balatt, bunun sonucunda da daha
yksek seviyeli programlama dilleri gelitirilmeye baland.
Tarihsel sre iinde Assembly dillerinden daha sonra gelitirilmi ve daha yksek seviyeli
diller 3. kuak diller saylr. Bu dillerin hepsi algoritmik dillerdir. Bugne kadar gelitirilmi
olan yzlerce yksek seviyeli programlama dilinden pek az bugne kadar varlklarn
srdrebilmitir. 3. kuak dillerin hemen hemen hepsi ana dilden tretilmitir. 3.
kuak dillerin ilkleri olan bu dil halen varlklarn srdrmektedir:
FORTRAN dili (FORmula TRANslator) karmak matematiksel hesaplamalar gerektiren
mhendislik ve bilimsel uygulamalarda kullanlmak zere 1954 - 1957 yllar arasnda IBM
firmas iin John Backus tarafndan gelitirildi. FORTRAN dili, youn matematik
hesaplamalarn gerektii bilimsel uygulamalarda halen kullanlmaktadr. FORTRAN dilinin
FORTRAN IV ve FORTRAN 77 olmak zere iki nemli srm vardr. Doksanl yllarn
balarnda FORTRAN - 90 isimli bir srm iin ISO ve ANSI standartlar kabul edilmitir.
FORTRAN dili, 3. kuak dillerin en eskisi kabul edilir.
COBOL (COmmon Business Oriented Language) 1959 ylnda, Amerika'daki bilgisayar
reticileri, zel sektr ve devlet sektrndeki bilgisayar kullanclarndan oluan bir grup
tarafndan gelitirildi. COBOL'un gelitirilme amac, veri ynetiminin gerektii ticari
uygulamalarda kullanlacak tanabilir bir programlama dili kullanmakt. COBOL dili de
halen yaygn olarak kullanlyor.
ALGOL (The ALGOritmick Language) 1958 ylnda Avrupa'da bir konsorsiyum tarafndan
gelitirildi. IBM Firmas FORTRAN dilini kendi donanmlarnda kullanlacak ortak
programlama dili olarak benimsediinden, Avrupa'llar da seenek bir dil gelitirmek
istemilerdi. ALGOL dilinde gelitirilen birok tasarm zellii, modern programlama
dillerinin hepsinde kullanlmaktadr.
60'l yllarn balarnda programlama dilleri zerinde yaplan almalar yapsal
programlama kavramn gndeme getirdi. PASCAL dili 1971 ylnda akademik evrelere
yapsal programlama kavramn tantmak iin Profesr Niclaus Wirth tarafndan gelitirildi.
Dilin yaratcs, dile matematiki ve filozof Blaise Pascal'n ismini vermitir. Bu dil, ksa
zaman iinde niversitelerde kullanlan programlama dili durumuna geldi.
Pascal dilinin ticari ve endstriyel uygulamalar desteklemek iin sahip olmas gereken bir
takm zelliklerden yoksun olmas, bu dilin kullanmn kstlamtr. Modula ve Modula-2
dilleri Pascal dili temel alnarak gelitirilmitir.
BASIC dili 1960'l yllarn ortalarnda John Kemeney ve Thomas Kurtz tarafndan
gelitirildi. BASIC isminin "Beginner's All Purpose Symbolic Instruction Code"
szcklerinin ba harflerinden oluturulduu sylenir. Yksek seviyeli dillerin en eski ve
en basit olanlarndan biridir. Tm basitliine karn, birok ticari uygulamada
kullanlmtr. BASIC dili de ANSI tarafndan standartlatrlmtr. Ancak BASIC dilinin ek
zellikler ieren srmleri sz konusudur. rnein Microsoft firmasnn kartt Visual
Basic diline nesne ynelimli programlamaya ilikin birok zellik eklendi. Daha sonra bu
dil Visual Basic dot Net ismini ald. Ayrca BASIC dilinin baz srmleri uygulama
programlarnda -rnein MS Excel ve MS Word programlarnda- kullancnn zelletirme
ve otomatikletirme amacyla yazaca makrolarn yazlmasnda kullanlan programlama
dili olarak da genel kabul grd.
ADA dili ise Amerikan Savunma Departman (Department of Defence -DoD) destei ile
70'li yllardan balanarak gelitirildi. DoD, dnyadaki en byk bilgisayar
kullanclarndan biridir. Bu kurum farkl yazlmsal gereksinimleri karlamak iin ok
sayda farkl programlama dili kullanyordu ve tm gereksinmelerini karlayacak bir dil
arayna girdi. Dilin tasarlanmas amacyla uluslararas bir yarma dzenledi. Yarmay
kazanan irket (CII-Honeywell Bull of France) Pascal dilini temel alarak balatt
5/529
almalarnn sonucunda Ada dilini gelitirdi. Ada dilinin dokmanlar 1983 ylnda
yaymland. Ada ismi, dnr Lord Byron'un kz olan Lady Ada Lovelace'n ismine
gndermedir. Ada Lovelace delikli kartlar hesap makinelerinde ilk olarak kullanlan
Charles Babbage'in yardmcsyd. Charles Babbage hayat boyunca "Fark Makinesi"
(Difference Engine) ve "Analitik Makine" (Analytical Engine) isimli makinelerin yapm
zerinde alt ama bu projelerini gerekletiremeden ld. Yine de gelitirdii tasarmlar
modern bilgisayarlarn atas kabul edilir. Ada Lovelace, Charles Babbage'n makinesi iin
delikli kartlar ve kullanlacak algoritmalar hazrlyordu. Lovelace'in 1800'l yllarn
banda ilk bilgisayar programn yazd kabul edilir. Ada genel amal bir dildir, ticari
uygulamalardan roketlerin ynlendirilmesine kadar birok farkl alanda kullanlmaktadr.
Dilin nemli zelliklerinden biri, gerek zaman uygulamalarna (real-time applications /
embedded systems) destek vermesidir. Baka bir zellii de yksek modler yaps
nedeniyle byk programlarn yazmn kolaylatrmasdr. Ancak byk, karmak
derleyicilere gereksinim duymas; C, Modula-2 ve C++ dillerine kar rekabetini
zorlatrmtr.
ok yksek seviyeli ve genellikle algoritmik yap iermeyen programlarn grsel bir
ortamda yazld diller ise 4. kuak diller olarak isimlendirilirler. Genellikle 4GL (fourth
generation language) olarak ksaltlrlar. nsan algsna en yakn dillerdir. RPG dili 4.
kuak dillerin ilki olarak kabul edilebilir. zellikle kk IBM makinelerinin kullanclar
olan irketlerin, rapor retimi iin kolay bir dil istemeleri zerine IBM firmas tarafndan
gelitirilmitir.
Programlama dilleri dzeylerine gre baz gruplara ayrlabilir:
ok yksek dzeyli diller ya da grsel diller ya da ortamlar (visual languages):
Access, Foxpro, Paradox, Xbase, Visual Basic, Oracle Forms.
Yksek dzeyli diller.
Fortran, Pascal, Basic, Cobol.
Orta dzeyli programlama dilleri:
Ada, C. (Orta seviyeli diller daha az kaypla makine diline evrilebildiinden daha hzl
alr.)
Dk dzeyli programlama dilleri:
Simgesel makine dili (Assembly language).
Makine dili:
En aa seviyeli programlama dili. Saf makine dili tamamen 1 ve 0 lardan oluur.
Okunabilirlik
6/529
Tanabilirlik
Tanabilirlik (portability) bir sistem iin yazlm olan kaynak kodun baka bir sisteme
gtrldnde, hatasz bir biimde derlenerek, doru bir ekilde altrlabilmesi
demektir.
Tanabilirlik standardizasyon anlamna da gelir. Programlama dilleri (ISO International
Standard Organization) ve ANSI (American National Standard Institute) tarafndan
standardize edilirler. lk olarak 1989 ylnda standartlar oluturulan C Dili, dier
programlama dillerinden daha tanabilir bir programlama dilidir.
Verimlilik
Verimlilik (Efficiency) bir programn hzl almas ve daha az bellek kullanma zelliidir.
Programn alma hz ve kulland bellek miktar pek ok etkene baldr. phesiz
kullanlan algoritmann da hz ve kullanlan bellek zerinde etkisi vardr. Programn
altrld bilgisayarn da doal olarak hz zerinde etkisi vardr. Verimlilik bir
programlama dilinde yazlm bir programn altrldnda kulland bellek alan ve
alma hz ile ilgili bir kstas olarak ele alnabilir. Verimlilik zerinde rol oynayabilecek
dier etkenler sabit brakldnda, kullanlan programlama dilinin tasarmnn da verim
zerinde etkili olduu sylenebilir. Bu adan bakldnda C verimli bir dildir.
Kullanm Alan
Baz diller zel bir uygulama alan iin tasarlanrlar. Sistem programlama Yapay zeka
uygulamalar, simlasyon uygulamalar, veritaban sorgulamalar, oyun programlarnn
yazm amacyla tasarlanan ve kullanlan programlama dilleri vardr. Baz diller ise daha
geni bir kullanm alanna sahiptir. rnein veritaban sorgulamalarnda kullanlmak zere
tasarlanan bir dil mhendislik uygulamalarnda da kullanlabilir.
C dili de bir sistem programlama dili olarak domasna karn, gl yapsndan dolay
ksa bir sre iinde genel amal bir dil haline gelmitir. Oysa PASCAL, BASIC ok daha
genel amal dillerdir.
C ana uygulama alan "sistem programcl" olan bir dildir. Ancak neredeyse tm
uygulama alanlar iin C dilinde programlar yazlmtr.
7/529
rnek vermek gerekirse UNIX iletim sisteminin % 80'i C dili ile geri kalan ise simgesel
makine dili ile yazlmtr. Bu iletim sistemi ilk olarak BELL Laboratuarlarnda
oluturulmutur. Kaynak kodlar gizli tutulmam, bylece eitli kollardan gelitirilmesi
mmkn olmutur. Daha sonra gelitirilen UNIX bazl iletim sistemi uygulamalarna
deiik isimler verilmitir.
C bilimsel ve mhendislik alanlarna kullanlabilen genel amal bir sistem programlama
dilidir.
Bir btn olarak zlmesi zor olan problemlerin paralara ayrlmas ve bu paralarn ayr
ayr zlmesinden sonra paralar arasndaki balantnn salanmas programlamada sk
bavurulan bir yntemdir. Bir programlama dili buna olanak salayan aralara sahipse
programlama dilinin alt programlama yetenei vardr denilebilir. Alt programlama
yetenei bir programlama dilinin, program paralar halinde yazmay desteklemesi
anlamna gelir.
Alt programlama Yapsal Programlama Teknii'nin de ayrlmaz bir parasdr. Alt
programlamann getirdii baz nemli faydalar vardr. Alt programlar kaynak kodun
klmesini salar. ok yinelenen ilemlerin alt programlar kullanlarak yazlmas
alabilir programn kodunu kltr. nk alt programlar yalnzca bir kere, alabilir
kod iine yazlr. Program kodu alt programn olduu yere atlatlarak bu blgenin
defalarca altrlmas salanabilir.
Alt programlama alglamay kolaylatrr, okunabilirlii artrr, ayn zamanda kaynak
kodun test edilmesini kolaylatrr, kaynak kodun daha kolay gncelletirilmesini salar.
Alt programlamann en nemli faydalarndan biri de oluturulan alt programlarn birden
fazla projede kullanlabilmesidir (reusability).
C alt programlama yetenei yksek bir dildir. C'de alt programlara ilev (function) denir.
levler C dilinin yap talardr.
9/529
Benzer amala Pascal diline eklemeler yaplarak Delphi dili, Cobol dilinin yenilenmesiyle
Oocobol, ada dilinin yenilenmesiyle ise ada 95 dilleri gelitirilmitir.
Baz programlama dilleri ise dorudan nesneye ynelmi programlama tekniini
destekleyecek ekilde tasarlanarak gelitirilmitir. Byle dillere saf nesne ynelimli diller
de denir. rnein Java, Eiffel, C#, saf nesne ynelimli dillerdir.
zetle, bir programlama dili hakknda sorulacak sorulardan belki de en nemlilerinden
biri, o programlama dilinin belirli bir programlama tekniini destekleyen aralara sahip
olup olmaddr.
C dili, var olan aralaryla prosedrel programlama tekniine tam destek veren bir dildir.
Giri / k Kolayl
10/529
C dili UNIX iletim sistemi ile btnleme iindedir. UNIX iletim sisteminde kullanlan
baz aralar kullancnn C dilini bildiini varsayar.
Dier tm bilgisayar programlama dillerinde olduu gibi C dilinin de zayf taraflar vardr.
Esnek ve gl bir dil olmas programcnn hata yapma riskini artrr. C dilinde yazlan
kodlarda yaplan yanllklarn bulunmas dier dillere gre daha zor olabilir.
C dilinin UNIX iletim sisteminin bir yan rn olarak doduu sylenebilir. nce Unix
iletim sisteminin tarihine deinelim:
1965 ylnda MITde MAC isimli bir proje gerekletirildi. MAC bir bilgisayar sisteminin
zaman paylamn salayan ilk projelerden biriydi. Bu proje ile ayn bilgisayar 30 a kadar
kullanc tarafndan paylalabiliyordu 160 ayr yazcy kullanmak da mmknd. Bu
projenin baarsndan cesaret alan MIT, General Electric ve Bell Laboratuarlar ile bir
ortak giriim oluturararak zaman paylaml yeni bir sistem oluturma almasna
balad. Bu projeye MULTICS (Multiplexed Information and Computing Service) ismi
verildi. Bell Laboratuarlar projenin yazlm ksmndan sorumluydu. 1969 ylnda Bell
Laboratuarlar proje sresinin uzamas ve proje maliyetinin yksek olmas nedeniyle
projeden ayrld.
1969 ylnda Bell Laboratuarlarnda Ken Thompson nclnde bir grup yeni bir
alternatif arayna girdi. MULTICS projesinde alan Ken Thompson ve ekip arkada
Dennis Ritchie bu konuda bir hayli deneyim kazanmt.
Thompson ve ekibi, insan ve makine arasndaki iletiimi kolaylatracak bir iletim sistemi
tasarmna giriti. letim sisteminin omurgas yazldnda MULTICS'e gnderme
yaplarak iletim sistemine Unix ismi verildi.
Bu yllarda programclar PL/1, BCPL gibi yksek seviyeli dilleri kullanyorlard.
Thompson DEC firmasnn ana bellei yalnzca 8K olan PDP 7 isimli bilgisayar zerinde
alyordu. Thompson 1960 l yllarda Martin Richards tarafndan gelitirilen BCPL dilini
kullanmakta deneyimliydi. Kendi dilini tasarlarken Thompson, 1960 yllarnn ortalarnda
Martin Richards tarafndan gelitirilmi BCPL dilinden yola kt. (BCPL = Business
Common Programming Language). Bu dil de CPL = Cambridge Programming
Language'den tretilmitir. CPL'in kayna da tm zamanlarn en eski ve en etkili
dillerinden biri olan ALGOL 60'dr. ALGOL 60, Pascal, ADA, Modula2 dillerinin de
atasdr. Bu dillere bu yzden C dilinin kuzenleri de diyebiliriz. Aada ALGOL 60 dil ailesi
grlyor:
11/529
Thompson gelitirdii bu dilin ismini B koydu. Dennis Ritchie, UNIX projesine katlnca B
dilinde programlamaya balad. B dili daha da gelitirilmiti ve artk daha yeni bir
teknoloji olan Dec PDP-11 bilgisayarlarda alyordu. Thompson, UNIX iletim sisteminin
bir ksmn B dilinde yeniden yazd. 1971 ylna gelindiinde B dilinin PDP-11 bilgisayarlar
ve UNIX iletim sisteminin gelitirilmesi iin ok uygun olmad iyice ortaya kt. Bu
yzden Ritchie, B programlama dilinin daha ileri bir srmn gelitirmeye balad.
Oluturduu dili ilk nce NB (new B) olarak isimlendirdi. Ama gelitirdii dil B dilinden
iyice kopmaya ve ayr bir karakter gstermeye balaynca dilin ismini de C olarak
deitirdi. 1973 ylnda UNIX iletim sisteminin byk bir ksm C dili ile yeniden yazld.
Ken Thompson ve Dennis Ritchie
Unix letim Sistemi zerinde
alrken (Yl: 1972)
C'nin evrimi ve gelimesi 70'li
yllarda da srd. Ancak uzun bir
sre C dili dar bir evrede
kullanld. Geni kitleler tarafndan
tannmas ve kullanlmaya
balamas 1978 ylnda Dennis
Ritchie ve Brian Kernighan
tarafndan yazlan "The C
Programming Language" kitab ile
olmutur. Bu kitap ayn zamanda yazlm konusunda yazlan en iyi eserlerden biri olarak
deerlendirilmektedir. C standartlarnn oluturulmasna kadar olan dnemde bu kitap
ounluun benimsedii genel kabul gren gayri resmi (de facto) bir standart grevi de
grmtr. Bu kitap ksaca K&R (Kernighan & Ritchie) olarak isimlendirilmektedir.
1970'li yllarda C programclarnn says azd ve bunlardan
ou UNIX kullanclaryd. Ama artk 80'li yllar gelince
C'nin kullanm UNIX snrlarn at, farkl iletim sistemleri
iin alan derleyiciler piyasaya kt. C dili de IBM
PC'lerde youn olarak kullanlmaya balad.
C'nin hzl bir biimde yaygnlamas baz sorunlar da
beraberinde getirdi. Derleyici yazan firmalar, referans
olarak Ritchie ve Kernighan'n kitabn esas alyorlard ama
sz konusu kitapta baz noktalar ok da ayrntl bir
biimde aklanmamt. zellikle hangi noktalarn C dilinin
bir zellii hangi noktalarn ise UNIX iletim sisteminin bir
zellii olduu o kadar ak olmad iin bir takm
karklklar ortaya kyordu. Bylece derleyici yazanlarn
rnlerinde de farkllklar ortaya kyordu. Ayrca kitabn
yaynlanmasndan sonra da dilde bir takm gelitirmeler,
iyiletirmeler, deiiklikler yapld iin, birbirinden ok
farkl derleyiciler piyasada kullanlmaya balanmt.
Hangi C
C tek bir programlama dili olmasna karn C'nin farkl srmlerinden sz etmek olasdr:
Geleneksel C
C dili ilk olarak 1978 ylnda yaymlanan Dennis Ritchie ve Brian Kernighan tarafndan
yazlm "The C Programming Language" isimli kitapta anlatlmt. 1980'li yllarda C dili
bu kitapta anlatlan genel geer kurallara gre kullanld, derleyici yazan firmalar bu
kurallar esas aldlar. Bu dnem iinde kullanlan derleyiciler arasnda baz kural ve yorum
farkllklar olsa da ana kurallar zerinde bir genel bir uzlama olduu sylenebilir. C
dilinin standartlatrlmas srecine kadar kullanlan bu srme "Geleneksel C (Traditional
12/529
Standart C (1989)
Standart C (1995)
1995 ylnda Wg14 komitesi C89 standartlar zerinde iki teknik dzeltme ve bir eklenti
yaymlad. Bu dzeltme notlar ve eklentiler ok nemli deiiklikler olarak
grlmemektedir. Bu dzeltmelerle deitirilmi standartlara "C89 with amendment 1" ya
da ksaca C95 denmektedir
Standart C (1999)
C99 ile getirilen deiiklikler ve eklentiler C95'e gre daha fazla ve nemlidir. Ancak C
dilinin doal yaps deitirilmemitir.
C++ Dili
C++ dili de 1980 li yllarn balarnda Bjarne Stroustrup tarafndan AT&T Bell
Laboratuarlar'nda gelitirildi. C++ dili C'den ayr, tamamyla baka bir programlama
dilidir. Dilin tasarmcs Bjarne Stroustoup, C'nin orta seviyeli zelliklerine baz eklemeler
yaparak, bata nesne ynelimli programlama teknii olmak zere baka programlama
tekniklerini de destekleyen ayr bir dil oluturdu.
C++ dili de dinamik bir gelime sreci sonunda 1998 ylnda standart haline getirildi.
Oluturulan resmi standardn ismi ISO/IEC 14882:1998'dir.
C++ birden fazla programlama tekniini destekleyen (multi-paradigm) bir dildir. Temel
szdizimi byk lde C dilinden alnmtr. Ancak szdizim zellikleri birbirine benzese
13/529
de dilin aralarnn kullanlma biimi C'den olduka farkldr. C++, C ile karlatrldnda
ok daha byk ve karmak bir dildir.
C++, C dilini de kapsayan bir st dil olarak dnlebilir. Eer baz noktalara dikkat
edilirse hem C dilinde hem de C++ dilinde geerli olabilecek programlar yazlabilir. C
dilinin byle kullanmna "clean C" ("Temiz C") denmektedir.
Bu karar verilirken, uygulamann gelitirilecei alan iin nasl bir C derleyicisinin olduu
phesiz nemlidir. Karar verilmesinde bir dier etken de yazlan kodun ne seviyede
tanabilir olmasnn istendiidir. Seeneklerin aadakiler olduu dnlebilir:
1. C99. C dilinin son srm. C dilinin tm zelliklerini iermektedir. Ancak baz
derleyiciler bu srm henz desteklememektedir.
2. C89. En fazla kullanlan srmdr. Bu srm kullanldnda genellikle C95 eklentileri
de kullanlmaktadr.
3. Geleneksel C. Yeni programlarn yazlmasnda artk kullanlmasa da, eskiden yazlan
programlarn bakmnda kullanmak gerekebilir.
4. Temiz C (Clean C). Yani C dilinin ayn zamanda C++ diline uyumlu olarak kullanlmas.
C99 srm, C89 ve Klasik C ile genel olarak yukarya doru uyumludur. Yani Klasik C
programlar ve C89 programlar, C99 dilinin kurallarna gre derlendiinde ya deiiklik
yapmak gerekmez ya da ok az deiiklik yapmak gerekir. Bu deiiklikler iin nilemci
program (preprocessor) kullanlabilir.
Gemie doru uyumlu program yazmaksa ou zaman mmkn deildir.
Objective C - Concurrent C
14/529
SAYI SSTEMLER
Gnlk hayatta onluk say sistemi kullanlr. Onluk say sisteminde bir saynn deeri
aslnda her bir basamak deerinin 10 saysnn sleriyle arpmlarndan elde edilen
toplam deeridir. rnein:
1273 = (3 * 1) + (7 * 10 ) + (2 * 100) + (1 * 1000)
Ancak bilgisayar sistemlerinde btn bilgiler ikilik say sisteminde (binary system) ifade
edilir.
Genel olarak say sistemi kalksa o say sisteminde o kadar simge bulunur.
rnein onluk say sisteminde 10 adet simge vardr:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Ayn ekilde ikilik say sisteminde yalnzca iki adet simge bulunur. Yani yalnzca 0 ve 1.
Birimler
kilik sistemde her bir basamaa (digit) 1 bit denir. Bit kelimesi binary digit
szcklerinden tretilmitir.
rnein 1011 says 4 bittir yani 4 bit uzunluundadr.
11011001 says 8 bittir.
8 bitlik bir byklk bir byte olarak isimlendirilir.
Kilo, byklk olarak 1000 kat anlamna gelir. Ancak bilgisayar alannda Kilo, 2'nin
1000'e en yakn ss olan 210 yani 1024 kat olarak kullanlr. Aada daha byk
birimlerin listesi veriliyor:
1
1
1
1
1
1
1
1
kilobyte
megabyte
gigabyte
terabyte
petabyte
exabyte
zettabyte
yottabyte
1
1
1
1
1
1
1
1
KB
MB
GB
TB
PB
EB
ZB
YB
1024
1024
1024
1024
1024
1024
1024
1024
byte
KB
MB
GB
TB
PB
EB
ZB
210
220
230
240
250
260
270
280
byte
byte
byte
byte
byte
byte
byte
byte
1
1
1
1
1
Nybble
byte
word
double word
quadro word
Tamsay deerlerini gstermek iin ikilik say sistemi iki farkl biimde kullanlabilir. Eer
yalnzca sfr ve pozitif tamsaylar gsteriliyorsa, bu biimde kullanlan ikilik say sistemine
iaretsiz (unsigned) ikilik say sistemi denir. kilik say sisteminin negatif tamsaylar da
gsterecek biimde kullanlmasna, iaretli (signed) ikilik say sistemi denir.
kilik say sisteminde ifade edilen bir saynn onluk say sistemindeki karln
hesaplamak iin en sadan balanarak btn basamaklar tek tek ikinin artan sleriyle
arplr. rnein:
15/529
1 0 1 1 = 1 * 20 + 1 * 21 + 0 * 22 + 1 * 23 = 11
0010 1001 = (1 * 1) + (1 * 8) + (1 * 32) = 41
MSD ve LSD
kilik say sisteminde yazlan bir saynn en solundaki bit, yukardaki rnekten de
grld gibi en yksek saysal deeri katar. Bu bite en yksek anlaml bit (most
significant digit) denir. Bu bit iin ounlukla "MSD" ksaltmas kullanlr.
kilik say sisteminde yazlan bir saynn en sandaki bit, yine yukardaki rnekten de
grld gibi en dk saysal deeri katar. Bu bite en dk anlaml bit (least
significant digit) denir. Bu bit iin ounlukla LSD ksaltmas kullanlr. rnek:
0101 1101 says iin
MSD = 0
LSD = 1
kilik say sisteminde yazlan bir saynn belirli bir bitinden sz edildiinde, hangi bitten
sz edildiinin doru bir ekilde anlalmas iin basamaklar numaralandrlr.
8 bitlik bir say iin, saynn en sandaki bit yani (LSD) saynn 0. bitidir. Saynn en
solundaki bit (yani MSD) saynn 7. bitidir.
Say srekli olarak ikiye blnr. Her blmden kalan deer (yani 1 ya da 0)
oluturulacak saynn sfrnc bitinden balanarak, basamaklarn oluturur. Bu ilem 0
deeri elde edilinceye kadar srdrlr.
rnek olarak 87 says ikilik say sisteminde ifade edilmek istensin:
87 / 2 = 43 (kalan 1) Saynn 0. biti 1
43 / 2 = 21 (kalan 1) Saynn 1. biti 1
21 / 2 = 10 (kalan 1) Saynn 2. biti 1
10 / 2 = 5 (kalan 0) Saynn 3. biti 0
5 / 2 = 2 (kalan 1) Saynn 4. biti 1
2 / 2 = 1 (kalan 0) Saynn 5. biti 0
1 / 2 = 0 (kalan 1) Saynn 6. biti 1
87 = 0101 0111
kinci bir yntem ise, onluk say sisteminde ifade edilen saydan srekli olarak ikinin en
byk ssn karmaktr. karlan her bir s iin ilgili basamaa 1 deeri yazlr. Bu
ilem 0 says elde edilene kadar srdrlr.
Yine 87 says ikilik say sisteminde ifade edilmek istensin:
87 - 64
23 - 16
7 - 4
3 - 2
1 - 1
=
=
=
=
=
23
7
3
1
0
0100
0101
0101
0101
0101
0000
0000
0100
0110
0111
87 = 0101 0111
Bire Tmleyen
Bire tmleyen (one's complement) saynn tm bitlerinin tersinin alnmasyla elde edilen
saydr. Yani bire tmleyen, saydaki 1 bitlerinin 0, 0 bitlerinin 1 yaplmasyla elde edilir.
Bir saynn bire tmleyeninin bire tmleyeni saynn yine kendisidir.
kiye Tmleyen
Bir saynn bire tmleyeninin 1 fazlas saynn ikiye tmleyenidir (two's complement).
16/529
Yani nce saynn bire tmleyeni yukardaki gibi bulunur. Daha sonra elde edilen sayya 1
eklenirse saynn ikiye tmleyeni bulunmu olur.
kiye tmleyeni bulmak iin daha ksa bir yol daha vardr:
Saynn en sandan balayarak ilk kez 1 biti grene kadar -ilk grlen 1 biti dahilsaynn ayns yazlr, daha sonraki tm basamaklar iin basaman tersi (Yani 1 iin 0, 0
iin 1) yazlr. rnein:
1110 0100 saysnn ikiye tmleyeni
0101 1000 saysnn ikiye tmleyeni
Negatif tamsaylarn da ifade edildii ikilik say sistemine "iaretli ikilik say sistemi"
(signed binary system) denir.
aretli ikilik say sisteminde negatif deerleri gstermek iin hemen hemen tm
bilgisayar sistemlerinde aadaki yntem uygulanr:
Saynn en soldaki biti iaret biti (sign bit) olarak kabul edilir. aret biti 1 ise say negatif,
0 ise say pozitif olarak deerlendirilir. kilik sistemde bir negatif say, ayn deerdeki
pozitif saynn ikiye tmleyenidir. rnek olarak, ikilik sistemde yazlan 27 says yine
ikilik sistemde yazlan 27 saysnn ikiye tmleyenidir.
Pozitif olan saylarn deeri, tpk iaretsiz say sisteminde olduu gibi elde edilir:
rnein, 0001 1110 iaretli ikilik say sisteminde pozitif bir saydr. Onluk say sisteminde
30 saysna eittir.
Negatif tamsaylarn deeri ancak bir dnmle elde edilebilir.
Saynn deerini hesaplamak iin ilk nce saynn ikiye tmleyeni bulunur. Bu saynn
hangi pozitif deer olduu bulunur. Elde edilmek istenen say, bulunan pozitif say ile ayn
deerdeki negatif saydr.
rnein, 1001 1101 saysnn onluk say sisteminde hangi sayya karlk geldiini
bulalm:
aret biti 1 olduuna gre say negatiftir.
1001 1101 saysnn ikiye tmleyeni 0110 0011, yani 99 deeridir.
O zaman 1001 1101 says -99'dur.
Onluk say sisteminde ifade edilen negatif saylarn iaretli ikilik say sisteminde yazlmas
iin aadaki yol izlenebilir:
nce saynn ayn deerli fakat pozitif olan ikilik sistemde ifade edilir. Daha sonra yazlan
saynn ikiye tmleyeni alnarak, yazmak istenilen say elde edilir.
rnein, ikilik sisteminde 17 deerini yazalm:
17 = 0001 0001
Bu saynn ikiye tmleyeni alnrsa
1110 1111
17/529
18/529
Onaltlk say sisteminde (hexadecimal system) saylar daha youn olarak kodlanp
kullanabilir.Onaltlk say sisteminde 16 simge bulunur.
lk 10 simge onluk sistemde kullanlanlarla ayndr:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9
Daha sonraki simgeler iin alfabenin ilk 6 harfi kullanlr:
A = 10, B = 11, C = 12, D = 13, E = 14, F = 15
Onaltlk say sisteminde yazlm bir sayy onluk sistemde ifade etmek iin, en sadan
balanarak basamak deerleri onaltnn artan sleriyle arplr:
01AF
= (15 * 1)
* 4096) = 431
Onluk say sisteminde yazlm bir sayy, onaltlk say sisteminde ifade etmek iin onluk
say sisteminden ikilik say sistemine yaplan dnme benzer yntem kullanlabilir. Say
srekli 16'ya blnerek, kalanlar soldan saa doru yazlr.
Uygulamalarda onaltlk say sisteminin getirdii nemli bir fayda vardr: Onaltlk say
sistemi ile ikilik say sistemi arasndaki dnmler kolay bir biimde yaplabilir:
Onaltlk say sistemindeki her bir basamak ikilik say sisteminde 4 bitlik (1 Nibble) alanda
ifade edilebilir:
0000
0001
0010
0011
0100
0101
0110
0111
0
1
2
3
4
5
6
7
1000
1001
1010
1011
1100
1101
1110
1111
8
9
A
B
C
D
E
F
rnek olarak, 2ADF saysn ikilik say sisteminde ifade edilmek istensin:
2
A
D
F
=
=
=
=
0010
1010
1101
1111
Bu durumda
2ADF = 0010 1010 1101 1111
kilik say sisteminden onaltlk say sistemine de benzer ekilde dnm yaplabilir:
nce bitler sadan balayarak drder drder ayrlr. En son drt bit eksik kalrsa sfr ile
tamamlanr. Sonra her bir drtlk grup iin dorudan onaltlk say sistemindeki karl
yazlr:
1010 1110 1011 0001 = AEB1
0010 1101 0011 1110 = 2D3E
Onaltlk say sisteminde yazlm iaretli bir saynn pozitif mi negatif mi olduunu
anlamak iin saynn en soldaki basamana bakmak yeterlidir. Bu basamak [8 - F]
aralnda ise say negatiftir.
19/529
Aada baz nemli iaretli tamsaylar, ikilik, onaltlk say sistemlerinde ifade ediliyor:
kilik say sistemi
0000 0000 0000 0000
0000 0000 0000 0001
0111 1111 1111 1111
1000 0000 0000 0000
1111 1111 1111 1111
Daha az kullanlan bir say sistemidir. Sekizlik say sisteminde 8 adet simge vardr:
0 1 2 3 4 5 6 7
Sekizlik say sisteminin her bir basama, ikilik say sisteminde 3 bit ile ifade edilir.
001
010
011
100
101
110
111
1
2
3
4
5
6
7
Sekizlik say sisteminin kullanlma nedeni de, ikilik say sistemine gre daha youn bir
ifade biimine olanak vermesi, ikilik say sistemiyle sekizlik say sistemi arasnda
dnmlerin ok kolay bir biimde yaplabilmesidir.
20/529
x= s * b * f k * b k
e
k =1
Gerek saylarn ifadesinde, gerek say kabul edilmeyen baz gsterimler sz konusudur.
Bunlar:
sonsuz (infinity)
NaN (not a number)
deerleridir. Bu deerler iaretli olabilir.
Sistemlerin ounda, gerek saylar IEEE (Institute of Electrical and Electronics
Engineers) 754 standardna gre tutulur. (IEEE Standard for Binary Floating-Point
Arithmetic - ISO/IEEE std 754-1985).Bu standarda gre gerek saylar iin iki ayr format
belirlenmitir:
1. Tek duyarlkl gerek say format (single precision format)
Bu formatta gerek saynn gsterimi genel formle aadaki biimde uyarlanabilir:
24
x= s * 2 * f k * 2k
e
k =1
21/529
rnek :
0 11111111 00001000000100000000000 (Say deil)
1 11111111 00010101010001001010101 (Say deil)
EEEEEEEE = 255 ve FFF...F = 0 ve S = 1 ise
EEEEEEEE = 255 ve FFF...F = 0 ve S = 0 ise
V = -sonsuz
V = +sonsuz
- 127)
- 127)
- 127)
- 127)
* 1.0
* 1.101
* 1.101
* 1.0
53
x= s * 2 * f k * 2 k
e
k =1
22/529
- 1023)
* (1.FFF...F)
nce saynn ondalk ksmnn bana 1 eklenir. Daha sonra bu say 2(EEEEEEEEEEE-123) ile
arplarak noktann yeri ayarlanr. Noktadan sonraki ksm, ikinin artan negatif sleriyle
arplarak elde edilir.
EEEEEEEEEEE = 0 ve FFF...F 0 ise V = (-1)S * 2-126 * (0. FFF...F)
EEEEEEEEEEE = 0 ve FFF...F = 0 ve S = 1 ise V = -0
EEEEEEEEEEE = 0 ve FFF...F = 0 ve S = 0 ise V = 0
23/529
1. Anahtar Szckler
2. simler
3. leler
4. Deimezler
25/529
sayac = son + 10
gibi bir ifadede 10 deimezi dorudan son isimli deiken ile ileme sokulur.
5. Dizgeler
ki trnak iindeki ifadelere "Dizge" (string / string litaerals) denir. Dizgeler programlama
dillerinin ounda tek bir atom olarak alnr, daha fazla paraya blnemezler.
"STRNGLER DE BRER ATOMDUR"
ifadesi bir dizgedir.
return 0;
0
)
<=
;
toplam
int
for
return
simlendirilenler:
main
toplam
printf
26/529
;
;
scanf
{
scanf
int
(
"%d"
++
leler:
=
<=
++
+=
Deimezler:
0
Dizgeler:
("ltfen bir say girin\n"
"%d"
"toplam = %d\n"
>
Nesne
Bellekte yer kaplayan ve ieriklerine eriilebilen alanlara nesne (object) denir. Bir ifadenin
nesne olabilmesi iin bellekte bir yer belirtmesi gerekir. Programlama dillerinde nesnelere
isimleri kullanarak eriilebilir.
a = b + k;
rneinde a, b ve k birer nesnedir. Bu ifadede a nesnesine, b ve k nesnelerine ait
deerlerin toplam atanr.
sonuc = 100;
sonuc isimli nesneye 100 deimez deeri atanr.
Nesnelerin baz zelliklerinden sz edilebilir:
Nesnelerin isimleri (name) nesneyi temsil eden yazlmsal varlklardr. Nesnelere isimleri
programc tarafndan verilir. Her dil iin nesne isimlendirmede baz kurallar sz
konusudur.
vergi = 20000;
Burada vergi bir nesne ismidir.
Nesne ile deiken terimleri birbirine tam olarak edeer deildir. Her deiken bir
nesnedir ama her nesne bir deiken deildir. Deiken demekle daha ok, programcnn
isimlendirdii nesneler kastedilir. Peki programcnn isimlendirmedii nesneler de var
mdr? Evet, gstericiler konusunda da grlecei gibi, deiken olmayan nesneler de
vardr. Nesne kavram deiken kavramn kapsar.
Nesnelerin deerleri (value) ilerinde tuttuklar bilgilerdir. Baka bir deyile nesneler iin
bellekte ayrlan yerlerdeki 1 ve 0'larn yorumlan biimi, ilgili nesnenin deeridir. Bu
deerler programlama dillerinin kurallarna gre, istenildikleri zaman programc
tarafndan deitirilebilirler. C dilinde baz nesnelerin deerleri ise bir kez verildikten
sonra bir daha deitirilemez.
Nesnenin tr (type) derleyiciye o nesnenin nasl yorumlanaca hakknda bilgi verir. Bir
nesnenin tr onun bellekteki uzunluu hakknda da bilgi verir. Her trn bellekte ne
kadar uzunlukta bir yer kaplad programlama dillerinde nceden belirtilmitir. Bir
nesnenin tr, ayrca o nesne zerinde hangi ilemlerin yaplabilecei bilgisini de verir.
Tr, nesnenin ayrlmaz bir zelliidir. Trsz bir nesne kavram sz konusu deildir.
Trler ikiye ayrlabilir:
27/529
fade
Nesne gsteren ifadelere "sol taraf deeri" (left value - L value) denir. Bir ifadenin sol
taraf deeri olabilmesi iin mutlaka bir nesne gstermesi gerekir. Bir ifadenin sol taraf
deeri olarak isimlendirilmesinin nedeni o ifadenin atama ilecinin sol tarafna
getirilebilmesidir.
rnein a ve b nesneleri tek bana sol taraf deerleridir. nk bu ifadeler atama
ilecinin sol tarafna getirilebilirler. rnein :
a = 17
ya da
b = c * 2
denilebilir. Ama a + b bir sol taraf deeri deildir. nk
a + b = 25
denilemez.
Deikenler, her zaman sol taraf deeridirler. Deimezler, sol taraf deeri olamazlar.
Sa Taraf Deeri:
Daha az kullanlan bir terimdir. Nesne gstermeyen ifadeler sa taraf deeri (right value)
olarak isimlendirilirler. Tipik olarak, atama ilecinin sol tarafnda bulunamayan yalnzca
28/529
Deimez fadeler
Yalnzca deimezlerden oluan bir ifadeye "deimez ifade" (constant expression) denir.
Bir deimez ifadede deikenler ya da ilev arlar yer alamaz:
10
3.5
10 + 20
ifadeleri deimez ifadelerdir.
Deimez ifadeler, derleme aamasnda derleyici tarafndan net saysal deerlere
dntrlebilir. C dilinin szdizim kurallar birok yerde deimez ifadelerin
kullanlmasn zorunlu klar.
Deyim
C dilinin cmlelerine deyim (statement) denir. C dilinde deyimler ";" ile sonlandrlr.
result = number1 * number2
bir ifadedir. Ancak
result = number1 * number2;
bir deyimdir. Bu deyim derleyicinin, number1 ve number2 deikenlerin deerlerinin
arplarak, elde edilen deerin result deikenine atanmasn salayacak ekilde kod
retmesine neden olur.
Baz deyimler yalnzca derleyiciye bilgi vermek amacyla yazlr, derleyicinin bir ilem
yapan kod retmesine yol amaz. Byle deyimlere bildirim deyimleri (declaration
statement) denir. Baz deyimler derleyicinin bir ilem yapan kod retmesini salar. Byle
deyimlere yrtlebilir deyimler (executable statement) denir.
29/529
BR C PROGRAMI OLUTURMAK
C dilinde yazlan bir programn altrlabilir hale getirilebilmesi iin, ounlukla aadaki
srelerden geilir:
1. Kaynak dosyann oluturulmas
Kaynak dosya, metin dzenleyici bir programda (text editr) yazlr. Kaynak dosya bir
metin dosyasdr. C dilinin kurallarna gre yazlan dosyalarn uzants, geleneksel olarak
".c" seilir.
2. Kaynak dosyann derleyici program (compiler) tarafndan derlenmesi:
Bir programlama dilinde yazlm program baka bir programlama diline eviren
programlara "evirici" (translator) denir. Dntrlmek istenen programn yazld dile
"kaynak dil" (source language), dnmn yapld dile ise "hedef dil" (target language)
denir. Hedef dil, makine dili ya da simgesel makine dili ise, byle evirici programlara
"derleyici" (compiler) denir.
Derleyici program kaynak dosyay alr, eviri ileminde eer baarl olursa bu kaynak
dosyadan bir "ama dosya" (object file) retir.
Derleyici programn derleme ilemini yapma srecine "derleme zaman " (compile time)
denir. Derleme ilemi baarszlk ile de sonulanabilir.
Bir derleyici program, kaynak metni makine diline evirme abasnda, kaynak metnin C
dilinin szdizim kurallarna uygunluunu da denetler.
Kaynak metinde dilin kurallarnn inendii durumlarda, derleyici program bu durumu
bildiren bir ileti (diagnostic message) vermek zorundadr. Derleyici programn verdii
ileti:
i) Bir "hata iletisi" (error message) olabilir. Bu durumda, derleyici programlar ounlukla
ama dosya retmeyi reddeder.
ii) Bir uyar iletisi olabilir (warning message). Bu durumda, derleyici programlar
ounlukla ama dosyay retir.
C standartlarna gre derleyici programlar, dilin kurallarnn inenmesi durumlarnn
dnda da, programcy mantksal hatalara kar korumak amacyla, istedikleri kadar
uyar iletisi retebilir.
Unix/Linux sistemlerinde oluturulan ama dosyalarn uzants ".o" dur. DOS ve Windows
sistemlerinde ama dosyalar ".obj" uzantsn alr.
Derleyici programlar, genellikle basit bir arayz ile iletim sisteminin komut satrndan
altrlacak biimde yazlr. Arayzn basit tutulmasnn nedeni baka bir program
tarafndan kolay kullanabilmesini salamak iindir. rnein, Microsoft firmasnn C/C++
derleyicisi aslnda "cl.exe" isimli bir programdr. UNIX sistemlerindeki GNU'nun gcc
derleyicisi ise aslnda gcc.exe isimli bir programdr.
Derleyici programlar daha kolay ynetmek iin, IDE (integrated development
environment) denilen gelitirme ortamlar kullanlabilir. IDE derleyici demek deil,
derleyiciyi altran ve program yazmay kolaylatran gelitirme ortamlardr. rnein
MinGW ve DevC++ derleyici deil, IDE programlardr. Bu programlar gcc derleyicisini
kullanmaktadr.
3. Daha nce elde edilmi ama dosyalar "balayc" (linker) program tarafndan
birletirilerek altrlabilir bir dosya elde edilir. UNIX sistemlerinde genellikle
altrlabilir dosyann uzants olmaz. Windows sistemlerinde altrlabilir dosyalarn
uzants ".exe" olarak seilir.
31/529
nilemci Program
C ve C++ dillerinde derleyici programdan daha nce kaynak kodu ele alan "nilemci"
(preprocessor) isimli bir program kullanlr. nilemci program ayr bir konu bal
altnda ele alnacak.
32/529
VER TRLER
Nesnelerin en nemli zelliklerinden biri, nesnenin trdr. Tr (type), nesnenin olmazsa
olmaz bir zelliidir. Tr olmayan bir nesneden sz etmek mmkn deildir. Derleyiciler
nesnelerle ve verilerle ilgili kod retirken, tr bilgisinden faydalanr. Derleyiciler nesnenin
tr bilgisinden, sz konusu veriyi bellekte ne ekilde tutacaklarn, verinin deerini elde
etmek iin veri alanndaki 1 ve 0 lar nasl yorumlayacaklarn, veriyi hangi ilemlere
sokabileceklerini renir.
Programlama dilleri asndan baktmz zaman trleri iki ayr gruba ayrabiliriz.
C dilinin toplam 4 ayr tamsay veri tr (integer types) vardr. Ancak her birinin kendi
iinde iaretli (signed) ve iaretsiz (unsigned) biimi olduundan toplam tamsay tr 8
kabul edilir.
aretli (signed) tamsay trlerinde pozitif ve negatif tam say deerleri tutulabilirken,
iaretsiz (unsigned) veri trlerinde negatif tamsay deerleri tutulamaz.
Aada C dilinin temel tamsay veri trleri tantlyor:
aretli karakter tr:
Bu veri trne ksaca signed char tr denir.
phesiz char szc ingilizce "character" szcnden ksaltlmtr, Trke "karakter"
anlamna gelir. Ancak bu trn ismi C'nin anahtar szckleri olan signed ve char
szckleri ile zdeleip, "signed char" olarak sylenir. aretli char trnden bir
nesnenin 1 byte'lk bir alanda tutulmas C standartlarnca gvence altna alnmtr.
1 byte'lk bir alan iaretli olarak kullanldnda bu alanda saklanabilecek deerler -128 /
127 deerleri arasnda olabilir.
aretsiz karakter tr:
aretsiz char trnn iaretli olandan fark 1 byte'lk alann iaretsiz olarak, yani
yalnzca 0 ve pozitif saylarn ifadesi iin kullanlmasdr. Bu durumda iaretsiz char
trnde 0 - 255 arasndaki tamsay deerleri tutulabilir.
Karakter tr:
33/529
aretli ya da iaretsiz olarak kullanlaca derleyicinin seimine bal olan bir trdr.
aretli ve iaretsiz ksa tamsay veri tr
short ve int szckleri C dilinin anahtar szcklerinden olduu iin bu tre genellikle short
int ya da ksaca short tr denir.
aretli veya iaretsiz short trnden bir nesne tanmland zaman, nesnenin bellekte
ka byte yer kaplayaca sistemden sisteme deiebilir. Sistemlerin ounda, short int
veri trnden yaratlan nesne bellekte 2 byte'lk bir yer kaplar. aretli short int trnden
bir nesne -32768 - +32767 aralndaki tamsay deerlerini tutabilirken, iaretsiz short
trnde tutulabilecek deerler 0 - 65535 aralnda olabilir.
aretli ve iaretsiz tamsay tr
Bu tre ksaca int tr denir.
aretli ve iaretsiz int trnden bir nesne tanmland zaman, nesnenin bellekte ka
byte yer kaplayaca sistemden sisteme deiebilir. ounlukla 16 bitlik sistemlerde, int
trnden veri 2 byte, 32 bitlik sistemlerde ise int trnden veri tr 4 byte yer kaplar.
16 bitlik sistem demekle ilemcinin yazma (register) uzunluunun 16 bit olduu anlatlr.
int veri trnn 2 byte uzunluunda olduu sistemlerde bu veri trnn say snrlar,
iaretli int tr iin -32768 - +32767, iaretsiz int veri tr iin 0 - +65535 arasnda olur.
int veri trnn 4 byte uzunluunda olduu sistemlerde bu veri trnn say snrlar,
iaretli int tr iin -2147483648 - +2147483647, iaretsiz int veri tr iin 0 +4.294.967.295 arasnda olur.
aretli ve iaretsiz uzun tamsay veri tr
long ve int szckleri C dilinin anahtar szcklerinden olduu iin bu tre genellikle long
int ya da ksaca long tr denir.
aretli veya iaretsiz long trnden bir nesne tanmland zaman, nesnenin bellekte ka
byte yer kaplayaca sistemden sisteme deiebilir. Sistemlerin ounda, long int veri
trnden yaratlan nesne bellekte 4 byte'lk bir yer kaplar. aretli long int trnden bir
nesne -2147483648- +2147483648 aralndaki tamsay deerlerini tutabilirken, iaretsiz
long trnde tutulabilecek deerler 0 +4.294.967.295 aralnda olabilir.
C dilinde gerek say deerlerini tutabilmek iin 3 ayr veri tr tanmlanmtr. Bunlar
srasyla, float, double ve long double veri trleridir. Gerek say veri trlerinin hepsi
iaretlidir. Yani gerek say veri trleri iinde hem pozitif hem de negatif deerler
tutulabilir. Gerek saylarn bellekte tutulmas sistemden sisteme deiebilen zellikler
ierebilir. Ancak sistemlerin ounda IEEE 754 sayl standarda uyulur.
Sistemlerin hemen hepsinde float veri trnden bir nesne tanmland zaman bellekte 4
byte yer kaplar. 4 byte lk yani 32 bitlik alana zel bir kodlama yaplarak gerek say
deeri tutulur. IEEE 754 sayl standartta 4 byte lk gerek say format "single precision"
(tek duyarlk) olarak isimlendirilirken, 8 byte lk gerek say format "double precision"
(ift duyarlk) olarak isimlendirilmitir.
Sistemlerin hemen hepsinde double veri trnden bir nesne tanmland zaman bellekte
8 byte yer kaplar.
long double veri trnn uzunluu sistemden sisteme deiiklik gsterir. Bu tr,
sistemlerin ounda 8, 10, 12 byte uzunluundadr.
34/529
Say sistemleri blmmzden, gerek saylarn n byte'lk bir alanda zel bir biimde
kodlandn hatrlayacaksnz. rnein IEEE 754 sayl standartta 4 ya da 8 byte lk alan
ayr paraya blnm, bu paralara zel anlamlar yklenmitir. rnein gerek
saynn 4 byte lk bir alanda tutulmas durumunda 1 bit, saynn iaretini tutmak iin
kullanlrken, 23 bit, saynn ondalk (fraction) ksmn tutar. Geriye kalan 8 bit ise,
noktann en saa alnmas iin gerekli bir arpm faktrn dolayl olarak tutar. Bu
durumda gerek saynn 8 byte'lk bir alanda kodlanmas durumunda, hem tutulabilecek
saynn bykl artarken hem de noktadan sonraki duyarlk da artar.
Aada C dilinin doal veri trlerine ilikin bilgiler bir tablo eklinde veriliyor:
35/529
UZUNLUK
(byte)
SINIR DEERLER
(DOS /UNIX)
signed char
unsigned char
char
signed short int
unsigned short int
signed int
unsigned int
signed long int
unsigned long int
_Bool (C 99)
signed long long int (C 99)
unsigned long long int(C 99)
1
1
1
2
2
2
4
2
4
4
4
1
8
8
-128 - 127
0 - 255
Derleyiciye bal
-32.768 / 32.767
0 / 65.535
-32.768 / 32.767
-2.147.483.648 - 2.147.483.647
0 / 65.535
0 / 4.294.967.295
-2.147.483.648 - 2.147.483.647
0 / 4.294.967.295
false / true
-9.223.372.036.854.775.808 / 9.223.372.036.854.775.807
0 / 18.446.744.073.709.551.615
UZUNLUK
(byte)
float
double
long double
8/10/12
float _Complex (C 99)
double _Complex (C 99)
long double _Complex (C 99)
float _Imaginary (C 99)
double _Imaginary (C 99)
long double _Imaginary (C 99)
SINIR DEERLER
en kk pozitif deer
en byk pozitif deer
1.17 x 10-38
3.40 x 1038
(6 basamak duyarlk)
(6 basamak duyarlk)
2.22 x 10-308
1.17 x 10-308
(15 basamak duyarlk)
(15 basamak duyarlk)
tanabilir deil
Yukarda verilen tablo, sistemlerin ou iin geerli de olsa ANSI C standartlarna gre
yalnzca aadaki zellikler gvence altna alnmtr:
char tr 1 byte uzunluunda olmak zorundadr.
short veri trnn uzunluu int trnn uzunluuna eit ya da int tr uzunluundan
kk olmaldr.
long veri trnn uzunluu int trne eit ya da int trnden byk olmak zorundadr.
Yani
short <= int <= long
Derleyiciler genel olarak derlemeyi yapacaklar sistemin zelliklerine gre int trnn
uzunluunu ilemcinin bir kelimesi kadar alrlar. 16 bitlik bir ilemci iin yazlan tipik bir
uygulamada
char tr 1 byte
36/529
37/529
BLDRM ve TANIMLAMA
Bildirim Nedir?
Bir kaynak dosya iinde yazlan geerli C deyimlerinin bir ksm, bir ilem yaplmasna
ynelik deil, derleyiciye derleme zamannda kullanaca bir bilgi vermeye yneliktir.
Byle deyimlere bildirim (declaration) deyimi denir. Derleyici geerli bir bildirim
deyiminin stnden getiinde bir bilgi alr ve ald bu bilgiyi yine derleme zamannda
kullanr.
Tanmlama Nedir?
Ancak baz bildirim deyimleri vardr ki, derleyici bu deyimlerden ald bilgi sonucunda,
bellekte bir yer ayrr. Tanmlama (definition), derleyicinin bellekte yer ayrmasn
salayan bildirim deyimleridir.
Deikenlerin Tanmlanmas
b = x + 2;
Yukardaki ifade tek bir ifade gibi yorumlanr ve derleyici buna geerli bir anlam veremez.
39/529
Tr belirten anahtar szckler, C dilinin nceden tanmlanm veri trlerine ilikin anahtar
szcklerdir. Bu szcklerin kullanlmasyla, tanmlanacak deikenlerin, 11 temel veri
trnden hangisine ait olduu bildirilir. C dilinin nceden tanmlanm veri trlerine
ilikin, bildirim ileminde kullanlabilecek anahtar szckler unlardr:
signed, unsigned, char, short, int, long, float, double
Bu szcklerin hepsi anahtar szck olduundan kk harf ile yazlmaldr, C dilinin
byk harf kk harf duyarl (case sensitive) bir dil olduunu hatrlayalm. C dilinin tm
anahtar szckleri kk harf ile tanmlanmtr.
Tr belirten anahtar szckler aadaki izelgede listelenen seeneklerden biri olmaldr.
Keli ayra iindeki ifadeler kullanlmas zorunlu olmayan, yani seime bal olan
anahtar szckleri gsteriyor. Ayn satrdaki tr belirten anahtar szckler tamamen ayn
anlamda kullanlabilir:
1
iaretli char tr
2
3
iaretsiz char tr
iaretli ksa tamsay tr
iaretli tamsay tr
iaretsiz tamsay tr
9
10
11
float tr
double tr
long double tr
[signed] char
char
unsigned char
[signed] short [int]
[signed] short
short [int]
short
unsigned short [int]
unsigned short
[signed] int
int
signed
unsigned int
unsigned
[signed] long [int]
[signed] long
long [int]
long
unsigned long [int]
unsigned long
float
double
long double
Yukardaki tablodan da grld gibi, belirli trleri birden fazla ekilde ifade etmek
mmkndr.
char a;
signed char a;
int a;
signed int a;
signed a;
long a;
long int a;
signed long a;
signed long int a;
Yukarda ayn kolon zerindeki bildirimlerin hepsi, derleyici tarafndan birbirine edeer
olarak ele alnr.
Bildirim szdiziminde, deiken ismi olarak, C dilinin isimlendirme kurallarna uygun
olarak seilen herhangi bir isim kullanlabilir.
C dilinde isimlendirilen (identifiers) varlklar balca 6 grubu ierir. Deikenler
(variables) bunlardan yalnzca biridir. levler (functions), etiketler (labels), makrolar
(macros), yap ve birlik isimleri (structure and union tags), enum deimezleri (enum
constants) isimlerini programclardan alr.
40/529
simler boluk ieremeyecei iin uygulamalarda genellikle boluk hissi vermek iin alttire
(underscore) karakteri kullanlr.
genel_katsayi_farki, square_total, number_of_cards
gibi.
simlendirmede baka bir seenek de her szcn ilk harfini byk, dier harfleri kk
yazmaktr:
GenelKatsayiFarki, SquareTotal, NumberOfCards
C dili standartlar isim uzunluu konusunda bir snrlama koymamtr. Ancak ismin ilk 31
karakterinin derleyici tarafndan dikkate alnmasn zorunlu klar. Ancak derleyicilerin
ou, ok daha uzun deiken isimlerini ileme sokabilirler. 31 karakterden daha uzun
isimler kullanldnda programc iin ok az da olsa yle bir risk sz konusudur:
Herhangi bir derleyici ilk 31 karakteri ayn olan iki farkl ismi ayn isim olarak ele alabilir.
C, byk harf kk harf duyarll olan bir dil olduu iin, isimlendirmelerde de byk
harf ile kk harfler farkl karakterler olarak ele alnr:
var, Var, VAr, VAR, vAR, vaR
deikelerinin hepsi ayr deikenler olarak ele alnr.
Bildirim rnekleri
int x;
unsigned long int var;
double MFCS;
unsigned _result;
signed short total;
Tr belirten anahtar szcklerin yazlmasndan sonra ayn tre ilikin birden fazla
nesnenin bildirimi, isimleri arasna virgl atomu koyularak yaplabilir. Bildirim deyimi yine
noktal virgl atomu ile sonlandrlmaldr.
unsigned char ch1, ch2, ch3, ch4;
float FL1, Fl2;
unsigned total, subtotal;
int _vergi_katsayisi, vergi_matrahi;
Farkl trlere ilikin bildirimler virgllerle birbirinden ayrlamaz.
long x, int y;
/* Geersiz! */
42/529
signed int x, y;
tamamen ayn anlamdadr. Yine
unsigned u;
ile
unsigned int u;
Tamamen ayn anlamdadr.
Bildirim deyiminde, tr belirten anahtar szck birden fazla ise bunlarn yazm sras
nemli deildir. Ama okunabilirlik asndan nce iaret belirten anahtar szcn sonra
tr belirten anahtar szcn kullanlmas gelenek haline gelmitir. rnein :
signed long int
signed int long
long signed int
long int signed
int long signed
int signed long
x;
x;
x;
x;
x;
x;
}
Yukardaki rnekte var1, var2, ch1, ch2, ch3 deikenlerinin tanmlanma yerleri
dorudur. Ancak f deikeninin bildirimi geersizdir. nk bildirim deyiminden nce
bildirim deyimi olmayan baka bir deyim yer alyor. Bu durum geersizdir.
Ayn program paras u ekilde yazlm olsayd bir hata sz konusu olmazd :
43/529
}
Bu durumda artk f deikeni de kendi blounun banda tanmlanm olur.
C dilinde tek bana bir sonlandrc atom bir deyim oluturur. Ve byle bir deyime, bo
deyim (null statement) denir. C szdizimine gre oluan bu deyim yrtlebilir bir
deyimdir. Dolaysyla aadaki kod parasnda y deikeninin tanmlamas derleme
geersizdir.
{
}
int x;;
int y;
/* Geersiz! */
Yukardaki kod parasnda x deikeninin bildiriminden sonra yer alan sonlandrc atom
yrtlebilir bir deyim olarak ele alnr. Bu durumda y deikeninin bildirimi geersizdir.
Ayn ekilde ii bo bir blok da C dilinde bir yrtlebilir deyim olarak ele alnr. Bu yazm,
sonlandrc atomun tek bana kullanlmasna tamamen edeerdir. Aadaki kod
paras da geersizdir:
{
int x;
{ }
int y;
/* Geersiz! */
Bir ya da birden fazla deyimin bir blok iine alnmasyla elde edilen yap C dilinde bileik
deyim (compound statement) ismini alr. Bileik deyimler de yrtlebilir deyimlerdir.
Aadaki kod paras da geersizdir:
{
}
{int x;}
int y;
/* Geersiz */
[C++ dilinde blok iinde bildirimi yaplan deikenlerin blok balarnda bildirilmeleri zorunlu deildir. Yani
C++'da deikenler bloklarn iinde herhangi bir yerde bildirilebilir.]
Bir deiken tanmlandnda bu deikene bir ilkdeer verilebilir (initialize). Bu zel bir
szdizim ile yaplr:
int a = 20;
lkdeer verme deyimi bir atama deyimi deildir, bir bildirim deyimidir. Bu deyim ile,
programn alma zamannda bir deikenin nceden belirlenen bir deer ile hayata
balamas salanr. Yukardaki bildirimi gren derleyici a deikenini 20 deeri ile
balatacak bir kod retir. lkdeer verme deyiminde atama ilecinin sa tarafnda
kullanlan ifadeye "ilkdeer verici" (initializer) denir.
Bir bildirim deyimi ile birden fazla deikene de ilkdeer verilebilir:
int a = 10, b = 20, c = 30;
44/529
45/529
DEMEZLER
Veriler ya nesnelerin iinde ya da dorudan deimez (sabit - constant) biiminde
bulunur. Deimezler nesne olmayan, programc tarafndan dorudan saysal byklk
olarak girilen verilerdir. Deimezlerin saysal deerleri derleme zamannda tam olarak
bilinir. rnein:
x = y + z;
ifadesi bize y ve z iindeki deerlerin toplanacan ve sonucun x deikenine
aktarlacan anlatr. Oysa
d = x + 10;
ifadesinde x deikeni iinde saklanan deer ile 10 says toplanmtr. Burada 10 says
herhangi bir deikenin iindeki deer deildir, dorudan say biiminde yazlmtr.
Deimezlerin Trleri
Nesnelerin trleri olduu gibi deimezlerin de trleri vardr. Nesnelerin trleri bildirim
yaplrken belirlenir. Deimezlerin trlerini ise derleyici, belirli kurallara uyarak
deimezlerin yazl biimlerinden saptar. Deimezlerin trleri nemlidir, nk C
dilinde deimezler, deikenler ve ileler bir araya getirilerek ifadeler (expressions)
oluturulur. C'de ifadelerin de tr vardr. fadelerin trleri, ierdikleri deimez ve
deikenlerin trlerinden elde edilir.
Tamsay Deimezleri
47/529
biimindedir.
Burada b karakterleri sekizlik say sistemindeki basamaklar (0 1 2 3 4 5 6 7) gsteriyor.
tr
signed int
signed long
unsigned long
signed int
unsigned int
signed long
unsigned long
unsigned int
unsigned long
signed long
unsigned long
unsigned long
Yukardaki tablo yle yorumlanmaldr: Belirli bir yazm iin verilen trlerden, yukardan
aa doru olmak zere, tama olmakszn ilgili deeri tutabilecek ilk tr, deimezin
trdr. Aadaki rnekleri inceleyin:
int trnn ve long trnn 2 byte olduu sistemler iin (DOS) rnekler
Deimez
456
59654
125800
3168912700
0X1C8
0XE906
0X1EB68
0XBCE1C53C
0710
0164406
0365550
027470342474
987U
45769U
1245800U
3589456800U
0X3DBU
48/529
0XB2C9U
0X130268U
0XD5F2C3A0U
01733U
0131311U
04601150U
032574541640U
25600L
3298780970L
0X6400L
0Xc49F672AL
062000L
030447663452L
890765UL
0XD978DUL
03313615UL
unsigned int
unsigned long
unsigned long
unsigned int
unsigned int
unsigned long
unsigned long
signed long
unsigned long
signed long
unsigned long
signed long
unsigned long
unsigned long
unsigned long
unsigned long
unsigned int
unsigned int
unsigned int
unsigned int
unsigned int
unsigned int
unsigned int
signed long
unsigned long
signed long
unsigned long
signed long
unsigned long
unsigned long
unsigned long
unsigned long
Karakter Deimezleri
Karakter deimezleri tipik olarak char trden nesnelere atanan deimezlerdir. Karakter
deimezleri C'de farkl biimlerde bulunabilir.
i. Bir karakterin grnts tek trnak (single quote) iinde yazlrsa derleyici tarafndan
dorudan karakter deimezi olarak ele alnr. rnek :
'a'
'J'
''
':'
'8'
'<'
Yukardaki atomlarn her biri birer karakter deimezidir.
C'de tek trnak iinde yazlan char trden deimezler, aslnda o karakterin (kullanlan
sistemin karakter kodundaki (rnein ASCII)) kod numarasn gsteren bir tamsaydr.
char ch;
ch = 'a';
/***/
ASCII karakter kodlamasnn kullanldn dnelim. Bu rnekte aslnda ch isimli char
trden bir deikene 'a' karakterinin ASCII tablosundaki kod numaras olan 97 deeri
aktarlr. Tek trnak iindeki karakter deimezlerini grnce aslnda onlarn kk birer
tamsay olduu bilinmelidir. Yukardaki rnekte istenirse ch deikenine aadaki gibi bir
atama yaplabilir:
ch = 'a' + 3;
Bu durumda ch deikenine saysal olarak 100 deeri atanr. Bu tamsay da ASCII
tablosundaki 'd' karakterinin kod numarasdr.
Karakter deimezlerinin dier yazmlarnda tek trnak iinde ters bl '\' karakteri ve
bunu izleyen baka karakter(ler) kullanlr. ngilizce de bu biimlere "escape sequence"
denir.
49/529
Tek trnak iindeki ters bl karakterinden sonra yer alan baz karakterler ok kullanlan
baz karakterlerin yerlerini tutar. Bunlarn listesi aada veriliyor:
Tanm
sonlandrc karakter (null character)
an sesi (alert)
geri boluk (back space)
tab karakteri (tab)
aa satr (new line)
dey tab (vertical tab)
sayfa ileri (form feed)
satr ba (carriage return)
ift trnak (double quote)
tek trnak (single quote)
soru iareti (question mark)
ters bl (back slash)
ASCII No
0
7
8
9
10
11
12
13
34
39
63
92
Tek trnak iinde ters bl ve 'x' karakterlerinden sonra onaltlk (hexadecimal) say
sisteminde bir say yazlrsa bu sistemin kulland karakter setinde, o saysal deerin
gsterdii kod numaral karakter deimezidir.
'\x41' /* 41H kod numaral karakterdir. */
'\xff' /* FFH kod numaral karakterdir. */
'\x1C' /* 1C kod numaral karakterdir. */
Aada harf isimli char trden deikene 41H deeri atanyor:
char harf;
harf = '\x41';
Bu da onluk say sisteminde 65 deeridir. ASCII karakter setinin kullanldn varsayalm.
65 kod nolu ASCII karakteri 'A' karakteridir. Dolaysyla harf isimli deikene 'A' atanm
olur.
Tek trnak iinde ters bl karakterinden sonra sekizlik say sisteminde bir deer
yazlrsa, bu kullanlan karakter setindeki o saysal deerin gsterdii kod numaral
karaktere iaret eden bir karakter deimezidir. Tek trnak iindeki ters bl karakterini
izleyen sekizlik sistemde yazlm say basamaktan uzun olmamaldr. Sekizlik
sistemde yazlan saynn banda sfr rakam olma zorunluluu yoktur.
'\012' /* 10 numaral ASCII karakteri, Tam say deeri 10 */
'\16'
/* 14 numaral ASCII karakteri. Tam say deeri 14 */
'\123' /* 83 numaral ASCII karakteri. Tam say deeri 83 */
Program iinde kullanmna bir rnek:
50/529
char a, b;
a = '\xbc' ;
b = '\012';
rnein, 7 numaral ASCII karakteri olan an sesi karakteri, deimez olarak ayr
biimde de yazlabilir:
'\x7'
'\07'
'\a'
Burada tercih edilecek biim son biim olmaldr. Hem tanabilir bir biimdir hem de
okunabilirlii daha iyidir. Baka karakter setlerinde an sesi karakteri 7 sra numaral
karakter olmayabilir, ama nceden belirlenmi ters bl karakter deimezi eklinde ifade
edersek hangi sistem olursa olsun an sesi karakterini verir. Ayrca kodu okuyan an sesi
karakterinin 7 numaral ASCII karakteri olduunu bilmeyebilir, ama '\a' nn an sesi
karakteri olduunu bilir.
Karakter deimezleri konusunu kapatmadan nce karakter setleri konusunda da biraz
bilgi verelim:
Gnmzde en ok kullanlan karakter seti ASCII karakter setidir. ASCII (American
Standard Code for Information Interchange) szcklerinin ba harflerinden oluan bir
ksaltmadr. ASCII karakter kodunda karakterler 7 bitlik bir alanda kodlanmtr. Baz
bilgisayarlar ise 8 bit alana geniletilmi kodlama kullanrlar ki bu sette 128 yerine 256
karakter temsil edilebilir.
Farkl bilgisayarlar farkl karakter kodlamas kullanabilir. rnek olarak IBM mainframe leri
daha eski olan EBCDIC kodlamasn kullanr. Unicode ismi verilen daha gelitirilmi bir
karakter kodlamas vardr ki karakterler 2-4 byte'lk alanda temsil edildikleri iin bu
kodlamada dnyada var olan tm karakterlerin yer almas hedeflenmitir. Gelecekte
birok makinenin bu karakter kodlamasn destekleyecek biimde tasarlanaca
dnlyor.
Karakter deimezleri tamsay deimezleridir. Ancak C'de daha ok bir yaz bilgisi ile
ilgili kullanrlar. Yazlar karakter deimezleri ile deerlerini alabilecekleri gibi, bir yaznn
deitirilmesi amacyla karakter deimezleri kullanlabilir.
C'de karakter deimezleri int trden olarak ele alnr ve ileme sokulur. Bu konu "tr
dnmleri" blmnde ele alnacak.
Gerek say deimezleri (floating constants) deerleri gerek say olan deimezlerdir.
C dilinde bir gerek say deimezi float, double ya da long double trden olabilir. C89
standartlarna gre bir gerek say deimezi yalnzca onluk say sistemi kullanlarak
yazlabilir.
51/529
Gerek say deimezleri stel biimde (scientific notation) yazlabilir. Bunun iin
deimezin sonuna 'e' ya da 'E' eki getirilir. Bu, saynn on zeri bir arpanla
arpldn gsterir. 'E' ya da 'e' karakterlerini '+', '-' ya da dorudan bir rakam
karakteri izleyebilir.
2.3e+04f
1.74e-6F
8.e+9f
burada e, 10 'un ss anlamna gelir:
1.34E-2f ile 0.0134
-1.2E+2F ile 120.f
ayn deimezlerdir.
52/529
LEVLER
lev Nedir?
C'de alt programlara ilev (function) denir. ngilizcedeki "function" szc bu balamda
matematiksel anlamyla deil dier programlama dillerinde ya da ortamlarnda kullanlan,
"alt program", "prosedr", szcklerinin karl olarak kullanlr.
Bir ilev, bamsz olarak altrlabilen bir program parasdr.
Bir program alt programlara yani ilevlere blerek yazmak baz faydalar salar:
1. Programn kaynak kodu klr. Bylece oluturulmas hedeflenen alabilir dosya da
(rnein .exe uzantl dosya) klr.
2. Kaynak dosyann okunabilirlii artar. Okunabilirliin artmas, kodu yazann ve okuyann
iini kolaylatrr. Bylece proje gelitirme sresinin azalmas ynnde kazanm salanm
olur.
3. Belirli kod paralarnn programn farkl yerlerinde yinelenmesi, programda yaplacak
olas bir deiikliin maliyetini artrr. Programn farkl yerlerinde, kodun kullanld yere
bal olarak deiiklikler yapmak gerekir. Kaynak dosyalarda byle deiiklikler yapmak
hem zaman alcdr hem de risklidir. nk bir deiikliin yaplmasnn unutulmas
durumunda ya da deiiklik yaplmamas gereken bir yerde kodun deitirilmesi
durumunda program yanl alabilir. Oysa ortak kod paralar ilevler eklinde
paketlendiinde, yalnzca ilevlerde deiiklik yaplmasyla, istenen deiiklik
gerekletirilmi olur.
4. Programda hata arama daha kolay gerekletirilir. Projelerdeki hata arama maliyeti
azalr.
5. Yazlan ilevler baka projelerde de kullanlabilir. Alt programlar tekrar kullanlabilir
(reusable) bir birim olutururlar. Bylelikle de projelerdeki kodlama giderlerini azaltrlar.
levler C'nin temel yap talardr. altrlabilen bir C program en az bir C ilevinden
oluur. Bir C programnn oluturulmasnda ilev saysnda bir kstlama yoktur.
levlerin onlar aran ilevlerden aldklar girdileri ve yine onlar aran ilevlere
gnderdikleri ktlar vardr. levlerin girdilerine aktel parametreler (actual parameters)
ya da argmanlar (arguments) denir. levlerin ktlarna ise geri dn deeri (return
value) diyoruz.
Bir ilev balca iki farkl amala kullanlabilir:
1. lev, almas sresince belli ilemleri yaparak belirli amalar gerekletirir.
2. lev, almas sonunda retecei bir deeri kendisini aran ileve gnderebilir.
53/529
Bir ilevin yrtlmesi sonunda onu aran ileve gnderdii deere, ilevin geri dn
deeri (return value) denir. Her ilev bir geri dn deeri retmek zorunda deildir. Bir
ilev yapaca bir ile ilgili olarak bir geri dn deeri retir ya da retmez.
levlerin geri dn deerleri farkl amalar iin kullanlabilir:
1. Baz ilevler zaten tek bir deeri elde etmek, tek bir deeri hesaplamak amacyla
tanmlanrlar. Elde ettikleri deeri de kendilerini aran ilevlere geri dn deeri olarak
iletirler. Bir kpn hacim deerini bulan bir ilev tanmladmz dnelim. Byle bir
ilev, hacmini bulaca kpn kenar uzunluunu arld yerden alr, bu deeri
kullanarak hacim deerini hesap eder, hesap ettii deeri darya geri dn deeri
olarak iletebilir.
2. Her ilevin amac bir deer hesaplamak deildir. Baz ilevler ise arlmalaryla
kendilerine sorulan bir soruya yant verirler. rnein bir saynn asal olup olmadn
snayan bir ilev tanmlandn dnelim. lev arld yerden, asalln snayaca
deeri alr. Tanmnda bulunan baz kodlar ile saynn asal olup olmadn snar. Saynn
asal ya da asal olmamasna gre darya iki farkl deerden birini geri dn deeri
olarak gnderebilir. Bu durumda ilevin geri dn deeri, hesap edilen bir deer deil,
sorunun yant olarak yorumlanacak bir deerdir.
3. Baz ilevler ise ne bir deeri hesaplamak ne de bir soruya yant vermek iin
tanmlanrlar. Tanmlanma nedenleri yalnzca bir i yapmaktr. Ancak ilevin yapmas
istenen iin, baaryla yaplabilmesi konusunda bir garanti yoktur. rnein bir dosyay
amak iin bir ilev tanmlandn dnelim. lev arld yerden alacak dosyann
ismi bilgisini alyor olabilir. Ancak dosyann alabilmesi eitli nedenlerden dolay
gvence altnda deildir. arlan ilev istenen dosyay ya aar ya aamaz. lev geri
dn deeriyle yapt iin baars hakknda bilgi verir. Bu durumda ilevin geri dn
deeri, hesap edilen bir deer deil, yaplmas istenen iin baars konusunda verilen bir
bilgi olarak yorumlanr.
4. Baz ilevler hem belli bir amac gerekletirirler hem de buna ek olarak amalarn
tamamlayan bir geri dn deeri retirler. Bir yaz iinde bulunan belirli bir karakteri
silecek bir ilev tasarlandn dnelim. levin varlk nedeni yaznn iinden istenen
karakterleri silmektir. arld yerden, silme yapaca yazy ve silinecek karakterin ne
olduu bilgisini alr ve iini yapar. Ancak iini bitirdikten sonra yazdan ka karakter silmi
olduunu geri dn deeri ile arld yere bildirilebilir.
5. Baz ilevlerin ise hi geri dn deerleri olmaz.
i) levin amac yalnzca bir ii gerekletirmektir, yapt iin baars gvence altndadr.
rnein yalnzca ekran silme amacyla tasarlanm olan bir ilevin geri dn deerine
sahip olmas gereksizdir. Sistemlerin ounda kt ekrannn silinmesi konusunda bir
baarszlk riski yoktur.
ii) lev darya bir deer iletir ancak deeri iletme iini geri dn deeri ile deil de
baka bir arac kullanarak gerekletirir.
levlerin geri dn deerlerinin de trleri sz konusudur. levlerin geri dn deerleri
herhangi bir trden olabilir. Geri dn deerlerinin trleri, ilevlerin tanmlanmas
srasnda belirtilir.
levlerin Tanmlanmas
levlerin kodunun yazlmas iin tanmlama (definition) terimi kullanlr. C'de ilev
tanmlama ileminin genel biimi yledir:
[Geri dn deerinin tr] <ilev ismi> ([parametreler])
{
/***/
}
54/529
Tanmlanan bir ilevin bir geri dn deeri retmesi zorunlu deildir. lev tanmnda bu
durum geri dn deerinin trnn yazld yere void anahtar szcnn yazlmasyla
anlatlr:
void func()
{
}
Yukarda tanmlanan func ilevi geri dn deeri retmiyor. Geri dn deeri
retmeyen ilevlere void ilevler denir.
lev tanmnda geri dn deerinin tr bilgisi yazlmayabilir. Bu durum, ilevin geri
dn deeri retmedii anlamna gelmez. Eer geri dn deeri tr bilgisi yazlmaz
ise, C derleyicileri tanmlanan ilevin int trden bir geri dn deerine sahip olduunu
varsayar. rnein:
func()
{
}
Yukarda tanmlanan func ilevinin geri dn deerinin tr int trdr. Yani ilevin
yukardaki tanmyla
int func()
{
}
tanm arasnda derleyici asndan bir fark yoktur. Geri dn deerinin trnn
yazlmamas gemie doru uyumluluk iin korunan bir kuraldr. int trne geri dnen bir
ilevin tanmnda int szcnn yazlmas tavsiye edilir.
[C++ dilinde ilev tanmnda geri dn deerinin trnn yazlmas zorunludur.]
55/529
C dilinde bir ilev tanm iinde bir baka ilev tanmlanamaz. Yani isel ilev
tanmlamalarna izin verilmez. rnein aadaki gibi bir tanmlama geersizdir, nk
func ilevi tanmlanmakta olan foo ilevinin iinde tanmlanyor:
double foo()
{
/***/
int func() /* Geersiz */
{
/***/
}
/***/
}
Tanmlamann aadaki biimde yaplmas gerekirdi:
double foo()
{
/***/
}
int func()
{
/***/
}
C dilinde ilevlerin geri dn deerleri return deyimi (return statement) ile oluturulur.
return deyiminin iki ayr biimi vardr:
return;
Ya da return anahtar szcn bir ifade izler:
return x * y;
return anahtar szcnn yanndaki ifadenin deeri, geri dn deeri olarak, ilevi
aran kod parasna iletilir.
return ifadesinin deiken iermesi bir zorunluluk deildir. Bir ilev bir deimez deerle
de geri dnebilir.
return 1;
return deyiminin bir baka ilevi de iinde bulunduu ilevi sonlandrmasdr. Bir ilevin
kodunun yrtlmesi srasnda return deyimi grldnde ilevin almas sona erer.
int func()
{
/**/
return x * y;
}
Yukardaki rnekteki func ilevinde return anahtar szcnn yannda yer alan x * y
ifadesi ile oluturulan return deyimi, func ilevini sonlandryor, func ilevinin bir geri
dn deeri retmesini salyor.
56/529
Baz programclar return ifadesini bir ayra iinde yazarlar. Bu ayra return deyimine ek
bir anlam katmaz. Yani
return x * y;
gibi bir deyim
return (x * y);
biiminde de yazlabilir. Okunabilirlik asndan zellikle uzun return ifadelerinde ayra
kullanm salk verilir.
return (a * b - c * d);
Bir ilevin tanmnda ilevin geri dn deeri tr yazlmsa, bu ilevin tanm iinde
return deyimiyle bir geri dn deeri retilmelidir. Bu mantksal bir gerekliliktir. Ancak
return deyimiyle bir geri dn deeri retilmemesi derleme zaman hatasna neden
olmaz. Bu durumda ilevin almas ilevin ana blounun sonuna gelindiinde sona erer ,
ilev arld yere bir p deeri iletir. Bu da istenmeyen bir durumdur. C
derleyicilerinin ou, geri dn deeri retecei yolunda bilgi verilen bir ilevin return
deyimiyle bir deer retmemesini mantksal bir uyar iletisiyle iaretler.
"Warning: Function func should return a value"
Geri dn deeri retmeyen ilevlerde, yani void ilevlerde, return anahtar szc
yannda bir ifade olmakszn tek bana da kullanlabilir:
return;
Bu durumda return deyimi iinde yer ald ilevi, geri dn deeri oluturmadan
sonlandrr.
C dilinde ilevler yalnzca tek bir geri dn deeri retebilir. Bu da ilevlerin kendilerini
aran ilevlere ancak bir tane deeri geri gnderebilmeleri anlamna gelir. Ancak,
ilevlerin birden fazla deeri ya da bilgiyi kendilerini aran ilevlere iletmeleri
gerekiyorsa, C dilinde bunu salayacak baka aralar vardr. Bu aralar daha sonraki
blmlerde ayrntl olarak greceksiniz.
main levi
main de dier ilevler gibi bir ilevdir, ayn tanmlama kurallarna uyar. C programlarnn
almas, ismi main olan ilevden balar. C programlar zel bir ilem yaplmamsa,
main ilevinin almasnn bitiiyle sonlanr. main ilevine sahip olmayan bir kaynak
dosyann derlenmesinde bir sorun kmaz. Ancak balama (linking) aamasnda balayc
main ilevinin bulunmadn grnce balama ilemini gerekletiremez. Balayc
programlar bu durumda bir hata iletisi verir.
int main()
{
return 0;
}
Biiminde tanmlanm bir main ilevi de int trden bir deer dndrmelidir. main
ilevinin rettii geri dn deeri, programn almas bittikten sonra iletim sistemine
iletilir.
Geleneksel olarak, main ilevinin 0 deerine geri dnmesi programn sorunsuz bir ekilde
sonlandrld anlamna gelir. main ilevinin 0 dnda bir deere geri dnmesi ise, kodu
okuyan tarafndan programn baarszlkla sona erdirildii biiminde yorumlanr. Yani baz
nedenlerle yaplmak istenenler yaplamam, bu nedenle main ilevi sonlandrlmtr.
57/529
main ilevi geri dn deeri reteceini bildirmi olmasna karn return deyimiyle belirli
bir deeri geri dndrmezse, main ilevinden de bir p deeri gnderilir.
Derleyicilerin ou, main ilevinin geri dn deeri retmemesi durumunda da bir
mantksal uyar iletisi retir.
Dier taraftan main ilevi de void bir ilev olarak tanmlanabilir:
void main()
{
}
Ancak bir szdizimi hatas olmamasna karn main ilevinin void bir ilev olarak
tanmlanmas doru kabul edilmez.
levlerin arlmas
C dilinde bir ilev ars (function call - function invocation), ismi ilev ar ileci olan
bir ile ile yaplr. lev ar ileci olarak () atomlar kullanlr. arlacak ilevin ismi bu
ileten nce yazlr.
func();
Yukarda deyim ile func isimli ilev arlr.
Bir ilev arld zaman programn ak, ilevin kodunun yrtlmesi iin bellekte
ilevin kodunun bulunduu blgeye atlar. levin kodunun altrlmas ilemi bittikten
sonra da ak yine aran ilevin kalnan yerinden srer.
Bir ilevin geri dn deeri varsa, ilev ar ifadesi, ilevin geri dn deerini retir.
Geri dn deeri reten bir ileve yaplan ar ifadesi sz konusu ilevin rettii geri
dn deerine edeerdir.
levin geri dn deeri bir deikene atanabilecei gibi dorudan aritmetik ilemlerde
de kullanlabilir. rnein:
sonuc = hesapla();
Burada hesapla ilevine yaplan ar ifadesiyle retilen geri dn deeri, sonuc isimli
deikene atanr. Bir baka deyile bir ilev ar ifadesinin rettii deer, ilgili ilevin
rettii (eer retiyorsa) geri dn deeridir. Yukardaki rnekte nce hesapla() ilevi
arlr, daha sonra ilevin kodunun altrlmasyla elde edilen geri dn deeri sonuc
deikenine atanr.
lev ar ifadeleri nesne gstermez yani sol taraf deeri (L value) deildir. Yani C
dilinde aadaki gibi bir atama deyimi geersizdir:
func() = 5;
/* Geersiz */
58/529
aran ilev ile arlan ilevin her ikisi de ayn ama kod (object code) iinde bulunmak
zorunda deildir. aran ilev ile arlan ilev farkl ama kodlar iinde bulunabilir.
nk derleme ilemi srasnda bir ilevin arldn gren derleyici, ama kod iine
arlan ilevin adn ve ar biimini yazar. aran ilev ile arlan ilev arasnda
balant kurma ilemi, balama aamasnda balayc program (linker) tarafndan yaplr.
Bu nedenle tanmlanan bir ilev iinde, tanml olmayan bir ilev arlsa bile derleme
aamasnda bir hata olumaz. Hata balama aamasnda oluur. nk balayc arlan
ilevi bulamaz.
levler arldklar yerlerden alacaklar bilgileri, ilev ar ifadeleri ile alrlar. Bir ilevin
formal parametreleri (formal parameters) ya da parametre deikenleri, ilevlerin
kendilerini aran ilevlerden aldklar girdileri tutan deikenleridir. Bir ilevin parametre
says ve bu parametrelerin trleri gibi bilgiler, ilevlerin tanmlanmas srasnda
derleyiciye bildirilir. lev ars ile gnderilen argman ifadelerin deerleri, ilevin ilgili
parametre deikenlerine kopyalanr.
rnein bir kpn hacmini hesaplayan ilev, arld yerden bir kpn kenar
uzunluunu alacana gre, bu deerin kopyalanmas iin, bir parametre deikenine
sahip olmas gerekir. Benzer ekilde iki saydan daha byk olann bulan bir ilevin iki
tane parametre deikenine sahip olmas gerekir.
C dilinde ilevlerin tanmlanmasnda kullanlan iki temel biim vardr. Bu biimler
birbirlerinden ilev parametrelerinin derleyicilere tantlma ekli ile ayrlrlar. Bu
biimlerden birincisi eski biim (old style) ikincisi ise yeni biim (new style) olarak
adlandrlr.
Eski biim hemen hemen hi kullanlmaz, ama C standartlarna gre halen geerlidir. Bu
biimin korunmasnn nedeni gemie doru uyumluluun salanmasdr. Kullanlmas
gereken kesinlikle yeni biimdir. Ancak eski kodlarn ya da eski kaynak kitaplarn
incelenmesi durumunda bunlarn anlalabilmesi iin eski biimin de renilmesi gerekir.
Eski Biim
Eski biimde (old style), ilevin parametre deikenlerinin yalnzca isimleri, ilev
parametre ayralar iinde yazlr. Eer parametre deikenleri birden fazla ise aralarna
virgl atomu koyulur. Daha sonra bu deikenlerin bildirimi yaplr. Bu bildirimler daha
nce rendiimiz, C dilinin bildirim kurallarna uygun olarak yaplr. rnek:
double alan(x, y)
double x, y;
{
return x * y;
}
Yukarda tanmlanan alan ilevinin iki parametre deikeni vardr ve bu parametre
deikenlerinin isimleri x ve y'dir. Her iki parametre deikeni de double trdendir.
levin geri dn deeri double trdendir.
int func (a, b, c)
int a;
double b;
long c;
{
/***/
}
Bu rnekte ise func ilevi parametre deikenine sahiptir. Parametre deikenlerinin
isimleri a, b ve c'dir. smi a olan parametre deikeni int trden, b olan double trden ve
ismi c olan ise long trdendir.
59/529
Eski biim, yeni biime gre uzundur. nk ilev ayralarnn iinde ismi yer alan
parametre deikenleri alt satrlarda yeniden bildirilir.
Yeni Biim
Yeni biimde (new style), eski biime gre hem daha ksadr hem de okunabilmesi eski
biime gre ok daha kolaydr.
Yeni biimde ilev parametre deikenlerinin bildirimi ilev ayralarnn iinde yalnzca bir
kez yaplr. Bu biimde, ilevin ayralarnn iine parametre deikenin tr ve yanna da
ismi yazlr. Eer birden fazla ilev parametre deikeni varsa bunlar virgllerle ayrlr,
ancak her bir deikenin tr bilgisi yeniden yazlr. rnek :
int func(int x, int y)
{
/***/
}
double foo(double a, int b)
{
/***/
}
lev parametre deikenleri ayn trden olsa bile her defasnda tr bilgisinin yeniden
yazlmas zorunludur. rnein:
int foo (double x, y)
{
/***/
}
/* Geersiz */
Her ilev parametre deikenine sahip olmak zorunda deildir. Baz ilevler istenen bir ii
yapabilmek iin dardan bilgi almaz. Parametre deikenine sahip olmayan bir ilevin
tanmnda, ilev parametre ayracnn ii bo braklr. lev parametre ayracnn iine void
anahtar szcnn yazlmas durumunda da ilevin parametre deikenine sahip
olmad sonucu kar.
int foo()
{
/***/
}
ile
int foo(void)
{
/***/
}
60/529
return 0;
Yukardaki rnekte main ilevi iinde, func isimli ilev arlyor ve arlan ileve x isimli
deikenin deeri argman olarak geiliyor. lev ars, programn alma zamannda,
programn aknn func ilevinin kodunun bulunduu yere sramasna neden olur. func
ilevindeki a isimli parametre deikeni iin bellekte bir yer ayrlr ve a parametre
deikenine argman olan ifadenin deeri, yani x deikeninin deeri atanr. Yani
int a = x;
ileminin derleyicinin rettii kod sonucu otomatik olarak yapld sylenebilir.
int main()
{
int x = 100, y = 200, z;
z = add(x, y);
/***/
return 0;
}
int add(int a, int b)
{
return a + b;
}
Yukarda tanmlanan add ilevi arldnda programn ak bu ileve gemeden nce, x
ve y deikenlerinin iinde bulunan deerler, add ilevinin parametre deikenleri olan a
ve b'ye kopyalanr.
1. Geri dn deeri retmeyen bir ileve yaplan ar, genellikle kendi bana bir deyim
oluturur. Byle bir ileve yaplan ar, bir ifadenin paras olarak kullanlmaz. lev
arsn genellikle sonlandrc atom izler;
61/529
void func()
{
/***/
}
int main()
{
func();
return 0;
}
2. Geri dn deeri reten ilevlerin rettii deerlerin kullanlmas zorunlu deildir.
Ancak baz ilevlerin geri dn deerlerinin kullanlmamas mantksal bir yanllk
olabilir. ki saynn toplam deerine geri dnen bir ilev tanmlandn dnelim. Byle
bir ilev arld yerden iki deer alarak bunlarn toplam deerine geri dnyor olsun.
Byle bir ilevin varlk nedeni bir deer hesaplamaktr. levin hesaplad deer
kullanlmyorsa ilev bo yere arlm, ilevin kodu bo yere alm olur.
Ancak baz ilevler bir i yaptklar gibi yaptklar ile ilgili tamamlayc bir bilgiyi de geri
dndrr. Byle bir ilev yalnzca yapt i iin arlabilir. Yani ilevi aran kod paras
ilevin geri dndrd deer ile ilgilenmeyebilir.
rnein foo() ilevi int trden bir deeri geri dn deeri reten ilev olsun.
a = foo();
Yukardaki ifadede foo ilevinin geri dn deeri a isimli deikene atanr. Bu ilev bir
kez arlmasna karn artk geri dn deeri a deikeninde tutulduu iin, bu geri
dn deerine ilev yeniden arlmadan istenildii zaman ulalabilir. Ancak:
foo();
eklinde bir ilev ar ifadesinde geri dn deeri bir deikende saklanmaz. Bu
duruma geri dn deerinin kullanlmamas (discarded return value) denir.
3. Sk karlalan durumlardan biri de, bir ilev arsyla elde edilen geri dn deerinin
bir baka ilev arsnda argman olarak kullanlmasdr. Aadaki rnei inceleyin:
int add(int a, int b)
{
return a + b;
}
int square(int a)
{
return a * a;
}
int main()
{
int x = 10;
int y = 25;
int z = square(add(x, y));
}
return 0;
Yukarda tanmlanan add ilevi, iki tamsaynn toplam deeri ile geri dnerken, square
ilevi ise dardan ald deerin karesi ile geri dnyor. main ilevi iinde yaplan
62/529
square(add(x, y));
ars ile add ilevinin geri dn deeri square ilevine argman olarak geiliyor.
4. Bir ilevin rettii geri dn deeri bir baka ilevin return deyiminde return ifadesi
olarak kullanlabilir. Bir baka deyile, bir ilev geri dn deerini bir baka ilevi
ararak oluturabilir. ki saynn karelerinin toplamna geri dnen sum_square isimli bir
ilev tanmlanmak istensin:
int sum_square(int a, int b)
{
return add(square(a), square(b));
}
Tanmlanan sum_square ilevi daha nce tanmlanm add ilevine yaplan arnn
rettii geri dn deeri ile geri dnyor. add ilevine gnderilen argmanlarn da,
square ilevine yaplan arlardan elde edildiini gryorsunuz.
Bir ilev kendisini de arabilir. Kendisini aran bir ileve zyinelemeli ilev (recursive
function) denir. Bir ilev kendini neden arr? Byle ilevlerle hedeflenen nedir? Bu konu
ileride ayr bir balk altnda ele alnacak.
63/529
Standart C levleri
Baz ilevlerin bulunmasnn dilin standartlar tarafndan gvence altna alnmas ile
aadaki faydalar salanm olur.
i) Baz ilemler iin ortak bir arayz salanm olur.
Mutlak deer hesaplayan bir ilevi yazmak ok kolaydr. Ancak standart bir C ilevi olan
abs ilevinin kullanlmasyla ortak bir arayz salanr. Her kaynak kod kendi mutlak deer
hesaplayan ilevini tanmlam olsayd, tanmlanan ilevlerin isimleri, parametrik yaplar
farkl olabilirdi. Bu durum da kod okuma ve yazma sresini uzatrd.
ii) Baz ileri gerekletirecek ilevlerin kodlar sistemden sisteme farkllk gsterebilir. Bu
da kaynak dosyann tanabilirliini azaltr. Bu ilemleri yapan standart ilevlerin
tanmlanm olmas kaynak kodun baka sistemlere tanabilirlii artrr.
iii) Baz ilevlerin yazlmas belirli bir alanda bilgi sahibi olmay gerektirebilir. rnein bir
gerek saynn bir baka gerek say ssn hesaplayan bir ilevi verimli bir ekilde
yazabilmek iin yeterli matematik bilgisine sahip olmak gerekir.
iv) Sk yaplan ilemlerin standart olarak tanmlanm olmas, programcnn yazaca kod
miktarn azaltr. Bylece proje gelitirme sresi de ksalr.
v) Derleyicilerin salad standart ilevler ok sayda programc tarafndan kullanlm
olduu iin ok iyi derecede test edilmilerdir. Bu ilevlerin tanmlarnda bir hata olma
olasl, programcnn kendi yazaca ilevlerle kyaslandnda ok dktr.
yi bir C programcsnn C dilinin standart ilevlerini ok iyi tanmas ve bu ilevleri yetkin
bir ekilde kullanabilmesi gerekir.
64/529
printf levi
printf standart bir C ilevidir. printf ilevi ile ekrana bir yaz yazdrlabilecei gibi, bir
ifadenin deeri de yazdrlabilir.
Deikenlerin iindeki deerler aslnda bellekte ikilik sistemde tutulur. Bir deikenin
deerinin ekrana, hangi say sisteminde ve nasl yazdrlaca programcnn isteine
baldr. Deikenlerin deerlerinin ekrana yazdrlmasnda standart printf ilevi kullanlr.
printf aslnda ok ayrntl zelliklere sahip bir ilevdir. imdilik iinize yarayacak kadar
ayrnty reneceksiniz. printf ilevlerle ilgili yukarda aklanan genel kurallara uymaz.
printf ilevi deiken sayda parametreye sahip bir ilevdir. Bir ilevin ka tane parametre
deikeni varsa o ilev arldnda, ileve o kadar argman geilmelidir, deil mi? Oysa
printf ilevine istenen sayda argman geilebilir. Bu ileve ka tane argman geilirse
ilevin o kadar sayda parametre deikenine sahip olaca dnlebilir. Bu nasl oluyor?
Deiken sayda parametreye sahip ilevler ileri bir konu olduundan, bu konu ancak
sonraki blmlerde ele alnacak.
printf ilevine ilk gnderilen argman genellikle ift trnak iinde yer alan bir yazdr. ift
trnak iinde yer alan byle yazlara dizge (string) denir. Dizgeler konusu ileride ayr bir
blmde ele alnacak.
printf ilevine argman olarak geilen dizge iinde yer alan tm karakterler ekrana
yazlr. Ancak printf ilevi dizge iindeki % karakterini ve bunu izleyen belirli sayda
karakteri ekrana yazmaz. lev, dizge iindeki % karakterlerini yanndaki belirli sayda
karakter ile birlikte formatlama karakterleri (conversion specifiers) olarak yorumlar.
Formatlama karakterleri, ift trnaktan sonra yazlan argmanlarla bire bir eletirilir.
Formatlama karakterleri nceden belirlenmitir, kendileriyle elenen bir ifadenin deerinin
ekrana ne ekilde yazdrlaca bilgisini ileve aktarrlar. Bu format bilgisi
*
*
*
*
*
*
65/529
yazs yazdrlr.
Aada formatlama karakterlerinden bazlarnn anlam veriliyor. printf ilevi ileride
ayrntl olarak ele alnacak.
Format
karakteri
%d
%ld
%x
%X
%lx
%u
%o
%f
%lf
%e
%c
%s
Anlam
Bir ifadeyi int trden yorumlayarak, elde ettii deeri onluk say sisteminde
yazar.
Bir ifadeyi long trden yorumlayarak, elde ettii deeri onluk say sisteminde
yazar.
Bir ifadeyi unsigned int trden yorumlayarak, elde ettii deeri onaltlk say
sisteminde yazar. Basamak sembolleri olarak a, b, c, d, e, f (kk)
harflerini kullanr.
Bir ifadeyi unsigned int trden yorumlayarak, elde ettii deeri onaltlk say
sisteminde yazar. Basamak simgeleri olarak A, B, C, D, E, F (byk)
harflerini kullanr.
Bir ifadeyi unsigned long trnden yorumlayarak, onaltlk say sisteminde
yazar.
Bir ifadeyi unsigned int trnden yorumlayarak, onluk say sisteminde yazar.
Bir ifadeyi unsigned int trnden yorumlayarak, sekizlik say sisteminde
yazar
float ve double trlerinden ifadelerin deerlerini onluk say sisteminde
yazar.
double ve long double trlerinden ifadelerin deerlerini onluk say sisteminde
yazar.
Gerek saylar stel biimde yazar.
char veya int trnden bir ifadeyi bir karakterin sra numaras olarak
yorumlayarak, ilgili karakterin grnts ekrana yazdrr.
Verilen adresteki yazy ekrana yazdrr.
Yukardaki tabloda grld gibi double tr hem %f format karakteri hem de %lf
format karakteri ile yazdrlabilir. Ama %lf okunabilirlii artrd iin daha ok tercih
edilir.
Yukardaki tabloya gre unsigned int trnden u isimli deikenin deeri aadaki
ekillerde yazdrabilir:
#include <stdio.h>
int main()
{
unsigned int u = 57054;
printf("u = %u\n", u); /* u deerini onluk sistemde yazar */
printf("u = %o\n", u); /* u deerini sekizlik sistemde yazar */
printf("u = %X\n", u); /* u deerini onaltlk sistemde yazar */
return 0;
}
long trden bir ifadenin deerini yazdrrken d, o, u ya da x karakterlerinden nce l
karakteri kullanlr:
66/529
#include <stdio.h>
int main()
{
long int lo = 23467;
unsigned long int unlo = 65242;
printf("unlo
printf("unlo
printf("unlo
printf("unlo
}
=
=
=
=
%ld\n",
%lu\n",
%lo\n",
%lX\n",
lo);
unlo);
unlo);
unlo);
/*
/*
/*
/*
return 0;
Yukardaki rneklerde unsigned int trden bir ifadenin deerinin printf ileviyle sekizlik ya
da onaltlk say sisteminde yazdrlabileceini grdnz. Peki iaretli trden bir
tamsaynn deeri sekizlik ya da onaltlk sistemde yazdrlamaz m? Yazdrlrsa ne olur?
Sz konusu iaretli tamsay pozitif olduu srece bir sorun olmaz. Saynn iaret biti 0
olduu iin saynn nicel bykln etkilemez. Yani doru deer ekrana yazlr, ama
say negatifse iaret biti 1 demektir. Bu durumda ekrana yazlacak saynn iaret biti de
nicel bykln bir paras olarak deerlendirilerek yazlr. Yani yazlan deer doru
olmaz.
% karakterinin yannda nceden belirlenmi bir format karakteri yoksa , % karakterinin
yanndaki karakter ekrana yazlr.
Yzde karakterinin kendisini ekrana yazdrmak iin format karakteri olarak %% kullanlr:
printf("%%25\n");
scanf levi
scanf ilevi, klavyeden her trl bilginin giriine olanak tanyan standart bir C ilevidir.
scanf ilevi de printf ilevi gibi aslnda ok ayrntl, geni kullanm zellikleri olan bir
ilevdir. Ancak bu noktada scanf ilevi yzeysel olarak ele alnacak.
scanf ilevinin de birinci parametresi bir dizgedir. Ancak bu dizge yalnzca klavyeden
alnacak bilgilere ilikin format karakterlerini ierir. printf ilevinde olduu gibi scanf
ilevinde de bu format karakterleri nceden belirlenmitir. % karakterinin yannda yer
alrlar. scanf ilevinin kulland format karakterlerinin printf ilevinde kullanlanlar ile
hemen hemen ayn olduu sylenebilir. Yalnzca gerek saylara ilikin format
karakterlerinde nemli bir farkllk vardr. printf ilevi %f format ile hem float hem de
double trden verileri ekrana yazabilirken scanf ilevi %f format karakterini yalnzca
float trden veriler iin kullanr. double tr iin scanf ilevinin kulland format
karakterleri %lf eklindedir. scanf ilevinin format ksmnda format karakterlerinden
baka bir ey olmamaldr. printf ilevi ift trnak iindeki format karakterleri dndaki
karakterleri ekrana yazyordu, ancak scanf ilevi format karakterleri dnda dizge iine
yazlan karakterleri ekrana basmaz, bu karakterler tamamen baka anlama gelir. Bu
nedenle ilevin nasl altn renmeden bu blgeye format karakterlerinden baka bir
ey koymayn. Buraya konulacak bir boluk bile farkl anlama gelir.
67/529
#include <stdio.h>
int main()
{
int x, y;
printf("iki say girin : ");
scanf("%d%d", &x, &y);
printf("%d + %d = %d\n", x, y, x + y);
return 0;
}
Yukardaki rnekte, program kullanan kiiye deer girmesinin beklendiini syleyen bir
yaz, printf ileviyle ekrana yazdrlyor. Bu i scanf ilevi ile yaplmazd. scanf ilevi ile
ekrana bir yaz yazdrmak mmkn deildir. scanf yalnzca giri amacyla tanmlanm bir
ilevdir, k ilemi yapmaz.
scanf("%d%d", &x, &y);
ars ile programn alma zamannda klavyeden girilecek deerler x ve y
deikenlerine aktarlr. x ve y deikenleri iin onluk say sisteminde klavyeden giri
yaplr. Giri arasna istenildii kadar boluk karakteri konulabilir. Yani ilk sayy girdikten
sonra SPACE, TAB ya da ENTER tuuna bastktan sonra ikinci deer girilebilir. rnein:
5 60
biiminde bir giri, geerli olaca gibi;
5
60
biiminde bir giri de geerlidir. scanf ilevine gnderilecek dier argmanlar & adres
ileci ile kullanlr. & bir gsterici ilecidir. Bu ileci gstericiler konusunda reneceksiniz.
Sistemlerin hemen hemen hepsinde klavyeden karakter alan ayr C ilevi bulunur. Bu
ilevlerin biri tam olarak standarttr ama dier ikisi sistemlerin hemen hemen hepsinde
bulunmasna karn standart C ilevi deildir. imdi bu ilevleri inceleyelim:
getchar levi
68/529
#include <stdio.h>
int main()
{
char ch;
ch = getchar();
printf("\nKarakter olarak ch = %c\nASCII numaras ch = %d\n", ch, ch);
}
return 0;
getchar derleyicilerin ounda stdio.h balk dosyas iinde bir makro olarak tanmlanr.
Makrolar ile ileride tanacaksnz.
getch levi
getche levi
69/529
C dilinde ekrana bir karakterin grntsn basmak iin baz standart C ilevleri
kullanlabilir:
putchar levi
return 0;
putch levi
70/529
Yorum Satrlar
Kaynak dosya iinde yer alan nilemci ya da derleyici programa verilmeyen aklama
amal yazlara yorum satrlar (comment lines) denir.
Yorum satrlar /* atomuyla balar */ atomuyla sonlanr. Bu iki atom ile, bu iki atom
arasnda kalan tm karakterler, nilemci programn kaynak kodu ele almasndan nce
tek bir boluk karakteriyle yer deitirir. Yorum satrlar herhangi sayda karakter
ierebilir. rnek:
/* Bu bir aklama satrdr */
Yorum satrlar birden fazla satra ilikin olabilir:
/*
bu satirlar
kaynak koda dahil
deildir.
*/
Bir dizge ya da bir karakter deimezi iinde yorum satr bulunamaz:
#include <stdio.h>
int main()
{
printf("/* bu bir yorum satiri degildir */");
return 0;
}
Yukardaki programn derlenip altrlmasyla, ekrana
/* bu bir yorum satr deil */
yazs yazdrlr.
Bir yorum satrnn kapatlmasnn unutulmas tipik bir hatadr.
#include <stdio.h>
int main()
{
int x = 1;
int y = 2;
x = 10;
y = 2;
71/529
e Yorum Satrlar
/*
*/
Yukardaki rnekte birinci */ atomundan sonraki kod paras kaynak dosyaya dahildir.
Ancak derleyicilerin ou uygun ayarlarn seilmesiyle i ie yorum satrlarna izin verir.
ie yorum satrlarna gereksinim, zellikle bir yorum satrnn kopyalanarak baka bir
yorum satr iine yaptrlmas durumunda oluur. Bazen de, yorum satr iine alnmak
istenen kod parasnn iinde de bir baka yorum satr olduundan, isel yorum satrlar
oluur.
Yorum satrlar ou zaman kaynak kodun okunabilirliini artrmak iin kullanlr. Kaynak
koddan dorudan karlamayan bilgiler aklama satrlaryla okuyucuya iletilebilir.
Bazen de yorum satrlar bir kaynak dosyann blm balklarn oluturmak amacyla
kullanlr.
Kaynak kodun aka verdii bir bilgiyi, yorum satryla aklamak programn
okunabilirliini bozar.
72/529
LELER
le Nedir
*/
*/
Terim Nedir
lelerin ileme soktuklar nesne veya deimezlere terim (operand) denir. C'de ileler
aldklar terim saysna gre gruba ayrlabilir.
leler, yaptklar ilemin sonucunda bir deer retir. lelerin rettii deer, ayn ifade
iinde var olan bir baka ilece terim olabilir. fade iinde en son deerlendirilen ilecin
rettii deer ise ifadenin deeri olur. Bir ifadenin deeri, ifade iinde yer alan ilelerin
rettii deerlere gre saptanr.
lelerin en nemli zellii, yaptklar ilemin sonucu olarak bir deer retmeleridir.
Programc, bir ifade iinde ilelerin rettii deeri kullanr ya da kullanmaz. lelerin
rettii deer aadaki biimlerde kullanlabilir:
i. lecin rettii deer bir baka deikene aktarlabilir:
x = y + z;
73/529
return 0;
lelerin ncelii
74/529
C dilinde ilelerin ana ilevleri, bir deer retmeleridir. Ancak baz ileler, terimi olan
nesnelerin deerlerini deitirir. Yani bu nesnelerin bellekteki yerlerine yeni bir deer
yazlmasna neden olurlar. Bir ilecin, terimi olan nesnenin deerini deitirmesine ilecin
yan etkisi (side effect) denir. Yan etki, bellekte yaplan deer deiiklii olarak tanmlanr.
rnein atama ilecinin, ++ ve -- ilelerinin yan etkisi vardr. Bu ileler, terimleri olan
nesnelerin bellekteki deerlerini deitirebilir.
75/529
Bu ileler, mantksal ilemler yapar. Mantksal ve, mantksal veya, mantksal deil
ileleri bu gruba girer.
Gsterici ileleri (pointer operators)
Bu ileler, adresler ile ilgili baz ilemlerin yaplmasn salar. Adres ileci, ierik ileci ile
keli ayra ileci bu gruba girer.
Bitsel ilem yapan ileler (bitwise operators)
Bu ileler, bitsel dzeyde baz ilemlerin yaplmasn salar. Bitsel deil ileci, bitsel
kaydrma ileleri, bitsel ve, veya, zel veya ileleri bu gruba giren ilelerdir.
Atama ileleri (assignment operators)
Bir nesneye atama yaplmasn salayan ilelerdir. Atama ileci ve ilemli atama ileleri
bu gruba girer.
zel amal ileler (special purpose operators)
Bunlar farkl ilerin yaplmasn salayan ve farkl amalara hizmet eden ilelerdir. Koul
ileci, sizeof ileci, tr dntrme ileci bu gruba giren ilelerdir.
lk grup, programlama dillerinin hepsinde vardr. Bitsel ilem yapan ileler ve
gsterici ileleri yksek seviyeli programla dillerinde genellikle bulunmaz. Programlama
dillerinin ou, kendi uygulama alanlarnda kolaylk salayacak birtakm zel amal
ilelere de sahip olabilir.
Aritmetik leler
Bu ileler, tek terimli, nek konumundaki (unary prefix) ilelerdir. aret ileci eksi (-),
teriminin deerinin ters iaretlisini retir. Yani derleyici, iaret eksi ilecinin kullanlmas
durumunda terim olan deeri -1 ile arpacak ekilde kod retir. Bu ilecin terimi herhangi
bir trden nesne gsteren ya da gstermeyen ifade olabilir. le ncelik tablosunun ikinci
seviyesinde bulunurlar. ncelik yn sadan soladr. lecin bir yan etkisi yoktur, yani
terimi olan nesnenin bellekteki deerini deitirmez.
"aret eksi" ilecinin rettii, bir nesne deildir, bir sa taraf deeridir. Aadaki ifade
matematiksel olarak doru olmasna karn C dilinde doru deildir, derleme zamannda
hata oluumuna neden olur:
int x;
-x = 5;
76/529
x bir nesne olmasna kar x ifadesi bir nesne deil, x nesnesinin deerinin ters iaretlisi
olan deerdir.
-x ve 0 - (x) edeer ifadelerdir.
-x ifadesi bir sol taraf deeri deildir, bu ifadeye bir atama yaplamaz.
aret ileci art (+), yalnzca matematiksel benzerlii salamak asndan C diline
eklenmi bir iletir. Derleyici tarafndan, tek terimli, nek konumunda bir ile olarak ele
alnr. Terimi olan ifade zerinde herhangi bir etkisi olmaz. Teriminin deeriyle ayn
deeri retir. +x ifadesi ile 0 + (x) ifadeleri edeerdir.
#include <stdio.h>
int main()
{
int x = -5;
x = -x - x;
printf("x = %d\n", x);
return 0;
}
x = -x x;
Yukardaki ifadede 3 ile vardr. Soldan saa bu ileleri sayalm: Atama ileci '=',
iaret ileci eksi '-', karma ileci '-'. fadenin deerinin hesaplanmasnda ile
nceliklerine gre hareket edilir. nce ikinci seviyede bulunan eksi iaret ileci deer '5'
deerini retir. retilen 5 deeri karma ilecinin terimi olur. Yaplan kartma
ileminden retilen deer 10'dur. Bu deer de atama ilecinin terimi olur, bylece x
deikenine 10 deeri atanr.
Kalan (%)leci
ki terimli, araek konumunda bir iletir. Terimlerinin her ikisi de tamsay trlerinden
(char, short, int, long) olmak zorundadr. Herhangi bir teriminin gerek say trnden
olmas geersizdir. lecin rettii deer, sol teriminin sa terimine blmnden kalandr.
lecin yan etkisi yoktur. rnein:
k = 15 % 4;
/* burada k ya 3 deeri atanr*/
x = 2 % 7;
/* burada x e 2 deeri atanr*/
int c = 13 - 3 * 4 + 8 / 3 - 5 % 2;
Burada c deikenine 2 deeri atanr. nk ilem u ekilde yaplr:
77/529
c = 13 - (3 * 4) + (8 / 3) - (5 % 2)
c = 13 - 12 + 2 - 1;
c = 2;
Aadaki programda 3 basamakl bir saynn birler, onlar ve yzler basamaklar ekrana
yazdrlyor:
#include <stdio.h>
int main()
{
int x;
printf("3 basamakli bir sayi girin: ");
scanf("%d", &x);
printf("birler basamagi = %d\n", x % 10);
printf("onlar basamagi = %d\n", x % 100 / 10);
printf("yuzler basamagi = %d\n", x / 100);
}
return 0;
ve
c++ ;
78/529
x = 10;
y = ++x;
Bu durumda:
++x 11
ve y = 11 deeri atanr.
x = 10;
y = x++;
Bu durumda
x++ => 10
ve y deikenine 10 deeri atanr.
Aadaki program inceleyin:
#include <stdio.h>
int main()
{
int a = 10;
int b = ++a;
printf("a = %d b = %d\n", a, b);
a = 10;
b = a++;
printf("a = %d b = %d\n", a, b);
return 0;
}
Yukardaki birinci printf ars ifadesi ekrana
11 11
deerlerini yazdrrken ikinci printf ars ekrana
11 10
deerlerini yazdrr. Aadaki rnei inceleyin:
#include <stdio.h>
int main()
{
int x = 10;
int y = 5;
int z = x++ % 4 * --y;
printf("z = %d\n", z);
printf("x = %d\n", x);
printf("y = %d\n", y);
}
return 0;
79/529
z
z
z
z
y
x
= x++ % 4 * 4;
= 10 % 4 * 4;
= 2 * 4;
= 8;
=> 4
=> 11
main()
int a = 10;
int b = func(a++);
printf("a = %d\n", a);
printf("b = %d\n", b);
return 0;
80/529
Davran
Tanmsz Davran
Belirlenmemi Davran
Derleyiciye zg Davran
C dilinin baz zellikleri, esneklik salamak amac ile standartlarda derleyici yazanlarn
seimlerine braklmtr. rnein int trnn uzunluunun ne olduu, varsaylan char
trnn signed m unsigned m olduu, i ie yorumlamalarn kabul edilip edilmedii
tamamen derleyici yazanlara baldr. Derleyiciler, bu zelliklerin nasl seildiklerini
belgelemek zorundadr. Bu tr davrana derleyiciye zg davran (implementation
dependent behaviour) denir. Bu davran zellikleri pekok derleyicide menlerden
deitirilebilmektedir.
Bulgu letileri
C standartlar temel olarak derleyiciyi yazanlar iin bir klavuz biimindedir. Derleyici
sorunlu bir kodla karlatnda uygun dntrme ilemlerini yapamyorsa sorunun
nedenine ilikin bir bildirimde bulunmak zorundadr. Standartlarda derleyicilerin sorunu
programcya bildirme durumuna "bulgu iletisi" (diagnostic message) denmektedir.
Standartlar iinde belirtilmi olan szdizimsel ve anlamsal kurallarn inendii
durumlarda bir uyar iletisi verilmelidir. Bu iletinin uyar (warning) ya da hata (error)
biiminde olmas, derleyicinin isteine braklmtr.
Ancak derleyicilerin hemen hepsinde uyarlar, derleyiciler tarafndan giderilebilecek kk
yanllar iin, hata ise daha byk yanllar iin verilir.
rnein bir gstericiye farkl trden bir adresin dorudan atanmas C'nin kurallarna
aykrdr. Bu durumda derleyici standartlara gre bir ileti vermelidir. Aslnda standartlara
gre, uyar ya da hata iletisi verilebilir, ama C derleyicilerinin hemen hepsi uyar iletisi
verir.
Standartlarda baz kurallarn inenmesi durumunda derleyicinin aka bir ileti
vermeyebilecei belirtilmitir (nodiagnostic required). Aslnda C standartlarnda belirtildii
gibi kural inenmeleri durumunda derleyicinin ilemi baar ile bitirip bitirmeyecei
aka belirtilmemitir. Yani standartlara gre derleyici, doru bir program
derlemeyebilir, yanl bir program derleyebilir.
81/529
Ancak C++ standartlarnda durum byle deildir. Dilin kuralna uymayan kodlarda
derleyici bir ileti vermeli, derleme ilemini baarszlk ile sonulanmaldr.
Derleyiciler, standartlarda belirtilen zelliklerin dnda da baz zelliklere sahip olabilir.
Bu tr zelliklere derleyicilerin eklentileri denir. Derleyicilerin eklentilerini kullanmak
tanabilirlii azaltr.
int x = 20, y;
int a = 5;
y = ++x + ++x;
y = ++x + x
a = ++a;
/* tanmsz davran */
/* tanmsz davran */
/* tanmsz davran */
"Koul ileci", "mantksal ve ileci", "mantksal veya ileci" ve "virgl" ileciyle oluturulan
ifadelerde bir sorun yoktur. Bu ilelerle ilgili nemli bir kurala ileride deineceiz.
2. Bir ilev arlrken ileve gnderilen argmanlarn birinde bir nesne ++ ya da -ilecinin terimi olmusa, bu nesne, ileve gnderilen dier argman olan ifadelerde
kullanlmamaldr.
Argman olan ifadelerin, ilevlerin ilgili parametre deikenlerine kopyalanmasna ilikin
sra, standart bir biimde belirlenmemitir. Bu kopyalama ilemi, baz sistemlerde soldan
saa baz sistemlerde ise sadan soladr. Aadaki rnei inceleyin:
int a = 10;
void func(int x, int y)
{
/***/
}
int main()
{
func (a, a++);
}
/* Tanmsz davran */
/***/
==
!=
82/529
lk drt ile, ile ncelik tablosunun 6. seviyesinde bulunurken dier iki karlatrma
ileci ncelik tablosunun 7. seviyesinde bulunur. Yani karlatrma ileleri, kendi
aralarnda iki ncelik grubu oluturur. Karlatrma ileleri, aritmetik ilelerden daha
dk ncelikli seviyededir.
Dier programlama dillerinin ounda bool ya da boolean (Matematiki George Bool'un
isminden) ismi verilen bir mantksal veri tr de doal bir veri tr olarak programcnn
kullanmna sunulmutur. Byle dillerde bool veri tr, yalnzca mantksal doru ya da
mantksal yanl deerlerini alabilen bir trdr. Bu dillerde karlatrma ilelerinin
rettii deerler ise bu trdendir. rnein C++ ya da Java dillerinde durum byledir.
C dilinde karlatrma ileleri, oluturduklar nermenin doruluu ve yanllna gre
int trden 1 ya da 0 deerini retir. nerme doru ise 1 deeri retilirken, nerme
yanl ise 0 deeri retilir. Bu ilelerin rettii deerler de tpk aritmetik ilelerin
rettii deerler gibi kullanlabilir.
Aadaki signum isimli ilevin tanmn inceleyin:
int signum(int val)
{
return (val > 0) - (val < 0);
}
signum ilevine gnderilen argman 0'dan byk bir deerse ilev +1 deerine,
argman 0'dan kk bir deerse ilev -1 deerine, argman 0 deeriyse ilev, 0
deerine geri dnyor. signum ilevinin geri dn deeri, karlatrma ilelerinin deer
retmesinden faydalanlarak elde ediliyor.
Baz programlama dillerinde
(val > 0) - (val < 0);
gibi bir ilem hata ile sonulanr. nk rnein Pascal dilinde
val > 0
ifadesinden elde edilen deer doru (True) ya da yanl (False) dir. Yani retilen deer
bool ya da boolean trndendir. Ama C doal bir dil olduu iin karlatrma ilelerinin
rettikleri deer bool tr ile kstlanmamtr. C'de mantksal veri tr yerine int tr
kullanlr. Mantksal bir veri trnn tamsay tryle ayn olmas C'ye esneklik ve doallk
kazandrmtr. C dilinde yazlan birok kalp kod, karlatrma ilelerinin int trden 1 ya
da 0 deeri retmesine dayanr. rnein
x = y == z;
Yukardaki deyim, C dili iin son derece doaldr ve okunabilirlii yksektir. Bu deyimin
yrtlmesiyle x deikenine ya 1 ya da 0 deeri atanr. Karlatrma ileci, atama
ilecinden daha yksek ncelik seviyesine sahip olduuna gre nce karlatrma ileci
olan '==' deer retir, ilecin rettii deer bu kez atama ilecinin terimi olur. Bu
durumda y deikeninin deerinin z deikenine eit olup olmamasna gre x deikenine
1 ya da 0 deeri atanr.
Karlatrma ilecinin kullanlmasnda baz durumlara dikkat edilmelidir:
int x = 12;
5 < x <
83/529
Mantksal leler
Bu ileler, terimleri zerinde mantksal ilem yapar. Terimlerini doru (true) ya da yanl
(false) olarak yorumladktan sonra ileme sokar. C'de ncelikleri farkl seviyede olan
mantksal ile vardr:
(!) mantksal deil ileci (logical not)
(&&) mantksal ve ileci (logical and)
(||) mantksal veya ileci (logical or)
Ancak "mantksal ve", "mantksal veya" ileleri, bilinen anlamda ile ncelik kurallarna
uymaz. Bu konuya biraz ileride deineceiz.
C'de mantksal veri tr olmadn biliyorsunuz. Mantksal veri tr olmad iin bu
trn yerine int tr kullanlr ve mantksal doru olarak 1, mantksal yanl olarak da 0
deeri kullanlr.
C dilinde herhangi bir ifade, mantksal ilelerin terimi olabilir. Bu durumda sz konusu
ifade, mantksal olarak yorumlanr. Bunun iin ifadenin saysal deeri hesaplanr.
Hesaplanan saysal deer, 0 d bir deer ise doru (1), 0 ise yanl (0) olarak
yorumlanr. rnein:
25 Doru (nk 0 d bir deer)
-12 Doru (nk 0 d bir deer)
0 Yanl (nk 0)
ifadesi mantksal bir ilecin terimi olduu zaman yanl olarak yorumlanr. nk saysal
deeri sfra eittir.
Mantksal deil ileci, nek konumunda bulunan tek terimli bir iletir. Bu ile, teriminin
mantksal deerinin tersini retir. Yani terimi mantksal olarak "doru" biiminde
yorumlanan bir deer ise ile yanl anlamnda int trden 0 deerini retir. Terimi,
mantksal olarak "yanl" biiminde yorumlanan bir deer ise ile doru anlamnda int
trden 1 deerini retir.
x
Doru (0 d deer)
Yanl (0)
!x
Yanl (0)
Doru (1)
rnekler :
a = !25; /* a deikenine 0 deeri atanr
b = 10 * 3 < 7 + !2
lem sras:
84/529
*/
!2 = 0
10 * 3 = 30
7 + 0 = 7
30 < 7 = 0
b = 0 (atama ileci en dk ncelikli iletir)
y = 5;
x = !++y < 5 != 8;
lem sras:
++y 6
!6 0
/* ++ ve ! ileleri ayn ncelik seviyesindedir ve ncelik
yn sadan soladr. */
0 < 5 1
1 != 8 1
x = 1
Bu ile ilikisel ilelerin hepsinden dk, || (veya / or) ilecinden yksek nceliklidir.
Terimlerinin ikisi de doru ise doru (1), terimlerinden biri yanl ise yanl (0) deerini
retir.
x
3
7
1
x
= 3 < 5 && 7;
< 5 1
1
&& 1 1
= 1
&& ilecinin, nce sol tarafndaki ilemler ncelik srasna gre tam olarak yaplr. Eer
bu ilemlerde elde edilen saysal deer 0 ise, && ilecinin sa tarafndaki ilemler hi
yaplmadan, yanl (0) saysal deeri retilir. rnein:
x = 20;
b = !x == 4 && sqrt(24);
!20 0
0 == 4 0
Sol taraf 0 deeri alacandan ilecin sa taraf hi yrtlmez dolaysyla da sqrt ilevi
arlmaz. Sonu olarak b deikenine 0 deeri atanr.
Uygulamalarda mantksal ileler ounlukla karlatrma ileleriyle birlikte kullanlr:
scanf("%d", &x);
y = x >= 5 && x <= 25;
Bu durumda y deikenine, ya 1 ya da 0 deeri atanr. Eer x deikeninin deeri 5'den
byk ya da eit ve 25'den kk ya da eit ise y deikenine 1 deeri, bunun dndaki
durumlarda y deikenine 0 deeri atanr.
85/529
ch = 'c'
z = ch >= 'a' && ch <= 'z'
Yukardaki rnekte, ch deikeninin kk harf olup olmamas durumuna gre z
deikenine 1 ya da 0 atanr.
ncelii en dk olan mantksal iletir. ki teriminden biri doru ise doru deerini
retir. ki terimi de yanl ise yanl deerini retir.
a = 3 || 5
x = 0 || -12
sayi = 0 || !5
/* a = 1 */
/* x = 1 */
/* sayi = 0 */
ch == 'A' || ch == 'E'
Yukardaki ifade ile, result isimli deikene, ch deikeninin deerinin 'A' ya da 'B' ye eit
olmas durumunda 1 deeri, aksi halde 0 deeri atanr. ch eer 'A' ya eit ise ikinci
karlatrma yaplmaz.
Mantksal ileler bir deer retebilmek iin terimlerini nce 1 ya da 0, yani doru ya da
yanl olarak yorumlar, ama yan etkileri yoktur. Yani terimlerinin nesne olmas
durumunda bu nesnelerin bellekteki deerlerini 1 ya da 0 olarak deitirmezler.
Atama leleri
Atama ileleri, C dilinde ncelik tablosunun en alttan ikinci seviyesinde, yani 14.
seviyesinde bulunur ve yalnzca virgl ilecinden daha yksek nceliklidir. Atama
ilelerinin bulunduu 14. seviye, sadan sola ncelik ynne sahiptir.
Dier ileler gibi atama ileci de, yapt atama ileminin yansra, bir deer retir.
86/529
Atama ilecinin rettii deer, nesneye atanan deerin kendisidir. Aadaki program
derleyerek altrn:
#include <stdio.h>
int main()
{
int x;
printf("ifade degeri = %d\n", x = 5);
printf("x = %d\n", x);
}
return 0;
main ilevi iinde yaplan birinci printf ars ile, x = 5 ifadesinin deeri yazdrlyor.
x = 5 ifadesinin deeri atama ilecinin rettii deer olan 5 deeridir. Yani ilk printf
ars ile ekrana 5 deeri yazdrlr. Atama ileci yan etkisi sonucu x nesnesinin deerini
5 yapar. Bu durumda ikinci printf ars ile x deikeninin deeri ekrana yazdrldndan
ekrana yazlan, 5 deeri olur.
Atama ilecinin rettii deer nesne deildir. Aadaki deyim geersizdir:
(b = c) = a;
/* Geersiz! */
=
=
=
=
=
=
d = 5;
%d\n",
%d\n",
%d\n",
%d\n",
%d\n",
a);
b);
c);
d);
e);
return 0;
le ncelik tablosundan da grlecei gibi, atama ileleri sadan sola ncelik ynne
sahiptir. Bu yzden:
a = b = c = d = 5;
deyimi C'de geerlidir.
Bu deyimde nce d deikenine 5 deeri atanr. Atama ilecinin rettii 5 deeri, bu kez
c deikenine atanr. Sadan sola doru ele alnan her atama ileci, nesneye atanan
deeri rettiine gre, tm deikenlere 5 deeri aktarlm olur. Atama ilecinin rettii
deerden faydalanmak, zellikle kontrol deyimlerinde karnza ok kacak.
87/529
Bir ilemin terimi ile, ilem sonucunda retilen deerin atanaca nesne ayn ise, ilemli
atama ileleri kullanlabilir.
<nesne1> = <nesne1> ilem <terim2>
ile
<nesne1>
ilem= <terim2>
ayn anlamdadr.
lemli atama ileleri, atama ileciyle ayn ncelik seviyesindedir.
lemli atama ileleri, hem okunabilirlik hem de daha ksa yazm iin tercih edilir.
Aadaki ifadeler edeerdir:
deger1 += 5;
sonuc *= yuzde;
x %= 5
deger1 = deger1 + 5;
sonuc = sonuc * yuzde;
x = x % 5;
88/529
Yukardaki rneklerden de grld gibi, atama grubu ilelerin yan etkileri vardr. Yan
etkileri, ilecin sol teriminin bellekteki deerinin deitirilmesi, yani ilecin sa tarafndaki
terimi olan ifadenin deerinin sol tarafndaki nesneye aktarlmas eklinde kendini
gsterir.
Virgl leci
ki ayr ifadeyi tek bir ifade olarak birletiren virgl ileci, C'nin en dk ncelikli
ilecidir.
ifade1;
ifade2;
ile
ifade1, ifade2;
ayn ileve sahiptir.
Virgl ilecinin, nce sol terimi olan ifadenin sonra sa terimi olan ifadenin ele alnmas
gvence altndadr. Bu ilecin rettii deer, sa tarafndaki ifadenin rettii deerdir.
Virgl ilecinin sol teriminin, retilen deere bir etkisi olmaz.
x = (y++, z = 100);
gibi bir deyimle x ve z deikenlerine 100 deeri atanr.
Aadaki rnekte if ayrac iindeki ifadenin rettii deer 0'dr.
if (x > 5,0) {
/***/
}
Virgl ileleri ile bir bileik deyim basit deyim durumuna getirilebilir:
if (x ==
a1 =
a2 =
a3 =
}
20) {
20;
30;
40;
yerine
if (x == 20)
a1 = 20, a2 = 30, a3 = 40;
yazlabilir.
Virgl ilecinin sa terimi nesne gsteren bir ifade olsa bile ilecin oluturduu ifade bir
nesne deildir:
int x, y;
/***/
(x, y) = 10;
Yukardaki atama ilemi geersizdir.
[C++ dilinde virgl ilecinin oluturduu bir ifade sol taraf deeri olabilir. Yukardaki atama C++ dilinde
geerlidir.]
89/529
ncelik leci
ncelik ileci ( ), bir ifadenin nceliini ykseltmek amacyla kullanlr.
x = (y + z) * t;
ncelik ileci, C'nin en yksek ncelikli ileler grubundadr. ncelik ileci de, kendi
arasnda soldan saa ncelik kuralna uyar. rnein:
a = (x + 2) / ((y + 3) * (z + 2) 1);
ifadesinde ilem sras yledir :
i1
i2
i3
i4
i5
i6
i7
:
:
:
:
:
:
:
x + 2
y + 3
z + 2
i2 * i3
i4 1
i1 / i5
a = i6
ncelik ilecinin terimi nesne gsteren bir ifade ise, ilecin rettii ifade de nesne
gsterir:
int x;
(x) = 20;
/* Geerli
*/
le ncelii, bir ilemin ilemci tarafndan daha nce yaplmas anlamna gelmez.
Aadaki ifadeyi dnelim:
x = func1() * func2() + func3();
arpma ilecinin toplama ilecinden daha yksek ncelikli olduunu biliyorsunuz. Ancak
bu ncelik, rnein yukardaki deyimde func1 ilevinin func3 ilevinden daha nce
arlaca gvencesi anlamna gelmez. ncelik ilecinin de kullanm byle bir gvence
salamaz.
x = func1() * (func2() + func3());
Bu kez de rnein func2 ilevinin func1 ilevinden daha nce arlmasnn gvencesi
yoktur.Bu kez de aadaki main ilevini inceleyin:
#include <stdio.h>
int main()
{
int x = 10;
int y = x + (x = 30);
printf("y = %d\n", y);
return 0;
}
main ilevi iinde yazlan
y = x + (x = 30);
90/529
deyimi pheli kod oluturur. y deikenine atanan deerin ne olaca konusunda bir
gvence yoktur. x = 30 ifadesinin ncelik ayrac iine alnmas, toplama teriminin sol
terimi olan x ifadesinin deerinin 30 olarak ele alnacan, gvence altna almaz.
Sonuta, y deikenine 60 deeri aktarlabilecei gibi, 40 deeri de atanabilir.
Ancak C dilinin 4 ileci terimlerine ilikin, daha nce ilem yapma gvencesini verir. Bu
ileler:
mantksal ve, mantksal veya, koul ve virgl ileleridir.
Mantksal ve/veya ilelerinin ksa devre davranlarn renmitiniz. Ksa devre
davrannn gerekletirilebilmesi iin bu ilelerin sol terimlerinin daha nce yaplmas
gvence altna alnmtr.
leride greceiniz koul ilecinin de bir deer retebilmesi iin, nce ilk teriminin
deerlendirilmesi gerekir. Virgl ilecinin ise zaten varlk nedeni nce sol, daha sonra sa
teriminin yaplmasn salamaktr.
91/529
le
()
[]
.
->
+
++
-~
!
*
&
sizeof
(tr)
*
/
%
+
<<
4
5
>>
6
<
>
<=
>=
==
!=
&
^
|
&&
||
?:
=
+=
8
9
10
11
12
13
14
-=
*=
/=
%=
<<=
>>=
&=
|=
^=
15
Tanm
ncelik kazandrma ve ilev ar
(precedence and function call)
keli ayra ileci (subscript)
yap elemanna yap nesnesi ile ulam
(structure access with object)
yap elemanna yap gstericisi ile ulam
(structure access with pointer)
iaret ileci (unary sign)
iaret ileci (unary sign)
artrma ileci (increment)
eksiltme ileci (decrement)
bitsel deil ileci (bitwise not)
mantksal deil ileci(logical not)
ierik ileci (indirection)
adres ileci (address of)
sizeof ileci (sizeof)
tr dntrme ileci
(type cast operator)
arpma ileci (multiplication)
blme ileci(division)
kalan ileci (modulus)
toplama ileci (addition)
karma ileci (subtraction)
bitsel sola kaydrma ileci(bitwise shift left)
92/529
ncelik Yn
(associativity)
soldan saa
sadan sola
soldan saa
soldan saa
soldan saa
soldan saa
soldan saa
soldan saa
soldan saa
soldan saa
soldan saa
soldan saa
sadan sola
sadan sola
soldan saa
BLNRLK ALANI VE MR
Daha nceki konularda nesnelerin isimlerinden, deerlerinden ve trlerinden sz
edilmiti. Nesnelerin C dili asndan ok nem tayan zellii daha sz konusudur.
Bunlar bilinirlik alan (scope), mr (storage duration) ve balant (linkage) zelliidir.
Bilinirlik Alan
Bilinirlik alan (scope), bir ismin tannabildii program araldr. Derleyiciye bildirilen
isimler, derleyici tarafndan her yerde bilinmez. Her isim derleyici tarafndan ancak "o
ismin bilinirlik alan" iinde tannabilir. Bilinirlik alan dorudan kaynak kod ile ilgili bir
kavramdr, dolaysyla derleme zamanna ilikindir. C dilinde derleyici, bildirimleri yaplan
deikenlere kaynak kodun ancak belirli blmlerinde ulalabilir. Yani bir deikenin
tanmlanmas, o deikene kaynak dosyann her yerinden ulalabilmesi anlamna gelmez.
Bilinirlik alanlar C standartlar tarafndan 4 ayr grupta toplanmtr:
i. Dosya Bilinirlik Alan (File scope) : Bir ismin bildirildikten sonra tm kaynak dosya
iinde, yani tanmlanan tm ilevlerin hepsinin iinde bilinmesidir.
ii. Blok Bilinirlik Alan (Block scope): Bir ismin bildirildikten sonra yalnzca bir blok iinde,
bilinmesidir.
iii. lev Bilinirlik Alan (Function Scope): Bir ismin, bildirildikten sonra yalnzca bir blok
iinde bilinmesidir. Yalnzca goto etiketlerini kapsayan zel bir tanmdr. Bu bilinirlik
alanna "goto deyimi" konusunda deinilecek.
iv. lev Bildirimi Bilinirlik Alan (Function Prototype Scope): lev bildirimlerindeki, ilev
parametre ayrac iinde kullanlan isimlerin tannabilirliini kapsayan bir tanmdr. Bu
bilinirlik alanna "lev Bildirimleri" konusunda deinilecek.
Bir kaynak dosya iinde tanmlanan deikenler, bilinirlik alanlarna gre "yerel" ve
"global" olmak zere ikiye ayrlabilir:
Yerel Deikenler
93/529
#include <stdio.h>
int main()
{
int x = 10;
printf("x = %d\n", x);
{
int y = 20;
printf("y = %d\n", y);
x = 30;
{
int z = 50;
y = 60;
printf("z = %d\n", z);
printf("x = %d\n", x);
printf("y = %d\n", y);
}
z = 100; /* Geersiz! */
y = x;
printf("x = %d\n", x);
printf("y = %d\n", y);
}
y = 500; /* Geersiz! */
printf("x = %d\n", x);
return 0;
}
Global Deikenler
94/529
#include <stdio.h>
int g;
void func()
{
g = 10;
}
int main()
{
g = 20;
printf("g = %d\n", g);
func();
printf("g = %d\n", g);
/* g = 20 */
/* g = 10 */
return 0;
}
Yukardaki rnekte g deikeni blok dnda tanmland iin -ya da hibir ilevin iinde
tanmlanmad iin- global deikendir. g deikeninin bilinirlik alan, dosya bilinirlik
alandr. Yani g deikeni, tanmlandktan sonra tm ilevlerin iinde kullanlabilir.
Yukardaki programda nce g global deikenine 20 deeri atanyor.Daha sonra bu deer
printf ileviyle ekrana yazdrlyor. Daha sonra func ilevi arlyor. func ilevi arlnca
kodun ak func ilevine geer. func ilevi iinde de g global deikeni bilinir. func
ilevinde global y deikenine 10 deerinin atanmasndan sonra bu deer yine printf
ileviyle ekrana yazdrlyor.
C dilinde ayn isimli birden fazla deiken tanmlanabilir. Genel kural udur: ki
deikenin bilinirlik alanlar ayn ise, bu deikenler ayn ismi tayamaz. Ayn ismi
tamalar derleme zamannda hata oluturur. ki deikenin bilinirlik alanlarnn ayn
olmas ne anlama gelir? ki deikenin bilinirlik alanlar, ayn kapanan kme ayrac ile
sonlanyorsa, bu deikenlerin bilinirlik alanlar ayn demektir.
{
float a;
int b;
double a;
{
int c;
/*...*/
}
/* Geersiz */
}
Yukardaki kod geersizdir. nk her iki a deikeninin de bilinirlik alan ayndr.
Farkl bilinirlik alanlarna sahip birden fazla ayn isimli deiken tanmlanabilir. nk
derleyiciler iin, artk bu deikenlerin ayn isimli olmas nemli deildir. Bunlar bellekte
farkl yerlerde tutulur. Aadaki rnei inceleyin:
95/529
#include <stdio.h>
int main()
{
int x = 100;
printf("%d\n", x);
{
int x = 200;
printf("%d\n", x);
{
int x = 300;
printf("%d\n", x);
}
}
return 0;
}
Yukardaki program parasnda bir hata bulunmuyor. nk her x deikeninin de
bilinirlik alanlar birbirlerinden farkldr. Peki yukardaki rnekte i bloklarda x ismi
kullanldnda derleyici bunu hangi x deikeni ile ilikilendirir?
Bir kaynak kod noktas, ayn isimli birden fazla deikenin bilinirlik alan iinde ise, bu
noktada deikenlerden hangisine eriilir?
Derleyici, bir ismin kullanm ile karlatnda bu ismin hangi yazlmsal varla ait
olduunu bulmaya alr. Bu ileme "isim arama" (name lookup) denir. sim arama, dar
bilinirlik alanndan geni bilinirlik alanna doru yaplr. Yani derleyici sz konusu ismi
nce kendi blounda arar. Eer isim, bu blok iinde tanmlanmam ise bu kez isim
kapsayan bloklarda aranr. sim, kapsayan bloklarda da bulunamaz ise bu kez global isim
alannda aranr.
Dar bilinirlik alanna sahip isim, daha geni bilinirlik alannda yer alan ayn ismi maskeler,
onun grnmesini engeller. Aadaki program inceleyin:
void func1()
{
int k;
/***/
}
void func2()
{
int k;
/***/
}
void func3()
{
int k;
/***/
}
Yukardaki kod parasnda bir hata sz konusu deildir. Her ilevde de k isimli bir
deiken tanmlanm olsa da bunlarn bilinirlik alanlar tamamen birbirinden farkldr.
Bir global deikenle ayn isimli yerel bir deiken olabilir mi? ki deikenin bilinirlik
alanlar ayn olmad iin bu durum bir hataya neden olmaz.
Ayn isimli hem bir global hem de bir yerel deikene eriilebilen bir noktada, eriilen
yerel deiken olur. nk ayn bilinirlik alannda, birden fazla ayn isimli deiken olmas
durumunda, o alan iinde en dar bilinirlik alanna sahip olanna eriilebilir. Aadaki kodu
inceleyin:
96/529
#include <stdio.h>
int
g = 20;
/* g global deiken */
void func()
{
/* global g deikenine atama yaplyor. */
g = 100;
/* global g deikeninin deeri yazdrlyor. */
printf("global g = %d\n", g);
}
int main()
{
int g; /* g yerel deiken */
/* yerel g deikenine atama yaplyor */
g = 200;
/* yerel g yazdrlyor. */
printf("yerel g = %d\n", g);
func();
/* yerel g yazdrlyor. */
printf("yerel g = %d\n", g);
}
return 0;
Nesnelerin mrleri
Statik mrl varlklar (static duration static storage class), programn almaya
balamasyla bellekte yerlerini alr, programn almas bitene kadar varlklarn
srdrr, yani bellekte yer kaplar. Statik mrl varlklar, genellikle ama kod (.obj) iine
yazlr. C dilinde statik mrl ayr varlk grubu vardr:
global deikenler
dizgeler (ift trnak iindeki yazlar)
statik yerel deikenler
97/529
Otomatik mrl nesneler programn almasnn belli bir zamannda yaratlan, belli sre
etkinlik gsterdikten sonra yok olan, yani mrlerini tamamlayan nesnelerdir. Bu tr
nesnelerin mrleri, programn toplam alma sresinden ksadr.
Yerel deikenler, otomatik mrldr. Programn alma zamannda tanmlandklar
bloun almas baladnda yaratlrlar, bloun almas bitince yok olurlar, yani
mrleri sona erer.
void func(int a, int b)
{
int result;
/***/
}
Yukardaki func ilevinin ana blou iinde result isimli bir yerel deiken tanmlanyor.
Programn almas srasnda func ilevinin koduna girildiinde result deikeni yaratlr.
Programn ak func ilevinden ktnda, result deikeninin mr sona erer.
Statik mrl deikenlerle otomatik mrl deikenler arasnda ilkdeer verme
(initialization) asndan da fark vardr. Statik mrl olan global deikenlere de yerel
deikenlerde olduu gibi ilkdeer verilebilir.
lkdeer verilmemi ya da bir atama yaplmam bir yerel deikenin iinde bir p deer
bulunur. Bu deer o an bellekte o deiken iin ayrlm yerde bulunan 1 ve 0 bitlerinin
oluturduu deerdir.
lkdeer verilmemi statik mrl deikenlerin 0 deeri ile balatlmas gvence
altndadr. lk deer verilmemi ya da bir atama yaplmam global deikenler iinde her
zaman 0 deeri vardr. Yani bu deikenler derleyici tarafndan retilen kod yardmyla 0
deeriyle balatlr.
Aadaki program derleyerek altrn:
#include <stdio.h>
int g;
int main()
{
int y;
printf("g = %d\n", g);
printf("y = %d\n", y);
}
/* Yanl /
return 0;
98/529
[Global deikenlere deimez ifadesi olmayan ifadelerle ilkdeer verilmesi C++ dilinde geerlidir. Yeni C
derleyicilerin ou, global deikenlere deimez ifadesi olmayan ifadelerle ilkdeer verilmesi durumunda da
kodu geerli sayma eilimindedir. Tanabilirlik asndan bu durumdan kanlmasn salk veriyoruz.]
Ancak yerel deikenlere ilkdeer verilme ileminde byle bir kstlama yoktur.
#include <stdio.h>
int func(void);
int x = 5;
int y = x + 5;
int z = func();
/* Geersiz */
/* Geersiz */
int main()
{
int a = b;
int k = b - 2;
int m = func();
/***/
}
Yukardaki programda main ilevi iinde a, k, m deikenlerinin tanmlanmalar geerlidir.
Dinamik bellek ilevleri ile yerleri ayrlm nesneler, dinamik mrldr. Dinamik bellek
ilevleri ile yaratlm nesneleri daha sonra greceksiniz.
99/529
6. Global deikenler global isim alann kirletir. Bu noktaya ileride "balant" kavram ele
alnd zaman yeniden deinilecek.
levler geri dn deerlerini, geici bir nesne yardmyla kendilerini aran ilevlere
iletir. Aadaki program inceleyin:
#include <stdio.h>
int add(int x, int y)
{
return x + y;
}
int main()
{
int a, b, sum;
printf("iki say girin: ");
scanf("%d%d", &a, &b);
sum = add(a, b);
printf("toplam = %d\n", sum);
}
return 0;
Bir ilevin geri dn deerinin tr aslnda, ilevin geri dn deerini iinde tayacak
geici nesnenin tr demektir. Yukarda tanm verilen add isimli ilevin main ilevi
iinden arldn gryorsunuz. Programn ak, add ilevi iinde return deyimine
geldiinde, geici bir nesne yaratlr. Bu geici nesne, return ifadesiyle ilkdeerini alr.
Yani return ifadesi aslnda oluturulan geici nesneye ilkdeerini veren ifadedir. Geri
dn deeri reten bir ileve yaplan ar, bu ilevin geri dn deerini iinde tutan
geici nesneyi temsil eder. Peki bu geici nesnenin mr ne kadardr? Bu nesne, return
deyimiyle yaratlr ve ilev arsn ieren ifadenin deerlendirilmesi sona erince yok
edilir. Yani rnekteki main ilevi iinde yer alan
sum = add(a, b);
deyiminin yrtlmesinden sonra, geici nesnenin mr de sona erer.
100/529
KONTROL DEYMLER
C dilinde yazlm bir programn cmlelerine deyim(statement) dendiini biliyorsunuz.
Baz deyimler, yalnzca derleyici programa bilgi verir. Bu deyimler derleyicinin ilem
yapan bir kod retmesine neden olmaz. Byle deyimlere "bildirim deyimi" (declaration
statement) denir.
Baz deyimler derleyicinin ilem yapan bir kod retmesine neden olur. Byle deyimlere
"yrtlebilir deyim" (executable statement) denir.
Yrtlebilir deyimler de farkl gruplara ayrlabilir:
Yaln Deyim:
Bir ifadenin, sonlandrc atom ile sonlandrlmasyla oluan deyimlere yaln deyim (simple
statement) denir;
x = 10;
y++;
func();
Yukarda 3 ayr yaln deyim yazlmtr.
Bo Deyim:
C dilinde tek bana bulunan bir sonlandrc atom ';', kendi bana bir deyim oluturur.
Bu deyime bo deyim (null statement) denir. Bo bir blok da bo deyim oluturur:
;
{}
Yukardaki her iki deyim de bo deyimdir.
Bileik Deyim:
Bir blok iine alnm bir ya da birden fazla deyimin oluturduu yapya, bileik deyim
(compound statement) denir. Aada bir bileik deyim grlyor.
{
x = 10;
y++;
func();
Kontrol deyimi:
101/529
if DEYM
C dilinde program akn denetlemeye ynelik en nemli deyim if deyimidir.
En yaln biimiyle if deyiminin genel szdizimi aadaki gibidir:
if (ifade)
deyim;
if ayrac iindeki ifadeye koul ifadesi (conditional expression) denir.
if ayracn izleyen deyime, if deyiminin doru ksm (true path) denir.
if deyiminin doru ksmn oluturan deyim, bir yaln deyim (simple statement) olabilecei
gibi, bir bo deyim (null statement), bir bileik deyim (compound statement) ya da
baka bir kontrol deyimi de (control statement) olabilir.
Yaln if deyiminin yrtlmesi aadaki gibi olur:
nce koul ifadesinin saysal deerini hesaplar. Hesaplanan saysal deer, mantksal
DORU ya da YANLI olarak yorumlanr. Koul ifadesinin hesaplanan deeri 0 ise yanl,
0'dan farkl bir deer ise doru olarak yorumlanr. rnein koul ifadesinin hesaplanan
deerinin 5 olduunu dnelim. Bu durumda kontrol ifadesi doru olarak
deerlendirilir. Eer ifade DORU olarak yorumlanrsa, if deyiminin doru ksm yaplr,
ifade YANLI olarak yorumlanrsa doru ksm yaplmaz. Yaln if deyimi, bir ifadenin
doruluuna ya da yanllna gre, bir deyimin yaplmas ya da yaplmamasna dayanr.
Aadaki program derleyerek altrn:
int main()
{
int x;
printf("bir sayi girin : ");
scanf("%d", &x);
if (x > 10)
printf("if deyiminin doru ksm!\n");
}
return 0;
main ilevi iinde yazlan if deyimiyle, klavyeden girilen tamsaynn 10'dan byk olmas
durumunda printf ars yrtlr, aksi halde yrtlmez.
if kontrol deyimi, else anahtar szcn de ierebilir. Byle if deyimine, yanl ksm
olan if deyimi denir. Yanl ksm olan if deyiminin genel biimi aadaki gibidir:
if (ifade)
deyim1;
else
deyim2;
Bu kez if deyiminin doru ksmn izleyen deyimden sonra else anahtar szcnn, daha
sonra ise bir baka deyimin yer aldn gryorsunuz. Genel biimdeki deyim2'ye if
deyiminin yanl ksm (false path) denir.
if deyiminin koul ifadesi, mantksal olarak DORU ya da YANLI olarak yorumlanr. Bu
kez koul ifadesinin DORU olmas durumunda deyim1, YANLI olarak yorumlanmas
durumunda deyim2 yaplr. Yanl ksm olan if deyimi, bir koul ifadesinin doru ya da
102/529
yanl olmasna gre iki ayr deyimden birinin yaplmasna yneliktir. Yani ifade doru ise
bir i, yanl ise baka bir i yaplr.
Aadaki rnei inceleyin:
#include <stdio.h>
int main()
{
char ch;
printf("bir karakter girin : ");
ch = getchar();
if (ch >= 'a' && ch <= 'z')
printf("%c kucuk harf!\n", ch);
else
printf("%c kucuk harf degil!\n", ch);
}
return 0;
Yukardaki main ilevinde standart getchar ilevi kullanlarak klavyeden bir karakter
alnyor. Alnan karakterin sra numaras, ch isimli deikene atanyor. Koul ifadesinin
doru ya da yanl olmas durumuna gre, klavyeden alnan karakterin kk harf olup
olmad bilgisi ekrana yazdrlyor. Koul ifadesine bakalm:
ch >= 'a' && ch <= 'z'
Bu ifadenin doru olmas iin "mantksal ve (&&)" ilecinin her iki teriminin de doru
olmas gerekir. Bu da ancak, ch karakterinin kk harf karakteri olmas ile mmkndr.
if deyiminin doru ve/veya yanl ksm bir bileik deyim olabilir. Bu durumda, koul
ifadesinin doru ya da yanl olmasna gre, birden fazla yaln deyimin yrtlmesi
salanabilir. Aadaki rnei inceleyin:
/***/
if (x > 0) {
y = x * 2 + 3;
z = func(y);
result = z + x;
}
else {
y = x * 5 - 2;
z = func(y - 2);
result = z + x - y;
}
/***/
Yukardaki if deyiminde, x > 0 ifadesinin doru olup olmasna gre, result deikeninin
deeri farkl ilemlerle hesaplanyor. if deyiminin hem doru hem de yanl ksmlarn
bileik deyimler oluturuyor.
Bir if deyiminin yanl ksm olmak zorunda deildir. Ancak bir if deyimi yalnzca else
ksmna sahip olamaz. Bu durumda if deyiminin doru ksmna bo deyim ya da bo
bileik deyim yerletirilmelidir:
if (ifade)
;
else
deyim1;
103/529
ya da
if (ifade)
{ }
else
deyim1;
Yalnzca yanl ksm olan, doru ksm bir bo deyim olan bir if deyimi, okunabilirlik
asndan iyi bir seenek deildir. Byle durumlarda daha iyi bir teknik, koul ifadesinin
mantksal tersini alp, if deyiminin yanl ksmn ortadan kaldrmaktr:
if (!ifade)
deyim1;
Aadaki kod parasn inceleyin:
/***/
if (x > 5)
;
else {
func1(x);
func2(x);
}
/***/
Yukardaki if deyiminde, x deikeninin deeri 5'ten bykse bir ey yaplmyor, aksi
halde func1 ve func2 ilevleri x deikeninin deeri ile arlyor. Koul ifadesi ters
evrilerek if deyimi yeniden yazlrsa:
/***/
if (x <= 5) {
func1(x);
func2(x);
}
/***/
if ayracnn iinde, ifade tanmna uygun herhangi bir ifade bulunabilir:
if (10)
deyim1;
if (-1)
deyim2;
Yukardaki koul ifadelerinin deeri, her zaman doru olarak yorumlanr. nk ifadeler,
sfrdan farkl deere sahiptir.
Aadaki koul ifadesi ise her zaman yanl olarak yorumlanacandan if deyiminin doru
ksm hibir zaman yrtlmez:
if (0)
deyim1;
Aadaki if deyiminde ise, x deikeninin deerinin 0 olup olmamasna gre, deyim1 ve
deyim2 yrtlr:
104/529
if (x) {
deyim1;
deyim2;
/***/
}
Yukardaki yapyla aadaki yap edeerdir:
if (x != 0) {
deyim1;
deyim2;
}
Aadaki rnei inceleyin :
if (!x) {
deyim1;
deyim2;
}
Bu if deyiminde ise ancak x deikeninin deerinin 0 olmas durumunda deyim1 ve
deyim2 yrtlr.
Yine yukardaki yapyla aadaki yap edeerdir:
if (x == 0) {
deyim1;
deyim2;
}
if deyiminin koul ifadesinde atama ileci sklkla kullanlr. Bylece, atama ilecinin
rettii deerden faydalanlr: Aadaki kod parasn inceleyin:
if ((x = getval()) > 5)
func1(x);
else
func2(x);
if deyiminin koul ifadesinde ise, arlan getval ilevinin geri dn deeri, x deikenine
aktarlyor. Atama ilecinin rettii deerin nesneye atanan deer olduunu anmsayn.
Atama ileci ile oluturulan ifadenin, ncelik ayrac iine alndn gryorsunuz. Bu
durumda hem getval ilevinin geri dn deeri x deikenine aktarlyor hem de ilevin
geri dn deerinin 5'ten byk olup olmad sorgulanyor. ncelik ayrac
kullanlmasayd getval ilevinin geri dn deerinin 5'ten byk olup olmamasna gre x
deikenine 0 ya da 1 deeri atanrd. Bu durumda da ya func1 ilevi 1 deeriyle ya da
func2 ilevi 0 deeriyle arlrd. Deyim aadaki gibi de yazlabilirdi, deil mi?
x = getval();
if (x > 5)
func1(x);
else
func2(x);
Ancak kalp kod, daha karmak deyimlerin yazlmasnda kolaylk salar. Aadaki if
deyiminin nasl yrtleceini dnn. "Mantksal ve" ilecinin birinci ksmnn, daha
nce ele alnmasnn, yani "ksa devre" davrannn gvence altnda olduunu anmsayn.
105/529
e if Deyimleri
106/529
else if Merdiveni
107/529
Merdiveninin en sonundaki if deyiminin yanl ksmnn zel bir nemi vardr. Yukardaki
rnekte deyim5, merdivenin son if deyiminin else ksmnda yer alyor. Merdiven iindeki
hibir if deyiminin koul ifadesi doru deilse, son if deyimininin yanl ksm yaplr, deil
mi? Yukardaki merdivenin yrtlmesi sonucu, deyim1, deyim2, deyim3, deyim4,
deyim5'den biri mutlaka yaplr.
Son basamaktaki if deyiminin yanl ksm olmayan bir else if merdiveninden, hibir i
yaplmadan da klabilir.
Hem okunabilirlik asndan hem de verim asndan, else if merdiveninde olasl ya da
skl daha yksek olan koullar, daha yukarya kaydrlmaldr.
Sk Yaplan Hatalar
zellikle C'ye yeni balayanlarn sk yapt bir hata, yaln if deyimiyle yanl ksm olan if
deyimini birbirine kartrmaktr. Yani if deyiminin yanl ksm unutulur:
#include <stdio.h>
int main()
{
int x;
printf("bir sayi girin: ");
scanf("%d", &x);
if (x % 2 == 0)
printf("%d cift sayi!\n", x);
printf("%d teksayi!\n", x);
}
return 0;
if ayrac iindeki ifadenin yanl olmas durumunda bir yanllk sz konusu deildir. Ama
ifade doru ise ekrana ne yazlr? Klavyeden 28 deerinin girildiini dnelim:
28 ift sayi!
28 tek sayi!
Belki de en sk yaplan hata, if ayracnn sonuna yanllkla sonlandrc atomun (;)
yerletirilmesidir. Aadaki main ilevini inceleyin:
#include <stdio.h>
int main()
{
int x;
printf("bir sayi girin: ");
scanf("%d", &x);
if (x % 2 == 0);
printf("%d cift sayi!\n", x);
}
return 0;
Yukardaki main ilevinde, x % 2 == 0 ifadesi doru da olsa yanl da olsa printf ilevi
arlr. printf ars if deyiminin dndadr. if deyiminin doru ksmn bir bo deyimin
(null statement) oluturmas szdizim kurallarna kesinlikle uyan bir durumdur. Yazlan if
deyimi, gerekte "x ift ise bir ey yapma" anlamna gelir. Bilinli bir biimde yazlma
108/529
olasl yksek olmayan bu durum iin, derleyicilerin ou mantksal bir uyar iletisi
vermez.
Ayn hata, yanl ksm olan bir if deyiminin doru ksmnda yaplsayd bir szdizim hatas
oluurdu, deil mi?
if (x > 5);
printf("doru!\n");
else
printf("yanl\n");
else anahtar szcnn, bir if deyimine bal olarak kullanlmas gerektiini biliyorsunuz.
Yukardaki kod parasndaki if deyimi, doru ksm "hibir ey yapma" anlamna gelen bir
if deyimidir. Dolaysyla, else anahtar szc hibir if deyimine balanmam olur. Bu da
bir szdizim hatasdr. nk bir if deyimine balanmayan, bir else olamaz.
Tabi ki bir if deyiminin doru ya da yanl ksmn bir bo deyim (null statement)
oluturabilir. Bu durumda okunabilirlik asndan, bu bo deyimin bir tab ieriden
yazlmas, bo deyimin bilinli olarak yerletirildii konusunda gl bir izlenim verir.
if ((val = getval()) != 0)
;
Sk yaplan baka bir yanllk, if ayrac iinde karlatrma ileci (==) yerine atama
ilecinin (=) kullanlmasdr.
/***/
if (x == 5)
printf("eit\n");
/***/
Yukardaki if deyiminde, x deikeninin deeri 5'e eitse printf ilevi arlyor.
Karlatrma ilecinin yan etkisi yoktur. Yani yukardaki if ayrac iinde x deikeninin
deeri, 5 deimezi ile yalnzca karlatrlyor, deitirilmiyor. Oysa karlatrma
ilecinin yerine yanllkla atama ileci kullanlrsa:
/***/
if (x = 5)
printf("eit\n");
/***/
Atama ileci, atama ilecinin sa tarafndaki ifadenin deerini retir, if ayrac iindeki
ifadenin deeri 5 olarak hesaplanr. 5, sfr d bir deer olduundan, x deikeninin
deeri ne olursa olsun, printf ilevi arlr. Atama ilecinin yan etkisi olduundan, x
deikenine de if deyiminin yrtlmesi ile 5 deeri atanr.
C derleyicilerinin ou, if ayrac iindeki ifade yaln bir atama ifadesi ise, durumu
pheyle karlayarak, mantksal bir uyar iletisi verir. rnein Borland derleyicilerinde
tipik bir uyar iletisi aadaki gibidir:
warning : possibly incorrect assignment! (muhtemelen yanl atama!)
Oysa if ayrac iinde atama ileci bilinli olarak da kullanlabilir:
if (x = func())
m = 20;
Bilinli kullanmda, derleyicinin mantksal uyar iletisinin kaldrlmas iin, ifade aadaki
gibi dzenlenebilir:
109/529
if ((x = func()) != 0)
m = 20;
Yukardaki rnekte olduu gibi, atama ilecinin rettii deer, ak olarak bir
karlatrma ilecine terim yaplrsa, derleyiciler bu durumda bir "mantksal uyar" iletisi
vermez.
ok yaplan baka bir hata da, if deyiminin doru ya da yanl ksmn bloklamay
unutmaktr. Yani bir bileik deyim yerine yanllkla yaln deyim yazlr.
if (x == 10)
m = 12;
k = 15;
Yukardaki if deyiminde yalnzca
m = 12;
deyimi if deyiminin doru ksmn oluturur.
k = 15;
deyimi, if deyimi dndadr. Bu durum genellikle programcnn, if deyiminin doru ya da
yanl ksmn nce yaln bir deyimle oluturmasndan sonra, doru ya da yanl ksma
ikinci bir yaln deyimi eklerken, bloklamay unutmas yznden oluur.
Kodun yazl biiminden de, if deyiminin doru ksmnn, yanllkla bloklanmad
anlalyor. Dorusu aadaki gibi olmalyd:
if (x == 10) {
m = 12;
k = 15;
}
Aadaki if deyimi ise, yine if anahtar szc ile elenmeyen bir else anahtar szc
kullanld iin geersizdir:
if ( x == 10)
m = 12;
k = 15;
else
/* Geersiz */
y = 20;
Bu tr yanllklardan saknmak iin baz programclar, if deyiminin doru ya da yanl
ksm yaln deyimden olusa da, bu basit deyimi bileik deyim olarak yazarlar:
if (x > 10) {
y = 12;
}
else {
k = 5;
}
Yukardaki rnekte if deyiminin doru ya da yanl ksmna baka bir yaln deyimin
eklenmesi durumunda szdizim hatas ya da bir yanllk olumaz. Ancak gereksiz
bloklamadan kanmak okunabilirlik asndan daha dorudur.
if ayrac iinde, bir deerin belirli bir aralkta olup olmadnn snanmak istendiini
dnn:
110/529
Snama levleri
bool veri tr, C'nin doal veri trlerinden olmad iin, C dilinde yazlan snama
ilevleri, yani bir soruya yant veren ilevler ounlukla int trne geri dner.
[C99 standartlaryla bool tr de doal veri tr olarak eklenmitir. C99 standartlar ile C diline gre _bool
anahtar szc eklenmitir.]
rnein bir tamsaynn asal say olup olmadn snayan bir ilev yazldn dnelim.
levin parametrik yaps aadaki gibi olur:
int isprime(int val);
Snama ilevlerinin geri dn deerlerinde yaygn olarak kullanlan anlama yledir:
Eer ilev, sorulan soruya doru ya da olumlu yant veriyorsa, 0 d herhangi bir deerle
geri dner. Sorulan sorunun ya da yaplan snamann sonucu, olumsuz ya da yanl ise,
ilev 0 deeriyle geri dner. Bu durum snama ilevini aran kod parasnn iini
kolaylatrr, aadaki gibi kalp kodlarn yazlmasna olanak verir:
Snamann olumlu sonulanmas durumunda bir i yaplacaksa aadaki gibi bir deyim
yazlabilir:
if (isprime(val))
deyim;
Snamann olumsuz sonulanmas durumunda bir i yaplacaksa
if (!isprime(val))
deyim;
yazlabilir.
Aadaki program inceleyin:
111/529
#include <stdio.h>
#include <ctype.h>
int main()
{
int ch;
printf("bir karakter girin : ");
ch = getchar();
if (isupper(ch))
printf("%c buyuk harf!\n", ch);
else
printf("%c buyuk harf degil!\n", ch);
}
return 0;
isupper, kendisine kod numaras gnderilen karakterin, byk harf karakteri olup
olmadn snayan standart bir ilevdir. Eer kod numarasn ald karakter byk harf
ise ilev, sfrdan farkl bir deere geri dner. Byk harf deil ise, ilevin geri dn
deeri 0'dr. Bu durumda main ilevinde yer alan if deyiminin koul ifadesi "ch byk harf
ise" anlamna gelir, deil mi? Koul ifadesi
if (!isupper(ch))
biiminde yazlsayd, bu "ch byk harf deil ise" anlamna gelirdi.
Aada, bir yln artk yl olup olmadn snayan isleap isimli bir ilev tanmlanyor. 4'e
tam blnen yllardan, 100'e tam blnmeyenler ya da 400'e tam blnenler artk
yldr:
#include <stdio.h>
int isleap(int y)
{
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}
int main()
{
int year;
printf("bir yil girin: ");
scanf("%d", &year);
if (isleap(year))
printf("%d yili artik yildir!\n", year);
else
printf("%d yili artik yil degildir!\n", year);
return 0;
}
112/529
isalnum
isspace
ispunct
isprint
isgraph
iscntrl
Geri Dn Deeri
Alfabetik karakterse doru, deilse yanl.
Byk harf ise doru, deilse yanl.
Kk harf ise doru, deilse yanl.
Saysal bir karakterse doru, deilse yanl.
Onaltlk say sistemi basamak simgelerinden birini gsteren bir karakterse,
yani 0123456789ABCDEFabcdef karakterlerinden biri ise doru, deilse
yanl.
Alfabetik ya da saysal bir karakterse doru, deilse yanl.
Boluk karakterlerinden biri ise (space, carriage return, new line, vertical
tab, form feed) doru, deilse yanl.
Noktalama karakterlerinden biriyse, yani kontrol karakterleri, alfanmerik
karakterler ve boluk karakterlerinin dndaki karakterlerden ise doru,
deilse yanl.
Ekranda grlebilen yani print edilebilen bir karakterse (space karakteri
dahil) doru, deilse yanl.
Ekranda grlebilen bir karakterse (space karakteri dahil deil) doru,
deilse yanl.
Kontrol karakteri ya da silme karakteri ise (ASCII setinin ilk 32 karakter ya
da 127 numaral karakter) doru, deilse yanl.
islower levi
113/529
isalpha levi
isalpha da standart bir ilevdir. Parametresine kod numaras aktarlan karakter alfabetik
karakterse, yani byk ya da kk harf ise ilev sfr d bir deere, alfabetik bir
karakter deilse sfr deerine geri dner.
int isalpha (int ch)
{
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
}
isdigit levi
isdigit, standart bir ilevdir. Parametresine kod numaras aktarlan karakter bir rakam
karakteri ise, ilev sfrdan farkl deer ile, rakam karakteri deilse sfr deeri ile geri
dner:
int isdigit (int ch)
{
return (ch >= '0' && ch <= '9');
}
isalnum levi
isalnum da standart bir ilevdir. Parametresine kod numaras aktarlan karakter alfabetik
karakter ya da bir rakam karakteri ise sfr d deere, aksi halde sfr deerine dner.
Aada bu ilev iki ayr biimde yazlyor:
#include <ctype.h>
int isalnum1(int ch)
{
return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z'
|| ch >= '0' && ch <= '9';
}
int isalnum2(int ch)
{
return isalpha(ch) || isdigit(ch);
}
isxdigit levi
isxdigit, bir karakterin, onaltlk say sistemine ait bir basamak simge olup olmadn
snayan standart bir ilevdir. Eer kod numarasn ald karakter
0123456789ABCDEFabcdef karakterlerinden biri ise ilev sfrdan farkl deere, bu
karakterlerden biri deil ise sfr deerine geri dner.
int isxdigit (int ch)
{
return ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F'
|| ch >= 'a' && ch <= 'f';
}
114/529
tolower levi
tolower standart bir C ilevidir. Parametresine kod numaras aktarlan karakter byk
harf ise, onun kk harf karlnn kod numarasyla geri dner. tolower ilevine byk
harf olmayan bir karakterin kod numaras aktarlrsa, ilev ayn deeri geri dndrr.
Aada bu ilev tanmlanyor:
#include <stdio.h>
int tolower (int ch)
{
if (ch >= 'A' && ch <= 'Z')
return ch - 'A' + 'a';
return ch;
}
int main()
{
char ch;
printf("bir karakter girin :");
ch = getchar();
printf("%c\n", tolower(ch));
}
return 0;
toupper levi
toupper, standart bir C ilevidir. Parametresine sra numaras aktarlan karakter, eer
kk harf ise, onun byk harf karlnn sra numarasyla geri dner. toupper ilevine
kk harf olmayan bir karakterin sra numaras aktarlrsa, ilev ayn deeri geri
dndrr. lev aada tanmlanyor:
int toupper(int ch)
{
if (ch >= 'a' && ch <= 'z')
return ch - 'a' + 'A';
return ch;
}
Aada iki saydan daha byk olanna geri dnen get_max2 ve saydan en byne
geri dnen getmax3 ilevi yazlyor:
int get_max2(int a, int b)
{
if (a > b)
return a;
return b;
}
115/529
max)
= b;
max)
= c;
return max;
#include <stdio.h>
int main()
{
int x, y, z;
printf("iki sayi girin : ");
scanf("%d%d", &x, &y);
printf("%d ve %d sayilarindan buyugu = %d\n", x, y, get_max2(x, y));
printf("uc sayi girin : ");
scanf("%d%d%d", &x, &y, &z);
printf("%d %d ve %d sayilarindan en buyugu = %d\n",x, y, z,
get_max3(x, y, z));
return 0;
}
Aadaki programda get_hex_char isimli bir ilev tanmlanyor. lev, kendisine
gnderilen 0 ile 15 arasndaki bir tamsaynn onaltlk say sistemindeki simgesi olan
karakterin sra numaras ile geri dnyor.
#include <stdio.h>
int get_hex_char(int number)
{
if (number >= 0 && number <= 9)
return ('0' + number);
if (number >= 10 && number <= 15)
return ('A' + number - 10);
return -1;
}
int main()
{
int number;
printf("0 ile 15 arasinda bir sayi girin : ");
scanf("%d", &number);
printf("%d = %c\n", number, get_hex_char(number));
return 0;
}
Aadaki programda get_hex_val isimli bir ilev tanmlanyor. lev, kendisine kod
numaras gnderilen, onaltlk say sisteminde bir basamak gsteren simgenin, onluk say
116/529
sistemindeki deerine geri dnyor. leve gnderilen karakter, onaltlk say sistemine
ilikin bir karakter deilse, -1 deeri dndrlyor.
#include <stdio.h>
#include <ctype.h>
int get_hex_val(int ch)
{
ch = toupper(ch);
if (isdigit(ch))
return ch - '0';
if (ch >= 'A' && ch <= 'F')
return ch - 'A' + 10;
return -1;
}
int main()
{
char hex;
printf("hex digit gsteren bir karakter girin: ");
hex = getchar();
printf("%c = %d\n", hex, get_hex_val(hex));
return 0;
}
Aadaki programda change_case isimli bir ilev tanmlanyor. change_case ilevi,
kendisine gnderilen karakter kk harf ise, bu karakteri byk harfe dntryor,
byk harf ise karakteri kk harfe dntryor. Eer harf karakteri deilse ilev,
karakterin kendi deeriyle geri dnyor.
#include <stdio.h>
#include <ctype.h>
int change_case(int ch)
{
if (isupper(ch))
return tolower(ch);
}
return toupper(ch);
int main()
{
int c;
printf("bir karakter girin : ");
c = getchar();
c = change_case(c);
putchar(c);
}
return 0;
117/529
#include <stdio.h>
#include <math.h>
int main()
{
double a, b, c;
double delta;
printf("denklemin katsayilarini girin\n");
printf("a = ");
scanf("%lf", &a);
printf("b = ");
scanf("%lf", &b);
printf("c = ");
scanf("%lf", &c);
delta = b * b - 4 * a * c;
if (delta < 0)
printf("denkleminizin gercek koku yok\n");
else if (delta == 0) {
printf("denkleminizin tek gercek koku var\n");
printf("kok = %lf\n", -b / (2 * a));
}
else {
double kokdelta = sqrt(delta);
printf("denkleminizin 2 gercek koku var\n");
printf("kok 1 = %lf\n", (-b + kokdelta) / (2 * a));
printf("kok 2 = %lf\n", (-b - kokdelta) / (2 * a));
}
}
return 0;
118/529
LEV BLDRMLER
Derleme ilemi, derleyici tarafndan kaynak kod iinde yukardan aaya doru yaplr.
Derleme aamasnda derleyici, bir ilev ars ile karlatnda, arlan ilevin geri
dn deerinin trn bilmek zorundadr. Bir ilevin geri dn deerinin tr, geri
dn deerinin hangi CPU yazmacndan (registers) alnacan belirler. Programn doru
almas iin derleme zamannda bu bilginin elde edilmesi zorunludur.
Eer arlan ilevin tanm, aran ilevden daha nce yer alyorsa, derleyici derleme
ilemi srasnda ilev ar ifadesine gelmeden nce, arlan ilevin geri dn deeri
tr hakknda zaten bilgi sahibi olur. nk derleme ilemi yukardan aa doru yaplr.
Aadaki rnei inceleyin:
#include <stdio.h>
double get_val(double x, double y)
{
return x * y / (x + y);
}
int main()
{
double d;
d = get_val(4.5, 7.3);
printf("d = %lf\n", d);
}
return 0;
Yukardaki rnekte get_val ilevi, kendisini aran main ilevinden daha nce
tanmlanyor. ar ifadesine gelmeden nce derleyici, get_val ilevinin geri dn deeri
trn zaten bilir.
arlan ilevin tanm aran ilevden daha sonra yaplrsa, derleyici ilev ar ifadesine
geldiinde, arlan ilevin geri dn deerinin trn bilemez. Bu sorunlu bir
durumdur:
#include <stdio.h>
int main()
{
double d;
//ilevin geri dn deerininin tr bilinmiyor.
d = get_val(4.5, 7.3);
printf("d = %lf\n", d);
return 0;
}
double get_val(double x, double y)
{
return x * y / (x + y);
}
Yukarda get_val ilevi, main ilevi iinde arlyor. Fakat get_val ilevinin tanm,
kaynak kod iinde main ilevinden daha sonra yer alyor. Derleyici, derleme zamannda
get_val ilevinin ars ile karlatnda, bu ilevin geri dn deerinin trn bilmez.
119/529
lev bildirimi, derleyiciye bir ilev hakknda bilgi veren bir deyimdir. Derleyici, ilev
arsna ilikin kodu buradan ald bilgiye gre retir. Ayrca derleyici, ald bu bilgiyle,
baz kontroller de yapabilir. Yapt kontroller sonucunda hata ya da uyar iletileri
reterek olas yanllklar engeller.
121/529
biiminde arlmas durumunda bir szdizim hatas ortaya kard. Gemie doru
uyumluluu salamak iin yle bir karar alnd. Eer ilevin parametre deikeni yoksa
bildirimde parametre ayracnn iine void anahtar szc yazlmaldr. Aadaki
bildirimleri inceleyin:
double foo();
double func(void);
Standartlara gre foo ilevinin bildiriminden derleyici, foo ilevinin parametre
deikenleri hakknda bir bilginin verilmedii sonucunu karr ve ilev arsyla
karlatnda ileve gnderilen argmanlarn saysna ilikin bir kontrol yapmaz. Yani
byle bildirilen bir ilev, kurallara uygun bir ekilde istenen sayda bir argmanla
arlabilir.
func ilevinin bildiriminden derleyici, func ilevin parametre deikenine sahip olmad
sonucunu karr ve ilev arsyla karlatnda, ileve bir ya da daha fazla sayda
argman gnderildiini grrse, bu durumu derleme zaman hatas olarak belirler.
[C++ dilinde ise her iki bildirim de edeerdir. Yani ilev bildiriminde parametre ayracnn iinin bo
braklmasyla buraya void anahtar szcnn yazlmas arasnda bir fark yoktur]
Bir ilevin bildirimi, programn herhangi bir yerinde yaplabilir. Bildirimler global dzeyde
yaplmsa, yani tm bloklarn dnda yaplmsa, bildirildikleri yerden dosya sonuna
kadar olan alan iinde geerliliklerini srdrr. Sz konusu ilev arlmadan, ilevin
bildirimi yaplm olmaldr.
Ancak uygulamalarda ok az rastlanmasna karlk, ilev bildirimleri yerel dzeyde de
yaplabilir. Bu durumda bildirim ile, yalnzca bildirimin yaplm olduu bloa bilgi verilmi
olur. Baka bir deyile ilev bildirimi de, deiken tanmlamalar gibi, bilinirlik alan
kuralna uyar.
Genel olarak ilev bildirimleri programn en yukarsnda ya da programcnn tanmlad
balk dosyalarnn birinin iinde yaplr. Balk dosyalar (header files), ileride ayrntl
olarak ele alnacak.
122/529
arlan bir ilevin kaynak kodunun, ayn kaynak dosya iinde yer almas gibi bir
zorunluluk yoktur. Derleyici, bir ilev ars ile karlatnda, arlan ilevin kaynak
kodunu aramaz. rettii hedef dosya (object file) iine, balayc iin, ilgili ilevin
arldn belirten bir bilgi yazar. arlan ilevin derlenmi kodunu bulmak ve hedef
dosyalar uygun bir biimde birletirmek, balayc programn grevidir.
arlan bir ilevin tanmnn ayn kaynak dosyada olmadn dnelim. Bu durumda
arlan ilevin bildirimi yaplmamsa ne olur? C dilinde bu durum bir szdizim hatas
deildir.
C++ dilinde bu durum dorudan szdizim hatasdr. Derleyici, bir ilevin arlmasndan nce, sz konusu
ilevin tanmn ya da bildirimini mutlaka grmek zorundadr.
C dilinde derleyici, bildirimini grmedii bir ilevin geri dn deerini int trden kabul
ederek hedef dosyay retir. Eer kaynak kod iinde ilevin geri dn deeri
kullanlmamsa, ya da arlan ilevin geri dn deeri gerekten int trden ise bir
sorun kmaz. Ancak ilevin geri dn deeri kullanlmsa ve geri dn deeri int
trden deilse, bir alma zaman hatas sz konusudur. Aadaki rnei derleyicinizde
derleyerek altrn:
#include <stdio.h>
int main()
{
double d;
d = sqrt(9.);
printf("d = %lf\n", d);
}
return 0;
Yukardaki programn derlenmesi srasnda bir hata olumaz. Ancak derleyici, arlan
sqrt ilevinin geri dn deerinin int trden olduunu varsayarak kod retir. Oysa,
derlenmi sqrt ilevinin geri dn deeri double trdendir. alma zaman srasnda,
ilevin geri dndrd double trden deer yerine, derleyicinin rettii kod sonucunda,
int trden bir deer ekilmeye allr. Bu, programn alma zamanna ynelik bir
hatadr.
imdi de main ilevinden nce aadaki bildirimi ekleyerek program yeniden derleyerek
altrn:
double sqrt(double val);
Standart C ilevlerinin bildirimleri, sonu .h uzantl olan balk (header) dosyalar
iindedir. #include nilemci komutuyla ilgili balk dosyasnn kaynak koda eklenmesiyle,
aslnda standart C ilevlerinin de bildirimi yaplm olur. Zira nilemci programn kts
olan kaynak program, artk derleyiciye verildiinde, eklenmi bu dosyada ilevin bildirimi
de bulunur. phesiz, ilgili balk dosyasn kaynak koda eklemek yerine, standart C
ilevlerinin bildirimleri programc tarafndan da yaplabilir. Bu durumda da bir yanllk sz
konusu olmaz. Yukardaki rnekte arlan sqrt ilevinin bildirimi iki ekilde kaynak koda
eklenebilir:
i. lev bildiriminin bulunduu balk dosyasnn, bir nilemci komutuyla kaynak koda
eklenmesiyle:
#include <math.h>
ii) levin bildirimi dorudan yazlabilir:
123/529
lev bildirimlerin ana amac, yukarda da belirtildii gibi, derleyiciye ilevin geri dn
deeri tr hakknda bilgi vermektir. Ancak ilev bildirimlerinde ilev parametrelerinin
trleri belirtilmise, derleyici prototip bildirimindeki parametre deikeni saysn ilev
ar ifadesindeki ileve gnderilen argman says ile karlatrr. rnein:
float process(float, float);
biiminde bir bildirim yazldnda eer process ilevi eksik ya da fazla argman ile
arlrsa derleme zamannda hata oluur.
x = process(5.8);
y = process(4.6, 7.9, 8.0)
Ayn trden geri dn deerine sahip ilevlerin bildirimi, virgllerle ayrlarak yazlabilir,
ama byle bir bildirim biimi, programclar tarafndan genel olarak pek tercih edilen bir
durum deildir.
double func1(int), func2(int, int), func3(float);
Yukardaki bildirim geerlidir. Byle bir bildirimle, derleyiciye func1, func2, func3
ilevlerinin hepsinin geri dn deerinin double trden olduu bilgisi verilir.
lev bildirimleri, deiken tanmlamalaryla da birletirilebilir. Bu da okunabilirlik
asndan tercih edilen bir durum deildir.
long func1(int), long func2(void), x, y;
Yukardaki bildirim deyimi ile func1 ve func2 ilevlerinin bildirimi yaplrken, x ve y
deikenleri tanmlanyor.
Bir ilevin bildiriminin yaplm olmas, o ilevin tanmlamasn ya da arlmasn zorunlu
klmaz. Bildirimi yaplan bir ilevi tanmlamamak hata oluturmaz.
Bir ilevin bildirimi birden fazla kez yaplabilir. Bu durum, bir derleme zaman hatas
oluturmaz. Ama yaplan bildirimler birbirleriyle elimemelidir.
Kaynak dosya iinde ayn ileve ilikin bildirimlerin farkl yerlerde, aadaki biimlerde
yapldn dnelim:
int func (int, int);
func (int, int);
int func(int x, int y);
func(int number1, int number2);
Yukardaki bildirimlerinin hibirinde bir eliki sz konusu deildir. lev parametre
deikenlerinin isimleri iin daha sonraki bildirimlerde farkl isimler kullanlmas bir eliki
yaratmaz. nk bu isimlerin bilinirlik alan (scope), yalnzca bildirimin yapld ayracn
iidir. Ancak aadaki farkl bildirimler geersizdir.
124/529
C ve C++ dillerinde bir proje, ou zaman birden fazla kaynak dosyadan, yani modlden
oluur. Kaynak dosyalarn ounda, baka kaynak dosyalar iinde tanmlanan ilevler
arlr. Yani ou zaman kaynak dosyalar arasnda bir hizmet alma verme ilikisi vardr.
Baka modllere hizmet verecek bir modl, iki ayr dosya eklinde yazlr. Dosyalardan
biri kodlama (implementation) dosyasdr. Bu dosyann uzants .c dir. Bu dosya
tarafndan dier modllere sunulan hizmetlere ilikin bildirimler, .h uzantl baka bir
dosyaya yerletirilir. Bu dosyaya balk dosyas (header file) denir. Dier modllere
hizmet verecek ilevlerin bildirimleri, balk dosyas iine yerletirilmelidir.
Hizmet alan kodlama dosyas, hizmet veren modln balk dosyasnn ieriini, kendi
dosyasna #include nilemci komutuyla ekler. Bylece bildirim yaplm olur. Bu durum
"nilemci komutlar" konusunda yeniden ele alnacak.
125/529
126/529
TR DNMLER
Bilgisayarlarn aritmetik ilemleri gerekletirmesinde bir takm kstlamalar sz
konusudur. Bilgisayarlarn aritmetik bir ilemi gerekletirmesi iin genellikle ileme
sokulan terimlerin uzunluklarnn ayn olmas, yani bit saylarnn ayn olmas ve bellekte
ayn formatta ifade edilmeleri gerekir. rnein ilemci 16 bit uzunluunda iki tam sayy
dorudan toplayabilir ama 16 bit uzunluunda bir tam say ile 32 bit uzunluundaki bir
gerek sayy dorudan toplayamaz.
C programlama dili, deiik trlerin ayn ifade iinde bulunmalarna izin verir. Yani tek bir
ifadede bir tamsay trnden deiken, float trden bir deimez ya da char trden bir
deiken birlikte yer alabilir. Bu durumda C derleyicisi, bunlar herhangi bir ileme
sokmadan nce, bilgisayar donanmnn ifadeyi deerlendirebilmesi iin uygun tr
dnmlerini yapar.
rnein 16 bitlik int trden bir deerle 64 bitlik double trden bir deer toplandnda,
nce 16 bitlik int trden deer, 64 bit uzunluunda double trden bir deer olarak ifade
edilir, daha sonra toplama ilemi gerekletirilir. Yine 16 bitlik bir int trden bir deerle
64 bitlik bir double trden bir deer arpldnda derleyici, nce int trden deeri 64
bitlik double trden bir deere dntrr. Bu tr dnm daha karmaktr, nk int
ve double trden deerler bellekte farkl biimlerde tutulur.
Bu tr dnmler, programcnn bir kod yazmasna gerek duyulmakszn otomatik olarak
gerekletirilir. Byle dnmlere, otomatik tr dnmleri (implicit type conversions)
diyeceiz. Dier taraftan C dili, programcya herhangi bir ifadeyi, bir ile kullanarak
baka bir trden ele alma olana da verir. Programc tarafndan yaplan byle tr
dnmlerine bilinli tr dnmleri (explicit type conversions/type casts) diyeceiz.
nce otomatik tr dnmlerini inceleyelim. Otomatik tr dnmleri ne yazk ki
karmak yapdadr ve iyi renilmemesi durumunda programlarda hatalar kanlmazdr.
C'de 11 ayr doal veri tr olduunu biliyorsunuz. Herhangi bir hataya neden olmamak
iin bunlarn her trl ikili bileimi iin nasl bir otomatik tr dnm yaplacan ok
iyi bilmek gerekir.
127/529
Yukardaki rnekte arlan sqrt ilevine gnderilen argman olan number deikeninin
tr, arlan ilevin parametre deikeninin trnden farkldr. Bu durumda bir tr
dntrme ilemi yaplr.
4. Bir return ifadesinin tr ile ilgili ilevin geri dn deerinin tr ayn deilse:
double func(int val)
{
/****/
return val;
}
Yukarda tanmlanan func isimli ilevin iinde yer alan return ifadesinin tr int iken,
ilevin bildirilen geri dn deeri tr double trdr. Bu durumda da, derleyici
tarafndan bir otomatik tr dntrme ilemi yaplr.
3. ve 4. maddeler de bir atama ilemi olarak dnlebilir. lev ar ifadesindeki
argmanlar, parametre deikenlerine kopyalanarak, geirilir. Yani rtl bir atama
ilemi sz konusudur. Yine return ifadeleri de aslnda ilevlerin geri dn deerlerini
tutacak geici nesnelere kopyalanrlar.
128/529
return 0;
129/529
ifadesinde pow ilevinin geri dn deeri double trden olduu iin, int trden olan i
deikeni de, ilemin yaplabilmesi iin geici bir blgede double trnde ifade edilerek
ileme sokulur.
Atama Tr Dnmleri
Bu tr dnmlerin ok basit bir kural vardr: Atama ncesinde, atama ilecinin sa
tarafndaki ifade, atama ilecinin sol tarafndaki nesnenin trnde ifade edilir:
Kk trlerin byk trlere dntrlmesinde bilgi kayb sz konusu deildir. rnein:
double leftx;
int righty = 5;
leftx = righty;
Yukardaki rnekte righty deikeninin tr int'tir. nce double tre otomatik dnm
yaplr, daha sonra double trnde ifade edilen righty deikeninin deeri leftx
deikenine atanr.
Aada 16 bitlik sistemler iin baz rnekler veriliyor:
TR
int
char
int
char
desimal
138
'd' (100)
-56
'\x95' (07)
unsigned int 45678
char
'0' (48)
hex
0x008A
0x64
0xFFC8
0x95
dntrlecek tr
long int
int
long int
int
hex
0x0000008A
0x0064
0xFFFFFFFC8
0xFF95
desimal
138L
100
-56L
-107
0xB26E
0x30
long int
long int
0X0000B26EL
0x00000030L
45678
30L
Negatif olan bir tamsay, kk trden byk tre dntrldnde saynn yksek
anlaml bitleri, negatifliin korunmas amacyla 1 biti ile beslenir.
Derleyici tarafndan yaplan atama tr dnmlerinde, atama ncesi, byk trn kk
tre dntrlmesi durumunda bilgi kayb sz konusu olabilir.
Aadaki basit kurallar verilebilir:
Eer atama ilecinin her iki taraf da tam say trlerinden ise (char, short, int, long),
atama ilecinin sa tarafnn daha byk bir trden olmas durumunda bilgi kayb olabilir.
Bilgi kayb ancak, atama ilecinin sa tarafndaki deerin, sol taraftaki trn snrlar
iinde olmamas durumunda sz konusu olur. Bilgi kayb, yksek anlaml byte'larn
kaybolmas eklinde ortaya kar. rnek:
long m = 0x12345678;
int y = m;
printf ("m = %x\n", m);
Yukardaki rnekte int trden olan y deikenine long trden bir deikenin deeri
atanmtr. Kodun 16 bitlik bir sistemde, rnein DOS altnda altn dnyoruz.
DOS altnda int tr iin say snrlar 32768 +32767 deerleridir. Bu saylar da, iki
byte'lk bir alan iin iaretli olarak yazlabilecek en byk ve en kk deerlerdir.
Onaltlk say sisteminde her bir basamak 4 bite ve her iki basamak 1 byte alana karlk
gelir. Dolaysyla 0x12345678 says 8 hex basamak, yani 4 byte uzunluunda bir saydr.
Oysa atamann yaplaca nesne int trdendir ve bu tr max. 4 hex basamak (2 byte)
uzunlukta olabilir. Bu durumda m deikenine ilikin deerin yksek anlaml 2 byte' yani
(4 hex basma) yitirilir. Atama ileminden sonra, printf ileviyle, y deikeninin deeri
5678 olarak yazdrlr.
130/529
Atama ilecinin sa terimi, bir gerek say trnden ise(float, double, long double) ve sol
terimi tam say trnden ise nce gerek say deerinin ondalk ksm kaybedilir. Eer
gerek saydan elde edilen tamsay ksm, atamann yapld tamsay trnden ifade
edilemiyorsa, bu durum tanmsz davrantr (undefined behaviour). Bu durumun
olutuu kodlardan kesinlikle kanmak gerekir. Ama derleyicilerin hemen hepsi, bu
durumda aadaki ekilde tr dnm yapar:
Atama ilecinin sa terimi olan gerek say bir ondalk ksm ieriyorsa, nce ondalk ksm
kaybedilir. Ondalk ksm kaybedildikten sonra kalan tamsay deer, eer sol terimin
trnn snrlar iinde kalyorsa daha fazla bir bilgi kayb olmaz, fakat sol taraf trnn
snrlar alyorsa fazladan bir bilgi kayb daha olur ve bu kez yksek anlaml byte'lar
kaybedilir. rnek:
#include <stdio.h>
int main()
{
double y = 234.12;
int x;
x = y;
printf("x = %d\n", x); /* x deikenine 234 deeri atanr*/
y = 7689523345.347;
x = y;
/* Yanl */
printf("x = %d\n", x);
}
return 0;
return 0;
131/529
ch == 135
karlatrma ileminde char trden olan ch deikeni, karlatrma ilemi ncesi signed
int trne ykseltilir. lem ncesi tamsayya ykseltme sonucu, yksek anlam byte'ler
1 bitleriyle beslenir. nk ch negatif bir deere sahiptir. Karlatrma ilemi ncesinde
ch'nin deeri
1111 1111 1000 0111
olur. Oysa karlatrma ilecinin sa terimi olan 135 deeri, int trden bir deimezdir.
Yani aslnda karlatrlan deerler aadaki gibi olur:
1111 1111 1000 0111
0000 0000 1000 0111
Karlatrma yanl olarak sonulanr.
Tamsayya Ykseltme
Daha nce de akland gibi tamsayya ykseltme (integral promotion), bir ifade iinde
bulunan char, unsigned char, short, unsigned short trlerinin, ifadenin derleyici
tarafndan deerlendirilmesinden nce, otomatik olarak int trne dntrlmeleri
anlamna gelir.
Peki dnm, signed int trne mi, unsigned int trne mi yaplr?
Genel kural udur: Tr dnmne urayacak terimin deeri int trnde ifade
edilebiliyorsa int, edilemiyorsa unsigned int trne dnm yaplr.
rnein unsigned short ve int trlerinin ayn uzunlukta olduu DOS iletim sisteminde
unsigned short tr, tamsayya ykseltilirken unsigned int trne dntrlr.
Eer tam sayya ykseltilecek deer, signed char, unsigned char ya da signed short
trlerinden ise, dnm signed int trne yaplr.
Bilgi kayb ile ilgili u hususu da gz ard etmemeliyiz. Baz durumlarda bilgi kayb tr
dnm yapld iin deil yaplmad iin oluur. Snr deer tamalar, bu duruma iyi
bir rnek olabilir.
rnek: (DOS altnda altmz dnelim)
long x = 1000 * 2000;
Yukardaki kod ilk bakta normal gibi grnyor. Zira arpma ileminin sonucu olan
2000000 deeri DOS altnda signed long tr say snrlar iinde kalr. Oysa bilgi kayb
atama ileminden nce gerekleir. 1000 ve 2000 int trden deimezlerdir, ileme
sokulduklarnda arpma ilecinin de rettii deer int trden olur. Bu durumda 2 byte
uzunlukta olan int tr, 2000000 deerini tutamayaca iin yksek anlaml byte
kaybedilir. 2000000 onaltlk say sisteminde 0x1E8480 olarak gsterilebilir. Yksek
anlaml byte kaybedilince ilem sonucu, 0x8480 olur. 0x8480 negatif bir saydr. nk
iaret biti 1'dir. kiye tmleyenini alrsak
0x8480
ikiye tmleyeni
(0x7B80 = 31616)
Grld gibi ilem sonucu retilecek deer 31616 dir. Bu durumda x deikeninin
tr long tr de olsa, atanacak deer 31616 olur.
Daha nce sylendii gibi, bir ileve gnderilen argmanlarla, bu argmanlar tutacak
ilevin parametre deikenleri arasnda tr fark varsa otomatik tr dnm gerekleir
132/529
return 0;
Yukardaki rnekte main ilevi iinde arlan func ilevine argman olarak, int trden
olan a ve b deikenlerinin deerleri gnderiliyor. lev tanm ar ifadesinden nce yer
ald iin int trden olan a ve b deikenlerinin deerleri, double trne dntrlerek
func ilevinin parametre deikenleri olan x ve y deikenlerine aktarlr. func ilevinin
main ilevinden sonra tanmlanmas durumunda, otomatik tr dnmnn
yaplabilmesi iin, ilev bildirimi ile derleyiciye parametre deikenlerinin trleri hakknda
bilgi verilmesi gerekir.
#include <stdio.h>
double func(double x, double y);
int main()
{
int a, b;
/***/
func(a, b);
}
return 0;
133/529
Tr Dntrme leci
Tr dntrme ileci (typecast operator) ile bir ifade bir ileme sokulmadan nce baka
bir trden ifade edilebilir. Tr dntrme ileci, nek konumunda bulunan tek terimli bir
iletir.
le, bir ayra ve ayra iine yazlan bir tr bilgisinden oluur:
(double)x
lecin rettii deer, terimi olan ifadenin ayra iindeki trden ifade edilmi deeridir.
Tr dntrme ileci de, dier tm tek terimli ileler gibi, ile ncelik tablosunun ikinci
ncelik seviyesinde bulunur.
Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
int x = 10;
int y = 4;
double z;
z = (double)x / y;
printf("z = %lf\n", z);
return 0;
}
Yukardaki programda
z = (double)x / y
ifadesinde nce tr dntrme ileci deer retir. Tr dntrme ilecinin rettii
deer, x nesnesinin double trde ifade edilmi deeridir. Bu durumda blme ilecine sra
geldiinde blme ilecinin terimi double trden 10 deeri olur. Bu kez de otomatik tr
dnm ile blme ilecinin sa terimi double trne dntrlerek blme ilemi
double trnde yaplr. Bu durumda blme ileci 2.5 deerini retir.
phesiz ifade aadaki biimde yazlsayd yine bilgi kayb olumazd:
z = x /(double)y
Ancak ifade aadaki gibi yazlsayd:
z = (double) (x / y)
bu durumda tr dntrme ilecinin terimi (x / y) ifadesi olurdu. Bu da bilgi kaybn
engellemezdi.
Bir verinin istenerek kaybedilmesi durumunda okunabilirlik asndan, otomatik tr
dnm yerine, tr dntrme ileci ile bilinli bir dnm yaplmaldr.
int i;
double d;
/***/
i = d;
134/529
double trden olan d deikeninin deerinin int trden i deikenine atanmas gvenilir
bir davran gstermez. Atama sonunda, en iyi olaslkla i deikenine d nin deerinin
yalnzca tam say ksm atanr. Byle bir kodu okuyanlar bu atamann yanllkla yapld
izlenimini edinirler. Derleyicilerin ou da olas bilgi kaybn uyar iletisiyle bildirir. Bu
atamann bilinli bir ekilde yaplmas durumunda tr dntrme ileci kullanlmaldr:
int i;
double d;
/***/
i = (int)d;
Aadaki programda, klavyeden girilen bir gerek say tam sayya yuvarlanyor. Girilen
deerin ondalk ksm .5'ten daha bykse say yukarya, .5'ten daha kkse say
aaya yuvarlanyor:
#include <stdio.h>
int main()
{
double d;
int x;
printf("bir gercek sayi girin : ");
scanf("%lf", &d);
if (d > 0)
x = d + .5;
else
x = d - .5;
printf("x = %d\n", x);
}
return 0;
135/529
DNG DEYMLER
Bir program parasnn yinelemeli olarak altrlmasn salayan kontrol deyimlerine
"dng deyimi" (loop statement) denir. C dilinde 3 ayr dng deyimi vardr:
while dng deyimi
do while dng deyimi
for dng deyimi
Bunlardan en fazla kullanlan, for dng deyimidir. for dng deyimi, yalnzca C dilinin
deil, tm programlama dillerinin en gl dng yapsdr. while ya da do while dng
deyimleri olmasa da, bu dngler kullanlarak yazlan kodlar, for dngsyle yazlabilir.
Ancak okunabilirlik asndan while ve do while dnglerinin tercih edildii durumlar
vardr.
main ilevinde yer alan while dngsn inceleyelim. Dng gvdesini bir bileik deyim
oluturuyor. i < 100 ifadesi doru olduu srece bu bileik deyim yrtlr. Yani printf
ilevi arlr, daha sonra i deikeninin deeri 1 artrlr. i deikeninin deeri 100
olduunda, kontrol ifadesi yanl olacandan dngden klr. Aadaki rnei
inceleyin:
137/529
#include <stdio.h>
int main ()
{
char ch = 'A';
while (ch <= 'Z')
putchar(ch++);
}
return 0;
Yukardaki main ileviyle ngiliz alfabesinin tm byk harf karakterleri srayla ekrana
yazdrlyor. ch isimli deikene nce 'A' deeri atandn gryorsunuz. Dng ch <=
'Z' ifadesi doru olduu srece dner. Dng gvdesini bu kez bir basit deyim
oluturuyor. Sonek konumundaki ++ ilecinin nesnenin kendi deerini rettiini
biliyorsunuz. Ancak ilecin yan etkisi nedeniyle ch deikeninin deeri 1 artrlyor. ch
deikeninin deeri 'Z' olduunda kontrol ifadesi halen dorudur. Ancak dngnn bir
sonraki turunda kontrol ifadesi yanl olduundan dngden klr.
while dng deyiminde dng gvdesindeki deyimin en az bir kez yaplmas gvence
altnda deildir. nce kontrol ifadesi ele alndndan, dngye ilk girite kontrol
ifadesinin yanl olmas durumunda, dng gvdesindeki deyim hi yrtlmez.
Kontrol fadeleri
return 0;
138/529
Virgl ilecinin sol terimi olan ifadenin daha nce yaplmasnn gvence altnda olduunu
biliyorsunuz. Yukardaki main ilevinde yer alan while dngsnn kontrol ifadesine
bakalm:
while (ch = getch(), toupper(ch) != 'Q')
nce virgl ilecinin sol terimi olan ifade yaplacana gre, getch ilevi arlr.
Klavyeden alnan karakterin kod numaras ch deikenine atanr. Daha sonra toupper
ilevinin arlmasyla, ch deikeni deerinin'Q' karakteri olup olmad snanr. Virgl
ilecinin rettii deerin, sa teriminin deeri olduunu anmsayn. Bu durumda
dngnn srdrlmesi hakknda sz sahibi olan ifade
toupper(ch) != 'Q'
ifadesidir. Yani dng ch 'Q' veya 'q' dnda byk harf karakteri olduu srece dner.
Kontrol ifadesini bir deiken de oluturabilir:
while (x) {
/***/
}
Yukardaki dng, x deikeni sfrdan farkl bir deere sahip olduu srece dner.
Kontrol ifadesi bir deimez de olabilir:
while (1) {
/***/
}
Yukardaki while deyiminde kontrol ifadesi olarak 1 deimezi kullanlyor. 1 sfr d bir
deer olduundan, yani kontrol ifadesi bir deikene bal olarak deimediinden, byle
bir dngden koul ifadesinin yanl olmasyla klamaz. Bu tr dnglere sonsuz dng
(infinite loops) denir. Sonsuz dngler programcnn bir hatas sonucu oluabildii gibi,
bilinli olarak, yani belirli bir amac gerekletirmek iin de oluturulabilir. while ayrac
iine 1 deimez deerinin olduu while dngs, bilinli olarak oluturulmu bir sonsuz
dng deyimidir.
Atama ilecinin kontrol ifadesi iinde kullanlmas da, sk rastlanan bir durumdur:
while ((val = get_value()) > 0) {
foo(val);
/***/
}
Yukardaki while dngsnde get_value ilevinin geri dn deeri, val isimli deikene
atanyor. Atama ileci ile oluturulan ifade ncelik ayrac iine alndn gryorsunuz.
Atama ilecinin rettii deer, nesneye atanan deer olduundan, byktr ilecinin sol
terimi yine get_value ilevinin geri dn deeridir. Bu durumda dng get_value
ilevinin geri dn deeri, 0'dan byk olduu srece dner. Dng gvdesi iinde
arlan foo ilevine val deerinin argman olarak gnderildiini gryorsunuz. foo ilevi,
get_value ilevinin geri dn deeri ile arlm olur.
break Deyimi
139/529
Bu biimde oluturulan deyime "break deyimi" (break statement) denir. break deyimi bir
dng deyiminin ya da switch deyiminin gvdesinde kullanlabilir. Bir dng deyiminin
yrtlmesi srasnda break deyimi ile karlaldnda, dngden klr, programn ak
dng gvdesi dndaki ilk deyim ile srer. Yani koulsuz olarak dngden klr.
Aadaki program inceleyin:
#include <stdio.h>
#include <math.h>
int main ()
{
int val;
while (1) {
printf("bir sayi girin : ");
scanf("%d", &val);
if (val < 0)
break;
printf("karekok %d = %lf\n", val, sqrt(val));
}
printf("donguden kld program sonlanyor!\n");
return 0;
}
Programda bilinli olarak bir sonsuz dng oluturuluyor. Dngnn her turunda val isimli
deikene klavyeden bir deer alnyor. Eer klavyeden 0'dan kk bir deer girilirse,
break deyimi ile dngden klyor.
break deyimi yalnzca bir dng deyiminin ya da switch deyiminin gvdesinde
kullanlabilir. Aadaki kod paras geersizdir:
if (x > 100) {
if (y < 200)
break;
/***/
}
continue Deyimi
140/529
Yukardaki main ilevinde bir sonsuz dng oluturuluyor. Dngnn her turunda getval
isimli ilevin geri dn deeri val deikeninde saklanyor. Eer val deikenine atanan
deer 0 ise break deyimiyle dngden klyor. Daha sonra yer alan if deyimi ile val
deerinin asal olup olmad snanyor. val'e atanan deer asal ise, dngnn kalan ksm
yrtlmyor, continue deyimiyle dngnn bir sonraki turuna geiliyor.
continue deyimi, zellikle dng iinde uzun if deyimleri olduunda, okunabilirlii
artrmak amacyla kullanlr.
while (k++ < 100) {
ch = getch();
if (!isspace(ch)) {
/* deyimler */
}
}
Yukardaki yazlan while dngs iinde, klavyeden getch ilevi ile ch deikenine bir
karakterin kod numaras alnyor. Klavyeden alnan karakter bir boluk karakteri deilse
deyimlerin yrtlmesi isteniyor. Yukardaki kod parasnn okunabilirlii, continue
deyiminin kullanlmasyla artrlabilir:
while (k++ < 100) {
ch = getch();
if (isspace(ch))
continue;
/* deyimler */
}
Baz programclar da continue deyimini dng gvdesinde yer alacak bir bo deyime
seenek olarak kullanrlar:
while (i++ < 100)
continue;
continue deyimi yalnzca bir dng deyiminin gvdesinde kullanlabilir. continue
deyiminin, dng dnda bir yerde kullanlmas geerli deildir.
Sk Yaplan Hatalar
while dng deyiminin gvdesinin yanllkla bo deyim yaplmas sk yaplan bir hatadr:
#include <stdio.h>
int main()
{
int i = 10;
Yukardaki dng while ayrac iindeki ifadenin deeri 0 olana kadar dner. printf ars
dng deyiminin gvdesinde deildir. while ayracn izleyen sonlandrc atom, dngnn
gvdesini oluturan deyim olarak ele alnr. Dngden kldnda ekrana 0 deeri yazlr.
Eer bir yanllk sonucu deil de, bilinli olarak while dngsnn gvdesinde bo deyim
(null statement) bulunmas isteniyorsa, okunabilirlik asndan bu bo deyim, while
ayracndan hemen sonra deil, alt satrda bir tab ieriden yazlmaldr.
141/529
while dng deyimiyle ilgili yaplan bir baka tipik hata da, dng gvdesini bloklamay
unutmaktr. Yani dng gvdesindeki deyimin bir bileik deyim olmas gerekirken,
yanllkla bir yaln deyim kullanlr:
#include <stdio.h>
int main()
{
int i = 1;
1'den 100'e kadar olan saylarn, aralarnda birer bolukla ekrana yazdrlmak istendiini
dnelim. Yukardaki while deyiminde i++ deyimi, yani dng deikeninin artrlmas
dngnn gvdesine ait deildir. Bu durumda i <= 100 ifadesi hep doru olacandan
sonsuz dng oluur ve ekrana srekli olarak 1 deeri yazlr.
if deyiminde olduu gibi while ayrac iinde de karlatrma ileci olan "==" yerine
yanllkla atama ileci "=" kullanlmas, yine sk yaplan hatadr:
while (x == 5) {
/***/
}
gibi bir dng, x deikeninin deeri 5 olduu srece dnerken aadaki deyim, bir
sonsuz dng oluturur:
while (x = 5) {
/***/
}
Dngnn her turunda x deikenine 5 deeri atanr. Atama ilecinin rettii deer olan
5, "doru" olarak yorumlanacandan, dng srekli dner.
return 0;
142/529
n bir pozitif tamsay olmak zere, n defa dnen bir while dngs oluturmak iin
while (n-- > 0)
ya da
while (n--)
kod kalplar kullanlabilir. Aada, bir tamsaynn belirli bir ssn hesaplayan power
isimli bir ilev yazlyor. levi inceleyin:
int power(int base, int exp)
{
int result = 1;
while (exp--)
result *= base;
return result;
}
lev iinde yazlan while dngs, exp deikeninin deeri kadar dner, deil mi? Bu
durumda base deikeni, exp kez kendisiyle arplm olur.
Bazen dng gvdesi bilinli bir ekilde bo deyim yaplr. Okunabilirlik asndan bu
durumda bo deyimin normal bir deyim gibi tablama kuralna uygun olarak yazlmas
tavsiye edilir. Aadaki program inceleyin:
#include <stdio.h>
#include <ctype.h>
#include <conio.h>
int main()
{
int ch;
printf("Evet mi Hayir mi? [e] [h] : ");
while ((ch = toupper(getch())) != 'E' && ch != 'H')
;
if (ch == 'E')
printf("evet dediniz!\n");
else
printf("hayr dediniz!\n");
}
return 0;
Yukardaki main ilevi iinde yazlan while dngs ile, kullanc klavyeden 'e', 'E', 'h',
'H' harflerinden birini girmeye zorlanyor. Dngy dikkatli bir ekilde inceleyin.
Dngnn kontrol ifadesi iinde "mantksal ve" ileci "&&" kullanlyor. "Mantksal ve"
ilecinin sol teriminin daha nce yaplmasnn gvence altnda olduunu anmsayn.
Standart olmayan getch ilevi ile, klavyeden bir karakter alnyor. Alnan karakterin sra
numaras, yani getch ilevinin geri dn deeri, standart toupper ilevine argman
olarak gnderiliyor. Bylece eer klavyeden kk harf karakteri girilmise byk harfe
dntrlm olur. toupper ilevinin geri dn deeri ch deikenine atanyor. Ayra
143/529
iinde yer alan ifadenin deeri, ch deikenine atanan deerdir. "Mantksal ve" ilecinin
sa tarafndaki ifadenin btn ile atama ilecinin rettii deerin de 'E' karakterine
eitsizlii sorgulanyor. ch deikenine atanan deer 'E' ise "mantksal ve" ilecinin
ikinci ksmna hi baklmaz, kontrol ifadesinin deeri yanl olarak yorumlanr. Bylece
dngden klr. ch deikenine atanan deerin 'H' olmas durumunda, && ilecinin sa
terimi deerlendirilir yani ch deikeninin deerinin 'H' karakterine eitsizlii sorgulanr.
Eer ch 'H' deerine eit ise kontrol ifadesi yine yanl olarak yorumlanr, dngden
klr. Bunun dndaki tm durumlarda, kontrol ifadesi doru olarak yorumlanacandan
dngnn dnmesi srer. Bir baka deyile, dngden klmas ancak klavyeden 'e',
'E', 'h', 'H' karakterlerinden birinin girilmesi ile mmkn olur.
Aada, bir tamsaynn ka basamakl olduu bilgisiyle geri dnen num_digit isimli bir
ilevin tanm yer alyor. Program derleyerek altrn:
#include <stdio.h>
int num_digit(int val)
{
int digit_counter = 0;
if (val == 0)
return 1;
while (val != 0) {
digit_counter++;
val /= 10;
}
return digit_counter;
int main()
{
int x;
printf("bir tamsayi girin :");
scanf("%d", &x);
printf("%d sayisi %d basamakl!\n", x, num_digit(x));
return 0;
}
Basamak saysn hesaplamak iin ok basit bir algoritma kullanlyor. Say, sfr elde
edilinceye kadar srekli 10'a blnyor. num_digit ilevinde, nce parametre deikeni
olan val in deerinin 0 olup olmad snanyor. Eer val 0 deerine eit ise, 1 deeri ile
geri dnlyor. 0 says da 1 basamakldr deil mi? Daha sonra oluturulan while
dngs val != 0 kouluyla dner. Yani val deikeninin deeri 0 olunca bu dngden
klr. Dngnn her turunda gvde iinde digit_counter deikeninin deeri 1 artrlyor.
Daha sonra val /= 10; deyimiyle val deikeni onda birine eitleniyor.
Aada bu kez kendisine gnderilen bir tamsaynn basamak deerlerinin toplam ile geri
dnen sum_digit isimli bir ilev tanmlanyor:
#include <stdio.h>
int sum_digit(int val)
{
int digit_sum = 0;
144/529
while (val) {
digit_sum += val % 10;
val /= 10;
}
}
return digit_sum;
int main()
{
int val;
printf("bir tamsayi girin :");
scanf("%d", &val);
printf("%d sayisinin basamaklari toplami = %d\n", val, sum_digit(val));
}
return 0;
sum_digit ilevinde, yine parametre deikeni olan val, bir dng iinde srekli 10'a
blnyor, val 0 oluncaya kadar dng gvdesindeki deyimler yrtlyor. Dng
gvdesi iinde
digit_sum += val % 10;
deyimi ile val deikeninin birler basama, deeri digit_sum deikenine katlyor.
Bylece dngden kldktan sonra digit_sum deikeni, dardan gnderilen saynn
basamaklar deerlerinin toplamn tutuyor.
Aada, kendisine gnderilen bir tamsaynn tersine geri dnen get_rev_num isimli bir
ilev yazlyor:
#include <stdio.h>
int get_rev_num(int val)
{
int rev_number = 0;
while (val) {
rev_number = rev_number * 10 + val % 10;
val /= 10;
}
return rev_number;
}
int main()
{
int val;
printf("bir tamsayi girin :");
scanf("%d", &val);
printf("%d sayisinin tersi = %d\n", val, get_rev_num(val));
}
return 0;
145/529
rev_number * 10 + val % 10
ifadesinin deeri atanyor.
leve gnderilen deerin 1357 olduunu dnelim:
rev_number
val
1357
135
75
13
753
7531
146/529
deyim;
while (ifade);
do while dngsnde kontrol ifadesi sondadr. while ayracndan sonra sonlandrc atom
bulunmaldr. Yani buradaki sonlandrc atom, dng deyiminin szdiziminin bir
parasdr. do while dngsnn yrtlmesi aadaki gibi olur:
do anahtar szcn izleyen deyim dngye girite bir kez yaplr, daha sonra while
ayrac iindeki kontrol ifadesine baklr. Kontrol ifadesi doru olduu srece dng
gvdesini oluturan deyim yaplr. do while dngsnn while dngsnden fark nedir?
while dngsnde dng gvdesindeki deyimin en az bir kez yaplmas gvence altnda
deildir. Ancak do while dngsnde kontrol sonda yapld iin gvdedeki deyim en az
bir kez yaplr.
Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
int val;
do {
printf("0 - 100 arasi bir deger girin : ");
scanf("%d", &val);
} while (val < 0 || val > 100);
printf("val = %d\n", val);
}
return 0;
main ilevinde do while dngs ile kullanc, 0 100 aralnda bir deer girmeye
zorlanyor. Eer girilen deer 0'dan kk ya da 100'den bykse, kullancdan yeni bir
deer isteniyor.
Daha nce while dngs kullanarak yazlan num_digit isimli ilev, bu kez do while
dngs ile yazlyor:
147/529
return digit_counter;
Aada tanmlanan print_ulam ileviyle bir tamsayya ilikin ulam serisi ekrana
yazdrlyor:
#include <stdio.h>
void print_ulam(int val)
{
printf("%d icin ulam serisi\n", val);
do {
printf("%d ", val);
if (val % 2 == 0)
val /= 2;
else
val = val * 3 + 1;
} while(val > 1);
printf("%d\n", val);
}
int main()
{
int x;
printf("bir sayi girin: ");
scanf("%d", &x);
print_ulam(x);
}
return 0;
148/529
return 0;
Programn ak for dng deyimine gelince, nce for ayrac iindeki birinci ifade ele alnr.
Yani i deikenine 0 deeri atanr.
imdi programn ak for ayracnn ikinci ksmna, yani kontrol ifadesine gelir ve i < 2
koulu sorgulanr. Kontrol ifadesinin deeri sfrdan farkl olduu iin, ifade mantksal
olarak doru kabul edilir. Bylece programn ak dng gvdesine geer. Dng
gvdesinin bir basit deyim tarafndan oluturulduunu gryorsunuz. Bu deyim yrtlr.
Yani ekrana i deikeninin deeri yazlarak imle alt satra geirilir.
Programn ak, bu kez for ayracnn nc ksmna gelir ve buradaki ifade ele alnr,
yani i deikeninin deeri 1 artrlr, i deikeninin deeri 1 olur.
kinci ifade yeniden deerlendirilir ve i < 2 ifadesi doru olduu iin bir kez daha dng
gvdesindeki deyim yrtlr.
Programn ak yine for ayracnn nc ksmna gelir ve buradaki ifade ele alnr, yani i
deikeninin deeri 1 artrlr. i deikeninin deeri 2 olur.
Programn ak yine for ayracnn ikinci ksmna gelir. Buradaki kontrol ifadesi yine
sorgulanr. i < 2 ifadesi, bu kez yanl olduu iin programn ak, dng gvdesine
girmez, dng gvdesini izleyen ilk deyimle srer. Yani ekrana:
sondeger = 2
yazlr.
Dng Deikenleri
for dngsnde bir dng deikeni kullanlmas gibi bir zorunluluk yoktur. rnein
aadaki dng, kurallara tamamen uygundur:
for (func1(); func2(); func3())
func4();
149/529
Yukardaki for dng deyimiyle, dngye girite func1 ilevi arlr. func2 ilevi sfr d
bir deere geri dndke dng gvdesindeki deyim yrtlr yani func4 ilevi arlr.
Kontrol ifadesine yeniden gelmeden, yani func4 ilevinin arlmasndan sonra bu kez
func3 ilevi arlr.
Aadaki for dng deyimiyle klavyeden 'x' karakteri girilmedii srece, alnan karakter
ekrana yazdrlyor:
#include <stdio.h>
#include <conio.h>
int main()
{
char ch;
for (ch = getch(); ch != 'x' ; ch = getch())
putchar(ch);
}
return 0;
Dng deikeninin tamsay trlerinden birinden olmas gibi bir zorunluluk yoktur. Dng
deikeni, gerek say trlerinden de olabilir:
#include <stdio.h>
int main()
{
double i;
for (i = 0.1; i < 6.28; i += 0.01)
printf("%lf ", i);
return 0;
}
Yukardaki dngde, double trden bir dng deikeni seiliyor. for ayracnn birinci
ksmnda dng deikenine 0.1 deeri atanyor. Ayracn nc ksmnda ise dng
deikeni 0.01 artrlyor. Dng, i deikeninin deerinin 6.28'den kk olmas
kouluyla dnyor.
Virgl ileci ile birletirilmi ifadelerin, soldan saa doru srayla ele alndn anmsayn.
for dnglerinin birinci ve nc ksmnda virgl ilecinin kullanlmasna sk rastlanr.
Aadaki dng deyimini inceleyin:
#include <stdio.h>
int main()
{
int i, k;
for (i = 1, k = 3; i * k < 12500; i += 2, k += 3)
printf("(%d %d)", i, k);
}
return 0;
150/529
Yukardaki for deyiminde, for ayracnn birinci ksmnda virgl ileci kullanlarak yazlan
ifade ile, i deikenine 1, k deikenine 3 deeri atanyor. Dng, i * k ifadesinin deeri
12500'den kk olduu srece dner. for ayracnn nc ksmnda i deikeninin
deeri 2, k deikeninin deeri 3 artrlyor.
for dng deyimi ayracnn birinci ksmnda bir ifade bulunmayabilir. Bu tamamen
kurallara uygun bir durumdur. 1'den 100'e olan kadar saylarn ekrana yazdrlmak
istendiini dnelim. Dng deikenine ilkdeer verme ilemi, for ayracnn birinci
ksmndan, for dngs dna alnabilir:
#include <stdio.h>
int main()
{
int i = 0;
C programclarnn ou bilinli bir ekilde sonsuz dng oluturmak istediklerinde for (;;)
kalbn yeler. Bu kalp while (1) kalbna edeerdir. kisi de sonsuz dng belirtir.
Sonsuz dng oluturmak iin for (;;) biimi while (1) biimine gre daha ok tercih
edilir.
#include <stdio.h>
int main()
{
int i = 0;
151/529
for (;;) {
if (i == 100)
break;
printf("%d ", i);
i++;
}
return 0;
}
imdi de, aadaki dng deyiminin yrtlmesiyle ekrana ne yazdrlacan kestirmeye
aln:
#include <stdio.h>
int main()
{
double d;
for (d = 1.5; d < 3,0; d += 0.1)
printf("%lf ", d);
return 0;
}
Ekrana hibir ey yazlmaz! Dngnn kontrol ifadesinin d < 3,0 olduunu gryorsunuz.
Gerek say deimezi yazarken '.' yerine yanllkla virgl karakteri kullanlm. Bu
durumda virgl ilecinin rettii deer, ikinci terim olan 0 deeridir. Kontrol ifadesi yanl
olarak yorumlanr bylece dng gvdesindeki deyim hi yrtlmez.
Bir dngnn gvdesi iinde continue deyiminin kullanlmas ile, gvde iinde geriye
kalan deyimlerin atlanarak dngnn bir sonraki turuna geilir. for dngs gvdesi
iinde continue deyimi ile karlaldnda, programn ak for ayracnn nc ifadesine
gelir ve bu ifade ele alnr.
Baz uygulamalarda, for dngsnn dng deikeni, bir bayrak grevi de grr. Bir for
dngs iinden, belirli bir koul olutuunda klmas gereksin:
for (i = 0; i < 100; ++i)
if (is_valid(i))
break;
Yukardaki dng deyiminin altrlmas sonucunda iki farkl durum sz konusudur. Eer
dngnn gvdesinde break deyimi yrtlrse, yani herhangi bir i deeri iin is_valid
ilevi sfr d bir deere geri dnerse, dng knda i deikeninin deeri, 100'den
152/529
int main()
{
int x, y;
int n = 5;
while (n--) {
printf("iki tamsayi girin : ");
scanf("%d%d", &x, &y);
printf("obeb = %d\n", obeb(x, y));
printf("okek = %d\n", okek(x, y));
}
return 0;
153/529
Aadaki programda, bir tamsay iin faktriyel deerini hesaplayan, fact isimli bir ilev
yazlyor. Program, int trnn 4 byte olduu bir sistemde derlenmeli:
#include <stdio.h>
int fact(int number)
{
int i;
int result = 1;
if (number == 0 || number == 1)
return 1;
for (i = 2; i <= number; ++i)
result *= i;
}
return result;
int main()
{
int k;
for (k = 0; k < 14; ++k)
printf("%2d! = %-10d\n", k, fact(k));
}
return 0;
154/529
#include <stdio.h>
int isprime(int number)
{
int k;
if (number == 0 || number == 1)
return 0;
if (number % 2 == 0)
return number == 2;
if (number % 3 == 0)
return number == 3;
if (number % 5 == 0)
return number == 5;
for (k = 7; k * k <= number; k += 2)
if (number % k == 0)
return 0;
return 1;
}
int main()
{
int k;
int prime_counter = 0;
Blenlerinin toplamna eit olan tamsaylara, mkemmel tamsay (perfect integer) denir.
rnein 6 ve 28 tamsaylar mkemmel tamsaylardr.
1 + 2 + 3 = 6
1 + 2 + 4 + 7 + 14 = 28
Aadaki program ile 10000'den kk mkemmel saylar aranyor. Bulunan saylar
ekrana yazdrlyor:
#include <stdio.h>
int is_perfect(int number);
int main()
{
int k;
for (k = 2; k < 10000; ++k)
if (is_perfect(k))
printf("%d perfect\n", k);
155/529
return 0;
number / 2; ++i)
i == 0)
i;
total;
}
Aadaki programda klavyeden srekli karakter alnmas salanyor, alnan karakterler
ekranda gsteriliyor. Arka arkaya "xyz" karakterleri girildiinde program sonlandrlyor:
#include <stdio.h>
#include <conio.h>
int main()
{
char ch;
int total = 0;
e Dngler
Bir dngnn gvdesini baka bir dng deyimi oluturabilir. Byle yaratlan dnglere
i ie dngler (nested loops) denir. Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
int i, k;
for (i = 0; i < 5; ++i)
for (k = 0; k < 10; ++k)
printf("(%d %d) ", i, k);
printf("\n\n(%d %d) ", i, k);
return 0;
}
Dtaki for dngsnn gvdesindeki deyim, bir baka for dngsdr. i < 5 ifadesi
doru olduu srece iteki for dng deyimi yrtlr. Son yaplan printf ars ekrana
hangi deerleri yazdrr? Dtaki dng i < 5 kouluyla dndne gre dtaki dngden
ktktan sonra i deikeninin deeri 5 olur. teki for dng deyiminin son kez
156/529
return 0;
return 0;
ie dnglerde break deyimi kullanmna dikkat etmek gerekir. teki bir dngnn
gvdesinde break deyiminin kullanlmas ile, yalnzca iteki dngden klr: Aadaki
rnei inceleyin:
157/529
while (1) {
while (1) {
if (ifade)
break;
/***/
}
/*i dngden break ile kldnda ak bu noktaya gelir */
}
Eer i ie dnglerden yalnzca itekinden deil de dnglerin hepsinden birden kmak
istenirse bu durumda goto kontrol deyimi kullanlmaldr. Bu konuyu goto kontrol deyimi
blmnde greceksiniz.
Burada ikinci while dngs tek bir kontrol deyimi olarak ele alnaca iin bloklamaya
gerek yoktur.
Dnglerden k
Bir dngden nasl klabilir? Bir dngden kmak iin aadaki yollardan biri
kullanlabilir.
1. Kontrol ifadesinin yanl olmasyla:
Dng deyimlerinin, kontrol ifadelerinin doru olduu srece dndn biliyorsunuz.
2. return deyimi ile:
Bir ilev iinde yer alan return deyimi ilevi sonlandrdna gre, bir dng deyimi iinde
return deyimi ile karlaldnda dngden klr.
3. break deyimi ile:
break deyiminin kullanlmas ile, programn ak dng deyimini izleyen ilk deyimle srer.
4. goto deyimi ile:
goto deyimi ile bir programn ak ayn ilev iinde dngnn dnda bir baka noktaya
ynlendirilebilir. Bylece dngden klabilir.
5. Program sonlandran bir ilev ars ile:
Standart exit ya da abort ilevleri ile programn kendisi sonlandrlabilir.
Bir dngden kmak amacyla, kontrol ifadesinin yanl olmasn salamak iin dng
deikenine doal olmayacak bir biimde deer atanmas, programn okunabilirliini
bozar. Byle kodlardan kanmak gerekir.
158/529
KOUL LEC
Koul ileci (conditional operator / ternary operator), C dilinin terimli tek ilecidir.
Herhangi bir ifade koul ilecinin terimlerinden biri olabilir. Koul ilecinin genel szdizimi
aadaki gibidir:
ifade1 ? ifade2 : ifade3
Koul ileci, yukardaki biimden de grld gibi, birbirinden ayrlm iki atomdan
oluur. ? ve : atomlar, ilecin terimini birbirinden ayrr.
Derleyici, bir koul ileci ile karlatn, ? atomundan anlar. ? atomunun solundaki
ifadenin (ifade1) saysal deeri hesaplanr. Bu ifade mantksal olarak yorumlanr. Eer
ifade1'in 0'dan farkl ise, bu durumda yalnzca ifade2' nin saysal deeri hesaplanr.
ifade1'in deeri 0 ise, bu kez yalnzca ifade3'n saysal deeri hesaplanr.
Dier ilelerde olduu gibi koul ileci de bir deer retir. Koul ilecinin rettii deer
ifade1 doru ise (0 d bir deer ise) ifade2'nin deeri, ifade1 yanl ise ifade3'n
deeridir. rnek:
m = x > 3 ? y + 5 : y 5;
Burada nce x > 3 ifadesinin saysal deeri hesaplanr. Bu ifadenin deeri 0'dan farkl ise
yani doru ise, koul ileci y + 5 deerini retir. x > 3 ifadesinin deeri 0 ise yani ifade
yanl ise, koul ileci y 5 deerini retir. Bu durumda m deikenine x > 3 ifadesinin
doru ya da yanl olmasna gre y + 5 ya da y 5 deeri atanr.
Ayn ilem if deyimi ile de yaplabilir :
if (x > 3)
m = y + 5;
else
m = y 5;
Koul ileci, ile ncelik tablosunun 13. ncelik seviyesindedir. Bu seviye atama ilecinin
hemen stdr. Aadaki ifadeyi ele alalm:
x > 3 ? y + 5 : y 5 = m
Koul ilecinin ncelii atama ilecinden daha yksek olduu iin, nce koul ileci ele
alnr. x > 3 ifadesinin doru olduunu ve ilecin y + 5 deerini rettiini dnelim.
y + 5 = m
Koul ilecinin rettii deer sol taraf deeri olmadndan, yukardaki ifade geersizdir.
Normal olarak koul ilecinin ilk terimini ayra iine almak gerekmez. Ancak bu terimin,
okunabilirlik asndan genellikle ayra iine alnmas tercih edilir.
(x >= y + 3) ? a * a : b
Koul ilecinin nc terimi konusunda dikkatli olmak gerekir. rnein:
m = a > b ? 20 : 50 + 5
a > b ifadesinin doru olup olmamasna gre koul ileci, 20 ya da 55 deerini retir ve
son olarak da m deikenine koul ilecinin rettii deer atanr. Ancak m deikenine
a > b ? 20 : 50
159/529
return 0;
/* Geersiz! */
/* Geersiz! */
160/529
if deyiminin yerine koul ileci kullanmak her zaman doru deildir. Koul ilecinin
kullanlmasnn salk verildii tipik durumlar vardr. Bu durumlarda genel fikir, koul
ilecinin rettii deerden ayn ifade iinde faydalanmak, bu deeri bir yere aktarmaktr:
1. Koul ilecinin rettii deer bir nesneye atanabilir.
p = (x == 5) ? 10 : 20;
m = (a >= b + 5) ? a + b : a b;
Yukardaki deyimlerin iini grecek if deyimleri de yazlabilirdi:
if (x == 5)
p = 10;
else
p = 20;
if (a >= b + 5)
m = a + b;
else
m = a - b;
2. Bir ilev, koul ilecinin rettii deer ile geri dnebilir:
return x > y ? 10 : 20;
Bu rnekte x > y ifadesinin doru olup olmamasna gre ilev, 10 ya da 20 deerine geri
dner. Yukardaki ifade yerine aadaki if deyimi de kullanlabilirdi :
if (x > y)
return 10;
return 20;
3. Koul ilecinin rettii deer ile bir ilev arlabilir:
func(a == b ? x : y);
Yukardaki deyimde, a, b'ye eit ise func ilevi x deeri ile, a, b'ye eit deil ise y deeri
ile arlr. Ayn ii gren bir if deyimi de yazlabilirdi:
if (a == b)
func(x);
else
func(y);
4. Koul ilecinin rettii deer, bir kontrol deyiminin kontrol ifadesinin bir paras olarak
da kullanlabilir:
if (y == (x > 5 ? 10 : 20))
func();
Yukardaki deyimde x > 5 ifadesinin doru olup olmamasna gre, if ayrac iinde, y
deikeninin 10 ya da 20 deerine eitlii sorgulanr.
Yukardaki durumlarda, koul ilecinin if deyimine tercih edilmesi iyi tekniktir. Bu
durumlarda koul ileci daha okunabilir bir yap oluturur.
Koul ilecinin bilinsizce kullanlmamas gerekir. Eer koul ilecinin rettii deerden
dorudan faydalanlmayacaksa koul ileci yerine if kontrol deyimi tercih edilmelidir.
rnein:
161/529
162/529
return x == y;
yazlabilirdi.
Koul ilecinin ncelik yn sadan soladr. Bir ifade iinde birden fazla koul ileci varsa,
nce en sadaki deerlendirilir. Aadaki kod parasn inceleyin:
#include <stdio.h>
int main()
{
int x = 1, y = 1,
m;
m = x < 5 ? y == 0 ? 4 : 6 : 8;
printf("m = %d\n", m);
}
return 0;
Yukardaki main ilevinde printf ilevi ars ile m deikeninin deeri olarak ekrana 6
yazlr. fade aadaki gibi ele alnr:
m = x
<
5 ? (y == 0 ? 4 : 6) : 8;
Aada iki saydan byk olanna geri dnen, max2 isimli ilev tanmlanyor.
int max2(int a, int b)
{
return a > b ? a : b;
}
1 1 1 1
pi
1 + + + ... serisi
3 5 7 9
4
163/529
165/529
#include komutu ile, ismi verilen dosyann ierii, bu komutun yazld yere yaptrlr.
Bu komut ile nilemci, belirtilen dosyay diskten okuyarak komutun yazl olduu yere
yerletirir. Bu komutla yaplan i, metin dzenleyici programlardaki "kopyala - yaptr"
(copy paste) ilemine benzetilebilir.
#include nilemci komutuyla, kaynak dosyaya eklenmek istenen dosyann ismi iki ayr
biimde belirtilebilir:
1. Asal ayra iinde:
#include <stdio.h>
#include <time.h>
2. ift trnak iinde
#include "general.h"
#include "genetic.h"
Dosya ismi eer asal ayra iinde verilmise, szkonusu dosya nilemci tarafndan,
yalnzca nceden belirlenmi bir dizin iinde aranr. allan derleyiciye ve sistemin
kurulumuna bal olarak, nceden belirlenmi bu dizin farkl olabilir. rnein:
\tc\include
\borland\include
\c600\include
gibi. Benzer biimde UNIX sistemleri iin bu dizin, rnein:
/usr/include
biiminde olabilir. Standart balk dosyalar, asal ayra iinde kaynak koda eklenir.
Sistemlerin ounda dosya ismi iki trnak iine yazldnda, nilemci ilgili dosyay nce
allan dizinde (current directory) arar. Burada bulamazsa sistem ile belirlenen dizinde
arar. rnein:
C:\sample
dizininde alyor olalm.
#include "strfunc.h"
komutu ile, nilemci strfunc.h isimli dosyay nce C:\sample dizininde arar. Eer burada
bulamazsa sistem tarafndan belirlenen dizinde arar. Programclarn kendilerinin
oluturduklar balk dosyalar, genellikle sisteme ait dizinde olmadklar iin, ift trnak
iinde kaynak koda eklenir.
#nclude nilemci komutu ile kaynak koda eklenmek istenen dosya ismi, dosya yolu
(path) da ierebilir:
166/529
#include <sys\stat.h>
#include "c:\headers\myheader.h"
#include nilemci komutu kaynak programn herhangi bir yerinde bulunabilir. Fakat
standart balk dosyalar gibi, iinde eitli bildirimlerin bulunduu dosyalar iin en iyi yer,
kukusuz programn en tepesidir.
#include komutu, i ie gemi (nested) bir biimde de bulunabilir. rnein ok sayda
dosyay kaynak koda eklemek etmek iin yle bir yntem izlenebilir.
ana.c
project.h
#include "project.h"
#include
#include
#include
#include
int main()
{
/****/
}
<stdio.h>
<conio.h>
<stdlib.h>
<time.h>
ana.c dosyas iine yalnzca project.h dosyas ekleniyor. nilemci bu dosyay kaynak
koda ekledikten sonra yoluna bu dosyadan devam eder.
zellikle byk programlar, modl ismi verilen ayr ayr paralar halinde yazlr. Bu
modllerden bazlarnn amac, dier modllere hizmet vermektir. C ve C++ dillerinde,
genel hizmet verecek kodlar (server codes), genel olarak iki ayr dosya halinde yazlr.
lev tanmlamalar, global deiken tanmlamalar uzants .c olan dosyada yer alr. Bu
dosyaya, kodlama dosyas (implementation file) denir. Hizmet alacak kodlar (client
codes) ilgilendiren bildirimler ise bir baka dosyada tutulur. Bu dosyaya, balk dosyas
(header file) denir. Bir balk dosyas, bir modln arayzdr (interface). Modl daryla
olan ilikisini arayz ile kurar.
Verilen hizmetlerden faydalanacak kullanc kodlar, hizmet veren kodlarn kendisini deil,
yalnzca arayzn grr. Hizmet alan kodlar, hizmet veren kodlarn arayzlerine bal
olarak yazlr. Bylece hizmet veren kodlarn kendisi ile arayzleri, birbirinden net olarak
ayrlm olur.
Hizmet veren kodlarn arayzleriyle tanmlarn birbirinden ayrmann ne gibi faydalar
olabilir?
Kullanc kodlar, yani hizmet alan kodlar, hizmet veren ilevlerin tanmlarna gre deil
de, arayzlerine bal olarak yazlr. Bundan aadaki faydalar salanabilir:
1. Hizmet veren kodlar yazanlar, ayn arayze bal kalmak kaydyla, tanm kodlarnda
deiiklik yapabilir. Bu durumda hizmet alan kodlarda bir deiiklik yaplmas gerekmez.
2. Kullanc kodlar yazacak programc, hizmet veren kodlara ilikin uygulama ayrntlarn
bilmek zorunda kalmadndan, daha kolay soyutlama yapar.
3. Birden fazla programcnn ayn projede almas durumunda, proje gelitirme sresi
ksaltlm olur.
167/529
SIZE
100
nilemci komutuyla, nilemci kaynak kod iinde grd her bir SIZE atomu yerine
100 atomunu yerletirir. Derleme modlne girecek kaynak programda, SIZE atomu artk
yer almaz.
#define nilemci komutu kullanlarak ounlukla bir isim, saysal bir deerle yer
deitirilir. Saysal bir deerle deitirilen isme, "simgesel deimez" (symbolic constant)
denir. Simgesel deimezler nesne deildir. Derleme modlne giren kaynak kodda,
simgesel deimezlerin yerini saysal ifadeler alm olur.
#define nilemci komutuyla tanmlanan isimlere, "basit makro" (simple macro) da denir.
Simgesel deimezler, geleneksel olarak byk harf ile isimlendirilir. Bylece kodu
okuyann deikenlerle, simgesel deimezleri ayrt edebilmesi salanr. Bilindii gibi C
dilinde, deiken isimlendirmelerinde arlkl olarak kk harfler kullanlr.
Bir simgesel deimez, baka bir simgesel deimezin tanmlamasnda kullanlabilir.
rnein:
#define MAX
#define MIN
100
(MAX - 50)
Yer deitirme ilemi, STR1'in kaynak kod iinde bir atom halinde bulunmas durumunda
yaplr:
#define SIZE
100
169/529
200
BYK
10
tanmlamas geersizdir.
nilemci program, #include komutu ile kaynak koda eklenen dosyann iindeki nilemci
komutlarn da altrr. Bu durumda iinde simgesel deimez tanmlamalar yaplm bir
dosya, #include komutu ile kaynak koda eklendiinde, bu simgesel deimezler de
kaynak kod iinde tanmlanm gibi geerli olur.
#define nilemci komutunda dizgeler de kullanlabilir:
#define HATA_MESAJI
/***/
printf(HATA_MESAJI);
/***/
750
void foo()
{
/***/
if (x == PERSONEL_SAYISI)
/***/
}
Kaynak kod iinde PERSONEL_SAYISI simgesel deimezi yerine dorudan 750 deeri
kullanlm olsayd, kodu okuyann, bu deimezin ne anlama geldiini karmas ok
daha zor olurdu, deil mi?
170/529
#define BYTE
#define BOOL
char
int
VALID;
INVALID;
TRUE;
FALSE;
FAILED;
gibi.
Baz ilevlere de, aran kod paras tarafndan simgesel deimezler gnderilir.
C'nin standart balk dosyalarnda da bu amala baz simgesel deimezler
tanmlanmtr. rnein stdlib.h balk dosyas iinde
#define EXIT_FAILURE
#define EXIT_SUCCESS
1
0
biiminde tanmlamalar vardr. Yani stdlib.h balk dosyas kaynak koda eklenirse
EXIT_FAILURE simgesel deimezi 1, EXIT_FAILURE simgesel deimezi, 0 yerine
kullanlabilir. Bu simgesel deimezler, standart exit ilevine yaplan arlarda kullanlr:
exit(EXIT_FAILURE);
stdio.h balk dosyas iinde standart fseek ilevine argman olarak gnderilmesi
amacyla simgesel deimez tanmlanmtr:
#define SEEK_SET
#define SEEK_CUR
#define SEEK_END
0
1
2
171/529
pos_flag = ON;
validiy_flag = INVALID;
switch kontrol deyimindeki case ifadeleri de ounlukla simgesel deimezlerle
oluturulur. Bu konuyu switch kontrol deyiminde inceleyeceiz.
10
3.14159
simgesel deimezi kullanlabilir. Her defasnda pi says, bir deimez olarak yazlrsa,
her defasnda ayn deer yazlamayabilir. rnein kaynak kodun bir yerinde 3.14159
deimezi yazlmken kaynak kodun bir baka noktasnda yanllkla 3.15159 gibi bir
deer de yazlabilir. Derleyici programn byle tutarszlklar iin mantksal bir uyar
iletmesi olana yoktur. Simgesel deimez kullanm bu tr hatalar ortadan kaldrr.
Yine derleyicilerin ou, math.h balk dosyas iinde de pek ok matematiksel deimez
tanmlar.
Baz simgesel deimezler hem tanabilirlik salamak hem de ortak arayz oluturmak
amacyla tanmlanr. Standart balk dosyalarndan limits.h iinde kullanlan tamsay
trlerinin sistemdeki snr deerlerini tayan standart simgesel deimezler
tanmlanmtr:
Simgesel
Deimez
CHAR_BIT
SCHAR_MN
SCHAR_MAX
Olabilecek En Kk
Deer
8
-127
127
UCHAR_MAX
255
Anlam
char trndeki bit says
signed char trnn en kk deeri
signed char trnn en byk
deeri
unsigned char trnn en byk
deeri
172/529
SHRT_MIN
-32.767
SHRT_MAX
32.767
USHRT_MAX
65535
INT_MIN
INT_MAX
UINT_MAX
-32.767
32.767
65.535
LONG_MIN
-2.147.483.648
LONG_MAX
-2.147.483.647
ULONG_MAX
4.294.967.295
LLONG_MIN
9.233.372.036.854.775.808
LLONG_MAX
9.233.372.036.854.775.807
ULLONG_MAX
18.446.744.073.709.551.615
CHAR_MIN
SCHAR_MIN ya da 0
CHAR_MAX
SCHAR_MAX ya da UCHAR_MAX
MB_LEN_MAX
#define komutu kaynak kodun herhangi bir yerinde kullanlabilir. Ancak tanmland
yerden kaynak kodun sonuna kadar olan blge iinde etki gsterir. nilemci program
dorudan bilinirlik alan kavramna sahip deildir. Bir bloun banda tanmlanan bir
simgesel deimez yalnzca o bloun iinde deil tanmland yerden kaynak kodun
sonuna kadar her yerde etkili olur.
Simgesel deimezler bazen balk dosyasnn iinde bazen de kaynak dosyann iinde
tanmlanr.
173/529
int a[N];
gibi bir tanmlama yapldn dnelim. nilemci bu tanmlamay
a[= 100];
biimine getirir ki bu da geersizdir.
#define nilemci komutu satrn yanllkla ';' atomu ile sonlandrmak bir baka tipik
hatadr.
#define N
100;
MAX
10 + 20
int main()
{
int result;
result = MAX * 2;
printf("%d\n", result);
}
return 0;
Yukardaki rnekte result deikenine 60 deil 50 deeri atanr. Ancak nilemci komutu
#define
MAX
(10 + 20)
Kaynak metnin yazld ISO 646 gibi baz karakter setlerinde '&', '|', '^' karakterleri
olmadndan, baz C ilelerinin yazmnda sorun olumaktadr.
C89 standartlarna daha sonra yaplan eklemeyle dile katlan iso646 balk dosyasnda,
standart baz C ilelerine dntrlen basit makrolar tanmlanmtr. Aada bu
makrolarn listesi veriliyor:
174/529
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
#define
and
and_eq
bitand
bitor
compl
not
not_eq
or
or_eq
xor
xor_eq
&&
&=
&
|
~
!
!=
||
|=
^
^=
175/529
switch DEYM
switch deyimi bir tamsay ifadesinin farkl deerleri iin, farkl ilerin yaplmas amacyla
kullanlr. switch deyimi, zellikle else if merdivenlerine okunabilirlik ynnden bir
seenek oluturur.
Deyimin genel biimi aadaki gibidir:
switch (ifade) {
case ifade1 :
case ifade2 :
case ifade3 :
.......
case ifade_n:
default:
}
switch, case, ve default C dilinin anahtar szckleridir.
switch ayrac iindeki ifadenin saysal deeri hesaplanr. Bu saysal deere eit deerde
bir case ifadesi olup olmad yukardan aa doru snanr. Eer byle bir case ifadesi
bulunursa programn ak o case ifadesine geirilir. Artk program buradan akarak ilerler.
switch ayrac iindeki ifadenin saysal deeri hibir case ifadesine eit deilse, eer varsa,
default anahtar szcnn bulunduu ksma geirilir.
#include <stdio.h>
int main()
{
int a;
printf("bir sayi girin : ");
scanf("%d", &a);
switch (a) {
case 1: printf("bir\n");
case 2: printf("iki\n");
case 3: printf("\n");
case 4: printf("drt\n");
case 5: printf("be\n");
}
return 0;
}
Yukardaki rnekte scanf ilevi ile, klavyeden a deikenine 1 deeri alnm olsun. Bu
durumda programn ekran kts u ekilde olur:
bir
iki
drt
be
Eer uygun case ifadesi bulunduunda yalnzca bu ifadeye ilikin deyim(ler)in
yrtlmesi istenirse break deyiminden faydalanlr. break deyiminin kullanlmasyla,
dnglerden olduu gibi switch deyiminden de klr. Daha nce verilen rnee break
deyimleri ekleniyor:
177/529
#include <stdio.h>
int main()
{
int a;
Uygulamalarda, switch deyiminde ounlukla her case ifadesi iin bir break deyiminin
kullanlr. Tabi byle bir zorunluluk yoktur.
case ifadelerini izleyen ":" atomundan sonra istenilen sayda deyim olabilir. Bir case
ifadesini birden fazla deyimin izlemesi durumunda bu deyimlerin bloklanmasna gerek
yoktur. Yani bir case ifadesini izleyen tm deyimler, bir blok iindeymi gibi ele alnr.
case ifadelerinin belirli bir sray izlemesi gibi bir zorunluluk yoktur.
default case
default bir anahtar szcktr. switch deyimi gvdesine yerletirilen default anahtar
szcn ':' atomu izler. Oluturulan bu case'e default case denir.
Edeer bir case ifadesi bulunamazsa programn ak default case iine girer.
Daha nce yazlan switch deyimine default case ekleniyor.
#include <stdio.h>
int main()
{
int a;
Yukarda da anlatld gibi switch ayrac iindeki ifadenin saysal deerine eit bir case
ifadesi bulunana kadar derleme ynnde, yani yukardan aaya doru, tm case
ifadeleri srasyla snanr. case ifadelerinin oluma skl ya da olasl hakknda elde bir
bilgi varsa, olasl ya da skl yksek olan case ifadelerinin daha nce yazlmas
gereksiz karlatrma saysn azaltabilir.
178/529
case ifadelerinin, tamsay trnden (integral types) deimez ifadesi olmas gerekir.
Bilindii gibi deimez ifadeleri, derleme aamasnda derleyici tarafndan net saysal
deerlere dntrlebilir:
case 1 + 3:
/* Geerli */
/* Geersiz */
deyim1;
deyim1;
deyim1;
deyim1;
deyim5;
break;
break;
break;
break;
Her switch deyiminin yerine ayn ii grecek ekilde bir else if merdiveni yazlabilir ama
her else if merdiveni bir switch deyimiyle karlanamaz. switch ayrac iindeki ifadenin bir
tamsay trnden olmas zorunludur. case ifadeleri de tamsay trlerinden deimez
ifadesi olmak zorundadr. switch deyimi, tamsay trnden bir ifadenin deerinin deiik
tamsay deerlerine eitliinin snanmas ve eitlik durumunda farkl ilerin yaplmas iin
kullanlr. Oysa else if merdiveninde her trl karlatrma sz konusu olabilir. rnek:
179/529
if (x >
m =
else if
m =
else if
m =
else
m =
20)
5;
(x > 30 && x < 55)
3;
(x > 70 && x < 90)
7;
2;
180/529
1
0
2
case TRUE
:
case FALSE
:
case UNDEFINED :
Yukardaki case ifadeleri geerlidir.
case ifadeleri olarak karakter deimezleri de kullanlabilir:
#include <stdio.h>
int main()
{
switch (getchar()) {
case '0': printf("sfr\n"); break;
case '1': printf("bir\n"); break;
case '2': printf("iki\n"); break;
case '3': printf("\n"); break;
case '4': printf("drt\n"); break;
case '5': printf("be\n"); break;
default : printf("gecersiz!\n");
}
return 0;
}
case ifadelerini izleyen deyimlerin 15 - 20 satrdan uzun olmas okunabilirlii zayflatr.
Bu durumda yaplacak ilemlerin ilev arlarna dntrlmesi iyi bir tekniktir.
switch (x) {
case ADDREC:
addrec();
break;
case DELREC:
delrec();
break;
case FINDREC:
findrec();
break;
}
181/529
Yukardaki rnekte case ifadesi olarak kullanlan ADDREC, DELREC, FINDREC daha nce
tanmlanm simgesel deimezlerdir. Her bir case iin yaplan ilemler, birer ilev iinde
sarmalanyor.
char ch = getch();
switch (ch) {
case 'E' : deyim1; break;
case 'H' : deyim2; break;
default : deyim3;
}
Bir switch deyiminde ayn saysal deere sahip birden fazla case ifadesi olamaz. Bu
durum derleme zamannda hata oluturur.
switch deyimi, baka bir switch deyiminin ya da bir dng deyiminin gvdesini
oluturabilir:
#include <stdio.h>
#include <conio.h>
#define
ESC
0X1B
int main()
{
int ch;
Yukardaki main ilevinde switch deyimi, dtaki while dngsnn gvdesini oluturuyor.
switch deyimi, dng gvdesindeki tek bir deyim olduundan, dtaki while dngsnn
bloklanmasna gerek yoktur. Tabi while dngsnn bloklanmas bir hataya neden olmaz.
Ancak case ifadeleri iinde yer alan break deyimiyle yalnzca switch deyiminden klr.
while dngsnn de dna kmak iin case ifadesi iinde goto deyimi kullanlabilir.
imdi de aadaki program inceleyin. Programda display_date isimli bir ilev
tanmlanyor. lev gn, ay ve yl deeri olarak ald bir tarih bilgisini ngilizce olarak
aadaki formatta ekrana yazdryor:
5th Jan 1998
include <stdio.h>
void display_date(int day, int month, int year)
{
printf("%d", day);
182/529
switch (day) {
case 1 :
case 21 :
case 31 : printf("st
case 2 :
case 22 : printf("nd
case 3 :
case 23 : printf("rd
default : printf("th
}
switch (month) {
case 1 : printf("Jan
case 2 : printf("Feb
case 3 : printf("Mar
case 4 : printf("Apr
case 5 : printf("May
case 6 : printf("Jun
case 7 : printf("Jul
case 8 : printf("Aug
case 9 : printf("Sep
case 10: printf("Oct
case 11: printf("Nov
case 12: printf("Dec
}
printf("%d", year);
"); break;
"); break;
"); break;
");
");
");
");
");
");
");
");
");
");
");
");
");
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
break;
int main()
{
int day, month, year;
int n = 20;
levin tanmnda iki ayr switch deyimi kullanlyor. lk switch deyimiyle, gn deerini
izleyen (th, st, nd, rd) sonekleri yazdrlrken, ikinci switch deyimiyle, aylara ilikin
ksaltmalar (Jan, Feb. Mar.) yazdrlyor.
case ifadelerini izleyen deyimlerden biri break deyimi olmak zorunda deildir. Baz
durumlarda break deyimi zellikle kullanlmaz, uygun bir case ifadesi bulunduunda daha
aadaki case lerin iindeki deyimlerin de yaplmas zellikle istenir. Aadaki program
derleyerek altrn:
#include <stdio.h>
int isleap(int y)
{
return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0);
}
int day_of_year(int day, int month, int year)
{
int sum = day;
183/529
switch (month - 1) {
case 11: sum += 30;
case 10: sum += 31;
case 9 : sum += 30;
case 8 : sum += 31;
case 7 : sum += 31;
case 6 : sum += 30;
case 5 : sum += 31;
case 4 : sum += 30;
case 3 : sum += 31;
case 2 : sum += 28 + isleap(year);
case 1 : sum += 31;
}
return sum;
int main()
{
int day, month, year;
int n = 5;
while (n-- > 0) {
printf("gun ay yil olarak bir tarih girin : ");
scanf("%d%d%d", &day, &month, &year);
printf("%d yilinin %d. gunudur!\n", year, day_of_year(day, month,
year));
}
}
return 0;
day_of_year ilevi dardan gn, ay ve yl deeri olarak gelen tarih bilgisinin ilgili yln
kanc gn olduunu hesaplayarak bu deerle geri dnyor. lev iinde kullanlan
switch deyimini dikkatli bir ekilde inceleyin. switch deyiminin ayrac iinde, dardan
gelen ay deerinin 1 eksii kullanlyor. Hibir case iinde bir break deyimi kullanlmyor.
Uygun bir case ifadesi bulunduunda, daha aada yer alan tm case iindeki deyimler
de yaplr. Bylece, dardan gelen ay deerinden daha dk olan her bir ayn ka
ektii bilgisi, gn toplamn tutan sum deikenine katlyor.
184/529
goto DEYM
Dier programlama dillerinde olduu gibi C dilinde de programn ak, bir koula bal
olmakszn kaynak kod iinde baka bir noktaya ynlendirilebilir. Bu, C dilinde goto
deyimi ile yaplr:
goto deyiminin genel szdizimi aadaki gibidir:
<goto etiket;>
....
<etiket:>
<deyim;>
goto, C dilinin 32 anahtar szcnden biridir. Etiket (label), programcnn verdii bir
isimdir. phesiz isimlendirme kurallarna uygun olarak seilmelidir. Programn ak, bu
etiketin yerletirilmi olduu yere ynlendirilir. Etiket, goto anahtar szcnn
kullanld ilev iinde herhangi bir yere yerletirilebilir. Etiket isminden sonra ':' atomu
yer almak zorundadr. Etiketi izleyen deyim de goto kontrol deyiminin szdiziminin bir
parasdr. Etiketten sonra bir deyimin yer almamas bir szdizim hatasdr.
Etiketin goto anahtar szcnden daha sonraki bir kaynak kod noktasna yerletirilmesi
zorunluluu yoktur. Etiket goto anahtar szcnden nce de tanmlanm olabilir:
#include <stdio.h>
int main()
{
/***/
goto GIT;
/***/
GIT:
printf("goto deyimi ile buraya gelindi\n");
}
return 0;
Yukardaki programda, etiket goto anahtar szcnden daha sonra yer alyor.
int main()
{
GIT:
printf("goto deyimi ile gelinecek nokta\n");
/***/
goto GIT;
/***/
}
return 0;
Yukardaki programda, etiket goto anahtar szcnden daha nce yer alyor.
goto etiketleri, geleneksel olarak byk harf ile, birinci stuna dayal olarak yazlr.
Bylece kaynak kod iinde daha fazla dikkat ekerler.
goto etiketleri bir ilev iinde, bir deyimden nce herhangi bir yere yerletirilebilir. Yani
etiket, ayn ilev iinde bulunmak kouluyla, goto anahtar szcnn yukarsna ya da
aasna yerletirilebilir. Bu zelliiyle goto etiketleri, yeni bir bilinirlik alan kural
oluturur. Bir isim, ilev iinde nerede tanmlanrsa tanmlansn o ilev iinde her yerde
bilinir. Bu bilinirlik alan kuralna "ilev bilinirlik alan" (function scope) denir.
185/529
goto etiketleri bulunduu bloun isim alanna eklenmez. goto etiket isimleri ayr bir isim
alannda deerlendirilir. Bir blok iindeki goto etiketi ile ayn isimli bir yerel deiken
olabilir:
void func()
{
int x;
goto x;
x:
x = 20;
}
Yapsal programlama tekniinde goto deyiminin kullanlmas nerilmez. nk goto
deyiminin kullanlmas bir takm sakncalar dourur:
1. goto deyimi programlarn okunabilirliini bozar. Kodu okuyan kii goto deyimiyle
karlatnda ilevin iinde etiketi arayp bulmak zorunda kalr ve program bu noktadan
okumay srdrr.
2. goto deyimlerinin kullanld bir programda bir deiiklik yaplmas ya da programn,
yaplacak eklemelerle, gelitirilmeye allmas daha zor olur. Programn herhangi bir
yerinde bir deiiklik yaplmas durumunda, eer program iinde baka yerlerden
deiikliin yapld yere goto deyimleri ile srama yaplm ise, bu noktalarda da bir
deiiklik yaplmas gerekebilir. Yani goto deyimi program paralarnn birbirine olan
bamlln artrr, bu da genel olarak istenen bir ey deildir.
Bu olumsuzluklara karn, baz durumlarda goto deyiminin kullanlmas programn
okunabilirliini bozmak bir yana, dier seeneklere gre daha okunabilir bir yapnn
olumasna yardmc olur:
ie birden fazla dng varsa, ve iteki dnglerden birindeyken, yalnzca bu dngden
deil, btn dnglerden birden klmak isteniyorsa goto deyimi kullanlmaldr.
Aadaki kod parasnda i ie dng bulunuyor. En iteki dngnn iinde func ilevi
arlarak ilevin geri dn deeri snanyor. lev eer 0 deerine geri dnerse
programn ak goto deyimiyle tm dnglerin dna ynlendiriliyor:
#include <stdio.h>
int test_func(int val);
int main()
{
int i, j, k;
for (i = 0; i < 100; ++i) {
for (j = 0; j < 100; ++j) {
for (k = 0; k < 20; ++k) {
/*...*/
if (!test_func(k))
goto BREAK;
/*...*/
}
}
}
BREAK:
printf("dng dndaki ilk deyim\n");
return 0;
}
186/529
Yukardaki ilev iinde i ie ayr dng deyimi yer alyor. En iteki dngnn iinde
arlan bir ilev ile bir snama ilemi yaplm, snamann olumsuz sonulanmas
durumunda, programn ak en dtaki dng deyiminin sonrasna ynlendiriliyor.
Oysa goto deyimi kullanmasayd, ancak bir bayrak (flag) deikenin kullanlmasyla ayn
ama gerekletirilebilirdi. Her dngnn knda bayrak olarak kullanlan deikenin
deerinin deitirilip deitirilmedii snanmak zorunda kalnrd.
#include <stdio.h>
#define
#define
BREAK
NO_BREAK
0
1
return 0;
ADDREC
LISTREC
DELREC
SORTREC
EXITPROG
1
2
3
4
5
int get_option(void);
void add_rec(void);
void list_rec(void);
void del_rec(void);
void sort_rec(void);
int main()
{
int option;
187/529
for (;;) {
option = get_option();
switch (option) {
case ADDREC
:add_rec();break;
case LISTREC
:list_rec();break;
case DELREC
:del_rec(); break;
case SORTREC
:sort_rec(); break;
case EXITPROG :goto EXIT;
}
}
EXIT:
return 0;
}
Yukardaki main ilevinde option deikeninin deeri EXITPROG olduunda programn
ak, goto deyimiyle sonsuz dngnn dna gnderiliyor. goto deyimi yerine break
deyimi kullanlsayd, yalnzca switch deyiminden klm olurdu.
goto deyimiyle, bir ilevin iindeki bir noktadan, yine kendi iindeki bir baka noktaya
srama yaplabilir. Byle sramalara yerel sramalar (local jumps) denir. Bir ilevin
iinden baka bir ilevin iine sramak baka aralarla mmkndr. Byle sramalara
yerel olmayan sramalar (non-local jumps) denir. C dilinde, yerel olmayan sramalar
ismi setjmp ve longjmp olan standart ilevlerle yaplr. Bu sramalar ounlukla "Olaan
d hatalarn ilenmesi" (exception handling) amacyla kullanlr.
188/529
rand levi
Standart rand ilevi rastgele say retir. Bu ilevin bildirimi aadaki gibidir:
int rand(void);
C standartlar rand ilevinin rastgele say retimi konusunda kullanaca algoritma ya da
teknik zerinde bir koul koymamtr. Bu konu derleyiciyi yazanlarn seimine bal
(implementation dependent) braklmtr. rand ilevinin bildirimi, standart bir balk
dosyas olan stdlib.h iindedir. Bu yzden rand ilevinin arlmas durumunda bu balk
dosyas "include" nilemci komutuyla kaynak koda eklenmelidir.
#include <stdlib.h>
rand ilevi her arldnda [0, RAND_MAX] aralnda rastgele bir tamsay deerini geri
dndrr. RAND_MAX stdlib.h balk dosyas iinde tanmlanan bir simgesel deimezdir.
C standartlar bu simgesel deimezin en az 32767 deerinde olmasn art komaktadr.
Derleyicilerin hemen hepsi RAND_MAX simgesel deimezini 32767 olarak, yani 2 byte
iaretli int trnn en byk deeri olarak tanmlar:
#define
RAND_MAX
32767
Aadaki program parasnda, 0 ile RAND_MAX arasnda 10 adet rastgele say retilerek
ekrana yazdrlyor. Program derleyerek altrn:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int k;
for (k = 0; k < 10; ++k)
printf("%d ", rand());
return 0;
}
Yukardaki kaynak kodla oluturulan programn her altrlmasnda ekrana ayn saylar
yazlr. rnein yukardaki program, DOS altnda Borland Turbo C 2.0 derleyicisi ile
derleyip altrldnda ekran kts aadaki gibi oldu:
346 130 10982 1090
11656
7117
17595
6415
22948
31126
189
srand levi
Oluturulan program her altrldnda neden hep ayn say zinciri elde ediliyor? rand
ilevi rastgele say retmek iin bir algoritma kullanyor. Bu algoritma derleyiciden
derleyiciye deise de, rastgele say retiminde kullanlan ana tema ayndr. Bir balang
deeri ile ie balanr. Buna tohum deeri (seed value) denir. Bu deer zerinde baz
ilemler yaplarak rastgele bir say elde edilir. Tohum deer zerinde yaplan ilem bu kez
elde edilen rastgele say zerinde yinelenir.
rand ilevi arlarn ieren bir program her altrldnda ayn tohum deerinden
balanaca iin ayn say zinciri elde edilir.
Bir baka standart ilev olan srand ilevi, rastgele say reticisinin tohum deerini
deitirmeye yarar. srand ilevinin stdlib.h balk dosyasnda yer alan bildirimi aadaki
gibidir:
void srand (unsigned seed);
srand ilevine gnderilen deer, ilev tarafndan rastgele say reticisinin tohum deeri
yaplr. srand ilevine argman olarak baka bir tohum deeri gnderildiinde ilevin
rettii rastgele say zinciri deiir.
Aada rand ve srand ilevleri tanmlanyor:
#define
RAND_MAX
32767
return 0;
Program bu ekliyle DOS altnda Borland Turbo C 2.0 derleyicisi ile derleyip
altrldnda ekran kts aadaki gibi oldu:
1862
11548
3973
4846
9095
16503
6335
13684
21357
21505
190
Ancak bu kez oluturulan program da her altrldnda yine yukardaki say zinciri elde
edilir, deil mi? rand ilevinin kullanmakta olduu nceden seilmi (default) tohum
deeri kullanlmasa da, bu kez her defasnda srand ilevine gnderilmi olan tohum
deeri kullanlr. Program birka kere altrp gerekten hep ayn say zincirinin retilip
retilmediini grn.
Baz durumlarda, programn her altrlmasnda ayn rastgele say zincirinin retilmesi
istenmez. rnein bir oyun programnda programn altrlmasyla hep ayn saylar
retilirse, oyun hep ayn biimde oynanr. Programn her almasnda farkl bir say
zincirinin elde edilmesi iin, srand ilevinin rastgele say reticisinin tohum deerini
programn her almasnda baka bir deer yapmas gerekir. Bu amala ou zaman
standart time ilevi ilevinden faydalanlr.
time standart bir C ilevidir, bildirimi standart bir balk dosyas olan time.h dosyas
iindedir. Parametre deikeni gsterici olan time ilevini, ancak ileride ayrntl olarak ele
alacaz. imdilik time ilevini iimizi grecek kadar inceleyeceiz. time ilevi kendisine 0
deeri gnderildiinde, nceden belirlenmi bir tarihten (sistemlerin ounda 01.01.1970
tarihinden) ilevin arld ana kadar geen saniye saysn geri dndrr. levin geri
dn deeri, derleyicilerin ounda long trden bir deerdir. inde rastgele say
retilecek programda, srand ilevine argman olarak time ilevinin geri dn deeri
gnderilirse, program her altnda, belirli bir zaman gemesi nedeniyle, rastgele say
reticisi baka bir tohum deeriyle ilkdeerini alr. Bylece programn her
altrlmasnda farkl say zinciri retilir:
srand(time(0));
srand ilevine yaplan bu ar, derleyicilerin ounda standart olmayan randomize isimli
bir makro olarak tanmlanmtr:
randomize();
Yukardaki ilev ars yerine bu makro da kullanlabilir. Makrolar konusu ileride ayrntl
olarak ele alnacak.
Yukardaki daha nce yazlan rnek program her altnda farkl say zinciri retecek
duruma getirelim:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
int k;
srand(time(0));
for (k = 0; k < 10; ++k)
printf("%d ", rand());
return 0;
}
Programlarda bazen belirli bir aralkta rastgele say retilmesi istenir. Bu amala kalan
ileci kullanlabilir. Aadaki ifadeleri inceleyin:
rand() % 2
Yalnzca 0 ya da 1 deerini retir.
191
rand() % 6
0 - 5 aralnda rastgele bir deer retir
rand() % 6 + 1
1 - 6 aralnda rastgele bir deer retir. (rnein bir zar deeri)
rand() % 6 + 3
3 - 8 aralnda rastgele bir deer retir.
Ancak derleyici programlarn salad rastgele say reticilerinin rettikleri rastgele
saylarn, dk anlaml bitleri ounlukla rastgele kabul edilemez. Bu durumda
yukardaki ifadeler, retilmesi gereken tm saylar iin eit bir dalm salamaz.
Dalmn daha dzgn olabilmesi iin baz yntemler kullanlabilir:
rand() % N
ifadesi yerine
rand()/(RAND_MAX / N + 1)
ya da
(int)((double)rand() / ((double)RAND_MAX + 1) * N)
ifadeleri yazlabilir.
Ya da aadaki gibi bir ilev tanmlanabilir:
#include <stdio.h>
#include <stdlib.h>
#define
10
int mrand()
{
unsigned int x = (RAND_MAX + 1u) / N;
unsigned int y = x * N;
unsigned int r;
srand(time(0)) arsnn bir dng iinde yer almas sk yaplan bir hatadr.
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int zar_at()
{
srand(time(0));
return rand() % 6 + 1 +
}
rand() % 6 + 1;
192
int main()
{
int k;
for (k = 0; k < 10; ++k)
printf("%d\n", zar_at());
}
return 0;
Yukarda yazlan programda yer alan zar_at isimli ilev, bir ift zar atldnda elde iki
zarn toplam deeriyle geri dnyor. srand(time(0)) ars zar_at ilevi iinde yaplyor.
main ilevi iinde oluturulan for dngsyle 10 kez zar_at ilevi arlyor. levin her
arsnda time ilevi hep ayn geri dn deerini retir. Bu durumda srand ilevine hep
ayn argman geildiinden rand ilevi arlar da hep ayn iki sayy retir. Yani ekrana
10 kez ayn deer yazdrlr. srand(time(0)) arsnn main ilevi iindeki for
dngsnden nce yaplmas gerekirdi, deil mi?
Aadaki main ilevinde uzunluklar 3 - 8 harf arasnda deien ngiliz alfabesindeki
harfler ile oluturulmu rastgele 10 szck ekrana yazdrlyor:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
#define
TIMES
MIN_WORD_LEN
MAX_WORD_LEN
10
3
8
void write_word(void)
{
int len = rand() % (MAX_WORD_LEN - MIN_WORD_LEN + 1) + MIN_WORD_LEN;
while (len--)
putchar('A' + rand() % 26);
int main()
{
int k;
srand(time(0));
for (k = 0; k < TIMES; ++k) {
write_word();
putchar('\n');
}
return 0;
193
while (len--) {
while (isvowel(ch = rand() % 26 + 'A'))
;
putchar(ch);
}
Aada rastgele bir tarihi ekrana yazdran print_random_date isimli bir ilev
tanmlanyor. lev her arldnda 1.1.MIN_YEAR, 31.12.MAX_YEAR tarihleri arasnda
rastgele ancak geerli bir tarih bilgisini ekrana yazyor:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
MAX_YEAR
MIN_YEAR
2010
1900
void print_random_date()
{
int d, m, y;
int isleap(int y)
{
return y % 4 == 0 && y % 100 != 0 || y % 400 == 0;
}
int main()
{
int k;
srand(time(0));
for (k = 0; k < 20; ++k)
print_random_date();
}
return 0;
Olaslk problemleri, olasla konu olayn bir bilgisayar program ile gerekletirilmesi
yoluyla zlebilir. yi bir rastgele say reticisi kullanld takdirde, olasla konu olay,
bir bilgisayar program ile oynatlr, olay bilgisayarn ilem yapma hzndan faydalanlarak
yksek saylarda yinelemeye sokulur. phesiz hesaplanmak istenen olaya ilikin olaslk
deeri, yaplan yineleme saysna ve rastgele say reticisinin niteliine bal olur.
Aadaki kod yaz tura atlmas olaynda tura gelme olasln hesaplyor:
194
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
TIMES
HEADS
30000
1
int main()
{
int heads_counter = 0;
int k;
srand(time(0));
for (k = 0; k < TIMES; ++k)
if (rand() % 2 == HEADS)
heads_counter++;
printf("tura gelme olasl = %lf", (double) heads_counter / TIMES);
}
return 0;
TIMES
100
olasl = 0.480000
TIMES
500
olasl = 0.496000
TIMES
2500
olasl = 0.506800
TIMES
10000
olasl = 0.503500
TIMES
30000
olasl = 0.502933
TIMES
100000
olasl = 0.501450
TIMES
1000000
olasl = 0.500198
Oyun sonucu
Oyuncu kazanr
Oyuncu kaybeder
Oyuncu kazanr
Oyuncu kaybeder
Oyuncu kazanr
Oyuncu kazanr
195
NKEZ
1000000
int zar_at()
{
int zar1 = rand() % 6 + 1;
int zar2 = rand() % 6 + 1;
return zar1 + zar2;
}
/* oyuncu kazanrsa 1 deerine, oyuncu kaybederse 0 deerine geri dner */
int oyun()
{
int zar_toplam;
zar_toplam = zar_at();
switch (zar_toplam) {
case 7 :
case 11: return 1;
case 2 :
case 3 :
case 12: return 0;
}
return oyun_devami(zar_toplam);
}
/* oyuncu 4, 5, 6, 8, 9, 10 atmissa oyunun devam.
oyuncu kazanrsa 1 deerine, oyuncu kaybederse 0 deerine geri dner */
int oyun_devami(int zar_toplam)
{
int yeni_zar;
for (;;) {
yeni_zar = zar_at();
if (yeni_zar == zar_toplam)
return 1;
if (yeni_zar == 7)
return 0;
}
}
int main()
{
int k;
int kazanma_sayisi = 0;
srand(time(0));
for (k = 0; k < NKEZ; ++k)
kazanma_sayisi += oyun();
printf("kazanma olasiligi = %lf\n", (double)kazanma_sayisi / NKEZ);
}
return 0;
196
Rastgele gerek say reten bir standart C ilevi yoktur. Ancak RAND_MAX simgesel
deimezinden faydalanarak
(double)rand() / RAND_MAX
ifadesi ile 0 1 aralnda rastgele bir gerek say retilebilir. Aada rastgele gerek
say reten drand isimli bir ilev tanmlanyor. levi inceleyerek, rastgele bir gerek
sayy nasl rettiini anlamaya aln:
#define
PRECISION
2.82e14
double drand()
{
double sum = 0;
double denom = RAND_MAX + 1;
double need;
int main()
{
int k;
for (k = 0; k < 10; ++k)
printf("%lf\n", drand());
return 0;
}
Aada pi says Monte Carlo yntemi diye bilinen yntemle bulmaya allyor. Bu
yntemde birim kare iinde yarap karenin kenar uzunluuna eit bir daire paras
olduu dnlr. Birim kare iinde rastgele alnan bir nokta, ya daire parasnn iinde
ya da dnda olur. Rastgele alnan bir noktann daire parasnn iinde olma olasl,
yarap 1 birim olan bir dairenin alannn drtte birinin, kenar 1 birim olan karenin
alanna orandr. Bu da
NTIMES
10000000
int main()
{
double x, y;
int k;
int inside_counter = 0;
srand(time(0));
for (k = 0; k < NTIMES; ++k) {
x = (double)rand() / RAND_MAX;
197
y = (double)rand() / RAND_MAX;
if (x * x + y * y <= 1)
inside_counter++;
}
printf("hesaplanan pi degeri = %lf\n", 4. * inside_counter / NTIMES);
return 0;
}
198
DZLER
Veri Yaps Nedir
Bir konuyla ilgili, mantksal iliki iindeki verilerin bellekte saklanmasna ynelik
dzenlemelere veri yaps denir. Veri yaplar bellekte belirli bir dzen iinde tutulmu
verilere ulalabilmesine, bu veriler zerinde baz ilemlerin etkin bir biimde yaplmasna
olanak salar.
Dizi Nedir
Bellekte bitiik bir biimde bulunan, ayn trden nesnelerin oluturduu veri yapsna dizi
(array) denir. Dizi veri yapsnn en nemli zellii, mantksal bir iliki iindeki ayn trden
verilerin bellekte bitiik (contigous) olarak tutulmasdr. Bunun da uygulamalarda
salad fayda udur: Dizinin bir elemanna, elemann konum bilgisiyle deimez bir
zamanda ulalabilir. Yani dizinin eleman says ne olursa olsun, konumu bilinen bir
elemana ulam zaman ayndr. Bu da baz uygulamalarn etkin bir ekilde
gerekletirilmesini kolaylatrr.
C Dilinde Diziler
C dilinde dizi (array), ayn trden bir ya da daha fazla nesnenin bellekte dizi veri yaps
biiminde tutulmasn salayan aratr.
C'de bir dizinin tanmlanmasyla birden fazla sayda nesne tek bir deyimle tanmlanabilir.
10 elemana sahip bir dizi tanmlamak yerine, phesiz isimleri farkl 10 ayr nesne de
tanmlanabilir. Ama 10 ayr nesne tanmlandnda bu nesnelerin bellekte bitiik olarak
yerlemeleri gvence altna alnm bir zellik deildir. Oysa dizi tanmlamasnda, dizinin
eleman olan btn nesnelerin bellekte bitiik olarak yer almalar gvence altna alnm
bir zelliktir. Dizi de bir veri tr olduuna gre, dizilerin de kullanlmalarndan nce
tanmlanmalar gerekir.
Dizilerin Tanmlanmas
199
int
int
int
int
int
x = 100;
a[x];
/* Geersiz */
b[5.];
/* Geersiz */
c[10 * 20];
d[sizeof(int) * 100];
Grld gibi "bir dizinin n. eleman" ve "bir dizinin n indisli eleman" terimleri dizinin
farkl elemanlarn belirtir. Bir dizinin n indisli eleman o dizinin n + 1 . elemandr.
200
Bir dizi tanmlamas ile karlaan derleyici, tanmlanan dizi iin bellekte yer ayrr.
Ayrlacak yer phesiz
dizinin eleman says * bir elemann bellekte kaplad yer
kadar byte olur. rnein:
int a[5];
gibi bir dizi tanmlamas yapldn dnelim. Windows iletim sisteminde allyorsa
derleyici a dizisi iin bellekte 4 * 5 = 20 byte yer ayrr.
Dizi indis ifadelerinde ++ ya da -- ileleri sk kullanlr:
int a[20];
int k = 10;
int i = 5;
a[k++] = 100;
deyimiyle dizinin 10 indisli elemanna yani dizinin 11. elemanna 100 deeri atanyor.
Daha sonra k deikeninin deeri 1 artrlarak 11 yaplyor.
a[--i] = 200;
deyimiyle dizinin 4 indisli elemanna yani dizinin 5. elemanna 200 deeri atanyor. Daha
sonra i deikeninin deeri 1 azaltlarak 4 yaplyor.
Keli ayra ilecinin kullanlmasyla artk dizinin herhangi bir eleman dier deikenler
gibi kullanlabilir. Aadaki rnekleri inceleyin:
a[0] = 1;
a dizisinin ilk elemanna 1 deeri atanyor.
printf("%d\n", b[5]);
b dizisinin 6. elemannn deeri ekrana yazdrlyor:
++c[3];
c dizisinin 4. elemannn deeri 1 artrlyor:
d[2] = e[4];
d dizisinin 3. elemanna e dizisinin 5. eleman atanyor:
Diziler zerinde ilem yapmak iin sklkla dng deyimleri kullanlr. Bir dng deyimi
yardmyla bir dizinin tm elemanlarna ulamak, ok karlalan bir durumdur.
Aada SIZE elemanl a isimli bir dizi iin for ve while dng deyimlerinin kullanld baz
kalplar gsteriliyor:
a dizisinin btn elemanlarna 0 deeri atanyor:
for (i = 0; i < SIZE; ++i)
a[i] = 0;
201
Dizilerin Tarlmas
Bir dizi tanmlamasn gren derleyici dizi iin bellekte dizinin tm elemanlarnn saca
byklkte bir alan ayrr:
double a[10];
Gibi bir tanmlama yapldnda, allan sistemde double trnn bellekte 8 byte yer
kaplad var saylrsa, dizi iin bellekte bitiik (contiguous) toplam 80 byte'lk bir yer
ayrlr.
Dizinin son eleman a[9] olur. ok sk yaplan bir hata, dizinin son elemanna ulamak
amacyla yanllkla bellekte derleyici tarafndan ayrlmam bir yere deer atamak, yani
diziyi tarmaktr:
a[10] = 5.;
deyimiyle bellekte ne amala kullanld bilinmeyen 8 byte' lk bir alana, yani gvenli
olmayan bir bellek blgesine deer aktarma giriiminde bulunulur. Dizi tamalar derleme
zamannda kontrol edilmez. Byle hatalar programn alma zaman ile ilgilidir.
202
Dizilere yukardaki gibi ilkdeer verildiinde, verilen deerler dizinin ilk elemanndan
balayarak dizi elemanlarna srayla atanm olur.
Dizilerin tm elemanlarna ilkdeer verme zorunluluu yoktur. Dizinin eleman saysndan
daha az sayda elemana ilkdeer verilmesi durumunda kalan elemanlara 0 deeri atanm
olur. Bu kural hem yerel hem de global diziler iin geerlidir.
Bu durumda bir dizinin btn elemanlarna 0 deeri verilmek isteniyorsa bunun en ksa
yolu aadaki gibidir:
int a[20] = {0};
Yalnzca dizinin ilk elemanna 0 ilkdeeri veriliyor. Bu durumda derleyici dizinin kalan
elemanlarna otomatik olarak 0 deeri yerletirecek kodu retir.
Dizi elemanlarna ilkdeer verilmesinde kullanlan ifadeler, deimez ifadeleri (constant
expression) olmaldr.
int a[10] = {b, b + 1, b + 2};
/* Geersiz */
/* Geersiz */
Yukardaki rnekte b dizisi 5 elemanl olmasna karn, ilkdeer verme deyiminde 6 deer
kullanlyor. Bu durum derleme zamannda hata oluturur.
lkdeer verme ileminde dizi boyutu belirtilmeyebilir. Bu durumda derleyici dizi
uzunluunu, verilen ilkdeerleri sayarak kendi hesaplar. Dizinin o boyutta aldn kabul
eder. rnein:
int a[] = {1, 2, 3, 4, 5};
Derleyici yukardaki deyimi grdnde a dizisinin 5 elemanl olduunu kabul eder. Bu
durumda yukardaki gibi bir bildirimle aadaki gibi bir bildirim edeerdir:
int a[5] = {1, 2, 3, 4, 5};
Baka rnekler :
char name[] = {'B' 'E ', 'R', 'N', 'A', '\0'};
unsigned short count[ ] = {1, 4, 5, 7, 8, 9, 12, 15, 13, 21};
Derleyici name dizisinin boyutunu 6, count dizisinin boyutunu ise 10 olarak varsayar.
Diziye ilkdeer verme listesi bir virgl atomuyla sonlandrlabilir:
int a[] = { 1, 4, 5, 7, 8, 9, 12, 15, 13,
2, 8, 9, 8, 9, 4, 15, 18, 25,
};
Bir dizi de dier nesneler gibi yerel ya da global olabilir. Yerel diziler bloklarn iinde
tanmlanan dizilerdir. Global diziler ise global isim alannda, yani tm bloklarn dnda
tanmlanr. Global bir dizinin tm elemanlar, global nesnelerin zelliklerine sahip olur.
203
Yani dizi global ise, dizi eleman olan nesneler dosya bilinirlik alanna (file scope) ve statik
mre (static storage duration) sahip olurlar. Global bir dizi sz konusu olduunda eer
dizi elemanlarna deer verilmemise, dizi elemanlar 0 deeriyle balatlr. Ama yerel
diziler sz konusu olduunda, dizi eleman olan nesneler blok bilinirlik alanna (block
scope) mr asndan ise otomatik mr karakterine (automatic storage class) sahip
olur. Deer atanmam dizi elemanlar iinde p deerler (garbage values) bulunur.
Aadaki program yazarak derleyin:
#include <stdio.h>
#define
int g[SIZE];
SIZE
10
int main()
{
int y[SIZE];
int i;
for (i = 0; i < SIZE; ++i)
printf("g[%d] = %d\n", i, g [i]);
for (i = 0; i < SIZE; ++i)
printf("y[%d] = %d\n", i, y [i]);
}
return 0;
Dizilerin elemanlar nesnedir. Ancak bir dizinin tamam bir nesne olarak ilenemez:
int a[SIZE], b[SIZE];
gibi bir tanmlamadan sonra, a dizisi elemanlarna b dizisinin elemanlar kopyalanmak
amacyla, aadaki gibi bir deyimin yazlmas szdizim hatasdr.
a = b;
/* Geersiz */
Yukardaki gibi bir atama derleme zaman hatasna neden olur. nk dizilerin isimleri
olan a ve b nesne gstermez. Dizinin bellekte kaplad toplam alan dorudan tek bir
nesne olarak ilenemez. Yani dizinin elemanlar birer nesnedir ama dizinin tamam bir
nesne deildir. C'de dizi isimleri dizilerin bellekte yerletirildikleri bloun balangcn
gsteren, dizinin tr ile ayn trden adres deerleridir. Dolaysyla deitirilebilir sol taraf
deeri (modifiable L value) deillerdir.
ki dizi birbirine ancak bir dng deyimi ile kopyalanabilir:
for (i = 0; i < SIZE; ++i)
a[i] = b[i];
Yukardaki dng deyimiyle b dizisinin her bir elemannn deeri a dizisinin e indisli
elemanna atanyor. Dizilerin kopyalanmas iin baka bir yntem de bir standart C ilevi
olan memcpy ilevini kullanmaktr. Bu ilev "gstericiler" konusunda ele alnacak.
Ayn trden nesneler bir dizi altnda tanmlanrlarsa, bir dng deyimi yardmyla dizi
elemanlarnn tamamn ileme sokan kodlar kolay bir biimde yazlabilir. Aadaki rnei
dikkatle inceleyin:
204
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SIZE
10
int main()
{
int a[SIZE];
int toplam = 0;
int k;
srand(time(0));
for (k = 0; k < SIZE; ++k) {
a[k] = rand() % 100;
printf("%d ", a[k]);
}
for (k = 0; k < SIZE; ++k)
toplam += a[k];
printf("\na elemanlari toplami = %d\n", toplam);
}
return 0;
main ilevinde yer alan ilk for dng deyimiyle a dizisinin elemanlarna standart rand
ilevi arlaryla 0 99 aralnda rastgele deerler atanyor. Yine ayn dng iinde
dizinin her bir elemannn deeri ekrana yazdrlyor. Bunu izleyen ikinci for deyimiyle a
dizisinin her bir elemannn deeri srasyla toplam isimli deikene katlyor.
Aadaki programda ise int trden bir dizinin en kk deere sahip olan elamannn
deeri bulunuyor:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define SIZE
10
int main()
{
int a[SIZE];
int toplam = 0;
int k, min;
srand(time(0));
for (k = 0; k < SIZE; ++k) {
a[k] = rand() % 100;
printf("%d ", a[k]);
}
min = a[0];
for (k = 1; k < SIZE; ++k)
if (min > a[k])
min = a[k];
printf("\nen kucuk eleman = %d\n", min);
return 0;
}
Algoritmay biliyorsunuz. min isimli deiken, dizinin en kk elemannn deerini
tutmas iin tanmlanyor. nce dizinin ilk elemannn dizinin en kk eleman olduu var
saylyor. Daha sonra bir for dng deyimiyle dizinin 1 indisli elemanndan balanarak
205
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int sum_of_odds = 0;
int sum_of_even = 0;
int no_of_odds = 0;
int k;
for (k = 0; k < SIZE; ++k)
if (a[k] % 2) {
sum_of_odds += a[k];
no_of_odds++;
}
else
sum_of_even += a[k];
if (no_of_odds)
printf("Teklerin ortalamasi = %lf\n",(double)sum_of_odds
/no_of_odds);
else
printf("Dizide tek sayi yok!\n");
if (SIZE - no_of_odds)
printf("Ciftlerin ortalamasi = %lf\n",(double)sum_of_even /(SIZE no_of_odds));
else
printf("Dizide cift sayi yok!\n");
return 0;
}
Aadaki programda bir dizi iinde arama yaplyor:
#include <stdio.h>
#define SIZE
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int k;
int searched_val;
printf("aranacak degeri girin : ");
scanf("%d", &searched_val);
for (k = 0; k < SIZE; ++k)
if (a[k] == searched_val)
break;
206
if (k < SIZE)
printf("a[%d] = %d\n", k, a[k]);
else
printf("aranan deger dizide yok!\n");
return 0;
}
searched_val isimli deiken, dizide aranacak deeri tutmak iin tanmlanyor. Bu
deikenin deeri standart scanf ileviyle klavyeden alnyor. Daha sonra oluturulan bir
for dngsyle, dizinin her bir elemannn aranan deere eitlii dng gvdesinde yer
alan bir if deyimiyle snanyor. Eer dizinin herhangi bir eleman aranan deere eit ise
break deyimi ile dngden klyor. Dng knda eer dng deikeni olan k, SIZE
deerinden kk ise aranan deer bulunmu yani dngden break deyimi ile klmtr.
Dngden break deyimi ile klmamsa k deikenin deeri SIZE deerine eit olur,
deil mi?
Dizinin elemanlar dizinin iinde srasz yer alyorsa, bir deerin dizide bulunmad
sonucunu karmak iin dizinin tm elemanlarna baklmaldr.
Ancak dizi sral ise binary search ismi verilen bir algoritmann kullanlmas arama
ileminin ok daha verimli yaplmasn salar:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
SIZE
100
int main()
{
int a[SIZE];
int k, mid, searched_val;
int val = 1;
int low = 0;
int high = 1;
srand(time(0));
for (k = 0; k < SIZE; ++k) {
a[k] = val;
val += rand() % 10;
printf("%d ", a[k]);
}
printf("\naranacak degeri girin : ");
scanf("%d", &searched_val);
while (low <= high) {
mid = (low + high) / 2;
if (a[mid] == searched_val)
break;
if (a[mid] > searched_val)
high = mid - 1;
else
low = mid + 1;
}
if (low > high)
printf("%d degeri dizide bulunamadi!\n", searched_val);
else
printf("a[%d] = %d\n", mid, searched_val);
}
return 0;
207
Yukardaki main ilevinde sral bir dizinin iinde arama yapmak amacyla binary search
isimli algoritma kullanlyor. Dizi sralanm olduuna gre, dizinin ortadaki elemanna
baklmasyla, dizideki elemanlarn yars artk sorgulama d braklr, deil mi?
low deikeni arama yaplacak dizi parasnn en dk indisini, high deikeni ise en
byk indisini tutuyor. Daha sonra low deeri, high deerinden kk ya da eit olduu
srece dnen bir while dngs oluturulduunu gryorsunuz. mid deikeni arama
yaplacak dizi parasnn ortadaki elemannn indisini tutuyor. Dizinin mid indisli
elemannn aranan deer olup olmadna baklyor. Aranan deer bulunamamsa iki
olaslk vardr: mid indisli dizi eleman aranan deerden byk ise high deikeninin
deeri mid - 1 yaplyor. Bylece arama yaplacak dizi boyutu yarya drlyor. mid
indisli dizi eleman aranan deerden kk ise low deikeninin deeri mid + 1 yaplyor.
Bylece yine arama yaplacak dizi boyutu yarya drlyor.
while dngs knda eer low deeri high deerinden bykse aranan deer
bulunamam demektir. Aksi halde dizinin mid indisli eleman aranan deerdir.
Dizilerin Sralanmas
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int i, k, temp;
for (i = 0; i < SIZE - 1; ++i)
for (k = 0; k < SIZE -1 - i; ++k)
if (a[k] > a[k + 1]) {
temp = a[k];
a[k] = a[k + 1];
a[k + 1] = temp;
}
for (k = 0; k < SIZE; ++k)
printf("%d ", a[k]);
}
return 0;
10
0
1
int main()
{
int a[SIZE] = {12, 25, -34, 45, -23, 29, 12, 90, 1, 20};
int i, k, temp, flag;
do {
flag = SORTED;
for (k = 0; k < SIZE - 1; ++k)
if (a[k] > a[k + 1]) {
208
temp = a[k];
a[k] = a[k + 1];
a[k + 1] = temp;
flag = UNSORTED;
}
} while (flag == UNSORTED);
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int i, k, temp;
for (i = 1; i < SIZE; ++i) {
temp = a[i];
for (k = i; k > 0 && a[k - 1] > temp; --k)
a[k] = a[k - 1];
a[k] = temp;
}
for (i = 0; i < SIZE; ++i)
printf("%d ", a[i]);
}
return 0;
Sralama ynn kkten bye yapmak yerine bykten ke yapmak iin iteki
dngy aadaki gibi deitirmek yeterli olur.
for (k = i; k > 0 && dizi[k - 1] < temp; --k)
Aadaki programda ise diziyi kkten bye sralamak iin "seme sralamas"
(selection sort) algoritmas kullanlyor:
#include <stdio.h>
#define SIZE
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int i, k, min, index;
for (k = 0; k < SIZE; ++k) {
min = a[k];
index = k;
for (i = k + 1; i < SIZE; ++i)
if (a[i] < min) {
209
min = a[i];
index = i;
}
a[index] = a[k];
a[k] = min;
}
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int i, k, temp;
for (i = 0; i < SIZE - 1; ++i)
for (k = 0; k < SIZE - 1 - i; ++k)
if (a[k] % 2 == a[k + 1] % 2 && a[k] > a[k + 1] ||
a[k] % 2 == 0 && a[k + 1] % 2 != 0) {
temp = a[k];
a[k] = a[k + 1];
a[k + 1] = temp;
}
for (k = 0; k < SIZE; ++k)
printf("%d ", a[k]);
}
return 0;
10
int main()
{
int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10};
int k;
for (k = 0; k < SIZE / 2; ++k) {
int temp = a[k];
a[k] = a[SIZE - 1 - k];
a[SIZE - 1 - k] = temp;
}
for (k = 0; k < SIZE; ++k)
210
return 0;
Dizinin ters evrilmesi iin dizi boyutunun yars kadar dnen bir dng iinde, dizinin
batan n. eleman ile sondan n. eleman takas ediliyor.
Aada tanmlanan urand isimli ilev her arldnda 0 - MAX deerleri arasnda farkl
bir rastgele say retiyor. levin MAX adet tamsayy rettikten sonra arldnda hata
durumunu bildirmek iin -1 deerine geri dnyor:
#include <stdio.h>
#define
MAX
100
return val;
int main()
{
int k;
srand(time(0));
for (k = 0; k < MAX; ++k)
printf("%d ", urand());
printf("\n\n%d\n", urand());
}
return 0;
lev, bir tamsaynn daha nce retilip retilmediini anlayabilmek iin flags isimli bir
global bayrak dizisini kullanyor. flags dizisinin bir elemannn deeri 0 ise o indise karlk
gelen deerin ilev tarafndan henz retilmedii anlalyor. Dizi elemannn deeri eer
1 ise, o deerin daha nce retildii anlalyor.
while (flags[val = rand() % MAX])
;
dngsnden flags dizisinin val deikenine atanan rastgele indisli bir elemannn deeri
sfr olduunda klr, deil mi?
211
Aadaki program, rastgele retilen bir saysal loto kuponunu ekrana yazyor:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
KOLON_SAYISI
void kolon_yaz()
{
int numaralar[50] = {0};
int k, no;
for (k = 0; k < 6; ++k) {
while (numaralar[no = rand() % 49 + 1])
;
numaralar[no]++;
}
for (k = 1; k < 50; ++k)
if (numaralar[k])
printf("%2d ", k);
}
int main()
{
int k;
srand(time(0));
kolon_yaz isimli ilev, tek bir kolonu ekrana yazdryor. levde numaralar isimli yerel
dizinin, yine bir bayrak dizisi olarak kullanldn gryorsunuz. Dizinin herhangi bir
indisli elemannn deerinin 0 olmas, o indis deerinin daha nce retilmeyen bir say
olduunu gsteriyor. for dngs iinde yer alan while dngs, daha nce retilmeyen
bir say bulununcaya kadar dnyor. Bylece 6 kez dnen for dngsyle 6 farkl say
retilmi oluyor.
Aadaki programda bir dizinin en byk ikinci elemannn deeri bulunuyor:
#include <stdio.h>
#define
SIZE
10
int main()
{
int a[SIZE] = {12, 34, 3, 56, 2, 23, 7, 18, 91, 4};
int k;
int max1 = a[0];
int max2 = a[1];
if (a[1] > a[0]) {
max1 = a[1];
max2 = a[0];
}
212
SIZE
100
int main()
{
int a[SIZE];
int i, k;
int counter;
srand(time(0));
for (k = 0; k < SIZE; ++k) {
a[k] = rand() % 30;
printf("%d ", a[k]);
}
printf("\n*******************************************************\n");
for (i = 0; i < SIZE; ++i) {
counter = 0;
for (k = 0; k < SIZE; ++k)
if (a[k] == a[i])
if (++counter == 2)
break;
if (counter == 1)
printf("%d ", a[i]);
}
printf("\n");
}
return 0;
50
100
213
int main()
{
int a[SIZE];
int k;
srand(time(0));
for (k = 0; k < SIZE; ++k) {
int val;
while (1) {
int i;
val = rand() % MAX;
for (i = 0; i < k; ++i)
if (val == a[i])
break;
if (i == k)
break;
}
a[k] = val;
}
/* dizi yazdrlyor */
for (k = 0; k < SIZE; ++k)
printf("%d ", a[k]);
printf("\n");
return 0;
}
Aadaki programda sral iki dizi, bir sral dizi biiminde birletiriliyor:
#include <stdio.h>
#define SIZE
10
int main()
{
int a[SIZE] = {2, 3, 6, 7, 8, 9, 13, 45, 78, 79};
int b[SIZE] = {1, 2, 4, 5, 7, 9, 10, 18, 33, 47};
int c[SIZE + SIZE];
int k;
int index1 = 0, index2 = 0;
for (k = 0; k < SIZE + SIZE; ++k)
if (index1 == SIZE)
c[k] = b[index2++];
else if (index2 == SIZE)
c[k] = a[index1++];
else {
if (a[index1] < b[index2])
c[k] = a[index1++];
else
c[k] = b[index2++];
}
for (k = 0; k < SIZE + SIZE; ++k)
printf("%d ", c[k]);
}
return 0;
214
Karakter dizileri, char trden dizilerdir. Karakter dizilerinin, baz ek zellikleri dnda,
dier dizi trlerinden bir fark yoktur. char trden diziler, daha ok, ilerinde yaz tutmak
iin tanmlanr.
char str[100];
Yukardaki tanmlamada str dizisi, btn elemanlar char trden olan 100 elemanl bir
dizidir. char trden bir dizi iinde bir yaz tutmak, dizinin her bir elemanna srayla yaznn
bir karakterinin sra numarasn atamak anlamna gelir. Bu arada char trden bir dizinin
iinde bir yaz tutmann zorunlu olmadn, byle bir dizi pekala kk tamsaylar tutmak
amacyla da kullanlabilir.
Yukarda tanmlanan dizi iinde "Ali" yazs tutulmak istensin:
str[0] = 'A';
str[1] = 'l';
str[2] = 'i';
Dizi 100 karakterlik olmasna karn dizi iinde 100 karakterden daha ksa olan yazlar da
tutulabilir. Peki dizi iinde saklanan yazya nasl eriilebilir? Yaznn uzunluk bilgisi
bilinmiyor. rnein yaz ekrana yazdrlmak istendiinde, int trden diziler iin daha nce
yazlan aadaki gibi bir dng deyiminin kullanldn dnelim:
for (k = 0; k < 100; ++k)
putchar(s[k]);
Byle bir dng ile yalnzca Ali yazs ekrana yazdrlmaz, dizinin dier 97 elemannn da
grntleri, yani p deerler ekrana yazdrlr, deil mi?
C dilinde karakterler zerinde ilemlerin hzl ve etkin bir biimde yaplabilmesi iin
"sonlandrc karakter" (null character) kavramndan faydalanlr.
Sonlandrc karakter, ASCII tablosunun ya da sistemde kullanlan karakter setinin sfr
numaral ('\x0' ya da '\0') karakteridir. Dolaysyla saysal deer olarak 0 saysna
eittir. Grnts yoktur. Sonlandrc karakter '0' karakteri ile kartrlmamaldr.
'0' karakterinin ASCII karakter setindeki kod numaras 48'dir. Dolaysyla tamsay
olarak deeri 48'dir. Oysa '\0' karakterinin ASCII sra numaras 0'dr. Dolaysyla
tamsay olarak deeri 0'dr. Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
printf("%d\n", '0');
printf("%d\n", '\0');
}
return 0;
Yukardaki ilk printf ilevinin arlmasyla ekrana 48 deeri yazdrlrken, ikinci printf
ilevinin arlmasyla ekrana 0 deeri yazdrlr.
char trden dizilere ilkdeer verme ilemi (initializing) aadaki biimlerde yaplabilir:
Dier trden dizilerde olduu gibi virgllerle ayrlan ilkdeerler, kme ayrac iinde yer
alr:
215
/* Geersiz */
Dizi eleman says kadar elemana ya da dizi eleman saysndan daha az sayda elemana
ilkdeer verilebilir. Daha az sayda elemana ilkdeer verilmesi durumunda ilkdeer
verilmemi elemanlar, dier dizilerde olduu gibi, 0 deeriyle balatlr. 0 deerinin
sonlandrc karakter olduunu biliyorsunuz:
char name[5] = {'A', 'l', 'i'};
216
Yukardaki rnekte derleyici, name dizisini 4 elemanl olarak alm varsayar. Dizinin
eleman saysndan daha fazla sayda elemana ilkdeer vermek geersizdir.
char city[5] = "stanbul";
/* Geersiz */
Bu durumun bir istisnas vardr. Eer dizinin eleman says kadar elemana ift trnak
iinde ilkdeer verilirse bu durum geerlidir. Derleyici bu durumda sonlandrc karakteri
dizinin sonuna yerletirmez.
[Bu durum C dili standartlarna yneltilen eletirilerden biridir. C++ dilinde bu durum szdizim hatasdr.]
char name[3] = "Ali";
'\0' karakteri, karakter dizileri zerinde yaplan ilemleri hzlandrmak iin kullanlr.
rnein int trden bir dizi kullanldnda, dizi elemanlar zerinde dngleri kullanarak
ilem yaparken dizi uzunluunun mutlaka bilinmesi gerekir. Ama char trden diziler sz
konusu olduunda artk dizi uzunluunu bilmek gerekmez, nk yaznn sonunda '\0'
karakter bulunacandan, kontrol ifadeleriyle bu durum snanarak yaznn sonuna gelinip
gelinmedii anlalabilir. Ancak '\0' karakterin, karakter dizilerinde yazlarn son eleman
olarak kullanlmasnn bir zarar da diziye fazladan bir karakter, yani '\0' karakteri
eklemek zorunluluudur. Bu nedenle SIZE elemanl bir dizide en fazla SIZE 1
uzunluunda bir yaz saklanabilir.
C dilinde bir yazy klavyeden alan ya da bir yazy ekrana yazan standart ilevler bulunur.
gets ilevi
Daha nce ele alnan getchar, getch ve getche ilevleri klavyeden tek bir karakter
alyorlard. gets, klavyeden karakter dizisi almakta kullanlan standart bir C ilevidir.
Kullanc, klavyeden karakterleri girdikten sonra enter tuuna basmaldr. lev, klavyeden
girilecek karakterlerin yerletirilecei dizinin ismini parametre olarak alr. Daha nce de
belirtildii gibi dizi isimleri aslnda bir adres bilgisi belirtir. gets ilevinin de parametresi
aslnda char trden bir adrestir. Ancak gstericilerle ilgili temel kavramlar henz
anlatlmad iin imdilik gets ilevini yalnzca iinize yarayacak kadar reneceksiniz.
rnein :
char s[20];
gets(s);
ile klavyeden enter tuuna baslana kadar girilmi olan tm karakterler, name dizisi iine
srayla yerletirilir. Klavyeden "Necati" yazsnn girildiini varsayalm:
gets ilevi, klavyeden girilen karakterleri diziye yerletirdikten sonra dizinin sonuna
sonlandrc karakteri yerletirir.
217
gets ilevi, dizi iin hibir ekilde dizinin tamasna ynelik bir kontrol yapmaz. gets ilevi
ile dizi eleman saysndan daha fazla karakter girilirse, dizi taaca iin beklenmeyen
sonularla karlalabilir. Bu tr durumlar gstericiler konusunda "gsterici hatalar"
bal altnda ayrntl olarak inceleyeceiz.
gets ilevi '\0' karakterini dizinin sonuna ekledii iin, SIZE boyutunda bir dizi iin gets
ileviyle alnacak karakter says en fazla SIZE 1 olmaldr. nk sonlandrc karakter
de dier karakterler gibi bellekte bir yer kaplar. rnek :
char isim[6];
gets(isim);
ile klavyeden Necati isminin girildiini dnelim:
isim dizisinin tanmlanmasyla derleyici bu dizi iin bellekte 6 byte yer ayrr (isim[0]
...isim[5]).
gets ilevi bu durumda '\0' karakterini, derleyicinin dizi iin ayrmad bir bellek
hcresine yazar. Bu tr durumlara "dizinin bir tarlmas hatas" (off bye one) denir.
Tama durumuyla ilgili olarak ortaya kacak hatalar, derleme zamanna deil alma
zamanna (run time) ilikindir.
Aadaki program inceleyin:
#include
#define
<stdio.h>
SIZE
100
int main()
{
char str[SIZE];
int ch;
int index = 0;
printf("bir yaz girin: ");
printf("\n\n");
while ((ch = getchar()) != '\n')
str[index++] = ch;
str[index] = '\0';
return 0;
}
Yukardaki main ilevinde klavyeden alnan bir yaz str dizisi iinde saklanyor. Klavyeden
'\n' karakteri alnana kadar, girilen tm karakterler str dizisinin elemanlarna srayla
atanyor. Klavyeden '\n' karakteri alndnda, diziye yazlan son karakterden sonra,
dizideki yaznn sonunu iaretlemesi amacyla '\0' karakter yazlyor.
Klavyeden alnan bir yaz, char trden bir dizinin iine standart scanf ileviyle de
yerletirilebilir. Bu amala %s format karakterleri kullanlr. Ancak bu durumda klavyeden
218
girilen karakterlerin hepsi diziye yerletirilmez. Klavyeden alnan ilk boluk karakteri ile
diziye yerletirme ilemi sona erer. Aadaki program inceleyin:
#include <stdio.h>
int main()
{
char name[20];
char fname[30];
int no;
printf("isim soyisim ve numara girin : ");
scanf("%s%s%d", name, fname, &no);
/***/
return 0;
}
Programn alma zamannda scanf ilevi arldnda aadaki giriin yapldn
dnelim. ('_' karakteri boluk karakterini gsteriyor):
__Necati___Ergin___564
Bu durumda Necati yazs name dizisine, Ergin yazs fname dizisine, 564 tamsay deeri
ise no isimli deikene yerletirilir.
puts ilevi
puts, standart bir C ilevidir. Bu ilev, bir karakter dizisinde tutulan yazy ekrana
yazdrmak iin kullanlr. Yazy saklayan karakter dizisinin ismini (dizi ismi derleyici
tarafndan otomatik olarak dizinin balang adresine dntrlmektedir) parametre
olarak alr. puts ilevi, karakter dizisini ekrana yazdktan sonra imleci sonraki satrn
bana geirir:
#include <stdio.h>
int main()
{
char name[20];
printf("bir isim girin : ");
gets(name);
puts(name);
return 0;
}
Yukardaki rnekte gets ilevi ile klavyeden alnan yaz, puts ilevi ile ekrana yazdrlyor.
Karakter dizileri iinde tutulan yazlar ekrana yazdrmak iin, standart printf ilevi de
kullanlabilir. Bu durumda formatlama karakterleri olarak %s kullanlarak dizinin ismi
(dizinin balang adresi) ile elenir.
printf("%s\n", name);
ile
puts(name);
219
ayn ii yapar. Ancak printf ilevi, dizi iinde tutulan yazy ekrana yazdrdktan sonra
imleci alt satra tamaz.
puts(name);
deyimi yerine aadaki kod paras da yazlabilirdi:
for (i = 0; name[i] != '\0'; ++i)
putchar(name[i]);
putchar('\n');
puts ilevi ve %s format karakteriyle kullanldnda printf ilevi, sonlandrc karakter
grene kadar btn karakterleri ekrana yazar. Bu durumda, yaznn sonundaki
sonlandrc karakter herhangi bir ekilde ezilirse her iki ilev de ilk sonlandrc karakteri
grene kadar yazma ilemini srdrr. Aadaki program inceleyin:
#include <stdio.h>
int main()
{
char city[] = "Ankara";
city[6] = '!';
puts(city);
}
return 0;
city[6] = '!';
atamasyla Ankara yazsnn sonundaki sonlandrc karakter ezilerek zerine ! karakteri
yazlyor. Daha sonra arlan puts ilevi ekrana
Ankara!
yazsn yazdktan sonra ilk sonlandrc karakteri grene kadar ekrana yazmay srdrr.
Gstericiler konusunda bu durumun bir gsterici hatas oluturduunu greceksiniz. puts
ve printf ilevleri, karakter dizilerini yazarken yalnzca sonlandrc karakteri dikkate alr.
Bu ilevler karakter dizilerinin uzunluklaryla ilgilenmez.
Aadaki programda bir karakter dizisi iinde tutulan yaznn uzunluu bulunuyor:
#include <stdio.h>
#define
SIZE
100
int main()
{
char str[SIZE];
int k;
int len = 0;
printf ("bir yazi girin : ");
gets(str);
printf("yazi = (%s)\n", str);
for (k = 0; str[k] != '\0'; ++k)
len++;
printf("(%s) yazisinin uzunlugu = %d\n", str, len);
220
return 0;
SIZE
100
int main()
{
char s[SIZE];
int k;
printf("bir yaz girin :");
gets(s);
for (k = 0; s[k] != '\0'; ++k)
;
for (--k; k >= 0; --k)
putchar(s[k]);
}
return 0;
Aadaki programda nce bir karakter dizisine bir yaz alnyor. Daha sonra yaznn
kk harf karakterleri byk harfe, byk harf karakterleri kk harfe dntrlyor:
#include <stdio.h>
#include <ctype.h>
#define
SIZE
100
int main()
{
char str[SIZE];
int k;
printf ("bir yazi girin : ");
gets(str);
printf("yazi = (%s)\n", str);
for (k = 0; str[k] != '\0'; ++k)
str[k] = isupper(str[k]) ? tolower(str[k]) : toupper(str[k]);
printf("donustulmus yazi = (%s)\n", str);
return 0;
}
Aadaki programda bir karakter dizisine klavyeden alnan yaz ters evriliyor:
221
#include <stdio.h>
#define
SIZE
100
int main()
{
char str[SIZE];
int k, temp, len;
printf ("bir yazi girin : ");
gets(str);
for (len = 0; str[len] != '\0'; ++len)
;
Yukardaki kodda kullanlan algoritmay inceleyin. Birinci for dngs ile yaznn uzunluu
bulunuyor. Daha sonra yaznn uzunluunun yars kadar dnen bir for dng deyimi
oluturuluyor. Dngnn her turunda yaznn batan n. karakteri ile sondan n. karakteri
yer deitiriliyor. Yaz uzunluu tek say ise, yaznn ortasndaki karakter yerinde kalr.
Yaznn sonundaki sonlandrc karakter
str[len]
olduuna gre, yaznn son karakteri
str[len 1]
karakteridir, deil mi?
Aadaki programda ise klavyeden girilen bir yaznn iinde bulunan tm ngilizce harfler
saylyor ve ka tane olduklar ekrana yazdrlyor:
#include <stdio.h>
#include <ctype.h>
#define
SIZE
500
int main()
{
char str[SIZE];
int letter_counter[26] = {0};
int k;
printf("bir yazi girin : ");
gets(str);
for (k = 0; str[k] != '\0'; ++k)
if (isalpha(str[k]))
letter_counter[toupper(str[k]) - 'A']++;
for (k = 0; k < 26; ++k)
if (letter_counter[k])
printf("%3d tane %c\n", letter_counter[k], 'A' + k);
return 0;
222
main ilevi iinde kullanlan letter_counter isimli dizi, bir saya dizisi olarak kullanlyor.
Dizinin 1. eleman 'A', 'a' karakterlerinin, dizinin 2. eleman 'B', 'b' karakterlerinin,
dizinin sonuncu eleman 'Z', 'z' karakterlerinin sayac olarak grev yapyor. Bu yerel
dizi, ilkdeer verme deyimiyle sfrlanyor. lk for dng deyimiyle, yaznn tm
karakterleri dolalyor, yaznn herhangi bir karakteri eer bir harf karakteri ise, byk
harfe dntrlerek bu karakterden 'A' deeri karlyor. Elde edilen deerin
letter_counter dizisine indis yapldn ve letter_counter dizisinin bu indisli elemannn
deerinin 1 artrldn gryorsunuz.
kinci for dng deyimiyle ise bu kez saya dizisinin, deeri 0 olmayan elemanlarnn
deerleri ekrana yazdrlyor.
Aadaki programda ise bir diziye alnan bir yaz iinden rakam karakterleri siliniyor.
Kodu inceleyin:
#include <stdio.h>
#include <ctype.h>
#define
SIZE
500
int main()
{
char str[SIZE];
int k;
int index = 0;
printf("bir yazi girin : ");
gets(str);
printf("yazi = (%s)\n", str);
for (k = 0; str[k] != '\0'; ++k)
if (!isdigit[k])
str[index++] = str[k];
str[index] = '\0';
printf("yazi = (%s)\n", str);
}
return 0;
Yazdan rakam karakterlerini silmek iin yaz, bulunduu yere yeniden kopyalanyor.
Ancak kopyalama yaplrken, rakam karakterleri kopyalanmyor. index isimli deiken,
dizinin neresine yazlacan gsteriyor. Eer bir karakter rakam karakteri deilse, bu
karakter dizinin index indisli elemanna atanyor, sonra index deikeninin deeri 1
artrlyor. Ancak yaznn tamamn dolaan for dngsnden ktktan sonra, silme
ileminden sonra oluan yaznn sonuna, sonlandrc karakter ekleniyor.
Aadaki programda bir yaznn toplam szck says bulunuyor:
#include <stdio.h>
#define
#define
#define
SIZE
OUTWORD
INWORD
200
0
1
223
{
char str[SIZE];
int word_counter = 0;
int k;
int word_flag = OUTWORD;
printf("bir yazi girin : ");
gets(str);
for (k = 0; str[k] != '\0'; ++k)
if (is_sep(str[k]))
word_flag = OUTWORD;
else if (word_flag == OUTWORD) {
word_flag = INWORD;
word_counter++;
}
printf("toplam %d sozcuk var!\n", word_counter);
}
return 0;
SIZE
100
int main()
{
char str[SIZE];
int index = 0;
int k;
printf("bir yazi girin : ");
gets(str);
for (k = 0; str[k] != '\0'; ++k)
224
225
sizeof leci
sizeof, bir ifadenin trnn bellekte ka byte yer kaplad deerini reten bir iletir.
sizeof ileci tek terimli nek konumunda bir iletir.
sizeof ilecinin terimi aadakilerden biri olabilir:
1. Terim olarak tr belirten szckler kullanlabilir. Bu durumda terimin ayra iine
alnmas zorunludur. rnekler:
sizeof(int)
sizeof(double)
sizeof(long)
le bu durumda terimi olan tr bilgisinin kullanlan sistemde ka byte yer kaplayaca
deerini retir. rnein Windows ya da UNIX sistemlerinde
sizeof(int)
gibi bir ifadenin deeri 4'tr.
2. Terim olarak bir ifade kullanlabilir. Bu durumda terimin ayra iine alnmas zorunlu
deildir. Ancak programclarn ou okunabilirlik asndan terimi ayra iine almay
yeler:
double x;
sizeof (x)
sizeof(17.8)
sizeof(func())
le bu durumda, terimi olan ifadenin ait olduu trn, kullanlan sistemde ka byte yer
kaplayaca deerini retir. rnein Windows ya da UNIX sistemlerinde
sizeof(x)
gibi bir ifadenin deeri 8'dir. Byle bir ifade doal olarak, ilgili sistemde x nesnesinin
bellekte ka byte yer kapladn belirlemekte de kullanlabilir. sizeof ileci en ok bu
biimiyle kullanlr. Yani ilecin terimi nesne gsteren bir ifade seilerek, terimi olan
nesnenin bellekte ka byte yer kaplad renilir.
3. sizeof ileci terim olarak bir dizi ismi aldnda, byte olarak o dizinin toplam
uzunluunu deer olarak retir:
double a[10];
sizeof(a)
ifadesi 80 deerini retir.
Dier taraftan sizeof ilecinin terim olarak dizinin bir elemann alarak rettii deer, dizi
hangi trden ise o trn kullanlan sistemdeki byte olarak uzunluu olur. Yani yukardaki
rnekte
sizeof(a[0])
ifadesi 8 deerini retir.
Bu durumda
226
sizeof(a) / sizeof(a[0])
ifadesi dizi boyutunu verir. rnek:
for (i = 0; i < sizeof(a) / sizeof(a[0]); ++i)
a[i] = 0;
a bir dizi ismi olmak zere, yukardaki dng, a dizisinin eleman says kadar dner.
Yukardaki dngde dizi boyutunun ak biimde yazlmas yerine
sizeof(a) / sizeof(a[0]
biiminde yazlmas size artc gelebilir. Byle bir yazm biiminin bir faydas olabilir mi?
Dizi tanmlamalarnda, ilkdeer verilen dizilerin boyutlarnn belirtilmesine gerek
olmadn, derleyicinin dizi boyutunu verilen ilkdeerlerin saysndan kardn
biliyorsunuz. Aadaki kodu inceleyin:
#include <stdio.h>
int main()
{
int a[] = {2, 5, 7, 8, 9, 23, 67};
int k;
for (k = 0; k < sizeof(a) / sizeof(a[0]); ++k)
printf("%d ", a[k]);
printf("\n");
}
return 0;
Yukardaki main ilevi iinde a isimli int trden bir dizi tanmlanyor. Tanmlanan diziye
ilkdeer veriliyor. Derleyici verilen ilkdeerlerin saysn sayarak dizinin boyutunu 8 olarak
saptar ve kodu buna gre retir. main ilevi iinde yer alan for dng deyimi, dizinin
eleman says kadar, yani 8 kez dner.
imdi kaynak kodda deiiklik yapldn, a dizisine birka eleman daha eklendiini
dnelim:
int a[] = {2, 5, 7, 8, 9, 23, 67, 34, 58, 45, 92};
Bu durumda for dng deyiminde bir deiiklik yaplmasna gerek kalmaz. nk
derleyici bu kez dizinin boyutunu 11 olarak hesaplar. for dng deyimi iinde kullanlan
sizeof(a) / sizeof(a[0]
ifadesi de bu kez 11 deerini retir.
Tek terimli tm ilelerin, ile ncelik tablosunun ikinci seviyesinde yer aldn
biliyorsunuz. sizeof da ikinci seviyede bulunan bir iletir.
sizeof ilecinin terimi ou kez bir ayra iine yazldndan, ilecin kullanm bir ilev
ars grntsne benzer:
sizeof(y)
Ancak sizeof bir ilev deil bir iletir. sizeof C dilinin 32 anahtar szcnden biridir.
227
sizeof ilecinin rettii deer unsigned int trdendir. lecin rettii deerin trn signed
int kabul etmek hataldr. lecin rettii deer, signed int trden negatif bir sayyla
ileme sokulduunda tr dnm iaretsiz yne yaplr:
-2 * sizeof(int)
-2, iaretli int trden bir deimezdir. sizeof(int) ifadesinin rettii deer ise unsigned int
trden 4 deeridir. lem ncesi yaplacak otomatik tr dnm ile -2 deeri unsigned
int trne dntrlr. lem unsigned int trde yaplr. Yani ilemin sonucu 8 olmaz.
[Aslnda sizeof operatrnn rettii deer standart bir typedef tr olan size_t trndendir. Standart typedef
trleri "Tr simleri Bildirimleri ve typedef Belirleyicisi " isimli blmde ele alnyor.]
sizeof ilecinin terimi olan ifade yan etki gstermez. Aadaki rnei inceleyin:
#include <stdio.h>
int func()
{
printf("func()\n");
}
return 1;
int main()
{
unsigned int x = sizeof(func());
printf("x = %u\n", x);
}
return 0;
main ilevi iinde, func ilevi arlmaz. sizeof ileci, terimi olan ifadeye yalnzca bir tr
bilgisi olarak bakar. rnek kodda yer alan
func()
ifadesinin tr int trdr.
Belirli bir trden nesnenin bellekte ka byte yer kaplayaca, sistemden sisteme farkllk
gsterebilir. Tr uzunluu gvence altnda bulunan tek doal tr, char trdr. char
trden bir nesne tm sistemlerde 1 byte yer kaplar. Tr uzunluklarnn sistemden sisteme
farkl olabilmesi, baz uygulamalarda tanabilirlik sorunlarna yol aabilir. sizeof ilecinin,
genel olarak bu tr tanabilirlik sorunlarn ortadan kaldrmaya ynelik olarak kullanld
sylenebilir.
228
GSTERCLER
Birinci blmde yazlan kodlarda nesnelerin deerleri kullanld. rnein deikenlerin
deerleri ilevlere argman olarak gnderildi. levler nesnelerin deerlerini geri
dndrd. "Gstericiler" (pointers) ile artk nesnelerin deerlerinin yan sra nesnelerin
adresleri zerinde de durulacak.
Nasl, oturduunuz evin adresinden sz ediliyorsa, programda kullandnz nesnelerin de
adreslerinden sz edilebilir. Bir nesnenin adresi o nesnenin bellekteki konumunu gsteren
bir bilgidir.
te C dilinde yazlan birok kod, nesnelerin adresi olan bilgileri kullanlr. Yazlmsal baz
amalarn gerekletirilmesi iin, nesnelerin adresleri deikenlerde saklanr, ilevlere
gnderilir, ilev arlaryla ilevlerden geri dn deeri olarak elde edilir.
Her nesne bellekte yer kapladna gre belirli bir adrese sahiptir. Nesnelerin adresleri,
sistemlerin ounda, derleyici ve program ykleyen iletim sistemi tarafndan ortaklaa
belirlenir. Nesnelerin adresleri program yklenmeden nce kesin olarak bilinemez ve
programc tarafndan da nceden saptanamaz. Programc nesnelerin adreslerini ancak
programn almas srasnda (run time) renebilir. rnein:
char ch;
Gibi bir tanmlamayla karlaan derleyici bellekte ch deikeni iin 1 byte yer ayrr.
Derleyicinin ch deikeni iin bellekte hangi byte' ayraca nceden bilinemez. Bu ancak
programn almas srasnda renilebilir. Yukardaki rnekte ch deikeninin yerel yerel
olduunu dnelim. ch deikeni, tanmlanm olduu bloun kodu yrtlmeye
balandnda yaratlr, bloun kodunun yrtlmesi bittiinde de mr sona erer.
Aadaki ekil ch deikeninin 1A02 adresinde olduu varsaylarak izilmitir:
1A00
1A01
ch
1A02
1A03
1A04
Tanmlanan nesne 1 byte'dan daha uzunsa, o zaman nesnenin adresi nasl belirlenir?
int b;
1 byte'tan uzun olan nesnelerin adresleri, onlarn ilk byte'larnn adresleriyle belirtilir.
Yukardaki rnekte b deikeninin adresi 1C02'dir. Zaten b deikeninin int trden olduu
bilindiine gre dier parasnn 1C03 adresinde olaca da aktr (int trden bir
nesnenin ilgili sistemde 2 byte yer kaplad varsaylyor).
Benzer biimde long trden olan y deikeninin bellekteki yerleiminin aadaki gibi
olduu varsaylrsa, adresinin 1F02 olduu sylenebilir:
229
Gsterici Nesneler
230
Bir gsterici nesnesinin tanm ile karlaan derleyici dier tanmlamalarda yapt gibibellekte o gsterici deikeni iin yer ayrr. Derleyicilerin gstericiler iin ayrdklar
yerlerin uzunluu donanma bal olup sistemden sisteme deiebilir. 32 bit sistemlerde
(rnein UNIX ve Windows 3.1 sonras sistemlerde) gsterici deikenler 4 byte
uzunluundadr. 8086 mimarisinde ve DOS altnda alan derleyicilerde ise gsterici
deikenler 2 byte ya da 4 byte olabilirler. DOS'ta 2 byte uzunluundaki gstericilere
yakn gstericiler (near pointer), 4 byte uzunluundaki gstericilere ise uzak gstericiler
(far pointer) denir.
Gstericilerin uzunluklar trlerinden bamszdr.
231
return 0;
Yukardaki programda hem char, int, double trlerinden hem de char *, int *, double *
trlerinden nesnelerin tanmlandn gryorsunuz. Daha sonra printf ileviyle bu
nesnelerin sizeof deerleri ekrana yazdrlyor. T trnden bir nesnenin sizeof deeri ne
olursa olsun T* trnden bir nesnenin sizeof deeri hep ayndr, deil mi? Yukardaki
program UNIX iletim sistemi iin derlenip altrldnda ekran kts aadaki gibi
olur:
sizeof(ch) = 1
sizeof(i) = 4
sizeof(d) = 8
sizeof(cp) = 4
sizeof(ip) = 4
sizeof(dp) = 4
sizeof(char *) = 4
sizeof(int *) = 4
sizeof(double *) = 4
Baz ifadeleri adres trndendir. Yani bu ifadelerin deeri adrestir. Bir gsterici
deikene, tr adres olan ifade yani bir adres deeri atanmaldr.
int *ptr;
gibi tanmlanan bir deiken, iinde int trden bir deikenin adresi olan bilgiyi
saklayacak deikendir. Byle bir adres bilgisi ptr deikenine nasl atanabilir?
Adres Deimezleri
1200,int trden bir tamsay deimezidir. Byle bir deimez, rnein int trden bir
nesneye atanabilir:
int x = 1200;
Tr dntrme ilemiyle bir tamsay deimezi bir adres bilgisine dntrlebilir:
(int *)1200
232
Yukardaki ifadenin tr (int *) trdr. Byle bir ifade int trden bir nesnenin adresi
olabilecek bir bilgidir. int trden 1200 deimezi tr dntrme ileciyle int trden bir
nesnenin adresi olabilecek bir tre dntrlmtr. Adres deimezlerinin yazmnda
geleneksel olarak onaltlk say sistemi kullanlr:
(double *)0x1AC4
Yukardaki ifade, double trden bir nesnenin adresi olabilecek bir bilgidir. Adres bilgisinin
tamsay ksmnn yazlmasnda onaltlk say sisteminin kullanldn gryorsunuz.
Bir gsterici deikene ayn trden bir adres bilgisi yerletirilmelidir. rnein :
int *p;
p = 100;
Burada p gsterici deikenine adres olmayan bir deer atanyor. Byle bir atamann
yanl olduu kabul edilir. Derleyicilerin hemen hepsi bu durumu mantksal bir uyar iletisi
ile bildirir. Bu durum ileride ayrntl olarak ele alnacak.
[Byle bir atama C++ dilinde geerli deildir.
int *p;
p = (char *) 0x1FC0;
Burada int trden p gstericisine char trden bir adres bilgisi atanyor. Yanl olan bu
durum da derleyicilerin ou tarafndan mantksal bir uyar iletisi ile iaretlenir.
[C++ derleyicileri byle bir atama durumunda da szdizim hatas iletisi vererek ama dosya retimini reddeder]
int *p;
p = (int *) 0x1FC4;
Bir adres bilgisi gstericiye atandnda adresin saysal bileeni gsterici iine yerletirilir.
int *p;
p = (int *) 0x1FC4;
Burada bellekte p gsterici deikeninin tutulduu yere 0x1FC4 saysal deeri yerletirilir.
Gsterici deikenler ilerinde adres bilgileri tadna gre bir gstericiye ayn trden
baka bir gsterici deikenin deerinin atanmas da tamamen uygundur.
int *p, *q;
p = (int *) 0x1AA0;
q = p;
Yukardaki atama ile q gstericisine p gstericisinin deeri atanyor. Yani bu atama
deyiminden sonra q gstericisinin de iinde (int *) 0x1AA0 adresi bulunur.
int k;
233
gibi bir tanmlama yapldnda k deikeni int trdendir. indeki deer int trden bir
deer olarak yorumlanr.
20
gibi, tek bir deimezden oluan bir ifade de int trdendir, nk 20 int trden bir
deimezdir. Baka bir deyile k ifadesiyle 20 ifadesinin trleri ayndr. Her iki ifadenin
tr de int trdr. Ancak k ifadesi nesne gsteren bir ifade iken 20 ifadesi nesne
gstermeyen bir ifadedir, yani bir sa taraf deeridir.
Yine bir adres deimezinden oluan
(int *) 0x1A00
ifadesinin tr de int trden bir adrestir, yani (int *) trnden bir ifadedir. Ancak bu
ifade de sol taraf deeri deildir.
Grld gibi gsterici deikenleri, belirli bir adres trnden nesnelerdir. Yani deerleri
adres olan deikenlerdir.
234
Gsterici leleri
C dilinin baz ileleri adres bilgileri ile ilgili olarak kullanlr. Gstericiler ile ilgili kodlar bu
ileleri kullanr. Gsterici ileleri unlardr:
*
&
[]
->
ierik ileci
adres ileci
keli ayra ileci
ok ileci
ok ileci yap trnden adreslerle kullanld iin bu ile "yaplar" konusunda ayrntl
olarak incelenecek.
Adres leci
Adres ileci (adress of operator), nek konumunda tek terimli (unary prefix) bir iletir.
le ncelik tablosunun ikinci seviyesinde yer alr. Bu ilecin rettii deer, terimi olan
nesnenin adresidir. Adres ilecinin terimi mutlaka bir nesne olmaldr. nk yalnzca
nesnelerin -sol taraf deerlerinin- adres bilgilerine ulalabilir. Adres ilecinin teriminin
nesne olmayan bir ifade olmas geersizdir.
int k;
gibi bir tanmalamadan sonra yazlan
&k
ifadesini ele alalm. Bu ifadenin rettii deer int trden bir adres bilgisidir.
Bu ifadenin tr (int *) trdr.
& ileci dier tek terimli ileler gibi, ile ncelik tablosunun 2. seviyesinde bulunur. Bu
ncelik seviyesinin ncelik ynnn "sadan sola" olduunu biliyorsunuz.
Bir gsterici deikeni, iinde bir adres bilgisi tutan bir nesne olduuna gre, bir gsterici
deikene adres ilecinin rettii bir adres bilgisi atanabilir.
int x = 20;
int *ptr;
ptr = &x;
Byle bir atamadan sonra unlar sylenebilir:
ptr nesnesinin deeri x deikeninin adresidir. ptr nesnesi x deikeninin adresini tutar.
Adres ileci ile elde edilen adres, ayn trden bir gsterici deikene atanmaldr. rnein
aadaki programda bir gsterici deikene farkl trden bir adres atanyor:
char ch = 'x';
int *p;
p = &ch;
/* Yanl */
Tabi bu ilecin rettii adres bilgisi de sol taraf deeri deildir. rnein:
int x;
++&x
/* Geersiz */
gibi bir ilem hata ile sonulanr. Artrma ilecinin terimi nesne olmaldr. Yukardaki
ifadede ++ ilecinin terimi olan &x ifadesi bir nesne deildir. Yalnzca bir adres deeridir.
235
Standart printf ilevi ile doal veri trlerinden ifadelerin deerlerinin ekrana
yazdrlabileceini biliyorsunuz. Bir ifadenin deerini ekrana yazdrmak iin, printf
ileviyle birinci argman olarak geilen dizge iinde nceden belirlenmi format
karakterlerinin (conversion specifiers) kullanldn hatrlayn. Acaba bir adres bilgisi de
uygun format karakteri kullanlarak ekrana yazdrlabilir mi? Evet! Standart printf
ilevinde bu ama iin %p format karakterleri kullanlr. %p format karakterleri ile
elenen argman bir adres bilgisi ise, printf ilevi ilgili adres bilgisinin yalnzca saysal
bileenini onaltlk say sisteminde ekrana yazdrr.
Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
int *ptr;
int x = 20;
ptr = &x;
printf("x nesnesinin adresi = %p\n", &x);
printf("ptr deikeninin deeri = %p\n", ptr);
printf("ptr nesnesinin adresi = %p\n", &ptr);
}
return 0;
ptr bir nesne olduu iin ptr nesnesi de adres ilecinin terimi olabilir, deil mi? ptr
nesnesinin deeri olan adres, x nesnesinin adresidir. Ama ptr nesnesinin kendi
adresinden de sz edilebilir. Bir gsterici deikenin deeri olan adres ile gsterici
deikenin kendi adresi farkl eylerdir.
printf("ptr nesnesinin adresi = %p\n", &ptr);
arsyla ptr deikeninin kendi adresi ekrana yazdrlyor.
C dilinde dizi isimleri bir ileme sokulduunda derleyici tarafndan otomatik olarak bir
adres bilgisine dntrlr.
char s[5];
gibi bir dizi tanmlamasnda sonra, dizinin ismi olan s bir ileme sokulduunda bu dizinin
ilk elemannn adresine dntrlr.
Dizi isimleri derleyici tarafndan, diziler iin bellekte ayrlan bloklarn balang yerini
gsteren bir adres bilgisine dntrlr. Yukardaki rnekte dizinin bellekte aadaki
ekilde yerletirildiini dnn:
s[0]
s[1]
s[2]
s[3]
s[4]
1C00
1C01
1C02
1C03
1C04
Bu durumda dizi ismi olan s, char trden 1C00 adresine edeerdir. Yani bu adresi bir
adres deimezi eklinde yazlm olsayd:
(char *)0x1COO
236
biiminde yazlrd.
Bu durumda s ifadesi ile &s[0] ifadesi ayn adres bilgisidir, deil mi?
Gsterici deikenlere kendi trlerinden bir adres bilgisi atamak gerektiine gre
aadaki atamalarn hepsi geerli ve dorudur:
int a[100];
long l[20];
char s[100];
double d[10];
int *p;
long *lp;
char *cp;
double *dp;
p = a;
lp = l;
cp = s;
dp = d;
Bir gstericiye yalnzca ayn trden bir dizinin ismi atanabilir. rnein:
int *p;
char s[] = "Necati";
p = s;
/YANLI */
237
++s;
deyimi geersizdir.
* erik leci
erik ileci (indirection operator / dereferencing operator) de, nek konumunda bulunan
tek terimli (unary prefix) bir iletir. erik ilecinin terimi bir adres bilgisi olmaldr. Bir
adres ifadesi, * ilecinin terimi olduunda, elde edilen ifade bellekte o adreste bulunan,
nesneyi temsil eder. Dolaysyla, * ileci ile oluturulan bir ifade bir nesneye karlk gelir,
sol taraf deeri olarak kullanlabilir.
int a;
gibi bir bildirimde a nesnesinin tr int trdr. nk a nesnesi iinde int trden bir veri
tutulur.
int *p;
bildiriminde p'nin tr int trden bir adrestir. Yani p nesnesinin tr (int *) trdr. p
nesnesinin iinde (int *) trnden bir veri tutulur.
char *ptr;
gibi bir bildirimden iki ey anlalr:
ptr char trden bir gstericidir. ine char trden bir adres bilgisi yerletirilmek iin
tanmlanmtr. ptr gstericisi * ileci ile birlikte kullanldnda elde edilen nesne char
trdendir. Yani *ptr char trden bir nesnedir.
rnein:
int *p;
p = (int *) 0x1FC2;
*p = 100;
Burada *p'nin tr int trdr. Dolaysyla *p = 100 gibi bir ilemden (DOS altnda)
yalnzca 0x1FC2 byte' deil, 0x1FC2 ve 0x1FC3 byte'larnn her ikisi birden etkilenir.
Gstericinin iindeki adresin saysal bileeni nesnenin dk anlaml byte'nn adresini
ierir. Bu durumda bir gsterici deikene, bellekteki herhangi bir blgenin adresi
atanabilir. Daha sonra * ileci ile o bellek blgesine eriilebilir.
* ilecinin terimi bir adres bilgisi olmak zorundadr. Yani terim adres deimezi olabilir,
dizi ismi olabilir, bir gsterici deiken olabilir veya adres ileci ile elde edilmi bir adres
ifadesi olabilir.
erik ileci yalnz gsterici nesneleriyle deil, adres bilgisinin her biimi ile (adres
deimezleri ve dizi isimleri vs.) kullanlabilir. Bu ile, terimi olan adresteki nesneye
erimekte kullanlr. le ile elde edilen deer, terimi olan adreste bulunan nesnenin
deerdir.
erik ileci ile retilen bir ifade nesne belirtir. Nesnenin tr terim olan nesnenin adresi
ile ayn trdendir.
Aadaki program derleyerek altrn, ekran ktsn yorumlayn:
#include <stdio.h>
int main()
{
char s[] = "Balkesir";
238
return 0;
Yukardaki programda,
i) s char trden bir dizinin ismi olduuna gre char trden bir adrese dntrlr. Bu
adres s dizisinin balang adresidir. *s ifadesi bu adresteki nesne olduuna gre, *s
ifadesi dizimizin ilk eleman olan nesnedir, yani *s ifadesinin deeri 'B'dir, deil mi?
ii) a int trden bir dizinin ismi olduuna gre int trden bir adrese dntrlr. Bu adres
a dizisinin balang adresidir. *a ifadesi bu adresteki nesne olduuna gre, *a ifadesi int
trden dizimizin ilk eleman olan nesnedir, yani *a ifadesi a[0] nesnesidir. Bu nesnenin
deeri 1'dir.
iii) *&x ifadesinde ise iki ayr ile kullanlyor. Adres ve ierik ileleri. Bu ilelerin her
ikisi de ile ncelik tablosunda ikinci seviyede yer alyor. le ncelik tablosunun ikinci
seviyesine ilikin ncelik yn sadan sola olduuna gre, ifadenin deerlendirilmesinde
nce adres ileci deer retir. Adres ilecinin rettii deer x nesnesinin adresidir, ierik
ilecinin terimi bu adres olur. erik ileci o adresteki nesneye ulatna gre *&x ifadesi
x nesnesinin adresindeki nesne, yani x nesnesinin kendisidir.
iv) ptr gstericisine x nesnesinin adresi atanyor. erik ilecinin terimi ptr nesnesi
olduundan, ptr nesnesinin deeri olan adresteki nesneye ulalr. Bu durumda da *ptr
nesnesi yine x nesnesinin kendisidir, deil mi?
erik ilecinin ncelik tablosunun ikinci dzeyinde olduunu biliyorsunuz. s bir dizi ismi
olmak zere
*s + 1;
ifadesinde nce ierik ileci deer retir. erik ilecinin rettii deer toplama ilecinin
terimi olur. Oysa ifade
*(s + 1)
biiminde olsayd nce + ileci ele alnrd.
Derleyiciler * atomu ieren bir ifadede * atomunun arpma ileci mi yoksa adres ileci
mi olduunu ifade iindeki kullanmna bakarak anlar. arpma ileci iki terimli iken ierik
ileci tek terimli nek konumunda bir iletir.
*s * 2
ifadesinde birinci '*' ierik ileci iken ikincisi * aritmetik arpma ilecidir.
239
Gstericiler daha ok bir ilevin parametre deikeni olarak kullanlr. Bir gsterici bir
nesne olduuna gre bir ilevin parametre deikeni herhangi bir trden gsterici olabilir:
void func(int *p)
{
/***/
}
levlerin parametre deikenleri, ilev arlaryla kendilerine geilen argman
ifadeleriyle ilkdeerlerini aldna gre, bir ilevin parametre deikeni bir gsterici ise
ilev de ayn trden bir adres bilgisi ile arlmaldr.
Byle bir ilev, parametre deikenine adresi kopyalanan yerel bir deikenin deerini
deitirebilir:
#include <stdio.h>
void func(int *ptr)
{
*ptr = 20;
}
int main()
{
int a = 10;
func(&a);
printf("%d\n", a);
}
return 0;
Yukardaki rnekte main ilevi iinde tanmlanan yerel a isimli deikenin adresi func
ilevine gnderiliyor. func ilevi arldnda, yaratlan parametre deikeni ptr
ilkdeerini &a ifadesinden alr. levin koduna geildiinde artk parametre deikeni olan
ptr gsterici deikeni, adresi gnderilen a nesnesini gsterir. Bu durumda
*ptr
ifadesi a nesnesinin kendisidir.
240
*ptr = 20;
deyimiyle a nesnesine 20 deeri atanr, deil mi?
Bir ilev bir deer elde edip aran ileve bu deeri iletmek isterse iki yntem
kullanlabilir:
i. Elde edilen deer arlan ilev tarafndan geri dn deeri olarak retilir.
ii. Elde edilen deer aran ilevin gndermi olduu adrese yerletirilir. Tabi bunun iin
arlan ilevin parametre deikeninin bir gsterici olmas gerekir. Aada kendisine
gnderilen bir saynn faktoriyelini hesaplayarak bu deeri parametre olarak gnderilen
adrese kopyalayan bir ilev yazlyor:
#include <stdio.h>
void factorial(int n, long *p);
int main()
{
long a;
int k;
for (k = 0; k < 14; ++k) {
factorial(k, &a);
printf ("%2d! = %ld\n", k, a);
}
}
return 0;
241
C'de "adres ile arma" (call by reference) denir. Byle bir arda ileve bir nesnenin
adresi gnderilir. levin parametre deikeni de bu adresi tutacak bir gsterici olur.
int trden iki yerel nesnenin deerleri takas edilmek istensin. Bu i, bulunulan ilev iinde
aadaki gibi yaplabilir:
int main()
{
int a = 10, b = 20, temp;
temp = a;
a = b;
b = temp;
/*....*/
Takas ileminin bir ilev tarafndan yaplmas istenirse, aadaki gibi bir ilev i grr
myd?
void swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
int main()
{
int a = 10, b = 20;
swap(a, b);
printf("a = %d\nb = %d\n", a, b);
}
return 0;
242
C dilinde bir adres bilgisi bir tamsay ile toplanabilir, bir adres bilgisinden bir tamsay
kartlabilir. Byle bir ifade toplanan ya da kartlan adres trndendir. rnein int
trden bir nesnenin adresi ile 1 tamsays toplanrsa yine int trden bir nesnenin adresi
bilgisi elde edilir.
Bir adres bilgisine 1 toplandnda, adresin saysal bileeni adrese sahip nesnenin
trnn uzunluu kadar artar. Bu durumda rnein DOS iletim sisteminde char trden
bir gstericinin deeri, 1 artrldnda adresin saysal bileeni 1, int trden bir gsterici 1
artrldnda ise adresin saysal bileeni 2 artar, double trden bir gsterici 1
artrldnda ise adresin saysal bileeni 8 artar.
Bir gsterici deikenin bellekte bir nesneyi gsterdiini dnelim. Bu gsterici
deikenin deeri 1 artrlrsa bu kez gsterici deikeni, gsterdii nesneden bir sonraki
nesneyi gsterir duruma gelir.
#include <stdio.h>
int main()
{
int k;
int a[10];
for (k = 0; k < 10; ++k) {
*(a + k) = k;
printf("%d ", a[k]);
}
}
return 0;
243
printf("%p\n",
printf("%p\n",
printf("%p\n",
printf("%d\n",
printf("%d\n",
printf("%d\n",
}
return 0;
Daha nce dizi elemanlarna erimekte kullandmz keli ayra aslnda iki terimli bir
gsterici ilecidir. Keli ayra ileci (index / subscript operator) ile ncelik tablosunun
en yksek ncelik seviyesindedir. lecin birinci terimi keli ayratan nce yer alr. Bu
terim bir adres bilgisi olur. kinci terim ise keli ayra iine yazlacak tam say trnden
bir ifade olur.
p[n]
ifadesi ile
*(p + n)
tamamen edeer ifadelerdir.
Yani keli ayra ileci, bir adresten n ilerideki nesneye erimek iin kullanlr. [] ileci ile
elde edilen nesnenin tr terimi olan adresin tr ile ayn trdendir. Aadaki programn
ekrana ne yazdracan nce tahmine etmeye aln. Daha sonra program derleyip
altrn:
244
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int *ptr = a + 2;
printf("%d\n", ptr[-1]);
printf("%d\n", ptr[0]);
printf("%d\n", ptr[1]);
}
return 0;
[] ilecinin birinci terimi dizi ismi olmak zorunda deildir. Daha nce de belirtildii gibi bir
dizinin ismi bir ifade iinde kullanldnda derleyici tarafndan o dizinin ilk elemannn
adresine yani dizinin balang adresine dntrlr.
[] ileci ile ncelik tablosunun en yksek dzeyinde bulunur. rnein:
&p[n]
ifadesinde nce keli ayra ileci deer retir. lecin rettii deer, bir nesneye
ilikindir. Adres ilecinin ncelik seviyesi keli ayra ilecinden daha dk olduu iin,
ilecin ulat nesne bu kez adres ilecinin terimi olur.
phesiz [] iindeki ifadenin saysal deeri negatif olabilir. rnein
p[-2]
geerli bir ifadedir. Benzer ekilde bu ifade
*(p - 2)
ifadesi ile ayn anlamdadr.
Aadaki rnekte keli ayra ileci ile adres ileci ayn ifade iinde kullanlyor.
#include <stdio.h>
int main()
{
char ch = 'A';
(&ch)[0] = 'B'
putchar(ch);
}
return 0;
C dilinin bir ok kod kalbnda gsterici ileleriyle ile artrma ya da eksiltme ileci birlikte
kullanlr.
1. erik ileci ile ++ ilecinin ayn ifade iinde yer almas
a) ++*p durumu
245
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int k;
int *ptr = a;
++*ptr;
for (k = 0; k < 5; ++k)
printf("%d ", a[k]);
}
/* 2 2 3 4 5 */
return 0;
++*ptr ifadesinde iki ile kullanlyor: erik ileci ile artrma ileci. Her iki ile de ile
ncelik tablosunun ikinci seviyesinde bulunur. kinci seviyenin ncelik yn sadan sola
olduuna gre nce daha sada bulunan ierik ileci deer retir. erik ileci ptr
gstericisinin gsterdii nesneye ular bylece bu nesne, artrma ilecine terim olur. Bu
durumda ptr gstericisinin gsterdii nesnenin deeri 1 artrlr. Ksaca
++*ptr;
deyimi , "ptr'nin gsterdii nesnenin deerini 1 artr" anlamna gelir.
*++p durumu
p gstericisinin 1 fazlas olan adresteki nesneye ulalr. Yani ifadenin deeri p
gstericisinin gsterdii nesneyi izleyen nesnenin deeridir. Tabi ifadenin
deerlendirilmesinden sonra ++ ilecinin yan etkisinden dolay p gstericisinin deeri 1
artrlr. Yani ptr bir sonraki nesneyi gsterir. Aadaki rnei dikkatle inceleyin:
#include <stdio.h>
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int k;
int *ptr = a;
*++ptr = 10;
*ptr = 20;
for (k = 0; k < 5; ++k)
printf("%d ", a[k]);
/* 1 20 3 4 5 */
return 0;
}
x = *++p;
deyimi ile x deikenine artrlm adresteki bilgi atanr.
*p++ durumu
++ ileci ve * ilecinin ikisi de ikinci ncelik seviyesindedir. Bu ncelik seviyesine ilikin
ncelik yn sadan soladr. nce ++ ileci ele alnr ve bu ile ifadenin geri kalan
ksmna p gstericisinin artmam deerini retir. Bu adresteki nesneye ulalr daha
sonra p gstericisinin deeri 1 artrlr. *p++ ifadesinin deeri p gstericisinin gsterdii
246
/* 10 20 3 4 5 */
return 0;
247
p[++i] durumu
x = p[++i];
nce ++i ifadesinin deeri elde edilir. Bu ifadenin deeri i'nin deerinin 1 fazlasdr. Daha
sonra p adresinden (i + 1) uzaklktaki nesneye ulalr. ++ ilecinin yan etkisi olarak i
deikeninin deeri 1 artrlr.
p[i++] durumu
x = p[i++];
nce i++ ifadesinin deeri elde edilir. Bu ifadenin deeri i'nin kendi deeridir. Daha sonra
p adresinden i uzaklktaki nesneye ulalr. ++ ilecinin yan etkisi olarak i deikeninin
deeri 1 artrlr.
Bir dizinin balang adresi ve boyutu bir ileve gnderilirse ilev dizi zerinde ilem
yapabilir. levin dizinin balang adresini alacak parametresi ayn trden bir gsterici
deiken olmaldr. levin dier parametresi dizinin boyutunu tutacak int trden bir
deiken olabilir.
Bir dizinin balang adresini parametre olarak alan ilev, dizi elemanlarna keli ayra
ileci ya da ierik ileci ile eriebilir. Ancak dizi elemanlarnn ka tane olduu bilgisi ilev
tarafndan bilinemez. Bu nedenle dizi uzunluu ikinci bir argman olarak ileve gnderilir.
rnek:
248
#include <stdio.h>
void display_array (const int *p, int size)
{
int i;
int main()
{
int a[5] = {3, 8, 7, 6, 10};
display_array(a, 5);
return 0;
}
Yukarda tanmlanan display_array ilevi int trden bir dizinin balang adresini ve
boyutunu alyor, dizinin tm elemanlarnn deerlerini ekrana yazdryor. Parametre
deikeni olan p gstericisinin bildiriminde yer alan const anahtar szcne daha sonra
deinilecek.
Aada ayn ilev iini yaparken bu kez ierik ilevini kullanyor:
void display_array (const int *p, int size)
{
while (size--)
printf("%d ", *p++);
}
249
Aada bir dizinin elemanlarna rastgele deerler yerletirmek amacyla bir ilev
tanmlanyor:
void set_random_array(int *ptr, int size, int max_val)
{
int k;
return max;
return min;
max_array ilevi adresini ve boyutunu ald dizinin en byk elemannn deeri ile geri
dnyor. min_array ilevi ise, benzer ekilde en kk elemann deeriyle geri dnyor.
250
SIZE
100
int main()
{
int a[ARRAY_SIZE];
srand(time(0));
set_random_array(a, SIZE, 1000);
printf("dizi 0 - 1000 araliginda
printf("dizi yazdiriliyor!\n");
display_array(a, SIZE);
printf("dizinin toplami = %d\n",
printf("dizinin en buyuk elemani
printf("dizinin en kucuk elemani
sort_array(a, SIZE);
printf("dizi siralama isleminden
display_array(a, SIZE);
return 0;
}
251
sort_array ilevinde ise, dizinin ardk iki eleman doru srada deil ise bu elemanlar
takas ediliyor. nce int trden iki nesnenin deerini takas edecek bir ilev yazalm.
Yazdmz bu ilevi sort_array ilevi iinde aralm:
void swap(int *p1, int *p2)
{
int temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void sort_array(int *ptr, int size)
{
int i, k;
sort_array ilevinde
if (ptr[k] > ptr[k + 1])
swap(ptr + k, ptr + k + 1);
deyimine dikkat edin. Bu deyimle, dardan adresi alnan dizinin k indisli eleman, k + 1
indisli elemanndan daha bykse, dizinin k ve k + 1 indisli eleman olan nesnelerin
deerleri swap ilevi arlarak takas ediliyor. swap ilevine argman olarak iki nesnenin
de adresi geiliyor. swap ilevi aadaki gibi de arlabilirdi, deil mi?
swap(&ptr[k], &ptr[k + 1]);
Aada bir diziyi ters eviren reverse_array isimli ilev tanmlanyor:
void reverse_array(int *ptr, int size)
{
int *pend = ptr + size - 1;
int n = size / 2;
while (n--)
swap(ptr++, pend--);
Bir ilevin parametre deikeni bir adres trnden olabildii gibi, bir ilevin geri dn
deeri de bir adres trnden olabilir. Byle bir ilevin tanmnda, ilevin geri dn
deerinin trnn yazlaca yere bir adres tr yazlr.
rnein int trden bir nesnenin adresini dndren func isimli ilev aadaki gibi
tanmlanabilir:
252
int *func(void)
{
/***/
}
func ilevinin geri dn deeri tr yerine int * yazldn gryorsunuz. Yukardaki ilev
tanmndan * atomu kaldrlrsa, ilevin int trden bir deer dndrd anlalr.
Adrese geri dnen bir ilev ne anlama gelir? lev arld yere, int trden bir nesnenin
adresini iletir. lev ar ifadesi, ilevin geri dn deerine yani bir adres bilgisine
edeerdir. levin geri dn deeri bir nesnede saklanmak istenirse ayn trden bir
gsterici deikene atanmaldr:
int *ptr;
ptr = func();
Benzer biimde:
levlerin geri dn deerlerini geici bir nesne yardmyla oluturduklarn biliyorsunuz.
Bir ilevin geri dn deerinin tr, geri dn deerini iinde tayacak geici nesnenin
trdr. Bu durumda, bir adres trne geri dnen bir ilevin, geri dn deerini iinde
tutacak geici nesne de bir gstericidir.
Bir adres bilgisiyle geri dnen ilevlere C programlarnda ok rastlanr. Standart C
ilevlerinin bazlar da, adres trnden bir deer dndrr.
Aadaki kod parasn inceleyin:
#include <stdio.h>
int g = 10;
int *foo()
{
return &g;
}
int main()
{
int *ptr;
printf("g = %d\n", g);
ptr = foo();
*ptr = 20;
printf("g = %d\n", g);
}
return 0;
foo ilevi arldnda global g isimli deikenin adresini dndryor. lev geri dn
deerini
return &g;
deyimiyle retiyor. &g ifadesi (int *) trndendir. Bu ifadenin deeri yine (int *)
trnden olan geici nesneye atanyor. main ilevi iinde arlan foo ilevinin geri
dndrd adres, ptr gsterici deikenine atanyor.
Bir dizinin en byk elemann bulup bu elemann deerine geri dnen getmax isimli ilev
daha nce yazlmt. Aada ayn ilev bu kez en byk dizi elemannn adresine geri
dnecek ekilde yazlyor:
253
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
SIZE
20
int main()
{
int a[SIZE];
int k;
srand(time(0));
for (k = 0; k < SIZE; ++k) {
a[k] = rand() % 1000;
printf("%d ", a[k]);
}
printf("\n");
printf("max = %d\n", *getmax(a, SIZE));
}
return 0;
levin kodunu inceleyin. Yerel pmax gsterici deikeni, dizinin en byk elemannn
adresini tutmak iin tanmlanyor. Balangta dizinin ilk eleman en byk kabul
edildiinden, pmax gstericisine nce dizinin ilk elemannn adresi atanyor:
pmax = (int *)p
atamasyla dardan alnan dizinin balang adresinin pmax isimli gstericiye atandn
gryorsunuz. Daha sonra oluturulan for dngsyle srasyla dizinin dier
elemanlarnn, pmax gstericisinin gsterdii nesneden daha byk olup olmadklar
snanyor. pmax'n gsterdii nesneden daha byk bir dizi eleman bulunduunda, bu
elemann adresi pmax gstericisine atanyor.
pmax = (int *)(p + i)
atamasnda p + i ifadesinin &p[i] ifadesine edeer olduunu biliyorsunuz.
Dng knda pmax gsteri deikeni dizinin en byk elemannn adresini tutar, deil
mi?
main ilevinde SIZE uzunluunda bir dizi nce rastgele deerle dolduruluyor. Daha sonra
dizi elemanlarnn deerleri ekrana yazdrlyor.
Aadaki ilev arsyla ekrana getmax ilevinin geri dndrd adresteki nesnenin
deeri yazdrlyor.
printf("max = %d\n", *getmax(a, SIZE));
Bu da dizinin en byk elemannn deeridir, deil mi?
254
Bir ilevin bir adres bilgisine geri dnmesinin baka faydalar da olabilir. Hesaplanmak
istenen bir deeri darya iletmek yerine, hesaplanan bu deeri iinde tutacak bir
nesnenin adresi darya iletilebilir. Nasl olsa bir nesnenin adresi elimizdeyken o nesnenin
deerine de ulaabiliriz, deil mi?
Type szc bir tr belirtiyor olsun. Type trnden bir deer dndren bir ilevin
bildirimi aadaki gibidir:
Type foo(void);
Byle bir ilevin geri dn deerini saklamak iin bu kez Type trnden bir nesneye ilev
ar ifadesini atamak gerekir.
Type val = foo();
Type trden bir nesne bellekte 100 byte yer kaplyor olsun. Bu durumda, arlan foo
ilevinin almas srasnda return deyimine gelindiinde 100 byte'lk bir geici nesne
oluturulur. return ifadesinden bu geici nesneye yaplan atama 100 byte'lk bir bloun
kopyalanmasna neden olur. Geici nesnenin val isimli deikene aktarlmas da, yine 100
byte'lk bir bellek blounun kopyalanmasna neden olur.
imdi de aadaki bildirime bakalm:
Type *foo(void);
Bu kez ilev hesaplad nesnenin deerini iinde tayan Type trnden bir nesnenin
adresini dndryor. Bu durumda, ilevin geri dn deerini iinde tayacak geici
nesne yalnzca, 2 byte ya da 4 byte olur, deil mi? Yani 100 byte'lk bir blok kopyalamas
yerine yalnzca 2 ya da 4 byte'lk bir kopyalama sz konusudur.
Byle bir ilevin geri dn deerini saklamak iin, ilev ar ifadesinin bu kez Type *
trnden bir nesneye atanmas gerekir.
Type *ptr;
ptr = foo();
foo ilevinin hesaplad deeri iinde tutan Type trnden nesnenin adresi, Type
trnden bir gsterici deikene atanyor. Type trnn sizeof deeri ne olursa olsun bu
ilem, yalnzca bir gstericinin sizeof deeri kadar byklkte bir bloun kopyalanmasna
neden olur, deil mi?
Bir ilevin, bir deerin kendisini darya iletmesi yerine o deeri iinde tutan nesnenin
adresini darya iletmesi, bellek ve zaman kullanm asndan maliyeti drr.
Bir de baka bir faydadan sz edelim. Bir ilevden bir nesnenin deerini alnrsa, bu
deeri tayan nesne deitirilemez. Ancak bir ilevden bir nesnenin adresi alnrsa, adresi
alnan nesne deitirilebilir.
Yukarda yazlan getmax ilevi bize dizinin en byk elemannn adresini dndryordu,
deil mi? Aadaki main ilevini inceleyin:
255
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
SIZE
100
return 0;
256
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
SIZE
100
return 0;
Adrese geri dnen bir ilev asla otomatik mrl yerel bir nesnenin adresiyle geri
dnmemelidir. Otomatik mrl yerel nesnelerin adresleriyle geri dnmek tipik bir
programlama hatasdr.
Klavyeden girilen bir ismin balang adresine geri dnen bir ilev yazlmak istensin:
char *getname()
{
char name_entry[40];
*/
#include <stdio.h>
int main()
{
char *ptr = get_name();
printf("alnan isim = %s\n", ptr);
}
return 0;
257
get_name ilevi iinde yerel bir dizi tanmlanyor. Kullancnn girdii isim yerel diziye
yerletiriliyor, daha sonra yerel dizinin adresiyle geri dnlyor.
Yerel deikenlerin otomatik mrl olduunu, yani ait olduklar bloun yrtlmesi
sonunda bellekten boaltldklarn biliyorsunuz. get_name ilevinin geri dn deeri,
yani yerel name_entry dizisinin balang adresi main ilevi iinde ptr gstericisine
atanyor. Oysa artk yerel dizi bellekten boaltld iin, ptr gsterici deikenine atanan
adresin hibir gvenilirlii yoktur. ptr gstericisinin gsterdii yerden okuma yapmak ya
da buraya yazmak gsterici hatasdr.
Adrese geri dnen bir ilev yerel bir deikenin adresiyle ya da yerel bir dizinin balang
adresiyle geri dnmemelidir. Yukarda yazlan getname ilevinin arlmas alma
zaman hatasna neden olur. C derleyicilerinin ou, bu durumu mantksal bir uyar iletisi
ile belirler.
Bir gsterici deiken, iinde adres bilgisi tutan bir nesnedir, deil mi?
int x = 10;
int *ptr = &x;
Yukardaki deyimleri aadaki cmlelerle ifade edebiliriz:
ptr gstericisi x nesnesinin adresini tutuyor.
ptr gstericisi x nesnesini gsteriyor.
*ptr nesnesi , ptr'nin gsterdii nesnedir.
*ptr , x nesnesinin kendisidir.
yle bir gsterici olsun ki hibir nesneyi gstermesin. Hibir yeri gstermeyen bir
gstericinin deeri yle bir adres olmaldr ki, bu adresin baka hibir amala
kullanlmad gvence altna alnm olsun. te hibir yeri gstermeyen bir adres olarak
kullanlmas amacyla baz balk dosyalarnda standart bir simgesel deimez
tanmlanmtr. Bu simgesel deimez NULL simgesel deimezi olarak bilinir.
NULL bir simgesel deimezdir. Bu simgesel deimez standart balk dosyalarndan
stdio.h, string.h ve stddef.h iinde tanmlanmtr.
NULL adresi herhangi trden bir gstericiye atanabilir. Byle bir atama tamamen
szdizimsel kurallara uygundur, uyar gerektiren bir durum da sz konusu deildir.
int *iptr = NULL;
char *cptr = NULL;
double *dptr = NULL;
NULL adresi hibir yeri gstermeyen bir gstericinin deeridir. Bir gsterici ya bir nesneyi
gsterir (yani bu durumda gstericinin deeri gsterdii nesnenin adresidir) ya da hibir
nesneyi gstermez (bu durumda gstericinin deeri NULL adresidir).
Bir adres bilgisinin doru ya da yanl olarak yorumlanmas sz konusu olduu zaman,
adres bilgisi NULL adresi ise "yanl" olarak yorumlanr. NULL adresi dndaki tm adres
bilgileri "doru" olarak yorumlanr. ptr isimli bir gstericinin deeri NULL adresi deil ise,
yani ptr gstericisi bir nesneyi gsteriyorsa bir ilev arlmak istensin. Byle bir if
deyiminin koul ifadesi iki ayr biimde yazlabilir:
if (ptr != NULL)
foo();
if (ptr)
foo();
258
Bu kez ptr isimli gsterici deikenin deeri NULL adresi ise, yani ptr gstericisi bir
nesneyi gstermiyorsa, foo ilevi arlmak istensin. if deyiminin koul ifadesi yine iki ayr
biimde yazlabilir:
if (ptr == NULL)
foo();
if (!ptr)
foo();
Bir gsterici deikene herhangi bir trden 0 deeri atandnda, atama ncesi 0 deeri
otomatik olarak NULL adresine dntrlr:
int *ptr = 0;
Yukardaki deyimle ptr gsterici deikenine NULL adresi atanyor.
Peki NULL adresi ne iin kullanlr?
Adrese geri dnen bir ilevin eer baarszl sz konusu ise, ilev baarszlk durumunu
NULL adresine geri dnerek bildirebilir.
int trden bir dizi iinde bulunan ilk asal saynn adresi ile geri dnen bir ilev yazmak
isteyelim:
int *get_first_prime(const int *ptr, int size);
levin birinci parametresi dizinin balang adresi, ikinci parametresi ise dizinin boyutu
olsun. levi aadaki biimde yazdmz dnn:
int is_prime(int val);
int *get_first_prime(const int *ptr, int size)
{
int k;
for (k = 0; k < size; ++k)
if (isprime (ptr[k]))
return ptr + k;
/* ??????? */
}
Yukardaki ilevde, dardan balang adresi alnan dizimizin her elemann asal olup
olmad snanyor, ilk asal say grldnde bu elemann adresiyle geri dnlyor. Peki
ya dizinin iinde hi asal say yoksa, for dng deyiminden kldnda ilev bir p deeri
geri dndrr, deil mi? Peki bu durumda ilev hangi geri dn deerini retebilir?
Madem ki NULL adresi hibir yeri gstermeyen bir adres, o zaman adrese geri dnen bir
ilev baarszlk durumunu NULL adresine geri dnerek bildirebilir, deil mi?
259
#include <stdio.h>
int is_prime(int val);
int *get_first_prime(const int *ptr, int size)
{
int k;
C dilinde, adrese geri dnen bir ilevin baarszlk durumunda NULL adresine geri
dnmesi, ok sk kullanlan bir konvensiyondur.
Parametre deikeni gsterici olan bir ilev, kendisine geilen NULL adresini bir bayrak
deeri olarak kullanabilir. Aadaki gibi bir ilev tasarladmz dnelim:
void func(char *ptr)
{
if (ptr == NULL) {
/***/
}
else {
/***/
}
}
lev kendisine geilen adresin NULL adresi olup olmamasna gre farkl iler yapyor.
Tabi bu durumun ilevi aran kod paras tarafndan bilinmesi gerekir. Standart C
ilevlerinden time ilevi byledir. time ilevinin gsterici parametresine NULL adresi
geildiinde ilev herhangi bir nesneye deer atamaz. Hesaplad deeri yalnzca geri
dn deeri olarak darya iletir. Ancak ileve NULL adresi dnda bir adres
gnderildiinde ilev verilen adresteki nesneye hesaplad deeri yazar.
Birok programc bir gsterici deikene gvenilir bir adres atamadan nce, gstericiye
NULL adresi deerini verir. Bylece kod iinde ilgili gstericinin henz bir nesneyi
gstermedii bilgisi gl bir biimde verilerek, kodun okunabilirlii artrlr.
Bir gstericinin mr henz sona ermeden, gsterdii nesnenin mr sona erebilir. Bu
durumda gstericinin deeri olan adres gvenilir bir adres deildir. Kod iinde bu durumu
vurgulamak iin gstericiye NULL adresi atanabilir.
Bir gstericiye farkl trden bir adres atandnda, C derleyicilerin ou durumu pheyle
karlayarak mantksal bir uyar iletisi verir. Ancak derleyici yine de farkl trden adresin
saysal bileenini hedef gstericiye atar. Borland derleyicileri bu durumda aadaki uyar
iletisini verir:
warning : suspicious pointer conversion in function ......
Aadaki kodu inceleyin:
260
#include <stdio.h>
int main()
{
double d = 1874567812.987;
int *ptr;
ptr = &d;
*ptr = 0;
printf("d = %lf\n", d);
}
return 0;
Yukardaki kodda double trden d deikeninin adresi int trden bir nesneyi gsterecek
ptr deikenine atanyor. Byle bir atamadan sonra ptr double trden d deikenini deil,
int trden bir nesneyi gsterir.
*ptr = 0;
atamasyla d deikeninin ilk 4 byte'na tamsay formatnda 0 deeri atanm olur. Byle
bir atamadan sonra d deikeninin deeri istenilmeyen bir biimde deitirilmi olur, deil
mi?
Eer gstericiye farkl trden adres bilinli bir biimde atanyorsa tr dntrme ileci
kullanlmaldr:
#include <stdio.h>
int main()
{
double d;
unsigned int k;
unsigned char *ptr;
printf("bir gercek sayi girin :");
scanf("%lf", &d);
ptr = (unsigned char *)&d;
for (k = 0; k < sizeof(double); ++k)
printf("%u\n", ptr[k]);
return 0;
}
Yukardaki main ilevinde double trden d deikeninin adresi unsigned char trnden bir
gstericiye atanyor. ptr gsterici deikeni byte byte ilerletilerek d deikeninin her bir
byte'nn deeri tamsay olarak yorumlanarak ekrana yazdrlyor.
ptr = (unsigned char *)&d;
deyiminde atamann bilinli olarak yapldn gstermek iin d deikeninin adresi nce
unsigned char trden bir adres bilgisine dntrlyor, daha sonra atama yaplyor.
Bu da bilinli olarak yaplma olasl ok az olan bir ilemdir. C derleyicileri pheli olan
bu durumu mantksal bir uyar iletisi ile programcya bildirirler. rnein bu uyar iletisi
Borland derleyicilerinde aadaki gibidir:
261
Bir byte'tan daha byk olan deikenlerin bellee yerleim biimi kullanlan
mikroilemciye gre deiebilir. Bu nedenle deikenlerin bellekteki grnmleri
tanabilir bir bilgi deildir. Mikroilemciler iki tr yerleim biimi kullanabilir:
i)Dk anlaml byte deerleri bellein dk sayl adresinde bulunacak biimde. Byle
yerleim biimine little endian denir. Intel ilemcileri bu yerleim biimini kullanr. Bu
ilemcilerin kullanld sistemlerde rnein
int x = 0x1234;
biimindeki bir x deikeni bellekte 1A00 adresinden balayarak yerletirilmi olsun:
262
1A00
1A01
---0001 0010
0011 0100
----
ekilden de grld gibi x deikeninin dk anlaml byte deeri (34H) yksek saysal
adreste (1A00H) olacak biimde yerletirilmitir.
Aadaki kod kullanlan sistemin little endian ya da big endian oldugunu snyor:
#include <stdio.h>
int main()
{
int x = 1;
if (*(char *)&x)
printf("little endian\n");
else
printf("big endian\n");
return 0;
}
Yazlan kodda nce adres ileciyle x deikeninin adresi elde ediliyor. Adres ilecinin
rettii deer int * trndendir. Daha sonra tr dntrme ileciyle, elde edilen adres
bilgisi char * trne dntrlyor. char * trnden adresin de ierik ilecinin terimi
olduunu gryorsunuz. Bu durumda ierik ileci x nesnesinin en dk saysal
adresindeki char trden nesneye eriir, deil mi? Eer bu nesnenin deeri 1 ise sistem
"little-endian" dr.
Yazlar karakter dizilerinin iinde bulunurlar. Bir ilevin bir yaz zerinde ilem
yapabilmesi iin bir yaznn balang adresini almas yeterlidir. Yani ilev yaznn
(karakter dizisinin) balang adresi ile arlr. Yazy iinde tutan char trden dizinin
boyutu bilgisini ileve geirmeye gerek yoktur. nk yazlarn sonunda sonlandrc
karakter vardr. Karakter dizileri zerinde ilem yapan kodlar dizinin sonunu sonlandrc
karakter yardmyla belirler.
Yazlarla ilgili ilem yapan bir ilev char trden bir gsterici deiken ile zerinde ilem
yapaca yaznn balang adresini alr. lev, yaznn sonundaki sonlandrc karakteri
grene kadar bir dng ile yaznn tm karakterlerine eriebilir.
str, char trnden bir gsterici olmak zere yaz zerinde sonlandrc karakteri grene
kadar ilem yapabilecek dngler yle oluturulabilir:
while (*str != '\0') {
/***/
++str;
}
for (i = 0; str[i] != '\0'; ++i) {
/***/
}
stdio.h iinde bildirilen standart puts ilevinin parametre deikeni char trnden bir
gstericidir:
263
return ptr;
264
strlen levi
En sk arlan standart C ilevlerinden biridir. levin ismi olan strlen, "string length"
szcklerinden gelir. Bu ilev bir yaznn karakter uzunluunu yani yaznn ka
karakterden olutuu bilgisini elde etmek iin kullanlr.
levin bildirimi:
size_t strlen(const char *str);
biimindedir. levin parametre deikeni, uzunluu hesaplanacak yaznn balang
adresidir. lev sonlandrc karakter grene kadar karakterlerin saysn hesaplar. Geri
dn deeri tr yerine yazlan size_t nin imdilik unsigned int trnn bir baka ismi
olduunu dnebilirsiniz.
#include <stdio.h>
#include <string.h>
int main()
{
char s[100];
printf("bir yaz giriniz : ");
gets(s);
printf("(%s) yazsnn uzunluu = %u\n", s, strlen(s));
}
return 0;
265
strchr levi
levin ismi olan strchr ,"string character" szcklerinden gelir. strchr ilevi bir karakter
dizisi iinde belirli bir karakteri aramak iin kullanlan standart bir C ilevidir.
levin string.h dosyas iindeki bildirimi aadaki gibidir:
char *strchr(const char *str, int ch);
Bu ilev ikinci parametresi olan ch karakterini, birinci parametresi olan str adresinden
balayarak sonlandrc karakter grene kadar arar. Aranan karakter sonlandrc
karakterin kendisi de olabilir. levin geri dn deeri, ch karakterinin yaz iinde
bulunabilmesi durumunda bulunduu yerin adresidir. Eer ch karakteri yaz iinde
bulunamazsa, ilev NULL adresine geri dner.
strchr ilevi aadaki gibi tanmlanabilir:
#include <stdio.h>
#include <string.h>
int main()
{
char s[100];
char *p, ch;
printf("bir yaz girin : ");
gets(s);
printf("yaz iinde arayacanz karakteri girin : ");
scanf("%c", &ch);
p = strchr(s, ch);
if (p == NULL)
printf("aranan karakter bulunamad\n");
else
printf("bulundu: (%s)\n", p);
}
return 0;
266
strrchr levi
Standart olan bu ilev strchr ilevi gibi bir yaznn iinde bir karakteri arar. Arama yaznn
sonundan balanarak yaplr. Yani "ankara" yazs iinde 'n' karakteri arandnda ilev
yaznn son karakterinin adresini dndrr. levin string.h balk dosyas iindeki
bildirimi aadaki gibidir:
char *strrchr(const char *str, int ch);
lev, str adresindeki yaz iinde bulunabilecek son ch karakterinin adresini dndrr.
Eer yaznn iinde ch karakteri yoksa ilevin geri dn deeri NULL adresidir. lev ile
yaznn sonundaki sonlandrc karakter de aranabilir.
#include <stdio.h>
char *mstrrchr(const char *str, int ch)
{
const char *p = str;
const char *pf = NULL;
while (*p) {
if (*p == ch)
pf = p;
p++;
}
if (ch == '\0')
return (char *)p;
return (char *)pf;
}
int main()
{
char s[100];
char *ptr;
int ch;
printf("bir yazi girin : ");
gets(s);
printf("aranacak karakteri girin: ");
ch = getchar();
ptr = mstrrchr(s, ch);
if (ptr == NULL)
printf("(%s) yazsnda (%c) bulunamad!\n", s, ch);
else
printf("bulundu: (%s)\n", ptr);
return 0;
}
strstr levi
levin ismi string string szcklerinden gelir. Bu ilevle bir yaz iinde baka bir yaz
aranr. levin bildirimi aadaki gibidir:
267
#include <stdio.h>
#include <string.h>
#define SIZE
100
int main()
{
char s1[SIZE];
char s2[SIZE];
char *ptr;
printf("iinde arama yaplacak yazy girin: ");
gets(s1);
printf("aranacak yazy girin: ");
gets(s2);
ptr = strstr(s1, s2);
if (!ptr)
printf("(%s) yazisi icinde (%s) yazisi yok!\n", s1, s2);
else
printf("bulundu! (%s)\n", ptr);
}
return 0;
strcspn levi
Bu ilev ile bir yaznn iinde baka bir yazda olan ilk karakterin indisi bulunur. levin
string.h iindeki bildirimi yledir:
size_t strcspn(const char *p1, const char *p2);
Geri dn deeri u ekilde de tanmlanabilir : lev p1 yazs iinde yaznn bandan
balayarak, p2 yazsnn karakterlerinden herhangi birini iermeyen yaznn uzunluu
deerine geri dner:
#include <stdio.h>
#include <string.h>
#define SIZE
100
int main()
{
char s1[SIZE];
char s2[SIZE];
size_t index;
printf("icinde arama yapilacak yaziyi girin: ");
gets(s1);
printf("karakterler: ");
gets(s2);
index = strcspn(s1, s2);
printf("(%s)\n", s1 + index);
s1[index] = '\0';
printf("(%s)\n", s1);
return 0;
}
268
strpbrk levi
Standart strpbrk ilevi ile bir yazda baka bir yaznn karakterlerinden herhangi biri
aranr:
char *mstrpbrk(const char *s1, const char *s2)
Eer s1 adresindeki yaznn iinde s2 adresindeki yaznn karakterlerinden herhangi biri
varsa ilev bu karakterin adresini dndrr. Eer s1 yazs iinde s2 yazsnn
karakterlerinin hibiri yoksa ilev NULL adresine geri dner.
Aada strpbrk ilevinin rnek bir tanm ile bir snama kodu yer alyor:
#include <string.h>
#include <stdio.h>
#define
SIZE
100
strcpy levi
Standart bir C ilevidir. levin ismi olan strcpy, "string" ve "copy" szcklerinden gelir.
lev ikinci parametresinde tutulan adresten balayarak, sonlandrc karakter grene
kadar, sonlandrc karakter de dahil olmak zere, tm karakterleri birinci parametresinde
tutulan adresten balayarak srayla yazar. levin string.h balk dosyas iindeki bildirimi
aadaki gibidir:
char *strcpy(char *dest, const char *source);
levin geri dn deeri kopyalamann yaplmaya baland adres yani dest adresidir.
strcpy ilevi aadaki gibi tanmlanabilir:
269
#include <stdio.h>
#include <string.h>
int main()
{
char dest[100] = "C reniyoruz!";
char source[100];
printf("kopyalanacak yazy girin : ");
gets(source);
printf("kopyalama yaplmadan nce yaz :(%s)\n", dest);
strcpy(dest, source);
printf("kopyalama yapildiktan sonra yaz :(%s)\n", dest);
}
return
0;
lev iinde kullanlan for dngsnn ikinci ksmnda nce atama yaplyor, daha sonra
atama ifadesinin deeri yani atama ilecinin sa tarafnda bulunan deer sonlandrc
karakter ile karlatrlyor. Bylece ilgili adrese sonlandrc karakter de kopyalandktan
sonra dngden klyor.
lev aadaki gibi de yazlabilirdi:
/***/
for (i = 0; source[i] != '\0'; ++i)
dest[i] = source[i];
dest[i] = '\0';
/***/
for dng deyiminde keli ayra ileci kullanld iin, birinci parametre deikenine
kopyalanan dest gsterici deikeninin deeri deitirilmiyor. levin sonunda dest adresi
ile geri dnlyor. levin yazmnda while dngs kullanlarak, dest deikeni iindeki
adres srekli artrlabilirdi. Bu durumda ilevin dest gstericisinin ilkdeeriyle geri
dnebilmesini salayabilmek iin, dest gstericisindeki deeri deitirmeden nce, bu
deeri baka bir gsterici deiken iinde saklamak gerekirdi:
char *mystrcpy(char *dest, const char *source)
{
char *temp = dest;
strcat levi
270
return 0;
/* strcpy(s1, s2); */
return temp;
Bir yaznn sonuna baka bir yazy eklemek, sona eklenecek yazy dier yaznn
sonundaki sonlandrc karakterin bulunduu yere kopyalamak anlamna gelir, deil mi?
Dolaysyla strcat ilevi aadaki biimlerde de tanmlanabilir.
char *mystrcat(char *s1, const char *s2)
{
strcpy(s1 + strlen(s1), S2);
return s1;
}
char *mystrcat(char *s1, const char *s2)
{
strcpy(strchr(s1, '\0'), s2);
return s1;
}
271
strcmp ilevi
Standart bir C ilevidir. levin ismi string compare szcklerinden gelir. lev iki karakter
dizisini karlatrmakta kullanlr. Karlatrma, iki karakter dizisi iindeki yaznn,
kullanlan karakter seti tablosu gznnde bulundurularak, ncelik ya da eitlik
durumunun sorgulanmasdr. rnein:
Adana yazs Ankara yazsndan daha kktr. nk eitlii bozan 'n' karakteri ASCII
karakter tablosunda 'd' karakterinden sonra gelir.
ankara yazs ANKARA yazsndan daha byktr. nk kk harfler ASCII tablosunda
byk harflerden sonra gelir.
Kk "masa" byk "MASA" dan daha byktr.
kalem yazs kale yazsndan daha byktr.
strcmp ilevinin string.h balk dosyas iindeki bildirimi aadaki gibidir:
int strcmp(const char *s1, const char *s2);
lev birinci parametre deikeninde balang adresi tutulan yaz ile, ikinci parametre
deikeninde balang adresi tutulan yazlar karlatrr.
levin geri dn deeri,
birinci yaz ikinci yazdan daha bykse pozitif bir deerdir,
birinci yaz ikinci yazdan daha kkse negatif bir deerdir,
birinci yaz ile ikinci yaz birbirine eit ise 0 deeridir.
#include <stdio.h>
#include <string.h>
int main()
{
char s[20];
char password[ ] = "Mavi ay";
printf("parolay girin : ");
gets(s);
if (!strcmp(s, password))
printf("Parola doru!..\n");
else
printf("Parola yanl!..\n");
return 0;
272
strncpy ilevi
Standart bir C ilevidir. levin ismi string number copy szcklerinden gelir. lev bir
yaznn (karakter dizisinin) ilk n karakterini baka bir yere kopyalamakta kullanlr. levin
string.h iindeki bildirimi aadaki gibidir :
char *strncpy(char *dest, const char *source, size_t n);
lev birinci parametre deikeninde balang adresi tutulan yazya, ikinci parametre
deikeninde adresi tutulan yazdan, nc parametresinde tutulan sayda karakteri
kopyalar.
levin geri dn deeri kopyalamann yaplaca adres yani dest adresidir.
nc parametre olan n says eer kopyalanacak yaznn uzunluundan daha kk ya
da eit ise ilev kopyalama sonunda sonlandrc karakteri birinci dizinin sonuna eklemez.
Yani n <= strlen(source) ise sonlandrc karakter eklenmez.
nc parametre olan n says eer kopyalanacak yaznn uzunluundan daha byk ise
ilev kopyalama sonunda sonlandrc karakteri birinci dizinin sonuna ekler.
Yani n > strlen(source) ise sonlandrc karakter de kopyalanr.
Aada strncpy ilevi tanmlanyor:
#include <string.h>
#include <stdio.h>
#define
SIZE
100
unsigned int
n)
while (n--)
*s++ = '\0';
}
return s1;
int main()
{
char str1[SIZE];
char str2[SIZE];
int n;
printf("birinci yaziyi girin : ");
gets(str1);
printf("ikinci yaziyi girin : ");
gets(str2);
printf("ikinci yazidan kac karakter kopyalanacak? ");
scanf("%d", &n);
strncpy(str1, str2, n);
printf("(%s)\n", str1);
return 0;
}
273
SIZE
100
int main()
{
char str1[SIZE] = "Necati Ergin";
char str2[SIZE] = "Mehmet Aktunc";
strncpy(str1, str2, 6);
printf("(%s)\n", str1);
return 0;
}
strncat levi
Standart bir C ilevidir. levin ismi ingilizce "string number concatanate" szcklerinden
gelir. Bir yaznn sonuna baka bir yazdan belirli bir sayda karakteri kopyalamak
amacyla kullanlr. string.h balk dosyas iinde bulunan bildirimi aadaki gibidir:
char *strncat(char *s1, const char *s2, size_t n);
lev birinci parametre deikeni iinde balang adresi verilen yaznn sonuna, ikinci
parametresinde balang adresi tutulan karakter dizisinden, nc parametresinde
tutulan tamsay adedi kadar karakteri kopyalar.
levin geri dn deeri sonuna ekleme yaplacak yaznn balang adresidir.
levin tanm ve ilevi snayan bir main ilevi rnek olarak aada veriliyor:
char *mstrncat(char *s1, const char *s2, unsigned int n)
{
char *ptr;
for (ptr = s1; *ptr != '\0'; ++ptr)
;
while (n-- && *s2 != '\0')
*ptr++ = *s2++;
*ptr = '\0';
}
return s1;
#include <string.h>
#include <stdio.h>
#define
SIZE
100
int main()
{
char dest[SIZE];
char source[SIZE];
274
int n;
printf("birinci yaziyi girin : ");
gets(dest);
printf("ikinci yaziyi girin : ");
gets(source);
printf("1. yazinin sonuna kac karakter kopyalanacak : ");
scanf("%d", &n);
mstrncat(dest, source, n);
printf("eklemeden sonra 1. yazi = (%s)\n", dest);
}
return 0;
strncmp levi
Standart bir C ilevidir. levin ismi string number compare szcklerinden gelir.. strcmp
ilevine benzer, ancak bu ilev iki yaznn tmn deil de, belirli bir sayda karakterlerini
karlatrma amacyla kullanlr.
lev birinci parametre deikeninde balang adresi tutulan yaz ile, ikinci parametre
deikeninde balang adresi tutulan yazlarn, nc parametresinde tutulan saydaki
karakterlerini karlatrr.
lev, birinci yaznn ilk n karakteri ikinci yaznn ilk n karakterinden daha bykse pozitif
bir deere
Birinci yaznn ilk n karakteri ikinci yaznn ilk n karakterinden daha kkse negatif bir
deere
Birinci yaznn ve ikinci yaznn ilk n karakteri birbirine eit ise 0 deerine geri dner.
Aada ilevin tanm ve ilevi snayan bir main ilevi veriliyor:
int strncmp(const char *s1, const char *s2, unsigned int n)
{
while (n--) {
if (*s1 != *s2)
return *(unsigned char *)s1 < *(unsigned char *)s2 ? -1 : 1;
if (*s1 == '\0')
return 0;
s1++;
s2++;
}
return 0;
}
#include <stdio.h>
#define
SIZE
100
int main()
{
char str1[SIZE];
char str2[SIZE];
int n, result;
printf("birinci yaziyi girin : ");
gets(str1);
printf("ikinci yaziyi girin : ");
gets(str2);
printf("iki yazinin kac karakteri karsilastirilacak? ");
scanf("%d", &n);
result = strncmp(str1, str2, n);
if (result == 0)
275
return 0;
strset levi
Standart olmayan bu ilev derleyicilerin ounda bulunur. levin ismi string ve set
szcklerinden gelir. Bir karakter dizisinin belirli bir karakterle doldurulmas amacyla
kullanlr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir:
char *strset(char *str, int ch);
lev birinci parametre deikeninde balang adresi olan yazy sonlandrc karakter
grene kadar ikinci parametre deikeninde tutulan karakterle doldurur. Yaznn
sonundaki sonlandrc karaktere dokunmaz.
levin geri dn deeri yine doldurulan yaznn balang adresidir.
#include <stdio.h>
#include <string.h>
int main()
{
char s[100];
int ch;
printf("bir yaz girin :");
gets(s);
printf("yazy hangi karakterle doldurmak istiyorsunuz : ");
ch = getchar();
printf("\nyaz %c karakteriyle dolduruldu (%s)\n", ch, strset(s, ch));
return 0;
}
strset ilevi aadaki gibi tanmlanabilir:
#include <stdio.h>
char *mystrset(char *str, int ch)
{
int i;
for (i = 0; str[i] != '\0'; ++i)
str[i] = ch;
}
return str;
strrev levi
Standart olmayan bu ilev de derleyicilerin ounda bulunur. levin ismi string reverse
szcklerinden gelir. lev bir yazy ters evirmek amacyla kullanlr. levin string.h
balk dosyas iinde yer alan bildirimi aadaki gibidir:
276
strupr(s);
puts(s);
strlwr(s);
puts(s);
return 0;
277
Birok programda balang adresi tutulan bir yaznn sonunu bulmak gerekir.
Bir yaznn balang adresini tutan bir gsteri deikeni, yaznn sonundaki sonlandrc
karakteri gsterir hale getirmek iin, ayr kalp kullanlabilir:
p bir yaznn balang adresini tutan gsterici olsun. Aadaki deyimlerin hepsi p
gsterici deikeni yaznn sonundaki, sonlandrc karakterin bulunduu yere teler.
p += strlen(p);
p = strchr(p, '\0');
while (*p != '\0')
++ptr;
Aadaki dngden ktktan sonra p gstericisi sonlandrc karakterden bir sonraki
adresi gsterir. Neden?
while (*p++ != '\0')
;
Bo Yaz
Bo yaz (null string) uzunluu 0 olan yazdr. str, char trden bir dizi olmak zere, eer
str[0] sonlandrc karakter ise, str dizisinde bo yaz tutulmaktadr. Bo yaz geerli bir
yazdr.
Yazlarla ilgili ilem yapan ilevler, adresini ald yazlarn bo yaz (null string) olmas
durumunda da doru almaldr. Aadaki dngy inceleyin:
while (*++p != '\0')
;
p bir yaznn balang adresini tutan gsterici deiken olmak zere, yukardaki
dngden kldnda p, yaznn sonundaki sonlandrc karakteri gsterir. Ancak p'nin
gsterdii yaz eer bo ise, yukardaki dng, yazya ait olmayan bellek alan zerinde
ilem yapmaya balar. Bu da phesiz bir programlama hatasdr.
imdi de aadaki if deyimini inceleyin:
int strfunc(const char *ptr)
{
if (!ptr || !*ptr)
return 0;
/***/
}
strfunc ilevi iinde yer alan if deyiminde yer alan
!ptr || !*ptr
ifadesi, ptr gsterici deikeninin deerinin NULL adresi olmas durumunda ya da ptr'nin
gsterdii yaznn bo olmas durumunda dorudur. Bu ifade, " ptr bir yazy
gstermiyorsa ya da ptr'nin gsterdii yaz bosa" anlamna gelir. Bu ifadenin mantksal
tersi olan
ptr && *ptr
278
ifadesi ise, ptr bir yazy gsteriyor ve ptr'nin gsterdii yaz bo deil ise anlamna gelir,
deil mi?
Her iki ifade de mantksal ilelerin ksa devre davran zelliinden faydalanlyor.
Mantksal &&, || ilelerinin ksa devre davran olmasayd, ptr'nin deeri NULL
adresiyken, ptr'nin gsterdii nesneye eriilmeye allrd.
rnekler
Aada yazlarla ilgili ilem yapan baz ilevler tanmlanyor. levlerde gsterici
deikenlerin kullanmn inceleyin.
Aada, bir yaznn C'nin kurallarna gre geerli bir isim olup olmad snayan
is_valid_id isimli ilev tanmlanyor. Yaz geerli bir isim ise ilev sfr d deere, deilse
sfr deerine geri dnyor:
#include <ctype.h>
int is_id(const char *ptr)
{
int ch;
//Bos yazi ise gecerli isim degil
if ((ch = *ptr++) == '\0')
return 0;
//lk karakter harf ya da "alt tire" olmal
if (!(isalpha(ch) || ch == '_'))
return 0;
//kalan karakterler harf rakam ya da "alt tire" olmal
while ((ch = *ptr++) != '\0')
if (!(isalnum(ch) || ch == '_'))
return 0;
}
return 1;
279
Gsterici Hatalar
Gstericileri kullanarak RAM zerinde bir blgeye eriilebilir. Bir programn almas
srasnda bellekte alyor durumda olan baka programlar da olabilir. Gstericileri
kullanarak o anda almakta olan programn bellek alanna veri aktarlrsa oradaki
programn kodu bozulaca iin programn almasnda eitli bozukluklar kabilir. Bu
bozukluk tm sistemi olumsuz ynde etkileyebilir.
Kim tarafndan kullanldn bilmediimiz bellek blgelerine gvenli olmayan blgeler
denir. Gvenli olmayan blgelere eriilmesine ise "gsterici hatalar" denir.
Gsterici hatalar yapldnda sistem kilitlenebilir, programlar yanl alabilir. Gsterici
hatalar sonucundaki olumsuzluklar hemen ortaya kmayabilir.
Gsterici hatalar gvenli olmayan blgelere eriildiinde deil oralara veri aktarldnda
oluur.
Gsterici hatalar derleme srasnda derleyici tarafndan saptanamaz. Bu tr hatalar
programn alma zaman srasnda olumsuzluklara yol aar. Tanmlama yntemiyle elde
edilmi olan bellek blgelerine gvenli blgeler denir. Bir nesne tanmlandnda, o nesne
iin derleyici tarafndan bellekte ayrlan yer, programc iin ayrlm bir alandr ve
gvenlidir.
Daha nce belirtildii gibi gstericiler de birer nesnedir. Dier nesnelerden farklar
ilerinde adres bilgileri tutmalardr. Gstericiler de nesne olduklarna gre dier nesneler
gibi yerel ya da global olabilirler. Global olarak tanmlanm gstericiler 0 deeriyle
balatlrken, yerel gstericiler p deerleriyle balatlr:
Yerel bir gsterici tanmlandktan sonra, herhangi bir ekilde bu gstericiye bir deer
atamas yaplmaz ise gstericinin iinde rastgele bir deer bulunacandan, bu gsterici *
ileci ile ya da [ ] ileci ile kullanldnda, bellekte rastgele bir yerde bulunan bir
nesneye ulalr. Dolaysyla, elde edilen nesneye bir atama yapld zaman, bellekte,
bilinmeyen rastgele bir yere yazlm olunur. Bu durum tipik bir gsterici hatasdr.
rnekler :
int main()
{
int *p;
*p = 25;
}
/* YANLI */
return 0;
280
char *p;
int main()
{
*p = 'm';
}
return 0;
Yukardaki kodun altrlmasnda "NULL pointer assignment" eklinde bir alma zaman
hatasyla karlalabilir. Bu snama derleyicinin alabilen program iine yerletirdii
"snama kodu" sayesinde yaplr.
lkdeer verilmemi gstericilerin neden olduu hatalar ilev arlaryla da ortaya
kabilir:
int main()
{
char *ptr;
gets(ptr); /* ????? */
return 0;
Yukardaki kod parasnda standart gets ilevi ile klavyeden alnan karakterler, bellekte
rastgele bir yere yazlr. Standart gets ilevi klavyeden alnan karakterleri kendisine
argman olarak gnderilen adresten balayarak yerletirdiine gre, daha nceki rnekte
verilen hata klavyeden girilen btn karakterler iin sz konusudur.
char *ptr;
/***/
ptr = (char *) 0x1FC5;
*ptr = 'M';
Yukardaki rnekte ptr gstericisine atanan (char *) 0x1FC5 adresinin gvenli olup
olmad konusunda hibir bilgi yoktur. Adrese ilikin blgenin kullanp kullanlmad
bilinemez. her ne kadar bellek alan iinde belli amalar iin kullanlan gvenli blgeler
varsa da 1FC5 byle bir blgeyi gstermez.
Bilindii gibi bir dizi tanmlamas grdnde derleyici, derleme srasnda dizi iin bellekte
toplam dizi uzunluu kadar yer ayrr. C derleyicileri derleme zamannda bir dizinin
tarlp tarlmadn kontrol etmez.
int main()
{
int a[10], k;
281
func ilevi iinde tanmlanan str dizisi iin toplam 6 karakterlik yer ayrlyor. Standart gets
ilevi klavyeden alnan karakterleri kendisine gnderilen adresten balayarak bellee
yerletirdikten sonra, sonlandrc karakteri de diziye yazar. O halde yukardaki rnekte
programn almas srasnda 6 ya da daha fazla karakterin girilmesi gsterici hatasna
neden olur. Sonlandrc karakter de ('\0') bellekte bir yer kaplayaca iin program iin
ayrlm bir bellek alan iinde bulunmas gerekir. rnein klavyeden girilen isim
necati
olsun. gets ilevi bu karakterleri aadaki gibi yerletirir :
Sonlandrc karakter, dizi iin ayrlan blgenin dna yerletiriliyor. Bu rnekte girilen
isim daha uzun olsayd, program iin ayrlmam bir blgeye daha fazla karakter
yazlacakt. Bu tr hatalarla karlamamak iin dizi yeteri kadar uzun olmal ya da
standart bir C ilevi olan gets ilevi yerine, dizi uzunluundan daha fazla sayda eleman
yerletirilmesine izin vermeyecek bir ilev kullanlmaldr. Bu amala fgets isimli ilev
arlabilir. Standart fgets ilevini dosyalar konusunda greceksiniz.
Dizgelerle ilgili ilemler yapan standart C ilevlerinden strcpy, strcat, strncpy, strncat
ilevlerinin yanl kullanlmas da benzer hatalar oluturabilir.
282
Baz ilevler bellek bloklar zerinde genelletirilmi ilemler yapar. Bu ilevler ilem
yaptklar bellek bloklarnda ne olduuyla ilgilenmez. Bir bellek blounun ieriinin
bellekte baka bir yere kopyalandn dnn. lev kaynak adresten hedef adrese byte
byte kopyalama yaparak bu amac gerekletirebilir. Byle bir ilevin parametre
deikenleri hangi trden olmaldr?
void gstericiler herhangi bir trden olmayan gstericilerdir. Bu trden deikenlerin
tanmlarnda void anahtar szc kullanlr:
void *ptr;
void gstericilerin tr bilgisi yoktur. void gstericilerde adreslerin yalnzca saysal
bileenleri saklanr. Bu yzden void gstericilerle dier trden gstericiler (adresler)
arasnda yaplan atamalar geerlidir. void trden bir gstericiye herhangi bir trden bir
adres sorunsuzca atanabilir. Belirli trden bir gsterici deikene void trden bir adres de
ayn ekilde sorunsuzca atanabilir.
char *ptr;
void *vp;
/***/
ptr = vp;
vp = ptr;
/* Geerli*/
/* Geerli */
void gstericiler belirli bir tre ait olmadklar iin, tr bilgisine sahip olan gstericiler
zerinde uygulanan baz ilemler void trden gstericilere uygulanamaz:
i) void trden gstericilerin * ya da [ ] ilelerinin terimi olmas geersizdir. Bu ileler bir
nesneye erimek iin tr bilgisine gereksinim duyar.
void func()
{
double a[50];
void *vptr;
vptr = a; /* Geerli */
*vptr = 3.7;
/* Geersiz! */
vptr[2] = 5.6;
/* Geersiz! */
/****/
}
Yukardaki kod parasnda *vptr ve vptr[2] ifadeleri geersizdir.
ii) void trden bir adres ile bir tamsaynn toplanmas ya da void trden bir adresten bir
tamsaynn kartlmas geersizdir. nk gsterici aritmetiine gre bir gstericinin
deeri n kadar artrldnda, gsterici iindeki adresin saysal bileeni n ile gstericinin
gsterdii nesnenin tr uzunluunun arpm kadar artar. void gstericilerin trleri
olmad iin bu durumda saysal bileenin ne kadar artaca da bilinemez.
void trden gstericiler ++ ve -- ilelerinin terimi olamaz.
++ptr;
ifadesi
ptr = ptr + 1;
ifadesine edeerdir.
283
void func()
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
void *vptr = a;
void *p;
p = vptr + 2; /* Geersiz */
++vptr;
/* Geersiz */
--vptr;
/* Geersiz */
vptr += 2;
/* Geersiz */
vptr -= 3;
/* Geersiz */
}
iii) Benzer ekilde, void trden iki adres birbirinden kartlamaz. Dier trden adresler
iin, ayn blok iindeki iki nesneye ilikin iki adresin birbirinden kartlmas tamamen
geerlidir. Byle bir ifadenin deeri iki adres arasndaki nesne saysna denk bir tamsay
oldugunu hatrlayn.
void func()
{
void *p1, *p2;
int k;
/***/
k = p1 - p2;
/* Geersiz */
}
void gsterici deikenleri adreslerin yalnzca saysal bileenlerini saklamak amacyla
kullanlrlar. Dier tr gstericiler arasndaki atama ilemlerinde uyar ya da hata
oluturmadklarndan dolay, trden bamsz adres ilemlerinin yapld ilevlerde
parametre deikeni biiminde de bulunabilirler. rnein:
void func(void *p);
func isimli ilevin parametre deikeni void trden bir gsterici olduundan bu ileve
argman olarak herhangi bir trden bir adres bilgisi gnderilebilir. Yani func ilevi
herhangi bir nesnenin adresi ile arlabilir. Bu durumda derleme zamannda bir hata
olumad gibi, derleyiciden bir uyar iletisi de alnmaz.
func ilevi, ald adresteki nesnenin trne bal bir ilem yapmaz.
C dilinde ilevler void trden adreslere de geri dnebilir. void trden adreslerin herhangi
bir trden gsterici deikene atanmas geerlidir
void *func(void);
int main()
{
int *p;
char *str;
p = func();
/***/
str = func();
}
/* Geerli */
/* Geerli */
return 0;
Standart ktphanede ba mem harfleri ile balayan biiminde bir grup ilev vardr. Bu
ilevler trden bamsz olarak bellek bloklaryla ilgili genel ilemler yapar.
284
void gstericilerin kullanmn en iyi aklayan rnek, standart string.h balk dosyas
iinde bildirilen memcpy ilevidir.
void *memcpy(void *pdest, const void *psource, size_t nbytes);
memcpy ilevi ikinci parametresiyle belirtilen adresten balayarak (psource), nbytes
sayda byte' birinci parametresiyle belirtilen adresten(pdest) balayarak kopyalar.
levin sonlandrc karakterle ya da yazlarla bir ilikisi yoktur, koulsuz bir kopyalama
yapar. Yani bir blok kopyalamas sz konusudur. lev kopyalad bellek blounda ne
olduu ya da hangi trden bir veri olduuyla ilgilenmeksizin kaynak adresten hedef
adrese belirli sayda byte' kopyalar. zellikle sistem programlarnda ok kullanlan bir
standart ilevdir.
memcpy ileviyle rnein ayn trden herhangi iki dizi birbirine kopyalanabilir:
#include <stdio.h>
#include <string.h>
#define
SIZE
10
int main()
{
int a[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int b[SIZE];
int i;
memcpy(b, a, sizeof(int));
for (i = 0; i < SIZE; ++i)
printf("%d\n", b[i]);
}
return 0;
return vp1;
285
#include <stdio.h>
void *mymemcpy(void *vp1, const void *vp2, size_t n)
{
char *p1 = vp1;
const char *p2 = vp2;
while (n--)
*p1++ = *p2++;
return vp1;
}
int main()
{
int a[5] = {1, 2, 3, 4, 5};
int b[5], i;
mymemcpy(b, a, 10);
for (i = 0; i < 5; ++i)
printf("%d\n", b[i]);
return 0;
}
Ancak memcpy ilevinin akk bloklarn kopyalanmasnda davran gvence altnda
deildir.
memmove ilevi
memove da bir blou bir yerden bir yere kopyalar. levin bildirimi memcpy ilevininki ile
tamamen ayndr.
void *memmove(void *dest, const void *source, unsignt int nbytes);
ki ilev arasndaki tek fark memmove ilevinin akk bloklarn kopyalanmasnda
davrannn gvence altnda olmasdr.
akk olmayan bloklarn kopyalanmasnda memmove ilevi mi tercih edilmelidir? Hayr,
bu iyi bir tavsiye olamaz. nk:
1. memmove ilevinin byle bir gvence vermesinin ek bir yk vardr.
2. memmove ilevin kullanlmas durumunda kodu okuyan kii kopyalamann yapld
blokla kopyalamann yaplaca bloklarn bir ekilde akt konusunda gl bir izlenim
edinir.
Bu durumda, kaynak blokla hedef bloun akmas durumunda ya da akma riskinin
bulunduu durumlarda memmove ilevi, aksi halde yani sz konusu bloklarn akmad
kesinlikle biliniyorsa memcpy ilevi tercih edilmelidir.
Aada memmove ilevi iin yazlan rnek bir kod yer alyor:
void *mymemmove(void *vp1, const void *vp2, unsigned int n)
{
char *p1 = vp1;
const char *p2 = vp2;
if (p1 > p2 && p2 + n > p1) {
p1 += n;
p2 += n;
while (n--)
*--p1 = *--p2;
}
286
else
while (n--)
*p1++ = *p2++;
return vp1;
}
memset ilevi
Standart bir C ilevidir. Adresi verilen bir bellek blounu belirli bir karakter ile yani bir
byte deeri ile doldurmak iin kullanlr. levin bildirimi:
void *memset(void *block, int c, unsigned int nbytes);
Bu ilev block adresinden balayarak nbytes byklndeki blou, ikinci parametresiyle
belirtilen byte deeriyle doldurulur.
lev rnein, herhangi bir trden bir diziyi sfrlamak amacyla kullanlabilir:
double d[100];
memset(d, 0, sizeof(d));
lev aadaki gibi tanmlanabilir:
#include <stdio.h>
void *mymemset(void *block, int c, unsigned int n);
int main()
{
int a[10];
int i;
mymemset(a, 0, sizeof(a));
for (i = 0; i < 10; ++i)
printf("%d\n",a[i]);
}
return 0;
return block;
memchr ilevi
Standart bir C ilevidir. Adresi verilen bir bellek blounda belirli bir byte deerini aramak
iin kullanlr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir:
void *memchr(const void *block, int c, unsigned int nbytes);
lev block adresinden balayan nbytes iinde c deerine sahip bir byte' arar. lev ilk
bulduu c deerine sahip byte'n adresi ile geri dner. levin geri dn deeri void
287
trden bir adrestir. Eer aranan byte bulunamaz ise ilev NULL adresine geri dner.
levin baars mutlaka snanmaldr. lev aadaki gibi tanmlanabilir:
void *mymemchr(const void *p_block, int c, unsigned int nbytes)
{
const char *p = p_block;
while (nbytes--) {
if (*p == c)
return (void *)p;
p++;
}
return NULL;
memcmp ilevi
Standart bir C ilevidir. Adresleri verilen iki bellek blounu karlatrmak amacyla
kullanlr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir:
int memcmp(const void *pblock1, const void *pblock1, unsigned int nbytes);
lev block1 ve block2 adreslerinden balayan nbytes byklndeki iki blou
karlatrr. levin geri dn deeri int trdendir. Geri dn deerinin yorumlanmas
standart strcmp ilevine benzer:
ki bellek blou tamamen aynysa, yani iki bloktaki tm byte'lar birbirine eitse, ilev 0
deerine geri dner.
Birinci bellek blou ikinci bellek bloundan daha bykse ilev 0'dan byk bir deere
geri dner. Birinci bellek blou ikinci bellek bloundan daha kkse ilev 0'dan kk bir
deere geri dner. Karlatrma yle yaplr: Bloklar dk saysal adreslerden
balayarak byte byte iaretsiz tamsay olarak karlatrlr. Farkl olan ilk byte ile
karlaldnda, daha byk olan tamsayya sahip blok daha byktr.
lev aadaki gibi tanmlanabilir:
int mymemcmp(const void *vp1, const void *vp2, size_t nbytes)
{
const unsigned char *p1 = vp1;
const unsigned char *p2 = vp2;
unsigned int k;
for (k = 0; k < nbytes; ++k)
if (p1[k] != p2[k])
return p1[k] < p2[k] ? -1 : 1;
}
return 0;
288
289
DZGELER
C dilinde iki trnak iindeki karakterlere dizge ifadesi (string literal) ya da ksaca dizge
(string) denir. rnein:
"Necati Ergin"
"x = %d\n"
"ltfen bir say giriniz : "
ifadelerinin hepsi birer dizgedir.
Dizgelerin tek bir atom olarak ele alndn nceki konularmzdan anmsayacaksnz. C'de
dizgeler, derleyiciler tarafndan aslnda char trden bir dizinin adresi olarak ele alnr. C
derleyicileri, derleme aamasnda bir dizgeyle karlatnda, nce bu dizgeyi oluturan
karakterleri bellein gvenli bir blgesine yerletirir, sonuna sonlandrc karakteri ekler.
Daha sonra dizge yerine, yerletirildii yerin balang adresini koyar. Bu durumda dizge
ifadeleri aslnda, dizgelerin derleyici tarafndan yerletirildii dizinin balang adresidir.
rnein:
char *p;
p = "Necati Ergin";
gibi bir kod parasnn derlenmesi srasnda, derleyici nce "Necati Ergin" dizgesini
bellein gvenli bir blgesine yerletirir. Daha sonra yerletirdii yerin balang adresini
dizge ifadesi ile deitirir.
Dizgeler char trden bir dizinin balang adresi olarak ele alndna gre, dizgelerin char
trden gsterici deikenlere atanmalar geerlidir. Aadaki main ilevini derleyerek
altrn:
#include <stdio.h>
int main()
{
printf("adres = %p\n", "Necati");
printf("adres = %s\n", "Necati");
}
return 0;
main ilevi iinde yaplan ilk printf arsnda "Necati" dizgesi ile %p format karakteri
eleniyor. printf ilevi ile, adres bilgilerinin %p format karakteriyle ekrana
yazdrlabileceini anmsayn. Bu durumda alan kod, "Necati" yazsnn yerletirildii
yerin balang adresini ekrana yazar. kinci printf arsnda ise "Necati" dizgesi %s
format karakteri ile eleniyor. Bu durumda ekrana ilgili adresteki yaz, yani Necati yazs
yazdrlr.
imdi de aadaki arya bakn:
putchar(*"Necati");
"Necati" dizgesinin bu kez ierik ilecinin terimi olduunu gryorsunuz. erik ileci,
terimi olan adresteki nesneye eriimi saladna gre, bu nesnenin deeri 'N'
karakterinin kod numarasdr. arlan putchar ilevinin almasyla ekrana N karakteri
baslr.
Aada tanmlanan get_hex_char ilevi, onaltlk say sisteminde bir basamak deeri
hangi karakter ile gsteriliyorsa, o karakterin kullanlan karakter setindeki kod
numarasn dndryor:
#include <stdio.h>
291
return 0;
return 0;
Parametre deikeni char trden bir gsterici olan ilevi, char trden bir adres ile
armak gerektiini biliyorsunuz. nk char trden bir gsterici deikene, doal olarak
char trden bir adres atanmaldr.
Derleyiciler asndan dizgeler de char trden bir adres belirttiklerine gre, parametre
deikeni char trden gsterici olan bir ilevi, bir dizge ile armak son derece doal bir
durumdur:
puts("Necati Ergin");
Burada derleyici "Necati Ergin" dizgesini bellee yerletirip sonuna sonlandrc karakteri
koyduktan sonra artk bu dizgeyi, karakterlerini yerletirdii bellek blounun balang
adresi olarak grr. puts ilevinin parametre deikenine de artk char trden bir adres
kopyalanr. puts ilevi parametre deikeninde tutulan adresten balayarak sonlandrc
karakteri grene kadar tm karakterleri ekrana yazar. Bu durumda ekranda
292
Necati Ergin
yazs kar.
Aadaki rnekte de "Necati Ergin" dizgesi str adresine kopyalanr. Dizge ifadelerinin
bulunduu yerde, char trden bir dizinin adresi bulunduu dnlmelidir.
#include <stdio.h>
#include <string.h>
int main()
{
char str[20];
strcpy(str, "Necati Ergin");
puts(str);
return 0;
}
Dizgeler salt okunur bellek alanlarnda tutulabilir. Bu yzden bir dizgenin ieriinin
kaynak kod iinde deitirilmesi yanltr. Dizgeleri deitiren kodlar tanmsz davran
(undefined behavior) zellii gsterir. Aadaki rnei inceleyin:
#include <stdio.h>
int main()
{
char *ptr = "Durak";
*ptr = 'B'; /* Yanl */
puts(ptr);
}
return 0;
main ilevi iinde tanmlanan ptr isimli gsterici deiken "Durak" dizgesini gsteriyor. ptr
gstericisinin gsterdii nesneye atama yaplmas yanltr. Yukardaki program derleme
zamanna ynelik bir hata iermiyor.
zde Dizgeler
C derleyicileri kaynak kodun eitli yerlerinde tamamen zde dizgelere rastlasa bile
bunlar iin farkl yerler ayrabilir. Ya da derleyici, dizgelerin salt okunur yazlar olmasna
dayanarak, zde dizgelerin yalnzca bir kopyasn bellekte saklayabilir. zde dizgelerin
nasl saklanaca derleyicinin seimine braklmtr. Birok derleyici, zde dizgelerin
bellekte nasl tutulacaklar konusunda programcnn seim yapmasna olanak verir.
Dizgelerin Karlatrlmas
/***/
if ("Ankara" == "Ankara")
printf("dogru!\n");
else
printf("yanlis!\n");
/***/
293
return 0;
294
Yukardaki if deyiminde CAN ve ATA isimleri strcmp ilevinin yapt biimde, yani bir yaz
olarak karlatrlmyor. Aslnda karlatrlan yalnzca iki adresin saysal bileenidir.
Dizgelerin mr
Dizgeler statik mrl varlklardr. Dizgeler, tpk global deikenler gibi programn
yklenmesiyle bellekte yer kaplamaya balar, programn sonuna kadar bellekte kalr.
Dolaysyla dizgeler alabilen kodu bytr. Birok sistemde statik verilerin toplam
uzunluunda belli bir snrlama sz konusudur.
Dizgeler derleyici tarafndan .obj modle balayc program tarafndan da .exe dosyasna
yazlr. Programn yklenmesiyle hayat kazanrlar.
altrlabilen bir program ounlukla ana blmden oluur:
kod
data
yn
Kod blmnde, ilevlerin makine kodlar vardr. Data blmnde, statik mrl varlklar
bulunur. Global deikenler ile dizgeler bu blmde bulunur. Yn blm yerel
deikenlerin sakland bellek alandr. Yn blm her sistemde snrl bir alandr.
rnein DOS iletim sisteminde 64K byklnde bir yn alan sz konusudur. Yani
hibir zaman yerel deikenlerin bellekte kaplad alan 64K deerini geemez.
WINDOWS iletim sisteminde varsaylan yn snr deeri 1 MB dr. Ancak bu snr deeri
istenildii kadar bytlebilir.
Adrese geri dnen bir ilevin, yerel bir deikenin ya da yerel bir dizinin adresi ile geri
dnmesi gsterici hatasdr. lev sonlandnda yerel deikenler bellekten boaltlaca
iin, ilevin geri dndrd adres gvenli bir adres olmaz. Aadaki programda byle
bir hatay yaplyor:
#include <stdio.h>
char *getname()
{
char s[100];
printf("ad ve soyad giriniz : ");
gets(s);
}
return s;
/* Yanl! */
int main()
{
char *ptr;
ptr = getname();
puts(ptr);
}
return 0;
Ancak char trden bir adrese geri dnen bir ilev bir dizge ile geri dnebilir. Bu durumda
bir alma zaman hatas sz konusu olmaz. Dizgeler statik mrl varlklar
olduklarndan programn alma sresi boyunca bellekteki yerlerini korur. rnein
aadaki ilev geerli ve dorudur:
295
Dizgelerin Birletirilmesi
Dizgeler tek bir atom olarak ele alnr. Bir dizge aadaki gibi paralanamaz:
void func()
{
char *ptr = "Necati Ergin'in C Ders
Notlarn okuyorsunuz";
/* Geersiz */
/***/
}
Ancak dizgelerin uzunluu arttka dizgeleri tek bir satrda yazmak zorlaabilir. Ekrandaki
bir satrlk grntye smayan satrlar kaynak kodun okunabilirliini bozar. Uzun
dizgelerin paralanmasna olanak vermek amacyla, C derleyicileri, aralarnda boluk
karakteri dnda baka bir karakter olmayan dizgeleri birletirerek tek bir dizge olarak
ele alr. rnein:
ptr = "Necati Ergin'in C Ders "
"Notlarn okuyorsunuz";
geerli bir ifadedir. Bu durumda derleyici iki dizgeyi birletirirek kodu aadaki biimde
ele alr:
ptr = "Necati Ergin'in C Ders Notlarn okuyorsunuz";
Derleyicinin iki dizgeyi birletirmesi iin, dizgelerin arasnda boluk karakterlerinin (white
space) dnda hibir karakterin olmamas gerekir:
p = "Necati" "Ergin";
ifadesi ile
p = "NecatiErgin";
ifadesi edeerdir.
Birletirmenin yansra, bir ters bl karakteri ile sonlandrlarak sonraki satra gei
salanabilir. rnein:
ptr = "Necati Ergin'in C Ders \
Notlarn okuyorsunuz";
deyimi ile
ptr = "Necati Ergin'in C Ders Notlarn okuyorsunuz";
deyimi edeerdir. Tersbl karakterinden sonra, dizge aadaki satrn bandan devam
etmelidir. Sz konusu dizge aadaki gibi yazlrsa:
296
Notlarn okuyorsunuz";
char *p;
p = "Necati\tErgin";
ifadesinde \t tek bir karakterdir (9 numaral ASCII karakteri olan tab karakteri).
Yani
printf("dizgedeki karakter says = %d\n", strlen(p));
ifadesi ile ekrana
dizgedeki karakter says = 12
yazdrlr.
Dizge ifadelerinde dorudan ift trnak ya da ters bl karakterleri kullanlamaz. Bu
karakterlerin zel ilevleri vardr. "ift trnak" karakter deimezinin kendisini ifade
etmek iin, ift trnak karakterinden nce bir ters bl karakteri kullanlr. rnein:
#include <stdio.h>
int main()
{
puts("\"Necati Ergin\"");
return 0;
}
main ilevi iinde yaplan
puts(ptr);
ars ile ekrana
"Necati Ergin"
yazs yazdrlr.
Dizge iinde yer alan ters bl karakter deimezi, onaltlk say sisteminde de ifade
edilebilir. Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
puts("\x41\x43\x41\x42\x41");
return 0;
}
297
Bo Dizge
Bir dizge bo olabilir (null string) yani iinde hibir karakter bulunmayabilir. Bu durum iki
ift trnak karakterinin arasna hibir karakter yazlmamas ile oluturulur.
char *ptr = "";
Yukardaki deyim ile ptr gstericisine bo dizge atanyor. Bo dizgeler de bellekte bir
adres belirtir. Derleyici bo bir dizge grdnde, bellekte gvenilir bir yere yalnzca
sonlandrc karakteri yerletirir.
Bo bir dizge uzunluu 0 olan bir dizgedir. Aadaki program derleyerek altrn:
#include <stdio.h>
#include <string.h>
int main()
{
char *ptr = "";
printf("uzunluk = %d\n", strlen(ptr));
return 0;
}
char *p = "stanbul";
char *err = "Bellek yetersiz";
char *s = "Devam etmek iin bir tua basnz";
Dizgeler derleyiciler tarafndan char trden bir dizi adresi olarak ele alndna gre, char
trden gsterici deikenlere dizgelerle ilkdeer verilmesi doal bir durumdur.
Dizilere ift trnak iinde ilkdeer vermek ile, gsterici deikenlere dizgelerle ilkdeer
vermek arasndaki farka dikkat etmek gerekir:
char *p = "Deneme";
char s[10] = "Deneme";
deyimleri tamamen farkldr.
Gsterici deikenlere ilkdeer verildiinde, derleyici bunu bir dizge ifadesi olarak ele alr.
Yani dizge bellee yerletirildikten sonra balang adresi gstericiye atanr. Oysa
dizilerde nce dizi iin yer ayrlr, daha sonra karakterler tek tek dizi elemanlarna
yerletirilir. Dizilere ilkdeer verirken kullanlan ift trnak ifadeleri adres bilgisine
dntrlmez. Dizi elemanlarna tek tek char trden deimezlerle ilkdeer verme ilemi
zahmetli olduu iin, programcnn iini kolaylatrmak amac ile byle bir ilkdeer verme
kural getirilmitir.
char s[10] = "Deneme";
deyimi aslnda
char s[10] = {'D', 'e', 'n', 'e', 'm', 'e', '\0'};
ile ayn anlamdadr.
298
Yaz Tutan char Trden Dizilerle Bir Dizgeyi Gsteren char Trden
Gstericilerin Karlatrlmas
C dilinde bir yaz bilgisi en az iki ayr biimde saklanabilir:
1. Bir yaz char trden bir dizi iinde saklanabilir:
#include <stdio.h>
#include <string.h>
void foo()
{
char s1[100] = "Necati Ergin";
char s2[100];
char s3[100];
Yukardaki foo ilevinde s1 dizisine Necati Ergin yazs ilkdeer olarak yerletiriliyor.
s2 dizisine ise standart gets ilevi arsyla klavyeden bir yaz alnyor.
s2 dizisindeki yaz, standart strcpy ilevine yaplan ar ile s3 dizisine kopyalanyor.
2. Yaz bir dizge olarak saklanarak char trden bir gsterici deikenin bu dizgeyi
gstermesi salanabilir:
char *ptr = "Necati Ergin";
ki yntem, birbirinin tamamen edeeri deildir. Aadaki noktalara dikkat edilmesi
gerekir:
Dizgeler statik mrl varlklar olduklar iin programn sonlanmasna kadar bellekte yer
kaplar. Bir gsterici deikenin bir dizgeyi gsterirken, daha sonra baka bir dizgeyi
gsterir duruma getirilmesi, daha nceki dizgenin bellekten boaltlaca anlamna
gelmez:
char *p = "Bu dizge programn sonuna kadar bellekte kalacak.";
p = "artk yukardaki dizge ile bir balant kalmayacak...";
Yaznn char trden bir dizi iinde tutulmas durumunda bu yazy deitirmek
mmkndr. Dizi elemanlarna yeniden atamalar yaplarak yaz istenildii gibi
deitirilebilir. Ama dizgelerin deitirilmesi tanmlanmam davran zellii gsterir,
yanltr:
#include <stdio.h>
#include <string.h>
void foo()
{
char *ps = "Metin";
char *pstr = "Ankara";
ps[1] = '';
strcpy(pstr, "Bolu");
/***/
/* Yanl! */
/* Yanl! */
299
GSTERC DZLER
Gstericiler de birer nesne olduuna gre gsterici dizileri de tanmlanabilir:
Elemanlar gsterici deikenlerden oluan dizilere "gsterici dizileri" (pointer arrays)
denir. rnekler:
char *pstr[10];
int *ap[50];
float *kilo[10];
Gsterici dizilerinin bildirimleri, normal dizi bildirimlerinden farkl olarak '*' atomu ile
yaplr. rnein:
char str[100];
bildiriminde, str 100 elemanl char trden bir dizi iken
char *pstr[100];
bildiriminde ise, pstr 100 elemanl char trden bir gsterici dizisidir. Yani dizinin her bir
eleman char * trnden nesnedir. Dizinin her bir eleman bir gsterici deikendir.
Derleyici, bir gsterici dizisi tanmlamas ile karlanca, dier dizilerde yapt gibi,
bellekte belirtilen sayda gsterici nesnesini tutabilecek kadar, bitiik bir bellek blou
ayrr. rnein:
char *p[10];
bildirimi ile derleyici, p dizisinin 10 elemanl ve her elemann char trden bir gsterici
olduunu anlar. Kullanlan sistemde gstericilerin uzunluunun 4 byte olduu varsaylrsa,
derleyici bu dizi iin 40 byte'lk bir bellek blou ayrr. Bu durumda;
p[0], p[1], p[2], ...p[9]
dizi elemanlarnn her biri char * trndendir. Bu dizinin elemanlarndan her bir nesne,
dierinden bamsz olarak kullanlabilir.
0x1FC0,
0x1FC2,
0x1FC4,
0x1FC6,
0x1FC8
301
char trden gsterici dizileri yazlarla ilgili ilemler yapan programlarda ska kullanlr.
Program iinde sk kullanlacak yazlarn, kaynak kod iinde her defasnda bir dizge olarak
yazlmas yerine, bir gsterici dizisinin elemanlarnda saklanmas ok rastlanan bir
temadr. Aadaki rnekte error_messages dizisinin her bir elemannda, hata iletilerine
ilikin yazlar saklanyor.
char *error_messages[] = {"Bellek Yetersiz!", "Hatal ifre", "Dosya
bulunamad", "Belirtilen dosya zaten var", "src hazr deil", "Okunacak
dosya alamyor", "yazlacak dosya alamyor!..", "Belirlenemeyen
hata!"};
Artk programn herhangi bir yerinde yazlardan biri yazdrlmak istendiinde, gsterici
dizisinin herhangi bir elemanna eriilerek ilgili yaznn balang adresi elde edilir.
/*...*/
if (fp == NULL) {
printf("%s\n", err[5]);
return 5;
}
imdi de aadaki program inceleyin:
#include <stdio.h>
int day_of_week(int day, int month, int year);
char *months[] = {"January", "February", "March", "April", "May", "June",
"July", "August", "September", "October", "November", "December"};
char *days[] = {"Sunday",
"Friday", "Saturday" };
"Monday",
302
Aada tanmlanan get_month ilevi, kendisine balang adresi gnderilen bir yaznn,
ngilizce ay isimlerinden biri olup olmadn snyor. Snad yaz geerli bir ay ismi ise,
ilev bu ayn kanc ay olduu bilgisiyle (1 - 12) geri dnyor. lev, kendisine gnderilen
yaz bir ay ismine karlk gelmiyor ise 0 deeriyle geri dnyor.
#include <stdio.h>
#include <string.h>
#define
SIZE
20
int main()
{
char s[SIZE];
int result;
printf("bir ay ismi giriniz .");
gets(s);
result = get_month(s);
if (result)
printf("%s yilin %d. ayidir\n", s, result);
else
printf("%s gecerli bir ay ismi degildir\n", s);
return 0;
}
Standart olmayan stricmp ilevinin, iki yaznn karlatrmasn byk harf kk harf
duyarll olmadan yapmas dnda, strcmp ilevinden baka bir fark bulunmadn
anmsayn.
Gsterici dizileri tpk dier diziler gibi, yerel ya da global olabilir. Dizinin global olmas
durumunda, dizi hayata baladnda dizinin elemanlarnn hepsinin iinde 0 deerleri
bulunurken, yerel bir gsterici dizisinin iinde rastgele deerler olur. Dizinin her bir
eleman iinde bir adres bilgisi olduuna gre, atama yaplmam global gsterici
dizilerinin her bir eleman iinde 0 adresi (NULL adresi) bulunur. lkdeer verilmemi
yerel gsterici dizilerinin elemanlar iinde ise rastgele adres deerleri bulunur.
Bir gsterici hatasna neden olmamak iin nce gsterici dizisi elemanlarna gvenli
adresler yerletirmek gerekir. Dizgeler statik mrl varlklar olduklar iin, bir gsterici
dizisinin elemanlarna dizgelerle deer vermek bir gsterici hatasna neden olmaz. Zira
dizgeler, daha nce de belirtildii gibi, nce derleyici tarafndan bellekte gvenli bir
blgeye yerletirilir, daha sonra ise yerletirildikleri bloun balang adresi olarak ele
alnr.
Baz durumlarda, bir gsterici dizisinin son elemanna zellikle NULL adresi atanr. Byle
bir gsterici dizisi, dizi boyutu belirtilmeden ilenebilir. Aadaki rnei inceleyin:
303
#include <stdio.h>
int main()
{
int k;
char *names[] = {"Ali", "Hasan", "Mehmet", "Sebahat", "Fatma",
"Tuncay", "Deniz", "Kerim", "Necati", NULL};
for (k = 0; names[k] != NULL; ++k)
puts(names[k]);
return 0;
}
Aadaki rnekte, char trden bir gsterici dizisinin elemanlar gsterdikleri yazlarn
byklklerine gre sralanyor:
#include <stdio.h>
#include <string.h>
#define
SIZE
10
int main()
{
int i, k;
char *names[SIZE] = {"Ali", "Hasan", "Mehmet", "Sebahat", "Fatma",
"Tuncay", "Kaan", "Taylan", "Aleyna", "Deniz"};
printf("dizi yazdiriliyor!\n");
for (k = 0; k < SIZE; ++k)
puts(names[k]);
for (k = 0; k < SIZE -1; ++k)
for (i = 0; i < SIZE - 1 -k; ++i)
if (strcmp(names[i], names[i + 1]) > 0) {
char *temp = names[i];
names[i] = names[i + 1];
names[i + 1] = temp;
}
printf("siralanmis dizi yazdiriliyor!\n");
for (k = 0; k < SIZE; ++k)
puts(names[k]);
return 0;
}
Sralama amacyla yine "kabarck sralamas" algoritmas kullanlyor. Gsterici dizisinin
ardk iki elemannn deerleri
strcmp(names[i], names[i + 1]) > 0
koulunun doru olmas durumunda takas ediliyor. Bu, "dizinin i indisli eleman olan
gstericinin gsterdii yaz, i + 1 indisli elemannn gsterdii yazdan daha bykse"
anlamna gelir, deil mi? Eer takas ilemi, daha nce dier trden dizileri sralarken
kullanlan
if (names[i] >
names[i + 1] > 0)
304
#include <stdio.h>
int main()
{
char *names[6] = {"Ali", "Hasan", "Mehmet", "Sebahat", "Fatma" "Tunc"};
int k;
for (k = 0; k < 6; ++k)
puts(names[k]);
return 0;
}
Bcei grebildiniz mi?
Aralarnda boluk karakteri dnda baka bir karakter olmayan dizgelerin, derleyici
tarafndan otomatik olarak birletirilerek tek bir dizge olarak deerlendirildiini
biliyorsunuz. Yukardaki kod parasnda names isimli dizinin elemanlarna ilkdeer
verirken kullanlan "Fatma" dizgesiyle "Tuncay" dizgesi arasnda virgl atomu
konulmam. Bu durumda derleyici bu iki dizgeyi birletirerek tek bir dizge olarak yani
"FatmaTuncay" biiminde ele alr. Bu dizge gsterici dizisinin sondan bir nceki elemanna
atanr. Bu durumda dizinin son elemanna NULL adresi atanm olur deil mi? main ilevi
iindeki for dngs de NULL adresine ular. phesiz bu bir gsterici hatasdr.
Aada bir tamsayy bir yazya dntren syaz isimli bir ilev tanmlanyor. lev
tanmlarnda yer alan gsterici dizilerinin kullanmn inceleyiniz:
#include <stdio.h>
void yuzyaz(unsigned int val)
{
static const char *birler[] = {"", "bir", "iki", "uc", "dort", "bes",
"alti", "yedi", "sekiz", "dokuz"};
static const char *onlar[] = {"", "on", "yirmi", "otuz", "kirk",
"elli", "altmis", "yetmis", "seksen", "doksan"};
int y = val / 100;
int o = val % 100 / 10;
int b = val % 10;
if (y > 1)
printf("%s", birler[y]);
if (y > 0)
printf("yuz");
if (o > 0)
printf("%s", onlar[o]);
if (b > 0)
printf("%s", birler[b]);
305
yuzyaz(milyon);
if (milyon)
printf("milyon");
int main()
{
unsigned int val;
printf("bir tamsayi giriniz:");
scanf("%u", &val);
syaz(val);
printf("\n");
}
return 0;
306
307
**pp = 2000;
printf("x = %d\n", x);
printf("y = %d\n", y);
return 0;
}
Yukardaki main ilevinde, int trden x deikeninin adresi, p isimli gsterici deikene
atandktan sonra
deyimiyle, p gstericisinin gsterdii nesneye yani x deikenine 100 deeri atanyor.
*p = 100;
pp = &x;
deyimiyle ise p nesnesinin adresi pp isimli gstericiye atanyor. Bu atamadan sonra pp
gstericisi p gstericisini gsterir.
*pp = &y;
deyimi ile pp gstericisinin gsterdii nesneye yani p deikenine bu kez y deikeninin
adresi atanyor. Bu atamadan sonra yrtlecek
*p = 200;
atamas ile artk p gstericisinin gsterdii nesneye yani y deikenine 200 deeri atanr.
imdi de aadaki deyimi inceleyin:
**pp = 2000;
erik ilecinin, ile ncelik tablosunun ikinci dzeyinde yer aldn ve sadan sola
ncelik ynne sahip olduunu biliyorsunuz.
Bu durumda nce *pp ifadesi ile pp gstericisinin gsterdii nesneye yani p nesnesine
eriilir. Daha sonra *(*pp) ifadesiyle de pp gstericisinin gsterdii nesnenin gsterdii
nesneye, yani p nesnesinin gsterdii nesneye, yani y deikenine ulalr. Deyimin
yrtlmesiyle y deikenine 2000 deeri atanm olur.
**pp ifadesi, pp'nin gsterdii nesnenin gsterdii nesneye, yani y nesnesine karlk
gelir.
Yerel bir nesnenin deerini deitirecek bir ilev, yerel nesnenin adresi ile arlmaldr.
Yerel bir gsterici deikenin deerini deitirecek bir ilev de, yerel gstericinin deerini
deil adresini almaldr. Aadaki rnei inceleyin:
void swap_ptr(int **p1, int **p2)
{
int *temp = *p1;
*p1 = *p2;
*p2 = temp;
}
Yukardaki programda int * trnden iki nesnenin deerini takas etmek amacyla
swap_ptr isimli bir ilev tanmlanyor. levin int ** trnden iki parametresi olduunu
gryorsunuz. Bu ilev phesiz deerlerini takas edecei nesnelerin adresleri ile
arlmaldr:
308
#include <stdio.h>
int main()
{
int x = 10;
int y = 20;
int *p = &x;
int *q = &y;
printf("*p =
printf("*q =
swap_ptr(&p,
printf("*p =
printf("*q =
}
%d\n",
%d\n",
&q);
%d\n",
%d\n",
*p);
*q);
*p);
*q);
return 0;
main ilevi iinde int trden x ve y isimli deikenler tanmlanyor. Daha sonra
tanmlanan p gsterici deikeni x nesnesini, q gsterici deikeni ise y deikenini
gsteriyor.
Daha sonra arlan swap_ptr ilevine p ve q gstericilerinin adresleri gnderiliyor. leve
yaplan ardan sonra, p gstericisi y deikenini, q gstericisi ise x deikenini gsterir.
imdi de aadaki ilevi inceleyin:
void ppswap(int **p1, int **p2)
{
int temp = **p1;
**p1 = **p2;
**p2 = temp;
}
ppswap ilevi hangi nesnelerin deerlerini takas ediyor?
Bir dizi zerinde ilem yapan ilevin dizinin balang adresi ile dizinin boyutunu almas
gerektiini biliyorsunuz.
int trden bir dizi ile ilgili ilem yapan bir ilevin bildirimi aadaki gibi olabilir:
void process_array(int *, int size);
Byle bir ilev dizinin ilk elemannn adresi ve dizinin boyutu ile arlr, deil mi?
int a[10];
gibi bir dizi sz konusu olduunda, dizi ismi olan a, ileme sokulduunda otomatik olarak
bu dizinin adresine dntrlr.
Yani derleyici asndan bakldnda a ifadesi &a[0] ifadesine edeerdir.
Bu ilev
process_array(a, 10);
biiminde arlabilir.
Bu kez elemanlar char * trden olan bir dizi tanmlanm olsun:
char *a[100];
309
Yine a ifadesi bir ileme sokulduunda, bu dizinin ilk eleman olan nesnenin adresine,
yazni dizinin balang adresine dntrlr.
Bu dizinin ilk eleman olan nesne char * trnden olduuna gre, bu nesnenin adresi char
** trndendir. Byle bir dizi zerinde ilem yapacak ilev, bu dizinin balang adresi ile
dizinin boyutunu alacana gre, aadaki gibi bildirilmelidir:
void process_array(char **parray, int size);
Bu ilev
process_array(a, 10);
biiminde arlabilir.
imdi de aadaki program inceleyin:
#include <stdio.h>
#include <string.h>
void swap_ptr(char **p1, char **p2)
{
char *temp = *p1;
*p1 = *p2;
*p2 = temp;
}
void display_str_array(char **p, int size)
{
int k;
int main()
{
char *names[10] = {"Eda", "Abdurrahman", "Berk", "Zarife", "Yusuf",
"Levent", "Sezgi", "Sukufe", "Ufuk", "Cansu"};
display_str_array(names, 10);
sort_str_array(names, 10);
getchar();
display_str_array(names, 10);
return 0;
}
310
OK BOYUTLU DZLER
C dilinde iki ya da daha ok boyuta sahip diziler tanmlanabilir:
int a[5][10][20];
Yukardaki bildirimle a dizisi 3 boyutlu bir dizidir.
int m[5][10];
m dizisi 2 boyutlu bir dizidir.
Uygulamalarda daha ok kullanlan 2 boyutlu dizilerdir. 2 boyutlu diziler matris olarak da
isimlendirilir.
Matrisler
C'de iki boyutlu bir dizi aslnda belirli bir boyuttaki tek boyutlu dizilerin dizisi olarak ele
alnr:
int a[5][10];
a dizisi her eleman 10 elemanl int trden bir dizi olan 5 elemanl bir dizidir. Yani a
dizisinin gerekte boyutu 5' tir. kinci keli ayra iinde yer alan 10 ifadesi a dizisinin
elemanlar olan dizilerin boyutudur.
Bu dizi tm dier diziler gibi bellekte bitiik bir blok halinde bulunur. Yani derleyici byle
bir dizi iin bellekte 50 x sizeof(int) byte byklnde bir blok ayarlar.
ok boyutlu bir dizinin elemanlaryla, bu dizinin problem dzlemindeki karl olan
matrisin elemanlarn birbirlerine kartrmak, sk yaplan hatalardandr.
5 x 10 boyutunda bir matrisin 50 eleman vardr. Ancak yukardaki a dizisinin yalnzca 5
eleman vardr.
Yukardaki dizide yer alan toplam 50 tane int trden nesnenin her birine nasl ulalabilir?
Bunun iin keli ayra ileci dizi ismiyle birlikte 2 kez kullanlabilir:
Aadaki program inceleyin. Bu programda bir matriste yer alan tm elemanlara 0-100
aralnda rastgele deerler veriliyor. Daha sonra matriste yer alan tm elemanlarn
deeri ekrana yazdrlyor:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
ROW
COL
5
10
int main()
{
int a[ROW][COL];
int i, k;
srand(time(0));
for (i = 0; i < ROW; ++i)
for (k = 0; k < COL; ++k)
a[i][k] = rand() % 100;
/********/
311
return 0;
Yukardaki kodu inceleyin. a dizisi 10 elemanl int trden dizilerin oluturduu bir dizidir.
Yani a[0] bu dizilerden ilkine ve a[4] bu dizilerden sonuncusuna karlk gelir.
a[0] bu dizilerden ilkine karlk geldiine gre a dizisinin ilk eleman olan 10 elemanl
dizinin rnein 5 indisli elemanna
a[0][5]
biiminde ulalabilir, deil mi? Keli ayra ilecinin birinci ncelik seviyesinde ve soldan
saa doru ncelik ynne sahip bir ile olduunu anmsayn.
Byle bir iki boyutlu diziye, problem dzlemindeki karl olarak, yani bir matris olarak
bakldnda, bu matriste yer alan ilk eleman
a[0][0]
nesnesidir. Bu nesne matrisin 1. satr ve 1. stununda yer alan nesnedir. Matrisin son
eleman ise
a[4][9]
nesnesidir. Bu nesne matrisin 5. satr ve 10. stununda yer alan nesnedir. Matristeki ilk
nesnenin adresi int trden bir gstericiye atanp bu gsterici srekli artrlrsa, matristeki
tm elemanlarn adresleri elde edilerek son elemann adresine ulalabilir. imdi
yukardaki rnee bir ekleme yapalm:
int main()
{
int a[ROW][COL];
int i, k;
int *ptr;
srand(time(0));
for (i = 0; i < ROW; ++i)
for (k = 0; k < COL; ++k)
a[i][k] = rand() % 100;
/********/
for (i = 0; i < ROW; ++i) {
for (k = 0; k < COL; ++k)
printf("%2d ", a[i][k]);
printf("\n");
}
printf("***********************************************\n");
ptr = &a[0][0];
312
i = ROW * COL;
while (i--)
printf("%2d ", *ptr++);
printf("\n");
}
return 0;
Aslnda iki boyutlu bir dizinin ileve geirilmesi tek boyutlu dizilerin ileve
geirilmesinden farkl deildir. Bir diziyi ileve geirmek iin dizinin ilk elemannn adresi
ve dizinin boyutu ileve gnderilir, deil mi?
Peki yukardaki rnekte yer alan a gibi bir dizinin ilk eleman nedir? Bu dizinin ilk eleman
a[0]'dr. Ve bu 10 elemanl int trden bir dizidir. imdi bu elemann adresinin alndn
dnelim:
&a[0]
Bu ifadenin tr nedir? C diline yeni balayanlar byle bir ifadenin trnn (int **) tr
olmas gerektiini dnrler. Oysa bu ifadenin tr daha nce karlamadmz bir
trdr:
10 elemanl int trden bir dizinin adresi olabilecek bir tr!
Bu tr biligisi C dilinde yle ifade edilir:
int (*)[10];
Yani a dizisinin ilk eleman bu trden bir gstericiye atanabilir:
int (*ptr)[10] = &a[0];
Bir dizinin ismi bir ifade iinde ileme sokulduunda otomatik olarak dizinin ilk elemann
adresine dntrlr, deil mi? O zaman &a[0] ifadesi yerine dorudan a ifadesi de
yazlabilir:
int (*ptr)[10] = a;
Evet, a ifadesinin tr int ** deil, int (*)[10] trdr.
313
Bu durum yle de ifade edilebilir: ptr int trden 10 elemanl bir diziyi gsteren
gstericidir. ptr herhangi bir boyuttaki int trden bir diziyi deil, yalnzca 10 elemanl int
trden bir diziyi gsterebilir. ptr gstericisi rnein ++ ileci ile 1 artrlrsa bellekte yer
alan bir sonraki 10 elemanl int trden diziyi gsterir. Yani
ptr + 1
adresinin saysal bileeni ptr adresinin saysal bileeninden sizeof(int) * 10 kadar daha
byktr.
ptr + 1 ifadesi iki boyutlu a dizisinin ikinci eleman olan 10 elemanl int trden dizinin
adresine karlk gelir. Bu durumda
*(ptr + 1)
ifadesi aslnda a[1] dizisidir.
phesiz *(ptr + 1) ifadesi yerine ptr[1] ifadesi de kullanlabilir. Bu durumda bu gsterici
yardmyla iki boyutlu dizinin tm elemanlarna bir dng ile ulalabilir:
for (k = 0; k < ROW; ++k)
ptr[k]
gibi bir dng deyimi ile dngnn her turunda matrisin bir satrna, yani iki boyutlu
dizinin bir eleman olan COL uzunluundaki dizilere ulalr.
Bu durumda 2 boyutlu bir dizi yani bir matris zerinde ilem yapacak bir ilevin
parametre deikeni byle bir gsterici olabilir. levin dier parametresi de dizinin
boyutunu alabilir. Aadaki program inceleyin:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
ROW
COL
5
10
314
int main()
{
int a[ROW][COL];
srand(time(0));
fill_matrix(a, ROW);
display_matrix(a, ROW);
}
return 0;
int (*ptr)[10] gibi bir gsterici bir ilevin parametre deikeni olarak kullanldnda int
ptr[][10] biiminde de yazlabilir. Yani aadaki iki bildirim birbirine edeerdir:
void fill_matrix(int (*ptr)[10], int size);
void fill_matrix(int ptr[][10], int size);
Hatta istenirse ilk keli ayra iine de bir tamsay yazlabilir. Ancak derleyici asndan bu
tamsaynn bir nemi yoktur. Baz programclar yalnzca okunabilirlik asndan dizinin
boyutunu ilk keli ayralarn iine yazarlar. Yukardaki bildirim yle de yaplabilirdi:
void fill_matrix(int ptr[5][10], int size);
Matrisleri ileve geirmenin bir baka yolu da matristeki ilk elemann adresini, matrisin
satr ve stun saysn ileve geirmek olabilir. Yukarda yazdmz ilevleri imdi
deitiriyoruz:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
ROW
COL
5
10
315
return 0;
Bu kez ilevler matristeki ilk elemann adresini, matrisin satr ve stun saysn alarak
matristeki tm elemanlara, bunlarn bellekte bitiik olarak yer ald bilgisinden hareketle
gsterici aritmetiiyle ulayor.
Tabi bu ilevlere dizi ismi de argman olarak geilebilir. Bu durumda tr dntrme
ileci kullanlmaldr:
void fill_matrix((int *)a, ROW, COL);
void display_matrix((int *)a, ROW, COL);
Dizi isimlerinin derleyici tarafndan otomatik olarak dizilerin ilk elemannn adresine
dntrldn biliyorsunuz. Benzer biimde iki boyutlu bir dizinin eleman olan tek
boyutlu bir diziye keli ayra ileci ile ulaldnda, bu ifade otomatik olarak bu tek
boyutlu dizinin ilk elemannn adresine dntrlr. Aadaki rnei inceleyin:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
ROW
COL
5
10
316
/******/
for (i = 0; i < ROW; ++i)
display_array(a[i], COL);
}
return 0;
ki boyutlu dizilere de ilkdeer verilebilir. Verilen ilkdeerler srasyla iki boyutlu dizinin
eleman olan tek boyutlu dizilerin elemanlarna atanr.
#include <stdio.h>
int main()
{
int a[3][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15};
int i, k;
stenirse ilkdeerleri ieren bloun iinde isel bloklar kullanlarak, eleman olan dizilerin
belirli sayda elemanna ilkdeer verilebilir. lk deer verilmeyen elemanlara otomatik
olarak 0 deeri atanr. Aadaki rnei de derleyerek altrn:
#include <stdio.h>
int main()
{
int a[3][5] = { {1, 2}, {3, 4, 5}, {6, 7, 8, 9}};
int i, k;
for (i = 0; i < 3; ++i) {
for (k = 0; k < 5; ++k)
317
}
return 0;
Nasl bir yaz char trden bir dizinin iinde tutulabiliyor ise, mantksal bir iliki iindeki n
tane yaz iki boyutlu bir dizi iinde tutulabilir:
char words[10][50];
Yukarda tanmlanan words isimli dizinin 10 eleman vardr. words isimli dizinin her bir
eleman char trden 50 elemanl dizidir. words dizisinin iinde uzunluu 49 karakteri
gemeyen, 10 tane yaz tutulabilir.
words[3]
Bu yazlardan drdncsnn adresi
words[6][2]
Bu yazlardan yedincisinin nc karakteridir.
Aadaki program inceleyin:
#include <stdio.h>
#include <string.h>
#define
ARRAY_SIZE
10
return 0;
main ilevi iinde iki boyutlu names isimli bir dizi tanmlanyor.
char names[ARRAY_SIZE][20];
318
char trden bir diziye ift trnak iinde yer alan karakterlerle ilkdeer verilebileceine
gre iki boyutlu bir dizinin elemanlar olan char trden tek boyutlu dizinin elemanlarna
da benzer ekilde ilkdeer verilebilir:
char names[5][10] = {"Ali", "Veli", "Hasan", "Tuncay", "Deniz"};
Yukarda, names isimli iki boyutlu dizinin eleman olan 10 elemanl char trden dizilere,
ift trnak iinde yer alan yazlarla ilkdeer veriliyor.
imdi de iki boyutlu char trden bir dizi zerinde ilem yapacak baz ilevler
tanmlayalm:
#include <stdio.h>
#include <string.h>
#define
ARRAY_SIZE
20
319
return 0;
Yukarda tanmlanan ilevlerden swap_str ilevi adreslerini ald iki yazy takas ediyor.
display_names isimli ilev ise balang adresini ve boyutunu ald iki boyutlu dizide yer
alan isimleri ekrana yazdryor.
sort_names isimli ilev ise balang adresini ve boyutunu ald iki boyutlu dizi iinde
tutulan isimleri kkten bye doru sralyor.
Mantksal iliki iinde n tane yaz, bir gsterici dizisi yardmyla tutulabilecei gibi iki
boyutlu bir dizi iinde de tutulabilir:
char *pnames[10] = {"Ali", "Veli", "Hasan", "Deniz", "Ferda", "Murat",
"Ayca", "Erdem", "Kaan", "Gurbuz"};
char names[10][20] = {"Ali", "Veli", "Hasan", "Deniz", "Ferda", "Murat",
"Ayca", "Erdem", "Kaan", "Gurbuz"};
Elemanlar dizgeleri gsteren bir gsterici dizisinin elemanlar, yalnzca okuma amacyla
kullanlabilecek yazlarn balang adreslerini tutar. Dizgelerin yalnzca okuma amacyla
kullanlabilecek yazlar olduunu anmsamalsnz.
Yukardaki rnekte, pnames dizisinin eleman olan gstericilerin gsterdii yazlar
zerinde deiiklik yapmak, doru deildir. Ancak names dizisi iinde yer alan isimler
istenirse deitirilebilir. Aadaki main ilevini inceleyin:
#include <string.h>
int main()
{
int k;
for (k = 0; k < 10; ++k) {
strrev(pnames[k]);
strrev(names[k]);
}
}
/* Yanl */
/* Yanl deil*/
return 0;
320
exit levi
abort levi
321
altrlabilen bir program bir bellek alann kullanr. Nesneler programn kulland bellek
alanndaki bloklardr. Bir ya da birden fazla nesnenin programn alma zamannda
kaplayaca yer iki ayr biimde elde edilebilir:
1. Nesne(ler) iin derleyici derleme zamannda bellekte bir yer ayarlar.
2. Nesne(ler) iin yer programn alma zamannda elde edilir.
Programn alma zaman srasnda belli bir byklkte bitiik (contigous) bir bellek
alannn alan program tarafndan ayrlmasna , byle bir alann istenildii zaman
sisteme geri verilmesine olanak salayan yntemlere "dinamik bellek ynetimi" (dynamic
memory management) denir.
C dilinde bir deiken ya da bir dizi tanmland zaman, bu deiken ya da dizinin
programn alma zamannda kaplayaca yer, derleme zamannda derleyici tarafndan
ayrlr:
int a[100];
Derleme srasnda yukardaki gibi bir dizi tanm ile karlaan derleyici bellekte -eer
kullanlan sistemde int tr uzunluunun 2 byte olduu varsaylrsa- toplam 200 byte yer
ayrr. Programn almas srasnda bir dizinin uzunluunu deitirmek mmkn deildir.
Eer bu dizi yerel ise otomatik mrldr. Yani iinde tanmlanm olduu bloun
kodunun yrtlmesi sresince hayatn srdrr. Dizi global ise statik mrldr. Yani
dizi, programn alma sresi boyunca bellekteki yerini korur.
Dinamik bellek ynetimi aralar kullanlarak bir ya da birden fazla nesnenin programn
alma zamannda kaplayaca yerin elde edilmesi derleme zamanndan, alma
zamanna geciktirilebilir.
Kullanlacak bellek blounun elde edilmesi neden derleme zamanndan alma zamanna
kaydrlsn? Bunun nemli baz nedenleri vardr. lerleyen sayfalarda bu nedenlerin
ouna deinilecek.
Bellek alannn programn alma zamannda elde edilmesinin en nemli nedenlerinden
biri gereksinim duyulan bellek blounun byklnn programn alma zamannda
belli olmasdr.
Dizi tanmlamalarnda dizi boyutunu gsteren ifade yani keli ayracn iindeki ifade,
deimez ifadesi olmaldr. Bu ifade deiken ieremez. nk derleyicinin dizi iin
bellekte yer ayrabilmesi iin, dizi boyutunu derleme zamannda bilmesi gerekir. Oysa
birok uygulamada kullanlmas gereken dizinin boyutu programn alma zamannda
belirlenir. Bir dizindeki dosyalarn isimlerinin kkten bye doru sralanmak amacyla
geici olarak bir dizide saklanmas gerektiini dnn. Bu amala kullanlacak dizinin
boyutu ne olmaldr? Bu balangta belli deildir. nk dizin iinde ka dosya olduu
belli deildir. Bu tip durumlara zellikle veri taban uygulamalarnda sk rastlanr. Baz
uygulamalarda dizilerin gerek uzunluu programn almas srasnda, ancak birtakm
olaylar sonucunda kesin olarak belirlenebilir. Bu durumda dizilerle alan programc
herhangi bir gsterici hatasyla karlamamak iin dizileri en kt olasl gz nnde
bulundurarak mmkn olduunca byk tanmlamak zorundadr. Bu da bellein verimsiz
kullanlmas anlamna gelir. stelik tanmlanan diziler yerel ise tanmlandklar bloun
sonuna kadar, tanmlanan diziler global ise programn almasnn sonuna kadar bellekte
tutulur. Oysa dizi ile ilgili ilem biter bitmez, dizi iin ayrlan bellek blgesinin boaltlmas
verimli bellek kullanm iin gereklidir.
Bellein kontrolu iletim sistemindedir. letim sistemlerinin baka bir amala
kullanlmayan bir bellek alann kullanma sunmasna yarayan sistem ilevleri vardr.
phesiz bu ilevler dorudan arlabilir. Ancak uygulamalarda ounlukla standart C
323
malloc ilevi
malloc ilevi programn alma zaman srasnda bellekten dinamik bir blok elde etmek
iin kullanlr. levin stdlib.h balk dosyas iindeki bildirimi aadaki gibidir:
void *malloc(size_t nbyte);
size_t trnn, unsigned int ya da unsigned long trlerinden birinin typedef ismi
olduunu biliyorsunuz.
lev, elde edilmek istenen bloun byte olarak uzunluunu alr. Ayrlan alann bitiik
(contiguous) olmas gvence altna alnmtr. malloc ilevinin geri dn deeri elde
edilen bellek blounun balang adresidir. Bu adres void trden olduu iin, herhangi bir
trden gstericiye sorunsuz bir ekilde atanabilir. Bu adres herhangi bir trden
gstericiye atand zaman artk elde edilen blok, balang adresini tutan gsterici
yardmyla bir nesne ya da bir dizi gibi kullanlabilir. malloc ilevinin istenilen blou
ayrmas gvence altnda deildir. malloc ilevi birok nedenden dolay baarsz olabilir.
Bellekte elde edilmek istenen alan kadar bo bellek alannn bulunmamas sk grlen bir
baarszlk nedenidir.
malloc ilevi baarsz olduunda NULL adresine geri dner. lev arsnn baars
mutlaka snanmaldr. malloc ilevi ile bellekte bir blok elde edilmesine ilikin aada bir
rnek veriliyor:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *pd;
int k, n;
srand(time(0));
n = rand() % 100 + 10;
pd = (int *) malloc(n * sizeof(int));
if (pd == NULL) {
printf("cannot allocate memory\n");
exit(EXIT_FAILURE);
}
printf("rastgele %d sayi\n", n);
for (k = 0; k < n; ++k) {
pd[k] = rand() % 100;
printf("%d ", pd[k]);
}
/***/
}
Yukardaki main ilevinde, n deikeninin deeri programn alma zamannda elde
standart rand ilevine yaplan ar ile rastgele olarak elde ediliyor. Daha sonra standart
malloc ileviyle n tane int trden nesnenin sabilecei kadar byklkte bir bellek blou
elde ediliyor.
324
malloc ilevinin baars bir if deyimiyle snanyor. Baarszlk durumunda, yani malloc
ilevinin geri dn deerinin NULL adresi olmas durumunda standart exit ilevi
arlarak program sonlandrlyor.
Daha sonra elde edilen dinamik blok int trden bir dizi olarak kullanlyor. Dinamik dizinin
elemanlarna 0 - 100 aralnda rastgele deerler atanyor.
Kaynak kodun tanabilirlii asndan kullanlacak bellek blounun bykl sizeof
ileciyle elde edilmelidir.
phesiz, malloc ilevi baarsz olduunda program sonlandrmak zorunlu deildir.
Atama ile snama bir defada da yaplabilir.
if ((pd = (int *) malloc(n * sizeof(int)) ==
printf("cannot allocate memory\n");
exit(EXIT_FAILURE);
}
NULL) {
malloc ilevinin baars mutlaka snanmaldr. levin baarsz olmas durumunda, geri
dn deeri olan adresten okuma ya da yazma yaplmas, NULL adresinin ieriinin
alnmasna neden olur. Bu da bir gsterici hatasdr.
Programclar ou kez, kk miktarlarda ok sayda bloun ayrlmas durumunda,
snamay gereksiz bulma eilimindedir. Oysa snama ileminden vazgemek yerine daha
kolaylatrc yntemler denenmelidir. rnein p1, p2, p3, p4, p5 gibi 5 ayr gsterici
deikenin her biri iin n byte dinamik alan elde edilmek istensin. Bu durumda snama
mantksal ileler ile tek bir if deyimi ile yaplabilir.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *p1, *p2, *p3, *p4, *p5;
p1
p2
p3
p4
p5
=
=
=
=
=
(char
(char
(char
(char
(char
*)malloc(n);
*)malloc(n);
*)malloc(n);
*)malloc(n);
*)malloc(n);
325
if (!pd) {
printf("cannot allocate memory!..\n");
exit(EXIT_FAILURE);
}
return pd;
}
Yukarda tanmlanan cmalloc isimli ilevin parametrik yaps malloc ileviyle ayn
olduunu gryorsunuz. lev malloc ilevinden farkl olarak, baarszlk durumunda
program sonlandryor. malloc ile yaplacak bir dinamik blok elde etme giriiminin
baarsz olmas durmunda program bir hata iletisi verilerek sonlandrlacaksa, kaynak
kodun srekli yinelenmesi yerine, cmalloc ilevi arlabilir.
malloc ilevinin geri dn deeri void trden bir adres olduu iin, bu adres sorunsuzca
herhangi bir trden bir gsterici deikene atanabilir. Ancak okunabilirlik asndan malloc
ilevinin geri dn deeri olan adresin, tr dntrme ileci yardmyla, kullanlacak
gsterici deikenin trne dntrlmesi nerilir. Bylece malloc ilevi ile elde edilen
bloun hangi trden nesne ya da nesnelermi gibi kullanlaca bilgisi kodu okuyana
verilmi olur.
C dilinin standartlatrlmasndan nceki dnemde yani klasik C dneminde, void trden
gstericiler olmad iin, malloc ilevinin geri dn deeri char trden bir adresti. Bu
durumda, geri dn deeri olan adresin, char tr dnda bir gstericiye atanmas
durumunda tr dnm bir zorunluluktu.
malloc ilevi birden fazla kez arlarak birden fazla dinamik alan elde edilebilir. malloc
ilevine yaplan farkl arlarla elde edilen bloklarn bellekte bitiik olmas gvence
altnda deildir. Bu yzden, her bir ilev arsnn geri dn deeri olan adres mutlaka
bir gsterici deikende saklanmaldr. Aadaki kod paras ardk malloc arlarnn
bitiik bellek bloklar elde edeceini varsayd iin yanltr:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *pd;
int i;
pd = (int *)malloc(sizeof(int) * 10);
if (!pd) {
printf("cannot allocate memory!\n");
exit(EXIT_FAILURE);
}
if (malloc(sizeof(int) * 10) == NULL) {
printf("cannot allocate memory!\n");
exit(EXIT_FAILURE);
}
/* Yeni blok eski bloun hemen altnda olmak zorunda deildir */
for (i = 0; i < 20; ++i)
pd[i] = i;
/*...*/
return 0;
}
malloc ilevi ile elde edilen bloun iinde p deerler vardr. Aadaki kod paras ile bu
durumu snaynz:
326
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *pd;
int i;
pd = (int *) malloc(10 * sizeof(int));
if (!pd) {
printf("yetersiz bellek!..\n");
exit(EXIT_FAILURE);
}
for (i = 0; i < 10; ++i)
printf("pd[%d] = %d\n", i, pd[i]);
return 0;
}
Dinamik bellek ilevlerinin kullanmna sunulan bellek blgesi ngilizce'de heap olarak
isimlendirilir. C++ dilinde bu alan free store olarak isimlendirilir. heap alannn bykl
sistemden sisteme deiebilir ancak alabilir bir program sz konusu olduunda,
altrlacak programn bellee yklenmesiyle, belirli byklkte bir heap alan programn
kullanmna ayrlr.
Sistemlerin ounda malloc ilevinin parametre deikeni unsigned int (size_t) trnden
olduuna gre, malloc ilevi ile DOS altnda en fazla 65535 byte yani 64KB byklnde
bir blok elde edilebilir. Oysa UNIX, WINDOWS gibi 32 bitlik sistemlerde unsigned int tr
4 byte uzunluunda olduuna gre, bu sistemlerde teorik olarak, malloc ilevi ile
4294967295 byte (4 MB) uzunluunda bitiik bir blok elde edilebilir. Tabi bu durum,
dinamik yer ayrma ileminin gvence altnda olduu anlamna gelmez.
Heap alan da snrldr. Srekli malloc ilevinin arlmas durumunda, belirli bir noktadan
sonra ilevler baarsz olarak NULL adresine geri dner.
Aada, kullanlan heap alannn bykl bir kod ile elde edilmeye allyor:
#include <stdio.h>
#include <stdlib.h>
#define BLOCK_SIZE
2048
int main()
{
long total = 0L;
void *pd;
for (;;) {
pd = malloc(BLOCK_SIZE);
if (!pd)
break;
total += BLOCK_SIZE;
}
printf("toplam heap alan = %ld byte\n", total);
system("pause");
return 0;
}
Yukardaki program DOS iletim sistemi iin nce BLOCK_SIZE simgesel deimezinin
2048 deeri ile derleyerek altrn. Daha sonra program BLOCK_SIZE deimezinin
deerini 1 yaparak yeniden derleyin ve altrn. Toplam deerin ok daha kldn
greceksiniz.
327
malloc ilevi dinamik blok elde etme ilemini programn alma zamannda yerine getirir.
Dinamik bellek ilevleriyle elde edilen bloklarn kontrol edilebilmesi iin heap alannda
uygun bir veri yaps ile bir tablo tutulur. Tutulan tabloda elde edilmi her bir dinamik
bloun balang adresi ve boyutu vardr. Bu tablonun kendisi de heap alanndadr. Bir
program altrldnda heap alan iki ayr biimde tketilir:
i) malloc/calloc/ realloc ilevleri tarafndan elde edilen bloklarn kullanma sunulmasyla
ii) her bir yer ayrma ilemi iin ilgili tabloya bir bilginin daha eklenmesi ile.
Dinamik bellek ilevlerine yaplan ardan elde edilen bir blok standart ilevlerinden free
ilevine yaplan ar ile sisteme geri verilebilir. free ilevinin bildirimi de stdlib.h balk
dosyas iindedir:
void free(void *block);
free ilevine daha nce malloc, calloc ya da realloc ilevleriyle elde edilmi olan bir bellek
blounun balang adresi geilmelidir. free ilevi arsyla bu blok heap alanna geri
verilmi olur. heap alanna geri verilen blok malloc, calloc ya da realloc ilevleri
tarafndan yeniden elde edilme potansiyeline girer.
Grld gibi free ilevi geri verilecek bellek blounun yalnzca balang adresini alr,
daha nce ayrlm bellek blounun boyutunu istemez. alma zamannda heap alannda
tutulan tablolarda, zaten elde edilmi bellek bloklarnn boyutu deeri tutulmaktadr.
free ilevine argman olarak daha nce dinamik bellek ilevleriyle elde edilmi bir bellek
blounun balang adresi yerine baka bir adres gnderilmesi tanmsz davrantr,
yaplmamaldr.
#include <stdlib.h>
int main()
{
char *p1, *ptr;
char s[100];
ptr = (char *)malloc(20);
free(ptr);
free(p1);
free(s);
}
/* Geerli ve doru*/
/* Yanl */
/* Yanl */
return 0;
Yukardaki rnekte
free(p1)
ars bir alma zaman hatasna neden olur. nk p1 adresinde dinamik bellek
ilevleri ile elde edilmi bir alan yoktur.
free(s)
ars da bir alma zaman hatasna neden olur. nk s dizisi iin yer, dinamik bellek
alanndan ayrlmamtr.
Daha nce dinamik olarak elde edilmi bir blok free ilevi ile heap alanna geri verilir,
ama bu bloun balang adresini tutan gsterici herhangi bir ekilde deitirilmez.
Bloun balang adresini tutan, free ilevine argman olarak gnderilen gsterici, free
ilevine yaplan ardan sonra artk gvenli olmayan bir adresi gsterir. Geri verilen bir
328
bellek blouna ulamak sk yaplan bir gsterici hatasdr. Aadaki kodu derleyerek
altrn:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define
ARRAY_SIZE
100
int main()
{
char name_entry[ARRAY_SIZE];
char *pd;
printf("bir isim giriniz: ");
gets(name_entry);
pd = (char *)malloc(strlen(name_entry + 1));
if (pd == NULL) {
printf("dinamik bir blok elde edilemiyor!\n");
exit(EXIT_FAILURE);
}
strcpy(pd, name_entry);
printf("yazi = %s\n", pd);
free(pd);
printf("yazi = %s\n", pd);
}
/* Yanl! */
return 0;
Yukardaki main ilevi iinde nce klavyeden alnan bir isim name_entry isimli yerel diziye
yerletiriliyor. Daha sonra arlan malloc ilevi ile klavyeden alnan ismin uzunluundan
1 byte daha byk bir bellek blou elde ediliyor. Elde edilen bellek blouna yerel dizideki
isim kopyalanyor. Daha sonra dinamik bloktaki isim ekrana yazdrlyor. Yaplan
free(pd)
arsyla dinamik blok heap alanna geri veriliyor. Ancak geri verme ileminden sonra
yaplan printf arsyla geri verilen bloa ulalyor. Bu bir gsterici hatasdr.
Dinamik bellek ilevleri dinamik yer ayrma ve dinamik blou geri verme ilemlerini
programn alma zamannda gerekletirirler.
free ilevine ilikin tipik bir hata da daha nce elde edilen bir dinamik bloun, free
ileviyle kltlmeye allmasdr. Aadaki kodu inceleyelin:
#include <stdlib.h>
int main()
{
char *pd;
pd = (char *)malloc(1000);
/****/
free(pd + 500); /* Yanlis */
/****/
free(pd);
return 0;
}
329
Yukardaki main ilevinde nce malloc ilevi ile 1000 byte'lk bir blok elde edilerek bloun
balang adresi pd gsterici deikenine atanyor. Daha sonra arlan free ilevine pd +
500 adresinin argman olarak gnderildiini gryorsunuz. Byle bir ar ile free
ilevinin daha nce ayrlan 1000 byte'lk alann 500 byte'n geri vermesi sz konusu
deildir.
pd + 500 adresi bir dinamik alann balang adresi deildir. Bu yzden ilev ars
tanmsz davran zellii gsterir. Bir malloc ars ile elde edilen dinamik alan free
ilevine yaplan bir aryla kltlemez.
free ilevine dinamik bir bloun balang adresi dnda baka bir adresin geilmesi
tanmsz davrantr. Bu durumun tek istisnas free ilevine NULL adresinin geilmesidir.
leve NULL adresinin geilmesi tanmsz davrana neden olmaz, yalnzca bir ileme
neden olmayan (no operation) ardr:
if (ptr)
free(ptr);
ptr bir gsterici deiken olmak zere yukardaki if deyiminde ptr deikeninin deeri
NULL adresi deilse free ilevi arlyor.
Bu if deyimi yerine free ilevi dorudan arlsa da kodun anlamnda fazlaca bir deiiklik
olmazd.
malloc ilevi ile elde edilen bir bellek blou boyutu programn alma zamannda elde
edilen bir dizi olarak kullanlabilir. Aadaki program derleyerek altrn:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
void set_array(int *ptr, int size)
{
int k;
for (k = 0; k < size; ++k)
ptr[k] = rand() % 100;
}
void display_array(const int *ptr, int size)
{
int k;
for (k = 0; k < size; ++k)
printf("%2d ", ptr[k]);
printf("\n");
}
int main()
{
int n;
int *pd;
printf("kac tane tamsayi : ");
scanf("%d", &n);
pd = (int *)malloc(sizeof(int) * n);
if (pd == NULL) {
printf("cannot allocate memory!\n");
exit(EXIT_FAILURE);
}
srand(time(0));
330
set_array(pd, n);
display_array(pd, n);
free(pd);
return 0;
}
Yukardaki programda programn alma zamannda ka nesnenin istendii bilgisi
kullancdan standart scanf ileviyle n deikenine alnyor. Daha sonra malloc ileviyle n
tane int trden nesnenin saca kadar byklkte bir dinamik bellek blou elde ediliyor.
Elde edilen dinamik bellek blounun balang adresinin pd isimli gstericiye atandn
gryorsunuz. Artk balang adresi pd ve boyutu n olan bir dizi sz konusudur, deil
mi? set_array ve display_array ilevleri bu dizinin balang adresi ve boyutu ile
arlyor.
calloc levi
calloc ilevi malloc ilevine ok benzer. stdlib.h balk dosyas iindeki bildirimi aadaki
gibidir.
void *calloc(size_t nblock, size_t block_size);
calloc ilevi heap alanndan birinci parametresi ile ikinci parametresi arpm kadar
byte'lk bir bellek blounu elde etmek iin kullanlr. levin geri dn deeri, yer ayrma
ileminin baarl olmas durumunda, elde edilen bellek blounun balang adresidir. Yer
ayrma ilemi baarl olmazsa calloc ilevi de malloc ilevi gibi NULL adresine geri dner.
Elemanlar int trden olan 100 elemanl bir dizi iin dinamik yer ayrma ilem calloc ilevi
ile aadaki gibi yaplabilir:
void func()
{
int *pd;
/***/
pd = (int*) calloc(100, sizeof(int));
if (pd == NULL) {
printf("cannot allocate memory\n");
exit(EXIT_FAILURE);
}
/***/
}
phesiz yukardaki kod parasnda calloc ilevi u ekilde de arlabilirdi:
pd = (int*) calloc(sizeof(int), 100);
calloc ilevinin malloc ilevinden baka bir fark da elde ettii bellek blounu
sfrlamasdr. calloc ilevi tarafndan elde edilen blounun tm byte'larnda sfr deerleri
vardr. malloc ilevi calloc ilevine gre ok daha sk kullanlr. Ayrlan blok sfrlanacaksa
malloc ilevi yerine calloc ilevi tercih edilebilir.
Yeri heap alanndan elde edilen n elemanl int trden bir dizinin elemanlar sfrlanmak
istensin. Bu ilem malloc ilevi ile yaplrsa, dizinin elemanlar ayr bir dng deyimi ile
sfrlanmaldr:
int main()
{
int *pd;
int i;
int n = rand() % 100 + 10;
pd = (int *) malloc(sizeof(int) * n);
331
/***/
for (i = 0; i < n; ++i)
ptr[i] = 0;
/***/
Bellekte Sznt
Dinamik bellek ilevleriyle heap alanndan elde edilen bir blok free ilevi arsna kadar
tutulur. Dinamik bloun balang adresi kaybedilirse, artk programn sonuna kadar bu
blou kullanma ans olmad gibi blok heap alanna geri de verilemez. Bu durum
bellekte sznt (memory leak) olarak isimlendirilir. Bir ilev iinde dinamik bir bloun elde
edildiini ancak ilev iinde bu bloun heap alanna geri verilmediini dnn:
void process()
{
char *pd = (char *)malloc(1000);
if (pd == NULL) {
printf("dinamik bir blok elde edilemiyor!\n");
exit(EXIT_FAILURE);
}
/***/
}
process isimli ilev her arldnda 1000 byte'lk bir alan elde ediliyor. Ancak ilev iinde
elde edilen dinamik alan heap alanna geri verilmiyor. Bu durumda process ilevine
yaplan her ar ile heap alanndan 1000 byte'lk bir alan bloke edilmi olur. process
ilevin ana program iinde ok sk arlan bir ilev olduu dnlrse, belirli bir ar
saysndan sonra heap alan tmyle tketilebilir. Yani belirli bir sre sonra artk malloc
ilevi yeni bir blou elde edemeyecek duruma gelir.
realloc ilevi
realloc ilevi daha nce malloc ya da calloc ilevleriyle elde edilmi bir bellek blounu
bytmek ya da kltmek iin arlr. levin bildirimi stdlib.h balk dosyas iindedir.
void *realloc(void *block, size_t newsize);
realloc ilevine gnderilen birinci argman, daha nce elde edilen bir bellek blounun
balang adresi, ikinci argman ise yeni bloun toplam uzunluudur.
realloc ilevi ile dinamik bir blok bytlmek istendiinde, ilev, blou daha yksek
saysal adrese doru bytmek zorunda deildir. realloc ilevi, daha nce elde edilmi
bloun bittii yerde, istenilen uzunlukta bir blok bulamazsa ya da dinamik alan bu ynde
bytmek istemezse, bu kez bloun tamam iin bellekte kullanlmayan baka bir yer
aratrr. Eer istenilen toplam uzunlukta ardl bir blok bulunursa buray ayrr, eski
bellek bloundaki deerleri yeni yere tar ve eski bellek blounu sisteme geri verir.
stenen uzunlukta srekli bir alan bulunamazsa ilev NULL adresiyle geri dner.
Aadaki programda yaplan dinamik bellek ilemlerini inceleyin:
332
#include <stdio.h>
#include <stdlib.h>
int main()
{
int *pd;
int k;
int n, nplus;
printf("kac tamsay : ");
scanf("%d", &n);
pd = (int *) malloc(sizeof(int) * n);
if (!pd) {
printf("bellek yetersiz!..\n");
exit(EXIT_FAILURE);
}
for (k = 0; k < n; ++k)
pd[k] = k;
printf("kac tamsay eklenecek: ");
scanf("%d", &nplus);
pd = (int *) realloc(pd, sizeof(int) * (n + nplus));
if (pd == NULL) {
printf("bellek yetersiz!..\n");
exit(EXIT_FAILURE);
}
for (; k < n + nplus; ++k)
pd[k] = k;
for (k = 0; k < n + nplus; ++k)
printf("%d ", pd[k]);
printf("\n");
free(pd);
return 0;
}
malloc ilevi ile int trden n tane nesnenin saca kadar byklkte bir dinamik alan
elde ediliyor. malloc ilevi baarl olamazsa standart exit ileviyle program
sonlandrlyor.
Elde edilen dinamik alandaki n tane nesneye bir dng yardmyla atamalar yaplyor.
Daha sonra arlan realloc ileviyle elde edilen dinamik alan nplus tane nesneyi daha
iine alabilecek biimde geniletiliyor. Eer realloc ilevi baarsz olursa program
sonlandrlyor. Ardndan realloc ilevinin geri dn deeri olan adresindeki n + nplus
adet nesneye sahip dizi yine bir dng deyimiyle yazdrlyor.
realloc ilevi baarl olmusa iki olaslk sz konusudur:
1. realloc ilevi daha nce elde edilen dinamik alann hemen altnda yani daha yksek
saysal adreste, ilk blou bytecek biimde bo alan bularak ek bellek alann buradan
salamtr. Bu sistemlerin ounda pek karlalan bir durum deildir. Bu durumda
realloc ilevinin geri dn deeri olan adres ilevin birinci parametresine gnderilen
adrestir.
333
2. realloc ilevi daha nce ayrlan bloun altnda bo yer bulamam ya da bu ynde bir
bytme ilemini tercih etmemi olabilir. Ve heap alannda toplam
(n + nplus) * sizeof(int)
kadar byte'lk baka bir bo blok ayarlam olabilir. Bu durumda realloc ilevi daha nce
elde edilmi alandaki deerleri de bu alana kopyalayarak eski blou sisteme geri
vermitir.
realloc ilevinin bu davranndan dolay geri dn deeri bir gsterici deikene mutlaka
atanmaldr. Aada sk yaplan tipik bir hata grlyor:
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *p;
p = malloc(10);
if (p == NULL) {
printf("can not allocate memory!..\n");
exit(EXIT_FAILURE);
}
/***/
if (realloc (p, 20) == NULL) {
printf("can not allocate memory!..\n");
exit(EXIT_FAILURE);
}
gets(p);
}
return 0;
realloc ilevinin baarl olmas daha nce elde edilen bloun altna yani daha byk
saysal adrese doru ek blok salad anlamna gelmez. realloc ilevi daha nce elde
edilmi dinamik alan baka bir yere tayarak yeni bloun balang adresiyle geri
dnm olabilir. Bu durumda eski blok serbest braklr, artk bu adresin bir gvenilirlii
yoktur.
Yukardaki rnekte realloc ilevinin, daha nce elde edilen blou bulunduu yerde
bytt varsaylyor. Eer realloc ilevi bellek blounu baka bir yere tayarak
bytmse, yukardaki kodda bir gsterici hatas yaplm olur, nk artk p
gstericisinin gsterdii adres gvenilir bir adres deildir.
Bu yzden uygulamalarda genellikle realloc ilevinin geri dn deeri, realloc ilevine
deeri gnderilen gstericiye atanr:
ptr = realloc(ptr, 100);
Ancak bu bir zorunluluk deildir. Eer realloc ilevi ile yaplan yer ayrma ileminin
baarl olmamas durumunda program sonlandrlmayacaksa ve daha nce dinamik
olarak elde edilen blgedeki deerler bir dosyaya yazlacak ise, artk realloc ilevinin
baarsz olmas durumunda, ptr gstericisine NULL adresi atanaca iin ptr gstericisinin
daha nce elde edilen blok ile ilikisi kesilmi olur.
Byle bir durumda geici bir gsterici kullanmak uygun olur:
temp = realloc(ptr, 100);
if (temp == NULL)
printf("cannot allocate memory!..\n);
334
Bu durumda ptr gstericisi halen daha nce elde edilen bloun balang adresini
gsteriyor.
C standartlar realloc ilevi ile ilgili olarak aadaki kurallar getirmitir:
i) Daha nce elde edilen bir bellek blounu bytmesi durumunda, realloc ilevi bloa
eklenen ksma herhangi bir ekilde deer vermez. Yani eski bloa eklenen yeni blok
iinde p deerler (garbage values) bulunur.
realloc ilevi eer daha nce elde edilmi bellek blounu bytemez ise NULL adresi ile
geri dner ancak daha nce elde edilmi olan ve bytlemeyen bellek bloundaki
deerler korunur.
ii) Eer realloc ilevine gnderilen birinci argman NULL adresi olursa, realloc ilevi
tamamen malloc ilevi gibi davranr. Yani:
realloc(NULL, 100);
ile
malloc(100);
arlar tamamen ayn anlamdadr. Her ikisi de 100 byte'lk bir dinamik alan elde etmeye
alr.
Buna neden gerek grlmtr? Yani neden malloc(100) gibi bir ar yapmak yerine
realloc(NULL, 100) eklinde bir ar tercih edilebilir?
Aadaki program inceleyin:
#include
#include
#include
#include
<stdio.h>
<ctype.h>
<stdlib.h>
<conio.h>
335
ptr[counter - 1] = grade;
if (counter == 0) {
printf("hibir not girii yapmadnz!..\n");
return 0;
}
printf("toplam %d tane not girdiniz\n", counter);
printf("girdiiniz notlar aada listeleniyor :\n");
display_array(ptr, counter);
printf("\nen buyuk not : %d\n", get_max(ptr, counter));
printf("en kk not : %d\n", get_min(ptr, counter));
printf("notlarn ortalamas : %lf\n", get_ave(ptr, counter));
printf("notlarn standart sapmas . %lf\n", get_stddev(ptr, counter));
free(ptr);
return 0;
}
Yukardaki programda:
ptr = (int *) realloc(ptr, sizeof(int) * counter);
deyiminde, dngnn ilk turunda ptr gstericisinin deeri NULL olduu iin realloc ilevi
malloc gibi davranacak ve int trden 1 adet nesnelik yer ayrr. Ancak dngnn daha
sonraki turlarnda realloc ilevine gnderilen adres NULL adresi olmayacandan, daha
nce elde edilen blok dng iinde srekli olarak bytlm olur.
Eer realloc ilevine gnderilen ikinci argman 0 olursa, realloc ilevi tamamen free ilevi
gibi davranr:
realloc(ptr, 0);
ile
free(ptr);
tamamen ayn anlamdadr. Buna neden gerek grlmtr? Yani neden
free(ptr)
gibi bir ar yapmak yerine
realloc(ptr, 0)
eklinde bir tercih edilsin?
Dinamik olarak ayrlan bir blok, free ileviyle serbest braklarak sisteme geri verilene
kadar gvenli olarak kullanlabileceine gre, bir ilev byle bir bloun balang adresine
geri dnebilir.
Aadaki ilevi inceleyin:
336
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
char *getname()
{
char s[30];
char *ptr;
printf("ismi giriniz : ");
gets(s);
ptr = (char *) malloc(strlen(s) + 1);
if (ptr == NULL) {
printf("cannot allocate memory!..\n");
exit(EXIT_FAILURE);
}
return strcpy(ptr, s);
}
getname ilevinde nce klavyeden bir isim yerel bir diziye alnyor. Daha sonra strlen
ilevi kullanlarak yerel diziye alnan ismin uzunluu bulunuyor. malloc ilevi ile bu ismi ve
sonuna gelecek sonlandrc karakteri iine alacak byklkte bir alan dinamik olarak elde
ediliyor. Daha sonra da strcpy ilevi kullanlarak, isim dinamik olarak elde edilen alana
kopyalanyor ve bu bloun balang adresine geri dnlyor.
Dinamik bir yer ayrma ilemi yapan ilevin arlmas sonucunda ayrlan bloun geri
verilmesi, ilevi arann kodun sorumluluunda olur:
int main()
{
char *p =
/*****/
free(p)
/*****/
return 0;
}
getname();
Bir ilev iinde dinamik olarak bir bellek blou ayrmak ve daha sonra bu bloun
balang adresine geri dnmek C dilinde ok kullanlan bir tekniktir.
Aada kaynak kodu verilen strcon ilevi birinci parametresinde balang adresi tutulan
yaznn sonuna ikinci parametresinde balang adresi tutulan yazy kopyalyor; fakat her
iki adresteki yazlar da bozmadan, birletirilmi yaznn balang adresi olan bir adrese
geri dnyor:
#include <string.h>
#include <stdlib.h>
char *strcon(const char *s1, const char*s2)
{
char *ptr = malloc (strlen(s1) + strlen(s2) + 1);
/* Basari sinamasi */
strcpy(ptr, s1);
strcat(ptr, s2);
}
return ptr;
337
levimiz, adresleri gnderilen yazlar dinamik bir alana kopyalayarak, dinamik alann
balang adresiyle geri dnyor. strcpy ve strcat ilevleri, birinci paramatrelerinin
deerleriyle geri dndnden
strcpy(ptr, s1);
strcat(ptr, s2);
return ptr;
deyimleri tek bir deyim olarak yazlabilirdi, deil mi?
return strcat(strcpy(ptr, s1), s2);
Dinamik bellek ilevleriyle elde edilen farkl bloklarn balang adreslerinin bir gsterici
dizisinin elemanlarnda tutulmas ok karlalan bir temadr. Aadaki program
inceleyin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *get_name()
{
char name_entry[40];
char *pd;
printf("ismi giriniz : ");
gets(name_entry);
pd = (char *) malloc(strlen(name_entry) + 1);
if (pd == NULL) {
printf("bellek yetersiz!..\n");
exit(EXIT_FAILURE);
}
return strcpy(pd, name_entry);
}
int main()
{
char *p[10];
int k;
int len_sum = 0;
for (k = 0; k < 10; ++k)
p[k] = get_name();
printf("isimler : \n");
for (k = 0; k < 10; ++k) {
printf("(%s) ", p[k]);
len_sum += strlen(p[k]);
}
printf("\n");
printf("girilen isimlerin ortalama uzunlugu = %lf\n", (double)len_sum /
10);
for (k = 0; k < 10; ++k)
free(p[k]);
return 0;
}
338
return 0;
imdi program inceleyin. Matrisin satr ve stun saysnn kullanc tarafndan klavyeden
girilmesi salanyor. Matrisin satr says nrows stun says ncols'dur. Matris nrows tane
ncols uzunluunda int trden dinamik diziden oluur. Yani matrisin her bir satr bir
dinamik dizidir. Bu dinamik dizilerin balang adresleri yine dinamik bir gsterici
dizisinde tutulur. nce nrows tane int * trnden nesne iin dinamik bir alann ayrldn
gryorsunuz:
prows = (int **)malloc(sizeof(int *) * no_of_rows);
339
Aadaki dng ile bu dinamik dizinin her bir elemannn int trden ncols uzunluunda
dinamik bir dizinin balang adresini tutulmas salanyor.
for (k = 0; k < no_of_rows; ++k) {
prows[k] = (int *) malloc(sizeof(int) * no_of_cols);
Yer ayrma ilemleri tamamlandktan sonra
prows[i][k]
ifadesiyle matrisin i ve k indisli elemanna ulalabilir. nk prows[i] (int *) trnden bir
nesnedir. Bu nesnenin deeri dinamik bir dizinin balang adresidir. Keli ayra ilecinin
ile ncelik tablosunun birinci dzeyinde olduunu, bu ncelik dzeyinin soldan saa
ncelik ynne sahip olduunu hatrlayn. Bu durumda nce daha soldaki keli ayra
ileci bir deer retir. Soldaki keli ayra ilecinin rettii (int *) trnden deer bu kez
ikinci keli ayra ilecine terim olur. Bu ile de satrlardan herhangi birini oluturan int
trden dinamik dizinin bir elemanna ulatrr. Bu durumda ulalan int trden nesne,
matrisin [i][k] indisli eleman olur.
Ayrlan dinamik alanlarn geri verilmesinde dikkatli olunmaldr. nce matrisin satrlarn
oluturan dinamik diziler heap alanna geri verilmeli, daha sonra ise dinamik dizilerin
balang adreslerini tutan gsterici dizisinin geri verilme ilemi gerekletirilmelidir:
for (i = 0; i < no_of_rows; ++i)
free(prows[i]);
free(prows);
340
BELRLEYCLER ve NTELEYCLER
Belirleyiciler (storage class specifiers), bildirimler yaplrken kullanlan ve nesnelerin
zellikleri hakknda derleyicilere bilgi veren anahtar szcklerdir.
C'de kullanlan belirleyiciler unlardr:
auto, register, static, extern ve typedef.
Tr niteleyicileri ise nesnelerin iindeki deerlerin deitirilip deitirilmeyeceine ilikin
bilgi verir.
Tr belirleyicileri unlardr:
const, volatile.
Yer belirleyici ve tr niteleyiciler C'nin anahtar szckleridir.
Yer belirleyici, tr belirleyici ya da tr ifade eden anahtar szcklerin dizilimi herhangi bir
biimde olabilir:
auto const unsigned long int
const auto unsigned long int
unsigned long int auto const
int const long auto unsigned
a;
a;
a;
a;
auto Belirleyicisi
auto yalnzca yerel deikenler iin kullanlabilecek bir yer belirleyicisidir. auto
belirleyicisinin global deikenlerin ya da ilevlerin parametre deikenlerininin
bildiriminde kullanlmas geersizdir.
Bu anahtar szck, nesnenin bilinirlik alan bittikten sonra kaybolacan, bellekte
kaplad yerin geerlilii kalmayacan gsterir. Yerel deikenler otomatik mrldr.
Yani bulunduklar bloa ilikin kod almaya balandnda yaratlr, sz konusu bloun
yrtlmesi bittikten sonra yok olurlar. te auto belirleyicisi bu durumu vurgulamak iin
kullanlr. Zaten bir yerel deiken, baka bir yer belirleyici anahtar szck kullanlmad
srece (default olarak) auto biiminde ele alnr. Bu durumda auto yer belirleyicisinin
kullanm gereksizdir:
{
}
auto int a;
float b;
341
/* Geersiz! */
/* Geersiz! */
register Belirleyicisi
register belirleyicisi, deikenin "bellekte deil de CPU yazmalarnn iinde" tutulmas
isteini derleyiciye ileten bir anahtar szcktr. Deikenlerin bellek yerine dorudan
yazmalarda tutulmas programn almasn hzlandrr.
Yazma (register) nedir? Yazmalar CPU (central processing unit) iinde bulunan tampon
bellek blgeleridir. CPU iindeki aritmetik ve mantksal ilemleri yapan birimin yazmalar
ve belleklerle ilikisi vardr. Genel olarak CPU tarafndan yaplan aritmetik ve mantksal
ilemlerin her iki terimi de bellee ilikin olamaz. rnein bellekte bulunan sayi1 ve sayi2
isimli iki deiken toplanarak elde edilen deer sayi3 isimli baka bir bellek blgesine
yazlmak istensin. Bu C'deki
sayi3 = sayi1 + sayi2;
ilemine karlk gelir. CPU bu ilemi ancak 3 admda gerekletirebilir:
1. adm : nce sayi1 bellekten CPU yazmalarndan birine ekilir.
MOV reg, sayi1
2. adm : Yazma ile sayi2 toplanr.
ADD reg, sayi2
3. adm: Toplam sayi3 ile belirtilen bellek alanna yazlr.
MOV sayi3, reg
Bellee yazma ve bellekten okuma ilemleri yazmalara yazma ve yazmalardan okuma
ilemlerine gre daha yavatr. nk bellee eriim iin bir makine zaman gerekir.
register belirleyicisi derleyiciye yalnzca bir istei iletir. Yani register belirleyicisi ile
bildirilen deikenin yazmata tutulacann bir gvencesi yoktur. Derleyiciler byle bir
istei yerine getirmeyebilir. CPU yazmalar hangi sistem sz konusu olursa olsun snrl
saydadr. Bu nedenle birka deikenden fazlas register belirleyicisi ile tanmlanm olsa
bile yazmalarda saklanmayabilir. C derleyicileri yazmalarda saklayamayacaklar
deikenler iin genel olarak hata veya uyar iletileri vermez. Yani derleyiciler,
tutabilecekleri yazma saysndan fazla register belirleyicisine sahip deikenlerle
karlatklarnda bunlara ilikin register belirleyicilerini dikkate almaz.
register belirleyicileri ancak yerel ya da parametre deikenleri ile kullanlabilir. Global
deikenler ile kullanlamazlar. rnekler:
register int g;
/* Geersiz! */
342
static Belirleyicisi
static belirleyicisi ancak yerel ya da global deikenlerin bildiriminde kullanlabilir. Bu
belirleyici, parametre deikenlerinin bildiriminde kullanlamaz.
static anahtar szcnn global ve yerel deikenlerin bildiriminde kullanlmas farkl
anlamlara gelir.
static yer belirleyicisi ile tanmlanm yerel deikenler ya da yerel diziler programn
almas boyunca bellekte kalr. Baka bir deyile, static anahtar szc yerel
deikenlerin mrn otomatik mrden statik mre ykseltir. Statik yerel deikenler
tpk global deikenler gibi programn almaya balamasyla yaratlr ve programn
yrtlmesi bitene kadar da bellekte tutulur.
Statik yerel deikenler programc tarafndan ilkdeer verildikten sonra kullanlr.
lkdeer verme ilemi programn almas srasnda deil, derleme zamannda derleyici
tarafndan yaplr. Derleyici bellekten yer ayrlmasna yol aacak makine kodunu
oluturur. Statik yerel deiken iin bellekte yer programn yklenmesi srasnda ayrlr.
Derleme zamannda gerekte bellekte yer ayrlmaz. Bellekte yer ayrma iini yapacak
makine kodu retilir. Statik yerel deikenler ilkdeerleriyle birlikte bellee yklenir.
Aadaki program derleyerek altrn:
#include<stdio.h>
void func1()
{
int x = 5;
printf("x = %d\n", x);
x++;
}
void func2()
{
static int y = 5;
printf("y = %d\n", y);
y++;
}
int main()
{
int k;
printf("func1 ilevi 5 kez arlyor!\n");
for (k = 0; k < 5; ++k)
func1();
printf("\nfunc2 ilevi 5 kez arlyor!\n");
for (k = 0; k < 5; ++k)
func2();
return 0;
}
343
Yukardaki program iinde tanmlanan func1 ilevi her arldnda x deikeni yaratlr
ve ilevin almas sonlandnda x deikeni bellekten boaltlr. Her ne kadar ilevin
sonlanmasndan nce x deikeninin deeri 1 artrlsa da, bunun bir sonraki arya hibir
etkisi olmaz. func1 ilevinin bir sonraki arsnda x deikeni yine 5 deeri ile balatlr.
Oysa func2 ilevi iin durum deiiktir. func2 ilevi iindeki y isimli yerel deiken blok
bilinirlik alanna ait fakat statik mrldr. y deikenine ilkdeer verme deyimi derleyici
tarafndan retilen kodun bir parasdr. y deikeni programn banda 5 deeri ile
balatlr ve programn sonuna kadar bellekteki yerini korur. lev ka kez arlrsa
arlsn, bellekteki yeri deimeyen ayn y deikeni kullanlr. Bu durumda ilevin ana
blounun sonunda y deikeninin deeri 1 artrld iin bir sonraki arda artk y
deikeninin deeri 6 olur. Programa ilikin ekran ktsn aada veriyoruz.
func1 ilevi 5 kez arlyor!
x = 5
x = 5
x = 5
x = 5
x = 5
func2 ilevi 5 kez arlyor!
y = 5
y = 6
y = 7
y = 8
y = 9
Bir adres deerine geri dnen ilevlerin, yerel deikenlerin adreslerine geri dnmemeleri
gerektiini biliyorsunuz. nk yerel deikenler otomatik mrldr ve ilevin almas
sonunda bellekten boaltlr. Ancak static anahtar szc ile tanmlanm yerel
deikenler, programn sonlanmasna kadar bellekteki yerlerini koruyacandan, ilevin
statik bir yerel deikenin adresine geri dnmesinde bir saknca yoktur:
#include <stdio.h>
char *get_name()
{
static char entry[100];
printf("bir isim giriniz : ");
gets(entry);
return entry;
}
Ancak statik bir yerel dizinin adresiyle geri dnmek, dinamik bir dizinin yani malloc
ileviyle yeri elde edilmi bir dizinin adresiyle geri dnmekten farkldr. lev her
arldnda ayn adresi dndrr. Aadaki main ilevini inceleyin:
344
#include <stdio.h>
int main()
{
char *p[10];
int k;
for (k = 0; k < 10; ++k)
p[k] = get_name();
/*...*/
for (k = 0; k < 10; ++k)
printf("isim = (%s)\n", p[k]);
}
return 0;
Yukardaki main ilevinde programc 10 elemanl bir gsterici dizisi tanmlanyor. Gsterici
dizisinin her bir elemanna get_name ilevinin arlarndan elde edilen geri dn
deerleri olan adresler atanyor. Ancak get_name ilevi her defasnda ayn statik yerel
dizinin adresiyle geri dnyor. Kullanc tarafndan yaplan isim girii her defasnda bir
nceki giriin zerine yazlr. main ilevinin sonunda gsterici dizisinin tm elemanlar
ayn yazy gsterir, deil mi?
Baz standart C ilevleri de statik yerel dizilerin adreslerine geri dner. rnein time.h
balk dosyas iinde bildirimi yaplan ctime ve asctime ilevleri statik bir dizi iinde
tutulan yaznn balang adresiyle geri dner.
void func()
{
static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
/**/
}
Yukardaki rnekte, func ilevi iinde alphabet dizisi static anahtar szcyle
tanmland iin, bir kez ilkdeer verildikten sonra her arldnda bu deerleri korur ve
varln programn sonuna kadar srdrr. Oysa static anahtar szc kullanlmasayd,
bu dizi func ilevine yaplan her arda yeniden yaratlacak ve dizinin elemanlarna
yaplan ilkdeer atamalar her defasnda yeniden yaplacakt. Bu da ileve yaplan her
ar iin ek bir makine zamannn harcanmasna neden olacakt.
<stdio.h>
<string.h>
<stdlib.h>
<time.h>
#define
LIMIT
20
345
int urand()
{
static char flags[LIMIT] = {0};
int ret_val;
if (!memchr(flags, 0, sizeof(flags)))
return -1;
while (flags[ret_val = rand() % LIMIT])
;
flags[ret_val]++;
}
return ret_val;
int main()
{
int k;
srand(time(0));
for (k = 0; k < LIMIT; ++k)
printf("%d ", urand());
printf("\n");
printf("%d\n", urand());
}
return 0;
urand ilevi iinde flags isimli statik bir yerel dizinin elemanlar 0 deeriyle balatlyor.
Dizinin her bir eleman ilgili deerin daha nce retilip retilmediini gsteren bir bayrak
olarak kullanlyor. rnein ilev arldnda 12 deerinin daha nce retilip retilmedii
flags dizisinin 12 indisli elemannn deerinden anlalyor. Bu elemann deeri 0 ise 12
deeri retilmemi, 1 ise 12 deeri retilmi demektir.
lev nce standart memchr ileviyle dizinin iinde 0 deerinin olup olmadna bakyor.
Dizinin hibir elemannn deeri 0 deilse, tm deerler retilmi demektir. lev -1
deerine geri dnerek bu durum hakknda bilgi iletiyor.
Bayrak dizisinde en az bir 0 deeri var ise, bir dng iinde srekli rastgele deer
retiliyor. retilen deer, ismi ret_val olan bir deikene atanyor. Bayrak dizisinin
ret_val indisli elemannn deeri 1 olduka dng devam ediyor. Dngden klmas iin
daha nce retilmemi bir deerin retilmesi gerekiyor. Dngden kldktan sonra
ret_val deikeninin deer, geri dndrlecek deerdir. Ancak bu deer geri
dndrlmeden nce bayrak dizisinin ret_val indisli elemannn deeri 1 yaplyor.
Bylece bir sonraki arda artk bu deeri yeniden retilmesi engelleniyor.
Statik yerel deikenler mr asndan global deikenler ile ayn zellikleri gsterirler.
Yani programn almaya balamas ile yaratlrlar ve programn almas bitince
mrleri sona erer. Ama bilinirlik alan (scope) asndan farkllk gsterirler. Global
deikenler dosya bilinirlik alanna uyarken statik yerel deikenler blok bilinirlik alanna
uyar. Statik yerel deikenlerin blok bilinirlik alanna sahip olmas verilerin gizlenmesini
(data hiding) kolaylatrr ve kodun yeniden kullanlabilirliini (reusability) artrr. nk
global deikenlere bal olan ilevler bir projeden bir baka projeye kolayca
tanamazlar.
Bir proje birbirinden bamsz olarak derlenebilen birden fazla kaynak dosyadan
oluabilir. Projelerin bamsz olarak derlenebilen her bir kaynak dosyasna "modl" denir.
346
Bir projeyi oluturan farkl kaynak dosyalar birbirinden bamsz olarak derlendikten
sonra, hepsi birlikte balayc (linker) ile birletirilerek tek bir altrlabilir dosyay
olutururlar.
Eer btn modller tek bir kaynak kod iinde birletirilirse en ufak bir deiiklikte tm
proje tekrar derlenmek zorundadr. Oysa modllere ayrlm projelerde, yalnz deiikliin
yapld modln derlenmesi yeterlidir. nk dier modller zaten derlenmitir ve onlar
yalnzca balama aamasnda ilem grrler. Programlar modller halinde yazmann bir
dier avantaj da grup almas yaparken ortaya kar. Bu durumda projelerin bamsz
paralar (modlleri) ayr kiiler tarafndan hazrlanabilir.
Dier taraftan modller de ilevler gibi yeniden kullanlabilen yaplardr. Bir modl birden
fazla projede kullanlabilir.
Balant Kavram
Projeyi oluturan kaynak dosyalarn birinde tanmlanan bir isme, projeyi oluturan baka
bir kaynak dosya iinde ulalabilir mi? Bu kaynak dosyalar en sonunda tek bir alr
dosyay oluturacaklarna gre, baz durumlarda bir modlde tanmlanan bir nesneye
baka bir modlde ulamak istenmesi son derece doaldr.
Bir modlde tanmlanm bir deikenin baka bir modlde kullanlp kullanlmayacan
gsteren zellie o nesnenin balant zellii (linkage) denir. Balant zellii bilinirlik
alanndan farkldr. Bilinirlik alan derleyiciyi ilgilendiren yani derleme zamanna ilikin bir
kavramdr. Bir nesne bilinirlik alan dnda kullanlrsa derleme zamannda hata oluur.
Ancak balant, balayc program ilgilendiren yani balama zamanna ilikin bir
kavramdr. Bir nesne, balantsnn olmad bir modlde kullanlrsa balama zamannda
hata oluur.
C standartlarna gre nesneler balant zellikleri asndan 3 ana gruba ayrlr.
1. D balantya sahip nesneler (external linkage)
Eer bir nesne tanmlandktan sonra, bu nesneye hem kendi modlnn iinde her yerde
hem de dier modllerde ulam mmknse "nesnenin d balants vardr" denir.
2. balantya sahip nesneler (internal linkage)
Eer bir nesne tanmlandktan sonra bu nesneye kendi modlnn iinde her yerde
ulalabiliyorsa, ancak projeyi oluturan dier modllerde bu nesneye ulalamyorsa,
"nesnenin i balants vardr" denir.
3. Balantsz Nesneler (no linkage)
Kendi modlnde ancak belirli bir blok iinde bilinen nesnelerdir.
Bir ilevin parametre deikenleri ve bir ilev iinde tanmlanan yerel nesneler balantsz
nesnelerdir. Bu nesnelere projeyi oluturan dier modllerde ulamak mmkn deildir.
Global deikenler normal olarak d balantya sahiptir. Yani bir global deikene baka
bir modl iinde ulalabilir. Peki projeye ait bir kaynak dosya iinde global bir deiken
tanmland zaman baka bir kaynak dosya iinde bu deiken nasl kullanlabilir?
347
extern Belirleyicisi
extern belirleyicisi ile bir bildirim yaplr. Bir deikenin extern belirleyicisi ile bildirilmesi
derleyiciye nesnenin baka bir modlde tanmlandn anlatr.
Bir proje mod1.c ve mod2.c isimli iki kaynak dosyadan olumu olsun :
MOD1.C
int a;
float func1()
{
/***/
a = 100;
/***/
}
MOD2.C
int func2()
{
...
a = 300;
/* HATA */
}
348
rnein yukarda verilen rnekte mod2c modlnde bulunan y1 ilevi iinde bulunan x1
ilevi arlyor olsun:
/****mod1.c
int a;
*****/
float x1()
{
a = 100;
}
mod2.c
extern int a;
extern float x1(void);
int y1()
{
float f;
f = x1();
a = 300;
/***/
}
mod2.c modlnde x1 ilevi iin yazlm olan prototip ifadesini inceleyin:
extern float x1(void);
Bu rnekte x1 ilevi baka bir modlde tanml olduu iin bildiriminde extern anahtar
szc kullanlyor . Ancak extern belirteci eklenmeseydi derleyici zaten extern
varsayacakt.
Bir global deiken hibir modlde tanmlanmadan, btn modllerde extern olarak
bildirilirse, tm modller hatasz bir ekilde derlenebilir. Hata balama aamasnda,
balaycnn extern olarak bildirilen nesneyi hibir modlde bulamamas biiminde ortaya
kar.
extern belirleyicisinin tek bir modl sz konusu olduunda "ama d" bir kullanm
vardr. Aadaki rnekte main ilevi iindeki global x deikeni, tanmlamadan nce
kullanldndan hataya neden olur.
int main()
{
/***/
x = 100;
/***/
}
int x;
/* x global bir deiken ama tanmndan daha nce kullanlm */
int func()
{
x = 200;
/***/
}
349
extern int x;
int main()
{
/**/
x = 100;
/**/
}
int x;
int func()
{
x = 200;
/***/
}
extern bildirimini bu ekilde kullanmak yerine, global deikeni programn en banda
tanmlamak daha iyi bir tekniktir.
sim Kirlenmesi
Bir projeyi oluturan dosyalar iinde d balantya sahip iki varln ismi ayn olamaz.
Eer iki global varlk ayn ismi paylayorsa balama aamasnda hata oluur.
Bu da zellikle byk projeler sz konusu olduunda tehlikeli bir durumdur. Zira byk
projelerin ounda hem ok sayda programc kod yazar, hem de baka firmalar
tarafndan yazlm dosyalar da projede kullanlr. Bu durumda farkl kaynak dosyalar
global varlklara ayn isimler verilemez. Ayn isimlerin seilmesi durumunda balama
zamannda hata oluur.
Bir global deiken yalnzca bir modl ilgilendiriyorsa, bu modln hizmet verdii
modlleri ilgilendirmiyorsa, programn alma zamannda dier modller tarafndan
yanllkla deitirilebilir. Baka modldeki global deikenlerle isim akmasna yol
aabilir yani isim kirlenmesine neden olabilir. Doal olan byle bir global deikenin i
balantya sahip olmasdr. Byle global deikenler static anahtar szc ile
tanmlanmaldr.
Modllerin Oluturulmas
Modl darya baz hizmetler verecek kodlarn oluturduu birimdir. C dilinde geleneksel
olarak bir modle ilikin iki ayr dosya oluturulur. Bu dosyalardan biri modln balk
dosyasdr. Bir balk dosyas bir modln arayzdr (interface). Bir modln arayz, o
modl kullanacak dosyalarn derlenmesi srasnda, derleyicinin bilmesi gereken bilgileri
salayan bildirimleri ierir. Modller daryla olan ilikilerini arayzleri ile kurar. Bir
modle ilikin dary ilgilendiren bildirimler geleneksel olarak uzants ".h" olarak
350
levler da global varlklardr. Bir modldeki baz ilevler darya hizmet vermek iin
tanmlanr. Bu ilevler modln arayznde yani balk dosyasnda bildirilmelidir. Ancak
modlde yer alan modln kendi i ileyiinde grev yapan ilevler, darya dorudan
hizmet vermez. Bu ilevler, kodlama dosyasnda static anahtar szc ile bildirilir ve
tanmlanrl. Bu durumda static anahtar szc, ilevin geri dn deerinin trnden
nce yazlr:
Tr Niteleyicileri
C de const ve volatile olmak zere iki tr niteleyicisi vardr.
[C99 standartlaryla restrict anahtar szcyle yeni bir tr niteleyicisi eklenmitir.]
const Belirleyicisi
int main()
{
const int i = 10;
i = 100;
/* Geersiz */
return 0;
}
Yerel bir deiken const belirleyicisi ile tanmlanacaksa ilkdeer verilmelidir, ilkdeer
verilmezse const belirleyicisi kullanmann bir anlam kalmaz. Aadaki rnei inceleyin:
void func ()
{
const int a;
/**/
}
/* anlamsz */
Bu rnekte a yerel bir deiken olduundan bir p deere sahiptir. Deikenin deeri bir
daha deitirilemeyeceine gre byle bir tanmlamann da bir anlam olamaz.
351
const belirleyicisinin kullanm amac nedir? Kullanld yere gre const belirleyicisi
aadaki faydalar salayabilir:
1. Programn okunabilirliini artrr. Bu ynyle kodu okuyana yardmc olur.
Program okuyanlar const anahtar szcn grdklerinde nesnenin deerinin
deitirilmeyecei bilgisini alrlar.
2. Yanllkla nesnenin deerinin deitirilmesi engellenir. Bu ynyle kodu yazana
yardmc olur. const bir nesnenin deeri atama yoluyla deitirlmesi durumunda derleme
zamannda hata oluur. Bylece deeri deitirilmemesi gereken bir nesnenin deerinin
yanllkla atama yoluyla deitirilmesi de engellenmi olur.
3. const anahtar szc ile tanmlanan nesneler, derleyici tarafndan salt okunan
bellekte saklanabilirler. Derleyici const nesnenin deerinin deitirilmeyeceini
bilmediinden, bu nesnelerin kullanld ifadeler iin ek eniyileme(optimizasyon)
ilemleri uygulayabilir. const anahtar szc bu ynyle derleyiciye yardmc olur.
SIZE
100
int main()
{
const int size = 100;
int legal_array[SIZE];
int illegal_array[size];
/***/
}
352
return 0;
Yukardaki main ilevinde tanmlanm, gsterdii yer const olan gstericiler iin yaplan
*iptr = 100;
*dptr = 1.23;
cptr[1] = 'x';
atamalar derleme zamannda hata oluumuna neden olur. Ancak gstericilerin
kendilerine yaplan
iptr = &x;
dptr = &d2;
cptr = "Selami";
atamalarnn tamamen kurallara uygun olduunu gryorsunuz.
const anahtar szcnn gstericilerle birlikte kullanlmasnda en sk grlen biim
budur ve zellikle ilevlerin parametre deikenleri olan gstericilerle bu biim kullanlr:
void func(const char *str)
{
/***/
}
Yukarda func isimli ilevinin parametre deikeni olan str gstericisi deerini ilevin
arsyla alr. lev ar ifadesindeki argman ilevin arlmasyla s gstericisine
353
kopyalanr. Ancak ilev iinde *s nesnesine ya da s[x] nesnesine bir atama yaplamaz.
Yani s gstericisinin gsterdii yerdeki nesne (ileve adresi gnderilen nesne)
deitirilemez. Yukardaki rnekte const anahtar szcnn kullanlmas hereyden nce
okunabilirlik ile ilgilidir. Yukardaki kodu okuyan bir C programcs func ilevinin dardan
adresi alnan nesnenin yalnzca deerinden faydalanlacan, yani bu nesnenin deerini
deitirmeyeceini anlar. Geleneksel olarak, const anahtar szcnn, parametre
deikeni olan gstericilerin tanmlanmasnda kullanlmamas okunabilirlik asndan bir
ileti olarak kabul edilir:
void func(char *s)
{
/***/
}
Yukardaki kodu okuyan bir C programcs func ilevinin (aslnda sentaks asndan bir
zorunluluk bulunmasa da) kendisine adresi gnderilen nesneyi deitireceini anlar. Kodu
okuyan kii yle dnr: Eer func ilevi adresi gnderilen nesneyi deitirmeyecek
olsayd, s gstericisi const anahtar szc ile tanmlanrd.
2. const anahtar szcnn '*' atomundan sonra kullanlmas:
Bu ekilde tanmlanm gstericilere kendisi const gsterici (const pointer) denir. Bu
gstericilere ilkdeer olarak verilen adres atama yoluyla deitirilemez. Yani gsterici
mr boyunca ayn nesneyi gsterir. Ancak gstericinin gsterdii nesneyi, gstericinin
ieriini alarak deitirmek mmkndr. const anahtar szcnn bu kullanm biiminde
gsterici tanmlanrken ilkdeer verilmelidir, yoksa const anahtar szcnn
kullanlmasnn bir anlam kalmaz. Aadaki rnei inceleyin:
int main()
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double d1 = 2.5, d2 = 3.5;
char str[] = "Necati";
int x = 20;
int *const iptr = a;
double *const dptr = &d1;
char *const cptr = str;
*iptr = 100;
*dptr = 1.23;
cptr[1] = 'x';
/*
iptr = &x;
dptr = &d2;
cptr = "Selami";
*/
}
Geersiz!
Geersiz!
Geersiz!
return 0;
3. const anahtar szcnn hem '*' atomundan nce hem de '*' atomundan sonra
kullanlmas:
Bu durumda hem kendisi const hem de gsterdii yer const olan bir gsterici tanmlanm
olur. Atama ileciyle ne gstericiye ne de gstericinin ierii alnarak elde edilen nesneye
atama yaplabilir. const szcnn bu biimde kullanlmas yine daha ok parametre
deikenlerinin tanmlanmasnda grlr ise de bu uygulamalarda seyrek olan bir
durumdur. Aaadaki rnei inceleyin:
354
int main()
{
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
double d1 = 2.5, d2 = 3.5;
char str[] = "Necati";
int x = 20;
const int *const iptr = a;
const double *const dptr = &d1;
const char *const cptr = str;
/*
*iptr = 100;
Geersiz!
*dptr = 1.23;
Geersiz!
cptr[1] = 'x';
Geersiz!
iptr = &x;
dptr = &d2;
cptr = "Selami";
}
Geersiz!
Geersiz!
Geersiz!
return 0;
Gstericiyi gsteren bir gstericinin const anahtar szc ile bildirilmesinde const
anahtar szcnn kullanlmas farkl olanaklar yaratr:
const char **ptr;
ptr nin gsterdii gstericinin gsterdii nesneye atama yaplamaz. const olan **ptr
nesnesidir. ptr nesnesine ve *ptr nesnelerine atama yaplabilir. (pointer to pointer to
const object)
char *const *ptr;
*ptr nin gsterdii gstericiye atama yaplamaz. const olan *ptr nesnesidir. ptr nesnesine
ve **ptr nesnelerine atama yaplabilir.
char ** const ptr;
ptr ye atama yaplamaz. const olan ptr nesnesidir. **ptr nesnesine ve *ptr nesnelerine
atama yaplabilir.
const char *const *ptr;
yalnzca ptr nesnesine atama yaplabilir.
char *const * const ptr;
yalnzca **ptr nesnesine atama yaplabilir.
const char ** const ptr;
yalnzca *ptr nesnesine atama yaplabilir.
Adrese geri dnen bir ilevin geri dn deeri const bir adres olabilir:
const char *get_name(void);
Yukarda bildirimi yaplan get_name ilevinin geri dn deeri const bir adrestir. Peki bu
ne anlama gelir? Bir ilevin geri dn deeri trnn aslnda, geri dn deerini iinde
tutacak geici nesnenin tr olduunu biliyorsunuz. get_name ilevinin geri dn
deerini tutacak geici nesnenin tr const char * trdr. Yani geici nesne gsterdii
yer const olan bir gstericidir. lev ars, ilevin geri dn deerini, yani geici
nesnenin deerini gsterdiine gre, byle bir ileve yaplan ar ierik ilecine ya da
355
keli ayra ilecine terim olduunda, ulalan nesneye atama yaplamaz. Aadaki
rnei inceleyin:
char *get_name();
const char *get_fname();
int main()
{
*get_name() = 'a';
get_name()[2] = 'x';
*get_fname() = 'a';
get_fname()[2] = 'x';
}
/* Geersiz
/* Geersiz
*/
*/
return 0;
Yukarda main ilevi iinde arlan get_name() ilevinin geri dndrd adresteki
nesneye 'a' deeri atanyor. Yine get_name()[2] ifadesi ile ilevin dndrd adresteki
nesneden iki sonraki nesneye ulalarak bu nesneye de 'x' deeri atanyor. Atamalar
geerlidir. Ancak geri dn deeri const char * trden olan get_fname ilevi iin benzer
atamalar geersizdir, derleme zamannda hata oluturur.
volatile Belirleyicisi
Derleyiciler eniyileme amacyla nesneleri geici olarak yazmalarda tutabilir.
Yazmalardaki bu eit geici barnmalar register belirleyicisi kullanlmasa da derleyiciler
tarafndan yaplabilir. rnein:
int kare (int a)
{
int b;
b = a * a;
return b;
}
Yukardaki ilevde b geici bir deikendir, dolaysyla derleyici b deikenini bellekte bir
yerde saklayacana, geici olarak yazmalarndan birinde saklasa da ilevsel bir farkllk
ortaya kmaz. Bu eit uygulamalarda derleyicinin deikenleri geici olarak yazmalarda
saklamas ilemleri hzlandrr. Aadaki kodu inceleyin:
int a;
int b;
/***/
a = b;
if (a == b) {
/***/
}
Bu rnekte doal olarak ilk admda b deikeni a deikenine aktarlmak zere
yazmalardan birine ekilir. Ancak derleyici if ayrac iindeki ifadede a == b
karlatrmasn yapmak iin bellekteki b yerine yazmataki b'yi kullanabilir. Verdiimiz
iki rnekte de derleyici bir takm eniyileme ilemleriyle program ilevi deimeyecek
biimde daha hzl alr hale getirmek istemitir. Ancak kimi uygulamalarda derleyicinin
bu biimde davranmas hatalara neden olabilir. kinci rnekte :
356
a = b;
/* Bir kesme gelerek b'yi deitirebilir!
if (a == b) {
/***/
}
*/
Bir donanm kesmesi (rnein 8h gibi) b'yi deitiriyorsa, bu durum if deyimi tarafndan
farkedilmeyebilir. te bu tr durumlarda deikenlerin eniyileme amacyla geici olarak
yazmalarda tutulmas arzu edilmeyen sonularn olumasna yol aabilir. volatile "
Deikenleri eniyileme amacyla yazmalarda bekletme, onlar bellekteki gerek
yerlerinde kullan!" anlamna gelen bir tr belirleyicisidir. Bu anlamyla volatile
belirleyicisini register belirleyicisi ile zt anlaml olarak dnebiliriz. Yukardaki rnekte b
deikenini volatile olarak bildirerek anlattmz gibi bir problemin kmas engellenebilir.
int a;
volatile int b;
volatile ok zel uygulamalarda kullanlabilen bir belirleyicidir.
Yerel, parametre ya da global deikenlerle birlikte kullanlabilen volatile belirleyicisi
ancak ok zel uygulamalarda nemli olabilir. Bu belirleyicinin bilinsizce kullanlmasnn
performans kt ynde etkileyebileceini unutmaynz.
357
YAPILAR
Yap nedir
Yaplar (structures) programcnn birden fazla nesne yaratmasna izin veren bir aratr.
Yaplarn kullanlmasyla bellekte birbirini izleyecek ekilde yer alan birden fazla nesne
yaratlabilir. Bellekte bitiik olarak yer alan nesnelerin dizi tanmlamalaryla da
yaratlabileceini biliyorsunuz. Ancak yaplarn dizilerden baz farkllklar vardr: Diziler
ayn trden nesneleri iinde tutabilirken, yaplar farkl trlerden nesneleri tutabilir.
Yaplarn kullanlmasnn ana nedeni budur. ou zaman, trleri farkl bir takm nesneler,
mantksal olarak bir btn oluturabilir. sim, ya, cinsiyet, departman, cret, renim
durumu gibi bilgileri farkl trden nesneler iinde saklanabilir. Bunlarn tamam bir
iyerinde alan bir kiiye ait bilgiler olabilir. Aralarnda mantksal iliki olan farkl trden
veriler yaplar iinde saklanabilir.
int trden 10 elemanl bir dizi tanmlandn dnelim:
int a[10];
Kullanlan sistemde int trnn 4 byte yer kapladn dnrsek bu dizi iin bellekte 40
byte'lk bir blok ayrlr deil mi? Programc bu dizinin elemanlarna keli ayra ileci ile
ulaarak, eleman olan nesneler zerinde dorudan ilemler yapabilir. Ancak dizinin
tamam bir nesne olarak kullanlamaz. Yani C dili bir dizinin tamamn bir nesne olarak
grmez. Dizi isimlerinin, C'de dizinin ilk eleman olan nesnenenin adreslerine
dntrldklerini hatrlayalm. Yaplarda durum biraz daha farkldr. Yap kullanlmasyla
da birden fazla nesne iin ardl bir blok ayrlr. Ancak bu kez bloun tamam da bir
nesne olarak kullanlabilir. Yapnn eleman olan nesneler zerinde ilemler
yapabileceimiz gibi, bu nesnelerin oluturduu bloun tamam zerinde de dorudan
baz ilemler yapabiliriz.
Yaplarn kullanlmasyla programc yeni bir tr yaratabilir. C'nin var olan doal veri
trlerinin yannda, mantksal bir anlam soyutlayan yeni trler yaratlabilir. rnein deeri
bir tarih bilgisi olan bir tr ya da deeri bir kompleks say olan bir tr oluturulabilir.
Bylece programlama ile bir zm oluturmak istediimiz problem dzlemi daha iyi
modellenebilir. Yaplarn iyi bir ekilde renilmesi, ileride nesne bazl(object based) ve
nesneye ynelimli (object oriented) programlama tekniklerinin iyi bir biimde
anlalabilmesine yardmc olur.
Yap Bildirimi
Yap ile programcnn yeni bir tr yaratmasna olanak verilir. Yaplarn kullanlabilmesi iin
yaplmas gereken ilk ilem bu yeni tr derleyiciye tantmaktr. Tantma ilemi yap
bildirimi ile olur. Yap bildirimini gren derleyici, bu yeni tr hakknda bilgi edinmi olur.
Bu bildirimle derleyiciye aadaki bilgiler verilir:
- trn ismi
- trn bellekte ne kadar yer kaplad
- elemanlarn isimleri
Derleyici bir yap bildirimini grdkten sonra, bu trden bir nesne tanmlandnda nesne
iin bellekte ne kadar yer ayracan rendii gibi, bu nesnenin programc tarafndan
kullanmna ilikin ifadelere ilikin derleme zamannda baz snamalar yapabilir.
Yap bildiriminin belirli bir sentaks vardr.
359
[yap etiketi] {
<eleman ismi>;
<eleman ismi>;
<eleman ismi>;
struct bir anahtar szcktr. Bildirimde mutlaka yer almas gerekir. struct anahtar
szcn bir yap etiketi (structure tag) izleyebilir. Yap etiketi C dilinin isimlendirme
kurallarna uygun olarak seilmi bir isimdir. Yap etiketinden sonra bir blok yer alr. Bu
blok bildirime ak bir blge belirler. Bu blok iinde yap elemanlarnn bildirimleri yaplr.
rnek verelim:
struct Date {
int day, month, year;
};
struct Point {
int x, y;
};
struct Complex {
double real;
double imag;
};
Yap etiketleri (structure tags) isimlendirme kurallarna uymaldr. Ancak programclarn
ou yap etiketlerinin ilk harflerini byk, dier harflerini kk seerler.
Yap bildiriminin yaplmas bellekte derleyici tarafndan bir yer ayrlmasna neden olmaz.
Yani bir tanmlama (definition) sz konusu deildir. Bu bildirimle programcnn yaratt
yeni bir veri tr hakknda derleyiciye bilgi verilir. Derleyici bu bilgiyi, bu trden
yaratlacak nesneler iin ablon olarak kullanr.
Yukardaki bildirimlerle yaratlm olan trlerin ismi srasyla, struct Sample, struct Date,
struct Point ve struct Complex'dir. Yani tr ismi struct anahtar szc ve yap etiketinin
birletirilmesiyle elde edilir. Yap etiketi tek bana bir tr bilgisi belirtmez. Ancak struct
anahtar szc olmadan da bir tr ismi yaratlmasn salayan aralar da ileride
greceimizi imdiden belirtelim.
Doal trlerden nesneler nasl tanmlanyorsa yap trnden bir deikenin tanmlanmas
da ayn szdizimle olur. Yani nce tr belirten szckler daha sonra da yap deikeninin
ismi yazlr:
360
361
#include <stdio.h>
struct Sample {
int i;
long l;
double d;
};
int main()
{
struct Sample sam;
printf("%d\n", sizeof(sam));
}
return 0;
Bir yap bildirimi sonlandrc atom ile sonlandrlmadan, yap bildiriminin kapanan kme
ayracndan hemen sonra bir deiken listesi yazlrsa yap bildirimi ile deiken
tanmlamas bir arada yaplm olur.
struct Date {
int day, month, year;
}bdate, edate;
Yukardaki deyim ile struct Date tr bildirilirken bu yap trnden bdate ve edate
deikenleri de tanmlanyor. Bu durumda yap deikenlerinin bilinirlik alanlar yap
bildiriminin yerletirildii yere bal olarak belirlenir. Yani sz konusu yap deikenleri
yerel ya da global olabilir.
Yap nesnelerinin hemen yap bildiriminden sonra tanmlanmas durumunda yap ismi
kullanma zorunluluu da bulunmaz. rnein yukardaki bildirim aadaki ekilde de
yaplabilir.
struct {
int day, month, year;
} bdate, edate;
Burada bdate ve edate yukarda bildirilen ama isimlendirilmeyen yap trnden
deikenlerdir. Bu yntemin dezavantaj artk ayn trden baka bir yap deikeninin
tanmlanmasnn mmkn olmaydr. rnein yukardaki kod parasndan sonra ayn
yap trnden bir yap nesnesi daha tanmlamak istediimizi dnelim. Bu tanmlama
struct {
int day, month, year;
}cdate;
biiminde yaplsa bile artk derleyici, bildirimleri edeer olan bu iki yap trn ayr yap
trleri olarak ele alr. Dolaysyla
cdate = bdate;
362
gibi bir atama yap trlerinin farkl olmas nedeniyle geerli deildir.
Byle bir tanmlamann baka bir dezavantaj da, bu yap tryle ilgili bir ilev
yazlamaydr. nk yap trnn bir ismi yoktur. Bir ilevin parametre deikeni tr
bilgisi olmadan tanmlanamaz.
Yaplarn dizilerden nemli bir fark da elemanlara eriim konusundadr. Dizi elemanlarna
eriim, dizi ismi ve keli ayra ileci [ ] (index operator - subscript operator) yoluyla
yaplrken, yap elemanlarna eriim iin nokta ileci kullanlr.
Nokta ilecine (dot operator) ilikin atom '.' dr. Nokta ileci, iki terimli araek
konumunda (binary infix) bir iletir. Bu ilecin sol terimi bir yap trnden nesne
olmaldr. lecin sa terimi ise nesnenin ait olduu ilgili yap trnnn elemanlarndan
biri olmak zorundadr. Nokta ileci ile ncelik tablosunun en yksek ncelikli dzeyinde
bulunur.
Bir yap nesnesi tanmlanarak, bu yap nesnesinin elemanlarna nokta ileci ile
eriildiinde artk bu elemanlarn her biri ayr bir nesne olarak ele alnr. Bu nesnelerin,
yap dnda tanmlanan nesnelerden herhangi bir fark yoktur. Aadaki kodu derleyerek
altrn:
#include <stdio.h>
struct Sample{
int i;
long l;
double d;
};
int main()
{
struct Sample sam;
sam.i = 10;
sam.l = 200000L;
sam.d = 1.25;
printf("sam.i = %d\nsam.l = %ld\nsam.d = %lf\n", sam.i, sam.l, sam.d);
return 0;
}
Yukardaki rnekte grld gibi sam.i, sam.l ve sam.d yap elemanlar ayr birer nesne
zellii gsterir. Bu elemanlara ayr ayr ulaabilir, ayr ayr atamalar yaplabilir. Bu
nesneler ++ ve -- ilelerinin ya da adres ilecinin terimi olabilir.
Yap elemanlar bellee, bildirimde ilk yazlan eleman kk saysal adreste olacak
biimde, bitiik olarak yerletirilir. Aadaki program derleyerek altrn:
363
struct Sample{
int i;
long l;
double d;
};
#include <stdio.h>
int main()
{
struct Sample sam;
printf("sam.i'nin adresi = %p\n", &sam.i);
printf("sam.l'nin adresi = %p\n", &sam.l);
printf("sam.d'nin adresi = %p\n", &sam.d);
}
return 0;
Yap nesnelerinin elemanlar deil de kendileri sz konusu olduunda, yani bir yap
nesnesi bir btn olarak ele alndnda ancak drt ilecin terimi olabilir: nokta ileci,
sizeof ileci, adres ileci, atama ileci. Bir yap nesnesinin bunlarn dnda bir ilecin
terimi olmas geersizdir.
Bir yap nesnesi sizeof ilecinin terimi olabilir. Bu durumda sizeof ilecinin rettii deer,
nesnenin ait olduu trn bellekte kaplad byte saysdr. Tabi sizeof ilecinin teriminin
bir tr ismi de olabileceini biliyorsunuz. Aadaki rnei inceleyin:
struct Point {
int x, y;
};
int main()
{
struct Point p;
printf("sizeof(struct Point) = %u\n", sizeof(struct Point));
printf("sizeof(p) = %u\n ", sizeof(p));
}
return 0;
Yukardaki main ilevini iinde yaplan printf arsnda sizeof ilecinin terimi bir tr ismi
(struct Point) iken, ikinci printf arsnda ise sizeof ilecinin terimi struct Point trnden
bir nesne olan p dir.
Bir yap nesnesi adres ilecinin terimi olabilir. Bu durumda adres ilecinin rettii deer
ilgili yap trnden bir adres bilgisidir:
struct Date isimli bir yapnn bildiriminin yaplmasndan sonra aadaki gibi bir nesne
tanmlandn dnelim:
364
struct Date {
int d, m, y;
};
struct Date mydate = {4, 3, 1964};
&mydate
ifadesi geerli bir ifadedir. Bu ifade (struct Date *) trndendir.
Peki &mydate ile &mydate.day ayn adresler midir? Bu adreslerin saysal bileenleri ayn
olmakla birlikte tr bileenleri farkldr. ki ifade, saysal bileenleri ayn olan farkl trden
iki adres bilgisidir.
&mydate ifadesinin tr (struct Date *) iken &mydate.day ifadesinin tr (int *) trdr.
365
int main()
{
struct Data d1, d2, d3, d4;
d1.i = 10;
d1.l = 200000L;
d1.d = 1.25;
d2 = d1;
d3.i = d2.i;
d3.l = d2.l;
d3.d = d2.d;
memcpy(&d4, &d3, sizeof(struct Data));
printf("d1.i
printf("d2.i
printf("d3.i
printf("d4.i
=
=
=
=
%d\nd1.l
%d\nd2.l
%d\nd3.l
%d\nd4.l
=
=
=
=
%ld\nd1.d =
%ld\nd2.d =
%ld\nd3.d =
%ld\nsam4.d
return 0;
}
Yukardaki main ilevinde struct Sample trnden d1, d2, d3, d4 isimli 4 deikenin
tanmlandn gryorsunuz. d2 deikenine atama ileci kullanlarak d1 deikeninin
deeri atanyor. d4 deikenine de standart memcpy ilevi kullanlarak d3 nesnesinden
kopyalama yaplyor.
Bir yap nesnesi tr dntrme ilecinin terimi olamaz. Yani aadaki gibi bir kod da
geersizdir:
struct S1 {
int a, b;
};
struct S2 {
int a, b;
};
void func()
{
struct S1 s1;
struct S2 s2;
s1.a = 10;
s1.b = 20;
s2 = (struct S2)s1;
/* Geersiz! */
Yap elemanlar bellee, bildirimde ilk yazlan eleman kk adreste olacak biimde,
bitiik olarak yerletirilir. Aadaki program derleyerek altrn:
366
#include <stdio.h>
struct Date {
int day, mon, year;
};
int main()
{
struct Date date;
printf("date.day'in adresi = %p\n", &date.day);
printf("date.mon'un adresi = %p\n", &date.mon);
printf("date.year'in adresi = %p\n", &date.year);
return 0;
}
Yapnn bir eleman herhangi trden bir dizi olabilir. Bu durumda da yapnn dizi
elemannn ismi bir nesne belirtmez, bir adres bilgisidir. nce aadaki rnei inceleyin:
#include <stdio.h>
#define
NAME_LEN
20
struct Person {
char name[NAME_LEN];
int no;
};
int main()
{
struct Person per;
gets(per.name);
puts(per.name);
putchar(per.name[3]);
per.name++
return 0;
}
Bildirilen struct Person yapsnn bir eleman char trden 20 elemanl bir dizidir. Bu
durumda kaynak kodumuz Unix iletim sisteminde derlendiinde, sizeof(struct Person)
24 olur. Tanmlanan her struct Person trnden nesnenin iinde 20 elemanl char trden
bir dizi yer alr.
main ilevi iinde kullanlan per.name ifadesi, per yap nesnesinin iinde yer alan char
trden dizinin balang adresidir. Bu ifade ileme sokulduunda, derleyici tarafndan
otomatik olarak char * trne dntrlr.
gets(per.name);
arsyla gets ilevine, per nesnesi iindeki char trden name dizisinin balang adresi
geiliyor. Bylece klavyeden alnan yaz nesnenin iindeki diziye yerletiriliyor.
puts(per.name);
arsyla da benzer biimde bu dizinin iindeki yaz ekrana yazdrlyor.
367
Ancak istenirse, nesnenin iindeki dizinin herhangi bir elemanna keli ayra ileci ya da
ierik ileci ile ulalabilir:
per.name[3]
*(per.name + 3)
ifadelerinin tr char trdr. Bu ifadeler per nesnesinin iindeki name dizisinin 3 indisli
eleman olan char trden nesneye karlk gelir.
struct Person trnden bir dizinin iinde tutulan yaznn uzunluu ne olursa olsun, struct
Person trnn sizeof deeri deimez. Baz durumlarda, yap nesneleri iinde yer alan
diziler, yap nesnesinin boyutunu gereksiz bir biimde artrd iin tercih edilmez.
Bir yapnn eleman neden bir dizi olur? Bu yolla bir yap nesnesi iinde ayn trden belirli
sayda deer tutulabilir. Bir renciye ilikin bilgilerin, bir yap tr ile modellendiini
dnn. Byle bir yapnn iinde, rencinin notlarn iinde tutacak int trden bir dizi
yer alabilir. Ancak uygulamalarda en ok grlen durum, yaz bilgilerinin yap iinde yer
alan char trden diziler iinde tutulmasdr.
Ayn trden belirli sayda deerin bir yap nesnesine bal olarak tutulmas iin, bir baka
yol da yapnn bir elemannn bir gsterici olmasdr.
Yapnn bir eleman herhangi trden bir gsterici olabilir. Aadaki rnei inceleyin:
#include <stdio.h>
struct Person {
char *name_ptr;
int no;
};
int main()
{
struct Person per;
per.name_ptr = "Necati Ergin";
per.no = 125;
printf("%s %d\n", per.name_ptr, per.no);
}
return 0;
Yukardaki kod parasnda bu kez struct Person trnn bir eleman char trden bir
gsterici olarak seiliyor. Bir gsterici kullanlan sistemde 2 ya da 4 byte yer
kaplayacana gre yukardaki struct Person yapsnn sizeof deeri UNIX altnda 8 byte
olur.
per.name_ptr = "Necati Ergin";
fadesi ile per nesnesinin name_ptr isimli elemanna "Necati Ergin" dizgesi atanyor.
Dizgelerin birer adres bilgisi olduunu, derleyici tarafndan char * trnden ifadeler
olarak deerlendirildiklerini biliyorsunuz.
per.name_ptr
char * trden nesne gsteren bir ifadedir. Bu ifade per nesnesi iinde yer alan char
trden gsterici deikene karlk gelir.
Bu gstericinin gsterdii yere yani
368
*per.name_ptr
per.name_ptr[n]
ifadelerine gsterici hatas oluturmadan atama yapabilmek iin, nce eleman olan
gstericinin gvenilir bir yeri gstermesi gerekir.
Bir yapnn elemannn bir gsterici olmas ou zaman, yap nesnesinin dinamik olarak
elde edilen bir bellek alann kullanmas amacyla istenir. Aadaki kod parasn
inceleyin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define
ARRAY_SIZE
100
struct Person {
char *name_ptr;
int no;
};
int main()
{
struct Person per;
char name_entry[ARRAY_SIZE];
printf("ismi giriniz : ");
gets(name_entry);
per.name_ptr = (char *)malloc(strlen(name_entry) + 1);
if (per.name_ptr == NULL) {
printf("bellek tahsis edilemiyor!\n");
exit(EXIT_FAILURE);
}
strcpy(per.name_ptr, name_entry);
printf("numarayi giriniz : ");
scanf("%d", &per.no);
printf("isim = %s\nNo : %d\n", per.name_ptr, per.no);
/*...*/
free(per.name_ptr);
}
return 0;
main ilevi iinde yaplanlar srasyla inceleyelim: nce struct Person trnden per isimli
bir deikenin tanmlandn gryorsunuz. Klavyeden girilen isim, nce standart gets
ileviyle yerel name_entry dizisine alnyor. Yerel diziye alnm ismin uzunluunun 1
fazlas kadar byte'lk bir alan malloc ileviyle dinamik olarak elde ediliyor. Yaz
uzunluunun 1 fazlas kadar yer ayrlmas yaznn sonunda yer alacak sonlandrc
karakter iin de yer salanmas amac tayor. Dinamik alann balang adresinin per
nesnesinin name_ptr elemannda saklandn gryorsunuz. Daha sonra standart strcpy
ileviyle yerel dizideki isim ayrlan dinamik bloa kopyalanyor.
per nesnesi bylece kendi eleman olan name_ptr gstericisi yoluyla dinamik bir alan
kontrol eder hale geliyor, deil mi? Bir struct Person nesnesinin tuttuu isme ulamak iin
nce nesnenin name_ptr elemanna eriip bu elemann deerinden de dinamik bloa
eriilebilir. Nesnenin mrnn sona ermesinden nce, bellek szntsn (memory leak)
engellemek amacyla, dinamik bloun geri verilmesi gerekir.
369
free(per.name_ptr);
arsyla geri verme ileminin yapldn gryorsunuz.
Dizilere ilkdeer vermeye ilikin zel bir szdizimi olduunu biliyorsunuz. Yap
deikenlerine de benzer bir szdizimle ilkdeer verilebilir. Verilen ilkdeerler sras ile
yap elemanlarna yerletirilir. Daha az sayda yap elemanna ilkdeer verilebilir. Bu
durumda ilkdeer verilmemi yap elemanlar otomatik olarak 0 deeri alr.
#include <stdio.h>
struct Date {
int day, month, year;
};
int main()
{
struct Date x = {10, 12, 1999};
struct Date y = {10};
printf("%d %d %d\n", x.day, x.month, x.year);
printf("%d %d %d\n", y.day, y.month, y.year);
}
return 0;
lkdeer verme szdiziminde, yap nesnesinin isminden sonra yer alan atama ilecini bir
blok izler. Bu bloun iinde virgllerle ayrlan bir liste ile yap nesnesinin elemanlarna
ilkdeer verilir. lk ifadenin deeri yap nesnesinin ilk elemanna, ikinci ifadenin deeri
yap nesnesinin ikinci elemanna atanr.
Dizilerde olduu gibi, yaplarda da bir yap nesnesinin elemanlarndan daha fazla sayda
elemana ilkdeer vermek geersizdir. Yapnn iinde yaz tutmak iin bildirilen char
trden bir dizi var ise bu diziye de ift trnak iinde ayrca ilkdeer verilebilir:
#include <stdio.h>
#define
MAX_NAME_LEN
20
struct Person {
char name[MAX_NAME_LEN + 1];
int no;
};
int main()
{
struct Person per = {"Mustafa", 256};
printf("isim : %s\nNo
}
return 0;
main ilevi iinde, struct Person trnden per deikenine ilkdeer veriliyor. Tanmlanan
per deikeninin name isimli char trden dizisine, ilkdeer verme szdizimiyle Mustafa
yazs yerletiriliyor. Yine per deikeninin no isimli elemanna da ilkdeer verme
szdizimiyle 256 deeri atanyor.
370
Yap eleman olan dizinin btn elemanlarna deil de, belirli sayda elemanna ilkdeer
vermek mmkndr. Aadaki rnei derleyerek altrn:
struct Sample {
int x;
int a[10];
double d;
};
#include <stdio.h>
int main()
{
struct Sample sam = {20, 1, 2, 3, 7.8};
int k;
printf("sam.x = %d\n", sam.x);
for (k = 0; k < 10; ++k)
printf("sam.a[%d] = %d\n", k, sam.a[k]);
printf("sam.d = %lf\n", sam.d);
}
return 0;
Bir yap nesnesinin adres ilecinin terimi olmasyla, yap nesnesinin adresi elde edilebilir.
Bir yap trnden gsterici deikenler de tanmlanabilir. Bir yap trnden gsterici
deikene, ayn yap trnden bir adres atanmaldr:
371
#include <stdio.h>
struct Point {
double x, y;
};
int main()
{
struct Point p;
struct Point *ptr = &p;
printf("sizeof ptr = %d\n", sizeof(ptr));
printf("sizeof *ptr = %d\n", sizeof(*ptr));
}
return 0;
main ilevi iinde struct Point trnden p isimli bir deiken tanmlanyor. Bu nesnenin
adresi struct Point trnden bir gsterici deiken olan ptr'ye atanyor. Bu atamadan
sonra artk p deikenine ptr gsterici deikeni ile de ulalabilir, deil mi?
Bir gsterici deiken ne trden bir nesneyi gsterirse gstersin, gstericinin sizeof
deeri 2 ya da 4 byte'dr.
sizeof(ptr)
ifadesi ile ptr gsterici deikeninin kendi sizeof deeri elde edilirken
sizeof(*ptr)
ifadesi ile ptr'nin gsterdii nesnenin yani p deikeninin sizeof deeri elde ediliyor.
ptr bir yap trnden bir gsterici ve mem de o yapnn bir eleman olmak zere ptr'nin
gsterdii nesnenin mem isimli elemanna aadaki gibi eriilebilir:
(*p).mem
erik ileci ile ncelik tablosunun ikinci dzeyindeyken, nokta ileci birinci ncelik
dzeyindedir. Burada *p ifadesinin ncelik ayrac iine alnmas zorunludur. Eer ifade
*p.mem
/* Geersiz */
biiminde yazlsayd, nce nokta ileci deer reteceinden, p.mem ifadesi ele
alnrd.Nokta ilecinin sol terimi bir yap nesnesi olmad iin, ifade geersiz olurdu.
Ok leci
Bir yap trnden adres sz konusu olduunda bu adresteki yap nesnesinin belirli bir
elemanna erimek iin ayr ile kullanlabilir: ncelik ileci, ierik ileci, nokta ileci.
Bu eriim, ismi ok ileci olan tek bir ilele yaplabilir.
Ok ileci - ve > karakterlerinin yanyana getirilmesiyle oluturulur. ki terimli araek
konumunda (Binary infix) bir iletir. Ok ileci ncelik tablosunun en yksek ncelik
seviyesindedir. -> ilecinin sol terimi bir yap trnden adres olmaldr. lecin sa terimi
ise ilgili yapnn bir eleman olmaldr. le, sol terimi olan adresteki yap nesnesinin, sa
terimi olan isimli elemanna erimek iin kullanlr.
ptr, bir yap trnden bir nesnenin adresini tutuyor olsun. Aadaki iki ifade edeerdir.
372
(*ptr).mem
ptr->mem
Nokta ve ok ilelerinin her ikisi de elemana erimek iin kullanlr. Ancak nokta ileci yap
deikeninin kendisiyle, ok ileci ise adresiyle eriim salar.
Bir ileve gnderilen argman olan ifade, ilevin ilgili parametre deikenine kopyalanr.
Bu durumda bir ilevin parametre deikeni bir yap trnden ise, ilev ayn yap
trnden bir nesne ile arlabilir. Ayn trden yap nesnelerinin atama ileciyle birbirine
atanabileceini biliyorsunuz. Byle bir atama blok kopyalamas anlamna geldii iin, hem
bellek hem de ilem zaman asndan greli bir kayba neden olur. stelik bu yntemle,
ilev kendisine gnderilen argmanlar deitiremez. nk ilev deerle arlmaktadr.
Aadaki rnei inceleyin:
#include <stdio.h>
#define
#define
MAX_NAME_LEN
MAX_FNAME_LEN
16
24
struct Person {
char name[MAX_NAME_LEN + 1];
char fname[MAX_FNAME_LEN + 1];
int no;
double wage;
};
void display_person(struct Person per)
{
printf("%d %s %s %.2lf\n", per.no, per.name, per.fname, per.wage);
}
int main()
{
struct Person person = {"Necati", "ERGIN", 2345, 3.56};
display_person(person);
return 0;
}
display_person ilevinin parametre deikeni struct Person trnden bir nesnedir. main
ilevi iinde struct Person trnden ismi person olan bir nesnenin ilkdeer verilerek
tanmlandn gryorsunuz. display_person ilevine yaplan arda argman olarak
person nesnesinin deeri kullanlyor. lev arld zaman, yaratlan ilevin parametre
deikeni olan per nesnesine, main blou iindeki person deikeninin deeri kopyalanr.
Bu bir blok kopyalama ilemidir. sizeof(struct Person) byklnde bir blok kopyalanr.
per nesnesi person nesnesi ile ayn nesne deildir. per nesnesi deeri person nesnesinin
deerine edeer baka bir nesnedir. display_person ilevi iinde
per.wage = 5.60;
gibi bir atama yaplsayd, phesiz bu atamadan person nesnesi etkilenmemi olurdu.
373
Yerel bir yap nesnesinin deeri byle bir ilevle deitirilemez, deil mi? levin bir yap
nesnesinin deerini almas ou zaman gereksiz bir blok kopyalanmasna neden olur.
lgili yap trnn sizeof deeri bydke, bellek ve ilemci zaman kullanm asndan
verimsizlik artacandan, pek tercih edilen bir yntem deildir. Kk yap nesneleri iin
kullanlabilir.
Bir ilevin parametre deikeni yap trnden bir gsterici olursa, ilev de bu trden bir
yap nesnesinin adresi ile arlabilir. Byle bir ileve yaplan ar ile, yalnzca bir
gsterici nesnesinin sizeof'u kadar veri ileve gnderilir.
Bu yntem ounlukla daha iyidir. Hemen her zaman bu yntem kullanlmaldr. Bu
yntemde yap ne kadar byk olursa olsun, aktarlan yalnzca bir adres bilgisidir. stelik
bu yntemde ilev kendisine adresi gnderilen yap deikenini deitirebilir. phesiz
byle bir aktarm ilemi, yap nesnesinin bellekte tek bir blok olarak yer almas yznden
mmkndr. Daha nce tanmlanan display_person isimli ilev, bu kez bir yap
nesnesinin adresini alacak biimde yazlyor:
void display_person(const struct Person *ptr)
{
printf("%d %s %s %.2lf\n", ptr->no, ptr->name, ptr->fname, ptr->wage);}
int main()
{
struct Person person = {"Necati", "ERGIN", 2345, 3.56};
display_person(&person);
}
return 0;
Bir ilevin geri dn deeri bir yap trnden olabilir. Aadaki rnei inceleyin:
struct Point {
double m_x, m_y;
};
struct Point make_point(double x, double y);
int main()
{
struct Point a;
374
a = make_point(3, 5);
/***/
return 0;
Nasl bir dizi iin bellek alan dinamik olarak elde edilebiliyorsa bir yap nesnesi iin de
dinamik bir bellek blou elde edilebilir. Aadaki rnei derleyerek altrn:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define
#define
MAX_NAME_LEN
MAX_FNAME_LEN
15
23
struct Person {
char name[MAX_NAME_LEN + 1];
char fname[MAX_FNAME_LEN + 1];
int no;
double wage;
};
void set_person(struct Person *ptr, const char *name_ptr, const char
*fname_ptr, int n, double w)
{
strcpy(ptr->name, name_ptr);
strcpy(ptr->fname, fname_ptr);
ptr->no = n;
ptr->wage = w;
}
void display_person(const struct Person *ptr)
{
printf("%d %s %s %.2lf\n", ptr->no, ptr->name, ptr->fname, ptr->wage);
375
}
int main()
{
struct Person *ptr;
ptr = (struct Person *)malloc(sizeof(struct Person));
if (ptr == NULL) {
printf("bellek tahsis edilemiyor!\n");
exit(EXIT_FAILURE);
}
set_person(ptr, "Kaan", "Aslan", 2345, 4.80);
display_person(ptr);
free(ptr);
return 0;
}
main ilevini inceleyin. malloc ileviyle struct Person trnden bir nesnenin sabilecei
byklkte bir bellek alan dinamik olarak elde ediliyor. Dinamik bloun balang adresi
struct Person trnden bir gsterici deiken olan ptr'de tutuluyor. set_person isimli
ilevin, dardan adresini ald yap nesnesini, dier parametrelerine aktarlan bilgilerle
doldurduunu gryorsunuz. Daha nce tanmlanan display_person isimli ilev ise, yine
adresini ald yap nesnesinin tuttuu bilgileri ekrana yazdryor. Bu ilevlere, yap
nesnesinin adresi olarak, elde edilen dinamik bloun balang adresinin geildiini
gryorsunuz.
Bir ilevin geri dn deeri, bir yap trnden adres de olabilir. Bu durumda ilevin geri
dn deerini iinde tutacak geici nesne bir yap trnden adrestir. lev ars, ilevin
geri dn deeri trnden bir gstericiye atanabilir. Aadaki ilev tanmn inceleyin:
struct Person *create_person(const char *name_ptr, const char
*fname_ptr,int n, double w)
{
struct Person *ptr;
ptr = (struct Person *)malloc(sizeof(struct Person));
if (ptr == NULL) {
printf("bellek elde edilemiyor!\n");
exit(EXIT_FAILURE);
}
set_person(ptr, name_ptr, fname_ptr, n, w);
}
return ptr;
create_person ilevi dinamik olarak yerini ayrd bir nesneyi parametrelerine aktarlan
bilgilerle dolduruyor. Daha sonra dinamik nesnenin adresiyle geri dnyor. Bu ilevi
kullanan bir kod paras aadaki gibi olabilir:
int main()
{
struct Person *ptr;
ptr = create_person("Kaan", "Aslan", 2345, 4.80);
display_person(ptr);
376
free(ptr);
/***/
return 0;
main ilevi iinde arlan create_person ilevinin geri dndrd adres, struct Person
trnden bir gsterici deiken olan ptr'de tutuluyor. lev, dinamik bir nesnenin adresini
dndrdnden, dinamik alan geri vermek, ilevi aran kod parasnn
sorumluluundadr.
Byle bir ilev yerel bir yap deikeninin adresine geri dnemez. Global bir yap
deikeninin adresine de geri dnmesinin ou zaman fazlaca bir anlam yoktur.
lev, dinamik olarak yeri ayrlm bir yap nesnesinin adresi ile de geri dnebilir.
Elde edilen dinamik alann "heap" bellek alanna geri verilmesi, ilevi aran kod
parasnn sorumluluundadr. main iinde free ilevine yaplan ar ile dinamik alann
geri verildiini gryorsunuz.
Bir ilevin yerel bir yap nesnesinin adresine geri dnmesi tipik bir gsterici hatasdr.
Yerel yap nesneleri de static anahtar szcyle bildirilebilir. phesiz bir ilevin static
bir yap nesnesinin adresine geri dnmesi bir gsterici hatas deildir.
377
s yap deikeninin struct Date trnden olan birth_date isimli elemanna nokta ileciyle
ulalabilir:
s.birth_date
Yukardaki ifade struct Date trnden bir nesne gsterir. stenirse bu nesnenin day, mon,
year isimli elemanlarna nokta ilecinin ikinci kez kullanlmasyla eriilebilir:
s.birth_date.mon
ifadesi int trden bir nesne gsterir. Nokta ilecinin ile ncelik tablosunun birinci ncelik
dzeyinde olduunu, bu ncelik dzeyine ilikin ncelik ynnn soldan saa olduunu
anmsayn.
Bir ya da birden fazla eleman programc tarafndan bildirilen bir trden olan yap
nesnelerine bileik nesne (composite object) denir. Yukardaki rnekte struct Student
trnden olan s deikeni bir bileik nesnedir.
Normal olarak ilkdeer vermede elemanlar srasyla, iteki yap da dikkate alnacak
biimde, yap elemanlarna atanr. Ancak iteki yapnn elemanlarna verilen deerlerin,
ayrca kme ayralar iine alnmas, okunabilirlii artrd iin salk verilir:
struct Student s = {"Necati Ergin", {10, 10, 1967},
123};
Eer iteki yap ayrca kme ayrac iine alnmsa iteki yapnn daha az sayda
elemanna ilkdeer vermek mmkn olabilir:
struct Student s = {"Necati Ergin", {10, 10}, 123};
Burada s deikeninin, birth_date elemannn year elemanna ilkdeer verilmiyor.
Derleyici bu elemana otomatik olarak 0 deeri yerletirir. Burada iteki kme ayralar
kullanlmasayd, 123 ilkdeeri year elemanna atanr, no elemanna ise 0 deeri verilirdi.
Aadaki gibi bir bildirim de geerlidir:
struct Data {
int a[3];
long b;
char c;
} x = {{1, 2, 3}, 50000L, 'A'}, *p;
Bir yapnn baka bir yap trnden elemana sahip olmas durumunda, ierilen elemann
ait olduu yap trnn arayznden, yani darya hizmet veren ilevlerinden
faydalanma olana doar.
Aadaki gibi bir yap bildirilmi olsun:
struct Person {
char name[16]
char fname[20]
struct Date bdate;
};
Yukardaki bildirimde struct Person yapsnn bir eleman struct Date trndendir.
struct Person per;
378
Birbirleri ile ilikili olan deikenler yap elemanlar olarak bir yap iinde toplanrsa algsal
kolaylk salanr. rnein dzlemde bir nokta, bir tarih bilgisi, bir depoda bulunan
mamllere ilikin zellikler bir yap ile temsil edilebilir.
C dilinde bir ilev en fazla 8 - 10 parametre almaldr. Daha fazla parametreye sahip
olmas kt bir tekniktir. Bir ilev ok fazla parametrik bilgiye gereksinim duyuyorsa, bu
parametrik bilgiler bir yap biiminde ifade edilmelidir. O yap trnden bir deiken
tanmlanmal, bu deikenin adresi ileve parametre olarak gnderilmelidir. rnein bir
kiinin nfus czdan bilgilerini parametre olarak alp bunlar ekrana yazdracak bir ilev
tasarlayacak olalm. Nfus czdan bilgilerinin hepsi bir yap biiminde ifade edilebilir ve
yalnzca bu yapnn adresi ileve gnderilebilir.
levlerin tek bir geri dn deeri vardr. Oysa ilevlerin ok deiik bilgileri aran
ileve iletmesi istenebilir. Bu ilem yle yaplabilir: letilecek bilgiler bir yap biiminde
ifade edilir. Sonra bu trden bir yap deikeni tanmlanarak adresi ileve gnderilir. lev
de bu yap deikeninin iini doldurur. Yani ilev knda bilgiler yap deikeninin iinde
olur.
Yap Dizileri
Yaplar da bir tr belirttiine gre yap trnden de diziler sz konusu olabilir. Bir yap
dizisi elemanlar bellekte bitiik olarak yerletirilen , elemanlar ayn yap trnden olan
bir dizidir.
Yap dizilerine de ilkdeer verilebilir. lkdeer verme srasnda kullanlan iteki kme
ayralar okunabilirlii artrr. rnek:
#include <stdio.h>
379
struct Date {
int d, m, y;
};
int main()
{
struct Date bdates[5] = {{10, 10, 1958}, {4, 3, 1964},
{21, 6, 1967}, {22, 8, 1956}, {11, 3, 1970}};
struct Date *pdate;
int i;
pdate = bdates;
for (i = 0; i < 5; ++i) {
printf("%02d / %02d / %04d\n", pdate->d, pdate->m, pdate->y);
++pdate;
}
return 0;
}
Bir yap dizisi zerinde ilem yapan ilev de tanmlanabilir. Byle bir ilev, kendisini
aran koddan, ilgili yap dizisinin balang adresi ile boyutunu almaldr. Aada, yap
dizileri ile ilgili ilemler yapan baz ilevler tanmlanyor. lev tanmlarn dikkatle
inceleyin:
380
#include
#include
#include
#include
#define
#define
#define
<stdio.h>
<string.h>
<stdlib.h>
<time.h>
NAME_LEN
FNAME_LEN
ARRAY_SIZE
16
20
200
struct Person {
char m_name[NAME_LEN];
char m_fname[FNAME_LEN];
int m_no;
double m_wage;
};
void swap_persons(struct Person *p1, struct Person *p2);
void set_person(struct Person *ptr, const char *name, const char *fname,
int no, double wage);
void set_person_random(struct Person *ptr);
void display_person(const struct Person *ptr);
void set_person(struct Person *ptr, const char *name, const char *fname,
int no, double wage);
void display_person_array(const struct Person *ptr, int size);
void sort_person_array(struct Person *ptr, int size);
char *name_array[20] = {"Ali", "Veli", "Hasan", "Necati", "Burcu", "Kaan",
"Selami", "Salah", "Nejla", "Huseyin", "Derya", "Funda", "Kemal", "Burak",
"Ozlem", "Deniz", "Nuri","Metin", "Guray", "Anil"};
char *fname_array[20] = {"Aslan", "Gencer", "Eker", "Ergin", "Serce",
"Kaynak", "Acar", "Aymir", "Erdin", "Doganoglu", "Avsar", "Ozturk",
"Yilmaz", "Tibet", "Arkin", "Cilasun", "Yildirim", "Demiroglu", "Torun",
"Polatkan"};
/*************************************************************************/
void swap_persons(struct Person *p1, struct Person *p2)
{
struct Person temp = *p1;
*p1 = *p2;
*p2 = temp;
}
/*************************************************************************/
void set_person(struct Person *ptr, const char *name, const char *fname,
int no, double wage)
{
ptr->m_no = no;
ptr->m_wage = wage;
strcpy(ptr->m_name, name);
strcpy(ptr->m_fname, fname);
}
/*************************************************************************/
void set_person_random(struct Person *ptr)
{
ptr->m_no = rand() % 5000;
ptr->m_wage = (double)rand() / RAND_MAX + rand() % 5 + 2;
strcpy(ptr->m_name, name_array[rand() % 100]);
strcpy(ptr->m_fname, fname_array[rand() % 50]);
}
/*************************************************************************/
void display_person(const struct Person *ptr)
{
printf("%-16s %-20s%-5d\t%4.2lf\n", ptr->m_name, ptr->m_fname, ptr-
381
>m_no, ptr->m_wage);
}
/*************************************************************************/
void set_person_array_random(struct Person *ptr, int size)
{
int k;
for (k = 0; k < size; ++k)
set_person_random(ptr + k);
}
/*************************************************************************/
void display_person_array(const struct Person *ptr, int size)
{
int k;
for (k = 0; k < size; ++k)
display_person(ptr + k);
}
/*************************************************************************/
void sort_person_array(struct Person *ptr, int size)
{
int i, k;
for (i = 0; i < size - 1; ++i)
for (k = 0; k < size - 1 - i; ++k)
if(strcmp(ptr[k].m_fname, ptr[k + 1].m_fname) > 0)
swap_persons(ptr + k, ptr + k + 1);
}
/*************************************************************************/
int main()
{
struct Person a[ARRAY_SIZE];
srand(time(0));
set_person_array_random(a, ARRAY_SIZE);
printf("siralanmadan once : \n");
display_person_array(a, ARRAY_SIZE);
sort_person_array(a, ARRAY_SIZE);
printf("siralanmadan sonra: \n");
display_person_array(a, ARRAY_SIZE);
return 0;
}
Aadaki gibi bir ilev tanmlanmak istensin:
void search_display(const struct Person *ptr, int size, const char *name);
lev balang adresini ve boyutunu ald dizi iinde name parametresine aktarlan isimli
kiilerin bilgilerini, soyadlarna gre artan srada ekrana yazdrsn. levin tanm ile
snama amacyla yazlan yeni main ilevini inceleyin:
382
void search_display(const struct Person *ptr, int size, const char *name)
{
int i,k;
struct Person **pd = NULL;
struct Person *temp;
int count = 0;
for (k = 0; k < size; ++k)
if (!strcmp(ptr[k].m_name, name)) {
pd = (struct Person **)realloc(pd, (count + 1) * sizeof(struct
Person *));
if (pd == NULL) {
printf("bellek blogu elde edilemiyor!\n");
exit(EXIT_FAILURE);
}
pd[count++] = (struct Person *)(ptr + k);
}
if (!count) {
printf("aranan isim dizide bulunamadi\n");
return;
}
for (i = 0; i < count - 1; ++i)
for (k = 0; k < count - 1 - i; ++k)
if (strcmp(pd[k]->m_fname, pd[k + 1]->m_fname) > 0) {
temp = pd[k];
pd[k] = pd[k + 1];
pd[k + 1] = temp;
}
for (k = 0; k < count; ++k)
display_person(pd[k]);
free(pd);
printf("toplam %d kisi bulundu!\n", count);
}
int main()
{
struct Person a[ARRAY_SIZE];
char name_entry[20];
srand(time(0));
set_person_array_random(a, ARRAY_SIZE);
display_person_array(a, ARRAY_SIZE);
printf("aranan ismi giriniz : ");
gets(name_entry);
search_display(a, ARRAY_SIZE, name_entry);
}
return 0;
Bir yapnn eleman baka bir yap trnden gsterici olabilir. rnein:
383
struct Date {
int day, month, year;
};
struct Person {
char name[30];
struct Date *bdate;
int no;
};
struct Person per;
eklinde bir tanmlama yaplm olsun:
per.bdate ifadesi struct Date * trndendir. Bu ifade bir nesne belirtir.
per.bdate->day ifadesinin tr int'dir. Bu ifade de nesne gsterir.
&per.bdate->day ifadesinin int * trndendir.
Tabi bu rnekte, bir deer atamas yaplmamsa, per.bdate ile belirtilen gsterici iinde
rastgele bir adres vardr. Bu gstericinin bir gsterici hatasna neden olmadan
kullanlabilmesi iin gvenilir bir adresi gstermesi gerekir. rnein bu alan malloc
ileviyle dinamik olarak elde edilebilir:
per.bdate = (struct Date *) malloc (sizeof(struct Date));
Yukardaki rnekte elimizde yalnzca struct Person trnden bir gsterici olduunu
dnelim.
struct Person *ptr;
1. ptr->bdate ifadesini struct Date * trndendir.
2. person->bdate->day ifadesinin tr int'dir.
Bu rneklerde henz hibir yer ayrma ilemi yaplmamtr. Hem ptr hem de ptr->date
gsterici deikenleri iin, dinamik bellek ilevleriyle yer elde edilmesi gerekir:
ptr = (struct Person *) malloc(sizeof(struct Person));
ptr->bdate = (struct Date *) malloc(sizeof(struct Date));
Burada dinamik olarak elde edilen alanlar ters srada serbest braklmaldr:
free(ptr->bdate);
free(ptr);
Bir yapnn eleman, kendi trnden bir yap deikeni olamaz. rnein:
struct Sample {
struct Sample a; /* GEERSZ */
};
nk burada struct Sample yapsnn uzunluu belirlenemez. Ancak bir yapnn eleman
kendi trnden bir gsterici olabilir. rnein:
struct Node {
int val;
struct Node *ptr;
};
384
Bal listenin ne olduunu anlayabilmek iin nce "dizi" veri yapsn hatrlayn.
Ayn trden nesnelerin bellekte tutulmak istendiini dnn. Bu nesneler bellekte
birbirini izleyecek biimde yani aralarnda hibir boluk olmayacak biimde tutulabilir. C
dilinin dizi aracnda da dizinin elemanlarnn bellekte bu ekilde tutulduunu biliyorsunuz.
Ancak nesneleri bellekte bitiik olarak yerletirmek her zaman istene bir durum deildir.
Bellekte nesnelerin bitiik yerletirilmesi drumunda, nesnelerden herhangi birine ulam
deimez zamanda yaplabilir. Yani dizinin ya da dinamik dizinin herhangi bir elemanna
ulama maliyeti, dizide tutuklan eleman sys ile doru orantl deildir. Dizide 100
eleman da olsa dizide 1000 eleman da olsa herhangi bir elemana ulam maliyeti
deimez bir zaman olur. Neden? Zira dizinin elemanna aslnda bir adres ilemiyle ulalr
deil mi? rnein pd bir dizinin balang adresini tutan bir gsterici ise
pd[n] gibi bir ilem
*(pd + n)
ilemine karlk gelir. Bu ilemin de maliyetinin deimez olduu aktr.
Bellekte elemanlar ardl olarak bulunmayan listelere bal liste denir. Bal listelerde
her eleman kendinden sonraki elemann nerede olduu bilgisini de tutar. lk elemann
yeri ise ayr bir gstericide tutulur. Bylece, bal listenin ilk elemannn adresi ile, bal
listenin tm elemanlarna ulalabilir. Bal liste dizisinin her eleman bir yap nesnesidir.
Bu yap nesnesinin baz yeleri bal liste elemanlarnn deerlerini veya tayacaklar
dier bilgileri tutarken, bir yesi ise kendinden sonraki bal liste eleman olan yap
nesnesinin adres bilgisini tutar. Bu ekilde elde edilen bir elemana "dm" ("node")
denir. rnek:
struct Node {
int val;
struct Node *next;
};
Amacmz int trden deerleri bellekte bir liste eklinde tutmak olsun. Yukarda struct
Node isimli bir yapnn bildirimini gryorsunuz. Tutulacak int trden deerler yapmzn
val isimli elemannn deeri olarak bellekte yer alacak. Yapnn yine struct Node trnden
olan gsterici eleman ise kendisinden bir sonra gelen yap nesnesinin adresini tutacak.
Bylece bir struct Node nesnesinin adresi elimizdeyken, bu nesnenin iinde tutulan int
trden veriye ulaabileceimiz gibi, nesnenin iinde yer alan next gstericisi yardmyla
da bir sonraki elemana ulaabiliriz.
Bir dizinin herhangi bir elemanna deimez bir zamanda eriilebilir. Zira bir elemana
ulama bir adres bilgisine bir tamsaynn toplanmasyla olur. Oysa bal listelerde bir
elemana eriebilmek iin, bal listede ondan nce yer alan btn elemanlar dolamak
gerekir. Bu durumda bir elemana ulamann ortalama maliyeti ortadaki elemana
ulamann maliyetidir. Bu da bal listedeki eleman saysnn artmasyla bir elemana
ulama maliyetinin dorusal biimde artaca anlamna gelir.
Dizilerde araya eleman ekleme ya da eleman silme ilemleri iin blok kaydrmas yapmak
gerekir. Oysa bal listelerde bu ilemler ok kolay yaplabilir.
385
386
unsigned int
Ancak typedef bildirimi derleyici tarafndan ele alnrken, #define nilemci komutu ile
tanmlanan isimler nilemci program ilgilendirir. Yani yukardaki nilemci komutunun
kullanlmasndan sonra, zaten derleyici UINT ismini grmez. Derleyiciye sra geldiinde,
UINT isminin yerini unsigned int atomlar alm olur.
Alglanmas zor olan tr isimlerine, typedef bildirimleriyle alglanmas daha kolay tr
isimleri verilebilir. typedef bildirimleri iin aada verilen basit kural kolaylk salar:
typedef anahtar szc, her tr bildirimin nne gelebilir. typedef anahtar szc bir
bildirimin nne geldiinde, typedef kullanlmam olsayd deiken ismi olacak isimler,
typedef anahtar szc eklendiinde artk ilgili trn ismi olur. rnein:
char *pstr;
biiminde bildirilen pstr char* trnden bir deikendir.
typedef char *pstr;
387
Yukardaki deyim ise bir typedef bildirimidir. Artk pstr derleyici tarafndan bir tr ismi
olarak ele alnr. pstr, char trden bir adres trnn baka bir ismi olarak, geerli bir
trdr. Yani yukardaki typedef bildiriminden sonra
pstr p;
gibi bir tanmlama yaplabilir. Bu tanmlama
char *p
ile ayn anlama gelir.
Bir typedef bildirimi ile elde edilen fayda her zaman #define nilemci komutuyla
salanamayabilir:
#define pstr char*
gibi bir nilemci ismi tanmlamas yapldnda, nilemci pstr ismini grd yerde
bunun yerine char * atomlarn yerletirir.
char *str;
gibi bir bildirimin
pstr str;
olarak yazlmasnda bir hata sz konusu olmaz. nilemci pstr yerine char *
yerletirdiinde derleyiciye giden kod
char *str
haline gelir.
char *p1, *p2, *p3;
nilemci #define komutunun kullanlarak yukardaki gibi bir bildirimin yaplmak istensin.
pstr p1, p2, p3;
yazldnda, nilemci yer deitirme ilemini yaptktan sonra derleyiciye verilen kod
aadaki biime dnr:
char *p1, p2, p3;
Bu tanmlama yaplmak istenen tanmlamaya edeer deildir. Yukardaki bildirimde
yalnzca p1 bir gsterici deikendir. p2 ile p3 gsterici deikenler deildir. char trden
deikenlerdir.
Bir diziye ilikin de yeni tr ismi bildirimi yaplabilir:
char isimdizi[20];
Yukardaki deyim ile isimdizi isimli char trden 20 elemanl bir dizi tanmlanm olur.
typedef char isimdizi[20];
Yukardaki deyim ise bir bildirimdir. Bu bildirim ile isimdizi artk 20 elemanl int trden
dizilerin tr ismidir. Bu typedef bildiriminden sonra eer
388
isimdizi a, b, c;
Gibi bir tanmlama yaplrsa, artk a, b, c her biri 20 elemanl char trden dizilerdir.
Bir typedef bildirimi ile birden fazla tr ismi yaratlabilir:
typedef unsigned int WORD, UINT;
Yukardaki bildirim deyiminden sonra, hem WORD hem de UINT, unsigned int trnn
yerine geen yeni tr isimleridir:
WORD x, y;
UINT k, l;
Artk x, y, k, l unsigned int trden deikenlerdir.
10 elemanl char trden gsterici dizisi iin tr ismi bildirimi yle yaplabilir:
typedef char *PSTRARRAY[10];
Bu bildirim deyiminden sonra
PSTRARRAY s;
ile
char *s[10];
tamamen ayn anlama gelir.
Bir tr ismi baka tr isimlerinin bildiriminde de kullanlabilir:
typedef unsigned int WORD;
typedef WORD UINT;
Yeni oluturulan tr isimleri, okunabilirlik asndan ya tamamen byk harflerden seilir,
ya da bu isimlerin yalnzca ilk harfleri byk harf yaplr.
Bir yap bildirimiyle yeni bir tr yaratlm olur. Bu tr nce derleyiciye tantldktan sonra,
bu tre ilikin deikenler tanmlanabilir:
struct Data {
int a, b, c;
};
Yukardaki bildirimle yeni bir veri tr yaratlyor. C dilinde bu veri trnn ismi struct
Data'dr. Trn ismi Data deildir. Yani bu veri trnden bir nesne tanmlamak
istendiinde tr ismi olarak struct Data yazlmaldr.
[C++ dilinde yap isimleri (structure tags) ayn zamanda trn de ismidir. struct anahtar szc olmadan
kullanldnda da bu trn ismi olarak derleyici tarafndan kabul grr.]
Yukardaki bildirimden sonra rnein bir tanmlama yaplacak olsa
struct Data d;
biiminde yaplmaldr. C dilinde bu tanmlamann
389
Data d;
biiminde yaplmas geersizdir. Oysa C++ dilinde bu durum geerlidir.
te struct anahtar szcnn, yap nesnesi tanmlamalarnda yap isminden nce
kullanlma zorunluluunu ortadan kaldrmak iin programclar, typedef bildirimiyle kendi
bildirdikleri yap trlerine ilikin yeni tr isimleri olutururlar.
Bir yap trne ilikin yeni bir tr isminin oluturulmas ayr biimde yaplabilir.
1. nce yap bildirimi yaplr. Daha sonra bildirilen yap tr iin ayr bir typedef bildirimi
yaplr:
struct tagPerson {
char name[30];
int no;
};
typedef struct tagPerson Person;
Person per = {"Necati Ergin", 123};
Yukardaki rnekte nce struct tagPerson isimli bir tr yaratlyor daha sonra typedef
bildirimiyle struct tagPerson trne yeni bir isim olarak Person ismi veriliyor. typedef
bildiriminden sonra, hem struct tagPerson hem de Person isimleri, tr isimleri olarak
kullanlabilir.
2. typedef bildirimi ile yap bildirimi tek bir bildirim biiminde birletirilebilir:
typedef struct tagPerson {
char name[30];
int no;
} Person;
Person per;
Daha nce verilen kural anmsayn: Bu bildirimin banda typedef anahtar szc
olmasayd Person ismi struct tagPerson trnden bir nesnenin ismi olurdu, deil mi?
Yukardaki rnekte hem ismi struct tagPerson olan bir yap bildiriliyor hem de typedef
bildirimiyle bu yapya yeni bir isim olarak Person ismi veriliyor. lgili bildirimden sonra
hem struct tagPerson hem de Person isimleri, tr isimleri olarak kullanlabilir.
3. Yap ismi (structure tag) kullanlmadan yaplan bir yap bildirimi ile typedef bildirimi
birletirilebilir:
typedef struct {
char name[30];
int no;
}Person;
Person y;
Yukardaki rnekte yaratlan trn tek bir ismi vardr. Bu isim Person ismidir. Bu tr
kullanlmak istendiinde artk struct anahtar szc kullanlamaz.
Programclarn ou, yap isimleriyle (structure tag), typedef bildirimiyle oluturulacak tr
isimleri iin farkl isimler bulmak yerine birka karakter kullanarak aralarnda iliki
kurarlar. ok kullanlan kalplardan biri, yap isminin bana bir "alt tire" karakteri
konularak tr isminden ayrlmasdr:
390
Baz standart ilevlerin bildiriminde, dorudan doal bir veri trn kullanmak yerine
daha nceden belirlenmi baz typedef isimleri kullanlr. Doal trler sistemden sisteme
farkl uzunlukta olabileceinden, baz ilevlerin ara yznde doal tr isimlerini kullanmak
yerine bir typedef isminin kullanlmas, derleyiciyi yazanlara daha byk bir esneklik
salar. rnein standart malloc ilevinin stdlib.h balk dosyas iindeki bildirimi aadaki
gibidir:
void *malloc(size_t ntypes);
Bu bildirimde size_t isminin bir tr ismi olarak kullanldn gryorsunuz.
Derleyicilerin ounda bu tr isminin bildirimi stddef.h, stdio.h, stdlib.h balk
dosyalarnda aadaki gibi yaplr:
391
typedef bildirimleri iin de bilinirlik alan kurallar geerlidir. Bir blok iinde tanmlanan bir
typedef ismi, o blok dnda bilinmez.
392
void func()
{
typedef int
}
void foo()
{
WORD x;
}
WORD;
/*Geersiz*/
Yukardaki programda
WORD x;
deyimi geersizdir. Zira WORD tr yalnzca func ilevinin ana blou iinde bilinen bir veri
trdr. Bu bloun dnda bilinmez.
C dilinde blok iinde yaplan bildirimlerin bloklarn banda yaplmas zorunludur. typedef
bildirimiyle blok iinde yaplan yeni tr ismi bildirimleri de bloklarn banda yaplmak
zorundadr.
Ancak hemen her zaman typedef bildirimleri global dzeyde yaplr. Uygulamalarda
typedef bildirimleri genellikle, ya kaynak dosyann banda ya da balk dosyalar iinde
yaplr. nk typedef isimleri ounlukla darya hizmet veren bir modln arayzne
aittir.
Ayn typedef ismi farkl iki trn yeni ismi olarak bildirilemez:
typedef int WORD;
/*...*/
typedef long WORD; /* Geersiz! */
Okunabilirlii artrmak iin typedef bildirimleri yaplabilir. Baz trlere onlarn kullanm
amalarna uygun isimler verilirse kaynak kod daha kolay okunur daha kolay
anlamlandrlr. rnein char tr genelde karakter deimezlerinin atand bir trdr.
char tr yalnzca bir byte'lk bir veri olarak kullanlacaksa, yani yaz ilemlerinde
kullanlmayacak ise aadaki gibi bir tr tanmlamas yerinde olur:
typedef char BYTE;
/*...*/
BYTE x;
C89 standartlarnda bool tr doal bir veri tr deildir. Ancak bir typedef bildirimiyle
int trne bool ismi verilebilir:
typedef int bool;
typedef bildirimleri yazm kolayl salar. Karmak pek ok tr ismi typedef bildirimi
kullanlarak kolay bir biimde yazlabilir. Program okuyanlar tr bilgisine karlk gelen
karmak atomlar yerine onu temsil eden yaln bir isimle karlarlar. Aada nce bir
ilev adresine ilikin tre typedef bildirimiyle yeni bir isim veriliyor, daha sonra bu trden
bir nesne tanmlanyor:
typedef struct Person * *Fpper)(struct Person *, int);
/*...*/
Fpper fptr;
3. typedef bildirimleri bazen de tanabilirlii artrmak amacyla kullanlr. typedef
bildirimlerinin kullanlmasyla, yazlan ilevlere ilikin veri yaplar deise bile kaynak
393
SAYAC;
394
typedef bildirimi ile gsterici trlerine yeni isim verilmesinde dikkat edilmesi gereken bir
nokta vardr. Tr niteleyicileri konusunda ele alnd gibi C dilinde aadaki gibi bir
tanmlama yapldnda
const int *ptr;
ptr gsterdii yer const olan bir gstericidir. Yani ptr deikeninin gsterdii yerdeki
nesne deitirilemez:
*ptr = 10;
gibi bir atama geersizdir. Ancak tanmlama
int *const ptr;
biiminde yaplrsa, ptr kendisi const olan bir gstericidir. ptr gstericisinin gsterdii
nesnenin deeri deitirilebilir, ama ptr gstericisinin iindeki adres deitirilemez, yani
ptr = (int *) 0x1F00;
gibi bir atama yaplmas geersizdir.
typedef int *IPTR;
gibi bir bildirimden sonra
const IPTR p;
biiminde bir tanmlama yapldnda, p gstericisinin deeri deitirilemez, p
gstericisinin gsterdii yerdeki nesnenin deeri deitirilebilir. Yani *p nesnesine atama
yaplabilir. Baka bir deyile
const IPTR ptr;
deyimi ile
int *const ptr;
deyimi edeerdir.
Windows iletim sistemi altnda alacak C ya da C++ programlarnn yazlmasnda
typedef bildirimleri sklkla kullanlr. windows.h isimli balk dosyasnda temel veri
trlerinin ouna typedef bildirimleriyle yeni isimler verilmitir. Windows API
programlamada windows.h balk dosyas kaynak koda eklenmelidir. Bu dosyann iinde
API ilevlerinin bildirimleri, eitli yap bildirimleri, typedef isimleri, nemli simgesel
deimezler bulunur.
int BOOL;
0
1
395
unsigned
unsigned
unsigned
unsigned
char BYTE;
short WORD;
long int DWORD;
int UINT;
Gstericilere ilikin typedef isimleri P harfiyle balar. LP uzak gstericileri belirtmek iin
n ek olarak kullanlr. Win16 sistemlerinde uzak ve yakn gsterici kavramlar vard.
Dolaysyla o zamanlar, P nekli gstericiler yakn gstericileri, LP nekli gstericiler ise
uzak gstericileri temsil ediyordu. Fakat Win32 sistemlerinde yakn ve uzak gsterici
kavramlar yoktur. Bu durumda, P nekli gstericilerle LP nekli gstericiler arasnda
hibir fark yoktur. Ancak, Win16'daki alkanlkla hala LP nekli typedef isimleri kullanlr.
Windows.h iinde her ihtimale kar -Win16 programlar alabilsin diye- near ve far
szckleri aadaki gibi silinmitir.
#define
#define
typedef
typedef
far
near
char near *PSTR;
char far *LPSTR;
PSTR ya da LPSTR Win32 sistemlerinde tamamen ayn anlama gelir ve char* trn
belirtir.
typedef char *PSTR;
typedef char *LPSTR;
Gstericilerde const'luk P ya da LP'den sonra C ekiyle belirtilir. rnein;
typedef const char *PCSTR;
typedef const char *LPCSTR;
Klasik typedef isimlerinin hepsinin gsterici karlklar da vardr. Btn gsterici trleri,
Win16 uyumlu olmas iin P ve LP nekleriyle ayrca bildirilmitir.
typedef
typedef
typedef
typedef
BYTE *PBYTE;
WORD *PWORD;
const BYTE *PCBYTE;
const DWORD *LPCDWORD;
C'nin doal trlerinin hepsinin byk harf normal, adres ve const adres biimleri vardr.
typedef long LONG;
typedef int INT;
typedef char CHAR;
Windows programlamada H ile balayan, handle olarak kullanlan pek ok typedef ismi
vardr. Bu typedef isimlerinin hepsi void * trndendir. rnein:
typedef
typedef
void
void
*HWND;
*HICON;
396
Standart balk dosyalarndan time.h iinde bildirilen, tarih ve zaman bilgileriyle ilgili
faydal iler yapmak iin tanmlanan baz standart ilevler vardr. Bu ilevlerin bazlarnn
parametre deikenleri ya da geri dn deerleri bir yap trnden adres bilgileridir.
Aada bu ilevler aklanyor:
time levi
time_t time (time_t *timer);
levin geri dn deeri standart bir typedef tr olan time_t trdr. Derleyicilerin
ounun bu tr long trnn typedef ismi olarak bildirir. levin parametre deikeni de
bu trden bir adrestir.
lev adresi gnderilen nesneye belirli bir tarihten (ou sistemde bu tarih 01.01.1970
tarihidir) ilev arsna kadar geen saniye says deerini yazar. Bu deer standartlarda
"takvim zaman" ("calender time") olarak geer. Tarih ve zaman zerinde ilem yapan
dier baz ilevler ilerini yapabilmek iin bu deere gereksinim duyar.
lev, bu deeri ayn zamanda geri dn deeri olarak da darya iletir. Eer takvim
zaman bilgisi elde edilemiyorsa ilev (time_t)-1 deerine geri dner.
Eer ileve NULL adresi gnderilirse, ilev bu deeri zel bir ileti olarak alglar, hibir
nesneye yazma yapmadan, saniye bilgisini yalnz geri dn deeri ile darya aktarr.
Aada ilevin kullanlmasyla ilgili basit bir kod paras veriliyor:
#include <stdio.h>
#include <time.h>
int main()
{
time_t timer1, timer2, timer3;
timer1 = time(&timer2);
printf("devam etmek iin bir tua basn : ");
getchar();
timer3 = time(NULL);
printf("timer1 = %ld\n", (long)timer1);
printf("timer2= %ld\n", (long)timer2);
printf("timer3= %ld\n", (long)timer3);
}
return 0;
localtime levi
01.01.1970'den geen saniye says yani takvim zaman dorudan kullanlabilecek bir
zaman bilgisi deildir. localtime ilevi bu bilgiyi alarak faydal paralara ayrr. levin
time.h balk dosyas iindeki bildirimi aadaki gibidir:
struct tm *localtime(const time_t *timer);
levin geri dn deeri, time.h iinde bildirilen bir yap olan struct tm trnden bir
adrestir. Bu yap aadaki gibi bildirilmitir:
397
struct tm {
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
int tm_wday;
int tm_yday;
int tm_isdst;
};
Yapnn ismi tm nekiyle balayan tm elemanlar int trdendir. Her bir eleman tarih ya
da zaman ile ilgili faydal bir veriyi tutar. Aada yapnn elemanlar hakknda bilgi
veriliyor:
tm_sec
: saniye deeri (0 - 60)
tm_min
: dakika deeri (0 - 60)
tm_hour
: saat deeri (0 24)
tm_mday
: Ayn gn (1 31)
tm_mon
: Ay deeri (0 Ocak, 1 ubat, 2 Mart)
tm_year
: Yl deerinin 1900 eksii
tm_wday
: Haftann gn (0 Pazar, 1 Pazartesi, 2 Sal)
tm_yday
: Yln gn (1 Ocak iin 0)
tm_isdst
:Gn tasarruf modu ile ilgili bilgi. Bu elemann deerinin pozitif ise
tasarruf modunda olunduu bilgisi iletilmi olur. Bu deer 0 ise tasarruf modu deildir.
Elemann deerinin negatif olmas durumunda bu bilgi elde edilemiyor demektir.
struct tm yaps ile tutulan zaman bilgisine "ayrtrlm zaman bilgisi" (broken-down
time) denir.
localtime ilevi statik mrl bir struct tm nesnesinin adresi ile geri dner. levin geri
dn deeri olan adresteki yap nesnesinin deeri kullanlmadan ya da baka bir
nesneye aktarlmadan, ilev bir kez daha arlrsa, daha nceki ar ile ilgili olarak
retilen deerin stne yazlm olur. levin parametre deikeni time_t trnden bir
nesneyi gsteren gstericidir. lev adresini ald bu nesneden takvim zaman bilgisini
alr. Aada localtime ilevini kullanan rnek bir kod veriliyor:
#include <stdio.h>
#include <time.h>
char *months[12] = {"Ocak", "Subat", "Mart", "Nisan", "Mayis", "Haziran",
"Temmuz", "Agustos", "Eylul","Ekim", "Kasim", "Aralik"};
char *days[7] = {"Pazar","Pazartesi", "Sali", "Carsamba", "Persembe",
"Cuma", "Cumartesi"};
int main()
{
time_t timer;
struct tm *tptr;
time(&timer);
tptr = localtime(&timer);
printf("Tarih : %02d %s %04d %s\n",
tptr->tm_mday,months[tptr>tm_mon], ptr->tm_year + 1900, days[tptr>tm_wday]);
printf("Saat:%02d:%02d:%02d\n", tptr->tm_hour, tptr->tm_min,
tptr->tm_sec);
printf("bugun %d yilinin %d. gunu\n", tptr->tm_year + 1900,
tptr->tm_yday);
if (tptr->tm_isdst < 0)
398
ctime levi
Bu ilev, takvim zaman bilgisini girdi olarak alarak bu bilgiyi bir yazya dntrr.
levin bildirimi aadaki gibidir:
char *ctime(const time_t *time);
levin parametre deikeni time_t trnden bir adrestir. lev adresini ald nesneden
takvim zaman bilgisini okur. levin geri dn deeri 26 karakterlik bir yaznn balang
adresidir. Bu yaz ayrtrlm zaman bilgilerini ieren zel olarak formatlanm bir
yazdr:
F
\n
asctime levi
ctime ilevinin yapt iin aynsn yapar. Ancak girdi olarak takvim zamann deil
ayrtrlm zaman bilgisini alr:
char *asctime (const struct tm *tblock);
levin parametre deikeni struct tm yaps trnden bir adrestir. lev, adresini ald
nesneden, ayrtrlm zaman bilgilerini okur. Bu bilgiyi bir yazya dntrerek, zel bir
formatta 26 karakterlik statik mrl bir dizi iine yazar. lev, ilgili yaznn balang
adresine geri dner. levin geri dn deeri olan adresteki yaz kullanlmadan ya da
baka bir diziye aktarlmadan, ilev bir kez daha arlrsa, daha nceki ar ile ilgili
olarak retilen yaznn stne yazlm olur. Aada ilevin kullanmna ilikin rnek bir
kod paras yer alyor:
399
'\0'
#include <stdio.h>
#include <time.h>
int main()
{
time_t timer;
timer = time(NULL);
printf("%s", asctime(localtime(&timer)));
return 0;
Yukardaki kod parasnda, localtime ilevinin geri dn deeri olan struct tm trnden
adres dorudan asctime ilevine argman olarak gnderiliyor. asctime ilevinin geri
dn deeri olan adres de printf ilevine geiliyor.
clock levi
levin geri dn deeri time.h balk dosyas iinde bildirilen standart bir typedef tr
olan clock_t trdr. Derleyicilerin hemen hepsinde bu tr long trnn typedef ismi
olarak bildirilir:
clock_t clock(void);
lev, programn almaya balamasyla ilev arsna kadar geen sreye geri dner.
Ancak ilevin geri dn deerine ilikin birim saniye deil ilemcinin zaman devresinin
tick saysdr. levin geri dn deerini saniyeye dntrmek iin, geri dn deeri
ilemcinin saniyedeki tick saysna blnmelidir. Zaman devresinin bir saniyedeki tick
sayisi time.h iinde CLOCKS_PER_SEC isimli bir simgesel deimez olarak tanmlanmtr.
#define
CLOCKS_PER_SEC
1000
CLK_TCK
CLOCKS_PER_SEC
return 0;
400
SENSITIVITY
0.1
for (;;) {
tend = clock();
duration = (double)(tend - tstart) / CLOCKS_PER_SEC;
if (fabs(duration - sec) < SENSITIVITY)
return;
}
difftime levi
Standart difftime ilevi takvim zaman cinsinden verilen iki zaman bilgisi arasndaki
saniye farkn bulmak iin kullanlabilir. lev bildirimi aadaki gibidir:
double difftime(time_t timer2, time_t timer1);
levin geri dn deeri timer1 deeri ile timer2 deeri arasnda geen saniye saysdr.
#include <stdio.h>
#include <math.h>
#include <time.h>
int main()
{
time_t start,finish;
long i;
double result, elapsed_time;
printf("20000000 kez kare kok aliniyor.\n");
time(&start);
for (i = 1; i <= 20000000; i++)
result = sqrt(i);
time(&finish);
elapsed_time = difftime(finish, start);
printf("\nToplam sure = %lf saniye.\n", elapsed_time);
return 0;
mktime levi
401
return 0;
402
Aadaki programda bir tarih bilgisini tutmak amacyla Date isimli bir yap bildiriliyor. Bu
yap trn kullanarak hizmet veren ilevler tanmlanyor.
/********** date.h **************************/
typedef struct {
int m_d;
int m_m;
int m_y;
}Date;
#define
#define
#define
YEAR_BASE
false
true
1000
0
1
PUBLIC
PRIVATE
FACTOR
static
2
403
404
ptr->m_d = d;
ptr->m_m = m;
ptr->m_y = y;
}
/*************************************************************************/
PUBLIC void set_today(Date *ptr)
{
time_t timer = time(NULL);
struct tm *tptr = localtime(&timer);
ptr->m_d = tptr->tm_mday;
ptr->m_m = tptr->tm_mon + 1;
ptr->m_y = tptr->tm_year + 1900;
}
/*************************************************************************/
PUBLIC void set_random(Date *ptr)
{
ptr->m_y = rand() % 50 + 1960;
ptr->m_m = rand() % 12 + 1;
ptr->m_d = rand() % daytabs[isleap(ptr->m_y)][ptr->m_m] + 1;
}
/*************************************************************************/
PUBLIC void display_date(const Date *ptr)
{
static const char *days[] = {"Pazar", "Pazartesi", "Sali", "Carsamba",
"Persembe", "Cuma",
"Cumartesi"};
static const char *mons[] = {"", "Ocak", "Subat", "Mart", "Nisan",
"Mayis", "Haziran",
"Temmuz", "Agustos", "Eylul","Ekim", "Kasim",
"Aralik"};
printf("%02d %s %4d %s\n", ptr->m_d, mons[ptr->m_m], ptr->m_y,
days[get_weekday(ptr)]);
}
/*************************************************************************/
PUBLIC int date_cmp(const Date *p1, const Date *p2)
{
if (p1->m_y != p2->m_y)
return p1->m_y - p2->m_y;
if (p1->m_m != p2->m_m)
return p1->m_m - p2->m_m;
return p1->m_d - p2->m_d;
}
/*************************************************************************/
PUBLIC void inc_date(Date *p)
{
*p = revdate(totaldays(p) + 1);
}
/*************************************************************************/
PUBLIC void dec_date(Date *p)
{
*p = revdate(totaldays(p) - 1);
}
/*************************************************************************/
PUBLIC Date ndays(const Date *p, int n)
{
return revdate(totaldays(p) + n);
}
/*************************************************************************/
PUBLIC int get_weekday(const Date *ptr)
{
return (totaldays(ptr) + FACTOR) % 7;
}
405
/*************************************************************************/
PUBLIC bool isweekend(const Date *ptr)
{
int day = get_weekday(ptr);
return day == 6 || day == 0;
}
/*************************************************************************/
int main()
{
Date today;
Date ndaysafter;
set_today(&today);
ndaysafter = ndays(&today, 10);
while (date_cmp(&today, &ndaysafter)) {
display_date(&today);
inc_date(&today);
}
}
return 0;
406
407
return pd;
}
/*************************************************************************/
PUBLIC void closelist(ListHandle handle)
{
clear_list(handle);
free(handle);
}
/*************************************************************************/
PUBLIC void push_front(ListHandle handle)
{
Node *pnew = create_node();
set_random(&pnew->date);
handle->size++;
if (handle->pstart == NULL) {
handle->pstart = handle->pend = pnew;
pnew->pnext = NULL;
return;
}
pnew->pnext = handle->pstart;
handle->pstart = pnew;
}
/*************************************************************************/
PUBLIC void push_back(ListHandle handle)
{
Node *pnew = create_node();
set_random(&pnew->date);
pnew->pnext = NULL;
handle->size++;
if (handle->pstart == NULL) {
handle->pstart = handle->pend = pnew;
return;
}
handle->pend->pnext = pnew;
handle->pend = pnew;
}
/*************************************************************************/
PUBLIC void display_list(ListHandle handle)
{
Node *cur = handle->pstart;
if (!handle->size) {
printf("empty list!\n");
return;
}
while (cur) {
display_date(&cur->date);
cur = cur->pnext;
}
}
/*************************************************************************/
PUBLIC void clear_list(ListHandle handle)
{
free_nodes(handle->pstart);
handle->pstart = handle->pend = NULL;
}
/*************************************************************************/
PUBLIC size_t get_size(ListHandle handle)
{
return handle->size;
}
408
/*************************************************************************/
PUBLIC void pop_front(ListHandle handle)
{
Node *temp;
if (!handle->size) {
printf("liste bos!\n");
return;
}
handle->size--;
if (handle->pstart == handle->pend) {
free(handle->pstart);
handle->pstart = handle->pend = NULL;
return;
}
temp = handle->pstart;
handle->pstart = handle->pstart->pnext;
free(temp);
}
/*************************************************************************/
PUBLIC void pop_back(ListHandle handle)
{
Node *temp, *cur;
if (!handle->size) {
printf("liste bos!\n");
return;
}
handle->size--;
if (handle->pstart == handle->pend) {
free(handle->pstart);
handle->pstart = handle->pend = NULL;
return;
}
temp = handle->pend;
for (cur = handle->pstart; cur->pnext != handle->pend; cur = cur>pnext)
;
cur->pnext = NULL;
handle->pend = cur;
free(temp);
}
/*************************************************************************/
void display_menu()
{
printf("[1] PUSH FRONT\n");
printf("[2] PUSH BACK\n");
printf("[3] DISPLAY LIST\n");
printf("[4] POP FRONT\n");
printf("[5] POP BACK\n");
printf("[6] EMPTY LIST\n");
printf("[7] EXIT\n");
printf("seciminiz : ");
}
/*************************************************************************/
int get_option()
{
int option;
409
display_menu();
scanf("%d", &option);
if (option < 1 || option > 7)
option = 0;
return option;
}
/*************************************************************************/
int main()
{
int option;
ListHandle handle = openlist();
for (;;) {
option = get_option();
switch (option) {
case 1: push_front(handle); break;
case 2: push_back(handle); break;
case 3: display_list(handle);break;
case 4: pop_front(handle);break;
case 5: pop_back(handle);break;
case 6: clear_list(handle); break;
case 7: goto END;
case 8: printf("invalid entry!\n");
}
}
END:
closelist(handle);
printf("end of program!\n");
}
return 0;
410
BRLKLER
Programc tarafndan yeni bir tr yaratmasna olanak veren bir baka ara da "birlikler"dir
(unions).
Birlikler yaplara ok benzer. Bir birliin kullanlabilmesi iin, yani bir birlik trnden
nesnelerin tanmlanabilmesi iin nce birliin bildirimi yaplmaldr. Birlik bildirimleri ayn
yap bildirimleri gibi yaplr. Tek fark struct anahtar szc yerine union anahtar
szcnn kullanlmasdr.
Aadaki rnei inceleyin:
union Dword {
unsigned char byte;
unsigned int word;
unsigned long dword;
};
Yukardaki deyimle, ismi union Dword olan yeni bir tr bildirilmi olur. Bu bildirimin
grlr olduu yerlerde, bu tr kullanlabilir. Bir typedef bildirimi yaplarak, bu trn
isminin, yalnzca Dword olmas da salanabilir:
typedef union {
unsigned char byte;
unsigned int word;
unsigned long dword;
}Dword;
411
Birlik nesneleri iin birliin en uzun eleman kadar yer ayrlr. Birlik elemanlarnn hepsi
ayn saysal adresten balayacak ekilde bellee yerleir. rnein:
union Dword {
unsigned char byte;
unsigned short word;
unsigned long dword;
};
union Dword a;
bildirimi ile, Dword trden a deikeni iin bellekte Unix, Windows sistemlerinde 4 byte
yer ayrlr. nk a deikeninin dword isimli eleman, 4 byte ile birliin en uzun
elemandr.
1F00
1F01
1F02
1F03
a.byte
a
a
a
a.word
a.dword
Birlik bir dizi ieriyorsa dizi tek bir eleman olarak alnr. rnein:
typedef union {
float f;
unsigned char bytes[4];
}Float;
Float x;
tanm ile x deikeni iin ne kadar yer ayrlr? Float birliinin iki eleman vardr. Birincisi
4 byte uzunluunda float trden bir deiken, ikincisi de 4 byte uzunluunda bir karakter
dizisidir. ki uzunluk da ayn olduuna gre x nesnesi iin 4 byte yer ayrlaca
sylenebilir.
Buna gre
Float x;
x.f = 10.2F;
ile x.bytes[0], x.bytes[1], x.bytes[2], x.bytes[3] srasyla float trden elemann byte
deerleridir.
Aadaki program inceleyin:
412
#include <stdio.h>
typedef union {
char ch;
int i;
double d;
char s[4];
}Data;
int main()
{
Data data;
printf("&data
printf("&data.ch
printf("&data.i
printf("&data.d
printf("data.s
=
=
=
=
=
%p\n",
%p\n",
%p\n",
%p\n",
%p\n",
&data);
&data.ch);
&data.i);
&data.d);
data.s);
return 0;
}
Yukardaki programn derlenip altrlmasyla ekrana hep ayn adres yazdrlr.
Birlik elemanlarnn ayn orjinden, yani ayn saysal adresten balayarak yerletirilmesi bir
elemann deeri deitirildiinde, dier elemanlarn da deerlerinin deiecei anlamna
gelir. Zaten birliklerin kullanlmasnn asl amac da budur.
C standartlarna gre, birlik nesnelerinin yalnzca ilk elemanna ilkdeer verilebilir. Bir
birlik nesnesinin birden fazla elemanna ilkdeer vermek geersizdir:
union Dword {
unsigned char byte;
unsigned int word;
unsigned long dword;
} x = {'m'};
union Dword
/* Geersiz */
413
typedef struct {
int stok_no;
float fiyat;
int urun_tipi;
char kitapisim[20];
char yazar[20];
int sayfa_sayisi;
char desen[20];
int renk;
int boyut;
char saatisim[20];
int model;
}Katalog;
Yukardaki bildirimde Katalog yapsnn urun_tipi isimli eleman rnn ne olduu bilgisini
tutar. Bu elemann deeri yalnzca KITAP, TSHORT ya da SAAT olabilir. Bunlarn simgesel
deimezler olarak tanmlandn dnelim. Yukarda bildirilen yap rnlerin btn
zelliklerini tutabilir ancak yle bir sakncas vardr:
Eer rn tipi KITAP deil ise isim[20], yazar[30] ve sayfa_sayisi isimli elemanlar hi
kullanlmaz. Yine rn tipi TSHORT deil ise desen[20], renk, boyut elemanlar hi
kullanlmaz.
Unix ya da Windows sistemlerinde byte hizalama altnda yukarda bildirilen Katalog
trnn sizeof deeri 108'dir. Yani Katalog trnden bir nesne tanmlandnda bu nesne
bellekte 108 byte yer kaplar.
#include <stdio.h>
int main()
{
Katalog katalog;
Ama Katalog yapsnn bir elemannn bir birlik trnden olmasyla yer kazanc
salanabilir:
414
typedef struct {
char isim[20];
char yazar[20];
int sayfa_sayisi;
}Kitap;
typedef struct {
char desen[20];
int renk;
int size;
}Tshirt;
typedef struct {
char isim[20];
int model;
}Saat;
typedef union {
Kitap kitap;
Tshirt tshirt;
Saat saat;
}Data;
typedef struct {
int stok_no;
float fiyat;
int urun_tipi;
Data data;
}Katalog;
Yukardaki bildirimleri inceleyin. nce kitap, tshirt ve saate ilikin bilgileri tutmak
amacyla Kitap, Tshirt ve Saat isimli yaplar bildirilmi. Unix, Windows sistemlerinde
Kitap, Tshirt ve Saat yaplarnn sizeof deerleri srasyla 44, 28 ve 24'dr. Daha sonra
Data isimli bir birliin bildirildiini gryorsunuz. Bu birliin elemanlar Kitap, Tshirt ve
Saat trlerinden olduuna gre bu birliin sizeof deeri en uzun elemannn yani Kitap
yaps trnden olan elemannn sizeof u kadardr, yani 44 dr.
Katalog isimli yapnn bildiriminde ise Data birlii trnden data isimli bir eleman yer
alyor. Bu durumda Katalog yapsnn sizeof deeri yalnzca 56 olur. Birliin
kullanlmasyla Katalog yapsnn sizeof deeri 108 den 56'ya drlyor.
#include <stdio.h>
int main()
{
printf("sizeof(Katalog) = %u\n", sizeof(Katalog));
return 0;
}
Katalog yapsyla ilgili ilem yapan kod paralar, Katalog yaps trnden nesnenin nce
urun_tipi isimli elemannn deerine bakarak, rnn cinsi bilgisini elde ettikten sonra,
duruma gre, data isimli elemanlar farkl biimde kullanabilir:
Katalog katalog;
/***/
if (katalog.urun_tipi == KITAP)
puts(katalog.data.kitap.isim)
415
Birlik kulanmnn ikinci nedeni herhangi bir veri trnn paralar zerinde ilem yapmak
ya da paralar zerinde ilem yaparak bir btn oluturmaktr. Aadaki rnei
inceleyin:
int trnn 2 byte olduu bir sistemde aadaki bildirimlerin yapldn dnelim:
typedef struct {
unsigned char low_byte;
unsigned char high_byte;
}Word;
typedef union {
unsigned int i;
Word w;
}Wordform;
bildirimlerinden sonra, Wordform trnden bir nesne tanmlanrsa bu birlik nesnesi, alak
(low_byte) ve yksek (high_byte) anlaml byte'larna ayr ayr eriilebilen bir tamsay
olarak kullanlabilir:
#include <stdio.h>
int main()
{
Wordform wf;
wf.w.low_byte = 0x12;
wf.w.high_byte = 0x34;
printf("%x\n", wf.i);
}
return 0;
Yani Intel ilemcilerinin bulunduu 16 bit sistemlerde yukardaki main ilevinin derlenip
altrlmasyla:
printf("%x\n", wf.i);
ars ile 3412 says ekrana yazdrr. Motorola ilemcilerinde (big endian) dk saysal
adreste dk anlaml dk anlaml byte deeri olacana gre saynn ters olarak
grlmesi gerekir.
Benzer bir tema, ilemcilerin yazmalarnn yazlmsal olarak temsil edilmesinde karmza
kar.
8086 ilemcilerinde toplam 14 yazma vardr.
- 4 tane genel amal yazma: AX, BX, CX, DX
- 4 tane segment yazmac: CS, DS, CS, ES
- 2 tane indeks yazmac: SI, DI
- 3 tane gsterici yazmac: IP, BP, SP
- 1 tane bayrak yazmac: F
Btn yazmalar 16 bit uzunluundadr. Genel amal yazmalar olan AX, BX, CX, DX
yazmalar 8'er bitlik iki paraya ayrlr. Genel amal yazmalar aadaki gibi bamsz
8 bitlik yazmalar gibi de kullanlabilir.
AX
AH
BX
AL
416
BH
CX
CH
DX
DH
BL
CL
DL
regs.x.cx
regs.x.bx
regs.x.cx
regs.x.dx
<-<-<-<-<-<-<-<--
regs.h.al
regs.h.ah
regs.h.bl
regs.h.bh
regs.h.cl
regs.h.ch
regs.h.dl
regs.h.dh
regs.x.si
regs.x.di
regs.x.flags
regs.x.cflag
417
#include <stdio.h>
#include <stdlib.h>
typedef struct _Complex {
double m_r, m_i;
}Complex;
void set_cr(Complex *p)
{
p->m_r = (rand() % 2 ? 1:-1) * ((double)rand() / RAND_MAX + rand()% 9);
p->m_i = (rand() % 2 ? 1:-1) * ((double)rand() / RAND_MAX + rand()% 9);
}
void display_c(const Complex *p)
{
printf("(%.2lf %+.2lfi)", p->m_r, p->m_i);
}
Yukarda tanmlanan set_cr ilevi adresini ald bir Complex nesnesini rastgele bir
deerle set ediyor. display_c isimli ilev ise adresini ald bir Complex nesnesinin
deerini ekrana yazdryor.
nce okunabilirlii artrmak iin baz simgesel deimezler tanmlayalm:
#define
#define
#define
#define
CHAR
INT
DOUBLE
COMPLEX
0
1
2
3
418
419
#define
ARRAY_SIZE
50
int main()
{
Element a[ARRAY_SIZE];
srand(time(0));
set_array(a, ARRAY_SIZE);
display_array(a, ARRAY_SIZE);
return 0;
}
main ilevi iinde Element trnden a isimli bir dizi tanmlanyor.
set_array(a, ARRAY_SIZE);
arsyla a dizisinin elemanlarna rastgele deerler atanyor. Tabi bu durumda dizinin
elemanlar rastgele bir biimde char, int, double ya da Complex trlerinden seiliyor.
display_array(a, ARRAY_SIZE);
arsyla dizinin elemanlarnn deerleri ekrana yazdrlyor. Aada programn
altrlmasyla elde edilen rnek bir ekran kts veriliyor:
(1.07)(4.49)(19755)(1.69 -8.82i)(0.91)
(4.45)(9.35)(1.41)(V)(5.57 +5.51i)
(K)(T)(11331)(7.78)(22316)
(26066)(28923)(2.36 -2.31i)(C)(8.39)
(9951)(8.79)(28301)(7.50)(18583)
(20564)(7.02)(-0.23 -5.46i)(28931)(-4.76 +2.32i)
(5850)(14891)(J)(-5.40 +8.65i)(H)
(7.50 -3.32i)(-3.81 +3.17i)(D)(-5.38 -6.72i)(2.15 +2.47i)
(-4.04 +2.10i)(4737)(-0.21 -0.61i)(-9.00 -3.72i)(4276)
(12552)(-6.95 -0.11i)(E)(0.71)(-4.72 +7.21i)
420
NUMARALANDIRMALAR
Yazlan birok programda, yalnzca belirli anlaml deerler alabilen deikenler kullanma
gereksinimi duyulur. rnein bir "Boolean" deikeni, yalnzca iki deere sahip olabilir:
"Doru" ve "Yanl". Haftann bir gnn tutacak bir deiken, haftann yedi gn
olduuna gre yedi farkl deerden birini alabilir.
Baka bir rnek olarak bir oyun kadnn rengini tutacak bir deikeni verilebilir. Byle
bir deiken yalnzca 4 deiik deer alabilir: Sinek, Karo, Kupa, Maa.
Byle deikenler tamsay trlerinden tanmlanabilir:
int renk;
renk = 1;
Yukardaki rnekte renk isimli deiken bir oyun kadnn renk bilgisini tutuyor. Bu
deikene 1 deerinin atanmas renk deerinin Karo olduunu gsteriyor.
Byle bir teknik uygulamalarda pekala kullanlabilir. Ancak bu tekniin baz sakncalar
vardr.
1. Kodu okuyan renk isimli deikene yaplan atamalarda kullanlan tamsay
deimezlerinin neyi temsil ettii konusunda dorudan bilgi alamaz. Kodun okunabilirlii
azalr. rnein
renk = 1;
eklinde bir atama dorudan renk deikenine "karo" deerinin verilmi olduu biiminde
de anlamlandramaz.
2. Derleyici renk deikeni ile ek bir kontrol yapamaz. Derleyici renk deikeninin
yalnzca 4 farkl deerden birini almas gerektiinin farknda deildir. Derleyiciye byle
bir bilgi verilmemitir. rnein byle bir deikene 10 gibi bir deerin atanmas
anlamszdr. Ancak derleyici derleme srasnda bu yanll bulamaz, bir hata ya da bir
uyar iletisiyle veremez.
C dili, isimlendirilmi belirli tamsay deerlerini alabilen, bir tr yaratlmasna olanak
veren bir araca sahiptir. Bu araca numaralandrrma (enumaration) ve bu arala ilikili
kullanlan deimezlere (enumaration constants) "numaralandrma deimezleri" denir.
Bir numaralandrma trnn ve bir numaralandrma trne ilikin deimezlerin bildirimi
aadaki gibi yaplr:
enum [enum trnn isimi] {deimez1,deimez2, ......};
enum bir anahtar szcktr. Derleyici kme ayralar arasnda isimlendirilmi
deimezleri 0 deerinden balayarak artan tamsay deerleriyle eler. rnek :
enum Renk {Sinek, Karo, Kupa, Maca};
Yukardaki bildirimle Sinek deimezi 0, Karo deimezi 1, Kupa deimezi 2, Maca
deimezi ise 3 deerini alr.
enum Bool {TRUE, FALSE};
burada TRUE deimezi 0 FALSE deimezi ise 1 deerini alr.
enum Months {January, February, March, April, May, June, July, August,
September, Oktober, November, December};
421
422
Bir numaralandrma trnden tanmlanm bir nesne iin derleyici, kullanlan sistemde int
trnn uzunluu kadar bir yer ayrr. Derleyici iin numaralandrma trnden bir nesne
ile int trden bir nesne arasnda fiziksel olarak herhangi bir fark bulunmaz.
rnein Unix altnda :
sizeof(enum Days) == sizeod(int) ==
ifadesi dorudur.
Tabi bu kuraln bir kt taraf int trnn 2 byte olduu bir sistemde kullanlabilecek en
byk numaralandrma deimezinin deeri 32767 olabilir.
enum bildirimi ile yeni bir tr yaratldna gre, bu trden nesne ya da gsterici
tanmlanabilir. Tanmlanacak ilevlerin geri dn deerleri ve/veya parametre
deikenleri de byle bir trden olabilir:
typedef enum {Sunday, Monday, Tuesday, Wednesday, Thursday,
Friday, Saturday}Days;
typedef struct {int d, m, y}Date;
Bir yap trnn bildiriminde, elemanlar bir numaralandrma trnden seilebilir:
typedef struct {
Days day;
Months month;
int year;
}Date;
423
Yukardaki rnekte Date isimli yap trnn day ve month isimli elemanlar Days ve
Months isimli numaralandrma trlerindendir.
424
/* Geerli */
/* Geerli */
425
#include <stdio.h>
#include <stdlib.h>
typedef enum {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday,
Saturday}Days;
void print_day(Days day)
{
static const char *days[] = {"Sunday", "Monday", "Tuesday",
"Wednesday", "Thursday", "Friday", "Saturday"};
printf("%s ", days[day]);
}
int main()
{
Days day;
int k;
for (k = 0; k < 10; ++k) {
day = (Days)(rand() % 7);
print_day(day);
}
}
return 0;
426
BTSEL LELER
Bitsel ileler (bitwise operators), bir tamsaynn bitleri zerinde ilemler yapar. Daha ok
sistem programlarnda ya da dk seviyeli kodlarda kullanlrlar. Bitsel ilelerin ortak
zellikleri, ileme soktuklar tamsaylar bir btn olarak deil, bit bit ele almalardr. Bu
ilelerin terimleri tamsay trlerinden olmak zorundadr. Terimlerinin gerek say
trlerinden olmas geersizdir.
C dilinde toplam 11 bitsel ile vardr. Aadaki tabloda bitsel ileler kendi aralarndaki
ncelik srasna gre listeleniyor:
ncelik
seviyesi
2
5
8
9
10
14
simge
~
<<
>>
&
^
|
<<=
>>=
&=
^=
|=
ile
Bitsel deil (bitwise not)
Bitsel sola kaydrma (bitwise left shift)
Bitsel saa kaydrma (bitwise right shift)
Bitsel ve (bitwise and)
Bitsel zel veya (bitwise exor)
Bitsel veya (bitwise or)
Bitsel ilemli atama ileleri
(bitwise compound assignment operators)
Yukardaki ileler iinde, yalnzca "Bitsel deil" (bitwise not) ileci, tek terimli nek
konumunda (unary prefix) bir iletir. Dierleri iki terimli (binary) araek konumunda
(infix) bulunan ilelerdir.
return 0;
Yukardaki programn, int trnn 4 byte olduu bir sistemde derlendiini dnelim. x
deikenine atanan 0x1AC3 deeri, ikilik say sisteminde aadaki biimde ifade edilir:
0000 0000 0000 0000 0001 1010 1100 0011
427
*/
return 0;
Bir tamsayy, sola bitsel olarak 1 kaydrmakla o tamsaynn ikiyle arplm deeri elde
edilir.
Bitsel saa kaydrma ileci, soldaki terimi olan tamsaynn, sadaki terimi olan tamsay
kadar saa kaydrlmasndan elde edilen deeri retir. Sol terim iaretsiz (unsigned) bir
tamsay trnden ise, ya da iaretli (signed) bir tamsay trnden ancak pozitif deere
sahip ise, snr dna kan bitler yerine, saynn solundan besleme 0 biti ile yaplr. Saa
kaydrlacak ifadenin iaretli bir tamsay trnden negatif deerli olmas durumunda snr
428
*/
return 0;
Bitsel kaydrma ilelerinin yan etkileri yoktur. Yani sol terimleri bir nesne ise, bu
nesnenin bellekteki deeri deimez. Kaydrma ilemi ile sol terim olan nesnenin deeri
deitirilmek isteniyorsa, bu ilelerin ilemli atama biimleri kullanlmaldr.
Kaydrma ilelerinin sa terimi, sol terimin ait olduu trn toplam bit saysndan daha
kk olmaldr. Bu koul salanmam ise oluan durum tanmszdr (undefined
behaviour). rnein Windows sistemlerinde int trden bir deerin 32 ya da daha fazla
sola ya da saa kaydrlmas tanmszdr. Bu durumdan kanlmaldr.
Bitsel kaydrma ilelerinin ncelik yn soldan saadr:
x << 4 >> 8
x, 2 byte'lk iaretsiz bir tamsay deiken olsun. Yukardaki ifade derleyici tarafndan
x << 4) >> 8
biiminde ele alnr. Bu ifade ile x deikeninin ortadaki 8 biti elde edilir.
Bitsel ve leci
"Bitsel ve" ileci (bitwise and), ile ncelik tablosunun 8. seviyesindedir. Bu seviyenin
ncelik yn soldan saadr. lecin yan etkisi yoktur. Terimleri nesne gsteren bir ifade
ise, bu terimlerin bellekteki deeri deimez. Deer retmek iin terimi olan tamsaylarn
karlkl bitlerini "ve" ilemine sokar. "Ve" ilecine ilikin ilem tablosu tablosu aada
yeniden veriliyor:
429
"Bitsel ve" ilecinin rettii deer, terimlerinin karlkl bitlerinin "ve" ilemine
sokulmasyla elde edilen deerdir:
#include <stdio.h>
int main()
{
unsigned int x = 0x1BC5;
unsigned int y = 0X3A0D;
unsigned int z = x & y;
printf("z = %X\n", z);
}
/*
/*
/*
/*
x
y
z
z
=
=
=
=
return 0;
return 0;
Yukardaki programda "mantksal ve" (&&) ileci yerine yanllkla "bitsel ve" (&) ileci
kullanlyor. Hem "mantksal ve" hem de "bitsel ve", iki terimli, araek konumunda
ilelerdir. Derleyiciler yukardaki kod iin bir hata ya da uyar iletisi retmez. Yukardaki
rnekte "mantksal ve" ilecinin kullanlmas durumunda, mantksal "doru" biiminde
yorumlanacak olan ifade, bitsel ve ilecinin kullanlmasyla 0 deeri retir, mantksal
"yanl" olarak yorumlanr.
Bitsel "zel veya" ileci (bitwise exor) ile ncelik tablosunun 9. seviyesindedir. ncelik
yn soldan saadr. Yan etkisi yoktur, terimleri olan nesnelerin bellekteki deeri
deimez. Deer retmek iin, terimi olan tamsaylarn karlkl bitlerini zel veya
(exclusive or) ilemine sokar. Bitsel "zel veya" ilecine ilikin ilem tablosu aada
veriliyor:
430
Yukardaki tablo yle zetlenebilir: Terimlerinden ikisi de ayn deere sahip ise, retilen
deer 0, terimlerden biri dierinden farkl ise retilen deer 1 olur.
#include <stdio.h>
int main()
{
unsigned int x = 0x1BC5;
unsigned int y = 0X3A0D;
unsigned int z = x ^ y;
printf("z = %X\n", z);
return 0;
}
/*
/*
/*
/*
x
y
z
z
=
=
=
=
0001
0011
0010
21C8
Bir tamsay, arka arkaya ayn deerle bitsel zel veya ilemine sokulursa, tamsaynn
kendi deeri elde edilir:
#include <stdio.h>
int main()
{
unsigned int a = 0X1BC5;
unsigned int b = 0X3A0D;
a ^= b;
a ^= b;
printf("a = %X\n", a);
return 0;
}
Baz ifreleme algoritmalarnda "zel veya" ileminin bu zelliinden faydalanlr.
"Bitsel veya" (bitwise or operator) ileci, ile ncelik tablosunun 10. seviyesindedir.
ncelik yn soldan saadr. Yan etkisi yoktur, terimleri nesne gsteren bir ifade ise
bellekteki deerlerini deitirmez. Deer retmek iin terimi olan tamsaylarn karlkl
bitlerini "veya" ilemine sokar. Bitsel veya ilecine ilikin ilem tablosu aada veriliyor:
Bitsel veya ileci, terimlerinin karlkl bitlerinin "veya" ilemine sokulmasyla elde edilen
deeri retir:
431
#include <stdio.h>
int main()
{
unsigned int x = 0x1BC5;
unsigned int y = 0X3A0D;
unsigned int z = x | y;
printf("z = %X\n", z);
}
/*
/*
/*
/*
x
y
z
z
=
=
=
=
return 0;
0 biti bitsel veya ileminde etkisiz elemandr. 1 biti bitsel ve ileminde yutan elemandr.
Aadaki programda bitsel ve, zel veya, veya ilelerinin rettikleri deerler ikilik say
sisteminde ekrana yazdrlyor. Program derleyerek altrn:
#include <stdio.h>
#include <stdlib.h>
void bit_print(int val)
{
char bits[sizeof(val) * 8 + 1];
itoa(val, bits, 2);
printf("%10d %032s\n", val, bits);
}
int main()
{
int x, y;
printf("iki sayi giriniz ");
scanf("%d%d", &x, &y);
bit_print(x);
bit_print(y);
printf("bitsel ve islemi\n");
bit_print(x & y);
printf("*********************************************************\n");
printf("bitsel ozel veya islemi\n");
bit_print(x ^ y);
printf("*********************************************************\n");
printf("bitsel veya islemi\n");
bit_print(x | y);
return 0;
}
Bitsel ileler ksa devre davranna sahip deidir. Yani bu ilelerin her iki terimi de
mutlaka ilenir.
Bitsel deil ilecinin dnda, tm bitsel ilelere ilikin ilemli atama ileleri vardr. Daha
nce de sylendii gibi bitsel ilelerin yan etkileri (side effect) yoktur. Bitsel ileler
terimleri olan nesnelerin bellekteki deerlerini deitirmez. Eer terimleri olan nesnelerin
deerlerinin deitirilmesi isteniyorsa bu durumda ilemli atama ileleri kullanlabilir:
432
x
x
x
x
x
=
=
=
=
=
x
x
x
x
x
<< y
>> y
& y
^ y
| y
yerine
yerine
yerine
yerine
yerine
x
x
x
x
x
<<= y
>>= y
&= y
^= y
|= y
kullanlabilir.
Bitsel zel veya ilemli atama ileci, tamsay trlerinden iki deikenin deerlerinin, geici
bir deiken olmakszn takas (swap) edilmesinde de kullanlabilir:
#include <stdio.h>
int main()
{
int x, y;
printf("iki sayi giriniz ");
scanf("%d%d", &x, &y);
printf("x = %d\ny = %d\n", x, y);
x ^= y ^= x ^= y;
printf("x = %d\ny = %d\n", x, y);
}
return 0;
Buna tamsaynn belirli bir bitinin "set edilmesi" de denebilir. Bir tamsaynn belirli bir
bitini birlemek iin, sz konusu tamsay, ilgili biti 1 olan ve dier bitleri 0 olan bir sayyla
"bitsel veya" ilemine sokulmaldr. nk bitsel veya ileminde 1 yutan eleman 0 ise
etkisiz elemandr.
Aadaki rnekte bir saynn 5. biti birleniyor:
#include <stdio.h>
int main()
{
int ch = 0x0041;
int mask = 0x0020;
ch |= mask;
printf("ch = %d\n", ch);
*/
return 0;
}
x bir tamsay k da bu saynn herhangi bir bit numaras olmak zere bir tamsaynn k.
bitini birleyecek bir ifade u biimde yazlabilir:
x |= 1 << k
433
Byle ilemleri gerekletirmek iin kullanlan 1 << k gibi ifadelere "bitsel maske"
(bitmask) ya da yalnzca "maske" denir.
Bir tamsaynn belirli bir bitini sfrlamak(clear) iin, sz konusu tamsay, ilgili biti 0 olan
ve dier bitleri 1 olan bir maskeyle "bitsel ve" ilemine sokulmaldr. nk "bitsel ve"
ileminde, 0 yutan eleman 1 ise etkisiz elemandr. Aadaki rnekte bir tamsaynn 5.
biti sfrlanyor:
#include <stdio.h>
int main()
{
int ch = 0x0061;
int mask = ~0x0020;
ch &= mask;
printf("ch = %d\n", ch);
/*
/*
/*
/*
ch =
mask
ch =
ch =
return 0;
}
x, bir tamsay, k da bu saynn herhangi bir bit numaras olmak zere, bir saynn k. bitini
sfrlayan bir ifade aadaki gibi genelletirilebilir:
x
return 0;
434
Baz uygulamalarda bir tamsaynn belirli bir bitinin deerinin ters evrilmesi (toggle flip) gerekir. Yani sz konusu bit 1 ise 0, 0 ise 1 yaplmaldr. Bu amala "bitsel zel veya"
ileci kullanlr. Bitsel zel veya ilecinde 0 biti etkisiz elemandr. Bir saynn k. bitinin
deerini deitirmek iin, say, k. biti 1 dier bitleri 0 olan bir maske ile "bitsel zel veya"
ilemine sokulur.
x, bir tamsay, k da bu saynn herhangi bir bit numaras olmak zere, bir saynn k. bitini
ters eviren bir ifade u ekilde yazlabilir:
x ^= 1 << k;
Bir tamsaynn belirli bitlerini sfrlamak iin ne yaplabilir? rnein int trden bir
tamsaynn 7., 8. ve 9. bitlerini, dier bitlerin deerlerini deitirmeden sfrlamak
isteyelim. Bunu gerekletirmek iin, tamsay 7., 8. ve 9. bitleri 0 olan dier bitleri 1 olan
bir maske ile bitsel ve ilemine sokulabilir. rnein int trnn 16 bit olduu bir sistemde
bu say aadaki bit dzenine sahip olur:
1111 1100 0111 1111
Bu deerin onaltlk say sisteminde gsterimi 0xFC7F biimindedir, deil mi?
x &= 0xFC7F;
Tamsaylarn belirli bitleri zerinde yaplan ileri, ilevlere yaptrmaya ne dersiniz?
void clearbits(int *ptr, size_t startbit, size_t nbits);
clearbits ilevi adresi gnderilen ifadenin startbit nolu bitinden balayarak nbits tane bitini
sfrlar.
rnein x isimli unsigned int trden bir deikenin 7. 8. 9. bitlerini sfrlamak iin ilev
aadaki biimde arlr:
clearbits(&x, 7, 3);
void clearbits(unsigned int *ptr, size_t startbit, size_t nbits)
{
size_t k;
for (k = 0; k < nbits; ++k)
*ptr &= ~(1 << nbits + k);
}
Benzer biimde setbits ilevi de yazlabilir. x isimli unsigned int trden bir deikenin 7.
8. 9. bitlerini birlemek iin ilev aadaki biimde arlr:
clearbits(&x, 7, 3);
void setbits(unsigned int *ptr, size_t startbit, size_t nbits)
{
size_t k;
for (k = 0; k < nbits; ++k)
*ptr |= ~(1 << nbits + k);
}
Peki rnein 32 bitlik bir alan birden fazla deeri tutacak ekilde kullanlabilir mi?
435
Aada int trden bir deeri, ekrana ikilik say sisteminde yazdran showbits isimli bir
ilev tanmlanyor:
#include <stdio.h>
void showbits(int x)
{
int i = sizeof(int) * 8 - 1;
for (; i >= 0; --i)
putchar (x >> i & 1 ? '1' : '0');
}
int main()
{
int val;
Aada ayn ii deiik bir biimde yapan showbits2 isimli bir ilevin tanm yer alyor:
436
void showbits2(int x)
{
unsigned int i = (~((unsigned)~0 >> 1));
while (i) {
putchar (x & i ? '1' : '0');
i >>= 1;
}
Aada tammlanan reverse_bits isimli ilev, int trden bir deerin bitlerini ters eviriyor:
#include <stdio.h>
int reverse_bits(int number)
{
int k;
int no_of_bits = sizeof(int) * 8;
int rev_num = 0;
for (k = 0; k < no_of_bits; ++k)
if (number & 1 << k)
rev_num |= 1 << (no_of_bits - 1 - k);
return rev_num;
}
Aada tammlanan reverse isimli ilev, unsigned char trden bir deerin bitlerini ters
eviriyor:
#include <stdio.h>
unsigned char reverse(unsigned char byte)
{
unsigned char dest;
dest = (byte << 4) | (byte >> 4);
dest = ((dest << 2) & 0xCC) | ((dest >> 2) & 0x33);
}
Aada tanmlanan no_of_setbits isimli ilev, kendisine gnderilen int trden bir deerin
ka tane bitinin 1 olduu bilgisi ile geri dnyor:
#include <stdio.h>
int no_of_setbits(unsigned int value)
{
int counter = 0;
int k;
for (k = 0; k < sizeof(int) * 8; ++k)
if (value & 1<<k)
counter++;
return counter;
}
int main()
437
int number;
printf("bir say girin : ");
scanf("%d", &number);
printf("saynzn %d biti 1n", no_of_setbits(number));
return 0;
return counter;
Yukarda tannmlanan ilevde yer alan for dngs iinde, dngnn her turunda value
deikeninin dk anlaml 4 bitindeki birlenmi bitlerin says, 4 bitin saysal deerinin
bitcounts isimli diziye indis yaplmasyla elde ediliyor. rnein 4 bitlik alanda ifade edilen
tamsaynn deeri 11 olsun:
11 = 1011
Bu saynn toplam 3 biti 1'dir.
bitcounts dizisinin 11 indisli elemann deeri 3'tr. Dngnn bir sonraki turuna
gemeden nce value deikeni saa 4 kez kaydrlarak bu kez value'nun bir sonraki 4
bitlik alanndaki bitlere baklyor.
Aada ilem hzn daha da artran bir ilev tanmlanyor:
const
0, 1,
1, 2,
1, 2,
2, 3,
1, 2,
2, 3,
2, 3,
3, 4,
1, 2,
2, 3,
2, 3,
3, 4,
2, 3,
3, 4,
3, 4,
4, 5,
};
static char
1, 2, 1, 2,
2, 3, 2, 3,
2, 3, 2, 3,
3, 4, 3, 4,
2, 3, 2, 3,
3, 4, 3, 4,
3, 4, 3, 4,
4, 5, 4, 5,
2, 3, 2, 3,
3, 4, 3, 4,
3, 4, 3, 4,
4, 5, 4, 5,
3, 4, 3, 4,
4, 5, 4, 5,
4, 5, 4, 5,
5, 6, 5, 6,
bit_array[]
2, 3, 1, 2,
3, 4, 2, 3,
3, 4, 2, 3,
4, 5, 3, 4,
3, 4, 2, 3,
4, 5, 3, 4,
4, 5, 3, 4,
5, 6, 4, 5,
3, 4, 2, 3,
4, 5, 3, 4,
4, 5, 3, 4,
5, 6, 4, 5,
4, 5, 3, 4,
5, 6, 4, 5,
5, 6, 4, 5,
6, 7, 5, 6,
= {
2, 3,
3, 4,
3, 4,
4, 5,
3, 4,
4, 5,
4, 5,
5, 6,
3, 4,
4, 5,
4, 5,
5, 6,
4, 5,
5, 6,
5, 6,
6, 7,
2,
3,
3,
4,
3,
4,
4,
5,
3,
4,
4,
5,
4,
5,
5,
6,
3,
4,
4,
5,
4,
5,
5,
6,
4,
5,
5,
6,
5,
6,
6,
7,
3,
4,
4,
5,
4,
5,
5,
6,
4,
5,
5,
6,
5,
6,
6,
7,
4,
5,
5,
6,
5,
6,
6,
7,
5,
6,
6,
7,
6,
7,
7,
8
438
count1bits ilevi 32 bitlik bir tamsaynn deeri 1 olan bitlerini sayyor. lev bu ii
yaparken 8 bitlik bir gruptaki 1 olan bitlerin saysn bit_array dizisini kullanarak buluyor.
Aada tanmlanan gcd_b ileviyle, iki tamsaynn ortak blenlerinden en by
hesaplanyor. levin tanm iinde kalan ilecinin kullanlmadna, bitsel ilelerin
kullanldna dikkat edin:
unsigned int gcd_b(unsigned int x, unsigned int y)
{
unsigned int temp;
unsigned int cpof = 0;
if (x == 0)
return y;
if (y == 0)
return x;
while (((x | y) & 1) == 0) {
x >>= 1;
y >>= 1;
++cpof;
}
while ((x & 1) == 0)
x >>= 1;
while (y) {
while (!(y & 1))
y >>= 1;
temp = y;
if (x > y)
y = x - y;
else
y -= x;
x = temp;
}
return x << cpof;
439
BT ALANLARI
Bir yapnn eleman bit seviyesinde tutulan bir tamsay deiken olabilir. Yaplarn byle
elemanlarna "bit alan" (bit fields) denir.
Bit alanlar, C'nin bit seviyesinde eriime olanak veren bir aracdr. Bit seviyesinde eriim
amacyla phesiz daha nceki konuda anlatlan bitsel ileler kullanlabilir. Ancak bit
alanlar programcya byle ulamlar iin daha kolay bir arayz sunar.
Bir yap iinde bir bit alan oluturmak iin zel bir szdizim kuralna uymak gerekir.
Elemann yap iindeki bildiriminde eleman ismini ':' atomu izler. Bu atomdan sonra bit
alannn ka bit yer kaplayaca bilgisini gsteren bir tamsay deimezi yazlr. Aadaki
bildirimi inceleyin:
struct Data {
unsigned int a: 5;
unsigned int b: 4;
unsigned int c: 7;
};
struct Data isimli yapnn a isimli eleman, yap iinde 5 bit yer kaplarken, b isimli eleman
4 bit, c isimli eleman 7 bit yer kaplar. Yapnn elemanlar unsigned int trnden olduuna
gre bu trden bir nesnenin
a eleman 0 - 31
b eleman 0 -15
c eleman 0 - 127
arasnda tamsay deerler tutabilir.
Bir bit alan eleman, iaretli ya da iaretsiz int trlerden olabilir. Bir bit alan eleman
gerek say trlerinden olamaz. C standartlar bit alan elemanlarnn char, short, long
trlerinden olamayacan belirtse de C derleyicilerin ou seimlik olarak bu duruma izin
verir. Tanabilirlik asndan bit alan elemann bildiriminde signed ya da unsigned
szcklerinin kullanlmas yerinde olur. nk derleyici yalnzca int anahtar szcyle
bildirilen bir bit alannn trn iaretli ya da iaretsiz int tr olarak alabilir.
Bit alan eleman bir dizi olamaz.
441
struct file_date
unsigned int
unsigned int
unsigned int
};
{
day: 5;
mon: 4;
year: 7;
Yukardaki bildirimden de grld gibi, tarih bilgisinin yl iin 7 bitlik bir alan
ayrlmtr. 7 bitlik bir alanda tutulabilecek en byk deer 127 dir. DOS iletim
sisteminde byle bir bit alannda yl deerinin 1980 fazlas tutulur.
Aadaki ilev file_date yaps iinde tutulan tarih bilgisini ekrana yazdryor:
void display_file_date(const file_date *ptr)
{
printf("%02u/%02u/%u", ptr->day, ptr->mon, ptr->year);
}
Bir yapnn bit alan elemanlarna, yine nokta ya da ok ileleriyle eriilir. phesiz,
derleyicinin rettii kod bitsel ilemlerin yaplmasn salar.
Bu kez zaman bilgisini tutmak iin elemanlar bit alanlar olan bir yap bildiriliyor:
struct file_time
unsigned int
unsigned int
unsigned int
};
{
hour: 5;
min: 6;
sec: 5;
Zaman bilgisinin saniye deeri iin 5 bitlik bir alan ayrlyor. 5 bitlik bir alanda
tutulabilecek en byk deer 31'dir. DOS iletim sisteminde, byle bir bit alannda gerek
saniye deerinin yars tutulur.
Aadaki ilev file_time yaps iinde tutulan zaman bilgisini ekrana yazdryor:
void display_file_date(const file_time *ptr)
{
printf("%02u:%02u%u", ptr->hour, ptr->min, 2 * ptr->sec);
}
Bir bitalan elemann adresi alnamaz. Bir bit alan elemann adres ilecinin terimi olmas
geersizdir:
#include <stdio.h>
void func();
{
struct file_date fdate;
scanf("%d", &fdate.day);
/***/
/* Geersiz */
Bir bit alannn bellekte yerleimi konusunda derleyiciler geni lde serbest
braklmtr. Bit alanlarnn bellekte yerleimi "saklama birimi" (storage unit) isimli bir
terimle aklanr. Saklama birimi, belirli bir byte uzunluudur ve derleyicinin seimine
baldr. rnein derleyicinin setii saklama birimi 1, 2, 4 vs. byte olabilir.
Bit alanlarnn bildirimiyle karlaan derleyici bit alanlarn, aralarnda boluk brakmadan
tek bir saklama birimine yerletirmeye altrr. Saklama biriminde bir bit alan iinde
yeteri kadar yer kalmaz ise bir sonraki saklama birimine geilir. Smayan elemann her
442
iki saklama biriminde mi tutulaca yoksa tamamen yeni saklama biriminde mi yer
alaca derleyicinin seimine braklmtr. Bir elemann ilgili bit alan iinde yerleiminin
soldan saa ya da sadan sola olmas da yine derleyicinin seimine braklmtr.
Bir bit alan uzunluu saklama biriminin kendi uzunluundan daha byk olamaz.
rnein saklama birimi 8 bit ise 9 bitlik bir bit alan eleman kullanlmaz.
Bit alan elemanlarn yerleimi zerinde daha fazla denetimin salanmas iin, elemana
isim vermeme olana da getirilmitir. Bir bit alan elemana isim verilmeyebilir. smi
olmayan bir bit alan eleman ii derleyici yine gerekli yeri ayrr. Bylece programc
kullanaca elemanlarn isel yerleimini kendine gre ayarlayabilir.
typedef struct {
int
: 5;
int hour
: 5;
int min
: 6;
}Time;
Yukardaki rnekte Time yapsnn isim verilmeyen birinci eleman iin, derleyici yine 5 bit
ayrr. Yani yapnn hour isimli eleman 5. bitten balayarak yerletirilir.
Bir baka olanak da, bit alan elemann uzunluunu 0 olarak belirlemektir. Bunun
nceden belirlenmi zel bir anlam vardr. Bir elemann uzunluunun 0 olduunu gren
derleyici bir sonraki elaman yeni bir saklama biriminden balatr:
Aadaki ilgili sistemin saklama birimi uzunluu bit says olarak elde ediliyor:
#include <stdio.h>
#include <limits.h>
typedef struct {
int : 1;
int : 0;
int : 1;
}StorageCheck;
int main()
{
printf("Saklama birimi = %u bit\n", sizeof(StorageCheck) / 2 *
CHAR_BIT);
}
return 0;
Yukardaki rnekte StorageCheck isimli yap tanmlanyor. Bu yapnn isim verilmeyen ilk
bitalan eleman iin 1 bit yer ayrldn gryorsunuz. kinci eleman iin ise uzunluk 0
olarak belirleniyor. Son elemann uzunluu yine 1 bitdir. Derleyici 3. eleman iin gereken
yeri bir sonraki saklama biriminden ayrr. Bu durumda eer saklama birimi 8 bit ise
yapnn toplam uzunluu 16 bit, saklama birimi 16 bit ise yapnn uzunluu 32 bit nihayet
saklama birimi 32 bit ise yapnn uzunluu 64 bit olur.
443
445
return 0;
446
NAME_LEN
80
if (argc != 3) {
printf("kaynak dosyanin ismini giriniz : ");
gets(source_name);
printf("kopya dosyanin ismini giriniz : ");
gets(dest_name);
}
else {
strcpy(source_name, argv[1]);
strcpy(dest_name, argv[2]);
}
/***/
return 0;
DOS iletim sisteminde olduu gibi, baz sistemlerde main ilevi nc bir parametre
alabilir. nc parametre sistemin evre deikenlerine ilikin bir karakter trnden
gstericiyi gsteren gstericidir.
int main(int argc, char *argv[], char *env[])
{
/***/
}
447
return 0;
Aada, komut satrndan altrlacak basit bir hesap makinesi programnn kodu
veriliyor:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
int main(int argc, char *argv[])
{
char ch;
int op1, op2;
if (argc != 4) {
printf("usage : cal Op1 Operator Op2\n");
exit(EXIT_FAILURE);
}
op1 = atoi(argv[1]);
op2 = atoi(argv[3]);
ch = argv[2][0];
printf(" = ");
switch (ch) {
case '+' :
case '-' :
case '/' :
case '*' :
case '%' :
case 'k' :
default :
}
return 0;
Aadaki program, komut satrndan gn, ay ve yl deerleri girilen bir tarihin haftann
hangi gnne geldiini ekrana yazdryor:
448
#include <stdio.h>
#include <stdlib.h>
char *days[] = {"Pazar", "Pazartesi", "Sali", "Carsamba", "Persembe",
"Cuma", "Cumartesi"};
int day_of_week(int d, int m, int y)
{
static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4,6, 2, 4};
y -= m < 3;
return (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7;
}
int main(int argc, char **argv)
{
int day, mon, year;
if (argc != 4) {
printf("gun ay ve yil degerini giriniz : ");
scanf("%d%d%d", &day, &mon, &year);
}
else {
day = atoi(argv[1]);
mon = atoi(argv[2]);
year = atoi(argv[3]);
}
printf("%s\n", days[day_of_week(day, mon, year)]);
return 0;
}
449
DOSYALAR
kincil belleklerde tanmlanm blgelere dosya denir. Dosya ilemleri tamamen iletim
sisteminin kontrol altndadr. Her dosyann bir ismi vardr. Ancak dosyalarn
isimlendirme kurallar sistemden sisteme gre deiebilir.
letim sistemi de bir programdr. Bu program da ayr ayr yazlm ilevlerin birbirlerini
armas biiminde alr. rnein komut satrnda bir programn isminin yazlarak
altrlmas aslnda birka sistem ilevinin arlmas ile yaplr. Komut satrndan yazlan
yazy alan, diskte bir dosyay arayan, bir dosyay bellee ykleyen, bellekteki program
altran ilevler dzenli olarak arlr.
letim sisteminin almas srasnda kendisinin de ard, sistem programcsnn da
dardan arabildii iletim sistemine ait ilevlere sistem ilevleri denir. Bu tr ilevlere
Windows sisteminde API (Application Programming Interface) ilevleri, UNIX iletim
sisteminde ise sistem arlar (system calls) denir.
Aslnda btn dosya ilemleri, hangi programlama dili ile allrsa allsn, iletim
sisteminin sistem ilevleri tarafndan yaplr. Sistem ilevlerinin isimleri ve parametrik
yaplar sistemden sisteme deiebilir.
Dosyann Almas
Bir dosya zerinde ilem yapmadan nce dosya almaldr. Dosya aabilmek iin iletim
sisteminin "dosya a" isimli bir sistem ilevi kullanlr. Dosyann almas srasnda dosya
ile ilgili eitli ilk ilemler iletim sistemi tarafndan yaplr.
Bir dosya aldnda, dosya bilgileri, ismine "Dosya Tablosu" (File table) denilen ve
iletim sisteminin iinde bulunan bir tabloya yazlr. Dosya tablosunun biimi sistemden
sisteme deiebilir. rnein tipik bir dosya tablosu aadaki gibi olabilir:
Dosya tablosu
Sra No
0
1
...
12
...
Dosya ismi
Dierleri
AUTOEXEC.BAT
...
...
...
Dosyann Kapatlmas
Dosyann kapatlmas almas srasnda yaplan ilemlerin geri alnmasn salar. rnein
dosyann kapatlmas srasnda, iletim sisteminin dosya tablosunda bulunan bu dosyaya
ilikin bilgiler silinir. Alan her dosya kapatlmaldr. Bir dosyann kapatlmamas eitli
sorunlara yol aabilir.
letim sistemlerinin dosyaya n byte veri yazan ve dosyadan n byte veri okuyan sistem
ilevleri vardr. Yazma ve okuma ilemleri bu ilevler kullanlarak yaplr.
451
fopen levi
FILE *fopen (const char *fname, const char *mode);
levin birinci parametresi alacak dosyann ismidir. kinci parametre a modu bilgisidir.
Her iki bilgi de bir yaz olarak ileve iletilir. Dosya ismi yol bilgisi de ierebilir. Dizin
geileri iin '/' karakteri de kullanlabilir. Bir dosya belirli modlarda alabilir. A modu
bilgisi, alacak dosya ile ilgili olarak hangi ilemlerin yaplabileceini belirler. Yine a
modu bilgisi, almak istenen dosyann var olup olmamas durumunda ilevin nasl
davranacan belirler. Aadaki tabloda, fopen ilevine a modu bilgisini iletmek zere
geilmesi gereken yazlar listelenmitir:
Mod
"w"
"w+"
"r"
"r+"
"a"
"a+"
Anlam
Dosya yazmak iin alr. Dosyadan okuma yaplamaz. Dosyann var olmas
zorunlu deildir. Dosya yok ise verilen isimde bir dosya yaratlr. Dosya var ise
dosya sfrlanr.
Var olan bir dosyay bu modda amak dosyann kaybedilmesine neden olur.
Dosyay hem yazma ve okuma iin aar. Dosyann var olmas zorunlu deildir.
Dosya yok ise veriln isimde bir dosya yaratlr. Dosya var ise sfrlanr.
Var olan bir dosyay bu modda amak dosyann kaybedilmesine neden olur.
Dosya okumak iin alr. Dosyaya yazlamaz. Dosya yok ise alamaz.
Dosya hem okuma hem yazma iin alr. Dosya yok ise alamaz.
Dosya sona ekleme iin alr. Dosyadan okuma yaplamaz. Dosyann var
olmas zorunlu deildir. Dosya yok ise verilen isimde bir dosya yaratlr. Dosya
var ise dosya sfrlanmaz.
Dosyay sonuna ekleme ve dosyadan okuma iin aar. Dosyann var olmas
zorunlu deildir. Dosya yok ise verilen isimde bir dosya yaratlr. Dosya var ise
sfrlanmaz.
452
levin geri dn deeri FILE yaps trnden bir adrestir. lev, alan dosyaya ilikin
birtakm bilgileri FILE yaps trnden bir nesnenin elemanlarnda saklar ve bu nesnenin
adresini geri dndrr. levin geri dn deerine ilikin FILE yaps stdio.h balk
dosyas iinde bildirilmitir. Bu yapnn elemanlar standart deildir. Sistemden sisteme
deiiklik gsterebilir. Zaten programc bu yapnn elemanlarna gereksinim duymaz.
fopen ilevi iletim sisteminin dosya a sistem ilevini ararak dosyay aar ve dosyaya
ilikin baz bilgileri FILE yaps trnden bir nesnenin elemanlarna yazarak bu nesnenin
balang adresini geri dndrr. rnein "file handle" deeri de bu yapnn ierisindedir.
Tabi fopen ilevinin geri verdii FILE trnden adres gvenli bir adrestir. Dosya eitli
sebeplerden dolay alamayabilir. Bu durumda fopen ilevi NULL adresine geri dner.
levin geri dn deeri kesinlikle kontrol edilmelidir. Tipik bir snama ilemi aadaki
gibi yaplabilir:
/*..*/
FILE *f;
if ((f = fopen("mektup.txt", "r")) == NULL) {
printf("cannot open file"...\n);
exit(EXIT_FAILURE);
}
Yukardaki rnekte ismi mektup.txt olan bir dosya okuma amacyla almaya allyor.
Dosya almaz ise ekrana bir hata iletisi verilerek, standart exit ilevi ile program
sonlandrlyor. Yukardaki kod parasnn atama ilecinin rettii deerin nesneye atanan
deer olmasndan faydalandn gryorsunuz. phesiz fopen ilevi ile almak istenen
bir dosyann alamamas durumunda programn sonlandrlmas zorunlu deildir. Ancak
bundan sonra verilecek kod rneklerinde, bir dosyann almamas durumunda imdilik
program sonlandrlacak.
Dosya ismi dosyann yeri hakknda src, yol gibi bilgi ierebilir. Dosya ismi bir dizge ile
veriliyorsa yol bilgisi verirken dikkatli olmak gerekir. Yol bilgisi '\' (ters bl) karakteri
ierebilir. dizge iinde '\' karakterinin kullanlmas, '\ 'karakterininin onu izleyen
karakterle birlikte, nceden belirlenmi ters bl karakter deimezi (escape sequence)
olarak yorumlanmasna yol aabilir. rnein :
fopen("C:\source\new.dat", "r");
Yukardaki ilev arsnda derleyici '\n' karakterini "newline" karakteri olarak yorumlar
'\s' karakterini ise "undefined" kabul eder. Bu sorundan saknmak '\' karakteri yerine
'\\' kullanlmasyla mmkn olur:
fopen("C:\\source\\new.dat", "r");
Sona ekleme modlar ("a", "a+") ok kullanlan modlar deildir. Dosyaya yazma
durumunda "w" modu ile "a" modu arasnda farkllk vardr. "w" modunda dosyada olan
byte n zerine yazlabilir. "a" modunda ise dosya ierii korunarak sadece dosyann
sonuna yazma ilemi yaplabilir.
Bir dosyann hem okuma hem de yazma amacyla almas durumunda yani a modunu
belirten dizgede '+' karakterinin kullanlmas durumunda dikkatli olmak gerekir. Okuma
ve yazma ilemleri arasnda mutlaka ya dosya konum gstericisinin konumlandrlmas
(mesela fseek ilevi ile) ya da dosyaya iliken tampon bellek alannn (buffer) boaltlmas
gerekir. Bu konuya ileride ayrntl bir ekilde deinilecek.
Bir dosyann alp alamayaca aadaki kk programla snanabilir:
Program komut satrndan
453
canopen dosya.dat
eklinde altrldnda ekrana "dosya.dat dosyas alabilir" ya da "dosya.dat dosyas
alamaz" yazar.
#include <stdio.h>
int main(int argc, char *argv[])
{
FILE *fp;
if (argc != 2) {
printf("usage: canopen filename\n");
return 1;
}
if ((fp = fopen(argv[1], "r")) == NULL) {
printf("%s dosyas alamaz\n", argv[1]);
return 2;
}
printf("%s dosyas alabilir\n", argv[1]);
fclose(fp);
return 0;
}
stdio.h balk dosyas iinde FOPEN_MAX isimli bir simgesel deimez tanmlanyor.
FOPEN_MAX simgesel deikeni ayn zamanda alabilecek en byk dosya saysdr.
fclose levi
EOF
(-1)
levin baars ancak phe altnda snanmaldr. Normal koullar altnda dosyann
kapatlmamas iin bir neden yoktur.
#include <stdio.h>
int main()
{
FILE *f;
if ((f = fopen("mektup.txt", "w")) == NULL) {
printf("mektup.txt dosyas yaratlamyor!\n");
exit(EXIT_FAILURE);
}
fclose(f);
printf("mektup.txt dosyasi kapatildi!\n");
return 0;
}
454
Derleyicilerin ounda bulunmasna karlk standart olmayan fcloseall isimli bir ilev de
vardr:
int fcloseall(void);
Bu ilev arldnda ak olan dosyalarn hepsi kapatlr. levin geri dn deeri,
kapatlan dosya saysdr.
imdi de alm bir dosyadan okuma yapan ya da alm bir dosyaya yazma yapan
standart C ilevlerini inceleyelim:
fgetc levi
int fgetc(FILE *f);
C'nin standart yazma ve okuma yapan ilevleri yazlan ve okunan ofset says kadar
dosya konum gstericisini ilerletirler. fgetc ilevi dosya gstericisinin gsterdii yerdeki
byte okur ve bu byte n tamsay deerini geri dn deeri olarak verir. lev baarsz
olursa, yani okuma ilemi yaplamaz ise stdio.h dosyas iinde simgesel deimez olarak
tanmlanm EOF deerine geri dner.
fgetc ilevinin geri dn deerini char trden bir deikene atamak yanl sonu
verebilir, bu konuda dikkatli olunmal ve ilevin geri dn deeri int trden bir
deikende saklanmaldr.
char ch;
ch = fgetc(fp);
Yukarda dosyadan okunan karakterin 255 numaral ASCII karakteri (0x00FF) olduunu
dnelim. Bu say char trden bir deikene atandnda yksek anlaml byte
kaybedilerek ch deikenine 0xFF deeri atanr. Bu durumda ch deikeni iaretli char
trden olduundan ch deikeni iinde negatif bir tamsaynn tutulduu anlam kar.
if (ch == EOF)
gibi bir karlatrma deyiminde, if ayrac iindeki karlatrma ileminin yaplabilmesi iin
otomatik tr dnm yaplr, yani ch tam sayya ykseltilir (integral promotion). Bu
otomatik tr dnmnde iaretli int trne evrilecek ch deikeni negatif olduu iin
FF byte' ile beslenir. Bu durumda eitlik karlatrmas doru sonu verir, yani dosyann
sonuna gelindii (ya da baka nedenden dolay okumann yaplamad) yorumu yaplr.
Oysa ch deikeni int trden olsayd, ch deikenine atanan deer 0x00FF olurdu. Bu
durumda karlatrma yapldnda ch deikeni ile EOF deerinin (0xFFFF) eit olmad
sonucuna varlrd.
fgetc ilevi kullanlarak okuma amacyla alm bir dosya karakter karakter okunabilir.
Aadaki programda klavyeden isimi alnan bir dosyann ierii ekrana yazdrlyor:
455
#include <stdio.h>
#include <stdlib.h>
#define
FILENAME_LEN
256
int main()
{
FILE *f;
char file_name[FILENAME_LEN];
int ch;
printf("Yazdirilacak dosyann ismi : ");
gets(file_name);
if ((f = fopen(file_name, "r")) == NULL) {
printf("cannot open file...\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(f)) != EOF)
putchar(ch);
fclose(f);
return 0;
}
Yukardaki kodu inceleyin. nce yazdrlacak dosyann ismi klavyeden alnarak file_name
isimli diziye yerletiriliyor. smi file_name dizisine alnan dosya fopen ilevine yaplan
ar ile okuma amacyla alyor. fopen ilevinin baarsz olmas durumunda program
sonlandrlyor.
while ((ch = fgetc(f)) != EOF)
putchar(ch);
dng deyimiyle, fgetc ilevi EOF deerini dndrnceye kadar dosyadan bir karakter
okunuyor ve okunan karakterin grnts standart putchar ileviyle ekrana yazdrlyor.
Okuma ilemi tamamlannca standart fclose ileviyle dosya kapatlyor.
imdi de komut satrndan altrlacak aadaki program inceleyin. lev komut
satrndan
<say> <dosya ismi> <karakter>
biiminde altrlr. Program ekrana ismi verilen dosyada nc komut satr argman
olarak verilen karakterden ka tane bulunduu bilgisini yazar. Program nce inceleyin
daha sonra derleyerek altrn:
456
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define
FILENAME_LEN
256
return 0;
Son olarak unu da ekleyelim. fgetc bir ilevdir. Ancak stdio.h balk dosyas iinde getc
isimli bir de makro tanmlanmtr. Derleyicilerin ou getc makrosuyla ayn isimli bir de
ilev tanmlar. Ancak fgetc bir makro deil ilevdir. Makrolar konusuna ilerdeki
blmlerde deinilecek.
fputc levi
457
biiminde arlrsa f adresi int trne ve 'A' tamsay deeri de FILE yaps trnden bir
adrese dntrlr. Bu durumda phesiz yazma ilemi baarsz olur.
Aada fgetc ve fputc ilevlerini kullanarak bir dosyann kopyasn kartan bir programn
kodunu gryorsunuz. Program komut satrndan
<kopyala> <kaynak dosya ismi> <hedef dosya ismi>
eklinde altrlr. Programn altrlmasyla ikinci komut satr argmanyla ismi verilen
dosyann nc komut satr argmanyla verilen isimli bir kopyas kartlr. Program
dikkatle inceleyin:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_FILE_NAME_LEN
256
return 0;
458
fputc bir ilevdir. Ancak stdio.h balk dosyas iinde getc isimli bir de makro
tanmlanmtr. Derleyicilerin ou putc makrosuyla ayn isimli bir de ilev tanmlar.
Ancak fgetc bir makro deil ilevdir.
fprintf levi
int fprintf(FILE *, const char *, );
Bu ilev tpk printf ilevi gibidir. Ancak ilk parametresi yazma ileminin hangi dosyaya
yaplacan belirtir. Dier parametreleri printf ilevin de olduu gibidir. printf ilevi
ekrana yazarken, fprintf ilevi birinci parametre deikeninde belirtilen dosyaya yazar.
Aadaki rnei inceleyin:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
int i, ch;
if ((f = fopen("data.txt", "w")) ==NULL) {
printf("cannot open file...\n");
exit(EXIT_FAILURE);
}
printf("data.txt dosyasi yaratld!\n");
for (i = 0; i < 10; ++i)
fprintf(f, "sayi = %d\n", i);
fclose(f);
printf("data.txt dosyas kapatild!\n");
if ((f = fopen("data.txt", "r")) == NULL) {
printf("cannot open file...\n");
exit(EXIT_FAILURE);
}
printf("data.txt dosyasi okuma amacyla ald!\n");
while ((ch = fgetc(f)) != EOF)
putchar(ch);
fclose(f);
}
return 0;
fscanf levi
459
Nasl fprintf ilevi, ekrana yazmak yerine, yazma ilemini birinci parametresiyle belirlenen
dosyaya yapyorsa, fscanf ilevi de, okumay klavyeden yapmak yerine belirtilen bir
dosyadan yapar. fscanf ilevi scanf ilevinden farkl olarak, birinci parametre deikenine
FILE trnden bir adres alr. levin bildirimi aadaki gibidir:
int fscanf(FILE *, const char *, );
levin geri dn deeri, dosyadan okunarak bellekteki alanlara yazlan deer saysdr.
Hibir alana yazma yaplmadysa, ilev 0 deerine geri dner. Eer ilk alana atama
yaplamadan dosyann sonuna gelinmise, ya da bir hata olumusa ilev EOF deerine
geri dner. kinci parametresine geilecek yazda kullanlacak format karakterleri scanf
ilevi ile ayndr. Aadaki program dikkatle inceleyin:
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
SIZE
PASS_GRADE
100
60
----------
4567
gibi bir satrn bulunduunu dnelim. Yaplacak okuma sonucunda yalnzca 1376 ve
4567 deerlerinin x ve y deikenlerine aktarlmas gerektiini dnelim. Bunun iin
aadaki gibi bir ar yaplabilir:
461
fgets levi
char *fgets(char *buf, int n, FILE *f);
Bu ilev dosya konum gstericisinin gsterdii yerden bir satrlk bilgiyi okur. lev
dosyadan '\n' karakterini okuyunca onu da birinci parametresinde verilen adrese yazarak
ilemini sonlandrr.
levin birinci parametresi okunacak bilginin bellekte yerletirilecei yerin adresidir. kinci
parametresi ise okunacak maksimum karakter saysdr. fgets ilevi en fazla n 1
karakteri okur. Okuduu karakterlerin sonuna sonlandrc karakteri ekler ve ilemini
sonlandrr. Eer satr zerindeki karakter says n - 1'den az ise tm satr okur ve
ilemini sonlandrr. rnein bu parametrenin 10 olarak girildiini dnelim. Satr
zerinde 20 karakter olsun. lev 9 karakteri okuyarak diziye yerletirir, sonuna
sonlandrc karakteri ekler. Ancak satr zerinde \n dahil olmak zere 5 karakter olsayd
ilev bu 5 karakteri de okuyarak sonuna da sonlandrc karakteri ekleyerek ilemini
sonlandrrd. levin ikinci parametresine char trden bir dizinin ya da dinamik olarak
elde edilen bir bloun boyutunu gemek tama hatalarn dorudan engeller. Zira fgets
ilevi en fazla, dizinin boyutundan bir eksik sayda karakteri okuyarak diziye yazar,
dizinin son elemanna da sonlandrc karakterin deerini yazar.
levin geri dn deeri, en az 1 karakter okunmu ise birinci parametresi ile belirtilen
adresin ayns, hibir karakter okunmamsa NULL adresidir.
Bir dng iinde fgets ilevi srekli olarak arlarak btn dosya okunabilir.
fgets ilevi ile bir dosyay satr satr ekrana yazdran aadaki program inceleyin:
#include <stdio.h>
#include <stdlib.h>
#define
#define
MAX_FILE_NAME_LEN
BUFFER_SIZE
256
100
int main()
{
FILE *f;
char file_name[MAX_FILE_NAME_LEN];
char buf[BUFFER_SIZE];
printf("Dosya ismi : ");
gets(file_name);
if ((f = fopen(file_name, "r")) == NULL) {
printf("cannot open the file %s\n", file_name);
exit(EXIT_FAILURE);
}
while (fgets(buf, BUFFER_SIZE, f) != NULL)
printf("%s", buf);
fclose(f);
return 0;
}
while (fgets(buf, BUFFER_SIZE, f) != NULL)
printf("%s", buf);
Dngsyle f ile gsterilen dosyadan satr satr okuma yaplarak okunan karakterler buf
dizisine yazlyor. Eer dosyadan okunacak bir karakter kalmadysa fgets ilevi NULL
adresine geri dner.
462
fputs levi
256
100
int main()
{
char source_file_name[FILE_NAME_LEN];
char dest_file_name[FILE_NAME_LEN];
char buffer [BUFFER_SIZE];
FILE *fs, *fd;
printf("kopyalanacak dosya ismi: ");
gets(source_file_name);
printf("kopya dosya ismi: ");
gets(dest_file_name);
fs = fopen(source_file_name, "r");
if (fs == NULL) {
printf("%s dosyasi acilamiyor!\n", source_file_name);
exit(EXIT_FAILURE);
}
fd = fopen(dest_file_name, "w");
if (fd == NULL) {
printf("%s dosyasi yaratilamiyor!\n", dest_file_name);
fclose(fd);
exit(EXIT_FAILURE);
}
while (fgets(buffer, BUFFER_SIZE, fs))
fputs(buffer, fd);
printf("kopyalama basari ile tamamlandi!\n");
fclose(fs);
fclose(fd);
return 0;
}
Bir dosya text modunda ya da binary modda alabilir. Varsaylan (default) a modu
text modudur. Yani dosyann hangi modda ald ak bir ekilde belirtilmezse dosyann
text modunda ald varsaylr. Dosyay binary modda aabilmek iin a mod yazsna
'b' eklemek gerekir. Aada bir dosyay binary modda aabilmek iin fopen ilevine
gnderilebilecek geerli dizgeler veriliyor:
"rb", "r+b", "rb+", "w+b", "wb+","a+b", "ab+"
463
DOS ve WINDOWS gibi baz iletim sistemlerinde bir dosyann text modu ya da binary
modda almas arasnda baz nemli farklar vardr:
DOS iletim sisteminde bir dosya yazdrldnda bir karakter aa satrn banda
grnyorsa bunu salamak iin o karakterden nce CR (carriage return) ve LF (line
feed) karakterlerinin bulunmas gerekir. CR karakteri C'de '\r' ile belirtilir. 13 numaral
ASCII karakteridir. LF karakteri C'de \n ile belirtilir. 10 numaral ASCII karakteridir.
rnein bir dosya yazdrldnda grnt
a
b
eklinde olsun. Dosyadaki durum a\r\nb eklindedir. Oysa UNIX tabanl sistemlerinde
aa satrn bana geebilmek iin sadece LF karakteri kullanlr. UNIX iletim sisteminde
a
b
grntsnn dosya karl
a\nb biimindedir.
DOS iletim sisteminde LF karakteri bulunulan satrn aasna ge CR karakteri ise
bulunulan satrn bana ge anlamndadr. rnein DOS iletim sisteminde bir bir
dosyann ierii a\nb biiminde ise dosya yazdrldnda
a
b
grnts elde edilir. Eer dosyann ierii a\rb biiminde ise dosya yazdrldnda
b
grnts elde edilir.
printf levinda \n ekranda aa satrn bana geme amacyla kullanlr. Aslnda printf
ilevinin 1. parametresi olan dizgenin iine \n yerletirildiinde UNIX'de yalnzca \ni DOS
iletim sisteminde ise \r ve\n ile bu gei salanr.
Text dosyalar ile rahat alabilmek iin dosyalar text ve binary olarak ikiye ayrlmtr.
Bir dosya text modunda aldnda dosyaya \n karakteri yazlmak istendiinde dosya
ilevleri otomatik olarak \r ve \n karakterlerini dosyaya yazar. Benzer bir biimde dosya
text modda almsa dosya gstericisi \r\n iftini gsteriyorsa dosyadan yalnzca /n
karakteri okunur. DOS iletim sisteminde text ve binary dosyalar arasndaki baka bir
fark da, CTRL Z (26 numaral ASCII karakterinin) dosyay sonlandrdnn
varsaylmasdr. Oysa dosya binary modda aldnda byle bir varsaym yaplmaz.
UNIX iletim sisteminde, text modu ile binary mod arasnda hibir fark yoktur. Yani UNIX
iletim sisteminde dosyann binary mod yerine text modunda almasnn bir sakncas
olmaz. Ancak DOS altnda text dosyas olmayan bir dosyann binary mod yerine text
modunda almasnn sakncalar olabilir. rnein DOS altnda bir exe dosyann binary
mod yerine text modunda aldn dnelim. Bu dosyada 10 numaral ve 13 numaral
ASCII karakterleri yanyana bulunduunda dosyadan yalnzca 1 byte okunur. Ayn ekilde
dosyadan 26 numaral ASCII karakteri okunduunda dosyadan artk baka bir okuma
yaplamaz. Dosyann sonuna gelindii varsaylr.
Aadaki program text modu ile binary mod arasndaki fark gsteriyor:
464
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *fp;
int k, ch;
fp = fopen("deneme", "w");
if (fp == NULL) {
printf("dosya alamyor\n");
exit(EXIT_FAILURE);
}
/* dosyaya 5 tane \n karakteri yazdrlyor */
for (k = 0; k < 5; ++k)
fputc('\n', fp);
fclose(fp);
printf("\ndosya binary modda alarak yazdrlyor\n");
fp = fopen("deneme", "rb");
if (fp == NULL) {
printf("dosya alamyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran kts
13 10 13 10 13 10 13 10 13 10
*/
fclose(fp);
printf("\ndosya kapatld. imdi dosya text modunda alarak
yazdrlyor .\n");
fp = fopen("deneme", "r");
if (fp == NULL) {
printf("dosya alamyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran kts
10 10 10 10 10
*/
fclose(fp);
/* simdi '\x1A' karakterinin text modunda dosyay sonlandrmas zellii
snanyor*/
fp = fopen("deneme", "w");
if (fp == NULL) {
printf("dosya alamyor\n");
exit(EXIT_FAILURE);
}
/* dosyaya 5 tane 'A' karakteri yazdrlyor */
for (k = 0; k < 5; ++k)
fputc('A', fp);
/* dosyaya '\x1A' karakteri yazdrlyor */
fputc('\x1a', fp);
/* dosyaya 10 tane 'A' karakteri yazdrlyor. */
for (k = 0; k < 5; ++k)
fputc('A', fp);
fclose(fp);
printf("\ndosya binary modda alarak yazdrlyor :\n");
fp = fopen("deneme", "rb");
465
if (fp == NULL) {
printf("dosya alamyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran kts
65 65 65 65 65 26 65 65 65 65 65
*/
printf("\ndosya kapatld, imdi dosya text modunda alarak
yazdrlyor\n");
fp = fopen("deneme", "r");
if (fp == NULL) {
printf("dosya alamyor\n");
exit(EXIT_FAILURE);
}
while ((ch = fgetc(fp)) != EOF)
printf("%d ", ch);
/* ekran kts
65 65 65 65 65 26 65 65 65 65 65
*/
fclose(fp);
}
return 0;
EOF Durumu
Dosyann sonunda hibir zel karakter yoktur. letim sistemi dosyann sonuna gelinip
gelinmediini dosyann uzunluuna bakarak anlayabilir. EOF (end of file) durumu dosya
konum gstericisinin dosyada olmayan son karakteri gstermesi durumudur. EOF
durumunda dosya konum gstericisinin offset deeri dosya uzunluu ile ayn deerdedir.
EOF durumunda dosyadan okuma yaplmak istenirse dosyadan okuma yapan ilevler
baarsz olur. Ancak a modu uygunsa dosyaya yazlabilir ve bu durumda dosyaya
ekleme yaplr.
Daha nce belirtildii gibi C dilinde alan bir dosya ile ilgili bilgiler FILE trnden bir yap
nesnesi iinde tutulur. Bu yapnn elemanlar dosyann zellikleri hakknda bilgi verir. C
programcs bu yapnn elemanlarnn deerleri ile dorudan ilgilenmez, zira fopen
ilevinin geri dn deeri bu yap nesnesini gsteren FILE yaps trnden bir gstericidir
ve C dilinin dosyalarla ilgili ilem yapan ilevleri ounlukla bu adresi parametre olarak
alarak, istenilen dosya ile ilgili bilgilere ular.
Sz konusu FILE yapsnn elemanlarndan biri de, bayrak olarak kullanlan EOF
bayradr. Aslnda derleyicilerin ounda int trden bir bayran yalnzca belirli bir bitidir.
C dilinin dosyalarla ilgili ilem yapan baz ilevleri EOF bayrann deerini deitirir. Yani
EOF bayran birler ya da sfrlarlar. Okuma yapan ilevler okumadan nce bu bayran
deerine bakar. EOF bayra set edilmise okuma baarl olmaz. Baarl bir okuma
yaplabilmesi iin EOF bayrann yeniden sfrlanmas gerekir. Dosya aan ilevler FILE
yapsndaki EOF bayran sfrlar. Bu ilevler dnda dosya konum gstericisinin deerini
deitiren ilevler (fseek, rewind, fsetpos) ile clearerr ilevleri de EOF bitini sfrlar.
Bir dosyaya int trden deerlerin yazlacan dnelim. Dosyaya int trden bir deer
yazmak ne anlama gelir? rnein int trnn 4 byte olduu bir sistemde, dosyaya
yazlacak tamsay deeri 1234567890 olsun. Bu deer bir dosyaya yazlmak ve o
dosyadan daha sonra geri okunmak istensin.
Bu ilem fprintf ilevi ile yaplabilir, deil mi?
466
Bu iki ilev C'de en ok kullanlan ilevlerdir. Genel olarak dosya ile RAM arasnda aktarm
(transfer) yaparlar. Her iki ilevin de bildirimi ayndr:
size_t fread(void *adr, size_t block_size, size_t n_blocks, FILE *);
size_t fwrite(const void *adr, size_t block_size, size_t n_blocks, FILE *);
size_t trnn sistemlerin hemen hemen hepsinde unsigned int ya da unsigned long
trnn typedef edilmi yeni ismi olduunu anmsayn.
fread ilevi dosya konum gstericisinin gsterdii yerden, ikinci ve nc parametresine
kopyalanan deerlerin arpm kadar byte' , bellekte birinci parametresinin gsterdii
adresten balayarak kopyalar. Genellikle ilevin ikinci parametresi veri yapsnn bir
elemannn uzunluunu, nc parametresi ile para says biiminde girilir. levin geri
dn deeri bellee yazlan ya da bellekten dosyaya yazlan para saysdr.
Bu ilevler sayesinde diziler ve yap nesneleri tek bir ar ile bir dosyaya aktarlabilirler.
rnein 10 elemanl int trden bir dizi aadaki gibi tek bir aryla dosyaya yazlabilir.
int a[5] = {3, 4, 5, 7, 8};
fwrite (a, sizeof(int), 5, f);
Yukardaki rnekte, dizi ismi olan a int trden bir adres bilgisi olduu iin, fwrite ilevine
1. argman olarak gnderilebilir. FILE trnden f gstericisi ile ilikilendirilen dosyaya
bellekteki a adresinden toplam sizeof(int) * 5 byte yazlr.
Aadaki kod parasnda bir yap nesnesi bellekten dosyaya aktarlyor:
467
typedef struct {
char name[20];
char fname[20];
int no;
}Person;
int main()
{
FILE *f;
Person per = {"Necati", "Ergin", 325);
f = fopen("person.dat", "wb");
/*********************************/
fwrite(&per, sizeof(Person), 1, f);
/****/
fwrite ilevi saylar bellekteki grnts ile dosyaya yazar. Yani fprintf ilevi gibi formatl
yazmaz. rnein DOS iletim sisteminde:
int i = 1535;
fwrite(&i, sizeof(int), 1, f);
Burada dosya yazdrlrsa 2 byte uzunluunda rastgele karakterler grnr. nk DOS
iletim sisteminde int tr 2 byte uzunluundadr. Bizim grdmz ise 1525'in rastgele
olan byte'lardr. Bilgileri ASCII karlklar ile dosyaya yazmak iin fprintf ilevi
kullanlabilir.
fread ve fwrite ilevleri bellekteki bilgileri aktardna gre dosyalarn da binary modda
alm olmas uygun olur.
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
int i;
int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int b[10];
if ((f = fopen("data", w+b")) == NULL) {
printf("cannot open file...\n");
exit(EXIT_FAILURE);
}
fwrite (a, sizeof(int), 10, f);
fseek(f, 0, SEEK_SET);
fread(b, sizeof(int), 10, f);
for (i = 0; i < 10; ++i)
printf("%d\n", b[i]);
fclose(f);
return 0;
}
fread ve fwrite ilevlerinin geri dn deerleri nc parametresi ile belirtilen okunan
ya da yazlan para saysdr. rnein
n = fread(a, sizeof(int), 10, f);
468
ars ile f ile gsterilen dosyadan sizeof(int) * 10 kadar byte okunarak RAM'da a
adresine yazlmak istenmitir. Yani dosya konum gstericisinin gsterdii yerden itibaren
btn saylar okunabildiyse ilev 10 deerine geri dner. Eer dosyadaki kalan byte
says okunmak istenen saydan az ise ilev btn byte'lar okur ve geri dn deeri
okunan byte says 2. parametresi ile belirtilen deer olur. rnein DOS iletim sistemi
altnda alyor olalm. Dosyada 10 byte bilgi kalm olsun.
n = fread(a, sizeof(int), 10, f);
ile ilev 5 deerine geri dner.
Aadaki iki ary inceleyelim:
fread(str, 100, 1, f);
fread(str, 1, 100, f);
Her iki ilev ars da RAM'deki str adresine FILE trnden f gstericisi ile ilikilendirilen
dosyadan 100 byte okumak amacyla kullanlabilir. Ancak birinci arda geri dn
deeri ya 0 ya 1 olabilecekken, ikinci ilev arsnda geri dn deeri 0 100(dahil)
aralnda herhangi bir deer olabilir.
Aadaki rnekte bir grup byte fread ilevi ile bir dosyadan okunuyor, fwrite ilevi ile
dier bir dosyaya yazlyor:
469
#include <stdio.h>
#include <stdlib.h>
#define
#define
BLOCK_SIZE
MAX_NAME_LEN
1024
80
int main()
{
FILE *fs, *fd;
char source_file_name[MAX_NAME_LEN];
char dest_file_name[MAX_NAME_LEN];
unsigned int n;
unsigned char buf[BLOCK_SIZE];
printf("kaynak dosya ismini giriniz : ");
gets(source_file_name);
printf("yeni dosya ismini giriniz : ");
gets(dest_file_name);
if ((fs = fopen(source_file_name, "rb")) == NULL) {
printf("%s dosyas alamyor!\n", source_file_name);
exit(EXIT_FAILURE);
}
printf("%s dosyas ald!\n", source_file_name);
if ((fd = fopen(dest_file_name, "wb")) == NULL) {
printf("%s dosyas yaratlamyor!\n", source_file_name);
fclose(fs);
exit(EXIT_FAILURE);
}
printf("%s dosyas yaratlamyor!\n", dest_file_name);
while ((n = fread(buf, 1, BLOCK_SIZE, fs)) != 0)
fwrite(buf, 1, n, fd);
fclose(fs);
printf("%s dosyas kapatld!\n", source_file_name);
fclose(fd);
printf("%s dosyas kapatld!\n", dest_file_name);
printf("kopyalama baaryla tamamland\n");
}
return 0;
!= 0)
fread ilevi ile fs ile gsterilen dosyadan, 1 byte'lk bloklardan BLOCK_SIZE kadar
okunmaya allyor. fread ilevinin geri dn deeri n isimli deikende saklanyor. n
deikenine atanan deer 0 olmad srece dngnn devam salanyor. Baka bir
deyile while dngs dosyadan en az 1 byte okuma yaplabildii srece dnyor. while
dngsnn gvdesinde yer alan fwrite ilevi ars ile kaynak dosyadan okunan n byte
hedef dosyaya yazlyor.
imdi de aadaki program inceleyin. Bu programda komut satrndan girilen iki deer
arasndaki tm asal saylar, ismi komut satrndan ismi girilen dosyaya formatsz olarak
yazlyor. Program komut satrndan aadaki gibi altrlabilir:
<asalyaz> <deger1> <deger2> <dosya ismi>
#include <stdio.h>
#include <stdlib.h>
470
#include <string.h>
int isprime(int val);
int main(int argc, char *argv[])
{
int lower_bound, upper_bound;
int k, temp;
int prime_counter = 0;
FILE *f;
if (argc != 4) {
printf("<asalyaz> <deger1> <deger2> <dosya ismi>\n");
exit(EXIT_FAILURE);
}
lower_bound = atoi(argv[1]);
upper_bound = atoi(argv[2]);
if (lower_bound > upper_bound) {
temp = lower_bound;
lower_bound = upper_bound;
upper_bound = temp;
}
f = fopen(argv[3], "wb");
if (f == NULL) {
printf("%s dosyasi yaratilamiyor!\n", argv[3]);
exit(EXIT_FAILURE);
}
printf("%s dosyasi yaratildi!\n", argv[3]);
for (k = lower_bound; k <= upper_bound; ++k)
if (isprime(k)) {
fwrite(&k, sizeof(int), 1, f);
prime_counter++;
}
printf("%s dosyasina %d adet asal sayi yazildi!\n", argv[3],
prime_counter);
fclose(f);
printf("%s dosyasi kapatildi!\n", argv[3]);
}
return 0;
fseek levi
Bu ilev dosya konum gstericisini istenilen bir offset deerine konumlandrmak amacyla
arlr. Bu ilevin arlmasyla, alm bir dosyann istenilen bir yerinden okuma
yapmak ya da istenilen bir yerine yazmak mmkn hale gelir. levin bildirimi aadaki
gibidir:
int fseek(FILE *f, long offset, int origin);
471
0
1
2
SEEK_SET);
472
ilemi hataldr. Durum alma zamanna ilikin tanmlanmam bir davran (undefined
behaviour) zellii gsterir. Yani alma zamannda herey olabilir. Yazmadan okumaya,
okumadan yazmaya geite dosya gstericisi konumlandrlmaldr. Bu durumun tek
istisnas, son yaplan okuma ile dosyann sonuna gelinmesi durumudur. Bu durumda
konumlandrma yaplmadan dosyann sonuna yazlabilecei gvence altndadr.
rewind levi
Bu ilev ile dosya konum gstericisi dosyann bana konumlandrlr. levin bildirimi
aadaki gibidir:
void rewind(FILE *fp);
rewind(f);
ars ile
(void) fseek(f, 0L, SEEK_SET);
ars ayn anlamdadr.
ftell levi
Bu ilev dosya konum gstericisinin deerini elde etmek iin arlr. levin bildirimi
aadaki gibidir:
long ftell(FILE *);
levin geri dn deeri dosya konum gstericisinin deeridir. Bu deer dosyann
bandan itibaren kanc byte olduu bilgisi olarak verilir. Bir hata durumunda ilev -1L
deerine geri dner.
kili (binary) bir dosyann uzunluu fseek ve ftell ilevlerine yaplan arlarla elde
edilebilir:
fseek(f, 0, SEEK_END);
length = ftell(f);
473
rename levi
tmpfile levi
tmpnam levi
Geici olarak kullanlacak bir dosya iin bir isim retilmesi amacyla kullanlr. levin
stdio.h balk dosyas iindeki bildirimi:
char *tmpnam(char *s);
biimindedir. lev, rettii dosya ismini kendisine gnderilen char trden adrese yazar.
lev, ayn zamanda ayn dosya ismini statik mrl bir dizinin iine yazarak, bu dizinin
balang adresini geri dndrr. Eer ileve argman olarak NULL adresi gnderilirse,
ilev yalnzca statik mrl dizinin adresinin dndrr. leve char trden bir dizinin
474
adresi gnderildiinde bu dizinin boyutu ne olmaldr? Baka bir deyile tmpnam ilevi ka
karakter uzunluunda bir dosya ismi retir? te bu deer stdio.h dosyas iinde
tanmlanan L_tmpnam simgesel deimeziyle belirtilir.
tmpnam ilevinin rettii dosya isminin allan dizin iinde daha nce kullanlmayan bir
dosya ismi olmas gvence altna alnmtr. Yani retilen dosya ismi bulunulan dizin
iinde tektir. Bir programda daha sonra silmek zere bir dosya alacan ve bu dosyaya
birtakm bilgilerin yazlmasndan sonra dosyann silineceini ya da dosyaya baka bir
isim verileceini dnelim. Bu durumda ilgili dosya yazma modunda alacana gre, bu
dosyaya var olan bir dosyann ismi verilmemelidir. Eer var olan bir dosyann ismi
verilirse, var olan dosya sfrlanaca iin dosya kaybedilir. Bu riske girmemek iin, geici
olarak kullanlacak dosya tmpfile ilevi kullanlarak almaldr. Ancak tmpfile ilevinin
kullanlmas durumunda, alan dosya kalc hale getirilemez. Yani herhangi bir nedenden
dolay geici dosyann silinmemesi istenirse, dosya kalc hale getirilmek istenirse dosya
fopen ileviyle almaldr. te bu durumda geici dosya baka bir dosyay riske etmemek
iin tmpnam ilevinin rettii isim ile almaldr.
Peki tmpnam ileviyle en fazla ka tane gvenilir dosya ismi retilebilir? te bu deer
stdio.h iinde tanmlanan TMP_MAX simgesel deimezi ile belirlenmitir.
Akmlar
C dilinde baz giri ve k birimleri (klavye, ekran gibi) dorudan bir dosya gibi ele alnr.
Herhangi bir giri k birimini akm (stream) olarak isimlendirir. Bir akm, bir dosya
olabilecei gibi, dosya olarak ele alnan bir giri k birimi de olabilir. rnein kk
programlar genellikle girdilerini genellikle tek bir akmdan alp (rnein klavyeden)
ktlarn da tek bir aka (rnein ekrana) iletir.
Bir programa yaplan girdilerin programa doru akan bir byte akmndan (input stream)
geldii kabul edilir. Yine bir programn kts da programdan darya doru akan byte lar
olarak (output stream) dnlr. Dosyalarla ilgili okuma yazma yapan ilevler dorudan
giri akmndan okuma yapp, k akmna yazabilirler.
Bir C program altrldnda 3 akm gsteren dosyann otomatik olarak ald kabul
edilir. Bu akmlar birer dosya olarak kullanlabilirler ve nceden tanmlanm FILE *
trnden deerlerle ilikilendirilmilerdir:
stdin : Standart giri birimini temsil eder. Bu akm normal olarak klavyeye balanmtr.
stdout : Standart k akmn temsil eder. Bu akm normal olarak ekrana balanmtr.
stderr : Standart hata akmn temsil eder. Bu akm da normal olarak ekrana
balanmtr.
Daha nce klavyeden girdi alan ilevler (getchar, gets, scanf) olarak rendiimiz ilevler
aslnda stdin akmndan okuma yapan ilevlerdir.
Daha nce ekrana yazan ilevler (putchar, puts, printf) olarak rendiimiz ilevler
aslnda stdout akmna yazan ilevlerdir.
Dosyadan okuma yapan ilevlere stdin, yazma yapan ilevlere ise stdout ve stderr FILE *
trnden deerler olarak geilebilir. rnein
fprintf(stdout, "Necati Ergin");
ars ekrana Necati Ergin yazsn yazdrr. Benzer biimde
fputc('A', stdout);
Ekrana 'A' karakteri bastrr.
fscanf(stdin, "%d", &val);
475
Akmlarn Ynlendirilmesi
476
return 0;
Yukardaki program altrldnda klavyeden NECATI ismi girildiinde ekrana "eit degil"
yazs yazdrlr. Zira fgets ilevi NECATI girii yapldktan sonra girilen newline karakterini
de diziye yerletirir. Yani aslnda strcmp ilevi
N E C A T I
N E C A T I \n
yazlarn karlatrr.
Karlatrma ileminden nce aadaki gibi bir if deyimi kullanlabilirdi:
if ((ptr = strchr(str, '\n')) != NULL)
*ptr = '\0';
Yukardaki deyimde strchr ileviyle str adresindeki yaznn iinde '\n' karakteri aranyor.
Yaznn iinde '\n' karakteri bulunursa, bu karakterin yerine sonlandrc karakter
yazlyor. Yani yaznn sonunda '\n' karakteri varsa yazdan siliniyor.
freopen levi
Bu ilev daha nce alm bir dosyay, kendi at dosyaya ynlendirir. levin bildirimi:
477
feof levi
Dosya konum gstericisi dosyann sonunu gsterdiinde bir dosyadan okuma yaplrsa
okuma ilemi baarl olmaz. Bu durumda isel olarak tutulan bir bayrak set edilir.
Dosyadan okuma yapan ilevler nce bu bayran deerine bakar. Bayrak set edilmise
okuma yapmazlar. feof ilevi bu bayran deerini alr:
int feof(FILE *);
Dosya konum gstericisi dosya sonunu gsterirken dosyadan okuma yaplmsa ilev sfr
d bir deere dner. Aksi halde ilev 0 deerine geri dner.
feof ileviye ilgili yaplan tipik bir hata, ilevin dosya konum gstericisinin dosyann
sonunu gsterip gstermediini snadn sanmaktr:
if (feof(f))
/*****/
deyimi ile byle bir snama yaplamaz. nk feof ilevi dosya konum gstericisi dosya
sonunu gstermesine karn bu konumdan daha hi okuma giriiminde bulunulmamsa 0
deerine geri dner. Bir metin dosyasnn ieriini ekrana yazdrmak isteyen aadaki C
kodu hataldr. Neden hatal olduunu bulmaya aln:
478
#include <stdio.h>
#include <stdlib.h>
#define FILE_NAME_LEN
#define BUFFER_SIZE
256
20
int main()
{
char source_file_name[FILE_NAME_LEN];
char buffer [BUFFER_SIZE];
FILE *fs;
printf("yazdirilacak dosya ismi: ");
gets(source_file_name);
fs = fopen(source_file_name, "r");
if (fs == NULL) {
printf("%s dosyasi acilamiyor!\n", source_file_name);
exit(EXIT_FAILURE);
}
while (!feof(fs)) {
fgets(buffer, BUFFER_SIZE, fs);
printf("%s", buffer);
}
fclose(fs);
}
return 0;
ferror levi
Okuma ya da yazma yapan ilevler, okuma ya da yazma ileminde bir hata olduunda
isel olarak ounlukla bitsel bir alanda tutulan bir bayra birlerler. Okuma ya da yazma
yapan ilevler nce bu bayran deerine bakar. Bayrak set edilmise yeni bir
okuma/yazma ilemi yaplamaz. nce bayran tekrar sfrlanmas gerekir. ferror ilevi
hata bayrann birlenip birlenmediini snar:
int ferror(FILE *);
hata bayra set edilmise ilev sfr d bir deere geri dner. Hata bayra set
edilmemise ilev 0 deerine geri dner.
clearerr levi
void clearerr(FILE *stream );
Okuma ya da yazma ileminde bir hata olduunda hata bayrann set edildiini, dosya
sonundan okuma yapma giriiminde de EOF bayrann set edildiini sylemitik. Bu
bayraklar set edilmi durumdayken bir okuma ya da yazma ilemi gerekletirilemez.
Yeniden bir okuma ya da yazma ileminin yaplabilmesi iin nce bayraklarn sfrlanmas
gerekir. Bu sfrlama ilemi iin clearerr ilevi arlabilir.
lev ilgili dosyaya ilikin FILE * trnden deeri alr ve bu dosyann EOF ve Error
bayraklarn sfrlar.
ungetc levi
479
ungetc ilevi iin dosyann okuma modunda alm olmas gerekir. levin arlmasndan
sonra yaplan ilk okumada c deeri okunur. lev baarl olursa, c deerine geri dner.
Baarszlk durumunda dosyann tampon alannda bir deiiklik olmaz ve EOF deeri
dndrlr. lev, karakteri tampon alanna yerletirdikten sonra EOF bayran da
sfrlar. Dosya konum gstericisi yeniden konumlandrlmadan ungetc ilevi arka arkaya
arlmamaldr.
kincil belleklerle (disket, hard disk vs.) yaplan ilemler bellekte yaplan ilemlere gre
ok yavatr. Bu yzden bir dosyadan bir karakterin okunmas ya da bir dosyaya bir
karakterin yazlmas durumunda her defasnda dosyaya dorudan ulamak verimli bir
yntem deildir.
lemin verimi tamponlama (buffering) yoluyla artrlr. Bir dosyaya yazlacak veri ilk nce
bellekteki bir tampon alannda saklanr. Bu tampon alan dolduunda ya da yazma
ileminin yaplaca dosya kapatldnda tampondaki alanda ne veri varsa dosyaya
yazlr. Buna tampon alannn boaltlmas (flushing) denir.
Giri dosyalar da benzer ekilde tamponlanabilir. Giri aygtndan rnein klavyeden
alnan byte'lar nce tampona yazlr.
Dosyalarn tamponlanmas verimlilikte ok byk bir arta neden olur. nk tampondan
yani bellekten bir karakter okunmas ya da tampona bir karakter yazlmas ihmal edilecek
kadar kk bir zaman iinde yaplr. Tampon ile dosya arasndaki transfer, phesiz yine
zaman alr ama bir defalk blok aktarm , kk kk aktarmlarn toplamndan ok daha
ksa zaman alr.
stdio.h balk dosyas iinde bildirimi yaplan ve dosyalarla ilgili ilem yapan ilevler
tamponlamay otomatik olarak gerekletirir. Yani dosyalarn tamponlanmas iin
programcnn birey yapmasna gerek kalmadan bu i geri planda programcya
sezdirilmeden yaplr. Ama baz durumlarda tamponlama konusunda programc belirleyici
durumda olmak isteyebilir. te bu durumlarda programc dosya tamponlama ilevlerini
(fflush, setbuf, setvbuf) kullanr:
fflush levi
setvbuf levi
480
Bir dosya ile ilgili ilem yapan bir ilev iki ayr biimde tasarlanabilir:
1. lev arldnda dosya aktr. Bu durumda ilevin bir parametresi FILE * trnden
olur. lev arlmadan nce, en son okuma ilemi mi yazma ilemi mi yapld
bilinemeyeceinden, byle bir ilev ilk ilem olarak dosya konum gstericisini
konumlandrmaldr. Aadaki ilevleri inceleyin:
481
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define
#define
SORTED
NOT_SORTED
1
0
rewind(f);
while (fread(&val, sizeof(int), 1, f)) {
if (counter && counter % 10 == 0)
printf("\n");
printf("%5d ", val);
counter++;
}
printf("\n********************************************************\n");
482
int main()
{
FILE *f;
f = fopen("sayilar.dat", "w+b");
if (f == NULL) {
printf("dosya yaratilamiyor!\n");
exit(EXIT_FAILURE);
}
srand((unsigned int)(time(0)));
add_ints_to_file(f);
print_file(f);
sort_file(f);
print_file(f);
fclose(f);
}
return 0;
main ilevi iinde arlan ilevlerden add_ints_to_file ilevi ald binary dosyann sonuna
rastgele tamsaylar formatsz olarak yazyor. Daha sonra arlan print_file ilevi, binary
dosyaya yazlan tm saylar ekrana yazyor. sort_file ilevi ile dosyadaki tamsaylar
kkten bye doru sralandktan sonra print_file ileviyle yeniden yazdrlyor.
2. lev arldnda dosya kapaldr. lev, aran kod parasndan dosya ismini ister.
Bu durumda ilev iini gerekletirmek iin nce dosyay aar. ini gerekletirdikten
sonra dosyay kapatr. Aada bir metin dosyasnn ieriini ekrana yazdran bir ilev
tanmlanyor:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void ftype(const char *file_name)
{
int ch;
483
MAKROLAR
Yapsal programlama alt programlamaya dayanr. Bir problem zm daha kolay
admlara ayrlr, bu admlar iin ilevler tanmlanr. Programn kullanaca veri ilevler
tarafndan ilenir. Tanmlanan ilevlerden bazlarnn kaynak kodu ok ksa olabilir.
Aadaki gibi bir ilev tanmlandn dnelim:
int get_max(int a, int b)
{
return a > b ? a : b;
}
get_max ilevi kendisine gnderilen iki saydan daha byk olanna geri dnyor. imdi
de u soruyu soralm: Bu kadar ksa kaynak koda sahip bir ilev tanmlanmal mdr? lev
tanmlamak yerine ilevin kodunu, dorudan ilev arsnn bulunduu yere yerletirmek
daha iyi olmaz m?
Yazlan bir kod parasnda iki saydan bynn bulunmas gerektiini dnelim:
x = get_max(y, z);
gibi bir deyim yerine
x = y > z ? y : z;
gibi bir deyim yazlamaz m?
Bir ilev tanmlayp tanmlanan ilev mi arlmal yoksa ilevin kodu dorudan m
yazlmaldr?
nce, ilev tanmlamay destekleyen argmanlar zerinde duralm:
1. lev ars, zellikle ilevin ismi iyi seilmi ise, ou zaman okunabilirlii daha
yksek bir kod oluturur. Aadaki rnei inceleyin:
a = get_max(ptr->no, per.no);
a = ptr->no > per.no ? ptr->no : per.no;
lk deyimde get_max isimli bir ilev arlm ikinci satrda iki ifadeden deeri daha
byk olan, bir ilev arlmadan bulunmutur. Hangi deyimin okunabilirlii daha iyidir?
2. lev ars ou durumda kaynak kodu kltr. zellikle, ileve konu ilem kaynak
kod iinde sk yineleniyorsa, bir ilevin tanmlanarak eitli yerlerde arlmas kaynak
kodu kltr.
3. lev ars kaynak kodda deiiklik yapmay daha kolay hale getirir:
Kaynak kod iinde bir ok yerde, iki deerden daha bynn bulunarak kullanldn
dnelim. Daha sonra kaynak kodun bu noktalarnda deiiklik yaplarak, ayn
noktalarda iki deerden daha knn bulunarak kullanlmas gerektiini dnelim.
Eer ilev ars yerine kod ak bir ekilde yazlmsa, deiiklik yapmak ok daha zor
olur.
4. lev ars, debug etme olanan artrr. Debugger programlar genel olarak ilev
arlar iin, kodun ak olarak yazlmasna gre daha iyi destek verir.
5. lev ars derleyiciye baz snamalar yapma olanan verir. leve gnderilen
argmanlarn toplam saysnn ilevin parametre deikenlerinin toplam saysna eit olup
485
olmad derleyici tarafndan snanr. Ayrca ileve gnderilen argmanlarn tr ile ilevin
ilgili parametre deikeninin trlerinin uyumlu olup olmad da derleyici tarafndan
snanr.
imdi de ilev ars yapmak yerine ilevin kodunu dorudan yazmak durumunda elde
edilebilecek avantajlarn ne olabileceine bakalm:
Bir ileve yaplan arnn ilemci zaman ve bellek kullanm asndan ek bir maliyeti
vardr. leve giri ve ilevden k kodlarnn yrtlmesi iin ek makina komutlarnn
yrtlmesi gerekir. Varsa ilevin parametre deikenleri de, ilev arldnda bellekte
yer kaplar. zellikle kaynak kodu ok ksa, kaynak dosya iinde ok arlan ilevler iin,
ou durumda bu maliyetin denmesi istenmez.
lev ars yapmak yerine kod ak bir biimde yazlrsa, derleyici kodun bulunduu yere
bal olarak daha verimli bir eniyileme (optimizasyon) gerekletirebilir.
Makro Nedir
ISEVEN(a)
MAX(a, b)
int main()
{
int x, y;
printf("iki sayi giriniz :");
scanf("%d%d", &x, &y);
if (ISEVEN(x))
printf("%d cift sayi!\n", x);
else
printf("%d tek sayi!\n", x);
printf("%d ve %d sayilarindan buyugu = %d\n", x, y, MAX(x, y));
}
return 0;
Yukardaki main ilevinde, ismi ISEVEN ve MAX olan iki makro tanmlanyor. nilemci
makro tanmnn zerinden getikten sonra
if (ISEVEN(x))
486
satr ile karlatnda, ISEVEN isminin daha nce tanmlanan makroya ait olduunu ve
makro argman olarak x atomunun kullanldn anlar. nilemci program ilgili yer
deitirme ilemini yaptnda bu satr aadaki biime dnr:
if ((!((x) & 1)))
Benzer ekilde
printf("%d ve %d sayilarindan buyugu = %d\n", x, y, MAX(x, y));
satr da MAX isimli makronun nilemci program tarafndan almas sonucu
printf("%d ve %d sayilarindan buyugu = %d\n", x, y, ((x) > (y) ? (x) :
(y)));
haline getirilir.
x * x
(x) * (x)
487
((x) *(x))
((x) * (x))
rnek Makrolar
GETRANDOM(min,
ISLEAP(y)
SWAPINT(x, y)
XOR(x, y)
max)
((rand()%(int)(((max) + 1)-(min)))+ (min))
((y) % 4 == 0 && (y) % 100 != 0 || (y) % 400 == 0)
((x) ^= (y) ^= (x) ^= (y))
(!(x) ^ !(y))
488
Yukarda tanmlanan getrandom isimli makro min ve max deerleri arasnda bir rastgele
tamsaynn retilmesine neden olur.
isleap makrosu nilemci tarafndan aldnda makro argman olan ifadenin deerinin
artk yl olmas durumunda 1 deeri aksi halde 0 deeri retilir. Bu durumda makro bir
snama ilevi gibi kullanlr.
swap_int makrosu argman olan tamsay deikenlerin deerlerini takas eden bir ifadeye
alr.
Bir makronun parametreye sahip olmas gerekmez. Aada tanmlanan randomize
makrosunu inceleyin:
#define randomize()
srand((unsigned int)time(NULL))
Bir makro tanmnn alm listesinde yer alan # atomu nilemci programn bir ilecidir.
# ileci nek konumunda tek terimli bir iletir. Bu ilemcinin terimi makro
parametrelerinden biri olmaldr. Parametresiz makrolarda bu ile kullanlamaz.
nilemci makroyu atnda makro parametresine karlk gelen makro argmann ift
trnak iine alr:
Aadaki kodu inceleyin:
#include <stdio.h>
#define
printint(x)
int main()
{
int a = 10;
int b = 20;
printint(a);
printint(a + b);
}
return 0;
Yukardaki rnekte, printint makrosu alm listesinde, makro parametresi olan x, dizge
yapma ilecinin terimi yaplyor. nilemci program, main ilevinin kodunu aadaki
biime dntrr.
int main()
{
int a = 10;
int b = 20;
printf("a" " = %d\n", a);
printint("a + b" " = %d\n", a + b);
return 0;
}
Boluk karakterleriyle birbirinden ayrlan dizgelerin, derleyici tarafndan otomatik olarak
birletirilerek tek dizge haline getirildiini anmsayn. Program derlenerek altrldnda
ekran kts aadaki gibi olur:
a = 10
a + b = 30
489
Makro argman olarak bir dizge verilirse dizgenin banda ve sonunda yer alan ift
trnak (") karakterleri ve dizgenin iinde yer alan ters bl (\) ve ift trnak (")
karakterinin nne makro almnda otomatik olarak ters bl karakteri yerletirilir.
Aadaki kodu inceleyin:
#include <stdio.h>
#define print(x)
printf(#x)
int main()
{
print(Ekranda bu yazi gorulecek\n);
print("Ekranda bu yazi cift tirnak icinde gorunecek"\n);
print("\""\n);
return 0;
}
Program derlenip altrldnda ekran kts aadaki gibi olur:
Ekranda bu yazi gorulecek
"Ekranda bu yazi cift tirnak icinde gorunecek"
"\""
nilemci programn ikinci ileci "##" atom birletirme ielecidir (tokenpasting operator).
Atom birletirme ileci iki terimlidir araek konumundadr. Parametreli ya da parametresiz
makrolarda kullanlabilir. Terimlerinin, makro parametrelerinden biri olmas zorunlu
deildir. nilemci, atom birletirme ilecinin terimlerini birletirerek, terimlerinden tek
bir atom yapar. Aadaki rnei inceleyin:
#include <stdio.h>
#define
paste(x, y)
x##y
int main()
{
int paste(a,1);
a1 = 10;
printf("a1 = %d\n", a1);
}
return 0;
Yukardaki rnekte, nilemci program tarafndan paste isimli makronun almas sonucu
a ve 1 makro argmanlar birletirilerek a1 atomu elde edilir.
Baz ktphanelerde belirli ilevler ayn isimle hem makro hem de ilev olarak
tanmlanrlar. Bundan amalanan, programcnn istee bal olarak makro kullanmn ya
da ilev arsn semesine olanak vermektir. Hem kare isimli bir makro tanmlanm
hem de kare isimli bir ilev bildirilmi olsun:
int kare(int);
#define
kare(a)
((a) * (a))
490
x = kare(y);
phesiz, byle bir deyimde makro kullanlm olur. nk nilemci, program kodunu
derleyici programdan daha nce ele alr. Derleyiciye sra geldiinde, nilemci zaten ilgili
makro iin yer deitirme ilemini tamamlam olur. Ancak makro yerine ilev arsn
semek iin iki yntem kullanlabilir.
1. #undef nilemci komutu kullanlarak makro tanm ortadan kaldrlabilir:
#undef kare
x = kare(y);
nilemci program #undef konutuyla karlatnda makro tanmn ortadan kaldrr ve
izleyen satrlarda kare ismini grdnde bir yer deitirme ilemi yapmaz.
2. kare ismi ayra iine alnarak makro devre d braklabilir
x = (kare)(y);
Bu durumda kare ismini izleyen ilk atom, alan ayra "(" karakteri olmadndan,
nilemci program yer deitirme ilemi yapmaz. Ancak ilev arsn engelleyen bir
durum sz konusu deildir. lev isminin bir adres belirttiini biliyorsunuz. Yukardaki
ifade ile ilev adresine dntrlecek olan kare ismi ncelik ayrac iine alnmtr.
3. Bir ilev, bir ilev gstericisi kullanlarak arlabilir:
int kare(int);
void func()
{
int (*fp)(int) = &kare;
fp();
}
Derleyiciler, standart ktphaneye ilikin ilevleri ayn zamanda makro olarak da
tanmlayabilirler. rnein derleyicilerin ou cytpe.h balk dosyasnda bildirilen karakter
test ilevlerini ayn zamanda makro olarak tanmlar.
1. Makrolar ou zaman ayn ii yapan ileve gre daha hzl alan bir kodun
retilmesine neden olurlar. Zaten makrolarn kullanld durumlarda hedeflenen de
budur.
2. Makrolar, her kullanld yerde nilemci tarafndan ald iin, ounlukla kaynak
kodu bytr. Kaynak kodun byd durumlarn ounda, altrlabilir dosyann
boyutu da byr. Ancak bir ilev ka kez arlrsa arlsn, o ilevin tanm kaynak kod
iinde bir kez yer alr.
3. Makro paremetreleri makro alm iinde birden fazla kullanlyor ise makro alm
sonucu istenmeyen durumlar oluabilir. Makro argman olarak kullanlan ifadenin bir yan
etkiye sahip olmas durmunda bu yan etki birden fazla kez oluur. Ancak ilevler sz
konusu olduunda byle bir risk sz konusu deildir.
4. Bir ilevin adresi ilev gstericilerinde tutularak baz ilemler gerekletirilebilir. Ancak
bir makronun adresi olmaz.
5. Bir makro sz konusu olduunda nilemci program, makroya verilen argmanlarn
says ile makro parametrelerinin saysnn uyumunu kontrol edebilir. Ancak nilemci
491
Bir makro alm listesinin yazm makronun tanmland satrda sonlanmak zorunda
deildir. Makro alm listesinin yazm alt satrdan devam edebilir. Bunun iin alt satra
gemeden nce ters bl karakteri '\' kullanlr.
Makrolar kullanlarak ilev kalb hazrlanabilir. Aadaki rnekte bir dizinin en byk
elemann bulan bir ilev kalb hazrlanyor:
#include <stdio.h>
#define generic_max(T)
generic_max(int)
generic_max(double)
int main()
{
int a[10] = {1, 4, 5, 7, 8, 9, 10, 2, 3, 6};
double b[10] = {1.2, 3.5, 7.8, 2.4, 4.4, .7, 3.2, 4.8, 2.9, 1.};
printf("a dizisinin en buyuk elemani = %d\n", getmax_int(a, 10));
printf("b dizisinin en buyuk elemani = %lf\n", getmax_double(b, 10));
return 0;
}
generic_max isimli makronun tanmnda makro parametresi olarak T isminin kullanldn
gryorsunuz. Bu isim bir tr bilgisi olarak kullanlyor. nilemci atom birletirme
ileciyle, makro alm sonucunda farkl ilev isimleri elde edilir.
Bir makro ismi ya da bir simgesel deimez, deitirme listesinde yer alabilir. Bu duruma
ngilizcede "self referential macro" denmektedir. Eer alm listesinde makronun kendi
ismi yer alrsa nilemci zyinelemeli bir yer deitirme ilemi yapmaz.
Aadaki rnei inceleyin:
#define max
(4 + max)
max isminin programn iinde kullanlan bir baka deikenin ismi olduunu dnelim.
x = max;
nilemci program yukardaki deyimin zerinden getiinde bu deyimi
x = 4 + max;
biimine dntrr. Ve daha sonra artk max ismini yine yer deitirme ilemine
sokmaz.
492
Bu makro stddef.h balk dosyas iinde tanmlanmtr. Makronun iki parametresi vardr.
Birinci parametreye bir yap tr ismi, ikinci parametreye yapnn bir eleman verilir.
Makronun almas sonucu oluan deimez ifadesinin deeri, bir yap elemannn yap
nesnesi iindeki konumunu gsteren offset deeridir. Yapnn ilk eleman iin bu deer
0'dr.
Aadaki rnei inceleyin:
#include <stdio.h>
#include <stddef.h>
struct Date {
int day, mon, year;
};
int main()
{
printf("%d\n", offsetof(struct Date, day));
printf("%d\n", offsetof(struct Date, mon));
printf("%d\n", offsetof(struct Date, year));
}
return 0;
((size_t)&((type *)0)member)
493
Koullu derleme, derleme ileminin bir koula ya da baz koullara gre yaplmasdr.
Yazlan kaynak kodun belirli ksmlar nilemci program tarafndan derleyiciye verilirken
baz ksmlar derleyiciye verilmez. Derleyiciye verilecek kaynak kod nilemci program
tarafndan seilir.
#if nilemci komutunun argman tamsay trnden bir deimez ifadesi olmaldr. if
szcn izleyen ifadenin deeri sfr d bir deer ise #if komutu ile #elif ya da #else
yada #endif komutlar arasnda kalan ksm derleyiciye verilir. Eer #if ifadesi yanl ise
yani ifadenin deeri sfr ise #if komutu ile #else arasnda kalan ksm derleyiciye
verilmez. Aadaki program derleyerek altrn:
int main()
{
#if 1
printf("bu yazi ekranda gorunecek\n");
#endif
#if 0
printf("bu yazi ekranda gorunmeyecek\n");
#endif
return 0;
}
#if komutunda kullanlacak ifade tamsay trnden olmaldr. Bu ifade iinde
Tamsay deimezleri kullanlabilir. nilemci program kullanlan tm say deimezlerini
iaretli long ya da iaretsiz long trnden varsayar.
Karakter deimezleri kullanlabilir.
Aritmetik ileler (+, -, *, /, %), karlatrma ileleri (<, <=, >, >=, ==, !=) , bitsel
ileler(~, &, ^, |) ve mantksal ileler (!, && ve || )kullanlabilir. && ve || ilelerinin
kullanmnda ksa devre davran geerlidir.
#if ifadesinin deerinin hesap edilmesinden nce simgesel deimezlerle ve makro
tanmlarna ilikin btn yer deitirme ilemleri yaplr.
Simgesel deimez ve makro ismi olmayan tm isimler 0 deerine sahip kabul edilirler.
#if ifadesinde sizeof ileci yer alamaz. Numaralandrma trlerinden deimezler temsil
ettikleri deerler olarak ele alnmaz. #if ifadesinde bir numaralandrma deimezi
kullanlrsa, bu deimez makro ismi olmayan tm isimler gibi 0 deeri olarak ele alnr.
#if komutu ile #endif komutu aralndaki alanda baka nilemci komutlar yer alabilir.
Bu alanda yer alan nilemci komutlar ancak #if ifadesi doru ise yerine getirilir.
#if komutunu izleyen ifade bu #if komutuyla ilikilendirilen #endif komutunu izleyen bir
yorum satr iinde yinelenirse, kodun okunabilirlii artar. Bu durum zellikle #if
komutlarnn i ie bulunmas durumunda fayda salar. Aadaki rnei inceleyin:
495
# if komutunu bir #else komutu izleyebilir. Bu durumda #if ifadesinin deeri 0 d bir
deer ise yani ifade doru ise #if ve #else komutlar arasnda kalan kaynak kod paras
derleyiciye verilir. Eer bu ifade yanl ise #else ve #endif komutlar arasndaki ksm
derleyici programa verilir. Bylece bir tamsay deimez ifadesinin doru veya yanl
olmasna gre derleyiciye farkl kod paralar verilebilir. Aadaki program derleyerek
altrn:
#include <stdio.h>
#define
MAX
100
int main()
{
#if
MAX > 10
printf("%d\n", MAX);
#else
printf("**********\n");
#endif /*MAX > 100 */
return 0;
}
MAX
60
int main()
{
#if MAX > 10 && MAX <= 20
printf("(if)%d\n", MAX);
#elif MAX > 20 && MAX <= 50
printf("(elif1)%d\n", MAX);
#elif MAX > 50 && MAX < 100
printf("(elif2)%d\n", MAX);
#else
printf("(else)%d\n", MAX);
#endif
return 0;
}
496
Makrolar konusunda nce nilemci programn dizge yapma (#) ve atom birletirme
(##) ilelerini grmtk. nilemci programn 3. ve son ilei defined ilecidir. defined
nilemci ilecini bir isim izler. Bu isim ayra iine de alnabilir:
defined MAX
defined (MAX)
Eer defined ilecinin terimi olan isimli daha nce tanmlanm bir simgesel var ise,
defined ileci 1 deeri retir. Bu isimli bir simgesel deimez yoksa defined ileci 0 deeri
retir.
Aadaki rnei inceleyin:
#include <stdio.h>
#define
MAX
100
int main()
{
#if defined MAX && !defined
MIN
printf("max = %d\n", MAX);
#endif
return 0;
}
#if nilemci komutunun kullanld her yerde #ifdef ve #ifndef nilemci komutlar
kullanlabilir:
#ifdef nilemci komutunu bir isim(identifier) izler. Eer bu isim daha nce tanmlanm
bir simgesel deimeze ilikinse ilk #else, #elif ya da #endif neilemci komutlarna
kadar olan ksm derleyiciye verilir. Eer #ifdef nilemci komutunu izleyen isimde bir
simgesel deimez tanml deil ise ilk #else #elif #endif komutuna kadar olan ksm
derleyiciye verilmez. Bu komut ile yaplan i #if nilemci komutu ile defined ilecinin
birlikte kullanlmasyla da yaplabilir:
#ifdef ISIM
gibi bir nilemci komutu kullanm ile
#if defined (ISIM)
gibi bir nilemci komutu ayn anlamdadr.
#ifndef nilemci komutunu da bir isim(identifier) izler. Eer bu isimde bir isim daha
nce #define nilemci komutuyla tanmlanmam ise, ilk #else, #elif ya da #endif
neilemci komutuna kadar olan ksm derleyiciye verilir.
#ifndef nilemci komutunu izleyen isimde bir simgesel deimez tanmlanm ise #else
#elif #endif komutlarna kadar olan ksm derleyiciye verilmez.
#ifndef
SYSTEM
497
#include <stdio.h>
#define ISIM1
int main()
{
#ifdef ISIM1
printf("bu yazi ekranda cikacak!\n");
#endif
#ifndef ISIM1
printf("bu yazi ekranda cikmayacak!\n");
#endif
return 0;
}
Bir simgesel deimezin ilki ile zde olmayan bir biimde ikinci kez tanmlanmas
tanmsz davrantr (undefined behaviour).
rnein aadaki gibi bir tanmlama ilemi yanltr:
#define
#define
MAX 100
MAX 200
498
Bir simgesel deimezin ilki ile zde olarak tanmlanmasnda herhangi bir sorun kmaz.
Bir simgesel deimez ikinci kez tanmlanmak istenirse nce eski tanmlamay ortadan
kaldrmak gerekir. Bu ilem #undef nilemci komutuyla yaplr.
#undef nilemci komutunun yanna geerlilii ortadan kaldrlacak simgesel deimezin
ya da makronun ismi yazlmaldr:
#undef
#define
MAX
MAX
20
Standart C dilinde 5 tane simgesel deimez nceden tanmlanm kabul edilir. Herhangi
bir balk dosyas iinde bu simgesel deimezler #define nilemci komutuyla
tanmlanm olmamasna karn kaynak kodun derleyici tarafndan ele alnmasndan nce
bir yer deitirme ilemine sokulurlar. Bu simgesel deimezler ounlukla hata arama
amacyla yazlan kodlarda kullanlrlar:
__LINE__ ntanml simgesel deimezi
Bu simgesel deimez kaynak kodun kanc satrnda kullanlm ise, o satrn numarasn
gsteren bir tamsay deimezi ile yer deitirilir.
__FILE__ ntanml simgesel deimezi
Bu simgesel deimez hangi kaynak dosya iinde kullanlm ise, o kaynak dosyann
ismini gsteren bir dizge ifadesiyle yer deitirilir.
__DATE__ ntanml simgesel deimezi
Bu simgesel derleme tarihini gsteren bir dizge ifadesiyle yer deitirilir. Tarih bilgisini
ieren yaznn format aadaki gibidir:
Aaa gg yyyy (ay, gn, yl)
__TIME__ ntanml simgesel deimezi
Bu simgesel deimez derleme zamann gsteren bir dizge ifadesiyle yer deitirilir.
Zaman bilgisini ieren yaznn format aadaki gibidir:
sa:dd:ss (saat, dakika, saniye)
__STDC__ ntanml simgesel deimezi
Eer derleyici standart C derleyicisi ise bu simgesel deimez tanml kabul edilir.
Derleyici standart C derleyicisi deil ise bu simgesel deimez tanmlanmam kabul
edilir.
Aadaki program derleyerek altrn:
# include <stdio.h>
int main()
{
printf("kaynak dosya ismi : %s\n", __FILE__);
printf("derleme tarihi = %s\n", __DATE__);
printf("derleme zamani = %s\n", __TIME__);
printf("bu satirin numarasi = %d\n", __LINE__);
#ifdef __STDC__
printf("standart C derleyicisi\n");
499
#else
printf("standart C derleyicisi degil\n");
#endif
return 0;
}
[C++ derleyiclerinde de __cplusplus ntanml simgesel deimezi, tanmlanm kabul edilir.]
Bu nilemci komutuyla derleyicinin kaynak koda ilikin tuttuu satr numaras ve dosya
ismi deitirilebilir. Bu komut iki ayr argman alabilir.
Komutun alabilecei birinci argman bir tamsay olarak satr numarasdr. Komutun
seimlik olarak alabilecei ikinci argman dosya ismini gsteren dizgedir.
Bu nilemci komutu kaynak kod reten programlar tarafndan kullanlabilir.
#line nilemci komutu size anlamsz gelebilir. Neden derleyicinin verecei hata iletisi
rnein 20. satr deil de 25. satr gstersin? Neden derleyici hata iletisinde derledii
kaynak dosyann ismini deil de bir baka dosyann ismini yazdrsn?
#line komutu programclardan ok, kt olarak C kaynak kodu reten programalar
tarafndan kullanlr. Aadaki program derleyerek altrn:
#include <stdio.h>
int main()
{
printf("%s dosyasnn %d. satr\n", __FILE__, __LINE__);
#line
100
"aaaaa.c"
printf("%s dosyasnn %d. satr\n", __FILE__, __LINE__);
}
return 0;
Projeler ounlukla farkl kaynak dosyalardan ve balk dosyalarndan oluur. Hizmet alan
kodlar (client codes) hizmet veren kodlarn (server codes) balk dosyalarn eklerler.
Balk dosyasn eklemenin #include nilemci komutuyla yapldn biliyorsunuz. Bir
kodlama dosyasnda iki ayr balk dosyasnn eklendiini dnelim:
/*** file1.c *****/
#include "header1.h"
#include "header2.h"
Balk dosyalar iinde baka balk dosyalarnn eklenmesine sk rastlanr. rnein
header1 ve header2 isimli balk dosyalarnn her ikisinde de header3.h isimli bir balk
dosyasnn eklendiini dnelim:
/*** header1.h *****/
#include "header3.h"
/*** header2.h *****/
#include "header3.h"
Bu durumda file1.c isimli kaynak dosya iine header3.h isimli balk dosyas iki kez
eklenmi olur. Bu duruma ngilizcede "multiple inclusion" denir. Peki bu durumun bir
sakncas var mdr?
Balk dosyalar iinde bildirimler bulunur. Bir balk dosyas iki kez eklenirse bu balk
dosyas iindeki bildirimler iki kez yaplm olur. Baz bildirimlerin zde olarak
yinelenmesinin bir sakncas yoktur. rnein bir ilev bildirimi daha nceki bir bildirimle
elimemek kaydyla yinelenebilir. Benzer durum extern bildirimleri ve typedef bildirimleri
500
501
200
50
200
50
0
100
Yukardaki rnekte birinci #if blounun altnda iki ayr #if #else yaps bulunur. DLEVEL
simgesel deimezinin 5'den byk olup olmamasna gre deerine gre nilemci
tarafndan dor ksm ya da yanl ksm ele alnr.
#elif nilemci komutunun da yer ald ikinci #if blounda ise DLEVEL simgesel
deimezinin deerine gre 4 seenekten biri ele alnr. DLEVEL simgesel deimezinin
deerine bal olarak STACK simgesel deimezi 0, 100 ya da 200 olarak tanmlanr.
DLEVEL simgesel deimezinin deerinin 5'den byk olmas durumunda ise
display(debugptr);
deyimi derleyiciye verilir. Bu durumda STACK simgesel deimezi tanmlanmaz.
502
assert Makrosu
assert, program gelitirirken programcnn gelitirme sreci iinde bir takm olas
bcekleri farketmesi iin kullanlan bir makrodur. C'nin standart balk dosyalarndan
assert.h iinde tanmlanmtr. assert makrosu, makroya argman olarak bir ifadenin
verilmesiyle kullanlr. rnein:
assert(p != NULL);
Ayra iindeki ifade, doru olduu ya da doru olmas gerektii dnlen bir durum
belirtir. assert makrosuna verilen ifadenin saysal deeri sfr d bir deerse, yani
snanan nerme doruysa, assert makrosu hibir ey yapmaz. Ancak makro ifadesinin
deeri sfrsa, yani ifadeye konu nerme mantksal olarak yanl ise assert makrosu
standart abort() ilevini ararak program sonlandrr.
abort ilevi tpk standart exit() ilevi gibi arld zaman program sonlandrr. Ancak
exit ilevinin yapt bir takm geri alma ilemlerini abort ilevi yapmaz. abort ilevi
program sonlandrdnda stdout akmna "Abnormal program termination" yazsn
basar. assert makrosu baarsz olduunda abort ilevini armadan nce doruluu
snanan ifadeyi ve assert ileminin hangi dosyada ve satrda olduu bilgisini ekrana
yazar:
#include <stdio.h>
#include <assert.h>
int main()
{
int x, y;
printf("iki sayi giriniz : ");
scanf("%d%d", &x, &y);
assert(y != 0);
printf("%d / %d = %d\n", x, y, x / y);
}
return 0;
503
assert makrolar kaynak koddan silinir. Btn assert makrolarn kaynak koddan silmek
iin programn kaynak kodun tepesine
#define NDEBUG
komutu yerletirilmelidir.
504
505
LEV GSTERCLER
altrlabilen bir programn diskteki grntsne program denir. Bir program
altrlmak zere bellee yklendiinde, yani alr duruma getirildiinde process ya da
task biiminde isimlendirilir. Yani program diskteki durumu, process ise almakta olan
bir program anlatr.
altrlabilen bir program ierik olarak ksmdan oluur:
altrlabilen bir dosyann iletim sistemi tarafndan altrlabilmesi iin, nce diskten
RAM'e yklenmesi gerekir. letim sisteminin program diskten alp bellee ykleyip
altran ksmna ykleyici (loader) denir. Normal olarak bir programn bellee
yklenmesi iin, programn toplam byklnn bellekteki bo alandan kk olmas
gerekir. Ancak UNIX, Windows gibi sistemlerde programlar kk bir bellek altnda
altrlabilir.
Bu zellie "sanal bellek" (virtual memory) zellii denir. Sanal bellek kullanm ile nce
programn kk bir ksm RAM'e yklenir. letim sistemi, program alrken programn
aknn kod ya da data bakmndan RAM'de olmayan bir ksma getiini anlar. Programn
RAM'deki ksmn boaltarak bu kez gereken ksmn RAM'e ykler. Yani program paral
olarak altrlr. DOS iletim sistemi sanal bellek kullanm yoktur. DOS sisteminin
program altrmaktaki bellek miktar en fazla 640KB kadardr.
C'deki tm statik mrl varlklar, yani global deikenler, statik yerel deikenler,
dizgeler altrlabilir dosyann "veri" (data) blmne yerletirilir. Statik mrl
deikenler bu blme ilkdeerleriyle yerletirilir. Yerel deikenler ile parametre
deikenleri altrlabilir dosyann "yn" (stack) blmnde geici olarak yaratlrlar.
Programn tm ilevleri derlenmi olarak "kod" (code) blmnde bulunur.
Nesnelerin nasl adresleri varsa ilevlerin de adresleri vardr. Bir ilevin adresi, o ilevin
makine kodlarnn bellekteki balang adresidir. levlerin balang adresleri ilev
gstericileri denilen zel gstericilere yerletirilebilir. Bir ilev gstericisine geri dn
deeri ve parametreleri belirtilen trden olan bir ilevin adresi atanabilir.
Yukardaki deyim ile ismi fp olan bir ilev gsterici deikeni tanmlanyor. fp gsterici
deikeni, geri dn deeri int trden olan, parametre deikeni olmayan bir ilevi
gsterebilir. Bir baka deyile, fp gsterici deikenine geri dn deeri int trden olan,
parametre deikeni olmayan bir ilevin adresi atanabilir.
Tanmlamada '*' atomu ve bunu izleyen gsterici isminin ayra iine alndn
gryorsunuz. Bu ayracn soluna ilev gstericisinin gsterecei ilevin geri dn
deerinin tr yazlr. Ayracn sandaki ayra iinde, ilev gstericisinin gsterecei
ilevin parametre deikenlerinin tr yazlr.
Bir baka rnek:
int (*fptr) (int, int);
507
Yukarda tanmlanan fptr isimli gsterici deikene geri dn deeri int trden olan,
parametreleri (int, int) olan bir ilevin adresi atanabilir.
Tanmlamada gsterici deikenin ismi eer ayra iine alnmazsa, bir ilev gstericisi
tanmlanm olmaz. Bir adres trne geri dnen bir ilev bildirilmi olur:
int *f(int, int);
Yukarda, ismi f olan geri dn deeri (int *) tr olan parametreleri (int, int) olan bir
ilev bildirilmitir.
lev simleri
Bir dizi isminin, bir ileme sokulduunda, otomatik olarak dizinin ilk elemannn adresine
dntrldn biliyorsunuz. Benzer ekilde bir ilev ismi de, bir ileme sokulduunda
derleyici tarafndan otomatik olarak ilgili ilevin adresine dntrlr. lev isimlerine,
yazlmsal olarak ilevlerin adresleri gzyle baklabilir.
Bir ilev ismi adres ilecinin de terimi olabilir. Bu durumda yine ilevin adresi elde edilir.
Yani func bir ilev ismi olmak zere
func
ile
&func
edeer ifadeler olarak dnlebilir. Her iki ifade de func ilevinin adresi olarak
kullanlabilir.
lev ar leci
ilev ar ileci tek terimli sonek konumunda bir iletir. le ncelik tablosunun en st
dzeyindedir. Bu ilein terimi bir ilev adresidir. le, programn akn o adrese
yneltir, yani o adresteki kodu altrr. rnein:
func()
Burada ilev ar ilecinin terimi func adresidir. le, func adresinde bulunan kodu, yani
func ilevini altrr. lev ar ilecinin rettii deer, arlan ilevin geri dn
deeridir. rnein:
a = add(10, 20);
Burada add ifadesinin tr geri dn deeri int parametresi (int, int) olan bir ilev
adresidir. Yani add ifadesinin
int (*)(int, int)
trne dntrlr.
add(10, 20)
gibi bir ilev arsnn oluturduu ifade ise int trdendir.
Bir ilev gstericisinin gsterdii ilev iki ayr biimde arlabilir. pf bir ilev gstericisi
deiken olmak zere, bu gstericinin gsterdii ilev
pf()
508
biiminde, ya da
(*pf)()
biiminde arlabilir.
Birinci biim daha doal grnmekle birlikte, pf isminin bir gsterici deikenin ismi mi,
yoksa bir ilevin ismi mi olduu ok ak deildir.
kinci biimde, *pf ifadesinin ncelik ayrac iine alnmas zorunludur. nk ilev ar
ilecinin ncelik seviyesi, ierik ilecinin ncelik seviyesinden daha yksektir.
Aadaki program derleyerek altrn:
#include <stdio.h>
void func()
{
printf("func()\n");
}
int main()
{
void (*pf)(void) = func;
pf();
(*pf)();
}
return 0;
main ilevi iinde tanmlanan pf isimli ilev gstericisi deikene func isimli ilevin adresi
atanyor. Daha sonra pf'nin gsterdii ilev, yani func ilevi iki ayr biimle arlyor.
Bir ilev gstericisi gsterici aritmetii kurallarna uymaz. lev adresleri dier adresler
gibi tamsaylarla toplanamaz. lev gstericileri ++ ya da -- ilecinin terimi olamaz.
Zaten gsterici aritmetiinin ilev adresleri iin bir anlam da olamazd. Bir ilev adresine
bir tamsay toplayp bellekte bir sonraki ilevin adresine ulamak nasl mmkn olurdu?
lev adresleri ierik ilecinin terimi olabilir. Byle ifadeler ile, yalnzca ilev ars
yaplabilir. Bu gstericiler karlatrma ilelerinin de terimi olabilir. rnein, iki ilev
gstericisinin ayn ilevi gsterip gstermedii == ya da != ileleriyle karlatrlabilir.
Bir ilevin parametre deikeni ilev gstericisi olabilir. Aadaki rnei inceleyin:
#include <stdio.h>
void func(void (*pf)(void))
{
pf();
}
void f1()
{
printf("f1()\n");
}
void f2()
{
printf("f2()\n");
}
509
int main()
{
func(f1);
func(f2);
return 0;
}
Yukardaki programda, func isimli ilevin parametre deikeni bir ilev gstericisidir. func
ilevinin parametre deikeni olan gsterici, geri dn deeri ve parametre deikeni
olmayan bir ilevi gsterebilir. levlerin parametre deikenleri, deerlerini ilev
arlarndaki argmanlardan aldna gre func ilevine argman olarak, geri dn
deeri ve parametre deikeni olmayan bir ilev adresi gnderilmelidir. func ilevi iinde,
ilevin parametresi olan fp isimli gstericinin gsterdii ilev arlr.
f1 ve f2 geri dn deeri ve parametre deikeni olmayan ilevlerdir. main ilevi iinde
func ilevinin nce f1 ilevin adresi ile daha sonra da f2 ilevinin adresi ile arldn
gryorsunuz. Bu durumda
func(f1);
arsyla nce func ilevi arlr. func ilevi iinde de f1 ilevin arlmas salanr.
lev gstericileri, bir ilevin belirli bir ksmnn dardan deitirilmesine olanak salar.
Yani ilev, bir amac gerekletirecek belirli ilemleri yapmak iin, dardan adresini ald
bir ilevi arabilir. Bylece ilevi aran kod paras, ard ilevin iinin belirli bir
ksmnn, kendi istedii gibi yaplmasn salayabilir.
Bylece yazlan kaynak kod miktar da azalabilir. Kaynak kodlarnn byk bir ksm ayn
olan ok sayda ilev yazmak yerine, ilev gstericisiyle arlan tek bir ilev yazmak
mmkn olabilir. lev gstericileri ile arlan ilevler, genelletirilmi ilevlerdir.
Dardan adreslerini aldklar ilevlerin arlmalaryla ilevleri daha zel hale getirilmi
olur.
Baz ilevler, zellikle gsterici parametresi alan ilevler, belirli bir tre dayal olarak
alr. rnein, elemanlar int trden olan bir diziyi sralayan bir ilev, elemanlar double
trden olan bir diziyi sralayamaz. Bu tr durumlarda ayn kodu, farkl trlere gre
yeniden yazmak gerekir. Trden bamsz olarak tek bir ilevin yazlabilmesi mmkn
olabilir. Trden bamsz ilem yapan ilevlerin gsterici parametreleri void * trnden
olmaldr. Ancak parametrelerin void * trnden olmas yalnzca ar asndan kolaylk
salar. levi yazacak olan programc, yine de ileve geirilen adresin trn saptamak
zorundadr. Bunun iin ileve bir tr parametresi geirilebilir. rnein herhangi bir trden
diziyi sralayacak ilevin bildirimi aadaki gibi olsun:
void *gsort(void *parray, size_t size, int type);
imdi ilevi yazacak kii type isimli parametreyi bir switch deyiminin ayrac iine alarak,
dardan adresi alnan dizinin trn saptayabilir. Bu yntem C'nin doal trleri iin
alsa da programcnn kendi oluturduu trden diziler iin doru almaz. Byle genel
ilevler ancak ilev gstericileri kullanlarak yazlabilir.
imdi bir dizinin en byk elemann adresiyle geri dnen bir ilevi trden bamsz olarak
yazmaya alalm. Dizi trnden bamsz olarak ilem yapan ilevlerin, genel parametrik
yaps genellikle aadaki gibi olur:
Dizinin balang adresini almak iin, void trden bir gsterici.
Dizinin eleman saysn alan, size_t trnden bir parametre.
510
Dizinin bir elemann byte uzunluunu yani sizeof deerini alan size_t trnden bir
parametre.
Dizinin elemanlarn karlatrma amacyla kullanlacak bir ilevin balang adresini alan,
ilev gstericisi parametre.
Bir dizinin en byk elemannn adresini bulan genel ilevin parametrik yaps yle
olabilir:
void *get_max(const void *pArray, size_t size, size_t width, int
(*cmp)(const void *, const void *));
Bu tr ilevlerin tasarmndaki genel yaklam udur:
lev herbir dizi elemannn adresini, gsterici aritmetiinden faydalanarak bulabilir.
Ancak dizi elemanlarnn tr bilinmediinden, ilev dizinin elemanlarnn karlatrma
ilemini yapamaz. Bu karlatrmay, ilev gstericisi kullanarak ilevi aran kod
parasna yaptrr. levi aracak programc, karlatrma ilevinin dizinin herhangi iki
elemannn adresiyle arlacan gznne alarak, karlatrma ilevini yle yazar:
Karlatrma ilevi, karlatrlacak iki nesnenin adresini alr. levin birinci
parametresine adresi alnan nesne, ikinci parametreye adresi alnan nesneden daha
bykse, ilev pozitif herhangi bir deere, kkse negatif herhangi bir deere, bu iki
deer eitse sfr deerine geri dner. Bu, standart strcmp ilevinin sunduu anlamadr.
Aadaki program inceleyin:
#include <stdio.h>
#include <string.h>
#define
MAX_NAME_LEN
20
511
sizeof(Person),
(isme gore) %s %d\n",
sizeof(Person), cmp_person_no);
(numaraya gore) %s %d\n",
return 0;
}
Yukardaki programda get_max isimli ilev bir dizinin balang adresini, boyutunu,
dizinin bir elemannn uzunluu ile dizinin elemanlarn karlatrma ileminde
kullanlacak ilevin adresini alyor. lev arsyla dizi adresi void trden bir gstericiye
alnyor. Ancak ilev iinde gsterici aritmetiinden faydalanmak amacyla bu adres char
trden bir gstericiye aktarlyor. Bir elemann boyutu bilindiine gre bu deer dizinin
balang adresine eklenerek dizinin bir sonraki elemannn adresi bulunabilir deil mi?
Karlatrma ilevi, karlatrma ilemini yapabilmek iin iki nesnenin adresini alyor.
Bylece get_max ilevi iinde, karlatrma ilevi, parametre olan ilev gstericisi ile
doru argmanlarla arlabiliyor. Dizinin en byk elemannn adresini bulmak iin yine
bilinen algoritma kullanlyor. Ancak dizinin iki elemannn deerini karlatrmak iin
dardan adresi alnan ilev arlyor.
Daha sonra ayr karlatrma ilevinin tanmlanm olduunu gryorsunuz. Tm
karlatrma ilevlerinin parametre deikenleri (const void *, const void *) dr. Bylece,
bu karlatrma ilevlerine get_max ilevi iki adres gnderdiinde bir tr uyumsuzluu
sorunu ortaya kmaz.
main ilevi iinde bir int trden bir de Person trnden dizi tanmlanyor. Bu dizilere
ilkdeer verilmi olduunu gryorsunuz. Daha sonra get_max ilevi arlarak dizilerin
en byk elemanlarnn adresleri bulunuyor. cmp_person_name ilevi Person trnden iki
nesneyi isimlerine gre karlatrrken, cmp_person_no ilevi ise Person trnden iki
nesneyi, numaralarna gre karlatryor.
Her trl sralama algoritmasnda karlatrma ve deer takas etme sz konusudur. Bir
dizinin tr bilinmese bile dizinin iki eleman takas edilebilir. Karlatrma ileminini
yapacak ilevin adresi aran kod parasndan alnrsa, trden bamsz sralama ilemi
gerekletirilebilir. Aadaki rnei inceleyin:
512
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define
SIZE
10
while (nbytes--) {
temp = *p1;
*p1++ = *p2;
*p2++ = temp;
}
513
printf("\n");
for (k = 0; k < SIZE; ++k)
printf("%d ", ai[k]);
}
return 0;
g_swap isimli ilev, balang adreslerini ald belirli uzunlukta iki bellek blounun
ieriini takas ediyor. Bu ilev, trden bamsz sralama ilemini gerekletiren bsort
isimli ilev tarafndan arlyor. bsort ilevinin parametrik yapsnn, daha nce yazlan
get_max isimli ileve benzer olduunu gryorsunuz. levin birinci parametresi
sralanacak dizinin adresi, ikinci parametresi dizinin eleman says, nc parametresi
dizinin bir elemannn uzunluu ve drdnc parametresi ise dizinin elemanlarn
karlatrma ilemini gerekletirecek ilevin adresidir.
bsort ilevi iinde "kabarck sralamas" algoritmas kullanlyor. Dizinin ardk iki
elemannn byklk-kklk iliikisi dardan adresi alnan ilevin, parametre deikeni
olan gsterici ile arlmasyla elde ediliyor. Eer dizinin bu iki eleman doru yerde
deilse bu elemanlar daha nce tanmlanan gswap ilevinin arlmasyla takas ediliyor.
Byle bir ilevle her trden dizi sraya sokulabilir. bsort ilevi yalnzca karlatrma
ilemini gerekletirecek uygun bir ilevin adresine gereksinim duyar. nk bu ilev
dardan adresi gelen dizinin elemanlarnn trn bilemez.
lev gstericileri tanmlarken parametre ayracnn iinin bo braklmas ile parametre
ayracnn ierisine void yazlmas farkl anlamlardadr. Parametre ayracnn ii bo
braklrsa, byle tanmlanan ilev gstericisi deikenlere, geri dn deeri uygun
herhangi bir parametre yapsna sahip olan ilevin adresi yerletirilebilir. Oysa void
yazlmas, parametresi olmayan ilevlerin adreslerinin yerletirilebilecei anlamna gelir.
Parametre ayracnn ii bo braklarak, arlacak ilevlerin parametrik yaps dardan
istendii gibi dzenlenebilir.
[C++ dilinde parametre ayracnn iinin bo braklmas, ilev gstericisi deikenin, gsterecei ilevin parametreye sahip
olmad bilgisini iletir. C++'da ayracn iine void yazlmasyla, burann bo braklmas arasnda bir anlam fark yoktur.]
Standart qsort ilevi, yukarda tanmlanan bsort ileviyle ayn parametrik yapya sahiptir.
Yazdmz bsort ilevi kabarck sralamas algoritmasn kullanyordu. Standart qsort ilevi
ise "quick sort " algoritmasn kullanr.
qsort ilevinin stdlib.h balk dosyas iindeki bildirimi aadaki gibidir:
void qsort(void *vp, size_t size, size_t width, int (*cmp)(const void *,
const void *));
levin birinci parametresi sraya dizilecek dizinin balang adresi, ikinci parametresi
dizinin eleman says, nc parametresi dizinin bir elemann uzunluu ve son
parametresi de karlatrma ilevinin adresidir. Karlatrma ilevi, birinci parametresine
adresi gnderilen elemann deeri, ikinci parametresine adresi gnderilen elemandan
bykse pozitif herhangi bir deere, kkse negatif herhangi bir deere, eitse sfra
geri dnecek biimde yazlrsa kkten bye sraya dizme, ters yazlrsa bykten
ke sraya dizme gerekleir.
Standartlar ncesi C derleyicilerinde qsort ilevinin bildiriminde karlatrma ilevinin
adresini alacak gstericinin parametre ayracnn ii bo braklmtr. Bu durum
karlatrma ilevini yazmay kolaylatrr. stenirse qsort levinda byle bir kolaylk elde
etmek iin stdlib.h balk dosyas iinde bulunan bildirimde ilev gsterici ayrac
boaltlabilir:
void qsort(void *parray, size_t size, size_t width, void(*fp)());
514
SIZE 20
return 0;
Hatay grebildiniz mi? Karlatrma ilemini gerekletirecek cmp isimli ilev yanl
yazlm. Bu ileve char * trnden iki nesnenin adresi gnderileceine gre, ilev iinde
yaplan tr dntrme ileminde, hedef tr char* tr deil char** tr olmalyd. (char
*) trnden bir nesnenin adresi olan bilgi (char **) trndendir.
lev aadaki gibi yazlmalyd:
int cmp(const void *vp1, const void *vp2)
{
const char * const *p1 = (const char * const *)vp1;
const char * const *p2 = (const char * const *)vp2;
}
515
void(*)(void)
trdr. Bu gstericinin gsterdii ilev
fparray[2]();
biiminde ya da
(*fparray[2])() ;
biiminde arlabilir. Byle bir dizinin elemanlarna da ilkdeer verilebilir:
void (*fparray[5])(void) = {f1, f2, f3, f4, f5};
Bylece, dizinin her bir elemannn bir ilevi gstermesi salanabilir.
Artk bir dng deyimi yardmyla gsterici dizisinin elemanlarna ulalarak dizinin
elemanlarnn gsterdii ilevler arlabilir. Aadaki kodu derleyerek altrn:
#include <stdio.h>
void
void
void
void
void
f1()
f2()
f3()
f4()
f5()
{printf("f1()\n");}
{printf("f2()\n");}
{printf("f3()\n");}
{printf("f4()\n");}
{printf("f5()\n");}
int main()
{
void (*fparray[5])(void) = {f1, f2, f3, f4, f5};
int k;
for (k = 0; k < 5; ++k)
fparray[k]();
}
return 0;
Bir ilevin geri dn deeri de bir ilev adresi olabilir. Aadaki rnei inceleyin:
#include <stdio.h>
void foo()
{
printf("foo()\n");
}
void (*func(void))(void)
{
void (*fp)(void) = foo;
}
return fp;
int main()
{
func()();
return 0;
}
516
Yukardaki programda tanmlanan func isimli ilevin parametre deikeni yok. func
ilevinin geri dn deeri, -geri dn deeri ve parametre deikeni olmayan- bir ilev
adresidir. Yani func ilevi, kendisini aran kod parasna byle bir ilevin adresini
iletiyor. func ilevi iinde tanmlanan yerel fp gsterici deikeni, foo isimli ilevi
gsteriyor. main ilevi iinde yer alan
func()();
deyiminde nce func ilevi arlr. Bu ardan elde edilen geri dn deeri, foo ilevinin
adresi olur. kinci ilev ar ileci de foo ilevin arlmasn salar.
Yukardaki bildirimden sonra FuncDouble, geri dn deeri double trden olan, tek
parametresi int trden olan bir ilev trdr.
FuncDouble *fptr, *fpa[10];
Yukardaki tanmlama ile fptr, geri dn deeri double ve parametresi int trden olan bir
ilevi gsteren gsterici deikendir. fpa ise fptr gibi gstericilerin oluturduu 10
elemanl bir dizidir. fpa dizisinin herbir eleman bir ilev gstericisidir.
Elemanlar FuncDouble trden olan bir deiken tanmlanamaz. C
FuncDouble func;
/* Geersiz */
517
#include <stdio.h>
#include <string.h>
typedef char *(*FPTR)(const char *, int);
FPTR func(FPTR ptr)
{
return ptr;
}
int main()
{
char str[] = "Necati Ergin";
puts(func(strchr)(str, 'E'));
return 0;
}
Yukardaki programn altrlmasyla ekrana Ergin yazs yazdrlr.
518
ZYNELEMEL LEVLER
Bir ilev baka bir ilevi ard gibi kendisini de arabilir. Bu tr ilevlere zyinelemeli
ilev (recursive functions) denir. Algoritmalar bu zellie gre ayr gruba ayrlabilir:
123-
levin ak yledir:
n
n
n
n
n
=
=
=
=
=
5
4
3
2
1
iin
iin
iin
iin
iin
val = 5 *
val = 4 *
val = 3 *
val = 2 *
return 1;
factorial(4);
factorial(3);
factorial(2);
factorial(1);
519
putchar('\n');
520
if ((ch = getchar())!='\n')
ters_yaz();
putchar(ch);
Aada bir tamsayy yalnzca puthar ilevini kullanarak yazdran dngsel yapl bir ilev
tanmlanyor:
#include <stdio.h>
#define
SIZE
100
void printd(int i)
{
char s[SIZE];
int k = 0;
int fnegative = 0;
if (i < 0) {
fnegative = 1;
i = -i;
}
do {
s[k++] = i % 10 + '0';
i /= 10;
} while(i);
if(fnegative)
s[k++] = '-';
for (k--; k >= 0; --k)
putchar(s[k]);
}
lev zyinelemeli olarak aadaki biimde tanmlanabilir:
#include <stdio.h>
void print(int n)
{
if (n < 0) {
putchar('-');
n = -n;
}
if (n / 10)
print(n / 10);
putchar(n % 10 + '0');
}
Aada bir tamsaynn belirli bir tamsay ssne geri donen power isimli ilev
zyinelemeli olarak yazlyor:
int power(int base, int exp)
{
if (exp == 0 )
return 1;
}
521
Aada iki tamsaynn ortak blenlerinden en byne geri dnen get_gcd isimli ilev
zyinelemeli olarak tanmlanyor:
int get_gcd(int a, int b)
{
if (a >= b && a % b == 0)
return b;
if (a < b)
return(get_gcd(b, a));
return get_gcd(b, a % b);
}
Aada tanmlanan fibonacci isimli ilev fibonacci serisinin n. terimine geri dnyor:
int fibonacci(int n)
{
if (n == 1 || n == 2)
return 1;
return fibonacci(n - 1) + fibonacci(n - 2);
}
Ayn ii yapan dngsel yapda bir ilev ok daha verimli olurdu:
int fibonacci(int n)
{
int x = 1;
int y = 1;
int k, result;
for (k = 2; k < n; ++ k) {
result = x + y;
x = y;
y = result;
}
return y;
Aada zyinelemeli olarak tanmlanan rakam_yaz ilevi bir tamsaynn her bir
basaman ekrana yaz olarak basyor:
#include <stdio.h>
void rakam_yaz(int n)
{
static const char *rakamlar[ ] = { "Sifir", "Bir", "Iki", "Uc", "Dort",
"Bes", "Alti", "Yedi", "Sekiz", "Dokuz" };
if (n > 9)
rakam_yaz(n / 10);
printf("%s ", rakamlar[n % 10]);
Aada bir tamsayy ekrana onaltlk say sisteminde yazdran to_hex isimli zyinelemeli
bir ilev tanmlanyor:
void to_hex(int n)
522
static const char *htab[ ] = { "0", "1", "2", "3", "4", "5", "6", "7",
"8", "9", "A", "B", "C", "D", "E", "F" };
if (n > 15)
to_hex(n / 16);
printf("%s", htab[n % 16]);
}
Aada bir yazy ters eviren strrev_r isimli bir ilev tanmlanyor.
#include <stdio.h>
#include <stdlib.h>
#define SIZE
100
swapstr(str, l + 1, r - 1);
return str;
int main()
{
char str[SIZE];
printf("bir yazi girin : ");
gets(str);
printf("yazi = (%s)\n", str);
strrev_r(str);
printf("yazi = (%s)\n", str);
}
return 0;
523
#include <stdio.h>
#include <string.h>
#define MAX_WORD_LEN 100
#define ARRAY_SIZE 1000
void revprint(const char *str)
{
char s[MAX_WORD_LEN + 1] = "";
int index = 0;
static const char seps[] = " \n\t,.:;!?";
while (*str && strchr(seps, *str))
str++;
if (!*str)
return;
while (!strchr(seps, *str))
s[index++] = *str++;
s[index] = '\0';
if (*str == '\0') {
printf("%s ", s);
return;
}
revprint(str);
printf("%s ", s);
}
int main()
{
char str[ARRAY_SIZE];
printf("bir cumle girin : ");
gets(str);
revprint(str);
return 0;
524
525
bir argman gderilmedii anlalrt. Yani ileve gnderilen son deerin -1 olmas
gerektii ilevin arayznn bir paras oluyor.
lev tanm iinde nce va_list trnden va isimli bir deikenin tanmlandn
gryorsunuz. va_start makrosuna va deikeni ve ilevin parametre deikeni val
argman olarak gnderiliyor. Bu arya karlk gelen isel kodun almasyla, va
deikeni argman listesinde uygun argman gsterecek ekilde konumlandrldn
dnebilirsiniz. Bylece artk ileve gnderilen argmanlarn deeri elde edilebilir.
va_arg Makrosu
va_arg makrosu ileve gnderilen argmanlarn deerlerini elde etmek iin kullanlr.
Bu makro bir ilev gibi dnldnde aadaki gibi gsterilebilir:
tr va_arg (va_list ap, tr);
va_list trnden deikenimiz va_start makrosu ile ilkdeerini aldktan sonra bu makro
senee bal tm argmanlar elde etmek iin arlr. Bu makrodan elde edilen
seenee bal argmann deeridir. Makronun almas sonucu argman listesi
gstericisi de bir sonraki argman gsterecek biimde konumlandrlm olur.
Bu makroya yaplan bir sonraki ar ile bir sonraki seimlik argmann deeri elde edilir.
Makroya gnderilecek ikinci bilgi, deeri elde edilecek argmann trdr. rnein
seimlik argman int trdense makronun ikinci argmanna
int
geilmelidir. Yeniden add_all ilevine dnyoruz:
int add_all(int val, ...)
{
va_list va_p;
int sum = 0;
va_start(va_p, val);
while (val != -1) {
sum += val;
val = va_arg(va_p, int);
}
/***/
}
ilevin tanmnda val deikeni -1 olmad srece dnen bir dng oluturuluyor.
Dngnn her turunda val deikeninin deeri sum deikenine katlyor ve val
deikenine seimlik argmanlardan bir sonrakinin deeri aktarlyor.
val = va_arg(va_p, int);
va_arg makrosunun ilk defa kullanlmasyla seimlik ilk argman, 2. kez kullanlmasyla
seimlik 2. argman vs. elde edilir. Eer belirli bir argmandan sonras elde edilmek
istenmiyorsa va_arg arlar sonuna kadar yaplmak zorunda deildir.
leve ar ile gnderilen argman saysndan daha fazla sayda argman elde etmeye
allrsa, yani va_arg makrosu olmas gerekeneden daha fazla arlmaya allrsa p
deerler elde edilir.
va_end Makrosu
526
Deiken sayda argman alan ilevlere yaplan arlar derleyici tarafndan zel biimde
deerlendirilir. int alt trlerden olan argmanlar otomatik olarak int trne ykseltilir.
float trnden olan argmanlar double trne ykseltilir:
#include <stdio.h>
int add_all(int val, ...);
int main()
{
short sh = 5;
printf("toplam1 = %d\n", add_all(23, 12, 30, 14, -1));
printf("toplam2 = %d\n", add_all('A', 10, sh, 20, 30, 40, -1));
return 0;
}
imdi kendisine adresleri gnderilen yazlar birletirecek ve deiken sayda argmanla
arlabilecek bir ilev tanmlanyor:
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#define
BUFFER_SIZE
1000
527
char *pd;
int k;
char buffer[BUFFER_SIZE] = {'\0'};
va_start (ap, ptr);
while (ptr){
strcat(buffer, ptr);
ptr = va_arg(ap, const char *);
}
pd = (char *)malloc(strlen(buffer) + 1);
if (pd == NULL) {
printf("bellek yetersiz!\n");
exit(EXIT_FAILURE);
}
va_end(ap);
return strcpy(pd, buffer);
}
int main ()
{
char *pstr = con_strings("C ", "ve ", "Sistem ", "Programcilari ",
"Dernegi", NULL);
puts(pstr);
free(pstr);
}
return 0;
528
Kaynaklar
1.American National Standard for Programming Languages - C
ANSI/ISO 9989-1990
2. C - A Reference Manual.
Samuel P. Harbison III - Guy L. Steele Jr.
Prentice-Hall, Inc 2002
ISBN 0-13-089592x
3. The Standard C Library
P. J. Plaguer
Prentice Hall P T R
ISBN 0-13-131509-9
4. C Programming FAQ's
Steve Summit
Addison Wesley
529