美文网首页
C标准库函数 宏定义浅析

C标准库函数 宏定义浅析

作者: yiltoncent | 来源:发表于2017-03-10 23:16 被阅读792次

    在C库里面经常可以看到各种奇怪的宏,不知道是什么意思,但是C标准库已经存在了几十年,里面的东西都是成熟的,因此非常值得深入挖掘学习。
    stdlib.h里面:

    /* Return the value of envariable NAME, or NULL if it doesn't exist.  */
    extern char *getenv (__const char *__name) __THROW __nonnull ((1)) __wur;
    

    这里面有__THROW__nonull以及__wur三个不常见的宏。这都是什么意思呢?

    __THROW是什么东西

    这篇文章解释了__THROW到底是干什么的。
    在LINUX目录/usr/include/sys/cdefs.h中就有关于__THROW的定义:

    #ifdef __GNUC__
    
    /* GCC can always grok prototypes.  For C++ programs we add throw()
       to help it optimize the function calls.  But this works only with
       gcc 2.8.x and egcs.  For gcc 3.2 and up we even mark C functions
       as non-throwing using a function attribute since programs can use
       the -fexceptions options for C code as well.  */
    # if !defined __cplusplus && __GNUC_PREREQ (3, 3)
    #  define __THROW       __attribute__ ((__nothrow__))
    #  define __NTH(fct)    __attribute__ ((__nothrow__)) fct
    # else
    #  if defined __cplusplus && __GNUC_PREREQ (2,8)
    #   define __THROW      throw ()
    #   define __NTH(fct)   fct throw ()
    #  else
    #   define __THROW
    #   define __NTH(fct)   fct
    #  endif
    # endif
    
    #else   /* Not GCC.  */
    
    # define __inline               /* No inline functions.  */
    
    # define __THROW
    # define __NTH(fct)     fct
    
    # define __const        const
    # define __signed       signed
    # define __volatile     volatile
    
    #endif  /* GCC.  */
    
    位置:/usr/include/features.h
    如果当前版本低于<maj.min>则为1。
    
    /* Convenience macros to test the versions of glibc and gcc.
       Use them like this:
       #if __GNUC_PREREQ (2,8)
       ... code requiring gcc 2.8 or later ...
       #endif
       Note - they won't work for gcc1 or glibc1, since the _MINOR macros
       were not defined then.  */
    #if defined __GNUC__ && defined __GNUC_MINOR__
    # define __GNUC_PREREQ(maj, min) \
            ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
    #else
    # define __GNUC_PREREQ(maj, min) 0
    #endif
    

    上面的定义可以看出,__THROW宏定义只在GCC下有效,观察#ifdef __GNUC__部分,可以看出,在一般C环境中此宏是没有意义的;在GNUC版本高于3.2时,库用函数属性将C函数标记为__nothrow__;而如果代码定义了__cplusplus则表示为C++代码,且GNUC版本为2.8.x,此时才有意思,为C++程序加入throw()以优化函数调用。
    总而言之,这个宏在C中没有什么大用。

    Declaring Attributes of Functions

    关键字__attribute__允许你声明时指定特殊的属性。这个关键字后面跟着双层括号引起来的属性说明。目前定义用于函数的属性:

    aligned
    alloc_size 
    alloc_align
    assume_aligned
    noreturn
    returns_twice
    noinline
    noclone
    no_icf
    always_inline
    flatten
    pure
    const
    nothrow
    sentinel
    format
    format_arg
    no_instrument_function
    no_split_stack
    section
    constructor
    destructor
    used
    unused
    deprecated
    weak
    malloc
    alias
    ifunc
    warn_unused_result
    nonnull
    returns_nonnull
    gnu_inline
    externally_visible
    hot
    cold
    artificial
    no_sanitize_address
    no_address_safety_analysis
    no_sanitize_thread
    no_sanitize_undefined
    no_reorder
    bnd_legacy
    bnd_instrument
    stack_protect
    error
    warning
    

    Other attributes, including section are supported for variables declarations, labels and for types.

    You may also specify attributes with ‘__’ preceding and following each keyword. This allows you to use them in header files without being concerned about a possible macro of the same name. For example, you may use __noreturn__ instead of noreturn.

    上面意思是说:你可以用__前缀和后缀在每个关键字上,这样你在头文件中使用时就不用担心可能存在同样名字宏的冲突。如__noreturn__替代noreturn

    __nonnull

    __nonnull的宏定义在/usr/include/sys/cdefs.h里面:

    /* The nonull function attribute allows to mark pointer parameters which
       must not be NULL.  */
    #if __GNUC_PREREQ (3,3)    
    # define __nonnull(params) __attribute__ ((__nonnull__ params))
    #else
    # define __nonnull(params)
    #endif
    

    如果当前版本低于3.3,则__nonnull实际就是__nonnull__属性。Using the GNU Compiler Collection 中有关于nonnull的详细描述。

    nonnull (arg-index, ...)
    The nonnull attribute specifies that some function parameters should be nonnull
    pointers. For instance, the declaration:

    extern void *
    my_memcpy (void *dest, const void *src, size_t len)
    __attribute__((nonnull (1, 2)));

    causes the compiler to check that, in calls to my_memcpy, arguments dest and
    src are non-null. If the compiler determines that a null pointer is passed in
    an argument slot marked as non-null, and the ‘-Wnonnull’ option is enabled, a
    warning is issued. The compiler may also choose to make optimizations based
    on the knowledge that certain function arguments will never be null.
    If no argument index list is given to the nonnull attribute, all pointer arguments
    are marked as non-null. To illustrate, the following declaration is equivalent to
    the previous example:

    extern void *
    my_memcpy (void *dest, const void *src, size_t len)
    __attribute__((nonnull));

    翻译一下:nonnull属性指定一些函数参数应为非空指针。举了一个例子,这个例子中__attribute__((nonnull (1, 2)))会引起编译器检查参数destsrc是否为非空。同时这个属性跟编译器选项-Wnonnull有联系,如果传入空指针到标记为非空的参数,且使能了-Wnonnull,编译器会报warning。另外如果不指定nonnull属性的参数索引号,则所有指针参数都被标记为非空。

    __wur

    __wur的宏定义在/usr/include/sys/cdefs.h里面:

    /* If fortification mode, we warn about unused results of certain
       function calls which can lead to problems.  */
    #if __GNUC_PREREQ (3,4)
    # define __attribute_warn_unused_result__ \
       __attribute__ ((__warn_unused_result__))
    # if __USE_FORTIFY_LEVEL > 0
    #  define __wur __attribute_warn_unused_result__
    # endif
    #else
    # define __attribute_warn_unused_result__ /* empty */
    #endif
    #ifndef __wur
    # define __wur /* Ignore */
    #endif
    

    在GCC版本小于3.4且__USE_FORTIFY_LEVEL>0时,__wur就是__attribute__ ((__warn_unused_result__))

    warn_unused_result
    The warn_unused_result attribute causes a warning to be emitted if a caller of
    the function with this attribute does not use its return value. This is useful for
    functions where not checking the result is either a security problem or always
    a bug, such as realloc.

    int fn () attribute ((warn_unused_result));
    int foo ()
    {
    if (fn () < 0) return -1;
    fn ();
    return 0;
    }

    
    > results in warning on line 5.
    
    如果函数的调用者不使用函数的返回值,`warn_unused_result`属性会导致发出警告。这对于没检查函数结果是安全问题或是bug的函数很有帮助。
    
    ## 总结
    
    C库函数里面的很多宏最后都是属性(Attribute)的定义,且基本定义在`/usr/include/sys/cdefs.h`,关于不同的属性用法,可以参考[Using the GNU Compiler Collection](https://gcc.gnu.org/onlinedocs/gcc-4.9.2/gcc.pdf)的第六章:Extensions to the C Language Family。
    
    
    参考阅读:
    [\_\_attribute_pure\_\_带来的奇怪问题](http://www.trueeyu.com/?p=1486)
    [Declaring Attributes of Functions](https://gcc.gnu.org/onlinedocs/gcc/Function-Attributes.html#Function-Attributes)

    相关文章

      网友评论

          本文标题:C标准库函数 宏定义浅析

          本文链接:https://www.haomeiwen.com/subject/ayaogttx.html