AGH AAGH EVIL CODE
May. 1st, 2005 02:03 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Brownie points to anyone who can spot the dire problem:
This otherwise innocous snippet took up my morning bluescreening Win9x!! If I didn't have access to a box with a real OS I don't know what I'd be doing right now... But fortunately, GDB under WinXP correctly traps the problem even though it runs "fine" from the prompt. Code is happy now.
Is it surprising that the dumbest goofs are the most devastating? Eesh. x..X
typedef AsmParserRec* AsmParser; int NewAsmParser(AsmParser* Parser, char* Input, int Length) { // Note: New() is like Pascal New, e.g. it modifies the supplied pointer. // Hence pass-by-reference. if(New(Parser, sizeof(AsmParser))) { (*Parser)->Input = Input; (*Parser)->Length = Length; (*Parser)->Line = 1; (*Parser)->Column = 1; ... }Hey, I know. Almost nobody ever comments in here, so reply with the right guess, and I'll get you an LJ icon or something? 8)
This otherwise innocous snippet took up my morning bluescreening Win9x!! If I didn't have access to a box with a real OS I don't know what I'd be doing right now... But fortunately, GDB under WinXP correctly traps the problem even though it runs "fine" from the prompt. Code is happy now.
Is it surprising that the dumbest goofs are the most devastating? Eesh. x..X
no subject
Date: 2005-05-01 07:04 pm (UTC)no subject
Date: 2005-05-01 07:33 pm (UTC)Nice try, but no. :)
no subject
Date: 2005-05-01 07:35 pm (UTC)no subject
Date: 2005-05-01 07:19 pm (UTC)That wasn't too clear, let me try again.
Say someone calls NewAsmParser. They're going to pass a pointer variable that they expect to then be filled with a pointer to a new AsmParser, right? But C++ passes by value. You're essentially passing in whatever garbage value was in the pointer already. The Parser variable in the function is actually a copy of whatever that garbage variable was, and it's local to the function. Fill it with a pointer in New, return, expect to use the pointer, and bam. Probably a NULL pointer exception.
You want to pass that pointer by reference, ie
int NewAsmParser(AsmParser* &Parser, char* Input, int Length) {
I think that's how you pass a pointer variable by reference. You could do this the C way:
int NewAsmParser(AsmParser **Parser, char* Input, int Length) {
// Note: New() is like Pascal New, e.g. it modifies the supplied pointer.
// Hence pass-by-reference.
if(New(*Parser, sizeof(AsmParser))) {
(**Parser).Input = Input;
(**Parser).Length = Length;
(**Parser).Line = 1;
(**Parser).Column = 1;
...
}
Call it by passing it a pointer to a pointer variable:
AsmParser *somePointer;
NewAsmParser(&somePointer,...);
Whoa. I just spotted the even bigger bug. (*Parser)->Input, you're dereferencing the pointer twice. That's probably the bug you found. Do I get a cookie for finding your OTHER bug?
no subject
Date: 2005-05-01 07:23 pm (UTC)no subject
Date: 2005-05-01 07:25 pm (UTC)New(*Parser,sizeof(AsmParserRec))
, I think. Grr. Typedefs for pointer "types" really confuse matters.no subject
Date: 2005-05-01 08:09 pm (UTC)Woot! You're mostly right, it needed to be AsmParserRec. :) Future methods were scribbling on the parser and smashing the heap, since pointers are only four bytes. Drove me nuts, I wasn't looking anywhere inside a silly little constructor!
New(Parser, ...) is still correct because New takes (void**). A normal top-level invocation goes New*(&object, size). New*() and Dispose*() are the only ones like this, and it's only because C lacks automatic pass-by-ref. AsmNextChar, et al, simply takes AsmParser.
Thing is, I would have caught the mistype if NewAsmParser had been written after the typedef. The intention was to emphasize them as opaque types. I'm not sure how that will pan out, but I admit it's heavily influenced by Pascal. (I really, really didn't miss C. My Pascal code rarely burns this badly! I'm only writing it to be portable...)
I suppose it's fortunate that this problem only showed up in a test case...
no subject
Date: 2005-05-01 08:33 pm (UTC)typedef
ing a pointer "type" will lead to confusion and bugs more than it will help prevent them. After all, typedef is just syntactic sugar designed to make programming easier. In this case, it required extra thought that you forgot about.Anyway, if you want opaque types and passing by reference, use C++ ;) Not that I'd normally be a strong proponent of C++, but it seems like the right tool for the job here.
no subject
Date: 2005-05-01 10:00 pm (UTC)I think typedef is going, though because it just looks nice, and doesn't really gain anything after all.