【问题标题】:When is the pointer &x created in function(&x)?什么时候在函数(&x)中创建指针&x?
【发布时间】:2019-07-24 19:03:19
【问题描述】:

何时创建指向ab 的指针? 在下面的代码中,我只声明/启动了ab。 但是函数swap&a&b 来处理它们,它们是指针。

我的问题是, 指向a和b的指针是用int aint b同时创建的吗?

或者它们是在使用参数&a&b 调用swap 函数时创建的?

#include <stdio.h>

void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}

int main()
{
    int a = 3;
    int b = 5;
    swap(&a, &b);
    printf("%d %d", a, b);
}

【问题讨论】:

  • 不会创建指向变量的指针,它们只是作为语言方式的结果而存在。在实践中,它们是这些变量的地址,很可能是通过将一些编译时已知的偏移量添加到基址或堆栈指针来生成的,而地址是在运行时生成的。
  • &amp; 运算符创建指针,就像 + 运算符添加数字,而一元 - 运算符否定数字一样。因此,如果我调用 f(&amp;a, b+c, -d)&amp;+- 运算符都或多或少同时工作,因为函数调用的参数正在准备中。
  • @SteveSummit &amp; 运算符创建指针?
  • @CinCout:当然,就像* 运算符在4*5 中创建产品20 一样。在任何一种情况下,4*5&amp;a,结果都只是一个值(数字、字母、地址或某种事物集合中的其他元素)而不是对象(其内容可以表示的存储区域值),但该值是作为运算符的结果产生的。
  • @CinCout 或“生成”指针。无论如何,这就是我倾向于以更高层次的方式处理它的方式。现在,这是真的,取决于ab 是什么,它们的地址就是它们的地址,并且(正如其他 cmets 指出的那样)可能没有真正的工作来“创建”它们。 (如果a 具有静态持续时间,例如,f(&amp;a) 基本上只是将一个常量传递给f,尽管它是一个链接时间常量。)但是鉴于ab 是局部变量,编译器是在将它们传递给swap()之前,必须发出实际代码来将它们的偏移量添加到当前堆栈/帧指针。

标签: c pointers unary-operator


【解决方案1】:

指向ab 的指针是作为函数调用中参数表达式的评估结果而创建的

swap(&a, &b);
     ^^  ^^

来自 C 标准(6.5.3.2 地址和间接运算符)

3 一元 & 运算符产生其操作数的地址。

因此,表达式&amp;a&amp;b 具有int * 类型并产生变量ab 的地址。

【讨论】:

  • 就 C 而言,这是准确的。但是在较低级别上,指针一直存在,而不是它们引用的类型。它没有创建新变量作为 & 运算结果的指针,而是简单地使用创建堆栈帧时可用的指针。
  • @CaitLANJenner 你错了。编译器关心 & 运算符并生成相应的目标代码。
  • 我并没有暗示它没有。也许这措辞很糟糕。是的,在这种情况下,编译器使用加载有效地址 (lea) 指令而不是移动 (mov) 指令。这肯定会产生影响,但关于 OP 的问题,它不会“创建”指针。可以这么说,指针存在而变量不存在。
  • @CaitLANJenner:我没有看到任何模型或级别——无论是 C 抽象计算机模型还是实际计算机中的指令或其他——在评估 &amp; 之前必须存在指针操作员。假设堆栈指针当前为 2400,a 位于其上方 12 个字节。就在通过执行lea 指令评估&amp; 之前,计算机中可能没有值2412 存在的位置——没有寄存器、内存或其他包含2412 的位。在lea 执行之后,2412 是在寄存器中。 lea 指令创建此指针。
  • “假设堆栈指针是当前的 2400,并且 a 位于其上方 12 个字节...” - 那么所有对 a 的引用在技术上取消引用 a 在 2400+12。此取消引用需要原始指针。我认为这里的混乱是我们在谈论两个不同的事情。要访问任何数据,我们需要知道它们所在的位置。以一种或另一种方式直接访问a 使用指向它存储位置的指针,可能是间接的。
【解决方案2】:

当您说创建了ab 时,这实质上意味着为这些变量分配了一块内存,然后使用各自的值对其进行初始化。这些内存块有一个地址。 swap() 中的指针只不过是内存中存储ab 的地址。

【讨论】:

  • 不完全正确,没有地址的变量不需要地址!甚至,地址运算符可能是变量在 all 处具有地址的原因。
  • @AnttiHaapala:在 C 标准使用的模型中,每个对象都有一个地址。在实际实现中,对象不需要使用存储来实现,因此不需要有地址。如果一个人写的是 C 标准定义的语义,那么说ab 为他们保留了内存并有地址是正确的说法;这句话完全正确。
  • @Eric Postpischil 这不是 100% 正确的。位域的成员没有地址。但我想这主要是开发硬件驱动程序时的一个问题。
  • @Drunix:很好,但讨论的对象不是位域。
【解决方案3】:

所有变量都存储在内存中的地址处。因此,您声明的每个变量都存在一个指针,即使您没有将它们声明为指针。要了解实际发生的事情,了解汇编语言的基础知识会有所帮助。以下是您在 x86_64 中编译并通过objdump 查看的代码的相关部分。

6ee:    c7 45 f0 03 00 00 00    mov    DWORD PTR [rbp-0x10],0x3
6f5:    c7 45 f4 05 00 00 00    mov    DWORD PTR [rbp-0xc],0x5
6fc:    48 8d 55 f4             lea    rdx,[rbp-0xc]
700:    48 8d 45 f0             lea    rax,[rbp-0x10]
704:    48 89 d6                mov    rsi,rdx
707:    48 89 c7                mov    rdi,rax
70a:    e8 9b ff ff ff          call   6aa <swap>

在 0x6ee 和 0x6f5 行你的变量被初始化。请注意,在这种情况下,它们实际上分别存储在 rbp-0x10 和 rbp-0xc 的堆栈帧中。在接下来的两行中,这些变量的地址分别存储到寄存器 rdx 和 rax 中,然后移动到适当的寄存器(rsi 和 rdi)中,以便将它们传递给交换函数。

总而言之,C 语言的编写水平比实际执行代码的机器架构更高。在机器语言中,变量存储在内存中的位置,这些位置可以通过指针以寄存器、内存部分等的偏移量形式访问。在上面的示例中,您可以看到数据 3 和 5“存储在指针位置”整个时间。

【讨论】:

  • 所有好信息,但我认为这个问题的笑话在于函数参数。从这个意义上说,地址的引用将通过使用 address of 运算符 &amp; 在调用 swap(&amp;a, &amp;b); 时创建,传递给 swap ab 位于内存中。很好地解释了这些地址的来源和方式。
  • 当然,我认为这个解释足以回答 OP 的问题。在这里,我尝试提供一些较低级别的详细信息,以帮助澄清实际发生的情况。我将问题解释为,“是指向在 &amp; 操作之后创建的变量的指针。从技术上讲,不是。但如果问题是关于 C 语法和行为,那么您提供的解释效果很好。
  • 您提出了一个很少有人想到的非常好的观点,即使声明具有立即值的变量,例如int a = 3;,实际上也只不过是为内存位置创建一个标签存储值的位置。这就是理解大会带来的好处。
猜你喜欢
  • 2016-06-11
  • 1970-01-01
  • 2021-01-19
  • 1970-01-01
  • 2012-02-10
  • 2010-12-19
  • 2021-10-07
  • 2023-03-29
  • 1970-01-01
相关资源
最近更新 更多