玩弄指標我產生了一種我無法解釋的行為。當我在輔助函式中列印一個字串時,我在列印一個未初始化的 char* 時在我的 main 中得到一個段錯誤(我認為這是合理的),棘手的部分是如果我以同樣的方式執行其他所有操作并且不使用 printf 行在輔助函式中,程式運行良好,以至于它令人驚訝地隱式回傳堆疊字串的地址。
這是我撰寫并運行它的程式(在 Fedora 35 上,安裝得像 vanilla 一樣)。即使您不能完全解釋該問題,也歡迎任何有助于理解這一點的幫助。
#include<stdio.h>
#include <stdlib.h>
#include<string.h>
/* This function generates a stack string and fills it */
char* generate_stack_string()
{
char stack_string[70];
strcpy(stack_string,"test");
/*the next line is the one that when added crashes the program*/
//printf("adress ( %p ) and text ( %s ) of stack string inside the function\n\n",stack_string, stack_string);
//function compiles and runs normally despite no return value, but seems to return something anyhow
}
int main()
{ char *pointer_in_main;
//this pointer is never malloced, this is intentional, to provoke the behaviour
printf("pointer_in_main adress before function call %p\n\n",pointer_in_main);
pointer_in_main = generate_stack_string();
printf("pointer_in_main adress after function call %p\n\n",pointer_in_main);
//This is where program crashses when the heap string is printed
printf("pointer_in_main content after function call %s\n\n",pointer_in_main);
}
uj5u.com熱心網友回復:
了解這都是未定義的行為后,讓我們看一下您的函式:
char* generate_stack_string()
{
char stack_string[70];
strcpy(stack_string,"test");
/*the next line is the one that when added crashes the program*/
//printf("adress ( %p ) and text ( %s ) of stack string inside the function\n\n",stack_string, stack_string);
//function compiles and runs normally despite no return value, but seems to return something anyhow
}
這個函式沒有回傳值,盡管它被宣告為這樣做。從函式回傳值通常通過將值放入暫存器來執行。由于沒有return宣告,因此所討論的暫存器中發生的任何值都將是回傳的值。由于函式中的最后一條陳述句也是函式呼叫,因此暫存器中的值就是從該函式回傳的值。在 的情況下strcpy,這恰好是第一個引數的值,即stack_string轉換為指標。因此,幸運的是,您正在回傳您打算回傳的指標。
運行此代碼,我得到以下輸出:
pointer_in_main adress before function call (nil)
pointer_in_main adress after function call 0x7ffe3c7cd3e0
pointer_in_main content after function call test
您也很“幸運”,stack_string上次呼叫printfin時,之前使用的記憶體內容沒有被覆寫main。
現在,如果我們取消注釋 in 中的printf呼叫generate_stack_string,我會得到以下輸出:
pointer_in_main adress before function call (nil)
adress ( 0x7ffec0587ee0 ) and text ( test ) of stack string inside the function
pointer_in_main adress after function call 0x51
Segmentation fault (core dumped)
在這里我們可以看到回傳的值超出了行程的有效地址空間,因此嘗試取消參考它會導致段錯誤。但是這個值是多少?
查看更新后的函式,執行的最后一行是對printf. 該函式回傳列印的字符數,0x51(十進制81)恰好是列印的字符數。所以這個值留在了用于從函式回傳值的暫存器中,所以這就是回傳的內容。
But again, this is all undefined behavior. With the modified code, I get the same segfault regardless of optimization level. If I run the original code with -O1 or higher, I get this output:
pointer_in_main adress before function call (nil)
pointer_in_main adress after function call (nil)
pointer_in_main content after function call (null)
So when your program has undefined behavior, all bets are off.
uj5u.com熱心網友回復:
您的程式中存在許多問題,其中大部分會導致未定義的行為。例如,指標pointer_in_main未初始化
,并且您在呼叫printf時使用了此指標:
printf("pointer_in_main adress before function call %p\n\n",pointer_in_main);//this is undefined behavior because pointer_in_main is uninitialized
//whatever happens after this is not reliable
在上述對 的呼叫中printf,您使用的是pointer_in_main導致未定義行為的未初始化指標。
未定義的行為意味著任何事情1都可能發生,包括但不限于給出預期輸出的程式。但永遠不要依賴(或基于)具有未定義行為的程式的輸出。
所以你看到的輸出(也許看到)是未定義行為的結果。正如我所說,不要依賴具有 UB 的程式的輸出。該程式可能會崩潰。
因此,使程式正確的第一步是洗掉 UB。只有這樣,您才能開始推理程式的輸出。
1有關未定義行為的更技術上準確的定義,請參見此處提到:對程式的行為沒有限制。
uj5u.com熱心網友回復:
對于初學者來說,這個宣告
printf("pointer_in_main adress before function call %p\n\n",pointer_in_main);
具有未定義的行為,因為指標未初始化并且具有不確定的值。
char *pointer_in_main;
此函式不回傳任何內容
char* generate_heap_string()
{
char heap_string[70];
strcpy(heap_string,"test");
/*the next line is the one that when added crashes the program*/
//printf("adress ( %p ) and text ( %s ) of heap string inside the function\n\n",heap_string,heap_string);
//function compiles and runs normally despite no return value, but seems to return something anyhow
}
所以再次呼叫這些 printf
pointer_in_main = generate_heap_string();
printf("pointer_in_main adress after function call %p\n\n",pointer_in_main);
//This is where program crashses when the heap string is printed
printf("pointer_in_main content after function call %s\n\n",pointer_in_main);
呼叫未定義的行為。
即使您將通過以下方式更改功能
char* generate_heap_string()
{
char heap_string[70];
strcpy(heap_string,"test");
/*the next line is the one that when added crashes the program*/
//printf("adress ( %p ) and text ( %s ) of heap string inside the function\n\n",heap_string,heap_string);
return heap_string;
}
那么這個 printf 的呼叫
pointer_in_main = generate_heap_string();
//...
//This is where program crashses when the heap string is printed
printf("pointer_in_main content after function call %s\n\n",pointer_in_main);
將再次呼叫未定義的行為,因為在函式中宣告的本地陣列在退出函式后將不再存在并且指標pointer_in_main具有無效值。
那就是在這個電話中
//This is where program crashses when the heap string is printed
printf("pointer_in_main content after function call %s\n\n",pointer_in_main);
試圖取消對無效指標的參考。
printf 呼叫之間的區別在于,在一種情況下,您試圖列印指標的無效值,但在另一種情況下
//This is where program crashses when the heap string is printed
printf("pointer_in_main content after function call %s\n\n",pointer_in_main);
您正在使用無效值來訪問記憶體。
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/446141.html
