發表文章

目前顯示的是有「謬誤與陷阱」標籤的文章

signal和sleep函數混用的問題

圖片
#include <stdio.h > #include  < stdlib.h > #include  < signal.h > #include  < unistd.h > void proc_signal(int signal) {     if (SIGINT == signal) {         printf("got a signal(%d) is SIGINT\n", signal);         exit(1);     } else if (SIGALRM == signal) {         printf("got a signal(%d) is SIGALRM\n", signal);     } else {         printf("got a signal(%d) is not SIGINT and SIGALRM\n", signal);     } } int main(int argc, char** argv) {     printf("main start\n");     if (SIG_ERR == signal(SIGALRM, proc_signal)) {         printf("An error occurred while setting a SIGALRM handler.\n");         return 0;     }     if (SIG_ERR == signal(SIGINT, proc_signal)) {         printf("An error occurred while setting a...

四捨五入的問題

圖片
我們很常利用的Rounding函數會是這樣的 inline int Round(double val) {   return (int)(val+0.5); } 不過 四捨五入在負數上的運作會有點不同,下面的程式就是展示這個不同點, 就wiki上的定義  http://en.wikipedia.org/wiki/Rounding 和我們慣用的寫法在負數會有些不同, 和將負數位移成正數之後,再位移回負數的寫法, 以及+0.5之後取floor的寫法是相同的結果。 #include < iostream > #include  < cmath > #include  < cstdlib > using namespace std; int main() {   cout << "Rounding in negtive value:" << endl;   cout << (int)(-0.4 + 0.5) << endl;    cout << (int)(-0.5 + 0.5) << endl;   cout << (int)(-1.4 + 0.5) << endl;   cout << (int)(-1.5 + 0.5) << endl;   cout << "Rounding in negtive value(shift&shift back):" << endl;   cout << (int)(-0.4 + 128 + 0.5) -128 << endl;    cout << (int)(-0.5 + 128 + 0.5) -128 << endl;   cout << (int)(-1.4 + 128 + 0.5) -1...

C/C++陣列初始化錯誤

有同學問我陣列給值的問題,這點在C++ Primer有寫到, 一些程式設計的書也會寫,只是一般同學都不會注意到"初始化"和"給值"是不同的。 #include <iostream > using namespace std; //正常作法-初始化 int inter_priority[6][6][4] =  { {{4,0,0,0},{4,3,2,1},{4,3,2,1},{4,2,3,1},{4,2,1,3},{4,0,0,0}}, {{4,3,2,1},{4,3,2,1},{4,3,2,1},{4,3,2,1},{4,2,1,3},{4,3,2,1}}, {{4,2,1,3},{4,3,2,1},{4,3,1,2},{4,3,2,1},{4,2,1,3},{4,3,1,2}}, {{4,1,3,2},{4,2,3,1},{4,3,1,2},{4,1,3,2},{4,1,2,3},{4,1,3,2}}, {{3,2,1,4},{4,2,1,3},{3,2,1,4},{4,2,1,3},{3,2,1,4},{3,2,1,4}}, {{4,0,0,0},{4,3,2,1},{4,3,1,2},{4,1,3,2},{4,2,1,3},{4,0,0,0}} }; // 同學的錯誤用法-當成給值 int inter_priority[6][6][4]; int main() { // 同學的錯誤用法-當成給值 inter_priority =  { {{4,0,0,0},{4,3,2,1},{4,3,2,1},{4,2,3,1},{4,2,1,3},{4,0,0,0}}, {{4,3,2,1},{4,3,2,1},{4,3,2,1},{4,3,2,1},{4,2,1,3},{4,3,2,1}}, {{4,2,1,3},{4,3,2,1},{4,3,1,2},{4,3,2,1},{4,2,1,3},{4,3,1,2}}, {{4,1,3,2},{4,2,3,1},{4,3,1,2},{4,1,3,2},{4,1,2,3},{4,1,3,2}}, {{3,2,1,4},{4,...

一維陣列與二維陣列轉換

這是我今天不經意的寫出一個謬誤程式,我希望將一個用一維陣列儲存的100x100小圖片,放大2倍儲存到另一個二維陣列,放大方式就是將一個像素自我複製變成4個像素。   int FrameHeight = 480;   int FrameWidth = 640;   for(int h=0; h   {     for(int w=0; w     {       int idx1 = h*FrameWidth/4+w/2;       int idx2 = h/2*FrameWidth/2+w/2;       if (idx1 != idx2)       {         cerr << "(" << h <<  "," << w<< ") idx1=" << idx2 << " idx2=" << idx2 << endl;       }   } } idx1 = h*FrameWidth/4+w/2 idx2 = h/2*FrameWidth/2+w/2 idx1是對h*FrameWidth除以四 和 idx2先對h/2之後再除以2應該是一樣的吧!? 這個兩個運算看似數學上是相等且合理的,實際上卻是大大的不同! 實際上這兩個運算,因為使用了C/C++的整數運算特性,應表示成 idx1 = floor(h*FrameWidth / 4.0) + floor(w/2.0) idx2 = floor(floor(h / 2.0) * FrameWidth / 4.0) + floor(w/2.0) 當我們帶入 h, w為101, 100的時候 idx1 = 16210 = 16160 + 50 = floor(101 * 640 / 4.0) + ceil(100 / 2.0) idx2 ...

迭代存取的謬誤

昨天寫的兩段愚蠢的Code,可以掉入兩個這樣的陷阱,刀也該拿去再磨磨了。 第一個 #include <vector> using namespace std; int main() { vector<int> v; // .... 寫了某些code 有可能有資料存到v裡頭 .... for (unsigned int i=v.size()-1 ; i>=0 ; --i) v.pop_back(); return 0; } 第二個 #include <list> using namespace std; int main() { list<int> l; for (int i=0 ; i<10 ; ++i) l.push_back(i); // 移除裡面的元素 for (int i=0 ; i<l.size() ; ++i) l.remove(i); return 0; } 第一個程式執行結果理應是無限迴圈,肇因於我在宣告i時使用了unsigned int作為資料型態,而我使用遞減進行for迴圈的迭代,使用unsigned僅僅是想減少warning的出現,沒想到這卻也是敗筆。由於unsinged int 沒有負數,因此當數值為0後,再進行減1的動作會變成最大值(反之,最大值再加1會成為0),因此這個迴圈的條件恆成立。若你認為只有for的條件判斷有問題需要修改,那你就錯了!在for的初始化部份也存在問題,當v.size()為0時,i的起始就因減1而成為最大值,也會造成程式的執行不正確,就算只改了判斷式,程式依然有可能進行錯誤的迭代。 第二個程式則是還沒移除所有的元素就會結束。因為l.size()是即時反應這個list中的元素個數,而我們使用l.remove()後會使l.size()減少,但是i依然會增加,導致我們只會移除list內一半數量。比較好的方法應該改為用while迴圈,並以l.empty()當作條件來判斷是否要結束;不過,若是常常使用vector.size()作為迴圈條件,就會覺得這個迴圈很直覺的是對的,難以察覺臭蟲的存在。

Java Puzzlers: 22. Dupe of URL

這隻程式會做什麼事情? public class BroswerTest { public static void main(String[] args) { System.out.println("iexplore"); http://www.google.com.tw System.out.println(":maximize"); } } 看出來了嗎? 其實它只會印字而已。 讓我們排版一下,這樣應該就瞭解了 public class BroswerTest { public static void main(String[] args) { System.out.println("iexplore"); http: //www.google.com.tw System.out.println(":maximize"); } } http: 是個label,在Java中也是有Label,是用在loop跳出時指定用的。 而// 則是整行註解,把後面的東西都註解掉了,所以這個URL可以編譯其實只是個假象。 上一篇Puzzlers: Java Puzzlers: 38.The Unwelcome Guest

Java Puzzlers: 38.The Unwelcome Guest

下面這段程式碼有沒有問題呢? public class UnwelcomeGuest {  public static final long GUEST_USER_ID = -1;  private static final long USER_ID;  static {   try {    USER_ID = getUserIdFromEnvironment();   } catch (IdUnavailableException e) {    USER_ID = GUEST_USER_ID;    System.out.println("Logging in as guest");   }  }  private static long getUserIdFromEnvironment()   throws IdUnavailableException {   throws new IdUnavailableException ();  }   public static void main(String[] args) {   System.out.println("User ID: " + USER_ID);  } } class IdUnavailableException extends Exception {  IdUnavailableException() {  } } 結果:這隻程式看似正常,實際上卻無法編譯! 這是因為要利用電腦來要判斷blank final是否在程式中被設定超過一次是極為困難的, Java編譯器使用的規則是『blank final只有在"當然"未被設定時,才能被設定』。 問題就是只能歸咎於,編譯器的演算法還不夠完善, Java為了安全起見,要求我們必須修改這個不夠安全的程式碼。

Java Puzzlers: 字串物件的特殊性11,67

請問這兩個的輸出結果各是什麼? System.out.println("A"+"B"); System.out.println('A'+'B'); from Java Puzzlers 結果:上面輸出的結果是是AB,下面的是則不是AB,而是印出131。 Java字串的加法,是會將兩個字串合起來,然而,字元的加法就沒有這樣的規則,而是運用了整數運算,將'A'與'B'的code number相加起來,產生131的結果。 而我們可以利用""(空字串)將整個運算式變成字串與字元的加法,也就是字串加入一個字元,就能使字元輸出成AB。若是要銜接非常多的字串,不建議使用Java緩慢的字串加法,請以StringBuffer或StringBuilder替代之。 System.out.println(""+'A'+'B'); 你還可以試試看下面的程式碼 int foo = 1234 , foo2 = 4321; System.out.println(foo + foo2); System.out.println(""+ foo + foo2); 當你要弄一個複雜的輸出,就有必要用括號處理你所想要的結果,或是使用JDK5.0後有的printf,利用如同C語言的printf方法,進行格式化輸出。 int foo = 1234 , foo2 = 4321; System.out.println("add=>"+ (foo + foo2) +" concat=>"+foo+foo2); 結論 套一句Java Puzzlers作者的話 When using the string concatenation operator, always parenthesize nontrivial operands. 當你使用字串連接運算時,永遠要用小括號將重要的運算式包起來。 Puzzlers 67: All Strung Out 我們創造了一個我們自己的String,打算取代原本的String 你覺得...