• 振南技术干货集:C语言的一些“骚操作”及其深层理解(3)


    注解目录

    第二章《c语言的一些“操作”及其深层理解》

    一、字符串的实质就是指针

    (如何将 35 转为对应的十六进制字符串”0X23”?)

    二 、转义符\

    (打入字符串内部的“奸细”。)

    三、字符串常量的连接

    (字符串常量是双面胶,你知道吗?)

    四、长字符串的拆分技巧

    (GPS 数据帧 NMEA、Shell 命令行和 AT 指令的解析,是长串拆分的典型应用。)

    五、巧取数值的各位数码

    (玩多位数码管的必有操作。)

    六、printf 的实质与使用技巧

    (自认为很了解 printf?那你试过向 3 个 UART 打印吗?或者打印到液晶屏上?)

    七、关于浮点数的传输

    (浮点只是一种假象,看清它的本质。)

    八、关于数据的直接操作

    (如何快速计算浮点的相反数,乘以-1.0?再想想。)

    九、 浮点的四舍五入与比较

    (老师说浮点不能直接判等,为什么?)

    十、的 出神入化的 for 循环

    (for 循环很熟悉了吧?OK,振南出了几道题,来试试。)

    十一、 隐藏的死循环

    (我们在明处,有时死循环在暗处。)

    十二、 看似多余的空循环

    (没用的东西?)

    十三、 独立执行体

    (这个概念 C 语言里没学过?那就对了,我经常用。)

    十四、 多用() 无坏处

    (万物皆可加括号。)

    十五、== 的反向测试

    (把==错写成=,能让你调程序调到吐血。)

    十六、 赋值操作的实质

    (让数学教授困惑半生的 C 语言赋值操作。)

    十七、 关于补码

    (摊牌了,CPU 其实不会作减法。)

    十八、 关于-1

    (-1 就是全 F,全 F 就是-1。)

    二十、字节快速位逆序

    (时间与空间的相互转化--计算机中的相对论)

    二十一、关于 volatile

    (有些东西不可优化。)

    二十二、关于变量互换

    (位操作的奇妙。)

    二十三、关于 sizeof

    (告诉你关于 sizeof 那些少人关注的问题。)

    二十四、memcpy 的效率

    (小小的函数也有大大的背景)

    二十五、[] 的本质

    (你以为[]只是数组下标?)

    二十六、# 与##( 串化与连接)

    (一个不曾出现在 C 语言教材中的知识点)

    1

    字符串的实质就是指针

    长字符串的拆分技巧


    很多时候我们需要进行长字符串的拆分。在振南的研发经历中,使用到这种操作的最典型的应用场合有三个。

    1.NMEA 协议数据的解析

    NMEA 可能很多人不太了解,但是说到GPS肯定大家都很熟悉。当我们从GPS 模块中读取定位信息的时候,数据就是遵循NMEA协议格式的。图 2.2 为一个标准的GPS数据帧。


    图 2.2 一个符合 NMEA 协议标准的 GPS 数据帧

    整个数据帧采用ASCII编码,它以 $GP作为开始,后面依次排列的是各项参数,参数之间使用,作为分隔。比如 $GPRMC为推荐定位信息,我当时就是使用这一条数据来获取经纬度信息的(当时是 Intel 杯嵌入式邀请赛需要作一个手持 GPS 跟踪器)。这条数据中N后面是纬度,E后面是经度。我们要作的就是将它们从整个数据帧(一个长字符串)中提取出来。所以,这就涉及到了所谓的“长串拆分”。

    2.Shell 命令行的命令解析

    在很多项目中,我都习惯于基于串口编写一个后台 Shell 系统,可以起到一个基本的调试作用。从而一定程度上减少修改代码和固件烧录的次数。比如,项目中如果涉及DAC电压经常的调整输出,我就会在后台中设计一个命令SetVn,以便随时灵活的操控DAC。随着项目功能的升级,后台命令也会变得开始复杂。比如 SetArg a b c d e f g h.... ,用于同时设置程序中多个关键参数的值;再比如 SetV channel n freq a,设置某通道第n个信号的输出幅值和频率。

    这些命令通过PC上的串口助手或调试终端来发送,比如超级终端、 SecureCRT 或 XShell等。程序中从串口接收到命令之后,将其放入内存的缓冲区中,其形式就是一个字符串。命令字以及后面的若干参数之间使用空格来分隔。程序要匹配命令字,并提取参数,以便执行相应的操作。所以,这也涉及长串的拆分。

    3.DTU 模块的 AT 指令解析

    AT指令其实和NMEA 是一个道理,它们都是一种通信协议格式,只不过AT指令更多使用在网络通信模块中,比如SIM800 、 ESP8266 、 HC06 蓝牙串口等。举个例子,我们想知道网络信号强度,就可以向模块发送” AT+CSQ\r\n ”,模块会返回” +CSQ:29 , 0\r\n ”。CSQ:后面的 29 就是信号强度。它们都是ASCII编码的,也就是一个字符串。我们需要将 29 从其中提取出来。当然,AT 指令也有比较复杂的,字符串会比较长,包含的参数也会比较多。所以,要想使用这些网络模块实现网络通信,就必须实现对 AT 指令的解析。

    说了这么多,都是在说长串拆分很重要。根本问题是如何实现它?很多人可能都会想到使用那个分隔字符,比如空格、逗号。然后去一个个数要提取的参数前面有几个分隔字符,然后后将相应位置上的字符组成一个新的短字符串,如图 2.3 所示


    图 2.3 通过分隔字符定位要提取的部分

    这种方法固然可行,但是略显笨拙。其实对于这种有明显分隔符的长字符串,我们可以采用“打散”或“爆炸”的思想,具体过程是这样的:将长字符串中的所有分隔符全部替换为’\0 ’,即字符串结束符。此时,长字符串就被分解成了在内存中顺序存放的若干个短字符串。如果要取出第 n 个短字符串,可以用这个函数:

    char*substr ( char*str , n )

    {

    unsigned charlen=strlen ( str );

    for (; len ﹥ 0 ; len-- ){if ( str [ len-1 ] ==ˈˈ )str [ len-1 ] =0 ;}

    for (; n ﹥ 0 ; n-- )

    {

    str+= ( strlen ( str ) +1 );

    }

    returnstr ;

    }

    很多时候我们需要一次性访问长字符串中的多个短字符串,此时振南经常会这样来作:通过一个循环,将长字符串中的所有分隔符替换为’\0 ’,在此过程中将每一个短字符串首字符的位置记录到一个数组中,代码如下:


    好,举个例子:我们要提取”abc 1000 50 off 2500 ”中的” abc ”、” 50 ”和” off ”,可以使用上面的函数来实现。

  • 相关阅读:
    谱定理等周边定理
    一种以RGB颜色登录的密码实现
    贪心算法——活动安排问题
    WPF样式
    C语言for循环高手小技巧
    【Seata】05 - Seata Saga 模式简单整理、Docker 部署 Nacos 单机(基于 Jpom)相关配置
    我的第一个react.js 的router工程
    你不得不知的MYSQL优化——索引下推
    WIFI跟WLAN是一样的吗
    Learn OpenGL 03 着色器
  • 原文地址:https://blog.csdn.net/weixin_51218153/article/details/134328368