- 将一个16进制常量显示为10进制字符串(通过串口打印到屏幕上),这是最常见的需求。
- 将一个表示16进制数的字符串转换为一个真正的10进制整数(从串口接收到 "1A",然后将其转换为数值 26)。
下面我将详细解释这两种场景的实现方法,并提供完整的示例代码。

(图片来源网络,侵删)
将16进制常量显示为10进制字符串
这是最简单的情况,假设你有一个16进制数,你想把它当作一个10进制数来显示,在C语言中,这非常直接。
方法:使用 printf 或 sprintf
标准C库中的 printf 函数是完成这个任务的最佳工具,你只需要使用 %d 或 %u 格式说明符即可。
%d:用于有符号十进制整数 (int)。%u:用于无符号十进制整数 (unsigned int)。
示例代码 (基于Keil C51)
假设你有一个16进制数 0x1A,它的10进制值是26。
#include <stdio.h> // 必须包含 stdio.h 才能使用 printf
void main(void)
{
// 定义一个16进制变量
unsigned char hex_value = 0x1A; // 0x1A 等于 26
// 方法1:直接使用 printf 打印
// %u 表示以无符号十进制形式输出
printf("直接打印: hex_value = %u\n", hex_value);
// 方法2:使用 sprintf 将结果存入字符串
// 这在单片机中非常有用,比如要通过串口发送数据
char output_string[20]; // 定义一个足够大的字符数组来存放结果
sprintf(output_string, "通过sprintf: hex_value = %u", hex_value);
// 在实际应用中,你可能需要将 output_string 通过串口发送出去
// UART_Send_String(output_string);
while(1)
{
// 主循环
}
}
代码解析
#include <stdio.h>:包含了标准输入输出函数库,printf和sprintf都在其中。unsigned char hex_value = 0x1A;:我们定义了一个变量hex_value并用16进制0x1A为其赋值,在C语言中,0x前缀表示这是一个16进制数,编译器在处理这个赋值时,会自动将0x1A转换为其内部的二进制表示(即十进制的26)。printf("...", hex_value);:printf函数会读取hex_value的实际数值(26),然后根据%u格式说明符,将其格式化为 "26" 这个字符串并打印出来。sprintf(output_string, "...", hex_value);:这个函数和printf类似,但它不是打印到屏幕,而是将格式化后的字符串写入到第一个参数指向的字符数组中,这在嵌入式系统中非常常见,因为单片机通常没有屏幕,但可以通过串口将字符串发送到电脑上显示。
适用于单片机的 printf 重定向
在标准的单片机开发环境中(如Keil C51),printf 默认可能不会输出到串口,你需要自己实现一个底层输出函数(通常是 fputc),并将其重定向到你的串口发送函数。

(图片来源网络,侵删)
示例:重定向 printf 到串口
#include <stdio.h>
// 假设这是你的串口发送一个字符的函数
void UART_SendChar(char ch)
{
// 这里实现你的UART发送单个字节的代码
// SBUF = ch;
// while(!TI); TI = 0;
}
// 重定向 fputc 函数到串口
int fputc(int ch, FILE *f)
{
UART_SendChar(ch);
return ch;
}
// 现在你可以直接使用 printf 了,它会自动调用 UART_SendChar
void main(void)
{
unsigned int hex_num = 0xFF; // 16进制FF,等于10进制255
printf("The decimal value of 0xFF is: %d\n", hex_num);
while(1);
}
将16进制字符串转换为10进制整数
这种情况更复杂一些,你得到的不是一个数值,而是一个字符数组(字符串),"1A"、"FF"、"42" 等,你需要将它转换成一个真正的整数(将 "1A" 转换为 26)。
方法:手动编写转换函数
最可靠和通用的方法是手动编写一个转换函数,因为标准C库的 strtol 函数在某些单片机编译器中可能不被支持或需要额外配置。
示例代码 (手动转换函数)
这个函数可以处理大写和小写的16进制字符。

(图片来源网络,侵删)
#include <stdio.h>
/**
* @brief 将16进制字符串转换为无符号整数
* @param hex_str: 指向16进制字符串的指针 ("1A", "FF", "0x1A")
* @param result: 用于存储转换结果的指针
* @return 0: 成功, -1: 失败 (例如字符串包含非法字符)
*/
int HexStringToUInt(const char *hex_str, unsigned int *result)
{
unsigned int temp = 0;
char c;
if (hex_str == NULL || result == NULL) {
return -1;
}
// 检查是否有 "0x" 或 "0X" 前缀,如果有则跳过
if (hex_str[0] == '0' && (hex_str[1] == 'x' || hex_str[1] == 'X')) {
hex_str += 2;
}
// 遍历字符串的每个字符,直到遇到字符串结束符 '\0'
while ((c = *hex_str++) != '\0') {
// 将字符转换为大写,简化判断
if (c >= 'a' && c <= 'f') {
c -= 32; // 'a' -> 'A', 'b' -> 'B', ...
}
// 判断字符是否为有效的16进制数字
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')) {
// 将字符转换为对应的数值
if (c >= '0' && c <= '9') {
temp = temp * 16 + (c - '0');
} else { // 'A' to 'F'
temp = temp * 16 + (c - 'A' + 10);
}
} else {
// 遇到非法字符,转换失败
return -1;
}
}
*result = temp;
return 0;
}
// --- 主函数示例 ---
void main(void)
{
unsigned int decimal_value;
const char *hex_str1 = "1A";
const char *hex_str2 = "FF";
const char *hex_str3 = "0x42"; // 带前缀的
const char *hex_str4 = "G12"; // 非法字符串
// 转换 "1A"
if (HexStringToUInt(hex_str1, &decimal_value) == 0) {
// printf("'%s' in decimal is: %u\n", hex_str1, decimal_value);
// 假设你有一个串口打印函数
UART_SendString("'");
UART_SendString(hex_str1);
UART_SendString("' in decimal is: ");
UART_SendNumber(decimal_value); // 需要自己实现UART_SendNumber
UART_SendString("\n");
}
// 转换 "FF"
if (HexStringToUInt(hex_str2, &decimal_value) == 0) {
// printf("'%s' in decimal is: %u\n", hex_str2, decimal_value);
UART_SendString("'");
UART_SendString(hex_str2);
UART_SendString("' in decimal is: ");
UART_SendNumber(decimal_value);
UART_SendString("\n");
}
// 转换 "0x42"
if (HexStringToUInt(hex_str3, &decimal_value) == 0) {
// printf("'%s' in decimal is: %u\n", hex_str3, decimal_value);
UART_SendString("'");
UART_SendString(hex_str3);
UART_SendString("' in decimal is: ");
UART_SendNumber(decimal_value);
UART_SendString("\n");
}
// 尝试转换非法字符串 "G12"
if (HexStringToUInt(hex_str4, &decimal_value) != 0) {
// printf("Error: '%s' is not a valid hex string.\n", hex_str4);
UART_SendString("Error: '");
UART_SendString(hex_str4);
UART_SendString("' is not a valid hex string.\n");
}
while(1);
}
代码解析
-
函数
HexStringToUInt:- 输入: 一个字符串指针
hex_str和一个用于存储结果的整数指针result。 - 处理:
- 首先检查输入是否有效。
- 使用
while循环逐个处理字符串中的字符。 - *`temp = temp 16 + ...
**: 这是核心算法,它将当前累积的结果temp` 左移4位(相当于乘以16),然后加上当前字符对应的数值。 - 字符转换: 通过判断字符的ASCII码范围,将其转换为0-9或10-15的数值,字符
'5'的ASCII码是53,'5' - '0'(53 - 48) 结果就是5,字符'A'的ASCII码是65,'A' - 'A' + 10(65 - 65 + 10) 结果就是10。 - 错误处理: 如果遇到任何不是0-9, A-F, a-f的字符,函数立即返回-1表示失败。
- 输出: 通过指针
result将转换后的整数返回给调用者,并返回0表示成功。
- 输入: 一个字符串指针
-
主函数:
- 演示了如何调用
HexStringToUInt函数。 - 处理了多种情况:普通16进制字符串、带
0x前缀的字符串以及非法字符串。 - 注释中提到的
UART_SendNumber是一个需要你自己实现的函数,它接收一个整数,然后像场景一中的sprintf那样,将其转换为字符串并发送出去。
- 演示了如何调用
| 场景 | 目标 | 方法 | 关键函数/技术 |
|---|---|---|---|
| 场景一 | 将 16进制常量 显示为 10进制字符串 | 直接使用格式化输出 | printf, sprintf |
| 场景二 | 将 16进制字符串 转换为 10进制整数 | 手动编写解析函数 | 自定义转换函数,处理ASCII码 |
对于大多数单片机应用,场景一 最为常见,掌握 printf 的使用和重定向是基本技能,而 场景二 在处理外部输入(如串口指令、配置文件等)时非常有用,理解其背后的算法原理非常重要。
