如何为scanf("%zn")构建可移植的size_t有符号等效类型?
解决C语言中%zn适配size_t的可移植方案
核心问题
C17标准明确规定%zn格式符要求对应指向有符号整数类型的指针,而size_t是无符号类型,直接传入size_t*会触发编译器警告(如GCC 12的警告)。由于C语言没有类似C++std::make_signed_t的类型工具,可通过以下几种可移植方式处理:
方案1:临时有符号变量中转(最稳妥的标准C方案)
先声明足够宽度的有符号整数变量,用%zn存储读取的字符数,再安全转换为size_t(已读字符数不可能为负,转换无溢出风险)。
示例代码:
#include <stdio.h> int main(void) { int count; sscanf("abc", "%*s%zn", &count); size_t i = (size_t)count; printf("%zu", i); return 0; }
若担心int宽度不足(比如64位平台下size_t为64位、int为32位),可改用intmax_t(需包含<stdint.h>),确保有符号类型宽度不小于size_t:
#include <stdio.h> #include <stdint.h> int main(void) { intmax_t count; sscanf("abc", "%*s%zn", &count); size_t i = (size_t)count; printf("%zu", i); return 0; }
方案2:用_Generic自动匹配对应有符号类型(C11及以上)
借助C11的_Generic特性,根据size_t的实际类型自动匹配同宽度的有符号类型,无需手动判断平台:
#include <stdio.h> #include <stdint.h> #define MAKE_SIGNED_SIZE_T(_var) _Generic((_var), \ unsigned char: signed char, \ unsigned short: short, \ unsigned int: int, \ unsigned long: long, \ unsigned long long: long long, \ uintmax_t: intmax_t) int main(void) { MAKE_SIGNED_SIZE_T(size_t{0}) count; sscanf("abc", "%*s%zn", &count); size_t i = (size_t)count; printf("%zu", i); return 0; }
方案3:使用POSIX扩展类型ssize_t(仅适用于POSIX系统)
如果代码仅在Linux、macOS等POSIX兼容环境运行,可直接用ssize_t——它被定义为size_t的有符号等效类型:
#include <stdio.h> #include <sys/types.h> int main(void) { ssize_t count; sscanf("abc", "%*s%zn", &count); size_t i = (size_t)count; printf("%zu", i); return 0; }
注意:ssize_t不属于C标准,无法移植到Windows等非POSIX系统。
内容的提问来源于stack exchange,提问作者Toby Speight
相关产品推荐
相关产品推荐

