我正在尝试重新编码printf的一部分。
setlocale(LC_ALL, "en_US.UTF-8");
int ret = printf("%S\n", "我是一只猫。");
printf("Printf returned %d\n", ret);
如果格式为%s ,则printf会写入宽字符并返回19.
如果格式为%S ,则printf返回-1,因为参数不是宽字符串(“之前没有 L )
在我自己的printf实现中,如何确定参数中传递的字符串是否宽,所以如果不是,我可以返回-1?
修改
答案 0 :(得分:6)
基本上,你不能。传递%S
不是宽字符串的内容是未定义的行为,任何事情都可能发生,包括从你的鼻子飞出来的dæmons。你很幸运printf
抓住了它,可能它会在被解释为"我是一只猫。"
数组时检测到wchar_t
的内容并非都是有效的代码点(如果发生这种情况,errno
} EILSEQ
)设置为printf
。
答案 1 :(得分:2)
在我自己的printf实现中,如何确定参数中传递的字符串是否宽,所以如果不是,我可以返回-1?
你做不到。 %S
格式说明符在printf(3)中记录为
(不是在C99或C11中,而是在SUSv2,SUSv3和SUSv4中。)同义词 为
%ls
。不要使用。
因此您可能不应该使用它(因为它在C11标准中不是,但在SUSv4中)。如果您确实将它用于您自己的printf
,那么 promise 就是相应的实际参数 是一个宽字符串。
但是,如果您的C编译器是最近的GCC,请在您的format
声明中使用适当的printf
function attribute(它是GCC扩展名) (或同样)功能。这会向用户发出警告,告知用户函数的错误类型参数。你甚至可以通过定义你自己的函数属性来自定义GCC(例如使用MELT),这将在编译时启用额外的类型检查,因此在给定指针的情况下没有可移植的方法来检查在运行时如果它是指向字符串或其他东西的指针(如整数数组)。
在运行时,您的printf
将使用stdarg(3)工具,因此必须“解释”格式字符串以适当处理各种格式说明符。如果没有编译器支持(在GCC中使用__attribute__((format(printf,1,2)))
(也是supported by Clang),或者使用您自己的函数属性),则无法对可变参数函数进行任何编译时类型检查。并且类型信息在运行时在C中被擦除。
另请参阅free software的C standard library实现中printf
类似函数的现有实现。 stdio/vfprintf.c
的MUSL libc文件非常易读。
另外,GNU libunistring有一些elementary string checks functions例如u16_check
检查16位整数的数组(给定其大小)是否是有效的UTF16字符串。请注意,UTF8中的"我是一只猫。"
不是零双倍字节或零宽度终止的UTF16字符串(因此简单地将其长度计算为wchar_t*
宽字符串为undefined behavior,因为buffer overflow!),甚至可能没有宽字符串所需的对齐方式。