2017-01-15 9 views
1

У меня есть argv [], определяемый как char *. Использование следующих отчетов:Понимание операторов разыменования, адресов и операторов массива в C

 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]); // 

Это последний, который я не понимаю. Что означает печать *argv[1]? почему это не так, как *argv[1][0], и почему вы не можете распечатать printf("%s\n",*argv[1]);. Кроме того, почему &*argv[1] другой адрес, то &argv[1]?

+0

Это может помочь вам прочитать страницу тега ['char *'] (http://stackoverflow.com/tags/char-pointer/info). Возможно также [этот вопрос] (http://stackoverflow.com/questions/3024197/what-does-int-argc-char-argv-mean) относительно 'argc' и' argv'. – einpoklum

+1

Это не проблема с пониманием char *, но указатели – user3655463

+0

@ user3655463: Увидев, как OP хотел разыменовать 'argv [1] [0]', я не согласен. – einpoklum

ответ

3

Массива операция нижнего индекса a[i] определяются как *(a + i) - данный адрес a, смещение i элементов (не байты ) из этого адреса и разыменовать результат. Таким образом, с учетом указателя p, *p эквивалентен *(p + 0), что эквивалентно p[0].

Тип argv: char **; учитывая, что все следующие условия:

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 

Поскольку тип argv[i][j] является char, *argv[i][j] не является допустимым выражением.

Вот плохая визуализация argv последовательности:

 +---+    +---+           +---+ 
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] | | 
                +---+ 

Это может помочь объяснить результаты различных выражений.

4
char *argv[] 

argv является массивом (1) из символьных указателей. Таким образом, это обычный массив, так как каждый элемент массива является указателем. argv[0] является указателем, argv[1] и т. Д.

argv[0] - первый элемент в массиве. Поскольку каждый элемент массива является указателем на указатель, значение этого также является указателем на символ (как мы уже упоминали выше).

*argv[1] - Теперь здесь argv[1] является вторым элементом в вышеупомянутом массиве, но argv[1] также является указателем на символ. Применяя *, просто разыщите указатель, и вы получите первый символ в строке, на который указывает argv[1]. Вы должны использовать %c для печати, так как это только символ.

argv[1][0] - это уже первый символ второй строки в массиве, поэтому больше нет места для разыменования. Это по существу так же, как и предыдущее.


(1) как было подчеркнуто строго говоря, это указатель на указатель, но, возможно, вы можете «думать» о нем, как массив указателей. Во всяком случае подробнее об этом здесь: https://stackoverflow.com/a/39096006/3963067

+2

Строго говоря, 'argv' не является массивом, это указатель. – HolyBlackCat

+0

@HolyBlackCat выглядит лучше? –

+0

Да, спасибо за исправление. – HolyBlackCat

2

Последняя строка printf("%c\n",*argv[1]); является как разыменования argv и доступ к индексу массива 1. Другими словами, это делает argv[1][0], как и в предыдущей строке, потому что доступ к нижнему индексу массива [1] имеет higher precedence, чем оператор разыменования (*).

Однако, если вы были в скобки выражения в последней строке, чтобы оператор разыменования быть обработаны первым, вы могли бы сделать это:

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

Теперь, когда вы запускаете программу, последнюю строку вывод будет argv[0][1] вместо [1][0], то есть второго символа в командной строке, который вы используете для выполнения программы.

3

Если argv[1] является указателем на char, то *argv[1] разыменовывает, что указатель и получает вас первый символ строки в argv[1], так что это то же самое, как argv[1][0] и печатается в формате "%c" спецификатора.

argv[1][0] - char сам по себе, а не указатель, поэтому он не может быть заменен.

+0

BTW, я не совсем уверен, что правильное написание для «раздираемого», как «разыгрываемое», выглядит уродливым и подчеркивается как некорректное с помощью макрокоманды macOS, но «раздираемое» чувствует себя лучше, хотя и подчеркивается. Кто-нибудь знает, что написано правильно? – ForceBru

2
  1. Это не относится к char *.
  2. Вы можете упростить, в чем разница между * ptr и ptr [0].
  3. Нет никакой разницы, потому что ptr [0] является сахаром для * (ptr + 0) или * ptr, потому что + 0 бесполезен.

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

%p Поскольку спецификатор ожидать void *, в обычном случае C автоматическом продвижение указателя в void * но printf() использования список переменного аргумента. В этом есть много правил, я хочу, чтобы вы читали doc, если хотите. Но char * wiil не следует рекламировать до void * и, как я говорю, printf(), за исключением void *, поэтому у вас есть неопределенное поведение, если вы его не набросаете.

+0

Почему это неправильно? он печатает адрес – DCR

+0

@GovindParmar. Приведение необходимо для обеспечения совместимого типа указателя. "** p ** Аргумент должен быть указателем на void." C11dr §7.21.6.1 8 – chux

+0

Я получаю тот же адрес, использую ли я (void *) & argv [1] или & argv [1] – DCR

 Смежные вопросы

  • Нет связанных вопросов^_^