顯示具有 C Programing 標籤的文章。 顯示所有文章
顯示具有 C Programing 標籤的文章。 顯示所有文章

2014年2月2日 星期日

const修飾字的各種情況解釋


  • 首先記住,下面兩種情況是一樣的,都是修飾x是不能被改變的,沒問題。
const TYPE x;         
TYPE const x;  

判斷方式 ,
1. 先將其TYPE 與 變數分離,若星號(*) 右邊有變數則依賴此變數。
          2. 再將const畫修飾線至TYPE,搞定!!




  • 以下為各種常見的配合,請搭配上述方法判斷:
  1. char* const p;   /*指標本身為不可更改*
  2. const char *p; /*指標所指的內容為不可更改*/
  3. char const *p; /*指標所指的內容為不可更改*/
  4. const int *fun(); /*函數所返回的指標內容為不可更改*/
  5. int* const fun();  /*函數所返回的指標本身為不可更改*

2013年12月4日 星期三

我對Callback function 的理解

網路上很多的說法與範例 , 不過個人對他的理解如下:

A --> 我
B --> 飲料外送員

假設  A 想喝飲料 , 所以 A 叫了 B 來外送飲料 ,
但是B 做一做飲料後發現 , 他沒有A的地址 , 所以沒辦法送(資訊不夠),
因此B 又呼叫了 A  , 叫A回傳給B  "地址" ,

所以 , 以上所說的 "地址" , 就是一個 callback function ,  他本身是一個函數指標,
當系統需要他時 , 他會不定時的被系統呼叫 , 剛好對應到上面所說的 "地址"。

範例我就不貼了 , 網路上一堆,重要的是理解觀念。


Reference :




2013年11月24日 星期日

前置處理器相關


  • 前置處理器之編譯器所制定的各種進制如下:
            2進制 :'0b' 開頭       ex:   #define  COUNT  0b1010111
            8進制 :'0' 開頭         ex:   #define  COUNT  01017   
            16進制  : '0x' 開頭    ex:   #define  COUNT  0x2dbfa

2013年11月12日 星期二

製作Library檔之實作

首先要先記住一個重要的觀念:
   
1. 靜態庫(Static library):
     特徵: 副檔名為 libxxx.a , 整個函數庫的所有數據都會被整合編譯進去目標程式中
     優點: 執行目標程式時不需要附帶外部的函數庫支持 , 因為所有使用的函數都已經被編譯進去了.
     缺點:  如果靜態函數庫改變了,那麼程式整個必須重新編譯, 另外利用靜態函數庫編譯整個檔案會很大

2. 動態庫(Shared object):
     特徵:附檔名為 libxxx.so , 編譯時並不會將動態庫編進目標程式中,而是程式執行到相對應的函數才會動態申請調用動態庫
     優點:因為動態庫並沒有編進目標程式中, 故目標程式檔案較小 ,動態庫的改變不影響本程式 , 所以動態庫的升級較方便.
     缺點: 我認為的缺點 , 就是執行程式時老是檢查Library path 的路徑有沒有缺少那一個library等等, 系統預設放在/usr/lib.



然後複習一下編譯流程:





  • 靜態庫的使用:
             使用工具:       gcc  、 ar(用來建立靜態庫文件等等)
                (1). 建立hello1.c 、 hello2.c 分別如下:
                         
                       hello1.c      --->    void hello1() { printf("hello1\n");}
                       hello2.c      --->    void hello2() { printf("hello2\n");}
                       
              (2). generate object file:
                    # gcc -c hello1.c hello2.c

              (3). generate the library from object file:
                    # ar -crs libhello.a  hello1.o hello2.o    靜態庫已建立完成(libhello.a包了兩個function).
 
             (4). 連結靜態庫:
                    main.c  ---->   void main() { hello1(); hello2(); }
                       編譯  --->     # gcc -o  main  -L. -lhello  
           
                       執行  --->     #./main        //將會列印出兩個hello1 、 hello2

  • 動態庫的使用:
  程式引用上面的hello1.c 、 hello2.c
              (1). 產生動態庫:

                      gcc -fpic -shared -o libhello_func.so hello1.c hello2.c

              (2.)  連結動態庫:

                     gcc -o main  main.c    libhello_func.so     (注意, 加上libhello_func.so只是告知編譯認得main裡面的function).
      
                或  gcc -o main main.c -L. -lhello_func   
                    (-L. 指引當前目錄至library_path , -lhello_func會根據命名規則找到libhello_func).

              
              (3). 直接在程式中打開連結動態庫:
                    藉由使用到C語言標準庫 dlopen 、 dlsym 、 dlerror 、 dlclose 等等的開啟動態庫函數.
                    此方法可以不用在編譯器指引library ,  直接在程式中連結動態庫.

                     不過編譯時要注意 要加" -ldl " 引數來調用dlopen等等相關函數, 例如:
                      gcc -o main main.c –ldl 


              注意一點   , Linux預設的Library path 都在 /usr/lib 或是 /lib , 因此你可以選擇將library.so 放置這裡 ,

              亦或是宣告指定的library path 讓系統找到, 
              --> export LD_LIBRARY_PATH = {libhello_func.so的目錄}:$LD_LIBRARY_PATH


            Reference :

2013年10月20日 星期日

char * 與 char [] 的差異

當我們宣告:
char * str = "abc"; 跟 char str[] = "abc";
compiler 對這兩個宣告的處理是不一樣的.
當宣告char pointer指向一個"abc",compiler會在data segement(read-only)儲存這些字串("abc")然後把地址交給char *str(在stack上的一個地址空間);
而宣告char array,"abc"也會存在data segement裡,但為了提供修改char array,compiler會再把"abc"從data segement讀出來,存一份copy到stack segement(read-write);
就這個複製到stack segement的動作,宣告char array會需要更多的指令出完成,ex:
        char str[3] = "abc";
    5e60:       f645 72f8       movw    r2, #24568      ; 0x5ff8
    5e64:       f2c0 0200       movt    r2, #0
    5e68:       f107 030c       add.w   r3, r7, #12
    5e6c:       6812            ldr     r2, [r2, #0]
    5e6e:       4611            mov     r1, r2
    5e70:       8019            strh    r1, [r3, #0]
    5e72:       f103 0302       add.w   r3, r3, #2
    5e76:       ea4f 4212       mov.w   r2, r2, lsr #16
    5e7a:       701a            strb    r2, [r3, #0]
        char * str= "abc";
    5e60:       f645 73e0       movw    r3, #24544      ; 0x5fe0
    5e64:       f2c0 0300       movt    r3, #0
    5e68:       60fb            str     r3, [r7, #12]
若這些宣告是在一個不斷執行的while loop裡面,相較於pointer 宣告,array宣告額外的指令數就會被放大更多,影響效能.(micro-optimization issue)

以上轉貼至2013進階嵌入式實作課程之Will Fong 同學的引述.

2013年10月14日 星期一

關於Volatile


  • 初步了解 ,會宣告成Volatile ,
         主要目的是要告知compiler不要自作聰明的去作最佳化。
         因為那樣可能會影響程式執行的順序與錯誤,網路上很多資料,我挑幾篇我已經看過          的,將他整理如下:




       
         C/C++中的volatile使用時機? 
 http://freestyler.pixnet.net/blog/post/23872864-c-c%2B%2B%E4%B8%AD%E7%9A%84volatile%E4%BD%BF%E7%94%A8%E6%99%82%E6%A9%9F%3F





2013年10月13日 星期日

sizeof( struct ) 的其中奧妙

sizeof(結構體型別) 就等於 size(成員變數) 的總和嗎?請先見下面的例子 StructSamples.cpp,兩個以 struct 定義出的自訂型別的資料成員都相同,但是順序不一樣:


#include <stdio.h>

struct TYPEA
{   short int code;
    float value;
    char flags;
};

struct TYPEB     // TYPEA 與 TYPEB 只有成員順序不同
{   float value;
    short int code;
    char flags;
};

void main(void)
{
    printf("sizeof(TYPEA) = %d\n", sizeof(TYPEA));
    printf("sizeof(TYPEB) = %d\n", sizeof(TYPEB));
}

上例的執行結果是:
sizeof(TYPEA) = 12
sizeof(TYPRB) = 8
為甚麼會不一樣?好像都是 4 的倍數?其實這是因為編譯器對 struct 作了最佳化,以現行 32-bits 的 CPU (或 32-bits 的應用程式)而言,CPU 的行為一次抓取就是 4 bytes,以下圖最左邊的狀況,假如編譯器未對 TYPEA 做最佳化,那麼它的 value 欄位剛好跨在 CPU 兩次抓取範圍之間,所以程式碼中要取用 value 欄位的時候,CPU 必須抓取兩次,然後取第一次抓取的後半部和第二次抓取的前半部才能或得 value 的資料內容,相當傷害效率,所以通常編譯器在安排 TYPEA 的記憶體布局(layout)的時候,會採用下圖中的設計,盡量保證 struct 自訂型別中的任何欄位 都可以一次抓取(除非像 double 或 64-bit 整數本身就已經 8 bytes 長),方法就是盡量讓資料欄位的位址落在 4 bytes 的記憶體邊界(boundary)上,甚至把整個結構體的尺寸填空到 4 的倍數,令後續的資料也會落在 4 bytes 倍數的位址上。



當然上圖中的 TYPEA 造成了記憶體的浪費,所以,好的 struct 設計應該主動地把資料欄位都放置 4 的倍數上,如 TYPEB 型別,雖然欄位和 TYPEA 相同,但是變個順序,就兼顧了效率和記憶體空間的要求了。除此之外,請記得struct 的尺寸不一定等於成員的尺寸和,所以計算配置記憶體所需的記憶體空間請務必使用 sizeof( )。


STDIN、STDOUT、STDERR 說明

  • 先把 STDIN、STDOUT、STDERR 的基本東西整理一下~
    • 0: stdin (Standard input) 標準輸入串流 (鍵盤輸入)
    • 1: stdout (Standard output) 標準輸出串流 (輸出於 Cli 視窗)
    • 2: stderr (Standard error) 標準錯誤輸出串流 (輸出於 Cli 視窗)

簡單易懂的圖片可見(取自: Standard streams - Wikipedia, the free encyclopedia):


要特別注意 stdout與stderr一般情況下都會輸出到螢幕,
但是到了存取到硬碟,也就是檔案系統時,stdout主要輸出於檔案,stderr主要輸出於螢幕。






2013年10月11日 星期五

2013年10月7日 星期一

.data section和.bss section有何差別

如下解釋:
  • global variable has initialized value ( nozeroe value)to store in  DATA section.
  • global variable has uninitialized value( zero value ) to store in BSS section

  • .bss:  用來存放程序中未初始化的全局變數的一塊內存區域。
  • .data:  用來存放程序中已初始化且不為0的全局變數的一塊內存區域。


    int xx = 100; 是放在 .data
    int c; 是放在 .bss