0

I'm looking at this small program that creates a shell. parse() takes a character pointer line and an array of character pointers argv, saving the address of each word in argv.

void  parse(char *line, char **argv) {
 while (*line != '\0') {       /* if not the end of line ....... */
      while (*line == ' ' || *line == '\t' || *line == '\n')
           *line++ = '\0';     /* replace white spaces with 0    */
      *argv++ = line;          /* save the argument position     */
      printf("%p\n",*argv);
      while (*line != '\0' && *line != ' ' &&
             *line != '\t' && *line != '\n')
           line++;             /* skip the argument until ...    */
 }
 *argv = '\0';                 /* mark the end of argument list  */
}

What I don't understand is that argv is somehow back at the first word after the function exits. The main function calls:

parse(line, argv);       /*   parse the line               */
      if (strcmp(argv[0], "exit") == 0)  /* is it an "exit"?     */
           exit(0);

How is argv[0] at the beginning of line when it was at the end of line right before?

5
  • 3
    *argv = '\0'; , argv is a pointer to string not a string, do you mean *argv = NULL? Commented Apr 23, 2016 at 5:31
  • 2
    See Minimal Complete Verifiable Example. Commented Apr 23, 2016 at 5:39
  • This question is related to stackoverflow.com/q/25769443/694576 if not a duplicate to the latter. Commented Apr 23, 2016 at 10:27
  • @AlterMann probably, although OP code does work because '\0' is identical to 0 (i.e. an int with value zero) Commented Apr 23, 2016 at 12:53
  • @M.M, yes, NULL and 0 are interchangeable in this context. Commented Apr 23, 2016 at 14:49

2 Answers 2

2

The reason is that parameters are passed by value in C. Think of it this way:

void func(int a)
{
     a = 20;
}

int main()
{
     int val = 10;
     printf("%d", val);
     func(val);
     printf("%d", val);
}

In both the prints above, the value printed out would be 10. If you want to modify val in func then you would have to pass its address. Same is true with pointers. Pointers are passed-by-value just like any other parameter. Thus, if you have something like this:

#include <stdio.h>

void func(char **argv)
{
  while (*argv != 0) {
    printf("%s\n", *argv);
    argv++;
  } 
  printf("*****done*****\n");
}

int main(int argc, char **argv)
{
  func(argv);
  printf("%s\n", argv[0]);
}

And you run it with:

gcc a.c
./a.out hello how are you

Output would be:

./a.out
hello
how
are
you
*****done*****
./a.out

If you want argv to point to the last parameter when it returns back from the function, then you would have to pass its address:

#include <stdio.h>

void func(char ***argv)
{
  while (**argv != 0) {
    printf("%s\n", **argv);
    (*argv)++;
  } 
  (*argv)--;
  printf("*****done*****\n");
}

int main(int argc, char **argv)
{
  func(&argv);
  printf("%s\n", argv[0]);
}

And now the output would be:

./a.out
hello
how
are
you
*****done*****
you
Sign up to request clarification or add additional context in comments.

Comments

2

If I understand where the confusion is, when you call void parse(char *line, char **argv) sending the line and argv pointers to the function, the function receives a COPY of each pointer with each copy having an address all its own.

So regardless what your do to the pointer itself within the function (e.g. iterate over the argv by argv++, argv++, those changes are never seen in main() because the increment is only happening to the copy of the argument vector and the original pointer back in main has no clue about any of it.

Now if you change the contents of any of the individual strings in argv, those changes will be seen back in main, but any other non-content modifying changes are local to your function only.

The only way you can change the pointer in your function is to pass the address-of your pointer to the function. That would give you a prototype of void parse(char **line, char ***argv) Then you can change the pointer iself in your function and have those changes visible in main (e.g. char *tmp = (*argv)[0]; (*argv)[0] = (*argv)[1]; (*argv)[1] = tmp;)

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.