Scr*_*est 17 c++ string printf
我有一个我想要打印的字符串.当我使用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);
}
Run Code Online (Sandbox Code Playgroud)
该cout给我"rawname:文件"
中printf给了我"rawname:",然后一串字符的波浪
Nek*_*ios 20
这是因为rawname被定义为std :: string.你需要使用
printf("rawname: %s", rawname.c_str());
原因是带有%s的printf期望内存中有一个空终止的C字符串.而std :: string stl字符串并不完全是原始的 - 它最终会在你的情况下终止null,不确定这是否是一种保证,因为长度是由stl容器类在内部管理的.
编辑:
正如评论中指出的那样,内部保证无效终止.所以你所看到的'波浪线'是该字符串中所有已分配但未被利用(或初始化)的内存的输出,直到空终止符.
Ton*_*roy 15
printf("%s", my_string.c_str());
Run Code Online (Sandbox Code Playgroud)
简短说明(后面解释的假设):
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 ^^^^^.
Run Code Online (Sandbox Code Playgroud)
您不能将非POD数据传递给函数 - 例如printf()- 接受任意数量的参数使用....("..."参数是C++继承自C的一个特性,它本身不适合与复杂的C++对象一起使用).
我的GCC编译器不喜欢这样:
printf("rawname: %s", rawname);
Run Code Online (Sandbox Code Playgroud)
GCC 4.5.2错误:
cannot pass objects of non-trivially-copyable
type 'struct std::string' through '...'
Run Code Online (Sandbox Code Playgroud)
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
Run Code Online (Sandbox Code Playgroud)
他们不会编译它,因为没有标准的方法来传递对象....编译器不能简单地...通过值或引用/指针来满足它们,因此不知道要生成什么代码.
但是你的编译器勇敢地做了些什么.让我们考虑std :: string对象暂时的样子,然后返回编译器可能接收和访问它的方式.
未指定std :: string的内部,但通常包含以下任何内容:
end())
end()成员并进行了计算size()- 使用惯用迭代器循环更好地工作c_str()直接返回它,但是这个指针 - 通过data()成员函数可用,标准允许解决非NUL终止文本,因此理论上它可能有一个NUL终止符仅在c_str()被调用时附加,或者c_str()可能将文本复制到别处,然后附加NUL并返回指向该新缓冲区的指针)和/或
这些可以是任何顺序.所以,最简单的可能性是:
std::string s = "this and that";
Run Code Online (Sandbox Code Playgroud)
现在,
"this and that"是一个字符串文字,让我们说地址为"A"; 这些数据被复制到string; 该string不会不记得它有它
s是实际的std::string对象,让我们说地址为"B"; 让我们想象一下,这是最简单的:
size_type size_;(将保持值13,是strlen("this and that"))const char* p_data_; 将指向一些新分配的堆内存 - 让我们说在地址"C" - 已经复制了"this和that\0"至关重要的是,地址"A",地址"B"和地址"C"是不同的!
如果我们有一个糟糕的编译器,将尝试通过我们的std::string对象printf(),则有两件事情printf()可能会收到的,而不是const char*说"%s"告诉它期望:
1)指向std::string对象的指针,即地址"B"
2)从地址"A" sizeof(std::string)复制到某个堆栈地址"B" 的数据字节和/或寄存器,如果它可以处理这些东西,它会期望它;-Pprintf()
printf() 然后开始从该地址打印字节,就像它们是字符一样,直到找到0/NUL字节:
对于上面的场景1,它打印对象中的字节,例如:
比如size_type是4个字节并且在对象的开头; 大小为13,它可能是13,0,0,0或0,0,0,13,具体取决于机器是使用big-endian还是little-endian存储约定......如果它在第一个NUL停止,它会打印字符13(恰好是ASCII回车符/ CR值,将光标返回到行首)然后停止,或者它可能绝对不打印.在你自己的情况下你的字符串内容是不同的,所以它会打印一些其他垃圾,但可能只有一个或两个字符才能达到0/NUL.
假设const char*在"C"处的堆分配缓冲区恰好位于对象的开头,那么将打印该地址中的各个字符:对于32位指针,可能是4个垃圾字符(假设它们都没有发生在是0/NUL),对于64位它将是8,然后它将继续下一个字段std::string(可能是一个end()跟踪指针,但如果它是一个size_type更有可能有0/NUL 的字段) .
printf()可能会将std::string对象数据的前四个字节解释为指向更多文本数据的指针...这与1)不同:假设size_type成员是第一个且值为13,printf()可能会将其误解const char*为地址13,然后尝试从那里读取字符.这实际上保证在打印任何东西之前崩溃(在现代操作系统上),因此这种行为实际上不太可能发生,这使我们得到"1".
您需要打印std :: string的内部char*:
printf("rawname: %s", rawname.c_str());
Run Code Online (Sandbox Code Playgroud)