Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

incrementing pointers to multi-dimensional array in C

Tags:

c

pointers

I have this small program:

#include <stdio.h>

int main() {
    char *argv[3] = {{"abc"}, {"def"}, {"ghi"}};

    printf("%c\n", (*++argv)[1]); //error. I wanted to print 'b'
    printf("%c\n", *++argv[1]); //prints e

    return 0;
}

The error message:

error: cannot increment value of type 'char *[3]'
printf("%c\n", (*++argv)[1]);

I want to increment argv to point to b. I took this usage (*++argv)[i] straight from C Programming Language by K&R where they have an example on page 117 on incrementing argv exactly like I did. They also point out that (*++argv)[0] is the pointer to the first character in a string while *++argv[0] increments the pointer argv[0]. Indeed (*argv)[1] will print b while *argv[1] will print d. Yet somehow incrementing (*++argv)[0] just keep resulting in error.

like image 672
Yos Avatar asked Mar 11 '23 11:03

Yos


2 Answers

First, this:

char *argv[3] = {{"abc"},{"def"},{"ghi"}};

Should be this, as noted by others:

char *argv[3] = {"abc", "def", "ghi"};

Second, what you've done is not what the K&R book does.

Array names act like they were constant pointers, so you can't change them like you tried to with that argv = argv + 1 (expanded version of ++argv).

But the book does that to an argv passed into main by the command line, so when char* argv[] enters main, it decays to a pointer to pointer (char**) and then, yes, you could make (*++argv)[1] work if you pass the strings as command line arguments.

Try to create a bogus argv inside main, like you did, and compare its related addresses with the addresses related to a real argv passed into main via command line.

You'll see that one is a char** and the other is a char* array:

#include <stdio.h>

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

    char* bogus_argv[] = {"abc", "def", "ghi"};
    printf("bogus_argv:     %p\n", bogus_argv);
    printf("&bogus_argv[0]: %p\n", &bogus_argv[0]);
    printf("&bogus_argv:    %p\n\n", &bogus_argv);

    printf("%c\n", (*++argv)[1]);  // prints 'b'
    printf("%c\n", *++argv[1]);    // prints 'e'

    return 0;
}

Run: ./program abc def ghi

Output on my machine:

argv:     0x7ffcde5aca98
&argv[0]: 0x7ffcde5aca98
&argv:    0x7ffcde5ac9a0

bogus_argv:     0x7ffcde5ac980
&bogus_argv[0]: 0x7ffcde5ac980
&bogus_argv:    0x7ffcde5ac980

b
e
like image 75
yLaguardia Avatar answered Mar 19 '23 20:03

yLaguardia


The example from K&R refers to argv as defined as the second argument to function main. Your definition is different, you define an array of 3 pointers to strings whereas the function argument is a pointer to such an array.

The function prototype syntax is somewhat misleading as the same syntax is used for a different type of object. Some people prefer this syntax:

int main(int argc, char **argv) {
    ...
}

With this prototype, the nature of argv (a pointer to a pointer to char) appears more explicitly, but many programmers prefer the equivalent char *argv[] syntax to underline the fact that argv points to an array of pointers as opposed to a single pointer.

argv being an array in your code, you cannot increment it: this explains why you get an error for printf("%c\n", (*++argv)[1]);.

You can change your program this way:

#include <stdio.h>

int main(void) {
    char *argument_array[4] = { "abc", "def", "ghi", NULL };
    char **argv = argument_array;

    printf("%c\n", (*++argv)[1]); // will print 'e' instead of 'b'
    printf("%c\n", *++argv[1]);   // prints 'h', not 'e'

    return 0;
}

Also note that you should remove the redundant {} in the initializer, and the ++ operator is prefix, so the output is not exactly what you expect.

like image 24
chqrlie Avatar answered Mar 19 '23 19:03

chqrlie