Проблема с выводом файла журнала - разница между строкой и c_str - C ++ - PullRequest
1 голос
/ 26 декабря 2011

Я пытаюсь завершить мой класс регистрации. Я написал это с нуля и не хочу использовать какую-либо альтернативную библиотеку. Моя проблема заключается в том, что моему регистратору не удается вывести std::string с, и он работает только тогда, когда я обозначаю его функцией string.c_str().

Вот моя функция вывода файла журнала:

void Log::writeSuccess(char * text,...)
{
    // Grab the variables and insert them
    va_list ap;
    va_start(ap, text);
    char buff[BUFFER_SIZE];
    vsnprintf(buff, sizeof(buff), text, ap);

    // Output to the log
    logfile << "<-!-> " << buff << endl;
}

Вот пример вызова моего объекта класса журнала (игнорируйте бесполезность вызова):

string test("This is a test string!");
errorLog.writeSuccess("Output: %s", test);

Я получаю случайные символы и искаженный вывод.

Однако, когда я добавляю строку test с .c_str(), текст выводится правильно.

Вся причина, по которой я стараюсь избегать cstrings, заключается в том, что я понимаю, что они не являются кроссплатформенными, и разрабатываю мой клиент для поддержки всех основных операционных систем.

Подведем итог:

  1. Что не так с моей функцией вывода журнала? Вы видите, как это можно улучшить?

  2. Должен ли я вообще избегать c_strings?

Ответы [ 2 ]

2 голосов
/ 26 декабря 2011

Вы получаете случайные искажения при передаче std::string в vsnprintf, потому что спецификатор формата "%s" соответствует C-строке - char*.

std::string - не типа char*, но std::string.c_str() является типа char*.vsnprintf будет в основном читать char s, на которые указывает адрес, который предполагает, что это начало C-строки , вплоть до NUL-символа '\0'.

std::string, помещенный в стек и переданный в качестве аргумента vsnprintf, не является указателем на char, однако vsnprintf будет просто обрабатывать эти байты как адрес и начинать читать char с / байты из этогоадрес, вызывая неопределенное поведение.

Семейство функций printf не является типобезопасным, так как они полагаются на строку формата и список переменных переменных, поэтому ваш код будет компилироваться, но вы получите неожиданные результаты.

Суть в том, что семейство функций printf ожидает char*, когда вы используете спецификатор формата "%s".

Я также думаю, что вы путаете строки в стиле C (char[]) с Microsoft-специфический CString класс.Строки в стиле C вообще не вызовут проблем на разных платформах;литерал "This is a test string!" является строкой в ​​стиле C (const char[]).

1 голос
/ 26 декабря 2011

При вызове функции с переменными параметрами вы должны использовать простые типы. Для string вы должны использовать c_str(). Там нет обходного пути. MFC CString разработан таким образом, чтобы вы могли без проблем использовать его напрямую, но это было решением Microsoft и частью их дизайна.

РЕДАКТИРОВАТЬ: Как я уже сказал, при вызове функции с переменными параметрами вы должны использовать string :: c_str (). Однако вместо C-подобных функций с переменными параметрами вы можете использовать что-то вроде boost::format() и его оператор подачи параметров%. Это также дает вам больше контроля над упорядочением параметров, что очень удобно для i18n .

...