Başlatılmamış yerel değişken en hızlı rasgele sayı üreteci midir?


329

Başlatılmamış yerel değişkenin tanımsız davranış ( UB ) olduğunu biliyorum ve ayrıca değerin daha fazla işlemi etkileyebilecek tuzak gösterimleri olabilir, ancak bazen rastgele sayıyı yalnızca görsel temsil için kullanmak ve bunları başka bir bölümde kullanmayacak Örneğin, görsel efektte rastgele renk içeren bir şey ayarlayın, örneğin:

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

daha hızlı mı

void updateEffect(){
    for(int i=0;i<1000;i++){
        star[i].setColor(rand()%255,rand()%255,rand()%255);
        star[i].setVisible(rand()%2==0?true:false);
    }
}

ve ayrıca diğer rastgele sayı üretecinden daha hızlı?


88
+1 Bu tamamen meşru bir soru. Uygulamada, başlatılmamış değerlerin rastgele olabileceği doğrudur . Onlar özellikle ve 's UB yapmaz değildir aslında soran o feci.
imallett

35
@imallett: Kesinlikle. Bu iyi bir soru ve geçmişte en az bir eski Z80 (Amstrad / ZX Spectrum) oyunu, programını arazisini kurmak için veri olarak kullandı . Yani emsaller bile var. Bugünlerde bunu yapamam. Modern işletim sistemleri tüm eğlenceyi ortadan kaldırır.
Bathsheba

81
Kesinlikle ana sorun rastgele olmamasıdır.
john

30
Aslında, başlatılmamış bir değişkenin rastgele değer olarak kullanılmasına bir örnek vardır, Debian RNG felaketine bakın ( bu makaledeki Örnek 4 ).
PaperBirdMaster

31
Uygulamada - ve inan bana, çeşitli mimarilerde çok hata ayıklama yapıyorum - çözümünüz iki şey yapabilir: ya başlatılmamış kayıtları oku ya da başlatılmamış belleği. Şimdi "başlatılmamış" belirli bir şekilde rastgele anlamına gelse de, pratikte büyük olasılıkla a) sıfırlar , b) tekrarlayan veya tutarlı değerler (önceden dijital ortam tarafından işgal edilen belleğin okunması durumunda) veya c) sınırlı bir değere sahip tutarlı çöp içerecektir set (önceden kodlanmış dijital veriler tarafından işgal edilen belleğin okunması durumunda). Bunların hiçbiri gerçek entropi kaynağı değildir.
mg30rg

Yanıtlar:


299

Diğerlerinin de belirttiği gibi, bu Tanımsız Davranış (UB).

Uygulamada, (muhtemelen) aslında (bir tür) çalışacaktır. X86 [-64] mimarileri üzerinde başlatılmamış bir kayıttan okumak gerçekten çöp sonuçları üretecek ve muhtemelen kötü bir şey yapmayacaktır (örneğin, kayıtların geçersiz olarak işaretlenebildiği Itanium'un aksine, NaN gibi yayılma hatalarını okur).

Yine de iki ana sorun var:

  1. Özellikle rastgele olmayacak. Bu durumda, yığıntan okuyorsunuz, böylece daha önce orada olanı alırsınız. Bu etkili bir şekilde rastgele, tamamen yapılandırılmış, on dakika önce girdiğiniz şifre veya büyükannenizin kurabiye tarifi olabilir.

  2. Bu gibi şeylerin kodunuza girmesine izin vermek Kötü (büyük 'B') uygulamasıdır. Teknik olarak, derleyici reformat_hdd();tanımsız bir değişkeni her okuduğunuzda eklenebilir. O olmaz , ama yine de bunu yapmak gerekir. Güvenli olmayan şeyler yapma. Ne kadar az istisna yaparsanız, her zaman yanlışlıkla yapılan hatalardan daha güvende olursunuz .

UB ile ilgili en acil sorun, programınızın tüm davranışını tanımsız hale getirmesidir. Modern derleyiciler bunu kodunuzun büyük alanlarını elide etmek için kullanabilir veya zaman içinde geri gidebilir . UB ile oynamak, canlı bir nükleer reaktörünü söken bir Victoria mühendisi gibidir. Yanlış gidecek bir milyon şey var ve muhtemelen temel prensiplerin veya uygulanan teknolojinin yarısını bilmeyeceksiniz. İyi olabilir , ama yine de olmasına izin vermemelisin. Ayrıntılar için diğer güzel cevaplara bakın.

Ayrıca, seni kovurum.


39
@Potatoswatter: Itanium kayıtları, aslında bir "başlatılmamış kayıt" olan NaT (Bir Şey Değil) içerebilir. Itanium'da, yazmadığınız bir kayıttan okumak programınızı iptal edebilir (daha fazla bilgiyi buradan edinebilirsiniz: blogs.msdn.com/b/oldnewthing/archive/2004/01/19/60162.aspx ). Başlatılmamış değerleri okumanın tanımlanmamış davranış olması için iyi bir neden vardır. Ayrıca
Itanium'un

58
Gerçekten "bu tür işler" kavramına itiraz ediyorum. Bugün doğru olmasa da, olmasa da, daha agresif derleyiciler nedeniyle her an değişebilir. Derleyici, herhangi bir okuma ile unreachable()programın yarısını değiştirebilir ve silebilir. Bu pratikte de olur. Bu davranış, inandığım bazı Linux dağıtımlarında RNG'yi tamamen etkisiz hale getirdi; Bu sorudaki çoğu cevap, başlatılmamış bir değerin bir değer gibi davrandığını varsayar. Bu yanlış.
usr

25
Ayrıca, iyi uygulamalar varsayalım, bu kod incelemesinde yakalanmalı, tartışılmalı ve bir daha asla gerçekleşmemelidir. Doğru uyarı bayraklarını kullandığımız için bu kesinlikle yakalanmalı, değil mi?
Shafik Yaghmour

17
@Michael Aslında öyle. Bir programın herhangi bir noktada tanımlanmamış davranışı varsa, derleyici programınızı tanımlanmamış davranışı başlatan önceki kodu etkileyecek şekilde optimize edebilir . Bunun nasıl başa çıkabileceğine dair çeşitli makaleler ve gösteriler var İşte iyi bir tane: blogs.msdn.com/b/oldnewthing/archive/2014/06/27/10537746.aspx (bu, standartta bulunan biti içerir) programınızdaki herhangi bir yol UB'yi çağırıyorsa tüm bahisler kapalıdır)
Tom Tanner

19
Bu cevap, “tanımlanmamış davranışı çağırmak teoride kötüdür, ama pratikte size çok fazla zarar vermez” gibi geliyor . Bu yanlış. UB'ye neden olacak bir ifadeden entropi toplamak, önceden toplanan tüm entropinin kaybolmasına neden olabilir (ve muhtemelen olacaktır ) . Bu ciddi bir tehlikedir.
Theodoros Chatzigiannakis

213

Şunu açıkça söyleyeyim: Programlarımızda tanımlanmamış davranışları çağırmıyoruz . Asla iyi bir fikir değildir, nokta. Bu kuralın nadir istisnaları vardır; örneğin, offsetof uygulayan bir kütüphane uygulayıcısıysanız . Davanız böyle bir istisna altındaysa, bunu zaten biliyorsunuzdur. Bu durumda , başlatılmamış otomatik değişkenlerin kullanılmasının tanımsız davranış olduğunu biliyoruz .

Derleyiciler, tanımlanmamış davranış çevresindeki optimizasyonlarla çok agresif hale geldi ve tanımlanmamış davranışın güvenlik kusurlarına yol açtığı birçok durum bulabiliriz. En kötü şöhretli durum muhtemelen C ++ derleme hatasına cevabımda bahsettiğim Linux çekirdeği boş gösterici kontrol kaldırmadır ? burada tanımlanmamış davranış etrafında bir derleyici optimizasyonu sonlu bir döngüyü sonsuz bir döngüye dönüştürdü.

Diğer şeylerin yanı sıra CERT'in Tehlikeli Optimizasyonlarını ve Nedensellik Kaybını ( video ) okuyabiliriz :

Derleyici yazarları, optimizasyonları iyileştirmek için C ve C ++ programlama dillerinde tanımlanmamış davranışlardan giderek daha fazla faydalanmaktadır.

Sıklıkla, bu optimizasyonlar geliştiricilerin kaynak kodları üzerinde neden-sonuç analizi yapma yeteneğine müdahale eder, yani alt sonuçların önceki sonuçlara bağımlılığını analiz eder.

Sonuç olarak, bu optimizasyonlar yazılımdaki nedenselliği ortadan kaldırır ve yazılım hatalarının, kusurlarının ve güvenlik açıklarının olasılığını arttırır.

Belirsiz değerlerle ilgili olarak, C standart hata raporu 451: Başlatılmamış otomatik değişkenlerin dengesizliği bazı ilginç okumalar yapar. Henüz çözülmemiştir ancak titrek değerler kavramını ortaya koymaktadır, bu da bir değerin belirsizliğinin program boyunca yayılabileceği ve programın farklı noktalarında farklı belirsiz değerlere sahip olabileceği anlamına gelir.

Bunun nerede gerçekleştiğine dair hiçbir örnek bilmiyorum ama bu noktada bunu göz ardı edemeyiz.

Gerçek örnekler, beklediğiniz sonuç değil

Rastgele değerler elde etme olasılığınız düşüktür. Bir derleyici döngüyü tamamen optimize edebilir. Örneğin, bu basitleştirilmiş durumla:

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r ;
    }
}

clang onu optimize eder ( canlı olarak görün ):

updateEffect(int*):                     # @updateEffect(int*)
    retq

veya belki de bu değiştirilmiş durumda olduğu gibi tüm sıfırları alın:

void updateEffect(int  arr[20]){
    for(int i=0;i<20;i++){
        int r ;    
        arr[i] = r%255 ;
    }
}

canlı izle :

updateEffect(int*):                     # @updateEffect(int*)
    xorps   %xmm0, %xmm0
    movups  %xmm0, 64(%rdi)
    movups  %xmm0, 48(%rdi)
    movups  %xmm0, 32(%rdi)
    movups  %xmm0, 16(%rdi)
    movups  %xmm0, (%rdi)
    retq

Bu iki durum da tanımsız davranışların mükemmel kabul edilebilir biçimleridir.

Bir Itanium'daysak , bir tuzak değeri elde edebiliriz :

[...] eğer kayıt özel bir şey olmayan bir değeri tutarsa, birkaç talimat dışında kayıt tuzaklarını okumak [...]

Diğer önemli notlar

UB Kanaryalar projesinde belirtilen gcc ve clang arasındaki farkın , başlatılmamış hafızaya göre tanımlanmamış davranıştan ne kadar istifade edeceklerine dikkat çekmek ilginçtir . Makale notları ( benimki vurgu ):

Tabii ki, bu tür herhangi bir beklentinin dil standardı ve belirli bir derleyicinin yapacağı şeyle ilgisi olmadığı konusunda kendimizle tamamen açık olmalıyız, çünkü bu derleyicinin sağlayıcıları bu UB'yi kullanmak istemiyorsa veya çünkü henüz sömürülmediler . Derleyici sağlayıcıdan gerçek bir garanti yoksa, henüz keşfedilmemiş UB'lerin zaman bombaları olduğunu söylemek isteriz : derleyici biraz daha agresif hale geldiğinde gelecek ay veya gelecek yıl gitmeyi bekliyorlar.

Matthieu M., her C Programcısının Tanımlanmamış Davranış # 2/3 Hakkında Bilmesi Gerekenler'in de bu soru ile alakalı olduğunu belirtiyor. Diğer şeylerin yanı sıra ( benimki vurgu ):

Sandığından da önemli ve korkutucu bir şey olduğunu hemen hemen hiçbir tanımsız davranışlarına göre optimizasyon gelecekte herhangi bir zamanda arabası kod tetiklenen başlayabilirim . Satır içi, döngü açma, bellek yükseltme ve diğer optimizasyonlar iyileşmeye devam edecek ve mevcut olma nedenlerinin önemli bir kısmı yukarıdaki gibi ikincil optimizasyonları ortaya koymaktır.

Bana göre, bu kısmen tatmin edici değil, çünkü derleyici kaçınılmaz olarak suçlanıyor, aynı zamanda C kodunun büyük gövdelerinin sadece patlamayı bekleyen kara mayınları olduğu anlamına geliyor .

Tamlık uğruna, muhtemelen uygulamaların tanımlanmamış davranışı iyi tanımlamayı seçebileceğinden bahsetmeliyim, örneğin gcc, sendikalar arasında punning tipine izin verirken C ++ 'da tanımlanmamış davranış gibi görünüyor . Durum buysa, uygulama bunu belgelemelidir ve bu genellikle taşınabilir olmayacaktır.


1
derleyici çıktı örnekleri için + (int) (PI / 3); gerçek hayattan örnek UB olduğunu, iyi, UB .

2
UB'yi etkin bir şekilde kullanmak, mükemmel bir hacker'ın ticari markasıydı. Bu gelenek muhtemelen 50 yıl veya daha uzun süredir devam ediyor. Ne yazık ki, bilgisayarların artık Kötü İnsanlar nedeniyle UB'nin etkilerini en aza indirmesi gerekiyor. Gerçekten UB makine kodu veya bağlantı noktası okuma / yazma, vb I ile serin şeyler yapmak için nasıl anlamaya keyif aldım OS 90s, işletim sistemi kullanıcı kendilerini korumak gibi değildi.
sfdcfox

1
@sfdcfox, makine kodunda / birleştiricide yapıyorsanız, tanımlanmamış davranış değildi (alışılmadık davranış olabilir).
Caleth

2
Aklınızda belirli bir montaj varsa, bunu kullanın ve uyumsuz C yazmayın. Sonra herkes belirli bir taşınabilir olmayan numara kullandığınızı bilecektir. Ve UB'yi kullanamayacağınız anlamına gelen Kötü İnsanlar değil, Intel vb.
Caleth

2
@ 500-InternalServerError, çünkü genel durumda kolayca algılanamayacakları veya hiç algılanamayacakları için bunlara izin verilmez. Hangi fark daha sonra saptanabilir dilbilgisi ihlalleri. Ayrıca, teoride tespit edilebilecek zayıf biçimlendirilmiş programları teoride güvenilir bir şekilde tespit edilemeyenlerden ayıran, kötü biçimlendirilmiş ve kötü biçimlendirilmiş bir teşhis gerekmemektedir.
Shafik Yaghmour

164

Hayır, korkunç.

Başlatılmamış bir değişken kullanma davranışı hem C hem de C ++ 'da tanımlanmamıştır ve böyle bir şemanın istenen istatistiksel özelliklere sahip olması pek olası değildir.

Bir "hızlı ve kirli" rastgele sayı üreteci istiyorsanız, o zaman rand()en iyi bahis. Uygulanmasında tek yaptığı bir çarpma, toplama ve bir modüldür.

Bildiğim en hızlı jeneratör uint32_t, sözde rastgele değişkenin türü olarak a Ikullanmanızı ve

I = 1664525 * I + 1013904223

ardışık değerler üretmek. Fantezi alan herhangi bir başlangıç ​​değerini I( tohum denir ) seçebilirsiniz . Açıkçası bu satır içi kodlayabilirsiniz. İmzasız tipte standart garantili sarma, modül olarak işlev görür. (Sayısal sabitler bu dikkate değer bilimsel programcı Donald Knuth tarafından elle seçilmiştir.)


9
Sunduğunuz "doğrusal uyumlu" jeneratör basit uygulamalar için iyidir, ancak yalnızca kriptografik olmayan uygulamalar için iyidir. Davranışını tahmin etmek mümkündür. Örneğin bkz . Don Knuth'un kendisi tarafından " Doğrusal uyumlu şifrelemeyi deşifre etme " (IEEE Bilgi Teorisi İşlemleri, Cilt 31)
Jay

24
@ Hızlı ve kirli bir birim değişken değişken ile karşılaştırıldığında olabilir? Bu çok daha iyi bir çözüm.
Mike McMahon

2
rand()bence bu amaca uygun değil ve tamamen reddedilmelidir. Çok arızalı kullanmaya devam etmeye gerek orada gerçekten çok çok yaklaşık olarak hızlı kolaylığı büyük olan bu özgürce lisanslı indir belirli günlerde ve çok üstün rasgele sayı jeneratörler (örn Mersenne Twister)rand()
Jack Aidley

1
rand () başka bir korkunç sorun var: iş parçacıkları içinde denilen bir tür kilit kullanır, kodunuzu önemli ölçüde yavaşlatır. En azından bir evresel sürüm var. C ++ 11 kullanıyorsanız, rastgele API ihtiyacınız olan her şeyi sağlar.
Marwan Burelle

4
Adil olmak gerekirse iyi bir rasgele sayı üreteci olup olmadığını sormadı. Hızlı olup olmadığını sordu. Eh, evet, muhtemelen oruçlu., Ama sonuçlar hiç de rastgele olmayacak.
jcoder

42

İyi soru!

Tanımsız, rastgele olduğu anlamına gelmez. Bir düşünün, küresel başlatılmamış değişkenlerde alacağınız değerler sistem veya sizin / çalışan diğer uygulamalar tarafından orada kaldı. Sisteminizin artık kullanılmayan bellekle ve / veya sistem ve uygulamaların ürettiği değerlerle ne yaptığına bağlı olarak şunları elde edebilirsiniz:

  1. Her zaman aynı.
  2. Küçük bir değer kümesinden biri olun.
  3. Bir veya daha fazla küçük aralıkta değerler alın.
  4. 16/32/64-bit sistemdeki işaretçilerden 2/4/8 ile bölünebilen birçok değeri görün
  5. ...

Alacağınız değerler, sistem ve / veya uygulamalar tarafından hangi rasgele olmayan değerlerin kaldığına bağlıdır. Yani, gerçekten biraz gürültü olacak (sisteminiz artık bellek kullanmıyorsa), ancak çizeceğiniz değer havuzu hiçbir şekilde rastgele olmayacaktır.

Yerel değişkenler için işler daha da kötüleşir çünkü bunlar doğrudan kendi programınızın yığınından gelir. Programınızın bu yığın konumlarını başka bir kodun yürütülmesi sırasında yazması çok iyi bir ihtimaldir. Bu durumda şans şansını çok düşük tahmin ediyorum ve yaptığınız 'rastgele' kod değişikliği bu şansı deniyor.

Rasgelelik hakkında okuyun . Gördüğünüz gibi, rastgelelik çok özel ve elde edilmesi zor bir özelliktir. Sadece izlemesi zor bir şey alırsanız (öneriniz gibi) rastgele bir değer elde edeceğinizi düşünmek yaygın bir hatadır.


7
... ve bu kodu tamamen dolduracak tüm derleyici optimizasyonlarını dışarıda bırakıyor.
Deduplicator

6 ... Hata ayıklama ve sürümde farklı "rastgele" alacaksınız. Tanımsız, yanlış yaptığınız anlamına gelir.
Sql Surfer

Sağ. "Undefined"! = "Keyfi"! = "Rastgele" ile kısaltır veya özetlersim. Bütün bu "bilinmeyenlik" türleri farklı özelliklere sahiptir.
fche

Global değişkenlerin, açıkça başlatılmış olsun veya olmasın, tanımlanmış bir değere sahip olduğu garanti edilir. Bu kesinlikle C ++ ve C için de geçerlidir .
Brian Vandenberg

32

Birçok iyi cevap, ama bir tane daha eklememe ve deterministik bir bilgisayarda hiçbir şeyin rastgele olmadığı noktasını vurgulamama izin verin. Bu, hem sahte-RNG tarafından üretilen sayılar hem de yığın üzerindeki C / C ++ yerel değişkenleri için ayrılmış bellek alanlarında bulunan "rastgele" sayılar için geçerlidir.

AMA ... çok önemli bir fark var.

İyi bir sözde jeneratör tarafından üretilen sayılar, onları gerçekten rastgele çekilişlere istatistiksel olarak benzer yapan özelliklere sahiptir. Örneğin, dağılım eşittir. Çevrim uzunluğu uzundur: döngü tekrarlanmadan milyonlarca rastgele sayı alabilirsiniz. Sekans otomatik olarak ilişkilendirilmez: örneğin, her 2., 3. veya 27. sayıyı alırsanız veya oluşturulan sayılardaki belirli rakamlara bakarsanız garip desenlerin ortaya çıkmaya başlamayacaksınız.

Buna karşılık, yığın üzerinde geride kalan "rasgele" sayılar bu özelliklerin hiçbirine sahip değildir. Değerleri ve görünürdeki rastgelelikleri tamamen programın nasıl oluşturulduğuna, nasıl derlendiğine ve derleyici tarafından nasıl optimize edildiğine bağlıdır. Örnek olarak, bağımsız bir program olarak fikrinizin bir varyasyonu:

#include <stdio.h>

notrandom()
{
        int r, g, b;

        printf("R=%d, G=%d, B=%d", r&255, g&255, b&255);
}

int main(int argc, char *argv[])
{
        int i;
        for (i = 0; i < 10; i++)
        {
                notrandom();
                printf("\n");
        }

        return 0;
}

Bu kodu bir Linux makinesinde GCC ile derlediğimde ve çalıştırdığımda, oldukça nahoş bir şekilde deterministik olduğu ortaya çıkıyor:

R=0, G=19, B=0
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255
R=130, G=16, B=255

Derlenmiş koda bir sökücü ile bakarsanız, olup bitenleri yeniden yapılandırabilirsiniz. Notrandom () öğesine yapılan ilk çağrı, yığının daha önce bu program tarafından kullanılmayan bir alanını kullanıyordu; orada ne olduğunu bilen. Ancak bu notrandom () çağrısından sonra, printf () çağrısı (GCC derleyicisinin aslında putchar () çağrısına optimize ettiği, ancak aldırma) ve yığının üzerine yazdığı bir çağrı var. Dolayısıyla, sonraki ve sonraki zamanlar, notrandom () çağrıldığında, yığın putchar () yürütülmesinden eski veriler içerecek ve putchar () her zaman aynı bağımsız değişkenlerle çağrıldığından, bu eski veriler her zaman aynı olacaktır, çok.

Dolayısıyla, bu davranış hakkında rastgele hiçbir şey yoktur ve bu şekilde elde edilen sayılar, iyi yazılmış bir sözde sayı üretecinin arzu edilen özelliklerinden herhangi birine sahip değildir. Aslında, çoğu gerçek hayat senaryosunda, değerleri tekrarlayıcı ve yüksek korelasyonlu olacaktır.

Gerçekten de, diğerleri gibi, bu fikri bir "yüksek performanslı RNG" olarak atmaya çalışan birini de ciddiye alırım.


1
“Deterministik bir bilgisayarda hiçbir şey rastgele değildir” - Bu aslında doğru değil. Modern bilgisayarlar, ayrı donanım jeneratörleri olmadan gerçek , öngörülemeyen rasgele üretmenizi sağlayan her türlü sensörü içerir . Modern bir mimaride, değerleri /dev/randomgenellikle bu tür donanım kaynaklarından tohumlanır ve aslında “kuantum gürültüsüdür”, yani kelimenin en iyi fiziksel anlamında gerçekten öngörülemez.
Konrad Rudolph

2
Ama sonra, bu deterministik bir bilgisayar değil, değil mi? Artık çevresel girdilere güveniyorsunuz. Her durumda, bu bizi, başlatılmamış bellekte "rastgele" bitlere karşı geleneksel bir sözde RNG'nin tartışmasının ötesine götürür. Ayrıca ... uygulayıcıların rasgele sayıların kriptografik olarak güvenli olduğundan emin olmak için ne kadar yol aldıklarını takdir etmek için / dev / random açıklamasına bakın ... daha ziyade, potansiyel olarak yüksek derecede korelasyonlu sensör okumaları, sadece küçük bir rasgelelik derecesi ile. Oldukça yavaş.
Viktor Toth

29

Tanımsız davranış, derleyicilerin yazarlarının sorunu göz ardı etmekte özgür oldukları anlamına gelir, çünkü programcılar asla ne olursa olsun şikayet etme hakkına sahip olmayacaklardır.

Teoride UB topraklarına girerken her şey olabilir ( burnunuzdan uçan bir daemon dahil) ) normal olarak derleyici yazarlarının umursamayacağı ve yerel değişkenler için değer, o noktada yığın belleğindeki her şey olacaktır .

Bu aynı zamanda içeriğin sıklıkla "garip" fakat sabit veya hafif rastgele veya değişken olacağı, ancak açık bir şekilde belirgin olacağı anlamına gelir (örn. Her yinelemede artan değerler).

Emin sen olamaz o iyi bir rasgele jeneratör olmak bekliyoruz.


28

Tanımsız davranış tanımsız. Bu, tanımsız bir değer elde ettiğiniz anlamına gelmez, programın her şeyi yapabileceği ve hala dil spesifikasyonunu karşılayabileceği anlamına gelir .

İyi bir optimize edici derleyici almalıdır

void updateEffect(){
    for(int i=0;i<1000;i++){
        int r;
        int g;
        int b;
        star[i].setColor(r%255,g%255,b%255);
        bool isVisible;
        star[i].setVisible(isVisible);
    }
}

ve onu bir noop'a derledik. Bu kesinlikle herhangi bir alternatiften daha hızlı. Hiçbir şey yapmayacağı dezavantajı vardır, ancak tanımlanmamış davranışın dezavantajı budur.


3
Bir çok şey, bir derleyicinin amacının, programcıların etki alanı gereksinimlerini karşılayan yürütülebilir dosyalar üretmesine yardımcı olup olmadığına veya amacının, davranışı C Standardının minimum gereksinimleriyle tutarlı olan en "verimli" yürütülebilir dosyayı üretmek olup olmadığına bağlıdır. böyle bir davranışın herhangi bir yararlı amaca hizmet edip etmeyeceğini dikkate almak. Önceki hedefle ilgili olarak, kodun r, g, b için rasgele başlangıç ​​değerleri kullanması veya pratik olması durumunda bir hata ayıklayıcı tuzağının tetiklenmesi, kodu bir nopa dönüştürmekten daha yararlı olacaktır. İkinci hedefle ilgili olarak ...
Supercat

2
... optimal bir derleyici, hangi yöntemin yukarıdaki yöntemin yürütülmesine neden olacağını belirlemeli ve yalnızca bu tür girişler alındığında geçerli olacak herhangi bir kodu ortadan kaldırmalıdır.
supercat

1
@supercat Ya da amacı, C standardına uygun verimli yürütülebilir dosyalar üretirken programcının uyumluluğun yararlı olmayacağı yerler bulmasına yardımcı olabilir. Derleyiciler, GCC'ler gibi Standardın gerektirdiğinden daha fazla teşhis yayarak bu uzlaşma amacını karşılayabilir -Wall -Wextra.
Damian Yerrick

1
Değerlerin tanımsız olması, çevresindeki kodun davranışının tanımsız olduğu anlamına gelmez. Hiçbir derleyici bu işlevi kullanmamalıdır. Girilen her ne olursa olsun, iki işlev çağrısı kesinlikle çağrılmalıdır ZORUNLU; ilki 0 ile 255 arasında üç sayı ile aranmalıdır, ikincisi doğru veya yanlış değerle aranmalıdır. Bir "iyi optimize edici derleyici" fonksiyon parametrelerini rastgele statik değerlere optimize edebilir, değişkenlerden tamamen kurtulabilir, ancak bu kadarıyla gidebilir (işlevlerin kendileri belirli girişlerde nooplara indirgenmedikçe).
Dewi Morgan

@DewiMorgan - çağrılan fonksiyonlar "bu parametreyi ayarla" tipinde olduğu için, giriş derleyicinin söz konusu olduğunu varsaydığı parametrenin mevcut değeri ile aynı olduğunda neredeyse kesinlikle nooplara indirgenir.
Jules

18

Henüz belirtilmedi, ancak tanımlanmamış davranışı çağıran kod yollarının, derleyicinin istediği her şeyi yapmasına izin verilir, örn.

void updateEffect(){}

Doğru döngünüzden kesinlikle daha hızlı olan ve UB nedeniyle mükemmel bir şekilde uyumludur.


18

Güvenlik nedeniyle, bir programa atanan yeni belleğin temizlenmesi gerekir, aksi takdirde bilgiler kullanılabilir ve şifreler bir uygulamadan diğerine sızabilir. Yalnızca belleği yeniden kullandığınızda, 0'dan farklı değerler elde edersiniz. Ve büyük olasılıkla, bir yığın üzerinde önceki değerin sabit olması, çünkü o belleğin önceki kullanımı sabittir.


13

Özel kod örneğiniz muhtemelen beklediğiniz şeyi yapmaz. Her döngü tekrarı teknik olarak r, g ve b değerleri için yerel değişkenleri yeniden oluştururken, pratikte yığın üzerindeki bellek alanı ile aynıdır. Bu nedenle, her bir yinelemeyle yeniden rastgele hale getirilmeyecek ve r, g ve b'nin ayrı ayrı ve başlangıçta ne kadar rastgele olduğuna bakılmaksızın, 1000 rengin her biri için aynı 3 değerleri atayacaksınız.

Gerçekten, eğer işe yaradıysa, neyin yeniden randomize edildiğini çok merak ediyorum. Düşünebildiğim tek şey, bu yığının üstünde piggypacked olan, ara sıra kesilmiş bir kesme olurdu. Belki de, kayıtların döngüde daha fazla yeniden kullanıldığı gerçek bellek konumları yerine kayıt değişkenleri olarak tutan dahili optimizasyon, özellikle ayarlanan görünürlük işlevi özellikle kayıt açsa, hile yapacaktır. Yine de, rastgele olmaktan uzak.


12

Buradaki insanların çoğu tanımsız davranıştan bahsettiği gibi. Tanımsız, geçerli bir tamsayı değeri (neyse ki) alabileceğiniz anlamına gelir ve bu durumda bu daha hızlı olacaktır (rand işlevi çağrısı yapılmadığından). Ama pratik olarak kullanmayın. Şansın her zaman yanınızda olmadığı için bunun korkunç sonuçlar alacağından eminim.


1
Çok iyi bir nokta! Bu pragmatik bir hile olabilir, ama aslında şans gerektiren bir hiledir.
anlamı-önemli

1
Kesinlikle şans yok. Derleyici tanımlanmamış davranışı optimize etmezse, aldığınız değerler mükemmel belirleyici olacaktır (= tamamen programınıza, girdilerine, derleyicisine, kullandığı kütüphanelere, iş parçacığı varsa iş parçacıklarının zamanlamasına bağlıdır). Sorun değil ki sebebi onlar uygulama ayrıntılarını Bu değerler hakkında bağımlı olduğundan.
cmaster - reinstate monica

Kesme yığınını uygulama yığınından ayrı olan bir işletim sisteminin yokluğunda, şans iyi bir şekilde dahil edilebilir, çünkü kesintiler bellek içeriğini sık sık geçerli yığın içeriğinin biraz ötesinde rahatsız edecektir.
supercat

12

Gerçekten kötü! Kötü alışkanlık, kötü sonuç. Düşünmek:

A_Function_that_use_a_lot_the_Stack();
updateEffect();

İşlev A_Function_that_use_a_lot_the_Stack()her zaman aynı başlatmayı yaparsa, yığını üzerinde aynı verilerle bırakır. Bu veri dediğimiz şeydir updateEffect(): her zaman aynı değer! .


11

Çok basit bir test yaptım ve hiç de rastgele değildi.

#include <stdio.h>

int main() {

    int a;
    printf("%d\n", a);
    return 0;
}

Programı her çalıştırdığımda, aynı numarayı yazdı ( 32767benim durumumda) - bundan daha az rastgele olamazsınız. Muhtemelen yığın üzerinde kalan çalışma zamanı kitaplığındaki başlangıç ​​kodu ne olursa olsun budur. Program her çalıştığında aynı başlangıç ​​kodunu kullandığı ve programda çalıştırmalar arasında başka hiçbir şey değişmediği için sonuçlar mükemmel şekilde tutarlıdır.


İyi bir nokta. Sonuç, bu "rasgele" sayı üretecinin kodda nereye çağrıldığına bağlıdır. Rastgele olmaktan ziyade tahmin edilemez.
NO_NAME

10

'Rastgele' ile ne demek istediğinizi tanımlamanız gerekir. Mantıklı bir tanım, aldığınız değerlerin çok az korelasyona sahip olmasını gerektirir. Bu ölçebileceğiniz bir şey. Kontrollü, tekrarlanabilir bir şekilde elde etmek de önemsiz değildir. Yani tanımsız davranış kesinlikle aradığınız şey değildir.


7

Başlatılmamış belleğin "unsigned char *" [örneğin döndürülen bir tampon malloc] kullanılarak güvenli bir şekilde okunabileceği belirli durumlar vardır . Kod, derleyiciyi pencereden dışarı atma endişesi duymadan bu tür bir belleği okuyabilir ve belleğin içerebileceği herhangi bir şey için kodun hazırlanmasının, başlatılmamış verilerin okunmamasını sağlamaktan daha verimli olabileceği zamanlar vardır ( bunun yaygın bir örneği, memcpyanlamlı veriler içeren tüm öğelerin ayrı ayrı kopyalanması yerine kısmen başlatılan arabellek üzerinde kullanılır ).

Bununla birlikte, bu gibi durumlarda bile, herhangi bir bayt kombinasyonu özellikle can sıkıcı olacaksa, okumanın her zaman bu bayt paternini vereceğini (ve belirli bir paternin üretimde can sıkıcı olacağını, ancak gelişimde olmadığını, kod üretimde olana kadar görünmez).

Başlatılmamış belleği okumak, sistemin son açılışından bu yana belleğin hiçbir zaman rastgele olmayan içerikle yazılmadığından ve üretimin yapılıp yapılmadığından emin olabileceğiniz gömülü bir sistemdeki rastgele nesil stratejinin bir parçası olarak yararlı olabilir. bellek için kullanılan işlem, açılış durumunun yarı rastgele biçimde değişmesine neden olur. Kod, tüm cihazlar her zaman aynı verileri verse bile çalışmalıdır, ancak örneğin bir grup düğümün, her birinin düğümlerin yarısına aynı başlangıç ​​değerini veren "çok rasgele" olmayan bir jeneratöre sahip olabildiğince hızlı bir şekilde benzersiz benzersiz kimlikler seçmesi gerektiği durumlarda Kimlik, herhangi bir başlangıç ​​rasgele kaynağa sahip olmamaktan daha iyi olabilir.


2
"baytların herhangi bir kombinasyonu özellikle can sıkıcı olacaksa, okumak her zaman bu bayt desenini verecektir" - siz bu kalıpla başa çıkmak için kod yazana kadar, bu noktada artık can sıkıcı değildir ve gelecekte farklı bir kalıp okunacaktır.
Steve Jessop

@SteveJessop: Kesinlikle. Geliştirme ve üretimle ilgili çizgim de benzer bir görüş aktarmayı amaçlıyordu. Kod, belirsiz bir "Bazı rastgelelikler hoş olabilir" kavramının ötesinde başlatılmamış bellekte olanlarla ilgilenmemelidir. Program davranışı, başlatılmamış bir parçanın içeriğinden etkileniyorsa, ileride edinilecek parçaların içeriği de bundan etkilenebilir.
supercat

5

Diğerlerinin söylediği gibi, hızlı olacak, ancak rastgele değil.

Çoğu derleyicinin yerel değişkenler için yapacağı şey, yığın üzerinde onlar için biraz yer kapmaktır, ancak herhangi bir şeye ayarlamaya zahmet etmemek (standart, gerekmediklerini söyler, bu yüzden neden oluşturduğunuz kodu yavaşlatır?).

Bu durumda, alacağınız değer, daha önce yığında ne olduğuna bağlı olacaktır - bundan önce yüz yerel karakter değişkeni 'Q' olarak ayarlanmış bir işlev çağırırsanız ve sonra işlevini çağırırsanız döndürürse, muhtemelen "rastgele" değerlerinizi sanki memset()hepsini 'Q'lara sanki davranmış gibi görürsünüz .

Daha da önemlisi, bunu kullanmaya çalışan örnek işleviniz için, bu değerler her okuduğunuzda değişmez, her seferinde aynı olur. Böylece aynı renk ve görünürlük için 100 yıldız alacaksınız.

Ayrıca, hiçbir şey derleyicinin bu değeri başlatmaması gerektiğini söylemez - bu nedenle gelecekteki bir derleyici bunu yapabilir.

Genel olarak: kötü fikir, yapma. (bir sürü "akıllı" kod seviyesi optimizasyonu gibi ...)


2
UB nedeniyle bunların hiçbiri garanti edilmese de ne olacağına dair güçlü tahminlerde bulunuyorsunuz . Pratikte de bu doğru değildir.
usr

3

Diğerlerinin de belirttiği gibi, bu tanımlanmamış bir davranıştır ( UB ), ancak "işe yarayabilir".

Başkaları tarafından zaten bahsedilen sorunlar dışında, başka bir sorun görüyorum (dezavantaj) - C ve C ++ dışında herhangi bir dilde çalışmaz. Bu sorunun C ++ ile ilgili olduğunu biliyorum, ama iyi C ++ ve Java kodu olacak kod yazabilirsiniz ve bu bir sorun değil o zaman neden olmasın? Belki bir gün birisi onu başka bir dile taşımak zorunda kalacak ve bunun gibi "sihirli numaralar" UB'nin neden olduğu hataları aramak kesinlikle bir kabus olacaktır (özellikle deneyimsiz bir C / C ++ geliştiricisi için).

Burada başka bir benzer UB hakkında soru var. Sadece bu UB hakkında bilmeden böyle bir hata bulmaya çalıştığınızı hayal edin. C / C ++ 'da böyle garip şeyler hakkında daha fazla okumak istiyorsanız, bağlantıdan gelen soruların cevaplarını okuyun ve bu BÜYÜK slayt gösterisini görün. Kaputun altında ne olduğunu ve nasıl çalıştığını anlamanıza yardımcı olacaktır; sadece "sihir" ile dolu başka bir slayt gösterisi değil. Deneyimli C / c ++ programcılarının bile bundan çok şey öğrenebileceğinden oldukça eminim.


3

Tanımsız dil hakkındaki herhangi bir mantığımıza güvenmek iyi bir fikir değil . Bu yazıda bahsedilen / tartışılan her şeye ek olarak, modern C ++ yaklaşımı / stili ile böyle bir programın derlenemeyebileceğinden bahsetmek istiyorum.

Bu, otomatik özellik avantajı ve aynı için yararlı bağlantı içeren önceki yazımda bahsedildi .

https://stackoverflow.com/a/26170069/2724703

Bu nedenle, yukarıdaki kodu değiştirirsek ve gerçek türleri otomatik olarak değiştirirsek , program bile derlenmez.

void updateEffect(){
    for(int i=0;i<1000;i++){
        auto r;
        auto g;
        auto b;
        star[i].setColor(r%255,g%255,b%255);
        auto isVisible;
        star[i].setVisible(isVisible);
    }
}

3

Düşünme şeklini seviyorum. Gerçekten kutunun dışında. Ancak takas gerçekten buna değmez. Bellek-çalışma zamanı tradeoff , çalışma zamanı için tanımlanmamış davranış dahil olmak üzere bir şey değildir .

İş mantığınız gibi "rastgele" kullandığınızı bilmek size çok rahatsız edici bir duygu vermelidir. Ben yapmam.


3

7757Başlatılmamış değişkenleri kullanmak için cazip olduğunuz her yeri kullanın . Asal sayılar listesinden rastgele seçtim:

  1. tanımlanmış davranış

  2. her zaman 0 olmayacağı garanti edilir

  3. asal

  4. geçersiz kılınmış değişkenler kadar istatistiksel olarak rastgele olması muhtemeldir

  5. değeri derleme zamanında bilindiği için başlatılmamış değişkenlerden daha hızlı olması muhtemeldir


Karşılaştırma için bu
yanıttaki

1

Dikkate alınması gereken bir ihtimal daha var.

Modern derleyiciler (ahem g ++) o kadar akıllıdır ki, hangi talimatların durumu etkilediğini ve neyin etkilemediğini görmek için kodunuzdan geçer ve bir talimatın durumu etkilememesi garanti edilirse, g ++ bu talimatı kaldıracaktır.

İşte olacak. g ++, okuduğunuzu, aritmetik yaptığınızı, kaydettiğinizi, aslında daha fazla çöp üreten bir çöp değeri olduğunu görecektir. Yeni çöpün eskisinden daha yararlı olduğuna dair bir garanti olmadığından, döngünüzle ortadan kalkacaktır. Bloop!

Bu yöntem yararlı, ama işte yapacağım şey. UB'yi (Tanımsız Davranış) rand () hızı ile birleştirin.

Tabii ki, rand()çalıştırılanları azaltın , ancak derleyicide karıştırmak istemediğiniz bir şey yapmayın.

Ve seni kovmayacağım.


Ben bir derleyici kodunuz aptalca bir şey yapıyor ve kaldırmak karar verebilir inanıyorum çok zor buluyorum. Ben sadece kullanılmayan kodu değil, tavsiye kodu optimize etmek için beklenir . Tekrarlanabilir bir test durumunuz var mı? Her iki durumda da, UB'nin önerisi tehlikelidir. Ayrıca, GCC etraftaki tek yetkili derleyici değil, bu yüzden onu "modern" olarak tanımlamak haksızlık.
underscore_d

-1

Başlatılmamış verilerin rasgelelik için kullanılması, düzgün bir şekilde yapılırsa kötü bir şey olmayabilir. Aslında, OpenSSL tam olarak PRNG'yi tohumlamak için bunu yapar.

Görünüşe göre bu kullanım iyi belgelenmemiştir, çünkü birisi Valgrind'in başlatılmamış verileri kullanmaktan şikayet ettiğini fark etti ve "düzeltti" ve PRNG'de bir hataya neden oldu .

Böylece bunu yapabilirsiniz, ancak ne yaptığınızı bilmeniz ve kodunuzu okuyan herhangi birinin bunu anladığından emin olmanız gerekir.


1
Bu, tanımlanmamış davranışla beklenen derleyicinize bağlı olacak, çünkü bugün cevap cilimimden istediklerini yapmayacağım.
Shafik Yaghmour

6
OpenSSL'nin bu yöntemi bir entropi girişi olarak kullandığı, bunun iyi olduğu anlamına gelmez. Sonuçta, kullandıkları diğer entropi kaynağı PID idi . Tam olarak iyi bir rastgele değer değil. Böyle kötü bir entropi kaynağına güvenen birinden, diğer entropi kaynaklarında iyi bir yargı beklemeyeceğim. Umarım şu anda OpenSSL'yi koruyan insanlar daha parlaktır.
cmaster - reinstate monica
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.