[英]C++ function called without object initialization
為什么運行以下代碼?
#include <iostream>
class A {
int num;
public:
void foo(){ num=5; std::cout<< "num="; std::cout<<num;}
};
int main() {
A* a;
a->foo();
return 0;
}
輸出是
num=5
我使用gcc編譯它,我在第10行只得到以下編譯器警告:
( 警告:'a'在此函數中未初始化使用 )
但根據我的理解,這段代碼不應該根本不運行嗎? 當num不存在時,為什么將值5分配給num,因為還沒有創建類型A的對象?
代碼產生未定義的行為,因為它試圖取消引用未初始化的指針。 未定義的行為是不可預測的,並且不遵循任何邏輯。 出於這個原因,任何有關為什么代碼執行某些操作或不執行某些操作的問題都沒有任何意義。
你問為什么它會運行? 它沒有運行。 它會產生未定義的行為 。
您問的是如何為不存在的成員分配5? 它沒有任何東西。 它會產生未定義的行為 。
你說輸出是5
? 錯誤。 輸出不是5
。 沒有有意義的輸出。 代碼產生未定義的行為 。 只是因為它在某種程度上碰巧在你的實驗中打印5
意味着什么都沒有,也沒有任何有意義的解釋
A* a;
是一個未初始化的指針。
你看到的價值就是垃圾,幸運的是你並沒有因為崩潰而結束。
這里沒有初始化。
這里沒有任務。
你的課程很簡單,沒有表現出更嚴重的問題。
A* a(0);
會導致崩潰。 在某些情況下,未初始化的指針會導致崩潰,並且更容易使用更復雜的類型進行復制。
這是處理未初始化的指針和對象的結果,它指出了編譯器警告的重要性。
你還沒有初始化*a
。
試試這個:
#include <iostream>
class A
{
int num;
public:
void foo(){ std::cout<< "num="; num=5; std::cout<<num;}
};
int main()
{
A* a = new A();
a->foo();
return 0;
}
不初始化指針(正確)可能導致未定義的行為。 如果你很幸運,你的指針指向堆中的一個位置,用於初始化*。 (假設執行此操作時不會拋出任何異常。)如果您運氣不好,則會覆蓋用於其他目的的部分內存。 如果你真的不走運,這將被忽視。
這不是安全的代碼; “黑客”可能會利用它。
*當然,即使您訪問該位置,也無法保證以后不會“初始化”。
“幸運”(實際上,“幸運”使調試程序變得更加困難):
// uninitialized memory 0x00000042 to 0x0000004B
A* a;
// a = 0x00000042;
*a = "lalalalala";
// "Nothing" happens
“倒霉”(使你的程序調試更容易,所以我不認為它“不吉利”,真的):
void* a;
// a = &main;
*a = "lalalalala";
// Not good. *Might* cause a crash.
// Perhaps someone can tell me exactly what'll happen?
這就是我認為的情況。
a->foo();
因為你只是調用A::foo(a).
起作用A::foo(a).
a
是main的調用堆棧中的指針類型變量。 當訪問位置a
時, foo()
函數可能會拋出分段錯誤,但如果沒有,則foo()
只是從a跳轉一些位置並用值5覆蓋4個字節的內存。然后它讀出相同的值。
我是對還是錯? 請讓我知道,我正在學習電話堆棧,並希望得到任何有關我的答案的反饋。
另請查看以下代碼
#include<iostream>
class A {
int num;
public:
void foo(){ num=5; std::cout<< "num="; std::cout<<num;}
};
int main() {
A* a;
std::cout<<"sizeof A is "<<sizeof(A*)<<std::endl;
std::cout<<"sizeof int is "<<sizeof(int)<<std::endl;
int buffer=44;
std::cout<<"buffer is "<<buffer<<std::endl;
a=(A*)&buffer;
a->foo();
std::cout<<"\nbuffer is "<<buffer<<std::endl;
return 0;
}
A* a;
a->foo();
這會調用未定義的行為 。 最常見的是它崩潰了程序。
C ++ 03標准中的§4.1/ 1節說,
可以將非函數非數組類型T的左值(3.10)轉換為右值。 如果T是不完整類型,則需要進行此轉換的程序格式不正確。 如果左值引用的對象不是類型T的對象,並且不是從T派生的類型的對象 ,或者如果對象未初始化,則需要此轉換的程序具有未定義的行為 。 如果T是非類型,則rvalue的類型是T的非限定版本。否則,rvalue的類型是T.
看到這個類似的主題: C ++標准在哪里確實解除引用未初始化的指針是未定義的行為?
當num不存在時,為什么它將值5分配給num,因為還沒有創建類型A的對象。
它被稱為幸運。 但它不會總是發生。
在創建對象時,即使您不使用關鍵字new
,也會為該特定對象分配類成員,因為該對象是指向類的指針。 因此,您的代碼運行正常,並為您提供num
的值,但GCC會發出警告,因為您沒有顯式實例化該對象。
我會指出(呵呵)你以前回答我的一個非常相似的問題: 微小的崩潰程序
基本上你用指針覆蓋了envs
堆棧變量,因為你沒有在main
聲明中添加envs
。
由於envs
是一個數組(字符串)數組,它實際上已經分配了很多,你用你的5
覆蓋了該列表中的第一個指針,然后再次讀取它以使用cout
進行打印。
現在這是它發生的原因的答案。 你顯然不應該依賴於此。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.