Solved

CArray: program crashes when it is not static - why ?

Posted on 2004-04-12
20
552 Views
Last Modified: 2013-11-20
Hello !

I have

CArray<CString, CString> m_strNewTabURLs

in my main application class.  There is also a function in this class that looks for a specified element of this CArray by looping through it.  This function uses m_strNewTabURLs.GetSize() to determine how many elements there are.

In my application InitInstance(), I state

m_strNewTabURLs.SetSize(100);

but when I come to get to the function call, I look at the debuggers output and find that it says that m_strNewTabURLs cannot be evaluated, hence the call to GetSize() crashes the code.  

The thing is, when this CArray is static, everything is fine...

Why ?  I have a std::vector in my app class that is not static, and that is fine !

TIA !
0
Comment
Question by:mrwad99
20 Comments
 
LVL 4

Expert Comment

by:bkfirebird
Comment Utility
>> This function uses m_strNewTabURLs.GetSize() to determine how many elements there are.

instead of GetSize() use GetCount()

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vclib/html/_mfc_carray.3a3a.getdata.asp
0
 
LVL 44

Expert Comment

by:AndyAinscow
Comment Utility
The SetSize assigns a chunck of memory for the array, it doesn't fill it with anything.  Are you trying to get a string back without putting one into the array?
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
AndyAinscow:

No, I am not trying to do anything except get the number of elements in the CArray via GetSize().  The question really is why this works when the CArray is static and crashes when the CArray is not !

bkfirebird:

From MSDN:

GetCount Gets the number of elements in this array.
GetSize Gets the number of elements in this array.

Is there hence a difference ?

~confused~
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
The non-static CArray should initialize when the constructor of the object it is a member of is called.  A static CArray would be constructed when the program starts up.

The only reason I can think of for the CArray not being valid when non-static is that the object it is a member of is not constructed yet.  Unfortunately, I can't think of any way the application object could have a virtual function liek InitInstance called before the constructor is called.  When I tried this myself it worked fine.

Perhpas if you can give more details on when you call the GetSize() function.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
OK, here it is:

I declare the CArray as a private member of my mainapp class.  I then intialise it:

BOOL CGui4UnexApp::InitInstance()
{
      if (!AfxSocketInit())
      {
            AfxMessageBox(IDP_SOCKETS_INIT_FAILED);
            return FALSE;
      }

      AfxEnableControlContainer();
      m_strNewTabURLs.SetSize(100); <--------------------------
                // other initialisation & code
}

In my view, I have a CBrowser control.  This displays certain web pages, but before each page is loaded I need to check it is not one of a few user specified URLs; if it is I need to take certain action.  These specific URLs are held in my CArray member that is causing the trouble.  I use a member variable in the main app class since I know only one instance of it will ever exist, and it wont get destroyed prematurely like it would if it was in a document or view instance.  Alternatively, I could put in my CMainFrame class, but for this question let's leave it as it is.  

So, when the user navigates to a page, I detect this via OnBeforeNavigate2Explorer1().  This then checks the members in my CArray (in the main app class) via

      if (m_pApp->StrExists(m_strTempURL)) {
                              // deal with this URL specially            
      }

where m_strTempURL is the URL that is about to be navigated to.  m_pApp is declared as

CGui4UnexApp*m_pApp = (CGui4UnexApp*)AfxGetApp();

and StrExists is defined as

BOOL CGui4UnexApp::StrExists(CString &str)
{
      for (int i = 0; i < i++) { <-------------------------------
            if (m_strNewTabURLs[i].Compare(str) == 0)
                  return TRUE;
      }
      return FALSE;
}

So you can see that the program is crashing on the line indicated above; m_strNewTabURLs.GetSize().

But I cannot figure out

a) Why this happens,
b) Why this does not happen when m_strNewTabURLs is static !

Any ideas ?!  Points have gone up to 60 hence.

TIA !
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
Well, first you don't need the SetSize.  The array will dynamically size as you add new strings.  I also think you missed a part of the loop, or else it will never stop.  I assume it was supposed to be

for (int i = 0;i < m_strNewTabURLs.GetSize();++i)

Now, obviously StrExists is not a static function, since the program wouldn't compile if it was, so the only alternative is that m_pApp is not actually pointing to the application.  That means that all the "members" of this invalid pointer are also invalid.

So the question is, why does the AfxGetApp() fail?  Try calling it right before the line where you call StrExist and see if this still happens.  Or put a breakpoint on the assignment of m_pApp to ensure that code is actually being called before the call to StrExists.

That's all I can think of at the moment.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Right ok; now we are getting somewhere.

Firstly, yes my for loop was incorrect; I had cut instead of copied the reference to

m_strNewTabURLs.GetSize();

I need to do a little more explaining.

I am using a CTabView (http://msdn.microsoft.com/msdnmag/issues/0600/Wicked/) - I think you were involved in one of my earlier questions about this KurtVon - that I have modified a lot to use the ActiveX browser control.  Hence each tab on each document is composed of a dialog; in my case CContainerDlg that contains the browser control etc etc.

Now, I knew I would be creating a lot of these dialogues, so decided since I would need to check the URL being navigated to in OnBeforeNavigateTo (handled in CContainerDlg) frequently, I would create a static member variable to prevent recreation over and over again of a pointer to my app class:

// .h
static CGui4UnexApp* m_pApp;

// .cpp
CGui4UnexApp* CContainerDlg::m_pApp = (CGui4UnexApp*)AfxGetApp();

This is the cause of the problem; when I use a locally declared pointer of the same nature the crash does not occur.

So why does this happen with a static member pointer ?  The pointer is valid, otherwise I would not have been able to step into StrExists in the first place !?

Thanks again !

0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
Actually, you don't need to create a pointer unless you are trying to save effort constantly typecasting.  The function AfxGetApp() is just an inline function that retrieves a global variable anyway.

As for why it could work with a static pointer.  Well, all the member functions of every object always exist.  Remember that C++ is really just C with some extra naming conventions.  There exist preprocessors that will turn any C++ program into a C program.

BOOL CGui4UnexApp::StrExists(CString& str)

is equivalent to the C function

BOOL __StrExists(CGui4UnexApp* this, CString& str)

So the "this" pointer is the only invalid thing in the function call, even if the function isn't static (the only exception is virtual functions).  Keeping that in mind, it's easy to see what happens.  Everything works fine until you try to access this->m_strNewTabsURLs. The pointer is invalid, so the member is invalid too.

This sort of thing gets caught automatically in most OO languages, but because C++ must be semantically identical to C, you get little conundrums like that.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
I am sorry KurtVon but I am not with you on that.  It sounds like you are getting at m_pMyApp not being valid; that cannot be though since it was able to locate StrExists() in the first place.

If m_pMyApp is static in CContainerDlg, then there is only one instance created no matter how many CContainerDlg instances are created, right ?  I fail to see how this affects getting at the member variables pointed to by it.

(Sorry)

?!
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
First, the App object is not static, it is a global pointer and is created by the WinMain routine.  The static pointer in your objects can point to the app object, and once it does everyone has it, but you need to assign it before any calls are made.

I think your confusion is over what the object actually is.  Let's say I have a simple object

class MyObject
{
    MyObject();
   ~MyObject();

    void DoSomething();
    virtual void DoMore();
    static void AndMoreStill();

    int m_nValue;
    static char m_chValue;
};

Now, in the C++ world this object needs to be created to exist.  This is what they teach you because it is the way you are expoected to think.  As lonbg as your program has no bugs it is a perfectly valid way of thinking.

In the C world (which is the real world the C++ world just lives in), it is really nothing more than than a structure and some functions.  Here's the same thing in C:

struct _MyObject
{
    void* DoMore;
    int m_nValue;
};

char _MyObject_m_chValue;

void _MyObject* _MyObject_MyObject()
{
    _MyObject* return = malloc(sizeof(_MyObject);

    // Call base class constructor, if any

    return->DoMore = (void*)_MyObject_DoMore;  //Assign the virtual function

    // Do the actual creator stuff
}

void _MyObject_Destructor(_MyObject* this)
{
    // Do destructor stuff

    free(this);
}

void _MyObject_DoSomething(_MyObject* this)
{
    // Do something
}

void _MyObject_DoMore(_MyObject* this)
{
    //Do more
}

void _MyObject_AndMoreStill()
{
    // statically do more
}


In fact, except perhaps for the name munging and some typecasting differences, this is almost exactly what the original C++ compilers did before passing the new code to a C compiler.

So note a few things.  First of all, the functions are all global, they exist even if the object was never created.  The only function you can't call on an uncreated object is the virtual function, because the pointer to the function isn't assigned until the creator is called.

On the other end of things, the static function doesn't take a pointer to teh object, so it can be called whether the object has been created or not, but it also means that it can only access global variables, like the munged m_chValue (which was static).

The world inbetween is the DoMore function.  It is a global function, so you can call it at any time.  If you were to write

   m_pMyObject->DoMore();

the C++ would turn into C code that looked like

   _MyObject_DoMore(m_pMyObject);

As you can see, the call itself is perfectly valid, even if the object being passed to the function may not be.

This is what is really going on under the thin veneer of C++, and when something goes wrong it really helps to know this stuff.  It also makes it clear why certain wierdness happens with constructors and virtual functions.

Welcome to the next level.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Right then, I have read and re-read what you have said and understand what the C++ compiler actually does to all of the code with that example.  So thanks for that :)  However...

Further annoyances:

1) >> The static pointer in your objects can point to the app object, and once it does everyone has it, but you need to assign it before any calls are made.

Assigning it before it is being used ?  So are you saying that it is the pointer being not initialised that is causing the trouble ?  I figured I was doing this though !  Having said that, I stepped into my code and set MSVC++ to run to the line

CGui4UnexApp* CContainerDlg::m_pApp = (CGui4UnexApp*)AfxGetApp();

and it never got there !  Stepping to the point at which m_pApp was used, MSVC++ told me that m_pApp could not be evaluated; so it really was not initialised !  *So I definitely see that the pointer is rubbish, hence anything that is a member of it is too*.

*But I still cannot see why it is not being initialised with the call above ! ARGHHHHHHH !*

>> Well, all the member functions of every object always exist

Following on from the comment above, I decided to test it out (to prove to myself in a simpler way ;) ).

class X
{
public:
X() { cout << "X() {}" << endl;}
void T() {}
};

X* x = NULL;
x->T();

Now the fact that I did not get "X() {}" displayed and a program crash proves what you were saying; the X object was not constructed but I could still attempt to call T() even though the pointer x is invalid.  Now this is exactly what is happening with my app, yeah ?

So, I now *do* get why StrExists can be called, and hence why m_strNewTabURLs would work if it was static (again thanks to your example), but the *remaining issue now is why this call to initialise m_pApp is not being carried out*.  I could put this in the constructor, but then it would be re-initialised with every new CContainerDlg being created.

???

2)

>>  The only function you can't call on an uncreated object is the virtual function, because the pointer to the function isn't assigned until the creator is called

Throughly understand that as a result of your example.  Later you say

>> The world inbetween is the DoMore function.  It is a global function, so you can call it at any time

but DoMore *is* the virtual function, that you have said cannot be called without the object being created, and above it looks like you are saying it can be called anytime ?

?

(More points for all this of course)

>> Welcome to the next level.

Thanks !
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
1) Now I see why the global variable is not being assigned.

The default assignment of global variables happens before the main routine in the program executes.  Since the AfxGetApp() function just returns a global variable itself, the variable is being assigned to an un-initialized variable which won't be assigned until the CWinApp is created.

Since you have no control over the order of default assignement of global variables it is generally dangerous to use anything but constants as the default value.

You really don't gain any benefit from using a static assigned to the variable anyway, since it is a global variable itself.  AfxGetApp is even an inline function (which calls yet another inline function, but that's another story).  If you really worry about the typecast, create you own static inline function that does the typecasting for you.

    static inline CGui4UnexApp* GetMyApp() { return (CGui4UnexApp*)AfxGetApp(); }

That shouldn't take any more time than using a static variable.

2) Oops, sorry.  In that paragraph replace "DoMore" with "DoSomething".  That's the problem with using an abstract example, it's hard to remember which is which.  I should have called the functions DoNormal DoVirtual and SoStatic to keep them straight.

Sorry about that.

Believe me, knowing how this all works under the hood is incredibly useful, especially when someone comes to you with some goofy problem that uses an obscure behavior.  Like what happens when you call a virtual function in a constructor?

Looking at the example above you can see that the base constructor is called, virtual functions are set (since the base constructor would override the virtual function setting if the order was reversed), and then the constructor is executed.  It suddenly becomes ovious that any virtual function calls in the constructor would automatically call the object's function, even if it was just a base of the object being constructed.

0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
>> the variable is being assigned to an un-initialized variable which won't be assigned until the CWinApp is created.

Yeah that is fine, but the thing is I don't create a CContainerDlg instance until after the CWinApp has been created anyway, since that is the class that is used in the view.  

Or do I............hence is what is happening like this:

BOOL CGui4UnexApp::InitInstance()
{
// Do all stuff with CMultiDocTemplate etc...
// parse command lines
// Create new CMainFrame instance

<--------I am creating a CContainerDlg as part of my view somewhere in here, hence pointer obtained via AfxGetApp() is invalid

return TRUE;
} <-- at this point the pointer to my CWinApp, obtained via AfxGetApp() *would* be valid since the CWinApp object is now constructed

?

Cheers again, and no worries about the mistake :)
0
 
LVL 11

Accepted Solution

by:
KurtVon earned 100 total points
Comment Utility
Not exactly.  What I mean is that if you assigned the variable when the object was created then it would work.  A static variable is really a global variable, so the default value is assigned when global variables are created before the program execution even begins (well, before the function "main" is called anyway).

class X
{
    static int p;

    int GetP() { return p; }
}

int X::p = 3;

Is translated into

struct _X
{
};

int _X_p = 3;

int _X_GetP() { return _X_p; }

Which is why you have to define storage for a static variable.

So the call to AfxGetApp() resolves since it was inlined, but it resolves to an uninitialized global variable.  Default values are calculated at compile time.

Another interesting odd way to act:

class A
{
    A() {}
    virtual int GetVal(int a = 1) { return a; }
};

class B : public A
{
    B() {}
    virtual int GetVal(int b = 2) { return b; }
}

main()
{
    A *x = new B();
    int r = x->GetVal();
}

So what is r?  It's 1.  Why?  Because x is an object of type A, so the preprocessor assumes it is calling the A function and since the parameter is missing, pulls it from A's GetValue default parameter.  It doesn't matter that it is really calling B's GetValue, it calls B's GetValue with the parameter 1.  Why?  The value is resolved at compile time, not at run time.

This is actually a popular job-interview question.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Right.

1)
>>  What I mean is that if you assigned the variable when the object was created then it would work

I take it this means assign the static variable in CContainerDlg when the CGui4Unex app was created ?

>> ...so the default value is assigned when global variables are created before the program execution even begins

Ok, so the default value for m_pApp will be some sort of rubbish value.  But since I never attempt to use the pointer before a CContainerDlg object has been created, I figured m_pApp would become the value returned by AfxGetApp()...  

Now when I first create a CContainerDlg object, this is not before CGui4Unex::InitInstance() has finished I presume (as it is part of the view).  So the problem is that the value returned by AfxGetApp() is rubbish hence rubbish is assigned to m_pApp ?

I think if you could post a trace of the (concerned) calls that are made and the order that they happen in (hence the stage at which m_pApp gets initialised) that would really help.

2)
>> ...Which is why you have to define storage for a static variable.

Again, I think I missed something there; from the example I could not see why this must be.  Can you clarify ?


And as regards that last example; it is quite scary when I realise exactly how much I don't know yet !  I think that requires more elaboration, but at a later point when the original question has been sorted !

---
I really do appologise for my apparent lack of ability to get my head round this one; it is just that I have never had a class object with a static variable that is not being initialised to what I figure it should be !  

But then again from past questions I have asked that you have answered, I am sure you are well aquainted with the fact that I like to bleed a question dry until I am comfortable with the answer; if I were not there would be no point closing it - I would only do myself an injustice :)

(Points have gone up again too).
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
Hmm, I guess the best way to explain how the program starts with respect to global/static variables:

1) The program is loaded into memory.
2) The program allocates heap space for the global variables and static members of all objects and functions.
3) Each of the global variable and static members is assigned based on the compile time values (which may include assigning to each other).
4) The proper main routine is called (main for DOS, WinMain for windows -- there is a main function in windows, but it just prints that this is a windows program and halts.)

The problem is that AfxGetApp() is a global variable (once all the "inline" calls are resolved).  If it were a normal function, you wouldn't even be able to compile.  However, instead in step 3 the M-PMyApp variable is assigned to the value stored in another global (um, I think it is afxModuleState->m_pCurrentWinApp) which, since it get's assigned to an object when WinMain is called, means it isn't a valid pointer yet.

And as for specifying storage:

class A
{
    static int value; // the static variable "value" is declared here, but this is
                           // just a reference so functions including the .h file can access
                           // the variable.
};

int A::value = 3; // This is the actual "storage" space for the variable.  When
                         // the compiler sees this, it makes a record that it needs to
                         // create space in step 2 above.  That's why it has to be declared
                         // in the cpp file.  If it was in the h file, every cpp file that included
                         // it would try to create another copy of the variable.

0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Riiiiiigggggghhhttt...

I think the penny has almost dropped.  My confusion lies in when static variables are created.  From what you were saying earlier about them being global, and hence being initialised at compile time...

In your example above, A::value will be 3, *regardless of whether or not I create an A object* right ?

Hence of course m_pApp will be rubbish, since at compile time the app is not yet created for AfxGetApp() to retrieve a valid pointer, right ?
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
Bingo.
0
 
LVL 11

Expert Comment

by:KurtVon
Comment Utility
In fact, I'd go so far as to say that the only association between a static variable and the object or function it is a member of is the name.  Otherwise a static lives it's entire life unassociated with the thing it was declared in.

This applies just as much to

int Func()
{
    static int a = 1;
    return a++;
}

which preprocesses into

int _Func_a = 1;

int Func()
{
    return _Func_a++;
}

Otherwise the variable would be reset every time the function was called.
0
 
LVL 19

Author Comment

by:mrwad99
Comment Utility
Once again unquestionable patience and astounding answers.  Thanks a million KurtVon :)

I will have a think about the 'popular interview question' and if I cannot figure it out here, it is ok if I post here and award more points for any explanation ?

Thanks again !
0

Featured Post

What Security Threats Are You Missing?

Enhance your security with threat intelligence from the web. Get trending threat insights on hackers, exploits, and suspicious IP addresses delivered to your inbox with our free Cyber Daily.

Join & Write a Comment

Suggested Solutions

Title # Comments Views Activity
Windows Drag & Drop Location 2 85
EvenOdd challenge 10 81
mapAB Challlenge 35 84
wordcount challenge 11 71
Introduction: Database storage, where is the exe actually on the disc? Playing a game selected randomly (how to generate random numbers).  Error trapping with try..catch to help the code run even if something goes wrong. Continuing from the seve…
Exception Handling is in the core of any application that is able to dignify its name. In this article, I'll guide you through the process of writing a DRY (Don't Repeat Yourself) Exception Handling mechanism, using Aspect Oriented Programming.
This video will show you how to get GIT to work in Eclipse.   It will walk you through how to install the EGit plugin in eclipse and how to checkout an existing repository.
In this tutorial you'll learn about bandwidth monitoring with flows and packet sniffing with our network monitoring solution PRTG Network Monitor (https://www.paessler.com/prtg). If you're interested in additional methods for monitoring bandwidt…

762 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

9 Experts available now in Live!

Get 1:1 Help Now