Ad alanları
Türevler
Eylemler

Değerlendirilme sırası

cppreference.com sitesinden
< cpp‎ | language
 
 
 
 

Neredeyse tüm C++ operatör işlenenlerinin değerlendirilme sırası (bir fonksiyon çağrısındaki parametrelerin değerlendirilme sırası ve herhangi bir ifade içindeki alt-ifadelerin değerlendirilme sırası da dahil) belirsizdir. Derleyici, işlenenleri herhangi bir sıra ile değerlendirebilir. Hatta aynı ifadenin tekrar eden bir sonraki değerlendirmelerinde başka bir sıralama da tercih edebilir.

Aşağıda belirtildiği üzere bu kuralın istisnaları vardır.

Aşağıda belirtilen durumlar dışında C++'da soldan-sağa ya da sağdan-sola değerlendirme şeklinde bir kavram bulunmamaktadır. Bu, operatörlerin soldan-sağa ya da sağdan-sola ilişkilendirilebilirliğiyle karıştırılmamalıdır: örneğin takip eden f1() + f2() + f3() ifadesi derleyiciler tarafından operator+'nın soldan-sağa ilişkilendirilebilirliği sebebiyle (f1() + f2()) + f3() şeklinde çözümlenmektedir. Ancak burada f3 fonksiyon çağrı ifadesi çalışma zamanında önce, sonra ya da f1() ifadesi ve f2() ifadesi arasında değerlendirilebilir.

Konu başlıkları

[düzenle] Önüne-sıralanma kuralları (C++11'den beri)

[düzenle] Tanımlar

[düzenle] İfade değerlendirme türleri

Her bir ifade ya da alt-ifade için derleyiciler tarafından işlettirilen iki tür 'ifade değerlendirme' söz konusudur (Derleyici açısından her ikisi de seçimliktir, derleyicinin bir ifadeyi değerlendirmesi sonucu 'yan etki' ve 'değer hesaplama' işleri birlikte gerçekleşebilir, veya sadece biri gerçekleşirken diğeri gerçekleşmeyebilir. Her ifade değerlendirme sonucu iki durum da birden oluşmayabilir):

  • değer hesaplama: bir ifade tarafından döndürülen değerin hesaplanmış olmasına verilen addır. Buna, nesnenin hangi tür olduğunun derleyici tarafından tespit edilmesi veya nesneye bir önceki erişim esnasında atanan değerin okunması da dahildir.
  • yan etki: Bilgisayar biliminde bir fonksiyon veya ifadenin, kendi kapsamı dışında kalan bazı verileri değiştirmesi veya yalnızca bir değer döndürmek yerine kendini çağıran fonksiyonla veya dış dünyayla gözlemlenebilir bir etkileşimde bulunması durumuna o fonksiyon veya ifadenin 'yan etkisi' denir. Örneğin, belirli bir fonksiyon yada ifade, global veya statik bir veriyi değiştirir, kendi parametrelerinden birini değiştirir, bir istisna fırlatır, ekrana veya bir dosyaya veri yazar, verileri okur veya diğer yan etkilere neden olan fonksiyonlar çağırır. Yan etkilerin varlığında, bir programın davranışı geçmişe bağlı olabilir; yani değerlendirilme sırası önemlidir. Yan etkileri olan bir fonksiyonu anlamak ve hata ayıklamak, bağlam ve olası geçmişi hakkında bilgi gerektirir. (ör. foo(++i, i++) ifadesi)
[düzenle] Örnek

'yan etki' ve 'değer hesaplama' kavramlarını aşağıdaki örnek ile daha iyi analiz edebiliriz:

#include <iostream>
void foo(int a)
{
    std::cout << a << std::endl;
}
int main()
{
    int i = 5;
    foo(i++);                    // değer hesaplama: i'yi parametre olarak geçir
                                 // yan etki: daha sonra i'yi arttır
 
    std::cout << i << std::endl; // sonuç: i'yi yazdır
}

Output:

5
6

Şimdi yukarıdaki foo fonksiyon-çağrı ifadesi üzerinden derleyicinin ifade 'değerlendirme' aşamalarına yakından bakalım:

1) foo(i++) ifadesinin değerlendirilme süreci başlar
2) fonksiyon parametresi için 'değer hesaplama' süreci başlar
  • i değişkenini getirilir
  • i değişkeni foo fonksiyonuna parametre olarak geçirilir (arttırılmadan önceki değeri fonksiyon içerisine kopyalanır)
  • fonksiyon parametresi için 'değer hesaplama' süreci sonlanır
3) i değişkeninin değeri artırılır, böylece 'yan etki' oluşmuş olur
4) foo fonksiyonunun gövdesi icra edilir
5) foo(i++) ifadesinin değerlendirilme süreci sonlanır

[düzenle] Sıralama

"önüne-sıralanma" aynı thread içinde gerçekleşen değerlendirmeler arasındaki öncelik ilişkisinde önce işlenen ifadeler için kullanılır. Sonra işlettirilecek olan ifadeler için ise "peşine-sıralanma" terimi kullanılır.

  • Eğer A, B'nin önüne-sıralanmış ise, A'nın değerlendirilmesi B'nin ki başlamadan önce bitmiş olur.
  • Eğer B, A'nın önüne-sıralanmış ise, B'nın değerlendirilmesi A'nin ki başlamadan önce bitmiş olur.
  • Eğer A, B'nin önüne-sıralanmamış ve B'de A'nın önüne sıralanmamış ise o zaman iki ihtimal söz konusu olur:
    • A ve B'nin değerlendirilmesi "sırasızdır": herhangi bir sıra ile değerlendirilebilirler. Hatta derleyici birinin değerlendirmesini yarıda kesip diğeri ile devam edebilir (program keyfi olarak farklı fonksiyon çağrılarını oluşturan işlemci yönergeleri araya sokulmuş gibi davranabilir).
    • A ve B'nin değerlendirilmesi "belirsiz-sıralanmış"dır: herhangi bir sıra ile değerlendirilebilirler. Ancak derleyici birinin değerlendirmesini yarıda kesip diğeri ile devam etmez. Öte yandan gelecek sefer sıralama tam tersi şekilde değişebilir.

[düzenle] Kurallar

1) Tam ifade'lerin tüm 'değer hesaplama' ve yan etkileri, yani;
kendilerinden sonra gelen bir diğer 'tam ifade'nin tüm 'değer hesaplama' ve 'yan etki'lerinin önüne-sıralanmıştır (yani önce değerlendirilir). (buna ifadenin sonucuna uygulanan örtülü tür dönüşümleri, geçici nesnelerin yıkıcı fonksiyonlarına yapılan çağrılar, varsayılan üye ilklendiricileri (kümeler ilklendirilirken -bir küme, kullanıcı-tanımlı kurucu fonksiyonu, private veya protected static olmayan üyeleri, temel sınıfı (base class) ve sanal fonksiyonu bulunmayan düz dizi ya da sınıfdır-), ve bir fonksiyon çağrısı ile sonuçlanan tüm dil başlatıcıları/kurucuları/ilklendiricileri dahildir)
2) Herhangi bir operatör'ün işlenenlerinin 'değer hesaplama'ları (ancak 'yan etkiler'i değil), operatörün sonucunun değer hesaplamasının önüne-sıralanır (ancak yan etkileri değil).
3) Bir fonksiyon çağrılacağı zaman (fonksiyon satır-içi (inline) olsun ya da olmasın, açık (explicit) fonksiyon çağrı sözdizimi ile çağrılsın ya da çağrılmasın fark etmeksizin), çağrılacak olan fonksiyonun parametre ifadelerinden herhangi biri ile ilişkili 'yan etki' ve 'değer hesaplama'ları, çağrılacak olan fonksiyonun gövdesindeki her bir ifade ya da deyimin önüne-sıralanmıştır.
4) Yerleşik (kullanıcı-tanımlı olmayan) sonek-arttırma ve sonek-azaltma operatörlerinin 'değer hesaplamaları', o operatörlerin 'yan etkileri'nin önüne-sıralanmıştır.
5) Yerleşik önek-arttırma and önek-azaltma operatörlerinin 'yan etkileri', o operatörlerin 'değer hesaplamaları'nın önüne-sıralanmıştır. ('bileşik atama' tanımı gereği oluşan örtülü kural)
6) Yerleşik mantıksal VE operatörü && ve yerleşik mantıksal VEYA operatörünün || ilk (sol) parametresinin tüm 'değer hesaplama' ve 'yan etkler'i ikinci (sağ) parametrenin tüm 'değer hesaplama' ve 'yan etkleri'nin önüne-sıralanmıştır.
7) koşul operatörü ?: içerisinde bulunan ilk ifadenin tüm 'değer hesaplama' ve 'yan etkiler'i, ikinci veya üçüncü ifadelerin tüm 'değer hesaplama' ve 'yan etkileri'nin önüne-sıralanmıştır.
8) Yerleşik atama operatörünün ve tüm bileşik atama operatörlerinin yan etkileri (sol parametrenin değiştirilme işlemi) sol ve sağ parametrelerin 'değer hesaplama'larının (ancak yan etkiler buna dahil değildir) peşine-sıralanmıştır (önüne-sıralanma'nın tam tersi), ve aynı zamanda (değiştirilmiş nesneye (sol parametreye) referans döndürülmeden önce gerçekleşen) atama ifadesinin 'değer hesaplama' işleminin önüne-sıralanmıştır.
9) Yerleşik virgül operatörü ,'nün ilk (sol) ifadesinin her bir 'değer hesaplama' ve 'yan etkiler'i, ikinci (sağ) ifadesinin her bir 'değer hesaplama' ve 'yan etkiler'nin önüne-sıralanmıştır.
10) Süslü parantezle çevrelenmiş bir liste-ilklendirme ifadesi içerisindeki virgül ile ayrılmış her bir ifade, soldan-sağa olacak şekilde, bir öncekinin tüm 'değer hesaplama' ve 'yan etkiler'i bir sonrakinin önüne-sıralanmıştır.
11) Diğer bir fonksiyonun önüne-sıralanmış ya da peşine-sıralanmış olmayan bir fonksiyon belirsiz-sıralanmıştır. (program, fonksiyon satır-içi olsa bile, farklı fonksiyon çağrılarını oluşturan işlemci yönergeleri araya sokulmuş gibi davranmamalıdır. bkz. as if).
11'inci kuralın bir istisnası vardır: std::execution::par_unseq yürütme politikası altında yürütülen bir standart kütüphane algoritması tarafından yapılan fonksiyon çağrıları "sırasızdır" ve program keyfi olarak farklı fonksiyon çağrılarını oluşturan işlemci yönergeleri araya sokulmuş gibi davranabilir. (C++17'den beri)
12) Tahsis fonksiyonu olan (new operatörü)'ne yapılan çağrı, new-ifadesi'nin kurucu parametrelerin değerlendirilmesi.. ..ile belirsiz-sıralanmıştır (C++17'ye kadar) / ..nin önüne-sıralanmıştır (C++17'den beri).
13) Bir fonksiyondan geriye dönüş yapılırken, fonksiyon çağrısının değerlendirilmesi sonucu oluşan geriye dönüş değerinin kopyalanması işlemi, return deyimi'nin en son işleneniyle birlikte oluşan tüm geçici değişkenlerin ve return deyimini çevreleyen bloğun yerel değişkenlerinin yok edilmesinin önüne-sıralanır.
(C++14'den beri)
14) Bir fonksiyon-çağrısı ifadesinde, fonksiyona adını veren ifade, tüm parametre ifadelerinin (varsayılan parametre ifadeleri de dahil) önüne-sıralanır.
15) Bir fonksiyon çağrısında, her bir parametrenin fonksiyona geçirilmesi sırasında gerçekleşen 'değer hesaplamalar'ı ve 'yan etkiler', fonksiyonun diğer tüm parametrelerinin 'değer hesaplamalar'ı ve 'yan etkiler'i ile belirsiz-sıralanmıştır.
16) Aşırı yüklenen her operatör fonksiyonu, operatör notasyonu kullanarak çağrıldığında, aşırı yüklemiş olduğu yerleşik operatörün sıralama kurallarına uyar.
17) Bir dizi çağırma ifadesinde E1[E2], E1 ifadesinin her 'değer hesaplama' ve 'yan etki'si, E2 ifadesinin tüm 'değer hesaplama' ve 'yan etki'lerinin önüne-sıralanmıştır.
18) Bir üye-işaretçisi ifadesinde E1.*E2 ya da E1->*E2, E1 ifadesinin her 'değer hesaplama' ve 'yan etki'si, E2 ifadesinin tüm 'değer hesaplama' ve 'yan etki'lerinin önüne-sıralanmıştır. (E1 dinamik türü, E2'nin gösterdiği üyeyi içermiyorsa)
19) Bir kaydırma operatör ifadesinde E1<<E2 ve E1>>E2, E1 ifadesinin her 'değer hesaplama' ve 'yan etki'si, E2 ifadesinin tüm 'değer hesaplama' ve 'yan etki'lerinin önüne-sıralanmıştır.
20) Tüm basit atama ifadelerinde E1=E2 ve her bileşik atama ifadesinde E1@=E2, E2 ifadesinin her 'değer hesaplama' ve 'yan etki'si, E1 ifadesinin tüm 'değer hesaplama' ve 'yan etki'lerinin önüne-sıralanmıştır.
21) Parantez içine alınmış bir ilklendiricideki, virgülle ayrılmış ifadeler listesindeki her bir ifade, bir fonksiyon çağrısı için olduğu gibi (aynı şekilde) değerlendirilir (belirsiz-sıralanmış, bkz. 15. madde)
(C++17'den beri)

[düzenle] Tanımsız davranış

ilkel tür (scalar) kısaca: aritmetik türler (int, char, float vb.), işaretçiler T* (herhangi bir T türünü ifade eder), enum, üye-işaretçileri, nullptr_t türü

1) İlkel bir nesne üzerindeki bir yan etki, aynı ilkel nesne üzerindeki başka bir yan etki ile ilişkili olarak "belirsiz-sıralanmış" ise, davranış tanımsızdır.

i = ++i + 2;       // tanımsız davranış C++11'e kadar
i = i++ + 2;       // tanımsız davranış C++17'e kadar
f(i = -2, i = -2); // tanımsız davranış C++17'e kadar
f(++i, ++i);       // tanımsız davranış C++17'e kadar, C++17'den sonra belirsizdir
i = ++i + i++;     // tanımsız davranış

2) İlkel bir nesne üzerindeki bir yan etki, aynı ilkel nesne üzerindeki bir 'değer hesaplama' ile ilişkili olarak "belirsiz-sıralanmış" ise, davranış tanımsızdır.

cout << i << i++; // tanımsız davranış C++17'e kadar
a[i] = i++;       // tanımsız davranış C++17'e kadar
n = ++i + i;      // tanımsız davranış

[düzenle] Sequence point rules (C++11'e kadar)

Buradan ötesi eski C++'ı ilgilendirdiği için (C++11 öncesi) çevrilmemiştir. Siz de kalanını çevirerek katkıda bulunabilirsiniz.

[düzenle] Definitions

Evaluation of an expression might produce side effects, which are: accessing an object designated by a volatile lvalue, modifying an object, calling a library I/O function, or calling a function that does any of those operations.

A sequence point is a point in the execution sequence where all side effects from the previous evaluations in the sequence are complete, and no side effects of the subsequent evaluations started.

[düzenle] Rules

1) There is a sequence point at the end of each full expression (typically, at the semicolon).

2) When calling a function (whether or not the function is inline and whether or not function call syntax was used), there is a sequence point after the evaluation of all function arguments (if any) which takes place before execution of any expressions or statements in the function body.

3) There is a sequence point after the copying of a returned value of a function and before the execution of any expressions outside the function.

4) Once the execution of a function begins, no expressions from the calling function are evaluated until execution of the called function has completed (functions cannot be interleaved).

5) In the evaluation of each of the following four expressions, using the built-in (non-overloaded) operators, there is a sequence point after the evaluation of the expression a.

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

[düzenle] Undefined behavior

1) Between the previous and next sequence point a scalar object must have its stored value modified at most once by the evaluation of an expression, otherwise the behavior is undefined.

i = ++i + i++; // undefined behavior
i = i++ + 1; // undefined behavior
i = ++i + 1; // undefined behavior (well-defined in C++11)
++ ++i; // undefined behavior (well-defined in C++11)
f(++i, ++i); // undefined behavior
f(i = -1, i = -1); // undefined behavior

2) Between the previous and next sequence point, the prior value of a scalar object that is modified by the evaluation of the expression, must be accessed only to determine the value to be stored. If it is accessed in any other way, the behavior is undefined.

cout << i << i++; // undefined behavior
a[i] = i++; // undefined behavior

[düzenle] Defect reports

Şablon:dr list begin Şablon:dr list item Şablon:dr list end

[düzenle] References

Şablon:ref std c++11 Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std Şablon:ref std end

[düzenle] See also

  • Operator precedence which defines how expressions are built from their source code representation.
C documentation for Order of evaluation