[关闭]
@yiltoncent 2015-04-16T13:47:00.000000Z 字数 5010 阅读 6861

C标准库函数 宏定义浅析

C语言基础


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

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

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

__THROW是什么东西

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

  1. #ifdef __GNUC__
  2. /* GCC can always grok prototypes. For C++ programs we add throw()
  3. to help it optimize the function calls. But this works only with
  4. gcc 2.8.x and egcs. For gcc 3.2 and up we even mark C functions
  5. as non-throwing using a function attribute since programs can use
  6. the -fexceptions options for C code as well. */
  7. # if !defined __cplusplus && __GNUC_PREREQ (3, 3)
  8. # define __THROW __attribute__ ((__nothrow__))
  9. # define __NTH(fct) __attribute__ ((__nothrow__)) fct
  10. # else
  11. # if defined __cplusplus && __GNUC_PREREQ (2,8)
  12. # define __THROW throw ()
  13. # define __NTH(fct) fct throw ()
  14. # else
  15. # define __THROW
  16. # define __NTH(fct) fct
  17. # endif
  18. # endif
  19. #else /* Not GCC. */
  20. # define __inline /* No inline functions. */
  21. # define __THROW
  22. # define __NTH(fct) fct
  23. # define __const const
  24. # define __signed signed
  25. # define __volatile volatile
  26. #endif /* GCC. */
  1. 位置:/usr/include/features.h
  2. 如果当前版本低于<maj.min>则为1
  3. /* Convenience macros to test the versions of glibc and gcc.
  4. Use them like this:
  5. #if __GNUC_PREREQ (2,8)
  6. ... code requiring gcc 2.8 or later ...
  7. #endif
  8. Note - they won't work for gcc1 or glibc1, since the _MINOR macros
  9. were not defined then. */
  10. #if defined __GNUC__ && defined __GNUC_MINOR__
  11. # define __GNUC_PREREQ(maj, min) \
  12. ((__GNUC__ << 16) + __GNUC_MINOR__ >= ((maj) << 16) + (min))
  13. #else
  14. # define __GNUC_PREREQ(maj, min) 0
  15. #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__允许你声明时指定特殊的属性。这个关键字后面跟着双层括号引起来的属性说明。目前定义用于函数的属性:

  1. aligned
  2. alloc_size
  3. alloc_align
  4. assume_aligned
  5. noreturn
  6. returns_twice
  7. noinline
  8. noclone
  9. no_icf
  10. always_inline
  11. flatten
  12. pure
  13. const
  14. nothrow
  15. sentinel
  16. format
  17. format_arg
  18. no_instrument_function
  19. no_split_stack
  20. section
  21. constructor
  22. destructor
  23. used
  24. unused
  25. deprecated
  26. weak
  27. malloc
  28. alias
  29. ifunc
  30. warn_unused_result
  31. nonnull
  32. returns_nonnull
  33. gnu_inline
  34. externally_visible
  35. hot
  36. cold
  37. artificial
  38. no_sanitize_address
  39. no_address_safety_analysis
  40. no_sanitize_thread
  41. no_sanitize_undefined
  42. no_reorder
  43. bnd_legacy
  44. bnd_instrument
  45. stack_protect
  46. error
  47. 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里面:

  1. /* The nonull function attribute allows to mark pointer parameters which
  2. must not be NULL. */
  3. #if __GNUC_PREREQ (3,3)
  4. # define __nonnull(params) __attribute__ ((__nonnull__ params))
  5. #else
  6. # define __nonnull(params)
  7. #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里面:

  1. /* If fortification mode, we warn about unused results of certain
  2. function calls which can lead to problems. */
  3. #if __GNUC_PREREQ (3,4)
  4. # define __attribute_warn_unused_result__ \
  5. __attribute__ ((__warn_unused_result__))
  6. # if __USE_FORTIFY_LEVEL > 0
  7. # define __wur __attribute_warn_unused_result__
  8. # endif
  9. #else
  10. # define __attribute_warn_unused_result__ /* empty */
  11. #endif
  12. #ifndef __wur
  13. # define __wur /* Ignore */
  14. #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.

  1. int fn () __attribute__ ((warn_unused_result));
  2. int foo ()
  3. {
  4. if (fn () < 0) return -1;
  5. fn ();
  6. return 0;
  7. }

results in warning on line 5.

如果函数的调用者不使用函数的返回值,warn_unused_result属性会导致发出警告。这对于没检查函数结果是安全问题或是bug的函数很有帮助。

总结

C库函数里面的很多宏最后都是属性(Attribute)的定义,且基本定义在/usr/include/sys/cdefs.h,关于不同的属性用法,可以参考Using the GNU Compiler Collection的第六章:Extensions to the C Language Family。

参考阅读:
__attribute_pure__带来的奇怪问题
Declaring Attributes of Functions

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注