forked from CSD-1993/KursNotlari
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathC++-Examples.txt
21480 lines (15621 loc) · 762 KB
/
C++-Examples.txt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
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 ile her türlü alıntı yapılabilir.
(Notları okurken editörünüzün "Line Wrapping" özelliğini pasif hale getiriniz)
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
1. Ders 14/08/2023 - Pazartesi
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Merhaba Dünya C++ programı
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World" << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
2. Ders 16/08/2023 - Çarşamba
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C++'ın ilk standardı ISO/IEC tarafından 1998 yılında oluşturuldu (ISO/IEC 14882: 1998).Bunu 2003 yılındaki standartlar izledi. 2003 standartları daha çok düzeltme
niteliğinde idi. Daha sonra C++'ın 2011 yılında yeni bir standardı oluşturuldu. Bu standartlarla C++'a pek çok yenilik eklendi. 2011 standartlarını 2014, 2017
ve 2020 standartları izledi. Şu anda üzerinde çalışılmakta olan standart 2023'tür. Bu standartlar halk arasında sırasıyla C++98, C++03, C++11, C++14, C++17,
C++20 ve C++23 olarak bilinmektedir.
Klasik C++ olan C++98 ve C++03'e yapılan eklemeler iki kategoride ele ele alınabilmektedir. Bunlardan biri "doğrudan dile yapılan eklemelerdir (core language
feaures)", diğeri ise C++'ın standart kütüphanesine yapılan eklemelerdir. C++'a en önemli eklemeler C++11 standartları ile yapılmıştır. C++11 ve sonrasına
C++ dünyasında "Modern C++" da denilmektedir. C++11'den sonra artık standartların üç senelik periyotlarla oluşturulması kabul edilmiştir. Kanımızca üç
semelik periyotlar C++ gibi bir dil için çok hızlı bir süreçtir. Bu hızlı gelişme çeşitli sancıları da beraberinde getirmiştir.
C Progralama Dilinin ilk standartları "ISO/IEC 9899: 1990" ismiyle 1990 yılında ISO tarafından oluşturulmuştur. Buna halk arasında C90 denilmektedir.
(Aslında C standartları önce 1989 yılında Amerika'nın standart kurumu olan ANSI tarafından aluşturulmuştu. 1990 ISO standartları bu ANSI standartlarının alınarak
bazı bölüm numaralarının değiştirilmesiyle oluşturulmuşur.) C'nin 1999 yılında yeni bir standardı daha oluşturuldu. Buna da C99 denilmektedir. Daha sonra
C'nin 2011 yılında yeni bir standardı oluşturulmuştur. Buna da C11 denilmektedir. Nihayet C'nin 2017 yılında son sürümü yayınlandı. Buna da C17 denilmektedir.
Ancak bu C17'de yeni özellikler eklenmedi. Yalnızca C11'deki bozukluklar düzeltildi. C'nin üzerinde çalışılan son standart sürümü C23'tür. C23 standartlarının
2024'te yayınlanacağı düşünülmektedir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C++ standartlarının üç senelik periyotlarla hızlı bir biçimde güncellenmesi bazı tasarım hatalarının ve pişmanlıkların oluşmasına da yol açmıştır. Dolayısıyla
Modern C++'a yönelik bazı ince ayrıntılar C++'ın versiyonundan versiyonuna değişmiş olabilmektedir. Biz yeni öğrenen kişilere C++ standartların doğrudan
okunmasını tavsiye etmemekteyiz. Çünkü standart metinleri (bazı diğer standartları da böyle) pedagojik metinler değildir. Olanı tam olarak betimlemek amacıyla
oluşturulmuş metinelerdir. Son yıllarda "C++ Reference" isminde C++ standartlarını açıklamalı (annotated) bir biçimde dokümante eden bir girişim oldukça
popüler hale gelmiştir. Biz de kursumuzda pek çok yerde doğrudan bu standartlara referans etmektense C++ Reference sitesinden faydalanacağız. Siteye aşağıdaki
bağlantıdan ulaşabilirsiniz:
https://en.cppreference.com/w/
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Bir C++ programı IDE'ler yoluyla kolayca derlenip, link işlemi yapılarak çalıştırılabilir. Windows sistemlerinde en yaygın kullanılan IDE Microsoft'un
"Visual Studio" isimli IDE'sidir. C++ için diğer bir IDE seçeneği "Qt-Creator" olabilir. Qt Creator "cross platform" biçimindedir. Yani Windows, macOS ve Linux
sistemlerinde benzer biçimde kullanılabilmektedir. Tabii aslında derleyiciler komut satırından çalıştırılan programlar biçiminde oluşturulmuştur. IDe'ler aslında
derleyicileri çalıştırarak derleme ve bağlama işlemlerini yapmaktadır. Microsoft'un C ve C++ derleyicisi "cl.exe" isimli programdır. Bu derleyici ile derleme
komut satırında şöyle yapılabilir:
cl sample.cpp
Derleyici default durumda derleme işleminden sonra bağlayıcı (linker) programı da çalıştırır. Bu işlemden eğer programınızda bir hata yoksa "sample.exe"
dosyasını elde edeceksinizç Tabii istersek çalıştırılabilen dosyanın ismini /Fe seçeneği ile de değiştirebiliriz:
cl /Fe:test.exe sample.cpp
UNIX/Linux ve macOS sistemlerinde GNU'nun g++ ve clang++ derleyicileri kullanılabilmektedir. g++ derleyicisi ile komut satırından derleme tipik olarak
şöyle yapılmaktadır:
g++ sample.cpp
clang++ derleuyicilerinin kullanımları da g++ ile uyumludur:
clang++ sample.cpp
Burada derleyici derleme işleminden sonra yine bağlayıcı programı çalıştırmaktadır. Bu durumda çalıştırılabilen dosya "a.out" biçiminde oluşur. Çalıştırılabilen
dosyanın ismini değiştirmek için -o seçeneği kullanılmaktadır. Örneğin:
g++ -o sample sample.cpp
ya da örneğin:
clang++ -o sample sample.cpp
UNIX/Linux sistemlerinde bir program dosyasını çalıştırmak için dosya isminin önüne ./ getirmeyi unutmayınız. Örneğin:
./sample
C++'ın çeşitli standartları olduğuna göre derleme işlemi bu standartlar belirtilerek yapılabilir. Visual Stduio IDE'sinde bu durum menüler yoluyla
ayarlanmaktadır. g++ ve clang derleyicinde -std seçeneği ile ayarlama yapılır. Örneğin:
-std=c++11
-std=c++14
-std=c++17
-std=c++20 (-std=c++2a)
-std=c++2b
Örneğin:
g++ -std=c++2a -o sample sample.cpp
Burada -std=c++2a artık yeni derleyicilerde -std=c++20 ile değiştirilmiştir. -std=c++2b ise C++23'ün de bazı özelliklerini barındırmaktadır.
gcc ile C++ programları da derlenebilir. (Aslında gcc önce GNU C derleyicisi olarak geliştirilmişti. Sonra diğer derleyicileri de çalıştıran bir program
haline getirildi.) Bu durumda gcc zaten g++ derleyicisini çalıştırmaktadır. Ancak gcc ile derleme yapılırken libstdc++ kütüphanesinin "-lstdc++" komut satırı
argümanı ile link aşamasında devreye sokulması gerekmektedir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C'de ve C++'ta standartlarda belirtilen sentaks ve semantik kurallara uyulmadan yazılmış olan programlar yine de derleyici tarafından başarıyla derlenebilirler.
Çünkü standartlar geçerli programların derlenmesi gerektiğini koşul olarak ifade etmiştir. Ancak geçesiz programların derlenip derlenmeyeceği konusunda bir hüküm
belirtmemiştir. Yalnızca standartlarda geçersiz durumlar karşısında derleyicilerin bunu en az bir mesajla (diagnostic message) bildirimeleri zorunlu tutulmuştur.
Yani bu durumda geçerli programlar her zaman başarıyla derlenmek zorundadır. Ancak geçersiz programlar başarıyla derlenebilir ya da derlenmeyebilir. Bu nedenle
derleme işleminin başarısına bakarak dilin kurallarını öğrenmeye çalışmak iyi bir yöntem değildir. Ayrıca C ve C++'ta derleyiciler diğer derleyicilerde olmayan
"eklentilere (extensions)" sahip olabilirler. Neyin bir eklenti olup olmadığının programcı tarafından bilinmesi gerekir. Eğer biz bir derleyicinin eklentisini
kullanırsak o kodu başka bir derleyiciye götürdüğümüzde kod başarılı olarak derlenmeyebilir. Çalıştığınız derleyicilerin eklentilerini bilmelisiniz.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C++'ın C'den farklılıkları genellikle "fazlalık" biçimindedir. Bu fazlalıkların bir bölümü C++'ı daha iyi bir C yapmak için dile eklenmiştir. Bunlara "C++'ın
C'den Nesne Yönelimli Programlama Tekniği İle Doğrudan İlgisi Olmayan Fazlalıkları ve Farklılıkları" diyeceğiz. Biz kurusumuzda önce bunlar üzerinde duracağız.
Sonra C++'ı "Nesne Yönelimli bir dil yapan C++ özgü konular üzerinde duracağız. Yani kursumuz kabaca üç bölümden oluşmaktradır:
1) C++'ın C'den Nesne Yönelimli Programlama Tekniği İle Doğrudan İlgisi Olmayan Fazlalıkları ve Farklılıkları
2) C++'ın C'den Nesne Yönelimli Programlama Tekniği İle Doğrudan İlgili Olan Fazlalıkları ve Farklılıkları
3) C++'ın Diğer Öenmli Özellikleri ve Standart Kütüphanesi
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Bu bölümde "C++'ın C'den Nesne Yönelimli Programlama Tekniği İle Doğrudan İlgisi Olmayan Fazlalıkları ve Farklılıkları" maddeler halinde ele alınacaktır.
Burada her maddeyi özet bir cümleyle başlatacağız ve sonra onun ayrıntılarına gireceğiz.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
1) C++'a // ile satır sonuna kadar yorumlama eklenmiştir. Bu özellik C90'da yoktu. Fakat C++98'den sonra sonra çıkan C99 ile birlikte C'ye de eklenmiştir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
cout << "Hello World" << endl; // Merhaba dünya programı
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
2) C++'ta yerel değişkenler blokların herhangi bir yerinde bildirilebilirler. Halbuki C90'da yerel değişkenler blokların başlarında bildirilmek zorundaydı.
Ancak C99 ile birlikte bu kural C'de de C++'takşi gibi değiştirildi. C++'ta bir yerel değişkenin faaliyet alanı bildirim yerinden bildirildiği bloğun sonuna
kadarki bölgededir. Yine C++'ta iç içe ya da ayrık bloklarlarda aynı isimli yerel değişkenler bildirilebilir. Ancak aynı blok içerisinde aynı isimli yerel
değişkenler bildirilemez.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
3) C++'ta for döngülerinin birinci kısmında bildirimler yapılabilmektedir. Bu özellik C90'da yoktu. Ancak C99 ile birlikte C'ye de eklendi. Ayrıca C++'ta
diğer deyimlerin parantezleri içerisinde de bildirimler yapılabilmektedir. for döngüsünün birinci kısmında bildirim yapılabilmesi sentaksını C++'tan almış olan
Java ve C# gibi dillerde söz konusudur. Bu özellik döngü yazımını kolaylaştırmaktadır. Örneğin:
for (int i = 0; i < 10; ++i) {
//...
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
for (int i = 0; i < 10; ++i)
cout << i << " ";
cout << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
for döngüsünün birinci kısmında bildirilen değişkenlerin faaliyet alanları for döngüsü ile sınırlıdır. Başka bir deyişle:
for (bildirim; ifade; ifade)
<deyim>
işleminin eşdeğeri aşağıdaki gibi düşünülmelidir:
{
bildirim;
for (;ifade; ifade)
<deyim>
}
Yani "for döngüsünü kapsayan bir gizli blok" varmış gibi düşünmelisiniz. for döngüsünün diğer kısımlarında bildirim yapılamamaktadır.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
for (int i = 0; i < 10; ++i) {
cout << i << " ";
}
cout << endl;
cout << i << endl; // error!
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Bu biçimdeki bir for döngüsünü aşağıya kopyalarsak faaliyet alanı bakımından bir sorun oluşmayacaktır.Örneğin:
for (int i = 0; i < 10; ++i) {
//...
}
for (int i = 0; i < 10; ++i) {
//...
}
Buradaki i değişkenleri aslında ayrık bloklardaki i değişkenleri gibidir. Yukarıdaki kodun eşdeğerini aşağıdaki gibi düşünmelisiniz:
{
int i = 0;
for (; i < 10; ++i) {
ß//...
}
}
{
int i = 0;
for (; i < 10; ++i) {
//...
}
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
for (int i = 0; i < 10; ++i)
cout << i << " ";
for (int i = 0; i < 10; ++i) // Buaraki i başka bir i
cout << i << " ";
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
İç içe for döngülerinde de aynı isimli değişkenlerin bulunması bu bağlamda bir sorun oluşturmaz. Ancak iç for döngüsünde biz dış for döngüsünün birinci
kısmında bildirilen değişkenleri kullanamayız. Örneğin:
for (int i = 0; i < 10; ++i)
for (int i = 0; i < 10; ++i) {
//...
}
Burada bir sorun yoktur. Çünkü yukarıdaki kodun eşdeğeri aşağıdaki gibidir:
{
int i = 0;
for (; i < 10; ++i)
{
int i = 0;
for (; i < 10; ++i) {
//...
}
}
}
Yani aslında buradaki söz konusu iki i değişkeni aynı blokta değildir, iç içe bloklardadır. Tabii biz iç döngüde dış döngüdeki i değişkenini artık kullanamayız.
Her ne kadar iç içe for döngülerinde döngülerin birinci kısmında aynı isimli değişkenler bildirilebiliyorsa da aslında bu durum iyi bir teknik değildir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
for (int i = 0; i < 10; ++i)
for (int i = 0; i < 10; ++i)
cout << i << endl; // Buradaki i iç for döngüsündeki i, diğer i'ye erişemeyiz
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
for döngülerinin birinci kısmında "aynı türden olmak koşulu ile" birden fazla değişkenin bilfirimi de yapılabilir. Örneğğin:
for (int i = 0, k = 100; i + k > 50; ++i, k -= 2) {
//...
}
Ancak burada bildirilen değişkenler farklı türlerden olamamaktadır. Örneğin:
for (int i = 0, long k = 100; ...) { // geçersiz!..
//...
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
for (int i = 0, k = 100; i + k > 50; ++i, k -= 2)
cout << i << ", " << k << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
for döngüsünün birinci kısmında bildirilen değişkenlere ilkdeğer verilmesi gerekir. Ancak bu durum C++'ta standartlarında zorunlu tutulmamıştır. Genel olarak
bu biçimde bildirilen değişkenlere ilkdeğer verilmemesinin bir anlamı yoktur. Örneğin:
for (int i; i < 10; ++i) { // geçerli ama çöp değer kullanılıyor
//...
}
Java ve C# gibi diğer dilelrde ilkdeğer verme bir zounluluktur.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
3. Ders 21/08/2023 - Pazartesi
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C++'ta if, while switch gibi deyimlerin parantezleri içerisinde de bildirimler yapılabilir. Bu durumda o değişkenlere atanan değerler test işlemine girer. Örneğin:
if (void *ptr = malloc(1024)) { // tahsisat başarılı ise
//...
}
Burada ptr'ye atanan değer test işlemine sokulmaktadır. Örneğin:
while (int a = foo()) {
//...
}
Burada foo fonksiyonu sıfır dışı bir değerle geri döndüğü sürece döngü devam edecektir. Örneğin:
switch (int a = rand() % 10) {
//...
}
Böyle bir özellik C'de yoktur.
Deyimlerin parantezleri içerisinde bildirim yapıldıktan sonra artık atanan değerin aynı ifadede işleme sokulması mümkün değildir. Örneğin:
while ((int i = foo() < 10) { // geçersiz! böyle bir sentaks yok
//...
}
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int foo()
{
static int i = 10;
return --i;
}
int main()
{
while (int i = foo())
cout << i * i << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
4) C++'a bool türü ve bool türünden değer belirten true ve false anahtar sözcükleri de eklenmiştir. bool türü C90'da yoktu. Ancak C99 ile birlikte C'ye de
_Bool ismiyle bir boolean tür eklenmiştir. (C99'da <stdbool.h> içerisinde bool ismi ve true ve false isimleri makrolar biçiminde de oluşturulmuşttr.) C++'a
bool türü eklenince if gibi while gibi deyimlerdeki koşul ifadeleri de "bool türden olacak" biçimde değiştirilmiştir. Yani C++'ta if deyiminin ve while deyiminin
koşul ifadesi artık bool türdendir. Eğer bu koşul ifadeleri bool türden değilse derleyici tarafından otomatik olarak (implictly) bool türüne dönüştürülmektedir.
Aynı durum for döngüsünün ikinci kısmı için de geçerlidir.
C++'ta karşılaştırma operatörlerinin ürettiği değerler de bool türdendir. (C'nin bütün standartlarında karşılaştırma operatörlerinin int türden değer ürettiğini
anımsayınız.)
Standartlar bool türü için ayrılacak yerin derleyiciye bağlı olarak değişebileceğini (implementation-defined) belirtmektedir (8.5.2.3-1). Tipik olarak
derleyicileer bool türü için 1 byte yer ayırmaktadır.
C++'a bool türünün C uyumunu koruyacak biçimde eklendiğine dikkat ediniz. Yani C++'taki bool türü adeta içerisinde 1 ve 0 değerleri bulunan bir tamsayı türü
gibidir. Ancak adres türleri otomatik olarak bool türüne dönüştürülebilmektedir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
int a = 0;
bool result;
result = a > 10; // karşılaştırma operatörleri bool türden değer üretir
cout << result << endl;
result = true; // true ve false birer anahtar sözcüktür
cout << result << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C++'ta bool türü aritmetik işlemlere sokulabilir. Bu durumda "int türüne yükselteme kuralı (integral promotions)" gereğince otomatik olarak int türüne
dönüştürüldükten sonra işleme sokulmaktadır (7.6-6). Dönüştürme sırasında true değeri 1 olarak false değer 0 olarak dönüştürülmektedir. Yani örneğin int + bool
gibi bir işlemin sonucu int türden, double + bool biçiminde bir işlemin sonucu double türden, bool + bool gibi bir işlemin sonucu int türden elde edilecektir.
Skaler türler de bool türüne otomatik olarak (implicitly) dönüştürülebilmektedir. Sıfır dışı skaler değerler true olarak, 0 değeri ise false olarak
dönüşürülmektedir. Ayrıca adres türlerinden bool türüne de otomatik dönüştürme vardır. NULL adres değerleri false olarak diğer adresler ise true olarak bool
türüne dönüştürülürler.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
int a = 10, c;
bool b = true;
char s[10];
c = a + b; // geçerli bool türü işlem öncesinde otomatik olarak int türüne dönüştürülür
cout << c << endl;
b = 120; // geçerli, sıfır dışı değerler true olarak dönüştürülür
cout << b << endl;
b = 0; // geçerli, sıfır değeri false olarak dönüştürülür
cout << b << endl.i
b = s; // geçerli NULL pointer falsde olarak diğer adresler true olarak dönüştürülür
cout << b << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
5) C++'ta bir fonksiyonun çağrılma noktasına kadar derleyicinin o fonksiyonun prototipiyle ya da tanımlamasıyla karşılaşmış olması gerekmektedir. C90'da
fonksiyon çağrısını gören derleyici eğer daha önce fonksiyon hakkında bir bilgi edinmemişse onun int geri dönüş değerine sahip olarak tanımlandığını varsayıyordu.
Gerçi bu kural da C99 ile birlikte C++'taki gibi değiştirilmiştir. Bu durumda C++'ta örneğin bir kütüphane fonksiyonu çağrılacaksa mutlaka o fonksiyonun
prototipinin bulunduğu başlık dosyası da include edilmelidir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
foo(); // C90'da geçerli ancak C++'ta ve C99'da geçersiz
return 0;
}
int foo(void)
{
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
6) C++'ta fonksiyon prototiplerinde parametre parantezinin içinin boş bırakılması ile parametre parantezlerinin içine void yazılması aynı anlamdadır. Ancak
C'nin tüm standartlarında bunlar farklı anlamlara gelir. C'de prototiplerde parametre parantezinin içinin boş bırakılması "bu fonksiyon herhangi bir sayıda
parametreye sahip olabilir, bu nedenle fonksiyon çağrılırken argümanlar sayıca ve türce kontrol edilmeyecek" anlamına geliyordu. Halbuki C++'ta artık parametre
parantezinin içinin boş bırakılmasıyla void yazılması tamamen aynı anlama gelmektedir. Tabii C'de de bir fonksiyonun tanımlanması sırasında parametre arantezinin
içinin boş bırakılmasıyla void yazılması aynı anlama geliyordu. C ile C++ arasındaki farklılık tanımlamada değil prototip bildiriminde ortaya çıkmaktadır.
Tabii C++'ta programcı isterse yine prototiplerde ya da tanımalam sırasında parametre parantezlerinin içerisine void yazabilir.
Aşağıdaki örnekteki kod C'de geçerli olduğu halde C++'ta geçersizdir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int foo();
int main()
{
foo(10, 20); // C'de geçerli, C++'ta geçersiz
return 0;
}
int foo(int a, int b)
{
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Yukarıda da belirttiğimiz gibi hem C'de hemde C++'ta başından beri fonksiyon tanımlaması sırasında parametre parantezinin içinin boş bırakılması fonksiyonun
parametreye sahip olmadığı anlamına gelmektedir. Biz Derneğimizde C kurslarında genel olarak parametresiz fonksiyonların tanımlamasında parametre parantezinin
içini boş bırakmayıp void yazıyorduk. Ancak C++ kurslarımızda parametre almayan fonksiyonlarda tanımlama sırasında ve prototip bildiriminde parametre parantezinin
içerisini boş bırakacağız.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int foo() // C'de de C++'ta da foo fonksiyonun parametresi yok void yazmakla aynı
{
return 0;
}
int main()
{
foo(10, 20); // C'de ve C++'ta geçersiz!
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
7) Hem C'de hem de C++'ta void göstericiye herhangi bir türden adres atanabilir. Ancak void bir adresin void olmayan göstericiye atanması C'de geçerli olduğu
halde C++'ta geçersizdir. C++'ta void bir adres void olmayan bir göstericiye tür dönüştürmesi yapılarak atanmalıdır. Örneğin:
void *pv;
int *pi;
//...
pv = pi; // hem C'de hep de C++'ta geçerli
pi = pv; // C'de geçerli fakat C++'ta geçersiz!
pi = (int *)pv; // hem C'de hem de C++'ta geçerli
C++'ta void adresin void olmayan bir göstericiye atanmasının (yani otomatik olarak dönüştürülmesinin) engellenmesinin temel nedeni aslıda C'deki bir açığı
kapatmak içindir. C'de aşağıdkai gibi bir bir açık vardı:
int *pi;
char *pc;
void *pv;
...
pv = pi; // C'de v e C++'ta geçerli
pc = pv; // C'de geçerli
Burada biz aslında pc = pi işlemini yapmış olmaktayız. Normalde geçersiz olması gereken bu işlem araya bir void gösterici sokularak geçerli hale gelmektedir.
Oysa C++'ta böylesi bir "arkadan dolaşma" yapılamamaktadır. Tabii C'de neden void bir adresin void olmayan bir göstericiye atanabildiğini sorgulayabilirsiniz.
Bu tarihsel bir özelliktir. Zaten C++ fırsat bu fırsat C'nin böylesi açıklarını da kapatmak istemiştir. Bu tür durumlarda tür dönüştürmesi genel olarak
"işlemin programcı tarafından yanlışlıkla değil bilinçli olarak yapıldığını" ifade etmektedir.
Programlama dillerinde "kuvvetli tür kontrolü (strong type checking)" ve "zayıf tür kontrolü (weak (loose) type checking)" biçimibde bir kavram vardır. Eğer
biz bir dilde farklı türleri serbestçe biribine atayıp onları birlikte işleme sokabiliyorsak o dilin tür kontrolü zayıftır. Eğer bir dilde biz farklı türleri
birbirine atayamayıp onları birlikte işleme sokamıyorsak o dilin tür kontrolü kuvvetlidir. C ve C++'ın tür kontrolü kuvvetli değildir. Orta düzeydedir.
Örneğin Java ve C#'taki tür kontrolü C ve C++'a göre daha kuvvetlidir. Swift gibi Rust gibi dillerde tür kontrolü çok daha kuvvetlidir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main()
{
int a[10];
void *pv;
double *pd;
pv = a; // hem C'de hem C++'ta geçerli
pd = pv; // C'de geçerli, C++'ta geçersiz
pd = (double *)pv; // geçerli
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
4. Ders 23/08/2023 - Çarşamba
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
8) C'de string'ler char türden bir dizi kabul edilmektedir. (Yani string'leri gören derleyici onun karakterlerini null karakter dahil olmak üzere char türden
statik ömürlü bir diziye yerleştirir. String yerine o dizinin kullanıldığını varsayar.) Ancak string'lerin karakterlerinin güncellenmesi "tanımsız davranışa
(undefined behavior)" yol açmaktadır. Ancak C++'ta string'ler const char türünden dizi anlamına gelmektedir. Dolayısıyla bir string'i char türden bir göstericiye
atarken göstericinin de gösterdiği yer const olan bir gösterici olması gerekir. Örneğin:
char *str;
//...
str = "ankara"; // C'de geçerli C++'ta geçersiz!
Örneğin:
const char *str;
//...
str = "ankara"; // C'de de C++'ta da geçerli
char türden, signed char türden ve unsigned char türden bir diziye ilkdeğer verirken kullanılan iki tırnakların string belirtmediğini anımsayınız. Bu
nedenle C++'ta da tıpkı C'de olduğu gibi char türden, signed char türden ve unsigned char türden diziler iki tırnak ifadesiyle ilkdeğer verilerek
tanımlanabilirler.
char s1[] = "ankara"; // C'de de C++'ta da geçerli
signed char s2[] = "ankara"; // C'de de C++'ta da geçerli
unsigned char s[] = "ankara"; // C'de de C++'ta da geçerli
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main(void)
{
const char *str = "ankara";
cout << str << endl;
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
9) C'de N elemanlı char türden, signed char türden ve unsigned char türden bir diziye iki tırnak içerisinde N karakterli bir yazı ile ilkdeğer verilebilir. Bu
durumda derleyici null karakteri diziye yerleştirmez. Ancak bu durum C++'ta geçerli değildir. C++'ta N karakterlik bir yazının ilkdeğer verildiği dizinin
en azından N + 1 karakter uzunluğunda olması (ya da uzunluk belirtilmemesi) gerekir. Örneğin:
char s[6] = "ankara"; // C'de geçerli ancak C++'ta geçersiz!
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
10) C++11 ile birlikte UNICODE string'ler ve karakter sabitleri de dile eklenmiştir. UNICODE UTF-8 için u8 öneki, UNICODE UTF-16 için u öneki ve UNICODE
UTF-32 için U öneki kullanılmaktadır. u önekli string'ler const char16_t türünden, U önekli string'ler const char32_t türünden ve u8 önekli string'ler de
const char8_t türünden bir dizi olarak ele alınmaktadır. C++20'ye kadar char8_t biçiminde bir tür yoktu. Bu tür C++20 ile eklenmiştir. Bu tür isimleri
typedef değil anahtar sözcüklerdir. Bu türlerin hepsi işaretsiz bir tamsayı türü belirtmektedir. Bunların işaretli biçimleri de yoktur. C++'ta u önekli string'ler
const char16_t türünden, U önekli string'ler const char32_t türünden ve u8 önekli string'ler ise const char8_t türünden dizi kabul edilmektedir. Örneğin:
const char *s = "Ankara"; // default karakter tablosu
const char16_t *k = u"Ağrı Dağı"; // UTF-16
const char32_t *t = U"Ağrı Dağı"; // UTF-32
const char8_t *m = u8"Ağrı Dağı"; // UTF-8
char8_t türünden, char16_t türünden ve char32_t türünden dizilere u8 önekli, u önekli ve U önekli iki tırnaklı ifadeler ilkdeğer olarak verilebilmektedir.
Tabii bu durumda dizilerin const olması gerekmez. Örneğin:
char8_t s1[] = u8"ankara"; // geçerli
char16_t s2[] = u"ankara"; // geçerli
char32_t s3[] = U"ankara"; // geçerli
C++20'ye kadar char8_t türü yoktu. Dolayısıyla C++20'ye kadar u8 önekli string'ler unsigned char türünden diziler gibi ele alınıyordu. C++20'de u8 önekli
string'ler için de char8_t türü eklenmiştir. C++20 satndartları C++20'ye eklenen char8_t türünün unsigned char türü ile aynı uzunlukta olması gerektiği
belirtilmiştir. (Başka bir deyişle C++20'de eklenen char8_t türü adeta unsigned char türü gibidir ancak fakat farklı bir türdür.)
char8_t türü, char16_t türü ve char32_t türü cout ile stdout dosyasına yazdırılamamaktadır.
Tabii nasıl u önekli, U önekli ve u8 önekli stringler varsa aynı zamanda bu önekli karakter sabitleri de vardır. Bu karakter sabitleri de sırasıyla char16_t, char32_t
ve char8_t türünden sabitler kabul edilmektedir. Örneğin:
char8_t a = u8'a';
char16_t b = u'b';
char32_t c = U'c';
Karakter kodlamaları (character encoding) ayrıntıları olan uygulamada çetrefil bir konudur. Bir karakter tablosu "glyph", "code point" ve "encoding" denilen
üç kavramla ilgilidir. Karakter tablosundaki karakter görüntülerine "glyph" denilmektedir. Karakter tablosunu oluşturanlar her karaktere bir numara verirler.
İlgili glyph'in numarasına "code point" denilmektedir. Bir code point'in ikilik sistemde byte'lar haline ifade edilme biçimine de "encoding" denilmektedir.
Örneğin ASCII tablosunda 'A' glyph'inin code point'i 65'tir. ASCII tablosu tamamne düz binary dönüştürmeyle encode edilmektedir. Son 20 yıldır ASCII tablosunun
ve bu tablonun "code page" varyasonları çeşitli bakımlardan yetersiz kaldığı için UNICODE denilen (ISO 10646) her karakterin kabaca 2 byte ile ifade edilebildiği
dünyanın bütün karakterlerinin içinde bulunduğu karakter tablosu oldukça yaygınlaşmıştır. Pek çok yeni programlama dilinde "char" türü UNICODE karakterleri
tutmak için oluşturulmuştur. UNICODE tablonun UTF-16, UTF-32 ve UTF-8 denilen encoding'leri vardır. UTF-16 UNICODE için en doğal encoding'tir. Bu encoding'te
kabaca her UNICODE code point WORD biçimde (2 bytelık sayı olarak) kodlanmaktadır. UTF-32'de ise her code point 4 byte ile kodlanmaktadır. Ancak UNICODE tablonun
en yaygın encoding'i UTF-8 denilen encoding'tir. UNICODE tablonun ilk 128 karakteri standart ASCII tablosu ile aynıdır. Sonraki 128 karakteri de ISO 8859-1
denilen Latin1 code page'i ile aynıdır. UTF8-8 encoding'inde standart ASCII karakterler 1 byte ile diğer karakterler duruma göre 2 byte, 3 byte 4 byte ve 5 byte
ile kodlanmaktadır. Bugün kullandığımız programalama editörlerinin büyük kısmının default encoding'i UNICODE UTF-8'dir.
Ancak bu konu sanıldığından daha çetrefildir. Çünkü bir C/C++ programı yazarken değişik aktörler devreye girmektedir. Bu aktörlerden birincisi kodu yazdığımız
editördür. Bu editörün default bir encoding'i vardır. Örneğin kursun yapıldığı bilgisayardaki Visual Studio IDE'sinin default encoding'i ASCII 1254 code page'idir.
Visual Studio Code IDE'sinin defaulşt encoding'i UNICODE UTF-8'dir. İkinci aktör bizzat derleyicinin kendisidir. Derleyici kaynak kodu aldığında o kaynak
koddaki yazının da belli bir encoding'e göre kodlandığını varsaymaktadır. Eğer bizim kaynak kodda kullandığımız encoding derleyicinin varsaydığı encoding'ten
farklıysa burada da potansiyel bir problem vardır. Buna derleyicinin "kaynak karakter kümesi (source character set)" denilmektedir. Kaynak karakter kümesi
derleycilerde komut satırı seçenekleriyle değiştirilebilmektedir. Üçüncü aktör ise programın çalıştırıldığı ortamdaki aygıtların (stdout ve stdin aygıt
sürücülerinin) varsaydığı encoding'tir. Buradaki uyuşmazlık karakterlerin doğru gözükmemesine yol açacaktır.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
11) C++'ta sınıflarla temel türlerin şablon uyumunun sağlanması için temel türlere de parantezler ile ilkdeğer verilebilmesi mümkün hale getirilmiştir.
Örneğin:
int a = 10;
Biz bu tanımlamayı şöyle de yapabilirdik:
int a(10); // C++'a özgü, C'de böyle bir ilkdeğer verme sentaksı yok
Ancak parantezlerin içini boş bırakmak "fonksiyon prototipi" anlamına gelmektedir. Örneğin:
int b(); // bu ilkdeğer verme sentaksı değil! Fonksiyon prototipi
Örneğin:
const char *name("ali");
Pekiyi bir diziye ya da yapıya da bu biçimde ilkdeğer verebilir miyiz? Örneğin:
int a[3](10, 20, 30);
Bu durum C++20'ye kadar geçerli değildi. Yani C++20 öncesinde dizilere ve yapılaba normal parantezlerle ilkdeğer verilemiyordu. Ancak C++20 ile birlikte
bu durum da geçerli hale getirilmiştir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
12) C++'ta çeşitli biçimlerde ilkdeğer verme (initialization) sentaksları bulunmaktadır. Örneğin bir değişkene bir ifade ile '=' atomu kullanılarak ilkdeğer
verilebilir:
int a = 10;
Böyle ilkdeğer verme C'de yoktur. Bu normal parantez sentaksı C++'ta sınıflar için düşünülmüştür. Ancak temel türler için de kullanılabilmektedir.
Bir diziye küme parantezleriyle ilkdeğer verilir:
int a[] = {1, 2, 3};
C'de ve C++'ta skaler türlere de küme parantezi ile ilkdeğer verilebilmektedir. Örneğin:
int a = {10};
Bir sınıf nesnesi normal parantezlerle ilkdeğerlenir:
Sample s(10, 20);
Yukarıda da belirttiğimiz C++'ta bu sınıflar düşünülmüş olan (...) sentaksı skaler türler için de kullanılabilmektedir. Örneğin:
int a(10);
İşte C++11 ile birlikte her durum için geçerli olan ilkdeğer verme sentaksı oluşturulmuştur. Buna "Uniform Initializer Syntax" denilmektedir. Bu sentaksta
hiç '=' atomu kullanılmadan doğrudan küme parantezleri içerisinde ilkdeğer verilir. Örneğin:
int a{10};
const char *b{"ali"};
int c[]{1, 2, 3};
int d{}; // d = 0
Bu ilkdeğer verme sentaksına "Uniform Initializer Syntax" denilmesinin nedeni her türden değişkene bu biçimde ilkdeğer verilebilmektedirilmesidir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main(void)
{
int a{10};
const char *b{"ali"};
int c[]{1, 2, 3};
int d{}; // d = 0
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Uniform Initializer Syntax ile ilkdeğer verme sırasında bilgi kaybına yol açabilecek dönüştürmeler geçerli değildir. Bilgi kaybına yol açabilecek dönüştürmelere
C++11 standartlarında "daraltıcı dönüştürmeler (narrowing conversion)" denilmektedir. Büyük tamsayı türünden küçük tamsayı türüne yapılan dönüştürmeler,
gerçek sayı türlerinden tamsayı türlerine yapılan dönüştürmeler bilgi kaybı oluşturabildiği için "daraltıcı dönüştürmeler (narrowing conversion)" durumundadır.
Örneğin:
int a{3.14}; // geçersiz!
int b = 3.14; // geçerli
double c{3.14}; // geçerli
float d{c}; // geçersiz!
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main(void)
{
int a{10.2}; // geçersiz!
int b = 10.2; // geçerli
int c[]{1, 2, 3.4, 5}; // geçersiz!
long d{100}; // geçerli
int e{d}; // geçersiz
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
5. Ders 28/08/2023 - Pazartesi
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Aşağıdaki dönüştürmeler daraltıcı dönüştürmeler olarak kabul edilmektedir:
- Gerçek sayı türlerinden tamsayı türlerine yapılan dönüştürmeler (örneğin long double, double ve float türünden int türüne yapılan dönüştürmeler). Örneğin:
double d{3.0};
int a{d}; // geçersiz!
int b{3.0}; // geçersiz!
- Büyük gerçek sayı türünden bir daha küçük bir gerçek sayı türüne yapılan dönüştürmeler. Örneğin double türünden float türüne yapılan dönüştürmeler
daraltıcı dönüştürmelerdir. Ancak büyük gerçek sayı türünden değer bir sabit ifadesi biçiminde verildiyse ve bu sabit ifadesi hedef tür ile tam olarak
ifade edilebiliyorsa bu daraltıcı dönüştürme kabul edilmemektedir. Örneğin:
double d{3.14};
float f{d}; // geçersiz!
float e{3.14} // geçerli, çünkü 3.14 bir sabit ifadesidir ve float türüyle tam olarak temsil edilebilmektedir.
float g{3.14 + 1}; // geçerli, çünkü 4.14 bir sabit ifadesidir ve float türüyle tam olarak temsil edilebilmektedir.
- Bir tamsayı türünden gerçek sayı türüne dönüştürmeler de bilgi kaybına yol açabilme potansiyeline sahip olduğu için daraltıcı dönüştürmelerdir. Ancak
tamsayı türünden değer bir sabit ifadesi biçiminde belirtilmişse ve bu sabit ifadesi hedef tür tarafından tam olarak ifade edilebiliyorsa bu bir daraltıcı
dönüştürme değildir. Örneğin:
int a{10};
double b{a}; // geçersiz!
char c;
double e{c}; // geçersiz!
float f{1234}; // geçerli
- Bir tamsayı türünden başka bir tamsayı türüne dönüştürme yapılırken eğer hedef tür "o sistemdeki" kaynak türün tüm değerlerini içeriyorsa bu bir daraltıcı
dönüştürme değildir. Ancak içermiyorsa bu bir daraltıcı dönüştürmedir. Örneğin:
int a{10};
unsigned b{a}; // geçersiz!
unsigned c{10};
int d{c}; // geçersiz!
long e{10};
int f{e}; // int ile long türünün aynı uzunlukta olduğu sistemlerde geçerli (örneğin Microsoft derleyicilerinde)
// ancak int ve long türünün farklı uzunluklarda olduğu sistemlerde geçersiz (örneğin 64 bit Linux derleyicilerinde)
Ancak kaynak tamsayı türündeki değer bir sabit ifadesi ise ve hedef türün sınırları içerisinde kalıyorsa bu bir daraltıcı dönüştürme değildir.
Örneğin:
unsigned a{10}; // geçerli, 10 int türden fakat unsigned int sınırları içerisinde
unsigned b{-10}; // geçersiz! -10 int türden fakat unsigned int sınırları içerisinde değil
int c{123L}; // geçerli, 123 long türden ancak int sınırları içerisinde
long d{123}; // geçerli, 123 int türden ve long türünün sınırları içerisinde
- Anımsanacağı gibi adres türlerinden bool türüne otomatik dönüştürme vardı. Ancak adres türlerinden bool türüne yapılan dönüştürmeler daraltı
dönüştürmelerdir. Örneğin:
char s[10];
bool b = s; // geçerli
bool c{s}; // geçersiz!
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
Aslında C++11 ile birlikte küme parantezleri ile ilkdeğer vermelerin hepsinde daraltıcı dönüştürme yasaklanmıştır. Örneğin aşağıdaki ilkdeğer vermeler
C++11 öncesi geçerli olduğu halde C++11 ile birlikte artık geçersizdir:
int a[] = {10.2, 1, 2}; // geçersiz!
int b = {3.14}; // geçersiz!
short c[]{10L, 100, 1000}; // geçerli
Görüldüğü gibi her ne kadar daraltıcı dönüştürmeler C++11'in Uniform Initializer Syntax'ı ile kavramsal olarak dile eklendiyse de yalnızca Uniform Initializer
Syntax ile değil tüm küme parantezleri ile ilkdeğer verilirken etkili olmaktadır.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
int main(void)
{
int a[] = {10.2, 1, 2}; // C++11 ile birlikte artık geçerli değil
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
13) C++'ta const nesneler onlara verilen ilkdeğerler "sabit ifadesi (constant expression)" ise sabit ifadesi belirtirler. Halbuki C'de const nesneler hiçbir
zaman sabit ifadesi belirtmezler. Ayrıca C++'ta global const nesneler "internal linkage"a sahiptir. (Yani sanki static global nesneler gibi düşünülmelidir.)
Tabii bir sabit ifadesi sabit ifadeleriyle de oluşturulabilmektedir. Örneğin:
const int a = 10; // a sabit ifadesi olarak kullanılabilir
const int b = a + 20; // b sabit ifadesi olarak kullanılabilir
const int c = foo(); // c sabit ifadesi olarak kullanılamaz!
Anımsanacağı gibi C'de (ve C++'ta) bazı durumlarda sabit ifadesi kullanımı zorunludur. Örneğin case ifadelerinin sabit ifadesi olması gerekir. Örneğin dizi
tanımlanırken dizi uzunluklarının sabit ifadesi olması gerekmektedir. (C99'da yerel diziler için bu zorunluluk ortadan kaldırılmıştır.) Örneğin C++'ta
şablonların tür olmayan (none-type) parametreleri sabit ifadesi olarak girilmek zorundadır. Sabit ifadelerinin diğer önemli bir işlevi de onların değerlerinin
derleme zamanında hesaplanabilmesi dolayısıyla programın çalışma zamanı sırasında gereksiz bir biçimde yapılmamasıdır. Bu optimizasyon temsaına "constant folding"
denilmektedir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
#include <iostream>
using namespace std;
const int SIZE = 10;
int main(void)
{
int a[SIZE]; // geçerli SIZE sabiti ifadesi ile ilkdeğer verilmiş bir const nesne
int val;
const int AA = 2;
int b = 10;
const int BB = b;
cout << "Bir değer giriniz:";
cin >> val;
switch (val) {
case AA: // geçerli AA bir sabit ifadesi
break;
case BB: // geçersiz! BB bir sabit ifadsi değil, çünkü ona verilen ilkdeğer sabit ifadesi değil
break;
}
return 0;
}
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
C++'ta global const nesnelerin "internal linkage" özelliğine sahip olması onların başka bir modülden extern yapılsa bile kullanılamayacağı anlamına gelmektedir.
Bu durumda derleyiciler global const nesnelere verilen ilkdeğerler sabit ifadesi ise onlar için hiç ayırmayabilirler. Çünkü aslında sabit ifadeleri ile ilkdeğer
verilmiş olan global const nesneler kod içerisinde kullanıldığında derleyici zaten onların değerlerine koda enjekte edecektir. Bunlar başka bir modülden de
kullanılamayacağına göre onlar için yer ayrılmasının da bir anlamı kalmayacaktır. Şüphesiz aynı durum yerel const nesneler için de geçerlidir. Anımsanacağı gibi
yerel değişkenlerin zaten "linkage" özelliği yoktur. Tabii programcı const nesnelere sabit ifadesi ile ilkdeğer verdikten sonra onların adreslerini alıp
kod içerisinde kullanırsa derleyiciler mecburen onlar için yer ayırmak durumunda kalır.
O halde C++'ta sabit ifadeleriyle ilkdeğer verilmiş global const nesneler adeta #define sembolik sabitleri gibi kullanılabilmektedir. Biz global const
nesneleri (özellikle sabit ifadeleriyle ilkdeğer verilmiş olanları) başlık dosyalarına (header files) yerleştirip birden fazla kaynak dosyadan include
edebiliriz. Bu durumda herhangi bir problem ortaya çıkmayacaktır. C++ programcıları #define sembolik sabitleri yerine global const nesneleri kullanmayı
tercih edebilmektedir.
Anımsanacağı gibi iki yer belirleyici (storage class specifier) anahtar sözcük bildirimde bir arada kullanılamamaktadır. Örneğin:
extern static int g_x; // geçersiz!
Ancak tür niteleyicileriyle (const ve volatile) yer belirleyicileri birlikte kullanılabilirler. Örneğin:
extern const int g_x = 10;
C++'ta const nesneler default durumda "inernal linkage" özelliğine sahiptir. Ancak özellikle extern belirlemesi yapılırsa "external linkage" özelliğine
sahip olmaktadır.
Ayrıca C++'ta const nesnelere artık ilkdeğer verilmek zorundadır. Halbuki C'de anlamsız olsa da const nesnelere ilkdeğer vermek zorunlu değildir.
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
14) C++'ta statik ömürlü değişkenlere (yani global ve statik yerel nesnelere) verilen ilkdeğerler sabit ifadesi olmak zorunda değildir. Halbuki C'de global
değişkenlere ve statik yerel değişkenlere verilen ilkdeğerlerin sabit ifadesi olması zorunludur. C'de bu zorunluluk derleyicinin statik ömürlü değişkeni
ilkdeğeri ile birlikre amaç koda yazma gerekeliliğinden kaynaklanmaktadır. Bu gereklilik C++'ta oratadan kaldırılmıştır. (Çünkü C++'ta main fonksiyonu
çağrılmadan önce derleyici başka kodlar da çalıştırabilmektedir.) Örneğin:
int foo()
{
return 10;
}
int g_x = foo(); // C'de geçersiz! C++'ta geçerli
--------------------------------------------------------------------------------------------------------------------------------------------------------------*/
/*-------------------------------------------------------------------------------------------------------------------------------------------------------------
15) C++11 ile birlikte C++'a ismine "constexpr" denilen bir tür niteliyici (type qualifier) da eklenmiştir. constexpr bir değişken her zaman bir sabit ifadesi
biçiminde kullanılabilir. (const bir değişken sabit ifadesi olarak kullanılabilmesi için ona verilen ilkdeğerin sabit ifadesi belirtmesi gerektiğini anımsayınız.)
Ancak constexpr değişkenlere verilen ilkdeğerlerin sabit ifadesi olması zorunludur. Global constexpr değişkenler de yine "internal linkage" sahiptirler.
constexpr değişkenler aynı zamanda const nesneler olarak ele alınmaktadır. Dolayısıyla örneğin bir constexpr değişkenin adresi ancak const bir göstericiye
atanabilir. Örneğin:
constexpr int a = 10; // geçerli
constexpr int b = a + 1; // geçerli
int c = 20;
constexpr int d = c + 1; // geçersiz! constexpr değişkene verilen ilkdeğerin sabit ifadasi olması gerekir
const int e = 10