├── dif_02.c ├── dif_03.c ├── dif_04.c ├── dif_05.c ├── dif_06.c ├── dif_07.c ├── farklar.md ├── diff.md └── c_const.md /dif_02.c: -------------------------------------------------------------------------------- 1 | struct Nec {}; //invalid in C valid in C++ (empty class) 2 | -------------------------------------------------------------------------------- /dif_03.c: -------------------------------------------------------------------------------- 1 | void foo(); 2 | 3 | int main() 4 | { 5 | foo(1, 2); //valid in C invalid in C++ 6 | } 7 | -------------------------------------------------------------------------------- /dif_04.c: -------------------------------------------------------------------------------- 1 | int main(void) 2 | { 3 | const int size = 5; 4 | 5 | int a[size] = { 0 }; //invalid in C valid in C++ 6 | } 7 | -------------------------------------------------------------------------------- /dif_05.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | for (int i = 0; i < 10; ++i) { 6 | int i = 333; //valid in C invalid in C++ 7 | //local i hides loop variable i 8 | printf("%d ", i); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /dif_06.c: -------------------------------------------------------------------------------- 1 | enum Color { 2 | White, 3 | Gray, 4 | Blue, 5 | Black 6 | }; 7 | 8 | 9 | int main(void) 10 | { 11 | //Color c1 = White; //invalid in C - valid in C++ 12 | enum Color c2 = Gray; //valid both in C and in C++ 13 | } 14 | -------------------------------------------------------------------------------- /dif_07.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main(void) 4 | { 5 | printf("sizeof(char) = %zu\n", sizeof(char)); 6 | printf("sizeof(int) = %zu\n", sizeof(int)); 7 | printf("sizeof('A') = %zu\n", sizeof('A')); 8 | 9 | // 'A' is a character constant in C of type int 10 | // 'A' is a character literal in C++ of type char 11 | } 12 | -------------------------------------------------------------------------------- /farklar.md: -------------------------------------------------------------------------------- 1 | C++ dilinde 2 | + C dilinde olan _"default function declaration" (implicit function declarations)"_ yok. (Aslında C99 standardı ile C dilinden de kaldırıldı). Tüm isimler _(identifiers)_ bildirilmeli. 3 | + C++ dilinde _old-style function definition_ (eski tip fonksiyon tanımı) geçerli değil. 4 | + C dilinde olan _"implicit int"_ _(örtülü int)_ kuralı geçerli değil. 5 | + string literalleri _const char_ diziler. (C dilinde string literalleri _char_ diziler) 6 | + karakter sabitlerinin _(character literals)_ türü _char_ (C dilinde int). 7 | + Karşılaştırma operatörleri ve lojik operatörler _bool_ türden _true_ ya da _false_ değerlerini üretiyorlar. (C dilinde bu operatörler işaretli _int_ türden _1_ ya da _0_ değerlerini üretiyorlar.) 8 | + _const_ nesnelere ilk değer vermek zorunlu (C dilinde zorunlu değil). 9 | + global _const_ nesneler iç bağlantıda _(internal linkage)_ (C dilinde dış bağlantıda) 10 | + ilk değerini sabit ifadesi _(constant expression)_ ile almış _const_ nesnelerin oluşturduğu ifadeler sabit ifadesi olarak ele alınıyor. 11 | + _char*_ türünden değişkenlere string literalleri ile ilk değer veremeyiz. _char*_ türünden değişkenlere string literallerini atayamayız. 12 | + _T_ ve _U_ farklı türler olmak üzere _T*_ türünden _U*_ türüne örtülü dönüşüm yok. (istisnalar ileride) 13 | + Aritmetik türler ile adres türleri arasında otomatik dönüşüm yok. (C dilinde var) 14 | + _T_ _void_ olmayan bir tür olmak üzere _void*_ türünden _T*_ türüne örtülü dönüşüm yok. (C dilinde var) 15 | + _structure, union, enum, class (user defined types)_ etiketleri _(tags)_ doğrudan türü niteleyen isimler olarak kullanılabiliyor (C dilinde türü nitelerken _struct_, _union_, _enum_ anahtar sözcüklerinin etiket _(tag)_ ile birlikte kullanılması zorunlu. 16 | + Programcı tarafından oluşturulan türlerin _(user defined types)_ bir öğeye sahip olması gerekmiyor. _(empty classes)_ 17 | + Fonksiyon tanımlarında fonksiyon parametrelerine isim vermek zorunlu değil (C dilinde zorunlu). C'de de yeni standartlarda bu durum değişiyor. 18 | + ```char str[4] = "mert"``` dizi boyutu ile ilk değer veren yazıdaki karakter sayısı eşit ise tanım _(definition)_ geçerli değil (C dilinde geçerli). 19 | + C++ dilinde aritmetik türlerden _enum_ türlerine örtülü dönüşüm yok (C dilinde var). 20 | + Farklı _enum_ türleri arasında örtülü dönüşüm yok (C dilinde var). 21 | + _auto_ anahtar sözcüğünün _C_ dilindeki anlamı geçerli değil. _auto_ farklı anlamlarda kullanılıyor _(type deduction)_. 22 | + _C99_ standartları ile C diline eklenen bazı araçlar C++ dilinde yok. _(restrict pointers, compound literals, flexible array members, designated initializers...)_ 23 | + C dilinde _R Value expression_ olan bazı ifadeler _L value expression_. (_value category_ kurallarında iki dil arasında kural farklılıkları var.) Örneğin
24 | ön ek ++ ve ön ek -- operatörlerinin oluşturduğu ifadeler C'de R value C++ dilinde L value. 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /diff.md: -------------------------------------------------------------------------------- 1 | # C ve C++ arasındaki Farklılıklar 2 | 3 | ## Bildirimlere `(Declarations)` İlişkin Farklılıklar 4 | 5 | C89'da fonksiyonların örtülü *(implicit)* bildirimi geçerli. C99 standartları ile bu durum geçersiz hale getirildi. 6 | Ancak C derleyicilerinin hemen hepsi geçmişe doğru uyumluluğu korumak amacıyla bu duruma izin veriyor. C++'da örtülü işlev bildirimi geçerli değil. 7 | 8 | ``` 9 | void func() 10 | { 11 | foo(); 12 | //C89'da geçerli implicit olarak 13 | //int foo(); 14 | //bildirimi yapılmış kabul ediliyor 15 | //C99'da geçersiz C++'da geçersiz 16 | } 17 | ``` 18 | 19 | Klasik C döneminden gelen "eski stil fonksiyon tanımlamaları" *(old style function definitions)* C'de geçerliliğini koruyor. C++ dilinde geçerli değil. 20 | 21 | ``` 22 | sum(a, b, c) 23 | double a, b, c; 24 | { 25 | return a + b + c; 26 | } 27 | ``` 28 | 29 | Yukarıdaki fonksiyon tanımı C'de geçerli, C++ dilinde geçersiz. 30 | 31 | 32 | 33 | **C89'da *implicit int* (gizli int - örtülü int - kapalı int) kuralı geçerli.** 34 | 35 | C99 standartları ile bu durum geçersiz kılındı. C++ dilinde *"implicit int"* geçerli değil. C99/11 derleyicileri, geçmişe doğru uyumluluğu korumak için bir bulgu iletisi *(diagnostic)* vererek kodu derleyebilir. Derleyicinizin *switch*'lerini inceleyiniz. 36 | 37 | ``` 38 | foo(); //C++'da geçersiz 39 | 40 | func() //C++'da geçersiz 41 | { 42 | const x = 1; //C++'da geçersiz 43 | static y = 2; //C++'da geçersiz 44 | /// 45 | return 1; 46 | } 47 | ``` 48 | 49 | C89 standartlarına göre yukarıdaki kodda 50 | - bildirilen *foo* işlevinin geri dönüş değeri türü *int* 51 | - tanımlanan *func* işlevinin geri dönüş değeri türü *int* 52 | - *func* işlevi içinde tanımlanan *x* ve *y* değişkenlerinin türü *int* 53 | 54 | **C dilinde aşağıdaki iki bildirim arasında önemli bir fark var:** 55 | 56 | ``` 57 | void f1(); //f1 işlevinin parametre değişkenleri hakkında bir bilgi verilmiyor 58 | void f2(void); //f2 işlevinin parametre değişkeni yok 59 | ``` 60 | 61 | C'de bu iki bildirimin arasındaki farklılık geçmişe doğru uyumluluğu korumayı amaçlıyor. C++ dilinde bu iki bildirim eşdeğer. (İki bildirimin arasında bir anlam farkı yok) Aşağıdaki kodu hem C dilinde hem de C++ dilinde derleyerek derleyicinizin verdiği bulgu iletilerini *(diagnostic messages)* inceleyin: 62 | 63 | ``` 64 | void func(void); 65 | void foo(); 66 | 67 | int main() 68 | { 69 | func(1, 2, 4); 70 | foo(1, 2, 4); 71 | } 72 | ``` 73 | 74 | C89'da for döngülerinin parantezi içindeki birinci kısımda bildirim yapılamıyor. C99 standartları ile böyle bildirimlere olanak sağlandı. 75 | C++ dilinde ise for döngülerinin birinci kısmında bildirim yapılabiliyor. Ancak burada bildirilen isimlerin kapsamlarına *(scope)* ilişkin C ile C++ dilleri arasında önemli bir kural farklılığı var. Aşağıdaki kodu inceleyin: 76 | 77 | ``` 78 | #include 79 | 80 | int main() 81 | { 82 | for (int i = 0; i < 10; ++i) { 83 | int i = 876; //c++'da geçersiz 84 | printf("%d ", i); //876 85 | } 86 | 87 | return 0; 88 | } 89 | ``` 90 | 91 | Aşağıdaki bildirim C'de geçerli ve *str* dizisinde tutulan yazının sonunda *null* karakter yok: 92 | 93 | ``` 94 | char str[5] = "Ahmet"; 95 | ``` 96 | 97 | Bu bildirim C++ dilinde geçersiz. 98 | 99 | ***const* nesne bildirimlerine ilişkin farklılıklar:** 100 | 101 | + C'de *const* nesnelere ilk değer vermek zorunlu değil. C++ dilinde zorunlu. 102 | 103 | + C'de sabit ifadeleri ile ilk değerini almış *const* nesneler sabit ifadesi gereken yerlerde kullanılamıyorlar. C++'da kullanılabiliyorlar. 104 | 105 | + C'de global *const* nesneler varsayılan biçimde dış bağlantıya *(external linkage*) aitler. C++ dilinde ise global *const* nesneler varsayılan biçimde iç bağlantıya *(internal linkage)* aitler. C++ dilinde global *const* nesneleri dış bağlantıya sokmak için bunların tanımında *"extern"* anahtar sözcüğünü kullanmak gerekiyor. 106 | 107 | C'de bir *enum*, *union* ve *struct* anahtar sözcükleri ile oluşturulan türler söz konusu olduğunda bildirimde kullanılan etiket *(tag)* değişken bildirimlerinde doğrudan kullanılamıyor: 108 | 109 | ``` 110 | struct Point { 111 | int x, y; 112 | }; 113 | 114 | union Data { 115 | char c1; 116 | char c2; 117 | int x; 118 | } 119 | 120 | enum Color {Red, Blue, Black}; 121 | 122 | Point p = {1, 3}; // C'de geçersiz C++'da geçerli 123 | Color c = Black; // C'de geçersiz C++'da geçerli 124 | Data data = {'A'}; // C'de geçersiz C++'da geçerli 125 | ``` 126 | *const* ve *static* anahtar sözcüklerinin pointer parametre değişkenleriyle kullanımı C'de geçerli C++'ta geçersiz. Aşağıdaki bildirimler C'de geçerli C++'da geçersiz 127 | 128 | ``` 129 | int foo(int p[const]); // int foo(int *const p) 130 | int bar(char s[static 5]); // uzunluğu en az 5 olan bir yazı adresi bekleniyor. 131 | ``` 132 | C'de kullanımdan düşmüş olan *auto* anahtar sözcüğünün C++ dilindeki anlamı farklı. Aşağıdaki kod C'de geçerli ancak C++'da geçersiz: 133 | 134 | ``` 135 | void func() 136 | { 137 | auto int x; //C'de geçerli C++'da geçersiz 138 | auto int y = 10; //C'de geçerli C++'da geçersiz 139 | //... 140 | } 141 | ``` 142 | 143 | *auto* C++ dilinin en sık kullanılan anahtar sözcüklerinden biri. auto anahtar sözcüğü "Tür çıkarımı" *(type deduction)* denilen araç seti ile ilgili. Tür çıkarımı C++ dilinin en önemli araçlarından biri. 144 | 145 | C'de dosya kapsamında *(file scope)* bir nesne birden fazla kez tanımlanabilir. Bu durumda tanımlanan aynı nesnelerdir. (İsimleri aynı olan farklı nesneler değil.) C'de bu duruma *"tentative definition"* denmektedir. C++'da bu durum geçerli değildir. Aşağıdaki kod C'de geçerli C++ dilinde geçersizdir: 146 | 147 | ``` 148 | int a, a, a; 149 | int a; 150 | int a; 151 | ``` 152 | 153 | C'de tür eş isimlerinin ve yapı/birlik/enum etiket *(tag)* isimlerinin aynı olması durumu geçerlidir. C++'da bu durum geçersizdir. Aşağıdaki kod C'de geçerli C++'da geçersizdir: 154 | 155 | ``` 156 | struct S { 157 | int x; 158 | }; 159 | 160 | typedef struct U { 161 | int y; 162 | } S; 163 | ``` 164 | C'de işlev bildirimlerinde, tanımlamalarında, *sizeof* operatörünün parantezi içinde, tür dönüştürme operatörünün içinde yeni tür bildirimleri yapılabilir. Böyle bildirimlerin hepsi C++ dilinde geçersizdir. Aşağıdaki kod *(Andrey Tarasevich)* C'de geçerli C++'da geçersizdir: 165 | 166 | ``` 167 | enum { G, H, I } foo() 168 | { 169 | int a = sizeof(enum E { A, B, C }) + (enum X { D, E, F }) 0; 170 | enum E e = a + B + F; 171 | return e == 0 ? G : H; 172 | } 173 | ``` 174 | C'de aynı çeviri biriminde *(translation unit)* tamamlanmamış bir türden *(incomplete type)* bir nesnenin önce tanımlanması daha sonra bu türün bildiriminin yapılması şartıyla geçerlidir. 175 | 176 | ``` 177 | struct S s; // struct S türü henüz "tamamlanmamis" 178 | struct S { int i; }; 179 | ``` 180 | 181 | Tür Dönüşümlerine İlişkin Farklılıklar 182 | 183 | + C dilinde farklı türden adresler arasında otomatik tür dönüşümü var, C++ dilinde yok. 184 | + C'de adres türleri ile tamsayı /gerçek sayı türleri arasında otomatik tür dönüşümü var, C++ dilinde yok. 185 | 186 | ``` 187 | void foo() 188 | { 189 | int x = 10; 190 | double dval = 1.2; 191 | 192 | int *ptr = x; //C'de geçerli C++'da geçersiz 193 | ptr = &dval; //C'de geçerli C++'da geçersiz 194 | int y = ptr; //C'de geçerli C++'da geçersiz 195 | } 196 | ``` 197 | Böyle otomatik tür dönüşümlerine izin vermek şüphsiz C dilinde de doğru değil. Ancak `C` derleyicisinin kontrol yükümlülüğü yok. Böyle otomatik tür dönüşümleri `C` derleyicilerinin hemen hepsinde lojik kontrole takılır ve tipik olarak bir uyarı mesajı alırız. 198 | 199 | `T` herhangi bir tür olmak üzere, `C`'de `(const T *)` türünden `(T *)` türüne otomatik tür dönüşümü var. `C++` dilinde yok. 200 | 201 | ``` 202 | void foo(void) 203 | { 204 | const int x = 10; 205 | int y = 20; 206 | 207 | int *ptr = &x; //C'de geçerli C++'da geçersiz 208 | const int *p = &y; 209 | 210 | ptr = p; //C'de geçerli C++'da geçersiz 211 | } 212 | ``` 213 | `C`'de `(void *)` türünden diğer adres türlerine otomatik tür dönüşümü var. `C++` dilinde yok. 214 | 215 | ``` 216 | #include 217 | 218 | void foo(size_t n) 219 | { 220 | int *p = malloc(n * sizeof(int)); //C'de geçerli C++'da geçersiz 221 | //... 222 | } 223 | ``` 224 | `C`'de diğer aritmetik türlerden `enum` türlerine otomatik tür dönüşümü var. `C++`'ta yok. 225 | `C`'de farklı `enum` türleri arasında otomatik tür dönüşümü var. `C++`'ta yok. 226 | 227 | ``` 228 | enum Pos {Off, On, Hold, StandBy}; 229 | enum Color { Red, Green, Black}; 230 | 231 | void foo(int val) 232 | { 233 | enum Pos pos = val; 234 | enum Color c1 = pos; //C'de geçerli C++'da geçersiz 235 | enum Color c2 = Off; //C'de geçerli C++'da geçersiz 236 | int x = pos; //C'de de C++'da da geçerli 237 | } 238 | ``` 239 | 240 | -------------------------------------------------------------------------------- /c_const.md: -------------------------------------------------------------------------------- 1 | # Gösterici (pointer) değişkenlerin bildiriminde const anahtar sözcüğünün kullanımı 2 | 3 | C dilinde **const** anahtar sözcüğünün bir pointer değişkenin tanımında kullanıldığı yere bağlı olarak verdiği anlam değişmektedir. 4 | 5 | ## const anahtar sözcüğünün * (asterisk) atomundan sonra kullanılması 6 | 7 | Aşağıdaki koda bakalım: 8 | 9 | ``` 10 | int x = 10; 11 | int *const ptr = &x; 12 | ``` 13 | 14 | İngilizcede bu şekilde tanımlanmış gösterici değişkenlere __"const pointer"__ denmektedir. 15 | C dilinde yaygın olarak kullanılmasa da _C++_'ta böyle pointer değişkenler için _"top level const"_ terimi de kullanılmaktadır. Bazı programcılar böyle gösterici değişkenler için _"right const"_ terimini kullanırlar (bildirimde _const_ anahtar sözcüğü asterisk atomunun sağına yazıldığı için). 16 | Bu tür gösterici değişkenler için kurs boyunca _"const gösterici"_ ya da bu durumu özellikle vurgulamak için _"kendisi const gösterici"_ terimini kullanacağım. 17 | 18 | Buradaki belirtilen _ptr_ değişkeninin değerinin hayatı boyunca değişmeyecek olduğudur. Başka bir deyişle, _ptr_ değişkeni hayatı boyunca _x_ değişkenini gösterecektir. Bu durumda derleyici, _ptr_ değişkeninin değerini değiştirmeye yönelik kodları geçersiz kabul etmekle yükümlüdür. Yani (yanlışlıkla) _ptr_ değişkenine başka bir nesnenin adresini atarsak geçersiz kod (sentaks hatası) oluşur. Buradaki taahhüdümüz (sözümüz) _ptr_ yoluyla erişilecek nesnenin, yani gösterilen nesnenin _(pointee)_ yani 19 | 20 | ``` 21 | *ptr 22 | ``` 23 | 24 | ifadesine karşılık gelen nesnenin değerinin değerinin değiştirilmeyeceği değildir, _ptr_'nin kendi değerinin değiştirilemeyeceğidir. 25 | _*ptr_ nesnesine **(pointee)** yani _ptr_'nin gösterdiği nesneye atama yapılabilir. Bu konuda bir söz verilmemiştir. Aşağıdaki koda bakalım: 26 | 27 | ``` 28 | int main() 29 | { 30 | int x = 10; 31 | int y = 20; 32 | 33 | int *const ptr = &x; //ptr is a const pointer 34 | //ptr = &y; //gecersiz 35 | *ptr = 90; //geçerli 36 | //... 37 | } 38 | ``` 39 | 40 | 41 | ## Peki, neden kendisi const pointer değişkenleri kullanırız? 42 | 43 | * Kodun mantıksal yapısı gösterici değişkenin değerinin hiç değişmemesini gerektirmektedir. Gösterici değişkene başka bir değerin atanmasının (yani başka bir nesnenin adresinin atanmasının) mantıksal bir hata olduğunu düşünelim: 44 | 45 | ``` 46 | int g1 = 10; 47 | int g2 = 20; 48 | 49 | void func() 50 | { 51 | int *ptr = &g1; //ptr'nin kendi hayatı boyunca g nesnesini göstermesi gerekiyor olsun. 52 | //... 53 | ptr = &g2; //Bu atama yanlışlıkla yapılmış olsa da geçerli 54 | } 55 | ``` 56 | 57 | *ptr*'nin hayatı boyunca *g1* değişkenini göstermesi gereksin. Eğer gösterici değişkeni yukarıdaki gibi tanımlarsak ve bu gösterici değişkene başka bir nesnenin adresini atarsak, bu atama mantılsal bir hata olmasına karşın kod geçerlidir. Böyle durumlarda *kendisi const pointer değişkenin* kullanılması kodlama hatası yapma riskini düşürmektedir. 58 | 59 | * Kodu okuyana *ptr*'nin değerinin değişmeyeceğini bildirmek birçok durumda kodun okunmasını kolaylaştırabilir. 60 | 61 | * Bazı durumlarda derleyicinin *ptr* değişkeninin değerinin değişmeyeceğini bilemesi derleyiciye daha iyi bir optimizasyon *(eniyileme)* olanağı vermektedir. 62 | 63 | ## const anahtar sözcüğünün bildirimde _* (asterisk)_ atomundan önce kullanılması 64 | 65 | Bu kez aşağıdaki koda bakalım: 66 | ``` 67 | int x = 10; 68 | const int *ptr = &x; 69 | ``` 70 | _ptr_ değişkeninin bildiriminde **const** anahtar sözcüğü _'*'_ _(asterisk)_ atomundan önce kullanılıyor. İngilizcede bu şekilde tanımlanmış gösterici değişkenlere _"pointer to const"_ denmektedir. Örneğin yukarıdaki koddaki _ptr_ için _"ptr is a pointer to const int"_ diyebiliriz. C dilinde yaygın olarak kullanılmasa da _C++_ dilinde böyle gösterici değişkenler için _"low level const"_ terimi de kullanılmaktadır. Bu tür gösterici değişkenler için kurs boyunca "const nesne göstericisi" ya da bu durumu özellikle vurgulamak için _"gösterdiği nesne const olan gösterici"_ terimlerini kullanacağım. Bazı programcılar böyle gösterici değişkenler için _"left const"_ terimini kullanırlar (bildirimde _const_ anahtar sözcüğü asterisk atomunun sağına yazıldığı için). 71 | 72 | Yukarıdaki tanımlamada tür belirten _int_ anahtar sözcüğü ile __const__ anahtar sözcüğünün bildirimde yer değiştirmesi bir anlam farklılığı oluşturmaz. Yani 73 | 74 | ``` 75 | const int *ptr = &x; 76 | ``` 77 | ile 78 | ``` 79 | int const *ptr = &x; 80 | ``` 81 | 82 | tanımlamaları tamamen aynı anlamdadır. Hangi biçimi tercih ettiğimiz kullandığımız kodlama konvensiyonları _(coding conventions)_ ile ilgilidir. Burada belirtilen, _ptr_ değişkeninin gösterdiği (ve ileride gösterebileceği) nesneleri salt okuma/erişim _(access)_ amaçlı gösteriyor olmasıdır. Başka bir deyişle, _*ptr_ ifadesine karşılık gelen nesneyi, _ptr_ yoluyla (aracılığı ile) değiştirmeme sözü vermiş oluyoruz. 83 | Bu durumda derleyici, _*ptr_ nesnesinin değerini değiştirmeye yönelik kodları geçersiz kabul etmekle yükümlüdür. Yani (yanlışlıkla) _*ptr_ yoluyla _ptr_'nin gösterdiği nesneye *(pointee)* bir atama yaparsak geçersiz kod (sentaks hatası) oluşur. Buradaki taahhüdümüz (sözümüz) _ptr_'nin değerini değiştirmemek değildir. _ptr_'nin değerini değiştirmemiz yani ona yeni bir değer atmamamız geçerlidir. Aşağıdaki koda bakalım: 84 | 85 | ``` 86 | int main() 87 | { 88 | int x = 10; 89 | int y = 20; 90 | 91 | const int *ptr = &x; //ptr is a pointer to const int 92 | //int const *ptr = &x; //ptr is a pointer to const int 93 | //*ptr = 90; //geçersiz 94 | ptr = &y; //geçerli 95 | //... 96 | } 97 | ``` 98 | 99 | Eğer yazdığınız kodda __const__ anahtar sözcüğünü nereye yazacağınız konusunda tereddütünüz varsa (kuralları unutmuşsanız) her zaman şu cümleyi hatırlayın: 100 | 101 | #### const neden önce geliyorsa const olan odur 102 | 103 | ``` 104 | int * const p = &x; 105 | ``` 106 | 107 | __const__ anahtar sözcüğü *p*'den önce geliyor. __const__ olan _p_'nin kendisi. _p_'ye atama yaparsak sentaks hatası olacak: 108 | 109 | ``` 110 | const int *p = &x; 111 | ``` 112 | 113 | __const__ anahtar sözcüğü _*p_'den önce geliyor. __const__ olan _*p_, yani _p_'nin gösterdiği nesne. _*p_ ifadesine atama yaparsak sentaks hatası oluşacak. 114 | 115 | ``` 116 | int const *p = &x; 117 | ``` 118 | 119 | __const__ anahtar sözcüğü _*p_'den önce geliyor. __const__ olan _*p_, yani _p_'nin gösterdiği nesne. _*p_ ifadesine atama yaparsak sentaks hatası oluşacak. 120 | 121 | __const__ anahtar sözcüğü iki konumda birden de kullanılabilir. Bu durumda her iki __const__ anahtar sözcüğünün verdiği anlam da korunur: 122 | 123 | ``` 124 | int x = 10; 125 | const int *const ptr = &x; 126 | ``` 127 | 128 | Yukarıdaki gibi tanımlanan bir gösterici değişkene İngilizcede _"const pointer to const int"_ denmektedir. Bu durumda verdiğimiz söz hem _ptr_'nin hem de _*ptr_'nin değerini değiştirmemektir. Bir başka deyişle hem _ptr_'ye başka bir adres atamak hem de _*ptr_ ifadesine başka bir değer atamak mantıksal hata ise, gösterici değişkeni bu şekilde tanımlamalıyız. Aşağıdaki koda bakalım: 129 | 130 | ``` 131 | int main() 132 | { 133 | int x = 10; 134 | int y = 20; 135 | 136 | const int *const ptr = &x; //ptr is a const pointer to const int 137 | //*ptr = 90; //geçersiz 138 | //ptr = &y; //geçersiz 139 | //... 140 | 141 | } 142 | ``` 143 | 144 | 145 | Gösterici değişkenlere ilişkin bu semantik yapı bizi daha çok fonksiyonların tanımlanması ve fonksiyonların çağırılması durumunda önem kazanıyor: 146 | 147 | ## Parametre değişkeni gösterici olan fonksiyonlar 148 | 149 | *T* bir tür olsun ve ismi _func_ olan bir fonksiyon aşağıdaki şekilde bildirilmiş olsun: 150 | 151 | ``` 152 | void func(T *p); 153 | ``` 154 | 155 | Fonksiyonun başka parametre değişkenleri de olabilir. Diğer parametre değişkenlerini şimdilik görmezden geliyoruz. C'de böyle bir fonksiyon arayüzünün _(interface)_ anlamı şudur: Fonksiyon, adresini istediği nesnenin değerini değiştirmek amacıyla _(onu set etmek için)_ nesneye erişecek. Böyle bir fonksiyona yapılan çağrı ile fonksiyona bir nesnenin (ya da bir dizinin) adresini gönderdiğimizde nesnemiz değişecek, nesnemize yeni değer ya da değerler (dizi ise) yazılacak. Böyle fonksiyonlara İngilizce'de duruma göre şöyle isimler yakıştırılıyor: _set function_, _setter_, _mutator_ _(değiştiren anlamında)_. Şimdi neden böyle fonksiyonların tanımlandığını konuşalım: 156 | 157 | * Fonksiyon, bize hesapladığı bir değeri iletmek amacıyla tanımlanmış olabilir. Yani fonksiyon hesapladığı değeri, geri dönüş değeri _(return value)_ ile bize iletmek yerine bu değeri bizden adresini istediği nesneye yazıyor olabilir. Bu noktada önemli olan sorulardan biri şu: Fonksiyon neden hesapladığı değeri geri dönüş değeri yöntemi ile değil de bu şekilde iletmeyi tercih etmiş olsun? Bu soruya daha sonra cevap vereceğim. Hadi böyle fonksiyonlara bir örnek verelim: 158 | 159 | ``` 160 | void get_random_date(struct Date *p) 161 | ``` 162 | 163 | Örneğimizde, _struct Date_ isimli türden nesneler (türün etiket isminin de ima ettiği gibi) tarih verileri tutuyor olsun. Yukarıdaki işlevin amacı, müşteri koda rastgele oluşturulmuş bir tarihi iletmek. Fonksiyon bu değeri pek ala geri dönüş değeri ile de kendisini çağıran koda iletebilirdi. Böyle bir fonksiyonu oluşturduğumuz bir _struct Date_ nesnesinin adresi ile çağırmamız gerekiyor. Böylece (adresini) gönderdiğimiz nesne fonksiyonun kodu çalıştıktan sonra rastgele bir tarih değeri tutuyor olacak. 164 | 165 | ``` 166 | { 167 | struct Date xdate; 168 | get_random_date(&xdate); 169 | //... 170 | } 171 | ``` 172 | 173 | Böyle fonksiyonların bu biçimdeki parametre değişkenleri İngilizcede yaygın olarak *"out parameter"* olarak isimlendiriliyor *(output parameter anlamında)*. Çünkü buradaki amaç, fonksiyonun hesapladığı (ürettiği) değer ya da değerleri kendisini çağıran koda iletmesi. 174 | 175 | * Fonksiyon yapmakla yükümlü iş gereği bizden adresini aldığı nesneyi değiştiriyor olabilir. Bir fonksiyonun bir başka yerel kapsamdaki _(local scope)_ nesneyi değiştirebilmesinin başka bir yolu yok zaten. Örneğin _T_ türünden iki nesneyi takas _(swap)_ edecek bir fonksiyon şu parametrik yapıda olacaktır: 176 | 177 | ``` 178 | void swap(T *p1, T *p2); 179 | ``` 180 | 181 | Bu fonksiyona adreslerini gönderdiğimiz _T_ türünden iki nesne takas edilecek. Diziler ya da yazılar üzerinde, dizilerin ya da yazıların öğelerini değiştirmeye yönelik işlemleri gerçekleştirecek fonksiyonlar böyledir değil mi? C'nin standart kütüphanesinden bir örnek verelim: 182 | 183 | ``` 184 | char *strcpy(char *pdest, const char *psource); 185 | ``` 186 | 187 | _strcpy_ fonksiyonu _psource_ adresindeki yazıyı _pdest_ adresine kopyalar. 188 | 189 | * Fonksiyon bizden bir yapı _(structure)_ nesnesinin adresini istiyor bu adresteki nesnenin bazı öğelerinden _(member)_ yapacağı işlemlerde kullanacağı değerleri _(input)_ okuyor aynı zamanda yapı nesnesinin bazı öğelerine ise hesapladığı değerleri yazıyor olabilir. Böyle fonksiyonların gösterici parametre değişkenlerine İngilizcede yaygın olarak _"in-out parameter"_ _(input - output anlamında)_ deniyor. Standart kütüphanenin _time.h_ başlık dosyasında bildirilen _mktime_ isimli fonksiyon buna bir örnek olarak verilebilir: 190 | 191 | ``` 192 | time_t mktime (struct tm * timeptr); 193 | ``` 194 | 195 | Standart _mktime_ fonksiyonu kendisini çağıracak koddan _struct tm_ türünden bir nesnenin adresini istiyor. Adresini istediği nesnenin öğelerinden hesaplamada kullanacağı bazı değerleri alarak kullanacak, ancak duruma göre aynı nesnenin bazı öğelerine de hesapladığı değerleri yazacak. 196 | 197 | Şimdi de diğer arayüzü inceleyelim. Yine _T_ bir tür olsun ve ismi _func_ olan bir fonksiyon aşağıdaki şekilde bildirilmiş olsun: 198 | 199 | ``` 200 | void func(const T *p); 201 | ``` 202 | 203 | Bu bildirim ile aşağıdaki bildirim arasında anlamsal bir fark olmadığını yine hatırlatayım: 204 | 205 | ``` 206 | void func(T const *ptr); 207 | ``` 208 | 209 | C'de böyle bir fonksiyon arayüzünün _(interface)_ anlamı şudur: Fonksiyon, adresini istediği nesneye, onun değerini okumak amacıyla yani bu değeri yapacağı işlerde kullanmak için erişecek. Böyle bir fonksiyona çağrı yaptığımızda (adresini) gönderdiğimiz nesnenin değerinin fonksiyon tarafından değiştirilmeyeceğinden emin olabiliriz. Böyle fonksiyonlara İngilizce'de duruma göre şöyle isimler yakıştırılıyor: _get function, getter, accessor_ _(erişen anlamında)_. Şimdi de neden böyle fonksiyonların tanımlandığını konuşalım... 210 | 211 | Yukarıdaki gibi bir fonksiyon yerine doğrudan değerle çağrılan bir fonksiyon _(call by value)_ oluşturabilirdik: 212 | 213 | ``` 214 | void func(T x); 215 | ``` 216 | 217 | Bu durumda bu fonksiyona yapılan her çağrı, argüman olarak gönderilen değerin fonksiyonun parametre değişkenine kopyalanması sonucunu doğuracaktı. Bu kopyalama maliyeti, _T_ türünden bir nesnenin bellekteki yerinin _(storage)_ büyüklüğü ile doğru orantılı olarak artacaktı. Örneğin sistemimizdeki kelime uzunluğu _4 byte_, _T_ türü için de _sizeof_ değeri _64_ olsun. Bu durumda her fonksiyon çağrısı _64 byte_'lık bir bellek bloğunun kopyalanması sonucunu doğuracaktı. 218 | 219 | ## gösterici dizileri ve const anahtar sözcüğü 220 | 221 | Gösterici dizilerinin tanımlanmasında yine __const__ anahtar sözcüğünün kullanıldığı yere bağlı olarak anlam değişir. Aşağıdaki koda bakalım: 222 | 223 | ``` 224 | int x = 10, y = 20, z = 30; 225 | 226 | int *const pa1[] = { &x, &y, &z }; 227 | const int *pa2[] = { &x, &y, &z }; 228 | ``` 229 | _pa1_ dizisinin tanımında __const__ anahtar sözcüğü _'*'_ (asterisk) atomundan sonra ve dizinin isminden önce yazılmış. Bu durumda __const__ olan dizinin kendisidir. Yani bu durumda dizinin öğelerini değiştirmeme sözü vermiş oluyoruz. Dizinin öğelerinin değiştirilmesi yani onlara atama yapılması derleyici tarafından sentaks hatası olarak işaretlenecek. Ancak bu durumda dizinin öğeleri olan göstericileri içerik _(dereferencing)_ operatörünün operandı yaparak değiştirmemizde bir engel yok. 230 | 231 | ``` 232 | int main() 233 | { 234 | int t = 0; 235 | pa1[0] = &t; //geçersiz 236 | *pa1[0] = 50; //geçerli 237 | //... 238 | } 239 | ``` 240 | 241 | _pa2_ dizisinde ise durum farklı. Burada dizinin elemanları _const int*_ türünden. Dizinin kendisi __const__ değil. Yani dizinin elemanlarının değerlerini değiştirebiliriz. Verdiğimiz söz dizinin öğeleri olan göstericiler ile eriştiğimiz nesneleri salt okuma amaçlı kullanmak. Aşağıdaki koda bakalım: 242 | 243 | ``` 244 | int main() 245 | { 246 | int t = 0; 247 | pa2[0] = &t; //geçerli 248 | *pa2[0] = 50; //geçersiz 249 | //... 250 | } 251 | ``` 252 | 253 | Burada da __const__ anahtar sözcüğü iki yerde birden kullanılabilir: 254 | 255 | ``` 256 | int x = 10, y = 20, z = 30; 257 | 258 | const int *const pa[] = { &x, &y, &z }; 259 | 260 | int main() 261 | { 262 | int t = 0; 263 | pa[0] = &t; //gecersiz 264 | *pa[0] = 50; //gecersiz 265 | //... 266 | } 267 | ``` 268 | 269 | ## gösterici gösteren gösterici (pointer to pointer) ve const anahtar sözcüğü 270 | 271 | Yine _const_ anahtar sözcüğünün kullanıldığı yere bağlı olarak bildirimin anlamı değişmektedir (_const_ neden önce gelirse _const_ olan odur). 272 | 273 | ``` 274 | int main() 275 | { 276 | int x = 10; 277 | int y = 20; 278 | int* p = &x; 279 | int* q = &y; 280 | 281 | int** const ptr1 = &p; 282 | //const pointer to pointer to int 283 | // const ptr1'den önce geliyor, const olan ptr1 284 | //ptr1 = &q; //geçersiz 285 | *ptr1 = &y; //geçerli 286 | **ptr1 = 99; //geçerli 287 | 288 | int* const* ptr2 = &p; 289 | //pointer to const pointer to int 290 | // const *ptr2'den önce geliyor, const olan *ptr2 291 | 292 | ptr2 = &q; //geçerli 293 | //*ptr2 = &y; //geçersiz 294 | **ptr2 = 99; //geçerli 295 | 296 | const int** ptr3 = &p; 297 | //pointer to pointer to const int 298 | // const **ptr3'den önce geliyor, const olan **ptr3 299 | 300 | ptr3 = &q; //geçerli 301 | *ptr3 = &y; //geçerli 302 | //**ptr3 = 99; //geçersiz 303 | 304 | } 305 | ``` 306 | --------------------------------------------------------------------------------