YM

Back

我的一个项目从 GCC 工具链切换到 clang 工具链编译时,发现原本的日志输出代码有些无法编译通过,是字符串格式化说明符与数据类型不匹配导致的。举个例子:

#include <stdio.h>
#include <stdint.h>

int main() {
    uint32_t a = 0;
    printf("1. %u\n", a);
    printf("2. %ld\n", a);
    return 0;
}

// 使用 GCC 编译时,会在 printf 1 处报错 (报错信息是另外的 ESP IDF 项目编译信息复制过来的,部分内容可能会不一样):
// error: format '%u' expects argument of type 'unsigned int', but argument 6 has type 'uint32_t' {aka 'long unsigned int'} [-Werror=format=]

// 使用 clang 编译时,会在 printf 2 处报错:
// error: format specifies type 'long' but the argument has type 'uint32_t' (aka 'unsigned int') [-Werror,-Wformat]
c

在我这个项目中,uint32_t 在 GCC 下被解释为 long unsigned int,而在 clang 下被解释为 unsigned int。这种情况下,如果使用 "%ld" 来格式化输出 uint32_t 类型的变量,就会被 clang 编译器警告。

当然,不可能更换编译器就要将所有输出都修改一遍,所以我们可以使用 inttypes.h 头文件中定义的格式说明符来格式化输出。下面是一些常用的数据类型及其格式说明符 (包含了 size_t):

数据类型格式说明符数据类型说明
int8_tPRId8有符号 8 位整数
uint8_tPRIu8无符号 8 位整数
int_least8_tPRIdLEAST8至少 8 位的有符号整数
uint_least8_tPRIuLEAST8至少 8 位的无符号整数
int_fast8_tPRIdFAST8至少 8 为的最快有符号整数类型
uint_fast8_tPRIuFAST8至少 8 为的最快无符号整数类型
int16_tPRId16有符号 16 位整数
uint16_tPRIu16无符号 16 位整数
int_least16_tPRIdLEAST16至少 16 位的有符号整数
uint_least16_tPRIuLEAST16至少 16 位的无符号整数
int_fast16_tPRIdFAST16至少 16 位的最快有符号整数类型
uint_fast16_tPRIuFAST16至少 16 位的最快无符号整数类型
int32_tPRId32有符号 32 位整数
uint32_tPRIu32无符号 32 位整数
int_least32_tPRIdLEAST32至少 32 位的有符号整数
uint_least32_tPRIuLEAST32至少 32 位的无符号整数
int_fast32_tPRIdFAST32至少 32 位的最快有符号整数类型
uint_fast32_tPRIuFAST32至少 32 位的最快无符号整数类型
int64_tPRId64有符号 64 位整数
uint64_tPRIu64无符号 64 位整数
int_least64_tPRIdLEAST64至少 64 位的有符号整数
uint_least64_tPRIuLEAST64至少 64 位的无符号整数
int_fast64_tPRIdFAST64至少 64 位的最快有符号整数类型
uint_fast64_tPRIuFAST64至少 64 位的最快无符号整数类型
intmax_tPRIdMAX有符号最大整数
uintmax_tPRIuMAX无符号最大整数
intptr_tPRIdPTR有符号的整数类型,其宽度足以容纳任何指针类型,用于将指针转换为整数,并在需要整数运算的情况下进行操作
uintptr_tPRIuPTR无符号的整数类型,其宽度足以容纳任何指针类型,用于将指针转换为整数,并在需要整数运算的情况下进行操作
ptrdiff_tPRIdPTR是有符号整数类型,用于存储两个指针之间的差值
size_t"zu"无符号整数类型,用于存储对象的大小,其宽度足以容纳对象的大小

将上面的代码改成这样:

#include <stdio.h>
#include <inttypes.h>
#include <stdint.h>

int main() {
    uint32_t a = 0;
    printf("1. %" PRIu32 "\n", a);
    return 0;
}
c

这样就可以在不同编译器下都能正确输出 uint32_t 类型的变量了。

如果还要了解更多,可以点进你项目中的 inttypes.h 头文件中查看更多的格式说明符。

stdint 常用数据类型及其格式说明符
https://yanming.link/blog/commonly-used-stdint-data-types-and-their-format-specifiers
Author YM
Published at December 4, 2024