Подтвердить что ты не робот

Получение разного вывода с помощью printf и cout - С++

У меня есть строка, которую я пытаюсь напечатать. когда я использовал cout, он выводится отлично, но использование printf оставляет его искаженным.

Вот код:

int main ( int argc, char *argv[] )
{
    // Check to make sure there is a single argument
    if ( argc != 2 )
    {
        cout<<"usage: "<< argv[0] <<" <filename>\n";
        return 1;
    }

    // Grab the filename and remove the extension
    std::string filename(argv[1]);
    int lastindex = filename.find_last_of("."); 
    std::string rawname = filename.substr(0, lastindex);

    cout << "rawname:" << rawname << endl;
    printf("rawname: %s", rawname);

}

cout дает мне "rawname: file"
printf дает мне "rawname:", а затем кучу squiggly символов

4b9b3361

Ответ 1

потому что rawname определяется как std::string. Вы должны использовать

printf("rawname: %s", rawname.c_str());

Причина в том, что printf с% s ожидает нулевую завершенную строку C в памяти. В то время как строка std::string stl не является абсолютно сырой - в конечном итоге она заканчивается в вашей ситуации, не уверен, что это даже гарантия, так как длина внутренне управляется классом контейнера stl.

Edit:

Как указано в комментарии, внутренне он гарантировал, что он не будет завершен. То, что вы видите как "squiggly lines", является выходом всей выделенной, но не использованной (или инициализированной) памяти в этой строке до символа нулевого терминатора.

Ответ 2

Что работает

printf("%s", my_string.c_str());

Что случилось - синопсис

Краткая иллюстрация (предположения, объясненные позже):

std::string s {
   // members in unknown order
   size_type member:    13 00 00 00                       HEAP
   const char* member:  pointer C to ................ "this and that"
};

You print characters here ^^^^^^       not          here ^^^^^.

Вы не можете передавать данные, отличные от POD, в функции, такие как printf(), которые принимают произвольное количество аргументов с помощью .... ( "..." - это функция С++, наследуемая от C, и она по своей сути непригодна для использования со сложными объектами С++).

Вы даже можете скомпилировать это?

Мои компиляторы GCC не нравятся:

printf("rawname: %s", rawname);

Ошибка GCC 4.5.2:

cannot pass objects of non-trivially-copyable
type 'struct std::string' through '...'

GCC 4.1.2 предупреждение + поведение во время выполнения:

cannot pass objects of non-POD type 'struct std::string'
through '...'; call will abort at runtime

# ./printf_string
zsh: illegal hardware instruction  ./printf_string

Они не будут скомпилировать его, потому что нет стандартного способа передачи объектов с помощью .... Компилятор не может решить из просто ..., нужны ли они по значению или по ссылке/указателю, поэтому не будет знать, какой код сгенерировать.

Но ваш компилятор храбро сделал что-то. Давайте рассмотрим, как выглядит объект std::string на мгновение, затем вернитесь к тому, как ваш компилятор мог получить и получить к нему доступ.

Жезлы объекта std::string

Внутренние элементы std::string не указаны, но обычно содержат любое из следующих значений:

  • член, записывающий текущий размер ИЛИ указатель за конец строки (ala end())
    • либо позволяет просто вычислять другую, но пару стандартных реализаций библиотеки, которые я проверил, оптимизированы для элемента указателя / end() и рассчитаны size() - лучше работают с идиоматическими циклами итератора
  • указатель на буфер символов в куче (на практике он, скорее всего, оставил NUL завершенным и c_str() возвращает его напрямую, но этот указатель, доступный через функцию члена data(), разрешен стандартом для адресации не- NUL завершен текст, поэтому теоретически он может иметь NUL-терминатор, добавленный только при вызове c_str(), или c_str() может скопировать текст в другом месте, а затем добавить NUL и вернуть указатель на этот новый буфер).
  • буфер буферизации с короткими строками, поэтому строки из нескольких символов не должны использовать кучу

и/или

  • указатель на некоторый объект с подсчетом ссылок в другом месте (у которого есть элементы выше + счетчик ссылок, мьютекс,...?)

Пример: простая реализация строки, сохраняющая текст

Они могут быть в любом порядке. Итак, самая простая возможность:

std::string s = "this and that";

Теперь,

  • "this and that" - строковый литерал, допустим, по адресу "A" ; эти данные копируются в string; string не помнит, откуда он получен из

  • s - это фактический объект std::string, допустим, по адресу "B"; предположим, что это самое простое:

    • size_type size_; (будет удерживать значение 13, будучи strlen("this and that"))
    • const char* p_data_; будет указывать на некоторую недавно выделенную кучную память - пусть говорят по адресу "C" - в который "этот и тот \0" был скопирован

Реально, адрес "A" , адрес "B" и адрес "C" различны!

Как printf() видит std::string

Если бы у нас был плохой компилятор, который попытался передать наш std::string объект printf(), то вместо const char*, который "%s" может <2 > получить printf() может <2 → ожидать:

1) указатель на объект std::string, то есть адрес "B"

2) sizeof(std::string) байты данных, скопированных из адреса "A" в некоторый адрес стека "B" и/или регистры, где printf() ожидал бы, если он сможет обрабатывать эти вещи; -P

printf() затем начинает печатать байты с этого адреса, как если бы они были символами, пока не найдет байт 0/NUL:

  • для сценария 1 выше, он печатает байты в объекте, например:

    • say size_type - 4 байта и в начале объекта; с размером 13, это может быть 13, 0, 0, 0 или 0, 0, 0, 13 в зависимости от того, использует ли машина соглашение о согласии с большим или средним порядком... если он останавливается при первом NUL, print character 13 (который является значением ASCII-каретки-возврата/CR, возвращающим курсор в начало строки), затем останавливается или может ничего не печатать. В вашем собственном случае ваше строковое содержимое было другим, поэтому оно напечатало бы какой-то другой мусор, но, вероятно, только один или два символа, прежде чем нажать 0/NUL.

    • скажем, что a const char* для выделенного кучи буфера на "C" находится в начале объекта, тогда будут напечатаны отдельные символы в этом адресе: для 32-разрядных указателей, которые, вероятно, 4 (при условии, что ни один из них не будет 0/NUL), для 64-бит это будет 8, затем оно будет продолжено с следующим полем в std::string (скорее всего, end() -tracking pointer, но если это поле size_type, у которого более вероятно наличие 0/NUL).

  • printf() может интерпретировать первые четыре байта данных объекта std::string как указатель на дополнительные текстовые данные... это отличается от 1): скажем, член size_type был первым, а значение равно 13, printf() может неправильно интерпретировать это как const char* для адреса 13, а затем попытаться прочитать символы оттуда. Это практически гарантировано сбой перед печатью (на современных ОС), поэтому очень маловероятно, что это поведение действительно произошло, что оставляет нас с "1".

Ответ 3

Вам нужно напечатать внутренний char * std::string:

printf("rawname: %s", rawname.c_str());

Ответ 4

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

cout << "rawname:" << rawname << endl;
printf("rawname: %s", rawname.c_str());

rawname не является массивом char, а экземпляром класса std::string. Чтобы получить фактический массив char, вы должны вызвать функцию c_str()

Ответ 5

Вы пытались использовать rawname.c_str() в printf?