如何将CString传递给格式字符串%s?

16
class MyString
{
public:
    MyString(const std::wstring& s2)
    {
        s = s2;
    }

    operator LPCWSTR() const
    {
        return s.c_str();
    }
private:
    std::wstring s;
};

int _tmain(int argc, _TCHAR* argv[])
{
    MyString s = L"MyString";
    CStringW cstring = L"CString";
    wprintf(L"%s\n", (LPCWSTR)cstring); // Okay. Becase it has an operator LPCWSTR()
    wprintf(L"%s\n", cstring); // Okay, fine. But how?        
    wprintf(L"%s\n", (LPCWSTR)s); // Okay. fine.
    wprintf(L"%s\n", s); // Doesn't work. Why? It prints gabage string like "?."
    return 0;
}

如何将CString传递给格式字符串%s?

顺便提一下,MSDN说(这很奇怪)

要在可变参数函数中使用CString对象
显式地将CString转换为LPCTSTR字符串,如下所示:

CString kindOfFruit = "bananas";
int      howmany = 25;
printf( "You have %d %s\n", howmany, (LPCTSTR)kindOfFruit ); 

2
“不起作用”是什么意思?是指无法编译,没有显示预期结果吗? - MikMik
3
这与C语言完全无关。 - Puppy
@MikMik:它显示类似于 "?." 的垃圾字符串。 - Benjamin
@Puppy,你的例子很好地展示了“没想到就不存在”的谬论。我在一个将C++代码移植到嵌入式航空电子应用中的项目中遇到过这个问题很多次。 - Bob Stein
4个回答

16

CString是专门设计的,它仅包含一个指向缓冲类中字符串数据的指针。当被传递给printf时,在格式化字符串中看到"%s"时,它将被视为指针。

最初这只是偶然在printf中能够工作,但这已经成为类接口的一部分。


本文基于微软已经停用的文档,所以我不能链接到他们承诺将继续使其工作的信息。

然而,在添加更多反对票之前,请阅读分享我的旧知识的某人的博客文章:

大兄弟帮你


3
我仍然不知道如何使用printf打印它。你能给我一个例子吗? - c0dehunter

9
    wprintf(L"%s\n", (LPCWSTR)cstring); // Okay. It's been cast to a const wchar_t*.
    wprintf(L"%s\n", cstring); // UNDEFINED BEHAVIOUR
    wprintf(L"%s\n", (LPCWSTR)s); // Okay, it's a const wchar_t*.
    wprintf(L"%s\n", s); // UNDEFINED BEHAVIOUR

您只能向此函数传递%s的内容为const wchar_t*,其他任何内容都是未定义的行为。传递CString只是碰巧起作用了。
C++开发了iostream的原因是这些可变参数函数极其不安全,不应该使用。而且,由于很多原因,CString也几乎是一种罪恶,尽可能使用std::wstringcout/wcout

3
CString 的未定义行为实际上是实现特定的行为。微软支持这种用法。 - Bo Persson
2
@BoP 在 C++ 中这是未定义行为,因为在 C 的标准库 printf 文档中也是未定义行为。微软给它赋予了一定的含义(尽管我还没有看到官方文档),但代码仍然不具备可移植性。 - Johannes Schaub - litb
1
@Johannes - 确实如此,但问题是为什么CString仍然能够运行。 - Bo Persson
1
哦,wprintf 的文档在我的 manpage 中对 %s 说:“如果没有 l 修饰符:则预期 const char * 参数是指向字符类型数组的指针”看起来问问题的人必须使用 %ls - Johannes Schaub - litb
2
@BoP 我只想指出这是未定义的行为。不确定的人可能会将您的评论解读为“不,这不是未定义的行为。但它是实现定义的”。这是错误的。我知道您可能想要表达什么。 - Johannes Schaub - litb
显示剩余2条评论

4

CString的第一个成员是指针:

class CStringA
{
      char* m_pString;
};

尽管它不是char*(即使对于 ANSI CString),但它基本上是一样的。当你将CString对象传递给任何printf系列函数(包括自定义实现,如果有的话),你正在传递CString对象(位于堆栈上)。%s解析会导致它被读取为指针-在这种情况下,这是一个有效的指针(第一个字节处的数据是m_pString)。


2

一般来说,这是未定义的行为。根据这篇文章所述,Visual C++仅调用从CString到POD类型的转换来保护您 - 这是未定义行为的可允许实现。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接