700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

时间:2021-10-28 11:51:57

相关推荐

【嵌入式开发】C语言 命令行参数 函数指针 gdb调试

.

作者:万境绝尘

转载请注明出处:/shulianghan/article/details/21551397|/?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 csdncsdnoctopus@octopus-Vostro-270s:~/code/c/pointer$ echo Hello WorldHello 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: > Created Time: 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 WorldHello World

使用指针访问参数模仿echo程序:

/*************************************************************************> File Name: echo_pointer.c> Author: octopus> Mail: > Created Time: 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 echooctopus@octopus-Vostro-270s:~/code/c/pointer$ ./echo Hello WorldHello World

(2) 模仿 简单grep 程序

程序介绍: 模仿 grep 过滤, 过滤数据来自标准输入流, grep 命令匹配第一个参数, 凡是输入的字符串 包含 第一个参数字符串, 就输出这个字符串, 相当于将字符串输出了2遍;

代码:

/*************************************************************************> File Name: grep.c> Author: octopus> Mail: > Created Time: 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 grepoctopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep abcqweasdzxcqabcqabc wabccwabcc 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: > Created Time: 03月21日 星期五 09时30分07秒************************************************************************/#include<stdio.h>#include<string.h>#define MAXLEN 15char 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 grepoctopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -xn aqw1 : qw asqc3 : qc octopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -n aqwas2 : as qcoctopus@octopus-Vostro-270s:~/code/c/pointer$ ./grep -x aqwqw asqcqc

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: > Created Time: Sat 22 Mar 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 qweasdzxcrtythe 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 -nqweasdzxcrtythe 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(*)())0)() ;

解析:

--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]# 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) Free Software Foundation, Inc.License GPLv3+: GNU GPL version 3 or later </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:</software/gdb/bugs/>...Reading symbols from /root/code/pointer/a.out...done.(gdb) list #列出程序内容31int get_line(char *, int);32int read_lines(char **, int);33void write_lines(char **, int);34void q_sort(void *v[], int left, int right, int (*comp)(void *, void *));35int numcmp(char *, char *);3637int 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 qwerBreakpoint 1, main (argc=1, argv=0x7fffffffe988) at method_pointer_sort.c:5959 (*p_q_sort)((void **)lineptr, 0, nlines - 1, numberic ? (int (*)(void*, void*))numcmp : (int (*)(void*, void*))strcmp);(gdb) print lineptr # 打印二维数组内容$1 = {0x60 "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) n69}(gdb) n0x000000306801d994 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

作者:万境绝尘

转载请注明出处:/shulianghan/article/details/21551397|/?post=29

.

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。