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和calculated
      size()
      ——更适合惯用迭代器循环
  • 指向堆上字符缓冲区的指针(实际上,它可能以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";