+1  A: 

Two questions here:

  • What's the problem? I don't mean what seems fishy in the debugger, but what actually goes wrong at the top level? What you're seeing in the debugger could be the result of optimizations, rather than being an actual error.
  • Do you have some minimal code that reproduces the error? The above doesn't really tell me much. If I'd run into exactly the same problem previously, it might be useful, but I haven't. So if I wanted to reproduce the problem, what should I do?
jalf
Thanks for your reply! 1st symptom was the debugger reporting a buffer overrun as it returned from a function that uses this class. Further inspection showed the char[] arrays weren't being set as expected, which ultimately led to the 'off by 4' observation. All optimizations are disabled.
Adam Liss
+1  A: 

My first guess would be compiling with unintended optimizations turned on.

My second guess would be that something strange is going on with unicode support (e.g. char vs wchar_t).

Dashogun
Thanks for your thoughts. The problem was definitely ATL_NO_VTABLE, probably related to COM. Writing a string of chars to the array does in fact write the correct chars to consecutive elements; it's only the offset that was wrong. I've quadruple-checked all optimizations -- they're off.
Adam Liss
A: 

Its always better to null terminate the string before you start using . By doing so even while debugging you will have a cleaner view of the array. More ever you have declared m_szString in public . Its better you declare the array in the private area and return it using a member function

sameer karjatkar
True, but all irrelevant with respect to the question. Regardless of how any of the elements is declared or initialized, the indexing should always be correct. This is not a question about best practices; it's a question about the way the compiler computes addresses.
Adam Liss
A: 

The Answer

The compiler was, in fact, calculating an incorrect address for some of the class members. The culprit turned out to be a #pragma pack 1 directive hidden in an obscure header file that was #included in some of the source files.

How I found it

50% persistence, 50% luck, and 10% math skills. :-)

I came across a very simple class with few methods and members, most of which were defined in the header file, including:

typedef int BOOL;  // Legacy, makes my skin crawl; don't ask.
BOOL isConnected() { return m_bConnected; };
BOOL m_bConnected;

...and the function returned false when I knew  m_bConnected was true:

  • The watch window showed the correct value. Programmatic changes to m_bConnected were reflected in the window.
  • A watch on &m_bConnected showed that it began 8 bytes into the class.
  • The raw memory window showed the correct value. Programmatic changes to m_bConnected were reflected there as well.
  • The 4 bytes before m_bConnected were all 0, which I interpreted to be padding.
  • Stepping the debugger through the code clearly showed a return value of false!

So I checked the disassembly window and found this (comments are mine):

mov    eax,dword ptr [this]        ; address of the class instance
mov    eax,dword ptr [eax+4]       ; offset to m_bConnected

In other words, the compiler calculated the offset as 4, when it should have been 8.

Aha!

Interestingly, when I removed the definition of isConnected() from the header and placed it into my source file, the address was calculated correctly! That convinced me that it was, indeed, a packing problem, and a search of the code base for #pragma pack quickly identified the culprit, an ancient header file that (legitimately) required alignment on 1-byte boundaries but never reset the packing to the default.

The Fix

The solution was as easy as enclosing the offending header like this:

#pragma pack(push)
// original code here
#pragma pack(pop)

Thanks for your interest. And Sara, if you're reading, I'm about to dance on my desk!

Adam Liss