1

I have argv[] defined as a char *. Using the following printf statements:

     printf("%s\n",argv[1]);   // prints out the entire string
     printf("%p\n",&argv[1]);  // & -> gets the address
     printf("%c\n",argv[1][0]);// prints out the first char of second var
     printf("%c\n",*argv[1]);  //  

It's this last one I don't understand. What does it mean to print *argv[1]? why isn't that the same as *argv[1][0] and how come you can't print out printf("%s\n",*argv[1]);. Also, why is &*argv[1] a different address then &argv[1]?

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
DCR
  • 14,737
  • 12
  • 52
  • 115
  • It might help you to read the [`char*` tag page](http://stackoverflow.com/tags/char-pointer/info). Perhaps also [this question](http://stackoverflow.com/questions/3024197/what-does-int-argc-char-argv-mean) regarding `argc` and `argv`. – einpoklum Jan 15 '17 at 18:03
  • 1
    It is not problem with understanding char* but pointers – Mateusz Wojtczak Jan 15 '17 at 18:04
  • @user3655463: Seeing how OP wanted to dereference `argv[1][0]`, I disagree. – einpoklum Jan 15 '17 at 18:07

5 Answers5

4
char *argv[]

argv is array(1) of char pointers. So it is normal array just each element of the array is a pointer. argv[0] is a pointer, argv[1], etc.

argv[0] - first element in the array. Since each element in the array is char pointer, value of this is also a char pointer (as we already mentioned above).

*argv[1] - Now here argv[1] is second element in the above array, but argv[1] is also a char pointer. Applying * just dereferences the pointer and you get the first character in the string to which argv[1] points to. You should use %c to print it as this is just a character.

argv[1][0] is already first character of second string in the array - so no more room for dereferencing. This is essentially same as previous.


(1) as highlighted strictly saying it is pointer to pointer, but maybe you can "think" of it as array of pointers. Anyway more info about it here: https://stackoverflow.com/a/39096006/3963067

Community
  • 1
  • 1
Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
4

The array subscript operation a[i] is defined as *(a + i) - given the address a, offset i elements (not bytes) from that address and dereference the result. Thus, given a pointer p, *p is equivalent to *(p + 0), which is equivalent to p[0].

The type of argv is char **; given that, all of the following are true:

    Expression         Type            Value
    ----------         ----            -----
          argv         char **         Pointer to a sequence of strings
         *argv         char *          Equivalent to argv[0]
        **argv         char            Equivalent to argv[0][0]
       argv[i]         char *          Pointer to a single string
      *argv[i]         char            Same as argv[i][0]
    argv[i][j]         char            j'th character of i'th string
      &argv[i]         char **         Address of the pointer to the i'th string

Since the type of argv[i][j] is char, *argv[i][j] is not a valid expression.

Here's a bad visualization of the argv sequence:

     +---+              +---+                                         +---+
argv |   | ---> argv[0] |   | ---------------------------> argv[0][0] |   |
     +---+              +---+                     +---+               +---+
                argv[1] |   | -------> argv[1][0] |   |    argv[0][1] |   |
                        +---+                     +---+               +---+
                         ...           argv[1][1] |   |                ...
                        +---+                     +---+               +---+
             argv[argc] |   | ---|||               ...   argv[0][n-1] |   |
                        +---+                     +---+               +---+
                                     argv[1][m-1] |   |
                                                  +---+

This may help explain the results of different expressions.

John Bode
  • 119,563
  • 19
  • 122
  • 198
3

If argv[1] is a pointer to char, then *argv[1] dereferences that pointer and gets you the first character of the string at argv[1], so it's the same as argv[1][0] and is printed with the "%c" format specifier.

argv[1][0] is a char itself, not a pointer, so it's not dereferensable.

ForceBru
  • 43,482
  • 10
  • 63
  • 98
  • BTW, I'm not quite sure what's the correct spelling for "dereferensable" as "dereferencable" looks ugly and is underlined as incorrect by macOS autocorrect, but "dereferensable" feels better, although is underlined as well. Does anyone know what spelling is correct? – ForceBru Jan 15 '17 at 18:07
2

The last line printf("%c\n",*argv[1]); is both dereferencing argv and accessing array index 1. In other words, this is doing argv[1][0], just like the previous line, because the array subscript access [1] has a higher precedence than the dereference operator (*).

However, if you were to parenthesize the expression in the last line to make the dereference operator be processed first, you would do this:

printf("%c\n", (*argv)[1]);

Now, when you run the program, the last line of output would be argv[0][1] instead of [1][0], i.e. the second character in the command line you use to execute the program.

Govind Parmar
  • 20,656
  • 7
  • 53
  • 85
2
  1. This is not specific to char *.
  2. You can simplify by what is the difference between *ptr and ptr[0].
  3. There are no difference because ptr[0] is a sugar for *(ptr + 0) or *ptr because + 0 is useless.

// printf("%p\n", &argv[1]); is wrong you must cast to (void *)
printf("%p\n", (void *)&argv[1]);

Because %p specifier expect a void *, in normal case C auto promote your pointer into void * but printf() use variable argument list. There are a lot of rule about this, I let you read the doc if you want. But char * wiil not be promote to void * and like I say printf() except void * so you have a undefined behavior if you don't cast it yourself.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
  • @GovindParmar The cast is needed to insure a compatible pointer type. "**p** The argument shall be a pointer to void. " C11dr §7.21.6.1 8 – chux - Reinstate Monica Jan 15 '17 at 18:12
  • I get the same address whether I use (void *)&argv[1] or &argv[1] – DCR Jan 15 '17 at 18:16
  • @DCR Yes, this is normal, I never use a system where pointer has different size but it's possible in C. For example, some system have `sizeof (int *) != sizeof (void *). It's just a note but it's you want to write proper and portable C is important to know that you must cast it. – Stargateur Jan 15 '17 at 18:20