2015-11-13 4 views
0

У меня возникла проблема при попытке понять указатель в программировании на языке C.C указатель программирования pre-fix post-fix

struct student { 
int age; 
char *name; 
}; 

int main() 
{ 
    struct student b[3] = { 18, "Peter", 19, "Mary", 20, "John" }; 
    struct student *p = b; 

    printf("%d\n", ++p->age); 
    printf("%s\n", (*p).name); 
    printf("%c\n", *p->name - 1); 
    printf("%c\n", *++p->name); 
    printf("%c\n", *p++->name); 
    printf("%c\n", *(++p)->name); 
    return 0; 
} 

выходы, которые я получаю от визуальной студии были:

19 
Peter 
0 
e 
e 
J 

В течение первых 3-х строк Я понимаю, как это работает. Однако, в *++p->name, как получилось, это 'e'. Я думал, что указатель уже указывает на Мэри, и это должно быть «а», а следующее должно быть «а», так как это пост-исправление. Тогда как для *(++p)->name, скажем, предыдущая строка вернула мне «е», как она пропустила Мэри и распечатала «J».

Мне было поручено написать вывод на бумаге без запуска программы, поэтому я хотел бы знать, почему. Заранее спасибо.

ответ

1

*++p->name эквивалент *(++(p->name)).

В этот момент p по-прежнему указывает на первый элемент b, поэтому его содержимое { 18, "Peter" }.

Он принимает указатель name, применяет приращение префикса к нему, и выражение вычисляет указатель на второй символ "Peter". Наконец, оператор разыменования дает символ 'e'.


В *(++p)->name, p указывает на последний элемент b (после прироста), так его содержимое { 20, "John" }.

Это принимает указатель name и разыгрывает его, чтобы дать нам первый символ "John", который является 'J'.

Редактировать: Возможно, вам будет полезно распечатывать значения указателей после каждого printf, чтобы помочь вам понять результат.

+0

Я думаю, что я получаю это уже. Имя * p ++ -> сдвигает мой указатель, указывающий на Мэри, и следующее имя * (++ p) -> будет указывать на Джона. Я прав? –

+0

Да, это звучит правильно. – tangrs

+0

Большое вам спасибо! –

1

Для операций адресации «Начните с суффикса, перейдите к префиксу и прочитайте оба набора изнутри». За исключением случаев, когда приоритет переопределяется parens, которые работают так же, как и с другими операторами.

Так *x->y интерпретируются как лечение x как -> указателя на структуру, глядя вверх его y члена, а затем обработку, что в качестве указателя и * разыменования, что для получения конечного значения.

И наоборот, (*x)->y интерпретируется как лечение x как указатель на pointef на структуру, * разыменования его, чтобы получить указатель на структуру, а затем -> извлечения их в y элемент.

Обратите внимание, что [] также является оператором суффикса. Итак, *x[3]->y извлекает третий указатель-на-структуру, получает y от этого, а затем разыгрывает.

__Полезный совет: _ Синтаксис и приоритет для объявлений C точно такие же, как для адресации C.Таким образом, int (*x)[4]; является указателем на массив из четырех целых чисел, тогда как int *x[4]; представляет собой массив из 4 указателей на ints.

(Я написал простой рекурсивный спуск парсер для объявлений C, много лет назад, который преобразует их в такие фразы. Как и любой хакер из старой школы, я назвал его C EXplainer или CEX, очевидное произношение.)

0
struct student b[3] = { 18, "Peter", 19, "Mary", 20, "John" }; 

Создает массив struct student с тремя элементами, которые, в зависимости от компилятора и дополнения, будет иметь sizeof 12-16 символов (x86_64) или 8-16 символов (x86), состоящие из 4-bytes (int) и 8-bytes (char *) (4 байта на x86) и будет иметь макет памяти чего-то вроде (например, целей):

int char* 
+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
^ 
p 

Отдельные строковые литералы ("Peter", "Mary", "John") создаются в памяти, а указатель на каждый строковый литерал хранится как name в отдельных элементах структуры.

Когда вы используете оператор ->, это является разыменованием для структуры. Использование оператора Преинкремента на p->age, вы получите следующий возрастной элемент в массиве b, но адрес указателя p неизменен:

printf("%d\n", ++p->age); 

+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
^   ^
p    ++p->age 

19 

Разыменование p и используя (точку) оператор '.', вы печатаете имя, связанное с текущим элементом в b:

printf("%s\n", (*p).name); 

Peter 

Когда вы вычитать 1 из *p->name вы декремент указателя на символ на 1 который оставляет вас в no-mans-land по адресу в памяти где-то перед строковым литералом «Питер».

printf("%c\n", *p->name - 1); 

+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
^  \ 
p  Peter (string literal in memory) 
    ^
     p->name - 1 
0 

Когда вы разыменования затем Преинкремент (вы увеличивающиеся указатель p->name) вы перемещаете один символ вперед в строковый литерал «Питер»:

printf("%c\n", *++p->name); 

+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
^  \ 
p  Peter 
     ^
     ++p->name 
e 

Когда вы в следующий раз разыменования, то после -increment, вы печатаете тот же символ, а затем опережения указатель на второй элемент в b:

printf("%c\n", *p++->name); 

+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
^  \ 
p  Peter 
     ^
     p->name 
e 

+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
      ^
       p 

Наконец по operato г старшинство, вы Преинкремент указатель (опережения указатель на третий элемент в b), а затем derefernce указатель p->name указывая на J:

printf("%c\n", *(++p)->name); 

+----+--------+----+--------+----+--------+ 
|age |name* |age |name* |age |name* | 
+----+--------+----+--------+----+--------+ 
          ^ \ 
          p  John 
           ^
            p->name 
J 
Смежные вопросы