今天我必须要使用 basename 函数,使用man手册查询了一下这个函数 man 3 basename 发现一个奇怪的信息
basename,dirname-分析路径名组件
#include
char *dirname(char *path);
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()为不同路径返回的字符串:
path | dirname | basename |
---|---|---|
/usr/lib | /usr | lib |
/usr/ | / | usr |
usr | . | usr |
/ | / | / |
. | . | . |
… | . | … |
dirname()和basename()都返回指向以空结尾的字符串的指针。(不要将这些指针传递给free(3)。)
ATTRIBUTES
For an explanation of the terms used in this section, see attributes(7).
┌──────────────────────┬───────────────┬─────────┐
│Interface │ Attribute │ Value │
├──────────────────────┼───────────────┼─────────┤
│basename(), dirname() │ Thread safety │ MT-Safe │
└──────────────────────┴───────────────┴─────────┘
注意
basename()有两个不同的版本————上面描述是posix版本,gnu版本是下面:
#define _GNU_SOURCE /* See feature_test_macros(7) */
#include
GNU版本从不修改它的参数,当路径的结尾是一个斜杠时返回空字符串,特别是当它是“/”时。没有dirname()的GNU版本。
使用glibc,当包含
在glibc实现中,这些函数的posix版本修改了path参数,并在使用诸如“/usr/”之类的静态字符串调用时修改产生段错误。
在glibc 2.2.1之前,dirname()的glibc版本没有正确地处理带有尾随“/”字符的路径名,如果给定了空参数,则会生成段错误。
#include
#include
#define _GNU_SOURCE
#include
int main(){
char *dirc, *basec, *bname, *dname;
char *path = "/etc/passwd";
dirc = strdup(path);
basec = strdup(path);
dname = dirname(dirc);
bname = basename(basec);
printf("dirname=%s, basename=%s\n", dname, bname);
return 0;
}
两个问题:
定义 _GNU_SOURCE 与许可证无关,与编写(非)可移植代码无关。如果您定义了“_GNU_SOURCE ”,您将得到:
对于许多POSIX指定的函数来说,行为是不正常的,GNU人员不同意标准委员会关于函数应该如何运行,并决定自己做一些事情。
只要你知道这些事情,定义 _GNU_SOURCE 就不是问题了,但是你应该避免定义它,而是在可能的时候定义POSIX_C_source=200809L或Xopen_source=700,以确保你的程序是可移植的。
尤其是,你不应该使用的来自GNU来源的东西是上面的2和4。
如果是这样,为什么不给人们提供不同的头文件,没有必要定义一些模糊的宏来获取函数的实现或者其他啊?
在不同的Unix版本中,同一个头部的内容通常稍有不同,因此没有单一的正确内容,例如,
还有一些问题也让我抓狂:编译器怎么知道要连接那个函数到可执行文件? 也是使用 #define 的方式吗?
一种常见的方法是根据 _GNU_SOURCE 是否被定义来有条件的 给标识符 basename 定义不同的名称:
#ifdef _GNU_SOURCE
# define basename __basename_gnu
#else
# define basename __basename_nongnu
#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,以便各个头文件启用这些功能。例如:
#define _GNU_SOURCE
#include
#include
...
_还可以使用 -D 标志在每次编译时启用 _GNU_SOURCE:
$ gcc -D_GNU_SOURCE file.c
(-D 不是特定于_GNU_SOURCE,而是以这种方式定义的任何宏)。