【问题标题】:__int64 to CString returns wrong values - C++ MFC__int64 到 CString 返回错误值 - C++ MFC
【发布时间】:2019-02-28 21:07:31
【问题描述】:

我想将 __int64 变量转换为 CString。代码就是这样的

__int64 i64TotalGB;
CString totalSpace;
i64TotalGB = 150;
printf("disk space: %I64d GB\n", i64TotalGB);
totalSpace.Format(_T("%I64d", i64TotalGB));
printf("totalSpace contains: %s", totalSpace);

第一个 printf 打印

"disk space: 150GB"

这是正确的,但是第二个 printf 会随机打印高数字,例如

"totalSpace contains: 298070026817519929"

我也尝试使用 INT64 变量而不是 __int64 变量,但结果是一样的。这可能是什么原因?

【问题讨论】:

  • 一般建议:不要再假装你的目标是 Win9x。你不是。使用CStringW 代替CString,将_T 和类似的宏替换为L 前缀,从此过上幸福的生活。换句话说:CStringW totalSpace; totalSpace.Format(L"%I64d", i64TotalGB); wprintf(L"totalSpace contains: %s", totalSpace.GetString());.

标签: c++ mfc printf c-strings int64


【解决方案1】:

这里:

totalSpace.Format(_T("%I64d", i64TotalGB));

您将i64TotalGB 作为参数传递给_T() macro,而不是作为第二个参数传递给Format()

试试这个:

totalSpace.Format(_T("%I64d"), i64TotalGB);

话虽如此,由于 MS 在字符编码方面的混乱 (ha),在这里使用 _T 是不正确的,因为 CString 是由 TCHAR 而不是 _TCHAR 组成的。所以考虑到这一点,不妨使用TEXT() 而不是T(),因为它依赖于UNICODE 而不是_UNICODE

totalSpace.Format(TEXT("%I64d"), i64TotalGB);

此外,这一行是错误的,因为它试图将 ATL CString 作为 char*(又名 C 样式字符串)传递:

printf("totalSpace contains: %s", totalSpace);

编译器对此给出警告:

warning C4477: 'printf' : format string '%s' requires an argument of type 'char *', but variadic argument 1 has type 'ATL::CString'

虽然CString 的结构实际上与传递它的方式兼容,但这仍然是正式的未定义行为。使用CString::GetString() 进行防范:

printf("totalSpace contains: %ls", totalSpace.GetString());

注意%ls 在我的配置下totalSpace.GetString() 返回了const wchar_t*。但是,作为"printf does not currently support output into a UNICODE stream.",此行的正确版本将支持您当前code page 之外的字符,以下列方式调用wprintf()

wprintf("totalSpace contains: %s", totalSpace.GetString());

说了这么多,这里是一个一般性的建议,不管问题背后的直接问题是什么。现在更好的做法完全不同,我引用了@IInspectable 的可敬回答,说“通用文本映射在 2 十年前是相关的”

还有什么选择?如果没有足够好的理由,请尝试明确地坚持CStringW (A Unicode character type string with CRT support)。更喜欢 L character literal 而不是古老的 data/text mappings,它取决于您的程序中是否定义了常量 _UNICODE_MBCS。相反,更好的做法是使用所有 API 和语言库调用的宽字符版本,例如 wprintf() 而不是 printf()

【讨论】:

  • @IInspectable 我现在看到它in the docs: “printf 目前不支持输出到 UNICODE 流。” -- 进行了编辑以反映。相当详细但重要到要注意恕我直言
【解决方案2】:

该错误是由于代码存在许多问题,特别是这两个问题:

  • totalSpace.Format(_T("%I64d", i64TotalGB));

    这使用了_T macro,但并不意味着它会被使用。它应该包装一个字符串文字。在代码中,它包含了第二个参数。

  • printf("totalSpace contains: %s", totalSpace);

    这假定一个 ANSI 编码的字符串,但传递一个 CString 对象,该对象可以存储 ANSI 以及 Unicode 编码的字符串。

建议的做法是完全放弃generic-text mappings,转而在整个1中使用Unicode(即Windows 上的UTF-16LE)。通用文本映射在 20 年前就很重要,以便将 Win9x 代码移植到基于 Windows NT 的产品中。

这样做

  • 选择CStringW 而不是CString
  • 删除所有出现的_TTEXT_TEXT,并将它们替换为L 前缀。
  • 使用宽字符版本的 Windows API、CRT 和 C++ 标准库。

固定的代码如下所示:

__int64 i64TotalGB;
CStringW totalSpace;  // Use wide-character string
i64TotalGB = 150;
printf("disk space: %I64d GB\n", i64TotalGB);
totalSpace.Format(L"%I64d", i64TotalGB);  // Use wide-character string literal
wprintf(L"totalSpace contains: %s", totalSpace.GetString());  // Use wide-character library

在不相关的说明中,虽然在变量参数列表中传递 CString 对象代替字符指针在技术上是安全的,但这是一个实现细节,并没有正式记录在案。如果您关心正确的代码,请致电CString::GetString()


1除非有正当理由使用以char 作为其基础类型的字符编码(如 UTF-8 或 ANSI)。在这种情况下,您仍然应该使用CStringA 明确说明它。

【讨论】:

    【解决方案3】:

    试试这个

    totalSpace.Format(_T("%I64d"), i64TotalGB);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2011-01-20
      • 1970-01-01
      • 2013-03-12
      • 1970-01-01
      • 1970-01-01
      • 2011-10-24
      • 1970-01-01
      • 2015-12-28
      相关资源
      最近更新 更多