Friday, September 24, 2010

[Debug] error: stray ‘\xxx’ in program

「The key to eliminating bugs from your code is learning from your mistakes.」
~ by Jerry Jongerius


「人非聖賢,孰能無過」,再厲害的programmer也無法百分之百肯定自己的code是bug-free的 [Ref.1]。以目前Trek的等級,無法長篇大論只能先case by case的來舉出平常實作當中所遇到的小問題,小Bug」但初學者往往忽略了這些小細節而浪費了許多時間就像Jerry在本文開頭所言,至少我們需要記住這隻臭蟲,那麼,它將不再出現



這篇文章目的要紀錄一個例子
  • 試想,當我們在coding時,可能使用了不同的Editor,不同的compiler,甚至是在不同的OS底下執行的由於不同系統採用了不同的編碼方式,導致compilercompile無法辨認某一種編碼規格此時便會產生錯誤


  • 案例:友人在windows底下用msn傳了份code給筆者, 而筆者此時是在使用Linux系統, 當筆者利用g++編譯時, 問題產生了,如底下截圖:


  • 問題及解決方法:會產生這個error的原因是, 筆者直接從msn的對話框中複製了該份code, 並直接使用, 而它的編碼方式與g++所認定的方法不一樣, 因此Bug產生。但我們code明明沒有寫錯, 再怎麼trace code也無法找出Bug。了解原因後, 解決方法就變簡單了, 您可以使用常用的Editor, 親自寫出code, 而不是直接從某個您不知道它編碼方式的文件複製/貼上code

  • 另外, 這種情況也常發生在直接複製網頁上的code, 例如:直接複製簡體中文的網頁, 往往會因為編碼問題而產生錯誤


  • 更多Bug方面的深入探討請見Reference. 1


Reference
  1. http://blog.joycode.com/jiangsheng/archive/2006/02/05/71101.aspx
  2. http://s428.photobucket.com/home/rbrovillos

Saturday, September 4, 2010

[C++] 函式,如何傳遞一個C++物件

Introduction
不論在C或C++中,當函式要傳遞一個「傳回值」時,通常可以透過「暫存器」來傳遞,也就是:函式把將要回傳的值,暫存在某個暫存器中,接下來呼叫方就可以去讀取該暫存器,也就達到傳遞的效果,以上作法的前提是,該「暫存器」容納得下該「傳回值」;然而,在C++中,當函式的「傳回值」不再是一般小尺寸的型態,而是大尺寸的物件時,會發生什麼事?編譯器會如何來處理這種情況?本篇文章即要討論這個情況。



Return Value — 「object」
當傳回值是個C++的「物件」,且程式沒有設定傳回值最佳化時(Return Value Optimization),則需要透過兩次的物件複製來達到傳遞目的:
  1. 將該物件複製堆疊上的臨時空間
  2. 將存放在臨時空間中的物件複製儲存函式回傳值的物件
然而,根據不同的compiler及不同的calling convention,甚至不同的平台,在傳遞物件上皆會有不同的實作方式。

接下來在實驗的地方,我將比較在「VC9(WITH Windows XP)及「g++(WITH Ubuntu 10.04 64bit)這兩種不同的compiler及不同的平台下,執行物件回傳的異同,以及Return Value Optimization的設定對於執行結果的影響。




Experiment
實驗一實驗二的地方,我將在VC9g++底下分別執行測試的程式碼,並將執行結果的截圖貼上來。而由於前面兩個實驗產生了不同的執行結果,我將在short summary的地方進一步分析原因,並透過實驗三來做驗證
 
  • Source Code

#include <iostream>

using namespace std;

struct cpp_obj
{
 cpp_obj()
 {
  cout << "ctor\n";
 }
 cpp_obj(const cpp_obj& c)
 {
  cout << "copy ctor\n";
 }
 cpp_obj& operator=(const cpp_obj& rhs)
 {
  cout << "operator=\n";
  return *this;
 }
 ~cpp_obj()
 {
  cout << "dtor\n";
 }
};

cpp_obj return_test()
{
 cpp_obj b;
    cout << "before return\n";
 return b;
}

int main()
{
 cpp_obj n;
 n = return_test();
}

  • Exp.1
          實驗一是在windows XP下使用VC9,底下是執行結果的截圖



  • Exp.2
          實驗二是在Ubuntu下使用g++,底下是執行結果的截圖




  • short summary of Exp.1 & Exp.2
實驗一當中,我們發現物件被複製了兩次,分別是
  1. 呼叫「copy constructor」來將物件複製到堆疊上的臨時空間
  2. 呼叫「operator=」來將存放在臨時空間中的物件複製到儲存函式回傳值的物件上
實驗二,我們發現減少了copy constructor的呼叫,原因是因為g++ compiler在執行程式時自動做了最佳化(Return Value Optimization),將物件直接建構在臨時空間上,因此物件少了一次的複製。


因此,在實驗三,我們要做的就是將VC9的程式最佳化打開,我們可以預料得到的是: 最佳化打開後,物件勢必會減少一次的複製。


  • Exp.3
實驗三是在windows XP下使用VC9,且打開程式的最佳化(Return Value Optimization),底下是執行結果的截圖




Conclusion
本篇文章介紹了函式是如何傳遞一個C++物件,且透過實驗來說明不同的compiler會有不同的設定,根據不同的compiler及不同的calling convention,甚至不同的平台,在傳遞物件上皆會有不同的實作方式。

筆者是在VC9(WITH Windows XP)及g++(WITH Ubuntu 10.04 64bit)這兩種compiler下編譯程式的,而我們發現Return Value Optimization提升了程式執行時的效能,深深影響著實驗的結果,更多討論可以詳見Reference 2。

回傳一個物件是一件非常繁雜的工作,也因此,我們應該盡量避免回傳「物件」


Reference 
  1. 程式設計師的自我修養 — 連結、載入、程式庫,俞甲子、石凡、潘愛民 
  2. MSDN, http://msdn.microsoft.com/en-us/library/ms364057%28VS.80%29.aspx