Как sprintf работает с CString и std :: string

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 ?!

Кто-нибудь может объяснить, как CString правильно работает с sprintf?

6

Решение

Это работает, потому что первый элемент класса CString является указателем на массив символов. Фактически, единственное поле в CString — это указатель на строковый массив. Этот класс использует некоторые приемы, чтобы скрыть внутренние данные (например, длину строки, зарезервированный размер буфера и т. Д.), Выделяя один большой буфер, а затем оставляя единственный указатель класса, указывающий на массив char, чтобы добраться до этих внутренних полей данных, он смещает этот указатель на известные смещение.

Что вы должны сделать, это вызвать s.GetBuffer (0); или (LPCTSTR) s; но используя его как

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

было разрешено, так как по замыслу создателей MFC, конечно, он работает под Windows, на других платформах может произойти сбой.

[редактировать после комментариев]

ваш код будет безопаснее, если вместо C-стиля (LPCTSTR)s вы будете использовать c ++ cast: static_cast<LPCTSTR>(s);, Но очень скоро вы обнаружите, что ваш код становится уродливым со всеми этими static_cast-s, особенно если у ваших sprintf-s много параметров. Это, насколько я помню (и, на мой взгляд), дизайн, приведения в стиле c ++ предназначены для того, чтобы вы переосмыслили свой дизайн, чтобы вообще не использовать приведения. В вашем случае вместо использования sprintf вы должны использовать std :: wstringstream (при условии, что вы используете сборку 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;
}
7

Другие решения

Это поведение CString кажется не официально поддерживается Microsoft (полагается на детали реализации из CString, которые, кажется, созданы для работы в тех случаях, которые вы цитировали, но могут измениться в будущем).

Обратите внимание, что MSDN документация CString PCXSTR оператор приведения гласит:

// 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 читает (выделение мое):

Использование объектов 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> к приведению в стиле C, используемому в документации MSDN. Или звоню CString::GetString() все будет хорошо.


Смотрите также этот блог: Большой брат помогает тебе.

И другой поток на StackOverflow, в котором обсуждается эта проблема.

6

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector