C 指针和引用

标签 c pointers reference

我想知道在 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

请注意 foobar 相同。其他编译器做类似的事情吗?嗯……

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

好吧,如果有任何疑问 foobar 是等价的,至少对编译器而言,我认为是这样的:

foo(char*):
        jmp     bar(char*)

是他们确实 的有力论据。

关于C 指针和引用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40470317/

相关文章:

c++ - 引用指针

c++ - 引用堆栈对象的地址

使用不同大小的字符串文字初始化给定大小的静态字符数组会导致未定义的行为吗?

c - 如何从 char** 获取 char*

c++ - 访问派生类对象列表的元素抛出 "read access violation"异常,而列表是由函数填充的

pointers - 我如何比较 Go 中的指针?

c++ - 我正在尝试将 C++ 引用与指针相关联

java - 如何按给定顺序返回数组元素?

c - 获取所有素数

c - 使用 Windows Spooler API 以编程方式打印 PDF 文件