【问题标题】:C pointers and memoryC 指针和内存
【发布时间】:2012-03-27 02:58:21
【问题描述】:

我正在学习 C,但现在我碰壁了。我很难理解指针。

想象一下我有这个代码:

#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>

#define DELTA 33

int calls, seed=356;

int sum_ds(int a){    
 int d=DELTA;          
 calls++;               
 return a+d+seed; 
}                       

int main() {
    int num;                                
    int *ptr;
    int **handle;

     num = 14;                              
     ptr = (int *)malloc(2 * sizeof(int));  
     handle = &ptr;
     *(*handle+0) = num;                    
     *(*handle+1) = num+1;  
     *ptr = num-2;      
     ptr = &num;        
     *ptr = sum_ds(num-2);
}

按照我的理解一步一步来。

1 - int calls 创建了一个名为 calls 的变量,并且没有对其进行初始化,因此它包含垃圾。它存储在 DATA 上,假设内存地址为 0xFFAA。

2 - int 种子创建一个名为种子的变量,用整数 356 初始化。它存储在 DATA 上,假设内存地址为 0xFFAB。

3 - int num 创建了一个名为 num 的变量,并且没有对其进行初始化,因此它包含垃圾。它存储在堆栈上,假设内存地址为 0xFFAC。

4 - int *ptr 创建一个指向 int 的指针并且不为其分配任何地址。它存储在堆栈上,假设内存地址为 0xFFAD。

5 - int **handle 创建一个指向 int 指针的指针,并且不为其分配任何地址。它存储在堆栈上,假设内存地址为 0xFFAE。 (这里有很多疑问)

6 - num = 14 转到地址 0xFFAC 并在其上存储数字 14。它在堆栈中完成。

7 - ptr = (int *)malloc(2 * sizeof(int)) 在 HEAP 上,它被分配了 2 个整数的内存大小,并且第一个内存字节的地址(比如说 0xFFZZ)存储在(在堆栈上) ptr 所以现在 *ptr 指向那个内存地址。

8 - 句柄 = &ptr 句柄现在指向 ptr。我相信它现在指向 0xFFZZ 上的任何内容(这里有很多疑问)

9 - *(*handle+0) = num 指向 int 指针的指针现在被赋值为 num (14) (这里有很多很多很多疑问)

10 - *(*handle+1) = num+1 指针的指针加上 int 之一,现在它被赋值为 num + 1 (15) (这里有很多很多很多疑问)

11 - *ptr = num-2 ptr 的值点,它被分配了 num - 2 (12) 的值。我相信它会到达内存地址 0xFFZZ 并在那里存储数字 12。

12 - ptr = &num ptr 现在指向 num,我相信它现在指向 0xFFAC。

13 - *ptr = sum_ds(num-2) ptr指向的值就是sum_ds的返回值。我相信 0xFFAC 它被分配了 401 (12+33+356)

是这样吗?

【问题讨论】:

  • int 的大小不是 1 字节......它是 4 字节(或 8 字节)。所以在第2步中,地址不能是0xFFAB(因为那个地址是第1步中int的第二个字节)
  • @Aziz:int通常会大于一个字节,但这不是严格要求的(但如果它只有一个字节,你必须有一个字节至少 16 位)。
  • +1 反对不合理的反对票。
  • @PéterTörök 感谢您的支持。不明白为什么有人给了我一个downvote :(

标签: c pointers


【解决方案1】:

一个变量有一个地址,并且存储在那个地址的是你刚刚输入的值:

int a = 10;

对吗?

指针是一种存储另一个变量地址的变量。 所以……

int a = 10;
int *p = &a;

这意味着“p”存储具有您要使用的值的“a”的地址。

执行下面这段代码,你就会明白: printf("%p %p %d %d\n", p, &a, *p, a);

【讨论】:

    【解决方案2】:

    1 - int calls 创建了一个名为 calls 的变量,并且没有对其进行初始化,因此它包含垃圾。它存储在 DATA 上,假设内存地址为 0xFFAA。

    2 - int 种子创建一个名为种子的变量,初始化为整数 356。它存储在 DATA 中,假设内存地址为 0xFFAB。

    一个小细节:sizeof(int) 大于 1(在大多数主流平台上是 4,所以第二个地址不能比第一个高 1。除此之外,AFAIK 到目前为止你是正确的。

    3 - int num 创建了一个名为 num 的变量,并且没有对其进行初始化,因此它包含垃圾。它存储在堆栈上,假设内存地址为 0xFFAC。

    4 - int *ptr 创建一个指向 int 的指针并且不为其分配任何地址。它存储在堆栈上,假设内存地址为 0xFFAD。

    另一个小细节:在大多数主流平台上,堆栈向下增长,因此第 4 个地址会小于第 3 个。除此之外,到目前为止,您是正确的。 (此外,数据段、堆和栈上的地址在现实生活中会大不相同。)

    7 - ptr = (int *)malloc(2 * sizeof(int)) 在 HEAP 上,它被分配了 2 个整数的内存大小,并且第一个内存字节的地址(比如说 0xFFZZ)存储在(在堆栈上) ptr 所以现在 *ptr 指向那个内存地址。

    为了挑剔,'Z' 不是十六进制数字 :-) 所以我们假设它是 0x1000

    8 - 句柄 = &ptr 句柄现在指向 ptr。我相信它现在指向 0xFFZZ 上的任何内容(这里有很多疑问)

    不,handle 现在包含ptr 的地址,即0xFFAD间接虽然 - 通过ptr - 它确实指向0x1000(在你的例子中是0xFFZZ)。

    9 - *(*handle+0) = num 指向 int 指针的指针现在被赋值为 num (14) (这里有很多很多很多疑问)

    基本正确。您使用的符号不是最容易处理的,这使您更难以理解正在发生的事情。在第 8 步之后,*handle 等同于 ptr。由于指针和数组在许多常见情况下可以互换,*(ptr+0) 等价于ptr[0],也等价于*ptr

    10 - *(*handle+1) = num+1 指针的指针加上 int 之一,现在它被赋值为 num + 1 (15) (这里有很多很多很多疑问)

    与上一点类似,您实际上是在分配ptr[1] = num+1。请记住,虽然ptrint*,所以ptrptr + 1 之间的地址差异等于sizeof(int),如上所述,通常为4。

    11 - *ptr = num-2 ptr 的值点,它被分配了 num - 2 (12) 的值。我相信它会到达内存地址 0xFFZZ 并在那里存储数字 12。

    是的,这会覆盖在步骤 9 中设置的值。

    12 - ptr = &num ptr 现在指向 num,我相信它现在指向 0xFFAC。

    正确。

    13 - *ptr = sum_ds(num-2) ptr指向的值就是sum_ds的返回值。我相信 0xFFAC 它被分配了 401 (12+33+356)

    正确。由于上一步使*ptr等价于num,所以本次调用也等价于num = sum_ds(num-2)

    【讨论】:

    • 感谢您的宝贵时间。我的记忆“符号”仅用于“插图”目的。我知道这不是我提到的,但不知道堆栈向下增长,所以感谢您指出这一点。现在按照您的解释,我坚持第 8 点,因为我不知道我在第 5 点和第 6 点上写的内容是否正确。基本上我不知道句柄是什么...
    • @Favolas,抱歉,我忽略了第 5 点和第 6 点,因为它们是正确的,handle 确实是指向 int 的指针。
    • @Favolas,顺便说一句,您可以打印出地址和指针以在任何时候检查它们的真实值,例如printf("handle is at address 0x%08X, pointing to 0x%08X, and *handle is 0x%08X .\n", &amp;handle, handle, *handle);.
    • 感谢您的所有帮助。制作此代码pastebin.com/DNA6h60t(对不起,但不知道如何在 cmets 上插入代码)以查看内存地址,以便我更好地理解。请注意,如果未注释,已注释的 printf 会出现段错误。基本上我的问题是处理。我知道它创建了一个指向带有 int **handle 的整数指针的指针,但是句柄和 *handle 是什么?它们甚至是“创造”的吗?
    • @Favolas,取消引用未初始化的指针确实是未定义的行为,即永远不要在生产代码中这样做,即使是出于调试目的。因此,您应该能够随时打印出&amp;handlehandle,但在正确初始化之前,您不得尝试打印出*handle**handle(在您的情况下,语句handle = &amp;ptr 会这样做) .
    【解决方案3】:

    由于calls 在任何函数之外,它是一个static 变量。静态变量初始化为0。

    由于num 是一个局部变量(auto 存储类),它没有初始化。

    在您的第 9 点,*(*handle+0) = num; 可能是最容易破译的,记住 handle = &amp;ptr,因此是 *handle = ptr,所以这基本上相当于 *(ptr+0) = num;,这(反过来)相当于 @987654329 @。

    对于第 10 点,除了+1 在这两种情况下,你得到几乎相同的东西,所以它说ptr[1] = num+1;

    对于第 11 点,*ptr=num-2; 会覆盖第 9 点中写入的内容——即,*ptr 与 *(ptr+0) 相同,因此等同于 ptr[0] = num-2;

    您在第 12 点中是正确的,ptr 已设置为指向 num。这意味着在第 13 点中,赋值等同于 num=sum_ds(num-2);

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-11-29
      • 2013-10-31
      • 2015-12-15
      • 1970-01-01
      • 2016-03-06
      • 1970-01-01
      • 2014-02-14
      相关资源
      最近更新 更多