简体   繁体   中英

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:

( warning: 'a' is used uninitialized in this function )

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?

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? It doesn't assign anything to anything. It produces undefined behavior .

You are saying the output is 5 ? Wrong. The output is not 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.

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 .

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 is a pointer type variable that is in main's call stack. 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.

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,

An lvalue (3.10) of a non-function, non-array type T can be converted to an rvalue. If T is an incomplete type, a program that necessitates this conversion is ill-formed. 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 . 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.

See this similar topic: Where exactly does C++ standard say dereferencing an uninitialized pointer is undefined behavior?


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.

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. 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.

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.

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 .

Now this is an answer to why it happens. You should obviously not rely on this.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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