为什么运行以下代码?
#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的对象?
答案 0 :(得分:4)
代码产生未定义的行为,因为它试图取消引用未初始化的指针。未定义的行为是不可预测的,并且不遵循任何逻辑。出于这个原因,任何关于为什么你的代码做某事或什么不做某事的问题都没有意义。
你问为什么会这样?它没有运行。它会产生未定义的行为。
您问的是如何为不存在的成员分配5?它没有任何东西。它会产生未定义的行为。
你说输出是5
?错误。输出不是5
。没有有意义的输出。代码产生未定义的行为。只是因为它在某种程度上碰巧在你的实验中打印5
意味着什么都没有,并且没有任何有意义的解释。
答案 1 :(得分:2)
A* a;
是一个未初始化的指针。
你看到的价值就是垃圾,幸运的是你不会因为崩溃而结束。
这里没有初始化。
这里没有作业。
你的课程很简单,没有表现出更严重的问题。
A* a(0);
会导致崩溃。在某些情况下,未初始化的指针会导致崩溃,并且更容易使用更复杂的类型进行复制。
这是处理未初始化的指针和对象的结果,它指出了编译器警告的重要性。
答案 2 :(得分:2)
您尚未初始化*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?
答案 3 :(得分:1)
A* a;
a->foo();
调用未定义的行为。最常见的是它会导致程序崩溃。
C ++ 03标准中的§4.1/ 1部分说,
a的左值(3.10) 非功能,非数组类型T即可 转换为右值。如果T是 不完整的类型,一个程序 需要这种转换是必要的 病态的。如果对象是哪个 左值引用不是类型的对象 T并不是一种类型的对象 派生自T,或如果对象是 未初始化的,一个程序 需要这种转换 未定义的行为。如果T是 非类型,右值的类型 是C的不合格版本的T. 否则,右值的类型是 吨。
请参阅此类似主题:Where exactly does C++ standard say dereferencing an uninitialized pointer is undefined behavior?
当num不存在时,为什么它将值5分配给num,因为还没有创建类型A的对象。
这被称为幸运。但它不会发生总是。
答案 4 :(得分:1)
这就是我认为的情况。
a->foo();
因为您只是致电A::foo(a).
a
是一个指针类型变量,位于main的调用堆栈中。 foo()
函数在访问位置a
时可能会抛出分段错误,但如果没有,则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;
}
答案 5 :(得分:0)
创建对象后,即使您不使用关键字new
,也会为该特定对象分配类成员,因为该对象是指向类的指针。因此,您的代码运行正常,并为您提供num
的值,但GCC会发出警告,因为您没有明确地实例化该对象。
答案 6 :(得分:0)
我会指出(嘿嘿)你对我之前的一个非常相似的问题的回答:Tiny crashing program
基本上你用指针覆盖了envs
堆栈变量,因为你没有在envs
声明中添加main
。
由于envs
是一个数组(字符串)数组,它实际上是非常分配的,你用你的5
覆盖该列表中的第一个指针,然后再次读取它以打印出来cout
。
现在这是为什么的答案。你显然不应该依赖。