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

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 【嵌入式开发】C语言 命令行参数 函数指针 gdb调试(二)

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);
}



.


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) 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
相关实践学习
阿里云图数据库GDB入门与应用
图数据库(Graph Database,简称GDB)是一种支持Property Graph图模型、用于处理高度连接数据查询与存储的实时、可靠的在线数据库服务。它支持Apache TinkerPop Gremlin查询语言,可以帮您快速构建基于高度连接的数据集的应用程序。GDB非常适合社交网络、欺诈检测、推荐引擎、实时图谱、网络/IT运营这类高度互连数据集的场景。 GDB由阿里云自主研发,具备如下优势: 标准图查询语言:支持属性图,高度兼容Gremlin图查询语言。 高度优化的自研引擎:高度优化的自研图计算层和存储层,云盘多副本保障数据超高可靠,支持ACID事务。 服务高可用:支持高可用实例,节点故障迅速转移,保障业务连续性。 易运维:提供备份恢复、自动升级、监控告警、故障切换等丰富的运维功能,大幅降低运维成本。 产品主页:https://www.aliyun.com/product/gdb
目录
相关文章
|
2月前
|
NoSQL 编译器 C语言
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。
C语言调试是开发中的重要技能,涵盖基本技巧如打印输出、断点调试和单步执行,以及使用GCC、GDB、Visual Studio和Eclipse CDT等工具。高级技巧包括内存检查、性能分析和符号调试。通过实践案例学习如何有效定位和解决问题,同时注意保持耐心、合理利用工具、记录过程并避免过度调试,以提高编程能力和开发效率。
53 1
|
2月前
|
存储 算法 C语言
用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容
本文探讨了用C语言开发游戏的实践过程,包括选择游戏类型、设计游戏框架、实现图形界面、游戏逻辑、调整游戏难度、添加音效音乐、性能优化、测试调试等内容,旨在为开发者提供全面的指导和灵感。
51 2
|
3月前
|
存储 C语言 C++
如何通过指针作为函数参数来实现函数的返回多个值
在C语言中,可以通过将指针作为函数参数来实现函数返回多个值。调用函数时,传递变量的地址,函数内部通过修改指针所指向的内存来改变原变量的值,从而实现多值返回。
|
3月前
|
存储 搜索推荐 C语言
如何理解指针作为函数参数的输入和输出特性
指针作为函数参数时,可以实现输入和输出的双重功能。通过指针传递变量的地址,函数可以修改外部变量的值,实现输出;同时,指针本身也可以作为输入,传递初始值或状态。这种方式提高了函数的灵活性和效率。
|
3月前
|
C语言
C语言调试
C语言调试
24 0
|
5月前
|
NoSQL Linux C语言
Linux GDB 调试
Linux GDB 调试
73 10
|
5月前
|
程序员 C语言
指针在函数参数和返回值中的使用
指针在函数参数和返回值中的使用
76 9
|
5月前
|
NoSQL Linux C语言
嵌入式GDB调试Linux C程序或交叉编译(开发板)
【8月更文挑战第24天】本文档介绍了如何在嵌入式环境下使用GDB调试Linux C程序及进行交叉编译。调试步骤包括:编译程序时加入`-g`选项以生成调试信息;启动GDB并加载程序;设置断点;运行程序至断点;单步执行代码;查看变量值;继续执行或退出GDB。对于交叉编译,需安装对应架构的交叉编译工具链,配置编译环境,使用工具链编译程序,并将程序传输到开发板进行调试。过程中可能遇到工具链不匹配等问题,需针对性解决。
168 3
|
5月前
|
NoSQL
技术分享:如何使用GDB调试不带调试信息的可执行程序
【8月更文挑战第27天】在软件开发和调试过程中,我们有时会遇到需要调试没有调试信息的可执行程序的情况。这可能是由于程序在编译时没有加入调试信息,或者调试信息被剥离了。然而,即使面对这样的挑战,GDB(GNU Debugger)仍然提供了一些方法和技术来帮助我们进行调试。以下将详细介绍如何使用GDB调试不带调试信息的可执行程序。
151 0
|
8月前
|
NoSQL 搜索推荐 openCL
【C/C++ 调试 GDB指南 】gdb调试基本操作
【C/C++ 调试 GDB指南 】gdb调试基本操作
427 2