作者 : 萬境絕塵
轉載請註明出處 : http://blog.csdn.net/shulianghan/article/details/21551397 | http://www.hanshuliang.com/?post=29
.
1. C語言命令行參數詳解
命令行參數 : 有兩個參數 int argc 和 char **argv;
-- argc : 標示輸入的參數個數, 注意命令本身也是參數;
-- argv : 指向 字符串數組的指針, 每個字符串是一個參數;
-- 約定 : argv[0] 是 程序名稱, argc 的最小值是1, 如果argc 是1, 那麼說明 命令後面沒有參數;
(1) 模仿 echo 程序 示例
echo程序示例 :
- octopus@octopus-Vostro-270s:~/code/c/pointer$ echo csdn
- csdn
- octopus@octopus-Vostro-270s:~/code/c/pointer$ echo Hello World
- Hello World
octopus@octopus-Vostro-270s:~/code/c/pointer$ echo csdn
csdn
octopus@octopus-Vostro-270s:~/code/c/pointer$ echo Hello World
Hello World
-- 分析命令行 : echo Hello World 將 Hello World 輸出到命令行中, 該命令 argc 值是3, argv[0] 是 echo, argv[1] 是 Hello, argv[2] 是 World, 注意 argv[3] 是 空指針 0;
使用數組下標方式訪問參數模仿echo示例程序 :
- /*************************************************************************
- > File Name: echo.c
- > Author: octopus
- > Mail: octopus_work.163.com
- > Created Time: 2014年03月19日 星期三 19時56分36秒
- ************************************************************************/
- #include<stdio.h>
- int main(int argc, char **argv)
- {
- int i;
- for(i = 1; i < argc; i ++)
- {
- //將參數輸出, 每次注意輸出一個空格, 如果是最後一個那就不用輸出空格了
- printf("%s%s", argv[i], (i < argc - 1) ? " " : "");
- }
- printf("\n");
- return 0;
- }
/*************************************************************************
> File Name: echo.c
> Author: octopus
> Mail: octopus_work.163.com
> Created Time: 2014年03月19日 星期三 19時56分36秒
************************************************************************/
#include<stdio.h>
int main(int argc, char **argv)
{
int i;
for(i = 1; i < argc; i ++)
{
//將參數輸出, 每次注意輸出一個空格, 如果是最後一個那就不用輸出空格了
printf("%s%s", argv[i], (i < argc - 1) ? " " : "");
}
printf("\n");
return 0;
}
執行效果 :
- octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World
- Hello World
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World
Hello World
使用指針訪問參數模仿echo程序 :
- /*************************************************************************
- > File Name: echo_pointer.c
- > Author: octopus
- > Mail: octopus_work.163.com
- > Created Time: 2014年03月19日 星期三 20時08分07秒
- ************************************************************************/
- #include<stdio.h>
- /*
- * echo Hello World
- * 循環條件 : --argc, 如果參數只有一個 echo 那麼什麼都不用打印
- * 打印參數個數 : 如果參數有 3 個, 那就循環 2 次, 打印兩個參數
- * 打印參數 : 從 第 2 個參數開始打印
- * 打印空格 : 如果argc > 1, 說明下一輪還要繼續打印, 此時打印空格
- */
- int main(int argc, char **argv)
- {
- while(--argc > 0)
- printf("%s%s", *++argv, (argc > 1) ? " " : "");
- printf("\n");
- return 0;
- }
/*************************************************************************
> File Name: echo_pointer.c
> Author: octopus
> Mail: octopus_work.163.com
> Created Time: 2014年03月19日 星期三 20時08分07秒
************************************************************************/
#include<stdio.h>
/*
* echo Hello World
* 循環條件 : --argc, 如果參數只有一個 echo 那麼什麼都不用打印
* 打印參數個數 : 如果參數有 3 個, 那就循環 2 次, 打印兩個參數
* 打印參數 : 從 第 2 個參數開始打印
* 打印空格 : 如果argc > 1, 說明下一輪還要繼續打印, 此時打印空格
*/
int main(int argc, char **argv)
{
while(--argc > 0)
printf("%s%s", *++argv, (argc > 1) ? " " : "");
printf("\n");
return 0;
}
執行結果 :
- octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc echo_pointer.c -o echo
- octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World
- Hello World
octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc echo_pointer.c -o echo
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World
Hello World
(2) 模仿 簡單grep 程序
程序介紹 : 模仿 grep 過濾, 過濾數據來自標準輸入流, grep 命令匹配第一個參數, 凡是輸入的字符串 包含 第一個參數字符串, 就輸出這個字符串, 相當於將字符串輸出了2遍;
代碼 :
- /*************************************************************************
- > File Name: grep.c
- > Author: octopus
- > Mail: octopus_work.163.com
- > Created Time: 2014年03月19日 星期三 20時45分47秒
- ************************************************************************/
- #include<stdio.h>
- #include<string.h>
- #define MAXLEN 50
- //先聲明函數, 才能在main函數中使用, 否則函數要在main函數之前定義纔可以使用
- int get_line(char *line, int max);
- int main(int argc, char **argv)
- {
- char line[MAXLEN];
- int found = 0;
- if(argc == 1)
- printf("wrong parameters ! \n");
- else
- /*
- * 當獲取的字符個數大於1的時候
- * 比較字符串 與 參數1
- * 如果返回的不爲NULL, 那麼說明查找到了字串
- * 將字串打印出來
- */
- while(get_line(line, MAXLEN) > 0)
- {
- if(strstr(line, argv[1]) != NULL)
- {
- printf("%s \n", line);
- found ++;
- }
- }
- return 0;
- }
- /*
- * 從標準輸入流中獲取字符串, 將字符串存儲到 char *line 指針指向的數組中
- * 注意字符串最大爲50個, 字符最多有49個, 剩下的最後一位存放 '\0'
- * 從標準輸入流中讀取字符, 放到數組中
- * 停止讀取字符條件 : 個數到達 48個, 讀取到了 回車 '\n' 或者 EOF (ctrl + D)
- */
- int get_line(char *line, int max)
- {
- int i, c;
- for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i++)
- line[i] = c;
- line[i] = '\0';
- return i;
- }
/*************************************************************************
> File Name: grep.c
> Author: octopus
> Mail: octopus_work.163.com
> Created Time: 2014年03月19日 星期三 20時45分47秒
************************************************************************/
#include<stdio.h>
#include<string.h>
#define MAXLEN 50
//先聲明函數, 才能在main函數中使用, 否則函數要在main函數之前定義纔可以使用
int get_line(char *line, int max);
int main(int argc, char **argv)
{
char line[MAXLEN];
int found = 0;
if(argc == 1)
printf("wrong parameters ! \n");
else
/*
* 當獲取的字符個數大於1的時候
* 比較字符串 與 參數1
* 如果返回的不爲NULL, 那麼說明查找到了字串
* 將字串打印出來
*/
while(get_line(line, MAXLEN) > 0)
{
if(strstr(line, argv[1]) != NULL)
{
printf("%s \n", line);
found ++;
}
}
return 0;
}
/*
* 從標準輸入流中獲取字符串, 將字符串存儲到 char *line 指針指向的數組中
* 注意字符串最大爲50個, 字符最多有49個, 剩下的最後一位存放 '\0'
* 從標準輸入流中讀取字符, 放到數組中
* 停止讀取字符條件 : 個數到達 48個, 讀取到了 回車 '\n' 或者 EOF (ctrl + D)
*/
int get_line(char *line, int max)
{
int i, c;
for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i++)
line[i] = c;
line[i] = '\0';
return i;
}
執行結果 : 參數是 abc, 如果輸入的字符串包含 abc, 那麼就將字符串再輸出一遍;
- octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc grep.c -o grep
- octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep abc
- qwe
- asd
- zxc
- qabc
- qabc
- wabcc
- wabcc
- 12
octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc grep.c -o grep
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep abc
qwe
asd
zxc
qabc
qabc
wabcc
wabcc
12
要點解析 :
-- int get_line(char *line, int max)函數 : 從輸入流中獲取輸入, 當獲取到'\n'或者EOF的時候, 就會返回該字符串指針, 注意 函數如果命名爲 getline()就會報錯, 與庫函數同名了;
-- char *strstr(const char *haystack, const char *needle)函數 : 查詢 haystack字符串中 是否包含 needle 字符串, 如果包含, 就將查詢到的子字符串的指針返回;
(3) 模仿帶可選參數的grep程序
需求 : 給程序加上可選參數;
-- UNIX程序約定 : 命令中 負號開頭的參數是可選的參數, 例如 ls -la, ls 是將目錄中的文件列出, 後面的 -la 可有可無;
-- 模擬grep程序可選參數 : -x 代表打印不匹配的文本行, -n 打印行號, 可以使用 grep -x -n 格式, 也可以使用 grep -nx 格式;
要點解析 :
-- option_analysis參數 : 因爲在後面需要用到 輸入的過濾參數, 即argv 的非 可選參數, 在遍歷可選參數的時候, 會對argv進行一系列的自增操作, 如果我們傳入的是argv二級指針, 那麼在函數中進行的自增操作不會改變argv值, 這裏我們需要改變argv的值, 因此需要傳入 argv二級指針的地址, 三級指針;
-- 區分 (*++argv)[0] 和 *++argv[0] : []的優先級最高, 下面的框圖分析兩種情況 , 先進行 (*++argv)[0], 然後進行 *++argv[0]運算;
(*++argv)[0] 與 *++argv[0]圖解 :
-- argv參數 :
-- 執行(*++argv)[0]表達式 :
-- 執行*++argv[0]表達式 :
代碼 :
- /*************************************************************************
- > File Name: grep_option.c
- > Author: octopus
- > Mail: octopus_work.163.com
- > Created Time: 2014年03月21日 星期五 09時30分07秒
- ************************************************************************/
- #include<stdio.h>
- #include<string.h>
- #define MAXLEN 15
- char line[MAXLEN];
- int c, lineno = 0, except = 0, number = 0, found = 0;
- int get_line(char *line, int max);
- void option_analysis(int argc, char ***argv);
- void out_put(char **argv);
- int main(int argc, char **argv)
- {
- option_analysis(argc, &argv);
- out_put(argv);
- return 0;
- }
- /*
- * 從標準輸入流中獲取字符串, 最長能獲取max個
- * 個數計算 :
- * 字符串最長是 max 個
- * 注意裏面包含了 '\0'
- * 實際字符個數只能是 max - 1 個
- * 實際的最大下標是 max - 1
- * 字符串終止 :
- * 輸入 '\n' (回車)
- * 輸入 EOF (CTRL + D)
- * 字符個數達到 max - 1 個
- * 注意 : 最後一個元素賦值爲 '\0'
- */
- int get_line(char *line, int max)
- {
- int i, c;
- for (i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i++)
- {
- line[i] = c;
- }
- line[i] = '\0';
- return i;
- }
- /*
- * 分析可選參數情況, 根據接收到的可選參數情況, 設置標識位
- *
- * 後來對方法參數進行了小修改, 這裏需要改變argv指針指向,
- * 因此需要將該指針的地址傳給函數
- *
- * 區分 (*++argv)[0] 和 *++argv[0]
- * (*++argv)[0] :
- * 執行順序 : (*(++argv))[0], * 和 ++ 同時存在 執行順序自右向左
- * 執行效果 :
- * ++argv 將指針指向了 下標爲1 的字符串首地址
- * *++argv 指的是第一個字符串
- * (*++argv)[0] 獲取的是第一個字符串的第0個字符元素, 這裏用來判斷是不是'-'
- * *++argv[0] : 此時argv指向第二個字符串首地址
- * 執行順序 : *(++(argv[0]))
- * 執行效果 :
- * 取出第0個字符串的第1個字符, 該串的第0個字符是'-'
- * argv[0]得到第0個字符的指針
- * ++argv[0] 是該字符串的第二個元素的地址
- * *++argv[0] 是該字符串第二個元素
- */
- void option_analysis(int argc, char ***argvp)
- {
- /*
- * 根據--argc > 0 判斷輸入的參數, 如果 --argc 大於0, 那麼說明後面還有參數
- * 循環所有的參數, 將所有的 -可選參數遍歷出來
- *
- * 每次循環 argv 指針就自增, 指向下一個參數字符串
- * 如果字符串的第0個字符是 '-', 那麼該參數字符串是可選參數
- */
- while (--argc > 0 && (*++(*argvp))[0] == '-')
- {
- /*
- * 先獲取這個可選字符串指針, 然後在一次遍歷這個指針
- * 根據遍歷的結果設置對應的標識位
- */
- while (c = *++(*argvp)[0])
- {
- switch(c)
- {
- case 'x':
- except = 1;
- break;
- case 'n':
- number = 1;
- break;
- default:
- printf("option illegal \n");
- argc = 0;
- found = -1;
- break;
- }
- }
- }
- }
- /*
- * 在上面的option_analysis函數中傳入的argv指針的地址, 因此上面對argv的自增操作改變了指針
- * 這裏的*argv 就可以取出 argv 第0個元素字符串的指針
- *
- * 如果輸入的字符串能匹配參數
- * 沒有輸入x的情況
- * 輸入了n 輸出帶行號, 不匹配的字符串
- * 沒有輸入n 輸出不帶行號, 不匹配的字符串
- * 如果輸入了x參數
- * 輸入了n 輸出帶行號的, 匹配的字符串
- * 沒有輸入n , 輸出不帶行號的, 匹配的字符串
- */
- void out_put(char **argv)
- {
- while (get_line(line, MAXLEN) > 0)
- {
- lineno++;
- if ((strstr (line, *argv) != NULL) != except)
- {
- if(number)
- printf("%d : ", lineno);
- printf("%s \n", line);
- }
- }
- }
/*************************************************************************
> File Name: grep_option.c
> Author: octopus
> Mail: octopus_work.163.com
> Created Time: 2014年03月21日 星期五 09時30分07秒
************************************************************************/
#include<stdio.h>
#include<string.h>
#define MAXLEN 15
char line[MAXLEN];
int c, lineno = 0, except = 0, number = 0, found = 0;
int get_line(char *line, int max);
void option_analysis(int argc, char ***argv);
void out_put(char **argv);
int main(int argc, char **argv)
{
option_analysis(argc, &argv);
out_put(argv);
return 0;
}
/*
* 從標準輸入流中獲取字符串, 最長能獲取max個
* 個數計算 :
* 字符串最長是 max 個
* 注意裏面包含了 '\0'
* 實際字符個數只能是 max - 1 個
* 實際的最大下標是 max - 1
* 字符串終止 :
* 輸入 '\n' (回車)
* 輸入 EOF (CTRL + D)
* 字符個數達到 max - 1 個
* 注意 : 最後一個元素賦值爲 '\0'
*/
int get_line(char *line, int max)
{
int i, c;
for (i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i++)
{
line[i] = c;
}
line[i] = '\0';
return i;
}
/*
* 分析可選參數情況, 根據接收到的可選參數情況, 設置標識位
*
* 後來對方法參數進行了小修改, 這裏需要改變argv指針指向,
* 因此需要將該指針的地址傳給函數
*
* 區分 (*++argv)[0] 和 *++argv[0]
* (*++argv)[0] :
* 執行順序 : (*(++argv))[0], * 和 ++ 同時存在 執行順序自右向左
* 執行效果 :
* ++argv 將指針指向了 下標爲1 的字符串首地址
* *++argv 指的是第一個字符串
* (*++argv)[0] 獲取的是第一個字符串的第0個字符元素, 這裏用來判斷是不是'-'
* *++argv[0] : 此時argv指向第二個字符串首地址
* 執行順序 : *(++(argv[0]))
* 執行效果 :
* 取出第0個字符串的第1個字符, 該串的第0個字符是'-'
* argv[0]得到第0個字符的指針
* ++argv[0] 是該字符串的第二個元素的地址
* *++argv[0] 是該字符串第二個元素
*/
void option_analysis(int argc, char ***argvp)
{
/*
* 根據--argc > 0 判斷輸入的參數, 如果 --argc 大於0, 那麼說明後面還有參數
* 循環所有的參數, 將所有的 -可選參數遍歷出來
*
* 每次循環 argv 指針就自增, 指向下一個參數字符串
* 如果字符串的第0個字符是 '-', 那麼該參數字符串是可選參數
*/
while (--argc > 0 && (*++(*argvp))[0] == '-')
{
/*
* 先獲取這個可選字符串指針, 然後在一次遍歷這個指針
* 根據遍歷的結果設置對應的標識位
*/
while (c = *++(*argvp)[0])
{
switch(c)
{
case 'x':
except = 1;
break;
case 'n':
number = 1;
break;
default:
printf("option illegal \n");
argc = 0;
found = -1;
break;
}
}
}
}
/*
* 在上面的option_analysis函數中傳入的argv指針的地址, 因此上面對argv的自增操作改變了指針
* 這裏的*argv 就可以取出 argv 第0個元素字符串的指針
*
* 如果輸入的字符串能匹配參數
* 沒有輸入x的情況
* 輸入了n 輸出帶行號, 不匹配的字符串
* 沒有輸入n 輸出不帶行號, 不匹配的字符串
* 如果輸入了x參數
* 輸入了n 輸出帶行號的, 匹配的字符串
* 沒有輸入n , 輸出不帶行號的, 匹配的字符串
*/
void out_put(char **argv)
{
while (get_line(line, MAXLEN) > 0)
{
lineno++;
if ((strstr (line, *argv) != NULL) != except)
{
if(number)
printf("%d : ", lineno);
printf("%s \n", line);
}
}
}
執行結果 :
- octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc grep_option.c -o grep
- octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -xn a
- qw
- 1 : qw
- as
- qc
- 3 : qc
- octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -n a
- qw
- as
- 2 : as
- qc
- octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -x a
- qw
- qw
- as
- qc
- qc
octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc grep_option.c -o grep
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -xn a
qw
1 : qw
as
qc
3 : qc
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -n a
qw
as
2 : as
qc
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -x a
qw
qw
as
qc
qc
2. 函數指針 和 指針函數
(1) 指針函數
概念 : 函數返回的結果是一個地址, 即返回的是一個指針, 這個函數就是指針函數;
指針函數格式 : 類型說明符 *函數名(參數列表);
-- 示例 : char *getchar(void);
-- 格式說明 : char * 表示函數返回值是指針, 調用這個函數, 返回一個指針指向的char類型;
運算符優先級 : 指針函數有兩個運算符 * 和 (), ()的優先級 大於 *, 因此函數名首先和 () 結合, 然後在和 * 結合;
(2) 函數指針
概念 : 函數指針指向了函數的地址, 該指針可以調用函數;
函數指針格式 : 類型說明符 (*指針名)(參數列表);
-- 示例 : char (*getcahr)(void);
運算符優先級 : * 和 指針名 先結合, 然後在與參數列表結合;
函數指針使用 :
-- 聲明函數指針 : void (*getchar)(), 聲明函數指針需要帶上參數列表;
-- 爲函數指針賦值 : getchar = &get_char 或者 getchar = get_char 兩種方法, & 可有可無;
-- 調用函數指針方法 : (*get_char)();
(3) 使用函數指針示例
示例需求 :
-- 獲取字符串數組 : 從標準輸入流中讀取字符串數據, 將字符串放入字符串數組 char **;
-- 可選參數 : -n, 如果有可選參數, 就是按照數值順序排序, 否則按照字典順序排序;
代碼 :
- /*************************************************************************
- > File Name: method_pointer_sort.c
- > Author: octopus
- > Mail: octopus_work.163.com
- > Created Time: Sat 22 Mar 2014 11:45:47 PM CST
- ************************************************************************/
- #include<stdio.h>
- #include<string.h>
- #include<stdlib.h>
- //定義排序字符串最大個數
- #define MAXLINES 50
- //每個字符串最多50個元素
- #define MAXLEN 50
- //定義一個 有MAXLINES 個 元素的數組, 數組中的元素師字符串, 即char類型指針
- char *lineptr[MAXLINES];
- /*
- * 聲明函數指針
- */
- int (*p_get_line)(char *, int);
- int (*p_read_lines)(char **, int);
- void (*p_write_lines)(char **, int);
- void (*p_q_sort)(void **, int, int, int (*)(void *, void*));
- /*
- * 聲明函數, 如果直接使用這些函數, 即使函數定義在主函數後面也不會出錯
- * 如果要將函數賦值給函數指針, 需要提前聲明這些函數
- */
- int get_line(char *, int);
- int read_lines(char **, int);
- void write_lines(char **, int);
- void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));
- int numcmp(char *, char *);
- int main(int argc, char **argv)
- {
- char line[MAXLEN];
- int len, nlines, numberic = 0;
- p_read_lines = read_lines;
- p_write_lines = write_lines;
- p_q_sort = q_sort;
- //如果參數中含有 -n 說明這是按照數值順序排序, 否則就按照字典順序排序
- if(argc > 1 && strcmp(argv[1], "-n") == 0)
- numberic = 1;
- if((nlines = (*p_read_lines)(lineptr, MAXLINES)) >= 0)
- {
- /*
- * 注意 :
- * 使用 ? : 表達式選擇 函數指針, 函數指針類型轉換的時候, 爲每個選項都添加轉換
- * 如果只轉換 ? : 結果, 會報出警告
- */
- (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);
- (*p_write_lines)(lineptr, nlines);
- return 0;
- }
- else
- {
- printf("error \n");
- return 1;
- }
- }
- //從標準輸入流中讀取數據, 放入字符串中
- int get_line(char *line, int max)
- {
- int i, c;
- for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i ++)
- {
- *(line + i) = c;
- }
- *(line + i) = '\0';
- return i;
- }
- //從標準輸入流中讀取數據, 放入字符串數組
- int read_lines(char *lineptr[], int max)
- {
- int len, nlines = 0;
- char *p, line[MAXLEN];
- while((len = get_line(line, MAXLEN)) > 0)
- {
- if(nlines >= MAXLINES || (p = malloc(sizeof(char) * (len + 1))) == NULL)
- {
- return -1;
- }
- else
- {
- strcpy(p, line);
- *(lineptr + nlines) = p;
- nlines++;
- }
- }
- return nlines;
- }
- //將字符串數組中的元素打印出來
- void write_lines(char *lineptr[], int nlines)
- {
- int i;
- for(i = 0; i < nlines; i++)
- {
- printf("the %d char sequence is : %s \n", i, *(lineptr + i));
- }
- }
- //數值比較
- int numcmp(char *s1, char *s2)
- {
- double v1, v2;
- v1 = atof(s1);
- v2 = atof(s2);
- if(v1 < v2)
- return -1;
- else if(v1 > v2)
- return 1;
- else if(v1 == v2)
- return 0;
- }
- //交換數組中 i j 元素
- void swap(void *v[], int i, int j)
- {
- void *temp;
- temp = *(v + i);
- *(v + i) = *(v + j);
- *(v + j) = temp;
- }
- //排序方法
- void q_sort(void *v[], int left, int right, int (*comp)(void *, void *))
- {
- int i, last;
- if(left >= right)
- {
- return;
- }
- swap(v, left, (left + right)/2);
- last = left;
- for(i = last + 1; i <= right; i++)
- {
- if((*comp)(v[i], v[left]) < 0)
- swap(v, ++last, i);
- }
- swap(v, left, last);
- q_sort(v, left, last - 1, comp);
- q_sort(v, last + 1, right, comp);
- }
/*************************************************************************
> File Name: method_pointer_sort.c
> Author: octopus
> Mail: octopus_work.163.com
> Created Time: Sat 22 Mar 2014 11:45:47 PM CST
************************************************************************/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
//定義排序字符串最大個數
#define MAXLINES 50
//每個字符串最多50個元素
#define MAXLEN 50
//定義一個 有MAXLINES 個 元素的數組, 數組中的元素師字符串, 即char類型指針
char *lineptr[MAXLINES];
/*
* 聲明函數指針
*/
int (*p_get_line)(char *, int);
int (*p_read_lines)(char **, int);
void (*p_write_lines)(char **, int);
void (*p_q_sort)(void **, int, int, int (*)(void *, void*));
/*
* 聲明函數, 如果直接使用這些函數, 即使函數定義在主函數後面也不會出錯
* 如果要將函數賦值給函數指針, 需要提前聲明這些函數
*/
int get_line(char *, int);
int read_lines(char **, int);
void write_lines(char **, int);
void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));
int numcmp(char *, char *);
int main(int argc, char **argv)
{
char line[MAXLEN];
int len, nlines, numberic = 0;
p_read_lines = read_lines;
p_write_lines = write_lines;
p_q_sort = q_sort;
//如果參數中含有 -n 說明這是按照數值順序排序, 否則就按照字典順序排序
if(argc > 1 && strcmp(argv[1], "-n") == 0)
numberic = 1;
if((nlines = (*p_read_lines)(lineptr, MAXLINES)) >= 0)
{
/*
* 注意 :
* 使用 ? : 表達式選擇 函數指針, 函數指針類型轉換的時候, 爲每個選項都添加轉換
* 如果只轉換 ? : 結果, 會報出警告
*/
(*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);
(*p_write_lines)(lineptr, nlines);
return 0;
}
else
{
printf("error \n");
return 1;
}
}
//從標準輸入流中讀取數據, 放入字符串中
int get_line(char *line, int max)
{
int i, c;
for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i ++)
{
*(line + i) = c;
}
*(line + i) = '\0';
return i;
}
//從標準輸入流中讀取數據, 放入字符串數組
int read_lines(char *lineptr[], int max)
{
int len, nlines = 0;
char *p, line[MAXLEN];
while((len = get_line(line, MAXLEN)) > 0)
{
if(nlines >= MAXLINES || (p = malloc(sizeof(char) * (len + 1))) == NULL)
{
return -1;
}
else
{
strcpy(p, line);
*(lineptr + nlines) = p;
nlines++;
}
}
return nlines;
}
//將字符串數組中的元素打印出來
void write_lines(char *lineptr[], int nlines)
{
int i;
for(i = 0; i < nlines; i++)
{
printf("the %d char sequence is : %s \n", i, *(lineptr + i));
}
}
//數值比較
int numcmp(char *s1, char *s2)
{
double v1, v2;
v1 = atof(s1);
v2 = atof(s2);
if(v1 < v2)
return -1;
else if(v1 > v2)
return 1;
else if(v1 == v2)
return 0;
}
//交換數組中 i j 元素
void swap(void *v[], int i, int j)
{
void *temp;
temp = *(v + i);
*(v + i) = *(v + j);
*(v + j) = temp;
}
//排序方法
void q_sort(void *v[], int left, int right, int (*comp)(void *, void *))
{
int i, last;
if(left >= right)
{
return;
}
swap(v, left, (left + right)/2);
last = left;
for(i = last + 1; i <= right; i++)
{
if((*comp)(v[i], v[left]) < 0)
swap(v, ++last, i);
}
swap(v, left, last);
q_sort(v, left, last - 1, comp);
q_sort(v, last + 1, right, comp);
}
執行結果 :
- [root@ip28 pointer]# gcc method_pointer_sort.c
- [root@ip28 pointer]# ./a.out
- qwe
- asd
- zxc
- rty
- the 0 char sequence is : asd
- the 1 char sequence is : qwe
- the 2 char sequence is : rty
- the 3 char sequence is : zxc
- [root@ip28 pointer]# ./a.out -n
- qwe
- asd
- zxc
- rty
- the 0 char sequence is : asd
- the 1 char sequence is : zxc
- the 2 char sequence is : qwe
- the 3 char sequence is : rty
[root@ip28 pointer]# gcc method_pointer_sort.c
[root@ip28 pointer]# ./a.out
qwe
asd
zxc
rty
the 0 char sequence is : asd
the 1 char sequence is : qwe
the 2 char sequence is : rty
the 3 char sequence is : zxc
[root@ip28 pointer]# ./a.out -n
qwe
asd
zxc
rty
the 0 char sequence is : asd
the 1 char sequence is : zxc
the 2 char sequence is : qwe
the 3 char sequence is : rty
.
3. 指針函數的複雜案例分析
(1) 指針函數 和 函數指針 分析
示例 :
-- 示例一 : char *get_line(char *line, int max);
-- 示例二 : char **get_line(char *line, int max);
-- 示例三 : int *(*get_line)(char *line, int max);
分析 :
-- 示例一 : get_line 普通函數, 返回值是一個char類型指針, 即返回一個字符串;
-- 示例二 : get_line 普通函數, 返回值是一個二級指針, 即字符串數組;
-- 示例三 : get_line 函數指針, 該指針的返回值是一個int類型的指針, 即指針; get_line不是函數名, 是一個指針變量, 使用 int *(*)(char *line, int max) get_line 可以清楚的定義該指針, 不過如果這樣定義就錯誤了;
(2) 指函數指針轉換
示例 :
- char fun();
- void (*p)();
- *(char*)&p = (char)fun;
- (*p)();
char fun();
void (*p)();
*(char*)&p = (char)fun;
(*p)();
-- void (*p)() : 該表達式定義了一個函數指針, 該指針p 指向一個函數, 這個函數的返回值 和 參數都爲NULL;
-- *(char*)&p : p是函數指針, &p 是指向函數指針的指針, (char*)&p 將 指向函數指針的指針 類型改爲 char*, *(char)&p 就是 取出轉換類型的函數指針, 這個是已經轉換好類型的函數指針;
-- (char)fun : 將fun函數的 函數指針轉換爲 char 類型, 函數的入口地址轉換爲 char 類型;
-- *(char*)&p = (char)fun : 指的是將函數的地址 賦值給 指針變量p;
-- (*p)() : 調用這個 指針 指向的函數;
(3) 將地址轉換成函數指針
示例 :
- (*(void(*)())0)() ;
(*(void(*)())0)() ;
解析 :
-- void(*)() : 函數指針類型, 該類型指向的函數 返回值 和 參數 均爲 NULL;
-- (void(*)())0 : 將 0 轉換爲函數指針, 前提是這個位置有該類型的函數;
-- *(void(*)())0 : 將 函數指針 指向的函數取出, 後面加上(), 就是執行這個函數;
(4) 函數指針作爲返回值
示例 : 函數指針作爲返回值, 正向寫寫不出來, 要反向推理;
- char(*get_char(char))(char *, int);
char(*get_char(char))(char *, int);
分析 : 從get_char 開始解析;
-- get_char(char) : get_char 先跟()結合, 表明這是一個函數;
-- *get_char(char) : get_char(char) 與 * 結合, 表明該函數返回值是一個指針;
-- (*get_char(char))(char *, int) : (*get_char(char)) 返回指針, 與後面的(char *, int)結合, 返回的是一個函數指針, 該指針指向函數;
-- char(*get_char(char))(char*, int) : 表明這個返回值函數指針指向的函數的返回值是 char 類型;
簡單的替代方法 : 使用 typedef;
- typedef char(*RETURN)(char *, int);
- RETURN get_char(char);
typedef char(*RETURN)(char *, int);
RETURN get_char(char);
-- 解析 : 先定義一個 char(*)(char *, int) 類型的函數指針, 將該類型命名爲 RETURN, 然後將其設置爲 get_char(char) 函數的返回值;
(5) 函數指針數組
示例 : int (*get_char[2])(char *line, int max);
解析 : 定義了一個函數指針數組, 該數組中有兩個函數指針元素, 這兩個函數的返回值是 int, 參數列表是 char * 和 int;
4. 複雜聲明
C語言聲明運算符優先級 : C語言中, * 運算符的優先級低於 ();
char (*array)[13] 解析: 聲明一個指針;
-- *array : 代表 array 定義的是一個指針;
-- (*array)[13] : (*array) 與後面的 [] 結合, 說明這個指針指向一個 數組;
-- char (*array)[13] : 說明數組中的元素類型是 char ;
char *array[13] 解析 : 聲明一個數組;
-- array[13] : 聲明一個數組, 這個數組中有13個元素;
-- *array[13] : 這個數組中的元素是指針類型元素;
-- char *array[13] : 指針類型爲 char;
char *fun() 解析 : 聲明指針函數;
-- fun() : fun 先與 () 結合, 說明 fun 是一個函數;
-- *fun() : fun() 與 * 結合, 說明返回值是 指針;
-- char *fun() : 返回的指針類型是 char 指針;
char (*fun)() 解析 : 聲明一個函數指針;
-- *fun : 代表 fun 指向一個指針;
-- (*fun)() : 代表該指針指向一個函數;
-- char (*fun)() : 代表這個函數的返回值是一個 char 類型;
char (*(*fun())[])() 解析 : 聲明一個函數;
-- fun() : fun 與 () 結合 說明這是一個函數;
-- *fun() : 說明 該 函數返回值是一個指針p1;
-- (*fun())[] : 該指針p1 指向一個數組array1;
-- *(*fun())[] : array1 數組 與 * 結合, 說明數組元素是 指針 p2;
-- (*(*fun())[])() : 指針 p2 與 () 結合, 說明該指針指向一個函數 fun;
-- char (*(*fun())[])() : fun 函數返回值是一個 char 類型;
char (*(*array[3])())[5] 解析 : 聲明一個數組;
-- array[3] : 說明 聲明的 array 是一個數組;
-- *array[3] : 表明 該數組元素類型是一個指針p1;
-- (*array[3])() : p1 指向的是一個函數, 即這是函數指針;
-- *(*array[3])() : 函數指針指向的函數返回值是一個 指針 *;
-- (*(*array[3])())[5] : 表明該 指針 指向一個數組;
-- char (*(*array[3])())[5] : 數組中的元素是 char 類型;
5. 使用gdb調試程序
簡單使用gdb : 調試 上面 2.(3) 示例程序;
-- 編譯可調試執行文件 : gcc -g method_pointer_sort.c ;
-- 使用gdb運行 : gdb a.out ;
-- 打斷點 : break 53 , break 行號 可以給程序打斷點;
-- 執行 : run , 使用該命令可以執行程序, 程序會在斷點處停下來;
-- 查看變量 : printf 變量名 , 可以查看變量內容;
-- 繼續執行 : n , 繼續執行;
示例 :
- [root@ip28 pointer]# gcc -g method_pointer_sort.c #編譯可調試程序
- [root@ip28 pointer]# gdb a.out #使用gdb執行調試
- GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)
- Copyright (C) 2009 Free Software Foundation, Inc.
- License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
- This is free software: you are free to change and redistribute it.
- There is NO WARRANTY, to the extent permitted by law. Type "show copying"
- and "show warranty" for details.
- This GDB was configured as "x86_64-redhat-linux-gnu".
- For bug reporting instructions, please see:
- <http://www.gnu.org/software/gdb/bugs/>...
- Reading symbols from /root/code/pointer/a.out...done.
- (gdb) list #列出程序內容
- 31 int get_line(char *, int);
- 32 int read_lines(char **, int);
- 33 void write_lines(char **, int);
- 34 void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));
- 35 int numcmp(char *, char *);
- 36
- 37 int main(int argc, char **argv)
- 38 {
- 39 char line[MAXLEN];
- 40 int len, nlines, numberic = 0;
- (gdb) break 53 #給53行打斷點
- Breakpoint 1 at 0x4006f9: file method_pointer_sort.c, line 53.
- (gdb) run # 執行程序
- Starting program: /root/code/pointer/a.out
- q
- w
- e
- r
- Breakpoint 1, main (argc=1, argv=0x7fffffffe988) at method_pointer_sort.c:59
- 59 (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);
- (gdb) print lineptr # 打印二維數組內容
- $1 = {0x602010 "q", 0x602030 "w", 0x602050 "e", 0x602070 "r", 0x0 <repeats 46 times>}
- (gdb) print nlines # 打印int類型變量
- $2 = 4
- (gdb) n # 繼續執行
- 60 (*p_write_lines)(lineptr, nlines);
- (gdb) n #繼續執行
- the 0 char sequence is : e
- the 1 char sequence is : q
- the 2 char sequence is : r
- the 3 char sequence is : w
- 61 return 0;
- (gdb) n
- 69 }
- (gdb) n
- 0x000000306801d994 in __libc_start_main () from /lib64/libc.so.6
- (gdb) quit #退出調試
- A debugging session is active.
- Inferior 1 [process 7799] will be killed.
- Quit anyway? (y or n) y