Thread: Build an array of strings dynamically

  1. #1
    Registered User
    Join Date
    Mar 2006
    Posts
    158

    Build an array of strings dynamically

    Following my last topic I have another question related somehow...

    I now have this generic menu function declared like this:
    Code:
    int menu_function(char **menuops, int nOps);
    And I call it with something like this:
    Code:
    char *menuops[] = {"Option 1", "Option 2", "Option 3"};
    int op = -1;
    
    op = menu_function(menuops, sizeof(menuops) / sizeof(char *));
    Now, I need to create a new and different array of strings dynamically. I'm opening some directory and get all file names inside that directory (this is already done) and I want to add all those filenames into an array of strings much like menuops so later I can call menu_function with those filenames as menu options.

    If I have a variable declared like:
    Code:
    char *menuops[];
    How can I build an array of strings into that variable?

  2. #2
    Lean Mean Coding Machine KONI's Avatar
    Join Date
    Mar 2007
    Location
    Luxembourg, Europe
    Posts
    444
    By using exactly what I wrote in your last topic already:
    Code:
    size_t stringNbr = 10;
    size_t maxStringLength = 128;
    int i;
    char **myStringArray;
    myStringArray = malloc(stringNbr * sizeof(char *));
    for (i = 0; i < stringNbr; i++)
    {
        myStringArray[i] = malloc(maxStringLength * sizeof(char));
    }
    To put a value into the array of strings, you use:

    Code:
    strcpy(myStringArray[i], "this is a string");
    And be aware that this notation:
    Code:
    char *menuops[];
    is not legal and is only used to initialize an array if the declaration follows.

    If you know the size of the array of strings, you can also write:
    Code:
    char myStringArray[10][300];
    to define an array of 10 strings of size 300.

  3. #3
    Registered User
    Join Date
    Mar 2006
    Posts
    158
    Sorry, didn't read the last part with full attention... So, for testing, I came up with this:

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(void) {
    	char *options[] = {"Option 1", "Option 2", "Option 3"};
    	char **myStringArray;
    	int i, numOps;
    	
    	numOps = sizeof(options) / sizeof(char *);
    	
    	myStringArray = malloc(numOps * sizeof(char *));
    	
    	for (i = 0; i < numOps; i++) {
        	myStringArray[i] = malloc(strlen(options[i]) * sizeof(char));
        	strcpy(myStringArray[i], options[i]);
    	}
    	
    	for (i = 0; i < numOps; i++) {
    		printf("&#37;s\n", myStringArray[i]);
    	}
    	
    	return 0;
    }
    I tested it, and it worked fine. Is it ok or the code can be improved?

    P.S:
    Simple question... Why sometimes I need to use sizeof(char *) instead of only sizeof(char)? Like in the following 2 lines:
    Code:
    numOps = sizeof(options) / sizeof(char *);	
    myStringArray = malloc(numOps * sizeof(char *));
    Last edited by Nazgulled; 04-02-2007 at 10:28 AM.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > numOps = sizeof(options) / sizeof(char *);
    I'd prefer
    numOps = sizeof(options) / sizeof(options[0]);
    Then you're not dependent on the declaration.

    > myStringArray = malloc(numOps * sizeof(char *));
    Say
    myStringArray = malloc(numOps * sizeof(*myStringArray ) );
    Then you're not dependent on the declaration.

    > myStringArray[i] = malloc(strlen(options[i]) * sizeof(char));
    You forgot to add 1 to the length to allow space for the \0
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    The questions been answered. Boo. I had a few things to say:

    You will notice a couple of things about malloc'd multidimensional arrays. First, you will have to estimate the sizes of the dimensions to get anything workable, so either go with very large, or refine what kind of data you expect them to hold and get a smaller size.

    Resizing a multidimensional array is probably going to be a pain, so if you want, you could also do something like this:
    Code:
    char *foo = malloc ( nrows * ncols * sizeof(*foo) );
    foo[nrows * this_row + idx] = /* accessing the array is a matter of some mild arithmatic, 
                                     to "skip over" chunks that don't matter */
    free(foo);                    /* by avoiding a fragmented memory strategy, reallocing and freeing 
                                     the memory is very elementary */
    Not to imply that this is the best choice in all situations, but generally you want to go with what you can understand, because there isn't a perfect method. The reason that people choose fragmented memory strategies is because it is easier resize in the middle of the array, I suppose. Keep in mind though, that you will probably always need to shift data around if one string needs more room, no matter what strategy you use.

  6. #6
    Registered User
    Join Date
    Mar 2006
    Posts
    158
    Well, I'm not able to make it work on my real code... This is the code I have:

    Code:
    void loadCategories(void) {
    	struct dirent *dpItem;
    	char **catList;
    	int i, j;	
    	
        DIR *dp;
    
        if((dp = opendir("data/games/JF")) != NULL) {   	
        	while((dpItem = readdir(dp)) != NULL) {
        		if(strcmp(dpItem->d_name, ".") && strcmp(dpItem->d_name, "..")) {
        			catList[i] = malloc((strlen(dpItem->d_name) + 1) * sizeof(char));
    				strcpy(catList[i++], dpItem->d_name);
        		}
        	}
        	
        	mvprintw(0,0, "%s\n", catList[0]);
        	
        	/*for (j = 0; j < i; i++) {
    			printw("%s\n", catList[j]);
    		}*/
    		
    		refresh();
    
        	closedir(dp);
        }
    }
    The problem is on the line mvprintw(0,0, "%s\n", catList[0]); which produces a segmentation fault, the code works if I comment that line (as you can see with the below for loop commented, cause I was trying to print all files found). But that won't help cause I need to print the file names found...

    Just in case you don't know (which I doubt) the printw() is cause I'm using ncurses and it works in the same way as printf().

    What's wrong with my code?

  7. #7
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    Well there is no obvious sign of where you allocate catList before assigning catList[ i ]
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  8. #8
    Lurking whiteflags's Avatar
    Join Date
    Apr 2006
    Location
    United States
    Posts
    9,612
    I'd also like to point out that i is being used before it holds a value (such as 0).
    Last edited by whiteflags; 04-02-2007 at 11:30 AM.

  9. #9
    Registered User
    Join Date
    Mar 2006
    Posts
    158
    I didn't because I don't know how much files there is in the directory, there can be 1, 2, or more I don't know. How can I allocated memory for catList if I don't know how much?

    But I don't think that's the problem, cause for now, I now there are only 2 files so I put:
    Code:
    catList = malloc(2 * sizeof(char *));
    Before the while loop and it didn't work anyway...

    Quote Originally Posted by citizen View Post
    I'd also like to point out that i is being used before it holds a value (such as 0).
    I though every int declared variable would have 0 automatically assigned if I didn't assigned a value manually... Either way, I also tried to define i as 0 before using it, and it didn't work also.

  10. #10
    Lean Mean Coding Machine KONI's Avatar
    Join Date
    Mar 2007
    Location
    Luxembourg, Europe
    Posts
    444
    Code:
    catList = malloc(2 * sizeof(char *));
    This only allocates 2 pointers to strings, not the strings themselves.

    And if you're interested in the files inside a directory, this FAQ entry might help you.

  11. #11
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,659
    > I didn't because I don't know how much files there is in the directory
    Well everything else isn't going to pick up the slack for you. If you don't do something, it isn't going to happen.

    BTW, this exact problem is covered in the FAQ under how to read files in a directory, including how to implement an expanding array.

    Edit: which KONI has just posted
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  12. #12
    Registered User
    Join Date
    Mar 2006
    Posts
    158
    In my previous post where I stated the 2 things that you guys pointed out and I said I tested them but didn't work... well, I've tested them but I never tested them both at the same time, so now it's working... I also changed my code and removed malloc() and strcpy() in favor of strdup() as suggested by a friend... I didn't know this function.

    Anyway, this is my actual code now:

    Code:
    void loadCategories(void) {
    	struct dirent *dpItem;
    	char **catList;
    	int i = 0, j;	
    	
        DIR *dp;
    
        if((dp = opendir("data/games/JF")) != NULL) {
        	catList = malloc(2 * sizeof(char *));
        	 	
        	while((dpItem = readdir(dp)) != NULL) {
        		if(strcmp(dpItem->d_name, ".") && strcmp(dpItem->d_name, "..")) {
    				catList[i++] = strdup(dpItem->d_name);
        		}
        	}
        	
        	for (j = 0; j < i; j++) {
    			mvprintw(j,0,"%s\n", catList[j]);
    		}
    		
    		refresh();
    
        	closedir(dp);
        }
    }
    And I have one last question as I didn't exactly understand what you said KONI. Right now, I know I have only 2 files in the directory so I've used catList = malloc(2 * sizeof(char *)); but if I have more? What should I do? Cound the files before allocating memory for catList? Or...

  13. #13
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I also changed my code and removed malloc() and strcpy() in favor of strdup() as suggested by a friend... I didn't know this function.
    strdup() is a non-standard function. I recommend that you don't use it, but you can if you want to. It's relatively easy to implement strdup() so if a compiler doesn't have it you can write a version of it. (You're already using POSIX functions like readdir() and closedir() -- might as well use strdup too. )

    Use realloc(), as outlined in these FAQs (especially the first one):
    http://faq.cprogramming.com/cgi-bin/...&id=1044780608
    http://faq.cprogramming.com/cgi-bin/...&id=1045780608
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  14. #14
    Registered User
    Join Date
    Mar 2006
    Posts
    158
    I tried to read the FAQ but the only code that uses realloc() is that big Win32 example which is too confusing for me... I can't understand how to use realloc on my code...

  15. #15
    Lean Mean Coding Machine KONI's Avatar
    Join Date
    Mar 2007
    Location
    Luxembourg, Europe
    Posts
    444
    1. What does malloc do ?
    2. What does realloc do ?
    3. When would you need use realloc ?
    4. How could you use this information to dynamically expand the array depending on the number of files in a directory ?

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Swapping strings in an array of strings
    By dannyzimbabwe in forum C Programming
    Replies: 3
    Last Post: 03-03-2009, 12:28 PM
  2. Replies: 2
    Last Post: 07-11-2008, 07:39 AM
  3. How To Declare and Dynamically Size a Global 2D Array?
    By groberts1980 in forum C Programming
    Replies: 26
    Last Post: 11-15-2006, 09:07 AM
  4. Passing an Array of Strings from VB to a C DLL
    By mr_nice! in forum Windows Programming
    Replies: 9
    Last Post: 03-08-2005, 06:16 AM
  5. Array of strings in C
    By szill in forum C Programming
    Replies: 10
    Last Post: 02-22-2005, 05:03 PM