Thread: help understanding arrays of strings!

  1. #1
    Registered User
    Join Date
    Oct 2007
    Posts
    100

    help understanding arrays of strings!

    hello everybody!
    I'd like to ask you an help to understand how to deal with arrays of strings. I don't know why but I've a mess in my mind..
    Every time my program has to read several strings from stdin I do something like this:
    Code:
    char strings[ROWS][COLS];
    int i;
    for (i=0;i<ROWS;i++) 
    {
    printf("String #%d: ",i+1);gets(strings[i]);
    }
    althought I'd like to declare the array of strings in this way:
    Code:
    char * strings[ROWS];
    which brings me to a buffer overflow..
    Worse, every time I have to pass such an array to a function i do this:
    Code:
    quickSort(strings,0,ROW-1);
    where the prototype of the function is for example:
    Code:
    void quickSort(char * str[],int lb, int ub)
    This cause such a warning from the compiler:
    Code:
     warning: passing argument 1 of ‘quickSort’ from incompatible pointer type
    Then, I understood that I haven't clear in my mind how to deal with arrays of strings (maybe with multidimensional arrays in general) and my book (C how to program by Deitel) didn't help.
    May you kindly suggest me any link (and excercises) to study well this topic?
    Thanks a lot!
    Bye!

  2. #2
    uint64_t...think positive xuftugulus's Avatar
    Join Date
    Feb 2008
    Location
    Pacem
    Posts
    355
    Quote Originally Posted by smoking81 View Post
    hello everybody!
    I'd like to ask you an help to understand how to deal with arrays of strings. I don't know why but I've a mess in my mind..
    Every time my program has to read several strings from stdin I do something like this:
    Code:
    char strings[ROWS][COLS];
    int i;
    for (i=0;i<ROWS;i++) 
    {
    printf("String #&#37;d: ",i+1);gets(strings[i]);
    }
    You really should try to understand the concept of arrays and pointers first.
    The declaration of strings in your code defines that it is an array of ROWS in count arrays of COLS characters.
    That's not necessarily bad, but it is not necessarily good either.
    althought I'd like to declare the array of strings in this way:
    Code:
    char * strings[ROWS];
    which brings me to a buffer overflow..
    Because now you declare string to be an array of ROWS pointers to char. That is an entirely different thing. Your prior code worked most of the times because you had allocated space to store the strings, but now you just allocate space for ROWS pointers. There is no space reserved for the actual strings.
    The strategy to follow to do it with such a variable, is:
    Read every string to a temporary buffer of adequate length.
    Allocate to the appropriate place in the strings array, enough space to hold the read in string.
    Copy the contents of the buffer to the freshly allocated space in strings.
    Worse, every time I have to pass such an array to a function i do this:
    Code:
    quickSort(strings,0,ROW-1);
    where the prototype of the function is for example:
    Code:
    void quickSort(char * str[],int lb, int ub)
    This cause such a warning from the compiler:
    Code:
     warning: passing argument 1 of ‘quickSort’ from incompatible pointer type
    That is because the compiler expects a char ** as argument 1 even though in the prototype it is written as a char *str[].
    The compiler converts all such declarations to char ** types. It would surprise you to learn that char string[ROWS][COLS] when passed to a function will be automatically converted to a char * type, hence the warning.

    Hint: Browse a bit around the forum or google around for "simple string arrays reading C", and you might need nothing else to understand.
    Last edited by xuftugulus; 02-21-2008 at 04:10 AM.
    Code:
    ...
        goto johny_walker_red_label;
    johny_walker_blue_label: exit(-149$);
    johny_walker_red_label : exit( -22$);
    A typical example of ...cheap programming practices.

  3. #3
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    char * str[] - this is array of pointers to char

    so char * strings[ROWS];
    contain only pointer, that you have to initialize (btw ROWS do not tell much better use STRING_COUNT or something

    You can initialize pointer with malloc

    Code:
    strings[i] = malloc(len);
    where len is the length of the strig entered by the user +1 (for nul-character)

    to enter string use fgets(buffer, sizeof buffer, stdin) instead of gets - see FAQ why.

    your buffer will be some temp storage like
    Code:
    char buffer[MAX_STRINGLEN];
    After you enter string into buffer, remove '\n' character, allocate strings[i] long enough to store the new string - you will copy string from buffer into strings[i] using strcpy
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  4. #4
    Deathray Engineer MacGyver's Avatar
    Join Date
    Mar 2007
    Posts
    3,210
    Don't use gets(). That's just asking for trouble.

    Here's a simple program dealing with an array of strings.

    Code:
    #include <stdio.h>
    #include <string.h>
    
    #define NUMSTRINGS 5
    #define NUMCHARS 512
    
    int main(void)
    {
    	size_t i;
    	char *temp;
    	char userinput[NUMSTRINGS][NUMCHARS];
    	
    	for(i=0;i<sizeof(userinput)/sizeof(*userinput);i++)
    	{
    		printf("Enter a string: ");
    		fflush(stdout);
    		fgets(userinput[i], sizeof(userinput[i]), stdin);
    		if((temp = strchr(userinput[i], '\n')))
    		{
    			*temp = '\0';
    		}
    	}
    	
    	for(i=0;i<sizeof(userinput)/sizeof(*userinput);i++)
    	{
    		printf("String[%d] = %s\n", i, userinput[i]);
    	}
    	
    	return 0;
    }
    Note, when passing multidimensional arrays to functions, you will have headaches if you don't define the functions properly. I tend to use dynamically sized buffers over multidimensional arrays, but that opens a totally different can of worms.

  5. #5
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    thank you so much guys!
    My problem is maybe due to the fact that I want to follow exactly the book and make excercises using just what is exposed in the previous chapter(s). I have a vague idea of what is the dynamic memory allocation(malloc etc) and the reasons why it is needed and I also guessed that the problem was exactly tied with this so I tried to bypass with the array[ROWS][COLS].. Unfortunately, this topic is exposed much further in the book...

    So I will spend now some time thinking about what you wrote!
    thanks again!

  6. #6
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    hello again!
    I put things together. what do you think about this?

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #define MAX_STRLEN 256
    #define STRING_COUNT 5
    
    void printStrings(char * []);
    
    int main()
    {
    	char * strings[STRING_COUNT];
    	int i;
    	char buffer[MAX_STRLEN];
    	char *tmp;
    	
    	for (i=0;i<STRING_COUNT;i++)
    	{
    		fflush(stdout);//???
    		printf("Enter string #%d: ",i); 
    		fgets (buffer, sizeof(buffer),stdin);
    
    		if (tmp=strchr(buffer,'\n'))
    		{
    			*tmp='\0';
    		}
    		
    		strings[i]=malloc(strlen(buffer-1)*sizeof(char));
    		
    		if (!strings[i]) 
    		{
    			printf("Out of memory");
    			exit(1);
    		}
    		
    		strcpy(strings[i],buffer);
    
    	}
    	printStrings(strings);
    }
    
    void printStrings(char * strings[])
    {
    	int i;
    	
    	for (i=0;i<STRING_COUNT;i++)
    	{
    		printf("%d: %s\n",i,strings[i]);
    	}
    }
    thanks again for your help!

  7. #7
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    fflush(stdout) should be AFTER printf - before fgets
    It forces to show the buffer created by printf before reading users input in case when the stdout stream is buffered

    malloc(strlen(buffer-1) * sizeof (char))

    should be
    malloc(strlen(buffer) + 1)

    You also should check the return value of fgets
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  8. #8
    uint64_t...think positive xuftugulus's Avatar
    Join Date
    Feb 2008
    Location
    Pacem
    Posts
    355
    Now that's a smart student.
    Code:
    ...
        goto johny_walker_red_label;
    johny_walker_blue_label: exit(-149$);
    johny_walker_red_label : exit( -22$);
    A typical example of ...cheap programming practices.

  9. #9
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    hello!
    I wanted to go forward with implementing my quickSort for strings and I found out that also my swap function doesn't work.
    I wrote a small program to test it and i cannot understand why it doesn't swap pointers.. Or better, it seems like if it uses the pointers as if they were passed by value.
    here is my code:

    Code:
    #include <stdlib.h>
    #include <string.h>
    #include <stdio.h>
    
    void swapInt (int * a, int * b);
    void swapStrings(char *,char *);
    
    int main ()
    {
    	int integers[5],i;
    	char * strings[5]={"zero","one","two","three","four"};
    
    	for (i=0;i<5;i++) 
    	{
    		integers[i]=i;
    	}
    
    	swapInt(&integers[1],&integers[3]);
    
    	for (i=0;i<5;i++) 
    	{
    		printf("%d: %d\n",integers[i]);
    	}
    
    	for (i=0;i<5;i++)
    	{
    		printf("%d[%p]: %s\n",i,strings[i],strings[i]);
    	}
    
    	swapStrings(strings[1],strings[3]);
    	
    	for (i=0;i<5;i++)
    	{
    		printf("%d[%p]: %s\n",i,strings[i],strings[i]);
    	}
    
    }
    
    void swapInt (int *a,int *b)
    {
    	int tmp =*a;
    	*a=*b;
    	*b=tmp;
    }
    
    void swapStrings (char *a,char *b)
    {
    	printf("SWAP INPUT: %p %p\n",a,b);
    	
    	char * tmp;
    	tmp=a;
    	a=b;
    	b=tmp;
    	
    	printf("SWAP OUTPUT: %p %p\n",a,b);
    }
    the output is this:

    Code:
    $ ./swap
    0: 0
    1: 3
    2: 2
    3: 1
    4: 4
    0[0x40075c]: zero
    1[0x400761]: one
    2[0x400765]: two
    3[0x400769]: three
    4[0x40076f]: four
    SWAP INPUT: 0x400761 0x400769
    SWAP OUTPUT: 0x400769 0x400761
    0[0x40075c]: zero
    1[0x400761]: one
    2[0x400765]: two
    3[0x400769]: three
    4[0x40076f]: four
    Hence, it seems that the function swaps the pointers but just temporary.. Where is my mistake this time? I'm hating C (and loving you! )
    thanks (once) again!

  10. #10
    Hurry Slowly vart's Avatar
    Join Date
    Oct 2006
    Location
    Rishon LeZion, Israel
    Posts
    6,788
    when you want to swap members of integers array - you pass them by pointer
    but when you want to swap members of the strings arrray - you pass them by values.

    Don't you see here a reason why only one function works?
    All problems in computer science can be solved by another level of indirection,
    except for the problem of too many layers of indirection.
    – David J. Wheeler

  11. #11
    uint64_t...think positive xuftugulus's Avatar
    Join Date
    Feb 2008
    Location
    Pacem
    Posts
    355
    The swapInt(int *a, int *b) function swaps the values of the int's pointed by the parameters you pass them through their address.
    The strings array has actually elements of type char * right? Therefore to swap two of these elements the function would need to be able to swap the char * strings[1] with the char *strings[3] for example.
    To accomplish such a thing the function, would need to know the ok ... address of the pointer strings[1] and of the pointer strings[3], therefore it would need a pointer at a char *...
    Code:
    void swapString(char **a, char **b)
    {
        char *tmp;
        tmp = *a;
        *b = *a;
        *a = tmp;
    }
    and you would call it like: swapString(&strings[1], &strings[3]);
    an it should work...
    Last edited by xuftugulus; 02-22-2008 at 04:44 AM.
    Code:
    ...
        goto johny_walker_red_label;
    johny_walker_blue_label: exit(-149$);
    johny_walker_red_label : exit( -22$);
    A typical example of ...cheap programming practices.

  12. #12
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by vart View Post
    when you want to swap members of integers array - you pass them by pointer
    but when you want to swap members of the strings arrray - you pass them by values.

    Don't you see here a reason why only one function works?
    yes, maybe that Deitel's book sXcXs! I have studied it since the 1st page and didn't find until now (page 345/534) an explaination of these thing (functions like malloc() are explained in the last chapters)....
    and of course also not a discussion (or excercise in the "POINTERS" chapter) about pointers to pointers!
    Last edited by smoking81; 02-22-2008 at 04:40 AM.

  13. #13
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    Quote Originally Posted by xuftugulus View Post
    The swapInt(int *a, int *b) function swaps the values of the int's pointed by the parameters you pass them through their address.
    The strings array has actually elements of type char * right? Therefore to swap two of these elements the function would need to be able to swap the char * strings[1] with the char *strings[3] for example.
    To accomplish such a thing the function, would need to know the ok ... address of the pointer strings[1] and of the pointer strings[3], therefore it would need a pointer at a char *...
    Code:
    void swapString(char **a, char **b)
    {
        char *tmp;
        tmp = *a;
        *b = *a;
        *a = tmp;
    }
    and you would call it like: swapString(&strings[1], &strings[3]);
    an it should work...
    now it's much clearer! thanks!

  14. #14
    uint64_t...think positive xuftugulus's Avatar
    Join Date
    Feb 2008
    Location
    Pacem
    Posts
    355
    It would all be much clearer if you know that functions in C, get their arguments passed by value.
    Wrong:
    Code:
    /* 
      * When called with two existing variables of type char *, s1 and s2 are copies of the
      * addresses of the variables you called them with. Therefore, one can derefernce those
      * addresses, and alter or access the contents of the strings. But the original variables
      * can not be accessed, or altered.
      */
    void swap(char *s1, char *s2)
    {
        char *temp = s1;
        s1 = s2;    /* This is a copy local to the function like temp. No sideffects with this statement.*/
        s2 = temp;
        return;    /* Local variables s1,s2 and tmp are freed. The original variables are untouched. */
    }
    Code:
    ...
        goto johny_walker_red_label;
    johny_walker_blue_label: exit(-149$);
    johny_walker_red_label : exit( -22$);
    A typical example of ...cheap programming practices.

  15. #15
    Registered User
    Join Date
    Oct 2007
    Posts
    100
    putting thigs together, now it seems to work. Of course, this is just a simple implementation of Quicksort as advised to do by Deitel:

    Code:
    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #define MAX_STRLEN 256
    #define STRING_COUNT 5
    
    void quickSort(char * [],int,int);
    int partition(char *[],int , int );
    void swap (char **,char **);
    void printStrings(char * []);
    
    int main()
    {
    	char * strings[STRING_COUNT];
    	int i;
    	char buffer[MAX_STRLEN];
    	char *tmp;
    	
    	for (i=0;i<STRING_COUNT;i++)
    	{
    		
    		printf("Enter string #%d: ",i);
    		fflush(stdout);
    
    		if (!fgets (buffer, sizeof(buffer),stdin))
    		{
    			printf("FGETS ERROR! Program terminated.\n");
    			exit(1);
    		}
    
    		if (tmp=strchr(buffer,'\n'))
    		{
    			*tmp='\0';
    		}
    		
    		strings[i]=malloc(strlen(buffer)+1);
    		
    		if (!strings[i]) 
    		{
    			printf("Out of memory\n");
    			exit(1);
    		}
    		
    		strcpy(strings[i],buffer);
    
    	}
    	quickSort(strings,0,STRING_COUNT-1);
    	printStrings(strings);
    }
    
    int partition (char * str[],int lb, int ub)
    {
    	int a=lb,b=ub;
    
    	while(a<b)
    	{
    		while ((a<b)&&(strcmp(str[a],str[b])<=0))
    		{
    			b--;
    		}
    
    		if (a<b)
    		{
    			swap(&str[a],&str[b]);
    			a++;
    		}
    	
    		while ((a<b)&&(strcmp(str[a],str[b])<=0)) 
    		{
    			a++;
    		}
    
    		if (a<b)
    		{
    			swap(&str[b],&str[a]);
    			b--;
    		}
    	}
    
    	return a;
    }
    
    void quickSort(char * str[],int lb, int ub)
    {
     if (lb>=ub) return;
     else
     {
    	int newPosition;
    	newPosition=partition(str,lb,ub);
    	quickSort(str,lb,newPosition-1);
    	quickSort(str,newPosition+1,ub);
    	
     }
    }
    
    void swap (char ** a, char ** b)
    {
     char * tmp = *a;
     *a=*b;
     *b=tmp;
    }
    
    void printStrings(char * strings[])
    {
    	int i;
    	
    	for (i=0;i<STRING_COUNT;i++)
    	{
    		printf("%d: %s\n",i,strings[i]);
    	}
    }

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Need Help understanding arrays and pointers
    By flicka in forum C++ Programming
    Replies: 12
    Last Post: 10-04-2005, 09:45 PM
  2. Replies: 2
    Last Post: 02-23-2004, 06:34 AM
  3. working with strings arrays and pointers
    By Nutka in forum C Programming
    Replies: 4
    Last Post: 10-30-2002, 08:32 PM
  4. strings or character arrays
    By Shadow12345 in forum C++ Programming
    Replies: 2
    Last Post: 07-21-2002, 10:55 AM
  5. Searching arrays for strings
    By Zaarin in forum C++ Programming
    Replies: 14
    Last Post: 09-03-2001, 06:13 PM