Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are
spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the
password reset link.
Due to spam on this forum, all posts now need moderator approval.
Entire forum
➜ Programming
➜ General
➜ Multi-Dimensional Char Arrays -- HELP!!
Multi-Dimensional Char Arrays -- HELP!!
|
It is now over 60 days since the last post. This thread is closed.
Refresh page
Pages: 1 2
3
4
Posted by
| TheTraveller
(23 posts) Bio
|
Date
| Tue 15 Jan 2008 10:10 AM (UTC) Amended on Tue 15 Jan 2008 10:16 AM (UTC) by TheTraveller
|
Message
| I'm working on an experimental project to modify SocketMUD 2.2 to use a MySQL database backend instead of external files. I've already gotten the most complex stuff done and conducted several successful database retrieval tests from within the MUD.
However, I've run into a brick wall on something that is probably insultingly simple, and yet after 6 straight hours of Googling, trial-and-error(ing), and banging my head against a wall, I am no closer to a solution than when I started! I'm widely considered an expert in PHP scripting and have some but fairly little experience with C/C++.
Here's what I'm trying to do: I'm working on a simple function that is passed a query string, queries the database, and returns the results in a multidimensional char array. In PHP, here is a conceptual equivalent of the type of variable I'm talking about: $fictional_results[$row_int][$column_int] = $col_value;
In PHP, I could have that working in a matter of seconds. But in C, just about anything I do returns a segmentation fault on runtime (not exactly the most helpful error message I might add). The codebase is written in C flat (i.e. not C++), and the string.h library does not seem compatible (if I try to #include <string.h> it's fine, but if I try to declare any variable as type string it returns a non-sensical parse error on compile).
Here are the relevant declarations in mud.h:
#include "/usr/include/mysql/mysql.h"
...
typedef struct mysql_xdata
{
char xrowdata[1024][1024][1024]; /* This is the variable I'm having trouble with. I've tried "char *xrowdata[1024][1024]", "char xrowdata[1024][1024]", etc, as well as just about every suggestion on Google.... Nothing works! All I want is string_variable[mysql_row_int][mysql_column_int]. */
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024]; /* Used for variable DEBUG. --XXXX */
} XMYSQL;
...
/*
* mysql.c
*/
XMYSQL *mysql_xquery( char xqueryx[1024] );
Now here's the function (in its current form, with a few things like passwords censored out) that performs the query in mysql.c:
XMYSQL *mysql_xquery( char xqueryx[1024] )
{
MYSQL *conn;
MYSQL_RES *res;
MYSQL_ROW row;
char *server = "localhost";
char *user = "root";
char *password = "XXXX";
char *database = "XXXX";
int column = 0;
int sqlrow = 0;
int totalcols = 0;
XMYSQL *xmysql;
xmysql = malloc( sizeof( XMYSQL ) );
sprintf( xmysql->xmysqldebugbuf, "DEBUG SUCCESSFUL!" );
conn = mysql_init(NULL);
/* Connect to database */
if ( !mysql_real_connect( conn, server, user, password, database, 0, NULL, 0 ) )
{
fprintf( stderr, "%s\n", mysql_error( conn ) );
exit( 0 );
}
/* send SQL query */
if ( mysql_query( conn, xqueryx ) )
{
fprintf( stderr, "%s\n", mysql_error( conn ) );
exit( 0 );
}
res = mysql_use_result( conn );
/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
column = 0;
sqlrow++;
while ( column < res->field_count )
{
column++;
totalcols++;
sprintf( xmysql->xrowdata[sqlrow][column], "%s", row[column - 1] ); /* !! ATTENTION !! -- This is the line that is causing the segmentation fault during runtime. If I comment it out, everything works fine, and all the other variables retain their values perfectly, and I have also tested extensively to ensure it's not an error with the MySQL interaction itself. */
}
}
/* Release memory used to store results and close connection */
mysql_free_result( res );
mysql_close( conn );
xmysql->xnumrows = sqlrow;
xmysql->xnumcols = column;
return xmysql;
}
Currently, I'm calling the function from socket.c just as a test debug, essentially to display the output of a test query whenever I connect and it displays the greeting stuff. The code for that isn't relevant to this I don't think, but I can include it anyway if you think it'll help.
I feel really frustrated being held in place by such a basic problem. I do know how C char arrays work (i.e. only stores one char for each key), but I can't seem to wrap my brain around how to go from that to creating a multi-dimensional string array that does what I'm trying to do.
Whew! Any ideas? I really appreciate any help you can provide. =)
--TT
| Top |
|
Posted by
| Robert Powell
Australia (367 posts) Bio
|
Date
| Reply #1 on Tue 15 Jan 2008 10:56 AM (UTC) Amended on Tue 15 Jan 2008 11:03 AM (UTC) by Robert Powell
|
Message
| Wouldnt the simplest approach be to only deal in rows. Read a whole row, write a whole row. Then you dont need to worry about your column position.
char xrowdata[whatever_string_length]
sprintf(xrowdata, "%s %s %s", col1, col2, col3 );
sprintf( xmysql->xrowdata[sqlrow][column], "%s", row[column - 1] );
Now im not expert but that does not look right, your putting the string row[column - 1] into xmysql->xrowdata[sqlrow][column]
now i will prolly say this incorect but are you not putting an array of chars into an array of char arrays. something is curely to go bust there. |
Just a guy having a bit of fun. Nothing more, nothing less, I do not need I WIN to feel validated. | Top |
|
Posted by
| Robert Powell
Australia (367 posts) Bio
|
Date
| Reply #2 on Tue 15 Jan 2008 11:13 AM (UTC) Amended on Tue 15 Jan 2008 11:15 AM (UTC) by Robert Powell
|
Message
|
#include <mysql/mysql.h>
MYSQL mysqlconn;
MYSQL_RES *result;
MYSQL_ROW row;
extern int sql_working;
/* declarations */
#define MY_SERVER "localhost"
#define MY_USER "YOUR USER"
#define MY_PWD "YOUR PASSWORD"
#define MY_DB "YOUR DATABASE"
#define MY_SOCK NULL
#define MY_PORT 3306
#define MY_FLAG 0
#define bool int
#define TRUE 1
#define FALSE 0
void load_mysql (void);
int MySQLQuery (char * query);
bool open_db (void);
void close_db (void);
---------
Now you need the handler functions..
Make a mysql.c file and put..
(Any suggestions? Did I do this right? It seems to work..)
---------
/*
* load_mysql()
*
* Opens the database, provides errors, etc
*/
void load_mysql(void)
{
if (!open_db() )
{
logf("**** MYSQL ****: Could not open Database.\n");
logf("**** MYSQL ****: Reason:\n%s\n", mysql_error(&mysqlconn));
exit (1);
return;
}
logf("**** MYSQL ****: Connection open and working.\n");
sql_working = 1;
return;
}
/*
* MySQLQuery()
*
* Inserts a raw query to the SQL server
* returns true if it works, false if it fails
*/
int MySQLQuery(char * query)
{
if (mysql_real_query(&mysqlconn, query, strlen(query)))
{
logf("**** MYSQL ****: Error:\n%s\n", mysql_error(&mysqlconn));
return 0;
}
mysql_free_result(result);
return 1;
}
/*
* open_db()
*
* Opens a connection to the SQL database
*/
bool open_db(void)
{
if (!mysql_init(&mysqlconn))
{
logf("**** MYSQL ****: Unable to open database!\n");
return FALSE;
}
if ((mysql_real_connect(&mysqlconn, MY_SERVER, MY_USER, MY_PWD, MY_DB, MY_PORT, MY_SOCK, MY_FLAG)) == NULL)
{
logf("**** MYSQL ****: open_db() failed during opening!\n");
mysql_close(&mysqlconn);
return FALSE;
}
return TRUE;
}
/*
* close_db()
*
* Closes connection to the SQL server
*/
void close_db(void)
{
logf("**** MYSQL ****: close_db(): closed\n");
mysql_close(&mysqlconn);
return;
}
thats all you need to do to open close and query a DB, code is not mine, credit goes to Thri
Terms of Use:
email me, saying your using it. Or not, it doesn't matter, this is too minor
to matter really, but my ego could use a boost ;)
If, you find any bugs, problems, "you shouldn't"s PLEASE, tell me, the handler functions
I post here are used in numerous things I do... So its important ;)
cyhawkx@gmail.com
-Thri |
Just a guy having a bit of fun. Nothing more, nothing less, I do not need I WIN to feel validated. | Top |
|
Posted by
| Isthiriel
(113 posts) Bio
|
Date
| Reply #3 on Tue 15 Jan 2008 12:23 PM (UTC) |
Message
|
Quote:typedef struct mysql_xdata
{
char xrowdata[1024][1024][1024]; /* This is the variable I'm having trouble with. I've tried "char *xrowdata[1024][1024]", "char xrowdata[1024][1024]", etc, as well as just about every suggestion on Google.... Nothing works! All I want is string_variable[mysql_row_int][mysql_column_int]. */
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024]; /* Used for variable DEBUG. --XXXX */
} XMYSQL;
char xrowdata[1024][1024][1024];
That's a gig. I don't know if malloc has an upper limit on how much memory it can allocate at once, but if it does, that probably exceeds it.
char *xrowdata[1024][1024];
Here you are declaring a 1024x1024 grid of character pointers, which I think is what you want (apart from the wastefulness of allocating the huge grid)... except they are never allocated before you try to sprintf them.
char xrowdata[1024][1024];
And now you have a 1024x1024 grid of single characters, which are also a bad target for sprintf.
You're copying the data out of the row object for some reason? What happens to it when you're finished with it? | Top |
|
Posted by
| TheTraveller
(23 posts) Bio
|
Date
| Reply #4 on Tue 15 Jan 2008 12:34 PM (UTC) |
Message
| Basically, to answer your question, I'm not married to any of the array declarations for that variable I listed. Again, all I want is the best/most efficient possible way to make it so that I can store all the query results data into an array that can be indexed by row, column. The only way I know how to do this is with a multidimensional array, but if you have a better method in mind, by all means tell me. =)
As for the rest of the code, please note that this is just a working prototype, so I really don't want to spend any time worrying about making it "cleaner"/etc, or anything else that would require a major re-write. All the MySQL stuff is working perfectly, it's just that I can't figure out how to store it into a variable that fits the specifications I outlined.
Will a multidimensional array work at all, or is there a better method of return? I do need both the row data and column data accessible in a single variable or structure or whatever in order for the later development stages to work.
--Kris
| Top |
|
Posted by
| Nick Cash
USA (626 posts) Bio
|
Date
| Reply #5 on Tue 15 Jan 2008 01:52 PM (UTC) Amended on Tue 15 Jan 2008 01:53 PM (UTC) by Nick Cash
|
Message
|
sprintf( xmysql->xrowdata[sqlrow][column], "%s", row[column - 1] ); /* ... */
At first glance, I would say the reason this isn't working quite right is because you declared it with three dimensions. I've never had any real world problem require a three dimensional array (I'm not saying there isn't one mind you...), and in this case you don't seem to need three dimensions.
Change:
char xrowdata[1024][1024][1024];
to
char xrowdata[1024][1024];
in your data struct and make clean.
And it -may- stop seg faulting on you. While this is not the most efficient means of doing this (and can be done much easier in C++), if all you are looking for is a working version I see no reason why that won't work.
That said, its also been a while since I've played with the MySQL C API, so test it out and see. You might consider looking at a working implementation (there are a great many out there) and see how they do it. |
~Nick Cash
http://www.nick-cash.com | Top |
|
Posted by
| David Haley
USA (3,881 posts) Bio
|
Date
| Reply #6 on Tue 15 Jan 2008 07:20 PM (UTC) |
Message
| First of all, as Whiteknight said, this would be much easier in C++ since you could use container classes like maps of maps of strings to let you index on the rows and columns; it would be easy, fast and efficient. Arguably, given your background, it would also be more appropriate for you to work in C++ because the STL provides some of the higher-level abstractions you are used to. All this is certainly possible in C, you just will have to work harder at it and understand the fundamental issues.
Some specific issues:
Quote: But in C, just about anything I do returns a segmentation fault on runtime (not exactly the most helpful error message I might add).
This is telling you that you did something incorrect to memory somewhere, like deleting something twice or accessing invalid memory. The error message isn't more helpful because C, unlike PHP, is run directly in machine code, and therefore there is no environment to monitor execution and fix (or at least detect) problems as they occur.
There are many tools to help find these, such as gdb and valgrind. Nick has a very good gdb guide on this site.
http://www.gammon.com.au/forum/bbshowpost.php?bbsubject_id=3653
Quote: The codebase is written in C flat (i.e. not C++), and the string.h library does not seem compatible (if I try to #include <string.h> it's fine, but if I try to declare any variable as type string it returns a non-sensical parse error on compile).
There is no type 'string' in C, with or without string.h. If you open string.h you'll find a collection of utility functions.
A string in C is just a sequence of bytes in memory terminated by a zero. So, char s[100], an array of 100 characters, is declaring a "string" of length 100 (well, 99 if you don't count the zero). char * s, a pointer to a character, can also be thought of as a string, at least, assuming there's a zero ending it an all that.
But basically, C has no built-in notion of string. C++ doesn't, technically, but does provide the 'string' class that does pretty much what you would expect it to (although it has its own idiosyncrasies, too). |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | Top |
|
Posted by
| Isthiriel
(113 posts) Bio
|
Date
| Reply #7 on Tue 15 Jan 2008 10:30 PM (UTC) Amended on Tue 15 Jan 2008 10:43 PM (UTC) by Isthiriel
|
Message
| The thing you have to remember is that C was originally written by assembly programmers. The language is statically typed, so the compiler is the last point that knows what type a variable is. At runtime it is all just memory.
malloc() returns a pointer to an undifferentiated block of memory of the size you ask for. The typecasts are to tell the compiler to warn you if you do something unexpected with the memory (and you can get around that by casting to (void *) -- which is useful when you are writing a dynamically typed system).
Because of this, a C 'string' isn't an object like a Java object of a PHP variable, it is a sequence of char in memory, so when you are declaring a 'string' you either declare an array of char or a pointer to some unspecified number of char (ie (char *)). The array will form part of the function's execution frame and is hence allocated on the stack. The pointer needs to be given a space on the heap allocated by malloc, and can't be used (segfault) until you have done that. At runtime, functions can't distinguish between an array and a pointer because they are both handed around as pointers to the first element (a legacy of the way variables were (are still?) passed as a single word, so a function knows that to get all of its arguments, it just has to pull a certain number of words off the calling stack).
All of the functions declared in string.h expect a pointer to a char (which might be array of char on the stack).
As far as the data structure goes, if you can get the number of results returned before allocating your xmysql:
typedef struct mysql_xdata {
char ***xrowdata;
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024];
} XMYSQL;
Then, to allocate it, after you have the row count:
xmysql = (XMYSQL *) malloc(sizeof(XMYSQL));
xmysql->xrowdata = (char ***) malloc(sizeof(char **) * row_count);
Then, when you are copying fields:
/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
column = 0;
xmysql->xrowdata[sqlrow] = (char **) malloc(sizeof(char *) * res->field_count);
while ( column < res->field_count )
{
xmysql->xrowdata[sqlrow][column] = (char *) malloc(sizeof(char) * (1 + strlen(row[column])));
strcpy(xmysql->xrowdata[sqlrow][column], row[column]);
column++;
totalcols++;
}
sqlrow++;
}
If you can't get the row count before walking the results, then you need to use a linked list (...I hope you know how to manage one), I tend to prefer doubly-linked lists because being able to walk it in reverse (without recursing into it) can be useful (and the 4+4*length byte overhead is usually negligible):
struct mysql_xrow;
typedef struct mysql_xrow {
struct mysql_xrow *next;
struct mysql_xrow *prev;
char **xdata;
}
typedef struct mysql_xdata {
struct mysql_xrow *xrowdata_head;
struct mysql_xrow *xrowdata_tail;
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024];
} XMYSQL;
Then, to allocate it:
xmysql = (XMYSQL *) malloc(sizeof(XMYSQL));
xmysql->xrowdata_head = NULL;
xmysql->xrowdata_tail = NULL;
Then, when you are copying fields:
/* output fields of each row */
while ( ( row = mysql_fetch_row( res ) ) != NULL )
{
struct mysql_xrow *xrow;
column = 0;
xrow = (struct mysql_xrow *) malloc(sizeof(struct mysql_xrow));
xrow->xdata = (char **) malloc(sizeof(char *) * res->field_count);
while ( column < res->field_count )
{
xrow->xdata[column] = (char *) malloc(sizeof(char) * (1 + strlen(row[column])));
strcpy(xrow->xdata[column], row[column]);
column++;
totalcols++;
}
sqlrow++;
if (NULL == xmysql->xrowdata_tail) {
xmysql->xrowdata_head = xmysql->xrowdata_tail = xrow
xrow->next = xrow->prev = NULL;
continue;
}
xrow->prev = xmysql->xrowdata_tail;
xrow->prev->next = xrow;
xrow->next = NULL;
xmysql->xrowdata_tail = xrow;
}
| Top |
|
Posted by
| TheTraveller
(23 posts) Bio
|
Date
| Reply #8 on Wed 16 Jan 2008 12:55 AM (UTC) |
Message
| Yeah I prefer C++, but as I said, SocketMUD is written in C, and I wasn't able to find a fully working version that was converted to C++. So I have no choice but to work it in C, unless of course any of you feels like helping me convert the entire codebase to C++ lol.
Ok now that that's outta the way, lemme read your latest instructions then I'll respond. =)
| Top |
|
Posted by
| David Haley
USA (3,881 posts) Bio
|
Date
| Reply #9 on Wed 16 Jan 2008 01:05 AM (UTC) |
Message
| You should be able to compile it in C++ mode, at least, with fairly minimal changes. You don't have to convert everything to classes and OOP structures. But, you will get all the advantages of being able to use standard container classes like vectors, maps, strings etc. |
David Haley aka Ksilyan
Head Programmer,
Legends of the Darkstone
http://david.the-haleys.org | Top |
|
Posted by
| TheTraveller
(23 posts) Bio
|
Date
| Reply #10 on Wed 16 Jan 2008 01:22 AM (UTC) Amended on Wed 16 Jan 2008 01:25 AM (UTC) by TheTraveller
|
Message
| Regarding the C++ thing, I still have no idea how to go about doing that (i.e. do I just rename the files to .cpp and make minor mods to the Makefile, or is there more to it?). I'd love to have the flexibility of C++, but I'm not sure how to switch it.
Regarding the code suggestions regarding the array issue....
I just tried your code for mud.h and mysql.c with a clean make, and I'm getting the following compile errors:
mud.h:188: two or more data types in declaration of `XMYSQL'
mud.h:188: warning: duplicate `typedef'
To be safe, I just copied/pasted your code; specifically, I replaced my code:
typedef struct mysql_xdata
{
char xrowdata[1024][1024][1024]; /* Row, column. --XXXX */
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024]; /* Used for variable DEBUG. --XXXX */
} XMYSQL;
With your code:
struct mysql_xrow;
typedef struct mysql_xrow
{
struct mysql_xrow *next;
struct mysql_xrow *prev;
char **xdata;
}
typedef struct mysql_xdata
{
struct mysql_xrow *xrowdata_head;
struct mysql_xrow *xrowdata_tail;
int xnumrows;
int xnumcols;
char xmysqldebugbuf[1024];
} XMYSQL;
What did I do wrong?
| Top |
|
Posted by
| Isthiriel
(113 posts) Bio
|
Date
| Reply #11 on Wed 16 Jan 2008 01:38 AM (UTC) Amended on Wed 16 Jan 2008 02:23 AM (UTC) by Isthiriel
|
Message
| Oops.
struct mysql_xrow /* There shouldn't be a typedef on this line. */
{
struct mysql_xrow *next;
struct mysql_xrow *prev;
char **xdata;
}; /* and there should be a semicolon here :-/ */
I should also add that when you are finished with the result, you need to free the actual data, then the rows, then the containing struct, like so:
void free_xmysql(XMYSQL *xmysql)
{
struct mysql_xrow *xrow = NULL;
short col = 0;
if (NULL == xmysql)
return;
while (NULL != xmysql->xrowdata_tail)
{
xrow = xmysql->xrowdata_tail;
xmysql->xrowdata_tail = xrow->prev;
xrow->prev->next = NULL;
xrow->prev = NULL;
col = xmysql->xnumcols;
while (0 < col)
{
free(xrow->xdata[--col]);
xrow->xdata[col] = NULL; /* redundant, but good practice */
}
free(xrow->xdata);
xrow->xdata = NULL; /* redundant, but good practice */
}
/* if tail is NULL, then there are no entries in the list so head is invalid, and should be NULL. */
xmysql->xrowdata_head = NULL;
free(xmysql);
return;
}
| Top |
|
Posted by
| TheTraveller
(23 posts) Bio
|
Date
| Reply #12 on Wed 16 Jan 2008 01:48 AM (UTC) |
Message
| That fixed the typedef error, but I'm still getting the "mud.h:188: two or more data types in declaration of `XMYSQL'" error.
| Top |
|
Posted by
| Isthiriel
(113 posts) Bio
|
Date
| Reply #13 on Wed 16 Jan 2008 02:21 AM (UTC) |
Message
| That would be the missing semicolon at the end of the struct definition. It's something I have a bad habit of forgetting. *sigh* | Top |
|
Posted by
| TheTraveller
(23 posts) Bio
|
Date
| Reply #14 on Wed 16 Jan 2008 02:48 AM (UTC) |
Message
| Alrighty, it works now, and I added the free-up function as well.
I always get confused when working with structs (just don't encounter them very often), so how would I use this new variable setup to access certain data?
Let's say, for example, we have the following query: "select * from players"
And let's say that, for whatever reason, I want to access character's display name (column 3) of the 5th result (row). Here's what I can figure out and what I can't, using snippets of the debug code from socket.c for this example:
XMYSQL * xpfile;
char qtxt[1024];
...
xpfile = malloc( sizeof( *xpfile ) );
sprintf( qtxt, "select * from players" );
xpfile = (XMYSQL *) mysql_xquery( qtxt );
text_to_buffer( sock_new, ?????????? ); /* Display name (column 3) for 5th player (row) in result. */
?????????? /* Any subsequent code needed, like to free-up memory or whatever? */
Is that right (not sure if the malloc is setup right)? What would I want to replace the "??????????" with to make it work (with as little code as possible)?
Thank you so much for your help thus far! =)
| Top |
|
The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).
To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.
129,665 views.
This is page 1, subject is 4 pages long: 1 2
3
4
It is now over 60 days since the last post. This thread is closed.
Refresh page
top