您的位置:首页 > 编程语言 > C语言/C++

C/C++ 常见误区

2010-08-14 13:45 489 查看
<!--
/* Font Definitions */
@font-face
{font-family:宋体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimSun;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:宋体;
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-alt:SimSun;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
@font-face
{font-family:Calibri;
panose-1:2 15 5 2 2 2 4 3 2 4;
mso-font-charset:0;
mso-generic-font-family:swiss;
mso-font-pitch:variable;
mso-font-signature:-520092929 1073786111 9 0 415 0;}
@font-face
{font-family:"/@宋体";
panose-1:2 1 6 0 3 1 1 1 1 1;
mso-font-charset:134;
mso-generic-font-family:auto;
mso-font-pitch:variable;
mso-font-signature:3 135135232 16 0 262145 0;}
/* Style Definitions */
p.MsoNormal, li.MsoNormal, div.MsoNormal
{mso-style-unhide:no;
mso-style-qformat:yes;
mso-style-parent:"";
margin:0cm;
margin-bottom:.0001pt;
text-align:justify;
text-justify:inter-ideograph;
mso-pagination:none;
font-size:10.5pt;
mso-bidi-font-size:11.0pt;
font-family:"Calibri","sans-serif";
mso-ascii-font-family:Calibri;
mso-ascii-theme-font:minor-latin;
mso-fareast-font-family:宋体;
mso-fareast-theme-font:minor-fareast;
mso-hansi-font-family:Calibri;
mso-hansi-theme-font:minor-latin;
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;
mso-font-kerning:1.0pt;}
.MsoChpDefault
{mso-style-type:export-only;
mso-default-props:yes;
font-family:"Calibri","sans-serif";
mso-bidi-font-family:"Times New Roman";
mso-bidi-theme-font:minor-bidi;}
/* Page Definitions */
@page
{mso-page-border-surround-header:no;
mso-page-border-surround-footer:no;}
@page WordSection1
{size:595.3pt 841.9pt;
margin:72.0pt 90.0pt 72.0pt 90.0pt;
mso-header-margin:42.55pt;
mso-footer-margin:49.6pt;
mso-paper-source:0;
layout-grid:15.6pt;}
div.WordSection1
{page:WordSection1;}
-->

在此论坛上发现了一些特别的问题,这些问题在其他地方并不存在,猜想是因为这里以学生为主,而学校的教材和教师与
IT
发展脱节严重。

1. C++
虽然主要是以
C
的基础发展起来的一门新语言,但她不是
C
的替代品,不是
C
的升级,
C++

C
是兄弟关系。没有谁比谁先进的说法,更重要

的一点是
C

C++
各自的标准委员会是独立的,最新的
C++
标准是
C++98
,最新的
C
标准是
C99
。因此也没有先学
C
再说
C++
的说法,也不再(注意这


"
不再
"
)有
C++
语法是
C
语法的超集的说法。

2. C++/CLI

C#
是微软的,它们与
C

C++
没有任何关系,虽然部分语法相似。但哪两种语言不相似呢?都是
abc

26
个字母。

3.
不要使用
TC/TC++/BC/CB
等古老的编译器来学习
C/C++
,因为它们太古老了,不支持新的
C/C++
标准。不要使用
CBX /VC++6.0 /VC2005
等对
C/C++
标准支持不好的编译器,虽然这些编译器适合工作,但不适合学习,因为它们中的语法陷阱很多。记住唯一适合学习的编译器是

gcc/mingw

[antigloss
注:
Dev-C++

使用的编译器就是
gcc
& g++]

4.
不要用
""
代替
<>

来包含系统头文件

,虽然有些编译器允许你这样做,但它不符合
C/C++
标准。

错误的示例:
#include "stdio.h"

#include "iostream"

[antigloss
注:习惯上,
<>

用于包含标准头文件

和系统头文件


""

用于包含自定义头文件

。标准似乎没有明确规定不准用
""
包含标准头文件和系统头文件。使用
""
包含标准头文件或者系统头文件只能说是一种
不良风格


]

5.
不要将
main
函数的返回类型定义为
void
,虽然有些编译器允许你这样做,但它不符合
C/C++
标准。不要将函数的
int
返回类型省略不写,在
C++
中要求编译器至少给一个警告。错误的示例:
void main() {}

main() {} [antigloss
注:
C99

C++98
都要求编译器对省略
int
至少发出一个警告
]

6.
不要把
VC++
中的
#include "stdafx.h"
贴出来,它是预编译头文件。如同上菜时不要把厨师也放到托盘中。

7. [C++]
不要
#include <
iostream.h>

,不要
#include <
string.h>

,因为它们已经被
C++
标准明确的废弃了,请改为
#include <
iostream>


#include <
cstring>

。规则就是:

a.
如果这个头文件是旧
C++
特有的,那么去掉
.h
后缀,并放入
std
名字空间,

比如
iostream.h
变为
iostream


b.
如果这个头文件是
C
也有的,那么去掉
.h
后缀,增加一个
c
前缀,比如
string.h

变为
cstring

stdio.h
变为
cstdio,
等等。

BTW
:不要把
string

cstring

string.h
三个头文件搞混淆

BTW

windows.h
不是
C/C++
的标准文件,因此它的命名
C/C++
不管。

8.
不要再写
char* p = "XXX"
这种语句,要写成
const char* p = "XXX"
,编译器之所以让前者通过编译是为了兼

容以前的大量的旧代码。
[antigloss
注:这段话对
C++
而言是正确的。但是,目前的
C99
标准似乎并没有定义
"XXX"
一定是常量。
]

BTW

const TYPE* p

TYPE const* p
是一样的,风格不同而已。

BTW

C
语言中也有
const
关键字。

9.

不要在同一条语句中包含一个变量的多个
++/--
,因为它们的解析在
C/C++
标准中没有规定,完全取决于编译器的个人行为。

10. C/C++
是平台无关性语言,因此系统相关的
process/GUI
等不在标准
C/C++
库中。比如
graphics.h

windows.h
等是由某个编译器提供的,而不是由
C/C++
提供的。

11. C/C++
只是语言,而且是平台无关性语言。论坛上有部分人甚至认为
C
就是
dos

C++
就是
windows
,那么请问
linux
是什么?

12. [C++]

面向对象曾经是设计
C with class

C++
的前身)的主要目的,但
C++
不是,
C++
是一个多典范语言。主要支持过程调用、基于对

象、面向对象、泛式编程这四种编程典范。当然还支持
functional, generative,metaprogramming
等典范。

13.
语法学家不是文学家,所以当你学会了一门计算机语言时,你还需要学习数据机构和算法,还需要掌握工具和平台
API
的用法。

14. C/C++
是通用语言,因此语法很复杂,你应当裁减成适合你自己的语法集合,比如裁减成
better C

ADT


15. C/C++
是通用语言,因此只含通用的库,你应该丰富自己需要的库,比如汽车工业协会有自己的
C/C++
函数
/

/
模板库。

C/C++
误区一:
void main()

很多人甚至市面上的一些书籍,都使用了
void
main( )
,其实这是错误的。
C/C++
中从来没有定义过
void
main( )

C++
之父
Bjarne Stroustrup
在他的主页上的
FAQ
中明确地写着
The
definition void main( ) { /* ... */ } is not and never has been C++, nor has it
even been C.

void main( )
从来就不存在于
C++
或者
C
)。下面我分别说一下
C

C++
标准中对
main
函数的定义。

1. C


C89

中,
main(
)
是可以接受的。
Brian W. Kernighan


Dennis M. Ritchie
的经典巨著
The C programming Language 2e
(《
C
程序设计语言第二版》)用的就是
main( )
。不过在最新的
C99
标准中,只有以下两种定义方式是正确的:

int main( void )

int main( int argc, char *argv[] )

(参考资料:
ISO/IEC
9899:1999 (E) Programming languages — C 5.1.2.2.1 Program startup


当然,我们也可以做一点小小的改动。例如:
char
*argv[]
可以写成
char **argv

argv

argc
可以改成别的变量名(如
intval

charval
),不过一定要符合变量的命名规则。

如果不需要从命令行中获取参数,请用
int
main(void)
;否则请用
int main( int argc, char *argv[] )


main
函数的返回值类型必须是
int
,这样返回值才能传递给程序的调用者(如操作系统)。

如果
main
函数的最后没有写

return
语句的话,
C99
规定编译器要自动在生成的目标文件中(如
exe
文件)加入
return
0;
,表示程序正常退出。不过,我还是建议你最好在
main
函数的最后加上
return

语句,虽然没有这个必要,但这是一个好的习惯。注意,
vc6
不会在目标文件中加入
return
0;
,大概是因为
vc6

98
年的产品,所以才不支持这个特性。现在明白我为什么建议你最好加上
return
语句了吧!不过,
gcc3.2

Linux
下的
C
编译器)会在生成的目标文件中加入
return 0;

2. C++

C++98
中定义了如下两种
main
函数的定义方式:

int main( )

int main( int argc, char *argv[] )

(参考资料:
ISO/IEC
14882(1998-9-01)Programming languages — C++ 3.6 Start and termination


int main( )
等同于
C99
中的
int
main( void )

int main( int argc, char *argv[] )
的用法也和
C99
中定义的一样。同样,
main
函数的返回值类型也必须是
int
。如果
main
函数的末尾没写
return
语句,
C++98
规定编译器要自动在生成的目标文件中加入
return 0;
。同样,
vc6
也不支持这个特性,但是
g++3.2

Linux
下的
C++
编译器)支持。

3.
关于
void main


C

C++
中,不接收任何参数也不返回任何信息的函数原型为
“void foo(void);”
。可能正是因为这个,所以很多人都误认为如果不需要程序返回值时可以把
main
函数定义成
void
main(void)
。然而这是错误的!
main
函数的返回值应该定义为
int
类型,
C

C++
标准中都是这样规定的。虽然在一些编译器中,
void main
可以通过编译(如
vc6
),但并非所有编译器都支持
void main
,因为标准中从来没有定义过
void
main

g++3.2
中如果
main
函数的返回值不是
int
类型,就根本通不过编译。而
gcc3.2
则会发出警告。所以,如果你想你的程序拥有很好的可移植性,请一定要用
int
main


4.
返回值的作用

main
函数的返回值用于说明程序的退出状态。如果返回
0
,则代表程序正常退出;返回其它数字的含义则由系统决定。通常,返回非零代表程序异常退出。下面我们在
winxp
环境下做一个小实验。首先编译下面的程序:

int main( void )

{

return 0;

}

然后打开附件里的

命令提示符

,在命令行里运行刚才编译好的可执行文件,然后输入
“echo %ERRORLEVEL%”
,回车,就可以看到程序的返回值为
0
。假设刚才编译好的文件是
a.exe
,如果输入
“a && dir”
,则会列出当前目录下的文件夹和文件。但是如果改成
“return -1”
,或者别的非
0
值,重新编译后输入
“a
&& dir”
,则
dir
不会执行。因为
&&
的含义是:如果

&&
前面的程序正常退出,则继续执行
&&
后面的程序,否则不执行。也就是说,利用程序的返回值,我们可以控制要不要执行下一个程序。这就是
int main
的好处。如果你有兴趣,也可以把
main
函数的返回值类型改成非
int
类型(如
float
),重新编译后执行
“a
&& dir”
,看看会出现什么情况,想想为什么会出现那样的情况。顺便提一下,如果输入
a ||
dir
的话,则表示如果
a
异常退出,则执行
dir


5.
那么
int main( int argc, char *argv[], char *envp[] )
呢?

这当然也不是标准
C/C++
里面定义的东西!
char
*envp[]
是某些编译器提供的扩展功能,用于获取系统的环境变量。因为不是标准,所以并非所有编译器都支持,故而移植性差,不推荐使用。

C/C++
误区二:
fflush(stdin)

1.

为什么


fflush



stdin




是错的


  首先请看以下程序:

  
include <stdio.h>

  
int main

void


  
{

  
int i


  
for
(;;)
{

  
fputs

"Please
input an integer

"

stdout
);

  
scanf

"%d"

&i
);

  
printf

"%d/n"

i
);

  
}

  
return 0


  
}

 

 这个程序首先会提示用户输入一个整数,然后等待用户输入,如果用户输入的是整数,程序会输出刚才输入的整数,并且再次提示用户输入一个整数,然后等待用

户输入。但是一旦用户输入的不是整数(如小数或者字母),假设
scanf
函数最后一次得到的整数是
2
,那么程序会不停地输出
“Please input an integer

2”
。这是因为

scanf

"%d"

&i
);

只能接受整数,如果用户输入了字母,则这个字母会遗留在

输入缓冲区

中。因为缓冲中有数据,故而
scanf
函数不会等待用户输入,直接就去缓冲中读取,可是缓冲中的却是字母,这个字母再次被遗留在缓冲中,如此反复,从而导致不停地输出
“Please input an integer

2”


  也许有人会说:

居然这样,那么在

scanf
函数后面加上
‘fflush

stdin
);

,把输入缓冲清空掉不就行了?

然而这是错的!
C

C++
的标准里从来没有定义过

fflush

stdin
)。也许有人会说:

可是我用

fflush

stdin


解决了这个问题,你怎么能说是错的呢?

的确,某些编译器(如
VC6
)支持用
fflush

stdin


来清空输入缓冲,但是并非所有编译器都要支持这个功能(
linux
下的
gcc
就不支持),因为标准中根本没有定义
fflush

stdin
)。
MSDN
文档里也清楚地写着
fflush
on input stream is an extension to the C standard

fflush
操作输入流是对
C

标准的扩充)。当然,如果你毫不在乎程序的移植性,用
fflush

stdin


也没什么大问题。以下是
C99

fflush
函数的定义:

  
int fflush

FILE
*stream
);

  如果
stream
指向输出流或者更新流(
update
stream
),

艺飧龈

铝髯罱

葱械牟僮鞑皇鞘淙耄


?fflush
函数将把这个流中任何待写数据传送至宿主环境(
host environment
)写入文件。否则,它的行为是未定义的。

  原文如下:

  
int fflush

FILE
*stream
);

 

 
If
stream points to an output stream or an update stream in which the most recent
operation was not input

the
fflush function causes any unwritten data for that stream to be delivered to
the host environment to be written to the file

otherwise

the behavior is undefined.

  其中,宿主环境可以理解为操作系统或内核等。

  由此可知,如果
stream
指向输入流(如

stdin
),那么
fflush
函数的行为是不确定的。故而使用
fflush

stdin
) 

是不正确的,至少是移植性不好的。

  
2.

 清空输入缓冲区的方法


  虽然不可以用
fflush

stdin
),但是我们可以自己写代码来清空输入缓冲区。只需要在
scanf
函数后面加上几句简单的代码就可以了。

  
/* C
版本
*/

  
#include <stdio.h>

  
int main( void )

  
{

  
int i, c;

  
for ( ; ; )

  
{

  
fputs("Please input an integer: ", stdout);

  
scanf("%d", &i);

  
if ( feof(stdin) || ferror(stdin) )

  
{ /*
如果用户输入文件结束标志(或文件已被读完),
*/

  
/*
或者发生读写错误,则退出循环    
*/

  
/* do something */

  
break;

  
}

  
/*
没有发生错误,清空输入流。    
*/

  
/*
通过

while
循环把输入流中的余留数据




*/

  
while ( (c = getchar()) != '/n' && c != EOF ) ;

  
/*
使用

scanf("%*[^/n]");
也可以清空输入流,
*/

  
/*
不过会残留
/n
字符。     
*/

  
printf("%d/n", i);

  
}

  
return 0;

  
}

  
/* C++
版本
*/

  
#include <iostream>

  
#include <limits> //
为了使用
numeric_limits

  
using std::cout;

  
using std::endl;

  
using std::cin;

  
using std::numeric_limits;

  
using std::streamsize;

  
int main()

  
{

  
int value;

  
for ( ; ; )

  
{

  
cout << "Enter an integer: ";

  
cin >> value;

  
if ( cin.eof() || cin.bad() )

  
{ //
如果用户输入文件结束标志(或文件已被读完),

  
//
或者发生读写错误,则退出循环

  
// do something

  
break;

  
}

  
//
读到非法字符后,输入流将处于出错状态,

  
//
为了继续获取输入,首先要调用

clear
函数

  
//
来清除输入流的错误标记,然后才能调用

  
// ignore
函数来清除输入流中的数据。

  
cin.clear();

  
// numeric_limits<streamsize>::max()
返回输入缓冲的大小。

  
// ignore
函数在此将把输入流中的数据清空。

  
//
这两个函数的具体用法请读者自行查询。

  
cin.ignore( numeric_limits<streamsize>::max(), '/n' );

  
cout << value << '/n';

  
}

  
return 0;

  
}

C/C++
误区三:强制转换
malloc()
的返回值

首先要说的是,使用

malloc
函数,请包含
stdlib.h

C++
中是

cstdlib
),而不是
malloc.h
。因为

malloc.h
从来没有在
C
或者
C++
标准中出现过!因此并非所有编译器都有
malloc.h
这个头文件。但是所有的
C
编译器都应该有

stdlib.h
这个头文件。


C++
中,强制转换
malloc()

的返回值是必须的,否则不能通过编译。但是在
C
中,这种强制转换却是多余的,并且不利于代码维护。

起初,
C
没有
void
指针,那时
char*

被用来作为泛型指针(
generic pointer
),所以那时

malloc
的返回值是
char*
。因此,那时必须强制转换

malloc
的返回值。后来,
ANSI C
(即
C89


标准定义了
void
指针作为新的泛型指针。
void
指针可以不经转换,直接赋值给任何类型的指针(函数指针除外)。从此,
malloc

的返回值变成了
void*

,再也不需要强制转换
malloc
的返回值了。以下程序在
VC6
编译无误通过。

#include <
stdlib.h>

int main( void )

{

double *p =
malloc( sizeof *p ); /*

不推荐用
sizeof( double ) */

free(p);

return 0;

}

当然,强制转换
malloc
的返回值并没有错,但画蛇添足!例如,日后你有可能把
double *p
改成
int *p
。这时,你就要把所有相关的
(double *) malloc (sizeof(double))
改成
(int
*)malloc(sizeof(int))
。如果改漏了,那么你的程序就存在
bug
。就算你有把握把所有相关的语句都改掉,但这种无聊乏味的工作你不会喜欢吧!不使用强制转换可以避免这样的问题,而且书写简便,何乐而不为呢?使用以下代

码,无论以后指针改成什么类型,都不用作任何修改。

double *p =
malloc( sizeof *p );

类似地,使用

calloc

realloc
等返回值为
void*

的函数时,也不需要强制转换返回值。

参考资料:

ISO/IEC
9899:1999 (E) Programming languages — C 7.20.3.3 The malloc function

ISO/IEC 9899:1999 (E) Programming languages — C P104 (6.7.2.2)

C/C++
误区四:
char c = getchar();

getchar
由宏实现:
#define getchar() fgetc(stdin)

getchar
有一个
int
型的返回值
.
当程序调用
getchar

.
程序就等着用户按键
.
用户输入的字符被存放在键盘缓冲区中
.
直到用户按回车为止
(


车字符也放在缓冲区中
).
当用户键入回车之后
,getchar
才开始从
stdio
流中每次读入一个字符
.getchar
函数的返回值是用户输入的第一个字

符的
ASCII

,
如出错返回
-1,
且将用户输入的字符回显到屏幕
.
如用户在按回车之前输入了不止一个字符
,
其他字符会保留在键盘缓存区中
,
等待后续
getchar
调用读取
.
也就是说
,
后续的
getchar
调用不会等待用户按键
,
而直接读取缓冲区中的字符
,
直到缓冲区中的字符读完为后
,
才等待用户按


.

getch

getchar
基本功能相同
,
差别是
getch
直接从键盘获取键值
,
不等待用户按

回车
,
只要用户按一个键
,getch
就立刻返回
,
getch
返回值是用户输入的
ASCII

,
出错返回
-1.
输入的字符不会回显在屏幕上
.getch
函数常用于程序调试中
,
在调试时
,
在关键位置显示有关

的结果以待查看
,
然后用
getch
函数暂停程序运行
,
当按任意键后程序继续运行
.
  这个版本忽略了个重点,
getch()
是非缓冲输入函数,就是不能用
getch
()来接受缓冲区已存在的字符,如以下
C++
程序,

  
int
i;while

cin>>i);cin.clear();getchar();
运行时如果输入
1 2 3
a
时必须用
getchar
()才能在后面程序获得正常输入,即使先前已经恢复流了,此处用
getch
()是万万不行的。

  另外补充个函数,
getche()
,这个函数与前两上类似,功能也相近,都是输入一个字符,返回值同样是输入字符的
ASCII
码,但不同的是,此函数在输入后立即从控制台取字符,不以回车为结束
(
带回显
)

许多初学者都习惯用
char
型变量接收

getchar

getc

fgetc
等函数的返回值,其实这么做是不对的,并且隐含着足以致命的错误


getchar

等函数的返回值类型都是
int

型,当这些函数读取出错

或者读完文件后

,会返回
EOF


EOF
是一个宏,标准规定它的值必须是一个
int

型的负数常量

。通常编译器都会把
EOF
定义为
-1

。问题就出在这里,使用
char
型变量接收
getchar
等函数的返回值会导致对
EOF
的辨认出错,或者错把好的数据误认为是
EOF
,或者把
EOF
误认为是好的数据。例如:

int c; /*

正确。应该使用
int
型变量接收
fgetc
的返回值
*/

while ( (c = fgetc(fp)) != EOF )

{

putchar(c);

}

如上例所示,我们很多时

候都需要先用一个变量接收
fgetc
等函数的返回值,然后再用这个变量和
EOF
比较,判断是否已经读完文件。上面这个例子是正确的,把
c
定义为
int
型保证了它能正确接收
fgetc
返回的
EOF
,从而保证了这个比较的正确性。但是,如果把

c
定义为
char
型,则会导致意想不到的后果



首先,因为
fgetc

等函数的返回值是
int
型的,当赋值给
char
型变量时,会发生
降级

,从而导致数据截断。例如:

---------------------------------

|
十进制
|
int | char |

|--------|--------------|-------|

| 10 | 00 00 00 0A | 0A |

| -1 | FF FF FF FF | FF |

| -2 | FF FF FF FE | FE |

---------------------------------

在此,我们假设
int

char
分别是
32
位和
8
位的。由上表可得,从
int
型到
char
型,损失了
3
个字节的数据。而当我们要拿
char
型和
int
型比较的时候,
char
型会自动
升级


int
型。
char
型升级为
int
型后的值会因为它到底是
signed char

还是
unsigned
char

而有所不同。不幸的是,如果我们没有使用

signed
或者
unsigned
来修饰
char
,那么我们无从知晓
char
到底是指

unsigned char
还是指
signed char
,因为这是由编译器决定

的。不过,无论
char


signed
的也好,
unsigned
的也罢,都不能改变使用
char
型变量接收
fgetc

等函数的返回值是错误

的这个事实。唯一能改变的是该错误导致的后果。前面我们说了,
char
型和
int
型比较时,
char
会自动升级为
int
,下面我们来看看

signed char

unsigned char
在转换成
int
后,它们的值有什么不同:

---------------------------------------

| char | unsigned |
signed |

|-------|---------------|-------------|

| 10 | 00 00 00 0A | 00 00 00 0A |

| FF | 00 00 00 FF | FF FF FF FF |

| FE | 00 00 00 FE | FF FF FF FE |

---------------------------------------

由上表可知,当
char


unsigned
的时候,其转换为
int
后的值是正数

。也就是说,假如我们把
c
定义为
char
型变量,而编译器默认
char

unsigned char
,那么以下表达式将永远成立



(c = fgetc(fp)) != EOF /* c

的值永远为正数,而标准规定
EOF
为负数
*/

也就是说以下循环是一个死循环



while ( (c
= fgetc(fp)) != EOF )

{

putchar(c);

}

读到这里,可能有些读者朋友会说:

那么我明确把
c
定义为

signed char
型的就没问题了吧!

很遗憾,就算把
c
定义为

signed char
,仍然是错误的。假设
fgetc
等函数读到一个字节的值为
FF

,那么返回值就是
00 00 00 FF

。把这个值赋值给
c
后,
c
的值变成
FF
。然后
c
的值为了和
EOF
比较,会自动升级为
int
型的值,也就是
FF FF FF FF

。从而导致以下表达式不成立



(c =
fgetc(fp)) != EOF /*

读到值为
FF
的字符,误认为
EOF */

也就是说以下循环在没有读完文件

的情况下提前退出



while ( (c
= fgetc(fp)) != EOF )

{

putchar(c);

}

综上所述,使用
char
型变量接收
fgetc

等函数的返回值是错误的,我们必须使用

int
型变量接收这些函数的返回值

,然后判断接收到的值是否
EOF
。只有判断发现该返回值并非
EOF
,我们才可以把该值赋值给
char
型变量。

同理,
C++
中,用
char
型变量接收
cin.get()

的返回值也是错误的。不过,把
char
型变量当作参数

传递给
cin.get

则是正确的。例如:

char c =
cin.get(); //

错误,理由同上

char c;

cin.get(c); //

正确

C/C++
误区五:检查
new
的返回值

首先澄清一下,这个误区仅对
C++
成立,这里不过是沿用
“C/C++
误区

这个衔头罢了。

我们都知道,使用

malloc/calloc
等分配内存的函数时,一定要检查其返回值是否为

空指针

(亦即检查分配内存的操作是否成功),这是良好的编程习惯,也是编写可靠程序所必需的。但是,如

果你简单地把这一招应用到
new
上,那可就不一定正确了。我经常看到类似这样的代码:

int* p =
new int[SIZE];

if ( p == 0 ) //
检查
p
是否空指针

return -1;

//
其它代码

其实,这里的
if (
p == 0 )
完全是没啥意义的。
C++
里,如果
new
分配内存失败,默认是抛出异常

的。所以,如果分配成功,
p == 0
就绝对不会成立;而如果分配失败了,也不会执行
if (
p == 0 )
,因为分配失败时,
new
就会抛出异常跳过后面的代码

。如果你想检查
new
是否成功,应该捕捉异常



try {

int* p = new
int[SIZE];

//
其它代码

} catch ( const bad_alloc& e ) {

return -1;

}

据说一些老的编译器里,
new
如果分配内存失败,是不抛出异常的(大概是因为那时
C++
还没加入异常机制),而是和

malloc
一样,返回空指针。不过我从来都没遇到过
new
返回空指针的情况。

当然,标准
C++
亦提供了一个方法来抑制

new
抛出异常

,而返回空指针:

int* p = new (std::nothrow) int; //
这样如果
new
失败了,就不会抛出异常,而是返回空指针

if ( p == 0 ) //
如此这般,这个判断就有意义了

return -1;

//
其它代码
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: