我想知道在 C 中调用 & 和 * 到底发生了什么。
是不是要消耗很多资源?每次我想获取相同给定变量的地址或将其保存在内存中(即缓存变量中)时,我应该调用 &
吗?与 * 相同,即当我想获取指针值时?
示例
void bar(char *str)
{
check_one(*str)
check_two(*str)
//... Could be replaced by
char c = *str;
check_one(c);
check_two(c);
}
最佳答案
I would like to know what's really happening calling & and * in C.
没有“调用”&
或*
这样的东西。它们是地址运算符或解引用运算符,分别指示编译器使用对象的地址或指针指向的对象。
C 不是 C++,所以没有引用;我认为您只是在问题标题中误用了这个词。
在大多数情况下,这基本上是看待同一事物的两种方式。
通常,当您确实需要一个对象的地址时,您会使用&
。由于编译器无论如何都需要使用地址来处理内存中的对象,因此没有开销。
对于使用运算符的具体含义,您必须查看编译器生成的汇编器。
示例:考虑这个简单的代码,通过 godbolt.org 反汇编:
#include <stdio.h>
#include <stdlib.h>
void check_one(char c)
{
if(c == 'x')
exit(0);
}
void check_two(char c)
{
if(c == 'X')
exit(1);
}
void foo(char *str)
{
check_one(*str);
check_two(*str);
}
void bar(char *str)
{
char c = *str;
check_one(c);
check_two(c);
}
int main()
{
char msg[] = "something";
foo(msg);
bar(msg);
}
根据供应商和优化设置,编译器输出可能非常。
clang 3.8 使用 -O2
check_one(char): # @check_one(char)
movzx eax, dil
cmp eax, 120
je .LBB0_2
ret
.LBB0_2:
push rax
xor edi, edi
call exit
check_two(char): # @check_two(char)
movzx eax, dil
cmp eax, 88
je .LBB1_2
ret
.LBB1_2:
push rax
mov edi, 1
call exit
foo(char*): # @foo(char*)
push rax
movzx eax, byte ptr [rdi]
cmp eax, 88
je .LBB2_3
movzx eax, al
cmp eax, 120
je .LBB2_2
pop rax
ret
.LBB2_3:
mov edi, 1
call exit
.LBB2_2:
xor edi, edi
call exit
bar(char*): # @bar(char*)
push rax
movzx eax, byte ptr [rdi]
cmp eax, 88
je .LBB3_3
movzx eax, al
cmp eax, 120
je .LBB3_2
pop rax
ret
.LBB3_3:
mov edi, 1
call exit
.LBB3_2:
xor edi, edi
call exit
main: # @main
xor eax, eax
ret
请注意 foo
和 bar
相同。其他编译器做类似的事情吗?嗯……
gcc x64 5.4 使用 -O2
check_one(char):
cmp dil, 120
je .L6
rep ret
.L6:
push rax
xor edi, edi
call exit
check_two(char):
cmp dil, 88
je .L11
rep ret
.L11:
push rax
mov edi, 1
call exit
bar(char*):
sub rsp, 8
movzx eax, BYTE PTR [rdi]
cmp al, 120
je .L16
cmp al, 88
je .L17
add rsp, 8
ret
.L16:
xor edi, edi
call exit
.L17:
mov edi, 1
call exit
foo(char*):
jmp bar(char*)
main:
sub rsp, 24
movabs rax, 7956005065853857651
mov QWORD PTR [rsp], rax
mov rdi, rsp
mov eax, 103
mov WORD PTR [rsp+8], ax
call bar(char*)
mov rdi, rsp
call bar(char*)
xor eax, eax
add rsp, 24
ret
好吧,如果有任何疑问 foo
和 bar
是等价的,至少对编译器而言,我认为是这样的:
foo(char*):
jmp bar(char*)
是他们确实 的有力论据。
关于C 指针和引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40470317/