如何在C语言中以两位十六进制值打印无符号字符?

22

我试图将一个无符号字符值作为2位十六进制值打印出来,但总是得到4位十六进制值的结果,不确定我的代码有什么问题。

// unsigned char declaration
unsigned char status = 0x00;
// printing out the value 
printf("status = (0x%02X)\n\r", (status |= 0xC0));

我希望得到一个两位数的十六进制结果,如0xC0,但我总是得到0xC0FF

同时,当我尝试使用%bu格式标识符以无符号字符形式打印相同的变量(状态)时,输出为255

如何只输出两个十六进制字符?


如果输出为0xFFC0,那么有更多的解释方式(尽管这可能涉及到编译器错误领域)。如果将|=赋值移动到printf()之前的单独语句中会发生什么?如果结果改变了,那么你可能需要处理一个编译器错误。你在哪个平台(操作系统)上使用哪个编译器的哪个版本? - Jonathan Leffler
IDE版本: µVision V4.02 版权所有 (c) Keil Elektronik GmbH / Keil Software,Inc. 1995 - 2009工具版本号: 工具链:PK51 Prof. Developers Kit 版本:9.01 工具链路径:C:\Software\Keil\C51\BIN
C编译器:C51.Exe V9.01 汇编器:A51.Exe V8.02 链接器/定位器:BL51.Exe V6.22 库管理员:LIB51.Exe V4.24 十六进制转换器:OH51.Exe V2.6 CPU DLL:S8051.DLL V3.72 对话框DLL:DP51.DLL V2.59
- Asad Waheed
当我运行包含你展示的代码的小程序时,输出结果是status = (0xC0)。你确定那段代码就是你程序中完全相同的吗?请编写一个小的自包含程序(包含#include <stdio.h>main()函数的完整定义),并将其与你的输出一起展示给我们。请复制粘贴它们,不要重新输入。 - Keith Thompson
5
据我所了解,Keil C 生成针对8051(一种嵌入式系统中的小型CPU)的代码,但我认为它并没有完全符合C标准。您的程序 应该 正常工作。我已经添加了一个“keil”标签,或许能吸引到更了解您使用的编译器的人。 - Keith Thompson
开始感觉编译器有问题了。你尝试过写这个代码吗:unsigned char status = 0x00; status |= 0xC0; printf("0x%02X\r\n", status);?还有这个:int st_pr; unsigned char status = 0x00; status |= 0xC0; st_pr = status & 0xFF; printf("0x%02X\r\n", st_pr);?如果你把格式改成%d呢?你有包含<stdio.h>吗? - Jonathan Leffler
显示剩余3条评论
4个回答

37
据我所知,Keil C 编译器 不完全符合C标准。因此,它可能不会完全遵循像将 char 值传递给变参函数这样的标准促升规则;在一个8位CPU上,不自动扩展8位值到16位或更高位是有性能优势的。
作为解决方法,您可以在将参数传递给 printf 之前显式截断高位(即将高位置0)。请尝试以下内容:
#include <stdio.h>

int main(void) {
    unsigned char status = 0x00;
    status |= 0xC0;

    printf("status = 0x%02X\n", (unsigned int)(status & 0xFF));
    return 0;
}

使用 0xFF 进行按位与操作可以清除除最低8位以外的所有位;转换为 unsigned int 可能不是必需的,但它保证了参数实际上是 printf 所期望的类型,并且具有 "%02X" 格式。

您还应查阅您的实现文档,了解有关类型促进和 printf 的任何非标准行为。


感谢这个解决方案的临时替代,它已经正常工作了。 - Asad Waheed
2
& 0xFF 应该是不必要的,除非编译器存在更严重的问题,无法将 char 参数隐式转换为 int。0xC0FF 的输出表明它实际上没有进行转换,并从堆栈中的垃圾或寄存器中剩余的其他 8 位中获取。但是,对于你的好发现还是要点个赞。 - Jim Balter

8

您正在向预期为int的格式字符串发送char。printf函数会从堆栈中抓取另一个字节来填充它。尝试:

 printf("%02X",(int)(status|0xC0));

你能告诉我如何将它打印为十六进制(它应该是无符号字符)吗? - Asad Waheed
1
不正确。由于printf是一个可变参数函数,类型为charunsigned char的参数会被提升为int(或在某些奇特的系统上提升为unsigned int)。而"%02X"期望的是unsigned int,而不是int - Keith Thompson
1
unsigned char 会被提升为 int,因为 printf() 是一个可变参数函数(假设已经包含了 <stdio.h>)。如果没有包含该头文件,则(a)应该包含它,(b)你没有原型在作用域内,所以 unsigned char 仍然会被提升为 int - Jonathan Leffler
1
结果证明我是有点意外地大部分正确的。:) 这个错误确实像是调用者/被调用者不匹配。 - AShelly

5

从所有的答案来看,我认为我们可能还缺少另一种解决方法。

const unsigned char chararr[]="abceXYZ";
for (int i=0; i< 7; ++i) {
    printf("%#04X %d %c\n", chararr[i], chararr[i], chararr[i]);
}
0X61 97 a
0X62 98 b
0X63 99 c
0X65 101 e
0X58 88 X
0X59 89 Y
0X5A 90 Z

如果您使用%#04x小写x,则输出将带有0x小写x前缀。#号告诉函数打印0x。 04指示要输出的数字位数,如果输入为“0x0a”,则会打印此内容,没有04则会打印“0xa”。
在我的戴尔工作站上,输出与问题所期望的相同。除非...
unsigned char status = 0x00;
printf("status = (0x%02X)\n\r", (status |= 0xC0));
// output
//status = (0xC0)
// is exactly expected by the original question. 

更好的说明可以通过示例来展示:
 37    printf("status = (%#02x)\n", (status |= 0xC0));
 38    printf("status = (%#04x)\n", (status |= 0xC0));
 39    printf("status = (%#04x)\n", 0x0f);
 40    printf("status = (%#02x)\n", 0x0f);
status = (0xc0)
status = (0xc0)
status = (0x0f)
status = (0xf)

1
惊人的代码,可以在一个屏幕上获取所有内容,干净而精确。这对我很有效。 - Florida

1
将其转换为无符号字符:
printf("status = (0x%02X)\n\r", (unsigned char)(status |= 0xC0));

它必须工作在这里检查... 顺便问一下,你用哪个编译器? - Tutankhamen
如果确实有所不同,那只是因为它避免了一些编译器错误。请注意,您的链接使用OP的代码给出了相同的结果。(您应该首先尝试这个。) - Jim Balter
IDE版本: µVision V4.02 版权所有 (c) Keil Elektronik GmbH / Keil Software,Inc. 1995 - 2009许可证信息:工具版本号: 工具链:PK51 Prof. Developers Kit 版本:9.01 工具链路径:C:\Software\Keil\C51\BIN
C编译器:C51.Exe V9.01 汇编程序:A51.Exe V8.02 链接器/定位器:BL51.Exe V6.22 图书管理员:LIB51.Exe V4.24 十六进制转换器:OH51.Exe V2.6 CPU DLL:S8051.DLL V3.72 对话框DLL:DP51.DLL V2.59
- Asad Waheed

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接