C 格式化占位符与其对应参数的类型应一致
10.6.7 ID_inconsistentFormatArgType
C 格式化占位符与其对应参数的类型应一致,否则会导致标准未定义的行为。
示例:
size_t n;
ptrdiff_t d;
....
printf("%u", n); // Non-compliant
printf("%d", d); // Non-compliant
printf("%lu", n); // Non-compliant
printf("%lld", d); // Non-compliant
size_t、ptrdiff_t 等类型是由实现定义的,标准没有规定其是否一定对应 unsigned、long 或 long long 等类型,而 %u、%d、%lu、%lld 只对应 unsigned、int、unsigned long、long long 等类型,所以示例代码都是不合理的。
应使 n 对应 %zu,d 对应 %td:
printf("%zu", n); // Compliant
printf("%td", d); // Compliant
对于 stdint.h 中定义的类型,应使用 inttypes.h 中定义的占位符:
int32_t i;
uint64_t u;
....
printf("%d", i); // Unportable
printf("%lu", u); // Unportable
printf("%" PRId32, i); // OK
printf("%" PRIu64, u); // OK
int32_t、uint64_t 并不一定对应 int、unsigned long,不应将 %d、%lu 等占位符在代码中写死,PRId32 和 PRIu64 是 inttypes.h 中定义的宏,可解决移植相关的问题。
又如:
void foo(const string& msg) {
printf("%s\n", msg); // Non-compliant
}
例中 %s 要求对应 char* 型指针,但 msg 是 string 型对象,造成栈读取错误,应改为:
void foo(const string& msg) {
printf("%s\n", msg.c_str()); // Compliant
}
由于可变参数列表自身的局限,很难在编译时发现这种问题,有些编译器会检查 printf、sprintf 等标准函数,但无法检查自定义函数,建议在 C++ 代码中禁用可变参数列表和 C 风格的格式化函数。
相关
依据
ISO/IEC 9899:1999 7.19.6.1(9)-undefined
ISO/IEC 9899:2011 7.21.6.1(9)-undefined