1

Been searching through here but please forgive me if this post is a duplicate... I just want to clarify things wherein I am not sure of...

First of all, let me give an introduction to what I have in mind:

  • The user will be asked to input a command (similar to the command line/shell commands).
  • The command will be received/inputted as a string separated by white spaces.
  • Next the string will be tokenized (split), the number of words will be counted.
  • With that number of words at hand, I will have to create a dynamic multi-dimensional array of strings, where the size/number of strings are the number of words we counted.

Example:

I Name 1234123 123123

The number of words/strings inside that command is 4, so the number of string pointers will be dynamically allocated.

I want this strings to be stored in a dynamic multi-dimensional array where:

char ** arguments;
int count;

I want to access the values inside the arguments array as:

for(i = 0; i < count; i++)
    printf("%s", arguments[i]);

MY PROBLEM IS

When I try to access these variables in my main function, the values inside the arguments variable are garbage and the count variable continues to increment, knowing that I have initialized the count variable back to 0.

You can check on my code if I have done something vague or wrong. Thanks!


Here are the codes that I was able to create where in which I think are useful:

/* This function counts the number of words inside the command input. */
int word_count(char * str)
{
    int i, ctr;

    for(ctr = i = 0; str[i] != '\0'; i++)
    {
        while(isspace(str[i]))
            i++;

        if(str[i] != '\0')
        {
            ctr++;

            while(str[i] != '\0' && ! isspace(str[i]))
                i++;
        }
    }

    return ctr;
}

/* This function splits the strings inside the command. */
void split_command(int count, char *** argv, char * command)
{
    char buffer[31];
    int i, j, k;

    *argv = (char **) malloc(sizeof(char *) * count);

    for(i = k = 0; command[i] != '\0'; i++)
    {
        while(isspace(command[i]))
            i++;

        if(command[i] != '\0')
        {
            for(j = 0 ;j < 30 && command[i] != '\0' && ! isspace(command[i]); j++, i++)
                buffer[j] = (j != 0) ? command[i] : toupper(command[i]);

            buffer[j] = '\0';

            if(strlen(buffer) > 0)
            {
                (*argv)[k] = (char *) malloc(sizeof(char) * (strlen(buffer) + 1));
                strcpy((*argv)[k], buffer);
                k++;
            }
        }
    }
}

/* This function will re-initialize the provided arguments and counters. */
void prepare_command(int * count, char *** argv)
{
    int i;

    for(i = 0; i < *count; i++)
    {
        (*argv)[i] = NULL;
        free((*argv)[i]);
    }

    *count = 0;
    free(*argv);
    *argv = NULL;
}

Main

void main(void)
{
    char ** arguments, * command;
    int count, i;
    boolean end = False; // Assume I created an enum for boolean {False, True}

    /* Some initialization here. */
    count = 0;
    arguments = NULL;

    do
    {
        gets(command);
        count = word_count(command); /* This will return the number of strings inside the command */
        split_command(count, &arguments, command); /* This will allocate an array. */

        /* When I try to display something from here, some values are garbage and some are from the previous allocation...  */
        for(i = 0; i < count; i++)
            printf("%s\n", arguments[i]);

        prepare_command(&count, &arguments);
    }while(!end);

}

PS: I know strtok and I just don't want to use it, and I don't have strtok_r inside any of my libraries here. So I created my own function to take care of something similar to that.

This might be a long post, but I would like you to help me guys... Thanks! Will have to edit this post if necessary. Thank you, hope you shed some light to me. :3 :)

arjayosma
  • 540
  • 3
  • 15
  • 3
    The compiler sees that you're casting the return value of `malloc()` and punishes you. –  Sep 29 '12 at 11:18
  • 1
    Your `prepare_command` will not free a memory block as you first assign NULL to the argv element and then you call free on that value – Serge Sep 29 '12 at 11:21
  • Compile with all warnings enabled and debug info -e.g. with `gcc -Wall -g` on Linux. Use a debugger like `gdb`. Use `valgrind` to catch memory leaks, etc. Use `strdup` to duplicate a string. – Basile Starynkevitch Sep 29 '12 at 11:25
  • 1
    Basing from what I know, malloc returns a void*, that's why you have to cast it into something similar to the L value...@H2CO3 – arjayosma Sep 29 '12 at 11:26
  • in the main function the `command` passed to the `gets` being uninitialized. – Serge Sep 29 '12 at 11:28
  • @aTo Basing from what I saw, this question is tagged "C" and not "C++". –  Sep 29 '12 at 11:28
  • @Serge Thanks for that, originally my code was in reverse the free comes first, then the assignment of the NULL value, thanks for pointing that out. – arjayosma Sep 29 '12 at 11:29
  • @aTo (If you didn't get my previous comment: in C, `void *` is implicitly compatible with any pointer type - [casting it is bad practice.](http://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc)) –  Sep 29 '12 at 11:31
  • @H2CO3 I guess it is a good practice to at least cast your pointers... – arjayosma Sep 29 '12 at 11:31
  • @H2CO3 Let me read on what you provided... Thanks mate! :) – arjayosma Sep 29 '12 at 11:32
  • Actually I would like to take my vote back on your comment about casting the pointer returned by malloc... just because the reason is to make it not make it similar but make the type of pointer exactly the same)) – Serge Sep 29 '12 at 11:32
  • @H2CO3 void* ... - it depends on a C standard – Serge Sep 29 '12 at 11:33
  • @Serge you too better read the question/answer I linked... –  Sep 29 '12 at 11:33
  • @H2CO3 For example, strict C99 mandates to make a type cast of void* – Serge Sep 29 '12 at 11:35
  • @H2CO3 Have you read this comment? "I think this is probably the most annoying advice you see here in SO. It's not that it is a bad advice. I find it annoying because it's almost completely harmless to do the cast, yet people are complaining about it all the time. The only decent argument I could find is "It can hide an error, if you forgot to include ", which simply won't happen if you use a decent compiler. I wish people were focusing on more important issues. – Karoly Horvath" It is really harmless to do a cast. It doesn't matter anyway, my previous program did work with a cast. – arjayosma Sep 29 '12 at 11:42
  • @aTo I primarly consider it harmful because typecasting is one of things that make code hard to read. I didn't mean to be offensive with this. –  Sep 29 '12 at 11:52
  • @H2CO3 I understand your point mate :) no worries with that... But it (casting) really helped me a lot in my previous programs... Thanks for that input anyway :) – arjayosma Sep 29 '12 at 12:13
  • @H2CO3 I did. Actually, I completely agree with you that void* must be compatible with any pointer. However, I do not completely reject the other point of view – Serge Sep 29 '12 at 15:06

1 Answers1

0

Here is a code snippet that I am using:

int parse_args(char** argv, char* data) {
    char c;
    int argc = 0;

    if (argv)
        *argv = data;

    int quoteopen = 0;
    int waitingnextarg = 1;
    for (;(c = *data); data++) {
        switch(c) {
            case '\n':
            case '\t':
            case ' ':
                if (!quoteopen) {
                    if (argv)
                        *data = 0;
                    waitingnextarg = 1;
                }
            break;
            case '"':
                if (argv)
                    *data = 0;
                if (quoteopen) {
                    waitingnextarg = 1;
                }
                quoteopen ^= 1;
            break;
            default:
                if (waitingnextarg) {
                    waitingnextarg = 0;
                    if (argv)
                        argv[argc] = data;
                    argc++;
                }
            break;
        }
    }

    return argc;
}

call with

int argc = parse_args(0, input);
char* argv[argc+1];
parse_args(argv, input);

Careful: changes the input string. This does not deal with any memory allocations, but only uses preallocated memory. If called with argv == NULL it only counts and returns argc, if called with a valid pointer argv it will change the input string and populate argv. If you need to preserve a copy of the input string call with

int argc = parse_args(0, input);
char input_copy[strlen(input) +1];
memcpy(input_copy, input, strlen(input) +1);
char* argv[argc+1];
parse_args(argv, input_copy);
Sergey L.
  • 21,822
  • 5
  • 49
  • 75