嵌入式软件开发常用的套路与技巧

发布时间:2023-12-12  

01. 调试相关的宏

在Linux使用gcc编译程序的时候,对于调试的语句还具有一些特殊的语法。

本文引用地址:

gcc编译的过程中,会生成一些宏,可以使用这些宏分别打印当前源文件的信息,主要内容是当前的文件、当前运行的函数和当前的程序行。

具体宏如下:

__FILE__  当前程序源文件 (char*)
__FUNCTION__  当前运行的函数 (char*)
__LINE__  当前的函数行 (int)

这些宏不是程序代码定义的,而是有编译器产生的。这些信息都是在编译器处理文件的时候动态产生的。

「测试示例:」

#include 
int main(void){
    printf("file: %sn", __FILE__);
    printf("function: %sn", __FUNCTION__);
    printf("line: %dn", __LINE__);
    return 0;
}

02. # 字符串化操作符

在gcc的编译系统中,可以使用#将当前的内容转换成字符串。

「程序示例:」

#include 
#define DPRINT(expr) printf("
%s = %dn", #expr, expr);
int main(void){
    int x = 3;
    int y = 5;
    DPRINT(x / y);
    DPRINT(x + y);
    DPRINT(x * y);
    
    return 0;
}

「执行结果:」

deng@itcast:~/tmp$ gcc test.c 
deng@itcast:~/tmp$ ./a.out  
x / y = 0x + y = 8x * y = 15

#expr表示根据宏中的参数(即表达式的内容),生成一个字符串。该过程同样是有编译器产生的,编译器在编译源文件的时候,如果遇到了类似的宏,会自动根据程序中表达式的内容,生成一个字符串的宏。

这种方式的优点是可以用统一的方法打印表达式的内容,在程序的调试过程中可以方便直观的看到转换字符串之后的表达式。

具体的表达式的内容是什么,有编译器自动写入程序中,这样使用相同的宏打印所有表达式的字符串。

//打印字符
#define debugc(expr) printf(" %s = %cn", #expr, expr)
//打印浮点数
#define debugf(expr) printf(" %s = %fn", #expr, expr)
//按照16进制打印整数
#define debugx(expr) printf(" %s = 0X%xn", #expr, expr);

由于#expr本质上市一个表示字符串的宏,因此在程序中也可以不适用%s打印它的内容,而是可以将其直接与其它的字符串连接。

因此,上述宏可以等价以下形式:

//打印字符
#define debugc(expr) printf(" #expr = %cn", expr)
//打印浮点数
#define debugf(expr) printf(" #expr = %fn", expr)
//按照16进制打印整数
#define debugx(expr) printf(" #expr = 0X%xn", expr);

「总结:」

#是C语言预处理阶段的字符串化操作符,可将宏中的内容转换成字符串。

03. ## 连接操作符

在gcc的编译系统中,##是C语言中的连接操作符,可以在编译的预处理阶段实现字符串连接的操作。

「程序示例:」

#include 
#define test(x) test##x
void test1(int a){
    printf("test1 a = %dn", a);
}
void test2(char *s){
    printf("test2 s = %sn", s);
}
int main(void){
    test(1)(100);
    test(2)("hello world");
    
    return 0;
}

上述程序中,test(x)宏被定义为test##x, 他表示test字符串和x字符串的连接。

在程序的调试语句中,##常用的方式如下

#define DEBUG(fmt, args...) printf(fmt, ##args)

替换的方式是将参数的两个部分以##连接。##表示连接变量代表前面的参数列表。使用这种形式可以将宏的参数传递给一个参数。args…是宏的参数,表示可变的参数列表,使用##args将其传给printf函数.

「总结:」

##是C语言预处理阶段的连接操作符,可实现宏参数的连接。

04. 调试宏第一种形式

一种定义的方式:

#define DEBUG(fmt, args...)             
    {                                   
    printf("file:%s function: %s line: %d ", __FILE__, __FUNCTION__, __LINE__);
    printf(fmt, ##args);                
    }

「程序示例:」

#include 
#define DEBUG(fmt, args...)             
    {                                   
    printf("file:%s function: %s line: %d ", __FILE__, __FUNCTION__, __LINE__);
    printf(fmt, ##args);                
    }

int main(void){
    int a = 100;
    int b = 200;
    char *s = "hello world";
    DEBUG("a = %d b = %dn", a, b);
    DEBUG("a = %x b = %xn", a, b);
    DEBUG("s = %sn", s);
    
    return 0;
}

「总结:」

上面的DEBUG定义的方式是两条语句的组合,不可能在产生返回值,因此不能使用它的返回值。

05. 调试宏的第二种定义方式

调试宏的第二种定义方式

#define DEBUG(fmt, args...)             
    printf("file:%s function: %s line: %d "fmt, 
    __FILE__, __FUNCTION__, __LINE__, ##args)

程序示例

#include 
#define DEBUG(fmt, args...)             
    printf("file:%s function: %s line: %d "fmt, 
    __FILE__, __FUNCTION__, __LINE__, ##args)

int main(void){
    int a = 100;
    int b = 200;
    char *s = "hello world";
    DEBUG("a = %d b = %dn", a, b);
    DEBUG("a = %x b = %xn", a, b);
    DEBUG("s = %sn", s);
    
    return 0;
}

「总结:」

fmt必须是一个字符串,不能使用指针,只有这样才可以实现字符串的功能。

06. 对调试语句进行分级审查

即使定义了调试的宏,在工程足够大的情况下,也会导致在打开宏开关的时候在终端出现大量的信息。而无法区分哪些是有用的。

这个时候就要加入分级检查机制,可以定义不同的调试级别,这样就可以对不同重要程序和不同的模块进行区分,需要调试哪一个模块就可以打开那一个模块的调试级别。

一般可以利用配置文件的方式显示,其实Linux内核也是这么做的,它把调试的等级分成了7个不同重要程度的级别,只有设定某个级别可以显示,对应的调试信息才会打印到终端上。

可以写出一下配置文件

[debug]
debug_level=XXX_MODULE

解析配置文件使用标准的字符串操作库函数就可以获取XXX_MODULE这个数值。

int show_debug(int level){
    if (level == XXX_MODULE)
    {
        #define DEBUG(fmt, args...)             
        printf("file:%s function: %s line: %d "fmt, 
        __FILE__, __FUNCTION__, __LINE__, ##args)       

    }
    else if (...)
    {
        ....
    }
}

07. 条件编译调试语句

在实际的开发中,一般会维护两种源程序,一种是带有调试语句的调试版本程序,另外一种是不带有调试语句的发布版本程序。

然后根据不同的条件编译选项,编译出不同的调试版本和发布版本的程序。

文章来源于:电子产品世界    原文链接

本站所有转载文章系出于传递更多信息之目的,且明确注明来源,不希望被转载的媒体或个人可与我们联系,我们将立即进行删除处理。

我们与500+贴片厂合作,完美满足客户的定制需求。为品牌提供定制化的推广方案、专属产品特色页,多渠道推广,SEM/SEO精准营销以及与公众号的联合推广...详细>>

利用葫芦芯平台的卓越技术服务和新产品推广能力,原厂代理能轻松打入消费物联网(IOT)、信息与通信(ICT)、汽车及新能源汽车、工业自动化及工业物联网、装备及功率电子...详细>>

充分利用其强大的电子元器件采购流量,创新性地为这些物料提供了一个全新的窗口。我们的高效数字营销技术,不仅可以助你轻松识别与连接到需求方,更能够极大地提高“闲置物料”的处理能力,通过葫芦芯平台...详细>>

我们的目标很明确:构建一个全方位的半导体产业生态系统。成为一家全球领先的半导体互联网生态公司。目前,我们已成功打造了智能汽车、智能家居、大健康医疗、机器人和材料等五大生态领域。更为重要的是...详细>>

我们深知加工与定制类服务商的价值和重要性,因此,我们倾力为您提供最顶尖的营销资源。在我们的平台上,您可以直接接触到100万的研发工程师和采购工程师,以及10万的活跃客户群体...详细>>

凭借我们强大的专业流量和尖端的互联网数字营销技术,我们承诺为原厂提供免费的产品资料推广服务。无论是最新的资讯、技术动态还是创新产品,都可以通过我们的平台迅速传达给目标客户...详细>>

我们不止于将线索转化为潜在客户。葫芦芯平台致力于形成业务闭环,从引流、宣传到最终销售,全程跟进,确保每一个potential lead都得到妥善处理,从而大幅提高转化率。不仅如此...详细>>