2014-01-08 105 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'