cpow wrote:
WedNESday wrote:
The 4 Mirror[] entries are 240, 4, 14 and 0 when the debugger is run. At no point in the code are they set to anything other than 0, 1, 2 or 3 for obvious reasons.
Then you're memcpy()ing or otherwise walking beyond the end of some other nearby array in memory...
Given
this showing exactly that, I would say your theory is likely. Not picking on ya WedNESday! Just saying cpow's proposal sounds likely given some established history.
Too bad Windows doesn't have native valgrind; it can usually detect this kind of thing.
I'll expand on what cpow wrote here with something that's a little more technical but might make more sense to you:
cpow wrote:
If you ever see something in memory you know your code isn't writing there, you're usually wrong. Your code is writing it there but not the code you'd expect. For example, looking at all of your accesses to Mirror[] will not lead you to the culprit--it may actually lead you to some other problem if you discover that you're accessing Mirror[] with an out-of-bounds index, but that's not what's causing this problem. Looking at accesses to nearby arrays could help...but it's a long shot.
The reason it's a "long shot" and so on has to do with how OSes handle memory allocation. When a program starts there's actually a boatload of memory allocated all over the place (based on relevant executable header data and all the underlying segments defined in the executable itself -- yes I'm greatly and intentionally simplifying). A crash/exception (again keeping it simple) only happens when trying to access memory that is outside of the allocated space for your program -- anything that your program has allocated (either intentionally, or the kernel allocating for your program as a result of the program loading, etc.) is game for being accessed (read or written) without any complaint.
Memory allocation schemes in an OS do so in pages -- sequential amounts of memory that are not necessarily back-to-back linear. Phrased differently, let's say you have this line:
foo = malloc(65536); bar = malloc(65536); You might be inclined to think that the underlying VM might allocate both 64KBytes back-to-back so that you could technically access
foo[65536] and
foo[65537] and actually be accessing memory allocated pointed to by the
bar pointer. That assumption is wrong -- however, there may be memory (for other reasons) allocated for your program past that 64KByte allocation (referring to what
foo points to) that can be accessed without an exceptions generated. It could be for some variables you allocated on the heap or the stack (either or). It could be for some underlying API bits that your program uses that allocates memory itself. All this is memory your program technically owns, which means you're actually free to access it in whatever ways you wish -- intentional or unintentional. This is how, for lack of better term, "memory gets corrupted" when a program does something it shouldn't be doing.
The result of this is often the programmer resorting to stupid ideas that "seem" to work and make him/her think they've solved the problem. Things like "I turned off optimisation and the problem is gone", "the issue doesn't happen if I enable debug symbols", "if I run 5 instances of the program the 4th one works fine", or screwing around with stack size (I really hate it when I see people do this). All these result in the programmer suddenly believing the underlying OS or system "is unstable" when in fact it's their software that's broken.
I've
mentioned this before (in the same thread I linked above actually). My point in bringing that up is that depending on where the VM decided to allocate memory for the pointer called
Pixel, it could be next to memory used for other things. When I say "other things" I mean quite literally
anything relating to your program. Again: accessing something out-of-bounds that's still associated with your process memory space won't result in an exception.
I myself learned about this the hard way, maybe a year or so after I had started learning C. I had a piece of code (a simple
fread() call and nothing more) that worked when using -O2 (optimisation level 2, i.e. more optimisations), but broke (crashed) when using -O1 or -O0. I had no idea why; I started blaming the compiler because the situation seemed backwards (I'd heard of optimiser bugs but generating working code with -O2 but crashing code with -O1 or no optimisation?) and I was pompous. It wasn't until "other mysterious issues" happened a week later that I compared my code to that of an open-source program. It took me a while to understand what was going on, but it was quite simply the exact same thing you experienced above with
COLORREF *Pixel (but for me it was with
FILE *fp vs.
FILE fp and how -- or rather, what -- I was passing to
fread()).
Tracking down out-of-bounds accesses like this is somewhat difficult and often requires that you build your binaries with a kind of "guard" or "wrapper" that may wrap itself around every single system or library call in attempt to try and do the messy work for you. I mentioned valgrind above; it does some of this as a wrapper, but there are other solutions that involve compiler features or third-party libraries that inject themselves into things (i.e.
malloc() might now actually call a third-party library to do some tracking, then gets handed off to the real
malloc()). I'm sure there are tools for this under Windows I just don't have familiarity with Windows development to be able to recommend any. :(