Как увеличить адрес указателя и значение указателя?

96

Допустим,

int *p;
int a = 100;
p = &a;

Что на самом деле будет делать следующий код и как?

p++;
++p;
++*p;
++(*p);
++*(p);
*p++;
(*p)++;
*(p)++;
*++p;
*(++p);

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

Примечание: Предположим, что адрес a=5120300хранится в указателе p, адрес которого равен 3560200. Теперь, каково будет значение p & aпосле выполнения каждого оператора?

Динеш
источник
3
почему бы вам просто не запустить его в отладчике?
AndersK
24
Ну .. почему бы просто не попробовать и не увидеть? printfнапечатает указатель с% p
Брайан Роуч
Если вам интересно поведение, просто поиграйте с ним. Просто напишите простую программу на c, которая рассматривает все эти варианты использования, и посмотрите, имеет ли это смысл для вас.
Сайрус
@AndersK. Может быть, OP ожидает неопределенного поведения? ...А может и нет.
Mateen Ulhaq

Ответы:

178

Во-первых, оператор ++ имеет приоритет над оператором *, а операторы () имеют приоритет над всем остальным.

Во-вторых, оператор числа ++ совпадает с оператором числа ++, если вы не назначаете его чему-либо. Разница в том, что число ++ возвращает число, а затем увеличивает число, а ++ число сначала увеличивает, а затем возвращает его.

В-третьих, увеличивая значение указателя, вы увеличиваете его на размер его содержимого, то есть увеличиваете его, как если бы вы выполняли итерацию в массиве.

Итак, подведем итог:

ptr++;    // Pointer moves to the next int position (as if it was an array)
++ptr;    // Pointer moves to the next int position (as if it was an array)
++*ptr;   // The value of ptr is incremented
++(*ptr); // The value of ptr is incremented
++*(ptr); // The value of ptr is incremented
*ptr++;   // Pointer moves to the next int position (as if it was an array). But returns the old content
(*ptr)++; // The value of ptr is incremented
*(ptr)++; // Pointer moves to the next int position (as if it was an array). But returns the old content
*++ptr;   // Pointer moves to the next int position, and then get's accessed, with your code, segfault
*(++ptr); // Pointer moves to the next int position, and then get's accessed, with your code, segfault

Поскольку здесь много случаев, я мог сделать какую-то ошибку, поправьте меня, если я ошибаюсь.

РЕДАКТИРОВАТЬ:

Итак, я ошибался, приоритет немного сложнее, чем то, что я написал, просмотрите его здесь: http://en.cppreference.com/w/cpp/language/operator_precedence

фелипемайя
источник
6
* ptr ++, значение не увеличивается, указатель есть. Эти унарные операторы имеют такой же приоритет, но вычисляются справа налево. Код означает «взять содержимое, на которое указывает ptr, затем увеличить ptr». Это очень распространенный код C (и да, довольно запутанный). Пожалуйста, исправьте это, и я удалю голос против. То же самое для * (ptr) ++, скобка ничего не делает.
Lundin
Большое спасибо, Лундин, я еще что-нибудь пропустил?
felipemaia
@Lundin Привет, исправлен ли ответ выше? Спасибо.
Unheilig
4
@Unheilig Первое предложение по-прежнему полностью неверно, постфикс ++ имеет приоритет над унарным *, имеющим тот же приоритет, что и префикс ++. В остальном вроде нормально.
Lundin
4
@felipemaia Вы уверены, что это произойдет автоматически? Может, это просто неопределенное поведение?
jotik
14

проверил программу и результаты такие,

p++;    // use it then move to next int position
++p;    // move to next int and then use it
++*p;   // increments the value by 1 then use it 
++(*p); // increments the value by 1 then use it
++*(p); // increments the value by 1 then use it
*p++;   // use the value of p then moves to next position
(*p)++; // use the value of p then increment the value
*(p)++; // use the value of p then moves to next position
*++p;   // moves to the next int location then use that value
*(++p); // moves to next location then use that value
Суджит Р. Кумар
источник
2
@alex использовать это означает, например, рассмотреть оператор, 'int * a = p ++;' Здесь будет использовано первое значение указателя p, после чего p переместится на следующую позицию. Таким образом, после выполнения вышеупомянутого оператора 'a' будет иметь адрес предыдущего местоположения, обозначенного 'p', а 'p' будет указывать на следующую позицию. То есть сначала используйте значение «p» для выражения присваивания, как указано выше, а затем увеличивайте значение «p», чтобы указать на следующую позицию
Суджит Р. Кумар
Короче говоря, я думаю, что он использует фразу «использовать» вместо более формального термина «назначать». Это все.
Апекшик Паниграхи 02
4

Ниже приводится пример различных предложений «просто распечатать». Я нашел это поучительным.

#include "stdio.h"

int main() {
    static int x = 5;
    static int *p = &x;
    printf("(int) p   => %d\n",(int) p);
    printf("(int) p++ => %d\n",(int) p++);
    x = 5; p = &x;
    printf("(int) ++p => %d\n",(int) ++p);
    x = 5; p = &x;
    printf("++*p      => %d\n",++*p);
    x = 5; p = &x;
    printf("++(*p)    => %d\n",++(*p));
    x = 5; p = &x;
    printf("++*(p)    => %d\n",++*(p));
    x = 5; p = &x;
    printf("*p++      => %d\n",*p++);
    x = 5; p = &x;
    printf("(*p)++    => %d\n",(*p)++);
    x = 5; p = &x;
    printf("*(p)++    => %d\n",*(p)++);
    x = 5; p = &x;
    printf("*++p      => %d\n",*++p);
    x = 5; p = &x;
    printf("*(++p)    => %d\n",*(++p));
    return 0;
}

Он возвращается

(int) p   => 256688152
(int) p++ => 256688152
(int) ++p => 256688156
++*p      => 6
++(*p)    => 6
++*(p)    => 6
*p++      => 5
(*p)++    => 5
*(p)++    => 5
*++p      => 0
*(++p)    => 0

Я привел адреса указателя к int s, чтобы их можно было легко сравнить.

Я скомпилировал его с помощью GCC.

Рико Пиконе
источник
1
Я бы изменил это, чтобы включить значение x и p после операции.
NetJohn
3

Что касается вопроса «Как увеличить адрес указателя и значение указателя?» Я думаю, что ++(*p++);это на самом деле хорошо определено и делает то, о чем вы просите, например:

#include <stdio.h>

int main() {
  int a = 100;
  int *p = &a;
  printf("%p\n",(void*)p);
  ++(*p++);
  printf("%p\n",(void*)p);
  printf("%d\n",a);
  return 0;
}

Это не изменение одного и того же дважды перед точкой последовательности. Я не думаю, что это хороший стиль для большинства применений - на мой взгляд, он слишком загадочен.

Флексографская
источник
На самом деле скобки не нужны: ++*p++успешно увеличивают и значение, и указатель (постфиксная ++привязка сильнее, чем разыменование *, и это происходит до префикса ++из-за порядка). Скобки необходимы только тогда, когда вам нужно значение перед его увеличением (*p++)++. Если вы выберете all-prefix, он ++*++pбудет работать нормально и без круглых скобок (но увеличивайте значение, указанное после приращения указателя).
cmaster - восстановить монику в должности
1
        Note:
        1) Both ++ and * have same precedence(priority), so the associativity comes into picture.
        2) in this case Associativity is from **Right-Left**

        important table to remember in case of pointers and arrays: 

        operators           precedence        associativity

    1)  () , []                1               left-right
    2)  *  , identifier        2               right-left
    3)  <data type>            3               ----------

        let me give an example, this might help;

        char **str;
        str = (char **)malloc(sizeof(char*)*2); // allocate mem for 2 char*
        str[0]=(char *)malloc(sizeof(char)*10); // allocate mem for 10 char
        str[1]=(char *)malloc(sizeof(char)*10); // allocate mem for 10 char

        strcpy(str[0],"abcd");  // assigning value
        strcpy(str[1],"efgh");  // assigning value

        while(*str)
        {
            cout<<*str<<endl;   // printing the string
            *str++;             // incrementing the address(pointer)
                                // check above about the prcedence and associativity
        }
        free(str[0]);
        free(str[1]);
        free(str);
Абхишек Д.К.
источник
Что такое асоциативность?
71GA,
1
в коде вы можете увидеть * str ++, теперь здесь оба * и ++ имеют одинаковый приоритет (тот же приоритет в терминологии непрофессионала), а также * str ++ не разделяются круглыми скобками, такими как * (str ++) или (* str) ++, поэтому становится необходимым, как это следует оценивать. так что справа налево означает (x = str ++), а затем (y = * x)
Abhishek DK