2014-01-08 104 views
1

看到简化的代码 - 我很困惑...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()中没有正确打印,但在第二个中是正确的? (3)如果我在Visual Studio中设置了断点,'buffer'显示为“997799”,而'theString'显示为{“997799”} - 这里有一些奇怪的事情发生?

谢谢大家! 编辑我非常感谢提供答案的详细程度 - 他们都更加清楚,并帮助我超越了我的问题 - 非常感谢你花帮忙:)

+0

'void main'不合法。使用'int main'。 'itoa'不是标准功能。并且http://stackoverflow.com/questions/10865957/c-printf-with-stdstring – chris

回答

3

当您使用%s符与printf()你答应传递char const*为相应的参数。除char const*之外的任何东西或衰减为char const*的东西都是未定义的行为。当然,传递一个C++对象将会有未定义的行为。

传递一个std::stringprintf()正确的方法是使用%s格式说明,并使用c_str()成员,例如:

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

您使用%d作为格式说明符std::string::size_type。那可能工作,但它不能保证工作!虽然std::string::size_type保证是std::size_t,这种类型可能是unsigned int,unsigned long,unsigned 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"; 
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()); 
+0

虽然它是文字后缀:'%ul'是''ul''不是''unsigned long'的格式说明符'', %u'表示一个'unsigned int'后跟一个'l'字符。你可能打算编写'%lu',但是,它仍然是错误的:'std :: size_t'的正确格式说明符是'%zu'。 –

+0

@DietmarKühl,我认为你错了。从C标准l(ell)指定以下d,i,o,u,x或X转换说明符适用于long int或unsigned long int ... –

+0

@DietmarKühl或者您的意思是应该使用%鲁?至于size_t,那么谢谢。当我写这篇文章的时候,我不记得那个说明者。 –

0

我的编译器(在OS X上铛)报告以下错误:

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。我们需要使用std::string::c_str()方法来返回一个字符数组,然后打印它。 printf本身不理解C++对象。 (我们也可以使用cout,它理解C++对象)。

这些变化导致代码看起来像:

#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][[email protected] /tmp] ./foo 
string is: 997799 of length 6 
buffer is: 997799 of length 6 
+0

如前所述,'%lu'是错误的格式说明符:不能保证'std :: size_t'是'unsigned long'的'typedef'。正确的格式说明符是'%zu'。方便的是,这清楚地表明,C的格式说明符真的很难使用,这显然使用IOStreams更安全。 –

0

作为另一个答案指出,对于(null)值的原因是因为(正式)printf%s预计输入为const char*std::string而不是 a const char*,并导致未定义的行为...当你做第一个theString.length()时,随机数的原因与你的编译器有关。

在使用g ++ 4.2.1的OpenBSD 5.1上,当我编译代码时,我得到了很多警告;其中一个特别是使用itoa,我不得不更改为sprintf,我还通过std::string获得了关于%sprintf的以下警告:warning: cannot pass objects of non-POD type 'struct std::string' through '...'; call will abort at runtime

当我运行编译后的代码它将中止和核心在第一printf转储因为std::string“对象”在电话会议中%s(这是“正确”的行为虽然在技术上仍然是“不确定的行为”)

然而,当我在MS编译器来编译上面的代码(没有编辑),其实我得到下面的输出:

string is: 997799 of length 6 
buffer is: 997799 of length 6 

仍然是“不确定”,但“预期”的结果。

所以显然MS编译器可识别一个printf和“变化”到一个string.c_str()呼叫std::string或MS C/C++库printf接受std::string并调用.c_str()你。

所以是100%“兼容”和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; 
} 

它通常不是好的做法混合C++和C调用/风格,但恕我直言,我用printf很多关于这是'方便',但通常会将其拿出来或仅使用一些#defines来进行调试构建。

我希望可以帮助回答'为什么为#2'