當你在宣告一個變數時是這樣的:
int ImVar;//<-----------------------1
當你在宣告一個函式時卻是這樣:
int ImFun(...);//---------------------2
變數宣告時名稱在最後面,而函式名稱卻在中間,
你會不會覺得這很奇怪?
本來用一個小括號括起來的參數定義就是函式名稱的附屬品
你可以當它是函式名稱的一部份。沒有了它函式名稱就不完整了。
(注意在C++中不同參數的同名函式在編譯器的內部函式名稱是不同的)
typedef int INT;//<------------------3
typedef int *PINT;//<--------------4
typedef int (*PINT);//<--------------5
3式是定義一個int的型態,名為INT
4式是定義一個int的指標型態,名為PINT
5式是定義一個指向int的指標型態,名為PINT
4式和5式的結果是等效的。
現在我們嘗試為函式定義型態:
typedef int IntFun(...);//<------------6
先注意到有關2式的說明,就不應再對為何函式名稱後還有(...)
6式定義一個型態(或返回)int函式,名稱為IntFun。
我們知道,函式名本身俱有隱性指標的性質,所以IntFun和 *IntFun是
等效的。
那麼可以明白的定義IntFun為指標嗎,應該可以的!直觀的感覺是套入
4式:
typedef int * IntFun(...);//<------------7
問題出來了,任何一個編譯器都會把7式解讀為:
定義一個型態(或返回)int *函式,名稱為IntFun。
這不是我們要的,那要如何指定指標('*')給IntFun而不是int呢?
答案就是括號,也就是套入5式而不是4式:
typedef int (*IntFun)(...);//<------------8
這就是原提問要的解答了,唯要注意的是
對型態的定義來說 4式和5式是等效的,
但對函式的定義來說6式和8式才是等效的;
那麼使用6式或8式效好?
一般都使用8弍,它有較好的可讀性,隱式指標總是令人較為困感的。
而且也不敢保證所有的編譯器都可以接受6式的敘述。
Ref: http://www.programmer-club.com.tw/ShowSameTitleN/c/25059.html
2014年6月30日 星期一
C 指標 函式指標 函式指標陣列
下面是一些範例(在64位元的機器中,指標為 8 bytes):
char (*p)[20];
sizeof(p) = 8
sizeof(p[0]) = 20
sizeof(p[1]) = 20
sizeof(*p) = 20
p = malloc(sizeof(*p));
strcpy(*p, "Kenneth Kuan");
*p = Kenneth Kuan
char *p1;
p1 = (char*) malloc(sizeof(char)*20);
sizeof(p1) = 8
sizeof(*p1) = 1
sizeof(p1[0]) = 1
sizeof(p1[1]) = 1
char p2[20];
sizeof(p2) = 20
sizeof(*p2) = 1
char *p3[20];
sizeof(p3) = 160
sizeof(*p3) = 8
sizeof(p3[0]) = 8
每個函式名稱都是指向該函式的指標,當你建立名為 go_to_warp_speed(int speed)的函式時,你同時也建立了稱作 go_to_wrap_speed的指標變數。
建立函式指標:
int (*warp_fn)(int);
warp_fn = go_to_warp_speed;
warp_fn(4);
warp_fn(4); 跟呼叫 go_to_warp_speed(4)一樣。
在建立函式指標時,記得要指定回傳值型別和接受的參數。
enum response_type {DUMP, SECOND_CHANCE, MARRIAGE);
typedef struct {
char *name;
enum response_type type;
}
建立函式指標陣列:
void (*replies[])(response) = {dump, second_chance, marriage};
replies[DUMP] == dump
replies[DUMP]等同於dump函式的名稱
char (*p)[20];
sizeof(p) = 8
sizeof(p[0]) = 20
sizeof(p[1]) = 20
sizeof(*p) = 20
p = malloc(sizeof(*p));
strcpy(*p, "Kenneth Kuan");
*p = Kenneth Kuan
char *p1;
p1 = (char*) malloc(sizeof(char)*20);
sizeof(p1) = 8
sizeof(*p1) = 1
sizeof(p1[0]) = 1
sizeof(p1[1]) = 1
char p2[20];
sizeof(p2) = 20
sizeof(*p2) = 1
char *p3[20];
sizeof(p3) = 160
sizeof(*p3) = 8
sizeof(p3[0]) = 8
每個函式名稱都是指向該函式的指標,當你建立名為 go_to_warp_speed(int speed)的函式時,你同時也建立了稱作 go_to_wrap_speed的指標變數。
建立函式指標:
int (*warp_fn)(int);
warp_fn = go_to_warp_speed;
warp_fn(4);
warp_fn(4); 跟呼叫 go_to_warp_speed(4)一樣。
在建立函式指標時,記得要指定回傳值型別和接受的參數。
enum response_type {DUMP, SECOND_CHANCE, MARRIAGE);
typedef struct {
char *name;
enum response_type type;
}
建立函式指標陣列:
void (*replies[])(response) = {dump, second_chance, marriage};
replies[DUMP] == dump
replies[DUMP]等同於dump函式的名稱
2014年6月25日 星期三
Linux Driver 開發概述
Device Driver 簡稱 Driver
裝置驅動程式用來將硬體本身的功能告訴作業系統,可視驅動程式爲硬體的靈魂,同時也可稱驅動程式為硬體和系統之間的橋樑。
裝置驅動程式可分為下列三類:
1. 字元裝置
一個位元組(Byte)一個位元組讀取資料的裝置,一般會在驅動層實現 open(), close(), read(), write() and ioctl()等函數。
2. 區塊裝置
一般是像磁碟一樣的裝置,進行讀寫時,每次只能傳輸一個或多個區塊,但是Linux可以讓應用程式像存取字元裝置一樣的存取區塊裝置。
3. 網路裝置
主要是面對資料封包的接收和發送而設計的,網路裝置在Linux中是一種很特殊的裝置,不像字元裝置與區塊裝置的API函數,而是實現了一種通訊端界面,任何網路資料傳輸都可以透過通訊端來完成。
CPU有兩種指令:
1. 特權指令:Kernel mode可使用
2. 普通指令:Kernel mode and User mode可使用
模組是可以在執行時加入核心的程式,Linux kernel支援很多種模組,驅動程式就是其中最重要的一種,甚至檔案系統也可以寫成一個模組。利用insmod指令將模組加入正在執行的核心,也可用rmmod指令將一個未使用的模組從核心中刪除。
載入模組有兩種類別:
1. 靜態載入:模組在核心啟動時載入
2. 動態載入:在核心已經執行時載入
裝置驅動程式用來將硬體本身的功能告訴作業系統,可視驅動程式爲硬體的靈魂,同時也可稱驅動程式為硬體和系統之間的橋樑。
裝置驅動程式可分為下列三類:
1. 字元裝置
一個位元組(Byte)一個位元組讀取資料的裝置,一般會在驅動層實現 open(), close(), read(), write() and ioctl()等函數。
2. 區塊裝置
一般是像磁碟一樣的裝置,進行讀寫時,每次只能傳輸一個或多個區塊,但是Linux可以讓應用程式像存取字元裝置一樣的存取區塊裝置。
3. 網路裝置
主要是面對資料封包的接收和發送而設計的,網路裝置在Linux中是一種很特殊的裝置,不像字元裝置與區塊裝置的API函數,而是實現了一種通訊端界面,任何網路資料傳輸都可以透過通訊端來完成。
CPU有兩種指令:
1. 特權指令:Kernel mode可使用
2. 普通指令:Kernel mode and User mode可使用
模組是可以在執行時加入核心的程式,Linux kernel支援很多種模組,驅動程式就是其中最重要的一種,甚至檔案系統也可以寫成一個模組。利用insmod指令將模組加入正在執行的核心,也可用rmmod指令將一個未使用的模組從核心中刪除。
載入模組有兩種類別:
1. 靜態載入:模組在核心啟動時載入
2. 動態載入:在核心已經執行時載入
Note for C
printf
%p 被用來格式化位址 (16進位, e.g., 0x3E8FA0)
====================================================================
指標有時候會被稱為 reference(參考), *運算子可用來 dereference(解參考)指標.
struct, union, enum 這三者要一起看
typedef struct cell_phone{
...
...
...
} phone;
...
...
...
} phone;
依照上述例子 phone 是別名
phone *myPhone;
//(*myPhone).xxx == myPhone->xxx
//上述兩式相等
struct結構中可以包含指向另一個struct的指標,但是struct本身不可以再包含完整的遞迴struct。因為C語言必須知道struct確切占據多少記憶體空間。
union的用法與struct相同,但是union只會針對它所定義的欄位之一配置記憶體空間,電腦將提供該union足以存放其最大欄位的空間。
typedef union {
short count;
float weight;
float volume;
} quantity;
quantity q = {.weight=1.5};
上述初始化等同於,下面這種寫法
quantity q;
q.weight = 1.5;
enum讓你可以列舉出一序列的符號,像下面這樣:
enum color {RED, GREEN, PUCE};
bitfield可以讓我們儲存自訂數目的位元,並且應該被宣告為unsigned int。
例如:
typedef struct {
unsigned int first_visit:1; //表示該欄位只占用一個位元(1bit)
unsigned int come_again:1;
unsigned int figner_lost:4;
unsigned int shark_attack:1;
unsigned int days_a_week:3; //表示該欄位占用三個位元(3bit)
} survey;
配置與釋放必須一對一
從heap配置動態記憶體 void *p = malloc(sizeof(int)*4);
釋放記憶體 free(p);
如何配置動態二維陣列?
如欲配置一[m][n]的二維陣列
有下列幾種作法
(ㄧ)
int **Array, *pData;
int m,n,i;
Array = (int**)malloc(m*sizeof(int *));
pData = (int*)malloc(m*n*sizeof(int));
for(i = 0; i < m; i++, pData += n)
Array[i] = pData;
只需做兩次malloc,free只要free Array和Array[0]就可以了
(二)
int i;
int **Array;
Array = (int **)malloc(m*sizeof(void *));
for (i = 0; i < m; i++)
Array = (int *)malloc(n*sizeof(int *));
這樣子的配置方式要做很多次的malloc,,並容易造成記憶體碎片化(memory fragment)
(三)
int i;
int **Array, *pData;
Array = (int **)malloc(m*sizeof(int *)+m*n*sizeof(int));
for (i = 0, pData = (int *)(Array+m); i < m; i++, pData += n)
Array[i]=pData;
這樣是最簡便的寫法 只要mallocㄧ次完成,free只要free Array即可
Ref: http://chiakie.pixnet.net/blog/post/3143518-%5Bc%5D-%E5%A6%82%E4%BD%95%E5%8B%95%E6%85%8B%E9%85%8D%E7%BD%AE%E4%BA%8C%E7%B6%AD%E9%99%A3%E5%88%97
%p 被用來格式化位址 (16進位, e.g., 0x3E8FA0)
====================================================================
指標有時候會被稱為 reference(參考), *運算子可用來 dereference(解參考)指標.
struct, union, enum 這三者要一起看
typedef struct cell_phone{
...
...
...
} phone;
不需要像上面這種寫法,可以略過struct的名稱,直接定義別名(alias),如下面的寫法。
typedef struct {...
...
...
} phone;
依照上述例子 phone 是別名
phone *myPhone;
//(*myPhone).xxx == myPhone->xxx
//上述兩式相等
struct結構中可以包含指向另一個struct的指標,但是struct本身不可以再包含完整的遞迴struct。因為C語言必須知道struct確切占據多少記憶體空間。
union的用法與struct相同,但是union只會針對它所定義的欄位之一配置記憶體空間,電腦將提供該union足以存放其最大欄位的空間。
typedef union {
short count;
float weight;
float volume;
} quantity;
quantity q = {.weight=1.5};
上述初始化等同於,下面這種寫法
quantity q;
q.weight = 1.5;
enum讓你可以列舉出一序列的符號,像下面這樣:
enum color {RED, GREEN, PUCE};
bitfield可以讓我們儲存自訂數目的位元,並且應該被宣告為unsigned int。
例如:
typedef struct {
unsigned int first_visit:1; //表示該欄位只占用一個位元(1bit)
unsigned int come_again:1;
unsigned int figner_lost:4;
unsigned int shark_attack:1;
unsigned int days_a_week:3; //表示該欄位占用三個位元(3bit)
} survey;
配置與釋放必須一對一
從heap配置動態記憶體 void *p = malloc(sizeof(int)*4);
釋放記憶體 free(p);
如何配置動態二維陣列?
如欲配置一[m][n]的二維陣列
有下列幾種作法
(ㄧ)
int **Array, *pData;
int m,n,i;
Array = (int**)malloc(m*sizeof(int *));
pData = (int*)malloc(m*n*sizeof(int));
for(i = 0; i < m; i++, pData += n)
Array[i] = pData;
只需做兩次malloc,free只要free Array和Array[0]就可以了
(二)
int i;
int **Array;
Array = (int **)malloc(m*sizeof(void *));
for (i = 0; i < m; i++)
Array = (int *)malloc(n*sizeof(int *));
這樣子的配置方式要做很多次的malloc,,並容易造成記憶體碎片化(memory fragment)
(三)
int i;
int **Array, *pData;
Array = (int **)malloc(m*sizeof(int *)+m*n*sizeof(int));
for (i = 0, pData = (int *)(Array+m); i < m; i++, pData += n)
Array[i]=pData;
這樣是最簡便的寫法 只要mallocㄧ次完成,free只要free Array即可
Ref: http://chiakie.pixnet.net/blog/post/3143518-%5Bc%5D-%E5%A6%82%E4%BD%95%E5%8B%95%E6%85%8B%E9%85%8D%E7%BD%AE%E4%BA%8C%E7%B6%AD%E9%99%A3%E5%88%97
GCC make 編譯 C 程式
gcc -c *.c
-c 告訴編譯器你想要為每個原始檔建造目的檔,但是沒有將之連結產生完整的可執行程式
gcc *.o -o a.out
將目的檔連結在一起,並且產生完整的可執行程式(a.out)
make所編譯的每個檔案被稱作 target
針對每個target,make必須被告知兩件事情:
1. dependencies (依存項目): target 將從哪些檔案產生
2. recipe (執行指令): 產生檔案所需要執行的一組指令
關於 target, dependencies and recipe的所有細節都必須被儲存在一個稱為 makefile or Makefile的檔案中。
launch.o: launch.c launch.h thruster.h
gcc -c launch.c
綠色的部分為target
藍色的部分為dependencies
紅色的部分為recipe
紫色的部分為TAB,所有的recipe必須以TAB字元開頭
進階一點的話可以參考更自動化的工具 autoconf
Ref: https://www.gnu.org/software/autoconf/
Example: http://www.ibm.com/developerworks/cn/linux/l-makefile/
-c 告訴編譯器你想要為每個原始檔建造目的檔,但是沒有將之連結產生完整的可執行程式
gcc *.o -o a.out
將目的檔連結在一起,並且產生完整的可執行程式(a.out)
make所編譯的每個檔案被稱作 target
針對每個target,make必須被告知兩件事情:
1. dependencies (依存項目): target 將從哪些檔案產生
2. recipe (執行指令): 產生檔案所需要執行的一組指令
關於 target, dependencies and recipe的所有細節都必須被儲存在一個稱為 makefile or Makefile的檔案中。
launch.o: launch.c launch.h thruster.h
gcc -c launch.c
綠色的部分為target
藍色的部分為dependencies
紅色的部分為recipe
紫色的部分為TAB,所有的recipe必須以TAB字元開頭
進階一點的話可以參考更自動化的工具 autoconf
Ref: https://www.gnu.org/software/autoconf/
Example: http://www.ibm.com/developerworks/cn/linux/l-makefile/
2014年6月24日 星期二
Python 標準輸入
#/usr/bin/env python
import sys
counter = 1
while True:
line = sys.stdin.readline()
if not line:
break
print "%s:%s" % (counter,line)
counter += 1
import sys
counter = 1
while True:
line = sys.stdin.readline()
if not line:
break
print "%s:%s" % (counter,line)
counter += 1
2014年6月18日 星期三
unix 系統基本指令
[控制鍵]: 首先要了解在下命令時一些控制鍵的用法 : Ctrl-U 刪除整行命令行 Ctrl-W 刪除命令行的最後一個字(Word) Ctrl-C 中斷程式或 shell script Ctrl-Z 暫停程式或 shell script Ctrl-S 暫停往下顯示 Ctrl-Q 繼續往下顯示 Ctrl-O 不要程式的輸出, 但不中斷程式 Ctrl-D 終止檔案輸入(EOF), 或 Logout 與檔案目錄有關的特殊字元 . 現在目錄 (一個句點) .. 上一層目錄 (兩個句點) ~ User 之主目錄 (老鼠尾巴) ? 檔名之單一字元 (問號) * 檔名之所有字元 (星號) 以下摘錄 unix 較常用之命令, 其中也包含了部份 csh 之 Built-in commnad : ls 顯示目錄內容, 同 DOS 之 DIR ls -al 詳細顯示此目錄內的所有檔案 (包含以 . 開頭之隱藏檔) ls *.c 顯示此目錄內的所有結尾為 .c 的檔案 (unix 無所謂附檔名) cd切換目錄 cd 切換目錄至您的主目錄 cd .. 切換目錄至上一層目錄 cd /usr/bin 切換目錄至 /usr/bin pwd 顯示現在目錄的路徑
2014年6月17日 星期二
處理 command-line options using getopt()
$./order_pizza -d now -t Anchovies Pineapple
Output:
Thick crust.
To be delivered now.
Ingredients:
Anchovies
Pineapple
2014年6月13日 星期五
記憶體與指標
int doses[] = {1, 3, 2, 1000}
int *t = doses;
//doses[3] == *(doses+3) == *(3+doses) == 3[doses]
//sizeof(doses) == 16
//sizeof(t) == 4(32bits) or 8(64bits)
//傳遞陣列變數給指標會發生退化(decay)
//不可做指標乘法但可以做指標加法
//使用fgets來取代scanf,因為fgets的function signature中,長度是必要項
//避免這樣的寫法 char *s = "Some string";
//改用這樣的寫法 const char *s = "Some string";
//因為字串 "Some string"本身存在於唯讀記憶體區段,好的寫法可以避免產生這種錯誤
//s[0] = 'S';
//declaration 宣告是一段程式碼,聲明某個東西(變數, 函式等)存在
//definition 一段程式碼
$(./bermuda | ./geo2json) < spooky.csv > output.json
//此命令利用管線(|)將行程(bermuda)的標準輸出連結到另一行程(geo2json)的標準輸入,並利用括弧將兩支獨立的程式當成單一程式,標準輸入的資料(spooky.csv)透過redirect standard input operator(<)來重導向至行程(bermuda),並且透過redirect standard output operator(>)來將行程(geo2json)之標準輸出導向檔案(output.json)。
FILE *in = fopen("i_dont_exist.txt", "r");
//Not Safety
FILE *in;
if (!(in = fopen("i_dont_exist.txt", "r"))){
fprintf(stderr, "Can't open the file.\n");
return 1;
}
//Safety
int *t = doses;
//doses[3] == *(doses+3) == *(3+doses) == 3[doses]
//sizeof(doses) == 16
//sizeof(t) == 4(32bits) or 8(64bits)
//傳遞陣列變數給指標會發生退化(decay)
//不可做指標乘法但可以做指標加法
//使用fgets來取代scanf,因為fgets的function signature中,長度是必要項
//避免這樣的寫法 char *s = "Some string";
//改用這樣的寫法 const char *s = "Some string";
//因為字串 "Some string"本身存在於唯讀記憶體區段,好的寫法可以避免產生這種錯誤
//s[0] = 'S';
//declaration 宣告是一段程式碼,聲明某個東西(變數, 函式等)存在
//definition 一段程式碼
$(./bermuda | ./geo2json) < spooky.csv > output.json
//此命令利用管線(|)將行程(bermuda)的標準輸出連結到另一行程(geo2json)的標準輸入,並利用括弧將兩支獨立的程式當成單一程式,標準輸入的資料(spooky.csv)透過redirect standard input operator(<)來重導向至行程(bermuda),並且透過redirect standard output operator(>)來將行程(geo2json)之標準輸出導向檔案(output.json)。
FILE *in = fopen("i_dont_exist.txt", "r");
//Not Safety
FILE *in;
if (!(in = fopen("i_dont_exist.txt", "r"))){
fprintf(stderr, "Can't open the file.\n");
return 1;
}
//Safety
訂閱:
文章 (Atom)