2015年10月13日 星期二

使用printf印出有顏色的Console訊息

有時候Console訊息很多,如果能把自己想看的訊息加點顏色,可以加快Debug速度

下面記錄了一個基於printf可以印出彩色訊息的function。

====================================================================
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>

#define NONECOLOR "\033[m"
#define RED "\033[0;32;31m"
#define LIGHT_RED "\033[1;31m"
#define GREEN "\033[0;32;32m"
#define LIGHT_GREEN "\033[1;32m"
#define BLUE "\033[0;32;34m"
#define LIGHT_BLUE "\033[1;34m"
#define DARY_GRAY "\033[1;30m"
#define CYAN "\033[0;36m"
#define LIGHT_CYAN "\033[1;36m"
#define PURPLE "\033[0;35m"
#define LIGHT_PURPLE "\033[1;35m"
#define BROWN "\033[0;33m"
#define YELLOW "\033[1;33m"
#define LIGHT_GRAY "\033[0;37m"
#define WHITE "\033[1;37m"


//**********************************************************
// Usage Example:
// int num=7;
// COLOR_DBG("%s Lucky Number is %d", GREEN, num);     
//
// Description:
// This message will show Green color.
//
// viod COLOR_DBG(const char* Format, const char* Color, const char* str, ...)
void COLOR_DBG(const char* Format, ...)
{
char str[255];
va_list ap;
va_start(ap,Format);
vsprintf(str,Format,ap);
sprintf(str,"%s%s",str,NONECOLOR);
printf("%s",str);
va_end(ap);
}


int main()
{
COLOR_DBG("%s Hello World!!!!!\n", YELLOW);
return 0;
}
=====================================================================

以下是使用gcc compiler後執行結果的截圖!


2015年10月7日 星期三

將C++的function包裝成Library(so檔)給C的程式使用

今天要練習的是,把C++的Function包給C程式使用時該怎麼作

首先我們先撰寫一支簡單的C++程式,如下所示
////////////////////  cplus_lib.cpp  ////////////////////////
#include <iostream>
using std::cout;

void cout_func()
{
cout << "Hello World!\n";
}
//////////////////////////////////////////////////////////////////

接著我們將她打包成動態連結檔libcplus_lib.so,步驟如下:
1. gcc -Wall -O2 -c -o cplus_lib.o cplus_lib.cpp
2. gcc -shared -o libcplus_lib.so cplus_lib.o

經過以上兩個步驟,我們可以得到動態連結檔的函式庫 libcplus_lib.so






接著我們試著再寫一支C程式,此程式去使用函式庫的cout_func()來印出"Hello World"
///////////////////////  main_cout.c  //////////////////////////////
#include <stdio.h>

int main()
{
        cout_func();
        return 0;
}
/////////////////////////////////////////////////////////////////////////////

接著編譯過程如下,注意要額外指定C++ 標準函式庫 libstdc++:
1.gcc -o main_cout_dynamic main_cout.c -L. -lcplus_lib -lstdc++

結果會出下以下的錯誤訊息:












解決方法,要在產生library的程式當中加入一些東西,如下所示:
////////////////////  cplus_lib.cpp  ////////////////////////
#include <iostream>
using std::cout;

#ifdef __cplusplus
extern "C" {
#endif
void cout_func()
{
cout << "Hello World!\n";
}
#ifdef __cplusplus
}
#endif
//////////////////////////////////////////////////////////////////
註:上面程式碼的意思是: 如果這是一段cpp的程式碼,就加入extern "C"{} 來處理其中的程式碼


這樣子編譯器才看得懂,所以再整個重新編譯一次,就成功了!
















註: 上圖第三行,作者把產生出來的library複製到library環境變數的預設路徑下,如果不這樣作,當執行main_cout_dymanic時,會出現找不到library的錯誤訊息,關於這部分作者正在尋找可以自己指定吃特定路徑的library的方法.....

如果有朋友知道方法,也請給予指教,謝謝!

2015年9月23日 星期三

linux下使用ping指令來快速知道網路是否連上Internet

最近再作NTP(網路同步時間)的功能,因此在使用者選擇好要同步的NTP Server之前,我必須先確認網路是可以連上Internet的

ping -q -c1 -W1 168.95.1.1

上面是關鍵的command,使用ping再加上它的一些參數,可以很快的知道網路是否有連上Internet。

這裡先說明一下ping參數的意義:
-q     => 不顯示任何傳送封包的資訊,只顯示最後的結果
-c1   => 在發送指定數目的包後停止,c1也就是只發一次的意思
-W1 => 等待回應的時間,W1就是只等待一秒


詳細的程式碼如下:
===================================================================

// NTP Server
#define NTP_SERVER_1 "tock.stdtime.gov.tw"      //response time is short.
#define NTP_SERVER_2 "tw.pool.ntp.org"
#define CH_TELECOM "168.95.1.1"                       //中華電信DNS

int ping_result(char *server)
{
        char cmd[64]={0};
        int ping_ret, status;

        sprintf(cmd, "ping -q -c1 -W1 %s 1>/dev/null 2>&1", server);
        status = system(cmd);
        if(-1 != status)
                ping_ret = WEXITSTATUS(status);

        if((status == 0) && (ping_ret == 0))    // If network can work on internet, these two value will be zero.
                return 1;
        else
                return 0;
}

void ping_ntp()
{
        int pingResult = ping_result(CH_TELECOM);    //ping CHT DNS first.
        if(pingResult == 1)
        {
                pingResult = ping_result(NTP_SERVER_1);      //second ping NTP Server.
        }

        if(pingResult == 0 )
        {
                printf("NETWORK_ERROR");
        }
        else
        {
                printf("RESPONSE_SUCCESS");
        }
}
=================================================================

這裡再說明一下,上面的ping_ntp的function當中,我會先去ping中華電信的DNS(168.95.1.1),這樣可以先知道是否有通Internet,第二步才去確認NTP SERVER是可以work的,若是連Internet都不通,也不必再花時間去ping NTP SERVER了~

2015年9月22日 星期二

opencv研究 關於CommandLineParser的用法

最近在看一些opencv的example source code,看到了CommandLineParser是以前沒有用過的class,因此瞭解了它的用法之後,把它記錄在這裡~

我這邊是拿opencv 3.0.0中的sample的image_alignment.cpp來當說明範例

首先要使用這fuction時都會在前頭先宣告一個"key",大概如下方所示:

============================================================
const std::string keys =
    "{@inputImage       | ../data/fruits.jpg | input image filename }"
    "{@templateImage |                            | template image filename (optional)}"
    "{@inputWarp        |                           | input warp (matrix) filename (optional)}"
    "{n numOfIter        | 50                       | ECC's iterations }"
    "{e epsilon              | 0.0001                | ECC's convergence epsilon }"
    "{o outputWarp      | outWarp.ecc       | output warp (matrix) filename }"
    "{m motionType    | affine                   | type of motion (translation, euclidean, affine, homography) }"
    "{v verbose             | 0                          | display initial and final images }"
    "{w warpedImfile   | warpedECC.png  | warped input image }"
;
===================================================================

接著會看到在main裡頭會先作CommandLineParser的宣告,宣告完之後會跟著一些parser資訊的程式碼:

int main (const int argc, const char * argv[])
{
    CommandLineParser parser(argc, argv, keys);

    string imgFile = parser.get<string>(0);
    string tempImgFile = parser.get<string>(1);
    string inWarpFile = parser.get<string>(2);

    int number_of_iterations = parser.get<int>("n");
    double termination_eps = parser.get<double>("e");
    string warpType = parser.get<string>("m");
    int verbose = parser.get<int>("v");
    string finalWarp = parser.get<string>("o");
    string warpedImFile = parser.get<string>("w");
       .
       .
       .
}
======================================================================

其實parser宣告完成之後,就可以用來取得key裡面的一些資訊,我們一行行來說明:

string imgFile = parser.get<string>(0); ===> 搭配key的寫法,這可以取到argv[1]的值,也就是執行檔後面第一個參數的值。

string tempImgFile = parser.get<string>(1); ===> 同理,可取到argv[2]的值。
string inWarpFile = parser.get<string>(2);  ===> 同理,可取到argv[3]的值。(若沒輸入,取到值為空)

 int number_of_iterations = parser.get<int>("n"); ==>可取出key的內容當中一開始有"n"的那一行並取到第一個符號("|")後面的值,在這邊我們可以取到"50"。

 double termination_eps = parser.get<double>("e"); ==>與上面同理,這範例當中我們可以取到"0.0001"
 string warpType = parser.get<string>("m"); ==>與上面同理,這範例當中我們可以取到"affine"
 int verbose = parser.get<int>("v"); ==>與上面同理,這範例當中我們可以取到"affine"
 string finalWarp = parser.get<string>("o"); ==>與上面同理,這範例當中我們可以取到"0"
 string warpedImFile = parser.get<string>("w"); ==>同理,我們可以取到"warpedECC.png"

而每一行的第二個"|"符號後面的資訊,類似說明的效果。


parser.printParams(); ==>可以把所有key的內容印出來


下圖是把這些parse出來的值印出來的結果:




2015年8月21日 星期五

在linux環境下,Windows與Linux檔案格式的差異性分析

    大家應該都有遇過這樣的問題,如果拿一個在windows環境下編輯的文字檔,拿到linux下作vim,在結尾的部分會出現^M的符號,其實這是因為windows以及linux換行符號不同所造成的。

首先介紹最常使用的三種作業系統,他們的換行符號如下所示:
       
      Linux:     "\n"
Windows:     "\r\n"
      MAC:     "\r"    (MAC的部分可能尚需查證,作者是網路上找的資料,並未實際求證)


以下我們作個實驗,首先在Windows系統下編輯一個檔案名為windows.txt,而內容則輸入"aaa"接著換行再輸入"bbb",編輯完成後把此檔案傳到Ubuntu為作業系統的電腦底下。

另外在Ubuntu為環境的電腦下,用vim編輯一個內容相同的檔案,名為linux.txt。



Linux底下有個好用的指令叫作"od",他可以用來查閱非文字檔內容,在這裡我們用這個指令查閱檔案內容,並以ASCII方式印出內容。

指令用法為 "od -c 檔名"


下圖是用上面的命令對windows.txt與linux.txt印出的結果:











我們可以明顯的看到 第一個印出來的windows.txt內容,換行符號確實是"\r\n",而linux.txt的換行符號卻是"\n",另外linux下編輯的檔案,也自動在第二行"bbb"的最後面多加了一個"\n"(作者vim這個檔案時,當輸入完"bbb"並沒有再按Enter鍵)

因此windows編輯的檔案要拿到linux下處理的時候,必須要特別小心注意!!


=========================================================
另外下面是一個可以把檔案中的內容,一行一行印出的shell script名子叫Danny_test.sh:

FILE_NAME="./windows.txt"

echo "" >> $FILE_NAME

if [ -f "$FILE_NAME" ]; then
        exec < $FILE_NAME
 
        while read line
        do
                #line=`echo $line| sed 's/\r//g'`
                line=`echo $line`
                echo $line
        done
fi

執行結果如下圖:









結果發現,只印出第一行aaa,bbb卻沒印,這裡有兩個原因:

1. 因為windows的換行符號\r\n跟linux的\n不同所造成
2. 因為windows下編輯的檔案結尾若沒有換行是不會加上\n的,linux下會自行加\n


接著分別解決方法
1. 使用sed 來將\r替換掉即可===>  line=`echo $line| sed 's/\r//g'`
2. echo "" >> windows.txt ,這樣作檔案的後面就會加上\n了


因此Danny_test.sh可以這樣寫:

FILE_NAME="./windows.txt"

echo "" >> $FILE_NAME

if [ -f "$FILE_NAME" ]; then
        exec < $FILE_NAME
 
        while read line
        do
                line=`echo $line| sed 's/\r//g'`
                echo $line
        done
fi

執行結果:


2015年8月5日 星期三

申請網卡MAC Address流程

每張網卡當出產時都需要有一組自己獨有的MAC Address,因此這樣的MAC位址是必須要申請的,還需要付一點費用。

負責管理MAC位址的是IEEE這個組織,要申請時請上他們的網站。
入口網如下面連結:
http://standards.ieee.org/develop/regauth/oui/index.html

我這邊來說明一下向IEEE申請時有以下幾種方案,如下圖所示,因為網頁都是英文版的因此我再詳細的對這三種方案說明一下:








1. MA-L(Mac Address Large)
申請這種方案感覺是最划算的,因為這方案可以使用的MAC Address數量最多,因為總共12位16進制的MAC,廠商ID會佔前六碼,其他六位數都由廠商自行使用,也就是說總共有16^6=16777216這麼多組可以使用,也就是約有1677萬個MAC Address可以使用。
接著我們來看一下申請所需的費用,如下圖所示:









第一欄位是申請MA-L方案所需的費用,大概是US.2575元。

第二欄位比較貴,因為第二欄位的種類是Private的,我的解讀是申請此種類的是無法在IEEE的網站上搜尋到公司對應的Address位址,因此還要額外再加上2980的費用。

第三欄位則是比第一欄貴了一點,它也是會對你的公司以及對應的MAC作隱藏的動作讓別人無法搜尋到,不過隱藏的時間只有一年。




2.MA-L(Mac Address Middle)
這個是IEEE新的方案,以往只有Large以及Small可以選,這個方案擁有的Mac Address數量大概有16^5=100萬個。費用表如下圖所示:










3.MA-S(Mac Address Small)
最後這個方案,針對的對象應該是比較偏向個人,因為它分配到的MAC總數量只有16^3=4096個,費用也只需要US.645元,感覺不太划算。










上面介紹完各種方案之後就看自己怎麼選擇囉!



各方案的申請網址(點選左邊欄位的Application)
MA-L       https://standards.ieee.org/regauth/application/mal/show


查詢公司申請的MAC(也可以透過點選左邊欄位 Public Listing到下面連結)

2015年7月29日 星期三

IE瀏覽器當VLC播放rtsp串流時切換頁面無回應的解決方法(onclick、onunload、herf優先順序)

今天在弄網頁的部分,網頁的內容有用到內嵌VLC,使用的Browser是IE

遇到一個很頭痛的問題是,當要從播放RTSP的VLC頁面中切換到別的頁面時,總是會造成IE Hang住,然後IE無回應 這時候IE只能強制關閉,然後重開。

猜想是因為離開頁面時,應該要呼叫VLC的Stop function先停止,再進行切換的動作。

先說明我網頁的架構如下圖所示,整個網頁有作分割式窗,上面小視窗的連結(herf)點擊之後,會在下面的main_Frame切換到超連結的頁面。另外每個Herf的區塊都有作自己要處理的onclick() function。
















後來我查了一些資料得知,當頁面要離開到別的頁面時,javascript的部分會去作windows.onunload() 函式,因此我就把這個VLC的Stop function加入在這個onunload()當中。但後來發現情況並沒有改善。

在查詢了一些資料後,又有一個重大發現,原來onclick()、onunload()、herf這三個東西是有一個優先順序的,動作的先後順序為 onclick() => onunload() => herf

因此我把VLC的Stop function加入到架構圖中的上面三個的onclick事件要做的function當中,就解決了這個IE瀏覽器當VLC播放rtsp串流時切換頁面無回應的問題。

下面程式碼為javascript,附上javascript在mainFrame以及topFrame分別要呼叫VLC stop的程式碼:

=============== mainFrame 的 javascript部分 ==============
function getVLC(name)  
{  
        if (window.document[name])    
        {  
                return window.document[name];  
        }  
        if (navigator.appName.indexOf("Microsoft Internet")==-1)  
        {  
                if (document.embeds && document.embeds[name])  
                        return document.embeds[name];    
        }  
        else  
        {  
                return document.getElementById(name);  
        }  
}  

getVLC("vlc").playlist.stop();





=============== topFrame 的 javascript部分 ==============
function getVLC(name)  
{  
        if (self.parent.frames["mainFrame"].document[name])    
        {  
                return self.parent.frames["mainFrame"].document[name];  
        }  
        if (navigator.appName.indexOf("Microsoft Internet")==-1)  
        {  
                if (self.parent.frames["mainFrame"].embeds && self.parent.frames["mainFrame"].document.embeds[name])  
                        return self.parent.frames["mainFrame"].document.embeds[name];    
        }  
        else  
        {  
                return self.parent.frames["mainFrame"].document.getElementById(name);  
        }  
}  

getVLC("vlc").playlist.stop();




2015年7月21日 星期二

cross compiler openssl遇到的 target already defined - linux-elf (offending arg: CC=arm-linux-gcc) 問題~

今天在研究openssl,準備針對抓下來的opensource作cross compiler。

openssl的下載點(OPENSSL),我抓的版本為openssl-1.0.2a。

首先解開壓縮包之後,先作configure的動作,指令如下:

#./config CC=arm-linux-gcc

但不知道為什麼,一直出現下面的訊息,導致config無法完成
==>  target already defined - linux-elf (offending arg: CC=arm-linux-gcc)



後來把指令改了一下,可以運作的指令如下:

#CC=arm-linux-gcc ./config no-asm shared



成功編譯完成的openssl執行檔會在 apps資料夾中

2015年7月1日 星期三

可變參數的function實作

由於最近寫的程式常常需要用到strcat ,而且一次要把好多個值strcat在一起

因此對可變參數的函式作了小小的研究,並寫成function以便日後使用


================== 正題開始 =================

在C/C++中,有所謂的variant argument(變動引數)。也就是可以讓函數
使用數量不固定的引數。printf()就是這類的函數。

定義一個函數的prototype(原型)時,若是將參數列以"..."代入,就表示這個函數即將
使用variant argument。如:

danny_func(char *result,  ...)

這樣子便可讓編繹器不檢驗傳入這種函數裡的引數型態和數量,編出來的程式碼在呼叫端
就能夠盡可能地把各式各樣引數傳入。


那麼,在如此的程式裡,要怎麼存取variant argument呢?因為缺少引數變數,所以我們
不可能像一般程式一樣直接存取它們,而是要改用stdarg.h裡面所提供的三個巨集與一個
型別,分別是:

va_list: 宣告一個指標,讓它指向引數串列。
va_start: 初始化這個指標,讓它真正指向正確的引數串列開頭。
va_arg: 來取得va_list中的資料。
va_end: 清除這個指標,把它設為NULL。範例如下:

因此打算實作一個 danny_strcat(char *result,  ...) 這樣子的function。

首先還是必須先使用到上面介紹的 linux當中的這些函式。




實作範例如下:
================= danny_strcat.c ====================
#include <stdio.h>
#include <string.h>
#include <stdarg.h>

void danny_strcat(char *result,  ...)
{
     va_list ap;
     char *p;
     va_start(ap, result);
     p = va_arg(ap, char *);

     while (p != NULL) {
      strcat(result, p);
      p = va_arg(ap, char*);
     }
     va_end(ap);
}

int main(void)

{
     char tmp[512]={0};
     char tmp2[32]="strcat";
     danny_strcat(tmp, "hello-", "world-", "danny-", tmp2, NULL);
     printf("In main tmp=%s\n",tmp);
     return 0;
}


輸出結果:






*目前的作法- 當呼叫這個可變參數的函式時,使用者必須在參數的最後面加上NULL,來告知它結束的點。

2015年6月13日 星期六

String轉unsigned long/String轉int/String轉float

有時候會需要用到一些型態轉換的C function因此這邊寫個小範例作個記錄~

=============== test_transit.c =================

#include <stdio.h>
     
int main()
{
        unsigned long ul;
        char str1[]="A5";
        //strtoul第一個參數帶入要轉換的自串
        //strtoul第三個參數帶入"要取幾進位的數"
        ul = strtoul(str1, NULL, 16); // string to unsigned long
        printf("==== String to unsigned long value(Hex)=%lX ====\n", ul);
        printf("==== String to unsigned long value(D)=%ld ====\n", ul);


        char str2[]="123";
        int a = atoi(str2);
        printf ("==== String to integer value=%d ====\n",a+10);


        char str3[]="100.23";
        float b = atof(str3);
        printf ("==== String to float value=%f ====\n",b);

        return 0;
}

===================================================

其中String轉unsigned long是成功的(OK)
String轉int也是成功的(OK)
但String轉float的部分有點問題,印出來的值跟轉換的字串不相同,這部分還需研究(Fail!) @@"
如果有研究出結果會在上來把float的部分更新


輸出結果:


2015年5月25日 星期一

判斷特定字串, 並改寫檔案的範例~

原本想要作到開啟一個設定檔,接著根據特定字串來直接改寫檔案

查詢了幾篇文章發現似乎無法這樣作

其中有個最主要的因素是因為:

文字檔是循序存放的,如果確定新資料與舊資料文字長度是一樣的,才可以直接作開檔、讀檔、再改寫。

舉個例子:
現在有一個設定檔login.info內容如下

username=danny
password=5566
location=taiwan

現在你想要讀出當字串為password時去改寫密碼的動作,假設你要新寫入的密碼為"123456",然後你使用直接開檔、讀檔、再改寫的方法,想必會變成下面的情形

username=danny
password=123456cation=taiwan

因為你新輸入的文字長度比舊的來的大,所以他會覆蓋到原本舊的其他的資料

=======================================================================
因此看來要改寫檔案就得用另一種方法了,這邊介紹的是,讀取原本檔案,然後寫入到另一個檔案去,沒有改寫的地方就照原本資料寫入,改寫的也一併寫入到新的資料。

最後再把原本資料砍掉,然後把新的資料改名成舊的資料名子。

詳細的範例如下所示:

首先是原始設定檔===========
devicename:dannyDevice
protocol:Bloger
resolution:HD
framerate:30



主要的程式碼部分============
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdarg.h>
#include <ctype.h>

#define DEVICE_INFO_PATH "./device.info"
#define TMP_DEVICE_INFO_PATH "./tmp_device.info"

void modify_file_func()
{
        char buffer[128];

/* open login info in device. */
FILE *fptr, *fptr_tmp;
char tmp[64]={0};

        if ((fptr = fopen(DEVICE_INFO_PATH, "r")) == NULL)
        {
                printf("open_file_error");
                exit(1);
        }
else if((fptr_tmp = fopen(TMP_DEVICE_INFO_PATH, "w")) == NULL)
{
                printf("open_tmp_file_error");
                exit(1);
}

while(fgets(buffer, sizeof(buffer), fptr) != NULL)
        {
if(strstr(buffer, "protocol:"))
{
sprintf(tmp,"protocol:%s\n", 123);
fputs(tmp, fptr_tmp);
memset(tmp,'\0',sizeof(tmp));
}
else if(strstr(buffer, "resolution:"))
{
sprintf(tmp,"resolution:%s\n", 456);
fputs(tmp, fptr_tmp);
memset(tmp,'\0',sizeof(tmp));
}
else if(strstr(buffer, "framerate:"))
{
sprintf(tmp,"framerate:%s\n", 789);
fputs(tmp, fptr_tmp);
memset(tmp,'\0',sizeof(tmp));
}
else
fputs(buffer, fptr_tmp);
        }
        fclose(fptr);
fclose(fptr_tmp);
        
        //刪掉原檔案,並改寫新檔案的名子
sprintf(tmp,"rm %s;mv %s %s", DEVICE_INFO_PATH, TMP_DEVICE_INFO_PATH, DEVICE_INFO_PATH);
system(tmp);
}


int main()
{
modify_file_func();
return 0;
}


最後的device.info檔案內容=======
devicename:dannyDevice
protocol:123
resolution:456
framerate:789



2015年5月21日 星期四

shell script中判斷命令是否正確成功的作法

首先先稍微了解一下 "1>/dev/null 2>&1"

0 = standard input (stdin)
1 = standard output (stdout)
2 = standard error (stderr)

"1>/dev/null 2>&1" 首先stdout的標準輸出的訊息會先丟到/dev/null,接著stderr的訊息也一起丟到stdout也就是丟到/dev/null,所以訊息不會顯示在螢幕上。

而/dev/null,是空裝置,是一個特殊的裝置檔案,它會丟棄一切寫入其中的資料。


======================================================================

下面我們寫一個簡單的範例來練習, 用一個正確的command($ok_cmd)以及故意寫一個錯誤的command($error_cmd),指令成功會回傳1,錯誤則回傳0,執行並判斷command是否正確。

程式碼如下:
#!/bin/sh

ok_cmd='ls -al'

error_cmd='ls --al'

if $ok_cmd 1>/dev/null 2>&1; then
    echo "ok_cmd is OK!."
else
    echo "ok_cmd is ERROR!."
fi

if $error_cmd 1>/dev/null 2>&1; then
    echo "error_cmd is OK!."
else
    echo "error_cmd is ERROR!."
fi



結果輸出:



2015年5月6日 星期三

當apt_get_update時遇到問題時的解決方法 ERROR MSG:(Problem with MergeList)(status file could not be parsed or opened)

之前要作 apt-get update,當更新到最後時出現了下面的Error message:

E: Encountered a section with no Package: header
E: Problem with MergeList /var/lib/apt/lists/archive.ubuntu.com_ubuntu_dists_natty_main_binary-i386_Packages
E: The package lists or status file could not be parsed or opened.

======================================================================

當遇到上面的錯誤情形時,只需要作以下幾個步驟就可以解決了:

首先叫出Terminal接著輸入以下幾個指令

1. sudo rm /var/lib/apt/lists/* -vf

2. sudo apt-get update


註:執行上面兩個指令之前,可以先將 /var/lib/apt/list/ 底下的東西全部備份,以免作了上面兩個步驟之後還是無法解決的話,可以復原 :D

如何把make時的錯誤訊息輸出到檔案

寫程式編譯的時候,有時候錯誤訊息很多

想要把所有的錯誤訊息都輸出到一個檔案,可以用下面的指令來實現~

make 2> error_msg

數字 含義 標准叫法
0 標准輸入  stdin(standard input )
1 標准輸出  stdout(standard output )
2 標准錯誤輸出  stderr(standard error)

而只下make,系統預設的是會把這三種訊息stdin、stdout、stderr都印到螢幕上。

想要將對應的信息輸出到某個檔案中,就用對應的數字加上重定向符號'>'來輸出到文件中。

下面是幾個範例:

1.想要把make輸出的全部信息,輸出到某個文件中,最常見的辦法就是:
make > error_msg 

此時預設情況是沒有改變2=stderr的輸出方式,還是輸出到螢幕,所以,如果有錯誤訊息,還是可以在螢幕上看到。


2.想要把make輸出中的錯誤與警告訊息輸出到檔案,可以使用:
make 2> error_msg

由於1=stdout沒有變,還是輸出到螢幕,所以,那些命令執行時候輸出的正常訊息,你還是可以在螢幕上看到。


3.只需要把make輸出中的正常的信息輸出到文件中(例如要看Makefile作了哪些編譯動作),可以用:
make 1> build_msg

由於2=stder沒有變,還是輸出到螢幕,所以,那些命令執行時候輸出的錯誤信息,你還是可以在螢幕上看到的。


4.想要把正常輸出信息和錯誤信息輸出到分別的文件中,可以用:
make 1> build_msg 2>error.msg 


5. 所有的信息都輸出到同一個文件中:
make > build_all_msg 2>&1 

其中的2>&1表示錯誤信息輸出到&1中,而&1,指的是前面的那個文件:build_all_msg 。

注意:上面所有的1,2等數字,後面緊跟着大於符號'>' ,中間不能有空格


2015年4月27日 星期一

fopen的開檔,讀檔與寫檔

開檔,讀檔,寫檔在資訊科學這門課程是相當基礎的資料處理,

但是假使平常比較不常做這些處理,當要你作的時候,真的是很要人命

因此作一些筆記是必要的,到時候要處理檔案的時候,就可以很快上手了

這邊分三部分來作筆記,分別為讀檔,寫檔,讀檔並寫檔

===========================================================
首先是讀檔的部分:

讀檔和寫檔是較為簡單的部分,讀檔的話基本上第一步先對檔案作fopen

接著只要使用fgets,這支C函式可以幫你把檔案一行一行的讀出來,fgets也會在每一行讀到結
尾"\n"時幫你把檔案指標指到下一行的起始

簡單的範例如下:
///////////// Read_File.c////////////////////
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define PATH "/etc/login.info"

int main()
{
    FILE *fptr;
    char login_info[128];

    if ((fptr = fopen(PATH, "r")) == NULL)
    {
                printf("open_file_error");
                exit(1);
     }
   
     while(fgets(login_info, 128, fptr) != NULL)
     {
                //fgets會把每一行資訊copy到login_info字元陣列
                //此處油開發者自行決定要對讀出的內容作甚麼處理
                //寫檔則再搭配使用 fwrite(buffer,1,sizeof(buffer),fptr);
      }
      fclose(fptr);
      return 1;
}
///////////////////////////////////////////////////////////////////////

fopen的第二個參數,用法請參考下圖:




接著是讀檔並且同時寫檔,作者這邊就只作一些重點整理了

首先也是對檔案作fopen,後面參數就請填上"r+"

接著以下這幾個function的使用就是讀檔並同時寫檔的重點

**********************************************
int ftell(FILE *fptr);
==>這支function可以取到目前fptr所offset的位置

舉個例子有個login.info的檔案內容如下:
username:admin
password:1234

那我用fptr=fopen("login.info", "r+")之後再搭配fget一次之後去作ftell(fptr)會等於15
也就是此時指標的位置會是在第二行一開頭
******************************************************

******************************************************
第二個重要的是
rewind(FILE *fptr);
==>這支function可以把fptr指標移到最前頭
*******************************************************

**************************************************
最後一個是
fseek(FILE *fptr, int offset, SEEK_SET);
==>此fuction可以幫你把fptr的指標位置移到你想要開始的地方

接著你可以作fprintf(fptr, " "); 你就可以修改你想改寫的部分了
******************************************************


2015年4月21日 星期二

C語言cgi搭配ajax的傳遞,搭配的Server是thttpd

今天要弄一個登入的功能

作法是html去作js檔裡面的Login函式

有問題的地方是在作Login函式時,會再透過ajax去送data給cgi作id與passwd的判斷

javascript的ajax程式碼如下所示:(擷取部分)

/////////////////////////////////////////////////////////////////////////////////
var url_Cgi="./cgi-bin/login.cgi";

var TransData = String.format("username={0}&password={1}",
UserName, Password);

$.ajax({
        url: url_Cgi,
        type: 'GET',
        async: false,
        data: TransData,
        error: function()
{
            AlertNetworkError();  
        },
        success: function(result)
{
if(result == login_success)
{
location = "main.html";
}
else
{
ShowAlert(result) );
}
        }

送過去的data可以知道是ajax的data部分,ajax會呼叫cgi程式,並把要送到cgi的字串帶到login.cgi的後面(例如login.cgi?username=danny&password=0000)

今天遇到的問題就是cgi怎麼把值回傳給ajax的result

原本以為cgi程式那邊的return值會傳給ajax的result,原來我的想法是錯的

cgi如果要想傳值給ajax的result,其實只需在程式裡面作下面的程式碼:

printf("Content-type:text/html\n\n");
printf("login_success");

這樣子作,ajax那邊拿到的result值就會是"login_success"了!


2015年4月17日 星期五

用C寫cgi時 popen或system遇到取值是空的問題

今天在寫cgi網頁時,用了前幾天學到的popen取設備端ip值的方法,如下:

char MyIpBuf[ MY_IP_LENTH ]={0};
FILE *fpRead;

//使用shell command來取得ip值  
char* command=(char*)"ifconfig eth0|grep 'inet addr' |awk -F \":\" '{print $2}' |awk '{print $1}'";
char* renewCh;
 
fpRead = popen(command, "r");
fgets(MyIpBuf, MY_IP_LENTH , fpRead);


奇怪的是,在browser輸入這個cgi網址時,怎麼popen取到的MyIpBuf值是空的,


這個問題讓我找了好久,也嘗試用system先輸出到檔案的方式,值還是空

奇怪的是,如果編譯成可執行檔,直接在terminal執行都取的到我要的值,

但是只要是編譯成cgi透過Browser執行,就會是空


後來發現把ifconfig改成完整的路徑,如下面這樣就沒有問題了:

char* command=(char*)"/sbin/ifconfig eth0|grep 'inet addr' |awk -F \":\" '{print $2}' |awk '{print $1}'";

猜測應該是Browser執行shell時,PATH路徑沒有/sbin因此找不到ifconfig這個指令吧?!

2015年4月15日 星期三

libstdc++ 太過龐大的瘦身方法 arm-linux-strip

最近在porting live555 到我們arm的板子上

由於板子只有16MB的空間可供使用(uboot+kernel+squashfs+user_space),可以說空間相當的吃緊

偏偏在作live555 cross-compiler放到板子後,竟然缺少了libstdc++.so.6這個C++的library。



缺了就補給他,於是跑去toolchain中找這個for arm的C++ libray,發現檔案大小竟然是3.7MB


若是這個檔案放上板子,空間就被去掉1/4了 @@"

於是就找了一些瘦身的方法,找到一個不錯的指令 "arm-linux-strip"

其作用是: 去掉elf可執行文件的資訊,使可執行文件變小


其實也是標準編譯時就可以加入的指令,使用之後發現還真不錯用 3.7MB變成----> 600KB左右

真是解決了我的心頭大患,不過如果編譯過程已經有加入這指令,你再執行一次大小是不會改變的

另外有看到一個滿厲害的東西 "用C++寫code 不用用到C++ lib去編譯" 可以參考這篇作者寫的

http://ptspts.blogspot.tw/2010/12/how-to-write-c-program-without-libstdc.html


不過要這樣做,在寫code的過程中,會有一些header檔不能include的限制~

原來busybox --install 的意思是這樣

今天學習了 busybox --install . 這指令的意義

第二個參數--install,busybox會把自己所有的指令集以hard link的方式安裝在當下路徑當中


另外一個指令是 busybox --install -s .

則是代表busybox會把自己所有的指令集以soft link的方式安裝在當下路徑當中

linux的環境下,在C程式裡popen的用法&取得ip位置的範例

記得以前有寫過network setting的頁面,其中必須取得現在環境中的IP值。

記得還是菜逼八的時候,是用system call去呼叫事先寫好的script,這支script作了ifconfig之後搭配sed或是awk篩選出 IP值之後再用 echo 輸出到某個檔案。

而最後要取IP值時,再想辦法從檔案中拿出來...




其實根本不用作的這麼複雜,使用popen()來作,還可以直接把取到的值放入變數中。
有關popen的功能以及原型,如下所示:

FILE *popen(const char *command, const char *type);

簡單的說就是popen會把command這個字串使用shell的方式去執行,接著輸出到C的程序裡。




下面的範例,就是一個可以取得IP位址的範例:
//============== get_ip.c ================
#include <stdio.h>
#include <string.h>

#define MY_IP_LENTH 32

int main()
{
     char MyIpBuf[ MY_IP_LENTH ]={0};
     FILE *fpRead;

     //使用shell command來取得ip值   
    char* command=(char*)"ifconfig eth0|grep 'inet addr' |awk -F \":\" '{print $2}' |awk '{print $1}'";
    char* renewCh;

    fpRead = popen(command, "r");
    fgets(MyIpBuf, MY_IP_LENTH , fpRead);

    //記得作pclose()的動作
    if(fpRead != NULL)
pclose(fpRead);
 
    //最後檢查取出的字串當中是否有多餘的換行,若有直接取代為'\0'作結尾
    renewCh=strstr(MyIpBuf,"\n");
    if(renewCh)
*renewCh= '\0';

    printf("=== %s ===\n", MyIpBuf);

    return 0;
}


輸出結果:


2015年4月3日 星期五

Linux環境下,thttpd支援cgi遇到的問題

架設好thttpd Server之後,在測試cgi是否能正常運作時遇到了一點點的問題

在多方詢問與請教估狗大神之後,終於有了頭緒。

首先我實驗的網址是我的本機端 http://localhost/cgi-bin/HelloWorld.cgi

總共會遇到的問題可能有三個,下面將一一描述:


1. 首先來看看thttp.conf這個設定檔
#======= thttp.conf ========
# This section overrides defaults
dir=/home/httpd/html
chroot  
user=root
# default = nobody
.....
.....
.....

要使用cgi第一部分要注意的是上面的紅字的部分,這樣的寫法當你要去執上面的網址時
會遇到下面的狀況:













Solution: 修改thttpd.conf請將chroot註解掉,如下所示:
#======= thttp.conf ========
# This section overrides defaults
dir=/home/httpd/html
#chroot  
user=root
# default = nobody
.....
.....
.....



2.第二個會遇到的問題,當你輸入網址之後可能遇到下面的狀況:













Solution: 遇到這個問題請先去修改thttpd.conf,可能原因是 "cgipat=/cgi-bin/"後面必須加個"*" 

正確的內容如下所示:

#======== thttpd.conf =========
# This section overrides defaults
dir=/home/httpd/html
#chroot
user=root
# default = nobody
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
# This section _documents_ defaults in effect
port=80
# nosymlink# default = !chroot
# novhost
cgipat=/cgi-bin/*        <======後面加個"*"



3. 第三個會遇到的問題是,當輸入網址後Browser會出現下載cgi檔案的提示,而不是顯示出你要的cgi頁面

Solution: 這個問題是因為你cgi檔案設定的權限有問題,似乎是因為cgi檔案必須要有可執行權限,解決方法是只需用以下指令
# chmod 755 HelloWorld.cgi

權限更改成功之後 終於看到cgi要show的畫面了 :D














總結: cgi網頁有問題時

1. 請修改thttpd.conf如下:
#======== thttpd.conf =========
# This section overrides defaults
dir=/home/httpd/html
#chroot
user=root
# default = nobody
logfile=/var/log/thttpd.log
pidfile=/var/run/thttpd.pid
# This section _documents_ defaults in effect
port=80
# nosymlink# default = !chroot
# novhost
cgipat=/cgi-bin/*   


2.# chmod 755 HelloWorld.cgi


以上~ 




2015年4月1日 星期三

嵌入式網頁遇到 The requested URL '/' resolves to a file which is marked executable but is not a CGI file 的問題

Device上面已經架設好了thttpd,也有順利的執行幾個網頁

今天要連上Device的某個網頁時,Browser上面一直出現錯誤訊息,如下所示:






接著一直著墨在html語法方面,一直尋找是不是有甚麼地方筆誤了

後來在google上找到了解法:

其實只需要把你連的這個網頁改成不可執行,其實就不會有這樣的錯誤訊息了。

所以請更改網頁的權限,指令如下:

# chmod a-x index.html

就不會有問題了!

2015年3月23日 星期一

(忘記root密碼的解決方法or切換root不知道密碼)&(直接切換root指令)

在Terminal使用su指令切換卻發現密碼錯誤。

要解決此問題請依照下面步驟:
1. 在終端輸入 sudo passwd root <----這一步驟會叫你先輸入當前使用者的密碼

2. 接著會出現"Enter new UNIX password:",這時請輸入要設定的root的密碼

3. 然後再輸入comfirm一次即可

下次你再輸入su時,鍵入設定的密碼即可切換成root身分


整個流程如下圖所示:



***補充

後來發現想要直接切換成root其實不用這麼麻煩

只需要下"sudo su -"

接下來會要求你輸入當前使用者的密碼,輸入成功後就切換成root了



2015年3月19日 星期四

宣告struct的兩種方式(struct與typedef struct)/struct給值的兩種方式

之前寫了許多struct的程式,因為趕project沒有對這些寫法特別去研究(多數是copy, paste Orz),現在比較有空了,就研究了一下並且做個紀錄。

首先是宣告struct的方法,分別有兩種方式首先講第一種(struct):
假設現在要宣告一個student的一個結構(struct),裡面包含學生的學號(id)以及年紀(age)可以這樣子寫=====>
struct Student{       //定義
     int id;
     int age;
};
而使用的寫法如下===>
struct Student stu[2];   //宣告
stu[0].id=903135;       //給值
stu[0].age=19;
stu[1].id=903137;
stu[1].age=20;


再講第二種寫法之前,稍微說明一下typedef這個東西,它的用法如下:
typedef int _Uint32;  //把_Uint32這個字串定義為等同於int
_Uint32 i=0;              //用_Uint32宣告整數變數
我相信大家對這種用法都滿熟悉的~


接下來如果使用第二種(typedef struct)的方式的話:
是這樣子寫的====>
typedef struct{     //定義
     int id;
     int age;
}Student;
上面的寫法可以看成Student被定義成是種struct型態(Student代表struct Student),並且成員有id與age。

因此使用的方式是這樣子寫的====>
Student stu[2];            //宣告
stu[0].id=903135;       //給值
stu[0].age=19;
stu[1].id=903137;
stu[1].age=20;

也可以寫成這樣:
Student stu[2] = {
{903135, 19},{903137, 20}
}

====================分隔線======================

另外一個Topic是struct給值的方法,上面的給值方式是我們比較常看到的,另外還有一種進階的寫法,是這樣子寫的====>
struct Student{       //定義
     int id;
     int age;
};

struct Student stu[2]=
{
          [0]={
                        .id=903135,
                        .age=19,
                 },
          [1]={
                        .id=903137,
                        .age=20,
                 },
}
**注意這種寫法式宣告同時就給值,給值的部分都是用逗號區隔喔(不是分號哦)!**

以下程式是實作的範例
//================= struct_example.c =================//
#include <stdio.h>

struct staff
{
int id;
int age;
};

typedef struct
{
int id;
int age;
}student;

int main()
{
int i;
// ==========================================
// struct first define usage

struct staff sta[2]=
{
[0]={
.id=100001,
.age=22,
   },
[1]={
.id=100002,
.age=23,
   },
};
// ==========================================

// ==========================================
// struct another define usage

student stu[2]={
      [0]={
.id=903135,
.age=19,
  },
      [1]={
.id=903137,
.age=20,
  },
};
// ============================================

for(i=0; i<2; i++)
        {
                printf("struct staff[%d].id=%d\n", i+1, sta[i].id);
                printf("struct staff[%d].age=%d\n", i+1, sta[i].age);
                printf("\n");
        }

printf("\n");

for(i=0; i<2; i++)
{
printf("struct student[%d].id=%d\n", i+1, stu[i].id);
printf("struct student[%d].age=%d\n", i+1, stu[i].age);
printf("\n");
}

return 0;
}
//====================== End =======================//


輸出結果如下:









2015年3月18日 星期三

在linux系統下,"ls -l"前面的符號或英文分別對應的檔案類型

在linux裡面,下"la -l"會看到如下圖當中的資訊:



最左邊列出的第一個字元,我們最常看到的標誌 為" d 或 - ",分別代表目錄(directory)或檔案為此檔案的種類,以下的內容擷取自鳥哥,詳細的說明了各種符號或是字母所代表的檔案類型:

Linux 的檔案種類主要有底下這幾種:
 
正規檔案( regular file ):就是一般類型的檔案,在由 ls –al 所顯示出來的屬性方面,第一個屬性為 [ - ]。另外,依照檔案的內容,又大略可以分為兩種檔案種類:

  • 純文字檔(ascii) :這是 Unix 系統中最多的一種囉,幾乎只要我們可以用來做為設定的檔案都屬於這一種;
  • 二進位檔(binary) :通常執行檔除了 scripts (文字型批次檔)之外,就是這一種檔案格式;

 
目錄 (directory):就是目錄啦!第一個屬性為 [ d ];
 
連結檔 (link):就是類似 Windows 底下的捷徑啦!第一個屬性為 [ l ];
 
設備檔 (device):與系統周邊相關的一些檔案,通常都集中在 /dev 這個目錄之下!通常又分為兩種:

  • 區塊 (block) 設備檔 :就是一些儲存資料,以提供系統存取的周邊設備,簡單的說就是硬碟啦!例如你的一號硬碟的代碼是 /dev/hda1 等等的檔案啦!第一個屬性為 [ b ];
  • 字元 (character) 設備檔 :亦即是一些序列埠的周邊設備,例如鍵盤、滑鼠等等!第一個屬性為 [ c ]。

 
那麼使用剛剛的『 ls -al 』這個指令,你就可以簡單的經由判斷每一個檔案的第一個屬性來瞭解這個檔案是何種類型!很簡單吧!
除了設備檔是我們系統中很重要的檔案,最好不要隨意修改之外(通常他也不會讓你修改的啦!),另一個比較有趣的檔案就是連結檔。如果你常常將應用程式捉到桌面來的話,你就應該知道在 Windows 底下有所謂的『捷徑』。同樣的,你可以將 linux 下的連結檔簡單的視為一個檔案或目錄的捷徑。但是基本上這兩個東西是不一樣的!在 Windows 系統的捷徑中,你將無法修改主程式,但是在 Linux 中,連結檔可以直接連結到主程式,因此你只要改了這個連結檔,則主程式亦被改變了!

a++與++a的差異

a++是先調用a的值,再把a的值加1; 換句話說a++是先處理其他的運算,然後a再加1
而++a則是先把a的值加1,再調用a的值; 也就是說++a是先把本身的值加1,然後再處理其他運算

接著看下面的兩個例子就可以清楚的了解:

範例一:

#include<stdio.h>

int main()
{
int a,b,c;
a=5;

b=a++;
printf("round_one a=%d, b=%d\n", a, b);

c=++a;
printf("round_two a=%d, c=%d\n", a, c);

return 0
}

瞭解了嗎,輸出結果如下===>
round_one a=6, b=5
round_two a=7, c=7


範例二:

#include <stdio.h>

int main ()
{
int a=5;
int b=10;

printf("a=%d\n",a++);   //印出a=5

printf("a=%d\n",a);   //印出a=6

      printf("b=%d\n",++b);   //印出b=11

        printf("b=%d\n",b);   //印出b=11

return 0;
}

2015年3月16日 星期一

NOR Flash與NAND Flash介紹

        幾乎每個人都聽過CF卡(Compact Flash)跟SD卡,在各種消費性產品上,例如數位相機或是手機上面都可以看到。這些記憶卡模組的底層技術,使用的就是快閃記憶體(Flash memory)。我們可以把它想像成類似固態硬碟的東西,可以在很小的空間裡儲存好幾MB、甚至好幾GB的資料,裡面沒有會動的碟片,很堅固耐用,而且只需單一電壓即可運作。接下來我們來了解一下快閃記憶體一些特別的地方。

         快閃記憶體被劃分成一塊塊相當大的可抹除單位,稱之為抹除區塊(erase block)。要區別各種快閃記憶體技術的一項決定性特徵,就是快閃記憶體是如何寫入與抹除資料的。


一. NOR Flash
       
        拿一顆普通的NOR Flash來說,經由軟體下指令直接把資料寫進某位置,我們就可以把一個位元的資料從1改成0,一次改一個位元(bit)或是一個字組(word)都可以。不過想從0改回1,就要先用一串特殊指令先把整個抹除區塊清掉才行。

        普通的NOR Flash被劃分為很多個抹除區塊,例如: 一個4MB的晶片可能有64個抹除區塊,每個大小為64KB,有些快閃記憶體的抹除區塊大小可以不同,這樣一來資料儲存時就更有彈性了,這些通常被稱為啟動區塊(boot block)或是啟動區段(boot sector)快閃記憶體晶片。開機載入程式位於較小的區塊,核心跟其他必備資料夾放在較大的區塊。
       
         想修改快閃記憶體中的某資料,必須先把資料所在的區塊整個抹除掉,即使只有1byte的資料需要更動,還是需要把整個區塊抹除掉,然後再把資料寫進去。跟硬碟磁區的大小相比,硬碟可單一寫入的磁區單位大小只有512byte或是1024byte,這一點的差異就可以知道NOR Flash更新資料所需花費的寫入時間會是硬碟的好幾倍。


二. NAND Flash

        NAND與NOR兩者的差異跟快閃記憶體內部的單元架構有關,NAND比NOR的快閃記憶體少了些限制,因為區塊可較小,所以寫入速度就變快了,使用起來也更有效率。

        NOR Flash跟微處理器之間的介面,也跟其他和微處理器作連接的裝置很類似,它們都有獨立運作的資料與位置匯流排,直接跟微處理器的資料/位址匯流排相連,快閃記憶體陣列中任何1 byte或word都可用某位置標示出來。相對而言,NAND Flash的裝置是以串列的方式作傳輸,介面很複雜,每家廠商作法都不同。使用NAND的裝置其操作模式比較類似傳統的硬碟與其控制器之間的傳輸介面,資料以一段段資料脈衝的方試存取,跟NOR的區塊比起來小多了,NAND的寫入時間比NOR長上10倍,不過抹除時間快很多。

       


總結:

        NOR可以直接被微處理器存取,放在NOR上的程式碼甚至可以直接被執行(不過基於效能考量很少這麼作,除非是在資源極少的系統上),實際上很多處理器都沒辦法像存取動態記憶體(DRAM)那樣,對快閃記憶體進行快取,所以執行起來速度就更慢了。另一方面,NAND比較適合用在以整批整塊方式儲存的那種檔案系統,不適合用在儲存二進位執行檔程式碼跟資料上。




Reference by: [嵌入式系統開發實務]~

2015年3月13日 星期五

查看目前Linux核心版本的指令

請在Terminal輸入以下指令:
$ cat /proc/version

輸出結果如下圖所示:


2015年3月10日 星期二

字串反轉-範例練習(錯誤發掘&修正)

一開始在練習這個範例的時候我是這樣寫的:


/******************** reverse function exercise has two error*****************/

#include <stdio.h>
#include <string.h>

int reverse(char *str)
{
      int i,j;
      char reg;
      j = strlen(str);

      for(i=0; i<j; i++)
      {
            reg = str[i];
            str[i] =str[j];
            str[j] = reg;
            j--;
      }
      return 0;
}

int main()
{
      char *m_str = "12345";
      reverse(m_str);
      printf("After reversing m_str=%s\n", m_str);
      return 0;
}

/**********************************************************/
上面的程式,編譯過程很順利,但是執行起來卻有兩個嚴重的錯誤,先賣個關子,各位看倌有看出來嗎?









首先第一個錯誤======> 執行後會看到錯誤訊息 "Segmentation fault",發生原因在於main裡面的 char *m_str= "12345"宣告,這樣的宣告方式只宣告了指標,但是並沒有實體位置,因此會有這個錯誤訊息。

上面問題解決之後,編譯完成後再執行一次執行檔,輸出結果如下:
         After reversing m_str=

m_str的值竟然會是空的! 這是第二個錯誤的地方。

而發生的原因是因為reverse() 這隻function裡面,在計算str的長度時忘了減1。解釋如下:
原始的str內容===> str[0]='1', str[1]='2', str[2]='3', str[3]='4', str[4]='5' 另外還有一個結尾字元 str[5]='/0'



因此在字元陣列交換時誤把str[5]的'/0'交換到了str[0],因此printf出來才會是空的值。


把這兩個問題修改過後,就沒有問題了,正確的程式碼如下:

/********************reverse function exercise*************************************/

#include <stdio.h>
#include <string.h>

int reverse(char *str)
{
      int i,j;
      char reg;
      j = strlen(str)-1;

      for(i=0; i<j; i++)
      {
            reg = str[i];
            str[i] =str[j];
            str[j] = reg;
            j--;
      }
      return 0;
}

int main()
{
      char m_str[] = "12345";
      reverse(m_str);
      printf("After reversing m_str=%s\n", m_str);
      return 0;
}

/**********************************************************/

2015年3月6日 星期五

./configure 遇到的錯誤訊息 bin/sh^M: bad interpreter: No such file or directory

今天在下載libjpeg並要進行configure時出現了===> "bin/sh^M: bad interpreter: No such file or directory" 的錯誤訊息,解決了之後幫自己作一個紀錄。

會發生的原因: 
    原因是configure這個script是在windows系統下用記事本文件編寫的。不同系統的編碼格式所引起的。為了確認是這個原因造成,請作下面幾個動作:

   1. vi 或是 vim這個configure檔案
   2. 進到vi的 command mode(按下ESC)
   3. 鍵入 :set fileformat 或是 :set ff

結果會看到 fileformat=dos


解決方法:
    1. vi 或是 vim這個configure檔案
    2. 進到vi的 command mode(按下ESC)
    3. 鍵入 :set fileformat=unix 或是 :set ff=unix
    4. 鍵入 :wq進行儲存

這樣子在下一次./configure就不會有這樣的錯誤訊息了~

2015年3月5日 星期四

Makefile的賦值運算符(=, :=, +=, ?=)

在Makefile的文件當中常常會看到這四種賦值運算符,下面我們寫個範例來介紹一下它們各別代表的意思。

首先編寫一個Makefile檔
ifdef DEFINE_INIT_VALUE
    VALUE = "Hello!"
endif

ifeq ($(CMD),colon)
    VALUE := “Hello all the World!”
endif

ifeq ($(CMD),question_mark)
    VALUE ?= “Hello World!”
endif

ifeq ($(CMD),plus)
    VALUE += “Danny!”
endif



all:
    @echo $(VALUE )


看看這幾種賦值運算的結果:
make DEFINE_INIT_VALUE=true CMD=colon                 輸出: ==>  Hello all the World!
make DEFINE_INIT_VALUE=true CMD=question_mark  輸出: ==>  Hello!
make DEFINE_INIT_VALUE=true CMD=plus                   輸出: ==>  Hello! Danny!

make DEFINE_INIT_VALUE= CMD=colon                 輸出: ==>  Hello all the World!
make DEFINE_INIT_VALUE= CMD=question_mark  輸出: ==>  Hello World!
make DEFINE_INIT_VALUE= CMD=plus                   輸出: ==>  Danny!


總結:
= 是最基本的賦值
:= 會覆蓋變數之前的值
?= 變數為空時才給值,不然則維持之前的值
+= 將值附加到變數的後面



另外 =, :=這兩賦值運算符號在網路上查詢,常常會看到這種說法:
= 在執行時擴展(values within it are recursively expanded when the variable is used, not when it's declared)
:= 在定義時擴展(values within it are expanded at declaration time)
而白話一點的說法如下範例所示


      1. =

      make會將整個makefile展開後,才決定變數的值。也就是说,變數的值會是整個Makefile中最後被指定的值。看例子:

            x = hello
            y = $(x) world!
            x = hi

            all:
                 @echo $(y)

      在上例中,輸出結果將會是 hi world! ,而不是 hello world!

      2. :=

      變數的值在Makefile展開途中就會被給定,而不是整個Makefile展開後的最终值。

            x := hello
            y := $(x) world!
            x := hi

            all:
                 @echo $(y)

      輸出結果 ====> hello world!

智力測驗-秤重問題

題目一:
    有8顆球,其中7個重量一樣,只有1個較輕,用一個天平,試以最少測量次數來找出有問題的那一個球。

答案:
    最少為兩次可找出。

解法:
   將球編號並分堆如下:
A = [1, 2, 3]
B = [4, 5, 6]
C = [7, 8]
  1. 如果A=B,則C有問題。
    • 如果7>8,則8有問題。
    • 如果7<8,則7有問題。
  2. 如果A>B,則B有問題。
    • 如果4=5,則6有問題。
    • 如果4>5,則5有問題。
    • 如果4<5,則4有問題。
  3. 如果A<B,則A有問題。
    • 如果1=2,則3有問題。
    • 如果1>2,則2有問題。
    • 如果1<2,則1有問題。



還有另一個較複雜的衍申題

題目二:
    籃子裡面有10顆球,其中一顆有問題,請你利用天平,秤重三次之後把那顆球找出來,並說明他是比較重或輕?

答案&解法:
    將球編號並分堆如下:
A = [1, 2, 3]
B = [4, 5, 6]
C = [7, 8, 9]
D = [10]
  1. 如果A=B,則C或10有問題。
    • 如果A=C,則10有問題。
      • 如果1>10,則10較輕。
      • 如果1<10,則10較重。
    • 如果A>C,則C有問題且較輕。
      • 如果7=8,則9較輕。
      • 如果7>8,則8較輕。
      • 如果7<8,則7較輕。
    • 如果A<C,則C有問題且較重。
      • 如果7=8,則9較重。
      • 如果7>8,則7較重。
      • 如果7<8,則8較重。
  2. 如果A>B,則A或B有問題,且A較重或B較輕。
    • 如果A=C,則B有問題且較輕。
      • 如果4=5,則6較輕。
      • 如果4>5,則5較輕。
      • 如果4<5,則4較輕。
    • 如果A>C,則A有問題且較重。
      • 如果1=2,則3較重。
      • 如果1>2,則1較重。
      • 如果1<2,則2較重。
  3. 如果A<B,則A或B有問題,且A較輕或B較重。
    • 如果A=C,則B有問題且較重。
      • 如果4=5,則6較重。
      • 如果4>5,則4較重。
      • 如果4<5,則5較重。
    • 如果A<C,則A有問題且較輕。
      • 如果1=2,則3較輕。
      • 如果1>2,則2較輕。
      • 如果1<2,則1較輕。

2015年3月2日 星期一

union的使用時機&struct的對齊(natural alignment)

繼前篇所討論的struct與union的差異, 這邊有個使用union的範例可以參考~

假設現在有三個種類的封包要被傳送, 而同一時間只會送出這三類的其中一類封包, 同時我們用struct與union兩種方式宣告, 在執行結果進行比較所需佔用的記憶體空間。

因此程式可以這樣寫:



//*****************example_union_usage.c****************//

#include <stdio.h>

struct structA
{
   int a;
   char b;
};

struct structB
{
   char a;
   short b;
};

struct structC
{
   int a;
   char b;
   float c;
};

struct CommuPacket       //使用union的
{
   int iPacketType;
   union
   {
        struct structA packetA;
        struct structB packetB;
        struct structC packetC;
   };
};

struct CommuPacket_2
{
   int iPacketType;
   struct
   {
        struct structA packetA;
        struct structB packetB;
        struct structC packetC;
   };
};

int main()
{
   struct CommuPacket struct_CommuPacket;
   struct CommuPacket_2 struct_CommuPacket_2;

   int struct_size, struct_size_2;
   struct_size = sizeof(struct_CommuPacket);
   struct_size_2 = sizeof(struct_CommuPacket_2);

   printf("struct_CommuPacket size=%d\n", struct_size);
   printf("struct_CommuPacket_2 size=%d\n\n", struct_size_2);

   int m_size_packetA, m_size_packetB, m_size_packetC;

   m_size_packetA = sizeof(struct_CommuPacket.packetA);
   printf("packetA size is %d\n", m_size_packetA);

   m_size_packetB = sizeof(struct_CommuPacket.packetB);
   printf("packetB size is %d\n", m_size_packetB);

   m_size_packetC = sizeof(struct_CommuPacket.packetC);
   printf("packetC size is %d\n", m_size_packetC);
}

//***************************************************//

輸出結果:



可以看到使用union的,佔了16byte,與struct的28byte比較,小了許多。

========================================================================






另外你可能會有疑問,為什麼packetA,packetB,packetC的size分別是8byte,4byte,12byte?

照理說不是應該是5byte,3byte,9byte嗎?

這就要講到struct的"自然對齊"(natural alignment)特性與"指定對齊":


一. natural alignment,是指按結構體的成員中size最大的成員對齊。

舉個例子:

struct natural_alignment
{
char a;
short b;
char c;
};

在上述struct中,size最大的是short,其長度為2byte,因而結構中的char成員a、c都以2為單位對齊,sizeof(natural_alignment)的結果等於6;



如果改為:

struct natural_alignment
{
char a;
int b;
char c;
};

其結果顯然為12。



二. 還有另外一種用法名為"指定對齊"

通常可以通過下面的方法來改變指定對齊的條件:

  • 使用虛擬指令#pragma pack (n),編譯器將按照n個位元組對齊;
  • 使用虛擬指令#pragma pack (),取消自定義位元組對齊方式

*如果#pragma pack (n)中指定的n大於結構體中最大成員的size,則沒有作用,結構體仍然按照size最大的成員進行對齊。


例如:

#pragma pack (n)
struct natural_alignment
{
char a;
int b;
char c;
};
#pragma pack ()
  當n為4、8、16時,其對齊方式都一樣,sizeof(naturalalign)的結果都等於12。而當n為2時,sizeof(naturalalign)的結果為8。

在VC++ 6.0編譯器中,我們可以指定其對齊條件,其操作方式為依次選擇projetct > setting > C/C++功能表,在struct member alignment中指定你要的對齊條件。

另外,通過__attribute((aligned (n)))也可以讓所作用的結構體成員對齊在n位元組邊界上,但是它較少被使用。