__int64 в CString возвращает неверные значения - C++ MFC

Я хочу преобразовать переменную __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, но результат тот же. Что может быть причиной этого?


person Luca Corsini    schedule 25.09.2018    source источник
comment
Общий совет: Перестаньте делать вид, что вы нацелены на Win9x. Это не так. Используйте CStringW вместо CString, замените _T и подобные макросы префиксом L и живите долго и счастливо. Другими словами: CStringW totalSpace; totalSpace.Format(L"%I64d", i64TotalGB); wprintf(L"totalSpace contains: %s", totalSpace.GetString());.   -  person IInspectable    schedule 25.09.2018


Ответы (3)


Здесь:

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

вы передаете i64TotalGB в качестве аргумента _T() макрос вместо того, чтобы передавать его в качестве второго аргумента Format().

Попробуй это:

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

Сказав это, благодаря беспорядку MS (ха) вокруг кодировок символов, использование _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 в настоящее время не поддерживает вывод в поток UNICODE.", правильная версия для этой строки, которая будет поддерживать символы за пределами вашего текущего кодовая страница — это вызов wprintf() следующим образом:

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

Сказав ВСЕ это, вот общий совет, независимо от прямой проблемы, стоящей за вопросом. Гораздо лучшая практика в настоящее время немного отличается, и я цитирую респектабельный ответ @IInspectable, в котором говорится, что «отображения общего текста были актуальны 2 десятилетия назад».

Какая альтернатива? При отсутствии достаточной причины попробуйте явно придерживаться CStringW (Строка символьного типа Unicode с поддержкой CRT). Предпочитайте L < em>символьный литерал вместо архаичного сопоставления данных и текста, которые зависят от того, определена ли в вашей программе константа _UNICODE или _MBCS. И наоборот, лучше использовать широкоформатные версии всех вызовов API и языковых библиотек, например wprintf() вместо printf().

person Geezer    schedule 25.09.2018
comment
@IInspectable Теперь я вижу это в документах: < i>printf в настоящее время не поддерживает вывод в поток UNICODE. -- внесены изменения для отражения. довольно деталь, но достаточно важная, чтобы отметить ИМХО - person Geezer; 25.09.2018

Ошибка является результатом многочисленных проблем с кодом, в частности этих двух:

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

    При этом используется _T макрос в как это не предназначено для использования. Он должен обернуть односимвольный строковый литерал. В коде он оборачивает второй аргумент.

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

    Это предполагает строку символов в кодировке ANSI, но передает объект CString, который может хранить как строки в кодировке ANSI, так и строки в кодировке Unicode.

Рекомендуемый порядок действий — удалить generic-text сопоставления в пользу использования Unicode (это UTF-16LE в Windows) повсюду1. Отображения универсального текста были актуальны два десятилетия назад, чтобы упростить перенос кода Win9x на продукты на базе Windows NT.

Сделать это

  • Выберите CStringW вместо CString.
  • Отбросьте все вхождения _T, TEXT и _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.

person IInspectable    schedule 25.09.2018

попробуй это

totalSpace.Format(_T("%I64d"), i64TotalGB);
person Harsh    schedule 25.09.2018