【问题标题】:can pointers exist outside their scope指针能否存在于其范围之外
【发布时间】:2018-02-22 11:55:49
【问题描述】:
  1. 为什么我第二次调用函数时指针“a”指向正确的位置

  2. 因为在对“cr”的第二次函数调用期间,if 块中的语句不会被执行,所以指针“a”是如何记住其先前位置的,即使它不是静态变量

代码:

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

typedef struct heavy{
    int data;
    struct heavy *nxt;
}ode;

void cr(ode**);
void prin(ode*);

int main()
{
    ode*head=NULL;
    cr(&head);cr(&head);
    prin(head);
    getch();
    return 0;
}

void cr(ode**p)
{
    ode*temp,*a;
    temp=*p;

    if(temp==NULL)
    {
        a=(ode*)malloc(sizeof(ode));
        a->data=1;
        a->nxt=(ode*)malloc(sizeof(ode));
        *p=a;
        a=a->nxt;
        a->nxt=NULL;
    }else{
        a->data=2;
        a->nxt=NULL;
    }
}

void prin(ode*head)
{
    if(head==NULL)
        printf("list is empty");
    while(head!=NULL)
    {
        printf("%d",head->data);
        head=head->nxt;
    }
}

【问题讨论】:

  • 真的,这是你最好的问题吗? -> why is this code working
  • 显式忘记会浪费 CPU 周期。不要依赖未定义的行为。
  • 正确缩进您的代码。机器(编译器)可以读取和编译任何东西,但对于人类来说,它需要在将文本块读取为 code 时做出一点sense
  • void cr(ode**p) 你应该检查a 因为未初始化使用。 -->> a-&gt;data=2;。如果temp != NULL 会发生什么?

标签: c pointers linked-list


【解决方案1】:

当您没有为局部变量赋值时(在这种情况下,我们谈论的是a),程序将以不可预测的方式运行,因此我们说未定义的行为。这意味着您的程序正确运行只是偶然,您应该始终记住在使用变量之前分配值。

特别是在这种情况下,我可以猜到为什么每次运行它都会起作用,这与函数调用在 C 中的工作方式有关。让我解释一下。

当我们调用一个函数时,stack 中的一个新 frame(层)(内存中的一个地方,局部变量和其他“local-ish”东西被存储)。正如我刚才所说的那样,堆栈是分层组织的。让我举个例子。

如果在一个名为 George() 的特定函数中,我声明并使用了 2 个局部变量

int George(){
    int a;
    int b;
    a = 5;
    return 0;
}

编译器会知道需要 2 个变量的空间,所以当我调用函数时,它会为要存储的这 2 个局部变量保留空间。新的堆栈框架将类似于:

| 'a': ____ | <-- 4 bytes space for variable a
| 'b': ____ | <-- 4 bytes space for variable b
|-----------|

(请记住,这不是堆栈的真实表示,但足以解释发生了什么)

为变量值保留的那些空间没有设置为默认值,它们包含之前内存中的内容(现在我们无法猜测)。 p>

当我从另一个函数(即main)调用此函数时,具有该形状的堆栈帧将被添加(push)到堆栈中:

int main(){
    int hello = 7;
    int hey;
    hey = George(); // Here we make the function call
    return 0;
}

堆栈将类似于:

STACK:
    | 'a': ____    | <- 'George' stack frame, containing
    | 'b': ____    |    local variables of George
    |--------------|
    | 'hello': 7   | <- 'main' stack frame, containing
    | 'hey': ____  |    local variables of main
    |--------------|

在 George 的第 3 行之后,就在 return 之前,堆栈将是:

 STACK:
      | 'a': 5       | <- Variable a has been set to 5
      | 'b': ____    |    
      |--------------|
      | 'hello': 7   | 
      | 'hey': ____  |   
      |--------------|

然后会有一个return。这里堆栈为pop'd,表示删除了一个帧(我们在main函数“domain”中返回,所以我们丢弃了George的局部变量)。

STACK:
    | 'hello': 7   | <- 'main' stack frame, with hey replaced with
    | 'hey': 0     |    George return value (0)
    |--------------|

一切正常,但是我们刚刚弹出的内存没有设置为 0 或其他默认值。在其他程序覆盖它之前,它会一直保持这种状态。这意味着,虽然我们不再使用这些值,但它们可能仍然存在。

在这种状态下,如果我们再次调用 George,我们将再次推送我们的堆栈:

 STACK:
      | 'a': ____    | <- 'a' address is in the same position
      | 'b': ____    |    where there was the 'a' in the previous
      |--------------|    function call to Giorgio
      | 'hello': 7   | 
      | 'hey': ____  |   
      |--------------|

在这种状态下,如果我们不给a 赋值,它将(可能)包含a 在上一次函数调用中的值,因为“新”a 具有相同的值'previous' a 的地址,因为调用的函数是相同的,并且中间没有其他函数调用可以覆盖以前的值。如果这样做,在我们为新的局部变量a 赋值之前,它包含5

你的程序运行时也会发生同样的情况

cr(&head);
cr(&head);

两次,一个在另一个之前。您的局部变量a 的值可能保持不变。

无论如何,除了解释,永远不要在你的代码中使用这种行为。这是一种非常非常糟糕的编程方式,而且结果通常是不可预测的。

我希望我的解释不会太糟糕。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-31
    • 2011-07-22
    • 1970-01-01
    • 2017-09-30
    • 1970-01-01
    • 2021-09-17
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多