• 欢迎访问搞代码网站,推荐使用最新版火狐浏览器和Chrome浏览器访问本网站!
  • 如果您觉得本站非常有看点,那么赶紧使用Ctrl+D 收藏搞代码吧

C标准库的实现详解

c语言 搞代码 4年前 (2022-01-06) 45次浏览 已收录 0个评论

这篇文章主要介绍了C标准库的实现,主要包括了的基本概念、实现及用法等,需要的朋友可以参考下

本文实例讲解了C标准库的实现过程及相关用法。分享给大家供大家参考。具体分析如下:

一、背景知识

头文件唯一的目的就是提供assert宏定义,可以在程序中关键的地方使用这个宏来进行断言。如果一处断言被证明非真,希望程序在标准错误流输出一条适当的提示信息,并使执行异常终止。

可以这样写代码:

 #include ... assert(0 <= i && i <sizeof(a) / sizeof(a[0])); 

当然上面的代码不是实战中的最好的形式,程序异常终止应该改为某种错误的恢复。

宏NDEBUG

可以通过在程序的某些地方定义宏NDEBUG来改变assert的展开方式

如果程序某个包含assert的地方没有定义NDEBUG,该头文件就会将宏assert定义为活动形式,它就可以展开为一个表达式,测试断言并在断言为假的时候输出一条错误信息,然后程序终止。反之,如果定义了NDEBUG,头文件就会把这个宏定义为不执行任何操作的静止形式。

二、的使用

来源gao!%daima.com搞$代*!码$网

从上面的代码中可以看到,可以使用一个简单的谓词来简化assert:

 if(!ok) abort(); //在头文件中声明 

如果觉得断言没有存在的必要,就在包含头文件之前加上下面的代码:

 #define NDEBUG //取消断言 #include

可以在整个源文件中用不同的方式控制断言,当断言在频繁执行的循环内部发生时,性能可能会急剧下降,或在达到提示性的部分之前,一个更早的断言可能会终止程序。要打开断言,可以写:

 #undef NDEBUG #include

要关闭断言,可以写:

 #define NDEBUG #include

注意:即使宏NDEBUG已经被定义了,我们仍然可以安全地定义它,这是一个良性重定义

三、的实现

从上面的分析知该头文件的大致框架如下:

 #undef assert //消除已定义的 #ifdef NDEBUG #define assert(expr) ((void) 0) //功能失效 #else #define assert (expr) ... #endif 

一个简单的编写宏assert的活动形式的方式如下:

 #define assert(expr) if(!(expr)) \ fprintf(stderr, "Assertion failed: %s, file %s, line %i\n", \ #expr, __FILE__, __LINE__) 

这种方式因为如下几种原因不能接受:

1、宏不能直接调用库的任何输出函数

上面的定义中包含fprintf、stderr等在stdio.h中定义的函数或宏,程序可能没有包含这个头文件

2、宏必须能扩展为一个void类型的表达式

3、宏应该可以扩展为有效并且紧凑的代码

这个版本却总是调用了一个传递了5个参数的函数

修改后的assert宏如下:

 #undef assert #ifdef NDEBUG #define assert(expr) ((void) 0) #else void __bad_assertion (const char *_mess); #define  __str(x)  # x #define  __xstr(x)  __str(x) #define  assert(expr)  ((expr)? (void)0 : \ __bad_assertion("Assertion \"" #expr \ "\" failed, file " __xstr(__FILE__) \ ", line " __xstr(__LINE__) "\n")) #endif 

其中__LINE__ 是内置宏,代表该行代码的所在行号,由于__LINE__没有扩展成字符串字面量,它变成了一个十进制常量,把它转换成适当的形式需要一个额外的处理层。向头文件中添加两个隐藏的宏__str和__xstr来实现,其中一个宏用它的十进制常量扩展来取代__LINE__,另一个是把十进制常量转换成一个字符串字面量

宏调用的隐藏库函数__bad_assertion的实现:

 #include #include #include void __bad_assertion(const char *mess) { fputs(mess, stderr); abort(); } 

函数__bad_assertion使用了两个其他的库函数,通过调用中声明的函数fputs把字符串写到标准错误流,并使用abort异常终止程序的执行,有关这些相关头文件以后会详细剖析。

四、的测试

 #include #include #include int main( void ) { FILE *fp; fp = fopen( "test.txt", "w" );//以可写的方式打开一个文件,如果不存在就创建一个同名文件 assert( fp );              //所以这里不会出错 fclose( fp ); fp = fopen( "noexitfile.txt", "r" );//以只读的方式打开一个文件,如果不存在就打开文件失败 assert( fp );              //所以这里出错 fclose( fp );              //程序永远都执行不到这里来 return 0; } 

注意:

1.在函数开始处检验传入参数的合法性如:

 int resetBufferSize(int nNewSize) {   //功能:改变缓冲区大小,   //参数:nNewSize 缓冲区新长度   //返回值:缓冲区当前长度   //说明:保持原信息内容不变   nNewSize= 0);   assert(nNewSize <= MAX_BUFFER_SIZE);   ... } 

2.每个assert只检验一个条件,因为同时检验多个条件时,如果断言失败,无法直观的判断是哪个条件失败,如:

 assert(nOffset>=0 && nOffset+nSize= 0); assert(nOffset+nSize <= m_nInfomationSize); 

3.不能使用改变环境的语句,因为assert只在DEBUG个生效,如果这么做,会使用程序在真正运行时遇到问题,如:

错误:

 assert(i++ <100); 

这是因为如果出错,比如在执行之前i=100,那么这条语句就不会执行,那么i++这条命令就没有执行。

正确:

 assert(i <100); i++; 

4.assert和后面的语句应空一行,以形成逻辑和视觉上的一致感。

5.在有的地方,assert不能代替条件过滤。

相信本文所述对大家C程序设计的学习有一定的借鉴价值。

以上就是C标准库的实现详解的详细内容,更多请关注gaodaima搞代码网其它相关文章!


搞代码网(gaodaima.com)提供的所有资源部分来自互联网,如果有侵犯您的版权或其他权益,请说明详细缘由并提供版权或权益证明然后发送到邮箱[email protected],我们会在看到邮件的第一时间内为您处理,或直接联系QQ:872152909。本网站采用BY-NC-SA协议进行授权
转载请注明原文链接:C标准库的实现详解

喜欢 (0)
[搞代码]
分享 (0)
发表我的评论
取消评论

表情 贴图 加粗 删除线 居中 斜体 签到

Hi,您需要填写昵称和邮箱!

  • 昵称 (必填)
  • 邮箱 (必填)
  • 网址