`
mmdev
  • 浏览: 13237343 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

【C陷阱和缺陷】库函数

 
阅读更多

一,返回整数的getchar函数

我们来看下面的程序:

#include "stdio.h"
void main()
{
    char c;
    while ( (c = getchar()) != EOF )
    {

        putchar( c );

    }
}

看起来这段程序应该把它的标准输入拷贝到输出,实际运行情况并非如此。

原因在于 c 声明为 char 型而非 int 型,这就意味着 c 不能容下所有可能的字符,特别是可能无法容下EOF

所以,有两种可能:或者某些合法字符被“截断”后,使得c的取值与EOF相同,此时程序在文件复制中途终止;或者 c 根本不可能捕捉到EOF,程序将进入死循环。

实际上,还有第三种情况,程序将随机运行。C 参考手册非常严格地定义了表达式 ( (c = getchar()) != EOF ) 的值。

1)当一个长整型数据被转换为短整型或字符型时,左端被删掉,多余的位被简单的忽略。

2)所有赋值操作符的组合方向都是从右至左。它们都要求有一个左值,并且赋值表达式的类型就是它的左操作数的类型。表达式的值就是赋值后的左操作数的值。

综合以上两点看,getchar( ) 的值要被删掉高位数据,删节后的值再与EOF相比。作为比较的一部分,c 必须要扩展为整型,扩展时左端或者补 0 ,或者补符号位,由具体情况而定。

有些编译器将不能正确地执行这个表达式。它们可能把 getchar( ) 的低位数据赋给 c 。但是,它们不是比较 c 和EOF,而是比较 getchar( ) 和EOF。如果编译器这样做,将会同样只是看起来“工作正常”

二,更新(同时读写)顺序文件

FILE *file;

fopen(filePath,"r+");

执行上述代码后,并不能对文件进行交替的读和写一个输入不能同时紧跟一个输出,反之亦然。

如果要同时进行输入和输出操作,必须在其中插入fseek的操作。

例如:正确写法

#include <stdio.h>
void main( void )
{
	FILE *stream;
	char list[30];
	int i, numread, numwritten;
	/* Open file in text mode: */
	if( (stream = fopen( "test.txt", "w+t" )) != NULL )
	{
		for ( i = 0; i < 25; i++ )
			list[i] = (char)('z' - i);
		/* Write 25 characters to stream */
		numwritten = fwrite( list, sizeof( char ), 25, stream );
		printf( "Wrote %d items\n", numwritten );
		fclose( stream );
	}
	else
		printf( "Problem opening the file\n" );

	if( (stream = fopen( "test.txt", "r+t" )) != NULL )
	{
		/* Attempt to read in 25 characters */
		numread = fread( list, sizeof( char ), 25, stream );
		printf( "Number of items read = %d\n", numread );
		printf( "Contents of buffer = %.25s\n", list );
		fclose( stream );
	}
	else
		printf( "File could not be opened\n" );

	getchar();
}

错误代码:

numwritten = fwrite( list, sizeof( char ), 25, stream );
printf( "Wrote %d items\n", numwritten );

fseek(stream,0L,1); // 不加这一句就会显示乱码(写完后要将指针指向开头)

numread = fread( list, sizeof( char ), 25, stream );
printf( "Number of items read = %d\n", numread );
printf( "Contents of buffer = %.25s\n", list );


三,缓冲输出和内存分配

#include <stdio.h>
//char buf[BUFSIZ]; //BUFSIZE由<stdio.h>定义
int main()
{
char c;
static char buf[5]; //BUFSIZE由<stdio.h>定义
//所有输入到stdout中的输出都应该先输入到buf中
//当buf满时或显示调用fflush时,才将缓冲区中的内容写入stdout


setbuf(stdout,buf);

while((c=getchar())!=0)
{
putchar(c);
fflush(stdout);
}
getchar();
return 0;
}

上面程序是错误的,因为程序将控制交给操作系统前c运行库会将buf清空,但在这之前buf字符数组已被释放。

解决方法:

1)将buf声明为静态:static char buf[BUFSIZE];或者将buf声明放到main之外;

2)动态分配缓冲区,程序中并不主动释放缓冲区。因为是动态分配的,所以main函数结束时并不释放缓冲区。

char *malloc(); setbuf(stdout,malloc(BUFSIZ));

这种情况下,没有必要去判断 malloc( ) 是否成功,因为如果失败了,它会返回一个空指针。空指针可以作为 setbuff( ) 的第二个参数,它意味着不被缓冲。这运行起来会比较慢,但它会正常运行。

四,使用errno检测错误

很多库函数在执行失败时会通知一个名字为errno的外部变量,通知程序该函数调用失败 。

1)/*调用库函数*/

if(errno)

/*处理错误*/

这样做是错误的,因为库函数在调用成功时并强制要求将errno设置为0,这样errno就可能是前一个库函数执行失败时的值。

2)errno = 0;

/*调用库函数*/

if(errno)

/*处理错误*/

这行代码还是错的。例如当fopen被要求创建一个新的文件供程序输出时,如果存在一个同名文件,fopen会先删除它,然后新建一个文件。这样fopen会调用另一个库函数检测是否有同名文件存在。假设用于检测同名文件的函数在文件不存在时会设置errno,则fopen每创建一个事先并不存在的文件时,即使程序没错,errno也会被设置。

解决方法:

在调用库函数时,先检测作为错误提示的返回值,再检测errno找出错误原因。

/*调用库函数*/ if(返回的错误值) 检测errno /*处理错误*/

五,库函数signal

singal在<signal.h>中声明。

signal(signal type,handler function);

signal type 代表系统头文件signal.h中定义的常量,标示要捕获的信号类型。Handler function 为事件发生时要加以调用的事件处理函数。

Malloc时调用signal,从signal中使用longjmp退出,都是不安全的。

信号非常复杂棘手,而且具有一些在本质上不可移植的特性。因此应该使signal处理函数尽可能的简单,并将它们组织在一起,将来可以很容易修改。

Exercise 5.1

当一个程序异常终止时,程序输出的最后几行通常会丢失,什么原因?解决办法?

Answer:当程序使用输出缓冲区时,如果程序异常终止,可能还没来得及清空缓冲区,此时这些输出可能会存在内存而不被写出了。这种情况会给调试这类程序的编程者造成一种假象,程序发生失败的时刻比实际上运行失败的真正时刻要早得多。

解决方法:在调试时强制不允许对输出进行缓冲:setbuf(stdout, (char*)0);

这一句必须在任何输出被写入到stdout之前执行。最好作为 mian函数的第一句。

Exercise 5.2
#include <stdio.h>

int main()

{

register int c;

while((c=getchar())!=EOF)

putchar(c);

return 0;

}
#define EOF -1

int main()

{

register int c;

while((c=getchar())!=EOF)

putchar(c);

return 0;

}


右边的程序在某些系统中仍然可运行,但运行的慢,原因?

答案:函数调用需要较大的开销,因此getchar和putchar常被在stdio.h中定义为宏,同时很多c语言的实现在库文件中也包含这两个函数。当不包含<stdio.h>头文件时,使用的是库函数getchar而不是getchar宏,所以程序变慢。


分享到:
评论

相关推荐

    C语言缺陷和陷阱.pdf

    根据提供的文件内容,我们可以提炼出一些关于C语言的编程...这些知识点是从文件内容中提炼出的关于C语言缺陷和陷阱的一些常见问题和解决方案。实际编程中,开发者应当保持警惕,尽可能规避这些陷阱,编写健壮的代码。

    C语言陷阱和缺陷与读书笔记word档

    这份"**C语言陷阱和缺陷与读书笔记word档**"正是为了帮助初学者避开这些潜在问题而编写的。 一、内存管理 在C语言中,程序员需要手动管理内存,这可能导致内存泄漏或野指针。当忘记释放已分配的内存时,就会发生...

    C陷阱与缺陷-建议与答案

    《C陷阱与缺陷》是人民邮电出版社2008年出版的书籍,作者是(美)凯尼格。全书分为8章,分别从词法分析、语法语义、连接、库函数、预处理器、可移植性缺陷等几个方面分析了C编程中可能遇到的问题。最后,作者用一章...

    C陷阱与缺陷-答案-练习答案

    《C陷阱与缺陷》是一本深入探讨C语言编程中常见问题和陷阱的经典著作。这本书旨在帮助程序员避免在编码过程中遇到的潜在错误,提高代码质量和可维护性。通过对书中的内容进行解析,我们可以提取出一系列重要的知识点...

    C语言缺陷与陷阱(笔记).doc

    C语言作为一种强大的编程工具,因其低级特性而备受程序员喜爱,但也因其潜在的缺陷和陷阱而让初学者困惑。这篇笔记详细探讨了C语言在不同层面可能存在的问题,旨在帮助开发者更好地理解和避免这些陷阱。 1. 词法...

    c缺陷与陷阱(笔记)

    预处理器、库函数和编译器实现可能在不同系统上有所不同,因此编写跨平台的代码需要谨慎处理这些问题。 总的来说,理解和避免C语言的缺陷和陷阱需要深入理解语言的细节,包括词法分析、语法结构、运算符行为以及预...

    C语言陷阱解读

    ### C语言陷阱解读 #### 一、概述 C语言作为一种高效且广泛应用的编程语言,在软件开发领域占据着重要地位。然而,尽管C语言简洁且功能强大,但它也存在一些潜在的陷阱和缺陷,这些陷阱可能会导致程序员在编写代码...

    从缺陷中学习C/C++初稿

    软件开发的道路上,充满了各种陷阱和障碍,比如基础问题、编译问题、库函数使用错误、文件处理不当、类和对象的不当使用、内存管理问题、多线程编程失误以及性能调优问题等。 在编程实践过程中,通过直接面对并解决...

    C缺陷与陷阱(笔记)

    【C缺陷与陷阱】这篇笔记主要探讨了C语言中容易造成误解和问题的特性,旨在帮助程序员避免因不熟悉语言细节而引发的错误。以下是笔记的主要内容概览: 1. **词法缺陷**: - **= 不是 ==**:在C语言中,单个等号`=`...

    c语言的 chm 文件

    10. **C陷阱与缺陷.pdf.qdt**:这个PDF文件可能讨论了C语言中常见的陷阱和错误,帮助开发者避免编程中的常见问题。 这些资源对于学习和提升C语言编程技能是非常宝贵的。通过阅读这些教程和文档,开发者可以了解...

    C++程序设计陷阱

    《C++程序设计陷阱》是Stephen C. Dewhurst所著的一本关于C++编程的书籍,专注于揭示在C++编程过程中可能遇到的各种问题和陷阱。这本书深入浅出地讲解了可能导致程序错误或难以维护的因素,旨在帮助程序员避免这些...

    C_language_bug.rar_贝尔实验室

    《C语言陷阱与缺陷》是源自贝尔实验室的一部经典之作,由C语言领域的权威专家精心编纂。这本书深入探讨了C语言中常见的编程错误、陷阱和潜在问题,旨在帮助程序员避免这些常见错误,提高代码质量和安全性。以下是该...

    C语言学习的一些常见资源,希望能对你有所帮助

    此外,还可以深入学习经典的C语言书籍,如《C程序设计语言》、《C陷阱与缺陷》,理解C语言的细节和陷阱。了解C语言编译器原理和实现,有助于优化代码性能和可维护性。通过研究C语言在操作系统、嵌入式系统、游戏开发...

    C语言资料大全1.0

    4. **经典书籍**:C语言的经典教材如《C程序设计语言》(K&R)和《C陷阱与缺陷》等书籍的电子版可能也在其中。这些书籍提供了深入的理解和洞察,是任何C程序员的宝贵资源。 5. **编程习题**:为了检验和提高编程...

    C语言避坑指南.pdf

    然而,与其他编程语言一样,C语言也存在一些陷阱和缺陷,如果不小心处理,可能会导致程序出现难以预料的行为甚至崩溃。本文旨在帮助开发者识别和避免C语言中的常见陷阱,提高代码质量。 #### 二、词法缺陷 ##### ...

    MISRA C 2012中文版

    MISRA C 2012的核心理念是通过限制和指导程序员使用C语言的某些特性,来避免可能导致软件缺陷的常见编程陷阱。这些规范涵盖了变量定义、类型转换、运算符使用、指针操作、内存管理、预处理器等多个方面。 1. 变量...

    MISRA C 2004_中文版.pdf

    MISRA C 2004是一个由汽车产业软件可靠性协会(MISRA)发布的C语言开发标准,旨在提高在汽车行业中软件开发的安全性和可靠性。该标准针对实时嵌入式系统的C语言编程,在汽车行业中的应用越来越广泛,这主要得益于...

    怎么学习C语言呢?这篇告诉你学习路径

    《C陷阱与缺陷》 - **作者**:Andrew Koenig。 - **内容**:介绍常见的编程错误及其解决方案。 - **特点**:通过实际案例提高编程质量。 通过以上详细的学习路径,初学者可以系统地掌握C语言的基础知识,并逐步...

Global site tag (gtag.js) - Google Analytics