C++, an "impossible" behavior - c++

If you've been programming for a while then you probably noticed something completely impossible occurs every now and then for which you are convinced there is no possible explanation ("IT'S A COMPILER BUG!!"). After you find out what it was caused by though you are like "oooohhh".
Well, it just happened to me :(
Here AuthDb is NOT NULL but a valid pointer:
SingleResult sr(AuthDb, format("SELECT Id, Access, Flags, SessionKey, RealmSplitPreference FROM accounts WHERE Name = '%s'") % Escaped(account_name));
Here it mysteriously becomes NULL:
struct SingleResult : public BaseResult
{
SingleResult(Database *db, const boost::format& query) { _ExecuteQuery(db, query.str()); }
}
Notice that it's the immediate next call. It can be explained much better with two screenshots:
http://img187.imageshack.us/img187/5757/ss1zm.png
http://img513.imageshack.us/img513/5610/ss2b.png
EDIT: AuthDb is a global variable. It itself keeps pointing to the right thing; but the copy of the ptr Database *db points at NULL.
ASM code (unfortunately I don't even know how to read it :/)
Of the first screenshot
01214E06 mov eax,dword ptr [ebp-328h]
01214E0C push eax
01214E0D push offset string "SELECT Id, Access, Flags, Sessio"... (13C6278h)
01214E12 lea ecx,[ebp-150h]
01214E18 call boost::basic_format<char,std::char_traits<char>,std::allocator<char> >::basic_format<char,std::char_traits<char>,std::allocator<char> > (11A3260h)
01214E1D mov dword ptr [ebp-32Ch],eax
01214E23 mov ecx,dword ptr [ebp-32Ch]
01214E29 mov dword ptr [ebp-330h],ecx
01214E2F mov byte ptr [ebp-4],2
01214E33 mov ecx,dword ptr [ebp-330h]
01214E39 call boost::basic_format<char,std::char_traits<char>,std::allocator<char> >::operator%<Snow::Escaped> (11A3E18h)
01214E3E push eax
01214E3F mov edx,dword ptr [__tls_index (144EC40h)]
01214E45 mov eax,dword ptr fs:[0000002Ch]
01214E4B mov ecx,dword ptr [eax+edx*4]
01214E4E mov edx,dword ptr [ecx+12A3Ch]
01214E54 push edx
01214E55 lea ecx,[sr]
01214E58 call Snow::SingleResult::SingleResult (11A27D4h)
01214E5D mov byte ptr [ebp-4],4 // VS GREEN ARROW IS HERE
01214E61 lea ecx,[ebp-150h]
01214E67 call boost::basic_format<char,std::char_traits<char>,std::allocator<char> >::~basic_format<char,std::char_traits<char>,std::allocator<char> > (11A1DBBh)
01214E6C mov byte ptr [ebp-4],5
01214E70 lea ecx,[ebp-170h]
01214E76 call Snow::Escaped::~Escaped (11A42D2h)
const bool account_found = !sr.Error();
01214E7B lea ecx,[sr]
01214E7E call Snow::BaseResult::Error (11A2964h)
01214E83 movzx eax,al
01214E86 test eax,eax
01214E88 sete cl
01214E8B mov byte ptr [account_found],cl
if (!account_found) {
01214E8E movzx edx,byte ptr [account_found]
01214E92 test edx,edx
01214E94 jne AuthSession+1C0h (1214F10h)
client.Kill(format("%s: Attempted to login with non existant account `%s'") % client % account_name, true);
Second screenshot
011A8E7D mov dword ptr [ebp-10h],ecx
011A8E80 mov ecx,dword ptr [this]
011A8E83 call Snow::BaseResult::BaseResult (11A31D9h)
011A8E88 mov dword ptr [ebp-4],0
011A8E8F lea eax,[ebp-30h]
011A8E92 push eax
011A8E93 mov ecx,dword ptr [query]
011A8E96 call boost::basic_format<char,std::char_traits<char>,std::allocator<char> >::str (11A1E01h)
011A8E9B mov dword ptr [ebp-34h],eax
011A8E9E mov ecx,dword ptr [ebp-34h]
011A8EA1 mov dword ptr [ebp-38h],ecx
011A8EA4 mov byte ptr [ebp-4],1
011A8EA8 mov edx,dword ptr [ebp-38h]
011A8EAB push edx
011A8EAC mov eax,dword ptr [db]
011A8EAF push eax
011A8EB0 mov ecx,dword ptr [this]
011A8EB3 call Snow::SingleResult::_ExecuteQuery (124F380h)
011A8EB8 mov byte ptr [ebp-4],0 // VS GREEN ARROW HERE
011A8EBC lea ecx,[ebp-30h]
011A8EBF call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::~basic_string<char,std::char_traits<char>,std::allocator<char> > (11A2C02h)
011A8EC4 mov dword ptr [ebp-4],0FFFFFFFFh
011A8ECB mov eax,dword ptr [this]
011A8ECE mov ecx,dword ptr [ebp-0Ch]
011A8ED1 mov dword ptr fs:[0],ecx
011A8ED8 pop edi
011A8ED9 add esp,38h
011A8EDC cmp ebp,esp
011A8EDE call _RTC_CheckEsp (12B4450h)
011A8EE3 mov esp,ebp
011A8EE5 pop ebp
011A8EE6 ret 8
UPDATE
Following the suggestion of peterchen, I added ASSERT(AuthDb); here:
ASSERT(AuthDb);
SingleResult sr(AuthDb, format("SELECT Id, Access, Flags, SessionKey, RealmSplitPreference FROM accounts WHERE Name = '%s'") % Escaped(account_name));
And it failed O.o And yet the debugger keeps insisting that it's not NULL.. It's not shadowed by a local
UPDATE2*
cout << AuthDb; is 0 in there even if the debugger says it's not
FOUND THE PROBLEM
Database *AuthDb = NULL, *GameDb = NULL; in a .cpp
extern thread Database *AuthDb, *GameDb; in a .h
The variable was marked thread (TLS - Thread local storage) in the header, but not TLS in the definition...
Countless hours wasted on this super stupid mistake, no warnings or hints or anything from the compiler that I feel like killing right now. :( Oh well, like I said for each impossible behavior there is a solution that once known seems obvious :)
Thanks to everyone who helped, I was really desperate!

Is AuthDB a thread-local variable?
Maybe the debugger isn't handling it correctly. What if you ASSERT(AuthDB) before the constructor is called?
UPDATE: If it is thread-local, it simply hasn't been initialized in this thread.

One possibility is that in the second screenshot, you have the debugger stopped at the very beginning of the function, before the stack has been manipulated, and so the variable locations aren't correct. You might also be after the end of the function, where the stack has already been torn down. I've seen that sort of thing before.
Expand that function to be a multiline function, so that it looks like this:
struct SingleResult : public BaseResult
{
SingleResult(Database *db, const boost::format& query)
{
_ExecuteQuery(db, query.str());
}
}
... and see if it still shows db as null when you have the debugger stopped on the _ExecuteQuery line.

Do you have a local AuthDB that is null and hides your global one?
(I'd expect the debugger in this case to correctly show you the local one... but with VS quality decay, I'd not rely on that)
I'd change the code to:
_ASSERTE(AuthDb);
SingleResult sr(AuthDb, format(...));
....
struct SingleResult : public BaseResult
{
SingleResult(Database *db, const boost::format& query)
{
_ASSERTE(db);
_ExecuteQuery(db, query.str());
}
}
And follow the disassembly in the debugger.

Well I'm not sure what classes/functions you're using here, but from a quick glance, shouldn't it be:
SingleResult sr(AuthDb, format("SELECT Id, Access, Flags, SessionKey, RealmSplitPreference FROM accounts WHERE Name = '%s'", Escaped(account_name)));
instead of:
SingleResult sr(AuthDb, format("SELECT Id, Access, Flags, SessionKey, RealmSplitPreference FROM accounts WHERE Name = '%s'") % Escaped(account_name));
It seems to me you're putting a modulus of the of Escaped(account_name) rather than passing that as an argument to format. However, I could just be confused.
Mike

I have no guess what is going on, but if I were debugging this I would be looking at the assembly language to see what is happening. You may need to get a better understanding of your platforms calling convention (i.e. how are arguments passed, on the stack, in registers, etc.) to troubleshoot this.

There is likely a bug somewhere else in your program. I suggest you find the problem by looking elsewhere in your code.

Another possibility--you might be looking at a memory overwrite due to a wild pointer somewhere.
Assuming your debugger supports it when you hit the first line set a memory-write breakpoint on AuthDb.

Related

why constructors "return" this pointer?

I noticed that the constructor will move this to eax before returning. This is a return value or something else?
class CTest {
int val_;
public:
CTest() {
0093F700 push ebp
0093F701 mov ebp,esp
0093F703 sub esp,0CCh
0093F709 push ebx
0093F70A push esi
0093F70B push edi
0093F70C push ecx
0093F70D lea edi,[ebp-0CCh]
0093F713 mov ecx,33h
0093F718 mov eax,0CCCCCCCCh
0093F71D rep stos dword ptr es:[edi]
0093F71F pop ecx
0093F720 mov dword ptr [this],ecx
val_ = 1;
0093F723 mov eax,dword ptr [this]
0093F726 mov dword ptr [eax],1
}
0093F72C mov eax,dword ptr [this]
0093F72F pop edi
0093F730 pop esi
0093F731 pop ebx
0093F732 mov esp,ebp
0093F734 pop ebp
0093F735 ret
VS2012 debug mode
I found that new will use its "return value". Seems like if(operator new() == 0) return 0; else return constructor();
class CTest {
int val_;
public:
CTest() {
val_ = 1;
__asm {
mov eax, 0x12345678
pop edi
pop esi
pop ebx
mov esp,ebp
pop ebp
ret
}
}
};
int main() {
CTest *test = new CTest; // test == 0x12345678
return 0;
}
Your second question disagrees with your first. How can new use if ( operator new() == 0 ) return 0; else return constructor(); if constructor() is producing the condition result?
Anyway…
What the compiler does with registers is the compiler's business. Registers tend to hold whatever information is immediately useful, and if the compiler is written with the belief that every time the constructor is used, the object is used immediately afterwards, it may reasonably choose to put this in a register.
An ABI may require constructors to do this, but I doubt any do. Anyway, such protocols only apply to things exported from libraries, not strictly within programs.
Any new expression does check the result of operator new against 0 before proceeding to initialize an object. operator new may signal failure by returning nullptr (or NULL, etc.).
This can actually be a problem with placement new expressions, because it represents unavoidable runtime overhead as the given pointer is generally already known to be non-null.
This can be a feature by design, in C++ and other languages, returning a reference to a given instance allows a more "idiomatic" use of the features offered by the object itself, in short it's the Named parameter Idiom .
But this is just 1 option, it can be useful sometimes, especially if you are able to design your library in a way that it only "takes actions" without having the need to pass a significant amount of parameters, so the chain of method calls stays readable.

Pointers and release build in Visual Studio

I have a weird problem when I create a release build with Visual Studio 2008. I was wondering if one of you could help me understand what is going on.
Description:
I have a class member function which returns a pointer to a structure stored in the class:
const MyStruct * Myclass::getPointer() {
return mystruct_variable; // this is properly initialyzed
}
One another point worth pointing out is that this class/method is in a dll, and I export it to use in a separate executable project. When I make a release build and try to use the above mentioned method, the run crashes depending on if the getPointer() method is inlined (i.e. placed in the header file part of the class) or not (placed in the cpp file).
The usage is:
const MyStruct * cf = myclassObj.getPointer();
int n = cf->size_t;
std::cout<<n<<std::endl;
When MyClass::getPointer() is inlined in the header the assembly looks like:
const MyStruct * cf = myclassObj.getPointer();
012514A8 mov esi,dword ptr [ebx+794h]
int n =cf->size_t;
012514AE mov eax,dword ptr [esi+20h]
std::cout<<n<<std::endl;
012514B1 mov ecx,dword ptr [__imp_std::endl (1252038h)]
012514B7 push ecx
012514B8 mov ecx,dword ptr [__imp_std::cout (125203Ch)]
012514BE push eax
012514BF call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1252048h)]
012514C5 mov ecx,eax
012514C7 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (1252044h)]
The same code when the class method for getPointer() is not inlined, and placed in the corresponding cpp file gives:
const MyStruct * cf = myclassObj.getPointer();
00DA14A8 mov ecx,ebx
00DA14AA call dword ptr [__imp_MyClass::getPointer(0DA2104h)]
int n =cf->size_t;
std::cout<<n<<std::endl;
00DA14B0 mov ecx,dword ptr [__imp_std::endl (0DA2038h)]
00DA14B6 mov esi,eax
00DA14B8 mov eax,dword ptr [esi+20h]
00DA14BB push ecx
00DA14BC mov ecx,dword ptr [__imp_std::cout (0DA203Ch)]
00DA14C2 push eax
00DA14C3 call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0DA2048h)]
00DA14C9 mov ecx,eax
00DA14CB call dword ptr [__imp_std::basic_ostream<char,std::char_traits<char> >::operator<< (0DA2044h)]
Any thoughts why the two cases have different assembly set? Am I doing something wrong?
Thanks!
If you link against a C++ DLL you must make sure all the compiler flags are exactly the same. Otherwise the size of structures, virtual tables etc. might be different and the code fails with an invalid access to memory. Inlining of course overcomes this as the code is inside the exe file, not in the dll, and thus compiled with the correct flags.
Simply put - for a release build use release DLL, for a debug build use debug DLL.

Is this a microsoft VC++ 2010 compiler bug of "new auto(enum_type)"

Enviroment: Microsoft Visual Studio 2010 with SP1 Preminum(10.0.40219.1 SP1Rel), Windows XP SP3
VC10 compiler support auto keyword, but the deduced type related information seems not always correct for enumeration.
example:
#include <type_traits>
enum fruit_t
{
apple = 100,
banana = 200,
};
int main()
{
const auto pa = new auto(banana);
const auto pb = new fruit_t(banana);
static_assert(std::is_same<decltype(pa), decltype(pb)>::value, "not same!");
delete pb;
delete pa;
}
The code above should have no compiler-time error or runtime error. But what suprising me is that, it compiles ok without any error or warning but, does not run correctly. The debugger tells after exit the main function:
HEAP CORRUPTION DETECTED: after %hs block (#55) at 0x00034878. CRT detected that the application wrote to memory after end of heap buffer.
so I guess that the compiler may have bug in "auto" type deduction. Assembler window below shows that, the requested memeory size in the first "operator new" call is 1 byte, while the second "operator new" 4 bytes. It suggests that the compiler has made a big mistake on the size of deduced type.
Did you think this is a compiler bug? And is there any bug fixes from Microsoft?
int main()
{
004113C0 push ebp
004113C1 mov ebp,esp
004113C3 sub esp,10Ch
004113C9 push ebx
004113CA push esi
004113CB push edi
004113CC lea edi,[ebp-10Ch]
004113D2 mov ecx,43h
004113D7 mov eax,0CCCCCCCCh
004113DC rep stos dword ptr es:[edi]
const auto pa = new auto(banana);
004113DE push 1
004113E0 call operator new (411181h)
004113E5 add esp,4
004113E8 mov dword ptr [ebp-104h],eax
004113EE cmp dword ptr [ebp-104h],0
004113F5 je main+51h (411411h)
004113F7 mov eax,dword ptr [ebp-104h]
004113FD mov dword ptr [eax],0C8h
00411403 mov ecx,dword ptr [ebp-104h]
00411409 mov dword ptr [ebp-10Ch],ecx
0041140F jmp main+5Bh (41141Bh)
00411411 mov dword ptr [ebp-10Ch],0
0041141B mov edx,dword ptr [ebp-10Ch]
00411421 mov dword ptr [pa],edx
const auto pb = new fruit_t(banana);
00411424 push 4
00411426 call operator new (411181h)
0041142B add esp,4
0041142E mov dword ptr [ebp-0F8h],eax
00411434 cmp dword ptr [ebp-0F8h],0
0041143B je main+97h (411457h)
0041143D mov eax,dword ptr [ebp-0F8h]
00411443 mov dword ptr [eax],0C8h
00411449 mov ecx,dword ptr [ebp-0F8h]
0041144F mov dword ptr [ebp-10Ch],ecx
00411455 jmp main+0A1h (411461h)
00411457 mov dword ptr [ebp-10Ch],0
00411461 mov edx,dword ptr [ebp-10Ch]
00411467 mov dword ptr [pb],edx
static_assert(std::is_same<decltype(pa), decltype(pb)>::value, "not same!");
delete pb;
0041146A mov eax,dword ptr [pb]
0041146D mov dword ptr [ebp-0ECh],eax
00411473 mov ecx,dword ptr [ebp-0ECh]
00411479 push ecx
0041147A call operator delete (411087h)
0041147F add esp,4
delete pa;
00411482 mov eax,dword ptr [pa]
00411485 mov dword ptr [ebp-0E0h],eax
0041148B mov ecx,dword ptr [ebp-0E0h]
00411491 push ecx
00411492 call operator delete (411087h)
00411497 add esp,4
}
Yes, I think it's a VS2010 bug. Running the same as you (or at least very similar) with XP SP3 (32-bit) and VS2010 SP1, I get the exact same error. It looks to be specific to enums, as trying it with classes showed everything working properly. I also tried adding another fruit item to the enum, with a value of 100000 just to make sure it wasn't something silly like your enum having all values below 255. Same result.
I did a quick search at Microsoft Connect, and I do not see a bug report for this, so I recommend that you enter one. That is the best way to make sure Microsoft knows and possibly get it fixed.

How does returning values from a function work?

I recently had a serious bug, where I forgot to return a value in a function. The problem was that even though nothing was returned it worked fine under Linux/Windows and only crashed under Mac. I discovered the bug when I turned on all compiler warnings.
So here is a simple example:
#include <iostream>
class A{
public:
A(int p1, int p2, int p3): v1(p1), v2(p2), v3(p3)
{
}
int v1;
int v2;
int v3;
};
A* getA(){
A* p = new A(1,2,3);
// return p;
}
int main(){
A* a = getA();
std::cerr << "A: v1=" << a->v1 << " v2=" << a->v2 << " v3=" << a->v3 << std::endl;
return 0;
}
My question is how can this work under Linux/Windows without crashing? How is the returning of values done on lower level?
On Intel architecture, simple values (integers and pointers) are usually returned in eax register. This register (among others) is also used as temporary storage when moving values in memory and as operand during calculations. So whatever value left in that register is treated as the return value, and in your case it turned out to be exactly what you wanted to be returned.
Probably by luck, 'a' left in a register that happens to be used for returning single pointer results, something like that.
The calling/ conventions and function result returns are architecture-dependent, so it's not surprising that your code works on Windows/Linux but not on a Mac.
There are two major ways for a compiler to return a value:
Put a value in a register before returning, and
Have the caller pass a block of stack memory for the return value, and write the value into that block [more info]
The #1 is usually used with anything that fits into a register; #2 is for everything else (large structs, arrays, et cetera).
In your case, the compiler uses #1 both for the return of new and for the return of your function. On Linux and Windows, the compiler did not perform any value-distorting operations on the register with the returned value between writing it into the pointer variable and returning from your function; on Mac, it did. Hence the difference in the results that you see: in the first case, the left-over value in the return register happened to co-inside with the value that you wanted to return anyway.
First off, you need to slightly modify your example to get it to compile. The function must have at least an execution path that returns a value.
A* getA(){
if(false)
return NULL;
A* p = new A(1,2,3);
// return p;
}
Second, it's obviously undefined behavior, which means anything can happen, but I guess this answer won't satisfy you.
Third, in Windows it works in Debug mode, but if you compile under Release, it doesn't.
The following is compiled under Debug:
A* p = new A(1,2,3);
00021535 push 0Ch
00021537 call operator new (211FEh)
0002153C add esp,4
0002153F mov dword ptr [ebp-0E0h],eax
00021545 mov dword ptr [ebp-4],0
0002154C cmp dword ptr [ebp-0E0h],0
00021553 je getA+7Eh (2156Eh)
00021555 push 3
00021557 push 2
00021559 push 1
0002155B mov ecx,dword ptr [ebp-0E0h]
00021561 call A::A (21271h)
00021566 mov dword ptr [ebp-0F4h],eax
0002156C jmp getA+88h (21578h)
0002156E mov dword ptr [ebp-0F4h],0
00021578 mov eax,dword ptr [ebp-0F4h]
0002157E mov dword ptr [ebp-0ECh],eax
00021584 mov dword ptr [ebp-4],0FFFFFFFFh
0002158B mov ecx,dword ptr [ebp-0ECh]
00021591 mov dword ptr [ebp-14h],ecx
The second instruction, the call to operator new, moves into eax the pointer to the newly created instance.
A* a = getA();
0010484E call getA (1012ADh)
00104853 mov dword ptr [a],eax
The calling context expects eax to contain the returned value, but it does not, it contains the last pointer allocated by new, which is incidentally, p.
So that's why it works.
As Kerrek SB mentioned, your code has ventured into the realm of undefined behavior.
Basically, your code is going to compile down to assembly. In assembly, there's no concept of a function requiring a return type, there's just an expectation. I'm the most comfortable with MIPS, so I shall use MIPS to illustrate.
Assume you have the following code:
int add(x, y)
{
return x + y;
}
This is going to be translated to something like:
add:
add $v0, $a0, $a1 #add $a0 and $a1 and store it in $v0
jr $ra #jump back to where ever this code was jumped to from
To add 5 and 4, the code would be called something like:
addi $a0, $0, 5 # 5 is the first param
addi $a1, $0, 4 # 4 is the second param
jal add
# $v0 now contains 9
Note that unlike C, there's no explicit requirement that $v0 contain the return value, just an expectation. So, what happens if you don't actually push anything into $v0? Well, $v0 always has some value, so the value will be whatever it last was.
Note: This post makes some simplifications. Also, you're computer is likely not running MIPS... But hopefully the example holds, and if you learned assembly at a university, MIPS might be what you know anyway.
The way of returning of value from the function depends on architecture and the type of value. It could be done thru registers or thru stack.
Typically in the x86 architecture the value is returned in EAX register if it is an integral type: char, int or pointer.
When you don't specify the return value, that value is undefined. This is only your luck that your code sometimes worked correctly.
When popping values from the stack in IBM PC architecture there is no physical destruction of the old values ​​of data stored there. They just become unavailable through the operation of the stack, but still remain in the same memory cell.
Of course, the previous values ​​of these data will be destroyed during the subsequent pushing of new data on the stack.
So probably you are just lucky enough, and nothing is added to stack during your function's call and return surrounding code.
Regarding the following statement from n3242 draft C++ Standard, paragraph 6.6.3.2, your example yields undefined behavior:
Flowing off the end of a function is equivalent to a return with no
value; this results in undefined behavior in a value-returning
function.
The best way to see what actually happens is to check the assembly code generated by the given compiler on a given architecture. For the following code:
#pragma warning(default:4716)
int foo(int a, int b)
{
int c = a + b;
}
int main()
{
int n = foo(1, 2);
}
...VS2010 compiler (in Debug mode, on Intel 32-bit machine) generates the following assembly:
#pragma warning(default:4716)
int foo(int a, int b)
{
011C1490 push ebp
011C1491 mov ebp,esp
011C1493 sub esp,0CCh
011C1499 push ebx
011C149A push esi
011C149B push edi
011C149C lea edi,[ebp-0CCh]
011C14A2 mov ecx,33h
011C14A7 mov eax,0CCCCCCCCh
011C14AC rep stos dword ptr es:[edi]
int c = a + b;
011C14AE mov eax,dword ptr [a]
011C14B1 add eax,dword ptr [b]
011C14B4 mov dword ptr [c],eax
}
...
int main()
{
011C14D0 push ebp
011C14D1 mov ebp,esp
011C14D3 sub esp,0CCh
011C14D9 push ebx
011C14DA push esi
011C14DB push edi
011C14DC lea edi,[ebp-0CCh]
011C14E2 mov ecx,33h
011C14E7 mov eax,0CCCCCCCCh
011C14EC rep stos dword ptr es:[edi]
int n = foo(1, 2);
011C14EE push 2
011C14F0 push 1
011C14F2 call foo (11C1122h)
011C14F7 add esp,8
011C14FA mov dword ptr [n],eax
}
The result of addition operation in foo() is stored in eax register (accumulator) and its content is used as a return value of the function, moved to variable n.
eax is used to store a return value (pointer) in the following example as well:
#pragma warning(default:4716)
int* foo(int a)
{
int* p = new int(a);
}
int main()
{
int* pn = foo(1);
if(pn)
{
int n = *pn;
delete pn;
}
}
Assembly code:
#pragma warning(default:4716)
int* foo(int a)
{
000C1520 push ebp
000C1521 mov ebp,esp
000C1523 sub esp,0DCh
000C1529 push ebx
000C152A push esi
000C152B push edi
000C152C lea edi,[ebp-0DCh]
000C1532 mov ecx,37h
000C1537 mov eax,0CCCCCCCCh
000C153C rep stos dword ptr es:[edi]
int* p = new int(a);
000C153E push 4
000C1540 call operator new (0C1253h)
000C1545 add esp,4
000C1548 mov dword ptr [ebp-0D4h],eax
000C154E cmp dword ptr [ebp-0D4h],0
000C1555 je foo+50h (0C1570h)
000C1557 mov eax,dword ptr [ebp-0D4h]
000C155D mov ecx,dword ptr [a]
000C1560 mov dword ptr [eax],ecx
000C1562 mov edx,dword ptr [ebp-0D4h]
000C1568 mov dword ptr [ebp-0DCh],edx
000C156E jmp foo+5Ah (0C157Ah)
std::operator<<<std::char_traits<char> >:
000C1570 mov dword ptr [ebp-0DCh],0
000C157A mov eax,dword ptr [ebp-0DCh]
000C1580 mov dword ptr [p],eax
}
...
int main()
{
000C1610 push ebp
000C1611 mov ebp,esp
000C1613 sub esp,0E4h
000C1619 push ebx
000C161A push esi
000C161B push edi
000C161C lea edi,[ebp-0E4h]
000C1622 mov ecx,39h
000C1627 mov eax,0CCCCCCCCh
000C162C rep stos dword ptr es:[edi]
int* pn = foo(1);
000C162E push 1
000C1630 call foo (0C124Eh)
000C1635 add esp,4
000C1638 mov dword ptr [pn],eax
if(pn)
000C163B cmp dword ptr [pn],0
000C163F je main+51h (0C1661h)
{
int n = *pn;
000C1641 mov eax,dword ptr [pn]
000C1644 mov ecx,dword ptr [eax]
000C1646 mov dword ptr [n],ecx
delete pn;
000C1649 mov eax,dword ptr [pn]
000C164C mov dword ptr [ebp-0E0h],eax
000C1652 mov ecx,dword ptr [ebp-0E0h]
000C1658 push ecx
000C1659 call operator delete (0C1249h)
000C165E add esp,4
}
}
VS2010 compiler issues warning 4716 in both examples. By default this warning is promoted to an error.

C++ std::string Parameter is <Bad Ptr>

I'm using Visual Studio 2010.
I have a class with the following constructor:
CVideoAnnotation::CVideoAnnotation(std::string aPort, DWORD aBaudRate)
I create an instance of CVideoAnnotation as follows:
CVideoAnnotation cVideoAnnotation("COM3", CBR_9600);
'CBR_9600' is a macro that resolves to 9600.
Down in the constructor, aBaudRate is 9600 as expected. However, aPort does not get passed properly. When I hover the cursor over it, IntelliSense gives a value of <Bad Ptr>.
Does anybody have any thoughts on why the string does not pass properly?
Thanks,
Dave
As an update to my original question, I'm adding the assembly code for the constructor call and the population of locals once inside the constructor.
CVideoAnnotation cVideoAnnotation("COM3", CBR_9600);
0041177D push 2580h
00411782 sub esp,20h
00411785 mov ecx,esp
00411787 mov dword ptr [ebp-174h],esp
0041178D push offset string "COM3" (4198C8h)
00411792 call std::basic_string<char,std::char_traits<char>,std::allocator<char> >::basic_string<char,std::char_traits<char>,std::allocator<char> > (41131Bh)
00411797 mov dword ptr [ebp-17Ch],eax
0041179D lea ecx,[ebp-11h]
004117A0 call dword ptr [__imp_CVideoAnnotation::CVideoAnnotation (41D4DCh)]
004117A6 mov dword ptr [ebp-180h],eax
004117AC mov dword ptr [ebp-4],0
CVideoAnnotation::CVideoAnnotation(std::string aPort, DWORD aBaudRate)
{
100137F0 push ebp
100137F1 mov ebp,esp
100137F3 push 0FFFFFFFFh
100137F5 push offset __ehhandler$??0CVideoAnnotation##QAE#V?$basic_string#DU?$char_traits#D#std##V?$allocator#D#2##std##K#Z (1001DC82h)
100137FA mov eax,dword ptr fs:[00000000h]
10013800 push eax
10013801 sub esp,164h
10013807 push ebx
10013808 push esi
10013809 push edi
1001380A push ecx
1001380B lea edi,[ebp-170h]
10013811 mov ecx,59h
10013816 mov eax,0CCCCCCCCh
1001381B rep stos dword ptr es:[edi]
1001381D pop ecx
1001381E mov eax,dword ptr [___security_cookie (10026090h)]
10013823 xor eax,ebp
10013825 mov dword ptr [ebp-10h],eax
10013828 push eax
10013829 lea eax,[ebp-0Ch]
1001382C mov dword ptr fs:[00000000h],eax
10013832 mov dword ptr [ebp-18h],ecx
10013835 mov dword ptr [ebp-84h],0
1001383F mov dword ptr [ebp-4],0
If you are implementing CVideoAnnotation in a separate DLL then you are having a well know issue of crossing DLL boundaries when using STL containers. To verify that this is the case create a new constructor taking a const char* instead of std::string and try..
Another thing, instead of std::string prefer to use const std::string&
Passing string to and from DLLs should work just fine. As noted above using STL containers in this way can be problematic but string is fine.
Do you have any evidence beyond Intellisense that the string is messed up? In Release builds Intelliesnse can be sketchy. Try adding cout << aPort << endl; into that constructor to make sure.
If it really is incorrect I would just debug from the caller into the constructor call - see where the string is pushed as a parameter, and see where on the stack it's picked up from for use inside the constructor. Something must be out of sync here, assuming both DLL and app use the same version of the C++ runtimes, and you should be able to tell why by inspecting the assembler code.
"COM3" is not a std::string it is a const char *, try creating a std::string to pass in.
I too was fighting what looked like bad byte alignment resulting in . Based on the suggestions here that crossing library boundaries can be problematic, I suspected (even though I am using a static library instead of DLL) that I might be dealing with the same sort of problem. I was instantiating a class in one library whose code was in another. When I moved the instantiation into the library where its code was the problem went away. I don't understand why this worked but at least I can now move forward.