当写我自己的“strdup”时 功能,如何避免与已经提供功能的平台发生冲突?

发布于 2024-07-12 09:10:03 字数 620 浏览 7 评论 0原文

我最近意识到我在 OS X 上经常使用的 strdup() 函数不是 ANSI C 的一部分,而是 POSIX 的一部分。 我不想重写所有代码,所以我想我只是编写自己的 strdup() 函数。 实际上,这并不难,只是一个 malloc() 和一个 strcpy()

无论如何,我有这个函数,但是如果我编写这个函数并将其链接到我的代码,并且它已经存在于 libc 中,我该怎么办? 我的链接器或编译器是否允许我基本上定义我自己的函数版本,或者我是否必须给它另一个名称? 如果有一种方法可以重用相同的名称,那就太方便了,这样如果用户的 libc 中存在 strcpy() ,他们就可以使用它,但如果他们的 libc 中不存在,他们就可以使用它。可以使用我的版本,尽可能少地更改代码。

简短版本:

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

  • b) 在不重写所有代码以不使用 strdup() 的情况下,我该如何避免在没有 strdup() 的平台上发生不好的事情>,这有点乏味吗?

I recently became aware that the strdup() function I've enjoyed using so much on OS X is not part of ANSI C, but part of POSIX. I don't want to rewrite all my code, so I think I'm just going to write my own strdup() function. It's not that hard, really, it's just a malloc() and a strcpy().

Anyway, I have the function, but what am I doing if I write this function and link it to my code, and it already exists in the libc? Will my linker or compiler allow me to basically define my own version of the function, or do I have to give it another name? It would be terribly convenient if there was a way to reuse the same name, so that if strcpy() exists in the user's libc they could use that, but if it didn't exist in their libc they could use my version instead, with as little code change as possible.

The short version:

  • a) What happens when I write my own function with the same name as a built-in function?

  • b) What can I do to avoid bad things happening to me on platforms that don't have strdup() without rewriting all my code to not use strdup(), which is just a bit tedious?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

别想她 2024-07-19 09:10:03

通常,您只需使用 #if 来定义在特定编译器下所需的函数。 如果内置库没有定义strdup,那么自己定义它没有问题(除非他们将来定义了它,你就必须把它拿出来。)

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

Usually, you just use an #if to define the function you want when under a certain compiler. If the built-in library doesn't define strdup, there is no problem in defining it yourself (other than if they do define it in the future, you'll have to take it out.)

// Only define strdup for platforms that are missing it..
#if COMPILER_XYZ || COMPILER_ABC
char *strdup(const char *)
{
   // ....
}
#endif
音栖息无 2024-07-19 09:10:03

正如 Rob Kennedy 指出的,最好的方法是在构建脚本中测试此函数是否存在。 我知道使用自动配置相当容易,但也可能使用其他跨平台构建脚本工具。

然后,您只需将其放入头文件中:


#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 等? 又做同样的工作。 通过在构建脚本中进行测试,它可以在不需要任何额外工作的情况下进行编译。

As Rob Kennedy noted the best way is to test inside your building scripts if this functions exists or not. I know that it is fairly easy with autoconfig, but probably with other cross-platform building scripts tools, too.

Then you simply place in your header file:


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

If strdup already exists on the target platform the libc version is used, if not your custom my_strdup function will be used.

EDIT: I should have added an explination why it is better.

First the compiler is unrelated to the existence of a function in the libc. For example take the function strlcpy. It is present on FreeBSD but not on Linux (glibc), although both are using gcc by default. Or what happens if someone is going to compile your code with clang?

Second a platform check (I don't know if there is a standard way) will only work if you explicitly add for every plattform you want to support the correct preprocessor conditional. So assuming you have mastered to compile your application on OSX and Win32 and you want to compile it now on Linux, you'll have to go through all preprocessor conditionals to see if they work for Linux. Maybe you also want to support FreeBSD, OpenBSD, etc.? Same work again. With a test in your building scripts, it may compile without any additional work.

宛菡 2024-07-19 09:10:03

你可以只使用一个宏; 这样您可以使用旧名称,但链接器将看到不同的名称:

char *my_strdup(const char *s)
{
    size_t len = strlen(s) + 1;
    char *p = malloc(len);
    if (p) { memcpy(p, s, len); }
    return p;
}
/* this goes in whatever header defines my_strdup */
char *my_strdup(const char *s);
#define strdup(x) my_strdup(x)

You could just use a macro; this way you can use the old name, but linker will see a different name:

char *my_strdup(const char *s)
{
    size_t len = strlen(s) + 1;
    char *p = malloc(len);
    if (p) { memcpy(p, s, len); }
    return p;
}
/* this goes in whatever header defines my_strdup */
char *my_strdup(const char *s);
#define strdup(x) my_strdup(x)
三月梨花 2024-07-19 09:10:03

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

您不能重新定义您所包含的头文件中已存在的函数。 这将导致编译错误。

b) 我该怎么做才能避免不好的事情发生
发生在我身上的平台
没有 strdup() 无需重写
我的所有代码都不使用 strdup(),其中
是不是有点乏味?

我建议您为 strdup 创建您自己的包装函数,并替换所有调用以使用新的包装函数。 例如:

char *StringDuplicate(const char *s1)
{
#ifdef POSIX
    return strdup(s1);
#else
    /* Insert your own code here */
#endif
}

将所有调用从 strdup 更改为 StringDuplicate() 应该是一个简单的查找和替换操作,这使其成为一种可行的方法。 然后,特定于平台的逻辑将保存在单个位置,而不是分散在整个代码库中。

a) What happens when I write my own
function with the same name as a
built-in function?

You cannot re-define a function that already exists in a header file you are including. This will result in a compilation error.

b) What can I do to avoid bad things
happening to me on platforms that
don't have strdup() without rewriting
all my code to not use strdup(), which
is just a bit tedious?

I would recommend creating your own wrapper function to strdup, and replacing all your calls to use the new wrapper function. For example:

char *StringDuplicate(const char *s1)
{
#ifdef POSIX
    return strdup(s1);
#else
    /* Insert your own code here */
#endif
}

Changing all your calls from strdup to StringDuplicate() should be a simple find-and-replace operation, making it a feasible approach. The platform-specific logic will then be kept in a single location rather than being scattered throughout your codebase.

嘿看小鸭子会跑 2024-07-19 09:10:03

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

You should also consider avoiding the creation of any identifier (including a function) that begins with str[a-z]. While this isn't reserved, the C standard (ISO/IEC 9899:1999) section 7.26.11 (future library directions) states "Function names that begin with str, mem, or wcs and a lowercase letter may be added to the declarations in the header."

梦巷 2024-07-19 09:10:03

仅供参考:我个人从未见过没有定义 strdup() 的环境。

FYI: I've never personally seen an environment that did not define strdup().

迷离° 2024-07-19 09:10:03

恐怕很难以可移植的方式自动解决。 我相信最好的选择是预编译选项,无论是使用库函数还是您自己的实现。
如果您决定使用相同的名称和签名定义自己的函数,它将进行链接(不会干扰包含的标头),您将在其中收到重复符号错误。

大多数链接器都有特殊选项,仅从库中获取丢失的符号并忽略重复的符号。 这可能是一个解决方案,但可能并非在所有链接器中都可用,可能会产生不需要的效果,并且需要额外的移植工作。

另一个想法是将您的函数定义为属性((weak))。 然后,当库中没有可用的函数时,链接器将使用该函数,并在库中存在该函数时使用该函数,而不会发出任何警告/错误。 然而,弱符号也不总是受支持。

如果您正在为桌面/移动设备进行构建,并且确定现代编译器可用,请选择弱编译器。 如果存在某些奇怪的微控制器的某些奇怪的编译器不支持弱符号的风险,请使用预编译选项和#ifdef。

您还应该考虑帕特的答案,该答案被否决为遗忘。 您不必仅仅因为可能而被迫始终使用 stdlib 调用。 在代码中实现琐碎的功能是可以的,特别是当它是一个不太受支持的功能时。 并非所有 stdlib 调用都是有用、安全或高质量的。

I'm afraid, it's difficult to solve automatically and in a portable way. I believe the best option is a precompile option whether the library function should be used or your own implementation.
If you decide to define your own function with the same name and signature, it will work down to linking (won't interfere with included headers), where you'll get a duplicate symbol error.

Most linkers have special options to only fetch missing symbols from libraries and ignore duplicate ones. This can be a solution, but might not be available in all linkers, might have unwanted effects and it's additional porting effort.

Another idea is defining your function as attribute((weak)). The linker will then use your function when it's not available in the libraries, and use the library one when it's there, without issuing any warning/error. However, weak symbols are not always supported, either.

If you're building for desktop/mobile and you're sure that modern compilers are available, go for weak. If there is a risk that some weird compiler for some weird microcontroller does not support weak symbols, use a precompile option and #ifdef.

And you should also consider the answer from Pat, that's downvoted to oblivion. You're not forced to always use stdlib calls just because it's possible. It's OK to implement trivial functionality in your code, especially when it's a not very well supported function. Not all stdlib calls are useful, safe or good quality.

寻梦旅人 2024-07-19 09:10:03

如果其他人读到此内容:即使可用,也不要使用平台的 strdup(),并且不要只是为了使用它而浪费时间/精力在 autoconf/automake 上。 说真的,这有多难:

char* mystrdup(const char* str)
{
 return strcpy(malloc( strlen(str) + 1),str);
}

这真的值得#ifdefs吗? 编译器检查? 吻

If anyone else reads this: Don't use a platform's strdup() even if available, and don't waste time/effort with autoconf/automake just to use it. Seriously, how hard is this:

char* mystrdup(const char* str)
{
 return strcpy(malloc( strlen(str) + 1),str);
}

Does this really warrant #ifdefs? Compiler checks? K.I.S.S.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文