[英]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.