Bir C ++ programcısının bilmesi gereken yaygın tanımsız davranışlar nelerdir?
De ki:
a[i] = i++;
Bir C ++ programcısının bilmesi gereken yaygın tanımsız davranışlar nelerdir?
De ki:
a[i] = i++;
Yanıtlar:
NULL
İşaretçinin kaydının silinmesimemcpy
Çakışan arabellekleri kopyalamak için kullanma .int64_t i = 1; i <<= 72
. Tanımsız)int i; i++; cout << i;
)volatile
veya sig_atomic_t
alındığı herhangi bir türde nesnenin değerini kullanmalong int
#if
ifadede tanımlanmış belirteci dinamik olarak oluşturmaİşlev parametrelerinin değerlendirilme sırası belirtilmemiş davranıştır . (Bu, programınızın tanımlanamayan davranışların aksine çökmesini, patlamasını veya pizza siparişi vermez .)
Tek gereksinim, işlev çağrılmadan önce tüm parametrelerin tam olarak değerlendirilmesi gerektiğidir.
Bu:
// The simple obvious one.
callFunc(getA(),getB());
Buna eşdeğer olabilir:
int a = getA();
int b = getB();
callFunc(a,b);
Veya bu:
int b = getB();
int a = getA();
callFunc(a,b);
İkisinden biri olabilir; derleyiciye kalmış. Sonuç, yan etkilere bağlı olarak önemli olabilir.
Derleyici, bir ifadenin değerlendirme bölümlerini yeniden sıralamakta serbesttir (anlamın değişmediği varsayılarak).
Orijinal sorudan:
a[i] = i++;
// This expression has three parts:
(a) a[i]
(b) i++
(c) Assign (b) to (a)
// (c) is guaranteed to happen after (a) and (b)
// But (a) and (b) can be done in either order.
// See n2521 Section 5.17
// (b) increments i but returns the original value.
// See n2521 Section 5.2.6
// Thus this expression can be written as:
int rhs = i++;
int lhs& = a[i];
lhs = rhs;
// or
int lhs& = a[i];
int rhs = i++;
lhs = rhs;
Çift Kontrollü kilitleme. Yapması kolay bir hata.
A* a = new A("plop");
// Looks simple enough.
// But this can be split into three parts.
(a) allocate Memory
(b) Call constructor
(c) Assign value to 'a'
// No problem here:
// The compiler is allowed to do this:
(a) allocate Memory
(c) Assign value to 'a'
(b) Call constructor.
// This is because the whole thing is between two sequence points.
// So what is the big deal.
// Simple Double checked lock. (I know there are many other problems with this).
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
a = new A("Plop"); // (Point A).
}
}
a->doStuff();
// Think of this situation.
// Thread 1: Reaches point A. Executes (a)(c)
// Thread 1: Is about to do (b) and gets unscheduled.
// Thread 2: Reaches point B. It can now skip the if block
// Remember (c) has been done thus 'a' is not NULL.
// But the memory has not been initialized.
// Thread 2 now executes doStuff() on an uninitialized variable.
// The solution to this problem is to move the assignment of 'a'
// To the other side of the sequence point.
if (a == null) // (Point B)
{
Lock lock(mutex);
if (a == null)
{
A* tmp = new A("Plop"); // (Point A).
a = tmp;
}
}
a->doStuff();
// Of course there are still other problems because of C++ support for
// threads. But hopefully these are addresses in the next standard.
Benim favorim "Şablonların somutlaştırılmasında sonsuz özyineleme" dir çünkü tanımlanmamış davranışın derleme zamanında gerçekleştiği tek şey olduğuna inanıyorum.
Tanımlanmamış davranışların yanı sıra , aynı derecede kötü uygulama tanımlı davranış da vardır .
Tanımsız davranış, bir program sonucu standart tarafından belirtilmeyen bir şey yaptığında oluşur.
Uygulama tanımlı davranış, sonucu standart tarafından tanımlanmayan, ancak uygulamanın belgelendirilmesi gereken bir programın eylemidir. Yığın Taşması sorusundan bir örnek "Çokbaytlı karakter değişmezleri", bunu derleyemeyen bir C derleyicisi var mı? .
Uygulama tanımlı davranış yalnızca taşımaya başladığınızda sizi ısırır (ancak derleyicinin yeni sürümüne yükseltmek de taşır!)
Değişkenler bir ifadede yalnızca bir kez güncellenebilir (teknik olarak sıra noktaları arasında bir kez).
int i =1;
i = ++i;
// Undefined. Assignment to 'i' twice in the same expression.
Çeşitli çevresel sınırların temel anlayışı. Tam liste C spesifikasyonunun 5.2.4.1 bölümünde bulunmaktadır. Burda biraz var;
Aslında bir anahtar ifadesi için 1023 vaka etiketleri sınırında biraz şaşırdım, üretilen kod / lex / parsers için oldukça kolay bir şekilde aşıldığını görebilirsiniz.
Bu sınırlar aşılırsa, tanımlanmamış davranışınız (çökmeler, güvenlik kusurları, vb.) Vardır.
Doğru, bunun C belirtiminden olduğunu biliyorum, ancak C ++ bu temel destekleri paylaşıyor.
memcpy
Çakışan bellek bölgeleri arasında kopyalama yapmak için kullanılır . Örneğin:
char a[256] = {};
memcpy(a, a, sizeof(a));
Davranış, C ++ 03 Standardı tarafından toplanan C Standardına göre tanımlanmamıştır.
özet
1 / #incid void * memcpy (void * kısıtlama s1, sabit boşluk * kısıtlama s2, boyut_t n);
Açıklama
2 / Memcpy işlevi s2 ile gösterilen nesneden n1 ile gösterilen nesneye n karakter kopyalar. Kopyalama çakışan nesneler arasında gerçekleşirse, davranış tanımsızdır. 3 döndürür Memcpy işlevi s1 değerini döndürür.
özet
1 # void * memmove (void * s1, const void * s2, size_t n) ekleyin;
Açıklama
2 Memmove işlevi, s2 ile gösterilen nesneden n1 ile gösterilen nesneye n karakter kopyalar. Kopyalama, s2 ile gösterilen nesneden gelen n karakter, ilk olarak s1 ve s2 ile gösterilen nesnelerle çakışmayan geçici bir n karakter dizisine kopyalanır ve daha sonra geçici diziden n karakter kopyalanır nesne s1 ile işaret etti. İadeler
3 Memmove işlevi s1 değerini döndürür.
C ++ 'ın bir boyutu garanti ettiği tek türdür char
. Ve boyut 1'dir. Diğer tüm türlerin boyutu platforma bağlıdır.