Проблема с std::string

269
22 января 2018, 08:19

Не могу понять, как сложить строки?

   std::string to_string( const char* format, ... ) {
        va_list args;
        va_start( args, format );
        const std::size_t len = _vscprintf( format, args ) + 1;
        std::string str( len, '\0' );
        vsprintf_s( &str[0], len, format, args );
        str.resize( len );
        return ( str );
    }
    int main() {
        std::string num( "num = " );
        for ( size_t i = 0; i < 10; i++ ) {
            //num += to_string( "%d", i ).c_str(); так оно работает
            num += to_string( "%d", i ); // num = 0 и все
        }
        printf_s( "%s\n", num.c_str() );
        return 0;
    }
Answer 1

У вас [почти] все работает прекрасно, но именно так как вы и написали. В конце вашей функции to_string вы делаете

str.resize( len );

где len включает и нулевой терминатор тоже. То есть из вашей функции вы возвращаете строки

"0\0"
"1\0"
"2\0"
"3\0"
...

и т.д. А потом вы эти строки конкатенируете в одну и получаете

0 \0 1 \0 2 \0 3 \0 ...

Все работает, как и должно работать. Строки std::string не являются нуль-терминированными и в их составе символ '\0' является обыкновенным символом.

Однако при попытке печати такой последовательности через printf, разумеется, печать идет только до первого '\0'. Напечатайте ваш результат через

std::cout << num << std::endl; 

и вы увидите, что все элементарные строки в составе вашей строки присутствуют. То есть все у вас прекрасно соединяется, только вы зачем-то напихали внутрь вашей строки ненужных нулей.

Память для каждого нуля внутри вашей функции изначально выделять нужно (ибо вы пользуетесь vsprintf_s), но перед возвращением результирующего значения этот нуль из строки надо убрать, т.е. в вашем случае сделать

str.resize( len - 1 );

Также не забывайте делать va_end и перезапускать va_list (даже если майкрософтовские примеры иногда "забывают" это делать).

(Максимально сохраняя ваш подход)

std::string to_string( const char* format, ... ) {
    va_list args;
    va_start( args, format );
    std::size_t len = _vscprintf( format, args );
    va_end( args );
    std::string str( len + 1, '\0' );
    va_start( args, format );
    vsprintf_s( &str[0], len + 1, format, args );
    va_end( args );
    str.resize( len );
    return str;
}

Функциональность, которую вы пытаетесь реализовать (раз уж вы хотите сделать это именно так), уже давно можно реализовать стандартными средствами через vsnprintf, без привлечения нестандартных майкрософтовских средств

std::string to_string( const char* format, ... ) {
    va_list args;
    va_start( args, format );
    std::size_t len = std::vsnprintf( nullptr, 0, format, args );
    va_end( args );
    std::string str( len + 1, '\0' );
    va_start( args, format );
    std::vsnprintf( &str[0], len + 1, format, args );
    va_end( args );
    str.resize( len );
    return str;
}
Answer 2

Откровенно говоря - это какое-то извращение, в С++ так работать... Но если очень уж хочется, то я бы делал примерно так:

string to_string( const char* format, ... )
{
    va_list args, copy;
    va_start(args, format);
    va_copy(copy,args);
    char * buf = new char[vsnprintf(0,0,format,args)+1];
    vsnprintf(buf, len+1, format, copy );
    va_end(args);
    va_end(copy);
    string str(buf);
    delete[] buf;
    cout << str << endl;
    return str;
}
int main()
{
    string num( "num = " );
    for ( size_t i = 0; i < 10; i++ ) {
        num += to_string( "%d", i ); // num = 0 и все
    }
    cout << num << endl;
}

А вообще - ну лучше уж стандартной to_string обойтись. Или, для сложного форматирования - stringstream. А так получается - какое-то натягивание пиджака на осьминога. Уж лучше тогда средствами C изначально пользоваться, не привлекая никакие string.

Answer 3

Так работает*

template<typename ... Args>
__inline std::string to_string( const char* format, Args ... args ) {
    int len = _scprintf( format, args ... );
    std::string str( len + 1, '\0' );
    sprintf_s( &str[0], len + 1, format, args ... );
    str.resize( len );
    return ( str );
}
READ ALSO
Смысл this в C++ / self в Python

Смысл this в C++ / self в Python

В чем суть self в ООП языка Python и this в C++ (лучше в примерах)?

259
Подключение Qt к &ldquo;Empty project&rdquo; в VS15

Подключение Qt к “Empty project” в VS15

Проект запускается, но есть проблемы:

259
Выдача содержимого каталога

Выдача содержимого каталога

Как написать код, который выдает содержимое указанного каталога подобно команде ls??

249