C, ilk 27 yılını boolean türü olmadan geçirdi. Bu bir gözden kaçırma değil - sonuçlarıyla daha ilk gün tanıştığınız bir tasarım kararı ve doğrudan ondan doğan meşhur bir hata.
Sıfır Yanlıştır, Geri Kalan Her Şey Doğrudur
C'nin koşul kurallarında özel bir doğru/yanlış değeri yoktur. if, while, for, &&, || ve ! hepsi tam olarak tek bir soru sorar: bu değer sıfır mı?
Negatif sayılar doğrudur. Kesirler doğrudur. Boş olmayan bir gösterici doğrudur. Yalnızca sıfır - hangi türde olursa olsun - yanlıştır.
C kodunun, kuralı bilene kadar aşırı kısa görünen deyimlerle dolu olmasının sebebi budur:
if (count) /* count sıfır değilse */
if (!count) /* count sıfırsa */
if (p) /* p NULL değilse */
if (!strcmp(a, b)) /* metinler eşitse - strcmp eşleşmede 0 döndürür */
Sonuncusu insanları yakalar. strcmp, metinler eşleştiğinde 0 döndürür, dolayısıyla "eşit" bir if içinde "değil" diye okunur. if (strcmp(a, b) == 0) yazmak daha anlaşılırdır ve tam olarak aynı şeyi yapar.
Karşılaştırmalar int Üretir
C'de bir karşılaştırma özel bir tür ifade değildir. Değeri 1 ya da 0 olan sıradan bir int'e değerlenir; onu yazdırabilir, saklayabilir ya da onunla aritmetik yapabilirsiniz:
! operatörü de aynı şekilde çalışır: işleneni sıfırsa 1, değilse 0 üretir. Dolayısıyla !!x, herhangi bir değeri tam olarak 0 ya da 1'e normalleştiren klasik bir deyimdir.
bool, true ve false (C99)
C99, _Bool adında gerçek bir boolean türü ve ona okunaklı bir isim veren bir başlık ekledi:
<stdbool.h> küçücüktür: bool'u _Bool, true'yu 1, false'u 0 olarak tanımlar. Başlığın tamamı budur. Ama onu kullanmak niyeti, int flag = 1;'in asla yapamayacağı biçimde görünür kılar.
_Bool'un gerçekten farklı tek bir davranışı vardır: yalnızca 0 ya da 1 saklar. Sıfırdan farklı herhangi bir değer atamak tam olarak 1'e dönüşür:
printf içinde booleanlar için hâlâ bir %b yoktur - onları %d ile yazdırın ya da kendiniz metne dönüştürün:
C23'te bu daha da basitleşti: bool, true ve false gerçek anahtar kelimeler oldu, dolayısıyla include artık gerekli değil. Bugünkü kodun çoğu hâlâ C17'yi hedefliyor, o yüzden #include <stdbool.h>'yi tutun.
Bir Fonksiyondan Boolean Döndürmek
bool'un yerini en açık biçimde hak ettiği yer burasıdır:
bool dönüş tipi, çağırana değerin ne anlama geldiğini söyler. check_file adlı bir fonksiyondan gelen int dönüş tipi bir boolean, bir sayı ya da bir hata kodu olabilir - ve C kütüphane geleneklerinde çoğu zaman üçüncüsüdür, orada 0 başarı demektir. Türü adlandırmak tahmini ortadan kaldırır.
= ile == Hatası
Atama, atanan değeri üreten bir ifade olduğu ve sıfırdan farklı her değer doğru olduğu için bu derlenir:
Aynı anda iki şey ters gitti. Koşul x'e 5 atayarak değerini yok etti ve sonra sıfırdan farklı olan 5'i test etti, dolayısıyla dal çalıştı. Programda varsayılan olarak ne hata ne de uyarı var.
Daha kötüsü, 0 içeren sürüm dalını hiç çalıştırmaz:
if (found = 0) { /* asla çalışmaz - ve found artık 0 */ }
Yararlılık sırasıyla üç savunma:
Uyarılarla derleyin. -Wall bunu yakalar:
warning: suggest parentheses around assignment used as truth value
Sıfır uyarıya ulaşmak buradaki asıl çözümdür ve bedeli tek bir bayraktır.
Sabiti başa yazın. "Yoda koşulu" yazım hatasını bir derleme hatasına dönüştürür:
if (5 == x) /* doğru */
if (5 = x) /* hata: lvalue değil - derleyici sizi durdurur */
Bazı ekipler buna bayılır, bazıları okunmaz bulur; her iki tutum da savunulabilir, ama işe yaradığı kesin.
Kastediyorsanız bilinçli olun. Koşul içinde atama, girdi okumak için gerçekten yararlıdır:
int c;
while ((c = getchar()) != EOF) { ... }
c = getchar() çevresindeki fazladan parantezler öncelik için gereklidir ve aynı zamanda atamanın kasıtlı olduğunu işaret eder. Deyim budur - koşul içindeki atamanız buna benzemiyorsa, muhtemelen bir yazım hatasıdır.
Sık Yapılan Boolean Hataları
true ile karşılaştırmak. if (flag == true), stdbool.h ile çalışır, ama flag 42 tutan bir int olduğu anda bozulur, çünkü 42 == 1 yanlıştır. if (flag) yazın.
Zincirleme karşılaştırmalar. if (0 < x < 10) her zaman doğrudur: 0 < x ifadesi 0 ya da 1 üretir ve ikisi de 10'dan küçüktür. if (x > 0 && x < 10) yazın.
Mantıksal yerine bit düzeyi operatörler. & ve |, && ve || değildir. Kısa devre yapmazlar ve bitleri birleştirirler:
Her iki işlenen de "doğru", yine de a & b sonucu 0. && kastedip & kullanmak, yalnızca belirli değerler için yanlış olan bir koşul üretir ki bu, bulunması en zor hata türüdür.
Kayan noktalı sayıları eşitlik için karşılaştırmak. if (0.1 + 0.2 == 0.3) yanlıştır. Bunun yerine bir tolerans ile karşılaştırın - nedeni için bkz. veri tipleri.
Sıkça Sorulan Sorular
C'nin boolean türü var mı?
C99'dan beri var: _Bool yerleşik bir türdür ve <stdbool.h> eklemek size daha okunaklı bool, true ve false yazımlarını verir. C99'dan önce hiç boolean yoktu - programlar yanlış için 0, doğru için 1 kullanarak int ile idare ederdi; bu hâlâ çalışır ve hâlâ yaygındır.
C'de neler doğru sayılır?
Sıfırdan farklı her değer. Bir koşulda 1, -5, 0.01 ve boş olmayan bir gösterici doğrudur; yalnızca 0, 0.0 ve boş gösterici yanlıştır. Ayrı bir boolean kontrolü yoktur - if (x) yalnızca x'in sıfırdan farklı olup olmadığını test eder.
C'de bool nasıl kullanılır?
#include <stdbool.h> ekleyin, ardından bool ready = true; beklediğiniz gibi çalışır. Perde arkasında bool, yalnızca 0 ya da 1 saklayan _Bool'dur - ona sıfırdan farklı herhangi bir değer atamak tam olarak 1 saklar.
C'de = ile == arasındaki fark nedir?
= atar, == karşılaştırır. if (x = 5), x'e 5 atar ve sonra sıfırdan farklı olan 5'i test eder, dolayısıyla dal her zaman çalışır. if (x == 5) ise eşitliği test eder. Birincisi hatasız derlenir; -Wall ile if (5 == x) yazma alışkanlığı bu yüzden vardır.