linux c 宏定义 #define _GNU_SOURCE 含义

今天我必须要使用 basename 函数,使用man手册查询了一下这个函数 man 3 basename 发现一个奇怪的信息

名称

basename,dirname-分析路径名组件

简介

 
  1. #include <libgen.h>

  2. char *dirname(char *path);

  3. char *basename(char *path);

警告
有两个不同的函数basename()-请参阅下面的。

函数dirname()和basename()将以空结尾的路径名字符串分解为目录和文件名组件。在通常情况下,dirname()返回最后一个“/”之前(但不包括在内)的字符串,basename()返回最后一个“/”之后的组件。尾随的“/”字符不算作路径名的一部分。

如果路径不包含斜杠,dirname()将返回字符串“.”,而basename()将返回路径的副本。如果path是字符串“/”,则dirname()和basename()都会返回字符串“/”。如果path是空指针或指向空字符串,则dirname()和basename()都返回字符串“.”。

将dirname()返回的字符串、一个“/” 和basename()返回的字符串连接起来,将生成完整的路径名.
dirname()和basename()都可能会修改路径的内容,因此在调用这两个函数时传入参数一定是一个路径的副本。

这些函数可能返回指向静态分配内存的指针,这些指针可能会被后续调用覆盖。或者,它们可以返回指向路径某一部分的指针,以便在不再需要函数返回的指针之前,不应修改或释放由路径引用的字符串。

下面的列表(取自SUS V2)显示了dirname()和basename()为不同路径返回的字符串:

pathdirnamebasename
/usr/lib/usrlib
/usr//usr
usr.usr
///
...
.

返回值

dirname()和basename()都返回指向以空结尾的字符串的指针。(不要将这些指针传递给free(3)。)

ATTRIBUTES

  1. For an explanation of the terms used in this section, see attributes(7).

  2. ┌──────────────────────┬───────────────┬─────────┐

  3. │Interface │ Attribute │ Value │

  4. ├──────────────────────┼───────────────┼─────────┤

  5. │basename(), dirname() │ Thread safety │ MT-Safe │

  6. └──────────────────────┴───────────────┴─────────┘

 

注意

basename()有两个不同的版本————上面描述是posix版本,gnu版本是下面:

 
  1. #define _GNU_SOURCE /* See feature_test_macros(7) */

  2. #include <string.h>

GNU版本从不修改它的参数,当路径的结尾是一个斜杠时返回空字符串,特别是当它是“/”时。没有dirname()的GNU版本。
使用glibc,当包含<libgen.h>时,将获得basename()的POSIX版本,否则将获得GNU版本。

漏洞

在glibc实现中,这些函数的posix版本修改了path参数,并在使用诸如“/usr/”之类的静态字符串调用时修改产生段错误。

在glibc 2.2.1之前,dirname()的glibc版本没有正确地处理带有尾随“/”字符的路径名,如果给定了空参数,则会生成段错误。

例子

 
  1. #include <libgen.h>

  2. #include <stdio.h>

  3. #define _GNU_SOURCE

  4. #include <string.h>

  5. int main(){

  6. char *dirc, *basec, *bname, *dname;

  7. char *path = "/etc/passwd";

  8. dirc = strdup(path);

  9. basec = strdup(path);

  10. dname = dirname(dirc);

  11. bname = basename(basec);

  12. printf("dirname=%s, basename=%s\n", dname, bname);

  13. return 0;

  14. }

两个问题:

  1. 如果是这样,为什么不给人们提供不同的头文件,没有必要定义一些模糊的宏来获取函数的实现或者其他啊?
  2. 还有一些问题也让我抓狂:编译器怎么知道要连接那个函数到可执行文件? 也是使用 #define 的方式吗?

解答一

定义 _GNU_SOURCE 与许可证无关,与编写(非)可移植代码无关。如果您定义了“_GNU_SOURCE ”,您将得到:

  • 访问许多非标准的GNU/Linux扩展函数
  • 对POSIX标准中省略的传统功能的访问(通常是有充分理由的,例如被更好的替代方案替换,或者与特定的遗留实现绑定)
  • 访问不可移植的低级功能,但有时需要实现系统实用程序,如mount、ifconfig等。

对于许多POSIX指定的函数来说,行为是不正常的,GNU人员不同意标准委员会关于函数应该如何运行,并决定自己做一些事情。

只要你知道这些事情,定义 _GNU_SOURCE 就不是问题了,但是你应该避免定义它,而是在可能的时候定义POSIX_C_source=200809L或Xopen_source=700,以确保你的程序是可移植的。

尤其是,你不应该使用的来自GNU来源的东西是上面的2和4。

解答二

如果是这样,为什么不给人们提供不同的头文件,没有必要定义一些模糊的宏来获取函数的实现或者其他啊?

在不同的Unix版本中,同一个头部的内容通常稍有不同,因此没有单一的正确内容,例如,<string.h>—有许多标准(xkcd)。在一整套宏中选择你最想要的,这样,这个库可以满足不同人的标准。

还有一些问题也让我抓狂:编译器怎么知道要连接那个函数到可执行文件? 也是使用 #define 的方式吗?

一种常见的方法是根据 _GNU_SOURCE 是否被定义来有条件的 给标识符 basename 定义不同的名称:

 
  1. #ifdef _GNU_SOURCE

  2. # define basename __basename_gnu

  3. #else

  4. # define basename __basename_nongnu

  5. #endif

这样库的实现中只要提供两种实现即可。

解答三

来自google的邮件列表

看看 glibc’s include/features.h:

定义了_GNU_SOURCE 表示可以使用
STRICT_ANSI,

_ISOC99_SOURCE,

_POSIX_SOURCE,

_POSIX_C_SOURCE,

_XOPEN_SOURCE,

_XOPEN_SOURCE_EXTENDED,

_LARGEFILE_SOURCE,

_LARGEFILE64_SOURCE,

_FILE_OFFSET_BITS=N,

_BSD_SOURCE,

_SVID_SOURCE 包括 GUN 扩展内容。

因此,它为gcc启用了大量编译标志

解答四

想详细了解 _GNU_SOURCE ,可以看一下下面的文档。

来自 GUN 文档

宏:_GNU_SOURCE

如果您定义这个宏,所有内容都包括在内: ISO C89, ISO C99, POSIX.1, POSIX.2, BSD, SVID, X/Open, LFS和GUN扩展。在POSIX.1与BSD冲突的情况下,POSIX.1 定义优先。

Linux 的man手册中的功能测试宏:

_GNU_SOURCE

定义这个宏(有任何值)隐式地定义了 _ATFILE_SOURCE, _LARGEFILE64_SOURCE, _ISOC99_SOURCE, _XOPEN_SOURCE_EXTENDED, _POSIX_SOURCE, _POSIX_C_SOURCE,其值为200809L(glibc版本2.10之前的200112L;glibc版本2.5之前的199506L;glibc版本2.1之前的199309L)和_XOPEN_SOURCE ,其值为700(600在2.10之前的glibc版本中;在2.2之前的glibc版本中为500)。此外,还公开了各种特定于GNU的扩展。

自从glibc 2.19以来,定义_GNU_SOURCE也具有隐式定义_DEFAULT_SOURCE的效果。在2.20之前的glibc版本中,定义_GNU_SOURCE还具有隐式定义_BSD_SOURCE 和_SVID_SOURCE的效果。

注意:在包含头文件之前,需要定义_GNU_SOURCE,以便各个头文件启用这些功能。例如:

 
  1. #define _GNU_SOURCE

  2. #include <stdio.h>

  3. #include <stdlib.h>

  4. ...

_还可以使用 -D 标志在每次编译时启用 _GNU_SOURCE:

$ gcc -D_GNU_SOURCE file.c

(-D 不是特定于_GNU_SOURCE,而是以这种方式定义的任何宏)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值