printf()混淆 - 在打印缓冲区时尝试打印字符串并打印正确的值时打印(null)

时间:2014-01-08 22:13:12

标签: c++ string null printf

看简化代码 - 我很困惑......

#include <stdio.h>
#include <string>      
#include <cstdlib>  // includes the standard library and overarchs over stdlib.h

using namespace std;  
void main()
{
    char buffer[10];
    string theString;
    int i = 997799; //(simplified)
    itoa(i,buffer,10);                      
    theString = buffer;             

    printf("\n string is: %s of length %d \n", theString, theString.length());
    printf("\n buffer is: %s of length %d \n", buffer, theString.length());
    return;
}

我得到的输出是意外的:

string is: (null) of length 926366009
buffer is: 997799 of length 6

(1)为什么字符串打印为空?

(2)为什么theString.length()在第一个printf()中没有正确打印,而是在第二个?#/ p>

(3)如果我在visual studio中设置断点'buffer'显示为“997799”,而'theString'显示为{“997799”} - 这里有什么奇怪的事情?

谢谢大家! 编辑我非常感谢所提供答案的详细程度 - 它们都增加了清晰度并帮助我超越了我的问题 - 非常感谢你花时间帮忙:)

4 个答案:

答案 0 :(得分:3)

程序具有未定义的行为,因为函数printf无法输出std :: string类型的对象。当使用格式符号%s时,该函数假定相应的参数是字符串文字的指针。因此该函数尝试输出std :: string类型的对象作为字符串文字。 要正确使用函数printf和std :: string类型的对象,您应该使用成员函数c_str()或data()(对于C ++ 2011)将它们转换为字符串。例如

printf("\n string is: %s of length %ul \n", theString.c_str(), theString.length());

答案 1 :(得分:3)

当您将%s说明符与printf()一起使用时,您承诺传递char const*作为相应的参数。传递除char const*之外的任何内容或衰减为char const*的内容都是未定义的行为。当然,传递C ++对象会有不确定的行为。

std::string传递给printf()的正确方法是使用%s格式说明符并使用c_str()成员,例如:

printf("string=%s\n", s.c_str());

您使用%d作为std::string::size_type的格式说明符。这将可能工作,但不能保证工作!虽然std::string::size_type保证为std::size_t,但此类型可能是unsigned intunsigned longunsigned long long,甚至是一些非标准的内置整数类型!拼写std::size_t格式说明符的正确方法是%zu(当然不是%ul,如同另一篇文章:它可能是%lu,但是,还是错的:

printf("string.size()=%zu\n", s.size());

由于您使用的是C ++,因此最好让编译器找出要调用的格式:

std::cout << "\n string is: " << theString << " of length " << theString.length() << " \n";
std::cout << "\n buffer is: " << buffer << " of length " << theString.length() << " \n";

答案 2 :(得分:0)

我的编译器(os x上的clang)报告以下错误:

c++     foo.cc   -o foo
foo.cc:6:1: error: 'main' must return 'int'
void main()
^~~~
int
foo.cc:11:5: error: use of undeclared identifier 'itoa'
    itoa(i,buffer,10);                      
    ^
foo.cc:14:48: error: cannot pass non-POD object of type 'string' (aka
      'basic_string<char, char_traits<char>, allocator<char> >') to variadic
      function; expected type from format string was 'char *'
      [-Wnon-pod-varargs]
    printf("\n string is: %s of length %d \n", theString, theString.length());
                          ~~                   ^~~~~~~~~
foo.cc:14:48: note: did you mean to call the c_str() method?
    printf("\n string is: %s of length %d \n", theString, theString.length());
                                               ^
                                                        .c_str()
foo.cc:14:59: warning: format specifies type 'int' but the argument has type
      'size_type' (aka 'unsigned long') [-Wformat]
    printf("\n string is: %s of length %d \n", theString, theString.length());
                                       ~~                 ^~~~~~~~~~~~~~~~~~
                                       %lu
foo.cc:15:56: warning: format specifies type 'int' but the argument has type
      'size_type' (aka 'unsigned long') [-Wformat]
    printf("\n buffer is: %s of length %d \n", buffer, theString.length());
                                       ~~              ^~~~~~~~~~~~~~~~~~
                                       %lu

让我们解决它们:

  1. void main() { ... }应为int main() { ... }

  2. itoa()在C ++中不是标准的。但是有std::stoi(),所以让我们使用它。如果我们写入字符数组而不是std::string,我们也可以使用std::snprintf

  3. 如果我们要将printf()std::string一起使用,我们需要使用std::string::c_str()方法返回一个字符数组并进行打印。 printf本身不了解C ++对象。 (我们也可以使用cout,它确实理解c ++对象。)

  4. 这些更改会导致代码如下:

    #include <cstdio>
    #include <string>      
    #include <cstdlib>
    
    using namespace std;
    int main()
    {
        char buffer[10];
        string theString;
        int i = 997799; //(simplified)
    
        snprintf(buffer, sizeof(buffer), "%d", i);
        theString = buffer;
    
        printf("string is: %s of length %lu\n", theString.c_str(), theString.length());
        printf("buffer is: %s of length %lu\n", buffer, strlen(buffer));
    }
    

    此代码输出:

    [4:46pm][wlynch@watermelon /tmp] ./foo
    string is: 997799 of length 6
    buffer is: 997799 of length 6
    

答案 3 :(得分:0)

正如另一个答案指出的那样,(null)值的原因是因为(正式)printf %s期望输入const char*,{{1}是一个std::string并导致未定义的行为...当您执行第一个const char*时随机数的原因与您的编译器有关。

在带有g ++ 4.2.1的OpenBSD 5.1上,当我编译代码时,我收到了很多警告;一个特别是使用theString.length(),我必须更改为itoa,我还会在sprintf %s上收到关于printf std::string的以下警告:{ {1}}。

当我运行已编译的代码时,由于在warning: cannot pass objects of non-POD type 'struct std::string' through '...'; call will abort at runtime'对象'上调用printf,它会在第一个%s中止和核心转储(这是'正确的'行为虽然技术上仍然是“未定义的行为”)

但是,当我在MS编译器上编译上面的代码(没有编辑)时,我实际得到以下输出:

std::string

仍然是“未定义”,但“预期”结果。

显然,MS编译器识别string is: 997799 of length 6 buffer is: 997799 of length 6 上的std::string并将其“更改”为printf调用或MS C / C ++库中的string.c_str()接受printf并为您调用std::string

因此要100%兼容'和C ++'兼容'考虑以下内容:

.c_str()

混合使用C ++和C调用/样式通常不是一个好习惯,但恕我直言我使用#include <iostream> #include <sstream> int main(int argc, char **argv) { std::stringstream theString; int i = 997799; //(simplified) theString << i; std::cout << "string is: " << theString.str() << " of length " << theString.str().length() << std::endl; // printf("string is: %s of length %d \n", theString.str().c_str(), theString.str().length()); return 0; } 因为它的“方便”但通常将其取出或使用一些printf进行调试构建使用仅

我希望能帮助回答“为什么#2”