云题海 - 专业文章范例文档资料分享平台

当前位置:首页 > C语言陷阱和缺陷

C语言陷阱和缺陷

  • 62 次阅读
  • 3 次下载
  • 2025/12/12 4:02:47

#define NULL 0

但其效果是相同的。要记住的一个重要的事情是,当用0作为指针时它决不能被解除引用。换句话说,当你将0赋给一个指针变量后,你就不能访问它所指向的内存。不能这样写: if(p == (char *)0) ... 也不能这样写:

if(strcmp(p, (char *)0) == 0) ...

因为strcmp()总是通过其参数来查看内存地址的。 如果p是一个空指针,这样写也是无效的: printf(p); 或

printf(\4.8 整数溢出

C语言关于整数操作的上溢或下溢定义得非常明确。

只要有一次操作数是无符号的,结果就是无符号的,并且以2^n为模,其中n为字长。如果两个操作数都是带符号的,则结果是未定义的。

例如,假设a和b是两个非负整型变量,你希望测试a + b是否溢出。一个明显的办法是这样的: if(a + b < 0) complain();

通常,这是不会工作的。

一旦a + b发生了溢出,对于结果的任何赌注都是没有意义的。例如,在某些机器上,一个加法运算会将一个内部寄存器设置为四种状态:正、负、零或溢出。 在这样的机器上,编译器有权将上面的例子实现为首先将a和b加在一起,然后检查内部寄存器状态是否为负。如果该运算溢出,内部寄存器将处于溢出状态,这个测试会失败。

使这个特殊的测试能够成功的一个正确的方法是依赖于无符号算术的良好定义,既要在有符号和无符号之间进行转换:

if((int)((unsigned)a + (unsigned)b) < 0) complain(); 4.9 移位运算符

两个原因会令使用移位运算符的人感到烦恼:

在右移运算中,空出的位是用0填充还是用符号位填充? 移位的数量允许使用哪些数? 第一个问题的答案很简单,但有时是实现相关的。如果要进行移位的操作数是无符号的,会移入0。如果操作数是带符号的,则实现有权决定是移入0还是移入符号位。如果在一个右移操作中你很关心空位,那么用unsigned来声明变量。这样你就有权假设空位被设置为0。

第二个问题的答案同样简单:如果待移位的数长度为n,则移位的数量必须大于等于0并且严格地小于n。因此,在一次单独的操作中不可能将所有的位从变量中移出。

例如,如果一个int是32位,且n是一个int,写n << 31和n << 0是合法的,但n << 32和n << -1是不合法的。

注意,即使实现将符号为移入空位,对一个带符号整数的右移运算和除以2的某次幂也不是等价的。为了证明这一点,考虑(-1) >> 1的值,这是不可能为0的。[译注:(-1) / 2的结果是0。] 5 库函数

每个有用的C程序都会用到库函数,因为没有办法把输入和输出内建到语言中去。在这一节中,我们将会看到一些广泛使用的库函数在某种情况下会出现的一些非预期行为。 5.1 getc()返回整数 考虑下面的程序: #include main() { char c;

while((c = getchar()) != EOF) putchar(c); }

这段程序看起来好像要讲标准输入复制到标准输出。实际上,它并不完全会做这些。

原因是c被声明为字符而不是整数。这意味着它将不能接收可能出现的所有字符包括EOF。

因此这里有两种可能性。有时一些合法的输入字符会导致c携带和EOF相同的值,有时又会使c无法存放EOF值。在前一种情况下,程序会在文件的中间停止复制。在后一种情况下,程序会陷入一个无限循环。

实际上,还存在着第三种可能:程序会偶然地正确工作。C语言参考手册严格地定义了表达式

((c = getchar()) != EOF) 的结果。

其6.1节中声明:

当一个较长的整数被转换为一个较短的整数或一个char时,它会被截去左侧;超出的位被简单地丢弃。 7.14节中声明:

存在着很多赋值运算符,它们都是从右至左结合的。它们都需要一个左值作为左侧的操作数,而赋值表达式的类型就是其左侧的操作数的类型。其值就是已经付过值的左操作数的值。

这两个条款的组合效果就是必须通过丢弃getchar()的结果的高位,将其截短为字符,之后这个被截短的值再与EOF进行比较。作为这个比较的一部分,c必须被扩展为一个整数,或者采取将左侧的位用0填充,或者适当地采取符号扩展。 然而,一些编译器并没有正确地实现这个表达式。它们确实将getchar()的值的低几位赋给c。但在c和EOF的比较中,它们却使用了getchar()的值!这样做的编译器会使这个事例程序看起来能够“正确地”工作。 5.2 缓冲输出和内存分配

当一个程序产生输出时,能够立即看到它有多重要?这取决于程序。

例如,终端上显示输出并要求人们坐在终端前面回答一个问题,人们能够看到输出以知道该输入什么就显得至关重要了。另一方面,如果输出到一个文件中,并最终被发送到一个行式打印机,只有所有的输出最终能够到达那里是重要的。 立即安排输出的显示通常比将其暂时保存在一大块一起输出要昂贵得多。因此,C实现通常允许程序员控制产生多少输出后在实际地写出它们。

这个控制通常约定为一个称为setbuf()的库函数。如果buf是一个具有适当大小的字符数组,则

setbuf(stdout, buf);

将告诉I/O库写入到stdout中的输出要以buf作为一个输出缓冲,并且等到buf满了或程序员直接调用fflush()再实际写出。缓冲区的合适的大小在中定义为BUFSIZ。

因此,下面的程序解释了通过使用setbuf()来讲标准输入复制到标准输出: #include main() { int c;

char buf[BUFSIZ]; setbuf(stdout, buf);

while((c = getchar()) != EOF) putchar(c); }

不幸的是,这个程序是错误的,因为一个细微的原因。

要知道毛病出在哪,我们需要知道缓冲区最后一次刷新是在什么时候。答案:主程序完成之后,作为库在将控制交回到操作系统之前所执行的清理的一部分。在这一时刻,缓冲区已经被释放了! 有两种方法可以避免这一问题。

首先,是用静态缓冲区,或者将其显式地声明为静态: static char buf[BUFSIZ]; 或者将整个声明移到主函数之外。

另一种可能的方法是动态地分配缓冲区并且从不释放它: char *malloc();

setbuf(stdout, malloc(BUFSIZ));

注意在后一种情况中,不必检查malloc()的返回值,因为如果它失败了,会返回一个空指针。而setbuf()可以接受一个空指针作为其第二个参数,这将使得stdout变成非缓冲的。这会运行得很慢,但它是可以运行的。 6 预处理器

搜索更多关于: C语言陷阱和缺陷 的文档
  • 收藏
  • 违规举报
  • 版权认领
下载文档10.00 元 加入VIP免费下载
推荐下载
本文作者:...

共分享92篇相关文档

文档简介:

#define NULL 0 但其效果是相同的。要记住的一个重要的事情是,当用0作为指针时它决不能被解除引用。换句话说,当你将0赋给一个指针变量后,你就不能访问它所指向的内存。不能这样写: if(p == (char *)0) ... 也不能这样写: if(strcmp(p, (char *)0) == 0) ... 因为strcmp()总是通过其参数来查看内存地址的。 如果p是一个空指针,这样写也是无效的: printf(p); 或 printf(\4.8 整数溢出 C语言关于整数操作的上溢或下溢定义得非常明确。 只要有一次操作数是无符号的,结果就是无符号的,并且以2^n为模,其中n为字长。如果两个操作数都是带符号的,则结果是未定义的。 例如,假设a和b是两个非负整型变量,你希望测试a

× 游客快捷下载通道(下载后可以自由复制和排版)
单篇付费下载
限时特价:10 元/份 原价:20元
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
VIP包月下载
特价:29 元/月 原价:99元
低至 0.3 元/份 每月下载150
全站内容免费自由复制
注:下载文档有可能“只有目录或者内容不全”等情况,请下载之前注意辨别,如果您已付费且无法下载或内容有问题,请联系我们协助你处理。
微信:fanwen365 QQ:370150219
Copyright © 云题海 All Rights Reserved. 苏ICP备16052595号-3 网站地图 客服QQ:370150219 邮箱:370150219@qq.com