Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Iterate through argv[]

Tags:

arrays

c

pointers

I'm new to C and I completed a small exercise that iterates through the letters in an argument passed to it and identifies the vowels. The initial code only worked for one argument (argv[1]). I want to expand it to be able to iterate through all arguments in argv[] and repeat the same process of identifying vowels.

The code:

#include <stdio.h>

int main(int argc, char *argv[])
{
    if (argc < 2) {
        printf("ERROR: You need at least one argument.\n");
        return 1;
    }

    if (argc == 2) {
        int i = 0;
        for (i = 0; argv[1][i] != '\0'; i++) {
            char letter = argv[1][i];

            if (letter == 'A' || letter == 'a') {
                printf("%d: 'A'\n", i);
                //so on
            }
        }
    } else {
        int i = 0;
        int t = 2;
        for (t = 2; argv[t] != '\0'; t++) {
            for (i = 0; argv[t][i] != '\0'; i++) {
                char letter = argv[t][i];

                if //check for vowel
            }

        }

        return 0;
    }
}

I read this answer and it seems the best solution is to use pointers, a concept I'm still a bit shaky with. I was hoping someone could use the context of this question to help me understand pointers better (by explaining how using pointers in this instance solves the problem at hand). Many thanks in advance.

like image 511
J. Behnken Avatar asked Jan 19 '18 02:01

J. Behnken


2 Answers

I was hoping someone could use the context of this question to help me understand pointers better....

In context of your program:

int main(int argc, char *argv[])

First, understand what is argc and argv here.

argc(argument count): is the number of arguments passed into the program from the command line, including the name of the program.

argv(argument vector): An array of character pointers pointing to the string arguments passed.

A couple of points about argv:

  • The string pointed to by argv[0] represents the program name.

  • argv[argc] is a null pointer.

For better understanding, let's consider an example:

Say you are passing some command line arguments to a program -

# test have a nice day

test is the name of the executable file and have, a, nice and day are arguments passed to it and in this case, the argument count (argc) will be 5.

The in-memory view of the argument vector (argv) will be something like this:

      argv                       --
        +----+    +-+-+-+-+--+     |
 argv[0]|    |--->|t|e|s|t|\0|     |
        |    |    +-+-+-+-+--+     |
        +----+    +-+-+-+-+--+     |
 argv[1]|    |--->|h|a|v|e|\0|     |
        |    |    +-+-+-+-+--+     |
        +----+    +-+--+           |
 argv[2]|    |--->|a|\0|            > Null terminated char array (string)
        |    |    +-+--+           |
        +----+    +-+-+-+-+--+     |
 argv[3]|    |--->|n|i|c|e|\0|     |
        |    |    +-+-+-+-+--+     |
        +----+    +-+-+-+--+       |
 argv[4]|    |--->|d|a|y|\0|       |
        |    |    +-+-+-+--+       |
        +----+                   --
 argv[5]|NULL|
        |    |
        +----+

A point to note about string (null-terminated character array) that it decays into pointer which is assigned to the type char*.

Since argv (argument vector) is an array of pointers pointing to string arguments passed. So,

argv+0 --> will give address of first element of array.
argv+1 --> will give address of second element of array.
...
...
and so on.

We can also get the address of the first element of the array like this - &argv[0].

That means:

argv+0 and &argv[0] are same.

Similarly,

argv+1 and &argv[1] are same.
argv+2 and &argv[2] are same.
...
...
and so on.

When you dereference them, you will get the string they are pointing to:

*(argv+0) --> "test"
*(argv+1) --> "have"
....
....
and so on.

Similarly,

*(&argv[0]) --> "test"

*(&argv[0]) can also written as argv[0].

which means:

*(argv+0) can also written as argv[0]. 

So,

*(argv+0) and argv[0] are same
*(argv+1) and argv[1] are same
...
...
and so on.

When printing them:

printf ("%s", argv[0]);   //---> print "test"
printf ("%s", *(argv+0)); //---> print "test"
printf ("%s", argv[3]);   //---> print "nice"
printf ("%s", *(argv+3)); //---> print "nice"

And since the last element of argument vector is NULL, when we access - argv[argc] we get NULL.

To access characters of a string:

argv[1] is a string --> "have"
argv[1][0] represents first character of string --> 'h'

As we have already seen:
argv[1] is same as *(argv+1)

So,
argv[1][0] is same as *(*(argv+1)+0)

To access the second character of string "have", you can use:

argv[1][1] --> 'a'
or,
*(*(argv+1)+1) --> 'a'

I hope this will help you out in understanding pointers better in context of your question.

To identify the vowels in arguments passed to program, you can do:

#include <stdio.h>

int main(int argc, char *argv[])
{

    if (argc < 2) {
            printf("ERROR: You need at least one argument.\n");
            return -1;
    }

    for (char **pargv = argv+1; *pargv != argv[argc]; pargv++) {
            /* Explaination:
             * Initialization -
             * char **pargv = argv+1; --> pargv pointer pointing second element of argv
             *                            The first element of argument vector is program name
             * Condition -
             * *pargv != argv[argc]; --> *pargv iterate to argv array
             *                            argv[argc] represents NULL
             *                            So, the condition is *pargv != NULL
             *                            This condition (*pargv != argv[argc]) is for your understanding
             *                            If using only *pragv is also okay 
             * Loop iterator increment -
             * pargv++
             */

            printf ("Vowels in string \"%s\" : ", *pargv);
            for (char *ptr = *pargv; *ptr != '\0'; ptr++) {
                    if (*ptr == 'a' || *ptr == 'e' || *ptr == 'i' || *ptr == 'o' || *ptr == 'u'
                    ||  *ptr == 'A' || *ptr == 'E' || *ptr == 'I' || *ptr == 'O' || *ptr == 'U') {
                            printf ("%c ", *ptr);
                    }
            }
            printf ("\n");
    }

    return 0;
}

Output:

#./a.out have a nice day
Vowels in string "have" : a e 
Vowels in string "a" : a 
Vowels in string "nice" : i e 
Vowels in string "day" : a
like image 184
H.S. Avatar answered Sep 30 '22 13:09

H.S.


You can use nested for loops to loop through all arguments. argc will tell you number of arguments, while argv contains the array of arrays. I also used strlen() function from strings library. It will tell you how long a string is. This way you can check for any number of arguments. Your if statement can also be changed to just 2, either argc is less than 2 or more than.

#include <stdio.h>
#include <string.h>

int main(int argc, char *argv[]) {
    if (argc < 2) {
        printf("ERROR: You need at least one argument.\n");
        return 1;
    } else {
        int i, x;
        int ch = 0;
        for (i=1; i<argc; i++) {
            for (x = 0; x < strlen(argv[i]); x++) {
                ch = argv[i][x];
                if (ch == 'A' || ch == 'a' || ch == 'e')
                    printf('Vowel\n');
            }
        }
    }
}

Python Equivalent for the nested loop

for i in range (0, argc):
    for x in range(0, len(argv[i])):
        ch = argv[i][x];
        if ch in ('A', 'a', 'e'):
            print('Vowel')
like image 43
thuyein Avatar answered Sep 30 '22 12:09

thuyein