C Programlama Dili Testi
(stefansf.de)- C dilinin kuralları, pointer karşılaştırması, aliasing, null pointer ve başlatılmamış değerler gibi basit görünen kodları bile tanımsız davranışa dönüştürebilir
- Tamsayı sabitleri ile
sizeof, karakter sabitleri veuint8_taritmetiği, tip seçimi ve integer promotion nedeniyle platforma, gösterime ve ara atamanın yapıldığı yere göre farklı sonuçlar verebilir - Fonksiyon bildirimlerindeki
foo()vefoo(void), prototip eksikliği, varsayılan argüman yükseltmeleri ve dönüş değeri olmayan fonksiyonlar, C ve C++ arasında yasallık veya davranış açısından farklılık gösterir - Diziler pointer değildir; dizi parametreleri pointer’a dönüştürülür ve
a,&a,&a[0]aynı adresi taşısa bile tipleri farklı olduğu için birbirinin yerine kullanılamaz - Operatör önceliği ile değerlendirme sırası ayrı şeylerdir;
switchgövdesinin yapısından geçici nesnelerin ömrüne kadar standart metni gerçek çalışma sonucunu belirler
Tanımsız davranış ve pointer kuralları
-
Pointer karşılaştırması ve strict aliasing kuralı
- Aynı tipteki
pveqpointer’ları aynı adresi gösterse bile, farklı nesnelerden türemişlerse ve aynı aggregate veya union nesnesinin parçası değillersep == qkarşılaştırması tanımsız davranış olabilir - Pointer’ların yalnızca sayısal adreslerden daha soyut olduğu konusu ilgili yazıda devam ediyor
- Bir
intnesnesineshortlvalue ile erişmek, strict aliasing kuralı gereği tanımsız davranıştır unsigned charpointer’ı istisna olarak her türlü nesneye alias yapabildiğinden, birintnesnesineunsigned charlvalue ile erişmek yasaldırunsigned chariçin padding bit ve trap representation bulunmadığı garanti edilir; C11’den itibarensigned chariçin de padding bit olmadığı garanti edilir- Tipe dayalı alias analizi ilgili yazıda ele alınıyor
- Aynı tipteki
-
Null pointer ve pointer gösterimi
- Bir null pointer’ın bit gösterimi mutlaka tüm bitleri 0 olmak zorunda değildir
- C standardı null pointer constant kavramını tanımlar, ancak çalışma zamanındaki null pointer gösterimini veya genel pointer gösterimini tanımlamaz
- Symbolics Lisp Machine 3600, sayısal pointer yerine
<array-object, index>biçiminde bir tuple kullanır ve null pointer gösterimi<nil, 0>şeklindedir - Ek örnekler clc SSS 5.17 içinde bulunabilir
- Sabit
0, bağlama göre tamsayı veya null pointer olabilir;(void *)0ise null pointer olarak değerlendirilir eifadesinin0değerini vermesi,(void *)eifadesinin null pointer olacağını garanti etmez- Yalnızca null pointer constant bir pointer türüne dönüştürüldüğünde null pointer’a eşit olması garanti edilir
- Null pointer üzerinde aritmetik yapmak tanımsız davranıştır; bu nedenle
ebir null pointer olsa bilee + 0ifadesinin null pointer olacağı garanti edilmez
-
Başlatılmamış değerler
- Otomatik saklama süreli başlatılmamış bir nesne okunduğunda, bu nesne
registersaklama sınıfında olabiliyorsa ve adresi hiç alınmamışsa C11 § 6.3.2.1 ¶ 2 uyarınca bu tanımsız davranış olur - Bu kural, DR338 içinde ele alınan Intel Itanium mimarisiyle bağlantılıdır
- Itanium’un genel amaçlı tamsayı register’ları 64 bit ve bir trap bit taşır; bu trap bit, register’ın başlatılıp başlatılmadığını gösteren
NaT(not-a-thing) değeridir - Değişkenin adresi alınırsa bu koşul ortadan kalkar, ancak değer yine de indeterminate olur ve trap representation veya unspecified value olabilir
- Bir trap representation okumak, C11 § 6.2.6.1 ¶ 5 uyarınca tanımsız davranıştır
- Eğer unspecified value söz konusuysa
x != xsonucununtrueveyafalseolması mümkündür;int xunspecified isex *= 0sonrasında bilex’in 0 olduğu garanti edilmez - Indeterminate ve unspecified value, DR260, DR451, N1793, N1818, N2012, N2013, N2221 belgelerinde tartışılmaktadır
- Otomatik saklama süreli başlatılmamış bir nesne okunduğunda, bu nesne
-
unsigned charvememcpyunsigned chartürü, C11 § 6.2.6.1 ¶ 3 uyarınca trap representation içermez; bu yüzden başlangıç değeri unspecified olur- StackOverflow’da C komitesi üyesinin yanıtı, standart kütüphane fonksiyonu
memcpyçağrısından sonraxdeğerinin specified hale gelmesi gerektiğini ve bu yoruma görex != xsonucununfalseolacağını savunur - C standardında bunu açıkça destekleyen dayanak net değildir ve DR451 içindeki komite yanıtı, indeterminate value üzerinde kütüphane fonksiyonu kullanmanın tanımsız davranış olduğunu söyleyerek bu yorumla çelişir
- Bu soru hâlâ açık durumdadır; ek tartışma için Uninitialized Reads yazısına bakılabilir
Tamsayı sabitleri, terfi ve sizeof
-
Tamsayı sabitlerinin gösterimi ve türü
- Soneki olmayan ondalık tamsayı sabitleri her zaman signed tür listesinden seçilir; ancak sekizlik ve onaltılık sabitler signed veya unsigned tür olabilir
- C17 § 6.4.4.1'e göre bir tamsayı sabitinin türü, ilgili değeri ifade edebilen listedeki ilk tür olarak belirlenir
- Sonek olmadığında ondalık sabitler için sıra
int,long int,long long int; sekizlik ve onaltılık sabitler için iseint,unsigned int,long int,unsigned long int,long long int,unsigned long long intşeklindedir INT_MAX+1ileUINT_MAXarasındaki sabitlerin türü, ondalık mı yoksa onaltılık mı olduklarına göre değişebilir ve değişken argümanlı fonksiyon çağrıları gibi ABI'ye duyarlı kodlarda fark yaratabilir- Arm 32-bit architecture ABI içinde
intvelong32 bit olarak tek bir register ile aktarılırken,long long64 bit olarak iki register ile aktarılır int'in 32 bit olduğu platformlarda-1 < 0x8000ifadesitrueolurken,int'in 16 bit olduğu platformlardafalseolur; bu da taşınabilirlik sorunu yaratabilir- generic selection, C++ overload fonksiyonları ve
sizeof(0x80000000) == sizeof(2147483648)gibi ifadelerde de sabit türü farkı sonucu değiştirebilir
-
sizeof(int) > -1sizeofişleci,size_ttüründe unsigned bir tamsayı döndürür- C11 § 6.3.1.8'deki usual arithmetic conversions uyarınca, signed operand unsigned operanddan daha düşük rank'e sahipse aynı rank'teki unsigned türe dönüştürülür
-1'e karşılık gelen signed integer, unsigned türe dönüştürüldüğünde o rank'teki en büyük unsigned integer olur- Bu nedenle
sizeof(int) > -1her zamanfalseolarak değerlendirilir
-
Karakter sabitlerinin türü
- C'de karakter sabitleri, C11 § 6.4.4.4 ¶ 10'a göre
inttüründedir - Bu nedenle
sizeof(char) == sizeof('x')ifadesinin her zamantrueolacağı garanti edilmez; yalnızcasizeof(int) == sizeof('x')garanti edilir - integer character constant, bir veya daha fazla multibyte character dizisi olabilir; dolayısıyla
'abc'de geçerlidir ve bunun gösterimi implementation-defined'dır - Tek bir karakter içeren integer character constant'ın değeri, aynı tek karakteri gösteren
chartüründeki nesnenin tamsayı gösterimiyle aynıdır
- C'de karakter sabitleri, C11 § 6.4.4.4 ¶ 10'a göre
-
uint8_taritmetiği ve bölmea,b,cokunmadan önce başlatılmış olsa bile, tamsayı terfileri ve ara atamanın konumu nedeniylexvezdeğerleri farklı olabilir- Her değişken değeri
intboyutuna terfi ettirildikten sonra toplama ve bölme yapılır; her atama sonucu ise ilgili değişken türüne truncate edilerek saklanır - Örneğin
a=255,b=1,c=2isex,((255 + 1) / 2) % 256 = 128olur - Ara değişken
y,(255 + 1) % 256 = 0olur; ardındanz,(0 / 2) % 256 = 0olur ve dolayısıyla128 != 0olur - unsigned integer overflow tanımlı davranıştır
- Modüler aritmetik, toplama üzerine dağıldığından bölmeyi toplama ile değiştirirseniz
xvezher zaman aynı olur - İlk atamayı
uint8_t x = ((uint8_t)(a + b)) / c;olarak değiştirseniz dexvezher zaman aynı olur
-
constdeğişkenler ve variable length arrayconstile nitelenmişnvemdeğişkenleri dizi boyutu olarak kullanılsa bile, bunlar C'de integer constant expression değildir- C11 § 6.6 ¶ 6'da integer constant expression; integer constant, enumeration constant, character constant, sonucu integer constant olan
sizeof,_Alignof, cast'in doğrudan operandı olan floating constant gibi yapılarla sınırlıdır - Dizi boyutu ifadesi integer constant expression değilse, C11 § 6.7.6.2 ¶ 4 uyarınca variable length array olur
- variable length array file scope'ta izin verilmediği için, genel dizi
xiçeren compilation unit derlenmez - block scope'ta variable length array'e izin verildiğinden, yerel dizi
yiçeren compilation unit derlenebilir - variable length array, implementasyonun desteklemek zorunda olmadığı bir conditional feature olduğundan, bunu desteklemeyen derleyicilerde block scope örneği de derlenmeyebilir
- C++'ta ise her iki compilation unit de derlenir ve C++'ta variable length array kavramı olmadığından
y, 42 elemanlı normal bir dizi olarak derlenir
Fonksiyon bildirimleri, dönüş değerleri, linkage
-
foo()vefoo(void)foo()biçimindeki bir fonksiyon bildirimi, argüman sayısı ve türleri bilinmeyen bir fonksiyonu bildirir;foo(void)ise argümanı olmayan bir nullary function bildirir- Bu fark, fonksiyon bildirimi·tanımı·prototipiyle ilgili yazıda ele alınıyor
- Argüman listesi olmayan bildirim, yalnızca fonksiyon adını tanıttığı ve argüman sayısı ile türlerini tanımlamadığı için, sonraki fonksiyon tanımıyla birleşerek yasal olabilir
- Prototip olmadan fonksiyon çağrılırsa default argument promotions uygulanır ve
float,double'a yükseltilir - Yükseltme sonrası fonksiyon türü, gerçek fonksiyon tanımının türüyle uyumlu değilse bildirim ile tanımın birleşimi geçerli değildir
- Bildirimsiz fonksiyon çağrısı C'de örtük fonksiyon bildirimi sayesinde derlenebilir, ancak C++'ta derleme hatasıdır
- Bildirim olmadan
bar(42)gibi bir çağrı yapılırsa tamsayı argüman yükseltmesi uygulanır ve42,intolarak ifade edilir; bu nedenlebar, herhangi bir dönüş türüTiçinT (*)(int)ile uyumlu değilse bu tanımsız davranış olur
-
Değer döndürmeyen value-returning fonksiyon
- Dönüş türü
intolan bir fonksiyon değer döndürmese bile, C'de çağrı sonucunun değeri kullanılmadığı sürece yasal olabilir - K&R C'de
voidtürü yoktu ve tür belirtilmezse varsayılan tür olarakintkabul ediliyordu; bu yüzden değer döndürmeyen fonksiyonlar ile örtükintkuralı tarihsel olarak bağlantılıdır - Örtük
intkuralı C99'da kaldırıldı; ilgili tartışmalar N661 ve C99 rationale içinde yer alıyor - C17 § 6.9.1 ¶ 12, fonksiyon sonundaki
}karakterine ulaşıldığında ve çağıran taraf fonksiyon çağrısının değerini kullandığında bunun tanımsız davranış olduğunu belirtir - C++98 § 6.6.3 ¶ 2'de, value-returning bir fonksiyonun sonuna kadar akıp gitmek başlı başına değersiz bir
returnile eşdeğerdir ve value-returning fonksiyonda bu, tanımsız davranış olur - C++ derleyicileri,
abort_program()'ın hangi dalda sonlandığını genel olarak kanıtlayamadığı için bu tür durumlarda hata yerine yalnızca tanı koyabilir
- Dönüş türü
-
linkage ve
extern- Önceki bildirimin görünür olduğu bir kapsamda aynı tanımlayıcı
externile yeniden bildirilirse, sonraki bildirimin linkage'ı önceki bildirimin linkage'ı ile aynı olur - C17 § 6.2.2 ¶ 4, önceki bildirim internal veya external linkage belirttiyse sonraki
externbildiriminin de aynı linkage'a sahip olduğunu söyler - Önceki bildirim görünür değilse veya önceki bildirimde linkage yoksa
externtanımlayıcı external linkage'a sahip olur - Ters sıradaki bildirim birleşimleri tanımsız davranış olabilir ve GCC ile Clang bunu yakalar
- Önceki bildirimin görünür olduğu bir kapsamda aynı tanımlayıcı
Niteleyiciler ve tamamlanmamış türler
-
Fonksiyon parametrelerinde
const- Fonksiyon bildiriminde parametre
x,constile nitelenmiş olup fonksiyon tanımında böyle değilse ve fonksiyon gövdesindex'e değer yazılsa bile bu yasaldır - C11 § 6.7.6.3 ¶ 15'e göre, fonksiyon parametresi türü uyumluluğu ve composite type değerlendirilirken, qualified type olarak bildirilen her parametre unqualified version olarak ele alınır
- Aynı konu DR040 içinde de ele alınmıştır
- Fonksiyon bildiriminde parametre
-
Fonksiyon dönüş türünde
const- Yalnızca fonksiyon tanımının dönüş türü
constile nitelenmiş, bildirimin dönüş türü nitelenmemişse bunun yanıtını basitçe doğru ya da yanlış diye vermek zordur - Genel uzlaşı, rvalue üzerindeki niteleyicilerin yok sayılması gerektiği yönündedir; ancak C11'e kadar standart metni bunu açıkça ele almıyordu
- C17'de cast, lvalue conversion ve function declarator bağlamlarında rvalue niteleyicilerinin yok sayılması gerektiği netleşti
- C17 § 6.7.6.3 ¶ 5'te, fonksiyonun döndürdüğü türün
T'nin unqualified version olduğu açıkça belirtilir; bu ifade C17'de eklenmiştir - Dönüş türündeki
constniteliği farklı olsa da fonksiyon türü ataması yasal olabilir - Ek tartışmalar DR423 ve DR481 içinde bulunabilir
- Yalnızca fonksiyon tanımının dönüş türü
-
Tamamlanmamış struct ve global değişken
- Global değişken bildirimi sırasında
struct footamamlanmamış bir tür olup boyutu bilinmese bile, daha sonra aynı translation unit içinde tür tamamlanıyorsa belirli durumlarda buna izin verilir - Benzer mantık global değişkenlere veya tamamlanmamış tür dizilerine de uygulanır
- Bu konu DR016 içinde de ele alınır
- Global değişken bildirimi sırasında
-
voidtüründe external object- Internal linkage'a sahip
voidtüründe değişken bildirimi yasal değildir; ancak external linkage'a sahipvoidtüründe değişken bildirimi sözdizimsel olarak yasaldır ve C11 standardında da açıkça yasaklanmaz - C11 § 6.2.5 ¶ 19'a göre
voidtürü, değerlerin boş kümesinden oluşan tamamlanamayan eksik nesne türüdür - C11 § 6.3.2.1 ¶ 1, lvalue'yu
voiddışındaki bir nesne türünün ifadesi olarak tanımladığı için,voidtüründekifoonesnesinin adı geçerli bir lvalue değildir - C11 açısından external
voidnesnesi üzerinde anlamlı ve conforming bir işlem düşünmek zordur - DR012, tür
const voidyapıldığındafoonesnesinin adresini almanın yasal olduğunu ele alır; bu, amaçlanmış bir özellikten çok bir oversight gibi görünür
- Internal linkage'a sahip
-
İşaretçi-
constdönüşümüTtüretilmiş bir nesne türü olduğundacpataması yasaldır; ancakcppatamasının da yasal olup olmadığına kısa bir yanıt yoktur- Bu konu örtük pointer to const conversion ile ilgili yazıda ele alınıyor
Diziler, string literal'ler, işaretçi ayarlaması
-
Dizi işaretçi değildir
- Dizi başlatma ile işaretçi başlatma eşdeğer değildir
- İlk biçim, otomatik veya statik saklama süresine sahip değiştirilebilir bir diziyi başlatır
- İkinci biçim, statik saklama süresine sahip bir diziyi gösteren bir işaretçiyi başlatır ve bu dizi mutlaka değiştirilebilir olmak zorunda değildir
- Dizi, işaretçi değildir; ayrıntılar ilgili yazıda ele alınıyor
-
a,&a,&a[0]int a[42];içina,&ave&a[0]ifadelerinin tümü dizinin ilk elemanının adresi olarak değerlendirilir- Ancak bu üç ifadenin türleri birbirinden farklıdır, bu yüzden birbirlerinin yerine kullanılamazlar
- Ayrıntılar ilgili yazıda ele alınıyor
-
Dizi parametreleri ve yerel diziler
- Fonksiyon parametresi türü “
Tdizisi” ise bu, “Tgösteren işaretçi”ye ayarlanır - Parametre
x,int[42]gibi görünse de gerçekteint *olarak ele alınır - Yerel değişken
y,int[42]isesizeof(y)değeri42 * sizeof(int)olur - Genel olarak nesne işaretçisinin boyutu 42 tamsayının boyutuna eşit olmadığından
sizeof(x) == sizeof(y)çoğu durumdafalseolur - Ayrıntılar ilgili yazıda ele alınıyor
- Fonksiyon parametresi türü “
Operatörler, değerlendirme sırası ve kontrol akışı
-
x+++y- C'de C++'taki gibi yeni operatörler tanımlanamadığı için
+++gibi yeni bir operatör yoktur x+++y, mevcut operatörlerin birleşimi olarak yorumlanır ve(x++) + yile eşdeğerdir--*--pde yeni bir operatör değil, mevcut operatörlerin birleşimidir--*--p,--(*(--p))ile eşdeğerdir ve örnekte-1olarak değerlendirilir; yan etki olarakx[0]içine-1atanır
- C'de C++'taki gibi yeni operatörler tanımlanamadığı için
-
Aritmetik operandların değerlendirme sırası
- Operatör önceliği iyi tanımlanmıştır, ancak aritmetik operandların değerlendirme sırası tanımlı değildir
(x=1) + (x=2)ifadesinde iki atamanın sırası tanımlı olmadığı içinx'in son değerinin1mi2mi olacağı belirli değildir; bu nedenle tanımsız davranıştır-std=c11 -O2seçeneğinde GCC 8.2.1 örnek ifadeyi4, Clang 7.0.0 ise3olarak değerlendirir
-
Mantıksal operatörlerin değerlendirme sırası
- Mantıksal operatörler
&&ve||için operandların değerlendirme sırası da iyi tanımlanmıştır - C standardının ifadesiyle, ilk operandın değerlendirilmesi ile ikinci operandın değerlendirilmesi arasında bir sequence point bulunur
- Örnekte önce
x=1değerlendiriliptrueolur, ardındanx=2değerlendirilip yinetrueolur; dolayısıyla ifadenin tamamıtrueolur
- Mantıksal operatörler
-
switchiçin serbest gövde yapısıswitchifadesinin gövdesi herhangi bir statement olabilir; bu yüzden loop veifiçeren karışık yapılar da geçerli olabilir- Kontrol ifadesi her zaman
falseolan birififadesinin içindekitruebranch bile, bircaselabel içeriyorsa ilgili statement canlı hale gelir veprintf("1");dead code değildir case 2ye atlandığında loop'un clause-1'i ve kontrol ifadesi çalışmayabilir; bu yüzdenideğişkeni önceden başlatılmış olmalıdırcase 1içindebreakolmadığı için fall through gerçekleşse bile,case 1ifintruebranch'inde vecase 2falsebranch'inde isecase 2atlanıpcase 3ile devam edilebilir- Üç çağrı
foo(0); foo(1); foo(2);sonrasında konsol çıktısı02313223olur - Loop ve switch'in birleştirildiği ünlü gerçek örneklerden biri Duff's device'tır
Geçici nesne ömrü ve C standart sürümleri arasındaki farklar
- Belirli bir kod parçası C11'de tanımsız davranış olabilirken C99'da böyle olmayabilir
- C11'de belirli nesnelerin ömrü kısalmıştır; bir fonksiyon çağrısının döndürdüğü nesne yalnızca sağ tarafın değerlendirildiği süre boyunca yaşar
- C99'da aynı nesne enclosing block'un sonuna kadar yaşar
- Ömrü sona ermiş bir nesneye başvurmak, C11 § 6.2.4 ¶ 2 uyarınca tanımsız davranıştır
- C99'da da automatic storage duration'a sahip nesnelerin ömrü en yakın enclosing block'a bağlıdır; bu nedenle ilgili block dışından nesneye başvurmak tanımsız davranıştır
- C11 § 6.2.4 ¶ 8, struct veya union türünde bir non-lvalue expression array member içeriyorsa, automatic storage duration ve temporary lifetime'a sahip bir nesneye başvurulduğunu belirtir
- Bu geçici nesnenin ömrü ifade değerlendirilirken başlar ve onu içeren full expression veya full declarator değerlendirmesi bittiğinde sona erer
- Temporary lifetime'a sahip bir nesneyi değiştirmeye yönelik girişim tanımsız davranıştır
- İlgili örnek N1285 belgesinden alınmıştır; ek tartışmalar da orada yer alır
1 yorum
Lobste.rs görüşleri
4. soru C23'te geçerli değil ama öncesinde geçerliydi
10. soru ne doğru ne yanlış cevaba sahip, bu yüzden çoktan seçmeli demek için biraz rahatsız edici
15. soru, özellikle 13. soruyla bağlantılı olarak teknik olarak yanlıştı ve 20. soru da “belirtilmemiş” olduğundan yine hiçbir cevap doğru değildi
30. soru ise okunuşuna göre muğlak
Yine de 31 sorunun 27'sini bildim; derleyici geliştiricisi olmamın da biraz faydası oldu
Yaklaşık dört soru çözdükten sonra, C'nin basit olduğu ve yan projelerde kullanılmaya değer olduğu yönündeki içimde kalmış his kayboldu
clang'de-std=<language-standard>-pedantic -Wall -Wextrakullanıp her uyarı çıktığında gerçekten düzeltirseniz ve pointer cast ile pointer manipülasyonundan mümkün olduğunca kaçınırsanız, büyük tuzaklardan uzak durmak mümkün gibi görünüyorGünümüzde GCC/
clanguyarıları oldukça iyi; <language-standard> olarak c89, c99, c11, c23 kullanılabiliyortcc gibi garip optimizasyonlar yapmayan bir derleyici kullanırsanız daha az tuhaf sürpriz yaşarsınız
Sadece “buradaki en saçma davranış ne olabilir?” ölçütüyle seçip 32 sorunun 21'ini bildim
Yanlışlarımın çoğu, o saçmalığın seviyesini yeterince derin düşünmememden kaynaklanıyordu
C'ye en son 15 yıldan daha uzun zaman önce biraz dokunmuştum ama böyle bir test görünce yeniden denemek istemedim
C23'e göre 4. sorunun cevabı geçerli değil
İlginç şekilde, bir süredir C kullanmıyor olmama rağmen 32 sorunun 27'sini bildim
Zaten bu yüzden statik denetleyicilere ve linter'lara güveniyordum
1. sorundan itibaren içime sinmedi
O pointer'ların nereden gelebileceği hesaba katılmamıştı ve orada anlatılan durumun oluşması için çok özel koşullar gerekiyor
Çoğu durumda, o pointer'ları oluşturmaya çalışmak zaten tanımsız davranış ama yine de adil sayılabilir
3. soru gerçekten şaşırtıcıydı; C'nin bir başka tuzağıydı
C tamsayı literal'lerinin baştan belirlenmiş bir tipe sahip olması başlı başına çok sinir bozucu
Tamsayı yükseltme kuralları bunu bir ölçüde dengeliyor ama aynı zamanda hata kaynağı da oluyor
Modern dillerin çoğu, hatta belki hepsi, örtük sayısal cast işlemlerini yasaklamalı; mümkünse literal tipini bağlamdan çıkarmalı, bu mümkün değilse açık cast istemeli
6. sorudan sonra teste güvenemediğim için bıraktım
İlk başta bunun nedeni 5. sorunun cevabının fiilen 6. soruyu yanlış yaptıracak şekilde tasarlanmış olmasıydı ama tekrar bakınca 6. sorunun kendisi de yanlış görünüyor
Açıklama, fonksiyon çağrısının tanımsız davranış olduğunu söylüyor ama soru fonksiyon tanımının yasal olup olmadığını soruyordu ve büyük ihtimalle yasaldı
Ve bu da çok nadir görülen bir şey gibi durmuyor
switch()sorusu gerçekten çok iyiydiZorluydu ama kafada çözme süreci çok keyifliydi