zoukankan      html  css  js  c++  java
  • 【C语言学习趣事】_GCC源代码分析_2_assert.h

      我记得在以前的一篇随笔中,我堆windows下的<assert.h>进行了分析,今天我们来看看gcc中这个文件的定义是怎样的。

    【1】assert宏的作用

      assert宏实现断言的作用,一般在源文件中引用格式如下:

    #include <assert.h>
    
    #undef NDEBUG
    assert(expression)

    关于assert宏:

      1、当 expression的值为0时进行断言,如果表达式expression的值非零,则不进行断言。

      2、assert宏进行断言的时候,在标准错误输出中输出断言发生的源文件名称:__FILE__ 和断言发生时语句所在的行: __LINE__

      3、可在程序的调试过程中,利用assert宏进行关键点程序进行测试,以输出一些有用的信息,当不需要调试的时候,可以通过定义NDEBUG宏来取消

    宏assert的作用。

    【2】assert.h

    /* Allow this file to be included multiple times
       with different settings of NDEBUG.  */
    
    //assert 为C库提供的一种断言机制
    //断言用来在标准错误输出流输出信息,并且使程序异常终止
    /*
        断言的机制:
    
    */
    
    //首先取消 assert 宏的定义,
    //这样做的目的是为了防止宏重复被定义
    #undef assert
    #undef __assert
    
    //通过判断是否定义宏 NDEBUG 来判断在源代码中是否需要宏assert
    /*
        如果定义了 NDEBUG 宏,就表示不需要在程序中引用 assert 宏
        NDEBUG: do not debug
        否则就在程序中,assert 宏将被执行
    
        可以发现assert宏在定义 NDEBUG时,定义很特别,宏参数并没有引用
    */
    #ifdef NDEBUG
        //定义了NDEBUG宏,assert 宏定义为不做任何提示输出
         #define assert(ignore)  ((void)0)
    #else
        void __eprintf ();        /* Defined in gnulib */
        #ifdef __STDC__    //定义了__STDC__宏
            #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__)))
            #define __assert(expression, file, lineno)  \
                  (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n",    \
                      expression, lineno, file), 0)
        #else /* no __STDC__; i.e. -traditional.  */
            #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__)))
            #define __assert(expression, file, lineno)  \
                  (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n",    \
                      "expression", lineno, file), 0)
        #endif /* no __STDC__; i.e. -traditional.  */
    #endif
    View Code

    【3】第一段预处理

    //首先取消 assert 宏的定义,
    //这样做的目的是为了防止重复定义宏的影响
    #undef assert
    #undef __assert

      这样做的目的是为了防止 assert 重定义,引起混乱;这样在引用的位置处,就可以取消前面的定义。

    【4】标准assert宏定义结构

      通常assert宏定义具有下面的结构

          

      这样结构的目的是为了对用户的 NDEBUG 宏做出正确的响应。

    【5】宏代码

    //通过判断是否定义宏 NDEBUG 来判断在源代码中是否需要宏assert
    /*
        如果定义了 NDEBUG 宏,就表示不需要在程序中引用 assert 宏
        NDEBUG: do not debug
        否则就在程序中,assert 宏将被执行
    
        可以发现assert宏在定义 NDEBUG时,定义很特别,宏参数并没有引用
    */
    #ifdef NDEBUG
        //定义了NDEBUG宏,assert 宏定义为不做任何提示输出
         #define assert(ignore)  ((void)0)
    #else
        void __eprintf ();        /* Defined in gnulib */
        #ifdef __STDC__    //定义了__STDC__宏
            #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__)))
            #define __assert(expression, file, lineno)  \
                  (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n",    \
                      expression, lineno, file), 0)
        #else /* no __STDC__; i.e. -traditional.  */
            #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__)))
            #define __assert(expression, file, lineno)  \
                  (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n",    \
                      "expression", lineno, file), 0)
        #endif /* no __STDC__; i.e. -traditional.  */
    #endif

      这里我们可以看到,这个宏,其实是定义了两个红: assert 和 __assert

    1、assert

      分两种情况: 

    定义了 __STDC__ 宏时:

    #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__)))

    未定义了 __STDC__ 宏时:

    #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__)))

      这两者的区别,仅在于 “#” 连接符的作用,如果不用  #expression 这种形式,则宏参数不能使 带引号的字符串, 而用了

    #expression 这种形式,assert宏的实际参数既可以是带引号的字符串。

    两个预定义宏:

      __FILE__: 返回C成员源文件名

      __LINE__ :返回代码行在C文件中的行数

    2、__assert

      这个宏只有一种形式:

    #define __assert(expression, file, lineno)  \
                  (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n",    \
                      "expression", lineno, file), 0)

      这个宏,其实是引用了函数 __eprintf() 函数来实现输出,这个函数不是标准的库函数。如果要实现标准assert宏,则不能引用标准库

    函数实现,这是因为如果在程序中如果没有包含这些库函数,则会引起异常。

    __eprintf() 函数: 

    #ifdef L_eprintf
    #include <stdio.h>
    /* This is used by the `assert' macro.  */
    void
    __eprintf (string, expression, line, filename)
         char *string;
         char *expression;
         int line;
         char *filename;
    {
      fprintf (stderr, string, expression, line, filename);
      fflush (stderr);
      abort ();
    }
    #endif

      就是说这个头文件还可以这样:

    #ifdef NDEBUG
        //定义了NDEBUG宏,assert 宏定义为不做任何提示输出
         #define assert(ignore)  ((void)0)
    #else
        void __eprintf ();  /* Defined in gnulib */
        #define __assert(expression, file, lineno)  \
                  (__eprintf ("Failed assertion `%s' at line %d of `%s'.\n",    \
                      expression, lineno, file), 0)    
        #ifdef __STDC__    //定义了__STDC__宏
            #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (#expression, __FILE__, __LINE__)))    
        #else /* no __STDC__; i.e. -traditional.  */
            #define assert(expression)  \
                  ((void) ((expression) ? 0 : __assert (expression, __FILE__, __LINE__)))
        #endif /* no __STDC__; i.e. -traditional.  */
    #endif

    【6】微软VS 2008 代码比较

    /***
    *assert.h - define the assert macro
    *
    *       Copyright (c) Microsoft Corporation. All rights reserved.
    *
    *Purpose:
    *       Defines the assert(exp) macro.
    *       [ANSI/System V]
    *
    *       [Public]
    *
    ****/
    
    #include <crtdefs.h>
    
    #undef  assert
    
    #ifdef  NDEBUG
    
    #define assert(_Expression)     ((void)0)
    
    #else
    
    #ifdef  __cplusplus
    extern "C" {
    #endif
    
    _CRTIMP void __cdecl _wassert(_In_z_ const wchar_t * _Message, _In_z_ const wchar_t *_File, _In_ unsigned _Line);
    
    #ifdef  __cplusplus
    }
    #endif
    
    #define assert(_Expression) (void)( (!!(_Expression)) || (_wassert(_CRT_WIDE(#_Expression), _CRT_WIDE(__FILE__), __LINE__), 0) )
    
    #endif  /* NDEBUG */

      不知道,有没有专门介绍gcc编译器源代码的书籍,我知道有一本书gcc internal, 是英语版的,看起来很废力,有没有人知道中文的这样的书籍;

    那位大侠不吝赐教,告诉我一本这样的书,或者这样的论坛,我找了好久也没有发现一个合意的。

  • 相关阅读:
    Halcon HWindowControl 控件中图像的缩放与移动
    OpenCV cv::mean()函数详解
    OpenCV 霍夫变换(Hough Transform)
    OpenCV 图像分割
    OpenCV 使用ROI进行图像切割
    OpenCV 轮廓查找与绘制-最小外接矩形
    OpenCV 提取轮廓的凸包、外包矩形、最小外包矩形、最小外包圆
    KubeDL 加入 CNCF Sandbox,加速 AI 产业云原生化
    DataWorks 功能实践速览
    coredump 瘦身风云
  • 原文地址:https://www.cnblogs.com/volcanol/p/3099268.html
Copyright © 2011-2022 走看看