sprintf如何与CString和std :: string一起使用

时间:2014-02-26 09:20:46

标签: c++ visual-c++ visual-studio-2008 mfc cstring

CString s = "test";
std::string ss = "test";

char z[100];
sprintf(z, "%s", ss.c_str()); // z = "test"  : OK

char z2[100];
sprintf(z2, "%s", ss); // z2 = "(null)" : OK. undefined behavior is expected

char z3[100];
sprintf(z3, "%s", s); // z3 = "test" : How is this possible ?!

有人可以解释CStringsprintf的合作方式吗?

2 个答案:

答案 0 :(得分:7)

它的工作原理是因为CString类的第一个元素是一个指向char数组的指针。实际上,CString中唯一的字段是指向字符串数组的指针。这个类使用一些技巧来隐藏内部数据(如字符串长度,保留缓冲区大小等),方法是分配一个大缓冲区,然后将唯一的类指针指向char数组,以获取那些内部数据字段,它通过已知的方式移动此指针偏移量。

你应该做的是调用s.GetBuffer(0);或(LPCTSTR)s;但用它作为

sprintf(z2, "%s", ss);

被允许作为MFC创建者的设计,当然它可以在Windows下在其他可能崩溃的平台上运行。

[评论后编辑]

如果不使用像(LPCTSTR)s这样的c样式转换,你的代码会更安全,你将使用c ++ cast:static_cast<LPCTSTR>(s);。但是很快你就会发现你的代码在所有这些static_cast-s中都很难看,特别是如果你的sprintf-s有很多参数的话。这是我记忆(在我看来)的设计,c ++风格的演员表旨在让你重新考虑你的设计,根本不使用演员表。在你的情况下,你应该使用std :: wstringstream而不是使用sprintf(假设你使用UNICODE构建):

#include<sstream>

std::wostream & operator<< (std::wostream &out, CString const &s) {
  out << s.GetString();
  return out;
}

int main(){
  CString s = _T("test");
  std::wstringstream ss;
  ss << s;  // no cast required, no UB here
  std::wcout << ss.str();
  return 0;
}

答案 1 :(得分:6)

CString的这种行为似乎正式支持Microsoft(它依赖于CString实施细节,它们似乎可以在像你引用的那个案例,但将来可能会改变。)

请注意MSDN documentation of CString PCXSTR cast operator读取:

// If the prototype isn't known or is a va_arg prototype, 
// you must invoke the cast operator explicitly. For example, 
// the va_arg part of a call to swprintf_s() needs the cast:

swprintf_s(sz, 1024, L"I think that %s!\n", (PCWSTR)strSports);

实际上演员阵容很糟糕,因为它是一个C风格的演员阵容。我会使用C ++风格的强化 static_cast<PCWSTR>(string) 或仅使用 CString::GetString() 方法。

另一个MSDN documentation page读取(强调我的):

  

在变量参数函数中使用CString对象

     

某些C函数采用可变数量的参数。值得注意的   示例是printf_s。因为这种功能的方式   声明,编译器无法确定参数的类型和   无法确定要对每个操作执行哪个转换操作   论点。因此,传递时必须使用显式类型转换   函数的CString对象,其中是变量的   参数 即可。要在变量参数函数中使用CString对象,   显式地将CString强制转换为LPCTSTR字符串,如图所示   以下示例。

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

同样,我更喜欢 C ++ - 样式 static_cast<PCTSTR>到MSDN文档中使用的C样式转换。或者致电CString::GetString()会很好。


另请参阅此博文:Big Brother helps you

另一个thread on StackOverflow in which this issue is discussed