printf和memcpy链接到标准C库

时间:2014-10-30 02:57:29

标签: c

据我了解,如果我在程序中调用printf,默认情况下(如果程序没有静态编译),它会调用标准C库中的printf。但是,如果我打电话给memcpy,我希望代码内联,因为如果memcpy只复制几个字节,函数调用非常昂贵。如果您有时在线并且调用其他人,则在libc升级之后程序的行为取决于实现。

在这两种情况下实际发生了什么?一般情况下?

4 个答案:

答案 0 :(得分:2)

C标准允许实施行为"好像"调用了实际的标准库函数。这确实是一种常见的优化:小memcpy次调用可以展开/内联,还有更多。

你是对的,在某些情况下你可以升级你的libc而不会看到任何已经优化的函数调用的变化。

答案 1 :(得分:1)

您始终可以尝试驱动编译器行为。例如,使用gcc

gcc -fno-inline -fno-builtin-inline -fno-inline-functions -fno-builtin...

您应该使用nm或直接在汇编源代码中检查中断调用来检查不同的结果。

答案 2 :(得分:1)

首先,该函数永远不会真正“内联” - 适用于您编写的在同一编译单元中可见的函数。

  

如果您有时在线并且在调用其他人时,libc升级后程序的行为取决于实现。

事实并非如此。 编译时可能会“内联”memcpy。编译完成后,你的libc版本没有任何区别。

在GCC中,memcpy被视为builtin。这意味着如果GCC决定它,对memcpy的调用将被替换为合适的实现。在x86上,这通常是rep movsb或类似的指令 - 取决于副本的大小,以及它是否是恒定大小。

答案 3 :(得分:1)

它依赖于很多东西,这里有你如何找到答案。 GNU Binutils附带了一个实用程序objdump,它提供了有关二进制文件中的内容的各种详细信息。

在我的系统(ARM Chromebook)上,编译test.c:

#include <stdio.h>

int main(void) {
  printf("Hello, world!\n");
}

使用gcc test.c -o test,然后运行objdump -R test给出

test:     file format elf32-littlearm

DYNAMIC RELOCATION RECORDS
OFFSET   TYPE              VALUE 
000105e4 R_ARM_GLOB_DAT    __gmon_start__
000105d4 R_ARM_JUMP_SLOT   puts
000105d8 R_ARM_JUMP_SLOT   __libc_start_main
000105dc R_ARM_JUMP_SLOT   __gmon_start__
000105e0 R_ARM_JUMP_SLOT   abort

这些是文件中的动态重定位条目,所有将从二进制文件外部的库链接的内容。这里似乎printf已完全优化,因为它只提供一个常量字符串,因此puts就足够了。如果我们将其修改为

printf("Hello world #%d\n", 1);

然后我们得到预期的

000105e0 R_ARM_JUMP_SLOT   printf

要明确链接memcpy,我们必须阻止gcc将自己的内置版本与-fno-buildin-memcpy一起使用。