You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

memcpy是否为带符号函数?编译无call指令及GOT相关疑问

关于memcpy编译行为的疑问解答

原始C代码

#include <stdio.h>
#include <string.h>
int *add(int a, int b){ int ar[1]; int result = a+b; memcpy(ar, &result, sizeof(int)); return ar; }
int main(){ int a = add(1,2)[0]; printf("%i\n",a); }

编译后的汇编代码

.text
.globl add
.type add, @function
add:
pushq %rbp #
movq %rsp, %rbp #,
movl %edi, -20(%rbp) # a, a
movl %esi, -24(%rbp) # b, b
# a.c:5: int result = a+b;
movl -20(%rbp), %edx # a, tmp91
movl -24(%rbp), %eax # b, tmp92
addl %edx, %eax # tmp91, _1
# a.c:5: int result = a+b;
movl %eax, -8(%rbp) # _1, result
# a.c:6: memcpy(ar, &result, sizeof(int));
---I SEE NO CALL INSTRUCTION---
movl -8(%rbp), %eax # MEM[(char * {ref-all})&result], _6
movl %eax, -4(%rbp) # _6, MEM[(char * {ref-all})&ar]
# a.c:7: return ar;
movl $0, %eax #--THE FUNCTION SHOULD RETURN ADDRESS OF ARRAY, NOT 0. OTHERWISE command terminated
# lea -4(%rbp), %rax #--ONLY THIS IS CORRECT, NOT `0`
# a.c:8: }
popq %rbp #
ret
.size add, .-add
.section .rodata
.LC0:
.string "%i\n"
.text
.globl main
.type main, @function
main:
pushq %rbp #
movq %rsp, %rbp #,
subq $16, %rsp #,
# a.c:11: int a = add(1,2)[0];
movl $2, %esi #,
movl $1, %edi #,
call add #
# a.c:11: int a = add(1,2)[0];
movl (%rax), %eax # *_1, tmp90
movl %eax, -4(%rbp) # tmp90, a
# a.c:12: printf("%i\n",a);
movl -4(%rbp), %eax # a, tmp91
movl %eax, %esi # tmp91,
leaq .LC0(%rip), %rdi #,
movl $0, %eax #,
call printf@PLT #
movl $0, %eax #, _6
# a.c:13: }
leave
ret
.size main, .-main
.ident "GCC: (Debian 8.3.0-6) 8.3.0"
.section .note.GNU-stack,"",@progbits

技术疑问解答

1. memcpy是否确实是一个带符号的函数?

没错,memcpy绝对是一个带符号的标准库函数。它是C标准库公开导出的函数,你可以用nm工具查看系统的libc.so文件,能找到标记为T memcpy的符号(T表示该符号位于代码段,是一个函数符号)。你当前没看到对应的call指令,和它是不是符号无关,是编译器优化导致的。

2. 若memcpy是符号,为何未作为call指令的操作数?

这是GCC的内置函数优化在起作用。当编译器能在编译期确定memcpy的操作长度是一个小常量(这里你复制的是sizeof(int),也就是4个字节,编译期就能明确),而且操作逻辑简单时,它会直接生成等价的汇编指令来完成复制——就像你看到的movl -8(%rbp), %eax和movl %eax, -4(%rbp)这两条指令,直接把result的值复制到ar数组里,完全不需要调用memcpy函数。

这种优化能省去函数调用的开销(比如栈帧保存、跳转、返回这些步骤),效率更高。如果你想强制编译器生成call memcpy的指令,只需要用-fno-builtin-memcpy参数编译代码,这时候你就能看到和printf类似的call memcpy@PLT指令了。

3. memcpy是否存在于GOT表中?若存在,其相对GOT的偏移量是多少?

在你当前的编译结果里,因为编译器把memcpy内联成了直接的汇编指令,根本没有生成对memcpy函数的外部调用,所以GOT表中不会有memcpy的条目。但如果你关闭内置优化(用-fno-builtin-memcpy编译),生成的可执行文件就会在GOT表中添加memcpy的条目。

至于具体的偏移量,这个没有固定值——它取决于链接时符号的排列顺序、可执行文件依赖的其他动态符号数量等因素。你可以用readelf -r或者objdump -R命令查看生成的可执行文件,就能找到memcpy对应的GOT偏移信息。

内容的提问来源于stack exchange,提问作者milanHrabos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.08 12:53:12