C++ 使用printf和cout-C+获得不同的输出+;
我有一个字符串要打印。当我使用C++ 使用printf和cout-C+获得不同的输出+;,c++,string,printf,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";
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);
}
intmain(intargc,char*argv[])
{
//检查以确保只有一个参数
如果(argc!=2)
{
cout您需要打印std::string的内部字符*:
printf("rawname: %s", rawname.c_str());
试试这个
cout << "rawname:" << rawname << endl;
printf("rawname: %s", rawname.c_str());
cout这是因为rawname被定义为std::string。您需要使用
printf(“rawname:%s”,rawname.c_str());
原因是带有%s的printf在内存中需要一个以null结尾的C字符串。而std::string stl字符串并不是完全原始的-它最终会以null结尾,但不确定这是否是一个保证,因为长度是由stl容器类内部管理的
编辑:
正如注释中所指出的,在内部它保证以null结尾。因此,您所看到的“曲线”是该字符串中所有已分配但未使用(或初始化)的内存的输出,直到null结尾字符为止。您在printf中试过rawname.c_str()吗?什么有效
出了什么问题-概要
简要说明(后面解释的假设):
不能将非POD数据传递给函数(如<代码> Primff())/>代码>使用C++ >…>代码>接受任意数量的参数。(“…”参数是C++继承的特征,它本身不适合用于复杂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对象的Gizzard
std::string的内部未指定,但通常包含以下任何内容:
- 记录当前大小的成员或超过字符串结尾的指针(ala
end()
)
- 其中一个允许对另一个进行简单计算,但我检查过的两个标准库实现Optimize for a pointer/
end()
member和calculatedsize()
——更适合惯用迭代器循环
- 指向堆上字符缓冲区的指针(实际上,它可能以NUL结尾,并且
c_str()
member函数,标准允许它处理非NUL终止的文本,因此理论上只有在调用c_str()
时,它才可以附加NUL终止符,或者c_str()
可以将文本复制到其他地方,然后附加NUL并返回指向新缓冲区的指针)
- “短字符串优化”数据缓冲区,因此只有几个字符的字符串不需要使用堆
和/或
- 指向其他地方某个引用计数对象的指针(其成员位于+引用计数器、互斥体等之上)
示例:存储文本的简单字符串实现
这些可以是任意顺序。因此,最简单的可能性是:
std::string s = "this and that";
现在,
- “this and that”是一个字符串文字,比如在地址“a”处;该数据被复制到
字符串中字符串中不记得它是从哪里得到的
s
是实际的std::string
对象,假设地址为“B”;假设它是最简单的:
size\u type size;
(将保存值13,即strlen(“这个和那个”)
)
const char*p\u data\uu;
将指向一些新分配的堆内存,比如在地址“C”处,其中“this and that\0”已被复制到其中
关键是,地址“A”、地址“B”和地址“C”是不同的
printf()如何查看std::字符串
如果我们有一个错误的编译器试图将std::string
对象传递给printf()
,那么printf()
可能会收到两件事,而不是%s
告诉它所期望的const char*
:
1) 指向std::string
对象的指针,即地址“B”
2) sizeof(std::string)
从地址“A”复制到某个堆栈地址“B”和/或寄存器的数据字节,如果它能够处理这些事情,printf()
会期望它这样做;-p
printf()
然后开始从该地址打印字节,就像它们是字符一样,直到找到0/NUL字节:
对于上面的场景1,它打印对象中的字节,例如:
- 假设
size\u type
是4个字节,位于对象的开头;对于大小13,它可能是13、0、0、0或0、0、0、13,这取决于机器使用的是大端存储约定还是小端存储约定……如果它在第一个NUL处停止,它将打印字符13(这恰好是一个ASCII回车/CR值,将光标返回到行的开头)然后停止,否则它可能完全不打印任何内容。在您自己的情况下,字符串内容不同,因此它可能会打印一些其他垃圾,但在命中0/NUL之前可能只打印一两个字符
- 假设一个
const char*
到堆分配的缓冲区中的“C”恰好位于对象的开头,那么该地址中的单个字符将被打印出来:对于32位指针,可能是4个垃圾字符(假设它们都不是0/NUL),对于64位指针,它将是8个
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 s = "this and that";