C語言 命令行參數 函數指針 gdb調試 .

作者 : 萬境絕塵

轉載請註明出處 : 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程序示例

  1. octopus@octopus-Vostro-270s:~/code/c/pointer$ echo csdn  
  2. csdn  
  3. octopus@octopus-Vostro-270s:~/code/c/pointer$ echo Hello World  
  4. 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示例程序

  1. /************************************************************************* 
  2.     > File Name: echo.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: 2014年03月19日 星期三 19時56分36秒 
  6.  ************************************************************************/  
  7.   
  8. #include<stdio.h>   
  9.   
  10. int main(int argc, char **argv)  
  11. {  
  12.     int i;  
  13.     for(i = 1; i < argc; i ++)  
  14.     {  
  15.         //將參數輸出, 每次注意輸出一個空格, 如果是最後一個那就不用輸出空格了   
  16.         printf("%s%s", argv[i], (i < argc - 1) ? " " : "");  
  17.     }  
  18.     printf("\n");  
  19.     return 0;  
  20. }  
/*************************************************************************
    > 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;
}


執行效果

  1. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World  
  2. Hello World  
octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World
Hello World


使用指針訪問參數模仿echo程序

  1. /************************************************************************* 
  2.     > File Name: echo_pointer.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: 2014年03月19日 星期三 20時08分07秒 
  6.  ************************************************************************/  
  7.   
  8. #include<stdio.h>   
  9.   
  10. /* 
  11.  * echo Hello World 
  12.  * 循環條件 : --argc, 如果參數只有一個 echo 那麼什麼都不用打印 
  13.  * 打印參數個數 : 如果參數有 3 個, 那就循環 2 次, 打印兩個參數 
  14.  * 打印參數 : 從 第 2 個參數開始打印 
  15.  * 打印空格 : 如果argc > 1, 說明下一輪還要繼續打印, 此時打印空格 
  16.  */  
  17. int main(int argc, char **argv)  
  18. {  
  19.     while(--argc > 0)  
  20.         printf("%s%s", *++argv, (argc > 1) ? " " : "");  
  21.     printf("\n");  
  22.     return 0;  
  23. }  
/*************************************************************************
    > 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;
}

執行結果

  1. octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc echo_pointer.c -o echo  
  2. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello World  
  3. 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遍;


代碼 :

  1. /************************************************************************* 
  2.     > File Name: grep.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: 2014年03月19日 星期三 20時45分47秒 
  6.  ************************************************************************/  
  7.   
  8. #include<stdio.h>   
  9. #include<string.h>   
  10. #define MAXLEN 50   
  11.   
  12. //先聲明函數, 才能在main函數中使用, 否則函數要在main函數之前定義纔可以使用   
  13. int get_line(char *line, int max);  
  14.   
  15. int main(int argc, char **argv)  
  16. {  
  17.     char line[MAXLEN];  
  18.     int found = 0;  
  19.     if(argc == 1)  
  20.         printf("wrong parameters ! \n");  
  21.     else  
  22.         /* 
  23.          * 當獲取的字符個數大於1的時候 
  24.          * 比較字符串 與 參數1 
  25.          * 如果返回的不爲NULL, 那麼說明查找到了字串 
  26.          * 將字串打印出來 
  27.          */  
  28.         while(get_line(line, MAXLEN) > 0)  
  29.         {  
  30.             if(strstr(line, argv[1]) != NULL)  
  31.             {  
  32.                 printf("%s \n", line);  
  33.                 found ++;  
  34.             }  
  35.         }  
  36.     return 0;  
  37. }  
  38.   
  39. /* 
  40.  * 從標準輸入流中獲取字符串, 將字符串存儲到 char *line 指針指向的數組中 
  41.  * 注意字符串最大爲50個, 字符最多有49個, 剩下的最後一位存放 '\0' 
  42.  * 從標準輸入流中讀取字符, 放到數組中 
  43.  * 停止讀取字符條件 : 個數到達 48個, 讀取到了 回車 '\n' 或者 EOF (ctrl + D) 
  44.  */  
  45. int get_line(char *line, int max)  
  46. {  
  47.     int i, c;  
  48.     for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i++)  
  49.         line[i] = c;  
  50.     line[i] = '\0';  
  51.     return i;  
  52. }  
/*************************************************************************
    > 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, 那麼就將字符串再輸出一遍;

  1. octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc grep.c -o grep  
  2. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep abc  
  3. qwe  
  4. asd  
  5. zxc  
  6. qabc  
  7. qabc   
  8. wabcc  
  9. wabcc   
  10. 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]表達式




代碼

  1. /************************************************************************* 
  2.     > File Name: grep_option.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: 2014年03月21日 星期五 09時30分07秒 
  6.  ************************************************************************/  
  7.   
  8. #include<stdio.h>   
  9. #include<string.h>   
  10. #define MAXLEN 15   
  11.   
  12. char line[MAXLEN];  
  13. int c, lineno = 0, except = 0, number = 0, found = 0;  
  14.   
  15. int get_line(char *line, int max);  
  16. void option_analysis(int argc, char ***argv);  
  17. void out_put(char **argv);  
  18.   
  19. int main(int argc, char **argv)  
  20. {  
  21.   
  22.     option_analysis(argc, &argv);  
  23.     out_put(argv);  
  24.     return 0;  
  25. }  
  26.   
  27. /* 
  28.  * 從標準輸入流中獲取字符串, 最長能獲取max個 
  29.  * 個數計算 :  
  30.  *   字符串最長是 max 個 
  31.  *   注意裏面包含了 '\0'  
  32.  *   實際字符個數只能是 max - 1 個 
  33.  *   實際的最大下標是 max - 1 
  34.  * 字符串終止 :  
  35.  *   輸入 '\n' (回車) 
  36.  *   輸入 EOF (CTRL + D) 
  37.  *   字符個數達到 max - 1 個 
  38.  * 注意 : 最後一個元素賦值爲 '\0' 
  39.  */  
  40. int get_line(char *line, int max)  
  41. {  
  42.     int i, c;  
  43.     for (i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i++)  
  44.     {  
  45.         line[i] = c;  
  46.     }  
  47.     line[i] = '\0';  
  48.     return i;  
  49. }  
  50.   
  51. /* 
  52.  * 分析可選參數情況, 根據接收到的可選參數情況, 設置標識位 
  53.  * 
  54.  * 後來對方法參數進行了小修改, 這裏需要改變argv指針指向,  
  55.  * 因此需要將該指針的地址傳給函數 
  56.  * 
  57.  * 區分 (*++argv)[0] 和 *++argv[0] 
  58.  *   (*++argv)[0] :  
  59.  *     執行順序 : (*(++argv))[0], * 和 ++ 同時存在 執行順序自右向左 
  60.  *     執行效果 :  
  61.  *       ++argv 將指針指向了 下標爲1 的字符串首地址  
  62.  *       *++argv 指的是第一個字符串 
  63.  *       (*++argv)[0] 獲取的是第一個字符串的第0個字符元素, 這裏用來判斷是不是'-' 
  64.  *   *++argv[0] : 此時argv指向第二個字符串首地址 
  65.  *     執行順序 : *(++(argv[0]))  
  66.  *     執行效果 :  
  67.  *       取出第0個字符串的第1個字符, 該串的第0個字符是'-' 
  68.  *       argv[0]得到第0個字符的指針 
  69.  *       ++argv[0] 是該字符串的第二個元素的地址 
  70.  *       *++argv[0] 是該字符串第二個元素 
  71.  */  
  72. void option_analysis(int argc, char ***argvp)  
  73. {  
  74.     /* 
  75.      * 根據--argc > 0 判斷輸入的參數, 如果 --argc 大於0, 那麼說明後面還有參數  
  76.      * 循環所有的參數, 將所有的 -可選參數遍歷出來 
  77.      * 
  78.      * 每次循環 argv 指針就自增, 指向下一個參數字符串 
  79.      * 如果字符串的第0個字符是 '-',  那麼該參數字符串是可選參數 
  80.      */  
  81.     while (--argc > 0 && (*++(*argvp))[0] == '-')  
  82.     {  
  83.         /* 
  84.          * 先獲取這個可選字符串指針, 然後在一次遍歷這個指針 
  85.          * 根據遍歷的結果設置對應的標識位 
  86.          */  
  87.         while (c = *++(*argvp)[0])  
  88.         {  
  89.             switch(c)  
  90.             {  
  91.                 case 'x':  
  92.                     except = 1;  
  93.                     break;  
  94.                 case 'n':  
  95.                     number = 1;  
  96.                     break;  
  97.                 default:  
  98.                     printf("option illegal \n");  
  99.                     argc = 0;  
  100.                     found = -1;  
  101.                     break;  
  102.             }  
  103.         }  
  104.     }  
  105. }  
  106.   
  107. /* 
  108.  * 在上面的option_analysis函數中傳入的argv指針的地址, 因此上面對argv的自增操作改變了指針 
  109.  * 這裏的*argv 就可以取出 argv 第0個元素字符串的指針 
  110.  * 
  111.  * 如果輸入的字符串能匹配參數 
  112.  *   沒有輸入x的情況 
  113.  *     輸入了n 輸出帶行號, 不匹配的字符串 
  114.  *     沒有輸入n 輸出不帶行號, 不匹配的字符串 
  115.  *   如果輸入了x參數 
  116.  *     輸入了n 輸出帶行號的, 匹配的字符串 
  117.  *     沒有輸入n , 輸出不帶行號的, 匹配的字符串 
  118.  */  
  119. void out_put(char **argv)  
  120. {  
  121.     while (get_line(line, MAXLEN) > 0)  
  122.     {  
  123.         lineno++;  
  124.         if ((strstr (line, *argv) != NULL) != except)  
  125.         {  
  126.             if(number)  
  127.                 printf("%d : ", lineno);  
  128.             printf("%s \n", line);  
  129.         }  
  130.     }  
  131. }  
/*************************************************************************
    > 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);
		}
	}
}

執行結果

  1. octopus@octopus-Vostro-270s:~/code/c/pointer$ gcc grep_option.c -o grep  
  2. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -xn a  
  3. qw  
  4. 1 : qw   
  5. as  
  6. qc  
  7. 3 : qc   
  8. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -n a  
  9. qw  
  10. as  
  11. 2 : as   
  12. qc  
  13. octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -x a  
  14. qw  
  15. qw   
  16. as  
  17. qc  
  18. 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, 如果有可選參數, 就是按照數值順序排序, 否則按照字典順序排序;


代碼

  1. /************************************************************************* 
  2.     > File Name: method_pointer_sort.c 
  3.     > Author: octopus 
  4.     > Mail: octopus_work.163.com  
  5.     > Created Time: Sat 22 Mar 2014 11:45:47 PM CST 
  6.  ************************************************************************/  
  7.   
  8. #include<stdio.h>   
  9. #include<string.h>   
  10. #include<stdlib.h>   
  11.   
  12. //定義排序字符串最大個數   
  13. #define MAXLINES 50   
  14. //每個字符串最多50個元素   
  15. #define MAXLEN 50   
  16. //定義一個 有MAXLINES 個 元素的數組, 數組中的元素師字符串, 即char類型指針   
  17. char *lineptr[MAXLINES];  
  18.   
  19. /* 
  20.  * 聲明函數指針 
  21.  */  
  22. int (*p_get_line)(char *, int);  
  23. int (*p_read_lines)(char **, int);  
  24. void (*p_write_lines)(char **, int);  
  25. void (*p_q_sort)(void **, intintint (*)(void *, void*));  
  26.   
  27. /* 
  28.  * 聲明函數, 如果直接使用這些函數, 即使函數定義在主函數後面也不會出錯 
  29.  * 如果要將函數賦值給函數指針, 需要提前聲明這些函數 
  30.  */  
  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;  
  41.   
  42.         p_read_lines = read_lines;  
  43.   
  44.         p_write_lines = write_lines;  
  45.   
  46.         p_q_sort = q_sort;  
  47.   
  48.         //如果參數中含有 -n 說明這是按照數值順序排序, 否則就按照字典順序排序   
  49.         if(argc > 1 && strcmp(argv[1], "-n") == 0)  
  50.                 numberic = 1;  
  51.   
  52.         if((nlines = (*p_read_lines)(lineptr, MAXLINES)) >= 0)  
  53.         {  
  54.                 /* 
  55.                  * 注意 :  
  56.                  *   使用 ? : 表達式選擇 函數指針, 函數指針類型轉換的時候, 爲每個選項都添加轉換 
  57.                  *   如果只轉換 ? : 結果, 會報出警告 
  58.                  */  
  59.                 (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);  
  60.                 (*p_write_lines)(lineptr, nlines);  
  61.                 return 0;  
  62.         }  
  63.         else  
  64.         {  
  65.                 printf("error \n");  
  66.                 return 1;  
  67.         }  
  68.   
  69. }  
  70.   
  71. //從標準輸入流中讀取數據, 放入字符串中   
  72. int get_line(char *line, int max)  
  73. {  
  74.         int i, c;  
  75.         for(i = 0; (c = getchar()) != '\n' && c != EOF && i < max - 1; i ++)  
  76.         {  
  77.                 *(line + i) = c;  
  78.         }  
  79.         *(line + i) = '\0';  
  80.         return i;  
  81. }  
  82.   
  83. //從標準輸入流中讀取數據, 放入字符串數組   
  84. int read_lines(char *lineptr[], int max)  
  85. {  
  86.         int len, nlines = 0;  
  87.         char *p, line[MAXLEN];  
  88.   
  89.         while((len = get_line(line, MAXLEN)) > 0)  
  90.         {  
  91.                 if(nlines >= MAXLINES || (p = malloc(sizeof(char) * (len + 1))) == NULL)  
  92.                 {  
  93.                         return -1;  
  94.                 }  
  95.                 else  
  96.                 {  
  97.                         strcpy(p, line);  
  98.                         *(lineptr + nlines) = p;  
  99.                         nlines++;  
  100.                 }  
  101.         }  
  102.         return nlines;  
  103. }  
  104.   
  105. //將字符串數組中的元素打印出來   
  106. void write_lines(char *lineptr[], int nlines)  
  107. {  
  108.         int i;  
  109.         for(i = 0; i < nlines; i++)  
  110.         {  
  111.                 printf("the %d char sequence is : %s \n", i, *(lineptr + i));  
  112.         }  
  113. }  
  114.   
  115. //數值比較   
  116. int numcmp(char *s1, char *s2)  
  117. {  
  118.         double v1, v2;  
  119.   
  120.         v1 = atof(s1);  
  121.         v2 = atof(s2);  
  122.   
  123.         if(v1 < v2)  
  124.                 return -1;  
  125.         else if(v1 > v2)  
  126.                 return 1;  
  127.         else if(v1 == v2)  
  128.                 return 0;  
  129. }  
  130.   
  131. //交換數組中 i j 元素   
  132. void swap(void *v[], int i, int j)  
  133. {  
  134.         void *temp;  
  135.   
  136.         temp = *(v + i);  
  137.         *(v + i) = *(v + j);  
  138.         *(v + j) = temp;  
  139. }  
  140.   
  141. //排序方法   
  142. void q_sort(void *v[], int left, int right, int (*comp)(void *, void *))  
  143. {  
  144.         int i, last;  
  145.   
  146.         if(left >= right)  
  147.         {  
  148.                 return;  
  149.         }  
  150.         swap(v, left, (left + right)/2);  
  151.         last = left;  
  152.         for(i = last + 1; i <= right; i++)  
  153.         {  
  154.                 if((*comp)(v[i], v[left]) < 0)  
  155.                         swap(v, ++last, i);  
  156.         }  
  157.         swap(v, left, last);  
  158.         q_sort(v, left, last - 1, comp);  
  159.         q_sort(v, last + 1, right, comp);  
  160.   
  161. }  
/*************************************************************************
    > 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);

}


執行結果

  1. [root@ip28 pointer]# gcc method_pointer_sort.c    
  2. [root@ip28 pointer]# ./a.out    
  3. qwe  
  4. asd  
  5. zxc  
  6. rty  
  7.   
  8. the 0 char sequence is : asd   
  9. the 1 char sequence is : qwe   
  10. the 2 char sequence is : rty   
  11. the 3 char sequence is : zxc   
  12. [root@ip28 pointer]# ./a.out -n   
  13. qwe  
  14. asd  
  15. zxc  
  16. rty  
  17.   
  18. the 0 char sequence is : asd   
  19. the 1 char sequence is : zxc   
  20. the 2 char sequence is : qwe   
  21. 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) 指函數指針轉換


示例

  1. char fun();  
  2. void (*p)();  
  3. *(char*)&p = (char)fun;  
  4. (*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) 將地址轉換成函數指針


示例

  1. (*(void(*)())0)() ;  
(*(void(*)())0)() ;


解析

-- void(*)() : 函數指針類型, 該類型指向的函數 返回值 和 參數 均爲 NULL;

-- (void(*)())0 : 將 0 轉換爲函數指針, 前提是這個位置有該類型的函數;

-- *(void(*)())0 : 將 函數指針 指向的函數取出, 後面加上(), 就是執行這個函數;


(4) 函數指針作爲返回值


示例 : 函數指針作爲返回值, 正向寫寫不出來, 要反向推理;

  1. 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;

  1. typedef char(*RETURN)(char *, int);  
  2. 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 , 繼續執行;


示例

  1. [root@ip28 pointer]# gcc -g method_pointer_sort.c #編譯可調試程序   
  2. [root@ip28 pointer]# gdb a.out #使用gdb執行調試   
  3. GNU gdb (GDB) CentOS (7.0.1-45.el5.centos)  
  4. Copyright (C) 2009 Free Software Foundation, Inc.  
  5. License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>  
  6. This is free software: you are free to change and redistribute it.  
  7. There is NO WARRANTY, to the extent permitted by law.  Type "show copying"  
  8. and "show warranty" for details.  
  9. This GDB was configured as "x86_64-redhat-linux-gnu".  
  10. For bug reporting instructions, please see:  
  11. <http://www.gnu.org/software/gdb/bugs/>...  
  12. Reading symbols from /root/code/pointer/a.out...done.  
  13. (gdb) list #列出程序內容   
  14. 31      int get_line(char *, int);  
  15. 32      int read_lines(char **, int);  
  16. 33      void write_lines(char **, int);  
  17. 34      void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));  
  18. 35      int numcmp(char *, char *);  
  19. 36  
  20. 37      int main(int argc, char **argv)  
  21. 38      {  
  22. 39              char line[MAXLEN];  
  23. 40              int len, nlines, numberic = 0;  
  24. (gdb) break 53 #給53行打斷點   
  25. Breakpoint 1 at 0x4006f9: file method_pointer_sort.c, line 53.  
  26. (gdb) run # 執行程序   
  27. Starting program: /root/code/pointer/a.out   
  28. q  
  29. w  
  30. e  
  31. r  
  32.   
  33.   
  34. Breakpoint 1, main (argc=1, argv=0x7fffffffe988) at method_pointer_sort.c:59  
  35. 59                      (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);  
  36. (gdb) print lineptr # 打印二維數組內容   
  37. $1 = {0x602010 "q"0x602030 "w"0x602050 "e"0x602070 "r"0x0 <repeats 46 times>}  
  38. (gdb) print nlines # 打印int類型變量   
  39. $2 = 4  
  40. (gdb) n # 繼續執行   
  41. 60                      (*p_write_lines)(lineptr, nlines);  
  42. (gdb) n #繼續執行   
  43. the 0 char sequence is : e   
  44. the 1 char sequence is : q   
  45. the 2 char sequence is : r   
  46. the 3 char sequence is : w   
  47. 61                      return 0;  
  48. (gdb) n  
  49. 69      }  
  50. (gdb) n  
  51. 0x000000306801d994 in __libc_start_main () from /lib64/libc.so.6  
  52. (gdb) quit #退出調試   
  53. A debugging session is active.  
  54.   
  55.         Inferior 1 [process 7799] will be killed.  
  56.   
  57. Quit anyway? (y or n) y  
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章