摘要: random()函數(shù)和rand()函數(shù)都可以產(chǎn)生隨機(jī)數(shù),但是,兩者的實(shí)現(xiàn)過程是不一樣的,在使用這兩個(gè)函數(shù)時(shí)總是會(huì)遇到一些疑問。該文結(jié)合實(shí)例分析了rand()函數(shù)產(chǎn)生隨機(jī)數(shù)的過程,對(duì)不同隨機(jī)函數(shù)的使用有一定的指導(dǎo)意義。
關(guān)鍵詞:C++語言; 隨機(jī)函數(shù);隨機(jī)數(shù)
中圖分類號(hào):TP312 文獻(xiàn)標(biāo)識(shí)碼:A 文章編號(hào):1009-3044(2015)34-0104-02
Abstract: random () function and rand () function can generate random numbers, but both the implementation process is not the same when using these two functions will always encounter some doubts. Examples of this paper analyzes the rand () function generates a random number process, the use of different random function has a certain significance.
Key words: C ++ language; random function; random numbers
在C語言中的隨機(jī)數(shù)函數(shù)是randomize()和random(),前者是產(chǎn)生隨機(jī)數(shù)的種子,而后者是根據(jù)前者生成的種子來產(chǎn)生隨機(jī)數(shù)。random()函數(shù)產(chǎn)生的隨機(jī)數(shù)是一種偽隨機(jī)數(shù)。在C++語言的開發(fā)工具中(例如DEV C++)已經(jīng)無法使用randomize()函數(shù)參數(shù)隨機(jī)數(shù)了,取而代之的是rand()函數(shù)[1-3],接下來就分析一下rand()函數(shù)產(chǎn)生隨機(jī)數(shù)的實(shí)現(xiàn)原理。
1 產(chǎn)生隨機(jī)數(shù)的實(shí)例
C++語言編寫的產(chǎn)生隨機(jī)數(shù)的程序,如下代碼所示。
#include "stdlib.h"
#include
using namespace std;
int main()
{for(int i=1;i<=10;i++)
cout< return 0; } 程序運(yùn)行后輸出10個(gè)整數(shù),分別是: 41 18467 6334 26500 19169 15724 11478 29358 26962 24464 為了更加清晰的了解rand( )函數(shù),可以通過工具逆向分析一下這個(gè)程序,逆向生成的主要代碼如下。 CALL DWORD PTRDS:[<&MSVCR90D.rand>] ;調(diào)用rand函數(shù) MOV DWORD PTR SS:[EBP-8],EAX ;rand的返回值賦值保存給[EBP-8] MOV EAX,DWORD PTR SS:[EBP-8] PUSH EAX PUSH OFFSET Randomiz.??_C@_0BF@NKDDNMAK@>; ASCII " Random number = %d" CALL DWORD PTR DS:[<&MSVCR90D.printf>] 首先程序先調(diào)用rand函數(shù),然后把返回值賦值給寄存器EAX,再把EAX返回的隨機(jī)數(shù)賦值給地址[EBP-8]保存,最后把EAX的值當(dāng)做參數(shù)壓棧后在調(diào)用printf()函數(shù)[4]。這就是輸出模塊的底層實(shí)現(xiàn)了,由于我們探尋的是rand()的函數(shù)的實(shí)現(xiàn)原理,所以,接下來探討一下rand函數(shù)的內(nèi)部實(shí)現(xiàn)。 2 rand函數(shù)的內(nèi)部實(shí)現(xiàn) rand函數(shù)的內(nèi)部代碼如下: 54E33085 PUSH ECX 54E33086 CALL MSVCR90D._getptd ;調(diào)用_getptd函數(shù) 54E3308B MOV DWORD PTR SS:[EBP-4],EAX 54E3308E MOV EAX,DWORD PTR SS:[EBP-4] 54E33091 MOV ECX,DWORD PTR DS:[EAX+14] 54E33094 IMUL ECX,ECX,343FD ;帶符號(hào)數(shù)乘法指令 54E3309A ADD ECX,269EC3 ;加法 54E330A0 MOV EDX,DWORD PTR SS:[EBP-4] 54E330A3 MOV DWORD PTR DS:[EDX+14],ECX 54E330A6 MOV EAX,DWORD PTR SS:[EBP-4] 54E330A9 MOV EAX,DWORD PTR DS:[EAX+14] 54E330AC SHR EAX,10 ;邏輯右移 54E330AF AND EAX,7FFF ;和 54E330B4 MOV ESP,EBP 54E330B6 POP EBP 54E330B7 RETN 在rand( )函數(shù)的內(nèi)部調(diào)用了一個(gè)未公開的函數(shù)_getptd(),無法得知_getptd( )函數(shù)的內(nèi)部細(xì)節(jié),不知道這個(gè)函數(shù)做了什么。但是,可以分析整個(gè)函數(shù)的代碼,并結(jié)合rand函數(shù)的外部代碼,可以看出_getptd( )函數(shù)的返回值并沒有被使用。同時(shí),變量[EAX+14]這個(gè)值引起了高度關(guān)注,可以深入分析變量[EAX+14]得出關(guān)于_getptd( )函數(shù)的信息[5]。可以借助OllyDbg動(dòng)態(tài)反匯編工具看一看[EAX+14]的初始值,反匯編代碼如下所示。
5E0C308B 8945 FC MOV DWORD PTR SS:[EBP-4], EAX
5E0C308E 8B45 FC MOV EAX, DWORD PTR SS:[EBP-4], EAX
5E0C3091 8B4814 MOV ECX, DWORD PTR DS:[EAX+14]
5E0C3094 69C9 FD430300 IMUL ECX, ECX, 343FD
5E0C309A 81C1 C29E2600 ADD ECX, 269EC3
5E0C30A0 8B55 FC MOV EDX, DWORD PTR SS:[EBP-4]
5E0C30A3 894A 14 MOV DWORD PTR DS:[EDX+14], ECX
5E0C30A6 8B45 FC MOV EAX, DWORD PTR SS:[EBP-4]
5E0C30A9 8B40 14 MOV EAX, DWORD PTR DS:[EAX+14]
通過反編譯后的結(jié)果,可以看出原來[EAX+14]指向的地址是005A0814,而它的值是0x00000001,也就相當(dāng)于十進(jìn)制數(shù)1。知道了這個(gè)關(guān)鍵點(diǎn),現(xiàn)在可以把rand( )函數(shù)的內(nèi)部代碼簡化一下,如下代碼所示。
MOV ECX, DWORD PTR DS:[EAX+14] ;ECX=000000001
IMUL ECX, ECX,343FD ;帶符號(hào)數(shù)乘法指令
ADD ECX, 269EC3 ;加法
MOV DWORD PTR DS:[EDX+14],ECX
MOV EAX, DWORD PTR DS:[EAX+14]
SHR EAX, 10 ;邏輯右移
AND EAX, 7FFF ;和
MOV ESP, EBP
POP EBP
RETN
rand()通過把[EAX+14]地址的值賦值給ECX,然后再使用帶符號(hào)數(shù)乘法指令和加法指令進(jìn)行運(yùn)算,最后把計(jì)算的結(jié)果重新賦值給[EAX+14],這樣做是覆蓋掉[EAX+14]原先的值00000001,然后在重新計(jì)算的[EAX+14]的值賦值給EAX,EAX在進(jìn)行邏輯右移和和操作[6],通過這些復(fù)雜的運(yùn)算,rand最終生成了一個(gè)隨機(jī)數(shù)。
3 自定義隨機(jī)函數(shù)
經(jīng)過分析,比較清楚的了解了rand( )函數(shù)的實(shí)現(xiàn)原理,此時(shí),就可以自己編寫一個(gè)產(chǎn)生隨機(jī)數(shù)的函數(shù)了,具體實(shí)現(xiàn)代碼如下所示。
#include
#include
int _tmain(int argc, _TCHAR* argv[])
{
int tnum = 0x00000001;
int Rnum = 0;
while(1){
_asm
{
pushad
mov ecx, tnum
imul ecx,ecx,343fdH
add ecx,369ec3H
mov tnum,ecx
mov eax,ecx
shr eax,10H
and eax,7fffH
mov Rnum,eax
popad
}
printf(" %d\t",Rnum);
system(“pause”);
}
return 0;
}
編譯運(yùn)行此程序,輸出的結(jié)果與rand函數(shù)運(yùn)行的結(jié)果是一樣的。
4 結(jié)束語
random()、rand()函數(shù)都可以產(chǎn)生隨機(jī)數(shù),但是在不同的編程語言及開發(fā)工具中的使用是有差別的。random函數(shù)不是ANSI C標(biāo)準(zhǔn),不能在gcc,vc等編譯器下編譯通過。本文分析了rand函數(shù)的內(nèi)部實(shí)現(xiàn)原理,分析了產(chǎn)生隨機(jī)數(shù)的過程,拓展了初學(xué)習(xí)C語言同學(xué)的知識(shí)視野,也激發(fā)他們探索C語言奧妙的興趣。
參考文獻(xiàn):
[1] 周建儒.C語言中邏輯關(guān)系與邏輯運(yùn)算的分析[J].電子測試,2012(22):38-39.
[2] 周志德,候正昌.C++程序設(shè)計(jì)[M].北京:電子工業(yè)出版社,2005.
[3] ??藸枺瑒⒆谔?C++編程思想[M].北京:機(jī)械工業(yè)出版社,2012.
[4] 趙鳳芝.C語言程序設(shè)計(jì)能力教程[M].北京:中國鐵道出版社,2006.
[5] 劉金魁.C語言程序設(shè)計(jì)基礎(chǔ)[M].北京:中國鐵道出版社,2014.
[6] 田美艷.計(jì)算機(jī)中C語言的應(yīng)用特點(diǎn)分析[J].電子制作,2015(4):87.