简体   繁体   English

调用C ++函数而不进行对象初始化

[英]C++ function called without object initialization

Why does the following code run? 为什么运行以下代码?

#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;
}

The output is 输出是

num=5

I compile this using gcc and I get only the following compiler warning at line 10: 我使用gcc编译它,我在第10行只得到以下编译器警告:

( warning: 'a' is used uninitialized in this function ) 警告:'a'在此函数中未初始化使用

But as per my understanding, shouldn't this code not run at all? 但根据我的理解,这段代码不应该根本不运行吗? And how come it's assigning the value 5 to num when num doesn't exist because no object of type A has been created yet? 当num不存在时,为什么将值5分配给num,因为还没有创建类型A的对象?

The code produces undefined behavior, because it attempts to dereference an uninitialized pointer. 代码产生未定义的行为,因为它试图取消引用未初始化的指针。 Undefined behavior is unpredictable and follows no logic whatsoever. 未定义的行为是不可预测的,并且不遵循任何逻辑。 For this reason, any questions about why your code does something or doesn't do something make no sense. 出于这个原因,任何有关为什么代码执行某些操作或不执行某些操作的问题都没有任何意义。

You are asking why it runs? 你问为什么它会运行? It doesn't run. 它没有运行。 It produces undefined behavior . 它会产生未定义的行为

You are asking how it is assigning 5 to a non-existing member? 您问的是如何为不存在的成员分配5? It doesn't assign anything to anything. 它没有任何东西。 It produces undefined behavior . 它会产生未定义的行为

You are saying the output is 5 ? 你说输出是5 Wrong. 错误。 The output is not 5 . 输出不是5 There's no meaningful output. 没有有意义的输出。 The code produces undefined behavior . 代码产生未定义的行为 Just because it somehow happened to print 5 in your experiment means absolutely nothing and has no meaningful explanation. 只是因为它在某种程度上碰巧在你的实验中打印5意味着什么都没有,也没有任何有意义的解释

A* a; is an uninitialized pointer. 是一个未初始化的指针。

the value you see is garbage, and you are luck you did not end up with a crash. 你看到的价值就是垃圾,幸运的是你并没有因为崩溃而结束。

there is no initialization here. 这里没有初始化。

there is no assignment here. 这里没有任务。

your class happens to be simple enough that more serious issues are not exhibited. 你的课程很简单,没有表现出更严重的问题。

A* a(0); would lead to a crash. 会导致崩溃。 an uninitialized pointer would lead to a crash in some cases, and is more easily reproduced with more complex types. 在某些情况下,未初始化的指针会导致崩溃,并且更容易使用更复杂的类型进行复制。

this is the consequence of dealing with uninitialized pointers and objects, and it points out the importance of compiler warnings. 这是处理未初始化的指针和对象的结果,它指出了编译器警告的重要性。

You haven't initialized *a . 你还没有初始化*a

Try this: 试试这个:

#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;
}

Not initializing pointers (properly) can lead to undefined behavior. 不初始化指针(正确)可能导致未定义的行为。 If you're lucky, your pointer points to a location in the heap which is up for initialization*. 如果你很幸运,你的指针指向堆中的一个位置,用于初始化*。 (Assuming no exception is thrown when you do this.) If you're unlucky, you'll overwrite a portion of the memory being used for other purposes. (假设执行此操作时不会抛出任何异常。)如果您运气不好,则会覆盖用于其他目的的部分内存。 If you're really unlucky, this will go unnoticed. 如果你真的不走运,这将被忽视。

This is not safe code; 这不是安全的代码; a "hacker" could probably exploit it. “黑客”可能会利用它。

*Of course, even when you access that location, there's no guarantee it won't be "initialized" later. *当然,即使您访问该位置,也无法保证以后不会“初始化”。


"Lucky" (actually, being "lucky" makes it more difficult to debug your program): “幸运”(实际上,“幸运”使调试程序变得更加困难):

// uninitialized memory 0x00000042 to 0x0000004B
A* a;
// a = 0x00000042;
*a = "lalalalala";
// "Nothing" happens

"Unlucky" (makes it easier to debug your program, so I don't consider it "unlucky", really): “倒霉”(使你的程序调试更容易,所以我不认为它“不吉利”,真的):

void* a;
// a = &main;
*a = "lalalalala";
// Not good. *Might* cause a crash.
// Perhaps someone can tell me exactly what'll happen?

This is what I think happens. 这就是我认为的情况。

a->foo(); works because you are just calling A::foo(a). 因为你只是调用A::foo(a).起作用A::foo(a).

a is a pointer type variable that is in main's call stack. a是main的调用堆栈中的指针类型变量。 foo() function may throw a segmentation error when location a is accessed, but if it does not, then foo() just jumps some locations from a and overwrites 4 bytes of memory with the value 5. It then reads out the same value. 当访问位置a时, foo()函数可能会抛出分段错误,但如果没有,则foo()只是从a跳转一些位置并用值5覆盖4个字节的内存。然后它读出相同的值。

Am I right or wrong? 我是对还是错? Please let me know, I am learning about call stacks and would appreciate any feedback on my answer. 请让我知道,我正在学习电话堆栈,并希望得到任何有关我的答案的反馈。

Also look at the following code 另请查看以下代码

#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();

That invokes undefined behaviour . 这会调用未定义的行为 Most commonly it crashes the program. 最常见的是它崩溃了程序。

The section §4.1/1 from the C++03 Standard says, C ++ 03标准中的§4.1/ 1节说,

An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. 可以将非函数非数组类型T的左值(3.10)转换为右值。 If T is an incomplete type, a program that necessitates this conversion is ill-formed. 如果T是不完整类型,则需要进行此转换的程序格式不正确。 If the object to which the lvalue refers is not an object of type T and is not an object of a type derived from T, or if the object is uninitialized, a program that necessitates this conversion has undefined behavior . 如果左值引用的对象不是类型T的对象,并且不是从T派生的类型的对象 ,或者如果对象未初始化,则需要此转换的程序具有未定义的行为 If T is a non-class type, the type of the rvalue is the cv-unqualified version of T. Otherwise, the type of the rvalue is T. 如果T是非类型,则rvalue的类型是T的非限定版本。否则,rvalue的类型是T.

See this similar topic: Where exactly does C++ standard say dereferencing an uninitialized pointer is undefined behavior? 看到这个类似的主题: C ++标准在哪里确实解除引用未初始化的指针是未定义的行为?


And how come it's assigning the value 5 to num when num doesn't exist because no object of type A has been created yet. 当num不存在时,为什么它将值5分配给num,因为还没有创建类型A的对象。

It's called being lucky. 它被称为幸运。 But it wouldn't happen always . 但它不会总是发生。

Upon object creation, the class members are allocated for that particular object even if you don't use the keyword new , since the object is pointer to class. 在创建对象时,即使您不使用关键字new ,也会为该特定对象分配类成员,因为该对象是指向类的指针。 So your code runs fine and gives you the value of num , but GCC issues a warning because you've not instantiated the object explicitly. 因此,您的代码运行正常,并为您提供num的值,但GCC会发出警告,因为您没有显式实例化该对象。

I'll point (hehe) you to a previous answer of mine to a very similar question: Tiny crashing program 我会指出(呵呵)你以前回答我的一个非常相似的问题: 微小的崩溃程序

Basically you're overwriting the envs stack variable with your pointer because you haven't added envs to the main declaration. 基本上你用指针覆盖了envs堆栈变量,因为你没有在main声明中添加envs

Since envs is an array of arrays (strings), it's actually very much allocated, and you're overwriting the first pointer in that list with your 5 , then reading it again to print with cout . 由于envs是一个数组(字符串)数组,它实际上已经分配了很多,你用你的5覆盖了该列表中的第一个指针,然后再次读取它以使用cout进行打印。

Now this is an answer to why it happens. 现在这是它发生的原因的答案。 You should obviously not rely on this. 你显然不应该依赖于此。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM