日本综合一区二区|亚洲中文天堂综合|日韩欧美自拍一区|男女精品天堂一区|欧美自拍第6页亚洲成人精品一区|亚洲黄色天堂一区二区成人|超碰91偷拍第一页|日韩av夜夜嗨中文字幕|久久蜜综合视频官网|精美人妻一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時(shí)間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
分析程序員在代碼編程中的“末行效應(yīng)”

我研究過數(shù)百個(gè)因“拷貝-粘貼”導(dǎo)致的錯(cuò)誤??梢钥隙ǖ氖牵绦騿T常常會(huì)在一大段代碼的***一段里犯錯(cuò)。好像還沒有任何編程書討論過這種現(xiàn)象,因此我決定自己寫點(diǎn)什么。我稱之為“末行效應(yīng)”。

創(chuàng)新互聯(lián)服務(wù)項(xiàng)目包括多倫網(wǎng)站建設(shè)、多倫網(wǎng)站制作、多倫網(wǎng)頁制作以及多倫網(wǎng)絡(luò)營(yíng)銷策劃等。多年來,我們專注于互聯(lián)網(wǎng)行業(yè),利用自身積累的技術(shù)優(yōu)勢(shì)、行業(yè)經(jīng)驗(yàn)、深度合作伙伴關(guān)系等,向廣大中小型企業(yè)、政府機(jī)構(gòu)等提供互聯(lián)網(wǎng)行業(yè)的解決方案,多倫網(wǎng)站推廣取得了明顯的社會(huì)效益與經(jīng)濟(jì)效益。目前,我們服務(wù)的客戶以成都為中心已經(jīng)輻射到多倫省份的部分城市,未來相信會(huì)繼續(xù)擴(kuò)大服務(wù)區(qū)域并繼續(xù)獲得客戶的支持與信任!

我叫Andrey Karpov,我的工作有點(diǎn)不尋常:我借助靜態(tài)分析工具研究各種應(yīng)用程序代碼,并描述從中找到的錯(cuò)誤或者缺陷。我這么做既有實(shí)際效益也因?yàn)楣ぷ餍枰?。使用的方法正是基于我們公司所推廣的PVS-Studio和CppCat工具的原理。套路很簡(jiǎn)單:找bug,然后寫文章分析bug,文章吸引到潛在用戶的注意,接著就是收益。但今天這篇文章不是介紹這些工具的。

在分析各種軟件項(xiàng)目的過程中,我把找到的bug以及相關(guān)代碼存入一個(gè)特殊的數(shù)據(jù)庫。順便說一下,有興趣的話各位可以看一看這個(gè)數(shù)據(jù)庫。我們把它轉(zhuǎn)換成網(wǎng)頁格式并上傳到了公司網(wǎng)站的“Detected errors”欄下。

這個(gè)數(shù)據(jù)庫***!目前它收錄了1500塊問題代碼片,正等著程序員們?nèi)パ芯浚瑥闹锌偨Y(jié)出特定規(guī)律。為將來的研究,手冊(cè)和文章奠定一個(gè)基礎(chǔ)。

我還沒認(rèn)真地分析過目前搜集到的材料。但是過程中我發(fā)現(xiàn)有一個(gè)明顯的模式反復(fù)出現(xiàn),決定深入研究一下。你大概看到了,文中我反復(fù)使用短語“注意***一行”。在我看來,這一定有某種規(guī)律。

末行效應(yīng)

編程的時(shí)候,程序員常常需要寫一系列相似的結(jié)構(gòu)。逐行敲鍵盤輸入無聊且低效。這就是為什么他們會(huì)使用奧義-“拷貝-粘貼”大法:一段代碼被拷貝粘貼幾次,然后修改。誰都知道這樣做的壞處:你很容易在粘貼后忘記修改某些內(nèi)容***滋生出問題。不幸的是,常常找不到比這更好的方法。

那么我發(fā)現(xiàn)了什么規(guī)律呢?我發(fā)現(xiàn)錯(cuò)誤常常發(fā)生在***的一塊粘貼代碼里。

下面是一個(gè)簡(jiǎn)短的例子:

 
 
  1. inline Vector3int32& operator+=(const Vector3int32& other) { 
  2.   x += other.x; 
  3.   y += other.y; 
  4.   z += other.y; 
  5.   return *this; 

注意這一行:”z += other.y;”。程序員忘記把‘y’替換成‘z’了。

也許你以為這是個(gè)假設(shè)的例子,然后它其實(shí)來自一個(gè)真實(shí)的應(yīng)用程序。接下來,我會(huì)讓你相信這是高頻常見的一種錯(cuò)誤。程序員們經(jīng)常在一連串相似操作的結(jié)尾犯這種錯(cuò)誤。

我聽說攀巖者常常在***的幾十米中滑落下來。并不是因?yàn)樗麄兝哿?,而正是由于他們?duì)即將到達(dá)的終點(diǎn)過于興奮,他們想象著成功后的喜悅,變得疏忽大意,***失足。我猜想程序員們也是這樣的。

接下來看一組數(shù)據(jù)。

研究了數(shù)據(jù)庫后,我分離出了84個(gè)代碼段由“拷貝-粘貼”大法生成。其中41段中錯(cuò)誤發(fā)生在中間的某些粘貼塊。比如:

 
 
  1. strncmp(argv[argidx], "CAT=", 4) && 
  2. strncmp(argv[argidx], "DECOY=", 6) && 
  3. strncmp(argv[argidx], "THREADS=", 6) && 
  4. strncmp(argv[argidx], "MINPROB=", 8)) { 

“THREADS=”字符串的長(zhǎng)度是8個(gè)字符,而非6。

另外的43段代碼中,錯(cuò)誤發(fā)生在***的粘貼塊。

當(dāng)然,43比41大不了多少。但是請(qǐng)注意,一段程序中,可能有很多類似的代碼塊,因此錯(cuò)誤可能發(fā)生在***,第二,第五甚至第十塊中。因此在其他代碼塊中我們有一個(gè)相對(duì)均勻的分布,而***一塊卻存在一個(gè)峰值。

平均而言,相似代碼塊總數(shù)為5。

于是前面4個(gè)代碼塊中均勻分布了41處錯(cuò)誤,平均每塊代碼有10個(gè)錯(cuò)誤。

然而***一塊代碼中有43個(gè)錯(cuò)誤!

下面的分布概圖凸顯出這個(gè)現(xiàn)象:

圖1. 五塊類似代碼段中的錯(cuò)誤分布概圖

因此我們可以總結(jié)出一個(gè)規(guī)律:

在最末的粘貼代碼塊中出錯(cuò)的概率是其他代碼塊的4倍。

這個(gè)規(guī)律可能并沒有普適性。它只是個(gè)有趣的發(fā)現(xiàn),其實(shí)際效用在于:提醒在你寫***一塊的時(shí)候保持警覺。

實(shí)例:

下面我要證明這并不是我的胡思亂想而是有真實(shí)的趨勢(shì)的。請(qǐng)看下面的實(shí)例。

當(dāng)然,我不會(huì)列出所有例子,僅列舉簡(jiǎn)單而有代表性的。

Source Engine SDK

 
 
  1. inline void Init( float ix=0, float iy=0, 
  2.                   float iz=0, float iw = 0 )  
  3.   SetX( ix ); 
  4.   SetY( iy ); 
  5.   SetZ( iz ); 
  6.   SetZ( iw ); 

***一行應(yīng)該是SetW()。

Chromium

 
 
  1. if (access & FILE_WRITE_ATTRIBUTES) 
  2.   output.append(ASCIIToUTF16("\tFILE_WRITE_ATTRIBUTES\n")); 
  3. if (access & FILE_WRITE_DATA) 
  4.   output.append(ASCIIToUTF16("\tFILE_WRITE_DATA\n")); 
  5. if (access & FILE_WRITE_EA) 
  6.   output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n")); 
  7. if (access & FILE_WRITE_EA) 
  8.   output.append(ASCIIToUTF16("\tFILE_WRITE_EA\n")); 
  9. break; 

***兩行相同。

ReactOS

 
 
  1. if (*ScanString == L'\"' || 
  2.     *ScanString == L'^' || 
  3.     *ScanString == L'\"') 

Multi Theft Auto

 
 
  1. class CWaterPolySAInterface 
  2. public: 
  3.     WORD m_wVertexIDs[3]; 
  4. }; 
  5. CWaterPoly* CWaterManagerSA::CreateQuad (....) 
  6.   .... 
  7.   pInterface->m_wVertexIDs [ 0 ] = pV1->GetID (); 
  8.   pInterface->m_wVertexIDs [ 1 ] = pV2->GetID (); 
  9.   pInterface->m_wVertexIDs [ 2 ] = pV3->GetID (); 
  10.   pInterface->m_wVertexIDs [ 3 ] = pV4->GetID (); 
  11.   .... 

***一行冗余代碼來自于慣性粘貼。數(shù)組的大小是3。

Source Engine SDK

 
 
  1. intens.x=OrSIMD(AndSIMD(BackgroundColor.x,no_hit_mask), 
  2.                 AndNotSIMD(no_hit_mask,intens.x)); 
  3. intens.y=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask), 
  4.                 AndNotSIMD(no_hit_mask,intens.y)); 
  5. intens.z=OrSIMD(AndSIMD(BackgroundColor.y,no_hit_mask), 
  6.                 AndNotSIMD(no_hit_mask,intens.z)); 

程序員忘記把***一行的中的“BackgroundColor.y”改成“BackgroundColor.z”。

#p#

Trans-Proteomic Pipeline

 
 
  1. void setPepMaxProb(....) 
  2. {   
  3.   .... 
  4.   double max4 = 0.0; 
  5.   double max5 = 0.0; 
  6.   double max6 = 0.0; 
  7.   double max7 = 0.0; 
  8.   .... 
  9.   if ( pep3 ) { ... if ( use_joint_probs && prob > max3 ) ... } 
  10.   .... 
  11.   if ( pep4 ) { ... if ( use_joint_probs && prob > max4 ) ... } 
  12.   .... 
  13.   if ( pep5 ) { ... if ( use_joint_probs && prob > max5 ) ... } 
  14.   .... 
  15.   if ( pep6 ) { ... if ( use_joint_probs && prob > max6 ) ... } 
  16.   .... 
  17.   if ( pep7 ) { ... if ( use_joint_probs && prob > max6 ) ... } 
  18.   .... 

程序員忘記把***一個(gè)判斷中的“prob > max6”改為“prob > max7”。

SeqAn

 
 
  1. inline typename Value::Type const & operator*() { 
  2.   tmp.i1 = *in.in1; 
  3.   tmp.i2 = *in.in2; 
  4.   tmp.i3 = *in.in2; 
  5.   return tmp; 

SlimDX

 
 
  1. for( int i = 0; i < 2; i++ ) 
  2.   sliders[i] = joystate.rglSlider[i]; 
  3.   asliders[i] = joystate.rglASlider[i]; 
  4.   vsliders[i] = joystate.rglVSlider[i]; 
  5.   fsliders[i] = joystate.rglVSlider[i]; 

***一行應(yīng)該用rglFSlider。

Qt

 
 
  1. if (repetition == QStringLiteral("repeat") || 
  2.     repetition.isEmpty()) { 
  3.   pattern->patternRepeatX = true; 
  4.   pattern->patternRepeatY = true; 
  5. } else if (repetition == QStringLiteral("repeat-x")) { 
  6.   pattern->patternRepeatX = true; 
  7. } else if (repetition == QStringLiteral("repeat-y")) { 
  8.   pattern->patternRepeatY = true; 
  9. } else if (repetition == QStringLiteral("no-repeat")) { 
  10.   pattern->patternRepeatY = false; 
  11.   pattern->patternRepeatY = false; 
  12. } else { 
  13.   //TODO: exception: SYNTAX_ERR 

***一塊少了‘patternRepeatX’。正確的代碼應(yīng)該是:

 
 
  1. pattern->patternRepeatX = false; 
  2. pattern->patternRepeatY = false; 

ReactOS

 
 
  1. const int istride = sizeof(tmp[0]) / sizeof(tmp[0][0][0]); 
  2. const int jstride = sizeof(tmp[0][0]) / sizeof(tmp[0][0][0]); 
  3. const int mistride = sizeof(mag[0]) / sizeof(mag[0][0]); 
  4. const int mjstride = sizeof(mag[0][0]) / sizeof(mag[0][0]); 

‘mjstride’永遠(yuǎn)等于1。***一行應(yīng)該是:

 
 
  1. const int mjstride = sizeof(mag[0][0]) / sizeof(mag[0][0][0]); 

Mozilla Firefox

 
 
  1. if (protocol.EqualsIgnoreCase("http") || 
  2.     protocol.EqualsIgnoreCase("https") || 
  3.     protocol.EqualsIgnoreCase("news") || 
  4.     protocol.EqualsIgnoreCase("ftp") ||          <<<--- 
  5.     protocol.EqualsIgnoreCase("file") || 
  6.     protocol.EqualsIgnoreCase("javascript") || 
  7.     protocol.EqualsIgnoreCase("ftp")) {          <<<--- 

***的“ftp”很可疑,它之前已經(jīng)被比較過了。

Quake-III-Arena

 
 
  1. if (fabs(dir[0]) > test->radius || 
  2.     fabs(dir[1]) > test->radius || 
  3.     fabs(dir[1]) > test->radius) 

dir[2]的值忘記檢查了。

Clang

 
 
  1. return (ContainerBegLine <= ContaineeBegLine && 
  2.         ContainerEndLine <= ContaineeEndLine && 
  3.         (ContainerBegLine != ContaineeBegLine || 
  4.          SM.getExpansionColumnNumber(ContainerRBeg) <= 
  5.          SM.getExpansionColumnNumber(ContaineeRBeg)) && 
  6.         (ContainerEndLine != ContaineeEndLine || 
  7.          SM.getExpansionColumnNumber(ContainerREnd) >= 
  8.          SM.getExpansionColumnNumber(ContainerREnd))); 

***一塊,“SM.getExpansionColumnNumber(ContainerREnd)”表達(dá)式在跟自己比較大小。

MongoDB

 
 
  1. bool operator==(const MemberCfg& r) const { 
  2.   .... 
  3.   return _id==r._id && votes == r.votes && 
  4.          h == r.h && priority == r.priority && 
  5.          arbiterOnly == r.arbiterOnly && 
  6.          slaveDelay == r.slaveDelay && 
  7.          hidden == r.hidden && 
  8.          buildIndexes == buildIndexes; 

程序員把***一行的“r”忘記了。

Unreal Engine 4

 
 
  1. static bool PositionIsInside(....) 
  2.   return 
  3.     Position.X >= Control.Center.X - BoxSize.X * 0.5f && 
  4.     Position.X <= Control.Center.X + BoxSize.X * 0.5f && 
  5.     Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f && 
  6.     Position.Y >= Control.Center.Y - BoxSize.Y * 0.5f; 

***一行中,程序員忘記了兩個(gè)地方。首先,“>=”應(yīng)改為“<=”,其次,減號(hào)應(yīng)改為加號(hào)。

Qt

 
 
  1. qreal x = ctx->callData->args[0].toNumber(); 
  2. qreal y = ctx->callData->args[1].toNumber(); 
  3. qreal w = ctx->callData->args[2].toNumber(); 
  4. qreal h = ctx->callData->args[3].toNumber(); 
  5. if (!qIsFinite(x) || !qIsFinite(y) || 
  6.     !qIsFinite(w) || !qIsFinite(w)) 

***一個(gè)qlsFinite中,傳入?yún)?shù)應(yīng)該是‘h’。

OpenSSL

 
 
  1. if (!strncmp(vstart, "ASCII", 5)) 
  2.   arg->format = ASN1_GEN_FORMAT_ASCII; 
  3. else if (!strncmp(vstart, "UTF8", 4)) 
  4.   arg->format = ASN1_GEN_FORMAT_UTF8; 
  5. else if (!strncmp(vstart, "HEX", 3)) 
  6.   arg->format = ASN1_GEN_FORMAT_HEX; 
  7. else if (!strncmp(vstart, "BITLIST", 3)) 
  8.   arg->format = ASN1_GEN_FORMAT_BITLIST; 

字符串“BITLIST”長(zhǎng)度為7,而非3。

就此打住吧。我舉的例子已經(jīng)夠說明問題了吧?

結(jié)論

本文告訴你“拷貝-粘貼”大法在***一個(gè)粘貼代碼塊中出錯(cuò)的概率很可能是其他塊的4倍。

這跟人類的心理學(xué)有關(guān),與技術(shù)水平無關(guān)。文中說明了即便是像Clang或者Qt項(xiàng)目中的編程高手也會(huì)犯這種錯(cuò)誤。

我希望這個(gè)現(xiàn)象的發(fā)現(xiàn)對(duì)于程序員們有所幫助,也許可以促使他們?nèi)パ芯课覀兊腷ug數(shù)據(jù)庫。相信如此有助于在這些錯(cuò)誤中發(fā)現(xiàn)新的規(guī)律并總結(jié)出新的編程建議。


網(wǎng)站題目:分析程序員在代碼編程中的“末行效應(yīng)”
網(wǎng)頁網(wǎng)址:http://www.dlmjj.cn/article/dpiiscd.html