Noobish C++ question

Started by
7 comments, last by Alberth 6 years, 11 months ago

Hey guys, what's up, I've got a C++ question that has been driving me stupid for the past couple days, and Google has been less than helpful. I've been working with SQLite and it works great with hard-coded strings, but I've been wanting to switch to prepared statements. I got down to "sqlite3_prepare_v2" and it compiles fine but at runtime throws a cryptic error basically telling me my C++ is newbie at best. "exception at <number>, blah blah bubblahbubublah at <number>". The numbers are in hex format, so I'm assuming they're memory addresses, and the "blah" part is always so general and vague that it's practically unGooglable. And the SQLite documentation, while descriptive enough to explain what the parameters and return values are, it's sadly lacking in examples. Fish around on Google and it's all about wrappers people wrote for Android or iOS. So I'm stuck outta luck, hoping that someone here might be able to share any info that could help me for decrypt this meaningless jibberish into something more human-friendly - or does it just come down to years of experience, memorizing memory addresses and knowing Windows internals better than Bill Gates? lol idk

But I'm guessing the real issue is just not knowing how to use my IDEs debugging tools, if it has any, and the process of debugging runtime errors/exceptions in general. I'm using the most recent CodeBlocks IDE with MinGW on WIndows 10 x64. Thanks in advance. :)

Advertisement
We cannot figure out an error message of "blah blah blah". Those words mean things.

Please give the exact error messages and exact lines of code.

If it works with hard-coded strings and fails with other strings, then likely the strings are not equal in some way.

In general, the simplest way to change something is to first reproduce a working situation with other strings. That way, if it fails, you can compare working versus non-working, which dramatically improves your chances in finding the problem.

That "blah blah" IS the human-friendly error message. The non-friendly message would be to just quit without saying anything. C++ wants to run as fast as possible, maintaining additional data for nice error messages is considered useless waste of time. (Which is true in the general case.) As a result, when it does crash, there is little information to show you.

You may want to look into compile options to convince the compiler to give more information. Also, likely there are ways to get a stack-dump at the point of the crash, so you can see what functions were being run when it crashed.

And as you already said, knowing your debug tools helps a lot here.

Maybe you did a thing to the stuff and it made a problem?

Since I have to guess, I'm going with invalid C string handling. Maybe null terminators, maybe basic pointer handling issues.

Stephen M. Webb
Professional Free Software Developer

But I'm guessing the real issue is just not knowing how to use my IDEs debugging tools, if it has any, and the process of debugging runtime errors/exceptions in general.


I don't know what CodeBlocks supports but my process is:

- Oh shit, it crashed.
- Restart with debugger attached, set to break on exceptions.
- Reproduce whatever I did the first time that made the program crash.
- When the crash occurs, look at the line of code the debugger is pointing to.
- You should be able to logically reason about what is going on in the line of code and which part(s) could throw this type of exception.
- Check the values of variables that are being read/written by this line of code and make sure all pointers have valid addresses and all variables have a value that is valid.
- Valid addresses are typically NOT 0x00000000 (null), NOT 0xBAADF00D, 0xCDCDCDCD, etc.
- If you see any of these "suspicious" common pointer values they indicate you didn't initialize that variable or allocate/free memory properly.
- If looking at the pointers doesn't help, start at the line of code that crashed and follow the assignment of variables back to their starting points and logically reason about what's going on until you spot the mistake.
- You may have to set breakpoints earlier in your program to investigate suspicious areas of code.
- Some IDEs let you set 'tracepoints' which are like breakpoints but they print messages instead of stopping.
- Some IDEs have conditional breakpoints that you can program to stop only when a value gets out of an expected range.
- Some IDEs have data breakpoints that you can set to only stop when something reads/writes a specific memory address.

Wow, thanks Nypyren and Alberth for the info. I had no idea there was a way to see what variables' values were being set to in real time or any of that. And I had no idea there were compiler settings that might help. Now that I at least have an idea of what to research (like how to do these things in my IDE) I definitely have some options now. This was what I was looking for (in particular Nypyren's explanation of the general process, which involves a lot of stuff I didn't even know was possible, lol). And yes, I understand that normally actual code and error messages help (lol, sorry frob) but my question was a general "how do I go about fixing this type of error" and not a "please help! fix this for me!". :)

But while we're on the subject, what I have is:

sqlite3_prepare_v2(db, sql.c_str(), -1, statement, NULL);

where db and statement are SQLite handles (to the database and prepared statement objects) and sql is an std::string. The SQLite docs say if the third parameter (the query string length in bytes) is negative, it'll read till the end of the string (so that's the -1). And the last parameter (the NULL) is an optional callback to free the string after the SQL stuff is done. I don't really understand why this would be necessary, and apparently neither does anyone else who writes examples, because in every example I've seen this is always NULL or 0. And that makes sense, because I'm not using the "new" keyword to create my strings (so I don't have to do "delete" at the end anyway...right? Or am I out in left field on that one?).

Now I'm having trouble finding the exact error message again, but it was "access violation" with a bunch of numbers, like I said. And of course Google that, and you'll find everything that has nothing to do with the problem at hand, cuz it's that general. So I started investigating the IDE's debug tools, and managed to get this: "#0 0x46a5c0 sqlite3LockAndPrepare (db=0xe02e00, zSql=0x8d2434 "INSERT INTO table('text') (C:\path\to\\sqlite3.c:116069)
#1 0x46a7e2 sqlite3_prepare_v2 (db=0xe02e00, zSql=0x8d2434 "INSERT INTO table('text') (C:\path\to\sqlite3.c:116151)" and from here it points to the line in my code where I called the line above.

So my first thought was "duh, there's no space between the table name and the paren, so it's an SQL syntax error" but I looked in my code and I did put a space there, and even after I changed the statement a bit it still shows that same string. And because the stack trace points to something in SQLite's code, I'm guessing it's something SQLite itself is doing with what I gave it. So at the end of all that, I know which part of the code is failing, but still don't know why (which is why the question was more general - I wanted to know how to fix stuff like this, not just "here, you fix it" lol). Hope that makes sense. :)

But anyway, it sounds like I got my answer to the original question (there are debugging tools in my IDE that I never knew about). But beyond that, I'd sure appreciate an example of how to use sqlite_prepare_v2 correctly. lol thanks again.

0xe02e00 and 0x8d2434 look like valid pointers (or possibly formerly valid addresses) to me.

The "statement" parameter looks suspicious to me. The docs I found said it's an "out" ** parameter.

Usually when there's a double-pointer parameter, the function expects you to allocate a pointer and pass a pointer-to-that-pointer (i.e. the & in "-1, &statement, NULL"). The function will then dereference the "outer" pointer and assign it a value (a pointer) that you can use after the function returns. Based on your description, I believe this step is what's crashing.

Typically the "allocation" of an 'out' variable is just making it on the stack (like in the following page I found):

http://www.dreamincode.net/forums/topic/122300-sqlite-in-c/

i.e.

// WRONG
sqlite3_stmt **statement; // uninitialized value, trying to dereference this will crash.
if(sqlite3_prepare_v2(database, "CREATE TABLE a (b INTEGER, c INTEGER);", -1, statement, 0) == SQLITE_OK)

// RIGHT
sqlite3_stmt *statement; // uninitialized value, but the *variable* lives on the stack, so taking the address of this and then dereferencing it is OK!
if(sqlite3_prepare_v2(database, "CREATE TABLE a (b INTEGER, c INTEGER);", -1, &statement, 0) == SQLITE_OK)
The code inside sqlite3_prepare_v2 probably looks something like this:


int sqlite3_prepare_v2(sqlite3 *db, const char *zSql, int nByte, sqlite3_stmt **ppStmt, const char **pzTail)
{
  // do stuff
  *ppStmt = pointer_to_statement_result; // this is what would crash if ppStmt is a bad pointer.
  return SQLITE_OK;
}

Awesome! I've been all over the web looking for something like this. Yes, I thought it might have to do with the whole "pointer-to-a-pointer" thing, because that really seemed weird; I know what pointers are and how to use them, but the only other time I've seen two asterisks is with an array of C strings (ex: char **SomeArray). But anyway, I'll check out the link and your example to see what I can do. tyvm. :)

but the only other time I've seen two asterisks is with an array of C strings (ex: char **SomeArray)

Arrays hardly exist in C, they are just pointers in disguise. The compiler remembers enough for allocation and sizeof(), but that's about it.

In this case however, it's passing a reference, except the value is a pointer rather than a 'normal' type.
int i;
f(&i);
void f(int *ip) { .. }

typedef int *val;
val i;
f(&i);
void f(val *ip) { .. }

int *i;
f(&i);
void f(int **ip) { .. }
A pointer is not only a pointer, it is also just a data value, much like int.

This topic is closed to new replies.

Advertisement