c - 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
stdlib 中的每个函数都类似于printf
或从 GOT 编辑(即寄存器保存 GOT 的地址)。但不是,它就像“汇编内联指令”而不是常规调用地址。甚至是一个符号?如果是这样,为什么不作为论据?在 GOT 表中吗?如果是这样,从 GOT 到该符号的偏移量是多少?puts
call
%rip
memcpy
memcpy
call
memcpy
解决方案
所以首先,你有一个错误:
$ cc -O2 -S test.c
test.c: In function ‘add’:
test.c:7:12: warning: function returns address of local variable
返回局部变量的地址具有未定义的行为,当且仅当调用者使用该值;这就是为什么您的编译器生成返回空指针的代码的原因,如果使用它会使程序崩溃,否则不会造成伤害。事实上,我的 GCC 副本仅生成以下内容add
:
add:
xorl %eax, %eax
ret
因为对返回值的处理使其他操作add
成为死代码。
(“仅在使用时”限制也是我的编译器生成警告而不是硬错误的原因。)
现在,如果我将您的程序修改为具有明确定义的行为,例如
#include <stdio.h>
#include <string.h>
void add(int *sum, int a, int b)
{
int result = a+b;
memcpy(sum, &result, sizeof(int));
}
int main(void)
{
int a;
add(&a, 1, 2);
printf("%i\n",a);
return 0;
}
然后我确实看到了memcpy
调用已被内联代码替换的汇编代码:
add:
addl %edx, %esi
movl %esi, (%rdi)
ret
这是许多现代 C 编译器的一个特性:它们知道 C 库的某些函数做什么,并且可以在有意义的时候内联它们。(您可以看到,在这种情况下,生成的代码比实际调用 . 时生成的代码更小更快memcpy
。)
GCC 允许我使用命令行选项关闭此功能:
$ gcc -O2 -ffreestanding test.c
$ sed -ne '/^add:/,/cfi_endproc/{; /^\.LF[BE]/d; /\.cfi_/d; p; }' test.s
add:
subq $24, %rsp
addl %edx, %esi
movl $4, %edx
movl %esi, 12(%rsp)
leaq 12(%rsp), %rsi
call memcpy@PLT
addq $24, %rsp
ret
在这种模式下,对memcpy
in的调用被视为与对inadd
的调用相同。您的编译器可能有类似的选项。printf
main
推荐阅读
- windows - 无法启动 Windows 服务:无法在计算机“。”上启动服务 [服务名称]。
- node.js - express-jsonschema 在错误响应中给出 [object Object]
- python - 无法从运行 Python Requests Get 的网页获取完整的 html
- flutter - Flutter:在没有上下文的情况下显示覆盖
- node.js - 如何仅显示同一属性 Handlebars 的一个数据?
- angular - 在 loadchildren 路径中导航不起作用
- awk - awk 关联数组增量
- mapstruct - Mapstruct:将对象列表映射到两个字符串/ UUID列表
- python - 如何确保两个参数具有可比性?
- python - 使用 Google Sheets API 在一个单元格中创建多个链接