You are on page 1of 529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

NDEKLER
Programlama ve C Say Sistemleri Genel Kavramler ve Terimler Bir C Program Oluturmak Veri Trleri Bildirim ve Tanmlama Deimezler levler leler Bilinirlik Alan ve mr Kontrol Deyimleri if Deyimi lev Bildirimleri Tr Dnmleri Dng Deyimleri Koul leci nilemci Komutlar - 1 Switch Deyimi goto Deyimi Rastgele Say retimi ve Kontrol Deyimlerine likin Genel Uygulamalar Diziler char Trden Diziler ve Yazlar sizeof leci Gstericiler Gsterici leleri Yazlarla lgili lem Yapan Standart levler Gsterici Hatalar void Trden Gstericiler Dizgeler Gsterici Dizileri Gstericiyi Gsteren Gsterici ok Boyutlu Diziler exit, abort atexit levleri Dinamik Bellek Ynetimi Belirleyiciler ve Niteleyiciler Yaplar Tr simleri Bildirimleri ve typedef Belirleyicisi Tarih ve Zaman ile lgili lem Yapan Standart levler Birlikler Numaralandrmalar Bitsel leler Bit Alanlar Komut Satr Argmanlar Dosyalar Makrolar nilemci Komutlar - 2 lev Gstericileri zyinelemeli levler Deiken Sayda Arguman Alan levler Kaynaklar 3 15 25 31 33 39 45 53 73 93 101 102 119 127 137 159 165 175 185 189 199 215 226 229 235 265 280 283 291 301 307 311 321 323 341 359 387 397 411 421 427 441 445 451 485 495 505 519 525 529

1/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

PROGRAMLAMA ve C
Yazlm (software), programlama ve programlamayla ilgili konularn geneline verilen isimdir. Yazlm denince akla programlama dilleri bu diller kullanlarak yazlm kaynak programlar ve oluturulan eitli dosyalar gelir.

Yazlm Nedir

Donanm Nedir

Donanm (hardware), bilgisayarn elektronik ksm, yapsna verilen isimdir.

Yazlmn Snflandrlmas

Yazlm uygulama alanlarna gre eitli gruplara ayrlabilir:

1. Bilimsel yazlmlar ve mhendislik yazlmlar Bilimsel konularda ve mhendislik uygulamalarndaki problemlerin zlmesinde kullanlan yazlmlardr. Bu tr yazlmlarda veri miktar greli olarak dktr ancak matematiksel ve istatistiksel algoritmalar youn olarak kullanlr. Byle programlar arlkl olarak hesaplamaya ynelik ilemler ierir ve bilgisayarn merkezi ilem birimini (CPU) youn bir biimde kullanr. Elektronik devrelerin zmn yapan programlar, istatistik analiz paketleri, bu tr programlara rnek olarak verilebilir. 2. Uygulama yazlmlar Veri taban arlkl yazlmlardr. Genel olarak verilerin yaratlmas, ilenmesi ve dosyalarda saklanmas ile ilgilidir. Bu tr programlara rnek olarak stok kontrol programlar, mteri izleme programlar, muhasebe programlar verilebilir. 3. Yapay zeka yazlmlar nsann dnsel ya da renmeye ynelik davranlarn taklit eden 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 canlandrma (animasyon) yazlmlar verilebilir. Bu yazlmlar arlkl olarak bilgisayarn grafik arabirimini kullanr. 5. Simlasyon yazlmlar Bir sistemi bilgisayar ortamnda simle etmek iin kullanlan yazlmlardr. 6. Sistem yazlmlar Bilgisayarn elektronik yapsn yneten yazlmlardr. Derleyiciler, haberleme programlar, iletim sistemleri birer sistem yazlmdr. rnein bir metin ileme program da bir sistem yazlmdr. Uygulama programlarna gre daha dk dzeyli ilem yaparlar.

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 farkl klan zellikler neler olabilir? Bir programlama dilini tanmlamak gerekirse hangi niteleyiciler kullanlabilir? Bu sorulara yant verebilmek iin deerlendirme yapmaya olanak salayan ltler olmaldr. Aada bu ltler ksaca inceleniyor:

Programlama Dillerinin Deerleme ltleri

3/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir programlama dilinin seviyesi (level) o programlama dilinin insan algsna ya da makineye yaknlnn lsdr. Bir programlama dili insan alglasna ne kadar yaknsa o kadar yksek seviyeli (high level) demektir. Yine bir programlama dili bilgisayarn elektronik yapsna ve alma biimine ne kadar yaknsa o kadar dk seviyeli (low level) demektir. Bilgisayarn ilemcisinin anlad bir "komut takm" (instruction set) vardr. lemci yalnzca kendi komut takm iinde yer alan komutlar altrabilir. Programlama dilinde yazlan metin, baz sreler sonunda bilgisayarn ilemcisinin komut takmnda yer alan komutlara dntrlr. Yksek seviyeli dillerle almak programc asndan kolaydr, iyi bir algoritma bilgisi gerektirmez. Bu dillerde yalnzca nelerin yaplaca programa bildirilir ama nasl yaplaca bildirilmez. Genel olarak programlama dilinin dzeyi ykseldike, o dilin renilmesi ve o dilde program yazlmas kolaylar. Makine dili bilgisayarn doal dilidir, bilgisayarn donanmsal tasarmna baldr. Bilgisayarlarn gelitirilmesiyle birlikte onlara i yaptrmak iin kullanlan ilk diller, makine dilleri olmutur. Bu yzden makine dillerine birinci kuak diller de denir. Makine dilinin programlarda kullanlmasnda karlalan iki temel sorun vardr. Makine dilinde yazlan kodlar dorudan makinenin ilemcisine, donanm paralarna verilen komutlardr. Deiik bir CPU kullanldnda ya da bellek dzenlemesi farkl bir ekilde yapldnda artk program almaz, programn yeniden yazlmas gerekir. nk makine dili yalnzca belirli bir CPU ya da CPU serisine uygulanabilir. Makine dili tanabilir (portable) deildir. Dier nemli bir sorun 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 dilinde yazlan programlar gelitirmek, 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 iinde makine dilinden, insan alglamasna ok yakn yksek seviyeli dillere (4. kuak diller) kadar uzanan bir sre sz konusudur. 1950'li yllarn hemen balarnda makine dili kullanmnn getirdii sorunlar ortadan kaldrmaya ynelik almalar younlat. Bu yllarda makine dillerinde yazlan programlar bilgisayarn ok snrl olan belleine ykleniyor, byle altrlyordu. lk nce makine dilinin alglanma ve anlalma zorluunu ksmen de olsa ortadan kaldran bir adm atld. Simgesel makine dilleri gelitirildi. Simgesel makine dilleri (Assembly Languages) makine komutlarndan birka tanesini paketleyen baz ksaltma szcklerden, komutlardan oluuyordu. Simgesel makine dillerinin kullanm ksa srede yaygnlat. Ancak simgesel 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 simgesel dilin komutlar, bilgisayar tarafndan komut komut makine diline evriliyor ve oluan makine kodu altrlyordu. Yani bilgisayar, program alma aamasnda nce yorumlayarak makine diline eviriyor, daha sonra makine diline evrilmi komutlar yrtyordu. Programlar bu ekilde altrldnda 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 yazlan programlara gerek program denebileceini sylyorlard. Bu sorunun da stesinden gelindi. O zamanlar iin ok parlak kabul edilebilecek fikir uydu: Kodun her altrlmasnda yazlan kod makine diline evrileceine, gelitirilecek bir baka program simgesel dilde yazlan kodu bir kez makine diline evirsin ve artk program ne zaman altrlmak istense, bilgisayar, yorumlama olmakszn yalnzca makine kodunu altrsn. Bu fikri Grace Hopper gelitirmiti. Grace Hopper'n buluuna

Seviye

4/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

"compiler" derleyici ismi verildi. (Grace Hopper ayn zamanda Cobol dilini gelitiren ekipten biridir, bug(bcek) szcn ilk olarak Grace Hopper kullanmtr.) Artk programclar, simgesel szcklerden oluan Assembly programlama dillerini kullanyor, yazdklar programlar derleyici tarafndan makine koduna dntrlyor ve makine kodu eski hzndan bir ey yitirmeksizin tam hzla alyordu. Assembly diller 2. kuak diller olarak tarihte yerini ald. Assembly dillerinin kullanlmaya balamasyla bilgisayar kullanm hzla artt. Ancak en basit ilemlerin bile bilgisayara yaptrlmas iin birok komut gerekmesi, programlama srecini hzlandrma ve kolaylatrma araylarn balatt, bunun sonucunda da daha yksek seviyeli programlama dilleri gelitirilmeye baland. Tarihsel sre iinde Assembly dillerinden daha sonra gelitirilmi ve daha yksek seviyeli diller 3. kuak diller saylr. Bu dillerin hepsi algoritmik dillerdir. Bugne kadar gelitirilmi olan yzlerce yksek seviyeli programlama dilinden pek az bugne kadar varlklarn srdrebilmitir. 3. kuak dillerin hemen hemen hepsi ana dilden tretilmitir. 3. kuak dillerin ilkleri olan bu dil halen varlklarn srdrmektedir: FORTRAN dili (FORmula TRANslator) karmak matematiksel hesaplamalar gerektiren mhendislik ve bilimsel uygulamalarda kullanlmak zere 1954 - 1957 yllar arasnda IBM firmas iin John Backus tarafndan gelitirildi. FORTRAN dili, youn matematik hesaplamalarn gerektii bilimsel uygulamalarda halen kullanlmaktadr. FORTRAN dilinin FORTRAN IV ve FORTRAN 77 olmak zere iki nemli srm vardr. Doksanl yllarn balarnda FORTRAN - 90 isimli bir srm iin ISO ve ANSI standartlar kabul edilmitir. FORTRAN dili, 3. kuak dillerin en eskisi kabul edilir. COBOL (COmmon Business Oriented Language) 1959 ylnda, Amerika'daki bilgisayar reticileri, zel sektr ve devlet sektrndeki bilgisayar kullanclarndan oluan bir grup tarafndan gelitirildi. COBOL'un gelitirilme amac, veri ynetiminin gerektii ticari uygulamalarda kullanlacak tanabilir bir programlama dili kullanmakt. COBOL dili de halen yaygn olarak kullanlyor. ALGOL (The ALGOritmick Language) 1958 ylnda Avrupa'da bir konsorsiyum tarafndan gelitirildi. IBM Firmas FORTRAN dilini kendi donanmlarnda kullanlacak ortak programlama dili olarak benimsediinden, Avrupa'llar da seenek bir dil gelitirmek istemilerdi. ALGOL dilinde gelitirilen birok tasarm zellii, modern programlama dillerinin hepsinde kullanlmaktadr. 60'l yllarn balarnda programlama dilleri zerinde yaplan almalar yapsal programlama kavramn gndeme getirdi. PASCAL dili 1971 ylnda akademik evrelere yapsal programlama kavramn tantmak iin Profesr Niclaus Wirth tarafndan gelitirildi. Dilin yaratcs, dile matematiki ve filozof Blaise Pascal'n ismini vermitir. Bu dil, ksa zaman iinde niversitelerde kullanlan programlama dili durumuna geldi. Pascal dilinin ticari ve endstriyel uygulamalar desteklemek iin sahip olmas gereken bir takm zelliklerden yoksun olmas, bu dilin kullanmn kstlamtr. Modula ve Modula-2 dilleri Pascal dili temel alnarak gelitirilmitir. BASIC dili 1960'l yllarn ortalarnda John Kemeney ve Thomas Kurtz tarafndan gelitirildi. BASIC isminin "Beginner's All Purpose Symbolic Instruction Code" szcklerinin ba harflerinden oluturulduu sylenir. Yksek seviyeli dillerin en eski ve en basit olanlarndan biridir. Tm basitliine karn, birok ticari uygulamada kullanlmtr. BASIC dili de ANSI tarafndan standartlatrlmtr. Ancak BASIC dilinin ek zellikler ieren srmleri sz konusudur. rnein Microsoft firmasnn kartt Visual Basic diline nesne ynelimli programlamaya ilikin birok zellik eklendi. Daha sonra bu dil Visual Basic dot Net ismini ald. Ayrca BASIC dilinin baz srmleri uygulama programlarnda -rnein MS Excel ve MS Word programlarnda- kullancnn zelletirme ve otomatikletirme amacyla yazaca makrolarn yazlmasnda kullanlan programlama dili olarak da genel kabul grd. ADA dili ise Amerikan Savunma Departman (Department of Defence -DoD) destei ile 70'li yllardan balanarak gelitirildi. DoD, dnyadaki en byk bilgisayar kullanclarndan biridir. Bu kurum farkl yazlmsal gereksinimleri karlamak iin ok sayda farkl programlama dili kullanyordu ve tm gereksinmelerini karlayacak bir dil arayna girdi. Dilin tasarlanmas amacyla uluslararas bir yarma dzenledi. Yarmay kazanan irket (CII-Honeywell Bull of France) Pascal dilini temel alarak balatt

5/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

almalarnn sonucunda Ada dilini gelitirdi. Ada dilinin dokmanlar 1983 ylnda yaymland. Ada ismi, dnr Lord Byron'un kz olan Lady Ada Lovelace'n ismine gndermedir. Ada Lovelace delikli kartlar hesap makinelerinde ilk olarak kullanlan Charles Babbage'in yardmcsyd. Charles Babbage hayat boyunca "Fark Makinesi" (Difference Engine) ve "Analitik Makine" (Analytical Engine) isimli makinelerin yapm zerinde alt ama bu projelerini gerekletiremeden ld. Yine de gelitirdii tasarmlar modern bilgisayarlarn atas kabul edilir. Ada Lovelace, Charles Babbage'n makinesi iin delikli kartlar ve kullanlacak algoritmalar hazrlyordu. Lovelace'in 1800'l yllarn banda ilk bilgisayar programn yazd kabul edilir. Ada genel amal bir dildir, ticari uygulamalardan roketlerin ynlendirilmesine kadar birok farkl alanda kullanlmaktadr. Dilin nemli zelliklerinden biri, gerek zaman uygulamalarna (real-time applications / embedded systems) destek vermesidir. Baka bir zellii de yksek modler yaps nedeniyle byk programlarn yazmn kolaylatrmasdr. Ancak byk, karmak derleyicilere gereksinim duymas; C, Modula-2 ve C++ dillerine kar rekabetini zorlatrmtr. ok yksek seviyeli ve genellikle algoritmik yap iermeyen programlarn grsel bir ortamda yazld diller ise 4. kuak diller olarak isimlendirilirler. Genellikle 4GL (fourth generation language) olarak ksaltlrlar. nsan algsna en yakn dillerdir. RPG dili 4. kuak dillerin ilki olarak kabul edilebilir. zellikle kk IBM makinelerinin kullanclar olan irketlerin, rapor retimi iin kolay bir dil istemeleri zerine IBM firmas tarafndan gelitirilmitir. Programlama dilleri dzeylerine gre baz gruplara ayrlabilir: ok yksek dzeyli diller ya da grsel diller ya da ortamlar (visual languages): Access, Foxpro, Paradox, Xbase, Visual Basic, Oracle Forms. Yksek dzeyli diller. Fortran, Pascal, Basic, Cobol. Orta dzeyli programlama dilleri: Ada, C. (Orta seviyeli diller daha az kaypla makine diline evrilebildiinden daha hzl alr.) Dk dzeyli programlama dilleri: Simgesel makine dili (Assembly language). Makine dili: En aa seviyeli programlama dili. Saf makine dili tamamen 1 ve 0 lardan oluur.

Okunabilirlik (readability) kaynak kodun abuk ve iyi bir biimde alglanabilmesi anlamna gelen bir terimdir. Kaynak kodun okunabilirlii sz konusu olduunda sorumluluk byk lde program yazan programcdadr. Fakat yine verimlilikte olduu gibi dillerin bir ksmnda okunabilirlii glendiren yap ve aralar bulunduu iin bu zellik bir lde programlama dilinin tasarmna da baldr. En iyi program kodu, sanld gibi "en zekice yazlm fakat kimsenin anlayamayaca" kod deildir. Birok durumda iyi programclar okunabilirlii hibir eye feda etmek istemezler. nk okunabilir bir program kolay alglanabilme zelliinden dolay yllar sonra bile gncelletirmeye olanak salar. Birok programcnn ortak kodlar zerinde alt geni kapsaml projelerde okunabilirlik daha da nem kazanr. C'de okunabilirlik en fazla vurgulanan kavramlardan biridir. lerideki birok blmde okunabilirlik konusuna sk sk deinildiini greceksiniz.

Okunabilirlik

6/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Tanabilirlik (portability) bir sistem iin yazlm olan kaynak kodun baka bir sisteme gtrldnde, hatasz bir biimde derlenerek, doru bir ekilde altrlabilmesi demektir. Tanabilirlik standardizasyon anlamna da gelir. Programlama dilleri (ISO International Standard Organization) ve ANSI (American National Standard Institute) tarafndan standardize edilirler. lk olarak 1989 ylnda standartlar oluturulan C Dili, dier programlama dillerinden daha tanabilir bir programlama dilidir.

Tanabilirlik

Verimlilik (Efficiency) bir programn hzl almas ve daha az bellek kullanma zelliidir. Programn alma hz ve kulland bellek miktar pek ok etkene baldr. phesiz kullanlan algoritmann da hz ve kullanlan bellek zerinde etkisi vardr. Programn altrld bilgisayarn da doal olarak hz zerinde etkisi vardr. Verimlilik bir programlama dilinde yazlm bir programn altrldnda kulland bellek alan ve alma hz ile ilgili bir kstas olarak ele alnabilir. Verimlilik zerinde rol oynayabilecek dier etkenler sabit brakldnda, kullanlan programlama dilinin tasarmnn da verim zerinde etkili olduu sylenebilir. Bu adan bakldnda C verimli bir dildir.

Verimlilik

Baz diller zel bir uygulama alan iin tasarlanrlar. Sistem programlama Yapay zeka uygulamalar, simlasyon uygulamalar, veritaban sorgulamalar, oyun programlarnn yazm amacyla tasarlanan ve kullanlan programlama dilleri vardr. Baz diller ise daha geni bir kullanm alanna sahiptir. rnein veritaban sorgulamalarnda kullanlmak zere tasarlanan bir dil mhendislik uygulamalarnda da kullanlabilir. C dili de bir sistem programlama dili olarak domasna karn, gl yapsndan dolay ksa bir sre iinde genel amal bir dil haline gelmitir. Oysa PASCAL, BASIC ok daha genel amal dillerdir. C ana uygulama alan "sistem programcl" olan bir dildir. Ancak neredeyse tm uygulama alanlar iin C dilinde programlar yazlmtr.

Kullanm Alan

Uygulama Alanlarna Gre Snflandrma

Programlama dillerini uygulama alanlarna gre de gruplayabiliriz:

1. Bilimsel ve mhendislik uygulama dilleri: Pascal, C, FORTRAN. C Programlama dili niversitelerdeki akademik almalarda da youn olarak kullanlr. 2. Veri taban dilleri: XBASE, (Foxpro, Dbase, CA-Clipper), Oracle Forms, Visual Foxpro. 3. Genel amal programlama dilleri: Pascal, C, Basic. 4. Yapay zeka dilleri: Prolog, Lisp . 5. Simlasyon dilleri GPSS, Simula 67 6. Makro Dilleri (Scripting languages) awk, Perl, Python, Tcl, JavaScript. 7. Sistem programlama dilleri: Simgesel makine dilleri, BCPL, C, C++, occam. Gnmzde sistem yazlmlarn neredeyse tamamnn C dili ile yazldn syleyebiliriz.

7/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

rnek vermek gerekirse UNIX iletim sisteminin % 80'i C dili ile geri kalan ise simgesel makine dili ile yazlmtr. Bu iletim sistemi ilk olarak BELL Laboratuarlarnda oluturulmutur. Kaynak kodlar gizli tutulmam, bylece eitli kollardan gelitirilmesi mmkn olmutur. Daha sonra gelitirilen UNIX bazl iletim sistemi uygulamalarna deiik isimler verilmitir. C bilimsel ve mhendislik alanlarna kullanlabilen genel amal bir sistem programlama dilidir.

Bir btn olarak zlmesi zor olan problemlerin paralara ayrlmas ve bu paralarn ayr ayr zlmesinden sonra paralar arasndaki balantnn salanmas programlamada sk bavurulan bir yntemdir. Bir programlama dili buna olanak salayan aralara sahipse programlama dilinin alt programlama yetenei vardr denilebilir. Alt programlama yetenei bir programlama dilinin, program paralar halinde yazmay desteklemesi anlamna gelir. Alt programlama Yapsal Programlama Teknii'nin de ayrlmaz bir parasdr. Alt programlamann getirdii baz nemli faydalar vardr. Alt programlar kaynak kodun klmesini salar. ok yinelenen ilemlerin alt programlar kullanlarak yazlmas alabilir programn kodunu kltr. nk alt programlar yalnzca bir kere, alabilir kod iine yazlr. Program kodu alt programn olduu yere atlatlarak bu blgenin defalarca altrlmas salanabilir. Alt programlama alglamay kolaylatrr, okunabilirlii artrr, ayn zamanda kaynak kodun test edilmesini kolaylatrr, kaynak kodun daha kolay gncelletirilmesini salar. Alt programlamann en nemli faydalarndan biri de oluturulan alt programlarn birden fazla projede kullanlabilmesidir (reusability). C alt programlama yetenei yksek bir dildir. C'de alt programlara ilev (function) denir. levler C dilinin yap talardr.

Alt Programlama Yetenei

Her programlama dilini renmenin ve renilen programlama dilinde uygulama gelitirebilmenin zorluk derecesi ayn deildir. Genel olarak programlama dillerinin dzeyi ykseldike, bu programlama dilini renme ve bakalarna retme kolaylar. Bugn yaygn olarak kullanlan yksek dzeyli programl dillerinin bu derece tutulmasnn nemli bir nedeni de bu dillerin ok kolay renilebilmesidir. Ancak yksek dzeyli dilleri renerek yetkin bir beceri dzeyi kazanmakta da ou zaman baka zorluklar vardr. Byle diller ok sayda hazr arac barndrrlar. rnein yksek dzeyli bir programlama ortamnda, GUI'ye ilikin hazr bir men ubuunun zelliklerini deitirmeye ynelik onlarca seenek sunulmu olabilir. Bu durumda programc, her bir seenein anlamn renmek durumunda kalr. Yani baz durumlarda programlama dilinin seviyesinin ykselmesi programc asndan bir algsal kolaylk getirmekle birlikte, programcya zellikle hazr aralara ynelik bir renme yk getirir.

renme ve retme Kolayl

Programlamann tarihsel geliim sreci iinde, baz programlama teknikleri (paradigmalar) ortaya kmtr. Programlamann ilk dnemlerinde yazlan programlarn boyutlar ok kk olduundan, program yazarken zel bir teknik kullanmaya pek gerek kalmyordu. nk bir program tek bir programcnn her ynyle stesinden gelebilecei kadar kkt ve basitti. Bir programda deiiklik yapmann ya da bir programa ekleme yapmann ciddi bir maliyeti yoktu. Bilgisayar donanmndaki teknik gelimeler, insanlarn bilgisayar programlarndan beklentilerinin artmas, bilgisayar programlarn ounlukla grsel arayzler kullanmalar, program boyutlarnn giderek bymesine yol at. Programlarn bymesiyle birlikte, program yazmaya ynelik farkl teknikler, yntemler gelitirildi. Bu tekniklere rnek olarak, "prosedrel programlama", "modler programlama", "nesne tabanl programlama", "nesneye ynelmi programlama", "trden bamsz programlama" verilebilir. 8/529

Programlama Tekniklerine Verilen Destekler

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

"Prosedrel programlama" ile "yapsal programlama" ou zaman ayn anlamda kullanlr. Yapsal programlama bir programlama tekniidir. Bugn artk hemen hemen btn programlama dilleri yapsal programlamay az ok destekleyecek ekilde tasarlanmaktadr. Yapsal programlama fikri 1960'l yllarda gelitirilmitir. Yapsal programlama teknii drt ana ilke zerine kuruludur: 1. Bl ve stesinden gel (divide and conquer) Tek bir btn olarak yazlmas zor olan programlar, daha kk ve stesinden daha kolay gelinebilecek paralara blnrler. Yani program, blnebilecek kk paralarna ayrlr (functional decomposition). Bu paralar alt program, ilev, prosedr, vs. olarak isimlendirilir. Alt programlamann salad faydalar daha nce aklanmt. 2. Veri gizleme (Data hiding) Yapsal programlama tekniinde, programn dier paralarndan ulalamayan, yalnzca belli bir bilinirlik alan olan, yani kodun yalnzca belli bir ksmnda kullanlacak deikenler tanmlanabilir. Bu tr deikenler genel olarak "yerel deikenler" (local variables) olarak isimlendirilirler. Deikenlerin bilinirlik alanlarnn kstlanabilmesi hata yapma riskini azaltt gibi, programlarn daha kolay deitirilebilmesini, program paralarnn baka programlarda tekrar kullanabilmesini de salar. Alt programlarn, daha geni ekliyle modllerin, bir ii nasl yapt bilgisi, o alt programn ya da modln kullancsndan gizlenir. Kullanc (client) iin 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 ara programn yukardan aa olarak ak ile uyum halindedir. Program paralarna ancak tek bir noktadan girilebilir. 4. Dngler, dier kontrol yaplar Yapsal programlama tekniinde dngler ve dier kontrol deyimleri sklkla kullanlr. Artk kullanmda olan hemen hemen btn programlama dilleri az ya da ok yapsal programlama tekniini destekler. Nesneye ynelimli programlama Nesneye ynelimlilik (object orientation) de bir programlama tekniidir. Yapsal programlama teknii 1960l yllarda gndeme gelmiken, nesneye ynelimli programlama teknii 1980'li yllarda yaygnlamtr. Bu teknik, kaynak kodlarn ok bymesi sonucunda ortaya kan gereksinim yznden gelitirilmitir. C dilinin tasarland yllarda, akla gelebilecek en byk programlarn bile kaynak kodlar ancak birka bin satrd. Bilgisayar donanmndaki gelimeler, kullanclarn bilgisayar programlarndan beklentilerinin artmas ve grafik arayznn etkin olarak kullanlmasyla, bilgisayar programlarnn boyutu ok byd. Kullanmda olan birok programn bykl yzbin satrlarla hatta milyon satrlarla llmektedir. Nesneye ynelmi programlama teknii, hereyden nce byk programlarn daha iyi yazlmas iin tasarlanm bir tekniktir. C dilinin yaratld yllarda byle bir tekniin ortaya kmas sz konusu deildi, nk programlar bugnk llere gre zaten ok kkt. Nesneye ynelmi programlama tekniinde, programa ilikin veri ile bu veriyi ileyen kod, nesne isimli bir birim altnda birletirilir. Yani bu tekniin yap talar nesnelerdir. Bu tekniin prosedrel programlamada en nemli fark, programcnn programlama dilinin dzleminde deil de dorudan problemin kendi dzleminde dnmesi, program kurgulamasdr. Bu da gerek hayata ilikin bir problemin yazlmda ok daha iyi modellenmesini salar. Nesne ynelimli programlama tekniinin yaygn olarak kullanlmaya balanmasyla birlikte birok programlama dilinin bnyesine bu tekniin uygulanmasn kolaylatrc aralar eklenerek, yeni srmleri oluturulmutur. rnein C++ dili, C diline nesne ynelimli programlama tekniini uygulayabilmek iin baz eklemelerin yaplmasyla gelitirilmitir.

9/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Benzer amala Pascal diline eklemeler yaplarak Delphi dili, Cobol dilinin yenilenmesiyle Oocobol, ada dilinin yenilenmesiyle ise ada 95 dilleri gelitirilmitir. Baz programlama dilleri ise dorudan nesneye ynelmi programlama tekniini destekleyecek ekilde tasarlanarak gelitirilmitir. Byle dillere saf nesne ynelimli diller de denir. rnein Java, Eiffel, C#, saf nesne ynelimli dillerdir. zetle, bir programlama dili hakknda sorulacak sorulardan belki de en nemlilerinden biri, o programlama dilinin belirli bir programlama tekniini destekleyen aralara sahip olup olmaddr. C dili, var olan aralaryla prosedrel programlama tekniine tam destek veren bir dildir.

Sral, indeksli ve rastgele dosyalara erime, veritaban kaytlarn geri alma, gncelletirme ve sorgulama yeteneidir. Veritaban programlama dillerinin (DBASE, PARADOX vs.) bu yetenekleri, dierlerinden daha stndr. Bu dillerin en tipik zelliklerini oluturur. Fakat C giri k kolayl gl olmayan bir dildir. C'de veri tabanlarnn ynetimi iin zel ktphanelerin kullanlmas gerekir.

Giri / k Kolayl

ncelenen kstaslardan sonra C dili belirli bir yere oturtulabilir: C orta seviyeli bir programlama dilidir. Dier yapsal programlama dillerine gre C dilinin seviyesi daha dktr. C dili hem yksek seviyeli dillerin kontrol deyimleri, veri yaplar gibi avantajlarna sahipken hem de bitsel ileler gibi makine kodu deyimlerini yanstan ilelere sahiptir. Yani C dili hem makinenin algsna hem de insann alglamasna yakn bir dildir. C makineye yeterince yakndr ama programcya da uzak deildir. Tercih edilmesinin ana nedenlerinden biri budur. Bir program C dili kullanarak yazmak, ayn program makine dilinde yazmaya gre ok daha kolay olmasna karn, C'de yazlm bir programn verimi ayn oranda dmez. C dili verim asndan bakldnda birok uygulama iin, dorudan makine diline tercih edilebilir. Makina dili yerine C dilinde programlama yaplmas oluturulan programn verimini ok drmez. C dilinin ana uygulama alan "Sistem programlama" dr. Sistem programlama ne demektir? Donanmn ynetilmesi, ynlendirilmesi ve denetimi iin yazlan, dorudan makinenin donanmla ilikiye giren programlara sistem program denir. rnein, iletim sistemleri, derleyiciler, yorumlayclar, aygt srcleri (device drivers), bilgisayarlarn iletiimine ilikin programlar, otomasyon programlar, sistem programlardr. C'den nce sistem programlar assembly dillerle yazlyordu. Gnmzde sistem programlarnn yazlmasnda C dilinin neredeyse tek seenek olduu sylenebilir. Bugn cep telefonlarndan uaklara kadar her yerde C kodlar alyor. C algoritmik bir dildir. C dilinde program yazmak iin yalnzca dilin szdizimini ve anlamsal 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. C ifade gc yksek, okunabilirlik zellii gl bir dildir. C dilinde yazlan bir metnin okunabilirliinin yksek olmas szel bir dil olmasndan, insann kulland dile yakn bir dil olmasndan deildir. C ok esnek bir dildir. Dier dillerde olduu gibi programcya kstlamalar getirmez. Makinann olanaklarn programcya daha iyi yanstr. C gl bir dildir, ok iyi bir biimde tasarlanmtr. C'ye ilikin ilelerin ve yaplarn bir ou daha sonra baka programlama dilleri tarafndan da benimsenmitir. C verimli bir dildir. C de yazlan programlar dilin dzeyinin dk olmas nedeniyle hzl alr. Verimlilik konusunda assembly diller ile rekabet edebilir. C doal bir dildir. C bilgisayar sistemi ile uyum iindedir. C kk bir dildir. Yeni sistemler iin derleyici yazmak zor deildir. C nin standart bir ktphanesi vardr. Bu ktphane ile sk yaplan ilemler iin ortak bir arayz salanmtr. C'nin eitimi dier bilgisayar dillerine gre daha zordur.

C Nasl Bir Programlama Dilidir

10/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C dili UNIX iletim sistemi ile btnleme iindedir. UNIX iletim sisteminde kullanlan baz aralar kullancnn C dilini bildiini varsayar. Dier tm bilgisayar programlama dillerinde olduu gibi C dilinin de zayf taraflar vardr. Esnek ve gl bir dil olmas programcnn hata yapma riskini artrr. C dilinde yazlan kodlarda yaplan yanllklarn bulunmas dier dillere gre daha zor olabilir.

C dilinin UNIX iletim sisteminin bir yan rn olarak doduu sylenebilir. nce Unix iletim sisteminin tarihine deinelim: 1965 ylnda MITde MAC isimli bir proje gerekletirildi. MAC bir bilgisayar sisteminin zaman paylamn salayan ilk projelerden biriydi. Bu proje ile ayn bilgisayar 30 a kadar kullanc tarafndan paylalabiliyordu 160 ayr yazcy kullanmak da mmknd. Bu projenin baarsndan cesaret alan MIT, General Electric ve Bell Laboratuarlar ile bir ortak giriim oluturararak zaman paylaml yeni bir sistem oluturma almasna balad. Bu projeye MULTICS (Multiplexed Information and Computing Service) ismi verildi. Bell Laboratuarlar projenin yazlm ksmndan sorumluydu. 1969 ylnda Bell Laboratuarlar proje sresinin uzamas ve proje maliyetinin yksek olmas nedeniyle projeden ayrld. 1969 ylnda Bell Laboratuarlarnda Ken Thompson nclnde bir grup yeni bir alternatif arayna girdi. MULTICS projesinde alan Ken Thompson ve ekip arkada Dennis Ritchie bu konuda bir hayli deneyim kazanmt. Thompson ve ekibi, insan ve makine arasndaki iletiimi kolaylatracak bir iletim sistemi tasarmna giriti. letim sisteminin omurgas yazldnda MULTICS'e gnderme yaplarak iletim sistemine Unix ismi verildi. Bu yllarda programclar PL/1, BCPL gibi yksek seviyeli dilleri kullanyorlard. Thompson DEC firmasnn ana bellei yalnzca 8K olan PDP 7 isimli bilgisayar zerinde alyordu. Thompson 1960 l yllarda Martin Richards tarafndan gelitirilen BCPL dilini kullanmakta deneyimliydi. Kendi dilini tasarlarken Thompson, 1960 yllarnn ortalarnda Martin Richards tarafndan gelitirilmi BCPL dilinden yola kt. (BCPL = Business Common Programming Language). Bu dil de CPL = Cambridge Programming Language'den tretilmitir. CPL'in kayna da tm zamanlarn en eski ve en etkili dillerinden biri olan ALGOL 60'dr. ALGOL 60, Pascal, ADA, Modula2 dillerinin de atasdr. Bu dillere bu yzden C dilinin kuzenleri de diyebiliriz. Aada ALGOL 60 dil ailesi grlyor:

C Programlama Dilinin Tarihi

11/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Thompson gelitirdii bu dilin ismini B koydu. Dennis Ritchie, UNIX projesine katlnca B dilinde programlamaya balad. B dili daha da gelitirilmiti ve artk daha yeni bir teknoloji olan Dec PDP-11 bilgisayarlarda alyordu. Thompson, UNIX iletim sisteminin bir ksmn B dilinde yeniden yazd. 1971 ylna gelindiinde B dilinin PDP-11 bilgisayarlar ve UNIX iletim sisteminin gelitirilmesi iin ok uygun olmad iyice ortaya kt. Bu yzden Ritchie, B programlama dilinin daha ileri bir srmn gelitirmeye balad. Oluturduu dili ilk nce NB (new B) olarak isimlendirdi. Ama gelitirdii dil B dilinden iyice kopmaya ve ayr bir karakter gstermeye balaynca dilin ismini de C olarak deitirdi. 1973 ylnda UNIX iletim sisteminin byk bir ksm C dili ile yeniden yazld. Ken Thompson ve Dennis Ritchie Unix letim Sistemi zerinde alrken (Yl: 1972) C'nin evrimi ve gelimesi 70'li yllarda da srd. Ancak uzun bir sre C dili dar bir evrede kullanld. Geni kitleler tarafndan tannmas ve kullanlmaya balamas 1978 ylnda Dennis Ritchie ve Brian Kernighan tarafndan yazlan "The C Programming Language" kitab ile olmutur. Bu kitap ayn zamanda yazlm konusunda yazlan en iyi eserlerden biri olarak deerlendirilmektedir. C standartlarnn oluturulmasna kadar olan dnemde bu kitap ounluun benimsedii genel kabul gren gayri resmi (de facto) bir standart grevi de grmtr. Bu kitap ksaca K&R (Kernighan & Ritchie) olarak isimlendirilmektedir. 1970'li yllarda C programclarnn says azd ve bunlardan ou UNIX kullanclaryd. Ama artk 80'li yllar gelince C'nin kullanm UNIX snrlarn at, farkl iletim sistemleri iin alan derleyiciler piyasaya kt. C dili de IBM PC'lerde youn olarak kullanlmaya balad. C'nin hzl bir biimde yaygnlamas baz sorunlar da beraberinde getirdi. Derleyici yazan firmalar, referans olarak Ritchie ve Kernighan'n kitabn esas alyorlard ama sz konusu kitapta baz noktalar ok da ayrntl bir biimde aklanmamt. zellikle hangi noktalarn C dilinin bir zellii hangi noktalarn ise UNIX iletim sisteminin bir zellii olduu o kadar ak olmad iin bir takm karklklar ortaya kyordu. Bylece derleyici yazanlarn rnlerinde de farkllklar ortaya kyordu. Ayrca kitabn yaynlanmasndan sonra da dilde bir takm gelitirmeler, iyiletirmeler, deiiklikler yapld iin, birbirinden ok farkl derleyiciler piyasada kullanlmaya balanmt.

Hangi C

C tek bir programlama dili olmasna karn C'nin farkl srmlerinden sz etmek olasdr:

C dili ilk olarak 1978 ylnda yaymlanan Dennis Ritchie ve Brian Kernighan tarafndan yazlm "The C Programming Language" isimli kitapta anlatlmt. 1980'li yllarda C dili bu kitapta anlatlan genel geer kurallara gre kullanld, derleyici yazan firmalar bu kurallar esas aldlar. Bu dnem iinde kullanlan derleyiciler arasnda baz kural ve yorum farkllklar olsa da ana kurallar zerinde bir genel bir uzlama olduu sylenebilir. C dilinin standartlatrlmas srecine kadar kullanlan bu srme "Geleneksel C (Traditional

Geleneksel C

12/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C)" ya da "Klasik C (Classic C)" denmektedir. phesiz C dilinin bu dneminde derleyici yazan firmalar uzlalm kurallarn dnda kendi zel eklentilerini de dile katmaktaydlar.

C dilinin standart hale getirilmesinin dilin ticari kullanmnn yaygnlamasna yardmc olacan dnen Amerikan Ulusal Standartlar Enstits (ANSI) C dili ve ktphanesi iin bir standart oluturmas amacyla 1982 ylnda bir komite oluturdu. Jim Brody bakanlnda alan ve X3J11 numarasyla anlan bu komitenin oluturduu standartlar 1989 ylnda kabul edilerek onayland. Bu standardn resmi ismi American National Standard X3.159-1989 olmakla birlikte ksaca ANSI C diye anlmaktadr. ANSI C almas tamamlandnda bu kez uluslararas bir standart oluturmak amacyla P.J.Plaguer bakanlnda oluturulan bir grup (ISO/IEC JTC1/SC22/WG14) ANSI standartlarn uluslararas bir standarda dntrd. Baz kk biimsel deiiklikler yaplarak oluturulan bu standardn resmi ismi ISO/IEC 9899:1990' dr. Bu standart daha sonra ANSI tarafndan da kabul edilmitir. Ortak kabul gren bu standarda Standard C (1989) ya da ksaca C89 denmektedir. Geleneksel C' den C89' a geite nemli deiiklikler olmutur.

Standart C (1989)

1995 ylnda Wg14 komitesi C89 standartlar zerinde iki teknik dzeltme ve bir eklenti yaymlad. Bu dzeltme notlar ve eklentiler ok nemli deiiklikler olarak grlmemektedir. Bu dzeltmelerle deitirilmi standartlara "C89 with amendment 1" ya da ksaca C95 denmektedir

Standart C (1995)

ISO/IEC standartlar belirli aralklarla gzden geirilmekte, gncellenmektedir. 1995 ylnda Wg14 kurulu C standartlar zerinde kapsaml deiiklikler ve eklentiler yapmak zere bir alma balatt. alma 1999 ylnda tamamland. Oluturulan yeni standardn resmi ismi ISO/IEC 9899:1999'dur. Ksaca C99 olarak anlmaktadr. Bu standart resmi olarak daha nceki tm srmlerin yerine gemitir. Derleyici yazan firmalar derleyicilerini yava yava da olsa bu standarda uygun hale getirmektedirler. C99 standartlar C89/C95'e birok eklenti salamtr. Bunlardan bazlar Sanal saylarn doal tr olarak eklenmesi Daha byk bir tamsay trnn eklenmesi Deiken uzunlukta diziler Boolean (Mantksal) veri tr ngilizce olmayan karakter setleri iin daha kapsaml destek Gerek say trleri iin daha iyi destek C++ tarz aklama satrlar satr ii (inline) ilevler

Standart C (1999)

C99 ile getirilen deiiklikler ve eklentiler C95'e gre daha fazla ve nemlidir. Ancak C dilinin doal yaps deitirilmemitir.

C++ Dili

C++ dili de 1980 li yllarn balarnda Bjarne Stroustrup tarafndan AT&T Bell Laboratuarlar'nda gelitirildi. C++ dili C'den ayr, tamamyla baka bir programlama dilidir. Dilin tasarmcs Bjarne Stroustoup, C'nin orta seviyeli zelliklerine baz eklemeler yaparak, bata nesne ynelimli programlama teknii olmak zere baka programlama tekniklerini de destekleyen ayr bir dil oluturdu. C++ dili de dinamik bir gelime sreci sonunda 1998 ylnda standart haline getirildi. Oluturulan resmi standardn ismi ISO/IEC 14882:1998'dir. C++ birden fazla programlama tekniini destekleyen (multi-paradigm) bir dildir. Temel szdizimi byk lde C dilinden alnmtr. Ancak szdizim zellikleri birbirine benzese

13/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

de dilin aralarnn kullanlma biimi C'den olduka farkldr. C++, C ile karlatrldnda ok daha byk ve karmak bir dildir. C++, C dilini de kapsayan bir st dil olarak dnlebilir. Eer baz noktalara dikkat edilirse hem C dilinde hem de C++ dilinde geerli olabilecek programlar yazlabilir. C dilinin byle kullanmna "clean C" ("Temiz C") denmektedir.

Hangi C'yi Kullanmal

Bu karar verilirken, uygulamann gelitirilecei alan iin nasl bir C derleyicisinin olduu phesiz nemlidir. Karar verilmesinde bir dier etken de yazlan kodun ne seviyede tanabilir olmasnn istendiidir. Seeneklerin aadakiler olduu dnlebilir: 1. C99. C dilinin son srm. C dilinin tm zelliklerini iermektedir. Ancak baz derleyiciler bu srm henz desteklememektedir. 2. C89. En fazla kullanlan srmdr. Bu srm kullanldnda genellikle C95 eklentileri de kullanlmaktadr. 3. Geleneksel C. Yeni programlarn yazlmasnda artk kullanlmasa da, eskiden yazlan programlarn bakmnda kullanmak gerekebilir. 4. Temiz C (Clean C). Yani C dilinin ayn zamanda C++ diline uyumlu olarak kullanlmas. C99 srm, C89 ve Klasik C ile genel olarak yukarya doru uyumludur. Yani Klasik C programlar ve C89 programlar, C99 dilinin kurallarna gre derlendiinde ya deiiklik yapmak gerekmez ya da ok az deiiklik yapmak gerekir. Bu deiiklikler iin nilemci program (preprocessor) kullanlabilir. Gemie doru uyumlu program yazmaksa ou zaman mmkn deildir.

Objective C - Concurrent C

C dilinin kullanm yaygnlatktan sonra C dilinden baka diller de tretilmitir: Objective C dili, NeXT firmas tarafndan OpenStep iletim sistemi iin uyarlanmtr. NeXT firmas daha sonra Apple firmas tarafndan satn alnmtr. Objective C dilinin Apple firmasnn yeni kuak iletim sistemi olan Rhapasody iin temel gelitirme arac olmas planlanmaktadr. "Concurrent C" dili, Bell Laboratuarlarndan Dr. Narain Gehani ve Dr. William D.Roome tarafndan gelitirilmitir. Daha ok, paralel programlamada kullanlmaktadr.

14/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

SAYI SSTEMLER
Gnlk hayatta onluk say sistemi kullanlr. Onluk say sisteminde bir saynn deeri aslnda her bir basamak deerinin 10 saysnn sleriyle arpmlarndan elde edilen toplam deeridir. rnein: 1273 = (3 * 1) + (7 * 10 ) + (2 * 100) + (1 * 1000) Ancak bilgisayar sistemlerinde btn bilgiler ikilik say sisteminde (binary system) ifade edilir. Genel olarak say sistemi kalksa o say sisteminde o kadar simge bulunur. rnein onluk say sisteminde 10 adet simge vardr: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 Ayn ekilde ikilik say sisteminde yalnzca iki adet simge bulunur. Yani yalnzca 0 ve 1.

kilik sistemde her bir basamaa (digit) 1 bit denir. Bit kelimesi binary digit szcklerinden tretilmitir. rnein 1011 says 4 bittir yani 4 bit uzunluundadr. 11011001 says 8 bittir. 8 bitlik bir byklk bir byte olarak isimlendirilir. Kilo, byklk olarak 1000 kat anlamna gelir. Ancak bilgisayar alannda Kilo, 2'nin 1000'e en yakn ss olan 210 yani 1024 kat olarak kullanlr. Aada daha byk birimlerin listesi veriliyor: 1 1 1 1 1 1 1 1 kilobyte megabyte gigabyte terabyte petabyte exabyte zettabyte yottabyte 1 1 1 1 1 1 1 1 KB MB GB TB PB EB ZB YB 1024 1024 1024 1024 1024 1024 1024 1024 byte KB MB GB TB PB EB ZB 210 220 230 240 250 260 270 280 byte byte byte byte byte byte byte byte

Birimler

zellikle sistem programclnda daha kk birimlere de isim verilir: 4 bit 8 bit 16 bit 32 bit 64 bit 1 1 1 1 1 Nybble byte word double word quadro word

Tamsay deerlerini gstermek iin ikilik say sistemi iki farkl biimde kullanlabilir. Eer yalnzca sfr ve pozitif tamsaylar gsteriliyorsa, bu biimde kullanlan ikilik say sistemine iaretsiz (unsigned) ikilik say sistemi denir. kilik say sisteminin negatif tamsaylar da gsterecek biimde kullanlmasna, iaretli (signed) ikilik say sistemi denir.

kilik Say Sistemine likin Genel lemler

kilik say sisteminde ifade edilen bir saynn onluk say sistemindeki karln hesaplamak iin en sadan balanarak btn basamaklar tek tek ikinin artan sleriyle arplr. rnein:

kilik Say Sisteminden Onluk Say Sistemine Dnm

15/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

1 0 1 1 = 1 * 20 + 1 * 21 + 0 * 22 + 1 * 23 = 11 0010 1001 = (1 * 1) + (1 * 8) + (1 * 32) = 41

MSD ve LSD

kilik say sisteminde yazlan bir saynn en solundaki bit, yukardaki rnekten de grld gibi en yksek saysal deeri katar. Bu bite en yksek anlaml bit (most significant digit) denir. Bu bit iin ounlukla "MSD" ksaltmas kullanlr. kilik say sisteminde yazlan bir saynn en sandaki bit, yine yukardaki rnekten de grld gibi en dk saysal deeri katar. Bu bite en dk anlaml bit (least significant digit) denir. Bu bit iin ounlukla LSD ksaltmas kullanlr. rnek: 0101 1101 says iin MSD = 0 LSD = 1 kilik say sisteminde yazlan bir saynn belirli bir bitinden sz edildiinde, hangi bitten sz edildiinin doru bir ekilde anlalmas iin basamaklar numaralandrlr. 8 bitlik bir say iin, saynn en sandaki bit yani (LSD) saynn 0. bitidir. Saynn en solundaki bit (yani MSD) saynn 7. bitidir.

Onluk Say Sisteminden kilik Say Sistemine Dnm

Say srekli olarak ikiye blnr. Her blmden kalan deer (yani 1 ya da 0) oluturulacak saynn sfrnc bitinden balanarak, basamaklarn oluturur. Bu ilem 0 deeri elde edilinceye kadar srdrlr. rnek olarak 87 says ikilik say sisteminde ifade edilmek istensin: 87 / 2 = 43 (kalan 1) Saynn 0. biti 1 43 / 2 = 21 (kalan 1) Saynn 1. biti 1 21 / 2 = 10 (kalan 1) Saynn 2. biti 1 10 / 2 = 5 (kalan 0) Saynn 3. biti 0 5 / 2 = 2 (kalan 1) Saynn 4. biti 1 2 / 2 = 1 (kalan 0) Saynn 5. biti 0 1 / 2 = 0 (kalan 1) Saynn 6. biti 1 87 = 0101 0111 kinci bir yntem ise, onluk say sisteminde ifade edilen saydan srekli olarak ikinin en byk ssn karmaktr. karlan her bir s iin ilgili basamaa 1 deeri yazlr. Bu ilem 0 says elde edilene kadar srdrlr. Yine 87 says ikilik say sisteminde ifade edilmek istensin: 87 - 64 23 - 16 7 - 4 3 - 2 1 - 1 = = = = = 23 7 3 1 0 0100 0101 0101 0101 0101 0000 0000 0100 0110 0111

87 = 0101 0111

Bire tmleyen (one's complement) saynn tm bitlerinin tersinin alnmasyla elde edilen saydr. Yani bire tmleyen, saydaki 1 bitlerinin 0, 0 bitlerinin 1 yaplmasyla elde edilir. Bir saynn bire tmleyeninin bire tmleyeni saynn yine kendisidir.

Bire Tmleyen

kiye Tmleyen

Bir saynn bire tmleyeninin 1 fazlas saynn ikiye tmleyenidir (two's complement).

16/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yani nce saynn bire tmleyeni yukardaki gibi bulunur. Daha sonra elde edilen sayya 1 eklenirse saynn ikiye tmleyeni bulunmu olur. kiye tmleyeni bulmak iin daha ksa bir yol daha vardr: Saynn en sandan balayarak ilk kez 1 biti grene kadar -ilk grlen 1 biti dahilsaynn ayns yazlr, daha sonraki tm basamaklar iin basaman tersi (Yani 1 iin 0, 0 iin 1) yazlr. rnein: 1110 0100 saysnn ikiye tmleyeni 0101 1000 saysnn ikiye tmleyeni 0001 1100 dr. 1010 1000 dr.

Bir saynn ikiye tmleyeninin ikiye tmleyeni saynn kendisidir.

8 Bitlik Alana Yazlabilen En Kk ve En Byk Deer


8 bitlik bir alana yazlacak en byk tam say tm bitleri 1 olan saydr: 1111 1111 = 255 8 bitlik bir alana yazlacak en kk tam say tm bitleri 0 olan say, yani 0'dr: 0000 0000 = 0

Negatif tamsaylarn da ifade edildii ikilik say sistemine "iaretli ikilik say sistemi" (signed binary system) denir. aretli ikilik say sisteminde negatif deerleri gstermek iin hemen hemen tm bilgisayar sistemlerinde aadaki yntem uygulanr: Saynn en soldaki biti iaret biti (sign bit) olarak kabul edilir. aret biti 1 ise say negatif, 0 ise say pozitif olarak deerlendirilir. kilik sistemde bir negatif say, ayn deerdeki pozitif saynn ikiye tmleyenidir. rnek olarak, ikilik sistemde yazlan 27 says yine ikilik sistemde yazlan 27 saysnn ikiye tmleyenidir. Pozitif olan saylarn deeri, tpk iaretsiz say sisteminde olduu gibi elde edilir: rnein, 0001 1110 iaretli ikilik say sisteminde pozitif bir saydr. Onluk say sisteminde 30 saysna eittir. Negatif tamsaylarn deeri ancak bir dnmle elde edilebilir. Saynn deerini hesaplamak iin ilk nce saynn ikiye tmleyeni bulunur. Bu saynn hangi pozitif deer olduu bulunur. Elde edilmek istenen say, bulunan pozitif say ile ayn deerdeki negatif saydr. rnein, 1001 1101 saysnn onluk say sisteminde hangi sayya karlk geldiini bulalm: aret biti 1 olduuna gre say negatiftir. 1001 1101 saysnn ikiye tmleyeni 0110 0011, yani 99 deeridir. O zaman 1001 1101 says -99'dur. Onluk say sisteminde ifade edilen negatif saylarn iaretli ikilik say sisteminde yazlmas iin aadaki yol izlenebilir: nce saynn ayn deerli fakat pozitif olan ikilik sistemde ifade edilir. Daha sonra yazlan saynn ikiye tmleyeni alnarak, yazmak istenilen say elde edilir. rnein, ikilik sisteminde 17 deerini yazalm: 17 = 0001 0001 Bu saynn ikiye tmleyeni alnrsa 1110 1111

Negatif Saylarn Gsterimi

17/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

says elde edilir. Say deeri ayn olan negatif ve pozitif saylar birbirinin ikiye tmleyenidir. Bu saylarn ikilik say sisteminde toplamlar 0'dr. aretli ikilik say sisteminde 1 byte'lk alana yazlabilecek en byk ve en kk saylar ne olabilir? En byk say kolayca hesaplanabilir. Saynn pozitif olmas iin, iaret bitinin 0 olmas ve say deerini en byk hale getirmek iin dier btn bitlerinin 1 olmas gerekir, deil mi? Bu say 0111 1111'dr. Bu saynn onluk say sistemindeki karl 127'dir. Peki ya en kk negatif say katr, nasl ifade edilir? 0111 1111 saysnn ikiye tmleyenini alndnda 127 says elde edilir. 1000 0001 (- 127) Bu saydan hala 1 kartlabilir: 1000 0000 (-128) 1000 0000, 1 byte alana yazlabilecek en kk negatif saydr. 1 byte alana yazlabilecek en byk say snr aldnda negatif blgeye geilir. En byk pozitif tamsayya 1 toplandn dnelim: 0111 1111 1 1000 0000 (en byk pozitif tamsay = 127) (en kk tamsay = -128)

aretli ikilik sistemde n byte alana yazlabilecek en byk tamsayya 1 eklendiinde n byte alana yazlabilecek en kk tamsay elde edilir. n byte alana yazlabilecek en kk tamsaydan 1 karldnda da n byte alana yazlabilecek en byk tamsay elde edilir. Yukarda aklamalara gre -1 saysnn iaretli ikilik say sisteminde 8 bitlik bir alanda aadaki ekilde ifade edilir: -1 = 1111 1111 Yani iaretli ikilik say sisteminde tm bitleri 1 olan say -1'dir.

18/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Onaltlk ve Sekizlik Say Sistemleri

Onaltlk say sisteminde (hexadecimal system) saylar daha youn olarak kodlanp kullanabilir.Onaltlk say sisteminde 16 simge bulunur. lk 10 simge onluk sistemde kullanlanlarla ayndr: 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 Daha sonraki simgeler iin alfabenin ilk 6 harfi kullanlr: A = 10, B = 11, C = 12, D = 13, E = 14, F = 15 Onaltlk say sisteminde yazlm bir sayy onluk sistemde ifade etmek iin, en sadan balanarak basamak deerleri onaltnn artan sleriyle arplr: 01AF = (15 * 1) + (10 * 16) + (1 * 256) + (0 * 4096) = 431

Onluk say sisteminde yazlm bir sayy, onaltlk say sisteminde ifade etmek iin onluk say sisteminden ikilik say sistemine yaplan dnme benzer yntem kullanlabilir. Say srekli 16'ya blnerek, kalanlar soldan saa doru yazlr. Uygulamalarda onaltlk say sisteminin getirdii nemli bir fayda vardr: Onaltlk say sistemi ile ikilik say sistemi arasndaki dnmler kolay bir biimde yaplabilir: Onaltlk say sistemindeki her bir basamak ikilik say sisteminde 4 bitlik (1 Nibble) alanda ifade edilebilir: 0000 0001 0010 0011 0100 0101 0110 0111 0 1 2 3 4 5 6 7 1000 1001 1010 1011 1100 1101 1110 1111 8 9 A B C D E F

rnek olarak, 2ADF saysn ikilik say sisteminde ifade edilmek istensin: 2 A D F = = = = 0010 1010 1101 1111

Bu durumda 2ADF = 0010 1010 1101 1111 kilik say sisteminden onaltlk say sistemine de benzer ekilde dnm yaplabilir: nce bitler sadan balayarak drder drder ayrlr. En son drt bit eksik kalrsa sfr ile tamamlanr. Sonra her bir drtlk grup iin dorudan onaltlk say sistemindeki karl yazlr: 1010 1110 1011 0001 = AEB1 0010 1101 0011 1110 = 2D3E Onaltlk say sisteminde yazlm iaretli bir saynn pozitif mi negatif mi olduunu anlamak iin saynn en soldaki basamana bakmak yeterlidir. Bu basamak [8 - F] aralnda ise say negatiftir.

19/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aada baz nemli iaretli tamsaylar, ikilik, onaltlk say sistemlerinde ifade ediliyor: kilik say sistemi 0000 0000 0000 0000 0000 0000 0000 0001 0111 1111 1111 1111 1000 0000 0000 0000 1111 1111 1111 1111 onaltlk say sistemi 0000 0001 7FFF 8000 FFFF onluk say sistemi 0 1 32767 -32768 -1

Daha az kullanlan bir say sistemidir. Sekizlik say sisteminde 8 adet simge vardr: 0 1 2 3 4 5 6 7 Sekizlik say sisteminin her bir basama, ikilik say sisteminde 3 bit ile ifade edilir. 001 010 011 100 101 110 111 1 2 3 4 5 6 7

Sekizlik Say Sistemi

Sekizlik say sisteminin kullanlma nedeni de, ikilik say sistemine gre daha youn bir ifade biimine olanak vermesi, ikilik say sistemiyle sekizlik say sistemi arasnda dnmlerin ok kolay bir biimde yaplabilmesidir.

20/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir gerek say aadaki gibi ifade edilebilir:

Gerek Saylarn Bellekte Tutulmas


p

x= s * b * f k * b k
e k =1
Yukardaki genel denklemde x b e p fk ifade edilecek gerek say Kullanlan say sisteminin taban deeri (Tipik olarak 2, 8, 10 ya da 16) stel deer. Bu deer format tarafndan belirlenen emin ve emax arasndaki bir deer Ondalk ksm belirleyen basamak says Say sistemindeki basamak deerleri. 0 fk b

Gerek saylarn ifadesinde, gerek say kabul edilmeyen baz gsterimler sz konusudur. Bunlar: sonsuz (infinity) NaN (not a number) deerleridir. Bu deerler iaretli olabilir. Sistemlerin ounda, gerek saylar IEEE (Institute of Electrical and Electronics Engineers) 754 standardna gre tutulur. (IEEE Standard for Binary Floating-Point Arithmetic - ISO/IEEE std 754-1985).Bu standarda gre gerek saylar iin iki ayr format belirlenmitir: 1. Tek duyarlkl gerek say format (single precision format) Bu formatta gerek saynn gsterimi genel formle aadaki biimde uyarlanabilir:

x= s * 2 * f k * 2k
e k =1
Bu formatta gerek say 32 bit (4 byte) ile ifade edilir. 32 bit ayr ksma ayrlmtr. i. aret biti (sign bit) (1 bit) Aada S harfi ile gsterilmitir. aret biti 1 ise say negatif, iaret biti 0 ise say pozitiftir. ii. stel ksm (exponent) (8 bit) Aada E harfleriyle gsterilmitir. iii. Ondalk ksm (fraction) (23 bit) Aada F harfleriyle gsterilmitir. SEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFF Aadaki formle gre saynn deeri hesaplanabilir : V, gerek saynn deeri olmak zere: EEEEEEEE = 255 ve FFF...F 0 ise V = NaN (Not a number)

24

21/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

rnek : 0 11111111 00001000000100000000000 (Say deil) 1 11111111 00010101010001001010101 (Say deil) EEEEEEEE = 255 ve FFF...F = 0 ve S = 1 ise EEEEEEEE = 255 ve FFF...F = 0 ve S = 0 ise 0 < EEEEEEEE < 255 ise V = (-1)S * 2(E - 127) * (1.FFF...F) nce saynn ondalk ksmnn bana 1 eklenir. Daha sonra bu say 2(E - 127) ile arplarak noktann yeri ayarlanr. Noktadan sonraki ksm ikinin artan negatif sleriyle arplarak elde edilir. rnekler: 0 10000000 00000000000000000000000 = +1 * 2(128 = 2 * 1.0 = 10.00 = 2 0 10000001 10100000000000000000000 = +1 * 2(129 = 22 * 1.101 = 110.100000 = 6.5 1 10000001 10100000000000000000000 = -1 * 2(129 = -22 * 1.101 = 110.100000 = -6.5 0 00000001 00000000000000000000000 = +1 * 2(1 = 2-126 EEEEEEEE = 0 ve FFF...F 0 ise V = (-1)S * 2-126 * (0.FFF...F) rnekler : 0 00000000 10000000000000000000000 = +1 * 2-126 * 0.1 0 00000000 00000000000000000000001 = +1 * 2-126 * 0.00000000000000000000001 = 2-149 (en kk pozitif deer) EEEEEEEE = 0 ve FFF...F = 0 ve S = 1 ise V = -0 EEEEEEEE = 0 ve FFF...F = 0 ve S = 0 ise V = 0 2. ift duyarlkl gerek say format (double precision format) Bu formatta gerek saynn gsterimi genel formle aadaki biimde uyarlanabilir:
- 127)

V = -sonsuz V = +sonsuz

* 1.0

- 127)

* 1.101

- 127)

* 1.101

- 127)

* 1.0

x= s * 2 * f k * 2 k
e k =1
Bu formatta gerek say 64 bit yani 8 byte ile ifade edilir. 64 bit ayr ksma ayrlmtr.

53

22/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

i. aret biti (sign bit) (1 bit) Aada S harfi ile gsterilmitir. aret biti 1 ise say negatif, iaret biti 0 ise say pozitiftir. ii. stel ksm (exponent) (11 bit) Aada E harfleriyle gsterilmitir. iii. Ondalk ksm (fraction) (52 bit) Aada F harfleriyle gsterilmitir. SEEEEEEEEEEEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF Aadaki formle gre saynn deeri hesaplanabilir, V saynn deeri olmak zere: EEEEEEEEEEE = 2047 ve FFF...F 0 ise V = NaN (Not a number) EEEEEEEEEEE = 2047 ve FFF...F = 0 ve S = 1 ise V = -sonsuz EEEEEEEEEEE = 2047 ve FFF...F = 0 ve S = 0 ise V = +sonsuz 0 < EEEEEEEEEEE < 2047 ise V = (-1)S * 2(EEEEEEEEEEE
- 1023)

* (1.FFF...F)

nce saynn ondalk ksmnn bana 1 eklenir. Daha sonra bu say 2(EEEEEEEEEEE-123) ile arplarak noktann yeri ayarlanr. Noktadan sonraki ksm, ikinin artan negatif sleriyle arplarak elde edilir. EEEEEEEEEEE = 0 ve FFF...F 0 ise V = (-1)S * 2-126 * (0. FFF...F) EEEEEEEEEEE = 0 ve FFF...F = 0 ve S = 1 ise V = -0 EEEEEEEEEEE = 0 ve FFF...F = 0 ve S = 0 ise V = 0

23/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

GENEL KAVRAMLAR ve TERMLER


Bir programlama dilinde yazlm kaynak dosyann (program) anlaml en kk paralarna "atom" (token) denir. Bir programlama dilinde yazlm bir metin, atomlardan oluur. Bir kaynak dosya, derleyici program tarafndan ilk nce atomlarna ayrlr. Bu ileme "atomlarna ayrma" (Tokenizing - Lexical analysis) denir. Farkl programlama dillerinin atomlar birbirlerinden farkl olabilir. Atomlar aadaki gibi gruplara ayrlabilir:

Atomlar ve Trleri

1. Anahtar Szckler

Anahtar szckler (keywords - reserved words) programlama dili tarafndan nceden belirlenmi anlamlara sahip atomlardr. Bu atomlar kaynak dosya iinde baka bir anlama gelecek biimde kullanlamazlar. rnein bu atomlarn deiken ismi olarak kullanlmalar geerli deildir. Standard ANSI C (C 89) dilinin 32 anahtar szc vardr: 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 Derleyici yazan firmalar da kendi yazdklar derleyiciler iin ek anahtar szckler tanmlayabilir. Baz programlama dillerinde anahtar szcklerin kk ya da byk harf ile yazlmas bir anlam fark yaratmaz. Ama C'de btn anahtar szckler kk harf olarak tanmlanmtr. C byk harf kk harf duyarl olan (case sensitive) bir dildir. Ama dier programlama dillerinin ounda byk - kk harf duyarl yoktur (case insensitive). C dilinde rnein bir deikene "register" isminin verilmesi geerli deildir. nk "register" bir anahtar szcktr, C dili tarafndan ayrlmtr. Ama buna karn bir deikene REGISTER, Register, RegisTER isimleri verilebilir, nk bunlar artk anahtar szck saylmazlar.

Deikenler, ilevler, makrolar, deimezler gibi yazlmsal varlklara programlama dili tarafndan belirlenmi kurallara uyulmak kouluyla isimler verilebilir. Yazlmsal bir varla verilen isme ilikin atomlar isimlerdir (identifier). Her programlama dilinde olduu gibi C dilinde de kullanlan isimlerin geerliliini belirleyen kurallar vardr.

2. simler

leler (operators) nceden tanmlanm bir takm ilemleri yapan atomlardr. rnein +, -, *, / , >=, <= birer iletir. Programlama dillerinde kullanlan ile simgeleri birbirinden farkl olabilecei gibi, ile tanmlamalar da birbirinden farkl olabilir. rnein birok programlama dilinde s alma ileci tanmlanmken C dilinde byle bir ile yoktur. C'de s alma ilemi ile ile deil bir standart ilev (function) yardmyla yaplr. C dilinde baz ileler iki karakterden oluur. Bu iki karakter bitiik yazlmaldr. Aralarna boluk karakteri koyulursa ile anlam yitirilir. C dilinin 45 tane ileci vardr.

3. leler

4. Deimezler

Deimezler (constants) dorudan ileme sokulan, deiken bilgi iermeyen atomlardr. rnein:

25/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sayac = son + 10 gibi bir ifadede 10 deimezi dorudan son isimli deiken ile ileme sokulur.

5. Dizgeler

ki trnak iindeki ifadelere "Dizge" (string / string litaerals) denir. Dizgeler programlama dillerinin ounda tek bir atom olarak alnr, daha fazla paraya blnemezler. "STRNGLER DE BRER ATOMDUR" ifadesi bir dizgedir.

Yukarda saylan atom snflarnn dnda kalan tm atomlar bu gruba sokulabilir. Genellikle dier atomlar birbirinden ayrma amacyla kullanldklar iin ayra (separators, punctuators, delimiters) olarak isimlendirilirler.

6. Ayralar, Noktalama aretleri

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: #include <stdio.h> int main() { int number, k; int total = 0; printf("ltfen bir say girin\n"); scanf("%d", &number); for(k = 1; k<= number; ++k) total += k; printf("toplam = %d\n", total); } return 0;

Bu kaynak kod aadaki gibi atomlarna ayrlabilir: # include < stdio.h > main number , k , total = printf ( "ltfen bir say girin\n" , & number ) ; for ( k = 1 ; k total += k ; printf ( "toplam = %d\n" , Yukardaki atomlar aada gruplandrlyor: Anahtar szckler: include int for return 0 ) <= ( ; ; ; toplam ) { scanf ++ ) int ( k ; "%d" ) }

simlendirilenler: main k toplam printf scanf

26/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

leler: = <= ++ +=

Deimezler: 0 Dizgeler: ("ltfen bir say girin\n" Ayralar, noktalama iaretleri < > ( ) , ; { } ) "%d" "toplam = %d\n" 1 0

Nesne

Bellekte yer kaplayan ve ieriklerine eriilebilen alanlara nesne (object) denir. Bir ifadenin nesne olabilmesi iin bellekte bir yer belirtmesi gerekir. Programlama dillerinde nesnelere isimleri kullanarak eriilebilir. a = b + k; rneinde a, b ve k birer nesnedir. Bu ifadede a nesnesine, b ve k nesnelerine ait deerlerin toplam atanr. sonuc = 100; sonuc isimli nesneye 100 deimez deeri atanr. Nesnelerin baz zelliklerinden sz edilebilir: Nesnelerin isimleri (name) nesneyi temsil eden yazlmsal varlklardr. Nesnelere isimleri programc tarafndan verilir. Her dil iin nesne isimlendirmede baz kurallar sz konusudur. vergi = 20000; Burada vergi bir nesne ismidir. Nesne ile deiken terimleri birbirine tam olarak edeer deildir. Her deiken bir nesnedir ama her nesne bir deiken deildir. Deiken demekle daha ok, programcnn isimlendirdii nesneler kastedilir. Peki programcnn isimlendirmedii nesneler de var mdr? Evet, gstericiler konusunda da grlecei gibi, deiken olmayan nesneler de vardr. Nesne kavram deiken kavramn kapsar. Nesnelerin deerleri (value) ilerinde tuttuklar bilgilerdir. Baka bir deyile nesneler iin bellekte ayrlan yerlerdeki 1 ve 0'larn yorumlan biimi, ilgili nesnenin deeridir. Bu deerler programlama dillerinin kurallarna gre, istenildikleri zaman programc tarafndan deitirilebilirler. C dilinde baz nesnelerin deerleri ise bir kez verildikten sonra bir daha deitirilemez. Nesnenin tr (type) derleyiciye o nesnenin nasl yorumlanaca hakknda bilgi verir. Bir nesnenin tr onun bellekteki uzunluu hakknda da bilgi verir. Her trn bellekte ne kadar uzunlukta bir yer kaplad programlama dillerinde nceden belirtilmitir. Bir nesnenin tr, ayrca o nesne zerinde hangi ilemlerin yaplabilecei bilgisini de verir. Tr, nesnenin ayrlmaz bir zelliidir. Trsz bir nesne kavram sz konusu deildir. Trler ikiye ayrlabilir:

27/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

1. nceden tanmlanm veri trleri nceden tanmlanm veri trleri (default types - built-in types, primitive types) programlama dilinin tasarmnda var olan veri trleridir. rnein C dilinde nceden tanmlanm 11 ayr veri tr vardr. 2. Programc tarafndan tanmlanan veri trleri Programc tarafndan tanmlanan veri trleri (user defined types) programcnn yaratt trlerdir. Programlama dillerinin ounda nceden tanmlanm trlerin yannda, programcnn yeni bir tr oluturmasn salayan aralar vardr. rnein C dilinde yaplar, birlikler, numaralandrma aralar ile, programc tarafndan yeni bir veri tr yaratlabilir. Programlama dillerindeki tr tanmlamalar birbirlerinden farkl olabilir. rnein baz programlama dillerinde Boolean isimli -mantksal doru ya da yanl deerlerini alan- bir tr vardr. Ama C89 dilinde byle bir tr dorudan tanmlanmamtr. Bilinirlik alan (scope), bir ismin, dilin derleyicisi ya da yorumlaycs tarafndan tannabildii program alandr. mr (storage duration - lifespan), programn altrlmas srasnda nesnenin varln srdrd zaman parasdr. Balant (linkage), nesnelerin program oluturan dier modllerde tannabilme zelliidir.

fade

Deiken, ile ve deimezlerin bileimlerine ifade (expression) denir. a + b / 2 c * 2, d = h + 34 var1 geerli ifadelerdir.

Nesne gsteren ifadelere "sol taraf deeri" (left value - L value) denir. Bir ifadenin sol taraf deeri olabilmesi iin mutlaka bir nesne gstermesi gerekir. Bir ifadenin sol taraf deeri olarak isimlendirilmesinin nedeni o ifadenin atama ilecinin sol tarafna getirilebilmesidir. rnein a ve b nesneleri tek bana sol taraf deerleridir. nk bu ifadeler atama ilecinin sol tarafna getirilebilirler. rnein : a = 17 ya da b = c * 2 denilebilir. Ama a + b bir sol taraf deeri deildir. nk a + b = 25 denilemez. Deikenler, her zaman sol taraf deeridirler. Deimezler, sol taraf deeri olamazlar.

Sol Taraf Deeri

Daha az kullanlan bir terimdir. Nesne gstermeyen ifadeler sa taraf deeri (right value) olarak isimlendirilirler. Tipik olarak, atama ilecinin sol tarafnda bulunamayan yalnzca

Sa Taraf Deeri:

28/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sa tarafnda bulunabilen ifadelerdir. Deimezler 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 ilecinin sa tarafna gelebilen her ifade sa taraf deeri olarak isimlendirilmez. Sa taraf deeri, genellikle bir ifadenin nesne gstermediini vurgulamak iin kullanlr.

Deimez fadeler

Yalnzca deimezlerden oluan bir ifadeye "deimez ifade" (constant expression) denir. Bir deimez ifadede deikenler ya da ilev arlar yer alamaz: 10 3.5 10 + 20 ifadeleri deimez ifadelerdir. Deimez ifadeler, derleme aamasnda derleyici tarafndan net saysal deerlere dntrlebilir. C dilinin szdizim kurallar birok yerde deimez ifadelerin kullanlmasn zorunlu klar.

Deyim

C dilinin cmlelerine deyim (statement) denir. C dilinde deyimler ";" ile sonlandrlr. result = number1 * number2 bir ifadedir. Ancak result = number1 * number2; bir deyimdir. Bu deyim derleyicinin, number1 ve number2 deikenlerin deerlerinin arplarak, elde edilen deerin result deikenine atanmasn salayacak ekilde kod retmesine neden olur. Baz deyimler yalnzca derleyiciye bilgi vermek amacyla yazlr, derleyicinin bir ilem yapan kod retmesine yol amaz. Byle deyimlere bildirim deyimleri (declaration statement) denir. Baz deyimler derleyicinin bir ilem yapan kod retmesini salar. Byle deyimlere yrtlebilir deyimler (executable statement) denir.

29/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BR C PROGRAMI OLUTURMAK
C dilinde yazlan bir programn altrlabilir hale getirilebilmesi iin, ounlukla aadaki srelerden geilir: 1. Kaynak dosyann oluturulmas Kaynak dosya, metin dzenleyici bir programda (text editr) yazlr. Kaynak dosya bir metin dosyasdr. C dilinin kurallarna gre yazlan dosyalarn uzants, geleneksel olarak ".c" seilir. 2. Kaynak dosyann derleyici program (compiler) tarafndan derlenmesi: Bir programlama dilinde yazlm program baka bir programlama diline eviren programlara "evirici" (translator) denir. Dntrlmek istenen programn yazld dile "kaynak dil" (source language), dnmn yapld dile ise "hedef dil" (target language) denir. Hedef dil, makine dili ya da simgesel makine dili ise, byle evirici programlara "derleyici" (compiler) denir. Derleyici program kaynak dosyay alr, eviri ileminde eer baarl olursa bu kaynak dosyadan bir "ama dosya" (object file) retir. Derleyici programn derleme ilemini yapma srecine "derleme zaman " (compile time) denir. Derleme ilemi baarszlk ile de sonulanabilir. Bir derleyici program, kaynak metni makine diline evirme abasnda, kaynak metnin C dilinin szdizim kurallarna uygunluunu da denetler. Kaynak metinde dilin kurallarnn inendii durumlarda, derleyici program bu durumu bildiren bir ileti (diagnostic message) vermek zorundadr. Derleyici programn verdii ileti: i) Bir "hata iletisi" (error message) olabilir. Bu durumda, derleyici programlar ounlukla ama dosya retmeyi reddeder. ii) Bir uyar iletisi olabilir (warning message). Bu durumda, derleyici programlar ounlukla ama dosyay retir. C standartlarna gre derleyici programlar, dilin kurallarnn inenmesi durumlarnn dnda da, programcy mantksal hatalara kar korumak amacyla, istedikleri kadar uyar iletisi retebilir. Unix/Linux sistemlerinde oluturulan ama dosyalarn uzants ".o" dur. DOS ve Windows sistemlerinde ama dosyalar ".obj" uzantsn alr. Derleyici programlar, genellikle basit bir arayz ile iletim sisteminin komut satrndan altrlacak biimde yazlr. Arayzn basit tutulmasnn nedeni baka bir program tarafndan kolay kullanabilmesini salamak iindir. rnein, Microsoft firmasnn C/C++ derleyicisi aslnda "cl.exe" isimli bir programdr. UNIX sistemlerindeki GNU'nun gcc derleyicisi ise aslnda gcc.exe isimli bir programdr. Derleyici programlar daha kolay ynetmek iin, IDE (integrated development environment) denilen gelitirme ortamlar kullanlabilir. IDE derleyici demek deil, derleyiciyi altran ve program yazmay kolaylatran gelitirme ortamlardr. rnein MinGW ve DevC++ derleyici deil, IDE programlardr. Bu programlar gcc derleyicisini kullanmaktadr. 3. Daha nce elde edilmi ama dosyalar "balayc" (linker) program tarafndan birletirilerek altrlabilir bir dosya elde edilir. UNIX sistemlerinde genellikle altrlabilir dosyann uzants olmaz. Windows sistemlerinde altrlabilir dosyalarn uzants ".exe" olarak seilir.

31/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C ve C++ dillerinde derleyici programdan daha nce kaynak kodu ele alan "nilemci" (preprocessor) isimli bir program kullanlr. nilemci program ayr bir konu bal altnda ele alnacak.

nilemci Program

32/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

VER TRLER
Nesnelerin en nemli zelliklerinden biri, nesnenin trdr. Tr (type), nesnenin olmazsa olmaz bir zelliidir. Tr olmayan bir nesneden sz etmek mmkn deildir. Derleyiciler nesnelerle ve verilerle ilgili kod retirken, tr bilgisinden faydalanr. Derleyiciler nesnenin tr bilgisinden, sz konusu veriyi bellekte ne ekilde tutacaklarn, verinin deerini elde etmek iin veri alanndaki 1 ve 0 lar nasl yorumlayacaklarn, veriyi hangi ilemlere sokabileceklerini renir. Programlama dilleri asndan baktmz zaman trleri iki ayr gruba ayrabiliriz.

Programlama dilinin tasarmndan kaynaklanan ve dilin kurallarna gre varl gvence altna alnm olan trlerdir. Her programlama dili programcnn dorudan kullanabilecei, eitli zelliklere sahip veri trleri tanmlar. C dilinde de nceden tanmlanm 11 tane veri tr vardr.

1. nceden Tanmlanm Trler

Programlama dillerinin ou, nceden tanmlanm veri trlerine ek olarak, programcnn da yeni trler tanmlanmasna izin verir. Programcnn tanmlayaca bir nesne iin nceden tanmlanm veri trleri yetersiz kalrsa, programc dilin belli szdizim (sentaks) kurallarna uyarak kendi veri trn yaratabilir. C dilinde de programc yeni bir veri trn derleyiciye tantabilir, 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 dilinin 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.

2. Programcnn Tanmlanm Olduu Trler

Tamsay Veri Trleri

C dilinin toplam 4 ayr tamsay veri tr (integer types) vardr. Ancak her birinin kendi iinde iaretli (signed) ve iaretsiz (unsigned) biimi olduundan toplam tamsay tr 8 kabul edilir. aretli (signed) tamsay trlerinde pozitif ve negatif tam say deerleri tutulabilirken, iaretsiz (unsigned) veri trlerinde negatif tamsay deerleri tutulamaz. Aada C dilinin temel tamsay veri trleri tantlyor: aretli karakter tr: Bu veri trne ksaca signed char tr denir. phesiz char szc ingilizce "character" szcnden ksaltlmtr, Trke "karakter" anlamna gelir. Ancak bu trn ismi C'nin anahtar szckleri olan signed ve char szckleri ile zdeleip, "signed char" olarak sylenir. aretli char trnden bir nesnenin 1 byte'lk bir alanda tutulmas C standartlarnca gvence altna alnmtr. 1 byte'lk bir alan iaretli olarak kullanldnda bu alanda saklanabilecek deerler -128 / 127 deerleri arasnda olabilir. aretsiz karakter tr: aretsiz char trnn iaretli olandan fark 1 byte'lk alann iaretsiz olarak, yani yalnzca 0 ve pozitif saylarn ifadesi iin kullanlmasdr. Bu durumda iaretsiz char trnde 0 - 255 arasndaki tamsay deerleri tutulabilir. Karakter tr: 33/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

aretli ya da iaretsiz olarak kullanlaca derleyicinin seimine bal olan bir trdr. aretli ve iaretsiz ksa tamsay veri tr short ve int szckleri C dilinin anahtar szcklerinden olduu iin bu tre genellikle short int ya da ksaca short tr denir. aretli veya iaretsiz short trnden bir nesne tanmland zaman, nesnenin bellekte ka byte yer kaplayaca sistemden sisteme deiebilir. Sistemlerin ounda, short int veri trnden yaratlan nesne bellekte 2 byte'lk bir yer kaplar. aretli short int trnden bir nesne -32768 - +32767 aralndaki tamsay deerlerini tutabilirken, iaretsiz short trnde tutulabilecek deerler 0 - 65535 aralnda olabilir. aretli ve iaretsiz tamsay tr Bu tre ksaca int tr denir. aretli ve iaretsiz int trnden bir nesne tanmland zaman, nesnenin bellekte ka byte yer kaplayaca sistemden sisteme deiebilir. ounlukla 16 bitlik sistemlerde, int trnden veri 2 byte, 32 bitlik sistemlerde ise int trnden veri tr 4 byte yer kaplar. 16 bitlik sistem demekle ilemcinin yazma (register) uzunluunun 16 bit olduu anlatlr. int veri trnn 2 byte uzunluunda olduu sistemlerde bu veri trnn say snrlar, iaretli int tr iin -32768 - +32767, iaretsiz int veri tr iin 0 - +65535 arasnda olur. int veri trnn 4 byte uzunluunda olduu sistemlerde bu veri trnn say snrlar, iaretli int tr iin -2147483648 - +2147483647, iaretsiz int veri tr iin 0 +4.294.967.295 arasnda olur. aretli ve iaretsiz uzun tamsay veri tr long ve int szckleri C dilinin anahtar szcklerinden olduu iin bu tre genellikle long int ya da ksaca long tr denir. aretli veya iaretsiz long trnden bir nesne tanmland zaman, nesnenin bellekte ka byte yer kaplayaca sistemden sisteme deiebilir. Sistemlerin ounda, long int veri trnden yaratlan nesne bellekte 4 byte'lk bir yer kaplar. aretli long int trnden bir nesne -2147483648- +2147483648 aralndaki tamsay deerlerini tutabilirken, iaretsiz long trnde tutulabilecek deerler 0 +4.294.967.295 aralnda olabilir.

C dilinde gerek say deerlerini tutabilmek iin 3 ayr veri tr tanmlanmtr. Bunlar srasyla, float, double ve long double veri trleridir. Gerek say veri trlerinin hepsi iaretlidir. Yani gerek say veri trleri iinde hem pozitif hem de negatif deerler tutulabilir. Gerek saylarn bellekte tutulmas sistemden sisteme deiebilen zellikler ierebilir. Ancak sistemlerin ounda IEEE 754 sayl standarda uyulur. Sistemlerin hemen hepsinde float veri trnden bir nesne tanmland zaman bellekte 4 byte yer kaplar. 4 byte lk yani 32 bitlik alana zel bir kodlama yaplarak gerek say deeri tutulur. IEEE 754 sayl standartta 4 byte lk gerek say format "single precision" (tek duyarlk) olarak isimlendirilirken, 8 byte lk gerek say format "double precision" (ift duyarlk) olarak isimlendirilmitir. Sistemlerin hemen hepsinde double veri trnden bir nesne tanmland zaman bellekte 8 byte yer kaplar. long double veri trnn uzunluu sistemden sisteme deiiklik gsterir. Bu tr, sistemlerin ounda 8, 10, 12 byte uzunluundadr.

Gerek Say Trleri

34/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Say sistemleri blmmzden, gerek saylarn n byte'lk bir alanda zel bir biimde kodlandn hatrlayacaksnz. rnein IEEE 754 sayl standartta 4 ya da 8 byte lk alan ayr paraya blnm, bu paralara zel anlamlar yklenmitir. rnein gerek saynn 4 byte lk bir alanda tutulmas durumunda 1 bit, saynn iaretini tutmak iin kullanlrken, 23 bit, saynn ondalk (fraction) ksmn tutar. Geriye kalan 8 bit ise, noktann en saa alnmas iin gerekli bir arpm faktrn dolayl olarak tutar. Bu durumda gerek saynn 8 byte'lk bir alanda kodlanmas durumunda, hem tutulabilecek saynn bykl artarken hem de noktadan sonraki duyarlk da artar. Aada C dilinin doal veri trlerine ilikin bilgiler bir tablo eklinde veriliyor:

35/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

TAMSAYI TRLER (INTEGER TYPES)


TR SM signed char unsigned char char signed short int unsigned short int signed int unsigned int signed long int unsigned long int _Bool (C 99) signed long long int (C 99) unsigned long long int(C 99) UZUNLUK (byte)
(DOS /UNIX)

SINIR DEERLER -128 - 127 0 - 255 Derleyiciye bal -32.768 / 32.767 0 / 65.535 -32.768 / 32.767 -2.147.483.648 - 2.147.483.647 0 / 65.535 0 / 4.294.967.295 -2.147.483.648 - 2.147.483.647 0 / 4.294.967.295 false / true
-9.223.372.036.854.775.808 / 9.223.372.036.854.775.807

1 1 1 2 2 2 4 2 4 4 4 1 8 8

0 / 18.446.744.073.709.551.615

GEREK SAYI TRLER (FLOATING TYPES)


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

float double

long double 8/10/12 float _Complex (C 99) double _Complex (C 99) long double _Complex (C 99) float _Imaginary (C 99) double _Imaginary (C 99) long double _Imaginary (C 99)

Yukarda verilen tablo, sistemlerin ou iin geerli de olsa ANSI C standartlarna gre yalnzca aadaki zellikler gvence altna alnmtr: char tr 1 byte uzunluunda olmak zorundadr. short veri trnn uzunluu int trnn uzunluuna eit ya da int tr uzunluundan kk olmaldr. long veri trnn uzunluu int trne eit ya da int trnden byk olmak zorundadr. Yani short <= int <= long Derleyiciler genel olarak derlemeyi yapacaklar sistemin zelliklerine gre int trnn uzunluunu ilemcinin bir kelimesi kadar alrlar. 16 bitlik bir ilemci iin yazlan tipik bir uygulamada char tr 1 byte

36/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

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 tryken, gerek saylar iin double veri trdr. Peki hangi durumlarda hangi veri trn kullanmak gerekir? Bunun iin hazr bir reete vermek pek mmkn deil, zira kullanacamz bir nesne iin tr seerken birok etken sz konusu olabilir. Ama genel olarak u bilgiler verilebilir: Gerek saylarla yaplan ilemler tam saylarla yaplan ilemlere gre ok daha 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 belirli lde yavalamas anlamna gelir. Bir tamsay trnn yeterli olmas durumunda gerek say trnn kullanlmas programn okunabilirliinin de azalmasna neden olurr.

37/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BLDRM ve TANIMLAMA
Bir kaynak dosya iinde yazlan geerli C deyimlerinin bir ksm, bir ilem yaplmasna ynelik deil, derleyiciye derleme zamannda kullanaca bir bilgi vermeye yneliktir. Byle deyimlere bildirim (declaration) deyimi denir. Derleyici geerli bir bildirim deyiminin stnden getiinde bir bilgi alr ve ald bu bilgiyi yine derleme zamannda kullanr.

Bildirim Nedir?

Ancak baz bildirim deyimleri vardr ki, derleyici bu deyimlerden ald bilgi sonucunda, bellekte bir yer ayrr. Tanmlama (definition), derleyicinin bellekte yer ayrmasn salayan bildirim deyimleridir.

Tanmlama Nedir?

Deikenlerin Tanmlanmas

C dilinde bir deiken derleyiciye tantlmadan kullanlamaz. Derleyicinin sz konusu deiken iin bellekte bir yer ayrmasn salamak iin, uygun bir szdizimi ile, deikenin ismi ve tr derleyiciye bildirilir. Bildirim ilemi yoluyla, derleyiciler deikenlerin hangi zelliklere sahip olduklarn anlar. Bylece bu deikenler iin programn alma zamanna ynelik olarak bellekte uygun bir yer ayrma ilemi yapabilir. C dilinde eer yaplan bir bildirim ilemi, derleyicinin bellekte bir yer ayrmasna neden oluyorsa bu ileme tanmlama (definition) denir. Tanmlama nesne yaratan bir bildirimdir. Programlama dillerinin ounda nesneler kullanlmadan nce derleyiciye tantlrlar. Her tanmlama ilemi ayn zamanda bir bildirim ilemidir. Ama her bildirim ilemi bir tanmlama olmayabilir. Baka bir deyile, tanmlama nesne yaratlmasn salayan bir bildirim ilemidir. C dilinde bir deikeni bildirimini yapmadan nce kullanmak geersizdir, derleme ileminde hata (error) oluumuna yol aar. Bir deikenin derleyiciye tantlmas, deikenin trnn ve isminin derleyiciye bildirilmesidir. 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.

Deiken Tanmlama leminin Genel Biimi


C'de bildirim ilemi aadaki ekilde yaplr : <tr belirten szckler> <deiken ismi> <;> Tanmlamann bir noktal virglle sonlandrldn gryorsunuz. Nasl normal dilde, nokta cmleleri sonlandryorsa, C dilinde de noktal virgl atomu, C dilinin cmleleri olan deyimleri sonlandrr. "Noktal virgl" atomu C dilinin cmlelerinin noktasdr. Bundan sonra noktal virgl atomuna "sonlandrc atom" (terminator) diyeceiz. Noktal virgl ayra trnden bir atomdur. C'de deyimler, ounlukla bu ayra ile birbirlerinden ayrlr. a = x + 1; b = x + 2; ifadelerinde bulunan noktal virgller bunlarn ayr birer deyim olduklarn gsterir. Eer bir tek noktal virgl olsayd derleyici iki ifadeyi tek bir ifade gibi yorumlard. a = x + 1 b = x + 2;

Yukardaki ifade tek bir ifade gibi yorumlanr ve derleyici buna geerli bir anlam veremez.

39/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Tr belirten anahtar szckler, C dilinin nceden tanmlanm veri trlerine ilikin anahtar szcklerdir. Bu szcklerin kullanlmasyla, tanmlanacak deikenlerin, 11 temel veri trnden hangisine ait olduu bildirilir. C dilinin nceden tanmlanm veri trlerine ilikin, bildirim ileminde kullanlabilecek anahtar szckler unlardr: signed, unsigned, char, short, int, long, float, double Bu szcklerin hepsi anahtar szck olduundan kk harf ile yazlmaldr, C dilinin byk harf kk harf duyarl (case sensitive) bir dil olduunu hatrlayalm. C dilinin tm anahtar szckleri kk harf ile tanmlanmtr. Tr belirten anahtar szckler aadaki izelgede listelenen seeneklerden biri olmaldr. Keli ayra iindeki ifadeler kullanlmas zorunlu olmayan, yani seime bal olan anahtar szckleri gsteriyor. Ayn satrdaki tr belirten anahtar szckler tamamen ayn anlamda kullanlabilir: 1 2 3 iaretli char tr iaretsiz char tr iaretli ksa tamsay tr [signed] char char unsigned char [signed] short [int] [signed] short short [int] short unsigned short [int] unsigned short [signed] int int signed unsigned int unsigned [signed] long [int] [signed] long long [int] long unsigned long [int] unsigned long float double long double

4 5 6 7

iaretsiz ksa tamsay tr iaretli tamsay tr iaretsiz tamsay tr iaretli uzun tamsay tr

8 9 10 11

iaretsiz uzun tamsay tr float tr double tr long double tr

Yukardaki tablodan da grld gibi, belirli trleri birden fazla ekilde ifade etmek mmkndr. char a; signed char a; int a; signed int a; signed a; long a; long int a; signed long a; signed long int a;

Yukarda ayn kolon zerindeki bildirimlerin hepsi, derleyici tarafndan birbirine edeer olarak ele alnr. Bildirim szdiziminde, deiken ismi olarak, C dilinin isimlendirme kurallarna uygun olarak seilen herhangi bir isim kullanlabilir. C dilinde isimlendirilen (identifiers) varlklar balca 6 grubu ierir. Deikenler (variables) bunlardan yalnzca biridir. levler (functions), etiketler (labels), makrolar (macros), yap ve birlik isimleri (structure and union tags), enum deimezleri (enum constants) isimlerini programclardan alr.

40/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C Dilinin simlendirme Kurallar


simlendirmede yalnzca 63 karakter kullanlabilir. Bunlar: ngiliz alfabesinde yer alan 26 kk harf karakteri: 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 ngiliz alfabesinde yer alan 26 byk harf karakteri: 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 alttire (_) (underscore) karakteridir. simlendirmelerde yukarda belirtilen karakterlerin dnda baka bir karakterin kullanlmas geersizdir. rnein, boluk karakterleri, Trkeye zg (, , , , , , , , , , , ) karakterler, +, -, /, *, & ya da $ karakterleri bir isimde kullanlamaz. Deiken isimleri rakam karakteriyle balayamaz. Harf karakteri ya da alttire karakteri dnda, yukarda geerli herhangi bir karakterle balayabilir. Bu kural derleyicinin deimezlerle isimleri birbirinden ayrmasn kolaylatrr. Deiken isimleri alttire '_' karakteriyle balayabilir. C'nin 32 anahtar szc 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. simlendirmede baka bir seenek de her szcn ilk harfini byk, dier harfleri kk yazmaktr: GenelKatsayiFarki, SquareTotal, NumberOfCards C dili standartlar isim uzunluu konusunda bir snrlama koymamtr. Ancak ismin ilk 31 karakterinin derleyici tarafndan dikkate alnmasn zorunlu klar. Ancak derleyicilerin ou, ok daha uzun deiken isimlerini ileme sokabilirler. 31 karakterden daha uzun isimler kullanldnda programc iin ok az da olsa yle bir risk sz konusudur: Herhangi bir derleyici ilk 31 karakteri ayn olan iki farkl ismi ayn isim olarak ele alabilir. C, byk harf kk harf duyarll olan bir dil olduu iin, isimlendirmelerde de byk harf ile kk harfler farkl karakterler olarak ele alnr: var, Var, VAr, VAR, vAR, vaR deikelerinin hepsi ayr deikenler olarak ele alnr.

Uzun Deiken simleri

simlendirme, yazlan programlarn okunabilirlii asndan da ok nemlidir. yi yazlm olan bir programda kullanlan isimlerin dilin kurallarna gre uygun olmalarnn dnda, baz baka zelliklere de sahip olmas gerekir: 1. Seilen isimler anlaml olmaldr. 41/529

simlendirmede Nelere Dikkat Edilmeli?

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Verilen isimler, deikenin kullanm amac hakknda, okuyana bilgi vermelidir. ou zaman, bir deikenin ismini x, y, z gibi alfabenin harflerinden semek kt bir tekniktir. Tanmlanan deiken, iinde neyin deerini tutacaksa buna ynelik bir isim vermek, kodu okuyann iini kolaylatrr, alglamasn hzlandrr. Ancak geleneksel olarak dng deikenlerine i, j, k isimleri verilir. 2. simler farkl zelliklere sahip deikenlerin ait olduklar grubun ismi olarak seilmemelidir. rnein saya grevini stlenen bir deikenin ismini counter olarak semek yerine prime_counter, valid_word_counter vs. olarak semek ok daha iyidir. Zira programn iinde birden fazla saya bulunabilir. Bunlarn isimleri, neyi saydklarna gre deiirse, kodu okuyan kiinin anlamlandrmas ok daha kolaylar. 3. simlendirmede dil btnl olmaldr. Yazlmda kullanlan temel dilin ngilizce olduunu kabul etmek zorundayz. Yazlm projelerinde isimlendirme genellikle ngilizce tabanl yaplr. Ancak baz deikenlerin isimlerinin ngilizce seilirken baz baka deikenlerin isimlerinin Trke seilmesi programn okunabilirliine zarar verir. 4. C'de deiken isimleri, geleneksel olarak kk harf youn seilirler. Tamam byk harflerle yazlm deiken isimleri daha ok simgesel deimezler iin kullanlrlar.

Bildirim rnekleri
int x; unsigned long int var; double MFCS; unsigned _result; signed short total; Tr belirten anahtar szcklerin yazlmasndan sonra ayn tre ilikin birden fazla nesnenin bildirimi, isimleri arasna virgl atomu koyularak yaplabilir. Bildirim deyimi yine noktal virgl atomu ile sonlandrlmaldr. unsigned char ch1, ch2, ch3, ch4; float FL1, Fl2; unsigned total, subtotal; int _vergi_katsayisi, vergi_matrahi; Farkl trlere ilikin bildirimler virgllerle birbirinden ayrlamaz. long x, int y; /* Geersiz! */

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

42/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

signed int x, y; tamamen ayn anlamdadr. Yine unsigned u; ile unsigned int u; Tamamen ayn anlamdadr. Bildirim deyiminde, tr belirten anahtar szck birden fazla ise bunlarn yazm sras nemli deildir. Ama okunabilirlik asndan nce iaret belirten anahtar szcn sonra tr belirten anahtar szcn kullanlmas gelenek haline gelmitir. rnein : signed long int signed int long long signed int long int signed int long signed int signed long x; x; x; x; x; x;

bildirimlerinin hepsi geerlidir. Seimlik olan anahtar szckler zellikle kullanlmak isteniyorsa birinci yazm biimi okunabilirlik asndan tercih edilmelidir.

Bildirimlerin Kaynak Kod indeki Yerleri

C dilinde genel olarak 3 yerde deiken bildirimi yaplabilir : 1. Bloklarn iinde 2. Tm bloklarn dnda. 3. levlerin parametre deikeni olarak, ilev parametre ayralarnn iinde. lev parametre ayralar iinde yaplan bildirimler, baka bir szdizim kuralna uyar. Bu bildirimleri "ilevler" konusunda ayrntl olarak ele alacaz. C dilinde eer bildirim bloklarn iinde yaplacaksa, bildirim ilemi bloklarn ilk ilemi olmak zorundadr. Baka bir deyile bildirimlerden nce, bildirim deyimi olmayan baka bir deyimin bulunmas geersizdir. Bir bildirimin mutlaka ilevin ana blounun banda yaplmas gibi bir zorunluluk yoktur. Eer i ie bloklar varsa iteki herhangi bir bloun banda da yani o bloun ilk ilemi olacak biimde, bildirim yaplabilir. rnekler : { int var1, var2; char ch1, ch2, ch3; var1 = 10; float f; /* Geersiz */

} Yukardaki rnekte var1, var2, ch1, ch2, ch3 deikenlerinin tanmlanma yerleri dorudur. Ancak f deikeninin bildirimi geersizdir. nk bildirim deyiminden nce bildirim deyimi olmayan baka bir deyim yer alyor. Bu durum geersizdir. Ayn program paras u ekilde yazlm olsayd bir hata sz konusu olmazd :

43/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

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

} Bu durumda artk f deikeni de kendi blounun banda tanmlanm olur. C dilinde tek bana bir sonlandrc atom bir deyim oluturur. Ve byle bir deyime, bo deyim (null statement) denir. C szdizimine gre oluan bu deyim yrtlebilir bir deyimdir. Dolaysyla aadaki kod parasnda y deikeninin tanmlamas derleme geersizdir. { } int x;; int y; /* Geersiz! */

Yukardaki kod parasnda x deikeninin bildiriminden sonra yer alan sonlandrc atom yrtlebilir bir deyim olarak ele alnr. Bu durumda y deikeninin bildirimi geersizdir. Ayn ekilde ii bo bir blok da C dilinde bir yrtlebilir deyim olarak ele alnr. Bu yazm, sonlandrc atomun tek bana kullanlmasna tamamen edeerdir. Aadaki kod paras da geersizdir: { int x; { } int y;

/* Geersiz! */

Bir ya da birden fazla deyimin bir blok iine alnmasyla elde edilen yap C dilinde bileik deyim (compound statement) ismini alr. Bileik deyimler de yrtlebilir deyimlerdir. Aadaki kod paras da geersizdir: { } {int x;} int y;

/* Geersiz */

[C++ dilinde blok iinde bildirimi yaplan deikenlerin blok balarnda bildirilmeleri zorunlu deildir. Yani C++'da deikenler bloklarn iinde herhangi bir yerde bildirilebilir.]

Tanmlanan Deikenlere lkdeer Verilmesi

Bir deiken tanmlandnda bu deikene bir ilkdeer verilebilir (initialize). Bu zel bir szdizim ile yaplr: int a = 20; lkdeer verme deyimi bir atama deyimi deildir, bir bildirim deyimidir. Bu deyim ile, programn alma zamannda bir deikenin nceden belirlenen bir deer ile hayata balamas salanr. Yukardaki bildirimi gren derleyici a deikenini 20 deeri ile balatacak bir kod retir. lkdeer verme deyiminde atama ilecinin sa tarafnda kullanlan ifadeye "ilkdeer verici" (initializer) denir. Bir bildirim deyimi ile birden fazla deikene de ilkdeer verilebilir: int a = 10, b = 20, c = 30;

44/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki deyimle a deikenine 10, b deikenine 20, c deikenine 30 ilkdeerleri veriliyor. lkdeer verme deyimi bir atama deyimi deildir: void func() { int a; a = 20; int b; /* Geersiz! */ /****/ } Yukardaki kod parasnda b deikeninin tanm geersizdir. nk b deikeninin tanmndan nce blok iinde yrtlebilir bir deyim yazlmtr. Ancak aadaki kod geerlidir: void func() { int a = 20; int b; /* Geerli! */ /****/ }

45/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DEMEZLER
Veriler ya nesnelerin iinde ya da dorudan deimez (sabit - constant) biiminde bulunur. Deimezler nesne olmayan, programc tarafndan dorudan saysal byklk olarak girilen verilerdir. Deimezlerin saysal deerleri derleme zamannda tam olarak bilinir. rnein: x = y + z; ifadesi bize y ve z iindeki deerlerin toplanacan ve sonucun x deikenine aktarlacan anlatr. Oysa d = x + 10; ifadesinde x deikeni iinde saklanan deer ile 10 says toplanmtr. Burada 10 says herhangi bir deikenin iindeki deer deildir, dorudan say biiminde yazlmtr.

Deimezlerin Trleri

Nesnelerin trleri olduu gibi deimezlerin de trleri vardr. Nesnelerin trleri bildirim yaplrken belirlenir. Deimezlerin trlerini ise derleyici, belirli kurallara uyarak deimezlerin yazl biimlerinden saptar. Deimezlerin trleri nemlidir, nk C dilinde deimezler, deikenler ve ileler bir araya getirilerek ifadeler (expressions) oluturulur. C'de ifadelerin de tr vardr. fadelerin trleri, ierdikleri deimez ve deikenlerin trlerinden elde edilir.

Tamsay deimezlerinin (integer constants) deerleri tamsaydr. Bir tamsay deimezi signed int unsigned int signed long unsigned long trlerinden olabilir. Bir tamsay deimezinin tr, yazmnda kullanlan say sistemine ve deimezin ald soneke gre belirlenir.

Tamsay Deimezleri

Varsaylan yazm onluk say sistemidir. Onaltlk say sisteminde yazm: 0Xbbb.. ya da 0xbbb..

Tamsay Deimezlerinin Onluk Onaltlk ve Sekizlik Say Sistemlerinde Yazm

biimindedir. Burada b karakterleri onaltlk say sistemindeki basamaklar gsteriyor. 9'dan byk basamak deerleri iin A, B, C, D, E, F karakterleri ya da a, b, c, d, e, f karakterleri kullanlr. Sekizlik say sisteminde yazm: 0bbbb..

47/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

biimindedir. Burada b karakterleri sekizlik say sistemindeki basamaklar (0 1 2 3 4 5 6 7) gsteriyor.

Tamsay Deimezlerinin Ald Sonekler

Bir tamsay deimezi, hangi say sisteminde yazlrsa yazlsn, u, U, l ya da L soneklerini alabilir. u ya da U sonekleri tamsay deimezinin iaretsiz tamsay trnden olduunu belirler. l ya da L sonekleri tamsay deimezinin long trden olduunu belirler. l soneki 1 karakteri ile grsel bir karkla neden olabileceinden, 'L' soneki kullanlmaldr. Bir karakter deimezi hem u, U soneklerinden birini hem de l, L soneklerinden birini alabilir. Bu durumda soneklerin yazm srasnn bir nemi yoktur.

Tamsay deimezinin tr aadaki tabloya gre belirlenir: Yazm biimi bb...b 0xbb...b 0bb...b bb...bU 0bb...bU 0Xbb...bU bb...bL 0bb..bL 0xbb..bL bb...bUL 0bb..bUL 0xbb..bUL tr signed int signed long unsigned long signed int unsigned int signed long unsigned long unsigned int unsigned long signed long unsigned long unsigned long

Tamsay Deimezlerinin Trleri

Yukardaki tablo yle yorumlanmaldr: Belirli bir yazm iin verilen trlerden, yukardan aa doru olmak zere, tama olmakszn ilgili deeri tutabilecek ilk tr, deimezin trdr. Aadaki rnekleri inceleyin: int trnn ve long trnn 2 byte olduu sistemler iin (DOS) rnekler Deimez 456 59654 125800 3168912700 0X1C8 0XE906 0X1EB68 0XBCE1C53C 0710 0164406 0365550 027470342474 987U 45769U 1245800U 3589456800U 0X3DBU int tr 2 byte long tr 4 byte (DOS) signed int signed long signed long unsigned long signed int unsigned int signed long unsigned long signed int unsigned int signed long unsigned long unsigned int unsigned int unsigned long unsigned long unsigned int int tr ve long tr 4 byte (Windows - Unix) signed int signed int signed int unsigned long signed int signed int signed int unsigned long signed int signed int signed int unsigned long unsigned int unsigned int unsigned int unsigned int unsigned int

48/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

0XB2C9U 0X130268U 0XD5F2C3A0U 01733U 0131311U 04601150U 032574541640U 25600L 3298780970L 0X6400L 0Xc49F672AL 062000L 030447663452L 890765UL 0XD978DUL 03313615UL

unsigned int unsigned long unsigned long unsigned int unsigned int unsigned long unsigned long signed long unsigned long signed long unsigned long signed long unsigned long unsigned long unsigned long unsigned long

unsigned int unsigned int unsigned int unsigned int unsigned int unsigned int unsigned int signed long unsigned long signed long unsigned long signed long unsigned long unsigned long unsigned long unsigned long

Karakter deimezleri tipik olarak char trden nesnelere atanan deimezlerdir. Karakter deimezleri C'de farkl biimlerde bulunabilir. i. Bir karakterin grnts tek trnak (single quote) iinde yazlrsa derleyici tarafndan dorudan karakter deimezi olarak ele alnr. rnek : 'a' 'J' '' ':' '8' '<' Yukardaki atomlarn her biri birer karakter deimezidir. C'de tek trnak iinde yazlan char trden deimezler, aslnda o karakterin (kullanlan sistemin karakter kodundaki (rnein ASCII)) kod numarasn gsteren bir tamsaydr. char ch; ch = 'a'; /***/ ASCII karakter kodlamasnn kullanldn dnelim. Bu rnekte aslnda ch isimli char trden bir deikene 'a' karakterinin ASCII tablosundaki kod numaras olan 97 deeri aktarlr. Tek trnak iindeki karakter deimezlerini grnce aslnda onlarn kk birer tamsay olduu bilinmelidir. Yukardaki rnekte istenirse ch deikenine aadaki gibi bir atama yaplabilir: ch = 'a' + 3; Bu durumda ch deikenine saysal olarak 100 deeri atanr. Bu tamsay da ASCII tablosundaki 'd' karakterinin kod numarasdr.

Karakter Deimezleri

Karakter deimezlerinin dier yazmlarnda tek trnak iinde ters bl '\' karakteri ve bunu izleyen baka karakter(ler) kullanlr. ngilizce de bu biimlere "escape sequence" denir.

Ters Bl Karakter Deimezleri

49/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Tek trnak iindeki ters bl karakterinden sonra yer alan baz karakterler ok kullanlan baz karakterlerin yerlerini tutar. Bunlarn listesi aada veriliyor:

nceden Tanmlanm Ters Bl Karakter Deimezleri


'\0' '\a' '\b' '\t' '\n' '\v' '\f' '\r' '\"' '\'' '\?' '\\' Tanm sonlandrc karakter (null character) an sesi (alert) geri boluk (back space) tab karakteri (tab) aa satr (new line) dey tab (vertical tab) sayfa ileri (form feed) satr ba (carriage return) ift trnak (double quote) tek trnak (single quote) soru iareti (question mark) ters bl (back slash) ASCII No 0 7 8 9 10 11 12 13 34 39 63 92

Kullanllarna bir rnek : char ch; ch = '\a';

Tek trnak iinde ters bl ve 'x' karakterlerinden sonra onaltlk (hexadecimal) say sisteminde bir say yazlrsa bu sistemin kulland karakter setinde, o saysal deerin gsterdii kod numaral karakter deimezidir. '\x41' /* 41H kod numaral karakterdir. */ '\xff' /* FFH kod numaral karakterdir. */ '\x1C' /* 1C kod numaral karakterdir. */ Aada harf isimli char trden deikene 41H deeri atanyor: char harf; harf = '\x41'; Bu da onluk say sisteminde 65 deeridir. ASCII karakter setinin kullanldn varsayalm. 65 kod nolu ASCII karakteri 'A' karakteridir. Dolaysyla harf isimli deikene 'A' atanm olur.

Onaltlk Say Sisteminde Yazlan Karakter Deimezleri

Tek trnak iinde ters bl karakterinden sonra sekizlik say sisteminde bir deer yazlrsa, bu kullanlan karakter setindeki o saysal deerin gsterdii kod numaral karaktere iaret eden bir karakter deimezidir. Tek trnak iindeki ters bl karakterini izleyen sekizlik sistemde yazlm say basamaktan uzun olmamaldr. Sekizlik sistemde yazlan saynn banda sfr rakam olma zorunluluu yoktur. '\012' /* 10 numaral ASCII karakteri, Tam say deeri 10 */ '\16' /* 14 numaral ASCII karakteri. Tam say deeri 14 */ '\123' /* 83 numaral ASCII karakteri. Tam say deeri 83 */ Program iinde kullanmna bir rnek:

Sekizlik Say Sisteminde Yazlan Karakter Deimezleri

50/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char a, b; a = '\xbc' ; b = '\012'; /* onaltlk say sisteminde yazlm karakter deimezi */ /* sekizlik say sisteminde yazlm karakter deimezi */

rnein, 7 numaral ASCII karakteri olan an sesi karakteri, deimez olarak ayr biimde de yazlabilir: '\x7' '\07' '\a' /* hex gsterimli karakter deimezi */ /* oktal gsterimli karakter deimezi */ /* nceden belirlenmi ters bl karakter deimezi */

Burada tercih edilecek biim son biim olmaldr. Hem tanabilir bir biimdir hem de okunabilirlii daha iyidir. Baka karakter setlerinde an sesi karakteri 7 sra numaral karakter olmayabilir, ama nceden belirlenmi ters bl karakter deimezi eklinde ifade edersek hangi sistem olursa olsun an sesi karakterini verir. Ayrca kodu okuyan an sesi karakterinin 7 numaral ASCII karakteri olduunu bilmeyebilir, ama '\a' nn an sesi karakteri olduunu bilir. Karakter deimezleri konusunu kapatmadan nce karakter setleri konusunda da biraz bilgi verelim: Gnmzde en ok kullanlan karakter seti ASCII karakter setidir. ASCII (American Standard Code for Information Interchange) szcklerinin ba harflerinden oluan bir ksaltmadr. ASCII karakter kodunda karakterler 7 bitlik bir alanda kodlanmtr. Baz bilgisayarlar ise 8 bit alana geniletilmi kodlama kullanrlar ki bu sette 128 yerine 256 karakter temsil edilebilir. Farkl bilgisayarlar farkl karakter kodlamas kullanabilir. rnek olarak IBM mainframe leri daha eski olan EBCDIC kodlamasn kullanr. Unicode ismi verilen daha gelitirilmi bir karakter kodlamas vardr ki karakterler 2-4 byte'lk alanda temsil edildikleri iin bu kodlamada dnyada var olan tm karakterlerin yer almas hedeflenmitir. Gelecekte birok makinenin bu karakter kodlamasn destekleyecek biimde tasarlanaca dnlyor.

Karakter deimezleri tamsay deimezleridir. Ancak C'de daha ok bir yaz bilgisi ile ilgili kullanrlar. Yazlar karakter deimezleri ile deerlerini alabilecekleri gibi, bir yaznn deitirilmesi amacyla karakter deimezleri kullanlabilir.

Karakter Deimezleri Nerede Kullanlr

C'de karakter deimezleri int trden olarak ele alnr ve ileme sokulur. Bu konu "tr dnmleri" blmnde ele alnacak.

Karakter Deimezleri int Trdendir

Gerek Say Deimezleri

Gerek say deimezleri (floating constants) deerleri gerek say olan deimezlerdir. C dilinde bir gerek say deimezi float, double ya da long double trden olabilir. C89 standartlarna gre bir gerek say deimezi yalnzca onluk say sistemi kullanlarak yazlabilir.

float Trden Deimezler


Nokta ieren,'f' ya da 'F' soneki alm deimezler, float trdendir. rnein: 1.31F 10.F -2.456f float trden deimezlerdir.

51/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

double Trden Deimezler


Nokta ieren 'f' ya da 'F' soneki almam deimezler ile float tr snrn ya da duyarln am deimezler double trden deimezler olarak deerlendirilir. rnein: -24.5 double trden deimezdir.

long double Trden Deimezler


long double trden deimezler noktal ya da stel biimdeki saylarn sonuna 'l' ya da 'L' getirilerek elde edilir: 1.34L 10.2L long double trden deimezlerdir.

Gerek say deimezleri stel biimde (scientific notation) yazlabilir. Bunun iin deimezin sonuna 'e' ya da 'E' eki getirilir. Bu, saynn on zeri bir arpanla arpldn gsterir. 'E' ya da 'e' karakterlerini '+', '-' ya da dorudan bir rakam karakteri izleyebilir. 2.3e+04f 1.74e-6F 8.e+9f burada e, 10 'un ss anlamna gelir: 1.34E-2f ile 0.0134 -1.2E+2F ile 120.f ayn deimezlerdir.

Gerek Say Deimezlerinin stel Biimde Yazlmas

52/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

LEVLER
C'de alt programlara ilev (function) denir. ngilizcedeki "function" szc bu balamda matematiksel anlamyla deil dier programlama dillerinde ya da ortamlarnda kullanlan, "alt program", "prosedr", szcklerinin karl olarak kullanlr. Bir ilev, bamsz olarak altrlabilen bir program parasdr.

lev Nedir?

Program levlere Blerek Yazmann Faydalar

Bir program alt programlara yani ilevlere blerek yazmak baz faydalar salar: 1. Programn kaynak kodu klr. Bylece oluturulmas hedeflenen alabilir dosya da (rnein .exe uzantl dosya) klr. 2. Kaynak dosyann okunabilirlii artar. Okunabilirliin artmas, kodu yazann ve okuyann iini kolaylatrr. Bylece proje gelitirme sresinin azalmas ynnde kazanm salanm olur. 3. Belirli kod paralarnn programn farkl yerlerinde yinelenmesi, programda yaplacak olas bir deiikliin maliyetini artrr. Programn farkl yerlerinde, kodun kullanld yere bal olarak deiiklikler yapmak gerekir. Kaynak dosyalarda byle deiiklikler yapmak hem zaman alcdr hem de risklidir. nk bir deiikliin yaplmasnn unutulmas durumunda ya da deiiklik yaplmamas gereken bir yerde kodun deitirilmesi durumunda program yanl alabilir. Oysa ortak kod paralar ilevler eklinde paketlendiinde, yalnzca ilevlerde deiiklik yaplmasyla, istenen deiiklik gerekletirilmi olur. 4. Programda hata arama daha kolay gerekletirilir. Projelerdeki hata arama maliyeti azalr. 5. Yazlan ilevler baka projelerde de kullanlabilir. Alt programlar tekrar kullanlabilir (reusable) bir birim olutururlar. Bylelikle de projelerdeki kodlama giderlerini azaltrlar. levler C'nin temel yap talardr. altrlabilen bir C program en az bir C ilevinden oluur. Bir C programnn oluturulmasnda ilev saysnda bir kstlama yoktur. levlerin onlar aran ilevlerden aldklar girdileri ve yine onlar aran ilevlere gnderdikleri ktlar vardr. levlerin girdilerine aktel parametreler (actual parameters) ya da argmanlar (arguments) denir. levlerin ktlarna ise geri dn deeri (return value) diyoruz. Bir ilev balca iki farkl amala kullanlabilir: 1. lev, almas sresince belli ilemleri yaparak belirli amalar gerekletirir. 2. lev, almas sonunda retecei bir deeri kendisini aran ileve gnderebilir.

Bir ilevin ne i yapacann ve bu ii nasl yapacann C dilinin szdizimi kurallarna uygun olarak anlatlmasna, yani o ilevin C kodunun yazlmasna, o ilevin tanm (definition) denir. lev tanmlamalar C dilinin szdizimi kurallarna uymak zorundadr. Bir ilev ars (call / invocation) ise o ilevin kodunun almaya davet edilmesi anlamna gelir. lev ar ifadesi karlnda derleyici, programn akn ilgili ilevin kodunun bulunduu blgeye aktaracak ekilde bir kod retir. Programn ak ilevin kodu iinde akp bu kodu bitirdiinde, yani ilevin almas bittiinde, programn ak yine ilevin arld noktaya geri dner. Bir ileve yaplacak ar da yine baz szdizimi kurallarna uymaldr.

levlerin Tanmlanmas ve arlmas

53/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir ilevin yrtlmesi sonunda onu aran ileve gnderdii deere, ilevin geri dn deeri (return value) denir. Her ilev bir geri dn deeri retmek zorunda deildir. Bir ilev yapaca bir ile ilgili olarak bir geri dn deeri retir ya da retmez. levlerin geri dn deerleri farkl amalar iin kullanlabilir: 1. Baz ilevler zaten tek bir deeri elde etmek, tek bir deeri hesaplamak amacyla tanmlanrlar. Elde ettikleri deeri de kendilerini aran ilevlere geri dn deeri olarak iletirler. Bir kpn hacim deerini bulan bir ilev tanmladmz dnelim. Byle bir ilev, hacmini bulaca kpn kenar uzunluunu arld yerden alr, bu deeri kullanarak hacim deerini hesap eder, hesap ettii deeri darya geri dn deeri olarak iletebilir. 2. Her ilevin amac bir deer hesaplamak deildir. Baz ilevler ise arlmalaryla kendilerine sorulan bir soruya yant verirler. rnein bir saynn asal olup olmadn snayan bir ilev tanmlandn dnelim. lev arld yerden, asalln snayaca deeri alr. Tanmnda bulunan baz kodlar ile saynn asal olup olmadn snar. Saynn asal ya da asal olmamasna gre darya iki farkl deerden birini geri dn deeri olarak gnderebilir. Bu durumda ilevin geri dn deeri, hesap edilen bir deer deil, sorunun yant olarak yorumlanacak bir deerdir. 3. Baz ilevler ise ne bir deeri hesaplamak ne de bir soruya yant vermek iin tanmlanrlar. Tanmlanma nedenleri yalnzca bir i yapmaktr. Ancak ilevin yapmas istenen iin, baaryla yaplabilmesi konusunda bir garanti yoktur. rnein bir dosyay amak iin bir ilev tanmlandn dnelim. lev arld yerden alacak dosyann ismi bilgisini alyor olabilir. Ancak dosyann alabilmesi eitli nedenlerden dolay gvence altnda deildir. arlan ilev istenen dosyay ya aar ya aamaz. lev geri dn deeriyle yapt iin baars hakknda bilgi verir. Bu durumda ilevin geri dn deeri, hesap edilen bir deer deil, yaplmas istenen iin baars konusunda verilen bir bilgi olarak yorumlanr. 4. Baz ilevler hem belli bir amac gerekletirirler hem de buna ek olarak amalarn tamamlayan bir geri dn deeri retirler. Bir yaz iinde bulunan belirli bir karakteri silecek bir ilev tasarlandn dnelim. levin varlk nedeni yaznn iinden istenen karakterleri silmektir. arld yerden, silme yapaca yazy ve silinecek karakterin ne olduu bilgisini alr ve iini yapar. Ancak iini bitirdikten sonra yazdan ka karakter silmi olduunu geri dn deeri ile arld yere bildirilebilir. 5. Baz ilevlerin ise hi geri dn deerleri olmaz. i) levin amac yalnzca bir ii gerekletirmektir, yapt iin baars gvence altndadr. rnein yalnzca ekran silme amacyla tasarlanm olan bir ilevin geri dn deerine sahip olmas gereksizdir. Sistemlerin ounda kt ekrannn silinmesi konusunda bir baarszlk riski yoktur. ii) lev darya bir deer iletir ancak deeri iletme iini geri dn deeri ile deil de baka bir arac kullanarak gerekletirir. levlerin geri dn deerlerinin de trleri sz konusudur. levlerin geri dn deerleri herhangi bir trden olabilir. Geri dn deerlerinin trleri, ilevlerin tanmlanmas srasnda belirtilir.

levlerin Geri Dn Deerleri

levlerin kodunun yazlmas iin tanmlama (definition) terimi kullanlr. C'de ilev tanmlama ileminin genel biimi yledir: [Geri dn deerinin tr] <ilev ismi> ([parametreler]) { /***/ }

levlerin Tanmlanmas

54/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki gsterimde bulunmas zorunlu szdizim elemanlar asal ayra iinde, bulunmas zorunlu olmayan szdizim elemanlar ise keli ayra iinde belirtilmitir. Tanmlanan ilevler en az bir blok iermelidir. Bu bloa ilevin ana blou denir. Ana blok iinde istenildii kadar i ie blok yaratlabilir. Aadaki ilev tanmndan func isimli ilevinin parametre almad ve geri dn deerinin de double trden olduu anlalr. double func() { } Yukardaki tanm inceleyin. nce ilevin geri dn deerinin trn gsteren anahtar szck yazlr. Bildirim ve tanmlama konusunda anlatlan C'nin doal trlerini belirten anahtar szck(ler) ile ilevin hangi trden bir geri dn deeri rettii belirtilir. Yukarda tanmlanan func isimli ilevin geri dn deeri double trdendir. Daha sonra ilevin ismi yazlr. levin ismi C dilinin isimlendirme kurallarna uygun olarak seilmelidir. Geleneksel olarak ilev isimleri de, deiken isimleri gibi kk harf youn olarak seilirler. lev ismini izleyen, alan ve kapanan ayralara ilevin parametre ayralar denir. Bu ayracn iinde, ilevin parametre deikenleri denen deikenlerin bildirimi yaplr. func isimli ilevin parametre ayracnn iinin bo braklmas bu ilevin parametre deikenine sahip olmadn gsteriyor. Parametre ayracn alan ve kapanan kme ayralar, yani bir blok izliyor. te bu bloa da ilevin ana blou (main block) denir. Bu bloun iine ilevin kodlar yazlr.

Tanmlanan bir ilevin bir geri dn deeri retmesi zorunlu deildir. lev tanmnda bu durum geri dn deerinin trnn yazld yere void anahtar szcnn yazlmasyla anlatlr: void func() { } Yukarda tanmlanan func ilevi geri dn deeri retmiyor. Geri dn deeri retmeyen ilevlere void ilevler denir. lev tanmnda geri dn deerinin tr bilgisi yazlmayabilir. Bu durum, ilevin geri dn deeri retmedii anlamna gelmez. Eer geri dn deeri tr bilgisi yazlmaz ise, C derleyicileri tanmlanan ilevin int trden bir geri dn deerine sahip olduunu varsayar. rnein: func() { } Yukarda tanmlanan func ilevinin geri dn deerinin tr int trdr. Yani ilevin yukardaki tanmyla int func() { } tanm arasnda derleyici asndan bir fark yoktur. Geri dn deerinin trnn yazlmamas gemie doru uyumluluk iin korunan bir kuraldr. int trne geri dnen bir ilevin tanmnda int szcnn yazlmas tavsiye edilir. [C++ dilinde ilev tanmnda geri dn deerinin trnn yazlmas zorunludur.]

void Anahtar Szc

55/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C dilinde bir ilev tanm iinde bir baka ilev tanmlanamaz. Yani isel ilev tanmlamalarna izin verilmez. rnein aadaki gibi bir tanmlama geersizdir, nk func ilevi tanmlanmakta olan foo ilevinin iinde tanmlanyor: double foo() { /***/ int func() /* Geersiz */ { /***/ } /***/ } Tanmlamann aadaki biimde yaplmas gerekirdi: double foo() { /***/ } int func() { /***/ }

levlerin Tanmlanma Yerleri

C dilinde ilevlerin geri dn deerleri return deyimi (return statement) ile oluturulur. return deyiminin iki ayr biimi vardr: return; Ya da return anahtar szcn bir ifade izler: return x * y; return anahtar szcnn yanndaki ifadenin deeri, geri dn deeri olarak, ilevi aran kod parasna iletilir. return ifadesinin deiken iermesi bir zorunluluk deildir. Bir ilev bir deimez deerle de geri dnebilir. return 1; return deyiminin bir baka ilevi de iinde bulunduu ilevi sonlandrmasdr. Bir ilevin kodunun yrtlmesi srasnda return deyimi grldnde ilevin almas sona erer. int func() { /**/ return x * y; } Yukardaki rnekteki func ilevinde return anahtar szcnn yannda yer alan x * y ifadesi ile oluturulan return deyimi, func ilevini sonlandryor, func ilevinin bir geri dn deeri retmesini salyor.

levlerin Geri Dn Deerlerinin Oluturulmas

56/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Baz programclar return ifadesini bir ayra iinde yazarlar. Bu ayra return deyimine ek bir anlam katmaz. Yani return x * y; gibi bir deyim return (x * y); biiminde de yazlabilir. Okunabilirlik asndan zellikle uzun return ifadelerinde ayra kullanm salk verilir. return (a * b - c * d); Bir ilevin tanmnda ilevin geri dn deeri tr yazlmsa, bu ilevin tanm iinde return deyimiyle bir geri dn deeri retilmelidir. Bu mantksal bir gerekliliktir. Ancak return deyimiyle bir geri dn deeri retilmemesi derleme zaman hatasna neden olmaz. Bu durumda ilevin almas ilevin ana blounun sonuna gelindiinde sona erer , ilev arld yere bir p deeri iletir. Bu da istenmeyen bir durumdur. C derleyicilerinin ou, geri dn deeri retecei yolunda bilgi verilen bir ilevin return deyimiyle bir deer retmemesini mantksal bir uyar iletisiyle iaretler. "Warning: Function func should return a value" Geri dn deeri retmeyen ilevlerde, yani void ilevlerde, return anahtar szc yannda bir ifade olmakszn tek bana da kullanlabilir: return; Bu durumda return deyimi iinde yer ald ilevi, geri dn deeri oluturmadan sonlandrr. C dilinde ilevler yalnzca tek bir geri dn deeri retebilir. Bu da ilevlerin kendilerini aran ilevlere ancak bir tane deeri geri gnderebilmeleri anlamna gelir. Ancak, ilevlerin birden fazla deeri ya da bilgiyi kendilerini aran ilevlere iletmeleri gerekiyorsa, C dilinde bunu salayacak baka aralar vardr. Bu aralar daha sonraki blmlerde ayrntl olarak greceksiniz.

main de dier ilevler gibi bir ilevdir, ayn tanmlama kurallarna uyar. C programlarnn almas, ismi main olan ilevden balar. C programlar zel bir ilem yaplmamsa, main ilevinin almasnn bitiiyle sonlanr. main ilevine sahip olmayan bir kaynak dosyann derlenmesinde bir sorun kmaz. Ancak balama (linking) aamasnda balayc main ilevinin bulunmadn grnce balama ilemini gerekletiremez. Balayc programlar bu durumda bir hata iletisi verir. int main() { return 0; } Biiminde tanmlanm bir main ilevi de int trden bir deer dndrmelidir. main ilevinin rettii geri dn deeri, programn almas bittikten sonra iletim sistemine iletilir. Geleneksel olarak, main ilevinin 0 deerine geri dnmesi programn sorunsuz bir ekilde sonlandrld anlamna gelir. main ilevinin 0 dnda bir deere geri dnmesi ise, kodu okuyan tarafndan programn baarszlkla sona erdirildii biiminde yorumlanr. Yani baz nedenlerle yaplmak istenenler yaplamam, bu nedenle main ilevi sonlandrlmtr.

main levi

57/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

main ilevi geri dn deeri reteceini bildirmi olmasna karn return deyimiyle belirli bir deeri geri dndrmezse, main ilevinden de bir p deeri gnderilir. Derleyicilerin ou, main ilevinin geri dn deeri retmemesi durumunda da bir mantksal uyar iletisi retir. Dier taraftan main ilevi de void bir ilev olarak tanmlanabilir: void main() { } Ancak bir szdizimi hatas olmamasna karn main ilevinin void bir ilev olarak tanmlanmas doru kabul edilmez.

C dilinde bir ilev ars (function call - function invocation), ismi ilev ar ileci olan bir ile ile yaplr. lev ar ileci olarak () atomlar kullanlr. arlacak ilevin ismi bu ileten nce yazlr. func(); Yukarda deyim ile func isimli ilev arlr. Bir ilev arld zaman programn ak, ilevin kodunun yrtlmesi iin bellekte ilevin kodunun bulunduu blgeye atlar. levin kodunun altrlmas ilemi bittikten sonra da ak yine aran ilevin kalnan yerinden srer. Bir ilevin geri dn deeri varsa, ilev ar ifadesi, ilevin geri dn deerini retir. Geri dn deeri reten bir ileve yaplan ar ifadesi sz konusu ilevin rettii geri dn deerine edeerdir. levin geri dn deeri bir deikene atanabilecei gibi dorudan aritmetik ilemlerde de kullanlabilir. rnein: sonuc = hesapla(); Burada hesapla ilevine yaplan ar ifadesiyle retilen geri dn deeri, sonuc isimli deikene atanr. Bir baka deyile bir ilev ar ifadesinin rettii deer, ilgili ilevin rettii (eer retiyorsa) geri dn deeridir. Yukardaki rnekte nce hesapla() ilevi arlr, daha sonra ilevin kodunun altrlmasyla elde edilen geri dn deeri sonuc deikenine atanr. lev ar ifadeleri nesne gstermez yani sol taraf deeri (L value) deildir. Yani C dilinde aadaki gibi bir atama deyimi geersizdir: func() = 5; /* Geersiz */

levlerin arlmas

levlerin geri dn deerleri sa taraf deeridir. sonuc = func1() + func2() + x + 10; gibi bir ifade geerlidir. arlm olan func1 ve func2 ilevleri altrlarak retilen geri dn deerleri ile x deikeni iindeki deer ve 10 deimezi toplanr. fadeden elde edilen deer, sonuc isimli deikene atanr.

lev arlarnn Yeri

levler, ancak tanmlanm ilevlerin iinden arlabilirler. Bloklarn dndan ilev arlamaz. [C++ dilinde blok dnda yazlan ilkdeer verme deyimlerinde ilkdeer verici (initializer) ifade bir ilev ars olabilir.]

58/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

aran ilev ile arlan ilevin her ikisi de ayn ama kod (object code) iinde bulunmak zorunda deildir. aran ilev ile arlan ilev farkl ama kodlar iinde bulunabilir. nk derleme ilemi srasnda bir ilevin arldn gren derleyici, ama kod iine arlan ilevin adn ve ar biimini yazar. aran ilev ile arlan ilev arasnda balant kurma ilemi, balama aamasnda balayc program (linker) tarafndan yaplr. Bu nedenle tanmlanan bir ilev iinde, tanml olmayan bir ilev arlsa bile derleme aamasnda bir hata olumaz. Hata balama aamasnda oluur. nk balayc arlan ilevi bulamaz.

levler arldklar yerlerden alacaklar bilgileri, ilev ar ifadeleri ile alrlar. Bir ilevin formal parametreleri (formal parameters) ya da parametre deikenleri, ilevlerin kendilerini aran ilevlerden aldklar girdileri tutan deikenleridir. Bir ilevin parametre says ve bu parametrelerin trleri gibi bilgiler, ilevlerin tanmlanmas srasnda derleyiciye bildirilir. lev ars ile gnderilen argman ifadelerin deerleri, ilevin ilgili parametre deikenlerine kopyalanr. rnein bir kpn hacmini hesaplayan ilev, arld yerden bir kpn kenar uzunluunu alacana gre, bu deerin kopyalanmas iin, bir parametre deikenine sahip olmas gerekir. Benzer ekilde iki saydan daha byk olann bulan bir ilevin iki tane parametre deikenine sahip olmas gerekir. C dilinde ilevlerin tanmlanmasnda kullanlan iki temel biim vardr. Bu biimler birbirlerinden ilev parametrelerinin derleyicilere tantlma ekli ile ayrlrlar. Bu biimlerden birincisi eski biim (old style) ikincisi ise yeni biim (new style) olarak adlandrlr. Eski biim hemen hemen hi kullanlmaz, ama C standartlarna gre halen geerlidir. Bu biimin korunmasnn nedeni gemie doru uyumluluun salanmasdr. Kullanlmas gereken kesinlikle yeni biimdir. Ancak eski kodlarn ya da eski kaynak kitaplarn incelenmesi durumunda bunlarn anlalabilmesi iin eski biimin de renilmesi gerekir.

lev Parametre Deikenlerinin Tanmlanmas

Eski Biim

Eski biimde (old style), ilevin parametre deikenlerinin yalnzca isimleri, ilev parametre ayralar iinde yazlr. Eer parametre deikenleri birden fazla ise aralarna virgl atomu koyulur. Daha sonra bu deikenlerin bildirimi yaplr. Bu bildirimler daha nce rendiimiz, C dilinin bildirim kurallarna uygun olarak yaplr. rnek: double alan(x, y) double x, y; { return x * y; } Yukarda tanmlanan alan ilevinin iki parametre deikeni vardr ve bu parametre deikenlerinin isimleri x ve y'dir. Her iki parametre deikeni de double trdendir. levin geri dn deeri double trdendir. int func (a, b, c) int a; double b; long c; { /***/ } Bu rnekte ise func ilevi parametre deikenine sahiptir. Parametre deikenlerinin isimleri a, b ve c'dir. smi a olan parametre deikeni int trden, b olan double trden ve ismi c olan ise long trdendir.

59/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Eski biim, yeni biime gre uzundur. nk ilev ayralarnn iinde ismi yer alan parametre deikenleri alt satrlarda yeniden bildirilir.

Yeni Biim

Yeni biimde (new style), eski biime gre hem daha ksadr hem de okunabilmesi eski biime gre ok daha kolaydr. Yeni biimde ilev parametre deikenlerinin bildirimi ilev ayralarnn iinde yalnzca bir kez yaplr. Bu biimde, ilevin ayralarnn iine parametre deikenin tr ve yanna da ismi yazlr. Eer birden fazla ilev parametre deikeni varsa bunlar virgllerle ayrlr, ancak her bir deikenin tr bilgisi yeniden yazlr. rnek : int func(int x, int y) { /***/ } double foo(double a, int b) { /***/ } lev parametre deikenleri ayn trden olsa bile her defasnda tr bilgisinin yeniden yazlmas zorunludur. rnein: int foo (double x, y) { /***/ } /* Geersiz */

bildirimi hataldr. Doru tanmlama aadaki biimde olmaldr: int foo (double x, double y) { /***/ } [C++ dilinde eski biim ilev tanmlamalar geerli deildir.]

Her ilev parametre deikenine sahip olmak zorunda deildir. Baz ilevler istenen bir ii yapabilmek iin dardan bilgi almaz. Parametre deikenine sahip olmayan bir ilevin tanmnda, ilev parametre ayracnn ii bo braklr. lev parametre ayracnn iine void anahtar szcnn yazlmas durumunda da ilevin parametre deikenine sahip olmad sonucu kar. int foo() { /***/ } ile int foo(void) { /***/ }

Parametre Deikenine Sahip Olmayan levler

60/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

tamamen ayn anlamdadr

Bir ilevin parametre deikenleri, o ilevin arlma ifadesiyle kendisine gnderilen argmanlar tutacak olan yerel deikenlerdir. rnek: void func(int a) { /***/ } int main() { int x = 10; /***/ func (x); } return 0;

Argmanlarn Parametre Deikenlerine Kopyalanmas

Yukardaki rnekte main ilevi iinde, func isimli ilev arlyor ve arlan ileve x isimli deikenin deeri argman olarak geiliyor. lev ars, programn alma zamannda, programn aknn func ilevinin kodunun bulunduu yere sramasna neden olur. func ilevindeki a isimli parametre deikeni iin bellekte bir yer ayrlr ve a parametre deikenine argman olan ifadenin deeri, yani x deikeninin deeri atanr. Yani int a = x; ileminin derleyicinin rettii kod sonucu otomatik olarak yapld sylenebilir. int main() { int x = 100, y = 200, z; z = add(x, y); /***/ return 0; } int add(int a, int b) { return a + b; } Yukarda tanmlanan add ilevi arldnda programn ak bu ileve gemeden nce, x ve y deikenlerinin iinde bulunan deerler, add ilevinin parametre deikenleri olan a ve b'ye kopyalanr.

1. Geri dn deeri retmeyen bir ileve yaplan ar, genellikle kendi bana bir deyim oluturur. Byle bir ileve yaplan ar, bir ifadenin paras olarak kullanlmaz. lev arsn genellikle sonlandrc atom izler;

lev ar fadelerinin Kullanmlar

61/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void func() { /***/ } int main() { func(); return 0; } 2. Geri dn deeri reten ilevlerin rettii deerlerin kullanlmas zorunlu deildir. Ancak baz ilevlerin geri dn deerlerinin kullanlmamas mantksal bir yanllk olabilir. ki saynn toplam deerine geri dnen bir ilev tanmlandn dnelim. Byle bir ilev arld yerden iki deer alarak bunlarn toplam deerine geri dnyor olsun. Byle bir ilevin varlk nedeni bir deer hesaplamaktr. levin hesaplad deer kullanlmyorsa ilev bo yere arlm, ilevin kodu bo yere alm olur. Ancak baz ilevler bir i yaptklar gibi yaptklar ile ilgili tamamlayc bir bilgiyi de geri dndrr. Byle bir ilev yalnzca yapt i iin arlabilir. Yani ilevi aran kod paras ilevin geri dndrd deer ile ilgilenmeyebilir. rnein foo() ilevi int trden bir deeri geri dn deeri reten ilev olsun. a = foo(); Yukardaki ifadede foo ilevinin geri dn deeri a isimli deikene atanr. Bu ilev bir kez arlmasna karn artk geri dn deeri a deikeninde tutulduu iin, bu geri dn deerine ilev yeniden arlmadan istenildii zaman ulalabilir. Ancak: foo(); eklinde bir ilev ar ifadesinde geri dn deeri bir deikende saklanmaz. Bu duruma geri dn deerinin kullanlmamas (discarded return value) denir. 3. Sk karlalan durumlardan biri de, bir ilev arsyla elde edilen geri dn deerinin bir baka ilev arsnda argman olarak kullanlmasdr. Aadaki rnei inceleyin: int add(int a, int b) { return a + b; } int square(int a) { return a * a; } int main() { int x = 10; int y = 25; int z = square(add(x, y)); } return 0;

Yukarda tanmlanan add ilevi, iki tamsaynn toplam deeri ile geri dnerken, square ilevi ise dardan ald deerin karesi ile geri dnyor. main ilevi iinde yaplan

62/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

square(add(x, y)); ars ile add ilevinin geri dn deeri square ilevine argman olarak geiliyor. 4. Bir ilevin rettii geri dn deeri bir baka ilevin return deyiminde return ifadesi olarak kullanlabilir. Bir baka deyile, bir ilev geri dn deerini bir baka ilevi ararak oluturabilir. ki saynn karelerinin toplamna geri dnen sum_square isimli bir ilev tanmlanmak istensin: int sum_square(int a, int b) { return add(square(a), square(b)); } Tanmlanan sum_square ilevi daha nce tanmlanm add ilevine yaplan arnn rettii geri dn deeri ile geri dnyor. add ilevine gnderilen argmanlarn da, square ilevine yaplan arlardan elde edildiini gryorsunuz.

Bir ilev kendisini de arabilir. Kendisini aran bir ileve zyinelemeli ilev (recursive function) denir. Bir ilev kendini neden arr? Byle ilevlerle hedeflenen nedir? Bu konu ileride ayr bir balk altnda ele alnacak.

levlerin Kendi Kendini armas

63/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Standard C ilevleri, C dilinin standartlatrlmasndan sonra, her derleyicide bulunmas zorunlu hale getirilmi ilevlerdir. Yani derleyicileri yazanlar standart C ilevlerini kendi derleyicilerinde mutlaka tanmlamak zorundadrlar. Bu durum C dilinin tanabilirliini (portability) artran ana etmenlerden biridir. Bir ilevin derleyiciyi yazanlar tarafndan tanmlanm ve derleyici paketine eklenmi olmas, o ilevin standart C ilevi olduu anlamna gelmez. Derleyiciyi yazanlar programcnn iini kolaylatrmak iin ok eitli ilevleri yazarak derleyici paketlerine eklerler. Ama bu tr ilevlerin kullanlmas durumunda, oluturulan kaynak kodun baka bir derleyicide derlenebilmesi ynnde bir gvence yoktur, yani artk kaynak kodun tanabilirlii azalr. rnein printf ilevi standart bir C ilevidir. Yani printf ilevi her derleyici paketinde ayn isimle bulunmak zorundadr. Standart C ilevlerinin derlenmi kodlar zel ktphanelerin iindedir. Balk dosyalar iinde, yani uzants .h biiminde olan dosyalarn iinde standart C ilevlerinin bildirimleri bulunur. lev bildirimi konusu ileride ayrntl bir biimde incelenecek. Ktphaneler (libraries), derlenmi dosyalardan oluur. DOS iletim sisteminde ktphane dosyalarnn uzants .lib, UNIX iletim sisteminde ise .a (archive) biimindedir. WINDOWS altnda uzants .dll biiminde olan dinamik ktphaneler de bulunur. Derleyicileri yazanlar tarafndan kaynak kodu yazlm standart C ilevleri nce derlenerek .obj haline getirilirler ve daha sonra ayn gruptaki dier ilevlerin .obj halleriyle birlikte ktphane dosyalarnn iine yerletirilirler. Standart C ilevleri balama aamasnda, balayc (linker) tarafndan alabilir (.exe) kod iine yazlrlar. Tmleik alan derleyicilerde balayclar, ama kod iinde bulamadklar ilevleri, yerleri nceden belirlenmi ktphaneler iinde arar. Oysa komut satrl uyarlamalarda (command line version) balayclarn hangi ktphanelere bakaca komut satrnda belirtilir.

Standart C levleri

Baz ilevlerin bulunmasnn dilin standartlar tarafndan gvence altna alnmas ile aadaki faydalar salanm olur. i) Baz ilemler iin ortak bir arayz salanm olur. Mutlak deer hesaplayan bir ilevi yazmak ok kolaydr. Ancak standart bir C ilevi olan abs ilevinin kullanlmasyla ortak bir arayz salanr. Her kaynak kod kendi mutlak deer hesaplayan ilevini tanmlam olsayd, tanmlanan ilevlerin isimleri, parametrik yaplar farkl olabilirdi. Bu durum da kod okuma ve yazma sresini uzatrd. ii) Baz ileri gerekletirecek ilevlerin kodlar sistemden sisteme farkllk gsterebilir. Bu da kaynak dosyann tanabilirliini azaltr. Bu ilemleri yapan standart ilevlerin tanmlanm olmas kaynak kodun baka sistemlere tanabilirlii artrr. iii) Baz ilevlerin yazlmas belirli bir alanda bilgi sahibi olmay gerektirebilir. rnein bir gerek saynn bir baka gerek say ssn hesaplayan bir ilevi verimli bir ekilde yazabilmek iin yeterli matematik bilgisine sahip olmak gerekir. iv) Sk yaplan ilemlerin standart olarak tanmlanm olmas, programcnn yazaca kod miktarn azaltr. Bylece proje gelitirme sresi de ksalr. v) Derleyicilerin salad standart ilevler ok sayda programc tarafndan kullanlm olduu iin ok iyi derecede test edilmilerdir. Bu ilevlerin tanmlarnda bir hata olma olasl, programcnn kendi yazaca ilevlerle kyaslandnda ok dktr. yi bir C programcsnn C dilinin standart ilevlerini ok iyi tanmas ve bu ilevleri yetkin bir ekilde kullanabilmesi gerekir.

Neden Standart levler

64/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf standart bir C ilevidir. printf ilevi ile ekrana bir yaz yazdrlabilecei gibi, bir ifadenin deeri de yazdrlabilir. Deikenlerin iindeki deerler aslnda bellekte ikilik sistemde tutulur. Bir deikenin deerinin ekrana, hangi say sisteminde ve nasl yazdrlaca programcnn isteine baldr. Deikenlerin deerlerinin ekrana yazdrlmasnda standart printf ilevi kullanlr. printf aslnda ok ayrntl zelliklere sahip bir ilevdir. imdilik iinize yarayacak kadar ayrnty reneceksiniz. printf ilevlerle ilgili yukarda aklanan genel kurallara uymaz. printf ilevi deiken sayda parametreye sahip bir ilevdir. Bir ilevin ka tane parametre deikeni varsa o ilev arldnda, ileve o kadar argman geilmelidir, deil mi? Oysa printf ilevine istenen sayda argman geilebilir. Bu ileve ka tane argman geilirse ilevin o kadar sayda parametre deikenine sahip olaca dnlebilir. Bu nasl oluyor? Deiken sayda parametreye sahip ilevler ileri bir konu olduundan, bu konu ancak sonraki blmlerde ele alnacak. printf ilevine ilk gnderilen argman genellikle ift trnak iinde yer alan bir yazdr. ift trnak iinde yer alan byle yazlara dizge (string) denir. Dizgeler konusu ileride ayr bir blmde ele alnacak. printf ilevine argman olarak geilen dizge iinde yer alan tm karakterler ekrana yazlr. Ancak printf ilevi dizge iindeki % karakterini ve bunu izleyen belirli sayda karakteri ekrana yazmaz. lev, dizge iindeki % karakterlerini yanndaki belirli sayda karakter ile birlikte formatlama karakterleri (conversion specifiers) olarak yorumlar. Formatlama karakterleri, ift trnaktan sonra yazlan argmanlarla bire bir eletirilir. Formatlama karakterleri nceden belirlenmitir, kendileriyle elenen bir ifadenin deerinin ekrana ne ekilde yazdrlaca bilgisini ileve aktarrlar. Bu format bilgisi * * * * * * Argman olan ifadenin hangi trden olarak yorumlanaca fadenin deerinin ekrana hangi say sistemi kullanlarak yazlaca fadenin ka karakterlik bir alana yazdrlaca Pozitif tamsaylarn yazmnda '+' karakterinin yazdrlp yazdrlmayaca Gerek saylarn yazmnda stel notasyonun kullanlp kullanlmayaca Gerek saylarn yazmnda noktadan sonra ka basaman yazlaca

printf levi

gibi aklamalardr. Aadaki program inceleyin: #include <stdio.h> int main() { int x = 25; double pi = 3.1415; printf("x = %d\npi = %lf\n", x, pi); return 0; } main ilevi iinde yaplan printf("x = %d\npi = %lf\n", x, pi); arsnda ileve gnderilen birinci argman olan ift trnak iindeki yazda iki ayr format dizgesi kullanlyor: %d ve %lf. %d format karakterleri ikinci argman olan x ile, %lf format karakterleri ise 3. argman olan pi ile eleniyor. Format karakterleri ile elenen ifadelerin deerleri, istenen formatlama zellikleri ile ekrana yazlr. rnein yukardaki aryla ekrana x = 25 pi = 3.14150

65/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

yazs yazdrlr. Aada formatlama karakterlerinden bazlarnn anlam veriliyor. printf ilevi ileride ayrntl olarak ele alnacak. Format karakteri %d %ld %x Anlam Bir ifadeyi int trden yorumlayarak, elde ettii deeri onluk say sisteminde yazar. Bir ifadeyi long trden yorumlayarak, elde ettii deeri onluk say sisteminde yazar. Bir ifadeyi unsigned int trden yorumlayarak, elde ettii deeri onaltlk say sisteminde yazar. Basamak sembolleri olarak a, b, c, d, e, f (kk) harflerini kullanr. Bir ifadeyi unsigned int trden yorumlayarak, elde ettii deeri onaltlk say sisteminde yazar. Basamak simgeleri olarak A, B, C, D, E, F (byk) harflerini kullanr. Bir ifadeyi unsigned long trnden yorumlayarak, onaltlk say sisteminde yazar. Bir ifadeyi unsigned int trnden yorumlayarak, onluk say sisteminde yazar. Bir ifadeyi unsigned int trnden yorumlayarak, sekizlik say sisteminde yazar float ve double trlerinden ifadelerin deerlerini onluk say sisteminde yazar. double ve long double trlerinden ifadelerin deerlerini onluk say sisteminde yazar. Gerek saylar stel biimde yazar. char veya int trnden bir ifadeyi bir karakterin sra numaras olarak yorumlayarak, ilgili karakterin grnts ekrana yazdrr. Verilen adresteki yazy ekrana yazdrr.

%X

%lx %u %o %f %lf %e %c %s

Yukardaki tabloda grld gibi double tr hem %f format karakteri hem de %lf format karakteri ile yazdrlabilir. Ama %lf okunabilirlii artrd iin daha ok tercih edilir. Yukardaki tabloya gre unsigned int trnden u isimli deikenin deeri aadaki ekillerde yazdrabilir: #include <stdio.h> int main() { unsigned int u = 57054; printf("u = %u\n", u); /* u deerini onluk sistemde yazar */ printf("u = %o\n", u); /* u deerini sekizlik sistemde yazar */ printf("u = %X\n", u); /* u deerini onaltlk sistemde yazar */ return 0; } long trden bir ifadenin deerini yazdrrken d, o, u ya da x karakterlerinden nce l karakteri kullanlr:

66/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { long int lo = 23467; unsigned long int unlo = 65242; printf("unlo printf("unlo printf("unlo printf("unlo } return 0; = = = = %ld\n", %lu\n", %lo\n", %lX\n", lo); unlo); unlo); unlo); /* /* /* /* onluk sistemde yazar */ onluk sistemde yazar */ sekizlik sistemde yazar */ onaltlk sistemde yazar */

Yukardaki rneklerde unsigned int trden bir ifadenin deerinin printf ileviyle sekizlik ya da onaltlk say sisteminde yazdrlabileceini grdnz. Peki iaretli trden bir tamsaynn deeri sekizlik ya da onaltlk sistemde yazdrlamaz m? Yazdrlrsa ne olur? Sz konusu iaretli tamsay pozitif olduu srece bir sorun olmaz. Saynn iaret biti 0 olduu iin saynn nicel bykln etkilemez. Yani doru deer ekrana yazlr, ama say negatifse iaret biti 1 demektir. Bu durumda ekrana yazlacak saynn iaret biti de nicel bykln bir paras olarak deerlendirilerek yazlr. Yani yazlan deer doru olmaz. % karakterinin yannda nceden belirlenmi bir format karakteri yoksa , % karakterinin yanndaki karakter ekrana yazlr. Yzde karakterinin kendisini ekrana yazdrmak iin format karakteri olarak %% kullanlr: printf("%%25\n");

scanf ilevi, klavyeden her trl bilginin giriine olanak tanyan standart bir C ilevidir. scanf ilevi de printf ilevi gibi aslnda ok ayrntl, geni kullanm zellikleri olan bir ilevdir. Ancak bu noktada scanf ilevi yzeysel olarak ele alnacak. scanf ilevinin de birinci parametresi bir dizgedir. Ancak bu dizge yalnzca klavyeden alnacak bilgilere ilikin format karakterlerini ierir. printf ilevinde olduu gibi scanf ilevinde de bu format karakterleri nceden belirlenmitir. % karakterinin yannda yer alrlar. scanf ilevinin kulland format karakterlerinin printf ilevinde kullanlanlar ile hemen hemen ayn olduu sylenebilir. Yalnzca gerek saylara ilikin format karakterlerinde nemli bir farkllk vardr. printf ilevi %f format ile hem float hem de double trden verileri ekrana yazabilirken scanf ilevi %f format karakterini yalnzca float trden veriler iin kullanr. double tr iin scanf ilevinin kulland format karakterleri %lf eklindedir. scanf ilevinin format ksmnda format karakterlerinden baka bir ey olmamaldr. printf ilevi ift trnak iindeki format karakterleri dndaki karakterleri ekrana yazyordu, ancak scanf ilevi format karakterleri dnda dizge iine yazlan karakterleri ekrana basmaz, bu karakterler tamamen baka anlama gelir. Bu nedenle ilevin nasl altn renmeden bu blgeye format karakterlerinden baka bir ey koymayn. Buraya konulacak bir boluk bile farkl anlama gelir.

scanf levi

67/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int x, y; printf("iki say girin : "); scanf("%d%d", &x, &y); printf("%d + %d = %d\n", x, y, x + y); return 0; } Yukardaki rnekte, program kullanan kiiye deer girmesinin beklendiini syleyen bir yaz, printf ileviyle ekrana yazdrlyor. Bu i scanf ilevi ile yaplmazd. scanf ilevi ile ekrana bir yaz yazdrmak mmkn deildir. scanf yalnzca giri amacyla tanmlanm bir ilevdir, k ilemi yapmaz. scanf("%d%d", &x, &y); ars ile programn alma zamannda klavyeden girilecek deerler x ve y deikenlerine aktarlr. x ve y deikenleri iin onluk say sisteminde klavyeden giri yaplr. Giri arasna istenildii kadar boluk karakteri konulabilir. Yani ilk sayy girdikten sonra SPACE, TAB ya da ENTER tuuna bastktan sonra ikinci deer girilebilir. rnein: 5 60 biiminde bir giri, geerli olaca gibi; 5 60 biiminde bir giri de geerlidir. scanf ilevine gnderilecek dier argmanlar & adres ileci ile kullanlr. & bir gsterici ilecidir. Bu ileci gstericiler konusunda reneceksiniz.

Klavyeden Karakter Alan C levleri

Sistemlerin hemen hemen hepsinde klavyeden karakter alan ayr C ilevi bulunur. Bu ilevlerin biri tam olarak standarttr ama dier ikisi sistemlerin hemen hemen hepsinde bulunmasna karn standart C ilevi deildir. imdi bu ilevleri inceleyelim:

Standart bu ilevin parametrik yaps aadaki gibidir: int getchar(void); levin geri dn deeri klavyeden alnan karakterin, kullanlan karakter seti tablosundaki sra numarasn gsteren int trden bir deerdir. getchar ilevi klavyeden karakter almak iin enter tuuna gereksinim duyar. Aada yazlan programda nce getchar ileviyle klavyeden bir karakter alnyor daha sonra alnan karakter ve karakterin saysal deeri ekrana yazdrlyor.

getchar levi

68/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { char ch; ch = getchar(); printf("\nKarakter olarak ch = %c\nASCII numaras ch = %d\n", ch, ch); } return 0;

getchar derleyicilerin ounda stdio.h balk dosyas iinde bir makro olarak tanmlanr. Makrolar ile ileride tanacaksnz.

getch levi

Standart olmayan bu ilevin parametrik yaps ounlukla aadaki gibidir: int getch(void); getch standart bir C ilevi olmamasna karn neredeyse btn derleyici paketleri tarafndan sunulur. Standart getchar ilevi gibi getch ilevi de klavyeden alnan karakterin kullanlan karakter setindeki sra numarasyla geri dner. Sistemlerin ounda bu ilevin getchar ilevinden iki fark vardr: 1. Baslan tu ekranda grnmez. 2. Sistemlerin ounda ENTER tuuna gereksinim duymaz. Yukarda verilen programda getchar yerine getch yazarak program altrrsanz fark daha iyi grebilirsiniz. getch ilevi zellikle tu bekleme ya da onaylama amacyla kullanlr: printf("devam iin herhangi bir tua basn...\n"); getch(); Burada klavyeden alnan karakterin ne olduunun bir nemi olmad iin ilevin geri dn deeri kullanlmyor. Derleyici paketlerinin hemen hepsinde bu ilevin bildirimi standart olmayan conio.h isimli balk dosyasnda olduundan, ilevin arld dosyaya conio.h balk dosyas eklenmelidir: #include <conio.h> Bu ilem nilemci komutlar blmnde ayrntl ekilde ele alnacak.

getche levi

Standart olmayan bu ilevin parametrik yaps ounlukla aadaki gibidir: int getche(void); getche ngilizce get char echo szcklerinden ksaltlmtr. getche ilevi de baslan tuun karakter setindeki sra numarasyla geri dner ve sistemlerin ounda enter tuuna gereksinim duymaz. Ama klavyeden alnan karakter ekranda grnr. Sistemlerin ounda getchar getch getche enter tuuna gereksinim duyar enter tuuna gereksinim duymaz enter tuuna gereksinim duymaz alnan karakter ekranda grnr. alnan karakter ekranda grnmez alnan karakter ekranda grnr.

69/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C dilinde ekrana bir karakterin grntsn basmak iin baz standart C ilevleri kullanlabilir:

Ekrana Bir Karakterin Grntsn Yazan C levleri

putchar levi

Bu standart ilevin parametrik yaps aadaki gibidir: int putchar(int ch); putchar standart bir C ilevidir. Btn sistemlerde bulunmas zorunludur. Parametresi olan karakteri ekranda imlecin bulunduu yere yazar. rnein: #include <stdio.h> int main() { char ch; ch = getchar(); putchar (ch); } return 0;

Yukardaki kodda putchar ilevinin yapt i printf ilevine de yaptrlabilirdi; printf("%c", ch); ile putchar(ch) tamamen ayn ii grr. putchar ilevi ile '\n' karakterini yazdrldnda printf ilevinde olduu gibi imle sonraki satrn bana geer. putchar ilevi ekrana yazlan karakterin ASCII karl ile geri dner. putchar ilevi derleyicilerin ounda stdio.h balk dosyas iinde bir makro olarak tanmlanmtr.

putch levi

Standart olmayan bu ilevin parametrik yaps ounlukla aadaki gibidir: int putch(int ch); putch standart bir C ilevi deildir. Dolaysyla sistemlerin hepsinde bulunmayabilir. Bu ilevin putchar ilevinden tek fark '\n' karakterinin yazdrlmas srasnda ortaya kar. putch, '\n" karakterine karlk yalnzca LF(line feed) (ASCII 10) karakterini yazar. Bu durum imlecin bulunduu kolonu deitirmeksizin aa satra gemesine yol aar.

70/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yorum Satrlar

Kaynak dosya iinde yer alan nilemci ya da derleyici programa verilmeyen aklama amal yazlara yorum satrlar (comment lines) denir. Yorum satrlar /* atomuyla balar */ atomuyla sonlanr. Bu iki atom ile, bu iki atom arasnda kalan tm karakterler, nilemci programn kaynak kodu ele almasndan nce tek bir boluk karakteriyle yer deitirir. Yorum satrlar herhangi sayda karakter ierebilir. rnek: /* Bu bir aklama satrdr */ Yorum satrlar birden fazla satra ilikin olabilir: /* bu satirlar kaynak koda dahil deildir.

*/ Bir dizge ya da bir karakter deimezi iinde yorum satr bulunamaz: #include <stdio.h> int main() { printf("/* bu bir yorum satiri degildir */"); return 0; } Yukardaki programn derlenip altrlmasyla, ekrana /* bu bir yorum satr deil */ yazs yazdrlr. Bir yorum satrnn kapatlmasnn unutulmas tipik bir hatadr. #include <stdio.h> int main() { int x = 1; int y = 2; x = 10; y = 2; /* x'e 10 deeri atanyor /* y'ye 20 deeri atanmyor */

printf("x = %d\n", x); printf("y = %d\n", y); return 0; } [C++ dilinde yorum satr oluturmann bir baka biimi daha vardr. Yorum satr // karakterleriyle balar, bulunulan satrn sonuna kadar srer. Yorum satrn sonlandrlmas bulunulan satrn sonu ile olur, yorum satrn sonlandracak ayr bir karakter bulunmaz. rnek: //Geerli bir Aklama satr Bu biim C89 standartlarna gre geerli deildir ancak C99 standartlaryla C'ye de eklenmitir.]

71/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

e Yorum Satrlar
/* */ /* */

ie yorum satrlar (nested comment lines) oluturmak geerli deildir:

Yukardaki rnekte birinci */ atomundan sonraki kod paras kaynak dosyaya dahildir. Ancak derleyicilerin ou uygun ayarlarn seilmesiyle i ie yorum satrlarna izin verir. ie yorum satrlarna gereksinim, zellikle bir yorum satrnn kopyalanarak baka bir yorum satr iine yaptrlmas durumunda oluur. Bazen de, yorum satr iine alnmak istenen kod parasnn iinde de bir baka yorum satr olduundan, isel yorum satrlar oluur.

Yorum satrlar ou zaman kaynak kodun okunabilirliini artrmak iin kullanlr. Kaynak koddan dorudan karlamayan bilgiler aklama satrlaryla okuyucuya iletilebilir. Bazen de yorum satrlar bir kaynak dosyann blm balklarn oluturmak amacyla kullanlr. Kaynak kodun aka verdii bir bilgiyi, yorum satryla aklamak programn okunabilirliini bozar.

Yorum Satrlar Neden Kullanlr

72/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

LELER
leler, nesneler veya deimezler zerinde nceden tanmlanm birtakm ilemleri yapan atomlardr. leler, mikroilemcinin bir ilem yapmasn ve bu ilem sonunda da bir deer retilmesini salar. Programlama dillerinde tanmlanm olan her bir ile en az bir makine komutuna karlk gelir. Benzer ilemleri yapmalarna karlk programlama dillerinde ile atomlar birbirlerinden farkllk gsterebilir. C programlama dilinde ifadeler ounlukla ileleri de ierirler. c = a * b / 2 + 3 ++x * y-a >= b /* 4 ile vardr ifadedeki srasyla =, *, /, + /* 3 ile vardr, ifadedeki srasyla ++, *, -/* 1 ile vardr. >= */ */ */

le Nedir

Terim Nedir

lelerin ileme soktuklar nesne veya deimezlere terim (operand) denir. C'de ileler aldklar terim saysna gre gruba ayrlabilir.

i) Tek terimli ileler (unary operators) rnein ++ ve -- ileleri tek terimli ilelerdir. ii) ki terimli ileler (binary operators) Aritmetiksel ileler olan toplama '+' ve blme '/' ileleri rnek olarak verilebilir. iii) terimli ile (ternary operator) C'de terimli tek bir ile vardr. Bu ilecin ismi "koul ileci" dir(conditional operator). leler konumlarna gre yani teriminin ya da terimlerinin neresinde bulunduklarna gre de gruplanabilir: 1. Sonek Konumundaki leler (postfix operators) Bu tip ileler terimlerinin arkasna getirilirler. rnein sonek ++ ileci (x++) 2. nek Konumundaki leler (prefix operators) Bu tip ileler terimlerinin nne getirilirler. rnein mantksal deil ileci (!x) 3. Araek Konumundaki leler (infix operators) Bu tip ileler terimlerinin aralarna getirilirler. rnein aritmetik toplama ileci (x + y)

leler, yaptklar ilemin sonucunda bir deer retir. lelerin rettii deer, ayn ifade iinde var olan bir baka ilece terim olabilir. fade iinde en son deerlendirilen ilecin rettii deer ise ifadenin deeri olur. Bir ifadenin deeri, ifade iinde yer alan ilelerin rettii deerlere gre saptanr. lelerin en nemli zellii, yaptklar ilemin sonucu olarak bir deer retmeleridir. Programc, bir ifade iinde ilelerin rettii deeri kullanr ya da kullanmaz. lelerin rettii deer aadaki biimlerde kullanlabilir: i. lecin rettii deer bir baka deikene aktarlabilir: x = y + z;

lelerin Deer retmesi

73/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rnekte y + z ifadesinin deeri, yani + ilecinin rettii deer, x deikenine aktarlr. ii. retilen deeri bir ileve argman olarak gnderilebilir: func(y + z); Yukardaki rnekte func ilevine argman olarak y + z ifadesinin deeri, yani toplama ilecinin rettii deer gnderiliyor. iii. retilen deer return deyimi ile ilevlerin geri dn deerlerinin oluturulmasnda kullanlabilir: int func() { return (y + z) } Yukarda func isimli ilevinin geri dn deeri y + z ifadesinin deeri yani + ilecinin rettii deerdir. lelerin rettii deerin hi kullanlmamas C szdizimi asndan bir hataya neden olmaz. Ancak byle durumlarda derleyiciler ounlukla bir uyar iletisi vererek programcy uyarr. rnein: int main() { int x = 20; int y = 10; x + y; } return 0;

Yukardaki kod parasnda yer alan x + y ifadesinde '+' ileci bir deer retir. '+' ilecinin rettii deer terimlerinin toplam deeri, yani 30'dur. Ancak bu deer kullanlmyor. Byle bir ilemin bilinli olarak yaplma olasl dktr. Borland derleyicilerinde verilen uyar iletisi u ekildedir: warning : "code has no effect!" (uyar : "kodun etkisi yok")

C dilinde ifadelerin trleri ve deerleri sz konusudur. Bir ifadenin deerini derleyici u ekilde saptar: fade iindeki ileler ncelik sralarna gre deer retir, retilen deerler, ifade iindeki ncelii daha az olan ilelere terim olarak aktarlr. Bu ilemin sonunda tek bir deer elde edilir ki bu da ifadenin deeridir. int x = 10; int y = 3; int z = 15; printf("%d\n", z % y / 2 + 7 -x++ * y);

lelerin ncelii

74/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki kod parasnda printf ilevi arsyla x % y / 2 + 7 -x++ * y ifadesinin deeri ekrana yazdrlr. Yazdrlan deer nedir? fade iindeki ileler ncelik sralarna gre deer retir, retilen deerler, dier ilelerin terimi olur. En son kalan deer ise ifadenin deeri, yani ekrana yazdrlan deer olur. Her programlama dilinde ilelerin birbirlerine gre ncelii sz konusudur. Eer ncelik kavram sz konusu olmasayd, ilelerin neden olaca ilemlerin sonular makineden makineye, derleyiciden derleyiciye farkl olurdu. C'de toplam 45 ile vardr. Bu ileler 15 ayr ncelik seviyesinde yer alr. C dilinin ile ncelik tablosu blm sonunda verilmitir. Bir ncelik seviyesinde eer birden fazla ile varsa, bu ilelerin ayn ifade iinde yer almas durumunda, ilelerin soldan saa m sadan sola m ncelikle ele alnaca da tanmlanmaldr. Buna, ncelik yn (associativity) denir. Ekteki tablonun 4. stunu ilgili ncelik seviyesine ilikin ncelik ynn belirtiyor. Tablodan da grld gibi her ncelik seviyesi soldan saa ncelikli deildir. 2, 13 ve 14. ncelik seviyelerinin sadan sola ncelik ynne sahip olduunu (right associative) gryorsunuz. Dier btn ncelik seviyeleri soldan saa ncelik seviyesine (left associative) sahiptir. Bir simge, birden fazla ile olarak kullanlabilir. rnein, ekteki tabloyu incelediinizde '*' simgesinin hem arpma ileci hem de bir gsterici ileci olan ierik alma ileci olarak kullanldn greceksiniz. Yine '&' (ampersand) simgesi hem bitsel ve ileci hem de gstericilere ilikin adres ileci olarak kullanlr.

C dilinde ilelerin ana ilevleri, bir deer retmeleridir. Ancak baz ileler, terimi olan nesnelerin deerlerini deitirir. Yani bu nesnelerin bellekteki yerlerine yeni bir deer yazlmasna neden olurlar. Bir ilecin, terimi olan nesnenin deerini deitirmesine ilecin yan etkisi (side effect) denir. Yan etki, bellekte yaplan deer deiiklii olarak tanmlanr. rnein atama ilecinin, ++ ve -- ilelerinin yan etkisi vardr. Bu ileler, terimleri olan nesnelerin bellekteki deerlerini deitirebilir.

lelerin Yan Etkileri

leler zerindeki Kstlamalar

Programlama dilinin kurallarna gre, baz ilelerin kullanmlaryla ilgili birtakm kstlamalar sz konusu olabilir. rnein ++ ilecinin kullanmnda, ilecin teriminin nesne gsteren bir ifade olmas gibi bir kstlama sz konusudur. Eer terim olan ifade bir nesne gstermiyorsa, yani sol taraf deeri deilse, derleme zamannda hata oluur. Kstlama, ilecin terim ya da terimlerinin trleriyle de ilgili olabilir. rnein kalan (%) ilecinin terimlerinin bir tamsay trnden olmas gerekir. Kalan ilecinin terimleri gerek say trlerinden olamaz. Terimin gerek say trlerinden birinden olmas geersizdir.

Aada ileler yaptklar ilere gre snflanyor:

lelerin Yaptklar lere Gre Snflandrlmas

Aritmetik ileler (arithmetic operators) Bu ileler aritmetik baz ilemlerin yaplmasna neden olur. Toplama, karma, arpma, artrma, eksiltme ileleri ile iaret ileleri, aritmetik ilelerdir. Karlatrma ileleri (relational operators) Bu ilelere ilikisel ileler de denir. Bu ileler bir karlatrma ilemi yaplmasn salar. Byktr, byk ya da eittir, kktr, kk ya da eittir, eittir, eit deildir ileleri karlatrma ileleridir. Mantksal ileler (logical operators)

75/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu ileler, mantksal ilemler yapar. Mantksal ve, mantksal veya, mantksal deil ileleri bu gruba girer. Gsterici ileleri (pointer operators) Bu ileler, adresler ile ilgili baz ilemlerin yaplmasn salar. Adres ileci, ierik ileci ile keli ayra ileci bu gruba girer. Bitsel ilem yapan ileler (bitwise operators) Bu ileler, bitsel dzeyde baz ilemlerin yaplmasn salar. Bitsel deil ileci, bitsel kaydrma ileleri, bitsel ve, veya, zel veya ileleri bu gruba giren ilelerdir. Atama ileleri (assignment operators) Bir nesneye atama yaplmasn salayan ilelerdir. Atama ileci ve ilemli atama ileleri bu gruba girer. zel amal ileler (special purpose operators) Bunlar farkl ilerin yaplmasn salayan ve farkl amalara hizmet eden ilelerdir. Koul ileci, sizeof ileci, tr dntrme ileci bu gruba giren ilelerdir. lk grup, programlama dillerinin hepsinde vardr. Bitsel ilem yapan ileler ve gsterici ileleri yksek seviyeli programla dillerinde genellikle bulunmaz. Programlama dillerinin ou, kendi uygulama alanlarnda kolaylk salayacak birtakm zel amal ilelere de sahip olabilir.

Aritmetik ileler, basit artimetiksel ilemler yapan ilelerdir.

Aritmetik leler

ki terimli, araek konumundaki (binary infix) ilelerdir. Dier btn programlama dillerinde olduklar gibi, terimlerinin toplamn ya da farkn almak iin kullanrlar. Yani rettikleri deer, terimlerinin toplam ya da fark deerleridir. Bu ilecin terimleri herhangi bir trden nesne gsteren ya da gstermeyen ifadeler olabilir. Terimlerinin ayn trden olmas gibi bir zorunluluk da yoktur. le ncelik tablosunun 4. seviyesinde bulunurlar. ncelik ynleri soldan saadr. Her iki ilecin de yan etkisi yoktur. Yani bu ileler terimlerinin bellekte sahip olduklar deerleri deitirmez. Toplama ve karma ileleri olan + ve ilelerini tek terimli + ve ileleriyle kartrmamak gerekir.

Toplama (+) ve karma(-) leleri

Bu ileler, tek terimli, nek konumundaki (unary prefix) ilelerdir. aret ileci eksi (-), teriminin deerinin ters iaretlisini retir. Yani derleyici, iaret eksi ilecinin kullanlmas durumunda terim olan deeri -1 ile arpacak ekilde kod retir. Bu ilecin terimi herhangi bir trden nesne gsteren ya da gstermeyen ifade olabilir. le ncelik tablosunun ikinci seviyesinde bulunurlar. ncelik yn sadan soladr. lecin bir yan etkisi yoktur, yani terimi olan nesnenin bellekteki deerini deitirmez. "aret eksi" ilecinin rettii, bir nesne deildir, bir sa taraf deeridir. Aadaki ifade matematiksel olarak doru olmasna karn C dilinde doru deildir, derleme zamannda hata oluumuna neden olur: int x; -x = 5;

aret leci Olan ve +

76/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

x bir nesne olmasna kar x ifadesi bir nesne deil, x nesnesinin deerinin ters iaretlisi olan deerdir. -x ve 0 - (x) edeer ifadelerdir. -x ifadesi bir sol taraf deeri deildir, bu ifadeye bir atama yaplamaz. aret ileci art (+), yalnzca matematiksel benzerlii salamak asndan C diline eklenmi bir iletir. Derleyici tarafndan, tek terimli, nek konumunda bir ile olarak ele alnr. Terimi olan ifade zerinde herhangi bir etkisi olmaz. Teriminin deeriyle ayn deeri retir. +x ifadesi ile 0 + (x) ifadeleri edeerdir. #include <stdio.h> int main() { int x = -5; x = -x - x; printf("x = %d\n", x); return 0; } x = -x x; Yukardaki ifadede 3 ile vardr. Soldan saa bu ileleri sayalm: Atama ileci '=', iaret ileci eksi '-', karma ileci '-'. fadenin deerinin hesaplanmasnda ile nceliklerine gre hareket edilir. nce ikinci seviyede bulunan eksi iaret ileci deer '5' deerini retir. retilen 5 deeri karma ilecinin terimi olur. Yaplan kartma ileminden retilen deer 10'dur. Bu deer de atama ilecinin terimi olur, bylece x deikenine 10 deeri atanr.

ki terimli, araek konumundaki ilelerdir. arpma ilecinin rettii deer, terimlerinin arpmdr. Blme ilecinin rettii deer ise sol teriminin sa terimine blmnden elde edilen deerdir. Bu ilelerin terimleri herhangi bir trden olabilir. Terimlerinin ayn trden olmas gibi bir zorunluluk yoktur. le ncelik tablosunun 3. seviyesinde bulunurlar. ncelik ynleri soldan saadr. Her iki ilecin de yan etkisi yoktur. Blme ilecinin kullanmnda dikkatli olmak gerekir. lecin her iki terimi de tamsay trlerinden ise ilecin rettii deer de bir tamsay olur. Yani bir tamsayy baka bir tamsayya blmekle bir gerek say elde edilmez. C programlama dilinde * simgesi ayn zamanda bir gsterici ileci olarak da kullanlr. Ama ayn simge kullanlmasna karn bu iki ile hibir zaman birbirine karmaz nk aritmetik arpma ileci iki terimli iken gsterici ileci tek terimlidir.

arpma (*) ve Blme (/) leleri

ki terimli, araek konumunda bir iletir. Terimlerinin her ikisi de tamsay trlerinden (char, short, int, long) olmak zorundadr. Herhangi bir teriminin gerek say trnden olmas geersizdir. lecin rettii deer, sol teriminin sa terimine blmnden kalandr. lecin yan etkisi yoktur. rnein: k = 15 % 4; /* burada k ya 3 deeri atanr*/ x = 2 % 7; /* burada x e 2 deeri atanr*/ int c = 13 - 3 * 4 + 8 / 3 - 5 % 2; Burada c deikenine 2 deeri atanr. nk ilem u ekilde yaplr:

Kalan (%)leci

77/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

c = 13 - (3 * 4) + (8 / 3) - (5 % 2) c = 13 - 12 + 2 - 1; c = 2; Aadaki programda 3 basamakl bir saynn birler, onlar ve yzler basamaklar ekrana yazdrlyor: #include <stdio.h> int main() { int x; printf("3 basamakli bir sayi girin: "); scanf("%d", &x); printf("birler basamagi = %d\n", x % 10); printf("onlar basamagi = %d\n", x % 100 / 10); printf("yuzler basamagi = %d\n", x / 100); } return 0;

Artrma (++) ve eksiltme (--) ileleri C dilinin en ok kullanlan ilelerindendir. Tek terimli ilelerdir. nek ya da sonek durumunda bulunabilirler. ++ ileci terimi olan deikenin deerini 1 artrmak, -- ileci de terimi olan deikenin deerini 1 eksiltmek iin kullanlr. Dolaysyla yan etkileri sz konusudur. Terimleri olan nesnenin bellekteki deerini deitirirler. Bu iki ile de 2. ncelik seviyesinde bulunduundan dier aritmetik ilelerden daha yksek ncelie sahiptir. 2. ncelik seviyesine ilikin ncelik yn sadan soladr. Yaln olarak kullanldklarnda, yani bulunduklar ifade iinde kendilerinden baka hibir ile olmakszn kullanldklarnda nek ya da sonek durumlar arasnda hibir fark yoktur. ++ ileci terimi olan nesnenin deerini 1 artrr, -- ileci terimi olan nesnenin deerini 1 eksiltir. Bu durumda ++c; ve c++ ;

Artrma (++) ve Eksiltme (--) leleri

deyimleri tamamen birbirine denk olup c = c + 1; anlamna gelirler. --c; ve c--; deyimleri tamamen birbirine denk olup c = c - 1; anlamna gelir. Bir ifade iinde dier ilelerle birlikte kullanldklarnda, nek ve sonek biimleri arasnda farkllk vardr: nek durumunda kullanldnda, ilecin rettii deer, artrma ya da eksiltme yapldktan sonraki deerdir. Yani terimin artrlm ya da azaltlm deeridir. Sonek durumunda ise ilecin rettii deer, artrma ya da eksiltme yaplmadan nceki deerdir. Yani terimi olan nesnenin artrlmam ya da azaltlmam deeridir. Nesnenin deeri ifadenin tm deerlendirildikten sonra artrlr ya da eksiltilir.

78/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

x = 10; y = ++x; Bu durumda: ++x 11 ve y = 11 deeri atanr. x = 10; y = x++; Bu durumda x++ => 10 ve y deikenine 10 deeri atanr. Aadaki program inceleyin: #include <stdio.h> int main() { int a = 10; int b = ++a; printf("a = %d b = %d\n", a, b); a = 10; b = a++; printf("a = %d b = %d\n", a, b); return 0; } Yukardaki birinci printf ars ifadesi ekrana 11 11 deerlerini yazdrrken ikinci printf ars ekrana 11 10 deerlerini yazdrr. Aadaki rnei inceleyin: #include <stdio.h> int main() { int x = 10; int y = 5; int z = x++ % 4 * --y; printf("z = %d\n", z); printf("x = %d\n", x); printf("y = %d\n", y); } return 0;

Yukarda kodu verilen main ilevinde ilem sras u ekilde olur:

79/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

z z z z y x

= x++ % 4 * 4; = 10 % 4 * 4; = 2 * 4; = 8; => 4 => 11

Aadaki rnei derleyerek altrn: #include <stdio.h> int func(int x) { return ++x; } int { main() int a = 10; int b = func(a++); printf("a = %d\n", a); printf("b = %d\n", b); return 0;

80/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C ve C++ standartlarnda sklkla kullanlan ve derleyicinin kodu yorumlama biimi hakknda bilgi veren nemli terimler vardr:

C Standartlarnda Kullanlan Baz nemli Terimlere likin Aklama

Davran

Derleyicinin belirli bir kod parasn yorumlama ve anlamlandrma biimine "derleyicinin davran" (behavior) denir.

C'de ve C++'da baz ifadeler, derleyiciden derleyiciye deiebilen fakat standartlarda ak olarak belirtilmemi olan yorumlama farllklarna yol aabilir. Byle ifadelerden kanmak gerekir. Bu tr ifadelerde derleyicinin davranna "tanmsz davran" (undefined behavior) denir. Programcnn byle ifadeler yazmas programlama hatas olarak kabul edilir. nk eer bir ifade tanmsz davran olarak belirleniyorsa bir sistemde programn altrlmas sonucunda nasl bir durumla karlalacann hibir gvencesi yoktur. Tanmsz davrana yol aan kodlar szdizimi asndan geerlidir. rnein bir ifadede bir deiken ++ ya da - ilecinin terimi olarak kullanlmsa ayn ifadede o deiken artk bir kez daha yer almamaldr. Yer alrsa artk tanmsz davrantr.

Tanmsz Davran

Belirlenmemi Davran

Kaynak kodun derleyici tarafndan farkl yorumlanabildii fakat bu konuda seeneklerin snrl olduu durumlara belirlenmemi davran (unspecified behavior) denir. Derleyiciler belirsiz davranlarda hangi seenein seilmi olduunu belgelemek zorunda deildir. phesiz programcnn belirsiz davrana yol aacak ifadelerden kanmas gerekir.

C dilinin baz zellikleri, esneklik salamak amac ile standartlarda derleyici yazanlarn seimlerine braklmtr. rnein int trnn uzunluunun ne olduu, varsaylan char trnn signed m unsigned m olduu, i ie yorumlamalarn kabul edilip edilmedii tamamen derleyici yazanlara baldr. Derleyiciler, bu zelliklerin nasl seildiklerini belgelemek zorundadr. Bu tr davrana derleyiciye zg davran (implementation dependent behaviour) denir. Bu davran zellikleri pekok derleyicide menlerden deitirilebilmektedir.

Derleyiciye zg Davran

C standartlar temel olarak derleyiciyi yazanlar iin bir klavuz biimindedir. Derleyici sorunlu bir kodla karlatnda uygun dntrme ilemlerini yapamyorsa sorunun nedenine ilikin bir bildirimde bulunmak zorundadr. Standartlarda derleyicilerin sorunu programcya bildirme durumuna "bulgu iletisi" (diagnostic message) denmektedir. Standartlar iinde belirtilmi olan szdizimsel ve anlamsal kurallarn inendii durumlarda bir uyar iletisi verilmelidir. Bu iletinin uyar (warning) ya da hata (error) biiminde olmas, derleyicinin isteine braklmtr. Ancak derleyicilerin hemen hepsinde uyarlar, derleyiciler tarafndan giderilebilecek kk yanllar iin, hata ise daha byk yanllar iin verilir. rnein bir gstericiye farkl trden bir adresin dorudan atanmas C'nin kurallarna aykrdr. Bu durumda derleyici standartlara gre bir ileti vermelidir. Aslnda standartlara gre, uyar ya da hata iletisi verilebilir, ama C derleyicilerinin hemen hepsi uyar iletisi verir. Standartlarda baz kurallarn inenmesi durumunda derleyicinin aka bir ileti vermeyebilecei belirtilmitir (nodiagnostic required). Aslnda C standartlarnda belirtildii gibi kural inenmeleri durumunda derleyicinin ilemi baar ile bitirip bitirmeyecei aka belirtilmemitir. Yani standartlara gre derleyici, doru bir program derlemeyebilir, yanl bir program derleyebilir.

Bulgu letileri

81/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Ancak C++ standartlarnda durum byle deildir. Dilin kuralna uymayan kodlarda derleyici bir ileti vermeli, derleme ilemini baarszlk ile sonulanmaldr. Derleyiciler, standartlarda belirtilen zelliklerin dnda da baz zelliklere sahip olabilir. Bu tr zelliklere derleyicilerin eklentileri denir. Derleyicilerin eklentilerini kullanmak tanabilirlii azaltr.

++ ve -- leleriyle lgili Tanmsz Davranlar

++ ve -- ilelerinin baz kullanmlar, tanmsz davran zellii gsterir. Byle kodlardan saknmak gerekir. 1. Bir ifadede bir nesne ++ ya da -- ilelerinin terimi olmusa, o nesne o ifadede bir kez daha yer almamaldr. rnein aadaki ifadelerin hepsi tanmsz davran zellii gsterirler:

int x = 20, y; int a = 5; y = ++x + ++x; y = ++x + x a = ++a; /* tanmsz davran */ /* tanmsz davran */ /* tanmsz davran */

"Koul ileci", "mantksal ve ileci", "mantksal veya ileci" ve "virgl" ileciyle oluturulan ifadelerde bir sorun yoktur. Bu ilelerle ilgili nemli bir kurala ileride deineceiz. 2. Bir ilev arlrken ileve gnderilen argmanlarn birinde bir nesne ++ ya da -ilecinin terimi olmusa, bu nesne, ileve gnderilen dier argman olan ifadelerde kullanlmamaldr. Argman olan ifadelerin, ilevlerin ilgili parametre deikenlerine kopyalanmasna ilikin sra, standart bir biimde belirlenmemitir. Bu kopyalama ilemi, baz sistemlerde soldan saa baz sistemlerde ise sadan soladr. Aadaki rnei inceleyin: int a = 10; void func(int x, int y) { /***/ } int main() { func (a, a++); } /***/

/* Tanmsz davran */

Karlatrma leleri (ilikisel ileler)


< > <= >= == !=

C programlama dilinde toplam 6 tane karlatrma ileci vardr: kktr ileci (less than) byktr ileci (greater than) kktr ya da eittir ileci (less than or equal) byktr ya da eittir ileci (greater than or equal) eittir ileci (equal) eit deildir ileci (not equal)

Bu ilelerin hepsi, iki terimli, araek konumundaki (binary infix) ilelerdir.

82/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

lk drt ile, ile ncelik tablosunun 6. seviyesinde bulunurken dier iki karlatrma ileci ncelik tablosunun 7. seviyesinde bulunur. Yani karlatrma ileleri, kendi aralarnda iki ncelik grubu oluturur. Karlatrma ileleri, aritmetik ilelerden daha dk ncelikli seviyededir. Dier programlama dillerinin ounda bool ya da boolean (Matematiki George Bool'un isminden) ismi verilen bir mantksal veri tr de doal bir veri tr olarak programcnn kullanmna sunulmutur. Byle dillerde bool veri tr, yalnzca mantksal doru ya da mantksal yanl deerlerini alabilen bir trdr. Bu dillerde karlatrma ilelerinin rettii deerler ise bu trdendir. rnein C++ ya da Java dillerinde durum byledir. C dilinde karlatrma ileleri, oluturduklar nermenin doruluu ve yanllna gre int trden 1 ya da 0 deerini retir. nerme doru ise 1 deeri retilirken, nerme yanl ise 0 deeri retilir. Bu ilelerin rettii deerler de tpk aritmetik ilelerin rettii deerler gibi kullanlabilir. Aadaki signum isimli ilevin tanmn inceleyin: int signum(int val) { return (val > 0) - (val < 0); } signum ilevine gnderilen argman 0'dan byk bir deerse ilev +1 deerine, argman 0'dan kk bir deerse ilev -1 deerine, argman 0 deeriyse ilev, 0 deerine geri dnyor. signum ilevinin geri dn deeri, karlatrma ilelerinin deer retmesinden faydalanlarak elde ediliyor. Baz programlama dillerinde (val > 0) - (val < 0); gibi bir ilem hata ile sonulanr. nk rnein Pascal dilinde val > 0 ifadesinden elde edilen deer doru (True) ya da yanl (False) dir. Yani retilen deer bool ya da boolean trndendir. Ama C doal bir dil olduu iin karlatrma ilelerinin rettikleri deer bool tr ile kstlanmamtr. C'de mantksal veri tr yerine int tr kullanlr. Mantksal bir veri trnn tamsay tryle ayn olmas C'ye esneklik ve doallk kazandrmtr. C dilinde yazlan birok kalp kod, karlatrma ilelerinin int trden 1 ya da 0 deeri retmesine dayanr. rnein x = y == z; Yukardaki deyim, C dili iin son derece doaldr ve okunabilirlii yksektir. Bu deyimin yrtlmesiyle x deikenine ya 1 ya da 0 deeri atanr. Karlatrma ileci, atama ilecinden daha yksek ncelik seviyesine sahip olduuna gre nce karlatrma ileci olan '==' deer retir, ilecin rettii deer bu kez atama ilecinin terimi olur. Bu durumda y deikeninin deerinin z deikenine eit olup olmamasna gre x deikenine 1 ya da 0 deeri atanr. Karlatrma ilecinin kullanlmasnda baz durumlara dikkat edilmelidir: int x = 12; 5 < x < 9

83/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki ifade matematiksel adan doru deildir. nk 12 deeri 5 ve 9 deerlerinin arasnda deildir. Ancak ifade C kodu olarak ele alndnda doru olarak deerlendirilir. nk 6. seviyede olan kktr (<) ilecine ilikin ncelik yn soldan saadr. nce soldaki '<' ileci deer retecek ve rettii deer olan 1 sadaki '<' ilecinin terimi olur. Bu durumda 1 < 9 ifadesi mantksal olarak doru olduu iin 1 deeri elde edilir.

Bu ileler, terimleri zerinde mantksal ilem yapar. Terimlerini doru (true) ya da yanl (false) olarak yorumladktan sonra ileme sokar. C'de ncelikleri farkl seviyede olan mantksal ile vardr: (!) mantksal deil ileci (logical not) (&&) mantksal ve ileci (logical and) (||) mantksal veya ileci (logical or) Ancak "mantksal ve", "mantksal veya" ileleri, bilinen anlamda ile ncelik kurallarna uymaz. Bu konuya biraz ileride deineceiz. C'de mantksal veri tr olmadn biliyorsunuz. Mantksal veri tr olmad iin bu trn yerine int tr kullanlr ve mantksal doru olarak 1, mantksal yanl olarak da 0 deeri kullanlr. C dilinde herhangi bir ifade, mantksal ilelerin terimi olabilir. Bu durumda sz konusu ifade, mantksal olarak yorumlanr. Bunun iin ifadenin saysal deeri hesaplanr. Hesaplanan saysal deer, 0 d bir deer ise doru (1), 0 ise yanl (0) olarak yorumlanr. rnein: 25 Doru (nk 0 d bir deer) -12 Doru (nk 0 d bir deer) 0 Yanl (nk 0) ifadesi mantksal bir ilecin terimi olduu zaman yanl olarak yorumlanr. nk saysal deeri sfra eittir.

Mantksal leler

Mantksal deil ileci, nek konumunda bulunan tek terimli bir iletir. Bu ile, teriminin mantksal deerinin tersini retir. Yani terimi mantksal olarak "doru" biiminde yorumlanan bir deer ise ile yanl anlamnda int trden 0 deerini retir. Terimi, mantksal olarak "yanl" biiminde yorumlanan bir deer ise ile doru anlamnda int trden 1 deerini retir. x Doru (0 d deer) Yanl (0) rnekler : a = !25; /* a deikenine 0 deeri atanr b = 10 * 3 < 7 + !2 lem sras: */ !x Yanl (0) Doru (1)

Mantksal Deil leci

84/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

!2 = 0 10 * 3 = 30 7 + 0 = 7 30 < 7 = 0 b = 0 (atama ileci en dk ncelikli iletir) y = 5; x = !++y < 5 != 8; lem sras: ++y 6 !6 0 /* ++ ve ! ileleri ayn ncelik seviyesindedir ve ncelik yn sadan soladr. */ 0 < 5 1 1 != 8 1 x = 1

Mantksal ve (&&) ileci

Bu ile ilikisel ilelerin hepsinden dk, || (veya / or) ilecinden yksek nceliklidir. Terimlerinin ikisi de doru ise doru (1), terimlerinden biri yanl ise yanl (0) deerini retir.

x 3 7 1 x

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

&& ilecinin, nce sol tarafndaki ilemler ncelik srasna gre tam olarak yaplr. Eer bu ilemlerde elde edilen saysal deer 0 ise, && ilecinin sa tarafndaki ilemler hi yaplmadan, yanl (0) saysal deeri retilir. rnein: x = 20; b = !x == 4 && sqrt(24); !20 0 0 == 4 0 Sol taraf 0 deeri alacandan ilecin sa taraf hi yrtlmez dolaysyla da sqrt ilevi arlmaz. Sonu olarak b deikenine 0 deeri atanr. Uygulamalarda mantksal ileler ounlukla karlatrma ileleriyle birlikte kullanlr: scanf("%d", &x); y = x >= 5 && x <= 25; Bu durumda y deikenine, ya 1 ya da 0 deeri atanr. Eer x deikeninin deeri 5'den byk ya da eit ve 25'den kk ya da eit ise y deikenine 1 deeri, bunun dndaki durumlarda y deikenine 0 deeri atanr.

85/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ch = 'c' z = ch >= 'a' && ch <= 'z' Yukardaki rnekte, ch deikeninin kk harf olup olmamas durumuna gre z deikenine 1 ya da 0 atanr.

ncelii en dk olan mantksal iletir. ki teriminden biri doru ise doru deerini retir. ki terimi de yanl ise yanl deerini retir.

Mantksal veya (||) leci

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

/* a = 1 */ /* x = 1 */ /* sayi = 0 */

"Mantksal ve", "mantksal veya" ilelerinde nce soldaki terimlerinin deerlendirilmesi gvence altna alnmtr. "Mantksal ve" ilecinin soldaki terimi yanl olarak yorumlanrsa ilecin sa terimi hi ele alnmaz. Ayn durum "mantksal veya" ileci iin de geerlidir. "Mantksal veya" ilecinin nce soldaki terimine baklr. Sol terimi doru olarak yorumlanrsa ilecin sa terimi hi dikkate alnmaz. C dili tarafndan gvence altna alnan bu zellie "ksa devre davran" (short circuit behavior) denir. Ksa devre davranna neden gerek duyulmutur? nk bu zellik baz kodlarn ok daha verimli yazlmasn salar. C'nin ileride greceimiz birok kalp kodu ksa devre davrannn kullanmna baldr. Aadaki ifadeyi inceleyin: result = ch == 'A' || ch == 'E'

&& ve || lelerinin Ksa Devre Davran

Yukardaki ifade ile, result isimli deikene, ch deikeninin deerinin 'A' ya da 'B' ye eit olmas durumunda 1 deeri, aksi halde 0 deeri atanr. ch eer 'A' ya eit ise ikinci karlatrma yaplmaz. Mantksal ileler bir deer retebilmek iin terimlerini nce 1 ya da 0, yani doru ya da yanl olarak yorumlar, ama yan etkileri yoktur. Yani terimlerinin nesne olmas durumunda bu nesnelerin bellekteki deerlerini 1 ya da 0 olarak deitirmezler.

Atama leleri

Atama ileleri, C dilinde ncelik tablosunun en alttan ikinci seviyesinde, yani 14. seviyesinde bulunur ve yalnzca virgl ilecinden daha yksek nceliklidir. Atama ilelerinin bulunduu 14. seviye, sadan sola ncelik ynne sahiptir.

Yaln Atama leci

Dier ileler gibi atama ileci de, yapt atama ileminin yansra, bir deer retir.

86/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Atama ilecinin rettii deer, nesneye atanan deerin kendisidir. Aadaki program derleyerek altrn: #include <stdio.h> int main() { int x; printf("ifade degeri = %d\n", x = 5); printf("x = %d\n", x); } return 0;

main ilevi iinde yaplan birinci printf ars ile, x = 5 ifadesinin deeri yazdrlyor. x = 5 ifadesinin deeri atama ilecinin rettii deer olan 5 deeridir. Yani ilk printf ars ile ekrana 5 deeri yazdrlr. Atama ileci yan etkisi sonucu x nesnesinin deerini 5 yapar. Bu durumda ikinci printf ars ile x deikeninin deeri ekrana yazdrldndan ekrana yazlan, 5 deeri olur. Atama ilecinin rettii deer nesne deildir. Aadaki deyim geersizdir: (b = c) = a; /* Geersiz! */

b = c atamasndan elde edilen deer, c nesnesinin kendisi deil, c nesnesinin saysal deeridir. C'nin birok kalp kodunda, atama ilecinin rettii deerden faydalanlr. Aadaki main ilevini inceleyin: #include <stdio.h> int main() { int a, b, c, d; a = b = c printf("a printf("b printf("c printf("d printf("e } return 0; = = = = = = d = 5; %d\n", %d\n", %d\n", %d\n", %d\n", a); b); c); d); e);

le ncelik tablosundan da grlecei gibi, atama ileleri sadan sola ncelik ynne sahiptir. Bu yzden: a = b = c = d = 5; deyimi C'de geerlidir. Bu deyimde nce d deikenine 5 deeri atanr. Atama ilecinin rettii 5 deeri, bu kez c deikenine atanr. Sadan sola doru ele alnan her atama ileci, nesneye atanan deeri rettiine gre, tm deikenlere 5 deeri aktarlm olur. Atama ilecinin rettii deerden faydalanmak, zellikle kontrol deyimlerinde karnza ok kacak.

lemli Atama leleri

87/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir ilemin terimi ile, ilem sonucunda retilen deerin atanaca nesne ayn ise, ilemli atama ileleri kullanlabilir. <nesne1> = <nesne1> ilem <terim2> ile <nesne1> ilem= <terim2>

ayn anlamdadr. lemli atama ileleri, atama ileciyle ayn ncelik seviyesindedir. lemli atama ileleri, hem okunabilirlik hem de daha ksa yazm iin tercih edilir. Aadaki ifadeler edeerdir: deger1 += 5; sonuc *= yuzde; x %= 5 deger1 = deger1 + 5; sonuc = sonuc * yuzde; x = x % 5;

katsayi = katsayi * (a * b + c * d); ifadesi de yine katsayi *= a * b + c * d; eklinde yazlabilir. imdi de aadaki main ilevini inceleyin: #include <stdio.h> int main() { int x = 3; int y = 5; x += y *= 3; printf("x = %d\n", x); printf("y = %d\n", y); return 0; } x += y *= 3; deyimiyle nce y deikenine 15 deeri atanr. Bu durumda *= ileci 15 deerini retir ve retilen 15 deeri bu kez += ilecinin terimi olur. Bylece x deikenine 18 deeri atanr. zellikle += ve -= ilelerinin yanl yazlmas, bulunmas zor hatalara neden olabilir. x += 5; deyimi x deikeninin deerini 5 artrrken, ilecin yanllkla aadaki gibi yazlmas durumunda x =+ 5; x deikenine 5 deeri atanr. nk burada iki ayr ile sz konusudur: Atama ileci olan = ve iaret ileci olan +.

88/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rneklerden de grld gibi, atama grubu ilelerin yan etkileri vardr. Yan etkileri, ilecin sol teriminin bellekteki deerinin deitirilmesi, yani ilecin sa tarafndaki terimi olan ifadenin deerinin sol tarafndaki nesneye aktarlmas eklinde kendini gsterir.

Virgl leci
ki ayr ifadeyi tek bir ifade olarak birletiren virgl ileci, C'nin en dk ncelikli ilecidir. ifade1; ifade2; ile ifade1, ifade2; ayn ileve sahiptir. Virgl ilecinin, nce sol terimi olan ifadenin sonra sa terimi olan ifadenin ele alnmas gvence altndadr. Bu ilecin rettii deer, sa tarafndaki ifadenin rettii deerdir. Virgl ilecinin sol teriminin, retilen deere bir etkisi olmaz. x = (y++, z = 100); gibi bir deyimle x ve z deikenlerine 100 deeri atanr. Aadaki rnekte if ayrac iindeki ifadenin rettii deer 0'dr. if (x > 5,0) { /***/ } Virgl ileleri ile bir bileik deyim basit deyim durumuna getirilebilir: if (x == a1 = a2 = a3 = } yerine if (x == 20) a1 = 20, a2 = 30, a3 = 40; yazlabilir. Virgl ilecinin sa terimi nesne gsteren bir ifade olsa bile ilecin oluturduu ifade bir nesne deildir: int x, y; /***/ (x, y) = 10; Yukardaki atama ilemi geersizdir. [C++ dilinde virgl ilecinin oluturduu bir ifade sol taraf deeri olabilir. Yukardaki atama C++ dilinde geerlidir.] 20) { 20; 30; 40;

89/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ncelik leci ncelik ileci ( ), bir ifadenin nceliini ykseltmek amacyla kullanlr.
x = (y + z) * t; ncelik ileci, C'nin en yksek ncelikli ileler grubundadr. ncelik ileci de, kendi arasnda soldan saa ncelik kuralna uyar. rnein: a = (x + 2) / ((y + 3) * (z + 2) 1); ifadesinde ilem sras yledir : i1 i2 i3 i4 i5 i6 i7 : : : : : : : x + 2 y + 3 z + 2 i2 * i3 i4 1 i1 / i5 a = i6

ncelik ilecinin terimi nesne gsteren bir ifade ise, ilecin rettii ifade de nesne gsterir: int x; (x) = 20; /* Geerli */

le ncelii, bir ilemin ilemci tarafndan daha nce yaplmas anlamna gelmez. Aadaki ifadeyi dnelim: x = func1() * func2() + func3();

le ncelii ve Bir lemin lemci Tarafndan nce Yaplmas

arpma ilecinin toplama ilecinden daha yksek ncelikli olduunu biliyorsunuz. Ancak bu ncelik, rnein yukardaki deyimde func1 ilevinin func3 ilevinden daha nce arlaca gvencesi anlamna gelmez. ncelik ilecinin de kullanm byle bir gvence salamaz. x = func1() * (func2() + func3()); Bu kez de rnein func2 ilevinin func1 ilevinden daha nce arlmasnn gvencesi yoktur.Bu kez de aadaki main ilevini inceleyin: #include <stdio.h> int main() { int x = 10; int y = x + (x = 30); printf("y = %d\n", y); return 0; } main ilevi iinde yazlan y = x + (x = 30);

90/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

deyimi pheli kod oluturur. y deikenine atanan deerin ne olaca konusunda bir gvence yoktur. x = 30 ifadesinin ncelik ayrac iine alnmas, toplama teriminin sol terimi olan x ifadesinin deerinin 30 olarak ele alnacan, gvence altna almaz. Sonuta, y deikenine 60 deeri aktarlabilecei gibi, 40 deeri de atanabilir. Ancak C dilinin 4 ileci terimlerine ilikin, daha nce ilem yapma gvencesini verir. Bu ileler: mantksal ve, mantksal veya, koul ve virgl ileleridir. Mantksal ve/veya ilelerinin ksa devre davranlarn renmitiniz. Ksa devre davrannn gerekletirilebilmesi iin bu ilelerin sol terimlerinin daha nce yaplmas gvence altna alnmtr. leride greceiniz koul ilecinin de bir deer retebilmesi iin, nce ilk teriminin deerlendirilmesi gerekir. Virgl ilecinin ise zaten varlk nedeni nce sol, daha sonra sa teriminin yaplmasn salamaktr.

91/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C Dilinin le ncelik Tablosu


Seviye 1 le () [] . -> 2 + ++ -~ ! * & sizeof (tr) * / % + << Tanm ncelik kazandrma ve ilev ar (precedence and function call) keli ayra ileci (subscript) yap elemanna yap nesnesi ile ulam (structure access with object) yap elemanna yap gstericisi ile ulam (structure access with pointer) iaret ileci (unary sign) iaret ileci (unary sign) artrma ileci (increment) eksiltme ileci (decrement) bitsel deil ileci (bitwise not) mantksal deil ileci(logical not) ierik ileci (indirection) adres ileci (address of) sizeof ileci (sizeof) tr dntrme ileci (type cast operator) arpma ileci (multiplication) blme ileci(division) kalan ileci (modulus) toplama ileci (addition) karma ileci (subtraction) bitsel sola kaydrma ileci(bitwise shift left) ncelik Yn (associativity) soldan saa

sadan sola

soldan saa

4 5

soldan saa soldan saa

>> 6 < > <= >= == != & ^ | && || ?: = += -= *= /= %= <<= >>= &= |= ^= 15 ,

7 8 9 10 11 12 13 14

bitsel saa kaydrma ileci (bitwise shift right) kktr ileci (less than) byktr ileci (greater than) kk eittir ileci (less than or equal) byk eittir ileci (greater than or equal) eittir ileci (equal) eit deildir ileci (not equal to) bitsel ve ileci (bitwise and) bitsel zel veya ileci (bitwise exor) bitsel veya ileci (bitwise or) mantksal ve ileci (logical and) mantksal veya ileci (logical or) koul ileci (conditional) atama ileci (assignement) toplamal atama ileci (assignment with addition) karmal atama ileci (assignment with subtraction) arpmal atama ileci (assignment with multiplication) blmeli atama ileci (assignment with division) kalanl atama ileci (assignment with modulus) bitsel sola kaydrmal atama ileci (assignment with bitwise left shift) bitsel saa kaydrmal atama ileci (assignment with bitwise right shift) bitsel ve ilemli atama ileci (assignment with bitwise and) bitsel veya ilemli atama ileci (assignment with bitwise or) bitsel zel veya ilemli atama ileci (assignment with bitwise exor) virgl ileci (comma)

soldan saa

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

soldan saa

92/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BLNRLK ALANI VE MR
Daha nceki konularda nesnelerin isimlerinden, deerlerinden ve trlerinden sz edilmiti. Nesnelerin C dili asndan ok nem tayan zellii daha sz konusudur. Bunlar bilinirlik alan (scope), mr (storage duration) ve balant (linkage) zelliidir.

Bilinirlik alan (scope), bir ismin tannabildii program araldr. Derleyiciye bildirilen isimler, derleyici tarafndan her yerde bilinmez. Her isim derleyici tarafndan ancak "o ismin bilinirlik alan" iinde tannabilir. Bilinirlik alan dorudan kaynak kod ile ilgili bir kavramdr, dolaysyla derleme zamanna ilikindir. C dilinde derleyici, bildirimleri yaplan deikenlere kaynak kodun ancak belirli blmlerinde ulalabilir. Yani bir deikenin tanmlanmas, o deikene kaynak dosyann her yerinden ulalabilmesi anlamna gelmez. Bilinirlik alanlar C standartlar tarafndan 4 ayr grupta toplanmtr: i. Dosya Bilinirlik Alan (File scope) : Bir ismin bildirildikten sonra tm kaynak dosya iinde, yani tanmlanan tm ilevlerin hepsinin iinde bilinmesidir. ii. Blok Bilinirlik Alan (Block scope): Bir ismin bildirildikten sonra yalnzca bir blok iinde, bilinmesidir. iii. lev Bilinirlik Alan (Function Scope): Bir ismin, bildirildikten sonra yalnzca bir blok iinde bilinmesidir. Yalnzca goto etiketlerini kapsayan zel bir tanmdr. Bu bilinirlik alanna "goto deyimi" konusunda deinilecek. iv. lev Bildirimi Bilinirlik Alan (Function Prototype Scope): lev bildirimlerindeki, ilev parametre ayrac iinde kullanlan isimlerin tannabilirliini kapsayan bir tanmdr. Bu bilinirlik alanna "lev Bildirimleri" konusunda deinilecek. Bir kaynak dosya iinde tanmlanan deikenler, bilinirlik alanlarna gre "yerel" ve "global" olmak zere ikiye ayrlabilir:

Bilinirlik Alan

Bloklarn iinde ya da ilevlerin parametre ayralar iinde tanmlanan deikenlere, yerel deikenler (local variables) denir. C dilinde bloklarn iinde tanmlanan deikenlerin tanmlama ilemlerinin, bloun en banda yaplmas gerektiini biliyorsunuz. Yerel deikenler, blok iinde tanmlanan deikenlerdir, bir ilevin ana blou iinde ya da isel bir blok iinde bildirilmi olabilirler. Yerel deikenlerin bilinirlik alan, blok bilinirlik alandr. Yani yerel deikenlere yalnzca tanmlandklar blok iinde ulalabilir. Tanmlandklar bloun daha dndaki bir blok iinde bu deikenlere eriilemez. Aadaki programda tanmlanan deikenlerin hepsi yereldir. nk x, y, z isimli deikenler bloklarn iinde tanmlanyor. Bu deikenler yalnzca tanmlanm olduklar blok iinde kullanlabilir. Tanmlandklar blok dnda bunlarn kullanlmas geersizdir. Yorum satrlar iine alnan deyimler geersizdir. z ve y deikenleri bilinirlik alanlarnn dnda kullanlmtr. Yukardaki rnekte deikenlerin hepsi yerel olduu iin blok bilinirlik alan kuralna uyar, ancak bu durum, 3 deikenin de bilinirlik alannn tamamen ayn olduu anlamna gelmez. rnek programda x deikeni en geni bilinirlik alanna sahipken y deikeni daha kk ve z deikeni de en kk bilinirlik alanna sahiptir:

Yerel Deikenler

93/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int x = 10; printf("x = %d\n", x); { int y = 20; printf("y = %d\n", y); x = 30; { int z = 50; y = 60; printf("z = %d\n", z); printf("x = %d\n", x); printf("y = %d\n", y); } z = 100; /* Geersiz! */ y = x; printf("x = %d\n", x); printf("y = %d\n", y);

} y = 500; /* Geersiz! */ printf("x = %d\n", x); return 0; }

levlerin parametre deikenleri de (formal parameters), blok bilinirlik alan kuralna uyar. Bu deikenler ilevin ana blou iinde bilinir. lev parametre deikeninin bilinirlik alan, ilevin ana blounun kapanmasyla sonlanr. Yani ilev parametre deikeninin bilinirlik alan, ilevin ana bloudur. void func (int a, double b) { /* a ve b bu ilevin her yerinde bilinir. */ } Yukardaki rnekte func ilevinin parametre deikenleri olan a ve b isimli deikenler, func ilevinin her yerinde kullanlabilir.

C dilinde bloklarn dnda da deikenlerin tanmlanabileceini biliyorsunuz. Bloklarn dnda tanmlanan deikenler "global deikenler" (global variables) olarak isimlendirilir. Derleme ileminin bir yn vardr. Bu yn kaynak kod iinde yukardan aaya dorudur. Bir deiken yerel de olsa global de olsa, tanmlamas yaplmadan nce kullanlmas geersizdir. Global deikenler tanmlandklar noktadan sonra kaynak dosyann sonuna kadar her yerde bilinir:

Global Deikenler

94/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int g; void func() { g = 10; } int main() { g = 20; printf("g = %d\n", g); func(); printf("g = %d\n", g); return 0; /* g = 20 */ /* g = 10 */

}
Yukardaki rnekte g deikeni blok dnda tanmland iin -ya da hibir ilevin iinde tanmlanmad iin- global deikendir. g deikeninin bilinirlik alan, dosya bilinirlik alandr. Yani g deikeni, tanmlandktan sonra tm ilevlerin iinde kullanlabilir. Yukardaki programda nce g global deikenine 20 deeri atanyor.Daha sonra bu deer printf ileviyle ekrana yazdrlyor. Daha sonra func ilevi arlyor. func ilevi arlnca kodun ak func ilevine geer. func ilevi iinde de g global deikeni bilinir. func ilevinde global y deikenine 10 deerinin atanmasndan sonra bu deer yine printf ileviyle ekrana yazdrlyor.

C dilinde ayn isimli birden fazla deiken tanmlanabilir. Genel kural udur: ki deikenin bilinirlik alanlar ayn ise, bu deikenler ayn ismi tayamaz. Ayn ismi tamalar derleme zamannda hata oluturur. ki deikenin bilinirlik alanlarnn ayn olmas ne anlama gelir? ki deikenin bilinirlik alanlar, ayn kapanan kme ayrac ile sonlanyorsa, bu deikenlerin bilinirlik alanlar ayn demektir. { float a; int b; double a; { int c; /*...*/ }

Ayn simli Deikenler

/* Geersiz */

} Yukardaki kod geersizdir. nk her iki a deikeninin de bilinirlik alan ayndr. Farkl bilinirlik alanlarna sahip birden fazla ayn isimli deiken tanmlanabilir. nk derleyiciler iin, artk bu deikenlerin ayn isimli olmas nemli deildir. Bunlar bellekte farkl yerlerde tutulur. Aadaki rnei inceleyin:

95/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int x = 100; printf("%d\n", x); { int x = 200; printf("%d\n", x); { int x = 300; printf("%d\n", x); } } return 0; } Yukardaki program parasnda bir hata bulunmuyor. nk her x deikeninin de bilinirlik alanlar birbirlerinden farkldr. Peki yukardaki rnekte i bloklarda x ismi kullanldnda derleyici bunu hangi x deikeni ile ilikilendirir? Bir kaynak kod noktas, ayn isimli birden fazla deikenin bilinirlik alan iinde ise, bu noktada deikenlerden hangisine eriilir? Derleyici, bir ismin kullanm ile karlatnda bu ismin hangi yazlmsal varla ait olduunu bulmaya alr. Bu ileme "isim arama" (name lookup) denir. sim arama, dar bilinirlik alanndan geni bilinirlik alanna doru yaplr. Yani derleyici sz konusu ismi nce kendi blounda arar. Eer isim, bu blok iinde tanmlanmam ise bu kez isim kapsayan bloklarda aranr. sim, kapsayan bloklarda da bulunamaz ise bu kez global isim alannda aranr. Dar bilinirlik alanna sahip isim, daha geni bilinirlik alannda yer alan ayn ismi maskeler, onun grnmesini engeller. Aadaki program inceleyin: void func1() { int k; /***/ } void func2() { int k; /***/ } void func3() { int k; /***/ } Yukardaki kod parasnda bir hata sz konusu deildir. Her ilevde de k isimli bir deiken tanmlanm olsa da bunlarn bilinirlik alanlar tamamen birbirinden farkldr. Bir global deikenle ayn isimli yerel bir deiken olabilir mi? ki deikenin bilinirlik alanlar ayn olmad iin bu durum bir hataya neden olmaz. Ayn isimli hem bir global hem de bir yerel deikene eriilebilen bir noktada, eriilen yerel deiken olur. nk ayn bilinirlik alannda, birden fazla ayn isimli deiken olmas durumunda, o alan iinde en dar bilinirlik alanna sahip olanna eriilebilir. Aadaki kodu inceleyin:

96/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int g = 20; /* g global deiken */

void func() { /* global g deikenine atama yaplyor. */ g = 100; /* global g deikeninin deeri yazdrlyor. */ printf("global g = %d\n", g); } int main() { int g; /* g yerel deiken */ /* yerel g deikenine atama yaplyor */ g = 200; /* yerel g yazdrlyor. */ printf("yerel g = %d\n", g); func(); /* yerel g yazdrlyor. */ printf("yerel g = %d\n", g); } return 0;

levlerin kendileri de btn bloklarn dnda tanmlandklarna gre global varlklardr. Gerekten de ilevler kaynak kodun her yerinden arlabilir. Ayn bilinirlik alanna ilikin, ayn isimli birden fazla deiken olmayacana gre, ayn isme sahip birden fazla ilev de olamaz. [Ancak C++ dilinde isimleri ayn parametrik yaplar farkl ilevler tanmlamak mmkndr.] Bildirilen bir isme bilinirlik alan iinde her yerde ulalamayabilir. nk bir isim, daha dar bir bilinirlik alannda ayn isim tarafndan maskelenmi olabilir. Bu yzden "bilinirlik alan" dnda bir de "grlebilirlik" (visibility) teriminden sz edilebilir. [C++ dilinde global bir ismin yerel bir isim tarafndan maskelenmesi durumda, global isme znrlk ileci (scope resolution operator) ismi verilen bir ilele eriim mmkndr.]

mr (storage duration / lifespan), nesnelerin, programn alma zaman iinde bellekte yer kaplad sreyi anlatmak iin kullanlan bir terimdir. Bir kaynak kod iinde tanmlanm deikenlerin hepsi, program almaya baladnda ayn zamanda yaratlmaz. Programlarda kullanlan varlklar, mrleri bakmndan gruba ayrlabilir: 1. Statik mrl varlklar 2. Otomatik mrl varlklar 3. Dinamik mrl varlklar

Nesnelerin mrleri

Statik mrl varlklar (static duration static storage class), programn almaya balamasyla bellekte yerlerini alr, programn almas bitene kadar varlklarn srdrr, yani bellekte yer kaplar. Statik mrl varlklar, genellikle ama kod (.obj) iine yazlr. C dilinde statik mrl ayr varlk grubu vardr: global deikenler dizgeler (ift trnak iindeki yazlar) statik yerel deikenler

i. Statik mrl Varlklar

97/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Dizgeler ile statik yerel deikenleri daha sonra greceksiniz. Global deikenler statik mrl varlklardr. Yani global deikenler programn almas sresince yaayan, yani programn almas sresince bellekte yer kaplayan deikenlerdir.

Otomatik mrl nesneler programn almasnn belli bir zamannda yaratlan, belli sre etkinlik gsterdikten sonra yok olan, yani mrlerini tamamlayan nesnelerdir. Bu tr nesnelerin mrleri, programn toplam alma sresinden ksadr. Yerel deikenler, otomatik mrldr. Programn alma zamannda tanmlandklar bloun almas baladnda yaratlrlar, bloun almas bitince yok olurlar, yani mrleri sona erer. void func(int a, int b) { int result; /***/ } Yukardaki func ilevinin ana blou iinde result isimli bir yerel deiken tanmlanyor. Programn almas srasnda func ilevinin koduna girildiinde result deikeni yaratlr. Programn ak func ilevinden ktnda, result deikeninin mr sona erer. Statik mrl deikenlerle otomatik mrl deikenler arasnda ilkdeer verme (initialization) asndan da fark vardr. Statik mrl olan global deikenlere de yerel deikenlerde olduu gibi ilkdeer verilebilir. lkdeer verilmemi ya da bir atama yaplmam bir yerel deikenin iinde bir p deer bulunur. Bu deer o an bellekte o deiken iin ayrlm yerde bulunan 1 ve 0 bitlerinin oluturduu deerdir. lkdeer verilmemi statik mrl deikenlerin 0 deeri ile balatlmas gvence altndadr. lk deer verilmemi ya da bir atama yaplmam global deikenler iinde her zaman 0 deeri vardr. Yani bu deikenler derleyici tarafndan retilen kod yardmyla 0 deeriyle balatlr. Aadaki program derleyerek altrn: #include <stdio.h> int g; int main() { int y; printf("g = %d\n", g); printf("y = %d\n", y); } return 0; /* Yanl /

ii. Otomatik mrl Varlklar

Bir yerel deikenin ilkdeer verilmeden ya da kendisine bir atama yaplmadan kullanlmas bir programlama hatasdr. Derleyicilerin hemen hemen hepsi byle durumlarda mantksal bir uyar iletisi verir. [C++ dilinde byle bir zorunluluk yoktur.] Global deikenlere ancak deimez ifadeleriyle ilkdeer verilebilir. Global deikenlere ilkdeer verme ileminde kullanlan ifadede (initializer), deikenler ya da ilev ar ifadeleri kullanlamaz. fade yalnzca deimezlerden olumak zorundadr.

98/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

[Global deikenlere deimez ifadesi olmayan ifadelerle ilkdeer verilmesi C++ dilinde geerlidir. Yeni C derleyicilerin ou, global deikenlere deimez ifadesi olmayan ifadelerle ilkdeer verilmesi durumunda da kodu geerli sayma eilimindedir. Tanabilirlik asndan bu durumdan kanlmasn salk veriyoruz.] Ancak yerel deikenlere ilkdeer verilme ileminde byle bir kstlama yoktur. #include <stdio.h> int func(void); int x = 5; int y = x + 5; int z = func(); int main() { int a = b; int k = b - 2; int m = func(); /***/ } Yukardaki programda main ilevi iinde a, k, m deikenlerinin tanmlanmalar geerlidir. /* Geersiz */ /* Geersiz */

Dinamik bellek ilevleri ile yerleri ayrlm nesneler, dinamik mrldr. Dinamik bellek ilevleri ile yaratlm nesneleri daha sonra greceksiniz.

iii. Dinamik mrl Varlklar

Bir programda bir deiken gereksinimi durumunda, global ya da yerel deiken kullanlmas baz avantajlar ya da dezavantajlar getirebilir. Ancak genel olarak global deikenlerin baz sakncalarndan sz edilebilir. zel bir durum sz konusu deil ise, yerel deikenler global deikenlere tercih edilmeli, global deikenler ancak zorunlu durumlarda kullanlmaldr. Global deikenler aadaki sakncalara neden olabilir: 1.Global deikenler statik mrl olduklarndan programn sonuna kadar bellekte yerlerini korur. Bu nedenle bellein daha verimsiz olarak kullanlmalarna neden olurlar. 2. Global deikenler tm ilevler tarafndan ortaklaa paylaldndan, global deikenlerin oka kullanld kaynak dosyalar okumak daha zordur. 3. Global deikenlerin ska kullanld bir kaynak dosyada, hata arama maliyeti daha yksektir. Global deikene ilikin bir hata sz konusu ise, bu hatay bulmak iin tm ilevler aratrlmaldr. Tm ilevlerin global deikenlere ulaabilmesi, bir ilevin global bir deikeni yanllkla deitirebilmesi riskini de dourur. 4. Global deikenlerin kullanld bir kaynak dosyada, deiiklik yapmak da daha fazla aba gerektirir. Kaynak kodun eitli blmleri, birbirine global deiken kullanmlaryla sk bir ekilde balanm olur. Bu durumda kaynak kod iinde bir yerde deiiklik yaplmas durumunda baka yerlerde de deiiklik yapmak gerekir. 5. Programclarn ou, global deikenleri mmkn olduu kadar az kullanmak ister. nk global deikenleri kullanan ilevler, baka projelerde kolaylkla kullanlamaz. Kullanldklar projelerde de ayn global deikenlerin tanmlanm olmas gerekir. Dolaysyla global deikenlere dayanlarak yazlan ilevlerin yeniden kullanlabilirlii azalr.

Global ve Yerel Deikenlerin Karlatrlmas

99/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

6. Global deikenler global isim alann kirletir. Bu noktaya ileride "balant" kavram ele alnd zaman yeniden deinilecek.

levler geri dn deerlerini, geici bir nesne yardmyla kendilerini aran ilevlere iletir. Aadaki program inceleyin: #include <stdio.h> int add(int x, int y) { return x + y; } int main() { int a, b, sum; printf("iki say girin: "); scanf("%d%d", &a, &b); sum = add(a, b); printf("toplam = %d\n", sum); } return 0;

levlerin Geri Dn Deerlerini Tutan Nesneler

Bir ilevin geri dn deerinin tr aslnda, ilevin geri dn deerini iinde tayacak geici nesnenin tr demektir. Yukarda tanm verilen add isimli ilevin main ilevi iinden arldn gryorsunuz. Programn ak, add ilevi iinde return deyimine geldiinde, geici bir nesne yaratlr. Bu geici nesne, return ifadesiyle ilkdeerini alr. Yani return ifadesi aslnda oluturulan geici nesneye ilkdeerini veren ifadedir. Geri dn deeri reten bir ileve yaplan ar, bu ilevin geri dn deerini iinde tutan geici nesneyi temsil eder. Peki bu geici nesnenin mr ne kadardr? Bu nesne, return deyimiyle yaratlr ve ilev arsn ieren ifadenin deerlendirilmesi sona erince yok edilir. Yani rnekteki main ilevi iinde yer alan sum = add(a, b); deyiminin yrtlmesinden sonra, geici nesnenin mr de sona erer.

100/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

KONTROL DEYMLER
C dilinde yazlm bir programn cmlelerine deyim(statement) dendiini biliyorsunuz. Baz deyimler, yalnzca derleyici programa bilgi verir. Bu deyimler derleyicinin ilem yapan bir kod retmesine neden olmaz. Byle deyimlere "bildirim deyimi" (declaration statement) denir. Baz deyimler derleyicinin ilem yapan bir kod retmesine neden olur. Byle deyimlere "yrtlebilir deyim" (executable statement) denir. Yrtlebilir deyimler de farkl gruplara ayrlabilir:

Bir ifadenin, sonlandrc atom ile sonlandrlmasyla oluan deyimlere yaln deyim (simple statement) denir; x = 10; y++; func(); Yukarda 3 ayr yaln deyim yazlmtr.

Yaln Deyim:

Bo Deyim:

C dilinde tek bana bulunan bir sonlandrc atom ';', kendi bana bir deyim oluturur. Bu deyime bo deyim (null statement) denir. Bo bir blok da bo deyim oluturur: ; {} Yukardaki her iki deyim de bo deyimdir.

Bir blok iine alnm bir ya da birden fazla deyimin oluturduu yapya, bileik deyim (compound statement) denir. Aada bir bileik deyim grlyor. { x = 10; y++; func();

Bileik Deyim:

Kontrol deyimleri, programn ak ynn deitirebilen deyimlerdir. Kontrol deyimleri ile programn ak farkl noktalara ynlendirilebilir. Bunlar, C dilinin nceden belirlenmi baz szdizimi kurallarna uyar, kendi szdizimleri iinde en az bir anahtar szck ierir. C dilinde aadaki kontrol deyimleri vardr: if deyimi while dng deyimi do while dng deyimi for dng deyimi break deyimi continue deyimi switch deyimi goto deyimi return deyimi

Kontrol deyimi:

101/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if DEYM
C dilinde program akn denetlemeye ynelik en nemli deyim if deyimidir. En yaln biimiyle if deyiminin genel szdizimi aadaki gibidir: if (ifade) deyim; if ayrac iindeki ifadeye koul ifadesi (conditional expression) denir. if ayracn izleyen deyime, if deyiminin doru ksm (true path) denir. if deyiminin doru ksmn oluturan deyim, bir yaln deyim (simple statement) olabilecei gibi, bir bo deyim (null statement), bir bileik deyim (compound statement) ya da baka bir kontrol deyimi de (control statement) olabilir. Yaln if deyiminin yrtlmesi aadaki gibi olur: nce koul ifadesinin saysal deerini hesaplar. Hesaplanan saysal deer, mantksal DORU ya da YANLI olarak yorumlanr. Koul ifadesinin hesaplanan deeri 0 ise yanl, 0'dan farkl bir deer ise doru olarak yorumlanr. rnein koul ifadesinin hesaplanan deerinin 5 olduunu dnelim. Bu durumda kontrol ifadesi doru olarak deerlendirilir. Eer ifade DORU olarak yorumlanrsa, if deyiminin doru ksm yaplr, ifade YANLI olarak yorumlanrsa doru ksm yaplmaz. Yaln if deyimi, bir ifadenin doruluuna ya da yanllna gre, bir deyimin yaplmas ya da yaplmamasna dayanr. Aadaki program derleyerek altrn: int main() { int x; printf("bir sayi girin : "); scanf("%d", &x); if (x > 10) printf("if deyiminin doru ksm!\n"); } return 0;

main ilevi iinde yazlan if deyimiyle, klavyeden girilen tamsaynn 10'dan byk olmas durumunda printf ars yrtlr, aksi halde yrtlmez.

Yanl Ksm Olan if Deyimi

if kontrol deyimi, else anahtar szcn de ierebilir. Byle if deyimine, yanl ksm olan if deyimi denir. Yanl ksm olan if deyiminin genel biimi aadaki gibidir: if (ifade) deyim1; else deyim2;

Bu kez if deyiminin doru ksmn izleyen deyimden sonra else anahtar szcnn, daha sonra ise bir baka deyimin yer aldn gryorsunuz. Genel biimdeki deyim2'ye if deyiminin yanl ksm (false path) denir. if deyiminin koul ifadesi, mantksal olarak DORU ya da YANLI olarak yorumlanr. Bu kez koul ifadesinin DORU olmas durumunda deyim1, YANLI olarak yorumlanmas durumunda deyim2 yaplr. Yanl ksm olan if deyimi, bir koul ifadesinin doru ya da

102/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

yanl olmasna gre iki ayr deyimden birinin yaplmasna yneliktir. Yani ifade doru ise bir i, yanl ise baka bir i yaplr. Aadaki rnei inceleyin: #include <stdio.h> int main() { char ch; printf("bir karakter girin : "); ch = getchar(); if (ch >= 'a' && ch <= 'z') printf("%c kucuk harf!\n", ch); else printf("%c kucuk harf degil!\n", ch); } return 0;

Yukardaki main ilevinde standart getchar ilevi kullanlarak klavyeden bir karakter alnyor. Alnan karakterin sra numaras, ch isimli deikene atanyor. Koul ifadesinin doru ya da yanl olmas durumuna gre, klavyeden alnan karakterin kk harf olup olmad bilgisi ekrana yazdrlyor. Koul ifadesine bakalm: ch >= 'a' && ch <= 'z' Bu ifadenin doru olmas iin "mantksal ve (&&)" ilecinin her iki teriminin de doru olmas gerekir. Bu da ancak, ch karakterinin kk harf karakteri olmas ile mmkndr. if deyiminin doru ve/veya yanl ksm bir bileik deyim olabilir. Bu durumda, koul ifadesinin doru ya da yanl olmasna gre, birden fazla yaln deyimin yrtlmesi salanabilir. Aadaki rnei inceleyin: /***/ if (x > 0) { y = x * 2 + 3; z = func(y); result = z + x; } else { y = x * 5 - 2; z = func(y - 2); result = z + x - y; } /***/ Yukardaki if deyiminde, x > 0 ifadesinin doru olup olmasna gre, result deikeninin deeri farkl ilemlerle hesaplanyor. if deyiminin hem doru hem de yanl ksmlarn bileik deyimler oluturuyor. Bir if deyiminin yanl ksm olmak zorunda deildir. Ancak bir if deyimi yalnzca else ksmna sahip olamaz. Bu durumda if deyiminin doru ksmna bo deyim ya da bo bileik deyim yerletirilmelidir: if (ifade) ; else deyim1;

103/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ya da if (ifade) { } else deyim1; Yalnzca yanl ksm olan, doru ksm bir bo deyim olan bir if deyimi, okunabilirlik asndan iyi bir seenek deildir. Byle durumlarda daha iyi bir teknik, koul ifadesinin mantksal tersini alp, if deyiminin yanl ksmn ortadan kaldrmaktr: if (!ifade) deyim1; Aadaki kod parasn inceleyin: /***/ if (x > 5) ; else { func1(x); func2(x); } /***/ Yukardaki if deyiminde, x deikeninin deeri 5'ten bykse bir ey yaplmyor, aksi halde func1 ve func2 ilevleri x deikeninin deeri ile arlyor. Koul ifadesi ters evrilerek if deyimi yeniden yazlrsa: /***/ if (x <= 5) { func1(x); func2(x); } /***/ if ayracnn iinde, ifade tanmna uygun herhangi bir ifade bulunabilir: if (10) deyim1; if (-1) deyim2; Yukardaki koul ifadelerinin deeri, her zaman doru olarak yorumlanr. nk ifadeler, sfrdan farkl deere sahiptir. Aadaki koul ifadesi ise her zaman yanl olarak yorumlanacandan if deyiminin doru ksm hibir zaman yrtlmez: if (0) deyim1; Aadaki if deyiminde ise, x deikeninin deerinin 0 olup olmamasna gre, deyim1 ve deyim2 yrtlr:

104/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (x) { deyim1; deyim2; /***/ } Yukardaki yapyla aadaki yap edeerdir: if (x != 0) { deyim1; deyim2; } Aadaki rnei inceleyin : if (!x) { deyim1; deyim2; } Bu if deyiminde ise ancak x deikeninin deerinin 0 olmas durumunda deyim1 ve deyim2 yrtlr. Yine yukardaki yapyla aadaki yap edeerdir: if (x == 0) { deyim1; deyim2; }

if deyiminin koul ifadesinde atama ileci sklkla kullanlr. Bylece, atama ilecinin rettii deerden faydalanlr: Aadaki kod parasn inceleyin: if ((x = getval()) > 5) func1(x); else func2(x); if deyiminin koul ifadesinde ise, arlan getval ilevinin geri dn deeri, x deikenine aktarlyor. Atama ilecinin rettii deerin nesneye atanan deer olduunu anmsayn. Atama ileci ile oluturulan ifadenin, ncelik ayrac iine alndn gryorsunuz. Bu durumda hem getval ilevinin geri dn deeri x deikenine aktarlyor hem de ilevin geri dn deerinin 5'ten byk olup olmad sorgulanyor. ncelik ayrac kullanlmasayd getval ilevinin geri dn deerinin 5'ten byk olup olmamasna gre x deikenine 0 ya da 1 deeri atanrd. Bu durumda da ya func1 ilevi 1 deeriyle ya da func2 ilevi 0 deeriyle arlrd. Deyim aadaki gibi de yazlabilirdi, deil mi? x = getval(); if (x > 5) func1(x); else func2(x); Ancak kalp kod, daha karmak deyimlerin yazlmasnda kolaylk salar. Aadaki if deyiminin nasl yrtleceini dnn. "Mantksal ve" ilecinin birinci ksmnn, daha nce ele alnmasnn, yani "ksa devre" davrannn gvence altnda olduunu anmsayn.

Koul fadesinde Atama lecinin Kullanlmas

105/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if ((y = getval()) > 5 && isprime(x)) func1(y); func2(y);

e if Deyimleri
if (ifade1) if (ifade2) { deyim1; deyim2; deyim3; } deyim4;

if deyiminin doru ya da yanl ksmn, baka bir if deyimi de oluturabilir:

Bu rnekte ikinci if deyimi birinci if deyiminin doru ksmn oluturur. Birinci ve ikinci if deyimlerinin yanl ksmlar yoktur. ie if deyimlerinde, son if anahtar szcnden sonra gelen else anahtar szc, en iteki if deyimine ait olur: if (ifade1) if (ifade2) deyim1; else deyim2; Yukardaki rnekte, yazm biimi nedeniyle 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 olur (dangling else). else anahtar szcnn birinci if deyimine ait olmas isteniyorsa, birinci if deyiminin doru ksm bloklanmaldr: 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; Yukardaki rnekte birinci if deyiminin doru ksm, birden fazla deyimden olutuu iin bu deyimlerden birisi de yine baka bir if deyimidir- bloklama yaplyor. deyim5, birinci if deyiminin yanl ksmn oluturur.

106/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki if deyimlerini inceleyin: Eer bir karlatrmann doru olarak sonulanmas durumunda yaplan dier karlatrmalarn doru olmas sz konusu deilse, bu tr karlatrmalara ayrk karlatrma denir. Ayrk karlatrmalarda, if deyimlerinin ayr ayr kullanlmas kt tekniktir: if (m == 1) printf("Ocak\n"); if (m == 2) printf(ubat\n"); if (m == 3) printf("Mart\n"); /***/ if (m == 12) printf("Aralk\n"); Yukardaki rnekte m deikeninin deerinin 1 olduunu dnn. Bu durumda ekrana Ocak yazs yazdrlr. Fakat daha sonra yer alan if deyimleriyle m deikeninin srasyla 2, 3, ... 12'ye eit olup olmad ayr ayr snanr. Ama x deikeni 1 deerine sahip olduundan, btn dier if deyimleri iindeki kontrol ifadelerinin yanl olarak deerlendirilecei bellidir. Bu durumda birinci if deyiminden sonraki btn if deyimleri gereksiz yere yrtlm olur. Ayn zamanda kodun okunabilirlii de bozulur. Ayrk karlatrmalarda else if merdivenleri kullanlmaldr: if (ifade1) deyim1; else if (ifade2) deyim2; else if (ifade3) deyim3; else if (ifade4) deyim4; else deyim5; Bu yapda, herhangi bir if deyiminin koul ifadesi doru olarak deerlendirilirse programn ak hibir zaman baka bir if deyimine gelmez. 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 biimi, okunabilirlik asndan tercih edilmelidir: if (ifade1) deyim1; else if (ifade2) deyim2; else if (ifade3) deyim3; else if (ifade4) deyim4; else deyim5;

else if Merdiveni

107/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Merdiveninin en sonundaki if deyiminin yanl ksmnn zel bir nemi vardr. Yukardaki rnekte deyim5, merdivenin son if deyiminin else ksmnda yer alyor. Merdiven iindeki hibir if deyiminin koul ifadesi doru deilse, son if deyimininin yanl ksm yaplr, deil mi? Yukardaki merdivenin yrtlmesi sonucu, deyim1, deyim2, deyim3, deyim4, deyim5'den biri mutlaka yaplr. Son basamaktaki if deyiminin yanl ksm olmayan bir else if merdiveninden, hibir i yaplmadan da klabilir. Hem okunabilirlik asndan hem de verim asndan, else if merdiveninde olasl ya da skl daha yksek olan koullar, daha yukarya kaydrlmaldr.

Sk Yaplan Hatalar

zellikle C'ye yeni balayanlarn sk yapt bir hata, yaln if deyimiyle yanl ksm olan if deyimini birbirine kartrmaktr. Yani if deyiminin yanl ksm unutulur: #include <stdio.h> int main() { int x; printf("bir sayi girin: "); scanf("%d", &x); if (x % 2 == 0) printf("%d cift sayi!\n", x); printf("%d teksayi!\n", x); } return 0;

if ayrac iindeki ifadenin yanl olmas durumunda bir yanllk sz konusu deildir. Ama ifade doru ise ekrana ne yazlr? Klavyeden 28 deerinin girildiini dnelim: 28 ift sayi! 28 tek sayi! Belki de en sk yaplan hata, if ayracnn sonuna yanllkla sonlandrc atomun (;) yerletirilmesidir. Aadaki main ilevini inceleyin: #include <stdio.h> int main() { int x; printf("bir sayi girin: "); scanf("%d", &x); if (x % 2 == 0); printf("%d cift sayi!\n", x); } return 0;

Yukardaki main ilevinde, x % 2 == 0 ifadesi doru da olsa yanl da olsa printf ilevi arlr. printf ars if deyiminin dndadr. if deyiminin doru ksmn bir bo deyimin (null statement) oluturmas szdizim kurallarna kesinlikle uyan bir durumdur. Yazlan if deyimi, gerekte "x ift ise bir ey yapma" anlamna gelir. Bilinli bir biimde yazlma

108/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

olasl yksek olmayan bu durum iin, derleyicilerin ou mantksal bir uyar iletisi vermez. Ayn hata, yanl ksm olan bir if deyiminin doru ksmnda yaplsayd bir szdizim hatas oluurdu, deil mi? if (x > 5); printf("doru!\n"); else printf("yanl\n"); else anahtar szcnn, bir if deyimine bal olarak kullanlmas gerektiini biliyorsunuz. Yukardaki kod parasndaki if deyimi, doru ksm "hibir ey yapma" anlamna gelen bir if deyimidir. Dolaysyla, else anahtar szc hibir if deyimine balanmam olur. Bu da bir szdizim hatasdr. nk bir if deyimine balanmayan, bir else olamaz. Tabi ki bir if deyiminin doru ya da yanl ksmn bir bo deyim (null statement) oluturabilir. Bu durumda okunabilirlik asndan, bu bo deyimin bir tab ieriden yazlmas, bo deyimin bilinli olarak yerletirildii konusunda gl bir izlenim verir. if ((val = getval()) != 0) ; Sk yaplan baka bir yanllk, if ayrac iinde karlatrma ileci (==) yerine atama ilecinin (=) kullanlmasdr. /***/ if (x == 5) printf("eit\n"); /***/ Yukardaki if deyiminde, x deikeninin deeri 5'e eitse printf ilevi arlyor. Karlatrma ilecinin yan etkisi yoktur. Yani yukardaki if ayrac iinde x deikeninin deeri, 5 deimezi ile yalnzca karlatrlyor, deitirilmiyor. Oysa karlatrma ilecinin yerine yanllkla atama ileci kullanlrsa: /***/ if (x = 5) printf("eit\n"); /***/ Atama ileci, atama ilecinin sa tarafndaki ifadenin deerini retir, if ayrac iindeki ifadenin deeri 5 olarak hesaplanr. 5, sfr d bir deer olduundan, x deikeninin deeri ne olursa olsun, printf ilevi arlr. Atama ilecinin yan etkisi olduundan, x deikenine de if deyiminin yrtlmesi ile 5 deeri atanr. C derleyicilerinin ou, if ayrac iindeki ifade yaln bir atama ifadesi ise, durumu pheyle karlayarak, mantksal bir uyar iletisi verir. rnein Borland derleyicilerinde tipik bir uyar iletisi aadaki gibidir: warning : possibly incorrect assignment! (muhtemelen yanl atama!) Oysa if ayrac iinde atama ileci bilinli olarak da kullanlabilir: if (x = func()) m = 20; Bilinli kullanmda, derleyicinin mantksal uyar iletisinin kaldrlmas iin, ifade aadaki gibi dzenlenebilir:

109/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if ((x = func()) != 0) m = 20; Yukardaki rnekte olduu gibi, atama ilecinin rettii deer, ak olarak bir karlatrma ilecine terim yaplrsa, derleyiciler bu durumda bir "mantksal uyar" iletisi vermez. ok yaplan baka bir hata da, if deyiminin doru ya da yanl ksmn bloklamay unutmaktr. Yani bir bileik deyim yerine yanllkla yaln deyim yazlr. if (x == 10) m = 12; k = 15; Yukardaki if deyiminde yalnzca m = 12; deyimi if deyiminin doru ksmn oluturur. k = 15; deyimi, if deyimi dndadr. Bu durum genellikle programcnn, if deyiminin doru ya da yanl ksmn nce yaln bir deyimle oluturmasndan sonra, doru ya da yanl ksma ikinci bir yaln deyimi eklerken, bloklamay unutmas yznden oluur. Kodun yazl biiminden de, if deyiminin doru ksmnn, yanllkla bloklanmad anlalyor. Dorusu aadaki gibi olmalyd: if (x == 10) { m = 12; k = 15; } Aadaki if deyimi ise, yine if anahtar szc ile elenmeyen bir else anahtar szc kullanld iin geersizdir: if ( x == 10) m = 12; k = 15; else /* Geersiz */ y = 20; Bu tr yanllklardan saknmak iin baz programclar, if deyiminin doru ya da yanl ksm yaln deyimden olusa da, bu basit deyimi bileik deyim olarak yazarlar: if (x > 10) { y = 12; } else { k = 5; } Yukardaki rnekte if deyiminin doru ya da yanl ksmna baka bir yaln deyimin eklenmesi durumunda szdizim hatas ya da bir yanllk olumaz. Ancak gereksiz bloklamadan kanmak okunabilirlik asndan daha dorudur. if ayrac iinde, bir deerin belirli bir aralkta olup olmadnn snanmak istendiini dnn:

110/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (10 < x < 20) func(); Yukardaki if deyiminde, x deikeni 10 20 deerleri arasnda ise func ilevinin arlmas istenmi. Ancak if ayrac iinde yer alan ifade her zaman dorudur. Yani func ilevi ars her zaman yrtlr. Kktr ileci soldan saa ncelik ynne sahip olduu iin, nce daha soldaki kktr ileci deer retir. lecin rettii deerin 1 ya da 0 olduunu biliyorsunuz. retilen 1 ya da 0 deeri, daha sadaki kktr ilecinin terimi olur. 20 deeri, 1 ya da 0'dan daha byk olduuna gre, ifade her zaman dorudur. Tehlikeli bir baka yanllk da, if ayrac iindeki ifadenin bir ilev ar ifadesi olmas durumunda, ilev ar ilecinin unutulmasdr: if (func()) m = 12; yerine if (func) m = 12; yazldn dnn. Bu durum bir szdizim hatas oluturmaz. Bu durumda her zaman, if deyiminin doru ksm yrtlr. C dilinde bir ilev ismi, o ilevin kodunun bellekteki yerine edeer bir adres bilgisi olarak ele alnr. Bu adres bilgisi her zaman sfrdan farkl bir deer olduundan, koul ifadesi her zaman doru olarak deerlendirilir.

Snama levleri

bool veri tr, C'nin doal veri trlerinden olmad iin, C dilinde yazlan snama ilevleri, yani bir soruya yant veren ilevler ounlukla int trne geri dner. [C99 standartlaryla bool tr de doal veri tr olarak eklenmitir. C99 standartlar ile C diline gre _bool anahtar szc eklenmitir.] rnein bir tamsaynn asal say olup olmadn snayan bir ilev yazldn dnelim. levin parametrik yaps aadaki gibi olur: int isprime(int val); Snama ilevlerinin geri dn deerlerinde yaygn olarak kullanlan anlama yledir: Eer ilev, sorulan soruya doru ya da olumlu yant veriyorsa, 0 d herhangi bir deerle geri dner. Sorulan sorunun ya da yaplan snamann sonucu, olumsuz ya da yanl ise, ilev 0 deeriyle geri dner. Bu durum snama ilevini aran kod parasnn iini kolaylatrr, aadaki gibi kalp kodlarn yazlmasna olanak verir: Snamann olumlu sonulanmas durumunda bir i yaplacaksa aadaki gibi bir deyim yazlabilir: if (isprime(val)) deyim; Snamann olumsuz sonulanmas durumunda bir i yaplacaksa if (!isprime(val)) deyim; yazlabilir. Aadaki program inceleyin:

111/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <ctype.h> int main() { int ch; printf("bir karakter girin : "); ch = getchar(); if (isupper(ch)) printf("%c buyuk harf!\n", ch); else printf("%c buyuk harf degil!\n", ch); } return 0;

isupper, kendisine kod numaras gnderilen karakterin, byk harf karakteri olup olmadn snayan standart bir ilevdir. Eer kod numarasn ald karakter byk harf ise ilev, sfrdan farkl bir deere geri dner. Byk harf deil ise, ilevin geri dn deeri 0'dr. Bu durumda main ilevinde yer alan if deyiminin koul ifadesi "ch byk harf ise" anlamna gelir, deil mi? Koul ifadesi if (!isupper(ch)) biiminde yazlsayd, bu "ch byk harf deil ise" anlamna gelirdi. Aada, bir yln artk yl olup olmadn snayan isleap isimli bir ilev tanmlanyor. 4'e tam blnen yllardan, 100'e tam blnmeyenler ya da 400'e tam blnenler artk yldr: #include <stdio.h> int isleap(int y) { return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); } int main() { int year; printf("bir yil girin: "); scanf("%d", &year); if (isleap(year)) printf("%d yili artik yildir!\n", year); else printf("%d yili artik yil degildir!\n", year); return 0; }

Standart Karakter Snama levleri

Karakter snama ilevleri, karakterler hakknda bilgi edinilmesini salayan ilevlerdir. Derleyicilerin ounda bu ilevler, ctype.h balk dosyas iinde ayn zamanda makro olarak tanmlanr. Bu nedenle, karakter snama ilevleri arlmadan nce kaynak koda ctype.h dosyas mutlaka eklenmelidir. Karakter snama ilevleri, ASCII karakter repertuarnn ilk yars iin geerlidir. Yani Trke karakterler iin kullanlmas durumunda

112/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

geri dn deerleri gvenilir deildir. Karakter snama ilevlerinin Trkemize zel , , , , , , , , I, , , , karakterleri iin doru olarak altrlmas, yerelletirme (localization) ile ilgili bir konudur. Bu konuya daha sonraki blmlerde deinilecek. Aada, standart karakter snama ilevlerini ieren bir tablo veriliyor: lev isalpha isupper islower isdigit isxdigit Geri Dn Deeri Alfabetik karakterse doru, deilse yanl. Byk harf ise doru, deilse yanl. Kk harf ise doru, deilse yanl. Saysal bir karakterse doru, deilse yanl. Onaltlk say sistemi basamak simgelerinden birini gsteren bir karakterse, yani 0123456789ABCDEFabcdef karakterlerinden biri ise doru, deilse yanl. Alfabetik ya da saysal bir karakterse doru, deilse yanl. Boluk karakterlerinden biri ise (space, carriage return, new line, vertical tab, form feed) doru, deilse yanl. Noktalama karakterlerinden biriyse, yani kontrol karakterleri, alfanmerik karakterler ve boluk karakterlerinin dndaki karakterlerden ise doru, deilse yanl. Ekranda grlebilen yani print edilebilen bir karakterse (space karakteri dahil) doru, deilse yanl. Ekranda grlebilen bir karakterse (space karakteri dahil deil) doru, deilse yanl. Kontrol karakteri ya da silme karakteri ise (ASCII setinin ilk 32 karakter ya da 127 numaral karakter) doru, deilse yanl.

isalnum isspace ispunct isprint isgraph iscntrl

Bu ilevlerden bazlarn kendimiz yazmaya alalm:

islower, kendisine kod numaras gnderilen karakterin, kk harf karakteri olup olmadn snayan, standart bir ilevdir. Kod numarasn ald karakter kk harf ise ilev, sfr d bir deere, yani mantksal "doru" deerine geri dner. Kk harf deil ise ilevin geri dn deeri sfr deeridir. Bu ilev aadaki biimde yazlabilir: #include <stdio.h> int islower (int ch) { return ch >= 'a' && ch <= 'z'; } int main() { char ch; printf("bir karakter girin: "); ch = getchar(); if (islower(ch)) printf("kk harf\n"); else printf("kk harf deil\n"); return 0; } Yukarda yazlan islower ilevinde nce parametre deikeninin kk harf olup olmad snanyor:

islower levi

113/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ch >= 'a' && ch <= 'z'; Kk harflerin ardk olarak yerletirildii bir karakter kodunda, yukardaki ifadenin deeri, ancak ch deikeninin deerinin, bir kk harfin sra numaras olmas durumunda dorudur.

isalpha da standart bir ilevdir. Parametresine kod numaras aktarlan karakter alfabetik karakterse, yani byk ya da kk harf ise ilev sfr d bir deere, alfabetik bir karakter deilse sfr deerine geri dner. int isalpha (int ch) { return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z'); }

isalpha levi

isdigit, standart bir ilevdir. Parametresine kod numaras aktarlan karakter bir rakam karakteri ise, ilev sfrdan farkl deer ile, rakam karakteri deilse sfr deeri ile geri dner: int isdigit (int ch) { return (ch >= '0' && ch <= '9'); }

isdigit levi

isalnum levi

isalnum da standart bir ilevdir. Parametresine kod numaras aktarlan karakter alfabetik karakter ya da bir rakam karakteri ise sfr d deere, aksi halde sfr deerine dner. Aada bu ilev iki ayr biimde yazlyor:

#include <ctype.h> int isalnum1(int ch) { return ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9'; } int isalnum2(int ch) { return isalpha(ch) || isdigit(ch); }

isxdigit, bir karakterin, onaltlk say sistemine ait bir basamak simge olup olmadn snayan standart bir ilevdir. Eer kod numarasn ald karakter 0123456789ABCDEFabcdef karakterlerinden biri ise ilev sfrdan farkl deere, bu karakterlerden biri deil ise sfr deerine geri dner. int isxdigit (int ch) { return ch >= '0' && ch <= '9' || ch >= 'A' && ch <= 'F' || ch >= 'a' && ch <= 'f'; }

isxdigit levi

114/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Kk harften byk harfe ya da byk harften kk harfe dnm yapmak, sk gereken ilemlerdendir. Standart toupper ve tolower ilevleri bu amala kullanlr.

Standart Karakter Dnm levleri

tolower standart bir C ilevidir. Parametresine kod numaras aktarlan karakter byk harf ise, onun kk harf karlnn kod numarasyla geri dner. tolower ilevine byk harf olmayan bir karakterin kod numaras aktarlrsa, ilev ayn deeri geri dndrr. Aada bu ilev tanmlanyor: #include <stdio.h> int tolower (int ch) { if (ch >= 'A' && ch <= 'Z') return ch - 'A' + 'a'; return ch; } int main() { char ch; printf("bir karakter girin :"); ch = getchar(); printf("%c\n", tolower(ch)); } return 0;

tolower levi

toupper, standart bir C ilevidir. Parametresine sra numaras aktarlan karakter, eer kk harf ise, onun byk harf karlnn sra numarasyla geri dner. toupper ilevine kk harf olmayan bir karakterin sra numaras aktarlrsa, ilev ayn deeri geri dndrr. lev aada tanmlanyor: int toupper(int ch) { if (ch >= 'a' && ch <= 'z') return ch - 'a' + 'A'; return ch; }

toupper levi

Aada iki saydan daha byk olanna geri dnen get_max2 ve saydan en byne geri dnen getmax3 ilevi yazlyor: int get_max2(int a, int b) { if (a > b) return a; return b; }

if Deyimini kullanan rnek Programlar

115/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int get_max3(int a, int b, int c) { int max = a; if (b > max if (c > max } max) = b; max) = c;

return max;

#include <stdio.h> int main() { int x, y, z; printf("iki sayi girin : "); scanf("%d%d", &x, &y); printf("%d ve %d sayilarindan buyugu = %d\n", x, y, get_max2(x, y)); printf("uc sayi girin : "); scanf("%d%d%d", &x, &y, &z); printf("%d %d ve %d sayilarindan en buyugu = %d\n",x, y, z, get_max3(x, y, z)); return 0; } Aadaki programda get_hex_char isimli bir ilev tanmlanyor. lev, kendisine gnderilen 0 ile 15 arasndaki bir tamsaynn onaltlk say sistemindeki simgesi olan karakterin sra numaras ile geri dnyor. #include <stdio.h> int get_hex_char(int number) { if (number >= 0 && number <= 9) return ('0' + number); if (number >= 10 && number <= 15) return ('A' + number - 10); return -1; } int main() { int number; printf("0 ile 15 arasinda bir sayi girin : "); scanf("%d", &number); printf("%d = %c\n", number, get_hex_char(number)); return 0; } Aadaki programda get_hex_val isimli bir ilev tanmlanyor. lev, kendisine kod numaras gnderilen, onaltlk say sisteminde bir basamak gsteren simgenin, onluk say

116/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sistemindeki deerine geri dnyor. leve gnderilen karakter, onaltlk say sistemine ilikin bir karakter deilse, -1 deeri dndrlyor. #include <stdio.h> #include <ctype.h> int get_hex_val(int ch) { ch = toupper(ch); if (isdigit(ch)) return ch - '0'; if (ch >= 'A' && ch <= 'F') return ch - 'A' + 10; return -1; } int main() { char hex; printf("hex digit gsteren bir karakter girin: "); hex = getchar(); printf("%c = %d\n", hex, get_hex_val(hex)); return 0; } Aadaki programda change_case isimli bir ilev tanmlanyor. change_case ilevi, kendisine gnderilen karakter kk harf ise, bu karakteri byk harfe dntryor, byk harf ise karakteri kk harfe dntryor. Eer harf karakteri deilse ilev, karakterin kendi deeriyle geri dnyor. #include <stdio.h> #include <ctype.h> int change_case(int ch) { if (isupper(ch)) return tolower(ch); } return toupper(ch);

int main() { int c; printf("bir karakter girin : "); c = getchar(); c = change_case(c); putchar(c); } return 0;

Aadaki C programnda katsaylar klavyeden alnan ikinci dereceden bir denklem zlyor:

117/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <math.h> int main() { double a, b, c; double delta; printf("denklemin katsayilarini girin\n"); printf("a = "); scanf("%lf", &a); printf("b = "); scanf("%lf", &b); printf("c = "); scanf("%lf", &c); delta = b * b - 4 * a * c; if (delta < 0) printf("denkleminizin gercek koku yok\n"); else if (delta == 0) { printf("denkleminizin tek gercek koku var\n"); printf("kok = %lf\n", -b / (2 * a)); } else { double kokdelta = sqrt(delta); printf("denkleminizin 2 gercek koku var\n"); printf("kok 1 = %lf\n", (-b + kokdelta) / (2 * a)); printf("kok 2 = %lf\n", (-b - kokdelta) / (2 * a)); } } return 0;

118/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

LEV BLDRMLER
Derleme ilemi, derleyici tarafndan kaynak kod iinde yukardan aaya doru yaplr. Derleme aamasnda derleyici, bir ilev ars ile karlatnda, arlan ilevin geri dn deerinin trn bilmek zorundadr. Bir ilevin geri dn deerinin tr, geri dn deerinin hangi CPU yazmacndan (registers) alnacan belirler. Programn doru almas iin derleme zamannda bu bilginin elde edilmesi zorunludur. Eer arlan ilevin tanm, aran ilevden daha nce yer alyorsa, derleyici derleme ilemi srasnda ilev ar ifadesine gelmeden nce, arlan ilevin geri dn deeri tr hakknda zaten bilgi sahibi olur. nk derleme ilemi yukardan aa doru yaplr. Aadaki rnei inceleyin: #include <stdio.h> double get_val(double x, double y) { return x * y / (x + y); } int main() { double d; d = get_val(4.5, 7.3); printf("d = %lf\n", d); } return 0;

Yukardaki rnekte get_val ilevi, kendisini aran main ilevinden daha nce tanmlanyor. ar ifadesine gelmeden nce derleyici, get_val ilevinin geri dn deeri trn zaten bilir. arlan ilevin tanm aran ilevden daha sonra yaplrsa, derleyici ilev ar ifadesine geldiinde, arlan ilevin geri dn deerinin trn bilemez. Bu sorunlu bir durumdur: #include <stdio.h> int main() { double d; //ilevin geri dn deerininin tr bilinmiyor. d = get_val(4.5, 7.3); printf("d = %lf\n", d); return 0; } double get_val(double x, double y) { return x * y / (x + y); } Yukarda get_val ilevi, main ilevi iinde arlyor. Fakat get_val ilevinin tanm, kaynak kod iinde main ilevinden daha sonra yer alyor. Derleyici, derleme zamannda get_val ilevinin ars ile karlatnda, bu ilevin geri dn deerinin trn bilmez.

119/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C derleyicisi bir ilev ar ifadesi ile karlatnda, ilevin geri dn deeri tr hakknda henz bilgi edinememise, sz konusu ilevin geri dn deerinin int trden olduunu varsayar. Yukardaki rnekte derleyici, get_val ilevinin geri dn deerinin int trden olduunu varsayar, buna gre kod retir. Daha sonra derleme ak ilevin tanmna geldiinde, bu kez ilevin geri dn deerinin double trnden olduu grlr. Hedef kod oluumunu engelleyen bu elikili durumu derleyiciler bir hata iletisi ile bildirir. [C++ dilinde eer arlan ilev aran ilevden daha nce tanmlanmamsa, ilevin geri dn deeri int trden kabul edilmez. Bu durumda ilev bildiriminin yaplmas zorunludur. Bu bildirimin yaplmamas durumunda derleme zamannda hata oluur.] arlan ilevi, aran ilevin stnde tanmlamak her zaman mmkn deildir. Byk bir kaynak dosyada onlarca ilev tanmlanabilir. Tanmlanan her ilevin birbirini armas sz konusu olabilir. Bu durumda arlacak ilevin aran ilevden nce tanmlanmas ok zor olur. Kald ki, C dilinde iki ilev birbirini de arabilir. Bu, zyinelemeli (recursive) bir ar dzeneine karlk gelir. Bu tr bir ilev tasarmnda, artk arlan ilevin daha nce tanmlanmas mmkn olamaz. double func1() { /***/ func2(); /***/ } double func2() { /***/ func1(); /***/ } Yukardaki ilev tanmlamalarndan hangisi daha yukar yerletirilirse yerletirilsin, yine de elikili bir durum sz konusu olur. Dier taraftan, arlan ilevlerin tanmlar ou zaman ayn kaynak dosya iinde yer almaz. Bu durumda derleyicinin arlan ilev hakknda bilgi almas nasl gerekleir?

lev Bildirimi Nedir

lev bildirimi, derleyiciye bir ilev hakknda bilgi veren bir deyimdir. Derleyici, ilev arsna ilikin kodu buradan ald bilgiye gre retir. Ayrca derleyici, ald bu bilgiyle, baz kontroller de yapabilir. Yapt kontroller sonucunda hata ya da uyar iletileri reterek olas yanllklar engeller.

lev Bildirimlerinin Genel Biimi

Bir ilev bildiriminin genel biimi aadaki gibidir: [geri dn deeri tr] <ilev ismi> ([tr1], [tr2].....); rnein get_val ilevi iin bildirim aadaki biimde yaplabilir: double get_val(double, double); Derleyici byle bir bildirimden aadaki bilgileri elde eder: 1. get_val ilevin geri dn deeri double trdendir. Bu bilgiden sonra artk derleyici bu ilevin arlmas durumunda geri dn deerini int trden varsaymaz, double trden bir geri dn deeri elde edilmesine gre bir kod retir. 120/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

2. get_val ilevinin iki parametre deikeni vardr. Bu bilgilendirmeden sonra artk derleyici bu ilevin doru sayda argman ile arlp arlmadn sorgulama ansna sahip olur. Eer ilevin yanl sayda argman ile arldn grrse, durumu bir hata iletisi ile bildirir. 3. get_val ilevinin parametre deikenleri double trdendir. Bu bilgiden sonra derleyici ileve baka trden argmanlar gnderilmesi durumunda, argmanlar zerinde otomatik tr dnm uygular. Bu konu "otomatik tr dnm" isimli blmde ele alnacak. Aada rnek bildirimler veriliyor: int multiply (int, int); double power (double, double); void clrscr(void); Tpk ilev tanmlamalarnda olduu gibi, ilev bildirimlerinde de ilevin geri dn deeri belirtilmemise, derleyici bildirimin int trden bir geri dn deeri iin yapldn kabul eder: func(double); bildirimi ile int func(double); bildirimi tamamen edeerdir. Ancak okunabilirlik asndan int anahtar szcnn aka yazlmas daha iyidir. [C++ dilinde geri dn deerinin trnn yazlmas zorunludur.] Eer bildirilen ilev, geri dn deeri retmiyorsa, void anahtar szc kullanlmaldr: void func(double); lev bildirimleri ile, yalnzca derleyiciye bilgi verilir. Bu bir tanmlama (definition) ilemi deildir. Dolaysyla yaplan bildirim sonucunda derleyici programn alma zamanna ynelik olarak bellekte bir yer ayrmaz.

Bildirimde Parametre Ayracnn inin Bo Braklmas


C'nin standartlatrma sreci ncesinde ilev bildirimlerinde,parametre ayracnn ii bo braklyordu. Bildirimde, ilevin parametre deikenlerinin trleri ve says hakknda bir bilgi verilmiyordu. Bildirimin tek amac, bir ilevin geri dn deerinin tr hakknda bilgi vermekti. Dolaysyla aadaki gibi bir bildirim double func(); func ilevin parametre deikenine sahip olmad anlamna gelmiyordu. Standartlatrma sreci iinde ilev bildirimlerine yaplan eklemeyle, ilevlerin parametre deikenlerinin says ve trleri hakknda da bilgi verilmesi olana verildi. Ancak bu durumda da ortaya yle bir sorun kt. Eski kurallara gre yazlan bir kod yeni kurallara gre derlendiinde double func(); gibi bir bildirim, ilevin parametre deikenine sahip olmad biiminde yorumlanrsa, bildirilen ilevin rnein func(5)

121/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

biiminde arlmas durumunda bir szdizim hatas ortaya kard. Gemie doru uyumluluu salamak iin yle bir karar alnd. Eer ilevin parametre deikeni yoksa bildirimde parametre ayracnn iine void anahtar szc yazlmaldr. Aadaki bildirimleri inceleyin: double foo(); double func(void); Standartlara gre foo ilevinin bildiriminden derleyici, foo ilevinin parametre deikenleri hakknda bir bilginin verilmedii sonucunu karr ve ilev arsyla karlatnda ileve gnderilen argmanlarn saysna ilikin bir kontrol yapmaz. Yani byle bildirilen bir ilev, kurallara uygun bir ekilde istenen sayda bir argmanla arlabilir. func ilevinin bildiriminden derleyici, func ilevin parametre deikenine sahip olmad sonucunu karr ve ilev arsyla karlatnda, ileve bir ya da daha fazla sayda argman gnderildiini grrse, bu durumu derleme zaman hatas olarak belirler. [C++ dilinde ise her iki bildirim de edeerdir. Yani ilev bildiriminde parametre ayracnn iinin bo braklmasyla buraya void anahtar szcnn yazlmas arasnda bir fark yoktur]

Bildirimlerde Parametre Deikenleri in sim yazlmas

lev bildirimlerinde, parametre deikenlerinin trlerinden sonra isimleri de yazlabilir. Bildirimlerde yer alan parametre deikenleri isimlerinin bilinirlik alanlar, yalnzca bildirim parametre ayrac ile snrldr. Standartlar bu durumu, ayr bir bilinirlik alan kural ile belirlemitir. lev bildirim ayrac iinde kullanlan isimler, yalnzca bu ayra iinde bilinir. Bu ayracn dna kldnda bu isimler bilinmez. Bu bilinirlik alan kuralna "lev Bildirimi Bilinirlik Alan Kural" (function prototype scope) denir. Buraya yazlan parametre isimleri, yalnzca okunabilirlik asndan faydaldr. Buradaki parametre isimlerinin, ilevin tanmnda kullanlacak parametre isimleriyle ayn olmas gibi bir zorunluluk yoktur. Yukardaki bildirimleri parametre deikenlerine isim vererek yeniden yazalm: float calculate(float a, float b); int multiply(int number1, int number2); double pow(double base, double exp); levlerin tanmlarn grmeden yalnzca ilevlerin bildirimlerini okuyanlar, bildirimlerde kullanlan parametre deikeni isimlerinden, bu deikenlerin ilev arlarndan hangi bilgileri bekledikleri konusunda fikir sahibi olurlar.

Bir ilevin bildirimi, programn herhangi bir yerinde yaplabilir. Bildirimler global dzeyde yaplmsa, yani tm bloklarn dnda yaplmsa, bildirildikleri yerden dosya sonuna kadar olan alan iinde geerliliklerini srdrr. Sz konusu ilev arlmadan, ilevin bildirimi yaplm olmaldr. Ancak uygulamalarda ok az rastlanmasna karlk, ilev bildirimleri yerel dzeyde de yaplabilir. Bu durumda bildirim ile, yalnzca bildirimin yaplm olduu bloa bilgi verilmi olur. Baka bir deyile ilev bildirimi de, deiken tanmlamalar gibi, bilinirlik alan kuralna uyar. Genel olarak ilev bildirimleri programn en yukarsnda ya da programcnn tanmlad balk dosyalarnn birinin iinde yaplr. Balk dosyalar (header files), ileride ayrntl olarak ele alnacak.

lev Bildirimlerinin Yerleri

Standart C ilevlerinin bildirimleri, standart balk dosyalar iine yerletirilmitir. Programc, uygulamalarda standart bir C ilevinin bildirimini kendi yazmaz, bu bildiriminin bulunduu balk dosyasn #include nilemci komutuyla kendi kaynak koduna ekler.

Standart C levlerinin Bildirimleri

122/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

arlan bir ilevin kaynak kodunun, ayn kaynak dosya iinde yer almas gibi bir zorunluluk yoktur. Derleyici, bir ilev ars ile karlatnda, arlan ilevin kaynak kodunu aramaz. rettii hedef dosya (object file) iine, balayc iin, ilgili ilevin arldn belirten bir bilgi yazar. arlan ilevin derlenmi kodunu bulmak ve hedef dosyalar uygun bir biimde birletirmek, balayc programn grevidir. arlan bir ilevin tanmnn ayn kaynak dosyada olmadn dnelim. Bu durumda arlan ilevin bildirimi yaplmamsa ne olur? C dilinde bu durum bir szdizim hatas deildir. C++ dilinde bu durum dorudan szdizim hatasdr. Derleyici, bir ilevin arlmasndan nce, sz konusu ilevin tanmn ya da bildirimini mutlaka grmek zorundadr. C dilinde derleyici, bildirimini grmedii bir ilevin geri dn deerini int trden kabul ederek hedef dosyay retir. Eer kaynak kod iinde ilevin geri dn deeri kullanlmamsa, ya da arlan ilevin geri dn deeri gerekten int trden ise bir sorun kmaz. Ancak ilevin geri dn deeri kullanlmsa ve geri dn deeri int trden deilse, bir alma zaman hatas sz konusudur. Aadaki rnei derleyicinizde derleyerek altrn: #include <stdio.h> int main() { double d; d = sqrt(9.); printf("d = %lf\n", d); } return 0;

lev Bildiriminin Yaplmamas

Yukardaki programn derlenmesi srasnda bir hata olumaz. Ancak derleyici, arlan sqrt ilevinin geri dn deerinin int trden olduunu varsayarak kod retir. Oysa, derlenmi sqrt ilevinin geri dn deeri double trdendir. alma zaman srasnda, ilevin geri dndrd double trden deer yerine, derleyicinin rettii kod sonucunda, int trden bir deer ekilmeye allr. Bu, programn alma zamanna ynelik bir hatadr. imdi de main ilevinden nce aadaki bildirimi ekleyerek program yeniden derleyerek altrn: double sqrt(double val); Standart C ilevlerinin bildirimleri, sonu .h uzantl olan balk (header) dosyalar iindedir. #include nilemci komutuyla ilgili balk dosyasnn kaynak koda eklenmesiyle, aslnda standart C ilevlerinin de bildirimi yaplm olur. Zira nilemci programn kts olan kaynak program, artk derleyiciye verildiinde, eklenmi bu dosyada ilevin bildirimi de bulunur. phesiz, ilgili balk dosyasn kaynak koda eklemek yerine, standart C ilevlerinin bildirimleri programc tarafndan da yaplabilir. Bu durumda da bir yanllk sz konusu olmaz. Yukardaki rnekte arlan sqrt ilevinin bildirimi iki ekilde kaynak koda eklenebilir: i. lev bildiriminin bulunduu balk dosyasnn, bir nilemci komutuyla kaynak koda eklenmesiyle: #include <math.h> ii) levin bildirimi dorudan yazlabilir:

123/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

double sqrt(double val); Ancak tercih edilecek yntem balk dosyasn kaynak koda eklemek olmaldr. nk programc tarafndan ilevin bildirimi yanl yazlabilir. Balk dosyalarnn kaynak koda eklenmesinin nedeni yalnzca ilev bildirimi deildir. Balk dosyalarnda daha baka bildirimler de vardr: Makrolar, simgesel deimezler, tr ismi bildirimleri, yap bildirimleri vs.

lev bildirimlerin ana amac, yukarda da belirtildii gibi, derleyiciye ilevin geri dn deeri tr hakknda bilgi vermektir. Ancak ilev bildirimlerinde ilev parametrelerinin trleri belirtilmise, derleyici prototip bildirimindeki parametre deikeni saysn ilev ar ifadesindeki ileve gnderilen argman says ile karlatrr. rnein: float process(float, float); biiminde bir bildirim yazldnda eer process ilevi eksik ya da fazla argman ile arlrsa derleme zamannda hata oluur. x = process(5.8); y = process(4.6, 7.9, 8.0) /* Geersiz! Eksik argman ile ar*/ /* Geersiz! Fazla argman ile ar*/

lev Bildirimi le Argman-Parametre Uyumu Sorgulamas

Ayn trden geri dn deerine sahip ilevlerin bildirimi, virgllerle ayrlarak yazlabilir, ama byle bir bildirim biimi, programclar tarafndan genel olarak pek tercih edilen bir durum deildir. double func1(int), func2(int, int), func3(float); Yukardaki bildirim geerlidir. Byle bir bildirimle, derleyiciye func1, func2, func3 ilevlerinin hepsinin geri dn deerinin double trden olduu bilgisi verilir. lev bildirimleri, deiken tanmlamalaryla da birletirilebilir. Bu da okunabilirlik asndan tercih edilen bir durum deildir. long func1(int), long func2(void), x, y; Yukardaki bildirim deyimi ile func1 ve func2 ilevlerinin bildirimi yaplrken, x ve y deikenleri tanmlanyor. Bir ilevin bildiriminin yaplm olmas, o ilevin tanmlamasn ya da arlmasn zorunlu klmaz. Bildirimi yaplan bir ilevi tanmlamamak hata oluturmaz. Bir ilevin bildirimi birden fazla kez yaplabilir. Bu durum, bir derleme zaman hatas oluturmaz. Ama yaplan bildirimler birbirleriyle elimemelidir. Kaynak dosya iinde ayn ileve ilikin bildirimlerin farkl yerlerde, aadaki biimlerde yapldn dnelim: int func (int, int); func (int, int); int func(int x, int y); func(int number1, int number2); Yukardaki bildirimlerinin hibirinde bir eliki sz konusu deildir. lev parametre deikenlerinin isimleri iin daha sonraki bildirimlerde farkl isimler kullanlmas bir eliki yaratmaz. nk bu isimlerin bilinirlik alan (scope), yalnzca bildirimin yapld ayracn iidir. Ancak aadaki farkl bildirimler geersizdir.

Bildirim Szdizimine likin Ayrntlar

124/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

double func(int x, double y); double func(int x, float y); /* Geersiz! */ long sample(double x); sample (double x); /* Geersiz! */

C ve C++ dillerinde bir proje, ou zaman birden fazla kaynak dosyadan, yani modlden oluur. Kaynak dosyalarn ounda, baka kaynak dosyalar iinde tanmlanan ilevler arlr. Yani ou zaman kaynak dosyalar arasnda bir hizmet alma verme ilikisi vardr. Baka modllere hizmet verecek bir modl, iki ayr dosya eklinde yazlr. Dosyalardan biri kodlama (implementation) dosyasdr. Bu dosyann uzants .c dir. Bu dosya tarafndan dier modllere sunulan hizmetlere ilikin bildirimler, .h uzantl baka bir dosyaya yerletirilir. Bu dosyaya balk dosyas (header file) denir. Dier modllere hizmet verecek ilevlerin bildirimleri, balk dosyas iine yerletirilmelidir. Hizmet alan kodlama dosyas, hizmet veren modln balk dosyasnn ieriini, kendi dosyasna #include nilemci komutuyla ekler. Bylece bildirim yaplm olur. Bu durum "nilemci komutlar" konusunda yeniden ele alnacak.

lev Bildirimlerinin Yerleri

125/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

126/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

TR DNMLER
Bilgisayarlarn aritmetik ilemleri gerekletirmesinde bir takm kstlamalar sz konusudur. Bilgisayarlarn aritmetik bir ilemi gerekletirmesi iin genellikle ileme sokulan terimlerin uzunluklarnn ayn olmas, yani bit saylarnn ayn olmas ve bellekte ayn formatta ifade edilmeleri gerekir. rnein ilemci 16 bit uzunluunda iki tam sayy dorudan toplayabilir ama 16 bit uzunluunda bir tam say ile 32 bit uzunluundaki bir gerek sayy dorudan toplayamaz. C programlama dili, deiik trlerin ayn ifade iinde bulunmalarna izin verir. Yani tek bir ifadede bir tamsay trnden deiken, float trden bir deimez ya da char trden bir deiken birlikte yer alabilir. Bu durumda C derleyicisi, bunlar herhangi bir ileme sokmadan nce, bilgisayar donanmnn ifadeyi deerlendirebilmesi iin uygun tr dnmlerini yapar. rnein 16 bitlik int trden bir deerle 64 bitlik double trden bir deer toplandnda, nce 16 bitlik int trden deer, 64 bit uzunluunda double trden bir deer olarak ifade edilir, daha sonra toplama ilemi gerekletirilir. Yine 16 bitlik bir int trden bir deerle 64 bitlik bir double trden bir deer arpldnda derleyici, nce int trden deeri 64 bitlik double trden bir deere dntrr. Bu tr dnm daha karmaktr, nk int ve double trden deerler bellekte farkl biimlerde tutulur. Bu tr dnmler, programcnn bir kod yazmasna gerek duyulmakszn otomatik olarak gerekletirilir. Byle dnmlere, otomatik tr dnmleri (implicit type conversions) diyeceiz. Dier taraftan C dili, programcya herhangi bir ifadeyi, bir ile kullanarak baka bir trden ele alma olana da verir. Programc tarafndan yaplan byle tr dnmlerine bilinli tr dnmleri (explicit type conversions/type casts) diyeceiz. nce otomatik tr dnmlerini inceleyelim. Otomatik tr dnmleri ne yazk ki karmak yapdadr ve iyi renilmemesi durumunda programlarda hatalar kanlmazdr. C'de 11 ayr doal veri tr olduunu biliyorsunuz. Herhangi bir hataya neden olmamak iin bunlarn her trl ikili bileimi iin nasl bir otomatik tr dnm yaplacan ok iyi bilmek gerekir.

Aada belirtilen drt durumda mutlaka otomatik bir tr dnm yaplr: 1. Aritmetik ya da mantksal bir ifadenin terimleri ayn trden deilse: Byle yaplan tr dnmlerine ilem ncesi tr dnmleri diyeceiz. 2. Atama ileci kullanldnda atama ilecinin sa tarafndaki ifadenin tr ile sol tarafndaki ifadenin tr ayn deilse: double toplam; long sayi1, sayi2; /***/ toplam = sayi1 + sayi2; Bu durumda yaplan tr dnmlerine atama tr dnmleri diyeceiz. 3. Bir ilev arsnda ileve gnderilen bir argmann tr ile ilevin ilgili parametre deikeninin tr ayn deilse: double sqrt (double val); void func() { int number; double result = sqrt(number); /***/ }

Hangi Durumlarda Tr Dnm Yaplr

127/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rnekte arlan sqrt ilevine gnderilen argman olan number deikeninin tr, arlan ilevin parametre deikeninin trnden farkldr. Bu durumda bir tr dntrme ilemi yaplr. 4. Bir return ifadesinin tr ile ilgili ilevin geri dn deerinin tr ayn deilse: double func(int val) { /****/ return val; } Yukarda tanmlanan func isimli ilevin iinde yer alan return ifadesinin tr int iken, ilevin bildirilen geri dn deeri tr double trdr. Bu durumda da, derleyici tarafndan bir otomatik tr dntrme ilemi yaplr. 3. ve 4. maddeler de bir atama ilemi olarak dnlebilir. lev ar ifadesindeki argmanlar, parametre deikenlerine kopyalanarak, geirilir. Yani rtl bir atama ilemi sz konusudur. Yine return ifadeleri de aslnda ilevlerin geri dn deerlerini tutacak geici nesnelere kopyalanrlar.

lem ncesi otomatik tr dnmleri, iki terimli ilelerin bulunduu ifadelerde terimlerin trlerinin farkl olmas durumunda uygulanr. Otomatik tr dnm sonucunda, farkl iki tr olmas durumu ortadan kaldrlarak terimlerin 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. Terimlerden biri int, dieri double trdendir. Bu durumda derleyici, terimlerden birini geici bir blgede dierinin trnden ifade edecek bir kod retir. Dolaysyla ilem, ortak olan trde yaplr. Peki int trnden olan terim mi double trnde ifade edilir, yoksa double trnden olan terim mi int trnde ifade edilir? Derleyici byle bir dnm bilgi kayb olmayacak biimde yapmaya alr. Bu durumda bilgi kaybn engellemek iin genel olarak daha kk trden olan terim, daha byk trde olan terimin trnde ifade edilir. Kurallar ayrntl olarak renmek iin oluabilecek durumlar iki ana grup altnda inceleyelim: 1. Terimlerden biri gerek say trlerinden birine ait ise: Terimlerden birinin long double trnden, dierinin farkl bir trden olmas durumunda dier terim long double trnde ifade edilir ve ilem long double trnde yaplr. Terimlerden birinin double trnden, dierinin farkl bir trden olmas durumunda dier terim double trnde ifade edilir ve ilem double trnde yaplr. Terimlerden birinin float trnden, dierinin farkl bir trden olmas durumunda dier terim float trnde ifade edilir ve ilem float trnde yaplr. 2. Terimlerden hibiri gerek say trlerinden deilse: Eer ifade iindeki terimlerden herhangi biri signed char, unsigned char, signed short int ya da unsigned short int trden ise aadaki algoritma uygulanmadan nce bu trler int

lem ncesi Aritmetik Tr Dnmleri

128/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

trne dntrlr. Yaplan bu dnme "tam sayya ykseltme" (integral promotion) denir. Daha sonra aadaki kurallar uygulanr: Terimlerden birinin unsigned long trnden, dierinin farkl bir trden olmas durumunda dier terim unsigned long trnde ifade edilir ve ilem unsigned long trnde yaplr. Terimlerden birinin signed long trnden, dierinin farkl bir trden olmas durumunda dier terim signed long trnde ifade edilir ve ilem signed long trnde yaplr. Terimlerden birinin unsigned int trnden, dierinin farkl bir trden olmas durumunda dier terim unsigned int trnde ifade edilir ve ilem unsigned int trnde yaplr. stisnalar: Eer terimlerden biri signed long int dieri unsigned int trnden ise ve kullanlan sistemde bu trlerin uzunluklar ayn ise (UNIX ve Win 32 sistemlerinde olduu gibi) her iki terim de unsigned long int trne dntrlr. Eer terimlerden biri signed int dieri unsigned short int trnden ise ve kullanlan sistemde bu trlerin uzunluklar ayn ise (DOS iletim sisteminde olduu gibi) her iki terim de unsigned int trne dntrlr. aretli bir tamsay trnden iaretsiz tamsay trne dnm yaplmas durumunda dikkatli olunmaldr: #include <stdio.h> int main() { int x = -2; unsigned int y = 1; if (y > x) printf("dogru!\n"); else printf("yanlis!\n"); } return 0;

Yukardaki programn almasyla ekrana "yanl" yazs yazdrlr. y > x ifadesinde '>' ilecinin sol terimi unsigned int trnden iken sa terimi int trdendir. Bu durumda yaplacak otomatik tr dntrme ilemi sonucunda int trden olan terim, ilem ncesinde unsigned int trnde ifade edilir. -2 deeri unsigned int trnde ifade edildiinde artk negatif bir deer olmayp byk bir pozitif say olur. rnein 2 byte' lk int tr sz konusu olduunda bu deer 65534'tr. Dolaysyla y > x ifadesi yanl olarak yorumlanr. nk otomatik tr dnmnden sonra y > x ifadesi artk yanltr. lev ar ifadeleri de, ilelerle birlikte baka ifadeleri oluturuyorsa, otomatik tr dnmlerine neden olabilir. Zira geri dn deerine sahip olan ilevler iin ileve yaplan ar ifadesi, ilevin geri dn deerine karlk gelir. rnein: int i = 5; ... pow(2, 3) + i

129/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ifadesinde pow ilevinin geri dn deeri double trden olduu iin, int trden olan i deikeni de, ilemin yaplabilmesi iin geici bir blgede double trnde ifade edilerek ileme sokulur.

Atama Tr Dnmleri
Bu tr dnmlerin ok basit bir kural vardr: Atama ncesinde, atama ilecinin sa tarafndaki ifade, atama ilecinin sol tarafndaki nesnenin trnde ifade edilir: Kk trlerin byk trlere dntrlmesinde bilgi kayb sz konusu deildir. rnein: double leftx; int righty = 5; leftx = righty; Yukardaki rnekte righty deikeninin tr int'tir. nce double tre otomatik dnm yaplr, daha sonra double trnde ifade edilen righty deikeninin deeri leftx deikenine atanr. Aada 16 bitlik sistemler iin baz rnekler veriliyor: TR int char int char desimal 138 'd' (100) -56 '\x95' (07) unsigned int 45678 char '0' (48) hex 0x008A 0x64 0xFFC8 0x95 0xB26E 0x30 dntrlecek tr long int int long int int long int long int hex 0x0000008A 0x0064 0xFFFFFFFC8 0xFF95 0X0000B26EL 0x00000030L desimal 138L 100 -56L -107 45678 30L

Negatif olan bir tamsay, kk trden byk tre dntrldnde saynn yksek anlaml bitleri, negatifliin korunmas amacyla 1 biti ile beslenir. Derleyici tarafndan yaplan atama tr dnmlerinde, atama ncesi, byk trn kk tre dntrlmesi durumunda bilgi kayb sz konusu olabilir. Aadaki basit kurallar verilebilir: Eer atama ilecinin her iki taraf da tam say trlerinden ise (char, short, int, long), atama ilecinin sa tarafnn daha byk bir trden olmas durumunda bilgi kayb olabilir. Bilgi kayb ancak, atama ilecinin sa tarafndaki deerin, sol taraftaki trn snrlar iinde olmamas durumunda sz konusu olur. Bilgi kayb, yksek anlaml byte'larn kaybolmas eklinde ortaya kar. rnek: long m = 0x12345678; int y = m; printf ("m = %x\n", m); Yukardaki rnekte int trden olan y deikenine long trden bir deikenin deeri atanmtr. Kodun 16 bitlik bir sistemde, rnein DOS altnda altn dnyoruz. DOS altnda int tr iin say snrlar 32768 +32767 deerleridir. Bu saylar da, iki byte'lk bir alan iin iaretli olarak yazlabilecek en byk ve en kk deerlerdir. Onaltlk say sisteminde her bir basamak 4 bite ve her iki basamak 1 byte alana karlk gelir. Dolaysyla 0x12345678 says 8 hex basamak, yani 4 byte uzunluunda bir saydr. Oysa atamann yaplaca nesne int trdendir ve bu tr max. 4 hex basamak (2 byte) uzunlukta olabilir. Bu durumda m deikenine ilikin deerin yksek anlaml 2 byte' yani (4 hex basma) yitirilir. Atama ileminden sonra, printf ileviyle, y deikeninin deeri 5678 olarak yazdrlr.

130/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Atama ilecinin sa terimi, bir gerek say trnden ise(float, double, long double) ve sol terimi tam say trnden ise nce gerek say deerinin ondalk ksm kaybedilir. Eer gerek saydan elde edilen tamsay ksm, atamann yapld tamsay trnden ifade edilemiyorsa, bu durum tanmsz davrantr (undefined behaviour). Bu durumun olutuu kodlardan kesinlikle kanmak gerekir. Ama derleyicilerin hemen hepsi, bu durumda aadaki ekilde tr dnm yapar: Atama ilecinin sa terimi olan gerek say bir ondalk ksm ieriyorsa, nce ondalk ksm kaybedilir. Ondalk ksm kaybedildikten sonra kalan tamsay deer, eer sol terimin trnn snrlar iinde kalyorsa daha fazla bir bilgi kayb olmaz, fakat sol taraf trnn snrlar alyorsa fazladan bir bilgi kayb daha olur ve bu kez yksek anlaml byte'lar kaybedilir. rnek: #include <stdio.h> int main() { double y = 234.12; int x; x = y; printf("x = %d\n", x); /* x deikenine 234 deeri atanr*/ y = 7689523345.347; x = y; /* Yanl */ printf("x = %d\n", x); } return 0;

imdi de aadaki program derleyerek altrn: #include <stdio.h> int main() { char ch; ch = 135; if (ch == 135) printf("dogru!\n"); else printf("yanl!\n"); } return 0;

Program altrldnda ekrana neden "yanl" yazs yazdrlyor? ch deikenine 135 deeri atanyor: ch = 135; Bu durumda yksek anlaml byte kaybedileceinden ch deikenine atanan deer 1000 0111 deeri olur.

131/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ch == 135 karlatrma ileminde char trden olan ch deikeni, karlatrma ilemi ncesi signed int trne ykseltilir. lem ncesi tamsayya ykseltme sonucu, yksek anlam byte'ler 1 bitleriyle beslenir. nk ch negatif bir deere sahiptir. Karlatrma ilemi ncesinde ch'nin deeri 1111 1111 1000 0111 olur. Oysa karlatrma ilecinin sa terimi olan 135 deeri, int trden bir deimezdir. Yani aslnda karlatrlan deerler aadaki gibi olur: 1111 1111 1000 0111 0000 0000 1000 0111 Karlatrma yanl olarak sonulanr.

Tamsayya Ykseltme

Daha nce de akland gibi tamsayya ykseltme (integral promotion), bir ifade iinde bulunan char, unsigned char, short, unsigned short trlerinin, ifadenin derleyici tarafndan deerlendirilmesinden nce, otomatik olarak int trne dntrlmeleri anlamna gelir. Peki dnm, signed int trne mi, unsigned int trne mi yaplr? Genel kural udur: Tr dnmne urayacak terimin deeri int trnde ifade edilebiliyorsa int, edilemiyorsa unsigned int trne dnm yaplr. rnein unsigned short ve int trlerinin ayn uzunlukta olduu DOS iletim sisteminde unsigned short tr, tamsayya ykseltilirken unsigned int trne dntrlr. Eer tam sayya ykseltilecek deer, signed char, unsigned char ya da signed short trlerinden ise, dnm signed int trne yaplr. Bilgi kayb ile ilgili u hususu da gz ard etmemeliyiz. Baz durumlarda bilgi kayb tr dnm yapld iin deil yaplmad iin oluur. Snr deer tamalar, bu duruma iyi bir rnek olabilir. rnek: (DOS altnda altmz dnelim) long x = 1000 * 2000; Yukardaki kod ilk bakta normal gibi grnyor. Zira arpma ileminin sonucu olan 2000000 deeri DOS altnda signed long tr say snrlar iinde kalr. Oysa bilgi kayb atama ileminden nce gerekleir. 1000 ve 2000 int trden deimezlerdir, ileme sokulduklarnda arpma ilecinin de rettii deer int trden olur. Bu durumda 2 byte uzunlukta olan int tr, 2000000 deerini tutamayaca iin yksek anlaml byte kaybedilir. 2000000 onaltlk say sisteminde 0x1E8480 olarak gsterilebilir. Yksek anlaml byte kaybedilince ilem sonucu, 0x8480 olur. 0x8480 negatif bir saydr. nk iaret biti 1'dir. kiye tmleyenini alrsak 0x8480 ikiye tmleyeni 1000 0100 1000 0000 0111 1011 1000 0000 (0x7B80 = 31616)

Grld gibi ilem sonucu retilecek deer 31616 dir. Bu durumda x deikeninin tr long tr de olsa, atanacak deer 31616 olur.

lev arlarnda Tr Dnm

Daha nce sylendii gibi, bir ileve gnderilen argmanlarla, bu argmanlar tutacak ilevin parametre deikenleri arasnda tr fark varsa otomatik tr dnm gerekleir

132/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ve argmanlarn tr, parametre deikenlerinin trlerine dntrlr. Ancak bu tr dnmnn gereklemesi iin, derleyicinin ilev ar ifadesine gelmeden nce ilevin parametre deikenlerinin trleri hakknda bilgi sahibi olmas gerekir. Derleyici bu bilgiyi iki ayr ekilde elde edebilir: arlan ilev aran ilevden daha nce tanmlanmsa derleyici, ilevin tanmlamasndan parametre deikenlerinin trn belirler. levin bildirimi yaplmsa derleyici, parametre deikenlerinin tr hakknda nceden bilgi sahibi olur. Aadaki rnei inceleyin: #include <stdio.h> double func(double x, double y) { /***/ } int main() { int a, b; /***/ func(a, b); } return 0;

Yukardaki rnekte main ilevi iinde arlan func ilevine argman olarak, int trden olan a ve b deikenlerinin deerleri gnderiliyor. lev tanm ar ifadesinden nce yer ald iin int trden olan a ve b deikenlerinin deerleri, double trne dntrlerek func ilevinin parametre deikenleri olan x ve y deikenlerine aktarlr. func ilevinin main ilevinden sonra tanmlanmas durumunda, otomatik tr dnmnn yaplabilmesi iin, ilev bildirimi ile derleyiciye parametre deikenlerinin trleri hakknda bilgi verilmesi gerekir. #include <stdio.h> double func(double x, double y); int main() { int a, b; /***/ func(a, b); } return 0;

double func(double x, double y) { /***/ } Peki arlan ilev aran ilevden daha sonra tanmlanmsa ve ilev bildirimi yaplmamsa -tabi bu durumda derleme zamannda hata olumamas iin ilevin int trden bir geri dn deerine sahip olmas gerekir- tr dnm gerekleebilecek mi? Bu durumda derleyici, ilevin parametre deikenlerinin trleri hakknda bilgi sahibi olamayaca iin, ileve gnderilen argmanlara, varsaylan argman dnm denilen ilemi uygular. Varsaylan argman dnm u ekilde olur:

133/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char ya da short trnden olan argmanlar tamsayya ykseltilir (integral promotion). float trnden olan argmanlar double trne dntrlr. Bunun dndaki trlerden olan argmanlar iin tr dnm yaplmaz.

Tr dntrme ileci (typecast operator) ile bir ifade bir ileme sokulmadan nce baka bir trden ifade edilebilir. Tr dntrme ileci, nek konumunda bulunan tek terimli bir iletir. le, bir ayra ve ayra iine yazlan bir tr bilgisinden oluur: (double)x lecin rettii deer, terimi olan ifadenin ayra iindeki trden ifade edilmi deeridir. Tr dntrme ileci de, dier tm tek terimli ileler gibi, ile ncelik tablosunun ikinci ncelik seviyesinde bulunur. Aadaki program derleyerek altrn: #include <stdio.h> int main() { int x = 10; int y = 4; double z; z = (double)x / y; printf("z = %lf\n", z); return 0; } Yukardaki programda z = (double)x / y ifadesinde nce tr dntrme ileci deer retir. Tr dntrme ilecinin rettii deer, x nesnesinin double trde ifade edilmi deeridir. Bu durumda blme ilecine sra geldiinde blme ilecinin terimi double trden 10 deeri olur. Bu kez de otomatik tr dnm ile blme ilecinin sa terimi double trne dntrlerek blme ilemi double trnde yaplr. Bu durumda blme ileci 2.5 deerini retir. phesiz ifade aadaki biimde yazlsayd yine bilgi kayb olumazd: z = x /(double)y Ancak ifade aadaki gibi yazlsayd: z = (double) (x / y) bu durumda tr dntrme ilecinin terimi (x / y) ifadesi olurdu. Bu da bilgi kaybn engellemezdi. Bir verinin istenerek kaybedilmesi durumunda okunabilirlik asndan, otomatik tr dnm yerine, tr dntrme ileci ile bilinli bir dnm yaplmaldr. int i; double d; /***/ i = d;

Tr Dntrme leci

134/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

double trden olan d deikeninin deerinin int trden i deikenine atanmas gvenilir bir davran gstermez. Atama sonunda, en iyi olaslkla i deikenine d nin deerinin yalnzca tam say ksm atanr. Byle bir kodu okuyanlar bu atamann yanllkla yapld izlenimini edinirler. Derleyicilerin ou da olas bilgi kaybn uyar iletisiyle bildirir. Bu atamann bilinli bir ekilde yaplmas durumunda tr dntrme ileci kullanlmaldr: int i; double d; /***/ i = (int)d; Aadaki programda, klavyeden girilen bir gerek say tam sayya yuvarlanyor. Girilen deerin ondalk ksm .5'ten daha bykse say yukarya, .5'ten daha kkse say aaya yuvarlanyor: #include <stdio.h> int main() { double d; int x; printf("bir gercek sayi girin : "); scanf("%lf", &d); if (d > 0) x = d + .5; else x = d - .5; printf("x = %d\n", x); } return 0;

135/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DNG DEYMLER
Bir program parasnn yinelemeli olarak altrlmasn salayan kontrol deyimlerine "dng deyimi" (loop statement) denir. C dilinde 3 ayr dng deyimi vardr: while dng deyimi do while dng deyimi for dng deyimi Bunlardan en fazla kullanlan, for dng deyimidir. for dng deyimi, yalnzca C dilinin deil, tm programlama dillerinin en gl dng yapsdr. while ya da do while dng deyimleri olmasa da, bu dngler kullanlarak yazlan kodlar, for dngsyle yazlabilir. Ancak okunabilirlik asndan while ve do while dnglerinin tercih edildii durumlar vardr.

while Dng Deyimi


while (ifade) deyim;

while dng deyiminin genel szdizimi aadaki gibidir:

while anahtar szcn izleyen ayra iindeki ifadeye kontrol ifadesi (control expression) denir. while ayracn izleyen ilk deyime dng gvdesi (loop body) denir. Dng gvdesini bir basit deyim, bo deyim, bileik deyim ya da bir kontrol deyimi oluturabilir. while dng deyiminin yrtlmesi syle olur: nce kontrol ifadesinin saysal deeri hesaplanr. Bu ifade mantksal olarak deerlendirilir. fade 0 deerine sahipse, yanl olarak yorumlanr. Bu durumda dng gvdesindeki deyim yrtlmez, programn ak dng deyimini izleyen ilk deyimle srer. Kontrol ifadesi sfrdan farkl bir deere sahipse, doru olarak yorumlanr bu durumda dng gvdesindeki deyim yrtlr. Dng gvdesindeki deyimin yrtlmesinden sonra kontrol ifadesinin deeri yeniden hesaplanr. Kontrol ifadesi sfrdan farkl bir deere sahip olduu srece dng gvdesindeki deyim yrtlr. Dngden kontrol ifadesinin sfr deerine sahip olmasyla, yani ifadenin yanl olarak yorumlanmasyla klr. C dilinin while dngs, bir koul doru olduu srece bir ya da birden fazla iin yaptrlmasn salayan bir dng deyimidir. Aadaki rnei inceleyelin: #include <stdio.h> int main() { int i = 0; while (i < 100) { printf("%d ", i); ++i; } return 0;

main ilevinde yer alan while dngsn inceleyelim. Dng gvdesini bir bileik deyim oluturuyor. i < 100 ifadesi doru olduu srece bu bileik deyim yrtlr. Yani printf ilevi arlr, daha sonra i deikeninin deeri 1 artrlr. i deikeninin deeri 100 olduunda, kontrol ifadesi yanl olacandan dngden klr. Aadaki rnei inceleyin:

137/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main () { char ch = 'A'; while (ch <= 'Z') putchar(ch++); } return 0;

Yukardaki main ileviyle ngiliz alfabesinin tm byk harf karakterleri srayla ekrana yazdrlyor. ch isimli deikene nce 'A' deeri atandn gryorsunuz. Dng ch <= 'Z' ifadesi doru olduu srece dner. Dng gvdesini bu kez bir basit deyim oluturuyor. Sonek konumundaki ++ ilecinin nesnenin kendi deerini rettiini biliyorsunuz. Ancak ilecin yan etkisi nedeniyle ch deikeninin deeri 1 artrlyor. ch deikeninin deeri 'Z' olduunda kontrol ifadesi halen dorudur. Ancak dngnn bir sonraki turunda kontrol ifadesi yanl olduundan dngden klr. while dng deyiminde dng gvdesindeki deyimin en az bir kez yaplmas gvence altnda deildir. nce kontrol ifadesi ele alndndan, dngye ilk girite kontrol ifadesinin yanl olmas durumunda, dng gvdesindeki deyim hi yrtlmez.

Herhangi bir ifade while dngsnn kontrol ifadesi olabilir. Kontrol ifadesi bir ilev ars ierebilir: while (isupper(ch)) { /***/ } Yukardaki while dngs, isupper ilevi sfr d bir deere geri dnd srece, yani ch byk harf karakteri olduu srece dner. Aadaki while dngs ise, isupper ilevi 0 deerine geri dnd srece, yani ch byk harf karakteri olmad srece dner. while (!isupper(ch)) { /***/ } while dng deyiminin kontrol ifadesinde virgl ileci de kullanlabilir. Aadaki rnei inceleyin: #include <stdio.h> #include <ctype.h> #include <conio.h> int main() { char ch; while (ch = getch(), toupper(ch) != 'Q') putchar(ch); } return 0;

Kontrol fadeleri

138/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Virgl ilecinin sol terimi olan ifadenin daha nce yaplmasnn gvence altnda olduunu biliyorsunuz. Yukardaki main ilevinde yer alan while dngsnn kontrol ifadesine bakalm: while (ch = getch(), toupper(ch) != 'Q') nce virgl ilecinin sol terimi olan ifade yaplacana gre, getch ilevi arlr. Klavyeden alnan karakterin kod numaras ch deikenine atanr. Daha sonra toupper ilevinin arlmasyla, ch deikeni deerinin'Q' karakteri olup olmad snanr. Virgl ilecinin rettii deerin, sa teriminin deeri olduunu anmsayn. Bu durumda dngnn srdrlmesi hakknda sz sahibi olan ifade toupper(ch) != 'Q' ifadesidir. Yani dng ch 'Q' veya 'q' dnda byk harf karakteri olduu srece dner. Kontrol ifadesini bir deiken de oluturabilir: while (x) { /***/ } Yukardaki dng, x deikeni sfrdan farkl bir deere sahip olduu srece dner. Kontrol ifadesi bir deimez de olabilir: while (1) { /***/ } Yukardaki while deyiminde kontrol ifadesi olarak 1 deimezi kullanlyor. 1 sfr d bir deer olduundan, yani kontrol ifadesi bir deikene bal olarak deimediinden, byle bir dngden koul ifadesinin yanl olmasyla klamaz. Bu tr dnglere sonsuz dng (infinite loops) denir. Sonsuz dngler programcnn bir hatas sonucu oluabildii gibi, bilinli olarak, yani belirli bir amac gerekletirmek iin de oluturulabilir. while ayrac iine 1 deimez deerinin olduu while dngs, bilinli olarak oluturulmu bir sonsuz dng deyimidir. Atama ilecinin kontrol ifadesi iinde kullanlmas da, sk rastlanan bir durumdur: while ((val = get_value()) > 0) { foo(val); /***/ } Yukardaki while dngsnde get_value ilevinin geri dn deeri, val isimli deikene atanyor. Atama ileci ile oluturulan ifade ncelik ayrac iine alndn gryorsunuz. Atama ilecinin rettii deer, nesneye atanan deer olduundan, byktr ilecinin sol terimi yine get_value ilevinin geri dn deeridir. Bu durumda dng get_value ilevinin geri dn deeri, 0'dan byk olduu srece dner. Dng gvdesi iinde arlan foo ilevine val deerinin argman olarak gnderildiini gryorsunuz. foo ilevi, get_value ilevinin geri dn deeri ile arlm olur.

break Deyimi
break;

break anahtar szcn dorudan sonlandrc atom izler:

139/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu biimde oluturulan deyime "break deyimi" (break statement) denir. break deyimi bir dng deyiminin ya da switch deyiminin gvdesinde kullanlabilir. Bir dng deyiminin yrtlmesi srasnda break deyimi ile karlaldnda, dngden klr, programn ak dng gvdesi dndaki ilk deyim ile srer. Yani koulsuz olarak dngden klr. Aadaki program inceleyin: #include <stdio.h> #include <math.h> int main () { int val; while (1) { printf("bir sayi girin : "); scanf("%d", &val); if (val < 0) break; printf("karekok %d = %lf\n", val, sqrt(val)); } printf("donguden kld program sonlanyor!\n"); return 0;

}
Programda bilinli olarak bir sonsuz dng oluturuluyor. Dngnn her turunda val isimli deikene klavyeden bir deer alnyor. Eer klavyeden 0'dan kk bir deer girilirse, break deyimi ile dngden klyor. break deyimi yalnzca bir dng deyiminin ya da switch deyiminin gvdesinde kullanlabilir. Aadaki kod paras geersizdir: if (x > 100) { if (y < 200) break; /***/ }

continue anahtar szcn de dorudan sonlandrc atom izler: continue; Bu ekilde oluturulan deyime "continue deyimi" (continue statement) denir. Programn ak bir dng deyimi iinde continue deyimine geldiinde, sanki dngnn turu bitmi gibi dngnn bir sonraki turuna geilir. int getval(void); int isprime(void); while (1) { val = getval(); if (val < 0) break; /* deyimler */ if (isprime(val)) continue; /* deyimler */ }

continue Deyimi

140/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki main ilevinde bir sonsuz dng oluturuluyor. Dngnn her turunda getval isimli ilevin geri dn deeri val deikeninde saklanyor. Eer val deikenine atanan deer 0 ise break deyimiyle dngden klyor. Daha sonra yer alan if deyimi ile val deerinin asal olup olmad snanyor. val'e atanan deer asal ise, dngnn kalan ksm yrtlmyor, continue deyimiyle dngnn bir sonraki turuna geiliyor. continue deyimi, zellikle dng iinde uzun if deyimleri olduunda, okunabilirlii artrmak amacyla kullanlr. while (k++ < 100) { ch = getch(); if (!isspace(ch)) { /* deyimler */ } } Yukardaki yazlan while dngs iinde, klavyeden getch ilevi ile ch deikenine bir karakterin kod numaras alnyor. Klavyeden alnan karakter bir boluk karakteri deilse deyimlerin yrtlmesi isteniyor. Yukardaki kod parasnn okunabilirlii, continue deyiminin kullanlmasyla artrlabilir: while (k++ < 100) { ch = getch(); if (isspace(ch)) continue; /* deyimler */ } Baz programclar da continue deyimini dng gvdesinde yer alacak bir bo deyime seenek olarak kullanrlar: while (i++ < 100) continue; continue deyimi yalnzca bir dng deyiminin gvdesinde kullanlabilir. continue deyiminin, dng dnda bir yerde kullanlmas geerli deildir.

Sk Yaplan Hatalar
#include <stdio.h> int main() { int i = 10;

while dng deyiminin gvdesinin yanllkla bo deyim yaplmas sk yaplan bir hatadr:

while (--i > 0); printf("%d\n", i); return 0;

/* burada bir bo deyim var */

Yukardaki dng while ayrac iindeki ifadenin deeri 0 olana kadar dner. printf ars dng deyiminin gvdesinde deildir. while ayracn izleyen sonlandrc atom, dngnn gvdesini oluturan deyim olarak ele alnr. Dngden kldnda ekrana 0 deeri yazlr. Eer bir yanllk sonucu deil de, bilinli olarak while dngsnn gvdesinde bo deyim (null statement) bulunmas isteniyorsa, okunabilirlik asndan bu bo deyim, while ayracndan hemen sonra deil, alt satrda bir tab ieriden yazlmaldr.

141/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

while dng deyimiyle ilgili yaplan bir baka tipik hata da, dng gvdesini bloklamay unutmaktr. Yani dng gvdesindeki deyimin bir bileik deyim olmas gerekirken, yanllkla bir yaln deyim kullanlr: #include <stdio.h> int main() { int i = 1; while (i <= 100) printf("%d ", i); i++; return 0;

1'den 100'e kadar olan saylarn, aralarnda birer bolukla ekrana yazdrlmak istendiini dnelim. Yukardaki while deyiminde i++ deyimi, yani dng deikeninin artrlmas dngnn gvdesine ait deildir. Bu durumda i <= 100 ifadesi hep doru olacandan sonsuz dng oluur ve ekrana srekli olarak 1 deeri yazlr. if deyiminde olduu gibi while ayrac iinde de karlatrma ileci olan "==" yerine yanllkla atama ileci "=" kullanlmas, yine sk yaplan hatadr: while (x == 5) { /***/ } gibi bir dng, x deikeninin deeri 5 olduu srece dnerken aadaki deyim, bir sonsuz dng oluturur: while (x = 5) { /***/ } Dngnn her turunda x deikenine 5 deeri atanr. Atama ilecinin rettii deer olan 5, "doru" olarak yorumlanacandan, dng srekli dner.

Kontrol ifadesi iinde sonek konumundaki ++ ya da -- ileci sk kullanlr. Byle bir durumda, nce ifadenin deerine baklarak dngnn srdrlp srdrlmeyecei karar verilir, sonra artrma ya da eksiltme ilecinin yan etkisi kendisini gsterir. Aadaki rnei inceleyin: #include <stdio.h> int main() { int i = 0; while (i++ < 100) ; printf("\n%d\n", i); } return 0;

Kontrol fadesinde Sonek Konumundaki ++ ya da -- lecinin Kullanlmas

/* ekrana 101 deerini basar. */

142/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

n Kez Dnen while Dngs


while (n-- > 0) ya da while (n--)

n bir pozitif tamsay olmak zere, n defa dnen bir while dngs oluturmak iin

kod kalplar kullanlabilir. Aada, bir tamsaynn belirli bir ssn hesaplayan power isimli bir ilev yazlyor. levi inceleyin: int power(int base, int exp) { int result = 1; while (exp--) result *= base; return result; } lev iinde yazlan while dngs, exp deikeninin deeri kadar dner, deil mi? Bu durumda base deikeni, exp kez kendisiyle arplm olur.

Bazen dng gvdesi bilinli bir ekilde bo deyim yaplr. Okunabilirlik asndan bu durumda bo deyimin normal bir deyim gibi tablama kuralna uygun olarak yazlmas tavsiye edilir. Aadaki program inceleyin: #include <stdio.h> #include <ctype.h> #include <conio.h> int main() { int ch; printf("Evet mi Hayir mi? [e] [h] : "); while ((ch = toupper(getch())) != 'E' && ch != 'H') ; if (ch == 'E') printf("evet dediniz!\n"); else printf("hayr dediniz!\n"); } return 0;

Dng Gvdesinin Bo Deyim Olmas

Yukardaki main ilevi iinde yazlan while dngs ile, kullanc klavyeden 'e', 'E', 'h', 'H' harflerinden birini girmeye zorlanyor. Dngy dikkatli bir ekilde inceleyin. Dngnn kontrol ifadesi iinde "mantksal ve" ileci "&&" kullanlyor. "Mantksal ve" ilecinin sol teriminin daha nce yaplmasnn gvence altnda olduunu anmsayn. Standart olmayan getch ilevi ile, klavyeden bir karakter alnyor. Alnan karakterin sra numaras, yani getch ilevinin geri dn deeri, standart toupper ilevine argman olarak gnderiliyor. Bylece eer klavyeden kk harf karakteri girilmise byk harfe dntrlm olur. toupper ilevinin geri dn deeri ch deikenine atanyor. Ayra

143/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

iinde yer alan ifadenin deeri, ch deikenine atanan deerdir. "Mantksal ve" ilecinin sa tarafndaki ifadenin btn ile atama ilecinin rettii deerin de 'E' karakterine eitsizlii sorgulanyor. ch deikenine atanan deer 'E' ise "mantksal ve" ilecinin ikinci ksmna hi baklmaz, kontrol ifadesinin deeri yanl olarak yorumlanr. Bylece dngden klr. ch deikenine atanan deerin 'H' olmas durumunda, && ilecinin sa terimi deerlendirilir yani ch deikeninin deerinin 'H' karakterine eitsizlii sorgulanr. Eer ch 'H' deerine eit ise kontrol ifadesi yine yanl olarak yorumlanr, dngden klr. Bunun dndaki tm durumlarda, kontrol ifadesi doru olarak yorumlanacandan dngnn dnmesi srer. Bir baka deyile, dngden klmas ancak klavyeden 'e', 'E', 'h', 'H' karakterlerinden birinin girilmesi ile mmkn olur.

Aada, bir tamsaynn ka basamakl olduu bilgisiyle geri dnen num_digit isimli bir ilevin tanm yer alyor. Program derleyerek altrn: #include <stdio.h> int num_digit(int val) { int digit_counter = 0; if (val == 0) return 1; while (val != 0) { digit_counter++; val /= 10; } return digit_counter;

while Dng Deyiminin Kullanld rnekler

int main() { int x; printf("bir tamsayi girin :"); scanf("%d", &x); printf("%d sayisi %d basamakl!\n", x, num_digit(x)); return 0; } Basamak saysn hesaplamak iin ok basit bir algoritma kullanlyor. Say, sfr elde edilinceye kadar srekli 10'a blnyor. num_digit ilevinde, nce parametre deikeni olan val in deerinin 0 olup olmad snanyor. Eer val 0 deerine eit ise, 1 deeri ile geri dnlyor. 0 says da 1 basamakldr deil mi? Daha sonra oluturulan while dngs val != 0 kouluyla dner. Yani val deikeninin deeri 0 olunca bu dngden klr. Dngnn her turunda gvde iinde digit_counter deikeninin deeri 1 artrlyor. Daha sonra val /= 10; deyimiyle val deikeni onda birine eitleniyor. Aada bu kez kendisine gnderilen bir tamsaynn basamak deerlerinin toplam ile geri dnen sum_digit isimli bir ilev tanmlanyor: #include <stdio.h> int sum_digit(int val) { int digit_sum = 0;

144/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

while (val) { digit_sum += val % 10; val /= 10; } } return digit_sum;

int main() { int val; printf("bir tamsayi girin :"); scanf("%d", &val); printf("%d sayisinin basamaklari toplami = %d\n", val, sum_digit(val)); } return 0;

sum_digit ilevinde, yine parametre deikeni olan val, bir dng iinde srekli 10'a blnyor, val 0 oluncaya kadar dng gvdesindeki deyimler yrtlyor. Dng gvdesi iinde digit_sum += val % 10; deyimi ile val deikeninin birler basama, deeri digit_sum deikenine katlyor. Bylece dngden kldktan sonra digit_sum deikeni, dardan gnderilen saynn basamaklar deerlerinin toplamn tutuyor. Aada, kendisine gnderilen bir tamsaynn tersine geri dnen get_rev_num isimli bir ilev yazlyor: #include <stdio.h> int get_rev_num(int val) { int rev_number = 0; while (val) { rev_number = rev_number * 10 + val % 10; val /= 10; } return rev_number; } int main() { int val; printf("bir tamsayi girin :"); scanf("%d", &val); printf("%d sayisinin tersi = %d\n", val, get_rev_num(val)); } return 0;

get_rev_num ilevi iinde tanmlanan rev_number deikeni 0 deeriyle balatlyor. lev iinde yer alan while dngsnn, parametre deikeni olan val'in deeri 0 olana kadar dnmesi salanyor. Dngnn her turunda, rev_number deikenine

145/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

rev_number * 10 + val % 10 ifadesinin deeri atanyor. leve gnderilen deerin 1357 olduunu dnelim: rev_number 0 7 75 753 7531 val 1357 135 13 1 0

Dng knda rev_number deikeninin deeri 7531 olur. Aadaki programda, bir tamsayy arpanlarna ayran ve arpanlar kkten bye ekrana yazdran, display_factors isimli bir ilev tanmlanyor: #include <stdio.h> void display_factors(int number) { int k = 2; printf("(%d) -> ", number); while (number != 1) { while (number % k == 0) { printf("%d ", k); number /= k; } ++k; } printf("\n"); } Aadaki programda 3 basamakl saylardan abc == a3 + b3 + c3 eitliini salayanlar ekrana yazdrlyor: #include <stdio.h> int main() { int k = 100; while (k < 1000) { int y = k / 100; int o = k % 100 / 10; int b = k % 10; if (y * y * y + o * o * o + b * b * b == k) printf("%d\n", k); ++k; } return 0;

Aadaki ilevin hangi deeri hesapladn bulmaya aln:

146/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int func(int val) { int sum = 0; while (val) { sum += val % 10; if (sum > 10) sum = 1 + sum % 10; val /= 10; } return sum; }

do while Dng Deyimi


do

do while dng deyiminin genel szdizimi aadaki gibidir: deyim; while (ifade); do while dngsnde kontrol ifadesi sondadr. while ayracndan sonra sonlandrc atom bulunmaldr. Yani buradaki sonlandrc atom, dng deyiminin szdiziminin bir parasdr. do while dngsnn yrtlmesi aadaki gibi olur: do anahtar szcn izleyen deyim dngye girite bir kez yaplr, daha sonra while ayrac iindeki kontrol ifadesine baklr. Kontrol ifadesi doru olduu srece dng gvdesini oluturan deyim yaplr. do while dngsnn while dngsnden fark nedir? while dngsnde dng gvdesindeki deyimin en az bir kez yaplmas gvence altnda deildir. Ancak do while dngsnde kontrol sonda yapld iin gvdedeki deyim en az bir kez yaplr. Aadaki program derleyerek altrn: #include <stdio.h> int main() { int val; do { printf("0 - 100 arasi bir deger girin : "); scanf("%d", &val); } while (val < 0 || val > 100); printf("val = %d\n", val); } return 0;

main ilevinde do while dngs ile kullanc, 0 100 aralnda bir deer girmeye zorlanyor. Eer girilen deer 0'dan kk ya da 100'den bykse, kullancdan yeni bir deer isteniyor. Daha nce while dngs kullanarak yazlan num_digit isimli ilev, bu kez do while dngs ile yazlyor:

147/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int num_digit(int val) { int digit_counter = 0; do { digit_counter++; val /= 10; } while(val != 0); } return digit_counter;

Aada tanmlanan print_ulam ileviyle bir tamsayya ilikin ulam serisi ekrana yazdrlyor: #include <stdio.h> void print_ulam(int val) { printf("%d icin ulam serisi\n", val); do { printf("%d ", val); if (val % 2 == 0) val /= 2; else val = val * 3 + 1; } while(val > 1); printf("%d\n", val); } int main() { int x; printf("bir sayi girin: "); scanf("%d", &x); print_ulam(x); } return 0;

for Dng Deyimi

for dng deyiminin genel szdizimi aadaki gibidir: for (ifade1; ifade2; ifade3) deyim; Derleyici for anahtar szcnden sonra bir ayra almasn ve ayra iinde iki noktal virgl atomu bulunmasn bekler. Bu iki noktal virgl, for ayracn ksma ayrr. Bu ksmda da ifade tanmna uygun ifadeler yer alabilir. for ayrac iinde iki noktal virgl mutlaka bulunmaldr. for ayracnn iinin bo braklmas, ya da for ayrac iinde bir, ya da daha fazla sayda noktal virgln bulunmas geersizdir. for ayracnn kapanmasndan sonra gelen ilk deyim, dng gvdesini (loop body) oluturur. Dng gvdesi, yaln bir deyimden oluabilecei gibi, bileik deyimden de yani blok iine alnm birden fazla deyimden de, oluabilir. Dng gvdesini bir bo deyim ya da bir kontrol deyimi de oluturabilir.

148/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

for ayrac iindeki her ifadenin de ayr ayr ilevi vardr. for ayracnn ikinci ksmn oluturan ifadeye kontrol ifadesi (control expression) denir. Tpk while ayrac iindeki ifade gibi, dngnn srdrlmesi konusunda bu ifade sz sahibidir. Bu ifadenin deeri sfrdan farkl ise, yani "doru" olarak yorumlanrsa, dng srer. Dng gvdesindeki deyim yrtlr. Kontrol ifadesinin deeri 0 ise, yani ifade yanl olarak yorumlanrsa programn ak for dng deyimini izleyen ilk deyimin yrtlmesiyle srer. Programn ak for deyimine gelince, for ayracnn birinci ksmndaki ifade deerlendirilir. Birinci ksmdaki ifade genellikle dng deikenine ilkdeer verme amacyla kullanlr. Ancak phesiz byle bir zorunluluk yoktur. for ayracnn nc ksmndaki ifade, dng gvdesindeki deyim ya da deyimler yrtldkten sonra, kontrol ifadesi yeniden snanmadan nce ele alnr. Bu ksm ounlukla, bir dng deikeninin artrlmas ya da azaltlmas amacyla kullanlr. Aadaki program inceleyin: #include <stdio.h> int main() { int i; for (i = 0; i < 2; ++i) printf("%d ", i); printf("\nson deger = %d\n", i); } return 0;

Programn ak for dng deyimine gelince, nce for ayrac iindeki birinci ifade ele alnr. Yani i deikenine 0 deeri atanr. imdi programn ak for ayracnn ikinci ksmna, yani kontrol ifadesine gelir ve i < 2 koulu sorgulanr. Kontrol ifadesinin deeri sfrdan farkl olduu iin, ifade mantksal olarak doru kabul edilir. Bylece programn ak dng gvdesine geer. Dng gvdesinin bir basit deyim tarafndan oluturulduunu gryorsunuz. Bu deyim yrtlr. Yani ekrana i deikeninin deeri yazlarak imle alt satra geirilir. Programn ak, bu kez for ayracnn nc ksmna gelir ve buradaki ifade ele alnr, yani i deikeninin deeri 1 artrlr, i deikeninin deeri 1 olur. kinci ifade yeniden deerlendirilir ve i < 2 ifadesi doru olduu iin bir kez daha dng gvdesindeki deyim yrtlr. Programn ak yine for ayracnn nc ksmna gelir ve buradaki ifade ele alnr, yani i deikeninin deeri 1 artrlr. i deikeninin deeri 2 olur. Programn ak yine for ayracnn ikinci ksmna gelir. Buradaki kontrol ifadesi yine sorgulanr. i < 2 ifadesi, bu kez yanl olduu iin programn ak, dng gvdesine girmez, dng gvdesini izleyen ilk deyimle srer. Yani ekrana: sondeger = 2 yazlr.

for dngsnde bir dng deikeni kullanlmas gibi bir zorunluluk yoktur. rnein aadaki dng, kurallara tamamen uygundur: for (func1(); func2(); func3()) func4();

Dng Deikenleri

149/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki for dng deyimiyle, dngye girite func1 ilevi arlr. func2 ilevi sfr d bir deere geri dndke dng gvdesindeki deyim yrtlr yani func4 ilevi arlr. Kontrol ifadesine yeniden gelmeden, yani func4 ilevinin arlmasndan sonra bu kez func3 ilevi arlr. Aadaki for dng deyimiyle klavyeden 'x' karakteri girilmedii srece, alnan karakter ekrana yazdrlyor: #include <stdio.h> #include <conio.h> int main() { char ch; for (ch = getch(); ch != 'x' ; ch = getch()) putchar(ch); } return 0;

Dng deikeninin tamsay trlerinden birinden olmas gibi bir zorunluluk yoktur. Dng deikeni, gerek say trlerinden de olabilir: #include <stdio.h> int main() { double i; for (i = 0.1; i < 6.28; i += 0.01) printf("%lf ", i); return 0; } Yukardaki dngde, double trden bir dng deikeni seiliyor. for ayracnn birinci ksmnda dng deikenine 0.1 deeri atanyor. Ayracn nc ksmnda ise dng deikeni 0.01 artrlyor. Dng, i deikeninin deerinin 6.28'den kk olmas kouluyla dnyor.

Virgl ileci ile birletirilmi ifadelerin, soldan saa doru srayla ele alndn anmsayn. for dnglerinin birinci ve nc ksmnda virgl ilecinin kullanlmasna sk rastlanr. Aadaki dng deyimini inceleyin: #include <stdio.h> int main() { int i, k; for (i = 1, k = 3; i * k < 12500; i += 2, k += 3) printf("(%d %d)", i, k); } return 0;

for Ayrac inde Virgl lecinin Kullanlmas

150/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki for deyiminde, for ayracnn birinci ksmnda virgl ileci kullanlarak yazlan ifade ile, i deikenine 1, k deikenine 3 deeri atanyor. Dng, i * k ifadesinin deeri 12500'den kk olduu srece dner. for ayracnn nc ksmnda i deikeninin deeri 2, k deikeninin deeri 3 artrlyor.

for dng deyimi ayracnn birinci ksmnda bir ifade bulunmayabilir. Bu tamamen kurallara uygun bir durumdur. 1'den 100'e olan kadar saylarn ekrana yazdrlmak istendiini dnelim. Dng deikenine ilkdeer verme ilemi, for ayracnn birinci ksmndan, for dngs dna alnabilir: #include <stdio.h> int main() { int i = 0; for (; i < 100; ++i) printf("%d ", i); return 0;

for Ayrac indeki ifadelerin Olmamas

for dng ayracnn nc ksmnda da bir ifade bulunmayabilir. Dng deikeninin artrlmas ya da eksiltilmesi, for ayrac ii yerine, dng gvdesinde gerekletirilebilir: #include <stdio.h> int main() { int i = 0; for (; i < 100;) { printf("%d ", i); i++; } return 0;

Birinci ve nc ifadesi olmayan for dngleri, while dngleriyle tamamen edeerdir. while dngleriyle yazlabilen her kod, bir for dngsyle de yazlabilir. while dngs, birinci ve nc ksm olmayan for dnglerine okunabilirlik asndan daha iyi bir seenek olur. for ayracnn ikinci ifadesi de hi olmayabilir. Bu durumda kontrol ifadesi olmayaca iin dng, bir koula bal olmakszn srekli dner. Yani sonsuz dng oluturulur. Ancak iki adet noktal virgl, yine ayra iinde mutlaka bulunmak zorundadr. Ayn i, bu kez bir sonsuz dngnn bilinli kullanlmasyla yaplyor:

C programclarnn ou bilinli bir ekilde sonsuz dng oluturmak istediklerinde for (;;) kalbn yeler. Bu kalp while (1) kalbna edeerdir. kisi de sonsuz dng belirtir. Sonsuz dng oluturmak iin for (;;) biimi while (1) biimine gre daha ok tercih edilir. #include <stdio.h> int main() { int i = 0;

151/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

for (;;) { if (i == 100) break; printf("%d ", i); i++; } return 0; } imdi de, aadaki dng deyiminin yrtlmesiyle ekrana ne yazdrlacan kestirmeye aln: #include <stdio.h> int main() { double d; for (d = 1.5; d < 3,0; d += 0.1) printf("%lf ", d); return 0; } Ekrana hibir ey yazlmaz! Dngnn kontrol ifadesinin d < 3,0 olduunu gryorsunuz. Gerek say deimezi yazarken '.' yerine yanllkla virgl karakteri kullanlm. Bu durumda virgl ilecinin rettii deer, ikinci terim olan 0 deeridir. Kontrol ifadesi yanl olarak yorumlanr bylece dng gvdesindeki deyim hi yrtlmez.

n Kez Dnen for Dngleri


n 0'dan byk bir tamsay olmak zere, aadaki dnglerden hepsi n kez dner. 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 dngnn gvdesi iinde continue deyiminin kullanlmas ile, gvde iinde geriye kalan deyimlerin atlanarak dngnn bir sonraki turuna geilir. for dngs gvdesi iinde continue deyimi ile karlaldnda, programn ak for ayracnn nc ifadesine gelir ve bu ifade ele alnr.

for Dnglerinde continue Deyiminin Kullanm

Baz uygulamalarda, for dngsnn dng deikeni, bir bayrak grevi de grr. Bir for dngs iinden, belirli bir koul olutuunda klmas gereksin: for (i = 0; i < 100; ++i) if (is_valid(i)) break; Yukardaki dng deyiminin altrlmas sonucunda iki farkl durum sz konusudur. Eer dngnn gvdesinde break deyimi yrtlrse, yani herhangi bir i deeri iin is_valid ilevi sfr d bir deere geri dnerse, dng knda i deikeninin deeri, 100'den

Dng Deikeninin Bayrak Amal Kullanlmas

152/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

kk olur. break deyimi hi yrtlmeden dng tm turlarn tamamlarsa, dng knda i deikeninin deeri 100 olur. Dng knda i deerinin 100 olup olmadnn snanmas ile, dngden nasl kld anlalabilir.

Aadaki programda, iki saynn ortak blenlerinin en by ve ortak katlarnn en kn hesaplayan okek ve obeb isimli ilevler tanmlanyor: #include <stdio.h> 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; return 1; } int okek(int number1, int number2) { 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; return number1 * number2;

for Dng Deyiminin Kullanmna rnekler

int main() { int x, y; int n = 5; while (n--) { printf("iki tamsayi girin : "); scanf("%d%d", &x, &y); printf("obeb = %d\n", obeb(x, y)); printf("okek = %d\n", okek(x, y)); } return 0;

Aada tanmlanan ilev, ortak blenlerin en byn Euclid algoritmasyla buluyor: int obeb(int a, int b) { int temp; while (b) { temp = b; b = a % b; a = temp; } return a; }

153/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki programda, bir tamsay iin faktriyel deerini hesaplayan, fact isimli bir ilev yazlyor. Program, int trnn 4 byte olduu bir sistemde derlenmeli: #include <stdio.h> int fact(int number) { int i; int result = 1; if (number == 0 || number == 1) return 1; for (i = 2; i <= number; ++i) result *= i; } return result;

int main() { int k; for (k = 0; k < 14; ++k) printf("%2d! = %-10d\n", k, fact(k)); } return 0;

Aada e says, fact ilevi kullanlarak bir seri toplamyla bulunuyor: #include <stdio.h> int fact(int); int main() { int k; double e = 0.; for (k = 0; k < 14; ++k) e += 1. / fact(k); printf("e = %lf\n", e); return 0; } Aadaki programda, kendisine gnderilen bir tamsaynn asal olup olmadn snayan isprime isimli bir ilev yazlyor. lev, eer kendisine gnderilen say asal ise sfrdan farkl bir deere, asal deil ise 0 deerine geri dnyor. Snama amacyla yazlan main ilevinde isprime ilevi arlarak, 1000'den kk asal saylar ekrana yazdrlyor:

154/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int isprime(int number) { int k; if (number == 0 || number == 1) return 0; if (number % 2 == 0) return number == 2; if (number % 3 == 0) return number == 3; if (number % 5 == 0) return number == 5; for (k = 7; k * k <= number; k += 2) if (number % k == 0) return 0; return 1; } int main() { int k; int prime_counter = 0; for (k = 0; k < 1000; ++k) if (isprime(k)) { if (prime_counter % 10 == 0 && prime_counter) putchar('\n'); prime_counter++; printf("%3d ", k); } return 0;

Blenlerinin toplamna eit olan tamsaylara, mkemmel tamsay (perfect integer) denir. rnein 6 ve 28 tamsaylar mkemmel tamsaylardr. 1 + 2 + 3 = 6 1 + 2 + 4 + 7 + 14 = 28 Aadaki program ile 10000'den kk mkemmel saylar aranyor. Bulunan saylar ekrana yazdrlyor: #include <stdio.h> int is_perfect(int number); int main() { int k; for (k = 2; k < 10000; ++k) if (is_perfect(k)) printf("%d perfect\n", k);

155/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

return 0;

int is_perfect(int number) { int i; int total = 1; for (i = 2; i <= if (number % total += return number == } Aadaki programda klavyeden srekli karakter alnmas salanyor, alnan karakterler ekranda gsteriliyor. Arka arkaya "xyz" karakterleri girildiinde program sonlandrlyor: #include <stdio.h> #include <conio.h> int main() { char ch; int total = 0; while (total < 3) { ch = getch(); putchar(ch); if (ch == 'x' && total == 0) total++; else if (ch == 'y' && total == 1) total++; else if (ch == 'z' && total == 2) total++; else total = 0; } return 0; number / 2; ++i) i == 0) i; total;

e Dngler

Bir dngnn gvdesini baka bir dng deyimi oluturabilir. Byle yaratlan dnglere i ie dngler (nested loops) denir. Aadaki program derleyerek altrn:

#include <stdio.h> int main() { int i, k; for (i = 0; i < 5; ++i) for (k = 0; k < 10; ++k) printf("(%d %d) ", i, k); printf("\n\n(%d %d) ", i, k); return 0; } Dtaki for dngsnn gvdesindeki deyim, bir baka for dngsdr. i < 5 ifadesi doru olduu srece iteki for dng deyimi yrtlr. Son yaplan printf ars ekrana hangi deerleri yazdrr? Dtaki dng i < 5 kouluyla dndne gre dtaki dngden ktktan sonra i deikeninin deeri 5 olur. teki for dng deyiminin son kez 156/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

yaplmasndan sonra da k deikeninin deeri 10 olur. Bu durumda en son printf ars ekrana (5 10) yazdrr. imdi de aada kodu verilen put_star ilevinin ne i yaptn bulmaya aln: #include <stdio.h> void put_stars(int n) { int i, k; for (i = 1; i <= n; ++i) { for (k = 1; k <= i; ++k) putchar('*'); putchar('\n'); } } int main() { int val; printf("bir deger girin : "); scanf("%d", &val); put_stars(val); } return 0;

Aadaki programda abc = a3 + b3 + c3 eitliini salayan basamakl saylar ekrana yazdryor. #include <stdio.h> int main() { int i, j, k; int number = 100; 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\n", number); number++; } } return 0;

ie dnglerde break deyimi kullanmna dikkat etmek gerekir. teki bir dngnn gvdesinde break deyiminin kullanlmas ile, yalnzca iteki dngden klr: Aadaki rnei inceleyin:

e Dnglerde break Deyiminin Kullanlmas

157/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

while (1) { while (1) { if (ifade) break; /***/ } /*i dngden break ile kldnda ak bu noktaya gelir */ } Eer i ie dnglerden yalnzca itekinden deil de dnglerin hepsinden birden kmak istenirse bu durumda goto kontrol deyimi kullanlmaldr. Bu konuyu goto kontrol deyimi blmnde greceksiniz. Burada ikinci while dngs tek bir kontrol deyimi olarak ele alnaca iin bloklamaya gerek yoktur.

Bir dngden nasl klabilir? Bir dngden kmak iin aadaki yollardan biri kullanlabilir. 1. Kontrol ifadesinin yanl olmasyla: Dng deyimlerinin, kontrol ifadelerinin doru olduu srece dndn biliyorsunuz. 2. return deyimi ile: Bir ilev iinde yer alan return deyimi ilevi sonlandrdna gre, bir dng deyimi iinde return deyimi ile karlaldnda dngden klr. 3. break deyimi ile: break deyiminin kullanlmas ile, programn ak dng deyimini izleyen ilk deyimle srer. 4. goto deyimi ile: goto deyimi ile bir programn ak ayn ilev iinde dngnn dnda bir baka noktaya ynlendirilebilir. Bylece dngden klabilir. 5. Program sonlandran bir ilev ars ile: Standart exit ya da abort ilevleri ile programn kendisi sonlandrlabilir. Bir dngden kmak amacyla, kontrol ifadesinin yanl olmasn salamak iin dng deikenine doal olmayacak bir biimde deer atanmas, programn okunabilirliini bozar. Byle kodlardan kanmak gerekir.

Dnglerden k

158/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

KOUL LEC
Koul ileci (conditional operator / ternary operator), C dilinin terimli tek ilecidir. Herhangi bir ifade koul ilecinin terimlerinden biri olabilir. Koul ilecinin genel szdizimi aadaki gibidir: ifade1 ? ifade2 : ifade3 Koul ileci, yukardaki biimden de grld gibi, birbirinden ayrlm iki atomdan oluur. ? ve : atomlar, ilecin terimini birbirinden ayrr. Derleyici, bir koul ileci ile karlatn, ? atomundan anlar. ? atomunun solundaki ifadenin (ifade1) saysal deeri hesaplanr. Bu ifade mantksal olarak yorumlanr. Eer ifade1'in 0'dan farkl ise, bu durumda yalnzca ifade2' nin saysal deeri hesaplanr. ifade1'in deeri 0 ise, bu kez yalnzca ifade3'n saysal deeri hesaplanr. Dier ilelerde olduu gibi koul ileci de bir deer retir. Koul ilecinin rettii deer ifade1 doru ise (0 d bir deer ise) ifade2'nin deeri, ifade1 yanl ise ifade3'n deeridir. rnek: m = x > 3 ? y + 5 : y 5; Burada nce x > 3 ifadesinin saysal deeri hesaplanr. Bu ifadenin deeri 0'dan farkl ise yani doru ise, koul ileci y + 5 deerini retir. x > 3 ifadesinin deeri 0 ise yani ifade yanl ise, koul ileci y 5 deerini retir. Bu durumda m deikenine x > 3 ifadesinin doru ya da yanl olmasna gre y + 5 ya da y 5 deeri atanr. Ayn ilem if deyimi ile de yaplabilir : if (x > 3) m = y + 5; else m = y 5; Koul ileci, ile ncelik tablosunun 13. ncelik seviyesindedir. Bu seviye atama ilecinin hemen stdr. Aadaki ifadeyi ele alalm: x > 3 ? y + 5 : y 5 = m Koul ilecinin ncelii atama ilecinden daha yksek olduu iin, nce koul ileci ele alnr. x > 3 ifadesinin doru olduunu ve ilecin y + 5 deerini rettiini dnelim. y + 5 = m Koul ilecinin rettii deer sol taraf deeri olmadndan, yukardaki ifade geersizdir. Normal olarak koul ilecinin ilk terimini ayra iine almak gerekmez. Ancak bu terimin, okunabilirlik asndan genellikle ayra iine alnmas tercih edilir. (x >= y + 3) ? a * a : b Koul ilecinin nc terimi konusunda dikkatli olmak gerekir. rnein: m = a > b ? 20 : 50 + 5 a > b ifadesinin doru olup olmamasna gre koul ileci, 20 ya da 55 deerini retir ve son olarak da m deikenine koul ilecinin rettii deer atanr. Ancak m deikenine a > b ? 20 : 50

159/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ifadesinin deerinin 5 fazlas atanmak isteniyorsa bu durumda ifade aadaki gibi dzenlenmelidir: m = (a > b ? 20 : 50) + 5; Koul ilecinin terimi de bir ilev ar ifadesi olabilir, ama arlan ilevlerin, geri dn deeri reten ilevler olmas gerekir. teriminden birinin geri dn deeri void olan bir ileve ilikin ilev ar ifadesi olmas, geersiz bir durum oluturabilir. Aadaki kod parasn inceleyin: #include <stdio.h> int func1(void); int func2(void); int func3(void); int main() { int m; m = func1() ? func2() : func3(); } return 0;

Yukarda koul ilecinin kullanld ifadede m deikenine, func1 ilevinin geri dn deerinin sfr d bir deer olmas durumunda func2 ilevinin geri dn deeri, aksi halde func3 ilevinin geri dn deeri atanr. Koul ilecinin rettii, bir nesne deil bir deerdir. Koul ilecinin rettii deer nesne gstermedii iin bu deere bir atama yaplamaz. Aadaki if deyimini inceleyin: if (x > y) a = 5; else b = 5; Yukardaki if deyiminde x > y ifadesinin doru olmas durumunda a deikenine, yanl olmas durumunda ise b deikenine 5 deeri atanyor. Ayn i koul ilecinin kullanlmasyla yaptrlrsa: (x > y) ? a : b = 5; /* Geersiz! */

Bu durum derleme zaman hatasna yol aar. nk koul ilecinin rettii a ya da b deikenlerinin deeridir, nesnenin kendisi deildir. Byle bir atama sol tarafn nesne gsteren bir ifade olmamas nedeniyle derleme zamannda hata oluturur. Ayn nedenden dolay aadaki ifade de geersizdir: (x > 5 ? y : z)++; /* Geersiz! */

Ayra iindeki ifade deerlendirildiinde elde edilen, y ya da z nesneleri deil, bunlarn deerleridir. Yani sonek konumundaki ++ ilecinin terimi nesne deildir. [C++ dilinde koul ilecinin 2. ya da 3. teriminin nesne olmas durumunda ilecin rettii deer sol taraf deeridir. Yani yukardaki deyimler C de geersiz iken C++'da geerlidir.]

Koul lecinin Kullanld Durumlar

160/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if deyiminin yerine koul ileci kullanmak her zaman doru deildir. Koul ilecinin kullanlmasnn salk verildii tipik durumlar vardr. Bu durumlarda genel fikir, koul ilecinin rettii deerden ayn ifade iinde faydalanmak, bu deeri bir yere aktarmaktr: 1. Koul ilecinin rettii deer bir nesneye atanabilir. p = (x == 5) ? 10 : 20; m = (a >= b + 5) ? a + b : a b; Yukardaki deyimlerin iini grecek if deyimleri de yazlabilirdi: if (x == 5) p = 10; else p = 20; if (a >= b + 5) m = a + b; else m = a - b; 2. Bir ilev, koul ilecinin rettii deer ile geri dnebilir: return x > y ? 10 : 20; Bu rnekte x > y ifadesinin doru olup olmamasna gre ilev, 10 ya da 20 deerine geri dner. Yukardaki ifade yerine aadaki if deyimi de kullanlabilirdi : if (x > y) return 10; return 20; 3. Koul ilecinin rettii deer ile bir ilev arlabilir: func(a == b ? x : y); Yukardaki deyimde, a, b'ye eit ise func ilevi x deeri ile, a, b'ye eit deil ise y deeri ile arlr. Ayn ii gren bir if deyimi de yazlabilirdi: if (a == b) func(x); else func(y); 4. Koul ilecinin rettii deer, bir kontrol deyiminin kontrol ifadesinin bir paras olarak da kullanlabilir: if (y == (x > 5 ? 10 : 20)) func(); Yukardaki deyimde x > 5 ifadesinin doru olup olmamasna gre, if ayrac iinde, y deikeninin 10 ya da 20 deerine eitlii sorgulanr. Yukardaki durumlarda, koul ilecinin if deyimine tercih edilmesi iyi tekniktir. Bu durumlarda koul ileci daha okunabilir bir yap oluturur. Koul ilecinin bilinsizce kullanlmamas gerekir. Eer koul ilecinin rettii deerden dorudan faydalanlmayacaksa koul ileci yerine if kontrol deyimi tercih edilmelidir. rnein:

161/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

x > y ? a++ : b++; Deyiminde koul ilecinin rettii deerden faydalanlmyor. Burada aadaki if deyimi tercih edilmelidir: if (x > y) a++; else b++; Baka bir rnek: x == y ? printf("eit\n") : printf("eit deil\n"); Bu rnekte, printf ilevinin bir geri dn deeri retmesinden faydalanlarak koul ileci kullanlm. Koul ileci, x == y ifadesinin doru olup olmamasna gre, ikinci veya nc ifade olan printf ilevi arlarndan birinin geri dn deerini retir. Bu da aslnda ekrana yazlan karakter saysdr. Ama ifade iinde, koul ilecinin rettii deerin kullanlmas sz konusu deildir. Burada da if deyimi tercih edilmelidir: if (x == y) printf("eit\n"); else printf("eit deil\n"); Koul ilecinin ikinci ve nc terimlerinin trleri farkl ise, dier ilelerde olduu gibi tr dntrme kurallar devreye girer: int i; double d; m = (x == y) ? i : d; Bu rnekte i deikeni int trden, d deikeni ise double trdendir. x == y karlatrma ifadesi doru ise, koul ilecinin rettii deerin tr double trdr. Baz durumlarda, if deyiminin de, koul ilecinin de, kullanlmas gerekmez: if (x > 5) m = 1; else m = 0; Yukardaki if deyimi yerine aadaki deyim yazlabilirdi: m = (x > 5) ? 1 : 0; Koul ilecinin retecei deerlerin yalnzca 1 veya 0 olabilecei durumlarda, dorudan karlatrma ileci kullanmak daha iyi teknik olarak deerlendirilmelidir: m = x > 5; Baka bir rnek : return x == y ? 1 : 0; yerine

162/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

return x == y; yazlabilirdi. Koul ilecinin ncelik yn sadan soladr. Bir ifade iinde birden fazla koul ileci varsa, nce en sadaki deerlendirilir. Aadaki kod parasn inceleyin: #include <stdio.h> int main() { int x = 1, y = 1,

m;

m = x < 5 ? y == 0 ? 4 : 6 : 8; printf("m = %d\n", m); } return 0;

Yukardaki main ilevinde printf ilevi ars ile m deikeninin deeri olarak ekrana 6 yazlr. fade aadaki gibi ele alnr: m = x < 5 ? (y == 0 ? 4 : 6) : 8;

Koul lecinin Kullanmna rnekler


int max2(int a, int b) { return a > b ? a : b; }

Aada iki saydan byk olanna geri dnen, max2 isimli ilev tanmlanyor.

Dng gvdesinde koul ilecinin kullanmn inceleyin: #include <stdio.h> int main() { double sum = 0.; int k; for (k = 0; k < 10000; ++k) sum += (k % 2 ? -1. : 1.) / (2 * k + 1); printf("pi = %lf\n", 4. * sum); return 0; }

1 1 1 1 pi 1 + + + ... serisi 3 5 7 9 4

e yaknsar. Aada bir dng ile, pi says hesaplanyor.

163/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

NLEMC KOMUTLARI (1)


C derleyicileri iki ayr modlden oluur: 1. nilemci Modl 2. Derleme Modl nilemcinin, bilgisayarn ilemcisi ya da baka bir donanmsal elemanyla hibir ilgisi yoktur. nilemci, belirli bir i gren bir yazlm programdr. nilemci, kaynak dosya zerinde birtakm dzenlemeler ve deiiklikler yapan bir n programdr. nilemci programnn bir girdisi bir de kts vardr. nilemcinin girdisi kaynak dosyann kendisidir. nilemci programn kts ise derleme modlnn girdisini oluturur. Yani kaynak program ilk aamada nilemci tarafndan ele alnr. nilemci modl, kaynak dosyada eitli metinsel dzenlemeler, deiiklikler yapar. Daha sonra deitirilmi ya da dzenlenmi olan bu kaynak dosya, derleme modl tarafndan ama koda dntrlr.

C programlama dilinde # ile balayan btn satrlar, nilemci programa verilen komutlardr (directives). nilemci program, nceden belirlenmi bir komut kmesindeki ilemleri yapabilir. Her bir komut, # atomunu izleyen bir szckle belirlenir. Aada tm nilemci komutlarnn listesi veriliyor: #include #define #if #else #elif #ifdef #ifndef #endif #undef #line #error #pragma nilemci komutlarn belirleyen yukardaki szckler, C dilinin anahtar szckleri deildir. Sra derleyiciye geldiinde bunlar, nilemci tarafndan kaynak dosyadan silinmi olur. rnein, istenirse include isimli bir deiken tanmlanabilir, ama bunun okunabilirlik asndan iyi bir fikir olmad sylenebilir. nilemci komutlarn belirten szckler, ancak # karakterini izledikleri zaman zel anlam kazanr. nilemci program, ama kod oluturmaya ynelik hibir i yapmaz, kaynak kod iinde baz metinsel dzenlemeler yapar. Kendisine verilen komutlar yerine getirdikten sonra, # ile balayan satrlar kaynak dosyadan siler. Derleme modlne girecek programda # ile balayan satrlar artk yer almaz. imdilik nilemci komutlarndan yalnzca #include ve #define komutlarn greceksiniz. Geriye kalan nilemci komutlar ileride ayrntl olarak ele alnacak.

165/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include nilemci Komutu


#include <dosya ismi> ya da #include "dosya ismi"

Bu nilemci komutunun genel szdizimi aadaki gibidir:

#include komutu ile, ismi verilen dosyann ierii, bu komutun yazld yere yaptrlr. Bu komut ile nilemci, belirtilen dosyay diskten okuyarak komutun yazl olduu yere yerletirir. Bu komutla yaplan i, metin dzenleyici programlardaki "kopyala - yaptr" (copy paste) ilemine benzetilebilir. #include nilemci komutuyla, kaynak dosyaya eklenmek istenen dosyann ismi iki ayr biimde belirtilebilir: 1. Asal ayra iinde: #include <stdio.h> #include <time.h> 2. ift trnak iinde #include "general.h" #include "genetic.h" Dosya ismi eer asal ayra iinde verilmise, szkonusu dosya nilemci tarafndan, yalnzca nceden belirlenmi bir dizin iinde aranr. allan derleyiciye ve sistemin kurulumuna bal olarak, nceden belirlenmi bu dizin farkl olabilir. rnein: \tc\include \borland\include \c600\include gibi. Benzer biimde UNIX sistemleri iin bu dizin, rnein: /usr/include biiminde olabilir. Standart balk dosyalar, asal ayra iinde kaynak koda eklenir. Sistemlerin ounda dosya ismi iki trnak iine yazldnda, nilemci ilgili dosyay nce allan dizinde (current directory) arar. Burada bulamazsa sistem ile belirlenen dizinde arar. rnein: C:\sample dizininde alyor olalm. #include "strfunc.h" komutu ile, nilemci strfunc.h isimli dosyay nce C:\sample dizininde arar. Eer burada bulamazsa sistem tarafndan belirlenen dizinde arar. Programclarn kendilerinin oluturduklar balk dosyalar, genellikle sisteme ait dizinde olmadklar iin, ift trnak iinde kaynak koda eklenir. #nclude nilemci komutu ile kaynak koda eklenmek istenen dosya ismi, dosya yolu (path) da ierebilir:

166/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <sys\stat.h> #include "c:\headers\myheader.h" #include nilemci komutu kaynak programn herhangi bir yerinde bulunabilir. Fakat standart balk dosyalar gibi, iinde eitli bildirimlerin bulunduu dosyalar iin en iyi yer, kukusuz programn en tepesidir. #include komutu, i ie gemi (nested) bir biimde de bulunabilir. rnein ok sayda dosyay kaynak koda eklemek etmek iin yle bir yntem izlenebilir. ana.c #include "project.h" int main() { /****/ } project.h #include #include #include #include <stdio.h> <conio.h> <stdlib.h> <time.h>

ana.c dosyas iine yalnzca project.h dosyas ekleniyor. nilemci bu dosyay kaynak koda ekledikten sonra yoluna bu dosyadan devam eder.

zellikle byk programlar, modl ismi verilen ayr ayr paralar halinde yazlr. Bu modllerden bazlarnn amac, dier modllere hizmet vermektir. C ve C++ dillerinde, genel hizmet verecek kodlar (server codes), genel olarak iki ayr dosya halinde yazlr. lev tanmlamalar, global deiken tanmlamalar uzants .c olan dosyada yer alr. Bu dosyaya, kodlama dosyas (implementation file) denir. Hizmet alacak kodlar (client codes) ilgilendiren bildirimler ise bir baka dosyada tutulur. Bu dosyaya, balk dosyas (header file) denir. Bir balk dosyas, bir modln arayzdr (interface). Modl daryla olan ilikisini arayz ile kurar. Verilen hizmetlerden faydalanacak kullanc kodlar, hizmet veren kodlarn kendisini deil, yalnzca arayzn grr. Hizmet alan kodlar, hizmet veren kodlarn arayzlerine bal olarak yazlr. Bylece hizmet veren kodlarn kendisi ile arayzleri, birbirinden net olarak ayrlm olur. Hizmet veren kodlarn arayzleriyle tanmlarn birbirinden ayrmann ne gibi faydalar olabilir? Kullanc kodlar, yani hizmet alan kodlar, hizmet veren ilevlerin tanmlarna gre deil de, arayzlerine bal olarak yazlr. Bundan aadaki faydalar salanabilir: 1. Hizmet veren kodlar yazanlar, ayn arayze bal kalmak kaydyla, tanm kodlarnda deiiklik yapabilir. Bu durumda hizmet alan kodlarda bir deiiklik yaplmas gerekmez. 2. Kullanc kodlar yazacak programc, hizmet veren kodlara ilikin uygulama ayrntlarn bilmek zorunda kalmadndan, daha kolay soyutlama yapar. 3. Birden fazla programcnn ayn projede almas durumunda, proje gelitirme sresi ksaltlm olur.

Balk Dosyalar Neden Kullanlr?

167/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#define nilemci komutunun ilevi, metin dzenleyici programlardaki "bul - deitir" (find - replace) zelliine benzetilebilir. Bu komut kaynak kod iindeki bir yazy baka bir yaz ile deitirmek iin kullanlr. nilemci, define szcnden sonraki boluklar atarak, boluksuz ilk yaz kmesini elde eder. Bu yazya STR1 diyelim. Daha sonra satr sonuna kadar olan tm yaz kmesi elde edilir. Buna da STR2 diyelim. nilemci, kaynak kod iinde STR1 yazs yerine STR2 yazsn yerletirir: #define SIZE 100

#define nilemci Komutu

nilemci komutuyla, nilemci kaynak kod iinde grd her bir SIZE atomu yerine 100 atomunu yerletirir. Derleme modlne girecek kaynak programda, SIZE atomu artk yer almaz. #define nilemci komutu kullanlarak ounlukla bir isim, saysal bir deerle yer deitirilir. Saysal bir deerle deitirilen isme, "simgesel deimez" (symbolic constant) denir. Simgesel deimezler nesne deildir. Derleme modlne giren kaynak kodda, simgesel deimezlerin yerini saysal ifadeler alm olur. #define nilemci komutuyla tanmlanan isimlere, "basit makro" (simple macro) da denir. Simgesel deimezler, geleneksel olarak byk harf ile isimlendirilir. Bylece kodu okuyann deikenlerle, simgesel deimezleri ayrt edebilmesi salanr. Bilindii gibi C dilinde, deiken isimlendirmelerinde arlkl olarak kk harfler kullanlr. Bir simgesel deimez, baka bir simgesel deimezin tanmlamasnda kullanlabilir. rnein: #define MAX #define MIN 100 (MAX - 50)

Yer deitirme ilemi, STR1'in kaynak kod iinde bir atom halinde bulunmas durumunda yaplr: #define SIZE 100

Bu tanmlamadan sonra kaynak kodda size = MAX_SIZE; printf("SIZE = %d\n", size); gibi deyimlerin bulunduunu dnelim. nilemci bu deyimlerin hibirinde bir deiiklik yapmaz. size = MAX_SIZE ifadesinde SIZE ayr bir atom deildir. Atom olan MAX_SIZE'dr. Yer deitirme ilemi byk kk harf duyarl ile yaplacandan, kaynak kod iinde yer alan size ismi de deitirilecek atom deildir. printf("SIZE = %d\n", max_size) ifadesinde atom olan dizge ifadesidir. Yani dizge iindeki SIZE, tek bana ayr bir atom deildir. #define nilemci komutu ile deimezlere ve ilelere ilikin yer deitirme ilemi yaplamaz.

169/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki #define nilemci komutlar geerli deildir: #define + #define 100 200

Simgesel deimezler, C dilinin deiken isimlendirme kurallarna uygun olarak isimlendirilmelidir: #define BYK 10

tanmlamas geersizdir. nilemci program, #include komutu ile kaynak koda eklenen dosyann iindeki nilemci komutlarn da altrr. Bu durumda iinde simgesel deimez tanmlamalar yaplm bir dosya, #include komutu ile kaynak koda eklendiinde, bu simgesel deimezler de kaynak kod iinde tanmlanm gibi geerli olur. #define nilemci komutunda dizgeler de kullanlabilir: #define HATA_MESAJI /***/ printf(HATA_MESAJI); /***/ "DOSYA AILAMIYOR \n"

Simgesel deimez tanmnda kullanlacak dizge uzunsa, kodun okunmasn kolaylatrmak iin, birden fazla satra yerletirilebilir. Bu durumda, son satr dndaki satrlarn sonuna "\" atomu yerletirilmelidir. Okunabilirlik asndan, tm simgesel deimez tanmlamalar alt alta gelecek biimde yazlmaldr. Seilen simgesel deimez isimleri, kodu okuyan kiiye bunlarn ne amala kullanld hakknda fikir vermelidir. Bir simgesel deimezin tanmlanm olmas, kaynak kod iinde deitirilebilecek bir bilginin olmasn zorunlu hale getirmez. Tanmlanm bir simgesel deimezin kaynak kod iinde kullanlmamas, herhangi bir hataya yol amaz.

Simgesel deimezler, yazlan kodun okunabilirliini ve alglanabilirliini artrr. Baz deimezlere isimlerin verilmesi, bu deimezlerin ne amala kullanld hakknda daha fazla bilgi verilebilir. Aadaki rnee bakalm: #define PERSONEL_SAYISI 750

Simgesel Deimezler Kodu Daha Okunabilir Klar

void foo() { /***/ if (x == PERSONEL_SAYISI) /***/ } Kaynak kod iinde PERSONEL_SAYISI simgesel deimezi yerine dorudan 750 deeri kullanlm olsayd, kodu okuyann, bu deimezin ne anlama geldiini karmas ok daha zor olurdu, deil mi?

Simgesel Deimezlerle Trlere sim Verilmesi

#define nilemci komutuyla C'nin doal veri trlerine de isimler verilebilir:

170/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#define BYTE #define BOOL

char int

BYTE foo(BYTE b); BOOL isprime(int val); char trnn aslnda 1 byte' lk bir tamsay tr olduunu biliyorsunuz. char isminin kullanlmas ounlukla yazlarla ya da karakterle ilgili bir i yapld izlenimini verir. Oysa bellek bloklar zerinde genel ilemler yapan ilevler de ounlukla char trn kullanr. Bu durumda yaplan ile ilgili daha fazla bir fikir vermek iin, rnein BYTE ismi kullanlabilir. C'de BOOL trnn olmadn hatrlyorsunuz. C'de bool veri tr yerine mantksal bir veri tr olarak int tr kullanlr. Ancak programn okunabilirliini artrmak iin #define nilemci komutuyla BOOL ismi kullanlabilir. Bu kullanma seenek olan typedef anahtar szcn ve yeni tr ismi tanmlamalarn ileride ele alacaz.

Okunabilirlii artrmaya ynelik bir baka kullanm da ilevlerin geri dn deerlerine yneliktir. Baz ilevlerin geri dn deerlerinin bir soruya yant verdiini, baz ilevlerin geri dn deerlerinin de bir ilemin baars hakknda fikir verdiini biliyorsunuz. Byle ilevler, geri dn deeri ifadeleri yerine simgesel deimezler kullanrlarsa okunabilirlik asndan daha iyi olabilir: return return return return return gibi. VALID; INVALID; TRUE; FALSE; FAILED;

levlerin Simgesel Deimezlerle Geri Dnmesi

Baz ilevlere de, aran kod paras tarafndan simgesel deimezler gnderilir. C'nin standart balk dosyalarnda da bu amala baz simgesel deimezler tanmlanmtr. rnein stdlib.h balk dosyas iinde #define EXIT_FAILURE #define EXIT_SUCCESS 1 0

levlerin Simgesel Deimezlerle arlmas

biiminde tanmlamalar vardr. Yani stdlib.h balk dosyas kaynak koda eklenirse EXIT_FAILURE simgesel deimezi 1, EXIT_FAILURE simgesel deimezi, 0 yerine kullanlabilir. Bu simgesel deimezler, standart exit ilevine yaplan arlarda kullanlr: exit(EXIT_FAILURE); stdio.h balk dosyas iinde standart fseek ilevine argman olarak gnderilmesi amacyla simgesel deimez tanmlanmtr: #define SEEK_SET #define SEEK_CUR #define SEEK_END 0 1 2

C programlarnda, bayrak deikenleri de ounlukla simgesel deimezlerle deerlerini alr:

Bayrak Deikenlerin Simgesel Deimezlerle Deerini Almas

171/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

pos_flag = ON; validiy_flag = INVALID; switch kontrol deyimindeki case ifadeleri de ounlukla simgesel deimezlerle oluturulur. Bu konuyu switch kontrol deyiminde inceleyeceiz.

Bir deimezin program iinde pek ok yerde kullanld durumlarda, bu deimeze ynelik bir deitirme ilemi tek yerden yaplabilir. Bylece sz konusu program, bir simgesel deimeze bal olarak yazlp daha sonra simgesel deimezin deitirilmesiyle farkl parametrik deerler iin yeniden derlenerek altrlabilir. rnein kullancnn belirli sayda tahmin yaparak bir sayy bulmasna dayanan bir oyun programn yazdmz dnelim. Programda, oyuncu 10 tahmin hakkna sahip olsun. Bu durumda kaynak kodun birok yerinde 10 deeri kullanlm olur, deil mi? Daha sonra oyun programnda oyuncunun tahmin saysnn 20'ye karlmak istendiini varsayalm. Kaynak kod iinde oyuncunun tahmin saysn gsteren 10 deimezlerinin deitirilerek 20 yaplmas gerekir. Bu deitirme ileminin programc tarafndan tek tek yaplmas hem zor hem de hataya aktr. Kaynak kodda kullanlm olan her 10 deimezi, oyuncunun tahmin hakkn gstermeyebilir. Oysa oyuncunun hakkn gsteren deer yerine bir simgesel deimez tanmlanp #define NO_OF_GUESS 10

Simgesel Deimezler Yoluyla Programn Deitirlmesi

program bu simgesel deimez kullanlarak yazlrsa, bu simgesel deimez tanmnda yaplacak deiiklikle tm program iinde 10 deerleri kolayca 20 deerine dntrlebilir.

Simgesel deimezlerin kullanm, zellikle gerek say deimezlerin kullanlmasnda olas tutarszlklar, yazm yanllarn engeller. rnein matematiksel hesaplamalar yapan bir programda, pi saysnn sk sk kullanldn dnelim. pi says yerine #define PI 3.14159

Gerek Say Deimezleri Yerine Kullanlan Simgesel Deimezler

simgesel deimezi kullanlabilir. Her defasnda pi says, bir deimez olarak yazlrsa, her defasnda ayn deer yazlamayabilir. rnein kaynak kodun bir yerinde 3.14159 deimezi yazlmken kaynak kodun bir baka noktasnda yanllkla 3.15159 gibi bir deer de yazlabilir. Derleyici programn byle tutarszlklar iin mantksal bir uyar iletmesi olana yoktur. Simgesel deimez kullanm bu tr hatalar ortadan kaldrr. Yine derleyicilerin ou, math.h balk dosyas iinde de pek ok matematiksel deimez tanmlar.

Baz simgesel deimezler hem tanabilirlik salamak hem de ortak arayz oluturmak amacyla tanmlanr. Standart balk dosyalarndan limits.h iinde kullanlan tamsay trlerinin sistemdeki snr deerlerini tayan standart simgesel deimezler tanmlanmtr: Simgesel Deimez CHAR_BIT SCHAR_MN SCHAR_MAX UCHAR_MAX Olabilecek En Kk Deer 8 -127 127 255 Anlam char trndeki bit says signed char trnn en kk deeri signed char trnn en byk deeri unsigned char trnn en byk deeri

Tanabilirlik Amacyla Tanmlanan Simgesel Deimezler

172/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

SHRT_MIN SHRT_MAX USHRT_MAX INT_MIN INT_MAX UINT_MAX LONG_MIN LONG_MAX ULONG_MAX LLONG_MIN LLONG_MAX ULLONG_MAX CHAR_MIN

-32.767 32.767 65535 -32.767 32.767 65.535 -2.147.483.648 -2.147.483.647 4.294.967.295 9.233.372.036.854.775.808 9.233.372.036.854.775.807 18.446.744.073.709.551.615 SCHAR_MIN ya da 0

CHAR_MAX

SCHAR_MAX ya da UCHAR_MAX

MB_LEN_MAX

signed short int trnn en kk deeri signed short int trnn en byk deeri unsigned short trnn en byk deeri signed int trnn en kk deeri signed int trnn en byk deeri unsigned int trnn en byk deeri signed long int trnn en kk deeri signed long int trnn en byk deeri unsigned long int trnn en byk deeri signed long long int trnn en kk deeri (C99) signed long long int trnn en byk deeri (C99) unsigned long long int trnn en byk deeri (C99) char trnn en kk deeri. Sistemdeki char tr iaretliyse bu simgesel deimezin deeri SCHAR_MIN deerine eittir. char tr iaretsiz ise UCHAR_MAX deerine eittir. char trnn en byk deeri. Sistemdeki char tr iaretliyse bu simgesel deimezin deeri SCHAR_MAX deerine eittir. char tr iaretsiz ise UCHAR_MAX deerine eittir. oklu byte karakterinin sahip olabilecei en fazla byte says. (Bu trn desteklendii lokallerde)

#define komutu kaynak kodun herhangi bir yerinde kullanlabilir. Ancak tanmland yerden kaynak kodun sonuna kadar olan blge iinde etki gsterir. nilemci program dorudan bilinirlik alan kavramna sahip deildir. Bir bloun banda tanmlanan bir simgesel deimez yalnzca o bloun iinde deil tanmland yerden kaynak kodun sonuna kadar her yerde etkili olur. Simgesel deimezler bazen balk dosyasnn iinde bazen de kaynak dosyann iinde tanmlanr.

Simgesel Deimezlerin Tanmlanma Yerleri

Simgesel Deimezlerin Kullanlmasnda Sk Yaplan Hatalar


Tipk bir hata, simgesel deimez tanmlamasnda gereksiz yere '=' karakterini kullanmaktr: #define N = 100 Bu durumda nilemci N grd yere = 100 yazsn yaptrr. rnein

173/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int a[N]; gibi bir tanmlama yapldn dnelim. nilemci bu tanmlamay a[= 100]; biimine getirir ki bu da geersizdir. #define nilemci komutu satrn yanllkla ';' atomu ile sonlandrmak bir baka tipik hatadr. #define N 100;

Bu durumda nilemci N grd yere 100; yerletirir. int a[N]; tanmlamas int a[100;]; haline gelir. Bu tanmlama geersizdir. Bu tr hatalarda genellikle derleyici, simgesel deimez ka yerde kullanlmsa o kadar hata iletisi verir. Simgesel deimezlerin tanmlanmasnda 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 60 deil 50 deeri atanr. Ancak nilemci komutu #define MAX (10 + 20)

biiminde yazlsayd , result deikenine 60 deeri atanm olurdu.

Kaynak metnin yazld ISO 646 gibi baz karakter setlerinde '&', '|', '^' karakterleri olmadndan, baz C ilelerinin yazmnda sorun olumaktadr. C89 standartlarna daha sonra yaplan eklemeyle dile katlan iso646 balk dosyasnda, standart baz C ilelerine dntrlen basit makrolar tanmlanmtr. Aada bu makrolarn listesi veriliyor:

Standart C lelerine likin Basit Makrolar

174/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#define #define #define #define #define #define #define #define #define #define #define

and and_eq bitand bitor compl not not_eq or or_eq xor xor_eq

&& &= & | ~ ! != || |= ^ ^=

175/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

switch DEYM
switch deyimi bir tamsay ifadesinin farkl deerleri iin, farkl ilerin yaplmas amacyla kullanlr. switch deyimi, zellikle else if merdivenlerine okunabilirlik ynnden bir seenek oluturur. Deyimin genel biimi aadaki gibidir: switch (ifade) { case ifade1 : case ifade2 : case ifade3 : ....... case ifade_n: default: } switch, case, ve default C dilinin anahtar szckleridir.

switch ayrac iindeki ifadenin saysal deeri hesaplanr. Bu saysal deere eit deerde bir case ifadesi olup olmad yukardan aa doru snanr. Eer byle bir case ifadesi bulunursa programn ak o case ifadesine geirilir. Artk program buradan akarak ilerler. switch ayrac iindeki ifadenin saysal deeri hibir case ifadesine eit deilse, eer varsa, default anahtar szcnn bulunduu ksma geirilir. #include <stdio.h> int main() { int a; printf("bir sayi girin : "); scanf("%d", &a); switch (a) { case 1: printf("bir\n"); case 2: printf("iki\n"); case 3: printf("\n"); case 4: printf("drt\n"); case 5: printf("be\n"); } return 0; } Yukardaki rnekte scanf ilevi ile, klavyeden a deikenine 1 deeri alnm olsun. Bu durumda programn ekran kts u ekilde olur: bir iki drt be Eer uygun case ifadesi bulunduunda yalnzca bu ifadeye ilikin deyim(ler)in yrtlmesi istenirse break deyiminden faydalanlr. break deyiminin kullanlmasyla, dnglerden olduu gibi switch deyiminden de klr. Daha nce verilen rnee break deyimleri ekleniyor:

switch Deyiminin Yrtlmesi

177/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int a; printf("bir sayi girin: "); 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; case 5 : printf("be\n"); } return 0;

Uygulamalarda, switch deyiminde ounlukla her case ifadesi iin bir break deyiminin kullanlr. Tabi byle bir zorunluluk yoktur. case ifadelerini izleyen ":" atomundan sonra istenilen sayda deyim olabilir. Bir case ifadesini birden fazla deyimin izlemesi durumunda bu deyimlerin bloklanmasna gerek yoktur. Yani bir case ifadesini izleyen tm deyimler, bir blok iindeymi gibi ele alnr. case ifadelerinin belirli bir sray izlemesi gibi bir zorunluluk yoktur.

default case

default bir anahtar szcktr. switch deyimi gvdesine yerletirilen default anahtar szcn ':' atomu izler. Oluturulan bu case'e default case denir. Edeer bir case ifadesi bulunamazsa programn ak default case iine girer. Daha nce yazlan switch deyimine default case ekleniyor. #include <stdio.h> int main() { int a; printf("bir sayi girin: "); 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; case 5 : printf("drt\n"); break; default: printf("hibiri\n"); } return 0;

Yukarda da anlatld gibi switch ayrac iindeki ifadenin saysal deerine eit bir case ifadesi bulunana kadar derleme ynnde, yani yukardan aaya doru, tm case ifadeleri srasyla snanr. case ifadelerinin oluma skl ya da olasl hakknda elde bir bilgi varsa, olasl ya da skl yksek olan case ifadelerinin daha nce yazlmas gereksiz karlatrma saysn azaltabilir.

178/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

case ifadelerinin, tamsay trnden (integral types) deimez ifadesi olmas gerekir. Bilindii gibi deimez ifadeleri, derleme aamasnda derleyici tarafndan net saysal deerlere dntrlebilir: case 1 + 3: /* Geerli */

mmkn nk 1 + 3 deimez ifadesi ama , case x + 5: /* Geersiz */

nk deimez ifadesi deil. Derleyici, derleme aamasnda saysal bir deer hesaplayamaz. case 'a' : Yukardaki case ifadesi geerlidir. 'a' bir karakter deimezidir. case ifadesi tamsay trnden bir deimez ifadesidir. case 3.5 : Yukardaki case ifadesi geersizdir. 3.5 bir gerek say deimezidir. switch kontrol deyimi yerine bir else if merdiveni yazlabilir. Yani switch deyimi olmasayd, yaplmak istenen i, bir else if merdiveni ile de yaplabilirdi. Ancak baz durumlarda else if merdiveni yerine switch deyimi kullanmak okunabilirlii artrr. rnein: if (a == 1) deyim1; else if (a == 2) deyim2; else if (a == 3) deyim3; else if (a == 4) deyim4; else deyim5; Yukardaki else if merdiveni ile aadaki switch deyimi ilevsel olarak edeerdir: switch (a) { case 1 : case 2 : case 3 : case 4 : default: } deyim1; deyim1; deyim1; deyim1; deyim5; break; break; break; break;

Her switch deyiminin yerine ayn ii grecek ekilde bir else if merdiveni yazlabilir ama her else if merdiveni bir switch deyimiyle karlanamaz. switch ayrac iindeki ifadenin bir tamsay trnden olmas zorunludur. case ifadeleri de tamsay trlerinden deimez ifadesi olmak zorundadr. switch deyimi, tamsay trnden bir ifadenin deerinin deiik tamsay deerlerine eitliinin snanmas ve eitlik durumunda farkl ilerin yaplmas iin kullanlr. Oysa else if merdiveninde her trl karlatrma sz konusu olabilir. rnek:

179/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (x > m = else if m = else if m = else m =

20) 5; (x > 30 && x < 55) 3; (x > 70 && x < 90) 7; 2;

Yukardaki else if merdiveninin yerine bir switch deyimi yazlamaz. switch deyimi baz durumlarda else if merdivenine gre ok daha okunabilir bir yap oluturur, yani switch deyiminin kullanlmas, hereyden nce, kodun daha kolay okunabilmesini, anlamlandrlmasn salar. Birden fazla case ifadesi iin ayn ilemlerin yaplmas yle salanabilir. case 1: case 2: case 3: deyim1; deyim2; break; case 4: Bunu yapmann daha ksa bir yolu yoktur. Baz programclar kaynak kodun yerleimini aadaki gibi dzenlerler: case 1: case 2: case 3: case 4: case 5: deyim1; deyim2; Aadaki program nce inceleyin, sonra derleyerek altrn: void print_season(int month) { switch (month) { case 12: case 1 : case 2 : printf("winter"); break; case 3 : case 4 : case 5 : printf("spring"); break; case 6 : case 7 : case 8 : printf("summer"); break; case 9 : case 10: case 11: printf("autumn"); } } print_season ilevi, bir ayn sra numarasn, yani yln kanc ay olduu bilgisini alyor, bu ay yln hangi mevsimi iinde ise, o mevsimin ismini ekrana yazdryor. Ayn i bir else if merdiveniyle nasl yaplabilirdi? Her if deyiminin koul ifadesi iinde mantksal veya ileci kullanlabilirdi:

180/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void print_season(int month) { if (month == 12 || month == 1 || month == 2) printf("winter"); else if (month == 3 || month == 4 || month == 5) printf("spring"); else if (month == 6 || month == 7 || month == 8) printf("summer"); else if (month == 9 || month == 10 || month == 11) printf("autumn"); } Simgesel deimezler, derleme ileminden nce nilemci tarafndan deitirilecei iin, case ifadelerinde yer alabilir: #define TRUE #define FALSE #define UNDEFINED case TRUE : case FALSE : case UNDEFINED : Yukardaki case ifadeleri geerlidir. case ifadeleri olarak karakter deimezleri de kullanlabilir: #include <stdio.h> int main() { switch (getchar()) { case '0': printf("sfr\n"); break; case '1': printf("bir\n"); break; case '2': printf("iki\n"); break; case '3': printf("\n"); break; case '4': printf("drt\n"); break; case '5': printf("be\n"); break; default : printf("gecersiz!\n"); } return 0; } case ifadelerini izleyen deyimlerin 15 - 20 satrdan uzun olmas okunabilirlii zayflatr. Bu durumda yaplacak ilemlerin ilev arlarna dntrlmesi iyi bir tekniktir. switch (x) { case ADDREC: addrec(); break; case DELREC: delrec(); break; case FINDREC: findrec(); break; } 1 0 2

181/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rnekte case ifadesi olarak kullanlan ADDREC, DELREC, FINDREC daha nce tanmlanm simgesel deimezlerdir. Her bir case iin yaplan ilemler, birer ilev iinde sarmalanyor. char ch = getch(); switch (ch) { case 'E' : deyim1; break; case 'H' : deyim2; break; default : deyim3; } Bir switch deyiminde ayn saysal deere sahip birden fazla case ifadesi olamaz. Bu durum derleme zamannda hata oluturur. switch deyimi, baka bir switch deyiminin ya da bir dng deyiminin gvdesini oluturabilir: #include <stdio.h> #include <conio.h> #define int main() { int ch; while ((ch = getch()) != ESC) switch (rand() % 7 + 1) { case 1: printf("Pazartesi\n"); break; case 2: printf("Sali\n"); break; case 3: printf("Carsamba\n"); break; case 4: printf("Persembe\n"); break; case 5: printf("Cuma\n"); break; case 6: printf("Cumartesi\n"); break; case 7: printf("Pazar\n"); } return 0; ESC 0X1B

Yukardaki main ilevinde switch deyimi, dtaki while dngsnn gvdesini oluturuyor. switch deyimi, dng gvdesindeki tek bir deyim olduundan, dtaki while dngsnn bloklanmasna gerek yoktur. Tabi while dngsnn bloklanmas bir hataya neden olmaz. Ancak case ifadeleri iinde yer alan break deyimiyle yalnzca switch deyiminden klr. while dngsnn de dna kmak iin case ifadesi iinde goto deyimi kullanlabilir. imdi de aadaki program inceleyin. Programda display_date isimli bir ilev tanmlanyor. lev gn, ay ve yl deeri olarak ald bir tarih bilgisini ngilizce olarak aadaki formatta ekrana yazdryor: 5th Jan 1998 include <stdio.h> void display_date(int day, int month, int year) { printf("%d", day);

182/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

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

"); break; "); break; "); break; ");

"); "); "); "); "); "); "); "); "); "); "); ");

break; break; break; break; break; break; break; break; break; break; break;

int main() { int day, month, year; int n = 20; while (n-- > 0) { printf("gun ay yil olarak bir tarih girin : "); scanf("%d%d%d", &day, &month, &year); display_date(day, month, year); putchar('\n'); } return 0;

levin tanmnda iki ayr switch deyimi kullanlyor. lk switch deyimiyle, gn deerini izleyen (th, st, nd, rd) sonekleri yazdrlrken, ikinci switch deyimiyle, aylara ilikin ksaltmalar (Jan, Feb. Mar.) yazdrlyor. case ifadelerini izleyen deyimlerden biri break deyimi olmak zorunda deildir. Baz durumlarda break deyimi zellikle kullanlmaz, uygun bir case ifadesi bulunduunda daha aadaki case lerin iindeki deyimlerin de yaplmas zellikle istenir. Aadaki program derleyerek altrn: #include <stdio.h> int isleap(int y) { return y % 4 == 0 && (y % 100 != 0 || y % 400 == 0); } int day_of_year(int day, int month, int year) { int sum = day; 183/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

switch (month - 1) { case 11: sum += 30; case 10: sum += 31; case 9 : sum += 30; case 8 : sum += 31; case 7 : sum += 31; case 6 : sum += 30; case 5 : sum += 31; case 4 : sum += 30; case 3 : sum += 31; case 2 : sum += 28 + isleap(year); case 1 : sum += 31; } return sum;

int main() { int day, month, year; int n = 5; while (n-- > 0) { printf("gun ay yil olarak bir tarih girin : "); scanf("%d%d%d", &day, &month, &year); printf("%d yilinin %d. gunudur!\n", year, day_of_year(day, month, year)); } } return 0;

day_of_year ilevi dardan gn, ay ve yl deeri olarak gelen tarih bilgisinin ilgili yln kanc gn olduunu hesaplayarak bu deerle geri dnyor. lev iinde kullanlan switch deyimini dikkatli bir ekilde inceleyin. switch deyiminin ayrac iinde, dardan gelen ay deerinin 1 eksii kullanlyor. Hibir case iinde bir break deyimi kullanlmyor. Uygun bir case ifadesi bulunduunda, daha aada yer alan tm case iindeki deyimler de yaplr. Bylece, dardan gelen ay deerinden daha dk olan her bir ayn ka ektii bilgisi, gn toplamn tutan sum deikenine katlyor.

184/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

goto DEYM
Dier programlama dillerinde olduu gibi C dilinde de programn ak, bir koula bal olmakszn kaynak kod iinde baka bir noktaya ynlendirilebilir. Bu, C dilinde goto deyimi ile yaplr: goto deyiminin genel szdizimi aadaki gibidir: <goto etiket;> .... <etiket:> <deyim;> goto, C dilinin 32 anahtar szcnden biridir. Etiket (label), programcnn verdii bir isimdir. phesiz isimlendirme kurallarna uygun olarak seilmelidir. Programn ak, bu etiketin yerletirilmi olduu yere ynlendirilir. Etiket, goto anahtar szcnn kullanld ilev iinde herhangi bir yere yerletirilebilir. Etiket isminden sonra ':' atomu yer almak zorundadr. Etiketi izleyen deyim de goto kontrol deyiminin szdiziminin bir parasdr. Etiketten sonra bir deyimin yer almamas bir szdizim hatasdr. Etiketin goto anahtar szcnden daha sonraki bir kaynak kod noktasna yerletirilmesi zorunluluu yoktur. Etiket goto anahtar szcnden nce de tanmlanm olabilir: #include <stdio.h> int main() { /***/ goto GIT; /***/ GIT: printf("goto deyimi ile buraya gelindi\n"); } return 0;

Yukardaki programda, etiket goto anahtar szcnden daha sonra yer alyor. int main() { GIT: printf("goto deyimi ile gelinecek nokta\n"); /***/ goto GIT; /***/ } return 0;

Yukardaki programda, etiket goto anahtar szcnden daha nce yer alyor. goto etiketleri, geleneksel olarak byk harf ile, birinci stuna dayal olarak yazlr. Bylece kaynak kod iinde daha fazla dikkat ekerler. goto etiketleri bir ilev iinde, bir deyimden nce herhangi bir yere yerletirilebilir. Yani etiket, ayn ilev iinde bulunmak kouluyla, goto anahtar szcnn yukarsna ya da aasna yerletirilebilir. Bu zelliiyle goto etiketleri, yeni bir bilinirlik alan kural oluturur. Bir isim, ilev iinde nerede tanmlanrsa tanmlansn o ilev iinde her yerde bilinir. Bu bilinirlik alan kuralna "ilev bilinirlik alan" (function scope) denir.

185/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

goto etiketleri bulunduu bloun isim alanna eklenmez. goto etiket isimleri ayr bir isim alannda deerlendirilir. Bir blok iindeki goto etiketi ile ayn isimli bir yerel deiken olabilir: void func() { int x; goto x; x: x = 20; } Yapsal programlama tekniinde goto deyiminin kullanlmas nerilmez. nk goto deyiminin kullanlmas bir takm sakncalar dourur: 1. goto deyimi programlarn okunabilirliini bozar. Kodu okuyan kii goto deyimiyle karlatnda ilevin iinde etiketi arayp bulmak zorunda kalr ve program bu noktadan okumay srdrr. 2. goto deyimlerinin kullanld bir programda bir deiiklik yaplmas ya da programn, yaplacak eklemelerle, gelitirilmeye allmas daha zor olur. Programn herhangi bir yerinde bir deiiklik yaplmas durumunda, eer program iinde baka yerlerden deiikliin yapld yere goto deyimleri ile srama yaplm ise, bu noktalarda da bir deiiklik yaplmas gerekebilir. Yani goto deyimi program paralarnn birbirine olan bamlln artrr, bu da genel olarak istenen bir ey deildir. Bu olumsuzluklara karn, baz durumlarda goto deyiminin kullanlmas programn okunabilirliini bozmak bir yana, dier seeneklere gre daha okunabilir bir yapnn olumasna yardmc olur: ie birden fazla dng varsa, ve iteki dnglerden birindeyken, yalnzca bu dngden deil, btn dnglerden birden klmak isteniyorsa goto deyimi kullanlmaldr. Aadaki kod parasnda i ie dng bulunuyor. En iteki dngnn iinde func ilevi arlarak ilevin geri dn deeri snanyor. lev eer 0 deerine geri dnerse programn ak goto deyimiyle tm dnglerin dna ynlendiriliyor: #include <stdio.h> int test_func(int val); int main() { int i, j, k; for (i = 0; i < 100; ++i) { for (j = 0; j < 100; ++j) { for (k = 0; k < 20; ++k) { /*...*/ if (!test_func(k)) goto BREAK; /*...*/ } } } BREAK: printf("dng dndaki ilk deyim\n"); return 0; }

186/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki ilev iinde i ie ayr dng deyimi yer alyor. En iteki dngnn iinde arlan bir ilev ile bir snama ilemi yaplm, snamann olumsuz sonulanmas durumunda, programn ak en dtaki dng deyiminin sonrasna ynlendiriliyor. Oysa goto deyimi kullanmasayd, ancak bir bayrak (flag) deikenin kullanlmasyla ayn ama gerekletirilebilirdi. Her dngnn knda bayrak olarak kullanlan deikenin deerinin deitirilip deitirilmedii snanmak zorunda kalnrd. #include <stdio.h> #define #define BREAK NO_BREAK 0 1

int test_func(int val); int main() { int i, j, k; int flag = NO_BREAK; for (i = 0; i < 100; ++i) { for (j = 0; j < 100; ++j) { for (k = 0; k < 20; ++k) { /*...*/ if (!test_func(k)) { flag = BREAK; break; } /*...*/ } if (flag == BREAK) break; } if (flag == BREAK) break; } printf("dng dndaki ilk deyim\n"); } return 0;

goto deyiminin kullanlmas okunabilirlik ynnden daha iyidir. Aadaki rnekte ise goto deyimiyle hem switch deyiminden hem de switch deyiminin iinde bulunduu for dngsnden klyor: #define #define #define #define #define ADDREC LISTREC DELREC SORTREC EXITPROG 1 2 3 4 5

int get_option(void); void add_rec(void); void list_rec(void); void del_rec(void); void sort_rec(void); int main() { int option; 187/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

for (;;) { option = get_option(); switch (option) { case ADDREC :add_rec();break; case LISTREC :list_rec();break; case DELREC :del_rec(); break; case SORTREC :sort_rec(); break; case EXITPROG :goto EXIT; } } EXIT: return 0; } Yukardaki main ilevinde option deikeninin deeri EXITPROG olduunda programn ak, goto deyimiyle sonsuz dngnn dna gnderiliyor. goto deyimi yerine break deyimi kullanlsayd, yalnzca switch deyiminden klm olurdu. goto deyimiyle, bir ilevin iindeki bir noktadan, yine kendi iindeki bir baka noktaya srama yaplabilir. Byle sramalara yerel sramalar (local jumps) denir. Bir ilevin iinden baka bir ilevin iine sramak baka aralarla mmkndr. Byle sramalara yerel olmayan sramalar (non-local jumps) denir. C dilinde, yerel olmayan sramalar ismi setjmp ve longjmp olan standart ilevlerle yaplr. Bu sramalar ounlukla "Olaan d hatalarn ilenmesi" (exception handling) amacyla kullanlr.

188/529

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

RASTGELE SAYI RETM ve KONTROL DEYMLERNE LKN GENEL UYGULAMALAR


Rastgele say retimi matematiin nemli konularndan biridir. Rastgele saylar ya da daha doru ifadeyle, rastgele izlenimi veren saylar (szde rastgele saylar - pseudo random numbers) istatistik, ekonomi, matematik, yazlm gibi pek ok alanda kullanlr. Rastgele saylar bir rastgele say reticisi (random number generator) tarafndan retilir. Rastgele say reticisi aslnda matematiksel bir ilevdir. Sz konusu ilev, bir balang deerini alarak bir deer retir. Daha sonra retmi olduu her deeri yeni girdi olarak alr, yeniden bir say retir. reticinin rettii saylar rastgeledir.

rand levi

Standart rand ilevi rastgele say retir. Bu ilevin bildirimi aadaki gibidir: int rand(void); C standartlar rand ilevinin rastgele say retimi konusunda kullanaca algoritma ya da teknik zerinde bir koul koymamtr. Bu konu derleyiciyi yazanlarn seimine bal (implementation dependent) braklmtr. rand ilevinin bildirimi, standart bir balk dosyas olan stdlib.h iindedir. Bu yzden rand ilevinin arlmas durumunda bu balk dosyas "include" nilemci komutuyla kaynak koda eklenmelidir. #include <stdlib.h> rand ilevi her arldnda [0, RAND_MAX] aralnda rastgele bir tamsay deerini geri dndrr. RAND_MAX stdlib.h balk dosyas iinde tanmlanan bir simgesel deimezdir. C standartlar bu simgesel deimezin en az 32767 deerinde olmasn art komaktadr. Derleyicilerin hemen hepsi RAND_MAX simgesel deimezini 32767 olarak, yani 2 byte iaretli int trnn en byk deeri olarak tanmlar: #define RAND_MAX 32767

Aadaki program parasnda, 0 ile RAND_MAX arasnda 10 adet rastgele say retilerek ekrana yazdrlyor. Program derleyerek altrn: #include <stdio.h> #include <stdlib.h> int main() { int k; for (k = 0; k < 10; ++k) printf("%d ", rand()); return 0; } Yukardaki kaynak kodla oluturulan programn her altrlmasnda ekrana ayn saylar yazlr. rnein yukardaki program, DOS altnda Borland Turbo C 2.0 derleyicisi ile derleyip altrldnda ekran kts aadaki gibi oldu: 346 130 10982 1090 11656 7117 17595 6415 22948 31126

189

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Oluturulan program her altrldnda neden hep ayn say zinciri elde ediliyor? rand ilevi rastgele say retmek iin bir algoritma kullanyor. Bu algoritma derleyiciden derleyiciye deise de, rastgele say retiminde kullanlan ana tema ayndr. Bir balang deeri ile ie balanr. Buna tohum deeri (seed value) denir. Bu deer zerinde baz ilemler yaplarak rastgele bir say elde edilir. Tohum deer zerinde yaplan ilem bu kez elde edilen rastgele say zerinde yinelenir. rand ilevi arlarn ieren bir program her altrldnda ayn tohum deerinden balanaca iin ayn say zinciri elde edilir. Bir baka standart ilev olan srand ilevi, rastgele say reticisinin tohum deerini deitirmeye yarar. srand ilevinin stdlib.h balk dosyasnda yer alan bildirimi aadaki gibidir: void srand (unsigned seed); srand ilevine gnderilen deer, ilev tarafndan rastgele say reticisinin tohum deeri yaplr. srand ilevine argman olarak baka bir tohum deeri gnderildiinde ilevin rettii rastgele say zinciri deiir. Aada rand ve srand ilevleri tanmlanyor: #define RAND_MAX 32767

srand levi

unsigned long int next = 1; int rand() { next = next * 1103515245 + 12345; return (unsigned int)(next / 65536) % 32768; } void srand(unsigned int seed) { next = seed; } srand ilevi arlmaz ise balang tohum deeri 1'dir. Yukardaki programa srand ilevi arsn ekleyerek yeniden derleyin, altrn: #include <stdio.h> #include <stdlib.h> int main() { int k; srand(100); for (k = 0; k < 10; ++k) printf("%d ", rand()); } return 0;

Program bu ekliyle DOS altnda Borland Turbo C 2.0 derleyicisi ile derleyip altrldnda ekran kts aadaki gibi oldu: 1862 11548 3973 4846 9095 16503 6335 13684 21357 21505

190

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Ancak bu kez oluturulan program da her altrldnda yine yukardaki say zinciri elde edilir, deil mi? rand ilevinin kullanmakta olduu nceden seilmi (default) tohum deeri kullanlmasa da, bu kez her defasnda srand ilevine gnderilmi olan tohum deeri kullanlr. Program birka kere altrp gerekten hep ayn say zincirinin retilip retilmediini grn. Baz durumlarda, programn her altrlmasnda ayn rastgele say zincirinin retilmesi istenmez. rnein bir oyun programnda programn altrlmasyla hep ayn saylar retilirse, oyun hep ayn biimde oynanr. Programn her almasnda farkl bir say zincirinin elde edilmesi iin, srand ilevinin rastgele say reticisinin tohum deerini programn her almasnda baka bir deer yapmas gerekir. Bu amala ou zaman standart time ilevi ilevinden faydalanlr. time standart bir C ilevidir, bildirimi standart bir balk dosyas olan time.h dosyas iindedir. Parametre deikeni gsterici olan time ilevini, ancak ileride ayrntl olarak ele alacaz. imdilik time ilevini iimizi grecek kadar inceleyeceiz. time ilevi kendisine 0 deeri gnderildiinde, nceden belirlenmi bir tarihten (sistemlerin ounda 01.01.1970 tarihinden) ilevin arld ana kadar geen saniye saysn geri dndrr. levin geri dn deeri, derleyicilerin ounda long trden bir deerdir. inde rastgele say retilecek programda, srand ilevine argman olarak time ilevinin geri dn deeri gnderilirse, program her altnda, belirli bir zaman gemesi nedeniyle, rastgele say reticisi baka bir tohum deeriyle ilkdeerini alr. Bylece programn her altrlmasnda farkl say zinciri retilir: srand(time(0)); srand ilevine yaplan bu ar, derleyicilerin ounda standart olmayan randomize isimli bir makro olarak tanmlanmtr: randomize(); Yukardaki ilev ars yerine bu makro da kullanlabilir. Makrolar konusu ileride ayrntl olarak ele alnacak. Yukardaki daha nce yazlan rnek program her altnda farkl say zinciri retecek duruma getirelim: #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int k; srand(time(0)); for (k = 0; k < 10; ++k) printf("%d ", rand()); return 0; } Programlarda bazen belirli bir aralkta rastgele say retilmesi istenir. Bu amala kalan ileci kullanlabilir. Aadaki ifadeleri inceleyin: rand() % 2 Yalnzca 0 ya da 1 deerini retir.

191

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

rand() % 6 0 - 5 aralnda rastgele bir deer retir rand() % 6 + 1 1 - 6 aralnda rastgele bir deer retir. (rnein bir zar deeri) rand() % 6 + 3 3 - 8 aralnda rastgele bir deer retir. Ancak derleyici programlarn salad rastgele say reticilerinin rettikleri rastgele saylarn, dk anlaml bitleri ounlukla rastgele kabul edilemez. Bu durumda yukardaki ifadeler, retilmesi gereken tm saylar iin eit bir dalm salamaz. Dalmn daha dzgn olabilmesi iin baz yntemler kullanlabilir: rand() % N ifadesi yerine rand()/(RAND_MAX / N + 1) ya da (int)((double)rand() / ((double)RAND_MAX + 1) * N) ifadeleri yazlabilir. Ya da aadaki gibi bir ilev tanmlanabilir: #include <stdio.h> #include <stdlib.h> #define N 10

int mrand() { unsigned int x = (RAND_MAX + 1u) / N; unsigned int y = x * N; unsigned int r; while ((r = rand()) >= y) ; return r / x;

srand(time(0)) arsnn bir dng iinde yer almas sk yaplan bir hatadr. #include <stdio.h> #include <stdlib.h> #include <time.h> int zar_at() { srand(time(0)); return rand() % 6 + 1 + }

rand() % 6 + 1;

192

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int main() { int k; for (k = 0; k < 10; ++k) printf("%d\n", zar_at()); } return 0;

Yukarda yazlan programda yer alan zar_at isimli ilev, bir ift zar atldnda elde iki zarn toplam deeriyle geri dnyor. srand(time(0)) ars zar_at ilevi iinde yaplyor. main ilevi iinde oluturulan for dngsyle 10 kez zar_at ilevi arlyor. levin her arsnda time ilevi hep ayn geri dn deerini retir. Bu durumda srand ilevine hep ayn argman geildiinden rand ilevi arlar da hep ayn iki sayy retir. Yani ekrana 10 kez ayn deer yazdrlr. srand(time(0)) arsnn main ilevi iindeki for dngsnden nce yaplmas gerekirdi, deil mi? Aadaki main ilevinde uzunluklar 3 - 8 harf arasnda deien ngiliz alfabesindeki harfler ile oluturulmu rastgele 10 szck ekrana yazdrlyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define #define TIMES MIN_WORD_LEN MAX_WORD_LEN 10 3 8

void write_word(void) { int len = rand() % (MAX_WORD_LEN - MIN_WORD_LEN + 1) + MIN_WORD_LEN; while (len--) putchar('A' + rand() % 26);

int main() { int k; srand(time(0)); for (k = 0; k < TIMES; ++k) { write_word(); putchar('\n'); } return 0;

Aada bu kez yazdrlan szcklerin iinde sesli harf olmamas salanyor: int isvowel(int c) { return c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U'; } void write_word(void) { int len = rand() % (MAX_WORD_LEN - MIN_WORD_LEN + 1) + MIN_WORD_LEN; int ch;

193

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

while (len--) { while (isvowel(ch = rand() % 26 + 'A')) ; putchar(ch); }

Aada rastgele bir tarihi ekrana yazdran print_random_date isimli bir ilev tanmlanyor. lev her arldnda 1.1.MIN_YEAR, 31.12.MAX_YEAR tarihleri arasnda rastgele ancak geerli bir tarih bilgisini ekrana yazyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define MAX_YEAR MIN_YEAR 2010 1900

void print_random_date() { int d, m, y; y = rand() % (MAX_YEAR - MIN_YEAR + 1) + MIN_YEAR; m = rand() % 12 + 1; switch (m) { case 4 : case 6 : case 9 : case 11: d = rand() % 30 + 1; break; case 2 : d = rand() % (isleap(y) ? 29 : 28) + 1; break; default: d = rand() % 31 + 1; } printf("%d/%d/%d\n", d, m, y);

int isleap(int y) { return y % 4 == 0 && y % 100 != 0 || y % 400 == 0; } int main() { int k; srand(time(0)); for (k = 0; k < 20; ++k) print_random_date(); } return 0;

Olaslk problemleri, olasla konu olayn bir bilgisayar program ile gerekletirilmesi yoluyla zlebilir. yi bir rastgele say reticisi kullanld takdirde, olasla konu olay, bir bilgisayar program ile oynatlr, olay bilgisayarn ilem yapma hzndan faydalanlarak yksek saylarda yinelemeye sokulur. phesiz hesaplanmak istenen olaya ilikin olaslk deeri, yaplan yineleme saysna ve rastgele say reticisinin niteliine bal olur. Aadaki kod yaz tura atlmas olaynda tura gelme olasln hesaplyor:

194

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <time.h> #define #define TIMES HEADS 30000 1

int main() { int heads_counter = 0; int k; srand(time(0)); for (k = 0; k < TIMES; ++k) if (rand() % 2 == HEADS) heads_counter++; printf("tura gelme olasl = %lf", (double) heads_counter / TIMES); } return 0;

Yukardaki program TIMES simgesel deimezinin farkl deerleri iin altrldnda ekran kts aadaki ekilde oldu: #define tura gelme #define tura gelme #define tura gelme #define tura gelme #define tura gelme #define tura gelme #define tura gelme TIMES 100 olasl = 0.480000 TIMES 500 olasl = 0.496000 TIMES 2500 olasl = 0.506800 TIMES 10000 olasl = 0.503500 TIMES 30000 olasl = 0.502933 TIMES 100000 olasl = 0.501450 TIMES 1000000 olasl = 0.500198

Aada bir baka olaslk almas yaplyor: Craps hemen hemen dnyann her yerinde bilinen, iki zarla oynanan bir kumardr. Oyunun kurallar yledir : Zarlar atacak oyuncu oyunu kasaya kar oynar. Atlan iki zarn toplam deeri 7 ya da 11 ise oyuncu kazanr. Atlan iki zarn toplam deeri 2, 3, 12 ise oyuncu kaybeder. (Buna craps denir!) ki zarn toplam deeri yukardakilerin dnda bir deer ise (yani 4, 5, 6, 8, 9, 10) oyun u ekilde srer : Oyuncu ayn sonucu buluncaya kadar zarlar tekrar atar. Eer ayn sonucu bulamadan nce oyuncu 7 atarsa (yani atlan iki zarn toplam deeri 7 olursa) oyuncu kaybeder. Eer 7 gelmeden nce oyuncu ayn sonucu tekrar atmay baarrsa , kazanr. Birka rnek : Oyuncunun att zarlar 11 3 9 8 6 3 12 5 8 4 2 4 9 6 5 8 9 2 3 7 7 10 4 8 11 8 3 6 5 4 9 10 Oyun sonucu Oyuncu kazanr Oyuncu kaybeder Oyuncu kazanr Oyuncu kaybeder Oyuncu kazanr Oyuncu kazanr

195

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki program, bu oyunu oynayan oyuncunun kazanma olasln hesaplyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define NKEZ 1000000

int zar_at() { int zar1 = rand() % 6 + 1; int zar2 = rand() % 6 + 1; return zar1 + zar2; } /* oyuncu kazanrsa 1 deerine, oyuncu kaybederse 0 deerine geri dner */ int oyun() { int zar_toplam; zar_toplam = zar_at(); switch (zar_toplam) { case 7 : case 11: return 1; case 2 : case 3 : case 12: return 0; } return oyun_devami(zar_toplam); } /* oyuncu 4, 5, 6, 8, 9, 10 atmissa oyunun devam. oyuncu kazanrsa 1 deerine, oyuncu kaybederse 0 deerine geri dner */ int oyun_devami(int zar_toplam) { int yeni_zar; for (;;) { yeni_zar = zar_at(); if (yeni_zar == zar_toplam) return 1; if (yeni_zar == 7) return 0; } } int main() { int k; int kazanma_sayisi = 0; srand(time(0)); for (k = 0; k < NKEZ; ++k) kazanma_sayisi += oyun(); printf("kazanma olasiligi = %lf\n", (double)kazanma_sayisi / NKEZ); } return 0;

196

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Rastgele Gerek Say retimi

Rastgele gerek say reten bir standart C ilevi yoktur. Ancak RAND_MAX simgesel deimezinden faydalanarak (double)rand() / RAND_MAX ifadesi ile 0 1 aralnda rastgele bir gerek say retilebilir. Aada rastgele gerek say reten drand isimli bir ilev tanmlanyor. levi inceleyerek, rastgele bir gerek sayy nasl rettiini anlamaya aln: #define PRECISION 2.82e14

double drand() { double sum = 0; double denom = RAND_MAX + 1; double need; for (need = PRECISION; need > 1; need /= (RAND_MAX + 1.)) { sum += rand() / denom; denom *= RAND_MAX + 1.; } return sum;

int main() { int k; for (k = 0; k < 10; ++k) printf("%lf\n", drand()); return 0; } Aada pi says Monte Carlo yntemi diye bilinen yntemle bulmaya allyor. Bu yntemde birim kare iinde yarap karenin kenar uzunluuna eit bir daire paras olduu dnlr. Birim kare iinde rastgele alnan bir nokta, ya daire parasnn iinde ya da dnda olur. Rastgele alnan bir noktann daire parasnn iinde olma olasl, yarap 1 birim olan bir dairenin alannn drtte birinin, kenar 1 birim olan karenin alanna orandr. Bu da

noktalarn ka tanesinin dairenin iinde olduunu bulursak, bu deerin n saysna orannn 4 kat saysn verir: #include <stdio.h> #include <stdlib.h> #include <time.h> #define NTIMES 10000000

deerine eittir. O zaman n tane rastgele nokta alp bu 4

int main() { double x, y; int k; int inside_counter = 0; srand(time(0)); for (k = 0; k < NTIMES; ++k) { x = (double)rand() / RAND_MAX;

197

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} printf("hesaplanan pi degeri = %lf\n", 4. * inside_counter / NTIMES); return 0; }

y = (double)rand() / RAND_MAX; if (x * x + y * y <= 1) inside_counter++;

198

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DZLER
Bir konuyla ilgili, mantksal iliki iindeki verilerin bellekte saklanmasna ynelik dzenlemelere veri yaps denir. Veri yaplar bellekte belirli bir dzen iinde tutulmu verilere ulalabilmesine, bu veriler zerinde baz ilemlerin etkin bir biimde yaplmasna olanak salar.

Veri Yaps Nedir

Bellekte bitiik bir biimde bulunan, ayn trden nesnelerin oluturduu veri yapsna dizi (array) denir. Dizi veri yapsnn en nemli zellii, mantksal bir iliki iindeki ayn trden verilerin bellekte bitiik (contigous) olarak tutulmasdr. Bunun da uygulamalarda salad fayda udur: Dizinin bir elemanna, elemann konum bilgisiyle deimez bir zamanda ulalabilir. Yani dizinin eleman says ne olursa olsun, konumu bilinen bir elemana ulam zaman ayndr. Bu da baz uygulamalarn etkin bir ekilde gerekletirilmesini kolaylatrr.

Dizi Nedir

C dilinde dizi (array), ayn trden bir ya da daha fazla nesnenin bellekte dizi veri yaps biiminde tutulmasn salayan aratr. C'de bir dizinin tanmlanmasyla birden fazla sayda nesne tek bir deyimle tanmlanabilir. 10 elemana sahip bir dizi tanmlamak yerine, phesiz isimleri farkl 10 ayr nesne de tanmlanabilir. Ama 10 ayr nesne tanmlandnda bu nesnelerin bellekte bitiik olarak yerlemeleri gvence altna alnm bir zellik deildir. Oysa dizi tanmlamasnda, dizinin eleman olan btn nesnelerin bellekte bitiik olarak yer almalar gvence altna alnm bir zelliktir. Dizi de bir veri tr olduuna gre, dizilerin de kullanlmalarndan nce tanmlanmalar gerekir.

C Dilinde Diziler

Dizilerin Tanmlanmas

Dizi tanmlamalarnn genel biimi: <tr> <dizi ismi> [<eleman says>]; Yukardaki genel biimde keli ayra, eleman saysnn seimlik olduunu deil, eleman says bilgisinin keli ayra iine yazlmas gerektiini gsteriyor. tr dizi ismi eleman says : Dizi elemanlarnn trn gsteren anahtar szcktr. : simlendirme kurallarna uygun olarak verilecek herhangi bir isimdir. : Dizinin ka elemana sahip olduunu gsterir.

rnek dizi bildirimleri: double a[20]; int ave[10]; char path[80]; Yukardaki tanmlamalarda a, 20 elemanl, her bir eleman double trden olan bir dizidir. ave, 10 elemanl, her bir eleman int trden olan bir dizidir. path, 10 elemanl, her bir eleman char trden olan bir dizidir. Tanmlamada yer alan, eleman says belirten ifadenin bir tamsay trnden deimez ifadesi olmas zorunludur. Bir baka deyile derleyici bu ifadenin deerini derleme zamannda elde edebilmelidir:

199

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int int int int int

x = 100; a[x]; /* Geersiz */ b[5.]; /* Geersiz */ c[10 * 20]; d[sizeof(int) * 100];

Yukardaki deyimlerden a ve b dizilerinin tanmlamalar geersizdir. a dizisinin tanmnda boyut belirten ifade olarak deimez ifadesi olmayan bir ifade kullanlyor. b dizisinin tanmnda ise boyut belirten ifade bir gerek say trndendir. c dizisinin tanmnda ise bir hata sz konusu deildir. 10 * 20 bir deimez ifadesidir. d dizisinin tanm da bir hata oluturmaz nk sizeof ilecinin rettii deer derleme zamannda elde edilir. Dizi bildirimlerinde eleman saysn belirten ifade yerine sklkla simgesel deimezler kullanlr: #define ARRAY_SIZE 100 int a[ARRAY_SIZE]; Program iinde dizi boyutu yerine hep ARRAY_SIZE simgesel deimezi kullanlabilir. Bylece programda daha sonra dizi boyutuna ilikin bir deiiklik yaplmak istendiinde, yalnzca simgesel deimezin deerinin deitirilmesi yeterli olur. Dier deiken bildirimlerinde olduu gibi, virgl ayracyla ayrlarak, birden fazla dizi, tr belirten szcklerin bir kez kullanlmasyla tanmlanabilir: int x[100], y[50], z[10]; x, y ve z, elemanlar int trden olan dizilerdir. Diziler ve dier nesneler trleri ayn olmak kaydyla tek bir tanmlama deyimiyle tanmlanabilir: 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 keli ayra ileciyle [] ulalabilir. Keli ayra ileci bir gsterici ilecidir. Bu ile "Gstericiler" konusunda ayrntl bir ekilde ele alnacak. Keli ayra ilecinin terimi dizi ismidir. Aslnda bu bir adres bilgisidir, nk bir dizi ismi ileme sokulduunda, ilem ncesi derleyici tarafndan otomatik olarak dizinin ilk elemannn adresine dntrlr. Keli ayra iinde dizinin kanc indisli elemanna ulalacan gsteren bir tamsay ifadesi olmaldr. C dilinde bir dizinin ilk eleman, dizinin sfr indisli elemandr. T bir tr bilgisi olmak zere T a[SIZE]; gibi bir dizinin ilk eleman a[0] son eleman ise a[SIZE - 1]'dir. rnekler: dizi[20] ave[0] total[j] /* a dizisinin 20 indisli yani 21. eleman olan nesne */ /* ave dizisinin 0 indisli yani birinci eleman olan nesne */ /* total dizisinin j indisli eleman olan nesne*/

Grld gibi "bir dizinin n. eleman" ve "bir dizinin n indisli eleman" terimleri dizinin farkl elemanlarn belirtir. Bir dizinin n indisli eleman o dizinin n + 1 . elemandr.

200

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir dizi tanmlamas ile karlaan derleyici, tanmlanan dizi iin bellekte yer ayrr. Ayrlacak yer phesiz dizinin eleman says * bir elemann bellekte kaplad yer kadar byte olur. rnein: int a[5]; gibi bir dizi tanmlamas yapldn dnelim. Windows iletim sisteminde allyorsa derleyici a dizisi iin bellekte 4 * 5 = 20 byte yer ayrr. Dizi indis ifadelerinde ++ ya da -- ileleri sk kullanlr: int a[20]; int k = 10; int i = 5; a[k++] = 100; deyimiyle dizinin 10 indisli elemanna yani dizinin 11. elemanna 100 deeri atanyor. Daha sonra k deikeninin deeri 1 artrlarak 11 yaplyor. a[--i] = 200; deyimiyle dizinin 4 indisli elemanna yani dizinin 5. elemanna 200 deeri atanyor. Daha sonra i deikeninin deeri 1 azaltlarak 4 yaplyor. Keli ayra ilecinin kullanlmasyla artk dizinin herhangi bir eleman dier deikenler gibi kullanlabilir. Aadaki rnekleri inceleyin: a[0] = 1; a dizisinin ilk elemanna 1 deeri atanyor. printf("%d\n", b[5]); b dizisinin 6. elemannn deeri ekrana yazdrlyor: ++c[3]; c dizisinin 4. elemannn deeri 1 artrlyor: d[2] = e[4]; d dizisinin 3. elemanna e dizisinin 5. eleman atanyor: Diziler zerinde ilem yapmak iin sklkla dng deyimleri kullanlr. Bir dng deyimi yardmyla bir dizinin tm elemanlarna ulamak, ok karlalan bir durumdur. Aada SIZE elemanl a isimli bir dizi iin for ve while dng deyimlerinin kullanld baz kalplar gsteriliyor: a dizisinin btn elemanlarna 0 deeri atanyor: for (i = 0; i < SIZE; ++i) a[i] = 0;

201

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Ayn i bir phesiz bir while dng deyimiyle de yaplabilirdi: i = 0; while (i < SIZE) a[i++] = 0; Aada a dizisinin elemanlarna standart scanf ileviyle standart giri biriminden deer alnyor: for (i = 0; i < SIZE; i++) scanf("%d", &a[i]); ya da i = 0; while (i < SIZE) scanf("%d", &a[i++]); Aada a dizisinin elemanlarnn toplam hesaplanyor: for (total = 0, i = 0; i < SIZE; i++) total += a[i]; ya da total = 0; i = 0; while (i < SIZE) total += a[i++];

Bir dizi tanmlamasn gren derleyici dizi iin bellekte dizinin tm elemanlarnn saca byklkte bir alan ayrr: double a[10]; Gibi bir tanmlama yapldnda, allan sistemde double trnn bellekte 8 byte yer kaplad var saylrsa, dizi iin bellekte bitiik (contiguous) toplam 80 byte'lk bir yer ayrlr. Dizinin son eleman a[9] olur. ok sk yaplan bir hata, dizinin son elemanna ulamak amacyla yanllkla bellekte derleyici tarafndan ayrlmam bir yere deer atamak, yani diziyi tarmaktr: a[10] = 5.; deyimiyle bellekte ne amala kullanld bilinmeyen 8 byte' lk bir alana, yani gvenli olmayan bir bellek blgesine deer aktarma giriiminde bulunulur. Dizi tamalar derleme zamannda kontrol edilmez. Byle hatalar programn alma zaman ile ilgilidir.

Dizilerin Tarlmas

Deiken tanmlamalarnda tanmlanan bir deikenin "ilkdeer verme szdizimi" diye isimlendirilen bir kural ile belirli bir deerle balatlmas salanabiliyordu. Tanmlanan dizilere de ilkdeer verilebilir: double sample[5] = {1.3, 2.5, 3.5, 5.8, 6.0}; char str[4] = {'d', 'i', 'z', 'i'}; unsigned a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

Dizilere lkdeer Verilmesi

202

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Dizilere yukardaki gibi ilkdeer verildiinde, verilen deerler dizinin ilk elemanndan balayarak dizi elemanlarna srayla atanm olur. Dizilerin tm elemanlarna ilkdeer verme zorunluluu yoktur. Dizinin eleman saysndan daha az sayda elemana ilkdeer verilmesi durumunda kalan elemanlara 0 deeri atanm olur. Bu kural hem yerel hem de global diziler iin geerlidir. Bu durumda bir dizinin btn elemanlarna 0 deeri verilmek isteniyorsa bunun en ksa yolu aadaki gibidir: int a[20] = {0}; Yalnzca dizinin ilk elemanna 0 ilkdeeri veriliyor. Bu durumda derleyici dizinin kalan elemanlarna otomatik olarak 0 deeri yerletirecek kodu retir. Dizi elemanlarna ilkdeer verilmesinde kullanlan ifadeler, deimez ifadeleri (constant expression) olmaldr. int a[10] = {b, b + 1, b + 2}; /* Geersiz */

gibi bir ilkdeer verme ilemi geersizdir. [Yukardaki tanmlama C++ dilinin kurallarna uygundur. C++ dilinde dizi elemanlarna deimez ifadeleriyle ilkdeer vermek zorunlu deildir] Bir diziye ilkdeer verme ileminde, dizi eleman saysndan daha fazla sayda ilkdeer vermek geersizdir: int b[5] = {1, 2, 3, 4, 5, 6}; /* Geersiz */

Yukardaki rnekte b dizisi 5 elemanl olmasna karn, ilkdeer verme deyiminde 6 deer kullanlyor. Bu durum derleme zamannda hata oluturur. lkdeer verme ileminde dizi boyutu belirtilmeyebilir. Bu durumda derleyici dizi uzunluunu, verilen ilkdeerleri sayarak kendi hesaplar. Dizinin o boyutta aldn kabul eder. rnein: int a[] = {1, 2, 3, 4, 5}; Derleyici yukardaki deyimi grdnde a dizisinin 5 elemanl olduunu kabul eder. Bu durumda yukardaki gibi bir bildirimle aadaki gibi bir bildirim edeerdir: int a[5] = {1, 2, 3, 4, 5}; Baka rnekler : char name[] = {'B' 'E ', 'R', 'N', 'A', '\0'}; unsigned short count[ ] = {1, 4, 5, 7, 8, 9, 12, 15, 13, 21}; Derleyici name dizisinin boyutunu 6, count dizisinin boyutunu ise 10 olarak varsayar. Diziye ilkdeer verme listesi bir virgl atomuyla sonlandrlabilir: int a[] = { 1, 4, 5, 7, 8, 9, 12, 15, 13, 2, 8, 9, 8, 9, 4, 15, 18, 25, };

Bir dizi de dier nesneler gibi yerel ya da global olabilir. Yerel diziler bloklarn iinde tanmlanan dizilerdir. Global diziler ise global isim alannda, yani tm bloklarn dnda tanmlanr. Global bir dizinin tm elemanlar, global nesnelerin zelliklerine sahip olur.

Yerel ve Global Diziler

203

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yani dizi global ise, dizi eleman olan nesneler dosya bilinirlik alanna (file scope) ve statik mre (static storage duration) sahip olurlar. Global bir dizi sz konusu olduunda eer dizi elemanlarna deer verilmemise, dizi elemanlar 0 deeriyle balatlr. Ama yerel diziler sz konusu olduunda, dizi eleman olan nesneler blok bilinirlik alanna (block scope) mr asndan ise otomatik mr karakterine (automatic storage class) sahip olur. Deer atanmam dizi elemanlar iinde p deerler (garbage values) bulunur. Aadaki program yazarak derleyin: #include <stdio.h> #define int g[SIZE]; int main() { int y[SIZE]; int i; for (i = 0; i < SIZE; ++i) printf("g[%d] = %d\n", i, g [i]); for (i = 0; i < SIZE; ++i) printf("y[%d] = %d\n", i, y [i]); } return 0; SIZE 10

Dizilerin Birbirine Atanmas


int a[SIZE], b[SIZE];

Dizilerin elemanlar nesnedir. Ancak bir dizinin tamam bir nesne olarak ilenemez:

gibi bir tanmlamadan sonra, a dizisi elemanlarna b dizisinin elemanlar kopyalanmak amacyla, aadaki gibi bir deyimin yazlmas szdizim hatasdr. a = b; /* Geersiz */

Yukardaki gibi bir atama derleme zaman hatasna neden olur. nk dizilerin isimleri olan a ve b nesne gstermez. Dizinin bellekte kaplad toplam alan dorudan tek bir nesne olarak ilenemez. Yani dizinin elemanlar birer nesnedir ama dizinin tamam bir nesne deildir. C'de dizi isimleri dizilerin bellekte yerletirildikleri bloun balangcn gsteren, dizinin tr ile ayn trden adres deerleridir. Dolaysyla deitirilebilir sol taraf deeri (modifiable L value) deillerdir. ki dizi birbirine ancak bir dng deyimi ile kopyalanabilir: for (i = 0; i < SIZE; ++i) a[i] = b[i]; Yukardaki dng deyimiyle b dizisinin her bir elemannn deeri a dizisinin e indisli elemanna atanyor. Dizilerin kopyalanmas iin baka bir yntem de bir standart C ilevi olan memcpy ilevini kullanmaktr. Bu ilev "gstericiler" konusunda ele alnacak.

Ayn trden nesneler bir dizi altnda tanmlanrlarsa, bir dng deyimi yardmyla dizi elemanlarnn tamamn ileme sokan kodlar kolay bir biimde yazlabilir. Aadaki rnei dikkatle inceleyin:

Dizilerin Kullanmna likin rnekler

204

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE int main() { int a[SIZE]; int toplam = 0; int k; srand(time(0)); for (k = 0; k < SIZE; ++k) { a[k] = rand() % 100; printf("%d ", a[k]); } for (k = 0; k < SIZE; ++k) toplam += a[k]; printf("\na elemanlari toplami = %d\n", toplam); } return 0; 10

main ilevinde yer alan ilk for dng deyimiyle a dizisinin elemanlarna standart rand ilevi arlaryla 0 99 aralnda rastgele deerler atanyor. Yine ayn dng iinde dizinin her bir elemannn deeri ekrana yazdrlyor. Bunu izleyen ikinci for deyimiyle a dizisinin her bir elemannn deeri srasyla toplam isimli deikene katlyor. Aadaki programda ise int trden bir dizinin en kk deere sahip olan elamannn deeri bulunuyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE int main() { int a[SIZE]; int toplam = 0; int k, min; srand(time(0)); for (k = 0; k < SIZE; ++k) { a[k] = rand() % 100; printf("%d ", a[k]); } min = a[0]; for (k = 1; k < SIZE; ++k) if (min > a[k]) min = a[k]; printf("\nen kucuk eleman = %d\n", min); return 0; } Algoritmay biliyorsunuz. min isimli deiken, dizinin en kk elemannn deerini tutmas iin tanmlanyor. nce dizinin ilk elemannn dizinin en kk eleman olduu var saylyor. Daha sonra bir for dng deyimiyle dizinin 1 indisli elemanndan balanarak 10

205

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

dizinin dier elemanlarnn deerlerinin min deikeninin deerinden daha kk olup olmad snanyor. Eer dizinin herhangi bir elemann deeri min deikeninin deerinden daha kk ise min deikeninin deeri deitiriliyor ve yeni bulunan elemann deeri min deikenine atanyor. Dng knda artk min deikeni, dizinin en kk elemannn deerini tutar, deil mi? Aadaki programda ise int trden bir dizinin tek ve ift say olan elemanlarnn aritmetik ortalamalar ayr ayr hesaplanyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int sum_of_odds = 0; int sum_of_even = 0; int no_of_odds = 0; int k; for (k = 0; k < SIZE; ++k) if (a[k] % 2) { sum_of_odds += a[k]; no_of_odds++; } else sum_of_even += a[k]; if (no_of_odds) printf("Teklerin ortalamasi = %lf\n",(double)sum_of_odds /no_of_odds); else printf("Dizide tek sayi yok!\n"); if (SIZE - no_of_odds) printf("Ciftlerin ortalamasi = %lf\n",(double)sum_of_even /(SIZE no_of_odds)); else printf("Dizide cift sayi yok!\n"); return 0; } Aadaki programda bir dizi iinde arama yaplyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int k; int searched_val; printf("aranacak degeri girin : "); scanf("%d", &searched_val); for (k = 0; k < SIZE; ++k) if (a[k] == searched_val) break;

206

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (k < SIZE) printf("a[%d] = %d\n", k, a[k]); else printf("aranan deger dizide yok!\n"); return 0; } searched_val isimli deiken, dizide aranacak deeri tutmak iin tanmlanyor. Bu deikenin deeri standart scanf ileviyle klavyeden alnyor. Daha sonra oluturulan bir for dngsyle, dizinin her bir elemannn aranan deere eitlii dng gvdesinde yer alan bir if deyimiyle snanyor. Eer dizinin herhangi bir eleman aranan deere eit ise break deyimi ile dngden klyor. Dng knda eer dng deikeni olan k, SIZE deerinden kk ise aranan deer bulunmu yani dngden break deyimi ile klmtr. Dngden break deyimi ile klmamsa k deikenin deeri SIZE deerine eit olur, deil mi? Dizinin elemanlar dizinin iinde srasz yer alyorsa, bir deerin dizide bulunmad sonucunu karmak iin dizinin tm elemanlarna baklmaldr. Ancak dizi sral ise binary search ismi verilen bir algoritmann kullanlmas arama ileminin ok daha verimli yaplmasn salar: #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 100

int main() { int a[SIZE]; int k, mid, searched_val; int val = 1; int low = 0; int high = 1; srand(time(0)); for (k = 0; k < SIZE; ++k) { a[k] = val; val += rand() % 10; printf("%d ", a[k]); } printf("\naranacak degeri girin : "); scanf("%d", &searched_val); while (low <= high) { mid = (low + high) / 2; if (a[mid] == searched_val) break; if (a[mid] > searched_val) high = mid - 1; else low = mid + 1; } if (low > high) printf("%d degeri dizide bulunamadi!\n", searched_val); else printf("a[%d] = %d\n", mid, searched_val); } return 0;

207

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki main ilevinde sral bir dizinin iinde arama yapmak amacyla binary search isimli algoritma kullanlyor. Dizi sralanm olduuna gre, dizinin ortadaki elemanna baklmasyla, dizideki elemanlarn yars artk sorgulama d braklr, deil mi? low deikeni arama yaplacak dizi parasnn en dk indisini, high deikeni ise en byk indisini tutuyor. Daha sonra low deeri, high deerinden kk ya da eit olduu srece dnen bir while dngs oluturulduunu gryorsunuz. mid deikeni arama yaplacak dizi parasnn ortadaki elemannn indisini tutuyor. Dizinin mid indisli elemannn aranan deer olup olmadna baklyor. Aranan deer bulunamamsa iki olaslk vardr: mid indisli dizi eleman aranan deerden byk ise high deikeninin deeri mid - 1 yaplyor. Bylece arama yaplacak dizi boyutu yarya drlyor. mid indisli dizi eleman aranan deerden kk ise low deikeninin deeri mid + 1 yaplyor. Bylece yine arama yaplacak dizi boyutu yarya drlyor. while dngs knda eer low deeri high deerinden bykse aranan deer bulunamam demektir. Aksi halde dizinin mid indisli eleman aranan deerdir.

Dizinin elemanlarn kkten bye ya da bykten ke sralamak iin farkl algoritmalar kullanlabilir. Aada, algsal karmakl ok yksek olmayan "kabarck sralamas" (bubble sort) isimli algoritma ile bir dizi sralanyor: #include <stdio.h> #define SIZE 10

Dizilerin Sralanmas

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int i, k, temp; for (i = 0; i < SIZE - 1; ++i) for (k = 0; k < SIZE -1 - i; ++k) if (a[k] > a[k + 1]) { temp = a[k]; a[k] = a[k + 1]; a[k + 1] = temp; } for (k = 0; k < SIZE; ++k) printf("%d ", a[k]); } return 0;

Ayn algoritma bir do while dngs kullanlarak da kodlanabilirdi: #include <stdio.h> #define SIZE #define UNSORTED #define SORTED 10 0 1

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

208

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} } while (flag == UNSORTED);

temp = a[k]; a[k] = a[k + 1]; a[k + 1] = temp; flag = UNSORTED;

for (i = 0; i < SIZE; ++i) printf("a[%d] = %d\n", i, a[i]); return 0; } Aadaki programda bir dizinin elemanlar kkten bye "araya sokma" (insertion sort) algoritmasyla sraya diziliyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int i, k, temp; for (i = 1; i < SIZE; ++i) { temp = a[i]; for (k = i; k > 0 && a[k - 1] > temp; --k) a[k] = a[k - 1]; a[k] = temp; } for (i = 0; i < SIZE; ++i) printf("%d ", a[i]); } return 0;

Sralama ynn kkten bye yapmak yerine bykten ke yapmak iin iteki dngy aadaki gibi deitirmek yeterli olur. for (k = i; k > 0 && dizi[k - 1] < temp; --k) Aadaki programda ise diziyi kkten bye sralamak iin "seme sralamas" (selection sort) algoritmas kullanlyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int i, k, min, index; for (k = 0; k < SIZE; ++k) { min = a[k]; index = k; for (i = k + 1; i < SIZE; ++i) if (a[i] < min) {

209

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} a[index] = a[k]; a[k] = min; }

min = a[i]; index = i;

for (k = 0; k < SIZE; ++k) printf("%d ", a[k]); return 0; } Aadaki programda dizinin deeri tek olan elemanlar kkten bye olacak ekilde dizinin bana, dizinin ift olan elemanlar ise kkten bye dizinin sonuna yerletiriliyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int i, k, temp; for (i = 0; i < SIZE - 1; ++i) for (k = 0; k < SIZE - 1 - i; ++k) if (a[k] % 2 == a[k + 1] % 2 && a[k] > a[k + 1] || a[k] % 2 == 0 && a[k + 1] % 2 != 0) { temp = a[k]; a[k] = a[k + 1]; a[k + 1] = temp; } for (k = 0; k < SIZE; ++k) printf("%d ", a[k]); } return 0;

Amac gerekletirmek iin yine "kabarck sralamas" algoritmasnn kullanldn, ancak takas yapmak iin snanan koul ifadesinin deitirildiini fark ettiniz mi? Aadaki programda bir dizi ters evriliyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 1, 7, 9, 12, 4, 8, 19, 10}; int k; for (k = 0; k < SIZE / 2; ++k) { int temp = a[k]; a[k] = a[SIZE - 1 - k]; a[SIZE - 1 - k] = temp; } for (k = 0; k < SIZE; ++k) 210

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("%d ", a[k]); } return 0;

Dizinin ters evrilmesi iin dizi boyutunun yars kadar dnen bir dng iinde, dizinin batan n. eleman ile sondan n. eleman takas ediliyor. Aada tanmlanan urand isimli ilev her arldnda 0 - MAX deerleri arasnda farkl bir rastgele say retiyor. levin MAX adet tamsayy rettikten sonra arldnda hata durumunu bildirmek iin -1 deerine geri dnyor: #include <stdio.h> #define MAX 100

int flags[MAX] = {0}; int urand(void) { int k, val; for (k = 0; k < MAX; ++k) if (flags[k] == 0) break; if (k == MAX) return -1; while (flags[val = rand() % MAX]) ; ++flags[val]; } return val;

int main() { int k; srand(time(0)); for (k = 0; k < MAX; ++k) printf("%d ", urand()); printf("\n\n%d\n", urand()); } return 0;

lev, bir tamsaynn daha nce retilip retilmediini anlayabilmek iin flags isimli bir global bayrak dizisini kullanyor. flags dizisinin bir elemannn deeri 0 ise o indise karlk gelen deerin ilev tarafndan henz retilmedii anlalyor. Dizi elemannn deeri eer 1 ise, o deerin daha nce retildii anlalyor. while (flags[val = rand() % MAX]) ; dngsnden flags dizisinin val deikenine atanan rastgele indisli bir elemannn deeri sfr olduunda klr, deil mi?

211

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki program, rastgele retilen bir saysal loto kuponunu ekrana yazyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define KOLON_SAYISI 8

void kolon_yaz() { int numaralar[50] = {0}; int k, no; for (k = 0; k < 6; ++k) { while (numaralar[no = rand() % 49 + 1]) ; numaralar[no]++; } for (k = 1; k < 50; ++k) if (numaralar[k]) printf("%2d ", k); } int main() { int k; srand(time(0)); for (k = 0; k < KOLON_SAYISI; ++k) { printf("kolon %2d : ", k + 1); kolon_yaz(); printf("\n"); } return 0;

kolon_yaz isimli ilev, tek bir kolonu ekrana yazdryor. levde numaralar isimli yerel dizinin, yine bir bayrak dizisi olarak kullanldn gryorsunuz. Dizinin herhangi bir indisli elemannn deerinin 0 olmas, o indis deerinin daha nce retilmeyen bir say olduunu gsteriyor. for dngs iinde yer alan while dngs, daha nce retilmeyen bir say bulununcaya kadar dnyor. Bylece 6 kez dnen for dngsyle 6 farkl say retilmi oluyor. Aadaki programda bir dizinin en byk ikinci elemannn deeri bulunuyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {12, 34, 3, 56, 2, 23, 7, 18, 91, 4}; int k; int max1 = a[0]; int max2 = a[1]; if (a[1] > a[0]) { max1 = a[1]; max2 = a[0]; }

212

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

for (k = 2; k < SIZE; ++k) if (a[k] > max1) { max2 = max1; max1 = a[k]; } else if (a[k] > max2) max2 = a[k]; printf("en buyuk ikinci deger = %d\n", max2); return 0; } Aadaki programda yalnzca bir dizinin iinde tek (unique) olan elemanlarn deerleri ekrana yazdrlyor. #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 100

int main() { int a[SIZE]; int i, k; int counter; srand(time(0)); for (k = 0; k < SIZE; ++k) { a[k] = rand() % 30; printf("%d ", a[k]); } printf("\n*******************************************************\n"); for (i = 0; i < SIZE; ++i) { counter = 0; for (k = 0; k < SIZE; ++k) if (a[k] == a[i]) if (++counter == 2) break; if (counter == 1) printf("%d ", a[i]); } printf("\n"); } return 0;

Aadaki program SIZE elemanl bir dizinin tm elemanlarna 0 - MAX aralnda birbirinden farkl rastgele deerler yerletiriyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE #define MAX 50 100

213

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int main() { int a[SIZE]; int k; srand(time(0)); for (k = 0; k < SIZE; ++k) { int val; while (1) { int i; val = rand() % MAX; for (i = 0; i < k; ++i) if (val == a[i]) break; if (i == k) break; } a[k] = val; } /* dizi yazdrlyor */ for (k = 0; k < SIZE; ++k) printf("%d ", a[k]); printf("\n"); return 0; } Aadaki programda sral iki dizi, bir sral dizi biiminde birletiriliyor: #include <stdio.h> #define SIZE 10

int main() { int a[SIZE] = {2, 3, 6, 7, 8, 9, 13, 45, 78, 79}; int b[SIZE] = {1, 2, 4, 5, 7, 9, 10, 18, 33, 47}; int c[SIZE + SIZE]; int k; int index1 = 0, index2 = 0; for (k = 0; k < SIZE + SIZE; ++k) if (index1 == SIZE) c[k] = b[index2++]; else if (index2 == SIZE) c[k] = a[index1++]; else { if (a[index1] < b[index2]) c[k] = a[index1++]; else c[k] = b[index2++]; } for (k = 0; k < SIZE + SIZE; ++k) printf("%d ", c[k]); } return 0;

214

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Karakter dizileri, char trden dizilerdir. Karakter dizilerinin, baz ek zellikleri dnda, dier dizi trlerinden bir fark yoktur. char trden diziler, daha ok, ilerinde yaz tutmak iin tanmlanr. char str[100]; Yukardaki tanmlamada str dizisi, btn elemanlar char trden olan 100 elemanl bir dizidir. char trden bir dizi iinde bir yaz tutmak, dizinin her bir elemanna srayla yaznn bir karakterinin sra numarasn atamak anlamna gelir. Bu arada char trden bir dizinin iinde bir yaz tutmann zorunlu olmadn, byle bir dizi pekala kk tamsaylar tutmak amacyla da kullanlabilir. Yukarda tanmlanan dizi iinde "Ali" yazs tutulmak istensin: str[0] = 'A'; str[1] = 'l'; str[2] = 'i'; Dizi 100 karakterlik olmasna karn dizi iinde 100 karakterden daha ksa olan yazlar da tutulabilir. Peki dizi iinde saklanan yazya nasl eriilebilir? Yaznn uzunluk bilgisi bilinmiyor. rnein yaz ekrana yazdrlmak istendiinde, int trden diziler iin daha nce yazlan aadaki gibi bir dng deyiminin kullanldn dnelim: for (k = 0; k < 100; ++k) putchar(s[k]); Byle bir dng ile yalnzca Ali yazs ekrana yazdrlmaz, dizinin dier 97 elemannn da grntleri, yani p deerler ekrana yazdrlr, deil mi? C dilinde karakterler zerinde ilemlerin hzl ve etkin bir biimde yaplabilmesi iin "sonlandrc karakter" (null character) kavramndan faydalanlr. Sonlandrc karakter, ASCII tablosunun ya da sistemde kullanlan karakter setinin sfr numaral ('\x0' ya da '\0') karakteridir. Dolaysyla saysal deer olarak 0 saysna eittir. Grnts yoktur. Sonlandrc karakter '0' karakteri ile kartrlmamaldr. '0' karakterinin ASCII karakter setindeki kod numaras 48'dir. Dolaysyla tamsay olarak deeri 48'dir. Oysa '\0' karakterinin ASCII sra numaras 0'dr. Dolaysyla tamsay olarak deeri 0'dr. Aadaki program derleyerek altrn: #include <stdio.h> int main() { printf("%d\n", '0'); printf("%d\n", '\0'); } return 0;

char Trden Diziler ve Yazlar

Yukardaki ilk printf ilevinin arlmasyla ekrana 48 deeri yazdrlrken, ikinci printf ilevinin arlmasyla ekrana 0 deeri yazdrlr.

char Trden Dizilere lkdeer Verilmesi

char trden dizilere ilkdeer verme ilemi (initializing) aadaki biimlerde yaplabilir: Dier trden dizilerde olduu gibi virgllerle ayrlan ilkdeerler, kme ayrac iinde yer alr:

215

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char name[7] = {'N', 'e', 'c', 'a', 't', 'i', '\0'}; Dier dizilerde olduu gibi dizi eleman saysndan daha fazla sayda elemana ilkdeer vermek geersizdir. char name[5] = {'N', 'e', 'c', 'a', 't', 'i'}; /* Geersiz */

Dizi eleman says kadar elemana ya da dizi eleman saysndan daha az sayda elemana ilkdeer verilebilir. Daha az sayda elemana ilkdeer verilmesi durumunda ilkdeer verilmemi elemanlar, dier dizilerde olduu gibi, 0 deeriyle balatlr. 0 deerinin sonlandrc karakter olduunu biliyorsunuz: char name[5] = {'A', 'l', 'i'};

Dizi elemanlarna ilkdeer verilirken dizi boyutu belirtilmeyebilir. Bu durumda derleyici dizi boyutunu verilen ilkdeerleri sayarak saptar. Derleyici diziyi bu boyutta alm varsayar. char name[ ] = {'A', 'l', 'i'}; Yukardaki tanmlama deyimiyle derleyici, name dizisinin 3 elemanl olarak aldn varsayar. char trden dizilerin tanmlanmasnda dizinin boyut deeri yazlmadan, dizinin elemanlarna virgllerle ayrlm deerlerle ilkdeer verilmesi durumunda, derleyici sonlandrc karakteri dizinin sonuna otomatik olarak yerletirmez. Bu durumda yaznn sonunda bulunmas gereken sonlandrc karakter ilkdeer olarak listede bulunmak zorundadr: char name[] = {'A', 'l', 'i', '\0'}; Ayn durum dizinin boyutu ile verilen ilkdeerlerin saysn ayn olduunda da geerlidir: char isim[7] = {'N', 'e', 'c', 'a', 't', 'i', '\0'}; Bu ekilde ilkdeer vermek zahmetli olduundan, ilkdeer vermede ikinci bir biim oluturulmutur. Karakter dizilerine ilkdeerler ift trnak iinde de verilebilir: char name[] = "Ali"; Bu biimin dierinden fark, derleyicinin sonuna otomatik olarak sonlandrc karakteri yerletirmesidir.

216

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rnekte derleyici, name dizisini 4 elemanl olarak alm varsayar. Dizinin eleman saysndan daha fazla sayda elemana ilkdeer vermek geersizdir. char city[5] = "stanbul"; /* Geersiz */

Bu durumun bir istisnas vardr. Eer dizinin eleman says kadar elemana ift trnak iinde ilkdeer verilirse bu durum geerlidir. Derleyici bu durumda sonlandrc karakteri dizinin sonuna yerletirmez. [Bu durum C dili standartlarna yneltilen eletirilerden biridir. C++ dilinde bu durum szdizim hatasdr.] char name[3] = "Ali"; /* C'de geerli, C++'da geersiz */

'\0' karakteri, karakter dizileri zerinde yaplan ilemleri hzlandrmak iin kullanlr. rnein int trden bir dizi kullanldnda, dizi elemanlar zerinde dngleri kullanarak ilem yaparken dizi uzunluunun mutlaka bilinmesi gerekir. Ama char trden diziler sz konusu olduunda artk dizi uzunluunu bilmek gerekmez, nk yaznn sonunda '\0' karakter bulunacandan, kontrol ifadeleriyle bu durum snanarak yaznn sonuna gelinip gelinmedii anlalabilir. Ancak '\0' karakterin, karakter dizilerinde yazlarn son eleman olarak kullanlmasnn bir zarar da diziye fazladan bir karakter, yani '\0' karakteri eklemek zorunluluudur. Bu nedenle SIZE elemanl bir dizide en fazla SIZE 1 uzunluunda bir yaz saklanabilir. C dilinde bir yazy klavyeden alan ya da bir yazy ekrana yazan standart ilevler bulunur.

Daha nce ele alnan getchar, getch ve getche ilevleri klavyeden tek bir karakter alyorlard. gets, klavyeden karakter dizisi almakta kullanlan standart bir C ilevidir. Kullanc, klavyeden karakterleri girdikten sonra enter tuuna basmaldr. lev, klavyeden girilecek karakterlerin yerletirilecei dizinin ismini parametre olarak alr. Daha nce de belirtildii gibi dizi isimleri aslnda bir adres bilgisi belirtir. gets ilevinin de parametresi aslnda char trden bir adrestir. Ancak gstericilerle ilgili temel kavramlar henz anlatlmad iin imdilik gets ilevini yalnzca iinize yarayacak kadar reneceksiniz. rnein : char s[20]; gets(s); ile klavyeden enter tuuna baslana kadar girilmi olan tm karakterler, name dizisi iine srayla yerletirilir. Klavyeden "Necati" yazsnn girildiini varsayalm: gets ilevi, klavyeden girilen karakterleri diziye yerletirdikten sonra dizinin sonuna sonlandrc karakteri yerletirir.

gets ilevi

217

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

gets ilevi, dizi iin hibir ekilde dizinin tamasna ynelik bir kontrol yapmaz. gets ilevi ile dizi eleman saysndan daha fazla karakter girilirse, dizi taaca iin beklenmeyen sonularla karlalabilir. Bu tr durumlar gstericiler konusunda "gsterici hatalar" bal altnda ayrntl olarak inceleyeceiz. gets ilevi '\0' karakterini dizinin sonuna ekledii iin, SIZE boyutunda bir dizi iin gets ileviyle alnacak karakter says en fazla SIZE 1 olmaldr. nk sonlandrc karakter de dier karakterler gibi bellekte bir yer kaplar. rnek : char isim[6]; gets(isim); ile klavyeden Necati isminin girildiini dnelim: isim dizisinin tanmlanmasyla derleyici bu dizi iin bellekte 6 byte yer ayrr (isim[0] ...isim[5]).

gets ilevi bu durumda '\0' karakterini, derleyicinin dizi iin ayrmad bir bellek hcresine yazar. Bu tr durumlara "dizinin bir tarlmas hatas" (off bye one) denir. Tama durumuyla ilgili olarak ortaya kacak hatalar, derleme zamanna deil alma zamanna (run time) ilikindir. Aadaki program inceleyin: #include #define <stdio.h> SIZE 100

int main() { char str[SIZE]; int ch; int index = 0; printf("bir yaz girin: "); printf("\n\n"); while ((ch = getchar()) != '\n') str[index++] = ch; str[index] = '\0'; return 0; } Yukardaki main ilevinde klavyeden alnan bir yaz str dizisi iinde saklanyor. Klavyeden '\n' karakteri alnana kadar, girilen tm karakterler str dizisinin elemanlarna srayla atanyor. Klavyeden '\n' karakteri alndnda, diziye yazlan son karakterden sonra, dizideki yaznn sonunu iaretlemesi amacyla '\0' karakter yazlyor. Klavyeden alnan bir yaz, char trden bir dizinin iine standart scanf ileviyle de yerletirilebilir. Bu amala %s format karakterleri kullanlr. Ancak bu durumda klavyeden

218

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

girilen karakterlerin hepsi diziye yerletirilmez. Klavyeden alnan ilk boluk karakteri ile diziye yerletirme ilemi sona erer. Aadaki program inceleyin: #include <stdio.h> int main() { char name[20]; char fname[30]; int no; printf("isim soyisim ve numara girin : "); scanf("%s%s%d", name, fname, &no); /***/ return 0; } Programn alma zamannda scanf ilevi arldnda aadaki giriin yapldn dnelim. ('_' karakteri boluk karakterini gsteriyor): __Necati___Ergin___564 Bu durumda Necati yazs name dizisine, Ergin yazs fname dizisine, 564 tamsay deeri ise no isimli deikene yerletirilir.

puts, standart bir C ilevidir. Bu ilev, bir karakter dizisinde tutulan yazy ekrana yazdrmak iin kullanlr. Yazy saklayan karakter dizisinin ismini (dizi ismi derleyici tarafndan otomatik olarak dizinin balang adresine dntrlmektedir) parametre olarak alr. puts ilevi, karakter dizisini ekrana yazdktan sonra imleci sonraki satrn bana geirir: #include <stdio.h> int main() { char name[20]; printf("bir isim girin : "); gets(name); puts(name); return 0;

puts ilevi

}
Yukardaki rnekte gets ilevi ile klavyeden alnan yaz, puts ilevi ile ekrana yazdrlyor. Karakter dizileri iinde tutulan yazlar ekrana yazdrmak iin, standart printf ilevi de kullanlabilir. Bu durumda formatlama karakterleri olarak %s kullanlarak dizinin ismi (dizinin balang adresi) ile elenir. printf("%s\n", name); ile puts(name);

219

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ayn ii yapar. Ancak printf ilevi, dizi iinde tutulan yazy ekrana yazdrdktan sonra imleci alt satra tamaz. puts(name); deyimi yerine aadaki kod paras da yazlabilirdi: for (i = 0; name[i] != '\0'; ++i) putchar(name[i]); putchar('\n'); puts ilevi ve %s format karakteriyle kullanldnda printf ilevi, sonlandrc karakter grene kadar btn karakterleri ekrana yazar. Bu durumda, yaznn sonundaki sonlandrc karakter herhangi bir ekilde ezilirse her iki ilev de ilk sonlandrc karakteri grene kadar yazma ilemini srdrr. Aadaki program inceleyin: #include <stdio.h> int main() { char city[] = "Ankara"; city[6] = '!'; puts(city); } return 0;

city[6] = '!'; atamasyla Ankara yazsnn sonundaki sonlandrc karakter ezilerek zerine ! karakteri yazlyor. Daha sonra arlan puts ilevi ekrana Ankara! yazsn yazdktan sonra ilk sonlandrc karakteri grene kadar ekrana yazmay srdrr. Gstericiler konusunda bu durumun bir gsterici hatas oluturduunu greceksiniz. puts ve printf ilevleri, karakter dizilerini yazarken yalnzca sonlandrc karakteri dikkate alr. Bu ilevler karakter dizilerinin uzunluklaryla ilgilenmez.

Aadaki programda bir karakter dizisi iinde tutulan yaznn uzunluu bulunuyor: #include <stdio.h> #define SIZE 100

Karakter Dizileriyle lgili Baz Kk Uygulamalar

int main() { char str[SIZE]; int k; int len = 0; printf ("bir yazi girin : "); gets(str); printf("yazi = (%s)\n", str); for (k = 0; str[k] != '\0'; ++k) len++; printf("(%s) yazisinin uzunlugu = %d\n", str, len);

220

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

return 0;

Programda yer alan for (k = 0; str[k] != '\0'; ++k) len++; dng deyiminin yrtlmesinden sonra, dng deikeni olan k'nn deeri de yaznn uznluu olur, deil mi? Aadaki programda char trden bir dizi iine alnan yaz, ekrana tersten yazdrlyor: #include <stdio.h> #define SIZE 100

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

Aadaki programda nce bir karakter dizisine bir yaz alnyor. Daha sonra yaznn kk harf karakterleri byk harfe, byk harf karakterleri kk harfe dntrlyor: #include <stdio.h> #include <ctype.h> #define SIZE 100

int main() { char str[SIZE]; int k; printf ("bir yazi girin : "); gets(str); printf("yazi = (%s)\n", str); for (k = 0; str[k] != '\0'; ++k) str[k] = isupper(str[k]) ? tolower(str[k]) : toupper(str[k]); printf("donustulmus yazi = (%s)\n", str); return 0; } Aadaki programda bir karakter dizisine klavyeden alnan yaz ters evriliyor: 221

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #define SIZE 100

int main() { char str[SIZE]; int k, temp, len; printf ("bir yazi girin : "); gets(str); for (len = 0; str[len] != '\0'; ++len) ; for (k = 0; k < len / 2; ++k) { temp = str[k]; str[k] = str[len - 1 - k]; str[len - 1 - k] = temp; } printf("ters cevrilmis yazi = (%s)\n", str); return 0;

Yukardaki kodda kullanlan algoritmay inceleyin. Birinci for dngs ile yaznn uzunluu bulunuyor. Daha sonra yaznn uzunluunun yars kadar dnen bir for dng deyimi oluturuluyor. Dngnn her turunda yaznn batan n. karakteri ile sondan n. karakteri yer deitiriliyor. Yaz uzunluu tek say ise, yaznn ortasndaki karakter yerinde kalr. Yaznn sonundaki sonlandrc karakter str[len] olduuna gre, yaznn son karakteri str[len 1] karakteridir, deil mi? Aadaki programda ise klavyeden girilen bir yaznn iinde bulunan tm ngilizce harfler saylyor ve ka tane olduklar ekrana yazdrlyor: #include <stdio.h> #include <ctype.h> #define SIZE 500

int main() { char str[SIZE]; int letter_counter[26] = {0}; int k; printf("bir yazi girin : "); gets(str); for (k = 0; str[k] != '\0'; ++k) if (isalpha(str[k])) letter_counter[toupper(str[k]) - 'A']++; for (k = 0; k < 26; ++k) if (letter_counter[k]) printf("%3d tane %c\n", letter_counter[k], 'A' + k); return 0;

222

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

main ilevi iinde kullanlan letter_counter isimli dizi, bir saya dizisi olarak kullanlyor. Dizinin 1. eleman 'A', 'a' karakterlerinin, dizinin 2. eleman 'B', 'b' karakterlerinin, dizinin sonuncu eleman 'Z', 'z' karakterlerinin sayac olarak grev yapyor. Bu yerel dizi, ilkdeer verme deyimiyle sfrlanyor. lk for dng deyimiyle, yaznn tm karakterleri dolalyor, yaznn herhangi bir karakteri eer bir harf karakteri ise, byk harfe dntrlerek bu karakterden 'A' deeri karlyor. Elde edilen deerin letter_counter dizisine indis yapldn ve letter_counter dizisinin bu indisli elemannn deerinin 1 artrldn gryorsunuz. kinci for dng deyimiyle ise bu kez saya dizisinin, deeri 0 olmayan elemanlarnn deerleri ekrana yazdrlyor. Aadaki programda ise bir diziye alnan bir yaz iinden rakam karakterleri siliniyor. Kodu inceleyin: #include <stdio.h> #include <ctype.h> #define SIZE 500

int main() { char str[SIZE]; int k; int index = 0; printf("bir yazi girin : "); gets(str); printf("yazi = (%s)\n", str); for (k = 0; str[k] != '\0'; ++k) if (!isdigit[k]) str[index++] = str[k]; str[index] = '\0'; printf("yazi = (%s)\n", str); } return 0;

Yazdan rakam karakterlerini silmek iin yaz, bulunduu yere yeniden kopyalanyor. Ancak kopyalama yaplrken, rakam karakterleri kopyalanmyor. index isimli deiken, dizinin neresine yazlacan gsteriyor. Eer bir karakter rakam karakteri deilse, bu karakter dizinin index indisli elemanna atanyor, sonra index deikeninin deeri 1 artrlyor. Ancak yaznn tamamn dolaan for dngsnden ktktan sonra, silme ileminden sonra oluan yaznn sonuna, sonlandrc karakter ekleniyor. Aadaki programda bir yaznn toplam szck says bulunuyor: #include <stdio.h> #define #define #define SIZE OUTWORD INWORD 200 0 1

int is_sep(int ch); int main()

223

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

{ char str[SIZE]; int word_counter = 0; int k; int word_flag = OUTWORD; printf("bir yazi girin : "); gets(str); for (k = 0; str[k] != '\0'; ++k) if (is_sep(str[k])) word_flag = OUTWORD; else if (word_flag == OUTWORD) { word_flag = INWORD; word_counter++; } printf("toplam %d sozcuk var!\n", word_counter); } return 0;

int is_sep(int ch) { char seps[] = " \t.,;:?!"; int k; for (k = 0; seps[k] != '\0'; ++k) if (ch == seps[k]) return 1; return 0; } is_sep ilevi, sra numarasn ald bir karakterin, szckleri birbirinden ayran ayra karakterlerinden biri olup olmadn snyor. main ilevi iinde tanmlanan word_flag isimli bayrak deikeni, bir szcn iinde mi dnda m olunduunu gsteriyor. Bu deikene ilkdeer olarak, kelimenin dnda (OUT) deerinin verildiini gryorsunuz. Bir for dng deyimiyle yaznn her bir karakterinin ayra karakteri olup olmad snanyor. Eer ayra karakteri ise word_flag deikenine OUT deeri atanyor. Eer karakter ayra karakteri deilse ve ayn zamanda bayran deeri OUT ise, bayraa IN deeri atanyor ve szck saysn tutan sayacn deeri 1 artrlyor. Aadaki programda, bir yaznn iinde ardk olarak yer alan e karakterlerin says bire indiriliyor: #include <stdio.h> #define SIZE 100

int main() { char str[SIZE]; int index = 0; int k; printf("bir yazi girin : "); gets(str); for (k = 0; str[k] != '\0'; ++k)

224

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (str[k] != str[k + 1]) str[++index] = str[k + 1]; printf("yazi = (%s)\n", str); return 0; }

225

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sizeof, bir ifadenin trnn bellekte ka byte yer kaplad deerini reten bir iletir. sizeof ileci tek terimli nek konumunda bir iletir. sizeof ilecinin terimi aadakilerden biri olabilir: 1. Terim olarak tr belirten szckler kullanlabilir. Bu durumda terimin ayra iine alnmas zorunludur. rnekler: sizeof(int) sizeof(double) sizeof(long) le bu durumda terimi olan tr bilgisinin kullanlan sistemde ka byte yer kaplayaca deerini retir. rnein Windows ya da UNIX sistemlerinde sizeof(int) gibi bir ifadenin deeri 4'tr. 2. Terim olarak bir ifade kullanlabilir. Bu durumda terimin ayra iine alnmas zorunlu deildir. Ancak programclarn ou okunabilirlik asndan terimi ayra iine almay yeler: double x; sizeof (x) sizeof(17.8) sizeof(func()) le bu durumda, terimi olan ifadenin ait olduu trn, kullanlan sistemde ka byte yer kaplayaca deerini retir. rnein Windows ya da UNIX sistemlerinde sizeof(x) gibi bir ifadenin deeri 8'dir. Byle bir ifade doal olarak, ilgili sistemde x nesnesinin bellekte ka byte yer kapladn belirlemekte de kullanlabilir. sizeof ileci en ok bu biimiyle kullanlr. Yani ilecin terimi nesne gsteren bir ifade seilerek, terimi olan nesnenin bellekte ka byte yer kaplad renilir. 3. sizeof ileci terim olarak bir dizi ismi aldnda, byte olarak o dizinin toplam uzunluunu deer olarak retir: double a[10]; sizeof(a) ifadesi 80 deerini retir. Dier taraftan sizeof ilecinin terim olarak dizinin bir elemann alarak rettii deer, dizi hangi trden ise o trn kullanlan sistemdeki byte olarak uzunluu olur. Yani yukardaki rnekte sizeof(a[0]) ifadesi 8 deerini retir. Bu durumda

sizeof leci

226

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sizeof(a) / sizeof(a[0]) ifadesi dizi boyutunu verir. rnek: for (i = 0; i < sizeof(a) / sizeof(a[0]); ++i) a[i] = 0; a bir dizi ismi olmak zere, yukardaki dng, a dizisinin eleman says kadar dner. Yukardaki dngde dizi boyutunun ak biimde yazlmas yerine sizeof(a) / sizeof(a[0] biiminde yazlmas size artc gelebilir. Byle bir yazm biiminin bir faydas olabilir mi? Dizi tanmlamalarnda, ilkdeer verilen dizilerin boyutlarnn belirtilmesine gerek olmadn, derleyicinin dizi boyutunu verilen ilkdeerlerin saysndan kardn biliyorsunuz. Aadaki kodu inceleyin: #include <stdio.h> int main() { int a[] = {2, 5, 7, 8, 9, 23, 67}; int k; for (k = 0; k < sizeof(a) / sizeof(a[0]); ++k) printf("%d ", a[k]); printf("\n"); } return 0;

Yukardaki main ilevi iinde a isimli int trden bir dizi tanmlanyor. Tanmlanan diziye ilkdeer veriliyor. Derleyici verilen ilkdeerlerin saysn sayarak dizinin boyutunu 8 olarak saptar ve kodu buna gre retir. main ilevi iinde yer alan for dng deyimi, dizinin eleman says kadar, yani 8 kez dner. imdi kaynak kodda deiiklik yapldn, a dizisine birka eleman daha eklendiini dnelim: int a[] = {2, 5, 7, 8, 9, 23, 67, 34, 58, 45, 92}; Bu durumda for dng deyiminde bir deiiklik yaplmasna gerek kalmaz. nk derleyici bu kez dizinin boyutunu 11 olarak hesaplar. for dng deyimi iinde kullanlan sizeof(a) / sizeof(a[0] ifadesi de bu kez 11 deerini retir.

sizeof lecinin ncelii

Tek terimli tm ilelerin, ile ncelik tablosunun ikinci seviyesinde yer aldn biliyorsunuz. sizeof da ikinci seviyede bulunan bir iletir.

sizeof ilecinin terimi ou kez bir ayra iine yazldndan, ilecin kullanm bir ilev ars grntsne benzer: sizeof(y) Ancak sizeof bir ilev deil bir iletir. sizeof C dilinin 32 anahtar szcnden biridir. 227

sizeof Bir lev Deildir

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sizeof ilecinin rettii deer unsigned int trdendir. lecin rettii deerin trn signed int kabul etmek hataldr. lecin rettii deer, signed int trden negatif bir sayyla ileme sokulduunda tr dnm iaretsiz yne yaplr: -2 * sizeof(int) -2, iaretli int trden bir deimezdir. sizeof(int) ifadesinin rettii deer ise unsigned int trden 4 deeridir. lem ncesi yaplacak otomatik tr dnm ile -2 deeri unsigned int trne dntrlr. lem unsigned int trde yaplr. Yani ilemin sonucu 8 olmaz. [Aslnda sizeof operatrnn rettii deer standart bir typedef tr olan size_t trndendir. Standart typedef trleri "Tr simleri Bildirimleri ve typedef Belirleyicisi " isimli blmde ele alnyor.]

sizeof lecinin rettii Deerin Tr

sizeof lecinin Terimi Olan fadenin Yan Etkisi


#include <stdio.h> int func() { printf("func()\n"); } return 1;

sizeof ilecinin terimi olan ifade yan etki gstermez. Aadaki rnei inceleyin:

int main() { unsigned int x = sizeof(func()); printf("x = %u\n", x); } return 0;

main ilevi iinde, func ilevi arlmaz. sizeof ileci, terimi olan ifadeye yalnzca bir tr bilgisi olarak bakar. rnek kodda yer alan func() ifadesinin tr int trdr.

Belirli bir trden nesnenin bellekte ka byte yer kaplayaca, sistemden sisteme farkllk gsterebilir. Tr uzunluu gvence altnda bulunan tek doal tr, char trdr. char trden bir nesne tm sistemlerde 1 byte yer kaplar. Tr uzunluklarnn sistemden sisteme farkl olabilmesi, baz uygulamalarda tanabilirlik sorunlarna yol aabilir. sizeof ilecinin, genel olarak bu tr tanabilirlik sorunlarn ortadan kaldrmaya ynelik olarak kullanld sylenebilir.

sizeof leci Ne Amala Kullanlr

228

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

GSTERCLER
Birinci blmde yazlan kodlarda nesnelerin deerleri kullanld. rnein deikenlerin deerleri ilevlere argman olarak gnderildi. levler nesnelerin deerlerini geri dndrd. "Gstericiler" (pointers) ile artk nesnelerin deerlerinin yan sra nesnelerin adresleri zerinde de durulacak. Nasl, oturduunuz evin adresinden sz ediliyorsa, programda kullandnz nesnelerin de adreslerinden sz edilebilir. Bir nesnenin adresi o nesnenin bellekteki konumunu gsteren bir bilgidir. te C dilinde yazlan birok kod, nesnelerin adresi olan bilgileri kullanlr. Yazlmsal baz amalarn gerekletirilmesi iin, nesnelerin adresleri deikenlerde saklanr, ilevlere gnderilir, ilev arlaryla ilevlerden geri dn deeri olarak elde edilir. Her nesne bellekte yer kapladna gre belirli bir adrese sahiptir. Nesnelerin adresleri, sistemlerin ounda, derleyici ve program ykleyen iletim sistemi tarafndan ortaklaa belirlenir. Nesnelerin adresleri program yklenmeden nce kesin olarak bilinemez ve programc tarafndan da nceden saptanamaz. Programc nesnelerin adreslerini ancak programn almas srasnda (run time) renebilir. rnein: char ch; Gibi bir tanmlamayla karlaan derleyici bellekte ch deikeni iin 1 byte yer ayrr. Derleyicinin ch deikeni iin bellekte hangi byte' ayraca nceden bilinemez. Bu ancak programn almas srasnda renilebilir. Yukardaki rnekte ch deikeninin yerel yerel olduunu dnelim. ch deikeni, tanmlanm olduu bloun kodu yrtlmeye balandnda yaratlr, bloun kodunun yrtlmesi bittiinde de mr sona erer. Aadaki ekil ch deikeninin 1A02 adresinde olduu varsaylarak izilmitir:
1A00 1A01

ch

1A02 1A03 1A04

Tanmlanan nesne 1 byte'dan daha uzunsa, o zaman nesnenin adresi nasl belirlenir? int b;

1 byte'tan uzun olan nesnelerin adresleri, onlarn ilk byte'larnn adresleriyle belirtilir. Yukardaki rnekte b deikeninin adresi 1C02'dir. Zaten b deikeninin int trden olduu bilindiine gre dier parasnn 1C03 adresinde olaca da aktr (int trden bir nesnenin ilgili sistemde 2 byte yer kaplad varsaylyor). Benzer biimde long trden olan y deikeninin bellekteki yerleiminin aadaki gibi olduu varsaylrsa, adresinin 1F02 olduu sylenebilir:

229

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int trden bir deiken tanmlanm olsun: int x; Byle bir nesnenin adresinin yazlmsal olarak kullanlabilmesi iin, bu bilginin bir biimde ifade edilmesi, bellekte tutulmas, yorumlanmas gerekir. x deikeninin adresi olan bilginin de, bir tr olmaldr. Byle bir tr bilgisi, nesnenin kendi trnden tretilir (derived type) ve C'de aadaki biimde gsterilir: (int *) double trden bir nesnenin adresi olabilecek bir bilginin tr de (double *) olarak gsterilir. x, T trnden bir nesne olmak zere x nesnesinin adresi olan bilginin trnn (T *) tr olduu kabul edilir.

Yazlmsal baz amalar gerekletirmek iin nesnelerin adreslerinin de deikenlerde tutulmas gerekir. Nasl bir tamsay deeri tamsay trlerinden bir deikende tutuluyorsa, bir adres bilgisi de, tr bir adres bilgisi olan deikende saklanr. int x; Yukarda tanmlanan x nesnesinin tr int trdr. Byle bir nesnenin adresi olan bilgi (int *) trnden olduuna gre bu bilgi doal olarak int * trnden olan bir deikende saklanmaldr.

Gsterici Nesneler

Gsterici Deikenlerin Bildirimleri


int *ptr; Yukarda ptr isimli bir deiken tanmlanyor. ptr deikeni int trden bir nesnenin adresi olan bilgiyi tutabilecek bir deikendir. ptr deikeninin deeri, int trden bir nesnenin adresi olan bilgidir. ptr deikenine int trden bir nesnenin adresi olabilecek bilgi atanmaldr. Benzer ekilde double trden bir nesnenin adresi olan bilgiyi saklamak iin de double *dp; gibi bir deiken tanmlanabilir. Gsterici deikenler, adres bilgilerini saklamak ve adreslerle ilgili ilemler yapmak iin kullanlan nesnelerdir. Gsterici deikenlerin deerleri adrestir. Gsterici deikenlerin bildirimlerinin genel biimi yledir:

230

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

<tr> *<gsterici ismi>; <tr>, gstericinin (iindeki adresin) trdr. char, int, float... gibi herhangi bir tr olabilir. Burada * atomu tr bilgisinin bir parasdr. Aada rnek gsterici bildirimleri yer alyor: float *f; char *ps; int *dizi; unsigned long *Pdword; Gsterici bildirimleri, dier trlere ilikin bildirimlerden * atomu ile ayrlr. char s; bildiriminde s, char trden bir deiken iken char *ps; bildiriminde ps, char trden bir gstericidir, yani tr char * olan bir nesnedir. Bu deikene char trden bir nesnenin adresi atanmaldr. Byle bir bildirimden u bilgiler karlabilir: ps bir nesnedir, yani bellekte bir yer kaplar. ps nesnesi iin bellekte ayrlan yerdeki 1'ler ve 0'lar char trden bir nesnenin adresinin, saysal deeri olarak yorumlanr. Tanmlamada yer alan '*' bir ile deildir. Szdizim kural olarak nesnenin bir gsterici olduunu anlatr. Gsterici bildirimleri ile normal bildirimler bir arada yaplabilir. rnein: int *p, a; Burada p int trden bir gsterici deikendir, ama a int trden bir normal bir deikendir. Ayn trden birden fazla gstericinin bildirimi yaplacaksa, araya virgl atomu konularak, her gsterici deikenin bildirimi * atomu ile yaplmaldr. char *p1, *p2 Yukardaki bildirimde p1 ve p2 char trden gsterici deikenlerdir. double *p1, *p2, d, a[20]; Yukardaki bildirimde p1 ve p2 double trden gsterici deikenler, d double trden bir deiken ve a ise elemanlar double trden 20 elemanl bir dizidir.

Bir gsterici nesnesinin tanm ile karlaan derleyici dier tanmlamalarda yapt gibibellekte o gsterici deikeni iin yer ayrr. Derleyicilerin gstericiler iin ayrdklar yerlerin uzunluu donanma bal olup sistemden sisteme deiebilir. 32 bit sistemlerde (rnein UNIX ve Windows 3.1 sonras sistemlerde) gsterici deikenler 4 byte uzunluundadr. 8086 mimarisinde ve DOS altnda alan derleyicilerde ise gsterici deikenler 2 byte ya da 4 byte olabilirler. DOS'ta 2 byte uzunluundaki gstericilere yakn gstericiler (near pointer), 4 byte uzunluundaki gstericilere ise uzak gstericiler (far pointer) denir. Gstericilerin uzunluklar trlerinden bamszdr.

Gsterici Deikenlerin Uzunluklar

231

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki program derleyerek altrn: #include <stdio.h> int main() { char *cp, ch; int *ip, i; double *dp, d; printf("sizeof(ch) = %u\n", sizeof(ch)); printf("sizeof(i) = %u\n", sizeof(i)); printf("sizeof(d) = %u\n", sizeof(d)); printf("sizeof(cp) = %u\n", sizeof(cp)); printf("sizeof(ip) = %u\n", sizeof(ip)); printf("sizeof(dp) = %u\n", sizeof(dp)); printf("sizeof(char *) = %u\n", sizeof(char *)); printf("sizeof(int *) = %u\n", sizeof(int *)); printf("sizeof(double *) = %u\n", sizeof(double *)); } return 0;

Yukardaki programda hem char, int, double trlerinden hem de char *, int *, double * trlerinden nesnelerin tanmlandn gryorsunuz. Daha sonra printf ileviyle bu nesnelerin sizeof deerleri ekrana yazdrlyor. T trnden bir nesnenin sizeof deeri ne olursa olsun T* trnden bir nesnenin sizeof deeri hep ayndr, deil mi? Yukardaki program UNIX iletim sistemi iin derlenip altrldnda ekran kts aadaki gibi olur: sizeof(ch) = 1 sizeof(i) = 4 sizeof(d) = 8 sizeof(cp) = 4 sizeof(ip) = 4 sizeof(dp) = 4 sizeof(char *) = 4 sizeof(int *) = 4 sizeof(double *) = 4

Adres Bilgisi Olan fadeler

Baz ifadeleri adres trndendir. Yani bu ifadelerin deeri adrestir. Bir gsterici deikene, tr adres olan ifade yani bir adres deeri atanmaldr.

int *ptr; gibi tanmlanan bir deiken, iinde int trden bir deikenin adresi olan bilgiyi saklayacak deikendir. Byle bir adres bilgisi ptr deikenine nasl atanabilir?

Adres Deimezleri
1200,int trden bir tamsay deimezidir. Byle bir deimez, rnein int trden bir nesneye atanabilir: int x = 1200; Tr dntrme ilemiyle bir tamsay deimezi bir adres bilgisine dntrlebilir: (int *)1200 232

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki ifadenin tr (int *) trdr. Byle bir ifade int trden bir nesnenin adresi olabilecek bir bilgidir. int trden 1200 deimezi tr dntrme ileciyle int trden bir nesnenin adresi olabilecek bir tre dntrlmtr. Adres deimezlerinin yazmnda geleneksel olarak onaltlk say sistemi kullanlr: (double *)0x1AC4 Yukardaki ifade, double trden bir nesnenin adresi olabilecek bir bilgidir. Adres bilgisinin tamsay ksmnn yazlmasnda onaltlk say sisteminin kullanldn gryorsunuz.

Gstericilerle lgili Tr Uyumu


int *p; p = 100;

Bir gsterici deikene ayn trden bir adres bilgisi yerletirilmelidir. rnein :

Burada p gsterici deikenine adres olmayan bir deer atanyor. Byle bir atamann yanl olduu kabul edilir. Derleyicilerin hemen hepsi bu durumu mantksal bir uyar iletisi ile bildirir. Bu durum ileride ayrntl olarak ele alnacak. [Byle bir atama C++ dilinde geerli deildir. int *p; p = (char *) 0x1FC0; Burada int trden p gstericisine char trden bir adres bilgisi atanyor. Yanl olan bu durum da derleyicilerin ou tarafndan mantksal bir uyar iletisi ile iaretlenir. [C++ derleyicileri byle bir atama durumunda da szdizim hatas iletisi vererek ama dosya retimini reddeder] int *p; p = (int *) 0x1FC4; /* geerli ve uygun bir atamadr */

Bir adres bilgisi gstericiye atandnda adresin saysal bileeni gsterici iine yerletirilir. int *p; p = (int *) 0x1FC4; Burada bellekte p gsterici deikeninin tutulduu yere 0x1FC4 saysal deeri yerletirilir.

Gsterici deikenler ilerinde adres bilgileri tadna gre bir gstericiye ayn trden baka bir gsterici deikenin deerinin atanmas da tamamen uygundur. int *p, *q; p = (int *) 0x1AA0; q = p; Yukardaki atama ile q gstericisine p gstericisinin deeri atanyor. Yani bu atama deyiminden sonra q gstericisinin de iinde (int *) 0x1AA0 adresi bulunur. int k;

233

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

gibi bir tanmlama yapldnda k deikeni int trdendir. indeki deer int trden bir deer olarak yorumlanr. 20 gibi, tek bir deimezden oluan bir ifade de int trdendir, nk 20 int trden bir deimezdir. Baka bir deyile k ifadesiyle 20 ifadesinin trleri ayndr. Her iki ifadenin tr de int trdr. Ancak k ifadesi nesne gsteren bir ifade iken 20 ifadesi nesne gstermeyen bir ifadedir, yani bir sa taraf deeridir. Yine bir adres deimezinden oluan (int *) 0x1A00 ifadesinin tr de int trden bir adrestir, yani (int *) trnden bir ifadedir. Ancak bu ifade de sol taraf deeri deildir. Grld gibi gsterici deikenleri, belirli bir adres trnden nesnelerdir. Yani deerleri adres olan deikenlerdir.

234

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C dilinin baz ileleri adres bilgileri ile ilgili olarak kullanlr. Gstericiler ile ilgili kodlar bu ileleri kullanr. Gsterici ileleri unlardr: * & [] -> ierik ileci adres ileci keli ayra ileci ok ileci indirection operator (dereferencing operator) address of operator index operator (subscript operator) arrow operator

Gsterici leleri

ok ileci yap trnden adreslerle kullanld iin bu ile "yaplar" konusunda ayrntl olarak incelenecek.

Adres ileci (adress of operator), nek konumunda tek terimli (unary prefix) bir iletir. le ncelik tablosunun ikinci seviyesinde yer alr. Bu ilecin rettii deer, terimi olan nesnenin adresidir. Adres ilecinin terimi mutlaka bir nesne olmaldr. nk yalnzca nesnelerin -sol taraf deerlerinin- adres bilgilerine ulalabilir. Adres ilecinin teriminin nesne olmayan bir ifade olmas geersizdir. int k; gibi bir tanmalamadan sonra yazlan &k ifadesini ele alalm. Bu ifadenin rettii deer int trden bir adres bilgisidir. Bu ifadenin tr (int *) trdr. & ileci dier tek terimli ileler gibi, ile ncelik tablosunun 2. seviyesinde bulunur. Bu ncelik seviyesinin ncelik ynnn "sadan sola" olduunu biliyorsunuz. Bir gsterici deikeni, iinde bir adres bilgisi tutan bir nesne olduuna gre, bir gsterici deikene adres ilecinin rettii bir adres bilgisi atanabilir. int x = 20; int *ptr; ptr = &x; Byle bir atamadan sonra unlar sylenebilir: ptr nesnesinin deeri x deikeninin adresidir. ptr nesnesi x deikeninin adresini tutar. Adres ileci ile elde edilen adres, ayn trden bir gsterici deikene atanmaldr. rnein aadaki programda bir gsterici deikene farkl trden bir adres atanyor: char ch = 'x'; int *p; p = &ch; /* Yanl */

Adres leci

Tabi bu ilecin rettii adres bilgisi de sol taraf deeri deildir. rnein: int x; ++&x /* Geersiz */

gibi bir ilem hata ile sonulanr. Artrma ilecinin terimi nesne olmaldr. Yukardaki ifadede ++ ilecinin terimi olan &x ifadesi bir nesne deildir. Yalnzca bir adres deeridir.

235

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Standart printf ilevi ile doal veri trlerinden ifadelerin deerlerinin ekrana yazdrlabileceini biliyorsunuz. Bir ifadenin deerini ekrana yazdrmak iin, printf ileviyle birinci argman olarak geilen dizge iinde nceden belirlenmi format karakterlerinin (conversion specifiers) kullanldn hatrlayn. Acaba bir adres bilgisi de uygun format karakteri kullanlarak ekrana yazdrlabilir mi? Evet! Standart printf ilevinde bu ama iin %p format karakterleri kullanlr. %p format karakterleri ile elenen argman bir adres bilgisi ise, printf ilevi ilgili adres bilgisinin yalnzca saysal bileenini onaltlk say sisteminde ekrana yazdrr. Aadaki program derleyerek altrn: #include <stdio.h> int main() { int *ptr; int x = 20; ptr = &x; printf("x nesnesinin adresi = %p\n", &x); printf("ptr deikeninin deeri = %p\n", ptr); printf("ptr nesnesinin adresi = %p\n", &ptr); } return 0;

Adres Deerlerinin Ekrana Yazdrlmas

ptr bir nesne olduu iin ptr nesnesi de adres ilecinin terimi olabilir, deil mi? ptr nesnesinin deeri olan adres, x nesnesinin adresidir. Ama ptr nesnesinin kendi adresinden de sz edilebilir. Bir gsterici deikenin deeri olan adres ile gsterici deikenin kendi adresi farkl eylerdir. printf("ptr nesnesinin adresi = %p\n", &ptr); arsyla ptr deikeninin kendi adresi ekrana yazdrlyor.

Dizi simlerinin Adres Bilgisine Dntrlmesi

C dilinde dizi isimleri bir ileme sokulduunda derleyici tarafndan otomatik olarak bir adres bilgisine dntrlr. char s[5]; gibi bir dizi tanmlamasnda sonra, dizinin ismi olan s bir ileme sokulduunda bu dizinin ilk elemannn adresine dntrlr. Dizi isimleri derleyici tarafndan, diziler iin bellekte ayrlan bloklarn balang yerini gsteren bir adres bilgisine dntrlr. Yukardaki rnekte dizinin bellekte aadaki ekilde yerletirildiini dnn:

s[0] s[1] s[2] s[3] s[4]

1C00 1C01 1C02 1C03 1C04

Bu durumda dizi ismi olan s, char trden 1C00 adresine edeerdir. Yani bu adresi bir adres deimezi eklinde yazlm olsayd: (char *)0x1COO

236

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

biiminde yazlrd. Bu durumda s ifadesi ile &s[0] ifadesi ayn adres bilgisidir, deil mi? Gsterici deikenlere kendi trlerinden bir adres bilgisi atamak gerektiine gre aadaki atamalarn hepsi geerli ve dorudur: int a[100]; long l[20]; char s[100]; double d[10]; int *p; long *lp; char *cp; double *dp; p = a; lp = l; cp = s; dp = d; Bir gstericiye yalnzca ayn trden bir dizinin ismi atanabilir. rnein: int *p; char s[] = "Necati"; p = s; /YANLI */

Dizi simleri Nesne Gstermez


int a[100]; int *ptr; gibi bir tanmlamadan sonra a gibi bir ifade kullanlrsa, bu iafade derleyici tarafndan otomatik olarak int * trne dntrlr. Yani bu ifadenin tr de (int *) trdr. ptr ifadesi nesne gsteren bir ifadeyken, yani bir sol taraf deeriyken, a ifadesi nesne gstermeyen bir ifade deeridir. Deitirilebilir sol taraf deeri (modifiable L value) olarak kullanlamaz. rnein a++ ifadesi geersizdir. C dilinde hibir deikenin ya da dizinin programn alma zamannda bulunaca yer 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

237

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

++s; deyimi geersizdir.

erik ileci (indirection operator / dereferencing operator) de, nek konumunda bulunan tek terimli (unary prefix) bir iletir. erik ilecinin terimi bir adres bilgisi olmaldr. Bir adres ifadesi, * ilecinin terimi olduunda, elde edilen ifade bellekte o adreste bulunan, nesneyi temsil eder. Dolaysyla, * ileci ile oluturulan bir ifade bir nesneye karlk gelir, sol taraf deeri olarak kullanlabilir. int a; gibi bir bildirimde a nesnesinin tr int trdr. nk a nesnesi iinde int trden bir veri tutulur. int *p; bildiriminde p'nin tr int trden bir adrestir. Yani p nesnesinin tr (int *) trdr. p nesnesinin iinde (int *) trnden bir veri tutulur. char *ptr; gibi bir bildirimden iki ey anlalr: ptr char trden bir gstericidir. ine char trden bir adres bilgisi yerletirilmek iin tanmlanmtr. ptr gstericisi * ileci ile birlikte kullanldnda elde edilen nesne char trdendir. Yani *ptr char trden bir nesnedir. rnein: int *p; p = (int *) 0x1FC2; *p = 100; Burada *p'nin tr int trdr. Dolaysyla *p = 100 gibi bir ilemden (DOS altnda) yalnzca 0x1FC2 byte' deil, 0x1FC2 ve 0x1FC3 byte'larnn her ikisi birden etkilenir. Gstericinin iindeki adresin saysal bileeni nesnenin dk anlaml byte'nn adresini ierir. Bu durumda bir gsterici deikene, bellekteki herhangi bir blgenin adresi atanabilir. Daha sonra * ileci ile o bellek blgesine eriilebilir. * ilecinin terimi bir adres bilgisi olmak zorundadr. Yani terim adres deimezi olabilir, dizi ismi olabilir, bir gsterici deiken olabilir veya adres ileci ile elde edilmi bir adres ifadesi olabilir. erik ileci yalnz gsterici nesneleriyle deil, adres bilgisinin her biimi ile (adres deimezleri ve dizi isimleri vs.) kullanlabilir. Bu ile, terimi olan adresteki nesneye erimekte kullanlr. le ile elde edilen deer, terimi olan adreste bulunan nesnenin deerdir. erik ileci ile retilen bir ifade nesne belirtir. Nesnenin tr terim olan nesnenin adresi ile ayn trdendir. Aadaki program derleyerek altrn, ekran ktsn yorumlayn: #include <stdio.h> int main() { char s[] = "Balkesir"; 238

* erik leci

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int a[] = {1, 2, 3, 4, 5}; int x = 10; int *ptr; putchar(*s); printf("%d\n", *a); *&x = 20; printf("x = %d\n", x); ptr = &x; *ptr = 30; printf("x = %d\n", x); } return 0;

Yukardaki programda, i) s char trden bir dizinin ismi olduuna gre char trden bir adrese dntrlr. Bu adres s dizisinin balang adresidir. *s ifadesi bu adresteki nesne olduuna gre, *s ifadesi dizimizin ilk eleman olan nesnedir, yani *s ifadesinin deeri 'B'dir, deil mi? ii) a int trden bir dizinin ismi olduuna gre int trden bir adrese dntrlr. Bu adres a dizisinin balang adresidir. *a ifadesi bu adresteki nesne olduuna gre, *a ifadesi int trden dizimizin ilk eleman olan nesnedir, yani *a ifadesi a[0] nesnesidir. Bu nesnenin deeri 1'dir. iii) *&x ifadesinde ise iki ayr ile kullanlyor. Adres ve ierik ileleri. Bu ilelerin her ikisi de ile ncelik tablosunda ikinci seviyede yer alyor. le ncelik tablosunun ikinci seviyesine ilikin ncelik yn sadan sola olduuna gre, ifadenin deerlendirilmesinde nce adres ileci deer retir. Adres ilecinin rettii deer x nesnesinin adresidir, ierik ilecinin terimi bu adres olur. erik ileci o adresteki nesneye ulatna gre *&x ifadesi x nesnesinin adresindeki nesne, yani x nesnesinin kendisidir. iv) ptr gstericisine x nesnesinin adresi atanyor. erik ilecinin terimi ptr nesnesi olduundan, ptr nesnesinin deeri olan adresteki nesneye ulalr. Bu durumda da *ptr nesnesi yine x nesnesinin kendisidir, deil mi? erik ilecinin ncelik tablosunun ikinci dzeyinde olduunu biliyorsunuz. s bir dizi ismi olmak zere *s + 1; ifadesinde nce ierik ileci deer retir. erik ilecinin rettii deer toplama ilecinin terimi olur. Oysa ifade *(s + 1) biiminde olsayd nce + ileci ele alnrd. Derleyiciler * atomu ieren bir ifadede * atomunun arpma ileci mi yoksa adres ileci mi olduunu ifade iindeki kullanmna bakarak anlar. arpma ileci iki terimli iken ierik ileci tek terimli nek konumunda bir iletir. *s * 2 ifadesinde birinci '*' ierik ileci iken ikincisi * aritmetik arpma ilecidir.

239

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir Gstericinin Bir Nesneyi Gstermesi


int x = 20; int *ptr ; ptr = &x; *ptr = 30; Yukardaki kod parasnda ptr gstericisine int trden x nesnesinin adresi atanyor. Bu atamadan sonra ptr gstericisinin deeri x nesnesinin adresidir. Bu durumda "ptr x deikenini gsteriyor" denir. ptr x'i gsteriyor ise *ptr, x nesnesinin kendisidir. Daha genel bir syleyile, ptr bir gsterici deiken ise *ptr o gstericinin gsterdii nesnedir! Tanmlanan bir deikene deikenin ismiyle dorudan ulaabildiiniz gibi, onu gsteren bir gstericiyi ierik ilecine terim yaparak dolayl bir biimde ulaabilirsiniz, deil mi? lecin ngilizce ismi olan "indirection operator" de bu durumu vurgular.

Gstericiler daha ok bir ilevin parametre deikeni olarak kullanlr. Bir gsterici bir nesne olduuna gre bir ilevin parametre deikeni herhangi bir trden gsterici olabilir: void func(int *p) { /***/ } levlerin parametre deikenleri, ilev arlaryla kendilerine geilen argman ifadeleriyle ilkdeerlerini aldna gre, bir ilevin parametre deikeni bir gsterici ise ilev de ayn trden bir adres bilgisi ile arlmaldr. Byle bir ilev, parametre deikenine adresi kopyalanan yerel bir deikenin deerini deitirebilir: #include <stdio.h> void func(int *ptr) { *ptr = 20; } int main() { int a = 10; func(&a); printf("%d\n", a); } return 0;

Parametre Deikeni Gsterici Olan levler

Yukardaki rnekte main ilevi iinde tanmlanan yerel a isimli deikenin adresi func ilevine gnderiliyor. func ilevi arldnda, yaratlan parametre deikeni ptr ilkdeerini &a ifadesinden alr. levin koduna geildiinde artk parametre deikeni olan ptr gsterici deikeni, adresi gnderilen a nesnesini gsterir. Bu durumda *ptr ifadesi a nesnesinin kendisidir.

240

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

*ptr = 20; deyimiyle a nesnesine 20 deeri atanr, deil mi? Bir ilev bir deer elde edip aran ileve bu deeri iletmek isterse iki yntem kullanlabilir: i. Elde edilen deer arlan ilev tarafndan geri dn deeri olarak retilir. ii. Elde edilen deer aran ilevin gndermi olduu adrese yerletirilir. Tabi bunun iin arlan ilevin parametre deikeninin bir gsterici olmas gerekir. Aada kendisine gnderilen bir saynn faktoriyelini hesaplayarak bu deeri parametre olarak gnderilen adrese kopyalayan bir ilev yazlyor: #include <stdio.h> void factorial(int n, long *p); int main() { long a; int k; for (k = 0; k < 14; ++k) { factorial(k, &a); printf ("%2d! = %ld\n", k, a); } } return 0;

void factorial(int n, long *p) { *p = 1; if (n == 0 || n == 1) return; while (n > 1) *p *= n--; } a bir yerel deiken olsun. C dilinde bir ilev func(a); biiminde arlmsa, arlan bu ilevin, a deikenini deitirme ans yoktur. Bu tr ilev arsna "deer ile arma" (call by value) denir. "levler" konusunda anmsayacanz gibi bu durumda a deikeninin deeri func ilevinin parametre deikenine kopyalanarak aktarlr. Yani func ilevinin parametre deikeni x nesnesinin kendisi deildir. func ilevinin kodu grlmese de ilev arsndan sonra yerel a deikeninin deerinin deimedii sylenebilir. levin a deikeninin deitirebilmesi iin func(&a); biiminde arlmas gerekir. rnein standart scanf ilevine & ileci ile bir nesnenin adresinin argman olarak yollanmasnn nedeni budur. Bu biimde yaplan ilev arsna

241

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C'de "adres ile arma" (call by reference) denir. Byle bir arda ileve bir nesnenin adresi gnderilir. levin parametre deikeni de bu adresi tutacak bir gsterici olur. int trden iki yerel nesnenin deerleri takas edilmek istensin. Bu i, bulunulan ilev iinde aadaki gibi yaplabilir: int main() { int a = 10, b = 20, temp; temp = a; a = b; b = temp; /*....*/

Takas ileminin bir ilev tarafndan yaplmas istenirse, aadaki gibi bir ilev i grr myd? void swap(int x, int y) { int temp = x; x = y; y = temp; } int main() { int a = 10, b = 20; swap(a, b); printf("a = %d\nb = %d\n", a, b); } return 0;

Yukardaki program altrdnda ekrana a = 10 b = 20 yazar! Yazlan swap ilevi a ve b deikenlerinin deerlerini deitirmez. Yerel nesneler olan a ve b deikenlerinin deerleri ancak bu deikenlerin adresleri bir ileve gnderilerek deitirilebilirdi. Oysa yukardaki swap ilevi a ve b deikenlerinin deerlerini parametre deikenleri olan x ve y deikenlerine kopyalyor. Yani deerleri deitirilen parametre deikenleri olan x ve y. Takas ilemini yapacak ilev kendisini aracak kod parasndan adres deerleri alaca iin gsterici parametre deikenlerine sahip olmal: void swap(int *p1, int *p2) { int temp = *p1; *p1 = *p2; *p2 = temp; }

242

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C dilinde bir adres bilgisi bir tamsay ile toplanabilir, bir adres bilgisinden bir tamsay kartlabilir. Byle bir ifade toplanan ya da kartlan adres trndendir. rnein int trden bir nesnenin adresi ile 1 tamsays toplanrsa yine int trden bir nesnenin adresi bilgisi elde edilir. Bir adres bilgisine 1 toplandnda, adresin saysal bileeni adrese sahip nesnenin trnn uzunluu kadar artar. Bu durumda rnein DOS iletim sisteminde char trden bir gstericinin deeri, 1 artrldnda adresin saysal bileeni 1, int trden bir gsterici 1 artrldnda ise adresin saysal bileeni 2 artar, double trden bir gsterici 1 artrldnda ise adresin saysal bileeni 8 artar. Bir gsterici deikenin bellekte bir nesneyi gsterdiini dnelim. Bu gsterici deikenin deeri 1 artrlrsa bu kez gsterici deikeni, gsterdii nesneden bir sonraki nesneyi gsterir duruma gelir. #include <stdio.h> int main() { int k; int a[10]; for (k = 0; k < 10; ++k) { *(a + k) = k; printf("%d ", a[k]); } } return 0;

Adreslerle lemler - Adreslerin Artrlmas ve Eksiltilmesi (Gsterici Aritmetii)

Yukardaki rnekte main ilevi iinde tanmlanan a dizisinin elemanlarna gsterici aritmetii kullanlarak ulalyor. *(a + k) a adresinden k uzaklktaki nesne anlamna gelir. Bu da dizinin k indisli elemandr, deil mi? Dizi int trden deil de double trden olsayd dizinin elemanlarna yine byle ulalabilirdi, deil mi? Gsterici aritmetii trden bamsz bir soyutlama salar. ki adres bilgisinin toplanmas geersizdir. Ancak ayn dizi zerindeki iki adres bilgisi birbirinden kartlabilir. ki adres birbirinden kartlrsa sonu bir tamsay trndendir. ki adres birbirinden kartldnda nce adreslerin saysal bileenleri kartlr, sonra elde edilen deer adresin ait olduu trn uzunluuna blnr. rnein a int bir dizi olmak zere trden bir adres olmak zere: &a[2] - &a[0] ifadesinden elde edilen deer 2 dir. Aadaki programda gsterici aritmetii sorgulanyor. Program derleyerek altrn ve ekran ktsn inceleyerek yorumlamaya aln: #include <stdio.h> int main() { char s[10]; int a[10]; double d[10];

243

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("%p\n", printf("%p\n", printf("%p\n", printf("%d\n", printf("%d\n", printf("%d\n", } return 0;

(char *)0x1AC0 + 1); (int *)0x1AC0 + 1); (double *)0x1AC0 + 1); &s[9] - &s[0]); &a[9] - &a[0]); &d[9] - &d[0]);

Ayn blok zerindeki iki adres, karlatrma ileleriyle karlatrlabilir: #include <stdio.h> int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int *ptr = a; while (ptr < a + 10) { printf("%d ", *ptr); ++ptr; } return 0; }

Adres deerlerinin karlatrlmas

Daha nce dizi elemanlarna erimekte kullandmz keli ayra aslnda iki terimli bir gsterici ilecidir. Keli ayra ileci (index / subscript operator) ile ncelik tablosunun en yksek ncelik seviyesindedir. lecin birinci terimi keli ayratan nce yer alr. Bu terim bir adres bilgisi olur. kinci terim ise keli ayra iine yazlacak tam say trnden bir ifade olur. p[n] ifadesi ile *(p + n) tamamen edeer ifadelerdir. Yani keli ayra ileci, bir adresten n ilerideki nesneye erimek iin kullanlr. [] ileci ile elde edilen nesnenin tr terimi olan adresin tr ile ayn trdendir. Aadaki programn ekrana ne yazdracan nce tahmine etmeye aln. Daha sonra program derleyip altrn:

[] Keli Ayra leci :

244

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int a[5] = {1, 2, 3, 4, 5}; int *ptr = a + 2; printf("%d\n", ptr[-1]); printf("%d\n", ptr[0]); printf("%d\n", ptr[1]); } return 0;

[] ilecinin birinci terimi dizi ismi olmak zorunda deildir. Daha nce de belirtildii gibi bir dizinin ismi bir ifade iinde kullanldnda derleyici tarafndan o dizinin ilk elemannn adresine yani dizinin balang adresine dntrlr. [] ileci ile ncelik tablosunun en yksek dzeyinde bulunur. rnein: &p[n] ifadesinde nce keli ayra ileci deer retir. lecin rettii deer, bir nesneye ilikindir. Adres ilecinin ncelik seviyesi keli ayra ilecinden daha dk olduu iin, ilecin ulat nesne bu kez adres ilecinin terimi olur. phesiz [] iindeki ifadenin saysal deeri negatif olabilir. rnein p[-2] geerli bir ifadedir. Benzer ekilde bu ifade *(p - 2) ifadesi ile ayn anlamdadr. Aadaki rnekte keli ayra ileci ile adres ileci ayn ifade iinde kullanlyor. #include <stdio.h> int main() { char ch = 'A'; (&ch)[0] = 'B' putchar(ch); } return 0;

C dilinin bir ok kod kalbnda gsterici ileleriyle ile artrma ya da eksiltme ileci birlikte kullanlr. 1. erik ileci ile ++ ilecinin ayn ifade iinde yer almas a) ++*p durumu

++ ve -- lelerinin Gsterici leleriyle Birlikte Kullanlmas

245

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int a[5] = {1, 2, 3, 4, 5}; int k; int *ptr = a; ++*ptr; for (k = 0; k < 5; ++k) printf("%d ", a[k]); } return 0; /* 2 2 3 4 5 */

++*ptr ifadesinde iki ile kullanlyor: erik ileci ile artrma ileci. Her iki ile de ile ncelik tablosunun ikinci seviyesinde bulunur. kinci seviyenin ncelik yn sadan sola olduuna gre nce daha sada bulunan ierik ileci deer retir. erik ileci ptr gstericisinin gsterdii nesneye ular bylece bu nesne, artrma ilecine terim olur. Bu durumda ptr gstericisinin gsterdii nesnenin deeri 1 artrlr. Ksaca ++*ptr; deyimi , "ptr'nin gsterdii nesnenin deerini 1 artr" anlamna gelir. *++p durumu p gstericisinin 1 fazlas olan adresteki nesneye ulalr. Yani ifadenin deeri p gstericisinin gsterdii nesneyi izleyen nesnenin deeridir. Tabi ifadenin deerlendirilmesinden sonra ++ ilecinin yan etkisinden dolay p gstericisinin deeri 1 artrlr. Yani ptr bir sonraki nesneyi gsterir. Aadaki rnei dikkatle inceleyin: #include <stdio.h> int main() { int a[5] = {1, 2, 3, 4, 5}; int k; int *ptr = a; *++ptr = 10; *ptr = 20; for (k = 0; k < 5; ++k) printf("%d ", a[k]); return 0; } x = *++p; deyimi ile x deikenine artrlm adresteki bilgi atanr. *p++ durumu ++ ileci ve * ilecinin ikisi de ikinci ncelik seviyesindedir. Bu ncelik seviyesine ilikin ncelik yn sadan soladr. nce ++ ileci ele alnr ve bu ile ifadenin geri kalan ksmna p gstericisinin artmam deerini retir. Bu adresteki nesneye ulalr daha sonra p gstericisinin deeri 1 artrlr. *p++ ifadesinin deeri p gstericisinin gsterdii /* 1 20 3 4 5 */

246

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

nesnenin deeridir. ++ ilecinin yan etkisinden dolay ifadenin deerlendirilmesinden sonra p gstericisinin deeri 1 artrlr. Yani p bir sonraki nesneyi gsterir. Aadaki rnei inceleyin: #include <stdio.h> int main() { int a[5] = {1, 2, 3, 4, 5}; int k; int *ptr = a; *ptr++ = 10; *ptr = 20; for (k = 0; k < 5; ++k) printf("%d ", a[k]); } return 0; /* 10 20 3 4 5 */

Adres ileci ile ++ ilecinin ayn ifade iinde yer almas &x++ /* Geersiz*/ x++ ifadesinin rettii deer adres ilecinin terimi olur. x++ ifadesinin rettii deer sol taraf deeri deildir. Adres ilecinin teriminin sol taraf deeri olmas gerekir. Bu durumda derleme zamannda hata oluur. &++x /* Geersiz */ ++x ifadesinin rettii deer adres ilecinin terimi olur. ++x ifadesinin rettii deer sol taraf deeri deildir ve adres ilecinin teriminin sol taraf deeri olmas gerekir. fade geersizdir. [C++ dilinde nek ++ ilecini bir sol taraf deeri rettii iin bu ifade C++ da geerlidir.] ++&x /* Geersiz */ Adres ilecinin rettii deer nek konumundaki artrma ilecinin terimi olur. ++ ilecinin teriminin nesne gsteren bir ifade olmas gerekir. Oysa &x ifadesi nesne gsteren bir ifade deildir. fade geersizdir. Adres ileci (&) ile artrma (++) ya da eksiltme (--) ilelerinin her trl bileimi derleme zamannda hata olumasna neden olur. Keli ayra ileci ile ++ ilecinin ayn ifade iinde yer almas ++p[i] durumu Keli ayra ileci birinci ncelik seviyesinde, ++ ileci ise ikinci ncelik seviyesindedir. Bu durumda derleyici tarafndan nce keli ayra ileci ele alnr. p[i] ifadesi bir nesne gsterir. Dolaysyla ++ ilecinin terimi olmasnda bir saknca yoktur. Sz konusu ifade p[i] = p[i] + 1; anlamna gelir. Yani p[i] nesnesinin deeri 1 artrlr. p[i]++ durumu x = p[i]++; nce p[i] nesnesinin artmam deeri retilir, ifadenin geri kalannda p[i] nesnesinin artmam deeri kullanlr. Yani yukardaki rnekte x deikenine p[i] nesnesinin artrlmam deeri atanr, daha sonra p[i] nesnesi 1 artrlr.

247

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

p[++i] durumu x = p[++i]; nce ++i ifadesinin deeri elde edilir. Bu ifadenin deeri i'nin deerinin 1 fazlasdr. Daha sonra p adresinden (i + 1) uzaklktaki nesneye ulalr. ++ ilecinin yan etkisi olarak i deikeninin deeri 1 artrlr. p[i++] durumu x = p[i++]; nce i++ ifadesinin deeri elde edilir. Bu ifadenin deeri i'nin kendi deeridir. Daha sonra p adresinden i uzaklktaki nesneye ulalr. ++ ilecinin yan etkisi olarak i deikeninin deeri 1 artrlr.

Gsterici Deikenlere lkdeer Verilmesi

Dier trden deikenlerde olduu gibi gsterici deikenlere de tanmlanmalar srasnda ilkdeer verilebilir. Gstericilere ilkdeer verme ilemi gstericinin trnden bir adres bilgisi ile yaplmaldr. rnekler: char s[100]; double x; int *ptr = (int *) 0x1A00; char * str = (char *) 0x1FC0; char *p = s; double *dbptr = &x; int i, *ptr = &i; Son deyimde tanmlanan i isimli deiken int trden, ptr isimli deiken ise int * trdendir. ptr deikenine ayn deyimle tanmlanan i deikeninin adresi atanyor.

Bir dizinin balang adresi ve boyutu bir ileve gnderilirse ilev dizi zerinde ilem yapabilir. levin dizinin balang adresini alacak parametresi ayn trden bir gsterici deiken olmaldr. levin dier parametresi dizinin boyutunu tutacak int trden bir deiken olabilir. Bir dizinin balang adresini parametre olarak alan ilev, dizi elemanlarna keli ayra ileci ya da ierik ileci ile eriebilir. Ancak dizi elemanlarnn ka tane olduu bilgisi ilev tarafndan bilinemez. Bu nedenle dizi uzunluu ikinci bir argman olarak ileve gnderilir. rnek:

Dizilerin levlere Gstericiler Yoluyla Geirilmesi

248

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> void display_array (const 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_array(a, 5); return 0; } Yukarda tanmlanan display_array ilevi int trden bir dizinin balang adresini ve boyutunu alyor, dizinin tm elemanlarnn deerlerini ekrana yazdryor. Parametre deikeni olan p gstericisinin bildiriminde yer alan const anahtar szcne daha sonra deinilecek. Aada ayn ilev iini yaparken bu kez ierik ilevini kullanyor: void display_array (const int *p, int size) { while (size--) printf("%d ", *p++); }

Gsterici Parametre Deikenlerinin Tanmlanmas


Bir ilevin parametre deikeninin gsterici olmas durumunda, bu gsterici iki farkl biimde tanmlanabilir: void func(int *ptr); void func(int ptr[]); Derleyici asndan iki biim arasnda hibir farkllk yoktur. Ancak baz programclar, ilev dardan bir dizinin balang adresini istiyorsa ikinci biimi tercih ederler: void sort_array(int ptr[], int size); Bu biim yalnzca ilev paraemtresi olan gstericilere ilikindir. Global ya da yerel gstericiler bu biimde tanmlanamaz: void foo(void) { int ptr[]; /* Geersiz */ } Aada int trden dizilerle ilgili baz faydal ilemler yapan ilevler tasarlanyor. levlerin tanmlarn dikkatli bir ekilde inceleyin. levlerin bazlarnn parametreleri olan gstericilerin bildiriminde const anahtar szcnn kullanldn greceksiniz. const anahtar szcn imdilik gznne almayn. Bu anahtar szck ileride ayrntl bir biimde ele alnacak.

249

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aada bir dizinin elemanlarna rastgele deerler yerletirmek amacyla bir ilev tanmlanyor: void set_random_array(int *ptr, int size, int max_val) { int k; for (k = 0; k < size; ++k) ptr[k] = rand() % (max_val + 1);

Diziler zerinde lem Yapan levlere rnekler

Bu ilev balang adresini ve boyutunu ald dizinin elemanlarn 0 max_val aralnda rastgele deerlerle dolduruyor. int sum_array(const int *ptr, int size) { int sum = 0; int k; for (k = 0; k < size; ++k) sum += ptr[k]; return sum; } sum_array ilevi dizinin elemanlarnn toplam deeriyle geri dnyor. Dizinin tm elemanlarnn deeri sum isimli yerel nesneye katlyor. lev sum nesnesinin deeri ile geri dnyor. int max_array(const int *ptr, int size) { int max = *ptr; int k; for (k = 1; k < size; ++k) if (ptr[k] > max) max = ptr[k]; } return max;

int min_array(const int *ptr, int size) { int min = *ptr; int k; for (k = 1; k < size; ++k) if (ptr[k] < min) min = ptr[k]; } return min;

max_array ilevi adresini ve boyutunu ald dizinin en byk elemannn deeri ile geri dnyor. min_array ilevi ise, benzer ekilde en kk elemann deeriyle geri dnyor.

250

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void sort_array(int *ptr, int size) { int i, k, temp; for (i = 0; i < size - 1; ++i) for (k = 0; k < size - 1 - i; ++k) if (ptr[k] > ptr[k + 1]) { temp = ptr[k]; ptr[k] = ptr[k + 1]; ptr[k + 1] = temp; } } sort_array ilevi adresini ve boyutunu ald diziyi" kabarck sralamas" algoritmasyla kkten bye doru sralyor. Aada yazlan ilevleri snayan bir main ilevi gryorsunuz. Tm ilevlerin tanmn main ilevinin tanm ile birlikte derleyerek program altrn: #include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 100

int main() { int a[ARRAY_SIZE]; srand(time(0)); set_random_array(a, SIZE, 1000); printf("dizi 0 - 1000 araliginda printf("dizi yazdiriliyor!\n"); display_array(a, SIZE); printf("dizinin toplami = %d\n", printf("dizinin en buyuk elemani printf("dizinin en kucuk elemani sort_array(a, SIZE); printf("dizi siralama isleminden display_array(a, SIZE); return 0; }

rastgele sayilarla dolduruldu!\n"); sum_array(a, ARRAY_SIZE)); = %d\n", max_array(a, SIZE)); = %d\n", min_array(a, SIZE)); sonra yazdiriliyor!\n");

levlerin Kendilerine Geilen Adres Bilgilerini Baka levlere Gemeleri

levler baka ilevleri arabilir, ardklar ilevlere kendi parametre deikenlerine geilen bilgileri argman olarak gnderebilir. phesiz bu durum parametreleri gsterici olan ilevler iin de geerlidir. int trden bir dizinin aritmetik ortalamasn hesaplamak amacyla mean_array isimli bir ilev tanmlayalm. Dizinin aritmetik ortalamasn bulmak iin nce toplamn bulmak gerekir, deil mi?

double mean_array(const int *ptr, int size) { return (double)sum_array(ptr, size) / size; } mean_array ilevi, kendisine geilen dizi adresi ile dizi boyutunu, dizinin toplamn hesaplamak amacyla sum_array ilevine argman olarak geiyor.

251

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

sort_array ilevinde ise, dizinin ardk iki eleman doru srada deil ise bu elemanlar takas ediliyor. nce int trden iki nesnenin deerini takas edecek bir ilev yazalm. Yazdmz bu ilevi sort_array ilevi iinde aralm: void swap(int *p1, int *p2) { int temp = *p1; *p1 = *p2; *p2 = temp; } void sort_array(int *ptr, int size) { int i, k; for (i = 0; i < size - 1; ++i) for (k = 0; k < size - 1 - i; ++k) if (ptr[k] > ptr[k + 1]) swap(ptr + k, ptr + k + 1);

sort_array ilevinde if (ptr[k] > ptr[k + 1]) swap(ptr + k, ptr + k + 1); deyimine dikkat edin. Bu deyimle, dardan adresi alnan dizinin k indisli eleman, k + 1 indisli elemanndan daha bykse, dizinin k ve k + 1 indisli eleman olan nesnelerin deerleri swap ilevi arlarak takas ediliyor. swap ilevine argman olarak iki nesnenin de adresi geiliyor. swap ilevi aadaki gibi de arlabilirdi, deil mi? swap(&ptr[k], &ptr[k + 1]); Aada bir diziyi ters eviren reverse_array isimli ilev tanmlanyor: void reverse_array(int *ptr, int size) { int *pend = ptr + size - 1; int n = size / 2; while (n--) swap(ptr++, pend--);

pend gsterici deikenine dizinin son elemannn balang adresi atanyor. while dngs dizinin eleman saysnn yars kadar dnyor. Dngnn her turunda, dizinin batan n. eleman ile sondan n. eleman takas ediliyor. ptr ve pend gsterici deikenleri, sonek konumunda olan ++ ve -- ilelerinin terimi oluyor. Dngnn her turunda ilelerin yan etkisi nedeniyle, ptr gstericisi bir sonraki nesneyi gsterirken, pend gstericisi bir nceki nesneyi gsteriyor.

Bir ilevin parametre deikeni bir adres trnden olabildii gibi, bir ilevin geri dn deeri de bir adres trnden olabilir. Byle bir ilevin tanmnda, ilevin geri dn deerinin trnn yazlaca yere bir adres tr yazlr. rnein int trden bir nesnenin adresini dndren func isimli ilev aadaki gibi tanmlanabilir:

Geri Dn Deeri Adres Trnden Olan levler

252

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int *func(void) { /***/ } func ilevinin geri dn deeri tr yerine int * yazldn gryorsunuz. Yukardaki ilev tanmndan * atomu kaldrlrsa, ilevin int trden bir deer dndrd anlalr. Adrese geri dnen bir ilev ne anlama gelir? lev arld yere, int trden bir nesnenin adresini iletir. lev ar ifadesi, ilevin geri dn deerine yani bir adres bilgisine edeerdir. levin geri dn deeri bir nesnede saklanmak istenirse ayn trden bir gsterici deikene atanmaldr: int *ptr; ptr = func(); Benzer biimde: levlerin geri dn deerlerini geici bir nesne yardmyla oluturduklarn biliyorsunuz. Bir ilevin geri dn deerinin tr, geri dn deerini iinde tayacak geici nesnenin trdr. Bu durumda, bir adres trne geri dnen bir ilevin, geri dn deerini iinde tutacak geici nesne de bir gstericidir. Bir adres bilgisiyle geri dnen ilevlere C programlarnda ok rastlanr. Standart C ilevlerinin bazlar da, adres trnden bir deer dndrr. Aadaki kod parasn inceleyin: #include <stdio.h> int g = 10; int *foo() { return &g; } int main() { int *ptr; printf("g = %d\n", g); ptr = foo(); *ptr = 20; printf("g = %d\n", g); } return 0;

foo ilevi arldnda global g isimli deikenin adresini dndryor. lev geri dn deerini return &g; deyimiyle retiyor. &g ifadesi (int *) trndendir. Bu ifadenin deeri yine (int *) trnden olan geici nesneye atanyor. main ilevi iinde arlan foo ilevinin geri dndrd adres, ptr gsterici deikenine atanyor. Bir dizinin en byk elemann bulup bu elemann deerine geri dnen getmax isimli ilev daha nce yazlmt. Aada ayn ilev bu kez en byk dizi elemannn adresine geri dnecek ekilde yazlyor:

253

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 20

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

int main() { int a[SIZE]; int k; srand(time(0)); for (k = 0; k < SIZE; ++k) { a[k] = rand() % 1000; printf("%d ", a[k]); } printf("\n"); printf("max = %d\n", *getmax(a, SIZE)); } return 0;

levin kodunu inceleyin. Yerel pmax gsterici deikeni, dizinin en byk elemannn adresini tutmak iin tanmlanyor. Balangta dizinin ilk eleman en byk kabul edildiinden, pmax gstericisine nce dizinin ilk elemannn adresi atanyor: pmax = (int *)p atamasyla dardan alnan dizinin balang adresinin pmax isimli gstericiye atandn gryorsunuz. Daha sonra oluturulan for dngsyle srasyla dizinin dier elemanlarnn, pmax gstericisinin gsterdii nesneden daha byk olup olmadklar snanyor. pmax'n gsterdii nesneden daha byk bir dizi eleman bulunduunda, bu elemann adresi pmax gstericisine atanyor. pmax = (int *)(p + i) atamasnda p + i ifadesinin &p[i] ifadesine edeer olduunu biliyorsunuz. Dng knda pmax gsteri deikeni dizinin en byk elemannn adresini tutar, deil mi? main ilevinde SIZE uzunluunda bir dizi nce rastgele deerle dolduruluyor. Daha sonra dizi elemanlarnn deerleri ekrana yazdrlyor. Aadaki ilev arsyla ekrana getmax ilevinin geri dndrd adresteki nesnenin deeri yazdrlyor. printf("max = %d\n", *getmax(a, SIZE)); Bu da dizinin en byk elemannn deeridir, deil mi? 254

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir ilevin bir adres bilgisine geri dnmesinin baka faydalar da olabilir. Hesaplanmak istenen bir deeri darya iletmek yerine, hesaplanan bu deeri iinde tutacak bir nesnenin adresi darya iletilebilir. Nasl olsa bir nesnenin adresi elimizdeyken o nesnenin deerine de ulaabiliriz, deil mi? Type szc bir tr belirtiyor olsun. Type trnden bir deer dndren bir ilevin bildirimi aadaki gibidir: Type foo(void); Byle bir ilevin geri dn deerini saklamak iin bu kez Type trnden bir nesneye ilev ar ifadesini atamak gerekir. Type val = foo(); Type trden bir nesne bellekte 100 byte yer kaplyor olsun. Bu durumda, arlan foo ilevinin almas srasnda return deyimine gelindiinde 100 byte'lk bir geici nesne oluturulur. return ifadesinden bu geici nesneye yaplan atama 100 byte'lk bir bloun kopyalanmasna neden olur. Geici nesnenin val isimli deikene aktarlmas da, yine 100 byte'lk bir bellek blounun kopyalanmasna neden olur. imdi de aadaki bildirime bakalm: Type *foo(void); Bu kez ilev hesaplad nesnenin deerini iinde tayan Type trnden bir nesnenin adresini dndryor. Bu durumda, ilevin geri dn deerini iinde tayacak geici nesne yalnzca, 2 byte ya da 4 byte olur, deil mi? Yani 100 byte'lk bir blok kopyalamas yerine yalnzca 2 ya da 4 byte'lk bir kopyalama sz konusudur. Byle bir ilevin geri dn deerini saklamak iin, ilev ar ifadesinin bu kez Type * trnden bir nesneye atanmas gerekir. Type *ptr; ptr = foo(); foo ilevinin hesaplad deeri iinde tutan Type trnden nesnenin adresi, Type trnden bir gsterici deikene atanyor. Type trnn sizeof deeri ne olursa olsun bu ilem, yalnzca bir gstericinin sizeof deeri kadar byklkte bir bloun kopyalanmasna neden olur, deil mi? Bir ilevin, bir deerin kendisini darya iletmesi yerine o deeri iinde tutan nesnenin adresini darya iletmesi, bellek ve zaman kullanm asndan maliyeti drr. Bir de baka bir faydadan sz edelim. Bir ilevden bir nesnenin deerini alnrsa, bu deeri tayan nesne deitirilemez. Ancak bir ilevden bir nesnenin adresi alnrsa, adresi alnan nesne deitirilebilir. Yukarda yazlan getmax ilevi bize dizinin en byk elemannn adresini dndryordu, deil mi? Aadaki main ilevini inceleyin:

255

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 100

void set_random_array(int *ptr, int size, int max_val); void display_array(const int *ptr, int size); int *getmax(const int *ptr, int size); int main() { int a[SIZE]; srand(time(0)); set_random_array(a, SIZE, 1000); display_array(a, SIZE); *getmax(a, SIZE) = -1; display_array(a, SIZE); *getmax(a, SIZE) = -1; display_array(a, SIZE); } return 0;

Aadaki deyime bakalm: *getmax(a, SIZE) = -1; levin geri dndrd adresin, ierik ilecine terim yapldn gryorsunuz. Bu ifadeyle ilevin geri dndrd adresteki nesneye ulalarak bu nesneye 1 deeri atanyor. Yani dizinin en byk elemannn deeri -1 yaplyor. Dizinin en byk elemann deerini geri dndren bir ilevle bu iin yaplmas mmkn olamazd. imdi aadaki program dikkatle inceleyin ve yazlan selec_sort isimli ilevde ne yapldn anlamaya aln:

256

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <time.h> #define SIZE 100

void swap(int *p1, int *p2); void display_array (const int *p, int size); void set_random_array(int *ptr, int size, int max_val); int *getmax(const int *ptr, int size); void select_sort(int *ptr, int size) { int k; for (k = 0; k < size - 1; ++k) swap (getmax(ptr + k, size - k), ptr + k); } int main() { int a[SIZE]; srand(time(0)); set_random_array(a, SIZE, 1000); printf("siralanmadan once\n"); display_array(a, SIZE); select_sort(a, SIZE); printf("siralanmadan sonra\n"); display_array(a, SIZE); } return 0;

Adrese geri dnen bir ilev asla otomatik mrl yerel bir nesnenin adresiyle geri dnmemelidir. Otomatik mrl yerel nesnelerin adresleriyle geri dnmek tipik bir programlama hatasdr. Klavyeden girilen bir ismin balang adresine geri dnen bir ilev yazlmak istensin: char *getname() { char name_entry[40]; printf("bir isim girin: "); gets(name_entry); return name_entry; /* Yanl!

Yerel Nesnelerin Adresleriyle Geri Dnmek

*/

#include <stdio.h> int main() { char *ptr = get_name(); printf("alnan isim = %s\n", ptr); } return 0;

257

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

get_name ilevi iinde yerel bir dizi tanmlanyor. Kullancnn girdii isim yerel diziye yerletiriliyor, daha sonra yerel dizinin adresiyle geri dnlyor. Yerel deikenlerin otomatik mrl olduunu, yani ait olduklar bloun yrtlmesi sonunda bellekten boaltldklarn biliyorsunuz. get_name ilevinin geri dn deeri, yani yerel name_entry dizisinin balang adresi main ilevi iinde ptr gstericisine atanyor. Oysa artk yerel dizi bellekten boaltld iin, ptr gsterici deikenine atanan adresin hibir gvenilirlii yoktur. ptr gstericisinin gsterdii yerden okuma yapmak ya da buraya yazmak gsterici hatasdr. Adrese geri dnen bir ilev yerel bir deikenin adresiyle ya da yerel bir dizinin balang adresiyle geri dnmemelidir. Yukarda yazlan getname ilevinin arlmas alma zaman hatasna neden olur. C derleyicilerinin ou, bu durumu mantksal bir uyar iletisi ile belirler.

NULL Adres Deimezi (Null Gsterici)


int x = 10; int *ptr = &x;

Bir gsterici deiken, iinde adres bilgisi tutan bir nesnedir, deil mi?

Yukardaki deyimleri aadaki cmlelerle ifade edebiliriz: ptr gstericisi x nesnesinin adresini tutuyor. ptr gstericisi x nesnesini gsteriyor. *ptr nesnesi , ptr'nin gsterdii nesnedir. *ptr , x nesnesinin kendisidir. yle bir gsterici olsun ki hibir nesneyi gstermesin. Hibir yeri gstermeyen bir gstericinin deeri yle bir adres olmaldr ki, bu adresin baka hibir amala kullanlmad gvence altna alnm olsun. te hibir yeri gstermeyen bir adres olarak kullanlmas amacyla baz balk dosyalarnda standart bir simgesel deimez tanmlanmtr. Bu simgesel deimez NULL simgesel deimezi olarak bilinir. NULL bir simgesel deimezdir. Bu simgesel deimez standart balk dosyalarndan stdio.h, string.h ve stddef.h iinde tanmlanmtr. NULL adresi herhangi trden bir gstericiye atanabilir. Byle bir atama tamamen szdizimsel kurallara uygundur, uyar gerektiren bir durum da sz konusu deildir. int *iptr = NULL; char *cptr = NULL; double *dptr = NULL; NULL adresi hibir yeri gstermeyen bir gstericinin deeridir. Bir gsterici ya bir nesneyi gsterir (yani bu durumda gstericinin deeri gsterdii nesnenin adresidir) ya da hibir nesneyi gstermez (bu durumda gstericinin deeri NULL adresidir). Bir adres bilgisinin doru ya da yanl olarak yorumlanmas sz konusu olduu zaman, adres bilgisi NULL adresi ise "yanl" olarak yorumlanr. NULL adresi dndaki tm adres bilgileri "doru" olarak yorumlanr. ptr isimli bir gstericinin deeri NULL adresi deil ise, yani ptr gstericisi bir nesneyi gsteriyorsa bir ilev arlmak istensin. Byle bir if deyiminin koul ifadesi iki ayr biimde yazlabilir: if (ptr != NULL) foo(); if (ptr) foo();

258

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu kez ptr isimli gsterici deikenin deeri NULL adresi ise, yani ptr gstericisi bir nesneyi gstermiyorsa, foo ilevi arlmak istensin. if deyiminin koul ifadesi yine iki ayr biimde yazlabilir: if (ptr == NULL) foo(); if (!ptr) foo(); Bir gsterici deikene herhangi bir trden 0 deeri atandnda, atama ncesi 0 deeri otomatik olarak NULL adresine dntrlr: int *ptr = 0; Yukardaki deyimle ptr gsterici deikenine NULL adresi atanyor. Peki NULL adresi ne iin kullanlr? Adrese geri dnen bir ilevin eer baarszl sz konusu ise, ilev baarszlk durumunu NULL adresine geri dnerek bildirebilir. int trden bir dizi iinde bulunan ilk asal saynn adresi ile geri dnen bir ilev yazmak isteyelim: int *get_first_prime(const int *ptr, int size); levin birinci parametresi dizinin balang adresi, ikinci parametresi ise dizinin boyutu olsun. levi aadaki biimde yazdmz dnn: int is_prime(int val); int *get_first_prime(const int *ptr, int size) { int k; for (k = 0; k < size; ++k) if (isprime (ptr[k])) return ptr + k; /* ??????? */ } Yukardaki ilevde, dardan balang adresi alnan dizimizin her elemann asal olup olmad snanyor, ilk asal say grldnde bu elemann adresiyle geri dnlyor. Peki ya dizinin iinde hi asal say yoksa, for dng deyiminden kldnda ilev bir p deeri geri dndrr, deil mi? Peki bu durumda ilev hangi geri dn deerini retebilir? Madem ki NULL adresi hibir yeri gstermeyen bir adres, o zaman adrese geri dnen bir ilev baarszlk durumunu NULL adresine geri dnerek bildirebilir, deil mi?

259

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int is_prime(int val); int *get_first_prime(const int *ptr, int size) { int k; for (k = 0; k < size; ++k) if (isprime (ptr[k])) return ptr + k; return NULL;

C dilinde, adrese geri dnen bir ilevin baarszlk durumunda NULL adresine geri dnmesi, ok sk kullanlan bir konvensiyondur. Parametre deikeni gsterici olan bir ilev, kendisine geilen NULL adresini bir bayrak deeri olarak kullanabilir. Aadaki gibi bir ilev tasarladmz dnelim: void func(char *ptr) { if (ptr == NULL) { /***/ } else { /***/ } } lev kendisine geilen adresin NULL adresi olup olmamasna gre farkl iler yapyor. Tabi bu durumun ilevi aran kod paras tarafndan bilinmesi gerekir. Standart C ilevlerinden time ilevi byledir. time ilevinin gsterici parametresine NULL adresi geildiinde ilev herhangi bir nesneye deer atamaz. Hesaplad deeri yalnzca geri dn deeri olarak darya iletir. Ancak ileve NULL adresi dnda bir adres gnderildiinde ilev verilen adresteki nesneye hesaplad deeri yazar. Birok programc bir gsterici deikene gvenilir bir adres atamadan nce, gstericiye NULL adresi deerini verir. Bylece kod iinde ilgili gstericinin henz bir nesneyi gstermedii bilgisi gl bir biimde verilerek, kodun okunabilirlii artrlr. Bir gstericinin mr henz sona ermeden, gsterdii nesnenin mr sona erebilir. Bu durumda gstericinin deeri olan adres gvenilir bir adres deildir. Kod iinde bu durumu vurgulamak iin gstericiye NULL adresi atanabilir.

Bir gstericiye farkl trden bir adres atandnda, C derleyicilerin ou durumu pheyle karlayarak mantksal bir uyar iletisi verir. Ancak derleyici yine de farkl trden adresin saysal bileenini hedef gstericiye atar. Borland derleyicileri bu durumda aadaki uyar iletisini verir: warning : suspicious pointer conversion in function ...... Aadaki kodu inceleyin:

Gstericilere likin Uyarlar ve Olas Gsterici Hatalar Bir Gstericiye Farkl Trden Bir Adres Atanmas:

260

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { double d = 1874567812.987; int *ptr; ptr = &d; *ptr = 0; printf("d = %lf\n", d); } return 0;

Yukardaki kodda double trden d deikeninin adresi int trden bir nesneyi gsterecek ptr deikenine atanyor. Byle bir atamadan sonra ptr double trden d deikenini deil, int trden bir nesneyi gsterir. *ptr = 0; atamasyla d deikeninin ilk 4 byte'na tamsay formatnda 0 deeri atanm olur. Byle bir atamadan sonra d deikeninin deeri istenilmeyen bir biimde deitirilmi olur, deil mi? Eer gstericiye farkl trden adres bilinli bir biimde atanyorsa tr dntrme ileci kullanlmaldr: #include <stdio.h> int main() { double d; unsigned int k; unsigned char *ptr; printf("bir gercek sayi girin :"); scanf("%lf", &d); ptr = (unsigned char *)&d; for (k = 0; k < sizeof(double); ++k) printf("%u\n", ptr[k]); return 0; } Yukardaki main ilevinde double trden d deikeninin adresi unsigned char trnden bir gstericiye atanyor. ptr gsterici deikeni byte byte ilerletilerek d deikeninin her bir byte'nn deeri tamsay olarak yorumlanarak ekrana yazdrlyor. ptr = (unsigned char *)&d; deyiminde atamann bilinli olarak yapldn gstermek iin d deikeninin adresi nce unsigned char trden bir adres bilgisine dntrlyor, daha sonra atama yaplyor.

Bu da bilinli olarak yaplma olasl ok az olan bir ilemdir. C derleyicileri pheli olan bu durumu mantksal bir uyar iletisi ile programcya bildirirler. rnein bu uyar iletisi Borland derleyicilerinde aadaki gibidir:

Bir Gsterici Deikene Adres Olmayan Bir Deerin Atanmas

261

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

"non-portable pointer conversion" Peki bir gstericiye adres bilgisi olmayan bir deer atanrsa ne olur? C'de bu dorudan bir szdizim hatas deildir. Yine otomatik tr dnm sz konusudur. Atama ilecinin sa tarafndaki ifadenin tr, atama ilecinin sol tarafnda bulunan nesne gsteren ifadenin trne evrilerek atama yaplr. Dolaysyla, atanan deer gsterici deikenin trnden bir adrese evrilerek gstericiye atanr. rnein: void func() { int x = 1356; int *ptr; ptr = x; /***/ } Yukardaki rnekte ptr = x; atama deyimi ile ptr deikenine x nesnesinin adresi deil, deeri atanyor. x deikeninin deeri olan 1356, atama ncesi tr dnmyle bir adres bilgisine dntrlerek ptr deikenine atanr. Artk ptr, x deikenini gstermez, 1356 adresindeki nesneyi gsterir: *ptr nesnesine erimek artk bir gsterici hatasdr.

Bir byte'tan daha byk olan deikenlerin bellee yerleim biimi kullanlan mikroilemciye gre deiebilir. Bu nedenle deikenlerin bellekteki grnmleri tanabilir bir bilgi deildir. Mikroilemciler iki tr yerleim biimi kullanabilir: i)Dk anlaml byte deerleri bellein dk sayl adresinde bulunacak biimde. Byle yerleim biimine little endian denir. Intel ilemcileri bu yerleim biimini kullanr. Bu ilemcilerin kullanld sistemlerde rnein int x = 0x1234; biimindeki bir x deikeni bellekte 1A00 adresinden balayarak yerletirilmi olsun:

Nesnelerin Bellekteki Yerleimleri

ekilden de grld gibi x deikeninin dk anlaml byte deeri (34H) dk saysal adreste (1A00H) olacak biimde yerletirilmitir. ii) kinci bir yerleim biimi, dk anlaml byte'n yksek saysal adrese yerletirilmesidir. Byle yerleim biimine big endian denir. Motorola ilemcileri bu yerleim biimini kullanr. Bu ilemcilerin kullanld sistemlerde rnein int x = 0x1234; biimindeki bir x deikeni bellekte 1A00 adresinden balayarak yerletirilmi olsun:

262

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

1A00 1A01

---0001 0010 0011 0100 ----

yksek anlaml byte yksek anlaml byte

ekilden de grld gibi x deikeninin dk anlaml byte deeri (34H) yksek saysal adreste (1A00H) olacak biimde yerletirilmitir. Aadaki kod kullanlan sistemin little endian ya da big endian oldugunu snyor: #include <stdio.h> int main() { int x = 1; if (*(char *)&x) printf("little endian\n"); else printf("big endian\n"); return 0; } Yazlan kodda nce adres ileciyle x deikeninin adresi elde ediliyor. Adres ilecinin rettii deer int * trndendir. Daha sonra tr dntrme ileciyle, elde edilen adres bilgisi char * trne dntrlyor. char * trnden adresin de ierik ilecinin terimi olduunu gryorsunuz. Bu durumda ierik ileci x nesnesinin en dk saysal adresindeki char trden nesneye eriir, deil mi? Eer bu nesnenin deeri 1 ise sistem "little-endian" dr.

Yazlar karakter dizilerinin iinde bulunurlar. Bir ilevin bir yaz zerinde ilem yapabilmesi iin bir yaznn balang adresini almas yeterlidir. Yani ilev yaznn (karakter dizisinin) balang adresi ile arlr. Yazy iinde tutan char trden dizinin boyutu bilgisini ileve geirmeye gerek yoktur. nk yazlarn sonunda sonlandrc karakter vardr. Karakter dizileri zerinde ilem yapan kodlar dizinin sonunu sonlandrc karakter yardmyla belirler. Yazlarla ilgili ilem yapan bir ilev char trden bir gsterici deiken ile zerinde ilem yapaca yaznn balang adresini alr. lev, yaznn sonundaki sonlandrc karakteri grene kadar bir dng ile yaznn tm karakterlerine eriebilir. str, char trnden bir gsterici olmak zere yaz zerinde sonlandrc karakteri grene kadar ilem yapabilecek dngler yle oluturulabilir: while (*str != '\0') { /***/ ++str; } for (i = 0; str[i] != '\0'; ++i) { /***/ }

Yazlarn levlere Gnderilmesi

stdio.h iinde bildirilen standart puts ilevinin parametre deikeni char trnden bir gstericidir:

puts ve gets levleri

263

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int puts(const char *str); lev, str adresindeki yazy standart k birimine yazar. lev yazma ilemini tamamladktan sonra ekrana bir de '\n' karakteri yazar. Eer yazma ilevi baarl olursa ilevin geri dn deeri negatif olmayan bir deerdir. Baarszlk durumunda ilev, negatif bir deere geri dner. Aada benzer ii gren myputs isimli bir ilev tanmlanyor: #include <stdio.h> void myputs(const char *str) { while (*str != '\0') putchar(*str++); putchar('\n'); } int main() { char s[] = "NecatiErgin"; int k; for (k = 0; k < 11; ++k) myputs(s + k); return 0; } stdio.h iinde bildirilen standart gets ilevi de aslnda gsterici parametreli bir ilevdir: char *gets(char *ptr); lev standart giri biriminden ald yazy parametresine aktarlan adrese yerletirir. Eer giri ilemi baarl olursa ilev ptr adresine geri dner. Baarszlk durumunda ilevin geri dn deeri NULL adresidir. Aada benzer ii gren mygets isimli bir ilev tanmlanyor: #include <stdio.h> char *mygets(char *ptr) { int ch; int index = 0; while ((ch = getchar()) != '\n') ptr[index++] = ch; ptr[index] = '\0'; } return ptr;

264

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yazlarla lgili lem Yapan Standart levler


C'nin standart baz ilevleri bir yaznn balang adresini parametre olarak alarak yaz ile ilgili birtakm faydal ilemler yapar. Bu ilevlere dizge ilevleri denir. Dizge ilevlerinin bildirimleri string.h dosyas iindedir.

En sk arlan standart C ilevlerinden biridir. levin ismi olan strlen, "string length" szcklerinden gelir. Bu ilev bir yaznn karakter uzunluunu yani yaznn ka karakterden olutuu bilgisini elde etmek iin kullanlr. levin bildirimi: size_t strlen(const char *str); biimindedir. levin parametre deikeni, uzunluu hesaplanacak yaznn balang adresidir. lev sonlandrc karakter grene kadar karakterlerin saysn hesaplar. Geri dn deeri tr yerine yazlan size_t nin imdilik unsigned int trnn bir baka ismi olduunu dnebilirsiniz. #include <stdio.h> #include <string.h> int main() { char s[100]; printf("bir yaz giriniz : "); gets(s); printf("(%s) yazsnn uzunluu = %u\n", s, strlen(s)); } return 0;

strlen levi

Standart C ilevi olan strlen aadaki biimlerde tanmlanabilir: unsigned int mystrlen1(const char *str) { unsigned int length = 0; while (*str != '\0') { ++length; ++str; } return length;

unsigned int mystrlen2(const char *str) { unsigned int len; for (len = 0; str[len] != '\0'; ++len) ; return len; }

265

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

unsigned int mystrlen3(const char *str) { const char *ptr = str; while (*str != '\0') str++; return str - ptr;

levin ismi olan strchr ,"string character" szcklerinden gelir. strchr ilevi bir karakter dizisi iinde belirli bir karakteri aramak iin kullanlan standart bir C ilevidir. levin string.h dosyas iindeki bildirimi aadaki gibidir: char *strchr(const char *str, int ch); Bu ilev ikinci parametresi olan ch karakterini, birinci parametresi olan str adresinden balayarak sonlandrc karakter grene kadar arar. Aranan karakter sonlandrc karakterin kendisi de olabilir. levin geri dn deeri, ch karakterinin yaz iinde bulunabilmesi durumunda bulunduu yerin adresidir. Eer ch karakteri yaz iinde bulunamazsa, ilev NULL adresine geri dner. strchr ilevi aadaki gibi tanmlanabilir: #include <stdio.h> #include <string.h> int main() { char s[100]; char *p, ch; printf("bir yaz girin : "); gets(s); printf("yaz iinde arayacanz karakteri girin : "); scanf("%c", &ch); p = strchr(s, ch); if (p == NULL) printf("aranan karakter bulunamad\n"); else printf("bulundu: (%s)\n", p); } return 0;

strchr levi

char *strchr(const char *str, int ch) { char c = ch; while (*str != '\0') { if (*str == c) return (char *)str; ++str; } if (ch == '\0') return (char *)str; return NULL; }

266

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Standart olan bu ilev strchr ilevi gibi bir yaznn iinde bir karakteri arar. Arama yaznn sonundan balanarak yaplr. Yani "ankara" yazs iinde 'n' karakteri arandnda ilev yaznn son karakterinin adresini dndrr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir: char *strrchr(const char *str, int ch); lev, str adresindeki yaz iinde bulunabilecek son ch karakterinin adresini dndrr. Eer yaznn iinde ch karakteri yoksa ilevin geri dn deeri NULL adresidir. lev ile yaznn sonundaki sonlandrc karakter de aranabilir. #include <stdio.h> char *mstrrchr(const char *str, int ch) { const char *p = str; const char *pf = NULL; while (*p) { if (*p == ch) pf = p; p++; } if (ch == '\0') return (char *)p; return (char *)pf; } int main() { char s[100]; char *ptr; int ch; printf("bir yazi girin : "); gets(s); printf("aranacak karakteri girin: "); ch = getchar(); ptr = mstrrchr(s, ch); if (ptr == NULL) printf("(%s) yazsnda (%c) bulunamad!\n", s, ch); else printf("bulundu: (%s)\n", ptr); return 0; }

strrchr levi

strstr levi

levin ismi string string szcklerinden gelir. Bu ilevle bir yaz iinde baka bir yaz aranr. levin bildirimi aadaki gibidir:

char *strstr(const char *p1, const char *p2); Eer p1 adresindeki yaz iinde p2 adresindeki yaz varsa, ilev yaznn bulunduu yerin adresini dndrr. Eer yoksa ilevin geri dn deeri NULL adresidir. Aadaki program derleyerek altrn:

267

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <string.h> #define SIZE int main() { char s1[SIZE]; char s2[SIZE]; char *ptr; printf("iinde arama yaplacak yazy girin: "); gets(s1); printf("aranacak yazy girin: "); gets(s2); ptr = strstr(s1, s2); if (!ptr) printf("(%s) yazisi icinde (%s) yazisi yok!\n", s1, s2); else printf("bulundu! (%s)\n", ptr); } return 0; 100

Bu ilev ile bir yaznn iinde baka bir yazda olan ilk karakterin indisi bulunur. levin string.h iindeki bildirimi yledir: size_t strcspn(const char *p1, const char *p2); Geri dn deeri u ekilde de tanmlanabilir : lev p1 yazs iinde yaznn bandan balayarak, p2 yazsnn karakterlerinden herhangi birini iermeyen yaznn uzunluu deerine geri dner: #include <stdio.h> #include <string.h> #define SIZE int main() { char s1[SIZE]; char s2[SIZE]; size_t index; printf("icinde arama yapilacak yaziyi girin: "); gets(s1); printf("karakterler: "); gets(s2); index = strcspn(s1, s2); printf("(%s)\n", s1 + index); s1[index] = '\0'; printf("(%s)\n", s1); return 0; } 100

strcspn levi

268

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

strpbrk levi

Standart strpbrk ilevi ile bir yazda baka bir yaznn karakterlerinden herhangi biri aranr: char *mstrpbrk(const char *s1, const char *s2) Eer s1 adresindeki yaznn iinde s2 adresindeki yaznn karakterlerinden herhangi biri varsa ilev bu karakterin adresini dndrr. Eer s1 yazs iinde s2 yazsnn karakterlerinin hibiri yoksa ilev NULL adresine geri dner. Aada strpbrk ilevinin rnek bir tanm ile bir snama kodu yer alyor: #include <string.h> #include <stdio.h> #define SIZE 100

char *mstrpbrk(const char *s1, const char *s2) { const char *p1, *p2; for (p1 = s1; *p1 != '\0'; ++p1) for (p2 = s2; *p2 != '\0'; ++p2) if (*p1 == *p2) return (char *)p1; return NULL; } int main() { char str1[SIZE]; char str2[SIZE]; char *ptr; printf("birinci yazy girin : "); gets(str1); printf("ikinci yazy girin : "); gets(str2); ptr = strpbrk(str1, str2); if (ptr == NULL) printf("\"%s yazisinda\" (%s) karakterlerinden hic biri yok!\n", str1, str2); else printf("bulundu : (%s)\n", ptr); return 0; }

Standart bir C ilevidir. levin ismi olan strcpy, "string" ve "copy" szcklerinden gelir. lev ikinci parametresinde tutulan adresten balayarak, sonlandrc karakter grene kadar, sonlandrc karakter de dahil olmak zere, tm karakterleri birinci parametresinde tutulan adresten balayarak srayla yazar. levin string.h balk dosyas iindeki bildirimi aadaki gibidir: char *strcpy(char *dest, const char *source); levin geri dn deeri kopyalamann yaplmaya baland adres yani dest adresidir. strcpy ilevi aadaki gibi tanmlanabilir:

strcpy levi

269

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <string.h> int main() { char dest[100] = "C reniyoruz!"; char source[100]; printf("kopyalanacak yazy girin : "); gets(source); printf("kopyalama yaplmadan nce yaz :(%s)\n", dest); strcpy(dest, source); printf("kopyalama yapildiktan sonra yaz :(%s)\n", dest); } return 0;

char *mystrcpy(char *dest, const char *source) { int i; for (i = 0; (dest[i] = source[i]) != '\0'; ++i) ; return dest;

lev iinde kullanlan for dngsnn ikinci ksmnda nce atama yaplyor, daha sonra atama ifadesinin deeri yani atama ilecinin sa tarafnda bulunan deer sonlandrc karakter ile karlatrlyor. Bylece ilgili adrese sonlandrc karakter de kopyalandktan sonra dngden klyor. lev aadaki gibi de yazlabilirdi: /***/ for (i = 0; source[i] != '\0'; ++i) dest[i] = source[i]; dest[i] = '\0'; /***/ for dng deyiminde keli ayra ileci kullanld iin, birinci parametre deikenine kopyalanan dest gsterici deikeninin deeri deitirilmiyor. levin sonunda dest adresi ile geri dnlyor. levin yazmnda while dngs kullanlarak, dest deikeni iindeki adres srekli artrlabilirdi. Bu durumda ilevin dest gstericisinin ilkdeeriyle geri dnebilmesini salayabilmek iin, dest gstericisindeki deeri deitirmeden nce, bu deeri baka bir gsterici deiken iinde saklamak gerekirdi: char *mystrcpy(char *dest, const char *source) { char *temp = dest; while ((*source++ = *dest++) != '\0') ; return temp;

Standart bir C ilevidir. levin ismi string ve concatanate szcklerinden gelir strcat ilevi bir yaznn sonuna baka bir yaznn kopyalanmas amacyla kullanlr. levin string.h dosyas iindeki bildirimi aadaki gibidir:

strcat levi

270

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char *strcat(char *s1, const char *s2); strcat ilevi eklemenin yaplaca ve balang adresi s1 birinci parametre deikeninde tutulan yaznn sonundaki sonlandrc karakteri ezerek, balang adresi ikinci parametre deikeninde tutulan yazy birinci yaznn sonuna (sonlandrc karakter de dahil olmak zere) ekler. Yani ilem sonunda s1 adresindeki yaznn uzunluu s2 adresindeki yaznn uzunluu kadar artar. levin geri dn deeri, sonuna eklemenin yapld yaznn balang adresi, yani s1 adresidir. #include <stdio.h> #include <string.h> int main() { char s1[100], s2[100]; printf("sonuna ekleme yaplacak yazy girin : "); gets(s1); printf("girdiiniz yaznn uzunluu = %d\n", strlen(s1)); printf("eklemek istediiniz yazy girin : "); gets(s2); printf("eklenecek yaznn uzunluu = %d\n", strlen(s2)); strcat(s1, s2); printf("ekleme yapldktan sonra 1. yaz : "); puts(s1); printf("ekleme yapldktan sonra yaznn uzunluu : %d\n", strlen(s1)) } return 0;

strcat ilevi aadaki gibi tanmlanabilir: char *mystrcat(char *s1, const char *s2) { char *temp = s1; while (*s1 != '\0') ++s1; while ((*s1++ == *s2++) != '\0') ; } return temp;

/* strcpy(s1, s2); */

Bir yaznn sonuna baka bir yazy eklemek, sona eklenecek yazy dier yaznn sonundaki sonlandrc karakterin bulunduu yere kopyalamak anlamna gelir, deil mi? Dolaysyla strcat ilevi aadaki biimlerde de tanmlanabilir. char *mystrcat(char *s1, const char *s2) { strcpy(s1 + strlen(s1), S2); return s1; } char *mystrcat(char *s1, const char *s2) { strcpy(strchr(s1, '\0'), s2); return s1; }

271

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Standart bir C ilevidir. levin ismi string compare szcklerinden gelir. lev iki karakter dizisini karlatrmakta kullanlr. Karlatrma, iki karakter dizisi iindeki yaznn, kullanlan karakter seti tablosu gznnde bulundurularak, ncelik ya da eitlik durumunun sorgulanmasdr. rnein: Adana yazs Ankara yazsndan daha kktr. nk eitlii bozan 'n' karakteri ASCII karakter tablosunda 'd' karakterinden sonra gelir. ankara yazs ANKARA yazsndan daha byktr. nk kk harfler ASCII tablosunda byk harflerden sonra gelir. Kk "masa" byk "MASA" dan daha byktr. kalem yazs kale yazsndan daha byktr. strcmp ilevinin string.h balk dosyas iindeki bildirimi aadaki gibidir: int strcmp(const char *s1, const char *s2); lev birinci parametre deikeninde balang adresi tutulan yaz ile, ikinci parametre deikeninde balang adresi tutulan yazlar karlatrr. levin geri dn deeri, birinci yaz ikinci yazdan daha bykse pozitif bir deerdir, birinci yaz ikinci yazdan daha kkse negatif bir deerdir, birinci yaz ile ikinci yaz birbirine eit ise 0 deeridir. #include <stdio.h> #include <string.h> int main() { char s[20]; char password[ ] = "Mavi ay"; printf("parolay girin : "); gets(s); if (!strcmp(s, password)) printf("Parola doru!..\n"); else printf("Parola yanl!..\n"); return 0;

strcmp ilevi

strcmp ilevi aadaki gibi tanmlanabilir: int mystrcmp(const char *s1, const char *s2) { while (*s1 == *s2) { if (*s1 == '\0') return 0; ++s1; ++s2; } return *(unsigned char *)s1 > *(unsigned char *)s2 ? 1 : -1; }

272

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Standart bir C ilevidir. levin ismi string number copy szcklerinden gelir. lev bir yaznn (karakter dizisinin) ilk n karakterini baka bir yere kopyalamakta kullanlr. levin string.h iindeki bildirimi aadaki gibidir : char *strncpy(char *dest, const char *source, size_t n); lev birinci parametre deikeninde balang adresi tutulan yazya, ikinci parametre deikeninde adresi tutulan yazdan, nc parametresinde tutulan sayda karakteri kopyalar. levin geri dn deeri kopyalamann yaplaca adres yani dest adresidir. nc parametre olan n says eer kopyalanacak yaznn uzunluundan daha kk ya da eit ise ilev kopyalama sonunda sonlandrc karakteri birinci dizinin sonuna eklemez. Yani n <= strlen(source) ise sonlandrc karakter eklenmez. nc parametre olan n says eer kopyalanacak yaznn uzunluundan daha byk ise ilev kopyalama sonunda sonlandrc karakteri birinci dizinin sonuna ekler. Yani n > strlen(source) ise sonlandrc karakter de kopyalanr. Aada strncpy ilevi tanmlanyor: #include <string.h> #include <stdio.h> #define SIZE 100 unsigned int n)

strncpy ilevi

char *mstrncpy(char *s1, const char *s2, { char *s = s1; while (n > 0 && *s2) *s++ = *s2++; n--; } while (n--) *s++ = '\0'; } return s1; {

int main() { char str1[SIZE]; char str2[SIZE]; int n; printf("birinci yaziyi girin : "); gets(str1); printf("ikinci yaziyi girin : "); gets(str2); printf("ikinci yazidan kac karakter kopyalanacak? "); scanf("%d", &n); strncpy(str1, str2, n); printf("(%s)\n", str1); return 0; }

273

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

nc parametreye aktarlan deer kopyalanan yaznn uzunluundan kk ya da eitse kopyalanan yere sizce neden sonlandrc karakter eklenmez? Bu ilevin yazlar iinde yer deitirme ilemi yapabilmesi istenmitir: #include <string.h> #include <stdio.h> #define SIZE 100

int main() { char str1[SIZE] = "Necati Ergin"; char str2[SIZE] = "Mehmet Aktunc"; strncpy(str1, str2, 6); printf("(%s)\n", str1); return 0; }

Standart bir C ilevidir. levin ismi ingilizce "string number concatanate" szcklerinden gelir. Bir yaznn sonuna baka bir yazdan belirli bir sayda karakteri kopyalamak amacyla kullanlr. string.h balk dosyas iinde bulunan bildirimi aadaki gibidir: char *strncat(char *s1, const char *s2, size_t n); lev birinci parametre deikeni iinde balang adresi verilen yaznn sonuna, ikinci parametresinde balang adresi tutulan karakter dizisinden, nc parametresinde tutulan tamsay adedi kadar karakteri kopyalar. levin geri dn deeri sonuna ekleme yaplacak yaznn balang adresidir. levin tanm ve ilevi snayan bir main ilevi rnek olarak aada veriliyor: char *mstrncat(char *s1, const char *s2, unsigned int n) { char *ptr; for (ptr = s1; *ptr != '\0'; ++ptr) ; while (n-- && *s2 != '\0') *ptr++ = *s2++; *ptr = '\0'; } return s1;

strncat levi

#include <string.h> #include <stdio.h> #define SIZE 100

int main() { char dest[SIZE]; char source[SIZE];

274

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int n; printf("birinci yaziyi girin : "); gets(dest); printf("ikinci yaziyi girin : "); gets(source); printf("1. yazinin sonuna kac karakter kopyalanacak : "); scanf("%d", &n); mstrncat(dest, source, n); printf("eklemeden sonra 1. yazi = (%s)\n", dest); } return 0;

Standart bir C ilevidir. levin ismi string number compare szcklerinden gelir.. strcmp ilevine benzer, ancak bu ilev iki yaznn tmn deil de, belirli bir sayda karakterlerini karlatrma amacyla kullanlr. lev birinci parametre deikeninde balang adresi tutulan yaz ile, ikinci parametre deikeninde balang adresi tutulan yazlarn, nc parametresinde tutulan saydaki karakterlerini karlatrr. lev, birinci yaznn ilk n karakteri ikinci yaznn ilk n karakterinden daha bykse pozitif bir deere Birinci yaznn ilk n karakteri ikinci yaznn ilk n karakterinden daha kkse negatif bir deere Birinci yaznn ve ikinci yaznn ilk n karakteri birbirine eit ise 0 deerine geri dner. Aada ilevin tanm ve ilevi snayan bir main ilevi veriliyor: int strncmp(const char *s1, const char *s2, unsigned int n) { while (n--) { if (*s1 != *s2) return *(unsigned char *)s1 < *(unsigned char *)s2 ? -1 : 1; if (*s1 == '\0') return 0; s1++; s2++; } return 0; } #include <stdio.h> #define SIZE 100

strncmp levi

int main() { char str1[SIZE]; char str2[SIZE]; int n, result; printf("birinci yaziyi girin : "); gets(str1); printf("ikinci yaziyi girin : "); gets(str2); printf("iki yazinin kac karakteri karsilastirilacak? "); scanf("%d", &n); result = strncmp(str1, str2, n); if (result == 0)

275

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("(%s) == (%s)\n", str1, str2); else if (result > 0) printf("(%s) > (%s)\n", str1, str2); else printf("(%s) < (%s)\n", str1, str2); } return 0;

Standart olmayan bu ilev derleyicilerin ounda bulunur. levin ismi string ve set szcklerinden gelir. Bir karakter dizisinin belirli bir karakterle doldurulmas amacyla kullanlr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir: char *strset(char *str, int ch); lev birinci parametre deikeninde balang adresi olan yazy sonlandrc karakter grene kadar ikinci parametre deikeninde tutulan karakterle doldurur. Yaznn sonundaki sonlandrc karaktere dokunmaz. levin geri dn deeri yine doldurulan yaznn balang adresidir. #include <stdio.h> #include <string.h> int main() { char s[100]; int ch; printf("bir yaz girin :"); gets(s); printf("yazy hangi karakterle doldurmak istiyorsunuz : "); ch = getchar(); printf("\nyaz %c karakteriyle dolduruldu (%s)\n", ch, strset(s, ch)); return 0; } strset ilevi aadaki gibi tanmlanabilir: #include <stdio.h> char *mystrset(char *str, int ch) { int i; for (i = 0; str[i] != '\0'; ++i) str[i] = ch; } return str;

strset levi

strrev levi

Standart olmayan bu ilev de derleyicilerin ounda bulunur. levin ismi string reverse szcklerinden gelir. lev bir yazy ters evirmek amacyla kullanlr. levin string.h balk dosyas iinde yer alan bildirimi aadaki gibidir:

276

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char *strrev(char *str); lev parametre deikeninde balang adresi tutulan yazy ters evirir. levin geri dn deeri ters evrilen yaznn balang adresidir. strrev ilevi aadaki gibi tanmlanabilir: char *mystrrev(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; }

Bu ilev standart olmamalarna karn hemen hemen her derleyicide bulunur. simleri string upper ve string lower szcklerinden gelir. Bu ilevler bir yaznn tm karakterleri iin byk harf kk harf dntrmesi yapar. levlerin geri dn deerleri parametresine aktarlan adrestir. Geri dn deerlerine genellikle gereksinim duyulmaz. Her iki ilev de temel Latin alfabesinde olan harfler iin dnm yapar. Trke karakterler iin de dnm gerekiyorsa bunun iin bir ilev tanmlanmaldr. #include <stdio.h> #include <string.h> int main() { char s[] = "C programcs olmak iin ok almak gerekir!"; strupr(s); puts(s); strlwr(s); puts(s); return 0;

strupr ve strlwr levleri

strupr ilevi aadaki gibi tanmlanabilir: #include <stdio.h> #include <ctype.h> char *mystrupr(char *str) { char *temp = str; while (*str != '\0') { if (islower(*str)) *str = toupper(*str); ++str; } return temp;

277

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Birok programda balang adresi tutulan bir yaznn sonunu bulmak gerekir. Bir yaznn balang adresini tutan bir gsteri deikeni, yaznn sonundaki sonlandrc karakteri gsterir hale getirmek iin, ayr kalp kullanlabilir: p bir yaznn balang adresini tutan gsterici olsun. Aadaki deyimlerin hepsi p gsterici deikeni yaznn sonundaki, sonlandrc karakterin bulunduu yere teler. p += strlen(p); p = strchr(p, '\0'); while (*p != '\0') ++ptr; Aadaki dngden ktktan sonra p gstericisi sonlandrc karakterden bir sonraki adresi gsterir. Neden? while (*p++ != '\0') ;

Bir Yaznn Balang Adresini Tutan Gstericiyi Yaznn Sonuna teleme

Bo yaz (null string) uzunluu 0 olan yazdr. str, char trden bir dizi olmak zere, eer str[0] sonlandrc karakter ise, str dizisinde bo yaz tutulmaktadr. Bo yaz geerli bir yazdr. Yazlarla ilgili ilem yapan ilevler, adresini ald yazlarn bo yaz (null string) olmas durumunda da doru almaldr. Aadaki dngy inceleyin: while (*++p != '\0') ; p bir yaznn balang adresini tutan gsterici deiken olmak zere, yukardaki dngden kldnda p, yaznn sonundaki sonlandrc karakteri gsterir. Ancak p'nin gsterdii yaz eer bo ise, yukardaki dng, yazya ait olmayan bellek alan zerinde ilem yapmaya balar. Bu da phesiz bir programlama hatasdr. imdi de aadaki if deyimini inceleyin: int strfunc(const char *ptr) { if (!ptr || !*ptr) return 0; /***/ } strfunc ilevi iinde yer alan if deyiminde yer alan !ptr || !*ptr ifadesi, ptr gsterici deikeninin deerinin NULL adresi olmas durumunda ya da ptr'nin gsterdii yaznn bo olmas durumunda dorudur. Bu ifade, " ptr bir yazy gstermiyorsa ya da ptr'nin gsterdii yaz bosa" anlamna gelir. Bu ifadenin mantksal tersi olan ptr && *ptr

Bo Yaz

278

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ifadesi ise, ptr bir yazy gsteriyor ve ptr'nin gsterdii yaz bo deil ise anlamna gelir, deil mi? Her iki ifade de mantksal ilelerin ksa devre davran zelliinden faydalanlyor. Mantksal &&, || ilelerinin ksa devre davran olmasayd, ptr'nin deeri NULL adresiyken, ptr'nin gsterdii nesneye eriilmeye allrd.

Aada yazlarla ilgili ilem yapan baz ilevler tanmlanyor. levlerde gsterici deikenlerin kullanmn inceleyin. Aada, bir yaznn C'nin kurallarna gre geerli bir isim olup olmad snayan is_valid_id isimli ilev tanmlanyor. Yaz geerli bir isim ise ilev sfr d deere, deilse sfr deerine geri dnyor: #include <ctype.h> int is_id(const char *ptr) { int ch; //Bos yazi ise gecerli isim degil if ((ch = *ptr++) == '\0') return 0; //lk karakter harf ya da "alt tire" olmal if (!(isalpha(ch) || ch == '_')) return 0; //kalan karakterler harf rakam ya da "alt tire" olmal while ((ch = *ptr++) != '\0') if (!(isalnum(ch) || ch == '_')) return 0; } return 1;

rnekler

279

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Gstericileri kullanarak RAM zerinde bir blgeye eriilebilir. Bir programn almas srasnda bellekte alyor durumda olan baka programlar da olabilir. Gstericileri kullanarak o anda almakta olan programn bellek alanna veri aktarlrsa oradaki programn kodu bozulaca iin programn almasnda eitli bozukluklar kabilir. Bu bozukluk tm sistemi olumsuz ynde etkileyebilir. Kim tarafndan kullanldn bilmediimiz bellek blgelerine gvenli olmayan blgeler denir. Gvenli olmayan blgelere eriilmesine ise "gsterici hatalar" denir. Gsterici hatalar yapldnda sistem kilitlenebilir, programlar yanl alabilir. Gsterici hatalar sonucundaki olumsuzluklar hemen ortaya kmayabilir. Gsterici hatalar gvenli olmayan blgelere eriildiinde deil oralara veri aktarldnda oluur. Gsterici hatalar derleme srasnda derleyici tarafndan saptanamaz. Bu tr hatalar programn alma zaman srasnda olumsuzluklara yol aar. Tanmlama yntemiyle elde edilmi olan bellek blgelerine gvenli blgeler denir. Bir nesne tanmlandnda, o nesne iin derleyici tarafndan bellekte ayrlan yer, programc iin ayrlm bir alandr ve gvenlidir.

Gsterici Hatalar

Gsterici Hatas Oluturan Tipik Durumlar


Daha nce belirtildii gibi gstericiler de birer nesnedir. Dier nesnelerden farklar ilerinde adres bilgileri tutmalardr. Gstericiler de nesne olduklarna gre dier nesneler gibi yerel ya da global olabilirler. Global olarak tanmlanm gstericiler 0 deeriyle balatlrken, yerel gstericiler p deerleriyle balatlr: Yerel bir gsterici tanmlandktan sonra, herhangi bir ekilde bu gstericiye bir deer atamas yaplmaz ise gstericinin iinde rastgele bir deer bulunacandan, bu gsterici * ileci ile ya da [ ] ileci ile kullanldnda, bellekte rastgele bir yerde bulunan bir nesneye ulalr. Dolaysyla, elde edilen nesneye bir atama yapld zaman, bellekte, bilinmeyen rastgele bir yere yazlm olunur. Bu durum tipik bir gsterici hatasdr. rnekler : int main() { int *p; *p = 25; } return 0; /* YANLI */

i)lkdeer Verilmemi Gstericilerin Yol At Hatalar:

Yukardaki rnekte tanmlanan p gstericisinin iinde bir p deer var. *p = 25; deyimiyle rastgele bir yere yani gvenli olmayan bir yere veri aktarlyor. Verinin aktarld yerde iletim sisteminin, derleyicinin ya da bellekte kalan baka bir programn (memory resident) kodu bulunabilir. Baz sistemlerde verinin aktarld yerde programn kendi kodu da bulunabilir. lkdeer verilmemi global gstericilerin iinde (ya da statik yerel gstericilerin iinde) sfr deeri bulunur. Sfr says (NULL adresi) gstericilerde snama amacyla kullanlr. Bu adrese bir veri aktarlmas durumunda, derleyicilerin ounda istee bal olarak bu hata alma zaman srasnda snanr. rnek:

280

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char *p; int main() { *p = 'm'; } return 0;

/* NULL pointer assignment */

Yukardaki kodun altrlmasnda "NULL pointer assignment" eklinde bir alma zaman hatasyla karlalabilir. Bu snama derleyicinin alabilen program iine yerletirdii "snama kodu" sayesinde yaplr. lkdeer verilmemi gstericilerin neden olduu hatalar ilev arlaryla da ortaya kabilir: int main() { char *ptr; gets(ptr); /* ????? */ return 0;

Yukardaki kod parasnda standart gets ilevi ile klavyeden alnan karakterler, bellekte rastgele bir yere yazlr. Standart gets ilevi klavyeden alnan karakterleri kendisine argman olarak gnderilen adresten balayarak yerletirdiine gre, daha nceki rnekte verilen hata klavyeden girilen btn karakterler iin sz konusudur.

ii)Gvenli Olmayan lkdeerlerin Neden Olduu Gsterici Hatalar


Bir gstericiye ilkdeer verilmesi , o gstericinin gvenli bir blgeyi gsterdii anlamna gelmez. 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 iinde belli amalar iin kullanlan gvenli blgeler varsa da 1FC5 byle bir blgeyi gstermez.

Bilindii gibi bir dizi tanmlamas grdnde derleyici, derleme srasnda dizi iin bellekte toplam dizi uzunluu kadar yer ayrr. C derleyicileri derleme zamannda bir dizinin tarlp tarlmadn kontrol etmez. int main() { int a[10], k; for (k = 0; k <= 10; ++k) a[k] = 0; /***/ return 0;

iii)Dizi Tamalarndan Doan Gsterici Hatalar

281

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rnek bir ok sistemde derlenip altrldnda dng k deikeninin 10 deeri iin de srer. Oysa a[10] eleman iin bir yer ayrlmamtr. Dizinin son eleman a[9] elemandr. Derleyici a dizisi iin a[0]'dan balayarak a[9] elemanlar iin bellekte toplam 10 nesnelik yani 40 byte'lk bir yer ayrr. Oysa yeri ayrlmam olan a[10] blgesinin kim tarafndan ve ne amala kullanld hakknda herhangi bir bilgi yoktur. Bu konuda bir garanti bulunmamakla birlikte derleyicilerin ou, ardk olarak tanmlanan elemanlar bellekte bitiik olarak (contiguous) olarak yerletirirler. Ama bu gvence altna alnm bir zellik deildir. Yani bunun gvence altna alnd dnlerek kod yazlmas problemlere yol aar. Yukardaki rnekte a[10] blgesi derleyicilerin ounda a dizisinden hemen sonra tanmlanan ve dng deikeni olarak kullanlan k deikenine ayrlr. Dolaysyla a[10] elemanna 0 deerini vermekle aslnda k deikenine 0 deeri aktarlm olabilir ve bu da dngnn sonsuz bir dngye dnmesine yol aabilir. Keli ayra ileci de bir gsterici ilecidir, bu ileci kullanarak dizi iin ayrlan alann dna atama yaplabilir. C dili derleyicileri kaynak kodu bu amala denetlemezler. Ayrca a dizisi iin a[-1], a[-2].. gibi ifadeler de szdizim asndan geerlidir ve buraya yaplacak atamalar da gsterici hatalarna yol aar. Bazen dizi tamalarna ilevler de gizli bir biimde neden olabilir. rnein: void func() { char str[6]; printf("isim girin : "); gets(str); /***/

func ilevi iinde tanmlanan str dizisi iin toplam 6 karakterlik yer ayrlyor. Standart gets ilevi klavyeden alnan karakterleri kendisine gnderilen adresten balayarak bellee yerletirdikten sonra, sonlandrc karakteri de diziye yazar. O halde yukardaki rnekte programn almas srasnda 6 ya da daha fazla karakterin girilmesi gsterici hatasna neden olur. Sonlandrc karakter de ('\0') bellekte bir yer kaplayaca iin program iin ayrlm bir bellek alan iinde bulunmas gerekir. rnein klavyeden girilen isim necati olsun. gets ilevi bu karakterleri aadaki gibi yerletirir :

Sonlandrc karakter, dizi iin ayrlan blgenin dna yerletiriliyor. Bu rnekte girilen isim daha uzun olsayd, program iin ayrlmam bir blgeye daha fazla karakter yazlacakt. Bu tr hatalarla karlamamak iin dizi yeteri kadar uzun olmal ya da standart bir C ilevi olan gets ilevi yerine, dizi uzunluundan daha fazla sayda eleman yerletirilmesine izin vermeyecek bir ilev kullanlmaldr. Bu amala fgets isimli ilev arlabilir. Standart fgets ilevini dosyalar konusunda greceksiniz. Dizgelerle ilgili ilemler yapan standart C ilevlerinden strcpy, strcat, strncpy, strncat ilevlerinin yanl kullanlmas da benzer hatalar oluturabilir.

282

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void Trden Gstericiler

Baz ilevler bellek bloklar zerinde genelletirilmi ilemler yapar. Bu ilevler ilem yaptklar bellek bloklarnda ne olduuyla ilgilenmez. Bir bellek blounun ieriinin bellekte baka bir yere kopyalandn dnn. lev kaynak adresten hedef adrese byte byte kopyalama yaparak bu amac gerekletirebilir. Byle bir ilevin parametre deikenleri hangi trden olmaldr? void gstericiler herhangi bir trden olmayan gstericilerdir. Bu trden deikenlerin tanmlarnda void anahtar szc kullanlr: void *ptr; void gstericilerin tr bilgisi yoktur. void gstericilerde adreslerin yalnzca saysal bileenleri saklanr. Bu yzden void gstericilerle dier trden gstericiler (adresler) arasnda yaplan atamalar geerlidir. void trden bir gstericiye herhangi bir trden bir adres sorunsuzca atanabilir. Belirli trden bir gsterici deikene void trden bir adres de ayn ekilde sorunsuzca atanabilir. char *ptr; void *vp; /***/ ptr = vp; vp = ptr; /* Geerli*/ /* Geerli */

void gstericiler belirli bir tre ait olmadklar iin, tr bilgisine sahip olan gstericiler zerinde uygulanan baz ilemler void trden gstericilere uygulanamaz: i) void trden gstericilerin * ya da [ ] ilelerinin terimi olmas geersizdir. Bu ileler bir nesneye erimek iin tr bilgisine gereksinim duyar. void func() { double a[50]; void *vptr; vptr = a; /* Geerli */ *vptr = 3.7; /* Geersiz! */ vptr[2] = 5.6; /* Geersiz! */ /****/ } Yukardaki kod parasnda *vptr ve vptr[2] ifadeleri geersizdir. ii) void trden bir adres ile bir tamsaynn toplanmas ya da void trden bir adresten bir tamsaynn kartlmas geersizdir. nk gsterici aritmetiine gre bir gstericinin deeri n kadar artrldnda, gsterici iindeki adresin saysal bileeni n ile gstericinin gsterdii nesnenin tr uzunluunun arpm kadar artar. void gstericilerin trleri olmad iin bu durumda saysal bileenin ne kadar artaca da bilinemez. void trden gstericiler ++ ve -- ilelerinin terimi olamaz. ++ptr; ifadesi ptr = ptr + 1; ifadesine edeerdir.

283

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void func() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; void *vptr = a; void *p; p = vptr + 2; /* Geersiz */ ++vptr; /* Geersiz */ --vptr; /* Geersiz */ vptr += 2; /* Geersiz */ vptr -= 3; /* Geersiz */ } iii) Benzer ekilde, void trden iki adres birbirinden kartlamaz. Dier trden adresler iin, ayn blok iindeki iki nesneye ilikin iki adresin birbirinden kartlmas tamamen geerlidir. Byle bir ifadenin deeri iki adres arasndaki nesne saysna denk bir tamsay oldugunu hatrlayn. void func() { void *p1, *p2; int k; /***/ k = p1 - p2; /* Geersiz */ } void gsterici deikenleri adreslerin yalnzca saysal bileenlerini saklamak amacyla kullanlrlar. Dier tr gstericiler arasndaki atama ilemlerinde uyar ya da hata oluturmadklarndan dolay, trden bamsz adres ilemlerinin yapld ilevlerde parametre deikeni biiminde de bulunabilirler. rnein: void func(void *p); func isimli ilevin parametre deikeni void trden bir gsterici olduundan bu ileve argman olarak herhangi bir trden bir adres bilgisi gnderilebilir. Yani func ilevi herhangi bir nesnenin adresi ile arlabilir. Bu durumda derleme zamannda bir hata olumad gibi, derleyiciden bir uyar iletisi de alnmaz. func ilevi, ald adresteki nesnenin trne bal bir ilem yapmaz. C dilinde ilevler void trden adreslere de geri dnebilir. void trden adreslerin herhangi bir trden gsterici deikene atanmas geerlidir void *func(void); int main() { int *p; char *str; p = func(); /***/ str = func(); } return 0; /* Geerli */ /* Geerli */

Standart ktphanede ba mem harfleri ile balayan biiminde bir grup ilev vardr. Bu ilevler trden bamsz olarak bellek bloklaryla ilgili genel ilemler yapar.

Parametre Deikeni void Gsterici Olan Standart C levleri

284

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void gstericilerin kullanmn en iyi aklayan rnek, standart string.h balk dosyas iinde bildirilen memcpy ilevidir. void *memcpy(void *pdest, const void *psource, size_t nbytes); memcpy ilevi ikinci parametresiyle belirtilen adresten balayarak (psource), nbytes sayda byte' birinci parametresiyle belirtilen adresten(pdest) balayarak kopyalar. levin sonlandrc karakterle ya da yazlarla bir ilikisi yoktur, koulsuz bir kopyalama yapar. Yani bir blok kopyalamas sz konusudur. lev kopyalad bellek blounda ne olduu ya da hangi trden bir veri olduuyla ilgilenmeksizin kaynak adresten hedef adrese belirli sayda byte' kopyalar. zellikle sistem programlarnda ok kullanlan bir standart ilevdir. memcpy ileviyle rnein ayn trden herhangi iki dizi birbirine kopyalanabilir: #include <stdio.h> #include <string.h> #define SIZE 10

int main() { int a[SIZE] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int b[SIZE]; int i; memcpy(b, a, sizeof(int)); for (i = 0; i < SIZE; ++i) printf("%d\n", b[i]); } return 0;

Aadaki ilev arlar da edeerdir: char s[50] = "Ali"; char d[50]; strcpy(d, s); memcpy(d, s, strlen(s) + 1); memcpy ilevinin gsterici olan parametreleri void trdendir. nk hangi trden adres geirilirse geirilsin, derleyicilerden herhangi bir uyar iletisi alnmaz. Bu ilev aadaki gibi tanmlanabilir: void *mymemcpy(void *vp1, const void *vp2, unsigned int n) { char *p1 = vp1; const char *p2 = vp2; unsigned int k; for (k = 0; k < n; ++k) p1[k] = p2[k]; } return vp1;

lev u biimde de tanmlanabilirdi:

285

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> void *mymemcpy(void *vp1, const void *vp2, size_t n) { char *p1 = vp1; const char *p2 = vp2; while (n--) *p1++ = *p2++; return vp1; } int main() { int a[5] = {1, 2, 3, 4, 5}; int b[5], i; mymemcpy(b, a, 10); for (i = 0; i < 5; ++i) printf("%d\n", b[i]); return 0; } Ancak memcpy ilevinin akk bloklarn kopyalanmasnda davran gvence altnda deildir.

memmove ilevi

memove da bir blou bir yerden bir yere kopyalar. levin bildirimi memcpy ilevininki ile tamamen ayndr. void *memmove(void *dest, const void *source, unsignt int nbytes); ki ilev arasndaki tek fark memmove ilevinin akk bloklarn kopyalanmasnda davrannn gvence altnda olmasdr. akk olmayan bloklarn kopyalanmasnda memmove ilevi mi tercih edilmelidir? Hayr, bu iyi bir tavsiye olamaz. nk: 1. memmove ilevinin byle bir gvence vermesinin ek bir yk vardr. 2. memmove ilevin kullanlmas durumunda kodu okuyan kii kopyalamann yapld blokla kopyalamann yaplaca bloklarn bir ekilde akt konusunda gl bir izlenim edinir. Bu durumda, kaynak blokla hedef bloun akmas durumunda ya da akma riskinin bulunduu durumlarda memmove ilevi, aksi halde yani sz konusu bloklarn akmad kesinlikle biliniyorsa memcpy ilevi tercih edilmelidir. Aada memmove ilevi iin yazlan rnek bir kod yer alyor: void *mymemmove(void *vp1, const void *vp2, unsigned int n) { char *p1 = vp1; const char *p2 = vp2; if (p1 > p2 && p2 + n > p1) { p1 += n; p2 += n; while (n--) *--p1 = *--p2; }

286

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

else while (n--) *p1++ = *p2++; return vp1; }

Standart bir C ilevidir. Adresi verilen bir bellek blounu belirli bir karakter ile yani bir byte deeri ile doldurmak iin kullanlr. levin bildirimi: void *memset(void *block, int c, unsigned int nbytes); Bu ilev block adresinden balayarak nbytes byklndeki blou, ikinci parametresiyle belirtilen byte deeriyle doldurulur. lev rnein, herhangi bir trden bir diziyi sfrlamak amacyla kullanlabilir: double d[100]; memset(d, 0, sizeof(d)); lev aadaki gibi tanmlanabilir: #include <stdio.h> void *mymemset(void *block, int c, unsigned int n); int main() { int a[10]; int i; mymemset(a, 0, sizeof(a)); for (i = 0; i < 10; ++i) printf("%d\n",a[i]); } return 0;

memset ilevi

void *mymemset(void *block, int c, size_t n) { char *p=(char *)block; while (n--) *p++ = c; } return block;

memchr ilevi
Standart bir C ilevidir. Adresi verilen bir bellek blounda belirli bir byte deerini aramak iin kullanlr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir: void *memchr(const void *block, int c, unsigned int nbytes); lev block adresinden balayan nbytes iinde c deerine sahip bir byte' arar. lev ilk bulduu c deerine sahip byte'n adresi ile geri dner. levin geri dn deeri void

287

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

trden bir adrestir. Eer aranan byte bulunamaz ise ilev NULL adresine geri dner. levin baars mutlaka snanmaldr. lev aadaki gibi tanmlanabilir: void *mymemchr(const void *p_block, int c, unsigned int nbytes) { const char *p = p_block; while (nbytes--) { if (*p == c) return (void *)p; p++; } return NULL;

memcmp ilevi
Standart bir C ilevidir. Adresleri verilen iki bellek blounu karlatrmak amacyla kullanlr. levin string.h balk dosyas iindeki bildirimi aadaki gibidir: int memcmp(const void *pblock1, const void *pblock1, unsigned int nbytes); lev block1 ve block2 adreslerinden balayan nbytes byklndeki iki blou karlatrr. levin geri dn deeri int trdendir. Geri dn deerinin yorumlanmas standart strcmp ilevine benzer: ki bellek blou tamamen aynysa, yani iki bloktaki tm byte'lar birbirine eitse, ilev 0 deerine geri dner. Birinci bellek blou ikinci bellek bloundan daha bykse ilev 0'dan byk bir deere geri dner. Birinci bellek blou ikinci bellek bloundan daha kkse ilev 0'dan kk bir deere geri dner. Karlatrma yle yaplr: Bloklar dk saysal adreslerden balayarak byte byte iaretsiz tamsay olarak karlatrlr. Farkl olan ilk byte ile karlaldnda, daha byk olan tamsayya sahip blok daha byktr. lev aadaki gibi tanmlanabilir: int mymemcmp(const void *vp1, const void *vp2, size_t nbytes) { const unsigned char *p1 = vp1; const unsigned char *p2 = vp2; unsigned int k; for (k = 0; k < nbytes; ++k) if (p1[k] != p2[k]) return p1[k] < p2[k] ? -1 : 1; } return 0;

Aada yazlan ilevi snayan bir main ilevi yazlyor: int main() { unsigned char s1[100] = {0}; unsigned char s2[100] = {0}; if (!mymemcmp(s1, s2, 100)) printf("bloklar esit!\n"); s1[90] = 1;

288

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (mymemcmp(s1, s2, 100) > 0) printf("birinci blok buyuk!\n"); return 0; }

289

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

C dilinde iki trnak iindeki karakterlere dizge ifadesi (string literal) ya da ksaca dizge (string) denir. rnein: "Necati Ergin" "x = %d\n" "ltfen bir say giriniz : " ifadelerinin hepsi birer dizgedir. Dizgelerin tek bir atom olarak ele alndn nceki konularmzdan anmsayacaksnz. C'de dizgeler, derleyiciler tarafndan aslnda char trden bir dizinin adresi olarak ele alnr. C derleyicileri, derleme aamasnda bir dizgeyle karlatnda, nce bu dizgeyi oluturan karakterleri bellein gvenli bir blgesine yerletirir, sonuna sonlandrc karakteri ekler. Daha sonra dizge yerine, yerletirildii yerin balang adresini koyar. Bu durumda dizge ifadeleri aslnda, dizgelerin derleyici tarafndan yerletirildii dizinin balang adresidir. rnein: char *p; p = "Necati Ergin"; gibi bir kod parasnn derlenmesi srasnda, derleyici nce "Necati Ergin" dizgesini bellein gvenli bir blgesine yerletirir. Daha sonra yerletirdii yerin balang adresini dizge ifadesi ile deitirir. Dizgeler char trden bir dizinin balang adresi olarak ele alndna gre, dizgelerin char trden gsterici deikenlere atanmalar geerlidir. Aadaki main ilevini derleyerek altrn: #include <stdio.h> int main() { printf("adres = %p\n", "Necati"); printf("adres = %s\n", "Necati"); } return 0;

DZGELER

main ilevi iinde yaplan ilk printf arsnda "Necati" dizgesi ile %p format karakteri eleniyor. printf ilevi ile, adres bilgilerinin %p format karakteriyle ekrana yazdrlabileceini anmsayn. Bu durumda alan kod, "Necati" yazsnn yerletirildii yerin balang adresini ekrana yazar. kinci printf arsnda ise "Necati" dizgesi %s format karakteri ile eleniyor. Bu durumda ekrana ilgili adresteki yaz, yani Necati yazs yazdrlr. imdi de aadaki arya bakn: putchar(*"Necati"); "Necati" dizgesinin bu kez ierik ilecinin terimi olduunu gryorsunuz. erik ileci, terimi olan adresteki nesneye eriimi saladna gre, bu nesnenin deeri 'N' karakterinin kod numarasdr. arlan putchar ilevinin almasyla ekrana N karakteri baslr. Aada tanmlanan get_hex_char ilevi, onaltlk say sisteminde bir basamak deeri hangi karakter ile gsteriliyorsa, o karakterin kullanlan karakter setindeki kod numarasn dndryor: #include <stdio.h>

291

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int get_hex_char(int val) { return "0123456789ABCDEF"[val]; } int main() { int k; for (k = 0; k < 16; ++k) putchar(get_hex_char(k)); } return 0;

levin geri dn deeri "0123456789ABCDEF"[val] ifadesinin deeridir. Bu da dizgenin yerletirildii adresten val uzaklktaki nesnenin deeridir. char trden bu nesnenin deeri de dizgede yer alan karakterlerden herhangi birinin sra numarasdr. main ilevi iinde 0 15 aralndaki deerlere karlk gelen karakterler bir dng iinde get_hex_char ilevi ile elde edilerek putchar ilevine argman olarak gnderiliyor. Programn ekran kts 0123456789ABCDEF olur. Dizgelerin bellekte kaplayacaklar yer derleme zamannda belirlenir. Aadaki program her altrldnda, ekrana hep ayn adres deeri yazlr: #include <stdio.h> int main() { int k; for (k = 0; k < 10; ++k) printf("%p\n", "Necati"); } return 0;

Parametre deikeni char trden bir gsterici olan ilevi, char trden bir adres ile armak gerektiini biliyorsunuz. nk char trden bir gsterici deikene, doal olarak char trden bir adres atanmaldr. Derleyiciler asndan dizgeler de char trden bir adres belirttiklerine gre, parametre deikeni char trden gsterici olan bir ilevi, bir dizge ile armak son derece doal bir durumdur: puts("Necati Ergin"); Burada derleyici "Necati Ergin" dizgesini bellee yerletirip sonuna sonlandrc karakteri koyduktan sonra artk bu dizgeyi, karakterlerini yerletirdii bellek blounun balang adresi olarak grr. puts ilevinin parametre deikenine de artk char trden bir adres kopyalanr. puts ilevi parametre deikeninde tutulan adresten balayarak sonlandrc karakteri grene kadar tm karakterleri ekrana yazar. Bu durumda ekranda

Dizgelerin levlere Argman Olarak Gnderilmesi

292

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Necati Ergin yazs kar. Aadaki rnekte de "Necati Ergin" dizgesi str adresine kopyalanr. Dizge ifadelerinin bulunduu yerde, char trden bir dizinin adresi bulunduu dnlmelidir. #include <stdio.h> #include <string.h> int main() { char str[20]; strcpy(str, "Necati Ergin"); puts(str); return 0; }

Dizgeler salt okunur bellek alanlarnda tutulabilir. Bu yzden bir dizgenin ieriinin kaynak kod iinde deitirilmesi yanltr. Dizgeleri deitiren kodlar tanmsz davran (undefined behavior) zellii gsterir. Aadaki rnei inceleyin: #include <stdio.h> int main() { char *ptr = "Durak"; *ptr = 'B'; /* Yanl */ puts(ptr); } return 0;

Dizgeler Salt Okunur Yazlardr

main ilevi iinde tanmlanan ptr isimli gsterici deiken "Durak" dizgesini gsteriyor. ptr gstericisinin gsterdii nesneye atama yaplmas yanltr. Yukardaki program derleme zamanna ynelik bir hata iermiyor.

zde Dizgeler

C derleyicileri kaynak kodun eitli yerlerinde tamamen zde dizgelere rastlasa bile bunlar iin farkl yerler ayrabilir. Ya da derleyici, dizgelerin salt okunur yazlar olmasna dayanarak, zde dizgelerin yalnzca bir kopyasn bellekte saklayabilir. zde dizgelerin nasl saklanaca derleyicinin seimine braklmtr. Birok derleyici, zde dizgelerin bellekte nasl tutulacaklar konusunda programcnn seim yapmasna olanak verir.

Dizgelerin Karlatrlmas
/***/ if ("Ankara" == "Ankara") printf("dogru!\n"); else printf("yanlis!\n"); /***/

Dizgelerin dorudan karlatrlmas yanl bir ilemdir.

293

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki kodun altrlmas durumunda, ekrana "yanl" ya da "doru" yazdrlmas gvence altnda deildir. Eer derleyici iki "Ankara" dizgesini bellekte ayr ayr yerlere yerletirmi ise, == karlatrma ileci 0 deeri retir. Ancak bir derleyici, "Ankara" yazsn tek bir yere yerletirip her iki dizgeyi ayn adres olarak da ele alabilir. Byle bir durumda == karlatrma ileci 1 deeri retir. Benzer bir yanllk aadaki kod parasnda da yaplyor: #include <stdio.h> int main() { char *pstr = "Mavi ay"; char s[20]; printf("parolay giriniz : "); gets(s); if (pstr == s) printf("dogru parola\n"); else printf("yanl parola\n"); } return 0;

Yukardaki programda s bir dizinin ismidir. s ismi ileme sokulduunda derleyici tarafndan otomatik olarak dizinin balang adresine dntrlr. pstr ise char trden bir gsterici deikendir. pstr gstericisine "Mavi ay" dizgesi atandnda, derleyici nce "Mavi ay" dizgesini bellekte gvenli bir yere yerletirir. Daha sonra dizgenin yerletirildii yerin balang adresini pstr gstericisine atar. Kullancnn, parola olarak "Mavi ay" girii yaptn varsayn. Bu durumda if deyimi iinde yalnzca s adresiyle pstr gstericisinin deeri olan adresin eit olup olmad snanr. Bu adresler eit olmadklar iin ekrana "yanl parola" yazlr. ki yaznn birbirine eit olup olmad standart strcmp ilevi ile snanmalyd: #include <stdio.h> #include <string.h> int main() { char *pstr = "Mavi ay"; char s[20]; printf("parolay giriniz : "); gets(s); if (!strcmp(pstr, s)) printf("dogru parola\n"); else printf("yanl parola\n"); return 0; } Tabi eitlik ya da eitsizlik karlatrmas gibi, byklk kklk karlatrmas da doru deildir. if ("CAN" > "ATA") printf("doru!\n"); else printf("yanli!\n");

294

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki if deyiminde CAN ve ATA isimleri strcmp ilevinin yapt biimde, yani bir yaz olarak karlatrlmyor. Aslnda karlatrlan yalnzca iki adresin saysal bileenidir.

Dizgelerin mr

Dizgeler statik mrl varlklardr. Dizgeler, tpk global deikenler gibi programn yklenmesiyle bellekte yer kaplamaya balar, programn sonuna kadar bellekte kalr. Dolaysyla dizgeler alabilen kodu bytr. Birok sistemde statik verilerin toplam uzunluunda belli bir snrlama sz konusudur. Dizgeler derleyici tarafndan .obj modle balayc program tarafndan da .exe dosyasna yazlr. Programn yklenmesiyle hayat kazanrlar. altrlabilen bir program ounlukla ana blmden oluur: kod data yn Kod blmnde, ilevlerin makine kodlar vardr. Data blmnde, statik mrl varlklar bulunur. Global deikenler ile dizgeler bu blmde bulunur. Yn blm yerel deikenlerin sakland bellek alandr. Yn blm her sistemde snrl bir alandr. rnein DOS iletim sisteminde 64K byklnde bir yn alan sz konusudur. Yani hibir zaman yerel deikenlerin bellekte kaplad alan 64K deerini geemez. WINDOWS iletim sisteminde varsaylan yn snr deeri 1 MB dr. Ancak bu snr deeri istenildii kadar bytlebilir.

Adrese geri dnen bir ilevin, yerel bir deikenin ya da yerel bir dizinin adresi ile geri dnmesi gsterici hatasdr. lev sonlandnda yerel deikenler bellekten boaltlaca iin, ilevin geri dndrd adres gvenli bir adres olmaz. Aadaki programda byle bir hatay yaplyor: #include <stdio.h> char *getname() { char s[100]; printf("ad ve soyad giriniz : "); gets(s); } return s; /* Yanl! */

Bir levin Bir Dizgeyle Geri Dnmesi

int main() { char *ptr; ptr = getname(); puts(ptr); } return 0;

Ancak char trden bir adrese geri dnen bir ilev bir dizge ile geri dnebilir. Bu durumda bir alma zaman hatas sz konusu olmaz. Dizgeler statik mrl varlklar olduklarndan programn alma sresi boyunca bellekteki yerlerini korur. rnein aadaki ilev geerli ve dorudur:

295

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char *getday(int day) { switch (day) { case 0: return "Sunday"; case 1: return "Monday"; case 2: return "Tuesday"; case 3: return "Wednesday"; case 4: return "Thursday"; case 5: return "Friday"; case 6: return "Saturday"; }

Dizgelerin Birletirilmesi
Dizgeler tek bir atom olarak ele alnr. Bir dizge aadaki gibi paralanamaz: void func() { char *ptr = "Necati Ergin'in C Ders Notlarn okuyorsunuz"; /* Geersiz */ /***/ } Ancak dizgelerin uzunluu arttka dizgeleri tek bir satrda yazmak zorlaabilir. Ekrandaki bir satrlk grntye smayan satrlar kaynak kodun okunabilirliini bozar. Uzun dizgelerin paralanmasna olanak vermek amacyla, C derleyicileri, aralarnda boluk karakteri dnda baka bir karakter olmayan dizgeleri birletirerek tek bir dizge olarak ele alr. rnein: ptr = "Necati Ergin'in C Ders " "Notlarn okuyorsunuz"; geerli bir ifadedir. Bu durumda derleyici iki dizgeyi birletirirek kodu aadaki biimde ele alr: ptr = "Necati Ergin'in C Ders Notlarn okuyorsunuz"; Derleyicinin iki dizgeyi birletirmesi iin, dizgelerin arasnda boluk karakterlerinin (white space) dnda hibir karakterin olmamas gerekir: p = "Necati" "Ergin"; ifadesi ile p = "NecatiErgin"; ifadesi edeerdir. Birletirmenin yansra, bir ters bl karakteri ile sonlandrlarak sonraki satra gei salanabilir. rnein: ptr = "Necati Ergin'in C Ders \ Notlarn okuyorsunuz"; deyimi ile ptr = "Necati Ergin'in C Ders Notlarn okuyorsunuz"; deyimi edeerdir. Tersbl karakterinden sonra, dizge aadaki satrn bandan devam etmelidir. Sz konusu dizge aadaki gibi yazlrsa:

296

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ptr = "Necati Ergin'in C Dersi \ Notlarn okuyorsunuz"; satr bandaki boluk karakterleri de dizgeye katlr. Sonu aadaki ifadeye edeer olur: ptr = "Necati Ergin'in C Dersi Notlarn okuyorsunuz";

Dizgelerde Ters Bl Karakter Deimezlerinin Kullanlmas

Dizgeler iinde ters bl karakter deimezleri de (escape sequence) kullanlabilir. Derleyiciler dizgeler iinde bir ters bl karakteri grdnde, onu yanndaki karakter ile birlikte tek bir karakter olarak ele alr. rnein:

char *p; p = "Necati\tErgin"; ifadesinde \t tek bir karakterdir (9 numaral ASCII karakteri olan tab karakteri). Yani printf("dizgedeki karakter says = %d\n", strlen(p)); ifadesi ile ekrana dizgedeki karakter says = 12 yazdrlr. Dizge ifadelerinde dorudan ift trnak ya da ters bl karakterleri kullanlamaz. Bu karakterlerin zel ilevleri vardr. "ift trnak" karakter deimezinin kendisini ifade etmek iin, ift trnak karakterinden nce bir ters bl karakteri kullanlr. rnein: #include <stdio.h> int main() { puts("\"Necati Ergin\""); return 0; } main ilevi iinde yaplan puts(ptr); ars ile ekrana "Necati Ergin" yazs yazdrlr. Dizge iinde yer alan ters bl karakter deimezi, onaltlk say sisteminde de ifade edilebilir. Aadaki program derleyerek altrn: #include <stdio.h> int main() { puts("\x41\x43\x41\x42\x41"); return 0; }

297

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bo Dizge

Bir dizge bo olabilir (null string) yani iinde hibir karakter bulunmayabilir. Bu durum iki ift trnak karakterinin arasna hibir karakter yazlmamas ile oluturulur. char *ptr = ""; Yukardaki deyim ile ptr gstericisine bo dizge atanyor. Bo dizgeler de bellekte bir adres belirtir. Derleyici bo bir dizge grdnde, bellekte gvenilir bir yere yalnzca sonlandrc karakteri yerletirir. Bo bir dizge uzunluu 0 olan bir dizgedir. Aadaki program derleyerek altrn: #include <stdio.h> #include <string.h> int main() { char *ptr = ""; printf("uzunluk = %d\n", strlen(ptr)); return 0; }

Dizgelerle Gsterici Deikenlere lkdeer Verilmesi


char *p = "stanbul"; char *err = "Bellek yetersiz"; char *s = "Devam etmek iin bir tua basnz";

Dizgeler kullanlarak char trden gsterici deikenlere ilkdeer verilebilir. rnein:

Dizgeler derleyiciler tarafndan char trden bir dizi adresi olarak ele alndna gre, char trden gsterici deikenlere dizgelerle ilkdeer verilmesi doal bir durumdur. Dizilere ift trnak iinde ilkdeer vermek ile, gsterici deikenlere dizgelerle ilkdeer vermek arasndaki farka dikkat etmek gerekir: char *p = "Deneme"; char s[10] = "Deneme"; deyimleri tamamen farkldr. Gsterici deikenlere ilkdeer verildiinde, derleyici bunu bir dizge ifadesi olarak ele alr. Yani dizge bellee yerletirildikten sonra balang adresi gstericiye atanr. Oysa dizilerde nce dizi iin yer ayrlr, daha sonra karakterler tek tek dizi elemanlarna yerletirilir. Dizilere ilkdeer verirken kullanlan ift trnak ifadeleri adres bilgisine dntrlmez. Dizi elemanlarna tek tek char trden deimezlerle ilkdeer verme ilemi zahmetli olduu iin, programcnn iini kolaylatrmak amac ile byle bir ilkdeer verme kural getirilmitir. char s[10] = "Deneme"; deyimi aslnda char s[10] = {'D', 'e', 'n', 'e', 'm', 'e', '\0'}; ile ayn anlamdadr.

298

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yaz Tutan char Trden Dizilerle Bir Dizgeyi Gsteren char Trden Gstericilerin Karlatrlmas
C dilinde bir yaz bilgisi en az iki ayr biimde saklanabilir: 1. Bir yaz char trden bir dizi iinde saklanabilir: #include <stdio.h> #include <string.h> void foo() { char s1[100] = "Necati Ergin"; char s2[100]; char s3[100]; printf("bir yaz giriniz: "); gets(s2); strcpy(s3, s2); /***/

Yukardaki foo ilevinde s1 dizisine Necati Ergin yazs ilkdeer olarak yerletiriliyor. s2 dizisine ise standart gets ilevi arsyla klavyeden bir yaz alnyor. s2 dizisindeki yaz, standart strcpy ilevine yaplan ar ile s3 dizisine kopyalanyor. 2. Yaz bir dizge olarak saklanarak char trden bir gsterici deikenin bu dizgeyi gstermesi salanabilir: char *ptr = "Necati Ergin"; ki yntem, birbirinin tamamen edeeri deildir. Aadaki noktalara dikkat edilmesi gerekir: Dizgeler statik mrl varlklar olduklar iin programn sonlanmasna kadar bellekte yer kaplar. Bir gsterici deikenin bir dizgeyi gsterirken, daha sonra baka bir dizgeyi gsterir duruma getirilmesi, daha nceki dizgenin bellekten boaltlaca anlamna gelmez: char *p = "Bu dizge programn sonuna kadar bellekte kalacak."; p = "artk yukardaki dizge ile bir balant kalmayacak..."; Yaznn char trden bir dizi iinde tutulmas durumunda bu yazy deitirmek mmkndr. Dizi elemanlarna yeniden atamalar yaplarak yaz istenildii gibi deitirilebilir. Ama dizgelerin deitirilmesi tanmlanmam davran zellii gsterir, yanltr: #include <stdio.h> #include <string.h> void foo() { char *ps = "Metin"; char *pstr = "Ankara"; ps[1] = ''; strcpy(pstr, "Bolu"); /***/ /* Yanl! */ /* Yanl! */

299

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

GSTERC DZLER
Gstericiler de birer nesne olduuna gre gsterici dizileri de tanmlanabilir: Elemanlar gsterici deikenlerden oluan dizilere "gsterici dizileri" (pointer arrays) denir. rnekler: char *pstr[10]; int *ap[50]; float *kilo[10]; Gsterici dizilerinin bildirimleri, normal dizi bildirimlerinden farkl olarak '*' atomu ile yaplr. rnein: char str[100]; bildiriminde, str 100 elemanl char trden bir dizi iken char *pstr[100]; bildiriminde ise, pstr 100 elemanl char trden bir gsterici dizisidir. Yani dizinin her bir eleman char * trnden nesnedir. Dizinin her bir eleman bir gsterici deikendir. Derleyici, bir gsterici dizisi tanmlamas ile karlanca, dier dizilerde yapt gibi, bellekte belirtilen sayda gsterici nesnesini tutabilecek kadar, bitiik bir bellek blou ayrr. rnein: char *p[10]; bildirimi ile derleyici, p dizisinin 10 elemanl ve her elemann char trden bir gsterici olduunu anlar. Kullanlan sistemde gstericilerin uzunluunun 4 byte olduu varsaylrsa, derleyici bu dizi iin 40 byte'lk bir bellek blou ayrr. Bu durumda; p[0], p[1], p[2], ...p[9] dizi elemanlarnn her biri char * trndendir. Bu dizinin elemanlarndan her bir nesne, dierinden bamsz olarak kullanlabilir.

Gsterici Dizilerine lkdeer Verilmesi


Gsterici dizilerine de ilkdeer verilebilir: rnein: int *p[] = { (int *) (int *) (int *) (int *) (int *) } 0x1FC0, 0x1FC2, 0x1FC4, 0x1FC6, 0x1FC8

Kukusuz, gsterici dizisinin elemanlarna atanan adreslerin bir ama dorultusunda kullanlabilmesi iin, gvenli blgeleri gstermesi gerekir. Bu nedenle uygulamada char trden gsterici dizisi dndaki gsterici dizilerine ilkdeer verilmesi durumuna sk rastlanmaz. Genellikle char trden gsterici dizilerine, dizge ifadeleriyle ilkdeer verilir.

Uygulamalarda en sk grlen, char trden olan gsterici dizileridir. Dizgelerin, C derleyicisi tarafndan otomatik olarak char trden dizi adreslerine dntrldn biliyorsunuz. Bu durumda char trden bir gsterici dizisinin elemanlarna dizgeler ile ilkdeer verilebilir:

char Trden Gsterici Dizileri

301

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char *aylar[] = {"Ocak", "ubat", "Mart", "Nisan", "Mays", "Haziran", "Temmuz", "Austos", "Eyll", "Ekim", "Kasm", "Aralk"}; aylar dizisinin her bir eleman char trden bir gstericidir. Bu gsterici dizisine, dizgeler ile ilkdeer veriliyor. Baka bir deyile, char trden bir gsterici dizisi olan aylar dizisinin her bir eleman, yln aylarnn ismi olan yazlar gsteriyor.

char trden gsterici dizileri yazlarla ilgili ilemler yapan programlarda ska kullanlr. Program iinde sk kullanlacak yazlarn, kaynak kod iinde her defasnda bir dizge olarak yazlmas yerine, bir gsterici dizisinin elemanlarnda saklanmas ok rastlanan bir temadr. Aadaki rnekte error_messages dizisinin her bir elemannda, hata iletilerine ilikin yazlar saklanyor. char *error_messages[] = {"Bellek Yetersiz!", "Hatal ifre", "Dosya bulunamad", "Belirtilen dosya zaten var", "src hazr deil", "Okunacak dosya alamyor", "yazlacak dosya alamyor!..", "Belirlenemeyen hata!"}; Artk programn herhangi bir yerinde yazlardan biri yazdrlmak istendiinde, gsterici dizisinin herhangi bir elemanna eriilerek ilgili yaznn balang adresi elde edilir. /*...*/ if (fp == NULL) { printf("%s\n", err[5]); return 5; } imdi de aadaki program inceleyin: #include <stdio.h> int day_of_week(int day, int month, int year); char *months[] = {"January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"}; char *days[] = {"Sunday", "Friday", "Saturday" }; "Monday", "Tuesday", "Wednesday", "Thursday",

char Trden Gsterici Dizilerinin Kullanlma Temalar

void display_date(int day, int mon, int year) { printf("%02d ", day); puts(aylar[mon - 1]); printf(" %d ", year); printf("%s", days[day_of_week]); } Yukardaki programda ismi months ve days olan char trden gsterici dizileri tanmlanyor. months dizisinin her bir eleman, bir ay ismini ieren yaznn balang adresini, days dizisinin her bir eleman ise bir gn ismini ieren yaznn balang adresini tutuyor. Gsterici dizisinin elemanlarna dizgelerle ilkdeer veriliyor. display_date isimli ilevin, bir tarihe ilikin gn, ay ve yl deerlerini aldn, ilgili tarihi ekrana yazdrrken, gsterici dizilerinden faydalandn gryorsunuz. Belirli sayda bir yaz grubu iinde, bir yaznn var olup olmadnn snanmas amacyla da char trden gsterici dizileri sklkla kullanlr. Bir yaznn belirli bir yazyla ayn olup olmad standart strcmp ileviyle snanabilir. Peki, bir yaznn, bir grup yaz iinde var olup olmad nasl saptanabilir?

302

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aada tanmlanan get_month ilevi, kendisine balang adresi gnderilen bir yaznn, ngilizce ay isimlerinden biri olup olmadn snyor. Snad yaz geerli bir ay ismi ise, ilev bu ayn kanc ay olduu bilgisiyle (1 - 12) geri dnyor. lev, kendisine gnderilen yaz bir ay ismine karlk gelmiyor ise 0 deeriyle geri dnyor. #include <stdio.h> #include <string.h> #define SIZE 20

int get_month(const char *str) { char *months[] = {"Ocak", "Subat", "Mart", "Nisan", "Mayis", "Haziran", "Temmuz", "Agustos", "Eylul", "Ekim", "Kasim", "Aralik"}; int k; for (k = 0; k < 12; ++k) if (!stricmp(months[k], str)) return k + 1; return 0;

int main() { char s[SIZE]; int result; printf("bir ay ismi giriniz ."); gets(s); result = get_month(s); if (result) printf("%s yilin %d. ayidir\n", s, result); else printf("%s gecerli bir ay ismi degildir\n", s); return 0; } Standart olmayan stricmp ilevinin, iki yaznn karlatrmasn byk harf kk harf duyarll olmadan yapmas dnda, strcmp ilevinden baka bir fark bulunmadn anmsayn. Gsterici dizileri tpk dier diziler gibi, yerel ya da global olabilir. Dizinin global olmas durumunda, dizi hayata baladnda dizinin elemanlarnn hepsinin iinde 0 deerleri bulunurken, yerel bir gsterici dizisinin iinde rastgele deerler olur. Dizinin her bir eleman iinde bir adres bilgisi olduuna gre, atama yaplmam global gsterici dizilerinin her bir eleman iinde 0 adresi (NULL adresi) bulunur. lkdeer verilmemi yerel gsterici dizilerinin elemanlar iinde ise rastgele adres deerleri bulunur. Bir gsterici hatasna neden olmamak iin nce gsterici dizisi elemanlarna gvenli adresler yerletirmek gerekir. Dizgeler statik mrl varlklar olduklar iin, bir gsterici dizisinin elemanlarna dizgelerle deer vermek bir gsterici hatasna neden olmaz. Zira dizgeler, daha nce de belirtildii gibi, nce derleyici tarafndan bellekte gvenli bir blgeye yerletirilir, daha sonra ise yerletirildikleri bloun balang adresi olarak ele alnr. Baz durumlarda, bir gsterici dizisinin son elemanna zellikle NULL adresi atanr. Byle bir gsterici dizisi, dizi boyutu belirtilmeden ilenebilir. Aadaki rnei inceleyin:

303

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int k; char *names[] = {"Ali", "Hasan", "Mehmet", "Sebahat", "Fatma", "Tuncay", "Deniz", "Kerim", "Necati", NULL}; for (k = 0; names[k] != NULL; ++k) puts(names[k]); return 0; } Aadaki rnekte, char trden bir gsterici dizisinin elemanlar gsterdikleri yazlarn byklklerine gre sralanyor: #include <stdio.h> #include <string.h> #define SIZE 10

int main() { int i, k; char *names[SIZE] = {"Ali", "Hasan", "Mehmet", "Sebahat", "Fatma", "Tuncay", "Kaan", "Taylan", "Aleyna", "Deniz"}; printf("dizi yazdiriliyor!\n"); for (k = 0; k < SIZE; ++k) puts(names[k]); for (k = 0; k < SIZE -1; ++k) for (i = 0; i < SIZE - 1 -k; ++i) if (strcmp(names[i], names[i + 1]) > 0) { char *temp = names[i]; names[i] = names[i + 1]; names[i + 1] = temp; } printf("siralanmis dizi yazdiriliyor!\n"); for (k = 0; k < SIZE; ++k) puts(names[k]); return 0; } Sralama amacyla yine "kabarck sralamas" algoritmas kullanlyor. Gsterici dizisinin ardk iki elemannn deerleri strcmp(names[i], names[i + 1]) > 0 koulunun doru olmas durumunda takas ediliyor. Bu, "dizinin i indisli eleman olan gstericinin gsterdii yaz, i + 1 indisli elemannn gsterdii yazdan daha bykse" anlamna gelir, deil mi? Eer takas ilemi, daha nce dier trden dizileri sralarken kullanlan if (names[i] > names[i + 1] > 0)

kouluyla yaplsayd ne olurdu? imdi aadaki kod parasn inceleyin:

304

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { char *names[6] = {"Ali", "Hasan", "Mehmet", "Sebahat", "Fatma" "Tunc"}; int k; for (k = 0; k < 6; ++k) puts(names[k]); return 0; } Bcei grebildiniz mi? Aralarnda boluk karakteri dnda baka bir karakter olmayan dizgelerin, derleyici tarafndan otomatik olarak birletirilerek tek bir dizge olarak deerlendirildiini biliyorsunuz. Yukardaki kod parasnda names isimli dizinin elemanlarna ilkdeer verirken kullanlan "Fatma" dizgesiyle "Tuncay" dizgesi arasnda virgl atomu konulmam. Bu durumda derleyici bu iki dizgeyi birletirerek tek bir dizge olarak yani "FatmaTuncay" biiminde ele alr. Bu dizge gsterici dizisinin sondan bir nceki elemanna atanr. Bu durumda dizinin son elemanna NULL adresi atanm olur deil mi? main ilevi iindeki for dngs de NULL adresine ular. phesiz bu bir gsterici hatasdr. Aada bir tamsayy bir yazya dntren syaz isimli bir ilev tanmlanyor. lev tanmlarnda yer alan gsterici dizilerinin kullanmn inceleyiniz: #include <stdio.h> void yuzyaz(unsigned int val) { static const char *birler[] = {"", "bir", "iki", "uc", "dort", "bes", "alti", "yedi", "sekiz", "dokuz"}; static const char *onlar[] = {"", "on", "yirmi", "otuz", "kirk", "elli", "altmis", "yetmis", "seksen", "doksan"}; int y = val / 100; int o = val % 100 / 10; int b = val % 10; if (y > 1) printf("%s", birler[y]); if (y > 0) printf("yuz"); if (o > 0) printf("%s", onlar[o]); if (b > 0) printf("%s", birler[b]);

void syaz(unsigned int val) { int milyar, milyon, bin, yuz; if (val >= 1000000000) { milyar = val / 1000000000; yuzyaz(milyar); printf("milyar"); } if (val > 1000000) { milyon = val % 1000000000 / 1000000;

305

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

yuzyaz(milyon); if (milyon) printf("milyon");

if (val > 1000) { bin = val % 1000000 / 1000; if (bin >= 1) yuzyaz(bin); if (bin > 1) printf("bin"); } yuz = val % 1000; yuzyaz(yuz);

int main() { unsigned int val; printf("bir tamsayi giriniz:"); scanf("%u", &val); syaz(val); printf("\n"); } return 0;

306

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

GSTERCY GSTEREN GSTERCLER


Gsterici deikenler ilerinde adres bilgisi tutan nesnelerdir. int *p; gibi bir tanmlamayla ismi p olan, bellekte 2 ya da 4 byte yer kaplayacak olan bir nesne yaratlm olur. Bu nesne int * trndendir. p bir nesne olduuna gre, p deikeninin de adresinden sz edilebilir. p deikeni adres ilecinin terimi olabilir: &p ifadesinin tr nedir? Bu ifade int * trnden bir nesnenin adresi olabilecek bir trdendir. Bu tr C dilinde (int **) tr olarak gsterilir. O zaman p gibi bir deikenin adresi bir baka nesnede tutulmak istenirse, p'nin adresini tutacak nesne int ** trnden olmaldr. Aadaki kod parasn inceleyin: void func() { int x = 10; int *p = &x; int **ptr = &p; /******/ } Yukardaki programda int ** trnden olan ptr isimli deikene, int * trnden olan p deikeninin adresi atanyor. Bunun anlam udur: ptr deikeninin deeri, p deikeninin adresidir. Baka bir deyile, ptr gstericisi p gstericisini gsterir. Bu durumda *ptr ifadesi ptr nesnesinin gsterdii nesnedir. Yani *ptr ifadesi p nesnesinin kendisidir. *ptr ifadesine yaplan atama aslnda p nesnesini deitirir. Aadaki program inceleyin: #include <stdio.h> int main() { int x = 10; int y = 20; int *p; int **pp; printf("x = %d\n", x); printf("y = %d\n", y); p = &x; *p = 100; pp = &p; *pp = &y; *p = 200; printf("x = %d\n", x); printf("y = %d\n", y);

307

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

**pp = 2000; printf("x = %d\n", x); printf("y = %d\n", y); return 0; } Yukardaki main ilevinde, int trden x deikeninin adresi, p isimli gsterici deikene atandktan sonra deyimiyle, p gstericisinin gsterdii nesneye yani x deikenine 100 deeri atanyor. *p = 100; pp = &x; deyimiyle ise p nesnesinin adresi pp isimli gstericiye atanyor. Bu atamadan sonra pp gstericisi p gstericisini gsterir. *pp = &y; deyimi ile pp gstericisinin gsterdii nesneye yani p deikenine bu kez y deikeninin adresi atanyor. Bu atamadan sonra yrtlecek *p = 200; atamas ile artk p gstericisinin gsterdii nesneye yani y deikenine 200 deeri atanr. imdi de aadaki deyimi inceleyin: **pp = 2000; erik ilecinin, ile ncelik tablosunun ikinci dzeyinde yer aldn ve sadan sola ncelik ynne sahip olduunu biliyorsunuz. Bu durumda nce *pp ifadesi ile pp gstericisinin gsterdii nesneye yani p nesnesine eriilir. Daha sonra *(*pp) ifadesiyle de pp gstericisinin gsterdii nesnenin gsterdii nesneye, yani p nesnesinin gsterdii nesneye, yani y deikenine ulalr. Deyimin yrtlmesiyle y deikenine 2000 deeri atanm olur. **pp ifadesi, pp'nin gsterdii nesnenin gsterdii nesneye, yani y nesnesine karlk gelir.

Yerel bir nesnenin deerini deitirecek bir ilev, yerel nesnenin adresi ile arlmaldr. Yerel bir gsterici deikenin deerini deitirecek bir ilev de, yerel gstericinin deerini deil adresini almaldr. Aadaki rnei inceleyin: void swap_ptr(int **p1, int **p2) { int *temp = *p1; *p1 = *p2; *p2 = temp; } Yukardaki programda int * trnden iki nesnenin deerini takas etmek amacyla swap_ptr isimli bir ilev tanmlanyor. levin int ** trnden iki parametresi olduunu gryorsunuz. Bu ilev phesiz deerlerini takas edecei nesnelerin adresleri ile arlmaldr:

Yerel Bir Gsterici Deikenin Deerini Deitiren levler

308

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { int x = 10; int y = 20; int *p = &x; int *q = &y; printf("*p = printf("*q = swap_ptr(&p, printf("*p = printf("*q = } return 0; %d\n", %d\n", &q); %d\n", %d\n", *p); *q); *p); *q);

main ilevi iinde int trden x ve y isimli deikenler tanmlanyor. Daha sonra tanmlanan p gsterici deikeni x nesnesini, q gsterici deikeni ise y deikenini gsteriyor. Daha sonra arlan swap_ptr ilevine p ve q gstericilerinin adresleri gnderiliyor. leve yaplan ardan sonra, p gstericisi y deikenini, q gstericisi ise x deikenini gsterir. imdi de aadaki ilevi inceleyin: void ppswap(int **p1, int **p2) { int temp = **p1; **p1 = **p2; **p2 = temp; } ppswap ilevi hangi nesnelerin deerlerini takas ediyor?

Bir dizi zerinde ilem yapan ilevin dizinin balang adresi ile dizinin boyutunu almas gerektiini biliyorsunuz. int trden bir dizi ile ilgili ilem yapan bir ilevin bildirimi aadaki gibi olabilir: void process_array(int *, int size); Byle bir ilev dizinin ilk elemannn adresi ve dizinin boyutu ile arlr, deil mi? int a[10]; gibi bir dizi sz konusu olduunda, dizi ismi olan a, ileme sokulduunda otomatik olarak bu dizinin adresine dntrlr. Yani derleyici asndan bakldnda a ifadesi &a[0] ifadesine edeerdir. Bu ilev process_array(a, 10); biiminde arlabilir. Bu kez elemanlar char * trden olan bir dizi tanmlanm olsun: char *a[100];

Bir Gsterici Dizisi zerinde lem Yapan levler

309

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yine a ifadesi bir ileme sokulduunda, bu dizinin ilk eleman olan nesnenin adresine, yazni dizinin balang adresine dntrlr. Bu dizinin ilk eleman olan nesne char * trnden olduuna gre, bu nesnenin adresi char ** trndendir. Byle bir dizi zerinde ilem yapacak ilev, bu dizinin balang adresi ile dizinin boyutunu alacana gre, aadaki gibi bildirilmelidir: void process_array(char **parray, int size); Bu ilev process_array(a, 10); biiminde arlabilir. imdi de aadaki program inceleyin: #include <stdio.h> #include <string.h> void swap_ptr(char **p1, char **p2) { char *temp = *p1; *p1 = *p2; *p2 = temp; } void display_str_array(char **p, int size) { int k; for (k = 0; k < size; ++k) printf("%s ", p[k]); printf("\n");

void sort_str_array(char **p, int size) { int i, k; for (k = 0; k < size - 1; ++k) for (i = 0; i < size - 1 - k; ++i) if (strcmp(p[i], p[i + 1]) > 0) swap_ptr(p + i, p + i + 1);

int main() { char *names[10] = {"Eda", "Abdurrahman", "Berk", "Zarife", "Yusuf", "Levent", "Sezgi", "Sukufe", "Ufuk", "Cansu"}; display_str_array(names, 10); sort_str_array(names, 10); getchar(); display_str_array(names, 10); return 0; }

310

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

OK BOYUTLU DZLER
C dilinde iki ya da daha ok boyuta sahip diziler tanmlanabilir: int a[5][10][20]; Yukardaki bildirimle a dizisi 3 boyutlu bir dizidir. int m[5][10]; m dizisi 2 boyutlu bir dizidir. Uygulamalarda daha ok kullanlan 2 boyutlu dizilerdir. 2 boyutlu diziler matris olarak da isimlendirilir.

C'de iki boyutlu bir dizi aslnda belirli bir boyuttaki tek boyutlu dizilerin dizisi olarak ele alnr: int a[5][10]; a dizisi her eleman 10 elemanl int trden bir dizi olan 5 elemanl bir dizidir. Yani a dizisinin gerekte boyutu 5' tir. kinci keli ayra iinde yer alan 10 ifadesi a dizisinin elemanlar olan dizilerin boyutudur. Bu dizi tm dier diziler gibi bellekte bitiik bir blok halinde bulunur. Yani derleyici byle bir dizi iin bellekte 50 x sizeof(int) byte byklnde bir blok ayarlar. ok boyutlu bir dizinin elemanlaryla, bu dizinin problem dzlemindeki karl olan matrisin elemanlarn birbirlerine kartrmak, sk yaplan hatalardandr. 5 x 10 boyutunda bir matrisin 50 eleman vardr. Ancak yukardaki a dizisinin yalnzca 5 eleman vardr. Yukardaki dizide yer alan toplam 50 tane int trden nesnenin her birine nasl ulalabilir? Bunun iin keli ayra ileci dizi ismiyle birlikte 2 kez kullanlabilir: Aadaki program inceleyin. Bu programda bir matriste yer alan tm elemanlara 0-100 aralnda rastgele deerler veriliyor. Daha sonra matriste yer alan tm elemanlarn deeri ekrana yazdrlyor: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define ROW COL 5 10

Matrisler

int main() { int a[ROW][COL]; int i, k; srand(time(0)); for (i = 0; i < ROW; ++i) for (k = 0; k < COL; ++k) a[i][k] = rand() % 100; /********/ 311

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

for (i = 0; i < ROW; ++i) { for (k = 0; k < COL; ++k) printf("%2d ", a[i][k]); printf("\n"); } } return 0;

Yukardaki kodu inceleyin. a dizisi 10 elemanl int trden dizilerin oluturduu bir dizidir. Yani a[0] bu dizilerden ilkine ve a[4] bu dizilerden sonuncusuna karlk gelir. a[0] bu dizilerden ilkine karlk geldiine gre a dizisinin ilk eleman olan 10 elemanl dizinin rnein 5 indisli elemanna a[0][5] biiminde ulalabilir, deil mi? Keli ayra ilecinin birinci ncelik seviyesinde ve soldan saa doru ncelik ynne sahip bir ile olduunu anmsayn. Byle bir iki boyutlu diziye, problem dzlemindeki karl olarak, yani bir matris olarak bakldnda, bu matriste yer alan ilk eleman a[0][0] nesnesidir. Bu nesne matrisin 1. satr ve 1. stununda yer alan nesnedir. Matrisin son eleman ise a[4][9] nesnesidir. Bu nesne matrisin 5. satr ve 10. stununda yer alan nesnedir. Matristeki ilk nesnenin adresi int trden bir gstericiye atanp bu gsterici srekli artrlrsa, matristeki tm elemanlarn adresleri elde edilerek son elemann adresine ulalabilir. imdi yukardaki rnee bir ekleme yapalm: int main() { int a[ROW][COL]; int i, k; int *ptr; srand(time(0)); for (i = 0; i < ROW; ++i) for (k = 0; k < COL; ++k) a[i][k] = rand() % 100; /********/ for (i = 0; i < ROW; ++i) { for (k = 0; k < COL; ++k) printf("%2d ", a[i][k]); printf("\n"); } printf("***********************************************\n"); ptr = &a[0][0];

312

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

i = ROW * COL; while (i--) printf("%2d ", *ptr++); printf("\n"); } return 0;

Daha nce yazlan main ilevine bir eklemenin yapldn gryorsunuz. ptr = &a[0][0]; deyimi ile int * trnden ptr nesnesine matris iinde yer alan ilk int trden nesnenin adresi atanr. i deikenine ROW * COL ifadesinin deeri, yani matriste yer alan toplam eleman says atanmtr. Bu durumda while (i--) dngs matrisin eleman says kadar dner, deil mi? Dngnn gvdesinde yer alan printf ars ile dngnn her turunda *ptr nesnesinin deeri yazdrlm ve daha sonra sonek konumunda olan ++ ileciyle gstericinin deeri 1 artrlm yani gstericinin bellekte bir sonraki int trden nesneyi gstermesi salanmtr. Bylece matris iinde yer alan btn deerler ekrana yazdrlmtr. Buradan u anlalmaldr: ki boyutlu dizinin elemanlar aslnda bellee ardl olarak yerletirilen tek boyutlu dizilerdir.

Aslnda iki boyutlu bir dizinin ileve geirilmesi tek boyutlu dizilerin ileve geirilmesinden farkl deildir. Bir diziyi ileve geirmek iin dizinin ilk elemannn adresi ve dizinin boyutu ileve gnderilir, deil mi? Peki yukardaki rnekte yer alan a gibi bir dizinin ilk eleman nedir? Bu dizinin ilk eleman a[0]'dr. Ve bu 10 elemanl int trden bir dizidir. imdi bu elemann adresinin alndn dnelim: &a[0] Bu ifadenin tr nedir? C diline yeni balayanlar byle bir ifadenin trnn (int **) tr olmas gerektiini dnrler. Oysa bu ifadenin tr daha nce karlamadmz bir trdr: 10 elemanl int trden bir dizinin adresi olabilecek bir tr! Bu tr biligisi C dilinde yle ifade edilir: int (*)[10]; Yani a dizisinin ilk eleman bu trden bir gstericiye atanabilir: int (*ptr)[10] = &a[0]; Bir dizinin ismi bir ifade iinde ileme sokulduunda otomatik olarak dizinin ilk elemann adresine dntrlr, deil mi? O zaman &a[0] ifadesi yerine dorudan a ifadesi de yazlabilir: int (*ptr)[10] = a; Evet, a ifadesinin tr int ** deil, int (*)[10] trdr.

ki Boyutlu Dizilerin levlere Geirilmesi

313

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu durum yle de ifade edilebilir: ptr int trden 10 elemanl bir diziyi gsteren gstericidir. ptr herhangi bir boyuttaki int trden bir diziyi deil, yalnzca 10 elemanl int trden bir diziyi gsterebilir. ptr gstericisi rnein ++ ileci ile 1 artrlrsa bellekte yer alan bir sonraki 10 elemanl int trden diziyi gsterir. Yani ptr + 1 adresinin saysal bileeni ptr adresinin saysal bileeninden sizeof(int) * 10 kadar daha byktr. ptr + 1 ifadesi iki boyutlu a dizisinin ikinci eleman olan 10 elemanl int trden dizinin adresine karlk gelir. Bu durumda *(ptr + 1) ifadesi aslnda a[1] dizisidir. phesiz *(ptr + 1) ifadesi yerine ptr[1] ifadesi de kullanlabilir. Bu durumda bu gsterici yardmyla iki boyutlu dizinin tm elemanlarna bir dng ile ulalabilir: for (k = 0; k < ROW; ++k) ptr[k] gibi bir dng deyimi ile dngnn her turunda matrisin bir satrna, yani iki boyutlu dizinin bir eleman olan COL uzunluundaki dizilere ulalr. Bu durumda 2 boyutlu bir dizi yani bir matris zerinde ilem yapacak bir ilevin parametre deikeni byle bir gsterici olabilir. levin dier parametresi de dizinin boyutunu alabilir. Aadaki program inceleyin: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define ROW COL 5 10

void fill_matrix(int (*ptr)[COL], int size) { int i, k; for (i = 0; i < size; ++i) for (k = 0; k < COL; ++k) ptr[i][k] = rand() % 100; } void display_matrix(int (*ptr)[COL], int size) { int i, k; for (i = 0; i < size; ++i) { for (k = 0; k < COL; ++k) printf("%2d ", ptr[i][k]); printf("\n"); }

314

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int main() { int a[ROW][COL]; srand(time(0)); fill_matrix(a, ROW); display_matrix(a, ROW); } return 0;

int (*ptr)[10] gibi bir gsterici bir ilevin parametre deikeni olarak kullanldnda int ptr[][10] biiminde de yazlabilir. Yani aadaki iki bildirim birbirine edeerdir: void fill_matrix(int (*ptr)[10], int size); void fill_matrix(int ptr[][10], int size); Hatta istenirse ilk keli ayra iine de bir tamsay yazlabilir. Ancak derleyici asndan bu tamsaynn bir nemi yoktur. Baz programclar yalnzca okunabilirlik asndan dizinin boyutunu ilk keli ayralarn iine yazarlar. Yukardaki bildirim yle de yaplabilirdi: void fill_matrix(int ptr[5][10], int size); Matrisleri ileve geirmenin bir baka yolu da matristeki ilk elemann adresini, matrisin satr ve stun saysn ileve geirmek olabilir. Yukarda yazdmz ilevleri imdi deitiriyoruz: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define ROW COL 5 10

void fill_matrix(int *ptr, int rowsize, int colsize) { int i, k; for (i = 0; i < rowsize; ++i) for (k = 0; k < colsize; ++k) ptr[i + k] = rand() % 100; } void display_matrix(int *ptr, int rowsize, int colsize) { int i, k; for (i = 0; i < rowsize; ++i) { for (k = 0; k < colsize; ++k) printf("%d ", ptr[i + k]); printf("\n"); } } int main() { int a[ROW][COL]; srand(time(0));

315

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

fill_matrix(&a[0][0], ROW, COL); display_matrix(&a[0][0], ROW, COL); } return 0;

Bu kez ilevler matristeki ilk elemann adresini, matrisin satr ve stun saysn alarak matristeki tm elemanlara, bunlarn bellekte bitiik olarak yer ald bilgisinden hareketle gsterici aritmetiiyle ulayor. Tabi bu ilevlere dizi ismi de argman olarak geilebilir. Bu durumda tr dntrme ileci kullanlmaldr: void fill_matrix((int *)a, ROW, COL); void display_matrix((int *)a, ROW, COL);

Dizi isimlerinin derleyici tarafndan otomatik olarak dizilerin ilk elemannn adresine dntrldn biliyorsunuz. Benzer biimde iki boyutlu bir dizinin eleman olan tek boyutlu bir diziye keli ayra ileci ile ulaldnda, bu ifade otomatik olarak bu tek boyutlu dizinin ilk elemannn adresine dntrlr. Aadaki rnei inceleyin: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define ROW COL 5 10

Matris Eleman Olan Dizilerin lk Elemanlarnn Adresi

void fill_array(int *ptr, int size) { int k; for (k = 0; k < size; ++k) ptr[k] = rand() % 100; } void display_array(const int *ptr, int size) { int k; for (k = 0; k < size; ++k) printf("%2d ", ptr[k]); printf("\n"); } Bu kez rnekte yer alan display_array isimli ilev, balang adresini ve boyutunu ald int trden bir dizinin elemanlarnn deerlerini ekrana yazdrrken, fill_array ilevi ise balang adresini ve boyutunun ald dizinin elemanlarna rastgele deerler atyor. int main() { int a[ROW][COL]; int i; srand(time(0)); for (i = 0; i < ROW; ++i) fill_array(a[i], COL);

316

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

/******/ for (i = 0; i < ROW; ++i) display_array(a[i], COL); } return 0;

main ilevi iinde yer alan for dng deyimini inceleyelim: for (i = 0; i < ROW; ++i) fill_array(a[i], COL); a[i] ifadesi dngnn her turunda a dizisinin eleman olan COL boyutundaki tek boyutlu dizilere karlk gelir deil mi? Bu ifade ileme sokulduunda derleyici tarafndan otomatik olarak bu dizinin ilk elemannn adresine dntrlr. a[0] ifadesi otomatik olarak &a[0][0] ifadesine dntrlr. fill_array isimli ileve ikinci argman olarak da COL ifadesi gnderilir. nk COL ifadesi adresi a[i] olan dizinin boyutunu gsterir. Bir dizinin tanm srasnda dizi boyutunu gsteren ifadelerin deimez ifadesi (constant expresion) olmas gerektiini biliyorsunuz. Ayn koul ok boyutlu diziler iin de geerlidir. Eer bir matrisin boyutlar derleme zamannda deil de programn alma zamannda belli oluyorsa dinamik bellek ynetimi kullanlmaldr. Bu konudaki rnekler "Dinamik Belek Ynetimi" blmnde ele alnmaktadr.

ki boyutlu dizilere de ilkdeer verilebilir. Verilen ilkdeerler srasyla iki boyutlu dizinin eleman olan tek boyutlu dizilerin elemanlarna atanr. #include <stdio.h> int main() { int a[3][5] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; int i, k; for (i = 0; i < 3; ++i) { for (k = 0; k < 5; ++k) printf("%2d ", a[i][k]); printf("\n"); } return 0;

ki Boyutlu Dizilere lkdeer Verilmesi

stenirse ilkdeerleri ieren bloun iinde isel bloklar kullanlarak, eleman olan dizilerin belirli sayda elemanna ilkdeer verilebilir. lk deer verilmeyen elemanlara otomatik olarak 0 deeri atanr. Aadaki rnei de derleyerek altrn: #include <stdio.h> int main() { int a[3][5] = { {1, 2}, {3, 4, 5}, {6, 7, 8, 9}}; int i, k; for (i = 0; i < 3; ++i) { for (k = 0; k < 5; ++k)

317

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("%2d ", a[i][k]); printf("\n"); } } return 0;

char Trden ki Boyutlu Diziler

Nasl bir yaz char trden bir dizinin iinde tutulabiliyor ise, mantksal bir iliki iindeki n tane yaz iki boyutlu bir dizi iinde tutulabilir: char words[10][50]; Yukarda tanmlanan words isimli dizinin 10 eleman vardr. words isimli dizinin her bir eleman char trden 50 elemanl dizidir. words dizisinin iinde uzunluu 49 karakteri gemeyen, 10 tane yaz tutulabilir. words[3] Bu yazlardan drdncsnn adresi words[6][2] Bu yazlardan yedincisinin nc karakteridir. Aadaki program inceleyin: #include <stdio.h> #include <string.h> #define ARRAY_SIZE 10

char *name_array[ARRAY_SIZE] = {"Ali", "Veli", "Hasan", "Necati", "Deniz", "Kaan", "Selami","Salah", "Nejla", "Figen"}; int main() { char names[ARRAY_SIZE][20]; int k; for (k = 0; k < ARRAY_SIZE; ++k) strcpy(names[k], name_array[k]); for (k = 0; k < ARRAY_SIZE; ++k) printf("(%s) ", names[k]); printf("\n"); for (k = 0; k < ARRAY_SIZE; ++k) strrev(names[k]); for (k = 0; k < ARRAY_SIZE; ++k) printf("(%s) ", names[k]); printf("\n"); } return 0;

main ilevi iinde iki boyutlu names isimli bir dizi tanmlanyor. char names[ARRAY_SIZE][20];

318

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu dizi iinde uzunluu en fazla 19 karakter olabilecek ARRAY_SIZE sayda isim tutulabilir, deil mi? for (k = 0; k < ARRAY_SIZE; ++k) strcpy(names[k], name_array[k]); dng deyimiyle name_array isimli bir gsterici dizisinin elemanlarnn gsterdii isimler, iki boyutlu names dizisinin eleman olan char trden dizilere standart strcpy ileviyle kopyalanyor. Aadaki dng deyimiyle her bir isim ekrana yazdrlyor: for (k = 0; k < ARRAY_SIZE; ++k) printf("(%s) ", names[k]); Aadaki dng deyimi ile standart olmayan strrev isimli ileve yaplan arlarla, iki boyutlu dizi iinde tutulan isimlerin hepsi ters evriliyor: for (k = 0; k < ARRAY_SIZE; ++k) strrev(names[k]);

char trden bir diziye ift trnak iinde yer alan karakterlerle ilkdeer verilebileceine gre iki boyutlu bir dizinin elemanlar olan char trden tek boyutlu dizinin elemanlarna da benzer ekilde ilkdeer verilebilir: char names[5][10] = {"Ali", "Veli", "Hasan", "Tuncay", "Deniz"}; Yukarda, names isimli iki boyutlu dizinin eleman olan 10 elemanl char trden dizilere, ift trnak iinde yer alan yazlarla ilkdeer veriliyor. imdi de iki boyutlu char trden bir dizi zerinde ilem yapacak baz ilevler tanmlayalm: #include <stdio.h> #include <string.h> #define ARRAY_SIZE 20

char Trden ki Boyutlu Dizilere lkdeer Verilmesi

void swap_str(char *p1, char *p2) { char temp[20]; strcpy(temp, p1); strcpy(p1, p2); strcpy(p2, temp); } void sort_names(char ptr[][20], int size) { int i, k; for (i = 0; i < size - 1; ++i) for (k = 0; k < size - 1 - i; ++k) if (strcmp(ptr[k], ptr[k + 1]) > 0) swap_str(ptr[k], ptr[k + 1]); } void display_names(const char ptr[][20], int size) { int i; for (i = 0; i < size; ++i)

319

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("(%s) ", ptr[i]); printf("\n"); } int main() { char names[ARRAY_SIZE][20] = {"Ali", "Veli", "Hasan", "Necati", "Deniz", "Kaan", "Selami","Salah", "Nejla", "Huseyin", "Derya", "Funda", "Kemal", "Burak", "Burcu", "Ozlem", "Nuri", "Metin", "Guray", "Anil"}; display_names(names, ARRAY_SIZE); sort_names(names, ARRAY_SIZE); printf("***********************************\n"); display_names(names, ARRAY_SIZE); } return 0;

Yukarda tanmlanan ilevlerden swap_str ilevi adreslerini ald iki yazy takas ediyor. display_names isimli ilev ise balang adresini ve boyutunu ald iki boyutlu dizide yer alan isimleri ekrana yazdryor. sort_names isimli ilev ise balang adresini ve boyutunu ald iki boyutlu dizi iinde tutulan isimleri kkten bye doru sralyor.

char * Trden Diziyle char Trden ki Boyutlu Dizi Arasndaki Farklar

Mantksal iliki iinde n tane yaz, bir gsterici dizisi yardmyla tutulabilecei gibi iki boyutlu bir dizi iinde de tutulabilir: char *pnames[10] = {"Ali", "Veli", "Hasan", "Deniz", "Ferda", "Murat", "Ayca", "Erdem", "Kaan", "Gurbuz"}; char names[10][20] = {"Ali", "Veli", "Hasan", "Deniz", "Ferda", "Murat", "Ayca", "Erdem", "Kaan", "Gurbuz"}; Elemanlar dizgeleri gsteren bir gsterici dizisinin elemanlar, yalnzca okuma amacyla kullanlabilecek yazlarn balang adreslerini tutar. Dizgelerin yalnzca okuma amacyla kullanlabilecek yazlar olduunu anmsamalsnz. Yukardaki rnekte, pnames dizisinin eleman olan gstericilerin gsterdii yazlar zerinde deiiklik yapmak, doru deildir. Ancak names dizisi iinde yer alan isimler istenirse deitirilebilir. Aadaki main ilevini inceleyin: #include <string.h> int main() { int k; for (k = 0; k < 10; ++k) { strrev(pnames[k]); strrev(names[k]); } } return 0; /* Yanl */ /* Yanl deil*/

320

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir C programnn almas main ilevinden balar, main ilevinin sonunda, ya da bu ilevin geri dn deeri retmesiyle, sonlanr. Program, eer bir baka ilevin altrlmas srasnda sonlandrlmak istenirse, standart exit ya da abort ilevleri arlabilir.

exit, abort atexit levleri

Standart bu ilev stdlib.h balk dosyas iinde bildirilmitir: void exit(int status); lev, altrlmakta olan program sonlandrmadan nce aadaki temizlik ilerini yapar: i) atexit ileviyle daha nce kayt edilmi ilevleri kayt edilmelerine gre ters sra iinde arr. ii) Yazma amal alm tm dosyalarn tampon alanlarn (buffer) boaltr (flush). Ak durumda olan tm dosyalar kapatr. iii) tmpfile ileviyle alm olan dosyalar siler. iv) Kontrolu, programn altrld sisteme, baar durumunu ileten bir bilgiyle geri verir. leve gnderilen deer 0 ya da EXIT_SUCCESS simgesel deimezi ise, ilev, sisteme programn baar nedeniyle sonladrld bilgisini iletir. leve gnderilen argman 1 ya da EXIT_FAILURE simgesel deimezi ise, ilev, sisteme programn baarszlk nedeniyle sonlandrld bilgisini iletir.

exit levi

abort levi

Standart bu ilev stdlib.h balk dosyas iinde bildirilmitir: void abort(void); lev bir C programn olaand biimde sonlandrmak amacyla arlr. Bir C program abort ilevine yaplan ar ile sonlandrldnda, atexit ileviyle daha nce kayt edilmi ilevler arlmaz. abort ars ile programn sonlanmasndan nce baz temizlik ilemlerinin yaplp yaplmayaca derleyicinin seimindedir. Standart assert makrosu iinde de bu ilev arlr. atexit levi Standart bu ilevin bildirimi yine stdlib.h balk dosyas iindedir. int atexit (void (*func)(void)); atexit ilevi ile, program sonlandnda ya da exit ilevi arldnda, arlmas istenen bir ilev kaydedilir. Program normal d yollarla, rnein abort ya da raise ileviyle sonlandrldnda, kaydedilen ilevler arlmaz. atexit ileviyle en az 32 ilev kaydedilebilir. levin parametresi, kaydedilecek ilevin adresidir. levin geri dn deeri ilemin baar durumunu iletir. 0 geri dn deeri, ilemin baarsn, 0 d bir deer ilemin baarszln gsterir. Kaydedilen bir ilevi kayttan karmann bir yolu yoktur. Kaydedilen ilevler kaydedildikleri sra ile ters srada arlrlar. Bir ilev birden fazla kez kaydedilebilir. Bu durumda birden fazla kez altrlr.

321

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DNAMK BELLEK YNETM


altrlabilen bir program bir bellek alann kullanr. Nesneler programn kulland bellek alanndaki bloklardr. Bir ya da birden fazla nesnenin programn alma zamannda kaplayaca yer iki ayr biimde elde edilebilir: 1. Nesne(ler) iin derleyici derleme zamannda bellekte bir yer ayarlar. 2. Nesne(ler) iin yer programn alma zamannda elde edilir. Programn alma zaman srasnda belli bir byklkte bitiik (contigous) bir bellek alannn alan program tarafndan ayrlmasna , byle bir alann istenildii zaman sisteme geri verilmesine olanak salayan yntemlere "dinamik bellek ynetimi" (dynamic memory management) denir. C dilinde bir deiken ya da bir dizi tanmland zaman, bu deiken ya da dizinin programn alma zamannda kaplayaca yer, derleme zamannda derleyici tarafndan ayrlr: int a[100]; Derleme srasnda yukardaki gibi bir dizi tanm ile karlaan derleyici bellekte -eer kullanlan sistemde int tr uzunluunun 2 byte olduu varsaylrsa- toplam 200 byte yer ayrr. Programn almas srasnda bir dizinin uzunluunu deitirmek mmkn deildir. Eer bu dizi yerel ise otomatik mrldr. Yani iinde tanmlanm olduu bloun kodunun yrtlmesi sresince hayatn srdrr. Dizi global ise statik mrldr. Yani dizi, programn alma sresi boyunca bellekteki yerini korur. Dinamik bellek ynetimi aralar kullanlarak bir ya da birden fazla nesnenin programn alma zamannda kaplayaca yerin elde edilmesi derleme zamanndan, alma zamanna geciktirilebilir. Kullanlacak bellek blounun elde edilmesi neden derleme zamanndan alma zamanna kaydrlsn? Bunun nemli baz nedenleri vardr. lerleyen sayfalarda bu nedenlerin ouna deinilecek. Bellek alannn programn alma zamannda elde edilmesinin en nemli nedenlerinden biri gereksinim duyulan bellek blounun byklnn programn alma zamannda belli olmasdr. Dizi tanmlamalarnda dizi boyutunu gsteren ifade yani keli ayracn iindeki ifade, deimez ifadesi olmaldr. Bu ifade deiken ieremez. nk derleyicinin dizi iin bellekte yer ayrabilmesi iin, dizi boyutunu derleme zamannda bilmesi gerekir. Oysa birok uygulamada kullanlmas gereken dizinin boyutu programn alma zamannda belirlenir. Bir dizindeki dosyalarn isimlerinin kkten bye doru sralanmak amacyla geici olarak bir dizide saklanmas gerektiini dnn. Bu amala kullanlacak dizinin boyutu ne olmaldr? Bu balangta belli deildir. nk dizin iinde ka dosya olduu belli deildir. Bu tip durumlara zellikle veri taban uygulamalarnda sk rastlanr. Baz uygulamalarda dizilerin gerek uzunluu programn almas srasnda, ancak birtakm olaylar sonucunda kesin olarak belirlenebilir. Bu durumda dizilerle alan programc herhangi bir gsterici hatasyla karlamamak iin dizileri en kt olasl gz nnde bulundurarak mmkn olduunca byk tanmlamak zorundadr. Bu da bellein verimsiz kullanlmas anlamna gelir. stelik tanmlanan diziler yerel ise tanmlandklar bloun sonuna kadar, tanmlanan diziler global ise programn almasnn sonuna kadar bellekte tutulur. Oysa dizi ile ilgili ilem biter bitmez, dizi iin ayrlan bellek blgesinin boaltlmas verimli bellek kullanm iin gereklidir. Bellein kontrolu iletim sistemindedir. letim sistemlerinin baka bir amala kullanlmayan bir bellek alann kullanma sunmasna yarayan sistem ilevleri vardr. phesiz bu ilevler dorudan arlabilir. Ancak uygulamalarda ounlukla standart C

Dinamik Bellek Ynetimi Nedir

323

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ilevleri kullanlr.Yazlan kaynak kodun tanabilirlii asndan standart C ilevleri tercih edilmelidir. Aada dinamik bellek ynetiminde kullanlan standart C ilevlerini tek tek ayrntl olarak inceleniyor:

malloc ilevi
malloc ilevi programn alma zaman srasnda bellekten dinamik bir blok elde etmek iin kullanlr. levin stdlib.h balk dosyas iindeki bildirimi aadaki gibidir: void *malloc(size_t nbyte); size_t trnn, unsigned int ya da unsigned long trlerinden birinin typedef ismi olduunu biliyorsunuz. lev, elde edilmek istenen bloun byte olarak uzunluunu alr. Ayrlan alann bitiik (contiguous) olmas gvence altna alnmtr. malloc ilevinin geri dn deeri elde edilen bellek blounun balang adresidir. Bu adres void trden olduu iin, herhangi bir trden gstericiye sorunsuz bir ekilde atanabilir. Bu adres herhangi bir trden gstericiye atand zaman artk elde edilen blok, balang adresini tutan gsterici yardmyla bir nesne ya da bir dizi gibi kullanlabilir. malloc ilevinin istenilen blou ayrmas gvence altnda deildir. malloc ilevi birok nedenden dolay baarsz olabilir. Bellekte elde edilmek istenen alan kadar bo bellek alannn bulunmamas sk grlen bir baarszlk nedenidir. malloc ilevi baarsz olduunda NULL adresine geri dner. lev arsnn baars mutlaka snanmaldr. malloc ilevi ile bellekte bir blok elde edilmesine ilikin aada bir rnek veriliyor: #include <stdio.h> #include <stdlib.h> int main() { int *pd; int k, n; srand(time(0)); n = rand() % 100 + 10; pd = (int *) malloc(n * sizeof(int)); if (pd == NULL) { printf("cannot allocate memory\n"); exit(EXIT_FAILURE); } printf("rastgele %d sayi\n", n); for (k = 0; k < n; ++k) { pd[k] = rand() % 100; printf("%d ", pd[k]); } /***/ } Yukardaki main ilevinde, n deikeninin deeri programn alma zamannda elde standart rand ilevine yaplan ar ile rastgele olarak elde ediliyor. Daha sonra standart malloc ileviyle n tane int trden nesnenin sabilecei kadar byklkte bir bellek blou elde ediliyor.

324

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

malloc ilevinin baars bir if deyimiyle snanyor. Baarszlk durumunda, yani malloc ilevinin geri dn deerinin NULL adresi olmas durumunda standart exit ilevi arlarak program sonlandrlyor. Daha sonra elde edilen dinamik blok int trden bir dizi olarak kullanlyor. Dinamik dizinin elemanlarna 0 - 100 aralnda rastgele deerler atanyor. Kaynak kodun tanabilirlii asndan kullanlacak bellek blounun bykl sizeof ileciyle elde edilmelidir. phesiz, malloc ilevi baarsz olduunda program sonlandrmak zorunlu deildir. Atama ile snama bir defada da yaplabilir. if ((pd = (int *) malloc(n * sizeof(int)) == printf("cannot allocate memory\n"); exit(EXIT_FAILURE); } NULL) {

malloc ilevinin baars mutlaka snanmaldr. levin baarsz olmas durumunda, geri dn deeri olan adresten okuma ya da yazma yaplmas, NULL adresinin ieriinin alnmasna neden olur. Bu da bir gsterici hatasdr. Programclar ou kez, kk miktarlarda ok sayda bloun ayrlmas durumunda, snamay gereksiz bulma eilimindedir. Oysa snama ileminden vazgemek yerine daha kolaylatrc yntemler denenmelidir. rnein p1, p2, p3, p4, p5 gibi 5 ayr gsterici deikenin her biri iin n byte dinamik alan elde edilmek istensin. Bu durumda snama mantksal ileler ile tek bir if deyimi ile yaplabilir. #include <stdio.h> #include <stdlib.h> int main() { char *p1, *p2, *p3, *p4, *p5; p1 p2 p3 p4 p5 = = = = = (char (char (char (char (char *)malloc(n); *)malloc(n); *)malloc(n); *)malloc(n); *)malloc(n);

if (!(p1 && p2 && p3 && p4 && p5)) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } /*....*/

malloc arlarndan herhangi biri baarsz olursa !(p1 && p2 && p3 && p4 && p5) ifadesi "doru" olarak yorumlanacandan if deyiminin doru ksm yaplr. Bazen de malloc ilevi programc tarafndan tanmlanan baka bir ilev ile sarmalanr: #include <stdio.h> #include <stdlib.h> void *cmalloc(size_t n) { void *pd = malloc(n);

325

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (!pd) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } return pd; } Yukarda tanmlanan cmalloc isimli ilevin parametrik yaps malloc ileviyle ayn olduunu gryorsunuz. lev malloc ilevinden farkl olarak, baarszlk durumunda program sonlandryor. malloc ile yaplacak bir dinamik blok elde etme giriiminin baarsz olmas durmunda program bir hata iletisi verilerek sonlandrlacaksa, kaynak kodun srekli yinelenmesi yerine, cmalloc ilevi arlabilir. malloc ilevinin geri dn deeri void trden bir adres olduu iin, bu adres sorunsuzca herhangi bir trden bir gsterici deikene atanabilir. Ancak okunabilirlik asndan malloc ilevinin geri dn deeri olan adresin, tr dntrme ileci yardmyla, kullanlacak gsterici deikenin trne dntrlmesi nerilir. Bylece malloc ilevi ile elde edilen bloun hangi trden nesne ya da nesnelermi gibi kullanlaca bilgisi kodu okuyana verilmi olur. C dilinin standartlatrlmasndan nceki dnemde yani klasik C dneminde, void trden gstericiler olmad iin, malloc ilevinin geri dn deeri char trden bir adresti. Bu durumda, geri dn deeri olan adresin, char tr dnda bir gstericiye atanmas durumunda tr dnm bir zorunluluktu. malloc ilevi birden fazla kez arlarak birden fazla dinamik alan elde edilebilir. malloc ilevine yaplan farkl arlarla elde edilen bloklarn bellekte bitiik olmas gvence altnda deildir. Bu yzden, her bir ilev arsnn geri dn deeri olan adres mutlaka bir gsterici deikende saklanmaldr. Aadaki kod paras ardk malloc arlarnn bitiik bellek bloklar elde edeceini varsayd iin yanltr: #include <stdio.h> #include <stdlib.h> int main() { int *pd; int i; pd = (int *)malloc(sizeof(int) * 10); if (!pd) { printf("cannot allocate memory!\n"); exit(EXIT_FAILURE); } if (malloc(sizeof(int) * 10) == NULL) { printf("cannot allocate memory!\n"); exit(EXIT_FAILURE); } /* Yeni blok eski bloun hemen altnda olmak zorunda deildir */ for (i = 0; i < 20; ++i) pd[i] = i; /*...*/ return 0; } malloc ilevi ile elde edilen bloun iinde p deerler vardr. Aadaki kod paras ile bu durumu snaynz:

326

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> int main() { int *pd; int i; pd = (int *) malloc(10 * sizeof(int)); if (!pd) { printf("yetersiz bellek!..\n"); exit(EXIT_FAILURE); } for (i = 0; i < 10; ++i) printf("pd[%d] = %d\n", i, pd[i]); return 0; } Dinamik bellek ilevlerinin kullanmna sunulan bellek blgesi ngilizce'de heap olarak isimlendirilir. C++ dilinde bu alan free store olarak isimlendirilir. heap alannn bykl sistemden sisteme deiebilir ancak alabilir bir program sz konusu olduunda, altrlacak programn bellee yklenmesiyle, belirli byklkte bir heap alan programn kullanmna ayrlr. Sistemlerin ounda malloc ilevinin parametre deikeni unsigned int (size_t) trnden olduuna gre, malloc ilevi ile DOS altnda en fazla 65535 byte yani 64KB byklnde bir blok elde edilebilir. Oysa UNIX, WINDOWS gibi 32 bitlik sistemlerde unsigned int tr 4 byte uzunluunda olduuna gre, bu sistemlerde teorik olarak, malloc ilevi ile 4294967295 byte (4 MB) uzunluunda bitiik bir blok elde edilebilir. Tabi bu durum, dinamik yer ayrma ileminin gvence altnda olduu anlamna gelmez. Heap alan da snrldr. Srekli malloc ilevinin arlmas durumunda, belirli bir noktadan sonra ilevler baarsz olarak NULL adresine geri dner. Aada, kullanlan heap alannn bykl bir kod ile elde edilmeye allyor: #include <stdio.h> #include <stdlib.h> #define BLOCK_SIZE int main() { long total = 0L; void *pd; for (;;) { pd = malloc(BLOCK_SIZE); if (!pd) break; total += BLOCK_SIZE; } printf("toplam heap alan = %ld byte\n", total); system("pause"); return 0; } Yukardaki program DOS iletim sistemi iin nce BLOCK_SIZE simgesel deimezinin 2048 deeri ile derleyerek altrn. Daha sonra program BLOCK_SIZE deimezinin deerini 1 yaparak yeniden derleyin ve altrn. Toplam deerin ok daha kldn greceksiniz. 2048

327

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

malloc ilevi dinamik blok elde etme ilemini programn alma zamannda yerine getirir. Dinamik bellek ilevleriyle elde edilen bloklarn kontrol edilebilmesi iin heap alannda uygun bir veri yaps ile bir tablo tutulur. Tutulan tabloda elde edilmi her bir dinamik bloun balang adresi ve boyutu vardr. Bu tablonun kendisi de heap alanndadr. Bir program altrldnda heap alan iki ayr biimde tketilir: i) malloc/calloc/ realloc ilevleri tarafndan elde edilen bloklarn kullanma sunulmasyla ii) her bir yer ayrma ilemi iin ilgili tabloya bir bilginin daha eklenmesi ile.

Dinamik bellek ilevlerine yaplan ardan elde edilen bir blok standart ilevlerinden free ilevine yaplan ar ile sisteme geri verilebilir. free ilevinin bildirimi de stdlib.h balk dosyas iindedir: void free(void *block); free ilevine daha nce malloc, calloc ya da realloc ilevleriyle elde edilmi olan bir bellek blounun balang adresi geilmelidir. free ilevi arsyla bu blok heap alanna geri verilmi olur. heap alanna geri verilen blok malloc, calloc ya da realloc ilevleri tarafndan yeniden elde edilme potansiyeline girer. Grld gibi free ilevi geri verilecek bellek blounun yalnzca balang adresini alr, daha nce ayrlm bellek blounun boyutunu istemez. alma zamannda heap alannda tutulan tablolarda, zaten elde edilmi bellek bloklarnn boyutu deeri tutulmaktadr. free ilevine argman olarak daha nce dinamik bellek ilevleriyle elde edilmi bir bellek blounun balang adresi yerine baka bir adres gnderilmesi tanmsz davrantr, yaplmamaldr. #include <stdlib.h> int main() { char *p1, *ptr; char s[100]; ptr = (char *)malloc(20); free(ptr); free(p1); free(s); } return 0; /* Geerli ve doru*/ /* Yanl */ /* Yanl */

Elde Edilen Dinamik Bloun Geri Verilmesi

Yukardaki rnekte free(p1) ars bir alma zaman hatasna neden olur. nk p1 adresinde dinamik bellek ilevleri ile elde edilmi bir alan yoktur. free(s) ars da bir alma zaman hatasna neden olur. nk s dizisi iin yer, dinamik bellek alanndan ayrlmamtr. Daha nce dinamik olarak elde edilmi bir blok free ilevi ile heap alanna geri verilir, ama bu bloun balang adresini tutan gsterici herhangi bir ekilde deitirilmez. Bloun balang adresini tutan, free ilevine argman olarak gnderilen gsterici, free ilevine yaplan ardan sonra artk gvenli olmayan bir adresi gsterir. Geri verilen bir

328

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

bellek blouna ulamak sk yaplan bir gsterici hatasdr. Aadaki kodu derleyerek altrn: #include <stdio.h> #include <stdlib.h> #include <string.h> #define ARRAY_SIZE 100

int main() { char name_entry[ARRAY_SIZE]; char *pd; printf("bir isim giriniz: "); gets(name_entry); pd = (char *)malloc(strlen(name_entry + 1)); if (pd == NULL) { printf("dinamik bir blok elde edilemiyor!\n"); exit(EXIT_FAILURE); } strcpy(pd, name_entry); printf("yazi = %s\n", pd); free(pd); printf("yazi = %s\n", pd); } return 0;

/* Yanl! */

Yukardaki main ilevi iinde nce klavyeden alnan bir isim name_entry isimli yerel diziye yerletiriliyor. Daha sonra arlan malloc ilevi ile klavyeden alnan ismin uzunluundan 1 byte daha byk bir bellek blou elde ediliyor. Elde edilen bellek blouna yerel dizideki isim kopyalanyor. Daha sonra dinamik bloktaki isim ekrana yazdrlyor. Yaplan free(pd) arsyla dinamik blok heap alanna geri veriliyor. Ancak geri verme ileminden sonra yaplan printf arsyla geri verilen bloa ulalyor. Bu bir gsterici hatasdr. Dinamik bellek ilevleri dinamik yer ayrma ve dinamik blou geri verme ilemlerini programn alma zamannda gerekletirirler. free ilevine ilikin tipik bir hata da daha nce elde edilen bir dinamik bloun, free ileviyle kltlmeye allmasdr. Aadaki kodu inceleyelin: #include <stdlib.h> int main() { char *pd; pd = (char *)malloc(1000); /****/ free(pd + 500); /* Yanlis */ /****/ free(pd); return 0; }

329

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki main ilevinde nce malloc ilevi ile 1000 byte'lk bir blok elde edilerek bloun balang adresi pd gsterici deikenine atanyor. Daha sonra arlan free ilevine pd + 500 adresinin argman olarak gnderildiini gryorsunuz. Byle bir ar ile free ilevinin daha nce ayrlan 1000 byte'lk alann 500 byte'n geri vermesi sz konusu deildir. pd + 500 adresi bir dinamik alann balang adresi deildir. Bu yzden ilev ars tanmsz davran zellii gsterir. Bir malloc ars ile elde edilen dinamik alan free ilevine yaplan bir aryla kltlemez. free ilevine dinamik bir bloun balang adresi dnda baka bir adresin geilmesi tanmsz davrantr. Bu durumun tek istisnas free ilevine NULL adresinin geilmesidir. leve NULL adresinin geilmesi tanmsz davrana neden olmaz, yalnzca bir ileme neden olmayan (no operation) ardr: if (ptr) free(ptr); ptr bir gsterici deiken olmak zere yukardaki if deyiminde ptr deikeninin deeri NULL adresi deilse free ilevi arlyor. Bu if deyimi yerine free ilevi dorudan arlsa da kodun anlamnda fazlaca bir deiiklik olmazd.

malloc ilevi ile elde edilen bir bellek blou boyutu programn alma zamannda elde edilen bir dizi olarak kullanlabilir. Aadaki program derleyerek altrn: #include <stdio.h> #include <stdlib.h> #include <time.h> void set_array(int *ptr, int size) { int k; for (k = 0; k < size; ++k) ptr[k] = rand() % 100; } void display_array(const int *ptr, int size) { int k; for (k = 0; k < size; ++k) printf("%2d ", ptr[k]); printf("\n"); } int main() { int n; int *pd; printf("kac tane tamsayi : "); scanf("%d", &n); pd = (int *)malloc(sizeof(int) * n); if (pd == NULL) { printf("cannot allocate memory!\n"); exit(EXIT_FAILURE); } srand(time(0));

Dinamik Bir Dizinin Kullanlmas

330

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

set_array(pd, n); display_array(pd, n); free(pd); return 0; } Yukardaki programda programn alma zamannda ka nesnenin istendii bilgisi kullancdan standart scanf ileviyle n deikenine alnyor. Daha sonra malloc ileviyle n tane int trden nesnenin saca kadar byklkte bir dinamik bellek blou elde ediliyor. Elde edilen dinamik bellek blounun balang adresinin pd isimli gstericiye atandn gryorsunuz. Artk balang adresi pd ve boyutu n olan bir dizi sz konusudur, deil mi? set_array ve display_array ilevleri bu dizinin balang adresi ve boyutu ile arlyor.

calloc ilevi malloc ilevine ok benzer. stdlib.h balk dosyas iindeki bildirimi aadaki gibidir. void *calloc(size_t nblock, size_t block_size); calloc ilevi heap alanndan birinci parametresi ile ikinci parametresi arpm kadar byte'lk bir bellek blounu elde etmek iin kullanlr. levin geri dn deeri, yer ayrma ileminin baarl olmas durumunda, elde edilen bellek blounun balang adresidir. Yer ayrma ilemi baarl olmazsa calloc ilevi de malloc ilevi gibi NULL adresine geri dner. Elemanlar int trden olan 100 elemanl bir dizi iin dinamik yer ayrma ilem calloc ilevi ile aadaki gibi yaplabilir: void func() { int *pd; /***/ pd = (int*) calloc(100, sizeof(int)); if (pd == NULL) { printf("cannot allocate memory\n"); exit(EXIT_FAILURE); } /***/ } phesiz yukardaki kod parasnda calloc ilevi u ekilde de arlabilirdi: pd = (int*) calloc(sizeof(int), 100); calloc ilevinin malloc ilevinden baka bir fark da elde ettii bellek blounu sfrlamasdr. calloc ilevi tarafndan elde edilen blounun tm byte'larnda sfr deerleri vardr. malloc ilevi calloc ilevine gre ok daha sk kullanlr. Ayrlan blok sfrlanacaksa malloc ilevi yerine calloc ilevi tercih edilebilir. Yeri heap alanndan elde edilen n elemanl int trden bir dizinin elemanlar sfrlanmak istensin. Bu ilem malloc ilevi ile yaplrsa, dizinin elemanlar ayr bir dng deyimi ile sfrlanmaldr: int main() { int *pd; int i; int n = rand() % 100 + 10; pd = (int *) malloc(sizeof(int) * n);

calloc levi

331

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

/***/ for (i = 0; i < n; ++i) ptr[i] = 0; /***/

Oysa calloc ilevi zaten sfrlama ilemini kendi iinde yapar: pd = calloc(sizeof(int) , n); Derleyicilerin ounda calloc ilevi, bellekte yer ayrma ilemini yapmas iin, kendi iinde malloc ilevini arr.

Dinamik bellek ilevleriyle heap alanndan elde edilen bir blok free ilevi arsna kadar tutulur. Dinamik bloun balang adresi kaybedilirse, artk programn sonuna kadar bu blou kullanma ans olmad gibi blok heap alanna geri de verilemez. Bu durum bellekte sznt (memory leak) olarak isimlendirilir. Bir ilev iinde dinamik bir bloun elde edildiini ancak ilev iinde bu bloun heap alanna geri verilmediini dnn: void process() { char *pd = (char *)malloc(1000); if (pd == NULL) { printf("dinamik bir blok elde edilemiyor!\n"); exit(EXIT_FAILURE); } /***/ } process isimli ilev her arldnda 1000 byte'lk bir alan elde ediliyor. Ancak ilev iinde elde edilen dinamik alan heap alanna geri verilmiyor. Bu durumda process ilevine yaplan her ar ile heap alanndan 1000 byte'lk bir alan bloke edilmi olur. process ilevin ana program iinde ok sk arlan bir ilev olduu dnlrse, belirli bir ar saysndan sonra heap alan tmyle tketilebilir. Yani belirli bir sre sonra artk malloc ilevi yeni bir blou elde edemeyecek duruma gelir.

Bellekte Sznt

realloc ilevi
realloc ilevi daha nce malloc ya da calloc ilevleriyle elde edilmi bir bellek blounu bytmek ya da kltmek iin arlr. levin bildirimi stdlib.h balk dosyas iindedir. void *realloc(void *block, size_t newsize); realloc ilevine gnderilen birinci argman, daha nce elde edilen bir bellek blounun balang adresi, ikinci argman ise yeni bloun toplam uzunluudur. realloc ilevi ile dinamik bir blok bytlmek istendiinde, ilev, blou daha yksek saysal adrese doru bytmek zorunda deildir. realloc ilevi, daha nce elde edilmi bloun bittii yerde, istenilen uzunlukta bir blok bulamazsa ya da dinamik alan bu ynde bytmek istemezse, bu kez bloun tamam iin bellekte kullanlmayan baka bir yer aratrr. Eer istenilen toplam uzunlukta ardl bir blok bulunursa buray ayrr, eski bellek bloundaki deerleri yeni yere tar ve eski bellek blounu sisteme geri verir. stenen uzunlukta srekli bir alan bulunamazsa ilev NULL adresiyle geri dner. Aadaki programda yaplan dinamik bellek ilemlerini inceleyin:

332

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> int main() { int *pd; int k; int n, nplus; printf("kac tamsay : "); scanf("%d", &n); pd = (int *) malloc(sizeof(int) * n); if (!pd) { printf("bellek yetersiz!..\n"); exit(EXIT_FAILURE); } for (k = 0; k < n; ++k) pd[k] = k; printf("kac tamsay eklenecek: "); scanf("%d", &nplus); pd = (int *) realloc(pd, sizeof(int) * (n + nplus)); if (pd == NULL) { printf("bellek yetersiz!..\n"); exit(EXIT_FAILURE); } for (; k < n + nplus; ++k) pd[k] = k; for (k = 0; k < n + nplus; ++k) printf("%d ", pd[k]); printf("\n"); free(pd); return 0; } malloc ilevi ile int trden n tane nesnenin saca kadar byklkte bir dinamik alan elde ediliyor. malloc ilevi baarl olamazsa standart exit ileviyle program sonlandrlyor. Elde edilen dinamik alandaki n tane nesneye bir dng yardmyla atamalar yaplyor. Daha sonra arlan realloc ileviyle elde edilen dinamik alan nplus tane nesneyi daha iine alabilecek biimde geniletiliyor. Eer realloc ilevi baarsz olursa program sonlandrlyor. Ardndan realloc ilevinin geri dn deeri olan adresindeki n + nplus adet nesneye sahip dizi yine bir dng deyimiyle yazdrlyor. realloc ilevi baarl olmusa iki olaslk sz konusudur: 1. realloc ilevi daha nce elde edilen dinamik alann hemen altnda yani daha yksek saysal adreste, ilk blou bytecek biimde bo alan bularak ek bellek alann buradan salamtr. Bu sistemlerin ounda pek karlalan bir durum deildir. Bu durumda realloc ilevinin geri dn deeri olan adres ilevin birinci parametresine gnderilen adrestir.

333

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

2. realloc ilevi daha nce ayrlan bloun altnda bo yer bulamam ya da bu ynde bir bytme ilemini tercih etmemi olabilir. Ve heap alannda toplam (n + nplus) * sizeof(int) kadar byte'lk baka bir bo blok ayarlam olabilir. Bu durumda realloc ilevi daha nce elde edilmi alandaki deerleri de bu alana kopyalayarak eski blou sisteme geri vermitir. realloc ilevinin bu davranndan dolay geri dn deeri bir gsterici deikene mutlaka atanmaldr. Aada sk yaplan tipik bir hata grlyor: #include <stdio.h> #include <stdlib.h> int main() { char *p; p = malloc(10); if (p == NULL) { printf("can not allocate memory!..\n"); exit(EXIT_FAILURE); } /***/ if (realloc (p, 20) == NULL) { printf("can not allocate memory!..\n"); exit(EXIT_FAILURE); } gets(p); } return 0;

realloc ilevinin baarl olmas daha nce elde edilen bloun altna yani daha byk saysal adrese doru ek blok salad anlamna gelmez. realloc ilevi daha nce elde edilmi dinamik alan baka bir yere tayarak yeni bloun balang adresiyle geri dnm olabilir. Bu durumda eski blok serbest braklr, artk bu adresin bir gvenilirlii yoktur. Yukardaki rnekte realloc ilevinin, daha nce elde edilen blou bulunduu yerde bytt varsaylyor. Eer realloc ilevi bellek blounu baka bir yere tayarak bytmse, yukardaki kodda bir gsterici hatas yaplm olur, nk artk p gstericisinin gsterdii adres gvenilir bir adres deildir. Bu yzden uygulamalarda genellikle realloc ilevinin geri dn deeri, realloc ilevine deeri gnderilen gstericiye atanr: ptr = realloc(ptr, 100); Ancak bu bir zorunluluk deildir. Eer realloc ilevi ile yaplan yer ayrma ileminin baarl olmamas durumunda program sonlandrlmayacaksa ve daha nce dinamik olarak elde edilen blgedeki deerler bir dosyaya yazlacak ise, artk realloc ilevinin baarsz olmas durumunda, ptr gstericisine NULL adresi atanaca iin ptr gstericisinin daha nce elde edilen blok ile ilikisi kesilmi olur. Byle bir durumda geici bir gsterici kullanmak uygun olur: temp = realloc(ptr, 100); if (temp == NULL) printf("cannot allocate memory!..\n);

334

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu durumda ptr gstericisi halen daha nce elde edilen bloun balang adresini gsteriyor. C standartlar realloc ilevi ile ilgili olarak aadaki kurallar getirmitir: i) Daha nce elde edilen bir bellek blounu bytmesi durumunda, realloc ilevi bloa eklenen ksma herhangi bir ekilde deer vermez. Yani eski bloa eklenen yeni blok iinde p deerler (garbage values) bulunur. realloc ilevi eer daha nce elde edilmi bellek blounu bytemez ise NULL adresi ile geri dner ancak daha nce elde edilmi olan ve bytlemeyen bellek bloundaki deerler korunur. ii) Eer realloc ilevine gnderilen birinci argman NULL adresi olursa, realloc ilevi tamamen malloc ilevi gibi davranr. Yani: realloc(NULL, 100); ile malloc(100); arlar tamamen ayn anlamdadr. Her ikisi de 100 byte'lk bir dinamik alan elde etmeye alr. Buna neden gerek grlmtr? Yani neden malloc(100) gibi bir ar yapmak yerine realloc(NULL, 100) eklinde bir ar tercih edilebilir? Aadaki program inceleyin: #include #include #include #include <stdio.h> <ctype.h> <stdlib.h> <conio.h>

void display_array(const int *ptr, int size); int get_max(const int *ptr, int size); int get_min(const int *ptr, int size); double get_ave(const int *ptr, int size); double get_stddev(const int *ptr, int size); int main() { int *ptr = NULL; int ch, grade; int counter = 0; for (;;) { printf("not girmek istiyor msunuz? [e] [h]\n"); while ((ch = toupper(getch())) != 'E' && ch != 'H') ; 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); }

335

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ptr[counter - 1] = grade;

if (counter == 0) { printf("hibir not girii yapmadnz!..\n"); return 0; } printf("toplam %d tane not girdiniz\n", counter); printf("girdiiniz notlar aada listeleniyor :\n"); display_array(ptr, counter); printf("\nen buyuk not : %d\n", get_max(ptr, counter)); printf("en kk not : %d\n", get_min(ptr, counter)); printf("notlarn ortalamas : %lf\n", get_ave(ptr, counter)); printf("notlarn standart sapmas . %lf\n", get_stddev(ptr, counter)); free(ptr); return 0; } Yukardaki programda: ptr = (int *) realloc(ptr, sizeof(int) * counter); deyiminde, dngnn ilk turunda ptr gstericisinin deeri NULL olduu iin realloc ilevi malloc gibi davranacak ve int trden 1 adet nesnelik yer ayrr. Ancak dngnn daha sonraki turlarnda realloc ilevine gnderilen adres NULL adresi olmayacandan, daha nce elde edilen blok dng iinde srekli olarak bytlm olur. Eer realloc ilevine gnderilen ikinci argman 0 olursa, realloc ilevi tamamen free ilevi gibi davranr: realloc(ptr, 0); ile free(ptr); tamamen ayn anlamdadr. Buna neden gerek grlmtr? Yani neden free(ptr) gibi bir ar yapmak yerine realloc(ptr, 0) eklinde bir tercih edilsin?

Dinamik olarak ayrlan bir blok, free ileviyle serbest braklarak sisteme geri verilene kadar gvenli olarak kullanlabileceine gre, bir ilev byle bir bloun balang adresine geri dnebilir. Aadaki ilevi inceleyin:

Dinamik Olarak Elde Edilen Alann Balang Adresine Geri Dnen levler

336

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <string.h> #include <stdlib.h> char *getname() { char s[30]; char *ptr; printf("ismi giriniz : "); gets(s); ptr = (char *) malloc(strlen(s) + 1); if (ptr == NULL) { printf("cannot allocate memory!..\n"); exit(EXIT_FAILURE); } return strcpy(ptr, s); } getname ilevinde nce klavyeden bir isim yerel bir diziye alnyor. Daha sonra strlen ilevi kullanlarak yerel diziye alnan ismin uzunluu bulunuyor. malloc ilevi ile bu ismi ve sonuna gelecek sonlandrc karakteri iine alacak byklkte bir alan dinamik olarak elde ediliyor. Daha sonra da strcpy ilevi kullanlarak, isim dinamik olarak elde edilen alana kopyalanyor ve bu bloun balang adresine geri dnlyor. Dinamik bir yer ayrma ilemi yapan ilevin arlmas sonucunda ayrlan bloun geri verilmesi, ilevi arann kodun sorumluluunda olur: int main() { char *p = /*****/ free(p) /*****/ return 0; }

getname();

Bir ilev iinde dinamik olarak bir bellek blou ayrmak ve daha sonra bu bloun balang adresine geri dnmek C dilinde ok kullanlan bir tekniktir. Aada kaynak kodu verilen strcon ilevi birinci parametresinde balang adresi tutulan yaznn sonuna ikinci parametresinde balang adresi tutulan yazy kopyalyor; fakat her iki adresteki yazlar da bozmadan, birletirilmi yaznn balang adresi olan bir adrese geri dnyor: #include <string.h> #include <stdlib.h> char *strcon(const char *s1, const char*s2) { char *ptr = malloc (strlen(s1) + strlen(s2) + 1); /* Basari sinamasi */ strcpy(ptr, s1); strcat(ptr, s2); } return ptr;

337

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

levimiz, adresleri gnderilen yazlar dinamik bir alana kopyalayarak, dinamik alann balang adresiyle geri dnyor. strcpy ve strcat ilevleri, birinci paramatrelerinin deerleriyle geri dndnden strcpy(ptr, s1); strcat(ptr, s2); return ptr; deyimleri tek bir deyim olarak yazlabilirdi, deil mi? return strcat(strcpy(ptr, s1), s2);

Dinamik bellek ilevleriyle elde edilen farkl bloklarn balang adreslerinin bir gsterici dizisinin elemanlarnda tutulmas ok karlalan bir temadr. Aadaki program inceleyin: #include <stdio.h> #include <stdlib.h> #include <string.h> char *get_name() { char name_entry[40]; char *pd; printf("ismi giriniz : "); gets(name_entry); pd = (char *) malloc(strlen(name_entry) + 1); if (pd == NULL) { printf("bellek yetersiz!..\n"); exit(EXIT_FAILURE); } return strcpy(pd, name_entry); } int main() { char *p[10]; int k; int len_sum = 0; for (k = 0; k < 10; ++k) p[k] = get_name(); printf("isimler : \n"); for (k = 0; k < 10; ++k) { printf("(%s) ", p[k]); len_sum += strlen(p[k]); } printf("\n"); printf("girilen isimlerin ortalama uzunlugu = %lf\n", (double)len_sum / 10); for (k = 0; k < 10; ++k) free(p[k]); return 0; }

Birden Fazla Dinamik Alann Bir Gsterici Dizisiyle Denetimi

338

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Baz durumlarda gsterici dizisinin kendisi de dinamik olarak yaratlr. Aadaki programda satr ve stn says klavyeden girilen bir matrisin elemanlarna nce 0 - 100 arasnda rastgele deerler veriliyor sonra matris ekrana yazdrlyor: #include <stdio.h> #include <stdlib.h> #include <time.h> int main() { int no_of_rows, no_of_cols; int **prows; int i, k; srand(time(0)); printf("satir sayisi : "); scanf("%d", &no_of_rows); printf("sutun sayisi : "); scanf("%d", &no_of_cols); prows = (int **)malloc(sizeof(int *) * no_of_rows); if (prows == NULL) { printf("dinamik bellek elde edilemiyor!\n"); exit(EXIT_FAILURE); } for (k = 0; k < no_of_rows; ++k) { prows[k] = (int *) malloc(sizeof(int) * no_of_cols); if (prows[k] == NULL) { printf("dinamik bellek elde edilemiyor!\n"); exit(EXIT_FAILURE); } } /****/ for (i = 0; i < no_of_rows; ++i) for (k = 0; k < no_of_cols; ++k) prows[i][k] = rand() % 100; /****/ for (i = 0; i < no_of_rows; ++i) { for (k = 0; k < no_of_cols; ++k) printf("%2d ", prows[i][k]); printf("\n"); } /****/ for (i = 0; i < no_of_rows; ++i) free(prows[i]); free(prows); } return 0;

imdi program inceleyin. Matrisin satr ve stun saysnn kullanc tarafndan klavyeden girilmesi salanyor. Matrisin satr says nrows stun says ncols'dur. Matris nrows tane ncols uzunluunda int trden dinamik diziden oluur. Yani matrisin her bir satr bir dinamik dizidir. Bu dinamik dizilerin balang adresleri yine dinamik bir gsterici dizisinde tutulur. nce nrows tane int * trnden nesne iin dinamik bir alann ayrldn gryorsunuz: prows = (int **)malloc(sizeof(int *) * no_of_rows);

339

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki dng ile bu dinamik dizinin her bir elemannn int trden ncols uzunluunda dinamik bir dizinin balang adresini tutulmas salanyor. for (k = 0; k < no_of_rows; ++k) { prows[k] = (int *) malloc(sizeof(int) * no_of_cols); Yer ayrma ilemleri tamamlandktan sonra prows[i][k] ifadesiyle matrisin i ve k indisli elemanna ulalabilir. nk prows[i] (int *) trnden bir nesnedir. Bu nesnenin deeri dinamik bir dizinin balang adresidir. Keli ayra ilecinin ile ncelik tablosunun birinci dzeyinde olduunu, bu ncelik dzeyinin soldan saa ncelik ynne sahip olduunu hatrlayn. Bu durumda nce daha soldaki keli ayra ileci bir deer retir. Soldaki keli ayra ilecinin rettii (int *) trnden deer bu kez ikinci keli ayra ilecine terim olur. Bu ile de satrlardan herhangi birini oluturan int trden dinamik dizinin bir elemanna ulatrr. Bu durumda ulalan int trden nesne, matrisin [i][k] indisli eleman olur. Ayrlan dinamik alanlarn geri verilmesinde dikkatli olunmaldr. nce matrisin satrlarn oluturan dinamik diziler heap alanna geri verilmeli, daha sonra ise dinamik dizilerin balang adreslerini tutan gsterici dizisinin geri verilme ilemi gerekletirilmelidir: for (i = 0; i < no_of_rows; ++i) free(prows[i]); free(prows);

340

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BELRLEYCLER ve NTELEYCLER
Belirleyiciler (storage class specifiers), bildirimler yaplrken kullanlan ve nesnelerin zellikleri hakknda derleyicilere bilgi veren anahtar szcklerdir. C'de kullanlan belirleyiciler unlardr: auto, register, static, extern ve typedef. Tr niteleyicileri ise nesnelerin iindeki deerlerin deitirilip deitirilmeyeceine ilikin bilgi verir. Tr belirleyicileri unlardr: const, volatile. Yer belirleyici ve tr niteleyiciler C'nin anahtar szckleridir.

Yer belirleyici, tr belirleyici ya da tr ifade eden anahtar szcklerin dizilimi herhangi bir biimde olabilir: auto const unsigned long int const auto unsigned long int unsigned long int auto const int const long auto unsigned a; a; a; a;

Yer Belirleyicilerle Bildirim lemi

Yukardaki bildirimlerin hepsi geerlidir. Ancak okunabilirlik asndan yer belirleyici szcn daha nce yazlmas nerilir. Eer bir bildirimde belirleyicisi ya da tr niteleyicisi bir tr bilgisi olmadan kullanlrsa, nceden seilmi (default) olan int tr bildirimin yapld kabul edilir. register int a; Yukardaki bildirim ile register a; bildirimi edeerdir. Ancak okunabilirlik asndan byle bir bildirim nerilmez.

auto Belirleyicisi
auto yalnzca yerel deikenler iin kullanlabilecek bir yer belirleyicisidir. auto belirleyicisinin global deikenlerin ya da ilevlerin parametre deikenlerininin bildiriminde kullanlmas geersizdir. Bu anahtar szck, nesnenin bilinirlik alan bittikten sonra kaybolacan, bellekte kaplad yerin geerlilii kalmayacan gsterir. Yerel deikenler otomatik mrldr. Yani bulunduklar bloa ilikin kod almaya balandnda yaratlr, sz konusu bloun yrtlmesi bittikten sonra yok olurlar. te auto belirleyicisi bu durumu vurgulamak iin kullanlr. Zaten bir yerel deiken, baka bir yer belirleyici anahtar szck kullanlmad srece (default olarak) auto biiminde ele alnr. Bu durumda auto yer belirleyicisinin kullanm gereksizdir: { } auto int a; float b;

341

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

auto yer belirleyicisi global deikenlerle ya da parametre deikenleriyle birlikte kullanlmaz. rnein : auto int a; function(auto int x) { /***/ } /* Geersiz! */ /* Geersiz! */

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 iinde" tutulmas isteini derleyiciye ileten bir anahtar szcktr. Deikenlerin bellek yerine dorudan yazmalarda tutulmas programn almasn hzlandrr. Yazma (register) nedir? Yazmalar CPU (central processing unit) iinde bulunan tampon bellek blgeleridir. CPU iindeki aritmetik ve mantksal ilemleri yapan birimin yazmalar ve belleklerle ilikisi vardr. Genel olarak CPU tarafndan yaplan aritmetik ve mantksal ilemlerin her iki terimi de bellee ilikin olamaz. rnein bellekte bulunan sayi1 ve sayi2 isimli iki deiken toplanarak elde edilen deer sayi3 isimli baka bir bellek blgesine yazlmak istensin. Bu C'deki sayi3 = sayi1 + sayi2; ilemine karlk gelir. CPU bu ilemi ancak 3 admda gerekletirebilir: 1. adm : nce sayi1 bellekten CPU yazmalarndan birine ekilir. MOV reg, sayi1 2. adm : Yazma ile sayi2 toplanr. ADD reg, sayi2 3. adm: Toplam sayi3 ile belirtilen bellek alanna yazlr. MOV sayi3, reg Bellee yazma ve bellekten okuma ilemleri yazmalara yazma ve yazmalardan okuma ilemlerine gre daha yavatr. nk bellee eriim iin bir makine zaman gerekir. register belirleyicisi derleyiciye yalnzca bir istei iletir. Yani register belirleyicisi ile bildirilen deikenin yazmata tutulacann bir gvencesi yoktur. Derleyiciler byle bir istei yerine getirmeyebilir. CPU yazmalar hangi sistem sz konusu olursa olsun snrl saydadr. Bu nedenle birka deikenden fazlas register belirleyicisi ile tanmlanm olsa bile yazmalarda saklanmayabilir. C derleyicileri yazmalarda saklayamayacaklar deikenler iin genel olarak hata veya uyar iletileri vermez. Yani derleyiciler, tutabilecekleri yazma saysndan fazla register belirleyicisine sahip deikenlerle karlatklarnda bunlara ilikin register belirleyicilerini dikkate almaz. register belirleyicileri ancak yerel ya da parametre deikenleri ile kullanlabilir. Global deikenler ile kullanlamazlar. rnekler: register int g; /* Geersiz! */

int func (register int y) /* Geerli */ { register int x; /* Geerli */ }

342

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

En fazla ka tane deikenin yazmalarda saklanabilecei bilgisayar donanmlarna ve derleyicilere baldr. Ayrca, uzunluu tamsay (int) trnden byk olan trler genellikle yazmalarda saklanamaz. Bu durumlarda da derleyicilerden hata veya uyar iletisi beklenmemelidir. Sonu olarak register belirleyicisi hzn nemli olduu ok zel ve ksa kodlarda ancak birka deiken iin kullanlmaldr. Modern derleyicilerin ou seime bal olarak kod zerinde eniyileme (optimizasyon) yaparak baz deikenleri yazmalarda saklar. Bu durum da ou zaman register anahtar szcnn kullanlmasn gereksiz klar.

static Belirleyicisi
static belirleyicisi ancak yerel ya da global deikenlerin bildiriminde kullanlabilir. Bu belirleyici, parametre deikenlerinin bildiriminde kullanlamaz. static anahtar szcnn global ve yerel deikenlerin bildiriminde kullanlmas farkl anlamlara gelir.

static yer belirleyicisi ile tanmlanm yerel deikenler ya da yerel diziler programn almas boyunca bellekte kalr. Baka bir deyile, static anahtar szc yerel deikenlerin mrn otomatik mrden statik mre ykseltir. Statik yerel deikenler tpk global deikenler gibi programn almaya balamasyla yaratlr ve programn yrtlmesi bitene kadar da bellekte tutulur. Statik yerel deikenler programc tarafndan ilkdeer verildikten sonra kullanlr. lkdeer verme ilemi programn almas srasnda deil, derleme zamannda derleyici tarafndan yaplr. Derleyici bellekten yer ayrlmasna yol aacak makine kodunu oluturur. Statik yerel deiken iin bellekte yer programn yklenmesi srasnda ayrlr. Derleme zamannda gerekte bellekte yer ayrlmaz. Bellekte yer ayrma iini yapacak makine kodu retilir. Statik yerel deikenler ilkdeerleriyle birlikte bellee yklenir. Aadaki program derleyerek altrn: #include<stdio.h> void func1() { int x = 5; printf("x = %d\n", x); x++; } void func2() { static int y = 5; printf("y = %d\n", y); y++; } int main() { int k; printf("func1 ilevi 5 kez arlyor!\n"); for (k = 0; k < 5; ++k) func1(); printf("\nfunc2 ilevi 5 kez arlyor!\n"); for (k = 0; k < 5; ++k) func2(); return 0; }

static Anahtar Szcnn Yerel Deikenlerin Bildiriminde Kullanlmas

343

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki program iinde tanmlanan func1 ilevi her arldnda x deikeni yaratlr ve ilevin almas sonlandnda x deikeni bellekten boaltlr. Her ne kadar ilevin sonlanmasndan nce x deikeninin deeri 1 artrlsa da, bunun bir sonraki arya hibir etkisi olmaz. func1 ilevinin bir sonraki arsnda x deikeni yine 5 deeri ile balatlr. Oysa func2 ilevi iin durum deiiktir. func2 ilevi iindeki y isimli yerel deiken blok bilinirlik alanna ait fakat statik mrldr. y deikenine ilkdeer verme deyimi derleyici tarafndan retilen kodun bir parasdr. y deikeni programn banda 5 deeri ile balatlr ve programn sonuna kadar bellekteki yerini korur. lev ka kez arlrsa arlsn, bellekteki yeri deimeyen ayn y deikeni kullanlr. Bu durumda ilevin ana blounun sonunda y deikeninin deeri 1 artrld iin bir sonraki arda artk y deikeninin deeri 6 olur. Programa ilikin ekran ktsn aada veriyoruz. func1 ilevi 5 kez arlyor! x = 5 x = 5 x = 5 x = 5 x = 5 func2 ilevi 5 kez arlyor! y = 5 y = 6 y = 7 y = 8 y = 9

Statik yerel nesnelerin kullanlmasna ilikin ana temelarda, bu nesnelerin mrlerinin statik olmasndan faydalanlr. Bu temalarda global deikenler de pekala kullanlabilir. Ancak global deikenler statik mrl olmalaryla birlikte dosya bilinirlik alanna aittir. Oysa statik yerel deikenler blok bilinirlik alanna aittir. Global deikenlerin dosya bilinirlik alanna sahip olmalarndan kaynaklanan dezavantajlardan etkilenmemek iin bir ok durumda ilevlerin statik mrl nesne gereksinimi statik yerel deikenlerle karlanr.

statik Yerel Nesnelerin Kullanlmasna likin Ana Temalar

Bir adres deerine geri dnen ilevlerin, yerel deikenlerin adreslerine geri dnmemeleri gerektiini biliyorsunuz. nk yerel deikenler otomatik mrldr ve ilevin almas sonunda bellekten boaltlr. Ancak static anahtar szc ile tanmlanm yerel deikenler, programn sonlanmasna kadar bellekteki yerlerini koruyacandan, ilevin statik bir yerel deikenin adresine geri dnmesinde bir saknca yoktur: #include <stdio.h> char *get_name() { static char entry[100]; printf("bir isim giriniz : "); gets(entry); return entry; } Ancak statik bir yerel dizinin adresiyle geri dnmek, dinamik bir dizinin yani malloc ileviyle yeri elde edilmi bir dizinin adresiyle geri dnmekten farkldr. lev her arldnda ayn adresi dndrr. Aadaki main ilevini inceleyin:

levlerin Statik Yerel Nesnelerin Adresleriyle Geri Dnmeleri

344

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { char *p[10]; int k; for (k = 0; k < 10; ++k) p[k] = get_name(); /*...*/ for (k = 0; k < 10; ++k) printf("isim = (%s)\n", p[k]); } return 0;

Yukardaki main ilevinde programc 10 elemanl bir gsterici dizisi tanmlanyor. Gsterici dizisinin her bir elemanna get_name ilevinin arlarndan elde edilen geri dn deerleri olan adresler atanyor. Ancak get_name ilevi her defasnda ayn statik yerel dizinin adresiyle geri dnyor. Kullanc tarafndan yaplan isim girii her defasnda bir nceki giriin zerine yazlr. main ilevinin sonunda gsterici dizisinin tm elemanlar ayn yazy gsterir, deil mi? Baz standart C ilevleri de statik yerel dizilerin adreslerine geri dner. rnein time.h balk dosyas iinde bildirimi yaplan ctime ve asctime ilevleri statik bir dizi iinde tutulan yaznn balang adresiyle geri dner.

Statik Yerel Dizilerin Kullanlmasyla Salanan Verim Art


Diziler de static anahtar szcyle tanmlanabilirler. void func() { static char alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; /**/ } Yukardaki rnekte, func ilevi iinde alphabet dizisi static anahtar szcyle tanmland iin, bir kez ilkdeer verildikten sonra her arldnda bu deerleri korur ve varln programn sonuna kadar srdrr. Oysa static anahtar szc kullanlmasayd, bu dizi func ilevine yaplan her arda yeniden yaratlacak ve dizinin elemanlarna yaplan ilkdeer atamalar her defasnda yeniden yaplacakt. Bu da ileve yaplan her ar iin ek bir makine zamannn harcanmasna neden olacakt.

Baz ilevler daha nceki arlaryla mantksal bir ba oluturarak ilerini gerekletirirler. rnein LIMIT pozitif bir tamsay olmak zere 0 LIMIT aralnda rastgele say retecek bir ilev tanmlamak istediimizi dnelim. lev daha nce retmi olduu bir sayy bir daha retmesin. lev bir saynn daha nce retilip retilmemi olduu bilgisini statik bir yerel bayrak dizisinde saklayabilir. Aadaki program derleyerek altrn: #include #include #include #include #define <stdio.h> <string.h> <stdlib.h> <time.h> LIMIT 20

levlerin Daha nceki arlaryla Mantksal Ba kurmas

345

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int urand() { static char flags[LIMIT] = {0}; int ret_val; if (!memchr(flags, 0, sizeof(flags))) return -1; while (flags[ret_val = rand() % LIMIT]) ; flags[ret_val]++; } return ret_val;

int main() { int k; srand(time(0)); for (k = 0; k < LIMIT; ++k) printf("%d ", urand()); printf("\n"); printf("%d\n", urand()); } return 0;

urand ilevi iinde flags isimli statik bir yerel dizinin elemanlar 0 deeriyle balatlyor. Dizinin her bir eleman ilgili deerin daha nce retilip retilmediini gsteren bir bayrak olarak kullanlyor. rnein ilev arldnda 12 deerinin daha nce retilip retilmedii flags dizisinin 12 indisli elemannn deerinden anlalyor. Bu elemann deeri 0 ise 12 deeri retilmemi, 1 ise 12 deeri retilmi demektir. lev nce standart memchr ileviyle dizinin iinde 0 deerinin olup olmadna bakyor. Dizinin hibir elemannn deeri 0 deilse, tm deerler retilmi demektir. lev -1 deerine geri dnerek bu durum hakknda bilgi iletiyor. Bayrak dizisinde en az bir 0 deeri var ise, bir dng iinde srekli rastgele deer retiliyor. retilen deer, ismi ret_val olan bir deikene atanyor. Bayrak dizisinin ret_val indisli elemannn deeri 1 olduka dng devam ediyor. Dngden klmas iin daha nce retilmemi bir deerin retilmesi gerekiyor. Dngden kldktan sonra ret_val deikeninin deer, geri dndrlecek deerdir. Ancak bu deer geri dndrlmeden nce bayrak dizisinin ret_val indisli elemannn deeri 1 yaplyor. Bylece bir sonraki arda artk bu deeri yeniden retilmesi engelleniyor.

Statik yerel deikenler mr asndan global deikenler ile ayn zellikleri gsterirler. Yani programn almaya balamas ile yaratlrlar ve programn almas bitince mrleri sona erer. Ama bilinirlik alan (scope) asndan farkllk gsterirler. Global deikenler dosya bilinirlik alanna uyarken statik yerel deikenler blok bilinirlik alanna uyar. Statik yerel deikenlerin blok bilinirlik alanna sahip olmas verilerin gizlenmesini (data hiding) kolaylatrr ve kodun yeniden kullanlabilirliini (reusability) artrr. nk global deikenlere bal olan ilevler bir projeden bir baka projeye kolayca tanamazlar.

Statik Yerel Deikenler ile Global Deikenlerin Karlatrlmas

Modl Kavram ve Nesnelerin Balant zellii

Bir proje birbirinden bamsz olarak derlenebilen birden fazla kaynak dosyadan oluabilir. Projelerin bamsz olarak derlenebilen her bir kaynak dosyasna "modl" denir.

346

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir projeyi oluturan farkl kaynak dosyalar birbirinden bamsz olarak derlendikten sonra, hepsi birlikte balayc (linker) ile birletirilerek tek bir altrlabilir dosyay olutururlar.

Eer btn modller tek bir kaynak kod iinde birletirilirse en ufak bir deiiklikte tm proje tekrar derlenmek zorundadr. Oysa modllere ayrlm projelerde, yalnz deiikliin yapld modln derlenmesi yeterlidir. nk dier modller zaten derlenmitir ve onlar yalnzca balama aamasnda ilem grrler. Programlar modller halinde yazmann bir dier avantaj da grup almas yaparken ortaya kar. Bu durumda projelerin bamsz paralar (modlleri) ayr kiiler tarafndan hazrlanabilir. Dier taraftan modller de ilevler gibi yeniden kullanlabilen yaplardr. Bir modl birden fazla projede kullanlabilir.

Byk Projelerin Modllere Ayrlmasnn Faydalar

Projeyi oluturan kaynak dosyalarn birinde tanmlanan bir isme, projeyi oluturan baka bir kaynak dosya iinde ulalabilir mi? Bu kaynak dosyalar en sonunda tek bir alr dosyay oluturacaklarna gre, baz durumlarda bir modlde tanmlanan bir nesneye baka bir modlde ulamak istenmesi son derece doaldr. Bir modlde tanmlanm bir deikenin baka bir modlde kullanlp kullanlmayacan gsteren zellie o nesnenin balant zellii (linkage) denir. Balant zellii bilinirlik alanndan farkldr. Bilinirlik alan derleyiciyi ilgilendiren yani derleme zamanna ilikin bir kavramdr. Bir nesne bilinirlik alan dnda kullanlrsa derleme zamannda hata oluur. Ancak balant, balayc program ilgilendiren yani balama zamanna ilikin bir kavramdr. Bir nesne, balantsnn olmad bir modlde kullanlrsa balama zamannda hata oluur. C standartlarna gre nesneler balant zellikleri asndan 3 ana gruba ayrlr. 1. D balantya sahip nesneler (external linkage) Eer bir nesne tanmlandktan sonra, bu nesneye hem kendi modlnn iinde her yerde hem de dier modllerde ulam mmknse "nesnenin d balants vardr" denir. 2. balantya sahip nesneler (internal linkage) Eer bir nesne tanmlandktan sonra bu nesneye kendi modlnn iinde her yerde ulalabiliyorsa, ancak projeyi oluturan dier modllerde bu nesneye ulalamyorsa, "nesnenin i balants vardr" denir. 3. Balantsz Nesneler (no linkage) Kendi modlnde ancak belirli bir blok iinde bilinen nesnelerdir. Bir ilevin parametre deikenleri ve bir ilev iinde tanmlanan yerel nesneler balantsz nesnelerdir. Bu nesnelere projeyi oluturan dier modllerde ulamak mmkn deildir. Global deikenler normal olarak d balantya sahiptir. Yani bir global deikene baka bir modl iinde ulalabilir. Peki projeye ait bir kaynak dosya iinde global bir deiken tanmland zaman baka bir kaynak dosya iinde bu deiken nasl kullanlabilir?

Balant Kavram

347

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

extern belirleyicisi ile bir bildirim yaplr. Bir deikenin extern belirleyicisi ile bildirilmesi derleyiciye nesnenin baka bir modlde tanmlandn anlatr. Bir proje mod1.c ve mod2.c isimli iki kaynak dosyadan olumu olsun : MOD1.C int a; float func1() { /***/ a = 100; /***/ } MOD2.C int func2() { ... a = 300; /* HATA */ }

extern Belirleyicisi

mod1.c dosyasnda tanmlanm olan a deikeni global bir deikendir ve d balantya sahip olduu iin normal olarak proje iindeki dier kaynak dosyalarda da kullanlabilir. mod2.c dosyasnda da bu deikene ulalabilir. Fakat iki modln ayr ayr derlendii yukardaki rnekte problemli bir durum sz konusudur. nk mod2.c dosyasnn derlenmesi srasnda derleyici a deikeninin mod1.c kaynak dosyas iinde global olarak tanmlandn bilemez. mod2.c kaynak dosyasn derlerken a deikeni hakknda bilgi bulamayan derleyici bu durumu hata olarak belirler. C dilinde bir deiken ancak nceden bildirilmise kullanlabilir, deil mi? extern belirleyicisi derleyiciye, ilgili global deikeninin kendi modl iinde deil de bir baka modl iinde tanml olduunu bildirme amacyla kullanlr. mod2.c modlnde a deikeninin extern anahtar szc ile bildirilmesi durumunda sz konusu sorun ortadan kalkar. mod2.c extern int a; /*extern bildirimiyle a deikeninin baka bir modlde tanmlanm olduu belirtiliyor*/ int func2() { a = 300; /***/ } extern bildirimini gren derleyici, deikenin projeye ait baka bir modlde tanmlandn varsayarak hata durumunu ortadan kaldrr. Ancak derleyici makine kodu retirken extern anahtar szc ile bildirilmi bir deikenin bellekteki yerini saptayamayacandan, bu ilemi btn modlleri gzden geirecek olan balayc programa brakr. Bylece deikenin tanmland modl bulup extern olarak bildirilmi olanlarla ilikilendirme ilemi balayc program (linker) tarafndan yaplr. Yani extern belirleyicisi ile programc derleyiciye, derleyici ise balaycya bildirimde bulunur. extern belirleyicisini gren derleyici bellekte bir yer ayrmaz. extern bildirimi bir tanmlama deildir, bildirimdir. Aslnda yalnz deikenler iin deil ilevler iin de extern belirleyicisinin kullanlmas sz konusudur. C derleyicileri kendi modlleri iinde tanmlanmadklar halde arlan standart C ilevleri gibi- ilevleri otomatik olarak extern kabul ederler. Bu nedenle ilev bildirimlerinde ayrca extern belirleyicisini yazmaya gerek yoktur. nk derleyici tarafndan yazlm varsaylr.

348

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

rnein yukarda verilen rnekte mod2c modlnde bulunan y1 ilevi iinde bulunan x1 ilevi arlyor olsun: /****mod1.c int a; *****/

float x1() { a = 100; } mod2.c extern int a; extern float x1(void); int y1() { float f; f = x1(); a = 300; /***/ } mod2.c modlnde x1 ilevi iin yazlm olan prototip ifadesini inceleyin: extern float x1(void); Bu rnekte x1 ilevi baka bir modlde tanml olduu iin bildiriminde extern anahtar szc kullanlyor . Ancak extern belirteci eklenmeseydi derleyici zaten extern varsayacakt. Bir global deiken hibir modlde tanmlanmadan, btn modllerde extern olarak bildirilirse, tm modller hatasz bir ekilde derlenebilir. Hata balama aamasnda, balaycnn extern olarak bildirilen nesneyi hibir modlde bulamamas biiminde ortaya kar. extern belirleyicisinin tek bir modl sz konusu olduunda "ama d" bir kullanm vardr. Aadaki rnekte main ilevi iindeki global x deikeni, tanmlamadan nce kullanldndan hataya neden olur. int main() { /***/ x = 100; /***/ } int x; /* x global bir deiken ama tanmndan daha nce kullanlm */ int func() { x = 200; /***/ }

Yukardaki kod derlendiinde, main ilevinin iindeki x deikeninin bildiriminin bulunamadn ifade eden bir hata iletisiyle karlalr. Bu durumda, eer bir global deiken tanmlamadan kullanlyorsa, hata olumamas iin daha nce extern bildiriminde bulunulmaldr.

349

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

extern int x; int main() { /**/ x = 100; /**/ } int x; int func() { x = 200; /***/ } extern bildirimini bu ekilde kullanmak yerine, global deikeni programn en banda tanmlamak daha iyi bir tekniktir.

static Anahtar Szcnn Global Deikenler le Kullanlmas


static belirleyicisi global bir deiken ile birlikte kullanlrsa, bu deikeni i balantya sahip yapar. Yani static anahtar szcyle tanmlanm bir global deiken i balantya sahiptir. Byle bir deiken extern bildirimiyle projeyi oluturan dier modllerde kullanlmaz, yalnzca kendi modlnde kullanlabilir. Global deikenler iin static tanmlamasnn yalnzca balant zerinde etkili olduuna, mr zerinde etkili olmadna dikkat ediniz. Bir de u soru zerinde dnelim? Neden i balantya sahip bir global deiken kullanmak isteyelim? Bir global deikeni kendi modlyle snrlamak ne gibi faydalar salayabilir?

Bir projeyi oluturan dosyalar iinde d balantya sahip iki varln ismi ayn olamaz. Eer iki global varlk ayn ismi paylayorsa balama aamasnda hata oluur. Bu da zellikle byk projeler sz konusu olduunda tehlikeli bir durumdur. Zira byk projelerin ounda hem ok sayda programc kod yazar, hem de baka firmalar tarafndan yazlm dosyalar da projede kullanlr. Bu durumda farkl kaynak dosyalar global varlklara ayn isimler verilemez. Ayn isimlerin seilmesi durumunda balama zamannda hata oluur.

sim Kirlenmesi

Bir global deiken yalnzca bir modl ilgilendiriyorsa, bu modln hizmet verdii modlleri ilgilendirmiyorsa, programn alma zamannda dier modller tarafndan yanllkla deitirilebilir. Baka modldeki global deikenlerle isim akmasna yol aabilir yani isim kirlenmesine neden olabilir. Doal olan byle bir global deikenin i balantya sahip olmasdr. Byle global deikenler static anahtar szc ile tanmlanmaldr.

Baka Modller Tarafndan Deitirilmesi stenmeyen Global Deikenler

Modllerin Oluturulmas

Modl darya baz hizmetler verecek kodlarn oluturduu birimdir. C dilinde geleneksel olarak bir modle ilikin iki ayr dosya oluturulur. Bu dosyalardan biri modln balk dosyasdr. Bir balk dosyas bir modln arayzdr (interface). Bir modln arayz, o modl kullanacak dosyalarn derlenmesi srasnda, derleyicinin bilmesi gereken bilgileri salayan bildirimleri ierir. Modller daryla olan ilikilerini arayzleri ile kurar. Bir modle ilikin dary ilgilendiren bildirimler geleneksel olarak uzants ".h" olarak

350

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

seilmi text dosyalarnda toplanrlar. "h" uzants "header" szcnn ba harfinden gelir. Bir modlden faydalanan bir kaynak dosya o modln balk dosyasn #include nilemci komutuyla eklerler. Bylece tm bildirimleri derleyici grm olur. O modle ilikin tanmlamalar ise .c uzantl ayr bir dosya iinde yer alr. Bu tanmlamalar global deikenlerin tanmlamalar ve ilevlerin tanmlamalardr. Teknik olarak bu dosyaya "kod dosyas" (implementation file) denir. Projede alan bir programc modln ara yzn deitirmeden, modln kodunu yani kaynak dosyay deitirirse, projede bu modl kullanan programclarn, kendi kodlarnda bir deiiklik yapmalar gerekmez. Bylece nce modller ilikin arayzler belirlenir ve bu arayzlere bal olarak kaynak dosyalar yazlrsa, kaynak dosyada bir deiim olsa da, bu kaynak dosyadaki ilevleri aran modllerde bir deiiklik yaplmas gerekmez.

Static Anahtar Szcnn lev Bildirimlerinde ya da Tanmlarnda Kullanlmas

levler da global varlklardr. Bir modldeki baz ilevler darya hizmet vermek iin tanmlanr. Bu ilevler modln arayznde yani balk dosyasnda bildirilmelidir. Ancak modlde yer alan modln kendi i ileyiinde grev yapan ilevler, darya dorudan hizmet vermez. Bu ilevler, kodlama dosyasnda static anahtar szc ile bildirilir ve tanmlanrl. Bu durumda static anahtar szc, ilevin geri dn deerinin trnden nce yazlr:

static int func(int);

Tr Niteleyicileri
C de const ve volatile olmak zere iki tr niteleyicisi vardr. [C99 standartlaryla restrict anahtar szcyle yeni bir tr niteleyicisi eklenmitir.]

const belirleyicisi, ilkdeer atandktan sonra nesnenin ieriinin deitirilemeyeceini anlatr. const belirleyicisi yerel, global deikenlerle ve ilevlerin parametre deikenleriyle birlikte kullanlabilir. const anahtar szc ile tanmlanan bir nesneye atama ileci ile atama yaplmas geersizdir: const double PI = 3.14159265 int main() { const int i = 10; i = 100; return 0; } Yerel bir deiken const belirleyicisi ile tanmlanacaksa ilkdeer verilmelidir, ilkdeer verilmezse const belirleyicisi kullanmann bir anlam kalmaz. Aadaki rnei inceleyin: void func () { const int a; /**/ } /* Geersiz */ /* Geerli. lkdeer verme. */

const Belirleyicisi

/* anlamsz */

Bu rnekte a yerel bir deiken olduundan bir p deere sahiptir. Deikenin deeri bir daha deitirilemeyeceine gre byle bir tanmlamann da bir anlam olamaz.

351

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

const belirleyicisinin kullanm amac nedir? Kullanld yere gre const belirleyicisi aadaki faydalar salayabilir: 1. Programn okunabilirliini artrr. Bu ynyle kodu okuyana yardmc olur. Program okuyanlar const anahtar szcn grdklerinde nesnenin deerinin deitirilmeyecei bilgisini alrlar. 2. Yanllkla nesnenin deerinin deitirilmesi engellenir. Bu ynyle kodu yazana yardmc olur. const bir nesnenin deeri atama yoluyla deitirlmesi durumunda derleme zamannda hata oluur. Bylece deeri deitirilmemesi gereken bir nesnenin deerinin yanllkla atama yoluyla deitirilmesi de engellenmi olur. 3. const anahtar szc ile tanmlanan nesneler, derleyici tarafndan salt okunan bellekte saklanabilirler. Derleyici const nesnenin deerinin deitirilmeyeceini bilmediinden, bu nesnelerin kullanld ifadeler iin ek eniyileme(optimizasyon) ilemleri uygulayabilir. const anahtar szc bu ynyle derleyiciye yardmc olur.

const nesneler deeri deimeyecek deimezler gibi kullanldklarna gre, bunlarn yerine ou zaman #define nilemci komutuyla oluturulmu simgesel deimezler de kullanlabilir. Ancak const nesneler ile simgesel deimezler arasnda baz farkllklar vardr. kisi arasnda tercih yaplmas durumunda bu farkllklar rol oynar: 1. Simgesel deimezler nesne deildir. Bu yzden programn alma zamannda bellekte bir yer kaplamazlar. Nesne olmadklar iin bilinirlik alanlar, mrleri ve balant zelliklerinden sz edilemez. Derleme aamasna gelindiinde derleyici simgesel deimezlerin yerine, nilemci tarafndan yerletirilen atomlar grr. 2. Derleyiciler const nesneler iin daha iyi "debugging" destei verirler. 3. Derleyiciler derleme zamannda bir hata olumas durumunda hatann yerini iaretleme konusunda const nesneler iin daha iyi destek verirler. 4. Bir dizi const anahtar szc ile tanmlanabilir. Bu durumda dizinin keli ayra ileci ile ulalan herhangi bir elemanna atama yaplamaz. Byle bir durumu nilemci #define komutuyla kolaylkla gerekletirmek mmkn deildir: void foo { const char alphabet[] = "abcdefghijklmnopqrstuvwxyz"; alphabet[2] = 'X'; /* GEERSZ*/ } 5. C'de const nesneler d balantya sahiptir. Bir const nesne, extern bildirimi yaplarak baka modllerde kullanlabilir. 6. C dilinde const nesneler deimez ifadesi olarak kullanlamazlar. Aadaki rnei inceleyin: #define SIZE 100

const Nesnelerle Simgesel Deimezlerin Karlatrlmas

int main() { const int size = 100; int legal_array[SIZE]; int illegal_array[size]; /***/ }

/* GEERSZ size deimez ifadesi deil */

352

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

const anahtar szc sklkla gsterici nesnelerinin tanmlamalarnda kullanlr. const anahtar szcnn gstericilerle birlikte ayr kullanlma biimi vardr. Kullanlan her biimde tanmlanan gstericinin zellii deiir. 1. const anahtar szcnn '*' atomundan nce kullanlmas: Byle gstericilere gsterdii yer const olan gstericiler (pointer to const objects) denir. Byle bir gstericinin gsterdii nesne, gstericinin ierii alnarak elde edilen nesneye atama yaplmas yoluyla deitirilemez. Ancak gstericinin kendisine atama yaplabilir. Yani gstericinin bir baka nesneyi gstermesi salanabilir. Aadaki rnei inceleyin: int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double d1 = 2.5, d2 = 3.5; char str[] = "Necati"; int x = 20; const int *iptr = a; double const *dptr = &d1; const char *cptr = str; /* *iptr = 100; Geersiz! *dptr = 1.23; Geersiz! cptr[1] = 'x'; Geersiz! */ iptr = &x; dptr = &d2; cptr = "Selami"; } return 0;

const Anahtar Szcnn Gsterici Tanmlamalarnda Kullanlmas:

Yukardaki main ilevinde tanmlanm, gsterdii yer const olan gstericiler iin yaplan *iptr = 100; *dptr = 1.23; cptr[1] = 'x'; atamalar derleme zamannda hata oluumuna neden olur. Ancak gstericilerin kendilerine yaplan iptr = &x; dptr = &d2; cptr = "Selami"; atamalarnn tamamen kurallara uygun olduunu gryorsunuz. const anahtar szcnn gstericilerle birlikte kullanlmasnda en sk grlen biim budur ve zellikle ilevlerin parametre deikenleri olan gstericilerle bu biim kullanlr: void func(const char *str) { /***/ } Yukarda func isimli ilevinin parametre deikeni olan str gstericisi deerini ilevin arsyla alr. lev ar ifadesindeki argman ilevin arlmasyla s gstericisine

353

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

kopyalanr. Ancak ilev iinde *s nesnesine ya da s[x] nesnesine bir atama yaplamaz. Yani s gstericisinin gsterdii yerdeki nesne (ileve adresi gnderilen nesne) deitirilemez. Yukardaki rnekte const anahtar szcnn kullanlmas hereyden nce okunabilirlik ile ilgilidir. Yukardaki kodu okuyan bir C programcs func ilevinin dardan adresi alnan nesnenin yalnzca deerinden faydalanlacan, yani bu nesnenin deerini deitirmeyeceini anlar. Geleneksel olarak, const anahtar szcnn, parametre deikeni olan gstericilerin tanmlanmasnda kullanlmamas okunabilirlik asndan bir ileti olarak kabul edilir: void func(char *s) { /***/ } Yukardaki kodu okuyan bir C programcs func ilevinin (aslnda sentaks asndan bir zorunluluk bulunmasa da) kendisine adresi gnderilen nesneyi deitireceini anlar. Kodu okuyan kii yle dnr: Eer func ilevi adresi gnderilen nesneyi deitirmeyecek olsayd, s gstericisi const anahtar szc ile tanmlanrd. 2. const anahtar szcnn '*' atomundan sonra kullanlmas: Bu ekilde tanmlanm gstericilere kendisi const gsterici (const pointer) denir. Bu gstericilere ilkdeer olarak verilen adres atama yoluyla deitirilemez. Yani gsterici mr boyunca ayn nesneyi gsterir. Ancak gstericinin gsterdii nesneyi, gstericinin ieriini alarak deitirmek mmkndr. const anahtar szcnn bu kullanm biiminde gsterici tanmlanrken ilkdeer verilmelidir, yoksa const anahtar szcnn kullanlmasnn bir anlam kalmaz. Aadaki rnei inceleyin: int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double d1 = 2.5, d2 = 3.5; char str[] = "Necati"; int x = 20; int *const iptr = a; double *const dptr = &d1; char *const cptr = str; *iptr = 100; *dptr = 1.23; cptr[1] = 'x'; /* iptr = &x; dptr = &d2; cptr = "Selami"; */ } return 0; Geersiz! Geersiz! Geersiz!

3. const anahtar szcnn hem '*' atomundan nce hem de '*' atomundan sonra kullanlmas: Bu durumda hem kendisi const hem de gsterdii yer const olan bir gsterici tanmlanm olur. Atama ileciyle ne gstericiye ne de gstericinin ierii alnarak elde edilen nesneye atama yaplabilir. const szcnn bu biimde kullanlmas yine daha ok parametre deikenlerinin tanmlanmasnda grlr ise de bu uygulamalarda seyrek olan bir durumdur. Aaadaki rnei inceleyin:

354

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int main() { int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; double d1 = 2.5, d2 = 3.5; char str[] = "Necati"; int x = 20; const int *const iptr = a; const double *const dptr = &d1; const char *const cptr = str; /* *iptr = 100; Geersiz! *dptr = 1.23; Geersiz! cptr[1] = 'x'; Geersiz! iptr = &x; dptr = &d2; cptr = "Selami"; } return 0; Geersiz! Geersiz! Geersiz!

Gstericiyi gsteren bir gstericinin const anahtar szc ile bildirilmesinde const anahtar szcnn kullanlmas farkl olanaklar yaratr: const char **ptr; ptr nin gsterdii gstericinin gsterdii nesneye atama yaplamaz. const olan **ptr nesnesidir. ptr nesnesine ve *ptr nesnelerine atama yaplabilir. (pointer to pointer to const object) char *const *ptr; *ptr nin gsterdii gstericiye atama yaplamaz. const olan *ptr nesnesidir. ptr nesnesine ve **ptr nesnelerine atama yaplabilir. char ** const ptr; ptr ye atama yaplamaz. const olan ptr nesnesidir. **ptr nesnesine ve *ptr nesnelerine atama yaplabilir. const char *const *ptr; yalnzca ptr nesnesine atama yaplabilir. char *const * const ptr; yalnzca **ptr nesnesine atama yaplabilir. const char ** const ptr; yalnzca *ptr nesnesine atama yaplabilir.

Bir ilevin Geri Dn Deerinin const Adres Olmas


const char *get_name(void);

Adrese geri dnen bir ilevin geri dn deeri const bir adres olabilir:

Yukarda bildirimi yaplan get_name ilevinin geri dn deeri const bir adrestir. Peki bu ne anlama gelir? Bir ilevin geri dn deeri trnn aslnda, geri dn deerini iinde tutacak geici nesnenin tr olduunu biliyorsunuz. get_name ilevinin geri dn deerini tutacak geici nesnenin tr const char * trdr. Yani geici nesne gsterdii yer const olan bir gstericidir. lev ars, ilevin geri dn deerini, yani geici nesnenin deerini gsterdiine gre, byle bir ileve yaplan ar ierik ilecine ya da

355

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

keli ayra ilecine terim olduunda, ulalan nesneye atama yaplamaz. Aadaki rnei inceleyin: char *get_name(); const char *get_fname(); int main() { *get_name() = 'a'; get_name()[2] = 'x'; *get_fname() = 'a'; get_fname()[2] = 'x'; } return 0;

/* Geersiz /* Geersiz

*/ */

Yukarda main ilevi iinde arlan get_name() ilevinin geri dndrd adresteki nesneye 'a' deeri atanyor. Yine get_name()[2] ifadesi ile ilevin dndrd adresteki nesneden iki sonraki nesneye ulalarak bu nesneye de 'x' deeri atanyor. Atamalar geerlidir. Ancak geri dn deeri const char * trden olan get_fname ilevi iin benzer atamalar geersizdir, derleme zamannda hata oluturur.

volatile Belirleyicisi
Derleyiciler eniyileme amacyla nesneleri geici olarak yazmalarda tutabilir. Yazmalardaki bu eit geici barnmalar register belirleyicisi kullanlmasa da derleyiciler tarafndan yaplabilir. rnein: int kare (int a) { int b; b = a * a; return b; } Yukardaki ilevde b geici bir deikendir, dolaysyla derleyici b deikenini bellekte bir yerde saklayacana, geici olarak yazmalarndan birinde saklasa da ilevsel bir farkllk ortaya kmaz. Bu eit uygulamalarda derleyicinin deikenleri geici olarak yazmalarda saklamas ilemleri hzlandrr. Aadaki kodu inceleyin: int a; int b; /***/ a = b; if (a == b) { /***/ } Bu rnekte doal olarak ilk admda b deikeni a deikenine aktarlmak zere yazmalardan birine ekilir. Ancak derleyici if ayrac iindeki ifadede a == b karlatrmasn yapmak iin bellekteki b yerine yazmataki b'yi kullanabilir. Verdiimiz iki rnekte de derleyici bir takm eniyileme ilemleriyle program ilevi deimeyecek biimde daha hzl alr hale getirmek istemitir. Ancak kimi uygulamalarda derleyicinin bu biimde davranmas hatalara neden olabilir. kinci rnekte :

356

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

a = b; /* Bir kesme gelerek b'yi deitirebilir! if (a == b) { /***/ }

*/

Bir donanm kesmesi (rnein 8h gibi) b'yi deitiriyorsa, bu durum if deyimi tarafndan farkedilmeyebilir. te bu tr durumlarda deikenlerin eniyileme amacyla geici olarak yazmalarda tutulmas arzu edilmeyen sonularn olumasna yol aabilir. volatile " Deikenleri eniyileme amacyla yazmalarda bekletme, onlar bellekteki gerek yerlerinde kullan!" anlamna gelen bir tr belirleyicisidir. Bu anlamyla volatile belirleyicisini register belirleyicisi ile zt anlaml olarak dnebiliriz. Yukardaki rnekte b deikenini volatile olarak bildirerek anlattmz gibi bir problemin kmas engellenebilir. int a; volatile int b; volatile ok zel uygulamalarda kullanlabilen bir belirleyicidir. Yerel, parametre ya da global deikenlerle birlikte kullanlabilen volatile belirleyicisi ancak ok zel uygulamalarda nemli olabilir. Bu belirleyicinin bilinsizce kullanlmasnn performans kt ynde etkileyebileceini unutmaynz.

357

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

YAPILAR
Yaplar (structures) programcnn birden fazla nesne yaratmasna izin veren bir aratr. Yaplarn kullanlmasyla bellekte birbirini izleyecek ekilde yer alan birden fazla nesne yaratlabilir. Bellekte bitiik olarak yer alan nesnelerin dizi tanmlamalaryla da yaratlabileceini biliyorsunuz. Ancak yaplarn dizilerden baz farkllklar vardr: Diziler ayn trden nesneleri iinde tutabilirken, yaplar farkl trlerden nesneleri tutabilir. Yaplarn kullanlmasnn ana nedeni budur. ou zaman, trleri farkl bir takm nesneler, mantksal olarak bir btn oluturabilir. sim, ya, cinsiyet, departman, cret, renim durumu gibi bilgileri farkl trden nesneler iinde saklanabilir. Bunlarn tamam bir iyerinde alan bir kiiye ait bilgiler olabilir. Aralarnda mantksal iliki olan farkl trden veriler yaplar iinde saklanabilir. int trden 10 elemanl bir dizi tanmlandn dnelim: int a[10]; Kullanlan sistemde int trnn 4 byte yer kapladn dnrsek bu dizi iin bellekte 40 byte'lk bir blok ayrlr deil mi? Programc bu dizinin elemanlarna keli ayra ileci ile ulaarak, eleman olan nesneler zerinde dorudan ilemler yapabilir. Ancak dizinin tamam bir nesne olarak kullanlamaz. Yani C dili bir dizinin tamamn bir nesne olarak grmez. Dizi isimlerinin, C'de dizinin ilk eleman olan nesnenenin adreslerine dntrldklerini hatrlayalm. Yaplarda durum biraz daha farkldr. Yap kullanlmasyla da birden fazla nesne iin ardl bir blok ayrlr. Ancak bu kez bloun tamam da bir nesne olarak kullanlabilir. Yapnn eleman olan nesneler zerinde ilemler yapabileceimiz gibi, bu nesnelerin oluturduu bloun tamam zerinde de dorudan baz ilemler yapabiliriz.

Yap nedir

Yaplarn kullanlmasyla programc yeni bir tr yaratabilir. C'nin var olan doal veri trlerinin yannda, mantksal bir anlam soyutlayan yeni trler yaratlabilir. rnein deeri bir tarih bilgisi olan bir tr ya da deeri bir kompleks say olan bir tr oluturulabilir. Bylece programlama ile bir zm oluturmak istediimiz problem dzlemi daha iyi modellenebilir. Yaplarn iyi bir ekilde renilmesi, ileride nesne bazl(object based) ve nesneye ynelimli (object oriented) programlama tekniklerinin iyi bir biimde anlalabilmesine yardmc olur.

Programcnn Tanmlad Bir Tr Olarak Yap

Yap ile programcnn yeni bir tr yaratmasna olanak verilir. Yaplarn kullanlabilmesi iin yaplmas gereken ilk ilem bu yeni tr derleyiciye tantmaktr. Tantma ilemi yap bildirimi ile olur. Yap bildirimini gren derleyici, bu yeni tr hakknda bilgi edinmi olur. Bu bildirimle derleyiciye aadaki bilgiler verilir: - trn ismi - trn bellekte ne kadar yer kaplad - elemanlarn isimleri Derleyici bir yap bildirimini grdkten sonra, bu trden bir nesne tanmlandnda nesne iin bellekte ne kadar yer ayracan rendii gibi, bu nesnenin programc tarafndan kullanmna ilikin ifadelere ilikin derleme zamannda baz snamalar yapabilir. Yap bildiriminin belirli bir sentaks vardr.

Yap Bildirimi

359

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yap Bildiriminin Genel ekli


<struct > <tr> <tr> <tr> ... }; [yap etiketi] { <eleman ismi>; <eleman ismi>; <eleman ismi>;

struct bir anahtar szcktr. Bildirimde mutlaka yer almas gerekir. struct anahtar szcn bir yap etiketi (structure tag) izleyebilir. Yap etiketi C dilinin isimlendirme kurallarna uygun olarak seilmi bir isimdir. Yap etiketinden sonra bir blok yer alr. Bu blok bildirime ak bir blge belirler. Bu blok iinde yap elemanlarnn bildirimleri yaplr. rnek verelim: struct Date { int day, month, year; }; struct Point { int x, y; }; struct Complex { double real; double imag; }; Yap etiketleri (structure tags) isimlendirme kurallarna uymaldr. Ancak programclarn ou yap etiketlerinin ilk harflerini byk, dier harflerini kk seerler. Yap bildiriminin yaplmas bellekte derleyici tarafndan bir yer ayrlmasna neden olmaz. Yani bir tanmlama (definition) sz konusu deildir. Bu bildirimle programcnn yaratt yeni bir veri tr hakknda derleyiciye bilgi verilir. Derleyici bu bilgiyi, bu trden yaratlacak nesneler iin ablon olarak kullanr. Yukardaki bildirimlerle yaratlm olan trlerin ismi srasyla, struct Sample, struct Date, struct Point ve struct Complex'dir. Yani tr ismi struct anahtar szc ve yap etiketinin birletirilmesiyle elde edilir. Yap etiketi tek bana bir tr bilgisi belirtmez. Ancak struct anahtar szc olmadan da bir tr ismi yaratlmasn salayan aralar da ileride greceimizi imdiden belirtelim.

Tm bildirimler gibi yap bildirimleri de yerel ya da global dzeyde yaplabilir. Tm bildirimler gibi yap bildirimlerinin de bilinirlik alan vardr. Bildirim bir blok iinde yaplrsa, bildirilen yap yalnzca bildirimin yapld blok iinde bilinir. Yap bildirimleri ancak ok zel durumlarda bir blok iinde yaplr. Kaynak dosya iindeki tm ilevlerin yeni yaratlan tr kullanabilmeleri iin, bildirim hemen her zaman global dzeyde yaplr. Tanmlanan ilevleri aracak dier kaynak dosyalar da, ou zaman ayn yapy kullanmak zorunda olduundan, yap bildirimleri genellkle modllerin balk dosyalarna yerletirilir. Bylece ilgili balk dosyasn ekleyen bir kaynak dosya iinde de ayn bildirim yaplm olur.

Yap Bildirimlerinin Yeri

Doal trlerden nesneler nasl tanmlanyorsa yap trnden bir deikenin tanmlanmas da ayn szdizimle olur. Yani nce tr belirten szckler daha sonra da yap deikeninin ismi yazlr:

Yap Trnden Deiken Tanmlamas

360

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct Date bdate; struct Point p1, p1; Yukardaki tanmlamalarn ilgili yap bildirimlerinin grlr olduu bir noktada yapld kabul ediliyor. Yap bildiriminin grlmedii bir kaynak kod noktasnda yap deikeni tanmlanmas geersizdir. struct Date mydate; tanmlamasyla ismi mydate olan tr struct Date olan bir deiken tanmlanyor. struct Point p1, p2, p3; tanmlamasyla trleri struct Point olan p1, p2 ve p3 isminde ayr deiken tanmlanyor. Nesnelerin temel zellikleri nesnelerin trne bal deildir. phesiz yap nesnelerinin de bilinirlik alanlarndan, mrlerinden, balant zelliklerinden sz edilebilir. Bu zellikler doal veri trleri iin ne anlam tayorlarsa yap nesneleri iin de ayn anlam tar. Global isim alannda tanmlanan bir yap nesnesi i) Dosya bilinirlik alanna aittir. Nesnenin tanmndan sonra yer alan tm ilevler bu yap nesnesine ulaabilirler. ii) Statik mrldr. Programn alma zamannn balangcndan, programn sonlanmasna kadar bellekte yer kaplar. iii) Statik mrl olduu iin ilkdeer atamas yaplmad zaman, bellekte kaplad btn byte lar 0 bitleriyle doldurulur. Pratik olarak bunun anlam yapnn tm elemanlarnn deerinin 0 olmasdr. Yerel alanda tanmlanan bir yap nesnesi i) Blok bilinirlik alanna aittir. Yalnzca tanmlamasnn yapld blok iinde bu nesneye ulalabilir. ii) Otomatik mrldr. Programn ak, nesnenin tanmland bloa girdiinde nesne yaratlr. Programn ak nesnenin tanmlad blou terk ettiinde nesne bellekten boaltlr. iii) Otomatik mrl olduu iin ilkdeer atamas yaplmad zaman, bellekte kaplad alanda p deerleri vardr. Pratik olarak bunun anlam yapnn tm elemanlarnn p deerleriyle balatlmasdr. Yap nesnelerinin bildirim ve tanmlamalarnda belirleyiciler kullanlabilir. rnein yerel ya da global bir yap nesnesi static ya da const belirleyicisi ile tanmlanabilir. Bu belirleyicilerin bildirim ya da tanmlamalara katt anlamlar yap nesneleri iin bir farkllk tamazlar. Yap deikenleri bileik nesnelerdir. Yani paralardan, alt nesnelerden oluurlar. Zaten yap bildirimi ile yaplan bir ilem de bu paralarn isimleri ve trleri hakknda derleyiciye bilgi verilmesidir. Bir yap deikeni (nesnesi) iin, yap bildiriminde belirtilen elemanlarn toplam uzunluu kadar (byte olarak) yer ayrlr. Aadaki rnei inceleyin:

361

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> struct Sample { int i; long l; double d; }; int main() { struct Sample sam; printf("%d\n", sizeof(sam)); } return 0;

Yukardaki program UNIX altnda altnda ekrana 16 deerini yazdrr. nk struct Sample trnden bir nesne olan sam nesnesinin bellekte kaplad yer elemannn kaplad uzunluun toplamdr. Bu aadaki ekilde de ifade edilebilir: sizeof(sam) == sizeof(int) + sizeof(long) + sizeof(double) Hizalama (alignment) konusunda yeniden bu noktaya deininilecek.

Bir yap bildirimi sonlandrc atom ile sonlandrlmadan, yap bildiriminin kapanan kme ayracndan hemen sonra bir deiken listesi yazlrsa yap bildirimi ile deiken tanmlamas bir arada yaplm olur. struct Date { int day, month, year; }bdate, edate; Yukardaki deyim ile struct Date tr bildirilirken bu yap trnden bdate ve edate deikenleri de tanmlanyor. Bu durumda yap deikenlerinin bilinirlik alanlar yap bildiriminin yerletirildii yere bal olarak belirlenir. Yani sz konusu yap deikenleri yerel ya da global olabilir. Yap nesnelerinin hemen yap bildiriminden sonra tanmlanmas durumunda yap ismi kullanma zorunluluu da bulunmaz. rnein yukardaki bildirim aadaki ekilde de yaplabilir. struct { int day, month, year; } bdate, edate; Burada bdate ve edate yukarda bildirilen ama isimlendirilmeyen yap trnden deikenlerdir. Bu yntemin dezavantaj artk ayn trden baka bir yap deikeninin tanmlanmasnn mmkn olmaydr. rnein yukardaki kod parasndan sonra ayn yap trnden bir yap nesnesi daha tanmlamak istediimizi dnelim. Bu tanmlama struct { int day, month, year; }cdate; biiminde yaplsa bile artk derleyici, bildirimleri edeer olan bu iki yap trn ayr yap trleri olarak ele alr. Dolaysyla cdate = bdate;

Yap Bildirimi le Deiken Tanmlamasnn Birlikte Yaplmas

362

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

gibi bir atama yap trlerinin farkl olmas nedeniyle geerli deildir. Byle bir tanmlamann baka bir dezavantaj da, bu yap tryle ilgili bir ilev yazlamaydr. nk yap trnn bir ismi yoktur. Bir ilevin parametre deikeni tr bilgisi olmadan tanmlanamaz.

Yaplar ve sim Alanlar

Yap isimleri ve yap elemanlarnn isimleri ayr isim alanlarnda deerlendirilir: Aadaki kod geerlidir: void func() { struct x {int x;}x; }

Yaplarn dizilerden nemli bir fark da elemanlara eriim konusundadr. Dizi elemanlarna eriim, dizi ismi ve keli ayra ileci [ ] (index operator - subscript operator) yoluyla yaplrken, yap elemanlarna eriim iin nokta ileci kullanlr. Nokta ilecine (dot operator) ilikin atom '.' dr. Nokta ileci, iki terimli araek konumunda (binary infix) bir iletir. Bu ilecin sol terimi bir yap trnden nesne olmaldr. lecin sa terimi ise nesnenin ait olduu ilgili yap trnnn elemanlarndan biri olmak zorundadr. Nokta ileci ile ncelik tablosunun en yksek ncelikli dzeyinde bulunur. Bir yap nesnesi tanmlanarak, bu yap nesnesinin elemanlarna nokta ileci ile eriildiinde artk bu elemanlarn her biri ayr bir nesne olarak ele alnr. Bu nesnelerin, yap dnda tanmlanan nesnelerden herhangi bir fark yoktur. Aadaki kodu derleyerek altrn: #include <stdio.h> struct Sample{ int i; long l; double d; }; int main() { struct Sample sam; sam.i = 10; sam.l = 200000L; sam.d = 1.25; printf("sam.i = %d\nsam.l = %ld\nsam.d = %lf\n", sam.i, sam.l, sam.d); return 0; } Yukardaki rnekte grld gibi sam.i, sam.l ve sam.d yap elemanlar ayr birer nesne zellii gsterir. Bu elemanlara ayr ayr ulaabilir, ayr ayr atamalar yaplabilir. Bu nesneler ++ ve -- ilelerinin ya da adres ilecinin terimi olabilir.

Yap Elemanlarna Eriim

Yap Elemanlarnn Bellekteki Yerleimi

Yap elemanlar bellee, bildirimde ilk yazlan eleman kk saysal adreste olacak biimde, bitiik olarak yerletirilir. Aadaki program derleyerek altrn:

363

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct Sample{ int i; long l; double d; };

#include <stdio.h> int main() { struct Sample sam; printf("sam.i'nin adresi = %p\n", &sam.i); printf("sam.l'nin adresi = %p\n", &sam.l); printf("sam.d'nin adresi = %p\n", &sam.d); } return 0;

Yap nesnelerinin elemanlar deil de kendileri sz konusu olduunda, yani bir yap nesnesi bir btn olarak ele alndnda ancak drt ilecin terimi olabilir: nokta ileci, sizeof ileci, adres ileci, atama ileci. Bir yap nesnesinin bunlarn dnda bir ilecin terimi olmas geersizdir.

Yap Nesneleri zerinde Yaplabilecek lemler

Bir yap nesnesi sizeof ilecinin terimi olabilir. Bu durumda sizeof ilecinin rettii deer, nesnenin ait olduu trn bellekte kaplad byte saysdr. Tabi sizeof ilecinin teriminin bir tr ismi de olabileceini biliyorsunuz. Aadaki rnei inceleyin: struct Point { int x, y; }; int main() { struct Point p; printf("sizeof(struct Point) = %u\n", sizeof(struct Point)); printf("sizeof(p) = %u\n ", sizeof(p)); } return 0;

sizeof leci ve Yaplar

Yukardaki main ilevini iinde yaplan printf arsnda sizeof ilecinin terimi bir tr ismi (struct Point) iken, ikinci printf arsnda ise sizeof ilecinin terimi struct Point trnden bir nesne olan p dir.

Bir yap nesnesi adres ilecinin terimi olabilir. Bu durumda adres ilecinin rettii deer ilgili yap trnden bir adres bilgisidir: struct Date isimli bir yapnn bildiriminin yaplmasndan sonra aadaki gibi bir nesne tanmlandn dnelim:

Adres leci ve Yap Nesneleri

364

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct Date { int d, m, y; }; struct Date mydate = {4, 3, 1964}; &mydate ifadesi geerli bir ifadedir. Bu ifade (struct Date *) trndendir. Peki &mydate ile &mydate.day ayn adresler midir? Bu adreslerin saysal bileenleri ayn olmakla birlikte tr bileenleri farkldr. ki ifade, saysal bileenleri ayn olan farkl trden iki adres bilgisidir. &mydate ifadesinin tr (struct Date *) iken &mydate.day ifadesinin tr (int *) trdr.

Bir yap nesnesi atama ilecinin sol ya da sa terimi olabilir. Bir yap nesnesine atama ileci ile ancak ayn trden baka bir yap nesnesi atanabilir. Atama ilecinin sandaki ve solundaki trlerin farkl yap trlerinden olmas geersizdir. Ayn trden iki yap nesnesinin 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 gerekir. ki yap deikeni de ayn trden deilse bu durum geersizdir. ki yapnn ayn trden olmalar iin ayn yap ismi ile tanmlanm olmalar gerekir. Aadaki iki yap, elemanlarnn trleri ve isimleri ayn olduu halde farkl iki tr belirtirler ve bu trlerden nesneler birbirlerine atanamaz. struct POINT_1 { int x, y; }; struct POINT_2{ int x, y; }; void func() { struct POINT_1 a; struct POINT_2 b; b.x = 10; b.y = 20; a = b; /* Geersiz */ } ki yap nesnesinin birbirine atanmas, nesnelerin tm elemanlarnn karlkl olarak birbirlerine atanmas sonucunu dourur. Yani bir blok kopyalamas sz konusudur. zellikle byk yaplar sz konusu olduunda atama ileci ile yap nesnelerini birbirine atanmasnn maliyetinin yksek olduu unutulmamaldr. lgili yap trnn sizeof deeri byklnde bir bellek blou, adeta memcpy benzeri bir ilevle kopyalanr. Aadaki program derleyerek altrn: #include <stdio.h> #include <string.h> struct Data{ int i; long l; double d; };

Atama leci ve Yap Nesneleri

365

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int main() { struct Data d1, d2, d3, d4; d1.i = 10; d1.l = 200000L; d1.d = 1.25; d2 = d1; d3.i = d2.i; d3.l = d2.l; d3.d = d2.d; memcpy(&d4, &d3, sizeof(struct Data)); printf("d1.i printf("d2.i printf("d3.i printf("d4.i return 0; } Yukardaki main ilevinde struct Sample trnden d1, d2, d3, d4 isimli 4 deikenin tanmlandn gryorsunuz. d2 deikenine atama ileci kullanlarak d1 deikeninin deeri atanyor. d4 deikenine de standart memcpy ilevi kullanlarak d3 nesnesinden kopyalama yaplyor. = = = = %d\nd1.l %d\nd2.l %d\nd3.l %d\nd4.l = = = = %ld\nd1.d = %ld\nd2.d = %ld\nd3.d = %ld\nsam4.d %lf\n\n", d1.i, d1.l, d1.d); %lf\n\n", d2.i, d2.l, d2.d); %lf\n\n", d3.i, d3.l, d3.d); = %lf\n\n", d4.i, d4.l, d4.d);

Bir yap nesnesi tr dntrme ilecinin terimi olamaz. Yani aadaki gibi bir kod da geersizdir: struct S1 { int a, b; }; struct S2 { int a, b; }; void func() { struct S1 s1; struct S2 s2; s1.a = 10; s1.b = 20; s2 = (struct S2)s1;

Tr Dntrme leci ve Yap Nesneleri

/* Geersiz! */

Yap Elemanlarnn Bellekteki Yerleimi

Yap elemanlar bellee, bildirimde ilk yazlan eleman kk adreste olacak biimde, bitiik olarak yerletirilir. Aadaki program derleyerek altrn:

366

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> struct Date { int day, mon, year; }; int main() { struct Date date; printf("date.day'in adresi = %p\n", &date.day); printf("date.mon'un adresi = %p\n", &date.mon); printf("date.year'in adresi = %p\n", &date.year); return 0; }

Yapnn bir eleman herhangi trden bir dizi olabilir. Bu durumda da yapnn dizi elemannn ismi bir nesne belirtmez, bir adres bilgisidir. nce aadaki rnei inceleyin: #include <stdio.h> #define NAME_LEN 20

Yap Eleman Olarak Dizilerin Kullanlmas

struct Person { char name[NAME_LEN]; int no; }; int main() { struct Person per; gets(per.name); puts(per.name); putchar(per.name[3]); per.name++ return 0; } Bildirilen struct Person yapsnn bir eleman char trden 20 elemanl bir dizidir. Bu durumda kaynak kodumuz Unix iletim sisteminde derlendiinde, sizeof(struct Person) 24 olur. Tanmlanan her struct Person trnden nesnenin iinde 20 elemanl char trden bir dizi yer alr. main ilevi iinde kullanlan per.name ifadesi, per yap nesnesinin iinde yer alan char trden dizinin balang adresidir. Bu ifade ileme sokulduunda, derleyici tarafndan otomatik olarak char * trne dntrlr. gets(per.name); arsyla gets ilevine, per nesnesi iindeki char trden name dizisinin balang adresi geiliyor. Bylece klavyeden alnan yaz nesnenin iindeki diziye yerletiriliyor. puts(per.name); arsyla da benzer biimde bu dizinin iindeki yaz ekrana yazdrlyor. /* Geersiz. Dizi ismi nesne deildir. */

367

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Ancak istenirse, nesnenin iindeki dizinin herhangi bir elemanna keli ayra ileci ya da ierik ileci ile ulalabilir: per.name[3] *(per.name + 3) ifadelerinin tr char trdr. Bu ifadeler per nesnesinin iindeki name dizisinin 3 indisli eleman olan char trden nesneye karlk gelir. struct Person trnden bir dizinin iinde tutulan yaznn uzunluu ne olursa olsun, struct Person trnn sizeof deeri deimez. Baz durumlarda, yap nesneleri iinde yer alan diziler, yap nesnesinin boyutunu gereksiz bir biimde artrd iin tercih edilmez. Bir yapnn eleman neden bir dizi olur? Bu yolla bir yap nesnesi iinde ayn trden belirli sayda deer tutulabilir. Bir renciye ilikin bilgilerin, bir yap tr ile modellendiini dnn. Byle bir yapnn iinde, rencinin notlarn iinde tutacak int trden bir dizi yer alabilir. Ancak uygulamalarda en ok grlen durum, yaz bilgilerinin yap iinde yer alan char trden diziler iinde tutulmasdr. Ayn trden belirli sayda deerin bir yap nesnesine bal olarak tutulmas iin, bir baka yol da yapnn bir elemannn bir gsterici olmasdr.

Yapnn bir eleman herhangi trden bir gsterici olabilir. Aadaki rnei inceleyin: #include <stdio.h> struct Person { char *name_ptr; int no; }; int main() { struct Person per; per.name_ptr = "Necati Ergin"; per.no = 125; printf("%s %d\n", per.name_ptr, per.no); } return 0;

Yap Eleman Olarak Gsterici Deikenlerin Kullanlmas

Yukardaki kod parasnda bu kez struct Person trnn bir eleman char trden bir gsterici olarak seiliyor. Bir gsterici kullanlan sistemde 2 ya da 4 byte yer kaplayacana gre yukardaki struct Person yapsnn sizeof deeri UNIX altnda 8 byte olur. per.name_ptr = "Necati Ergin"; fadesi ile per nesnesinin name_ptr isimli elemanna "Necati Ergin" dizgesi atanyor. Dizgelerin birer adres bilgisi olduunu, derleyici tarafndan char * trnden ifadeler olarak deerlendirildiklerini biliyorsunuz. per.name_ptr char * trden nesne gsteren bir ifadedir. Bu ifade per nesnesi iinde yer alan char trden gsterici deikene karlk gelir. Bu gstericinin gsterdii yere yani

368

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

*per.name_ptr per.name_ptr[n] ifadelerine gsterici hatas oluturmadan atama yapabilmek iin, nce eleman olan gstericinin gvenilir bir yeri gstermesi gerekir. Bir yapnn elemannn bir gsterici olmas ou zaman, yap nesnesinin dinamik olarak elde edilen bir bellek alann kullanmas amacyla istenir. Aadaki kod parasn inceleyin: #include <stdio.h> #include <stdlib.h> #include <string.h> #define ARRAY_SIZE 100

struct Person { char *name_ptr; int no; }; int main() { struct Person per; char name_entry[ARRAY_SIZE]; printf("ismi giriniz : "); gets(name_entry); per.name_ptr = (char *)malloc(strlen(name_entry) + 1); if (per.name_ptr == NULL) { printf("bellek tahsis edilemiyor!\n"); exit(EXIT_FAILURE); } strcpy(per.name_ptr, name_entry); printf("numarayi giriniz : "); scanf("%d", &per.no); printf("isim = %s\nNo : %d\n", per.name_ptr, per.no); /*...*/ free(per.name_ptr); } return 0;

main ilevi iinde yaplanlar srasyla inceleyelim: nce struct Person trnden per isimli bir deikenin tanmlandn gryorsunuz. Klavyeden girilen isim, nce standart gets ileviyle yerel name_entry dizisine alnyor. Yerel diziye alnm ismin uzunluunun 1 fazlas kadar byte'lk bir alan malloc ileviyle dinamik olarak elde ediliyor. Yaz uzunluunun 1 fazlas kadar yer ayrlmas yaznn sonunda yer alacak sonlandrc karakter iin de yer salanmas amac tayor. Dinamik alann balang adresinin per nesnesinin name_ptr elemannda saklandn gryorsunuz. Daha sonra standart strcpy ileviyle yerel dizideki isim ayrlan dinamik bloa kopyalanyor. per nesnesi bylece kendi eleman olan name_ptr gstericisi yoluyla dinamik bir alan kontrol eder hale geliyor, deil mi? Bir struct Person nesnesinin tuttuu isme ulamak iin nce nesnenin name_ptr elemanna eriip bu elemann deerinden de dinamik bloa eriilebilir. Nesnenin mrnn sona ermesinden nce, bellek szntsn (memory leak) engellemek amacyla, dinamik bloun geri verilmesi gerekir.

369

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

free(per.name_ptr); arsyla geri verme ileminin yapldn gryorsunuz.

Yap Deikenlerine lkdeer Verilmesi

Dizilere ilkdeer vermeye ilikin zel bir szdizimi olduunu biliyorsunuz. Yap deikenlerine de benzer bir szdizimle ilkdeer verilebilir. Verilen ilkdeerler sras ile yap elemanlarna yerletirilir. Daha az sayda yap elemanna ilkdeer verilebilir. Bu durumda ilkdeer verilmemi yap elemanlar otomatik olarak 0 deeri alr. #include <stdio.h> struct Date { int day, month, year; }; int main() { struct Date x = {10, 12, 1999}; struct Date y = {10}; printf("%d %d %d\n", x.day, x.month, x.year); printf("%d %d %d\n", y.day, y.month, y.year); } return 0;

lkdeer verme szdiziminde, yap nesnesinin isminden sonra yer alan atama ilecini bir blok izler. Bu bloun iinde virgllerle ayrlan bir liste ile yap nesnesinin elemanlarna ilkdeer verilir. lk ifadenin deeri yap nesnesinin ilk elemanna, ikinci ifadenin deeri yap nesnesinin ikinci elemanna atanr. Dizilerde olduu gibi, yaplarda da bir yap nesnesinin elemanlarndan daha fazla sayda elemana ilkdeer vermek geersizdir. Yapnn iinde yaz tutmak iin bildirilen char trden bir dizi var ise bu diziye de ift trnak iinde ayrca ilkdeer verilebilir: #include <stdio.h> #define MAX_NAME_LEN 20

struct Person { char name[MAX_NAME_LEN + 1]; int no; }; int main() { struct Person per = {"Mustafa", 256}; printf("isim : %s\nNo } return 0; : %d\n", per.name, per.no);

main ilevi iinde, struct Person trnden per deikenine ilkdeer veriliyor. Tanmlanan per deikeninin name isimli char trden dizisine, ilkdeer verme szdizimiyle Mustafa yazs yerletiriliyor. Yine per deikeninin no isimli elemanna da ilkdeer verme szdizimiyle 256 deeri atanyor.

370

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yap eleman olan dizinin btn elemanlarna deil de, belirli sayda elemanna ilkdeer vermek mmkndr. Aadaki rnei derleyerek altrn: struct Sample { int x; int a[10]; double d; }; #include <stdio.h> int main() { struct Sample sam = {20, 1, 2, 3, 7.8}; int k; printf("sam.x = %d\n", sam.x); for (k = 0; k < 10; ++k) printf("sam.a[%d] = %d\n", k, sam.a[k]); printf("sam.d = %lf\n", sam.d); } return 0;

Programcnn sam.x nesnesine 20, sam.a dizisinin ilk elemanna srasyla 1, 2, 3 deerlerini atamak istediini, sam.a dizisinin geri kalan elemanlarnn otomatik olarak sfrlanmasnn beklediini dnelim. sam.d nesnesine de 7.8 ilkdeerinin verilmek istendiini dnelim. Bildirimi yaplm struct Sample trnden sam isimli nesneye aadaki gibi ilkdeer aktarlyor: struct Sample sam = {20, 1, 2, 3, 7.8}; Ancak yukardaki deyimle bu yaplamaz. Derleyici 20 deerini sam.x nesnesine yerletirirken sam.a dizisinin ilk drt elemanna srasyla 1, 2, 3, 7.8 deerlerini yerletirir. Tabi otomatik tr dnmyle double trden olan 7.8 dizinin drdnc elemanna atanrken int trne dntrlr. double trden per.d nesnesine ise ilkdeer verilmediinden, bu eleman otomatik olarak 0 deeriyle balatlr. lkdeer verme deyiminde, ikinci bir blok kullanlarak eleman olan dizinin ilk 3 elemanna ilkdeer atamas yaplmas salanabilir. lkdeer verme deyimini aadaki gibi deitirerek kodu yeniden derleyip, altrn: struct Sample sam = {20, {1, 2, 3}, 7.8}; Byle bir ilkdeer verme ileminde, ikinci kme ayrac ifti iinde yazlan virgllerle ayrlm deerler, eleman olan dizinin ilk elemanna atanrken, kapanan kme ayracn izleyen 7.8 deeri ise yap nesnesinin d isimli elemanna aktarlm olur.

Yap Trnden Adresler ve Gstericiler

Bir yap nesnesinin adres ilecinin terimi olmasyla, yap nesnesinin adresi elde edilebilir. Bir yap trnden gsterici deikenler de tanmlanabilir. Bir yap trnden gsterici deikene, ayn yap trnden bir adres atanmaldr:

371

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> struct Point { double x, y; }; int main() { struct Point p; struct Point *ptr = &p; printf("sizeof ptr = %d\n", sizeof(ptr)); printf("sizeof *ptr = %d\n", sizeof(*ptr)); } return 0;

main ilevi iinde struct Point trnden p isimli bir deiken tanmlanyor. Bu nesnenin adresi struct Point trnden bir gsterici deiken olan ptr'ye atanyor. Bu atamadan sonra artk p deikenine ptr gsterici deikeni ile de ulalabilir, deil mi? Bir gsterici deiken ne trden bir nesneyi gsterirse gstersin, gstericinin sizeof deeri 2 ya da 4 byte'dr. sizeof(ptr) ifadesi ile ptr gsterici deikeninin kendi sizeof deeri elde edilirken sizeof(*ptr) ifadesi ile ptr'nin gsterdii nesnenin yani p deikeninin sizeof deeri elde ediliyor.

Yap Gstericisi le Yap Elemanlarna Eriim

ptr bir yap trnden bir gsterici ve mem de o yapnn bir eleman olmak zere ptr'nin gsterdii nesnenin mem isimli elemanna aadaki gibi eriilebilir: (*p).mem erik ileci ile ncelik tablosunun ikinci dzeyindeyken, nokta ileci birinci ncelik dzeyindedir. Burada *p ifadesinin ncelik ayrac iine alnmas zorunludur. Eer ifade *p.mem /* Geersiz */

biiminde yazlsayd, nce nokta ileci deer reteceinden, p.mem ifadesi ele alnrd.Nokta ilecinin sol terimi bir yap nesnesi olmad iin, ifade geersiz olurdu.

Ok leci

Bir yap trnden adres sz konusu olduunda bu adresteki yap nesnesinin belirli bir elemanna erimek iin ayr ile kullanlabilir: ncelik ileci, ierik ileci, nokta ileci. Bu eriim, ismi ok ileci olan tek bir ilele yaplabilir. Ok ileci - ve > karakterlerinin yanyana getirilmesiyle oluturulur. ki terimli araek konumunda (Binary infix) bir iletir. Ok ileci ncelik tablosunun en yksek ncelik seviyesindedir. -> ilecinin sol terimi bir yap trnden adres olmaldr. lecin sa terimi ise ilgili yapnn bir eleman olmaldr. le, sol terimi olan adresteki yap nesnesinin, sa terimi olan isimli elemanna erimek iin kullanlr. ptr, bir yap trnden bir nesnenin adresini tutuyor olsun. Aadaki iki ifade edeerdir.

372

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

(*ptr).mem ptr->mem Nokta ve ok ilelerinin her ikisi de elemana erimek iin kullanlr. Ancak nokta ileci yap deikeninin kendisiyle, ok ileci ise adresiyle eriim salar.

Bir ilevin deerle (call by value) ya da adresle (call by reference) arlabileceini biliyorsunuz. Yap nesneleriyle ilgili bir i gren ilev de, deerle ya da adresle arlabilir.

Yap Nesnelerinin levlere Geirilmesi

Yap Nesnesinin Deerinin leve Gnderilmesi

Bir ileve gnderilen argman olan ifade, ilevin ilgili parametre deikenine kopyalanr. Bu durumda bir ilevin parametre deikeni bir yap trnden ise, ilev ayn yap trnden bir nesne ile arlabilir. Ayn trden yap nesnelerinin atama ileciyle birbirine atanabileceini biliyorsunuz. Byle bir atama blok kopyalamas anlamna geldii iin, hem bellek hem de ilem zaman asndan greli bir kayba neden olur. stelik bu yntemle, ilev kendisine gnderilen argmanlar deitiremez. nk ilev deerle arlmaktadr. Aadaki rnei inceleyin: #include <stdio.h> #define #define MAX_NAME_LEN MAX_FNAME_LEN 16 24

struct Person { char name[MAX_NAME_LEN + 1]; char fname[MAX_FNAME_LEN + 1]; int no; double wage; }; void display_person(struct Person per) { printf("%d %s %s %.2lf\n", per.no, per.name, per.fname, per.wage); } int main() { struct Person person = {"Necati", "ERGIN", 2345, 3.56}; display_person(person); return 0; } display_person ilevinin parametre deikeni struct Person trnden bir nesnedir. main ilevi iinde struct Person trnden ismi person olan bir nesnenin ilkdeer verilerek tanmlandn gryorsunuz. display_person ilevine yaplan arda argman olarak person nesnesinin deeri kullanlyor. lev arld zaman, yaratlan ilevin parametre deikeni olan per nesnesine, main blou iindeki person deikeninin deeri kopyalanr. Bu bir blok kopyalama ilemidir. sizeof(struct Person) byklnde bir blok kopyalanr. per nesnesi person nesnesi ile ayn nesne deildir. per nesnesi deeri person nesnesinin deerine edeer baka bir nesnedir. display_person ilevi iinde per.wage = 5.60; gibi bir atama yaplsayd, phesiz bu atamadan person nesnesi etkilenmemi olurdu.

373

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yerel bir yap nesnesinin deeri byle bir ilevle deitirilemez, deil mi? levin bir yap nesnesinin deerini almas ou zaman gereksiz bir blok kopyalanmasna neden olur. lgili yap trnn sizeof deeri bydke, bellek ve ilemci zaman kullanm asndan verimsizlik artacandan, pek tercih edilen bir yntem deildir. Kk yap nesneleri iin kullanlabilir.

Bir ilevin parametre deikeni yap trnden bir gsterici olursa, ilev de bu trden bir yap nesnesinin adresi ile arlabilir. Byle bir ileve yaplan ar ile, yalnzca bir gsterici nesnesinin sizeof'u kadar veri ileve gnderilir. Bu yntem ounlukla daha iyidir. Hemen her zaman bu yntem kullanlmaldr. Bu yntemde yap ne kadar byk olursa olsun, aktarlan yalnzca bir adres bilgisidir. stelik bu yntemde ilev kendisine adresi gnderilen yap deikenini deitirebilir. phesiz byle bir aktarm ilemi, yap nesnesinin bellekte tek bir blok olarak yer almas yznden mmkndr. Daha nce tanmlanan display_person isimli ilev, bu kez bir yap nesnesinin adresini alacak biimde yazlyor: void display_person(const struct Person *ptr) { printf("%d %s %s %.2lf\n", ptr->no, ptr->name, ptr->fname, ptr->wage);} int main() { struct Person person = {"Necati", "ERGIN", 2345, 3.56}; display_person(&person); } return 0;

Yap Nesnesinin Adresinin leve Gnderilmesi

display_person isimli ilevin parametre deikeninin const struct Person *ptr biiminde bildirildiini gryorsunuz. lev dardan struct Person trnden bir nesnenin adresini istiyor. Bildirimde kullanlan const anahtar szcnn, adresi alnan yap nesnesinde deiiklik yaplmayaca bilgisini ilettiini biliyorsunuz. levin kodu iinde kullanlan ptr->no ifadesi int trdendir. Bu ifade dardan adresi alnan yap nesnesinin no isimli eleman olan nesneye karlk gelir.

Bir ilevin geri dn deeri bir yap trnden olabilir. Aadaki rnei inceleyin: struct Point { double m_x, m_y; }; struct Point make_point(double x, double y); int main() { struct Point a;

Bir Yap Trne Geri Dnen levler

374

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

a = make_point(3, 5); /***/ return 0;

struct Point make_point(double x, double y) { struct Point temp; temp.m_x = x; temp.m_y = y; return temp; } levlerin geri dn deerlerini geici bir nesne yardmyla darya ilettiklerini biliyorsunuz. Geri dn deeri reten bir ilev iinde kullanlan return ifadesi, ilevin geri dn deerini iinde tutacak geici bir nesneye atanr. lev ar ifadesi de bu geici nesnenin deerini temsil eder. Bu geici nesne, ilevin kodunun almas return deyimine gelince yaratlr, ilev arsnn yer ald ifadenin deerlendirilmesi bitince yok edilir. Bir ilevin geri dn deerinin tr, ilevin geri dn deerini iinde tayacak geici nesnenin trdr. Yukardaki kod parasnda make_point ilevinin geri dn deeri, struct Point trndendir. Bu durumda ilevin geri dn deerinin aktarld geici nesne de struct Point trnden olur. Byle bir ilevin geri dn deeri, ayn trden bir yap deikenine atanabilir. Ancak byle ilevler bellekte ok fazla bir yer kaplamayan kk yap nesneleri iin kullanlmaldr. nk return ifadesiyle geici blgeye geici blgeden de geri dn deerinin saklanaca deikene atamalar yaplr. Bu da kt bir teknik olmaya adaydr. Kk yaplar iin tercih edilebilir. nk algsal karmakl daha azdr.

Yap Trnden Bir Alann Dinamik Olarak Elde Edilmesi

Nasl bir dizi iin bellek alan dinamik olarak elde edilebiliyorsa bir yap nesnesi iin de dinamik bir bellek blou elde edilebilir. Aadaki rnei derleyerek altrn: #include <stdio.h> #include <string.h> #include <stdlib.h> #define #define MAX_NAME_LEN MAX_FNAME_LEN 15 23

struct Person { char name[MAX_NAME_LEN + 1]; char fname[MAX_FNAME_LEN + 1]; int no; double wage; }; void set_person(struct Person *ptr, const char *name_ptr, const char *fname_ptr, int n, double w) { strcpy(ptr->name, name_ptr); strcpy(ptr->fname, fname_ptr); ptr->no = n; ptr->wage = w; } void display_person(const struct Person *ptr) { printf("%d %s %s %.2lf\n", ptr->no, ptr->name, ptr->fname, ptr->wage); 375

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} int main() { struct Person *ptr; ptr = (struct Person *)malloc(sizeof(struct Person)); if (ptr == NULL) { printf("bellek tahsis edilemiyor!\n"); exit(EXIT_FAILURE); } set_person(ptr, "Kaan", "Aslan", 2345, 4.80); display_person(ptr); free(ptr); return 0; } main ilevini inceleyin. malloc ileviyle struct Person trnden bir nesnenin sabilecei byklkte bir bellek alan dinamik olarak elde ediliyor. Dinamik bloun balang adresi struct Person trnden bir gsterici deiken olan ptr'de tutuluyor. set_person isimli ilevin, dardan adresini ald yap nesnesini, dier parametrelerine aktarlan bilgilerle doldurduunu gryorsunuz. Daha nce tanmlanan display_person isimli ilev ise, yine adresini ald yap nesnesinin tuttuu bilgileri ekrana yazdryor. Bu ilevlere, yap nesnesinin adresi olarak, elde edilen dinamik bloun balang adresinin geildiini gryorsunuz.

Bir Yap Trnden Adrese Geri Dnen levler

Bir ilevin geri dn deeri, bir yap trnden adres de olabilir. Bu durumda ilevin geri dn deerini iinde tutacak geici nesne bir yap trnden adrestir. lev ars, ilevin geri dn deeri trnden bir gstericiye atanabilir. Aadaki ilev tanmn inceleyin: struct Person *create_person(const char *name_ptr, const char *fname_ptr,int n, double w) { struct Person *ptr; ptr = (struct Person *)malloc(sizeof(struct Person)); if (ptr == NULL) { printf("bellek elde edilemiyor!\n"); exit(EXIT_FAILURE); } set_person(ptr, name_ptr, fname_ptr, n, w); } return ptr;

create_person ilevi dinamik olarak yerini ayrd bir nesneyi parametrelerine aktarlan bilgilerle dolduruyor. Daha sonra dinamik nesnenin adresiyle geri dnyor. Bu ilevi kullanan bir kod paras aadaki gibi olabilir: int main() { struct Person *ptr; ptr = create_person("Kaan", "Aslan", 2345, 4.80); display_person(ptr);

376

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

free(ptr); /***/ return 0;

main ilevi iinde arlan create_person ilevinin geri dndrd adres, struct Person trnden bir gsterici deiken olan ptr'de tutuluyor. lev, dinamik bir nesnenin adresini dndrdnden, dinamik alan geri vermek, ilevi aran kod parasnn sorumluluundadr. Byle bir ilev yerel bir yap deikeninin adresine geri dnemez. Global bir yap deikeninin adresine de geri dnmesinin ou zaman fazlaca bir anlam yoktur. lev, dinamik olarak yeri ayrlm bir yap nesnesinin adresi ile de geri dnebilir. Elde edilen dinamik alann "heap" bellek alanna geri verilmesi, ilevi aran kod parasnn sorumluluundadr. main iinde free ilevine yaplan ar ile dinamik alann geri verildiini gryorsunuz. Bir ilevin yerel bir yap nesnesinin adresine geri dnmesi tipik bir gsterici hatasdr. Yerel yap nesneleri de static anahtar szcyle bildirilebilir. phesiz bir ilevin static bir yap nesnesinin adresine geri dnmesi bir gsterici hatas deildir.

Bir yapnn eleman baka bir yap trnden olabilir. Bir yapnn elemannn baka bir yap trnden olabilmesi iki ayr biimde salanabilir: nce eleman olarak kullanlan yap tr bildirilir. Bu bildirimin grlr olduu bir yerde elemana sahip olan yapnn bildirimi yaplr. Aadaki rnei inceleyin: struct Date { int day, month, year; }; struct Student { char name[30]; struct Date birth_date; int no; }; Yukardaki rnekte nce struct Date yaps bildiriliyor. Daha sonra yaplan struct Student trnn bildiriminde, struct Date trnden bir eleman kullanlyor. kinci yntemde, eleman olan yap deikeninin bildirimi, elemana sahip yapnn bildirimi iinde yaplr: struct Student { char name[30]; struct Date { int day, month, year; }birth_date; int no; }; Yukardaki rnekte yer alan struct Date yaps gibi, bir yapnn iinde bildirilen yapya "isel yap" (nested structure) denir. Burada, ite bildirilen yap da sanki darda bildirilmi gibi ilem grr. Yani ieride bildirilen yap trnden deikenler tanmlanabilir. Yukardaki kod paras yalnzca bir bildirime karlk gelir. birth_date isimli bir nesne tanmlanm olmaz. struct Student trnden bir nesne tanmlandnda, bu yap nesnesinin struct Date trnden birth_date isimli eleman olur. ki ayr szdiziminden herhangi birinden sonra struct Student trnden bir nesne tanmlanm olsun: struct Student s;

Bileik Nesneler ve sel Yaplar

377

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

s yap deikeninin struct Date trnden olan birth_date isimli elemanna nokta ileciyle ulalabilir: s.birth_date Yukardaki ifade struct Date trnden bir nesne gsterir. stenirse bu nesnenin day, mon, year isimli elemanlarna nokta ilecinin ikinci kez kullanlmasyla eriilebilir: s.birth_date.mon ifadesi int trden bir nesne gsterir. Nokta ilecinin ile ncelik tablosunun birinci ncelik dzeyinde olduunu, bu ncelik dzeyine ilikin ncelik ynnn soldan saa olduunu anmsayn. Bir ya da birden fazla eleman programc tarafndan bildirilen bir trden olan yap nesnelerine bileik nesne (composite object) denir. Yukardaki rnekte struct Student trnden olan s deikeni bir bileik nesnedir.

Normal olarak ilkdeer vermede elemanlar srasyla, iteki yap da dikkate alnacak biimde, yap elemanlarna atanr. Ancak iteki yapnn elemanlarna verilen deerlerin, ayrca kme ayralar iine alnmas, okunabilirlii artrd iin salk verilir: struct Student s = {"Necati Ergin", {10, 10, 1967}, 123};

Bileik Nesnelere lkdeer Verilmesi

Eer iteki yap ayrca kme ayrac iine alnmsa iteki yapnn daha az sayda elemanna ilkdeer vermek mmkn olabilir: struct Student s = {"Necati Ergin", {10, 10}, 123}; Burada s deikeninin, birth_date elemannn year elemanna ilkdeer verilmiyor. Derleyici bu elemana otomatik olarak 0 deeri yerletirir. Burada iteki kme ayralar kullanlmasayd, 123 ilkdeeri year elemanna atanr, no elemanna ise 0 deeri verilirdi. Aadaki gibi bir bildirim de geerlidir: struct Data { int a[3]; long b; char c; } x = {{1, 2, 3}, 50000L, 'A'}, *p;

Bir yapnn baka bir yap trnden elemana sahip olmas durumunda, ierilen elemann ait olduu yap trnn arayznden, yani darya hizmet veren ilevlerinden faydalanma olana doar. Aadaki gibi bir yap bildirilmi olsun: struct Person { char name[16] char fname[20] struct Date bdate; }; Yukardaki bildirimde struct Person yapsnn bir eleman struct Date trndendir. struct Person per;

Bileik Nesne Oluturmann Faydalar

378

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

gibi bir nesne tanmlandnda bu nesnenin eleman olan per.bdate ifadesi struct Date trnden bir nesneye karlk gelir. per nesnesinin bu eleman sz konusu olduunda, struct Date trnn darya kar hizmet vermek zere tanmlam ilevleri arlabilir. rnein struct Date tryle ilgili olarak aadaki gibi bir ilevin bildirilmi olsun: void display_date(const struct Date *); per nesnesinin iinde tuttuu tarih bilgisini ekrana yazdrmak iin dorudan display_date ilevi arlabilir, deil mi? void display_person(const struct Person *ptr) { printf("isim : %s\n", ptr->name); printf("soyisim : %s\n", ptr->fname); printf ("doum tarihi : "); display_date(&ptr->bdate); } display_person ilevi, kendisine gnderilen adresteki struct Person trnden nesnenin eleman olan, bdate nesnesi iinde saklanan tarih bilgisini ekrana yazdrmak amacyla display_date isimli ilevi aryor. leve argman olarak bdate elemannn adresini gnderiyor.

Yaplarn Neden Kullanlr

Yaplarn kullanlmasyla baz faydalar elde edilebilir:

Birbirleri ile ilikili olan deikenler yap elemanlar olarak bir yap iinde toplanrsa algsal kolaylk salanr. rnein dzlemde bir nokta, bir tarih bilgisi, bir depoda bulunan mamllere ilikin zellikler bir yap ile temsil edilebilir. C dilinde bir ilev en fazla 8 - 10 parametre almaldr. Daha fazla parametreye sahip olmas kt bir tekniktir. Bir ilev ok fazla parametrik bilgiye gereksinim duyuyorsa, bu parametrik bilgiler bir yap biiminde ifade edilmelidir. O yap trnden bir deiken tanmlanmal, bu deikenin adresi ileve parametre olarak gnderilmelidir. rnein bir kiinin nfus czdan bilgilerini parametre olarak alp bunlar ekrana yazdracak bir ilev tasarlayacak olalm. Nfus czdan bilgilerinin hepsi bir yap biiminde ifade edilebilir ve yalnzca bu yapnn adresi ileve gnderilebilir. levlerin tek bir geri dn deeri vardr. Oysa ilevlerin ok deiik bilgileri aran ileve iletmesi istenebilir. Bu ilem yle yaplabilir: letilecek bilgiler bir yap biiminde ifade edilir. Sonra bu trden bir yap deikeni tanmlanarak adresi ileve gnderilir. lev de bu yap deikeninin iini doldurur. Yani ilev knda bilgiler yap deikeninin iinde olur.

Yap Dizileri

Yaplar da bir tr belirttiine gre yap trnden de diziler sz konusu olabilir. Bir yap dizisi elemanlar bellekte bitiik olarak yerletirilen , elemanlar ayn yap trnden olan bir dizidir. Yap dizilerine de ilkdeer verilebilir. lkdeer verme srasnda kullanlan iteki kme ayralar okunabilirlii artrr. rnek: #include <stdio.h>

379

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct Date { int d, m, y; }; int main() { struct Date bdates[5] = {{10, 10, 1958}, {4, 3, 1964}, {21, 6, 1967}, {22, 8, 1956}, {11, 3, 1970}}; struct Date *pdate; int i; pdate = bdates; for (i = 0; i < 5; ++i) { printf("%02d / %02d / %04d\n", pdate->d, pdate->m, pdate->y); ++pdate; } return 0; } Bir yap dizisi zerinde ilem yapan ilev de tanmlanabilir. Byle bir ilev, kendisini aran koddan, ilgili yap dizisinin balang adresi ile boyutunu almaldr. Aada, yap dizileri ile ilgili ilemler yapan baz ilevler tanmlanyor. lev tanmlarn dikkatle inceleyin:

380

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include #include #include #include #define #define #define

<stdio.h> <string.h> <stdlib.h> <time.h> NAME_LEN FNAME_LEN ARRAY_SIZE 16 20 200

struct Person { char m_name[NAME_LEN]; char m_fname[FNAME_LEN]; int m_no; double m_wage; }; void swap_persons(struct Person *p1, struct Person *p2); void set_person(struct Person *ptr, const char *name, const char *fname, int no, double wage); void set_person_random(struct Person *ptr); void display_person(const struct Person *ptr); void set_person(struct Person *ptr, const char *name, const char *fname, int no, double wage); void display_person_array(const struct Person *ptr, int size); void sort_person_array(struct Person *ptr, int size); char *name_array[20] = {"Ali", "Veli", "Hasan", "Necati", "Burcu", "Kaan", "Selami", "Salah", "Nejla", "Huseyin", "Derya", "Funda", "Kemal", "Burak", "Ozlem", "Deniz", "Nuri","Metin", "Guray", "Anil"}; char *fname_array[20] = {"Aslan", "Gencer", "Eker", "Ergin", "Serce", "Kaynak", "Acar", "Aymir", "Erdin", "Doganoglu", "Avsar", "Ozturk", "Yilmaz", "Tibet", "Arkin", "Cilasun", "Yildirim", "Demiroglu", "Torun", "Polatkan"}; /*************************************************************************/ void swap_persons(struct Person *p1, struct Person *p2) { struct Person temp = *p1; *p1 = *p2; *p2 = temp; } /*************************************************************************/ void set_person(struct Person *ptr, const char *name, const char *fname, int no, double wage) { ptr->m_no = no; ptr->m_wage = wage; strcpy(ptr->m_name, name); strcpy(ptr->m_fname, fname); } /*************************************************************************/ void set_person_random(struct Person *ptr) { ptr->m_no = rand() % 5000; ptr->m_wage = (double)rand() / RAND_MAX + rand() % 5 + 2; strcpy(ptr->m_name, name_array[rand() % 100]); strcpy(ptr->m_fname, fname_array[rand() % 50]); } /*************************************************************************/ void display_person(const struct Person *ptr) { printf("%-16s %-20s%-5d\t%4.2lf\n", ptr->m_name, ptr->m_fname, ptr-

381

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

>m_no, ptr->m_wage); } /*************************************************************************/ void set_person_array_random(struct Person *ptr, int size) { int k; for (k = 0; k < size; ++k) set_person_random(ptr + k);

} /*************************************************************************/ void display_person_array(const struct Person *ptr, int size) { int k; for (k = 0; k < size; ++k) display_person(ptr + k); } /*************************************************************************/ void sort_person_array(struct Person *ptr, int size) { int i, k; for (i = 0; i < size - 1; ++i) for (k = 0; k < size - 1 - i; ++k) if(strcmp(ptr[k].m_fname, ptr[k + 1].m_fname) > 0) swap_persons(ptr + k, ptr + k + 1); } /*************************************************************************/ int main() { struct Person a[ARRAY_SIZE]; srand(time(0)); set_person_array_random(a, ARRAY_SIZE); printf("siralanmadan once : \n"); display_person_array(a, ARRAY_SIZE); sort_person_array(a, ARRAY_SIZE); printf("siralanmadan sonra: \n"); display_person_array(a, ARRAY_SIZE); return 0; } Aadaki gibi bir ilev tanmlanmak istensin: void search_display(const struct Person *ptr, int size, const char *name); lev balang adresini ve boyutunu ald dizi iinde name parametresine aktarlan isimli kiilerin bilgilerini, soyadlarna gre artan srada ekrana yazdrsn. levin tanm ile snama amacyla yazlan yeni main ilevini inceleyin:

382

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void search_display(const struct Person *ptr, int size, const char *name) { int i,k; struct Person **pd = NULL; struct Person *temp; int count = 0; for (k = 0; k < size; ++k) if (!strcmp(ptr[k].m_name, name)) { pd = (struct Person **)realloc(pd, (count + 1) * sizeof(struct Person *)); if (pd == NULL) { printf("bellek blogu elde edilemiyor!\n"); exit(EXIT_FAILURE); } pd[count++] = (struct Person *)(ptr + k); } if (!count) { printf("aranan isim dizide bulunamadi\n"); return; } for (i = 0; i < count - 1; ++i) for (k = 0; k < count - 1 - i; ++k) if (strcmp(pd[k]->m_fname, pd[k + 1]->m_fname) > 0) { temp = pd[k]; pd[k] = pd[k + 1]; pd[k + 1] = temp; } for (k = 0; k < count; ++k) display_person(pd[k]); free(pd); printf("toplam %d kisi bulundu!\n", count); } int main() { struct Person a[ARRAY_SIZE]; char name_entry[20]; srand(time(0)); set_person_array_random(a, ARRAY_SIZE); display_person_array(a, ARRAY_SIZE); printf("aranan ismi giriniz : "); gets(name_entry); search_display(a, ARRAY_SIZE, name_entry); } return 0;

Yaplara likin Karmak Durumlar

Bir yapnn eleman baka bir yap trnden gsterici olabilir. rnein:

383

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct Date { int day, month, year; }; struct Person { char name[30]; struct Date *bdate; int no; }; struct Person per; eklinde bir tanmlama yaplm olsun: per.bdate ifadesi struct Date * trndendir. Bu ifade bir nesne belirtir. per.bdate->day ifadesinin tr int'dir. Bu ifade de nesne gsterir. &per.bdate->day ifadesinin int * trndendir. Tabi bu rnekte, bir deer atamas yaplmamsa, per.bdate ile belirtilen gsterici iinde rastgele bir adres vardr. Bu gstericinin bir gsterici hatasna neden olmadan kullanlabilmesi iin gvenilir bir adresi gstermesi gerekir. rnein bu alan malloc ileviyle dinamik olarak elde edilebilir: per.bdate = (struct Date *) malloc (sizeof(struct Date)); Yukardaki rnekte elimizde yalnzca struct Person trnden bir gsterici olduunu dnelim. struct Person *ptr; 1. ptr->bdate ifadesini struct Date * trndendir. 2. person->bdate->day ifadesinin tr int'dir. Bu rneklerde henz hibir yer ayrma ilemi yaplmamtr. Hem ptr hem de ptr->date gsterici deikenleri iin, dinamik bellek ilevleriyle yer elde edilmesi gerekir: ptr = (struct Person *) malloc(sizeof(struct Person)); ptr->bdate = (struct Date *) malloc(sizeof(struct Date)); Burada dinamik olarak elde edilen alanlar ters srada serbest braklmaldr: free(ptr->bdate); free(ptr); Bir yapnn eleman, kendi trnden bir yap deikeni olamaz. rnein: struct Sample { struct Sample a; /* GEERSZ */ }; nk burada struct Sample yapsnn uzunluu belirlenemez. Ancak bir yapnn eleman kendi trnden bir gsterici olabilir. rnein: struct Node { int val; struct Node *ptr; };

384

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir gstericinin tr uzunluu neyi gsterdiinden bamsz olarak, belirli olduundan yukardaki bildirimde derleyicinin struct Node trnn sizeof deerini saptamasna engel bir durum yoktur. Bu tr yaplar zellikle bal liste ve aa yaplarn (algoritmalarn) gerekletirmek amacyla kullanlabilir.

Bal listenin ne olduunu anlayabilmek iin nce "dizi" veri yapsn hatrlayn. Ayn trden nesnelerin bellekte tutulmak istendiini dnn. Bu nesneler bellekte birbirini izleyecek biimde yani aralarnda hibir boluk olmayacak biimde tutulabilir. C dilinin dizi aracnda da dizinin elemanlarnn bellekte bu ekilde tutulduunu biliyorsunuz. Ancak nesneleri bellekte bitiik olarak yerletirmek her zaman istene bir durum deildir. Bellekte nesnelerin bitiik yerletirilmesi drumunda, nesnelerden herhangi birine ulam deimez zamanda yaplabilir. Yani dizinin ya da dinamik dizinin herhangi bir elemanna ulama maliyeti, dizide tutuklan eleman sys ile doru orantl deildir. Dizide 100 eleman da olsa dizide 1000 eleman da olsa herhangi bir elemana ulam maliyeti deimez bir zaman olur. Neden? Zira dizinin elemanna aslnda bir adres ilemiyle ulalr deil mi? rnein pd bir dizinin balang adresini tutan bir gsterici ise pd[n] gibi bir ilem *(pd + n) ilemine karlk gelir. Bu ilemin de maliyetinin deimez olduu aktr. Bellekte elemanlar ardl olarak bulunmayan listelere bal liste denir. Bal listelerde her eleman kendinden sonraki elemann nerede olduu bilgisini de tutar. lk elemann yeri ise ayr bir gstericide tutulur. Bylece, bal listenin ilk elemannn adresi ile, bal listenin tm elemanlarna ulalabilir. Bal liste dizisinin her eleman bir yap nesnesidir. Bu yap nesnesinin baz yeleri bal liste elemanlarnn deerlerini veya tayacaklar dier bilgileri tutarken, bir yesi ise kendinden sonraki bal liste eleman olan yap nesnesinin adres bilgisini tutar. Bu ekilde elde edilen bir elemana "dm" ("node") denir. rnek: struct Node { int val; struct Node *next; }; Amacmz int trden deerleri bellekte bir liste eklinde tutmak olsun. Yukarda struct Node isimli bir yapnn bildirimini gryorsunuz. Tutulacak int trden deerler yapmzn val isimli elemannn deeri olarak bellekte yer alacak. Yapnn yine struct Node trnden olan gsterici eleman ise kendisinden bir sonra gelen yap nesnesinin adresini tutacak. Bylece bir struct Node nesnesinin adresi elimizdeyken, bu nesnenin iinde tutulan int trden veriye ulaabileceimiz gibi, nesnenin iinde yer alan next gstericisi yardmyla da bir sonraki elemana ulaabiliriz.

Bal Liste Nedir

Bir dizinin herhangi bir elemanna deimez bir zamanda eriilebilir. Zira bir elemana ulama bir adres bilgisine bir tamsaynn toplanmasyla olur. Oysa bal listelerde bir elemana eriebilmek iin, bal listede ondan nce yer alan btn elemanlar dolamak gerekir. Bu durumda bir elemana ulamann ortalama maliyeti ortadaki elemana ulamann maliyetidir. Bu da bal listedeki eleman saysnn artmasyla bir elemana ulama maliyetinin dorusal biimde artaca anlamna gelir. Dizilerde araya eleman ekleme ya da eleman silme ilemleri iin blok kaydrmas yapmak gerekir. Oysa bal listelerde bu ilemler ok kolay yaplabilir.

Bal Listelerle Dizilerin Karlatrlmas

385

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

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.

386

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

TR SMLER BLDRMLER ve typedef BELRLEYCS


C dilinde derleyicinin daha nce bildii bir tre her bakmdan onun yerini tutabilen yeni isimler verilebilir. Bu typedef anahtar szc kullanlarak yaplan bir bildirim deyimi ile salanr. typedef bildiriminin genel biimi yledir: typdef <isim> <yeni isim>; rnek: typedef unsigned int UINT; Bu bildirimden sonra UINT ismi derleyici tarafndan unsigned int trnn yeni bir ismi olarak ele alnr. Yani kaynak kod iinde, typedef bildiriminin grlr olduu bir noktada, UINT ismi kullanldnda derleyici bunu unsigned int tr olarak anlamlandrr. UINT x, y, z; Bildiriminde artk x, y, z deikenleri unsigned int trnden tanmlanm olurlar. typedef bildiriminden sonra artk UINT ismi tr belirten isim gereken her yerde kullanlabilir: printf("%d\n", sizeof(UINT)); typedef anahtar szc ile yeni bir tr ismi oluturulmas, 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 bildirimi derleyici tarafndan ele alnrken, #define nilemci komutu ile tanmlanan isimler nilemci program ilgilendirir. Yani yukardaki nilemci komutunun kullanlmasndan sonra, zaten derleyici UINT ismini grmez. Derleyiciye sra geldiinde, UINT isminin yerini unsigned int atomlar alm olur. Alglanmas zor olan tr isimlerine, typedef bildirimleriyle alglanmas daha kolay tr isimleri verilebilir. typedef bildirimleri iin aada verilen basit kural kolaylk salar: typedef anahtar szc, her tr bildirimin nne gelebilir. typedef anahtar szc bir bildirimin nne geldiinde, typedef kullanlmam olsayd deiken ismi olacak isimler, typedef anahtar szc eklendiinde artk ilgili trn ismi olur. rnein: char *pstr; biiminde bildirilen pstr char* trnden bir deikendir. typedef char *pstr;

387

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki deyim ise bir typedef bildirimidir. Artk pstr derleyici tarafndan bir tr ismi olarak ele alnr. pstr, char trden bir adres trnn baka bir ismi olarak, geerli bir trdr. Yani yukardaki typedef bildiriminden sonra pstr p; gibi bir tanmlama yaplabilir. Bu tanmlama char *p ile ayn anlama gelir. Bir typedef bildirimi ile elde edilen fayda her zaman #define nilemci komutuyla salanamayabilir: #define pstr char* gibi bir nilemci ismi tanmlamas yapldnda, nilemci pstr ismini grd yerde bunun yerine char * atomlarn yerletirir. char *str; gibi bir bildirimin pstr str; olarak yazlmasnda bir hata sz konusu olmaz. nilemci pstr yerine char * yerletirdiinde derleyiciye giden kod char *str haline gelir. char *p1, *p2, *p3; nilemci #define komutunun kullanlarak yukardaki gibi bir bildirimin yaplmak istensin. pstr p1, p2, p3; yazldnda, nilemci yer deitirme ilemini yaptktan sonra derleyiciye verilen kod aadaki biime dnr: char *p1, p2, p3; Bu tanmlama yaplmak istenen tanmlamaya edeer deildir. Yukardaki bildirimde yalnzca p1 bir gsterici deikendir. p2 ile p3 gsterici deikenler deildir. char trden deikenlerdir. Bir diziye ilikin de yeni tr ismi bildirimi yaplabilir: char isimdizi[20]; Yukardaki deyim ile isimdizi isimli char trden 20 elemanl bir dizi tanmlanm olur. typedef char isimdizi[20]; Yukardaki deyim ise bir bildirimdir. Bu bildirim ile isimdizi artk 20 elemanl int trden dizilerin tr ismidir. Bu typedef bildiriminden sonra eer

388

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

isimdizi a, b, c; Gibi bir tanmlama yaplrsa, artk a, b, c her biri 20 elemanl char trden dizilerdir. Bir typedef bildirimi ile birden fazla tr ismi yaratlabilir: typedef unsigned int WORD, UINT; Yukardaki bildirim deyiminden sonra, hem WORD hem de UINT, unsigned int trnn yerine geen yeni tr isimleridir: WORD x, y; UINT k, l; Artk x, y, k, l unsigned int trden deikenlerdir. 10 elemanl char trden gsterici dizisi iin tr ismi bildirimi yle yaplabilir: typedef char *PSTRARRAY[10]; Bu bildirim deyiminden sonra PSTRARRAY s; ile char *s[10]; tamamen ayn anlama gelir. Bir tr ismi baka tr isimlerinin bildiriminde de kullanlabilir: typedef unsigned int WORD; typedef WORD UINT; Yeni oluturulan tr isimleri, okunabilirlik asndan ya tamamen byk harflerden seilir, ya da bu isimlerin yalnzca ilk harfleri byk harf yaplr.

Bir yap bildirimiyle yeni bir tr yaratlm olur. Bu tr nce derleyiciye tantldktan sonra, bu tre ilikin deikenler tanmlanabilir: struct Data { int a, b, c; }; Yukardaki bildirimle yeni bir veri tr yaratlyor. C dilinde bu veri trnn ismi struct Data'dr. Trn ismi Data deildir. Yani bu veri trnden bir nesne tanmlamak istendiinde tr ismi olarak struct Data yazlmaldr. [C++ dilinde yap isimleri (structure tags) ayn zamanda trn de ismidir. struct anahtar szc olmadan kullanldnda da bu trn ismi olarak derleyici tarafndan kabul grr.] Yukardaki bildirimden sonra rnein bir tanmlama yaplacak olsa struct Data d; biiminde yaplmaldr. C dilinde bu tanmlamann

typedef Bildirimlerinin Yaplar in Kullanm

389

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Data d; biiminde yaplmas geersizdir. Oysa C++ dilinde bu durum geerlidir. te struct anahtar szcnn, yap nesnesi tanmlamalarnda yap isminden nce kullanlma zorunluluunu ortadan kaldrmak iin programclar, typedef bildirimiyle kendi bildirdikleri yap trlerine ilikin yeni tr isimleri olutururlar. Bir yap trne ilikin yeni bir tr isminin oluturulmas ayr biimde yaplabilir. 1. nce yap bildirimi yaplr. Daha sonra bildirilen yap tr iin ayr bir typedef bildirimi yaplr: struct tagPerson { char name[30]; int no; }; typedef struct tagPerson Person; Person per = {"Necati Ergin", 123}; Yukardaki rnekte nce struct tagPerson isimli bir tr yaratlyor daha sonra typedef bildirimiyle struct tagPerson trne yeni bir isim olarak Person ismi veriliyor. typedef bildiriminden sonra, hem struct tagPerson hem de Person isimleri, tr isimleri olarak kullanlabilir. 2. typedef bildirimi ile yap bildirimi tek bir bildirim biiminde birletirilebilir: typedef struct tagPerson { char name[30]; int no; } Person; Person per; Daha nce verilen kural anmsayn: Bu bildirimin banda typedef anahtar szc olmasayd Person ismi struct tagPerson trnden bir nesnenin ismi olurdu, deil mi? Yukardaki rnekte hem ismi struct tagPerson olan bir yap bildiriliyor hem de typedef bildirimiyle bu yapya yeni bir isim olarak Person ismi veriliyor. lgili bildirimden sonra hem struct tagPerson hem de Person isimleri, tr isimleri olarak kullanlabilir. 3. Yap ismi (structure tag) kullanlmadan yaplan bir yap bildirimi ile typedef bildirimi birletirilebilir: typedef struct { char name[30]; int no; }Person; Person y; Yukardaki rnekte yaratlan trn tek bir ismi vardr. Bu isim Person ismidir. Bu tr kullanlmak istendiinde artk struct anahtar szc kullanlamaz. Programclarn ou, yap isimleriyle (structure tag), typedef bildirimiyle oluturulacak tr isimleri iin farkl isimler bulmak yerine birka karakter kullanarak aralarnda iliki kurarlar. ok kullanlan kalplardan biri, yap isminin bana bir "alt tire" karakteri konularak tr isminden ayrlmasdr:

390

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

typedef struct _Employee { int no; char name[30]; double fee; } Employee; phesiz aadaki gibi bir bildirim de hata oluturmazd: typedef struct Employee { int no; char name[30]; double fee; }Employee; Trn eski ismi Employee deil struct Employee dir. Yani struct Employee trne typedef bildirimiyle Employee ismi verilmitir. Bu durum bir saknca oluturmaz. Windows API programlarnda yap bildirimlerine ilikin typedef bildirimlerinde aadaki gibi bir kalp da kullanlr: typedef struct tagEmployee { int no; char name[30]; double fee; } Employee; Yap ismi tag nekiyle balatlyor, seilen typedef isminde tag neki kaldrlyor. Bir yap bildirimi sz konusu olduunda, yap trne isim vermek yerine o yapya ilikin adres trne yeni isim verilebilir. Aadaki rnei inceleyin: struct { int a, b, c; }*Hdata; Yukardaki bildirimde, bildirimi yaplan yapnn bir ismi yoktur. Ancak typedef bildirimiyle bu yapya ilikin adres trne Hdata ismi veriliyor. Bu durumda Unix, Windows sistemlerinde sizeof(Hdata) ifadesinin deeri 4 sizeof(*Hdata) ifadesinin deeri 12 dir.

Baz standart ilevlerin bildiriminde, dorudan doal bir veri trn kullanmak yerine daha nceden belirlenmi baz typedef isimleri kullanlr. Doal trler sistemden sisteme farkl uzunlukta olabileceinden, baz ilevlerin ara yznde doal tr isimlerini kullanmak yerine bir typedef isminin kullanlmas, derleyiciyi yazanlara daha byk bir esneklik salar. rnein standart malloc ilevinin stdlib.h balk dosyas iindeki bildirimi aadaki gibidir: void *malloc(size_t ntypes); Bu bildirimde size_t isminin bir tr ismi olarak kullanldn gryorsunuz. Derleyicilerin ounda bu tr isminin bildirimi stddef.h, stdio.h, stdlib.h balk dosyalarnda aadaki gibi yaplr:

Standart Balk Dosyalarnda Bulunan Baz typedef Bildirimleri

391

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

typedef unsigned int size_t; Bu tr gerekte ne olduu derleyicileri yazanlara braklm olan bir trdr. Derleyiciler ilgili balk dosyalarnda yaplan typedef bildirimleriyle iaretsiz bir tamsay trlerinden birine size_t ismini verir. Standartlara gre size_t tr sizeof ilecinin rettii deerin trdr. ANSI standartlarnda bir ok ilevin bildiriminde size_t tr geer. rnein strlen ilevinin gerek bildirimi, string.h balk dosyasnn iinde size_t strlen(const char *); olarak yaplr. Yani malloc ilevinin parametre deikeni ya da strlen ilevinin geri dn deeri size_t trndendir. Bu trn gerekte ne olduu derleyicileri yazanlara braklr. Ancak hemen hemen btn derleyicilerde size_t tr unsigned int trnn typedef ismi olarak belirlenir. size_t tr gibi aslnda ne olduu derleyiciye braklm olan, yani derleyici yazanlarn ilgili balk dosyalarnda typedef bildirimlerini yapacaklar baka tr isimleri de C standartlar tarafndan tanmlanmtr. Bu trlerden bazlar unlardr: time_t : standart time ilevinin geri dn deerinin trdr. time.h balk dosyas iinde, derleyiciyi yazanlar herhangi bir temel veri trne typedef bildirimiyle bu ismi verirler. Bir zorunluluk olmasa da, time_t derleyicilerin hemen hepsinde long trnn typedef ismi olarak seilir. clock_t : standart clock ilevinin geri dn deerinin trdr. time.h balk dosyas iinde, derleyiciyi yazanlar herhangi bir temel veri trne typedef bildirimiyle bu ismi verirler. Derleyicilerin hemen hepsinde long trnn typedef ismi olarak seilir. ptrdiff_t : Bir adres bilgisinden baka bir adres bilgisinin kartlmasyla bir tamsay elde edildiini biliyorsunuz. ki adresin birbirinden fark ptrdiff_t trndendir. Zaten bu trn ismi de pointer difference szcklerinden gelir. Bu tr iaretli tamsay trlerinden biri olmak zorundadr. Derleyicilerin hemen hepsinde int trnn typedef ismi olarak seilir. fpos_t: stdio.h balk dosyas iinde bildirilen fgetpos ve fsetpos ilevlerinin parametre deikeni olan gstericilerin trdr. wchar_t: Bu trn bildirimin stdlib.h ve stddef.h balk dosyalarnn iinde yaplmtr. Sistem tarafndan desteklenen yredeki (locale) en byk geniletilmi karakter setini temsil edebilecek bir trdr. rnein geniletilmi karakter setinin tm deerleri iki byte'lk bir alanda ifade edilebiliyorsa wchar_t tr unsigned short int trnn typedef ismi olabilir. wchar_t ismi wide character szcklerinden gelir. Geni karakterler, geni karakter deimezleri, oklu karakterler gibi noktalara "yerelletirme" konusunda ayrntl olarak deinilecek. div_t ve ldiv_t: Bu trlerin bildirimleri stdlib.h isimli balk dosyasnda yaplmtr. div_t stdlib.h iinde bildirilen standart div ilevinin geri dn deeri olan yap trdr. ldiv_t tr de yine stdlib.h balk dosyas iinde bildirilen ldiv isimli ilevin geri dn deeri olan yap trdr.

typedef bildirimleri iin de bilinirlik alan kurallar geerlidir. Bir blok iinde tanmlanan bir typedef ismi, o blok dnda bilinmez.

typedef ile Bildirimi Yaplan Tr simlerinin Bilinirlik Alanlar

392

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void func() { typedef int } void foo() { WORD x; }

WORD;

/*Geersiz*/

Yukardaki programda WORD x; deyimi geersizdir. Zira WORD tr yalnzca func ilevinin ana blou iinde bilinen bir veri trdr. Bu bloun dnda bilinmez. C dilinde blok iinde yaplan bildirimlerin bloklarn banda yaplmas zorunludur. typedef bildirimiyle blok iinde yaplan yeni tr ismi bildirimleri de bloklarn banda yaplmak zorundadr. Ancak hemen her zaman typedef bildirimleri global dzeyde yaplr. Uygulamalarda typedef bildirimleri genellikle, ya kaynak dosyann banda ya da balk dosyalar iinde yaplr. nk typedef isimleri ounlukla darya hizmet veren bir modln arayzne aittir. Ayn typedef ismi farkl iki trn yeni ismi olarak bildirilemez: typedef int WORD; /*...*/ typedef long WORD; /* Geersiz! */

Okunabilirlii artrmak iin typedef bildirimleri yaplabilir. Baz trlere onlarn kullanm amalarna uygun isimler verilirse kaynak kod daha kolay okunur daha kolay anlamlandrlr. rnein char tr genelde karakter deimezlerinin atand bir trdr. char tr yalnzca bir byte'lk bir veri olarak kullanlacaksa, yani yaz ilemlerinde kullanlmayacak ise aadaki gibi bir tr tanmlamas yerinde olur: typedef char BYTE; /*...*/ BYTE x; C89 standartlarnda bool tr doal bir veri tr deildir. Ancak bir typedef bildirimiyle int trne bool ismi verilebilir: typedef int bool; typedef bildirimleri yazm kolayl salar. Karmak pek ok tr ismi typedef bildirimi kullanlarak kolay bir biimde yazlabilir. Program okuyanlar tr bilgisine karlk gelen karmak atomlar yerine onu temsil eden yaln bir isimle karlarlar. Aada nce bir ilev adresine ilikin tre typedef bildirimiyle yeni bir isim veriliyor, daha sonra bu trden bir nesne tanmlanyor: typedef struct Person * *Fpper)(struct Person *, int); /*...*/ Fpper fptr; 3. typedef bildirimleri bazen de tanabilirlii artrmak amacyla kullanlr. typedef bildirimlerinin kullanlmasyla, yazlan ilevlere ilikin veri yaplar deise bile kaynak

typedef Bildirimlerinin Amac Nedir

393

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

programn deimesi gerekmez. rnein, bir ktphanede birtakm ilevlerin geri dn deerleri unsigned int trnden olsun. Daha sonraki uygulamalarda bu ilevlerin geri dn deerlerinin trnn unsigned long tr olarak deitirildiini dnelim. Eer programc bu bu ilevlere ilikin kodlarda typedef bildirimleri kullanmsa, daha nce yazd kodlar deitirmesine gerek kalmaz, yalnzca typedef bildirimlerini deitirmesi yeterli olur. rnein: typedef unsigned int HANDLE; /***/ HANDLE hnd; hnd = GetHandle(); Burada GetHandle ilevinin geri dn deerinin tr sonraki uyarlamalarda deierek unsigned long yaplm olsun. Yalnzca typedef bildiriminin deitirilmesi yeterli olur: typedef unsigned long HANDLE; Bir C programnda deerleri 0 ile 50000 arasnda deiebilecek olan saya amacyla kullanlacak deikenler kullanlmak istensin. Bu ama iin long int tr seilebilir, nk long int tr Windows ya da Unix sistemlerinde 2.147.483.647 ye kadar deerleri tutabilir. Ama long int tr yerine int trn kullanmak, aritmetik ilemlerin daha hzl yaplabilmesi asndan tercih edilebilir. Ayrca int trden olan deikenler baz sistemlerde bellekte daha az yer kaplayabilir. int trn kullanmak yerine bu ama iin yeni bir tr ismi yaratlabilir: typedef int SAYAC; SAYAC a, b, c; Kaynak kodun int trnn 16 bit uzunluunda olduu bir sistemde derlenmesi durumunda typedef bildirimi deitirilebilir: typedef long SAYAC;

Bu teknikle tanabilirlie ilikin btn sorunlarn zlm olaca dnlmemelidir. rnein SAYAC trnden bir deiken printf ya da scanf ilevlerine yaplan arlarda argman olan ifadenin tr olarak kullanlm olabilir: #include <stdio.h> typedef int SAYAC; int main() { SAYAC a, b, c; /***/ scanf("%d%d%d", &a, &b. &c); /***/ printf("%d %d %d", a, b, c); /***/ return 0; } Yukardaki deyimlerde a, b, c deikenleri SAYAC trnden yani int trden tanmlanyor. printf ile scanf ilevlerine yaplan arlarda da bu deikenlere ilikin format karakterleri olarak %d seiliyor. Ancak SAYAC trnn long tr olarak deitirilmesi durumunda printf ve scanf ilevlerinde bu trden deikenlerin yazdrlmasnda kullanlan format karakterlerinin de %d yerine %ld olarak deitirilmesi gerekir.

394

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

typedef bildirimi ile gsterici trlerine yeni isim verilmesinde dikkat edilmesi gereken bir nokta vardr. Tr niteleyicileri konusunda ele alnd gibi C dilinde aadaki gibi bir tanmlama yapldnda const int *ptr; ptr gsterdii yer const olan bir gstericidir. Yani ptr deikeninin gsterdii yerdeki nesne deitirilemez: *ptr = 10; gibi bir atama geersizdir. Ancak tanmlama int *const ptr; biiminde yaplrsa, ptr kendisi const olan bir gstericidir. ptr gstericisinin gsterdii nesnenin deeri deitirilebilir, ama ptr gstericisinin iindeki adres deitirilemez, yani ptr = (int *) 0x1F00; gibi bir atama yaplmas geersizdir. typedef int *IPTR; gibi bir bildirimden sonra const IPTR p; biiminde bir tanmlama yapldnda, p gstericisinin deeri deitirilemez, p gstericisinin gsterdii yerdeki nesnenin deeri deitirilebilir. Yani *p nesnesine atama yaplabilir. Baka bir deyile const IPTR ptr; deyimi ile int *const ptr; deyimi edeerdir. Windows iletim sistemi altnda alacak C ya da C++ programlarnn yazlmasnda typedef bildirimleri sklkla kullanlr. windows.h isimli balk dosyasnda temel veri trlerinin ouna typedef bildirimleriyle yeni isimler verilmitir. Windows API programlamada windows.h balk dosyas kaynak koda eklenmelidir. Bu dosyann iinde API ilevlerinin bildirimleri, eitli yap bildirimleri, typedef isimleri, nemli simgesel deimezler bulunur.

typedef le Adres Trlerine sim Verilmesi

windows.h inde Tanmlanan typedef simleri


typedef int BOOL;

Bu trle ilikili iki simgesel deimez de tanmlanmtr. #define FALSE #define TRUE 0 1

395

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BOOL tr, zellikle ilevlerin geri dn deerlerinde kullanlr. Bu durum ilevin 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 simgeler. 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 kullanlr. Win16 sistemlerinde uzak ve yakn gsterici kavramlar vard. Dolaysyla o zamanlar, P nekli gstericiler yakn gstericileri, LP nekli gstericiler ise uzak gstericileri temsil ediyordu. Fakat Win32 sistemlerinde yakn ve uzak gsterici kavramlar yoktur. Bu durumda, P nekli gstericilerle LP nekli gstericiler arasnda hibir fark yoktur. Ancak, Win16'daki alkanlkla hala LP nekli typedef isimleri kullanlr. Windows.h iinde her ihtimale kar -Win16 programlar alabilsin diye- near ve far szckleri aadaki gibi silinmitir. #define #define typedef typedef far near char near *PSTR; char far *LPSTR;

PSTR ya da LPSTR Win32 sistemlerinde tamamen ayn anlama gelir ve char* trn belirtir. typedef char *PSTR; typedef char *LPSTR; Gstericilerde const'luk P ya da LP'den sonra C ekiyle belirtilir. rnein; typedef const char *PCSTR; typedef const char *LPCSTR; Klasik typedef isimlerinin hepsinin gsterici karlklar da vardr. Btn gsterici trleri, Win16 uyumlu olmas iin P ve LP nekleriyle ayrca bildirilmitir. typedef typedef typedef typedef BYTE *PBYTE; WORD *PWORD; const BYTE *PCBYTE; const DWORD *LPCDWORD;

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

396

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Tarih ve Zaman le lgili lem Yapan Standart levler

Standart balk dosyalarndan time.h iinde bildirilen, tarih ve zaman bilgileriyle ilgili faydal iler yapmak iin tanmlanan baz standart ilevler vardr. Bu ilevlerin bazlarnn parametre deikenleri ya da geri dn deerleri bir yap trnden adres bilgileridir. Aada bu ilevler aklanyor:

time levi
time_t time (time_t *timer); levin geri dn deeri standart bir typedef tr olan time_t trdr. Derleyicilerin ounun bu tr long trnn typedef ismi olarak bildirir. levin parametre deikeni de bu trden bir adrestir. lev adresi gnderilen nesneye belirli bir tarihten (ou sistemde bu tarih 01.01.1970 tarihidir) ilev arsna kadar geen saniye says deerini yazar. Bu deer standartlarda "takvim zaman" ("calender time") olarak geer. Tarih ve zaman zerinde ilem yapan dier baz ilevler ilerini yapabilmek iin bu deere gereksinim duyar. lev, bu deeri ayn zamanda geri dn deeri olarak da darya iletir. Eer takvim zaman bilgisi elde edilemiyorsa ilev (time_t)-1 deerine geri dner. Eer ileve NULL adresi gnderilirse, ilev bu deeri zel bir ileti olarak alglar, hibir nesneye yazma yapmadan, saniye bilgisini yalnz geri dn deeri ile darya aktarr. Aada ilevin kullanlmasyla ilgili basit bir kod paras veriliyor: #include <stdio.h> #include <time.h> int main() { time_t timer1, timer2, timer3; timer1 = time(&timer2); printf("devam etmek iin bir tua basn : "); getchar(); timer3 = time(NULL); printf("timer1 = %ld\n", (long)timer1); printf("timer2= %ld\n", (long)timer2); printf("timer3= %ld\n", (long)timer3); } return 0;

01.01.1970'den geen saniye says yani takvim zaman dorudan kullanlabilecek bir zaman bilgisi deildir. localtime ilevi bu bilgiyi alarak faydal paralara ayrr. levin time.h balk dosyas iindeki bildirimi aadaki gibidir: struct tm *localtime(const time_t *timer); levin geri dn deeri, time.h iinde bildirilen bir yap olan struct tm trnden bir adrestir. Bu yap aadaki gibi bildirilmitir:

localtime levi

397

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct tm { int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; int tm_year; int tm_wday; int tm_yday; int tm_isdst; }; Yapnn ismi tm nekiyle balayan tm elemanlar int trdendir. Her bir eleman tarih ya da zaman ile ilgili faydal bir veriyi tutar. Aada yapnn elemanlar hakknda bilgi veriliyor: tm_sec : saniye deeri (0 - 60) tm_min : dakika deeri (0 - 60) tm_hour : saat deeri (0 24) tm_mday : Ayn gn (1 31) tm_mon : Ay deeri (0 Ocak, 1 ubat, 2 Mart) tm_year : Yl deerinin 1900 eksii tm_wday : Haftann gn (0 Pazar, 1 Pazartesi, 2 Sal) tm_yday : Yln gn (1 Ocak iin 0) tm_isdst :Gn tasarruf modu ile ilgili bilgi. Bu elemann deerinin pozitif ise tasarruf modunda olunduu bilgisi iletilmi olur. Bu deer 0 ise tasarruf modu deildir. Elemann deerinin negatif olmas durumunda bu bilgi elde edilemiyor demektir. struct tm yaps ile tutulan zaman bilgisine "ayrtrlm zaman bilgisi" (broken-down time) denir. localtime ilevi statik mrl bir struct tm nesnesinin adresi ile geri dner. levin geri dn deeri olan adresteki yap nesnesinin deeri kullanlmadan ya da baka bir nesneye aktarlmadan, ilev bir kez daha arlrsa, daha nceki ar ile ilgili olarak retilen deerin stne yazlm olur. levin parametre deikeni time_t trnden bir nesneyi gsteren gstericidir. lev adresini ald bu nesneden takvim zaman bilgisini alr. Aada localtime ilevini kullanan rnek bir kod veriliyor: #include <stdio.h> #include <time.h> char *months[12] = {"Ocak", "Subat", "Mart", "Nisan", "Mayis", "Haziran", "Temmuz", "Agustos", "Eylul","Ekim", "Kasim", "Aralik"}; char *days[7] = {"Pazar","Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi"}; int main() { time_t timer; struct tm *tptr; time(&timer); tptr = localtime(&timer); printf("Tarih : %02d %s %04d %s\n", tptr->tm_mday,months[tptr>tm_mon], ptr->tm_year + 1900, days[tptr>tm_wday]); printf("Saat:%02d:%02d:%02d\n", tptr->tm_hour, tptr->tm_min, tptr->tm_sec); printf("bugun %d yilinin %d. gunu\n", tptr->tm_year + 1900, tptr->tm_yday); if (tptr->tm_isdst < 0)

398

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("gun isigi tasarruf modu bilgisi elde edilemiyor!\n"); else if (tptr->tm_isdst > 0) printf("gun isigi tasarruf modundayiz!\n"); else printf("gun isigi tasarruf modunda degiliz!\n"); return 0; }

ctime levi

Bu ilev, takvim zaman bilgisini girdi olarak alarak bu bilgiyi bir yazya dntrr. levin bildirimi aadaki gibidir: char *ctime(const time_t *time); levin parametre deikeni time_t trnden bir adrestir. lev adresini ald nesneden takvim zaman bilgisini okur. levin geri dn deeri 26 karakterlik bir yaznn balang adresidir. Bu yaz ayrtrlm zaman bilgilerini ieren zel olarak formatlanm bir yazdr: F r i F e b 2 3 1 2 : 2 5 : 5 4 2 0 0 4 \n '\0'

Fri Feb 23 12:25:12 2004 lev statik mrl bir dizinin adresiyle geri dner. levin geri dn deeri olan adresteki yaz kullanlmadan ya da baka bir diziye aktarlmadan, ilev bir kez daha arlrsa, daha nceki ar ile ilgili olarak retilen yaznn stne yazlm olur. Aada ilevin kullanmna ilikin rnek bir kod paras yer alyor: #include <stdio.h> #include <time.h> int main() { char *ptr; time_t timer; timer = time(NULL); ptr = ctime(&timer); printf("%s", ptr); return 0; }

ctime ilevinin yapt iin aynsn yapar. Ancak girdi olarak takvim zamann deil ayrtrlm zaman bilgisini alr: char *asctime (const struct tm *tblock); levin parametre deikeni struct tm yaps trnden bir adrestir. lev, adresini ald nesneden, ayrtrlm zaman bilgilerini okur. Bu bilgiyi bir yazya dntrerek, zel bir formatta 26 karakterlik statik mrl bir dizi iine yazar. lev, ilgili yaznn balang adresine geri dner. levin geri dn deeri olan adresteki yaz kullanlmadan ya da baka bir diziye aktarlmadan, ilev bir kez daha arlrsa, daha nceki ar ile ilgili olarak retilen yaznn stne yazlm olur. Aada ilevin kullanmna ilikin rnek bir kod paras yer alyor:

asctime levi

399

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <time.h> int main() { time_t timer; timer = time(NULL); printf("%s", asctime(localtime(&timer))); return 0;

Yukardaki kod parasnda, localtime ilevinin geri dn deeri olan struct tm trnden adres dorudan asctime ilevine argman olarak gnderiliyor. asctime ilevinin geri dn deeri olan adres de printf ilevine geiliyor.

clock levi

levin geri dn deeri time.h balk dosyas iinde bildirilen standart bir typedef tr olan clock_t trdr. Derleyicilerin hemen hepsinde bu tr long trnn typedef ismi olarak bildirilir: clock_t clock(void); lev, programn almaya balamasyla ilev arsna kadar geen sreye geri dner. Ancak ilevin geri dn deerine ilikin birim saniye deil ilemcinin zaman devresinin tick saysdr. levin geri dn deerini saniyeye dntrmek iin, geri dn deeri ilemcinin saniyedeki tick saysna blnmelidir. Zaman devresinin bir saniyedeki tick sayisi time.h iinde CLOCKS_PER_SEC isimli bir simgesel deimez olarak tanmlanmtr. #define CLOCKS_PER_SEC 1000

Kodun tanabilirlii asndan bu simgesel deimez kullanlmaldr. Derleyicilerin ou daha ksa bir simgesel deimezin kullanlmasn salamak zere CLK_TCK isimli bir simgesel deimez daha tanmlar: #define CLK_TCK CLOCKS_PER_SEC

Ancak tanabilirlik asndan CLOCKS_PER_SEC simgesel deimezi kullanlmaldr. clock ileviyle ilgili aadaki rnek program inceleyin: #include <stdio.h> #include <math.h> #include <time.h> int main() { clock_t clock1, clock2; long k; clock1 = clock(); for (k = 1; k < 10000000; ++k) sqrt(sqrt(k) + sqrt(k + 1)); clock2 = clock(); printf("dongu %lf saniye surdu!\n", (double)(clock2 - clock1) / CLK_TCK); } return 0;

400

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aada clock ilevinden faydalanan bir geciktirme ilevi yazlyor: #include <math.h> #include <time.h> #define SENSITIVITY 0.1

void delay(double sec) { double total = 0.; clock_t tstart = clock(); clock_t tend; double duration; for (;;) { tend = clock(); duration = (double)(tend - tstart) / CLOCKS_PER_SEC; if (fabs(duration - sec) < SENSITIVITY) return; }

difftime levi

Standart difftime ilevi takvim zaman cinsinden verilen iki zaman bilgisi arasndaki saniye farkn bulmak iin kullanlabilir. lev bildirimi aadaki gibidir: double difftime(time_t timer2, time_t timer1); levin geri dn deeri timer1 deeri ile timer2 deeri arasnda geen saniye saysdr. #include <stdio.h> #include <math.h> #include <time.h> int main() { time_t start,finish; long i; double result, elapsed_time; printf("20000000 kez kare kok aliniyor.\n"); time(&start); for (i = 1; i <= 20000000; i++) result = sqrt(i); time(&finish); elapsed_time = difftime(finish, start); printf("\nToplam sure = %lf saniye.\n", elapsed_time); return 0;

Bu standart ilev ayrtrlm zaman bilgisini (broken-down time) takvim zamanna (calender time) dntrr. levin bildirimi aadaki gibidir: time_t mktime(struct tm *tptr):

mktime levi

401

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ilevin parametresi ayrtrlm zaman bilgisinin tutan struct tm trnden nesnenin adresidir. levin parametresinde kullanlan adresin const olmadna dikkat edin. Buradan ilevin bu adrese yazma yapaca sonucunu karabilirsiniz. levin geri dn deeri takvim zaman deeridir. Eer ilgili sistemde takvim zaman deeri elde edilemiyorsa ilev -1 deerine geri dner. levin yle bir yan etkisi de vardr. Eer parametresine ald adresteki yap nesnesinin elemanlar olmas gereken deerleri ayorsa, mktime ilevi deerlerin fazla ksmn bir sonraki elemana ekler. Bu amala nce tm_sec isimli elemana baklr. Buradaki fazlalklar tm_min elemanna verilir. Burada da bir fazlalk oluur ise srasyla yap nesnesinin tm_hour, tm_mday, tm_mon ve tm_year elemanlar deitirilir. Bu elemanlar deeri alndktam sonra yap nesnesinin tm_wday ve tm_yday isimli elemanlar sahip olmas gereken deerlere getirilir. Aadaki kod, ilevin bu zelliini kullanarak bir tarihin ne gnne geldiini buluyor: #include <stdio.h> #include <time.h> int main() { struct tm t; const char *days[] = {"Pazar", "Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma","Cumartesi", "bilinmiyor"}; t.tm_mday = 11; t.tm_mon = 2; t.tm_year = 2005 - 1900; t.tm_sec = 1; t.tm_min = 0; t.tm_hour = 0; t.tm_isdst = -1; if (mktime(&t) == 1) t.tm_wday = 7; printf("gun = %s\n", days[t.tm_wday]); } return 0;

402

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki programda bir tarih bilgisini tutmak amacyla Date isimli bir yap bildiriliyor. Bu yap trn kullanarak hizmet veren ilevler tanmlanyor. /********** date.h **************************/ typedef struct { int m_d; int m_m; int m_y; }Date; #define #define #define YEAR_BASE false true 1000 0 1

Yaplarla lgili Uygulamalar

typedef int bool; void set_date(Date *ptr, int d, int m, int y); void set_today(Date *ptr); void set_random(Date *ptr); void display_date(const Date *ptr); void inc_date(Date *p); void dec_date(Date *p); Date ndays(const Date *p, int n); int date_cmp(const Date *p1, const Date *p2); int get_day(const Date *p); int get_month(const Date *p); int get_year(const Date *p); int get_yearday(const Date *ptr); int get_weekday(const Date *ptr); bool isweekend(const Date *ptr); bool isleap(int y); /********** date.c **************************/ #include <stdio.h> #include <stdlib.h> #include <time.h> #include <assert.h> #define #define #define PUBLIC PRIVATE FACTOR static 2

PRIVATE int daytabs[2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, }; PRIVATE bool check_date(int d, int m, int y); PRIVATE int totaldays(const Date *p); PRIVATE Date revdate(int totaldays); PRIVATE bool check_date(int d, int m, int y) { if (y < YEAR_BASE) return false; if (m < 1 || m > 12) return false;

403

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (d < 1 || d > daytabs[isleap(y)][m]) return false; return true; } /*************************************************************************/ PRIVATE int totaldays(const Date *ptr) { int sum = 0; int k; for (k = YEAR_BASE; k < ptr->m_y; ++k) sum += 365 + isleap(k); return sum + get_yearday(ptr); } /*************************************************************************/ PRIVATE Date revdate(int totaldays) { Date ret_val; int val; int index; ret_val.m_y = YEAR_BASE; while (totaldays > (val = isleap(ret_val.m_y) + 365)) { totaldays -= val; ret_val.m_y++; } ret_val.m_m = 1; index = isleap(ret_val.m_y); while (totaldays > daytabs[index][ret_val.m_m]) totaldays -= daytabs[index][ret_val.m_m++]; ret_val.m_d = totaldays; return ret_val; } /*************************************************************************/ PUBLIC int get_yearday(const Date *ptr) { int sum = ptr->m_d; int k; int index = isleap(ptr->m_y); for (k = 1; k < ptr->m_m; ++k) sum += daytabs[index][k]; return sum; } /*************************************************************************/ PUBLIC int dayofweek(const Date *ptr) { return totaldays(ptr) % 7; } /*************************************************************************/ PUBLIC bool isleap(int y) { return y % 4 == 0 && y % 100 != 0 || y % 400 == 0; } /*************************************************************************/ PUBLIC void set_date(Date *ptr, int d, int m, int y) { assert(check_date(d, m, y));

404

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} /*************************************************************************/ PUBLIC void set_today(Date *ptr) { time_t timer = time(NULL); struct tm *tptr = localtime(&timer); ptr->m_d = tptr->tm_mday; ptr->m_m = tptr->tm_mon + 1; ptr->m_y = tptr->tm_year + 1900; } /*************************************************************************/ PUBLIC void set_random(Date *ptr) { ptr->m_y = rand() % 50 + 1960; ptr->m_m = rand() % 12 + 1; ptr->m_d = rand() % daytabs[isleap(ptr->m_y)][ptr->m_m] + 1; } /*************************************************************************/ PUBLIC void display_date(const Date *ptr) { static const char *days[] = {"Pazar", "Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi"}; static const char *mons[] = {"", "Ocak", "Subat", "Mart", "Nisan", "Mayis", "Haziran", "Temmuz", "Agustos", "Eylul","Ekim", "Kasim", "Aralik"}; printf("%02d %s %4d %s\n", ptr->m_d, mons[ptr->m_m], ptr->m_y, days[get_weekday(ptr)]); } /*************************************************************************/ PUBLIC int date_cmp(const Date *p1, const Date *p2) { if (p1->m_y != p2->m_y) return p1->m_y - p2->m_y; if (p1->m_m != p2->m_m) return p1->m_m - p2->m_m; return p1->m_d - p2->m_d; } /*************************************************************************/ PUBLIC void inc_date(Date *p) { *p = revdate(totaldays(p) + 1); } /*************************************************************************/ PUBLIC void dec_date(Date *p) { *p = revdate(totaldays(p) - 1); } /*************************************************************************/ PUBLIC Date ndays(const Date *p, int n) { return revdate(totaldays(p) + n); } /*************************************************************************/ PUBLIC int get_weekday(const Date *ptr) { return (totaldays(ptr) + FACTOR) % 7; }

ptr->m_d = d; ptr->m_m = m; ptr->m_y = y;

405

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

/*************************************************************************/ PUBLIC bool isweekend(const Date *ptr) { int day = get_weekday(ptr); return day == 6 || day == 0; } /*************************************************************************/ int main() { Date today; Date ndaysafter; set_today(&today); ndaysafter = ndays(&today, 10); while (date_cmp(&today, &ndaysafter)) { display_date(&today); inc_date(&today); } } return 0;

406

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki programda bir tekli bal liste oluturuluyor. /********** datelist.h **************************/ #include "date.h" typedef struct tagNode { Date date; struct tagNode *pnext; }Node; typedef struct { Node *pstart; Node *pend; size_t size; }*ListHandle; ListHandle openlist(void); void closelist(ListHandle); void push_front(ListHandle handle); void push_back(ListHandle handle); void display_list(ListHandle handle); void pop_front(ListHandle handle); void pop_back(ListHandle handle); void remove_date(const Date *); void clear_list(ListHandle handle); size_t get_size(ListHandle handle); /********** datelist.c **************************/ PRIVATE Node *create_node(void); PRIVATE void free_nodes(Node *p); /*************************************************************************/ PRIVATE void free_nodes(Node *p) { Node *temp; while (p) { temp = p; p = p->pnext; free(temp); } } /*************************************************************************/ PRIVATE Node *create_node(void) { Node *pd = (Node *)malloc(sizeof(Node)); if (!pd) { printf("cannot allocate memory!\n"); exit(EXIT_FAILURE); } return pd; } /*************************************************************************/ PUBLIC ListHandle openlist(void) { ListHandle pd = (ListHandle)malloc(sizeof(*pd)); if (!pd) { printf("cannot allocate memory!\n"); exit(EXIT_FAILURE); } pd->pstart = pd->pend = NULL; pd->size = 0;

407

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

return pd; } /*************************************************************************/ PUBLIC void closelist(ListHandle handle) { clear_list(handle); free(handle); } /*************************************************************************/ PUBLIC void push_front(ListHandle handle) { Node *pnew = create_node(); set_random(&pnew->date); handle->size++; if (handle->pstart == NULL) { handle->pstart = handle->pend = pnew; pnew->pnext = NULL; return; } pnew->pnext = handle->pstart; handle->pstart = pnew; } /*************************************************************************/ PUBLIC void push_back(ListHandle handle) { Node *pnew = create_node(); set_random(&pnew->date); pnew->pnext = NULL; handle->size++; if (handle->pstart == NULL) { handle->pstart = handle->pend = pnew; return; } handle->pend->pnext = pnew; handle->pend = pnew; } /*************************************************************************/ PUBLIC void display_list(ListHandle handle) { Node *cur = handle->pstart; if (!handle->size) { printf("empty list!\n"); return; } while (cur) { display_date(&cur->date); cur = cur->pnext; }

} /*************************************************************************/ PUBLIC void clear_list(ListHandle handle) { free_nodes(handle->pstart); handle->pstart = handle->pend = NULL; } /*************************************************************************/ PUBLIC size_t get_size(ListHandle handle) { return handle->size; }

408

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

/*************************************************************************/ PUBLIC void pop_front(ListHandle handle) { Node *temp; if (!handle->size) { printf("liste bos!\n"); return; } handle->size--; if (handle->pstart == handle->pend) { free(handle->pstart); handle->pstart = handle->pend = NULL; return; } temp = handle->pstart; handle->pstart = handle->pstart->pnext; free(temp);

} /*************************************************************************/ PUBLIC void pop_back(ListHandle handle) { Node *temp, *cur; if (!handle->size) { printf("liste bos!\n"); return; } handle->size--; if (handle->pstart == handle->pend) { free(handle->pstart); handle->pstart = handle->pend = NULL; return; } temp = handle->pend; for (cur = handle->pstart; cur->pnext != handle->pend; cur = cur>pnext) ; cur->pnext = NULL; handle->pend = cur; free(temp); } /*************************************************************************/ void display_menu() { printf("[1] PUSH FRONT\n"); printf("[2] PUSH BACK\n"); printf("[3] DISPLAY LIST\n"); printf("[4] POP FRONT\n"); printf("[5] POP BACK\n"); printf("[6] EMPTY LIST\n"); printf("[7] EXIT\n"); printf("seciminiz : "); } /*************************************************************************/ int get_option() { int option;

409

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} /*************************************************************************/ int main() { int option; ListHandle handle = openlist(); for (;;) { option = get_option(); switch (option) { case 1: push_front(handle); break; case 2: push_back(handle); break; case 3: display_list(handle);break; case 4: pop_front(handle);break; case 5: pop_back(handle);break; case 6: clear_list(handle); break; case 7: goto END; case 8: printf("invalid entry!\n"); } } END: closelist(handle); printf("end of program!\n"); } return 0;

display_menu(); scanf("%d", &option); if (option < 1 || option > 7) option = 0; return option;

410

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BRLKLER
Programc tarafndan yeni bir tr yaratmasna olanak veren bir baka ara da "birlikler"dir (unions). Birlikler yaplara ok benzer. Bir birliin kullanlabilmesi iin, yani bir birlik trnden nesnelerin tanmlanabilmesi iin nce birliin bildirimi yaplmaldr. Birlik bildirimleri ayn yap bildirimleri gibi yaplr. Tek fark struct anahtar szc yerine union anahtar szcnn kullanlmasdr. Aadaki rnei inceleyin: union Dword { unsigned char byte; unsigned int word; unsigned long dword; }; Yukardaki deyimle, ismi union Dword olan yeni bir tr bildirilmi olur. Bu bildirimin grlr olduu yerlerde, bu tr kullanlabilir. Bir typedef bildirimi yaplarak, bu trn isminin, yalnzca Dword olmas da salanabilir: typedef union { unsigned char byte; unsigned int word; unsigned long dword; }Dword;

Birlik Trnden Deikenlerinin Tanmlanmas


Birlik deikenleri ayn yap deikenleri gibi tanmlanr. Birliklerde de, bellekte yer ayrma ilemi yap bildirimi ile deil, yap nesnesinin tanmlanmas ile yaplr. Dword a, b; deyiminden sonra, a ve b, Dword trnden iki deikendir. Yine yaplarda olduu gibi, birliklerde de bildirim ile tanmlama ilemi birlikte yaplabilir: union Double { double d; unsigned char s[8]; } a, b, c; Bu durumda a, b ve c deikenlerinin bilinirlik alanlar, birlik bildiriminin yapld yere bal olarak, yerel ya da global olabilir. Birlik elemanlarna da yap elemanlarnda olduu gibi nokta ileciyle eriilir. rnein yukardaki tanmlamadan sonra a.d birliin double trnden ilk elemann belirtir. Benzer biimde birlik trnden nesneleri gsterecek, gsterici deikenler de tanmlanabilir. Ok ileci ile yine yaplarda olduu gibi birliklerde de, adres yoluyla birlik elemanlarna ulalabilir: union Dword *p; p->word = 100; p->word ifadesi ile, p adresindeki birlik nesnesinin word isimli elemanna eriilir.

411

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Birlik nesneleri iin birliin en uzun eleman kadar yer ayrlr. Birlik elemanlarnn hepsi ayn saysal adresten balayacak ekilde bellee yerleir. rnein: union Dword { unsigned char byte; unsigned short word; unsigned long dword; }; union Dword a; bildirimi ile, Dword trden a deikeni iin bellekte Unix, Windows sistemlerinde 4 byte yer ayrlr. nk a deikeninin dword isimli eleman, 4 byte ile birliin en uzun elemandr.

Birlik Nesnelerinin Bellekteki Yerleimi

1F00 1F01 1F02 1F03

a.byte

a a a

a.word a.dword

Birlik bir dizi ieriyorsa dizi tek bir eleman olarak alnr. rnein: typedef union { float f; unsigned char bytes[4]; }Float; Float x; tanm ile x deikeni iin ne kadar yer ayrlr? Float birliinin iki eleman vardr. Birincisi 4 byte uzunluunda float trden bir deiken, ikincisi de 4 byte uzunluunda bir karakter dizisidir. ki uzunluk da ayn olduuna gre x nesnesi iin 4 byte yer ayrlaca sylenebilir.

Buna gre Float x; x.f = 10.2F; ile x.bytes[0], x.bytes[1], x.bytes[2], x.bytes[3] srasyla float trden elemann byte deerleridir. Aadaki program inceleyin:

412

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> typedef union { char ch; int i; double d; char s[4]; }Data;

int main() { Data data; printf("&data printf("&data.ch printf("&data.i printf("&data.d printf("data.s return 0; } Yukardaki programn derlenip altrlmasyla ekrana hep ayn adres yazdrlr. Birlik elemanlarnn ayn orjinden, yani ayn saysal adresten balayarak yerletirilmesi bir elemann deeri deitirildiinde, dier elemanlarn da deerlerinin deiecei anlamna gelir. Zaten birliklerin kullanlmasnn asl amac da budur. = = = = = %p\n", %p\n", %p\n", %p\n", %p\n", &data); &data.ch); &data.i); &data.d); data.s);

C standartlarna gre, birlik nesnelerinin yalnzca ilk elemanna ilkdeer verilebilir. Bir birlik nesnesinin birden fazla elemanna ilkdeer vermek geersizdir: union Dword { unsigned char byte; unsigned int word; unsigned long dword; } x = {'m'}; union Dword y = {'a', 18, 24L}; /* Geersiz */

Birlik Nesnelerine lkdeer Verilmesi

Birlikler Neden Kullanlr


Birlikler balca ama iin kullanlabilir. Birinci ama bellekte yer kazanmaya yneliktir. Birlik kullanarak farkl zamanlarda kullanlacak birden fazla deiken iin ayr ayr yer ayrma zorunluluu ortadan kaldrlr. rnein bir hediyelik eya katalou ile deiik rnn satldn dnelim: kitap, tshort ve saat. Her bir rn iin bir stok numaras, fiyat bilgisi ve rn tip bilgisinin dnda rne bal olarak baka zelliklerin de tutulmak zorunda olduunu dnelim: kitaplar : isim, yazar, sayfa says. t-short : desen, renk, boyut saat : model

413

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

typedef struct { int stok_no; float fiyat; int urun_tipi; char kitapisim[20]; char yazar[20]; int sayfa_sayisi; char desen[20]; int renk; int boyut; char saatisim[20]; int model; }Katalog; Yukardaki bildirimde Katalog yapsnn urun_tipi isimli eleman rnn ne olduu bilgisini tutar. Bu elemann deeri yalnzca KITAP, TSHORT ya da SAAT olabilir. Bunlarn simgesel deimezler olarak tanmlandn dnelim. Yukarda bildirilen yap rnlerin btn zelliklerini tutabilir ancak yle bir sakncas vardr: Eer rn tipi KITAP deil ise isim[20], yazar[30] ve sayfa_sayisi isimli elemanlar hi kullanlmaz. Yine rn tipi TSHORT deil ise desen[20], renk, boyut elemanlar hi kullanlmaz. Unix ya da Windows sistemlerinde byte hizalama altnda yukarda bildirilen Katalog trnn sizeof deeri 108'dir. Yani Katalog trnden bir nesne tanmlandnda bu nesne bellekte 108 byte yer kaplar. #include <stdio.h> int main() { Katalog katalog; printf("sizeof(katalog) = %d\n", sizeof(katalog)); return 0;

Ama Katalog yapsnn bir elemannn bir birlik trnden olmasyla yer kazanc salanabilir:

414

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

typedef struct { char isim[20]; char yazar[20]; int sayfa_sayisi; }Kitap; typedef struct { char desen[20]; int renk; int size; }Tshirt; typedef struct { char isim[20]; int model; }Saat; typedef union { Kitap kitap; Tshirt tshirt; Saat saat; }Data; typedef struct { int stok_no; float fiyat; int urun_tipi; Data data; }Katalog; Yukardaki bildirimleri inceleyin. nce kitap, tshirt ve saate ilikin bilgileri tutmak amacyla Kitap, Tshirt ve Saat isimli yaplar bildirilmi. Unix, Windows sistemlerinde Kitap, Tshirt ve Saat yaplarnn sizeof deerleri srasyla 44, 28 ve 24'dr. Daha sonra Data isimli bir birliin bildirildiini gryorsunuz. Bu birliin elemanlar Kitap, Tshirt ve Saat trlerinden olduuna gre bu birliin sizeof deeri en uzun elemannn yani Kitap yaps trnden olan elemannn sizeof u kadardr, yani 44 dr. Katalog isimli yapnn bildiriminde ise Data birlii trnden data isimli bir eleman yer alyor. Bu durumda Katalog yapsnn sizeof deeri yalnzca 56 olur. Birliin kullanlmasyla Katalog yapsnn sizeof deeri 108 den 56'ya drlyor. #include <stdio.h> int main() { printf("sizeof(Katalog) = %u\n", sizeof(Katalog)); return 0; } Katalog yapsyla ilgili ilem yapan kod paralar, Katalog yaps trnden nesnenin nce urun_tipi isimli elemannn deerine bakarak, rnn cinsi bilgisini elde ettikten sonra, duruma gre, data isimli elemanlar farkl biimde kullanabilir: Katalog katalog; /***/ if (katalog.urun_tipi == KITAP) puts(katalog.data.kitap.isim)

415

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Birlik kulanmnn ikinci nedeni herhangi bir veri trnn paralar zerinde ilem yapmak ya da paralar zerinde ilem yaparak bir btn oluturmaktr. Aadaki rnei inceleyin: int trnn 2 byte olduu bir sistemde aadaki bildirimlerin yapldn dnelim: typedef struct { unsigned char low_byte; unsigned char high_byte; }Word; typedef union { unsigned int i; Word w; }Wordform; bildirimlerinden sonra, Wordform trnden bir nesne tanmlanrsa bu birlik nesnesi, alak (low_byte) ve yksek (high_byte) anlaml byte'larna ayr ayr eriilebilen bir tamsay olarak kullanlabilir: #include <stdio.h> int main() { Wordform wf; wf.w.low_byte = 0x12; wf.w.high_byte = 0x34; printf("%x\n", wf.i); } return 0;

Yani Intel ilemcilerinin bulunduu 16 bit sistemlerde yukardaki main ilevinin derlenip altrlmasyla: printf("%x\n", wf.i); ars ile 3412 says ekrana yazdrr. Motorola ilemcilerinde (big endian) dk saysal adreste dk anlaml dk anlaml byte deeri olacana gre saynn ters olarak grlmesi gerekir. Benzer bir tema, ilemcilerin yazmalarnn yazlmsal olarak temsil edilmesinde karmza kar. 8086 ilemcilerinde toplam 14 yazma vardr. - 4 tane genel amal yazma: AX, BX, CX, DX - 4 tane segment yazmac: CS, DS, CS, ES - 2 tane indeks yazmac: SI, DI - 3 tane gsterici yazmac: IP, BP, SP - 1 tane bayrak yazmac: F Btn yazmalar 16 bit uzunluundadr. Genel amal yazmalar olan AX, BX, CX, DX yazmalar 8'er bitlik iki paraya ayrlr. Genel amal yazmalar aadaki gibi bamsz 8 bitlik yazmalar gibi de kullanlabilir. AX AH BX

AL

416

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BH CX CH DX DH

BL CL DL

Bu yazmalar ayn zamanda btnn paralardr, yani AH ve AL yazmalarna ayr ayr deerler yazp AX yazmac btnsel bir deer olarak ekilebilir. Yazmalar bellekte bir birlik (union) ile temsil edilebilirler. struct BYTEREGS { unsigned char al, ah, bl, bh, cl, ch, dl, dh; }; struct WORDREGS { unsigned ax, bx, cx, dx, si, di, flags, cflag; }; union REGS { struct BYTEREGS h; struct WORDREGS x; }; <-<-<-<-<-<-<-<-regs.h.al regs.h.ah regs.h.bl regs.h.bh regs.h.cl regs.h.ch regs.h.dl regs.h.dh

regs.x.cx regs.x.bx regs.x.cx regs.x.dx regs.x.si regs.x.di regs.x.flags regs.x.cflag

Birliklerin Kark Veri Yaplarnda Kullanlmas


Birlikler kullanlarak, elemanlar farkl trden olan diziler oluturulabilir: Bir dizinin elemanlar char, int, double veya programc tarafndan yaratlan bir tr olan Complex trnden olabilsin. Dizinin herhangi bir eleman bu drt trden herhangi birinden olabilecek ekilde kullanlabilsin. nce Complex isimli bir tr yaratalm:

417

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> typedef struct _Complex { double m_r, m_i; }Complex; void set_cr(Complex *p) { p->m_r = (rand() % 2 ? 1:-1) * ((double)rand() / RAND_MAX + rand()% 9); p->m_i = (rand() % 2 ? 1:-1) * ((double)rand() / RAND_MAX + rand()% 9); } void display_c(const Complex *p) { printf("(%.2lf %+.2lfi)", p->m_r, p->m_i); } Yukarda tanmlanan set_cr ilevi adresini ald bir Complex nesnesini rastgele bir deerle set ediyor. display_c isimli ilev ise adresini ald bir Complex nesnesinin deerini ekrana yazdryor. nce okunabilirlii artrmak iin baz simgesel deimezler tanmlayalm: #define #define #define #define CHAR INT DOUBLE COMPLEX 0 1 2 3

imdi ismi Data olan bir birlik tr yaratalm. typedef union{ char ch; int i; double d; Complex c; }Data; Data trnn sizeof deeri en uzun eleman olan Complex trden c isimli elemann sizeof deeri kadar olur deil mi? Unix ya da Windows sistemlerinde Data trnn sizeof deeri 16 byte olur. imdi de ismi Element olan bir yap tr yaratalm: typedef struct { char type; Data data; }Element; Element trnn sizeof deeri elemanlarnn sizeof deerlerinin toplam kadar olduuna gre (byte alignment'n etkin olduu varsaylyor) UNIX ve Windows sistemlerinde Element trnden bir nesne bellekte 17 byte yer kaplar. Oluturulacak dizinin elemanlar Element trnden olur. Element trnn eleman olan type Element trden bir nesnenin eleman olan data'nn hangi elemannn kullanld bilgisini tutar. imdi Element trden bir nesneyi rastgele bir deerle set edecek bir ilev tanmlayalm:

418

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void set_elem_random(Element *ptr) { switch (rand() % 4) { case CHAR : ptr->type = CHAR; ptr->data.ch = rand() % 26 + 'A'; break; case INT : ptr->type = INT; ptr->data.i = rand(); break; case DOUBLE : ptr->type = DOUBLE; ptr->data.d = (double)rand() / RAND_MAX + rand() % 10; break; case COMPLEX: ptr->type = COMPLEX; set_cr(&ptr->data.c); } } imdi de Element trnden bir nesnenin deerini ekrana yazdran bir ilev tanmlayalm: void display_elem(const Element *ptr) { switch (ptr->type) { case CHAR : printf("(%c)", ptr->data.ch); break; case INT : printf("(%d)", ptr->data.i); break; case DOUBLE : printf("(%.2lf)", ptr->data.d); break; case COMPLEX : display_c(&ptr->data.c); } } imdi de Element trnden bir diziyi rastgele deerlerle set edecek ve dizinin elemanlarn ekrana yazdracak ilevler yazalm: void display_array(const Element *ptr, int size) { int k; for (k = 0; k < size; ++k) { if (k && k % 5 == 0) printf("\n"); display_elem(ptr + k); }

void set_array(Element *ptr, int size) { int k; for (k = 0; k < size; ++k) set_elem_random(ptr + k);

imdi aadaki main ilevini inceleyin:

419

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#define

ARRAY_SIZE

50

int main() { Element a[ARRAY_SIZE]; srand(time(0)); set_array(a, ARRAY_SIZE); display_array(a, ARRAY_SIZE); return 0; } main ilevi iinde Element trnden a isimli bir dizi tanmlanyor. set_array(a, ARRAY_SIZE); arsyla a dizisinin elemanlarna rastgele deerler atanyor. Tabi bu durumda dizinin elemanlar rastgele bir biimde char, int, double ya da Complex trlerinden seiliyor. display_array(a, ARRAY_SIZE); arsyla dizinin elemanlarnn deerleri ekrana yazdrlyor. Aada programn altrlmasyla elde edilen rnek bir ekran kts veriliyor: (1.07)(4.49)(19755)(1.69 -8.82i)(0.91) (4.45)(9.35)(1.41)(V)(5.57 +5.51i) (K)(T)(11331)(7.78)(22316) (26066)(28923)(2.36 -2.31i)(C)(8.39) (9951)(8.79)(28301)(7.50)(18583) (20564)(7.02)(-0.23 -5.46i)(28931)(-4.76 +2.32i) (5850)(14891)(J)(-5.40 +8.65i)(H) (7.50 -3.32i)(-3.81 +3.17i)(D)(-5.38 -6.72i)(2.15 +2.47i) (-4.04 +2.10i)(4737)(-0.21 -0.61i)(-9.00 -3.72i)(4276) (12552)(-6.95 -0.11i)(E)(0.71)(-4.72 +7.21i)

420

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

NUMARALANDIRMALAR
Yazlan birok programda, yalnzca belirli anlaml deerler alabilen deikenler kullanma gereksinimi duyulur. rnein bir "Boolean" deikeni, yalnzca iki deere sahip olabilir: "Doru" ve "Yanl". Haftann bir gnn tutacak bir deiken, haftann yedi gn olduuna gre yedi farkl deerden birini alabilir. Baka bir rnek olarak bir oyun kadnn rengini tutacak bir deikeni verilebilir. Byle bir deiken yalnzca 4 deiik deer alabilir: Sinek, Karo, Kupa, Maa. Byle deikenler tamsay trlerinden tanmlanabilir: int renk; renk = 1; Yukardaki rnekte renk isimli deiken bir oyun kadnn renk bilgisini tutuyor. Bu deikene 1 deerinin atanmas renk deerinin Karo olduunu gsteriyor. Byle bir teknik uygulamalarda pekala kullanlabilir. Ancak bu tekniin baz sakncalar vardr. 1. Kodu okuyan renk isimli deikene yaplan atamalarda kullanlan tamsay deimezlerinin neyi temsil ettii konusunda dorudan bilgi alamaz. Kodun okunabilirlii azalr. rnein renk = 1; eklinde bir atama dorudan renk deikenine "karo" deerinin verilmi olduu biiminde de anlamlandramaz. 2. Derleyici renk deikeni ile ek bir kontrol yapamaz. Derleyici renk deikeninin yalnzca 4 farkl deerden birini almas gerektiinin farknda deildir. Derleyiciye byle bir bilgi verilmemitir. rnein byle bir deikene 10 gibi bir deerin atanmas anlamszdr. Ancak derleyici derleme srasnda bu yanll bulamaz, bir hata ya da bir uyar iletisiyle veremez. C dili, isimlendirilmi belirli tamsay deerlerini alabilen, bir tr yaratlmasna olanak veren bir araca sahiptir. Bu araca numaralandrrma (enumaration) ve bu arala ilikili kullanlan deimezlere (enumaration constants) "numaralandrma deimezleri" denir. Bir numaralandrma trnn ve bir numaralandrma trne ilikin deimezlerin bildirimi aadaki gibi yaplr: enum [enum trnn isimi] {deimez1,deimez2, ......}; enum bir anahtar szcktr. Derleyici kme ayralar arasnda isimlendirilmi deimezleri 0 deerinden balayarak artan tamsay deerleriyle eler. rnek : enum Renk {Sinek, Karo, Kupa, Maca}; Yukardaki bildirimle Sinek deimezi 0, Karo deimezi 1, Kupa deimezi 2, Maca deimezi ise 3 deerini alr. enum Bool {TRUE, FALSE}; burada TRUE deimezi 0 FALSE deimezi ise 1 deerini alr. enum Months {January, February, March, April, May, June, July, August, September, Oktober, November, December};

421

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu bildirim ile numaralandrma deimezlerine January = 0, February = 1, ... December = 11 biiminde artan deerler verilir. Numaralandrma deimezlerine atama ileci ile kme ayralar iinde deerler verilebilir, bu biimde deer verilmi bir numaralandrma deimezinden sonraki deimezin deeri nceki deerden bir fazlasdr. enum Months {January = 1, February, March, April, May, June, July, August,September, Oktober, November, December}; Bu bildirim ile numaralandrma deimezlerine January = 1, February = 2, ... December = 12 biiminde artan deerler verilmi olur. enum deimezlerine int tr say snrlar iinde pozitif ya da negatif deerler verilebilir: enum RTVals { RT1 = -127, RT2, RT3, RT4 = 12, RT5, RT6, RT7 = 0, RT8, RT9 = 90 }; Yukardaki bildirim ile numaralandrma deimezlerinin alaca deerler aadaki gibi olur: RT1 = -127, RT2 = -126, RT3 = -125, RT4 = 12, RT5 = 13, RT8 = 1, RT9 = 90 RT6 = 14, RT7 = 0,

Bir numaralandrma bildiriminde kullanlan farkl numaralandrma deimezlerinin deeri ayn olabilir. Aadaki bildirim geerlidir: enum Bool {YANLIS, FALSE = 0, DOGRU, TRUE = 1}; Bir numaralandrma deimezinin ismi, ayn bilinirlik alannda kullanlan tm isimlerden farkl olmaldr. Bir numaralndrma deimezi ismi ile ayn bilinirlik alannda bildirilen bir deikenin isminin ya da bir baka numaralandrma deimezinin isminin ayn olmas geersizdir. Numaralandrma isimleri (enum tag), yap ve birlik isimleri gibi bilinirlik alan kurallarna uyar. Bu isimler ayn bilinirlik alanndaki dier numaralandrma, yap ve birlik isimlerinden farkl olmaldr. Bildirimlerde numaralandrma ismi (enumeration tag) hi belirtilmeyebilir. Bu durumda genellikle kme ayracnn kapanmasndan sonra o numaralandrma trnden nesne(ler) tanmlanarak deyim sonlandrc atom ile bitirilir. enum {Sunday = 1, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}day1, day2, day3; Yukardaki rnekte day1, day2, day3 bildirimi yaplm numaralandrma trnden (belirli bir tr ismi seilmemitir) yaratlm deikenlerdir. Byle bir bildirim + tanmlama ileminin dezavantaj, artk bir daha ayn numaralandrma trnden baka bir deikenin yaratlmamasdr.

422

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bildirilen bir numaralandrma trnden nesne tanmlamak da zorunlu deildir: enum {LOW = 10, HIGH = 20}; Yukardaki bildirim geerlidir. Bildirilen trn bir ismi yoktur ama bu bildirimin grlr olduu her yerde LOW ve HIGH numaralandrma deimezleri kullanlabilir.

Bir numaralandrma trnden tanmlanm bir nesne iin derleyici, kullanlan sistemde int trnn uzunluu kadar bir yer ayrr. Derleyici iin numaralandrma trnden bir nesne ile int trden bir nesne arasnda fiziksel olarak herhangi bir fark bulunmaz. rnein Unix altnda : sizeof(enum Days) == sizeod(int) == ifadesi dorudur. Tabi bu kuraln bir kt taraf int trnn 2 byte olduu bir sistemde kullanlabilecek en byk numaralandrma deimezinin deeri 32767 olabilir. 4

Numaralandrma Trlerinin sizeof Deerleri

Numaralandrmalar ve typedef Bildirimi

Bir numaralandrma tr iin de bir typedef bildirimi yaplabilir. Bylece trn kullanmnda artk enum anahtar szcnn kullanlmasna gerek kalmaz: typedef enum tagRenk{SINEK, KARO, KUPA, MACA} Renk; Bildiriminden sonra artk enum anahtar szc kullanlmadan Renk kart1, kart2, kart3, kart4; gibi bir tanmlama yaplabilir. typedef bildiriminden sonra artk Renk bir tr ismi belirtir. imdi de aadaki rnee bakalm: typedef enum {FALSE, TRUE} Bool; Artk Bool ismi FALSE ve TRUE deerlerini alabilen bir trn ismidir. [C++ dilinde yap, birlik ve numaralandrma trlerine ilikin isimler (tag) ayn zamanda trn genel ismi olarak kullanlabilecei iin yukardaki gibi bir typedef bildirimine gerek yoktur.]

enum bildirimi ile yeni bir tr yaratldna gre, bu trden nesne ya da gsterici tanmlanabilir. Tanmlanacak ilevlerin geri dn deerleri ve/veya parametre deikenleri de byle bir trden olabilir: typedef enum {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}Days; typedef struct {int d, m, y}Date; Bir yap trnn bildiriminde, elemanlar bir numaralandrma trnden seilebilir: typedef struct { Days day; Months month; int year; }Date;

Numaralandrma Trlerinin Kullanm

423

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki rnekte Date isimli yap trnn day ve month isimli elemanlar Days ve Months isimli numaralandrma trlerindendir.

Numaralandrma Deimezleri Deimez fadesi Olarak Kullanlabilir

Bir numaralandrma deimezi bir deimez ifadesi (constant expression) oluturur. Bu yzden numaralandrma deimezleri deimez ifadesi gereken yerlerde kullanlabilir. Aadaki kodu inceleyin: enum {MIN_SIZE = 100, AVERAGE_SIZE = 500, MAX_SIZE = 1000}; void func() { int a[MIN_SIZE]; int b[AVERAGE_SIZE]; int c[MAX_SIZE]; /***/ } Yukardaki main ilevinin iinde tanmlanan a, b ve c isimlerinin boyutlar numalandrma deimezleri ile belirlenmitir. Numaralandrma deimezlerin switch deyiminin case ifadeleri olarak kullanlmalar ok sk karlalan bir durumdur: Renk renk; /***/ switch (renk) { case SINEK: /***/ case KARO : /***/ case KUPA : /***/ case MACA : /***/ }

#define nilemci komutu nilemci programa ilikindir. Fakat numaralandrma deimezleri derleme aamasnda ele alnr. Bir #define simgesel deimezi iin bilinirlik alan diye bir kavram sz konusu deildir, nk bilinirlik alan derleyicinin anlamlandrd bir kavramdr. Ancak numaralandrma trleri ve deimezleri iin bilinirlik alan kurallar geerlidir. Eer bir numaralandrma tr, bir ilev iinde bildirilmise bu tre ilikin numaralandrma deimezleri sz konusu ilevin dnda bilinmez. Birden fazla #define simgesel deimezi, mantksal bir iliki iinde kullanlsa bile, derleyici bu ilikinin farknda olmaz. Bu yzden, derleyici programn, bu simgesel deimezlerin yanl kullanmna ilikin bir uyar iletisi retme ansna sahip olmaz. Ancak derleyici ayn tr kapsamnda bildirilen numaralandrma deimezlerinin arasndaki mantksal ilikinin farkndadr. Bunlarn yanl kullanlmas durumunda uyar iletisi retebilir. Numaralandrma deimezleri de #define komutuyla tanmlanm simgesel deimezler gibi, nesne belirtmez. rnein : enum METAL {Demir, Bakir, Kalay, Cinko, Kursun}; /***/ Kalay = 3; /* Geersiz */

Numaralandrma Trleri ile #define Deimezlerinin Karlatrlmas

424

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Numaralandrma deimezleri, mantksal iliki iinde bulunan belirli sayda tanmlamalar iin tercih edilir. Mantksal iliki iindeki baz isimlerin bir tr kapsam altnda ele alnmas okunabilirlii artrr. rnein: enum Gun {Pazartesi, Sali, Carsamba, Persembe, Cuma, Cumartesi, Pazar}; Derleyicilerin sistemlere ynelik salad balk dosyalarnda, birok numaralandrma tr ve bu trlere ilikin numaralandrma deimezleri tanmlanr. rnein aadaki numaralandrma bildirimi 80x86 sistemlerinde alan bir Borland derleyicisinin graphics.h isimli balk dosyasndan alnmtr: enum COLORS { BLACK, BLUE, GREEN, CYAN, RED, MAGENTA, BROWN, LIGHTGRAY, DARKGRAY, LIGHTBLUE, LIGHTGREEN, LIGHTCYAN, LIGHTRED, LIGHTMAGENTA, YELLOW, WHITE };

C dilinde bir numaralandrma tr derleyici tarafndan fiziksel olarak int tr biiminde ele alnr. Numaralandrma trleri ile dier doal trler arasnda otomatik tr dnm sz konusudur. rnein bir numaralandrma deikenine int tden bir deer atanabilir. Ya da int trden bir deikene bir numaralandrma deimezi atanabilir: typedef enum {OFF, HOLD, STANDBY, ON}Position; void func() { Position pos = 2; int x = ON; /***/ }

Numaralandrma Trlerine likin Tr Dnmleri

/* Geerli */ /* Geerli */

Ancak numaralandrma deikenlerine dier doal trlerden atama yapldnda tr dntrme ilecinin kullanlmas programn okunabilirliini artrr: pos = (Position)2;

Bir numaralandrma deeri ekrana standart printf ileviyle %d format karakteriyle elenerek yazdrlabilir. Numaralandrma deimezinin tamsay deerini deil de ismini ekrana yazdrmann dorudan bir yolu yoktur. Bu amala bir ilev yazlabilir:

Numaralandrma Deimezlerinin Ekrana Yazdrlmas

425

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> typedef enum {Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday}Days; void print_day(Days day) { static const char *days[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"}; printf("%s ", days[day]); } int main() { Days day; int k; for (k = 0; k < 10; ++k) { day = (Days)(rand() % 7); print_day(day); } } return 0;

426

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BTSEL LELER
Bitsel ileler (bitwise operators), bir tamsaynn bitleri zerinde ilemler yapar. Daha ok sistem programlarnda ya da dk seviyeli kodlarda kullanlrlar. Bitsel ilelerin ortak zellikleri, ileme soktuklar tamsaylar bir btn olarak deil, bit bit ele almalardr. Bu ilelerin terimleri tamsay trlerinden olmak zorundadr. Terimlerinin gerek say trlerinden olmas geersizdir. C dilinde toplam 11 bitsel ile vardr. Aadaki tabloda bitsel ileler kendi aralarndaki ncelik srasna gre listeleniyor: ncelik seviyesi 2 5 8 9 10 14 simge ~ << >> & ^ | <<= >>= &= ^= |= ile Bitsel deil (bitwise not) Bitsel sola kaydrma (bitwise left shift) Bitsel saa kaydrma (bitwise right shift) Bitsel ve (bitwise and) Bitsel zel veya (bitwise exor) Bitsel veya (bitwise or) Bitsel ilemli atama ileleri (bitwise compound assignment operators)

Yukardaki ileler iinde, yalnzca "Bitsel deil" (bitwise not) ileci, tek terimli nek konumunda (unary prefix) bir iletir. Dierleri iki terimli (binary) araek konumunda (infix) bulunan ilelerdir.

Bitsel Deil leci


Bitsel deil ileci (bitwise not), dier tm tek terimli ileler gibi ile ncelik tablosunun ikinci seviyesindedir. Bu ile, terimi olan tamsaynn bitleri zerinde 1'e tmleme ilemi yaparak bir deer elde eder. Yani terimi olan tamsay deerinin 1 olan bitlerini 0, 0 olan bitlerini 1 yaparak deer retir. lecin yan etkisi (side effect) yoktur. lecin terimi bir nesne ise, bu nesnenin deeri deitirilmez. Aada program inceleyin: #include <stdio.h> int main() { unsigned int x = 0x1AC3; unsigned int y; y = ~x; printf("y = %X\n", y); return 0;

Yukardaki programn, int trnn 4 byte olduu bir sistemde derlendiini dnelim. x deikenine atanan 0x1AC3 deeri, ikilik say sisteminde aadaki biimde ifade edilir: 0000 0000 0000 0000 0001 1010 1100 0011 427

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu durumda y deikenine atanan deer 1111 1111 1111 1111 1110 0101 0011 1100 olur. printf ilevi ekrana y = FFFFE53C yazar. imdi de aadaki program derleyerek altrn: #include <stdio.h> int main() { int x = 0; int y = ~x; printf("y = %d\n", y); return 0; } aretli ikilik say sisteminde, 0'n bire tmleyeni -1 deeridir, deil mi?

Bitsel Kaydrma leleri

ki tane bitsel kaydrma ileci (bitwise shift operator) vardr:

Bitsel saa kaydrma ileci >> (bitwise right shift) Bitsel sola kaydrma ileci << (bitwise left shift) Her iki ile de, ncelik tablosunun 5. seviyesinde bulunur. Dolaysyla bu ilelerin ncelii tm aritmetik ilelerden daha dk, fakat karlatrma ilelerinden daha yksektir. Bitsel kaydrma ileleri iki terimli araek konumundaki (binary infix) ilelerdir. Bitsel sola kaydrma ileci, soldaki terimi olan tamsaynn, sadaki terimi olan tamsay kadar sola kaydrlmasndan elde edilen deeri retir. Snr 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; unsigned int z; z = x << y; printf("z = %u\n", z); } return 0;

/* x = 0000 0000 0011 0100 */

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

*/

Bir tamsayy, sola bitsel olarak 1 kaydrmakla o tamsaynn ikiyle arplm deeri elde edilir. Bitsel saa kaydrma ileci, soldaki terimi olan tamsaynn, sadaki terimi olan tamsay kadar saa kaydrlmasndan elde edilen deeri retir. Sol terim iaretsiz (unsigned) bir tamsay trnden ise, ya da iaretli (signed) bir tamsay trnden ancak pozitif deere sahip ise, snr dna kan bitler yerine, saynn solundan besleme 0 biti ile yaplr. Saa kaydrlacak ifadenin iaretli bir tamsay trnden negatif deerli olmas durumunda snr

428

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

dna kan bitler iin soldan yaplacak beslemenin 0 ya da 1 bitleriyle yaplmas derleyiciye baldr (implementation specified). Yani derleyiciler bu durumda saynn iaretini korumak iin soldan yaplacak beslemeyi 1 biti ile yapabilecek bir kod retebilecekleri gibi, saynn iaretini korumay dnmeksizin 0 biti ile besleyecek bir kod da retebilirler. aretli negatif bir tamsaynn bitsel saa kaydrlmas tanabilir bir zellik deildir. Bir tamsayy saa bitsel olarak 1 kaydrmakla, o saynn ikiye blnm deeri elde edilir: #include <stdio.h> int main() { unsigned int x = 52; unsigned int y = 2; unsigned int z = x >> y; printf("z = %u\n", z); } return 0;

/* x = 0000 0000 0011 0100 */ /* z = 0000 0000 0000 1101 */ /* z = 13 */

Bitsel kaydrma ilelerinin yan etkileri yoktur. Yani sol terimleri bir nesne ise, bu nesnenin bellekteki deeri deimez. Kaydrma ilemi ile sol terim olan nesnenin deeri deitirilmek isteniyorsa, bu ilelerin ilemli atama biimleri kullanlmaldr. Kaydrma ilelerinin sa terimi, sol terimin ait olduu trn toplam bit saysndan daha kk olmaldr. Bu koul salanmam ise oluan durum tanmszdr (undefined behaviour). rnein Windows sistemlerinde int trden bir deerin 32 ya da daha fazla sola ya da saa kaydrlmas tanmszdr. Bu durumdan kanlmaldr. Bitsel kaydrma ilelerinin ncelik yn soldan saadr: x << 4 >> 8 x, 2 byte'lk iaretsiz bir tamsay deiken olsun. Yukardaki ifade derleyici tarafndan x << 4) >> 8 biiminde ele alnr. Bu ifade ile x deikeninin ortadaki 8 biti elde edilir.

"Bitsel ve" ileci (bitwise and), ile ncelik tablosunun 8. seviyesindedir. Bu seviyenin ncelik yn soldan saadr. lecin yan etkisi yoktur. Terimleri nesne gsteren bir ifade ise, bu terimlerin bellekteki deeri deimez. Deer retmek iin terimi olan tamsaylarn karlkl bitlerini "ve" ilemine sokar. "Ve" ilecine ilikin ilem tablosu tablosu aada yeniden veriliyor:

Bitsel ve leci

429

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

"Bitsel ve" ilecinin rettii deer, terimlerinin karlkl bitlerinin "ve" ilemine sokulmasyla elde edilen deerdir: #include <stdio.h> int main() { unsigned int x = 0x1BC5; unsigned int y = 0X3A0D; unsigned int z = x & y; printf("z = %X\n", z); } return 0;

/* /* /* /*

x y z z

= = = =

0001 1011 1100 0101 */ 0011 1010 0000 1101 */ 0001 1010 0000 0101 */ 0x1A05 */

1 biti "bitsel ve" ileminde etkisiz elemandr. 0 biti "bitsel ve" ileminde yutan elemandr. "Mantksal ve" ileci yerine yanllkla "bitsel ve" ilecini kullanmak sk yaplan bir hatadr. Aadaki kodu dikkatli bir ekilde inceleyin: #include <stdio.h> int main() { int x = 85; int y = 170; if (x && y) printf("dogru!\n"); else printf("yanlis!\n"); if (x & y) printf("dogru!\n"); else printf("yanlis!\n"); } return 0;

Yukardaki programda "mantksal ve" (&&) ileci yerine yanllkla "bitsel ve" (&) ileci kullanlyor. Hem "mantksal ve" hem de "bitsel ve", iki terimli, araek konumunda ilelerdir. Derleyiciler yukardaki kod iin bir hata ya da uyar iletisi retmez. Yukardaki rnekte "mantksal ve" ilecinin kullanlmas durumunda, mantksal "doru" biiminde yorumlanacak olan ifade, bitsel ve ilecinin kullanlmasyla 0 deeri retir, mantksal "yanl" olarak yorumlanr.

Bitsel zel Veya leci

Bitsel "zel veya" ileci (bitwise exor) ile ncelik tablosunun 9. seviyesindedir. ncelik yn soldan saadr. Yan etkisi yoktur, terimleri olan nesnelerin bellekteki deeri deimez. Deer retmek iin, terimi olan tamsaylarn karlkl bitlerini zel veya (exclusive or) ilemine sokar. Bitsel "zel veya" ilecine ilikin ilem tablosu aada veriliyor:

430

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki tablo yle zetlenebilir: Terimlerinden ikisi de ayn deere sahip ise, retilen deer 0, terimlerden biri dierinden farkl ise retilen deer 1 olur. #include <stdio.h> int main() { unsigned int x = 0x1BC5; unsigned int y = 0X3A0D; unsigned int z = x ^ y; printf("z = %X\n", z); return 0; }

/* /* /* /*

x y z z

= = = =

0001 0011 0010 21C8

1011 1100 0101 */ 1010 0000 1101 */ 0001 1100 1000 */ */

Bir tamsay, arka arkaya ayn deerle bitsel zel veya ilemine sokulursa, tamsaynn kendi deeri elde edilir: #include <stdio.h> int main() { unsigned int a = 0X1BC5; unsigned int b = 0X3A0D; a ^= b; a ^= b; printf("a = %X\n", a); return 0; } Baz ifreleme algoritmalarnda "zel veya" ileminin bu zelliinden faydalanlr.

/* a = 0001 1011 1100 0101 */ /* b = 0011 1010 0000 1101 */ /* a = 0010 0001 1100 1000*/ /* b = 0001 1011 1100 0101 */ /* a = 0X1BC5 */

Bitsel Veya leci

"Bitsel veya" (bitwise or operator) ileci, ile ncelik tablosunun 10. seviyesindedir. ncelik yn soldan saadr. Yan etkisi yoktur, terimleri nesne gsteren bir ifade ise bellekteki deerlerini deitirmez. Deer retmek iin terimi olan tamsaylarn karlkl bitlerini "veya" ilemine sokar. Bitsel veya ilecine ilikin ilem tablosu aada veriliyor:

Bitsel veya ileci, terimlerinin karlkl bitlerinin "veya" ilemine sokulmasyla elde edilen deeri retir:

431

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> int main() { unsigned int x = 0x1BC5; unsigned int y = 0X3A0D; unsigned int z = x | y; printf("z = %X\n", z); } return 0;

/* /* /* /*

x y z z

= = = =

0001 1011 1100 0101 */ 0011 1010 0000 1101 */ 0011 1011 1100 1101 */ 0x3BCD */

0 biti bitsel veya ileminde etkisiz elemandr. 1 biti bitsel ve ileminde yutan elemandr. Aadaki programda bitsel ve, zel veya, veya ilelerinin rettikleri deerler ikilik say sisteminde ekrana yazdrlyor. Program derleyerek altrn: #include <stdio.h> #include <stdlib.h> void bit_print(int val) { char bits[sizeof(val) * 8 + 1]; itoa(val, bits, 2); printf("%10d %032s\n", val, bits); } int main() { int x, y; printf("iki sayi giriniz "); scanf("%d%d", &x, &y); bit_print(x); bit_print(y); printf("bitsel ve islemi\n"); bit_print(x & y); printf("*********************************************************\n"); printf("bitsel ozel veya islemi\n"); bit_print(x ^ y); printf("*********************************************************\n"); printf("bitsel veya islemi\n"); bit_print(x | y); return 0; }

Bitsel ileler ksa devre davranna sahip deidir. Yani bu ilelerin her iki terimi de mutlaka ilenir.

Bitsel leler Ksa Devre Davran Gstermez

Bitsel deil ilecinin dnda, tm bitsel ilelere ilikin ilemli atama ileleri vardr. Daha nce de sylendii gibi bitsel ilelerin yan etkileri (side effect) yoktur. Bitsel ileler terimleri olan nesnelerin bellekteki deerlerini deitirmez. Eer terimleri olan nesnelerin deerlerinin deitirilmesi isteniyorsa bu durumda ilemli atama ileleri kullanlabilir:

Bitsel lemli Atama leleri

432

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

x x x x x

= = = = =

x x x x x

<< y >> y & y ^ y | y

yerine yerine yerine yerine yerine

x x x x x

<<= y >>= y &= y ^= y |= y

kullanlabilir. Bitsel zel veya ilemli atama ileci, tamsay trlerinden iki deikenin deerlerinin, geici bir deiken olmakszn takas (swap) edilmesinde de kullanlabilir: #include <stdio.h> int main() { int x, y; printf("iki sayi giriniz "); scanf("%d%d", &x, &y); printf("x = %d\ny = %d\n", x, y); x ^= y ^= x ^= y; printf("x = %d\ny = %d\n", x, y); } return 0;

Yukardaki programda, x ve y deikenlerinin deerleri bitsel zel veya ilecinin kullanlmasyla takas ediliyor.

Bitsel lelerin Kullanlmasna likin Baz Temalar

Bitsel ilelerin kullanlmasna daha ok sistem programlarnda rastlanr. Sistem programlarnda bir tamsaynn bitleri zerinde baz ilemler yaplmas sklkla gerekli olur. Aada bitsel dzeyde sk yaplan ilemler aklanyor:

Bir Tamsaynn Belirli Bir Bitinin Birlenmesi

Buna tamsaynn belirli bir bitinin "set edilmesi" de denebilir. Bir tamsaynn belirli bir bitini birlemek iin, sz konusu tamsay, ilgili biti 1 olan ve dier bitleri 0 olan bir sayyla "bitsel veya" ilemine sokulmaldr. nk bitsel veya ileminde 1 yutan eleman 0 ise etkisiz elemandr. Aadaki rnekte bir saynn 5. biti birleniyor: #include <stdio.h> int main() { int ch = 0x0041; int mask = 0x0020; ch |= mask; printf("ch = %d\n", ch); return 0; } x bir tamsay k da bu saynn herhangi bir bit numaras olmak zere bir tamsaynn k. bitini birleyecek bir ifade u biimde yazlabilir: x |= 1 << k

/* ch = 65 (0000 0000 0100 0001) */ /* mask = 32 (0000 0000 0010 0000) */ /* ch = 97 (0000 0000 0110 0001) /* ch = 97 */ */

433

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Byle ilemleri gerekletirmek iin kullanlan 1 << k gibi ifadelere "bitsel maske" (bitmask) ya da yalnzca "maske" denir.

Bir tamsaynn belirli bir bitini sfrlamak(clear) iin, sz konusu tamsay, ilgili biti 0 olan ve dier bitleri 1 olan bir maskeyle "bitsel ve" ilemine sokulmaldr. nk "bitsel ve" ileminde, 0 yutan eleman 1 ise etkisiz elemandr. Aadaki rnekte bir tamsaynn 5. biti sfrlanyor: #include <stdio.h> int main() { int ch = 0x0061; int mask = ~0x0020; ch &= mask; printf("ch = %d\n", ch); return 0; } x, bir tamsay, k da bu saynn herhangi bir bit numaras olmak zere, bir saynn k. bitini sfrlayan bir ifade aadaki gibi genelletirilebilir: x &= ~(1 << k);

Bir Tamsaynn Belirli Bir Bitinin Sfrlanmas

/* /* /* /*

ch = mask ch = ch =

97 (0000 0000 0110 0001) */ = ~32 (1111 1111 1101 1111)*/ 65 (0000 0000 0100 0001) */ 65 */

Bir tamsaynn belirli bir bitinin 0 m 1 mi oldugunun renilmesi iin, sz konusu tamsay, ilgili biti 1 olan ve dier bitleri 0 olan bir maskeyle "bitsel ve" ilemine sokularak mantksal olarak yorumlanmaldr. nk "bitsel ve" ileminde 0 yutan eleman, 1 ise etkisiz elemandr. fade mantksal olarak "dogru" biiminde yorumlanrsa, ilgili bit 1, yanl olarak yorumlanrsa ilgili bit 0 demektir. x bir tamsay, k da bu saynn herhangi bir bit numaras olmak zerebir saynn k. bitinin 1 ya da 0 olduunu snayan bir deyim aadaki biimde yazlabilir: if (x & 1 << k) /* k. bit 1 */ else /* k. bit 0 */ Bir pozitif tamsaynn tek say olup olmad "bitsel ve" ileciyle snanabilir. Bir tamsay tek say ise saynn 0. biti 1 dir. #include <stdio.h> int main() { int x; printf("bir sayi giriniz "); scanf("%d", &x); if (x & 1) printf("%d tek sayi\n", x); else printf("%d cift sayi\n", x); } return 0;

Bir Tamsaynn Belirli Bir Bit Deerinin Elde Edilmesi (0 m 1 mi)

434

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir Tamsaynn Belirli Bir Bitini Ters evirmek

Baz uygulamalarda bir tamsaynn belirli bir bitinin deerinin ters evrilmesi (toggle flip) gerekir. Yani sz konusu bit 1 ise 0, 0 ise 1 yaplmaldr. Bu amala "bitsel zel veya" ileci kullanlr. Bitsel zel veya ilecinde 0 biti etkisiz elemandr. Bir saynn k. bitinin deerini deitirmek iin, say, k. biti 1 dier bitleri 0 olan bir maske ile "bitsel zel veya" ilemine sokulur. x, bir tamsay, k da bu saynn herhangi bir bit numaras olmak zere, bir saynn k. bitini ters eviren bir ifade u ekilde yazlabilir: x ^= 1 << k;

Bir tamsaynn belirli bitlerini sfrlamak iin ne yaplabilir? rnein int trden bir tamsaynn 7., 8. ve 9. bitlerini, dier bitlerin deerlerini deitirmeden sfrlamak isteyelim. Bunu gerekletirmek iin, tamsay 7., 8. ve 9. bitleri 0 olan dier bitleri 1 olan bir maske ile bitsel ve ilemine sokulabilir. rnein int trnn 16 bit olduu bir sistemde bu say aadaki bit dzenine sahip olur: 1111 1100 0111 1111 Bu deerin onaltlk say sisteminde gsterimi 0xFC7F biimindedir, deil mi? x &= 0xFC7F; Tamsaylarn belirli bitleri zerinde yaplan ileri, ilevlere yaptrmaya ne dersiniz? void clearbits(int *ptr, size_t startbit, size_t nbits); clearbits ilevi adresi gnderilen ifadenin startbit nolu bitinden balayarak nbits tane bitini sfrlar. rnein x isimli unsigned int trden bir deikenin 7. 8. 9. bitlerini sfrlamak iin ilev aadaki biimde arlr: clearbits(&x, 7, 3); void clearbits(unsigned int *ptr, size_t startbit, size_t nbits) { size_t k; for (k = 0; k < nbits; ++k) *ptr &= ~(1 << nbits + k); } Benzer biimde setbits ilevi de yazlabilir. x isimli unsigned int trden bir deikenin 7. 8. 9. bitlerini birlemek iin ilev aadaki biimde arlr: clearbits(&x, 7, 3); void setbits(unsigned int *ptr, size_t startbit, size_t nbits) { size_t k; for (k = 0; k < nbits; ++k) *ptr |= ~(1 << nbits + k); } Peki rnein 32 bitlik bir alan birden fazla deeri tutacak ekilde kullanlabilir mi?

Birden Fazla Bit zerinde lem Yapmak

435

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Negatif saylarn kullanlmad dnlrse 4 bitlik bir alanda 0 15 aralndaki deerler tutulabilir. Bir deikenin deerinin 0 15 aralnda deitiini varsayalm: Baz durumlarda 4 bitle ifade edilebilen bir deerin 2 ya da 4 byte yer kaplayan bir tamsay trnde tutulmas istenmeyebilir. 32 bitlik bir alan iinde aslnda 4 ayr deer tutulabilir, deil mi? Bir saynn belirli bir bit alannda bir tamsay deerini tutmak iin ne yaplabilir? nce saynn ilgili bitleri sfrlanr. Bu ama iin yukarda yazlan clearbits gibi bir ilev arlabilir. Daha sonra say, uygun bir deerle bitsel veya ilemine sokulabilir. Aadaki ilev tanmn inceleyin: void putvalue(unsigned int*ptr, size_t startbit, size_t nbits, int value) { clearbits(ptr, startbit, nbits); *ptr |= value << startbit; } putvlaue ilevi adresi gnderilen nesnenin startbit nolu bitinden balayarak nbits bitlik alanna value deerini yerletirir. lev aadaki gibi arlabilir: putvalue(&x, 4, 3, 7); Bir tamsay deikenin belirli bit alanlar iinde saklanm deer nasl elde edilebilir? phesiz bu i de bir ileve yaptrlabilir: unsigned getvalue(unsigned int x, size_t startbit, size_t nbits); getvalue ilevi birinci parametresine aktarlan tamsay deerin, startbit numaral bitinden balayarak nbits bitlik alannda saklanan deerle geri dner: unsigned int getvalue(unsigned int number, size_t startbit, size_t nbits) { number <<= sizeof(int) * 8 - startbit - nbits; return number >>= sizeof(int) * 8 - nbits; }

Bitsel lelerin Kullanmna likin Baz rnek Uygulamalar

Aada int trden bir deeri, ekrana ikilik say sisteminde yazdran showbits isimli bir ilev tanmlanyor: #include <stdio.h> void showbits(int x) { int i = sizeof(int) * 8 - 1; for (; i >= 0; --i) putchar (x >> i & 1 ? '1' : '0'); } int main() { int val; printf("bir sayi giriniz : "); scanf("%d", &val); showbits(val); return 0;

Aada ayn ii deiik bir biimde yapan showbits2 isimli bir ilevin tanm yer alyor:

436

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void showbits2(int x) { unsigned int i = (~((unsigned)~0 >> 1)); while (i) { putchar (x & i ? '1' : '0'); i >>= 1; }

Aada tammlanan reverse_bits isimli ilev, int trden bir deerin bitlerini ters eviriyor: #include <stdio.h> int reverse_bits(int number) { int k; int no_of_bits = sizeof(int) * 8; int rev_num = 0; for (k = 0; k < no_of_bits; ++k) if (number & 1 << k) rev_num |= 1 << (no_of_bits - 1 - k); return rev_num; } Aada tammlanan reverse isimli ilev, unsigned char trden bir deerin bitlerini ters eviriyor: #include <stdio.h> unsigned char reverse(unsigned char byte) { unsigned char dest; dest = (byte << 4) | (byte >> 4); dest = ((dest << 2) & 0xCC) | ((dest >> 2) & 0x33); } return ((dest << 1) & 0xAA) | ((dest >> 1) & 0x55);

Aada tanmlanan no_of_setbits isimli ilev, kendisine gnderilen int trden bir deerin ka tane bitinin 1 olduu bilgisi ile geri dnyor: #include <stdio.h> int no_of_setbits(unsigned int value) { int counter = 0; int k; for (k = 0; k < sizeof(int) * 8; ++k) if (value & 1<<k) counter++; return counter; } int main()

437

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int number; printf("bir say girin : "); scanf("%d", &number); printf("saynzn %d biti 1n", no_of_setbits(number));

return 0;

imdi de ok daha hzl alacak bir ilev tasarlayalm: int no_of_setbits(unsigned 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;

Yukarda tannmlanan ilevde yer alan for dngs iinde, dngnn her turunda value deikeninin dk anlaml 4 bitindeki birlenmi bitlerin says, 4 bitin saysal deerinin bitcounts isimli diziye indis yaplmasyla elde ediliyor. rnein 4 bitlik alanda ifade edilen tamsaynn deeri 11 olsun: 11 = 1011 Bu saynn toplam 3 biti 1'dir. bitcounts dizisinin 11 indisli elemann deeri 3'tr. Dngnn bir sonraki turuna gemeden nce value deikeni saa 4 kez kaydrlarak bu kez value'nun bir sonraki 4 bitlik alanndaki bitlere baklyor. Aada ilem hzn daha da artran bir ilev tanmlanyor: const 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, }; static char 1, 2, 1, 2, 2, 3, 2, 3, 2, 3, 2, 3, 3, 4, 3, 4, 2, 3, 2, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 2, 3, 2, 3, 3, 4, 3, 4, 3, 4, 3, 4, 4, 5, 4, 5, 3, 4, 3, 4, 4, 5, 4, 5, 4, 5, 4, 5, 5, 6, 5, 6, bit_array[] 2, 3, 1, 2, 3, 4, 2, 3, 3, 4, 2, 3, 4, 5, 3, 4, 3, 4, 2, 3, 4, 5, 3, 4, 4, 5, 3, 4, 5, 6, 4, 5, 3, 4, 2, 3, 4, 5, 3, 4, 4, 5, 3, 4, 5, 6, 4, 5, 4, 5, 3, 4, 5, 6, 4, 5, 5, 6, 4, 5, 6, 7, 5, 6, = { 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8

int count1bits(unsigned long x) {

438

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

return bit_array[x & 0xff] + bit_array[(x >> 8) & 0xff] + bit_array[(x >> 16) & 0xff] + bit_array[(x >>24) & 0xff]; }

count1bits ilevi 32 bitlik bir tamsaynn deeri 1 olan bitlerini sayyor. lev bu ii yaparken 8 bitlik bir gruptaki 1 olan bitlerin saysn bit_array dizisini kullanarak buluyor. Aada tanmlanan gcd_b ileviyle, iki tamsaynn ortak blenlerinden en by hesaplanyor. levin tanm iinde kalan ilecinin kullanlmadna, bitsel ilelerin kullanldna dikkat edin: unsigned int gcd_b(unsigned int x, unsigned int y) { unsigned int temp; unsigned int cpof = 0; if (x == 0) return y; if (y == 0) return x; while (((x | y) & 1) == 0) { x >>= 1; y >>= 1; ++cpof; } while ((x & 1) == 0) x >>= 1; while (y) { while (!(y & 1)) y >>= 1; temp = y; if (x > y) y = x - y; else y -= x; x = temp; } return x << cpof;

439

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

BT ALANLARI
Bir yapnn eleman bit seviyesinde tutulan bir tamsay deiken olabilir. Yaplarn byle elemanlarna "bit alan" (bit fields) denir. Bit alanlar, C'nin bit seviyesinde eriime olanak veren bir aracdr. Bit seviyesinde eriim amacyla phesiz daha nceki konuda anlatlan bitsel ileler kullanlabilir. Ancak bit alanlar programcya byle ulamlar iin daha kolay bir arayz sunar. Bir yap iinde bir bit alan oluturmak iin zel bir szdizim kuralna uymak gerekir. Elemann yap iindeki bildiriminde eleman ismini ':' atomu izler. Bu atomdan sonra bit alannn ka bit yer kaplayaca bilgisini gsteren bir tamsay deimezi yazlr. Aadaki bildirimi inceleyin: struct Data { unsigned int a: 5; unsigned int b: 4; unsigned int c: 7; }; struct Data isimli yapnn a isimli eleman, yap iinde 5 bit yer kaplarken, b isimli eleman 4 bit, c isimli eleman 7 bit yer kaplar. Yapnn elemanlar unsigned int trnden olduuna gre bu trden bir nesnenin a eleman 0 - 31 b eleman 0 -15 c eleman 0 - 127 arasnda tamsay deerler tutabilir.

Bit Alan Elemanlarn Trleri

Bir bit alan eleman, iaretli ya da iaretsiz int trlerden olabilir. Bir bit alan eleman gerek say trlerinden olamaz. C standartlar bit alan elemanlarnn char, short, long trlerinden olamayacan belirtse de C derleyicilerin ou seimlik olarak bu duruma izin verir. Tanabilirlik asndan bit alan elemann bildiriminde signed ya da unsigned szcklerinin kullanlmas yerinde olur. nk derleyici yalnzca int anahtar szcyle bildirilen bir bit alannn trn iaretli ya da iaretsiz int tr olarak alabilir. Bit alan eleman bir dizi olamaz.

Baz dsal aygtlar verileri bitsel dzeyde iletiyor olabilir. Baz sktrma ilemlerinde bir tamsaynn belirli sayda biti kullanlyor olabilir. Baz ifreleme ilemlerinden bir tamsaynn belirli bit alanlarnda yaplan kodlama yapyor olabilir. ok sayda bayrak deikeni bit alanlar olarak tutulabilir. phesiz bu ilemleri yapmak iin bitsel ileler de kullanlabilir. Ancak bit alanlar bu ilerin yaplmas iin programcya ok daha kolay bir arayz sunar, programcnn iini kolaylatrr. rnein DOS iletim sistemi dosyalara ilikin tarih ve zaman bilgilerini 16 bitlik alanlarda tutar:

Bit Alanlar Neden Kullanlr:

441

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

struct file_date unsigned int unsigned int unsigned int };

{ day: 5; mon: 4; year: 7;

Yukardaki bildirimden de grld gibi, tarih bilgisinin yl iin 7 bitlik bir alan ayrlmtr. 7 bitlik bir alanda tutulabilecek en byk deer 127 dir. DOS iletim sisteminde byle bir bit alannda yl deerinin 1980 fazlas tutulur. Aadaki ilev file_date yaps iinde tutulan tarih bilgisini ekrana yazdryor: void display_file_date(const file_date *ptr) { printf("%02u/%02u/%u", ptr->day, ptr->mon, ptr->year); } Bir yapnn bit alan elemanlarna, yine nokta ya da ok ileleriyle eriilir. phesiz, derleyicinin rettii kod bitsel ilemlerin yaplmasn salar. Bu kez zaman bilgisini tutmak iin elemanlar bit alanlar olan bir yap bildiriliyor: struct file_time unsigned int unsigned int unsigned int }; { hour: 5; min: 6; sec: 5;

Zaman bilgisinin saniye deeri iin 5 bitlik bir alan ayrlyor. 5 bitlik bir alanda tutulabilecek en byk deer 31'dir. DOS iletim sisteminde, byle bir bit alannda gerek saniye deerinin yars tutulur. Aadaki ilev file_time yaps iinde tutulan zaman bilgisini ekrana yazdryor: void display_file_date(const file_time *ptr) { printf("%02u:%02u%u", ptr->hour, ptr->min, 2 * ptr->sec); }

Bitalan Elemanlarn Adresleri

Bir bitalan elemann adresi alnamaz. Bir bit alan elemann adres ilecinin terimi olmas geersizdir: #include <stdio.h> void func(); { struct file_date fdate; scanf("%d", &fdate.day); /***/ } /* Geersiz */

Bir bit alannn bellekte yerleimi konusunda derleyiciler geni lde serbest braklmtr. Bit alanlarnn bellekte yerleimi "saklama birimi" (storage unit) isimli bir terimle aklanr. Saklama birimi, belirli bir byte uzunluudur ve derleyicinin seimine baldr. rnein derleyicinin setii saklama birimi 1, 2, 4 vs. byte olabilir. Bit alanlarnn bildirimiyle karlaan derleyici bit alanlarn, aralarnda boluk brakmadan tek bir saklama birimine yerletirmeye altrr. Saklama biriminde bir bit alan iinde yeteri kadar yer kalmaz ise bir sonraki saklama birimine geilir. Smayan elemann her

Bit Alanlarnn Bellekte Yerleimi

442

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

iki saklama biriminde mi tutulaca yoksa tamamen yeni saklama biriminde mi yer alaca derleyicinin seimine braklmtr. Bir elemann ilgili bit alan iinde yerleiminin soldan saa ya da sadan sola olmas da yine derleyicinin seimine braklmtr. Bir bit alan uzunluu saklama biriminin kendi uzunluundan daha byk olamaz. rnein saklama birimi 8 bit ise 9 bitlik bir bit alan eleman kullanlmaz. Bit alan elemanlarn yerleimi zerinde daha fazla denetimin salanmas iin, elemana isim vermeme olana da getirilmitir. Bir bit alan elemana isim verilmeyebilir. smi olmayan bir bit alan eleman ii derleyici yine gerekli yeri ayrr. Bylece programc kullanaca elemanlarn isel yerleimini kendine gre ayarlayabilir. typedef struct { int : 5; int hour : 5; int min : 6; }Time; Yukardaki rnekte Time yapsnn isim verilmeyen birinci eleman iin, derleyici yine 5 bit ayrr. Yani yapnn hour isimli eleman 5. bitten balayarak yerletirilir. Bir baka olanak da, bit alan elemann uzunluunu 0 olarak belirlemektir. Bunun nceden belirlenmi zel bir anlam vardr. Bir elemann uzunluunun 0 olduunu gren derleyici bir sonraki elaman yeni bir saklama biriminden balatr: Aadaki ilgili sistemin saklama birimi uzunluu bit says olarak elde ediliyor: #include <stdio.h> #include <limits.h> typedef struct { int : 1; int : 0; int : 1; }StorageCheck; int main() { printf("Saklama birimi = %u bit\n", sizeof(StorageCheck) / 2 * CHAR_BIT); } return 0;

Yukardaki rnekte StorageCheck isimli yap tanmlanyor. Bu yapnn isim verilmeyen ilk bitalan eleman iin 1 bit yer ayrldn gryorsunuz. kinci eleman iin ise uzunluk 0 olarak belirleniyor. Son elemann uzunluu yine 1 bitdir. Derleyici 3. eleman iin gereken yeri bir sonraki saklama biriminden ayrr. Bu durumda eer saklama birimi 8 bit ise yapnn toplam uzunluu 16 bit, saklama birimi 16 bit ise yapnn uzunluu 32 bit nihayet saklama birimi 32 bit ise yapnn uzunluu 64 bit olur.

443

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

KOMUT SATIRI ARGMANLARI


Bir program altrld zaman, programn almasn ynlendirecek ya da deitirecek baz parametreler, alacak programa gnderilmek istenebilir. Programa gnderilecek bir parametre rnein bir dosya ismi olabilecei gibi, programn deiik biimlerde almasn salayacak bir seenek de olabilir. UNIX iletim sistemindeki ls komutunu dnelim. Eer ls program ls yazarak altrlrsa, bulunulan dizin iindeki dosyalarn isimleri listelenir. 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 program yine ls -l sample.c yazarak altrlrsa, yalnzca sample.c dosyasna ilikin bilgiler grntlenir. te bir program altrrken program isminin yanna yazlan dier parametrelere komut satr argmanlar (command line arguments) denir. Komut satr argmanlar yalnzca iletim sistemi komutlar iin geerli deildir. Tm programlar iin komut satr argmanlar kullanlabilir. Komut satr argmanlarna C dili standartlarnda program parametreleri (program parameters) denir. C programlarnn almaya balad main ilevi de, istee bal olarak iki parametre deikenine sahip olabilir. Bu parametre deikenleri geleneksel olarak argc ve argv olarak isimlendirilir. int main(int argc, char *argv[]) { /***/ } argc "argument count" szcklerinden ksaltlmtr. Komut satr argmanlarnn saysn gsterir. Bu sayya programn ismi de dahildir. argv ise "argument vector" szcklerinden ksaltlmtr. argv, dizgeler eklinde saklanan komut satr argmanlarn gsteren, char trden bir gsterici dizisinin adresini alan gstericidir. Yani argv gstericisinin gsterdii yerde bir gsterici dizisi vardr. Bu gsterici dizisinin her eleman, komut satr argman olan yazlar tutar. Bu durumda argv[0] gstericisi programn ismi olan yaznn balang adresini tutar. argv[1]'den argv[argc - 1] e kadar olan gstericiler ise program ismini izleyen dier komut satr argman olan yazlarn balang adreslerini tutar. argv[argc] gstericisi ise NULL adresi deerine sahiptir. Yukardaki rnekte kullanc ls programn ls -l sample.c eklinde altrdnda argc, 3 deerini alr. argv[0], program ismini gsterir. argv[0] = "ls"; argv[1] program ismini izleyen birinci argman gsterir. argv[1] = "-l";

445

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

argv[2] program ismini izleyen ikinci argman gsterir. argv[2] = "sample.c"; argv[argc] yani argv[3] ise NULL adresini gsterir. Bir ilevin, gstericiyi gsteren gsterici (pointer to pointer) parametresinin iki ayr biimde tanmlanabileceini anmsayn: void func(char **ptr); void foo(char *ptr[]); Her iki ilevin de parametre deikeni char trden gstericiyi gsteren bir gstericidir. Bu durumda, komut satr argmanlarn ileyecek bir main ilevinin de gstericiyi gsteren gsterici olan argv parametresi, iki farkl biimde tanmlanabilir: int main(int argc, char **argv) { /***/ } int main(int argc, char **argv) { /***/ } Komut satr argmanlar boluklarla birbirinden ayrlm olmaldr. Yukardaki rnekte program ls -lsample.c biiminde altrlrsa argc, 2 deerine sahip olurdu. Komut satr argmanlar, iletim sistemi tarafndan komut satrndan alnarak derleyicinin rettii giri kodu yardmyla main ilevine gnderilir. Aada komut satr argmanlarn ekrana 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;

main ilevinde yer alan dng deyimi aadaki biimde de yazlabilirdi: for (i = 0; argv[i] != NULL ; ++i) printf("argv[%d] : %s\n", i, argv[i]); Komut satrndan altrlan programlar genellikle, nce girilen argmanlar yorumlar ve snar. rnein bir dosyann kopyasn karmak zere, ismi kopyala olan bir programn, komut satrndan altrlmak istendiini dnelim. Program, komut satrndan kopyalanacak dosyann ismini ve yeni yaratlacak dosyann ismini istesin:

446

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

kopyala deneme.c test.c Program komut satrndan yukardaki gibi altrldnda, ismi deneme.c olan bir dosyann ismi test.c olan bir kopyasn olutursun. Bu durumda argc 3 degerini alr, deil mi? #include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { /***/ if (argc != 3) { printf("kullanim : <kopyala> <kaynak dosya ismi> <yeni dosya ismi>\n"); exit(EXIT_FAILURE); } /***/ return 0; } Yukardaki main ilevinde, program 3 komut satr argman verilerek altrlmadysa, yani eksik ya da fazla argmanla altrldysa, program bir hata iletisiyle sonlandrlyor. Komut satrndan argmanlar doru girilmedii zaman bir program sonlandrlmak zorunda deildir. Dardan doru olarak girilemeyen argmanlarn bu kez, programn almas durumunda program kullanan kii tarafndan girilmesi istenebilir. Aadaki rnei inceleyin: #include <stdio.h> #include <string.h> #define NAME_LEN 80

int main(int argc, char *argv[]) { char source_name[NAME_LEN]; char dest_name[NAME_LEN]; if (argc != 3) { printf("kaynak dosyanin ismini giriniz : "); gets(source_name); printf("kopya dosyanin ismini giriniz : "); gets(dest_name); } else { strcpy(source_name, argv[1]); strcpy(dest_name, argv[2]); } /***/ return 0;

DOS iletim sisteminde olduu gibi, baz sistemlerde main ilevi nc bir parametre alabilir. nc parametre sistemin evre deikenlerine ilikin bir karakter trnden gstericiyi gsteren gstericidir. int main(int argc, char *argv[], char *env[]) { /***/ }

447

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aadaki programda, komut satrndan girilen szckler ters srada ve tersten yazdrlyor: #include <stdio.h> int main(int argc, char **argv) { int k, i; for (k = argc - 1; k > 0; --k) { for (i = 0; argv[k][i] != '\0'; ++i) ; for (i--; i >= 0; --i) putchar(argv[k][i]); putchar('\n'); } } return 0;

Aada, komut satrndan altrlacak basit bir hesap makinesi programnn kodu veriliyor: #include <stdio.h> #include <stdlib.h> #include <math.h> int main(int argc, char *argv[]) { char ch; int op1, op2; if (argc != 4) { printf("usage : cal Op1 Operator Op2\n"); exit(EXIT_FAILURE); } op1 = atoi(argv[1]); op2 = atoi(argv[3]); ch = argv[2][0]; printf(" = "); switch (ch) { case '+' : case '-' : case '/' : case '*' : case '%' : case 'k' : default : } return 0; printf("%d\n", op1 + op2); return 0; printf("%d\n", op1 - op2); return 0; printf("%lf\n", (double)op1 / op2); return 0; printf("%d\n", op1 * op2); return 0; printf("%lf\n", (double)op1 * op2 / 100); return 0; printf("%lf\n", pow(op1, op2)); return 0; printf("hatal ile\n");

Aadaki program, komut satrndan gn, ay ve yl deerleri girilen bir tarihin haftann hangi gnne geldiini ekrana yazdryor:

448

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> char *days[] = {"Pazar", "Pazartesi", "Sali", "Carsamba", "Persembe", "Cuma", "Cumartesi"}; int day_of_week(int d, int m, int y) { static int t[] = {0, 3, 2, 5, 0, 3, 5, 1, 4,6, 2, 4}; y -= m < 3; return (y + y / 4 - y / 100 + y / 400 + t[m - 1] + d) % 7; } int main(int argc, char **argv) { int day, mon, year; if (argc != 4) { printf("gun ay ve yil degerini giriniz : "); scanf("%d%d%d", &day, &mon, &year); } else { day = atoi(argv[1]); mon = atoi(argv[2]); year = atoi(argv[3]); } printf("%s\n", days[day_of_week(day, mon, year)]); return 0; }

449

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DOSYALAR
kincil belleklerde tanmlanm blgelere dosya denir. Dosya ilemleri tamamen iletim sisteminin kontrol altndadr. Her dosyann bir ismi vardr. Ancak dosyalarn isimlendirme kurallar sistemden sisteme gre deiebilir. letim sistemi de bir programdr. Bu program da ayr ayr yazlm ilevlerin birbirlerini armas biiminde alr. rnein komut satrnda bir programn isminin yazlarak altrlmas aslnda birka sistem ilevinin arlmas ile yaplr. Komut satrndan yazlan yazy alan, diskte bir dosyay arayan, bir dosyay bellee ykleyen, bellekteki program altran ilevler dzenli olarak arlr. letim sisteminin almas srasnda kendisinin de ard, sistem programcsnn da dardan arabildii iletim sistemine ait ilevlere sistem ilevleri denir. Bu tr ilevlere Windows sisteminde API (Application Programming Interface) ilevleri, UNIX iletim sisteminde ise sistem arlar (system calls) denir. Aslnda btn dosya ilemleri, hangi programlama dili ile allrsa allsn, iletim sisteminin sistem ilevleri tarafndan yaplr. Sistem ilevlerinin isimleri ve parametrik yaplar sistemden sisteme deiebilir.

Bir dosya zerinde ilem yapmadan nce dosya almaldr. Dosya aabilmek iin iletim sisteminin "dosya a" isimli bir sistem ilevi kullanlr. Dosyann almas srasnda dosya ile ilgili eitli ilk ilemler iletim sistemi tarafndan yaplr. Bir dosya aldnda, dosya bilgileri, ismine "Dosya Tablosu" (File table) denilen ve iletim sisteminin iinde bulunan bir tabloya yazlr. Dosya tablosunun biimi sistemden sisteme deiebilir. rnein tipik bir dosya tablosu aadaki gibi olabilir: Dosya tablosu Sra No 0 1 ... 12 ... Dosya ismi Dosyann Diskteki Dosyann Yeri zellikleri Dierleri

Dosyann Almas

AUTOEXEC.BAT

...

...

...

letim sisteminin sistem ilevlerinin de parametre deikenleri, geri dn deerleri vardr. "Dosya a" sistem ilevinin parametresi alacak dosyann ismidir.lev, dosya tablosunda dosya bilgilerinin yazld sra numaras ile geri dner ki bu deere "file handle" denir. Bu handle deeri dier dosya ilevlerine parametre olarak geirilir. Dosyann almas srasnda buna ek olarak baka nemli ilemler de yaplr.

Dosyann kapatlmas almas srasnda yaplan ilemlerin geri alnmasn salar. rnein dosyann kapatlmas srasnda, iletim sisteminin dosya tablosunda bulunan bu dosyaya ilikin bilgiler silinir. Alan her dosya kapatlmaldr. Bir dosyann kapatlmamas eitli sorunlara yol aabilir.

Dosyann Kapatlmas

Dosyaya Bilgi Yazlmas ve Okunmas

letim sistemlerinin dosyaya n byte veri yazan ve dosyadan n byte veri okuyan sistem ilevleri vardr. Yazma ve okuma ilemleri bu ilevler kullanlarak yaplr.

451

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Dosya Konum Gstericisi


Bir dosya byte'lardan oluur. Dosyadaki her bir byte a 0'dan balayarak artan srada bir say karlk getirilir. Bu sayya ilgili byte n ofset numaras denir. Dosya konum gstericisi isel olarak tutulan long trden bir deikendir, bir ofset deeri yani bir byte numaras belirtir. Dosyaya yazan ve dosyadan okuma yapan standart ilevler, bu yazma ve okuma ilemlerini her zaman dosya konum gstericisinin gsterdii yerden yapar. Bu ilevler, dosyann neresine yazlacan ya da dosyann neresinden okunacan gsteren bir deer istemez. rnein dosya konum gstericisinin gsterdii yer 100. ofset olsun. Dosyadan 10 byte bilgi okumak iin bir sistem ilevi arldnda, 100. ofsetden balayarak 10 byte bilgi okunur. letim sisteminin dosya gstericisini konumlandran bir sistem ilevi vardr. Dosya ilk aldnda dosya konum gstericisi 0. ofseti gsterir. rnein bir dosyann 100. byte ndan balayarak 10 byte okunmak istenirse sras ile u ilemlerin yaplmas gerekir: lgili dosya alr dosya konum gstericisi 100. offset'e konumlandrlr Dosyadan 10 byte okunur Dosya kapatlr. C dilinde dosya ilemleri iki biimde yaplabilir : 1. letim sisteminin sistem ilevleri dorudan arlarak. 2. Standart C ilevleri kullanlarak. Bildirimleri stdio.h iinde olan standart dosya ilevlerinin hepsinin ismi f ile balar. Standart C ilevleri da ilemlerini yapabilmek iin aslnda iletim sisteminin sistem ilevlerini arr. letim sisteminin sistem ilevleri tanabilir deildir. simleri ve parametrik yaplar sistemden sisteme deiebilir. Bu yzden standart C ilevlerinin kullanlmas tavsiye edilir.

fopen levi
FILE *fopen (const char *fname, const char *mode); levin birinci parametresi alacak dosyann ismidir. kinci parametre a modu bilgisidir. Her iki bilgi de bir yaz olarak ileve iletilir. Dosya ismi yol bilgisi de ierebilir. Dizin geileri iin '/' karakteri de kullanlabilir. Bir dosya belirli modlarda alabilir. A modu bilgisi, alacak dosya ile ilgili olarak hangi ilemlerin yaplabileceini belirler. Yine a modu bilgisi, almak istenen dosyann var olup olmamas durumunda ilevin nasl davranacan belirler. Aadaki tabloda, fopen ilevine a modu bilgisini iletmek zere geilmesi gereken yazlar listelenmitir: Mod "w" Anlam Dosya yazmak iin alr. Dosyadan okuma yaplamaz. Dosyann var olmas zorunlu deildir. Dosya yok ise verilen isimde bir dosya yaratlr. Dosya var ise dosya sfrlanr. Var olan bir dosyay bu modda amak dosyann kaybedilmesine neden olur. Dosyay hem yazma ve okuma iin aar. Dosyann var olmas zorunlu deildir. Dosya yok ise veriln isimde bir dosya yaratlr. Dosya var ise sfrlanr. Var olan bir dosyay bu modda amak dosyann kaybedilmesine neden olur. Dosya okumak iin alr. Dosyaya yazlamaz. Dosya yok ise alamaz. Dosya hem okuma hem yazma iin alr. Dosya yok ise alamaz. Dosya sona ekleme iin alr. Dosyadan okuma yaplamaz. Dosyann var olmas zorunlu deildir. Dosya yok ise verilen isimde bir dosya yaratlr. Dosya var ise dosya sfrlanmaz. Dosyay sonuna ekleme ve dosyadan okuma iin aar. Dosyann var olmas zorunlu deildir. Dosya yok ise verilen isimde bir dosya yaratlr. Dosya var ise sfrlanmaz.

"w+"

"r" "r+" "a" "a+"

452

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

levin geri dn deeri FILE yaps trnden bir adrestir. lev, alan dosyaya ilikin birtakm bilgileri FILE yaps trnden bir nesnenin elemanlarnda saklar ve bu nesnenin adresini geri dndrr. levin geri dn deerine ilikin FILE yaps stdio.h balk dosyas iinde bildirilmitir. Bu yapnn elemanlar standart deildir. Sistemden sisteme deiiklik gsterebilir. Zaten programc bu yapnn elemanlarna gereksinim duymaz. fopen ilevi iletim sisteminin dosya a sistem ilevini ararak dosyay aar ve dosyaya ilikin baz bilgileri FILE yaps trnden bir nesnenin elemanlarna yazarak bu nesnenin balang adresini geri dndrr. rnein "file handle" deeri de bu yapnn ierisindedir. Tabi fopen ilevinin geri verdii FILE trnden adres gvenli bir adrestir. Dosya eitli sebeplerden dolay alamayabilir. Bu durumda fopen ilevi NULL adresine geri dner. levin geri dn deeri kesinlikle kontrol edilmelidir. Tipik bir snama ilemi aadaki gibi yaplabilir: /*..*/ FILE *f; if ((f = fopen("mektup.txt", "r")) == NULL) { printf("cannot open file"...\n); exit(EXIT_FAILURE); } Yukardaki rnekte ismi mektup.txt olan bir dosya okuma amacyla almaya allyor. Dosya almaz ise ekrana bir hata iletisi verilerek, standart exit ilevi ile program sonlandrlyor. Yukardaki kod parasnn atama ilecinin rettii deerin nesneye atanan deer olmasndan faydalandn gryorsunuz. phesiz fopen ilevi ile almak istenen bir dosyann alamamas durumunda programn sonlandrlmas zorunlu deildir. Ancak bundan sonra verilecek kod rneklerinde, bir dosyann almamas durumunda imdilik program sonlandrlacak. Dosya ismi dosyann yeri hakknda src, yol gibi bilgi ierebilir. Dosya ismi bir dizge ile veriliyorsa yol bilgisi verirken dikkatli olmak gerekir. Yol bilgisi '\' (ters bl) karakteri ierebilir. dizge iinde '\' karakterinin kullanlmas, '\ 'karakterininin onu izleyen karakterle birlikte, nceden belirlenmi ters bl karakter deimezi (escape sequence) olarak yorumlanmasna yol aabilir. rnein : fopen("C:\source\new.dat", "r"); Yukardaki ilev arsnda derleyici '\n' karakterini "newline" karakteri olarak yorumlar '\s' karakterini ise "undefined" kabul eder. Bu sorundan saknmak '\' karakteri yerine '\\' kullanlmasyla mmkn olur: fopen("C:\\source\\new.dat", "r"); Sona ekleme modlar ("a", "a+") ok kullanlan modlar deildir. Dosyaya yazma durumunda "w" modu ile "a" modu arasnda farkllk vardr. "w" modunda dosyada olan byte n zerine yazlabilir. "a" modunda ise dosya ierii korunarak sadece dosyann sonuna yazma ilemi yaplabilir. Bir dosyann hem okuma hem de yazma amacyla almas durumunda yani a modunu belirten dizgede '+' karakterinin kullanlmas durumunda dikkatli olmak gerekir. Okuma ve yazma ilemleri arasnda mutlaka ya dosya konum gstericisinin konumlandrlmas (mesela fseek ilevi ile) ya da dosyaya iliken tampon bellek alannn (buffer) boaltlmas gerekir. Bu konuya ileride ayrntl bir ekilde deinilecek. Bir dosyann alp alamayaca aadaki kk programla snanabilir: Program komut satrndan

453

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

canopen dosya.dat eklinde altrldnda ekrana "dosya.dat dosyas alabilir" ya da "dosya.dat dosyas alamaz" yazar. #include <stdio.h> int main(int argc, char *argv[]) { FILE *fp; if (argc != 2) { printf("usage: canopen filename\n"); return 1; } if ((fp = fopen(argv[1], "r")) == NULL) { printf("%s dosyas alamaz\n", argv[1]); return 2; } printf("%s dosyas alabilir\n", argv[1]); fclose(fp); return 0; } stdio.h balk dosyas iinde FOPEN_MAX isimli bir simgesel deimez tanmlanyor. FOPEN_MAX simgesel deikeni ayn zamanda alabilecek en byk dosya saysdr.

fclose levi

Standart bu ilevin stdio.h balk dosyas iindeki bildirimi aadaki gibidir: int fclose(FILE *stream); Bu ilev alm ola bir dosyay kapatr. lev fopen ya da fropen ilevinden elde edilen FILE yaps trnden adresi parametre olarak alr ve ak olan dosyay kapatr. levin geri dn deeri 0 ise dosya baarl olarak kapatlm demektir. levin geri dn deeri EOF ise dosya kapatlamamtr. EOF , stdio.h balk dosyas iinde tanmlanan bir simgesel deimezdir, derleyicilerin ounda (-1) olarak tanmlanmtr: #define EOF (-1)

levin baars ancak phe altnda snanmaldr. Normal koullar altnda dosyann kapatlmamas iin bir neden yoktur. #include <stdio.h> int main() { FILE *f; if ((f = fopen("mektup.txt", "w")) == NULL) { printf("mektup.txt dosyas yaratlamyor!\n"); exit(EXIT_FAILURE); } fclose(f); printf("mektup.txt dosyasi kapatildi!\n"); return 0; }

454

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Derleyicilerin ounda bulunmasna karlk standart olmayan fcloseall isimli bir ilev de vardr: int fcloseall(void); Bu ilev arldnda ak olan dosyalarn hepsi kapatlr. levin geri dn deeri, kapatlan dosya saysdr. imdi de alm bir dosyadan okuma yapan ya da alm bir dosyaya yazma yapan standart C ilevlerini inceleyelim:

fgetc levi
int fgetc(FILE *f); C'nin standart yazma ve okuma yapan ilevleri yazlan ve okunan ofset says kadar dosya konum gstericisini ilerletirler. fgetc ilevi dosya gstericisinin gsterdii yerdeki byte okur ve bu byte n tamsay deerini geri dn deeri olarak verir. lev baarsz olursa, yani okuma ilemi yaplamaz ise stdio.h dosyas iinde simgesel deimez olarak tanmlanm EOF deerine geri dner. fgetc ilevinin geri dn deerini char trden bir deikene atamak yanl sonu verebilir, bu konuda dikkatli olunmal ve ilevin geri dn deeri int trden bir deikende saklanmaldr. char ch; ch = fgetc(fp); Yukarda dosyadan okunan karakterin 255 numaral ASCII karakteri (0x00FF) olduunu dnelim. Bu say char trden bir deikene atandnda yksek anlaml byte kaybedilerek ch deikenine 0xFF deeri atanr. Bu durumda ch deikeni iaretli char trden olduundan ch deikeni iinde negatif bir tamsaynn tutulduu anlam kar. if (ch == EOF) gibi bir karlatrma deyiminde, if ayrac iindeki karlatrma ileminin yaplabilmesi iin otomatik tr dnm yaplr, yani ch tam sayya ykseltilir (integral promotion). Bu otomatik tr dnmnde iaretli int trne evrilecek ch deikeni negatif olduu iin FF byte' ile beslenir. Bu durumda eitlik karlatrmas doru sonu verir, yani dosyann sonuna gelindii (ya da baka nedenden dolay okumann yaplamad) yorumu yaplr. Oysa ch deikeni int trden olsayd, ch deikenine atanan deer 0x00FF olurdu. Bu durumda karlatrma yapldnda ch deikeni ile EOF deerinin (0xFFFF) eit olmad sonucuna varlrd. fgetc ilevi kullanlarak okuma amacyla alm bir dosya karakter karakter okunabilir. Aadaki programda klavyeden isimi alnan bir dosyann ierii ekrana yazdrlyor:

455

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #define FILENAME_LEN 256

int main() { FILE *f; char file_name[FILENAME_LEN]; int ch; printf("Yazdirilacak dosyann ismi : "); gets(file_name); if ((f = fopen(file_name, "r")) == NULL) { printf("cannot open file...\n"); exit(EXIT_FAILURE); } while ((ch = fgetc(f)) != EOF) putchar(ch); fclose(f); return 0; } Yukardaki kodu inceleyin. nce yazdrlacak dosyann ismi klavyeden alnarak file_name isimli diziye yerletiriliyor. smi file_name dizisine alnan dosya fopen ilevine yaplan ar ile okuma amacyla alyor. fopen ilevinin baarsz olmas durumunda program sonlandrlyor. while ((ch = fgetc(f)) != EOF) putchar(ch); dng deyimiyle, fgetc ilevi EOF deerini dndrnceye kadar dosyadan bir karakter okunuyor ve okunan karakterin grnts standart putchar ileviyle ekrana yazdrlyor. Okuma ilemi tamamlannca standart fclose ileviyle dosya kapatlyor. imdi de komut satrndan altrlacak aadaki program inceleyin. lev komut satrndan <say> <dosya ismi> <karakter> biiminde altrlr. Program ekrana ismi verilen dosyada nc komut satr argman olarak verilen karakterden ka tane bulunduu bilgisini yazar. Program nce inceleyin daha sonra derleyerek altrn:

456

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <string.h> #define FILENAME_LEN 256

int main(int argc, char **argv) { FILE *f; char file_name[FILENAME_LEN]; int ch, cval; int char_counter = 0; if (argc != 3) { printf("Dosya ismi : "); gets(file_name); printf("sayilacak karakter : "); cval = getchar(); } else { strcpy(file_name, argv[1]); cval = *argv[2]; } if ((f = fopen(file_name, "r")) == NULL) { printf("%s dosyasi acilamiyor!\n", file_name); exit(EXIT_FAILURE); } while ((ch = fgetc(f)) != EOF) if (ch == cval) char_counter++; fclose(f); printf("%s dosyasinda %d adet %c karakteri var!\n", file_name, har_counter, ch); } return 0;

Son olarak unu da ekleyelim. fgetc bir ilevdir. Ancak stdio.h balk dosyas iinde getc isimli bir de makro tanmlanmtr. Derleyicilerin ou getc makrosuyla ayn isimli bir de ilev tanmlar. Ancak fgetc bir makro deil ilevdir. Makrolar konusuna ilerdeki blmlerde deinilecek.

fputc levi

int fputc(int ch, FILE *p); Bu ilev dosya konum gstericisinin bulunduu yere 1 byte bilgiyi yazar. levin birinci parametresi yazlacak karakter, ikinci parametresi ise yazlacak dosyaya ilikin FILE yaps adresidir. levin geri dn deeri EOF ise yazma ilemi baarsz olmu demektir. Yazma ilemi baarl olmusa ilev yazlan karakterin sra numaras ile geri dner. Sk yaplan bir hata ileve gnderilecek argumalarn srasn kartrmaktr. rnein f FILE yaps trnden bir nesneyi gsteren gsterici olsun. fputc ilevi fputc('A', f) yerine yanllkla fputc(f, 'A');

457

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

biiminde arlrsa f adresi int trne ve 'A' tamsay deeri de FILE yaps trnden bir adrese dntrlr. Bu durumda phesiz yazma ilemi baarsz olur. Aada fgetc ve fputc ilevlerini kullanarak bir dosyann kopyasn kartan bir programn kodunu gryorsunuz. Program komut satrndan <kopyala> <kaynak dosya ismi> <hedef dosya ismi> eklinde altrlr. Programn altrlmasyla ikinci komut satr argmanyla ismi verilen dosyann nc komut satr argmanyla verilen isimli bir kopyas kartlr. Program dikkatle inceleyin: #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAX_FILE_NAME_LEN 256

int main(int argc, char **argv) { FILE *fsource, *fdest; char source_name[MAX_FILE_NAME_LEN]; char dest_name[MAX_FILE_NAME_LEN]; int ch; int byte_counter = 0; if (argc != 3) { printf("kopyalanacak dosyann ismi : "); gets(source_name); printf("kopya dosyann ismi : "); gets (dest_name); } else { strcpy(source_name, argv[1]); strcpy(dest_name, argv[2]); } if ((fsource = fopen(source_name, "r")) == NULL) { printf("%s dosyasi acilamiyor\n", source_name); exit(EXIT_FAILURE); } printf("%s dosyasi acildi!\n", source_name); if ((fdest = fopen(dest_name, "w")) == NULL) { printf("%s dosyasi yaratilamiyor\n", dest_name); fclose(fsource); exit(EXIT_FAILURE); } printf("%s dosyasi yaratildi!\n", dest_name); while ((ch = fgetc(fsource)) != EOF) { fputc(ch, fdest); byte_counter++; } fclose(fsource); printf("%s dosyasi kapatildi!\n", source_name); fclose(fdest); printf("%s dosyasi kapatildi!\n", dest_name); printf("%d uzunlugunda %s dosyasinin %s isimli kopyasi cikarildi!\n", byte_counter, source_name, dest_name); } return 0;

458

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

fputc bir ilevdir. Ancak stdio.h balk dosyas iinde getc isimli bir de makro tanmlanmtr. Derleyicilerin ou putc makrosuyla ayn isimli bir de ilev tanmlar. Ancak fgetc bir makro deil ilevdir.

fprintf levi
int fprintf(FILE *, const char *, ); Bu ilev tpk printf ilevi gibidir. Ancak ilk parametresi yazma ileminin hangi dosyaya yaplacan belirtir. Dier parametreleri printf ilevin de olduu gibidir. printf ilevi ekrana yazarken, fprintf ilevi birinci parametre deikeninde belirtilen dosyaya yazar. Aadaki rnei inceleyin: #include <stdio.h> #include <stdlib.h> int main() { FILE *f; int i, ch; if ((f = fopen("data.txt", "w")) ==NULL) { printf("cannot open file...\n"); exit(EXIT_FAILURE); } printf("data.txt dosyasi yaratld!\n"); for (i = 0; i < 10; ++i) fprintf(f, "sayi = %d\n", i); fclose(f); printf("data.txt dosyas kapatild!\n"); if ((f = fopen("data.txt", "r")) == NULL) { printf("cannot open file...\n"); exit(EXIT_FAILURE); } printf("data.txt dosyasi okuma amacyla ald!\n"); while ((ch = fgetc(f)) != EOF) putchar(ch); fclose(f); } return 0;

Programn ekran kts: data.txt dosyasi yaratild! data.txt dosyasi kapatild! data.txt dosyasi okuma amacyla ald! sayi = 0 sayi = 1 sayi = 2 sayi = 3 sayi = 4 sayi = 5 sayi = 6 sayi = 7 sayi = 8 sayi = 9

fscanf levi

Dosyadan okuma yapan bir ilevdir. scanf ilevine ok benzer.

459

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Nasl fprintf ilevi, ekrana yazmak yerine, yazma ilemini birinci parametresiyle belirlenen dosyaya yapyorsa, fscanf ilevi de, okumay klavyeden yapmak yerine belirtilen bir dosyadan yapar. fscanf ilevi scanf ilevinden farkl olarak, birinci parametre deikenine FILE trnden bir adres alr. levin bildirimi aadaki gibidir: int fscanf(FILE *, const char *, ); levin geri dn deeri, dosyadan okunarak bellekteki alanlara yazlan deer saysdr. Hibir alana yazma yaplmadysa, ilev 0 deerine geri dner. Eer ilk alana atama yaplamadan dosyann sonuna gelinmise, ya da bir hata olumusa ilev EOF deerine geri dner. kinci parametresine geilecek yazda kullanlacak format karakterleri scanf ilevi ile ayndr. Aadaki program dikkatle inceleyin: #include <stdio.h> #include <stdlib.h> #include <time.h> #define #define SIZE PASS_GRADE 100 60

char *name[50] = {"Ali", "Veli", "Hasan", "Necati", "Aye", "Kaan", "Selami","Salah","Nejla", "Huseyin", "Derya", "Funda", "Kemal", "Burak", "Ozlem", "Deniz", "Nuri", "Metin", "Guray", "Anil", "Umut", "Selda", "Belgin", "Figen", "Korhan", "hsan", "Ufuk", "Necmettin", "Taylan", "Abdullah", "Perihan", "Soner", "Can", "Ata", "Berk", "Melahat", "Zarife", "Yelda", "Ertan", "Mustafa", "Gizem", "Funda", "Aleyna", "Simge", "Damla", "Kaan", "Kerim", "Cumali", "Ferda", "Sami"}; char *fname[30] = {"Aslan", "Ozkan", "Eker", "Ergin", "Serce", "Kaynak", "Acar", "Aymir", "Erdin", "Doganoglu", "Avsar", "Ozturk", "Ylmaz", "Tibet", "Arkn", "Cilasun", "Yildirim", "Demiroglu", "Torun", "Polatkan", "Burakcan", "Kale", "Nergis", "Kayserili", "Duman", "Tansel", "Kurt", "Tonguc", "Melek", "Mungan"}; int main() { FILE *fgrades, *fpass, *ffail; int no_of_lines, k; char name_entry[SIZE]; char fname_entry[SIZE]; int grade; int pass_counter = 0; int fail_counter = 0; srand(time(0)); fgrades = fopen("notlar.txt", "w"); if (fgrades == NULL) { printf("notlar.txt dosyasi yaratilamiyor!\n"); exit(EXIT_FAILURE); } printf("notlar.txt dosyasi yaratildi!\n"); no_of_lines = rand() % 2000 + 1000; for (k = 0; k < no_of_lines; ++k) fprintf(fgrades, "%s %s %d\n", name[rand() % 50], fname[rand() % 30], rand() % 101); printf("notlar.txt dosyasina %d satir kayit yazildi!\n", no_of_lines); fclose(fgrades); printf("notlar.txt dosyasi kapatildi!\n"); fgrades = fopen("notlar.txt", "r"); if (fgrades == NULL) { 460

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

printf("cannot open notlar.txt!\n"); exit(EXIT_FAILURE); } printf("notlar.txt dosyasi acildi!\n"); fpass = fopen("gecen.txt", "w"); if (fpass == NULL) { printf("gecen.txt dosyasi yaratilamiyor!\n"); exit(EXIT_FAILURE); } printf("gecen.txt dosyasi yaratildi!\n"); ffail = fopen("kalan.txt", "w"); if (ffail == NULL) { printf("kalan.txt dosyasi yaratilamiyor!\n"); exit(EXIT_FAILURE); } printf("kalan.txt dosyasi yaratildi!\n"); while (fscanf(fgrades, "%s%s%d", name_entry, fname_entry, &grade) != EOF) { if (grade >= PASS_GRADE) { fprintf(fpass, "%s %s %d\n", name_entry, fname_entry, grade); pass_counter++; } else { fprintf(ffail, "%s %s %d\n", name_entry, fname_entry, grade); fail_counter++; } } fprintf(fpass, "TOPLAM GECEN OGRENCI SAYISI = %d\n", pass_counter); fprintf(ffail, "TOPLAM KALAN OGRENCI SAYISI = %d\n", fail_counter); if (fcloseall() != 3) { printf("dosya kapatilamiyor!\n"); exit(EXIT_FAILURE); } return 0; } Programda her satr rastgele bir isim soyisim ve not deerinden oluan "notlar.txt" isimli bir dosya yaratlyor. Daha sonra bu dosya okuma amacyla alarak, dosyann her satrnda bulunan isim ile soyisimler char trden dizilere, not deerleri ise int trden bir deikene okunuyor. Okunan not deerinin PASS_GRADE deerinden yksek olup olmamasna gre ilgili satr, yazma amacyla alm "gecen.txt" ya da "kalan.txt" isimli dosyalara yazdrlyor. Bylece "notlar.txt" dosyasndan "gecen.txt" ve "kalan.txt" isimli iki farkl dosya oluturuluyor. fscanf ilevinde kullanlan format karakterlerine ilikin nemli bir ayrntya deinelim: Dosyadan yaplan her okumann dntrlerek mutlaka bellekte bir alana yazlmas zorunlu deildir. Boluk karakterleriyle ayrlan bir karakter grubu bir yere atanmadan dosya tampon alanndan karlmak isteniyorsa, format karakter grubunda % karakterinden sonra '*' karakteri kullanlr. rnein bir metin dosyasnda dosya konum gstericisinin gsterdii yerde 1376 ---------4567

gibi bir satrn bulunduunu dnelim. Yaplacak okuma sonucunda yalnzca 1376 ve 4567 deerlerinin x ve y deikenlerine aktarlmas gerektiini dnelim. Bunun iin aadaki gibi bir ar yaplabilir:

461

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

fscanf(f, "%d%*s%d", &x, &y);

fgets levi
char *fgets(char *buf, int n, FILE *f); Bu ilev dosya konum gstericisinin gsterdii yerden bir satrlk bilgiyi okur. lev dosyadan '\n' karakterini okuyunca onu da birinci parametresinde verilen adrese yazarak ilemini sonlandrr. levin birinci parametresi okunacak bilginin bellekte yerletirilecei yerin adresidir. kinci parametresi ise okunacak maksimum karakter saysdr. fgets ilevi en fazla n 1 karakteri okur. Okuduu karakterlerin sonuna sonlandrc karakteri ekler ve ilemini sonlandrr. Eer satr zerindeki karakter says n - 1'den az ise tm satr okur ve ilemini sonlandrr. rnein bu parametrenin 10 olarak girildiini dnelim. Satr zerinde 20 karakter olsun. lev 9 karakteri okuyarak diziye yerletirir, sonuna sonlandrc karakteri ekler. Ancak satr zerinde \n dahil olmak zere 5 karakter olsayd ilev bu 5 karakteri de okuyarak sonuna da sonlandrc karakteri ekleyerek ilemini sonlandrrd. levin ikinci parametresine char trden bir dizinin ya da dinamik olarak elde edilen bir bloun boyutunu gemek tama hatalarn dorudan engeller. Zira fgets ilevi en fazla, dizinin boyutundan bir eksik sayda karakteri okuyarak diziye yazar, dizinin son elemanna da sonlandrc karakterin deerini yazar. levin geri dn deeri, en az 1 karakter okunmu ise birinci parametresi ile belirtilen adresin ayns, hibir karakter okunmamsa NULL adresidir. Bir dng iinde fgets ilevi srekli olarak arlarak btn dosya okunabilir. fgets ilevi ile bir dosyay satr satr ekrana yazdran aadaki program inceleyin: #include <stdio.h> #include <stdlib.h> #define #define MAX_FILE_NAME_LEN BUFFER_SIZE 256 100

int main() { FILE *f; char file_name[MAX_FILE_NAME_LEN]; char buf[BUFFER_SIZE]; printf("Dosya ismi : "); gets(file_name); if ((f = fopen(file_name, "r")) == NULL) { printf("cannot open the file %s\n", file_name); exit(EXIT_FAILURE); } while (fgets(buf, BUFFER_SIZE, f) != NULL) printf("%s", buf); fclose(f); return 0; } while (fgets(buf, BUFFER_SIZE, f) != NULL) printf("%s", buf); Dngsyle f ile gsterilen dosyadan satr satr okuma yaplarak okunan karakterler buf dizisine yazlyor. Eer dosyadan okunacak bir karakter kalmadysa fgets ilevi NULL adresine geri dner.

462

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

levin bildirimi aadaki gibidir: int fputs(const char *str, FILE *stream); lev, birinci parametresine geilen adresteki yazy ikinci parametresine geilen FILE trnden adresle ilikilendirilen dosyaya yazar. Yaznn sonunda yer alan sonlandrc karakter ilev tarafndan dosyaya yazlmaz. lev, yazma ileminde baarsz olursa EOF deerine geri dner. Baar durumunda ilevin geri dn deeri negatif olmayan bir tamsaydr. Aadaki programda, fputs ve fgets ilevleri kullanlarak bir metin dosyasnn kopyas karlyor: #include <stdio.h> #include <stdlib.h> #define FILE_NAME_LEN #define BUFFER_SIZE 256 100

fputs levi

int main() { char source_file_name[FILE_NAME_LEN]; char dest_file_name[FILE_NAME_LEN]; char buffer [BUFFER_SIZE]; FILE *fs, *fd; printf("kopyalanacak dosya ismi: "); gets(source_file_name); printf("kopya dosya ismi: "); gets(dest_file_name); fs = fopen(source_file_name, "r"); if (fs == NULL) { printf("%s dosyasi acilamiyor!\n", source_file_name); exit(EXIT_FAILURE); } fd = fopen(dest_file_name, "w"); if (fd == NULL) { printf("%s dosyasi yaratilamiyor!\n", dest_file_name); fclose(fd); exit(EXIT_FAILURE); } while (fgets(buffer, BUFFER_SIZE, fs)) fputs(buffer, fd); printf("kopyalama basari ile tamamlandi!\n"); fclose(fs); fclose(fd); return 0; }

Bir dosya text modunda ya da binary modda alabilir. Varsaylan (default) a modu text modudur. Yani dosyann hangi modda ald ak bir ekilde belirtilmezse dosyann text modunda ald varsaylr. Dosyay binary modda aabilmek iin a mod yazsna 'b' eklemek gerekir. Aada bir dosyay binary modda aabilmek iin fopen ilevine gnderilebilecek geerli dizgeler veriliyor: "rb", "r+b", "rb+", "w+b", "wb+","a+b", "ab+"

Metin Dosyalar ve kilik Dosyalar

463

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DOS ve WINDOWS gibi baz iletim sistemlerinde bir dosyann text modu ya da binary modda almas arasnda baz nemli farklar vardr: DOS iletim sisteminde bir dosya yazdrldnda bir karakter aa satrn banda grnyorsa bunu salamak iin o karakterden nce CR (carriage return) ve LF (line feed) karakterlerinin bulunmas gerekir. CR karakteri C'de '\r' ile belirtilir. 13 numaral ASCII karakteridir. LF karakteri C'de \n ile belirtilir. 10 numaral ASCII karakteridir. rnein bir dosya yazdrldnda grnt a b eklinde olsun. Dosyadaki durum a\r\nb eklindedir. Oysa UNIX tabanl sistemlerinde aa satrn bana geebilmek iin sadece LF karakteri kullanlr. UNIX iletim sisteminde a b grntsnn dosya karl a\nb biimindedir. DOS iletim sisteminde LF karakteri bulunulan satrn aasna ge CR karakteri ise bulunulan satrn bana ge anlamndadr. rnein DOS iletim sisteminde bir bir dosyann ierii a\nb biiminde ise dosya yazdrldnda a b grnts elde edilir. Eer dosyann ierii a\rb biiminde ise dosya yazdrldnda b grnts elde edilir. printf levinda \n ekranda aa satrn bana geme amacyla kullanlr. Aslnda printf ilevinin 1. parametresi olan dizgenin iine \n yerletirildiinde UNIX'de yalnzca \ni DOS iletim sisteminde ise \r ve\n ile bu gei salanr. Text dosyalar ile rahat alabilmek iin dosyalar text ve binary olarak ikiye ayrlmtr. Bir dosya text modunda aldnda dosyaya \n karakteri yazlmak istendiinde dosya ilevleri otomatik olarak \r ve \n karakterlerini dosyaya yazar. Benzer bir biimde dosya text modda almsa dosya gstericisi \r\n iftini gsteriyorsa dosyadan yalnzca /n karakteri okunur. DOS iletim sisteminde text ve binary dosyalar arasndaki baka bir fark da, CTRL Z (26 numaral ASCII karakterinin) dosyay sonlandrdnn varsaylmasdr. Oysa dosya binary modda aldnda byle bir varsaym yaplmaz. UNIX iletim sisteminde, text modu ile binary mod arasnda hibir fark yoktur. Yani UNIX iletim sisteminde dosyann binary mod yerine text modunda almasnn bir sakncas olmaz. Ancak DOS altnda text dosyas olmayan bir dosyann binary mod yerine text modunda almasnn sakncalar olabilir. rnein DOS altnda bir exe dosyann binary mod yerine text modunda aldn dnelim. Bu dosyada 10 numaral ve 13 numaral ASCII karakterleri yanyana bulunduunda dosyadan yalnzca 1 byte okunur. Ayn ekilde dosyadan 26 numaral ASCII karakteri okunduunda dosyadan artk baka bir okuma yaplamaz. Dosyann sonuna gelindii varsaylr. Aadaki program text modu ile binary mod arasndaki fark gsteriyor:

464

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> int main() { FILE *fp; int k, ch; fp = fopen("deneme", "w"); if (fp == NULL) { printf("dosya alamyor\n"); exit(EXIT_FAILURE); } /* dosyaya 5 tane \n karakteri yazdrlyor */ for (k = 0; k < 5; ++k) fputc('\n', fp); fclose(fp); printf("\ndosya binary modda alarak yazdrlyor\n"); fp = fopen("deneme", "rb"); if (fp == NULL) { printf("dosya alamyor\n"); exit(EXIT_FAILURE); } while ((ch = fgetc(fp)) != EOF) printf("%d ", ch); /* ekran kts 13 10 13 10 13 10 13 10 13 10 */ fclose(fp); printf("\ndosya kapatld. imdi dosya text modunda alarak yazdrlyor .\n"); fp = fopen("deneme", "r"); if (fp == NULL) { printf("dosya alamyor\n"); exit(EXIT_FAILURE); } while ((ch = fgetc(fp)) != EOF) printf("%d ", ch); /* ekran kts 10 10 10 10 10 */ fclose(fp); /* simdi '\x1A' karakterinin text modunda dosyay sonlandrmas zellii snanyor*/ fp = fopen("deneme", "w"); if (fp == NULL) { printf("dosya alamyor\n"); exit(EXIT_FAILURE); } /* dosyaya 5 tane 'A' karakteri yazdrlyor */ for (k = 0; k < 5; ++k) fputc('A', fp); /* dosyaya '\x1A' karakteri yazdrlyor */ fputc('\x1a', fp); /* dosyaya 10 tane 'A' karakteri yazdrlyor. */ for (k = 0; k < 5; ++k) fputc('A', fp); fclose(fp); printf("\ndosya binary modda alarak yazdrlyor :\n"); fp = fopen("deneme", "rb");

465

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if (fp == NULL) { printf("dosya alamyor\n"); exit(EXIT_FAILURE); } while ((ch = fgetc(fp)) != EOF) printf("%d ", ch); /* ekran kts 65 65 65 65 65 26 65 65 65 65 65 */ printf("\ndosya kapatld, imdi dosya text modunda alarak yazdrlyor\n"); fp = fopen("deneme", "r"); if (fp == NULL) { printf("dosya alamyor\n"); exit(EXIT_FAILURE); } while ((ch = fgetc(fp)) != EOF) printf("%d ", ch); /* ekran kts 65 65 65 65 65 26 65 65 65 65 65 */ fclose(fp); } return 0;

Dosyann sonunda hibir zel karakter yoktur. letim sistemi dosyann sonuna gelinip gelinmediini dosyann uzunluuna bakarak anlayabilir. EOF (end of file) durumu dosya konum gstericisinin dosyada olmayan son karakteri gstermesi durumudur. EOF durumunda dosya konum gstericisinin offset deeri dosya uzunluu ile ayn deerdedir. EOF durumunda dosyadan okuma yaplmak istenirse dosyadan okuma yapan ilevler baarsz olur. Ancak a modu uygunsa dosyaya yazlabilir ve bu durumda dosyaya ekleme yaplr. Daha nce belirtildii gibi C dilinde alan bir dosya ile ilgili bilgiler FILE trnden bir yap nesnesi iinde tutulur. Bu yapnn elemanlar dosyann zellikleri hakknda bilgi verir. C programcs bu yapnn elemanlarnn deerleri ile dorudan ilgilenmez, zira fopen ilevinin geri dn deeri bu yap nesnesini gsteren FILE yaps trnden bir gstericidir ve C dilinin dosyalarla ilgili ilem yapan ilevleri ounlukla bu adresi parametre olarak alarak, istenilen dosya ile ilgili bilgilere ular. Sz konusu FILE yapsnn elemanlarndan biri de, bayrak olarak kullanlan EOF bayradr. Aslnda derleyicilerin ounda int trden bir bayran yalnzca belirli bir bitidir. C dilinin dosyalarla ilgili ilem yapan baz ilevleri EOF bayrann deerini deitirir. Yani EOF bayran birler ya da sfrlarlar. Okuma yapan ilevler okumadan nce bu bayran deerine bakar. EOF bayra set edilmise okuma baarl olmaz. Baarl bir okuma yaplabilmesi iin EOF bayrann yeniden sfrlanmas gerekir. Dosya aan ilevler FILE yapsndaki EOF bayran sfrlar. Bu ilevler dnda dosya konum gstericisinin deerini deitiren ilevler (fseek, rewind, fsetpos) ile clearerr ilevleri de EOF bitini sfrlar.

EOF Durumu

Bir dosyaya int trden deerlerin yazlacan dnelim. Dosyaya int trden bir deer yazmak ne anlama gelir? rnein int trnn 4 byte olduu bir sistemde, dosyaya yazlacak tamsay deeri 1234567890 olsun. Bu deer bir dosyaya yazlmak ve o dosyadan daha sonra geri okunmak istensin. Bu ilem fprintf ilevi ile yaplabilir, deil mi?

Formatl ve Formatsz Yazm

466

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

f yazma amacyla alm bir dosyann gstericisi olmak zere fprintf(f, "%d", 1234567890); Yukardaki aryla dosya konum gstericisinin gsterdii yerden balanarak dosyaya 10 byte eklenmi olur. Dosyaya yazlan byte'lar 1234567890 saysnn basamak deerlerini gsteren rakamlarn sra numaralardr. Sistemde ASCII karakter kodlamasnn kullanldn dnelim. Dosyaya aadaki byte'lar yazlmtr: 49 50 51 42 53 54 55 56 57 59 48 Oysa 32 bitlik bir sistemde 1234567890 gibi bir deer 4 byte'lk alanda ifade edilir deil mi? rnein 1234567890 saysnn bellekteki grnts aadaki gibidir: 0100 1001 1001 0110 0000 0010 1101 0010 Bu 4 byte RAM'de olduu gibi dosyaya yazlamaz m? te dosyaya bu ekilde yazm formatsz yazmdr. C'nin iki nemli standart ilevi RAM'den dosyaya dosyadan RAM'e belirli sayda byte' formatsz biimde aktarr. imdi bu ilevleri inceleyeceiz:

fread ve fwrite levleri

Bu iki ilev C'de en ok kullanlan ilevlerdir. Genel olarak dosya ile RAM arasnda aktarm (transfer) yaparlar. Her iki ilevin de bildirimi ayndr: size_t fread(void *adr, size_t block_size, size_t n_blocks, FILE *); size_t fwrite(const void *adr, size_t block_size, size_t n_blocks, FILE *); size_t trnn sistemlerin hemen hemen hepsinde unsigned int ya da unsigned long trnn typedef edilmi yeni ismi olduunu anmsayn. fread ilevi dosya konum gstericisinin gsterdii yerden, ikinci ve nc parametresine kopyalanan deerlerin arpm kadar byte' , bellekte birinci parametresinin gsterdii adresten balayarak kopyalar. Genellikle ilevin ikinci parametresi veri yapsnn bir elemannn uzunluunu, nc parametresi ile para says biiminde girilir. levin geri dn deeri bellee yazlan ya da bellekten dosyaya yazlan para saysdr. Bu ilevler sayesinde diziler ve yap nesneleri tek bir ar ile bir dosyaya aktarlabilirler. rnein 10 elemanl int trden bir dizi aadaki gibi tek bir aryla dosyaya yazlabilir. int a[5] = {3, 4, 5, 7, 8}; fwrite (a, sizeof(int), 5, f); Yukardaki rnekte, dizi ismi olan a int trden bir adres bilgisi olduu iin, fwrite ilevine 1. argman olarak gnderilebilir. FILE trnden f gstericisi ile ilikilendirilen dosyaya bellekteki a adresinden toplam sizeof(int) * 5 byte yazlr. Aadaki kod parasnda bir yap nesnesi bellekten dosyaya aktarlyor:

467

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

typedef struct { char name[20]; char fname[20]; int no; }Person; int main() { FILE *f; Person per = {"Necati", "Ergin", 325); f = fopen("person.dat", "wb"); /*********************************/ fwrite(&per, sizeof(Person), 1, f); /****/

fwrite ilevi saylar bellekteki grnts ile dosyaya yazar. Yani fprintf ilevi gibi formatl yazmaz. rnein DOS iletim sisteminde: int i = 1535; fwrite(&i, sizeof(int), 1, f); Burada dosya yazdrlrsa 2 byte uzunluunda rastgele karakterler grnr. nk DOS iletim sisteminde int tr 2 byte uzunluundadr. Bizim grdmz ise 1525'in rastgele olan byte'lardr. Bilgileri ASCII karlklar ile dosyaya yazmak iin fprintf ilevi kullanlabilir. fread ve fwrite ilevleri bellekteki bilgileri aktardna gre dosyalarn da binary modda alm olmas uygun olur. #include <stdio.h> #include <stdlib.h> int main() { FILE *f; int i; int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; int b[10]; if ((f = fopen("data", w+b")) == NULL) { printf("cannot open file...\n"); exit(EXIT_FAILURE); } fwrite (a, sizeof(int), 10, f); fseek(f, 0, SEEK_SET); fread(b, sizeof(int), 10, f); for (i = 0; i < 10; ++i) printf("%d\n", b[i]); fclose(f); return 0; } fread ve fwrite ilevlerinin geri dn deerleri nc parametresi ile belirtilen okunan ya da yazlan para saysdr. rnein n = fread(a, sizeof(int), 10, f);

468

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ars ile f ile gsterilen dosyadan sizeof(int) * 10 kadar byte okunarak RAM'da a adresine yazlmak istenmitir. Yani dosya konum gstericisinin gsterdii yerden itibaren btn saylar okunabildiyse ilev 10 deerine geri dner. Eer dosyadaki kalan byte says okunmak istenen saydan az ise ilev btn byte'lar okur ve geri dn deeri okunan byte says 2. parametresi ile belirtilen deer olur. rnein DOS iletim sistemi altnda alyor olalm. Dosyada 10 byte bilgi kalm olsun. n = fread(a, sizeof(int), 10, f); ile ilev 5 deerine geri dner. Aadaki iki ary inceleyelim: fread(str, 100, 1, f); fread(str, 1, 100, f); Her iki ilev ars da RAM'deki str adresine FILE trnden f gstericisi ile ilikilendirilen dosyadan 100 byte okumak amacyla kullanlabilir. Ancak birinci arda geri dn deeri ya 0 ya 1 olabilecekken, ikinci ilev arsnda geri dn deeri 0 100(dahil) aralnda herhangi bir deer olabilir.

fread ve fwrite levleriyle Blok Blok Kopyalama

Aadaki rnekte bir grup byte fread ilevi ile bir dosyadan okunuyor, fwrite ilevi ile dier bir dosyaya yazlyor:

469

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #define #define BLOCK_SIZE MAX_NAME_LEN 1024 80

int main() { FILE *fs, *fd; char source_file_name[MAX_NAME_LEN]; char dest_file_name[MAX_NAME_LEN]; unsigned int n; unsigned char buf[BLOCK_SIZE]; printf("kaynak dosya ismini giriniz : "); gets(source_file_name); printf("yeni dosya ismini giriniz : "); gets(dest_file_name); if ((fs = fopen(source_file_name, "rb")) == NULL) { printf("%s dosyas alamyor!\n", source_file_name); exit(EXIT_FAILURE); } printf("%s dosyas ald!\n", source_file_name); if ((fd = fopen(dest_file_name, "wb")) == NULL) { printf("%s dosyas yaratlamyor!\n", source_file_name); fclose(fs); exit(EXIT_FAILURE); } printf("%s dosyas yaratlamyor!\n", dest_file_name); while ((n = fread(buf, 1, BLOCK_SIZE, fs)) != 0) fwrite(buf, 1, n, fd); fclose(fs); printf("%s dosyas kapatld!\n", source_file_name); fclose(fd); printf("%s dosyas kapatld!\n", dest_file_name); printf("kopyalama baaryla tamamland\n"); } return 0;

Yukar program inceleyin. Kopyalama aadaki dng deyimiyle yaplyor: while ((n = fread(buf, 1, BLOCK_SIZE, fs)) fwrite(buf, 1, n, fd); != 0)

fread ilevi ile fs ile gsterilen dosyadan, 1 byte'lk bloklardan BLOCK_SIZE kadar okunmaya allyor. fread ilevinin geri dn deeri n isimli deikende saklanyor. n deikenine atanan deer 0 olmad srece dngnn devam salanyor. Baka bir deyile while dngs dosyadan en az 1 byte okuma yaplabildii srece dnyor. while dngsnn gvdesinde yer alan fwrite ilevi ars ile kaynak dosyadan okunan n byte hedef dosyaya yazlyor. imdi de aadaki program inceleyin. Bu programda komut satrndan girilen iki deer arasndaki tm asal saylar, ismi komut satrndan ismi girilen dosyaya formatsz olarak yazlyor. Program komut satrndan aadaki gibi altrlabilir: <asalyaz> <deger1> <deger2> <dosya ismi> #include <stdio.h> #include <stdlib.h>

470

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <string.h> int isprime(int val); int main(int argc, char *argv[]) { int lower_bound, upper_bound; int k, temp; int prime_counter = 0; FILE *f; if (argc != 4) { printf("<asalyaz> <deger1> <deger2> <dosya ismi>\n"); exit(EXIT_FAILURE); } lower_bound = atoi(argv[1]); upper_bound = atoi(argv[2]); if (lower_bound > upper_bound) { temp = lower_bound; lower_bound = upper_bound; upper_bound = temp; } f = fopen(argv[3], "wb"); if (f == NULL) { printf("%s dosyasi yaratilamiyor!\n", argv[3]); exit(EXIT_FAILURE); } printf("%s dosyasi yaratildi!\n", argv[3]); for (k = lower_bound; k <= upper_bound; ++k) if (isprime(k)) { fwrite(&k, sizeof(int), 1, f); prime_counter++; } printf("%s dosyasina %d adet asal sayi yazildi!\n", argv[3], prime_counter); fclose(f); printf("%s dosyasi kapatildi!\n", argv[3]); } return 0;

Okuma ya da yazma yapan ilevler, okuma ya da yazma ilemini dosya konum gstericisinin deeri olan konumdan yapar. Bir dosyann istenilen bir konumundan okuma ya da yazma yapabilmek iin nce dosya konum gstericisi konumlandrlmaldr. Dosya konum gstericisi standart C ilevleri ile konumlandrlabilir:

Dosya Konum Gstericisi le lgili levler

fseek levi

Bu ilev dosya konum gstericisini istenilen bir offset deerine konumlandrmak amacyla arlr. Bu ilevin arlmasyla, alm bir dosyann istenilen bir yerinden okuma yapmak ya da istenilen bir yerine yazmak mmkn hale gelir. levin bildirimi aadaki gibidir: int fseek(FILE *f, long offset, int origin);

471

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

levin birinci parametresi hangi dosyann dosya konum gstericisinin konumlandrlacan belirler. levin ikinci parametresi konumlandrma ileminin yaplaca offset deeridir. levin nc parametresi konumlandrmann hangi noktadan itibaren yaplacan belirler. Bu parametreye stdio.h iinde bildirilen SEEK_SET, SEEK_CUR, SEEK_END simgesel deimezlerden biri geilmelidir. Derleyiciler bu simgesel deimezleri aadaki gibi tanmlar: #define SEEK_SET #define SEEK_CUR #define SEEK_END 0 1 2

Son parametreye SEEK_SET deeri geilirse, konumlandrma dosya bandan itibaren yaplr. Bu durumda ikinci parametre >= 0 olmaldr. rnein: fseek(f, 10L, SEEK_SET);

ile dosya gstericisi 10. offsete konumlandrlr. Ya da fseek(f, 0L, SEEK_SET); levin nc parametre deikenine geilen deer SEEK_CUR ise, 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 gelir. rnein dosya gstericisi 10. byte' gsteriyor olsun. fseek(f, -1, SEEK_CUR); ars ile dosya gstericisi 9. offset e konumlandrlr. levin nc parametre deikenine geilen deer SEEK_END ise konumlandrma EOF durumundan itibaren yani dosya sonundan itibaren yaplr. Bu durumda ikinci parametreye geilen deer <= 0 olmaldr. rnein dosya gstericisini dosyann sonuna konumlandrmak iin fseek(f, 0, SEEK_END); arsn yapmak gerekir. fseek(f, -1, SEEK_END); ars ile dosya konum gstericisi son karakterin bulunduu yere ekilir. levin geri dn deeri ilemin baars hakknda bilgi verir. Geri dn deeri 0 ise ilem baarl, 0 d bir deer ise ilem baarszdr. Yalnzca sorunlu durumlarda geri dn deerinin snanmas salk verilir. Yazma ve okuma ilemleri arasnda dosya gstericisinin bir konumlandrma ilevi ile konumlandrlmas gerekir. Ya da fflush ilevine yaplan ar ile dosya tampon alan boaltlmaldr. Gerekirse bo bir fseek ars ile, konumlandrma bulunulan yere yaplabilir: fseek(0, 0L, SEEK_CUR). rnein dosyadan bir karakter okunup, bir sonraki karaktere okunmu karakterin 1 fazlas yazlacak olsun: ch = fgetc(f); fputc(ch + 1, f);

472

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ilemi hataldr. Durum alma zamanna ilikin tanmlanmam bir davran (undefined behaviour) zellii gsterir. Yani alma zamannda herey olabilir. Yazmadan okumaya, okumadan yazmaya geite dosya gstericisi konumlandrlmaldr. Bu durumun tek istisnas, son yaplan okuma ile dosyann sonuna gelinmesi durumudur. Bu durumda konumlandrma yaplmadan dosyann sonuna yazlabilecei gvence altndadr.

Bu ilev ile dosya konum gstericisi dosyann bana konumlandrlr. levin bildirimi aadaki gibidir: void rewind(FILE *fp); rewind(f); ars ile (void) fseek(f, 0L, SEEK_SET); ars ayn anlamdadr.

rewind levi

Bu ilev dosya konum gstericisinin deerini elde etmek iin arlr. levin bildirimi aadaki gibidir: long ftell(FILE *); levin geri dn deeri dosya konum gstericisinin deeridir. Bu deer dosyann bandan itibaren kanc byte olduu bilgisi olarak verilir. Bir hata durumunda ilev -1L deerine geri dner. kili (binary) bir dosyann uzunluu fseek ve ftell ilevlerine yaplan arlarla elde edilebilir: fseek(f, 0, SEEK_END); length = ftell(f);

ftell levi

Bu ilevler, dosya konum gstericisinin deerini elde etmek ya da deerini deitirmek iin, birbirleriyle ilikili olarak kullanlr. Bu ilevlerle dosya iindeki bir nokta iaretlenerek, daha sonra okuma ya da yazma amacyla dosya konum gstericisi ayn noktaya konumlandrlabilir. fgetpos ilevi ile dosya konum gstericisinin deeri elde edilir, daha sonra bu konuma geri dnebilmek iin fsetpos ilevi kullanlr. int fgetpos(FILE *, fpos_t *pos); lev birinci parametresine geilen adresle ilikilendirilen dosyann dosya konum gstericisinin deerini elde ederek ikinci parametresine geilen adrese yazar. fpos_t tr stdio.h balk dosyas iinde bildirilen standart bir typedef trdr. Bu tr yalnzca fgetpos ile fsetpos ilevlerinde kullanlr. levin geri dn deeri ilemin baarsn gsterir. lev, baar durumunda 0 deerine, baarszlk durumunda sfrdan farkl bir deere geri dner. int fsetpos(FILE *, const fpos_t *pos); lev, birinci parametresine geilen adresle ilikilendirilen dosyann dosya konum gstericisini ikinci parametresine geilen adresten okunan deere konumlandrr. levin ikinci parametresine fgetpos ilevinden elde edilen bir deeri tayan nesnenin adresi geilmelidir.

fgetpos ve fsetpos levleri

473

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

zellikle byk dosyalar sz konusu olduunda fsetpos/fgetpos ilevleri fseek ilevine tercih edilmelidir. nk fseek ilevinin ikinci parametresi long trden iken, fgetpos/fsetpos ilevlerinde kullanlan fpos_t tr ok byk dosyalarn konum bilgilerini tutabilecek byklkte olan, isel olarak tanmlanm bir yap tr olabilir.

Dosyalarla lgili lem Yapan Dier Standart levler remove levi


Bu ilev bir dosyay siler. levin bildirimi int remove (const char *filename); biimindedir. leve argman olarak silinecek dosyann ismi gnderilir. levin geri dn deeri, dosyann baarl bir ekilde silinebilmesi durumunda 0, aksi halde yani dosya silinememise sfr d bir deerdir. Ak olan bir dosyann silinebilmesi sisteme bal olduundan, yazlan kodun tanabilirlii asndan, silinecek bir dosya ak ise nce kapatlmaldr.

Bu ilev bir dosyann ismini deitirmek iin kullanlr. levin bildirimi: int rename (const char *old, const char *new); biimindedir. leve birinci argman olarak dosyann eski ismi ikinci argman olarak ise dosyann yeni ismi gnderilmelidir. levin geri dn deeri, isim deitirmen ileminin baarl olmas durumunda 0, aksi halde yani dosyann ismi deitirilemmesi durumunda 0 d bir deerdir. Sistemlerin ounda ak olan bir dosyann isminin deitirilmeye allmas durumunda ilev baarsz olur ve 0 d bir deere geri dner.

rename levi

tmpfile levi

lev geici bir dosya amak amacyla kullanlr. levin bildirimi : FILE * tmpfile(void); tmpfile ilevi at geici dosyay "wb" modunda aar. Alan dosya fclose ilevi ile kapatldnda ya da dosya kapatlmazsa program sona erdiinde otomatik olarak silinir. levin geri dn deeri, alan geici dosya ile iliki kurulmasna yarayacak, FILE yaps trnden bir adrestir. Herhangi bir nedenle dosya geici dosya alamyorsa ilev NULL adresine geri dner. stdio.h balk dosyas iinde tanmlanan TMP_MAX simgesel deimezi tmpfile ileviyle yaratlabilecek maksimum geici dosya saysn gsterir. Yazlan bir kaynak kodda, ayn anda alm olan geici dosyalarn says TMP_MAX deerinden daha byk olmamaldr.

tmpnam levi

Geici olarak kullanlacak bir dosya iin bir isim retilmesi amacyla kullanlr. levin stdio.h balk dosyas iindeki bildirimi: char *tmpnam(char *s); biimindedir. lev, rettii dosya ismini kendisine gnderilen char trden adrese yazar. lev, ayn zamanda ayn dosya ismini statik mrl bir dizinin iine yazarak, bu dizinin balang adresini geri dndrr. Eer ileve argman olarak NULL adresi gnderilirse, ilev yalnzca statik mrl dizinin adresinin dndrr. leve char trden bir dizinin

474

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

adresi gnderildiinde bu dizinin boyutu ne olmaldr? Baka bir deyile tmpnam ilevi ka karakter uzunluunda bir dosya ismi retir? te bu deer stdio.h dosyas iinde tanmlanan L_tmpnam simgesel deimeziyle belirtilir. tmpnam ilevinin rettii dosya isminin allan dizin iinde daha nce kullanlmayan bir dosya ismi olmas gvence altna alnmtr. Yani retilen dosya ismi bulunulan dizin iinde tektir. Bir programda daha sonra silmek zere bir dosya alacan ve bu dosyaya birtakm bilgilerin yazlmasndan sonra dosyann silineceini ya da dosyaya baka bir isim verileceini dnelim. Bu durumda ilgili dosya yazma modunda alacana gre, bu dosyaya var olan bir dosyann ismi verilmemelidir. Eer var olan bir dosyann ismi verilirse, var olan dosya sfrlanaca iin dosya kaybedilir. Bu riske girmemek iin, geici olarak kullanlacak dosya tmpfile ilevi kullanlarak almaldr. Ancak tmpfile ilevinin kullanlmas durumunda, alan dosya kalc hale getirilemez. Yani herhangi bir nedenden dolay geici dosyann silinmemesi istenirse, dosya kalc hale getirilmek istenirse dosya fopen ileviyle almaldr. te bu durumda geici dosya baka bir dosyay riske etmemek iin tmpnam ilevinin rettii isim ile almaldr. Peki tmpnam ileviyle en fazla ka tane gvenilir dosya ismi retilebilir? te bu deer stdio.h iinde tanmlanan TMP_MAX simgesel deimezi ile belirlenmitir.

C dilinde baz giri ve k birimleri (klavye, ekran gibi) dorudan bir dosya gibi ele alnr. Herhangi bir giri k birimini akm (stream) olarak isimlendirir. Bir akm, bir dosya olabilecei gibi, dosya olarak ele alnan bir giri k birimi de olabilir. rnein kk programlar genellikle girdilerini genellikle tek bir akmdan alp (rnein klavyeden) ktlarn da tek bir aka (rnein ekrana) iletir.

Akmlar

Bir programa yaplan girdilerin programa doru akan bir byte akmndan (input stream) geldii kabul edilir. Yine bir programn kts da programdan darya doru akan byte lar olarak (output stream) dnlr. Dosyalarla ilgili okuma yazma yapan ilevler dorudan giri akmndan okuma yapp, k akmna yazabilirler. Bir C program altrldnda 3 akm gsteren dosyann otomatik olarak ald kabul edilir. Bu akmlar birer dosya olarak kullanlabilirler ve nceden tanmlanm FILE * trnden deerlerle ilikilendirilmilerdir: stdin : Standart giri birimini temsil eder. Bu akm normal olarak klavyeye balanmtr. stdout : Standart k akmn temsil eder. Bu akm normal olarak ekrana balanmtr. stderr : Standart hata akmn temsil eder. Bu akm da normal olarak ekrana balanmtr. Daha nce klavyeden girdi alan ilevler (getchar, gets, scanf) olarak rendiimiz ilevler aslnda stdin akmndan okuma yapan ilevlerdir. Daha nce ekrana yazan ilevler (putchar, puts, printf) olarak rendiimiz ilevler aslnda stdout akmna yazan ilevlerdir. Dosyadan okuma yapan ilevlere stdin, yazma yapan ilevlere ise stdout ve stderr FILE * trnden deerler olarak geilebilir. rnein fprintf(stdout, "Necati Ergin"); ars ekrana Necati Ergin yazsn yazdrr. Benzer biimde fputc('A', stdout); Ekrana 'A' karakteri bastrr. fscanf(stdin, "%d", &val);

stdout stdin ve stderr Akmlar

475

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ars klavyeden alnan deeri val deikenine atar.

letim sistemlerinin ou giri ve k akmlarnn baka dosyalara ynlendirilmesine izin verir. rnein ismi asalyaz olan bir programn altrldnda ekrana 1 - 1000 aralnda asal saylar yazdrdn dnelim. Programn kaynak kodunda yazdrma ilemi iin printf ilevi arlm olsun. Bu program DOS / WINDOWS iletim sistemlerinde komut satrndan bu program asalyaz biiminde altrldnda ktsn ekrana yazar. Komut satrndan asalyaz program, asalyaz > asal.txt biiminde altrlrsa axsalyaz programnn stdout akmna yani ekrana gnderdii her ey asal.txt dosyasna yazlr. Burada kullanlan '>' simgesine "ynlendirme" (redirection) simgesi denir. Benzer biimde '<' ynlendirme simgesi de stdin akm iin kullanlr. rnein, ismi process olan bir program process < numbers.txt biiminde altrlrsa, normal olarak klavyeden alnacak her bilgi numbers.txt dosyasndan alnr. Her iki ynlendirme simgesi de bir arada kullanlabilir: process < numbers.txt > prime.txt process ilevi yukardaki gibi altrldnda girdisini numbers.txt dosyasndan alacak ktsn ise prime.txt dosyasna yazar. Bunlarn dnda UNIX iletim sisteminde '|' biiminde baka bir ynlendirme simgesi daha vardr. x | y gibi bir ilemde x ve y iki program olmak zere, bu iki program ayn anda altrlr, x programnn ekrana yazd her ey y programnda klavyeden giriliyormu gibi ilem grr. Bu ynlendirme ilemine pipe ad verilir. stderr dosyas da normal olarak ekrana ynlendirilir. Yani standart fprintf ilevi ile stderr dosyasna bir yazma ilemi yaplrsa, yazlanlar yine ekrana kar. Ancak stdout akm baka bir dosyaya ynlendirilirse, stderr akm ynlendirme ileminden etkilenmez, halen ekrana bal kalr. Yani komut satrndan ynlendirme yaplm olsa bile fprintf(stderr, "hata!\n"); gibi bir ar sonucunda "Hata" yazs, dosyaya deil ekrana yazlr. Hata iletileri printf ilevi ile ekrana yazdrlmak yerine fprintf ilevi ile stderr akmna yazdrlmaldr.

Akmlarn Ynlendirilmesi

fgets levinin stdin Deeri le arlmas:

fgets ilevinin nc parametresin FILE * trnden bir deer istediini biliyorsunuz:

476

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

fgets(char *str, int n, FILE *f); levin bu parametresine stdin deeri geildiinde, diziye yerletirilecek satr klavyeden alnr. Byle bir ar gets ilevine yaplan bir arya tercih edilmelidir, nk gsterici hatas riski ortadan kaldrlm olur. char str[20]; gets(s); Yukarda gets ilevine yaplan arda, klavyeden 20 ya da daha fazla karakter girilmesi bir gsterici hatas olumasna neden olur. Ancak ar fgets(str, 20, stdin); biiminde yapldnda diziye en fazla 19 karakter yazlr. Yani gsterici hatas olumas riski yoktur. Yalnz burada dikkat edilmesi gereken nokta fgets ileviyle diziye '\n' karakterinin de yazlmas olasldr. Aadaki kodu inceleyin: #include <stdio.h> #include <string.h> int main() { char str[20]; printf("bir isim giriniz : "); fgets(str, 20, stdin); if (!strcmp(str, "NECATI")) printf("esit\n"); else printf("esit degil\n"); } return 0;

Yukardaki program altrldnda klavyeden NECATI ismi girildiinde ekrana "eit degil" yazs yazdrlr. Zira fgets ilevi NECATI girii yapldktan sonra girilen newline karakterini de diziye yerletirir. Yani aslnda strcmp ilevi N E C A T I N E C A T I \n yazlarn karlatrr. Karlatrma ileminden nce aadaki gibi bir if deyimi kullanlabilirdi: if ((ptr = strchr(str, '\n')) != NULL) *ptr = '\0'; Yukardaki deyimde strchr ileviyle str adresindeki yaznn iinde '\n' karakteri aranyor. Yaznn iinde '\n' karakteri bulunursa, bu karakterin yerine sonlandrc karakter yazlyor. Yani yaznn sonunda '\n' karakteri varsa yazdan siliniyor.

freopen levi

Bu ilev daha nce alm bir dosyay, kendi at dosyaya ynlendirir. levin bildirimi:

477

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

FILE *freopen(const char *filename, const char *mode, FILE *stream); biimindedir. Uygulamalarda daha ok standart dosyalarn (stdin, stdout, stderr) baka dosyalara ynlendirilmesinde kullanlr. rnein bir programn ktlarnn data.dat isimli dosyaya yazlmas istenirse : if (freopen("data.dat", "w", stdout) == NULL) { printf("data.dat dosyas alamyor\n"); exit(EXIT_FAILURE); } Yukardaki ilev arsyla stdout dosyasnn ynlendirildii dosya kapatlarak stdout dosyasnn data.dat dosyasna ynlendirilmesi salanr. Bu ynlendirme ilemi komut satrndan yaplm olabilecei gibi, freopen ilevine daha nce yaplan bir ar ile de gerekletirilmi olabilir. freopen ilevinin geri dn deeri ileve gnderilen nc argman olan FILE yaps trnden gstericidir. freopen dosyas ynlendirmenin yaplaca dosyay aamazsa NULL adresine geri dner. Eer ynlendirmenin yapld eski dosya kapatlamyorsa, freopen ilevi bu durumda bir iaret vermez.

Dosya konum gstericisi dosyann sonunu gsterdiinde bir dosyadan okuma yaplrsa okuma ilemi baarl olmaz. Bu durumda isel olarak tutulan bir bayrak set edilir. Dosyadan okuma yapan ilevler nce bu bayran deerine bakar. Bayrak set edilmise okuma yapmazlar. feof ilevi bu bayran deerini alr: int feof(FILE *); Dosya konum gstericisi dosya sonunu gsterirken dosyadan okuma yaplmsa ilev sfr d bir deere dner. Aksi halde ilev 0 deerine geri dner. feof ileviye ilgili yaplan tipik bir hata, ilevin dosya konum gstericisinin dosyann sonunu gsterip gstermediini snadn sanmaktr: if (feof(f)) /*****/ deyimi ile byle bir snama yaplamaz. nk feof ilevi dosya konum gstericisi dosya sonunu gstermesine karn bu konumdan daha hi okuma giriiminde bulunulmamsa 0 deerine geri dner. Bir metin dosyasnn ieriini ekrana yazdrmak isteyen aadaki C kodu hataldr. Neden hatal olduunu bulmaya aln:

feof levi

478

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #define FILE_NAME_LEN #define BUFFER_SIZE 256 20

int main() { char source_file_name[FILE_NAME_LEN]; char buffer [BUFFER_SIZE]; FILE *fs; printf("yazdirilacak dosya ismi: "); gets(source_file_name); fs = fopen(source_file_name, "r"); if (fs == NULL) { printf("%s dosyasi acilamiyor!\n", source_file_name); exit(EXIT_FAILURE); } while (!feof(fs)) { fgets(buffer, BUFFER_SIZE, fs); printf("%s", buffer); } fclose(fs); } return 0;

Okuma ya da yazma yapan ilevler, okuma ya da yazma ileminde bir hata olduunda isel olarak ounlukla bitsel bir alanda tutulan bir bayra birlerler. Okuma ya da yazma yapan ilevler nce bu bayran deerine bakar. Bayrak set edilmise yeni bir okuma/yazma ilemi yaplamaz. nce bayran tekrar sfrlanmas gerekir. ferror ilevi hata bayrann birlenip birlenmediini snar: int ferror(FILE *); hata bayra set edilmise ilev sfr d bir deere geri dner. Hata bayra set edilmemise ilev 0 deerine geri dner.

ferror levi

clearerr levi
void clearerr(FILE *stream ); Okuma ya da yazma ileminde bir hata olduunda hata bayrann set edildiini, dosya sonundan okuma yapma giriiminde de EOF bayrann set edildiini sylemitik. Bu bayraklar set edilmi durumdayken bir okuma ya da yazma ilemi gerekletirilemez. Yeniden bir okuma ya da yazma ileminin yaplabilmesi iin nce bayraklarn sfrlanmas gerekir. Bu sfrlama ilemi iin clearerr ilevi arlabilir. lev ilgili dosyaya ilikin FILE * trnden deeri alr ve bu dosyann EOF ve Error bayraklarn sfrlar.

Bu ilev dosyadan okunan karakteri, dosyann tampon alanna geri koyar. int ungetc(int c, FILE *f);

ungetc levi

479

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ungetc ilevi iin dosyann okuma modunda alm olmas gerekir. levin arlmasndan sonra yaplan ilk okumada c deeri okunur. lev baarl olursa, c deerine geri dner. Baarszlk durumunda dosyann tampon alannda bir deiiklik olmaz ve EOF deeri dndrlr. lev, karakteri tampon alanna yerletirdikten sonra EOF bayran da sfrlar. Dosya konum gstericisi yeniden konumlandrlmadan ungetc ilevi arka arkaya arlmamaldr.

kincil belleklerle (disket, hard disk vs.) yaplan ilemler bellekte yaplan ilemlere gre ok yavatr. Bu yzden bir dosyadan bir karakterin okunmas ya da bir dosyaya bir karakterin yazlmas durumunda her defasnda dosyaya dorudan ulamak verimli bir yntem deildir. lemin verimi tamponlama (buffering) yoluyla artrlr. Bir dosyaya yazlacak veri ilk nce bellekteki bir tampon alannda saklanr. Bu tampon alan dolduunda ya da yazma ileminin yaplaca dosya kapatldnda tampondaki alanda ne veri varsa dosyaya yazlr. Buna tampon alannn boaltlmas (flushing) denir. Giri dosyalar da benzer ekilde tamponlanabilir. Giri aygtndan rnein klavyeden alnan byte'lar nce tampona yazlr. Dosyalarn tamponlanmas verimlilikte ok byk bir arta neden olur. nk tampondan yani bellekten bir karakter okunmas ya da tampona bir karakter yazlmas ihmal edilecek kadar kk bir zaman iinde yaplr. Tampon ile dosya arasndaki transfer, phesiz yine zaman alr ama bir defalk blok aktarm , kk kk aktarmlarn toplamndan ok daha ksa zaman alr. stdio.h balk dosyas iinde bildirimi yaplan ve dosyalarla ilgili ilem yapan ilevler tamponlamay otomatik olarak gerekletirir. Yani dosyalarn tamponlanmas iin programcnn birey yapmasna gerek kalmadan bu i geri planda programcya sezdirilmeden yaplr. Ama baz durumlarda tamponlama konusunda programc belirleyici durumda olmak isteyebilir. te bu durumlarda programc dosya tamponlama ilevlerini (fflush, setbuf, setvbuf) kullanr:

Dosya Tamponlama levleri

Dosyalar zerindeki giri k ilemleri, ounlukla tamponlama yoluyla yaplr. Dosyaya yazma ilemi gerekletiren bir ilev arldnda, eer tamponlama yaplyorsa, ilev yazma ilemini bellekteki bir tampon alanna yapar. Dosya kapatldnda ya da tamponlama alan dolduunda, tamponlama alan boaltlarak dosyaya yazlr. fflush ilevinin arlmasyla, dosyann kapatlmas ya da tamponlama alannn dolmas beklenmeksizin, tamponlama alan boatlarak dosyaya yazlr. Bu ilem istenilen sklkta yaplabilir. levin bildirimi: int fflush (FILE *stream); biimindedir. fflush(fp); ars ile FILE yaps trnden fp gstericisi ile ilikilendirilen dosyann tamponlama alan (buffer) boaltlr. Eer fflush ilevine NULL adresi gnderilirse, ak olan btn dosyalarn tamponlama alanlar boaltlr. Tamponlama alannn boaltlmas ilemi baarl olursa fflush ilevi 0 deerine geri dner, aksi halde EOF deerine geri dner.

fflush levi

setvbuf ilevi bir dosyann tamponlanma biiminin deitirilmesi ve tampon alannn yerinin ve boyutunun deitirilmesi amacyla kullanlr. levin bildirimi aadaki ekildedir:

setvbuf levi

480

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int setvbuf(FILE *stream, char *buf, int mode, size_t size); leve gnderilen nc argman tamponlama eklini belirler. nc argmann deeri stdio.h balk dosyas iinde tanmlanan simgesel deimezlerle belirlenir. _IOFBF (full buffering - tam tamponlama) Veri 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 ilevine gnderilen ikinci argman RAM'de tamponlamann yaplaca bloun balang adresidir. Tamponlamann yaplaca alan statik ya da dinamik mrl olabilecei gibi, dinamik bellek ilevleriyle de elde edilebilir. leve gnderilen son argman tamponlama alannda tutulacak byte'larn saysdr. setvbuf ilevi dosya aldktan sonra, fakat dosya zerinde herhangi biri ilem yaplmadan nce arlmaldr. levin baarl olmas durumunda ilev 0 deerine geri dner. leve gnderilen nc argmann geersiz olmas durumunda ya da ilevin ilgili tamponlamay yapamamas durumunda, geri dn deeri sfrdan farkl bir deer olur. leve gnderilen tampon alannn geerliliinin bitmesinden nce, yani mrnn tamamlanmasndan nce dosya kapatlmamaldr.

Bir dosya ile ilgili ilem yapan bir ilev iki ayr biimde tasarlanabilir: 1. lev arldnda dosya aktr. Bu durumda ilevin bir parametresi FILE * trnden olur. lev arlmadan nce, en son okuma ilemi mi yazma ilemi mi yapld bilinemeyeceinden, byle bir ilev ilk ilem olarak dosya konum gstericisini konumlandrmaldr. Aadaki ilevleri inceleyin:

Dosyalarla lgili lem Yapan levlerin Yazm

481

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <time.h> #define #define SORTED NOT_SORTED 1 0

void swap(int *p1, int *p2) { int temp = *p1; *p1 = *p2; *p2 = temp; } void add_ints_to_file(FILE *f) { int k, val, number; fseek(f, 0L, SEEK_END); number = rand() % 100 + 300; for (k = 0; k < number; ++k) { val = rand(); fwrite(&val, sizeof(int), 1, f); }

void print_file(FILE *f) { int val; int counter = 0; rewind(f); while (fread(&val, sizeof(int), 1, f)) { if (counter && counter % 10 == 0) printf("\n"); printf("%5d ", val); counter++; } printf("\n********************************************************\n");

void sort_file(FILE *f) { int a[2]; int k, temp, sort_flag, number_of_ints; fseek(f, 0L, SEEK_END); number_of_ints = ftell(f) / sizeof(int); do { sort_flag = SORTED; for (k = 0; k < number_of_ints - 1; ++k) { fseek(f, sizeof(int) * k, SEEK_SET); fread(a, sizeof(int), 2, f); if (a[0] > a[1]) { swap(a, a + 1); fseek(f, sizeof(int) * k, SEEK_SET); fwrite(a, sizeof(int), 2, f); sort_flag = NOT_SORTED; } }

482

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

} while (sort_flag == NOT_SORTED);

int main() { FILE *f; f = fopen("sayilar.dat", "w+b"); if (f == NULL) { printf("dosya yaratilamiyor!\n"); exit(EXIT_FAILURE); } srand((unsigned int)(time(0))); add_ints_to_file(f); print_file(f); sort_file(f); print_file(f); fclose(f); } return 0;

main ilevi iinde arlan ilevlerden add_ints_to_file ilevi ald binary dosyann sonuna rastgele tamsaylar formatsz olarak yazyor. Daha sonra arlan print_file ilevi, binary dosyaya yazlan tm saylar ekrana yazyor. sort_file ilevi ile dosyadaki tamsaylar kkten bye doru sralandktan sonra print_file ileviyle yeniden yazdrlyor. 2. lev arldnda dosya kapaldr. lev, aran kod parasndan dosya ismini ister. Bu durumda ilev iini gerekletirmek iin nce dosyay aar. ini gerekletirdikten sonra dosyay kapatr. Aada bir metin dosyasnn ieriini ekrana yazdran bir ilev tanmlanyor: #include <stdio.h> #include <string.h> #include <stdlib.h> void ftype(const char *file_name) { int ch; FILE *f = fopen(file_name, "r"); if (f == NULL) { printf("cannot open %s\n", file_name); exit(EXIT_FAILURE); } while ((ch = fgetc(f)) != EOF) putchar(ch); fclose(f);

483

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

MAKROLAR
Yapsal programlama alt programlamaya dayanr. Bir problem zm daha kolay admlara ayrlr, bu admlar iin ilevler tanmlanr. Programn kullanaca veri ilevler tarafndan ilenir. Tanmlanan ilevlerden bazlarnn kaynak kodu ok ksa olabilir. Aadaki gibi bir ilev tanmlandn dnelim: int get_max(int a, int b) { return a > b ? a : b; } get_max ilevi kendisine gnderilen iki saydan daha byk olanna geri dnyor. imdi de u soruyu soralm: Bu kadar ksa kaynak koda sahip bir ilev tanmlanmal mdr? lev tanmlamak yerine ilevin kodunu, dorudan ilev arsnn bulunduu yere yerletirmek daha iyi olmaz m? Yazlan bir kod parasnda iki saydan bynn bulunmas gerektiini dnelim: x = get_max(y, z); gibi bir deyim yerine x = y > z ? y : z; gibi bir deyim yazlamaz m? Bir ilev tanmlayp tanmlanan ilev mi arlmal yoksa ilevin kodu dorudan m yazlmaldr? nce, ilev tanmlamay destekleyen argmanlar zerinde duralm: 1. lev ars, zellikle ilevin ismi iyi seilmi ise, ou zaman okunabilirlii daha yksek bir kod oluturur. Aadaki rnei inceleyin: a = get_max(ptr->no, per.no); a = ptr->no > per.no ? ptr->no : per.no; lk deyimde get_max isimli bir ilev arlm ikinci satrda iki ifadeden deeri daha byk olan, bir ilev arlmadan bulunmutur. Hangi deyimin okunabilirlii daha iyidir? 2. lev ars ou durumda kaynak kodu kltr. zellikle, ileve konu ilem kaynak kod iinde sk yineleniyorsa, bir ilevin tanmlanarak eitli yerlerde arlmas kaynak kodu kltr. 3. lev ars kaynak kodda deiiklik yapmay daha kolay hale getirir: Kaynak kod iinde bir ok yerde, iki deerden daha bynn bulunarak kullanldn dnelim. Daha sonra kaynak kodun bu noktalarnda deiiklik yaplarak, ayn noktalarda iki deerden daha knn bulunarak kullanlmas gerektiini dnelim. Eer ilev ars yerine kod ak bir ekilde yazlmsa, deiiklik yapmak ok daha zor olur. 4. lev ars, debug etme olanan artrr. Debugger programlar genel olarak ilev arlar iin, kodun ak olarak yazlmasna gre daha iyi destek verir. 5. lev ars derleyiciye baz snamalar yapma olanan verir. leve gnderilen argmanlarn toplam saysnn ilevin parametre deikenlerinin toplam saysna eit olup

485

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

olmad derleyici tarafndan snanr. Ayrca ileve gnderilen argmanlarn tr ile ilevin ilgili parametre deikeninin trlerinin uyumlu olup olmad da derleyici tarafndan snanr. imdi de ilev ars yapmak yerine ilevin kodunu dorudan yazmak durumunda elde edilebilecek avantajlarn ne olabileceine bakalm: Bir ileve yaplan arnn ilemci zaman ve bellek kullanm asndan ek bir maliyeti vardr. leve giri ve ilevden k kodlarnn yrtlmesi iin ek makina komutlarnn yrtlmesi gerekir. Varsa ilevin parametre deikenleri de, ilev arldnda bellekte yer kaplar. zellikle kaynak kodu ok ksa, kaynak dosya iinde ok arlan ilevler iin, ou durumda bu maliyetin denmesi istenmez. lev ars yapmak yerine kod ak bir biimde yazlrsa, derleyici kodun bulunduu yere bal olarak daha verimli bir eniyileme (optimizasyon) gerekletirebilir.

Makro, ilev tanmlamadan ilev arsnn getirdii baz avantajlardan yararlanlmasn salayan bir aratr. Ortada gerek bir ilev tanm sz konusu olmadndan, ilev iin denen bir ek gider de sz konusu deildir. Makrolar, nilemci programn #define komutu ile tanmlanr. Bir makro tanm, nilemci programa verilen parametrik bir yer deitirme komutudur. #define MAX(X, Y) ((x )> (y) ? (x) :(y))

Makro Nedir

MAX, tanmlanan makronun ismidir. Bu ismi "alan ayra" karakteri izlemelidir. Makro ismini izleyen ayra iinde, virgllerle ayrlarak bildirilen isimlere "makro parametreleri" denir. Kapanan ayrac izleyen boluktan sonra gelen atomlar "makro yer deitirme listesi"dir. Yer deitirme listesinde, makro parametreleri istenildii kadar istenildii sra ile kullanlabilir. nilemci program kaynak kodun izleyen kesiminde MAX ismi ile karlatnda, ayra iindeki ifadeleri makro argman olarak kabul eder. Makro tanmnda bulunan yer deitirme listesinde makro parametreleri nasl kullanlmsa, makro arsnn bulunduu yerlerde benzer bir yer deitirme ilemi yaplr. Aadaki kodu inceleyin: #include <stdio.h> #define #define ISEVEN(a) MAX(a, b) (!((a) & 1)) ((a) > (b) ? (a) : (b))

int main() { int x, y; printf("iki sayi giriniz :"); scanf("%d%d", &x, &y); if (ISEVEN(x)) printf("%d cift sayi!\n", x); else printf("%d tek sayi!\n", x); printf("%d ve %d sayilarindan buyugu = %d\n", x, y, MAX(x, y)); } return 0;

Yukardaki main ilevinde, ismi ISEVEN ve MAX olan iki makro tanmlanyor. nilemci makro tanmnn zerinden getikten sonra if (ISEVEN(x))

486

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

satr ile karlatnda, ISEVEN isminin daha nce tanmlanan makroya ait olduunu ve makro argman olarak x atomunun kullanldn anlar. nilemci program ilgili yer deitirme ilemini yaptnda bu satr aadaki biime dnr: if ((!((x) & 1))) Benzer ekilde printf("%d ve %d sayilarindan buyugu = %d\n", x, y, MAX(x, y)); satr da MAX isimli makronun nilemci program tarafndan almas sonucu printf("%d ve %d sayilarindan buyugu = %d\n", x, y, ((x) > (y) ? (x) : (y))); haline getirilir.

Gvenli Makro Yazmak


Aadaki makroyu inceleyin: #define Kare(x) x * x

Kaynak kod iinde bu makro aadaki gibi kullanlm olsun: void func() { int a = 10; int b; b = Kare(a + 5); /***/ } Kare eer bir ilev olsayd, ileve gnderilen argman olan a + 5 ifadesinin nce deeri hesaplanrd. Argman olan ifadenin deeri 15 olduu iin arlan ilev 225 deerine geri dnerdi. Ancak nilemci program aadaki gibi bir yer deitirme ilemi yapar: b = a + 5 * a + 5; arpma ileci toplama ilecine gre daha yksek ncelikli olduu iin burada b deikenine atanacak deer 65 olur, deil mi? Bu tr ncelik sorunlarn zmek amacyla makro parametreleri, alm listesinde ayra iine alnmaldr: #define Kare(x) (x) * (x)

Bu kez ayn ifade nilemci tarafndan aadaki gibi alr: b = (a + 5) * (a + 5); b deikenine atanan deer 225 olur. Ancak imdi de makronun aadaki gibi kullanldn dnn: b = 100 / Kare(a); Kare ilev olsad b deikenine 1 deeri atanrd deil mi? Ancak yukardaki makro nilemci tarafndan b = 100 / (a) *( a);

487

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Burada b deikenine atanan deer 100 olur. Bu tr ncelik sorunlarn zmek amacyla makro alm listesi dtan ncelik ayrac iine alnmaldr. #define Kare(x) ((x) *(x))

Bu kez ayn ifade nilemci tarafndan aadaki gibi alr: b = 100 / ((a) * (a)); Bu kez b deikenine atanan deer 1 olur. Makro alm listesini dtan ayra iine almak ve alm listesinde yer alan makro parametrelerinin her birini ayra iine almak makrolar gvenilir klmaya yetmez. Zaten makrolarla ilgili en byk sorun gvenilirlik sorunudur. Aadaki kodu inceleyin: #include <stdio.h> #define Kare(x) int func(int val); int main() { int x = 10; int y = Kare(x++); int z = Kare(func(y)); /*********/ return 0; } y = Kare(x++); deyiminde eer Kare bir ilev olsayd bu ilev 10 deeri ile arlm olurdu. Sonek konumundaki ++ ilecinin yan etkisi nedeniyle, x nesnesinin deeri 11 olurdu. Ancak makro nilemci tarafndan aldnda derleyicinin grecei kod y = ((x++) * (x++)); biiminde olur ki, bu durum "tanmsz davrantr" (undefined behaviour). z = Kare(func(y)); deyimini de nilemci aadaki gibi aar: z = ((func(y)) * (func(y))); Derlenen kod, func ilevinin iki kez arlmasna neden olur. Oysa Kare bir ilev olsayd, arlan func ilevinin retecei geri dn deeri, bu kez Kare ilevine argman olarak gnderilmi olurdu. ((x) * (x))

rnek Makrolar
#define #define #define #define

Aada baz faydal makro tanmlamalarna rnekler verilmitir. rnekleri inceleyin: GETRANDOM(min, ISLEAP(y) SWAPINT(x, y) XOR(x, y) max) ((rand()%(int)(((max) + 1)-(min)))+ (min)) ((y) % 4 == 0 && (y) % 100 != 0 || (y) % 400 == 0) ((x) ^= (y) ^= (x) ^= (y)) (!(x) ^ !(y))

488

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukarda tanmlanan getrandom isimli makro min ve max deerleri arasnda bir rastgele tamsaynn retilmesine neden olur. isleap makrosu nilemci tarafndan aldnda makro argman olan ifadenin deerinin artk yl olmas durumunda 1 deeri aksi halde 0 deeri retilir. Bu durumda makro bir snama ilevi gibi kullanlr. swap_int makrosu argman olan tamsay deikenlerin deerlerini takas eden bir ifadeye alr. Bir makronun parametreye sahip olmas gerekmez. Aada tanmlanan randomize makrosunu inceleyin: #define randomize() srand((unsigned int)time(NULL))

Bir makro tanmnn alm listesinde yer alan # atomu nilemci programn bir ilecidir. # ileci nek konumunda tek terimli bir iletir. Bu ilemcinin terimi makro parametrelerinden biri olmaldr. Parametresiz makrolarda bu ile kullanlamaz. nilemci makroyu atnda makro parametresine karlk gelen makro argmann ift trnak iine alr: Aadaki kodu inceleyin: #include <stdio.h> #define printint(x) printf(#x " = %d\n", x);

nilemci Dizge Yapma leci

int main() { int a = 10; int b = 20; printint(a); printint(a + b); } return 0;

Yukardaki rnekte, printint makrosu alm listesinde, makro parametresi olan x, dizge yapma ilecinin terimi yaplyor. nilemci program, main ilevinin kodunu aadaki biime dntrr. int main() { int a = 10; int b = 20; printf("a" " = %d\n", a); printint("a + b" " = %d\n", a + b); return 0; } Boluk karakterleriyle birbirinden ayrlan dizgelerin, derleyici tarafndan otomatik olarak birletirilerek tek dizge haline getirildiini anmsayn. Program derlenerek altrldnda ekran kts aadaki gibi olur: a = 10 a + b = 30

489

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Makro argman olarak bir dizge verilirse dizgenin banda ve sonunda yer alan ift trnak (") karakterleri ve dizgenin iinde yer alan ters bl (\) ve ift trnak (") karakterinin nne makro almnda otomatik olarak ters bl karakteri yerletirilir. Aadaki kodu inceleyin: #include <stdio.h> #define print(x) printf(#x)

int main() { print(Ekranda bu yazi gorulecek\n); print("Ekranda bu yazi cift tirnak icinde gorunecek"\n); print("\""\n); return 0; } Program derlenip altrldnda ekran kts aadaki gibi olur: Ekranda bu yazi gorulecek "Ekranda bu yazi cift tirnak icinde gorunecek" "\""

nilemci programn ikinci ileci "##" atom birletirme ielecidir (tokenpasting operator). Atom birletirme ileci iki terimlidir araek konumundadr. Parametreli ya da parametresiz makrolarda kullanlabilir. Terimlerinin, makro parametrelerinden biri olmas zorunlu deildir. nilemci, atom birletirme ilecinin terimlerini birletirerek, terimlerinden tek bir atom yapar. Aadaki rnei inceleyin: #include <stdio.h> #define paste(x, y) x##y

nilemci Atom Birletirme leci

int main() { int paste(a,1); a1 = 10; printf("a1 = %d\n", a1); } return 0;

Yukardaki rnekte, nilemci program tarafndan paste isimli makronun almas sonucu a ve 1 makro argmanlar birletirilerek a1 atomu elde edilir.

Baz ktphanelerde belirli ilevler ayn isimle hem makro hem de ilev olarak tanmlanrlar. Bundan amalanan, programcnn istee bal olarak makro kullanmn ya da ilev arsn semesine olanak vermektir. Hem kare isimli bir makro tanmlanm hem de kare isimli bir ilev bildirilmi olsun: int kare(int); #define kare(a) ((a) * (a))

Bir Makro le Bir levin Ayn smi Paylamas

Kaynak kodda aadaki gibi bir deyim olsun:

490

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

x = kare(y); phesiz, byle bir deyimde makro kullanlm olur. nk nilemci, program kodunu derleyici programdan daha nce ele alr. Derleyiciye sra geldiinde, nilemci zaten ilgili makro iin yer deitirme ilemini tamamlam olur. Ancak makro yerine ilev arsn semek iin iki yntem kullanlabilir. 1. #undef nilemci komutu kullanlarak makro tanm ortadan kaldrlabilir: #undef kare x = kare(y); nilemci program #undef konutuyla karlatnda makro tanmn ortadan kaldrr ve izleyen satrlarda kare ismini grdnde bir yer deitirme ilemi yapmaz. 2. kare ismi ayra iine alnarak makro devre d braklabilir x = (kare)(y); Bu durumda kare ismini izleyen ilk atom, alan ayra "(" karakteri olmadndan, nilemci program yer deitirme ilemi yapmaz. Ancak ilev arsn engelleyen bir durum sz konusu deildir. lev isminin bir adres belirttiini biliyorsunuz. Yukardaki ifade ile ilev adresine dntrlecek olan kare ismi ncelik ayrac iine alnmtr. 3. Bir ilev, bir ilev gstericisi kullanlarak arlabilir: int kare(int); void func() { int (*fp)(int) = &kare; fp(); } Derleyiciler, standart ktphaneye ilikin ilevleri ayn zamanda makro olarak da tanmlayabilirler. rnein derleyicilerin ou cytpe.h balk dosyasnda bildirilen karakter test ilevlerini ayn zamanda makro olarak tanmlar.

1. Makrolar ou zaman ayn ii yapan ileve gre daha hzl alan bir kodun retilmesine neden olurlar. Zaten makrolarn kullanld durumlarda hedeflenen de budur. 2. Makrolar, her kullanld yerde nilemci tarafndan ald iin, ounlukla kaynak kodu bytr. Kaynak kodun byd durumlarn ounda, altrlabilir dosyann boyutu da byr. Ancak bir ilev ka kez arlrsa arlsn, o ilevin tanm kaynak kod iinde bir kez yer alr. 3. Makro paremetreleri makro alm iinde birden fazla kullanlyor ise makro alm sonucu istenmeyen durumlar oluabilir. Makro argman olarak kullanlan ifadenin bir yan etkiye sahip olmas durmunda bu yan etki birden fazla kez oluur. Ancak ilevler sz konusu olduunda byle bir risk sz konusu deildir. 4. Bir ilevin adresi ilev gstericilerinde tutularak baz ilemler gerekletirilebilir. Ancak bir makronun adresi olmaz. 5. Bir makro sz konusu olduunda nilemci program, makroya verilen argmanlarn says ile makro parametrelerinin saysnn uyumunu kontrol edebilir. Ancak nilemci 491

Makrolar ile levler Arasndaki Farklar

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

program makro argmanlaryla makro parametrelerinin tr asndan uyumunu kontrol edemez, makrolar trden bamszdr. Ancak ilevler trlere ilikin yazldndan derleyicinin yapaca tr kontrollerine tabidir. Derleyici ileve gnderilen argmanlarla ilev parametrelerinin trlerinin uyumsuz olmas durumunda hata ya da uyar iletisi retebilir.

Bir makro alm listesinin yazm makronun tanmland satrda sonlanmak zorunda deildir. Makro alm listesinin yazm alt satrdan devam edebilir. Bunun iin alt satra gemeden nce ters bl karakteri '\' kullanlr.

Makro Almlarnn Alt Satrdan Devam Etmesi

Makrolar ile lev Kalb Oluturulmas

Makrolar kullanlarak ilev kalb hazrlanabilir. Aadaki rnekte bir dizinin en byk elemann bulan bir ilev kalb hazrlanyor: #include <stdio.h> #define generic_max(T) T getmax_##T(const T *ptr, size_t size) {\ int k; T max = *ptr; for (k = 1; k < size; ++k)\ if (ptr[k] > max) max = ptr[k]; return max;}

generic_max(int) generic_max(double) int main() { int a[10] = {1, 4, 5, 7, 8, 9, 10, 2, 3, 6}; double b[10] = {1.2, 3.5, 7.8, 2.4, 4.4, .7, 3.2, 4.8, 2.9, 1.}; printf("a dizisinin en buyuk elemani = %d\n", getmax_int(a, 10)); printf("b dizisinin en buyuk elemani = %lf\n", getmax_double(b, 10)); return 0; } generic_max isimli makronun tanmnda makro parametresi olarak T isminin kullanldn gryorsunuz. Bu isim bir tr bilgisi olarak kullanlyor. nilemci atom birletirme ileciyle, makro alm sonucunda farkl ilev isimleri elde edilir.

Bir makro ismi ya da bir simgesel deimez, deitirme listesinde yer alabilir. Bu duruma ngilizcede "self referential macro" denmektedir. Eer alm listesinde makronun kendi ismi yer alrsa nilemci zyinelemeli bir yer deitirme ilemi yapmaz. Aadaki rnei inceleyin: #define max (4 + max)

Makro Almnda Makro sminin Kullanlmas

max isminin programn iinde kullanlan bir baka deikenin ismi olduunu dnelim. x = max; nilemci program yukardaki deyimin zerinden getiinde bu deyimi x = 4 + max; biimine dntrr. Ve daha sonra artk max ismini yine yer deitirme ilemine sokmaz.

492

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu makro stddef.h balk dosyas iinde tanmlanmtr. Makronun iki parametresi vardr. Birinci parametreye bir yap tr ismi, ikinci parametreye yapnn bir eleman verilir. Makronun almas sonucu oluan deimez ifadesinin deeri, bir yap elemannn yap nesnesi iindeki konumunu gsteren offset deeridir. Yapnn ilk eleman iin bu deer 0'dr. Aadaki rnei inceleyin: #include <stdio.h> #include <stddef.h> struct Date { int day, mon, year; }; int main() { printf("%d\n", offsetof(struct Date, day)); printf("%d\n", offsetof(struct Date, mon)); printf("%d\n", offsetof(struct Date, year)); } return 0;

Standart offsetof Makrosu

offsetof makrosu derleyicilerin ounda aadaki gibi tanmlanr: #define offsetof(type, member) ((size_t)&((type *)0)member)

493

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

NLEMC KOMUTLARI (2)


Koullu derleme, derleme ileminin bir koula ya da baz koullara gre yaplmasdr. Yazlan kaynak kodun belirli ksmlar nilemci program tarafndan derleyiciye verilirken baz ksmlar derleyiciye verilmez. Derleyiciye verilecek kaynak kod nilemci program tarafndan seilir.

Koullu Derleme Nedir?

Koullu derlemede amacyla kullanlan nilemci komutlar #if, #else, #elif, #ifdef, #ifndef, #endif komutlardr.

Koulu Derleme nilemci Komutlar

#if nilemci komutunun argman tamsay trnden bir deimez ifadesi olmaldr. if szcn izleyen ifadenin deeri sfr d bir deer ise #if komutu ile #elif ya da #else yada #endif komutlar arasnda kalan ksm derleyiciye verilir. Eer #if ifadesi yanl ise yani ifadenin deeri sfr ise #if komutu ile #else arasnda kalan ksm derleyiciye verilmez. Aadaki program derleyerek altrn: int main() { #if 1 printf("bu yazi ekranda gorunecek\n"); #endif #if 0 printf("bu yazi ekranda gorunmeyecek\n"); #endif return 0; } #if komutunda kullanlacak ifade tamsay trnden olmaldr. Bu ifade iinde Tamsay deimezleri kullanlabilir. nilemci program kullanlan tm say deimezlerini iaretli long ya da iaretsiz long trnden varsayar. Karakter deimezleri kullanlabilir. Aritmetik ileler (+, -, *, /, %), karlatrma ileleri (<, <=, >, >=, ==, !=) , bitsel ileler(~, &, ^, |) ve mantksal ileler (!, && ve || )kullanlabilir. && ve || ilelerinin kullanmnda ksa devre davran geerlidir. #if ifadesinin deerinin hesap edilmesinden nce simgesel deimezlerle ve makro tanmlarna ilikin btn yer deitirme ilemleri yaplr. Simgesel deimez ve makro ismi olmayan tm isimler 0 deerine sahip kabul edilirler. #if ifadesinde sizeof ileci yer alamaz. Numaralandrma trlerinden deimezler temsil ettikleri deerler olarak ele alnmaz. #if ifadesinde bir numaralandrma deimezi kullanlrsa, bu deimez makro ismi olmayan tm isimler gibi 0 deeri olarak ele alnr. #if komutu ile #endif komutu aralndaki alanda baka nilemci komutlar yer alabilir. Bu alanda yer alan nilemci komutlar ancak #if ifadesi doru ise yerine getirilir. #if komutunu izleyen ifade bu #if komutuyla ilikilendirilen #endif komutunu izleyen bir yorum satr iinde yinelenirse, kodun okunabilirlii artar. Bu durum zellikle #if komutlarnn i ie bulunmas durumunda fayda salar. Aadaki rnei inceleyin:

#if ve #endif nilemci Komutlar

495

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#if MAX > 100 #if MIN < 20 #define LIMIT 100 #endif /* MIN < 20 */ #define UPPER_BOUND 2000 #endif /* MAX > 100 */

# if komutunu bir #else komutu izleyebilir. Bu durumda #if ifadesinin deeri 0 d bir deer ise yani ifade doru ise #if ve #else komutlar arasnda kalan kaynak kod paras derleyiciye verilir. Eer bu ifade yanl ise #else ve #endif komutlar arasndaki ksm derleyici programa verilir. Bylece bir tamsay deimez ifadesinin doru veya yanl olmasna gre derleyiciye farkl kod paralar verilebilir. Aadaki program derleyerek altrn: #include <stdio.h> #define MAX 100

#else nilemci Komutu

int main() { #if MAX > 10 printf("%d\n", MAX); #else printf("**********\n"); #endif /*MAX > 100 */ return 0; }

elif szc else if szcklerinin birletirilerek ksaltlmasndan elde edilmitir. Tpk C dilinin if deyimi gibi bir #if komutunun doru ya da yanl ksmnda baka bir #if komutu yer alabilir. #if ve #else komutlaryla bir merdiven oluturulabilir. Bu amala kullanlabilecek bir komut #elif komutudur. #elif komutunu yine bir tamsay deimez ifadesi izler. Eer bu tamsay deimez ifadesi 0 d bir deere sahipse doru olarak yorumlanr ve #elif komutu ile bunu izleyen bir baka #elif ya da #endif komutu arasndaki ksm derleyiciye verilir. Aadaki rnei derleyerek altrn: #include <stdio.h> #define MAX 60

#elif nilemci Komutu

int main() { #if MAX > 10 && MAX <= 20 printf("(if)%d\n", MAX); #elif MAX > 20 && MAX <= 50 printf("(elif1)%d\n", MAX); #elif MAX > 50 && MAX < 100 printf("(elif2)%d\n", MAX); #else printf("(else)%d\n", MAX); #endif return 0; }

496

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

defined nilemci leci

Makrolar konusunda nce nilemci programn dizge yapma (#) ve atom birletirme (##) ilelerini grmtk. nilemci programn 3. ve son ilei defined ilecidir. defined nilemci ilecini bir isim izler. Bu isim ayra iine de alnabilir: defined MAX defined (MAX) Eer defined ilecinin terimi olan isimli daha nce tanmlanm bir simgesel var ise, defined ileci 1 deeri retir. Bu isimli bir simgesel deimez yoksa defined ileci 0 deeri retir. Aadaki rnei inceleyin: #include <stdio.h> #define MAX 100

int main() { #if defined MAX && !defined MIN printf("max = %d\n", MAX); #endif return 0; }

#if nilemci komutunun kullanld her yerde #ifdef ve #ifndef nilemci komutlar kullanlabilir: #ifdef nilemci komutunu bir isim(identifier) izler. Eer bu isim daha nce tanmlanm bir simgesel deimeze ilikinse ilk #else, #elif ya da #endif neilemci komutlarna kadar olan ksm derleyiciye verilir. Eer #ifdef nilemci komutunu izleyen isimde bir simgesel deimez tanml deil ise ilk #else #elif #endif komutuna kadar olan ksm derleyiciye verilmez. Bu komut ile yaplan i #if nilemci komutu ile defined ilecinin birlikte kullanlmasyla da yaplabilir: #ifdef ISIM gibi bir nilemci komutu kullanm ile #if defined (ISIM) gibi bir nilemci komutu ayn anlamdadr. #ifndef nilemci komutunu da bir isim(identifier) izler. Eer bu isimde bir isim daha nce #define nilemci komutuyla tanmlanmam ise, ilk #else, #elif ya da #endif neilemci komutuna kadar olan ksm derleyiciye verilir. #ifndef nilemci komutunu izleyen isimde bir simgesel deimez tanmlanm ise #else #elif #endif komutlarna kadar olan ksm derleyiciye verilmez. #ifndef SYSTEM

#ifdef ve ifndef nilemci Komutlar

gibi bir nilemci komutu ile #if !defined (SYSTEM) gibi bir nilemci komutu ayn anlamdadr. Aadaki program derleyerek altrn:

497

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #define ISIM1 int main() { #ifdef ISIM1 printf("bu yazi ekranda cikacak!\n"); #endif #ifndef ISIM1 printf("bu yazi ekranda cikmayacak!\n"); #endif return 0; }

#error nilemci Komutu

#error nilemci komutu koullu derlemede kullanlr. nilemci program bu komut ile karlatnda derleme ilemini daha nileme aamasnda sonlandrr. Kullanm aadaki gibidir: #error yaz error nilemci komutunun yannda -boluk karakteri ile ayrlm- bir hata iletisi yer alr. nilemci #error komutunu grnce bu hata iletisini ekrana yazarak derleme ilemine son verir. Hata yazsnn ekranda nasl gsterilecei derleyiciye gre deiebilir. #ifndef __STDC__ #error Bu program yalnzca C derleyicisinde derlenir. #endif Yukardaki rnekte #ifndef komutu ile, derleme ilemini yapacak derleyicinin standart C derleyicisi olup olmad snanyor, derleyici eer standart C derleyicisi deil ise derleme ilemi nilemci aamasnda sonlandrlyor. Derleme ilemi sonlandrldnda ekrana: Bu program yalnzca C derleyicisinde derlenir. yazs yazdrlr. #if UINT_MAX < 65535 #error unsigned int turu yeterli buyuklukte degil #endif Yazlan programn int trnn 2 byte dan daha kk olan bir sistem iin derlenmeye allmas durumunda ekrana Error directive: unsigned int turu yeterli buyuklukte degil Biiminde bir yaz yazdrlarak derleme ilemine son verilir.

#undef nilemci Komutu

Bir simgesel deimezin ilki ile zde olmayan bir biimde ikinci kez tanmlanmas tanmsz davrantr (undefined behaviour). rnein aadaki gibi bir tanmlama ilemi yanltr: #define #define MAX 100 MAX 200

498

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bir simgesel deimezin ilki ile zde olarak tanmlanmasnda herhangi bir sorun kmaz. Bir simgesel deimez ikinci kez tanmlanmak istenirse nce eski tanmlamay ortadan kaldrmak gerekir. Bu ilem #undef nilemci komutuyla yaplr. #undef nilemci komutunun yanna geerlilii ortadan kaldrlacak simgesel deimezin ya da makronun ismi yazlmaldr: #undef #define MAX MAX 20

Yukardaki nilemci komutlatyla nce MAX simgesel deimezinin tanm ortadan kaldrlyor, sonra MAX simgesel deimezi 200 olarak tanmlanyor. #undef ile tanmlanmas kaldrlmak istenen simgesel deimez, daha nce tanmlanm olmasa bile bu durum bir soruna yol amaz. Yukardaki rnekte, MAX simgesel deimezi daha nce tanmlanmam olsayd, bu durum bir hataya yol amazd.

nceden Tanmlanm Simgesel Deimezler

Standart C dilinde 5 tane simgesel deimez nceden tanmlanm kabul edilir. Herhangi bir balk dosyas iinde bu simgesel deimezler #define nilemci komutuyla tanmlanm olmamasna karn kaynak kodun derleyici tarafndan ele alnmasndan nce bir yer deitirme ilemine sokulurlar. Bu simgesel deimezler ounlukla hata arama amacyla yazlan kodlarda kullanlrlar: __LINE__ ntanml simgesel deimezi Bu simgesel deimez kaynak kodun kanc satrnda kullanlm ise, o satrn numarasn gsteren bir tamsay deimezi ile yer deitirilir. __FILE__ ntanml simgesel deimezi Bu simgesel deimez hangi kaynak dosya iinde kullanlm ise, o kaynak dosyann ismini gsteren bir dizge ifadesiyle yer deitirilir. __DATE__ ntanml simgesel deimezi Bu simgesel derleme tarihini gsteren bir dizge ifadesiyle yer deitirilir. Tarih bilgisini ieren yaznn format aadaki gibidir: Aaa gg yyyy (ay, gn, yl) __TIME__ ntanml simgesel deimezi Bu simgesel deimez derleme zamann gsteren bir dizge ifadesiyle yer deitirilir. Zaman bilgisini ieren yaznn format aadaki gibidir: sa:dd:ss (saat, dakika, saniye) __STDC__ ntanml simgesel deimezi Eer derleyici standart C derleyicisi ise bu simgesel deimez tanml kabul edilir. Derleyici standart C derleyicisi deil ise bu simgesel deimez tanmlanmam kabul edilir. Aadaki program derleyerek altrn: # include <stdio.h> int main() { printf("kaynak dosya ismi : %s\n", __FILE__); printf("derleme tarihi = %s\n", __DATE__); printf("derleme zamani = %s\n", __TIME__); printf("bu satirin numarasi = %d\n", __LINE__); #ifdef __STDC__ printf("standart C derleyicisi\n");

499

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#else printf("standart C derleyicisi degil\n"); #endif return 0; } [C++ derleyiclerinde de __cplusplus ntanml simgesel deimezi, tanmlanm kabul edilir.] Bu nilemci komutuyla derleyicinin kaynak koda ilikin tuttuu satr numaras ve dosya ismi deitirilebilir. Bu komut iki ayr argman alabilir. Komutun alabilecei birinci argman bir tamsay olarak satr numarasdr. Komutun seimlik olarak alabilecei ikinci argman dosya ismini gsteren dizgedir. Bu nilemci komutu kaynak kod reten programlar tarafndan kullanlabilir. #line nilemci komutu size anlamsz gelebilir. Neden derleyicinin verecei hata iletisi rnein 20. satr deil de 25. satr gstersin? Neden derleyici hata iletisinde derledii kaynak dosyann ismini deil de bir baka dosyann ismini yazdrsn? #line komutu programclardan ok, kt olarak C kaynak kodu reten programalar tarafndan kullanlr. Aadaki program derleyerek altrn: #include <stdio.h> int main() { printf("%s dosyasnn %d. satr\n", __FILE__, __LINE__); #line 100 "aaaaa.c" printf("%s dosyasnn %d. satr\n", __FILE__, __LINE__); } return 0;

#line nilemci Komutu

Projeler ounlukla farkl kaynak dosyalardan ve balk dosyalarndan oluur. Hizmet alan kodlar (client codes) hizmet veren kodlarn (server codes) balk dosyalarn eklerler. Balk dosyasn eklemenin #include nilemci komutuyla yapldn biliyorsunuz. Bir kodlama dosyasnda iki ayr balk dosyasnn eklendiini dnelim: /*** file1.c *****/ #include "header1.h" #include "header2.h" Balk dosyalar iinde baka balk dosyalarnn eklenmesine sk rastlanr. rnein header1 ve header2 isimli balk dosyalarnn her ikisinde de header3.h isimli bir balk dosyasnn eklendiini dnelim: /*** header1.h *****/ #include "header3.h" /*** header2.h *****/ #include "header3.h" Bu durumda file1.c isimli kaynak dosya iine header3.h isimli balk dosyas iki kez eklenmi olur. Bu duruma ngilizcede "multiple inclusion" denir. Peki bu durumun bir sakncas var mdr? Balk dosyalar iinde bildirimler bulunur. Bir balk dosyas iki kez eklenirse bu balk dosyas iindeki bildirimler iki kez yaplm olur. Baz bildirimlerin zde olarak yinelenmesinin bir sakncas yoktur. rnein bir ilev bildirimi daha nceki bir bildirimle elimemek kaydyla yinelenebilir. Benzer durum extern bildirimleri ve typedef bildirimleri 500

Bir Balk Dosyasnn Birden Fazla Kez Kaynak Dosyaya Eklenmesi

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

iin de geerlidir. Ancak programc tarafndan yaplan bir tr bildiriminin yinelenmesi, bildirimler zde olsa bile geersizdir: struct A { /****/ }; struct A { /****/ }; Yukardaki rnekte struct A isimli yapnn bildirimi ikinci kez yapldnda derleyici program bir hata iletisi verir. Bir rnek de standart ktphaneden verelim: time.h isimli standart balk dosyasnda struct tm isimli bir yapnn bildirildiini biliyorsunuz. Eer bir kodlama dosyas iine time.h balk dosyasnn ierii iki kez boaltlrsa, kodlama dosyasnn derlenmesi aamasnda hata oluur. nk struct tm yaps iki kez bildirilmi olur. Proje gelitirilmesi sresinde byle hatalarn giderilmesi fazladan zaman kaybna yol aar. nilemci koullu derleme komutlaryle bu konuda bir nlem alnabilir. C ve C++ dillerinde balk dosyalar ounlukla aadaki biimde hazrlanr: //header1.h #ifndef _HEADER1_H_ #define _HEADER1_H_ #endif Bu balk dosyas bir kaynak dosya tarafndan ilk kez eklendiinde _HEADER1_H_ simgesel deimezi henz tanmlanm olmadndan #endif nilemci komutuna kadar olan ksm derleyiciye verilir. Ancak daha sonra bu balk dosyas bir kez daha kaynak koda eklenmek istenirse artk _HEADER1_H simgesel deimezi tanmlanm olduundan #endif nilemci komutuna kadar olan ksm artk kaynak koda verilmez.

1. Koullu derleme komutlar sklkla debug amacyla kullanlr. Program yazlrken debug amacyla programa baz kodlar eklenir. Ancak programn son srmnde hata aramaya ynelik kodlarn bulunmas istenmez. nk bu kodlarn programn alma zamannda getirecei ek maliyet istenmez. Program iindeki belirli kod paralAr yalnzca debug srmde derlenir: #if DEBUG /***/ #endif 2. Baz programlarn birden fazla iletim sisteminde almas istenebilir. Kaynak kodun belirli ksmlar nilemci program tarafndan iletim sistemine gre seilerek derleyiciye verilir: #if defined(WINDOWS) /***/ #elif defined (DOS) /***/ #elif defined(OS2) /***/ #endif

Koullu Derlemeye likin nilemci Komutlar Nerelerde Kullanlr?

501

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

3. Bazen de ayn program farkl derleyiciler ile derlenir. #ifdef __STDC__ /***/ #else /***/ #endif Koullu derlemeye ilikin nilemci komutlarnn kullanlmasna ilikin bir program paras rnei aada veriliyor: #if DLEVEL > 5 #define SIGNAL 1 #if STACKUSE == 1 #define STACK #else #define STACK #endif #else #define SIGNAL 0 #if STACKUSE == 0 #define STACK #else #define STACK #endif #endif #if DLEVEL == 0 #define STACK #elif DLEVEL == 1 #define STACK #elif DLEVEL > 5 display(debugptr); #else #define STACK 200 #endif

200 50

200 50

0 100

Yukardaki rnekte birinci #if blounun altnda iki ayr #if #else yaps bulunur. DLEVEL simgesel deimezinin 5'den byk olup olmamasna gre deerine gre nilemci tarafndan dor ksm ya da yanl ksm ele alnr. #elif nilemci komutunun da yer ald ikinci #if blounda ise DLEVEL simgesel deimezinin deerine gre 4 seenekten biri ele alnr. DLEVEL simgesel deimezinin deerine bal olarak STACK simgesel deimezi 0, 100 ya da 200 olarak tanmlanr. DLEVEL simgesel deimezinin deerinin 5'den byk olmas durumunda ise display(debugptr); deyimi derleyiciye verilir. Bu durumda STACK simgesel deimezi tanmlanmaz.

502

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

assert Makrosu

assert, program gelitirirken programcnn gelitirme sreci iinde bir takm olas bcekleri farketmesi iin kullanlan bir makrodur. C'nin standart balk dosyalarndan assert.h iinde tanmlanmtr. assert makrosu, makroya argman olarak bir ifadenin verilmesiyle kullanlr. rnein: assert(p != NULL); Ayra iindeki ifade, doru olduu ya da doru olmas gerektii dnlen bir durum belirtir. assert makrosuna verilen ifadenin saysal deeri sfr d bir deerse, yani snanan nerme doruysa, assert makrosu hibir ey yapmaz. Ancak makro ifadesinin deeri sfrsa, yani ifadeye konu nerme mantksal olarak yanl ise assert makrosu standart abort() ilevini ararak program sonlandrr. abort ilevi tpk standart exit() ilevi gibi arld zaman program sonlandrr. Ancak exit ilevinin yapt bir takm geri alma ilemlerini abort ilevi yapmaz. abort ilevi program sonlandrdnda stdout akmna "Abnormal program termination" yazsn basar. assert makrosu baarsz olduunda abort ilevini armadan nce doruluu snanan ifadeyi ve assert ileminin hangi dosyada ve satrda olduu bilgisini ekrana yazar: #include <stdio.h> #include <assert.h> int main() { int x, y; printf("iki sayi giriniz : "); scanf("%d%d", &x, &y); assert(y != 0); printf("%d / %d = %d\n", x, y, x / y); } return 0;

Yukardaki program inceleyin. Klavyeden y deikenine girilen deer 0 ise, assert makrosuna verilen ifade yanl olduundan, program abort ilevinin arlmasyla sonlandrlr. Program altrarak, program sonlandrldnda ekrana baslan yazy inceleyin. assert, assert.h dosyas iinde aadakine benzer biimde tanmlanm bir makrodur. #ifndef NDEBUG #define assert(exp) if(!(exp)){fprintf(stderr,"Assertion failed: %s,file %s, line %d\n",\ #exp, __FILE__, __LINE__);abort();} #else #define assert(exp) #endif Bu makro unlar yapar: Eer programc assert.h dosyasnn ieriinin boaltld yerden daha yukarda olan kaynak kod alan iinde NDEBUG simgesel deimezini tanmlamamsa btn assert makrolar yerine kontrol kodlar yerletirilir. Kontrol kodu grld gibi ifadenin saysal deeri sfr ise abort ilevi ile program sonlandrlr. Eer programc assert.h dosyasnn eklenmesinden nce NDEBUG simgesel deimezini tanmlam ise grld gibi btn

503

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

assert makrolar kaynak koddan silinir. Btn assert makrolarn kaynak koddan silmek iin programn kaynak kodun tepesine #define NDEBUG komutu yerletirilmelidir.

assert makrosu programn gelitirilme aamasnda olas hatalar yakalamak iin programc tarafndan kaynak koda eklenen bir makrodur. Programc kodunu yazarken bir bcek karsnda oluabilecek durumlar iin assert mokrolarn yerletirir. Programn gelitirilme srecinde denemeler srasnda assert mokrosu tarafndan program sonlandrlrsa programc olmamas gereken bir durumun olduunu anlar ve bu durumun bir bcek nedeni ile olutuunu dnr. phesiz bcek tam olarak assert mokrosunun bulunduu yerde olmayabilir. Baka yerde yaplan hatalar dolays ile bcek olumu olabilir. Bu durum assert makrosu tarafndan bir ipucu olarak ele geirilir. assert makrosuna yakalanldnda programc kodunu aka gre incelemeli ve bcein nereden kaynaklandn saptamaya alr. assert makrosu ile yaplan kontroller aslnda dzgn alan bir program iin gereksiz olan kontrollerdir. te programn assert makrolarnn kontrol kodlar yerletirdii (yani NDEBUG simgesel deimezinin tammlanmad) versiyonuna "hata arama srm" (debug versiyonu) denir. Hata arama srm assert makrolar ile kaynak kodun imi olduu srmdr. Programc kodunda hibir bcek olmadndan emin ise program teslim etmeden nce NDEBUG simgesel deimezini tanmlayarak program son kez derler. Buna programn "srm biimi" (release version) denir. Byk programlar debug versiyonunda oluturulmal, son aamada release versiyonu elde edilmelidir.

assert Makrosu Neden Kullanlr?

assert makrolar programn ticari srmlerinde ya da kodda kalmas gereken durumlar iin kullanlmamaldr. rnein malloc ve fopen ilevlerinin geri dn deerleri programn hem debug srmnde hem de release srmnde kontrol edilmelidir. assert makrolar u durumlarda kullanlabilir: lev parametrelerinin olmamas gereken anormal deerleri almas durumu assert makrosu ile kontrol edilebilir. zellikle ilev parametresi olan gstericilerin hemen ana bloun giriinde NULL adresi olma durumu kontrol edilebilir, nk bceklerden dolay gstericilerin NULL deerine dmesi sk rastlanlan bir durumdur. rnein sralama ilemi yapan bir ilevin tanm iinde assert makrosu yle kullanlabilir: void sort_array(int *pArray, size_t size) { int i, k, temp; /**/ assert(pArray != NULL); /**/ } Program iinde programn dzenlenme sistemine gre asla olmamas gereken bir durum oluuyorsa, bir bcekten phelenilmesi gerektii durumlarda assert makrosu kullanlmaldr. assert ifadeleri ok uzun tutulmamaldr. nk bu durumda assert ifadesinin baarszl durumunda neden anlalamayabilir. rnein: assert(row1 < 25 && col1 < 80 && row2 < 25 && col2 < 80); yerine aadaki kodun kullanlmas daha anlamldr:

assert Makrosu Nerelerde Kullanlr?

504

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

assert(row1 < 25 && col1 < 80); assert(row2 < 25 && col2 < 80);

505

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

LEV GSTERCLER
altrlabilen bir programn diskteki grntsne program denir. Bir program altrlmak zere bellee yklendiinde, yani alr duruma getirildiinde process ya da task biiminde isimlendirilir. Yani program diskteki durumu, process ise almakta olan bir program anlatr. altrlabilen bir program ierik olarak ksmdan oluur:

altrlabilen bir dosyann iletim sistemi tarafndan altrlabilmesi iin, nce diskten RAM'e yklenmesi gerekir. letim sisteminin program diskten alp bellee ykleyip altran ksmna ykleyici (loader) denir. Normal olarak bir programn bellee yklenmesi iin, programn toplam byklnn bellekteki bo alandan kk olmas gerekir. Ancak UNIX, Windows gibi sistemlerde programlar kk bir bellek altnda altrlabilir. Bu zellie "sanal bellek" (virtual memory) zellii denir. Sanal bellek kullanm ile nce programn kk bir ksm RAM'e yklenir. letim sistemi, program alrken programn aknn kod ya da data bakmndan RAM'de olmayan bir ksma getiini anlar. Programn RAM'deki ksmn boaltarak bu kez gereken ksmn RAM'e ykler. Yani program paral olarak altrlr. DOS iletim sistemi sanal bellek kullanm yoktur. DOS sisteminin program altrmaktaki bellek miktar en fazla 640KB kadardr. C'deki tm statik mrl varlklar, yani global deikenler, statik yerel deikenler, dizgeler altrlabilir dosyann "veri" (data) blmne yerletirilir. Statik mrl deikenler bu blme ilkdeerleriyle yerletirilir. Yerel deikenler ile parametre deikenleri altrlabilir dosyann "yn" (stack) blmnde geici olarak yaratlrlar. Programn tm ilevleri derlenmi olarak "kod" (code) blmnde bulunur. Nesnelerin nasl adresleri varsa ilevlerin de adresleri vardr. Bir ilevin adresi, o ilevin makine kodlarnn bellekteki balang adresidir. levlerin balang adresleri ilev gstericileri denilen zel gstericilere yerletirilebilir. Bir ilev gstericisine geri dn deeri ve parametreleri belirtilen trden olan bir ilevin adresi atanabilir.

Bir ilev gstericisi zel bir szdizimi ile tanmlanr: int (*fp)(void);

lev Gstericisi Deikenlerin Tanmlanmas

Yukardaki deyim ile ismi fp olan bir ilev gsterici deikeni tanmlanyor. fp gsterici deikeni, geri dn deeri int trden olan, parametre deikeni olmayan bir ilevi gsterebilir. Bir baka deyile, fp gsterici deikenine geri dn deeri int trden olan, parametre deikeni olmayan bir ilevin adresi atanabilir. Tanmlamada '*' atomu ve bunu izleyen gsterici isminin ayra iine alndn gryorsunuz. Bu ayracn soluna ilev gstericisinin gsterecei ilevin geri dn deerinin tr yazlr. Ayracn sandaki ayra iinde, ilev gstericisinin gsterecei ilevin parametre deikenlerinin tr yazlr. Bir baka rnek: int (*fptr) (int, int);

507

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukarda tanmlanan fptr isimli gsterici deikene geri dn deeri int trden olan, parametreleri (int, int) olan bir ilevin adresi atanabilir. Tanmlamada gsterici deikenin ismi eer ayra iine alnmazsa, bir ilev gstericisi tanmlanm olmaz. Bir adres trne geri dnen bir ilev bildirilmi olur: int *f(int, int); Yukarda, ismi f olan geri dn deeri (int *) tr olan parametreleri (int, int) olan bir ilev bildirilmitir.

Bir dizi isminin, bir ileme sokulduunda, otomatik olarak dizinin ilk elemannn adresine dntrldn biliyorsunuz. Benzer ekilde bir ilev ismi de, bir ileme sokulduunda derleyici tarafndan otomatik olarak ilgili ilevin adresine dntrlr. lev isimlerine, yazlmsal olarak ilevlerin adresleri gzyle baklabilir. Bir ilev ismi adres ilecinin de terimi olabilir. Bu durumda yine ilevin adresi elde edilir. Yani func bir ilev ismi olmak zere func ile &func edeer ifadeler olarak dnlebilir. Her iki ifade de func ilevinin adresi olarak kullanlabilir.

lev simleri

lev ar leci

ilev ar ileci tek terimli sonek konumunda bir iletir. le ncelik tablosunun en st dzeyindedir. Bu ilein terimi bir ilev adresidir. le, programn akn o adrese yneltir, yani o adresteki kodu altrr. rnein: func() Burada ilev ar ilecinin terimi func adresidir. le, func adresinde bulunan kodu, yani func ilevini altrr. lev ar ilecinin rettii deer, arlan ilevin geri dn deeridir. rnein: a = add(10, 20); Burada add ifadesinin tr geri dn deeri int parametresi (int, int) olan bir ilev adresidir. Yani add ifadesinin int (*)(int, int) trne dntrlr. add(10, 20) gibi bir ilev arsnn oluturduu ifade ise int trdendir.

Bir ilev gstericisinin gsterdii ilev iki ayr biimde arlabilir. pf bir ilev gstericisi deiken olmak zere, bu gstericinin gsterdii ilev pf()

levlerin lev Gstericileri ile arlmas

508

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

biiminde, ya da (*pf)() biiminde arlabilir. Birinci biim daha doal grnmekle birlikte, pf isminin bir gsterici deikenin ismi mi, yoksa bir ilevin ismi mi olduu ok ak deildir. kinci biimde, *pf ifadesinin ncelik ayrac iine alnmas zorunludur. nk ilev ar ilecinin ncelik seviyesi, ierik ilecinin ncelik seviyesinden daha yksektir. Aadaki program derleyerek altrn: #include <stdio.h> void func() { printf("func()\n"); } int main() { void (*pf)(void) = func; pf(); (*pf)(); } return 0;

main ilevi iinde tanmlanan pf isimli ilev gstericisi deikene func isimli ilevin adresi atanyor. Daha sonra pf'nin gsterdii ilev, yani func ilevi iki ayr biimle arlyor.

Bir ilev gstericisi gsterici aritmetii kurallarna uymaz. lev adresleri dier adresler gibi tamsaylarla toplanamaz. lev gstericileri ++ ya da -- ilecinin terimi olamaz. Zaten gsterici aritmetiinin ilev adresleri iin bir anlam da olamazd. Bir ilev adresine bir tamsay toplayp bellekte bir sonraki ilevin adresine ulamak nasl mmkn olurdu? lev adresleri ierik ilecinin terimi olabilir. Byle ifadeler ile, yalnzca ilev ars yaplabilir. Bu gstericiler karlatrma ilelerinin de terimi olabilir. rnein, iki ilev gstericisinin ayn ilevi gsterip gstermedii == ya da != ileleriyle karlatrlabilir.

lev Gstericileri ve Gsterici Aritmetii

Bir ilevin parametre deikeni ilev gstericisi olabilir. Aadaki rnei inceleyin: #include <stdio.h> void func(void (*pf)(void)) { pf(); } void f1() { printf("f1()\n"); } void f2() { printf("f2()\n"); }

lev Gstericilerinin lev Parametreleri Olarak Kullanlmas

509

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int main() { func(f1); func(f2); return 0; } Yukardaki programda, func isimli ilevin parametre deikeni bir ilev gstericisidir. func ilevinin parametre deikeni olan gsterici, geri dn deeri ve parametre deikeni olmayan bir ilevi gsterebilir. levlerin parametre deikenleri, deerlerini ilev arlarndaki argmanlardan aldna gre func ilevine argman olarak, geri dn deeri ve parametre deikeni olmayan bir ilev adresi gnderilmelidir. func ilevi iinde, ilevin parametresi olan fp isimli gstericinin gsterdii ilev arlr. f1 ve f2 geri dn deeri ve parametre deikeni olmayan ilevlerdir. main ilevi iinde func ilevinin nce f1 ilevin adresi ile daha sonra da f2 ilevinin adresi ile arldn gryorsunuz. Bu durumda func(f1); arsyla nce func ilevi arlr. func ilevi iinde de f1 ilevin arlmas salanr.

lev gstericileri, bir ilevin belirli bir ksmnn dardan deitirilmesine olanak salar. Yani ilev, bir amac gerekletirecek belirli ilemleri yapmak iin, dardan adresini ald bir ilevi arabilir. Bylece ilevi aran kod paras, ard ilevin iinin belirli bir ksmnn, kendi istedii gibi yaplmasn salayabilir. Bylece yazlan kaynak kod miktar da azalabilir. Kaynak kodlarnn byk bir ksm ayn olan ok sayda ilev yazmak yerine, ilev gstericisiyle arlan tek bir ilev yazmak mmkn olabilir. lev gstericileri ile arlan ilevler, genelletirilmi ilevlerdir. Dardan adreslerini aldklar ilevlerin arlmalaryla ilevleri daha zel hale getirilmi olur.

lev Gstericileri Niin Kullanlr

Baz ilevler, zellikle gsterici parametresi alan ilevler, belirli bir tre dayal olarak alr. rnein, elemanlar int trden olan bir diziyi sralayan bir ilev, elemanlar double trden olan bir diziyi sralayamaz. Bu tr durumlarda ayn kodu, farkl trlere gre yeniden yazmak gerekir. Trden bamsz olarak tek bir ilevin yazlabilmesi mmkn olabilir. Trden bamsz ilem yapan ilevlerin gsterici parametreleri void * trnden olmaldr. Ancak parametrelerin void * trnden olmas yalnzca ar asndan kolaylk salar. levi yazacak olan programc, yine de ileve geirilen adresin trn saptamak zorundadr. Bunun iin ileve bir tr parametresi geirilebilir. rnein herhangi bir trden diziyi sralayacak ilevin bildirimi aadaki gibi olsun: void *gsort(void *parray, size_t size, int type); imdi ilevi yazacak kii type isimli parametreyi bir switch deyiminin ayrac iine alarak, dardan adresi alnan dizinin trn saptayabilir. Bu yntem C'nin doal trleri iin alsa da programcnn kendi oluturduu trden diziler iin doru almaz. Byle genel ilevler ancak ilev gstericileri kullanlarak yazlabilir. imdi bir dizinin en byk elemann adresiyle geri dnen bir ilevi trden bamsz olarak yazmaya alalm. Dizi trnden bamsz olarak ilem yapan ilevlerin, genel parametrik yaps genellikle aadaki gibi olur: Dizinin balang adresini almak iin, void trden bir gsterici. Dizinin eleman saysn alan, size_t trnden bir parametre.

Trden Bamsz lem Yapan levler

510

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Dizinin bir elemann byte uzunluunu yani sizeof deerini alan size_t trnden bir parametre. Dizinin elemanlarn karlatrma amacyla kullanlacak bir ilevin balang adresini alan, ilev gstericisi parametre. Bir dizinin en byk elemannn adresini bulan genel ilevin parametrik yaps yle olabilir: void *get_max(const void *pArray, size_t size, size_t width, int (*cmp)(const void *, const void *)); Bu tr ilevlerin tasarmndaki genel yaklam udur: lev herbir dizi elemannn adresini, gsterici aritmetiinden faydalanarak bulabilir. Ancak dizi elemanlarnn tr bilinmediinden, ilev dizinin elemanlarnn karlatrma ilemini yapamaz. Bu karlatrmay, ilev gstericisi kullanarak ilevi aran kod parasna yaptrr. levi aracak programc, karlatrma ilevinin dizinin herhangi iki elemannn adresiyle arlacan gznne alarak, karlatrma ilevini yle yazar: Karlatrma ilevi, karlatrlacak iki nesnenin adresini alr. levin birinci parametresine adresi alnan nesne, ikinci parametreye adresi alnan nesneden daha bykse, ilev pozitif herhangi bir deere, kkse negatif herhangi bir deere, bu iki deer eitse sfr deerine geri dner. Bu, standart strcmp ilevinin sunduu anlamadr. Aadaki program inceleyin: #include <stdio.h> #include <string.h> #define MAX_NAME_LEN 20

typedef struct tag_Person { char name[MAX_NAME_LEN]; int no; }Person; typedef unsigned char Byte; void *get_max(const void *parray, size_t size, size_t width, int (*fp)(const void *, const void *)) { Byte *pb = (Byte *)parray; void *pmax = (void *)parray; size_t k; for (k = 1; k < size; k++) if (fp(pb + k * width, pmax) > 0) pmax = pb + k * width; return pmax;

int cmp_int(const void *vp1, const void *vp2) { return *(const int *)vp1 - *(const int *)vp2; } int cmp_person_name(const void *vp1, const void *vp2) { return strcmp(((const Person *)vp1)->name, ((const Person *)vp2)>name); }

511

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

int cmp_person_no(const void *vp1, const void *vp2) { return ((const Person *)vp1)->no - ((const Person *)vp2)->no; } int main() { int a[10] = {3, 8, 4, 7, 6, 9, 12, 1, 9, 10}; Person per[10] = { {"Ali Serce", 123}, {"Kaan Aslan", 563}, {"Ahmet Adlori", 312},{"Necati Ergin", 197}, {"Guray Sonmez", 297}, {"Erdem Eker", 144}, {"Nuri Yilmaz", 765}, {"Tayfun Tan", 117}, {"Demir Kerim", 222},{"Can Mercan", 12}}; Person *p_person; int *iptr; iptr = (int *)get_max(a, 10, sizeof(int), cmp_int); printf("int dizinin en buyuk elemani %d\n", *iptr); p_person = (Person *) get_max(per, 10, cmp_person_name); printf("per dizisinin en buyuk elemani p_person->name, p_person->no); p_person = (Person *) get_max(per, 10, printf("per dizisinin en buyuk elemani p_person->name, p_person->no); return 0; } Yukardaki programda get_max isimli ilev bir dizinin balang adresini, boyutunu, dizinin bir elemannn uzunluu ile dizinin elemanlarn karlatrma ileminde kullanlacak ilevin adresini alyor. lev arsyla dizi adresi void trden bir gstericiye alnyor. Ancak ilev iinde gsterici aritmetiinden faydalanmak amacyla bu adres char trden bir gstericiye aktarlyor. Bir elemann boyutu bilindiine gre bu deer dizinin balang adresine eklenerek dizinin bir sonraki elemannn adresi bulunabilir deil mi? Karlatrma ilevi, karlatrma ilemini yapabilmek iin iki nesnenin adresini alyor. Bylece get_max ilevi iinde, karlatrma ilevi, parametre olan ilev gstericisi ile doru argmanlarla arlabiliyor. Dizinin en byk elemannn adresini bulmak iin yine bilinen algoritma kullanlyor. Ancak dizinin iki elemannn deerini karlatrmak iin dardan adresi alnan ilev arlyor. Daha sonra ayr karlatrma ilevinin tanmlanm olduunu gryorsunuz. Tm karlatrma ilevlerinin parametre deikenleri (const void *, const void *) dr. Bylece, bu karlatrma ilevlerine get_max ilevi iki adres gnderdiinde bir tr uyumsuzluu sorunu ortaya kmaz. main ilevi iinde bir int trden bir de Person trnden dizi tanmlanyor. Bu dizilere ilkdeer verilmi olduunu gryorsunuz. Daha sonra get_max ilevi arlarak dizilerin en byk elemanlarnn adresleri bulunuyor. cmp_person_name ilevi Person trnden iki nesneyi isimlerine gre karlatrrken, cmp_person_no ilevi ise Person trnden iki nesneyi, numaralarna gre karlatryor. sizeof(Person), (isme gore) %s %d\n", sizeof(Person), cmp_person_no); (numaraya gore) %s %d\n",

Her trl sralama algoritmasnda karlatrma ve deer takas etme sz konusudur. Bir dizinin tr bilinmese bile dizinin iki eleman takas edilebilir. Karlatrma ileminini yapacak ilevin adresi aran kod parasndan alnrsa, trden bamsz sralama ilemi gerekletirilebilir. Aadaki rnei inceleyin:

Trden Bamsz Sralama

512

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <stdlib.h> #include <string.h> #define SIZE 10

typedef unsigned char BYTE; void gswap(void *vp1, void *vp2, size_t nbytes) { unsigned char *p1 = (unsigned char *)vp1; unsigned char *p2 = (unsigned char *)vp2; unsigned char temp; while (nbytes--) { temp = *p1; *p1++ = *p2; *p2++ = temp; }

void bsort(void *vparray, size_t nelem, size_t width, int (*compare)(const void *, const void *)) { size_t i, j; BYTE *base = (BYTE *)vparray; for (i = 0; i < nelem - 1; i++) for (j = 0; j < nelem - 1 - i; j++) if (compare(base + j * width, base + (j + 1) * width) > 0) gswap(base + j * width, base + (j + 1) * width, width);

int cmp_int(const void *vp1, const void *vp2) { return *(const int *)vp1 - *(const int *)vp2; } int cmp_double(const void *vp1, const void *vp2) { const double *dp1 = (const double *) vp1; const double *dp2 = (const double *) vp2; if (*dp1 < *dp2) return -1; return *dp1 > *dp2; } int main() { int k; double ad[SIZE] = {4.3, 7.4, 2.7, 8.88, 66.99, 4.8, 90.67, 23.87, 7.89, 10.87}; int ai[SIZE] = {12, 4, 56, 45, 23, 60, 17, 56, 29, 1}; bsort(ad, SIZE, sizeof(double), cmp_double); bsort(ai, SIZE, sizeof(int), cmp_int); for (k = 0; k < SIZE; ++k) printf("%.2lf ", ad[k]);

513

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

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

g_swap isimli ilev, balang adreslerini ald belirli uzunlukta iki bellek blounun ieriini takas ediyor. Bu ilev, trden bamsz sralama ilemini gerekletiren bsort isimli ilev tarafndan arlyor. bsort ilevinin parametrik yapsnn, daha nce yazlan get_max isimli ileve benzer olduunu gryorsunuz. levin birinci parametresi sralanacak dizinin adresi, ikinci parametresi dizinin eleman says, nc parametresi dizinin bir elemannn uzunluu ve drdnc parametresi ise dizinin elemanlarn karlatrma ilemini gerekletirecek ilevin adresidir. bsort ilevi iinde "kabarck sralamas" algoritmas kullanlyor. Dizinin ardk iki elemannn byklk-kklk iliikisi dardan adresi alnan ilevin, parametre deikeni olan gsterici ile arlmasyla elde ediliyor. Eer dizinin bu iki eleman doru yerde deilse bu elemanlar daha nce tanmlanan gswap ilevinin arlmasyla takas ediliyor. Byle bir ilevle her trden dizi sraya sokulabilir. bsort ilevi yalnzca karlatrma ilemini gerekletirecek uygun bir ilevin adresine gereksinim duyar. nk bu ilev dardan adresi gelen dizinin elemanlarnn trn bilemez. lev gstericileri tanmlarken parametre ayracnn iinin bo braklmas ile parametre ayracnn ierisine void yazlmas farkl anlamlardadr. Parametre ayracnn ii bo braklrsa, byle tanmlanan ilev gstericisi deikenlere, geri dn deeri uygun herhangi bir parametre yapsna sahip olan ilevin adresi yerletirilebilir. Oysa void yazlmas, parametresi olmayan ilevlerin adreslerinin yerletirilebilecei anlamna gelir. Parametre ayracnn ii bo braklarak, arlacak ilevlerin parametrik yaps dardan istendii gibi dzenlenebilir.
[C++ dilinde parametre ayracnn iinin bo braklmas, ilev gstericisi deikenin, gsterecei ilevin parametreye sahip olmad bilgisini iletir. C++'da ayracn iine void yazlmasyla, burann bo braklmas arasnda bir anlam fark yoktur.]

Standart qsort ilevi, yukarda tanmlanan bsort ileviyle ayn parametrik yapya sahiptir. Yazdmz bsort ilevi kabarck sralamas algoritmasn kullanyordu. Standart qsort ilevi ise "quick sort " algoritmasn kullanr. qsort ilevinin stdlib.h balk dosyas iindeki bildirimi aadaki gibidir: void qsort(void *vp, size_t size, size_t width, int (*cmp)(const void *, const void *)); levin birinci parametresi sraya dizilecek dizinin balang adresi, ikinci parametresi dizinin eleman says, nc parametresi dizinin bir elemann uzunluu ve son parametresi de karlatrma ilevinin adresidir. Karlatrma ilevi, birinci parametresine adresi gnderilen elemann deeri, ikinci parametresine adresi gnderilen elemandan bykse pozitif herhangi bir deere, kkse negatif herhangi bir deere, eitse sfra geri dnecek biimde yazlrsa kkten bye sraya dizme, ters yazlrsa bykten ke sraya dizme gerekleir. Standartlar ncesi C derleyicilerinde qsort ilevinin bildiriminde karlatrma ilevinin adresini alacak gstericinin parametre ayracnn ii bo braklmtr. Bu durum karlatrma ilevini yazmay kolaylatrr. stenirse qsort levinda byle bir kolaylk elde etmek iin stdlib.h balk dosyas iinde bulunan bildirimde ilev gsterici ayrac boaltlabilir: void qsort(void *parray, size_t size, size_t width, void(*fp)());

Standart qsort levi

514

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

qsort ilevi ile bir gsterici dizisinin sralanmasna dikkat edilmelidir. Aadaki program inceleyin ve sralama ileminin neden yaplamadn anlamaya alnz: #include <stdio.h> #include <string.h> #include <stdlib.h> #define SIZE 20

int cmp(const void *vp1, const void *vp2) { const char *p1 = (const char *)vp1; const char *p2 = (const char *)vp2; return strcmp(p1, p2); } int main() { char *names[SIZE] = {"Ali", "Veli", "Hasan", "Necati", "Burcu", "Kaan", "Selami", "Salah", "Nejla", "Huseyin", "Derya", "Funda", "Kemal", "Burak", "Ozlem", "Deniz", "Nuri","Metin", "Guray", "Anil"}; int k; qsort(names, SIZE, sizeof(char *), cmp); for (k = 0; k < SIZE; ++k) printf("%s ", names[k]); } return 0;

Hatay grebildiniz mi? Karlatrma ilemini gerekletirecek cmp isimli ilev yanl yazlm. Bu ileve char * trnden iki nesnenin adresi gnderileceine gre, ilev iinde yaplan tr dntrme ileminde, hedef tr char* tr deil char** tr olmalyd. (char *) trnden bir nesnenin adresi olan bilgi (char **) trndendir. lev aadaki gibi yazlmalyd: int cmp(const void *vp1, const void *vp2) { const char * const *p1 = (const char * const *)vp1; const char * const *p2 = (const char * const *)vp2; } return strcmp(*p1, *p2);

lev Gstericisi Dizileri


void (*fparray[10])(void);

Elemanlar ilev gstericileri olan diziler de tanmlanabilir:

Yukardaki tanmlama ile fparray, her bir eleman, geri dn deeri ve parametre deikeni olmayan bir ilevi gsteren 10 elemanl bir dizidir. fparray[2] ifadesiyle bu dizinin nc elemanna ulalr ki bu ifadenin tr

515

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

void(*)(void) trdr. Bu gstericinin gsterdii ilev fparray[2](); biiminde ya da (*fparray[2])() ; biiminde arlabilir. Byle bir dizinin elemanlarna da ilkdeer verilebilir: void (*fparray[5])(void) = {f1, f2, f3, f4, f5}; Bylece, dizinin her bir elemannn bir ilevi gstermesi salanabilir. Artk bir dng deyimi yardmyla gsterici dizisinin elemanlarna ulalarak dizinin elemanlarnn gsterdii ilevler arlabilir. Aadaki kodu derleyerek altrn: #include <stdio.h> void void void void void f1() f2() f3() f4() f5() {printf("f1()\n");} {printf("f2()\n");} {printf("f3()\n");} {printf("f4()\n");} {printf("f5()\n");}

int main() { void (*fparray[5])(void) = {f1, f2, f3, f4, f5}; int k; for (k = 0; k < 5; ++k) fparray[k](); } return 0;

Bir ilevin geri dn deeri de bir ilev adresi olabilir. Aadaki rnei inceleyin: #include <stdio.h> void foo() { printf("foo()\n"); } void (*func(void))(void) { void (*fp)(void) = foo; } return fp;

Bir levin Bir lev Adresini Geri Dndrmesi

int main() { func()(); return 0; }

516

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Yukardaki programda tanmlanan func isimli ilevin parametre deikeni yok. func ilevinin geri dn deeri, -geri dn deeri ve parametre deikeni olmayan- bir ilev adresidir. Yani func ilevi, kendisini aran kod parasna byle bir ilevin adresini iletiyor. func ilevi iinde tanmlanan yerel fp gsterici deikeni, foo isimli ilevi gsteriyor. main ilevi iinde yer alan func()(); deyiminde nce func ilevi arlr. Bu ardan elde edilen geri dn deeri, foo ilevinin adresi olur. kinci ilev ar ileci de foo ilevin arlmasn salar.

Bir ilev tr iin typedef bildirimi yaplabilir: typedef double FuncDouble(int);

levler in Yaplan typedef Bildirimleri

Yukardaki bildirimden sonra FuncDouble, geri dn deeri double trden olan, tek parametresi int trden olan bir ilev trdr. FuncDouble *fptr, *fpa[10]; Yukardaki tanmlama ile fptr, geri dn deeri double ve parametresi int trden olan bir ilevi gsteren gsterici deikendir. fpa ise fptr gibi gstericilerin oluturduu 10 elemanl bir dizidir. fpa dizisinin herbir eleman bir ilev gstericisidir. Elemanlar FuncDouble trden olan bir deiken tanmlanamaz. C FuncDouble func; /* Geersiz */

Ancak bu trden bir ilevin bildiriminde bir sorun yoktur: extern FuncDouble func; Yukardaki deyimle, geri dn deeri double trden olan, parametre deikeni int trden olan bir ilev bildiriliyor. Uygulamalarda daha ok bir ilev adresi tr iin typedef bildirimi yaplr: typedef int (*CmpFunc)(const void *, const void *); Yukardaki bildirimden sonra CmpFunc, geri dn deeri int trden olan, iki parametresi de const void * trnden olan bir ilev adresi trdr: CmpFunc fcmp1, fcmp2, fcmpa[10]; Yukardaki tanmlama ile fcmp1 ve fcmp2, geri dn deeri int trden olan, iki parametresi de const void * trnden olan bir ilevi gsterecek deikenlerdir. fcmp1 ve fcmp2 ilev gstericisi deikenlerdir. fcmpa ise, her bir eleman fcmp deikeninin trnden olan, 10 elemanl bir dizidir. Bir ilev adresi trnn, bir ilevin parametre deikeninin tr ya da ilevin geri dn deerinin tr olmas bildirimleri olduka kark hale getirir. Okuma ve yazma kolayl asndan ilev adreslerine ilikin trlere typedef bildirimleriyle yeni isimler verilir. Aadaki rnei inceleyin:

517

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <string.h> typedef char *(*FPTR)(const char *, int); FPTR func(FPTR ptr) { return ptr; } int main() { char str[] = "Necati Ergin"; puts(func(strchr)(str, 'E')); return 0; } Yukardaki programn altrlmasyla ekrana Ergin yazs yazdrlr.

518

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

ZYNELEMEL LEVLER
Bir ilev baka bir ilevi ard gibi kendisini de arabilir. Bu tr ilevlere zyinelemeli ilev (recursive functions) denir. Algoritmalar bu zellie gre ayr gruba ayrlabilir: 123zyinelemeli olmayan ilevlerle yazlabilen algoritmalar. Hem zyinelemeli olmayan hem de zyinelemeli ilevlerle yazlabilen algoritmalar. Yalnzca zyinelemeli ilevlerle yazlan algoritmalar.

zyinelemeli ilevlerle yazlmas gereken tipik algoritmalar unlardr: Dizin aacn dolaan algoritmalar Parsing algoritmalar Aa algoritmalar zel amal pek ok algoritma Bir algoritmann kendi kendisini aran ilevlerle yazlp yazlamayacann lt nedir? Algoritmada ilerlendiinde yine ilk batakine benzer bir durumla karlalyorsa byk olaslkla bu algoritma kendi kendini aran bir ilevle yazlabilir. Bir ilev kendini ardnda ilevin btn yerel deikenleri yeniden yaratlr. Ayn durum ilevin parametre deikeni iin de sz konusudur. Bu yzden zyinelemeli ilevler genellikle yn alann dier ilevlerle karlatrldnda daha ok kullanr. zyinelemeli ilevler ilemi gerekletirmek amacyla bir sre kendi kendini arr. Daha sonra ilevlerden k ilemleri balar, yani arlan tm ilevlerden geri klmaya balanr.

Aada faktriyel hesaplayan dngsel (iterative) bir ilev yazlyor: int fact(int n) { int result = 1; while (n) result *= n--; return result;

zyinelemeli levlere Tipik rnekler

Ayn ilev zyinelemeli olarak da yazlabilir: int fact(int n) { if (n == 0 || n == 1) return 1; return n * factorial(n - 1); }

levin ak yledir: n n n n n = = = = = 5 4 3 2 1 iin iin iin iin iin val = 5 * val = 4 * val = 3 * val = 2 * return 1; factorial(4); factorial(3); factorial(2); factorial(1);

519

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Dngsel yapyla yazlan faktriyel ilevinin daha verimli olduu sylenebilir. Aada bir tamsayy ikilik say sisteminde yazdran dngsel yapl bir ilev tanmlanyor: #include <stdio.h> void bitprint(int i) { int k; for (k = sizeof(int) * 8 - 1; k >= 0; --k) putchar((i >> k & 1) + '0');

lev zyinelemeli olarak aadaki biimde tanmlanabilir: #include <stdio.h> void bitprint(int i) { if (i == 0) return; bitprint(i >> 1); putchar(i & 1 + '0'); } Yine zyinelemeli olmayan biimin daha verimli olduu sylenebilir. Aada bir yazy tersten yazdran dngsel yapl bir ilev tanmlanyor: #include <stdio.h> void putsrev(const char *str) { int i; for (i = strlen(str) - 1; i >= 0; --i) putchar(str[i]); } putchar('\n');

lev zyinelemeli olarak aadaki biimde tanmlanabilir: #include <stdio.h> void putsrev(const char *str) { if (*str == '\0') return; putsrev(str + 1); putchar(*str); } Aada tanm verilen ters_yaz isimli ilev klavyeden girilen bir yazy tersten yazdryor: #include <stdio.h> void ters_yaz() { char ch;

520

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

if ((ch = getchar())!='\n') ters_yaz(); putchar(ch);

Aada bir tamsayy yalnzca puthar ilevini kullanarak yazdran dngsel yapl bir ilev tanmlanyor: #include <stdio.h> #define SIZE 100

void printd(int i) { char s[SIZE]; int k = 0; int fnegative = 0; if (i < 0) { fnegative = 1; i = -i; } do { s[k++] = i % 10 + '0'; i /= 10; } while(i); if(fnegative) s[k++] = '-'; for (k--; k >= 0; --k) putchar(s[k]); } lev zyinelemeli olarak aadaki biimde tanmlanabilir: #include <stdio.h> void print(int n) { if (n < 0) { putchar('-'); n = -n; } if (n / 10) print(n / 10); putchar(n % 10 + '0'); } Aada bir tamsaynn belirli bir tamsay ssne geri donen power isimli ilev zyinelemeli olarak yazlyor: int power(int base, int exp) { if (exp == 0 ) return 1; } return base * power(base, exp - 1);

521

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Aada iki tamsaynn ortak blenlerinden en byne geri dnen get_gcd isimli ilev zyinelemeli olarak tanmlanyor: int get_gcd(int a, int b) { if (a >= b && a % b == 0) return b; if (a < b) return(get_gcd(b, a)); return get_gcd(b, a % b); } Aada tanmlanan fibonacci isimli ilev fibonacci serisinin n. terimine geri dnyor: int fibonacci(int n) { if (n == 1 || n == 2) return 1; return fibonacci(n - 1) + fibonacci(n - 2); } Ayn ii yapan dngsel yapda bir ilev ok daha verimli olurdu: int fibonacci(int n) { int x = 1; int y = 1; int k, result; for (k = 2; k < n; ++ k) { result = x + y; x = y; y = result; } return y;

Aada zyinelemeli olarak tanmlanan rakam_yaz ilevi bir tamsaynn her bir basaman ekrana yaz olarak basyor: #include <stdio.h> void rakam_yaz(int n) { static const char *rakamlar[ ] = { "Sifir", "Bir", "Iki", "Uc", "Dort", "Bes", "Alti", "Yedi", "Sekiz", "Dokuz" }; if (n > 9) rakam_yaz(n / 10); printf("%s ", rakamlar[n % 10]);

Aada bir tamsayy ekrana onaltlk say sisteminde yazdran to_hex isimli zyinelemeli bir ilev tanmlanyor: void to_hex(int n)

522

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

static const char *htab[ ] = { "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F" }; if (n > 15) to_hex(n / 16); printf("%s", htab[n % 16]); } Aada bir yazy ters eviren strrev_r isimli bir ilev tanmlanyor. #include <stdio.h> #include <stdlib.h> #define SIZE 100

void swapstr(char *str, int l, int r) { char temp; if (l >= r) return; temp = str[l]; str[l] = str[r]; str[r] = temp; } swapstr(str, l + 1, r - 1);

char *strrev_r(char *str) { swapstr(str, 0, strlen(str) - 1); } return str;

int main() { char str[SIZE]; printf("bir yazi girin : "); gets(str); printf("yazi = (%s)\n", str); strrev_r(str); printf("yazi = (%s)\n", str); } return 0;

phesiz yaznn ters evrilmesi zyinelemesiz bir ilevle de gerekletirilebilir. zyinelemeli yazlan strrev_r ilevinin parametresi olduunu gryorsunuz: Yaznn balang adresi, takas edilecek ilk ve son elemann indis deerleri. levin strrev ilevinde olduu gibi yaznn yalnzca balang adresini almas bir sarma ilev yazlarak salanyor. Sarma ilev (wrapper function) iin kendisini yapan ilev deildir. Sarma ilev ii yapan ilevi aran kk ilevlere denir. Aadaki programda tanmlanan revprint isimli ilev adresini ald bir yaznn szcklerini ters srayla ekrana yazdryor:

523

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

#include <stdio.h> #include <string.h> #define MAX_WORD_LEN 100 #define ARRAY_SIZE 1000 void revprint(const char *str) { char s[MAX_WORD_LEN + 1] = ""; int index = 0; static const char seps[] = " \n\t,.:;!?"; while (*str && strchr(seps, *str)) str++; if (!*str) return; while (!strchr(seps, *str)) s[index++] = *str++; s[index] = '\0'; if (*str == '\0') { printf("%s ", s); return; } revprint(str); printf("%s ", s); } int main() { char str[ARRAY_SIZE]; printf("bir cumle girin : "); gets(str); revprint(str); return 0;

524

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

DEKEN SAYIDA ARGMAN ALAN LEVLER


Cde deiken sayda argman alan ilevler tanmlanabilir. Deiken sayda argman alan bir ilevin bildirimi aadaki gibidir: [levin geri dn deeri]ilev ismi(parametre bildirimleri, ...); nokta "..." atomunun ismi ingilizce de "elipsis"tir. Bir ilevin deiken sayda argman alabilmesi iin son parametresinin nokta atomuyla bildirilmesi zorunludur. Byle ilevler istenen sayda argmanla arlabilir. Byle ilevlerin soldan en az bir paramteresi bilinen bir trden olmaldr. Aada baz deiken sayda argman alan ilevlerin bildirimleri grlyor: int printf(const char *, ...); int scanf(const char *, ...); double get_mean(int, ...); levlerin tanm da ayn biimde yaplmaldr. lev tanmlarnda ileve gnderilen argmanlara baz standart makrolar kullanlarak ulalr. Bu makrolar stdarg.h isimli standart balk dosyas iinde tanmlanmtr. Deiken sayda argman alan ilev iinde, tr ve ismi belirtilmi parametre deikenlerinin deerine dier ilevlerde olduu gibi isimleriyle ulalr. Seimlik olan argmanlara, yani ka tane olduu bilinmeyen argmanlarn her birine ulam baz standart makrolar kullanlmasyla gerekletirilir: nce bu makrolar tanyalm: va_start Makrosu Bu makro kullanlarak vs_list trnden bir deikene ilkdeeri verilir. va_list standart <stdarg.h> balk dosyas iinde bildirilen bir typedef ismidir. Seenee bal argmanlar dolaacak gsterici bu trdendir. nce bu trden bir deiken tanmlanmaldr. Bu gstericinin argman listesini dolama ilemini yapabilmesi iin, nce bu deikene va_start makrosu ile ilkdeeri verilmelidir. va_start makrosunun aadaki gibi bir ileve karlk geldiini dnebilirsiniz: void va_start (va_list ap, belirli son parametre); Bu makroya birinci argman olarak va_list trnden deiken gnderilir. Makroya gnderilmesi gereken ikinci argman ilevin tr ve ismi belirli olan son parametre deikeninin deeridir. #include <stdarg.h> int add_all(int val, ) { va_list va; va_start(va, val); /***/ } add_all isimli ilev kendisine gnderilen deiken sayda pozitif tamsaylarn toplam deeriyle geri dnecek. levin ilk parametresinin int trden olduunu gryorsunuz. Bu ileve en az bir argman gnderilmelidir. lev arsnda kullanlacak toplam argman says istee bal olarak ayarlanabilir. Ancak son argmann deeri -1 olduunda, baka

525

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

bir argman gderilmedii anlalrt. Yani ileve gnderilen son deerin -1 olmas gerektii ilevin arayznn bir paras oluyor. lev tanm iinde nce va_list trnden va isimli bir deikenin tanmlandn gryorsunuz. va_start makrosuna va deikeni ve ilevin parametre deikeni val argman olarak gnderiliyor. Bu arya karlk gelen isel kodun almasyla, va deikeni argman listesinde uygun argman gsterecek ekilde konumlandrldn dnebilirsiniz. Bylece artk ileve gnderilen argmanlarn deeri elde edilebilir.

va_arg Makrosu

va_arg makrosu ileve gnderilen argmanlarn deerlerini elde etmek iin kullanlr. Bu makro bir ilev gibi dnldnde aadaki gibi gsterilebilir: tr va_arg (va_list ap, tr); va_list trnden deikenimiz va_start makrosu ile ilkdeerini aldktan sonra bu makro senee bal tm argmanlar elde etmek iin arlr. Bu makrodan elde edilen seenee bal argmann deeridir. Makronun almas sonucu argman listesi gstericisi de bir sonraki argman gsterecek biimde konumlandrlm olur. Bu makroya yaplan bir sonraki ar ile bir sonraki seimlik argmann deeri elde edilir. Makroya gnderilecek ikinci bilgi, deeri elde edilecek argmann trdr. rnein seimlik argman int trdense makronun ikinci argmanna int geilmelidir. Yeniden add_all ilevine dnyoruz: int add_all(int val, ...) { va_list va_p; int sum = 0; va_start(va_p, val); while (val != -1) { sum += val; val = va_arg(va_p, int); } /***/ } ilevin tanmnda val deikeni -1 olmad srece dnen bir dng oluturuluyor. Dngnn her turunda val deikeninin deeri sum deikenine katlyor ve val deikenine seimlik argmanlardan bir sonrakinin deeri aktarlyor. val = va_arg(va_p, int); va_arg makrosunun ilk defa kullanlmasyla seimlik ilk argman, 2. kez kullanlmasyla seimlik 2. argman vs. elde edilir. Eer belirli bir argmandan sonras elde edilmek istenmiyorsa va_arg arlar sonuna kadar yaplmak zorunda deildir. leve ar ile gnderilen argman saysndan daha fazla sayda argman elde etmeye allrsa, yani va_arg makrosu olmas gerekeneden daha fazla arlmaya allrsa p deerler elde edilir.

va_end Makrosu

Argman listesi gstericisinin ii bittiinde va_end makrosu arlr. void va_end (va_list ap)

526

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Bu makro va_list trnden deikenle ilgili son ilemleri yapar. int add_all(int val, ...) { va_list va_p; int sum = 0; va_start(va_p, val); while (val != -1) { sum += val; val = va_arg(va_p, int); } va_end(va_p); return sum;

Sistemlerin ounda va_end makrosu karlnda bir ey yaplmaz. Yani uygulamalarda bu makro arlmasa da yaplan ilemde deiiklik olmaz. Bu durum GNU derleyicisinde her zaman dorudur. Yine de okunabilirlik, gvenlik ve tanabilirlik alarndan bu makronun arlmas doru tekniktir. stenirse birden fazla argman listesi gstericisi kullanlabilir. Bu durumda argman gstericilerinin her birine va_start makrosu ile ayr ayr ilkdeerleri verilmelidir. Gstericilerden herhangi biri ile ileve gnderilen argmanlar elde edilebilir.

Deiken sayda argman alan ilevlere yaplan arlar derleyici tarafndan zel biimde deerlendirilir. int alt trlerden olan argmanlar otomatik olarak int trne ykseltilir. float trnden olan argmanlar double trne ykseltilir: #include <stdio.h> int add_all(int val, ...); int main() { short sh = 5; printf("toplam1 = %d\n", add_all(23, 12, 30, 14, -1)); printf("toplam2 = %d\n", add_all('A', 10, sh, 20, 30, 40, -1)); return 0; } imdi kendisine adresleri gnderilen yazlar birletirecek ve deiken sayda argmanla arlabilecek bir ilev tanmlanyor: #include <stdio.h> #include <stdlib.h> #include <stdarg.h> #define BUFFER_SIZE 1000

Deiken Sayda Argman Alan levlere Yaplan arlar

char *con_strings(const char *ptr, ...) { va_list ap;

527

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

char *pd; int k; char buffer[BUFFER_SIZE] = {'\0'}; va_start (ap, ptr); while (ptr){ strcat(buffer, ptr); ptr = va_arg(ap, const char *); } pd = (char *)malloc(strlen(buffer) + 1); if (pd == NULL) { printf("bellek yetersiz!\n"); exit(EXIT_FAILURE); } va_end(ap); return strcpy(pd, buffer); } int main () { char *pstr = con_strings("C ", "ve ", "Sistem ", "Programcilari ", "Dernegi", NULL); puts(pstr); free(pstr); } return 0;

con_strings ilevinin tanmn inceleyin: va_list trnden ap deikeni va_start makrosuyla ilkdeerini alyor. va_start (ap, ptr); Parametre deikeni olan ptr nin deeri NULL adresi olmad srece dnen bir while dngs oluturulduunu gryorsunuz. Dngnn her turunda ileve gnderilen argmanlardan her birinin deeri va_arg makrosuyla elde ediliyor ve bu deer ptr gstericisine atanyor. ptr gstericisinin gsterdii yaz nce yerel buffer isimli dizideki yaznn sonuna standart strcat ileviyle ekleniyor. leve ar ile gnderilen son argmann NULL adresi olmas gerekiyor. ptr gstericisinin deeri NULL adresi olduunda buffer dizisindeki yaz dinamik bir bellek blouna kopyalanyor. lev iinde va_end makrosu arldktan sonra ilev dinamik bloun adresiyle geri dnyor.

528

C ve Sistem Programclar Dernei - C Ders Notlar - Necati Ergin

Kaynaklar
1.American National Standard for Programming Languages - C ANSI/ISO 9989-1990 2. C - A Reference Manual. Samuel P. Harbison III - Guy L. Steele Jr. Prentice-Hall, Inc 2002 ISBN 0-13-089592x 3. The Standard C Library P. J. Plaguer Prentice Hall P T R ISBN 0-13-131509-9 4. C Programming FAQ's Steve Summit Addison Wesley

529

You might also like