Kısa devre mantıksal operatörler zorunlu mu? Ve değerlendirme sırası?


140

ANSI standart mu görev mantıksal operatörler ya da C ya da C ++ 'de, kısa devre olduğu?

K & R kitabını kodunuzun bu işlemlerin kısa devre yapılmasına bağlı olmaması gerektiğini söyleyerek hatırlıyorum, çünkü olmayabilir. Birisi lütfen mantık operasyonlarının her zaman kısa devre yaptığını söylediği standartta işaret edebilir mi? Ben çoğunlukla C ++ ile ilgileniyorum, C için de bir cevap harika olurdu.

Ayrıca değerlendirme sırasının kesinlikle tanımlanmadığını okuduğumu hatırlıyorum (bu yüzden hatırlamıyorum), bu nedenle kodunuzun bir ifadedeki işlevlerin belirli bir sırayla yürütüleceğini varsaymamalıyız: bir ifadenin sonunda tüm başvurulan işlevler ancak derleyicinin en verimli düzeni seçme özgürlüğü vardır.

Standart bu ifadenin değerlendirme sırasını gösteriyor mu?

if( functionA() && functionB() && functionC() ) cout<<"Hello world";

12
Dikkatli: POD tipleri için geçerlidir. Ancak operatöre aşırı yükleme yaparsanız && veya operatörü || belirli bir sınıf için bu DEĞİLDİR ben tekrar kısayol DEĞİLDİR. Bu nedenle bu sınıfları kendi sınıflarınız için tanımlamamanız önerilir.
Martin York

Bu operatörleri bir süre önce, bazı temel boole cebir işlemlerini yapacak bir sınıf yarattığımda yeniden tanımladım. Muhtemelen "bu kısa devreleri ve sol-sağ değerlendirmeleri yok eder!" bunu unutursam. Ayrıca aşırı * / + ve eş anlamlılarını yaptı :-)
Joe Pineda

Bir if-bloğunda fonksiyon çağrılarının olması iyi bir programlama uygulaması değildir. Her zaman yöntemin dönüş değerini tutan ve if bloğunda kullanan bir değişken belirtin.
SR Chaitanya

6
@SRChaitanya Bu doğru değil. Keyfi olarak kötü uygulama olarak tanımladığınız şey her zaman, özellikle de burada booleans döndüren işlevlerle yapılır.
Lorne Marquis

Yanıtlar:


154

Evet, operatörler için ||ve &&hem C hem de C ++ standartlarında kısa devre ve değerlendirme sırası gereklidir .

C ++ standardı diyor (C standardında eşdeğer bir madde olmalıdır):

1.9.18

Aşağıdaki ifadelerin değerlendirilmesinde

a && b
a || b
a ? b : c
a , b

bu ifadelerde operatörlerin yerleşik anlamını kullanarak, ilk ifadenin değerlendirilmesinden sonra bir sıralama noktası vardır (12).

C ++ ekstra tuzak vardır: kısa devre yok DEĞİL türleri olduğu aşırı yük operatörleri için geçerli ||ve &&.

Dipnot 12: Bu paragrafta belirtilen işleçler, 5. maddede açıklandığı gibi yerleşik işleçlerdir. Bu işleçlerden biri geçerli bir bağlamda aşırı yüklendiğinde (madde 13), böylece kullanıcı tanımlı bir işleç işlevi belirlendiğinde, ifade bir işlev çağırma ve işlenenler, aralarında ima edilen bir sıra noktası olmadan bir bağımsız değişken listesi oluşturur .

Çok özel bir gereksiniminiz yoksa genellikle bu işleçlerin C ++ 'da aşırı yüklenmesi önerilmez. Bunu yapabilirsiniz, ancak diğer kişilerin kodlarında beklenen davranışı bozabilir, özellikle de bu operatörler dolaylı olarak bu operatörleri aşırı yükleyen şablonlarla örnekleme yoluyla kullanılırsa.


3
Kısa devre aşırı yüklü mantık ops için geçerli olacağını bilmiyordum, bu bağırsak. Lütfen standarda veya kaynağa bir referans ekleyebilir misiniz? Sana güvenmiyorum, sadece bunun hakkında daha fazla bilgi edinmek istiyorum.
Joe Pineda

4
evet, bu mantıklı. && (a, b) operatörüne argüman görevi görür. ne olduğunu söyleyen onun uygulanmasıdır.
Johannes Schaub - litb

10
litb: B'yi değerlendirmeden && (a, b) operatörüne geçirmek mümkün değildir. Ve b'yi değerlendirmenin bir yolu yoktur, çünkü derleyici yan etkilerin olmadığını garanti edemez.
jmucchiello

2
Bunu üzgün buluyorum. Operatörleri yeniden tanımlamalıydım && ve || ve yine de tam olarak deterministikler , derleyici bunu tespit edecek ve değerlendirmelerini kısa devrede tutacak: sonuçta, düzen ilgisiz, hiçbir yan etki garanti etmiyorlar!
Joe Pineda

2
@Joe: ancak operatörün dönüş değeri ve argümanları boole'den başka bir şeye değişebilir. Ben üç değer ("doğru", "yanlış" ve "bilinmeyen") ile "özel" mantık uygulardım. Dönüş değeri belirleyicidir, ancak kısa devre davranışı uygun değildir.
Alex B

70

Kısa devre değerlendirmesi ve değerlendirme sırası, hem C hem de C ++ için zorunlu bir anlamsal standarttır.

Değilse, böyle bir kod ortak bir deyim olmazdı

   char* pChar = 0;
   // some actions which may or may not set pChar to something
   if ((pChar != 0) && (*pChar != '\0')) {
      // do something useful

   }

Bölüm 6.5.13 C99 teknik özelliklerinin mantıksal VE operatörü (PDF bağlantısı) diyor

(4). Bitsel ikili ve operatörün aksine, && operatörü soldan sağa değerlendirmeyi garanti eder; birinci işlenenin değerlendirilmesinden sonra bir sıralama noktası vardır. İlk işlenen 0 ile karşılaştırılırsa, ikinci işlenen değerlendirilmez.

Benzer şekilde, bölüm Mantıksal VEYA operatörü 6.5.14 diyor

(4) Bitselin aksine | operatörü, || operatör soldan sağa değerlendirmeyi garanti eder; birinci işlenenin değerlendirilmesinden sonra bir sıralama noktası vardır. İlk işlenen 0 ile eşit değilse, ikinci işlenen değerlendirilmez.

Benzer ifadeler C ++ standartlarında bulunabilir, bu taslak metindeki bölüm 5.14'e bakın . Damaların başka bir cevapta not ettiği gibi, && veya || 'yı geçersiz kılarsanız, her iki işlenen de normal bir işlev çağrısı haline geldiğinden değerlendirilmelidir.


Ah, aradığım şey! Tamam, o değerlendirme sırası hem ve kısa devre ANSI-C 99 uyarınca görevlendirilmiş! ANSI-C ++ için eşdeğer referansı görmek isterim, ancak neredeyse% 99 aynı olmalıyım.
Joe Pineda

C ++ standartları için iyi bir ücretsiz bağlantı bulmak zor, bazı googling ile buldum taslak bir kopyaya bağlantı var.
Paul Dixon

POD Türleri için geçerlidir. Ancak operatörü && veya operatörü aşırı yüklerseniz || bunlar kısayol değildir.
Martin York

1
evet bool için, her zaman garantili değerlendirme sırası ve kısa devre davranışına sahip olacağınızı belirtmek ilginçtir. çünkü iki yerleşik tip için operatörü && aşırı yükleyemezsiniz. farklı davranması için işlenenlerde en az bir kullanıcı tanımlı türe ihtiyacınız vardır.
Johannes Schaub - litb

Keşke hem Dama'yı hem de bu cevabı kabul edebilseydim. Çoğunlukla C ++ ile ilgilendiğim için, diğerini kabul ediyorum, ancak bu da mükemmel olduğunu itiraf etmeliyim! Çok teşekkür ederim!
Joe Pineda

19

Evet, zorunlu kılar (hem değerlendirme sırası hem de kısa devre). Örneğinizde, tüm işlevler true değerini döndürürse, çağrıların sırası kesinlikle functionA, sonra functionB ve sonra functionC'den gelir. Bunun için kullanılır

if(ptr && ptr->value) { 
    ...
}

Virgül operatörü için aynı:

// calls a, then b and evaluates to the value returned by b
// which is used to initialize c
int c = (a(), b()); 

Bir sol ve sağ işlenen arasında söyler &&, ||, ,ve birinci ve ikinci / üçüncü işlenen arasında ?:(şartlı operatör) bir "sekans noktası" dır. Herhangi bir yan etki bu noktadan önce tamamen değerlendirilir. Yani, bu güvenli:

int a = 0;
int b = (a++, a); // b initialized with 1, and a is 1

Virgül operatörünün, şeyleri ayırmak için kullanılan sözdizimsel virgülüyle karıştırılmaması gerektiğini unutmayın:

// order of calls to a and b is unspecified!
function(a(), b());

C ++ Standardı şöyle diyor 5.14/1:

&& operatör grupları soldan sağa. İşlenenler her ikisi de dolaylı olarak bool türüne dönüştürülür (madde 4). Her iki işlenen de doğru ve yanlışsa sonuç doğrudur. &, &&, soldan sağa değerlendirmenin aksine: ilk işlenen yanlışsa ikinci işlenen değerlendirilmez.

Ve içinde 5.15/1:

|| operatör grupları soldan sağa. Her iki işlenen de dolaylı olarak boole dönüştürülür (madde 4). İşlenenlerinden herhangi biri doğruysa true değerini, aksi halde false değerini döndürür. Aksine |, || soldan sağa değerlendirmeyi garanti eder; ayrıca, ilk işlenen doğru olarak değerlendirilirse ikinci işlenen değerlendirilmez.

Her ikisinin de yanında şunları söylüyor:

Sonuç bir bool. Geçici ifadelerin (12.2) imha edilmesi dışında ilk ifadenin tüm yan etkileri ikinci ifadenin değerlendirilmesinden önce meydana gelir.

Buna ek olarak, 1.9/18diyor

İfadelerin her birinin değerlendirilmesinde

  • a && b
  • a || b
  • a ? b : C
  • a , b

bu ifadelerde (5.14, 5.15, 5.16, 5.18) operatörlerin yerleşik anlamını kullanarak, ilk ifadenin değerlendirilmesinden sonra bir sıralama noktası vardır.


10

İyi eski K&R'den:

C garanti eder &&ve ||soldan sağa değerlendirilir - yakında bunun önemli olduğu durumları göreceğiz.


3
K&R 2. baskı s40. "&& veya || ile bağlanan ifadeler soldan sağa değerlendirilir ve sonucun gerçeği veya yanlışlığı bilinir bilinmez değerlendirme durur. Çoğu C programı bu özelliklere güvenir." Alıntıladığınız metni kitabın hiçbir yerinde bulamıyorum. Bu çok eski 1. baskıdan mı? Lütfen bu metni nerede bulduğunuzu açıklayın.
Lundin

1
Tamam bu eski öğretici alıntı olduğunu ortaya çıkıyor . 1974'ten beri ve son derece alakasız.
Lundin

6

Çok dikkatli olun.

Temel tipler için bunlar kısayol operatörleridir.

Ancak bu işleçleri kendi sınıfınız veya numaralandırma türleriniz için tanımlarsanız kısayol değildir. Bu farklı koşullar altında kullanımlarındaki bu anlamsal farklılık nedeniyle, bu operatörleri tanımlamamanız önerilir.

İçin operator &&ve operator ||değerlendirme sırası zor olurdu sağ (Aksi kısa kesim bırakılır temel türleri için :-) Ama tanımladığınız aşırı operatörler için bu nedenle temelde sözdizimsel bir yöntem tanımlamaya şeker ve parametrelerin değerlendirilmesi olduğu sırası vardır Tanımsız.


1
Operatör aşırı yüklemesinin POD olması ya da olmaması ile ilgisi yoktur. Bir işleç işlevi tanımlamak için, bağımsız değişkenlerden en az birinin bir sınıf (veya yapı veya birleşim) veya bir numaralandırma veya bunlardan birine başvuru olması gerekir. POD olmak, üzerinde memcpy kullanabileceğiniz anlamına gelir.
Derek Ledbetter

Ben de öyle diyordum. Sınıfınız için && aşırı yüklerseniz, o zaman gerçekten sadece bir yöntem çağrısı. Böylece parametrelerin değerlendirme sırasına güvenemezsiniz. Açıkçası & POD türleri için aşırı yükleme yapamazsınız.
Martin York

3
"POD türleri" terimini yanlış kullanıyorsunuz. Herhangi bir yapı, sınıf, birleşim veya numaralandırma, POD için aşırı yükleme yapabilirsiniz. Her iki taraf da sayısal türler veya işaretçilerse aşırı yükleyemezsiniz.
Derek Ledbetter

POD (char / int / float vb) olarak bir agresif POD (ne hakkında konuşuyorsunuz) olarak kullanıyordum ve genellikle yerleşik bir tür olmadığı için ayrı ayrı veya daha açık olarak atıfta bulunuluyor.
Martin York

2
Yani "temel türler" demek istediniz ama "POD türleri" yazdınız mı?
Öö Tiib

0

Sorunuz C ++ operatör önceliğine gelir ve ilişkilendirilebilirliği ile ilgilidir. Temel olarak, birden çok işleç içeren ve parantez içermeyen ifadelerde, derleyici bu kuralları izleyerek ifade ağacını oluşturur.

Öncelik için, benzer bir şeye sahip olduğunuzda A op1 B op2 C, şeyleri ya (A op1 B) op2 Cya olarak gruplayabilirsiniz A op1 (B op2 C). Eğer op1önceliği daha yüksekse op2, ilk ifadeyi alırsınız. Aksi takdirde, ikincisini alırsınız.

İlişkilendirilebilirlik için, benzer bir şeye sahip A op B op Colduğunuzda, tekrar incileri (A op B) op Cveya olarak gruplayabilirsiniz A op (B op C). Eğerop sol birleşim vardır, biz ilk ifadesi ile bitirmek. Eğer doğru ilişki varsa, ikincisiyle sonuçlanırız. Bu aynı öncelik seviyesindeki operatörler için de geçerlidir.

Bu özel durumda, &&önceliğe göre daha yüksek önceliğe sahiptir ||, bu nedenle ifade şu şekilde değerlendirilecektir:(a != "" && it == seqMap.end()) || isEven .

Düzenin kendisi, ifade ağacı formunda "soldan sağa" şeklindedir. Bu yüzden önce değerlendireceğiz a != "" && it == seqMap.end(). Eğer doğruysa, tüm ifade doğrudur, aksi takdirde biz gideriz isEven. Prosedür elbette sol alt ifade içinde kendini tekrar tekrar tekrarlar.


İlginç tidbitler, ancak öncelik kavramının kökeni matematik notasyonundadır. Aynı şey a*b + c, *önceliğe göre daha yüksek önceliğe sahiptir +.

Daha da ilginç / anlaşılmaz, A1 op1 A2 op2 ... opn-1 Antüm operatörlerin aynı önceliğe sahip olduğu belirsiz bir ifade için , oluşturabileceğimiz ikili ifade ağaçlarının sayısı Katalan sayıları tarafından verilir . Büyük için n, bunlar son derece hızlı büyür. d


Bütün bunlar doğrudur, ancak değerlendirme sırası ve kısa curcuiting ile değil operatör önceliği ve ilişkilendirilebilirlik ile ilgilidir. Bunlar farklı şeyler.
Thomas Padron-McCarthy

0

Wikipedia'ya güveniyorsanız:

[ &&ve ||] anlam bakımından bit-bilge operatörlerinden farklıdır & ve | çünkü sonuç yalnızca soldan belirlenebiliyorsa asla doğru işleneni değerlendirmeyeceklerdir

C (programlama dili)


11
Bir standardımız olduğunda neden wiki'ye güvenelim!
Martin York

1
Wikipedia'ya güveniyorsanız, 'Wikipedia güvenilir bir kaynak değildir' .
Lorne Marquis

C ++ 'da aşırı yüklenmiş operatörler kısa devre yapmadığından, bu gittiği sürece doğrudur, ancak eksiktir.
Thomas Padron-McCarthy
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.