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
如果当前版本低于<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 fortypes
.
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)))
会引起编译器检查参数dest
和src
是否为非空。同时这个属性跟编译器选项-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)