C sprintf 使用字节参数中断(Keil 编译器)
C sprintf breaks with byte parameters (Keil compiler)
我在 2 projects/platforms 中有代码 运行。它适用于一个,而不是另一个。代码是这样的:
uint8_t val = 1;
uint8_t buff[16];
sprintf(buff, "%u", val);
预期输出为“1”(gcc),但在一个编译器 (Keil) 上为 returns“511”,十六进制为 0x1FF。看起来它没有使用此编译器将 byte 提升为 int。这已得到确认,因为如果我这样做它就可以正常工作:
sprintf(buff, "%u", (int)val);
我的问题是:为什么一个编译器做我认为的 'right thing',而另一个不做?是我不正确的 expectations/assumptions、编译器设置还是其他原因?
您的假设可能是正确的,也可能是错误的。这取决于编译器的实现。所有现代(或者应该说是智能)编译器都会像你提到的那样做。但是Keil,从ver。 9.02,你需要为printf指定正确的变量长度。
这是Keil C处理各种printf函数的方式。
您需要准确指定它的长度。所有正则都是 16 位(无符号)整数,包括 %d、%x 和 %u。对 8 位使用修饰符 'b',对 32 位使用修饰符 'l'。如果你给出错误的长度,你会得到错误的数字。更糟糕的是,其余的变量都是错误的。
例如,要使用 8 位 'char',您可以使用 '%bd'(%bu 和 %bx),对于 32 位 'long' 使用 %ld、%lu 和 %lx。
char c = 0xab;
printf("My char number is correctly displayed as '0x%02bx'\n", c);
还要注意,同样,从sscanf获取数值数据,也是一样的。下面的例子是使用sscanf获取一个32位的long变量:
long var;
char *mynum = "12345678";
sscanf(mynum, "%ld", &var);
变量 var 在 sscanf
之后包含数字 12345678
。
下面显示了 Keil 的 printf 系列中使用的变量的长度。
%bd, %bx, %bu - should be used for 8-bit variables
%d, %x, %u - should be used for 16-bit variables, and
%ld, %lx, %lu - should be used for 32-bit variables
为了获得最大的可移植性,您可以使用 inttypes.h
中的这些宏:(还有其他的)
PRId8, PRIx8, PRIu8
PRId16, PRIx16, PRIu16
PRId32, PRIx32, PRIu32
正常(如我所料):
#define PRIu8 "u"
但对于本例中的 Keil 编译器:
#define PRIu8 "bu"
例如,
printf("0x%"PRIx8" %"PRIu16"\n", byteValue, wordValue);
虽然这很麻烦。我建议更友好的编译器。
令人惊奇的是,即使做了几十年,您仍然对这些东西一无所知。
我在 2 projects/platforms 中有代码 运行。它适用于一个,而不是另一个。代码是这样的:
uint8_t val = 1;
uint8_t buff[16];
sprintf(buff, "%u", val);
预期输出为“1”(gcc),但在一个编译器 (Keil) 上为 returns“511”,十六进制为 0x1FF。看起来它没有使用此编译器将 byte 提升为 int。这已得到确认,因为如果我这样做它就可以正常工作:
sprintf(buff, "%u", (int)val);
我的问题是:为什么一个编译器做我认为的 'right thing',而另一个不做?是我不正确的 expectations/assumptions、编译器设置还是其他原因?
您的假设可能是正确的,也可能是错误的。这取决于编译器的实现。所有现代(或者应该说是智能)编译器都会像你提到的那样做。但是Keil,从ver。 9.02,你需要为printf指定正确的变量长度。
这是Keil C处理各种printf函数的方式。 您需要准确指定它的长度。所有正则都是 16 位(无符号)整数,包括 %d、%x 和 %u。对 8 位使用修饰符 'b',对 32 位使用修饰符 'l'。如果你给出错误的长度,你会得到错误的数字。更糟糕的是,其余的变量都是错误的。 例如,要使用 8 位 'char',您可以使用 '%bd'(%bu 和 %bx),对于 32 位 'long' 使用 %ld、%lu 和 %lx。
char c = 0xab;
printf("My char number is correctly displayed as '0x%02bx'\n", c);
还要注意,同样,从sscanf获取数值数据,也是一样的。下面的例子是使用sscanf获取一个32位的long变量:
long var;
char *mynum = "12345678";
sscanf(mynum, "%ld", &var);
变量 var 在 sscanf
之后包含数字 12345678
。
下面显示了 Keil 的 printf 系列中使用的变量的长度。
%bd, %bx, %bu - should be used for 8-bit variables
%d, %x, %u - should be used for 16-bit variables, and
%ld, %lx, %lu - should be used for 32-bit variables
为了获得最大的可移植性,您可以使用 inttypes.h
中的这些宏:(还有其他的)
PRId8, PRIx8, PRIu8
PRId16, PRIx16, PRIu16
PRId32, PRIx32, PRIu32
正常(如我所料):
#define PRIu8 "u"
但对于本例中的 Keil 编译器:
#define PRIu8 "bu"
例如,
printf("0x%"PRIx8" %"PRIu16"\n", byteValue, wordValue);
虽然这很麻烦。我建议更友好的编译器。
令人惊奇的是,即使做了几十年,您仍然对这些东西一无所知。