C标准库函数 宏定义浅析

在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
如果当前版本低于则为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标准库函数 宏定义浅析)