You are on page 1of 390

PROGRAMLAMA VE C

1 . BLM :
Yazlm Nedir

PROGRAMLAMA VE C

Yazlm (software) programlama ve programlamayla ilgili konularn geneline verilen isimdir. Yazlm denince ilk olarak aklmza programlama dilleri, bu diller kullanlarak yazlm kaynak programlar ve eitli amalar iin oluturulmu dosyalar gelir. Donanm Nedir Donanm (hardware) : Bilgisayarn elektronik ksm, yapsna verilen isimdir. Yazlmn Snflandrlmas Yazlm uygulama alanlarna gre 5 gruba ayrabiliriz : 1. Bilimsel ve mhendislik yazlmlar (scientific & engineering software). Bilimsel ve mhendislik konularndaki problemlerin zlmesinde kullanlan programlardr. Bu tr programlarda veri miktar greli olarak dktr ancak matematiksel ve istatistiksel algoritmalar youn olarak kullanlabilir.Tamamen hesaplama arlkl ilemler ierir. Bu tr programlar arlkl olarak bilgisayarn Merkezi lem Birimini (CPU) kullanrlar. Elektronik devrelerin zmn yapan programlar, istatistik analiz paketlerini bu tr programlara rnek olarak verebiliriz. 2. Mesleki yazlmlar (Business software). Veri taban arlkl yazlmlardr. Genel olarak verilerin yaratlmas, ilenmesi ve dosyalarda saklanmas ile ilgilidir. Bu tr programlara rnek olarak stok kontrol programlar, mteri takip programlar, muhasebe programlarn verebiliriz. 3. Yapay zeka yazlmlar (artificial intelligence software). nsan davranlarn taklit etmeyi amalayan yazlmlardr. rnek olarak robot yazlmlar, satran ya da bri oynatan programlar vs. verilebilir. 4. Grntsel yazlmlar. Grntsel ilemlerin ve algoritmalarn ok youn olarak kullanld programlardr. rnek olarak oyun ve animasyon yazlmlarn verebiliriz. Bu yazlmlar arlkl olarak bilgisayarn grafik arabirimini kullanrlar. 5. Sistem yazlmlar (system software): Bilgisayarn elektronik yapsn yneten yazlmlardr. Derleyiciler, haberleme programlar, iletim sistemi birer sistem yazlmdr. rnein text editr de bir sistem yazlmdr. Uygulama programlarna gre daha dk seviyeli ilem yaparlar. Programlama Dillerinin Snflandrlmas Programlama dillerini eitli alardan snflandrabiliriz. En sk kullanlan snflandrmalar: 1. Seviyelerine gre snflandrma. 2. Uygulama alanlarna gre snflandrma. Bilgisayar Dillerinin Seviyelerine Gre Snflandrmas ve Seviyelerine Gre Bilgisayar Dillerinin Geliimi Bir programlama dilinin seviyesi deyince o programlama dilinin insan algsna olan yaknlnn derecesini anlyoruz. Bir programlama dili insan alglasna ne kadar yaknsa o kadar yksek seviyeli demektir (high level). Yine bir programlama dili bilgisayarn elektronik yapsna ve alma biimine ne kadar yaknsa o
1

1 kadar dk seviyeli (low level) demektir. Yksek seviyeli dillerle almak programc asndan kolaydr. Algoritma yoktur. Bu dillerde yalnzca nelerin yaplaca programa bildirilir ama nasl yaplaca bildirilmez. Genel olarak programlama dilinin seviyesi ykseldike , o dilin renilmesi ve o dilde program yazlmas kolaylar. Bir bilgisayar yalnzca kendi makine dilini dorudan anlayabilir. Makine dili bilgisayarn doal dilidir ve bilgisayarn donanmsal tasarmna baldr. Bilgisayarlarn gelitirilmesiyle birlikte onlara i yaptrmak iin kullanlan ilk diller de makine dilleri olmutur. Bu yzden makine dillerine 1. kuak diller de diyebiliriz. Makine dilinin programlarda kullanlmasnda karlalan iki temel problem vardr. Makine dilinde yazlan kodlar dorudan makinann ilemcisine, donanm paralarna verilen komutlardr. Deiik bir CPU kullanldnda ya da bellek organizasyonu farkl bir ekilde yapldnda artk program almayacak ve programn tekrar yazlmas gerekecektir. nk makine dili yalnzca belirli bir CPU ya da CPU serisine uygulanabilir. Makine dili tanabilir (portable) deildir. Dier nemli bir problem ise, makine dilinde kod yazmann ok zahmetli olmasdr.Yazmann ok zaman alc ve uratrc olmasnn yan sra yazlan program okumak ya da alglamak da o denli zordur. zellikle program boyutu bydnde artk makine dili programlarn gelitirmek, daha bytmek iyice karmak bir hale gelir. Balangta yalnzca makine dili vard. Bu yzden makine dilleri 1. kuak diller olarak da isimlendirilir. Yazlmn ve donanmn tarihsel geliimi ierisinde makine dilinden, insan alglamasna ok yakn yksek seviyeli dillere (4. kuak diller) kadar uzanan bir sre sz konusudur. Bu tarihsel sreci ana hatlaryla inceleyelim : 1950 li yllarn hemen balarnda makine dili kullanmn getirdii problemleri ortadan kaldrmaya ynelik almalar younlat. Bu yllarda makine dilleri bilgisayarn ok snrl olan belleine ykleniyor ve programlar byle altrlyordu. lk nce makine dilinin alglanma ve anlalma zorluunu ksmen de olsa ortadan kaldran bir adm atld. Sembolik makine dilleri gelitirildi. Sembolik makine dilleri (Assembly languages) yalnzca 1 ve 0 dan oluan makine dilleri yerine ngilizce baz ksaltma szcklerden oluuyordu. Sembolik makine dillerinin kullanm ksa srede yaygnlat. Ancak sembolik makine dillerinin makine dillerine gre ok nemli bir dezavantaj sz konusuydu. Bu dillerde yazlan programlar makine dilinde yazlan programlar gibi bilgisayarn belleine ykleniyor ancak programn altrlma aamasnda yorumlayc (interpreter) bir program yardmyla sembolik dilin komutlar, bilgisayar tarafndan komut komut makine diline evriliyor ve oluan makine kodu altrlyordu. Yani bilgisayar, program alma aamasnda nce yorumluyarak makine diline eviriyor daha sonra makine diline evrilmi komutlar icra ediyordu. Bu ekilde altrlan programlarn hz neredeyse 30 kat yavalyordu. Bu dnemde zellikle iki yorumlayc program ne kmt: John Mauchly nin UNIVAC 1 iin yazd yorumlayc (1950) ve John Backus tarafndan 1953 ylnda IBM 701 iin yazlan "Speedcoding" yorumlama sistemi. Bu tr yorumlayclar makine koduna gre ok yava alsalar da programclarn verimlerini artryorlard. Ama zellikle eski makine dili programclar yorumlayclarn ok yava olduklarn, yalnzca makine dilinde yazlanlarn gerek program deneceini sylyorlard. Bu sorunun da stesinden gelindi. O zamanlar iin ok parlak kabul edilebilecek fikir uydu: Her defasnda yazlan kod, kodun altrlmas srasnda makine diline evireceine, gelitirilecek bir baka program sembolik dilinde yazlan kodu bir kez makine diline evirsin ve artk program ne zaman altrlmak istense, bilgisayar yorumlama olmakszn yalnzca makine kodunu altrsn. Bu fikiri gelitiren Grace Hopper isimli bir bayand. Grace Hopper'n buluuna "compiler" derleyici ismi verildi. (Grace Hopper ayn zamanda Cobol dilini gelitiren ekipten biridir, bug(bcek) szcn ilk olarak Grace Hopper kullanmtr.) Artk programclar sembolik szcklerden oluan Assembly programlarn kullanyor. Yazdklar programlar derleyici tarafndan makine koduna dntrlyor ve makine kodu

PROGRAMLAMA VE C eski hzndan birey kaybetmeksizin 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 bir ok komut gerekmesi, programlama prosesini daha hzl bir hale getirmek iin araylar balatm, bunun sonucunda da daha yksek seviyeli programlama dilleri gelitirilmeye balanmtr. Tarihsel sre iinde Assembly dillerinden daha sonra gelitirilmi ve daha yksek seviyeli diller 3. kuak diller saylmaktadr. Bu dillerin hepsi algoritmik dillerdir. Bugne kadar gelitirilmi olan yzlerce yksek seviyeli programlama dilinden yalnzca pek az bugne kadar varlklarn srdrebilmitir: FORTRAN dili (FORmula TRANslator) kompleks matematiksel hesaplamalar gerektiren mhendislik ve bilimsel uygulamalarda kullanlmak zere 1954 - 1957 yllar arasnda IBM firmas iin John Backus tarafndan gelitirilmitir. FORTRAN dili, youn matematik hesaplamalarn gerektii bilimsel uygulamalarda halen yaygn olarak kullanlmaktadr. FORTRAN dilinin FORTRAN IV ve FORTRAN 77 olmak zere iki nemli versiyonu bulunmaktadr. Doksanl yllarn balarnda FORTRAN - 90 isimli bir versiyon iin ISO ve ANSI standartlar kabul edilmitir. FORTRAN dili 3. seviye dillerin en eskisi kabul edilmektedir. COBOL (COmmon Business Oriented Language) 1959 ylnda, Amerika'daki bilgisayar reticileri, zel sektr ve devlet sektrndeki bilgisayar kullanclarndan oluan bir grup tarafndan gelitirilmitir. COBOL'un gelitirilme amac veri ynetimi ve ilemenin gerektii ticari uygulamalarda kullanlacak tanabilir bir programlama dili kullanmaktr. COBOL dili de halen yaygn olarak kullanlmaktadr. ALGOL (The ALGOritmick Language) 1958 ylnda Avrupa'da bir konsorsiyum tarafndan gelitirilmeye balanmtr. IBM Firmas FORTRAN dilini kendi donanmlarnda kullanlacak ortak programlama dili olarak benimsediinden, Avrupa'llar da alternatif bir dil gelitirmek istemilerdi. ALGOL dilinde gelitirilen bir ok prensip modern programlama dillerinin hepsinde kullanlmaktadr. 60'l yllarn balarnda programlama dilleri zerinde yaplan almalar yapsal programlama kavramn gndeme getirmitir. Bu dillerden bazlarna ksaca gz atalm: PASCAL dili 1971 ylnda akademik evrelere yapsal programlama kavramn tantmak iin Profesr Niclaus Wirth tarafndan gelitirilmi (Dilin yaratcs, dile matematiki ve filozof Blaise Pascal'n ismini vermitir.) ve bu dil ksa zaman iinde niversitelerde kullanlan programlama dili haline gelmitir. Pascal dilinin ticari ve endstriyel uygulamalar desteklemek iin sahip olmas gereken bir takm zelliklerden yoksun olmas bu dilin bu uygulamalarda fazla kullanlmamasna yol amtr. Modula ve Modula-2 dilleri Pascal dili baz alnarak gelitirilmitir. BASIC dili 1960'l yllarn ortalarnda John Kemeney ve Thomas Kurtz tarafndan gelitirilmitir. Her ne kadar BASIC isminin "Beginner's All_purpose Symbolic Instruction Code" szcklerinin ba harflerinden oluturulduu sylense de, bu szcklerin daha sonradan uydurulduu aktr. Yksek seviyeli dillerin en eski ve en basit olanlarndan biridir.Tm basitliine karn, bir ok ticari uygulamada kullanlmtr. BASIC dili de ANSI tarafndan standartlatrlmtr. Ancak BASIC dilinin ilave zellikler ieren bir sr versiyonu sz konusudur. rnein Microsoft firmasnn kartt Visual Basic diline Nesne Ynelimli Programlamaya ilikin birok zellik eklenmitir. Ayrca BASIC dilinin baz versiyonlar uygulama programlarnda (rnein MS Excel ve MS Word'de) kullancnn zelletirme ve otomatikletirme amacyla yazaca makrolarn yazlmasnda kullanlan programlama dili olarak da genel kabul grmtr.

1 ADA dili ise Amerikan Savunma Departman (Department of Defence -DoD) destei ile 70 li yllar ve 80'li yllarn balarnda gelitirilmitir. 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 baz olarak alan almalar sonucunda Ada dilini gelitirdi. Ada dilinin dkmanlar 1983 ylnda yaymlanmtr.(Ada ismi, air Lord Byron'un kz olan Lady Ada Lovelace'n isminden alntdr. Ada Lovelace delikli kartlar hesap makinalarnda ilk olarak kullanlan Charles Babbage'in yardmcsyd. Charles Babbage hayat boyunca "Fark makinas" (Difference Engine) ve "Analitik Makine" (Analytical Engine) isimli makinalarn yapm zerinde alt ama bu projelerini gerekletiremeden ld. Yine de gelitirdii tasarmlar modern bilgisayarlarn atas kabul edilmektedir. Ada Lovelace Charles Babbage'n makinas iin delikli kartlar ve kullanlacak algoritmalar hazrlyordu. Bu bayann 1800'l yllarn banda ilk bilgisayar programn yazd kabul edilmektedir.) Ada dili genel amal bir dildir, ticari uygulamalardan roketlerin ynlendirilmesine kadar birok farkl alanda kullanlmaktdr. Dilin nemli zelliklerinden bir tanesi gerek zaman uygulamalarna (real-time applications / embedded systems) destek vermesidir. Baka bir zellii de yksek modlaritesi nedeniyle byk programlarn yazmn kolaylatrmasdr. Ancak byk ve karmak derleyicilere ihtiya 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 olarak ksaltlrlar. (fourth generation language). nsan algsna en yakn dillerdir. RPG dili 4. kuak dillerin ilki olarak kabul edilebilir.zellikle kk IBM makinalarnn kullanclar olan irketlerin, rapor retimi iin basit bir dil istemeleri zerine IBM firmas tarafndan gelitirilmitir. Programlama dillerini seviyelerine gre 5 ana gruba ayrabiliriz: 1. ok yksek seviyeli diller ya da grsel diller (visual languages): Access, Foxpro, Paradox, Xbase, Visual Basic, Oracle Forms.

2. Yksek seviyeli diller (Bunlara algoritmik diller de denir): Fortran, Pascal, Basic, Cobol. 3. Orta seviyeli programlama dilleri: Ada, C. Orta seviyeli diller daha az kaypla makine diline evrilebildiinden daha hzl alr. 4. Alak seviyeli programlama dilleri: Sembolik makine dili (Assembly language). 5. Makine dili: En aa seviyeli programlama dili. (Saf makine dili tamamen 1 ve 0 lardan oluuyor.) Uygulama Alanlarna Gre Snflandrma 1. Bilimsel ve mhendislik uygulama dilleri: Pascal, C (C programlama dili niversitelerdeki akademik almalarda da youn olarak kullanlyor.), FORTRAN

2. Veri taban dilleri: XBASE, (Foxpro, Dbase, CA-Clipper), Oracle Forms, Visual Foxpro. 3. Genel amal programlama dilleri: Pascal, C, Basic.

PROGRAMLAMA VE C 4. Yapay zeka dilleri: Prolog, Lisp. 5. Simulasyon dilleri GPSS, Simula 67 6. Makro Dilleri (Scripting languages) awk, Perl, Python, Tcl, JavaScript. 7. Sistem programlama dilleri: Sembolik makine dilleri, BCPL, C, C++, occam. Gnmzde sistem yazlmlarn neredeyse tamamnn C dili ile yazldn syleyebiliriz. rnek vermek gerekirse UNIX iletim sisteminin % 80'i C dili ile geri kalan ise sembolik makine dili ile yazlmtr. Bu iletim sistemi ilk olarak BELL labaratuarlarnda 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. Programlama Dillerinin Deerleme ltleri Kaynaklar u an halen kullanmda olan yaklak 1000 - 1500 programlama dilinin varlndan sz ediyor. Neden bu kadar fazla programlama dili var? Bu kadar fazla programlama dili olmasna karn neden halen yeni programlama dilleri tasarlanyor? Bir programlama dilini dierine ya da dierlerine gre daha farkl klan zellikler neler olabilir? Bir programlama dilini tanmlamak istesek hangi sfatlar kullanabiliriz? Programlama dilleri hakkndaki bu sorulara yant verebilmemiz iin elimizde deerlendirme yapmamza olanak salayacak ltler olmaldr. Bu ltleri ksaca inceleyelim:

Verimlilik (efficiency) Bu zellie programn hzl alma zellii diyebiliriz. Programn alma hz pek ok faktre baldr. Algoritmann da hz zerinde etkisi vardr. almann yapld bilgisayarn da doal olarak hz zerinde etkisi vardr. Verimlilii bir programlama dilinde yazlm bir programn hzl almas ile ilgili bir kavram olarak dnebiliriz. Bu adan bakldnda C verimli bir dildir. Veri trleri ve yaplar (data types and structures) eitli veri trlerini (tamsay, gerek say, karakter...) ve veri yaplarn (diziler, yaplar vs.) destekleme yeteneidir. Veri yaplar, veri trlerinin oluturduu mantksal birliklerdir. rnein C ve Pascal dilleri veri yaplar bakmndan zengin dillerdir. Alt programlama yetenei (Modularity) Bir btn olarak zlmesi zor olan problemlerin paralara ayrlmas ve bu paralarn ayr ayr zmlenmesinden sonra paralar arasndaki koordinasyonun salanmas programada sk bavurulan bir yntemdir. Bir programlama dili buna olanak salayacak aralara sahipse alp programlama yetenei vardr diyebilirriz. Alt programlama yetenei bir programlama dilinin, program paralar halinde yazmay desteklemesi anlamna gelir. (C modlaritesi ok yksek bir dildir) Alt programlama Yapsal Programlama teknii'nin de ayrlmaz bir parasdr. Alt programlamann getirdii baz nemli avantajlar vardr. Alt programlar kodu kltr. ok tekrarlanan ilemlerin alt programlar kullanlarak yazlmas alabilir programn kodunu kltr. nk alt programlar yalnzca bir kere alabilir kod iine yazlrlar. Ama program kodu alt programn olduu yere atlatlarak bu blgenin defalarca altrlmas salanabilir.

1 Alt programlama alglamay kolaylatrr, okunabilirlii artrr. Alt programlama kaynak kodun test edilebilirliini artrr. Kaynak kodun daha kolay gncelletirilmesi ve yeniden kullanlabilme olanan artrr. Alt programlamann en nemli avantajlarndan biri de genel amal kodlar yazarak bu yazlan kodlar birden fazla projede kullanabilmektir. (reusability) C alt programlama yetenei yksek bir dildir. C'de alt programlara fonksiyon denir. Fonksiyonlar C Dili'nin yaptalardr. Yapsallk (structural programming support) Yapsallk bir programlama tekniidir. Bugn artk hemen hemen btn programlama dilleri yapsal programlamay az ok destekleyecek bir ekilde gelitirilmitir. Yapsal Programlama fikri 1960'l yllarda gelitirilmitir. Yapsal programlama teknii drt ana ilke zerine kurulmutur : 1. Bl ve stesinden gel (divide and conquer) Yapsal programlama tekniinde, tek bir btn olarak zm getirmek zor olan programlar, daha kk ve stesinden daha kolay gelinebilecek paralara blnrler. Bu paralar fonksiyon, prosedr, subroutine, alt program vs. olarak isimlendiriler. Alt program yapsnn getirdii avantajlar modularite konusunda yukarda aklanmtr.

2. Veri gizleme (Data hiding) Yapsal programlama tekniinde, programn dier paralarndan ulalamayan, yalnzca belli bir faaliyet alan olan, yani kodun yalnzca belli bir ksmnda faaliyet gsterecek deikenler tanmlanabilir. Bu tr deikenler genel olarak "yerel deikenler" (local variables) olarak isimlendirilirler. Deikenlerin faaliyet alanlarnn kstlanabilmesi hata yapma riskini azaltt gibi, programlarn daha kolay deitirilebilmesini ve program paralarnn baka programlarda tekrar kullanabilmesini de salar. Alt programlarn, ve daha geni ekliyle modllerin, bir ii nasl yapt bilgisi, o alt programn ya da modln kullancsndan gizlenir. Kullanc iin (client) alt programn ya da modln ii nasl yapt deil, ne i yapt nemlidir. 3. Tek giri ve Tek k (single entry single exit) Yapsal programlama tekniini destekleyen dillerde her bir altprogram parasna girmek iin tek bir giri ve tek bir k mekanizmas vardr. Bu mekanizma programn yukardan aa olarak ak ile uyum halindedir. Program paralarna ancak tek bir noktadan girilebilir. 4. Dngler ve dier kontrol yaplar. Artk hemen hemen kullanmda olan btn programlama dilleri az ya da ok Yapsal Programlama tekniini desteklemektedir. Zira bu teknik 60'l yllar iin devrim niteliindeydi.

Esneklik (flexibility) Esneklik programlama dilinin programcy kstlamamas anlamna gelir.Esnek dillerde birok ilem, hata yapma riski artmasna karn ramen kullanc iin serbest braklmtr. Programc bu serbestlikten ancak yetkin bir programcysa bir fayda salayabilir. Fakat programc deneyimsiz ise bu esneklikten zarar grebilir. renme ve retme kolayl (pedagogy) Her programlama dilini renmenin ve renilen programlama dilinde uygulama gelitirebilmenin zorluu ayn deildir. Genel olarak programlama dillerinin seviyesi ykseldike, renme ve bu programlama dilini bakalarna retme kolaylar, renme iin harcanacak aba ve zaman azalr. Bugn yaygn olarak kullanlan yksek seviyeli programl dillerinin bu derece popler olmasnn nemli bir nedeni de bu dillerin ok kolay renilebilmesidir. Ne yazk ki C renimi zor ve zahmetli bir dildir.

PROGRAMLAMA VE C Genellik (generality) Programlama dillerinin ok eitli uygulamalarda etkin olarak kullanlabilmesidir. rnein COBOL mhendislik uygulamalarnda tercih edilmez zaten ticari uygulamalar iin tasarlanmtr, Clipper ya da FOXPRO veri taban dilleridir. Oysa PASCAL, BASIC daha genel amal dillerdir. C dili de bir sistem programlama dili olarak domasna karn, gl yapsndan dolay, ksa bir sre iinde, genel amal bir dil haline gelmitir. Giri / k (input / output, I / O facility) kolayl Sral, indeksli ve rasgele dosyalara erime, veritaban kaytlarn geri alma, gncelletirme ve sorgulama yeteneidir. Veritaban programlama dillerinin (DBASE, PARADOX vs.) bu yetenekleri dierlerinden daha stndr ve bu dillerin en tipik zelliklerini oluturur. Fakat C giri k kolayl kuvvetli olmayan bir dildir. C'de veri tabanlarnn ynetimi iin zel ktphanelerin kullanlmas gerekir. Okunabilirlik (readability) Okunabilirlik, kaynak kodun abuk ve iyi bir biimde alglanabilmesi anlamna gelen bir terimdir. Kaynak kodun okunabilirliinde sorumluluk byk lde program yazan kiidedir. Fakat yine verimlilik de olduu gibi dillerin bir ksmnda okunabilirlii glendiren yap ve mekanizmalar bulunduu iin bu zellik bir lde dilin tasarmna da baldr. En iyi program kodu, sanld gibi "en zekice yazlm fakat kimsenin anlayamayaca" kod deildir. Birok durumda iyi programclar okunabilirlii hibireye feda etmek istemezler. nk okunabilir bir program kolay alglanabilme zelliinden dolay seneler sonra bile gncelletirmeye olanak salar. Birok kiinin ortak kodlar zerinde alt geni kapsaml projelerde okunabilirlik daha da nem kazanmaktadr. C de okunabilirlik en fazla vurgulanan kavramlardan biridir. Biz de kursumuz boyunca okunabilirlik konusuna sk sk deineceiz ve C programlarnn okunabilirlii konusunda baz temel prensipleri benimseyeceiz. 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. 1989 ylnda standartlatrma almalar biten C Dili, dier programlama dillerinden daha tanabilir bir programlama dilidir. Nesne Ynelimlilik (object orientation) Nesne ynelimlilik de bir programlama tekniidir. Yapsal programlama Teknii 1960 ylarnda gndeme gelmiken, Nesne Ynelimli Programlama Teknii 1980'li yllarda popler olmutur. Bu teknik kaynak kodlarn ok bymesi sonucunda ortaya kan gereksinim yznden gelitirilmitir. C dilinin gelitirildii yllarda, akla gelebilecek en byk programlar ancak onbin satrlar mertebesindeydi, ancak kullanclarn bilgisayar programlarndan beklentilerinin artmas ve grafik arayznn artk etkin olarak kullanlmasyla, bilgisayar programlarnn boyutu ok bym, yzbin satrlarla hatta milyon satrlarla llebilir hale gelmitir. Nesne ynelimli programlama Teknii, hereyden nce byk programlarn yazlmas iin tasarlanm bir tekniktir. C dilinin yaratld yllarda byle bir tekniin ortaya kmas sz konusu deildi, nk zaten programlar bugnk llere gre ok kkt. Nesne ynelimli programlama Tekniinin yaygn olarak kullanlmaya balanmasyla birlikte bir ok programlama dilinin bnyesine bu tekniin uygulanmasn kolaylatrc aralar eklenek, yeni versiyonlar oluturulmutur. rnein C'nin nesne ynelimli programlama tekniini uygulayabilmek iin Bjarne

1 Stroustrup tarafndan gelitirilmi haline C++ denmektedir. C++ dili C dili baz olarak alnp, gelitirilmi yeni bir programlama dilidir. C++ dilini iyi renebilmek iin ncelikle C dilini ok iyi renmek gerekir. Pascal diline eklemeler yaplarak Delphi dili, Cobol dilinden yenilemesiyle OOCobol, Ada dilinin yenilenmesiyle ise ADA 95 dilleri gelitirilmitir. Baz programlama dilleri ise dorudan N.Y.P.T'ni destekleyecek ekilde tasarlanarak gelitirilmitir. rnein JAVA dili C++ dilinin basitletirilmi biimi olup daha ok Internet uygulamalarnda kullanlmaktadr. Baka bir rnek olarak da Eiffel dili verilebilir. C Nasl bir Programlama Dilidir? Btn bunlardan sonra yukarda akladmz kavramlar da kullanarak C dilini aadaki ekilde tanmlayabiliriz : C orta seviyeli bir programlama dilidir. Yapsal dier programlama dillerine gre C dilinin seviyesi daha dktr. C dili hem yksek seviyeli dillerin, kontrol deyimleri, veri yaplar gibi avantajlarn bnyesinde barndryor, ayn zamanda bitsel operatrler gibi makine kodu deyimlerini yanstan operatrlerlere sahip. Yani hem makinaya yakn hem de insan alglamasna. Zaten ok tercih edilmesinin en nemli nedenlerinden biri de bu. C bir sistem programlama dilidir. Sistem Programlama ne anlama geliyor? Donanmn ynetilmesi, kontrolu ve denetimi iin yazlan, dorudan donanmla ilikiye giren programlara sistem program diyoruz. rnein, iletim sistemleri, derleyiciler, yorumlayclar, aygt srcleri (device drivers), bilgisayarlarn iletiimine ilikin programlar, otomasyon programlar, sistem programlardr. Dier uygulama programlarna destek veren yazlmlar da ounlukla sistem programlar olarak ele alnrlar. C'den nce sistem programlar assembly dillerle yazlyordu.Sistem programlarnn yazlmasnda hemen hemen alternatifsiz olduunu syleyebiliriz. Bugn cep telefonlarndan, uaklara kadar her yerde C kodlar almaktadr. rnein Boeing uaklarnda 100.000 satrdan fazla C kodu alt bilinmektedir.

C algoritmik bir dildir. C'de program yazmak iin yalnzca dilin sentaks ve sementik yapsn bilmek yetmez genel bir algoritma bilgisi de gerekir. C dier dillerle kyaslandnda tanabilirlii ok yksek olan bir dildir. nk 1989 ylndan bu yana genel kabul grm standartlara sahiptir. fade gc yksek , okunabilirlik zellii gl bir dildir. C ok esnektir. Dier dillerde olduu gibi programcya kstlamalar getirmez. Gl bir dildir. ok iyi bir biimde tasarlanmtr. C'ye ilikin operatrlerin ve yaplarn bir ou daha sonra baka programlama dilleri tarafndan da benimsenmitir. C verimli bir dildir. Seviyesinden dolay hzl alr. Verimlilik konusunda assembly diller ile rekabet edebilir. C doal bir dildir. C bilgisayar sisteminin biimiyle uyum iindedir. C kk bir dildir. Yeni sistemler iin derleyici yazmak zor deildir. C'nin eitimi dier bilgisayar dillerine gre daha zordur. C Programlama Dili'nin Tarihi C dilinin tarihini incelediimizde C dilinin UNIX iletim sisteminin bir yan rn olarak doduunu syleyebiliriz. UNIX iletim sisteminin orjinal ilk versiyonunu Bell Labaratuarlar'nda alan Ken Thompson tek bana yazmt ve UNIX'in bu ilk versiyonu DEC PDP-7 isimli bilgisayarda alyordu. DEC PDP-7 ilk mini bilgisayarlardan biriydi ve ana bellei yalnzca 16 K (16 MB deil!). Yllardan 1969'du. Zamannn dier iletim sistemleri gibi UNIX de assembly dilinde yazlmt. Assembly dilinde yazlan programlar gelitirmek ok zor ve zahmetli olduundan, Thompson UNIX iletim sistemini daha gelitirebilmek iin, makine dilinden daha yksek seviyeli bir dile gereksinim duydu. Bu amala kk bir programlama dili tasarlad. 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
8

PROGRAMLAMA VE C 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 grlmektedir: Algol 60

Algol 68

Algol W

Simula 67

BCPL

C Pascal C++

Java Modula-2 Ada Delphi

Oberon

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 teknoloji olan PDP-11 bilgisayarlarda alyordu. Thompson UNIX iletim sisteminin bir ksmn B dilinde tekrar yazd. Artk 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 versiyonunu 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 tekrar yazld.

Ken Thompson ve Dennis Ritchie Unix letim Sistemi zerinde alrken (Yl: 1972)
C'nin evrimi ve gelimesi 70'li yllarda da devam etti. 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
9

1 deerlendirilmektedir. C'nin standardize edilmesine kadar olan dnemde bu kitap ounluun benimsedii genel kabul gren gayriresmi bir standard vazifesi de grmtr. 1970'li yllarda C programclarnn says azd ve bunlardan ou UNIX kullanclaryd. Ama artk 80'li yllar gelince C nin kullanm UNIX snrlarn at, ve farkl iletim sistemlerinde alan derleyiciler piyasaya kt, C dili de IBM PC'lerde youn olarak kullanlmaya balad. C'nin artan poplaritesi problemleri de beraberinde getirdi. Derleyici yazan kiiler, referans olarak Ritchie ve Kernighan'n kitabn esas alyorlard ama sz konusu kitapta baz noktalar ok da detayl bir biime 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. Artk C dilinin standardizasyonu neredeyse zorunlu bir hale gelmiti! C'nin standardizasyon almalar 1983 ylnda ANSI (American National Standards Institute ) gzetiminde ve desteinde balad. Yaplan birok deiiklikten sonra standart almalar 1988 ylnda sona erdi ve 1989 ylnn Aralk aynda ANSI C standard Jim Brodie bakanlnda X3.159 - 1989 numarasyla resmi olarak onayland. 1990 ylnda ise ISO/IEC 9899 - 1990 numarasyla ISO (International Standards Organization) tarafndan standardizasyonu kabul edildi. Standardizasyonu tamamlandktan sonra C yksek oranda tanabilir bir sistem programlama dili haline gelmitir. Gnmzde de sistem programlarnn (derleyiciler, editrler, iletim sistemleri) ou C dili ile yazlmaktadr. Fotoraflar Dennis M. Ritchie

10

11

2 . BLM :

SAYI SSTEMLER

Gnlk hayatta 10luk say sistemini kullanyoruz. 10 luk sistemde bir saynn deeri aslnda her bir basamak deerinin 10 saysnn ilgili kuvvetiyle arpmlarnn toplanmasyla elde edilir. rnein 1273 = (3 * 1) + (7 * 10 ) + (2 * 100) + (1 * 1000) Ancak bilgisayar sistemlerinde btn bilgiler ikilik sistemde(binary system) ifade edilir. Genel olarak say sistemi kalksa o say sisteminde o kadar sembol bulunur. rnein 10luk sistemde 10 adet sembol vardr ve bu semboller 0, 1, 2, 3, 4, 5, 6, 7, 8, 9dur. Ayn ekilde ikilik say sisteminde yalnzca iki adet sembol bulunur. Yani yalnzca 0 ve 1. Bir sayy baka bir say sisteminde ifade etmek o saynn deerini deitirmez. Yalnzca saynn gsterili biimi deiir. rnein onluk say sisteminde saysal deeri 32 olan bykl eitli farkl say sistemlerinde farkl biimlerde gsterebiliriz ama saynn bykln deitirmi olmayz. kilik sistemde her bir basamaa 1 bit denir. Bit kelimesi binary digit szcklerinden tretilmitir. rnein 1011 says 4 bittir. (Ya da 4 bit uzunluundadr). 11011001 says 8 bittir. 8 bitlik bir byklk bir byte olarak isimlendirilir. 1 kilobyte 1K = 1024 byte dr. (yani 210 byte) 1 mega byte 1 MB = 1024 Kilo byte dr. (yani 220 byte) 1 gigabyte 1 GB = 1024 MB dr. (yani 230 byte) 1 terabyte 1 TB = 1024 GB dr. (yani 240 byte) 1 petabyte 1PB = 1024 TB dr. (yani 250 byte) 1 exabyte 1EB = 1024 PB dr. (yani 260 byte) 1 zettabyte 1ZB = 1024 EB dir.( yani 270 byte) 1 yottabyte 1YB = 1024 ZB dr.( yani 280 byte) Kilo byklk olarak 1000 kat anlamna gelmektedir, ancak bilgisayar alannda Kilo 2'nin 1000'e en yakn kuvveti olan 210 yani 1024 kat olarak kullanlr. 4 bit 8 bit 16 bit 32 bit 64 bit 1 1 1 1 1 Nybble (Nibble eklinde de yazlr) byte word double word quadro word

olarak da isimlendirilmektedir. ikilik sisteme ilikin genel ilemler i. kilik sistemdeki bir saynn 10 luk sistemde ifade edilmesi: ikilik say sisteminde ifade edilen bir saynn 10luk sistemdeki karln hesaplamak iin en sadan balayarak btn basamaklar tek tek 2nin artan kuvvetleriyle arplr. rnein : 1 0 1 1 = 1 * 20 + 1 * 21 + 0 * 22 + 1 * 23 = 11 0010 1001 = (1 * 1) + (1 * 8) + (1 * 32) = 41

12

SAYI SSTEMLER Bu arada sk kullanlan iki terimi de aklayalm. kilik say sisteminde yazlan bir saynn en solundaki bit, yukardaki rnekten de grld gibi en yksek saysal deeri katyor. Bu bite en yksek anlaml bit (most significant digit) diyeceiz ve bu bit iin bundan sonra MSD ksaltmasn kullanacaz. kilik say sisteminde yazlan bir saynn en sandaki bit, yine yukardaki rnekten de grld gibi en dk saysal deeri katyor. Bu bite en dk anlaml bit (least significant digit) diyeceiz ve bu bit iin bundan sonra LSD ksaltmasn kullanacaz. rnek : 0101 1101 says iin MSD = 0 LSD = 1 kilik say sisteminde yazlan bir saynn belirli bir basamandan (bitinden) sz ettiimizde, 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. ii. 10luk sistemdeki bir saynn 2lik sistemde ifadesi : Say srekli olarak 2 ye blnr. Her blmden kalan deer( yani 1 ya da 0) oluturulacak saynn 0. bitinden balayarak, basamaklarn oluturacaktr. Bu ileme 0 says elde edilinceye kadar devam edilir. rnek: 87 saysn ikilik say sisteminde ifade etmek isteyelim: 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 10 luk sistemde ifade edilen saydan srekli olarak 2'nin en byk kuvvetini karmaktr. 2nin karlan her bir kuvveti iin ilgili basamaa 1 deeri yazlr. Bu ileme 0 says elde edilene kadar devam edilir. rnek: Yine 87 saysn ikilik say sisteminde ifade etmek isteyelim: 87'den karlabilecek, yani 87'den byk olmayan ikinin en byk kuvveti nedir? Cevap 64. O zaman 64 = 26 olduuna gre 6.bit 1 deerini alacak. 87 - 64 = 23. imdi 23'den karlabilecek ikinin en byk kuvvetini bulalm. Bu say1 16'dr. Yani 24 'dr. O zaman saymzn 4. biti de 1 olacak. 23 - 16 = 7. 7'den karlabilecek ikinin en byk kuvveti 4'dr ve 4 = 22 'dir. Saymznj 2. biti de 1 olacak. 7 - 4 = 3. 3 - 2 = 1 (2 = 21 ) Saymzn 1. biti 1 olacak. 1 - 1 = 0 (1 = 20 ) Saymzn 0. biti 1 olacak. 1 deeri olmayan tm bitleri 0 bitiyle doldurarak saymz ikilik sistemde ifade edelim: 87 = 0101 0111 iii. kilik sistemde ifade edilen bir saynn 1e tmleyeni. Saynn tm bitlerinin tersinin alnmasyla elde edilir. Yani saydaki 1ler 0 ve 0lar 1 yaplr.
13

2 Bir saynn 1e tmleyeninin 1e tmleyeni saynn yine kendisidir. iv. kilik sistemde ifade edilen bir saynn 2ye tmleyeninin bulunmas: nce saynn 1e tmleyeni yukardaki gibi bulunur. Daha sonra elde edilen sayya 1 eklenirse saynn 2ye tmleyeni bulunmu olur. 2'ye tmleyeni bulmak iin daha daha pratik bir yol daha vardr : Saynn en solundan balayarak ilk defa 1 biti grene kadar (ilk grlen 1 dahil) saynn ayns yazlr, daha sonraki tm basamaklar iin basaman tersi yazlr. (Yani 1 iin 0 ve 0 iin 1) rnein : 1110 0100 saysnn ikiye tmleyeni 0001 1100 dr. 0101 1000 saysnn ikiye tmleyeni 1010 1000 dr. Bir saynn ikiye tmleyeninin ikiye tmleyeni saynn kendisidir. (Deneyiniz) 8 bitlik bir alana yazlacak en byk tam say katr? 1111 1111 = 255 dir. (1 + 2 + 4 + 8 + 16 + 32 + 64 + 128 = 255) 8 bitlik bir alana yazlacak en kk tam say katr? 0000 0000 = 0dr. Negatif bir tamsay ikilik sistemde nasl gsterilir? Negatif tamsaylarn da ifade edildii ikilik say sistemine iaretli ikilik say sistemi ( signed binary system) denir. aretli ikilik say siteminde, negatif saylar gstermek iin hemen hemen tm bilgisayar sistemlerinde aadaki yol izlenir: Saynn en yksek anlaml biti iaret biti ( sign bit) olarak kabul edilir. Ve bu bit 1 ise say negatif, bu bit 0 ise say pozitif olarak deerlendirilir. kilik sistemde bir negatif say ayn deerdeki pozitif saynn ikiye tmleyenidir. rnek olarak, ikilik sistemde yazacamz 27 says yine ikilik sistemde yazlan 27 saysnn ikiye tmleyenidir. Pozitif olan saylarn deerini tpk iaretsiz say sisteminde olduu gibi elde ederiz: 0001 1110 iaretli sistemde pozitif bir saydr. (Decimal olarak 29 saysna eittir.) Ancak negatif olan saylarn deerini ancak bir dnmle elde edebiliriz: 1001 1101 iaretli sistemde negatif bir saydr. (nk iaret biti 1)

2lik sistemde ifade edilen negatif bir saynn 10luk sistemde hangi negatif sayya eit olduunu nasl bulunur?
Saynn en yksek anlaml biti (MSD) iaret bitidir. Bu bit 1 ise say negatifdir. Saynn kaa eit olduunu hesaplamak iin ilk nce saynn 2ye tmleyeni bulunur. Ve bu saynn hangi pozitif sayya karlk geldiini hesap edilir. Elde etmek istenen say, bulunan pozitif say ile ayn deerdeki negatif say olacaktr. rnein 1001 1101 saysnn 10luk sistemde hangi sayya karlk geldii bulunmak istenirse: Saynn en soldaki biti 1 olduuna gre bu say negatif bir say olacaktr. Hangi negatif say olduunu bulmak iin saynn 2ye tmleyenini alnr.

14

SAYI SSTEMLER 1001 1101 saysnn ikiye tmleyeni 0110 0011 saysdr. Bu saynn 10'luk sistemde hangi sayya denk olduu hesaplanrsa : (1 * 1 + 1 * 2 + 0 * 4 + 0 * 8 + 0 * 16 + 1 * 32 + 1 * 64 = 99) ilk yazlan saynn -99 olduu anlalm olur.

10'luk sistemde ifade edilen negatif saylarn iaretli ikilik sistemde yazlmas :
nce saynn ayn deerli fakat pozitif olan ikilik sistemde ifade edilir : Daha sonra yazlan saynn ikiye tmleyenini alnarak, yazmak istenilen say elde edilir. rnek : kilik sistemde 17 yazmak istenirse; nce 17 yazlr. bu saynn 2'ye tmleyeni alnrsa 0001 0001 1110 1111 says elde edilir.

Say deeri ayn olan Negatif ve Pozitif saylar birbirlerinin ikiye tmleyenleridir. kilik sistemde gsterilmi olsa da ayn saynn negatifiyle pozitifinin toplam 0 deerini verecektir. (Deneyiniz!) Bir bytelk (8 bitlik) bir alana yazabileceimiz (iaret bitini dikkate almadan) en byk say 255 (1111 1111) ve en kk say ise 0dr.(0000 0000). Peki iaret biti dikkate alndnda 1 bytelk alana yazlabilecek en byk ve en kk saylar ne olabilir? En byk say kolayca hesaplanabilir. iaret biti 0 olacak (yani say pozitif olacak) ve say deerini en byk hale getirmek iin dier btn bit deerleri 1 olacak, bu say 0111 1111 saysdr. Bu sayy desimal sisteme dntrrsek 127 olduunu grrz. Peki ya en kk negatif say katr ve nasl ifade edilir? 0111 1111 saysnn ikiye tmleyenini alndnda 127 saysn elde edilir. 1000 0001 (127) Bu saydan hala 1 kartabilir. 1000 0000 (-128) 1 byte alana yazlabilecek en kk negatif saydr. Burada dikkat edilmesi gereken iki nemli nokta vardr : 1 byte alana yazlabilecek en byk say snr aldnda negatif blgeye geilir. 0111 1111 (en byk pozitif tamsay) 1 (1 toplarsak) 1000 0000 (-128 yani en kk tamsay) yani 1 byte alana yazlabilecek en byk tamsayya 1 eklendiinde 1 byte alana yazlabilecek en kk tamsayy elde ederiz. 1 byte alana yazlabilecek en kk tamsaydan 1 kardmzda da 1 byte alana yazlabilecek en byk tamsayy elde ederiz. Yukarda anlattklarmza gre -1 saysnn iaretli ikilik say sisteminde 8 bitlik bir alanda aadaki ekilde ifade edilecektir. -1 = 1111 1111 Yani iaretli ikilik say sisteminde tm bitleri 1 olan say -1'dir. leride bu sayyla ok iimiz olacak!

15

2 16lk say sistemi (hexadecimal numbering system) ve 8lik say sistemi (octal system) Bilgisayarlarn tamamen 2lik sistemde altn sylemitik, ama yukarda grld gibi 2lik sistemde saylarn ifade edilmesi hem ok uzun hem de zahmetli. Bu yzden, yazm ve alglama kolayl salamak iin 16lk ve 8lik say sistemleri de kullanlmaktadr. 16'lk ve 8lik say sistemlerinde saylar daha youn olarak kodlanp kullanabilir. Bata da sz edildii gibi 10 luk sistemde 10, 2lik sistemde ise 2 sembol bulunmaktadr. Bu durumda 16lk say sisteminde de 16 sembol bulunur. ilk 10 sembol 10'luk sistemde kullanlan sembollerle tamamen ayndr : 1, 2, 3, 4, 5, 6, 7, 8, 9, Daha sonraki semboller A B C D E F = 10 = 11 = 12 = 13 = 14 = 15

16lk say sisteminde yazlm bir sayy 10luk sisteme evirmek iin, en sadan balayarak basamak deerleri 16nn artan kuvvetleriyle arplr : 01AF = (15 * 1) + (10 * 16) + (1 * 256) + (0 * 4096) = 431 10luk sistemde yazlm bir sayy 16lk sisteme evirmek iin 10 luk sistemden 2lik sisteme yaplan dnmlerdekine benzer ekilde say srekli 16 ya blnerek, kalanlar soldan saa doru yazlr. Pratikte 16 lk say sistemlerinin getirdii nemli bir avantaj vardr. Bu avantaj 16 lk say sistemi ile 2lik say sistemi arasndaki dnmlerin kolay bir ekilde yaplmasdr. 16lk sistemdeki her digit 2lik sistemdeki 4 bit (1 Nibble) alan ile ifade edilebilir : 0001 0010 0011 0100 0101 0110 0111 1000 1001 1010 1011 1100 1101 1110 1111 1 2 3 4 5 6 7 8 9 A B C D E F

16

SAYI SSTEMLER rnek : 2ADFH saysnn (en sondaki H saynn hexadecimal olarak gsterildiini anlatr yani sayya ilikin bir sembol deildir) 16'lk sistemde ifadesi : 2 A D F = = = = 0010 1010 1101 1111

Bu durumda 2ADFH = 0010 1010 1101 1111 2lik sistemden 16lk sisteme yaplacak dnmler de benzer ekilde yaplabilir : nce saylar sadan balayarak drder drder ayrrz (en son drt eksik kalrsa sfr ile tamamlarz.) Sonra her bir drtlk grup iin dorudan 16'lk say sistemindeki karln yazarz. 1010 1110 1011 0001 0010 1101 0011 1110 = AEB1H = 2D3EH

soru : 16'lk say sisteminde 2 byte'lk bir alanda yazlm olan 81AC H says pozitif mi negatif midir? cevap : Saynn yksek anlaml biti 1 olduu iin, iaretli say sistemlerinde say negatif olarak deerlendirilecektir. (1001 0001 1010 1100) 16 bitlik bir alanda ve iaretli say sisteminde -1 saysn nasl ifade edebiliriz : Cevap : FFFF

8lik say sistemi (octal numbering system)


Daha az kullanlan bir say sistemidir. 8 adet sembol vardr. (0 1 2 3 4 5 6 7) 8lik say sisteminin her bir digiti 2lik sistemde 3 bit ile ifade edilir. 001 010 011 100 101 110 111 1 2 3 4 5 6 7

8'lik say sisteminin de kullanlma nedeni, 2'lik say sistemine gre daha yogun bir ifade tarz olmas, ve ikilik say sistemiyle, 8'lik say sistemi arasnda yaplacak dnmlerin ok kolay bir biimde yaplabilmesidir.

GEREK SAYILARIN BELLEKTE TUTULMASI


Sistemlerin ou gerek saylar IEEE 754 standardna gre tutarlar. (Institute of Electrical and Electronics Engineers) Bu standarda gre gerek saylar iin iki ayr format belirlenmitir: single precision format (tek hassasiyetli gerek say format) Bu formatta gerek say 32 bit (8 byte) ile ifade edilir. 32 bit ayr ksma ayrlmtr.
17

1. aret biti (sign bit) (1 bit) Aada S harfi ile gsterilmitir. aret biti 1 ise say negatif, iaret biti 0 ise say pozitiftir. 2. stel ksm (exponent) (8 bit) Aada E harfleriyle gsterilmitir. 3. Ondalk ksm (fraction) (23 bit) Aada F harfleriyle gsterilmitir. S 31 EEEEEEEE 30-----------23 FFFFFFFFFFFFFFFFFFFFFFF 22-------------------------------------0

Aadaki formle gre saynn deeri hesaplanabilir : V saynn deeri olmak zere: E = 255 ise ve F 0 d bir deer ise V = NaN (Not a number) bir gerek say olarak kabul edilmez. rnek : 0 11111111 00001000000100000000000 = Say deil 1 11111111 00010101010001001010101 = Say deil

E = 255 ise ve F = 0 ise ve S = 1 ise V = -sonsuz E = 255 ise ve F = 0 ise ve S = 1 ise V = +sonsuz 0 < E < 255 ise V = (-1)S * 2(E -127) * (1.F) nce saynn fraction ksmnn bana 1. eklenir. Daha sonra bu say 2 (E-127) ile arplarak noktann yeri ayarlanr. Noktadan sonraki ksm 2'nin artan negatif kuvvetleriyle arplarak elde edilecektir. rnekler : 0 10000000 00000000000000000000000 = +1 * 2 = 2 * 1.0 = 10.00 =2
(128 - 127)

* 1.0

0 10000001 10100000000000000000000 = +1 * 2 = 22 * 1.101 = 110.100000 = 6.5

(129 - 127)

* 1.101

1 10000001 10100000000000000000000 = -1 * 2 = -22 * 1.101 = 110.100000

(129 - 127)

* 1.101

18

SAYI SSTEMLER = -6.5 0 00000001 00000000000000000000000 = +1 * 2 = 2-126 E = 0 ve F sfr d bir deer ise V = (-1)S * 2(-126) * (0.F) rnekler : 0 00000000 10000000000000000000000 = +1 * 2-126 * 0.1 = 0 00000000 00000000000000000000001 = +1 * 2-126 0. 00000000000000000000001 = 2-149 (en kk pozitif deer) E = 0 ve F = 0 ve S = 1 ise V = -0 E = 0 ve F = 0 ve S = 0 ise V = 0
(1 - 127)

* 1.0

double precision format (ift hassasiyetli gerek say format) Bu formatta gerek say 64 bit (8 byte) ile ifade edilir. 64 bit ayr ksma ayrlmtr. 1. aret biti (sign bit) (1 bit) Aada S harfi ile gsterilmitir. aret biti 1 ise say negatif, iaret biti 0 ise say pozitiftir. 2. stel ksm (exponent) (11 bit) Aada E harfleriyle gsterilmitir. 3. Ondalk ksm (fraction) (52 bit) Aada F harfleriyle gsterilmitir. S EEEEEEEEEEE FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF 63 62------------------52 51-----------------------------------------------------------------------------0 Aadaki formle gre saynn deeri hesaplanabilir : Aadaki formle gre saynn deeri hesaplanabilir : V saynn deeri olmak zere: E = 2047 ise ve F 0 d bir deer ise V = NaN (Not a number) bir gerek say olarak kabul edilmez. E = 2047 ise ve F = 0 ise ve S = 1 ise V = -sonsuz E = 2047 ise ve F = 0 ise ve S = 1 ise V = +sonsuz 0 < E < 2047 ise
19

V = (-1)S * 2(E -1023) * (1.F) nce saynn fraction ksmnn bana 1. eklenir. Daha sonra bu say 2(E-1023) ile arplarak noktann yeri ayarlanr. Noktadan sonraki ksm 2'nin artan negatif kuvvetleriyle arplarak elde edilecektir. E = 0 ve F sfr d bir deer ise V = (-1)S * 2(-126) * (0.F) E = 0 ve F = 0 ve S = 1 ise V = -0 E = 0 ve F = 0 ve S = 0 ise V = 0

20

SAYI SSTEMLER

21

22

GENEL KAVRAMLAR

3 . BLM :
ATOM KAVRAMI VE ATOM TRLER

GENEL KAVRAMLAR

Bir programlama dilinde yazlm program en kk paralara blmeye alalm. yle bir noktaya geleceiz ki, artk bu paralar daha da blmeye altmzda anlamsz paralar oluacak. te bir programlama dilinde anlam tayan en kk birime atom (token) denir. Atomlar daha fazla paraya blnemezler. Yazdmz kaynak kod (program) derleyici tarafndan ilk nce atomlarna ayrlr. (Tokenizing). Atom yalnzca C diline ilikin bir kavram deildir. Tm programlama dilleri iin atom kavram sz konusudur, ama farkl programlama dillerinin atomlar birbirlerinden farkl olabilir. Atomlar aadaki gibi gruplara ayrabiliriz : 1. Anahtar Szckler (keywords, reserved words) Bu atomlar dil iin belli bir anlam tarlar. Deiken olarak kullanlmalar yasaklanmtr. Yani programc bu anahtar szckleri kendi tanmlayaca deikenlere isim olarak veremez. Standard ANSI C dilinde 32 tane anahtar szck bulunmaktadr.(Derleyici yazan firmalar kendi yazdklar derleyiciler iin ilave anahtar szckler tanmlayabilmektedir.) auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while Baz programlama dillerinde anahtar szcklerin kk ya da byk harf olmas fark etmemektedir. Ama Cde btn anahtar szckler kk harf olarak tanmlanmtr. C byk harf kk harf duyarl olan bir dildir. (case sensitive) bir dildir. Ama dier programlama dillerinin ounda byk - kk harf duyarl yoktur. (case insensitive) rnein, programc olarak biz kullanacamz bir deikene register ismini vermeyiz. nk bu bir anahtar szcktr. (C dili tarafndan rezerve edilmitir) Ama buna karn biz istediimiz bir deikene REGISTER, Register, RegisTER vs. gibi isimler verebiliriz, nk bunlar artk anahtar szck saylmazlar. Anahtar szck olan yalnzca tamamen kk harf ile yazlan "register" dir. 2. simlendirilenler (identifiers) Deikenlere, fonksiyonlara, makrolara, yap ve birliklere vs. programlama dili tarafndan belirlenmi kurallara uyulmak artyla, istediimiz gibi isim verebiliriz. Bu atomlar genellikle bellekte bir yer belirtirler. C dilinde deikenlerin isimlendirilmesine ilikin kurallar vardr. Bunu ileride detayl olarak greceiz. 3. Operatrler (Operators) Operatrler nceden tanmlanm birtakm ilemleri yapan atomlardr. rnein +, -, *, / , >=, <= birer operatrdr. Programlama dillerinde kullanlan operatr sembolleri birbirinden farkl olabilecei gibi, operatr tanmlamalar da birbirinden farkl olabilir. rnein birok programlama dilinde s alma operatr tanmlanmken C dilinde byle bir operatr yoktur. s alma ilemi operatr ile deil bir fonksiyon yardmyla yaplabilir. C dilinde baz operatrler iki karakterden olumaktadr bu iki karakter bitiik yazlmaldr aralarna space karakteri koyarsak operatr anlamn yitirir. 4. Sabitler (Constants) Dorudan ileme sokulan deiken bilgi iermeyen atomlardr.
23

3 rnein SAYAC = SON + 10 gibi bir ifadede 10 sabiti dorudan SON deikeni ile toplanmaktadr. 5. Stringler (String literals) ki trnak iindeki ifadelere string denir. Stringler programlama dillerinin ounda tek bir atom olarak alnrlar, daha fazla paraya blnemezler. STRNGLER DE BRER ATOMDUR ifadesi bir stringdir. 6. Ayralar ya da noktalama iaretleri (Separators, Punctuators, Delimiters) Yukarda saylan atom snflarnn dnda kalan tm atomlar bu gruba sokabiliriz. Genellikle dier atomlar birbirinden ayrma amacyla kullanldklar iin ayra olarak isimlendirilirler. rnek bir C programnn atomlarna ayrlmas: Aada 1 den kullancnn klavyeden girdii bir tamsayya kadar olan tamsaylar toplayan ve sonucu ekrana yazdran bir C program grlyor. Bu kaynak kodu atomlarna ayralm. Amacmz sz konusu program aklamak deil, atomlar hakknda gerek bir programdan rnek vermek.

#include <stdio.h> main() { int number, k, total = 0; printf("ltfen bir say giriniz\n"); scanf("%d", &number); for(k = 1; k<= number; ++k) total += k; printf("toplam = %d\n", toplam); return 0; } # include < stdio.h > main ( ) { int number , k printf ( "ltfen bir say giriniz\n" ) ; scanf ( "%d" , & for ( k = 1 ; k <= ; ++ k ) total += k printf ( "toplam = %d\n" , toplam ) ; } programda yer alan atomlardan anahtar szckler include int for , total = 0 number ) ; ; ;

return

isimlendirilenler (identifiers / variables) main n k toplam printf scanf operatrler = <= ++ sabitler 0 1

+=

24

GENEL KAVRAMLAR stringler ("ltfen bir say giriniz\n"

"%d"

"toplam = %d\n"

ayralar noktalama iaretleri < > ( ) , ; { }

NESNE (OBJECT) Bellekte yer kaplayan ve ieriklerine eriilebilen alanlara nesne denir. Bir ifadenin nesne olabilmesi iin bellekte bir yer belirtmesi gerekir. Programlama dillerinde nesnelere isimlerini kullanarak eriebiliriz. a = b + k; rneinde a, b ve k birer nesnedir. Bu ifadede a nesnesine b ve k nesneleriine ait deerlerin toplam atanmaktadr. sonuc = 100; sonuc isimli nesneye 100 sabit deeri atanmaktadr. nesnelerin baz zelliklerinden sz edilebilir :

simleri (name) : Nesneyi temsil eden karakterlerdir. Nesnelere isimleri programc tarafndan verilir. Her dil iin nesne isimlendirmede baz kurallar sz konusudur. VERGI = 20000; (Burada VERGI bir nesne ismidir.) Nesne ile Deiken kavramlar birbirine tam olarak edeer deildir. Her deiken bir nesnedir ama her nesne bir deiken deildir. Deikenler, programcnn isimlendirdii nesnelerdir. Peki programcnn isimlendirmedii de nesneler var mdr? Evet, gstericiler konusunda da greceimiz gibi, deiken olmayan nesneler de vardr, nesne kavram deiken kavramn kapsamaktadr.

Deerleri (value) : Nesnelerin ilerinde tuttukler bilgilerdir. Baka bir deyile nesneler iin bellekte ayrlan yerklerdeki 1 ve 0 larn yorumlan biimi ilgili nesnenin deeridir. Bu deerler programlama dillerinin kurallarna gre , istenildikleri zaman programc tarafndan deitirilebilirler. C dilinde baz nesneler ise bir kez deer verildikten sonra bir daha deitirilemezler.

Trleri (Type) : Nesnenin tr derleyiciye o nesnenin nasl yorumlanaca hakknda bilgi verir. Yine 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 yaplabilecii bilgisini de verir. Tr nesnenin ayrlmaz bir zelliidir, trsz bir nesne kavram sz konusu deildir. Trleri ikiye ayrabiliriz : 1. nceden tanmlanm veri trleri (default types) Bu trler programlama dilinin tasarmnda var olan veri trleridir. rnein C dilinde nceden tanmlanm 11 ayr veri tr vardr.

25

3 2. Programc tarafndan tanmlanan veri trleri (user defined types) Programlama dillerinin ounda programcnn tr tanmlamasna izin vermektedir. rnein C dilinde yaplar, birlikler, bit alanlar, C++ dilinde de snflar programc tarafndan tanmlanan veri trleridir. Programlama dillerindeki tr tanmlamalar birbirlerinden farkl olabilir. rnein baz programlama dillerinde Boolean isimli (Mantksal Doru ya da Yanl deerlerini alan) bir trdr tanmlanmtr. Ama C dilinde byle bir tr dorudan tanmlanmamtr.

Faaliyet alanlar (scope / visibility) : Nesnenin, dilin derleyicisi ya da yorumlaycs tarafndan tannabildii program alandr. (ileride detayl inceleyeceiz)

mrleri (storage duration / lifespan) : Programn altrlmas srasnda nesnenin varln srdrd zaman parasdr. (leride detayl inceleyeceiz)

Balantlar (linkage) Nesnelerin program oluturan dier modllerde tannabilme zelliidir. (leride detayl inceleyeceiz)

FADE (Expression) Deiken, operatr ve sabitlerin kombinasyonlarna ifade denir. a+b/2 c * 2, d = h + 34 var1 geerli ifadelerdir.

DEYM (statement) Derleyicinin, bilgisayara bir i yaptracak ekilde kod retmesine (yani icra edilebilecek bir kod retmesine) yol aan ifadelere deyim denir. rnein C dilinde ; ile sonlandrlm ifadelere deyim diyoruz. 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 olacaktr. Deyimleri leride detayl olarak inceleyeceiz.

26

GENEL KAVRAMLAR SOL TARAF DEER (Left Value) Nesne gsteren ifadelere denir. Bir ifadenin sol taraf deeri olabilmesi iin mutlaka bir nesne gstermesi gerekir. Bir ifadenin Sol taraf deeri olarak isimlendirilmesinin nedeni o ifadenin atama operatrnn sol tarafna getirilebilmesidir. rnein a ve b nesneleri tek bana sol taraf deerleridir. nk bu ifadeler atama operatrnn 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. sabitler sol taraf deeri olamazlar. SA TARAF DEER (Rigth Value) Daha az kullanlan bir terimdir. Nesne gstermeyen ifadeler sa taraf deeri olarak isimlendirilirler. Tipik olarak, atama operatrnn sol tarafnda bulunamayan yalnzca sa tarafnda bulunabilen ifadelerdir. Sabitler her zaman sa taraf deeri olutururlar. (Bir ifade sol taraf deeri deilse sa taraf deeridir. Sa taraf deeri ise sol taraf deeri deildir.Her ikisi birden olamaz. Yani atama operatrnn sa tarafna gelebilen her ifade sa taraf deeri olarak isimlendirilmez.) Sa taraf deeri, genellikle bir ifadenin nesne gstermediini vurgulamak iin kullanlr.

27

4 . BLM :

VER TRLER

Nesne (Object) kavramn incelediimiz zaman, nesnelerin en nemli zelliklerinden birinin nesnenin tr olduunu belirtmitik. Tr (type) nesnenin olmazsa olmaz bir zelliidir ve tr olmayan bir nesneden sz etmek mmkn deildir. Derleyiciler nesnelerle ve verilerle ilgili kod retirken, tr bilgisinden faydalanrlar. Tr bilgisinden, sz konusu veriyi bellekte ne ekilde tutacaklarn, verinin deerini ne ekilde yorumlayacaklarn, veriyi hangi ilemlere tabi tutabileceklerini renirler. Programlama dilleri asndan baktmz zaman trleri iki ayr gruba ayrabiliriz.

1. nceden tanmlanm veri trleri (Doal veri trleri) (Basic tpes, default types, built-in types, primitive types)
Programlama dilinin tasarmndan kaynaklanan ve dilin kurallarna gre varl garanti altna alnm olan trlerdir. Her programlama dili programcnn dorudan kullanabilecei, eitli zelliklere sahip veri trleri tanmlar. C dilinde de nceden tanmlanm 11 adet veri tr vardr.

2. Programcnn tanmlanm olduu veri trleri (user defined types)


Programlama dillerinin ou, nceden tanmlanm veri trlerine ek olarak, programcnn da yeni trler tanmlanmasna izin vermektedir. Programcnn tanmlayaca bir nesne iin nceden tanmlanm veri trleri yetersiz kalyorsa, programc kendi veri trn yaratabilir. C dilinde de programc yeni bir veri trn derleyiciye tantabilir ve tantt veri trnden nesneler tanmlayabilir. Farkl programlama dillerindeki nceden tanmlanan veri trleri birbirlerinden farkl olabilir. Daha nce renmi olduunuz bir programlama dilindeki trlerin aynsn C dilinde bulamayabilirsiniz. C dilininin nceden tanmlanm 11 veri tr vardr. Bu veri trlerinden 8 tanesi tamsay trnden verileri tutmak iin, kalan 3 tanesi ise gerek say trnden verileri tutmak iin tasarlanmtr. Biz bu trlere srasyla "Tamsay veri trleri" (integer types) ve "gerek say veri trleri" (floating types) diyeceiz.

tamsay veri trleri (integer types) C dilinin toplam 4 ayr tamsay veri tr vardr ancak her birinin kendi iinde iaretli ve iaretsiz 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. Bu trleri srasyla inceleyelim: iaretli ve iaretsiz char veri tr : phesiz char szc ingilizce character szcnden ksaltlmtr ve trke "karakter" anlamna gelmektedir. Ancak bu trn ismini, bundan sonraki derste C dilinin bir anahtar szc olduunu reneceimiz char szc ile zdeletirip, "char tr" (ar diye okuyunuz) diye syleyeceiz. aretli char trnden bir nesnenin bir byte'lk bir alanda tutulmas C standartlarnca garanti altna alnmtr.

28

VER TRLER 1 byte'lk bir alan iaretli olarak kullandmzda yazabileceimiz deerlerin -128 / 127 deerleri arasnda deiebileceini say sistemleri dersimizden hatrlayalm. iaretsiz char veri 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. iaretli ve iaretsiz short int veri tr (iaretli ksa tamsay tr - iaretsiz ksa tamsay tr) : Yine bundan sonraki derste reneceimiz gibi, short ve int szckleri C dilinin anahtar szcklerinden olduu iin bu trn ismini genellikle short int, ya da ksaca short tr olarak telaffuz edeceiz. iaretli ve iaretsiz short veri 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 alan kaplayacaktr. iaretli short int veri trnden bir nesne -32768 - +32767 aralndaki tamsay deerlerini tutabilirken, iaretsiz short tr sz konusu olduundan tutulabilecek deerler 0 - +65535 aralnda olabilir. iaretli int (signed int) tr ve iaretsiz int (unsigned int) tr : iaretli ve iaretsiz int veri trnden bir nesne tanmland zaman, nesnenin bellekte ka byte yer kaplayaca sistemden sisteme deiebilir. ounlukla 16 bitlik sistemlerde, int veri , 32 bitlik sistemlerde ise int veri tr 4 byte yer kaplamaktadr. 16 bitlik sistem, 32 bitlik sistem ne anlama geliyor. 16 bitlik sistem demekle ilemcinin yazma (register) uzunluunun 16 bit oldugunu anlatyoruz. 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 olacaktr. iaretli ve iaretsiz long int veri tr (iaretli uzun tamsay tr - iaretsiz uzun tamsay tr) Bu trn ismini genellikle long int, ya da ksaca long tr olarak telaffuz edeceiz. iaretli ve iaretsiz long int veri trnden biriyle tanmlanan bir nesnenin bellekte ka byte yer kaplayaca sistemden sisteme deiebilir. Sistemlerin ounda, long int veri trnden yaratlan nesne bellekte 4 byte'lk bir alan kaplayacaktr. aretli long int veri trnden bir nesne -2147483648 +2147483647 aralndaki tamsay deerlerini tutabilirken, iaretsiz long int tr sz konusu olduundan tutulabilecek deerler 0 - +4.294.967.296 aralnda olur. GEREK SAYI TRLER 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 uyukmaktadr. Sistemlerin hemen hemen hepsinde float veri trnden bir nesne tanmland zaman bellekte 4 byte yer kaplayacaktr. 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 hassasiyet) olarak isimlendirilmitir. Bu standartta 32 bitlik alan 3 blme ayrlmtr. 1 bitlik alan (sign bit): gerek saynn iaret bilgisini yani pzoitif mi negatif mi olduu bilgisini tutar. 8 bitlik alan (exponential part) : 23 bitlik alan (fraction part) : saynn ondalk ksmn tutar.

29

4 Sistemlerin hemen hemen hepsinde double veri trnden bir nesne tanmland zaman bellekte 8 byte yer kaplayacaktr. Gerek saylarn bellekte tutulmas sistemden sisteme deiebilen zellikler ierebilir. Ancak sistemlerin ounda IEEE 754 sayl standarda uyulmaktadr. long double veri trnden bir nesne tanmland zaman bellekte 10 byte yer kaplayacaktr. C dilinin doal veri trlerine ilikin bilgileri aada bir tablo eklinde veriyoruz:

30

VER TRLER

C DLNN NCEDEN TANIMLANMI (DEFAULT ) VER TRLER

TAMSAYI TRLER (INTEGER TYPES)


TR SM signed char unsigned char signed short int unsigned short int signed int unsigned int long int unsigned long int UZUNLUK(byte) (DOS / UNIX) 1 1 2 2 2 4 2 4 4 4 SINIR DEERLER -128 0 -32.768 0 -32.768 -2.147.483.648 0 0 -2.147.483.648 0 127 255 32.767 65.535 32.767 2.147.483.647 65.535 4.294.967.296 2.147.483.647 4.294.967.296

GEREK SAYI TRLER (FLOATING TYPES) TR SM UZUNLUK (byte) 4 8 10 SINIR DEERLER en kk pozitif deer en byk pozitif deer 1.17 x 10-38 3.40 x 1038 (6 basamak hassasiyet) 2.22 x 10-308 1.17 x 10-38 (15 basamak hassasiyet) (15 basamak hassasiyet) tanabilir deil

float double long double

Yukarda verilen tablo sistemlerin ou iin geerli de olsa ANSI C standartlarna gre yalnzca aadaki zellikler garanti altna alnmtr: char tr 1 byte uzunluunda olmak zorundadr. short veri trnn uzunluu int trnn uzunluuna eit ya da int tr uzunluundan kk olmaldr. Yani short <= int long veri trnn uzunluu int trne eit ya da int trnden byk olmak zorundadr. Yani long >= int 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

31

4 char tr 1 byte int tr 2 byte (ilemcinin bir kelimesi kadar) short tr 2 byte (short = int) long tr 4 byte (long > int) alnabilir. Yine 32 bitlik bir ilemci iin yazlan tipik bir uygulamada char tr 1 byte int tr 4 byte (ilemcinin bir kelimesi kadar) short tr 2 byte (short < int) long tr 4 byte (long = int) alnabilir. C dilinin en ok kullanlan veri trleri tamsaylar iin int tr iken gerek saylar iin double veri trdr. Peki hangi durumlarda hangi veri trn kullanmak gerekir. Bu sorunun cevab olarak hazr bir reete vermek pek mmkn deil, zira kullanacamz bir nesne iin tr seerken bir ok faktr sz konusu olabilir, ama genel olarak u bilgileri verebiliriz : Gerek saylarla yaplan ilemler tam saylarla yaplan ilemlere gre ok daha fazla yavatr. Bunun nedeni phesiz gerek saylarn zel bir ekilde belirli bir byte alanna kodlanmasdr. Tamsaylarn kullanlmasnn yeterli olduu durumlarda bir gerek say trnn kullanlmas , alan programn hznn belirli lde yavalatlmas anlamna gelecektir. Bir tamsay trnn yeterli olmas durumunda gerek say trnn kullanlmas programn okunabilirliininin de azalmasna neden olacaktr.

32

BLDRM VE TANIMLAMA

BLDRM VE TANIMLAMA Progamlama dillerinin ounda nesneler kullanlmadan nceye derleyiciye tantlrlar. Nesnelerin kullanlmalarndan nce, zellikleri hakknda derleyiciye bilgi verme ilemlerine bildirim (declaration) denir. Bildirim ilemi yoluyla, derleyiciler nesnelerin hangi zelliklere sahip olduklarn anlarlar ve bylece bu nesneler iin bellekte uygun bir yer tahsisat yapabilirler. Yaratlacak nesne hakknda derleyiciye verilecek en nemli bilgi phesiz nesneye ilikin tr (type) bilgisidir. C dilinde eer yaplan bir bildirim ilemi, derleyicinin bellekte bir yer ayrmasna neden oluyorsa bu ileme tanmlama (definition) denir. Tanmlama nesne yaratan bir bildirimdir. Her tanmlama ilem ayn zamanda bir bildirim ilemidir ama her bildirim ilemi bir tanmlama olmayabilir. Baka bir deyile, tanmlama nesne yaratan bir bildirim ilemidir. C dilinde bir deikeni bildirimini yapmadan nce kullanmak derleme ileminde hata (error) oluumuna yol aar. Bir deikenin derleyiciye tantlmas deikenin trnn ve isminin derleyiciye bildirilmesidir ki, derleyici bu bilgiye dayanarak deiken iin bellekte ne kadar yer ayracan, deikenin iin ayrlan byte'lardaki 1 ve 0 larn nasl yorumlanaca bilgisini elde eder. C Dilinde Bildirim leminin Genel Biimi C programlama Dili'nde bildirim ilemi aadaki ekilde yaplmaktadr : <tr> <nesne ismi> <;> Burada noktal virgl karakterine sonlandrc karakter diyoruz. Noktal virgl ayra trnden bir atomdur ve C'de btn ifadeler noktal virgl ile birbirlerinden ayrlrlar. a = x + 1; b = x + 2; ifadelerinde bulunan noktal virgller bunlarn ayr birer ifade olduklarn gsterirler. Eer bir tek noktal virgl olsayd derleyici iki ifadeyi tek bir ifade gibi yorumlayacakt. a = x + 1 b = x + 2; Yukardaki ifade tek bir ifade gibi yorumlanr ve derleyici buna bir anlam veremez. Tr belirten anahtar szckler, C dilinin nceden tanmlanm veri trlerine ilikin anahtar szcklerdir. Bu szckleri bildirim sentaksnda kullanarak, daha nce renmi olduumuz 11 temel veri trnden hangisinden deiken tanmlamak istediimizi derleyiciye bildirmi oluyoruz. C dilinin nceden tanmlanm veri trlerine ilikin, bilidirim 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 har duyarl (case sensitive) bir dil olduunu hatrlayalm. C dilinin tm anahtar szckleri kk harf ile tanmlanmtr. Tr belirten anahtar szckler aadaki tabloda listelenen seeneklerden biri olmaldr. Keli parantez ierisindeki ifadeler kullanlmas zorunlu olmayan, yani seime bal olan anahtar szckleri
33

gstermektedir. Ayn kullanlabilmektedir. 1 2 3 4 5 6 7 8 9 10 11 char unsigned char short unsigned short int unsigned int long unsigned long float double long double

satrdaki

tr

belirten

anahtar

szckler

tamamen

ayn

anlamda

[signed ] char [signed] short [signed] int unsigned [signed] long unsigned long [int] short [int] signed long [int] [signed] long [int] [signed] short [int]

Yukardaki tablodan da grld gibi, belirli trleri birden fazla ekilde ifade etmek mmkndr. char a; int a; long a; signed char a; signed int a; long int a; signed a; signed long a; signed long int a; Yukarda ayn kolon zerindeki bildirimlerin hepsi ayn trden nesne yaratr. Bildirim ileminde nesne ismi olarak, C dilinin isimlendirme kurallarna uygun olarak seilen herhangi bir isim kullanlabilir. C dilinde isimlendirilenler (identifiers) kavram 6 grubu ierir. Deikenler (variable) bunlardan yalnzca bir tanesidir. Fonksiyonlar (functions), etiketler (labels), makrolar (macros), yap ve birlik isimleri (structure and union tags), enum sabitleri (enum constants) isimlerini programclardan alrlar. C Dilinin simlendirme Kurallar simlendirmede yalnzca 63 karakter kullanlabilir. Bunlar: ngiliz alfabesinde yer alan 26 karakter, (a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t, u, v, w, x, y, z, A, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z) rakam karakterleri (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) ve alttire (underscore) karakteridir. (_) simlendirmelerde yukarda belirtilen karakterlerin dnda baka bir karakterin kullanlmas derleme zamannda hata oluumuna yol aar. (mein boluk karakterinin kullanlmas Trke karakterlerin kullanlmas, +, -, /, *, & ya da $ karakterinin kullanlmas gibi). Deiken isimleri rakam karakteriyle balayamaz. Rakam karakteri dnda, yukarda geerli herhangi bir karakterle balayabilir. C'nin anahtar szckleri isimlendirme amac ile kullanlamaz. simler boluk ieremeyecei iin uygulamalarda genellikle boluk hissi vermek iin alttire (underscore) karakteri kullanlr. genel_katsayi_farki, square_total, number_of_cards gibi. Baka bir teknik de isimlendirmede her szcn ilk harfini Byk, dier harfleri kk yazmaktr.

34

BLDRM VE TANIMLAMA

GenelKatsayiFarki, SquareTotal, NumberOfCards gibi. C dilinde yaplan isimlendirmelerde, isimlerin maksimum uzunluu tanmlanmamtr. Bu derleyicilere gre deiebilir. Ancak bir ok derleyicide 32 says kullanlmaktadr. Eer verilen isim 32 karakterden daha fazla karakter ieriyorsa, derleyici bu ismi budar, yani yalnzca ilk 32 karakterini alglar. C dili byk harf kk harf duyarl olan bir dil olduu iin (case sensitive) isimlendirmelerde de byk harf ve kk harfler farkl karakterler olarak ele alnacaktr : var, Var, VAr, VAR, vAR, vaR deikelerinin hepsi ayr deikenler olarak ele alnacaktr. Bu noktalarn hepsi C dilinin sentaks asndan, hata oluumunu engellemek iin zorunlu durumlar belirtmek iin anlatlmtr. simlendirme yazlan programlarn okunabilirlii asndan da ok nemlidir. Kullanlan isimlerin legal olmalarnn dnda, anlaml olmalarna, kodu okuyacak kiiye bir fikir verecek ekilde seilmelerine de dikkat edilmelidir. Bildirim ilemi noktal virgl ile sonlandrlmaldr. 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 koyularak yaplabilir. Bildirim deyimi yine noktal virgl 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; /* error */

signed ve unsigned szckleri tr belirten anahtar szck(ler) olmadan yalnz balarna kullanlabilirler. Bu durumda int trden bir deikenin bildiriminin yapld kabul edilir: signed x, y; ile signed int x, y; tamamen ayn anlamdadr. Yine unsigned u;
35

ile unsigned int u; tamamen ayn anlamdadr. Ancak bu tr bir bildirimi tavsiye etmiyoruz, standartlar komitesi ileride bu zelliin dilin kurallarndan kaldrlabileceini bildirmitir. (deprecated feature). Bildirim ileminde, tr belirten anahtar szck birden fazla ise bunlarn yazm sras nemli deildir, ama okunabilirlik asndan nce iaret belirten anahtar szcn sonra tip 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;

hepsi geerli bildirimlerdir. Ama yukardaki bildirimde, seimlik olan anahtar szckler zellikle kullanlmak isteniyorsa 1. yazm biimi okunabilirlik asndan tercih edilmelidir. Bildirimlerin Kaynak Kod inde Yapl Yerleri C dilinde genel olarak 3 yerde bildirim yaplabilir : 1. Bloklarn iinde 2. Tm bloklarn dnda. 3. Fonksiyon parametre deikeni olarak fonksiyon parantezlerinin ierisinde Fonksiyon parametre parantezleri ierisinde yaplan bildirimler, baka bir sentaks kuralna uyarlar, bu bildirimler fonksiyonlar konusuna gelindiinde detayl olarak incelenecektir. C dilinde eer bildirim bloklarn iinde yaplacaksa, bildirim ilemi bloklarn ilk ilemi olmak zorundadr. Baka bir deyile bildirimlerden nce baka bir ifade bulunmamal ya da bildirimden nce bir fonksiyon arlmamaldr. (Aksi halde derleme zaman srasnda hata oluur.) Bildirimin mutlaka ana bloun banda yaplmas gibi bir zorunluluk yoktur. Eer iie bloklar varsa iteki herhangi bir bloun banda da (o bloun ilk ilemi olacak ekilde) bildirim yaplabilir. rnekler : {

int var1, var2; char ch1, ch2, ch3; var1 = 10; float f;

/* error */

} Yukardaki rnekte var1, var2, ch1, ch2, ch3 deikenlerinin tanmlanma yerleri dorudur. Ancak f deikeni yanl yerde bildirilmitir. nk bildirim ileminden nce baka bir ilem (deyim) yer almaktadr. Bu durum derleme aamasnda hata oluumuna neden olur. Ayn program paras u ekilde yazlm olsayd bir hata sz konusu olmazd :
36

BLDRM VE TANIMLAMA

int var1, var2; char ch1, ch2, ch3; var1 = 10; { float f; }

} bu durumda artk f deikeni de kendi blounun banda (ilk ilem olarak) tanmlanmtr. leride de greceimiz gibi C dilinde tek bana bir noktal virgl, bir deyim oluturur. C sentaksna gre oluan bu deyim icra edilebilir bir deyimdir. Dolaysyla aadaki kod parasnda y deikeninin tanmlamas derleme zamannda hata oluturacaktr. {

int x;; int y;

/* hata! ikinci sonlandrc atom icra edilebilir bir deyim olarak ele alnyor. */

Ayn ekilde bo bir blok da C dilinde bir deyim gibi ele alnr. Bu yazm tamamen noktal virgln (sonlandrcnn) yalnz kullanlmasna edeerdir. Dolaysyla aadaki kod paras da hataldr: {

int x; {} int y;

/* ERROR ! y deikeninin bildirimi doru yerde deil. */

} Bir ya da birden fazla deyimin de blok iine alnmas C dilinde bileik deyim (compound statement) ismini alr ve bileik deyimler de icra edilebilir deyim kategorisine girerler. Dolaysyla aadaki kod paras da hataldr. {

{int x;} int y;

/* ERROR */

(C++ dilinde blok iinde bildirimi yaplan deikenlerin, bloklarn ilk ilemleri olacak ekilde bildirilmeleri zorunlu deildir. Yani C++ da deikenler bloklarn iinde herhangi bir yerde bildirilebilirler.)

37

38

SABTLER

5 . BLM :

SABTLER

Veriler ya nesnelerin ierisinde ya da dorudan sabit biiminde bulunurlar. Sabitler nesne biiminde olmayan, programc tarafndan dorudan girilen verilerdir. Sabitlerin saysal deerleri derleme zamannda tam olarak bilinmektedir. rnein : x = y + z; ifadesi bize a ve b iindeki saylarn toplanaca ve cye 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. Nesnelerin trleri olduu gibi sabitlerin de trleri vardr. Nesnelerin trleri daha nce grdmz gibi bildirim yaplrken belirlenir. Sabitlerin trlerini ise derleyici, belirli kurallar dahilinde sabitlerin yazl biimlerinden tespit eder. Sabitlerin trlerini bilmek zorundayz, nk C dilinde sabitler, deikenler ve operatrler bir araya getirilerek (kombine edilerek) ifadeler (expressions) oluturulur. Daha sonra detayl greceimiz gibi C dilinde ifadelerin de bir tr vardr ve ifadelerin trleri, ierdikleri sabit ve deikenlerin trlerinden elde edilir. O halde sabit trlerini detayl olarak inceleyelim : Tamsay Sabitleri (integer constants) aretli Tamsay Sabitleri (signed int) : Bunlar tipik olarak int trden deikenlerine atanan ve tamsay biiminde olan sabitlerdir, yazlrken herhangi bir ek almazlar. Cde int tr snrlar iinde olan her tamsay birer tamsay sabit (ya da int trden sabit ) olarak ele alnr. -25 30000 25789 -320 0 saylarnn hepsi iaretli tamsay (signed int) sabiti olarak ele alnrlar, nk int tr say snrlar iinde bulunuyorlar ve sonlarnda herhangi bir ek bulunmuyor. int tr sistem bamldr ve int sabitleri de sistemden sisteme deiebilir. sistem DOS, WINDOWS 3.1 UNIX WINDOWS 95 uzunluk 2 byte 4 byte snr deerler - 32768, + 32767 -2147483648, +2147483647

rnein 425000 Dosta int sabiti deildir ama UNIXte int sabittir. Uzun Tamsay Sabitleri(Long integer constants)

39

5 Uzun Tamsay Sabitleri aretli Uzun Tamsay Sabitleri (signed long) long trden sabitler iki trl ifade edilirler : 1. long int trnn say snrlar iinde bulunan bir saynn sonuna L ya da l yazarak. Bu durumda derleyiciler ilgili say int snrlar iinde olsa da long sabit olarak ele alr. 22345l long sabittir. nk sonunda l eki var. 0l, -23465L, 325l long sabitlerdir. long sabit kullanmnda alglanmas daha kolay olduu iin L soneki tercih edilmelidir. l soneki 1 rakamyla grnm asndan ok benzedii iin karkla neden olabilir. 2. int trn say snrlarn aan fakat long int tr say snrlar iinde kalan her tamsay dorudan long int trden sabit olarak ele alnr. Bu durum doal olarak, DOS gibi int ve long trlerinin birbirinden farkl olduu sistemlerde anlamldr. rnein DOSda 325000 -33333 1278902 long trden sabitlerdir. Oysa 32 bitlik sistemlerde long trnn uzunluuyla int trn uzunluu ayn (4 byte) olduu iin bu saylar int sabiti olarak ele alnacaktr. Bu sistemlerde yukardaki saylar long sabit olarak ele almak istersek sonlarna l ya da L eklememiz gerekmektedir. Karakter Sabitleri (char) char sabitleri tipik olarak char trden nesnelere atanan sabitlerdir. (Byle bir zorunluluk yok.) char trden sabitler C dilinde drt ayr biimde bulunabilirler. 1. stenilen bir karakter tek trnak (single quote) ierisinde kullanlrsa char trden sabit olarak ele alnr. rnek : 'a' 'J' '' ':' '8' '<' Yukardaki gsterimlerin herbiri birer char trden sabitidir. C'de tek trnak ierisinde belirtilen char sabitleri, aslnda o karakterin karakter setindeki (rnein ASCII tablosundaki) sra numarasn gsteren bir tamsaydr. { char ch; ch = 'a'; ...

40

SABTLER

Bu rnekte aslnda ch isimli char trden deikene a karakterinin ASCII tablosundaki sra numaras olan 97 says aktarlmaktadr. Tek trnak iindeki karakter sabitlerini grnce aslnda onlarn kk birer tamsay olduunu bilmeliyiz. nk bellekte karakter diye birey yoktur herey ikilik sistemde 1 ve 0 lardan oluan saylardr. Yukardaki rnekte istersek ch deikenine aadaki gibi bir atama yapabiliriz: ch = 'a' + 3; Bu durumda ch deikenine saysal olarak 100 deeri atanacaktr. Bu sayya da ASCII tablosundaki 'd' karakteri karlk gelir. 2. nceden tanmlanm ters bl karakter sabitleri (escape sequences) Yukarda tanmlanan yntemde ekrana baslamayan yani ekranda grnt oluturmayan (non printable) karakterleri ifade edemeyiz. rnein an karakteri (an sesi) ya da ters boluk (backspace) karakteri ekrana baslamaz. Tek trnak iindeki ters bl (back slash) karakterinden sonra yer alan baz karakterler ok kullanlan ancak baslamayan baz karakterlerin yerlerini tutarlar. Bunlarn listesi aada verilmitir: nceceden Tanmlanm Ters Bl Karakter Sabitleri (Escape Sequences) Tanm NULL karakter 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) ters bl (back slash) ASCII No 0 7 8 9 10 11 12 13 34 92

'\0' '\a' '\b' '\t' '\n' '\v' '\f' '\r' '\"' '\\'

'\x0' '\x7' '\x8' '\x9' '\xA' '\xB' '\xC' '\xD' '\x22' '\x5C'

'\0' '\07' '\010' '\011' '\012' '\013' '\014' '\015' '\042' '\134'

kullanllarna bir rnek : {

char ch; ch = '\a';

} 3. 16'lk (hexadecimal) say sisteminde tanmlanm karakter sabitleri Tek trnak iinde tersbl ve x karakterlerinden sonra bir hex say verilirse bu ASCII tablosundaki o saysal deerin gsterdii sra numarasndaki karaktere iaret eden bir karakter sabitidir. '\x41' '\xff' '\x1C' /* 41H numaral ASCII karakteridir. */ /* FFH numaral '2' karakter sabitidir. /* 1C numaral ASCII karakter sabitidir. */

*/

Kk "x" yerine byk harfle "X" yazmak C'nin ilk klasik versiyonunda kabul ediliyordu imdi artk geerli deildir. rnek :

41

char harf; harf = '\x41';

} Yukardaki rnekte harf isimli char trden deikene 41H ASCII sra no.lu karakter atanmtr. Bu da desimal sistemdeki 65 saysna eittir. 65 sra nolu ASCII karakteri 'A' karakteridir. Dolaysyla harf isimli deikene 'A' atanmtr. 4. 8'lik (octal) say sistemde tanmlanm karakter sabitleri Tek trnak iinde tersbl karakterinden sonra bir oktal say yazlrsa bu kullanlan karakter setindeki o saysal deerin gsterdii sra numarasndaki karaktere iaret eden bir karakter sabitidir. Tek trnak iindeki ters bl karakterini izleyen say basamaktan uzun olmamaldr. Sekizlik saylarn yazmnda olduu gibi saynn banda sfr olma zorunluluu yoktur. Bu ekilde yazlabilecek en byk karakter sabiti '\377' dir.: '\012' '\16' '\123' /* 10 numaral ASCII karakteri, Tam say deeri 10 */ /* 14 numaral ASCII karakteri. Tam say deeri 14 */ /* 83 numaral ASCII karakteri. Tam say deeri 83 */

Program iinde kullanmna bir rnek: { char a, b; a = '\xbc' ; b = '\012'; } 7 numaral ASCII karakteri olan an karakterini sabit olarak 3 biimde de yazabiliriz. '\x7' '\07' '\a' /* hex gsterimli karakter sabiti */ /* oktal gsterimli karakter sabiti */ /* nceden belirlenmi ters bl karakter sabiti */ /* hex sistemde tanmlanm char sabit */ /* oktal sistemde tanmlanm bir char sabit */

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 sabiti eklinde ifade edersek hangi sistem olursa olsun an sesi karakterini verecektir. Ayrca kodu okuyan kii an sesi karakterinin 7 numaral ASCII karakteri olduunu bilmeyebilir ama C programcs olarak '\a' nn an sesi karakteri olduunu bilecektir. Karakter sabitleri konusunu kapatmadan nce karakter setleri konusunda da biraz bilgi verelim: Gnmzde kullanlan en popler karakter seti ASCII karakter setidir. ASCII (American Standard Code for Information Interchange) szcklerinin baharflerinden oluan bir ksaltmadr. ASCII setinin orjinal versiyonunda karakterler 7 bitlik bir alanda kodlanmtr. Baz bilgisayarlar ise 8 bit alana geniletilmi ASCII seti kullanrlar ki bu sette 128 yerine 256 karakter temsil edilebilmektedir. Farkl bilgisayarlar farkl karakter setleri kullanabilmektedir. rnek olarak IBM mainframe'leri daha eski bir set olan EBCDIC seti kullanrlar. Unicode ismi verilen daha gelitirilmi bir karakter seti vardr ki
42

SABTLER karakterler 2 byte alanda temsil edildikleri iin bu sette 65.536 farkl karakter yer alabilmektedir. Gelecekte bir ok makinann bu karakter setini destekleyecek biimde tasarlanaca dnlmektedir. aretsiz trlere ilikin sabitler aretsiz trlere ilikin sabitler onlarn iaretli biimlerinin sonuna u ya da U getirilmesiyle elde edilirler. -15000 15000U 1200L 1200Lu (signed) int sabit (unsigned) int sabit. (signed) long sabit (unsigned) long sabit.

Sonek olarak kullanlan l, L, u ve U harflerinin sras nemli deildir. 123ul 123UL 123Lu 123lu Yukardaki hepsi geerli birer uzun tamsay (unsigned long int) sabittir. Tamsay sabitlerinin 16'lk ve 8'lik sistemlerde gsterilmesi C'de tamsay sabitleri (char, int, long) 10'luk sistemin yansra 16'lk ve 8'lik sistemlerde de yazlabilirler. Bu say sistemleriyle yazlm tamsay sabit trleri iin yukarda verilen kurallar aynen geerlidir. nk bir sayy 16'lk ya da 8'lik sistemde yazmakla onun yalnzca grnmn deitirmi oluruz. Sabit trleri gsterim biimiyle deil nicelikle ilikilidir. C dilinde ikilik say sisteminde sabitlerin yazlmas sz konusu deildir. 16'lk sistemde gsterim 0Xbbb.. biimindedir. (b karakterleri basamaklar gsteriyor, 9'dan byk basamak deerleri iin A, B, C, D, E, F karakterleri ya da a, b, c, d, e, f karakterleri kullanlabilir. 8'lik sistemde ise 0bbb.. biimindedir. (nadir olarak kullanlr). rnekler: 0x12 says hex gsterimli bir tamsay (int) sabit. 0X12L says hex gsterimli bir uzun tamsay (long) sabit. 0x1C205470 hex gsterimli bir uzun tamsay (long) sabit. nk (DOS'da) tamsay say snrn amaktadr. 0x1934ul hex gsterimli iaretsiz uzun tamsay (unsigned long) sabittir. 01234 oktal gsterimli tamsay (int) sabit 0567L oktal gsterimli uzun tamsay (long) sabit 0777U oktal gsterimli iaretsiz tamsay (unsigned int) sabit 0452Lu oktal gsterimli (unsigned long) sabit. Sabitler yukarda gsterildii gibi her say sisteminde de yazlabilir, hatta bir ifade iinde kullanlan sabitler farkl say sistemlerinde de yazlm olabilirler, bu derleme zamannda error oluturacak bir neden olmayp tamamen legaldir. ... int x; x = 0x1AC2 + 345 + 01234;

43

5 Gerek Say Sabitleri (floating Constants) 1. float trden sabitler Nokta ieren ve sonuna f ya da F getirilmi sabitler float trden sabitler olarak ele alnrlar. rnein: 1.31F 10.F -2.456f float trden sabitlerdir. Not : Klasik C'de, yani C dilinin standartlatrlmasndan nceki dnemde float trden bir sabit elde etmek iin, saynn sonuna f eki almas yeterliydi yani nokta iermesi gerekmiyordu ama standartlarda yaplan deiiklikle artk float sabitlerin sonuna ek gelse de mutlaka nokta iermeleri gerekiyor. Yani derleyiciler 3f eklinde bir yazm derleme zamannda bir hata (error) mesajyla bildiriyorlar. 2. double trden sabitler Sonuna f ya da F eki almam nokta ieren sabitler ile float duyarln am sabitler double sabitler olarak deerlendirilirler. rnein : -24.5 double trden sabittir.

3. long double trden sabitler long double trden sabitler noktal ya da stel biimdeki saylarn sonuna l ya da L getirilerek elde edilirler : 1.34L 10.2L long double trden sabitlerdir. Gerek Say Sabitlerinin stel Biimde Gsterilmesi Gerek say sabitleri stel biimde de ifade edilebilirler, bunun iin saynn sonuna e ya da E eki getirilerek bir tamsay yazlr. Bu, saynn 10x gibi bir arpanla arpldn gsterir. 2.3e+04f 1.74e-6F 8.e+9f burada e 10'un kuveti anlamna gelmektedir: 1.34E-2f ile 0.0134 -1.2E+2F ile 120.f ayn sabitlerdir.

44

FONKSYONLAR

6 . BLM :

FONKSYONLAR

C'de alt programlara fonksiyon denir. Fonksiyon szc burada matematiksel anlamyla deil dier programlama dillerinde kullanlan, "alt program", "prosedr", "subroutine" szcklerinin karl olarak kullanlmaktadr. Fonksiyonlar C dilinin temel yap talardr. altrlabilen bir C program en az bir C fonksiyonundan oluur. Bir C programnn oluturulmasnda fonksiyon saysnda bir kstlama yoktur. Fonksiyonlarn onlar aran fonksiyonlardan aldklar girdileri ve yine onlar aran fonksiyonlara gnderdikleri ktlar vardr. Fonksiyonlarn girdilerine aktel parametreler (actual parameters) ya da argumanlar (arguments) diyoruz. Fonksiyonlarn ktlarna geri dn deeri (return value) diyoruz. Bir fonksiyon iki farkl amala kullanlabilir : 1. Fonksiyon, icras sresince belli amalar gerekletirir. (Belli ilemleri yapar) 2. Fonksiyon icras sonunda retecei bir deeri kendisini aran fonksiyona gnderebilir. Fonksiyonlarn Tanmlanmas ve arlmas Bir fonksiyonun ne i yapacann ve bu ii nasl yapacann C dilinin sentaks kurallarna uygun olarak anlatlmasna o fonksiyonun tanmlanmas (definition) denir. Fonksiyon tanmlamalar aada incelenecei gibi birtakm sentaks kurallarna tabidir. Bir fonksiyonun arlmas ise o fonksiyonun yapaca ii icraya davet edilmesi anlamna gelir. Fonksiyon arma ifadesi karlnda derleyici, programn akn ilgili fonksiyonun kodunun bulunduu blgeye aktaracak ekilde bir kod retir. Programn ak fonksiyonun kodu iinde akp bu kodu bitirdiinde, yani fonksiyon icra edildiinde, programn ak yine fonksiyonun arld noktaya geri dnecektir. Fonksiyon armaya ilikin sentaks da yine aada aklanacaktr. Fonksiyonlarn Geri Dn Deerleri (return values) Bir fonksiyonun yrtlmesi sonunda onu aran fonksiyona dnnde gnderdii deere, fonksiyonun geri dn deeri (return value) denmektedir. Her fonksiyon bir geri dn deeri retmek zorunda deildir. Fonksiyonlarn geri dn deerleri farkl amalar iin kullanlabilir; 1. Baz fonksiyonlar tek bir deer elde etmek amacyla tasarlanmlardr. Elde ettikleri deeri de kendilerini aran fonksiyonlara geri dn deeri olarak iletirler. rnein: y = pow(2, 3); pow fonksiyonu standart bir C fonksiyonudur. Birinci parametresiyle belirtilen saynn ikinci parametresiyle belirtilen kuvvetini hesaplayarak, hesaplad sayy geri dn deeri olarak kendisini aran fonksiyona iletir. Yukardaki rnekte 2 saysnn 3. kuvveti bu fonksiyon yardmyla hesaplanarak bulunan deer y deikenine atanmtr. 2. Baz fonksiyonlarn geri dn deerleri fonksiyonun yrtlmesi srasnda yaplan ilemlerin baars hakknda bilgi verir. Yani bu tr fonksiyonlarn geri dn deerleri test amacyla kullanlmaktadr. Geri dn deerleri yaplmas istenen ilemin baarl olup olmamas durumunu aklar. rnein : p = malloc(200);

45

6 ifadesiyle bellekte 200 byte uzunluunda bir blok tahsis etmek isteyen programc bu ilemin baarl bir biimde yerine getirilip getirilmediini de test etmek zorundadr. Hemen arkasndan p deikeninin ald deeri kontrol edecek ve ilemin baars hakknda bir karara varacaktr. Dolaysyla malloc fonksiyonunun geri dn deeri, fonksiyonun yapmas gereken iin baarl bir ekilde sonulanp sonulanmadn gstermektedir. 3. Baz fonksiyonlar kendilerine gnderilen argumanlar belirli bir kritere gre test ederler. rettikleri geri dn deerleri ise test sonucunu belirtir. rnein: if (isalpha(ch)) { ... } Burada isalpha fonksiyonu arguman olarak gnderilen karakterin bir harf karakteri olup olmadn test eder. Eer harf karakteriyse, isalpha fonksiyonu 0 d bir deere geri dnecek, eer harf karakteri deilse 0 deerine geri dnecektir. aran fonksiyonda da geri dn deerine gre farkl ilemler yaplabilecektir. 4. Baz fonksiyonlar hem belli bir amac gerekletirirler hem de buna ek olarak amalarn tamamlayan bir geri dn deeri retirler. rnein : x = printf("Merhaba Dnya\n"); Burada printf fonksiyonu ekrana Merhaba Dnya yazsn yazmak iin kullanlmtr. Ancak ekrana yazd karakter saysn da geri dn deeri olarak vermektedir. Bir yaz iersinde bulunan belirli bir karakteri silecek bir fonksiyon tasarladmz dnelim. Fonksiyon iini bitirdikten sonra yazdan ka karakter silmi olduunu geri dn deeri ile arld yere bildirilebilir. 5. Bazen geri dn deerlerine ihtiya duyulmaz. rnein yalnzca ekran silme amacyla tasarlanm olan bir fonksiyonun geri dn deerine sahip olmas gereksizdir. clrscr(); clrscr fonksiyonu yalnzca ekran siler, byle bir fonksiyonun geri dn deerine ihtiyac yoktur. Fonksiyonlarn geri dn deerlerinin de trleri sz konusudur. Fonksiyonlarn geri dn deerleri herhangi bir trden olabilir. Geri dn deerlerinin trleri fonksiyonlarn tanmlanmas srasnda belirtilir. Fonksiyonlarn Tanmlanmas Kendi yazdmz fonksiyonlar iin tanmlama (definition) terimini kullanyoruz. C'de fonksiyon tanmlama ileminin genel biimi yledir: [Geri dn deerinin tr] <fonksiyon ismi> ([parametreler]) { ... ... } Yukardaki gsterimde asal parantez iinde belirtilen ifadeler zorunlu olarak bulunmas gerekenleri keli parantez iinde belirtilen ifadeler ise bulunmas zorunlu olmayan, istee bal (optional) ifadeleri
46

FONKSYONLAR gstermektedir. Tanmlanan fonksiyonlar en az bir blok ierirler. Bu bloa fonksiyonun ana blou denir. Ana blok iinde istenildii kadar iie blok yaratlabilir. Aadaki fonksiyon tanmlamasndan fonk1 fonksiyonunun parametre almadn ve geri dn deerinin de double trden olduunu anlyoruz. double fonk1() { ... ... Fonksiyonun ana blou ... }

void Anahtar Szc Bir fonksiyonun parametre deikeni ya da geri dn deeri olmak zorunda deildir. Bir fonksiyonun parametre deikeni olmad iki ekilde belirtilebilir: 1. Fonksiyon parametre parantezinin ii bo braklr, yani buraya hibirey yazlmaz. 2. Fonksiyon parametre parantezinin iine void anahtar szc yazlr. fonk() { ... } fonk(void) { ... }

Yukardaki tanmlamalar C'de ayn anlama gelmiyor. Fonksiyon prototipleri konusunu renirken bu iki tanmlama arasndaki fark da renmi olacaz. imdilik bu iki tanmlamann ayn anlama geldiini ve fonksiyonun parametre almadn belirttiklerini varsayacaz. Geri dn deerine ihtiya duyulmad durumlarda da geri dn deerinin tr yerine void anahtar szc yerletirilir. rnein: void sample(void) { ... } Yukarda tanmlanan sample fonksiyonu parametre almamakta ve bir geri dn deeri de retmemektedir. Fonksiyon tanmlarken geri dn deeri yazlmayabilir. Bu durum geri dn trnn olmad anlamna gelmez. Eer geri dn deeri yazlmazsa, C derleyicileri tanmlanan fonksiyonun int trden bir geri dn deerine sahip olduunu varsayarlar. rnein : sample2() { ... } Tanmlanan sample2 fonksiyonunun parametresi yoktur ama int trden bir geri dn deeri vardr. C dilinde fonksiyon iinde fonksiyon tanmlanamaz!
47

rnein aadaki durum error oluturur, nk sample2 fonksiyonu sample1 fonksiyonunun iinde tanmlanmtr: double sample1() { ... int sample2() { ... } ... }

/* error */

tanmlamann aadaki ekilde yaplmas gerekirdi : double sample1() { ... } int sample2() { ... } Fonksiyonlarn arlmas (function calls) C dilinde fonksiyon arma operatr olarak () kullanlmaktadr. Bir fonksiyon arld zaman programn ak fonksiyonu icra etmek zere bellekte fonksiyonun kodunun bulunduu blgeye atlar, fonksiyonun icra edilme ilemi bittikten sonra da ak tekrar aran fonksiyonun kalnan yerinden devam eder. Bir fonksiyonun geri dn deeri varsa, fonksiyon arma ifadesi geri dn deerini retir. Geri dn deeri bir deikene atanabilecei gibi dorudan aritmetik ilemlerde de kullanlabilir. rnein: sonuc = hesapla(); Burada hesapla fonksiyonunun arlma ifadesiyle retilen geri dn deeri sonuc atanmaktadr. Bir baka deyile bir fonksiyon arma ifadesinin rettii deer, ilgili rettii (eer retiyorsa) geri dn deeridir. Yukardaki rnekte nce hesapla() arlacak daha sonra fonksiyonun icra edilmesiyle oluan geri dn deeri sonuc atanacaktr. deikenine fonksiyonun fonksiyonu deikenine

Fonksiyonlarn geri dn deerleri nesne deildir yani sol taraf deeri (L value) deildir. Yani C dilinde aadaki gibi bir atama her zaman hata verecektir: hesapla() = 5; /* hata (L value required) */

Fonksiyonlarn geri dn deerleri sa taraf deeri (R value) dir. sonuc = hesapla1() + hesapla2() + x + 10;
48

FONKSYONLAR

gibi bir ifade geerlidir. arlm olan hesapla1 ve hesapla2 fonksiyonlar icra edilerek retilen geri dn deerleri ile x deikeni iindeki deer ve 10 sabiti toplanacaktr. fadeden elde edilen deer sonu deikenine atanacaktr. Fonksiyonlar ancak tanmlanm fonskiyonlarn ierisinden arlabilirler. Bloklarn dndan fonksiyon arlamaz. aran fonksiyon ile arlan fonksiyonun her ikisi de ayn ama kod ierisinde bulunmak zorunda deildir. aran fonksiyon ile arlan fonksiyon farkl ama kodlar ierisinde de bulunabilir. nk derleme ilemi srasnda bir fonksiyonun arldn gren derleyici, ama kod ierisine (yani .obj iine) arlan fonksiyonun adn ve arl biimini yazmaktadr. aran fonksiyon ile arlan fonksiyon arasnda balant kurma ilemi, balama aamasnda, balayc program (linker) tarafndan yaplr. Bu nedenle tanmlanan bir fonksiyon ierisinde, var olmayan bir fonksiyon arlsa bile derleme aamasnda bir hata olumaz. Hata balama aamasnda oluur. nk balayc arlan fonksiyonu bulamayacaktr. Btn C programlar almaya main fonksiyonundan balar. Programn balad nokta olma dnda main fonksiyonunun dier fonksiyonlardan baka hibir fark yoktur. main fonksiyonun icras bitince program da sonlanr. Bir C programnn alabilmesi iin mutlaka bir main fonksiyonuna sahip olmas gerekir. Eer main fonksiyonu yoksa hata balama (linking) aamasnda balayc program tarafndan bildirilecektir.

Standart C Fonksiyonlar Standard C fonksiyonlar, C dilinin standarlatrlmasndan sonra, her derleyicide bulunmas zorunlu hale getirilmi fonksiyonlardr. Yani derleyicileri yazanlar mutlaka standard C fonksiyonlarn kendi derleyicilerinde tanmlamak zorundadrlar. Bu durum C dilinin tanabilirliini (portability) artran ana faktrlerden biridir. Bir fonksiyonun derleyiciyi yazanlar tarafndan tanmlanm ve derleyici paketine eklenmi olmas, o fonksiyonun standart C fonksiyonu olduu anlamna gelmez. Derleyiciyi yazanlar programcnn iini kolaylatrmak iin ok eitli fonksiyonlar yazarak derleyici paketlerine eklerler. Ama bu tr fonksiyonlarn kullanlmas durumunda, oluturulan kaynak kodun baka bir derleyicide derlenebilmesi ynnde bir garanti yoktur, yani artk kaynak kodun tanabilirlii azalr. rnein printf fonksiyonu standart bir C fonksiyonudur. Yani printf fonksiyonu her derleyici paketinde ayn isimle bulunmak zorundadr. Standart C fonksiyonlar zel ktphanelerin ierisinde bulunurlar. Balk dosyalar iinde, yani uzants .h biiminde olan dosyalarn iinde standart C fonksiyonlarnn prototipleri bulunmaktadr. Fonksiyon prototipleri konusu ileride detayl olarak incelenecektir. Ktphaneler (libraries) derlenmi dosyalardan oluur. DOS'da ktphane dosyalarnn uzants .lib, UNIX'de ise .a (archive) biimindedir. WINDOWS altnda uzants .dll biiminde olan dinamik ktphaneler de bulunmaktadr. Derleyicileri yazanlar tarafndan kaynak kodu yazlm standart C fonksiyonlar nce derlenerek .obj haline getirilirler ve daha sonra ayn gruptaki dier fonksiyonlarn .obj halleriyle birlikte ktphane dosyalarnn iine yerletirilirler. Standart C fonksiyonlar balama aamasnda, balayc (linker) tarafndan alabilir (.exe) kod ierisine yazlrlar. Entegre alan derleyicilerde balayclar ama kod ierisinde bulamadklar fonksiyonlar, yerleri nceden belirlenmi ktphaneler iinde ararlar. Oysa

49

6 komut satrl uyarlamalarnda (command line version) balayclarn hangi ktphanelere bakaca komut satrnda belirtilir. Standart fonksiyonlarn kullanmak programlarn tanabilirliini artrd gibi proje gelitirme sresini de ksaltr. Bu yzden iyi bir C programcsnn C dilinin standart fonksiyonlarn ok iyi tanmas ve bu fonksiyonlar yetkin bir ekilde kullanabilmesi gerekmektedir. Fonksiyonlarn Geri Dn Deerlerinin Oluturulmas C dilinde fonksiyonlarn geri dn deerleri return anahtar szc ile oluturulur. return anahtar szcnn bir baka ilevi de iinde bulunduu fonksiyonu sonlandrmasdr. #include <stdio.h> int sample(void) { int x = 10; int y = 20; return x * y; } int main() { int c; c = sample(); printf("c = %d\n", c); return 0; } Yukardaki rnekteki sample fonksiyonunda return anahtar szcnn yannda yer alan x * y ifadesi sample fonksiyonunu sonlandrmakta ve sample fonksiyonunun geri dn deerini oluturmaktadr. Fonksiyonun geri dn deeri, main fonksiyonu iinde c deikenine atanm ve daha sonra standart C fonksiyonu olan printf ile c fonksiyonunun deeri ekrana yazdrlmtr. fonksiyonun geri dn deerini baka bir deikene atamadan aadaki ifade ile de dorudan ekrana yazdrabilirdik : printf("%d\n", sample()); Ayn rnekte main fonksiyonu iinde de bir return ifadesinin yer ald grlmektedir. main de bir fonksiyondur ve main fonksiyonunun da bir geri dn deeri olabilir. main fonksiyonun geri dn deeri programn icras bittikten sonra iletim sistemine bildirilmektedir. main fonksiyonunun bana bir geri dn deer tr yazlmazsa derleyiciler main fonksiyonunun geri dn deerinin int trden olduunu varsayarlar. zellikle yeni derleyiciler, tanmlamalarnda bir geri dn deeri retecekleri belirtilen fonksiyonlarnn, return anahtar szcyle geri dn deeri retmemelerini bir uyar (warning) mesaj ile bildirirler. Borland derleyicilerinde bu uyar mesaj genellikle "warning : function should return a value..." eklindedir. Bu uyar mesajn kesmek iin iki yol vardr: 1. main fonksiyonu da yukardaki rnekte olduu gibi int trden bir geri dn deeri retir. Geleneksel olarak bu deer 0 ise programn problemsiz bir ekilde sonlandrld anlamna gelir.

50

FONKSYONLAR 2. main fonksiyonunun bana void anahtar szc yazlarak bu fonksiyonun bir geri dn deeri retmeyecei derleyiciye bildirilir. Bu durumda derleyici geri dn deeri beklemedii iin bir uyar mesaj gndermez. return anahtar szcnn kullanlmas zorunlu deildir. Bir fonksiyon iinde return anahtar szc kullanlmamsa fonksiyonun icras, fonksiyonun ana blounun sonuna gelindiinde otomatik olarak biter. Tabi bu tr bir fonksiyon anlaml bir ekilde bir geri dn deeri retemeyecektir. Bir geri dn deerine sahip olacak ekilde tanmlanm fakat return ile geri dn deeri oluturulmam fonksiyonlar rastgele bir deer dndrrler. return anahtar szcnden sonra parantez kullanlabilir ama parantez kullanm zorunlu deildir. Okunabilirlik asndan zellikle uzun return ifadelerinde parantez kullanm tavsiye edilmektedir. return (a * b - c * d); return 5; /* return ifadesinin deiken iermesi bir zorunluluk deildir. Bir fonksiyon sabit bir deerle de geri dnebilir. */

return sample(); Bu rnekte return anahtar szcnden sonra bir fonksiyon arma ifadesi yer almaktadr. Bu durumda nce arlan fonksiyon icra edilir, ve geri dn deeri elde edilir, daha sonra elde edilen geri dn deeri tanmlanmas yaplan fonksiyonun da geri dn deeri yaplmaktadr. Geri dn deeri olmayan fonksiyonlarda return anahtar szc yannda bir ifade olmakszn tek bana da kullanlabilir : return; Bu durumda return iinde yer ald fonksiyonu geri dn deerini oluturmadan sonlandrr. C dilinde fonksiyonlar yalnzca bir geri dn deeri retebilirler. Bu da fonksiyonlarn kendilerini aran fonksiyonlara ancak bir tane deeri geri gnderebilmeleri anlamna gelmektedir. Ancak, fonksiyonlarn birden fazla deeri ya da bilgiyi kendilerini aran fonksiyonlara iletmeleri gerekiyorsa, C dilinde bunu salayacak baka mekanizmalar vardr ve bu mekanizmalar ileride detayl olarak incelenecektir. Fonksiyonlarn rettii geri dn deerlerinin kullanlmas ynnde bir zorunluluk yoktur. rnein fonk() fonksiyonu int trden bir deeri geri dnen bir fonksiyon olsun: a = fonk(); yukardaki ifadese fonk fonksiyonunun geri dn deeri a deikenine atanmaktadr. Dolaysyla biz bu fonksiyonu bir kez armamza karn artk geri dn deerini a deikeninede tuttuumuz iin, bu geri dn deerine fonksiyonu tekrar armadan istediimiz zaman ulaabiliriz. Ancak: fonk(); eklinde bir fonksiyon arma ifadesinde artk geri dn deeri bir deikende saklanmamakatdr. Bu duruma geri dn deerinin kullanlmamas denir. (discarded return value).

51

6 rnein standart bir C fonksiyonu olan printf fonksiyonun da bir geri dn deeri vardr (printf fonksiyonu ekrana bastrlan toplam karakter saysna geri dner) ama bu geri dn deeri nadiren kullanlr.

Fonksiyon Parametre Deikenlerinin Tanmlanmas Bir fonksiyonun parametreleri ya da parametre deikenleri fonksiyonlarn kendilerini aran fonksiyonlardan aldklar girdileri tutan deikenleridir. Bir fonksiyonun parametre says ve bu parametrelerin trleri gibi bilgiler, fonksiyonlarn tanmlanmas sarsnda derleyicilere bildirilirler. C dilinde fonksiyonlarn tanmlanmasnda kullanlan 2 temel biim vardr. Bu biimler birbirlerinden fonksiyon parametrelerinin derleyicilere tantlma ekli ile ayrlrlar. Bu biimlerden birincisi eski biim (old style) ikincisi ise yeni biim (new style) olarak adlandrlr. Artk eski biim hemen hemen hi kullanlmamaktadr, ama C standartlarna gre halen geerliliini korumaktadr. Kullanlmas tavsiye edilen kesinlikle yeni biimdir ancak eski kodlarn ya da eski kaynak kitaplarn incelenmesi durumunda bunlarn anlalabilmesi iin eski biimin de renilmesi gerekmektedir.

Eski Biim (old style) Bu biimde fonksiyonun parametre deikenlerinin yalnzca ismi fonksiyon parantezleri iinde yazlr. (Eer parametre deikenleri birden fazla ise aralarna virgl koyulur. Daha sonra alt satra geilerek bu deikenlerin bildirimi yaplr. Bu bildirimler daha nce rendiimiz, C dilinin bildirim kurallarna uygun olarak yaplr. rnek : double alan(x, y) int x, y; { return x * y; } Yukarda tanmlanan alan fonksiyonunun iki parametre deikeni vardr ve bu parametre deikenlerinin isimleri x ve y'dir. Her iki parametre deikeni de int trdendir. int sample (a, b, c) int a; double b; long c; { ... } Bu rnekte ise sample fonksiyonu parametre almaktadr. 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. Eski biimin dezavantaj gereksiz yere uzun oluudur. nk fonksiyon parantezelerinin iinde parametre deikenlerinin isimi yer almakta sonra tekrar bu isimler alt satrlarda yeniden kullanlarak bildirim yaplmaktadr.

52

FONKSYONLAR Yeni Biim (new style) Yeni biim eski biime gre hem daha ksadr hem de okunabilmesi eski biime gre ok daha kolaydr. Yeni biimde fonksiyon parametre deikenlerinin bildirimi fonksiyon parantezelerinin iinde yalnzca bir kez yaplmaktadr. Bu biimde fonksiyonun parantezlerinin iine parametre deikenin tr ve yanna da ismi yazlr. Eer birden fazla fonksiyon parametre deikeni varsa bunlar virgllerle ayrlr ancak her defasnda tr bilgisi yeniden yazlr . rnek : int sample (int x, int y) { ... } int fonk(double x, int y) { ... } Bu biimde dikkat edilmesi gereken nemli nokta, fonksiyon parametre deikenleri ayn trden olsalar bile her defasnda tr bilgisinin tekrar yazlmas zorunluluudur. rnein : int sample (double x, y) { ... } /* error */

bildirimi hataldr. Doru tanmlamann aadaki ekilde olmas gerekir: int sample (double x, double y) { ... }

Klavyeden Karakter Alan C Fonskiyonlar Sistemlerin hemen hemen hepsinde klavyeden karakter alan 3 ayr C fonksiyonu bulunur. Bu fonksiyonlarn biri tam olarak standarttr ama dier ikisi sistemlerin hemen hemen hepsinde bulunmasna karn tam olarak standart deildir.

getchar Fonksiyonu

int getchar(void);
getchar standart bir C fonksiyonudur. Geri dn deeri klavyeden alnan karakterin ASCII tablosundaki sra numarasn gsteren int trden bir saydr. getchar fonskiyonu klavyeden karakter almak iin enter tuuna ihtiya duyar. Aada yazlan programda nce klavyeden bir karakter alnm daha sonra alnan karakter ve karakterin saysal karlklar ekrana yazdrlmtr. (getchar fonksiyonunun geri dn deeri klavyede baslan tua ilikin karakterin sistemde kullanlan karakter seti tablosundaki sra numarasdr.)

53

6 #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 dosyasnda bir makro olarak tanmlanmtr. Makrolar konusunu daha ileride inceleyeceiz. getch Fonksiyonu

int getch(void);
getchar fonksiyonu gibi bu fonksiyonda klavyede baslan tuun kullanlan karakter setindeki numarasyla geri dner. getchar fonksiyonundan iki fark vardr. 1. Baslan tu ekranda grnmez. 2. Enter tuuna ihtiya duymaz. sra

Yukarda verilen programda getchar yerine getch yazarak program altrrsanz fark daha iyi grebilirsiniz. Tam olarak standardize edilmemitir ama neredeyse btn sistemlerde bulunur. getch fonksiyonu zellikle tu bekleme ya da onaylama amacyla kullanlmaktadr: .... printf("devam iin herhangi bir tua basnz...\n"); getch(); Burada baslacak tuun programc asndan bir nemi olmad iin fonksiyonun geri dn deeri kullanlmamtr.

getche Fonksiyonu

int getche(void);
getche ngilizce get char echo szcklerinden gelmektedir. getche fonksiyonu da baslan tuun karakter setindeki sra numarasyla geri dner ve enter tuuna gereksinim duymaz. Ama baslan tua ilikin karakter ekranda grnr.

getchar getch getche

enter tuuna ihtiya duyar enter tuuna ihtiya duymaz enter tuuna ihtiya duymaz

alnan karakter ekranda grnr. alnan karakter ekranda grnmez alnan karakter ekranda grnr.

Ekrana Bir Karakterin Grntsn Yazan C Fonksiyonlar C dilinde ekrana karakter yazamakta iki fonksiyonun kullanld grlr :

54

FONKSYONLAR

putchar Fonksiyonu

int putchar(int ch);


putchar standart bir C fonksiyonudur. Btn sistemlerde bulunmas zorunludur. Parametresi olan karakteri ekranda imlecin bulunduu yere yazar. rnein: #include <stdio.h> main() { char ch; ch = getchar(); putchar (ch); return 0; } Burada putchar fonksiyonunun yapt ii printf fonksiyonuna da yaptrabilirdik; printf("%c", ch); putchar(ch) ile tamamen ayn ileve sahiptir. putchar fonksiyonu ile '\n' karakterini yazdrdmzda printf fonksiyonunda olduu gibi imle sonraki satrn bana geer. putchar fonksiyonu ekrana yazlan karakterin ASCII karl ile geri dnmektedir. putchar fonksiyonu derleyicilerin ounda stdio.h dosyas iinde bir makro olarak tanmlanmtr. Makrolar konusunu ileriki derslerde detayl olarak reneceiz.

putch Fonksiyonu

int putch(int ch);


putch standart bir C fonksiyonu deildir. Dolaysyla sistemlerin hepsinde bulunmayabilir. Bu fonksiyonun putchar fonksiyonundan tek fark '\n' karakterinin yazdrlmas srasnda ortaya kar. putch, '\n" karakterine karlk yalnzca LF(line feed) (ASCII 10) karakterini yazmaktadr. Bu durum imlecin bulunduu kolonu deitirmeksizin aa satra gemesine yol aar. printf Fonksiyonu Deikenlerin ierisindeki deerler aslnda bellekte ikilik sistemde tutulmaktadr. Bir deikenin ierisindeki deerin ekrana, kalk sistemde ve nasl yazdrlaca programcnn isteine baldr. Deikenlerin ierisindeki deerlerin ekrana yazdrlmasnda printf fonksiyonu kullanlr. printf standart bir C fonksiyonudur. printf aslnda ok ayrntl zelliklere sahip bir fonksiyondur. Burada yalnzca temel zellikleri grsel bir biimde aklanacaktr. printf iki trnak ierisindeki karakterleri ekrana yazar. Ancak iki trnak iinde grd % karakterlerini ekrana yazmaz. printf fonksiyonu % karakterlerini yanndaki karakter

55

6 ile birlikte format karakteri olarak yorumlar. Format karakterleri iki trnaktan sonra yazlan parametrelerle birebir eletirilir. rnek: int x, y; x = 125; y = 200; printf("x = %d\ny = %d\n", x, y);

printf fonksiyonunun yukardaki ekilde arlmasyla x ve y deikeni iindeki deerler ekrana onluk sistemde yazdrlacaktr. Format karakterleri yerine elenen deikenlerin ierisindeki deerler ekrana yazlr. Format karakterleri saylarn ekrana nasl yazlacan belirtmekte kullanlr. format karakteri %d %ld %x %X %lx %u %o %f %lf %e %c %s %lf Anlam

int trn desimal sistemde yazar. long trn desimal sistemde yazar unsigned int trn hexadecimal sistemde yazar. unsigned int trn hexadecimal sistemde yazar.(semboller byk harfle) unsigned long trn hexadecimal sistemde yazar. unsigned int trn decimal sistemde yazar. unsigned int trn oktal sistemde yazar. float ve double trlerini desimal sistemde yazar. double trn desimal sistemde yazar. gerek saylar stel biimde yazar. char veya int trn karakter grnts olarak yazdrr. string olarak yazdrr. long double trn desimal sistemde yazdrr.

Yukardaki tabloda grld gibi double tr hem %f format karakteri hem de %lf format karakteri ile yazdrlabilmektedir. Ama %lf (okunabilirilii artrd iin) daha ok tercih edilmektedir. Yukardaki tabloya gre unsigned int trnden bir sayy aadaki ekillerde yazdrabiliriz : unsigned int u; printf("%u", u); printf("%o, u); printf("%x, u); /* u saysn 10'luk sistemde yazar */ /* u saysn 8'lik sistemde yazar */ /* u saysn 16'lk sistemde yazar */

short bir sayy yazarken d o u ya da x karakterlerinden nce h karakterini kullanyoruz : short int sh; printf("%hd", sh); /* 10'luk sistemde yazar */

56

FONKSYONLAR unsigned short int unsh; printf("%hu", unsh); printf("%ho", unsh); printf("%hx", unsh); /* 10'luk sistemde yazar */ /* 8'lik sistemde yazar */ /* 16'lk sistemde yazar */

long bir sayy yazarken d o u ya da x karakterlerinden nce l karakterini kullanyoruz : long int lo; printf("%ld", lo); unsigned long int unlo; printf("%lu", unlo); printf("%lo", unlo); printf("%lx", unlo); /* 10'luk sistemde yazar */ /* 8'lik sistemde yazar */ /* 16'lk sistemde yazar */ /* 10'luk sistemde yazar */

Yukardaki bilgilerde unsigned bir tamsayy printf fonksiyonuyla 8'lik ya da 16'lk sistemde yazdrabileceimizi grdk. Peki signed bir tamsayy 8'lik ya da 16'lk sistemde yazdramaz myz? Yazdrrsak ne olur? Szkonusu signed tamsay pozitif olduu srece bir sorun olmaz. Saynn iaret biri 0 olduu iin saynn nicel bykln etkilemez. Yani doru say ekrana yazar, 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 olmayacaktr. % karakterinin yannda nceden belirlenmi bir format karakteri yoksa , % karakterinin yanndaki karakter ekrana yazlr. %% (%) karakterini yaz anlamna gelir. scanf Fonksiyonu scanf fonksiyonu klavyeden her trl bilginin giriine olanak tanyan standart bir C fonksiyonudur. scanf fonksiyonu da printf fonksiyonu gibi aslnda ok detayl, geni kullanm zellikleri olan bir fonksiyondur. Ancak biz bu noktada scanf fonksiyonunu yzeysel bir ekilde tanyacaz. scanf fonksiyonunun da birinci parametresi bir stringdir. Ancak bu string klavyeden alnacak bilgilere ilikin format karakterlerini ierir. Bu format karakterleri nceden belirlenmitir ve % karakterinin yannda yer alrlar. scanf fonksiyonunun kulland format karakterlerinin printf fonksiyonunda kullanlanlar ile ayn olduunu syleyebiliriz. Yalnzca gerek saylara ilikin format karakterlerinde nemli bir farkllk vardr. printf fonksiyonu %f format ile hem float hem de double trden verileri ekrana yazabilirken scanf fonksiyonu %f format karakterini yalnzca float trden veriler iin kullanr. double tr iin scanf fonksiyonunun kulland format karakterleri %lf eklindedir. scanf fonksiyonunun format ksmnda format karakterlerinden baka bir ey olmamaldr. printf fonksiyonu ift trnak iindeki format karakterleri dndaki karakterleri ekrana yazyordu, ancak scanf fonksiyonu format karakterleri dnda string iine yazlan karakterleri ekrana basmaz, bu karakterler tamamen baka anlama gelecektir. Bu nedenle fonksiyonun nasl altn renmeden bu blgeye format karakterlerinden baka bir ey koymaynz. Buraya konulacak bir boluk bile farkl anlama gelmektedir. int x, y; scanf(%d%d, &x, &y);

57

6 Yukardaki rnekte x ve y saylar iin desimal sistemde klavyeden giri yaplmaktadr. Giri arasna istenildii kadar boluk karakteri konulabilir. Yani ilk sayy girdikten sonra ikinci sayy SPACE, TAB ya da ENTER tuuna bastktan sonra girebilirsiniz. rnein: 5 60

biiminde bir giri geerli olaca gibi; 5 60 biiminde bir giri de geerlidir. scanf fonksiyonuna gnderilecek dier argumanlar & operatr ile kullanlmaktadr. & bir gsterici operatrdr. Bu operatr gstericiler konusunda reneceiz. scanf fonksiyonunun yalnzca giri iin kullanlr, ekrana yazmak iin printf fonksiyonunu kullanmamz gerekir : int number; printf(bir sayi giriniz : ); scanf(%d, &number); C++ dilinde bir fonksiyon tanmlanmasnda fonksiyonun geri dn deerinin tr olarak void anahtar szc yazlmamsa, fonksiyon return anahtar szc kullanlarak mutlaka bir geri dn deeri retmelidir. Fonksiyonun geri dn deeri retmemesi durumunda derleme zamannda hata oluacaktr. Yani C dilinde olduu gibi rasgele bir deer retilmesi sz konusu deildir. Yine C++ dilinde geri dn deeri retecek bir fonksiyonun tanmlanmas iinde return anahtar szc yaln olarak kullanlamaz. return anahtar szcnn yannda mutlaka bir ifade yer almaldr.

58

NESNELERN FAALYET ALANLARI VE MRLER

7 . BLM :

NESNELERN FAALYET ALANLARI VE MRLER

Daha nce C dilinde nesnelerin 3 ayr zelliini grmtk. Bunlar nesnelerin isimleri, deerleri ve trleriydi. Nesnelerin C dili asndan ok nem tayan iki zellikleri daha sz konusudur. Bunlar tannabilirlik alanlar (scope) ve mrleridir (storage duration). Bu dersimizde bu iki kavram detayl olarak inceleyeceiz. Tannabilirlik Alan (scope / visibility) Tannabilirlik alan bir nesnenin mrn srdrd ve tannabildii program araldr. Burada program aral demekle kaynak kodu kastediyoruz. Dolaysyla tannabilirlik 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 deikeni tanmlyor olmamz o deikene kodun istediimiz bir yerinde ulaabilmemizi salamaz. Tannabilirlik alanlarn 2 ayr grupta toplayabiliriz : Blok tannabilirlik alan. (Block scope): Bir deikenin tanmlandktan sonra, derleyici tarafndan, yalnzca belirli bir blok iinde tannabilmesidir. Dosya tannabilirlik alan (File scope) : Bir deikenin tanmlandktan sonra tm kaynak dosya iinde, yani tanmlanan tm fonksiyonlarn hepsinin ierisinde tannabilmesidir. Deikenleri de tannabilirlik alanlarna gre ikiye ayrabiliriz : Yerel deikenler (local variables) Global deikenler (global variables) C dili iin ok nemli olan bu deiken tiplerini imdi detayl olarak inceleyeceiz : Yerel Deikenler (local variables) Bloklarn ilerinde tanmlanan deikenlere yerel deikenler denir. Hatrlanaca gibi C dilinde bloklarn ilerinde tanmlanan deikenlerin tanmlama ilemleri blok iinde ilk ilem olarak yaplmalyd. (C++ dilinde byle bir zorunluluk yoktur) Yerel deikenler blok ilerinde tanmlanan deikenlerdir, i ie bloklarn sz konusu olmas durumunda hangi blok ierisinde tanmlanrlarsa tanmlansnlar bunlar yerel deiken olarak adlandracaz. Yani yerel deiken olmalar iin en d blok iinde tanmlanmalar gerekmiyor. Yerel deikenlerin tannabilirlik alan blok tannabilirlik alandr. Yani yerel deikenlere yalnzca tanmlandklar blok iinde ulalabilir. Tanmlandklar bloun daha dnda bir blok iinde bu deikenlere ulaamayz. rnek : main () { float x; ... ... { int y; ... ... { int z;

x deikeninin tannabilirlik alan y deikeninin tannabilirlik alan

59

7 ... ...
z deikeninin tannabilirlik alan

} } }

Yukardaki rnekte tanmlanan deikenlerden hepsi yerel deikenlerdir. nk x y z deikenleri bloklarn ilerinde tanmlanmlardr. Bu deikenlere yalnzca tanmlanm olduklar blok iinde ulaabiliriz. Tanmlandklar blok dnda bunlara ulamaya almak derleme aamasnda error ile neticelenecektir. Dikkat etmemiz gereken bir nokta da udur : Yukardaki rnekte bu deikenlerin hepsi yerel deikenler olduklar iin blok tannabilirlik alan kuralna uyarlar, ancak bu tannabilirlik alanlarnn tamamen ayn olmasn gerektirmez. Yukardaki ekilden de grld gibi x deikeni en geni tannabilirlik alanna sahipken y deikeni daha kk ve z deikeni de en kk tannabilirlik alanna sahiptir. Yukardaki rnei geniletelim : main () { float x = 2.5; printf(x = %f\n, x); printf(y = %d\n, y); printf(z = %ld\n, z); { int y = 1; /* LEGAL BU ALANDA xE ULAILABLR */ /* ERROR BU ALANDA yYE ULAILAMAZ. */ /* ERROR BU ALANDA zYE ULAILAMAZ. */

printf(x = %f\n, x); printf(y = %d\n, y); printf(z = %ld\n, z); ... ... {

/* LEGAL BU ALANDA xE ULAILABLR */ /* LEGAL BU ALANDA yE ULAILABLR */ /* ERROR BU ALANDA zYE ULAILAMAZ. */

long z = 5; /* LEGAL BU ALANDA xE ULAILABLR */ /* LEGAL BU ALANDA xE ULAILABLR */ /* LEGAL BU ALANDA xE ULAILABLR */

} }

printf(x = %f\n, x); printf(y = %d\n, y); printf(z = %ld\n, z); }

C dilinde ayn isimli birden fazla deiken tanmlanabilmektedir. Genel kural udur: iki deikenin scoplar (tannabilirlik alanlar ayn ise) ayn isimi tayamazlar, ayn isim altnda tanmlanmalar derleme zamannda hata oluturur. ki deikenin scoplarnn ayn olup olmadklar nasl tespit edilecek? Standartlar bu durumu yle aklamaktadr : ki deikenin scoplar ayn kapanan kme parantezinde sonlanyorsa, bu deikenlerin scoplar ayn demektir. {
60

NESNELERN FAALYET ALANLARI VE MRLER float a; int b; double a; { int c; ... } } Yukardaki program parasnn derlenmesi derleme aamasnda error ile neticelenir. nk her iki a deikeninin de tannabilirlik alan (scoplar) ayndr. (scoplar ayn kme paranteziyle sonlanmaktadr.) Bu durum error oluturmasayd, yukardaki rnekte derleyici hangi a deikeninin yazdrlmak istendiini nasl anlayacakt. Zira bu durum error ile engellenmeseydi printf fonksiyonunun arld yerde her iki a deikeni de tannabilir olacakt. C dilinde farkl tannabilirlik alanlarna sahip birden fazla ayn isimli deiken tanmlanabilir. nk derleyiciler iin artk bu deikenlerin ayn isimli olmas nemli deildir. Bunlar bellekte farkl yerlerde tutulurlar. { int x = 100; printf(%d\n, x); { int x = 200; printf(%d\n, x); { int x = 300; printf(%d\n, x);

/* error */

} } }

Yukardaki program parasnda bir hata bulunmamaktadr. nk her x deikeninin de tannabilirlik alanlar birbirlerinden farkldr. Peki yukardaki rnekte ierideki bloklarda x ismini kullandmzda derleyici hangi x deikenini kast ettiimizi nasl anlayacak? Belirli bir kaynak kod noktasnda, ayn isimli birden fazla deikenin faaliyet alan (scope) iindeysek, deiken ismini kullandmzda derleyici hangi deikene ulaacaktr? Ayn isimli deikenlere ulama konusundaki kural udur : C dilinde daha dar faaliyet alanna sahip deiken dier ayn isimli deikenleri maskeler. Konunun daha iyi anlalmas iin bir ka rnek daha verelim : {

int a; char ch; long b; double a, f; /* hata ayn tannabilirlik alannda ve ayn blok seviyesinde ayn isimli iki deiken tanmlanm */

}
61

{ int var1; char var2; { int var1; char var2; } } Yukardaki kodda herhangi bir hata bulunmamaktadr. var1 ve var2 deikenlerinin ismi ikinci kez iteki blokta tanmlama ileminde kullanlmtr ama artk bu tannabilirlik alannda farkllk yaratan ayr bir bloktur, dolaysyla bir hata sz konusu deildir.

void sample1(void) { int k; ... } void sample2(void) { int k; ... } void sample3(void) { int k; ... } Yukardaki kodda da bir hata sz konusu deildir. Zira her fonksiyonunda da k isimli bir deiken tanmlanm olsa da bunlarn tannabilirlik alanlar tamamen birbirinden farkldr. Global Deikenler (global variables) C dilinde tm bloklarn dnda da deikenlerin tanmlanabileceini sylemitik. te btn bloklarn dnda tanmlanan deikenler global deikenler olarak isimlendirilirler. Btn bloklarn d kavramn daha iyi anlamak iin bir rnek verelim : #include <stdio.h> /* bu blge tm bloklarn d burada global bir deiken tanmlanabilir. */ int sample1() { ...
62

NESNELERN FAALYET ALANLARI VE MRLER } /* bu blge tm bloklarn d burada global bir deiken tanmlanabilir. */ int sample2() { ... } /* bu blge tm bloklarn d burada global bir deiken tanmlanabilir. */ int sample 3() { ... } /* bu blge tm bloklarn d burada global bir deiken tanmlanabilir. */ main() { ... } /* bu blge tm bloklarn d burada global bir deiken tanmlanabilir. */ Yorum satrlarnn bulunduu yerler global deikenlerin tanmlanabilecei yerleri gstermektedir. Bu blgeler hibir fonksiyon iinde deidir. Global deikenler dosya tannabilirlik alan kuralna uyarlar. Yani global deikenler programn her yerinde ve btn fonksiyonlarn iinde tannabilirler. Burada bir noktay gzden karmamak gerekir, bir deiken yerel de olsa global de olsa tanmlamas yaplmadan nce bu deikene ulalamaz. Derleme ileminin bir yn vardr ve bu yn kaynak kod ierisinde yukardan aaya dorudur. Bunu u ekilde de ifade edebiliriz : Global deikenler tanmlandklar noktadan sonra kaynak kod ierisinde her yerde tannabilirler. #include <stdio.h> int y; void sample(void) { y = 10; } void main() { y = 20; printf(y = %d\n, y); sample(); printf(y = %d\n, y); } Yukardaki rnekte y deikeni tm bloklarn dnda tanmland iin (ya da hibir fonksiyonun iinde tanmlanmad iin) global deikendir. y deikeninin tannabilirlik alan dosya tannabilirlik alandr. yani y deikeni tanmlandktan sonra tm fonksiyonlarn iinde tannabilir. Yukardaki programn almas main fonksiyonundan balayacaktr. y global deikenine nce 20 deeri atanmakta ve daha sonra bu deer printf fonksiyonuyla ekrana yazdrlmaktadr. Daha sonra sample fonksiyonu
63

/* y bir global deiken tanmlandktan sonra her yerde tannabilir */

/*baka bir fonksiyondan da y global deikenine ulalabilir */

/* y = 20 /* y = 10

*/ */

7 arlmtr. sample fonksiyonu arlnca kodun ak sample fonksiyonuna geer. sample fonksiyonu iinde de y global deikeni tannabilir. sample fonksiyonunda global y deikenine 10 deeri atanmakta ve daha sonra bu deer yine printf fonksiyonuyla ekrana yazdrlmaktadr. Peki bir global deikenle ayn isimli yerel bir deiken olabilir mi? Kesinlikle olabilir! ki deikenin tannabilirlik alanlar ayn olmad iin bu durum bir hataya neden olmaz. Ayn isimli hem global hem de yerel bir deikene ulalabilecek bir noktada, ulalan yerel deiken olacaktr, nk daha nce de sylediimiz gibi ayn tannabilirlik alannda birden fazla ayn isimli deiken olmas durumunda o alan iinde en dar tannabilirlik alanna sahip olanna eriilebilir. Aadaki kodu ok dikkatli inceleyelim : int g= 20; /* g global bir deiken */

void sample(void) { g = 100; printf(global g = %d\n, g); } int main() { int g; g = 200; printf(yerel g = %d\n, g); sample(); printf(yerel g = %d\n, g); return 0; }

/* global g deikenine atama yaplyor. */ /* global g yazdrlyor. */

/* g yerel deiken */ /* yerel olan g deikenine atama yaplyor */ /* yerel g yazdrlyor. */ /* yerel g yazdrlyor. */

Fonksiyonlarn kendileri de btn bloklarn balarnda tanmlandklarna gre global nesnelerdir. Gerekten de fonksiyonlar kaynak kodun heryerinden arlabilirler. Ayn tannabilirlik alanna ilikin ayn isimli birden fazla deiken olamayacaana gre , ayn isme sahip birden fazla fonksiyon da olamaz. (C++ dilinde ayn isimli fakat farkl parametrik yapya sahip fonksiyonlar tanmlamak mmkndr.) Programclarn ou global deikenleri mmkn olduu kadar az kullanmak ister. nk global deikenleri kullanan fonksiyonlar baka projelerde kullanlamazlar. Kullanldklar projelerde de ayn global deikenlerin tanmlanm olmas gerekecektir. Dolaysyla global deikenlere dayanlarak yazlan fonksiyonlarn yeniden kullanlabilirlii azalmaktadr. Parametre Deikenleri (formal parameters) Parametre deikenleri, fonksiyon parametreleri olarak kullanlan deikenlerdir. Parametre deikenleri de blok tannabilirlik alan kuralna uyarlar. Yani parametresi olduklar fonksiyonun her yerinde tannabilirler. Fonksiyon parametre deikeninin scope'u fonksiyonun ana blounun kapanmasyla sonlanacaktr. Yani fonksiyon parametre deikeninin tannabilirlik alan fonksiyonun ana bloudur. function(int a, double b) { / *a ve b bu fonksiyonun heryerinde tannr. */ }

64

NESNELERN FAALYET ALANLARI VE MRLER Baka bir rnek : sample (int a, int b) { int a; /* hata ayn tannrlk alan iinde ve ayn seviyede ayn isimli deiken */ ... { int b; /* hata deil tannabilirlik alanlar farkl */ ... } } Bu rnekte fonksiyonun ana blounun banda tanmlanm olan a, ayn tannabilirlik alannda ayn blok seviyesinde ayn isimli birden fazla deiken olamaz kuralna gre geersizdir. Biri parametre deikeni biri yerel deiken olmasna karn, her iki a deikeni ayn tannabilirlik alanna sahiptir.

Nesnelerin mrleri (storage duration / lifespan) mr, nesnelerin faaliyet gsterdii zaman araln anlatmak iin kullanlan bir kavramdr. Bir kaynak kod iinde tanmlanm nesnelerin hepsi program almaya baladnda ayn zamanda yaratlmazlar. mrleri bakmndan nesneleri iki gruba ayrabiliriz : 1. Statik mrl nesneler (static objects) 2. Dinamik mrl nesneler (dynamic / automatic objects) Statik mrl Nesneler (static duration static storage class) Statik mrl nesneler, programn almaya balamasyla yaratlrlar, programn almas bitene kadar varlklarn srdrrler, yani bellekte yer kaplarlar. Statik nesneler genellikle ama kod (.obj) ierisine yazlrlar. C dilinde statik mrl 3 nesne grubu vardr : global deikenler stringler(iki trnak ierisndeki ifadeler) statik yerel deikenler stringler ve statik yerel deikenleri daha sonra inceleyeceiz. statik mrl nesneler olan global deikenlerin mrleri konusunda unlar syleyebiliriz : Global deikenler programn almas sresince yaayan, yani programn almas sresince bellekte yer igal eden deikenlerdir. Dinamik mrl Nesneler Dinamik mrl nesneler programn almasnn belli bir zamannda yaratlan ve belli sre faaliyet gsterdikten sonra yok olan (mrlerini tamamlayan) nesnelerdir. Bu tr nesnelerin mrleri programn toplam alma sresinden ksadr. C dilinde dinamik mrl nense grubu vardr : yerel deikenler parametre deikenleri dinamik bellek fonksiyonlar ile tahsisat yaplarak yaratlm nesneler

65

dinamik bellek fonskiyonlar ile yaratlm nesneleri daha sonra inceleyeceiz. Yerel deikenler ve parametre deikenleri dinamik mrl nesnelerdir. Tanmlandklar bloun almas baladnda yaratlrlar, bloun almas bitince yok olurlar (mrleri sona erer.) Tannabilirlik alanlar kendi bloklarnn uzunluu kadar olan yerel deikenlerin mrleri, program aknn bu bloa gelmesiyle balar ve bloun alma sresi bitince de sona erer . rnek : { double x; ... /* programn ak bu noktaya geldiinde bellekte x iin bir yer iinde a yaamaya devam ediyor.*/ /* programn ak bloun sonuna geldiinde a deikeninin de mr sona eriyor */ ayrlyor. ve blok

Fonksiyonlarn parametre deikenleri de benzer biimde fonksiyon arldnda yaratlrlar, fonksiyon icras boyunca yaarlar, fonksiyonun icras bitince yok olurlar. Statik deikenlerle dinamik deikenler arasnda ilk deer verme(initialization) asndan da fark bulunmaktadr. Statik olan global deikenlere de yerel deikenlerde olduu gibi ilk deer verilebilir. lk deer verilmemi ya da bir atama yaplmam bir yerel deikenin iinde rasgele bir deer bulunur. Bu deer o an bellekte o deiken iin ayrlm yerde bulunan rasgele bir saydr.(garbage value). Oysa ilk deer verilmemi, ya da bir atama yaplmam global deikenler iinde her zaman 0 deeri vardr. Yani bu deikenler 0 deeriyle balatlrlar. Aadaki ksa program derleyerek altrnz : #include <stdio.h> int globx; int main() { int localy; printf("globx = %d\n", globx); printf("localy = %d\n", localy); return 0; } Yerel deikenler ile global deikenler arasndaki baka bir fark da, global deikenlere ancak sabit ifadeleriyle ilk deer verilebilmesidir. Global deikenlere ilk deer verme ileminde kullanlan ifadede (initializer), deikenler ya da fonksiyon arma ifadeleri kullanlamazlar, ifade yalnzca sabitlerden olumak zorundadr. (C++ dilinde byle bir zorunluluk bulunmamaktadr.) Ancak yerel deikenlere ilk deer verilme ileminde byle bir kstlama bulunmamaktadr. rnek : #include <stdio.h> int x = 5; /* legal */
66

NESNELERN FAALYET ALANLARI VE MRLER int y = x + 5; int z = funk(); int main() { int a = b; int k = b - 2; int l = funk() ... /* error! ilk deer verme ileminde sabit ifadesi kullanlmam */ /* error! ilk deer verme ileminde funksiyon arma ifadesi var */

/* legal, k yerel deiken olduu iin ilk deer verme ifadesi deiken ierebilir */ /* legal, k yerel deiken olduu iin ilk deer verme ifadesinde fonksiyon arma i fadesi bulunabilir. */

int funk() { ... } Argumanlarn Parametre Deikenlerine Kopyalanmas C dilinde bir fonksiyonun parametre deikenlerinin tanmlandklar fonksiyon iinde her yerde tannabildiklerini sylemitik. Peki bir fonksiyonun parametre deikenleri ne ie yararlar? Bir fonksiyonun parametre deikenleri, o fonksiyonun arlma ifadesiyle kendisine gnderilen argumanlar tutacak olan yerel deikenlerdir. rnek : fonk(int a) { ..... } main() { int x; .... fonk (x); } Yukardaki rnekte fonk fonksiyonu x arguman ile arldnda, programn ak fonk fonksiyonunun kodunun bulunduu yere srar, fonk fonksiyonundaki a isimli parametre deikeni iin bellekte bir yer ayrlr ve a parametre deikenine x deikeninin deeri atanr. Yani a = x; ileminin otomatik olarak yapldn syleyebiliriz. Baka bir rnek verelim : #include <stdio.h> main() { int x = 100, y = 200, z; z = add(x, y);
67

7 printf("%d\n", z); return 0;

int add(int a, int b) { return a + b; } add fonksiyonu arldnda programn ak bu fonksiyona gemeden nce, x ve y deikenlerinin iinde bulunan deerler add fonksiyonunun parametre deikenleri olan a ve b'ye kopyalanrlar.

68

8 . BLM :

OPERATRLER

Operatrler nesneler veya sabitler zerinde nceden tanmlanm birtakm ilemleri yapan atomlardr. Operatrler mikroilemcinin bir ilem yapmasna neden olurlar ve bu ilem sonunda da bir deer retilmesini salarlar. Programlama dillerinde tanmlanm olan her bir operatr en az bir makine komutuna karlk gelmektedir. Benzer ilemleri yapmalarna karlk programlama dillerinde operatr atomlar birbirlerinden farkllk gsterebilir. C programlama dilinde her ifade en az bir operatr ierir. (Sabitler, nesneler ve operatrlerin kombinezonlarna ifade denir.) c=a*b/2+3 ++x * y-a >= b /* 4 operatr vardr ifadedeki srasyla =, *, /, + */ /* 3 operar vardr, ifadedeki srasyla ++, *, -- */ /* 1 operatr vardr. >= */

Her programlama dilinde operatrlerin birbirlerine gre ncelii sz konusudur. (Eer ncelik kavram sz konusu olmasayd, operatrlerin neden olaca ilemlerin sonular makinadan makinaya, derleyiciden derleyiciye farkl olurdu.) C' de toplam 45 operatr vardr, ve bu operatrler 15 ayr ncelik seviyesinde yer alr. (baknz operatr ncelik tablosu) . Bir ncelik seviyesinde eer birden fazla operatr varsa bu operatrlerin soldan saa m sadan sola m ncelikle dikkate alnaca da tanmlanmaldr. Buna ncelik yn diyebiliriz. (associativity) Bir sembol, birden fazla operatr olarak kullanllabilmektedir. rnein * operatr hem arpma hem de ierik alma operatr (gstericilerle ilgili bir operatr) olarak kullanlr. Yine bir C dilinde &(ampersand) hem bitsel ve hem de gstericilere ilikin adres operatr olarak kullanlmaktadr. Operatr ncelik listesinde grdmz operatrlerin ounu bu ders tanyacaz ama bir ksm ileriki derslerimizde yer alacak. (rnein gsterici operatrleri)

Operatr Terminolojisi
Hem operatrler konusunun daha iyi anlalmas hem de ortak bir dil oluturmak amacyla operatrler hakknda kullanabileceimiz terimleri inceleyelim. Operand Operatrlerin deer zerinde ilem yaptklar nesne veya sabitlere denir. C'de operatrleri aldklar operand saysna gre 3 gruba ayrabiliriz. 1. Tek Operand Alan Operatrler (unary operators) rnein ++ ve -- operatrleri unary operatrlerdir. (C dilinde unary operatrler operatrncelik tablosunun 2. seviyesinde bulunurlar.)

2. ki Operand Alan Operatrler. (binary operators) Aritmetik ilem operatr olan + ve / operatrlerini rnek olarak verebiliriz. 3. Operand Alan Operatr (ternary operator) oul kullanmyoruz nk C'de 3 opererand olan tek bir operatr vardr. (koul operatr conditional operator)

69

Operatrleri operandnn ya operandlarnn neresinde bulunduklarna gre de gruplayabiliriz: 1. Sonek Konumundaki Operatrler (postfix operators) Bu tip operatrler operandlarnn arkasna getirilirler. rnein sonek ++ operatr (x++)

2. nek Konumundaki Operatrler (prefix operators) Bu tip operatrler operandlarnn nne getirilirler. rnein nek ++ operatr (++x) C Dilinin Operatr ncelik Tablosu Seviye 1 Operatr () [] . -> + ++ -~ ! * & sizeof (tr) * / % + << >> < > <= >= == != & ^ | && || ?: = += -= *= Tanm ncelik kazandrma ve fonksiyon arma index operatr (subscript) yap elemanna ulam (structure access) yap elemanna gsterici ile ulam iaret operatr (unary) iaret operatr (unary) 1 artrma (increment) 1 eksiltme (decrement) bitsel deil (bitwise not) mantksal deil (logical not) ierik operatr (indirection) adres operatr (address of) sizeof operatr tr dntrme (type cast operator) arpma (multiplication) blme (division) modulus (blmden kalan) toplama (addition) karma (subtraction) bitsel sola kaydrma (bitwise shift left) bitsel saga kaydrma (bitwise shift right) kktr (less than) byktr (greater than) kk eittir (less than or equal) byk eittir (greater than or equal) eittir (equal) eit deildir (not equal to) bitsel VE (bitwise AND) bitsel EXOR (bitwise EXOR) bitsel VEYA (bitwise OR) mantksal VE (logical AND) mantksal VEYA (logical OR) koul operatr (conditional operator) atama (assignement) ilemli atama (assignment addition) ilemli atama (assignment subtraction) ilemli atama (assignment multiplication) ncelik Yn (associativity) soldan saa

sadan sola

soldan saa

4 5 6

soldan saa soldan saa soldan saa

7 8 9 10 11 12 13 14

soldan saa soldan saa soldan saa soldan saa soldan saa soldan saa sadan sola sadan sola

70

8 /= %= <<= >>= &= |= ^= , ilemli atama (assignment ilemli atama (assignment ilemli atama (assignment ilemli atama (assignment ilemli atama (assignment ilemli atama (assignment ilemli atama (assignment virgl operatr (comma) division) modulus) shift left) shift right) bitwise AND) bitwise OR) bitwise EXOR)

15

3. Araek Konumundaki Operatrler (infix operators) Bu tip operatrler operandlarnn aralarna getirilirler. rnein aritmetiksel toplama operatr (x + y) Son olarak operatrleri yaptklar ilere gre snflayalm : 1. 2. 3. 4. 5. 6. Aritmetik operatrler (arithmetic operators) likisel operatrler (relational operators) Mantksal operatrler (logical operators) Gsterici operatrleri (pointer operators) Bitsel ilem yapan operatrler (bitwise operators) zel amal operatrler (special purpose operators)

lk 3 grup programlama dillerinin hepsinde vardr. Bitsel ilem yapan operatrler ve gsterici operatrleri yksek seviyeli programla dillerinde genellikle bulunmazlar. Programlama dillerinden ou, kendi uygulama alanlarnda kolaylk salayacak birtakm zel amal operatrlere de sahip olabilir. Operatrlerin Deer retmeleri Operatrlerin en nemli zellikleri bir deer retmeleridir. Operatrlerin ana ilevi bir deer retmeleridir. Programc, bir ifade iinde operatrlerin rettii deeri kullanr ya da kullanmaz. (discard edebiliriz.) Operatrlerin rettii deeri nasl kullanabiliriz: retilen deeri baka bir deikene aktararak kullanabiliriz. x = y + z; Yukardaki rnekte y + z ifadesinin deer, yani + operatrnn rettii deer x deikenine aktarlmaktadr. retilen deeri baka bir fonksiyona arguman olarak gnderebiliriz. func(y + z); Yukardaki rnekte func fonksiyonuna arguman olarak y + z ifadesinin deeri gnderilmektedir. (Yani + operatrnn rettii deer) retilen deeri return anahtar szc ile fonksiyonlarn geri dn deerlerinin belirlenmesinde kullanabiliriz. int sample(void) { ...
71

8 return (y + z) } yukarda sample fonksiyonunun geri dn deeri y + z ifadesinin deeridir. (Yani + operatrnn rettii deer) Operatrlerin elde ettii deerin hi kullanlmamas C sentaks asndan bir hataya neden olmaz. Ancak byle durumlarda derleyiciler bir uyar mesaj vererek programcy uyarrlar. rnein : int x = 20; int y = 10; ... x + y; Yukardaki ifadede + operatr bir deer retir. (+ operatrnn rettii deer operandlarnn toplam deeri, yani 30 deeridir.) Ancak bu deer hibir ekilde kullanlmamtr. Derleyici byle bir ilemin byk ihtimalle yanllkla yapldn dnecek ve bir uyar mesaj verecektir. Borland derleyicilerinde verilen uyar mesaj u ekildedir : warning : "code has no effect!" Daha nce belirttiimiz gibi C dilinde ifadelerin trleri ve deerleri sz konusudur. Bir ifadenin deerini derleyici u ekilde tespit eder : fade iindeki operatrler ncelik sralarna gre deer retirler, ve rettikleri deerler, ifade iindeki ncelii daha az olan operatrlere operand olarak aktarlr. Bu ilemin sonunda tek bir deer elde edilir ki bu da ifadenin deeridir. int x = 10; int y = 3; printf("%d\n", x % y / 2 + 7 && !x || y); Yukardaki kod parasnda printf fonksiyonu armyla x % y / 2 + 7 && !x || y ifadesinin deeri yazdrlmaktadr. Yazdrlacak deer nedir? ifade iindeki operatrler srayla deer retecek ve retilen deerler ncelik srasna gre, deer retecek operatrlere operand olacaktr. En son kalan deer ise ifadenin deeri, yani ekrana yazdrlan deer olacaktr. Operatrlerin Yan Etkileri (side effect of operators) C dilinde operatrlerin ana ilevleri, bir deer retmeleridir. Ancak baz operatrler operand olan nesnelerin deerlerini deitirirler. Yani bu nesnelerin bellekteki yerlerine yeni bir deer yazlmasna neden olurlar. Bir operatrn kendilerine operand olan nesnenin deerlerini deitirmesine operatrn yan etkisi diyeceiz. Yan etki, bellekte yaplan deer deiiklii olarak tanmlanmaktadr. rnein atama operatrnn, ++ ve -- operatrlerinin yan etkileri sz konusudur. Operatrler zerindeki Kstlamalar Programlama dilinin tasarmnda, baz operatrlerin kullanlmalaryla ilgili birtakm kstlamalar sz konusu olabilir. rnein ++ operatrnn kullanmnda, operandn nesne gsteren bir ifade olmas gibi bir kstlama sz konusudur. Eer operand olan ifade bir nesne gstermiyorsa, yani sol taraf deeri deilse, derleme zamannda hata oluacaktr. Kstlama operatrn operand ya da operandlarnn trleriyle de ilgili olabilir. rnein % operatrnn operandlarnn tamsay trlerinden birine ait olmas gerekmektedir. % operatrnn operandlar gerek

72

8 say trlerinden birine ait olamaz. Operandn gerek say trlerinden birinden olmas durumunda derleme zamannda hata oluacaktr. Operatrlerin Detayl Olarak ncelenmesi Aritmetik Operatrler Aritmetik operatrler drt ilemlerle ilgili ilem yapan operatrlerdir. + ve - Operatrleri. (toplama ve karma operatrleri) ki operand alan araek operatrlerdir. (binary infix) Dier btn programlama dillerinde olduklar gibi operandlarnn toplamn ve farkn almak iin kullanrlar. Yani rettikleri deer, operandlarnn toplam ya da fark deerleridir. Operandlar zerinde herhangi bir kstlama yoktur. ncelik tablosundan da grlecei gibi genel ncelik tablosunun 4. srasnda yer alrlar ve ncelik ynleri soldan saadr (left associative). Yan etkileri yoktur, yani operandlarnn bellekte sahip olduklar deerleri deitirmezler. Toplama ve karma operatrleri olan + ve operatrlerini tek operand alan + ve operatrleriyle kartrmamak gerekir. aret Operatr Olan ve + Operatrleri Bu operatrler unary prefix (tek operand alan nek konumundaki) operatrlerdir. - operatr operand olan ifadenin deerinin ters iaretlisini retir. Unary prefix operatrnn rettii bir nesne deil bir deerdir. Aadaki ifade matematiksel olarak doru olmasna karn C dili asndan hataldr, derleme zamannda hata (error) oluumuna neden olur: int x; -x = 5; x bir nesne olmasna kar x ifadesi bir nesne deil, x nesnesinin deerinin ters iaretlisi olan deerdir. Dolaysyla bir sol taraf deeri deildir ve bu ifadeye bir atama yaplamaz. + operatr yalnzca sentaks asndan C diline ilave edilmi bir operatrdr. Unary prefix bir operand olarak derleyici tarafndan ele alnr. Operand olan ifade zerinde herhangi bir etkisi olmaz. Unary prefix olan ve + operatrleri operatr ncelik tablosunun 2. seviyesinde bulunurlar ve ncelik ynleri sadan sola dorudur. Aadaki ifadeyi ele alalm : int x = 5; int y = -2; -x - -y; Yukardaki ifadede 3 operatr bulunmaktadr. Srasyla unary prefix operatr, binary infix karma operatr ve yine unary prefix - operatr. fadenin deerinin hesaplanmasnda operatr nceliklerine gre hareket edilecektir. unary prefix olan operatr 2. ncelik seviyesinde olduu iin binary infix olan karma operatrne gre daha yksek nceliklidir, ve kendi iinde ncelik yn sadan soladr. nce y ifadesi deer retir. retilen deer 2 olacaktr. daha sonra x ifadesi deer retir. retilen deer 5 olacaktr. retilen bu deerler karma operatrne operand olurlar ve ana ifadenin deeri 5 2 yani 7 deeri olarak hesaplanr. * ve / Operatrleri ki operand alan araek operatrlerdir. (binary infix) * operatr operandlar arpmak / ise operandlar blmek amacyla kullanlr. Yani retilen deer operandlarnn arpm ya da blm deerleridir. Operandlar herhangi bir trden olabilir. ncelik tablosunda grlebilecei gibi, genel ncelik
73

8 tablosunun 3. srasnda bulunurlar. ncelik ynleri soldan saadr. Her iki operatrn de bir yan etkisi yoktur. / operatrnn kullanmnda dikkatli olmak gerekir. Eer her iki operand da tamsay trlerindense operatrn bulunduu ifadenin tr de ayn tamsay trnden olacaktr. (Bu konuya ileride detayl deineceiz.) C programlama dilinde * sembol ayn zamanda bir gsterici operatr olarak da kullanlr. Ama ayn sembol kullanlmasna karn bu iki operatr hibir zaman birbirine karmaz nk aritmetik arpma operatr olarak kullanldnda binary infix durumundadr, ama gsterici operatr olarak kullanldnda unary prefix dir. % (modulus) Operatr ki operand alan araek bir operatrdr. (binary infix) Operatrlerin her ikisinin de tr de tamsay trlerinden (char, short, int, long) biri olmak zorundadr. (Deilse bu durum derleme zamannda error ile neticelenir.) rettii deer sol tarafndaki operandn sa tarafndaki operanda blmnden kalandr. Operatrn yan etkisi yoktur. rnein: c = 15 % 4; /* burada c'ye 3 deeri atanacaktr */

int c = 13 - 3 * 4 + 8 / 3 - 5 % 2; Burada c'ye 2 deeri atanacaktr. nk ilem u ekilde yaplmaktadr: c = 13 - (3 * 4) + (8 / 3) - (5 % 2) c = 13 - 12 + 2 - 1; c = 2; Artrma ++ ve Eksiltme -- Operatrleri (increment and decrement operators) Artrma (++) ve eksiltme (--) operatrleri C dilinde en ok kullanlan operatrlerdendir. Tek operand alrlar. nek ya da sonek durumunda bulunabilirler. ++ operatr operand olan deikenin ieriini 1 artrmak - operatr de operand olan deikenin ieriini 1 eksiltmek iin kullanlr. Dolaysyla yan etkileri sz konusudur. Operandlar olan nesnenin bellekteki deerini deitirirler. Bu iki operatr de dier aritmetik operatrlerden daha yksek ncelie sahiptir. Operatr ncelik tablosunun 2. seviyesinde bulunurlar ve ncelik ynleri sadan soladr. (right associative) Yaln olarak kullanldklarnda (baka hibir operatr olmakszn) nek ya da sonek durumlar arasnda hibir fark yoktur. ++ bir artr ve - bir eksilt anlamna gelir. ++c ve c++ ifadeleri tamamen birbirine denk olup c = c + 1 anlamna gelirler. --c ve c ifadeleri tamamen birbirine denk olup c = c 1 anlamna gelirler. Dier operatrlerle birlikte kullanldklarnda (rnein atama operatrleriyle) nek ve sonek biimleri arasnda farkllk vardr : nek durumunda kullanldnda operatrn rettii deer artrma/eksiltme yapldktan sonraki deerdir. Yani operandn artrlm ya da azaltlm deeridir. Sonek durumunda ise operatrn rettii deer artrma / eksiltme yaplmadan nceki deerdir. Yani operand olan nesnenin artrlmam ya da azaltlmam deeridir. Operandn deeri ifadenin tm deerlendirildikten sonra artrlacak ya da eksiltilecektir. x = 10;
74

8 y = ++x; Bu durumda: ++x 11 ve y = 11 deeri atanr.. x = 10; y = x++; y = 10; ++x 11 Aadaki program inceleyelim: int main() { int a, b; a = 10; 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 ifadesi ekrana 11 11 yazdrrken ikinci printf ifadesi 11 10 yazdrr. Bir rnek : x = 10; y = 5; z = x++ % 4 * --y; --y 4 10 % 4 2 2 * 4 8 z=8 x++ 11 Bir rnek daha: c = 20; d = 10; a = b = d + c--; lem sras: 10 + 20 30 b = 30
75

8 a = 30 c-- 19 fadenin sonunda deikenler: a = 30, b = 30, c = 19, d = 10 deerlerini alrlar. Fonksiyon arma ifadelerinde bir deiken postfix ++ ya da operatr ile kullanlmsa, ilk nce fonksiyon artrlmam veya eksiltilmemi deiken ile arlr, fonksiyonun almas bitince deiken 1 arttlr veya eksiltilir. #include <stdio.h> int func(int x) { printf(%d\n, x); } int main() { int a; a = 10; func(a++); printf(%d\n, a); return 0; } ++ ve -- Operatrleriyle lgili pheli Kodlar (undefined behaviours) ++ ve operatrlerinin bilinsiz baz kullanmlar derleyiciler arasnda yorum farkllna yol aarak tanabilirlii bozarlar. Byle kodlardan saknmak gerekir. 3 tane + (+++) ya da 3 tane (---) karakteri boluk olmakszn yan yana getirilmemelidir. x = a+++b; /* pheli kod */

Burada derleyiciler x = a++ + b;ilemlerini yapabilecei gibi x = a+ ++b; ilemlerini de yapabilirler. Bir ifadede bir deiken ++ ya da operatrleriyle kullanlmsa, o deiken o ifadede bir kez daha yer almamaldr. rnein aadaki ifadelerin hepsi pheli kodlardr : int x = 20, y; y = ++x + ++x; y = ++x + x a = ++a; /* pheli kod */ /* pheli kod */ /* pheli kod */

Bir fonksiyon arlrken parametrelerden birinde bir deiken ++ ya da ile kullanlmsa budeiken dier parametrelerde gzkmemelidir. Argumanlarm patrametre deikenlerine kopyalanma sras standart bir biimde belirlenmemitir. Bu kopyalama ilemi baz sistemlerde soldan saa baz sistemlerde ise sadan soladr. rnek : int a = 10;
76

fonk (a, a++);

/* pheli kod fonk fonksyinuna 10, 10 deerleri mi 11, 10 deerleri mi nderiliyor? */

void fonk(x, y) { ... } Karlatrma Operatrleri (ilikisel operatrler) C programlama dilinde toplam 6 tane karlatrma operatr vardr: < > <= >= == != kktr (less than) byktr (greater than) kk eit (less than or equal) byk eit (greater than or equal) eittir. (equal) eit deildir. (not equal)

Bu operatrlerin hepsi iki operand alan araek operatrlerdir. (binary infix) Operatr ncelik tablosuna bakldnda, karlatrma operatrlerinin aritmetik operatrlerden daha sonra geldii yani daha dk ncelikli olduklar grlr. Karlatrma operatrleri kendi aralarnda iki ncelik grubu biiminde bulunurlar. Karlatrma operatrleri bir nerme olutururlar ve sonu ya doru ya da yanl olur. Karlatrma operatrlerinden elde edilen deer , nerme doru ise 1 nerme yanl ise 0 deeridir ve int trdendir. Bu operatrlerin rettii deerler de tpk aritmetik operatrlerin rettii deerler gibi kullanlabilirler. rnekler : #include <stdio.h> int main() { int x = 5; ... y = (x > 5) + 1; printf("%d\n", y); funk(y >= x); return 0;

Yukardaki rnekte ekrana 1 deeri yazdrlacaktr. Daha sonra da funk fonksiyonu 0 deeriyle arlacaktr. Baz programlama dillerinde x = (a < b) + 1; gibi bir ilem hata ile sonulanr. nk rnein Pascal dilinde a < b ifadesinden elde edilen deer True ya da False dir. (yani BOOL ya da BOOLEAN trndendir.) Ama C doal bir dil olduu iin karlatrma operatrlerinin rettikleri deeri Boolean tr ile kstlamamtr. Cde mantksal veri tr yerine int tr kullanlr. Mantksal bir veri trnn tamsay tryle ayn olmas Cye esneklik ve doallk kazandrmtr. Baka bir rnek :
77

x = y == z; Yukardaki deyim C dili iin son derece doal ve okunabilir bir deyimdir. Bu deyimin icrasyla x deikenine ya 1 ya da 0 deeri atanacaktr. Karlatrma operatrnn kullanlmasnda baz durumlara dikkat etmemiz gerekmektedir. rnein int x = 12; 5>x>9 Yukardaki ifade matematiksel adan doru deildir. nk 12 deeri 5 ve 9 deerlerinin arasnda deildir. Ancak ifade C asndan doru olarak deerlendirilir. nk ayn seviyede olan iki operatre (> ve >) ilikin ncelik yn soldan saadr. nce soldaki > operatr deer retecek ve rettii deer olan 0 sadaki > operatrne operand olacaktr. Bu durumda 0 > 9 ifadesi elde edilecek ve bu ifadeden de 0 yani yanl deeri elde edilecektir.

Mantksal Operatrler (logical operators) Bu operatrler operandlar zerinde mantksal ilem yaparlar. Operandlarn Doru (true) ya da Yanl(false) olarak yorumladktan sonra ileme sokarlar. Cde dilinde ncelikleri farkl seviyede olan (tabloya baknz) mantksal operatr vardr: ! deil operatr (not) && Ve operatr (and) || veya operatr (or) Tablodan da grlecei gibi && ya da || operatrlerin ayn ifade iinde birden fazla kullanlmas durumunda ncelik yn soldan saadr, 2. seviyede bulunan ! operatr iin ise ncelik yn sadan soladr. Cde mantksal veri tr yoktur. Mantksal veri tr olmad iin bunun yerine int tr kullanlr ve mantksal doru olarak 1 mantksal yanl olarak da 0 deeri kullanlr. C dilinde herhangi bir ifade mantksal operatrlerin operand 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) int x = 5; x * x 25 fadesi mantksal bir operatrn operand olduu zaman yanl olarak yorumlanacaktr. nk saysal deeri 0a eittir. ! Operatr

78

8 Deil operatr tek operand alr ve her zaman nek durumundadr. (unary prefix) ! operatr operandnn mantksal deerini deitirir. Yani Doru deerini yanl deerini, yanl deerini de Doru deerine dntrr: x Doru (0 d deer) Yanl (0) rnekler : a = !25; b = 10 * 3 < 7 + !2 lem sras: !2 = 0 10 * 3 = 30 7+0=7 30 < 7 = 0 b = 0 (atama operatr en dk ncelikli operatrdr) y = 5; x = !++y < 5 != 8; lem sras: ++y 6 !6 0 /* ++ ve ! operatrleri ayn ncelik seviyesindedir ve ncelik yn sadan soladr. */ 0 < 5 1 1 != 8 1 x=1 && (ve / and) Operatr Bu operatr ilikisel operatrlerin hepsinden dk, || (veya / or) operatrnden yksek nceliklidir. Operandlarnn ikisi de Doru ise Doru (1), operandlardan bir tanersi yanl ise yanl (0) deerini retir. x yanl yanl Doru Doru y yanl Doru yanl Doru x&&y yanl yanl yanl Doru /* a deikenine 0 deeri atanr */ !x Yanl (0) Doru (1)

x = 3 < 5 && 7; 3 < 5 1 7 1 1 && 1 1 x=1

79

8 && operatrnn nce sol tarafndaki ilemler ncelik srasna gre tam olarak yaplr. Eer bu ilemlerde elde edilen saysal deer 0 ise && operatrnn 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 operatrn sa taraf hi icra edilmeyecek dolaysyla da sqrt fonksiyonu arlmayacaktr. Dolaysyla b deikenine 0 deeri atanacaktr. Uygulamalarda mantksal operatrler ounlukla ilikisel (karlatrma) operatrleriyle birlikte kullanlrlar : scanf(%d, &x); y = x >= 5 && x <= 25; Bu durumda y deikenine ya 1 ya da 0 deeri atanacaktr. Eer x 5den byk ya da eit ve 25den kk ya da eit ise y deikenine 1 deeri bunun dndaki durumlarda y deikenine 0 deeri atanacaktr. ch = c z = ch >= a && ch <= z Yukardaki rnekte ch deikeninin kk harf olup olmamas durumuna gre z deikenine 1 ya da 0 atanacaktr. || (veya / and) Operatr ncelii en dk olan mantksal operatrdr. ki operandndan biri Doru ise Doru deerini retir. ki operand da yanl ise yanl deerini retir. x yanl yanl Doru Doru y yanl Doru yanl Doru x||y yanl Doru Doru Doru /* a = 1 */ /* x = 1 */ /* sayi = 0 */

a = 3 || 5 x = 0 || -12 sayi = 0 || !5

|| operatrnn nce sol tarafndaki ilemler ncelik srasna gre tam olarak yaplr. Eer bu ilemlerden elde edilen saysal deer 1 ise sa tarafndaki ilemler yaplmaz. rnekler: a = 20; b = 10; y = a + b >= 20 || a b <= 10; a + b 30 30 >= 20 1

80

8 Artk sa taraftaki ilemlerin yaplmasna gerek kalmadan y deikenine 1 deeri atanr. Aadaki ifadede ch deikeninin iinde bulunan karakterin (Trke karakterler de dahil olmak zere) kk harf olup olmad bulunur: ch = ; a = ch >= a && ch <= z || ch == || ch == || ch == || ch == || ch == || ch == ; Son olarak unu da ilave edelim, mantksal operatrler bir deer retebilmek iin operandlarn nce 1 ya da 0 (yani DORU ya da YANLI) olarak yorumlarlar, ama yan etkileri yoktur. Yani operandlarnn nesne olmas durumunda bu neslerin bellekteki deerlerini 1 ya da 0 olarak deitirmezler.

zel Amal Operatrler


Bu blmde zel amal operatrlerden atama, ilemli atama, ncelik ve virgl operatrlerini inceleyeceiz. Atama Operatr (assignment operator) Atama operatr C dilinde ncelik tablosunun en alttan ikinci seviyesinde bulunur ve yalnzca virgl operatrnden daha yksek nceliklidir. Her operatr bir deer retir ve atama operatr de yapt atama ileminin yansra bir deer retir. Atama operatrnn rettii deer sa taraf deerinin kendisidir. rnein: if (a = 20) { ... } Burada srasyla unlar olur : 1. 20 says x deikenine atanr. 2. Bu ilemden 20 says retilir. 3. 20 sfr d bir say olduu iin if deyimi doru olarak deerlendirilir. Atama operatrnn rettii deer nesne deildir. dolaysyla; (b = c) = a; /* ifadesi Cde geerli bir deyim deildir. */

b = c atamasndan elde edilen deer c nesnesinin kendisi deil c nesnesinin saysal deeridir. Operatr tablosundan da greceimiz gibi atama operatr kendi arasnda sadan sola nceliklidir. (right associative) Bu yzden : var1 = var2 = var3 = 0; deyimi Cde geerlidir. Bu deyimde nce var3 deikenine 0 deeri atanacak ve retilen 0 deeri var2 deikenine atanacak ve yine retilen 0 deeri son olarak var1 deikenine atanacaktr. lemli Atama Operatrleri Bir ilemin operand ile ilem sonucunda retilen deerin atanaca nesne ayn ise ilemli atama operatrleri kullanlr. <nesne1> = <nesne1> ilem <operand2> ile
81

8 <nesne1> ilem= <operand2> ayn anlamdadr. lemli atama operatrleri atama operatryle sadan sola eit ncelike sahiptir. (operatr ncelik tablosuna baknz) lemli atama operatrleri hem okunabilirlik hem da daha ksa yazm iin tercih edilirler. Aadaki ifadeler edeerdir: deger1 += 5; sonuc *= yuzde; x %= 5 deger1 = deger1 + 5; sonuc = sonuc * yuzde; x = x % 5;

katsayi = katsayi * (a * b + c * d) ifadesi de yine katsayi *= a * b + c * d; eklinde yazlabilir. var1 = 3; var2 = 5; var1 += var2 *= 3; ifadesinde var2 = var2 * 3 ilemiyle nce var2 deikenine 15 deeri atanacak ve ilem sonucunda 15 deeri retilecek var1 = var1 + 15 ilemiyle var1 deikenine 18 deeri atanacaktr. zellikle += ve -= operatrlerinin yanl yazlmas, tespit edilmesi zor hatalara neden olabilir. x += 5; ifadesi x degikeninin eski deerini 5 artryorken bu deyim yanllkla aadaki gibi yazld takdirde x =+ 5; x deikenine 5 deeri atanacaktr. nku burada iki ayr operatr sz konusudur. (atama operatr olan = ve iaret operatr olan +) yukardaki rneklerden de grld gibi atama grubu operatrlerinin yan etkileri vardr. Yan etkileri operatrn sol operandnn bellekteki deerinin deitirilmesi (operatrn sa tarafndaki operand olan ifadenin deerinin sol tarafndaki nesneye aktarlmas) eklinde kendini gsterir. Virgl (,) Operatr ki ayr ifadeyi tek bir ifade olarak birletiren virgl operatr Cnin en dk ncelikli operatrdr. ifade1; ifade2; ile ifade1, ifade2; ayn ileve sahiptir. Virgl operatrnn nce sol tarafndaki ifade sonra sa tarafndaki ifade tam olarak yaplr. Bu operatrn rettii deer sa tarafndaki ifadenin rettii deerdir. Virgln sol tarafndaki ifadenin rettii deerin virgl operatrnn rettii deere bir etkisi yoktur. rnein : x = (y *= 5, z = 100);

82

8 ifadesinde x deikenine 100 deeri atanacaktr. Aadaki rnekte if deeri yanl olarak deerlendirilecektir : if (a =10, b = 0) { ... } Virgl operatrleri ile birden fazla ifade tek bir ifade olarak birletirilebilir. rnein : if (x == 20) { a1 = 20; a2 = 30; a3 = 40; } yerine if (x == 20) a1 = 20, a2 = 30, a3 = 40; yazlabilir. ncelik Operatr ( ) ncelik operatr bir ifadenin nceliini ykseltmek amacyla kullanlmaktadr. x = (y + z) * t; ncelik operatrn C nin en yksek ncelikli operatrler grubundadr. ncelik operatr de kendi arasnda soldan saa ncelik kuralna uyar. rnein: a = (x + 2) / ((y + 3) * (z + 2) 1); ifadesinde ilem sras yledir : i1 : x + 2 i2 : y + 3 i3 : z + 2 i4 : i2 * i3 i5 : i4 1 i6 : i1 / i5 i7 : a = i6

83

9 . BLM :

if DEYM

C dilinde program akn kontrol etmeye ynelik en nemli deyim if deyimidir. if deyiminin genel biimi aada verilmitir: if (ifade) deyim1; else deyim2; deyim1 ve deyim2 yaln deyim (simple statement) olabilecei gibi, bileik bir deyim (compound statement) ya da baka bir kontrol deyimi de (control statement) olabilir. if parantezi iindeki ifadeye kontrol ifadesi (control expression) denir. if deyiminin icras aadaki gibi yaplr: Derleyici nce kontrol ifadesinin saysal deerini hesaplar. Hesaplad saysal deeri mantksal doru ya da yanl olarak yorumlar. fadenin hesaplanan deeri 0 ise yanl, 0 dnda bir deer ise doru olarak yorumlanr. (rnein kontrol ifadesinin hesaplanan deerinin 5 olduunu dnelim, bu durumda kontrol ifadesi doru olarak deerlendirilecektir). Eer ifadenin sonucu doru ise else anahtar szcne kadar olan ksm, eer ifadenin sonucu yanl ise else anahtar szcnden sonraki ksm icra edilir. rnek : {

int var = 3; if (var * 5 < 50) deyim1; else deyim2; deyim3;

} 1. adm : if parantezi iindeki ifadenin saysal deeri hesaplanacak : 15 < 50 sonu 1 (doru) 2. adm : deyim1 icra edilecek. 3. adm : daha sonra else ksm atlanarak deyim3 icra edilecek. if parantezi ierisindeki ifadeler karmak yapda da olabilir : ... char ch = Q; if (ch >= a && ch <= z) printf(%c\n, ch); else printf(kk harf deil\n); ch = A;

84

if DEYM Yukardaki if deyimi ile ch karakterinin kk harf olup olmad test edilmektedir. ch karakterinin kk harf olmas durumunda bu karakter ekrana baslacak, aksi durumda ekrana kk harf deil mesaj yazlacaktr. Her iki durumdada programn ak ch = A; atama deyimiyle devam edecektir. if deyiminin doru ve / veya yanl ksm birden fazla deyimden oluuyorsa bloklama yaplmaldr : if (b * b 4 * a * c < 0) { deyim1; deyim2; deyim3; } else { deyim4; deyim5; deyim6; } Yukardaki rnekte kontrol ifadesinin doru olmas durumunda, deyim1, deyim2, deyim3 icra edilecektir. Kontrol ifadesinin yanl olmas durumunda ise deyim4, deyim5, deyim6 icra edilecektir. Bir if deyiminin else ksm olmayabilir: if (result < 0) { clrscr(); printf(sonu 0dan kk\n); } ++x; ... Bir if deyimi yalnzca else ksmna sahip olamaz. Bu durumda if deyiminin doru ksmna bo deyim ya bo bileik deyim yerletirilmelidir. if (ifade) ; else deyim1; ya da if (ifade) {} else deyim1; Ama daha iyi teknik koul ifadesini deitirmektir: if (!ifade) deyim1;
85

if parantezindeki ifade deiken iermek zorunda deildir, sabit ifadesi de olabilir: if (10) deyim1 ... if (-1) deyim2 Yukardaki kontrol ifadelerinin deeri her zaman doru olacaktr. (0 d deer) if ( 0) deyim1; Yukardaki kontrol ifadesi ise her zaman yanl olacandan if deyiminin doru ksm hibir zaman icra edilmeyecektir.

if (x) { deyim1; deyim2; .... } Bu if deyiminde ise x deikeninin deerinin 0 d bir deer olup olmamasna gre deyim1 ve deyim2 icra edilecektir. Yukardaki yapyla aadaki yap edeerdir: if (x != 0) { deyim1; deyim2; .... } Aadaki rnei inceleyelim : if (!x) { deyim1; deyim2; .... } Bu if deyiminde edilecektir. ise ancak x deikeninin deerinin 0 olmas durumunda deyim1 ve deyim2 icra

Yine yukardaki yapyla aadaki yap edeerdir: if (x == 0) { deyim1; deyim2; ....


86

if DEYM }

if ((ch = getchar()) == h) deyim1; else deyim2; Bu if deyiminin kontrol ifadesinde ise klavyeden bir deer alnmakta alnan deer ch deikenine atanmakta ve daha sonra da ch deikeni deerinin h karakteri olup olmad test edilmektedir. Eer klavyeden alnan deer 'h' ise deyim1 deil ise deyim2 yaplacaktr. kontrol ifadesi iindeki parantezler atama ilemine ncelik kazandrmak amacyla kullanlmaktadr. Parantez kullanlmasayd eitlik karlatrma operatrnn (==) ncelii atama operatrnden daha yksek olduu iin nce karlatrma ilemi yaplacak daha sonra retilen 0 ya da 1 deeri ch deikenine atanacakt. Yukardaki rnekte de grld gibi bir if deyiminin koul ifadesi iinde atama operatrnn bir deer retmesi fikrinden sklkla faydalanlr. Fonksiyonlarn geri dn deerleri de sklkla if deyiminin kontrol ifadesi olarak kullanlr. if (isupper(ch) != 0) deyim1; else deyim2; isupper parametresinin byk harf olup olmadn kontrol eden bir standart C fonksiyonudur. Yukardaki if deyiminde ch karakterinin byk harf olup olmamasna gre deyim1 ya da deyim2 icra edilecektir. Yukardaki koul ifadesi yerine C programclar genellikle aadaki ifadeyi tercih ederler : if (isupper(ch)) deyim1; else deyim2;

if deyiminin doru ya da yanl ksm baka bir if deyimi de olabilir : if (ifade1) if (ifade2) { deyim1; deyim2; deyim3; } deyim4; Bu rnekte ikinci if deyimi birinci if deyiminin doru ksmn oluturmaktadr. Birinci ve ikinci if deyimlerinin yanl ksmlar yoktur.

87

9 ie if deyimlerinde son if anahtar szcnden sonra gelen else anahtar szc en iteki if deyimine ait olacaktr: if (ifade1) if (ifade2) deyim1; else deyim2;

Yukardaki rnekte yazm tarz olarak else ksmnn birinci if deyimine ait olmas gerektii gibi bir grnt verilmi olsa da else ksm ikinci if deyimine aittir. else anahtar szc bu gibi durumlarda kendisine yakn olan if deyimine ait olacaktr. (dangling else) Eer else anahtar szcnn birinci if deyimine ait olmas isteniyorsa, birinci if deyiminin doru ksm blok iine alnmaldr.

if (ifade1) { if (ifade2) deyim1; } else deyim2; Yukardaki rnekte else ksm birinci if deyimine aittir.

if (ifade1) { if (ifade2) deyim1; else { deyim2; deyim3; } deyim4; } else deyim5; Birinci if deyiminin doru ksm birden fazla deyimden olutuu iin (bu deyimlerden birisi de yine baka bir if deyimidir) bloklama yaplmtr. deyim5 birinci if deyiminin yanl ksmn oluturmaktadr. Ayrk karlatrma ve else if merdivenleri : Aadaki if deyimlerini inceleyelim :

if (m == 1) printf(Ocak\n); if (m == 2) printf(ubat\n); if (m == 3) printf(Mart\n);


88

if DEYM ..... if (m == 12) printf(Aralk\n); Yukardaki rnekte verilen yapda olduu gibi, eer bir karlatrmann doru olarak yorumlanmas durumunda yaplan dier karlatrmalarn doru olmas sz konusu deilse bu tr karlatrmalara ayrk karlatrma denir. Ayrk karlatrmalarda ayr ayr if deyimlerinin kullanlmas kt tekniktir. Yukardaki rnekte m deikeninin deerinin 1 olduunu dnelim. Bu durumda ekrana Ocak yazdrlacak fakat daha sonra yer alan if deyimleriyle m deikeninin srasyla 2, 3, .... 12ye eit olup olmad test edilecektir. Ama x deikeni 1 deerine sahip olduundan btn dier if deyimleri iindeki kontrol ifadelerinin yanl olarak deerlendirilecei bellidir. Ayrk karlatrmalarda else if merdivenleri uygulamalar ok kullanlr : if (ifade1) deyim1; else if (ifade2) deyim2; else if (ifade3) deyim3; else if (ifade4) deyim4; else deyim5; Bu yapda artk herhangi bir if deyimi ierisindeki bir kontrol ifadesi doru olarak deerlendirilirse programn ak hibir zaman baka bir if deyimine gelmeyecektir. Bu yapya else if merdiveni (cascaded if / else if ladder) denir. else if merdivenlerinin yukardaki biimde yazl zellikle uzun else if merdivenlerinde okunabilirlii bozduu iin aadaki yazm tarz okunabilirlik asndan tercih edilmelidir.

if (ifade1) deyim1; else if (ifade2) deyim2; else if (ifade3) deyim3; else if (ifade4) deyim4; else deyim5;

if Deyiminin Kullanlmasna likin Sk Yaplan Hatalar if parantezinin sonuna yanllkla ; yerletirilmesi: ... if (x > 5); printf("doru!\n");
89

9 ... Yukardaki rnekte x > 5 ifadesi doru da yanl da olsa printf fonksiyonu arlacaktr. Zira printf arm if deyiminin dndadr. if deyiminin doru ksmn bir bo deyim (Null statement) oluturmaktadr. Ayn nedenden dolay aadaki kod derleme zamannda error oluumuna neden olacaktr : ... if (x > 5); printf("doru!\n"); else printf("yanl\n"); ... if anahtar szc olmayan bir else anahtar szc: Tabi ki bir if deyiminin doru ya da yanl ksmn bir ; (bo deyim - null statement) oluturabilir. Bu durumda okunabilirlik asndan bu bo deyim bir tab ieriden yazlmaldr: ... if (funk()) ; else x = 5; if parantezi ierisinde karlatrma operatr (==) yerine yanllkla atama operatrnn (=) kullanlmas ... if (x == 5) printf("eit\n"); ... Yukardaki if deyiminde x deikeninin deeri eer 5'e eitse printf fonksiyonu arlacaktr. Operatrler konusunda da deindiimiz gibi karlatrma operatrnn yan etkisi yoktur, yani yukardaki if parantezi ierisinde x deikeninin deeri yalnzca 5 sabiti ile karlatrlmakta, deikenin deeri deitirilmemektedir. Oysa karlatrma operatrnn yerine yanllkla atama operatr kullanlrsa: ... if (x = 5) printf("eit\n"); ... Atama operatr atama operatrnn sa tarafndaki ifadenin deerini reteceinden if parantezi iindeki ifadenin deeri 5 olarak hesaplanacak ve 5 de 0 d bir deer olduundan printf fonksiyonu x deikeninin deeri ne olursa olsun, arlacaktr. Tabi, atama operatr yan etkisi olan bir operatr olduundan x deikeni de if deyiminin icras srasnda 5 deerini alacaktr. C derleyicilerinin ou if parantezi iindeki ifade yaln bir atma ifadesi ise, durumu pheyle karlayarak, bir uyar mesaj verirler. rnein Borland derleyicilerinde tipik bir uyar mesaj aadaki gibidir :

90

if DEYM warning : possibly incorrect assignment! (muhtemelen yanl atama!) Oysa if parantezi ierisinde atama operatr bilinli olarak da kullanlabilir. Bilinli kullanmda, uyar mesajnn kaldrlmas iin ifade aadaki gibi dzenlenebilir : ... if ((x = funk()) != 0) m = 20; ... Yukardaki rnekte atama operatrnn rettii deer ak olarak bir karlatrma operatrne operand yapld iin derleyiciler bu durumda bir uyar mesaj vermezler.

if deyiminin doru ya da yanl ksmnn birden fazla basit deyimden olumas durumunda if deyiminin doru ya da yanl ksm bileik deyim haline getirilmelidir. ... if ( x == 10) m = 12; k = 15; ... Yukardaki if deyiminde sadece m = 12; deyimi if deyiminin doru ksmn oluturmaktadr. k = 15; deyimi if deyimi dndadr. Bu durum, genellikle programcnn if deyiminin doru ya da yanl ksmn nce basit bir deyimle oluturmasndan sonra, doru ya da yanl ksma ikinci bir basit deyimi eklerken, bloklama yapmay 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 derleme zamannda error oluumuna neden olacaktr : ... if ( x == 10) m = 12; k = 15;
91

9 else ... y = 20;

Bu tr yanllklardan saknmak iin baz C programclar if deyiminin doru ya da yanl ksm basit 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 basit deyimin ilave edilmesi durumunda bir yanllk ya da error olumayacaktr. Ancak biz yukardaki gibi bir kodu stil asndan beenmiyoruz. Gereksiz bloklamadan kanmalyz. Bazen de if deyiminin yanl ksm unutulur : ... if (x == 10) printf("x 10'a eit!\n"") printf("x 10'a eit deil!\n); ... if parantezi ierisindeki ifadenin yanl olmas durumunda bir yanllk sz konusu deil ama ifade doru ise ekrana ne yazlacak? x 10'a eit! x 10'a eit deil! Tehlikeli bir bug da if parantezi ierisindeki ifadenin bir fonksiyon arma ifadesi olmas durumunda yanllkla fonksiyon arma operatrnn unutulmasdr! ... if (funk()) m = 12; ... yerine ... if (funk) m = 12; ... yazlrsa, if deyiminin her zaman doru ksm icra edilir. Zira C dilinde bir fonksiyon ismi, o fonksiyonun kodunun bellekteki yerine edeer bir adres bilgisi olarak ele alnr. (Gstericiler konusunda greceiz.) Bu adres bilgisi de her zaman 0 d bir deer olacandan, koul ifadesi her zaman doru olarak deerlendirilecektir.
92

if DEYM if Deyiminin Kullanld rnek Uygulamalar islower Fonksiyonu islower standart bir C fonksiyonudur. Parametresi olan karakter, kk harf karakterlerinden biri ise Doru (sfr d bir deer), deilse Yanl (sfr) deerine geri dner. Bu fonksiyonu aadaki ekilde yazabiliriz : #include <stdio.h> int _islower (int ch) { if (ch >= a && ch <= z) return ch: return 0; } main() { char ch; ch = getchar(); if (_islower(ch)) printf(kk harf\n); else printf(kk harf deil\n);

Yukarda yazlan _islower fonksiyonunda nce parametre deikeninin kk harf olup olmad test edilmektedir. Parametre deikeni eer kk harf ise if deyiminin Doru ksm yaplacaktr. Doru ksmnn yaplmas ile fonksiyon chnn kendi deerine geri dnecektir. (Bu da 0 d bir deerdir. Pekala 1 gibi sabit bir deer ile de geri dnebilirdik ama ch parametre deikeninin deeri ile geri dnmemiz fonksiyonun kullanlmasnda baz avantajlar getirebilecektir. Fonksiyonun yazmnda dikkat edilmesi gereken bir nokta da udur : if deyiminin Yanl ksm yoktur. (Yani else anahtar szc kullanlmamtr. Test fonksiyonlarnda bu durum ok sk grlr. nk if deyiminin Doru ksmnda return anahtar szc ile fonksiyon yalnzca bir geri dn deeri retmekle kalmamakta ayn zamanda sonlanmaktadr. Bu durumda else anahtar szcne gerek kalmaz. (nk Doru ksmnn yaplmasndan sonra Yanl ksmnn da yaplmas mmkn deildir.) Bu tr durumlarda else anahtar szcnn kullanlmas kt teknik olarak deerlendirilir. isalpha Fonksiyonu isalpha fonksiyonu da standart bir C fonksiyonudur. Parametresi olan karakter , eer alfabetik karakterse (yani byk ya da kk harf ise) Doru (sfr d bir deere), alfabetik bir karakter deilse Yanl (sfr deerine) geri dner. #include <stdio.h> int isalpha (char ch) { if (ch >= a && ch <= z || ch >= A && ch <= Z) return ch: return 0;

93

9 } main() { char ch; ch = getchar(); if (isalpha(ch)) printf(alfabetik karakter\n); else printf(alfabetik karakter deil\n); tolower Fonksiyonu tolower standart bir C fonksiyonudur. Parametresi olan karakter, eer byk harf ise, onun kk harf karlyla geri dner. tolower kk harf olmayan karakterlere hi dokunmaz, onlar deitirmeden geri dn deeri olarak verir : #include <stdio.h> int tolower (int ch) { if (ch >= A && ch <= Z) return ch A' + a; return ch; } main() { char ch; ch = getchar(); printf(%c\n, tolower(ch)); } isdigit Fonksiyonu isdigit standart bir C fonksiyonudur. Parametresi olan karakter, eer bir rakam karakteri ise 0 d bir deer ile, rakam karakteri deilse 0 deeri ile geri dner. int _digit (char ch) { if (ch >= 0 && ch <= 9) return ch: return 0; } yukardaki rneklerde grld gibi, fonksiyonlarn parametreleri ve geri dn deerleri char trden olsa bile, int biiminde gsterilir. Karakter Test Fonksiyonlar Karakter test fonksiyonlar karakterler hakknda bilgi edinmemizi salayan fonksiyonlardr. Bu fonksiyonlarn hepsi ctype.h balk dosyas iinde makro olarak bulunurlar. Bu nedenle karakter test

94

if DEYM fonksiyonlar kullanlmadan nce kaynak koda mutlaka ctype.h dosyas dahil edilmelidir. Karakter test fonksiyonlar ASCII karakter setinin ilk yars iin geerlidir, yani trke karakterler iin kullanlmas durumunda geri dn deerleri gvenilir deildir. C dilinindeki standart karakter test fonksiyonlar: fonksiyon isalpha isupper islower isdigit isxdigit isalnum isspace ispunct 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 hex saylar gsteren bir karakterse Doru deilse Yanl alfabetik ya da nmerik bir karakterse Doru deilse Yanl Boluk karakterlerinden biriyse(space, carriage return, new line, vertical tab, form feed) Doru deilse Yanl Noktalama karakterlerinden biriyse (kontrol karakterleri, alfanmerik karakterler ve boluk karakterlerinin dndaki karakterler) Doru deilse Yanl Ekranda grlebilen (print edilebilen) bir karakterse (space karakteri dahil) Doru, deilse Yanl. Ekranda grlebilen bir karakterse (space dahil deil) Doru deilse yanl Kontrol karakteri ya da silme karakteri ise (lk 32 karakter ya da 127 numaral karakter) Doru deilse Yanl ASCII tablosunun standart ksm olan ilk 128 karakterden biriyse Doru deilse Yanl

isprint isgraph iscntrl isascii

Uygulamalar Kendisine gnderilen 0 ie 15 arasndaki bir saynn hexadesimal sembol karl karakter ile geri dnen get_hex_char fonksiyonunun yazlmas : 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; } test kodu : main() { int number; printf("0 ile 15 arasnda bir say giriniz : "); scanf("%d", &number); printf("hexadesimal digit karl = %c\n", get_hex_char(number)); return 0; }

95

Kendisine gnderilen hex digit olan bir karakterin desimal sistemdeki deerine geri dnen get_hex_value(char digit) fonksiyonunun yazlmas. #include <stdio.h> #include <ctype.h> int get_hex_value(char digit) { digit = toupper(digit); if (digit >= '0' && digit <= '9') return digit - '0'; if (digit >= 'A' && digit <= 'F') return digit - 'A' + 10; } test kodu : main() { char hex; printf("hex digit gsteren bir karakter giriniz: "); hex = getchar(); printf("girmi oldugunuz hex basaman desimal deeri %d\n", get_hex_value(hex));

return 0;

Kendisine gderilen karakter kk harf ise byk harfe dntren, byk harf ise kk harfe dntren, eer harf karakteri deilse karakterin kendisiyle geri dnen change_case isimli bir fonksiyonun tasarlanmas : #include <stdio.h> #include <conio.h>

int change_case(int ch) { if (ch >= 'A' && ch <= 'Z') return ch - 'A' + 'a'; if (ch >= 'a' && ch <= 'z') return ch - 'a' + 'A'; return ch; } test kodu : main() { int kar;

96

if DEYM printf("bir karakter giriniz : "); kar = getchar(); kar = change_case(kar); putchar(kar); getch(); return 0;

change_case fonksiyonunu standart C fonksiyonlarn kullanarak aadaki ekilde de tanmlayabilirdik : int change_case(int ch) { if (isupper(ch)) return tolower(ch); return toupper(ch); } kinci dereceden bir denklemin zm kmesini bulan bir program : #include <stdio.h> #include <math.h>

main() { double a, b, c; double delta, kokdelta; printf("denklemin katsaylarn giriniz : "); scanf("%lf%lf%lf", &a, &b, &c); delta = b * b - 4 * a * c; if (delta < 0) { printf("denkleminizin gerek kk yok!\n"); return 0; } if (delta == 0) { printf("denkleminizin tek gerek kk var\n"); printf("kok = %lf\n", -b / (2 * a)); return 0; } kokdelta = sqrt(delta); printf("denkleminizin 2 gerek kk var : "); printf("kk 1 = %lf\n", (-b + kokdelta) / (2 * a)); printf("kk 1 = %lf\n", (-b - kokdelta) / (2 * a)); return 0; }

97

10

10 . BLM :

FONKSYON PROTOTPLER

C programlama dilinde, bir fonksiyonun arlmas durumunda derleyiciler fonksiyonun geri dn deerinin trn bilmek zorundadr. C derleyicileri fonksiyonlarn geri dn deerlerini CPU yazmalarndan (registers) alrlar ve aslnda geri dn deeri tr, deerin hangi yazmatan alnacan gsterir. Eer arlan fonksiyonun tanmlamas, fonksiyon arma ifadesinden daha nce yer alyorsa, derleyici derleme ilemi srasnda fonksiyon arma ifadesine gelmeden nce, arlan fonksiyonun geri dn deeri tr hakknda zaten bilgi sahibi olacaktr. nk derleme ilemi yukardan aa doru yaplr. # include <stdio.h> float calculate(float x, float y) { return x * y / (x + y); } int main() { float a, b, c; c = calculate(a, b); printf(%f\n, c ); return 0; } Yukardaki rnekte calculate fonksiyonu kendisini aran main fonksiyonundan daha nce tanmlanmtr. Dolaysyla arma ifadesine gelmeden nce derleyici, calculate fonksiyonunun geri dn deeri trn zaten bilecektir. Eer arlan fonksiyonun tanmlamas aran fonksiyondan daha sonra yaplmsa, derleyici fonskiyon arma ifadesine geldiinde, sz konusu fonksiyonun geri dn deerinin trn belirleyemez. Bu problemli bir durumdur. # include <stdio.h> int main() { float a, b, c; c = calculate(a, b); printf(%f\n, c ); return 0; }

float calculate (float x, float y) { return x * y / (x + y); }

98

FONKSYON PROTOTPLER Yukarda calculate fonksiyonu main iinde arlmtr. Fakat calculate fonksiyonunun tanmlamas kaynak kod iinde mainden daha sonra yer almaktadr. Derleme ak ierisinde calculate fonksiyonuna ilikin arma ifadesine gelindiinde, derleyici bu fonksiyonun geri dn deerini bilmemektedir. C derleyicileri derleme ilemi srasnda bir fonksiyon arma ifadesi grdklerinde, eer fonksiyonun geri dn deeri tr hakknda henz sahibi deillerse, sz konusu geri dn deerinin int trden olduunu kabul ederler. Yukardaki rnekte derleyici calculate fonksiyonunun geri dn deerinin int trden olduunu varsayacak ve buna gre kod retecektir. Daha sonra derleme ak fonksiyonun tanmlama ksmna geldiinde ise artk i iten gemi olacaktr. Hedef kod oluumunu engelleyen bu durumu derleyiciler bir hata mesaj ile bildirirler. Bu hata mesaj Microsoft derleyicilerinde : 'calculate': redefinition Borland derleyicilerinde ise : Type mismatch in redeclaration of 'calculate' arlan fonksiyonu aran fonksiyonun stnde tanmlamak her zaman mmkn deildir. Byk bir programda yzlerce fonksiyon tanmlanabilir ve tanmlanan her fonksiyonun birbirini armas sz konusu olabilir. Bu durumda arlacak fonksiyonu aran fonksiyonun zerinde tanmlanmas ok zor olacaktr. Kald ki, C dilinde iki fonksiyon birbirini de arabilir. Bu tr bir fonksiyon tasarmnda artk arlan fonksiyonun daha nce tanmlanmas mmkn olamayacaktr : double func1(void) { ... func2(); ... } double func2(void) { ... func1(); ... } Ayrca standart C fonksiyonlar da ancak balama aamasna gelindiinde balayc (linker) tarafndan ktphanelerden alnarak alabilen kod (.exe) iine yerletirilirler. te bu gibi durumlarda derleyiciye arlan fonksiyonun geri dn tr bilgisi fonksiyon prototipleriyle verilir. arlana kadar tanmlamas yaplmam fonksiyonlar hakknda derleyicilerin bilgilendirilmesi ilemi fonksiyon prototip bildirimleri ile yaplr. Fonksiyon Prototip Bildirimlerinin Genel Biimi [geri dn deeri tr] <fonksiyon ismi> ([tr1], [tr2].....); rnein calculate fonksiyonu iin prototip aadaki biimde yazlabilir: float calculate(float, float); Derleyici byle bir prototip bildiriminden calculate fonksiyonunun geri dn deerinin trnn float olduunu anlayacaktr.

99

10 Birka rnek daha verelim: int multiply (int, int); double pow (double, double); void clrscr(void); Tpk fonksiyon tanmlamalarnda olduu gibi, fonksiyon prototip bildirimlerinde de, fonksiyonun geri dn deeri belirtilmemise, derleyici bildirimin int trden bir geri dn deeri iin yapldn anlayacaktr. func(double); Yukardaki prototip bildirimi rneinde, derleyici func fonksiyonunun geri dn deerinin int trden olduu bilgisini alr, fonksiyonun geri dn deerinin olmad bilgisini deil. Eer tanmlanacak fonksiyon geri dn deeri retmeyecekse, void anahtar szc kullanlmaldr: void func(double); Fonksiyon protipleri yalnzca derleyiciyi bildirme amacyla kullanlr bir bildirimdir (declaration) bir tanmlama (definition) ilemi deildir, dolaysyla yaplan bildirim sonucunda bellekte bir yer ayrlmaz. Fonksiyon prototip bildirimlerinde parametre deikenlerinin trlerinden sonra parametre isimleri de yazlabilir. Prototiplerdeki parametre isimlerinin faaliyet alanlar yalnzca parametre parantezi ile snrldr. Buraya yazlan parametre deikenleri isimleri yalnzca okunabilirlik asndan faydaldr. Buradaki deiken isimlerinin fonksiyonun gerek tanmlamasnda kullanlacak formal parametre deikenlerinin isimleriyle ayn olmas zorunlulugu yoktur. Yukardaki prototiplerini parametre isimleriyle tekrar yazalm. float calculate(float a, float b); int multiply(int number1, int number2); double pow(double base, double exp); Fonksiyon prototip bildirimlerinde parametre deikenlerine isim verilmesi, bildirimleri okuyan kiilerin fonksiyonlarn tanmlarn grmeden, deikenler iin kullanlan isimleri sayesinde, fonksiyonlarn yapt i konusunda daha fazla bilgi sahibi olmalarna yardmc olur. Ayn trden geri dn deerine sahip fonksiyonlarn bildirimi virgllerle ayrlarak yazlabilir, ama bu genel olarak pek tercih edilen bir durum deildir : double func1(int), func2(int, int), func3(float); Yukardaki bildirimde func1, func2 ve func3 fonksiyonlarnn hepsi double trden geri dn deerine sahip fonksiyonlardr. Fonksiyon prototip bildirimleri deiken tanmlamalaryla da birletirilebilir. Bu da tercih edilen bir durum deildir. long func1(int), long func2(void), x, y; Yukardaki deyim ile func1 ve func2 fonksiyonlarnn prototip bildirimi yaplmken, x ve y deikenleri tanmlanmtr.

100

FONKSYON PROTOTPLER (C++ dilinde eer arlan fonksiyon aran fonksiyondan daha nce tanmlanmamsa, fonksiyonun geri dn deeri int trden kabul edilmez. Bu durumda fonksiyon prototip bildiriminin yaplmas zorunludur. Prototip bildiriminin yaplmamas durumunda derleme zamannda hata (error) oluacaktr.) Fonksiyon Prototiplerinin Bildirim Yerleri Fonksiyon prototiplerinin bildirimi programn herhangi bir yerinde yaplabilir. Prototipler bildirimleri global dzeyde yaplmsa (yani tm bloklarn dnda yaplmsa) bildirildikleri yerden dosya sonuna kadar olan alan iinde geerliliklerini srdrrler. nemli olan nokta sz konusu fonksiyon arlmadan bildiriminin yapm olmasdr. Ancak uygulamalarda ok az raslanmasna karlk, fonksiyon prototipleri yerel dzeyde de yaplabilir. Bu durumda prototip bildirimi ile , yalnzca bildirimin yaplm olduu bloa bilgi verilmi olur. Baka bir deyile prototip bildirimi de, deiken tanmlamalar gibi faaliyet alan kuralna uyar. Geleneksel olarak fonksiyon prototip bildirimleri programn en yukarsnda ya da programcnn tanmlad balk dosyalarnn birinin iinde yaplr. Balk dosyalar (header files) ileride detayl olarak ele alnacaktr. Standart C Fonksiyonlarnn Prototipleri Standart C fonksiyonlarnn prototipleri standart balk dosyalar iine yerletirilmitir. Programc, uygulamalarda standart bir C fonksiyonunun prototipini kendi yazmaz, bu prototip bildiriminin bulunduu balk dosyasn #include nilemci komutuyla (ileride detayl greceiz) koda dahil eder. Standart C fonksiyonlarnn prototip bildirimlerinin yaplmamas durumda hata ortaya kmaz. Eer geri dn deeri tr int deilse ve geri dn deeri kullanlrsa, programda yanllk sz konusu olacaktr. Derleme zamannda hata oluumu sz konusu deildir, nk derleyici fonksiyonun geri dn deerinin trn int trden kabul edecek fakat daha sonra kaynak kod iinde fonksiyonun tanmlamasn gremediinden hata olumayacaktr. Ancak programn alma zaman srasnda fonksiyonun geri dn deeri int trden bir deer olarak alnacandan yanllk sz konusu olacak ve program doru almayacaktr. Standart C fonksiyonlarnn prototipleri sonu .h uzantl olan (header) balk dosyalar iindedir. nilemci komutuyla ilgili balk dosyasnn kaynak koda ilave edilmesiyle, aslnda standart C fonksiyonunun da prototip bildirimi yaplm olmaktadr. Zira nilemci modlnn kts olan kaynak program artk derleyiciye verildiinde, ekleme yaplm bu dosyada fonksiyonun prototip bildirimi de bulunmaktadr. phesiz, balk dosyay kaynak koda ilave etmek yerine standart C fonksiyonunun prototipini kendimiz de kaynak koda yazabiliriz, bu durumda da bir yanllk sz konusu olmayacaktr. rnek : Standart bir C fonksiyonu olan pow fonksiyonunun prototipini iki ekilde kaynak koda ekleyebiliriz: 1. Fonksiyon prototipinin bulunduu balk dosyasn kaynak koda bir nilemci komutuyla dahil ederek. #include <math.h> 2. Fonksiyonun prototipini kendimiz yazarak. double pow(double taban, double us); Ancak tercih edilecek yntem balk dosyasn kaynak koda dahil etmek olmaldr. nk:

101

10 1. Programc tarafndan fonksiyonun prototipi yanl yazlabilir.

2. Balk dosyalarnn kaynak koda dahil edilmesinin nedeni yalnzca fonksiyon prototip bildirimi deildir. Balk dosyalarnda daha baka bilgiler de vardr. (Makrolar, sembolik sabitler, tr tanmlamalar, yap bildirimleri vs.) Fonksiyon Prototip Bildirimi le Arguman-Parametre Uyumu Kontrol Fonksiyon prototiplerinin ana amac yukarda da belirtildii gibi, derleyiciye fonksiyonun geri dn deeri tr hakknda bilgi vermektir. Ancak fonksiyon prototip bildirimlerinde fonksiyon parametrelerinin trleri belirtilmise, derleyici prototip bildirimindeki parametre deikeni saysn fonksiyon arma ifadesindeki fonksiyona gnderilen arguman says ile karlatrr. rnein: float calculate(float, float); biiminde bir prototip yazldnda eer calculate fonskiyonu eksik ya da fazla parametre ile arlrsa derleme hatas oluacaktr. x = calculate(5.8); /* hata eksik parametre ile arlm */ y = calculate(4.6, 7.9, 8.0) /* hata fazla parametre ile arlm */ Fonksiyon prototip bildiriminde parantezin ii bo braklrsa (buraya hibir parametre tr bilgisi yazlmazsa) bu durumun zel bir anlam vardr. Bu durumda derleyiciden parametre kontrolu yapmas istenmemi olur. (Derleyici artk fonksiyon arma ifadesindeki arguman saysyla fonksiyonun formal parametrelerinin saysnn eitliini kontrol etmez.) Parametre kontrolu yaplmasnn istenmemesi uygulamalarda sk grlen bir durum deildir. Parametre deikenlerinin says ile fonksiyona gnderilen arguman saysnn uyumu kontrol, C diline standartlarma almalar srasnda eklenmitir. Klasik C diye adlandrdmz, C dilinin standartlatrlmasndan nceki dnemde byle bir kontrol sz konusu deildi ve prototip bildirimlerinde fonksiyon parantezlerinin ii bo braklrd. Geriye doru uyumun korunmas ve eskiden yazlm kaynak kodlarn da desteklenmesi amacyla, fonksiyon prototip bildirimlerinde fonksiyon parantezlerinin iinin bo braklmas durumu halen geerli bir ilem olarak braklmtr. float calculate(); /* derleyiciden parametre kontrol yapmamas isteniyor */

Yukardaki bildirimden calculate fonksiyonunun parametre almad anlam kmaz. Yukardaki bildirimden sonra eer calculate fonksiyonu aadaki ifadelerle arlrsa derleme zaman hatas olumayacaktr: x = calculate (5.8); y = calculate (4.6, 7.9, 8.0) /* derleme zamannda hata olumaz */ /* derleme zamannda hata olumaz */

Eer fonksiyonun gerekten parametre deikeni yoksa, ve derleyicinin fonksiyonun arlmas durumunda arguman parametre deikeni kontrolu yapmas isteniyorsa, prototip bildiriminde fonksiyon parantezi ierisine void anahtar szc yazlmaldr. float sample(void); Burada sample fonksiyonunun parametre almad bildirilmitir. Eer fonksiyon x = sample(20); /* derleme hatas */

fadesiyle arlrsa derleme hatas oluur.

102

FONKSYON PROTOTPLER (C++ dilinde fonksiyon prototip bildiriminde, fonksiyon parametre parantezinin iinin bo braklmas, fonksiyonun parametre deikeni olmadn gstermektedir. Baka bir deyile, prototip bildirimi srasnda parantezlerin iini bo brakmak, derleyiciden arguman says kontrolu yapmasn istememek anlamna gelmez. C++'da arguman says ile parametre deilenlerinin saysnn uyumu daima derleyici tarafndan kontrol edilir. Dolaysyla derleyici, prototip bildiriminde, parametre parantezi iine bir ey yazlmadn grdkten sonra, fonksiyonun tanmlanma ifadesinde parametre deiken(ler)inin varln grrse, bu durumu bir error mesaj ile bildirir.) Fonksiyon prototip bildiriminin yaplm olmas o fonksiyonun tanmlamasn ya da arlmasn zorunlu klmaz. Prototip bildirimi yaplan bir fonksiyonu tanmlamamak hata oluturmaz. Bir fonksiyonun prototip bildirimi birden fazla yaplabilir.. Bu durumda hata oluturmaz. Ama yaplan bildirimler birbirleriyle elimemelidir. Kaynak dosya iinde ayn fonksiyona ilikin prototip bildirimlerinin farkl yerlerde ve aadaki biimlerde yapldn dnelim : int sample (int, int); sample (int, int); int sample(int x, int y); sample(int number1, int number2); Yukardaki bildirimlerinin hibirinde bir eliki sz konusu deildir. Fonksiyon parametre deikenlerinin isimleri iin daha sonraki bildirimlerde farkl isimler kullanlmas bir eliki yaratmayacaktr. nk bu isimlerin faaliyet alan (name scope) yalnzca bildirimin yapld parantezin iidir. Ancak aadaki farkl bildirimler derleme zamannda error oluturacaktr : double func(int x, double y); double func(int x, float y); long sample(double x); sample (double x);

/* error! bildirimler arasnda eliki var */

/*

error!

bildirimler

arasnda

eliki

var.

*/

Fonksiyon prototiplerinde parametre deikenlerinin trlerinin de belirtilmesi, argumanlarn parametre deikenlerime aktarlmasnda tr dnmne olanak salamaktadr. Bu durum tr dnmleri konusunda ele alnacaktr.

103

11

11 . BLM :

KOUL OPERATR

Koul operatr (conditional operator) C dilinin 3 operand alan tek operatrdr. (ternary operator) Koul operatrnn 3 operand, ifade tanmna uygun herhangi bir ifade olabilir. Koul operatrnn genel sentaks aadaki gibidir: ifade1 ? ifade2 : ifade3 Koul operatr yukardaki biimden de grld gibi birbirinden ayrlm iki atomdan olumaktadr. ? ve : atomlar operatrn 3 operandn birbirinden ayrr. Derleyici bir koul operatr ile karlatn ? atomundan anlar ve ? atomunun solundaki ifadenin (ifade1) saysal deerini hesaplar. Eer ifade1in deeri 0 d bir saysal deerse, bu durum koul operatr tarafndan doru olarak deerlendirilir, ve bu durumda yalnzca ifade2nin saysal deeri hesaplanr. Eer ifade1in deeri 0 ise bu durum koul operatr tarafndan yanl olarak deerlendirilir ve bu durumda yalnzca ifade3n saysal deeri hesaplanr. Dier operatrlerde olduu gibi koul operatr de bir deer retir. Koul operatrnn rettii deer ifade1 doru ise (0 d bir deer ise) ifade2nin deeri, ifade1 yanl ise ifade3n deeridir. rnek: m = x > 3 ? y + 5 : y 5; Burada nce x > 3 ifadesinin saysal deeri hesaplanacaktr. Bu ifade 0 d bir deerse (yani doru ise) koul operatr y + 5 deerini retecektir. x > 3 ifadesinin deeri 0 ise (yani yanl ise) koul operatr y 5 deerini retecektir. Bu durumda m deikenine x > 3 ifadesinin doru ya da yanl olmasna gre y + 5 ya da y 5 deeri atanacaktr. Ayn ilem if deyimi ile de yaplabilir : if (x > 3) m = y + 5; else m = y 5; Koul operatr Operatr ncelik Tablosunun 13. ncelik seviyesindedir. Bu seviye atama operatrnn hemen stdr. Aadaki ifadeyi ele alalm: x>3?y+5:y5=m Koul operatrnn ncelii atama operatrnden daha yksek olduu iin, nce koul operatr ele alnr. x > 3 ifadesinin DORU olduunu ve operatrn y + 5 deerini rettiini dnelim. Toplam ifadenin deerlendirilmesinde kalan ifade y+5=m olacak ve bu da derleme zaman hatasna yol aacaktr. nk y + 5 ifadesi sol taraf deeri deildir, nesne gstermez. (Lvalue required).

104

KOUL OPERATR Koul operatrnn birinci ksmn (ifade1) parantez iine almak gerekmez. Ancak, okunabilirlik asndan genellikle parantez iine alnmas tercih edilir. (x >= y + 3) ? a * a : b Koul operatrnn nc operand (ifade3) konusunda dikkatli olmak gerekir. rnein : m = a > b ? 20 : 50 + 5 a > b ifadesinin doru olup olmamasna gre koul operatr 20 ya da 55 deerini retecek ve son olarak da m deikenine koul operatrnn rettii deer atanacaktr. Ancak m deikenine a > b ? 20 : 50 ifadesinin deerinin 5 fazlas atanmak isteniyorsa bu durumda ifade aadaki gibi dzenlenmelidir: m = (a > b ? 20 : 50) + 5 Koul operatrnn 3 operand da bir fonksiyon arma ifadesi olabilir, ama arlan fonksiyonlarn geri dn deeri reten fonksiyonlar olmas (void olmayan) gerekmektedir. operanddan biri geri dn deeri void olan bir fonksiyona ilikin fonksiyon arma ifadesi olursa koul operatr deer retmeyeceinden bu durum derleme zamannda hata olumasna neden olacaktr. Aadaki kod parasn inceleyelim: #include <stdio.h> int func1(void); int func2(void); int func3(void); int main() { int m; m = func1() ? func2() : func3(); return 0;

Yukarda koul operatrnn kullanld ifadede m deikenine, func1 fonksiyonunun geri dn deerinin 0 d bir deer olmas durumunda func2 fonksiyonunun geri dn deeri, aksi halde func3 fonksiyonunun geri dn deeri atanacaktr. Koul operatrnn rettii bir nesne deil bir deerdir. Koul operatrnn rettii deer nesne gstermedii iin bu deere bir atama yaplamaz. Aadaki if deyimini inceleyelim: if (x > y) a = 5; else b = 5; Yukardaki if deyimine x > y ifadesinin doru olmas durumunda a deikenine, yanl olmas durumunda ise b deikenine 5 deeri atanyor. Ayn ii koul operatr kullanarak yapmak istenip de aadaki ifade oluturulursa:

105

11 (x > y) ? a : b = 5; bu durum derleme zaman hatasna yol aacaktr. nk koul operatrnn rettii a ya da b deikenlerinin deeridir, nesnenin kendisi deildir. Byle bir atama sol tarafn nesne gsteren bir ifade olmamasndan dolay derleme zamannda hata oluturur. Ayn nedenden dolay aadaki ifadenin deerlendirilmeside derleme zamannda hata olumasna neden olacaktr: (x > 5 ? y : z)++; Parantez iindeki ifade deerlendirildiinde elde edilen y ya da z nesneleri deil bunlarn deerleridir. Yani postfix ++ operatrnn operand nesne deildir. Bu durumda error oluacaktr. (L value required!) Koul Operatrnn Kullanld Durumlar Koul operatr her zaman if deyimine bir alternatif olarak kullanlmamaldr. Koul operatrnn kullanlmasnn tavsiye edildii tipik durumlar vardr ve bu durumlarda genel fikir, koul operatrnn rettii deerden ayn ifade iinde faydalanmak, bu deeri bir yere aktarmaktr. 1. Bir nermenin doru ya da aynl olmasna gre farkl iki deerden birinin ayn deikene aktarlmas durumunda: p = (x == 5) ? 10 : 20; m = (a >= b + 5) ? a + b : a b; Yukardaki deyimleri if else yapsyla da kurabilirdik: if (x == 5) p = 10; else p = 20; if (a >= b + 5) m = a + b; else m = a b; 2. Fonksiyonlarda return ifadesini olutururken: return (x > y ? 10 : 20); Bu rnekte x > y ifadesinin doru olup olmamasna gre fonksiyonun geri dn deeri 10 ya da 20 olacaktr. Yukaridaki ifade yerine aadaki if yaps da kullanlabilirdi : if (x > y) return 10; else return 20; 3. Fonksiyon arma ifadelerinde arguman olarak: func(a == b ? x : y);
106

KOUL OPERATR

Bu ifadede func fonksiyonu a deikeninin b degikenine eit olup olmamas durumuna gre x ya da y argumanlaryla arlacaktr. if deyimiyle aadaki ekilde karlanabilir: if (a == b) func(x); else func(y); 4. if deyiminde koul ifadesi olarak da koul operatrnn kullanld grlr: if ( y == (x > 5 ? 10 : 20)) Yukaridaki ifadede x > 5 ifadesinin doru olup olmamasna gre if parantezi iinde y deikeninin 10 ya da 20ye eitlii test edilecektir. Yukardaki durumlarda koul operatrnn if deyimine tercih edilmesi iyi tekniktir. Bu durumlarda koul operatr daha okunabilir bir yap oluturmaktadr. (Cye yeni balayanlar iin if yaps daha iyi okunabilir ya da alglanabilir, ancak bir C programcisi iin koul operatrnn kullanlmas daha okunabilir bir yap oluturur.) Koul operatrnn bilinsizce kullanlmamas gerekir. Eer koul operatrnn rettii deerden dorudan faydalanlmayacaksa koul operatr yerine if kontrol deyimi tercih edilmelidir. rnein: x > y ? a++ : b++; Deyiminde koul operatrnn rettii deerden faydalanlmamaktadr. Burada aadaki if yaps tercih edilmelidir: if (x > y) a++; else b++; Baka bir rnek: x == y ? printf(eit\n) : printf(eit deil\n); Bu rnekte printf fonksiyonunun bir geri dn deeri retmesinden faydalanlarak koul operatr kullanlmtr. Koul operatr x == y ifadesinin doru olup olmamasna gre, 2. veya 3. ifade olan printf fonksiyonu arma ifadelerinden birinin deerini (geri dn deerini) retecektir. (Bu da aslnda ekrana yazlan karakter saysdr.) Ama ifade iinde koul operatrnn rettii deerin kullanlmas sz konusu deildir. Burada da if kontrol deyimi tercih edilmelidir : if (x == y) printf(eit\n); else printf(eit deil\n); Koul operatrnn ikinci ve nc operandlarnn trleri farkl ise tr dntrme kurallari dier operatrlerde olduu gibi devreye girecektir:

107

11 long a; int b; m = (x == y) ? b : a; Bu rnekte a nesnesi long trden b nesnesi ise int trdendir. x == y karlatrma ifadesi yanl olsa bile tr dnm gerekleecek ve int trden olan b long tre dntrlecektir. Baz durumlarda koul operatrnn de if kontrol deiminin de kullanlmas gerekmeyebilir. if (x > 5) m = 1; else m = 0; Yukardaki if deyimi koul operatr ile aadaki ekilde oluturulabilirdi : m = (x > 5) ? 1 : 0; Ancak koul operatrnn retecei deerlerin 1 veya 0 olabilecei durumlarda dorudan karlatrma operatrn kullanmak daha iyi teknik olarak deerlendirilmelidir. m = x > 5; Baka bir rnek : return x == y ? 1 : 0; yerine return x == y; yazabilirdik. Aada tanmlanan is_leap fonksiyonunu inceleyelim, fonksiyonun geri dn deerinin retilmesinde koul operatr kullanlmayp dorudan mantksal operatrlerin 0 ya da 1 deeri retmeleri fikrinden faydalanlmtr: #define BOOL int

BOOL is_leap(int year) { return !(year % 4) && year % 100 || !(year % 400); } Koul operatrnn ncelik yn sadan soladr. (right associative). Bir ifade iinde birden fazla koul operatr varsa nce en sadaki deerlendirilecektir. Aadaki kod parasn inceleyelim : int x = 1; int y = 1;
108

KOUL OPERATR

m = x < 5 ? y == 0 ? 4 : 6 : 8; printf("m = %d\n", m); Yukardaki kod parasnda printf fonksiyonu armnda m deikeninin deeri 6 olarak yazdrlacaktr. fade aadaki gibi ele alnacaktr : m = x < 5 ? (y == 0 ? 4 : 6) : 8;

109

12

12 . BLM :

DNG DEYMLER

Bir program parasnn yinelemeli olarak altrlmasn salayan kontrol deyimlerine dng denir. C dilinde 3 ayr dng deyimi vardr: Kontroln bata yapld while dngleri Kontroln sonda yapld while dngleri (do while dngleri) for dngleri Bu dng deyimlerinden en fazla kullanlan for deyimidir. for deyimi yalnzca C dilini deil, tm programlama dillerinin en gl dng yapsdr. Aslnda while ya da do while dngleri olmasa da bu dngler kullanlarak yazlan kodlar, for dngsyle yazlabilir. Ancak okunabilirlik asndan while ve do while dnglerinin tercih edildii durumlar vardr. Kontrolun Basta Yapld while Dngleri Genel biimi: while (ifade) deyim; while anahtar szcn izleyen parantez ierisindeki ifadeye kontrol ifadesi (control statement) denir. while parantezini izleyen ilk deyime dng gvdesi denir. Dng gvdesi basit bir deyim olabilecei gibi, bloklanm birden fazla deyimden de (bileik deyim) oluabilir. while deyiminin icras u ekilde olur: Kontrol ifadesinin saysal deeri hesaplanr. Kontrol ifadesinin saysal deeri 0 d bir deerse, mantksal olarak doru kabul edilir ve dng gvdesindeki deyim ya da deyimler altrlr. Kontrol ifadesinin saysal deeri 0 ise mantksal olarak yanl kabul edilir programn ak dngnn dndaki ilk deyimle devam eder. Yani while dngs, kontrol ifadesinin saysal deeri 0 d bir deer olduu srece, dng gvdesini oluturan deyim(ler)in icras ile devam eder. Daha nce belirttiimiz gibi C dilinde yaln bir deyimin olduu yere bileik bir deyim de yerletirilebilir. Bu durumda while dngs aadaki gibi de oluturulabilir : while (ifade) { ifade1; ifade2; ifade3; ...... } Bu durumda kontrol ifadesinin saysal deeri 0 d bir deer olduu (doru) srece blok parantezleri arasnda kalan tm deyimler icra edilecektir. rnek: #include <stdio.h> int main() { int i = 0; while (i < 10) { printf ("%d\n", i)

110

DNG DEYMLER ++i; } return 0; } Baka bir rnek: #include <stdio.h> #include <ctype.h> int main() { char ch; while (ch = getch(), toupper(ch) != 'Q') putchar(ch); return 0; } Baka bir rnek daha: int main() { char ch; while (ch = getch(), isupper (ch)) putchar(ch); return 0;

while parantezinin iindeki ifade yani koul ifadesi, ifade tanmna uygun herhangi bir ifade olabilir. while (1) { .... } Yukardaki while deyiminde kontrol ifadesi olarak bir sabit olan 1 says kullanlmtr. 1 deeri 0 d bir deer olduundan ve kontrol ifadesi bir deikene bal olarak deiemeyeceinden, byle bir dngden kmak mmkn olmayacaktr. Bu tr dnglere sonsuz dngler (infinite loops) denir. Sonsuz dngler bir yanllk sonucu oluturulabildii gibi, bilinli olarak da oluturulabilir. Sonsuz dnglerden baz yntemlerle klabilir. break Anahtar Szc break anahtar szc ile bir dng sonlandrlabilir. Kullanm break; eklindedir. Programn ak break anahtar szcn grdnde, dng krlarak dngnn ak dng gvdesi dndaki ilk deyim ile devam eder. Yani koulsuz olarak dngden klr. int main (void) {
111

12 char ch; while (1) { ch = getch(); if (ch == 'q') break; putchar(ch); } printf("dngden kld!..\n"); return 0;

Yukardaki programda bir sonsuz dng oluturulmutur. Dng ierisinde, dngnn her bir iterasyonunda ch deikenine klavyeden bir deer alnmaktadr. Eer klavyeden alnan karakter 'q' ise break anahtar szcyle programn ak while dng gvdesi dndaki ilk deyimle devam edecektir. ie Dngler Bir dngnn gvdesini baka bir kontrol deyimi oluturabilir. Dng gvdesini oluturan kontrol deyimi bir if deyimi olabilecei gibi baka bir dng deyimi de olabilir. (while, do while, for deyimleri) int main() { int i = 0; int k = 0; while (i < 10) { while (k < 10) { printf("%d %d", i, k); ++k; } } ++i; return 0;

ie dnglerde ierideki dngde break deyimi kullanldnda yalnzca ierideki dngden klr, her iki dngden birden kmak iin goto deyimi kullanlmaldr. (ileride greceiz) while dngs bir btn olarak tek deyim iinde ele alnr. rnek: while (1) while (1) { .....

..... .....,

Burada ikinci while dngs tek bir kontrol deyimi olarak ele alnaca iin, bloklamaya gerek yoktur. while dngsnn yanllkla bo deyim ile kapatlmas ok sk yaplan bir hatadr. int main()
112

DNG DEYMLER { int i = 10; while (--i > 0); /* burada bir bo deim var */ printf("%d\n", i); return 0;

Dng while parantezi ierisindeki ifadenin deeri 0 olana kadar devam eder ve bo deim dng gvdesi olarak icra edilir. Dngden kldnda ekrana 0 baslr. Sonlandrc ; while parantezinden sonra konulursa herhangi bir sentaks hatas olumaz. Derleyici while dngsnn gvdesinin yalnzca bir bo deyimden olutuu sonucunu kartr. Eer bir yanllk sonucu deil de bilinli olarak while dngsnn gvdesinde bo deyim (null statement) bulunmas isteniyorsa, okunabilirlik asndan, bu bo deyim while parantezinden hemen sonra deil, alt satrda ve bir tab ieriden yazlmaldr. while Dngs erisinde Postfix ++ ya da -- Operatrnn Kullanlmas Bir postfix artrm ya da eksiltme ilemi yapldnda nce dngye devam edilip edilmeyecei karar verilir, sonra artrm ya da eksiltim uygulanr. rnek : int main() { int i = 0; while (i++ < 100) printf("%d\n", i); printf("%d\n", i); return 0;

/* ekrana 101 deerini basar. */

Baka bir rnek: ... int i = 10; while (i-- > 0) printf("%d\n", i); printf("%d\n", i); n bir pozitif tam say olmak zere while dngs kullanlarak n defa dnen bir while dngs oluturmak iin while (n-- > 0) ya da while (n--) kullanlabilir. Aadaki iie dng yaps bir gecikme salamak iin kullanlmtr. int main()
113

12 { int i = 0; long j; while (i++ < 10) { printf("%d\n", i); j = 1000000L; while (j--> 0 ) ; return 0;

Bazen dngler bilinli bir ekilde bo deyimle kapatlmak istenebilir. Bu durumda bo deyim normal bir deyim gibi tablama kuralna uygun olarak yerletirilmelidir. Kontroln Sonda Yapld while Dngleri Genel biim; 1. do

ifade 1; while (ifade 2);

2. do { ifade 1; ifade 2; } while (ifade); do while dngsnde kontrol ifadesi sondadr. while parantezinden sonra sonlandrc ";" bulunmaldr. Yani buradak sonlandrc yanllk sonucu koyulmamtr, deyime ilikin sentaksn bir parasdr. Dng gvdesindeki deyim(ler) en az bir kere icra edilecektir. rnek : int main() { int i = 0; do { ++i; printf("%d\n", i); } while (i < 10); return 0; } Baka bir rnek: int main() { char ch; do { printf ("(e)vet / (h)ayt?\n"); ch = getch(); } while (ch != 'e' && ch != 'h'); printf("ok...\n");
114

DNG DEYMLER return 0; } Uygulama 1'den 100'e kadar saylar her satrda be tane olacak biimde ekrana yazan bir C programnn yazlmas: #include <stdio.h> int main() { int i = 0; do { printf("%d ", ++i); if (i % 5 == 0) printf("\n"); } while (i < 100); return 0;

Baka bir zm: #include <stdio.h> void main() { int i = 0; while (i < 100) { printf ("%d", i); if (i % 5 == 4) printf("\n"); ++i; return 0;

Uygulama Bir tamsaynn basamak saysn bulan program. #include <stdio.h> int main() { int digits = 0; int number; printf("bir tamsayi girin: "); scanf("%d", &number); do { n /= 10;
115

12 digits++; } while (n > 0); printf("the number has %d digit(s).\n", digits); return 0;

Ayn program while dngs kullanarak yazalm. ... while (n > 0) { n /= 10; digits++; } ... do while dngs yerine while dngs kullanldnda, girilen saynn 0 olmas durumunda dng gvdesindeki deyimler hi icra edilmeyecekti. Bu durumda ekranda: The number has 0 digit(s) yazs kacakt.

for Dngleri for dngleri yalnzca C dilinin deil, belki de tm programlama dillerinin en gl dng yaplardr. for dnglerinin genel biimi u ekildedir: for (ifade1; ifade2; ifade3) deyim1; for (ifade1; ifade2; ifade3) { deyim1; deyim2; ... } Derleyici for anahtar szcnden sonra bir parantez almasn ve parantez ierisinde iki noktal virgl bulunmasn bekler. Bu iki noktal virgl for parantezini ksma ayrr. Bu ksmlar yukarda ifade1 ifade2 ve ifade 3 olarak gsterilmitir. for parantezi iinde mutlaka 2 noktal virgl bulunmaldr. for parantezi iinin bo braklmas, ya da for parantezi ierisinde 1, 3 ya da daha fazla noktal virgln bulunmas derleme zamannda hata olumasna yol aacaktr. for parantezinin kapanmasndan sonra gelen ilk deyim dng gvdesini (loop body) oluturur. Dng gvdesi basit bir deyimden oluabilecei gibi, bileik deyimden de yani blok iine alnm birden fazla deyimden de oluabilir. for parantezi ierisindeki her ksmn da ayr ayr ilevleri vardr. for parantezinin 2. ksmn oluturan ifadeye kontrol ifadesi denir. (control expression). Tpk while parantezi iindeki ifade gibi, dngnn devam konusunda bu ifade sz sahibidir. Bu ifadenin deeri 0
116

DNG DEYMLER d bir deer ise, yani mantksal olarak doru ise, dng devam eder. Dng gvdesindeki deyim(ler) icra edilir. Kontrol ifadesinin deeri 0 ise programn ak for dngsnn dndaki ilk deyimle devam edecektir. Programn ak for deyimine gelince, for parantezinin 1. ksm 1 kez icra edilir ve genellikle dng deikenine ilk deer verme amacyla kullanlr. (Byle bir zorunluluk yoktur). for dngsnn 3. ksm dng gvdesindeki deyim ya da deyimler icra edildikten sonra, dnte altrlr. Ve ounlukla dng deikeninin artrlmas ya da azaltlmas amacyla kullanlr. (Byle bir zorunluluk yok.) for (ilk deer; koul; ilem) { ... ... ... } int main() { int i; for (i = 0; i < 2; ++i) printf(%d\n, i); printf(son deer = %d\n, i); return 0; } Yukardaki program inceleyelim: Programn ak for deyimine gelince, nce for parantezi iindeki 1. ifade icra ediliyor. Yani i deikenine 0 deeri atanyor. imdi programn ak for parantezinin 2. ksmna yani kontrol ifadesine geliyor ve i < 2 koulu sorgulanyor. Kontrol ifadesinin deeri 0 d bir deer olduu iin, ifade mantksal olarak doru kabul ediliyor ve programn ak dng gvdesine geiyor. Dng gvdesi bloklanmad iin, dng gvdesinde tek bir deyim var. (basit deyim). Bu deyim icra ediliyor. Yani ekrana i deikeninin deeri yazlarak imle alt satra geiriliyor. Programn ak bu kez for parantezinin 3. ksmna geliyor ve buradaki ifade bir deyimmi gibi icra edeiliyor, yani i deikeninin deeri 1 artrlyor. i deikeninin deeri 1 oluyor. 2. ifade yeniden deerlendiriliyor ve i < 2 ifadesi doro olduu iin bir kez daha dng gvdesi ira ediliyor. Programn ak yine for parantezinin 3. ksmna geliyor ve buradaki ifade bir deyimmi gibi icra edeiliyor, yani i deikeninin deeri 1 artrlyor. i deikeninin deeri 2 oluyor. Programn ak yine for parantezinin 2. ksmna geliyor ve buradaki kontrol ifadesi tekrar sorgulanyor. i < 2 ifadesi bu kez yanl olduu iin programn ak dng gvdesine girmiyor ve programn ak dng gvdesi dndaki ilk deyimle devam ediyor. Yani ekrana :

117

12 son deer = 2 yazlyor. Uygulama 1den 100e kadar olan saylarn toplamn bulan program : int main() { int i; int total = 0; for (i = 0; i < 100; ++i) total += i; printf(Toplam = %d, total); return 0; } Dng deikeninin tamsay trlerinden birinden olmas gibi bir zorunluluk yoktur. Dng deikeni gerek say trlerinden de olabilir. int main() { double i; for (i = 0; i < 6.28; i = i + 0.01) printf(lf\n, i); return 0; } Daha nce de sylendii gibi for parantezi iindeki her 3 ksm da ifade tanmna uygun ifadeler ierebilir, yani bir dng deikenine ilk deer verilmesi, dng deikenine bal bir koul ifadesi olmas, dng deikeninin de azaltlmas ya da artrlmas bir zorunluluk deildir. int main() { char ch; for (ch = getch(); ch != p ; ch = getch()) putchar(ch); return 0;

Baka bir rnek: int main() { for (printf(1. ifade\n"); printf(2. ifade\n), getch() != q; printf(3. ifade\n)); } Virgl operatr ile birletirilmi deimlerin soldan saa doru srayla icra edileceini ve toplam ifadenin retecei deerin en sadaki ifadenin deeri olacan hatrlayn.
118

DNG DEYMLER

for dngsnn 1. ksm hi olmayabilir. rnein dng dnda, programn ak for deyiminin icrasna gelmeden nce, dng deikenine ilk deer verilmi olabilir. ... i = 0; for (; i < 100; ++i) printf(%d\n, i); for dngsnn 3. ksm da olmayabilir. Dng deikeninin artrlmas ya da eksiltilmesi for parantezi ii yerine dng gvdesi ierisinde gerekletirilebilir. 1. ve 3. ksm olmayan (yalnzca 2. ksma sahip) bir for dngs rnei: ... i = 0; for (; i < 100; ) { printf(%d\n, i); ++i; } ... 1.ve 3. ksm olmayan for dngleri tamamen while dngleriyle edeerdir. Cde for dngleriyle while dngleriyle yapabildiimiz hereyi yapabiliriz. O zaman yle bir soru aklmza gelebilir: Madem for dngleri while dnglerini tamamen kapsyor, o zaman while dnglerine ne gerek var? while dnglerinin baz durumlarda kullanlmas for dnglerine gre ok daha okunabilir bir yap yaratmaktadr. for parantezinin 2. ksm da hi olmayabilir. Bu durumda kontrol ifadesi olmayaca iin dng bir koula bal olmakszn srekli dnecektir. Yani sonsuz dng oluturulacaktr. Ancak iki adet noktal virgl yine parantez iinde mutlaka bulunmak zorundadr. for parantezinin hibir ksm olmayabilir. Ancak yine iki noktal virgl bulunmak zorundadr: ... i = 0; for (;;) { printf(%d\n, i); ++i; if (i == 100) break; } ... for (;;) ile while (1) edeerdir. kisi de sonsuz dng belirtir. Sonsuz dng oluturmak iin for (;;) biimi while (1)'e gre daha ok tercih edilir. Bunun nedeni eski derleyicilerde while(1) ifadesi kullanldnda dgnn her dnnde kontrol ifadesinin tekrar test edilmesidir. Ama yeni derleyicilerde byle bir kontrol sz konusu deildir. Ama baz programclar while (1) ifadesini tercih ederler. (Tabi burada kontrol ifadesi 1 yerine 0 d herhangi bir deer de olabilirdi ama geleneksel olarak 1 ifadesi kullanlmaktadr.)
119

12 Sonsuz Dnglerden k 1. break anahtar szc ile. Bu durumda programn ak dng gvdesi dndaki ilk deyime ynlenecektir. (eer i ie dng varsa break anahtar szc ile yalnzca iteki dngden klacaktr.) 2. return anahtar szc ile bu durumda fonksiyonun (main de olabilir) icras sona erecektir. 3. goto anahtar szcyle. ie birden fazla dng varsa en iteki dng iinden en dtaki dngnn dna kadar kabiliriz. (goto anahtar szcnn ok az saydaki faydal kullanmndan biri budur.) 4. exit fonksiyonu ile. (ileride greceiz)

continue Anahtar Szc continue anahtar szc de tpk break anahtar szc gibi bir dng ierisinde kullanlabilir. Programn ak continue anahtar szcne geldiinde sanki dng yinelemesi bitmi gibi yeni bir yinelemeye geilir. Eer for dngs ierisinde kullanlyorsa yeni bir yinelemeye gemeden nce dngnn 3. ksm yaplr. rnek : #include <stdio.h> int main() { int i, k; char ch; for (i = 0; i < 100; ++i) { if (i % 3 == 0) continue; printf("%d\n", i); } return 0; } break anahtar szc bir dngy sonlandrmak iin, continue anahtar szc de bir dngnn o anda iinde bulunulan yinelemesini sonlandrmak iin kullanlr. continue anahtar szc zellikle, dng ierisinde uzun if deyimlerini varsa, okunabilirlii artrmak amacyla kullanlr. for (i = 0; i < n; ++i) { ch = getch(); if (!isspace(ch)) { ... ... ... } }

120

DNG DEYMLER Yukardaki kod parasnda dng iinde, klavyeden getch fonksiyonu ile deer atanan ch deikeni boluk karakteri deilse, bir takm deyimlerin icras istenmi. Yukardaki durum continue anahtar szcyle daha okunabilir hale getirilebilir : for (i = 0; i < n; ++i) { ch = getch(); if (isspace(ch)) continue; ... ... ... } n kere dnen for deyimi kalplar for (i = 0; i < n; ++i) for (i = 1; i <= n; ++i) for (i = n - 1; i >= 0; --i) for (i = n; i > 0; --i) Bir dngden kmak iin dng deikeni ile oynamak kt bir tekniktir. Bu programlar okunabilirlikten uzaklatrr. Bunun yerine break anahtar szc ile dnglerden klmaldr. Uygulama Basamaklarnn kpleri toplam kendisine eit olan 3 basamakl saylar bulan program. #include <stdio.h> #include <conio.h> int main() { int i, j, k; int number = 100; clrscr(); for (i = 1; i <= 9; ++i) for (j = 0; j <= 9; ++j) for (k = 0; k <= 9; ++k) { if (i * i * i + j * j * j + k * k * k == number) printf("%d says artlar saglyor\n", number); number++; } return 0; } Baka bir zm: #include <stdio.h> #include <conio.h>
121

12

int main() { int i, b1, b2, b3; clrscr(); for (i = 100; i <= 999; ++i) { b1 = i / 100; b2 = i % 100 / 10; b3 = i % 10; if (b1 * b1 * b1 + b2 * b2 * b2 + b3 * b3 * b3 == i) printf("%d says artlar salyor\n", i); } return 0;

Uygulama Kendisine gnderilen iki tamsaynn obeb ve okek deerlerini hesaplayan fonksiyonlar. #include <stdio.h> #include <conio.h> int obeb(int number1, int number2); int okek(int number1, int number2); int main() { int x, y; int n = 20; clrscr(); while (n-- > 0) { printf("iki tamsay giriniz : "); scanf("%d%d", &x, &y); printf("obeb = %d\n", obeb(x, y)); printf("okek = %d\n", okek(x, y)); } getch(); return 0;

int obeb(int number1, int number2) { int i; int min = (number1 < number2) ? number1 : number2; for (i = min; i >= 1; --i) if (number1 % i == 0 && number2 % i == 0) return i;

int okek(int number1, int number2)


122

DNG DEYMLER { int i; int max = (number1 > number2) ? number1 : number2; for (i = max; i <= number1 * number2; i += max) if (i % number1 == 0 && i % number2 == 0) return i; } Uygulama Kendisine gnderilen int trden argumann faktriyel deerini hesaplayan fonksiyon. long 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; } Uygulama Birinci parametre deikeninde tutulan tamsaynn ikinci parametre deikeninde tutulan tamsay kuvvetini hesaplayan fonksiyon. long power(int base, int exp) { long result = 1; int k; for (k = 1; k <= exp; ++k) result *= base; return result;

ya da long power(int base, int exp) { long result = 1; while (exp-- > 0) result *= base;

Uygulama Kendisine gnderilen saynn asal say olup olmadn test eden isprime fonksiyonu.

123

12 int isprime(long 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; } Uygulama Blenlerinin toplamna eit olan saylara mkemmel tamsay (perfect integer) say denir. rnein 28 says bir mkemmel tamsaydr. 1 + 2 + 4 + 7 + 14 = 28 Kendisine gnderilen bir argumann mkemmel tamsay olup olmadn test eden is_perfect fonksiyonu. #include <stdio.h> int is_perfect(int number); int main() { int k; for (k = 1000; k <= 9999; ++k) if (isperfect(k)) { printf("%d perfect\n"); return 0; } return 0;

int is_perfect(int number) { int i; int total = 1; for (i = 2; i < = number / 2; ++i) if (number % i == 0) total += i; return number == total;
124

DNG DEYMLER }

Uygulama int trden bir sayy arpanlarna ayran ve arpanlar kkten bye ekrana yazdran display_factors fonksiyonunu. #include <stdio.h> #include <conio.h> void factors(int number); /* test kodu : 1111 ile 1200 arasndaki saylar arpanlara ayryor */ int main() { int k; for (k = 1111; k < 1200; ++k) { printf("%d saysnn arpanlar = ", k); factors(k); putchar('\n'); getch(); } return 0;

void factors(int number) { int temp = number; int k; for (k = 2; k <= number / 2; ++k) while (temp % k == 0) { printf("%d ", k); temp /= k; }

Uygulama Klavyeden alnan cmleyi ekrana yazan ve cmle "." karakteriyle sonlannca yazlan toplam kelime saysn ve ortalama kelime uzunlugunu bulan program. #include <stdio.h> #include <conio.h> #include <ctype.h> int main() { int ch; int wordlength = 0, total = 0, wordcount = 0;

125

12 clrscr(); while ((ch = getch()) != '.') { putchar(ch); if (isspace(ch)) { total += wordlength; if (wordlength) wordcount++; wordlength = 0; } else wordlength++;

} wordcount++; total += wordlength; printf("\n\ntoplam kelime says = %d\n", wordcount); printf("ortalama uzunluk = %f\n", (double) total / wordcount); return 0;

Uygulama Klavyeden srekli karakter alnmasn salayan, alnan karakterleri ekranda gsteren, ancak arka arkaya "xyz" karakterleri girildiinde sonlanan bir program. #include <stdio.h> #include <conio.h> main() { char ch; int total = 0; while (total < 3) { ch = getch(); putchar(ch); if (ch == '' && total == 0) total++; else if (ch == '' && total == 1) total++; else if (ch == 'k' && total == 2) total++; else total = 0; } return 0; } alma Sorular Prototipi aada verilen isprimex fonksiyonunu tanmlaynz. int isprimex(long number);

126

DNG DEYMLER isprimex fonksiyonuna gnderilen argumann asal olup olmad test edilecek, eer say asal ise bu kez saynn basamak deerleri toplanarak elde edilen saynn asal olup olmad test edilecektir. Bu ilem sonuta tek basamakl bir say kalana kadar devam edecektir. Eer en son elde edilen tek basamakl say dahil tm saylar asal ise isprimex fonksiyonu 0 d bir deere geri dnecektir. Eer herhangi bir kademede asal olmayan bir say elde edilirse fonksiyon 0 deerine geri dnecektir. Yazdnz fonksiyonu aadaki main fonksiyonu ile test edebilirsiniz : #include <stdio.h> #include <conio.h> int isprimex(long number); int main() { long k; clrscr(); for (k = 19000; k <= 20000; ++k) if (isprimex(k)) printf("%ld \n", k); return 0; }

127

13

13 . BLM :

TR DNMLER

Bilgisayarlarn aritmetik ilemleri gerekletirmesinde bir takm kstlamalar sz konusudur. Bilgisayarlarn aritmetik bir ilemi gerekletirmesi iin (genellikle) ileme sokulan operandlarn uzunluklarnn ayn olmas (yani bit saylarnn ayn olmas) ve ayn ekilde bellee yerletirilmi olmalar gerekmektedir. rnek vermek gerekirse, bilgisayar 16 bit uzunluunda iki tam sayy dorudan toplayabilir ama 16 bit uzunluunda bir tam say ile 32 bit uzunluundaki bir tamsayy ya da 32 bit uzunluunda bir tamsay ile 32 bit uzunluunda bir gerek sayy dorudan toplayamaz. C programlama dili, deiik trlerin ayn ifade ierisinde bulunmalarna izin verir. Yani tek bir ifadede bir tamsay trnden deiken, bir float sabit 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 bir int sayyla 32 bitlik bir int sayy topladmzda, derleyici nce 16 bitlik int sayy 32 bit uzunluunda bir int sayya dntrecek ve ondan sonra toplama ilemini gerekletirecektir. Yine 16 bitlik bir int sayyla 64 bitlik bir double sayy arpmak istediimizde derleyici nce int sayy 64 bitlik bir double sayya dntrecektir. Bu tr dnm daha komplekstir nk int ve double saylar bellekte farkl ekillerde tutulurlar. Bu tr dnmleri programcnn kontrol dnda otomatik olarak gerekletirilirler. Bu tr dnmlerine otomatik tr dnmleri (implicit type conversions) diyeceiz. C dili programcya herhangi bir deikenin ya da sabitin trn, bir operatr kullanarak deitirme olana da verir. Programc tarafndan yaplan bu tip tr dnmlerine bilinli tr dnmleri (explicit type conversions/type casts) diyeceiz. nce otomatik tr dnmlerini inceleyeceiz. Otomatik tr dnmleri ne yazk ki karmak yapdadr ve iyi renilmemesi durumunda programlarda hatalar kanlmazdr. Zira C dilinde 11 ana tr vardr. Herhangi bir hataya dmemek iin bunlarn her trl ikili kombinezonu iin nasl bir otomatik tr dnm yaplacan ok iyi bilmemiz gerekir. Aada belirtilen 4 durumda mutlaka otomatik bir tr dnm yaplacaktr: 1. Aritmetik ya da mantksal bir ifadedenin operandlar ayn trden deilse: ... int ix, iy; float fl; if (fl * ix / iy) { ... } Bu durumda yaplan tr dnmlerine genel aritmetik tr dnmleri diyeceiz. 2. Atama operatr kullanldnda atama operatrnn sa tarafyla sol taraf ayn trden deilse: double toplam; long sayi1, sayi2; toplam = sayi1 + sayi2;

128

TR DNMLER

Bu durumda yaplan tr dnmlerine atama tr dnmleri diyeceiz. 3. Bir fonksiyon arlmas srasnda kullanlan bir argumann tr ile fonksiyonun ilgili parame deikeni ayn trden deilse: double sonuc; int sayi1, sayi2; ... sonuc = pow(sayi1, sayi2) double pow (double taban, double us) { ... }

4. Bir return ifadesinin tr ile ilgili fonksiyonun geri dn deerinin tr arasnda farkllk varsa: double funktion (int para1, int para2) { ... return (para1 + para2) } 3. ve 4. durumlar da bir atama ilemi olarak dnlebilir. Zira fonksiyon arma ifadesindeki argumanlar parametre deikenlerine kopyalanarak (atanarak) geirilir. Yine return ifadesi de aslnda geici blgeye yaplan bir atama ilemidir. Otomatik Tr Dnmleri Otomatik tr dnmleri iki operand alan operatrlerin bulunduu ifadelerde operandlarn trlerinin farkl olmas durumunda uygulanr. Ve otomatik tr dnm sonucunda farkl iki tr olmas durumu ortadan kaldrlarak operandlarn her ikisinin de trlerinin ayn olmas salanr. rnein int i; double d, result; result = i + d; Bu ifadenin sa tarafnda yer alan i ve d deikenlerinin trleri farkldr. (biri int dieri double trden). Bu durumda i ve d nesnelerinin trleri otomatik olarak ayn yaplr. Peki int tr m double tre dntrlecek yoksa double tr m int tre dntrlecek? Eer double tr int tre dntrlse bilgi kayb sz konusu olurdu (nk bu durumda gerek saynn en az virglden sonraki ksm kaybedilir). C'de otomatik tr dnmleri mmknse bilgi kayb olmayacak ekilde yaplr. Bu durumda bilgi kaybn engellemek iin genel olarak kk tr byk tre dntrlr. (istisnalar var) Bu duruma terfi (promotion) diyeceiz. Kurallar detayl olarak renmek iin oluabilecek durumlar iki ana durum altnda inceleyelim:

129

13 1. Operandlardan birinin tr gerek say trlerinden biriyse. Operandlardan birinin long double dierinin farkl bir tr olmas durumunda dier operand long double trne evrilir. Operandlardan birinin double dierinin farkl bir tr olmas durumunda dier operand double trne evrilir. Operandlardan birinin float dierinin farkl bir tr olmas durumunda dier operand float trne evrilir. 2. Operandlardan hibiri gerek say trlerinden deilse: Eer ifade iindeki operandlardan herhangi biri char, unsigned char, short ya da unsigned short trden ise aadaki algoritma uygulanmadan nce bu trler int tre dntrlr. Bu duruma tam sayiya terfi (integral promotion) denir. Daha sonra aadaki kurallar uygulanr: Operandlardan birinin unsigned long dierinin farkl bir tr olmas durumunda (long, unsigned int, int) dier operand unsigned long trne evrilir. Operandlardan birinin long trden dierinin farkl bir trden olmas durumunda (unsigned int, int) dier operand long trne evrilir. Operandlardan biri unsigned int trden dierinin farkl bir trden olmas durumunda (int) ikinci operand unsigned int trne evrilir. stisnalar: Eer operandlardan biri long int dieri unsigned int trnden ise ve kullanlan sistemde bu trlerin uzunluklar ayn ise (UNIX ve Win 32 sistemlerinde olduu gibi) her iki tr de unsigned long int trne dntrlr. Eer operandlardan biri int dieri unsigned short trnden ise ve kullanlan sistemde bu trlerin uzunluklar ayn ise (DOS'da olduu gibi) her iki tr de unsigned int trne dntrlr. Otomatik tr dnmleri ile ilgili kurallar aadaki tabloda zetleyelim: operandlardan herhangi biri yaplacak dnm char,short,unsigned char,unsigned short ilgili operand int tre dntrlecek. operand 1 long double double float unsigned long long operand 2 double, float, unsigned long, long, int, unsigned int float, unsigned long, long, int, unsigned int unsigned long, long, int, unsigned int long, int, unsigned int int, unsigned int yaplacak dnm 2. operand long double trne evrilecek. 2. operand double trne evrilecek. 2. operand float trne evrilecek. 2. operand unsigned long trne evrilecek 2. operand long trne dntrlecek.

130

TR DNMLER (istisnai duruma dikkat!) 2. operand unsigned int trne dntrlecek.

unsigned int

int

Fonksiyon arma ifadeleri de, operatrlerle birlikte baka ifadeleri oluturuyorsa, otomatik tr dnmlerine neden olabilir. Zira geri dn deerine sahip olan fonksiyonlar iin fonksiyonun arlma ifadesi, fonksiyonun geri dn deerini temsil etmektedir. rnein: int i = 5; ... pow(2, 3) + i ifadesinde pow fonksiyonunun geri dn deeri double trden olduu iin, int trden olan i deikeni de, ilemin yaplabilmesi iin double trne evrilerek ileme sokulacaktr.

Atama Tr Dnmleri Bu tr dnmlerin ok basit bir kural vardr: Atama operatrnn sa tarafndaki tr, atama operatrnn sol tarafndaki tre otomatik olarak dntrlr. Kk trlerin byk trlere dntrlmesinde bilgi kayb sz konusu deildir. rnein : double leftx; int righty = 5; leftx = righty; yukardaki rnekte righty deikeninin tr inttir. nce double tre otomatik dnm yaplacak ve double hale getirilen 5 sabiti (5.) leftx deikenine atanacaktr. Aada 16 bitlik TR int char int char unsigned int char sistemler iin baz rnekler verilmektedir : desimal hex dntrlecektr 138 0x008A long int d (100) 0x64 int -56 0xFFC8 long int \x95 (-07) 0x95 int 45678 0xB26E long int 0 (48) 0x30 long int

hex 0x0000008A 0x0064 0xFFFFFFFC8 0xFF95 0XFFFFB26EL 0x00000030L

desimal 138L 100 -56L -107 -19858L 30L

Negatif olan bir tamsay kk trden byk tre dntrldnde saynn yksek anlaml bitleri negatifliin korunmas amacyla 1 ile beslenmektedir. (Hex olarak F ile) Derleyici tarafndan yaplan otomatik, atama tr dnmlerinde, dntrlmesi durumunda bilgi kayb sz konusu olabilir. Aadaki basit kurallar verebiliriz: Eer atama operatrnn her iki taraf da tam say trlerinden ise (char, short, int, long), atama operatrnn sa tarafnn daha byk bir trden olmas durumunda bilgi kayb olabilir. Bilgi kayb ancak, atama operatrnn sa tarafndaki deerin, sol taraftaki trn snrlar iinde olmamas byk trn kk tre

131

13 durumunda sz konusu olacaktr. Bilgi kayb yksek anlaml bytelarn kaybolmas eklinde ortaya kar. rnek: long m = 0x12345678; int y; y = m; printf (m = %x\n, m); Yukardaki rnekte int trden olan y deikenine long trden bir deikenin deeri atanmtr. (16 bitlik bir sistemde rnein DOS altnda altmz dnyoruz.). DOS altnda int tr iin say snrlar 32768 +32767 deerleridir. Bu saylarda iki bytelk bir alan iin iaretli olarak yazlabilecek en byk ve en kk saylardr. Hex gsterimde her bir hex digit 4 bite ve her iki hex digit 1 byte alana tekabl edecektir. Dolaysyla 0x12345678 says 8 hex digit yani 4 byte uzunluunda bir saydr. Oysa atamann yaplaca taraf int trdendir ve bu tr max. 4 hex digit (2 byte) uzunlukta olabilmektedir. Bu durumda m deikenine ilikin deerin yksek anlaml 2 byte yani (4 hex digiti) kaybedilecektir. Atama ileminden sonra, printf fonksiyonuyla y deikeninin deeri yazdrldnda (hex gsterimli) ekrana 5678 deerinin yazdrld grlecektir. Atama operatrnn sa taraf gerek say trlerinden bir trden ise (float, double, long double) ve sol taraf ise tam say trlerinden birine ait ise, bu hereyden nce undefined behaviour (pheli kod) durumudur ve bu durumun olutuu kodlardan kesinlikle kanmak gerekir. Ama derleyicilerin hemen hemen hepsi bu durumda aadaki ekilde tr dnm yaparlar: Sa taraftaki gerek say trndeki deer nokta ieriyorsa, nce kesir ksm kaybedilir. Kesir ksm kaybedildikten sonra kalan deer eer sol taraf trnn snrlar iinde kalyorsa daha fazla bir bilgi kayb olmaz, fakat sol taraf trnn snrlar alyorsa ilave bir bilgi kayb daha olur ve yksek anlaml bytelar kaybedilir. rnek: double y = 234.12; int x; x = y; printf(x = %d\n, x ); y = 32768.1; x = y; printf(x = %d\n, x );

/* x deikenine 234 deeri atanacaktr */

/* x deikenine -32768 deeri atanacaktr */

Tam Sayya Terfi (integral promotion) Daha nce de akland gibi integral promotion bir ifade iinde bulunan char, unsigned char, short, unsigned short trlerinin, ifadenin derleyici tarafndan deerlendirilmesinden nce, otomatik olarak int tre dntrlmeleri anlamna gelir. Peki dnm signed int trne mi unsigned int trne mi yaplacak, bunu nasl bileceiz? Genel kural udur: Tr dnmne urayacak trn btn deerleri int tr ile ifade edilebiliyorsa int, edilemiyorsa unsigned int trne dnm yaplr.

132

TR DNMLER rnein unsigned short ve int trlerinin ayn uzunlukta olduu DOS iletim sisteminde unsigned short tr, tamsayya terfi ettirilirken unsigned int trne dntrlr. Eer tam sayya terfi ettirilecek deer, signed char, unsigned char ya da short (signed short) trlerinden ise dnm signed int trne yaplacaktr. Bilgi kayb ile ile ilgili u hususu da gz ard etmemeliyiz. Baz durumlarda bilgi kayb tr dnm yapld iin deil yaplmad iin oluur. Snr deer tamalar buna 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 long tr say snrlar iinde kalmaktadr. Oysa bilgi kayb atama ileminden nce gerekleir. 1000 ve 2000 int trden sabitlerdir, ileme sokulduklarnda arpma operatrnn de rettii deer int trden olacaktr. Bu durumda 2 byte uzunlukta olan int tr 2000000 saysn tutamayaca iin yksek anlaml byte kaybedilecektir. 2000000 hex sistemde 0x1E8480 olarak gsterilebilir. Yksek anlaml byte kaybedilince ilem sonucu 0x8480 olarak bulunur. 0x8480 negatif bir saydr. kiye tmleyenini alrsak 0x8480 1000 0100 1000 0000 ikiye tmleyeni 0111 1011 1000 0000

(0x7B80 = 31616)

Grld gibi ilem sonucu retilecek deer 31616 dir. Bu durumda x deikeninin tr long da olsa, atanacak deer 31616 olacaktr. Fonksiyon armalarnda Tr Dnm Daha nce sylendii gibi bir fonksiyona gnderilecek argumanlarla, bu argumanlar tutacak fonksiyonun parametre deikenleri arasnda tr fark varsa otomatik tr dnm gerekleecek ve argumanlarn tr, parametre deikenlerinin trlerine dntrlecektir. Ancak bu tr dnmnn gereklemesi iin, derleyicinin fonksiyon arma ifadesine gelmeden nce fonksiyonun parametre deikenlerinin trleri hakknda bilgi sahibi olmas gerekecektir. Derleyici bu bilgiyi iki ayr ekilde elde edebilir: 1. arlan fonksiyon aran fonksiyondan daha nce tanmlanmsa, derleyici fonksiyonun tanmlamasndan parametre deikenlerinin trn belirler. 2. Fonksiyonun prototip bildirimi yaplmsa derleyici parametre deikenlerinin tr hakknda nceden bilgi sahibi olur. rnek: #include <stdio.h> double func(double x, double y) { ... } int main() { int a, b; ...
133

13 funk(a, b); return 0;

Yukardaki rnekte main fonksiyonu iinde arlan func fonksiyonuna arguman olarak int trden olan a ve b deikenlerinin deerleri gnderilmitir. Fonksiyon tanmlamas arma ifadesinden nce yer ald iin int trden olan a ve b deikenlerinin deerleri, double tre dntrlerek func fonksiyonunun parametre deikenleri olan x ve y deikenlerine aktarlrlar. func fonksiyonunun main fonksiyonundan daha sonra tanmlanmas durumunda otomatik tr dnmnn yaplabilmesi iin prototip bildirimi ile derleyiciye parametre deikenlerinin trleri hakknda bilgi verilmesi gerekir. #include <stdio.h> double funk(double x, double y); int main() { int a, b; ... funk(a, b); return 0; } double funk(double x, double y) { ... }

Peki arlan fonksiyon aran fonksiyondan daha sonra tanmlanmsa, ve fonksiyon prototipi bildirimi yaplmamsa (tabi bu durumda derleme zamannda error olumamas iin fonksiyonun int trden bir geri dn deerine sahip olmas gerekecektir) tr dnm gerekleebilecek mi? Bu durumda fonksiyonun parametre deikenlerinin trleri hakknda derleyici bilgi sahibi olamayaca iin, fonksiyona gnderilen argumanlar, default arguman dnm denilen dnme tabi tutar. Default arguman dnm u ekilde olur: char ya da short trnden olan argumanlar tamsayya terfi ettirilir. (integral promotion). float trnden olan argumanlar double trne dntrlr. Bunun dndaki trlerden olan argumanlarn tr dntrlmesine tabi tutulmaz.

134

switch DEYM

14 . BLM :

switch DEYM

switch deyimi ile bir ifadenin farkl deerleri iin farkl iler yapmak mmkn hale gelir. zellikle else if yaplarna bir alternatif olarak kullanlr. Genel biimi: switch (ifade) { case ifade_1: case ifade_2: case ifade_3: ....... case ifade_n: default: } switch, case ve default anahtar szcklerdir. switch Deyiminin almas Derleyici switch parantezi ierisindeki ifadenin saysal deerini hesaplar. Bu saysal deere eit bir case ifadesi olup olmadn aratrr. Bulursa programn ak o case ifadesine geirilir. Artk program buradan akarak ilerler. Eer switch parantezi iindeki ifadenin saysal deeri hibir case ifadesine eit deilse ak default anahtar szcnn bulunduu ksma geirilir. default anahtar szcnn bulunmas zorunlu deildir. Uygun bir case ifadesi yoksa ve default anahtar szc de yoksa programn ak switch deyiminin iine girmez ve switch deyimi dndaki ilk deyim ile devam eder. rnek: #include <stdio.h> int main() { int a; scanf(%d, &a); switch (a) { case 1: printf(bir\n); case 2: printf(iki\n); case 3: printf(\ n); case 4: printf(drt\n); default: printf(hibiri\n); } return 0;

Burada unutulmamas gereken noktay tekrar edelim: Uygun bir case ifadesi bulunduunda programn ak buraya geirilir. Yalnzca bulunan case ifadesini izleyen deyim ya da deyimlerin icra edilmesi sz konusu deildir.Uygun case ifadesini izleyen deyimlerin icrasndan sonra programn ak daha aada bulunan case ifadlerini izleyen deyimlerin icra edilmesiyle devam eder.

135

14

Yukardaki rnekte scanf fonksiyonu ile a deikenine 1 atandn varsayalm. Bu durumda program kts u ekilde oluacaktr : bir iki drt hibiri Eer uygun case ifadesi bulunduunda yalnzca bu ifadeye ilikin deyim(ler)in icra edilmesini istersek break anahtar szcnden faydalanrz. break anahtar szc dnglerden olduu gibi switch deyimlerinden de kmamza olanak verir. #include <stdio.h>, int main() { int a; scanf(%d, &a); switch (a) { case 1: printf(bir\n); break; case 2: printf(iki\n); break; case 3: printf(\ n); break; case 4: printf(drt\n); break; default : printf(hibiri\n); break; } return 0; } Uygulamalarda da switch deyiminde ounlukla her case ifadesi iin bir break anahtar szcnn kullanldn grrz. (Tabi byle bir zorunluluk yok!) case ifadelerini izleyen ":" atomundan sonraki deyimler istenildii kadar uzun olabilir. case ifadelerinin sral olmas ya da default anahtar szcnn en sonda olmas gibi bir zorunluluk yoktur. default herhangi bir yere yerletirilebilir. default ifadesi nerede olursa olsun, programn ak ancak uygun bir case ifadesi bulunamazsa default iine girecektir. Yukarda da anlatld gibi switch parantezi ierisindeki ifadenin saysal deerine eit bir case ifadesi bulunana kadar derleyici derleme ynnde (yani yukardan aaya doru) tm case ifadelerini srasyla kontrol eder. Bu yzden en yksek olasla ilikin (biliniyorsa) case ifadesinin en baa (dierlerinden nce) yerletirilmesi iyi tekniktir. switch kontrol deyimi belli llerde else if (else if ladder - cascaded if) yaplaryla karlanabilir. Yani switch deyimi olmasayd yapmak istediklerimizi else if deyimi ile yapabilirdik. Ancak baz durumlarda else if yaps yerine switch deyimi kullanmak okunabilirlii artrmaktadr. rnein aadaki iki kod ilevsel olarak birbirinin ayndr: if ( a == 1) { ifade_1; ifade_2; switch (a) { case 1: ifade_1;
136

switch DEYM } else if (a == 2) { ifade_3; ifade_4; } else if (a == 4) { ifade_5; } else { ifade_6; ifade_7; } rnek : Bir tarih bilgisini (ay, gn, yl) yaz ile gg yaz yyyy (12 - Austos - 2002 gibi) biiminde ekrana yazan, prototipi void display_date(int day, int month, int year) biiminde olan fonksiyon. . #include <stdio.h> #include <conio.h> void display_date(int day, int month, int year); int main() { int d, m, y; int k; for (k = 0; k < 10; ++k) { scanf(%d%d%d, &d, &m, &y); display_date(d, m, y); putchar('\n'); } getch(); return 0; } void display_date(int day, int month, int year) { printf(%d , day); switch (month) { case 1: printf(Ocak); break; case 2: printf(Subat); break; case 3: printf(Mart); break; case 4: printf(Nisan); break; case 5: printf(Mayis); break; case 6: printf(Haziran); break; case 7: printf(Temmuz); break; case 8: printf(Agustos); break; case 9: printf(Eylul); break; case 10: printf(Ekim); break;
137

ifade_2; case 2: ifade_3; ifade_4; case 4: ifade_5; default: ifade_6; ifade_7; }

14 case 11: printf(Kasim); break; case 12: printf(Aralik); break;

} printf( %d\n, year);

Birden fazla case ifadesi iin ayn ilemlerin yaplmas yle salanabilir. case 1: case 2: case 3: ifade1; ifade2; ifade3; break; case 4: Bunu yapmann daha ksa bir yolu yoktur. Baz programclar bu yapy u ekilde de yazarlar: case 1: case 2: case 3: case 4: case 5: ifade1: ifade2;

Her switch deyimini else if yapsyla karlayabiliriz, ama her else if yapsn switch deyimiyle karlayamayz. switch deiminin parantezi iindeki ifadenin tr tamsay olmak zarundadr. case ifadeleri de tamsay sabit ifadesi olmak zarundadr. switch deyimi, tamsay trnden bir ifadenin deerinin deiik tamsay deerlerine eitliinin test edilmesi ve eitlik durumunda farkl ilerin yaplmas iin kullanlr. Oysa else if yapsnda her trl karlatrma sz konusu olabilir. rnek : if (x > 20) m = 5; else if (x > 30 && x < 55) m = 3; else if (x > 70 && x < 90) m = 7; else m = 2; Yukardaki else if yapsn switch deyimiyle karlayamayz. yle bir soru aklmza gelebilir : Madem her switch yapsn else if yapsyla karlayabiliyoruz, o zaman switch deyimine ne gerek var? Yani switch deyiminin varl C diline ne kazandryor? switch deyimi baz durumlarda else if yapsna gre ok daha okunabilir bir yap oluturmaktadr, yani switch deyiminin kullanlmas hereyden nce kodun daha kolay okunabilmesi ve anlamlandrlmas asndan bir avantaj salayacaktr. case ifadelerinin tam say trnden (integral types) sabit ifadesi olmas gerekmektedir. Bilindii gibi sabit ifadeleri derleme aamasnda derleyici tarafndan net saysal deerlere dntrlebilir.

138

switch DEYM case 1 + 3: /* legal */

mmkn nk 1 + 3 sabit ifadesi ama , case x + 5: /* error */

nk sabit ifadesi deil. Derleyici derleme aamasnda saysal bir deer hesaplayamaz. case a : case 3.5 : /* geerli, bir sabit ifadesidir. */ /* gerek say (double) trnden olduu iin geerli deildir. */

case ifadelerini izleyen deyimlerin 15 - 20 satrdan uzun olmas okunabilirlii zayflatr. Bu durumda yaplacak ilemlerin fonksiyon arma biimine dntrlmesi doru bir tekniktir. switch (x) { case ADDREC: addrec(); break; case DELREC: delrec(); break; case FINDREC: findrec(); break; } gibi. (Bu rnekte case ifadelerindeki ADDREC, DELREC vs. daha nce tanmlanm sembolik sabitlerdir.) Sembolik sabitler derleme ileminden nce nilemci tarafndan deitirilecekleri iin, case ifadelerinde yer alabilirler : #define TRUE #define FALSE #define UNDEFINED ... case TRUE: case FALSE: case UNDEFINED: Yukardaki ifadeler, sembolik sabitlerin kullanld geerli birer case ifadesidir. char trnn de bir tamsay tr olduunu hatrlatarak case ifadelerinin doal olarak karakter sabitleri de ierebileceini belirtelim. char ch; ch = getch(); switch (ch) { case E : deyim1; break;
139

1 0

14 case H : deyim2; break; default : deyim3;

Bir switch deyiminde ayn saysal deere sahip birden fazla case ifadesi olmaz. Bu durum derleme zamannda hata oluturur. switch deyimi baka bir switch deyiminin, do while ya da for deyiminin gvdesini oluturabilir. Bu durumda dardaki dngnn bloklanmasna gerek olmayacaktr, nk switch deyimi gvdeyi oluturan tek bir deyim olarak ele alnacaktr : ... for ((ch = getch()) != ESC) switch (rand() % 7 + 1) { case 1: printf("Pazartesi"); break; case 2: printf("Sal"); break; case 3: printf("aramba"); break; case 4: printf("Perembe"); break; case 5: printf("Cuma"); break; case 6: printf("Cumartesi"); break; case 7: printf("Pazar"); } Yukardaki kod parasnda switch deyimi dtaki for dngsnn gvdesini oluturmaktadr, ve switch deyimi tek bir deyim (kontrol deyimi) olarak ele alnacandan, dtaki for dngsnn bloklanmasna gerek yoktur (tabi bloklama bir hataya neden olmayacaktr.) Ancak case ifadeleri iinde yer alan break anahtar szcyle yalnzca switch deyiminden klr, for dngsnn dna kmak iin case ifadesi iinde goto anahtar szc kullanlmaldr. Uygulama Kendisine gnderilen bir tarihi ingilizce (15th Aug. 2000 ) formatnda ekrana yazan fonksiyonun. void displaydate(int day, int month, int year); #include <stdio.h> #include <conio.h> void dispdate(int day, int month, int year); int main() { int day, month, year; int n = 20; clrscr(); while (n-- > 0) { printf("gun ay yil olarak bir tarih giriniz : "); scanf("%d%d%d", &day, &month, &year); dispdate(day, month, year); putchar('\n'); } return 0;
140

switch DEYM } void dispdate(int day, int month, int year) { printf("%2d", day); switch (day) { case 1 : case 21: case 31: printf("st "); break; case 2 : case 22: printf("nd "); break; case 3 : case 23: printf("rd "); break; default : printf("th "); } switch (month) { case 1 : printf("Jan "); break; case 2 : printf("Feb "); break; case 3 : printf("Mar "); break; case 4 : printf("Apr "); break; case 5 : printf("May "); break; case 6 : printf("Jun "); break; case 7 : printf("Jul "); break; case 8 : printf("Aug "); break; case 9 : printf("Sep "); break; case 10: printf("Oct "); break; case 11: printf("Nov "); break; case 12: printf("Dec "); } printf("%d", year);

Uygulama Kendisine gnderilen bir tarihin (gn, ay, yl ) o yln kanc gn olduunu bulan fonksiyon. #include <stdio.h> #include <conio.h> int isleap(int year); int dayofyear(int day, int month, int year); int main() { int n = 20; int day, month, year; clrscr(); while (n-- > 0) {
141

14 printf("gun ay yil olarak bir tarih giriniz : "); scanf("%d%d%d", &day, &month, &year); printf("%d yilinin %d. gunudur.\n", year, dayofyear(day, month, year)); } return 0; } int isleap(int year) { return (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) } int dayofyear(int day, int month, int year) { int yearday = day; switch (month - 1) { case 11: yearday += 30; case 10: yearday += 31; case 9 : yearday += 30; case 8 : yearday += 31; case 7 : yearday += 31; case 6 : yearday += 30; case 5 : yearday += 31; case 4 : yearday += 30; case 3 : yearday += 31; case 2 : yearday += isleap(year) ? 29 : 28; case 1 : yearday += 31; } return yearday;

Uygulama 01.01.1900 31.12.2000 tarihleri arasnda geerli rasgele tarih reterek ekrana yazan fonksiyon. #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <time.h>

int isleap(int year); void randomdate(void); int main() { int n = 20; clrscr(); srand(time(NULL)); while (n-- > 0) { randomdate(); putchar('\n');
142

switch DEYM } return 0;

int isleap(int year) { if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) return year; return 0; } void randomdate(void) { int day, month, year; int monthdays; year = rand() % 101 + 1900; month = rand() % 12 + 1; switch (month) { case 4: case 6: case 9: case 11: monthdays = 30; break; case 2: monthdays = isleap(year) ? 29 : 28; break; default : monthdays = 31; } day = rand() % monthdays + 1; printf("%02d - %02d - %d", day, month, year);

Uygulama int make_char_eng(char ch); fonksiyonu. Kendisine gnderilen karakter trke'ye zel karakterlerden birisi ise (, , , vs.) bu karakterlerin ingilizce benzerine aksi halde karakterin kendisine geri dnen fonksiyon: #include <stdio.h> #include <conio.h> int make_char_eng(char ch); int main() { int ch; while ((ch = getch()) != 'q') putchar(make_char_eng(ch)); return 0;

int make_char_eng(char ch)


143

14 { switch (ch) { case '': return 'c'; case '': return 'g'; case '' : return 'i'; case '': return 'o'; case '': return 's'; case '': return 'u'; case '': return 'C'; case '': return 'G'; case '': return 'I'; case '': return 'O'; case '': return 'S'; case '': return 'U'; default : return ch; }

alma Sorusu: ki basamakl bir sayy yaz olarak ekrana basan num_text isimli fonksiyonu yaznz. Fonksiyonun prototipi : void num_text(int n); eklindedir. Fonksiyonun parametre deikeni yazlacak olan iki basamakl saydr. Aadaki programla test edebilirsiniz : #include <stdio.h> void num_text(int n); int main() { for (k = 10; k < 100; ++k) { num_text(k); putchar('\n'); getch(); } return 0; }

144

NLEMC KAVRAMI

15 . BLM :
1. nilemci Modl 2. Derleme Modl

NLEMC KAVRAMI

imdiye kadar tek bir yap halinde ele aldmz C derleyicileri aslnda, iki ayr modlden olumaktadr:

sim benzerlii olsa da nilemcinin bilgisayarn ilemcisi ya da baka bir donanmsal elemanyla hibir ilgisi yoktur, nilemci tamamen C derleyicisine ilikin yazlmsal bir kavramdr. nilemci, kaynak program zerinde birtakm dzenlemeler ve deiiklikler yapan bir n programdr. nilemci programnn bir girdisi bir de kts vardr. nilemcinin girdisi kaynak programn kendisidir. nilemcinin kts ise derleme modlnn girdisini oluturur. Yani kaynak program ilk aamada nilemci tarafndan ele alnr. nilemci modl kaynak programda eitli dzenlemeler ve deiiklikler yapar, daha sonra deitirilmi ve dzenlenmi olan bu kaynak program derleme modl tarafndan ama koda dntrlir. kaynak kod nilemci ama program

derleyici

C programlama dilinde # ile balayan btn satrlar nilemciye verilen komutlardr. # karakterinin sanda bulunan szckler ki bunlara nilemci komutlar denir (preprocessor directives), nilemciye ne yapmas gerektiini anlatr. rnein: #include #define #if #ifdef #ifndef hepsi birer nilemci komutudur. nilemci komutlar derleyici asndan anahtar szck deildir # karakteriyle birlikte anlam kazanrlar, yani istersek include isimli bir deiken tanmlayabiliriz ama bunun okunabilirlik asndan iyi bir fikir olmadn syleyebiliriz. nilemci komutlarn belirten szckler ancak sol taraflarndaki # karakteriyle kullanldklar zaman zel anlam kazanrlar ki bunlara nilemci anahtar szckleri de diyebiliriz. nilemci ama kod oluturmaya ynelik hibir i yapmaz. nilemci kaynak program # ieren satrlardan arndrr. Derleme modlne girecek programda artk # ieren satrlar yer almayacaktr. Bir ok nilemci komutu olmakla birlikte imdilik yalnzca #include komutu ve #define nilemci komutlarn inceleyeceiz. Geriye kalan nilemci komutlarn da ileride detayl olarak inceleyeceiz. #include nilemci komutu genel kullanm biimi: #include <dosya ismi> ya da

145

15

#include "dosya ismi" #include, ilgili kaynak dosyann derleme ilemine dahil edileceini anlatan bir nilemci komutudur. Bu komut ile nilemci belirtilen dosyay diskten okuyarak komutun yazl olduu yere yerletirir. (metin editrlerindeki copy paste ilemi gibi) #include komutundaki dosya ismi iki biimde belirtilebilir: 1. Asal parantezlerle: #include <stdio.h> #include <time.h> 2. ift trnak ierisinde: #include "stdio.h" #include "deneme.c" Dosya ismi eer asal parantezler iinde verilmise, szkonusu dosya nilemci tarafndan yalnzca nceden belirlenmi bir dizin ierisinde aranr. altmz 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. Genellikle standart balk dosyalar nilemci tarafndan belirlenen dizinde olduundan, asal parantezler ile kaynak koda dahil edilirler. Dosya ismi iki trnak iine yazldnda nilemci ilgili dosyay nce allan dizinde (current directory), burada bulamazsa bu kez de sistem ile belirlenen dizinde arayacaktr. rnein: C:\SAMPLE dizininde alyor olalm. #include "strfunc.h" komutu ile nilemci strfunc.h dosyasn nce C:\SAMPLE dizininde arar. Eer burada bulamazsa bu kez sistem ile belirlenen dizinde arar. Programclarn kendilerinin oluturduklar balk dosyalar genellikle sisteme ait dizinde olmadklar iin iki trnak iinde kaynak koda dahil edilirler. #include ile koda dahil edilmek istenen dosya ismi dosya yolu da (path) de ierebilir: #include <sys\stat.h> #include "c:\headers\myheader.h" .... gibi.
146

NLEMC KAVRAMI

Bu durumda asal parantez ya da iki trnak gsterimleri arasnda bir fark yoktur. Her iki gsterimde de nilemci ilgili dosyay yalnzca yolun belirttii dizinde arar. #include komutu ile yalnzca baslk dosyalarnn koda dahil edilmesi gibi bir zorunluluk yoktur. Herhangi bir kaynak dosya da bu yolla kaynak koda dahil edilebilir.

prog.c #include "topla.c" int main() { toplam = a + b; printf("%d", toplam); return 0; } topla.c int a = 10; int b = 20; int toplam; Yukardaki rnekte #include "topla.c" komutu ile nilemci komutunun bulunduu yere topla.c dosyasnn ieriini yerletirecek bylece prog.c dosyas derlendiinde herhangi bir hata olumayacaktr. nk artk derleme ilemine girecek dosya: int a = 10; int b = 20; int toplam; int main() { toplam = a + b; printf("%d", toplam); return 0; } eklinde olacaktr. #include komutu kaynak programn herhangi bir yerinde bulunabilir. Fakat standart balk dosyalar gibi, ierisinde eitli bildirimlerin bulunduu dosyalar iin en iyi yer kukusuz programn en tepesidir. #include komutu iie gemi (nested) bir biimde de bulunabilir. rnein ok sayda dosyay kaynak koda dahil etmek iin yle bir yntem izleyebiliriz. ana.c project.h
147

15

#include "project.h" int main() { ... }

#include #include #include #include

<stdio.h> <conio.h> <stdlib.h> <time.h>

ana.c dosyas ierisine yalnzca project.h dahil edilmitir. nilemci bu dosyay kaynak koda dahil ettikten sonra yoluna bu dosyadan devam edecektir. #define nilemci Komutu #define nilemci komutu text editrlerindeki bul ve deitir zellii (find & replace) gibi alr. Kaynak kod ierisindeki bir yazy baka bir yaz ile deitirmek iin kullanlr. nilemci define anahtar szcnden sonra boluklar atarak ilk boluksuz yaz kmesini elde eder. (Buna STR1 diyelim.) Daha sonra boluklar atarak satr sonuna kadar olan tm boluklarn kmesini elde eder. (Buna da STR2 diyelim) Kaynak kod ierisinde STR1 yazs yerine STR2 yazsn yerletirir. (STR2 yazs yalnzca boluktan ibaret de olabilir. Bu durumda STR1 yazs yerine boluk yerletirilir, yani STR1 yazs silinmi olur.) rnekler : #define SIZE 100

nilemci komutuyla, nilemci kaynak kod ierisinde grd tm SIZE szckleri yerine 100 yerletirecektir. Derleme modlne girecek kaynak programda artk SIZE szc hi yer almayacaktr. #define kullanlarak bir yaznn bir saysal deerle yer deitirmesinde kullanlan yazya "sembolik sabit" (symbolic constants) denir. Sembolik sabitler nesne deildir. Derleme modlne giren kaynak kodda sembolik sabitler yerine bunlarn yerini saysal ifadeler alm olur. Sembolik sabitler basit makrolar (simple makro) olarak da isimlendirirler ama biz "sembolik sabit" demeyi yeliyoruz. Sembolik sabitlerin kullanlmasnda dikkatli olunmaldr. nilemci modlnn herhangi bir ekilde aritmetik ilem yapmad yalnzca metinsel bir yer deitirme yapt unutulmamaldr: #define MAX 10 + 20 int main() { int result; result = MAX * 2; printf("%d\n", result); return 0; } Yukardaki rnekte result deikenine 50 deeri atanr. Ancak n ilemci komutunu #define MAX (10 + 20)

148

NLEMC KAVRAMI eklinde yazsaydk, result deikenine 60 deeri atanm olurdu. Bir sembolik sabit baka bir sembolik sabit tanmlamasnda kullanlabilir. rnein: #define #define MAX MIN 100 (MAX - 50)

nilemci " " ierisindeki yazlarda deiiklik yapmaz. (stringlerin blnemeyen atomlar olduunu hatrlayalm.) Yer deitirme ilemi byk kk harf duyarl ile yaplr. Ancak sembolik sabitler geleneksel olarak byk harf ile isimlendirilirler. Bunun nedeni, kodu okuyan kiinin deikenlerle sembolik sabitleri ayrt edebilmesine yardmc olmaktr. Bilindii gibi geleneksel olarak deiken isimlendirmesinde C dilinde arlkl olarak kk harfler kullanlmaktadr. #define komutu ile ancak deikenler ve anahtar szckler yer deitirilebilir. Sabitler ve operatrler yer deitiremez. Aadaki #define nilemci komutlar geerli deildir: #define #define + 100 200

Sembolik sabitler C dilinin deiken isimlendirme kurallarna uygun olarak isimlendirilir: #define BYK 10

tanmlamasnda hata oluur. (Hata tanmlama satrnda deil sembolik sabitin kod iinde kullanld yerlerde oluur. Tanmlanan sembolik sabit kod iinde kullanlmazsa hata da olumayacaktr.) nilemci #include komudu ile kaynak koda dahil edilen dosyann ierisindeki nilemci komutlarn da altrr. inde semboli sabit tanmlamalar yaplm bir dosya #include komudu ile kaynak koda dahil edildiinde, bu sembolik sabitler de kaynak kod iinde tanmlanm gibi geerli olur. #define sembolik sabit tanmlamalarnda stringler de kullanlabilir : #define HATA_MESAJI "DOSYA AILAMIYOR \n"

... printf(HATA_MESAJI); ... Sembolik sabit tanmlamasnda kullanlacak string uzunsa okunabilirlik asndan birden fazla satra yerletirilebilir. Bu durumda son satr dnda dier satrlarn sonuna "\" karakteri yerletirilmelidir. nilemci deitirme ilemini yaptktan sonra #define komutlarn koddan kartr. Okunabilirlik asndan tm sembolik sabit tanmlamalar alt alta getirecek ekilde yazlmaldr. Seilen sembolik sabit isimleri kodu okuyan kiiye bunlarn ne amala kullanld hakknda fikir vermelidir. C'nin balk dosyalarnda baz sembolik sabitler tanmlanmtr. rnein stdio.h ierisinde #define NULL 0

149

15 biiminde bir satr vardr. Yani stdio.h dosyas koda dahil edilirse NULL sembolik sabiti 0 yerine kullanlabilir. math.h iinde de pek ok matematiksel sabit tanmlanmtr. Bir sembolik sabitin tanmlanm olmas kaynak kod ierisinde deitirilebilecek bir bilginin olmasn zorunlu hale getirmez. Tanmlanm bir sembolik sabitin kaynak kod ierisinde kullanlmamas hataya yol amaz. #define nilemci Komutu Neden Kullanlr 1. Okunabilirlii ve algnalabilirlii artrr. Bir takm sabitlere onlarn ne amala kullanldn anlatan yazlar karlk getirilirse programa bakan kiiler daha iyi anlamlandrr. #define PERSONEL_SAYISI 750 int main() { if (x == PERSONEL_SAYISI) ... return 0; } 2. Bir sabitin program ierisinde pekok yerde kullanld durumlarda deitirme ilemi tek yerden yaplabilir. Bylece sz konusu program sembolik sabite bagl olarak yazlp, daha sonra sembolik sabitin deitirilmesiyle farkl parametrik deerler iin altrlabilir. 3. Saysal sabitlerin kullanlmasnda tutarszlklar ve yazm yanllarn engeller. rnein matematiksel hesaplamalar yapan bir kodda sksk pi saysn kullandmz dnelim. pi says yerine #define PI 3.14159

sembolik sabitini kullanabiliriz. Her defasnda pi saysnn sabit olarak koda girersek, her defasnda ayn deeri yazamayabiliriz. rnein bazen 3.14159 bazen 3.14156 girebileceimiz gibi yanllkla 3.15159 da girebiliriz. Sembolik sabit kullanm bu tr hatalar ortadan kaldrr. 4. Sembolik sabitler kullanlarak C sintaksnda kk deiiklikler yaplabilir. #define #define #define FOREVER BEGIN END } for(;;) {

Byle bir sembolik sabit kullanmn kesinlikle tavsiye etmiyoruz. Okunabilirlii artrmas deil azaltmas sz konusudur. Bir C programcs iin en okunabilir kod C'nin kendi sentaksnn kullanld koddur. #define komutu kaynak kodun herhangi bir yerinde kullanlabilir. Ancak tanmland yerden kaynak kodun sonuna kadar olan blge ierisinde etki gsterir. Ancak en iyi tanmlama yeri kaynak kodun tepesidir. Geleneksel olarak sembolik sabitler #include satrlarndan (bir satr boluk verildikten sonra) hemen sonra yer alrlar.

150

NLEMC KAVRAMI Sembolik Sabitlerin Kullanlmasnda ok Yaplan Hatalar 1. Sembolik sabit tanmlamasnda gereksiz yere = karakterini kullanmak. #define N = 100 /* yanl */

Bu durumda nilemci N grd yere = 100 yaptracaktr. rnein int a[N] gibi bir dizi tanmlanmsa nilemci bu tanmlamay a[N = 100] yapar ki bu da derleme aamasnda hata (error) ile neticelenir. 2. Sembolik sabit tanmlama satrn ; ile sonlandrmak. #define N 100; /* yanl */

Bu durumda nilemci N grd yere 100; yerletirir. int a[N]; tanmlamas int a[100;] haline gelir. Derleme aamasnda hata oluur. (Borland derleyicileri hata mesaj :Array bounds missing ])Bu tr hatalarda derleyici sembolik sabit ka yerde kullanlmsa o kadar error verecektir. 3. Sembolik sabitlerle yer deitirecek ifadelerin operatr iermesi durumunda bu ifadeleri paranteze almadan yerletirmek. #define ... SIZE 5 + 10

i = SIZE * 5 ...

/* 5 + 10 * 5 = 55 (75 deil) */

151

16

16 . BLM :

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 yaplmaktadr. goto deyiminin genel sentaks aadaki ekildedir. <goto label;> .... <label:> <statement;> goto C dilinin 32 anahtar szcnden biridir. (anahtar szcklerin kk harfle yazldklarn tekrar hatrlatalm.) Label (etiket) programcnn verdii bir isimdir. phesiz C dilinin isimlendirme kurallarna uygun olarak seilmelidir. Programn ak bu etiketin yerletirilmi olduu yere ynlendirilecektir. Etiket, goto anahtar szcnn kullanld fonksiyon iinde, en az bir deimden nce olaak ekilde, herhangi bir yere yerletirilebilir. Etiket isminden sonra gelen : atomu sentaks tamamlar. Etiketin goto anahtar szcnden daha sonraki bir kaynak kod noktasna yerletirilmesi zorunluluu yoktur, goto anahtar szcnden nce de tanmlanm olabilir. rneklerle aklayalm: int main() { ... goto GIT; ... ... GIT: printf("goto deyimi ile buraya gelindi); return 0; }

int main() { GIT: printf("goto deyimi ile gelinecek nokta); ... goto GIT; ... return 0; } goto etiketleri bir fonksiyon ierisinde, bir deyimden nce herhangi bir yere yerletirilebilir. Yani ayn fonksiyon iinde bulunmak kaydyla goto anahtar szcnn yukarsna ya da aasna etiketi yerletirebiliriz. Bu da aslnda deiik bir faaliyet alan tipidir. C dili standartlarnda bu faaliyet alan trne "fonksiyon faaliyet alan" denmitir. Yapsal programlama tekniinde goto deyiminin kullanlmas tavsiye edilmez. nk goto deyiminin kullanlmas birtakm dezavantajlar dourur:

152

goto DEYM 1. goto deyimi programlarn okunabilirliini bozar. Kodu takip eden kii goto deyimiyle karlatnda fonksiyonun iinde etiketi arayacak, ve program bu noktadan takip etmeye koyulacaktr. 2. goto deyimlerinin kullanld bir programda bir deiiklik yaplmas ya da programn, yaplacak eklemelerle, gelitirilmeye allmas daha zor olacaktr. Programn herhangi bir yerinde bir deiiklik yaplmas durumunda, eer program ierisinde baka yerlerden kodun ak deiikliin yapld yere goto deyimleri ile atlatlm ise, bu noktalarda da bir deiiklik yaplmas gerekebilerecektir. Bu olumsuzluklara karn, baz durumlarda goto deyiminin kullanlmas programn okunabilirliini bozmak bir yana, dier alternatiflere gre daha okunabilir bir yapnn olumasna yardmc olacaktr: ie birden fazla dng varsa, ve iteki dnglerden birindeyken, yalnzca bu dngden deil, btn dnglerden birden klmak isteniyorsa goto deyimi kullanlmaldr. Aadaki kod parasnda iie 3 dng bulunmaktadr. En iteki dngnn iinde func fonksiyonu arlarak fonksiyonun geri dn deeri test edilmekte, fonksiyon eer 0 deerine geri dnyorsa programn ak goto deyimiyle tm dnglerin dna ynlendirilmektedir: int i, j, k; ... for (i = 0; i < 100; ++i) { ... for (j = 0; j < 100; ++j) { ... for (k = 0 { ... if (!func()) goto BREAK; ... } } } BREAK: printf("dng dndaki ilk deyim\n"); ... Oysa goto deyimini kullanmasaydk, ancak bir bayrak (flag) kullanarak, ve her dngnn knda bayrak olarak kullanlan deikenin deerinin deitirilip deitirilmediini kontrol ederek bunu baarabilirdik: int i, j, k; int flag = 0; ... for (i = 0; i < 100; ++i) { ... for (j = 0; j < 100; ++j) { ... for (k = 0; k < 100; ++k) { ...
153

16 if (!func()) { flag = 1; break; } ... } if (flag) break;

} if (flag) break;

} printf("dng dndaki ilk deyim\n"); ... Yine aadaki kod parasnda goto deyimiyle hem switch deyiminden hem de switch deyiminin iinde bulunduu for dngsnden klmaktadr. ... int main() { int option; for (;;) { option = get_option(); switch (option) { case ADDREC :addrec();break; case LISTREC :listrec();break; case DELREC :delrec(); break; case SORTREC :sortrec(); break; case EXITPROG :goto EXIT; } } EXIT: return SUCCESS; } Yukardaki kod parasnda option deikeninin deeri EXITPROG olduunda programn ak goto deyimiyle sonsuz dngnn dna gnderilmitir. goto deyimi yerine break anahtar szc kullanlsayd, yalnzca switch deyiminden klm olunacakt.

154

RASTGELE SAYI RETM

17 . BLM :

RASTGELE SAYI RETM

Rasgele say retimi matematiin nemli konularndan biridir. Rasgele saylar ya da daha doru ifadeyle, rasgele izlenimi veren saylar (szde rasgele saylar - pseudo random numbers) istatistik, ekonomi, matematik gibi pekok alanda olduu gibi programclkta da kullanlmaktadr. Rasgele saylar bir rasgele say reticisi (random number generator) tarafndan retilirler. Rasgele say reticisi aslnda matematiksel bir fonksiyondur. Sz konusu fonksiyon bir balang deeri alarak bir deer retir. Daha sonra rettii her deeri girdi olarak alr ve tekrar baka bir say retir . reticinin rettii saylar rasgele izlenimi vermektedir. C standartlar rasgele tamsay reten bir fonskiyonun standart bir C fonksiyonu olarak tanmlanmasn zorunlu klmtr. Bu fonksiyonun prototip bildirimi aadaki gibidir: int rand(void); C standartlar rand fonksiyonunun rasgele say retimi konusunda kullanaca algoritma ya da teknik zerinde bir koul koymamtr. Bu konu derleyiciyi yazanlarn seimine bal (implementation dependent) braklmtr. rand fonksiyonunun prototipi standart bir balk dosyas olan stdlib.h iinde bildirilmitir. Bu yzden rand fonksiyonunun arlmas durumunda bu balk dosyas "include" nilemci komuduyla kaynak koda dahil edilmelidir. #include <stdlib.h> rand fonksiyonu her arldnda [0, RAND_MAX] aralnda rasgele bir tamsay deerini geri dndrr. RAND_MAX stdlib.h balk dosyas iinde tanmlanan bir sembolik sabittir, derleyicilerin ounda RAND_MAX sembolik sabiti 32767 olarak, yani 2 byte'lk signed int trnn maximum deeri olarak tenmlanmtr. Aadaki program parasnda 0 ile 32767 arasnda 10 adet rasgele say retilerek ekrana yazdrlmaktadr : #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 yazlacaktr. rnein yukardaki program DOS altnda Borland Turbo C 2.0 derleyicisi ile derleyip altrdmzda ekran kts aadaki gibi oldu:

155

16 346 130 10982 1090 11656 7117 17595 6415 22948 31126 Oluturulan program her altrldnda neden hep ayn say zinciri elde ediliyor? rand fonksiyonu rasgele say retmek iin bir algoritma kullanyor. Bu algoritma derleyiciden derleyiciye deisede, rasgele say retiminde kullanlan ana tema ayndr. Bir balang deeri ile ie balanr. Buna tohum deer diyoruz. Bu deer zerinde baz ilemler yaplarak rasgele bir say elde edilir. Tohum deer zerinde yaplan ilem bu kez elde edilen rasgele say zerinde tekrarlanr... rand fonksiyonu rasgele say retmek iin bir balang deeri kullanyor. Rasgele say reticilerinin (random number generator) kullandklar balang deerine tohum deeri (seed) denir. rand fonksiyonunu ieren program her altrdmzda ayn tohum deerinden balayaca iin ayn say zinciri elde edilecektir. te ikinci fonksiyon olan srand fonksiyonu, rasgele say reticisinin tohum deerini deitirmeye yarar: void srand (unsigned seed); srand fonksiyonuna gnderilen arguman rasgele say reticisinin tohum deerini deitir. srand() fonksiyonuna baka bir tohum deeri gnderdiimizde fonksiyonun rettii rasgele say zinciri deiecektir. Yukardaki programa bir ilave yaparak yeniden altrn. #include <stdio.h> #include <stdlib.h> int main() { int k; srand(100); for (k = 0; k < 10; ++k) printf("%d ", rand()); return 0; } 1862 11548 3973 4846 9095 16503 6335 13684 21357 21505 Ancak bu kez oluturduumuz program her eltrdmzda yine yukardaki say zinciri elde edilecek. Zira rand fonksiyonunun kullanmakta olduu nceden seilmi (default) tohum deerini kullanmasak da, rasgele say retme mekanizmas bu kez her defasnda bizim srand fonksiyonuyla gndermi olduumuz tohum deerini kullanacak. Program birka kere altrp ayn say zincirini rettiini test edin. Bir ok durumda programn her altrlmasnda ayn rasgele say zincirinin retilmesi istenmez. Programn her almasnda farkl bir say zincirinin elde edilmesi iin programn her almasnda srand fonksiyonu baka bir deerle, rasgele say reticisinin tohum deerini set etmelidir. Bu amala ou zaman time fonksiyonundan faydalanlr: time fonksiyonu standart bir C fonksiyonudur, prototip bildirimi standart bir balk dosyas olan time.h iindedir. time fonksiyonu, parametre deikeni gsterici olan bir fonksiyon olduundan ileride detayl olarak ele alnacaktr. imdilik time fonksiyonunu iimizi grecek kadar inceleyeceiz. time fonksiyonu 0 argumanyla arldnda 01.01.1970 tarihinden fonksiyonun arld ana kadar geen saniye saysn geri dndrr. Fonksiyonun geri dn deeri derleyicilerin ounda long trden bir deerdir. inde rasgele say retilecek programda, srand fonksiyonuna arguman olarak time fonksiyonunun geri dn

156

RASTGELE SAYI RETM deeri arguman olarak gnderilirse, program her altnda rasgele say reticisi baka bir tohum deeriyle ilk deerini alacaktr, byle progarmn her altrlmasnda farkl say zinciri retilecektir. srand(time(0)); srand fonksiyonunun bu ekilde arm derleyicilerin ounda randomize isimli bir makro olarak tanmlanmtr. Yukardaki fonksiyon arm yerinde bu makro da arlabilir. Makrolar konusunu ileride detayl olarak inceeyeceiz : randomize(); Yukardaki rnek program her altnda farkl say zinciri retecek hale 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 rasgele say retmek isteyebiliriz. Bu durumda mod operatr kullanlabilir : rand() rand() rand() rand() % % % % 2 Yalnzca 0 ya da 1 deerini retir. 6 0 - 5 aralnda rasgele bir deer retir 6 + 1 1 - 6 aralnda rasgele bir deer retir. 6+3 5 - 8 aralnda rasgele bir deer retir.

Karmak olaslk problemleri, olsla konu olayn bir bilgisayar program ile simule edilemesi yoluyla zlebilir. yi bir rasgele say reticisi kullanld takdirde, olasla konu olay bir bilgisayar program ile simule edilir ve olay bilgisayarn ilem yapma hzndan faydanlanlarak yksek saylarda tekrar ettirilir. phesiz hesap edilmek istenen olaya ilikin olaslk deeri, yaplan tekrar saysna ve rasgele say reticisinin kalitesine bal olacaktr. Aadaki kod yaz tura atlmas olaynda tura gelme olasln hesap etmektedir. #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <time.h> 100 1

#define TIMES #define HEADS int main() {

157

16 long heads_counter = 0; long k; for (k = 0; k < TIMES; ++k) if (rand() % 2 == HEADS) heads_counter++; printf("tura gelme olasl = %lf", (double) heads_counter / TIMES); getch(); return 0;

Yukardaki program TIMES sembolik sabitinin farkl deerleri iin altrdmzda ekran kts aadaki ekilde oldu: #define TIMES 100 tura gelme olasl = 0.480000 #define TIMES 500 tura gelme olasl = 0.496000 #define TIMES 2500 tura gelme olasl = 0.506800 #define TIMES 10000 tura gelme olasl = 0.503500 #define TIMES 30000 tura gelme olasl = 0.502933 #define TIMES 100000 tura gelme olasl = 0.501450 #define TIMES 1000000 tura gelme olasl = 0.500198 #define TIMES 10000000 tura gelme olasl = 0.500015 #define TIMES 2000000 tura gelme olasl = 0.500198 Aadaki main fonksiyonunda uzunluklar 3 - 8 harf arasnda deien ingiliz alfabesindeki harfler ile oluturulmu rasgele 10 kelime ekrana yazdrlmaktadr: #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <time.h> 20

#define TIMES void write_word(void); int main() { int k;

srand(time(0)); for (k = 0; k < TIMES; ++k) { write_word();


158

RASTGELE SAYI RETM putchar('_); } getch(); return 0;

void write_word(void) { int len = rand() % 6 + 3; while (len--) putchar('A' + rand() % 26);

Programn ekran kts: USPFY KDUP BUQJB LMU PKPBKFA WQM NQHPTXL QCANSTEH XZU MNRI OXUTGISR XBNG BYOQ TFO MQUCSU OIHFPJ BLVD WDDEM OHMHJBZY ALIAQ

159

18

18 . BLM :

DZLER

Bellekte ardl bir biimde bulunan ve ayn trden nesnelerin oluturduu veri yapsna dizi (array) denir. Dizilerin kullanlmasn gerektiren iki nemli zellikleri vardr: 1. Dizi elemanlar bellekte ardl (contiguous) olarak bulunurlar. 2. Dizi elemanlar ayn trden nesnelerdir. Diziler bileik nesnelerdir. Yani bir dizinin tanmlanmasyla birden fazla sayda nesne birlikte tanmlanabilir. (Bileik szc ingilizce "aggregate" szcnn karl olarak kullanlmtr.) 10 elemanlk bir dizi tanmlamak yerine, phesiz farkl isimlere sahip 10 ayr nesne de tanmlanabilir. Ama 10 ayr nesne tanmlandnda bu nesnelerin bellekte ardl olarak yerlemeleri garanti altna alnm bir zellik deildir. Oysa dizi tanmlamasnda, dizinin eleman olan btn nesnelerin bellekte ardl olarak yer almalar garanti altna alnm bir zelliktir. Dizilerde bir veri tr olduuna gre dizilerin de kullanlmalarndan nce tanmlanmalar gerekir. Dizilerin Tanmlanmas Dizi tanmlamalarnn genel biimi: <tr> <dizi ismi> [<eleman says>]; Yukaridaki gsterimde keli parantez eleman saysnn seimlik olduunu deil, eleman says bilgisinin keli parantez iine yazlmas gerektiini gstermektedir. tr : Dizi elemanlarnn trn gsteren anahtar szcktr. dizi ismi : simlendirme kurallarna uygun olarak verilecek herhangi bir isimdir. eleman says : Dizinin ka elemana sahip olduunu gsterir. rnek Dizi Bildirimleri: double a[20]; /* a, 20 elemanl ve elemanlar double trden olan bir dizidir*/ float ave[10]; /* ave 10 elemanl ve her eleman float trden olan bir dizidir. */ unsigned long total[100]; /* total 100 elemanl ve her eleman unsigned long trden olan bir dizidir */ char path[80]; /* path 80 elemanl ve her eleman char trden olan bir dizidir. */ Tanmlamada yer alan eleman saysnn mutlaka tamsay trlerinden birinden sabit ifadesi olmas zorunludur. (Sabit ifadesi [constant expression] tanmn hatrlayalm; deiken ve fonksiyon arm iermeyen, yani yalnzca sabitlerden oluan ifadelere, sabit ifadesi denir.) int dizi[x]; int dizi[5.]; /* x dizisinin bildirimi derleme zamannda hata olusturur .*/ /* gerek say trnden sabit ifadesi olduu iin derleme zamannda hata olusturur . */ /* sample dizisinin bildirimi geerlidir. Eleman saysn gsteren ifade sabit ifadesidir. */

int sample[10 * 20]

Dizi bildirimlerinde eleman says yerine sklkla sembolik sabitler kullanlr: #define ... MAXSIZE 100

160

DZLER int dizi[MAXSIZE]; ... /* geerli bir bildirimdir */

Dier deiken bildirimlerinde olduu gibi, virgl ayracyla ayrlarak, birden fazla dizi tek bir tr belirten anahtar szckle tanmlanabilir. int x[100], y[50], z[10]; x, y ve z elemanlar int trden olan dizilerdir. Dizi tanmlamalar dier deiken tanmlamalar ile kombine edilebilir. int a[10], b, c; a int trden 10 elemanl bir dizi, b ve c int trden nesnelerdir. Dizi elemanlarnn her biri ayr birer nesnedir. Dizi elemanlarna index operatryle [] ulalabilir. Index operatr bir gsterici operatrdr. Gstericiler konusunda ayrntl bir ekilde ele alnacaktr. ndex operatrnn operand dizi ismidir. (Aslnda bu bir adres bilgisidir, nk dizi isimleri adres bilgisi belirtirler.) Keli parantez iinde dizinin kanc indisli elemanna ulaacamz gsteren bir tamsay ifadesi olmaldr. C dilinde dizilerin ilk eleman sfrnc indisli elemandr. a[n] gibi bir dizinin ilk eleman a[0] son eleman ise a[n - 1] dur. rnekler: dizi[20] /* a dizisinin 20. indisli yani 21. sradaki eleman. */ ave[0] /* ave dizisinin 0. indisli yani birinci sradaki eleman */ total[j] /* total dizisinin j indisli eleman */ Grld gibi bir dizinin n. eleman ve bir dizinin n indisli eleman terimleri dizinin farkl elemanlarna iaret eder. Bir dizinin n indisli eleman o dizinin n + 1 . elemandr. Bir dizi tanmlamas ile karlaan derleyici, tanmlanan dizi iin bellekte yer tahsis edecektir..Ayrlacak yer phesiz dizinin eleman says * bir elemann bellekte kaplad yer kadar byte olacaktr. rnein: int a[5]; gibi bir dizi tanmlamas yapldn dnelim. DOS iletim sisteminde alyorsak, derleyici a dizisi iin bellekte 2 * 5 = 10 byte yer ayracaktr. Bellekte bu dizinin yerleimi aadaki gibi olacaktr : ... ------------------------------------------------------------... a[0] a[1] a[2] a[3] a[4] ...

10 byte

161

18

Dizi kullanmnn getirdii en byk avantaj dng deyimleri kullanarak tm dizi elemanlarn kolay bir ekilde eriilebilmesidir. Dizi indis ifadelerinde ++ ya da operatrlerinin kullanld sk grlr. int a[20]; int k = 10; int i =5; a[k++] = 100; /* a dizisinin 10 indisli elemanna yani 11. elemanna 100 deeri atanyor. */ a[--i] = 200; /* a dizisinin 4 indisli elemanna yani 5. elemanna 200 deeri atanyor. */ ndex operatrnn kullanlmasyla artk dizinin herhangi bir eleman dier deikenler gibi kullanlabilir. rnekler: a[0] = 1; /* a dizisinin ilk elemanna 0 deeri atanyor. */ printf(%d\n, sample[5]); /* sample dizisinin 6. eleman yazdrlyor. */ ++val[3]; /* val dizisinin 4. eleman 1 artrlyor. */ val[2] = sample[2]; /* val dizisinin 3. elemanna sample dizisinin 3. eleman atanyor. */ Diziler zerinde ilem yapmak iin sklkla for ve while dngleri kullanlr. Aada SIZE elemanl ve sample isimli bir dizi iin for ve while dnglerinin kullanld baz kalplar gsterilmektedir: for (i = 0; i < SIZE; ++i) sample[i] = 0; i = 0; while (i < SIZE) { sample[i] = 0; ++i; } /* sample dizisinin btn elemanlar sfrlanyor */

for (i = 0; i < SIZE; i++) { scanf(%d, &sample[i]); }

i = 0; while (i < SIZE) { scanf(%d, &sample[i]); ++i; } /* sample dizisinin elemanlarna scanf fonksiyonuyla klavyeden deer aliniyor.*/

for (i = 0; i < SIZE; i++) total += sample[i];

162

DZLER

i = 0; while (i < SIZE) { total += sample[i]; ++i; } /* sample dizisinin elemanlar toplaniyor. */ Bir dizi tanmlamas yapld zaman derleyici tanmlamas yaplm dizi iin bellekte toplam dizi uzunluu kadar yer ayrr. rnein : double a[10]; gibi bir tanmlama yapldnda dizi iin bellekte ardl (contigious) toplam 80 bytelk bir yer ayrlacaktr. Dizinin son eleman a[9] olacaktr. ok sk yaplan bir hata, dizinin son eleman diye bellekte derleyici tarafndan tahsis edilmemi bir yere deer atamaktr. a[10] = 5.; deyimiyle bellekte rasgele 8 bytelk bir alana (gvenli olmayan) bir yere 5. deeri yazlmaktadr. Dizilere ilk deer verilmesi (array initialization) Dizilere ilk deer verme ileminin genel ekli aadaki gibidir : <tr> <dizi ismi>[[uzunluk]] = {d1, d2, d3........}; rnekler : double sample[5] = {1.3, 2.5, 3.5, 5.8, 6.0}; char str[4] = {d, i, z, i}; unsigned[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Dizilere yukardaki gibi ilk deer verildiinde, verilen deerler dizinin ilk elemanndan balayarak dizi elemanlarna srayla atanm olur. Dizilerin tm elemanlarna ilk deer verme zorunluluu yoktur. Dizinin eleman saysndan daha az sayda elemana ilk deer verilmesi durumunda kalan elemanlara otomatik olarak 0 deeri atanm olur. Bu kural hem yerel hem de global diziler iin geerlidir. Bu durumda btn dizi elemanlarna 0 deeri atanmak isteniyorsa bunun en ksa yolu : int a[20] = {0}; yalnzca dizinin ilk elemanna 0 deeri vermektir. Bu durumda derleyici dizinin kalan elemanlarna otomatik olarak 0 deeri atayacaktr. Dizi elemanlarna ilk deer verilmesinde kullanlan ifadeler, sabit ifadeleri olmaldr. int a[10] = {b, b + 1, b + 2};

163

18 gibi bir ilk deer verme ilemi derleme zaman hatas oluturur. Bir diziye ilk deer verme ileminde dizi eleman saysndan daha fazla sayda ilk deer vermek derleme zamannda hata oluumuna neden olur : int b[5] = {1, 2, 3, 4, 5, 6}; /* error */ yukardaki rnekte b dizisi 5 elemanl olmasna karn, ilk deer verme ifadesinde 6 deer kullanlmtr. dizi elemanlarna ilk deer verme ileminde dizi uzunluu belirtilmeyebilir, bu durumda derleyici dizi uzunluunu verilen ilk deerleri sayarak kendi hesaplar ve dizinin o uzunlukta aldn kabul eder. rnein : int sample[] = {1, 2, 3, 4, 5}; derleyici yukardaki ifadeyi grdnde sample dizisinin 5 elemanl olduunu kabul edecektir. Bu durumda yukardaki gibi bir bildirimle aadaki gibi bir bildirim edeer olacaktr: int sample[5] = {1, 2, 3, 4, 5}; baka rnekler : char name[ ] = {N, e, c, a, t, i , E, r, g, i, n, \0}; unsigned short count[ ] = {1, 4, 5, 7, 8, 9, 12, 15, 13, 21}; derleyici name dizisinin uzunluunu 13, count dizisinin uzunluunu ise 10 olarak varsayacaktr. yerel ve global diziler Bir dizi de, dier nesneler gibi yerel ya da global olabilir. Yerel diziler bloklarn ilerinde tanmlanan dizilerdir. Global diziler ise tm bloklarn dnda tanmlanrlar. Global bir dizinin tm elemanlar global nesnelerin zelliklerine sahip olacaktr. Yani dizi global ise dizi eleman olan nesneler dosya faaliyet alanna (file scope) sahip olacak, ve mr karakteri asndan da statik mrl (static storage duration) olacaklardr. Global bir dizi sz konusu olduunda eer dizi elemanlarna deer verilmemise, dizi elemanlar 0 deeriyle balatlacaktr. Ama yerel diziler sz konusu olduunda, dizi eleman olan nesneler blok faaliyet alanna (block scope) sahip olacaklar, mr asndan ise dinamik mr karakterinde olacaklardr. (dynamic storage class) Deer atanmam dizi elemanlar iinde rasgele deerler (garbage values) bulunacaktr. Aadaki rnei yazarak derleyicinizde yazarak deneyiniz : #include <stdio.h> int global[10]; main() { int yerel[10], i; for (i = 0; i < 10; ++i) printf(global[%d] = %d\n, i, global[i]); for (i = 0; i < 10; ++i) printf(yerel[%d] = %d\n, i, yerel[i]);
164

DZLER } dizilerin birbirine atanmas int a[SIZE], b[SIZE]; tanmlamasndan sonra, a dizisi elemanlarna b dizisinin elemanlar kopyalanmak istenirse a = b; /* hata */

yukardaki gibi bir atama derleme zaman hatas verecektir. nk a ve b dizi isimleridir. C dilinde dizi isimleri nesne gstermezler. Dizi isimleri dizilerin bellekte yerletirildikleri alann balangcn gsteren ve dizinin tr ile ayn trden adres deerleridir. (Dolaysyla sabit deerlerdir.) Dizileri ancak bir dng kullanarak kopyalayabiliriz : for (i = 0; i < SIZE; ++i) a[i] = b[i]; Yukardaki dng deyimiyle b dizisinin her bir elemannn deeri a dizisinin ilgili indisli elemanna atanmtr. Dizilerin kopyalanmas iin baka bir yntem de bir standart C fonksiyonu olan memcpy (memory copy) fonksiyonunu kullanmaktr. Bu fonksiyon gstericiler konusundan sonra ele alnacaktr. dizilerle ilgili uygulama rnekleri: Dizilerin kullanlmasn tevik eden temel nedenlerden biri ayn trden nesnelerin bir dizi altnda tanmlanmasyla, dngler yardmyla dizi elemanlarnn ok kolay bir ekilde ileme tutulabilmesidir. Aadaki rneklerde bu temalar ilenmitir : Uygulama 1 : Bir dizi elemanlarnn toplamnn bulunmas #include <stdio.h> #include <conio.h> #define SIZE 10

main() { int dizi[SIZE] = {12, 25, -34, 45, 67, 89, 24, -2, -15, 40}; int toplam = 0; int k; clrscr(); for (k = 0; k < SIZE; ++k) toplam += dizi[k]; printf("dizi elemanlar toplam = %d\n", toplam); return 0;

Uygulama 2 : bir dizi iindeki en kk sayy bulan program :

165

18 #include <stdio.h> #include <conio.h> #define SIZE 10

main() { int dizi[SIZE] = {12, 25, -34, 45, 67, 89, 24, -2, -15, 40}; int min, k; clrscr(); min = dizi[0]; for (k = 1; k < SIZE; ++k) if (min > dizi[k]) min = dizi[k]; printf("en kk eleman = %d\n", min); return 0;

eer en kk elemann kendisiyle birlikte, dizinin kanc eleman olduu da bulunmak istenseydi : int indis; ... for (k = 1; k < SIZE; ++k) if (min > dizi[k]) { min = dizi[k]; indis = k; /* en kk eleman yenilendike indisi baka bir deikende saklanyor. */ } Uygulama 3: Bir diziye klavyeden deer alarak daha sonra dizi elemanlarn ekrana yazdran program : #include <stdio.h> #include <conio.h> #define SIZE main() { int dizi[SIZE]; int k; clrscr(); for (k = 0; k < SIZE; ++k) scanf("%d", &dizi[k]); for (k = 0; k < SIZE; ++k) printf("dizi[%d] = %d\n", k, dizi[k]); return 0; 10

Uygulama 4 : Bir dizi ierisindeki elemanlar kkten bye doru bubble sort algoritmasyla sraya dizen program:
166

DZLER

#include <stdio.h> #include <conio.h> #define SIZE 10

main() { int dizi[SIZE] = {12, 25, -34, 45, -23, 29, 12, 90, 1, 20}; int i, k, temp; clrscr(); for (i = 0; i < SIZE - 1; ++k) for (k = 0; k < SIZE - 1; ++k) if (dizi[k] > dizi[k + 1]) { temp = dizi[k]; dizi[k] = dizi[k + 1]; dizi[k + 1] = temp; } for (i = 0; i < SIZE; ++i) printf("dizi[%d] = %d\n", k, dizi[k]); return 0;

Bu algoritmann performans basit bir dzenleme ile artrlabilir. Dtaki dngnn her dnnde en byk say en sona gidecektir. Bu durumda iteki dngnn her defasnda SIZE 1 kez dnmesine gerek yoktur. teki dng SIZE 1 i kez dnebilir. ... for (i = 0; k < SIZE - 1; ++k) for (k = 0; k < SIZE - 1; ++k) ... Bubble sort algoritmasn sraya dizme ilemi tamamlandnda dtaki dngy sonlandracak biimde dzenleyebiliriz : #include <stdio.h> #include <conio.h> #define SIZE #define UNSORTED #define SORTED 10 0 1

main() { int dizi[SIZE] = {12, 25, -34, 45, -23, 29, 12, 90, 1, 20}; int i, k, temp, flag; for (i = 0; i < SIZE - 1; ++k) { flag = SORTED; for (k = 0; k < SIZE - 1; ++k) if (dizi[k] > dizi[k + 1]) { temp = dizi[k];
167

18 dizi[k] = dizi[k + 1]; dizi[k + 1] = temp; flag = UNSORTED; } if (flag == SORTED) break;

} for (i = 0; i < SIZE; ++i) printf("dizi[%d] = %d\n", i, dizi[i]); return 0;

bu yeni dzenleme dierine gre daha iyi de olsa performans asndan ok ciddi bir farkll yoktur. Ayn algoritmay bir do while dngs kullanarak da yazabilirdik: #include <stdio.h> #include <conio.h> #define SIZE #define UNSORTED #define SORTED 10 1

main() { int dizi[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 (dizi[k] > dizi[k + 1]) { temp = dizi[k]; dizi[k] = dizi[k + 1]; dizi[k + 1] = temp; flag = UNSORTED; } } while (flag == UNSORTED); for (i = 0; i < SIZE; ++i) printf("dizi[%d] = %d\n", i, dizi[i]); return 0;

Uygulama 5: Bir dizi ierisindeki elemanlar kkten bye insertion sort algoritmasyla sraya dizen program: #include <stdio.h> #include <conio.h> #define SIZE 10
168

DZLER

main() { int dizi[SIZE] = {12, 25, -34, 45, -23, 29, 12, 90, 1, 20}; int i, j, temp; clrscr(); for (i = 1; i < SIZE; ++i) { temp = dizi[i]; for (j = i; j > 0 && dizi[j - 1] > temp; --j) dizi[j] = dizi[j - 1]; dizi[j] = temp; } for (i = 0; i < SIZE; ++i) printf("dizi[%d] = %d\n", i, dizi[i]); return 0;

sralama ynn kkten bye yapmak yerine bykten ke yapmak iin ierideki dngy for (j = i; j > 0 && dizi[j - 1] < temp; --j) eklinde deitirmek yeterli olacaktr.

Uygulama 6 : Bir dizi ierisindeki elemanlar kkten bye selection sort algoritmasyla sraya dizen program: #include <stdio.h> #define SIZE 10

int a[SIZE] = { 12, 23, 45, 78, -23, ,56, 78, 3, 9, -4}; int main() { int k, l, max, indis; for (k = 0; k < SIZE; ++k) { max = a[k]; indis = k; for (l = k + 1; l < SIZE; ++l) if (a[l] > max) { max = a[l]; indis = l; } a[indis] = a[k]; a[k] = max; } for (k = 0; k < SIZE; ++k) printf(%d\n, a[k]); return 0;
169

18 } karakter dizileri Karakter dizileri char trden dizilerdir. Karakter dizilerinin baz ilave zellikler dnda dier dizi trlerinden bir fark yoktur. char trden diziler daha ok ilerinde yaz tutmak iin tanmlanrlar. char s[100]; yukardaki tanmlamada s dizisi btn elemanlar char trden olan 100 elemanl bir dizidir. char trden bir dizi iinde bir yaz tutmak demek, dizinin herbir elemanna srayla yaznn bir karakterini atamak anlamna gelecektir. (Bu arada char trden bir dizinin iinde bir yaz tutmann zorunlu olmadn, byle bir dizinin pekala kk tamsaylar tutmak amacyla da kullanlabileceini hatrlatalm) Yukarda tanmlanan dizi iinde "Ali" yazsn tutmak isteyelim : s[0] = 'A'; s[1] = 'l'; s[2] = 'i'; imdi yle bir problem ortaya kyor. Dizi 100 karakterlik olmasna karn biz dizi iinde 100 karakter uzunluundan daha ksa olan yazlar da tutabiliriz. Peki biz yazya tekrar ulamak istediimizde bunu nasl yapacaz? Yaznn uzunluk bilgisini bilmiyoruz ki! rnein yazy ekrana yazdrmak istediimizde, int trden dizilerde kullandmz ekilde aadaki gibi bir dng kullanrsak : for (k = 0; k < 100; ++k) putchar(s[k]); bu dng ile yalnzca ALi yazs ekrana yazdrlmayacak dizinin dier 97 elemannn da grntleri (rasgele deerler) ekrana yazdrlacak. Programlama dilleri bu duruma bir zm bulma konusunda iki temel yaklam benimserler. a. Dizinin 1. elemanna yaznn uzunluk bilgisi yazlr. Bylece dizideki yazya ulalmak istendiinde nce dizinin 1. elemanndan yaznn uzunluk bilgisi ekilir. Bylece dng deyiminin iterasyon says deeri elde edilmi olur. Dng bu deere gre oluturulur. s[0] = 3; s[0] = 'A'; s[1] = 'l'; s[2] = 'i'; .... for (k = 1; k <= s[0]; ++k) putchar(s[k]); C dilinde bu yaklam kullanlmaz. b. Dizinin elemanlarna yaznn karakterleri srasyla yerletirilir. Yaz bittikten sonra dizinin sradaki elemanna zel bir karakter yerletirilir. Bu zel karakter doal olarak normal artlar altnda bir yaznn
170

DZLER eleman olamayacak bir karakter olarak seilmelidir. Bylece char trden dizi iinde saklanm yazya tekrar ulalmak istendiinde, kullanlacak dng deyiminde, dizinin dng deikeni olan indisli elemannn seilen zel karaktere eit olmad srece dngnn dnmesi art oluturulur. C dilinin tasarmnda bu yaklam tercih edilmitir. C dilinde karakterler zerinde ilemlerin hzl ve etkin bir biimde yaplabilmesi iin sonlandrc karakter (NULL KARAKTER) kavramndan faydalanlmaktadr. 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. Bu karakter DOS ve UNIX sistemlerinde sonlandrc karakter olarak kullanlmaktadr. stdio.h ierisinde NULL sembolik sabiti 0 olarak tanmland iin sonlandrc karaktere NULL karakter de denir. NULL karakter 0 karakteri ile kartrlmamaldr. 0 karakterinin ASCII sra numaras 48dir. Dolaysyla tamsay deeri olarak 48 deerine sahiptir. \0 karakterinin ASCII sra numaras 0dr. Dolaysyla tamsay deeri olarak 0 deerine sahiptir. ... printf(%d\n, 0); printf(%d\n, \0); ... yukardaki ilk printf fonksiyonunun arlmasyla ekrana 48 deeri yazdrlrken, ikinci printf fonksiyonunun arlmasyla ekrana 0 deeri yazdrlmaktadr. char trden dizilere ilk deer verilmesi char trden dizilere ilk deer verme ilemi (initializing) aadaki biimlerde yaplabilir . Dier trden dizilerde olduu gibi virgllerle ayrlan ilk deerler kme parantezi iinde yer alr : char name[12] = {N, e, c, a, t, i, , E, r, g, i,n}; Dier dizilerde olduu gibi dizi eleman saysndan daha fazla sayda elemana ilk deer vermek derleme zamannda hata oluumuna neden olur. char name[5] = {N, e, c, a, t, i, , E, r, g, i,n}; /* error */

Dizi eleman says kadar elemana ya da dizi eleman saysndan daha az sayda elemana ilk deer verilebilir. Daha az sayda elemana ilk deer verilmesi durumunda ilk deer verilmemi elemanlar dier dizilerde olduu gibi 0 deeriyle balatlacaklardr. 0 deerinin NULL karakter olduunu hatrlayalm : char name [10] = {A, l, i}; ... A l i \0 \0 \0 \0 ... name[0] name[1] name[2] name[3] name[4] name[5] name[6]

171

18 \0 \0 \0 ... name[7] name[8] name[9] ...

Dizi elemanlarna ilk deer verilirken dizi boyutu belirtilmeyebilir. Bu durumda derleyici dizi boyutunu verilen ilk deerleri sayarak saptar ve diziyi bu boyutta alm varsayar. char name[ ] = {A, l, i}; yukardaki tanmlama deyimiyle derleyici name dizisinin 3 elemanl olarak aldn varsayacaktr. Boyut bilgisi vermeden, char trden dizilere tek tek ilk deer verilmesi durumunda, ve boyut bilgisi verilerek dizinin toplam eleman says kadar elemana tek tek ilk deer verilmesi durumunda, derleyici NULL karakteri dizinin sonuna otomatik olarak yerletirmeyecektir. Bu durumda eer sonlandrc karakter zelliinden faydalanlmak isteniyorsa, NULL karakter de verilen dier ilk deerler gibi programc tarafndan verilmelidir. rnek : char name[ ] = {A, l, i, \0}; char isim[7] = {N, e, c, a, t, i, \0}; Bu ekilde ilk deer vermek zahmetli olduundan, ilk deer vermede ikinci bir biim oluturulmutur. Karakter dizilerine ilk deerler ift trnak iinde de verilebilir : char name[ ] = Ali; bu biimin dierinden fark derleyicinin sonuna otomatik olarak NULL karakteri yerletirmesidir. ... A l i \0 ... name[0] name[1] name[2] name[3]

yukardaki rnekte derleyici diziyi 4 elemanl olarak alm varsayacaktr. Dizi eleman saysndan daha fazla sayda elemana ilk deer vermeye almak, bu biimin kullanlmas durumunda da derleme zamannda hata oluumuna neden olacaktr. char city[5] = stanbul; /* error */

Bu durumun bir istisnas vardr. Eger tam olarak dizi eleman says kadar dizi elemanna ift trnak iinde ilk deer verilirse bu durum ERROR oluturmaz. Derleyici bu durumda NULL karakteri dizinin sonuna yerletirmez. Bu durum C dili standartlarna yneltilen eletirilerden biridir. (C++ dilinde son kabul edilen standartlara gre bu durum da error oluturmaktadr.) char name[3] =Ali; ... A l i ... name[0] name[1] name[2] /* C'de hata deil, C++'da hata */

172

DZLER ... buraya bu durumda yerletirilmiyor. null karakter

'\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 gerekmektedir. Ama char trden diziler sz konusu olduunda artk dizi uzunluunu bilmemiz gerekmez, nk yaznn sonunda '\0' karakter bulunacandan, kontrol ifadelerinde bu durum test edilerek yaznn sonuna gelinip gelinmedii anlalr. Ancak '\0' karakterin, karakter dizilerinde yazlarn son eleman olarak kullanlmasnn dezavantaj da diziye fazladan bir karakter yani '\0' karakteri eklemek zorunluluudur. Bu nedenle SIZE elemanl bir diziye en fazla SIZE 1 tane karakter girilmelidir. klavyeden karakter dizisi alan ve ekrana karakter dizisi yazan standart C fonksiyonlar Daha nce grdmz getchar, getch, ve getche fonksiyonlar klavyeden tek bir karakter alyorlard. Yine putchar fonksiyonu ise tek bir karakteri ekrana yazyordu. C dilinde birden fazla karakteri (bir stringi) klavyeden alan ya da birden fazla karakteri ekraana yazan standart fonksiyonlar bulunmaktadr. gets fonksiyonu gets fonksiyonu klavyeden karakter dizisi almakta kullanlan standart bir C fonksiyonudur. Kullanc karakterleri girdikten sonra enter tuuna basmaldr. Klavyeden girilecek karakterlerin yerletirilecei dizinin ismini parametre olarak alr. daha nce de belirtildii gibi dizi isimleri aslnda bir adres bilgisi belirtmektedir. gets fonksiyonunun da parametresi aslnda char trden bir adrestir. Ancak gstericilerle ilgili temel kavramlar henz renmediimiz iin imdilik gets fonksiyonunun char trden bir dizi iine klavyeden girilen karakterleri yerletirdiini kabul edeceiz. rnein : char name[20]; gets(name); ile klavyeden enter tuuna baslana kadar girilmi olan tm karakterler name dizisi iine srayla yerletirilirler. Klavyeden Necati yazsnn girildiini kabul edelim. ... N e c a t i \0 ... ... name[0] name[1] name[2] name[3] name[4] name[5] name[6] ...

gets fonksiyonu klavyeden girilen karakterleri diziye yerletirdikten sonra dizinin sonuna NULL karakter (sonlandrc karakter) diye isimlendirilen 0 numaral ASCII karakterini (ya da kullanlan karakter setinin 0 numaral karakterini) koyar.

173

18 gets fonksiyonu dizi iin hi bir ekilde snr kontrolu yapmaz. gets fonksiyonu ile dizi eleman saysndan fazla karakter girilirse, d,z, taaca iin beklenmeyen sonularla karlalabilir. Bu tr durumlar gstericiler konusunda (gsterici hatalar) detayl olarak incelenecektir. gets fonksiyonu \0 karakterini dizinin sonuna ekledii iin SIZE uzunluunda bir dizi iin gets fonksiyonuyla alnacak karakter says en fazla SIZE 1 olmaldr. nk sonlandrc karakterde dier karakterler gibi bellekte bir yer kaplamaktadr. rnek : char isim[12]; gets(isim); ile klavyeden Necati Ergin isminin girildiini dnelim. ... N e c a t i E r g i n \0 ... ... isim[0] isim[1] isim[2] isim[3] isim[4] isim[5] isim[6] isim[7] isim[8] isim[9] isim[10] isim[11] tama

isim dizisinin tanmlanmasyla derleyici bu dizi iin bellekte 12 byte yer ayracaktr. isim[0] ...isim[11] gets fonksiyonu bu durumda \0 karakterini derleyicinin dizi iin tahsis etmedii bir bellek hcresine yazacaktr. Bu tr durumlara dizinin tarlmas (off bye one) denmektedir. tama durumuyla ilgili olarak ortaya kacak hatalar derleme zamanna deil alma zamanna (run time) ilikindir. puts fonksiyonu puts fonksiyonu bir karakter dizisinin ieriini ekrana yazdrmak iin kullanlr. erii yazdrlacak olan karakter dizisinin ismini parametre olarak alr. puts fonksiyonu karakter dizisini ekrana yazdktan sonra imleci sonraki satrn bana geirir. char name[20]; gets(name); puts(name); yukardaki rnekte gets fonksiyonu ile klavyeden alnan karakter dizisi puts fonksiyonu ile ekrana yazdrlmaktadr. karakter dizilerini ekrana yazdrmak iin printf fonksiyonu da kullanlabilir.

174

DZLER printf(%s\n, name); ile puts(name); ayn ii yapmaktadr. Ancak printf fonksiyonu dizi ieriini ekrana yazdrdktan sonra imleci alt satra tamaz. puts(name); deyimi yerine aadaki kod parasn da yazabilirdik. for (i = 0; name[i] != \0; ++i) putchar(s[i]); putchar(\n); puts fonksiyonu ve %s format karakteriyle kullanldnda printf fonksiyonu, sonlandrc karakter grene kadar btn karakterleri ekrana yazar. Bu durumda, herhangi bir ekilde NULL karakter ezilirse her iki fonksiyon da ilk sonlandrc karakteri grene kadar yazma ilemine devam edecektir. rnein : char city[ ] = Ankara; city[6] = !; deyimi ile sonlandrc karakter ortadan kaldrlrsa (ezilirse) : puts(name); eklinde puts fonksiyonu arldnda ekrana Ankara!yazldktan sonra tesadfen ilk NULL karakter grlene kadar ekrana yazmaya devam edilir. puts ve printf fonksiyonlar karakter dizilerini yazarken yalnzca NULL karakteri dikkate alrlar. karakter dizilerinin uzunluklaryla ilgilenmezler. sizeof operatrnn dizilerle kullanlmas sizeof operatr operand olarak bir dizi ismi aldnda byte olarak o dizinin toplam uzunluunu deer olarak retir. double sample[10]; sizeof(sample) ifadesi 80 deerini retecektir. sizeof operatr operand olarak dizinin bir elemann aldnda rettii deer, dizi hangi trden ise o trn kullanlan sistemdeki byte olarak uzunluu olacaktr. yani yukardaki rnekte sizeof(sample[0]) ifadesi, 8 deerini retecektir.

sizeof operatrn bir dizinin uzunluk bilgisi gerektii yerde de kullanabiliriz :


175

18

sizeof(sample) / sizeof(sample[0]) ifadesi dizi uzunluunu verecektir. rnek : for (i = 0; i < sizeof(a) / sizeof(a[0]); ++i) a[i] = 0;

karakter dizileriyle ilgili baz kk uygulama rnekleri Uygulama 7: Karakter dizisi ierisindeki yaznn uzunluunu bulan program: #include <stdio.h> main() { int k = 0, char s[50]; gets(s); while (s[k] != \0) ++k; printf(uzunluk = %d\n, k);

yukarda while dngs yerine for dngs kullanlabilirdi : for (k = 0; s[k] != \0; ++k) ;

Uygulama 8: karakter dizisini tersten yazdran program : #include <stdio.h> #define SIZE 100

main() { char s[SIZE]; int k; printf("bir yaz giriniz :"); gets(s); for (k = 0; s[k + 1] != '\0'; ++k) ++k; for (; k >= 0; --k) putchar(s[k]); return 0;

176

DZLER

Uygulama 9: Karakter dizisindeki, byk harfleri kk harfe kk harfleri byk harfe eviren bir program : #include <stdio.h> #include <ctype.h> #define SIZE main() { char s[SIZE]; int k; printf ("bir yaz giriniz : "); gets(s); for (k = 0; s[k] != '\0'; ++k) s[k] = isupper(s[k]) ? toupper(s[k]) : tolower(s[k]); /* islower la baslamayacakmyd? */ printf("dntrlm yaz \n"); puts(s); return 0; } Uygulama 10 : Karakter dizisini tersyz eden program : #include <stdio.h> main() { char s[50]; int n, j, temp, gets(s); for (n = 0; s[n] != \0; ++n) ; for (j = 0; j < n / 2; ++j) { temp = s[n j 1]; s[n j 1] = s[j]; s[j] = temp; } puts(s); } Uygulama 11 : bir karakter dizisi iindeki harfleri kkten bye doru sralayan bir program #include <stdio.h> #define #define main() {
177

100

SIRALI SIRASIZ

1 0

18 char s[100]; int flag, k, temp; clrscr(); printf("bir yaz giriniz :"); gets(s); do { flag = SIRALI; for (k = 0; s[k + 1] != '\0'; ++k) if (s[k] > s[k + 1]) { temp = s[k]; s[k] = s[k + 1]; s[k + 1] = temp; flag = SIRASIZ; } } while (flag == SIRASIZ); printf("sralanm yaz :\n"); puts(s); getch(); }

178

GSTERCLER

19 . BLM :
Adres Kavram

GSTERCLER

Adres kavram hem donanma hem de yazlma ilikindir. Donanmsal adan adres bellekte yer gsteren bir saydan ibarettir. Mikroilemci bellekte bir blgeye ancak o blgenin adres bilgisiyle eriebilir. Bellekte (RAM'de) her byte (8 bit) dierlerinden farkl bir adresle temsil edilir. Sfr saysndan balayarak her bytea artan srada bir karlk getirerek elde edilen adresleme sistemine dorusal adresleme sistemi (linear addressing), bu sistem kullanlarak elde edilen adreslere de dorusal adresler denilmektedir. Donanmsal olarak RAM'deki her bir byte'a okuma ya da yazma yapma amacyla ulalabilir. rnein, 64 Klk bir bellein dorusal olarak adreslenmesi aadaki biimde yaplabilir : BELLEK DORUSAL ADRES 0 (0000) 1 2 3 4 .................. 65533 (FFFD) 65534 (FFFE) 65535 (FFFF)

...........

Bilgisayara ilikin pekok nitelikte olduu gibi dorusal adreslemede de 10luk sistem yerine 16lk sistemdeki gsterimler tercih edilir. Bundan byle yalnzca adres dendiinde dorusal adres anlalmaldr. Nesnelerin Adresleri Her nesne bellekte yer kapladna gre belirli bir adrese sahiptir. Nesnelerin adresleri, sistemlerin ounda, derleyici ve program ykleyen iletim sistemi tarafndan ortaklaa olarak belirlenir. Nesnelerin adresleri program yklenmeden nce kesin olarak bilinemez ve programc tarafndan da nceden tespit edilemez. Programc, nesnelerin adreslerini ancak programn almas srasnda (run time) renebilir. rnein: char ch; biiminde bir tanmlamayla karlaan derleyici bellekte ch deikeni iin 1 byte yer ayracaktr. Derleyicinin ch deikeni iin bellekte hangi byte' ayracan nceden bilemeyiz. Bunu ancak programn almas srasnda renebiliriz. Yukardaki rnekte ch yerel bir deikendir. ch deikeni ilgili blok icra edilmeye balandnda yaratlp, bloun icras bittiinde de yok olmaktadr. ch deikeninin 1A03 adresinde olduu varsaylarak aadaki ekil izilmitir: 1A00 1A01 1A02 1A03 1A04 1A05

ch

179

19

Tanmlanan nesne 1 bytedan daha uzunsa, o zaman nesnenin adresi nasl belirlenir? int b; 1C00 1C01 1C02 1C03 1C04 1C05 1C06 1C07

1 bytedan uzun olan nesnelerin adresleri, onlarn ilk bytelarnn (dk anlaml bytelarnn) adresleriyle belirtilir. Yukardaki rnekte x deikeninin adresi 1C03tr. Zaten x deikeninin tamsay trnden olduu bilindiine gre dier parasnn 1C04 adresinde olaca da aktr. Benzer biimde long trnden olan y deikeninin bellekteki yerleiminin aadaki gibi olduu varsaylrsa, adresinin 1F02 olduunu syleyebiliriz: 1F00 1F01 1F02 1F03 1F04 1F05 1F06 1F07

Yerel ya da global olsun, ardl (contigious) bir biimde tanmlanm nesnelerin bellekte de ardl bir biimde tutulacann bir garantisi yoktur. rnein: { int x; char ch; ...

Buradaki ch deikeninin x deikeninden 2 byte sonra bulunacann bir garantisi yoktur. Ayr Bir Tr Olarak Adres Yazlmsal adan RAM'de belirli bir byte'dan ne amala bahsetmek isteyebiliriz? O byte iinde yer alan nesnenin deerini tespit etmek iin! Eer tm nesnelerimiz 1 byte uzunluunda olsalard, o zaman yazlmsal olarak adres ile fiziksel adres arasnda herhangi bir fark bulunmayacakt. Oysa RAM'deki belirli bir byte'dan sz ettiimizde, o byte' (belki onu izleyen baz byte'larla birlikte) herhangi trden bir nesne kaplyor olabilir. Yazlmsal olarak adres bilgisi yalnzca bellekte yer gsteren bir saydan ibaret deildir; ayn zamanda o adresin gsterdii yerdeki bilginin ne biimde yorumlanacan belirten bir tr bilgisini de iermektedir.

180

GSTERCLER rnein yazlmsal olarak 1250 adresindeki deerden sz etmek isteseydik aadaki sorularn cevab akta kalacakt. Sz konusu deer yalnzca 1250 adresindeki byte da m saklanm yoksa bunu izleyen (1251, 1252 vs.) bytelar da nesnenin deerinin tespit edilmesinde kullanlacak m? Sz konusu deer hangi formatta yorumlanacak? (Yani iaretli tamsay aritmetiine gre mi, iaretsiz tam say aritmetiine gre mi, gerek say formatna gre mi, vs.) yazlmsal olarak bir adres bilgisinin iki bileeni vardr: 1. Bellekte yer gsteren saysal bir deer 2. Adresin tr Adreslerin Trleri Bir adresin tr demekle o adrese ilikin bellek blgesinde bulunan bilginin derleyici tarafndan yorumlan biimi anlalmaldr. rnein: char ch; gibi bir bildirim sonrasnda ch deikeninin bellee aadaki biimde yerlemi olduunu kabul edelim: 1A00 1A01 1A02 1A03 1A04 1A05

ch

Yukardaki ekle baktmzda 1A03 saysnn bir adres belirttiini anlyoruz. 1A03 adresinde bulunan bilgi char trdendir. nk derleyici 1A03 adresinde bulunan bilgiyi char trden bir nesne olarak yorumlamaktadr. Adres yalnz bana saysal bileeniyle bir anlam tamaz, gsterdii trle birlikte belirtilmesi gerekmektedir. Cde adres bilgisi ayr bir veri/nesne trdr. Daha nce grdmz 11 temel veri trne ilaveten 11 ayr adres trnn de varlndan sz edebiliriz. Adres Sabitleri Adresler tamsay grnmnde olsalar da tamsay sabitleri gibi belirtilmezler. nk tr bilgilerinin de belirtilmesi gerekir. Adres sabitleri, tamsay trlerindeki sabitler zerinde bilinli tr dnm yaplarak elde edilirler. Bir tamsay sabitini adres trne evirmek iin tr dntrme operatr kullanlr. (<tr> *) <tamsay sabiti> Tr dntrme operatrnn iindeki * adres ya da gstericiyi temsil etmektedir. rnein 0x1F00 hexadesimal olarak gsterilmi int trden bir sabittir. Ancak: (int *) 0x1F00

181

19

int trnden bir adres sabitidir. 0x16C0L long trden bir tamsay sabittir. Ancak: (long *) 0x16C0L long trden bir adres sabitidir. Bunun anlam udur: Derleyici 16C0 adresiyle belirtilen yerdeki bilgiyi long olarak yorumlayacaktr. Adres trne bilinli dnm yalnz sabitlerle yaplmayabilir, rnein: (char *) var var isimli deiken hangi trden olursa olsun, yukardaki ifade ile char trden bir adres haline dntrlmtr. Bir adres bilgisini bir nesne iinde tutmak isteyelim. (u an iin neden byle bir ey isteyebileceimiz sorusu akla gelebilir, ama ileride bunun ok doal ve gerekli bir ilem olduunu greceiz.) Bu ii nasl gerekletireceiz? lk olarak aklmza u gelebilir: int trden bir nesne tanmlayalm ve adresin saysal bileenini tanmladmz int trden nesne iinde tutalm. Yani adresin saysal bileeni olan tamsayy int trden bir nesneye atayalm. Bu durumda iki nemli saknca ortaya kacaktr: Sz konusu int nesnenin deerine programn herhangi bir yerinde tekrar ulatmzda, bunun bir adres bilgisinin (yani iki bileenli bir bilginin) saysal bileeni mi yoksa int trden normal bir deer mi olarak yorumlanacan nereden bileceiz? Peki rnein deikenin isminden, deerinin bir adres bilgisinin saysal bileeni olduu sonucunu kardmz dnelim, bu durumda adresin tr bileeninin ne olduunu nereden anlayacaz? O zaman belki de u nerilecektir: int trden bir adres bilgisini int trden bir nesnede, char trden bir adres bilgisini char trden bir nesnede, double trden bir adres bilgisini ise double trden bir nesnede saklayalm, vs. Ama rnein 64K'lk bir adres alannn sz konusu olduunu dnelim. toplam 65535 byte sz konusu olabileceine gre bize 2byte uzunluunda bir nesne gerekecek. rnein 1 byte uzunlukta bir nesne iinde ancak ilk 255 byte'a ilikin saysal bileenleri tutarken, 8 byte uzunluunda bir nesne iinde de gerekmedii halde ok byk bir alana ilikin adres bilgisini tutabiliriz. int sabitleri int trden deikenlere, gerek say sabitleri gerek say trnden deikenlere doal olarak atanyorlar. Yani her sabit trnn doal olarak atanabilecei trden bir deiken tanmlayabiliyoruz. te adres de ayr bir tr olduuna gre adres trnn de doal olarak atanabilecei deikenler tanmlanabilir. Bu deikenlere (nesnelere) gsterici (pointer) denir. a /* char trden bir sabittir */ char a; /* a char trden bir deikendir */ int b; /* b int trden bir deikendir */ 2000L /* long trden bir sabittir */ long l; /* l long trden bir deikendir */ (<tr> *) 0x1B00 /* adres sabiti */ pointer /* iinde adres tutan bir deiken */

182

GSTERCLER Gsterici (pointer) iinde adres bilgisi tutan bir deikendir (nesnedir). Gstericiler de nesne olduklar iin bellekte bir yer kaplarlar. Gstericilerin bellekte kapladklar yerdeki 1ler ve 0larn bir tamsay olarak yorumlanr, ve yorumlanan bu deer bir adres bilgisinin saysal bileenini gsterir. Peki adres bilgisinin iki bileenli bir bilgi olduunu sylemitik. Gstericinin deeri adresin saysal bileeni olarak yorumlanacaksa adresin tr bileeni nasl elde edilecek? Zira bellekte yalnzca 1 ler ve 0 lar var. Gstericilerin tuttuu adres bilgilerinin saysal bileenleri gstericiler iinde saklanan tamsaynn deeridir. Adres bilgisinin tr bileeni ise gstericinin tanmlanmas srasnda bildirilen trdr. Gstericilerin Bildirimleri Gstericiler adres bilgilerini saklamak ve adreslerle ilgili ilemler yapmak iin kullanlan nesnelerdir. Gstericilerin ilerinde adres bilgileri bulunur. Bu nedenle gsterici ile adres hemen hemen e anlaml olarak dnlebilir. Ancak gsterici deyince bir nesne, adres deyince bir tr akla gelmelidir. Gsterici bildirimlerinin genel biimi yledir: <tr> *<gsterici ismi>; <tr> gstericinin (ierisindeki adresin) trdr. char, int, float... gibi herhangi bir tr olabilir. Burada * gstericiyi ya da adresi temsil etmektedir. rnek gsterici bildirimleri: float *f; char *s; int *dizi; unsigned long *PDWORD; Gsterici bildirimlerinin dier trlere ilikin bildirimlerden * atomu ile ayrlmaktadr. char s; bildiriminde s char trden bir deiken iken char *s; bildiriminde s char trden bir gstericidir. erisine char trden bir adres konulmaldr. Bu bildirimden derleyici u bilgileri karacaktr: s bir nesnedir, yani bellekte bir yer kaplar. s nesnesi iin bellekte ayrlan yerdeki 1 ler ve 0 lar char trden bir adresin saysal bileeni olarak yorumlanrlar. Bu adresin tr bileeni ise char trdr. Burada * bir operatr deildir. Sentaks olarak nesnenin bir gsterici olduunu anlatmaktadr. Ritchie stilinde, okunabilirlik asndan * ile gsterici ismi bitiik yazlmaldr. Gsterici bildirimleri ile normal bildirimler bir arada yaplabilir. rnein : int *p, a; Burada p int trden bir gstericidir ama a int trden bir normal bir deikendir.

183

19 Ayn trden birden fazla gstericinin bildirimi yaplacaksa araya virgl konularak, her gsterici deikenin bildirimi * atomu ile yaplmaldr. char *str, *ptr; Yukardaki bildirimde str ve ptr char trden gstericilerdir. long *p1, *p2, l, k[20]; Yukardaki bildirimde p1 ve p2 long trden gstericiler, l long trden bir deiken ve k ise long trden 20 elemanl bir dizidir. Gstericilerin Uzunluklar Bir gsterici tanmlamasyla karlaan derleyici dier tanmlamalarda yapt gibi- bellekte o gsterici iin bir yer tahsis eder. Derleyicilerin gstericiler iin tahsis ettikleri yerlerin uzunluu donanm baml olup, sistemden sisteme deiebilmektedir. 32 bit sistemlerde (rnein UNIX ve Windows 95 sistemlerinde) gstericiler 4 byte uzunluundadr. 8086 mimarisinde ve DOS altnda alan derleyicilerde ise gstericiler 2 byte ya da 4 byte olabilirler. DOSta 2 byte uzunluunda ki gstericilere yakn gstericler (near pointer), 4 byte uzunluundaki gstericilere ise uzak gstericiler (far pointer) denilmektedir. Biz uygulamalarmzda imdilik gstericilerin 2 byte olduunu varsayacaz. Gstericilerin uzunluklar trlerinden bamszdr. rnein: char *str; int *p; float *f; DOS altnda str, p ve f isimli gstericilerin hepsi de bellekte 2 byte ya da 4 byte yer kaplarlar. nk gstericilerin tr yalnzca iinde tuttuklar adres bilgisinin hangi tr olarak yorumlanaca ile ilgilidir. Bir gstericiye ayn trden bir adres bilgisi yerletirilmelidir. rnein : int *p; p = 100; Burada p gstericisine adres olmayan bir bilgi atanmaktadr. C dilinin kurallarna gre, derleme zamannda bir hata oluumuna yol amaz ama yanltr. (Derleyiciler bu durumu bir uyar mesaj ile bildirirler.) Bu durum ileride detayl olarak ele alnacaktr. int *p; p = (char *) 0x1FC0; Burada int trden p gstericisine char trden bir adres bilgisi atanmaktadr. C dilinin kurallarna gre, derleme zamanndan bir hata oluumuna yol amaz ama yanltr. (Derleyiciler bu durumu bir uyar mesaj ile bildirirler.) int *p; p = (int *) 0x1FC5; /* geerli ve uygun bir atamadr */

Bir adres bilgisi gstericiye atandnda adresin saysal bileeni gsterici ierisine yerletirilir.
184

GSTERCLER

int *p; p = (int *) 0x1FC3; Burada bellekte p gsterici deikeninin tutulduu yere 0x1FC3 saysal deeri yerletirilecektir. ... 0001 1111 1100 0011 ...

p gstericisinin bellekte bulundugu yere 1FC3 deeri yazlyor.

Gstericilere kendi trlerinden bir adres bilgisi atanmasnn gerektiini sylemitik. imdiye kadar adres bilgilerini yalnzca adres sabitleri eklinde grdk ve rneklerimizde de gstericilere adres sabitlerini atadk. Peki adres sabitleri dnda adres bilgileri tayan baka ifadeler mevcut mudur? Evet. rnein diziler konusunu incelerken dizi isimlerinin, nesne gstermediini, bir adres bilgisi olduunu sylemitik, imdi bu konuyu daha detayl olarak inceleyeceiz: Dizi isimleri bir adres bilgisi belirtir. Adres bilgisinin iki bileeni olduuna gre rnein: char s[10]; gibi bir dizi tanmlamas yapldnda dizi ismi olan s bir adres bilgisi belirtecektir. Peki bu bilginin tr bileeni ve saysal bileenleri nelerdir? Dizi isimleri, trleri dizinin trleriyle ayn ve saysal bileenleri dizi iin bellekte ayrlan bloun balang yerini gsteren bir adres bilgisi olarak ele alnrlar. rnein yukardaki rnekte dizinin bellekte aadaki ekilde yerletirildiini dnelim: s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9] 1C00 1C01 1C02 1C03 1C04 1C05 1C06 1C07 1C08 1C09

Bu durumda dizi ismi olan s, char trden 1C00 adresine edeerdir. Gstericilere kendi trlerinden bir adres bilgisi atamak gerektiine gre aadaki atamalarn hepsi legal ve dorudur: int a[100]; long l[20]; char s[100]; double d[10]; int *p; long *lp;

185

19 char *cp; double *dp; p = a; lp = l; cp = s; dp = d; Bir gstericinin iine ayn trden bir dizinin ismi atanmaldr. rnein: int *p; char s[ ] = Necati; p = s; Yukardaki rnekte int trden bir gstericiye char trden bir adres bilgisi atanmtr. Derleme zamannda error oluumuna neden olmaz ama yanltr, ileride detayl olarak incelenecektir. C dilinde hibir deikenin ya da dizinin tahsisat yeri programc tarafndan belirlenemez. Programc deikeni tanmlar, derleyici onu herhangi bir yere yerletirebilir. Dizi isimleri gstericiler gibi sol taraf deeri olarak kullanlamaz. rnein, s bir dizi ismi olmak zere ++s; deyimi error oluturur. nk dizi isimleri nesne gstermezler. zetle, adres belirten 3 tr ifade ele alnd. 1. Adres sabitleri. 2. Gstericiler. 3. Dizi isimleri. Gstericiler ilerinde adres bilgileri tadklarna gre bir gstericiye ayn trden baka bir gstericinin deerinin atanmas da tamamen uygundur. int *p, *q; p = (int *) 0x1AA0; q = p; Yukardaki atama ile q gstericisine p gstericisinin deeri atanmtr. Yani bu atama deyiminden sonra q gstericisinin de iinde (int *) 0x1AA0 adresi bulunacaktr. ... 0001 1010 p 1010 0000 ... ... ... 0001 1010 q

p gstericisine (int *) 1AA0 adresi atanyor.

186

GSTERCLER 1010 0000 q gstericisine de p gstericisinin iindeki deer atanyor

int k; gibi bir tanmlama yaptmzda k deikeni int trdendir, iindeki deer int olarak yorumlanacaktr. 20 gibi, tek bir sabitten oluan bir ifade de int trdendir, nk 20 int trden bir sabittir. 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. (sa taraf deeridir) int *ptr; gibi bir tanmlama yapldnda ptr nesnesinin tr nedir? ptr iinde int trden bir adres sakladna gre ptr nesnesinin tr int trden bir adrestir. int a[100]; Yukardaki tanmlamadan sonra a gibi bir ifade kullanlrsa bu ifadenin tr de "int trden bir adres" dir. Ancak ptr ifadesi nesne gsteren bir ifadeyken, yani bir sol taraf deeriyken, a ifadesi nesne gsteren bir ifade deeridir. Sol taraf deeri olarak kullanlamaz. Yine bir adres sabitinden oluan (int *) 0x1A00 ifadesinin tr de int trden bir adrestir. Ancak bu ifade de sol taraf deeri deildir. Grld gibi gstericiler, belirli bir adres trnden, nesnelerdir. (sol taraf deerleridir.) Gsterici Operatrleri C dilinde toplam 4 tane gsterici operatr vardr. Bu operatrler gstericiler ve adres bilgileriyle kullanlabilirler. Gsterici operatrleri unlardr : * ierik operatr indirection operator (dereferencing operator)
187

19 & [] -> adres operatr keli parantez operatr ok operatr address of operator index operator (subscript operator) arrow operator

Bu operatrlerden ok operatr yap trnden adreslerle kullanld iin yaplar konusunda detayl olarak incelenecektir. Gsterici Operatrlerinin Ayrntl ncelenmesi & Adres Operatr (adress of operator) Adres operatr tek operand alan nek konumunda bir operatrdr. (unary prefix). Operatr ncelik tablosunun ikinci seviyesinde yer alr. Bu operatrn rettii deer operand olan nesnenin adresidir. Yani operatrn rettii adres bilgisinin saysal bileeni nesnenin bellekteki fiziksel adres numaras, tr bileeni ise nesnenin tr ile ayn trdr. & operatrnn operand mutlaka bir nesne olmaldr. nk yalnzca nesnelerin (sol taraf deerlerinin) adres bilgilerine ulalabilir. & operatrne operand olarak nesne olmayan bir ifade gnderilirse bu durum derleme zamannda hata oluumuna neden olacaktr. (Borland derleyicileri bu durumda yle bir error mesaj verirler : must take address of memory location) int k; &k ifadesini ele alalm. Bu ifadenin rettii deer int trden bir adres bilgisidir. Adres bilgilerinin iki bileeni olduunu sylemitik. Yukardaki ifadenin rettii bilginin saysal bileeni k nesnesinin bellekte yerletirildii yerin balang adresi, tr bileeni ise int trdr. Zira k deikeni int trden bir deikendir. & operatr dier tek operand alan (unary) operatrler gibi, operatr ncelik tablosunun 2. seviyesinde bulunur. Bilindii gibi bu ncelik seviyesinin ncelik yn sadan soladr. Adres operatr ile elde ettiimiz adres ayn trden bir gstericiye atanmaldr. rnein aadaki programda bir gstericiye farkl trden bir adres atanmtr: char ch = 'x'; int *p; p = &ch; /* error deil ama yanl */

Bu durumda C'de adres belirten 4 tr ifade grm olduk: 1. 2. 3. 4. Adres sabitleri. Gstericiler. Dizi isimleri. & operatr ile oluturulmu ifadeler.

Tabi bu operatrn rettii adres bilgisi nesne deildir. rnein: int x; ++&x /* error */

188

GSTERCLER

gibi bir ilem error ile neticelenir. ++ operatrnn operand nesne olmaldr. Yukardaki ifadede ++ operatrne operand olan &x ifadesi bir nesne gstermemektedir. Yalnzca bir adres deeridir. * erik Operatr (indirection operator) erik operatr de nek konumunda bulunan ve tek operand alan bir operatrdr (unary prefix). Bir gsterici, * operatrnn operand olursa, elde edilen ifade p gstericisinin ierisindeki RAM adresinde bulunan, nesneyi temsil eder. Dolaysyla, * operatr ile oluturulan bir ifade bir nesneyi temsil etmektedir, ve sol taraf deeri olarak kullanlabilir. int a; gibi bir bildirimde a nesnesinin tr intdir. nk a nesnesi ierisinde int trden bir bilgi tutulmaktadr. int *p; bildiriminde p nin tr int trden bir adrestir. char *ptr; gibi bir bildirimden iki ey anlalr: ptr char trden bir gstericidir. erisine char trden bir adres bilgisi yerletirilir. ptr gstericisi * operatr ile birlikte kullanldnda elde edilen nesne char trdendir. Yani *ptr char trden bir nesnedir. rnein: int *p; p = (int *) 0x1FC3; *p = 100; Burada *pnin tr intdir. Dolaysyla *p = 100 gibi bir ilemden yalnzca 0x1FC3 byte deil, 0x1FC3 ve 0x1FC4 bytelarndan her ikisi birden etkilenir. Gstericinin ierisindeki adresin saysal bileeni nesnenin dk anlaml bytenn adresini ierir. Bu durumda bir gstericinin ierisine RAMdeki herhangi bir blgenin adresi atanabilir. Daha sonra * operatr ile o RAM blgesine eriilebilir. * operatrnn operand bir adres bilgisi olmak zorundadr. Yani operand adres sabiti olabilir. Dizi ismi olabilir. Bir gsterici olabilir. Adres operatr ile elde edilmi bir adres ifadesi olabilir. * operatr yalnz gstericilerle deil, her tr adres bilgisi ile (adres sabitleri ve dizi isimleri vs.) de kullanlabilir. Bu operatr operand ile belirtilen adresteki nesneye erimekte kullanlr. Bu operatr ile elde edilen deer, operand olan adreste bulunan deerdir. * operatr bir adrese uygulandnda ifade bir nesne belirtir. Nesnenin tr operand olarak kullanlan adresin tr ile ayn trdendir. rnein : int main() {
189

19 char s[] = Balkesir; putchar(*s); return 0;

* operatr operatr ncelik tablosunun 2. dzeyinde sadan sola ncelikli bulunmaktadr. rnein : *s + 1; ifadesinde nce *s yaplr. Sonra + operatr yaplr. Oysa ifade *(s + 1) biiminde olsayd nce + operatr yaplrd. Derleyiciler * operatrnn arpma operatr m yoksa adres operatr m olduunu ifade ierisindeki kullanmna bakarak anlar. arpma operatr iki operand alrken adres operatr nek konumundadr ve tek operand alr. rnek : *s * 2 ifadesinde 1. * adres operatr iken 2. * aritmetik arpma operatrdr. [] Kseli Parantez (index) Operatr : Dizi elemanlarna erimekte kullandmz keli parantez aslnda unary prefix bir gsterici operatrdr. [] ierisine tamsay trnden bir ifade yazlr. (fadenin deeri pozitif olmak zorunda deildir.) [] operatrnn operand dizi ismi olmak zorunda deildir. Bir adres bilgisi olmak zorundadr. [] operatrnn operand bir dizi ismi olabilir. Gsterici olabilir. Dier adres belirten ifadeler olabilir. p[n] ile *(p + n) tamamen edeerdir. Yani keli parantez operatr bir adresten n ilerisinin ieriini almak iin kullanlr. [] operat ile elde edilen nesnenin tr operand olan adresin tr ile ayn trdendir. rnein: #include <stdio.h> int main() { char s[] = "stanbul"; char *p; p = s + 1; putchar(p[2]); return 0; } [] operatr ncelik tablosunun en yksek dzeyinde bulunur. rnein: &p[n]

190

GSTERCLER ifadesinde nce [] yaplr, nesne elde edilir daha sonra nesnenin adresi alnr. [] ierisindeki ifadenin saysal deeri negatif olabilir. rnein p[-2] geerli bir ifadedir. #include <stdio.h> int main() { char ch = '0'; (&ch)[0] = 'b' putchar(ch); return 0; } &ch ifadesi parantez iine alnmasayd [] operatrnn ncelii sz konusu olacandan nce ch[0] yaplrd. Buradan da bir nesne elde edilemeyeceinden (& operatrnn operandnn nesne olmas gerektiinden) hata oluurdu. Adreslerle lemler / Adreslerin Artrlmas ve Eksiltilmesi (gsterici aritmetii) C dilinde adreslerle tamsay trnden saylar toplama ve karma ilemlerine sokulabilir. ki adres ayn trden ise karlatrma operatrleri ile ileme sokulabilir. Burada adreslerin saysal bileenleri ileme sokulmaktadr. C dilinde bir adres bilgisi tamsay trleri ile artrlabilir veya eksiltilebilir. Sonu ayn trden bir adres bilgisi olur. Bir adres bilgisi 1 artrldnda adresin saysal bileeni adresin trnn uzunluu kadar artmaktadr. Bu durumda rnein DOS'da char trden bir gstericiyi, 1 artrdmzda adresin saysal bileeni 1 int trden bir gstericiyi 1 artrdmzda ise adresin saysal bileeni 2 artar. Bu durumda p[n] ifadesi p adresinden n byte ilerisinin ierii deil, p adresinden n * p gstericisinin trnn uzunluu kadar byte ilerinin ierii anlamna gelir. ki adres bilgisinin toplanmas faydal bir ilem olmad gerekesiyle yasaklanmtr. Ancak iki adres bilgisi birbirinden kartlabilir. ki adres birbirinden kartlrsa sonu int trden olur. ki adres birbirinden kartldnda nce adreslerin saysal bileenleri kartlr, sonra elde edilen deer adresin trnn uzunluuna blnr. rnein a int trden bir adres olmak zere: &a[2] - &a[0] ifadesinden elde edilen deer 2'dir. #include <stdio.h> int main() { int a[5] = {1, 2, 3, 4, 5}; int *p; p = a; printf("%p adresindeki deer = %d\n", p, *p); ++p; printf("%p adresindeki deer = %d\n", p, *p); return 0;

191

19 ++ ve -- operatrlerinin gsterici operatrleriyle birlikte kullanlmas ++*p durumu x = ++*p; *p = *p + 1; anlamna gelir. *p bir nesne gsterdii iin nesnenin deeri 1 artrlacaktr. Yukardaki deyimle x deikenine *p nesnesinin artrlm deeri atanacaktr. *++p durumu p gstericisinin deeri 1 artrlr. Artrlm adresteki nesneye ulalm olur. x = *++p; deyimi ile x deikenine artrlm adresteki bilgi atanr. *p++ durumu ++ operatr ve * operatrnn ikisi de 2. ncelik seviyesindedir ve bu ncelik seviyesine ilikin ncelik yn sadan soladr. nce ++ operatr ele alnr ve bu operatr ifadenin geri kalan ksmna p gstericisinin artmam deerini gnderir. Bu adresteki nesneye ulalr ve daha sonra p gstericisinin deeri 1 artrlr. x = *p++; deyimi ile x deikenine *p nesnesinin deeri atanr daha sonra p gstericisinin deeri 1 artrlr. &x++ /* error */

x nesnesinin artmam deeri ifadenin geri kalanna gnderilir. (Adres operatrne operand olur.) Bu da bir nesne olmad iin error oluur. (Adres operatrnn operand nesne olmak zorundadr.) &++x /* error */ x nesnesinin artm deeri ifadenin geri kalanna gnderilir. (Adres operatrne operand olur.) Bu da bir nesne olmad iin error oluur. (Adres operatrnn operand nesne olmak zorundadr.) ++&x /* error */ x nesnesinin adresi ++ operatrne operand olur. ++ operatrnn operand nesne olmak zorunda olduu iin derleme zamannda error oluacaktr. & operatr ile ++ ya da -- operatrlerinin her trl kombinasyonu derleme zamannda hata olumasna neden olur.

++p[ i ] Durumu ndex operatr 1. ncelik seviyesinde, ++ operatr ise 2. ncelik seviyesindedir. Bu durumda nce derleyici tarafndan indeks operatr ele alnr. p[i] ifadesi bir nesne gsterir. Dolaysyla ++ operatrne operand olmasnda bir saknca yoktur. Sz konusu ifade p[i] = p[i] + 1; anlamna gelmektedir. Yani p[i] nesnesinin deeri 1 artrlacaktr.

192

GSTERCLER p[i]++ Durumu x = p[i]++; nce p[i] nesensinin artmam deeri retilir, ifadenin geri kalanna p[i] nesnesinin artmam deeri kullanlr. Yani yukardaki rnekte x deikenine p[i] nesnesinin artrlmam deeri atanr, daha sonra p[i] nesnesi 1 artrlr. p[++i] Durumu x = p[++i]; nce i 1 artrlr. Daha sonra artrlm adresteki bilgi x deikenine atanr. p[i++] Durumu x = p[i++]; Burada p[i] nesnesinin deeri x deikenine atanr. Daha sonra i deikeni 1 artrlr. p++[i] durumu x = p++[i]; Pek tercih edilen bir ifade deildir.Burada nce p[i] x deikenine atanr. Sonra p gstericisinin deeri 1 artrlr.

Gstericilere lk Deer Verilmesi Dier trden deikenlerde olduu gibi gstericilere de tanmlanmalar srasnda ilk deer verilebilir. Gstericilere ilk deer verme ilemi gstericinin trnden bir adres bilgisi ile yaplmaldr. rnekler: char s[100]; double x; int *ptr = (int *) 0x1A00; char * str = (char *) 0x1FC0; chat *p = s; double *dbptr = &x;

Fonksiyon Parametre Deikeni Olarak Gstericilerin Kullanlmas Bir fonksiyonun parametre deikeni herhangi bir trden gsterici olabilir. void func(int *p) { ... } Bir fonksiyonun parametre deikeni bir gsterici ise fonksiyon da ayn trden bir adres bilgisi ile arlmaldr.

193

19 Bir fonksiyonun baka bir fonksiyonun yerel deikenini deitirebilmesi iin o fonksiyonun yerel deikeninin adresini parametre olarak almas gerekir. #include <stdio.h> void func(int *p) { *p = 20; } int main() { int a = 10; func(&a); printf("%d\n", a); return 0; } Bir fonksiyon bir deer elde edip, aran fonksiyona bu deeri iletmek isterse 2 yntem kullanlabilir: 1. Elde edilen deer arlan fonksiyon tarafndan geri dn deeri olarak retilir.

2. Elde edilen deer aran fonksiyonun gndermi olduu adrese yerletirilir. Tabi bunun iin arlan fonksiyonun parametre deikeninin bir gsterici olmas gerekir. Bir rnekle gsterelim: Kendisine gnderilen bir saynn faktoriyelini hesaplayana ve bu deeri parametre olarak gnderilen adrese kopyalayan bir fonksiyon yazalm. void factorial(int n, long *p); #include <stdio.h> int main() { long a; factorial(7, &a); printf ("%d! = %ld", 7, a); return 0; } void factorial(int n, long *p); { if (n == 0 || n == 1) *p = 1; for (*p = 1; n > 0; n--) *p *= n; } a bir yerel deiken olsun. C dilinde bir fonksiyon
194

GSTERCLER

func(a); biiminde arlmsa, arlan bu fonksiyonun, a deikenini deitirme ans yoktur. (Bu tr fonksiyon armna "deer ile arma" (call by value) denmektedir. Fonksiyonun a deikenini deitirebilmesi iin func(&a); biiminde arlmas gerekir. rnein scanf fonksiyonuna & operatr ile bir nesnenin adresinin arguman olarak yollanmasnn nedeni budur. Bu ekilde fonksiyon armaya C'de "adres ile arma" (call by reference) denmektedir. int trden iki yerel nesnenin deerlerini deitirmek (swap etmek) istediimizi dnelim. Bunu bulunduumuz fonksiyon ierisinde aadaki gibi yapabiliriz: int main() { int a = 10, b = 20, temp; temp = a; a = b; b = temp; /*....*/

swap ileminin bir fonksiyon tarafndan yaplmasn istersek, aadaki gibi bir fonksiyon iimizi 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; } Yukardaki program altldnda ekrana a = 10 b = 20 yazacaktr. Zira swap fonksiyonu a ve b deikenlerinin deerlerini deitirmemitir. Zaten yerel nesneler olan a ve b deikenlerinin deerleri ancak adresleri bir fonksiyona gnderilerek deitirilebilirdi. Oysa bizim yukardaki swap fonksiyonumuz a ve b deikenlerinin deerlerini
195

19 parametre deikenleri olan x ve y deikenlerine kopyalyor. Yani deerleri deitirilen parametre deikenleri x ve y'nin deerleri. stediimiz amac gerekletirecek fonksiyon dardan adres alaca iin gsterici parametre deikenlerine sahip olmal: void swap(int *p1, int *p2) { int temp = *p1; *p1 = *p2; *p2 = temp; } Dizilerin Fonksiyonlara Gstericiler Yoluyla Geirilmesi Bir diziyi fonksiyona geirebilmek iin dizinin balang adresinin ve uzunluunun geirilmesi yeterlidir. Dizinin balang adresini alacak parametre deikeninin ayn trden bir gsterici olmas gerekir. Fonksiyonun 2. parametresi dizinin uzunluunu tutacak int trden bir deiken olabilir. Bir dizinin balang adresini parametre olarak alan fonksiyon dizi elemanlarna keli parantez operatr ya da ierik operatr ile eriebilir. Ancak dizi elemanlarnn ka tane olduu bilgisi fonksiyon tarafndan bilinemez. Bu nedenle dizi uzunluunu ikinci bir parametre olarak fonksiyona yolluyoruz. rnek: #include <stdio.h> void display (int *p, int size) { int i; for (i = 0; i < size; ++i) printf("%d ", p[i]);

int main() { int a[5] = {3, 8, 7, 6, 10}; display(a, 5); return 0;

Dizi uzunluu hakknda fonksiyona bilgi vermek amac ile baka teknikler de kullanlr. Dizi uzunluunu dizinin birinci parametresinde saklayabiliriz. Yukardaki fonksiyonu, dizi uzunluunu birinci dizi elemannda saklayarak yeniden yazalm: void disp (int *p) { int i; for (i = 1; i < p[0]; ++i) printf("%d ", p[i]);

int main() { int a[6] = {6, 3, 8, 7, 6, 10};

/* birinci eleman dizi uzunluu bilgisini saklyor */


196

GSTERCLER

disp(a); return 0; } Bir dizi ierisinde, zlen problemin koullarna bal olarak, belirli bir deerin bulunmas olanakszsa, bu deer dizi sonunu belirten bir iaret olarak kullanlabilir. Bir dizi iinde rencilerin aldklar notlar sakladmz varsayalm. rencilerin negatif bir not almalar mmkn olmadna gre dizi ierisinde negatif bir say bulunamaz. Bu durumda biz -1 gibi bir deeri dizi sonunu belirtmek amacyla kullanabiliriz. Yukardaki rnei yeniden yazalm: void display (int *p) { int i; for (i = 0; p[i] != -1; ++i) printf("%d ", p[i]); } int main() { int a[6] = {3, 8, 7, 6, 10, -1}; display(a, 5); return 0; } n elemanl int trden bir dizinin aritmetik ortalamasn bulan getavg fonksiyonunu tasarlanmas: #include <stdio.h> double getavg(int *p, int size); int main() { int s[10] = {1, 23, 45, -4, 67, 12, 22, 90, -3, 44}; double average; average = getavg(s, 10); printf("dizinin aritmetik ortalamas = %lf\n", average); return 0; } double getavg(int *p, int size) { int i; double total = 0; for (i = 0; i < size; ++i) total += p[i]; return total / size; }
197

19

size elemanl int trden bir dizi ierisindeki en byk sayya geri dnen getmax isimli fonksiyonun yazlmas: #include <stdio.h> int getmax(int *p, int size); int main() { int s[10] = {1, 23, 45, -4, 67, 12, 22, 90, -3, 44}; printf("%d\n", getmax(s, 10)); return 0;

int getmax(int *p, int size) { int i, max; max = p[0]; for (i = 1; i < size; ++i) if (max > p[i]) max = p[i]; return max;

Bubble sort yntemiyle int trden bir diziyi kkten bye sraya dizen fonksiyon rnei: #include <stdio.h> #define SIZE 10 void bsort(int *p, int size); void bsort(int *p, int size) { int i, k, temp; for (i = 0; i < size - 1; ++i) for (k = 0; k < size - 1 - i; ++k) if (p[k] > p[k+1]) { temp = p[k]; p[k] = p[k+1]; p[k+1] = temp; } } int main() { int a[SIZE] = {3, 4, 5, 8, 78,12, -2, 11, 41, -34};
198

GSTERCLER int i; bsort(a, SIZE); for (i = 0; i < SIZE; ++i) printf("a[%d] = %d\n",i a[i]); return 0;

Geri Dn Deeri Adres Trnden Olan Fonksiyonlar Bir fonksiyon parametre deikeni olarak bir gsterici alabildii gibi, adres trlerinden bir geri dn deerine de sahip olabilir. Geri dn deerinin adres olduu, fonksiyon tanmlanrken * operatr ile aadaki biimde belirtilmektedir. <adresin tr> *<fonksiyon ismi> ([parametreler]) { ... } rnein int trden bir adrese geri dnen ve parametre deikeni almayan sample isimli fonksiyon aadaki gibi tanmlanabilir: int *sample(void) { ... } Yukardaki fonksiyon tanmlama ifadesinden * atomu kaldrlrsa fonskiyon int trden bir deer dndrr. Bir adres trne geri dnen bir fonksiyonun arlma ifadesi, ayn trden bir gstericiye atanmaldr. int *ptr; ptr = sample(); gibi. Benzer ekilde: char *str; char *func(void) { ... } str = func(); ... Adrese geri dnen fonksiyonlara C programlarnda ok rastlanr. Standart C fonksiyonlarndan bir ou, adres trnden bir deer dndrr.

199

19 Bir dizinin en byk elemann bulup bu elemann deerine geri dnen getmax fonksiyonunu daha nce tasarlamtk. imdi ayn fonksiyonu en byk elemann adresine geri dnecek ekilde tasarlayalm: #include <stdio.h> int *getmax(int *p, int size); int main() { int s[10] = {1, 23, 45, -4, 67, 12, 22, 90, -3, 44}; printf("%d\n", *getmax(s, 10)); return 0; } int *getmax(int *p, int size) { int *pmax, i; pmax = p; for (i = 1; i < size; ++i) if (*pmax > p[i]) pmax = p + i; return pmax;

Gstericilere likin Uyarlar ve Olas Gsterici Hatalar Bir Gstericiye Farkl Trden Bir Adres Atanmas: Bir gstericiye farkl trden bir adres atandnda, C derleyicileri durumu pheyle karlayarak bir uyar mesaj verirler. Yani bu ilem hata (error) deil uyar gerektirmektedir. Ancak derleyici yine de farkl trden adresin saysal bileenini hedef gstericiye atar. Borland derleyicileri bu durumda aadaki uyar mesajn verirler: warning : suspicious pointer coversion in function ...... Bu ilemin faydal bir gereke ile yaplma ihtimali zayftr. Ancak bazan bilinli olarak da yaplabilir. O zaman bilinli olarak yapld konusunda derleyiciyi ikna etmek gerekmektedir. int *p; char s[ ] = "Ankara"; p = s; /* uyar */

Uyar bilinli tr dntrmesiyle kesilebilir. p = (int *) s; Tr dntrme operatr kullanlarak char trden bir adres int trden bir adrese dntrlmtr.

200

GSTERCLER Bir gstericiye farkl trden bir adres atanmas durumunda, otomatik tr dnm sonucunda, atama operatrnn sa tarafndaki ifadenin tr, atama operatrnn sol tarafndaki nesnenin trne dntrlecek, ve dntrlm olan adres bilgisi nesneye atanacaktr. rnein int trden bir gstericiye char trden bir adres atadmz dnelim: int main() { char s[20] = "Necati Ergin"; int *p; p = s; ... return 0;

Bu durumda int trden p gstericisini ierik operatr ya da indeks operatryle kullandmzda elde ettiimiz nesne char trden deil int trden olacaktr. Byle bir durumun bilinli olarak yaplm olma ihtimali azdr, ve bilinli olarak yaplmas durumunda tr dntrme operatr kullanlarak, derleyicinin verecei uyar mesaj kesilmelidir. (derleyici ikna edilmelidir) C++ dilinde bir gstericiye farkl trden bir adres atanmas durumunda, derleme zamannda hata oluacaktr. Yani bu durum uyar seviyesinden error seviyesine ykseltilmitir. Bir Gstericiye Adres Olmayan Bir Deerin Atanmas Bu da bilinli olarak yaplma ihtimali ok az olan bir ilemdir. C derleyicileri pheli olan bu durumu bir uyar mesaj ile programcya bildirirler. rnein bu uyar mesaj Borland derleyicilerinde: "nonportable pointer conversion" eklindedir. Peki bir gstericiye adres bilgisi olmayan bir deer atarsak ne olur? Yine otomatik tr dnm sz konusudur. Atama operatrnn sa tarafndaki ifadenin tr, atama operatrnn sol tarafnda bulunan nesne gsteren ifadenin trne evrilerek, atama yaplacaktr. Dolaysyla, atanan deer gstericinin trnden bir adrese evrilecek ve gstericiye atanacaktr. rnein: int main() { int k, *ptr; k = 1356; ptr = k; /* uyar */ ... return 0;

Yukardaki rnekte ptr = k; atama deyiminden sonra bellekte ptr gstericisi iin ayrlan yere 1356 yazlacaktr. Yani *ptr ifadesi ile int trden bir nesneye ulalacaktr ki bu nesne de 1356 ve 1357 adreslerinde bulunan nesnedir. Bu durumun bilinli olarak yaplmas durumunda yine tr dntrme operatr kullanlarak derleyici ikna edilmeli ve uyar mesaj kesilmelidir:

201

19 int main() { int k, *ptr; k = 1356; ptr = (int * ) k; ... return 0;

/* uyar yok */

Yazlarn Fonksiyonlara Parametre Olarak Geirilmesi Yazlar karakter dizilerinin ierisinde bulunurlar. Bir yazy fonksiyona parametre olarak geirebilmek iin yaznn yalnzca balang adresini fonksiyona geirmek yeterlidir. Yani fonksiyon yaznn (karakter dizisinin) balang adresi ile arlr. Yazy iinde tutan char trden dizinin uzunluk bilgisini fonksiyona geirmeye gerek yoktur. nk yazlar saklamak amacyla kullanlan karakter dizilerinin sonu null (ASCII karakter tablosnda 0 sra numaral karakter.) karakterle iaretlenmitir. Karakter dizileri zerinde ilem yapan algoritmalar dizinin sonunu null karakter yardmyla belirlerler. Yazlarla ilem yapan fonksiyon char trden bir gsterici ile zerinde ilem yapaca karakter dizisinin balang adresini alr. Fonksiyon, null karakter grene kadar bir dng ile yaznn btn karakterlerine eriir. str char trnden bir gsterici olmak zere yaz zerinde null karakter grene kadar ilem yapabilecek dngler yle oluturulabilir: while (*str != '\0') { ... ++str; } for (i = 0; str[i] != '\0'; ++i) { ... } rnein puts fonksiyonunun parametre deikeni char trnden bir gstericidir. puts fonksiyonunu myputs ismi ile yeniden yazalm:

#include <stdio.h> void myputs(char *str) { while (*str != '\0') { putchar(*str); ++str; } putchar('\n'); } int main() { char s[] = "Deneme";
202

GSTERCLER

myputs(s); return 0; }

Yazlarla lgili lem Yapan Fonksiyonlar Bir grup standart C fonksiyonu vardr ki, bu fonksiyonlar bir yaznn balang adresini parametre olarak alarak yaz ile ilgili birtakm faydal ilemler yaparlar. Bu fonksiyonlara string fonksiyonlar denir. String fonksiyonlarnn prototipleri string.h dosyas iindedir. Bu fonksiyonlardan bazlarn inceleyelim : strlen Fonksiyonu Bu fonksiyon bir yaznn karakter uzunluunu (ka karakterden olutuu bilgisini) elde etmek iin kullanlr. Fonksiyonun prototipi: unsigned int strlen(char *str); eklindedir. Fonksiyonun parametre deikeni uzunluu hesaplanacak yaznn balang adresidir. Fonksiyon null karakter grene kadar karakterlerin saysn hesaplar. #include <stdio.h> #include <string.h> int main() { char s[100]; printf("bir yaz giriniz : "); gets(s); printf("%d\n", strlen(s)); return 0; } standart C fonksiyonu olan strlen fonksiyonunu kendimiz yazsaydk aadaki biimlerde yazabilirdik: #include <stdio.h> unsigned strlen1 (char *str); unsigned strlen2(char *str); unsigned strlen3(char *str);

int main() { char s[100]; printf("bir yaz giriniz : "); gets(s); printf("yaznn uzunluu : %d\n", strlen1(s)); printf("yaznn uzunluu : %d\n", strlen1(s));
203

19 printf("yaznn uzunluu : %d\n", strlen1(s)); return 0; } unsigned int strlen1(char *str) { unsigned int length = 0; while (*str != '\0) { ++length; ++str; } return length;

unsigned int strlen2(char *str) { unsigned int len; for (len = 0; str[len] != '\0'; ++len) ; return len; } unsigned int strlen3(char *str) { char *ptr = str; while (*str != '\0') str++; return str - ptr;

strlen2 fonksiyonunda len deikeni hem dng deikeni hem de saya olarak kullanlmtr. null karakter '\0' saysal deer olarak 0 deerine eit olduu iin yukardaki dnglerin koul ifadelerini aadaki ekilde yazabilirdik: while (*str) for (i = 0; str[i]; ++i) Yukardaki koul ifadelerinde de *str ya da str[i] 0 deerine eit olduunda kodun ak dng dndaki ilk deyimle devam ederdi. Ancak okunabilirlik asndan null karakterle karlatrmann aka yaplmas daha uygundur. strchr fonksiyonu Fonksiyonun ismi olan strchr "string character" szcklerinin ksaltlarak birletirilmesinden elde edilmitir. strchr fonksiyonu bir karakter dizisi iinde belirli bir karakteri aramak iin kullanlan standart bir C fonksiyonudur. Prototipi string.h dosyas iinde bulunmaktadr. strchr fonksiyonunun prototipi aadaki gibidir:

204

GSTERCLER char *strchr(char *str, int ch); Bu fonksiyon, 2. parametresi olan ch karakterini, 1. parametresi olan str adresinden balayarak null karakter grene kadar arar. (Aranan karakter null karakterin kendisi de olabilir.) Fonksiyonun geri dn deeri, ch karakterinin yaz iinde bulunabilmesi durumunda ilk bulunduu yerin adresidir. Eer ch karakteri yaz iinde bulunamazsa, fonksiyon NULL adresine geri dnecektir. strchr fonksiyonunu kendimiz yazsaydk aadaki ekilde yazabilirdik: #include <stdio.h> #include <string.h> int main() { char s[100]; char *p, ch; printf("bir yaz giriniz : "); gets(s); printf("yaz iinde arayacanz karakteri giriniz : ") scanf("%c", &ch); p = strchr(s, ch); if (p == NULL) { printf("aranan karakter bulunamad\n"); else puts(p); return 0; } char *my_strchr(char *str, int ch) { while (*str != '\0) { if (ch == *str) return str; ++str; } if (ch == '\0') return str; return NULL;

Yukarda verilen main fonksiyonununda strchr fonksiyonunu ardmz yerde kendi yazdmz my_strchr fonksiyonunu ararak fonksiyonun doru alp almadn test edebiliriz. if (ch == '\0') return str; deyimleri aranan karakterin null karakter olup olmadn test etmek iin eklenmitir. while dngs yerine for dngs de kullanabilirdik; ...
205

19 int i; for (i = 0; str[i] != '\0'; ++i) if (ch == str[i]) return (str + i); ... strcpy fonksiyonu fonksiyonun ismi olan strcpy, string ve copy szcklerinin ksaltlarak birletirilmesinden elde edilmitir.Fonksiyon ikinci parametresinde tutulan adresten balayarak, NULL karakter grene kadar, (NULL karakter dahil olmak zere) tm karakterleri srasyla birinci parametresinde tutulan adresten balayarak srayla yazar. Fonksiyonun prototipi string.h dosyas iindedir. Fonksiyonun prototipi aadaki gibidir: char *strcpy(char *dest, char *source); Fonksiyonun geri dn deeri kopyalamann yaplmaya baland adrestir. (Yani dest adresi) #include <stdio.h> #include <string.h> int main() { char dest[100] = "C reniyoruz!"; char source[100]; printf("kopyalanacak yazy giriniz : "); gets(source); printf("kopyalama yaplmadan nce kopyalamann yapaca yerde bulunan yaz : \n"); puts(dest); strcpy(dest, source); printf("kopyalamadan sonra kopyalamann yapld yerde bulunan yaz : \n"); puts(dest); return 0;

strcpy fonksiyonunu kendimiz yazmak isteseydik aadaki ekilde yazabilirdik : char *_strcpy(char *dest, char *source) { int i; for (i = 0; (dest[i] = source[i]) != '\0'; ++i) ; return dest; } fonksiyon iinde kullanlan for dngsnde nce atama yaplm, daha sonra atama ifadesinin deeri (k, bu da atama operatrnn sa tarafnda bulunan deerdir) NULL karakter ile karlatrlmtr. Bylece ilgili adrese NULL karakter de kopyalandktan sonra dngden klmtr. fonksiyonu aadaki gibi de yazabilirdik : ...
206

GSTERCLER for (i = 0; source[i] != '\0'; ++i) dest[i] = source[i]; dest[i] = '\0'; ... for dngsnde indeks operatr kullanld iin, birinci parametre deikenine kopyalanan dest adresi deitirilmemi ve fonksiyonun sonunda bu dest adresi ile geri dnlmtr. Fonksiyonun tasarmnda whilde dngs kullansaydk, ve dest iindeki adresi deitirseydik, fonksiyonun dest gstericisinin ilk deeriyle geri dnebilmesini salayabilmek iin, dest gstericisindeki deeri deitirmeden nce, bu deeri baka bir gsterici iinde saklamak gerekecekti : char *_strcpy(char *dest, char *source) { char *temp = dest; while ((*source++ = *dest++) != '\0') ; return temp;

Yazlan fonksiyonlarn doru alp almadklarn ayn main fonksiyonu ile test edebiliriz. strcat fonksiyonu fonksiyonun ismi string ve concatanate szcklerinin ksaltlarak birletirilmesiyle elde edilmitir. strcat fonksiyonu bir karakter dizisinin sonuna baka bir karakter dizisinin kopyalanmas amacyla kullanlmaktadr. Fonksiyonun prototipi string.h dosyas iindedir. Fonksiyonun prototipi aadaki gibidir: char *strcat(char *s1, char *s2); strcat fonksiyonu eklemenin yaplaca ve balang adresi s1 birinci parametre deikeninde tutulan yaznn sonundaki NULL karakteri ezerek, balang adresi ikinci parametre deikeninde tutulan yazy birinci yaznn sonuna (NULL karakter de dahil olmak zere) eklemektedir. Yani ilem sonunda s1 dizisi s2 dizisi kadar bymektedir. Fonksiyonun geri dn deeri, sonuna eklemenin yapld yaznn balang adresidir. (Yani s1 adresi)

#include <stdio.h> #include <string.h> int main() { char s1[100], s2[100]; printf("sonuna ekleme yaplacak yazy giriniz : "); gets(s1); printf("girdiiniz yaznn uzunluu = %d\n", strlen(s1)); printf("eklemek istediiniz yazy giriniz : "); gets(s2); printf("eklenecek yaznn uzunluu = %d\n", strlen(s2)); strcat(s1, s2);
207

19 printf("ekleme yapldktan sonra 1. yaz : "); puts(s1); printf("ekleme yapldktan sonra yaznn uzunluu : %d\n", strlen(s1)); return 0; } strcat fonksiyonunu kendimiz yazsaydk aadaki ekilde yazabilirdik : char *_strcat(char *s1, char *s2) { char *temp = s1; while (*s1 != '\0') ++s1; while ((*s1++ == *s2++) != '\0') ; return temp;

/* strcpy(s1, s2); */

Yazlan fonksiyonun doru alp almadn ayn main fonksiyonu ile test edebiliriz.

strset fonksiyonu Standart olmayan bu fonksiyon derleyicilerin ounda bulunur. Fonksiyonun ismi string ve set szcklerinin ksaltlarak birletirilmesinden elde edilmitir. Bir karakter dizisinin belirli bir karakterle doldurulmas amacyla kullanlmaktadr. Prototipi string.h dosyas iinde bulunmaktadr. Fonksiyonun prototipi aadaki gibidir : char *strset(char *str, int ch); Fonksiyon birinci parametre deikeninde balang adresi olan yazy NULL karakter grene kadar ikinci parametre deikeninde tutulan karakterle doldurur. (yaznn sonundaki NULL karaktere dokunmaz) . Fonksiyonun geri dn deeri yine doldurulan yaznn balang adresidir. #include <stdio.h> #include <conio.h> int main() { char s[100]; int ch; printf("bir yaz giriniz :"); gets(s); printf("yazy hangi karakterle doldurmak istiyorsunuz : "); ch = getchar(); printf("\nyaznn %c karakteriyle doldurulduktan sonraki hali : %s\n", ch, strset(s, ch));

208

GSTERCLER return 0; } strset fonksiyonunu kendimiz yazsaydk aadaki ekillerde yazabilirdik : #include <stdio.h> char *_strset(char *str, int ch) { int i; for (i = 0; str[i] != '\0'; ++i) str[i] = ch; return str;

char *_strset2(char *str, int ch) { char *temp = str; while (*str != '\0') { *str = ch; ++str; } return temp; } strcmp fonksiyonu Standart bir C fonksiyonudur. Fonksiyonun ismi string ve compare szcklerinin ksaltlarak birletirilmesinden elde edilmitir. Fonksiyon iki karakter dizisini karlatrmakta kullanlr. Karlatrma, iki karakter dizisi iindeki yaznn kullanlan karakter seti tablosu gznnde bulundurularak, ncelilk ya da eitlik durumunun sorgulanmasdr. rnein : Adana yazs Ankara yazsndan daha kktr. nk eitlii bozan 'n' karakteri ASCII tablosunda 'd' karakterinden sonra gelmektedir. ankara yazs ANKARA yazsndan daha byktr. nk kk harfler ASCII tablosunda byk harflerden sonra gelmektedir. kalem yazs kale yazsndan daha byktr. strcmp fonksiyonunun string.h ierisindeki prototipi aadaki gibidir : int strcmp(char *s1, char *s2); fonksiyon 1. parametre deikeninde balang adresi tutulan yaz ile, ikinci parametre deikeninde balang adresi tutulan yazlar karlatrr. fonksiyonun geri dn deeri 1. yaz 2.yazdan daha bykse pozitif bir deere
209

19 1. yaz 2. yazdan daha kkse negatif bir deere 1.yaz ve 2. yaz birbirine eit ise 0 deerine geri dner. #include <stdio.h> #include <string.h> int main() { char s[20]; char password[ ] = "Mavi ay"; printf("parolay giriniz : "); gets(s); if (!strcmp(s, password)) printf("Parola doru!..\n"); else printf("Parola yanl!..\n"); return 0;

strcmp fonksiyonunu kendimiz yazsaydk aadaki ekillerde yazabilirdik : int _strcmp(char *s1, char *s2) { while (*s1 == *s2) { if (*s1 == '\0') return 0; ++s1; ++s2; } return *s1 - *s2; }

strrev fonksiyonu Standart olmayan bu fonksiyon derleyiclerin ounda bulunur. Fonksiyonun ismi ingilizce string ve reverse scklerinin ksaltlarak birletirilmesinden elde edilmitir. Karakter dizilerini ters evirmek amacyla kullanlr. string.h ierisinde yer alan prototipi aadaki gibidir : char *strrev(char *str); fonksiyon parametre deikeninde balang adresi tutulan yazy tersyz eder. Fonksiyonun geri dn deeri tersyz edilen yaznn balang adresidir. #include <stdio.h> #include <string.h> main() { char s[100];

210

GSTERCLER printf("ters evirilecek yazy giriniz : "); gets(s); printf("yaznzn ters evrilmi hali : \n"); puts(setrev(s)); return 0; } strrev fonksiyonunu kendimiz yazsaydk aadaki ekilde yazabilirdik : #include <string.h> #include <stdio.h> char *strrev(char *str) { int i, temp; int length = strlen(str); for (i = 0; i < length / 2, ++i) { temp = str[i]; str[i] = str[length - i - 1]; str[length - i - 1] = temp; } return str; } strncpy fonksiyonu Standart bir C fonksiyonudur. Fonksiyonun ismi ingilizce, string number copy szcklerinin ksaltlarak birletirilmesinden elde edilmitir. Fonksiyon bir yaznn (karakter dizisinin) ilk n karakterini baka bir yazya(karakter dizisine) kopyalamakta kullanlr. Fonksiyonun string.h ierisindeki prototipi aadaki gibidir : char *strncpy(char *dest, char *source, int n); fonksiyon 1. parametre deikeninde balang adresi tutulan yazya, ikinci parametre deikeninde adresi tutulan yazdan, nc parametresinde tutulan sayda karakteri kopyalar. Fonksiyonun geri dn deeri kopyalamann yaplaca adrestir. (Yani dest adresi) nc parametre olan n says eer kopyalanacak yaznn uzunluundan daha kk ya da eit fonksiyon kopyalama sonunda NULL karakteri birinci dizinin sonuna eklemez. n <= strlen(source) ise NULL karakter eklenmiyor. nc parametre olan n says eer kopyalanacak yaznn uzunluundan daha byk kopyalama sonunda NULL karakteri birinci dizinin sonuna ekler. n > strlen(source) ise NULL karakter ekleniyor. #include <stdio.h> #include <string.h> int main()
211

ise

ise fonksiyon

19 { char dest[100], source[100]; int n; printf("birinci yazy giriniz : "); fflush(stdin); gets(dest); printf("ikinci yazy giriniz : "); gets(source); printf("ikinci yazdan 1. yazya ka karakter kopyalamak istiyorsunuz : "); scanf("%d", &n); strncpy(dest, source, n); printf("kopyalamadan sonra 1. yaznn yeni ekli : "); puts(dest); return 0; } strncpy fonksiyonunu kendimiz yazsaydk aadaki ekilde yazabilirdik : #include <stdio.h> char *_strncpy(char *dest, char *source, int n) { int i; for (i = 0; i < n && source[i] != '\0'; ++i) dest[i] = source[i]; if (n > i) dest[i] = '\0'; return dest;

strncat fonksiyonu Standart bir C fonksiyonudur. Fonksiyonun ismi ingilizce string number concatanate szcklerinin ksaltlarak birletirilmesiyle elde edilmitir. Bir karakterden dizisinin sonuna baka bir karakter dizisinden belirli bir sayda karakteri kopyalamak amacyla kullanlr. string.h iinde bulunan prototipi aadaki gibidir : char *strncat(char *s1, char *s2, int n); fonksiyon 1. parametre deikeni iinde balang adresi verilen yaznn sonuna (NULL karakteri ezerek), 2. parametresinde balang adresi tutulan karakter dizisinden, 3. parametresinde tutulan tamsay adedi kadar karakteri kopyalar. fonksiyonun geri dn deeri sonuna ekleme yaplacak yaznn balang adresidir. (yani s1 adresi) fonksiyonun almasn aklayacak bir rnek aada verilmitir :

#include <stdio.h> #include <string.h>


212

GSTERCLER

int main() { char dest[100], source[100]; int n; printf("birinci yazy giriniz : "); fflush(stdin); gets(dest); printf("ikinci yazy giriniz : "); gets(source); printf("ikinci yazdan 1. yaznn sonuna ka karakter kopyalamak istiyorsunuz : "); scanf("%d", &n); strncat(dest, source, n); printf("eklemeden sonra 1. yaznn yeni ekli : "); puts(dest); return 0; } strncmp fonksiyonu Standart bir C fonksiyonudur. Fonksiyonun ismi ingilizce string number compare szcklerinin ksaltlarak birletirilmesiyle elde edilmitir. strcmp fonksiyonuna benzer, ancak bu fonksiyon iki yaznn tmn deil de, belirli bir sayda karakterlerini karlatrma amacyla kullanlr. fonksiyon 1. parametre deikeninde balang adresi tutulan yaz ile, ikinci parametre deikeninde balang adresi tutulan yazlarn, nc parametresinde tutulan saydaki karakterlerini karlatrr. fonksiyonun geri dn deeri 1. yaznn n karakteri 2.yaznn n karakterinden daha bykse pozitif bir deere 1. yaznn n karakteri 2.yaznn n karakterinden daha kkse negatif bir deere 1.yaz ve 2. yaznn n kakateri birbirine eit ise 0 deerine geri dner. /* byk harf kk harf duyarl ile (case sensitive) bir yaz iinde baka bir yazy arayan mystrstr fonksiyonu. Fonksiyon aranan yazy aramann yaplaca yaz iinde bulursa bulduu yaznn balang adresine, bulamazsa NULL adresine geri dnmektedir. */ #include <stdio.h> #include <conio.h> #include <string.h> char *mystrstr(char *s1, char *s2); int main() { char s1[100]; char s2[100]; char *ptr; clrscr(); printf("aramann yaplaca yazy girin : "); gets(s1);
213

19 printf("aranacak yazy girin :"); gets(s2); ptr = mystrstr(s1, s2); if (ptr) puts(ptr); else puts("aradnz yaz bulunamad\n"); getch(); return 0;

char *mystrstr(char *s1, char *s2) { int i, j; int lens1, lens2; lens1 = strlen(s1); lens2 = strlen(s2); for (i = 0; lens1 - i >= lens2; ++i, ++s1) { for (j = 0; s1[j] == s2[j]; ++j) if (s2[j + 1] == '\0') return s1; } return NULL;

strupr ve strlwr fonksiyonlar Standart C fonksiyonlar olmamalarna karn hemen hemen her derleyicide bulunurlar. simleri string upper ve string lower kelimelerinin ksaltlarak birletirilmesinden elde edilmitir. Bu fonksiyonlarn bir yaznn tm karakterleri iin byk harf kk harf dntrmesi yaparlar. Fonksiyonlarn geri dn deerleri parametresi ile verilen adresin aynsdr. Geri dn deerlerine genellikle ihtiya duyulmaz. Her iki fonksiyon da ingiliz alfabesinde olan harfler iin dnm yaparlar. Trke karakterler iin de dnm yapacak bir fonksiyon kullanmak istiyorsak kendimiz yazmalyz. #include <stdio.h> #include <string.h> int main() { char s[ ] = "C programcs olmak iin ok almak gerekir!"; strupr(s); puts(s); return } strupr ve strlwr fonksiyonlarn kendimiz de aadaki ekilde
214

0;

GSTERCLER

#include <stdio.h>, #include <ctype.h> char *_strupr(char *str) { char *temp = str; while (*str != '\0') { if (islower(*str)) * str = toupper(*str); ++str; } return temp;

/* if (*str >= 'a' && *str <= 'z') */ /* *str = *str - 'a' + 'A'; */

char *_strlwr(char *str) { char *temp = str; while (*str != '\0') { if (isupper(*str)) * str = tolower(*str); ++str; } return temp;

/* if (*str >= 'A' && *str <= 'Z') */ /* *str = *str - 'A' + 'a'; */

strlwr ve strupr fonksiyonlarnn trke versiyonlarn aadaki ekilde yazabiliriz : #include <stdio.h> #include <string.h> #include <ctype.h> char *struprtrk(char *str) { char trklower[ ] = ""; char trkupper[ ] = "I"; int index; char *p, *temp; temp = str; while (*str != '\0') { p = strchr(trklower, *str); if (p) { index = p - trklower; *str = trkupper[index]; } else *str = toupper(*str); ++str; }
215

19 return temp; } char *strlwrtrk(char *str) { char trklower[ ] = ""; char trkupper[ ] = "I" int index; char *p, *temp; temp = str; while (*str != '\0') { p = strchr(trkupper, *str); if (p) { index = p - trkupper; *str = trklower[index]; } else *str = tolower(*str); ++str; } return temp;

char trden bir gstericiyi NULL karaktere teleme Bir gstericiyi NULL karakteri gsterir hale getirmek iin 3 yntem kullanlabilir : char *p; i. ii. iii. p += strlen(p); p = strchr(p, '\0'); while (*p != '\0') ++ptr;

Yukardakiler iinde en hzl alabilecek kod ncsdr. 2. biim tercih edilebilir ama 1. yntem pek tercih edilmez. Aadaki dngden ktktan sonra p gstericisi NULL karakterden bir sonraki adresi gstermektedir : while (*p++ != '\0') ;

GSTERC HATALARI Gstericileri kullanarak RAM zerinde herhangi bir blgeye eriebiliriz. 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 kabilecektir. Bu bozukluk tm sistemi olumsuz ynde etkileyebilir.
216

GSTERCLER Kim tarafndan kullanldn bilmediimiz bellek blgelerine gvenli olmayan blgeler denir. Gvenli olmayan blgelere veri aktarlmasna da "gsterici hatalar" denir. Gsterici hatalar yapldnda sistem kilitlenebilir, programlar yanl alabilirler. Gsterici hatalar sonucundaki olumsuzluklar hemen ortaya kmayabilir. Gsterici hatalar gvenli olmayan blgelere eriildiinde deil oralara veri aktarldnda oluur. Gsterici hatalar derleme srasnda derleyici tarafndan tespit edilemezler. Programn alma zaman srasnda olumsuzluklara yol aarlar. tanmlama yntemiyle tahsis 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. gsterici hatas oluturan tipik durumlar 1) ilk deer verilmemi gstericilerin yol at hatalar : 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 rasgele deerlerle balatlrlar. (garbage value) Yerel bir gsterici tanmlandktan sonra, herhangi bir ekilde bu gstericiye bir deer atamas yaplmaz ise gstericinin iinde rasgele bir deer bulunacandan, bu gsterici *operatr ile ya da [ ] operatr ile kullanldnda, bellekte rasgele bir yerde bulunan bir nesneye ulalacaktr. Dolaysyla, elde edilen nesneye bir atama yapld zaman, bellekte, bilinmeyen rasgele bir yere yazlm olunur. Bu durum tipik bir gsterici hatasdr. rnekler : main() { int *p; *p = 25;

/* p gstericisinin iinde rasgele bir adres var */ /* p gstericisinin iindeki adreste buluna nesneye 25 deeri atanyor */

Yukardaki rnekte rasgele bir yere (gvenli olmayan bir yere) veri aktarlmaktadr. Verinin aktarld yerde iletim sisteminin, derleyicinin ya da bellekte kalan baka bir programn (memory resident) kodu bulunabilir. Verinin aktarld yerde programn kendi kodu da bulunabilir. lk deer verilmemi global gstericilerin iinde (ya da statik yerel gstericilerin ierisinde) sfr deeri bulunmaktadr. Sfr says (NULL adresi) gstericilerde test amacyla kullanlmaktadr. Bu adrese bir veri aktarlmas durumunda , derleyicilerin ounda istee bal olarakbu hata alma zaman (runtime) srasnda kontrol edilmektedir. rnek : char *ptr; main() { *p = 'm'; }

/* NULL pointer assignment */

Yukardaki kodun altrlmasnda "NULL pointer assignment" eklinde bir alma zaman hatasyla karlalabilir. Bu kontrol derleyicinin alabilen program ierisine yerletirdii "kontrol kodu" sayesinde yaplmaktadr.

217

19 lk deer verilmemi gstericilerin neden olduu hatalar fonksiyon armalaryla da ortaya kabilir : main() { char *ptr; gets(ptr); } Yukardaki kod parasnda gets fonksiyonu ile klavyeden alnan karakterler, bellekte rasgele bir yere yazlacaktr. gets fonksiyonu klavyeden alnan karakterleri kendisine arguman olarak gnderilen adresten balayarak yerletirdiine gre, daha nceki rnekte verilen hata klavyeden girilen btn karakterler iin sz konusudur. 2) Gvenli olmayan ilk deerlerin neden olduu gsterici hatalar Bir gstericiye ilk deer verilmesi , o gstericinin gvenli bir blgeyi gsterdii anlamna gelmeyecektir. rnein : 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 ierisinde belli amalar iin kullanlan gvenli blgeler varsa da 1FC5 byle bir blgeyi gstermemektedir. 3) dizi tamalarndan doan gsterici hatalar Bilindii gibi bir dizi tanmlamas grdnde derleyici, derleme srasnda dizi iin bellekte toplam dizi uzunluu kadar yer ayrr. C'nin seviyesi ve felsefesi gerei dizi tamalar derleyici tarafndan tespit edilmemektedir. main() { int a[10], k; for (k = 0; k <= 10; ++k) a[k] = 0; ... /* ????? */

Yukardaki rnekte dng k deikeninin 10 deeri iin de srecektir. Oysa a[10] eleman iin bir tahsisat yaplmamtr. Dizinin son eleman a[9] elemandr. Derleyici a dizisi iin a[0]'dan balayarak a[9] eleman iin bellekte toplam 10 elemanlk yani 20 byte'lk bir yer tahsis edecektir. Oysa tahsis edilmemi olan a[10] blgesinin kim tarafndan ve ne amala kullanld hakknda herhangi bir bilgi bulunmamaktadr. (Bu konuda standartlar tarafndan bir zorunluluk bulunmamakla birlikte derleyicilerin ou, ardl olarak tanmlanan elemanlar bellekte ardl olarak (contigious) olarak yerletirirler. Ama bu garanti altna alnm bir zellik deildir. Yani bunun garanti altna alnd dnlerek kod yazlmas
218

GSTERCLER problemlere yol aacaktr. Yukardaki rnekte a[10] blgesi derleyicilerin ounda a dizisinden hemen sonra tanmlanan ve dng deikeni olarak kullanlan k deikenine ayrlacaktr. Dolaysyla a[10] elemanna 0 deerini vermekle aslnda k deikenine 0 deeri verilecek ve bu da dngnn sonsuz bir dngye dnmesine yol aabilecektir.) Bilindii gibi indeks operatr de bir gsterici operatrdr, bu operatr kullanarak dizi iin tahsis edilen alann dna atama yaplabilir. C dili bu konuda bir kontrol mekanizmas getirmemektedir. Ayruca a dizisi iin a[-1], a[-2].. gibi ifadelerin de sentaks asndan geerlidir ve buraya yaplacak atamalar da gsterici hatalarna yol aacaktr. Bazan dizi tamalarna gizli bir biimde fonksiyonlar da neden olabilirler. rnein : char str[12]; ... printf("ad soyad : "); gets(str); yukardaki kod parasnda str dizisi iin toplam 12 karakterlik (12 byte) yer tahsis edilmitir. gets fonksiyonu klavyeden alnan karakterleri kendisine gnderilen adresten balayarak bellee yerletirdikten sonra, NULL karakteri de eklemektedir. O halde yukardaki rnekte programn almas srasnda 11 karakterden fazla giri yaplmas gsterici hatasna neden olacaktr. Sonlandrc karakter olan NULL karakter de ('\0') bellekte bir yer kaplayaca iin tahsis edilmi bir blge iinde bulunmas gerekir. rnein klavyeden girilen ad ve soyad : Necati Ergin olsun. gets fonksiyonu bu karakterleri aadaki gibi yerletirecektir : ... 'N' 'e' 'c' 'a' 't' 'i' '' 'E' 'r' 'g' 'i' 'n' '\0 sonlandrc ' yazlyor. ...

s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] s[8] s[9] s[10] s[11]

karakter

gvenli

olmayan

bir

yere

Grld gibi sonlandrc karakter, dizi iin tahsis edilen blgenin dna yerletirilmitir. Bu rnekte girilen isim daha uzun olsayd, tahsis edilmemi blgeye daha fazla karakter yazlacakt. Bu tr hatalarla karlamamak iin dizi yeteri kadar uzun olmal ya da standart bir C fonksiyonu olan gets fonksiyonu yerine, dizi uzunluundan daha fazla sayda eleman yerletirilmesine izin vermeyecek zel bir giri fonksiyonu yazlmal ve kullanlmaldr. stringlerle ilgili ilemler yapan standart C fonksiyonlarndan strcpy, strcat, strncpy, strncat fonksiyonlarnn yanl kullanlmas da benzer hatalar oluturabilecektir. rnein :

219

19

char s[10] = "Eskiehir"; ... strcpy(s, "Kahramanmara"); strcat(s, "li"); strncpy(s, "Kahramanmara", 15) strncat(s, "liyim", 5);

/* gsterici hatas */ /* gsterici hatas */ /* gsterici hatas */ /* gsterici hatas */

yukardaki fonksiyonlardan hepsi tahsis edilmeyen bir blgeye yazma yaptklar iin gsterici hatasna neden olacaktr.

void Gstericiler void gstericiler herhangi bir trden olmayan gstericilerdir. Bildirimlerinde void anahtar szc kullanlr. void *ptr; void *str; void gstericilerin tr bilgisi yoktur. void gstericilerde adreslerin yalnzca saysal bileenleri saklanr. Bu yzden void gstericilerle dier trden gstericler (adresler) arasnda yaplan atamalarda uyar sz konusu deildir. void gstericileri kullanrken tr dntrme operatrn kullanmaya gerek yoktur. char *str; void *p; .... str = p; p = str; /* herhangi bir uyar mesaj alnmaz */ /* herhangi bir uyar mesaj alnmaz */

Benzer biimde fonksiyon arma ifadelerinden argumanlarn fonksiyon parametre deikenlerine kopyalanmasnda da, void gstericiler sz konusu olduunda, adres trlerinin farkllndan dolay uyar mesaj alnmaz. funk1(void *p) { ... } main() { int *ptr; ... funk1(ptr); } funk2(int * p) { ...
220

/* p = ptr gibi bir atama ilemi yaplmaktadr. Uyar mesaj alnmaz. */

GSTERCLER } int main() { void *ptr; ... funk2(ptr); }

/* p = ptr gibi bir atama ilemi yaplmaktadr. Uyar mesaj alnmaz. */

void gstericiler belirli bir tre ait olmadklar iin, tr bilgisine sahip olan gstericiler zerinde uygulanan baz ilemler void trden gstericilere uygulanamazlar: 1. void trden gstericilerin * ya da [ ] operatrleriyle kullanlmalar derleme zamannda error oluturacaktr. nk bu operatrler nesneye erimek iin tr bilgisine ihtiya duyarlar. long l[50]; void *vptr; vptr = l; ... *p = 10L; ... p[2] = 25L; /* normal bir ilem. void trden gstericiye long trden bir adres atanyor. */ /* hata! void gsterici * operatryle kullanlamaz. */ /* hata! void gsterici [ ] operatryle kullanlamaz. */

2. void trden bir gstericiden bir tamsaynn toplanmas, ya da void trden bir gstericiden bir tamsaynn kartlmas derleme zamannda hata oluturur. nk pointer aritmetiine gre bir gstericiyi n kadar artrdmzda, gsterici iindeki adresin saysal bileeni n * gstericinin trn uzunluu kadar artar. void gstericilerin trleri olmad iin bu durumda saysal bileenin ne kadar artaca da bilinemez. void trden gstericiler ++ ve -- operatrlerine operand olamazlar. ++ptr; ifadesi ptr = ptr +1; ifadesine edeerdir. int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; void *ptr = &k; void *p; p = ptr + 2; /* error! void trden bir gstericiye bir tamsay toplanyor! */ ++ptr; /*error ! */ --ptr; /* error! */ ptr += 2; /*error */ ptr -= 3; /* error */ 3. Benzer ekilde, void trden iki adres birbirinden kartlamaz. (Dier trden adresler iin, ayn trden iki adresin birbirinden kartlmasnn tamamen legal oldugunu ve ifadenin deerinin pointer aritmetiine gre bir tamsay oldugunu hatrlayalm!)

221

19 void *p1, *p2; int k; ... k = p1 - p2;

/* error! void trden adresler birbirinden karlamaz! */

void gstericiler adreslerin saysal bileenlerini geici olarak saklamak amacyla kullanlrlar. Dier tr gstericiler arasndaki atama ilemlerinde uyar ya da hata oluturmadklarndan dolay, trden bamsz adres ilemlerinin yapld fonksiyonlarda parametre deikeni biiminde de bulunabilirler. rnein: void sample(void *p) { ... } sample isimli fonksiyonun parametre deikeni void trden bir gsterici olduundan bu fonksiyona arguman olarak herhangi trden bir adres bilgisi gnderilebilir. Yani sample fonksiyonu herhangi trden bir adres ile arlabilir. Bu durumda derleme zamannda bir hata olumad gibi bir uyar mesaj da alnmaz. C dilinde fonksiyonlar void trden adreslere de geri dnebilirler. Void trden adresler derleme zamannda bir hata olumakszn, ve bir uyar mesaj alnmadan herhangi trden bir gstericiye atanabildii iin, bu fonksiyonlarn geri dn deerleri herhangi trden bir gstericiye atanabilir: main() { int *p; char * str; p = sample(); /* hata olumaz ve uyar mesaj alnmaz. */ ... str = sample(); /* hata olumaz ve uyar mesaj alnmaz. */ } void *sample(); { ... }

void gstericilere ilikin uygulamalar void gstericilerin kullanmn en iyi aklayan rnek memcpy fonksiyonudur. memcpy fonksiyonu 2. parametresiyle belirtilen adresten balayarak n sayda byte birinci parametresiyle belirtilen adresten balayarak kopyalar. Fonksiyonun NULL karakterle ya da yazlarla bir ilikisi yoktur, koulsuz bir kopyalama yapar. Yani bir blok kopyalamas sz konusudur. Uygulamada en fazla, ayn trden herhangi iki dizinin kopyalanmas amacyla kullanlr. Fonksiyonun prototipi string.h balk dosyas iindedir. rnek : #include <stdio.h>
222

GSTERCLER #include <string.h> #define SIZE 10

void main() { int a[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int b[SIZE]; int i; memcpy(b, a, 2 * SIZE); for (i = 0; i < SIZE; ++i) printf(%d\n, b[i]); } aadaki fonksiyon ar da edeerdir. char s[50] = Ali; char d[50]; strcpy(d, s); memcpy(d, s, strlen(s) + 1); memcpy fonksiyonunun gsterici olan parametreleri void trdendir. nk hangi trden adres geirilirse geirilsin, derleyicilerden herhangi bir uyar mesaj alnmaz. void memcpy(void *dest, void *source, int n); ancak memcpy fonksiyonunun akk bloklarn kopyalanmasnda davran standart deildir. akk bloklarn kopyalanmasnda ayr prototipe sahip olan memmove fonksiyonu tercih edilmelidir. zellikle dizi elemanlarnn kaydrlmas gibi uygulamalarda akk bloklarn kopyalanmas sz konusu olabilir. Bu durumda memmove fonksiyonu tercih edilmelidir. Standart Cde ba mem ile balayan mem.... biiminde bir grup fonksiyon vardr. Bu fonksiyonlar trden bamsz olarak byte says ile ilem yaparlar. memset fonksiyonu prototipi : memset(void *p, char ch, int n); Bu fonksiyon 1. parametresiyle belirtilen adresten balayarak n tane byte' , 2. parametresiyle belirtilen bilgiyle doldurulur. Bu fonksiyon herhangi trden bir diziyi sfrlamak amacyla kullanlabilir. rnein : double d[100]; memset(d, 0, sizeof(d)); void gsterici parametresine sahip olan fonksiyonlarn yazlmas :

223

19 Byle bir fonksiyon yazlrken parametre deikeni olan void gsterici nce tr belirli bir gstericiye atanr, daha sonra ilemler bu gstericiyle gerekletirilir. rnek 1 : void *mymemcpy (void *dest, void *source, int n) { char *-dest = (char *) dest; char *_source = (char *) source; int i; for (i = 0; i < n; ++i) _dest[i] = _source[i]; return dest;

ayn fonksiyonu u ekilde de yazabilirdik : void *mymemcpy (void *dest, void *source, int n) { char *-dest = (char *) dest; char *_source = (char *) source; while (n-- > 0) *_dest++ = *_source++; return dest;

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]);

memset fonksiyonunun yazlmas

#include <stdio.h> void *mymemset(void *p, int x, int n); int main() { int a[10]; int i; mymemset(a, 0, 20);

224

GSTERCLER for (i = 0; i < 10; ++i) printf(%d\n, a[i]); return } void *mymemset(void *p, int x; int n); { char *_p = (char *)p; while (n-- > 0) * _p++ = x; return p;

0;

Gstericilerle lgili rnek Uygulamalar Uygulama 1 : long trden bir sayy verilen bir adrese virgllerle ayrarak string olarak yazan longtoa fonksiyonunun yazlmas. Fonksiyon gnderilen adrese geri dnecektir. #include <stdio.h> #include <string.h> #define NEGATIVE (-1) #define POSITIVE 1 char *longtoa(long val, char *str) { int i, sign; char digit; if (val < 0) { sign = NEGATIVE; val = -val; } else sign = POSITIVE; i = 0; do { digit = val % 10; str[i++] = digit + '0'; val /= 10; if (i % 4 == 3 && val) str[i++] = ','; } while (val); str[i] = '\0'; strrev(str); return str;

225

19

int main() { char str[80]; long val; printf("bir say giriniz : "); scanf("%ld", &val); puts(longtoa(val, str)); return 0; } Uygulama 2: byk harf kk harf duyarl ile (case sensitive) bir yaz iinde baka bir yazy arayan mystrstr fonksiyonu. Fonksiyon aranan yazy aramann yaplaca yaz iinde bulursa bulduu yaznn balang adresine, bulamazsa NULL adresine geri dnmektedir. #include <stdio.h> #include <conio.h> #include <string.h> char *mystrstr(char *s1, char *s2); int main() { char s1[100]; char s2[100]; char *ptr; clrscr(); printf("aramann yaplaca yazy girin : "); gets(s1); printf("aranacak yazy girin :"); gets(s2); ptr = mystrstr(s1, s2); if (ptr) puts(ptr); else puts("aradnz yaz bulunamad\n"); getch();

char *mystrstr(char *s1, char *s2) { int i, j; int lens1, lens2; lens1 = strlen(s1); lens2 = strlen(s2); for (i = 0; lens1 - i >= lens2; ++i, ++s1) {
226

GSTERCLER for (j = 0; s1[j] == s2[j]; ++j) if (s2[j + 1] == '\0') return s1; } return NULL; } Uygulama 3: klavyeden girilen bir yaz ierisindeki trke karakterlerin saysn bulan fonksiyon. #include #include #include #include <stdio.h> <conio.h> <string.h> <ctype.h>

int gettrknum(char *str); void main() { char s[100]; printf("YAZIYI GRNZ : "); gets(s); printf("\ntrke karakter says = %d", gettrknum(s));

int gettrknum(char *str) { int counter = 0; int i; char trk[ ] = ""; for (i = 0; str[i] != '\0'; ++i) if (strchr(trk, str[i])) ++counter; return counter;

Uygulama 4: Byk harf kk harf duyarl olmadan iki stringi karlatran fonksiyon. #include #include #include #include <stdio.h> <conio.h> <string.h> <ctype.h>

int mystricmp(char *s1, char *s2); void main() { char s1[100]; char s2[100];
227

19 int result; int n = 10; while (n-- > 0) { clrscr(); printf("1. yazy girin : "); gets(s1); printf("2. yazy girin :"); gets(s2); result = mystricmp(s1, s2); if (result == 0) puts("s1 = s2"); else if (result > 0) puts("s1 > s2"); else puts("s1 < s2"); getch(); }

int mystricmp(char *s1, char *s2) { while (toupper(*s1) == toupper(*s2)) { if (*s1 == '\0') return 0; ++s1; ++s2; } return toupper(*s1) - toupper(*s2); } Uygulama 5 : Kendisine balang adresi gnderilen bir yaznn iindeki boluklar atan rem_space fonksiyonu. Fonksiyon kendisine gnderilen adrese geri dnecektir. #include <stdio.h> char *rem_space(char *ptr) { int k; int indis = 0; for (k = 0; ptr[k] != '\0'; ++k) if (ptr[k] != ' ' && ptr[k] != '\t') ptr[indis++] = ptr[k]; ptr[indis] = '\0'; return ptr;

main() { char s[100];

228

GSTERCLER printf("bir yaz giriniz "); gets(s); printf("bosluksuz yaz : %s", rem_space(s)); return 0;

gstericilerle ilgili rnek uygulamalar


/* mystrstr.c byk harf kk harf duyarl ile (case sensitive) bir yaz iinde baka bir yazy arayan mystrstr fonksiyonu. Fonksiyon aranan yazy aramann yaplaca yaz iinde bulursa bulduu yaznn balang adresine, bulamazsa NULL adresine geri dnmektedir. */ #include <stdio.h> #include <conio.h> #include <string.h> char *mystrstr(char *s1, char *s2); int main() { char s1[100]; char s2[100]; char *ptr; printf("aramann yaplaca yazy girin : "); gets(s1); printf("aranacak yazy girin :"); gets(s2); ptr = mystrstr(s1, s2); if (ptr) puts(ptr); else puts("aradnz yaz bulunamadn"); getch(); return 0;

char *mystrstr(char *s1, char *s2) { int i, j; int lens1, lens2; lens1 = strlen(s1); lens2 = strlen(s2); for (i = 0; lens1 - i >= lens2; ++i, ++s1) { for (j = 0; s1[j] == s2[j]; ++j) if (s2[j + 1] == '\0')
229

19 return s1; } return NULL; }

/* trknum.c klavyeden girilen bir yaz ierisindeki trke karakterlerin saysn bulan fonksiyon */
#include #include #include #include <stdio.h> <conio.h> <string.h> <ctype.h>

int gettrknum(char *str); int main() { char s[100]; printf("YAZIYI GRNZ : "); gets(s); printf("ntrke karakter says = %d", gettrknum(s)); return 0; } int gettrknum(const char *str) { int counter = 0; int i; char trk[ ] = ""; for (i = 0; str[i] != '_; ++i) if (strchr(trk, str[i])) ++counter; return counter; }

/* stricmp.c Byk harf kk harf duyarl olmadan iki stringi karlatran bir fonksiyonun tasarm fonksiyon */ #include <stdio.h> #include <conio.h> #include <string.h> #include <ctype.h>

230

GSTERCLER int mystricmp(char *s1, char *s2); int main() { char s1[100]; char s2[100]; int result; int n = 10; while (n-- > 0) { printf("1. yazy girin : "); gets(s1); printf("2. yazy girin :"); gets(s2); result = mystricmp(s1, s2); if (result == 0) puts("s1 = s2"); else if (result > 0) puts("s1 > s2"); else puts("s1 < s2"); getch(); } return 0;

int mystricmp(char *s1, char *s2) { while (toupper(*s1) == toupper(*s2)) { if (*s1 == '_) return 0; ++s1; ++s2; } return toupper(*s1) - toupper(*s2); } /* nowords.c adresi verilen bir yazdaki kelime saysn bulan int no_of_words(char *str); fonksiyonunun tasarlanmas */ #include <stdio.h> #include <ctype.h> #define #define #define OUT IN MAX_LEN 0

1 200

int wcount(const char *str)


231

19 { int w_count = 0; int state_flag = OUT; while (*str != ' ') { if (isspace(*str) || ispunct(*str)) state_flag = OUT; else if (state_flag == OUT) { state_flag = IN; ++w_count; } ++str; } return w_count;

int main() { char s[MAX_LEN]; printf("bir yaz giriniz : "); gets(s); printf("yaznzda %d szck var!.\n", wcount(s)); return 0;

232

STRINGLER

20 . BLM :

STRINGLER

C programa dilinde iki trnak ierisindeki ifadelere string ifadeleri ya da ksaca stringler denir. rnein: "Kaan Aslan" "x = %d\n" "ltfen bir say giriniz : " ifadelerinin hepsi birer stringdir. Stringlerin tek bir atom olarak ele alndn nceki derslerden hatrlyoruz. C'de stringler aslnda char trden bir adres olarak ele alnmaktadr. C derleyicileri, derleme aamasnda bir stringle karlatnda, nce bu stringi bellein gvenli bir blgesine yerletirir, sonuna NULL karakteri ekler ve daha sonra string yerine yerletirildii yerin balang adresini koyar. Bu durumda string ifadeleri aslnda stringlerin bellekteki balang yerini gsteren char trden birer adrestir. rnein: char *p; ... p = "Nuri Ylmaz"; gibi bir kodun derlenmesi srasnda, derleyici nce "Nuri Ylmaz" stringini bellein gvenli bir blgesine yerletirir; daha sonra yerletirdii yerin balang adresini string ifadesi ile deitirir. Stringler char trden bir adres olarak ele alndna gre char trden gstericilere atanmalarnda error ya da uyar gerektiren bir durum sz konusu deildir. Stringlerin Fonksiyonlara Arguman Olarak Gnderilmesi Parametre deikeni char trden bir gsterici olan fonksiyonu char trden bir adres ile armak gerektiini biliyoruz. nk doal olarak char trden bir gstericiye char trden bir adres atanmaldr. Derleyiciler asndan stringler de char trden bir adres belirttiklerine gre, parametre deikeni char trden gsterici olan bir fonksiyonu bir string ile armak son derece doal bir durumdur, ve C dilinde bu yap ok kullanlr: puts("Necati Ergin"); Burada derleyici "Necati Ergin" stringini bellee yerletirip sonuna NULL karakteri koyduktan sonra artk bu stringi, karakterlerini yerletirdii bellek blounun balang adresi olarak grecektir. puts fonksiyonunun parametre deikenine de artk char trden bir adres kopyalanacaktr. puts fonksiyonu parametre deikeninde tutulan adresten balayarak NULL karakteri grene kadar tm karakterleri ekrana yazmaktadr. Bu durumda ekranda Merhaba yazs kacaktr. Baka bir rnek: char str[20]; ... strcpy(str, "Kaan Aslan");

233

20 Bu rnekte de "Kaan Aslan" stringi str adresinden balayarak kopyalanmaktadr. String ifadelerinin bulunduu yerde char trden bir adresin bulunduu dnlmelidir. imdi aadaki ifadeyi yorumlayalm: strcpy("Ali", "Kaan"); Derleyici derleme aamasnda iki stringi de bellekte gvenli bir blgeye yerletirir. alma zaman srasnda strcpy fonksiyonu "Ali" stringini gsteren adresten balayarak "Kaan" stringini gsteren adrese NULL karakter grene kadar kopyalama yapacana gre, bu ifadenin faydal hibir anlam olmayacaktr. stelik bu ifade, ikinci string birinciden uzun olduu iin bir gsterici hatasna da neden olur. imdi aadaki ifadeyi inceleyelim: p = "Kaan" + "Necati"; Yukardaki rnekte aslnda "Kaan" stringinin balang adresi ile "Necati" stringinin balang adresi toplanmaktadr. Peki bu toplamn yararl bir sonucu olabilir mi? Derleyiciler anlamszlndan dolay iki adresin toplanmasna izin vermezler. Fakat bir gstericiden baka bir gstericinin deerini karmann yararl gerekeleri olabilir. Bu yzden gstericilerin birbirinden kartlmas derleyicilerin hepsinde geerli bir ilemdir. Gsterici aritmetiine gre bir adresten ayn trden bir baka bir adres kartld zaman elde edilen sonu bir tamsaydr, ve bu say iki adresin saysal bileenlerinin fark deerinin adresin tr uzunluuna blnmesiyle elde edilir: #include <stdio.h> int main() { char *p = (char *) 0x1AA8; char *q = (char *) 0x1AA0; long *lp = (long *) 0x1AA8; long *lq = (long *) 0x1AA0; printf("fark1 = %d\n", p - q); printf("fark2 = %d\n", lp - lq); return 0;

Yukardaki programn altrlmasyla ekrana: fark1 = 8 fark2 = 2 yazlacaktr. C derleyicileri kaynak kodun eitli yerlerinde tamamen zde stringlere rastlasa bile bunlar iin farkl yerler ayrabilirler. Derleyici zde stringlerin sadece bir kopyasn da bellekte saklayabilir. zde stringlerin nasl saklanaca baz derleyicilerde programcnn seimine braklmtr. Derleyicinin ayarlar arasnda bu konuyla ilgili bir seenek bulunur. rnein bir programn ierisinde: printf("Dosya alamyor!..\n"); ...
234

STRINGLER printf("Dosya alamyor!..\n"); ... printf("Dosya alamyor!..\n"); ifadelerinin bulunduunu varsayalm. Farkl yerlerde "Dosya alamyor!..\n" gibi zde stringler bulunsa bile derleyici bunlar iin tekrar ve farkl yerler ayrabilir. Bu da byk uygulamalar iin bellein verimsiz kullanlmasna yol aabilir. Bellekte yer ayrma ilemi derleme aamasnda yaplmaktadr. Aadaki kod parasnn altrlmasyla ekrana hep ayn adres baslacaktr : char *p; int k; ... for (k = 0; k < 10; ++k) { p = "Necati Ergin"; printf("%p\n", p); } ... Eer yer ayrma ilemi alma zaman srasnda yaplsayd, o zaman farkl adresler baslabilirdi. Stringlerin dorudan karlatrlmas yanl bir ilemdir. ... if (Ankara == Ankara) printf(dogru\n); else printf(yanl\n); ... Yukardaki kodun altrlmas durumunda byk bir olaslkla ekrana yanl yazdrlacaktr. nk derleyicinin if parantezi iindeki karlatrma ifadesindeki iki Ankara stringinin bellekte ayr yerlere yerletirilmesi durumunda, bu iki string birbirinden farkl iki ayr char trden adres olarak deerlendirilir. Benzer bir yanllk aadaki kod parasnda da yaplmtr. char *str = Mavi ay; char s[20]; printf(parolay giriniz : ); gets(s); if (str == s) printf(dogru parola\n); else printf(yanl parola\n); s bir dizi ismidir ve derleyici tarafndan dizinin yerletirildii bloun balangc olan char trden bir adres sabiti gibi ele alnr. str ise char trden bir gstericidir. str gstericisine Mavi ay stringi atandnda, derleyici nce Mavi ay stringini bellekte gvenli bir yere yerletirir daha sonra stringin yerletirildii yerin balang adresini str gstericisine atar. program kullanann parola olarak Mavi ay girdiini varsayalm. Bu durumda if deyimi iinde yalnzca s adresiyle str gstericisinin iindeki adresin

235

20 eit olup olmad kontrol edilmektedir. Bu adresler de eit olmadklar iin ekrana "yanl parola" yazlacaktr. ki yaznn birbirine eit olup olmad strcmp fonksiyonu ile kontrol edilmeliydi: char *str = Mavi ay; char s[20]; printf(parolay giriniz : ); gets(s); if (!strcmp(str, s)) printf(dogru parola\n); else printf(yanl parola\n);

Stringlerin mrleri Stringler statik mrl nesnelerdir. Tpk global deikenler gibi programn yklenmesiyle bellekte yer kaplamaya balarlar, programn sonuna kadar bellekte kalrlar. Dolaysyla stringler alabilen kodu bytr. Birok sistemde statik verilerin toplam uzunluunda belli bir snrlama sz konusudur. Stringler derleyici tarafndan .obj modle linker tarafndan da .exe dosyasna yazlrlar. Programn yklenmesiyle hayat kazanrlar. Bir exe program 3 blmden olumaktadr: kod data stack Kod blmnde, fonksiyonlarn makine kodlar vardr. Data blmnde, statik mrl nesneler bulunmaktadr, global deikenler ve stringler bu blmde bulunurlar. Stack blm yerel deikenlerin sakland bellek alandr. Stack blm her sistemde snrl bir alandr. rnein DOS iletim sisteminde 64K byklnde bir stack sz konusudur. Yani hi bir zaman yerel deikenlerin bellekte kaplad alan 64K deerini geemez. WINDOWS iletim sisteminde default stack snr 1 MBdr. Ancak bu snr istenildii kadar bytlebilir. Daha nceki derslerimizde, bir fonksiyonun yerel bir deikenin adresi ile geri dnmemesi gerektiini sylemitik. nk fonksiyon sonlandnda yerel deikenler bellekten boaltlaca iin, fonksiyonun geri dndrd adres gvenli bir adres olmayacaktr. rnein aadaki program hataldr: char *getname(void) { char s[100]; printf(ad ve soyad giriniz : ); gets(s); return s; } int main() { char *ptr;

236

STRINGLER ptr = getname(); puts(ptr); return 0; } Fonksiyon bir stringin adresiyle geri dnebilir, bu durumda bir yanllk sz konusu olmayacaktr. nk stringler programn alma sresi boyunca bellektedir. rnein aadaki programda hatasz olarak alr: char *getday(int day) { char *ptr; switch (day) { case 0: p = Sunday; break; case 1: p = Monday; break; case 2: p = Tuesday; break; case 3: p = Wednesday; break; case 4: p = Thursday; break; case 5: p = Friday; break; case 6: p = Saturday; } return p; } Stringlerin Birletirilmesi Daha nce sylendii gibi stringler tek bir atom olarak ele alnmaktadr. Bir string aadaki gibi paralanamaz: char *ptr; ... ptr = "Necati Ergin'in C Dersi Notlarn okuyorsunuz";

/* error */

Ancak string ifadeleri bydke bunu tek bir satrda yazmak zorlaabilir. Ekrandaki bir satrlk grntye smayan satrlar kaynak kodun okunabilirlirliini bozar. Uzun stringlerin paralanmasna olanak vermek amacyla C derleyicileri yanyana yazlan string ifadelerini birletirirler. rnein: ptr = "Necati Ergin'in C Dersi" "Notlarn okuyorsunuz"; geerli bir ifadedir. Bu durumda iki string birletirilerecek ve aadaki biime getirilecektir. ptr = "Necati Ergin'in C Dersi Notlarn okuyorsunuz"; Derleyicinin iki stringi birletirmesi iin, stringlerin arasnda hibir operatrn bulunmamas gerekmektedir. : p = "Necati " "Ergin"; ifadesi ile

237

20 p = "Necati Ergin"; ifadesi edeerdir. Birletirmenin yan sra, bir ters bl karakteri ile sonlandrlarak sonraki satra gei salanabilir. rnein: ptr = "Necati Ergin'in C Dersi \ Notlarn okuyorsunuz"; ifadesi ile ptr = "Necati Ergin'in C Dersi Notlarn okuyorsunuz"; ifadesi edeerdir. Tersbl iaretinden sonra string aadaki satrn bandan itibaren devam etmelidir. Sz konusu string aadaki ekilde yazlrsa: ptr = "Necati Ergin'in C Dersi \ Notlarn okuyorsunuz"; satr bandaki boluk karakterleride stringe katlr ve sonu aadaki ifadeye edeer olur: ptr = "Necati Ergin'in C Dersi Notlarn okuyorsunuz";

Ancak ters bl karakteri ile sonraki satrn bandan devam etme standart olarak her C derleyicisinde geerli olmayabilir. atmal bir durumla karlatnzda altnz derleyicilerin referans kitaplarna bavurmalsnz. Stringlerde Ters Bl Karakter Sabitlerinin Kullanlmas Stringler ierisinde ters bl karakter sabitleri de (escape sequence) kullanlabilir. Derleyiciler stringler ierisinde bir ters bl karakteri grdklerinde, onu yanndaki karakter ile birlikte tek bir karakter olarak ele alrlar. rnein: char *p; p = "Necati\tErgin"; ifadesinde \t bir karakterdir. (9 numaral ASCII carakteri olan tab karakteri) yani printf("stringdeki karakter says = %d\n", strlen(p)); ifadesi ile ekrana stringdeki karakter says = 12 yazdrlacaktr.

238

STRINGLER String ifadelerinde dorudan ift trnak ya da ters bl karakterleri kullanlamaz, nk bu karakterlerin zek ilevi var. ift trnak karakter sabitinin kendisini ifade etmek iin ift tirnak karakterinden nce bir ters bl karakteri kullanlr. rnein: p = "\"Necati Ergin\""; gibi bir string ifadesi geerlidir. Bu durumda puts(p); ile ekrana "Necati Ergin" yazs baslacaktr. Stringlerle Gstericilere lkdeer Verilmesi Stringler kullanlarak char trden gstericilere ilkdeer verilebilir. rnein: char *p = "stanbul"; char *err = "Bellek yetersiz"; char *s = "Devam etmek iin bir tua basnz"; ... String ifadeleri aslnda char trden bir adres olduuna gre ilk deer verilen gstericilerin de char trden gstericiler olmas gerekir. Dizilere iki trnak ierisinde ilk deer vermeyle gstericilere stringlerle ilk deer verme arasndaki ayrma dikkat etmek gerekir. char *p = "Deneme"; char s[10] = "Deneme"; ifadeleri tamamen farkldr. Gstericilere ilkdeer verildiinde derleyici bunu bir string ifadesi olarak ele almaktadr. Yani string 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 kullandmz iki trnak ifadeleri adres belirtmezler (string deildireler). Dizi elemanlarna tek tek char trden sabitlerle ilk deer verme ilemi zahmetli olduu iin, programcnn iini kolaylatrmak amac ile dile bu ilk deer verme sentaks eklenmitir. Yani char s[10] = "Deneme"; aslnda char s[10] = {'D', 'e', 'n', 'e', 'm', 'e', '\0'}; ile ayn anlamdadr.

239

20 cinde Yaz Tutan char Trden Dizilerle Bir Stringi Gsteren char Trden Gstericilerin Karlatrlmas imdiye kadar alm olduumuz bilgileri deerlendirdiimizde, C dilinde bir yazy saklamak iki ayr ekilde salanabilir: 1. Yazy char trden bir dizi iinde saklamak: ... char s1[100] = Necati Ergin; char s2[100]; char s3[100]; gets(s2); strcpy(s2, s3); / * Diziye yaznn karakterleri klavyeden alnyor */ /* s3 dizisinin elemanlar s2 dizisi iine kopyalanyor */

2. Yazy bir string olarak saklayarak char trden bir gstericinin bu stringi gstermesini salamak . char *str = Necati Ergin; ki yntem birbirinin gerekmektedir: tamamen alternatifi deildir ve aadaki noktalara dikkat edilmesi

stringler statik nesneler olduklar iin programn sonlanmasna kadar bellekte yer kaplarlar. Yani bir gstericinin bir stringi gsterirken daha sonra baka bir stringi gsterir duruma getirilmesi , daha nceki stringin bellekten silinmesi anlamna gelmeyecektir. char *s = Bu string programn sonuna kadar bellekte kalacak.; s = artk yukardaki string ile bir balantmz kalmayacak...; Yaznn char trden bir dizi iinde tutulmas durumunda bu yazy deitirmemiz mmkndr. Dizi elemanlarna yeniden atamalar yaparak yazy istediimiz gibi deitirebiliriz ama stringlerin deitirilmesi tanabilir bir zellik deildir. Stringler iinde bulunan yazlar deitirilmemelidir. Aadaki kod paralar tanabilir deildir: char *s = Metin; char *str = Ankara; s[1] = ; strcpy(str, Bolu); Derleyicilerin zde stringleri ayn adreste saklamalar konusunda bir garanti yok. zde stringler ayn adreste tek kopya olarak ya da farkl adreslerde tutuluyor olabilir. Daha nce sylediimiz gibi derleyiciler bu konuda birbirinden farkl davranabilirler. Yani yukardaki rneklerde biz Metin ya da Ankara stringini deitirdiimizde, bu stringleri program iinde baska yerlerde de kullanmsak, bunlarn hepsinin deitirilmi olmas gibi bir tehlike sz konusudur. char *s = "dosya alamyor"; /* birinci string */ char a[] = "dosya alamyor"; ... printf("%s", s);
240

STRINGLER ... printf("dosya alamyor"); ... strcpy(s, "Dosya Ald"); ...

/* ikinci string */

"dosya alamyor" stringinin derleyici tarafndan ayn adreste tek kopya olarak saklandn farsayalm. s adresinde bulunan stringe yeni bir yaz kopyalannca hem birinci string hemde ikinci string deimi olur.

241

21

21 . BLM :

GSTERC DZLER

Gstericiler de birer nesne olduguna gre gsterici dizileri de tanmlanabilmelidir. Elemanlar gstericilerden oluan dizilere gsterici dizileri (pointer arrays) denir. Bildirimleri aadaki ekilde yaplr : <tr> *<dizi ismi [dizi_uzunluu];> rnein : char *s[10]; int *sample[50]; float *kilo[10]; Gsterici dizilerinin bildirimleri normal dizi bildirimlerinden farkl olarak * operatr ile yaplmaktadr. rnein: char str[100]; bildiriminde, str 100 elemanl char trden bir diziyken char *str[100]; bildiriminde ise, str 100 elemanl char trden bir gsterici dizisidir. Derleyici bir gsterici dizisi tanmlamas ile karlanca dier dizilerde yapt gibi, bellekte belirtilen sayda gsteriyi tutabilecek kadar ardl yer tahsis eder. rnein: char *p[10]; bildirimi ile derleyici, s dizisinin 10 elemanl ve her elemann char trden bir gsterici olduunu anlar. Kullanlan sistemde gstericilerin uzunluunun 2 byte olduunu kabul edersek, 20 byte srekli bir blge tahsis edecektir. Bu durumda; p[0], p[1], p[2], ...p[9] dizi elemanlarnn her biri char trden bir gstericidir. Elemanlarndan her biri dierinden bamsz olarak kullanlabilir ve her bir eleman bir nesnedir. Aadaki rnei inceleyiniz : main() { char *s[10]; int k; for (k = 0; k < 10; ++k) s[k] = (char *) malloc(30); if (s[k] == NULL) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); }

242

GSTERC DZLER printf("ismi giriniz : "); gets(s[k]);

Bu rnekte her eleman char trden bir gsterici olan, 10 elemanl bir gsterici dizisi alm ve dizinin her eleman iin, dng ierisinde malloc fonksiyonu ile 30 byte byklnde bir bellek blou tahsis edilmitir.Tahsis edilen bloklarn balang adresleri gsterici dizisinin elemanlarna yerletirildikten sonra, gets fonksiyonu ile klavyeden girilen karakterlerin tahsis edilen bloklara aktarlmtr. s isimli dizi yerel bir dizi olduuna gre, dizi elemanlar ierisinde rasgele (garbage values) deerler olduunu belirtelim. Dolaysyla malloc fonksiyonu ile yer ayrmadan yaplan veri aktarmlar gsterici hatalarna yol aacaktr. Dinamik olarak tahsis edilen alanlar, gerektii zaman yine bir dng yardmyla serbest braklabilir : ... for (k = 0; k < 10; ++k) free(s[k]); ...

gsterici dizilerine ilk deer verilmesi Normal dizilere ilkdeer nasl veriliyorsa gsterici dizilerine de ilk deer ayn biimde verilir. rnein: int *p[] = { (int *) 0x1FC0, (int *) 0x1FC2, (int *) 0x1FC4, (int *) 0x1FC6, (int *) 0x1FC8 } Kukusuz ki, gsterici dizisine atanan adreslerin bir ama dorultusunda kullanlabilmesi iin gvenli blgeleri gstermesi gerekir. Bu nedenle uygulamada karakter gsterici dizisi dndaki gsterici dizilerine ilk deer verilmesi durumuna pek raslanmaz. Gsterici dizilerine ilk deer verme ilemi genellikle karakter gsterici dizilerine string ifadeleriyle ilk deer verilmesi biiminde karmza kmaktadr.

char trden gsterici dizileri


Uygulamalarda en sk grlen gsterici dizileri char trden olan gsterici dizilerdir. Daha nce stringler konusunda da grdmz gibi, stringler C derleyicisi tarafndan char trden adresler olarak ele alndna gre, char trden bir gsterici dizisine stringler araclyla ilk deer verilebilir: char *aylar[] = {Ocak", "ubat", "Mart", "Nisan", "Mays", "Haziran", "Temmuz", "Austos",
243

21 "Eyll", "Ekim", "Kasm", "Aralk" }; aylar dizisinin her bir eleman char trden bir gstericidir. Ve bu gsterici dizisine stringler ile ilk deer verilmitir. Baka bir deyile char trden bir gsterici dizisi olan aylar dizisinin her bir eleman bir yazy gstermektedir.

char trden gsterici dizilerinin kullanlma nedenleri

1. Program iinde sk kullanlacak yazlarn her defasnda yazlmas yerine bir gsterici dizisinin elemanlarnda saklanmas. Aadai rnekte err dizisinin her bir elemannda hata mesajlarna ilikin yazlar saklanmtr. char *err[] = {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 istenirse, yazdrlacak olan yalnzca gsterici dizisinin herhangi bir elemannda balang adresi tutulan yazdr : ... if (fp == NULL) { printf("%s\n", err[5]); return 5; } .... 2. Baz yazlarn bu ekilde bir algoritmaya bal olarak kullanlmas : #include <stdio.h> int dayofweek(int day, int month, int year); char *aylar[] = {Ocak", "ubat", "Mart", "Nisan", "Mays", "Haziran", "Temmuz", "Austos", "Eyll", "Ekim", "Kasm", "Aralk" };

244

GSTERC DZLER char *gunler[] = {"Pazar", "Pazartesi", "Sal", "aramba", "Perembe", "Cuma", "Cumartesi" };

void display_date(int day, int mon, int year) { printf("%02d ", day); puts(aylar[mon - 1]); printf(" %d ", year); }

Bir yaznn belirli bir yazyla ayn olup olmadn, strcmp fonksiyonuyla saptayabiliriz. Peki bir yaznn bir grup yaz iinde var olup olmadn nasl tespit edebiliriz? Aada tanmlanan getmonth fonksiyonu kendisine balang adresi gnderilen bir yaznn ingilizce ay isimlerinden biri olup olmadn test etmekte, eer byle ise bu ayn kanc ay olduu bilgisiyle geri dnmektedir. (1 - 12) Fonksiyon, kendisine gnderilen argumanda balang adresi tutulan yaz bir ay ismine karlk gelmiyor ise 0 deeriyle geri dnmektedir.

#include <stdio.h> #include <string.h> int getmonth(char *str) { char *months[] = {"Jan", Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; int k; for (k = 0; k < 12; ++k) if (!stricmp(months[k], str) return k + 1; return 0; } main() { char s[20]; int n = 20; int result; while (n-- > 0) { printf("bir ay ismi giriniz ."); gets(s); result = getmonth(s); if (result)
245

21 printf("%s yln %d. aydr\n", s, result); else } return 0; } stricmp fonksiyonunun iki yaznn karlatrmasn byk harf kk harf duyarll olmadan yapmas dnda, strcmp fonksiyonundan baka bir fark bulunmadn hatrlayalm. Gsterici dizileri de tpk dier diziler gibi yerel ya da global olabilecektir. Eer dizinin global olmas durumunda dizi elemanlarnn hepsinin iinde 0 deerleri olurken, yerel bir gsterici dizisinin iinde rasgele deerler olacaktr. Dizinin her bir eleman iinde bir adres bilgisi olduuna gre, atama yaplmam global gsterici dizilerinin her bir eleman iinde 0 adresi (NULL adresini) bulunurken, atama yaplmam yerel gsterici dizilerinin elemanlar iinde rasgele deerler bulunmaktadr. Bir gsterici hatasna neden olmamak iin nce gsterici dizisi elemanlarna gvenli adresler yerletirmek gerekmektedir. Stringler statik nesneler gibi ele alndklar iin bir gsterici dizisi elemanlarna stringlerle deer vermek bir gsterici hatasna enden olmayacaktr. Zira stringler, daha nce de belirtildii gibi nce derleyici tarafndan bellekte gvenli bir blgeye yerletirilirler, daha sonra ise yerletirildikleri bloun balang adresi olarak ele alnrlar. printf("%s gecerli bir ay ismi deildir\n");

246

GSTERCY GSTEREN GSTERCLER

22 . BLM :
konu eklenecek.

GSTERCY GSTEREN GSTERCLER

247

23

23 . BLM :
int a[100];

DNAMK BELLEK YNETM

C dilinde bir dizi tanmland zaman, bu dizi iin derleyici tarafndan bellekte yer ayrlr. rnein:

Derleme srasnda yukardaki gibi bir dizi tanmlamas ile karlaan derleyici bellekte (eer kullanlan sistemde int tr uzunluunun 2 byte olduu varsaylrsa) toplam 200 byte yer ayracaktr. Programn almas srasnda bir dizinin uzunluunu deitirmek mmkn deildir. Diziler konusunda akland gibi, dizi tanmlama ifadelerinde dizi boyutunu gsteren ifade (keli parantezin ierisindeki ifade) sabit ifadesi olmaldr, deiken ieremez. nk derleyicinin bellekte yer ayrmas iin, dizi boyutunu bilmesi gerekir. Oysa pratikte birok uygulamada almas gereken dizinin boyutu programn almas srasnda (runtime) belirlenmektedir. Birtakm saylarn kullanlmak zere klavyeden girildiini dnelim. Kullancnn ka tane say gireceinin belli olmadn dnelim. Kullancnn girdii saylar tutmak iin alan bir dizinin boyutu ne olmaldr? Ya da bir dizindeki dosyalar sraya gre dizmek iin dosya bilgilerini geici olarak bir dizide saklayacamz dnelim. Dizinin uzunluu ne olmaldr? Bu balangta belli deildir. nk dizin iinde ka dosya olduu belli deildir. Bu tr rnekleri oaltabiliriz. Bu tip durumlara zellikle veri taban uygulamalarnda sklkla raslarz. 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 gznnde bulundurarak amak zorundadr. Bu da bellein verimsiz bir ekilde kullanlmas anlamna gelir. stelik alan diziler yerel ise ilgili blok sonlanana kadar, global ise programn almasnn sonuna kadar bellekte tutulacaktr. Oysa, dizi ile ilgili ilem biter bitmez, dizi iin ayrlan bellek blgesinin boaltlmas verimli bellek kullanm iin gereklidir. Programn alma zaman srasnda belli byklkte ardl (contigious) bir bellek blgesinin programc tarafndan tahsis edilemesine ve istenildii zaman serbest braklmasna olanak salayan yntemlere dinamik bellek ynetimi denir. C dilinde dinamik bellek ynetimi dinamik bellek fonksiyonlaryla yaplmaktadr. Dinamik bellek ynetiminde kullanlan standart C fonksiyonlar hakknda aada detayl bilgi verilmektedir. phesiz bu fonksiyonlarn dnda, ticari derleyici paketlerinin ktphanesinde, standart olmayan dinamik bellek fonksiyonlar da bulunabilir. Ancak yazlan kaynak kodun tanabilirlii asndan standart C fonksiyonlar tercih edilmelidir. imdi dinamik bellek ynetiminde kullanlan standart C fonksiyonlarn tek tek detayl olarak inceleyelim: malloc fonksiyonu malloc fonksiyonu programn alma zaman srasnda bellekten dinamik tahsisat yapmak iin kullanlr. Fonksiyonun prototipi aadaki gibidir : void *malloc(size_t nbyte); size_t trnn derleyiciye yazanlarn seimine bal olarak unsigned int ya da unsigned long trlerinden birinin yeni tr ismi olarak tanmlanmas gerektiini, ve sistemlerin hemen hemen hepsinde size_t trnn unsigned int tr olduunu hatrlayalm. Fonksiyona gnderilecek arguman tahsis edilmek istenen bloun byte olarak uzunluudur. Tahsis edilen alann srekli (contigous) olmas garanti altna alnmtr. malloc fonksiyonunun geri dn deeri tahsis edilen bellek blounun balang adresidir. Bu adres void trden olduu iin, herhangi bir trden

248

DNAMK BELLEK YNETM gstericiye sorunsuz bir ekilde atanabilir. Bu adres herhangi bir trden gstericiye atand zaman artk tahsis edilen blok gsterici yardmyla bir dizi gibi kullanlabilir. malloc fonksiyonunun istenilen blou tahsis etmesi garanti altnda deildir. Birok nedenden dolay malloc fonksiyonu baarsz olabilir. Bellekte tahsis edilmek istenen alan kadar bo bellek bulunmamas sk grlen baarszlk nedenidir. malloc fonksiyonu baarsz olduunda NULL adresine geri dner. malloc fonksiyonu bellekten yer ayrdktan sonra ileminin baarl olup olmad mutlaka kontrol edilmelidir. malloc fonksiyonu ile bellekte bir blok tahsis edilmesine ilikin aada bir rnek verilmitir.

char *ptr; ptr = (char *) malloc(30); /* bellekte 30 byte'lk bir yer tahsis edilmek isteniyor */

if (ptr == NULL) { printf("cannot allocate memory\n"); exit(EXIT_FAILURE); } /* baarszlk durumunda program sonlandrlyor */ printf("please enter the name : "); gets(ptr); /* tahsis edilen alana klavyeden giri yaplyor */ ... phesiz, malloc fonksiyonu baarsz olduunda program sonlandrmak zorunlu deiliz. Atama ve kontrol bir defada da yaplabilir. if ((ptr = (char *) malloc(30)) == NULL) { printf("cannot allocate memory\n"); exit(EXIT_FAILURE); /* baarszlk durumunda program sonlandrlyor */ } malloc fonksiyonu ile yaplan dinamik tahsisatn baarl olup olmad mutlaka kontrol edilmelidir. Fonksiyonun baarsz olmas durumunda, geri dn deerinin aktarld gsterici aracl ile bellege birey yazlmaya allrsa, atama NULL adrese yaplm olur. (NULL pointer assignment). Programn yanl almas kanlmazdr. Programclar ou kez, kk miktarlarda ve ok sayda bloun tahsis edilmesi durumunda, kontrol gereksiz bulma eilimindedir. Oysa kontrolden vazgemek yerine daha kolaylatrc yntemler denenmelidir. rnein p1, p2, p3, p4, p5 gibi 5 ayr gsterici iin 10'ar byte alan tahsis edilmek istensin. Bu durumda kontrol mantksal operatrler ile tek hamlede yaplabilir. char *p1, *p2, *p3, *p4, *p5; ... p1 = (char *)malloc(10); p2 = (char *)malloc(10); p3 = (char *)malloc(10); p4 = (char *)malloc(10); p5 = (char *)malloc(10); if (!(p1 && p2 && p3 && p4 && p5)) { printf("cannot allocate memory!..\n");

249

23 exit(EXIT_FAILURE); } malloc fonksiyonu bloklarn herhangi birinde baarsz olursa bu durum if deyimi iinde tespit edilmektedir. malloc fonksiyonunun geri dn deeri void trden bir adres olduu iin, bu adres sorunsuzca her trden gstericiye atanabilir. Ancak okunabilirlik asndan malloc fonksiyonunun geri dn deeri olan adresin, tr dntrme operatr yardmyla, kullanlacak gstericinin trne dntrlmesi tavsiye edilir. Bylece malloc fonksiyonu ile tahsis edilen bloun ne trden bir diziymi gibi kullanlaca bilgisi de aka okuyucuya verilmi olabilir. C dilinin standartlatrlmasndan nceki dnemde void trden gstericiler olmad iin, malloc fonksiyonunun geri dn deeri char trden bir adresti ve bu durumda, geri dn deeri olan adresin, char tr dnda bir gstericiye atanmas durumunda tr dnm bir zorunluluktu. int trden bir dizi iin dinamik olarak tahsisat yaptmz dnelim. rnein bir kullancnn klavyeden int trden saylar gireceini dnelim. Kullancdan, ka tane say girecei bilgisi alnsn ve istenilen miktardaki sayy saklayabilecek bir alan dinamik olarak tahsis edilsin. int main() { int number; int i; int *ptr; printf("ka tane say girmek istiyorsunuz? "); scanf("%d", &number); ptr = (int *) malloc(number * 2); ... return 0; } Yukardaki kod parasnda int tr uzunluunun 2 byte olduu varsaylmtr. Kaynak kod int tr uzunluunun 4 byte olduu bir sistemde (rnein UNIX) derlenirse problemler ortaya kacaktr. Bu tanabilirlik problemi sizeof operatrnn kullanlmasyla zlr. ptr = (int *) malloc(number * sizeof(int)); Artk allan sistem ne olursa olsun number sayda tamsay eleman iin alan tahsis edilmi olacaktr. malloc fonksiyonu birden fazla arlarak birden fazla alan tahsis edilebilir. malloc fonksiyonun farkl armlaryla tahsis edilen bloklarn bellekte ardl olmas garanti altna alnm deildir. Bu yzden fonksiyonun geri dn deeri olan adres mutlaka bir gstericide saklanmaldr. Aadaki kod paras ardk olarak arlan malloc fonksiyonlarnn ardk bellek bloklar tahsis edeceini varsayd iin yanltr. #include <stdio.h> #include <stdlib.h> int main() {
250

DNAMK BELLEK YNETM int * ptr; int i; ptr = malloc(sizeof(int) * 10); malloc(sizeof(int) * 10); for (i = 0; i < 20; ++i) ptr[i] = 5; return 0; } malloc fonksiyonu ile tahsis edilen bloun iinde rasgele deerler vardr. Aadaki kod paras ile bunu test edebiliriz. #include <stdio.h> #include <stdlib.h> int main() { int *ptr; int i; ptr = (int *) malloc(10 * sizeof(int)); if (ptr == NULL) { printf("yetersiz bellek!..\n"); exit(1); } for (i = 0; i < 10; ++i) printf("ptr[%d] = %d\n", i, ptr[i]); return 0;

/* tahsis edilen alan daha nce tahsis edilen alann hemen altnda olmak zorunda deildir */

Dinamik bellek fonksiyonlar kullanlarak tahsis edilebilecek bellek blgesi heap olarak isimlendirilmitir. heap alan donanmsal bir kavram olmayp sistemden sisteme deiebilmektedir. (C++ dilinde bu alan free store olarak isimlendirilmektedir.) malloc fonksiyonunun parametre deikeni unsigned int (size_t) trnden olduuna gre, DOS altnda en fazla 65535 byte (64K) ardl tahsisat yaplabilir. Oysa UNIX, WINDOWS ve dier 32 bitlik sistemlerde unsigned int tr 4 byte uzunluund olduuna gre, bu sistemlerde teorik olarak, malloc fonksiyonu ile 4294967295 byte (4 MB) uzunluunda ardl bir tahsisat yaplabilir.Tabi bu durum, tahsisatn yaplabilecei anlamna gelmez. calloc fonksiyonu calloc fonksiyonu malloc fonksiyonuna ok benzer. Prototipi aadaki gibidir. void *calloc(size_t nblock, size_t block_size); calloc fonksiyonu kedisine gnderilen birinci arguman ile ikinci arguman arpm kadar ardl byte' heap alanndan tahsis etmek iin kullanlr. Elemanlar int trden olan 100 elemanl bir dizi iin dinamik bellek tahsisat calloc fonksiyonu ile aadaki ekilde yaplabilir:
251

23

int *ptr; ... ptr = (int*) calloc(100, sizeof(int)); if (ptr == NULL) { printf("cannot allocate memory\n"); exit(EXIT_FAILURE); } ... phesiz yukardaki kod parasnda calloc fonksiyonu u ekilde de arlabilirdi: ptr = (int*) calloc( sizeof(int), 100); calloc fonksiyonunun malloc fonksiyonundan baka bir fark da tahsis ettii bellek blounu sfrlanmasdr. calloc fonksiyonu tarafndan tahsis edilen blounun tm byte'larnda sfr deerleri vardr. malloc fonksiyonu calloc fonksiyonuna gre ok daha sk kullanlr. Tahsis edilen blok sfrlanacaksa malloc fonksiyonu yerine calloc fonksiyonu tercih edilmelidir. rnein 50 elemanl int trden bir dizi iin bellekten bir blou dinamik olarak tahsis ettikten sonra sfrlamak istediimizi dnelim. Bu ilemi malloc fonksiyonu ile yaparsak, diziyi ayrca dng ierisinde sfrlamamz gerekecektir : int *ptr; int i; ... ptr = (int *) malloc(sizeof(int) * 100); if (ptr == NULL) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } for (i = 0; i < 100; ++i) ptr[i] = 0; ... Oysa calloc fonksiyonu zaten sfrlama ilemini kendi ierisinde yapmaktadr. calloc fonksiyonu, bellek tahsisatn yapabilmek iin, kendi iinde malloc fonksiyonunu arr. realloc fonksiyonu realloc fonksiyonu daha nce malloc ya da calloc fonksiyonlaryla tahsis edilmi bellek blounu byltmek ya da kltmek amacyla kullanlr. Prototipi dier dinamik bellek fonksiyonlarnda olduu gibi stdlib.h balk dosyas iindedir. void *realloc(void *block, unsigned newsize); realloc fonksiyonuna gnderilen birinci arguman, daha nce tahsis edilen bellek blounun balang adresi, ikinci arguman ise bloun toplam yeni uzunluudur. realloc fonksiyonu daha nce tahsis edilmi bloun hemen altnda sreklilii bozmayacak ekilde tahsisat yapamaya alr. Eer daha nce tahsis edilmi bloun aasnda istenilen uzunlukta srekli yer bulamazsa realloc, bu sefer bloun tamam iin bellekta baka bo yer aratrr. Eer istenilen
252

DNAMK BELLEK YNETM toplam uzunlukta ardl (contigious) bir blok bulunursa buray tahsis eder, eski bellek bloundaki deerleri yeni yere tar ve eski bellek blounu iletim sistemine iade eder. stenen uzunlukta srekli bir alan bulunamazsa NULL adresiyle geri dner. Aadaki programda yaplan dinamik bellek tahsisat ilemlerini inceleyelim: #include <stdio.h> #include <stdlib.h> int main() { int *ptr; ptr = (int *) malloc(sizeof (int) * 5); /* 1 */ if (ptr == NULL) { printf("bellek yetersiz!..\n"); exit(EXIT_FAILURE); } for (i = 0; i < 5; ++i) /* 2 */ ptr[i] = i; ptr = realloc(ptr, sizeof(int) * 10); /* 3 */ if (ptr == NULL) { printf("bellek yetersiz!..\n"); exit(EXIT_FAILURE);

} return 0;

1. malloc fonksiyonu ile 5 int lik yer tahsis ediliyor. Eer tahsisat ilemi baarl deilse exit fonksiyonuyla program sonlandrlyor. 1A00 1A01 1A02 1A03 1A04 1A05 1A06 1A07 1A08 1A09 ? ? ? ? ? ? ? ? ? ? <--ptr

2. Bir dng yardmyla tahsis edilen alana atamalar yaplyor. 1A00 1A01 1A02 1A03 1A04 1A05 1A06 0 1 2 3 <--ptr

253

23 1A07 1A08 1A09

3. realloc fonksiyonuyla tahsis edilen alan 10 int saylk rnein DOS altnda alyorsak 20 byte'a kartlyor. Eer realloc fonksiyonu baarsz olursa program sonlandrlyor. realloc fonksiyonu baarl olmusa iki ihtimal sz konusudur. 1. realloc fonksiyonu daha nce tahsis edilen alann altnda boalan bularak ilave tahsisat buradan yapmtr : ptr---- 1A00 --> 1A01 1A02 1A03 1A04 1A05 1A06 1A07 1A08 1A09 1A0A 1A0B 1A0C 1A0D 1A0E 1A0F 1A10 1A11 1A12 1A13 0 1 2 3 4 ? ? ? ? ?

2. realloc fonksiyonu daha nce tahsis edilen bloun altnda bo yer bulamam ve heap alannda toplam 20 bytelk baka bir bo yer bulmu olabilir. Bu durumda realloc fonksiyonu daha nce tahsis edilmi alandaki deerleri de bu alana kopyalamtr : ptr-----> 1F00 1F01 1F02 1F03 1F04 1F05 1F06 1F07 1F08 1F09 1F0A 1F0B 0 1 2 3 4 ?

254

DNAMK BELLEK YNETM 1F0C 1F0D 1F0E 1F0F 1F10 1F11 1F12 1F13 ? ? ? ?

realloc fonksiyonunun bu davranndan dolay mutlaka geri dn deeri bir gstericiye atanmaldr. Aada sk yaplan ok tipik bir hata grlmektedir: 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 fonksiyonunun baarl olmas tahsis edilen bloun altnda ilave tahsisat yapld anlamna gelmez. (realloc fonksiyonu daha nce tahsis edilmi alan baka bir yere tayarak yeni bloun balang adresiyle geri dnm olabilir. Bu durumda eski blok serbest braklr ve artk bu adresin bir gvenilirlii yoktur. Yukardaki rnekte realloc fonksiyonunun tahsisat daha nce tahsis edilen blou byterek yapt varsaylmtr. Eer realloc fonksiyonu bir tama yapmsa, yukardaki kodda bir gsterici hatas yaplm olacaktr, nk artk p gstericisinin gsterdii adres gvenilir bir adres deildir. Bu yzden uygulamalarda genellikle realloc fonksiyonunun geri dn deeri, realloc fonksiyonuna gnderilen 1. adresi iinde tutan gstericiye atanr. ptr = realloc(ptr, 100); gibi. Ancak bu bir zorunluluk deildir. Eer realloc fonksiyonu ile yaplan tahsisatn baarl olmamas durumunda program sonlandrlmayacaksa (rnein artk daha fazla tahsisat yaplamamas durumunda) daha nce dinamik olarak tahsis edilen blgedeki deerler bir dosyaya yazlacak ise, artk realloc fonksiyonunun baarsz olmas durumunda, ptr gstericisine NULL adresi atanaca iin ptr gstericisinin daha nce tahsis edilen blok ile ilikisi kesilmi olacaktr. Byle bir durumda geici bir gsterici kullanmak uygun olacaktr: temp = realloc(ptr, 100);
255

23 if (temp == NULL) printf("cannot allocate memory!..\n); Bu durumda ptr gstericisi halen daha nce tahsis edilen bloun balang adresini gstermektedir. C standartlar realloc fonksiyonu ile ilgili olarak aadaki kurallar getirmitir: Bir bellek blounu genletmesi durumunda, realloc fonksiyonu bloa ilave edilen ksma herhangi bir ekilde deer vermez. Yani eski bloa eklenen yeni blok iinde rasgele deerler (garbage values) bulunacaktr. realloc fonksiyonu eer daha nce tahsis edilmi belelk blounu bytemez ise NULL adresi ile geri dnecektir ancak tahsis edilmi olan (bytlemeyen) bellek bloundaki deerler korunacaktr. Eer realloc fonksiyonuna gnderilen birinci arguman NULL adresi olursa, realloc fonksiyonu tamamen malloc fonksiyonu gibi davranr. Yani: realloc(NULL, 100); ile malloc(100); tamamen ayn anlamdadr. (Buna neden gerek grlmtr? Yani neden malloc(100) gibi bir arm yapmak yerine realloc(NULL, 100) eklinde bir arm tercih edelim? Aadaki program inceleyelim : #include #include #include #include <stdio.h> <conio.h> <ctype.h> <stdlib.h>

void display_array(int *ptr, int size); int find_max(int *ptr, int size); int find_min(int *ptr, int size); double find_ave(int *ptr, int size); double find_stddev(int *ptr, int size); int main() { int *ptr = NULL; int ch, grade; int counter = 0; clrscr(); for (;;) { printf("not girmek istiyor msunuz? [e] [h]\n"); while ((ch = toupper(getch())) != 'E' && ch != 'H') ;
256

DNAMK BELLEK YNETM if (ch == 'H') break; printf("notu giriniz : "); scanf("%d", &grade); counter++; ptr = (int *) realloc(ptr, sizeof(int) * counter); if (ptr == NULL) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } 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 listelenmektedir :\n"); display_array(ptr, counter); printf("\nen buyuk not : %d\n", find_max(ptr, counter)); printf("en kk not : %d\n", find_min(ptr, counter)); printf("notlarn ortalamas : %lf\n", find_ave(ptr, counter)); printf("notlarn standart sapmas . %lf\n", find_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 fonksiyonu malloc gibi davranacak ve int trden 1 adet nesnelik yer tahsis edecektir. Ancak dngnn daha sonraki turlarnda realloc fonksiyonuna gnderilen adres NULL adresi olmayacandan, daha nce tahsis edilen blok dng iinde srekli olarak bytlm olacaktr. Tahsis edilen bloun serbest braklmas malloc ya da dier dinamik bellek fonksiyonlar "heap" diye isimlendirilen bir bellek blgesinden tahsisat yaparlar. Ancak heap alan da snrldr ve srekli bu fonksiyonlarn arlmas durumunda, belirli bir noktadan sonra fonksiyonlar baarsz olarak NULL adresine geri dnecektir. heap alannn bykln aadaki kod ile test edebiliriz : #include <stdio.h> #include <stdlib.h> #define BLOCK_SIZE int main() { long total = 0; char *ptr;
257

512

23

for (;;) { ptr = (char *) malloc(BLOCK_SIZE); if (ptr == NULL) break; total += BLOCK_SIZE; } printf("toplam heap alan = %ld byte\n", total); return 0; } Dinamik bellek fonksiyonlaryla tahsis edilen bir blok free fonksiyonu kullanlarak sisteme iade edilebilir. free fonksiyonunun prototipi de dier dinamik bellek fonksiyonlarnnkiler gibi stdlib.h balk dosyas iindedir: void free(void *ptr);

free fonksiyonuna gnderilecek olan arguman, daha nce malloc, calloc ya da realloc fonksiyonlaryla tahsis edilmi olan bellek blounun balang adresidir. Bu blok heap alanna iade edilmi olur. Bylece malloc, calloc ya da realloc fonksiyonunun bundan sonraki bir armnda iade edilen blok, tekrar tahsis edilme potansiyelindedir. free fonksiyonuna arguman olarak daha nce tahsis edilen bir bellek blounun balang adresi yerine baka bir adres gnderilmesi pheli kod oluturur (undefined behaviour) yaplmamaldr. char *p1; char s[100]; ptr = (char *)malloc(20); .... free(ptr) /* Legal */ free(p1) /* Bu adreste dinamik bellek fonksiyonlar ile tahsis edilmi bir alan yok. Hata. */ free(s) /* Hata s dizisi iin tahsisat dinamik bellek fonksiyonlar ile yaplmamtr. */ free fonksiyonu ile daha nce tahsis edilen blok sisteme iade edilir, ama bu bloun balang adresini tutan gsterici herhangi bir ekilde deitirilmez. Bloun balang adresini tutan, ve free fonksiyonuna arguman olarak gnderilen gsterici, free fonksiyonun arlmasndan sonra artk gvenli olmayan bir adresi gstermektedir. char *ptr = malloc(100); ... free(ptr); ... strcpy(ptr, "Necarti Ergin");

/* yanl, bir gsterici hatas!! */

Eer realloc fonksiyonuna gnderilen ikinci arguman 0 olursa, realloc fonksiyonu tamamen free fonksiyonu gibi davranr. realloc(ptr, 0); ile
258

DNAMK BELLEK YNETM

free(ptr); tamamen ayn anlamdadr. (Buna neden gerk grlmtr? Yani neden free(ptr) gibi bir arm yapmak yerine realloc(ptr, 0) eklinde bir arm tercih edelim? Dinamik Olarak Tahsis Edilen Bir Alann Balang Adresine Geri Dnen Fonksiyonlar Dinamik olarak tahsis edilen bir blok, free fonksiyonuyla serbest braklarak sisteme iade edilene kadar gvenli olarak kullanlabileceine gre , bir fonksiyon byle bir bloun balang adresine geri dnebilir. Aadaki fonksiyon tasarmn inceleyelim : #include <stdio.h> #include <string.h> #include <stdlib.h> char *getname(void) { 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); } strcpy(ptr, s); return ptr; } Yukardaki fonksiyonda kullancdan isim nce kullancdan, yerel bir diziye alnyor. Daha sonra strlen fonksiyonu kullanlarak ismin uzunluu tespit ediliyor ve malloc fonksiyonu ile bu ismi ve sonuna gelecek NULL karakteri iine alacak byklkte bir alan dinamik olarak tahsis ediliyor. Daha sonra da strcpy fonksiyonu kullanlarak, isim dinamik olarak tahsis edilen alana kopyalanayor ve bu bloun balang adresine geri dnlyor. Dinamik tahsisat fonksiyonun arlmas neticesinde yaplacak ve tahsis edilen bloun serbest braklmas fonksiyonu arann sorumluluunda olacaktr : int main() { char *p; ... p = getname(); ... free(p) ... return 0; }

259

23 Bir fonksiyon iinde dinamik olarak bir bellek blou tahsis etmek ve daha sonra bu bloun balang adresine geri dnmek C dilinde ok kullanlan bir tekniktir: Aada tasarm verilen strcon fonksiyonu birinci parametresinde balang adresi tutulan yaznn sonuna ikinci parametresinde balang adresi tutulan yazy kopyalayacak fakat her iki adresteki yazlar da bozmadan, birletirilmi yaznn balang adresi olan bir adrese geri dnecektir. Bu dinamik bellek fonksiyonlarnn kullanlmasyla mmkndr : #include <stdio.h> #include <string.h> #include <stdlib.h> char *strcon(const char *s1, const char*s2) { char *ptr; ptr = malloc (strlen(s1) + strlen(s2) + 1); if (ptr == NULL) { printf(cannot allocate memory..\n"); exit(EXIT_FAILURE); } strcpy(ptr, s1); strcat(ptr, s2); return ptr;

260

BELRLEYCLER

24 . BLM :

BELRLEYCLER

Belirleyiciler (specifiers), bildirimler yaplrken kullanlan ve nesnelerin ikincil zellikleri hakknda derleyicilere bilgi veren anahtar szcklerdir. Belirleyicileri yer belirleyicileri (storage class specifiers) ve tr belirleyicileri (type qualifiers) olarak iki grupta inceleyeceiz. Yer belirleyicileri genel olarak nesnelerin tutulduklar yerler hakknda bilgi verirler. Tr belirleyicileri ise nesnelerin iindeki deerlerin deitirilip deitirilmeyeceine ilikin bilgi verirler. C'de 4 tanesi yer belirleyici (auto, register, static, extern) ve 2 tane tr belirleyici (const, volatile) vardr. Bu belirleyici isimleri C dilinin birer anahtar szcdr. Yer ve Tr Belirleyicileriyle Bildirim lemi Bildirimin genel biimi: [yer belirleyici] [tr belirleyici] <tr> nesne1, [nesne2], [nesne3], ... auto const register volatile static extern Yer belirleyici, tr belirleyici ya da tr ifade eden anahtar szcklerin dizilimi herhangi bir biimde olabilir. auto const unsigned long int a; const auto unsigned long int a; unsigned long int auto const a; int const long auto unsigned a; Yukardaki bildirimlerin hepsi geerli bildirimlerdir. Ancak okunabilirlik asndan bildirimlerin yukardaki genel biime uygun olarak yaplmas tavsiye edilir. Eer bir bildirimde yer ya da tr belirleyicileri bir tr bilgisi olmadan kullanlrsa, nceden seilmi (default) olarak int tr bildirimin yapld kabul edilir. const a; /* const int a; bildirimi ile edeerdir. */ imdi yer ve tr belirleyicilerini tek tek detayl olarak inceleyelim : auto Belirleyicisi auto yalnzca yerel deikenler iin kullanlabilecek bir yer belirleyicisidir. auto belirleyicisinin global deikenlerin ya da fonksiyonlarn parametre deikenlerininin bildiriminde kullanlmas derleme zamannda hata oluturur. Bu anahtar szck, nesnenin faaliyet alan bittikten sonra kaybolacan, bellekte kaplad yerin geerlilii kalmayacan gsterir. Yerel deikenler bulunduklar blok icra edilmeye balandnda yaratlyorlar, sz konusu bloun icras bittikten sonra yok oluyorlard. te auto belirleyicisi bu durumu vurgulamak iin kullanlmaktadr. 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. {
261

24 auto int a; float b;

auto yer belirleyicisi global deikenlerle ya da parametre deikenleriyle birlikte kullanlmaz. rnein : auto int a; /* hata nk global bir deiken auto olarak bildirilemez */ function(auto int x) /* hata parametre deikeni auto biiminde bildirilemez */ { auto int var; /* yereldeiken auto olabilir */ int x; /*yerel deiken auto belirleyicisi kullanlmasa da auto olarak ele alnr*/ ... } auto anahtar szc baz mikroilemcilerde uyumu korumak iin dnlmtr. Modern sistemlerde anlaml bir kullanm yoktur. register belirleyicisi register belirleyicisi, deikenin bellekte deil de CPU yazmalarnn ierisinde tutulacan belirten bir anahtar szcktr. Deikenlerin bellek yerine yazmalar ierisinde tutulmas programn almasn hzlandrr. Yazma (register) nedir? Yazmalar CPU (central processing unit) ierisinde bulunan tampon bellek blgeleridir. CPU ierisindeki aritmetik ve mantksal ilemleri yapan birimin yazmalar ve belleklerle ilikisi vardr. Genel olarak CPU tarafndan yaplan aritmetik ve mantksal ilemlerin her iki operand da bellee ilikin olamaz. rnein bellekte bulunan sayi1 ve sayi2 ile gsterdiimiz 2 sayiyi toplayarak sayi3 ile gsterdiimiz baka bir bellek blgesine yazmak isteyelim. Bu Cdeki sayi3 = sayi1 + sayi2; ilemine karlk gelmektedir. 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 data3 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 gerekmektedir. 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 mesajlar vermezler. Yani derleyiciler, tutabilecekleri yazma saysndan fazla register belirleyicisine sahip deikenlerle karlatklarnda bunlara ilikin register belirleyicilerini dikkate almazlar. register belirleyicileri ancak yerel ya da parametre deikenleri ile kullanlabilir global deikenler ile kullanlamazlar. rnekler
262

BELRLEYCLER

register int x; /* hata x deikeni global register belirleyicisi ile kullanlamaz */ int sample (register int y) { register float x; ... } /* hata deil */ /* hata deil */

Ne kadar deikenin yazmalarda saklanabilecei bilgisayar donanmlarna ve derleyicilere baldr. Ayrca, uzunluu tamsay (int) trnden byk olan trler genellikle yazmalarda saklanamazlar bu durumlarda da derleyicilerden hata veya uyar mesaj beklenmemelidir. Sonu olarak register belirleyicisi hzn nemli olduu ok zel ve ksa kodlarda ancak birka deiken iin kullanlmaldr. Modern derleyicilerin ou (seime bal) kod optimizasyonu yaparak baz deikenleri yazmalarda saklayabilirler. Bu durum da ou zaman register anahtar szcnn kullanlmasn gereksiz klar. static Belirleyicisi static belirleyicisine sahip deikenler programn alma sresince bellekten kaybolmazlar. Bir bakma static belirleyicisi auto belirleyicisinin zt anlamlsdr. static belirleyicisi ancak yerel ya da global deikenlere birlikte kullanlabilirler. static belirleyicisi parametre deikenleriyle kullanlmazlar. static anahtar szcnn global ve yerel deikenlerle birlikte kullanlmas farkl anlamlara gelir bu yzden bu durumlar ayr ayr inceleyeceiz: static Anahtar Szcnn Yerel Deikenlerle Kullanlmas static yer belirleyicisine sahip olan yerel deikenler programn icras boyunca bellekte kalrlar. Baka bir deyile, static anahtar szc yerel deikenlerin mrn uzatmaktadr. Statik yerel deikenler tpk global deikenler gibi programn almaya balamasyla yaratlrlar ve programn icras bitene kadar da bellekte tutulurlar. Statik yerel deikenler programc tarafndan ilk deer verildikten sonra kullanlrlar. lk deer verme ilemi programn almas srasnda deil, derleme zamannda derleyici tarafndan yaplr. (Derleyici bellekten yer ayrlmasna yol aacak makine kodunu oluturur ve statik deiken iin bellekte yer programn yklenmesi srasnda ayrlr. "Derleyici tarafndan bellekte yer ayrlr." derken bunu kastediyoruz. Derleme zamannda gerekten bellekte yer ayrlmyor, bellekte yer ayrma iini yapacak makine kodu yaratlyor.) Statik yerel deikenler ilk deerleriyle birlikte bellee yklenirler. rnek : int statfunc(void) { static int x = 12; /* bu ksm yalnzca derleme srasnda ve bir kez ilem grr */ ++x; /* fonksiyonun her arlnda yeniden yaratlmaz ve deerini korur */ return x; } void main() { printf(a = %d\n, statfunc()); printf(a = %d\n, statfunc()); }

/* a = 13 */ /* a = 14 */

263

24 Yukarda verilen rnekte statfunc() fonksiyonunun ierisindeki yerel x deikeninin deeri fonksiyonun her arlnda bir artrlr. nk x statik yerel deiken olduu iin, fonksiyonun her arlnda yeniden yaratlmayacak, en son ald deeri koruyacaktr. Daha nce sylendii gibi, bir adres deerine geri dnen fonksiyonlar yerel deikenlerin adreslerine geri dnmemelidir. Ancak yerel deikenin statik olarak tanmlanmas durumunda, statik bir yerel deiken programn sonlanmasna kadar bellekteki yerini koruyacandan, fonksiyonun statik bir yerel deikenin adresine geri dnmesinde bir saknca yoktur: char * funk(void) { static char ch; ... return &ch; } Diziler bir ya da daha fazla nesnenin bir arada tanmland veri yaplar olduguna gre, yerel diziler de static anahtar szcyle tanmlanabilirler. func(void) { static aylar[12] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}; ... } Yukardaki rnekte, func() fonksiyonu iinde aylar dizisi static anahtar szcyle tanmland iin, bir kez ilk deer verildikten sonra her arld zaman bu deerleri koruyacak ve varln programn sonlanmasna kadar devam ettirecektir. Oysa static anahtar szc kullanlmasayd, bu dizi func() fonksiyonun her arlmasyla yeniden yaratlacak ve tm ilk deer atamalar her defasnda yeniden yaplacakt. Bu da fonksiyonun her arlmasnda belirli bir makine zamannn tekrar harcanmasna neden olacakt. Statik Yerel Deikenler le Global Deikenlerin Karlatrlmas Statik yerel deikenler mr asndan global yerel deikenler ile ayn zellikleri gsterirler. Yani programn almaya balamas ile yaratlrlar ve programn almas bitince mrleri sona erer. Ama faaliyet alan (scope) asndan farkllk gsterirler. Global deikenler dosya faaliyet alanna uyarken statik yerel deikenler blok faaliyet alanna uyarlar. Statik yerel deikenlerin faaliyet alanlarnn dar olmas (blok faaliyet alanna sahip olmas) soyutlamay (abstraction) ve yeniden kullanlabilirlii (reusability) kuvvetlendirmektedir. nk global deikenlere bal olan fonksiyonlar projeden projeye tanamazlar ve kendi ilerinde bir btnlk oluturmazlar. static Anahtar Szcnn Global Deikenler le Kullanlmas static belirleyicisi global bir deiken ile birlikte kullanlrsa, global deikenin faaliyet alann, yalnzca tanmland modl kapsayacak biimde daraltr. Yani global deikenleri kendi modlleri iine hapseder. Statik global deikenler yalnzca tanmlandklar modl ierisinde faaliyet gsterebilirler. Statik global deikenleri daha iyi anlayabilmek iin modl kavramnn ne olduunu bilmek zorundayz: Bir proje birbirinden bamsz olarak derlenebilen birden fazla kaynak dosyadan oluabilir. Projelerin bamsz olarak derlenebilen herbir kaynak dosyasna modl denir. Bir projeyi oluturan farkl kaynak dosyalar birbirinden bamsz olarak derlendikten sonra, hepsi birlikte balayc (linker) ile birletirilerek tek bir .exe dosyas oluturulur.
264

BELRLEYCLER Byk Projelerin Modllere Ayrlmasnn Ne Gibi Faydalar Vardr Eer btn modller tek bir kaynak kod ierisinde 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. Global bir deiken normal olarak tm modller ierisinde faaliyet gsterebilirken (Bu durumda, bir sonraki konuda aklayacamz gibi dier, modllerde extern bildiriminin yaplm olmas gerekmektedir.) statik global deikenler yalnzca tanmlandklar modl ierisinde faaliyet gsterirler. Global deikenler iin statik tanmlamasnn yalnzca faaliyet alan zerinde etkili olduuna, mr zerinde etkili olmadna dikkat ediniz. Farkl modllerde ayn isimli iki statik global deiken tanmlanabilir. Cde ayn faaliyet alanna sahip ayn isimli birden fazla deiken tanmlanamaz kuraln anmsayalm. ki farkl modlde tanmlanan ayn isimli iki statik global deikenin faaliyet alan farkldr.

extern Belirleyicisi ve Balant Kavram (Linkage) extern belirleyicisini aklamadan nce linkage (balant) kavram zerinde durmak istiyoruz. lk derslerimizde nesnelerin zelliklerinden birinin de balant (linkage) zellii olduunu sylemitik. C standartlar ayr balant snf tanmlamtr : 1. D Balant (external linkage)

Bir modlde tanmlanan bir nesneye program oluturan baka modllerde de ulalabilmesi zelliidir. Eer bir deikene baka bir modlde de tannabiliyorsa o deikenin d balants var denir. 2. i balant (internal linkage) Bir modlde tanmlanan bir nesneye yalnzca kendi modl ierisinde ulalabilmesi zelliidir. Eer bir deiken ancak kendi modl ierisinde tannabiliyorsa, dier modllerde tannamyorsa, o deikenin i balants var denir. 3. balantsz (no linkage) Bir modlde tanmlanan bir nesneye, o modl iinde yalnzca belirli bir blok iinde ulalabilmesi zelliidir. Eer bir deiken ancak kendi modl ierisinde belirli bir blok dahilinde tannabiliyor, bu blok dnda kendi modl iinde tannmyorsa, o deikenin balants yoktur denir. Global deikenlerin d balants varken, statik global deikenlerin i balants vardr. Yerel deikenlerin balants yoktur. Btn belirleyiciler iinde belki de anlalmas en g olan extern belirleyicisidir. extern belirleyicisi genel olarak, derleyiciye nesnenin baka bir modlde tanmlandn bildirmek iin kullanlr Bir proje MOD1.C ve MOD2.C biiminde iki modlden olumu olsun : MOD1.C int a; MOD2.C int fonk2() {

265

24 float fonk1() { ... a = 100; ... } main() { ... ... } MOD1.C modlnde tanmlanm olan a deikeni global bir deikendir ve dosya faaliyet alanna uyduu iin normal olarak proje iindeki dier modllerde de faaliyet gsterebilir. MOD2.C modlnde de faaliyet gsterebilir. Fakat iki modln ayr ayr derlendii yukardaki rnekte problemli bir durum sz konusudur. nk MOD2.C modlnn derlenmesi srasnda derleyici a deikeninin MOD1.C modl ierisinde global olarak tanmlandn bilemez. MOD2.C modln derlerken a deikeni hakknda bilgi bulamayan derleyici bu durumu hata (error) olarak belirler. (C dilinde bir deiken ancak nceden bildirilmise kullanlabilir.) extern belirleyicisi derleyiciye, ilgili global deikeninin kendi modl ierisinde deil de bir baka modl ierisinde tanml olduunu bildirme amacyla kullanlr. MOD2.C modlndeki a deikeninin extern olarak bildirilmesi durumunda sz konusu problem ortadan kalkar. MOD2.C extern int a; /*extern bildirimiyle a deikeninin baka bir modlde tanmlanm olduu belirtiliyor*/ int fonk2() { ... a = 300; ... } extern bildirimini gren derleyici deikenin projeye ait baka bir modlde tanmlandn varsayarak hata durumunu ortadan kaldrr. Ancak derleyici makine kodu retirken extern olarak bildirilmi bir deikenin bellekteki yerini tespit edemeyeceinden , bu ilemi btn modlleri gzden geirecek olan balayc programa brakr. Bylece deikenin tanmland modl bulup, extern olarak bildirilmi olanlarla ilikilendirme ilemi balayc (linker) tarafndan yaplmaktadr. Yani extern belirleyicisi ile programc derleyiciye, derleyici ise balaycya bildirimde bulunmaktadr. extern belirleyicisini gren derleyici bellekte bir yer ayrmaz. extern bildirimi bir tanmlama deildir, bildirimdir. Aslnda yalnz deikenler iin deil fonskiyonlar iin de extern belirleyicisinin kullanlmas sz konusudur. C derleyicileri kendi modlleri ierisinde tanmlanmadklar halde arlan (standart C fonksiyonlar gibi) fonksiyonlar otomatik olarak extern kabul ederler. Bu nedenle fonksiyon prototiplerinde ayrca extern belirleyicisini yazmaya gerek yoktur. nk derleyici tarafndan yazlm varsaylr. ... a = 300;

/* HATA */

266

BELRLEYCLER rnein yukarda verilen rnekte MOD2.c modlnde bulunan y1 fonksiyonu ierisinde bulunan x1 fonksiyonu arlyor olsun: MOD1.c int a; float x1() { ... a = 100; ... } main() { ... } MOD2.c extern int a; extern float x1(void); int y1() { float f; ... f = x1(); a = 300; ... }

MOD2.c modlnde x1 fonksiyonu iin yazlm olan prototip ifadesini inceleyiniz: extern float x1(void); Bu rnekte x1 fonksiyonu baka bir modlde tanml olduu iin prototip ifadesine extern belirleyicisi konmutur. Ancak konulmasayd derleyici zaten extern varsayacakt. (Tpk yerel deikenler iin auto belirleyicisini varsayd gibi.) 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 kacaktr. extern belirleyicisinin tek bir modl sz konusu olduunda ama d bir kullanm vardr. Aadaki rnekte main fonksiyonu ierisindeki global x deikeni, tanmlanmadan nce kullanldndan hataya neden olmaktadr. void main() { ... x = 100; } int x; int fonk() { .... x ... } /* x global bir deiken ama tanmlanmasndan nce yukarda kullanlm */

200;

267

24 yukardaki kod derlendiinde , main fonksiyonunun ierisindeki x deikeninin bildiriminin bulunamadn ifade eden bir hata mesajyla karlalr. Bu durumda, eer bir global deiken tanmlamadan nce kullanlyorsa, hata olumamas iin daha nce extern bildiriminde bulunulmaldr. extern int x; void main() { ... x = 100; } int x; int fonk() { .... x ... }

200;

extern bildirimini bu ekilde kullanmak yerine, global deikeni programn en banda tanmlamak daha iyi bir tekniktir.

extern bildirimlerinin yapl yerleri extern bildirimleri kaynak kodun herhangi bir yerinde yaplabilir. Global ya da yerel olmas szkonusu deilldir. Bildirimin yapld yerden dosya sonuna kadarcolan blge iin geerlidir. Ancak extern bildirimlerinin programn tepesinde ya da programcya ait bir balk dosyasnn iinde yaplmas daha uygun olur.

const belirleyicisi const ilk deer atandktan sonra nesnenin ieriinin deitirilemeyeceini anlatan tr belirleyici, bir anahtar szcktr. Yerel, global ve parametre deikenleriyle birlikte kullanlabilir. rnein : const double PI = 3.14159265 main() { const int i = 10; i = 100; } Bir deiken const belirleyicisi ile tanmlanacaksa ilk deer verilmelidir. Aksi halde const belirleyicisi kullanmann bir anlam kalmaz. Aadaki rnei inceleyiniz. /* hata */ /*Geerli */

268

BELRLEYCLER void sample (void) { const int a; /* anlamsz */ } Bu rnekte a yerel bir deikendir, dolaysyla rastgele bir deere sahiptir. eriini bir daha deitiremeyeceimize gre tanmlanmasnn da bir anlam olamaz. const belirleyicisinin kullanm amac ne olabilir diye dnebilirsiniz? Sklkla u merak edilir: Eer const belirleyicisi koruma amal olarak kullanlyorsa kim kime kar korunuyor? const belirleyicisinin iki yararl ilevi vardr.: Okunabilirlii artrr. nk program inceleyen bir kii const belirleyicisine sahip deikenin deerinin bir daha deitirilemeyeceini dnerek daha fazla bilgi edinir. Yanllkla nesnenin deerinin deitirilmesi engellenir. const belirleyicisi deeri hi deimeyecek sabitler iin kullanlmaldr. const bildirimlerinin nesne yarattna nesnenin yalnzca okunabildiine (read only) dikkat ediniz. NOT : C++da const belirleyicisi zorunluluk olmadka nesne yaratmaz. (#define nilemci komutu gibidir, ancak derleme aamasnda ilem grr) const anahtar szcnn gsterici tanmlamalarnda kullanlmas : const anahtar szcnn gstericilerle birlikte ayr kullanlma biimi vardr. Kullanlan her biimde tanmlanan gstericinin zellii deiecektir : 1. const anahtar szcnn gsterici tanmlamasnda ilk szck olarak kullanlmas. rnekler : double dizi[20]; int k; const char *str; const int *ptr; const

double

*dptr;

Bu durumda gstericinin gsterdii nesne (yani gsterici iindeki adreste yer alan nesne) deitirilemez ancak gstericinin kendi deeri deitirilebilir. Bu tr bir tanmlamadan sonra gstericinin gsterdii yerdeki nesnenin deitirilmeye allmas derleme zaman hatas ile neticelenecektir: *str = 'A'; /* error */ ptr[2] = 25; /* error */ *dptr = 4.5; /* error */ str = (char *)0x1FFF0; /* legal */ ptr = &k; /* legal */ dptr = dizi; /* legal */ const anahtar szcnn gstericilerle birlikte kullanlmasnda en sk grlen biim budur ve zellikle fonksiyonlarn parametre deikenleri olan gstericilerle bu biim kullanlr: void func(const char *s)
269

24 { ... } Yukarda func fonksiyonunun parametre deikeni olan s gstericisi deerini fonksiyonun armasyla alacaktr. Fonksiyon arma ifadesindeki arguman fonksiyonun arlmasyla s gstericisine kopyalanacaktr. Ancak fonksiyon iinde *s nesnesine ya da s[x] nesnesine bir atama yaplamaz. Yani s gstericisinin gsterdii yerdeki nesne (fonksiyona adresi gnderilen nesne) deitirilemez. Yukardaki rnekte const anahtar szcnn kullanlmas hereyden nce okunabilirlik ile ilgilidir. Yukardaki kodu okuyan bir C programcs func fonksiyonunun dardan adresi alnan nesnenin yalnzca deerinden istifade edeceini, yani bu nesnenin deerini deitirmeyeceini anlar. Geleneksel olarak, const anahtar szcnn, parametre deikeni olan gstericilerin tanmlanmasnda kuallanlmamas okunabilirlik asndan bir mesaj olarak kabul edilir: void func(char *s) { ... } Yukardaki kodu okuyan bir C programcs func fonksiyonunun (aslnda sentaks asndan bir zorunluluk bulunmasa da) kendisine adresi gnderilen nesneyi deitireceini anlar. Kodu okuyan kii yle dnecektir : Eer func fonksiyonu adresi gnderilen nesneyi deitirmeyecek olsayd, s gstericisi const anahtar szc ile tanmlanrd. 2. const anahtar szcnn gsterici isminden hemen nce kullanlmas . rnekler : ... char ch; int m; float f; float fdizi[20]; char *const str = &ch ; int *const ptr = (int *) 0x 1AC3; float *const fptr = &f; Bu durumda gstericinin gsterdii yerdeki nesne deitirilebilir ama gstericinin ierii deitirilemez: str = (char *) 0x1FC0; /* error */ ptr = &m; /* error */ fptr = fdizi; /* error */ *str = 'A'; *ptr = 15; fptr[2] = 12.3f; /*legal */ /*legal */ /*legal */

const anahtar szcnn bu kullanm biiminde gsterici tanmlanrken ilk deer verilmelidir, yoksa const anahtar szcnn kullanlmasnn bir anlam kalmaz. 3. const anahtar szcnn hem tanmlama ifadesinden nce hem de gsterici isminden nce kullanlmas. rnekler : ... char ch;
270

BELRLEYCLER int k; const char *const str = (char *) 0x1AAA; const char *const ptr = (int *) 0x1FFF; Bu durumda ne gstericinin ierii deitirilebilir ne de gstericinin gsterdii yerdeki nesne deitirilebilir. Her iki durum da derleme zamannda error oluumuna yol aacaktr : str = &ch; *str = 'A'; ptr = &k; *ptr = 12; /* error */ /* error */ /* error */ /* error */

const szcnn bu biimde kullanlmas yine daha ok parametre deikenlerinin tanmlanmasnda grlr ise de bu uygulamalarda seyrek olan bir durumdur.

volatile belirleyicisi Derleyiciler optimizasyon amacyla nesneleri geici olarak yazmalarda tutabilir. Yazmalardaki bu eit geici barnmalar register belirleyicisi kullanlmasa da derleyiciler tarafndan yaplabilir. rnein: int kare (int a) { int x; x = a * a; return x;

Yukardaki fonksiyonda x geici bir deikendir, dolaysyla derleyici x 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 hzlandrmaktadr. Aadaki kodu inceleyiniz: int a; int b; ... a = b; if (a == b) { ... } Bu rnekte doal olarak ilk admda b deikeni a deikenine aktarlmak zere yazmalardan birine ekilecektir. Ancak derleyici if ierisindeki ifadede a == b karlatrmasn yapmak iin bellekteki b yerine yazmataki byi kullanabilir. Verdiimiz iki rnekte de derleyici birtakm optimizasyonlarla program ilevi deimeyecek biimde daha hzl alr hale getirmek istemitir. Ancak kimi uygulamalarda derleyicinin bu biimde davranmas hatalara neden olabilmektedir. kinci rnekte : a = b; /* Bir kesme gelerek byi deitirebilir! */ if (a == b) {
271

24 ... } Bir donanm kesmesi (rnein 8h gibi) byi deitiriyorsa, bu durum if deyimi tarafndan farkedilmeyebilir. te bu tr durumlarda deikenlerin optimizasyon amacyla geici olarak yazmalarda tutulmas arzu edilmeyen sonularn olumasna yol aabilmektedir. volatile Deikenleri optimizasyon 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 olabilmektedir. Bu belirleyicinin bilinsizce kullanlmasnn performans kt ynde etkileyebileceini unutmaynz.

Belirleyicilerin kullanllarn gsteren bir rnek : #include <stdio.h> int a; extern int b; static int c; void func(int d, register int e) { auto int g; int h; static int i; extern int j; register int k; } yukardaki programda tanmlanan deikenlerin faaliyet alan, mr ve balant asndan zellikleri aadaki tabloda belirtilmektedir : (isim) name a b c d e g h (mr) storage duration statik (static) statik (static) statik (static) otomatik (automatic) otomatik (automatic) otomatik (automatic) otomatik (automatic) (faaliyet alan) scope dosya (file) dosya (file) dosya (file) blok (block) blok (block) blok (block) blok (block) (balant) linkage d (external) d (external) * d (external) yok (none) yok (none) yok (none) yok (none)

272

BELRLEYCLER i j k statik (static) statik (static) otomatik (automatic) blok (block) blok (block) blok (block) yok (none) d (external) * yok (none)

* b ve j deikenleri kendi modlleri iinde tanmlanan global deikenler de olabilir. Bu durumda bu deikenle static anahtar szcyle tanmlanm olmalar durumunda i balantya sahip olacak, static anahtar szc olmadan tanmlanmlar ise d balantya sahip olacaklardr.

belirleyicilerin hangi tr deikenlerle kullanlabileceklerini gsteren tablo : belirleyici auto static register extern volatile const global deiken yerel deiken parametre deikeni

273

25

25 . BLM :

YAPILAR

Yaplar (structures) da diziler gibi birden fazla nesneyi ilerinde tutabilen bir veri trrdr. Yaplarn da elemanlar bellekte ardl (contigious) bir biimde bulunur. Fakat yaplarn dizilerden temel fark udur: Diziler ayn trden nesneleri iinde tutabilirken, yaplar farkl trlerden nesneleri tutabilirler. Yaplarn kullanlmasnn ana nedeni budur. ou zaman, trleri farkl bir takm nesneler, mantksal olarak bir btn oluturabilirler. sim, ya, departman, cret, renim durumu gibi bilgileri farkl trden nesneler iinde saklayabiliriz ve bunlarn tamam alan bir personele ait bilgiler olabilir. Bu gibi ayn konu ile ilgili farkl trden veriler yaplar iinde saklanr.

Yap Bildiriminin Genel ekli <struct > [yap ismi] { <tr> <m1>; <tr> <m2>; <tr> <m3>; ... }; Yukardaki gsterimde struct bir anahtar szcktr. Bildirimde mutlaka yer almas gerekmektedir. Yap ismi (structure tag) C dilinin isimlendirme kurallarna uygun olarak seilmi bir isimdir. rnek bildirimler: struct SAMPLE { int a; long b; char ch; }; struct DATE { int day, month, year; }; struct POINT { int x, y; }; Yap isimleri (structure tags) geleneksel olarak byk harfle yazlr. Bu bir zorunluluk deildir. Yap bildiriminin yaplmas bellekte derleyici tarafndan bir yer ayrlmasna neden olmaz. Yani bir tanmlama (definition) sz konusu deildir. Bu bildirimle (declaration) programcnn yaratt yeni bir veri tr hakknda derleyiciye bilgi verilmektedir. Yap bildiriminin yaplmasndan sonra artk bildirimi yaplm yap trnden nesneler tanmlanabilir. Yap bilidiriminin yaplmas ile yeni bir veri tr yaratlmtr. Derleyici artk bu tr hakknda bilgi sahibi oldugundan, bu yeni veri trnden, nesneler tanmlanabilir. Yeni veri trnden nesnelerin tanmlanmas durumunda artk derleyici bu nesneler iin bellekte ne kadar bir alan ayrmas gerektii bilgisine sahip olacaktr.

274

YAPILAR Yap Trnden Nesne Tanmlamas <struct> <yap isimi> <nesne ismi>; rnekler : struct DATE x; /* x DATE yaps trnden bir nesnedir. */ struct POINT p1, p1; /* p1 ve p2 POINT yaps trnden nesnelerdir. */ struct SAMPLE sample; /* sample SAMPLE yap trnden bir nesnedir. */ Yap deikenleri bileik nesnelerdir. Yani paralardan oluurlar. Zaten yap bildirimlerinin yaplmasnn amac da bu paralarn isimleri ve trleri hakknda derleyiciye bilgi vermektir. Bir yap bildirimini gren derleyici, daha sonra bildirimi yaplan yap trnden bir deiken tanmlanmas durumunda, bu deiken iin bellekte ne kadar yer ayracan, ve ayrd yeri ne ekilde organize edeceini bilir. Bir yap deikeni (nesnesi) iin, yap bildiriminde belirtilen elemanlarn toplam uzunluu kadar (byte olarak) yer ayrlr. struct SAMPLE { int a; long b; char ch; }; void main() { struct SAMPLE x; printf("%d\n", sizeof(x));

Yukardaki program parasnda ekrana (DOS altnda) 7 says yazdrlacaktr. nk yap nesnesi olan x nesnesinin bellekte kaplad yer parasnn kaplad uzunluun toplamdr. Bunu aadaki ekilde de ifade edebiliriz: sizeof(x) == sizeof(int) + sizeof(long) + sizeof(char) (Hizalama [alignment] konusuna geldiimizde bu durumu daha detayl olarak inceleyeceiz.) Yap Elemanlarna Eriim Yaplarn dizilerden nemli bir fark da elemanlara eriim konusundadr. Dizi elemanlarna eriim, dizi ismi (aslnda bir adres bilgisidir) ve indis operatr [ ] (index operator - subscript operator) yoluyla yaplrken, yap elemanlarna eriim dorudan yap nesnesinin ve elemann isimleriyle olur. Yap elemanlarna eriimde nokta operatr kullanlr. Nokta operatr (.) iki operand alan araek konumunda (binary infix) bir operatrdr. Bu operatrn sol tarafndaki operand bir yap trnden deiken olmaldr. Sa tarafndaki operand ise ilgili yap trnnn (yani yap bildiriminde nceden belirlenmi) bir yesi olmak zorundadr. Nokta operatr, operatr ncelik tablosunun en yksek dzeyinde bulunur. Yap nesneleri de yerel ya da global olabilir. Dier nesnelerde olduu gibi yap nesneleri iinde faaliyet alan (scope) kavram sz konusudur. Tm bloklarn dnda tanmlanan yap nesneleri global iken

275

25 bloklarn ilerinde tanmlanan yap deikenleri yereldir. Global yap nesneleri dier trden global nesneler gibi statik mr karakterine sahiptir ve dosya faaliyet alan kuralna uyarlar. Yerel yap nesneleri ise dinamik mrldr ve blok faaliyet alan kuralna uyarlar. Yap deikenlerine programc tarafndan deer verilmemise, yap deikeni yerel (local) ise tm elemanlarnda rasgele deerler (garbage value) bulunur. Yap nesnesi global ise tm elemanlarnda 0 deeri bulunur. Bir yap nesnesi tanmlanarak, bu yap nesnesinin elemanlarna nokta operatr ile ulaldnda artk bu elemanlarn her biri ayr bir nesne olarak ele alnr. Bu nesnelerin yap dnda tanmlanan nesnelerden herhangi bir fark yoktur, nesnelerin tm zelliklerine sahiptirler. rnek: struct SAMPLE { int a; long b; char c; }; int main() { struct SAMPLE x; x.a = 10; x.b = 200000; x.c = 'M'; printf("x.a = %d\nx.b = %ld\nx.c = %c\n", x.a, x.b, x.c); return 0; } Yukardaki rnekte grld gibi x.a, x.b ve x.c yap elemanlar ayr birer nesne zellii gsterirler. Bu elemanlara ayr ayr ulaabilir ve ayr ayr atamalar yapabiliriz. Bu nesneleri ++ ve -- operatrlerine ya da & operatrne operand yapabiliriz. Yap Bildirimlerinin Yapl Yerleri Yap bildirimi global ya da yerel olarak yaplabilir. Eer yerel olarak yaplrsa yalnzca bildirimin yapld blokta o yap trnden nesne tanmlanabilir. Bildirim global olarak yaplrsa her yerde o yap trnden deiken tanmlanabilir. Uygulamalarda hemen hemen her zaman yap bildirimleri programn tepesinde global olarak yaplr. (Yap bildirimi balk dosyalarnn iinde de yaplabilir. Bu durumu ileride detayl olarak inceleyeceiz.) Yap bildiriminde yer alan yap elemanlarnn faaliyet alan yap bildirim blouyla snrldr. Yani yap bildirimi blou dnda, yap eleman ile ayn isimli bir deiken tanmlanabilir. Yap ismi (structure tag) ile ayn isimli deikenler de tanmlanabilir. Aadaki program parasnda isimlendirme asndan bir hata yok. Okunabilirlik asndan iyi bir uygulama deil. struct SAMPLE { int sample;
276

YAPILAR long x; }; int main() { int sample, SAMPLE; ... return 0; } Yap Elemanlarnn Bellekteki Yerleimi Yap elemanlar bellee, bildirimde ilk yazlan eleman kk adreste olacak biimde, ardl olarak yerletirilir. rnek: struct SAMPLE { int a; long b; char c; }; int main() { struct SAMPLE x; printf("x.a adresi = %p\nx.b adresi = %p\n x.c adresi = %p\n", &x.a, &x.b, &x.c);

Yap Deikenlerine lk Deer Verilmesi (initialization) Yap deikenlerine kme parantezleri ierisinde ilk deer verilebilir. Bu durumda verilen ilk deerler sras ile yap elemanlarna yerletirilir. Daha az sayda yap elemanna ilk deer verilebilir, bu durumda ilk deer verilmemi yap elemanlar 0 deeri alrlar. struct DATE { int day, month, year; }; int main() { struct DATE x = {10, 12, 1999}; struct DATE y = {10, 12}; printf("%d %d %d\n", x.day, x.month, x.year}; printf("%d %d %d\n", y.day, y.month, y.year}; } Dizilerde olduu gibi, yaplarda da yap elemanlarndan daha fazla sayda elemana ilk deer vermek derleme zamannda hata oluumuna neden olacaktr.

277

25 Yap Eleman Olarak Gstericilerin Kullanlmas Yapnn bir eleman herhangi trden bir gsterici olabilir. Bu durumda bu yap elemanna ulalddnda, bu gsterici de yap eleman olmayan gstericiler gibi deerlendirilir rnek: struct PERSON { char *name; int no; }; int main() { struct PERSON x; x.name = "Necati"; x.no = 125; printf("%s %d\n", x.name, x.no); return 0; } Yukardaki rnekte yap elemanlarna ilk deer verme sentaksyla da (initialization) deer atanabilirdi: .... struct PERSON x = {"Necati", 125}; ... Yap Eleman Olarak Dizilerin Kullanlmas Yapnn bir eleman herhangi trden bir dizi olabilir. Dizilerde olduu gibi, bu durumda da yapnn dizi elemannn ismi nesne belirtmez. (adres bilgisi belirtir). rnek: struct PERSON { char name[30]; int no; }; int main() { struct PERSON x; gets(x.name); puts(x.name); putchar(x.name.[3]); x.name++ /* error. dizi ismi nesne deildir. */ return 0; } Yap Nesneleri zerinde Yaplabilecek lemler Yap deikenleri bir btn olarak aritmetik operatrlerle ve karlatrma operatrleri ile ileme sokulamaz. Yap nesneleri zerinde u ilemler yaplabilir:

278

YAPILAR

1. Bir yap deikeninin adresi alnabilir. 2. Ayn trden iki yap deikeni birbirine atanabilir. 3. sizeof operatr ile bir yap nesnesinin bellekte kaplad alan bulunabilir. Ayn trden iki yap deikeninin birbirine atanmasnda yap elemanlar karlkl olarak birbirlerine atanr. Yani bir blok kopyalanmas sz konusudur. Atama ilemi iin kesinlikle iki yap deikeninin de ayn trden olmas gerekmektedir. ki yap deikeni de ayn trden deilse bu durum derleme aamasnda hata oluturur. ki yapnn ayn trden olmalar iin ayn yap ismi ile tanmlanm olmalar gerekir. Aadaki iki yap, elemanlarnn trleri ve dizilileri ayn olduu halde birbirleri ile ayn deildir ve birbirlerine atanamazlar. struct POINT_1 { int x, y; }; struct POINT_2{ int x, y; }; ... struct POINT_1 a; struct POINT_2 b; b.x = 10; b.y = 20; a=b /* derleme zamannda hata. a ve b yaplar ayn trden deil. */

Yap Trnden Adresler ve Gstericiler Bir yap deikeninin adresi alnabilir. Bu durumda elde edilen adresin saysal bileeni yapnn bellekteki balang adresi, tr bileeni ise yap ile ayn trden adrestir. Bir yap trnden gstericiler de tanmlanabilir. Yap trnden gstericilere ayn yap trnden bir adres atanmaldr. rnek: struct POINT_1 *p = &a; Gstericiler Yap Elemanlarna Eriim p yap trnden bir gsterici ve elm de o yapnn bir eleman olmak zere eriim u biimde yaplr: (*p).elm Burada ncelik operatrnn kullanlmas zorunludur. Kullanlmazsa nce p.elm ele alnr daha sonra * operatr ileme sokulurdu. Bu durum da derleme zamannda hata oluumuna neden olurdu. Yaplarn Fonksiyonlara Parametre Olarak Geirilmesi Yaplar fonksiyonlara geirebilmek iin iki yntem kullanlabilir: 1. Yapnn kendisinin fonksiyona parametre olarak geirilmesi. Ayn trden yap nesnelerinin birbirlerine atanabilmesi zelliinden faydalanlr. Bu yntemde fonksiyonun parametre deikeni bir yap deikeni olur. Fonksiyon da ayn yap trnden bir yap

279

25 deikeni ile arlr. Ayn trden iki yap deikeninin birbirine atanmas geerli olduuna gre bu ilem de geerlidir. Bu ilem bir blok kopyalamas gerektirdii iin hem bellek hem de zaman asndan greli bir kayba neden olur. stelik bu yntemle, fonksiyon kendisine gnderilen argumanlar deitiremez. Yani fonksiyon deerle arlmtr. (call by value) rnek : struct PERSON { char name[30]; int no; }; void disp(struct PERSON y) { printf("%s %d\n", y.name, y.no); } int main() { struct PERSON x = {"Necati ERGIN", 125}; disp(x); } 2. Yap deikenlerinin adreslerinin geirilmesi. Bu yntemde fonksiyonun parametre deikeni yap trnden bir gsterici olur. Fonksiyon da bu trden bir yap deikeninin adresi ile arlr. Bu yntem iyi bir tekniktir. Hemen her zaman bu yntem kullanlmaldr. Bu yntemde yap ne kadar byk olursa olsun aktarlan yalnzca bir adres bilgisidir. stelik bu yntemde fonksiyon kendisine adresi gnderilen yap deikenini deitirebilir. phesiz byle bir aktarm ileminin mmkn olabilmesi yap elemanlarnn bellekteki ardllk zelliinden faydalanlmaktadr. rnek : struct PERSON { char name[30]; int no; }; void disp(struct PERSON *p) { printf("%s %d\n", (*p).name, (*p).no); }

int main() { struct PERSON x = {"Necati Ergin", 156}; disp(&x); } Yaplarn Kullanm Yerleri Yaplar temel olarak 3 nedenden dolay kullanlrlar.

280

YAPILAR 1. Birbirleri ile ilikili olan deikenler yap elemanlar olarak bir yap ierisinde toplanrsa algsal kolaylk salanr. rnein dzlemde bir nokta, bir tarih bilgisi, bir depoda bulunan mamllere ilikin zellikler bir yap ile temsil edilebilir.

2. C dilinde bir fonksiyon en fazla 8 - 10 parametre almaldr. Daha fazla parametreye sahip olmas kt bir tekniktir. Bir fonksiyon ok fazla parametrik bilgiye gereksinim duyuyorsa, bu parametrik bilgiler bir yap biiminde ifade edilmelidir. O yap trnden bir deiken tanmlanmal, bu deikenin adresi fonksiyona parametre olarak gnderilmelidir. rnein bir kiinin nfus czdan bilgilerini parametre olarak alp bunlar ekrana yazdracak bir fonksiyon tasarlayacak olalm. Nfus czdan bilgilerinin hepsi bir yap biiminde ifade edilebilir ve yalnzca bu yapnn adresi fonksiyona gnderilebilir. 3. C dilinde fonksiyonlarn tek bir geri dn deeri vardr. Oysa fonksiyonlarn ok deiik bilgileri aran fonksiyona iletmesi istenebilir. Bu ilem yle yaplr: letilecek bilgiler bir yap biiminde ifade edilir. Sonra bu trden bir yap deikeni tanmlanarak adresi fonksiyona gnderilir. Fonksiyon da bu yap deikeninin iini doldurur. Yani fonksiyon knda bilgiler yap deikeninin iinde olur. 4. C dilinin tasarmnda belirlenmi veri trleri yalnzca temel trleri kapsayacak ekildedir. rnein tarihler ya da karmak saylar iin C dilinde belirlenmi temel bir tr yoktur (default veri tipi yoktur.) Yaplar bu gibi veri trlerini oluturmak iin kullanyoruz. Ok Operatr OK operatr - ve > karakterlerinin yanyana getirilmesiyle oluturulur. ki operand alan araek konumunda bir operatrdr. (Binary infix ) Ok operatr ncelik tablosunun en yksek ncelik seviyesindedir. -> operatrnn sol tarafndaki operand yap trnden bir adres olmaldr. -> operatrnn sa tarafndaki operand ise ilgili yapnn bir eleman olmaldr. Sol tarfndaki operand ile belirtilen yapnn (o adresteki yapnn) sa tarafnda belirtilen elemanna ulamak iin kullanlr. p, yap trnden bir nesnenin adresini tutuyor olsun. Aadaki iki ifade edeerdir. (*)p.a p->a

Yani nokta ve ok operatrlerinin her ikisi de elemana erimek iin kullanlr. Ancak nokta operatr yap deikeninin kendisiyle ok operatr ise adresiyle eriim salar. Okunabilirlik asndan ok operatrnn solunda ve sanda boluk braklmamasn tavsiye ediyoruz. &p->a ifadesiyle p'nin gsterdii yapnn a elemannn adresi alnr. (Ok operatr adres operatrne gre daha nceliklidir.) Bir Yap Trne Geri Dnen Fonksiyonlar Bir fonksiyonun geri dn deeri bir yap trnden olabilir. rnek: struct POINT { int x, y; }; struct POINT make_point(int x, int y); int main() {
281

25 struct POINT a; a = make_point(3, 5); ... return 0; } struct POINT make_point(int x, int y) { struct POINT temp; temp.x = x; temp.y = y; return temp;

Bu durumda geri dn deerinin aktarld geici blge de struct POINT trnden olur. Byle bir fonksiyonun geri dn deeri ayn trden bir yap deikenine atanabilir. Ancak byle fonksiyonlar kk yaplar iin kullanlmaldr. nk return ifadesiyle geici blgeye geici blgeden de geri dn deerinin saklanaca deikene atamalar yaplacaktr. Bu da kt bir teknik olmaya adaydr. Kk yaplar iin tercih edilebilir. nk algsal karmakl daha azdr. make_point fonksiyonunun parametre deikenleri ile POINT yapsnn yeleri ayn isimde olduklar halde faaliyet alanlar farkldr. Yap Trnden Bir Alann Dinamik Olarak Tahsis Edilmesi Nasl bir dizi iin bellek alan dinamik olarak tahsis edilebiliyorsa bir yap iin de byle bir tahsisat yaplabilir. Aadaki rnekte DATE trnden bir yap iin dinamik tahsisat yaplmtr: struct DATE { int day, month, year; }; int main() { struct DATE *p; p = (struct DATE *) malloc (sizeof(struct DATE)); p->day = 10; p->month = 12; p->year = 2000; printf("%d %d %d\n", p->day, p->month, p->year); free(p); return 0;

Bir Yap Adresine Geri Dnen Fonksiyonlar Bir fonksiyonun geri dn deeri bir yap trnden adres de olabilir. rnein:
282

YAPILAR

struct POINT { int x, y; }; struct POINT *dump_point(struct POINT *p); int main() { struct POINT *p; struct POINT a; a.x = 10; b.x = 20; p = dump_point(&a); return 0;

struct POINT dump_point(struct POINT *p) { struct POINT *retval; if ((retval = (struct POINT *) malloc(sizeof(struct POINT))) == NULL) { printf("not enough memory"); exit(EXIT_FAILURE); } *retval = *p; return retval; } Tabi byle bir fonksiyon yerel bir yap deikeninin adresine geri dnemez. Global bir yap deikeninin adresine de geri dnmesinin bir anlam yoktur. (Statik bir yerel yap nesnesinin adresiyle geri dnlmesinde hibir saknca yok. Statik yerel deikenler konusundaki bilgilerinizi hatrlaynz.) En iyi tasarm, ieride dinamik olarak tahsis edilmi bir yapnn adresine geri dnmektir. Tahsis edilen alann "heap" bellek alanna iade edilmesi, fonksiyonu arann sorumluluundadr. Yap Bildirimi le Deiken Tanmlamasnn Birlikte Yaplmas Bir yap bildirimi noktal virgl ile kapatlmayp, yap bildiriminin kapanan blok parantezinden hemen sonra bir deiken listesi yazlrsa yap bildirimi ile deiken tanmlamas bir arada yaplm olur. struct DATE { int day, month, year; } bdate, edate; Bu tanmlamada struct DATE trnden bir yapnn bildirimi ile birlikte bu yap trnden bdate ve edate deikenleri de tanmlanmtr. Bu durumda yap deikenlerinin faaliyet 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 bulunmamaktadr. rnein yukardaki bildirim aadaki ekilde de yaplabilir.
283

25

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; eklinde yaplsa bile artk derleyici, eleman yaps ve elemanlarn trleri ayn olsa bile bu yapy ayr bir yap tr olarak ele alacaktr. Dolaysyla cdate = bdate; gibi bir atama yap trlerinin farkl olmas nedeniyle geerli deildir. Byle bir tanmlamann baka bir dezavantaj da bu yap tryle ilgili bir fonksiyon yazlamaydr. nk yap trnn bir ismi yoktur, ve bir fonksiyonun parametre deikeni tr bilgisi olmadan tanmlanamaz. ie Yaplar (nested structures) Bir yapnn eleman baka bir yap trnden yap deikeni ise bu duruma iie yaplar denir. ie yap bildirimi iki biimde yaplabilir : 1. nce eleman olarak kullanlan yap bildirilir. Onun aasnda dier yap bildirimi yaplr. rnek: struct DATE { int day, month, year; }; struct PERSON { char name[30]; struct DATE birthdate; int no; }; Bu durumda iteki yapya ulamak iin 2 tane nokta operatr kullanmak gerekir. int main() { struct PERSON employee; employee.birthdate.day = 10; employee.birthdate.month = 12; employee.birthdate.year = 2000; strcpy(employee.name, "Necati Ergin"); employee.no = 1472;
284

YAPILAR printf("%d %d %d - %s - %d\n", employee.birthdate.day, employee.birthdate.month, employee.birthdate.year, employee.name, employee.no); return 0; }

2. Bu yntemde eleman olan yap deiken tanmlamas ile birlikte elemana sahip yapnn ierisinde bildirilir. rnek: struct PERSON { char name[30]; struct DATE { int day, month, year; } birthdate; int no; }; Burada ite bildirilen yap da sanki darda bildirilmi gibi ilem grr. yani ieride bildirilen yap trnden deikenler tanmlanabilir. Burada dikkat edilmesi gereken bir noktada iie yap bildiriminin yaplmasna ramen bir deiken tanmlamasnn yaplmam olmasdr. Yani birthdate bir nesne deildir. Ancak struct PERSON trnden bir deiken tanmlandnda, bu yap deikeninin bir alt eleman olacaktr:

struct PERSON X; X.birthdate.day = 20; ie Yaplara lk Deer Verilmesi Normal olarak ilk deer vermede elemanlar srasyla, iteki yap da dikkate alnacak biimde, yap yelerine atanr. Ancak iteki yapnn ayrca kme parantezleri ierisine alnmas okunabilirlii artrd iin tavsiye edilir. rnek: struct PERSON per = {"Necati Ergin", {10, 10, 1967}, 123}; Eer iteki yap ayrca kme parantezi iine alnmsa iteki yapnn daha az sayda elemanna ilk deer vermek mmkn olabilir. rnek : struct PERSON per = {"Necati Ergin", {10, 10}, 123}; Burada doum tarihinin year elemanna ilk deer verilmemitir. Derleyici bu elemana otomatik olarak 0 deeri yerletirecektir. Ancak burada iteki kme parantezleri kullanlmasayd 123 ilk deeri year elemanna verilmi olacak, no elemanna otomatik olarak 0 deeri verilmi olacakt. Ayn durum yap iinde dizi bildirimlerinde de sz konusudur. rnek : struct SAMPLE { int a[3]; long b; char c; };

285

25 struct SAMPLE x = {{1, 2, 3}, 50000L, 'x'}; Aadaki gibi bir bildirim de geerlidir : struct SAMPLE { int a[3]; long b; char c; } x = {{1, 2, 3}, 50000L, 'A}, *p;

ie Yap Kullanmnn Avantajlar Aadaki gibi bir yap bildiriminin yapldn dnelim : struct ISIM { char ad[ADUZUNLUK + 1]; char soyad[SADUZUNLUK + 1]; }; sim yapsn daha byk bir yapnn paras olarak kullanabiliriz. struct OGRENCI { struct ISIM isim; int no, yas; char cinsiyet; } ogrenci1, ogrenci2; Bu durumda ogrenci1 degiskeninin ad ve soyad'na ulamak icin iki kez nokta operatr kullanlacaktr: strcpy(ogrenci1.isim.ad, Hakan); Yukardaki rnekte OGRENCI yapsnn eleman olarak ISIM yapsn kullanmak yerine, dogrudan ad[ADUZUNLUK + 1] ve char soyad[SADUZUNLUK + 1] dizilerini OGRENCI yapsnn elemanlar yapabilirdik. Ama isim yapsn dorudan bir eleman olarak kullanrsak baz ilemleri daha kolay yapabiliriz. rnein rencilerin ad ve soyadn yazan bir fonksiyon yazacamz dnelim. Bu durumda yazlacak fonksiyona 2 arguman gndermek yerine yalnzca 1 arguman gnderilecektir. isim_yaz(ogrenci1.isim); Benzer ekilde isim trnden bir yap elemanndan grencinin ad ve soyad bilgilerini OGRENCI yaps trnden bir deikenin alt elemanlarna kopyalama daha kolay yaplacaktr : struct isim yeni_isim; ... ogrenci1.isim = yeni_isim; Yap Dizileri Yaplar da bir tr belirttiine gre yap trnden de diziler sz konusu olabilir. Yap dizilerine de normal dizilere ilk deer verilmesine benzer ekilde ilk deer verilebilir. lk deer verme srasnda kullanlan, iteki kme parantezleri okunabilirlii artrr. rnek:

286

YAPILAR struct DATE { int day, month, year; }; int main() { struct DATE birthday[5] = {{10, 10, 1958}, {04, 03, 1964}, {21, 6.1967}, {22. 8, 1956}, {11, 3, 1970}}; struct DATE *pdate; int i; pdate = birthdate; for (i = 0; i < 5; ++i) { printf("%02d / %02d / %04d\n", pdate->day, pdate->month, pdate->year); ++pdate; } return 0;

Program parasn u ekilde de yazabilirdik: int main() { ... for (i = 0; i < 5; ++i) disp(&birthdate[i]); return 0; } void Disp(struct DATE *p) { printf("%02d / %02d / %04d\n", pdate->day, pdate->month, pdate->year); } Yaplara likin Karmak Durumlar Bir yapnn eleman baka bir yap trnden yap gstericisi olabilir. rnein: struct DATE { int day, month, year; }; struct PERSON { char name[30]; struct DATE *bdate; int no; }; struct PERSON x; eklinde bir tanmlamann yapldn dnelim. 1. x.bdate ifadesinin tr struct DATE trnden bir adrestir ve nesne belirtir. (Nesne belirtir nk bdate bir gsterici deikendir.)
287

25 2. x.bdate->day ifadesinin tr int dir ve nesne belirtir. 3. &x.bdate->day ifadesinin tr int trden bir adrestir. Tabi bu rnekte, bir deer atamas yaplmamsa, x.bdate ile belirtilen gsterici ierisinde rasgele bir adres vardr. (garbage value) Bu gstericinin kullanlabilmesi iin tahsis edilmi bir alan gstermesi gerekir. rnein bu alan dinamik olarak tahsis edilebilir. x.bdate = (struct DATE *) malloc (sizeof(struct DATE)); Yukardaki rnekte elimizde yalnzca struct PERSON trnden bir gsterici olduunu dnelim. struct PERSON *person; 1. person->bdate ifadesinin tr struct DATE trnden bir adrestir. 2. person->bdate->day ifadesinin tr int dir. Bu rneklerde henz hibir yer iin bilinli bir tahsisat yaplmamtr. Hem person gstericisi iin hem de person->date gstericisi iin, dinamik bellek fonksiyonlaryla bellekte yer tahsisatlarnn yaplmas gerekir: person = (struct PERSON *) malloc(sizeof(struct PERSON)); person->bdate = (struct DATE *) malloc(sizeof(struct DATE)); Burada dinamik olarak tahsis edilen alanlar ters srada iade (free) edilmelidir. free(person->bdate); free(person); Bir yapnn eleman kendi trnden bir yap deikeni olamaz. rnein: struct SAMPLE { struct SAMPLE a; }; /* hata */ nk burada SAMPLE yapsnn uzunluu belirlenemez. Ancak bir yapnn eleman kendi trnden bir gsterici olabilir. rnein: struct LLIST { int val; struct LLIST *next; }; Bu tr yaplar zellikle bal liste ve aa yaplarn (algoritmalarn) gerekletirmek amacyla kullanlabilir. (Bu konu detayl olarak bal listeler dersinde ele alnacaktr. Bal Liste Nedir 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 yap trnden bir gstericide tutulur. Bylece 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. rnek:
288

YAPILAR

struct LLIST { int val; struct LLIST *next; }; Bal listenin ilk elemannn adresi global bir gstericide tutulabilir. Son elemanna ilikin gsterici ise NULL adresi olarak brakllr. Bal liste elemanlar malloc gibi dinamik bellek fonksiyonlar ile oluturulur. Bal Listelerle Dizilerin Karlatrlmas Dizilerde herhangi bir elemana ok hzl eriilebilir. Oysa bal listelerde bir elemana eriebilmek iin, bal listede ondan nce yer alan btn elemanlar dolamak gerekir. Dizilerde araya eleman ekleme ya da eleman silme ilemleri iin blok kaydrmas yapmak gerekir. Oysa bal listelerde bu ilemler ok kolay yaplabilir. Diziler bellekte ardl bulunmak zorundadr. Bu durum bellein blnm olduu durumlarda belirli uzunlukta dizilerin almasn engeller. Yani aslnda istenilen toplam byklk kadar bo bellek vardr ama ardl deildir. te bu durumda bal liste tercih edilir. Bal liste kullanm srasnda eleman ekleme, eleman silme, bal listeyi gezme (traverse), vb. ilemler yaplr.

289

26

26 . BLM :

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI

time_t time (time_t *timer); 01.01.1970 tarihinden itibaren geen saniye saysn bulur. Bu deer baz tarih ve zaman fonksiyonlarnda girdi olarak kullanlmaktadr.

geri dn deeri 01.01.1970 den arld zamana kadar geen saniye saysdr. Bu deeri ayn zamanda kendisine gnderilen argumandaki adrese de yazar. Arguman olarak NULL adresi gnderilirse saniye deerini yalnzca geri dn deeri olarak retir. struct tm localtime(const time_t *timer); Geri dn deeri struct tm trnden bir localtime fonksiyonu arld andaki sistem tarihinin ve yap adresidir. Bu yap iinde ayrtrlm zamannn elde edilmesi amacyla kullanlr. 01.01.1970 tarih ve zaman bilgileri bulunmaktadr. tarihinden geen saniye saysn (yani time fonksiyonunun ktsn) parametre olarak alr, bunu o andaki tarih ve zaman deerine dntrerek statik olarak tahsis edilmi bir yap ierisinde saklar. char *ctime(const time_t *time); fonksiyon kendi ierisnde statik olarak Bu fonksiyon time fonksiyonunun ktsn parametre tahsis ettii dizinin adresine geri dner. olarak alr, 26 karakterlik NULL ile biten bir diziye (Dolaysyla hep ayn adrese geri yerletirerek dizinin balang adresiyle geri dner. Tarih dnmektedir.) ve zaman bilgisinin dizi ierisindeki durumu yledir: DDD MMM dd hh:mm:ss YYYY Mon Oct 20 11:31:54 1975\n\0 DDD : ngilizce gnlerin ilk harfinden elde edilen ksaltma MMM : ngilizce aylarn ilk harfinden elde edilen ksaltma dd : Saysal olarak ayn hangi gn olduu hh : Saat mm : Dakika ss : Saniye YYYY : Yl char *asctime (const struct tm *tblock); fonksiyon kendi ierisnde statik olarak ctime fonksiyonuna benzer bir ilevi yerine tahsis ettii dizinin adresine geri dner. getirir.Parametre olarak ctime fonksiyonundan farkl (Dolaysyla hep ayn adrese geri olarak struct tm trnden bir gsterici alr. Tpk ctime dnmektedir.) fonksiyonunda olduu gibi tarihi ve zaman 26 karakterlik bir diziye yerletirir. clock_t clock(void) Baar durumunda programn Programn balangcndan itibaren geen saat ticklerinin balangcndan beri geen ilemci zaman saysn verir. deerine geri dner. clock fonksiyonu iki olay arasndaki zamansal fark bulmak Baarszlk durumunda ilemci zaman elde iin kullanlr. Elde edilen zaman deerini saniyeye edilemiyorsa, ya da deeri elde evirmek iin, bu deeri CLK_TCK sembolik sabitine edilemiyorsa) 1 deerine geri dner. blmek gerekir. double difftime(time_t time2, time_t time1); Geri dn deeri time1 ve time2 ki zaman arasndaki fark saniye olarak hesaplar. zamanlar arasndaki saniye olarak farktr.

290

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI Uygulama #include <conio.h> #include <time.h> void showtime(void); void delay(unsigned long n); double keeptime(void); double chrono(void); int main() { int i = 10; clrscr(); chrono(); while (i-- > 0) { delay(3000000ul); printf("%lf\n", chrono()); } return 0; } void showtime(void) { time_t timer; struct tm *tptr; while (!kbhit() || getch() != 27) { time(&timer); tptr = localtime(&timer); printf("%02d:%02d:%02d\r", tptr->tm_hour, tptr->tm_min, tptr->tm_sec); } } double keeptime(void) { static int flag = 0; static clock_t start, end; if (!flag) { start = clock(); flag = 1; return -1.; } end = clock(); flag = 0; return (end - start) / CLK_TCK;

double chrono(void) {
291

26 static int flag = 0; static clock_t start, end; if (flag == 0) { start = clock(); flag = 1; return -1.; } end = clock(); return (end - start) / CLK_TCK;

void delay(unsigned long n) { while (n-- > 0) ; } Uygulama Tarihler ile ilgili lem Yapan fonksiyonlar. /* INCLUDE FILES */ #include #include #include #include <stdio.h> <stdlib.h> <conio.h> <time.h>

/* symbolic constants */ #define VALID #define INVALID #define FALSE #define TRUE #define #define #define #define #define #define #define SUN MON TUE WED THU FRI SAT 0 2 4 5 6 1 0 0 1

1 3

/* type definitions */ typedef int BOOL; typedef int DAYS; typedef struct DATE { int day, month, year; }DATE;
292

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI

/* function prototypes */ void setDate(int day, int month, int year, DATE *date); void displayDate(const DATE *date); void displayDate2(const DATE *date); long totaldays(const DATE *date); int dayofyear(const DATE *date); int Datecmp(const DATE *date1, const DATE *date2); int datedif(const DATE *date1, const DATE *date2); BOOL isleap(int year); BOOL validDate(const DATE *date); BOOL isweekend(const DATE *date); DAYS Dateday(const DATE *date); DATE DATE DATE DATE nDate(const DATE *date, int n); randomDate(void); totaltoDate(long totaldays); randomDate(void);

/* global variables */ char *days[] = {"Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"}; char *months[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; /* function definitions */ int main() { /* type your test code here */ } /* Gnderilen argumann artk yl olup olmadgn test eder. Artk yl ise 1 degil ise 0 deerine geri dner. */ BOOL isleap(int year) { if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) return TRUE; return FALSE; } /* Gnderilen adresteki DATE trnden yap nesnesine ilk 3 argumanndaki ay, gn, yl deerlerini yerletirir. */ void setDate(int day, int month, int year, DATE *date) {
293

26 date->day = day; date->month = month; date->year = year; } /* Gnderilen adresteki yap nesnesinin elemanlarn ekrana yazdrr. */ void displayDate(const DATE *date) { printf("%02d %02d %4d", date->day, date->month, date->year); } /* gnderilen adresteki yap nesnesinin saklad tarihi ingilizce format ile ekrana yazar. rnek: 3 rd Sep 2000 Sunday */ void displayDate2(const DATE *date) { printf("%02d", date->day); switch (date->day) { case 1: case 21: case 31: printf("st "); break; case 2: case 22: printf("nd "); break; case 3: case 23: printf("rd "); break; default : printf("th "); } printf("%s ", months[date->month - 1]); printf("%d ", date->year); printf("%s", days[Dateday(date)]); } /* 01.01.1900 tarihinden adresi verilen tarihe kadar geen gn saysna geri dner. */ long totaldays(const DATE *date) { long result = 0; int year; for (year = 1900; year < date->year; ++year) result += 365 + isleap(year); result += dayofyear(date); return result;

/* adresi gnderilen yap nesnesindeki tarihin ilgili yln kanc gn olduunu bulur. */ int dayofyear(const DATE *date) { int result = date->day;

294

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI switch (date->month - 1) { case 11: result += 30; case 10: result += 31; case 9: result += 30; case 8: result += 31; case 7: result += 31; case 6: result += 30; case 5: result += 31; case 4: result += 30; case 3: result += 31; case 2: result += 28 + isleap(date->year); case 1: result += 31; } return result;

/* Adresleri gnderilen yap nesnelerinde saklanan tarihler arasndaki gn farkn bulur. */ int datedif(const DATE *date1, const DATE *date2) { return abs(totaldays(date1) - totaldays(date2)); } /* Parametre deikenindeki gn saysn DATE trnden bir nesneye dntrerek geri dn deeri olarak retir. */ DATE totaltoDate(long totaldays) { int months[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; DATE result; int i = 0; result.year = 1900; while (totaldays > (isleap(result.year) ? 366 : 365)) { totaldays -= isleap(result.year) ? 366 : 365; result.year++; } months[1] += isleap(result.year); while (totaldays > months[i]) { totaldays -= months[i]; ++i; } result.month = i + 1; result.day = totaldays; return result;

/* Adresi gnderilen yap nesnesindeki tarihten n gn sonraki ya da nceki tarihin ne oldugunu hesaplayarak bu tarihe geri dner. */ DATE nDate(const DATE *date, int n)
295

26 { } return totaltoDate(totaldays(date) + n);

/* adresi gnderilen yap nesnesindeki tarihin hangi gn olduunu hesap eder. */ DAYS Dateday(const DATE *date) { switch (totaldays(date) % 7) { case 0: return SUN; case 1: return MON; case 2: return TUE; case 3: return WED; case 4: return THU; case 5: return FRI; case 6: return SAT; } return 0; } /* Adresleri gnderilen yap nesnelerindeki tarihleri karlatrr. */ int Datecmp(const DATE *date1, const DATE *date2) { if (date1->year != date2->year) return date1->year - date2->year; if (date1->month != date2->month) return date1->month != date2->month; if (date1->day != date2->day) return date1->day - date2->day; return 0; } /* Adresi gnderilen yap adresindeki tarihin gecerli bir tarih olup olmadn test eder. */ BOOL validDate(const DATE *date) { int months[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; months[1] = 28 + isleap(date->year); if (date->year < 1900) return INVALID; if (date->day > months[date->month - 1] || date->day <= 0) return INVALID; if (date->month < 1 || date-> month > 12) return INVALID; return VALID; } /* Tarihin hafta sonu olup olmadn test eder. */ BOOL isweekend(const DATE *date)
296

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI { DAYS result = Dateday(date); if (result == SAT || result == SUN) return TRUE; return FALSE;

/* 01.01. 1900 ve 31.12.2000 tarihleri arasnda rasgele bir tarih retir. */ DATE randomDate() { int months[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; DATE random; random.year = rand() % 101 + 1900; months[1] += isleap(random.year); random.month = rand() % 12 + 1; random.day = rand() % months[random.month - 1] + 1; return random; }

Uygulama Tekli bal liste oluturulmasna ilikin bir program. #include #include #include #include #define #define #define #define #define #define <stdio.h> <conio.h> <stdlib.h> <string.h> ADD_BEGIN ADD_END DELETE_NAME DELETE_NO DISPLAY EXIT_PROG 1 2 3 4 5 6

typedef struct _PERSON { char name[20]; int no; struct _PERSON *next; }PERSON; PERSON *start = NULL; void void void void void add_begin(void); display_list(void); add_end(void); delete_name(void); delete_no(void);
297

26 int get_option(void); int main() { int option; for (;;) { option = get_option(); switch (option) { case ADD_BEGIN : add_begin(); break; case ADD_END : add_end(); break; case DELETE_NAME : delete_name(); break; case DELETE_NO : delete_no(); break; case DISPLAY : display_list(); break; case EXIT_PROG : goto EXIT; default : printf("geersiz seenek!..\n"); } } EXIT: exit(EXIT_SUCCESS); return 0; } /************************************************/ int get_option(void) { int option; printf("\n[1] bal listenin bana ekle.\n"); printf("[2] bal listeye sondan ekle.\n"); printf("[3] bal listeden isme gre sil.\n"); printf("[4] bal listeden no'ya gre sil.\n"); printf("[5] bal listeyi listele.\n"); printf("[6] programdan k.\n"); option = getch() - '0'; return option;

/************************************************/ void add_begin(void) { PERSON *new; new = (PERSON *)malloc (sizeof(PERSON)); if (new == NULL) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } fflush(stdin); printf("ismi giriniz : "); gets((char *)new); printf("No'yu giriniz : ");
298

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI scanf("%d", &new->no); new->next = start; start = new; } /************************************************/ void add_end(void) { PERSON *new, *cur; new = (PERSON *)malloc(sizeof(PERSON)); if (new == NULL) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } fflush(stdin); printf("ismi giriniz : "); gets((char *)new); printf("No'yu giriniz : "); scanf("%d", &new->no); if (start == NULL) { start = new; new->next = NULL; return; } for (cur = start; cur->next != NULL; cur = cur->next) ; cur->next = new; new->next = NULL; } /************************************************/ void display_list(void) { PERSON *cur; printf("\n bilgiler listeleniyor :\n\n"); for (cur = start; cur != NULL; cur = cur->next) printf("%-20s\t%05d\n", cur->name, cur->no); } /************************************************/ void delete_name(void) { PERSON *cur, *prev; char nametemp[30]; printf("silinecek ismi girin : "); fflush(stdin); gets(nametemp); for (prev = NULL, cur = start; cur != NULL && strcmp(cur->name, nametemp);
299

26 prev = cur, cur = cur->next) ; if (cur == NULL) { printf("silinecek kayt bulunamad\n"); return; } if (prev == NULL) start = start->next; else prev->next = cur->next; free(cur); printf("kayt silindi!\n");

/************************************************/ void delete_no(void) { PERSON *cur, *prev; int no; printf("silinecek No'yu girin : "); scanf("%d", &no); for (prev = NULL, cur = start; cur != NULL && cur->no != no; prev = cur, cur = cur->next) ; if (cur == NULL) { printf("kayt bulunamad\n"); return; } if (prev == NULL) start = start->next; else prev->next = cur->next; free(cur); printf("kayt silindi!..\n");

} Uygulama Sral bal liste oluturulmasna ilikin bir program. #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <string.h>

typedef struct _PERSON{ char name[20]; int no; struct _PERSON *next; }PERSON; PERSON *start = NULL;
300

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI

int get_option(void); void add(void); void display(void); void delete_name(void); void fill(void); PERSON *find_name(char *name); void del_list(void); void search(void);

int main() { int option; for (;;){ option = get_option(); switch (option){ case 1: add(); break; case 2: display(); break; case 3: delete_name(); break; case 4: fill(); break; case 5: search(); break; case 6: del_list(); break; case 0: goto EXIT; default: printf("make your choice\n"); } } EXIT: return 0; } /************************************************/ int get_option(void) { int option; printf("\n[1] add a new name\n"); printf("[2] display list\n"); printf("[3] delete name\n"); printf("[4] fill the link list with test data\n"); printf("[5] find name\n"); printf("[6] delete list\n"); printf("[0] exit\n"); option = getch() - '0'; return option; } /************************************************/ void add(void) { PERSON *prev, *cur, *_new; char name[50];
301

26 fflush(stdin); printf("name:"); gets(name); for (prev = NULL, cur = start; cur != NULL && strcmp(name, cur->name) > 0; prev = cur, cur = cur->next) ; if (cur != NULL && !strcmp(name, cur->name)) { printf("name already exists\n"); return; } if ((_new = (PERSON *) malloc(sizeof(PERSON))) == NULL) { printf("not enough memory"); exit(EXIT_FAILURE); } strcpy(_new->name, name); printf("number:"); scanf("%d", &_new->no); _new->next = cur; if (prev == NULL) start = _new; else prev->next = _new; } /************************************************/ void display(void) { PERSON *cur; putchar('\n'); for (cur = start; cur != NULL; cur = cur->next) printf("%-20s %d\n", cur->name, cur->no); printf("press any key to continue\n"); getch(); } /************************************************/ void delete_name(void) { PERSON *cur, *prev; char name[50]; fflush(stdin); printf("name: "); gets(name); for (prev = NULL, cur = start; cur != NULL && strcmp(cur->name, name); prev = cur, cur = cur->next) ;
302

C DLNN TARH VE ZAMAN LE LGL LEM YAPAN STANDART FONKSYONLARI if (cur == NULL) { printf("couldn't find\n"); return; } if (prev == NULL) start = cur->next; else prev->next = cur->next; printf("%s was deleted\n", name); free(cur);

/************************************************/ void fill(void) { int i; PERSON *_new, *prev, *cur; PERSON a[7] = { {"ali", -3, NULL}, {"erdem", 7, NULL}, {"kaan", -1, NULL}, {"necati", 5, NULL}, {"gurbuz", 9, NULL}, {"damla", 20, NULL}, {"metin", 15, NULL} }; for (i = 0; i < 7; i++) { if ((_new = (PERSON *) malloc(sizeof(PERSON))) == NULL) { printf("not enough memory"); exit(EXIT_FAILURE); } *_new = a[i]; for (prev = NULL, cur = start; cur != NULL && strcmp(_new->name, cur->name) > 0; prev = cur, cur = cur->next) ; if (cur != NULL && !strcmp(_new->name, cur->name)) { printf("name already exists\n"); return; } _new->next = cur; if (prev == NULL) start = _new; else prev->next = _new; } } /************************************************/ void search(void) { char name[50]; PERSON *p;
303

26

fflush(stdin); printf("name: "); gets(name); if ((p = find_name(name)) == NULL) printf("could not find\n"); else printf("name: %-20s number: %d\n", p->name, p->no); } /************************************************/ void del_list(void) { PERSON *cur = start, *prev; while (cur != NULL) { prev = cur; cur = cur->next; free(prev); } start = NULL; } /************************************************/ PERSON *find_name(char *name) { PERSON *p; for (p = start; p != NULL && strcmp(name, p->name) > 0; p = p->next) ; if (p != NULL && !strcmp(name, p->name)) return p; return NULL; }

304

TR TANIMLAMALARI ve typedef ANAHTAR SZC

27 . BLM :

TR TANIMLAMALARI ve typedef ANAHTAR SZC

C dilinde bir tre her bakmdan onun yerini tutabilen alternatif isimler verilebilir. Bu ilemler typedef anahtar szc ile yaplr. Genel biimi: typdef <isim> <yeni isim>; rnek : typedef unsigned int UINT; Bu bildirimden sonra UINT ismi derleyici tarafndan unsigned int trnn yeni bir ismi olarak ele alnacaktr. Yani kaynak kod ierisinde UINT szc kullanldnda derleyici bunu unsigned int olarak anlamlandracaktr. UINT x, y, k; Bildiriminde artk x, y, k deikenleri unsigned int trnden tanmlanm olurlar. printf("%d\n", sizeof(UINT)); typedef anahtar szc ile yeni bir tr ismi yaratlmas bu tre ilikin nceki ismin kullanlmasna engel olmaz. Yani yukardaki rnekte gsterilen typedef bildiriminin yaplmasndan sonra unsigned int result; gibi bir bildirimin yaplmasna engel bir durum sz konusu deildir. phesiz #define nilemci komutuyla da ayn i yaplabilirdi. #define UINT unsigned int

Ancak typedef anahtar szc derleyici tarafndan ele alnrken #define ile tanmlanan sembolik sabitler nilemciyi ilgilendirmektedir. Kark tr tanmlamalarnda aadaki kural sentaks asndan hata yaplmasn ngelleyecektir. typedef anahtar szc, her tr bildirimin nne gelir. typedef anahtar szc bir bildirimin nne geldiinde typedef kullanlmam olsayd nesne olacak isimler, typedef anahtar szc eklendiinde artk tr ismi olurlar. rnekler: char * pstr; bildirimi ile pstr char trden bir gsteri olur. typedef char *pstr; bir tr tanmlamasdr ve artk pstr bir tr olarak ele alnr. pstr char trden bir adres trnn baka bir ismi olarak, geerli bir trdr.

305

27 Yani yukardaki tr tanmlamasndan sonra pstr p; gibi bir bildirim yaplabilir. Bildirim char *p ile ayn anlama gelir. Bu rnekte yaplan tr tanmlamas #define nilemci komutuyla, yani sembolik sabit tanmlamasyla yapldnda kodda yanllk olabilirdi. #define pstr char * gibi bir sembolik sabit tanmlamas yapldnda nilemci pstr grd yerde bunun yerine char * yerletirecektir. char *str; gibi bir bildirimin pstr str; olarak yazlmasnda bir hata sz konusu olmaz nk nilemci pstr yerine char * yerletirdiinde derleyiciye giden kod char *str haline gelir. char *p1, *p2, *p3; pstr nilemci sabitini kullanarak yukardaki gibi bir bildirimin yaplmak istendiini dnelim. pstr p1, p2, p3; yazldnda, nilemci yer deitirme ilemini yaptktan sonra derleyiciye verilen kod aadaki biime dnecektir : char *p1, p2, p3; Bu tanmlama yaplmak istenen tanmlamaya eit deildir. Yukardaki bildirimde yalnzca p1 char trden bir gstericidir. p2 ve p3 char trden gstericiler deil char trden nesnelerdir. Bu rnekte grld gibi, typdef anahtar szcnn ilevi #define nilemci komutuyla her zaman yerine getirilemiyor. Bir diziye ilikin tr tanmlamas da yaplabilir. char isimdizi[20]; typedef isimdizi[20]; isimdizi a, b, c; /* isimdizi char trden 20 elemanl bir dizidir. */ /* isimdizi 20 elemanl char trden bir dizi trdr. */ /* a, b ve c herbiri 20 elemanl char trden dizilerdir. */

Bir typedef ile birden fazla tr ismi tanmlanabilir. typedef unsigned int WORD, UINT; Bu tr tanmlamasyla hem WORD hem de UINT unsigned int trnn yerine geebilir. WORD x, y; ...
306

TR TANIMLAMALARI ve typedef ANAHTAR SZC UINT k, l; 10 elemanl char trden gsterici dizisi iin tr tanmlamasn yle yapabiliriz: typedef char *PSTRARRAY[10]; Bu tr tanmlamasndan sonra PSTRARRAY s; ile char *s[10]; tamamen ayn anlama gelecektir. Bir tr ismi baka tr isimlerinin tanmlanmasnda da kullanlabilir. typedef unsigned int WORD; typedef WORD UINT; Tr isimleri geleneksel olarak byk harfle yazlr ama bu C sentaks asndan bir zorunluluk deildir. typedef Tr Tanmlamalarnn Yaplarla Birlikte Kullanm C dilinde yaplar ayr bir veri trdr. Bu tr nce derleyiciye tantldktan sonra, bu tre ilikin deikenler (nesneler) tanmlanabilir. rnein: struct SAMPLE { int a, b, c; }; Yukardaki rnekte yeni bir veri tr yaratlmtr. Bu veri trnn ismi struct SAMPLE dr. (SAMPLE deil) Yani bu veri trnnden bir nesne tanmlamak istersek tr ismi olarak struct SAMPLE yazlmaldr. (Oysa C++ dilinde yap isimleri (structure tags) ayn zamanda trn de ismidir ve struct anahtar szc olmadan kullanldnda bu trn ismi olarak derleyici tarafndan kabul grr.) Yukardaki bildirimden sonra rnein bir tanmlama yaplacak olsa struct SAMPLE x; eklinde yaplmaldr. C dilinde bu tanmlamann SAMPLE x; eklinde yaplmas derleme zamannda hata oluturacaktr. (Oysa C++ dilinde bu durum bir hata oluturmaz.) te struct anahtar szcnn, yap tanmlamlamalarnda yap isminden nce kullanlma zorunluluunu ortadan kaldrmak iin programclar, typedef anahtar szcyle kendi bildirdikleri yap (birlik, bit alanlar, enum trleri ) trlerine ilikin yeni tr isimleri olutururlar. Bir yap trne ilikin yeni bir tr isminin tanmlanmas 2 ayr ekilde yaplabilir.
307

27

1.

nce yap

bildirimini

yapmak, daha sonra bu

yap trnden bir

tr ismi tanmlamak.

struct PERSON { char name[30]; int no; }; typedef struct PERSON PER; int main() { PER x = {Necati Ergin, 123}; printf(%s%d\n, x.name, x.no); return 0;

2. typedef bildirimi ile yap bildirimini bir arada yapmak. typedef struct PERSON { char name[30]; int no; } PER; PER x; Yap ismini (tag) hi belirtmeden de typedef bildirimi yaplabilirdi. typedef struct { char name[30]; int no; } PER; PER y; Ancak yukardaki son durumda artk deiken bildiriminin yeni tanmlanan tr ismiyle yaplmas zorunluluk haline gelir. Programclarn ou yap isimleriyle (tag) tr isimleri iin farkl isimler bulmak yerine birka karakter kullanarak aralarnda iliki kurarlar. ok kullanlan yntemlerden biri, yap isminin bana bir alt tire konularak tr isminden ayrlmasdr : typedef struct _EMPLOYEE { int no; char name[30]; double fee; } EMPLOYEE; (Yukardaki rnekte tamamen ayn isim [mesela EMPLOYEE] hem yap ismi hem de yeni tr ismi olarak kullanlabilir. Bu durum derleme zamannda bir hataya yol amaz. )

308

TR TANIMLAMALARI ve typedef ANAHTAR SZC typedef struct EMPLOYEE { int no; char name[30]; double fee; } EMPLOYEE; nk trn eski ismi EMPLOYEE deil struct EMPLOYEE dir. Yani struct EMPLOYEE trnn ismi EMPLOYEE olarak typedef edilmitir ki bu durum bir saknca oluturmaz. windows.h ierisinde yap ismi ile yeni tr isminin, yap ismine tag szc eklenerek birbirinden ayrldn gryoruz. typedef struct tagEMPLOYEE { int no; char name[30]; double fee; } EMPLOYEE;

Balk Dosyalarnda Standart Olarak Bulunan Baz typedef Bildirimleri Temel C Kurdu boyunca bildirimi bizim tarafmzdan yaplmam olan baz tr isimlerini kullandk. typedef anahtar szc ile ilgili rendiklerimizden sonra bu tr isimlerinin ne olduklarn daha iyi anlayabiliriz. size_t tr ismi ile dinamik bellek fonksiyonlar ile alrken karlamtk. Bu tr isminin bildirimi stdlib.h, alloc.h de dier baz balk dosyalar iinde aadaki ekilde yaplmtr. typedef unsigned size_t; Bu tr aslnda gerekte ne olduu derleyicileri yazanlara braklm olan bir trdr. size_t tr sizeof operatrnn rettii deerin trdr. ANSI standartlarnda bir ok fonksiyonun parametrik yaps ierisinde size_t tr geer. rnein malloc fonksiyonunun gerek prototipi alloc.h, mem.h balk dosyalarnn iinde void *malloc (size_t size); biiminde yazlmtr. Yani malloc fonksiyonunun parametresi size_t trdendir ama bu trn gerekte ne olduu derleyicileri yazanlara braklmtr. Ancak hemen hemen btn derleyicilerde size_t unsigned int olarak belirlenmitir. size_t tr gibi aslnda ne olduklar derleyiciye braklm olan (yani derleyici yazanlarn typedef bildirimlerini yapacaklar) baka tr isimleri de C standartlar tarafndan tanmlanmtr. size_t (sizeof operatrnn rettii deerin tr.) Derleyiciyi yazanlar unsigned int ya da unsigned long trne typedef ederler. (Bir zorunluluk olmasa da derleyicilerin hemen hemen hepsinde unsigned int tr olarak tanmlanmtr.) time_t (time fonksiyonunun geri dn deerinin tr, derleyiciyi yazanlar herhangi bir scalar (birleik olmayan) default veri trne typedef edebilirler. (Bir zorunluluk olmasa da derleyicilerin hemen hemen hepsinde long tr olarak tanmlanmtr.)

309

27 clock_t (clock fonksiyonunun geri dn deerinin tr, derleyiciyi yazanlar herhangi bir skalar (bileik olmayan) default veri trne typedef edebilirler. (Bir zorunluluk olmas da derleyicilerin hemen hemen hepsinde long tr olarak tanmlanmtr.) ptrdiff_t (gsterici deerlerine ilikin fark deeri tr, (Bir zorunluluk olmas da derleyicilerin hemen hemen hepsinde int tr olarak tanmlanmtr.) typedef ile Bildirimi Yaplan Tr simlerinin Faaliyet Alanlar typedef isimleri iin de faaliyet alan kurallar geerlidir. Blok ierisinde tanmlanan bir typedef ismi blok dnda tannmaz. func() { typedef int WORD; ... } int main() { WORD x; /* hata */ ... return 0; } Yukardaki programda WORD x; tanmlamasn gren derleyici bir hata mesaj retecektir. Zira WORD tr yalnzca func fonksiyonunun ana blou iinde tannan ve anlamlandrlan bir veri trdr. Bu bloun dnda tannmamaktadr. C dilinde blok iinde yaplan bildirimler, bloklarn ilk ilemi olmak zorunda olduundan typedef anahtar szcyle blok iinde yaplan tr bildirimleri de bloklarn ilk ilemi olmak zorundadr. Aksi halde derleme zamannda hata oluacaktr. Hemen her zaman typedef bildirimleri global olacak yaplmaktadr. Uygulamalarda typedef bildirimleri genellikle, ya kaynak dosyann banda ya da balk dosyalar iinde tanmlanr. Bir typedef isminin ayn faaliyet alan iinde (rnein global dzeyde) zde olmayacak ekilde iki kere tanmlanmas derleme zamannda hata oluturacaktr : typedef int WORD; ... typedef long WORD;

/* error */

typedef tanmlamalar ne amala kullanlr 1. Okunabilirlii artrmak iin. Baz trlere onlarn kullanm amalarna uygun isimler verilirse kaynak kodu inceleyen kiiler kodu daha kolay anlamlandrr. rnein char tr genelde karakter sabitlerinin atand bir trdr. Yazlacak bir kodda char tr yalnzca bir bytelk bir alan olarak kullanlacaksa, yani karakter ilemlerinde kullanlmayacaksa aadaki gibi bir tr tanmlamas yerinde olacaktr.

310

TR TANIMLAMALARI ve typedef ANAHTAR SZC typedef char BYTE; ... BYTE x; 2. Yazm kolayl salar. Karmak pek ok tr ifadesi typedef sayesinde kolay bir biimde yazlabilmektedir. Program inceleyen kii karmak operatrler yerine onu temsil eden yaln bir isimle karlar. typedef struct PERSON EMPLOYEE[100]; ... EMPLOYEE manager; 3. Tr tanmlamalar tanabilirlii artrmak amacyla da kullanlrlar. Tr tanmlamalar sayesinde kullandmz fonksiyonlara ilikin veri yaplar deise bile kaynak programn deimesi gerekmez. rnein, kullandmz ktphanede birtakm fonksiyonlarn geri dn deerleri unsigned int olsun. Daha sonraki uygulamalarnda bunun unsigned long yapldn dnelim. Eer programc bu bu fonksiyonlara ilikin kodlarda tr tanmlamas kullanmsa daha nce yazd kodlar deitirmesine gerek kalmaz, yalnzca tr tanmlamasn deitirmek yeterlidir. rnein: typedef unsigned int HANDLE; ... HANDLE hnd; hnd = GetHandle(); Burada GetHandle fonksiyonunun geri dn deerinin tr sonraki uyarlamalarda deierek unsigned long yaplm olsun. Yalnzca tr tanmlamasnn deitirilmesi yeterli olacaktr: typedef unsigned long HANDLE; Baka bir rnek : Bir C programnda deerleri 0 ile 50000 arasnda deiebilecek olan saya deikenler kullanacamz dnelim. Bu ama iin long int trn seebiliriz, nk long int tr bilindii gibi 2.147.483.647 ye kadar deerleri tutabilir. Ama long tr yerine int trn kullanmak, aritmetik ilemlerin daha hzl yaplabilmesi asndan tercih edilecektir. Ayrca int trden olan deikenler bellekte daha az yer kaplayabilecektir. int trn kullanmak yerine bu ama iin kendi tr tanmlamamz yapabiliriz : typedef int SAYAC; SAYAC a, b, c; Kaynak kodun int trnn 16 bit uzunluunda olduu bir sistemde derlenmesi durumunda tr tanmlama ifadesini deitirebiliriz: typedef long SAYAC; Bu teknikle tanabilirlie ilikin btn problemlerimizin zlm olacan dnmemeliyiz. rnein SAYAC trnden bir deiken printf ya da scanf fonksiyonu armlarnda arguman olarak kullanlm olabilir :

311

27 typedef int SAYAC; ... SAYAC a, b, c; ... scanf("%d%d%d", &a, &b. &c); ... printf("%d %d %d", a, b, c); Yukardaki ifadelerde a. b. c deikenleri SAYAC trnden (int trden) tanmlanmlardr. printf ve scanf fonksiyonlarnn arma ifadelerinde de bu deikenlere ilikin format karakterleri doal olarak %d seilmitir. Ancak SAYAC trnn long tr olarak deitirilmesi durumunda printf ve scanf fonkksiyonlarnda bu trden deikenlerin yazdrlmasnda kullanlan format karakterlerinin de %d yerine %ld olarak deitirilmesi gerekecektir. typedef le Gsterici Trlerine sim Verilmesi typedef anahtar szc ile gsterici trlerine isim verilmesinde dikkat etmemiz gereken bir nokta var. Belirleyiciler (specifiers) konusunda incelediimiz gibi C dilinde aadaki gibi bir tanmlama yapldnda const int *ptr; ptr gstericisinin gsterdii yerdeki nesne deitirilemez. Yani *ptr = 10; gibi bir atama yaplmas durumunda derleme zamannda hata oluacaktr. Ancak tanmlama int *const ptr; eklinde yaplrsa ptr gstericisinin gsterdii yerdeki nesnenin deeri deitirilebilir, ama ptr gstericisinin iindeki adres deitirilemez, yani ptr = (int *) 0x1F00; gibi bir atama yaplmas durumunda derleme zamannda hata oluur. typedef int * IPTR; gibi bir tr tanmlanarak const IPTR p; eklinde bir tanmlama yapldnda, p gstericisinin deeri deitirilemez, p gstericisinin gsterdii yerdeki nesnenin deeri deitirilebilir. (yani *p nesnesine atama yaplabilir.) Baka bir deyile const IPTR p; deyimi ile int *const ptr;

312

TR TANIMLAMALARI ve typedef ANAHTAR SZC deyimi edeerdir. Windows iletim sistemi altnda alacak C ya da C++ programlarnn yazlmasnda typedef sklkla kullanlmaktadr. windows.h isimli balk dosyasnda temel veri trlerinin ouna typedef'le yeni isimler verilmitir. Windows programlamada Windows.h dosyas kaynak koda dahil edilmelidir. Bu dosyann ierisinde API fonksiyonlarnn prototipleri, eitli yap bildirimleri, typedef isimleri, nemli sembolik sabitler bulunmaktadr. windows.h erisinde Tanmlanan Typedef simleri typedef int BOOL Bu trle ilikili 2 sembolik sabit de tanmlanmtr. #define FALSE #define TRUE 0 1

BOOL tr, zellikle fonksiyonlarn geri dn deerlerinde karmza kar. Bu durum fonksiyonun baarlysa 0 d bir deere, baarszsa 0 deerine geri dnecei anlamna gelir. Baar kontrol, 1 deeriyle karlatrlarak yaplmamaldr. Aadaki typedef isimleri, iaretsiz 1 byte, 2 byte ve 4 byte tam saylar temsil eder. typedef typedef typedef typedef 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 kullanlmaktadr. Win16 sistemlerinde uzak ve yakn gsterici kavramlar vard. Dolaysyla o zamanlar, P n ekli 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, Win16daki alkanlkla hala LP nekli typedef isimleri kullanlmaktadr. Windows.h ierisinde her ihtimale kar (Win16 programlar alabilsin diye) near ve far szckleri aadaki gibi silinmitir. #define far #define near typedef char near *PSTR; typedef char far *LPSTR; PSTR ya da LPSTR Win32 sistemlerinde tamamen ayn anlama gelir ve char trden adres trn belirtir. typedef char *PSTR; typedef char *LPSTR; Gstericilerde constluk P ya da LPden sonra C ekiyle belirtilir. rnein; typedef const char *PCSTR; typedef const char *LPCSTR;

313

27 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;

Cnin doal trlerinin hepsinin byk harf normal, adres ve const adres versiyonlar vardr. typedef long LONG; typedef int INT; typedef char CHAR; Windows programlamada H ile balayan, handle olarak kullanlan pek ok typede ismi vardr. Bu typedef isimlerinin hepsi void * trndendir. rnein: typedef void *HWND; typedef void *HICON;

314

BRLKLER

28 . BLM :

BRLKLER

Programc tarafndan tanmlanan (user defined) ve bileik bir veri yaps olan birlikler yaplara ok benzer. Birliklerin de 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. Birlik bildirimlerinin genel ekli yledir: union [birlik ismi] { tr <birlik eleman>; tr <birlik eleman>; tr <birlik eleman>; ... }; rnein; union DWORD { unsigned char byte; unsigned int word; unsigned long dword; }; Dier bir rnek: union DBL_FORM { double n; unsigned char s[8]; };

Birlik Deikenlerinin Tanmlanmas Birlik deikenlerinin tanmlanmas ayn yap deikenlerinde olduu gibidir. Tpk yaplarda olduu gibi birliklerde de bellekte yer ayrma ilemi bildirim ile deil tanmlama ilemi ile yaplmaktadr. Birlik deikeni tanmlamalarnn yap bildirimi tanmlamalarndan tek fark struct anahtar szc yerine union anahtar szcnn kullanlmasdr. rnekler : union DWORD a, b; ile a ve b union DWORD trnden iki deiken olarak tanmlanmlardr. union DBL_FORM x, y; x ve y union DBL_FORM trnden iki deikendir. Yine tpk yaplarda olduu gibi birliklerde de bildirim ile tanmlama ilemi birlikte yaplabilir : union DBL_FORM { double n; unsigned char s[8]; } a, b, c;

315

28 Bu durumda a, b ve c deikenlerinin faaliyet alanlar birlik bildiriminin yapld yere bal olarak yerel ya da global olabilir. Birlik elemanlarna da yap elemanlarnda olduu gibi nokta operatryle eriilir. rnein yukardaki tanmlama dikkate alnrsa a.n birliin double trnden ilk elemann belirtmektedir. Benzer biimde birlik trnden gstericiler de tanmlanabilir. Ok operatr ile yine yaplarda olduu gibi birliklerde de gsterici yoluyla birlik elemanlarna ulalabilir. rnein: union DWORD *p; ... printf("%d", p->word); p->word ifadesi ile p adresinde birlik nesnesinin word elemanna eriilmektedir. Genel olarak unlar syleyebiliriz: Yaplarla birliklerin bildirilmesi, tanmlanmas ve yap deikenlerinin kullanlmas tamamen ayn biimde yaplmaktadr. kisi arasnda tek fark yap ve birlik elemanlarnn bellekteki organizasyonunda ortaya kar. Birlik elemanlarnn bellekteki organizasyonu konusuna deinmeden nce bir bytedan daha uzun bilgilerin bellekteki grnmleri zerinde duralm. Saylarn Bellekteki Yerleimleri Bir bytedan daha byk olan saylarn bellee yerleim biimi kullanlan mikroilemciye gre deiebilir. Bu nedenle saylarn bellekteki grnmleri tanabilir bir bilgi deildir. Mikroilemciler iki tr yerleim biimi kullanabilirler: Dk anlaml byte deerleri bellein dk anlaml adresinde bulunacak biimde. (little endian) 80x86 ailesi Intel ilemcileri bu yerleim biimini kullanr. Bu ilemcilerin kullanld sistemlere rnein int x = 0x1234; biimindeki bir x deikeninin bellekte 1FC0 adresinden balayarak yerletiini varsayalm: ... 34 12 ...

1AOO 1A01

dk anlaml byte yksek anlaml byte

ekilden de grld gibi x deikenini oluturan saylar dk anlaml byte deeri (34H) dk anlaml bellek adresinde (1A00H) olacak biimde yerletirilmitir. imdi aadaki kodu inceleyiniz : ... unsigned long a = 0x12345678; unsigned int *b; b = (unsigned int*) &a; printf(%x\n, *b); ile eklrana hex sistemde 5678 saylar baslr. Aadaki ekli inceleyiniz. b deikeninin 1B10 adresinden balayarak yerletirildii varsaylmtr : ... 1B10 78 1B10 *b

316

BRLKLER 1B11 1B12 1B13 ... Dk anlaml byte deerleri, bellein yksek anlaml adresinde bulunacak ekilde (big endian) Motorola ilemcileri bu yerleim biimini kullanr. rnein yukardaki kod Motorola ilemcilerinin kullanld bir sistemde yazlm olsayd : unsigned long a = 0x12345678; unsigned int *b; b = (unsigned int*) &a; printf(%x\n, *b); ekrana 1234 baslrd. ... 1B10 1B11 1B12 1B13 ... Aadaki kod kullanlan sistemin little endian ya da big endian oldugunu test etmektedir : int x = 1; if (*(char *)&x == 1) printf("little-endian\n"); else printf("big-endian\n"); Birlik Elemanlarnn Bellekteki Organizasyonu Birlik deikenleri iin birliin en uzun eleman kadar yer ayrlr. Birlik elemanlarnn hepsi ayn orjinden balayacak ekilde bellee yerleirler. rnein : union DWORD { unsigned char byte; unsigned int word; unsigned long dword; }; ... union DWORD a; bildirimi ile a deikeni iin 4 byte yer ayrlacaktr. nk a deikeninin dword eleman 4 byte ile birliin en uzun elemandr. Birlik bir dizi ieriyorsa dizi tek bir eleman olarak alnr. rnein a.word
317

56 34 12

12 34 56 78

1B10

*b

28 A00 1A01 1A02 1A03 a.byte a.dword

union DBL_FORM { double n; unsigned char s[8]; }; ... union DBL_FORM x; tanmlamas ile x deikeni iin ne kadar yer ayrlacaktr? DBL_FORM birliinin iki eleman vardr. Birincisi 8 byte uzunluunda bir karakter dizisi, ikincisi de 8 byte uzunluunda double trden bir deikendir. ki uzunluk da ayn olduguna gre x iin 8 byte yer ayrlacan syleyebiliriz: ... s[0] s[1] s[2] s[3] s[4] s[5] s[6] s[7] ...

x.n

Buna gre DBL_FORM X; ... X.n = 10.2; ile x.S[0], x.s[1], x.s[2], ....x.s[7] srasyla double elemannn byte deerlerini gstermektedir. Birlik elemanlarnn ayn orjinden, yani ayn adresten balayarak yerletirilmesi bir elemann deitirince dier elemanlarnn da ieriinin deiecei anlamna gelir. Zaten birliklerin kullanlmasnn asl amac da budur. Birlik eleman olarak yaplarn kullanlmas uygulamada en sk karlalan durumdur. rnein : struct WORD { unsigned char low_byte; unsigned char high_byte; }; union WORD_FORM { unsigned int x; struct WORD y; };

318

BRLKLER bildirimlerinden sonra union WORD_FORM trnden bir birlik tanmlanrsa : union WORD_FORM wf; bu birliin alak (low_byte) ve yksek (high_byte) anlaml byte deerlerine ayr ayr eriebiliriz ya da onu bir btn olarak kullanabiliriz : Yani intel ilemcilerinin bulunduu 16 bit sistemlerde : wf.y.low_byte = 0x12; wf.y.high_byte = 0x34; ilemlerinden sonra; printf(%x\n, wf.x); ile 3412 saysn ekrana yazdracaktr. (Motorola ilemcilerinde bellein dk anlaml blgesinde dk anlaml byte deeri olacana gre saynn ters olarak grlmesi gerekir. Birlik elemanlarnn bellekte ayn orjinden balanarak yerletirildiini aadaki kod parasndan da rahatlkla grebiliriz : Birlik Nesnelerine lk Deer Verilmesi ANSI ve ISO standartlarna gre birliknes nelerinin yalnzca ilk elemanlarna deer verilebilir. Eer bir birlik nesnesinin birden fazla elemanna ilk deer verilmeye allrsa derleme zamannda error oluacaktr: union DWORD { unsigned char byte; unsigned int word; unsigned long dword; } x = {'m'}; union DWORD y = {'a', 18, 24L}; /* error */

Birlikler Neden Kullanlr Birlikler iki amala kullanlabilir. Birincisi yer kazanc salamak iindir. Birlik kullanarak farkl zamanlarda kullanlacak birden fazla deiken iin ayr ayr yer ayrma zorunluluu ortadan kaldrlr. rnein bir hediyelik eya katalo ile 3 deiik rnn satldn dnelim: kitap, tshort ve saat. Her bir rn iin bir stok numaras, fiyat bilgisi ve tip bilgisinin dnda rne bal olarak baka zelliklerine de sahip olduunu dnelim: kitaplar : isim, yazar, sayfa sayisi. t-short : desen, renk, size. saat : model struct katalog { int stok_no; float fiyat; int urun_tipi char kitapisim[20]; char yazar[20]; int sayfa_sayisi;
319

28 char desen[20]; int renk; int size; char saatisim[20]; int model; }; Yukardaki bildirimde urun_tipi eleman yalnzca KITAP, TSHORT ya da SAAT olabilecektir. (Bunlarn sembolik sabit olarak tanmlandn dnelim.) Yukarda bildirilen yap rnlerin btn zelliklerini tutabilmekle birlikte yle bir dezavantaj vardr : Eer rn tipi KITAP deil ise isim[20], yazar[30] ve sayfa_sayisi elemanlar hi kullanlmayacaktr. Yine rn tipi TSHORT deil ise desen[20], renk, size elemanlar hi kullanlmayacaktr. Ama katalog yapsnn iine birlikler yerletirirsek yer kazancmz olacaktr: struct katalog { int stok_no; float fiyat; int urun_tipi union { struct { char isim[20]; char yazar[20]; int sayfa_sayisi; } kitap; struct { char desen[20]; int renk; int size; } tshort; struct { char isim[20]; int model; } saat; } cins; };

Birlik kulanmnn ikinci nedeni herhangi bir veri trnn paralar zerinde ilem yapmak ya da paralar zerinde ilem yaparak bir veri trnnden btnn oluturmaktr. Bunun iin int trden bir saynn byte deerlerini ayrtrma rneini yineleyebiliriz : struct WORD { unsigned char low_byte; unsigned char high_byte; }; union WORD_FORM { unsigned int x; struct WORD y;
320

BRLKLER }; union word_form wf; Tanmlamalarndan sonra wf.x elemanna deer atadmzda wf.y.low_byte ve wf.y.high_byte ile yksek ve alak anlaml byte deerleri zerine kolaylkla ilem yapabiliriz. Birliklerin Kark Veri Yaplarnda Kullanlmas Birlikleri kullanarak, elemanlar farkl trden olan diziler oluturabiliriz. Belirli deerleri bir dizi iinde tutmak zorunda olduumuzu dnelim. Ancak dizi elemanlar int, long, double trlerinden olabilsin. rnein dizinin, herhangi bir elemanna ulatmzda bu eleman int ise onu int olarak, long ise long olarak ve double ise double olarak (bilgi kayb olmakszn) kullanabilelim. Diziyi double tr ile tanmlarsak, dizi elemanlarna atama yaptmz zaman, otomatik tr dnm sonucunda btn elemanlar double trne evrileceinden dizi elemanlarnn tr bilgisini kaybetmi olurduk. Ancak aadaki ekilde bir birlik oluturabiliriz : union DEGER { int i; long l; double d; }; union DEGER trnden her bir nesne bellekte, en uzun eleman kadar, yani 8 byte yer kaplayacak, ve her bir eleman (member) ayn adresten balayacaktr. imdi double trden bir dizi amaktansa union DEGER trnden bir dizi aabiliriz. Dizinin her bir elemannn, istediimiz alt elemanna deer atamas yapabiliriz : main() { union DEGER s[100]; s[0].i = 3; s[1].d = 6. s[2].l = 12L ... } Hangi trden bir deer tutmak istiyorsak, bu deeri s dizisinin eleman olan bir union DEGER nesnesinin ilgili alt elemannda tutuyoruz. Ancak hala yle bir eksiimiz var : rnein dizinin 17. elemann gerek tryle ve deeriyle kullanmak istiyoruz. 17. eleman nesnesininin hangi trden olduunu nereden bileceiz? Hangi dizi elemannn hangi trden deer iin kullanld bilgisini de bir ekilde saklamalyz. Bunun iin en iyi yntem bir yap bildirimi yapmak, ve DEGER birliini de bu yapnn alt eleman olarak bildirmektir. Yapnn baska bir eleman da birliin hangi elemannn kullanlacag bilgisini tutacaktr : #define INT #define LONG 0 1
321

/* s[0] int trden bir deeri tutuyor */ /* s[1] double trden bir deeri tutuyor */ /* s[2] long trden bir deeri tutuyor */

28 #define DOUBLE 2 ...

struct KARTIP { char type; union DEGER deger; }; imdi struct KARTIP trnden bir dizi tanmlayabiliriz: ... struct KARTIP s[100]; ... s[0].type = INT; S[0].deger.i = 3; s[1].type = DOUBLE; s[1].deger.d = 6.; s[2].type = LONG; s[2].deger.d = 12L;

322

BTSEL OPERATRLER

29 . BLM :

BTSEL OPERATRLER

Bitsel operatrler (Bitwise operators) bir tamsaynn bitleri zerinde ilem yapan operatrlerdir, daha ok sistem programlarnda kullanlrlar. Bitsel operatrlern ortak zellikleri operandlar olan tamsaylar bir btn olarak deil, bit bit ele alarak ileme sokmalardr. Bitsel operatrlerin operandlar tamsay trlerinden olmak zorundadr. Operandlar tamsay trlerinden birinden deilse derleme zamannda hata oluur. C dilinde toplam 11 tane bitsel operatr vardr. Bu operatrler, kendi aralarndaki ncelik srasna gre, aadaki tabloda verilmitir: 2 5 8 9 10 14 ~ << >> & ^ | <<= >>= &= ^= |= bitsel deil bitsel sola kaydrma bitsel saa kaydrma bitsel ve bitsel zel veya bitsel veya bitsel ilemli operatrleri bitwise bitwise bitwise bitwise bitwise bitwise atama bitwise not left shift right shift and exor or compound assignment operators

Yukardaki operatrler iinde yalnzca bitsel Deil (bitwise not) operatr nek konumunda tek operand alan (unary prefix) bir operatrdr. Dier btn bitsel operatrler iki operand alrlar ve operandlarnn arasnda bulunurlar. (binary infix) imdi bu operatrleri tek tek tanyalm : Bitsel Deil Operatr Dier tm tek operand alan operatrler gibi operatr ncelik tablosunun ikinci seviyesindedir. Yan etkisi yoktur. (side effect) Operand bir nesne ise, bu nesnenin bellekteki deerini deitirmez. rettii deer, operand olan tamsaynn bitleri zerinde 1'e tmleme ilemi yaplarak elde edilen deerdir. Yani operand olan ifade deerinin 1 olan bitleri 0, 0 olan bitleri 1 yaplarak deer retilir. #include <stdio.h> int main() { unsigned int x = 0x1AC3; unsigned int y; y = ~x; printf("y = %x\n", y); return 0;

/* x = 0001 1010 1100 0011 */

/* y = 1110 0101 0011 1100 */ /* y = 0xE53C */

Bitsel Kaydrma Operatrleri (bitwise shift operators) ki adet bitsel kaydrma operatr vardr: Bitsel saa kaydrma operatr >> (bitwise right shift)
323

29 Bitsel sola kaydrma operatr << (bitwise left shift) Her iki operatr de operatr ncelik tablosunun 5. seviyesinde bulunmaktadr. (Dolaysyla bu operatrlerin ncelii tm aritmetik operatrlerden daha aada fakat karlatrma operatrlerinden daha yukardadr. Bitsel kaydrma operatrleri binary infix operatrlerdir. (ki operand alan araek konumundaki operatrlerdir.) Bitsel sola kaydrma operatrnn rettii deer soldaki operand olan tamsaynn sadaki operand olan tamsay kadar sola kaydrlm biimidir. Menzil dna kan bitler iin saynn sandan 0 biti ile besleme yaplr. rnek : #include <stdio.h> int main() { unsigned int x = 52; unsigned int y = 2; z = x << y; printf("z = %d\n", z) return 0;

/* x = 0000 0000 0011 0100 */

/* z = 0000 0000 1101 0000 */ /* z = 208 */

Bir sayy sola bitsel olarak 1 kaydrmakla o saynn ikiyle arplm deeri elde edilir. Bir sayy saa bitsel olarak 1 kaydrmakla o saynn ikiye blnm deeri elde edilir. Bitsel kaydrma operatrlerinin yan etkileri yoktur. Yani soldaki operandlar bir nesne ise, bu nesnenin bellekteki deerini deitirmezler. Kaydrma sonucu soldaki nesnenin deeri deitirilmek isteniyorsa daha sonra inceleyeceimiz bitsel ilemli atama operatrleri kullanlmaldr. (>>= ya da <<=) Bitsel saa kaydrma operatrnn rettii deer soldaki operand olan tamsaynn sadaki operand olan tamsay kadar saa kaydrlm biimidir. Soldaki operand iaretsiz (unsigned) ya da pozitif iaretli (signed) bir tamsay ise menzil dna kan bitler yerine saynn solundan besleme 0 biti ile yaplmas C standartlar tarafndan garanti altna alnmtr. Saa kaydrlacak ifadenin negatif deerli, iaretli bir tamsay trnden olmas durumunda menzil dna kan bitler iin soldan yaplacak beslemenin 0 ya da 1 bitleriyle yaplmas uygulama bamldr. (implementation depended). Yani derleyiciyi yazanlar bu durumda saynn iaretini korumak iin soldan yaplacak beslemeyi 1 biti ile yapabilecekleri gibi, saynn iaretini korumay dnmeksizin 0 biti ile de yapabilirler. aretli negatif bir tamsaynn bitsel saa kaydrlmas tanabilir bir zellik olmadndan dikkatli olunmaldr. #include <stdio.h> int main() { unsigned int x = 52; unsigned int y = 2; z = x >> y; printf("z = %dn", z) return 0; }

/* x = 0000 0000 0011 0100 */

/* z = 0000 0000 0000 1101 */ /* z = 13 */

324

BTSEL OPERATRLER Bitsel Ve Operatr (bitwise and) Operatr ncelik tablosunun 8. seviyesindedir. ncelik yn soldan saadr. Yan etkisi yoktur, operandlar nesne gsteren bir ifade ise bellekteki deerlerini deitirmez. Deer retmek iin operand olan tamsaylarn karlkl bitlerini Ve ilemine tabi tutar. Ve ilemine ait doruluk tablosu aada tekrar verilmektedir: x y x&y 1 1 1 1 0 0 0 1 0 0 0 0 Bitsel ve operatrnn rettii deer operandlarnn karlkl bitlerinin ve ilemine sokulmasyla elde edilen deerdir. #include <stdio.h> int main() { unsigned int x = 0x1BC5; unsigned int y = 0X3A0D unsigned int z; z = x & y; printf("z = %xn", Z); return 0;

/* x = 0001 1011 1100 0101 */ /* y = 0011 1010 0000 1101 */

/* z = 0001 1010 0000 0101 */ /* z = 0x1A05 */

1 biti bitsel ve ileminde etkisiz elemandr. 0 biti bitsel ve ileminde yutan elemandr. Bitsel zel Veya Operatr (bitwise exor) Operatr ncelik tablosunun 9. seviyesindedir. ncelik yn soldan saadr. Yan etkisi yoktur, operandlar nesne gsteren bir ifadeyse bellekteki deerlerini deitirmez. Deer retmek iin operand olan tamsaylarn karlkl bitlerini zel veya (exclusive or) ilemine tabi tutar. Bitsel zel veya ilemine ilikin doruluk tablosu aada verilmektedir: x 1 1 0 0 y 1 0 1 0 x^y 0 1 1 0

Yukardaki tablo yle zetlenebilir. Operandlardan ikisi de ayn deere sahip ise retilen deer 0 olacak, operandlardan biri dierinden farkl ise retilen deer 1 olacaktr. #include <stdio.h> int main() { unsigned int x = 0x1BC5;

/* x = 0001 1011 1100 0101 */

325

29 unsigned int y = 0X3A0D unsigned int z; z = x ^ y; printf("z = %xn", z); return 0; /* y = 0011 1010 0000 1101 */

/* z = 0010 0001 1100 1000 */ /* z = 0x21C8 */

Bir sayya arka arka arkaya ayn deerle zel veya ilemi yaplrsa ayn deer elde edilir: #include <stdio.h> int main() { unsigned int x = 0x1BC5; /* x = 0001 1011 1100 0101 */ unsigned int y = 0X3A0D /* y = 0011 1010 0000 1101 */ x = x ^ y; /* x = 0010 0001 1100 1000 x = 0x21C8 */ x = x ^ y; /* x = 0001 1011 1100 0101 x = 0x1BC5 */ printf("x = %xn", x); /* x = 0x0x1BC5 */ return 0; } Baz ifreleme algoritmalarnda zel veya ileminin bu zelliinden faydalanlmaktadr. bitsel exor operatr tamsay trlerinden iki deikenin deerlerinin, geici bir deiken olmakszn swap edilmesinde (deerlerinin deitirilmesinde) de kullanlabilir. Aadaki programda x ve y deikenlerini deerleri bitsel exor operatrnn kullanlmasyla swap edilmektedir : #include <stdio.h> int main() { int x = 10; int y = 20; x ^= y ^= x^= y; printf("x = %d\n y = %d\n", x, y); return 0;

Bitsel Veya Operatr (bitwise or operator) Operatr ncelik tablosunun 10. seviyesindedir. ncelik yn soldan saadr. Yan etkisi yoktur, operandlar nesne gsteren bir ifade ise bellekteki deerlerini deitirmez. Deer retmek iin operand olan tamsaylarn karlkl bitlerini veya ilemine tabi tutar. Veya ilemine ait doruluk tablosu aada verilmektedir: x 1 1 0 0 y 1 0 1 0 x|y 1 1 1 0

326

BTSEL OPERATRLER

Bitsel veya operatrnn rettii deer operandlarnn karlkl bitlerinin veya ilemine sokularak elde edilen deerdir. #include <stdio.h> int main() { unsigned int x = 0x1BC5; unsigned int y = 0X3A0D unsigned int z; z = x | y; printf("z = %x\n", z); return 0;

/* x = 0001 1011 1100 0101 */ /* y = 0011 1010 0000 1101 */

/* z = 0011 1011 1100 1101*/ /* z = 0x3BCD */

0 biti bitsel veya ileminde etkisiz elemandr. 1 biti bitsel ve ileminde yutan elemandr. Bitsel Operatrlere likin lemli Atama Operatrleri Bitsel deil operatrnn dnda tm bitsel operatrlere ilikin ilemli atama operatrleri vardr. Daha nce de sylendii gibi bitsel operatrlerin yan etkileri (side effect) yoktur. Bitsel operatrler operandlar olan nesnelerin bellekteki deerlerini deitirmezler. Eer operandlar olan nesnelerin deerlerinin deitirilmesi isteniyorsa bu durumda ilemli atama operatrleri kullanlmaktadr. 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 Operatrlerin Kullanlmasna likin Baz Temalar Bitsel operatrlerin kullanlmasna daha ok sistem programlarnda raslanr. Sistem programlarnda bir saynn bitleri zerinde baz ilemler yaplmas sklkla gerekli olmaktadr. Aada bitsel dzeyde yaplan ilemlerden rnekler verilmektedir. Bir Saynn Belirli Bir Bitinin 1 Yaplmas (birlenmesi ) (set a bit / turn a bit on) Buna saynn belirli bir bitinin set edilmesi de denebilir. Bir saynn belirli bir bitini set etmek iin, sz konusu say, ilgili biti 1 olan ve dier bitleri 0 olan bir sayyla veya ilemine tabi tutulmaldr. nk bitsel veya ileminde 1 yutan eleman 0 ise etkisiz elemandr. Aadaki rnekte bir saynn 5. biti set edilmektedir. #include <stdio.h> #include <conio.h> int main()

327

29 { int ch = 0x0041; int mask = 0x0020; /* ch = 65 /* mask = 32 0000 0000 0100 0001 */ 0000 0000 0010 0000 */

ch |= mask; /* ch = 0000 0000 0110 0001 */ printf("ch = %d\n", ch); /* ch = 97 */ getch(); return 0;

x bir tamsay olmak zere, bir saynn herhangi bir bitini set edecek bir ifadeyi u ekilde yazabiliriz: x |=1 << k Bir Saynn Belirli Bir Bitinin 0 Yaplmas (sfrlanmas) (clear a bit / turn the bit off) Buna saynn belirli bir bitinin temizlenmesi de denebilir. Bir saynn belirli bir bitini sfrlamak iin, sz konusu say, ilgili biti 0 olan ve dier bitleri 1 olan bir sayyla bitsel ve ilemine tabi tutulmaldr. nk bitsel ve ileminde 0 yutan eleman 1 ise etkisiz elemandr. Bir bitin sfrlanmas iin kullanlacak bu sayya maske (mask) denir. Aadaki rnekte bir saynn 5. biti sfrlanmaktadr. #include <stdio.h> #include <conio.h> int main() { int ch = 0x0061; /* int mask = ~0x0020; /* ch &= mask; /* printf("ch = %dn", ch);/* getch(); return 0; }

ch = 97 mask = ~32 ch ch = 65 */

0000 0000 0110 0001 */ 1111 1111 1101 1111 */ 0000 0000 0100 0001 */

x bir tamsay olmak zere, bir saynn herhangi bir bitini set edecek bir ifadeyi u ekilde yazabiliriz x &= ~(1 << k); Bir Saynn Belirli Bir Bitinin Deerinin Test Edilmesi (0 m 1 mi) Bir saynn belirli bir bitinin 0 m 1 mi oldugunun renilmesi iin, sz konusu say, if deyiminin koul ifadesi olarak (if parantezi iinde) ilgili biti 1 olan ve dier bitleri 0 olan bir sayyla bitsel ve ilemine tabi tutulmaldr. nk bitsel ve ileminde 0 yutan eleman 1 ise etkisiz elemandr. programn ak if deyiminin doru ksmna giderse, ilgili bitin 1, yanl ksmna gider ise ilgili bitin 0 oldugu sonucu karlacaktr. x bir tamsay olmak zere, bir saynn herhangi bir bitinin 1 ya da 0 olduunu anlamak iin aadaki ifadeyi yazabiliriz. if (x & 1 << k) /* k. bit 1 */
328

BTSEL OPERATRLER else /* k. bit 0 */ Bir Saynn Belirli Bir Bitini Ters evirmek (toggle) Baz uygulamalarda bir saynn belirli bir bitinin deerinin deitirilmesi istenebilir. Yani sz konusu bir 1 ise 0 yaplacak, sz konusu bit 0 ise 1 yaplacaktr. Bu amala bitsel zel veya operatr kullanlr. Bitsel zel veya operatrnde 0 biti etkisiz elemandr. Bir saynn k.bitinin deerini deitirmek iin, say, k.biti 1 dier bitleri 0 olan bir say ile bitsel zel veya ilemine tabi tutulur. x bir tamsay olmak zere, bir saynn herhangi bir bitinin deerini deitirmek iin aadaki ifadeyi yazabiliriz. x ^= 1 << k; Bir tamsaynn belirli bitlerini sfrlamak iin ne yapabiliriz? rnein int trden bir nesnenin 7., 8. ve 9.bitlerini sfrlamak isteyelim (tabi dier bitlerini deitirmeksizin). 7., 8. ve 9. bitleri 0 olan dier bitleri 1 olan bir say ile bitsel ve ilemine tabi tutarz. rnein 16 bitlik int saylarn kullanld bir sistemde bu say aadaki bit dzenine sahip olacaktr. 1111 1100 0111 1111 x &= 0xFC7F; Bu gibi tamsaylarn bitleri zerinde yaplan ileri fonksiyonlara yaptrmaya ne dersiniz? void clearbits(int *ptr, int startbit, int endbit); clearbits fonksiyonu adresi gnderilen ifadenin startbit ve endbit aralndaki bitlerini sfrlayacaktr. rnein x isimli int trden bir nesnenin 7. 8. 9. bitlerini sfrlamak iin fonksiyon aadaki ekilde arlacaktr: clearbits(&x, 7, 9); void clearbits(int *ptr, int startbit, int endbit) { int k; for (k = startbit; k <= endbit; ++k) *ptr &= ~(1 << k); (bu say 0xFC7F deilmidir?)

Benzer ekilde setbits fonksiyonunu da yazabiliriz : void setbits (int *ptr, int startbit, int endbit) { int k; for (k = startbit; k <= endbit; ++k) *ptr |= 1 << k;

329

29 Peki rnein 16 bitlik bir alann iindeki belirli bitleri birden fazla deeri tutacak ekilde kullanabilir miyiz? rnein 4 bitlik bir alan ierinde (negatif saylar kullanmadmz dnrsek 0 15 aralndaki deerleri tutabiliriz. O zaman yalnzca bu aralktaki deerleri kullanmak istiyorsak ve bellek (ya da dosya) bykl asndan kstlamalar sz konusu ise 16 bitlik bir alan iinde aslnda biz 4 ayr deer tutabiliriz deil mi? Bir saynn belirli bir bit alannda bir tamsay deerini tutmak iin ne yapabiliriz? nce saynn ilgili bitlerini sfrlar (rnein yukarda yazdmz clearbits fonksiyonuyla) daha sonra sayy, sfrlanm bitleri yerletireceimiz saynn bit paternine eit dier bitleri 0 olan bir say ile bitsel veya ilemine tabi tutarz, deil mi? Aadaki fonksiyon prototip bildirimine bakalm: void putvalue(int *ptr, int startbit, int endbit, int value); putvlaue fonksiyonu adresi gnderilen nesnenin startbit endbit deerleri arasndaki bitlerine value saysn yerletirecektir. Fonksiyon aadaki gibi arlabilir : putvalue(&x, putvalue(&x, putvalue(&x, putvalue(&x, 0, 3, 8); 4, 7, 12); 8, 11, 5); 12, 15, 3);

void putvalue(int *ptr, int startbit, int endbit, int value); { int temp = value << startbit; clearbits(ptr, startbit, endbit); *ptr |= temp; } Peki yukardaki fonksiyonlar ile rnein 16 bitlik bir saynn belirli bit alanlar iinde saklanm deerleri nasl okuyacaz. Bu ii de bir fonksiyona yaptralm : int getvalue(int x, int startbit, int endbit); getvalue fonksiyonu startbit ve endbit ile belirlenen bit alanna yerletirilmi deer ile geri dnecektir. int getvalue(int number, int startbit, int endbit) { int temp = number >>= startbit; clearbits(&temp, endbit + 1, sizeof(int) * 8 - 1); return temp;

Uygulama int trden bir saynn bit bit ekrana yazdrlmas include <stdio.h>

330

BTSEL OPERATRLER void showbits(int x) { int i = sizeof(int) * 8 - 1; for (; i >= 0; --i) if (x >> i & 1 == 1) putchar('1'); else putchar('0'); } Bu fonksiyonu aadaki ekilde de yazabilirdik: #include <stdio.h> void showbits2(int x) { unsigned i = (~((unsigned)~0 >> 1)); while (i) { if (x & i) putchar('1'); else putchar('0'); i >>= 1; }

Uygulama int trden bir saynn bitlerini ters eviren reverse_bits fonksiyonunun yazlmas: #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; }

Uygulama int trden bir deerin ka adet bitinin set edilmi olduu bilgisine geri dnen no_of_setbits fonksiyonunun yazlmas :
331

29

#include <stdio.h>

int no_of_setbits(int value) { int counter = 0; int k; for (k = 0; k < sizeof(int) * 8; ++k) if (value & 1<<k) counter++; return counter; } int main() { int number; printf("bir say girin : "); scanf("%d", &number); printf("saynzn %d biti 1n", no_of_setbits(number)); return 0; } Daha hzl alacak bir fonksiyon tasarlamaya ne dersiniz? #include <stdio.h> int no_of_setbits(int value) { static int bitcounts[] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; int counter = 0; for (; value != 0; value >>= 4) counter += bitcounts[value & 0x0F]; return counter;

332

BT ALANLARI

30 . BLM :
konu eklenecek

BT ALANLARI

333

31

31 . BLM :

KOMUT SATIRI ARGUMANLARI

Bir program altrdmz zaman, programn almasn ynlendirecek ya da deitirecek birtakm parametre(leri) alacak programa gndermek isteyebiliriz. Bu parametreler bir dosya ismi olabilecei gibi, programn deiik biimlerde almasn salayacak bir seenek de olabilir. UNIX iletim sistemindeki ls komutunu dnelim. Eer ls programn ls yazarak altrrsak, bulunulan dizin iindeki dosyalarn isimleri listelenir. (DOS'daki dir komutu gibi). Ama ls yerine ls -l yazlarak program altrlrsa bu kez yalnzca dosya isimleri deil, dizindeki dosyalarn bykln , dosyalarn sahiplerini, yaratlma tarih ve zamanlarn vs. gsteren daha ayrntl bir liste ekranda grntlenir. ls programn yine ls -l sample.c yazarak altrrsak, yalnzca sample.c dosyasna ilikin bilgiler grntlenecektir. te bir program altrrken program isminin yanna yazlan dier parametrelere komut satr argumanlar (command line arguments) denilmektedir. Komut satr argumanlar yalnzca iletim sistemi komutlar iin geerli deildir. Tm programlar iin komut satr argumanlar kullanlabilir. Komut satr argumanlarna C dili standartlarnda program parametreleri (program parameters) denmektedir. C programlarnn almaya balad main fonksiyonu da istee bal olarak iki parametre alabilir. Bu parametreler geleneksel olarak argc ve argv olarak isimlendirilirler. int main(int argc, char *argv[]) { ... } argc (argument count) komut satr argumanlarnn saysn gsterir. Bu sayya programn ismi de dahildir. argv ise , stringler eklinde saklanan komut satr argumanlarn gsteren, char trden bir gsterici dizisidir. Bu durumda arg[0] gstericisini programn ismini tutan stringi, argv[1] den argv[argc - 1] e kadar olan gstericiler ise program ismini izleyen dier argumanlarn tutulduklar stringleri gsterirler. argv[argc] ise her zaman bir NULL gstericiyi gstermektedir. Yukardaki rnekte kullanc ls programn ls -l sample.c eklinde altrdnda argc 3 deerini alr. argv[0] program ismini gsterir. argv[1] = "ls"; argv[1] program ismini izleyen 1. arguman gsterir. argv[1] = "-1"; argv[2] program ismini izleyen 2. arguman gsterir. argv[2] = "sample.c"; argv[argc] yani argv[3] ise NULL adresini gsterecektir.

334

KOMUT SATIRI ARGUMANLARI

komut satr argumanlar boluklarla birbirinden ayrlm olmaldr. Yukardaki rnekte program ls -lsample.c eklinde altrlrsa argc = 2 olurdu. Komut satr argumanlarnn ikincisi karakter trnden bir gstericiyi gsteren gstericidir. Yani argv[0], argv[1], argv[2]... herbiri char trden bir gstericidir. Komut satr argmalar NULL ile sonlandrlm bir biimde bu adreslerde bulunurlar. argv[0] src ve dizin dahil olmak zere (full path name) programn ismini vermektedir. Dier argmanlar srasyla argv[1], argv[2], argv[3], ... adreslerindedir. Komut satr argumanlar iletim sistemi tarafndan komut satrndan alnr ve derleyicinin rettii giri kodu yardmyla main fonksiyonuna parametre olarak kopyalanr. Aada komut satr argmanlarn basan rnek bir program gryorsunuz: #include <stdio.h> int main(int argc, char *argv[]) { int i; for (i = 0; i < argc; ++i) printf("argv[%d] : %s\n", i, argv[i]); return 0; } phesiz bu kodu aadaki ekilde de yazabilirdik . int main(int argc, char *argv[]) { int i; for (i = 0; argv[i] != NULL ; ++i) printf("argv[%d] : %s\n", i, argv[i]); return 0; } Komut satr argumanlarnn isimleri argc ve argv olmak zorunda deildir. Bunlar yerine herhangi iki isim de kullanlabilir. Ancak argc ve argv isimleri "argument counter" ve "argument vector" szcklerinden ksaltlmtr. Bu isimler programclar tarafndan geleneksel olarak kullanlmaktadr. Komut satr argmanlarn alan programlar genellikle, nce girilen argumanlar yorumlar ve test ederler. rnein : int main(int argc, char *argv[]) { if (argc == 1) { printf("ltfen bir argumanla altrnz!..\n"); exit(1);
335

31 } if (argc == 2) { printf("ltfen bir argumanla altrnz!..\n"); exit(1); } return 0; } Yukardaki rnekte program bir komut satr arguman verilerek altrlmadysa (yani eksik argumanla altrldysa) bir hata mesajyla sonlandrlyor. Benzer biimde fazla sayda argman iin de byle bir kontrol yaplmtr. DOS'ta olduu gibi baz sistemlerde main fonksiyonu nc bir parametre alabilir. nc parametre sistemin evre deikenlerine ilikin bir karakter trnden gstericiyi gsteren gstericidir. int main(int argc, char *argv[], char *env[]) { ... return 0; } main fonksiyonuna tm parametreler geilmek zorunda deildir. rnein : int main(int argc) { ... return 0; } gibi yalnzca birinci parametrenin kullanld bir tanmlama geerlidir. Ancak yalnzca ikinci parametre kullanlamaz. rnein: int main(char *argv[]) { ... return 0; } Komut Satr Argumanlarnn Kullanlmasna Bir rnek Komut satrndan alan basit bir hesap makinas #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <math.h>

int main(int argc, char *argv[]) { char ch; int op1, op2;

336

KOMUT SATIRI ARGUMANLARI 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 '+' : printf("%d\n", op1 + op2); return 0; case '-' : printf("%d\n", op1 - op2); return 0; case '/' : printf("%lf\n", (double)op1 / op2); return 0; case '*' : printf("%d\n", op1 * op2); return 0; case '%' : printf("%lf\n", (double)op1 * op2 / 100); return 0; case 'k' : printf("%lf\n", pow(op1, op2)); return 0; default : printf("hatal operatr\n"); } return 0;

337

32

32 . BLM :

DOSYALAR

kincil bellekte tanmlanm blgelere dosya denir. Her dosyann bir ismi vardr. Ancak dosyalarn isimlendirme kurallar sistemden sisteme gre deiebilmektedir. Dosya ilemleri tamamen iletim sisteminin kontrol altndadr. letim sistemi de ayr ayr yazlm fonksiyonlarn birbirlerini armas biiminde alr. rnein komut satrnda ismi yazlm olan bir programn altrlmas birka sistem fonksiyonunun arlmas ile yaplmaktadr. Komut satrndan yazy alan, diskte bir dosyay arayan, bir dosyay RAMe ykleyen, RAMdeki program altran fonksiyonlar dzenli olarak arlmaktadr. letim sisteminin almas srasnda kendisinin de ard, sistem programcsnn da dardan arabildii iletim sistemine ait fonksiyonlara sistem fonksiyonlar denir. Bu tr fonksiyonlara Windows sisteminde API (Application Proggramming Interface) fonksiyonlar, UNIX iletim sisteminde ise sistem armalar (system calls) denir. Aslnda btn dosya ilemleri, hangi programlama dili ile allrsa allsn, iletim sisteminin sistem fonksiyonlar tarafndan yaplr. Sistem fonksiyonlarnn isimleri ve parametrik yaplar sistemden sisteme deiebilmektedir. Dosyalara likin Temel Kavramlar Dosyann Almas Bir dosya zerinde ilem yapmadan nce dosya almaldr. Dosya aabilmek iin iletim sisteminin dosya a isimli bir sistem fonksiyonu 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 ierisinde bulunan bir tabloya yazlr. Dosya tablosunun biimi sistemden siteme deiebilir. rnein tipik bir dosya tablosu adaki gibi olabilir : Dosya tablosu (File table) Sra No 0 1 ... 12 ... Dosya ismi Dosyann Diskteki Yeri Dosyann zellikleri Dierleri

AUTOEXEC.BAT

...

...

...

Sistem fonksiyonlarnn da parametreleri ve geri dn deerleri vardr. "Dosya a" sistem fonksiyonunun parametresi alacak dosyann ismidir.Fonksiyon, dosya bilgilerinin yazld sra numaras ile geri dner ki bu deere "file handle" denir. Bu handle deeri dier dosya fonksiyonlarna parametre olarak geirilir. Dosyann almas srasnda buna ek olarak baka nemli ilemler de yaplmaktadr. 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. Kapatlmamas eitli problemlere yol aabilir.

338

DOSYALAR Dosyaya Bilgi Yazlmas ve Okunmas letim sistemlerinin dosyaya n byte veri yazan ve dosyadan n byte veri okuyan sistem fonksiyonlar vardr. Yazma ve okuma ilemleri bu fonksiyonlar kullanlarak yaplr. Dosya pozisyon gstericisi (file pointer) Bir dosya bytelardan oluur. Dosyadaki her bir bytea 0dan balayarak artan srada bir say karlk getirilmitir. Bu sayya ilgili byten ofset numaras denir. Dosya pozisyon gstericisi long trden bir saydr ve bir ofset deeri belirtir. Dosyaya yazan ve dosyadan okuma yapan fonksiyonlar bu yazma ve okuma ilemlerini her zaman dosya pozisyon gstericisinin gsterdii yerden yaparlar. rnein dosya gstericisinin gsterdii yer 100 olsun. Dosyadan 10 byte bilgi okumak iin sistem fonksiyonu arldnda, 100. ofsetden itibaren 10 byte bilgi okunur. letim sisteminin dosya gstericisini konumlandran bir sistem fonksiyonu vardr. Dosya ilk aldnda dosya gstericisi 0. ofsetdedir. rnein bir dosyann 100. Ofsetinden itibaren 10 byte okunmak istenirse sras ile u ilemlerin yaplmas gerekir: lgili dosya alr. Dosya pozisyon gstericisi 100. Offsete konumlandrlr. Dosyadan 10 byte okunur. Dosya kapatlr. C dilinde dosya ilemleri 2 biimde yaplabilir : 1. letim sisteminin sistem fonksiyonlar dorudan arlarak. 2. Standart C fonksiyonlar kullanlarak. Prototipleri stdio.h ierisinde olan standart dosya fonksiyonlarnn hepsinin ismi f ile balar.Tabi standart C fonksiyonlar da ilemlerini yapabilmek iin aslnda iletim sisteminin sistem fonksiyonlarn armaktadr. letim sisteminin sistem fonksiyonlar tanabilir deildir. simleri ve parametrik yaplar sistemden sisteme deiebilir. Bu yzden standart C fonksiyonlarnn kullanlmas tavsiye edilir. fopen fonksiyonu FILE *fopen (const char *fname, const char *mode) Fonksiyonun 1. Parametresi alacak dosyann ismidir. 2. Parametre a modudur. Dosya ismi path ierebilir. Dizin geileri iin / de konulabilir. A modu unlar olabilir : Mode "w" "w+" "r" "r+" "a" "a+" Anlam Dosyay yazmak iin aar. Dosyadan okuma yaplamaz. Dosyann mevcut olamas zorunlu deildir. Dosya mevcut deilse yaratlr. Dosya mevcut ise sfrlanr. Dosyay yazma ve okuma iin aar. Dosyann mevcut olamas zorunlu deildir. Dosya mevcut deilse yaratlr. Dosya mevcut ise sfrlanr. Dosyay okuma iin aar. Dosyaya yazma yaplamaz. Dosya mevcut deilse alamaz. Dosyay okuma ve yazma iin aar. (Dosyann pozisyon gstericisi dosyann bandadr). Dosya mevcut deilse alamaz. Dosyay sonuna ekleme iin aar. Dosyadan okuma yaplamaz. Dosyann mevcut olamas zorunlu deildir. Dosya mevcut deilse yaratlr. Dosyay sonuna ekleme ve dosyadan okuma iin aar. Dosyann mevcut olamas zorunlu deildir. Dosya mevcut deilse yaratlr.

Fonksiyonun geri dn deerine ilikin FILE yaps stdio.h ierisinde bildirilmitir. Fonskiyonun geri dn deeri FILE yaps trnden bir adrestir. Bu yapnn elemanlar standart deildir. Sistemden

339

32 sisteme deiiklik gsterebilir. Zaten programc bu yapnn elemanlarna gereksinim duymaz. fopen fonksiyonu iletim sisteminin dosya a sistem fonksiyonunu ararak dosyay aar ve dosyaya ilikin baz bilgileri bu FILE yaps ierisine yazarak bu yapnn balang adresini geri dndrr. rnein "file handle" deeri de bu yapnn ierisindedir. Tabi fopen fonksiyonunun geri verdii FILE trnden adres gvenli bir adrestir. Dosya eitli sebeplerden dolay alamayabilir. Bu durumda fopen NULL gsterici dndrr. Fonksiyonun geri dn deeri kesinlikle kontrol edilmelidir. Tipik bir kontrol ilemi aadaki gibi yaplabilir: if ((f = fopen(data, r)) == NULL) { printf(cannot open file...\n); } Dosya ismi dosyann yeri hakknda bilgi (src, path gibi) ierebilir. Dosya ismi string ile veriliyorsa path bilgisi verirken dikkatli olmak gerekecektir. path bilgisi \ karakteri ierebilir. string iinde \ karakterinin kullanlmas, \ karakterininin onu izleyen karakterle birlikte nceden belirlenmi ters bl karakter sabiti olarak yorunlanmasna yol aabilecektir. rnein : fopen("C:\source\new.dat", "r"); fonksiyon armnda derleyici \n karakterini "newline" karakteri olarak yorumlayacak \s karakterini ise "undefined" kabul edecektir. Bu problemden saknmak \ yerine \\ kullanlmasyla mmkn olur: fopen("C:\\source\\new.dat", "r"); Append modlar ("a", "a+") ok kullanlan modlar deildir. Dosyaya yazma durumunda "w" modu ile "a" modu arasnda farkllk vardr. "w" modunda dosyada olan ofsetin 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 stringde '+' karakterinin kullanlmas durumunda dikkatli olmak gerekir. Okuma ve yazma ilemleri arasnda mutlaka ya dosya pozisyon gstericisinin konumlandrlmas (mesela fseek fonksiyonu ile) ya da dosyaya iliken tampon bellek alannn (buffer) tazelenmesi gerekecektir. Bir dosyann alp alamayacan aadaki ekilde test edebiliri: Program komut satrndan canopen dosya.dat eklinde altrldnda ekrana "xxxxx dosyas alabilir" ya da "xxxxx dosyas alamaz" yazacaktr. #include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; if (argc != 2) { printf("usage: canopen filename\n"); return 2; } if ((fp = fopen(argv[1], "r")) == NULL) {
340

exit(1);

DOSYALAR printf("%s dosyas alamaz\n", argv[1]); return 1;

} printf("%s dosyas alabilir\n", argv[1]); fclose(fp); return 0;

fclose fonksiyonu int fclose(FILE *stream); Bu fonksiyon alm olan bir dosyay kapatr. Fonksiyon fopen ya da fropen fonksiyonundan elde edilen FILE yaps trnden adresi parametre olarak alr ve ak olan dosyay kapatr. Fonksiyonun geri dn deeri 0 ise dosya baarl olarak kapatlmtr. Fonksiyonun geri dn deeri EOF ise dosya kapatlamamtr. EOF stdio.h iinde tanmlanan bir sembolik sabittir ve derleyicilerin ounda (-1) olarak tanmlanmtr. Fonksiyonun baars ancak phe altnda test edilmelidir. Normal artlar altnda dosyann kapatlmamas iin bir neden yoktur. int main() { FILE *f; if ((f = fopen(data, w)) ==NULL) { printf(cannot open file...\n); exit(1); } fclose(f); return 0; }

fgetc fonksiyonu int fgetc(FILE *f); letim sisteminin dolaysyla Cnin yazma ve okuma yapan fonksiyonlar yazlan ve okunan miktar kadar dosya gstericisini ilerletirler. fgetc fonksiyonu dosya gstericisinin gsterdii yerdeki byte okur ve geri dn deeri olarak verir. Fonksiyon baarsz olursa , stdio.h dosyas ierisinde sembolik sabit olarak tanmlanm EOF deerine geri dner. Pek ok derleyici de EOF sembolik sabiti aadaki gibi tanmlanmtr. #define EOF (-1) fgetc fonksiyonunun geri dn deerini char trden bir deikene atamak yanl sonu verebilir, bu konuda dikkatli olunmal ve fonksiyonun 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' kaybedilecek ve ch deikenine 0xFF

341

32 deeri atanacaktr. Bu durumda ch deikeni iaretli char trden olduundan ch deikeni iinde negatif bir saynn tutulduu anlam kar. if (ch == EOF) gibi bir karlatrma deyiminde, if parantezi ierisindeki karlatrma ileminin yaplabilmesi iin otomatik tr dnm yaplr. Bu otomatik tr dnmnde iaretli int trne evrilecek ch deikeni FF byte' ile beslenecektir. (negatif olduu iin). Bu durumda eitlik karlatrmas doru netice verecek, yani dosyann sonuna gelindii (ya da baka nedenden dolay okumann yaplamad) yorumu yaplacaktr. Oysa ch deikeni int trden olsayd, ch deikenine atanan deer 0x00FF olacak ve bu durumda karlatrma yapldnda ch deikeni ile EOF deerinin (0xFFFF) eit olmad sonucuna varlacaktr. Bir dosyann ieriini ekrana yazdran rnek program:

#include <stdio.h> int main() { FILE *f; char fname[MAX_PATH]; int ch; printf(Dosya ismi : ); gets(fname); if (f = fopen(fname, r)) == NULL) { printf(cannot open file...\n); exit(1); } while (ch = fgetc(f)) != EOF) putchar(ch); fclose(f); return 0; } not : maximum path uzunluu DOSda 80 UNIX ve WINDOWSda 256 karakteri geemez. fputc Fonksiyonu int fputc(int ch, FILE *p); Bu fonksiyon dosya gstericisinin bulunduu yere 1 byte bilgiyi yazar. Fonksiyonun 1. parametresi yazlacak karakter, 2. parametresi ise yazlacak dosyaya ilikin FILE yaps adresidir. Fonksiyonun geri dn deeri EOF ise ilem baarszdr. Deilse fonksiyon yazlan karakter ile geri dner. fgetc ve fputc fonksiyonlar kullanlarak bir dosyay kopyalayan rnek program: #include <stdio.h> #include <stdlib.h>

342

DOSYALAR #define MAX_PATH 80

int main() { FILE *fsource, *fdest; char source[MAX_PATH], dest[MAX_PATH]; int ch; printf(kopyalanacak dosya : ); gets(source); printf(kopya dosya : ); gets (dest); if ((fsource = fopen(source, r)) == NULL) { printf(cannot open file...\n); exit(EXIT_FAILURE); } if ((fdest = fopen(dest, w)) == NULL) { printf(cannot create file...\n); exit(EXIT_FAILURE); } while ((ch = fgetc(fsource)) != EOF) fputc(ch, fdest); fclose(fsource); fclose(fdest); printf(1 file copied...\n); return 0;

fprintf Fonksiyonu Bu fonksiyon tpk printf fonksiyonu gibidir. Ancak ilk parametresi yazma ileminin hangi dosyaya yaplacan belirtir. Dier parametreleri printf fonksiyonun da olduu gibidir. zetle printf fonksiyonu ekrana yazar ancak fprint fonksiyonu 1. parametre deikeninde belirtilen dosyaya yazar. #include <stdio.h> #include <stdlib.h> #define MAX_PATH int main() { FILE *f; int i; if ((f = fopen(data, w)) ==NULL) { printf(cannot open file...\n"); exit(1); } for (i = 0; i < 10; ++i) fprintf(f, sayi = %d\n, i); fclose(f); 80

343

32 return 0; }

fgets fonksiyonu char *fgets(char *buf, int n, FILE *f); Bu fonksiyon dosya gstericisinin gsterdii yerden 1 satrlk bilgiyi okur. Fonksiyon dosyadan '\n' karakterini okuyunca onu da birinci parametresinde verilen adrese yazarak ilemini sonlandrr. Fonksiyonun 1. parametresi okunacak bilginin RAM'de yerletirilecei yerin adresidir. 2. parametresi ise okunacak maksimum karakter saysdr. fgets en fazla n 1 karakteri okur. Okuduu karakterlerin soonuna null karakteri ekler ve ilemini sonlandrr. Eer satr zerindeki karakter says n- 1'den az ise tm satr okur ve ilemini sonlandrr. rnein bu parametreyi 10 olarak girdiimizi dnelim. Satr zerinde 20 karakter olsun. Fonksiyon 9 karakteri okur, sonuna NULL karakteri ekler. Ancak satr zerinde \n dahil olmak zere 5 karakter olsayd fonksiyon bu 5 karakteri de okuyarak sonuna da NULL karakter ekleyerek ilemini sonlandracakt. Bir dng ierisinde fgets fonksiyonu srekli olarak arlarak btn dosya okunabilir. Fonksiyonun geri dn deeri, en az 1 karakter okunmu ise 1. parametresi ile belirtilen adresin ayns, hi okunmamsa NULL adresidir. rnek : Bir dosyay fgets fonksiyonu ile satr satr olarak ekrana yazan program: #define MAX_PATH #define MBUFSIZE 80 100

int main() { FILE *f; char s[MAX_PATH]; char buf[BUFSIZE]; printf(Dosya : ); gets(s); if ((f = fopen(s, r)) == NULL) { printf(cannot open file...\n); exit(1); } while (fgets(buf, BUFSIZE, f) != NULL) printf(%s, buf); fclose(f); return 0;

Text ve Binary Dosya Kavramlar DOS VE WINDOWS iletim sistemlerinde bir dosya text ve binary modda alabilir. Varsaylan a modu textdir. Yani dosyann hangi modda ald ak bir ekilde belirtilmezse dosyann text modda ald varsaylacaktr. Dosyay binary modda aabilmek iin a modunun sonuna b eklemek gerekir. rnein : f = fopen(data, r) text modda
344

DOSYALAR f = fopen(data, rb) binary modda

DOSda bir dosya type edildiinde bir karakter aa satrn banda grnyorsa bunu salamak iin o karakterden nce CR (carriage return) ve LF (line feed) karakterlerinin bulunmas gerekir. CR karakteri Cde \r ile belirtilir. 13 numaral ASCII karakteridir. LF karakteri Cde \n ile belirtilir. 10 numaral ASCII karakteridir. rnein bir dosya type edildiinde grnt a b eklinde olsun. Dosyadaki durum a\r\nb eklindedir. Oysa UNIX sistemlerinde aa satrn bana geebilmek iin sadece LF karakteri kullanlmaktadr. UNIX de a b grntsnn dosya karl a\nb biimindedir. DOSda LF karakteri bulunulan satrn aasna ge CR karakteri ise bulunulan satrn bana ge anlamndadr. rnein DOS da bir bir dosyann ierii a\nb biiminde ise dosya type edildiinde a b grnts elde edilir. Eer dosyann ierii a\rb biiminde ise dosya type edildiinde b grnts elde edilir. printf fonksiyonunda \n ekranda aa satrn bana geme amacyla kullanlr. Aslnda biz printf fonksiyonunun 1. parametresi olan stringin iine \n yerletirdiimizde UNIX'de yalnzca \ni DOS'da 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 fonksiyonlar otomatik olarak \r ve \n karakterlerini dosyaya yazarlar. Benze r 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 olmayacaktr. 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 modda aldn dnelim. Bu dosyada 10 numaral ve 13 numaral ASCII karakterleri yanyana bulunduunda dosyadan yalnzca 1 byte okunacaktr. Ayn ekilde dosyadan 26 numaral ASCII karakteri okunduunda dosyadan artk baka bir okuma yaplamayacaktr. (Dosyann sonuna gelindii varsaylacaktr.) /* textmode.c program DOS altnda text modu ile binary mod arasndaki fark gsteren bir program
345

32 */ #include <stdio.h> #include <conio.h> #include <stdlib.h> main() { FILE *fp; int k, ch; clrscr(); 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 */

346

DOSYALAR fclose(fp);

/* simdi '\x1A' karakterinin text modunda dosyay sonlandrmas zellii test ediliyor */ 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"); 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
347

32 65 65 65 65 65 26 65 65 65 65 65 */ fclose(fp); return 0;

EOF (END OF FILE) DURUMU Dosyann sonunda hibir zel karakter yoktur. letim sistemi dosyann sonuna gelinip gelinmediini dosyann uzunluuna bakarak anlayabilir. EOF durumu dosya pozisyon gstericisinin dosyada olmayan son karakteri gstermesi durumudur. EOF durumunda dosya pozisyon gstericisinin offset deeri dosya uzunluu ile ayn deerdedir. EOF durumunda dosyadan okuma yaplmak istenirse dosya fonksiyonlar baarsz olur. Ancak a modu uygunsa dosyaya yazma yaplabilir ve bu durumda dosyaya ekleme yaplr. Daha nce sylendii gibi C dilinde alan bir dosya ile ilgili bilgiler FILE trnden bir yap nesnesi iinde tutulur. Bu yapnn her bir eleman dosyann bir zellii hakknda bilgi vermektedir. C programcs bu yapnn elemanlarnn deerleri ile dorudan ilgilenmez, zira fopen fonksiyonunun geri dn deeri bu yap nesnesini gsteren FILE yaps trnden bir gstericidir ve C dilinin dosyalarla ilgili ilem yapan fonksiyonlar ounlukla bu adresi parametre olarak alarak, istenilen dosyaya ularlar. Sz konusu FILE yapsnn elemanlarndan biri de flag olarak kullanlan EOF indikatrdr. (aslnda int trden bir flag'in yalnzca belirli bir bitidir.) C dilinin dosyalarla ilgili ilem yapan baz fonksiyonlar EOF indikatrnn deerini deitirirler. (set ya da clear ederler. Set edilmesi bu indikatre 1 deerinin atanmas clear edilmesi ise 0 deerinin atanmasdr. Bu indikatrn set edilmesi demek dosya pozisyon gstericisinin dosyann sonunu gstermesi demektir. Dosya aan fonksiyonlar FILE yapsndaki EOF indikatrn sfrlarlar. Bu fonksiyonlar dnda fseek fonksiyonu ve clearerr fonksiyonlar da EOF indikatrn sfrlarlar. (clear ederler). fseek fonksiyonu : Bu fonksiyon dosya pozisyon gstericisini istenilen bir offsete konumlandrmak amacyla kullanlr. Bu fonksiyonun kullanlmasyla, alm bir dosyann istenilen bir yerinden okuma yapmak ya da istenilen bir yerine yazmak mmkn hale gelir. Prototipi : int fseek(FILE *f, long offset, int origin); Fonksiyonun ikinci parametresi konumlandrma ileminin yaplaca offset deeridir. Fonksiyonun 3. parametresi 0, 1, veya 2 olabilir. Bu deerler stdio.h dosyasnda #define SEEK_SET #define SEEK_CUR #define SEEK_END 0 1 2

biiminde sembolik sabitlerle tanmlanmtr ve fseek fonksiyonun arlmasnda daha ok bu sembolik sabitler kullanlmaktadr. son parametre 0 ise, konumlandrma dosya bandan itibaren yaplr. Bu durumda 2. parametre >= 0 olmaldr. rnein: fseek(f, 10L, 0);
348

DOSYALAR

ile dosya gstericisi 10. offsete konumlandrlr. Ya da fseek(f, 0, 0); ile dosya gstericisi dosyann bana konumlandrlr. Dosya pozisyon gstericisinin dosyann bana konumlandrlmas iin rewind fonksiyonu da kullanlabilir: void rewind(FILE *fp); rewind(f); fonksiyonun 3. parametre deikenine geilen deer 1 ise (SEEK_CUR) , konumlandrma dosya gstericisinin en son bulunduu yere gre yaplr. Bu durumda ikinci parametre pozitif ya da negatif deere sahip olabilir. Pozitif bir deer ileri, negatif bir deer geri anlamna gelecektir. rnein dosya gstericisi 10. offsette olsun. fseek(f, -1, SEEK_CUR); armas ile dosya gstericisi 9. offset'e konumlandrlr. fonksiyonun 3. parametre deikenine geilen deer 2 ise (SEEK_END), konumlandrma EOF durumundan itibaren yani dosya sonunu referans alnarak yaplr. Bu durumda ikinci parametre <= 0 olmaldr. rnein dosya gstericisini EOF durumuna ekmek iin : fseek(f, 0, SEEK_END); armasn yapmak gerekir. Ya da baka bir rnek: fseek(f, -1, SEEK_END); armas ile dosya gstericisi son karakterin offsetine ekilir. Fonksiyonun geri dn deeri ilemin baars hakknda bilgi verir. Geri dn deeri 0 ise ilem baarldr. Geri dn deeri 0 d bir deer ise ilem baarszdr. Ancak problemli durumlarda geri dn derinin test edilmesi tavsiye edilir. Yazma ve okuma ilemleri arasnda dosya gstericisinin fseek fonksiyonu ile konumlandrlmas gerekir.Konumlandrma gerekirse bo bir fseek armas ile yaplabilir. rnein dosyadan bir karakter okunup , bir sonraki karaktere bu karakterin 1 fazlasn yazacak olalm. ch = fgetc(f); fputc(ch + 1, f); ilemi hataldr. Yazmadan okumaya, okumadan yazmaya geite dosya gstericisi konumlandrlmaldr. feof fonskiyonu : bu fonksiyon dosya gstericisinin EOF durumunda olup olmadn (EOF indikatrnn set edilip edilmediini) test etmek amacyla kullanlr. Prototipi: int feof(FILE *f);

349

32 Eer dosya gstericisi dosya sonunda ise (EOF indikatr set edilmise) fonksiyon 0 d bir deere , deilse (EOF indikatr set edilmemise) 0 deeri ile geri dner. Ancak feof fonksiyonunun 0 d bir deere geri dnebilmesi iin dosya gstericisinin dosyann sonunda olmasnn yan sra en son yaplan okuma ileminin de baarsz olmas gerekir. daha nce sylendii gibi baz fonksiyonlar (fopen, fseek, rewind, clearerr) EOF indikatorn clear ederler, yani EOF indikatrnn 0 deerinde olmas (clear edilmi olmas) dosyann sonunda olunmadnn bir garantisi deildir.

fread ve fwrite fonksiyonlar Bu iki fonksiyon C dilinde en ok kullanlan dosya fonksiyonlardr. Genel olarak dosya ile RAM arasnda transfer yaparlar. Her iki fonskiyonun da prototipleri ayndr. size_t fread(void *adr, size_t size, size_t n, FILE *); size_t fwrite (const void *adr, size_t size, size_t n, FILE *); size_t trnn derleyiciyi yazanlar tarafndan unsigned int ya da unsigned long trnn typedef edilmi yeni ismi olduunu hatrlayalm. fread fonskiyonu dosya pozisyon gstericisinin gsterdii yerden, 2. ve 3. parametresine kopyalanan deerlerin arpm kadar byte' , RAM'de 1. parametresinin gsterdii adresten balayarak kopyalar. Geleneksel olarak fonksiyonun 2. parametresi veri yapsnn bir elemannn uzunluunu, 3. parametresi ile para says biiminde girilir. Bu fonksiyonlar sayesinde diziler ve yaplar tek hamlede dosyaya transfer edilebilirler. rnein 10 elemanl bir dizi aadaki gibi tek hamlede 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 fonksiyonuna 1. arguman olarak gnderilebilir. FILE trnden f gstericisi ile ilikilendirilen dosyaya RAM'deki a adresinden toplam sizeof(int) * 5 byte yazlmaktadr. Ancak tabi fwrite fonskiyonu saylar bellekteki grnts ile dosyaya yazar. (yani fprintf fonksiyonu gibi formatl yazmaz.) rnein: int i = 1535; fwrite(&i, sizeof(int), 1, f); Burada dosya type edilirse 2 byte uzunluunda rasgele karakterler grnr. nk DOS'da int tr 2 byte uzunluundadr. Bizim grdmz ise 1525'in rasgele olan bytelardr. Bilgileri ASCII karlklar ile dosyaya yazmak iin fprintf fonksiyonu kullanlabilir.. fread ve fwrite fonksiyonlar bellekteki bilgileri transfer ettiine gre dosyalarn da binary modda alm olmas uygun olacaktr. #include <stdio.h> #include <stdlib.h>
350

DOSYALAR

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]);

fread ve fwrite fonksiyonlarnn geri dn deerleri 3. parametresi ile belirtilen okunan ya da yazlan para saysdr. rnein n = fread(a, sizeof(int), 10, f); ifadesinde fonksiyon btn saylar okuyabildiyse 10 saysna geri dner. Eer dosyadaki kalan byte says okunmak istenen saydan az ise fonksiyon btn byte'lar okur ve geri dn deeri okunan byte says 2. parametresi ile belirtilen say olur. rnein DOS altnda alyor olalm. Dosyada 10 byte bilgi kalm olsun. n = fread(a, sizeof(int), 10, f); ile fonksiyon 5 saysna geri dnecektir. Aadaki iki ifadeyi inceleyelim: fread(str, 100, 1, f); fread(str, 1, 100, f); her iki fonksiyon arma ifadesi de RAM'deki str adresine FILE trnden f gstericisi ile ilikilendirilen dosyadan 100 byte okumak amacyla kullanlabilir. Ancak birinci armada geri dn deeri ya 0 ya 1 olabilecekken, ikinci fonksiyon armasnda geri dn deeri 0 100(dahil) herhangi bir deer olabilecektir.

Blok blok kopyalama ilemi Aadaki rnekte bir grup byte fread fonskiyonu ile bir dosyadan okunmu ve fwrite fonksiyonu ile dier bir dosyaya yazlmtr. #define BLOCK_SIZE 1024 #define MAX_PATH 80
351

32

int main() { FILE *fs, *fd; char s[MAX_PATH], d[MAX_PATH]; unsigned n; printf("kaynak dosya : "); gets(s); printf("ama dosya : "); gets(d); if ((fs = fopen(s, "rb")) == NULL) { printf("cannot open file ...\n"); exit(EXIT_FAILURE); } if ((fd = fopen(d, "wb")) == NULL) { printf("cannot open file ...\n"); fclose(fs); exit(EXIT_FAILURE); } while ((n = fread(buf, 1, BLOCK_SIZE, fs)) > 0) fwrite(buf, 1, n, fd); fclose(fs); fclose(fd); printf("1 file copied...\n"); return 0;

remove fonksiyonu Bu fonksiyon bir dosyay silmek iin kullanlr. Fonksiyonun prototipi : int remove (const char *filename); eklindedir. Fonksiyona arguman olarak silinecek dosyann ismi gnderilir. Fonksiyonun geri dn deeri, dosyann baarl bir ekilde silinebilmesi durumunda 0 aksi halde (yani dosya silinememise) 0 d bir deerdir. Ak olan bir dosyann silinmesi "implementation depended" (derleyiciye bal) olduundan, yazlan kodun tanabilirlii asndan, silinecek bir dosya ak ise nce kapatlmaldr. rename fonksiyonu Bu fonksiyon bir dosyann ismini deitirmek iin kullanlr. Fonksiyonun prototipi : int rename (const char *old, const char *new); eklindedir. Fonksiyona 1. arguman olarak dosyann eski ismi ikinci arguman olarak ise dosyann yeni ismi gnderilmelidir. Fonksiyonun geri dn deeri, isim deitirmen ileminin baarl olmas durumunda 0, aksi halde (dosyann ismi deitirilemiyorsa 0 d bir deerdir. (rnein ak olan bir dosyann isminin deitirilmeye allmas baarl olamayacandan fonksiyon bu durumda 0 d bir deere geri dnecektir.)
352

DOSYALAR

tmpfile fonksiyonu Fonksiyon geici bir dosya amak amacyla kullanlr. Fonksiyonun prototipi : FILE * tmpfile(void); tmpfile fonksiyonu at geici dosyay "wb" modunda aar. Alan dosya fclose fonksiyonu ile kapatldnda ya da (kapatlmazsa), program sona erdiinde otomatik olarak silinecektir. Fonksiyonu geri dn deeri, alan geici dosya ile iliki kurulmasna yarayacak, FILE yaps trnden bir adrestir. Herhangi bir nedenle dosya geici dosya alamyorsa fonksiyon NULL adresine geri dnecektir. tmpnam fonksiyonu Geici olarak kullanlacak bir dosya iin bir isim retilmesi amacyla kullanlr. Fonksiyonun prototipi : char *tmpnam(char *s); eklindedir. Fonksiyon rettii dosya ismini kendisine gnderilen char trden adrese yerletirir. Eer fonksiyona arguman olarak NULL adresi gnderilirse, fonksiyon retilen dosya ismini statik bir dizi iinde tutarak bu dizinin adresiyle geri dnecektir. Fonksiyona char trden bir dizinin adresi gnderildiinde bu dizinin boyutu ne kadar olmaldr. Baka bir deyile tmpnam fonksiyonu ka karakter uzunluunda bir dosya ismi retecektir. te bu deer stdio.h dosyas iinde tanmlanan L_tmpnam sembolik sabitiyle belirtilmektedir. tmpnam fonksiyonunun rettii dosya isminin daha nce kullanlmayan bir dosya ismi olmas garanti altna alnmtr. Yani retilen dosya ismi tektir. (unique file name) Bir programda daha sonra silmek zere bir dosya aacamz ve bu dosyaya birtakm bilgileri yazacamz dnelim. Bu durumda dosyay yazma modunda aacamza gre, aacamz dosyaya olan bir dosyann ismini veremeyiz. Eer verirsek , var olan dosya sfrlanaca iin bu dosyay kaybederiz. Bu riske girmemek iin, geici olarak kullanlacak dosya tmpfiel fonksiyonu kullanlarak almaldr. Ancak tmpfile fonksiyonunun kullanlmas durumunda, alan dosya kalc hale getirilemez. Yani herhangi bir nedenden dolay geici dosyann silinmemesini istersek, (dosyay kalc hale getirmek istersek) dosyay fopen fonksiyonuyla amalyz. te bu durumda geici dosyay baka bir dosyay riske etmemek iin tmpnam fonksiyonunun rettii isim ile amalyz. Peki tmpnam fonksiyonuyla en fazla ka tane "unique file name " retebiliriz. te bu say stdio.h iinde tanmlanan TMP_MAX sembolik sabiti ile belirlenmitir.

C dilinde baz giri ve k birimleri(klavye, ekran gibi) dorudan bir dosya gibi ele alnrlar. C standartlar herhangi bir giri k birimini "stream" olarak isimlendirmektedir. Bir stream bir dosya olabilecei gibi, dosya olarak ele alnan bir giri k birimi de olabilir. rnein kk programlar genellikle girdilerini genellikle tek bir stream,den alp (mesela klavye) ktlarn da tek bir streame (mesela ekran) iletirler. freopen fonksiyonu freopen fonksiyonu daha nce alm bir dosyay, fropen fonksiyonu ile alan dosyaya ynlendirir. Fonksiyonun prototipi : FILE *freopen(const char *filename, const char *mode, FILE *stream); eklindedir.

353

32 Uygulamalarda daha ok standart dosyalarn (stdin, stdout, stderr) baka dosyalara ynlendirilmesinde kullanlr. rnein bir programn ktlarnn data.dat isimli dosyaya yazlmasn istersek : if (freopen("data.dat", "w", stdout) == NULL) { printf("data.dat dosyas alamyor\n"); exit(EXIT_FAILURE); } yukardaki fonksiyon armyla stdout dosyasnn ynlendirildii dosya kapatlarak (bu ynlendirme ilemi komut satrndan yaplm olabilecei gibi, freopen fonksiyonunun daha nceki arm ile de yaplm olabilir.) stdout dosyasnn data.dat dosyasna ynlendirilmesi salanr. freopen fonksiyonunun geri dn deeri fonksiyona gnderilen nc arguman olan FILE yaps trnden gstericidir. freopen dosyas ynlendirmenin yaplaca dosyay aamazsa NULL adresine geri dner. Eer ynlendirmenin yapld eski dosya kapatlamyorsa, freopen fonksiyonu bu durumda bir iaret vermez. dosya buffer fonksiyonlar

kincil belleklerle (disket, hard disk vs.) yaplan ilemler RAM'de 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 performans bufferlama yoluyla artrlmaktadr. Bir dosyaya yazlacak data ilk nce bellekteki bir buffer alannda saklanr. Bu buffer alan dolduunda ya da yazlmann yaplaca dosya kapatldnda bufferdaki data alannda ne varsa dosyaya yazlr. Buna bufferin boaltlmas (to flush the buffer) denir. Giri dosyalar da benzer ekilde bufferlanabilir. Giri biriminden alnan data (rnein klavyeden) nce buffera yazlr. dosyalarn bufferlamas erimlilikte ok byk bir arta neden olur. nk bufferdan (RAM'den) bir karakter okunmas ya da buffera bir karakter yazlmas ihmal edilecek kadar kk bir zaman iinde yaplr. Buffer ile dosya arasndaki transfer phesiz yine vakit alacaktr ama bir defalk blok transferi, kk kk transferlerin toplamndan ok daha ksa zaman alacaktr. stdio.h balk dosyas iinde prototipi bildirimi yaplan ve dosyalarla ilgili ilem yapan fonksiyonlar tamponlamay otomatik olarak gerekletirirler. Yani dosyalarn tamponlanmas iin bizim birey yapmamza gerek kalmadan bu i geri planda bize sezdirilmeden yaplmaktadr. Ama baz durumlarda tamponlama konusunda programc belirleyici durumda olmak isteyebilir. te bu durumlarda programc dosya tamponlama fonksiyonlarn (fflush, setbuf, setvbuf) kullanacaktr: fflush fonksiyonu Bir program ktsn bir dosyaya yazarken (rnein stdout dosyasna) yazlan dosya ilk nce RAM'deki tamponlama alanna gider. Dosya kapatldnda ya da tamponlama alan dolduunda, tamponlama alan boaltlarak dosyaya yazlr. fflush fonksiyonunun kullanlmasyla, dosyann kapatlmas ya da tamponlama alannn dolmas beklenmeksizin, tamponlama alan boatlarak dosyaya yazlr. Bu ilem istenilen sklkta yaplabilir. Fonksiyonun prototipi : int fflush (FILE *stream); eklindedir.

354

DOSYALAR fflush(fp); arm ile FILE yaps trnden fp gstericisi ile ilikilendirilen dosyann tamponlama alan (buffer) boaltlr. Eer fflush fonksiyonuna NULL adresi gnderilirse, ak olan btn dosyalarn tamponlama alanlar boaltlacaktr. Tamponlama alannn boaltlmas ilemi baarl olursa fflush fonksiyonu 0 deerine geri dnecek aksi halde EOF deerine geri dnecektir. setvbuf fonksiyonu setvbuf fonksiyonu bir dosyann tamponlanma eklinin deitirilmesi ve tampon alannn yerinin ve boyutunun deitirilmesi amacyla kullanlr. Fonksiyonun prototipi aadaki ekildedir : int setvbuf(FILE *stream, char *buf, int mode, size_t size); Fonksiyona gnderilen nc arguman tamponlama eklini belirler. nc argumann deeri stdio.h balk dosyas iinde tanmlanan sembolik sabitlerle belirlenir. _IOFBF (full buffering - tam tamponlama) data dosyaya tamponlama alan dolduunda yazlr. Ya da giri tamponlamas sz konusu ise dosyadan okuma tamponlama alan bo olduu zaman yaplr. _IOLBF (line buffering - satr tamponlamas) Tamponlama alan ile dosya arasndaki okuma ya da yazma ilemi satr satr yaplr. _IONBF (no buffering - tamponlama yok) Dosyadan okuma ya da soyaya yazma tamponlama olmadan dorudan yaplr. setvbuf fonksiyonuna gnderilen ikinci arguman RAM'de tamponlamann yaplaca bloun balang adresidir. Tamponlamann yaplaca alan statik ya da dinamik mrl olabilecei gibi, dinamik bellek fonksiyonlaryla da tahsis edilebilir. Fonksiyona gnderilen son arguman tamponlama alannda tutulacak bytelarn saysdr. setvbuf fonksiyonu dosya aldktan sonra, fakat dosya zerinde herhangi biri ilem yaplmadan nce arlmaldr. Fonksiyonun baarl olmas durumunda fonksiyon 0 deerine geri dnecektir. Fonksiyona gnderilen nc argumann geersiz olmas durumunda ya da fonksiyonun ilgili tamponlamay yapamamas durumunda, geri dn deeri 0 d bir deer olacaktr. Fonksiyona gnderilen buffer alannn geerliliinin bitmesinden nce (mrnn tamamlanmasndan nce) dosya kapatlmamaldr. dosya ilemleri ile ilgili rnek uygulamalar: Uygulama 1 : Dosya zerinde sral veri taban tutulmas #include #include #include #include <stdio.h> <stdlib.h> <conio.h> <string.h>

/* symbolic constants */

355

32 #define #define #define #define #define #define #define #define #define #define #define #define MAX_PATH ADDREC LISTREC FINDREC DELREC EDITREC PACKRECS SORTREC EXITPROG DELETED NORMALREC INVALID 80 1 2 3 4 5 6 7 8 0 1 0

/* structure declarations */ typedef struct _PERSON { char name[30]; int no; int delflag; } PERSON;

/* function prototypes */ int GetOption(void); void AddRec(void); void ListRec(void); void FindRec(void); void DelRec(void); void EditRec(void); void SortRec(void); void Pack(void);

/* global variables */ FILE *f; char fname[MAX_PATH]; /* function definitions */ void AddRec(void) { PERSON per; printf("Ad soyad : "); fflush(stdin); gets(per.name); printf("No : "); scanf("%d", &per.no); per.delflag = NORMALREC;
356

DOSYALAR fseek(f, 0, SEEK_END); fwrite(&per, sizeof(PERSON), 1, f);

void FindRec(void) { PERSON per; char name[30]; printf("ltfen kayt ismini giriniz :"); fflush(stdin); gets(name); fseek(f, 0, SEEK_SET); while (fread(&per, sizeof(PERSON), 1, f) > 0) { if (per.delflag != NORMALREC) continue; if (!stricmp(per.name, name)) { printf("\n%s %d\n\n", per.name, per.no); return; } } printf("\nKayt bulunamad...\n\n");

void ListRec(void) { PERSON per; putchar('\n'); fseek(f, 0L, SEEK_SET); while (fread(&per, sizeof(PERSON), 1, f) > 0) { printf("\n%20s %5d", per.name, per.no); if (per.delflag == DELETED) printf("\tDELETED"); } putchar('\n\n');

void DelRec(void) { char name[30]; PERSON per; printf("Silinecek kaytn ad ve soyad : "); fflush(stdin); gets(name); fseek(f, 0, SEEK_SET); while (fread(&per, sizeof(PERSON), 1, f) > 0) { if (!stricmp(per.name, name)) { per.delflag = DELETED; fseek(f, -(long)sizeof(PERSON), 1); fwrite(&per, sizeof(PERSON), 1, f);
357

32 printf("Record deleted!..\n"); return;

} printf("silinecek kayt bulunamad"); } void EditRec(void) { char name[30]; PERSON per; printf("Deitirilecek kaytn ad ve soyad : "); fflush(stdin); gets(name); fseek(f, 0, SEEK_SET); while (fread(&per, sizeof(PERSON), 1, f) > 0) { if (per.delflag == NORMALREC && !stricmp(per.name, name)) { printf("Ad soyad : "); fflush(stdin); gets(per.name); printf("No : "); scanf("%d", &per.no); fseek(f, -(long)sizeof(PERSON), 1); fwrite(&per, sizeof(PERSON), 1, f); printf("Record updated!..\n"); return; } } printf("deitirilecek kayt bulunamad\n"); } int GetOption(void) { int option; printf("\n1) Kayt Ekle\n"); printf("2) Kayt Listele\n"); printf("3) Kayt Bul\n"); printf("4) Kayt Sil\n"); printf("5) Kayt deitir\n"); printf("6) Pack\n"); printf("7) Srala\n"); printf("8) k\n"); printf("\nSeiminiz :"); scanf("%d", &option); if (option < 0 || option > 8) return INVALID; return option;

358

DOSYALAR void SortRec(void) { PERSON per[2], tmp; int i, count, chgFlag; fseek(f, 0, SEEK_END); count = ftell(f) / sizeof(PERSON); do { chgFlag = 0; for (i = 0; i < count - 1; ++i) { fseek(f, (long)i * sizeof(PERSON), SEEK_SET); if (fread(per, sizeof(PERSON), 2, f) != 2) { printf("cannot read from the file!..\n"); exit(EXIT_FAILURE); } if (per[0].no > per[1].no) { chgFlag = 1; tmp = per[0]; per[0] = per[1]; per[1] = tmp; fseek(f, (long)i * sizeof(PERSON), SEEK_SET); if (fwrite(per, sizeof(PERSON), 2, f) != 2) { printf("cannot read from the file!..\n"); exit(EXIT_FAILURE); }

} } while (chgFlag); } void Pack(void) { FILE *fnew; PERSON per; if ((fnew = fopen("temp", "wb")) == NULL) { printf("cannot create temporary file!..\n"); exit(EXIT_FAILURE); } fseek(f, 0l, SEEK_SET); while (fread(&per, sizeof(PERSON), 1, f) > 0) { if (per.delflag == NORMALREC) fwrite(&per, sizeof(PERSON), 1, fnew); } fclose(fnew); fclose(f); if (unlink(fname)) { printf("Fatal Error : Cannot open database file!..\n"); exit(EXIT_FAILURE);
359

32 } if (rename("temp", fname)) { printf("fatal Error: cannot delete database file!..\n"); exit(EXIT_FAILURE); } if ((f = fopen(fname, "r+b")) == NULL) { printf("Fatal Error : Cannot open database file!..\n"); exit(EXIT_FAILURE); } printf("Pack operation succesfully completed!..\n");

void main() { char dfname[MAX_PATH]; int option; printf("Data File : "); gets(dfname); if ((f = fopen(dfname, "r+b")) == NULL) if ((f = fopen(dfname, "w+b")) == NULL) { printf("Cannot open database file!..\n"); exit(EXIT_FAILURE); } strcpy(fname, dfname); for (;;) { option = GetOption(); switch (option) { case ADDREC : AddRec(); break; case LISTREC : ListRec(); break; case FINDREC : FindRec(); break; case DELREC : DelRec(); break; case EDITREC : EditRec(); break; case PACKRECS : Pack(); break; case SORTREC : SortRec(); break; case EXITPROG : goto EXIT; case INVALID : printf("Geersiz Seenek!..\n"); } } EXIT: fclose(f); }

Uygulama 2: developer's back up program (G. Aslan) #include #include #include #include #include <stdio.h> <stdlib.h> <string.h> <io.h> <errno.h>

360

DOSYALAR #define GOOD #define FAIL #define FBUFSIZ 0 (-1) (63*512) /* Buffer size */

char *Buffer, *in_buf, *out_buf; char CmpFile[128], OutFile[128]; int filecmp(FILE *fi, FILE *fo) { int c1, c2; long l1, l2; l1 = filelength(fileno(fi)); l2 = filelength(fileno(fo)); if (l1 != l2) return FAIL; rewind(fi); rewind(fo); for (;;) { c1 = getc(fi); c2 = getc(fo); if (c1 != c2) { return FAIL; } if (c1 == EOF) break;

return GOOD; } int filecopy(FILE *fi, FILE *fo) { int c, nbytes; rewind(fi); rewind(fo); for (;;) { c = fread(Buffer, 1, FBUFSIZ, fi); if (c == 0) { break; } nbytes = fwrite(Buffer, 1, c, fo); if (nbytes != c) { return FAIL; } }
361

32 return GOOD; } int main(int argc, char *argv[]) { FILE *fi, *fo; char *ptr; int i; printf("*GA Developer's Backup Utility. Version 1.0\n" "(C) *GA, 1995\n\n"); if (argc != 2) { fprintf(stderr, "Usage: BK <filename>\n"); return 1; } if ((Buffer = malloc(FBUFSIZ)) == NULL || (in_buf = malloc(FBUFSIZ)) == NULL || (out_buf = malloc(FBUFSIZ)) == NULL) { fprintf(stderr, "Not enough memory\n"); return 2; } if ((fi = fopen(argv[1], "rb")) == NULL) { ptr = argv[1]; OPN_ERR: fprintf(stderr, "File could not be opened: '%s'\n", ptr); return 3; } setvbuf(fi, in_buf, _IOFBF, FBUFSIZ); strcpy(CmpFile, argv[1]); ptr = strchr(CmpFile, '.'); if (ptr == NULL) ptr = strchr(CmpFile, '\0'); for (i = 1; i <= 999; ++i) { sprintf(ptr, ".%03d", i); if (access(CmpFile, 0)) break; } if (i == 1000) { fprintf(stderr, "Backup operation failed: File limit!\n"); return 3; } strcpy(OutFile, CmpFile); if (i > 1) { sprintf(ptr, ".%03d", i-1);
362

DOSYALAR

if ((fo = fopen(CmpFile, "rb")) == NULL) { ptr = CmpFile; goto OPN_ERR; } setvbuf(fo, out_buf, _IOFBF, FBUFSIZ); if (!filecmp(fi, fo)) { printf("No differences encountered: '%s'\n", CmpFile); return 0; } fclose(fo); } printf("File being copied: %s ---> %s\n", argv[1], OutFile); if ((fo = fopen(OutFile, "wb")) == NULL) { ptr = OutFile; goto OPN_ERR; } setvbuf(fo, out_buf, _IOFBF, FBUFSIZ); if (filecopy(fi, fo)) { fprintf(stderr, "File copy error!\n"); return 4; } fcloseall(); return 0;

Uygulama 3 : bol.c ve bir.c programlar : bir dosyann belirkli byte byklnde n kadar sayda dosyaya blnmesi ve daha sonra baka bir programla bu dosyalarn tekrar birletirilmesi. /*******bol.c *******************/

#include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LEN 80

int main(int argc, char **argv) { FILE *fs, *fd; char fsname[MAX_LEN]; char fdname[MAX_LEN] = "dos0001.xxx";
363

32 int chunk; long no_of_chars = 0L; int no_of_files = 0; int ch;

if (argc != 3) { printf("bolunecek dosyanin ismini giriniz : "); gets(fsname); printf("kac byte'lik parcalara bolmek istiyorsunuz?"); scanf("%d", &chunk); } else { strcpy(fsname, argv[1]); chunk = atoi(argv[2]); } printf("%s dosyasi %d uzunlugunda dosyalara bolunecek!\n", fsname, chunk); fs = fopen(fsname, "rb"); if (fs == NULL) { printf("%s dosyasi acilamiyor!\n", fsname); exit(EXIT_FAILURE); } fd = NULL; while ((ch = fgetc(fs)) != EOF) { if (fd == NULL) { fd = fopen(fdname, "wb"); if (fd == NULL) { printf(" %s dosyasi yaratilamiyor!\n", fdname); exit(EXIT_FAILURE); } no_of_files++; printf("%s dosyasi yaratildi!\n", fdname); } fputc(ch, fd); no_of_chars++; if (no_of_chars % chunk == 0) { fclose(fd); printf("%s dosyasi kapatildi!\n", fdname); fd = NULL; sprintf(fdname, "dos%04d.xxx", no_of_files + 1); } } fclose(fs); if (no_of_chars % chunk != 0) { fclose(fd); printf("%s dosyasi kapatildi!\n", fdname); } printf("%ld uzunlugunda %s dosyasi %d uzunlugunda %d adet dosyaya bolundu!\n",
364

DOSYALAR no_of_chars, fsname, chunk, no_of_files); return 0; } /*******bir.c *******************/ #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_LEN 80

int main(int argc, char **argv) { FILE *fs, *fd; char fdname[MAX_LEN]; char fsname[MAX_LEN] = "dos0001.xxx"; int ch; int no_of_files = 0; long no_of_chars = 0L; int k;

if (argc != 2) { printf("birlestirilecek dosyanin ismini giriniz : "); gets(fdname); } else { strcpy(fdname, argv[1]); } fd = fopen(fdname, "wb"); if (fd == NULL) { printf("%s dosyasi yaratilamiyor!\n", fdname); exit(EXIT_FAILURE); } printf("%s dosyasi yaratildi!\n", fdname); while (fs = fopen(fsname, "rb")) { no_of_files++; printf("%s dosyasi acildi!\n", fsname); while ((ch = fgetc(fs)) != EOF) { fputc(ch, fd); no_of_chars++; } fclose(fs); printf("%s dosyasi kapatildi!\n", fsname); sprintf(fsname, "dos%04d.xxx", no_of_files + 1);

} fclose(fd);

365

32 printf("%s dosyasi kapatildi!\n", fdname); printf("%d adet dosya %ld uzunlugunda %s ismli dosya altinda\ birlestirildi!\n", no_of_chars, fdname); for (k = 1; k <= no_of_files; ++k) { sprintf(fsname, "dos%04d.xxx", k); remove(fsname); printf("%s dosyasi silindi!\n", fsname); } return 0; }

no_of_files,

366

enum TR VE enum SABTLER

33 . BLM :

enum TR VE enum SABTLER

Yazlan bir ok programda, yalnzca belirli anlaml deerler alabilen deikenler kullanma ihtiyac duyulur. rnein bir "Boolean" deikeni, yalnzca iki deere sahip olabilmektedir (doru ve yanl deerleri) C dilinde Boolean diye bir veri tipinin olmadn hatrlayalm. Baka bir rnek olarak bir oyun kadnn rengini tutacak bir deikeni verebiliriz. Byle bir deiken yalnzca 4 deiik deer alabilecektir : Sinek, Karo, Kupa, Maa. Uygulamalarda yaplacak olan, deikene tamsay deerleri vermek ve her tamsayy deikenin alaca deerle ilikilendirmektir. rnein: int renk; renk = 1; /* renk deikeni iskambil kadnn renk bilgisini tutacak */ /* 1 tamsay sabitinin karoyu temsil ettiini varsayalm */

Byle bir teknik uygulamalarda pekala kullanlabilir. Ancak bu tekniin dezavantaj, alglanabilmesinin ve okunabilmesinin zor olmasdr. Program okuyacak kii int renk; tanmlama ifadesini grd zaman, renk deikeninin yalnzca 4 farkl deer alabileceini bilemedii gibi, renk = 1; eklinde bir atama yapldn grdnde de renk deikenine "karo" deerinin atanm olduunu anlayamaz. Sembolik sabitlerin kullanlmas ile okunabilirlik byk lde artrlabilir. Yukardaki rneimiz iin aadaki sembolik sabitlerin tanmlanm olduunu dnelim: #define #define #define #define #define KARTRENK SINEK KARO KUPA 3 MACA int 1 2 4

KARTRENK renk; renk = KARO; Artk ifade daha okunabilir bir hale getirilmitir. Bu yntem ilk kullanlan teknikten phesiz daha iyidir, ama yine de en iyi zm olduu sylenemez. Yukardaki sembolik sabit tanmlamalarnn ayn tre ait olduunu program okuyan kiiye gsterecek bir ibare yoktur. Trn alabilecei deer says daha fazla sayda ise her biri iin bir sembolik sabit tanmlamak zahmetli olacaktr. KARO, KUPA vs. olarak isimlendirdiimiz sembolik sabitler programn derlenmesinden nce, nilemci aamasnda tamsay sabitlerle yer deitireceinden, hata ayklama (debug) aamasnda artk bu sabitlere ulalamayacaktr. C dili, tasarm yaplan tr iin yalnzca belirli deerlerin alnabilmesini salayan bir tre sahiptir. Bu tr enum anahtar szcyle belirtilir. Trn alabilecei belirli deerleri gsteren isimlere ise enum sabitleri (enumeration constants) denir.

367

33 enum Trnn ve enum Trne likin enum Sabitlerinin Bildirimi enum [trn ismi] {esabit1, esabit2, ......}; enum bir anahtar szcktr. Derleyici kme parantezleri arasnda isimlendirilmi sembolik sabitlere 0 deerinden balayarak artan srada bir tamsay karlk getirir. rnek: enum RENK {Sinek, Karo, Kupa, Maca}; gibi bir bildirim yapldnda Sinek enum sabiti 0, Karo enum sabiti 1, Kupa enum sabiti 2, Maca enum sabiti ise 3 deerini alr. enum BOOL {FALSE, TRUE}; burada TRUE enum sabiti 1 FALSE enum sabiti ise 0 deerini alacaktr. enum MONTHS {January, February, March, April, May, June, July, August, September, October, November, December}; Bu bildirimde January = 0, February = 1, ... December = 11 olacak ekilde enum sabitlerine 0 deerinden balatarak ardl olarak deer verilir. Eer enum sabitlerine atama operatr ile kme parantezleri iinde deerler verilirse, bu ekilde deer verilmi enum sabitinden sonraki sabitlerin deerleri birer artarak otomatik olarak verilmi olur: enum MONTHS {January = 1, February, March, April, May, June, July, August, September, Oktober, November, December}; Artk ayn rnek iin enum sabitlerinin deerleri January = 1, February = 2, ... December = 12 olmutur. enum sabitlerine int tr say snrlar ierisinde pozitif ya da negatif deerler verilebilir : enum Sample { RT1 = -127, RT2, RT3, RT4 = 12, RT5, RT6, RT7 = 0, RT8, RT9 = 90 }; Yukardaki tanmlamada sabitlerin alaca deerler aadaki gibi olacaktr : RT1 = -127, RT2 = -126, RT3 = -125, RT4 = 12, RT5 = 13, RT6 = 14, RT7 = 0, RT8 = 1, RT9 = 90 Grld gibi bir enum sabitine atama operatr ile atama yapld zaman onu izleyen enum sabitlerinin alaca deerler, bir sonraki atamaya kadar, ardl olarak deerleri 1'er artarak biimde oluturulur.

368

enum TR VE enum SABTLER Yukardaki ifadelerin hepsi bildirim ifadeleridir, tanmlama ilemi deildir. Bir baka deyile derleyici bir nesne yaratmamakta ve dolaysyla bellekte bir yer ayrmamaktadr. enum bildirimi tr bildirmektedir. Bildirilen tr enum trnn yaratlmasnda kullanlan enum tr ismidir. (enumeration tag). Tpk yaplarda ve birliklerde olduu gibi bu trden bir nesne de tanmlanabilir: enum enum enum enum Sample rst; MONTHS this_month; RENK kart1, kart2, kart3; BOOL flag1, openflag, endflag, flag2;

Bildirimlerde enum tr ismi (enumeration tag) hi belirtilmeyebilir. Bu durumda genellikle kme parantezinin kapanmasndan sonra o enum trnden nesne(ler) tanmlanarak ifade noktal virgl ile sonlandrlr. enum {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday} day1, day2, day3; Yukardaki rnekte day1, day2, day3 bildirimi yaplm enum trnden (belirli bir enum tr ismi yok) yaratlm deikenlerdir. Byle bir tanmlama ileminin dezavantaj, artk bir daha ayn enum trnden baka bir deikenin yaratlmamasdr. enum trnden bir nesne iin derleyici, kullanlan sistemde int trnn uzunluu ne kadar ise, o uzunlukta bir yer ayrr. Derleyici iin enum trden bir nesne ile int trden bir nesne arasnda herhangi bir fark bulunmamaktadr. rnein DOS altnda: sizeof(rst) == sizeod(rst) == sizeof(this_month) == sizeof(kart1) == sizeof(flag1) == 2 olacaktr. enum ile bir tr tanmlandna gre, bu trden nesne yaratlabilecei gibi fonksiyonlarn geri dn deerleri ve / veya parametre deikenleri de enum ile yaratlm trden olabilirler. rnein : RENK kartbul (RENK kart1, RENK kartt2); Kartbul fonksiyonu enum RENK trnden iki parametre almakta ve enum RENK trnden bir deere ger dnmektedir. Aadaki program parasn inceleyelim: ... enum RENK {Sinek = 0, Karo, Kupa, Maca}; RENK kartbul(RENK kart1, RENK kart2); ... { RENK kartx, karty, kartz; .... kartx = kartbul(karty, kartz); if (kartx == Sinek){ ...
369

33 } } kartx, karty, kartz deikenleri enum RENK trndendir. Prototipini grdmz kartbul fonksiyonu da enum RENK trnden iki parametre almakta ve enum KART trnden bir deere geri dnmektedir. Dolaysyla program paras iinde kartx deikenine enum RENK trnden bir deer atanm olacaktr. enum sabitleri ve #define nilemci komutuyla tanmlanm sembolik sabitler nesne belirtmezler. rnein: enum METAL {Demir, Bakir, Kalay, Cinko, Kursun}; ... Bakir = 3;/* hata oluturur. nk enum sabitleri sol taraf deeri deillerdir. */ #define komutu nilemciye ilikindir fakat enum sabitlerini ele alarak ileme sokmak derleme modlne ilikindir. Yani #define ve enum farkl aamalarda ele alnrlar. enum sabitleri ok sayda ve ardl tanmlamalar iin tercih edilirler ve bir tr ismi olarak da okunabilirlii artrrlar. rnein: BOOL isprime(int x); gibi bir prototipi gren programc isprime fonksiyonunun yalnzca doru ya da yanl deerlerinden birini rettiini, bylelikle fonksiyonun test amacyla yazldn anlar. enum sabitleri C dilinin faaliyet alan kurallarna uyarlar. Eer bir enum tr bir fonksiyon iinde bildirilmise bu tre ilikin enum sabitleri szkonusu fonksiyonun dnda tannmazlar. Derleyicilerin standart balk dosyalarnda bir ok enum tr ve bu trlere ilikin sembolik sabitler tanmlanmtr. rnein aadaki enum bildirimi 80x86 sistemlerinde alan bir Borland derleyicisinin GRAPHICS.H dosyasndan alnmtr. enum COLORS { BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE }; enum tr isimleri de yap ve birliklerde olduu gibi, typedef anahtar szc yardmyla tip isimlerine dntrlebilirler. rnein: typedef enum {Sinek, Karo, Kupa, Maca} renk;
370

enum TR VE enum SABTLER

bildiriminden sonra artk enum anahtar szc kullanlmadan renk kart1, kart2, kart3, kart4; gibi bir tanmlama yaplabilir. artk renk bir tip ismi belirtmektedir. enum tr iin ok kullanlan bir typedef rnei de: typedef enum {FALSE, TRUE} BOOL; (C++ dilinde struct, union ve enum trne ilikin isimler ayn zamanda trn genel ismi olarak kullanlabilecei iin yukardaki gibi bir typedef bildirimine gerek kalmayacaktr. ) enum Trnn ve enum Sabitlerinin Kullanmna likin Uyarlar enum tr ile bildirilen tr ismi kapsamnda bildirimi yaplan sembolik sabitlerin tamsay olarak derleme aamasnda deerlendirildii sylenmiti. enum trnden tanmlanan bir deikene enum ablonunda belirtilen sembolik sabitler yerine tamsaylar ile atama yaplabilir. enum BOOL {TRUE, FALSE} flag1; tanmlamasnda flag1 = FALSE; yerine flag1 = 1; eklinde atama yapabiliriz? Verilen rnekte flag1 enum BOOL trnden bir deikendir, ve derleyici enum trnden bir deiken ile int trden bir deiken aarsnda fark gzetmez. Ancak derleyicilerin ou, programcnn bunu bilinsizce ya da yanllkla yaptn dnerek bir uyar mesaj verirler. Yukarda verilen rnek iin verilebilecek tipik bir uyar mesaj :"assigning int to BOOL" eklinde olacaktr. enum trden bir deikene ablonda belirtilen snrlar dnda kalan deerleri de atayabiliriz. Yukardaki rnek iin: flag1 = 10; eklinde atama yapabiliriz. (Bu durumda doal olarak yine ayn uyar mesajn alrz.) Dahas enum trden bir deikene baka trden bir deiken ya da sabit de atayabiliriz. Bu durumda tr dnm kurallar gerei atamadan nce otomatik tr dnm yaplarak sa taraf ifadesi int trne dntrlecektir. double x = 5.6; flag1 = x; Bu durumda x deikenine 5 deeri atancaktr. Unutulmamas gereken udur: enum trnn kullanlmasnn nedeni ncelikle okunabilirlii artrmaktr. Yukardaki atamalar yapmamz teknik olarak mmkn olmakla birlikte, bu rnekler okunabilirlii kt ynde etkilediklerinden ancak bilinsiz kullanma rnek olabilir. deal durum, enum trden nesnelere ilgili enum tr iin nceden belirlenmi enum sabitlerini atamaktr.
371

33

Bir enum nesnesini operatrler ileme sokabiliriz. rnein: Derleyici enum trnden nesneleri ayn int tr gibi ele alacandan enum trnden nesneler de int trden nesneler gibi operatrler yardmyla yeni ifadelerin oluturulmasnda kullanlabilirler. enum sabitlerinin kendisi sol taraf deeri gerektiren durumlarda kullanlamazlar. enum {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday} day1, day2, day3; Yukardaki tanmlamadan sonra ++Sunday; gibi bir ifade geersiz olacaktr. nk enum sabitleri bir soltaraf deeri deildir. ++day1; ifadesi ise geerlidir. ++day1 ifadesi day1 + 1 anlamna geleceinden, sonuta day1'e (enum trnden deiken) int trden bir say atanm olacandan baz derleyiciler bu durum iin bir uyar verebilirler.

372

MAKROLAR

34 . BLM :

MAKROLAR

nilemci komutlarndan, #define nilemci komutunun sembolik sabit tanmlamalarnda kullanln daha nce grmtk. Makrolar #define nilemci komutunun paremetreli kullanm ile elde edilen yaplardr. #define nilemci anahtar szcn izleyen isim parametreli olarak kullanlabilir. Bu durum sentaks olarak isime bitiik olarak yazlan alan parantez ile belirtilir. Bu parantezin iinde tanmlanan makroya ilikin parametre(ler) yer alr. Kapanan parantezi izleyen yaz ise makroya ilikin deitirme listesidir. nilemci makro parametrelerine ilikin deitirme listesini bir ablon olarak kabul eder ve kaynak kod iinde makronun arldn tespit ettiinde makro armnda kullanlan argumanlar, ablona uygun olarak aar. Basit bir rnekle balayalm: #define Alan(x, y) ((x) * (y)) Yukardaki makro tanmlamasnda x ve y makroya ilikin parametrelerdir. nilemci yukardaki satr grdkten sonra kaynak kod iinde bu makronun arldn tespit ederse, makroyu yukardaki ablona gre aacaktr. rnein kaynak kod ierisinde a = Alan(b, c); eklinde bir deyimin yer aldn dnelim. Bu durumda nilemci Alan makrosunun arldn anlar. Ve ifadeyi aadaki ekilde aar : a = ((b) * (c)); Makro almnn sadece metinsel bir yer deitirme oldugunu bilmeliyiz. Yani yukardaki deyim rnein : a = Alan(7 + 3, 8 + 1); eklinde olsayd, nilemci bunu a = ((7 + 3) * (8 + 1)); eklinde aacakt. Yer deitirme ilemcinin derlemenin n modl olan nilemci tarafndan derleme ncesi yapldn ve derleyicinin object kod oluturmak iin ele ald kaynak kodun nilemci modlnn kts olduunu hatrlatalm. Yani derleyicinin ele ald kodda artk makro armlar deil makrolarn nilemci tarafndan alm biimleri bulunacaktr. Baka bir makroyu inceleyelim : #include <stdio.h> #define Kare_fark(x, y) int main() { int a = 10; int b = 5; (((x) - (y)) * ((x) + (y)))

373

34 int c; c = Kare_fark(a, b); printf("c = %d\n", c); return 0; } Yukardaki kaynak kodu alan nilemci iini bitirdikten sonra derleyicinin ele alaca kaynak kod aadaki ekilde olacaktr : stdio.h dosyasnn ierii int main() { int a = 10; int b = 5; int c; c = (((a) - (b)) * ((a) + (b))); printf("c = %d\n", c); return 0; } Bir yln artkyl olup olmadn test etmek iin kullanlacak baka bir makro: #define is_leap(y) ((y) % 4 == 0 && (y) % 100 != 0 || (y) % 400 == 0) Makrolar Ne Amala Tanmlanr Makrolar fonksiyonlara bir alternatif olarak kullanlmaktadr. Yukardaki rnekleri tekrar inceleyelim. Alan makrosu yerine: int alan(int en, int boy) { return en * boy; } Kare_fark makrosu yerine: int Kare_fark(int a, int b); { return (a - b) * (a + b); } is_leap makrosu yerine: int is_leap(int year) { return year % 4 == 0 && year % 100 != 0 || year % 400 == 0; } fonksiyonlar tanmlanabilirdi.

374

MAKROLAR Baka bir rnek olarak da toupper fonksiyonunun makro biiminde tanmlanm olmas verilebilir. (toupper derleyicilerin ounda fonksiyon olarak deil makro biiminde tanmlanmtr.) #define TOUPPER(c) ((c) >= 'a' && (c) <= 'z' ? (c) - 'a' + 'A' : (c)) Aada fonksiyon tanmlamalarna alternatif olacak baz basit makrolar tanmlanmaktadr: #define #define #define #define ISUPPER(c) ISLOWER(c) ISDIGIT(c) ISEVEN(x) ((c) >= 'A' && (c) >= 'Z') ((c) >= 'a' && (c) >= 'z') ((c) >= '0' && (c) >= '9') ((x) % 2 == 0)

Bir makronun parametresi olmayabilir. rnein: #define getchar() getc(stdin) Yukardaki makro yerine sembolik sabit de kullanlabilirdi. Ancak fonksiyon yapsna benzetmek amacyla parametresi olmayan bir makro olarak tanmlanmtr. Makrolarla Fonksiyonlar Arasndaki Farkllklar Nelerdir Makrolar fonksiyonlara alternatif olarak kullanulmalarna karlk, makrolar ile fonksiyonlar arasnda ok nemli farklar bulunmaktadr: 1. Makrolar kaynak kodu dolaysyla da alabilir (exe) kodu bytrler. Makrolar nilemci aamasnda deerlendirilir. rnein is_leap makrosunun #define nilemci komutuyla tanmlandktan sonra kaynak kod iinde yz kere arldn dnelim. nilemci kaynak kodu ele aldnda yz tane arlma ifadesinin her biri iin makroyu aacaktr. Derleyici modl kaynak kodu ele aldnda artk makrolar deil makrolarn alm eklini grecektir. Makro almlar kaynak kodu bytecektir. Kaynak kodun bymesinden dolay alabilen dosyann boyutu da byyecektir. Oysa makro yerine bir fonksiyon tanmlansayd, Fonksiyon arlmas durumunda yalnzca arlma bilgisi kayanak koda yazlmaktadr. 2. Makrolarn en byk avantaj, fonksiyon arma ilemindeki greli yavalktan kaynaklanr. Fonkiyon armas sembolik makine dili (assembler) dzeyinde ele alndnda, baz ilave makine komutlar da icra edilmekte ve bu makine komutlarnn icras ve yaplan baz ilave ilemler icra sresini uzatacaktr. Fonksiyon arlrken CALL ve geri dnte RET makine komutlar icra edilmektedir. Fonksiyon arm iin ayrca parametre aktarm ve stack dzenlemesi ilemleri yaplmaktadr. Makrolarn almas metinsel bir yer deitirme olduu iin, yukarda anlatlan ilave ilemler yaplmaz. Byece programn hz grece olarak artacaktr. 3. Makrolar trden bamszdr. (generic). Makrolar metinsel bir yer deitirme olduundan fonksiyonlar gibi belirli bir tre bal deildirler. rnein iki saydan byne geri dnen bir fonksiyon yazmak istersek bu fonksiyonun parametre deikenleri belirli bir trden olmak zorundadr: int max_of_two(int number1, int number2) { return (number1 > number2 ? number1 : number2); }

375

34 Yukardaki fonksiyon int trden deerler iin yazIlmtr. Baka trden deerler fonlksiyona arguman olarak gnderildinde tr dnm kurallar ileyecek ve argumanlarn tr parametre deikenlerinin trlerine dntrlecektir. Oysa yukardaki fonksiyon yerine bir makro tanmlanm olmas durumunda bu makro trden bamsdz olacaktr : #define max_of_two(a, b) ((a) > (b) ? (a) : (b))

Yukardaki makro aadaki ekillerde arlabilir: int x, y, z; char c1, c2, c3; double d1, d2,d3; ... x = max_of_two(y, z); c1 = max_of_two(c2, c3); d1 = max_of_two(d2, d3); 4. Fonksiyonlar kendilerine gnderilen argumanlar bir kez ele alrlar ancak makrolar argumanlarn birden fazla ele alabilirler: Aadaki makrolar gz nne alalm: #define KARE(a) int x = 10, y; y = KARE(x++) ifadesi makro tanmlamasna uygun olarak aldnda y = ((x++) * (x++)) ifadesi oluacaktr ki bu ifade de, operatrler konusunda grdmz gibi "undefined behavior" zellii gsterecektir. Oysa KARE bir fonksiyon olsayd bu fonksiyonun KARE(x++); eklinde arlmas bir probleme neden olmayacakt. x++ ifadesi x deikeninin (artmayan) deerini reteceinden, KARE fonksiyonu 10 deeri ile arlm olacakt. 5. Makrolarda makrolarn argumanlar ile parametreleri arasnda tr dntrlmesi sz konusu deildir. Bir fonksiyon arlma ifadesini grdnde derleyici fonksiyona gnderilen arguman ile fonksiyonun ilgili parametre deikeni arasndaki tr uyumunu kontrol eder. Eer bu trler arasnda bir farkllk varsa, mmknse, tr dntrme kurallar gerei arguman olan ifdadenin trn parametre deikeninin trne evirir. Makro argumanlarnn nilemci tarafndan makronun parametrelerine gre tr uyumunun kontrol edilmesi ya da tr dntrme ilemine tabi tutulmas sz konusu deildir. C++ dilinde makrolarn kullanm C diline gre ok daha azalmtr. Zira C++ dilinde bir ok durumda makrolar yerine inline fonksiyonlar kullanlmaktadr. 6. Bir gsterici bir makroyu gsteremez. ((a) * (a))

376

MAKROLAR leride greceimiz gibi C dilinde bir fonksiyonu gsteren gsterici tanmlanabilir. Ve bu gstericilerden C programclnda eitli biimlerde faydalanmak mmkndr. Ancak makrolar nilemci aamasnda ele alndndan bir gstericini bir makroyu gstermesi sz konusu deildir. # ve ## nilemci operatrleri Makro tanmlamalar iki zel operatr ierebilirler. Bu operatrler derleyiciyi deil nilemciyi ilgilendirmektedir. Yani nilemcinin kts derleyici modlne verildiinde artk kaynak kod bu operatr atomlarndan arndrlm olacaktr. # atomu makronun yer deitirme listesinde (replacement list) yer alabilecek bir operatrdr. Operand makro parametresidir. Makronun alm srasnda #operandna alnan arguman string haline getirilir. Yani ift trnak iine alnr. Bu yzden bu atoma stringe dntrme operatr de denir. (stringizing operator) #atomunun kullanlabilecei bir ka tema aada verilmektedir. Aadaki makroyu inceleyelim : #define PRINT_INT(x) printf(#x " = %d\n", x)

rnein kaynak kod ierisinde int result = 5; PRINT_INT(result); eklinde makronun arldn dnelim. Makro aldnda derleyiciye giden kaynak kod aadaki ekilde olacaktr : int result = 5; printf("result" " = %d\n", result); Aralarnda boluk karakterleri dnda baka bir karakter bulunmayan stringlerin derleyici tarafndan tek bir string olarak birletirildiklerini anmsarsak, yukardaki ifadenin derleyici asndan aadaki ekilde yorumlanacan grebiliriz : printf("result = %d\n", result); Bylece yukardaki deyimin icra edilmesiyle ekrana aadaki ekilde bir yaz yazdrlabilecektir : result = 5; ## nilemci operatr iki operand alan araek konumunda bir operatrdr. Makronun almas srasnda Operand olan atomlar birletirerek tek bir atom haline getirir. Bu yzden atom birletirme operatr diye de isimlendirilir. (Token pasting operator), Atom birletirme operatrnn operandlarndan biri eer bir makro parametresi ise, birletirme ilemi parametre ilgili argumanla yer deitirdikten sonra yaplacaktr. Atom birletirme operatr ##, string yapma atomu kadar sk kullanlan bir operatr deildir. Sadece ok zel durumlarda kullanlmaktadr. Makro tanmlamalarnda nelere dikkat edilmelidir? Makro tanmlamalarnda en sk yaplan hatalar makro alm neticesinde, operatr ncelikleri yznden alm kodun beklenilmeyen ekilde deer retmesidir. rneklerle aklayalm :

377

34 #define ALAN(a) Yulardaki makro x = ALAN(y); eklinde arlrsa nilemci kodu x = y * y; eklinde aacandan bir problem olumayacaktr. Ancak makronun int y = 1; x = ALAN(y + 5) eklinde arldn dnelim. Bu durumda derleyici kodu x = y + 5 * y + 5; eklinde aacaktr. Ve bu durumda y deikenine 36 yerine 11 deeri atanacaktr. Bu tr problemlerin nlenebilmesi iin makro tanmlamalarnda makro alm ifadesinde yer alan makro parametreleri parantez iine alnmaldr. Yukardaki makro #define ALAN(a) (a) * (a) a*a

eklinde tanmlansayd makro arm nilemci tarafndan aldnda x = (y + 5) * (y + 5); ifadesi oluacaktr ki, deyimin icras sonucunda x deikenine 36 deeri atanacaktr. Peki ya makro aadaki ekilde arlrsa x = 72 / ALAN(y + 5); x deikenine 2 deerinin atanmas gerekireken, makro aadaki gibi alacandan x = 72 / (y + 5) * (y + 5) ; x deikenine 72 deeri atanacaktr. Makro tanmlamalarnda makro alm ifadeleri en dardan parantez ierisine alnmaldr. Sz konusu makro #define ALAN(a) ((a) * (a))

eklinde tanmlanm olsayd

378

MAKROLAR x = 72 / ((y + 5) * (y + 5) ); makro nilemci tarafndan yukardaki gibi alacakt ve bu kodun icras ile x deikenine gerekten 2 deeri atanacakt. Makrolarla ilgili olarak sk yaplan baka bir hata da makro tanmlamasnda makro ismi ile makro parametre parantezi arasnda boluk brakmaktr. ALAN makrosunun yanllkla aadaki gibi tanmlandn dnelim. #define ALAN (a) ((a) * (a))

nilemci artk yukardaki tanmlamay bir makro deil bir sembolik sabit olarak ele alacaktr. nilemci kaynak kodun geri kalan ksmnda grd ALAN yazlarn (a) ((a) * (a)) ile yer deitirecektir. rnein : ALAN(x + 3); gibi bir ifadenin var olduunu dnelim. nilemci bu kodu aadaki ekilde aacaktr : (a) ((a) * (a))(x + 3);

ayn isimli makro ve fonksiyonlar Bir makro ile ayn isimli bir fonksiyon kaynak kod iinde yer alabilir. Bu durumda bu isimle bir arm yapldnda ne olacaktr. makro mu fonksiyon mu arlm olacaktr? #define max(x, y) ((x )> (y) ? (x) : (y))

gibi bir makronun tanmlandn ve kaynak kod ierisinde aadaki fonksiyon tanmnn da yer aldn dnelim. int max(int number1, int number2) { return x > y ? x : y; } Kaynak kod ierisinde rnein c = max(a, b); eklinde bir deyimin yer aldn dnelim. Bu durumda ne olacaktr? Tabi ki nilemci makroyu aacaktr. Zira makrolarn almas, yani metinsel yer deitirme ileminin yaplmas, daha derleyici kaynak kodu ele almadan nilemci tarafndan yaplacandan, derleyici modlne sra geldiinde derleyici artk yukardaki arm ifadesi yerinde c = a > b ? a : b; ifadesini grecektir. Ayn isimli makro ve fonksiyon tanmlamalarnn var olmas durumunda, makro almn yaplmas yerine fonksiyonunu arlmas isteniyorsa ne yaplabilir?
379

34

Bu durumda fonksiyon arma ifadesinde fonksiyon ismi parantez iine alnrsa, nilemci artk bir yer deitirme yapmaz ve derleyici fonksiyon arma kodu retir.

uzun makrolarn yazlmas Daha ilevsel makrolarn yazlmasnda virgl operatr kullanlabilir. Aadaki makroyu dnelim: #define ECHO(s) (gets(s), puts(s))

Fonksiyon armlar da birer ifade olduundan, fonksiyon arm ifadelerinin virgl operatryle birletirilmesi tamamen legaldir. Yukardaki makroyu bir fonksiyonmu gibi arabiliriz: char *ptr = "Ahmet"; ECHO(str); nilemci makroyu aadaki ekilde aacaktr : char *ptr = "Ahmet"; (gets(str), puts(str)); Ancak bir makronun bir seri deyimi ierdiini dnelim. fade yerine belirli sayda deyimin olmas, baz durumlarda virgl operatrnn kullanlmasna izin vermeyebilir. nk virgl operatrnn operandlar deyim deil ifadelerdir. Uzun ve ilemsel makrolarn yazlmasnda virgl operatrnn kullanlmas yerine makro deitirme listesini en dtan kme parantezleri iine de alabilirdik : #define ECHO(s) { gets(s); puts(s); }

Makro yukardaki gibi tanmlandnda baz problemler oluabilir. rnein makronun aadaki gibi bir if deyimi iinde arldn dnelim : if (echo_flag) ECHO(str); else gets(str); nilemci makroyu aadaki gibi aacaktr : if (echo_flag) { gets(str); puts(str); }; else gets(str); Kod derleyici modlne geldiinde aadaki gibi yorumlanacandan if (echo_flag) { gets(str); puts(str); } ; else
380

MAKROLAR gets(str); if ksm olmayan bir else olmas gerekesiyle derleme zamannda bir error oluacaktr. Oluan problemi nasl zebiliriz? Makronun arlmas durumunda sonlandrc ";" at0munu koymayarak? Bu irkin bir tarz olurdu... Bu durumda iki yntem kullanabiliriz : 1. Makro almnda do while dng yapsn kullanmak: #define ECHO(s) do { gets(s); puts(s); } while (0) \ \ \ \

(#define nilemci komutunun kullanlmasnda satr sonunda yer alan "\" sonraki satrda devam edeceini gsterdiini hatrlayalm) artk yukardaki makro ECHO(str);

karakterinin komutun bir

eklinde arldnda, makro nilemci tarafndan aldktan sonra, arma ifadesinin sonundaki sonlandrc (;) do while deyiminin sentaksnda bulunan while parantezini izleyen sonlandrc olacaktr. 2. Makro almnda if deyimi kullanmak: #define ECHO(s) if (1) { gets(s); puts(s); } else yukardaki makro ECHO(str); eklinde arlp nilemci tarafndan aldnda makro arma ifadesinin sonundaki sonlandrc (;) if deyiminin yanl ksmna ilikin (else ksmna ilikin) bo deyim olacaktr. \ \ \ \ \

381

35

35 . BLM :

DER NLEMC KOMUTLARI

Daha nceki derslerimizde nilemci kavramn incelemi ve nilemciye ilikin iki komutu incelemitik. Bu komutlar #include ve #define nilemci komutlaryd. nilemciye ilikin dier komutlar da bu desimizde ele alacaz. Koullu derlemeye ilikin nilemci komutlar Koullu derlemeye ilikin komutlar #if #ifdef ve #ifndef olmak zere tanedir. Koullu derleme komutuyla karlaan nilemci, koulun salanmas durumunda belirlenmi program blounu derleme ilemine dahil eder, koul salanmyorsa ilgili program blounu kartarak derleme ilemine dahil etmez. #if nilemci Komutu Genel biimi yledir: #if <sabit ifadesi> ... ... #else ... .. #endif Sabit ifadesinin saysal deeri sfr d bir deer ise (sabit ifadesi doru olarak deerlendirilerek ) #else anahtar szcne kadar olan ksm, eer sabit ifadesinin saysal deeri 0 ise (sabit ifadesi yanl olarak deerlendirilerek) #else ile #endif arasndaki ksm derleme ilemine dahil edilir. rnek : #include <stdio.h> int main() { #if 1 printf("bu blm derleme ilemine sokulacak\n"); #else printf("bu blm derleme ilemine sokulmayacak\n"); #endif } nilemci, program # ieren satrlardan arndrarak aadaki biimde derleme modlne verecektir: [stdio.h dosyasnkn ierii ] void main() { printf("bu blm derleme ilemine sokulacak\n"); } #if komutunun sandaki sabit ifadesi operatr ierebilir ancak deiken ieremez. rnein: #define MAX 100 ... #if MAX > 50 ...
382

DER NLEMC KOMUTLARI #else ... #endif Tpk C dilinde olduu gibi nilemci komutlarnda da #else - #if merdivenlerine sk raslanr. #define MAX 100 ... #if MAX > 50 ... #else #if MAX < 1 ... #endif #endif yerine #elif nilemci komutu da kullanlabilir. Ancak bu durumda #elif nilemci komutuna ilikin #endif ile dardaki #if komutuna ilikin #endif ortak olacaktr. #define MAX 100 ... #if max > 50 ... #elif MAX > 30 ... #else ... #endif Koullu derleme ilemi kaynak kodun belirli blmlerini belli durumlarda derleme ilemine sokmak iin kullanlr.phesiz bu durum programc tarafndan da kodun eklenip silinmesiyle salanabilirdi. Ancak #if komutu buna daha kolay olanak salamaktadr. #ifdef ve #ifndef nilemci Komutlar Belli bir sembolik sabitin tanmland durumda #else anahtar szcne kadar olan ksm, tanmlanmad durumda #else ve #endif arasndaki ksm derleme ilemine dahil edilir. Genel biimi aadaki gibidir : #ifdef <sembolik sabit> ... #else ... #endif rnein: #include <stdio.h> #define SAMPLE void main()
383

35 { #ifdef SAMPLE printf("SAMPLE tanmlanm"); #else printf("SAMPLE tanmlanmam"); #endif } Burada SAMPLE #define ile tanmland iin #ifdef doru olarak ele alnr ve derleme ilemine ilk printf() dahil edilir. #ifdef ile sorgulama yaplrken sembolik sabitin ne olarak tanmlandnn bir nemi yoktur. #ifdef nilemci komutu ayn balk dosyasn kullanan ve birka modlden oluan programlarda yaygn olarak kullanlr. rnein dosya1.c, dosya2.c ve dosya3.c modllerinden oluan bir projede project.h isimli bir balk dosyas ortak olarak kullanlyor olsun. Bu durumda global deikenlerin yalnzca bir modlde global dierlerinde extern olarak kullanlmas gerekecektir. Bu aadaki gibi salanabilir. /* project.h */

#ifdef GLBVAR #define EXTRN #else #define EXTRN extern #endif /* Global deikenler */ EXTRN int x; EXTRN int y; ... imdi modllerin birinde, bu balk dosyasnn include edilmesinden nce, GLBVAR adl bir sembolik sabit tanmlanrsa, nilemci EXTRN szcn sileceinden (onun yerine boluk karakteri kopyalayacandan) o modl iin global tanmlama yaplm olur. Deilse EXTRN yerine extern anahtar szc nilemci tarafndan yerletirilecektir. #ifdef nceden tanmlanm sembolik sabitlerle tanabilirlii salamak amacyla da sklkla kullanlmaktadr. #ifndef komutu #ifdef komutunun tam tersidir. Yani sembolik sabit tanmlanmamsa #else ksmna kadar olan blm, tanmlanmsa #else ile #endif arasndaki ksm derleme ilemine dahil edilir. rnein : #ifndef BLINK #define BLINK #endif

0x80

Burada BLINK sembolik sabiti ancak daha nce tanmlanmamsa tanmlanmas yaplmtr. Daha nce tanmlanmsa tanmlanma ilemi yaplmamtr. defined() nilemci Operatr defined bir sembolik sabitin tanmlanp tanmlanmadn anlamak iin kullanlan bir nilemci operatrdr. Parantez ierisindeki sembolik sabit tanmlanmsa 1 deerini tanmlanmamsa 0 deerini retir. rnein
384

DER NLEMC KOMUTLARI

#if defined(MAX) ... #endif Burada MAX tanmlanmsa defined nilemci operatr 1 deerini retecektir. Bu deer de #if tarafndan doru olarak kabul edilecektir. Burada yaplmak istenen #ifdef ile aadaki gibi de elde edilebilirdi: #ifdef MAX ... #endif Koullu Derlemeye likin nilemci Komutlar Nerelerde Kullanlr 1. Debug Amacyla Bir program yazarken, programn doru alp almadn test etmek amacyla birtakm ilave kodlar yazabiliriz. Bu tr test kodlarnn biz program test ettiimiz srece almasn anak programnz yazp bitirdikten sonra, yapacamz son derlemede, bu test kodlarnn derleme ilemine sokulmamasn isteriz. Test amac ile yazlan kod paracklar derleme ilemine DEBUG adl bir sembolik sabitin deerine bal olarak aadaki ekilde derleme ilemine katlr ya da katlmaz. #if DEBUG ... #endif Eer DEBUG nilemci sabitinin deeri 0 ise #if ile #endif arasndaki kod paral derlemeye ilemine katlmaz. 2. Birden Fazla Makinede ya da letim Sisteminde alacak Programlarn Yazlmasnda #if defined(WINDOWS) ... #elif defined(DOS) ... #elif defined(OS2) ... #endif 3. Bir Programn Farkl Derleyicilerde Derlenmesi Durumunda #ifdef ... #else ... #endif __STDC__

Yukarda nilemci komutlar ile derleyicinin standart C derleyicisi olup olmadna baklarak belirli kaynak kod paralar derlemeye dahil ediliyor.

385

35 4. Bir Balk Dosyasnn Bir Kereden Daha Fazla Kaynak Koda Dahil Edilmesini nlemek Amacyla. (multiple inclusion) Ayn balk dosyas kaynak koda #include nilemci komutuyla iki kez eklenmemelidir. Bir balk dosyas bir kaynak koda birden fazla eklenirse ne olur? Balk dosyalar iinde ne oldugunu hatrlayalm : 1. Fonksiyon prototip bildirimleri Hatrlayacamz gibi fonksiyon prototip bildirimlerinin zde olarak birden fazla yaplmas bir error oluumuna neden olmaz. 2.Sembolik sabit ve makro tanmlamalar zde sembolik sabit ve makro tanmlamalar kaynak kodun nilemci tarafndan ele alnmas srasnda bir hataya neden olmaz. 3.typedef bildirimleri (tr tanmlamalar) typedef anahtar szc ile yaplan tr tanmlamalar zde olmalar durumunda yine derleme zamannda bir hata oluumuna neden olmazlar. 4.Yap, birlik, bit alan, enum tr bildirimleri Bir zde olarak bile birden fazla yaplrsa derleme zamannda hata oluumuna neden olur. Bu yzden bir balk dosyas kaynak koda birden fazla dahil edilirse ve o balk dosyas iinde bir yapnn, birliin, bit alannn ya da enum trnn bildirimi yaplm ise derleme zamannda error olulacaktr. Dolaysyla balk dosyasnn ikinci kez kaynak koda eklenmesi engellenmelidir. Bir balk dosyasnn kaynak koda birden fazla eklenmesi aadaki gibi engellenebilir: /* GENERAL. H BR BALIK DOSYASI RNE */

#ifndef GENERAL_H ... #define GENERAL_h ... #endif Yukardaki kod parasnda balk dosyas #ifndef ve #endif nilemci komutlarnn arasna alnmtr. Eer GENERAL_H sembolik sabiti tanmlanmsa, bu general.h balk dosyasnn kaynak koda daha nce dahil edildii anlamna gelecek ve #ifndef ile #endif arasndaki kaynak kod paras kaynak koda eklenmeyecektir. Eer GENERAL_H sembolik sabiti tanmlanm ise, #ifndef ile #endif arasndaki kaynak kod paras kaynak koda eklenecektir. Eklenen kod paras iinde #define GENERAL_h nilemci komutu da bulunmaktadr. Bu standart yntemle, yani balk dosyalarnn bir sembolik sabitin tanmlanmasna bal olarak kaynak koda eklenip eklenmemesi salanr bylece bir kaynak kodun birden fazla kaynak koda eklenmesi engellenmi olur.

Koullu derlemeye ilikin nilemci komutlarnn kullanlmasna ilikin bir program paras rnei aada verilmitir : #if DLEVEL > 5 #define SIGNAL 1 #if STACKUSE == 1 #define STACK #else #define STACK

200 50
386

DER NLEMC KOMUTLARI #endif #else #define SIGNAL 0 #if STACKUSE == 0 #define STACK #else #define STACK 50 #endif #endif #if DLEVEL == 0 #define STACK 0 #elif DLEVEL == 1 #define STACK #elif DLEVEL > 5 display(debugptr); #else #define STACK 200 #endif

200

100

Yukardaki rnekte birinci #if blounun altnda iki ayr #if #else yaps bulunmaktadr. DLEVEL sembolik sabitinin 5'den byk olup olmamasna gre deerine gre nilemci tarafndan doru ksm ya da yanl ksm ele aacaktr. #elif nilemci komutunun da yer ald ikinci #if blounda ise DLEVEL sembolik sabitinin deerine gre 4 seenekten biri ele alnacaktr. DLEVEL sembolik sabitinin deerine bal olarak STACK sembolik sabiti 0, 100 ya da 200 olarak tanmlanmaktadr. DLEVEL sembolik sabitinini deerinin 5'den byk olmas durumunda ise display(debugptr); deyimi derleyiciye verilmekte ve bu durumda STACK sembolik sabiti tanmlanmamaktadr. Genel nilemci Komutlar #undef nilemci komutu Bir sembolik sabitin ilki ile zde olmayan bir biimde ikinci kez tanmlanmas C nilemcisi tarafndan "uyar " olarak deerlendirilir. Bu durumda ilk tanmlanann m ikinci tanmlanann m geerli olaca tanabilir bir zellik deildir. (Derleyiciler genellikle ikinci tanmlanma noktasna kadar ilkinin, ikinci tanmlanma noktasndan sonra ise ikincinin geerliliini kabul ederler.) rnein aadaki gibi bir tanmlama ilemi uyar gerektirir. #define MAX ... #define MAX 100 200

Bir sembolik sabitin ilki ile zde olarak tanmlanmasnda herhangi bir problem kmayacaktr. Bir sembolik sabit ikinci kez tanmlanmak isteniyorsa nce eski tanmlamay ortadan kaldrmak gerekmektedir. Bu ilem #undef nilemci komutu ile yaplr. #undef nilemci komutunun yanna geerlilii ortadan kaldrlacak sembolik sabitin ya da makronun ismi yazlmaldr. rnein : #undef MAX
387

35 #define MAX 200

nce MAX sembolik sabitinin tanmlanmas iptal edilmi, sonra 200 olarak yeniden tanmlanmtr. #undef ile tanmlanmas kaldrlmak istenen sembolik sabit, daha nce tanmlanm olmasa bile bu durum bir probleme yol amaz. rnein yukardaki rnekte MAX tanmlanmam olsayd bile bir uyar ya da hataya yol amazd. #error nilemci komutu nilemci #error komutunu grnce bu komutu izleyen mesaj basarak derleme ilemibne son verir. rnein: #ifdef #error #endif __TINY__ Bu program tiny modelde derlenmez!..

#error direktifi error nilemci komutunun yannda (boluk karakteri ile ayrlm) bir hata mesaj yer alr. Derleyici error direktifini grnce bu hata mesajn yazarak deleme ilemine son verecektir. #ifdef __TINY__ #error bu program tiny modelde derlenemez #endif

NCEDEN TANIMLANMI SEMBOLK SABTLER

nilemci bu sabit yerine kaynak koddaki o anda bulunan satr numaralarn yerletirir. Bu zel durumlar test edilerek eitli seenekler deerlendirilmektedir. Bu ksmda nceden tanmlanm sembolik sabitlerin standart olanlarndan bir ksmn inceleyeceiz. __LINE__ nilemci bu sabit yerine kaynak koddaki o anda bulunulan satr numarasn yerletirir. #include dosyalarnn ii bu ileme dahil edilmez. Aadaki kodu inceleyiniz: #include <stdio.h> int main() { printf("satr no . %d\n", __LINE__) return 0; } __FILE__ nilemci bu sabit yerine iki trnak ierisinde kaynak dosyann ismini yazar. Aadaki rnekte kaynak dosyann ismi ekrana yazdrlyor. string ifadelerinin karakteri gsteren birer adres olduunu anmsaynz. #include <stdio.h>
388

DER NLEMC KOMUTLARI

int main() { printf("dosya ismi : %s\n", __FILE__); return 0; } __DATE__ ve __TIME__ : nilemci bu sabitlerin yerine derlemenin yapld tarih ve zaman yazar. __DATE__ "aaa gg yyyy", __TIME __ ise ss:dd:ss biiminde yazlmaktadr.

Tanabilirlie ilikin sembolik sabitler __STDC__ C'de kullandmz kimi anahtar szckler tam anlamyla standart deildir. Bu anahtar szckler sistemler aras farkllklara karlk verebilmek iin kullanlmaktadr. rnein 8086 sistemlerinde kullandmz far ve near standart olarak her sistemde bulunmayan iki anahtar szcktr. Derleyiciniz eger yalnzca C'nin anahtar szcklerini destekliyorsa __STDC__ sembolik sabiti tanmlanm varsaylr. erisinde standart olmayan anahtar szckler geen programlar bu sembolik sabit kullanlarak daha tanabilir bir hale getirilebilir : #ifdef #define #endif __STDC__ far

Yukardaki rnekte eer yalnzca standart C'nin anahtar szcklerini kullanan bir derleyici ile alyorsanz __STDC__ tanmlanm olacandan far anahtar szc silinecektir. __TINY__ __SMALL__ __MEDIUM__ __COMPACT__ __LARGE__ __HUGE__ Borland derleyicilerinde aktif olarak kullanlan bellek modeline gre bu sembolik sabitlerden bir tanesi tanmlanm saylr. rnein o anda LARGE modelde allyorsa yalnzca __LARGE__ SEMBOLK SABT TANIMLANMI SAYILIR. __MSDOS__ DOS ile UNIX arasndaki tanabilirlik problemlerini zmek iin kullanlr. Eer MSDOS altndaki bir derleyici ile alyorsanz __MSDOS__ sembolik sabiti tanmlanm kabul edilmektedir. #ifndef #define #endif __MSDOS__ far

Yukardaki rnekte eer MSDOS ile allmyorsa far anahtar szc silinmektedir.

389

35

__TURBOC__ Borland firmasnn turbo C uyarlamalaerndan bir tanesinde alyorsanz bu sembolik sabit derleyiciniz tarafndan tanmlanm varsaylr. Sembolik sabitin tanmlanan deeri (test iin nemli olamaz) uyarlamadan uyarlamaya deiebilmektedir. __BORLANDC__

Borland firmasnn BORLAND C uyarlamalaerndan bir tanesinde alyorsanz bu sembolik sabit derleyiciniz tarafndan tanmlanm varsaylr. Sembolik sabitin tanmlanan deeri (test iin nemli olamaz) uyarlamadan uyarlamaya deiebilmektedir. __cplusplus C++ ismiyle piyasaya kan derleyiciler standart C yi de destekler. Bu durumda derleyicilerin hangi C uyarlamasnda altklar bu sembolik sabit yardmyla renilebilir.

390

You might also like