Thread: Question about Structs

  1. #1
    Registered User
    Join Date
    Jan 2011
    Posts
    41

    Question about Structs

    Hi,

    I am working on a project where I need to use a structure. Here is the layout of the structure and the type I defined for it.


    Code:
    struct PARAM
       {
          char *InputRedirect; /* file name or NULL */
          char *OutputRedirect; /* file name or NULL */
          int Background; /* either 0 (false) or 1 (true) */
          int ArgumentCount; /* same as argc in main() */
          char *ArgumentVector[MAXARGS]; /* array of strings */
       };
    	
    	/* define a struct type named Param_t */
       typedef struct PARAM Param_t;
    
       /* allocate space for the struct */
       Param_t *p = (Param_t *) malloc(sizeof(Param_t));
    The program I am working on is supposed to prompt the user for input, read and tokenize their input and store data in the structure, the user must enter "exit" to terminate the program. At the end I have to print the structure: so on an input
    "Hello My Name is Hunter"
    I should see:
    ArgumentVector[0] = Hello
    ArgumentVector[1] = My
    ArgumentVector[2] = Name
    ArgumentVector[3] = is
    ArgumentVector[4] = Hunter

    but for some reason the "exit" string i type to terminate the program is overwriting ArgumentVector[0].

    I am new to C (coming from Java) Any help would be great?

    Thanks,
    Hunter

  2. #2
    The Dragon Reborn
    Join Date
    Nov 2009
    Location
    Dublin, Ireland
    Posts
    629
    can you post the rest of the code?

    might make it clearer
    You ended that sentence with a preposition...Bastard!

  3. #3
    -bleh-
    Join Date
    Aug 2010
    Location
    somewhere in this universe
    Posts
    463
    The problem may lies with how you use the loop to write innto ArgumentVector. There is nothing wrong with what you just posted, well, except for casting malloc. Mind posting the rest of the code?
    "All that we see or seem
    Is but a dream within a dream." - Poe

  4. #4
    Registered User
    Join Date
    Jan 2011
    Posts
    41
    This is the code in it's entirety, also please don't post a solution this is for an assignment, just a point the right direction is all im looking for.

    Code:
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    #define MAXARGS 32 /* maximum # of arguments to the program*/
    
    /* Structure to hold input data */
       struct PARAM
       {
          char *InputRedirect; /* file name or NULL */
          char *OutputRedirect; /* file name or NULL */
          int Background; /* either 0 (false) or 1 (true) */
          int ArgumentCount; /* same as argc in main() */
          char *ArgumentVector[MAXARGS]; /* array of strings */
       };
    	
    	/* define a struct named Param_t */
       typedef struct PARAM Param_t;
    	
    /**
     * Entry point into the program
     * argc     - # of arguments
     * *argv[]  - the arguments
     */
       int main(int argc, char *argv[])
       {
          const char DELIMITERS[] =" \n\t";
          const char EXIT[] = "exit";
       	
          char input[MAXARGS];
          char *background;
          char *inputRedirect;
          char *outputRedirect;
          char *tokens; 
          int index = 0; /* index into the argument vector, initialized at 0 */
    		
    		/* allocate space for the struct */
          Param_t *p = (Param_t *) malloc(sizeof(Param_t));
       	
       	/* prompt the user */
          printf("Enter input using the keyboard, when you wish to stop type exit:\n\n$$$ ");
          fgets(input, MAXARGS, stdin); /* read from the terminal */
       
       	/* while you haven't read the word "exit", keep reading and tokenizing */
          while( strncmp(input, EXIT, strlen(EXIT)) != 0 ) /* only compares the first 4 characters to avoid whitespace */
          {
             printf("input: %s\n\n", input);
             tokens = strtok(input, DELIMITERS);
             while(tokens != NULL)
             {
                
                background     = strchr(tokens, '&');
                inputRedirect  = strchr(tokens, '<');
                outputRedirect = strchr(tokens, '>');
             	
             	/* copy argc into the structure */
                p -> ArgumentCount = argc;
             	
             	/* special cases */
                if(inputRedirect != NULL) 
                   p -> InputRedirect = tokens; 
                else	
                   p -> InputRedirect = NULL;
             		
                if(outputRedirect != NULL) 
                   p -> OutputRedirect = tokens; 
                else	
                   p -> OutputRedirect = NULL;
             		
                if(background != NULL) 
                   p -> Background = 1;      
                else	
                   p -> Background = 0;      
             	
             	/* normal case */
                if(inputRedirect == NULL && outputRedirect == NULL)
                {
    					printf("%s\n", tokens);
                   p -> ArgumentVector[index++] = tokens;
                }
             	
             	/* read next token */
                tokens = strtok(NULL, DELIMITERS);
             }
             printf("\n$$$ "); /* prompt line */
             fgets(input, MAXARGS, stdin); /* read from the terminal */
          }
       	
       	/* print structure */
          
    		int j;
    		for(j = 0; j < 5; j++)
    		{
    			printf("ArgumentVector[%2d]: [%s]\n", j, p->ArgumentVector[j]);
    		}
          return 0;
       }
    Thanks,
    Hunter

  5. #5
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    You only allocate one instance of a Param_t structure, and every command is stored in there, overwriting the previous one. If you need to keep them all, you will need an array or linked list. Also, that last for loop iterates through 5 elements of p->ArgumentVector regardless of what's in there. Thus, your "exit" command has only one word and overwrites only the first word of ArgumentVector. The other 4 words, "my name is Hunter" remain in ArgumentVector. Try only looping while i < p->ArgumentCount.

  6. #6
    Registered User
    Join Date
    Jan 2011
    Posts
    41
    Why would I need to allocate more than one instance of Param_t? I am only using one structure to hold the data. The last for loop is merely there to show what is in the array.

    I know this probably seems simple, but I'm new to needed to allocate my own memory.

    Thanks,
    Hunter

  7. #7
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    I would definitely say that memory management in C is one of the tougher concepts for many people, so don't despair.

    I suggested you might need to allocate multiple instances of a Param_t struct only if you wanted to keep a history of many/all commands typed by the user. It was just a thought, since you mentioned that the "exit" command overwrote the previous command, implying that you wanted to retain the old command and the new one. Perhaps one Param_t is enough, in which case you don't actually need to allocate memory for the object since you only use it in the main function. You could simply declare it and access it like so:
    Code:
    Param_t foo;
    strcpy(foo.ArgumentVector[0], "bar");
    If you do decide to stick with your current method of allocating the memory yourself, you need to free() it when you're done. In C it's your responsibility to do both.

    For starters, decide whether you need to store only the most recent command or multiple commands. That should help you determine potential memory needs/usage.

  8. #8
    Registered User
    Join Date
    Jan 2011
    Posts
    41
    Well I had read that when I am using a pointer to a struct I should use "->" to access members of the struct.

    My main question is: what would cause ArgumentVector[0] to have "exit" inside it instead of "Hello".

    So on my input: "Hello My Name is Hunter"

    I am getting:
    ArgumentVector[0] = exit
    ArgumentVector[1] = My
    ArgumentVector[2] = Name
    ArgumentVector[3] = is
    ArgumentVector[4] = Hunter

    instead of this: (this is correct output)
    ArgumentVector[0] = Hello
    ArgumentVector[1] = My
    ArgumentVector[2] = Name
    ArgumentVector[3] = is
    ArgumentVector[4] = Hunter


    Am I not allocating enough space for the structure?

    Hunter

  9. #9
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by mcmillhj View Post
    Well I had read that when I am using a pointer to a struct I should use "->" to access members of the struct.
    Yes, that's correct, if you have a pointer to a struct, as you do in your example. In my example, I didn't have a pointer to a struct, I simply had a struct so I used the '.' member accessor. Notice the use of a * in your declaration that isn't in mine. That makes your p a pointer and mine an object. This can be a difficult thing for a Java person to understand, since Java hides all this pointer and reference count business behind the scenes. Note also, there is no reference counting in C so things don't magically deallocate when you're done.

    My main question is: what would cause ArgumentVector[0] to have "exit" inside it instead of "Hello".

    So on my input: "Hello My Name is Hunter"

    I am getting:
    ArgumentVector[0] = exit
    ArgumentVector[1] = My
    ArgumentVector[2] = Name
    ArgumentVector[3] = is
    ArgumentVector[4] = Hunter

    instead of this: (this is correct output)
    ArgumentVector[0] = Hello
    ArgumentVector[1] = My
    ArgumentVector[2] = Name
    ArgumentVector[3] = is
    ArgumentVector[4] = Hunter


    Am I not allocating enough space for the structure?

    Hunter
    You allocated space for one Param_t structure. You read each command into this, overwriting information from the last command. Each time you get a line of input and tokenize it, you reset i to 0, so you're overwriting the 0th, 1st, etc elements of ArgumentVector in the one and only struct you allocated. There is no magic "new" that happens when you get the next line of input. Then you only print out the struct at the end of the program, after you've read all the commands. Thus, it has some weird amalgamation of previous commands in there. Move that printing loop inside the main while loop to have it print out each command:
    Code:
    while( strncmp(input, EXIT, strlen(EXIT)) != 0 ) /* only compares the first 4 characters to avoid whitespace */
    {
        printf("input: %s\n\n", input);
        tokens = strtok(input, DELIMITERS);
        while(tokens != NULL)
        {
            ....
        }
        ...
        int j;
        for(j = 0; j < p->ArgumentCount; j++)
        {
            printf("ArgumentVector[%2d]: [%s]\n", j, p->ArgumentVector[j]);
        }
    }

  10. #10
    and the Hat of Guessing tabstop's Avatar
    Join Date
    Nov 2007
    Posts
    14,336
    inputRedirect and outputRedirect point into the input buffer. When you do another fgets and get more data, say goodbye to your old data. For instance, if on the next line you type "exit xx xxxx xx xxxxxx", see what you get.

    You probably want to malloc space for your inputRedirect things to point to, and then copy the string into the new space, rather than rely on the buffer never changing.

  11. #11
    Registered User
    Join Date
    Jan 2011
    Posts
    41
    Sorry Im getting pretty confused here.

    In response to your post Anduril, the index is set to 0 in the main function, which only gets called once, and it only increments when I am adding to the ArgumentVector array. Index is never reset, Can you explain how this is overwriting the previous data?

    if you don't mind could you run it with this input:
    "Hello My Name is Hunter"
    then type:
    "exit"

    and see if you can tell what I mean.

    Thanks,
    Hunter

  12. #12
    Registered User
    Join Date
    Nov 2010
    Location
    Long Beach, CA
    Posts
    5,909
    Quote Originally Posted by mcmillhj View Post
    Sorry Im getting pretty confused here.

    In response to your post Anduril, the index is set to 0 in the main function, which only gets called once, and it only increments when I am adding to the ArgumentVector array. Index is never reset, Can you explain how this is overwriting the previous data?
    You're right. Apparently, I just imagined you doing something totally different. I gues I'll remove my head from my....

    if you don't mind could you run it with this input:
    "Hello My Name is Hunter"
    then type:
    "exit"

    and see if you can tell what I mean.

    Thanks,
    Hunter
    I have, and I know exactly what you mean. Perhaps tabstop's suggestion of using "exit xx xxxx ..." will clarify a bit, but if not:

    In C, when you assign a pointer you are only copying the address of an item. It's a very shallow copy, compared to what Java does. When you say
    "p->ArgumentVector[index++] = tokens", you are pointing the index'th element of ArgumentVector to whatever address is in tokens (some place in your input buffer). They do not contain or refer to copies of each word in your input buffer, but point into the input array itself. When index is 0, on your first command, strtok(input, DELIMITERS) returns the beginning of input, where it found "Hello". You assign this to ArgumentVector[0] and move to the next words, putting them in ArgumentVector[1], etc. All of these pointers will be somewhere in input, since that is the string you are tokenizing, and strtok works on that string, not some copy. It actually replaces any characters it found in DELIMITER with a null character and gives you a pointer to the start of that word. When you're done processing your first command, you have something like:
    Code:
    "Hello\0my\0name\0is\0hunter\0"
     ^      ^   ^     ^   ^
     |      |   |     |   +ArgumentVector[4]
     |      |   |     +ArgumentVector[3]
     |      |   +ArgumentVector[2]
     |      +ArgumentVector[1]
     +ArgumentVector[0]
    Then, when you read your second command, you overwrite the beginning of input, so you have the following:

    Code:
    "exit\n\0my\0name\0is\0hunter"
     ^       ^   ^     ^   ^
     |       |   |     |   +ArgumentVector[4]
     |       |   |     +ArgumentVector[3]
     |       |   +ArgumentVector[2]
     |       +ArgumentVector[1]
     +ArgumentVector[0]
    Hope that clears things up a bit.

  13. #13
    Registered User
    Join Date
    Jan 2011
    Posts
    41
    Ok now I see. I wasn't really thinking that the input stream was still around after the first command. I need to get my mind out of Java-mode. Thank you very much for your help and patience, I think I see what I need to do now.

    Hunter

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. another do while question
    By kbpsu in forum C++ Programming
    Replies: 3
    Last Post: 03-23-2009, 12:14 PM
  2. Structs question
    By BigFish21 in forum C Programming
    Replies: 25
    Last Post: 04-23-2008, 09:57 PM
  3. Replies: 5
    Last Post: 02-20-2004, 09:36 AM
  4. Question...
    By TechWins in forum A Brief History of Cprogramming.com
    Replies: 16
    Last Post: 07-28-2003, 09:47 PM
  5. opengl DC question
    By SAMSAM in forum Game Programming
    Replies: 6
    Last Post: 02-26-2003, 09:22 PM