strdup()函数

15

我最近意识到,我一直在OS X上使用的strdup()函数并不是ANSI C的一部分,而是POSIX的一部分。我不想重写所有的代码,所以我想我会写自己的strdup()函数。它其实并不难,只需要一个malloc()和一个strcpy()。无论如何,我有了这个函数,但如果我编写这个函数并将其链接到我的代码中,并且它已经存在于libc中,那么我在做什么呢?我的链接器或编译器是否允许我定义自己版本的函数,还是我必须给它另一个名称?如果有一种方法可以重复使用相同的名称,那将非常方便,这样如果strcpy()存在于用户的libc中,他们可以使用它,但如果它不存在于他们的libc中,则可以尽可能少地更改代码,使用我的版本代替。

简短版:

a)当我编写与内置函数同名的函数时会发生什么?

b)除了费点力气之外,有什么方法可以避免在没有strdup()的平台上遇到问题,而不必重写所有代码来避免使用strdup()吗?

7个回答

20

通常,你只需要使用#if来定义在特定编译器下想要的函数。如果内置库未定义strdup,则自己定义它并没有问题(除非将来他们定义了它,那么你就必须删除这个定义)。

// Only define strdup for platforms that are missing it..
#if COMPILER_XYZ || COMPILER_ABC
char *strdup(const char *)
{
   // ....
}
#endif

8
如果你雄心勃勃的话,你甚至可以设置automake和autoconf来配置必要的宏以测试是否需要定义自己的strdup,而不是枚举所有你希望支持的编译器和环境。 - Rob Kennedy

6

您可以使用以下宏,这样您就可以使用旧名称,但链接器将看到不同的名称:

char *my_strdup(const char *s) {
    char *p = malloc(strlen(s) + 1);
    if(p) { strcpy(p, s); }
    return p;
}

/* this goes in whatever header defines my_strdup */
char *my_strdup(const char *s);
#define strdup(x) my_strdup(x)

这是一个不错的解决方案,但你需要将 #define strdup(x) 包装在其他检查机制中,以确定是否需要替换它。如果不需要替换,你可能想使用 libc 的 strdup。 - Matt
4
除了可能的优化之外,检查那个并没有太大的害处。顺便说一下,如果使用缓存的strlen然后使用memcpy,可能会更快,因为在许多编译器中,它是一个内置函数,最终会变成单个CPU指令。 - Tim Čas

6

正如Rob Kennedy所指出的,最好的方法是在构建脚本内测试此函数是否存在。我知道使用autoconfig非常容易实现,但其他跨平台构建脚本工具也可能可以实现。

然后,您只需在头文件中放置:


#ifndef HAVE_STRDUP
# ifdef HAVE__STRDUP
#  define strdup _strdup
# else
#  define strdup my_strdup
# endif
#endif

如果目标平台上已经存在 strdup,则使用 libc 版本,否则将使用自定义的 my_strdup 函数。
编辑:我应该添加一个解释为什么这样做更好。
首先,编译器与 libc 中函数的存在无关。例如,考虑函数 strlcpy 。它在 FreeBSD 上存在,在 Linux(glibc)上不存在,尽管两者默认都使用 gcc。或者如果有人要使用 clang 编译您的代码会发生什么?
其次,平台检查(我不知道是否有标准方法)只能在您明确添加每个要支持的平台的正确预处理器条件时才能工作。因此,假设您已经掌握了在 OSX 和 Win32 上编译应用程序的方法,并且现在想在 Linux 上编译它,则必须查看所有预处理器条件,以查看它们是否适用于 Linux。也许您还希望支持 FreeBSD、OpenBSD 等?又是同样的工作。通过在构建脚本中执行测试,可以在不进行任何其他工作的情况下对其进行编译。

4
a) 当我写一个与内置函数同名的自定义函数时会发生什么?
您不能重新定义已经存在于您包含的头文件中的函数。这将导致编译错误。
b) 在没有重写所有代码以避免使用strdup()的平台上,我该如何避免出现问题?
我建议创建自己的包装函数来替代strdup,并替换所有调用以使用新的包装函数。例如:
char *StringDuplicate(const char *s1)
{
#ifdef POSIX
    return strdup(s1);
#else
    /* Insert your own code here */
#endif
}

将所有的strdup调用替换为StringDuplicate()应该是一个简单的查找和替换操作,使得这种方法变得可行。平台特定的逻辑将被集中在一个位置,而不是分散在您的代码库中。


3

您还应考虑避免创建以str[a-z]开头的任何标识符(包括函数)。虽然这不是保留的,但C标准(ISO / IEC 9899:1999)第7.26.11节(未来库方向)指出:“以str,mem或wcs和小写字母开头的函数名称可以添加到头文件中的声明中。”


2

提醒:我个人从未见过未定义strdup()函数的环境。


1
我曾经也喜欢使用strdup,直到我在irc.freenode.org的##c频道告诉了那些人。他们不喜欢它,并且认为如果有两行简单、保证可用的方法,为什么要依赖于不可移植的函数呢?(在Windows中,strdup已被弃用,而Win CE似乎没有它) - Johannes Schaub - litb
MSDN说应该使用_strdup。但我认为那些弃用警告都是无稽之谈 :) 但今天让我惊讶的是看到strdup实际上被如此广泛地使用。 - Johannes Schaub - litb

-3
如果有其他人看到这个:即使可用,请不要使用平台的strdup(),也不要浪费时间/精力用autoconf / automake来使用它。 真的,这有多难:
char* mystrdup(const char* str)
{
 return strcpy(malloc( strlen(str) + 1),str);
}

这真的需要#ifdef吗?编译器检查?K.I.S.S.。


16
如果 malloc 失败会发生什么? - Chaim Geretz

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接