百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 热门文章 > 正文

C语言将十六进制数据转换为字符串

bigegpt 2024-10-31 12:18 4 浏览

程序开发可能会遇到要将收到的数据包(十六进制数)转换为字符串,可以使用自定义函数在不使用字符串库函数的情况下实现转换,可以用于APP版本号、日期、写日志或者其他特殊用法。今天主要是以实际例子简单介绍不使用库函数实现十六进制数据转换为字符串以及字符串的复制。


1.十六进制数据转字符串的hex2str函数

/***********************************************************************
函数名称:hex2str
函数功能:将十六进制数转换为字符串
输入参数:
    hexdata 表示输入的十六进制数
    s 表示字符指针指向存储的结果字符串 
    length 表示输入十六进制的数据的长度 
************************************************************************/
static void hex2str(unsigned int hexdata, char* s, int length)
{
    int k;
    s[length] = 0;
    /* 一位一位取十六进制数 一个十六进制数 = 四个二进制数  
    hexdata >>= 4  每次运算完一个十六进制转字符之后右移4位二进制 */
    for (k = length - 1; k >= 0; k--, hexdata >>= 4)
    {
      /*hexdata & 0xF 是将数据的最低的四位二进制数取出 即取出最低位的十六进制数
    例如 0x91&  0x0F = bin 1001 0001 &  bin 0000 1111=bin 0000 0001=0x1 */ 


        if ((hexdata & 0xF) <= 9)/* 对十六进制中的数据进行处理 */
           {
                s[k] = (hexdata & 0xF) + '0';/*数字变成字符,只需要加上字符0的ASCLL值 */
           }
        else
            {
                s[k] = (hexdata & 0xF) + 'A' - 0x0A;/*字母变成字符,只需要加上字符A的ASCLL值
        0xB + 'A' - 0x0A = 1+'A' = 'B' 十六进制中大小写字母表示的含义相同
        0xb = 0xB*/
            }
    }
}

2.测试程序1

程序

#include <stdio.h> //加载头文件 


/***********************************************************************
函数名称:hex2str
函数功能:将十六进制数转换为字符串
输入参数:
    hexdata 表示输入的十六进制数
    s 表示字符指针指向存储的结果字符串 
    length 表示输入十六进制的数据的长度 
************************************************************************/
static void hex2str(unsigned int hexdata, char* s, int length)
{
    int k;
    s[length] = 0;
    /* 一位一位取十六进制数 一个十六进制数 = 四个二进制数  
    hexdata >>= 4  每次运算完一个十六进制转字符之后右移4位二进制 */
    for (k = length - 1; k >= 0; k--, hexdata >>= 4)
    {
      /*hexdata & 0xF 是将数据的最低的四位二进制数取出 即取出最低位的十六进制数
    例如 0x91&  0x0F = bin 1001 0001 &  bin 0000 1111=bin 0000 0001=0x1 */ 


        if ((hexdata & 0xF) <= 9)/* 对十六进制中的数据进行处理 */
           {
                s[k] = (hexdata & 0xF) + '0';/*数字变成字符,只需要加上字符0的ASCLL值 */
           }
        else
            {
                s[k] = (hexdata & 0xF) + 'A' - 0x0A;/*字母变成字符,只需要加上字符A的ASCLL值
        0xB + 'A' - 0x0A = 1+'A' = 'B' 十六进制中大小写字母表示的含义相同
        0xb = 0xB*/
            }
    }
}


int main() 
{
  unsigned int a = 0xAbCD3123;/*定义数据*/
  unsigned int b = 0x20240214;
  unsigned int c = 0x20240214U;
  char str[9]="00000000";/*存储结果*/
  char *s;
  s = &str[0];
  int len = 8;
  printf("%s\n",str);
  hex2str(a,s,len);
  printf("%s\n",str);
  hex2str(b,s,len);
  printf("%s\n",str);
  hex2str(c,s,len);
  printf("%s\n",str);  
  system("pause");/*让控制窗口停留而不是一闪而过 */
  return 0;
 }

运行结果


3.测试程序2

#include <stdio.h> //加载头文件 
#define TIME (0x20240214U) //宏定义 


/***********************************************************************
函数名称:hex2str
函数功能:将十六进制数转换为字符串
输入参数:
    hexdata 表示输入的十六进制数
    s 表示字符指针指向存储的结果字符串 
    length 表示输入十六进制的数据的长度 
************************************************************************/
static void hex2str(unsigned int hexdata, char* s, int length)
{
    int k;
    s[length] = 0;
    /* 一位一位取十六进制数 一个十六进制数 = 四个二进制数  
    hexdata >>= 4  每次运算完一个十六进制转字符之后右移4位二进制 */
    for (k = length - 1; k >= 0; k--, hexdata >>= 4)
    {
      /*hexdata & 0xF 是将数据的最低的四位二进制数取出 即取出最低位的十六进制数
    例如 0x91&  0x0F = bin 1001 0001 &  bin 0000 1111=bin 0000 0001=0x1 */ 


        if ((hexdata & 0xF) <= 9)/* 对十六进制中的数据进行处理 */
           {
                s[k] = (hexdata & 0xF) + '0';/*数字变成字符,只需要加上字符0的ASCLL值 */
           }
        else
            {
                s[k] = (hexdata & 0xF) + 'A' - 0x0A;/*字母变成字符,只需要加上字符A的ASCLL值
        0xB + 'A' - 0x0A = 1+'A' = 'B' 十六进制中大小写字母表示的含义相同
        0xb = 0xB*/
            }
    }
}
/***********************************************************************
函数名称:strc
函数功能:将两个字符串拼接 
输入参数:
    str1 表示指向字符串1的字符指针 ,结果存储在字符串1中 
    str2 表示指向字符串2的字符指针  


************************************************************************/
static void strc(char *str1,char *str2)
{


   int i=0,j=0;
   while(str1[i]!='\0')
   {
     i++;
   }
    while(str2[j]!='\0')
   {
     str1[i++]=str2[j++];
   }
   str1[i]='\0';


}


int main()
{
    unsigned int  i = 0;
    char Version_num[9]="00000000";
    char Version_numF[10]="0000F0000";
    char Version[17] = "YUNLONGV";
    char *s;
    s = &Version_num[0];
    unsigned int len = 8;
    hex2str( TIME,s, len);
    printf("%s\n",Version_num);
    for(i = 0;i<4;i++)
    {
        Version_numF[i] = Version_num[i];
    }
    for(i = 5;i<9;i++)
    {
        Version_numF[i] = Version_num[i-1];
    }
    printf("%s\n",Version_numF);
    strc(Version,Version_numF);
    printf("%s\n",Version);
    printf("\n");
    system("pause");/*让控制窗口停留而不是一闪而过 */
    return 0;


}

运行结果

4.程序改进

上述的程序在实际汽车嵌入式的单片机运行时会出现以下问题:

问题1:在程序每次被周期调度时,都会执行strc字符串复制函数,导致字符数组超出大小,溢出影响CAN通信。


问题2:定义的字符数组放在了函数内容里面,当函数执行完之后,内容会被释放,指针返回的是随机的内容。

问题3:程序定义的变量和内容太多。

解决方案:简化程序,直接对字符数组进行赋值操作,利用右移函数直接将十六进制数字转换为相应的数字。

程序

#include <stdio.h>
#define TIME (0x20240214U) //宏定义 
typedef unsigned char       uint8;
typedef unsigned long       uint32;
static void strc(char *arr1,char *arr2)
{


   int i=0,j=0;
   while(arr1[i]!='\0')
   {
     i++;
   }
    while(arr2[j]!='\0')
   {
     arr1[i++]=arr2[j++];
   }
   arr1[i]='\0';
   
}


 static void hex2str(uint32 data, char* s, int len)
{
     int i;
     s[len] = 0;
     for (i = len - 1; i >= 0; i--, data >>= 4)
     {
         if ((data & 0xf) <= 9)
            {
                 s[i] = (data & 0xf) + '0';
            }
         else
             {
                 s[i] = (data & 0xf) + 'A' - 0x0a;
             }
     }
 }
char Version[17] = "YUNLONGV";//不能放在函数内部,不然程序结束之后内存会被释放 
const uint8 * GetSoftwareVersionInformation(void)
{
   uint8  i = 0;
     char Version_num[8]="SW00000";
     char Version_numF[10]="0000F0000";
     
     char *s;
     s = &Version_num[0];
     uint8 len = 8;
     hex2str( TIME,s, len);
     printf("%s\n",Version_num);
     for(i = 0;i<4;i++)
     {
         Version_numF[i] = Version_num[i];
     }
     for(i = 5;i<9;i++)
     {
         Version_numF[i] = Version_num[i-1];
     }
     printf("%s\n",Version_numF);
     strc(Version,Version_numF);
//     printf("%s\n",Version);
    return ((const uint8 *)  Version);
}
char Version1[17] = "YUNLONGVxxxxFxxxx";
const uint8 * GetSoftwareVersionInformation1(void)
{
  uint8 i = 0;
  for(i = 0;i<8;i++)
  {
      if(i<4) 
      {
      Version1[i+8] = '0'+(TIME>>((7-i)*4)&(0x0FU));
      }
      else
      {
        Version1[i+9] = '0'+(TIME>>((7-i)*4)&(0x0FU));  
      }  
  }


  return ((const uint8 *)  Version1);
}
int main()
{
  printf("%s\n",GetSoftwareVersionInformation());
  printf("%s\n",GetSoftwareVersionInformation());//程序每调用一次就会累加一次 溢出  要让程序只执行一次 
  printf("%s\n",GetSoftwareVersionInformation1()); 
  system("pause");
  return 0;
 } 


5.程序涉及的基础知识

(1)c语言static关键字的作用

(1)static修饰全局变量:使用static修饰全局变量,该变量将变成静态全局变量,只能在该c文件中使用,从而限定了作用域。一般的全局变量是可以在整个工程文件(包含了多个*.c文件),也就是在一个c文件中定义的全局变量,需要在其他的c文件中使用时需要使用extern关键字声明。

(2)static修饰局部变量:局部变量就是在函数内部定义的变量,当使用static修饰局部变量后,该变量就变成了静态的局部变量,作用域会持续到整个程序结束。一般的局部变量在离开被定义的函数之后,内存就会被释放。

(3)static修饰函数:当使用static修饰函数时,这个函数就变成了静态函数,作用域就仅限于该c文件中,而不能被项目中其他的c文件调用,这样可以减少团队开发时使用相同的函数名定义不同的功能。

(2)system("pause")

system("pause")是将正在执行的程序暂停执行,在控制台窗口敲下任意键之后程序会继续执行,这样可以让控制台窗口不会一闪就关闭。

(3)>>=运算

/*
1.化十进制数为(注意了)对应的二进制数,对应指格式对应
2.通通右移,正数左补0,负数左补1,右边丢弃
3.化为十进制数
举例:short int a=8;a=a>>1;




1.a=0 000 1000
2.右移一位后:a= 0 000 100
3.补0:a=0 000 0100
4.化为十进制数:a=4
*/

(4)数组名

C语言中的数组名有两种含义,一是标识数组,二是代表数组的首地址,数组名的实质就是数组的首地址。


6.参考内容

[1] CSDN作者A鱼翔浅底A的文章《>> << 0xf 等用法》,文章链接为:

https://blog.csdn.net/wangdamingll/article/details/53434098

[2]CSDN作者QQ851301776的文章《C语言把十六进制数据转换为字符串》,文章链接为:

《https://blog.csdn.net/weixin_43155199/article/details/123276021》

[3] CSDN作者二十又的文章《【c语言】实现两个字符串的连接(自己定义函数)》,文章链接为:

https://blog.csdn.net/qq_62755550/article/details/122095381


本文内容来源于网络,仅供参考学习,如内容、图片有任何版权问题,请联系处理,24小时内删除。


作 者 | 郭志龙

编 辑 | 郭志龙
校 对 | 郭志龙

相关推荐

悠悠万事,吃饭为大(悠悠万事吃饭为大,什么意思)

新媒体编辑:杜岷赵蕾初审:程秀娟审核:汤小俊审签:周星...

高铁扒门事件升级版!婚宴上‘冲喜’老人团:我们抢的是社会资源

凌晨两点改方案时,突然收到婚庆团队发来的视频——胶东某酒店宴会厅,三个穿大红棉袄的中年妇女跟敢死队似的往前冲,眼瞅着就要扑到新娘的高额钻石项链上。要不是门口小伙及时阻拦,这婚礼造型团队熬了三个月的方案...

微服务架构实战:商家管理后台与sso设计,SSO客户端设计

SSO客户端设计下面通过模块merchant-security对SSO客户端安全认证部分的实现进行封装,以便各个接入SSO的客户端应用进行引用。安全认证的项目管理配置SSO客户端安全认证的项目管理使...

还在为 Spring Boot 配置类加载机制困惑?一文为你彻底解惑

在当今微服务架构盛行、项目复杂度不断攀升的开发环境下,SpringBoot作为Java后端开发的主流框架,无疑是我们手中的得力武器。然而,当我们在享受其自动配置带来的便捷时,是否曾被配置类加载...

Seata源码—6.Seata AT模式的数据源代理二

大纲1.Seata的Resource资源接口源码2.Seata数据源连接池代理的实现源码3.Client向Server发起注册RM的源码4.Client向Server注册RM时的交互源码5.数据源连接...

30分钟了解K8S(30分钟了解微积分)

微服务演进方向o面向分布式设计(Distribution):容器、微服务、API驱动的开发;o面向配置设计(Configuration):一个镜像,多个环境配置;o面向韧性设计(Resista...

SpringBoot条件化配置(@Conditional)全面解析与实战指南

一、条件化配置基础概念1.1什么是条件化配置条件化配置是Spring框架提供的一种基于特定条件来决定是否注册Bean或加载配置的机制。在SpringBoot中,这一机制通过@Conditional...

一招解决所有依赖冲突(克服依赖)

背景介绍最近遇到了这样一个问题,我们有一个jar包common-tool,作为基础工具包,被各个项目在引用。突然某一天发现日志很多报错。一看是NoSuchMethodError,意思是Dis...

你读过Mybatis的源码?说说它用到了几种设计模式

学习设计模式时,很多人都有类似的困扰——明明概念背得滚瓜烂熟,一到写代码就完全想不起来怎么用。就像学了一堆游泳技巧,却从没下过水实践,很难真正掌握。其实理解一个知识点,就像看立体模型,单角度观察总...

golang对接阿里云私有Bucket上传图片、授权访问图片

1、为什么要设置私有bucket公共读写:互联网上任何用户都可以对该Bucket内的文件进行访问,并且向该Bucket写入数据。这有可能造成您数据的外泄以及费用激增,若被人恶意写入违法信息还可...

spring中的资源的加载(spring加载原理)

最近在网上看到有人问@ContextConfiguration("classpath:/bean.xml")中除了classpath这种还有其他的写法么,看他的意思是想从本地文件...

Android资源使用(android资源文件)

Android资源管理机制在Android的开发中,需要使用到各式各样的资源,这些资源往往是一些静态资源,比如位图,颜色,布局定义,用户界面使用到的字符串,动画等。这些资源统统放在项目的res/独立子...

如何深度理解mybatis?(如何深度理解康乐服务质量管理的5个维度)

深度自定义mybatis回顾mybatis的操作的核心步骤编写核心类SqlSessionFacotryBuild进行解析配置文件深度分析解析SqlSessionFacotryBuild干的核心工作编写...

@Autowired与@Resource原理知识点详解

springIOCAOP的不多做赘述了,说下IOC:SpringIOC解决的是对象管理和对象依赖的问题,IOC容器可以理解为一个对象工厂,我们都把该对象交给工厂,工厂管理这些对象的创建以及依赖关系...

java的redis连接工具篇(java redis client)

在Java里,有不少用于连接Redis的工具,下面为你介绍一些主流的工具及其特点:JedisJedis是Redis官方推荐的Java连接工具,它提供了全面的Redis命令支持,且...