程序开发可能会遇到要将收到的数据包(十六进制数)转换为字符串,可以使用自定义函数在不使用字符串库函数的情况下实现转换,可以用于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小时内删除。
作 者 | 郭志龙
编 辑 | 郭志龙
校 对 | 郭志龙