2013-07-04 16:45:19
找了很多資料,沒有說的很明白的,下面是老外的一篇文章,解釋的比較清楚,后面給出翻譯。
Clarifying stdio.h versus cstdio
轉自:http://forums.codeguru.com/showthread.php?344430-Clarifying-stdio-h-versus-cstdio
I constantly see recommendations to #include <cstdio> instead of using stdio.h and the same for the other C headers. What most posters fail to mention is that this should put all the symbols into namespace std and NOT into the global namespace.
This you have to write std::printf(...). Simply writing printf alone will not work .
The same applies to the headers :
cassert ciso646 csetjmp cstdio ctime cctype climits csignal cstdlib cwchar cerrno clocale cstdarg cstring cwctype cfloat cmath cstddef.
This is clearly defined by ISO 14882 in footnote 160:
std. Therefore, the newer forms are the preferred forms for all uses except for C++ programs which are intended to be strictly compatible
with C.
Most compilers completely fail to implement this correctly and put all the names into both namespaces when using the new headers. As this is incorrect behaviour, it is quite likely to change in the future which means that code you write now may break on newer compilers as they get closer to the standard.
As an aside note that many compilers fail to give even a warning (and even on the highest warning level) when you write void main(), even though the standard clearly dictates that main MUST return int.
Nathan Myers, a member of the C++ standards committee wrote this paper on header strategy here:http://www.cantrip.org/cheaders.html
Markus
大致翻譯了一下,如下:
經常看到有建議說要使用 #include <cstdio>代替 #include stdio.h,但很少有人提及這將把所有的symbol放進std namespace,而非 global namespace.這樣,就必須寫成std::printf(...)(注意是必須,但有的編譯器卻能通過,具體解釋見下面),簡單的寫成printf是不行的。
這對於以下的有文件同樣:cassert ciso646 csetjmp cstdio ctime cctype climits csignal cstdlib cwchar cerrno clocale cstdarg cstring cwctype cfloat cmath cstddef.
這在ISO 14882 in footnote 160:中有明確說明:
std. Therefore, the newer forms are the preferred forms for all uses except for C++ programs which are intended to be strictly compatible
with C.
也就是:".h"頭文件將所有名字放在global namespace中,在新的方式下(指的是諸如cstdio這樣的頭文件),名字是放在namespace std中的。因此,新的方式是所有應用推薦的方式,除非是要編寫與C嚴格兼容的程序。
許多編譯器不能完全正確地實施這個規則,在使用新的header時,將所有的名字放入兩個namespace。但因為這是不正確的,所以在以后很可能會改變(編譯器會有所優化),這意味着你現在寫的代碼在新的編譯器上可能會出錯,因為它們更接近於新的標准。
許多編譯器連一個warning都不能給出(即使是在最高warning級別下),比如當你寫一個void main()時,許多編譯器都不會給出warning,即使標准明確指出main函數必須返回int型數據。
C++標准委員會的成員Nathan Myers寫了一篇關於header strategy文章:http://www.cantrip.org/cheaders.html
小結:
- 在C++下,若要使用C中已有庫中的函數如stdio,文件包含方式為前面加一個c,同時去掉.h后綴,如#include <cstdio>,同時必須加上using namaspace;對於其他類似的函數同樣;
- 對於C++特有的庫,直接用去掉.h后綴的文件包含,並加上using namaspace;
VS2010下實例分析:
針對上面的討論,在VS2010下,用一段小代碼測試
在C++中,若要使用printf,我們知道該函數在C中是在stdio中包含的
文件包含寫法1(標准C++風格,推薦):
對於C++,標准的寫法如下
1 #include <cstdio> 2 using namespace std; 3 4 int main() 5 { 6 int a = 16; 7 8 printf("%d\n",a); 9 10 return 0; 11 }
編譯,沒有waring,沒有error,運行可正確輸出。
文件包含寫法2(舊式C風格,不推薦,且在VS2010下編譯出錯):
C標准的寫法,如下:
1 #include <stdio.h>
但是在VS2010下編譯,會報錯找不到該頭文件:Cannot open include file: 'cstdio.h': No such file or directory
疑問:根據上面的解釋,雖然寫的是C++的代碼,但是這樣寫也可以,是標准支持的,只不過標准推薦用第一種寫法。此處為何編譯出錯,原因未知???
文件包含寫法3(錯誤的寫法,但是大多數編譯器不活給出waring或error,仍可編譯通過,在以后的編譯器下可能會出錯,強烈不推薦):
但如果文件包含去掉using namespace std;,即寫成:
1 #include <cstdio>
編譯,運行結果與文件包含寫法1完全一樣,沒有任何warning。這就是上面所說的許多編譯器不能完全正確地實施這個規則,在使用新的header時,將所有的名字放入兩個namespace。在以后的編譯器下可能會出錯
疑問:使用printf函數,包含iostream也可以???
我們知道printf對於C而言,是包含在stdio中的,對於C++則是在cstdio中的,但是奇怪的是,我在VS2010下,只包含iostream,如下:
1 #include <iostream>
居然也可正確編譯、運行,原因未知,可能還是類似於文件包含寫法3的原因,編譯器沒有很好的處理C與C++的文件包含問題。
在iostream中並沒有包含printf函數,為何可正確使用printf???
同樣地,寫成
1 #include <iostream> 2 using namespace std;
但是,若要使用cout、cin等iostraem中真正包含的函數,就必須加上using namespace std;的聲明,而不能只寫#include <iostream>,如下:
1 #include <iostream> 2 //using namespace std; 3 4 int main() 5 { 6 int a = 16; 7 8 //printf("%d\n",a); 9 cout<<a<<endl; 10 return 0; 11 }
會提示沒有聲明cout、endl:error C2065: 'cout' : undeclared identifier
C++頭文件包含何時要加.h,何時不加.h,何時在前面加c?
C++中不要#include <iostream.h>,不要#include <string.h>,因為它們已經被C++標准明確的廢棄了,請改為 #include <iostream> 和 #include <cstring>.規則就是:
a. 如果這個頭文件是舊C++特有的,那么去掉 h后綴,並放入std名字空間,
比如 iostream.h 變為 iostream.
因此,若要使用iostream庫中函數,文件包含為
#include <iostream>
using namespace std;
若只寫#include <iostream>,而沒有using namespace std;,會報錯:error C2065: 'cout' : undeclared identifier,因為cout是在std中命名的,必須加上using namespace std;
可能在早期的編譯器中,也支持#include <iostream.h>,可不用寫using namespace std,但用VS2010編譯,會報錯Cannot open include file: 'iostream.h': No such file or directory。
b. 如果這個頭文件是C也有的,那么去掉 h后綴,增加一個c前綴,比如 string.h
變為 cstring;stdio.h 變為 cstdio, 等等
Cplusplus的網站:http://www.cplusplus.com/reference/cstdio/
Input and Output operations can also be performed in C++ using the C Standard Input and Output Library (cstdio, known as stdio.h in the C language). This library uses what are called streams to operate with physical devices such as keyboards, printers, terminals or with any other type of files supported by the system. Streams are an abstraction to interact with these in an uniform way; All streams have similar properties independently of the individual characteristics of the physical media they are associated with.
下面來自百度百科:
下面參考:http://no001.blog.51cto.com/1142339/1065812
在C語言中,stdio.h 頭文件是主要的。而在后來的C++語言中,C只是C++的一個子集,且C++中,已不推薦再用C的類庫,但為了對已有代碼的保護,還是對原來的頭文件支持。
cstdio是c++從C的stdio.h繼承來的,在前面加C同時不要H后綴,在C++環境當然是選用前者,兩者內容都一樣,只是cstdio頭文件中定義的名字被定義在命名空間std中。使用后者就會帶來額外的負擔,需要區分哪些是
標准庫明是C++特有的,哪些是繼承過來的!!所以在C++中要盡量避免C風格的出現!!
cstdio code:
// cstdio standard header
#pragma once
#ifndef _CSTDIO_
#define _CSTDIO_
#include <yvals.h>
#ifdef _STD_USING
#undef _STD_USING
#include <stdio.h>
#define _STD_USING
#else
#include <stdio.h>
#endif
#define _HAS_CONVENTIONAL_CLIB 1
#define _IOBASE _base
#define _IOPTR _ptr
#define _IOCNT _cnt
#ifndef RC_INVOKED
#if _GLOBAL_USING
_STD_BEGIN
using ::size_t; using ::fpos_t; using ::FILE;
using ::clearerr; using ::fclose; using ::feof;
using ::ferror; using ::fflush; using ::fgetc;
using ::fgetpos; using ::fgets; using ::fopen;
using ::fprintf; using ::fputc; using ::fputs;
using ::fread; using ::freopen; using ::fscanf;
using ::fseek; using ::fsetpos; using ::ftell;
using ::fwrite; using ::getc; using ::getchar;
using ::gets; using ::perror;
using ::putc; using ::putchar;
using ::printf; using ::puts; using ::remove;
using ::rename; using ::rewind; using ::scanf;
using ::setbuf; using ::setvbuf; using ::sprintf;
using ::sscanf; using ::tmpfile; using ::tmpnam;
using ::ungetc; using ::vfprintf; using ::vprintf;
using ::vsprintf;
_STD_END
#endif
#endif
#ifndef _Filet
#define _Filet FILE
#endif
#ifndef _FPOSOFF
#define _FPOSOFF(fp) ((long)(fp))
#endif
#endif