diff --git a/c-basic/CSD-C-Basic-Book/C-OzetNotlar-Ornekler.txt b/c-basic/CSD-C-Basic-Book/C-OzetNotlar-Ornekler.txt new file mode 100644 index 0000000..11c6f51 --- /dev/null +++ b/c-basic/CSD-C-Basic-Book/C-OzetNotlar-Ornekler.txt @@ -0,0 +1,28486 @@ +/*---------------------------------------------------------------------------------------------------------------------- + + C ve Sistem Programcıları Derneği + + C Programlama Dili + + Sınıfta Yapılan Örnekler ve Özet Notlar + + Eğitmen: Kaan ASLAN + + Bu notlar Kaan ASLAN tarafından oluşturulmuştur. Kaynak belirtmek koşulu İle her türlü alıntı yapılabilir. + + (Notları sabit genişlikli font kullanan programlama editörleri ile açınız.) + (Editörünüzün "Line Wrapping" özelliğini pasif hale getiriniz.) + + Son Güncelleme: 08/04/2025 - Salı + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C Programlama Dili 1971-1972 yıllarında Dennis Ritchie tarafından AT&T Bell Lab'ta UNIX işletim sisteminin bir yan + ürünü olarak geliştirilmişir. AT&T Bell Lab. Ken Thompson, Brian Kernighan gibi önemli kişilerle UNIX isimli işletim + sistemini geliştiriyordu. UNIX işletim sistemi o zamanın DEC PDP-8 makineleri için yazılıyordu. İlk UNIX sistemleri + ağırlıklı sembolik makine dilinde yazılmıştır. Ancak yazımı kolaylaştırmak için Ken Thompson'ın B ismini verdiği + programlama dilinden (dilciğinen de diyebiliriz) de faydalanılmıştır. İşte Dennis Ritchie bu B programlama Dilini + geliştirerek C haline getirmiştir. UNIX işletim sistemi 1973 yılında tamamen C kullanılarak yeniden yazılmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 1978 yılında Dennis Ricthie ve Brian Kernighan C'yi tanıtan "The C Programming Laanguage" isimli bir kitap yazdılar. + Daha sonra bu kitabın 1987 yılında 2. Baskısı (Second Edition) oluşturuldu. Nihayet 1989 yılında C Programlama Dili + ANSI (American National Standard Institute) tarafıında standardize edildi. C'nin ANSI standartları "ANSI C" olarak + bilinmektedir ve kısaca bu standartlara C89 denilmektedir. C'de 1989 öncesi devire "standart öncesi devir" denir. + 1990 yılında ISO kurumu C'nin ANSI standratlarını alarak bölüm numaralandırmalarını değiştirip tasdik etmiştir. Bu + standartlar "ANSI/ISO 9899:1990" kod numarasıyla basılmıştır. Bu standarda halk arasında C90 denilmektedir. C Programlama + Dili 90'lı yıllarda dünyanın en popüler ve yaygın programlama dili haline gelmiştir. ISO 1999 yılında C'ya bazı kurallar + ekleyerek yeni bir standart oluşturdu. Bu standartlar da "ISO/IEC 9899:1999" kod numarasını verdi. Bu standartlar da + halk arasında kısaca C99 olarak ifade edilmektedir. Daha sonra ISO yine C'ya bazı eklemeler yaparak 2011 standartlarını + oluşturdu. Bu standartların kod numarası "ISO/IEC 9899:2011" biçimindedir. Bu standartlar da kalk arasında C11 olarak + ifade edilmektedir. Nihayet ISO 2017 yılında yeni bir standart oluşturdu. Ancak bu standart C11'in bir düzeltmesi + biçimindedir. Şu anda C'nin en son standardı "ISO/IEC 9899:2017" kod numarasıyla tasdik edilen biçimidir. Bu da halk + arasında C17 olarak ifade edilmektedir. C'nin son standardı olan C23 üzerinde çalışılmaktadır. Halen tam olarak tasdik + edilmemiştir. Bu standartlar 2023 yılında basılamadığı için artık C2Y olarak isimlendirilmiştir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C++ Programlama Dİli C Programlama Dİlinin nesne yönelimli bir biçimi olarak düşünülebilir. Tamamen olmasa da C++ C'yi + kapsamaktadır ve fazlalıkları vardır. C++'a bu fazlalıklar "Nesne Yönelimli Programlama Tekniğini (NYPT)" uygulamak + için eklenmiştir. Derneğimizde C++ Programlama Dilinin eğitimi C bilenlere yönelik olarak verilmektedir. + + C++ Programlama Dilinin standart gelişimi de şöyledir: + + - ISO/IEC 14882: 1998 (C++98) + - ISO/IEC 14882 :2003 (C++03) + - ISO/IEC 14882 :2011 (C++11) + - ISO/IEC 14882 :2014 (C++14) + - ISO/IEC 14882 :2017 (C++17) + - ISO/IEC 14882 :2020 (C++20) + - ISO/IEC 14882 :2020 (C++23 Henüz basılmadı) +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bilgisayar donanımını yöneten, donanım ile kullanıcı arasında arayüz oluşturan temel sistem programlarına "işletim + sistemi (operating system)" denilmektedir. İşletim sistemleri iki katman olarak düşünülebilir. Çekirdek (kernel) + işletim sisteminin donanımı yöneten motor kısmıdır. Kabuk (shell) ise kullanıcı ile arayüz oluşturan kısmıdır. İşletim + sistemleri çeşitli bakımlardan sınıflandırılabilir. Örneğin: + + - Tek prosesli işletim sistemleri + - Çok prosesli işletim sistemleri + - Gerçek zamanlı işletim sistemleri + - Masaüstü (desktop) ve mobil işletim sistemleri + - Server işletim sistemleri + + Bugün masaüstü işletim sistemlerinin en yaygın kullanılanı Windows sistemleridir (75 civarı). Bunu macOS izlemektedir + (22 civarı) bunu da Linux izlemektedir. (%2.5 civarı). Mobil işletim sistemlerinin en yaygın olanı ise %70 civarında + kullanıma sahip olan Android sistemleridir. Bunu %25 civarlarında kullanıma sahip olan IOS sistemleri izlemektedir. + Diğer mobil işletim sistemleri %1'in oldukça altındadır. + + Linux sistemleri Server dünyasında en yaygın kullanılan işletim sistemleridir. Server sistemlerinin %70 civarı Linux + makinelerden oluşmaktadır. Artık pek gömülü sistem projelerinde de Linux işletim sistemi kullanılmaktadır. + + Bazı işletim sistemleri sıfırdan yazılmıştır. Bazı işletim sistemleri ise mevcut işletim sistemlerinin kodlarıdan + faydalanılarak oluşturulmuştur. Örneğin Android büyük ölçüde Linux çekirdeğinin kodlarına sahiptir. Windows, Linux + özgün kod temeline sahip olan işletim sistemleridir. Eskiden BSD sistemleri özgün değildi. Sonra tamamen sıfırdan + yeniden yazıldı. Benzer biçimde Solaris gibi sistemlerde sıfırdan yazılmıştır. Anak macOS sistemleri böyle değildir. + macOS sistemleri FreeBSD ve Mach isimli çekirdeklerin birleşimiyle oluşturulmuştur. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir bilgisayar sisteminde en önemli üç birim "CPU", "RAM" ve "Disk" birimleridir. Bütün işlemler CPU (Central Processing + Unit) tarafından yapılır. CPU işlemlerin yapıldığı kavramsal birimdir. Bunun entegre devre biçiminde üretilmiş haline + "mikroişlemci (microprocessor)" denilmektedir. CPU'nun elektiriksel olarak bağlantılı olduğu belleklere "ana bellek + (main memory)", ya da "Birincil bellek (primary memory)" denilmektedir. Ana belleklere ise halk arasında RAM denilmektedir. + Programlama dillerindeki değişkenler program çalışırken RAM'de bulunurlar. Ancak işlemler CPU tarafından yapılır. + Örneğin: + + a = b + c; + + gibi bir işlemde aslında a, b, ve c RAM'de bulunmaktadır. Bu işlem yapılırken b ve c CPU'ya çekilir. CPU içerisindeki + elektrik devreleri toplama işlemini yapar. Sonuç RAM'deki a'ya aktarılır. Bilgisayarın güç kaynağı kapatıldığında + RAM'deki bilgiler silinmektedir. Bunun için bu bilgilerin daha kalıcı bir bellekte saklanması gerekir. Bu tür belleklere + "ikinci bllekler (secondary storage device)" denilmektedir. Eskiden ikincil bellek olarak floppy disketler, CD/DVD + ROM'lar ve hard diskler kullanılıyordu. Ancak günümüzde artık SSD (solid State Disk) denilen "flash bellekler" + kullanılmaktadır. Genellikle bilgisayar sistemlerinde ikincil belleklerle birincil bellekler arasında bir aktarım + yolu bulunmaktadır. Bu aktarım yardımcı işlemciler (bunlara DMA (Dynamic Memory Access) denilmektedir) tarafından + yapılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C doğal kodlu bir çalışma sistemi için tasarlanmıştır. Biz C'de bir kod yazıp bunu derlediğimizde ve bağladığımızda + çalıştırılabilir (executable) bir dosya elde ederiz. Bu dosyanın içerisinde o anda çalışmakta olduğumuz mikroişlemcinin + doğrudan çalıştırabileceği makine komutları bulunur. Yani C'de yazdığımız ve derlediğimiz program mikroişlemci tarafından + doğrudan çalıştırılmaktadır. + + Ancak 1990'lı yılların ortalarında Java ortamıyla (Java framework) birlikte ve sonra da 2002 yılında .NET ortamıyla + birlikte "arakodlu çalışma sistemi" yaygınlaşmaya başlamıştır. Bu sistemde derleyicilerin ürettiği kodlar gerçek bir + mikroişlemcinin makine kodları değildir. Kendi içerisinde belli bir standardı olan ancak hiçbir mikroişlemcinin makine + kodu olmayan yapay bir ara koddur (intermediate code). Dolayısıyla bu arakod mikroişlemci tarafında çalıştırılamaz. + İşte bu arakodlar çalıştırılmak istendiğinde bu ortamların (frameworkds) bir alt sistemi devreye girmekte ve bu arakodları + o anda gerçek makine komutlarına dünüştürüp çalıştırmaktadır. Bu sürece "tam zamanın derleme (just-in time compilation)" + denilmektedir. + + Java ismi hem bir ortam (framework) belirtmekte hem de bir programlama dili belirtmektedir. Oysa .NET platformun ismi + C# ise programlama dilinin ismidir. Java Programlama Dilinde yazılmış olan kodun derlenmesiyle elde edilen ara koda + "Java Byte Code" denilmektedir. Benzer biçimde C# ile yazılmış kodun derlenmesiyle elde edilen arakoda ise "Common + Intermediate Language (CIL)" denilmektedir. Her iki ortamda da bu kodlar doğrudan değil bu ortamların alt sistemleri + tarafından çalıştırılmak istedniğinde belli bir düzen içerisinde o anda gerçek komutlarına dönüştürülmektedir. Tabii + böyle bir arakod sistemi JIT derlemesi nedeniyle doğal kodlu sistemlere göre daha yavaş bir çalışma sunmaktadır. + Microsoft kendi .NET sistemi için buradaki zaman kaybının %18 civarında olduğunu belirtmektedir. + + C'de yazılmış ve derlenmiş olan bir program hem işletim sistemine hem de işlemciye bağımlıdır. Yani biz Windows + sistemlerinde x86 serisi Intel işlemcilerinin bulunduğu bir bilgisayareda yazdığımız ve derlediğimiz kodu Linux'ta + çalıştıramayız. İşte Java gibi .NET gibi ortamlarda yazılmış ve derlenmiş olan kodlar işletim sisteminden ve işlemciden + bağımsız arakoda dönüştürülmektedir. Böylece bu ortamlar çeşitli işletim sistemi ve mikroişlemci mimarileri için + yazılmış olduğundan Java ve .NET ortamları için yazılan programlar "platform bağımsız" bir biçimde her yerde + çalışabilmektedir. + + Java ve .NET gibi ortamlara İngilizce "framework" denilmekltedir. Platform sözcüğü İngilizce daha çok "işletim sistemi + ve işlemcinin" oluşturduğu küme için söylenmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Taşınabilirlik (portability) eski bir terimdir ve hala kullanılmaktadır. Taşıanbilirlik denildiğinde "kaynak kodun + taşınabilirliği" anlaşılmaktadır. Bu bağlamda taşınabilirlik "yazılmış olan kaynak kodun başka sistemlere götürüldüğünde + sorunsuz derlenmesi ve aynı biçimde çalışması" anlamına gelemektedir. Örneğin C'nin taşınabilir bir dil olması demek + C programlarının standart bir dilde yazıldığından her C derleyicisinin bunu kabul etmesi demektir. Ancak son 30 yıldır + derlenmiş olan programların taşınabilirliği biçiminde "binary portability" terim de kullanılmaya başlanmıştır. Derlenmiş + programın taşınabilirliği onun başka platformlara götürüldüğünde sorunsuz çalışabilmesi anlamına gelmektedir. Java + gibi .NET gibi rtamlar bunu hedeflemektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir programlama dilinde yazılmış olan bir programı eşdeğer olarak başka bir dile dönüştüren araçlara "çevirici programlar + (translators)" denilmektedir. Çevirici programlarda çevrilecek dile "kaynak dil (source language)" çevrime işleminin + sonunda elde edilen programın diline ise "hedef dil (target language)" denilmektedir. Hedef dili alçak seviyeli olan + çevirici programlara ise "derleyici (compiler)" denilmekltedir. Saf makine dilleri, sembolik makine dilleri ve ara + kodlar alçak seviyeli dillerdir. + + Yorumlayıcılar (interpreters) kaynak kodu okuyup hiç hedef kod üretmeden doğrudan çalıştıran programlardır. Dolayısıyla + yorumlayıcılar aslında çevirici programlar değildir. Bazı dillerde yalnızca derleyicilerle çalışılır (örneğin C, C++). + Bazı dillerde ise yalnızca yorumlayıcılar bulunmaktadır. (Örneğin Ruby, R gibi). Bazı dillerde ise hem derleyiciler + hem de yorumlayıcılarla programlar çalıştırılabilir (örneğin Basic gibi). (Biz İngilizce "interpreter" sözcüğünü Türkçe + "yorumlayıcı" biçiminde çeviriyoruz. Aslında İngilizce "interpreter" sözcüğü "translator" sözcüğü dikkate alınarak + "mütercim tercüman" anlamında uydurulmulmuştur.) + + Derleyici yazmak yorumlayıcı yazmaktan daha zordur. Derleyiciler ile yazılan kod genel olarak daha hızlı çalıştırılmaktadır. + Yorumlayıcılarla çalışırken biz kaynak kodu gizleyemeyiz. Ancak derleyicilerle çalışırken bir üretilen makine kodlarını + karşı tarafa verebiliriz. + + Eğer bir derleyici kendisinin çalıştığı işlemciden farklı bir işlemci için kod üretiyorsa o tür derleyicilere + "çapraz derleyiciler (cross compilers)" denilmektedir. Örneğin x86 işlemcilerinin bulunduğu Windows sistemlerinde + çalışan bir C derleyicisi eğer örneğin PIC işlemcileri kod üretiyorsa bu bir çapraz derleyicidir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Kendisi bir bilgisayar olmayıp asıl amacı başka işlemleri yapmak olan aygıtlardaki bilgisayar devrelerine "gömülü + sistemler (embedded systems)" denilmektedir. Örneğin ölçü aletlerinin, kapı güvenlik sistemlerinin, turnike geçiş + sistemlerinin, çamaşır makinelerinin, buzdolaplarının, fırınların, kahve makinelerinin içerisindeki bilgisayar sistemlerini + gömülü sistemlere örnek olarak verebiliriz. Gömülü sistemlerdeki bilgisayar devreleri genel olarak düşük güç harcayan, + düşük kapasiteli, ancak ucuz olma eğilimindedir. Mikrodenetleyiciler bu tür gömülü sistemlerde yoğun biçimde kullanılmaktadır. + Dolayısıyla gömülü sistemler dünyasında aşağı seviyeli bir çalışma söz konusu olduğu için C Programlama Dili de bu + sistemlerde yoğun olarak kullanılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Dil (language) iletişimde kullanılan semboller kümesidir. Dil karmaşık bir olgudur. Bir olgunun dil olarak değerlendirilmesi + için iki kural topluluğunun o olguda bulunyor olması gerekir: Sentaks ve smantik. Bir dilin en yalın öğelerine "atom + (token)" denilmektedir. Örneğin doğal dillerde atomlar zözcüklerdir. İşte sentaks atomların doğru yazılmasına ve doğru + dizilmesine ilişkin kurallardır. Örneğin: + + I going to am school + + Burada bir sentakshatası yapılmıştır. Buradaki atomlar uygun bir biçimde yan yana getirilmemiştir. Burada yapılan hata + aşağıdaki C kodunda yapılan hataya tamamen benzemektedir: + + if a > ( 10 ) printf("Ok"); + + Sentaks bakımından doğru olan atom dizilimlerinin ne anlam ifade ettiğine ilişkin kurallara "semantik" denilmektedir. + Sentaks ve semantik kurallara sahip her olguya dil denilmektedir. Örneğin HTML'de bir senataks vardır. Oluşturulan + tag'ların bir anlamı da vardır. O zaman HTML bir dildir. + + Diller doğal diller ve kurgusal diller olmak üzere iki ayrılır. Doğal diller Türkçe gibi İngilizce gibi doğal yaşam + sonıcında oluşmuş dillerdir. Doğal dillerde sentaksın matematiksel düzeyde kesin ifade edilmesi mümkün değildir. + Çünkü doğal dillerde çok istisnalar vardır. Kurgusal diller insanların belli bir mantık çerçevesinde belli bir amaç + doğrultusunda tasarladığı dillerdir. Bunların sentaksları kesindir. İki anlamlılık ve istisna çok yoktur ya da çok + azdır. Bilgisayar alanında kullanılan kurgusal dillere "bilgisayar dilleri (computer languages)" denilmektedir. Bir + bilgisayar dilinde bir akış varsa ona aynı zamanda "programlama dili (programming language)" denilmektedir. Örneğin + HTML bir bilgisayar dilidir. Ancak bir programlama dili değildir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Programlama dilleri çeşitli biçimlerde sınıflandırılabilmektedir. En çok kullanılan sınıflandırma biçimleri şunlardır: + + 1) Seviyelerine göre sınıflandırma + 2) Uygulama alanlarına göre sınıflandırma + 3) Programlama modeline göre sınıflandırma +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Seviye (level) bir programalama dilinin insan algısına yakınlığının bir ölçüsüdür. Yüksek seviyeli diller insana yakın + alçak seviyeli diller makineye yakın dillerdir. Seviyelerine göre diller yüksekten alçağa kategorik olarak genellikle + şöyle sınıflandırılmaktadır: + + - Çok Yüksek Seviyeli Diller + - Yüksek Seviyeli Diller + - Orta Seviyeli Diller + - Sembolik Makine Dilleri + - Saf Makine Dilleri ve Arakodlar + + C ortra seviyeli (middle level) bir programlama dilidir. Ancak Java, C#, Python gibi diller yüksek seviyeli diller + olarak gruplanmaktadır. Çok yüksek seviyeli dillerde artık algoritma da ortadan kalkmaktadır. Genellikle bu tür diller + "belli bir alana yönelik (domain specific)" biçimdedirler. Saf makike dilleri ve arakodlar 1'lerden ve 0'lardan oluşmaktadır. + Bunların sembolik biçimlerine "sembolik makine dilleri (assembly languages)" denilmektedir. Sembolik makine dilleri, + saf makine dilleri ve arakodlara da "alçak seviyeli diller" denir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Programlama dillerinin uygulama alanlarına göre sınıflandırılması "hangi tür uygulamalar için hangi dillerin daha + uygun bir araç olacağı" ile ilgilidir. Bu bakımdan pek çok alt sınıflandırma yapılabilmektedir. Aşağıda birkaç önemli + alt sınıf verilmiştir: + + - Bilimsel ve Mühendislik Diller (Scientific and Enginnering Languages): Fortran, C, C++, Pascal, Java, C#, ... + - Veritabanı Dilleri (Database Languages): SQL, Clipper, ... + - Web Dilleri (Web Languages): Java Script, PHP, Ruby, Java, C#, Python, ... + - Yapay Zeka Dilleri (Artificial Intelligence Langauges): Lisp, Prolog, Python, C, C++,... + - Görsel ve Animasyon Dilleri (Visual and Animation Languages): Action Script, ... + - Sistem Proramlama Dilleri (System Programming Languages): C, C++, Sembolik Makine Dilleri, Rust, Go + - Genel Amaçlı Diller (General Purpose Languages): C, C++, Java, C#, Pascal, ... + + C Programlama Dİli bilimsel ve mühendislik alanlarda kullanılan, genel amaçlı, uzmanlığı sistem programlama olan bir + dildir. + + Sistem programlama "bilgisayar donanımı ile arayüz oluşturan, uygulama programlarına çeşitli bakımlardan hizmet veren, + aşağı seviyeli temel yazılımların oluşturulması amacıyla yapılan programlama faaliyetlerine denilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Programlama modeli (programming paradigm) programlama yaparken kullandığımız genel yöntemleri ve biçimleri anlatan bir + kavramdır. Programlama dilleri belli programlama modellerini uygulayabilmek için özel tasarlanmıştır. Bu bakımdan dilleri + tipik olarak aşağıdaki gibi sınıflara ayırabiliriz: + + - Prosedürel Diller (Procedural Languages): Bu dillerde programlar altprogramların (bunlara fonksiyon, prosedür ya da + "subroutine" de denilmektedir) birbirlerini çağırması ile oluşturulmaktadır. Fortran, C, Basic, Pascal gibi 90 öncesi + klasik programlama dillerinin büyük bölümü böyledir. + + - Nesne Yönelimli ve Nesne Tabanlı Diller (Object Oriented Languages): İçerisinde sınıf kavramı geçen diller. Bunlar + programların sınıflar kullanılarak yazılmabilmesine olanak sağlamaktadır. + + - Fonksiyonel Diller (Functional Languages): Bunlar adeta "formül yazar gibi program yazmaya olanak sağlayan" dillerdir. + Aslında bu diller de kendi aralarında bir spektrum oluşturmaktadır. Fonksiyonel dil demekle genellikle yüksek oranda + fonksiyonal olan (pure functional) diller kastedilmektedir. + + - Imperative Diller (Imperative Languages): Programların deyim deyim çalıştırıldığı mantıksal, görsel ve fonksiyonel + dillerin dışındaki diller genel olarak imperative diller olarak da bilinmektedir. + + - Mantıksal Diller (Logical Languages): Mantıksal ifadelerin ve sonuç çıkartma işlemlerinin yoğun kullanıldığı dillerdir. + Lisp ve Prolog gibi. + + - Görsel Diller (Visual Languages): Fare hareketleriyle tamamne görsel biçimde program akışının oluşturulduğu çok + yüksek seviyeli dillerdir. Programlama eğitiminde kullanılan Scratch buna örnek verilebilir. + + - Çok Modelli Diller(Multiparadigm Languages): Yukarıdaki modellerden birden fazlasını belli ölçülerde destekleyen + dillerdir. Örneğin C++'ta biz C gibi kod yazabiliriz. Ama sınıflar kullanarak da kod yazabiliriz. Fonksiyonel bazı + dil özelliklerini C++11'den sonra C++'ta kullanabilmekteyiz. O zaman C++ çok modelli bir programlama dilidir. Yeni + tasarlanan diller zaten genel olarak çok modelli olma eğilimindedir. Bu yeni diller hem nesne yönelimli hem de + fonksiyonel özellikleri bünyesinde barındırmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C Programalama Dili orta seviyeli, prosedürel, imperative, genel amaçlı, bilimsel ve mühendislik çalışmalarda kullanılan + ancak uzmanlık alanı "sistem programlama" olan bir dildir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Uygulama geliştirmeyi kolaylaştıran, kendi içerisinde editörü olan, menüleri olan, genellikle debugger'ları olan, başka + birtakım araçları bulunan yazılımlara "IDE (Integrated Developmen Environment)" denilmektedir. IDE derleyici değildir. + IDE derleyiciyi barındırmaz. IDE'de derleme işlemi yapılırken IDE derleyiciyi dışarıdan çalıştırmaktadır. Derleyiciler + genel olarak komut satırında çalışan GUI arayüzü olmayan araçlar biçiminde oluşturulmaktadır: + + C için kullanılabilecek önemli IDE'ler şunlardır: + + - Windows sistemleri için Microsoft'un "Visual Studio" IDE'si. Kursumuzda bunun "Community-2022" parasız versiyonunu + kullanacağız. + + - QtCreator IDE'si. Cross platform bir IDE'dir. Aslında "Qt" denilen framework için yazılmıştır. Ancak genel amaçlı + C/C++ IDE'si olarak da kullanılabilmektedir. macOS ve Linux sistemlerinde iyi bir alternatiftir. + + - Eclipse IDE'si. Bu IDE bir Java IDE'si olarak çıkmıştı. Ondan sonra pek çok programlama dili için "plugin" yöntemiyle + kullanılabilmeye başlandı. Bu IDE'nin C/C++ versiyonu doğrudan indirilebilir. + + - CLion IDE'si. Bu JetBrains firmasının C/C++ IDE'sidir. Ancak paralıdır ve community versiyonu yoktur. + + - Visual Studio Code IDE'si Aslında bu yazılım IDE ile editör arasında bir yerdedir. Ancak cross platform özelliği + vardır. Kullanımı biraz dah zahmetli olsa da çok az yer kaplamaktadır (lightweight IDE). Plug-in'lerle (extensions) + işlevleri genişletilebilmektedir. + + - XCode IDE'si. Bu IDE Apple firmasının temel IDE'sidir. Dolayısıyla adeta Visual Studio IDE'sinin Apple versiyonu + gibi düşünülebilir. Her ne kadar Apple'ın temel dili Swift ve Objective C olsa da XCode C/C++ çalışmasını + desteklemektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 5. Ders - 07/06/2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Biz 10'luk sistemi (decimal system) kullanmaktayız. 10'luk sistemde sayıları ifade etmek için 10 sembol vardır: + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + + 10'luk sistemde sayının her bir basamağı 10'nun kuvvetleriyle çarpılıp toplanmaktadır. Örneğin: + + 123.25 = 3 * 10^0 + 2 * 10^1 + 1 * 10^2 + 2 * 10^-1 + 5 * 10^-2 + + Halbuki bilgisayarlar 'lik sistemi (binary system) kullanmaktadır. ikilik sistemde sayıları ifade etmek için 2 sembol + kullanılmaktadır: + + 0 + 1 + + ikilik sistemde sayının her bir basamağına "bit ("binary digit" sözcüklerinden kısaltma)" denilmektedir. ikilik sistemde + sayının her basamağı 2'nin kuvvetiyle çarpılarak sayı elde edilir. Bit en küçük bellek birimidir. 8 bite 1 byte denilmektedir. + Genellikle bitler 4'erli gruplanarak yazılırlar. Örneğin: + + 1010 0010 + + Burada 1 byte'lık bir bilgi vardır. Byte temel bellek birimidir. + + Byte da küçük bir birimdir. Kilo diğer bilimlerde "1000 katı" anlamına gelmektedir. Ancak bilgisayarlar ikilik sistemi + kullandığj için 1000 katı iyi bir kat değildir. Bu nedenle genellikle olarak Kilo byte için 2'nin 10'uncu kuvveti olan + 1024 kat kullanılır. Yani 1KB (kısaca 1K) 1024 byte'tır. Mega diğer bilimlerde kilonun 1000 katıdır. Dolayısıyla milyar + kat anlamına gelmektedir. Ancak bilgisayar bilimlerinde genellikle mega kilonun 1024 katı olarak alınır. Bu durumda + 1 MB = 1020 * 1024 (2^20) KB'dir. Giga ise meganın 1024 katıdır. Bu durumda 1 GB = 1024 * 1024 * 1024 byte'tır ( 2^30). + Giga'dan sonra tera, tera'dan sonra peta, ondan sonra da exa gelmektedir. Bazı kaynaklar kilo, mega ve giga byte + terimlerini diğer bilimlerde olduğu gibi 10^3, 10^6 ve 10^9 biçiminde kullanmaktadır. Bu konudaki karışıklık daha + sonraları "International System of Units (SI)" tarafından giderilmeye çalışılmıştır. SI'ye göre artık 1 Kilobyte 1000 + byte, 1 Kikibyte ise 1024 byte'tır. Ancak Microsoft gibi bazı kurumlar Kilobyte tanımını 1024 olarak kullanmaya devam + etmektedir. + + 1 byte içerisinde yazılabilecek en küçük ve en büyük sayılar şöyledir: + + 0000 0000 ---> 0 + 1111 1111 ---> 255 + + 1 byte içerisinde 1 ve 0'ların bütün permütasyonları 256 tanedir. 2 byte içerisinde en büyük sayıyı yazacak olsak şöyle + olurdu: + + 1111 1111 1111 1111 ---> 65535 + + Biz burada ikilik sistemde tamsayıları ifade ettik. Ama bütün sayıları pozitif kabul ettik. Pekiyi negatif tamsayılar + nasıl ifade edilmektedir? + + Bugün negatif sayıların ifade edilmesi için "ikiye tümleyen (two's complement)" sistemi denilen bir sistem kullanılmaktadır. + Bu sistemde pozitif ve negatif sayılar birbirlerinin ikiye tümleyenidirler. ikiye tümleyen bire tümleyene bir eklenerek + elde edilmektedir. Bir sayının bire tümleyeni sayıdaki 0'ların 1, 1'lerin 0 yapılmasıyla elde edilir. Bu durumda ikiye + tümleyen şöyle hesaplanır. Örneğin aşağıdaki sayının ikiye tümleyenini bulmaya çalışalım: + + 0101 0110 + + Sayının bire tümleyenine bir ekleyeceğiz. Sayının bire tümöleyeni şöyledir: + + 1010 1001 + + Şimdi ona 1 ekleyelim: + + 1010 1001 + 0000 0001 + --------- + 1010 1010 + + Aslında ikiye tümleyeni bulmanın kolay bir yolu da vardır: Sayıda sağdan sola ilk 1 görene ilk 1 dahil olmak üzere aynısı + yazılarak ilerlenir. Sonra 0'lar 1, 1'ler 0 yapılarak devam edilir. Örneğin: + + 0101 0110 + + sayının ikiye tümleyenini tek hamlede bulalım: + + 10101010 + + Negatif sayıları ifade edebilmek için kullanılan ikiye tümleme sisteminde en soldaki bir işaret bitidir. Bu bit 0 ise + sayı pozitif, 1 ise negatiftir. Negatif ve pozitif sayılar birbirlerinin ikiye tümleyenidir. Örneğin bu sistemde +10 + yazmak isteyelim. Bunu işaret bitini 0 yaparak yazabiliriz: + + 0 000 1010 ---> +10 + + Şimdi -10 yazmak isteyelim. Bunun için +10'un ikiye tümleyenini alalım: + + 1 111 0110 ---> -10 + + Bu sistemde +n ile -n toplandığında 0 elde edilir: + + 0 000 1010 ---> +10 + 1 111 0110 ---> -10 + --------------------- + 0 000 0000 ---> 0 + + Bu sistemde tek bir sıfır vardır. O da tüm bitleri 0 olan sıfırdır. Bu sistemde 1 byte içerisinde yazılabilecek en büyük + pozitif sayı şöyledir: + + 0 111 1111 ---> +127 + + Şimdi bunun ikiye tümleyenini alalım: + + 1 000 0001 ---> -127 + + Pekiyi bu sistemde en küçük negatif sayı nedir? Bu sistemde bir tane sıfır olduğuna göre 255 tane permütasyon eşit bölünemez. + Demek ki ya pozitif sayılar ya negatif sayılar bir tane daha fazla olmak zorundadır. Bu sistemde ikiye tümleyeni olmayan iki + sayı vardır: + + 0000 0000 + 1000 0000 + + Birincisi 0'dır. İkinci sayı -127'den bir eksik olan sayıdır. O halde bu sayının -128 kabul edilmesi daha uygundur. + + Demek ki bu sistemde n byte içerisinde yazılabilecek en büyük pozitif sayı ilk biti 0 olan diğer tüm birleri 1 olan sayıdır. + En küçük negatif sayı ise ilk biti 1 olan diğer tüm bitleri 0 olan sayıdır. Örneğin bu sistemde iki byte ile yazabileceğimiz + en büyük pozitif sayı şöyledir: + + 0111 1111 1111 1111 ---> +32767 + + En küçük negatif sayı ise şöyledir: + + 1000 0000 0000 000 ---> -32768 + + Bu sisteme ilişkin tipik sorular ve yanıtları şöyledir: + + SORU: Bu sistemde +n sayısını nasıl yazarsınız? + YANIT: En soldaki bit 0 yapılıp n sayısı ikilik sistemde yazılır. + + SORU: Bu sistemde -n nasıl yazarsınız? + CEVAP: Yazabiliyorsanız doğrudan yazın. Ancak doğrudan yazamıyorsanız önce +n değerini yazın ve ikiye tümleyenini alın. + Örneğin bu sistemde -1 yazalım. Önce +1 yazalım: + + 0000 0001 ---> +1 + + Şimdi bunun ikiye tümleyenini alalım: + + 1111 1111 ----> -1 + + SORU: Bu sistemde bir sayının kaç olduğu bize sorulsa bunu nasıl yanıtlarız? + CEVAP: Eğer en soldaki bit 0 ise sayının değeri doğrudan hesplanır. Eğer en soldaki bit 1 ise bu sayının negatif olduğunu + gösterir. Bu durumda sayının ikiye tümleyeni alınır. Pozitifinden hareketle negatifi bulunur. Örneğin 1110 1110 sayısı + kaçtır? Burada işaret biti 1 olduğuna göre sayı negatiftir. Negatif ve pozitif sayılar birbirlerinin ikiye tümleyenidirler. + O zaman bu sayının ikiye tümleyenini alıp pozitifinden faydalanarak sayıyı bulalım: + + 0001 0010 ---> +18 + + o zaman bize sorulan sayı -18'dir. + + Bu sistemde örneğin 1 byte içerisinde yazılabilecek en büyük pozitif sayıya 1 toplayalım: + + 0111 1111 ---> +127 + 1000 0000 ---> -128 + + Demek ki bu sistemde bir sayıyı üst limitten taşırırsak yüksek bir negatif sayıyla karışılaırız. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Tamsayılar ikilik sistemde "işaretsiz (unsigned)" ya da "işaretli (signed)" sistemde yorumlanabilirler. İşaretsiz + sistemde sayının en soldaki biti olarak yorumlanmaz. Sayı herzaman sıfır ya da pozitiftir. İşaretli sistemde ise + sayının en solundaki bit işaret bitidir. Sayı ikiye tümleyen aritmetiğine göre yorumlanır. + + Pekiyi sayının işaretli mi işaretsiz mi olduğuna nasıl karar verilmektedir? Programcı sayıyı tutacağı değişkeni C'de + işaretli ya da işaretsiz tamsayı türü olarak belirleyebilir. İşlemciler aslında genellikle işaretli ve işaretsiz + ayırımını yapmazlar. Çünkü bu tür de aslında aynı biçimde işleme sokulmaktadır. Sonucun yorumu değişmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Pekiyi noktalı sayılar ikilik sistemde nasıl ifade edilmektedir? İşte insanlar noktalı sayıları ifade etmek için iki + format geliştirmişlerdir. Bunlardan birine "sabit noktalı formatlar (fixed point formats)" diğerine "kayan noktalı + formatlar (floating point formats)" denilmektedir. Sabit noktalı formatlar eski devirlerde basit bir mantıkla tasarlanmıştır. + Bu formatlar bugün hala kullanılıyor olsa da büyük ölçüde artık bunların çağı kapanmıştır. Bugün kayan noktalı format + denilen formatlar kullanılmaktadır. + + Sabit noktalı formatlarda noktalı sayı için n byte yer ayrılır. Noktanın yeri önceden bellidir. Örneğin sayı 4 byte + ile ifade edilsin. Noktanın yeri de tam ortada olsun. Bu durumda syının tam kısmı 2 byte ile noktalı kısmı 2 byte ile + ifade edilir. Ancak sayının noktalı kısmı 2'nin negatif kuvvetleriyle kodlanmaktadır. VBöylece iki sabit noktalı sayıyı + paralel toplayıcılarla kolay bir biçimde toplayabiliriz: Örneğin bu sistemde 5.25 ile 6.25 sayılarını ifade edip toplayalım: + + 0000 0000 0000 0101 . 0100 0000 0000 0000 ---> 5.25 + 0000 0000 0000 0110 . 0100 0000 0000 0000 ---> 6.25 + ------------------------------------------------------- + 0000 0000 0000 1011 . 1000 0000 0000 0000 ---> 11.5 + + Pekiyi bu yöntemin ne dezavantajı vardır? Yöntemin en önemli dezavantajı dinamik olmamasıdır. Yani sayının tam kısmı + düşükse tam kısım için gereksiz bir yer ayrılmış olur. Benzer biçimde sayının noktadan sonraki kısmı düşükse noktadan + sonrakşi kısım için gereksiz bir biçimde yer ayrılmış olur. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 6. Ders - 09/06/2022 +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Sabit noktalı formatların dinamik olmaması nedeniyle kayan noktalşı formatlar geliştirilmiştir. Bu formatlarda noktanın + yeri sabit değildir. Noktanın yeri format içerisinde ayrıca tutulmaktadır. Noktalı sayının noktası yokmuş gibi ifade + edilmesi durumunda sayının bu haline "mantis (mantissa)" denilmektedir. İşte kayan formatlarda sayı için ayrılan alanın + bir bölümünde mantis bir bölümünde de "noktanın yeri" tutulmaktadır. Noktanın yerini belirleyen kısma "üstel kısım + (exponential part)" denilmektedir. Tabii bir de sayının başında işaret biti bulunur. Bu durumda kayan noktalı bir + sayının format aşağıdakine benzerdir: + + [işaret biti] [mantis] [noktanın yeri (exponential)] + + Bugün ağırlıklı kullanılan kayan noktalı format IEEE 754 denilen formattır. Bu formatın üç farklı genişlikte biçimi + vardır: + + IEEE 754 - Short Real Format (4 byte) + IEEE 754 - Long Real Format (8 byte) + IEEE 754 - Extended Real Format (10 byte) + + Bugün Intel, ARM, MIPS, Alpha, Power PC gibi yaygın işlemciler donanımsal olarak bu formatı desteklemektedir. Aynı + zamanda bu format yaygın olarak Reel Sayı Ünitesi olmayan mikrodenetleyicilerdeki derleyiciler tarafından da kullanılmaktadır. + + Kayan noktalı formatların (örneğin IEEE 754 formatının) en ilginç ve problemli tarafı "yuvarlama hatası (rounding error)" + denilen durumdur. Yuvarlama hatası noktalı sayının tam olarak ifade edilemeyip onun yerine ona yakın bir sayının ifade + edilmesiyle oluşan hatadır. Yuvarlama hatası sayıyı ilk kez depolarken de oluşabilir, aritmetik işlemlerin sonucunda + da oluşabilir. Tabii noktalı sayıların bir bölümü bu formatta hiçbir yuvarlama hatasına maruz kalmadan ifade edilebilmektedir. + Ancak bazı sayılarda bu hata oluşabilmektedir. Bu hatayı ortadan kaldırmanın yolu yoktur. Tabii sayı için daha fazla + alan ayrılırsa yuvarlama hatasının etkisi de azalacaktır. + + Yuvarlama hatalarından dolayı programlama dillerinde iki noktalı sayının tam eşitliğinin karşılaştırılması anlamlı + değildir. Örneğin aşağıdaki işlemde yuvarlama hatasından dolayı sayılar sanki eşit değişmiş gibi ele alınacaktır. + + 0.3 - 0.1 == 0.2 (false) + + Pekiyi yuvarlama hatasının önemli olduüu ve bunun istenmediği tarzda uygulamalarda (örneğin finansal uygulamalarda, + bilimsel birtakım uygulamalarda) ne yapak gerekir? İşte bunun tek yolu noktalı sayıları kayan noktalı formatta tutmamak + olabilir. Bazı programlama dillerinde noktalı sayıyı kayan noktalı formatta tutmayan böylece yuvarlama hatalarına + maruz bırkmayan özel türler (örneğin C#'taki decimal) vardır. Ancak bu türler işlemciler tarafından desteklenmediği + için yapay türlerdir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Yazılar da aslında bilgisayar belleğinde ikilik sistemde sayılar biçiminde tutulmaktadır. Bir yazıyı oluşturan elemanlara + "karakter" denilmektedir. İşte bir yazıda her bir karakter ikilik sistemde bir sayı ile ifade edilir. Böylece yazı + aslında ikilik sistemde bir sayı dizisi gibi tutulmaktadır. İşte bir karakter için hangi sayının karşı geldiğini + belirten tablolara "karakter tabloları" denilmektedir. Karakter tablosundaki karakter şekillerine "glyph" denilmektedir. + Her karaktere tabloda bir sıra numarası verilmiştir. Buna da "code point" denilmektedir. Dünyanın ilk standart karakter + tablosu "ASCII (American Standard Code Information Interchange)" denilen tablodur. ASCII tablosu aslında 7 bit bir + tablodur. Dolayısıyla tabloda 128 tane glyph için code point bulundurulmuştur. ASCII dışında IBM EBCDIC tablosunu + geliştirmiştir. Wang firması WISCII tablosunu kullanmıştır. ASCII tablosu Amerikalılar tarafından yalnızca İngilizce + karakterleri ifade etmek için oluşturulmuştur. Bilgisayarlar yaygınlaşmaya başladığında farklı karakterlere sahip + olan Türkiye gibi, Yunanistan gibi, Almanya gibi ülkeler bu ASCII tablosunu 8 bite çıkartıp elde edilen 128'lik yeni + alanı kendi karakterlerini ifade etmek için kullanmışlardır. ASCII tablosunun ilk yarısı (yani [0, 128] numaraları + karakterleri) standarttır. Ancak ikinci yarısı "code page" adı altında farklı ülkeler tarafından farklı yerleşimler + yapılarak kullanılmaktadır. DOS zamanlarında Türkçe karakterler için OEM 857 denilen code page kullanılıyordu. Daha + sonra Microsoft Windows sistemlerinde Türkçe karakterler için 1254 code page'i düzenledi. ISO bu code page'leri standart + hale getirmiştir. Bugün Türkçe karakterler ISO tarafından ASCII 8859-9 Code page'i ile düzenlenmiştir. + + ASCII tablosu ve onların code page'leri uzun süre kullanılmış ve hala kullanılmakta olsa da maalesef karışıklıklara + yol açmaktadır. İşte son 20 yıldır artık karakterleri 2 byte içerisinde ifade ederek dünyanın bütün dillerinin ve ortak + sembollerinin tek bir tabloya yerleştirilmesi ile ismine Unicode denilen bir tablo oluşturulmuştur (www-Unicode.org). + Unicode tablo ISO tarafından 10646 ismiyle de bazı farklılıklarla standardize edilmiştir. Unicode tablonun + ilk 128 karakteri standart ASCII karakterleri, ikinci 128 karakteri ISO 8859-9 code page'indeki karakterlerdir. + + Bir karakter tablosundaki code point'lerin ikilik sistemde ifade edilme biçimine "encoding" denilmektedir. ASCII code + page'lerinde encoding doğrudan code point'in 1 byte'lık sayı karşılığıdır. Ancak Unicode tablonun değişik encoding'leri + kullanılmaktadır. Unicode tablonun klasik encoding'i UTF-16'dır. Burada code point doğrudan 16 bir bir sayı biçiminde + ifade edilir. UTF-32 encoding'inde ise code point 32 bitlik bir sayı biçiminde ifade edilmektedir. (Unicode tablodaki + karakterler için başlangıçta 2 byte yetiyordu. Ancak daha sonraları tabloya pek çok karakter eklendi ve 2 byte yetmez + hale geldi.) Ancak Unicode tablonun en yaygın kullanılan encoding'i UTF-8 encoding'idir. UTF-8 kodlamasında standart + ASCII karakterler 1 byte ile, diğer karakterler 2 byte, 3 byte, 4 byte ve 5 byte kodlanabilmekedir. Türkçe karakterler + UTF-8 encoding'inde 2 byte yer kaplamaktadr. UTF-8 encoding'i Unicode bir yazının adeta sıkıştırılmış bir hali gibi + düşünülebilir. + + Bugün pek çok programlama editörleri default durumda dosyayı Unicode UTF-8 encoding'ine göre saklamaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bilgisayar dünyasında çok kullanılan diğer bir sayı sistemi de 16'lık sistemdir. 16'lık sisteme İngilizce "hexadecimal + system" denilmektedir. 16'lık sistemde syaıları ifade etmek için 16 sembol bulunmaktadır. İlk 10 sembol 10'luk sistemdeki + sembollerden alınmıştır. Sonraki 6 sembol alfabetik karakterlerden alınmıştır: + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + A + B + C + D + E + F + + 16'lık sistemdeki her bir basamağa "hex digit" denilmektedir. Örneğin: + + 1FC8 + + Burada 4 hex digit'lik bir sayı vardır. 16'lık sistemdeki bir sayıyı 10'luk sisteme dönüştürmek için her hex digit + 16'lık kuvvetleriyle çarpılıp toplanır. Ancak 16'lık sistemdeki sayı kullanım gereği bakımından aslında 10'lu sisteme + pek dönüştürülmez. 16'lık sistemdeki her bir hex digit 4 bit ile ifade edilebilmektedir: + + 0 0000 + 1 0001 + 2 0010 + 3 0011 + 4 0100 + 5 0101 + 6 0110 + 7 0111 + 8 1000 + 9 1001 + A 1010 + B 1011 + C 1100 + D 1101 + E 1110 + F 1111 + + 16'lık sistemden ikilik sisteme dönüştürme yapmak çok kolaydır. Tek yapılacak şey bir hex digit'e karşılık yandaki + tablodaki 4 biti getirmektir. Örneğin: + + 1FC9 = 0001 1111 1100 1001 + FA3D = 1111 1010 0011 1101 + + ikilik sistemdeki bir sayı da 16'lık sisteme çok kolay dönüştürülür. Tek yapılacak şey sayıyı dörderli gruplayıp ona + karşı gelen hex digit'i yazmaktır. Örneğin: + + 1010 0001 1110 1000 0011 0101 = A1E835 + A 1 E 8 3 5 + + Bilgisayar dünyasında 16'lık sistem aslında ikilik sistemin yoğun bir gösterimi olarak kullanılmaktadır. Yani ikilik + sistem çok yer kapladığı için kişiler ikilik sistem yerine 16'lık sistemi kullanırlar. Bu nedenle belleğin, dosyanın + içeriğini görüntüleyen programlar bunları ikilik sistem yerine 16'lık sistemde görüntülemektedir. + + 1 byte 2 hex digit ile ifade edilmektedir. Örneğin: + + 1A 23 5C 78 + + Burada 4 byte'lık bir bilgi vardır. Örneğin 2 byte içerisinde yazılabilecek en küçük negatif işaretli sayının hex + karşılığı 8000 biçimindedir. Örneğin bir byte'lık işaretli sistemde yazılabilecek en büyük pozitif sayı 7F biçimindedir. + İşareti tamsayı sisteminde 4 byte içerisinde -1 sayısı FFFFFFFF biçimindedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Eskiden daha fazla kullanılıyor olsa da toplamda oldukça seyrek kullanılan dğer bir sayı sistemi de 8'lik sayı sistemidir. + Bu sisteme İngilizce "octal system" denilmektedir. 8'lik sayı sistemindeki her bir basamağa "octal digit" denir. Octal + digit sembolleri olarak 10'luk sistemin ilk 8 sembolü kullanılmaktadır: + + 0 + 1 + 2 + 3 + 4 + 5 + 6 + 7 + + Her octal digit 3 bit ile ifade edilebilir: + + 0 000 + 1 001 + 2 010 + 3 011 + 4 100 + 5 101 + 6 110 + 7 111 + + Bu durumda bir octal sayı ikilik sisteme kolay bir biçimde dönüştürülebilir: + + 476 100 111 110 + 741 111 100 001 + + Benzer biçimde ikilik sistemdeki bir sayı da sağdan sola üçer bir gruplandırılarak 8'lik sisteme dönüştürülebilmektedir. + Örneğin: + + 1011 1011 = 273 + 0111 1110 = 176 + + 8'lik sistem de ikilik sistemin yoğun bir gösterimi olarak kullanılmaktadır. Ancak 8'i tam ortalayamadığı için kullanımı + seyrektir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 7. Ders - 14/06/2022-Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Klavyeden bastımız tuşlara ilişkin karakterlerin İngilizce isimleri şöyledir: + + + plus + - minus, dash, hyphen + * asterisk + / slash + \ back slash + % percent sign + () paranthesis (left, right, opening, closing) + {} (curly) brace (left, right) + [] (square) bracket (left, right) + = equal sign + # sharp, number sign + ' single quote + "" doueble quote + _ underscore + ^ caret + & ampersand + ! exclamation mark + , comma + : colon + ; semicolon + | pipe + < less than + > greater than + . period + ? question mark + ` back tick + ~ tilde + @ at + ... ellipsis + $ dollar sign +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Ekrana Merhaba Dunya yazısını çıkartan örnek C programı aşağıdaki gibidir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + printf("Hello World\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bir C programı en yalın olarak önce bir text editör ile yazılır ve diske uzantısı ".c" biçiminde kaydedilir. Sonra + komut satırından C derleyicisi ile derlenir. Derleyiciler genel olarak komut satırından çalıştırılacak biçimde yazılırlar. + En çok kullanılan C derleyicileri şunlardır: + + - Microsoft C Derleyicisi + - gcc Derleyicisi + - clang Derleyicisi + - Intel C derleyicisi + + Bu derleyicilerin dışında daha pek çok C derleyicisi vardır ve çeşitli kesimler tarafından kullanılmaktadır. Windows'ta + ağırlıklı olarak Microsoft'un C derleyicileri kullanılmaktadır. UNIX/Linux ve macOS sistemlerinde ise çoğu kez "gcc" + ya da "clang" derleyicileri tercih edilmektedir. "gcc" derleyicisinin ve clang derleyicisinin Windows versiyonu da + vardır. "gcc" derleyicisinin Windows için gerçekleştirimine (Windows port;'una) "mingw" denilmektedir. + + C programını C derleyicisi ile derledikten sonra eğer hiçbir hata yoksa derleyici "relocatable object module" denilen + bir dosya oluşturmaktadır. Bu dosyaya biz Türkçe "amaç dosya" da diyeceğiz. Amaç dosya (relocatable object module) + daha sonra "bağlayıcı (linker)" denilen bir programa sokulur. Bu bağlayıcı programı da "çalıştırılabilir (executable)" + dosya üretir. Biz de nihayetinde bu dosyayı çalıştırırız. + + .c ---> C Derleyicisi ---> Amaç Dosya (Object file) ---> Bağlayıcı (Linker) ---> Çalıştırılabilir (executable) Dosya + + Bağlayıcı (linker da diyeceğiz) aslında bir grup amaç dosyayı alıp tek bir çalıştırılabilir dosya oluşturabilmektedir. + Bir amaç dosyanın içerisinde derlenmiş kodların yanı sıra bağlayıcnın birleştirme yapabilmesi için çeşitli bilgiler + de vardır. + + Windows'ta bağlayıcı olarak genellikle Microsoft'un "link.exe" isimli programı kullanılmaktadır. UNIX/Linux sistemlerinde + de ağırklıklı olarak GNU'nun "ld" isimli bağlayıcı ya da LLVM projesindeki "ldd" bağlayıcısı kullanılır. + + Derleyicinin ürettiği amaç dosyanın uzantıları Windows sistemlerinde ".obj", UNIX/Linux ve macOS sistemlerinde ise + ".o" biçimindedir. + + Bağlayıcının ürettiği "çalıştırılabilir" dosya ise Windows sistemlerinde ".exe" uzantılıdır. UNIX/Linux ve macOS + sistemlerinde dosyanın çalıştırılabilir olup olmadığı uzantı ile değil dosya erişim hakları ile ("x" hakkı ile) + belirlenmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Windows sistemlerinde Merhaba Dünya programının komut satırından derlenip çalıştırışması şöyle yapılır: + + 1) Program bir editörde yazılır ve ".c" uzantılı biçimde saklanır. Biz bunun "sample.c" olduğunu varsayalım. + + 2) Daha sonra komut satırı programı çalıştırılır ve dosayanın saklandığı dizine gidilir. Komut satırı programı olarak + "cmd.exe" programını doğrudan kullanmayınız. Çünkü bu program gerekli "path ayarlarına" sahip değildir. Bunun yerine + komut satırına geçmek için "Developer Command Prompt for VS 2022" programını kullanınız. Bu kısa yolu masaüstüne + taşırsanız rahat edersiniz. + + 3) Microsoft'un C derleyicisi "cl.exe" isimli programdır. Bu program en basit olarak şöyle çalıştırılır: + + cl + + Örneğin: + + cl sample.c + + "cl.exe" programı da derlemeyi yaptıktan sonra zaten bağlayıcıyı (linker) da kendisi çalıştırmaktadır. + + 4) Artık "cl.exe" derleme işlemini yapıp bağlayıcı programı da çalıştırdığı için çalıştırılabilir dosya oluşturulmuş + olur. Tek yapacağmız şey çalıştırılabilir programın ismini yazarak ENTER tuluna basmaktır. + + "cl.exe" derleyicisinin yalnızca derleme yapmasını ancak bağlayıcıyı çalıştırmamasını istiyorsak "/c" seçeneğini + (switch) kullanmamız gerekir. Örneğin: + + cl /c sample.c + + Şimdi artık derleyici linker programını çalıştırmayacaktır. Yalnızca ".obj" dosyayı oluşturacaktır. Biz istersek + bağlayıcıyı da bağımsız olarak çalıştırabiliriz. Microsoft'un bağlayıcı programı "link.exe" isimli programdır. + + link sample.obj + + Baradan "sample.exe" programı elde edilecektir. cl.exe derleyicisinde çalıştırılabilir dosyanın ismini değiştirebilmek + için /Fe: seçeneği kullanılmaktadır. Örneğin: + + cl /Fe:test.exe sample.c + + Artık çalıştırılabilir dosyanın ismi "sample.exe" değil "test.exe" olacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Linux sistemlerinde Merhaba Dünya programının derlenerek çalıştırılması da şöyle yapılmaktadr: + + 1) Yine önce bir editörde program yazılır ve ".c" dosyası olarak kaydedilir. Biz kaynak dosyamıza "sample.c" ismini + vermiş olalım. + + 2) Komut satırından kaynak dosyanın bulunduğu dizine geçilir. gcc derleyicisi ile clang derleyicilerinin komut satırı + seçenkleri tamamen aynıdır. Bu derleyicilerle erleme işlemi şöyle yapılır: + + gcc + clang + + Örneğin: + + gcc sample.c + + gcc de tıpkı cl.exe programında olduğu gibi önce derleme işlemini yapar. Sonra bağlayıcı programı çalıştırıp çalıştırılabilen + dosyayı oluşturur. gcc derleyicisi derlemeyi bitirip bağlayıcıyı çalıştırdıktan sonra "object dosyayı" silmektedir. + Bu biçimde oluşturulan çalıştırılabilen dosya "a.out" ismindedir. Bu dosyanın çalıştırılması şöyle yapılmalıdır: + + ./a.out + + Windows sistemlerinde çalıştırılabilir dosyanın yalnızca isminin yazılması yeterlidir. Ancak UNIX/Linux ve macOS sistemlerinde + ./isim biçiminde çalıştırma yapılır. gcc derleyicisinde çalıştırılabilir dosyaya isim vermek için "-o isim" çeneği + kullanılmaktaır. Örneğin: + + gcc -o sample sample.c + + Burada "sample.c" dosyası derlenir ve "sample" isimli çalıştırılabilir dosya oluşturulur. Tabii istersek gcc derleyicilerinde + de yalnızca derleme yapıp bağlayıcıyı çalıştırmayabiliriz. Bunun için "-c" seçeneği kullanılmaktadır. Örneğin: + + gcc -c sample.c + + Burada derleme işlemi yapılır, "sample.o" object dosyası oluşturulur ancak başlayıcı çalıştırılmaz. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Micrsoft Visual Studio IDE'sinde bir C programının derlenip çalıştırılabilmesi için tipik olarak şunlar yapılmalıdır: + + 1) Önce Visual Studio IDE'si çalıştırılır. (Kurs yapıldığı sırada kullanılan IDE Visual Studio 2022 Community Edition + biçimidedir.) IDE açlıştırıldıktan sonra bir giriş sayfası gözükür. Oradan "Continue without code" seçilerek ana ekrana + geçilir. + + 2) Visual Studio IDE'sinde bir çalışma yapmak için bir proje yaratılmalıdır. Ancal projeler de "çözüm (solution)" denilen + kapların içerisindedir. O halde aslında bir proje yaratmak için bir çözüm de yaratılmaktadır. Bir çözüm aslında birdne + fazla projeyi tutan bir kap gibidir. Proje yaratmak için "File/New/Project" seçilir. Proje türü olarak "C++ Empty Project" + seçilir. + + 3) Bundan sonra projeye bir isim verilir. Visual Studio proje bilgilerini burada ismi verilen bir dizin yaratarak onun + içerisine yerleştirmektedir. "Location" proje dizininin hangi dizinin altında yaratılacağını belirtir. "Place solution + and project in the same directory" checkbox'ı çarpılanmalıdır. Sonra proje yaratılır. Artık elimizde içi boş bir proje + vardır. Bir proje yaratıldığında aynı zamanda bir "çözüm (solution)" de yaratılmış olur. Çözümü idare etmek için + "Solution Explorer" denilen pencereden faydalanılmaktadır. + + 4) Artık sıra projeye bir kaynak dosya eklemeye gelmiştir. Bu işlem "Project/Add New Item" menüsü ile ya da "Solution + Explorer"da proje üzerinde bağlam menüsünü açıp "Add/New Item" seçilerek de yapılabilir. Artık karşımıza başka bir + diyalog penceresi çıkacaktır. Burada "C++ File" seçilip dosya ismi uzantısı ".c" olacak biçimde seçilmelidir. Microsoft + C++ demekle aynı zamanda C'yi de kastetmektedir. Aslında "cl.exe" derleyicisi hem C hem de C++ derleyicisidir. Bu + derleyici kaynak kodun uzantısına bakarak hangi dile göre derleme yapacağına karar verir. Dolayısıyla bizim kesinlikle + dosya uzantısını ".c" biçiminde girmemiz gerekir. + + 5) "Kaynak dosya" projeye eklendikten sonra kod yazılır. + + 6) Build/Compile seçilirse dosya yalnızca derlenir. "Build" kavramı C/C++ dünyasında "derleme ve bağlama işlemini" + anlatmaktadır. "Build/Build Solution" seçilirse solution içerisindeki tüm projeler derlenip link edilir. Build/Build XXX" + seçilirse (burada XXX aktif projenin ismidir) bu durumda yalnızca aktif proje derlenerek bağlanır. + + 7) Programı çalıştırmak için "Debug/Start Without Debugging" seçilir. Bunun kısa yol tuşu "Ctrl + F5"tir. Zaten + Ctrl+F5'e bastığımızda dosyada bir değişiklik varsa build işlemi yapılmaktadır. O halde aslında bizim tek yapacağımız + şey Ctrl+F5 tuşlarına basmaktır. + + Projeyi (ve çöüzümü) kapatma işlemi File/Close Solution menüsü ile yapılabilir. Bir projeyi açmanın en kolay yolu + giriş ekranındaki "Open recent" listesinden son projelerden birini seçmektir. Dğer yolu "File/Open-Project" menüsünü + seçip buradan solution dizininne gelip uzantısı ".sln" olan dosyayı seçmektir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 8. Ders - 16/06/2022-Perşembe +----------------------------------------------------------------------------------------------------------------------*/ + + +/*---------------------------------------------------------------------------------------------------------------------- + C/C++ için çok tercih edilen diğer bir IDE de "Qt Creator" denilen IDE'dir. Buradaki çalışma biçimi ana hatlarıyla + Visual Studio'ya benzemektedir. Önce yine bir proje yaratılmalıdır. Bunun için "File/New File or Project" menüsü + seçilir. Açılan diyalog penceresinden template olarak "None Qt Project" seçilir. Buradan da "Plain C Application" + seçilir. Projeye isim verilir ve projenin yaratılacağı dizin belirtilir. Qt Creator IDE'si bu seöenekle bir ".c" + dosyasını projeye ekleyip onun içerisine birkaç satırlık "Merhaba Dünya" programını yazmaktadır. Derleme ve link + işlemi ve çalıştırma işlemi tek tuşla (Ctrl+R) yapılabilir. Yine bu işlem GUI ekranındaki çalıştır düğmesine tıklanarak + da yapılabilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Kod derleyici tarafından derlenirken derleyiciler bazı sorunlar karşısında "tanı mesajları (diagnositic messages)" + ile bu sorunları programcıya iletirler. Derleyicilerin verdiği tanı mesajları üçe ayrılmaktadır: + + 1) Uyarılar (Warnings) + 2) Hatalar (Errors) + 3) Ölümcül Hatalar (Fatal Errors) + + Uyarılar amaç kod oluşumunu engellemeyecek derecede mantıksal hatalar için verilmektedir. Yani derleyici kodu + anlamlandırmakla birlike programcının yapmış olabileceği olası bazı mantık hatalarına dikkat çekmek için uyarı + vermektedir. Hatalar amaç dosyanın (object file) oluşumunu engelleyecek derecede olan ciddi hatalardır. Genellikle + dilin kurallarına uyulmaması yani kodun hatalı bir biçimde yazılması sonucunda oluşur. Programcının mutlaka bu tür + hataları düzeltmesi gerekir. Ölümcül hatalar (fatal errors) derleme işleminin bile devam ettirilmesini engelleyecek + derecede önemli hatalardır. Normal olarak bir hata ile karşılaşıldığında tüm tanı mesajlarının topluca listelenmesi + için derleme işlemine devam edilir. Ancak bir ölümcül hata ile karşılaşıldığında artık derleme işlemi bile sonlandırılır. + Ölümcül hatalar genellikle sistemdeki önemli sorunlar yüzünden ortaya çıkmaktadır. Örneğin diskin tamamen dolu + olması, derleyicinin işlemine devam edebilmesi için gereken ana belleğin yetersiz olması gibi durumlar tipik ölümcül + hata gerekçeleridir. + + Derleyiciler genellikle tanı mesajllarında ilgili sorunun kaynak kodun neresinde eolduğunu da belirtirler. Pek çok + derleyici hata mesajlarına içsel birer numara vermektedir. Bu numara yoluyla tanı mesajı hakkında daha fazla bilgi + elde edilebilmektedir. Tanı mesajları standardize edilmemiştir. Derleyiciden derleyiciye tanı mesajlarının içeriği + değişebilmktedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir programlama dilinde kendi başına anlamlı olan en küçük birime "atom (token)" denilmektedir. Örneğin aşağıdaki gibi + C'de yazılmış bir kod parçası olsun: + + if (a > 10) + x = 10; + else + y = 20; + + Bu kodu şöyle atomlarına ayırabiliriz: + + if + ( + a + > + 10 + ) + x + = + 10 + ; + else + y + = + 20 + ; + + Burada koddaki tüm öeğelerin en küçük anlamı birimlere ayrıştırıldığına dikkat ediniz. "Merhana Dünya" programı + da şöyle atomlarına ayrılabilir: + + # + include + < + stdio.h + > + int + main + ( + void + ) + { + printf + ( + "Hello World\n" + ) + ; + return + 0 + ; + } + + Gerçekten de derleyiciler derlemenin ilk aşamasında kaynak kodu bu biçimde atomlarına ayırmaktadır. Derlyicilerin + bu işi yapan modüllerine "scanner" ya da "lexical analyzer" ya da "tokenizer" denilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Atomlar tipik olarak 6 gruba ayrılmaktadır: + + 1) Anahtar Sözcükler (Keywords/Reserved Words): Dil için özel anlamı olan, değişken olarak kullanılması yasaklanmış + sözcüklerden oluşan atomlardır. Örneğin "if" gibi, "return" gibi, "int" gibi. Bu sözcükler doğrudan derleyici tarafından + tanınmaktadır. + + 2) Değişkenler (Identifers): İsmini programcının istediği gibi verebildiği atomlardır. Örneğin bir programdaki i, count, + a, b gibi atomlar tipik olarak değişken atomlardır. "Merhaba Dümya" programındaki printf ve main anahtar sözcük değildir. + Değişken atom statüsündedir. Bir atomun anahtar sözcük olması için derleyicinin onu gördüğünden değişkenden farklı + bir işlem uygulaması gerekir. + + 3) Sabitler (Literals): Bir sayı ya bir değişkenin içerisindedir ya da program içerisinde doğrudan kullanımıştır. İşte + program içerisinde doğrudan yazılmış olan sayılara "sabit" denilmektedir. Örneğin: + + a = b + 10; + + Burada a ve b değişken atomdur, ancak 10 sabit atomdur. + + 4) Operatörler (Operators): Bir işleme yol açan ve işlem sonucunda bir değer üretilmesini sağlayan + gibi - gibi, * + gibi atomlara operatör denilmektedir. Örneğin: + + a = b + c * 3 + + Bu ifadedeki atomlar ve türleri şöyledir: + + a değişken + = operatör + b değişken + + operatör + c değişken + * operatör + 3 sabit + + 5) Stringler (String literals): İki tırnak içerisindeki yazılar iki tırnaklarıyla birlikte tek bir atom belirtir. + Bunlara "string" denilmektedir. C standartlarında string'ler de sabit atom olarak sınıflandırılmaktadır. + + 6) Ayıraçlar (Delimiters/Punctuators): Yukarıdaki grupların dışında kalan ifadeleri birbirinden ayırmak için kullanılan + tüm atomlar ayıraç grubundadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Değişkenlerin, operatörlerin ve sabitlerin her bir kombinasyonuna "ifade (expression)" denilmektedir. Örneğin: + + a + b + a + b - 2 + 10 + 3 * 4 + 3 - 2 * a + foo() + a = b * c + + birer ifadedir. Tek başına bir değişken ve tek başına bir sabit ifade belirtir. Ancak tek başına bir operatör ifade + belirtmez. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bellekte yer kaplayan ve erişilebilen bölgelere "nesne (object)" denilmektedir. Programlama dillerindeki değişkenler + genellikle nesne durumundadır. Örneğin: + + a = 10 + + gibi bir ifadede a bir nesne durumundadır. Biz bu a ismiyle a'nın bellek bölgesine erişebilmekteyiz. Bir olgunun + nesne belirtmesi için yalnızca bellekte yer kaplaması yetmez. Aynı zamanada "erişilebilir" olması da gerekir. + Örneğin sabitler de bellekte yer kaplarlar. Ancak erişilebilir olmadıkları için nesne değillerdir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bir ifade ya nesne belirtir ya da nesne belirtmez. Nesne belirten ifadelere "sol taraf değeri (lvalue)", nesne + belirtmeyen ifadelere "sağ taraf değeri (rvalue)" denilmektedir. Örneğin: + + a ifadesi nesne belirtir. Sol taraf değeridir. + b[i] ifadesi nesne belirtir, sol taraf değeridir. + a + b ifadesi nesne belirtmez, sağ taraf değeridir. + 10 ifadesi nesne belirtmez, sağ taraf değeridir. + printf("Helo World") ifadesi nesne belirtmez, sağ taraf değeridir. + + Sol taraf değeri (left value) ismi tipik olarak bu tür iafadelerin atama operatörünün soluna getirilebilmesi nedeniyle + verilmiştir. Sağ taraf değeri (right value) atama operatörünün soluna getirilemeyen ifadelere denilmektedir. Tabii + bunlar tipik olarak atama operatörünün sağına da getirilirler. Ancak atama operatörünün sağına getirilen her şey + sağ taraf değeri değildir. Soluna getirilemeyenler sağ taraf değeridir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Sentaks açıklamak için çeşitli "meta diller (meta languages)" oluşturulmuştur. Bunların en çok kullanılanı "BNF + (Backus Naur Form)" denilen notasyondur. BNF notasyonu "EBNF (Extended BNF)" biçiminde ISO tarafından standardize de + edilmiştir. Gerçekten de programlama dillerinin standartları genellikle BNF notasyonu ile ya da onun bir türevi ile + açıklanmaktadır. Ancak biz kurusumuzda "açısal parantez-köşeli parantez" tekniğini kullanacağız. Bu teknikte açısal + parantezler içerisinde öğeler zorunlu öğeleri, köşeli parantezler içerisindeki öğeler "isteğe bağlı (optional)" öğeleri + belirtmektedir. Bunların dışındaki tüm atomların belirtilen konumda bulundurulması gerekir. Örneğin if deyimini + bu teknikle şöyle ifade edilebiliriz: + + if () + + [ + else + + ] +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 9. Ders 21/06/2022-Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Boşluk oluşturmak için kullanılan karakterlere "boşluk karakterleri (white space)" denilmektedir. Boşuk karakterli + şunlardır: + + SPACE (32) + LF (Line Feed) (10) + CR (Carriage Return) (13) + TAB (9) + VTAB (11) + + TAB karakter aslında tek bir karakterdir. Bu karakteri gören editörler imleci belli bir miktarda ilerletirler. Bazı + editörler biz TAB tuşuna bastığımızda dosyaya TAB karakter basmaz bunun yerine editörün ayarlarında belirtildiği + miktarda SPACE karakteri basar. Bunun nedeni kaynak kod başka bir TAB ayarına ayarlanmış bir editörde açıldığında + aynı biçimde gözükmesini sağlamaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'nin yazım kuralı iki madde ile özetlenebilir: + + 1) #'li satırlar hariç atomlar arasında istenildiği kadar boşluk karakterleri bırakılabilir. Örneğin aşağıdaki program + geçerlidir: + + #include + + int + + main + ( void + ) + { + printf + ( + "Hello World\n" + ) + ; + + return + 0 + ; + } + + 2) #'li satırlar hariç atomlar istenildiği kadar bitişik yazılabilirler. Ancak anahtar sözcüklerle değişkenler ve + sabitler btişik yazılamazlar. Merhaba Dünya programıını aşağıdaki gibi kompakt bir biçimde de yazabilirdik: + + #include + + int main(void){printf("Hello World\n");return 0;} + + Tabii programcının kodunu güzel gözükecek ve iyi okunabilecek biçimde yazması gerekir. C'de çeşitli yazım stilleri + vardır. En yaygın kullanılan yazım stili Dennis Ritchie ve Brian Kernighan'ın "The C Programming Language" kitabında + uyguladığı yazım biçimidir. Buna "Ritchie Kernighan Tarzı" denilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki Merhaba Dünya programının açıklaması şöyledir: + + Burada #include satırı "stdio.h" isimli bir dosyanın kaynak koda dahil edildiğini belirtmektedir. Yani bu + satır "stdio.h" dosyasının içeriğinin oraya "paste edileceği" anlamına gelmektedir. Başka bir deyişle biz "stdio.h" + dosyasını bu komutun bulunduğu yere yerleştirip bu komuttu silsek tamamen aynı durum oluşacaktır. Programda main isimli + bir fonksiyon tanımlanmıştır. Bir fonksiyonun tanımlanması onun bizim tarafımızdan yazılması anlamına gelir. Yani bu + programda biz main isimli bir fonksiyon yazmış durumdayız. Bir fonksiyonu tanımlamanın (yani yazmanın) genel biçimi + şöyledir: + + [fonksiyonun geri dönüş değerinin türü] ([parametre bildirimi]) + { + /* ... */ + } + + Fonksiyonun geri dönüş değerinin türü C90'da yazılmayabiliyordu. Bu durumda C90 bunun int olarak yazılmış olduğunu + varsayıyordu. Ancak C99 ile birlikte fonksiyonlarda geri dönüş değerinin belirtilmesi zorunlu tutulmuştur. main + fonksiyonun geri dönüş değerinin türü standartlara göre int olmak zorundadır. + + C'de "main" özel bir fonksiyondur. C programları her zaman "main" isimli fonksiyondan çalışmaya başlar. Programlama + dillerinde programın çalışmaya başladığı fonksiyonlara "entry point" denilmektedir. Bir fonksiyonun parametreleri + olabilir ya da olmayabilir. Eğer fonksiyonun parametresi yoksa parametre parantezi boş bırakılabilir ya da oraya + void anahtar sözcüğü yazılabilir. Tanımlama sırasında boş bırakmakla void anahtar sözcüğünün yazılması arasında bir + fark yoktur. + + Her fonksiyonun bir ana bloğu olmak zorundadır. C'de iki küme parantezi arasındaki bölgeye "blok (block)" denilmektedir. + Bir fonksiyon çalıştırıldığında fonksiyonun ana bloğundaki deyimler sırasıyla çalıştırılır. Ana blok bittiğinde + fonksiyon sonlanmış olur. main programı bittiğinde dolayısıyla tüm program sonlanmış olacaktır. "Merhaba Dünya" + programında main fonksiyonun ana bloğunun içerisinde printf isimli bir fonksiyon çağrılmıştır. Bir fonksiyonun + çağrılması (call) demek onun çalıştırılması demektir. Bir fonksiyon çağrıldığında akış fonksiyona gider, fonksiyonun + içerisindeki deyimler tek tek çalıştırılır. Fonksiyon bitince akış çağırma noktasından devam eder. printf fonksiyonu + çağrıldığında iki tırnak içerisindeki yazıları ekrana (stdout dosyasına) basmaktadır. Ekranda bir imleç (cursor) + vardır. Yazı bu imlecin bulunduğu itibaren ekrana yazdırılır. Sonra imleç yazının sonunda bırakılır. İmleç program + çalışmaya başladığında satırın başındadır. printf fonksiyonunda iki tırnak içerisindeki "\n" karakteri "imleci aşğı + satırın başına geçir" biçiminde özel bir anlama gelmektedir. Yani bundan sonra biz bir daha printf fonksiyonunu + çağıracak olsak artık o yazı aşağı satırın başından itibaren yazılacaktır. printf bir standart C fonksiyonudur. + Standart C fonksiyonları derleyicileri yazanlar tarafından yazılmış (tanımlanmış) olan hazır fonksiyonlardır. + main fonksiyonun sonunda return deyimi bulunmak zorunda değildir. Bu deyim ileride açıklanacktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + printf("Hello World\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'nin C90, C99, C11, C17 ve C23 standartlarının olduğunu belirtmiştik. Derleyiciler genel olarak bu standartlara uygun + olacak biçimde derleme yapabilmektedir. Visual Studio IDE'sinde derleyicinin uygulayacağı standardı ayarlamak için proje + seçeneklerine gelinir. C-C++/Language/C Language Standart sekmesinden standart belirlenir. Biz kursumuzda burada "ISO C17" + standardını aktif hale getireceğiz. Aynı şey Qt Creator IDE'sinde ".pro" dosyasının içerisine aşağıdaki satırın eklenmesiyle + yapılabilmektedir: + + CONFIG += c17 + + gcc ve clang derleyicilerinde komut satırında derleme yaparken -std=c90, -std=c99, -std=c11, -std=c17 seçenekleriyle + derleme standardı ayarlanabilir. Örneğin: + + gcc -std=c17 -o sample sample.c + + Ayrıca Microsoft derleyicilerinde proje seçeneklerinden C-C++ sekmesinde "SDL Checks" "No" yapılarak kapatılmalıdır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Programlama dillerinde "tür (type)" bir nesnenin bellekte kapladığı alanı, onun içerisindeki 1'lerin ve 0'ların nasıl + yorumlanacağını, o nesnenin hangi operatörlerle işleme sokulabileceğini belirten önemli bir bilgidir. C'de her nesnenin + ve her ifadenin bir türü vardır. Türler çeşitli anahtar sözüklerle ifade edilirler. Biz de aşağıda C'deki temel türleri + açıklayacağız. + + [signed] int: Bu tür işaretli bir tamsayı türüdür. int türünün kaç byte yer kaplayacağı standartlarda derleyicileri + yazanların isteğine bırakılmıştır. Ancak standartlara göre int türü minimum 2 byte olmalıdır. Buügn 32 bie ve 64 bir + Windows ve UNIX/Linux ve macOS sistemlerindeki derleyicilerde int türü 4 bye (32 bit) uzunluktadır. Dolayısıyla int + türden bir nesne bu sistemlerde [-2147483678, 2147483647] aralığında tamsayı değerler tutabilir. Bazı mikrodenetleyici + derleyicilerinde ise int 2 byte (16 bit) uzunluğunda olabilmektedir. Derleyicileri yazanlar genellikle int türünü o + sistemdeki CPU yazmaçlarının uzunluğu kadar ya da o uzunlukla ifade edilebilecek kadar almaktadır. Bu tür belirtilirken + int demekle signed int demek arasında ya da int signed demek arasında bir fark yoktur. + + [unsigned] int: Her işaretli tamsayı türünün bir de işaretsiz biçimi vardır. signed int türünün işaretsiz biçimi + unsigned int türüdür. Tamsayı türlerinin işaretli biçimleri ile işaretsiz biçimleri aynı miktarda yer kaplarlar. + Aralarındaki tek fark işaret bitinin yorumudur. Dolayısıyla bu tür de 32 bit ve 64 bit UNIX/Linux ve macOS sistemlerinde + 4 byte yer kaplamaktadır. unsigned int türünden bir nesne içerisine bu sistemlerde yerleştirilebilecek sayı sınırı + [0, +4294967295] biçimindedir. Bu türü biz unsigned biçiminde ya da unsigned int biçiminde ya da int unsigned biçiminde + ifade edebiliriz. + + [signed] long [int]: long türü int türünden uzun olabilir ya da int türüyle aynı uzunlukta olabilir. Ancak int türünden + daha kısa olamaz. Standratlara göre long türü en az 4 byte (32 bit) uzunlukta olmak zorundadır. long türü de işaretli + bir tamsayı türüdür. Buradaki long ismi "int türünden uzun olabilen" anlamına gelmektedir. 32 bit ve 64 bit Windows + sistemlerindeki derleyicilerde long türü int türüyle aynı uzunluktadır (yani 4 byte). Ancak 32 bit UNIX/Linux ve + macOS sistemlerindeki derleyicilerde long türü 4 byte iken, 64 bit UNIX/Linux ve macOS sistemlerindeki derleyicilerde + long türü 8 byte (64 bit) uzunluğundadır. long türünü biz en kısa biçimde "long" olarak ifade edebiliriz. Ancak + signed long int, long int, signed int long gibi anahtar sözcükler yer değiştirerek de ifade edebiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 10. Ders 23/06/2022-Perşembe +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + unsigned long [int]: Bu tür long türünün işaretsiz biçimidir. Dolayısıyla sistemlerde long türüyle aynı uzunlukta + yer kaplar ancak sayının başındaki bit işaret biti olarak ele alınmaz. 32 ve 64 bit Windows Sistemlerinde ve 32 bit + UNIX/Linux ve macOS sistemlerinde bu tür long türünde olduğu gibi 4 byte (yani 32 bit) yer kaplamaktadır. Dolayısıyla + bu sistemlerdeki sınırı [0, +4294967295] biçimindedir. + + [signed] short [int]: Bu tür int türünden küçük olabilen ya da int türü ile aynı uzunlukta olabilen işaretli bir tamsayı + türüdür. Standartlara göre short türü en az 2 byte (yani 16 bit) olmak zorundadır. 32 bit ve 64 bit Windows ve UNIX/Linux + ve macOS sistemlerinde short türü 2 byte (yani 16 bit) uzunluktadır. Dolayısıyla bu sistemlerde bu türden bir nesnesinin + içerisine biz [-32768, +32767] sınırları içerisinde bir tamsayı yerleştirebiliriz. + + unsigned short [int]: Bu tür signed short türünün işaretsiz biçimidir. Dolayısıyla short türü kadar yer kaplar. short + türünün 2 byte olduğu sistemlerde bu türden bir nesnenin içerisine biz [0, +65535] arasında tamsayı değerler yerleştirebiliriz. + + signed char: C standartlarında "byte" sözcüğü pek az yerde kullanılmıştır. Çünkü "byte" genellikle 8 bit için kullanılan + bir terimdir. Oysa bazı bilgisayar sistemlerinde RAM'deki adreslenebilen birimler 8 bit değil 10, 11 bit değerlerde + olabilmektedir. Tabii bu sistemler son derece seyrektir. Ancak standartlar bu uç durumu da dikkate almaktadır. C + standartlarında char terimi "RAM'de adreslenebilen en küçük birimin büyüklüğünü" temsil etmektedir. Örneğin tipik + olarak eğer adreslenebilen en küçük birim 8 bit ise char 8 bittir. Ancak 10 bit ise char 10 bittir. Görüldüğü gibi + eğer C standartlarında char yerine byte terimi kullanılsaydı byte 8 bit olduğu için bu uç durum temsil edilemeyebilirdi. + Tabii bugünkü yaygın sistemlerin hemen hepsinde adreslenebilen en küçük birim 8 bittir. Dolayısıyla yaygın sistemlerin + hepsinde gerçekten char türü 8 bit yani bir byte uzunluğundadır. Zaten C standartlarında "byte" terimi tamamen bit + uzunluğu farklı olabilen yani "adreslenebilen en küçük birim" anlamında kullanılmaktadır. Başka bir dyişle bu tanımla + "char" ile "byte" aynı anlamdadır. Ayrıca standartlar ilgili sistemdeki adreslenebilen en küçük birimdeki bit sayısının + kaç bit olduğunu (yani char türünün kaç bitten oluştuğunu) dosyası içerisinde CHAR_BITS sembolik sabitiyle + derleyicinin belirtmesini zorunlu tutmaktadır. + + Mademki char türü yaygın sistemlerin hepsinde 8 bitten oluşmaktadır, o halde signed char türünün de bu sistemlerdeki + sınırları [-128, +127] arasındadır. Özetle signed char bir byte'lık işaretli tamsayı türünü belirtmektedir. + + unsigned char: Bu tür char türünün işaretsiz biçimidir. Dolayısıyla bu türün de bellekte kapladığı alan ilgili sistemdeki + adreslenebilen en küçük birimin bit uzunluğu kadardır. 8 bitlik yaygın sistemlerde unsigned char türünden bir nesneye + [0, +255] arasında değerler yerleştirilebilir. + + char: Yalnızca char denildiğinde bunun signed char mı yoksa unsigned char mı anlamına geleceği C standartlarında + derleyicileri yazanların isteğine bırakılmıştır. Microsoft C derleyicileri, gcc ve clang derleyicileri char türünü + default olarak signed char kabul etmektedir. Fakat başka derleyiciler unsigned char kabul edebilirler. Aslında + Microsoft derleyicilerinde, gcc ve clang derleyicilerinde char denildiğinde default durum derleyici ayarlarından da + değiştirilebilmektedir. Her ne kadar char türü ilgili sistemde signed char ya da unsigned char anlamına geliyorsa da + char, signed char ve unsigned char ne olursa olsun farklı türler gibi değerlendirilmektedir. Bunun önemi başka + konularda ortaya çıkacaktır. + + [signed] long long [int]: Bu tür C99 ile birlikte standartlara dahil edilmiştir. Dolayısıyla C90 uyumlu eski C + derleyicilerinde bu türü kullanamayabilirsiniz. long long türü long türünden uzun ya da long türüyle aynı uzunlukta + olabilen işaretli bir tamsayı türüdür. Standartlarda minimum 8 byte (yani 64 bit) olabileceği belirtilmiştir. Şu + andaki yaygın derleyicilerin hepsinde long long türü 8 byte uzunluktadır. 8 byte uzuluk için long long türünden bir + nesneye yerleştirilebilecek sayı sınırı [-9223372036854775808, +9223372036854775807] (katrilyar mertebesinde, 8 + exabyte) biçimindedir. + + unsigned long long [int]: Bu tür de long long türünün işaretsiz biçimidir. Dolayısıyla yaygın sistemlerin hepsinde + 8 byte (yani 64 bit) uzunluktadır. unsigned long long türünden bir nesneye yerleştirilecek sayı sınırı da + [0, +18446744073709551615] (16 exabyte) biçimindedir. +/*---------------------------------------------------------------------------------------------------------------------- + +/*---------------------------------------------------------------------------------------------------------------------- + Yukarıdaki tüm türlere C'nin tamsayı türleri denilmektedir. C'de ayrıca üç tane de gerçek sayı (noktalı sayı) türü + vardır: float, double ve long double. Gerçek sayı türlerinin işaretli ve işaretsiz biçimleri yoktur. Bunlar zaten + doğuştan işaretlidir. + + float: Bu tür 4 byte uzunluğunda gerçek sayı türüdür. Her ne kadar standartlar kullanılacak gerçek sayı formatını açıkça + belirtmiş olmasa da "Implementation Limits" kısmında gerçek sayı türleri için belirtilen limitler "IEE 754" standardını + ima etmektedir. Bu durumda float türü hemen her sistemde 4 byte uzunluktadır. float türünün yuvarlama hatalarına direnci + zayıftır. Bu nedenle float türü aslında C programcıları tarafından az tercih edilen bir gerçek sayı + türüdür. + + double: Standartlara göre double türü float türü ile aynı olabilir ya da ondan daha duyarlıklı olabilir. Yaygın sistemlerin + büyük çoğunluğunda double türü 8 byte uzunluktadır ve "IEEE 754 LLong Real Format" biçiminde temsil edilmektedir. Ancak + bazı mikrodenetleyici derleyicilerinde double türü float ile tamamen aynı uzunlukta olabilmektedir. C programcılarının + en fazla tercih ettiği gerçek sayı türü double türüdür. Çünkü bu türün yuvarlama hatalarına direnci float türünden çok + daha iyidir. + + long double: long double standartala göre double ile aynı duyarlılıkta ya da double türünden daha duyarlıklı olabilen + bir türdür. Bugün Microsoft C derleyicilerinde, gcc ve clang derleyicilerinde long double türü tamamen double türüyle + aynı özelliktedir. Yani bu tür de bu derleyicilerde "IEEE 754 Long Real Format" biçiminde ifade edilmektedir. Fakat + bazı derleyicilerde (Örneğin eski Borland firmasının C derleyicilerinde) long double 10 byte'lık "IEEE 754 Extended + Real Format" biçiminde de derleyiciler tarafından alınabilmektedir. + + Bir C derleyicisinde aslında float, double ve long double türlerinin hepsi 4 byte uzunlukta olabilir. +/*---------------------------------------------------------------------------------------------------------------------- + +/*---------------------------------------------------------------------------------------------------------------------- + C99 ile birlikte C'ye ikil değerler tutmak için _Bool isminde yeni bir tür daha eklenmiştir. (Bu tür isminin bu biçimde + size tuhaf gelecek şekilde isimlendirilmesinin amacı geçmişe doğru uyumu koruyabilmek içindir. C99 çıktğında "bool" + gibi bir ismi programcılar kendi programlarında kullanmış olabileceklerinden dolayı bu türü temsil etmek için "reserved + " isimlerden biri tercih edilmiştir. C'de başı '_' ile başlayan ve ilk harfi büyük harf olan isimlerin zaten kullanılması + yasaklanmış durumdaydı.) _Bool türü için standartlar 0 ve 1 değerlerini tutabilen bir büyüklükte olması gerektiğini + belirtmişlerdir. Dolayısıyla _Bool türü aslında herhangi bir tamsayı türünün uzunluğu kadar olabilir. Tabii derleyiciler + bu türden nesneler için genel olarak 1 byte yer ayırmaktadır. + + _Bool türü dosyası içerisinde "bool" ismiyle de typedef edilmiştir. Dolaysıyla programcı isterse + başlık dosyasını include edip _Bool yerine bool ismini de kullanabilir. Genellikle bool türünün olduğu diğer programlama + dillerinde true ve false biçiminde anahtar sözcükler de bulundurulmaktadır. Ancak C99'da bu biimde anahtar sözcüler + yoktur. Ancak içerisinde true 1 olarak, false 0 olarak define edilmiştir. Dolayısıyla eğer + dosyası include edilirse true ve false sözcükleri 1 ve 0 yerine kullanılabilir. + + C'nin üzerinde çalışılan son sürümü olan C23'te artık bool isminin de true ve false değerlerinin de anahtar sözcük + yapılması planlanmıştır. Yani artık C23 ile birlikte tıpkı C++'ta olduğu gibi bool bir anahtar sözcük durumundadır. + Ancak C23'te C99 uyumunun korunması için _Bool türü de muhafaza edilmektedir. + + Son olarak C99 ile birlikte C'ye karmaşık sayı (complex number) türü de eklenmiştir. Karmaşık sayı belirtmek için + _Complex tür ismi anahtar sözcük olarak dile eklenmiştir. Ancak _Complex tek başına kullanılamaz. float, double + ve long double tür isimleriyle birlikte kullanılabilir. Yani C99 ile birlikte üç karmaşık sayı türü dile eklenmiş + durumdadır: + + float _Complex + double _Complex + long double _Complex + + Karmaşık sayılar gerçek ve sanal kısımları float olan, double olan ve long double olan iki bileşenli sayılardır. + Karmaşık sayı için i sayısı C99'da _COMPLEX_I anahtar sözcüğü ile temsil edilmiştir. Dolaysyıyla örneğin double + _Complex türünden + bir z değişkenine biz 3.2 + 2.4i değerini şöyle yazarız: + + z = 3.2 + 2.4 * _COMPLEX_I + + Ayrıca yazım kolaylığı için dosyası içerisinde _COMPLEX anahtar sözcüğü "complex" ismiyle de typedef + edilmiştir. Yani biz eğer dosyasını include edersek _COMPLEX yerine complex sözcüğünü de kullanabiliriz. + Benzer biçimde içerisinde I isimli sembolik sabit de _COMPLEX_I olacak biçimde define edilmiştir. + Yani biz dosyasını include etmiş isek i sayısı için _COMPLEX_I yerine I harfini de kullanabiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bu kadar çok tür varken aslında programcılar özel bir neden olmadıktan sonra tamsayı türü olarak hep int türünü, + gerçek sayı türü olarak da double türünü tercih ederler. C programcıları bir değişkenine içerisine küçük tamsayı değerleri + yerleştirecek olsalar bile o dğeişkeni char, short olarak değil yine int olark tanımlarlar. Fakat örneğin bir nicelik + int türünün sınırları içerisine sığmıyorsa artık daha büyük türler seçilmelidir. int türünden küçük türler programcılar + tarafından tekil nesneler için değil büyük diziler için tercih edilmektedir. Örneğin bir kişinin yaşını bir değişkende + tutacak olalım. Biz yine bu ndeğişkeni int türden almalıyız. Ancak bir milyon kişinin yaşını tutacaksak artık bu bir + milyonluk diziyi int türünden değil char türünden oluşturabiliriz. Aynı durum double türü için de geçerlidir. Programcı + ancak çok miktarda noktalı sayıyı tutacaksa float türünü tercih etmelidir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 11. Ders 28/06/2022-Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C/C++, Java, C# gibi statik tür sistemine sahip programlama dillerinde bir değişkeni kullanmadan önce onun derleyiciye + tanıtılması gerekir. İşte değişkenlerin kullanılmadan önce derleyiciye tanıtılması işlemine "bildirim (declaration)" + denilmektedir. Bildirim işleminin basit genel biçimi şöyledir: + + ; + + Buradaki değişken listesi aralarına ',' atomu getirilmiş bir ya da birden fazla değişkenden oluşabilir. Örneğin: + + int a; + long b, c, d; + double weight; + + Aslında bildirim işleminin genel biçimi biraz daha ayrıntılıdır. Burada biz basit bir genel biçim verdik. + + C "büyük harf küçük harf duyarlılığı olan (case sensitive)" bir programlama dilidir. Yani C'de değişken isimlendirmesinde + büyük harflerle küçük harfler farklı karakterler olarak ele alınırlar. Değişken isimlerndirmesinde pek çok programlama + dilindeki benzer kuralalr geçerlidir: + + - Değişken isimleri nümerik karakterlerle başlatılamaz. Ancak alfabetik karakterlerle başlatılıp nümerik karakterlerle + devam ettirilebilir. + - Değişken isimleri boşluk içeremez, operatör içeremez. + - Değişkenler anahtar sözcüklerdne oluşturulamazlar. + - Alt tire karakteri alfabetik karakter gibi kullanılabilmektedir. + - Değişken isimlerindeki karakterler İnglizce küçük harfler, İngilizce büyük harfler ve alt tire karakterleri ve sayısal + karakterler olabilir. Türkçe gibi diğer dillerin karakterlerinin kullanılıp kullanılmayacağı derleyicileri yazanların + isteğine bırakılmıştır. Yani bazı derleyiciler örneğin Türkçe karakterlerden oluşan isimleri kabul ederken bazı derleyiciler + etmeyebilirler. + - Değişkenlerdeki karakter sayısı istenildiği kadar uzun olabilir. Ancak derleyiciler uzun değişken isimlerinin ilk n + karakterini dikkate alırlar. Bu n değeri derleyiciden derleyiciye değişebilmektedir. Bugün kullanılan yaygın derleyiciler + en az 256 karakteri desteklemektedir. Bu da zaten oldukça uzundur. + + C'de başı iki alt tire ile başlayan ve başı bir alt tire ve büyük harfle başlayan tüm isimler "reserved" yapılmıştır. + Programcıların bu biçimde isimlendirme yapmaması gerekir. Eğer programcılar böyle isimlendirme yaparsa kodları derlenir + ancak çalışma sırasında olumsuzluklar gözükebilir. (Bu duruma "tanımsız davranış (undefined behavior)" denilmektedir. + Bu kavram ileride açıklanacaktır.) Başı tek alt tire ile başlayan tüm isimler global faaliyet alanında "reserved" + yapılmıştır. (Yani örneğin biz _count gibi bir ismi global değişken olarak kullanamayız. Ancak yerek değişken olarak + kullanabiliriz. Global ve yerel faaliyet alanları konusu ileride ele alınacaktır.) + + C'de ağırlıklı bir biçimde küçük harfli isimlendirmeler tercih edilmektedir. Değişken isimlerinin anlamlı ve telefaffuz + edilebilir olması tavsiye edilir. Birden çok sözcükten oluşan değişken isimlerinde sözcüklerin ayrımsanması için üç + harflendirme biçimi (casting) kullanılmaktadır: + + 1) Klasik C Tarzı Harflendirme: Buna "yılan notasyonu (snake casting)" de denilmektedir. Burada sözcüklerin arasında + alt tire bulundurulur. Örneğin: + + number_of_students + total_count + weight + + 2) Deve Notasyonu (Camel Casting): Burada ilk sözcüğün tamamı küçük harflerle yazılır. Ancak sonrakşi sözcüklerin + yalnızca ilk harfleri büyük yazılır. Örneğin: + + numberOfStudents + totalCount + weight + + 3) Pascal Notasyonu (Pascal Casting): Burada da her sözcüğün ilk harfi büyük yazılır. Örneğin: + + NumberOfStudents + CreateWindow + SetWindowText + Sample + + Biz kursumuzda ağırlıklı olarak klasik C tarzı yazımı (yılan notasyonu) kullanacağız. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir bildirim işlemiyle eğer derleyici bildirilen değişken için bellekte yer ayırıyorsa o bildirime aynı zamanda + "tanımlama (definition)" da denilmektedir. Örneğin: + + int a; + + Bu bir bildirimdir. Ama aynı zamanda tanımlamadır. Çünkü derleyici bu bildirimde bildirilen a değişkeni için aynı + zamanda bellekte yer de ayırmaktadır. Her tanımalama bir bildirimdir ancak her bildirim bir tanımlama değildir. Tabii + bildirim olup da tanımlama olmayan durumlar seyrektir. Örneğin: + + int a; /* bu hem bir bildirimdir hem de bir tanımlamadır */ + extern int b; /* bu bir bildirimdir ama tanımlama değildir, tabii extern gibi bir konu henüz görülmedi */ +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir değişkene bildirim sırasında değer atayabiliriz. Bu işleme "ilkdeğer verme (initializtion)" denilmektedir. Örneğin: + + int a = 10, b, c = 20; + + Burada a ve c değişkenlerine ilkdeğer verilmiştir. Ancak b değişkenine ilkdeğer verilmemiştir. İlkdeğerverme ile + değişkene ilk kez değer atama aynı şey değildir. Örneğin: + + int a; + + a = 10; + + Buradaki atama bir ilkdeğer verme değildir. İlkdeğer verme bildirim sırasında değer atama anlamına gelmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Aslında C standartlarında "ekran" ve "klavye" kavramları hiç geçmemektedir. Örneğin C standartları printf fonksiyonun + "ekrana yazdığını" söylememektedir. Standartlara göre printf fonksiyonu "stdout" denilen bir dosyaya yazmaktadır. Ancak + bu dosya bir aygıta yönlendirilmiş olabilir. Örneğin klasik bilgisayar sistemlerimizde stdout ekranı kontrol eden terminal + aygıt sürücüsüne yönlendirilmiş durumdadır. Dolayısıyla printf stdout dosyasına yazar ancak yazılanlar ekranda görülür. + Tabii bir sistemde stdout başka aygıtlara ya da dosyalara yönlendirilmiş olabilir. Örneğin stdout seri porta yönlendirilmişse + artık printf fonksiyonu ile yazdırılanlar seri porta aktarılır. Aynı durum klavye için de geçerlidir. Aslında klavyeden + okumak diye de bir kavram yoktur. stdin dosyasından okumak diye bir kavram vardır. stdin dosyası da klasik bilgisayar + sistemlerinde genellikle klavye ile ilişkilendirilmiştir. Ancak örneğin stdin seri porta yönlendirilmişse artık seri + porttan gelen bilgiler okunur. Görüldüğü gibi "stdout" ve "stdin" aslında değişik donanımsal birimleri temsil ediyor + olabilir. Biz kurusumuzda bazen "ekran" ve "klavye" laflarını edeceğiz. Burada tenik olarak "stdout" ve "stdin" + dosyaları anlaşılmalıdır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + printf aslında oldukça kapsamlı bir fonksiyondur. printf fonksiyonunda iki tırnak içerisindeki karakterler ekrana + (yani stdout dosyasına) basılır. Ancak printf % karakterini gördüğünde % karakterini ve onun yanındaki bir ya da + iki karakteri ekrana yazdırmaz. % karakterinin yanındaki bazı özel karakterlere format karakterleri denilmektedir. + % karakteri ve format karakterleri "yer tutucu" belirtir. Bu yer tutucular printf'in iki tırnak argümanından sonraki + argümanlarla sırasıyla eşleştirilmektedir. Böylece aslında format karakterleri değil de bu argümanların değerleri + yer tutucu yerine yazdırlır. Örneğin: + + int a = 10, b = 20; + + printf("a = %d, b = %d\n", a, b); + + Burada %d yer tutucudur. İlk %d yerine a'nın değeri, ikinci %d yerine b'nin değeri yazdırılır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 12. Ders 30-06-2022 +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Format karakterleri eşleşen argümanın türünü ve yazdırma için kullanılan tabanı belirtecek biçimde farklılık göstermektedir. + En çok kullanılan format karakterleri şunlardır: + + %d ---> signed char, short ve int türlerini 10'luk sistemde yazdırmak için + %x ---> işaretli ve işaretsiz char, short ve int türlerini 16'lık sistemde yazdırmak için + %o ---> işaretli ve işaretsiz char, short ve int türlerini 8'lik sistemde yazdırmak için + %ld ---> long türünü 10'luk sistemde yazdırmak için + %lld ---> long long türünü 10'luk sistemde yazdırmak için + %lx ---> long türünü 16'lık sistemde yazdırmak için + %llx ---> long long türünü 16'lık sistemde yazdırmak için + %lo ---> long türünü 8'lik sistemde yazdırmak için + %llo ---> long long türünü 8'lik sistemde yazdırmak için + %u ---> unsigned char, unsigned short ve unsigned int türlerini 10'luk sistemde yazdırmak için + %lu ---> uunsigned long türünü 10'luk sistemde yazdırmak için + %f ---> float ve double türlerini 10'luk sistemde yazdırmak için (default durumda noktadan sonra 6 basamak + yuvarlanarak yazdırılır) + %e ---> float ve double türlerini üstel niçimde yazdırmak için. + %c ---> char, short ve int türlerini karakter görüntüsü olarak yazdırmak için +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a = 97; + double b = 12.45; + unsigned int c = 32503456; + int d = 100; + double e = 12000.345678; + + printf("%d - %c\n", a, a); /* 97 - a */ + printf("%f\n", b); /* 12.450000 */ + printf("%u\n", c); /* 32503456 */ + printf("%x\n", d); /* 64 */ + printf("%e\n", e); /* 1.200035e+04 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + printf fonksiyonunda % karakterinden sonra fakat format karakterinden önce bir sayı belirtilirse ilgili argüman o + sayı ile belirtilen genişlikte bir alan ayrılarak o alanda yazılır. Default durum sağa dayalı olarak yazdırılmasıdır. + Sola dayalı yazdırmak için bu genişlik belirten sayının önüne ayrıca bir de '-' karakteri eklenir. Özellikle sütunsal + hizalamalar için "%-nd" gibi (burada n yerine bir sayı geitirilmelidir) format karakterleri kullanılmaktadır. Eğer g + enişlik belirten sayı yazdırılacak sayının basamak sayısından az ise sayının hepsi yazdırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +main(void) +{ + int a = 10; + int b = 7; + int c = 121; + int d = 1234567; + + printf("%-20d%f\n", a, sqrt(a)); + printf("%-20d%f\n", b, sqrt(b)); + printf("%-20d%f\n", c, sqrt(c)); + printf("%-20d%f\n", d, sqrt(d)); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + float ve double sayılarda sayının toplam genişliği ve noktadan sonraki kısmın genişliği ayrı ayrı belirtilebilmektedir. + Örneğin "%10.2f" toplam 10 alan içerisinde sayı noktadan sonra iki basamak olacak biçimde yazdırılır. Burada yalnızca + noktanın sağ tarafının kaç basamak yazdırılacağı da belirtilebilir. Örneğin "%.3f" sayının tam kosmının tam olarak + yazılacağı ancak noktadan sonraki kısmın üç basamak biçiminde yuvarlanarak yazdırılacağı anlamına gelir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +main(void) +{ + double a = 12.346; + + printf("___%10.2f___\n", a); /*___ 12.35___*/ + printf("___%-10.2f___\n", a); /*___12.35 ___*/ + printf("___%.10f___\n", a); /*___12.3460000000___*/ + printf("___%.0f___\n", a); /*___12___*/ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bildirimler üç yerde yapılabilir: + + 1) Fonksiyonların içerisinde. Fonskiyonların içerisinde bildirilen değişkenlere "yerel değişkenler (local variables)" + denilmektedir. + 2) Fonksiyonların dışında. Fonksiyonların dışında bildirilen değişkenlere "global değişkenler (globaş variables)" + denilmektedir. + 3) Fonksiyonların parametre parantezleri içerisinde. Fonksiyonların parametre parantezleri içerisinde bildirilen + dğeişkenlere "parametre değişkenleri (parameters)" denilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int x; /* global deişken */ + +void foo(int n) /* Parametre değişkeni */ +{ + int a; /* yerel değişken */ + /* ... */ +} + +int y; /* global değişken */ + +int main(void) +{ + int b; /* yerel değişken */ + /* ... */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de küme parantezleri arasındaki bölgeye "block (block)" denilmektedir. Bir fonksionun ana bir bloğu olma zorundadır. + Ancak o ana bloğun içeriside istenildiği kadar çok iç içe ya da ayrık blok bulundurulabilir. Örneğin: + + void foo(void) + { + ... + { + ... + } + { + ... + { + ... + } + } + .... + } + + C90'da yerel değişkenler blokların başında bildirilmek zorundaydı. Burada blokların başı demekle blokların ilk işlemi + olacak biçimde bildirim yapma kastedilmektedir. Ancak bu kural C99 ve ötesinde değiştirilmiştir. C99 ve ötesinde yerel + değişkenler blokların herhangi bir yerinde bildirilebilirler. Örneğin: + + int main() + { + printf("this is a test\n"); + int a; /* C90'da geçersiz! C99 ve ötesinde geçerli */ + + { + int b; /* C90'da da geçerli */ + printf("this is a test\n"); + } + + int c; /* C90'da geçersiz! C99 ve ötesinde geçerli */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bir tamsayı 10'luk, 16'lık ve 8'lik sistemde bir sabit biçiminde belirtilebilmektedir. Default sistem 10'luk + sistemdir. Ancak bir tamsayı 0x ile ya da 0X ile başlanarak yazılırsa bu durumda sayının 16'lık sistemde yazılmış + olduğu kabul edilir. Eğer bir sayı başına 0 getirilerek yazılırsa bu da sayının 8'lik sistemde yazılmış olduğu anlamına + gelir. Örneğin: + + 100 (onluk sistemde 100) + 0x64 (16'lık sistemdeki 64 yani 10'luk sistemde 100) + 012 (8'lik sistemde 12 yani 10'luk sistemde 10) + + Tabii biz tamsayı değeri kaçlık sistemde yazarsak yazalım aslında bellekte her zaman bu sayı ikilik sistemde tutulmaktadır. + + C'de bir tamsayıyı ikilik sistemde yazmanın bir yolu yoktur. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +main(void) +{ + int a; + + a = 100; + printf("%d\n", a); /* 100 */ + + a = 0x64; + printf("%d\n", a); /* 100 */ + + a = 012; + printf("%d\n", a); /* 10 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bir noktalı sayı üstel biçimde de yazılabilir. Bunun için sayıdan sonra 'e' ya da 'E' karakteri ve üs sayısı + belirtilir. Buradaki üs 10'un kaçıncı kuvveti olduğunu belirtmektedir. Örneğin: + + a = 1.23e20; + b = 1.23E-12 +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +main(void) +{ + double a; + + a = 123.456e5; + printf("%f\n", a); /* 12345600.000000 */ + + a = 1.23e-5; + printf("%f\n", a); /* 0.000012 */ + + a = 1e20; + printf("%f\n", a); /* 100000000000000000000.000000 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bu bölümde C'de temel türlere ilişkin sabitlerin nasıl iafde edileceği üzerinde duracağız. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Doğrudan yazılan sayılara "sabit (literal)" denilmektedir. C'de yalnızca değişkenlerin değil sabitlerin de türleri + vardır. Bir sabitin türü onun nasıl yazıldığına ve niceliğine bakılarak belirlenmektedir. Bir C programcısının da + bir sabiti gördüğünde onun türünü tespit edebilmesi gerekir. + + 1) Sayı nokta içermiyorsa ve sonunda bir ek de yoksa eğer sayı 10'luk sistemde yazılmışsa sırasıyla sayı "int", "long" + ve ""long long" türlerinin hangisinin içerisinde ilk kez kalıyorsa sabit o türdendir. Örneğin: + + 0 int türden sabit + 123 int türden sabit + -123 Bu bir sabit değildir. Burada sabit olan 123'tür. Sayının başındaki '-' bir operatördür. + + Şimdi çalıştığımız sistemde int ve long türünün 4 byte ancak long long türünün 8 byte olduğunu varsayalım. Bu durumda: + + 3000000000 long long türden sabit + 10000000000000 long long türden sabit + + 2) Sayı nokta içermiyorsa sonunda da ek yoksa ancak 16'lık sistemde ya da 8'lik sistemde yazılmışsa sayı sırasıyla int, + unsigned int, long, unisgned long, long long ve unsigned long long sınırlarının hangisinin içerisinde ilk kez kalıyorsa + sabit o türdendir. Çalıştığımız sistemde int ve long türünün 4 byte ancak long long türünün 8 byte olduğunu varsayalım. + + 0x10 int türden sabit + 0xFC123478 unsigned int türden sabit + 0x321223123123; long long türden sabit + + 3) Sayı nokta içermiyorsa ancak sayının sonunda ona yapışık bir biçimde 'u' ya da 'U' eki varsa sayı 10'luk, 16'lık, + 8'lik sistemde yazıldığında sabit sırasıyla unsigned int, unsigned long int ve unsigned long long int türlerinin + hangisinin sınırları içeirsinde ilk kez kalıyorsa sabit o türdendir. Örneğin: + + 123U unsigned int türünden sabit + 0u unsigned int türdne sabit + 30000000000000U unsigned long long türünden sabit + 0x1234u unsigned int türünden sabit + 01234U unsigned int türünden sabit + + 4) Sayı nokta içermiyorsa ve sayının sonunda onunla yapışık bir biçimde 'l' ya da 'L' harfi varsa, sayı 10'luk sistemde + yazılmışsa sabit long ve long long türlerinin hangisinin sınırları içerisinde ilk kez kalıyorsa o türdendir. Örneğin: + + 1L long türden bir sabit + 1234567890123L long long türden sabit + + 5) Sayı nokta içermiyorsa ve sayının sonunda onunla yapışık bir biçimde 'l' ya da 'L' harfi varsa sayı 16'lık ya da + 8'lik sistemde yazılmışsa sabit long, unsigned long, long long ve unsigned long long türlerinin hangisinin sınırları + içerisinde ilk kez kalıyorsa sabit o türdendir. Örneğin: + + 0x12L long türden sabit + 0123L long türden sabit + + 6) Sayı nokta içermiyorsa ve sayının sonunda onunla yapışık "ul ya da lu" varsa ('u' ya da 'l' ler büyük ya da küçük + olabilir) sayı 10'luk sistemde, 16'lık sistemde ya da 8'lik sistemde yazıldığında sabit sırasıyla unsigned long ve + unsigned long long sınırlarının hangisinin içerisinde ilk kez kalıyorsa o türdendir. + Örneğin: + + 12LU unsigned long int türden sabir + 1234567890123ul unsigned long long türden sabit + + 7) Sayı nokta içermiyorsa ve sayının sonunda "ll" ya da "LL" soneki varsa sayı 10'luk sistemde yazıldığında long long + türden sabit belirtir. Örneğin: + + 1LL long long türden sabit + 100ll long long türden sabit + + 8) Sayı nokta içermiyorsa ve sayının sonunda "ll" ya da "LL" soneki varsa ve sayı 16'lık ya da 8'lik sistemde yazılmışsa + long long ve unsigned long long türlerinin hangisinin sınırları içerisinde ilk kez kalıyorsa sabit o türdendir. Örneğin: + + 0x12LL long long türden sabit + + 9) Sayı nokta içermiyorsa ve sayının sonunda "ull" ya da "llu" "soneki varsa (burada 'u' ve "ll" büyk harf ya da jüçük + harf olablir) bu durumda sabit unsigned long long türündendir. Örneğin: + + 1uLL unsigned long long türdne sabit + + 10) Sayı nokta içeriyorsa ve sayının sonunda bir ek yoksa sabit double türdendir. Örneğin: + + 1.2 double türden sabit + 0.2 double türden sabit + + Noktanın solunda bir şey yoksa ve noktanın sağında bir şey yoksa orada 0 olduğu kabul edilmektedir. Bu Fortran zamanından + beri kullanılan bir gelenektir. Örneğin. + + .12 double türden sabit, 0.12 ile aynı anlamda + 12. double türden sabit, 12.0 ile aynı anlamda + + Sayı üstel biçimde yazılmışsa sayı nokta içermese bile double türden olur. Örneğin: + + 1e3 Bu sayı 1000 anlamına geliyor olsa da üstel biçimde yazıldığı için double türden sabit belirtmektedir. + + 11) Sayı nokta içeriyorsa ve sayının sonunda 'f' ya da 'F' soneki varsa sabit float türdendir. Örneğin: + + 12.3f float türden sabit + .1F float türden sabit + 12.F float türden sabit + + Sayı nokta içermiyorsa sayının sonuna 'f' ya da 'F' soneki getirilemez. Örneğin: + + 12F geçersiz sabit! + 1e3F geçerli, burada noktaya gerek yok, çünkü sayı üstel biçimde yazılmış + + 12) Sayı nokta içeriyorsa ancak sayının sonunda 'l' ya da 'L' varsa sabit long double türden olur. Örneğin: + + 12L long türden sabit + 12.3 double türden + 12.3L long double türden +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 13. Ders 05/07/2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 13) C'de tek tırnakla iki tırnak arasında çok fark vardır. (Halbuki bazı dillerde tek tırnak ile iki tırnak arasında + farklılık yoktur.) C'de bir karakter tek tırnak içerisine alınırsa bu ilgili karakterin karakter tablosundaki sıra + numarasını (code point) belirten bir sayı anlamına gelir. Örneğin C'de 'a' ifadesi aslında eğer ASCII karakter tablosu + kullanılıyorsa 97 sabiti ile aynı anlamdadır. + + C'de bir karakter tek tırnak içerisine alınırsa bu ifade int türden sabit kabul edilir. Bu biçimdeki ifadelere "int + türden karakter sabitleri (integer character constants) de denilmektedir". Örneğin: + + #include + + int main(void) + { + int ch; + + ch = 'a'; + + printf("ch = %d, ch = %c\n", ch, ch); /* ch = 97, ch = a */ + + return 0; + } + + Ancak karakter tablolarındaki bazı karakterlerin görüntü karşılığı yoktur. Yani bu karakterleri ekrana yazdırmak + istediğimizde bir şey görmeyiz. Ancak bazı eylemler gerçekleşir. Bu tür karakterlere "görüntülenemeyen karakterler + (non-printable characters)" de denilmektedir. İşte bu görüntülenemeyen bazı karakterlere ilişkin karakter sabitleri + özel bir biçimde ifade edilmektedir. ASCII karakter tablosunun (dolayısıyla Unicode karakter tablosunun da) ilk 32 + karakteri görüntülenemeyen özel kontrol karakterinden oluşmaktadır. İşte çok kullanılan bazı görüntülenemeyen karakterler + tek tırnak içerisinde "önce bir ters bölü sonra özel bazı karakterler ile" temsil edilmektedir. Bu karakter sabitlerine + "ters bölü karakter sabitleri (escape sequnces)" denilmektedir. Bunların listesi şöyledir: + + '\a' alert (7 numaralı ASCII karakteri), beep sesi çıkar + '\b' back space (8 numaralı ASCII karakteri), sanki back space tuşuna basılmış etkisi oluşur + '\f' form feed (12 numaralı SCII karakterş), bir sayfa atar + '\n' line feed (10 numaralı ASCII karakteri), imleç aşağı satırın başına geçer + '\r' carriage return (13 numaralaı ASCII karakteri), imleç bulunduğu satırın başına geçer) + '\t' tab (9 numaralı ASCII karakteri), imle. bir tab ileri gider + '\v' vertical tab (11 numaralı ASCII karakteri), imleç düşey olarak kaydırılır. + + Burada önemli olan nokta '\n' gibi bir karakter sabitinin her ne kadar tırnak içerisinde iki karakter bulunuyorsa da + aslında tek bir karaktere ilişkin karakter sabiti belirttiğidir. Yani '\n' karakter sabiti ne ters bölü karakterinin + ne de 'n' karakterinin karakter sabitidir. Tamamen başka bir karakter olan "LF (line feed)" denilen karakterin karakter + sabitidir. + + Ters bölü karakter sabitleri iki tırnak içerisinde tek bir karakter olarak ele alınmaktadır. Örneğin: + + #include + + int main(void) + { + printf("ali\aveli\nselami\tfatma\nsacit\n"); + + return 0; + } + + Ters bölü karakterinin kendisine ilişkin karakter sabiti '\' biçiminde yazılamaz. Eğer biz böyle bir şey yazarsak + derleyici bunu "sanki ters bölü karakter sabitlerinden birisini yazmak istiyormuşuz da onu yazamamışız gibi" değerlendirir. + Ters bölü karakterinin kendisine ilişkin karakter sabitini '\\' biçiminde yazabiliriz. Örneğin: + + #include + + int main(void) + { + char ch; + + ch = '\\'; + printf("%c\n", ch); + + return 0; + } + + Benzer biçimde iki tırnak içerisinde de ters bölü karakterinin kendisini yazdırmak istiyorsak iki ters bölü karakteri + kullanmalıyız. Örneğin: + + #include + + int main(void) + { + printf("c:\temp\a.dat\n"); /* yanlış yazım */ + printf("c:\\temp\\a.dat\n"); /* doğru yazım */ + + return 0; + } + + Tek tırnak karakterine ilişkin karakter sabiti ''' biçiminde yazılamaz. Bu durumda derleyici durumu "sanki tek tırnağın + içerisine bir şey yazılmamış gibi" değerlendirecektir. Tek tırnak karakterinin karakter sabiti '\'' biçiminde yazılmalıdır. + Örneğin: + + #include + + int main(void) + { + char ch; + + ch = '\''; + printf("%c\n", ch); + + return 0; + } + + İki tırnağın içerisinde tek tırnak karakterini ters bölüsüz de yazabilriz. Yani iki tırnak içerisindeki tek tırnak + karakterleri bir soruna yol açmamaktadır. Örneğin: + + printf("Izmir'in merkezi\n"); /* geçerli */ + + Tabii istersek yine de bu tek tırnağı ters bölü karakteri biçiminde de yazabilirdik: + + printf("Izmir\'in merkezi\n"); /* geçerli, yukarıdaki ile aynı */ + + Benzer biçimde iki tırnak içerisinde iki tırnak karakteri de doğrudan yazılamaz. Örneğin: + + printf(""Ankara""); /* geçersiz! */ + + İki tırnak içerisinde iki tırnak karakteri \" biçiminde belirtilmelidir. Örneğin: + + printf("\Ankara\""); /* geçerli "Ankara" yazısı çıkacak. + + Tebii tek tırnak içerisinde iki tırnak karakteri de sorunsuz olarak kullanılabilir. Örneğin: + + ch = '"'; /* geçerli, sorun yok */ + + Ancak sorun yaratmıyor olsa da biz istersek tek tırnak içerisinde iki tırnak karakterini yine \" biçiminde de yazabiliriz. + Örneğin: + + ch = '\"'; + + Aslında C'de tek tırnak içerisine tek bir karakterin yerleştirilmesi zorunlu değildir. Tek tırnak içerisine int türünün + byte uzunluğu kadar karakter yerleştirilebilir (örneğin int türü 4 byte ise 4 karakter, 8 byte ise 8 karakter yerleştirilebilir). + Tek tırnak içerisine birden fazla karakter yerleştirildiğinde bunlara "multibyte karakterler" denilmektedir. Multibyte + karakterlerin ne belirttiği derleyicileri yazanların isteğine bırakılmıştır. Biz bu multibyte karakter kavramını ileride + yeniden ele alacağız. + + Bir karakter sabitinin başına onunla yapışık bir L harfi (L harfi büyük harf olmak zorundadır) getirilebilir. Bu tür + karakter sabitlerine "geniş karakter sabitleri (wide character constants)" denilmektedir. Örneğin: + + L'a' + + Geniş sabitleri wchar_t türündendir. Bu konu ileride ele alınacaktır. + + C11 ile birlikte karakter sabitlerinin önüne yine onunla yapışık 'u' ve 'U' getirilebilmektedir. Örneğin: + + u'a' + U'b' + + 'u' öneki getirilmiş karakter sabitleri Unicode UTF-16 encoding'ini, 'U' öneki getirilmiş karakter sabitleri de Unicode + UTF-32 encoding'ini belirtir. Bunlar sırasıyla char16_t ve char32_t türündendir. (char16_t ve char32_t türleri + içerisinde typedef edilmiş türlerdir.) Bu konu da ileride ele alınacaktır. Aslında C23'e kadar C standartlarında 'u' ve + 'U' önekli karakter sabitlerinin açıkça Unicode UTF-16 ve Unicode UTF-32 encoding'ine ilişkin olduğu belirtilmemiştir. + Bu türden karakter sabitlerinin hangi encoding'e göre code point belirttiği derleyiciyi yazanların isteğine bırakılmıştı. + Ancak C23'te artık açıkça bu sabitlerin Unicode UTF-16 ve Unicode UTF-32 türünden sabit belirttiği ifade edilmiştir. + + Bugün Microsoft'un C derleyicileri ve gcc ve clang derleyicileri geniş karakter sabitlerini Unicode UTF-16 ya da Unicode + UTF-32 olarak ele almaktadır. Dolayısıyla geniş karakter sabitlerinin hangi karakter tablosu ve hangi encoding'e + ilişkin olduğu derleyiciden derleyiciye değişebilmektedir. Halbuki artık C23 ile birlikte 'u' ve 'U' öneki sayesinde + taşınabilir bir biçimde Unicode UTF-16 ve Unicode UTF-32 karakter sabitleri oluşturulabilmektedir. + + 14) C'de int türden küçük türlerin sabitleri yoktur. Yani C'de char, signed char, unsigned char, short ve unsigned + short türünden sabitler yoktur. En küçük sabit int türündendir. Tek tırnak içerisine yazılmış karakter sabitlerinin + de aslında int türden olduğunu anımsayınız. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir C programında Türkçe karakterlerin düzgün görünmemesinin birkaç nedeni olabilir. C standartlarına göre derleyici + için iki karakter kümesi söz konusudur: "Kaynak karakter kümesi (source character set)" ve "çalıştırma karakter kümesi + (execution character set)". Kaynak karakter kümesi derleyicinin kaynak dosyadaki karakterleri anlamlandırdığı karakter + kümesidir. Örneğin derleyicimizin kaynak karakter kümesi "Unicode UTF-8" ise bu durumda derleyici kaynak dosyanın + "Unicode UTF-8" olarak kodlanmış bir dosya olduğunu varsayacaktır. Türkçe için çeşitli bir byte'lık karakter kodlamaları + da vardır. Örneğin Micsosoft 1254, ISO 8859-9, OEM 857 gibi. Çalıştırma karakter kümesi ise derleyicinin amaç kodu + oluştururken anaç kod için kullandığı karakter kümesidir. Derleyicilerde genellikle default durumda bu iki karakter + kümesi aynıdır. Örneğin: + + int ch; + + ch = 'ş'; + + Buradaki program parçasını biz Unicode UTF-8 olarak save etmişsek buradaki 'ş' karakteri iki byte ile kodlanacaktır. + Örneğin eğer derleyicimizin kaynak karakter kümesi Microsoft 1254 ise derleyici bu iki byte'ıuü sanki tek tırnak + içerisinde birden fazla karakter yazmışız gibi yani "multibyte" olarak ele alacaktır. Bu da Türkçe 'ş' karakterini + derleyicinin tanıyamaması anlamına gelecektir. Halbuki derleyicimizin kaynak karakter kümesi de "Unicode UTF-8" + olsaydı derleyicimiz dosyadaki bu iki byte'ın aslında Türkçe 'ş' karakteri olduğunu anlayacaktı. Buradan şu sonucu + çıkartabiliriz: Eğer biz Türkçe karakterleri derleyicinin tanımasını istiyorsak editörümüzün karakter kodlaması ile + derleyicimizin kaynak karakter kodlamasını aynı yapmalıyız. Pekiyi derleyicimizin Türkçe 'ş' harfini düzgün anladığinı + varsayalım. Bu durumda derleyicimiz yukarıdaki örnekte ch değişkenin içerisine hangi byte'ları yerleştirecektir? + İşte bu da "çalıştırma karakter kümesi" ile ilgilidir. Eğer derleyicimizin çalıştırma karakter kümesi de "Unicode + UTF-8" ise bu durumda derleyicimiz ch değişkenin içerisine 'ş' karakteri için yine aynı byte'ları kodlayacaktır. Durumun + böyle olduğunu varsayalım. Şimdi çalılan kodda ch değişkenin içerisinde iki byte'lık Unicode UTF-8 Türkçe 'ş' karakteri + vardır. İşte biz bu karakteri stdout dosyasına yazdırdığımızda ekranda yine de 'ş' karakterini göremeyebiliriz. Çünkü + terminal aygıt sürücüsünün de beklediği bir karakter kodlaması vardır. Terminal aygıt sürücüsü eğer bizden örneğin + Windows 1254 karakter kodlaması bekliyorsa bu durumda bizim gönderdiğimiz iki byte'ı terminal aygıt sürücüsü + iki ayrı karakter olarak yorumlayacaktır. Biz de muhtemelen 'ş' yerine anlamsız iki karakter göreceğiz. Bu durumda + terminal aygıt sürücüsünün karakter kodlamasının da bizimkiyle uyumlu olması gerekmektedir. + + Linux ortamında genellikle her şeyin default karakter kodlaması "Unicode UTF-8"dir. Eğer biz Linux'ta "Unicode + UTF-8" dosyasında Türkçe karakterler kullanırsak tüm yukarıdaki öğeler aynı ayarda olduğu için sorun çıkmayacaktır. + + Windows'ta Visual Studio IDE'sinde ayarlamalar biraz daha karmaşıktır. Eğer bilgisayarımızın bölgesel ayarları Türkçe + ise Microsoft buradaki editörün default karakter kodlamasını, derleyicinin kaynak ve çalıştırma karakter kümesini + "ASCII 1254 Code Page" olarak ayarlamaktadır. Eğer terminal aygıt sürücüsü de aynı ayardaysa bir sorun oluşmayacaktır. + Ancak eğer terminal aygıt sürücüsü "Unicode UTF-8" olarak ayarlanmışsa bu durumda bu uyumsuzluğu gidermek gerekir. + Şimdi terminal aygıt sürücüsünün kodlamasının UTF-8 olduğunu varsayalım. Bu durumda bizim IDE tarafında uyumu + koruyabilmemiz için kaynak dosyanın "Unicode UTF8" olarak kodlanması, derleyicinin kaynak ve çalıştırma karakter + kodlamalarının da "Unicode UTF-8" olarak ayarlanması gerekir. Microsoft C derleyicilerinde kaynak ve çalıştırma karakter + kodlamaları "/source-charset:" ve "/execution-charset:" komut satırı seçenekleri + ile değiştirilebilmektedir. Aynı ayar "gcc" ve "clang" derleyicilerinde de "-finput-charset=" ve + "-fexec-charset=" komut satırı argümanlarıyla yapılabilmektedir. Tabii bu komut satırı argümanlarına + istediği +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de karakter sabitleri sayısal işlemlere sokulabilir. Çünkü zaten onlar birer sayı belirtmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int result; + + result = 'a' + 1; + printf("%c, %d\n", result, result); /* b, 98 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + putchar fonksiyonu bizden int türden bir değer alır. O değere karşı gelen karakter numarasına ilişkin karakterin + görüntüsünü ekrana (stdout dosyasına) yazar. Yani putchar(ch) çağrısıyla printf("%c", ch) çağrısı işlevsel olarak + tamamen eşdeğerdir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + a = 48; + + putchar(a); /* 0 */ + putchar('\n'); + putchar('?'); /* ? */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + getchar fonksiyonu adeta putchar fonksiyonun tersini yapmaktadır. Bu fonksiyonun parametresi yoktur. Fonksiyon + klavyeden (stdin dosyasından) bir karaktere basılıp ENTER tuşuna basılmasını bekler. getchar bu karakterin karakter + tablosundaki sıra numarasına geri döner. getchar bize int türden bir değer vermektedir. Örneğin: + + int ch; + + ch = getchar(); + + getchar fonksiyonunu yanlışlıkla aşağıdaki gibi kullanmaya çalışmayınız: + + getchar(ch); + + getchar fonksiyonun parametresi yoktur. Fonksiyonun geri dönüş değerini bir değişkene yerleştirmelisiniz: + + ch = getchar(); +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int ch; + + ch = getchar(); + putchar(ch); + putchar('\n'); + printf("ch %d, ch = %c\n", ch, ch); + + return 0; +} +/*---------------------------------------------------------------------------------------------------------------------- + Aslında klavyeden (stdin dosyasından) okumalar bir tapon eşliğinde yapılmaktadır. Biz getchar fonksiyonunda birden + fazla karakter girebiliriz. Bu durumda girilen tüm karakterler önce bir "tampona (buffer)" yerleştirilir sonra o + tampondan alınarak verilir. getchar için basılan ENTER tuşu da tampona '\n' karakteri olarak eklenmektedir. getchar + (ve stdin dosyasından okuma yapan diğer fonksiyonlar) eğer tamponda zaten karakter varsa bizden karakter istemezler. + stdin tamponunda karakter yoksa yeniden okuma talep ederler. Örneğin: + + int ch; + + ch = getchar(); + putchar(ch); + + ch = getchar(); + putchar(ch); + + Biz burada ilk getchar için 'a' karakterine basıp ENTER tuşuna basmış olalım. Bu durumda taponun içeriği şöyle olacaktır: + + Tampon => a\n + + İlk getchar tampondaki sıradaki karakter olan 'a' okuyacaktır. Ancak ikinci getchar tapon dolu olduğu için klavyeden + yeni bir giriş istemeyecektir. Tampondaki '\n' karakterini alıp geri dönecektir. Ancak bir tane daha getchar çağrısı + yaparsak artık o getchar tampon boş olduğu için klavyeden okuma isteyecektir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + printf fonksiyonunun klavyeden (stdin dosyasından) okuma yapan scanf isimli kardeşi vardır. scanf temel olarak printf gibi kullanılmaktadır. Ancak + scanf fonksiyonundaki format karakterleri çıktı ile ilgili değil yapılan giriş ile ilgili bilgi verir. Örneğin printf fonksiyonunda %d "int bir değeri 10'luk + sistemde ekrana yaz" anlamına gelirken scanf fonksiyonunda %d "int bir nesne için "10'luk sistemde giriş yap" anlamına gelmektedir. scanf fonksiyonunda + iki tırnaktan sonraki değişkenlerin önümne & operatörü getirilir. (Bu operatör ileride ele alınacaktır). Örneğin: + + int a; + + scanf("%d", &a); + + Burada klavyeden girilen sayı a nesnesinin içerisine yerleştirilir. scanf fonksiyonundaki iki tırnak içerisine format karakterlerindne başka bir şey + yazmayınız. Buraya yazdığınız başka karakterler başka anlamlara gelmektedir. scanf buradaki karakterleri ekrana yazdırmaz. Ekrana bir şey yazdırmak istiyorsanız + printf fonksiyonunu kullanmalısınız. Örneğin: + + int a; + + scanf("%x", &a); + + Burada %x klavyedne girilen değerin 16'lık sistemde girilmiş olduğunu varsayarak a nesnesine yerleştirecektir. printf fonksiyonuyla scanf fonksiyonu arasındaki + format karakterleri aynı biçimdedir. Ancak birkaç istisna vardır. printf fonksiyonunda hem float hem de double %f ile yazdırılır. Ancak scanf fonksiyonunda + float %f ile double %lf ile okunmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + double a; + + printf("Bir sayi giriniz:"); + scanf("%lf", &a); /* double %lf ile okunmalıdır */ + + printf("%f\n", a); /* printf fonksiyonunda %lf diye bir formak karakteri yoktur */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Tek bir scanf ile birden fazla nesne için okuma yapılabilir. Burada format karakterlerinin dışında şimdilik başka bir karakter bulundurmayınız. + Girişler sırasında istenildiği kadar boşluk karakteri (SPACE, TAB, ENTER) bulundurulabilir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + printf("Iki deger giriniz:"); + scanf("%d%d", &a, &b); + + printf("a = %d, b = %d\n", a, b); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- +Aşağıdaki programda klavyeden (stdin dosyasından) iki int değe rokunmuş bunların çarpımı ekrana (stdout dosyasına) yazdırılmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + printf("Iki deger giriniz:"); + scanf("%d%d", &a, &b); + + printf("%d\n", a * b); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Yeni öğrenen tarafından yanlışlıkla scanf'teki format karakterlerinin sonuna \n konulabilmektedir. Bu tamamen başka bir anlama gelir. Böyle yapmayınız. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int ain(void) +{ + int a, b; + + printf("Iki deger giriniz:"); + scanf("%d%d\n", &a, &b); /* dikkat! yanlışlıkla \n konulmuş! */ + + printf("%d\n", a * b); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Tabii scanf ile biz getchar gibi karakter de okuyabiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + char ch; + + scanf("%c", &ch); + putchar(ch); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + 14. Ders 07/07/2022 - Perşembe +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir işleme yol açan, işlem sonucunda bir değer üretilmesini sağlayan atomlara "operatör" denilmektedir. Bir operatörün işleme soktuğu ifadeleri ise + "opeand (operand)" denir. Örneğin a + b ifadesinde + bir operatördür. a ve b bu operatörün operand'larıdır. + + Operatör konusunu iyi anlayabilmek için operatörleri sınıflandırmak gerekir. Operatörler genel olarak üç biçimde sınıflandırılmaktadır: + + 1) İşlevlerine Göre + 2) Operand Sayılarına Göre + 3) Operatörün Konumuna Göre + + İşlevlerine göre sınıflandırmada operatörün hangi amaçla kullanıldığına göre sınıflandırma yapılır. Tipik sınıflandırma şöyle yapılmaktadır: + + 1) Artirmetik Operatorleer (Arithmetic Operators): Bunlar toplama, çarpma gibi klasik operatörlerdir. + 2) Karşılaştırma Operatörleri (Comparision Operators): Bunlar >, <, >=, <=, ==, != gibi iki değeri karşılaştırmak için kullanılan operatörlerdir. Bu + operatörlere "ilişkisel operatörler (relational operators)" da denilmektedir + 3) Mantıksal Operatörler (Logical Operators): Bunlar AND, OR, NOT işlemleri yapan operatörlerdir. + 4) Gösterici Operatörleri (Pointer Operators): Adreslerle işlemler yapan operatörlerdir. Bunlar her programlama dilinde bulunmazlar. + 5) Bit Operatörleri (Bitwise Operators): Bit operatörleri de pek çok dilde bulunmaktadır. Bunların sayıların karşılıklı bitlerini işleme sokan + operatörlerdir. + 6) Özel Amaçlı Operatörler (Special Purpose Operators): Değişik konulara ilişkin işlem yapan yukarıdaki gruplar içerisine girmeyen operatörlerdir. + + Operand sayılarına göre operatörler üç grubu ayrılmaktadır: + + 1) İki operand'lı Operatörler (Binary Operators): Bunlar iki operand alırlar. Yani bir şeyle bir şeyi işleme sokarlar. Örneğin '+', '*', '/', '-' + operatörleri iki operand'lı operatörlerdir. + 2) Tek operand'lı Operatörler (Unary Operators): Bunlar tek bir değeri işleme sokarlar. Örneğin NOT operatörü programlama dillerinde bir değerin NOT'ını + alır, iki değerin NOT'ını almaz. Ya da örneğin -5 ifadesindeki '-' operatörü çıkartma operatörü değildir. İşaret eksi operatördür ve tek operand'lı bir operatördür. + 3) Üç operand'lı operatörler (Ternary Operators): Üç operand'lı operatörler aslında çok seyrek bulunurlar. Örneğin C'de üç operand'lı tek bir operatör vardır. + + Operatörler operatörün operan'larına göre konumuna göre de üçe ayrılmaktadır: + + 1) Araek Operatörler (Infix Operators): Bu operatörler iki operand'lıdır ve operand'larının arasına getirilerek kullanılmaktadır. Örneğin a + b işleminde + '+' operatörlerinin araek bir operatör olduğuna dikkat ediniz. + 2) Önek Operatörler (Prefix Operators): Bunlar operand'larının önüne getirilerek kullanılırlar. Örneğin !a gibi bir kullanımda ! operatörü operand'ının önüne + getirilmiştir. + 3) Sonek Operatörler (Postfix Operators): Bunlar da operand'larının sonuna getirilerek kullanılırlar. Örneğin foo() gibi bir ifadede parantezler operatör + görevindedir. foo ise bu operatörün operandıdır. Burada operatör operand'ının sonuna getirilmiştir. + + Bir operatör ele alınırken önce yukarıdaki üç sınıflandırmada da operatörün nereye düştüğü ifade edilmelidir. Sonra operatöre ilişkin başka özellikler belirtilmelidir. + Örneğin, "/ operatörü iki operand'lı araek (binart infix) bir artimetik operatördür." Ya da örneğin "! operatörü tek operand'lı öncek (unary prefix) bir mantıksal operatördür". + Ya da örneğin "& operatörü iki operand'lı araek bir bit operatörüdür". +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir ifadede birden fazla operatör kullanıldığında bunlar birbirlerine göre belli bir sırada yapılırlar. Bu duruma "operatörler arasındaki öncelik + ilişkisi (operator precedency)" denilmektedir. Operatörlerin arasındaki öncelik ilişkisi "operatörlerin öncelik tablousu" denilen bir tabloyla + betimlenmektedir. Bu tablo satırlardan oluşur. Üst satırrdaki operatörler alt satırdaki operatörlerden daha önceliklidir. Aynı satırdaki operatörler + eşit öncelikli biçimde bulunurlar. Ancak aynı satırdaki operatörlerin önceliği "soldan sağa (left to right)" ya da "sağdan sola (right to left)" biçimde + olabilir. Soldan sağa öncelik demek o satırda bulunanlardan ifade içerisinde hangisi soldaysa o önce yapılır demektir. Sağdan sola öncelik de benzerdir. + Aşağıda operatmrlerin öncelik tablosunun iskelet hali verilmiştir: + + () Soldan-Sağa + * / Soldan Sağa + + - Soldan Sağa + = Sağdan Sola + + Buradaki () operatörü öncellik parantezini ve fonksiyon çağırma operatörünü anlatmaktadır. Örneğin: + + a = b - c * d; + + İ1: c * d + İ2: b - İ1 + İ3 a = İ2 + + Burada aslında b'dn c * d'nin çıkartıldığına dikkat ediniz. Örneğin: + + a = b / c * d + + Burada / ve * soldan-sağa eşit önceliklidir. İfade içerisinde (öncleik tablosunda değil) solda / olduğu için önce / sonra * yapılacaktır: + + İ1: b / c + İ2: İ1 * d + İ3: a = İ2 + + Örneğin: + + a = b + c + d; + + Burada solda olan '+' önce yapılacaktır: + + İ1: b + c + İ2: İ1 + d + İ3: a = İ2 + + Örneğin: + + a = b = c; + + Atama operatörünün sağdan-sola grupta olduğuna dikkat ediniz: + + İ1: b = c + İ2: a = İ1 + + Öncelik tablosundaki satırlarda bulunan operatörler o satırda değişik sırada yazılabilirler. Çünkü aynı satırdaki operatörlerin o satırdaki sırasının + bir önemi yoktur. "Soldan-sağa" ya da "sağdan-sola" ifade içerisindkei duruma ilişkindir. + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + *, /, + ve - operatörleri "iki operand'lı araek (binary infix)" aritmetik operatörlerdir. Bunlar klasik dört işlemi yaparlar. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + % operatörü iki operand'lı araek bir aritmetik operatördür. Bu operatör sol taraftaki operandın sağ taraftaki operanda bölümünden elde edilen kalan değerini + üretir. Bu operatörün her iki operandı da tamsayı türlerine ilişkin olmak zorundadır. Öncelik tablosunda * ve / ile soldan sağa eşit öncelik grupta bulunur. + Negarit sayının pozitif sayıya bölümünden elde edilen kalan nagtiftir. Pozitif sayının negatif sayıya bölümündne elde edilen kalan pozitiftir. + + () Soldan-Sağa + * / % Soldan Sağa + + - Soldan Sağa + = Sağdan Sola + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int result; + + result = 10 % 4; + printf("%d\n", result); /* 2 */ + + result = -10 % 4; + printf("%d\n", result); /* -2 */ + + result = 10 % -4; + printf("%d\n", result); /* 2 */ + + result = -10 % -4; + printf("%d\n", result); /* -2 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + + ve - sembolleri hem toplama ve çıkartma operatörü hem de işaret - ve işaret + operatörünü temsil etmektedir. İşaret + ve işaret - operatörleri + "tek operand'lı öncek (unary prefix)" operatörlerdir. İşaret - operatörü operand'ının negatif değerini üretir. İşaret + operatörü ise operand'ı ile aynı + değeri üretmektedir. (Yani aslında işaert + operatörü bir şey yapmamaktadır). Bu iki operatör öncelik tablosunun ikinci düzeyinde sağdan-sola gruğta bulunurlar: + + () Soldan-Sağa + + - Sağdan-Sola + * / % Soldan Sağa + + - Soldan Sağa + = Sağdan Sola + + Örneğin: + + a = b - - - c; + + İ1: -c + İ2: -İ1 + İ3: b - İ2 + İ4: a = İ3 + + Burada işl - sembolün "çıkartma" diğerlerinin "işaret -" olduğuna dikkat ediniz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int result; + int a = -4; + + result = 10 - - - - -a; + printf("%d\n", result); /* 14 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de programın atomlarına ayrılma aşamasında yan yana en uzun karakter topluluğundan atom yapılmaya çalışılır. C'de sonraki konuda göreceğiniz gibi ++ ve + -- operatmrleri de vardır. Dolayısıyla ++ ve -- yan yana yazılırsa iki ayrı işaret + ve işaret - operatörü değil ++ ve -- operatörleri anlaşılır + Benzer biçimde a>=3 gibi bir ifadede a, >= ve 3 biçiminde üç farklı atom vardır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + ++ ve -- operatörleri "tek operand'lı, öncek ve sonek olarak kullanılabilen" operatörlerdir. Yani biz bu operatörleri ++a gibi de a++ gibi de kullanabiliriz. + Bu operatörlerin önek ve sonek kullanımlarında semantik farklılık vardır. ++ operatörüne "artırma (increment)", -- operatörüne "eksiltme (decrement)" operatörleri + denilmektedir. ++ operatörü "operandı içerisindeki değeri 1 artır, -- operatörü operandı içerisindeki değeri 1 eksilt anlamına gelir." +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + a = 3; + + ++a; /* a = a + 1 */; + printf("%d\n", a); /* 4 */ + + a++; + printf("%d\n", a); /* 5 */ + + a = 3; + + --a; /* a = a - 1 */ + printf("%d\n", a); /* 2 */ + + a--; + printf("%d\n", a); /* 1 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + ++ ve -- operatörleri öncelik tablosunun ikinci düzeyinde sağda-sola grupta bulunmaktadır: + + () Solda-Sağa + + - ++ -- Sağdan-Sola + * / % Soldan Sağa + + - Soldan Sağa + = Sağdan Sola + + Aslında C'nin tek operand'lı (unary) bütün operatörleri zaten öncelik tablosunun ikinci düzeyinde sağdan-sola gruba yerleştirilmiştir. + + ++ ve -- operatörleri her zaman tablodaki öncelikte yapılır. Ancak sonraki işleme eğer operatörler önek olarka kullanılmışsa artırılmış ya da eksiltilmiş değer, + sonek olarak kullanılmışsa artırılmamış ya da eksiltilmemiş değer sokulmaktadır. Örneğin: + + a = 3; + b = ++a * 2; + + Burada 3 operatör vardır. En önceliklisi ++ operatördür. O halde a 1 artırılacak ve 4 olacaktır. Sonraki işlem * işlemidir. O halde * işlemine + artırma öncek yapıldığı için artırılmış değer olan 4 sokulacaktır. Bu duurmda 4 değişkeni 4 değerine olurken b değişkeni 8 olacaktır. Şimdi aynı işlemi + sonek olarak yapalım: + + a = 3; + b = a++ * 2; + + Burada da a önce artırılır 4 olur. Ancak sonraki işlem olan * işlemine a'nın artırılmış değeri olan 3 sokulur. Bu durumda a 4 olurken b ise + 6 olacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + a = 3; + + b = ++a * 2; + printf("a = %d, b = %d\n", a, b); /* a = 4, b = 8 */ + + a = 3; + + b = a++ * 2; + printf("a = %d, b = %d\n", a, b); /* a = 4, b = 6 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Önek ve sonek etki aşağıdaki basit örnekle de daha iyi anlaşılabilir: + + a = 3; + b = --a; + + Burada önce a eksiltilir, 2 olur. Sonraki işlem atama işlemidir. O halde b'ye a'nın eksiltilmiş değeri atanır. Yani b de 2 olacaktır. Fakat örneğin: + + a = 3; + b = a--; + + Burada yine a bir eksiltilir ve 2 olur. Ancak sonraki işlem olan atama işlemine a'nın eksiltilmemiş değeri olan 3 sokulur. Böylece b 3 olur. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + a = 3; + + b = --a; + printf("a = %d, b = %d\n", a, b); /* a = 2, b = 2 */ + + a = 3; + + b = a--; + printf("a = %d, b = %d\n", a, b); /* a = 2, b = 3 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Tabii ++ ve -- operatörleri tek başlarına kullanılıyorsa bunların önek ve sonek kullanımları arasında bir fark oluşmaz yani örneğin: + + ++a; + + ile + + a++; + + arasında bir fark yoktur. Fark ifadede başka operatörler varsa ortaya çıkmaktadır. Örneğin: + + a = 3; + b = 2; + + c = ++a * b--; + + Burada önce b eksiltilir 1 olur. Sonra a artırılır 4 olur. Çarpma işlemine a'nın artırılmış değeri ancak b'nin eksiltilmemiş değeri sokulur. Bu durumda + c'ye 8 atanacaktır. + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b, c; + + a = 3; + b = 2; + + c = ++a * b--; + printf("a = %d, b = %d, c = %d\n", a, b, c); /* a = 4, b = 1, c = 8 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + 15. Ders 19/07/2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Tabii başka bir hiçbir operatör yoksa ++ ve -- operatörlerinin önek ve sonek kullanımları arasında bir fark oluşmaz. Örneğin: + + ++a; + + ile + + a++; + + arasında bir farklılık yoktur. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + a = 3; + ++a; + + printf("a = %d\n", a); /* a = 4 */ + + a = 3; + a++; + + printf("a = %d\n", a); /* a = 4 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + ++ ve -- operatörlerinin operand'larının nesne belirtmesi yani sol tarafa değeri olması gerekir. Örneğin aşağıdaki gibi bir ifade geçerli değildir: + + ++3; /* geçersiz! */ +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C (ve C++) standartlarında "tanımsız davranış (undefined behavior)" denilen bir kavram vardır. Tanımsız davranış terimi standartlarda semantik bir tanımın + yapılmadığı kodlar için kullanılmaktadır. Tanımsız davranışa yol açan kodlar sentaks bakımdan geçerlidir. Dolayısıyla başarılı bir biçimde derlenirler. + Ancak programın çalışma zamanı sırasında sorunlar ortaya çıkabilmektedir. Bu sorunlar "programın çökmesi", "umulmadık biçimde programın çalışması", + "hatalı birtakım değerlerin ortaya çıkması" biçiminde olabilir. Bazen tanımsız davranışa yol açam kodlar görünüşte bir soruna yol açmayabilir. Ancak + programın değişik zamanlarda çalışmtırılması sırasında tutarsızlıklar oluşturabilmektedir. Sonuç olarak bir kod eğer "tanımsız davranışa" yol açıyorsa + programcının o kodu kullanmaması gerekir. Kullanırdsa artık programın sağlıklı çalışması gaeranti olmaz. Tanımsız davranışların "derleme aşamasına ilişkin değil", + "prıogramın çalışma zamanına ilişkin" oluşsuzluklar doğrabildiğine dikkat ediniz. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C (ve C++) standartlarında karşılaşılan diğer bir kavram da "derleyiciye bağlı davranış (implementation depnedent (defined) behavior)" kavramıdır. + Standratlarda bazı durumlarda ilgili özelliğin derleyiciden derleyiciye değişebileceği belirtilmiştir. Yani ilgili özellik için açık bir belirleme yapmak yerine + standartlar bu belirlemenin derleyicileri yazanlar tarafından yapılacağını belirtmektedir. Örneğin int türünün (ve char dışındaki diğer türlerin) + uzunlukları derleyiden derleyiciye değişebilmektedir. Bu uzunluklar "derleyiciye bağlı bir davranışa" yol açmaktadır. Ancak derleyiciye bağlı davranışların + ilgili derleyicinin dokümantasyonunda dokümante edilmiş olması gerekmektedir. Yani derleyicilerin bir referans gibi kitapları olmalıdır. Orada standartlarda + belirtilen "derleyiciye bağlı davranışların" o derleyicide nasıl ele alındığının belirtilmesi gerekmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C (ve C++) standartlarında geçen diğer önemli bir kavram da "belirsiz davranış (unspecified behavior)" kavramıdır. Belirsiz davranışta sınırlı sayıda seçenek + söz konusudur. Ancak bu seçeneklerin hangisinin uygulnadığı derleyiciden derleyiciye değişebilir. Bu seçeneklerin hiçbiri normal koşullarda programın + çökmesine ya da hatalı sonuçların oluşmasına yol açmamaktadır. Derleyiciler belirsiz davranışlarda hangi seçeneği seçtiklerini dokümante etmek zorunda değillerdir. + Belirsiz davranışın tanımsız davranıştan en önemli farkı tanımsız davranışın tamamen patolojik bir durum olması ancak belirsiz davranışın patolojik bir durum olmamasıdır. + Belirsiz davranışının derleyiciye bağlı davranıştan en önemli farkı, belirsiz davranış için derleyicilerin bunları dokümante etme zorunluluklarının olmamasıdır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C (ve C++) standartlarındaki önemli bir durum da şudur: C standartları dilin sentaks ve semantik kısıtlarına uyulmadığı durumlarda bu durum için derleyicilerin + en az bir hata mesajı vermesi gerektiğini belirtmektedir. Ancak standartlara göre geçerli bir program derleyici tarafından başarılı bir biçimde + derlenmek zorundadır ancak geçersiz bir program derleyici tarafından yine de başarılı bir biçimde derlenebilir. Yani standartlar geçersiz programların + başarılı bir biçimde derlenip derlenmeyeceği konusunda bir yargıda bulunmamaktadır. Gerçekten de pek çok derleyici bazı geçersiz kodları birer uyarı vererek + başarılı bir biçimde derlemektedir. Ancak bu durum kodun geçerli olduğu anlamına gelmemektedir. (Dolayısıyla C'de bizim bir durumun geçerliliği hakkında + derleyicinin kodu başarılı bir biçimde derleyip derlemediğine bakarak karar vermemeiz gerekir. Çünkü derleyiciler geçersiz kodları da başarılı bir biçimde derleyebilmektedir.) + Tabii bizim dilin kuralarrına tamamen uymamız gerekir. Çünkü bir derleyici geçersiz programı derliyor olsa da diğer bir derleyici onu derlemeyebilir. + Ancak kodumuz geçerliyse her derleyici kodumuzu derlemek zorundadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bir nesne bir ifadede ++ ya da -- operatörleriyle kullanılmışsa artık o ifadede bir daha o nesne kullanılmamalıdır. Eğer kullanılırsa bu durum + tanımsız davranışa yol açmaktadır. Bu durumda aşağıdaki gibi kodların hepsi geçerli ancak tanımsız davranışa yol açan kodlardır: + + b = ++a + a; + b = a++ + a; + b = ++a + ++a; + a = ++a; + b = a + a--; + + Bu kodlarda nasıl bir sonuç elde edileceğinin bir garantisi yoktur. Ancak yukarıdaki kodlar örneğin Java ve C# gibi dillerde "tanımlı (well defined)" + kodlardır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de toplam 6 tane karşılaştırma operatörü vardır: + + <, >, <=, >= + == != + + Öncelik tablosunda karşılaştırma operatörleri aritmetik operatörlerden daha düşük öncelikli biçimde bulunmaktadır: + + () Soldan-Sağa + + - ++ -- Sağdan-Sola + * / % Soldan-Sağa + + - Soldan-Sağa + < > <= >= Soldan-Sağa + != == Soldan-Sağa + = Sağdan-Sola + + Karşılaştırma operatörlerinin de öncelik tablosunda iki farklı düzeyde bulunduuna dikkat ediniz. + + C'de karşılaştırma operatörlerinin ürettiği değerler int türdendir. Eğer önerme doğruysa bu operatörler 1 değerini, yanlışsa 0 değerini üretirler. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int result; + + result = 3 > 1; + printf("%d\n", result); /* 1 */ + + result = 3 == 1; + printf("%d\n", result); /* 0 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki ifadeye dikkat ediniz: + + b = 10 < a < 20; + + Bu ifade matematikte a'nın 10 ile 20 arasında olduğuna ilişkin bir anlama gelse de C'de böyle bir anlama gelmemektedir. C'de bu ifade şöyle ele alınmaktadır: + + İ1: 10 < a (1 ya da 0 elde edilir) + İ2: İ1 < 20 + İ3: b = İ2 + + Karşılaştırma operatörleri aritmektik operatörlerden düşük önceliklidir. Örneğin: + + a + b > c + d + + Böyle bir işlemde a + b ile c + d karşılaştırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int result; + + result = 1 + 2 < 3 + 4; + printf("%d\n", result); /* 1 */ + + result = 1 + (2 < 3) + 4; + printf("%d\n", result); /* 6 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de üç mantıksal operatör vardır: + + ! NOT + && AND + || OR + + && ve || operatörleri iki operand'lı arek operatörlerdir. Ancak ! operatörü tek operand'lı önek bir operatördür. Öncelik tablosunda ! operatörü + diğer tek operand'lı operatörlerin bulunduğu ikinci düzeydedir. Ancak && ve || operatörleri karşılaştırma operatörlerinden daha düşük önceliklidir. + + () Soldan-Sağa + + - ++ -- ! Sağdan-Sola + * / % Soldan-Sağa + + - Soldan-Sağa + < > <= >= Soldan-Sağa + != == Soldan-Sağa + && Soldan-Sağa + || Soldan-Sağa + = Sağdan-Sola +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Mantıksal operatörler her zaman int türden değer üretirler. İşlem sonucu Doğru ise 1 değerini, yanlış ise 0 değerini üretmektedirler. Bu operatörler + önce operand'larını Doğru ya da Yanlış olarak ele alırlar. Eğer operand sıfır dışı (non-zero) herhangi bir değerdeyse Doğru olarak, sıfır değerindeyse + Yanlış olarak ele alınmaktadır. Örneğin: + + -3 && 5.7 + + Burada -3 Doğru olarak, 5.7 de Doğru olarak ele alınır. Doğru ve Doğru işlemi Doğru sonucunu verir. Doğru için 1 değeri üretilecektir. Örneğin: + + -1 || 0 + + Buradan 1 değeri üretilir. Örneğin: + + 0 && -8 + + Buradan 0 değeri üretilir. + + ! operatörü Doğruyu Yanlış, Yanlışı Doğru yapan bir operatördür. Öncelik tablosunun ikinci düzeyinde sağdan sola öncelikte bulunur. Örneğin: + + result = !3.5; + + Burada 3.5 Doğru olarak ele alınır. ! operatörü Yanlış değeri için 0 üretmektedir. Örneğin: + + result = !!!-3.2; + + İ1: !-3.2 ---> 0 + İ2: !İ1 ---> 1 + İ3: !İ2 ---> 0 + İ4: result = İ3 + + Örneğin: + + result = !0 + 2 + + İ1: !0 ---> 1 + İ2: İ1 + 2 ---> 3 + İ3: result = İ2 + + && ve || operatörlerinin karşılaştırma operatörlerinden düşük öncelikli olması karşılaştırmanın sonuçlarının mantıksal işlemesokulacağı anlamına gelmektedir. Örneğin: + + result = a > 10 && a < 20; + + Burada iki koşulk da doğruysa 1 değeri diğer durumlarda 0 değeri elde edilecektir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + int result; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + result = a >= 10 && a <= 20; + + printf("%d\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + && ve || operatörlerinin "kısa devre (short circuit)" özelliği vardır. Bu operatörler klasik öncelik tablosu kuralına uymazlar. Bu operatörlerin + sağında ne kadar öncelikli operatör olursa olsun bunların önce sol tarafı yapılır. Eğer && operatöründe sol taraf sıfır ise sağ taraf hiç yapılmaz + sonuç hemen 0 olarak belirlenir. Eğer && operatöründe sol taraf sıfır dışı bir değer ise bu durumda sağ taraf yapılmaktadır. Aynı dırım || operatörü için de + geçerlidir. Bu operatörün sol tarafı eğer sıfır dışı bir değerdeyse sağ tarafı hiç yapılmaz ve sonuç 1 olarak belirlenir. Eğer bu operatörün sol tarafı + sıfır dışı bir değerdeyse bu durumda sağ tarafı yapılır. + + Aşağıdaki program bu durumun anlaşılması için verilmiştir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + int result; + + a = 1; + b = 3; + result = a > 10 && ++b > 2; + + printf("result = %d, b = %d\n", result, b); /* result = 0, b = 3 */ + + a = 20; + b = 3; + result = a > 10 && ++b > 2; + + printf("result = %d, b = %d\n", result, b); /* result = 1, b = 4 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Her ne kadar henüz fonksiyonlar konusunu görmediysek de aşağıdaki örnekte bar fonksiyonu çağrılmayacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int foo(void) +{ + printf("foo\n"); + + return 0; +} + +int bar(void) +{ + printf("bar\n"); + + return 1; +} + +int main(void) +{ + int result; + + result = foo() && bar(); + + printf("%d\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + 16. Ders 21/07/2022 - Perşembe +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + && ve || operatörleri aynı ifadede kullanıldığında yine en soldaki operatörün sol tarafı önce yapılır. Aslında kısa devre özelliği yalnızca sonucun hızlı bir + biçimde bulunmasına yol açmaktadır. Yoksa kısa devre özelliğinin olmadığı durumla olduğu durum arasında bir sonuç farklılığı oluşmaz. Örneğin: + + ifade1 || ifade2 && ifade3 + + Burada önce ifade1 yapılır. Eğer ifade1 sıfır dışı bir değerse başka hiçbir şey yapılmaz. Sonuç 1 olarak elde edilir. Eğer ifade1 sıfır ise bu durumda + ifade2 yapılır. İfade2 de sıfır ise ifade3 yapılmaz. Burada tüm ifadelerin yapılması için ifade1'in sıfır, ifade2'nin sıfır dışı bir değer vermesi gerekir. + Örneğin: + + ifade1 && ifade2 || ifade3 + + Burada yine ifade1 önce yapılır. İfade1 sıfır ise ifade2 yapılmaz. Ancak ifade3 yapılır. Eğer ifade1 sıfır dışı bir değerde ise bu durumda ifade2 yapılır. + Eğer ifade2 de sıfır dışı ise ifade3 yapılmaz. Aşağıdaki ifadede önce ifade'ün yapılması daha hızlı sonucun elde edilmesine yol açabileceği halde her zaman && ve || operatörlerinin sol tarafı + önce yapılmaktadır. Yani aşağıdaki örnekte yine ifade1 ince yapılacaktır.: + + ifade1 && ifade2 || ifade3 + + Her ne kadar henüz fonksiyonları görmemiş olsak da aşağıdaki örnek kısa devre özelliğini incelemek amacıyla kullanılabilir. Tabii aslında parantezler de + işlemlerin yappılma sırası bakımından bir şeyi değiştirmeyecektir. Örneğin: + + ifade1 && (ifade2 || ifade3) + + Burada her ne kadar || işlemi paranteze alınmışsa da bu parantez içi önce yapılmaz. Çünkü önce yapılsaydı && operatörünün sağ tarafı önce yapılmış olurdu. + Burada da yine önce ifade1 yapılır. İfade1 0 ise başka bir şey yapılmaz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int foo(void) +{ + printf("foo\n"); + + return 0; +} + +int bar(void) +{ + printf("bar\n"); + + return 0; +} + +int tar(void) +{ + printf("tar\n"); + + return 1; +} + +int main(void) +{ + int result; + + result = foo() || bar() && tar(); + printf("%d\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Atama operatörü iki operand'lı araek özel amaçlı bir operatördür. Bu operatörün sol tarafındaki operand'ın bir nesne belirtmesi gerekir. Yani sol taraf değeri + (LValue) olması gerekir. Atama operatörü de bir değer üretmektedir. Atama operatörünün ürettiği değer sol taraftaki nesneye atanmış olan değerdir. + Atama operatörü öncelik tablosunda düşük düzeyde sağdan sola grupta bulunmaktadır. + + () Soldan-Sağa + + - ++ -- ! Sağdan-Sola + * / % Soldan-Sağa + + - Soldan-Sağa + < > <= >= Soldan-Sağa + != == Soldan-Sağa + && Soldan-Sağa + || Soldan-Sağa + = Sağdan-Sola + + Bu durumda örneğin: + + a = b = 10; + + İ1: b = 10 --> 10 + İ2: a = İ1 + + Böylece burada 10 hem b'ye hem de a!ya atanmış olur. Örneğin: + + a = b = 10 + 20; + + Burada a ve b'ye 30 atanmaktadır. Ancak örneğin: + + a = (b = 10) + 20; + + Burada parantez içi önce yapılacağına göre b'ye 10 atanacak ve bu işlemden 10 değeri elde edilecektir. Sonra bu 10 değeri 20 ile toplanıp a'ya atanacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + a = b = 10 + 20; + + printf("a = %d, b = %d\n", a, b); /* a = 30, b = 30 */ + + a = (b = 10) + 20; + + printf("a = %d, b = %d\n", a, b); /* a = 30, b = 10 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Tanımlama sırasında tanımlanan değişkene '=' atomu ile ilkdeğer verilebiliyordu. İlkdeğer vermedeki '=' bir operatör olarak değerlendirilmemektedir. + Bu işlem bildirim işleminin bir parçasıdır. Dolayısıyla buradaki '=' bir operatör olarak ele alınmaz. Böyle olunca da buradaki '=' atomunun bir değer + üretmesi söz konusu değildir. Örneğin aşağıdaki gibi bir bildirim geçerli değildir: + + int a = b = 10; /* geçersiz! Buradaki '=' bir operatör değil */ + + Ancak aşağıdaki gibi bir bildirim geçerlidir: + + int a = 10, b = a; /* geçerli */ + + C'de bir değişken dekleratörden sonra (bu kavram ileride açıklanacaktır) ancak ilkdeğer vermeden önce faaliyet alanına sokulmuş olmaktadır. Dolayısıyla + C'de aşağıdaki gibi bir bildirim geçerli ancak anlamsızdır. Örneğin: + + int a = a; + + Burada a yerel bir değişkense a'ya çöp değer, global bir değişkense 0 atanmaktadır. + + Bazen programcı bir değeri önce atayıp, atanmış değeri başka bir değerle karşılaştırmak isteyebilir. Bunun için atama operatörüne öncelik vermek gerekir. + Örneğin: + + (ch = getchar()) != 'q' + + Burada önce getchar ile klavyeden (stdin dosyasından) okunan değer ch değişkenine atanmıştır. Sonra bu atanan değer karşılaştırma işlemine sokulmuştur. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bir grup +=, -=, *=, /=, %=, ... biçiminde "bileşik atama operatörü (compund assignment operator)" vardır. Bu operatörlerin hepsi iki operand'lı + araek operatörlerdir. "op", +, -, *, / gibi bir operatör belirtmek üzere: + + a op= b + + işlemi tamamen, + + a = a op b + + işlemi ile eşdeğerdir. Örneğn: + + a += 2; + + ile + + a = a + 2; + + eşdeğerdir. Örneğin: + + a *= b; + + ile + + a = a * b; + + eşdeğerdir. + + Bileşik atama operatörleri öncelik tablosunda atama operatör ile sağdan sola aynı grupta bulunmaktadır. + + () Soldan-Sağa + + - ++ -- ! Sağdan-Sola + * / % Soldan-Sağa + + - Soldan-Sağa + < > <= >= Soldan-Sağa + != == Soldan-Sağa + && Soldan-Sağa + || Soldan-Sağa + =, +=, /=, *=,... Sağdan-Sola + + Örneğin: + + a *= 2 + 3; + + Burada önce 2 ile 3 toplanır. Sonra *= işlemi yapılır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a = 2; + + a *= 2 * 3; + + printf("%d\n", a); /* 12 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bileşik atama operatörleri de değer üretmektedir. Bu operatörlerin ürettiği değerler yine sol taraftaki nesneye atabnuş olan değerlerdir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a = 2, b; + + b = (a *= 2) * 3; + + printf("a = %d, b = %d\n", a, b); /* a = 4, b = 12 */ + + a = 2; + b = a *= 2 * 3; + + printf("a = %d, b = %d\n", a, b); /* a = 12, b = 12 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Virgül de iki oepardn'lı araek bir operatördür. Önceli tablosunun en düşük öncelikli operatörüdür. + + () Soldan-Sağa + + - ++ -- ! Sağdan-Sola + * / % Soldan-Sağa + + - Soldan-Sağa + < > <= >= Soldan-Sağa + != == Soldan-Sağa + && Soldan-Sağa + || Soldan-Sağa + =, +=, /=, *=,... Sağdan-Sola + , Soldan-Sağa + + Virgül operatörü aslında iki ifadeyi tek bir ifade biçiminde ifade edebilmek için düşünülmüştür. Tipik kullanım biçimi şöyledir: + + ifade1, ifade2 + + Virgül operatörünün sağında ne kadar yüksek öncelikli bir operatör olursa olsun, önce onun sol tarafı tamamen yapılır birilir, sonra sağ tarafı + tamamen yapılır bitirilir. Virgül operatöründen elde edeilen değer sağ taraftaki ifadenin değeridir. Örneğin: + + a = 10; b = 20; + + Burada iki ayrı ifade vardır. Ancak örneğin: + + a = 10, b = 20; + + Burada tek bir ifade vardır. Bazen sentaks olarak tek bir ifadein gerektiği yerde birden fazla ifade kullanılabilmesi için bu iki ifadenin , operatörü + ile birleştirilmesi gerekebilmektedir. Virgül öncelik tablosunun en düşük öncelikli operatörüdür. Dolayısıyla örneğin: + + a = 10, b = 20; + + gibi bir işlem şu sırada yapılır: + + İ1: a = 10 + İ2: b = 20 + İ3: İ1, İ2 + + Virgül operatörünün ürettiği değer sağ taraftaki ifadenin değeridir. Yani virgül operatörünün solundaki ifadenin değer üretmekte bir etkisi yoktur. + Örneğin: + + c = (a = 10, b = 20); + + Burada parantezler sayesinde en soldaki atama operatörü virgül operatöründen ayrıştrılmıştır. Burada önce parantez içi yapılacaktır. Parantez içerisinde + virgül operatörü vardır. O zaman viegül operatörünün sol tarafı önce yapılacağında göre önce a = 10 işlemi sonra b = 20 işlemi yapılır. Virgül operatöründen + elde edilen değer sağ taraftaki ifadenin değeri olduğuna göre buradan 20 elde edilecektir. İşte bu 20 aynı zamanda c'ye atanmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b, c; + + c = (a = 10, b = 20); + printf("a = %d, b = %d, c = %d\n", a, b, c); /* a = 10, b = 20, c = 20 */ + + return 0; +} +/*---------------------------------------------------------------------------------------------------------------------- + Tabii her virgül virgl operatörü değildir. Örneğin bildirim yaparken kullandığımız virgüller bu bağlamda bir operatör belirtmezler. Buradaki virgüller + ayıraç atom görevindedir. Örneğin: + + int a, b, c; /* Buradaki virgüller operatör görevinde değil */ + + Örneğin bir fonksiyon çağırırken argümanları ayırmak için kullandığımız virgül de ayrıraç atom görevindedir: + + foo(a, b, c); /* Buradaki virgüller de operatör görevinde değil */ + + Eğer argümandaki ',' atomumun virgül operatör olması isteniyorsa bu durumda parantezler kullanılmalıdır. Örneğin: + + foo(a, b); + + Buradaki ',' operatör görevinde değildir. Dolayısıyla foo fonksiyonunun iki parametresi vardır. Fakat örneğin: + + foo((a, b)); + + Buradaki virgül artık paranteze alındığı için operatör görevinddir. Parantez içerisinden b'nin değeir elde edilecektir. Dolayısıyla fonksiyonun aslında + tek parametresi vardır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + printf("%d\n", (a = 10, b = 20)); /* tuhaf ama geçerli, b yazdırılıyor */ + printf("%d\n", a); /* 10 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Virgül operatörü de soldan-sağa önceliklidir. Yani bir ifadede birden fazla virgül operatörü bulunabilir. Örneğin: + + ifade1, ifade2, ifade3 + + Burada işlemler şöyle yütülür: + + İ1: ifade1, ifade2 + İ2: İ1, ifade3 + + Yani burada sonuçta bu ifadeler soldan sağa sırasıyla yapılacaktır. Buradan elde edilen toplam sonuç en sağdaki ifadenin değeridir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + a = (10, 20, 30); /* geçerli ama tuhaf, a'ya 30 atanır */ + + printf("%d\n", a); /* 30 */ + + return 0; +} + +*---------------------------------------------------------------------------------------------------------------------- + C'de ';' ifadeleri sonlandırmak için kullanılmaktadır. Bu görevdeki atomlara programlama dillerinde "sonlandırıcı (terminator)" denilmektedir. + Biz ifadenin sonuna ';' koyduğumuzda artık o ifadeyle sonraki ifadenin ayrı ifadeler olduğunu derleyiciye söylemiş oluruz. Eğer bir ifadenin sonundaki + ';' unutulursa derleyici önceki ifadeyle sonraki ifadeyi tek bir ifade olarak ele alır. Bu da sentaks hatasına yol açar. Örneğin: + + a = 10 + b = 20; + + Burada muhtemelen a = 10'dan sonraki ';' atomu unutulmuştur. O halde derleyiciye göre burada tek bir ifade vardır. Ancak bu ifade geçerli değildir. + + Bazı dillerde sonlandırıcı olarak LF karakteri kullanılmaktadır. Dolayısıyla o dillerde aynı satıra tek bir ifade yazılmak zorundadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Biz şimdiye kadar zaten var olan fonksiyonları çağırdık. Artık biz de fonksiyon yazacağız. Bir fonksiyonun yazılmasına C standartlarında "fonksiyonun + tanımlanması (function definition)" denilmektedir. Fonksiyon tanımlamanın genel biçimi şöyledir: + + ([parametre bildirimi]) + { + /* ... */ + } + + Örneğin: + + int foo() + { + /* ... */ + } + + Burada int fonksiyonun geri dönüş değerinin türüdür. foo ise fonksiyonun ismini belirtir. Fonksiyon parametre değişkenlerine sahip değildir. + Fonksiyonun geri dnüş değerinin türü klasik C'de (yani C90'da) yazılmak zorunda değildi. Bu duurmda sanki "int" yazılmış gibi işlem yapılıyordu. + Ancak C99 ile birlikte fonksiyonun geri dönüş değerinin türünün yazılması zorunlu hale getirilmiştir. + + bar() /* C90'da geçerli C99 ve sonrasında geçerli değil */ + { + /* ... */ + } + + Eğer fonksiyonun parametresi yoksa parametre parantezinin içi boş bırakılabilir ya da parametre parantezinin içerisine void yazılabilir. İkisi arasında + hiçbir farklılık yoktur. Biz kursumuzda genel olarak parametresiz fonksiyonlarda parametre parantezinin içine void anahtar sözcüğünü yazacağız. + Ancak programcıların bir bölümü hiçbir şey yazmamayı tercih etmektedir. + + Biz kurusumuzdki örneklerde "öylesine uydurulmuş fonksiyon isimleri" olarak foo, bar, tar, zar gibi isimleri kullanacağız. Bu isimlerin hiçbir özel + anlamı yoktur. Örneklerde öylesine uydurulmuş isimlerdir. + + Tanımlanan her fonksiyonun bir ana bloğu vardır. Buna "fonksiyonun gövdesi (function body)" de denilmektedir. + + C'de iç içe (nested) fonksiyon tanımlaması yapılamaz. Her fonksiyon biribirinin dışında ve global düzeyde tanımlanmak zorudadır. Örneğin: + + int foo() + { + int bar() /* geçersiz! */ + { + /* ...*/ + } + /* ... */ + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 17. Ders 26/07/2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir fonksiyon çağrıldıktan sonra onu çağıran fonksiyona ilettiği değere "geri dönüş değeri (return value)" denilmektedir. Fonksiyonun geri dönüş değerinin + bir türü vardır. Bu tür fonksiyon isminin soluna yazılır. Geri dönüş değerini oluşturmak için return deyimi kullanılır. return deyiminin genel biçimi şöyledir: + + return [ifade] + + return deyimi hem fonksiyonu sonlandırır hem de geri dönüş değerini oluşturur. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int foo(void) +{ + printf("foo\n"); + + return 100; +} + +int main(void) +{ + int result; + + result = foo() * 2; + printf("%d\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bir fonksiyonun geri dönüş değerinin olması onu kullanmayı zorunlu hale getirmez. Yani fonksiyonların geri dönüş değerlerini fonksiyonu çağıran + hiç kullanmayabilir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int foo(void) +{ + printf("foo\n"); + + return 100; +} + +int main(void) +{ + foo(); /* foo çağrıldı ancak geri dönüş değeri kullanılmadı, tamamen geçerli */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Fonksiyon tanımlarken geri dönüş değerinin tütü yerine "void" anahtar sözcüğü yazılırsa bu durum "fonksiyonun bir değer geri föndürmediği" anlamına gelmektedir. + Böyle fonksiyonlar geri dönüş değerinin kullanıldığı bir ifadede kullanılamazlar. Örneğin: + + void foo(void) + { + printf("foo\n"); + } + ... + x = foo(); /* geçersiz! foo'nun geri dönüş değer yok */ + x = foo() * 2; /* geçersiz! foo'nun geri dönüş değeri yok */ + foo(); /* geçerli */ +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +void foo(void) +{ + printf("foo\n"); +} + +int main(void) +{ + foo(); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Geri dönüş değeri void olan fonksiyonlara "void fonksiyonlar" da denilmektedir. void fonksiyonlar da return deyimi kullanılabilir ancak return deyiminin yanına bir ifade + yazılamaz. Örneğin: + + void foo(void) + { + printf("foo\n"); + + return; /* geçerli */ + } + + void bar(void) + { + printf("foo\n"); + + return 10; /* geçersiz! void fonksiyon bir değerle geri döndürülemez */ + } + + Pekiyi o zaman void fonksiyonlardaki return deyimi ne işe yaramaktadır? İşte void fonksiyonlardaki return deyimleri fonksiyonu bir koşul altında + erken sonlandırmak için kullanılabilir. void fonksiyonlarda return kullanılmazsa akış fonksiyonun ana bloğunu bitirdiğinde zaten fonksiyon sonlanmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +void foo(void) +{ + printf("foo\n"); + + return; /* geçerli */ +} + +int main(void) +{ + foo(); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Geri dönüş değeri void olmayan fonksiyonlarda eğer akış return deyimini görmeden ana blok sonlanırsa bu durum C'de geçerlidir (halbuki C#, Java gibi dillerde + geçersizdir). Bu durumda geri dönüş değeri olarak çöğ bir değer elde edilmektedir. Genellikle derleyiciler böylesi durumlarda bir uyarı mesajı ile programcıyı + uyarmaktadır. Ancak geri dönüş değeri void olmayan fonksiyonlarda return deyiminde return ifadesinin mutlaka bulundurulması gerekir. Örneğin: + + int foo(void) + { + printf("foo\n"); + } /* dikkat! fonksiyon çöp değerle geri dönüyor */ + + int bar(void) + { + printf("bar\n"); + + return; /* geçersiz! return anahtar sözcüğünün yanında bir ifade olması gerekirdi */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int foo(void) +{ + printf("foo\n"); +} + +int main(void) +{ + int result; + + result = foo(); /* dikkat! geçerli ama çöp değer elde ediliyor */ + printf("%d\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Mademki akış return deyimini göründe fonksiyon sonlanmaktadır. O halde return deyiminin altına deyim yazmanın bir anlamı yoktur. Bu durum anlamsız olsa da + C'de geçerlidir. Örneğin: + + int foo(void) + { + printf("foo\n"); + + return 100; + + printf("foo ends...\n"); /* unreachable code */ + } + + Akışın asla ulaşamayacağı erişilemeyen bölgelere İngilizce "unreachable code" denilmektedir. Derleyiciler erişilemeyen kodları tespit edip bir uyarı + mesajı ile programcıya bildirebilmektedir. Pek çok derleyici erişilemeyen kodları tamaman koddan çıkartarak bir optimizasyon yapmaktadır. Bu optimizasyon + temasına "dead code elimination" denilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de main fonksiyonun geri dönüş değeri int türden olmak zorundadır. Ancak derleyiciler eğer isterlerse main fonksiyonunun başka türlerden geri + dönüş değerine sahip olmasına izin verebilirler. main fonksiyonundaki return deyimi aynı zamanda programı da sonlandırmaktadır. İşletim sistemleri dünyasında + çalışmakta olan programlara "process" denilmektedir. main fonksiyonu sonlandığında return deyimindeki ifade işletim sistemine "exit code" olarak iletilmektedir. + İşletim sistemleri bu exit kodu alır, eğer başka bprosesler isterse belli koşullarda onlara verebilir. Ancak exit kodunun kaç olduğuyla ilgilenmez. + Fakat geleneksel olarak C'de başarılı ve mutlu sonlanmalar için exit kodu olarak 0, başarısız sonlanmalar için sıfır dışı değerler kullanılmaktadır. + Biz örneklerimizde main fonksaiyonunu her zaman 0 ile geri döndüreceğiz. Aslında C standartlarında main fonksiyonuna özgü olarak, eğer main fonksiyonunda hiç + return kullanılmazsa sanki ana bloğun sonuna return 0 yazılmış gibi işlem uygulanmaktadır. Yani main fonksiyonunda biz hiç return yazmasak da zaten return 0 + yazmış gibi bir durum oluşmaktadır. Tabii bu durum yalnızca main fonksiyonuna özgüdür. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Fonksiyonların geri dönüş değerleri geçici nesne yoluyla onu çağıran fonksiyona iletilmektedir. Programın akışı return deyimini gördüğünde önce derleyici + geri dönüş değeri türünden geçici bir nesne yaratır. Sonra return ifadesini bu geçici nesneye atar. Biz geri dönüş değerini kullandığımızda aslında o geçici + nesneyi kullanmış oluruz. Fonksiyonun çağrısı bittiğinde bu geçici nesne de derleyici tarafındna otomatik olarak yok edilmektedir. Örneğin: + + int foo(void) + { + /* ... */ + + return ifade; + } + ... + x = foo() * 2; + + Burada aslında arka planda aşağıdaki gibi işlemler gerçekleşmektedir: + + int temp = ifade; /* akış return deyimine geldiğinde */ + ... + x = temp * 2; + /* temp yok ediliyor */ + + Bir nesne yaratılırken ona değer atanmasına "ilkdeğer verme (initialization)" denildiğini anımsayınız. Fonksiyonun geri dönüş değerinin atanacağı + geçici nesne return ifadesiyle yaratıldığı için aslında return işlemi geçici nesneye bir ilkdeğer verme işlemi olarak da alınmaktadır. + + O halde fonksiyonun geri dönüş değerinin türü aslında return işlemiyle yaratılacak olan geçici nesnenin türünü belirtir. Bizim ileride atama işlemi hakkında söyleceğimiz + her şey return işlemi için de geçerlidir. Derleyiciler genel olarak mümkün olduğunca return işlemi sırasındaki geçici nesneleri CPU yazmaçlarında + yaratmaktadır. + + Fonksiyon çağrıları C'de her zaman sağ taraf değeri (rvalue) belirtmektedir. Yani return işlemiyle yaratılanm bu geçici nesneye biz atama yapamayız. Örneğin: + + foo() = 10; /* geçersiz! */ + + void fonksiyonlarda böyle bir geçici nesnenin hiç yaratılmayacağına da dikkat ediniz. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Fonksiyonların dış dünyadan aldıkları değerlere "parametre (parameter)" denilmektedir. C'de fonksiyon parametreleri parametre parantezinin içerisinde + tür ve değişken ismi belirtilerek ve ',' atomu ile parametreler ayrılarak bildirilmektedir. Örneğin: + + void foo(int a, long b, double c) + { + /* ... */ + } + + void bar(double a, int b) + { + /* ... */ + } + + Parametreler aynı türden olsa bile tür belirten sözcüğünyeniden yazılması gerekmektedir. Örneğin: + + void foo(int a, b) /* geçersiz! */ + { + /* ... */ + } + + Yuklarıdaki tanımlama geçersizdir. Şöyle yapılması gerekirdi: + + void foo(int a, int b) + { + /* ... */ + } + + Parametreli bir fonksiyon parametre sayısı kadar "argümanla" çağrılmalıdır. Argümanlar herhangi birer ifade olabilir. Örneğin: + + void foo(int a, int b) + { + /* ... */ + } + ... + foo(10 + 20, 30 + 40); /* geçerli */ + + Argüman olan ifadeler yine ',' atomu ile ayrılmaktadır. Fonksiyonu çağırırken yazılan ifadelere "argümen (argument)" denilmektedir. + + Parametreli bir fonksiyon çağrıldığında önce argümanların değerleri hesaplanır. Sonra argümanlardan parametre değişkenlerine karşılık bir atama + yapılır. Sonra da akış fonksiyona geçirilir. Yani C'de parametre aktarımı atama (ya da kopyalama) biçiminde yapılmaktadır. Örneğin: + + void foo(int a, int b) + { + /* ... */ + } + /* ... */ + int x = 10, y = 20; + + foo(x + 1, y + 2) + + Burada foo fonksiyonu çağrıldığında önce x + 1 ve y + 2 ifadelerinin değerleri hesaplanacak sonra x + 1 değeri a'ya, y + 2 değeri ise b'ye atanacaktır. + Sonra da akış fonksiyona geçirilecektir. Parametre değişkenlerinin bağımsız ayrı nesneler olduğuna dikkat ediniz. Fonksiyon çağırma işlemi argümanlardan + parametre değişkenlerine yapılan gizli bir atama işlemini gerektirmektedir. + + Aslında izleyen paragraflarda da göreceğimiz gibi fonkdiyonun parametre değişkenleri fonksiyon çağrıldığında yaratılmaktadır. Dolayısıyla + fonksiyon çağırma işlemi aslında parametre değişkenlerine ilkdeğer verme işlemi gibi de ele alınabilir. + + O halde C'de atama anlamına gelen üç durum vardır: + + 1) Açıkça '=' operatörü ile yapılan atamalar + 2) return işlemi sırasında geçici nesneye yapılan atamalar + 3) Fonksiyon çağırma sırasında argümanlardan parametre değişkenlerine yapılan atamalar + + Biz ileride atama işlemi için söyleyeceğimiz her şey bu üç durum için de geçerli olacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +void foo(int a, int b) +{ + printf("a = %d, b = %d\n", a, b); +} + +int main(void) +{ + int x = 100, y = 200; + + foo(10, 20); + foo(10 + 1, 20 + 2); + foo(x, y); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bazen fonksiyonlar parametreleriyle aldıkları değeri birtakım işlemlere sokup onu geri dönüş değeri olarak verebilirler. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int add(int a, int b) +{ + return a + b; +} + +int main(void) +{ + int result; + + result = add(10, 2); + printf("%d\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de bir atama işleminde kaynak tür ile hedef tür farklı olabilir. Örneğin: + + a = b; + + Burada a ve b'nin türleri farklı olabilir. Ancak bu konu ileride özel olarak ele alınacaktır. Siz şimdilik bu konu ele alınana kadar atama işleminde + kaynak türle hedef türü aynı türden yapmaya özen gösteriniz. Örneğin: + + #include + + double add(int a, int b) + { + int result; + + result = a + b; + + return result; /* dikkat! farklı türler birbirlerine atanıyor, ileri ele alınacak */ + } + + int main(void) + { + double x = 2.3, y = 4.5; + int result; + + result = add(x, y); /* dikkat farklı türler biribirene atanıyor, ileride ele alınacak */ + printf("%d\n", result); + + return 0; + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de parametreleriyle aldıkğı değerler üzerinde işlemler yaparak sonucu geri dönüş değeri olarak veren bir standart matematiksel fonksiyon vardır. + Bu fonksiyonları kullanmadan önce dosyası include edilmelidir. Örneğin sqrt fonksiyonun parametrik yapısı şöyledir: + + double sqrt(double x); + + Fonksiyon parametresiyle aldığı double sayının kareköküne geri dönmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + double val, result; + + printf("Bir deger giriniz:"); + scanf("%lf", &val); + + result = sqrt(val); + printf("%f\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + pow fonksiyonu bir sayının belli bir kuvvetine geri dönmektedir. Fonksiyonun parametrik yapısı şöyledir: + + double pow(double a, double b); + + Fonksiyon a üzeri b işlemine geri dönmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + double a, b, result; + + printf("Taban:"); + scanf("%lf", &a); + + printf("Us:"); + scanf("%lf", &b); + + result = pow(a, b); + printf("%f\n", result); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + sin, cos, tan, asin, acos, atan fonksiyonları trigonometrik işlemler yapmaktadır. Bu fonksiyonların parametreleri ve geri dönüş değerleri double + türdendir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + double result, radian; + + result = sin(3.141592653589793238462643 / 2); + printf("%f\n", result); + + radian = asin(result); + printf("%f\n", radian); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + round fonksiyonu double bir değeri parametre olarak alıp ona en yakın tamsayıyı yine double bir değer olarak vermektedir. + Fonksiyonun parametrik yapısı şöyledir: + + double round(double x); +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + double result; + + result = round(3.6); + printf("%f\n", result); /* 4 */ + + result = round(3.4); + printf("%f\n", result); /* 3 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C99 ile birlikte roundf ve roundl fonksiyonları da standartlara eklenmiştir. Bunların parametrik yapıları şöyledir: + + float roundf(float x); + long double roundl(float x); + + Yine C99 ile birlikte tamsayı değerlere geri dönen aşağıdakai fonksiyonlar da eklenmiştir. + + long int lround(double x); + long int lroundf(float x); + long int lroundl(long double x); + long long int llround(double x); + long long int llroundf(float x); + long long int llroundl(long double x); +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + long result; + + result= lround(3.6); + printf("%ld\n", result); /* 4 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + floor isimli fonksiyon double bir sayıya en yakın ondan küçük ya da ona eşit tamsayıyı bize double türden vermektedir. ceil ise tam ters işlem yapar. Yani + bir double sayıdan büyük ya da ona eşit en yakın tamsayıyı double türden vermektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + double result; + + result= floor(3.9); + printf("%f\n", result); /* 3 */ + + result = floor(-3.9); + printf("%f\n", result); /* -4 */ + + result = ceil(3.1); + printf("%f\n", result); /* 4 */ + + result = ceil(-3.1); + printf("%f\n", result); /* -3 */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C99 ile birlikte float ve long double için aşağıdaki fonksiyonlar da standartlara eklenmiştir: + + float floorf(float x); + long double floorl(long double x); + float ceilf(float x); + long double ceill(long double x); +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 18. Ders 28/07/2022 - Perşembe +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bildirilen bir değişkenin kullanılabildiği program aralığına "faaliyet alanı (scope)" denilmektedir. C'de üç faaliyet alanı vardır: + + 1) Blok Faaliyet Alanı (Block Scope): Yalnızca bir blokta o bloğun kapsadığı bloklarda tanınma aralığıdır. + 2) Dosya Faaliyet Alanı (File Scope): Tüm fonksiyonlarda yani her yerde tanınma aralığıdır. + 3) Fonksiyon Faaliyet Alanı (Function Scope): Bir fonksiyonun her yerinde tanınma aralığıdır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de yerel değişkenler blok faaliyet alanı kuralına uyarlar. Yani bildirildikleri yerden bildirildikleri bloğun sonuna kadarki bölgede kullanılabilirler. + Anımsanacağı gibi C90'da yerel değişkenler blokların başlarında yani blokların ilk işlemleri olacak biçimde bildirilmek zorundaydı. Bu kural C99'da + kaldırıldı. Yerel değişkenlerin bloğun herhangi bir yerinde bildirilmeleri sağlandı. Örneğin: + + void foo(void) + { + printf("foo\n"); + + int a; /* C90'da geçersiz! C99'dan itibaren geçerli */ + } + + void bar(void) + { + int a; /* C90'da da geçerli */ + + printf("bar\n"); + } + + Bir fonksiyonun içerisinde içerisinde istenildiği kadar iç içe ve ayrık blok oluşturulabilir. Örneğin: + + void foo(void) + { + { + printf("Ok\n"); + /* .... */ + } + { + printf("Ok\n"); + } + } + + Yerel değişkenler bildirildikleri yerden itibaren bildirildikleri bloğun sonuna kadarki bölgede kullanılabilirler. Örneğin: + + void foo(void) + { + int a; + + { + int b; + + a = 10; /* geçerli, a faaliyet gösteriyor */ + b = 20; /* geçerli, b faaliyet gösteriyor */ + } + printf("%d\n", a); /* geçerli, a faaliyet gösteriyor */ + printf("%d\n", b); /* geçersiz! b burada faaliyet göstermiyor */ + } + + void bar(void) + { + a = 100; /* geçersiz, a burada faaliyet göstermiyor */ + } + + Tabii C99 ve sonrasında bildirilen bir yeral değişken bildirim yerinden önce de kullanılamaz. Örneğin: + + void bar(void) + { + a = 10; /* geçersiz! a faaliyet göstermiyor */ + + int a; + + a = 20; /* geçerli */ + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de aynı faaliyet alanına ilişkin aynı isimli birden fazla değişken tanımlanamaz. Ancak farklı faaliyet alanlarına ilişkin aynı isimli değişkenler + tanımlanabilir. Aynı bloğun farklı yerlerinde tanımlanan değişkenler bu bakımdan aynı faaliyet alanı içerisinde kabul edilirler. Bu nedenle C'de aynı + blok içerisinde aynı isimli bir den fazla değişken tanımlanamaz. Ancak farklı bloklarda aynı isimli değişkenler tanımlanabilir. Örneğin: + + void foo(void) + { + int a; + /* ....*/ + double a; /* geçersiz! aynı blok içerisinde aynı isimli tek bir değişken tanımlanabilir */ + } + + Örneğin: + + void bar(void) + { + int a; + { + double a; /* geçerli iç içe bloklarda aynı isimli değişkenler tanımlanabilir */ + /* ... */ + } + /* ... */ + } + + void tar(void) + { + int a; /* geçerli */ + /* ... */ + } + + Farklı bloklardaki aynı isimli değişkenler aslında tamamen farklı nesneler belirtirler, bunların yalnızca isimleri aynıdır. Örneğin: + + void foo(void) + { + int a; + + { + int a; /* Bu tamamen farklı bir a */ + /* ... */ + } + } + + C'de aynı blokta birden fazla aynı isimli değişken faaliyet gösteriyorsa o blokta o değişken kullanıldığında her zaman "dar faaliyet alanına sahip olan" + değişkenin kullanılmış olduğu kabul edilir. Örneğin: + + { + int a; + + { + int a; + + a = 10; /* dar faaliyet alanına sahip olan iç bloktaki a'dır */ + } + + a = 20; /* dış bloktaki a, zaten iç bloktaki a burada faaliyet göstermiyor */ + } + + İç içe bloklarda aynı isimli değişkenlerin bildirildiği durumda iç blokta dış bloktaki değişkene erişmenin herhangi bir yolu yoktur. Bu duruma + "iç bloktaki değişkenin dış bloktaki gizlemesi (hiding)" denilmektedir. + + C99 ve sonrasında bir yerel değişken bloğun herhangi bir yerinde bildirilebildiğine göre bildirim yerine kadar üst bloktaki dğeişken faaliyet gösteriyor + durumdadır ve henüz üst bloktaki aynı isimli değişken gizlenmemiştir. Örneğin: + + #include + + int main(void) + { + int a; + + { + a = 20; /* ana bloktaki a */ + + int a = 30; + + printf("%d\n", a); /* iç bloktaki a */ + } + + printf("%d", a); /* ana bloktaki a */ + + return 0; + } +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bildirimi fonksiyonların dışında yapılan global değişkenler "dosya faaliyet alanı (file scope)" kuralına uyarlar. Yani kaynak dosyanın her yerinde, + tüm fonksiyonların içerisinde biz global değişkenleri kullanabiliriz. Ancak C'de derleme işleminin de bir yönü vardır. Bu yön yukarıdan aşağıya + doğrudur. Bir değişken bildirilmeden önce kullanılamaz. Bu nedenle bir global değişkeni aşağıda bir yerde bildirirsek bildirim yerinde aşağıya kadar + her yerde kullanırız. Ancak genel olarak global değişkenler kaynak dosyanın tepesinde bildirilirler. Öneğin: + + int a; + + void foo(void) + { + a = 20; /* global olan a */ + } + + int main(void) + { + a = 10; /* global a */ + foo(); + printf("%d\n", a); /* global a, 20 çıkacak */ + } + + Bir global değişken için en iyi tanımlama yeri kaynak kodun tepesidir. Örneğin: + + + void foo(void) + { + a = 10; /* geçersiz! henüz derleyici a'yı görmedi */ + } + + int a; + + void bar(void) + { + a = 20; /* geçerli */ + } + + void tar(void) + { + a = 30; /* geçerli */ + } + + Bir global değişkenle aynı isimli bir yerel değişken tanımlanabilir. Çünkü bunların faaliyet alanları farklıdır. Bir blokta aynı isimli birden fazla + değişken faaliyet gösteriyorsa o blokta dar faaliyet alanına sahip olan değişkene erişilmektedir. Örneğin: + + #include + + int x; + + void foo(void) + { + double x; + + x = 20; /* yerel x kullanılıyor */ + } + + int main(void) + { + x = 10; /* global x */ + + foo(); + printf("%d\n", x); /* global x, 10 çıkacak */ + + return 0; + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de istisnai olarak ilkdeğer verilmemiş birden fazla aynı isimli global değişken tanımlanabilir. Buna "tentative definition" denilmektedir. + Bu durumda aslında toplamda tek bir nesne yaratılır. Yani birden fazla tanımlama bu istisnasi durumda birden fazla nesne anlamına gelmez. + Aynı nesnenin gereksiz bir biçimde yeniden "tentative" olarak belirtlmesi anlamına gelir. Tabii bu durum yerel değişkenler için söz konusu değildir. + Tentative tanımlama tamamen istisnai bir durumdur ve yalnızca global değişkenler için söz konusudur. Bu nedenle aşağıdaki tanımlama geçerlidir. + Ancak aşağıdaki kodda tek bir x nesnesi vardır. "Tentative" sözcüğü "deneme niteliğinde" gibi bir anlama gelmektedir: + + #include + + int x; + int x; /* geçerli, özel bir durum, tentative definition */ + + int main(void) + { + x = 10; + printf("%d\n", x); + + return 0; + } + + int x; /* geçerli, tentative definiton */ + + Tentative tanımlama olması için global değişkene ilkdeğer verilmemiş olması gerekmektedir. Aynı isimli bir global değişkene bir kez ilkdeğer verilebilir. + Ancak birden fazla kez ilkdeğer verilemez. Örneğin: + + int a = 10; /* geçerli, tentative değil */ + int a; /* geçerli, tentative, aslında burada bir a yaratılmıyor */ + + Ancak örneğin: + + int a = 10; + int a = 20; /* geçersiz! tentative değil */ + + Bu kural ileride yeniden başka bir konunun içerisinde ele alınacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Daha önce de belirtidliği gibi içerisine henüz değer atanmamış yerel değişkenin içeisinde bellekte daha önceden kalmış olan "çöp bir değer (garbage value)" + bulunur. Ancak içerisine henüz değer atanmamış global bir değişkende her zaman 0 değeri olması garanti edilmiştir. + Ayrıca C'de içerisinde çöp değerlerin olduğu yerel değişkenlerin kullanılması "tanımsız davranışa (undefined behavior)" yol açmaktadır +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int x; + +int main(void) +{ + int a; + + printf("%d\n", a); /* geçerli, içerisine henüz değer atanmamış yerel değişkenler içerisinde çöp değer vardır (tanımsız davranış) */ + printf("%d\n", x); /* içerisine henüz değer atanmamış global nesneler içerisinde her zaman 0 olur */ +s + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Genel olarak global değişkenlerin yalnızca gerektiği durumlarda kullanılması gerekir. Yerel değişkenlerle yapabileceğimiz şeyler için global değişken + tanımlamak kötü bir tekniktir. Örneğin programımızda yalnızca main fonksiyonu olsun. Bu durumda global bir değişken tanımlamaya hiç gerek yoktur. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Fonksiyonların parametre değişkenleri faaliyet alanı bakımından ana bloğun başında tanımlanmış olan yerel değişkenler gibidir. Örneğin: + + void foo(int a, int b) + { + /* ... */ + } + + Buradaki a ve b parametre değişkenleri faaliyet alanı bakımından aşağıdaki fonksiyonla eşdeğerdir: + + void foo() + { + int a; + int b; + + /* ... */ + } + + Görüldüğü gibi fonksiyonların parametre değişkenleri "blok faaliyet alanı (block scope)" uymaktadır. Yani yalnızca o fonksiyonda kulalnılabilirler. + Dolayısıyla farklı iki fonksiyonun parametre değişkenleri aynı isimde olabilir. Örneğin: + + void foo(int a) + { + /* ... */ + } + + void bar(int a) /* geçerli, a yalnızca bu fonksiyonda kullanılabilir. + { + /* ... */ + } + + Mademki fonksiyonun parametre değişkenleri faaliyet alanı bakımından ana bloğun başında bildirilen değişkenler gibidir, o halde parametre değişkeni ile + aynı isimli fonksiyonun ana bloğunda bir değişken bildirilemez. Örneğin: + + void foo(int a) + { + int a; /* geçersiz! parametre değişkeni olan a da aynı faaliyet alanına sahip */ + + /* ... */ + } + + Fakat örneğin: + + void foo(int a) + { + { + int a; /* geçerli! iç içe yerel bloklarda aynı isimli değişkenler tanımlanabilir */ + + /* ... */ + } + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de fonksiyon faaliyet alanına sahip tek değişken "goto etiketleridir". Goto deyimi deyimlerin ele alındığı geşecek bölümlerde görülecektir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Daha önceden de belirtildiği gibi C Programlama Dili "Prosedürel Programlama Modeline (Procedural Programming Paradigm)" uygun tasarlanmıştır. + Prosedürel programlamada "fonksiyonlar" birer yapı taşıdır. Programlar fonksiyonların birbirlerini çağırması biçiminde organize edilirler. Halbuki + Nesne Yönelimli Programlama Modelinin yapı taşı "sınıf" denilen kavramdır. C++ Programala Dili zaten C Programa Dilinin "Nesne Yönelimli Programalama Modelinin" + uygulanabilmesi için genişletilmiş bir biçimidir. + + Pekiyi prosedürel teknikte neden program fonksiyonların birbirlerini çağırması biçiminde organize edilmektedir? Yani neden bürün program main fonksiyonunda + yazılıp bitirilmemektedir? Programın fonksiyonlar biçiminde organize edilmesinin birkaç açık sebei vardır: + + 1) Mühendislikte karmaşık bir problem genellikle parçalarına ayrılarak çözülmektedir. İşte fonksiyonlar karmaşık problemin parçalarını oluşturmak + için kulalnılmaktadır. Karmaşık işlemin parçaları fonksiyonlara yaptırılır. Sonra bu fonksiyonların çağrılmasıyla karmaşık işlem gerçekleştirilir. + Örneğin bir otomobil aslında çok fazla parçadan oluşmaktadır. Bu parçalar birbirleriyle monte edilmiştir. Sonuçta otomobil çalışır hale gelmiştir. + Aynı yöntem yazılımda da izlenmektedir. + + 2) Fonksiyonlar "yeniden kullanılabilirliği (reusability)" mümkün hale getirmektedir. Yani işin bir kısmını yapan kodları fonksiyon olarak yazarsak + başka projelerde de aynı fonksiyonları kullanabiliriz. Fonksiyonların oluşturduğu topluluğa "kütüphane (library)" denilmektedir. Örneğin standart C + fonksiyonları kütüphane biçiminde oluşturulmuştur. Biz onları farklı projelerde kullanabilmekteyiz. + + 3) Fonksiyonlar tekrarı engellemek amacıyla kullanılmaktadır. Bir iş kodun çeşitli yerlerinde yineleniyorsa onu fonksiyon olarak yazarsak toplamda + bu kodlardan bir tane projemizde bulundurmuş oluruz. Fonksiyonlar olmasaydı aynı kodu tekrar tekrar yazmak zorunda kalırdık. Bu durumda kod tekrarı + toplamda kodun fazla yer kaplamasına yol açardı. O kısımda yapılacak değişikler programın pek çok yerinde yapılmak zorunda kalırdı. Bu durum kodun aynı zamanda + daha karmaşık gözükmesine yol açardı. + + 4) Fonksiyonlar okunabilirliği de artırmaktadır. Fonksiyonların isimleri olduğu için kodu inceleyen kişiler onu daha kolay anlamlandırırlar. + Bu isimler aslında o kodun ne yaptığı hakkında da bilgi verir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 19. Ders - 02.08.2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir programlama dilindeki "çalıştırma birimlerine" "deyim (statement)" denilmektedir. Yani program aslında deyim denilen kod parçalarının peşi sıra + çalıştırılmasıyla çalışmaktadır. Deyimler C'de 5 gruba ayrılmaktadır: + + 1) Basit Deyimler (Simple Statements): Bunlar bir ifadenin sonuna ';' atomu konularak elde edilen deyimlerdir. Yani ifade; biçiminde bir görünüme sahiplerdir. + Örneğin: + + a = b + c; + foo(); + + Bunlar birer basit deyimdit. İfade (expression) kavramının ';' atomunu içermediğine ifadenin sonuna ';' getirildiğinde onun bir deyim olduğuna + dikkat ediniz. + + 2) Bileşik Deyimler (Compound Statements): Bir blok içerisine sıfır tane ya da daha fazla deyim yerleştirilirse bloğun kendisi de bir deyim olur. + Ona "bileşik deyim" denilmektedir. Örneğin + + { + ifade1; + ifade2; + ifade3; + } + + Burada bu bloğun tamamı dışarıdan bakıldığında tek bir deyimdir. + + 3) Kontrol Deyimleri (Control Statements): Programlama dillerinde programın akışı üzerinde etkili olan, if gibi, while gibi, for gibi deyimlere + "kontrol deyimleri" denilmektedir. Kontrol deyimleri dışarıdan bakıldığında tek bir deyim olarak ele alınırlar. + + 4) Bildirim Deyimleri (Declarartion Statements): Bildirim yapmakta kullandığımız sentaks biçimi de aslında bir deyim belirtir. Bunlara bildirim deyimleri + denilmektedir. Örneğin: + + int a, b, c; + + 5) Boş Deyimler (Null Statements): Solunda ifade olmadan kullanılan noktalı virgüller de bir deyim belirtir. Bunlara boş deyim denilmektedir. Örneğin: + + x = 10;; + + Burada iki deyim vardır. Birincisi x = 10; deyimidir. Bu bir basit deyimdir. İkincisi bundan sonraki noktalı virgüldür. Boş deyimler için bir şey yapılmıyor + olsa da bunlar yine bir deyim statüsündedir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Yukarıda da belirtildiği gibi program deyimlerin çalıştırılmasıyla çalıştırılmaktadır. Her deyim çalıştığında bir şeyler yapılır. Şimdi bu deyimler + çalıştırıldığında ne olacağı üzerinde duralım: + + - Bir basit deyimin çalıştırılması demek o basit deyimdeki ifadenin çalıştırılması demektir. + + - Bir bileşik deyimin çalıştırılması bileşik deyimi oluşturan deyimlerin sırasıyla çalıştırılması anlamına gelmektedir. Örneğin: + + { + ifade1; + ifade2; + { + ifade3; + ifade4; + } + } + ifade5; + + Burada dışarıdan bakıldığında iki deyim vardır: Bileşik deyim ve basit deyim. Bir bileşik deyimin çalıştırılması onu oluşturan deyimlerin sırasıyla + çalıştırılması anlamına geldiğine göre burada sırasıyla aslında ifade1, ifade2, ifade3, ifade4, ifade5 çalıştırılacaktır. + + - Kontrol deyimleri çalıştırıldığında nelerin olacağı zaten sonraki başlıklarda ele alınacaktır. + + - Bir bildirim deyimi çalıştırıldığında bildirilen değişkenler için bellekte yerler ayrılmaktadır. Örneğin: + + int a, b, c; + + Burada a, b ve c nesneleri için yerler ayrılacaktır. + + - Boş deyimin çalıştırılması sırasında bir şey yapılmamaktadır. Yani boş deyimler bir yan etkiye yol açmamaktadır. + + Bir fonksiyon çağrıldığında fonksiyonun belirttiği ana blok yani bileşik deyim çalıştırılır. Bu durumda bir C programının çalışması demek aslında + main fonksiyonun çağrılması demektir. Örneğin: + + int add(int a, int b) + { + return a + b; + } + + int main(void) + { + int a; + + a = add(10, 20); + printf("%d\n", a); + + return 0; + } + + Burada main fonksiyonu çağrıldığında onun ana bloğunun belirttiği bileşik deyim çalıştırılır. Bu bileşik deyim içerisinde bir bildirim deyimi, 2 tane basit deyim ve bir + tane kontrol deyimi vardır. a = add(10, 20) basit deyimi çalıştırılırken de add fonksiyonun ana beloğunun belrttiği bileşik deyim çalıştırılmış olur. + Yani görüldüğü gibi aslında program deyimlerin çalıştırılmasıyla çalıştırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Şimdi tek tek kontrol deyimlerini ele alacapız. En yaygın kullanılan kontrol deyimi if deyimidir. if deyiminin genel biçimi şöyledir: + + if () + + [ else + ] + + if anahtar sözcüğünden sonra parantezler içerisinde bir ifadenin bulunması gerekir. if deyiminin "doğru" ve "yanlış" kısımları vardır. Doğru ve yanlış kısımlarında + tek bir deyim bulunmak zorundadır. Programcı burada birden fazla deyim bulundurmak istiyorsa onu bileşik deyim olarak ifade etmelidir. if deyimiin yanlış kısmı + olmak zorunda değildir. if deyiminin tamamı dışarıdan bakıldığında tek bir deyim olarak ele alınmaktadır. + + if deyimi şöyle çalıştırılmaktadır: Önce if parantezi içerisindeki ifadenin sayısal değeri hesaplanır. Bu değer sıfır dışı bir değerse deyimin yalnızca + "doğru" kısmındaki deyim çalıştırılır. Bu ifadenin değeri 0 ise deyimin yalnızca "yanlış" kısmındaki deyim çelıştırılır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + if (a > 0) + printf("pozitif\n"); + else + printf("negatif ya da sifir\n"); + + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki programda ikinci derece denklemin kökleri yazdırılmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +void disp_roots(double a, double b, double c) +{ + double delta; + double x1, x2; + + delta = b * b - 4 * a * c; + if (delta >= 0) { + x1 = (-b + sqrt(delta)) / (2 * a); + x2 = (-b - sqrt(delta)) / (2 * a); + + printf("x1 = %f, x2 = %f\n", x1, x2); + } + else + printf("Gercek kok yok!..\n"); +} + +int main(void) +{ + double a, b, c; + + printf("a:"); + scanf("%lf", &a); + + printf("b:"); + scanf("%lf", &b); + + printf("c:"); + scanf("%lf", &c); + + disp_roots(a, b, c); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + İç içe (nested) if deyimi söz konusu olabilir. Örneğin aşağıda üç sayının en büyüğünü bulan bir if deyimi kullanılmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b, c; + + printf("a:"); + scanf("%d", &a); + + printf("b:"); + scanf("%d", &b); + + printf("c:"); + scanf("%d", &c); + + if (a > b) + if (a > c) + printf("%d\n", a); + else + printf("%d\n", c); + else + if (b > c) + printf("%d\n", b); + else + printf("%d\n", c); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + if deyiminin "yanlış" kısmı yani else kısmı olmak zoruda değildir. Eğer derleyici if deyiminin "doğru" kısmından sonra else anahtar sözcüğünü + göremezse bunun "else kısmı olmayan bir if" olduğuna karar verir ve if deyiminin bittiğini düşünür. Örneğin: + + if (ifade1) ifade2; ifade3; + + Burada if eyiminin doğru kısmı ifade2 ile birmiştir ve else anahtar sözcüğü gelmemiştir. Bu udurmda artık ifade3 if içerisinde değildir. Bu kod parçasına + dışarıdan bakıldığında iki deyim vardır: if deyimi ve bir basit deyim. + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + if (a > 0) + printf("pozitif\n"); + printf("son\n"); /* if deyiminin dışında */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bazen yeni programcılar if deyiminin doğru kısmını yanlışlıkla boş deyimle kapatırlar. Budurumda kod geçerli olduğu halde istenileni yapmaz hale + gelir. Örneğin: + + if (ifade1); /* dikkat! yanlışlıkla yerleştirilmiş boş deyim */ + ifade2 + + Burada artık if deyiminin "doğru" kısmında boş deyim vardır. Boş deyimden sonra else anahtar sözcüğü gemediği için if deyimi bitmiştir. + Dolayısıyla ifade2; if deyimi dışındadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + if (a > 0); /* dikkat! yanlışlıkla yerleştirilmiş boş deyim */ + printf("pozitif\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki kod parçasında programcı if deyiminin "doğru" kısmına birden fazla deyim yerleştirmiştir. Bunları bloklayarak tek deyim biçiminde + ifade etmesi gerekirdi: + + if (ifade1) + ifade2; + ifade3; + else + ifade4; + + Derleyici bakış açısıyla kodu incelediğimizde derleyici if parantezinden sonra blok açılmadığını gördüğünde yalnızca ifade2; deyiminin if deyiminin doğru + kısmını oluşturduğunu düşünmektedir. ifade2; deyiminden sonra else gelmediği için derleyiciye göre if deyimi sonlanmıştır. Derleyici daha sonra + else anahtar sözcüğünü gördüğünde durumu "sanki if olmadan yalnız başına else anahtar sözcüğü kullanılmış gibi" ele almaktadır. Bu durumda verilen mesaj + "error: else without if" gibi bir şey olabilir. + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir koşul doğru iken diğerlerinin doğru olma olasılığı yoksa bu koşullara "ayrık koullar" denir. Yani ayrık koşullarda koşulların yalnızca bir + tanesi doğru olabilmektedir. Örneğin: + + a > 0 + a < 0 + a == 0 + + Bu koşullar ayrıktır. Örneğin: + + a == 1 + a == 2 + a == 3 + + Bu koşullar da ayrıktır. Ancak örneğin: + + a > 0 + a > 10 + + Bu koşullar ayrık değildir. + + Ayrık koşulların ayrı if deyimleri ile ele alınması kötü bir tekniktir. Örneğin: + + if (a == 1) + printf("bir\n"); + if (a == 2) + printf("iki\n"); + if (a == 3) + printf("uc\n"); + + Burada a == 1 ise gereksiz bir biçimde diğer iki koşul da -doğrulanmayacağı halde*- gereksiz bir biçimde yapılmaktadır. a == 2 ise de a == 3 koşulu + gereksiz biçimde yapılacaktır. İşte ayrık koşullar "else if" ile ele alınmalıdır. Örneğin: + + if (a == 1) + printf("bir\n"); + else + if (a == 2) + printf("iki\n"); + else + if (a == 3) + printf("üc\n"); + + Burada dışarıdan bakıldığında tek bir if deyimi vardır. Her if diğerinin else kısmı içeisindedir. Pek çok programcı böyle else-if merdivenlerini + aşağıdaki gibi alt alta yazmaktadır: + + if (a == 1) + printf("bir\n"); + else if (a == 2) + printf("iki\n"); + else if (a == 3) + printf("uc\n"); + else if (a == 4) + printf("dort\n"); + else + printf("hicbiri\n"); + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + if (a == 1) + printf("bir\n"); + else if (a == 2) + printf("iki\n"); + else if (a == 3) + printf("uc\n"); + else if (a == 4) + printf("dort"); + else + printf("bes\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte sayının işareti yazdırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + if (a > 0) + printf("pozitif\n"); + else if (a < 0) + printf("negatif\n"); + else + printf("sifir\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C, C++, Java ve C# gibi dillerde "dangling else" denilen bir durum vardır. Eğer iki if için tek bir else varsa bu else içteki if deyimine + ilişkin kabul edilmektedir. Örneğin: + + if (ifade1) if (ifade2) ifade3; else ifade4; + + Buradaki else içteki if deyiminin else kısmıdır. Bunu daha güzel şöyle yazabiliriz: + + if (ifade1) + if (ifade2) + ifade3; + else + ifade4; + + Bazen deneyimli programcılar bile bu "dangling else" durumunda hata yapabilmektedir. Örneğin aşağıdaki gibi bir kodla karşılaşmış olalım: + + if (ifade1) + if (ifade2) + ifade3; + else + ifade4; + + Burada muhtemelen programcı ifade4; deyiminin dıştaki if deyimin else kısmında olmasını istemiştir. Çünkü hizalaması bunu düşündürmektedir. + Ancak derleyici hizalamaya bakmamaktadır. Dolayısıylla derleyici buradaki else kısmının içteki if deyiminin else kısmı olduğuna karar verir. + O halde programcı bir "bug" yapmıştır. Bu tür "dangling else" durumlarında eğer gerçekten else kısmın dıştaki if deyimine ilişkin olması isteniyorsa + bilinçli bloklama yapılmalıdır. Örneğin: + + if (ifade1) { + if (ifade2) + ifade3; + } + else + ifade4; + + Burada artık else kısmı dıştaki if deyimine ilişkindir. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 20.Ders 04/08/2022 +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + if deyiminin yalnızca yanlış kısmı bulunamaz. Bu işlem koşulun tersi oluşturularak dolaylı biçimde sağlanabilir. Örneğin: + + if (a > 0) + else { /* geçersiz */ + /* ... */ + } + + Burada aslında a > 0 değilse bir işlem yapılmak istenmiştir: + + if (a <= 0) { + /* ... */ + } + + Tabii mademki if deyiminin yalnızca else kısmı bulunamaz. O halde doğru kısmına bir boş deyim yerleştirilerek de aynı durum sağlanabilir: + + if (a > 0) + ; + else { /* geçerli */ + /* ... */ + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Bir program parçesının yinelemeli olarak çalıştırılmasını sağlayan kontrol deyimlerine "döngü (loop)" denilmektedir. C'de döngüler iki kısma yarılmaktadır: + + 1) while Döngüleri (while Loops) + 2) for Döngüleri (for loops) + + while döngüleri de kendi aralarınd "kontrolün başta yapıldığı while döngüleri" ve "kontrolün sonra yapıldığı while döngüleri" olmak üzere ikiye ayrılmaktadır. + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Kontrolün başta yapıldığı while döngülerinin genel biçimi şöyledir: + + while () + + + while anahtar sözcüğünden sonra parantez içerisinde bir ifadenin bulunması gerekir. while döngüsü bir deyim içerir. Tabii bu deyim, basit, bileşik + ya da herhangi bir deyim olabilir. Yani döngünün içerisine birden fazla deyim yerleştirilecekse bloklama yapılmalıdır. + + while döngüsü şöyle çalışmaktadır: Derleyici while parantezinin içerisindeki ifadenin sayısal değerini hesaplar. Bu değer sıfır dışı bir değerese (yani doğru ise) + döngü deyimi çalıştırılıp başa dönülür. Döngü while parantezi içerisindeki ifadenin değeri 0 olduğunda sonlanır. + + Aşağıdaki örnekte 0'dan 10'a kadar (10 dahil değil) sayılar ekrana (stdout dosyasına)yazdırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 0; + while (i < 10) { + printf("%d\n", i); + ++i; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte 10'dan başlanarak 0'a kadar (0 dahil değil) sayılar ekrana yazdırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 10; + while (i) { + printf("%d\n", i); + --i; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte klavyeden (stdin dosyasından) 'q' karakteri girilene kadar döngü devam etmektedir. Burada atama operatörüne öncelik vermek için + parantez kullanıldığına dikkat ediniz. Ayrıca getchar fonksiyonun ve stdin dosyasından okuma yapan diğer fonksiyonların tamponlu (buffered) çalıştırklarını + anımsayınız. Eğer tampon doluysa getchar yeni bir giriş istememektedir. Ancak tampon boşsa yeni bir giriş istemektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int ch; + + while ((ch = getchar()) != 'q') + putchar(ch); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Virgül operatörünün önce sol tarafının sonra sağ tarafının yapıldığını ve virgül operatörünün sağ tarafındaki ifadenin değerini ürettiğini anımsayınız. + O halde yukarıdaki döngü eşdeğer olarak aşağıdaki gibi de olabilirdi. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int ch; + + while (ch = getchar(), ch != 'q') + putchar(ch); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte 1'den n'e kadar sayıların toplamı hesplanmaktadır. (Tabii aslında bu toplam tek bir ifade ile de hesaplanabilirdi). +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i, n, total; + + printf("Bir sayi giriniz:"); + scanf("%d", &n); + + i = 1; + total = 0; + while (i <= n) { + total += i; + ++i; + } + printf("%d\n", total); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aslında scanf fonksiyonun da bir geri dönüş değeri vardır. scanf fonksiyonu başarılı bir biçimde yerleştirilen parça sayısı ile geri dönmektedir. + scanf stdin tamponunun başındaki boşluk karakterlerini (leading space) atar. Sonra format karakterlerine uygun olmayan ilk karakter gördüğünde + onu tampona geri bırakıp işlemini sonlandırır. Örneğin: + + result = scanf("%d", &val); + + Burada biz bir sayı yerine "ali" bir yazı girmiş olalım. Bu durumda scanf a karakterini tampondan aldığında bunun %d format karakterine uygun olmadığını tespit eder. + Bu a karakterini tampona geri bırakıp 0 değeri ile geri döner. Örneğin: + + result = scanf("%d%d", &a, &b); + + Burada klavyeden şunları girmiş olalım: + + 100 ali + + scanf burada yalnızca a için yerleştirme yapabilecektir. Tamponda ali kalacaktır ve b için yerleştirme yapmayacaktır. Bu durumda scanf 1 değeri ile geri dönecektir. + Bu nedenle aşağıdaki örnekte eğer biz klavyeden bir syaı girmezsek sonsuz döngü oluşacaktır: +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int val; + + while (scanf("%d", &val), val != 0) + printf("%d\n", val * val); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Yukarıdaki örnekte biz eğer klavyeden geçersiz bir karakter girildiğinde de döngüyü sonlandırmak istiyorsak scanf fonksiyonun geri dönüş değerine de + bakmalıyız. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int val; + + while (scanf("%d", &val) && val != 0) + printf("%d\n", val * val); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + while parantezi içerisindeki ifadede önek ya da sonek ++ ya da -- operatörü kullanılabilir. Aşağıdaki örnekte önek ++ operatörü kullanılmıştır. + burada artırım önce yapılıp artırılmış değer karşılaştırmaya sokulacaktır. Dolayısıyla ilk yazılacak değer 1, son yazılacak değer 9 olacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 0; + while (++i < 10) + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Eğer wehile parantezi içerisindeki ++ ya da -- operatörüğ sonek durumundaysa artırım ya da eksiltim öncelik sırasına göre yapılmakla birlikte sonraki + işleme artırılmamış ya da eksiltilmemiş değer sokulacaktır. + + Aşağıdaki örnekte ilk yazdırılacak değer 1'dir. Son yazdırılacak değer ise 10 olacaktır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 0; + while (i++ < 10) + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + n bir nesne belirtmek üzere biz n defa yinelenen bir döngüyü while ile şöyle oluşturabiliriz: + + while (n-- > 0) { + /* ... */ + } + + Bu bir kalıp olarak kullanılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int n; + + n = 3; + while (n-- > 0) + printf("ok\n"); /* üç kere yinelenecek */ + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aslında while parantezinin içerisinde yalnızca sonek ++ ya da -- operatörü varsa artırmak ya da eksiltme yapılır. Ancak kontrole nesnenin artırılmamış ya da + eksiltilmemiş değeri sokulur. Dolayısıyla n pozitif olmak üzere aşağıdaki işlevsel olarak döngüler eşdeğerdir: + + while (n-- > 0) { + /* ... */ + } + + while (n--) { + /* ... */ + } + + while (n-- != 0) { + /* ... */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Kontrolün sonda yapıldığı while döngüleri (do-while döngüleri) seyrek kullanılmaktadır. Genel biçimleri şöyledir: + + do + + while (); + + while parantezi sonundaki ';' boş deyim belirtmez. Kullanılması zorunlu olan sentaksın bir parçasını oluşturmaktadır. Döngünün do anahtar sözcüğü ile + başlatıldığına dikkat ediniz. Yine döngü içerisinde tek bir deyim vardır. Bu deyim basit, bileşik ya da herhangi bir deyim olabilir. Örneğin: + + do + ifade1; + while (ifade2); + + Örneğin: + + do { + ifade1; + ifade2; + ifade3; + } while (ifade4); + + do-while döngüsünde kontrol noktasının sonda olduğuna dikkat ediniz. Dolayısıyla döngü en az bir kez yinelenmektedir. Burada do anahtar sözcüğü olmasaydı + döngü kontrolün başta yapıldığı while döngüsü olarak ele alınırdı. Örneğin: + + { + ifade1; + ifade2; + ifade3; + } while (ifade4); + + Derleyiciye göre buarada iki deyim vardır: Bileşik deyim ve ondan bağımsız olarak kontrolün başta yapıldığı while döngüsü. Dolaysıyla buradaki ';' + boş deyim anlamına gelmektedir. + + Aşağıdaki örnekte ekrana ilk çıkacak değer 0 son çıkacak değer 9'dur. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 0; + do { + printf("%d\n", i); + ++i; + } while (i < 10); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Kontrolünm sonda yapıldığı while döngülerine yukarıda da belirttiğimiz gibi aslında oldukça seyrek gereksinim duyulmaktadır. Aşağıdaki örnekte + kullanıcıdan 'e' ya da 'h' karakteri ile bir seçim yapması istenmiştir. Eğer kullanıcı e' ya da 'h' karakterinden birini girmemişse aynı soru yinelenmiş + ve kullanıcı bu karakterlerdne birini girmeye zorlanmıştır. Buradaki döngünün kontrolün sonda yapıldığı while döngüsü olması çok daha anlamlıdır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +void clear_stdin(void) +{ + while (getchar() != '\n') + ; +} + +int main(void) +{ + int ch; + + do { + printf("(e)vet/(h)ayir?"); + ch = getchar(); + if (ch != '\n') + clear_stdin(); + } while (ch != 'e' && ch != 'h'); + + if (ch == 'e') + printf("evet\n"); + else + printf("hayir\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Programcılar yanlışlıkla kontrolün başta yapıldığı while döngülerini boş deyim ile kapatabilmektedir. Örneğin: + + while (n-- > 0); + printf("%d\n", n); + + Burada while parantezinin sonuna yerleştirilen ';' boş deyim belirtir. Dolayısıyla artık aşağıdaki printf while döngüsünün içerisinde değildir. + Eüer programcı gerçekten döngüyü boş deyim kapatmak istiyorsa (örneğin bir gecikme sağlamak istemiş olabilir) bu durumda ';' sanki bir deyim gibi + hizalanmalıdır. Çünkü kodu gören kişi bunun yanlışlıkla yapılmadığını anlayacaktır: + + while (n-- > 0) + ; + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int n; + + n = 5; + while (n-- > 0); /* dikkat! yanlışlıkla yerleştirilmiş boş deyim */ + printf("%d\n", n); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Bazen sonsuz döngülerin oluşturulması gerekebilir. Bunun için while parantezi içerisine sıfırın dışında herhangi bir sayı yerleştirilebilir. + Tabii genellikle programcılar 1 sayısını tercih ederler. Örneğin: + + while (1) { /* sonsuz döngü (infinite loop) */ + /* ... */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + 21.Ders 16/08/2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + En çok kullanılan döngüler for döngüleridir. for döngülerinin genel biçimi şöyledir: + + for ([ifade1]; [ifade2]; [ifade3]) + + + for anahtar sözcüğündne sonra parantezler içerisinde iki tane ';' bulunmak zorundadır. Bu iki ';' for döngüsünü üç kısma ayırır. for döngüsünün + bu kısımlarında "ifade (expression)" tanımna uyan herhangi ifadeler bulunabilir. for döngüsünün içeirsindeki deyim yine herhangi bir deyim olabilir. + + for döngüleri en fazla aşağıdaki gibi karşımıza çıkar: + + for (ilkdeğer; koşul; artırım) { + /* ... */ + } + + Örneğin: + + for (i = 0; i < 10; ++i) { + /* ... */ + } + + for döngüsü şöyle çalışmaktadır: Önce döngüye girişte for döngüsünün birinci kısmındaki ifade bir kez çalıştırılır. Artık bu ifade bir daha çalıştırılmaz. + İkinci kısımdak ifade ilk girişte ve her yinelemede çalıştırılmaktadır. Döngü bu ikinci kısımdaki ifade sıfır dışı bir değerde olduğu sürece yinelenmektedir. + Döngünün üçüncü kısmı döngü deyimi çalıştırıldıktan sonra başa dönerken çalıştırılmaktadırç for döngüsünün çalışması tamamen aşağıdaki ile eşdeğerdir: + + ifade1; + while (ifade2) { + + ifade3; + } + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + for (i = 0; i < 10; ++i) + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Örneğin biz artırımı ikişer ikişer de yapabiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + for (i = 0; i < 10; i += 2) + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki döngüde belli bir değerden eksiltim uygulanarak sıfıra kadar yinelenme sağlanmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + for (i = 10; i != 0; --i) + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün üçüncü kısmında ++ ya da -- operatörü kullanıldığında bunun önek mi yoksa sonek mi olduğunun hiçbir önemi yoktur. Örneğin: + + for (i = 0; i < 10; ++i) { + /* ... */ + } + + ile + + for (i = 0; i < 10; i++) { + /* ... */ + } + + eşdeğerdir. + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün üç kısmında da ifade tanımına uyan herhangi ifadeler yerleştirilebilir. Önemli olan bunun programcının amacına uygunluğudur. + Örneğin aşağıdaki gibi bir for döngüsü tamamen geçerlidir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 0; + for (printf("ifade1\n"); i < 3; printf("ifade3\n")) { + printf("deyim\n"); + ++i; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte 0'dan 6.28'e kadar sayıların sinüz değerleri 0.1 artırımla yazdırılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int main(void) +{ + double x, y; + + for (x = 0; x < 6.28; x += 0.1) { + y = sin(x); + printf("%f\t%f\n", x, y); + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte 1'den klavyeden girilen sayıya kadar sayıların toplamı hesaplanmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int n, total, i; + + printf("Bir sayi giriniz:"); + scanf("%d", &n); + + total = 0; + for (i = 1; i <= n; ++i) + total += i; + + printf("%d\n", total); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte isprime fonksiyonu bir sayının asal olup olmadığını tespit etmektedir. Eğer sayı asalsa fonksiyon 1 değeri ile, asal değilse 0 + değeri ile geri dönmektedir. Bir döngü içerisinde return deyimini kullanırsak fonksiyon sonlanır dolayısıyla döngü de sonlanmış olur. + + Aşağıdaki örnekte isprime fonksiyonundan faydalanılarak 2'den 1000'e kadar asal sayılar yan yana yazdrılmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int isprime(int val) +{ + int i; + + for (i = 2; i < val; ++i) + if (val % i == 0) + return 0; + + return 1; +} + +int main(void) +{ + int i; + + for (i = 2; i < 1000; ++i) + if (isprime(i)) + printf("%d ", i); + printf("\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Öklit teoremine göre aslında sayı asal değilse mutlaka sayının kareköküne kadar bir çarpanı vardır. Yani sayının kareköküne kadar kontrol yapmak yeterlidir. + Ayrıca çift sayılın kontrol edilmesine de gerek yoktur. Ancak 2 için özel bir durum vardır. 2 çift olmasına karşın asal bir sayıdır. + + O halde yukarıdaki isprime fonksiyonunu daha etkin çalışacak biçimde aşağıdaki gibi düzeltebiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include +#include + +int isprime(int val) +{ + int i; + double val_sqrt; + + if (val % 2 == 0) + return val == 2; + + val_sqrt = sqrt(val); + for (i = 3; i <= val_sqrt; i += 2) + if (val % i == 0) + return 0; + + return 1; +} + +int main(void) +{ + int i; + + for (i = 2; i < 1000; ++i) + if (isprime(i)) + printf("%d ", i); + printf("\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün birinci kısmındaki ifade hiç yazılmayabilir. Örneğin döngünün birinci kısmındaki ifade yukarıya alınırsa dğeişhen hiçbir şey olmaz: + + for (ifade1; ifade2; ifade3) + + + ile + + ifade1; + for(; ifade2; ifade3) + + + eşdeğerdir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + i = 0; + for (; i < 10; ++i) + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün üçüncü kısmı da yazılmayabilir. Örneğin: + + for (ifade1; ifade2; ifade3) + + + ile + + ifade1; + for (; ifade2; ) { + + ifade3; + } + + eşdeğerdir. + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + Birinci ve üçüncü kısmı olmayan for döngüleri tamamen while döngüleriyle eşdeğerdir. Örneğin: + + while (ifade) { + /* ... */ + } + + ile + + for (; ifade; ) { + /* .... */ + } + + eşdeğerdir. + + Görüldüğü gibi for döngüsü while döngüsü gibi, while döngüsü de for döngüsü gibi kullanılabilmektedir: + + ifade1; + while (ifade2) { + + ifade3; + } + + ile + + + for (ifade1; ifade2; ifade3) + + + eşdeğerdir. + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + for döngülerinin ikinci ksımındaki ifade de hiç yazılmayabilir. Bu durumda koşulun sürekli bir biçimde sağlandığı kabul edilmektedir. Örneğin: + + for (ifade1;; ifade2) { + /* .... */ + } + + Burada döngü sürekli yinelenir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + for (i = 0;; ++i) /* sonsuz döngü */ + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aslında for döngüsünün hiçbir kısmı olmayabilir. Ancak her zaman iki tane ';' parantez içerisinde bulunmak zorundadır. Böyle for döngüleri "sonsuz döngü" + oluşturmak için kullanılabilmektedir. Örneğin: + + for (;;) { + /* ... */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + double val; + + for (;;) { /* sonsuz döngü */ + printf("Bir deger giriniz:"); + scanf("%lf", &val); + printf("%f\n", val * val); + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün kısımlarında virgül operatörü kullanılarak ifadeler genişletilebilir. Örneğin biz for döngüsünün birinci kısmında birden fazla dğeişkene + virgül operatöründen faydalanarak değer atayabiliriz. Benzer biçimde üçüncü kısımda da virgül operatörü ile birden fazla işlem yapabiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i, k; + + for (i = 0, k = 100; i + k > 50; ++i, k -= 2) + printf("%d %d\n", i, k); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsü yanlışlıkla boş deyim ile kapatılabilmektedir. Bu durumda boş deyim döngü deyimi gibi ele alınır. Dolayısıyla kodun anlamı tamamen + değişir. Örneğin: + + for (i = 0; i < 10; ++i); + printf("%d\n", i); + + Burada döngü yanlışlıkla boş deyim ile kapatılmıştır. Bu durumda printf artık döngünün dışında kalmıştır. Tabii bazen döngü gerçekten boş deyim ile + kapatılmak istenebilir. Bu durumda ';' bir tab içeden yazılarak okunabilirlik artırılabilir. Örneğin: + + for (i = 0; i < 1000000; ++i) + ; +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + for (i = 0; i < 10; ++i); /* dikkat! döngü yanlışlıkla boş deyim ile kapatılmış! */ + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte ilk getchar() klavyeden okunanları tampona yerleştirir ve ilk karakterin sıra numarasıyla geri döner. Sonraki getchar çağrıları + tampondaki sıradaki karakterleri alır. Tamponun sonunda ENTER tulu nedeniyle '\n' karakteri bulunacaktır. O halde aşağıdaki kodda klavyedne girilen karakterlerin + sayısı hesaplanmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i; + + for (i = 0; getchar() != '\n'; ++i) + ; + + printf("%d\n", i); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + İç içe (nested) döngüler söz konusu olabilir. Döngü deyimleri de dışarıdan bakıldığında tek bir deyim durumundadır. Eğer bir döngünün içerisinde + başka bir döngü varsa blok açmaya hiç gerek yoktur. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int i, k; + + for (i = 0; i < 10; ++i) + for (k = 0; k < 10; ++k) + printf("(%d, %d)\n", i, k); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte klavyeden okunan n satır sayısı olmak üzere şu kalıp bastırılmaktadır: + + * + ** + *** + **** + ... + ****.... **** +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int n; + int i, k; + + printf("Bir sayi giriniz:"); + scanf("%d", &n); + + for (i = 1; i <= n; ++i) { + for (k = 0; k < i; ++k) + putchar('*'); + putchar('\n'); + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'ye C99 ile birlikte C++'ta da zaten olan "for döngüsünün birinci kısmında bildirim yapabilme" olanağı eklendi. Bu kurala göre biz döngü + değişkenini doğrudan for döngüsünün birinci kısmında bildirebiliriz. Örneğin: + + for (int i = 0; i < 10; ++i) { + /* ... */ + } + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + for (int i = 0; i < 10; ++i) + printf("%d ", i); + printf("\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün birinci kısmında bildirilen değişkene ilkdeğer vermek gerekir. Standartlarda burada bildirilen değişkenlere ilkdeğer vermemek + geçerli kabul edilse de toplamda anlamsızdır. Örneğin: + + for (int i; i < 10; ++i) { /* geçerli ama anlamsız */ + /* ... */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün birinci kısmında bildirilen değişkenler yalnızca o for döngüsünde kullanılabilir. Çünkü orada gizli bir bloğun olduğu kabul edilmektedir. + Yani örneğin: + + for (bildirim; ifade2; ifade3) + + + döngüsünü eşdeğeri şöyledir: + + { + bildirim; + for (; ifade2; ifade3) + + } + + Böylece örneğin: + + for (int i = 0; i < 10; ++i) { + /* ... */ + } + printf("%d\n", i); /* geçersiz! i burada faaliyet göstermiyor */ + + Aşağıdaki örnekte her iki for döngüsündeki i aslında o for döngülerinde kullanılan farklı yerel i'lerdir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + for (int i = 0; i < 10; ++i) + printf("%d ", i); + printf("\n"); + + for (int i = 0; i < 10; ++i) + printf("%d ", i); + printf("\n"); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki gibi bir durum da mümkündür: + + int i; + + for (int i = 0; i < 10; ++i) { + /* burada i'yi kullanırsak for döngüsünün birinci kısmında bildirilen i olur */ + } + + Aşağıdaki durum da geçerli olsa da bu tür koslardan kaçınınız: + + for (int i = 0; i < 10; ++i) + for (int i = 0; i < 10; ++i) { /* geçerli */ + /* Burada i kullanılırsa iç for döngüsündeki i anlaşılır */ + } +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + for (int i = 0; i < 10; ++i) + for (int i = 0; i < 10; ++i) /* geçerli ama kötü teknik */ + putchar('.'); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + for döngüsünün birinci kısmında birden fazla değişkenin bildirimi yapılabilir. Bu durumda bu değişkenlerin aynı türdne olması gerekir. Farklı türlerden + değişkenlerin birinci kısımda bildirilme olanağı yoktur. Örneğin: + + for (int i = 0, k = 100; i + k > 50; ++i, k -= 2) { /* geçerli + /* ... */ + } + + Ancak örneğin: + + for (int i = 0, double k = 0; ;) { /* geçersiz! böyle bir sentaks yok! */ + /* .... */ + } + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + for (int i = 0, k = 100; i + k > 50; ++i, k -= 2) + printf("%d %d\n", i, k); + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + break deyimi döngü deyimlerinin içerisinde ya da switch deyiminin içerisinde kullanılabilir. Genel biçimi şöyledir: + + break; + + Programın akışı break deyimini gördüğünde içinde bulunulan döngü deyimi sonlandırılır. Programın akışı döngü deyiminden sonraki deyim ile devam eder. + Yani break döngüyü bitirmektedir. Tabii döngü break genellikle bir koşul altında kullanılır. Örneğin: + + for (;;) { + /* .... */ + if (koşul) + break; + /* ... */ + } + + Sonsuz döngülerden çıkmak için break tek seçenektir. Ancak breajk deyimi sonsuz olmayan döngülerde de kullanılabilir. + + Bazen döngülerden çıkış koşulları çok çeşitli olabilmektedir. Bu tür durumlarda programcılar döngüyü sonsuz döngü yapıp içeriden break ile çıkmayı + tercih edebilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + double val; + + for (;;) { + printf("Bir sayi giriniz:"); + scanf("%lf", &val); + if (val == 0) + break; + printf("%f\n", val * val); + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + 22.Ders 18/08/2022 - Perşembe +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + İç içe döngülerde break deyimi yalnızca kendi döngüsünü sonlandırmaktadır. Yani break hangi döngünün içerisinde kullanılmışsa yalnızca onu + kırmaktadır. + + Aşağıdaki örnekte ENTER tuşuna basıldığında iç döngü sonraki yinelemeyle devam eder. q tuşuyna basıldığında önce iç döngüdeki break ile iç döngüden + çıkılır, sonra dış döngüdeki break ile dış döngüden de çıkılır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +void clear_stdin(void) +{ + while (getchar() != '\n') + ; +} + +int main(void) +{ + int ch; + + for (int i = 0; i < 10; ++i) { + for (int k = 0; k < 10; ++k) { + printf("(%d, %d)\n", i, k); + printf("Press ENTER to continue or q to exit:"); + ch = getchar(); + if (ch != '\n') + clear_stdin(); + if (ch == 'q') + break; + } + if (ch == 'q') + break; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aşağıdaki örnekte bir prompt çıkartılmıştır. Bu prompt eşliğinde tek karakterli komutlar istenmektedir. q tuşuna basıldığında komut yorumlayıcıdan + çıkılmaktadır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +void clear_stdin(void) +{ + while (getchar() != '\n') + ; +} + +int main(void) +{ + int ch; + + for (;;) { + printf("CSD>"); + + ch = getchar(); + if (ch != '\n') + clear_stdin(); + + if (ch == 'q') + break; + if (ch == 'r') + printf("remove command executes...\n"); + else if (ch == 'c') + printf("copy command executes...\n"); + else if (ch == 'd') + printf("dir command executes...\n"); + else if (ch == 'm') + printf("move command executes...\n"); + else + printf("invalid command: %c\n", ch); + + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Yukarıdaki örnekteki programı baştaki SPACE ve TAB karakterlerini atacak biçimde geliştirebiliriz. Aynı zamanda baştak SPACE ve TAB atıldıktan sonra ENTER + tuşuna basılırsa yeni bir prompt'a geçecek biçimde programı düzenleyebiliriz. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int get_command(void) +{ + int ch; + + while ((ch = getchar()) == ' ' || ch == '\t') + ; + + if (ch != '\n') + while (getchar() != '\n') + ; + + return ch; +} + +int main(void) +{ + int ch; + + for (;;) { + printf("CSD>"); + + ch = get_command(); + + if (ch != '\n') { + if (ch == 'q') + break; + if (ch == 'r') + printf("remove command executes...\n"); + else if (ch == 'c') + printf("copy command executes...\n"); + else if (ch == 'd') + printf("dir command executes...\n"); + else if (ch == 'm') + printf("move command executes...\n"); + else + printf("invalid command: %c\n", ch); + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + if deyiminin doğru kısmında break ya da return gibi deyimler varsa if deyimine else kısmının konulmasının bir anlamı olmaz. Örneğin: + + if (ifade1) + break; + else + ifade2; + + ile aşağıdaki eşdeğerdir: + + if (ifade1) + break; + ifade2; + + Aşağıdaki if deyimine bakınız: + + if (ifade1 && ifade2) + ifade3; + + Bu işlemin eşdeğeri şöyledir: + + if (ifade1) + if (ifade2) + ifade3; + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + continue deyimi yalnızca döngü içerisinde kullanılaabilen bir deyimdir. Genel biçimi şöyledir: + + continue; + + Programın akışı continue deyimini gördüğünde döngünün içerisindeki deyim sonlandırılıp yeni bir yinelemeye geçilmektedir. Yani break deyimi döngü + deyiminin kendisini sonlandırırken, continue deyimi döngü içerisindeki deyimin sonlandırılmasına yol açar. continue seyrek kullanılan bir deyimdir. + continue deyimi for döngüsü içerisinde kullanılırsa yeni bir yineleme oluşacağı için for döngüsünün üçüncü kısmı başa dönüşte yapılacaktır. + + Aşağıdaki örnekte i çift iken akış continue deyimini görür. Böylece döngüde yeni bir yinelemeye geçilir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + for (int i = 0; i < 10; ++i) { + if (i % 2 == 0) + continue; + printf("%d\n", i); + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + continue deyimi özelllikle döngüler içerisindeki geniş if bloklarını elimine etmek için kullanılmaktadır. Örneğin: + + for (;;) { + ch = get_command(); + if (ch != '\n') { + /* ... */ + } + } + + Bu işlemin eşdeğeri şöyle oluşturulabilir: + + for (;;) { + ch = get_command(); + if (ch == '\n') + continue; + /* ... */ + } + + Tabii bazen bir döngü içerisinde pek çok yerde akışın başa sarılması istenebilir. Bu tür durumlarda continue tasarımı oldukça sade göstermektedir. + continue deyimi de iç içe döngülerde yalnızca iç döngüyü başa sarar. + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int get_command(void) +{ + int ch; + + while ((ch = getchar()) == ' ' || ch == '\t') + ; + + if (ch != '\n') + while (getchar() != '\n') + ; + + return ch; +} + +int main(void) +{ + int ch; + + for (;;) { + printf("CSD>"); + + ch = get_command(); + if (ch == '\n') + continue; + + if (ch == 'q') + break; + if (ch == 'r') + printf("remove command executes...\n"); + else if (ch == 'c') + printf("copy command executes...\n"); + else if (ch == 'd') + printf("dir command executes...\n"); + else if (ch == 'm') + printf("move command executes...\n"); + else + printf("invalid command: %c\n", ch); + + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Yalnızca sabitlerden ve operatörlerden oluşan ifadelere "sabit ifadeleri (constant expression)" denilmektedir. Örneğin: + + 3 + 2 + 5 + 2 + 5 * 3 + 1 + 2 + 3 + 4 + + birer sabit ifadesidir. Sabit ifadelerinin derleme aşamasında derleyici tarafından sayısal değerleri hesaplanabilmektedir. Pek çok derleyici + sabit ifadelerini derleme işlemi sırasında hesaplar böylece bu işlemlerin gereksiz bir biçimde programın çalışma zamanı sırasında yapılmasını engeller. + Bu optimizasyon temasına "constant folding" denilmektedir. C'de bazı durumlarda sabit ifadelerinin kullanılması zorunludur. Örneğin: + + - Global değişkenlere verilen ilkdeğerlerin sabit ifadesi olması zorunludur. + - Global dizilerde (C99 öncesi tüm dizilerde) uzunluk sabit ifadeleriyle belirtilmek zorundadır. + - case ifadeleri sabit ifadesi olmak zorundadır. +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + switch deyimi bir ifadenin çeş,tl, sayısal değerleri için farklı birtakım işlemlerin yapılması için düşünülmüş bir deyimdir. switch deyimi olmasaydı + aslında gereksinim duyulan şeyler if deyimleriyle de yapılabilirdi. Ancak switch deyimi okunabilirliği artırmaktadır ve bazı durumlarda derleyicinin daha etkin + kod üretmesini sağlamaktadır. switch deyiminin tipik genel biçimi şöyledir: + + switch () { + case : + /* ... */ + [break;] + case : + /* ... */ + [break;] + case : + /* ... */ + [break;] + /* ... */ + [default: + /* .... */ + ] + } + + switch anahttar sözcüğünden sonra parantez içerisinde bir ifade bulunmak zorundadır. switch deyimi tipik olarak case bölümlerinden oluşur. + case anahtar sözcüğünden sonra sabit ifadesi bulunması gerekir. case bölümleri tipik olarak break deyimleriyle sonlandırılmaktadır. Ancak bu zorunlu + değildir. switch deyiminin isteğe bağlı bir default bölümü olabilir. + + switch deyimi şöyle çalışmaktadır: Önce switch parantezi içerisindeki ifadenin sayısal değeri hesaplanır. Sonra bu değere tam eşit olan case bölümü + araştırılır. Eğer bu değere eşit olan bir case bölümü varsa akış o bölüme aktarılır. O bölümdeki deyimler çalıştırılır. break deyimi döngülerde olduğu gibi + switch deyiminin de sonlandırılmasına yol açmaktadır. Eğer switch parantezi içerisindeki ifadenin değeri ile eşit olan bir case bölümü yoksa ancak + default bölüm varsa akış default bölüme aktarılmaktadır. default bölüm olmak zorunda değildir. Eğer switch parantezi içerisindeki ifadenin sayısal değerine + eşit olan bir case bölümü yoksa ve default bölüm de yoksa akış switch deyimiminin dışındaki ilk deyimle devam eder. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int val; + + printf("Bir deger giriniz:"); + scanf("%d", &val); + + switch (val) { + case 1: + printf("bir\n"); + break; + case 2: + printf("iki\n"); + break; + case 3: + printf("üc\n"); + break; + case 4: + printf("dort\n"); + break; + case 5: + printf("bes\n"); + break; + default: + printf("hicbiri\n"); + break; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aynı switch deyiminde aynı değerde birden fazla case bölümü olamaz. case anahtar sözcüğünün yanındaki ifadenin sabit ifadesi olması zorunludur. + Bu sayede derleyici case ifadelerinin yinelenmediğini derleme aşamasında tespit edebilmektedir. + + case bölümlerinin tamsayı türlerine ilişkin sabit ifadesi olması zorunludur. Yani case anahtar sözcüğünün yanında float, double, long double gibi + noktalı sayılar bulunamaz. Benzer biçimde switch parantezi içerisindeki ifade de tamsayı türlerine ilişkin olmak zorundadır. + + switch deyiminde case bölümlerinin sıralı (sorted) olması ya da default bölümün sonda olması bir zorunluluk değildir. Ancak case bölümlerinin sıralı olması + ve default bölümün sonda olması okunabilirliği artırabilmektedir. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int val; + + printf("Bir deger giriniz:"); + scanf("%d", &val); + + switch (val) { + case 5: + printf("bes\n"); + break; + case 1: + printf("bir\n"); + break; + default: + printf("hicbiri\n"); + break; + case 2: + printf("iki\n"); + break; + + case 4: + printf("dort\n"); + break; + case 3: + printf("üc\n"); + break; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + 23. Ders 23/08/2022 - Salı +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + C'de switch deyiminde "aşağıda doğru düşme (fall through)" denilen bir özellik vardır. Akış bir case bölümüne devredildikten sonra o case bölümünün + sonunda break yok ise aşağıda doğru akmaya devam eder. İlk break görüldüğünde switch'ten çıkılır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int val; + + printf("Bir deger giriniz:"); + scanf("%d", &val); + + switch (val) { + case 1: + printf("bir\n"); // fallthrough + case 2: + printf("iki\n"); + break; + case 3: + printf("üc\n"); // fallthrough + case 4: + printf("dort\n"); + break; + case 5: + printf("bes\n"); + break; + default: + printf("hicbiri\n"); + break; + + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Frklı case değerleri için aynı şeylerin yapılması isteniyorsa bunun en pratik yöntemi aşağıdaki gibidir: + + case 1: + case 2: + ... + break; + + Bunun daha pratik bir yolu yoktur. Burada switch ifadesi 1 ise fallthrouh nedeniyle zaten 2 ile aynı kod çalıştırılacaktır. + + Aşağıdaki örnekte komut satırı uygulaması swithc deyimi ile yapılmıştır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int get_command(void) +{ + int ch; + + while ((ch = getchar()) == ' ' || ch == '\t') + ; + + if (ch != '\n') + while (getchar() != '\n') + ; + + return ch; +} + +int main(void) +{ + int ch; + + for (;;) { + printf("CSD>"); + + ch = get_command(); + if (ch == '\n') + continue; + if (ch == 'q') + break; + + switch (ch) { + case 'e': + case 'r': + printf("remove command executes...\n"); + break; + case 'c': + printf("copy command executes...\n"); + break; + case 'd': + printf("dir command executes...\n"); + break; + case 'm': + printf("move command executes...\n"); + break; + default: + printf("invalid command: %c\n", ch); + break; + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + switch deyiminde hiçbir henüz case ya da default bölümlerinden önce kodlar yerleştirilebilir. Bildirimler yapılabilir. + Burada yapılan bildirimler geçerlidir. Ancak buraya yerleştirilen kodlar çalıştırılmazlar. Dolayısıyla buradaki + tanımlamada nesneye ilkdeğer veriliyor olsa bile bu ilkdeğerler işleme sokulmamaktadır. Örneğin: + + switch (expr) { + int i = 4; + f(i); + //... + default: + printf("%d\n", i); + } + + Burada i değişkeninin bildirimi geçerlidir. Ancak akış hiçbir zaman o noktaya gelemeyeceği için verilen ilkdeğer + nesneye yerleştirilemeyecektir. f fonksiyonu da hiçbir zaman çağrılmayacaktır. Bu tür durumlarda bu işlemlerin switch + yukarısına alınması gerekir. Örneğin: + + int i = 4; + f(i); + switch (expr) {} + // ... + default: + printf("%d\n", i); + } + + Ayrıca case ya da default bölümlerinde bildirim yapılamamaktadır. Örneğin: + + switch (expr) { + case 1: + int a; // geçersiz! + //... + } + + Yalnızca bir case ya da default bölümde değişkenin kullanılmasını istiyorsanız. Orada ayrı bir blok oluşturmalısınız. + Örneğin: + + switch (expr) { + case 1: + { + int a; // geçerli + //... + } + //... + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + switch deyimlerinde case bölümlerinin çok uzatılması okunabilirliği bozmaktadır. Bu nedenle case bölümlerinde uzun işlemler yapılacaksa + o işlemleri yapan fonksiyonlar tanımlanmalı ve case bölümlerinde bu fonksiyonlar çağrılmalıdır. +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int get_command(void) +{ + int ch; + + while ((ch = getchar()) == ' ' || ch == '\t') + ; + + if (ch != '\n') + while (getchar() != '\n') + ; + + return ch; +} + +void erase_command(void) +{ + printf("erase command executes...\n"); +} + +void copy_command(void) +{ + printf("copy command executes...\n"); +} + +void dir_command(void) +{ + printf("dircommand executes...\n"); +} + +void move_command(void) +{ + printf("move command executes...\n"); +} + +int main(void) +{ + int ch; + + for (;;) { + printf("CSD>"); + + ch = get_command(); + if (ch == '\n') + continue; + if (ch == 'q') + break; + + switch (ch) { + case 'e': + case 'r': + erase_command(); + break; + case 'c': + copy_command(); + break; + case 'd': + dir_command(); + break; + case 'm': + move_command(); + break; + default: + printf("invalid command: %c\n", ch); + break; + } + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + C'de case bölümlerinin hemen switch bloğüunun içerisinde olması zorunlu değildir. Bir case bölümü başka bir case bölümünün içerisinde bir yerlerde + olabilir. C# ve Java gibi dillerde böyle bir özellik yoktur. Örneğin: + + switch (ifade) { + case 1: + ... + ... + if (falanca) { + ... + case 2: + ..... + ..... + break; + } + break; + ... + } + +----------------------------------------------------------------------------------------------------------------------*/ + +#include + +int main(void) +{ + int a, b; + + printf("Bir deger giriniz:"); + scanf("%d", &a); + + switch (a) { + case 1: + printf("bir\n"); + printf("Bir deger daha giriniz:"); + scanf("%d", &b); + if (b > 0) { + case 2: + printf("islemler devam ediyor..\n"); + break; + } + break; + case 3: + printf("uc\n"); + break; + default: + printf("default\n"); + break; + } + + return 0; +} + +/*---------------------------------------------------------------------------------------------------------------------- + Aslında switch içerisind tek bir deyim varsa switch deyimi de bloklanmak zorunda değildir. Örneğin: + + switch (ifade) + case 1: + printf("bir\n"); printf("iki\n"); + + Burada yalnızca ilk printf printf çağrısı switch deyiminin içerisindedir. İkinci printf switch içerisinde değildir. Standartlarda case bir deyim olarak + şöyle ifade edilmiştir: + + case : deyim + + switch (ifade) + case 1: { + printf("bir\n"); printf("iki\n"); + } + + Burada switch içerisinde yine tek bir deyim vardır. O deyim de bileşik deyimdir. Dolayısıyla iki printf çağrısı da switch içerisinddir. Örneğin: + + switch (a) + case 1: + printf("bir\n"); + break; /* geçersiz! */ + + Burada switch içerisinde yalnızca printf çağrısı vardır. break deyimi switch içerisinde değildir. switch ve döngü içerisinde olmayan break deyimleri + geçersizdir. + + C standartlarında aslında switch deyiminin genel biçimi şöyle verilmiştir: + + switch () + + + Yani bu genel biçime göre aslında switch deyimi case deyimini içermeyebilir. Ancak case içermeyen switch deyimleri geçerli olsa da anlamlı değildir. Örneğin: + + switch (ifade) { /* geçerli ama anlamlı değil */ + ifade1; + ifade2; + } + + İşte switch deyiminde eğer bloklama yapılmazsa onun içeriinde tek deyimin olduğu kabul edilmektedir. Örneğin: + + switch (ifade) + ifade1; ifade2; + + Burada switch içerisinde case bölümü ya da default bölümü olmadığına göre switch deyimi anlmasızdır. Ancak gerçerlidir. Burada ifade2 switch deyimi + dışındadır. + + Bir döngü içerisinde bir switch deyimi olsun. Bu switch deyimi içerisinde break kullandığımızda biz switch deyimini sonlandırmış oluruz. Döngü deyimini + sonlandırmış olmayız. Ancak continue deyimi switch için anlamlı olmadığına göre döngü içerisindeki switch deyiminde continue kullanıldığında + switch deyimi de sonlanarak sonraki yinelemeye geçilir. Örneğin: + + for (;;) { + switch (ifade) { + case 1: + ... + if (falanca) + continue; /* bu continue döngü başına dönüşü sağlar + ... + } + .... + } + +----------------------------------------------------------------------------------------------------------------------*/ + +/*---------------------------------------------------------------------------------------------------------------------- + goto deyimi programın akışını koşulsuz biçimde belli bir noktaya aktarmak için kullanılan bir kontrol deyimidir. Genel biçimi şöyledir: + + goto