Получение разного вывода с помощью 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 символов
Ответы
Ответ 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?