int*& f(int*& x, int* y){
int** z = &y;
*z = x;
return *z;
}
大家好,我在考試中得到了這個代碼,但我遇到了一些問題。
我的理解是,給定對指標 (x) 和在函式體中構造的指標副本 (y) 的參考,正在創建本地雙指標 (z) 并用 y l 值初始化,然后取消參考 1 次,所以 y 正在被訪問,并且 y noe 中包含的地址成為 x 中包含的地址。之后 *z 作為我假設 y 的指標參考回傳。
如果我的前一部分是正確的,我不會解釋為什么回傳在函式退出時被釋放的 y(因為是臨時引數)不會在程式中產生任何問題,實際上考試的答案是函式回傳了一個懸空參考,我同意這一點,但是,復制粘貼代碼,并“播放”回傳的變數,甚至在回傳引數后做隨機的東西,以便“編輯堆疊”,其中釋放的 y 仍然存在并“準備好被覆寫”(如果我仍然正確的話)不會出現程式的任何未定義行為。
我唯一的解釋是 return 只復制 y 中包含的 r 值,或者它可能回傳 l 值和 r 值(因為回傳對指標的參考)但是當與外部指標關聯時“呼叫”函式 y 不'沒有被正確地釋放,或者以某種方式獲取值的指標代替了被釋放的 y 指標。
在底部您可以找到用于測驗函式 int*& f(int*&, int*) 的代碼。
我的問題是:這是一個適當的懸空參考還是可以在程式中使用這種東西的邊界情況?
#include <iostream>
using namespace std;
int a = 65;
int*& f(int*& x, int* y)
{
cout<<"indirizzo di y: "<<&y<<endl;
cout<<"indirizzo di x: "<<&x<<endl;
int** z = &y;
cout<<"indirizzo di *z prima: "<<*z<<endl;
*z=x;
cout<<"indirizzo di *z dopo: "<<*z<<endl;
cout<<"y punta a: "<<y<<endl;
cout<<"z dopo: "<<z<<endl;
return *z;
}
int*& crashaFisso() //function that crashes every time with a "proper" dangling reference
{
int a=10;
int* x =&a;
return x;
}
int main()
{
system("CLS");
int b = 20;
int codicerandom=0;
int* i = &a;
cout<<"indirizzo di i: "<<i<<endl;
int* u = &b;
cout<<"indirizzo funzione: "<<&f(i,u)<<endl;
int* aux = f(i,u);
int* crash = crashaFisso();
cout<<"crash: "<<*crash<<endl;
cout<<"aux: "<<*aux<<endl;
for(int i=0;i<100;i )
codicerandom =i;
for(int i=0;i<100;i )
codicerandom =i;
for(int ji=100;ji>0;ji--)
{
codicerandom =ji;
for(int x=100;x>0;x--)
{
codicerandom -= x*2;
}
}
cout<<codicerandom<<endl;
cout<<"crash: "<<*crash<<endl;
cout<<"aux: "<<*aux<<endl;
cout<<"crash: "<<*crash<<endl;
cout<<"aux: "<<*aux<<endl;
a=32;
cout<<"aux: "<<*aux<<endl;
return 0;
}
uj5u.com熱心網友回復:
考試答案是該函式回傳一個懸空參考
正確的。
但是 (...) 不會呈現程式的任何未定義行為。
是什么讓你這么想的?未定義的行為并不意味著“程式無法正常作業”或“程式崩潰”。未定義的行為正是它所說的:行為不是由標準定義的。事實上,它可能“正確”作業(無論這意味著什么),標準并沒有禁止它。這就是為什么它如此危險。因為可能在您的測驗中它可以正常作業,因為硬體、作業系統、特定編譯器以及發生的其他一些假設。但問題是它不能保證正常作業。如果您更改機器、作業系統、編譯器(甚至切換優化設定)、代碼或什至在兩天后編譯它,它可能會以(ekhm)未定義的方式表現得很奇怪。
一般來說,如果存在 UB,則無法知道程式是否正確運行。您試圖通過考慮左值、右值、分配等來分析情況,而現實情況是,當存在 UB 時,整個程式毫無意義。你只是在浪費時間。
不要寫UB代碼。不管它看起來是否有效。
uj5u.com熱心網友回復:
不呈現程式的任何未定義行為。
未定義的行為意味著對程式行為沒有保證。在這種情況下,程式似乎總是可以作業。但是也允許下次你用新的編譯器版本編譯程式時(或者甚至只是再次運行相同的程式),它可能突然不再作業了。
在你的程式中f并crashaFisso回傳一個懸空參考 type int*。這本身并不是未定義的行為。您可以回傳懸空參考和指標。然而,這樣的回傳值是沒有用的,因為它們不能以任何實際的方式使用。
在您的代碼中
&f(i,u)
是第一個問題。您在此處獲取懸空參考的地址。這很可能本身已經未定義的行為,我目前并不完全確定。如果不是,則將生成的無效指標值傳遞給輸出該值的函式最多具有實作定義的行為,這可能沒問題。
線
int* aux = f(i,u);
是明確未定義的行為。您正在嘗試從懸空參考在銷毀之前參考的物件中獲取值以初始化新int*指標。那是絕對未定義的行為。
uj5u.com熱心網友回復:
未定義的行為意味著任何事情1都可能發生,包括但不限于給出預期輸出的程式。但永遠不要依賴(或基于)具有未定義行為的程式的輸出。
因此,您看到的輸出是未定義行為的結果。正如我所說,不要依賴具有 UB 的程式的輸出。
因此,使程式正確的第一步是洗掉 UB。只有這樣,您才能開始推理程式的輸出。
1對于不確定的行為在技術上更為準確的定義看這個地方要提到的是:有對程式的行為沒有任何限制。
uj5u.com熱心網友回復:
您的測驗并不像它可能的那樣尖銳:為了顯示參考懸空,您應該實際存盤參考,而不是(已經破壞的本地)變數具有的值的副本,參考是一個別名。當然,在代碼中訪問函式回傳的參考已經是未定義的行為,但很可能之前的區域變數的記憶體還沒有被覆寫。存盤它所擁有的價值會創建一個副本;副本(假設該值“意外地”仍然完好無損)是安全的,并指向通過第一個引數傳遞給函式的值,該引數是完全有效的。這就是為什么您在嘗試寫入堆疊以覆寫它時沒有成功的原因。
這是一個使用參考而不是副本的英語化最小示例(注意變數的定義dangling_ref)。我編譯并運行它兩次,使用標準優化和最大優化。只需更改編譯器選項即可更改輸出(并且,我假設是一個錯誤,確定是否輸出警告!)。這是 msys2 上的示例會話。
$ cat dangling-ref.cpp
#include <iostream>
using namespace std;
int*& dangling_ref_ret(int*& x, int* y)
{
int** z = &y;
*z = x;
cout << "ret addr " << *z << " (should be == " << y << ")" << ", val = " << **z << endl;
return *z;
}
int main()
{
int b = 1;
int* pb = &b;
int c = 2;
int*& dangling_ref = dangling_ref_ret(pb, &c);
cout << "val of dangling_ref " << dangling_ref << " is " << *dangling_ref << endl;
}
$ gcc --version
gcc (GCC) 10.2.0
Copyright (C) 2020 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
$ g -Wall -o dangling-ref dangling-ref.cpp && ./dangling-ref.exe
ret addr 0xffffcc34 (should be == 0xffffcc34), val = 1
val of dangling_ref 0xffffcc34 is 1
$ g -Wall -O3 -o dangling-ref dangling-ref.cpp && ./dangling-ref.exe
dangling-ref.cpp: In function ‘int*& dangling_ref_ret(int*&, int*)’:
dangling-ref.cpp:9:13: warning: function may return address of local variable [-Wreturn-local-addr]
9 | return *z;
| ^
dangling-ref.cpp:4:38: note: declared here
4 | int*& dangling_ref_ret(int*& x, int* y)
| ~~~~~^
ret addr 0xffffcc10 (should be == 0xffffcc10), val = 1
val of dangling_ref 0xffffcc14 is 2
Visual Studio 在除錯模式和發布模式之間的行為也不同。
您可以在godbolt上嘗試不同的編譯器和選項。
uj5u.com熱心網友回復:
Bruh,它永遠不會崩潰,因為參考是堆疊中的一個變數,并且在您退出函式時沒有超出范圍,所以它沒有被釋放,因此沒有懸空。
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/408002.html
標籤:
上一篇:矢量會導致虛假共享嗎
下一篇:是什么導致堆疊緩沖區溢位?
