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

rmi*_*rmi 7 c++ mfc cstring visual-studio-2008 visual-c++

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 ?!
Run Code Online (Sandbox Code Playgroud)

谁能解释一下如何CString正常使用sprintf

mar*_*inj 7

它的工作原理是因为CString类的第一个元素是一个指向char数组的指针.实际上,CString中唯一的字段是指向字符串数组的指针.此类使用一些技巧通过分配一个大的缓冲器,然后在离开仅类指针隐藏内部数据(像字符串长度,保留的缓冲器大小,等等)所指向的字符数组,以获得对这些内部数据字段它通过已知的移动该指针偏移.

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

sprintf(z2, "%s", ss);
Run Code Online (Sandbox Code Playgroud)

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

[评论后编辑]

你的代码将更安全,如果不是像(LPCTSTR)s你将使用c ++ cast 的c风格的演员: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;
}
Run Code Online (Sandbox Code Playgroud)

  • 调用的正确方法是**`CString :: GetString()`**,**不是**`GetBuffer()`.另请注意_C样式的强制转换是坏的:请使用**C++ - 样式强制转换**:`static_cast <LPCTSTR>(ss)`. (2认同)

Mr.*_*C64 6

这种行为CString似乎并非正式支持微软(它依赖于实现细节CString,这似乎在制作像你举一个案件工作,但在将来可能会改变).

请注意,强制转换CString PCXSTR操作符的MSDN文档如下:

// 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);
Run Code Online (Sandbox Code Playgroud)

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

另一个MSDN文档页面读取(强调我的):

在变量参数函数中使用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);    
Run Code Online (Sandbox Code Playgroud)

同样,我更喜欢C++风格 static_cast<PCTSTR>的MSDN文档中使用的C风格的转换.或者打电话CString::GetString()会很好.


另请参阅此博客文章:老大哥帮助您.

StackOverflow上的另一个线程讨论了这个问题.