.
作者 : 万境绝尘
转载请注明出处 : 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
--
分析命令行
: echo Hello World 将 Hello World 输出到命令行中, 该命令 argc 值是3, argv[0] 是 echo, argv[1] 是 Hello, argv[2] 是 World, 注意 argv[3] 是 空指针 0;
使用数组下标方式访问参数模仿echo示例程序 :
-
-
-
-
-
-
-
- #include
-
- 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
使用指针访问参数模仿echo程序
:
-
-
-
-
-
-
-
- #include
-
-
-
-
-
-
-
-
- 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
(2) 模仿 简单grep 程序
程序介绍 : 模仿 grep 过滤, 过滤数据来自标准输入流, grep 命令匹配第一个参数, 凡是输入的字符串 包含 第一个参数字符串, 就输出这个字符串, 相当于将字符串输出了2遍;
代码 :
-
-
-
-
-
-
-
- #include
- #include
- #define MAXLEN 50
-
-
- 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
-
-
-
-
-
-
- while(get_line(line, MAXLEN) > 0)
- {
- if(strstr(line, argv[1]) != NULL)
- {
- printf("%s \n", line);
- found ++;
- }
- }
- return 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;
- }
执行结果
: 参数是 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
要点解析 :
-- 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]表达式 :
代码 :
-
-
-
-
-
-
-
- #include
- #include
- #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;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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;
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- void option_analysis(int argc, char ***argvp)
- {
-
-
-
-
-
-
-
- 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;
- }
- }
- }
- }
-
-
-
-
-
-
-
-
-
-
-
-
-
- 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
2. 函数指针 和 指针函数
(1) 指针函数
概念 : 函数返回的结果是一个地址, 即返回的是一个指针, 这个函数就是指针函数;
指针函数格式 : 类型说明符 *函数名(参数列表);
-- 示例 : char *getchar(void);
-- 格式说明 : char * 表示函数返回值是指针, 调用这个函数, 返回一个指针指向的char类型;
运算符优先级 : 指针函数有两个运算符 * 和 (), ()的优先级 大于 *, 因此函数名首先和 () 结合, 然后在和 * 结合;
(2) 函数指针
概念 : 函数指针指向了函数的地址, 该指针可以调用函数;
函数指针格式 : 类型说明符 (*指针名)(参数列表);
-- 示例 : char (*getcahr)(void);
运算符优先级 : * 和 指针名 先结合, 然后在与参数列表结合;
函数指针使用 :
-- 声明函数指针 : void (*getchar)(), 声明函数指针需要带上参数列表;
-- 为函数指针赋值 : getchar = &get_char 或者 getchar = get_char 两种方法, & 可有可无;
-- 调用函数指针方法 : (*get_char)();
(3) 使用函数指针示例
示例需求 :
-- 获取字符串数组 : 从标准输入流中读取字符串数据, 将字符串放入字符串数组 char **;
-- 可选参数 : -n, 如果有可选参数, 就是按照数值顺序排序, 否则按照字典顺序排序;
代码 :
-
-
-
-
-
-
-
- #include
- #include
- #include
-
-
- #define MAXLINES 50
-
- #define MAXLEN 50
-
- 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;
-
-
- 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;
- }
-
-
- 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]
- [root@ip28 pointer]
- 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]
- 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)();
解析
:
-- 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(*)() : 函数指针类型, 该类型指向的函数 返回值 和 参数 均为 NULL;
-- (void(*)())0 : 将 0 转换为函数指针, 前提是这个位置有该类型的函数;
-- *(void(*)())0 : 将 函数指针 指向的函数取出, 后面加上(), 就是执行这个函数;
(4) 函数指针作为返回值
示例 : 函数指针作为返回值, 正向写写不出来, 要反向推理;
- 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);
--
解析
: 先定义一个 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]
- [root@ip28 pointer]
- 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
- 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:
- ...
- 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
- 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 46 times>}
- (gdb) print nlines
- $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