C语言 printf、wprintf、%s、%S、%ls、char* 和 wchar*:编译器警告未宣布错误?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/17700797/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 06:55:36  来源:igfitidea点击:

printf, wprintf, %s, %S, %ls, char* and wchar*: Errors not announced by a compiler warning?

cmingw

提问by Antonio

I have tried the following code:

我尝试了以下代码:

wprintf(L"1 %s\n","some string"); //Good
wprintf(L"2 %s\n",L"some string"); //Not good -> print only first character of the string
printf("3 %s\n","some string"); //Good
//printf("4 %s\n",L"some string"); //Doesn't compile
printf("\n");
wprintf(L"1 %S\n","some string"); //Not good -> print some funny stuff
wprintf(L"2 %S\n",L"some string"); //Good
//printf("3 %S\n","some string"); //Doesn't compile
printf("4 %S\n",L"some string");  //Good

And I get the following output:

我得到以下输出:

1 some string
2 s
3 some string

1 g1 %s

2 some string
4 some string

So: it seems that both wprintfand printfare able to print correctly both a char* and a wchar*, but only if the exact specifier is used. If the wrong specifier is used, you might not get a compiling error (nor warning!) and end up with wrong behavior.Do you experience the same behaviour?

所以说:看来双方wprintfprintf能正确打印都一个char *和* WCHAR,但前提是准确的说明符使用。如果使用了错误的说明符,您可能不会收到编译错误(也不会发出警告!)并最终导致错误的行为。你有同样的行为吗?

Note: This was tested under Windows, compiled with MinGW and g++4.7.2 (I will check gcc later)

注意:这是在Windows下测试的,用MinGW和g++4.7.2编译(我稍后会检查gcc)

Edit: I also tried %ls (result is in the comments)

编辑:我也试过 %ls (结果在评论中)

printf("\n");
wprintf(L"1 %ls\n","some string"); //Not good -> print funny stuff
wprintf(L"2 %ls\n",L"some string"); //Good
// printf("3 %ls\n","some string"); //Doesn't compile
printf("4 %ls\n",L"some string");  //Good

采纳答案by R.. GitHub STOP HELPING ICE

I suspect GCC (mingw) has custom code to disable the checks for the wide printffunctions on Windows. This is because Microsoft's own implementation (MSVCRT) is badly wrongand has %sand %lsbackwardsfor the wide printffunctions; since GCC can't be sure whether you will be linking with MS's broken implementation or some corrected one, the least-obtrusive thing it can do is just shut off the warning.

我怀疑 GCC (mingw) 有自定义代码来禁用printf对 Windows 上的广泛功能的检查。这是因为 Microsoft 自己的实现 (MSVCRT) 是严重错误的%s并且在广泛的功能方面有%ls倒退printf;由于 GCC 无法确定您将链接到 MS 的损坏实现还是一些更正的实现,因此它可以做的最不显眼的事情就是关闭警告。

回答by Mats Petersson

The format specifers matter: "%s" says that the next string is a narrow string ("ascii" and typically 8 bits per character). "%S" means wide char string. Mixing the two will give "undefined behaviour", which includes printing garbage, just one character or nothing.

格式说明很重要:“%s”表示下一个字符串是一个窄字符串(“ascii”,通常每个字符 8 位)。“%S”表示宽字符字符串。将两者混合将产生“未定义的行为”,其中包括打印垃圾、只有一个字符或什么都没有。

One character is printed because wide chars are, for example, 16 bits wide, and the first byte is non-zero, followed by a zero byte -> end of string in narrow strings. This depends on byte-order, in a "big endian" machine, you'd get no string at all, because the first byte is zero, and the next byte contains a non-zero value.

打印一个字符是因为宽字符是例如 16 位宽,并且第一个字节是非零的,然后是一个零字节 -> 窄字符串中的字符串结尾。这取决于字节顺序,在“大端”机器中,您根本不会得到字符串,因为第一个字节为零,而下一个字节包含非零值。

回答by Steve R

At least in Visual C++: printf (and other ACSII functions): %s represents an ASCII string %S is a Unicode string wprintf (and other Unicode functions): %s is a Unicode string %S is an ASCII string

至少在 Visual C++ 中: printf(和其他 ACSII 函数):%s 表示一个 ASCII 字符串 %S 是一个 Unicode 字符串 wprintf(和其他 Unicode 函数):%s 是一个 Unicode 字符串 %S 是一个 ASCII 字符串

As far as no compiler warnings, printf uses a variable argument list, with only the first argument able to be type checked. The compiler is not designed to parse the format string and type check the parameters that match. In cases of functions like printf, that is up to the programmer

就没有编译器警告而言,printf 使用可变参数列表,只有第一个参数可以进行类型检查。编译器不是设计来解析格式字符串和类型检查匹配的参数的。对于 printf 之类的函数,这取决于程序员

回答by user3581075

For s:When used with printf functions, specifies a single-byte or multi-byte character string; when used with wprintf functions, specifies a wide-character string. Characters are displayed up to the first null character or until the precision value is reached.

对于 s:与 printf 函数一起使用时,指定单字节或多字节字符串;与 wprintf 函数一起使用时,指定宽字符串。字符一直显示到第一个空字符或达到精度值。

For S:When used with printf functions, specifies a wide-character string; when used with wprintf functions, specifies a single-byte or multi-byte character string. Characters are displayed up to the first null character or until the precision value is reached.

对于 S:当与 printf 函数一起使用时,指定一个宽字符串;与 wprintf 函数一起使用时,指定单字节或多字节字符串。字符一直显示到第一个空字符或达到精度值。

In Unix-like platform, s and S have the same meaning as windows platform.

在类 Unix 平台中,s 和 S 与 windows 平台的含义相同。

Reference: https://msdn.microsoft.com/en-us/library/hf4y5e3w.aspx

参考:https: //msdn.microsoft.com/en-us/library/hf4y5e3w.aspx

回答by 71GA

Answer A

答案 A

None of the answers above pointed out why you might not see some of your prints. This is also because here you are dealing with streams (I didn't know this)and stream has something called orientation. Let me cite something from thissource:

上面的答案都没有指出为什么您可能看不到某些印刷品。这也是因为在这里您正在处理流(我不知道这一点)并且流具有称为方向的东西。让我从这个来源引用一些东西:

Narrow and wide orientation

A newly opened stream has no orientation. The first call to any I/O function establishes the orientation.

A wide I/O function makes the stream wide-oriented, a narrow I/O function makes the stream narrow-oriented. Once set, the orientation can only be changed with freopen.

Narrow I/O functions cannot be called on a wide-oriented stream; wide I/O functions cannot be called on a narrow-oriented stream. Wide I/O functions convert between wide and multibyte characters as if by calling mbrtowcand wcrtomb. Unlike the multibyte character strings that are valid in a program, multibyte character sequences in the file may contain embedded nulls and do not have to begin or end in the initial shift state.

窄和宽的方向

新打开的流没有方向。对任何 I/O 函数的第一次调用都会确定方向。

宽 I/O 函数使流 面向宽,窄 I/O 函数使流面向窄。一旦设置,方向只能使用freopen更改。

不能在面向宽的流上调用窄 I/O 函数;不能在面向窄的流上调用宽 I/O 函数。宽 I/O 函数在宽字符和多字节字符之间转换,就像调用mbrtowcwcrtomb 一样。与程序中有效的多字节字符串不同,文件中的多字节字符序列可能包含嵌入的空值,并且不必以初始移位状态开始或结束。

So once you use printf()your orientation becomes narrow and from this point on you can't get anything out of wprintf()and you realy don't. Unless you use freeopen()which is intended to be used on files.

所以一旦你使用printf()你的方向就会变得狭窄,从这一点开始你不能得到任何东西wprintf(),你真的没有。除非您使用freeopen()which 旨在用于文件。



Answer B

答案 B

As it turns out you can use freeopen()like this:

事实证明,您可以freeopen()像这样使用:

freopen(NULL, "w", stdout);             

To make stream "not defined"again. Try this example:

再次使流“未定义”。试试这个例子:

#include <stdio.h>
#include <wchar.h>
#include <locale.h>

int main(void)
{
    // We set locale which is the same as the enviromental variable "LANG=en_US.UTF-8".
    setlocale(LC_ALL, "en_US.UTF-8");

    // We define array of wide characters. We indicate this on both sides of equal sign
    // with "wchar_t" on the left and "L" on the right.
    wchar_t y[100] = L"ο Δικαιοπολι? εν αγρω εστιν\n";

    // We print header in ASCII characters
    wprintf(L"content-type:text/html; charset:utf-8\n\n");

    // A newly opened stream has no orientation. The first call to any I/O function
    // establishes the orientation: a wide I/O function makes the stream wide-oriented,
    // a narrow I/O function makes the stream narrow-oriented. Once set, we must respect
    // this, so for the time being we are stuck with either printf() or wprintf().

    wprintf(L"%S\n", y);    // Conversion specifier %S is not standardized (!)
    wprintf(L"%ls\n", y);   // Conversion specifier %s with length modifier %l is 
                            // standardized (!)

    // At this point curent orientation of the stream is wide and this is why folowing
    // narrow function won't print anything! Whether we should use wprintf() or printf()
    // is primarily a question of how we want output to be encoded.

    printf("1\n");          // Print narrow string of characters with a narrow function
    printf("%s\n", "2");    // Print narrow string of characters with a narrow function
    printf("%ls\n",L"3");   // Print wide string of characters with a narrow function

    // Now we reset the stream to no orientation.
    freopen(NULL, "w", stdout);

    printf("4\n");          // Print narrow string of characters with a narrow function
    printf("%s\n", "5");    // Print narrow string of characters with a narrow function
    printf("%ls\n",L"6");   // Print wide string of characters with a narrow function

    return 0;
}

回答by David Foerster

%Sseems to conform to The Single Unix Specification v2and is also part of the current (2008) POSIX specification.

%S似乎符合The Single Unix Specification v2并且也是当前(2008)POSIX 规范的一部分

Equivalent C99 conforming format specifiers would be %sand %ls.

等效的符合 C99 的格式说明符是%s%ls