[Home] [Downloads] [Search] [Help/forum]

Gammon Forum

See www.mushclient.com/spam for dealing with forum spam. Please read the MUSHclient FAQ!

[Folder]  Entire forum
-> [Folder]  Programming
. -> [Folder]  General
. . -> [Subject]  Multi-Dimensional Char Arrays -- HELP!!
Home  |  Users  |  Search  |  FAQ
Username:
Register forum user name
Password:
Forgotten password?

Multi-Dimensional Char Arrays -- HELP!!

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page


Pages: 1 2  3  4  

Posted by TheTraveller   (23 posts)  [Biography] 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
[Go to top] top

Posted by Robert Powell   Australia  (367 posts)  [Biography] 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.
[Go to top] top

Posted by Robert Powell   Australia  (367 posts)  [Biography] 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.
[Go to top] top

Posted by Isthiriel   (111 posts)  [Biography] 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?
[Go to top] top

Posted by TheTraveller   (23 posts)  [Biography] 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
[Go to top] top

Posted by Nick Cash   USA  (626 posts)  [Biography] 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
[Go to top] top

Posted by David Haley   USA  (3,881 posts)  [Biography] 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
[Go to top] top

Posted by Isthiriel   (111 posts)  [Biography] 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;
}
[Go to top] top

Posted by TheTraveller   (23 posts)  [Biography] 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. =)
[Go to top] top

Posted by David Haley   USA  (3,881 posts)  [Biography] 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
[Go to top] top

Posted by TheTraveller   (23 posts)  [Biography] 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?
[Go to top] top

Posted by Isthiriel   (111 posts)  [Biography] 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;
}

[Go to top] top

Posted by TheTraveller   (23 posts)  [Biography] 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.
[Go to top] top

Posted by Isthiriel   (111 posts)  [Biography] 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*
[Go to top] top

Posted by TheTraveller   (23 posts)  [Biography] 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! =)
[Go to top] 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.


32,095 views.

This is page 1, subject is 4 pages long: 1 2  3  4  [Next page]

It is now over 60 days since the last post. This thread is closed.     [Refresh] Refresh page

Go to topic:           Search the forum


[Go to top] top

Quick links: MUSHclient. MUSHclient help. Forum shortcuts. Posting templates. Lua modules. Lua documentation.

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.

[Home]


Written by Nick Gammon - 5K   profile for Nick Gammon on Stack Exchange, a network of free, community-driven Q&A sites   Marriage equality

Comments to: Gammon Software support
[RH click to get RSS URL] Forum RSS feed ( https://gammon.com.au/rss/forum.xml )

[Best viewed with any browser - 2K]    [Hosted at FutureQuest]