簡體   English   中英

在 C 中打印懸空指針

[英]Printing Dangling Pointers in C

#include <stdio.h>

int main()
{
    int *ptr;
    {
        int x = 2;
        ptr = &x;
    }

    printf("%x %d", ptr, *ptr);
    return 0;
}

輸出:x 的地址,x 的值。

這里, ptr應該是一個懸空指針,對吧? 然而,它仍然存儲x的地址。 即使在刪除該塊之后,它如何仍然指向x的值?

#include <stdio.h>

int * func (int n)
{
    int temp;
    int *ptr = &temp;
    temp = n * n;
    return ptr;
}

int main()
{
    int n = 4;
    int *p = func(4);
    printf("%x, %d", p, *p);
    return 0;
}

輸出:臨時地址,16

在這個程序中,數據變量temp和它的指針變量ptr是在單獨的函數中創建的。 為什么它會產生正確的結果?

#include <stdio.h>

int * func (int n)
{
    int temp;
    int *ptr = &temp;
    temp = n * n;

    for (int i = 0; i < 10; i++)
        printf("%d ", *ptr);
    return ptr;
}
    
int main()
{
    int n = 4;
    int *p = func(4);
    printf("\n%x, %d", p, *p);
    for (int i = 0; i < 10; i++)
        printf("%d ", *ptr);
    *p = 12;
    printf("%d\n", *p);
    printf("%d\n", *p);
    return 0;
}

輸出:16 16 16 16 16 16 16 16 16 16
臨時地址,1
16 16 16 16 16 16 16 16 16 16
12
12

除了 for 循環之外,上面的程序類似於第二個程序。 main()函數中,它每次都給出正確的輸出。 即使我嘗試將其更改為*p = 10 ,無論打印多少次,它仍然會提供正確的輸出。

但是在第二個程序中,由於未定義的行為,它只給出一次正確的輸出。 它在第一個 printf 之后給出垃圾值。

但是在第三個程序中,它如何仍然每次都給出正確的輸出?

我的問題是:

  1. 指針變量指向一個超出范圍的局部變量,但仍然打印正確的輸出,並且可以通過更改指針變量的值來訪問它。 為什么?
  2. 與 increment() 中創建的 temp 一樣,ptr 也是在本地創建的。 為什么它始終正確打印值而沒有任何警告或錯誤? 如果 for 循環不存在,打印一次后也會報錯。 為什么會這樣?
    當我通過 temp 時,我收到警告和分段錯誤錯誤。 但是為什么 ptr 這個局部變量會正確打印值呢?
  3. 在第一個程序中,多次打印 *ptr 后,它給出了正確的輸出,並且我能夠更改 *ptr = 1; 在第一個 printf 之后。 為什么即使變量超出范圍,我也可以訪問 ptr?

謝謝大家的回答。 我現在從你所有的答案中理解。 非常感謝你。

您的兩個程序行為都未定義。

在第一個代碼中,您的程序正在通過其地址訪問x ,該地址位於聲明它的塊之外。 x是一個局部(自動)非靜態變量,它的生命周期僅限於它的作用域1),即它被聲明的塊。 任何在其生命周期之外訪問它的嘗試都將導致未定義的行為2) 第二個代碼中的temp變量也是如此。

未定義的行為包括它可能會錯誤地執行(崩潰或靜默地生成錯誤的結果),或者它可能恰好按照程序員的意圖去做。

此外,用於打印指針的正確格式說明符是%p


1)。 來自 C11 標准#6.2.1p4 [強調我的]

每個其他標識符的范圍由其聲明的位置決定(在聲明符或類型說明符中)。 如果聲明標識符的聲明符或類型說明符出現在任何塊或參數列表之外,則標識符具有文件范圍,該范圍在翻譯單元的末尾終止。 如果聲明標識符的聲明符或類型說明符出現在塊內或函數定義中的參數聲明列表內,則標識符具有塊作用域,在相關塊的末尾終止 ......

2)。 來自 C11 標准#6.2.4p2 [強調我的]

2 對象的生命周期是程序執行期間保證為其保留存儲空間的部分。 一個對象存在,有一個常量地址,33) 並在其整個生命周期中保留其最后存儲的值。34)如果對象在其生命周期之外被引用,則行為是未定義的。 當指針指向(或剛剛過去)的對象到達其生命周期結束時,指針的值變得不確定。

第一個代碼中的“x”和第二個代碼中的“temp”是局部變量,因此當變量超出定義的塊時,它會從堆棧中釋放。

‘ptr’和‘p’是指向這些局部變量地址的指針,但是在局部變量從堆棧中釋放后,這些指針中存儲的值就無效了。

局部變量釋放后,值是否留在內存中,是開發工具和環境的問題。 即釋放棧,然后清空占用局部變量的指針的內存,在OS或編譯器中被處理,關鍵是你不能再使用該地址的值有效。

當我回顧 VC++2008 時,局部變量被釋放后,指針沒有更多的有效值。 它具有隨機值。

我已經通過 IDA 反匯編了您的第三個程序。
func() 函數被編譯為 main() 函數的一部分,而不是作為獨立函數編譯。

在此處輸入圖片說明 因此,保留了正確的值。
我猜這是編譯過程中的優化結果。

但是,當我向 func() 添加一行時,程序的結果是不同的。
在此處輸入圖片說明

在這種情況下,編譯器將“func()”識別為一個函數。
出現了預期的結果,程序在 '*p = 12' 處崩潰。

在此處輸入圖片說明

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM