2009年11月28日 星期六

GCC 最佳化參數對 const 變數的影響

之前有寫過幾篇關於 const 變數的文章
這次要實驗的程式碼為
/* main.c */
#include <stdio.h>

void foo(const int* const);
void bar(int);

int main(int argc, char* argv[])
{
    const int value = 11;
    printf("%d\n", value);
    foo(&value);
    bar(value);
    printf("%d\n", value);
    return 0;
}

void foo(const int* const value)
{
    int* tmp = (int*) value;
    *tmp = 22;
    printf("%d\n", *value);
}

void bar(int value)
{
    printf("%d\n", value);
}
以及
/* main.cpp */
#include <cstdio>

void foo(const int* const);
void bar(int);

using namespace std;

int main(int argc, char* argv[])
{
    const int value = 11;
    printf("%d\n", value);
    foo(&value);
    bar(value);
    printf("%d\n", value);
    return 0;
}

void foo(const int* const value)
{
    int* tmp = const_cast<int*>(value);
    *tmp = 22;
    printf("%d\n", *value);
}

void bar(int value)
{
    printf("%d\n", value);
}
為了要實驗各個參數寫了一個 Makefile
all:
    $(CC) -Wall -g -O0 main.c -o main-O0
    @echo main-O0 && ./main-O0
    $(CC) -Wall -g -O1 main.c -o main-O1
    @echo main-O1 && ./main-O1
    $(CC) -Wall -g -Os main.c -o main-Os
    @echo main-Os && ./main-Os
    $(CC) -Wall -g -O2 main.c -o main-O2
    @echo main-O2 && ./main-O2
    $(CC) -Wall -g -O3 main.c -o main-O3
    @echo main-O3 && ./main-O3
    $(CXX) -Wall -g -O0 main.cpp -o main-O0
    @echo main-O0 && ./main-O0
    $(CXX) -Wall -g -O1 main.cpp -o main-O1
    @echo main-O1 && ./main-O1
    $(CXX) -Wall -g -Os main.cpp -o main-Os
    @echo main-Os && ./main-Os
    $(CXX) -Wall -g -O2 main.cpp -o main-O2
    @echo main-O2 && ./main-O2
    $(CXX) -Wall -g -O3 main.cpp -o main-O3
    @echo main-O3 && ./main-O3
執行 make 後得到的結果為
cc -Wall -g -O0 main.c -o main-O0
main-O0
11
22
22
22
cc -Wall -g -O1 main.c -o main-O1
main-O1
11
22
11
11
cc -Wall -g -Os main.c -o main-Os
main-Os
11
22
11
11
cc -Wall -g -O2 main.c -o main-O2
main-O2
11
22
11
11
cc -Wall -g -O3 main.c -o main-O3
main-O3
11
22
11
11
g++ -Wall -g -O0 main.cpp -o main-O0
main-O0
11
22
11
11
g++ -Wall -g -O1 main.cpp -o main-O1
main-O1
11
22
11
11
g++ -Wall -g -Os main.cpp -o main-Os
main-Os
11
22
11
11
g++ -Wall -g -O2 main.cpp -o main-O2
main-O2
11
22
11
11
g++ -Wall -g -O3 main.cpp -o main-O3
main-O3
11
22
11
11
由結果可以看出 C++ 對於 const 變數的處理都一樣
反而在 C 上面對預設的 -O0 參數不會執行任何常數字面的最佳化處理

2009年11月27日 星期五

GNU Build System 當中 {bin,noinst,check,EXTRA}_PROGRAMS 使用上的分別

在 Automake 的腳本檔 Makefile.am 裡面,要編寫一個執行檔案的規則可以這樣寫
bin_PROGRAMS = mp3player
mp3player_SOURCES = mp3player.c

這樣在 $ ./configure 之後就可以使用 $ make 編譯出 mp3player 這個執行檔
甚至於 $ make install 還可以幫你安裝到預定的目錄底下
這個預定的目錄是像 $ ./configure --prefix=/opt 這樣指定的
如果沒有特別指定的話,通常預設值都會是 /usr/local

如果只想要編譯出執行檔卻不想要在 $ make install 時,被安裝到預定的目錄底下,在腳本檔應該要這樣寫
noinst_PROGRAMS = mp3player

如果想要在 make 時,不會去編譯執行檔的話,只有在 $ make check 時才會去編譯執行檔,可以這樣寫
check_PROGRAMS = mp3player

如果想要在 $ make 跟 $ make check 都不會去編譯執行檔,只有在 $ make mp3player 時才會去編譯執行檔,可以這樣寫
EXTRA_PROGRAMS = mp3player

更詳細的說明請看 $ info automake

P.S. 有 $ 開頭代表在命令列下輸入的指令

2009年11月16日 星期一

GCC C++ Compiler 根本沒有還原 const 變數的數值,只不過先行代換掉使用 const 變數的地方。

延續前三篇文章的討論
C++ Programming: Call by Reference/Value/Pointer and const_cast
GCC C++ Compiler 會聰明地幫你還原 const 變數的數值
GCC C++ Compiler 對於 const 變數的處理到底會有多聰明呢?
這次實驗的程式碼如下
#include <iostream>

void foo(const int&);
void bar(const int&);

using namespace std;

int main(int argc, char* argv[])
{
    const int value = 11;
    cout << value << endl;
    foo(value);
    cout << value << endl;
    bar(value);
    cout << value << endl;
    cout << *(int*)&value << endl;
    return 0;
}

void foo(const int& value)
{
    int& tmp = const_cast<int&>(value);
    tmp = 22;
}

void bar(const int& value)
{
    cout << value << endl;
}
正如網友 jclin 提到的「我比較不相信 GCC 會幫你還原 const value 的作法. 只是 compiler 知道那是 const, 基於 optimization 的角度
還有 chihchun 提到的「main::value 是一個 alias, compiler (g++ 4.3.4) 會替換成 integral literal,但是依然會在 Stack 中配置此變數供人使用。
GCC C++ Compiler 只不過是把原始碼先轉換成以下這段
#include <iostream>

void foo(const int&);
void bar(const int&);

using namespace std;

int main(int argc, char* argv[])
{
    const int value = 11;
    cout << 11 << endl;
    foo(value);
    cout << 11 << endl;
    bar(value);
    cout << 11 << endl;
    cout << *(int*)&value << endl;
    return 0;
}

void foo(const int& value)
{
    int& tmp = const_cast<int&>(value);
    tmp = 22;
}

void bar(const int& value)
{
    cout << value << endl;
}
const 變數並不代表它無法被改變數值,透過 const_cast 還是可以改變數值的,只不過已經先被當作字面常數替換掉的地方,在 C++ 程式碼的運作上不是那麼容易瞭解。

2009年11月15日 星期日

GCC C++ Compiler 對於 const 變數的處理到底會有多聰明呢?

延續前兩篇文章的程式碼實驗
C++ Programming: Call by Reference/Value/Pointer and const_cast
GCC C++ Compiler 會聰明地幫你還原 const 變數的數值
這次想要實驗的程式碼片段如下
#include <iostream>

struct Foo {
    const int value;
};

void foo(const struct Foo* const);
void foofoo(const struct Foo* const);

using namespace std;

int main(int argc, char* argv[])
{
    const struct Foo bar = {11};
    cout << bar.value << '\t' << &bar.value << endl;
    foo(&bar);
    cout << bar.value << '\t' << &bar.value << endl;
    foofoo(&bar);
    cout << bar.value << '\t' << &bar.value << endl;
}

void foo(const struct Foo* const bar)
{
    static struct Foo inner = {22};
    struct Foo** tmp = const_cast<struct Foo**>(&bar);
    cout << *tmp << '\t' << bar << '\t' << bar->value << endl;
    *tmp = &inner;
    cout << *tmp << '\t' << bar << '\t' << bar->value << endl;
}

void foofoo(const struct Foo* const bar)
{
    int &value = const_cast<int&>(bar->value);
    value = 22;
}
編譯後執行的結果為
11    0xbfbb3090
0xbfbb3090    0xbfbb3090    11
0x804a034    0x804a034    22
11    0xbfbb3090
22    0xbfbb3090
對照程式碼就可以發現呼叫 foo() 後 const 變數 bar 被還原成原本的數值了
不過呼叫 foofoo() 後 const 變數 bar 底下的 const 變數 value 並沒有被還原成原本的數值
所以 GCC C++ Compiler 對 const 變數數值的還原保護僅限於直接傳入函式的變數本身
並不包含該 const 變數底下的 const 變數,到這時總算是解決了 $4 心中的疑惑
為什麼兩三年前寫 C++ 時,使用 const_cast 可以改變 const 變數的數值
而在 H4 聚會的實驗時反而遇到了跟 $4 過去經驗乍看下相衝突的結果 :-)

2009年11月14日 星期六

GNOME Global Menu

官方網站 http://code.google.com/p/gnome2-globalmenu/
首先看一下使用前

面板上面並沒有加入很多應用程式的啟動圖示
所以在右上角有一塊區域一直沒有使用到
首先新增加 GNOME Global Menu 的 GPG Key
wget http://gnome2-globalmenu.googlecode.com/files/GlobalMenuKey.gpg
sudo apt-key add GlobalMenuKey.gpg
然後新增一個檔案在 /etc/apt/sources.list.d/gnome2-globalmenu.list
# gnome2-globalmenu
deb http://ppa.launchpad.net/globalmenu-team/ppa/ubuntu jaunty main
deb-src http://ppa.launchpad.net/globalmenu-team/ppa/ubuntu jaunty main
然後在面板上面增加一個 Applet

最後調整 GNOME Global Menu 的設定

於是就把 GTK+ 的選單列都移動到面板上面了~ :D

2009年11月13日 星期五

GCC C++ Compiler 會聰明地幫你還原 const 變數的數值

關於前一篇文章 C++ Programming: Call by Reference/Value/Pointer and const_cast 所留下的問題
再寫一段簡單的程式碼來作實驗
/* main.cpp */
void funcA(const int &);
void funcB(int);
void funcC(const int *);

int main(int argc, char* argv[])
{
    const int value = 11;
    funcA(value);
    funcB(value);
    funcC(&value);
    funcB(value);
    return 0;
}

/* Call by Reference */
void funcA(const int& value)
{
    int& tmp = const_cast<int&>(value);
    tmp = 22;
}

/* Call by Value */
void funcB(int value)
{
    value = 33;
}

/* Call by Pointer */
void funcC(const int* value)
{
    int* tmp = const_cast<int*>(value);
    *tmp = 44;
}
這次利用 GCC 來產生 x86 的組合語言看看到底發生了什麼事情
$ g++ -S main.cpp
就會產生 main.s 檔案
來看看其中的片段
    call    _Z5funcARKi
    movl    $11, (%esp)
    call    _Z5funcBi
    leal    -8(%ebp), %eax
    movl    %eax, (%esp)
    call    _Z5funcCPKi
    movl    $11, (%esp)
    call    _Z5funcBi
答案是在呼叫完 funcA 跟 funcC 之後
GCC 聰明地將 value 的值給還原成 11 了
如果在 C++ 程式碼中第二次呼叫 funcB 給拿掉就不會看到第二個
    movl    $11, (%esp)
聰明的 GCC 只會在適當的時候幫你還原 const 變數的數值,造成 const 變數值從來沒被改變過的假象 :)

jQuery 實戰手冊 (JQuery in Action) - 第四章閱讀心得分享

ISBN 978-986-181-638-8
範例程式可以在 http://www.manning.com/bibeault 下載
此書的範例程式都是使用 jquery-1.2.1.js
所以先手動替換成 jquery-1.3.2.js
find -type f -name '*.html' -exec sed -i 's/jquery-1.2.1.js/jquery-1.3.2.js/' {} \;
第四章的範例 Bamboo Asian Grille - Online Order Form 不知道為什麼下面這行程式碼無法正常運作
$('~ span:first',this).text(...);
於是改成下面這段程式碼就可以正常運作了
$(this).next('span').text(...);
另外就是下面這一段程式碼其實有點多餘
.each(function(){ if (checked) this.focus(); });
可以改成下面那樣就可以達到同樣的功能
.focus();
主要是作者想要拿來玩弄 closure 的東西

C++ Programming: Call by Reference/Value/Pointer and const_cast

昨天晚上 H4 的聚會上面在討論這個問題
所以寫了一小段 C++ 程式碼來作實驗
/* main.cpp */
#include <iostream>

using namespace std;

void funcA(int&);
void funcB(int);
void funcC(int*);

int main(int argc, char* argv[])
{
    int value = 10;
    cout << value << endl;
    funcA(value);
    cout << value << endl;
    funcB(value);
    cout << value << endl;
    funcC(&value);
    cout << value << endl;
    return 0;
}

/* Call by reference */
void funcA(int& value)
{
    value = 20;
}

/* Call by value */
void funcB(int value)
{
    value = 30;
}

/* Call by pointer/address */
void funcC(int* value)
{
    *value = 40;
}
將上面這段程式碼儲存成 main.cpp
再使用 GCC 編譯執行
$ g++ -Wall -g main.cpp -o main
$ ./main
10
20
20
40
對照程式碼應該就可以看出這三者之間的差異了

不過後來有人有提到 const 的問題
於是將程式碼改寫成
#include <iostream>

using namespace std;

void funcA(const int&);
void funcB(int);
void funcC(const int*);

int main(int argc, char* argv[])
{
    const int value = 10;
    cout << value << " " << &value << endl;
    funcA(value);
    cout << value << " " << &value << endl;
    funcB(value);
    cout << value << " " << &value << endl;
    funcC(&value);
    cout << value << " " << &value << endl;
    return 0;
}

/* Call by reference */
void funcA(const int& value)
{
    int &tmp = const_cast<int&>(value);
    tmp = 20;
    cout << "\t" << value << " " << &value << endl;
}

/* Call by value */
void funcB(int value)
{
    value = 30;
    cout << "\t" << value << " " << &value << endl;
}

/* Call by pointer/address */
void funcC(const int* value)
{
    int* tmp = const_cast<int*>(value);
    *tmp = 40;
    cout << "\t" << *value << " " << value << endl;
}
編譯執行的結果為
$ ./main
10 0xbfb17040
    20 0xbfb17040
10 0xbfb17040
    30 0xbfb17020
10 0xbfb17040
    40 0xbfb17040
10 0xbfb17040
為什麼會這樣呢?