Sign in to follow this  
WuTz

Can "this" be NULL?

Recommended Posts

Hi! I have another problem: When I want to build my Engine in the Release-mode, I just jumps over a lot of code, without triggering any DebugOutputs, MsgBoxes, or any other functions. I found out, that this is because of the code-optimizations. (In debug mode it works just perfectly)
void WMeshHandler::LoadFromPackage(LPWSTR Package,LPWSTR File)
{ //<---------------- from here
if(!this)
	{
		return; //Todo: WTF!?
	}
	void* Vertices;
	DWORD* Indices;
	DWORD* Attributes;





	if(wcslen(Package)<=0 || wcslen(File)<=0)
	{
		WarnBox(L"Warning! [w]tech trys to load a mesh, without having any Package information passed to the LoadFromPackage() function!");
		return;
	}



	long Size;
	CreateFromDataStruct ds;

	WCHAR n[MAX_PATH];

	WTechPackage2* Pck;
	PckHandler->FindPackageByName(Package,&Pck);
	if(!Pck)
	{
		WCHAR Msg[256];

		wsprintf(LPS Msg,L"Could NOT find a package called \"%s\"! Loading of Mesh \"%s\" failed! WTech may crashes now!",Package,File);
		ErrorBox(LPS Msg);
		return;
	}
	WCHAR fn[MAX_PATH];
	wcscpy(LPS fn,File);
	Pck->GetFileName(File); //Strip the .Ext if there is any

	OutputDebugString(L"WMeshNumVertices\n");
	wcscpy(LPS n,File);
	wcscat(LPS n,L".WMeshNumVertices");
	if(Pck->GetVariableFile(LPS n,&ds.NumVertices,NULL)==false)
	{
		ErrorBox(L"Could NOT load the mesh from the Package!");
		return;
	}

	OutputDebugString(L"WMeshNumFaces\n");
	wcscpy(LPS n,File);
	wcscat(LPS n,L".WMeshNumFaces");
	Pck->GetVariableFile(LPS n,&ds.NumFaces,NULL);

	
		
	
	OutputDebugString(L"WMeshNumIndices\n");
	wcscpy(LPS n,File);
	wcscat(LPS n,L".WMeshNumIndices");
	Pck->GetVariableFile(LPS n,&ds.IndicesNum,NULL);

	OutputDebugString(L"WMeshVertex\n");
	wcscpy(LPS n,File);
	wcscat(LPS n,L".WMeshVertex");
	Pck->GetFile(LPS n,(byte **)&Vertices,(UINT *)&Size);
	ds.Vertices=Vertices;
	
	OutputDebugString(L"WMeshIndex\n");
	wcscpy(LPS n,File);
	wcscat(LPS n,L".WMeshIndex"); //<------------- To here!
	Pck->GetFile(LPS n,(byte **)&Indices,NULL);
	ds.Indices=Indices;

	OutputDebugString(L"WMeshAttribute\n");
	wcscpy(LPS n,File);
	wcscat(LPS n,L".WMeshAttribute");
	Pck->GetFile(LPS n,(byte **)&Attributes,NULL);
	ds.Attributes=Attributes;

	CreateMeshFromData(ds,Package,LPS fn);

	/*delete[] Vertices;
	delete[] Indices;
	delete[] Attributes;*/
}

And one more mysterious thing: The "this"-pointer is NULL... Can that be? Since I know that "code-jumping" is because of the code optimizations, I turned them off. This means: Copied the Debug profile, changed the runtime library and the preDefines to the release ones. Here, it crashes, because "this" is NULL, too! Help would be great! :D

Share this post


Link to post
Share on other sites
Quote:
Original post by WuTz
And one more mysterious thing: The "this"-pointer is NULL... Can that be?
Yes:
WMeshHandler* pFoo = NULL;
pFoo->LoadFromPackage(L"Blah", L"Blah2");

Share this post


Link to post
Share on other sites
The compiler won't "optimize out" any code that has side effects.
You are probably stepping through your program without debugging information.

As for the crash, you are invoking the member function on a NULL pointer, could you post the code where the method was invoked from? Look in the call stack when it crashes.

Edit: Argh, beaten.

Share this post


Link to post
Share on other sites
Quote:
Original post by WuTz
The "this"-pointer is NULL... Can that be?

Sure.

#include <iostream>

struct Foo
{
void print_this()
{
std::cout << this << std::endl;
}
};

int main()
{
Foo* p = 0;
p->print_this();
}

Share this post


Link to post
Share on other sites
remember how the whole this-pointer mechanism actually works internally.
upon calling a non-static class method, the compiler implicitly passes a pointer to the called object as first parameter. this is what's called a thiscall, opposed to the stdcall calling convention of ordinary functions.

now when you do something like this:

MyClass* c = NULL;
c->SomeMethod();



then the this pointer inside SomeMethod will evaluate to NULL. c++ does not check this on calling, so if you're trying to access any member of MyClass, the whole thing will crash. that's probably what happened in your code.

[Edited by - ComicSansMS on March 31, 2010 8:47:03 AM]

Share this post


Link to post
Share on other sites
Hey! Great work! :)

I found out, that DXUT has decided to don't call my InitEngine function, where the pointer to the PackageHandler gets passed to my Scene-Class, which passes them to all Objects. And because this pointer was NULL, any other where NULL! :)

Thx for all the help! :D

Share this post


Link to post
Share on other sites
Beware though, that if the class has any virtual functions in it it will most likely crash when calling one of them.



struct Foo
{
virtual void bar();
};


...

Foo* foo = 0;
foo->bar(); // Crash!





Share this post


Link to post
Share on other sites
Quote:
Original post by phresnel
Quote:
Original post by ComicSansMS


Please, oh please, tell me that you are not a fan of comic sans


more like a fan of irony [rolleyes]

sorry for ot.

Share this post


Link to post
Share on other sites
Haven't ever tried calling members from the "null memory". Now I'm just thinking about it:

struct mystruct
{
// no memory used, just 1 byte used by compiler (I assume sizeof( struct )== 1)

void dododo( void )
{
// <...>
}
};

struct mystruct* p= 0;
p->dododo();

Tell me why p->do(); should crash. mystruct occupies no memory; 1 byte (or more, always depends) probably is occupied by a compiler. It might crash only if there is no preprocessing/optimization/etc. Compiler could simply make it static, because there is no other memory manually occupied in mystruct by user/developer. So, if I were a compiler, I would simply translate it into this peace of code:

struct mystruct
{
static void dododo( void )
{
// <...>
}
};

mystruct::dododo();

Only if there is a polymorphism, compiler is unable to do this, because it simply cannot know where your memory is.

But hey! Don't blame me! I'm not really sure about this behaviour. So let's see if compilers really behave like... me (now I'm going to "hack assembly", to make sure).

P.s. hey, how do you turn your code highlighting on?

Share this post


Link to post
Share on other sites
Quote:
Original post by nerijus ramanauskas
Only if there is a polymorphism, compiler is unable to do this, because it simply cannot know where is your memory.


It will PROBABLY be unable to do this, but that isn't a certainty.

The reason being, most compilers (that i know of anyways!) implement the vtable by having a hidden pointer at the beginning of your objects memory which points to the classes vtable.

However, the implemenation of how that works isn't specified in the standards so each compiler can implement it differently if they wish to.

So, one compiler might implement it in a way where in the example case, using polymorphism will work just fine :P

Minor trivia but knowledge is good so figured id clear that up (even though others have hinted at it)

Share this post


Link to post
Share on other sites
Quote:
Original post by nerijus ramanauskas
Tell me why p->do(); should crash.


It doesn't need to crash, it is undefined behavior.

There is no reason why compiler couldn't raise an exception, or terminate an application in this case.

One reason why it cannot work is this:
Foo * foo = NULL;
foo->bar(); // undefined
// is same as
(*foo).bar(); // illegal, foo is not dereferencable


Of course, NULL pointer, or value 0 might be a perfectly valid address.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
There is no reason why compiler couldn't raise an exception, or terminate an application in this case.

Absolutely agree, but there is also no reason why compiler couldn't simply optimize it "out".

Share this post


Link to post
Share on other sites
Quote:
Original post by nerijus ramanauskas
Quote:
Original post by Antheus
There is no reason why compiler couldn't raise an exception, or terminate an application in this case.

Absolutely agree, but there is also no reason why compiler couldn't simply optimize it "out".


The compiler is not required to optimize things out just because it can. In practice, in many cases it does (otherwise people would use the competitor's compiler instead), but code that expects the compiler to perform an optimization is still incorrect.

If your code doesn't actually require the this-pointer, why is it in a member function?

[Edited by - Zahlman on March 31, 2010 2:14:15 PM]

Share this post


Link to post
Share on other sites
Quote:

Original post by Zahlman
code that expects the compiler to perform an optimization is still incorrect.

Hey, no one said it was recommended to use null pointers in "real life". That's of course unrecommended to expect the compiler to perform an optimization, especially when VC++ turns it off when debugging.

Share this post


Link to post
Share on other sites
Quote:
Original post by Antheus
Foo * foo = NULL;
foo->bar(); // undefined
// is same as
(*foo).bar(); // illegal, foo is not dereferencable


My stomach tells me there is absolutely no semantic difference whatsoever between foo->bar() and (*foo).bar(). Is it wrong?

Share this post


Link to post
Share on other sites
If the member function you call from a NULL pointer, does NOT use / modify any data members, why would it not be possible? It would be like calling a regular function. Of course, the member function could be made static, but _technically_ it should be possible to do the following:

class A
{
public:
int a()
{
return 5;
}
};

A *a = NULL;
std::cout << a->a();

Share this post


Link to post
Share on other sites
Quote:
Original post by Decrius
but _technically_ it should be possible to do the following:

Only when the moon is full and your nasal demons are sleeping. The implementation details you are envisioning are not guaranteed by the standard.

Share this post


Link to post
Share on other sites
I am going to go away from the common consensus here and state that a this pointer can never be NULL.
A this pointer is available in the body of a none static member, constructor or destructor. To be defined behavior by the standard, the pointer used to call the class is required to be a valid pointer of the correct type or derived type. Therefore according to the standard the this pointer can not be null.

Share this post


Link to post
Share on other sites
Quote:
Original post by CmpDev
I am going to go away from the common consensus here and state that a this pointer can never be NULL.

...if we limit ourselves to well formed C++ programs, you'd be correct. However, it does crop up as a common cause for seemingly 'impossible' crashes involving using a simple member variable type. "All I do is read this int, a, how the hell can it be crashing?" "Your 'this' pointer is probably invalid (null, uninitialized, or dangling)."

You're welcome to propose superior terminology.

Share this post


Link to post
Share on other sites
Quote:
Original post by DevFred
Quote:
Original post by Decrius
but _technically_ it should be possible to do the following:

Only when the moon is full and your nasal demons are sleeping. The implementation details you are envisioning are not guaranteed by the standard.


Yeah, hehe, that's why I said _technically_ ;). Because for a regular member function that doesn't need any data members there is no need for a pointer to the data members (and thus the pointer could _technically_ be NULL).

Share this post


Link to post
Share on other sites
From the things that have been said, IMHO I'd say the truth lies somewhere in-between everyone's posted.

As as been shown, the this pointer can be NULL. I've had it happen to me at work and it's really annoying to see.

In practice though, as someone writing member functions, it is a valid assumption that 'this' is valid. You should never see or write code that does actually does this check.

It comes down to system responsibility and it's the callers fault should this occur. If they didn't check a return value they should have, or a function that should never return NULL actually did, that system should be fixed and not your class.

Share this post


Link to post
Share on other sites
Quote:
And one more mysterious thing: The "this"-pointer is NULL... Can that be?

The this pointer can never be null, unless you invoke undefined behaviour - such as using the member access or dereference operator on a null pointer. If you do invoke undefined behaviour like that, it's very common for this to appear to be null.

Quote:
Since I know that "code-jumping" is because of the code optimizations, I turned them off.

That's a dangerous way to fix a problem. Optimizations never cause code to behave in a different way than unoptimized code unless you've got a bug*.

Quote:
My stomach tells me there is absolutely no semantic difference whatsoever between foo->bar() and (*foo).bar(). Is it wrong?


If foo is an instance of, reference to, or pointer to a class that has overloaded them then they might be different. Otherwise they're exactly the same.


*There are exceptions - e.g. the NVR optimzation alters observable behaviour (it's explicitly allowed to by the language standard).

Share this post


Link to post
Share on other sites
Quote:
I am going to go away from the common consensus here and state that a this pointer can never be NULL.
A this pointer is available in the body of a none static member, constructor or destructor. To be defined behavior by the standard, the pointer used to call the class is required to be a valid pointer of the correct type or derived type. Therefore according to the standard the this pointer can not be null.


According to the standard there are no threads, no endianess, no windows, no .dll or .so files and export works.

Back in the real world, almost every compiler exhibits the same behaviour when a non-vitural function is called via a null pointer - this appears as null. Almost all of them are happy to create null references as well.

In fact, any compiler that doesn't exhibit that behaviour is emitting uneccessarily slow code - so if you've got optimizations turned on and your compiler doesn't do this, it's time to change compiler.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this