Як збільшити адресу вказівника та значення вказівника?


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. Можливо, ОП очікує невизначеної поведінки? ... А може, ні.
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". Це дуже поширений код С (і так, досить заплутаний). Будь ласка, виправте це, і я зніму голос проти. Те саме для * (ptr) ++, дужки нічого не роблять.
Лундін,

Щиро дякую, Лундін, я ще щось пропустив?
felipemaia

@Lundin Привіт, чи виправлена ​​відповідь вище? Дякую.
Unheilig,

4
@Unheilig Перше речення все ще абсолютно неправильне, постфікс ++ має перевагу над унарним *, який має той самий пріоритет, що і префікс ++. Окрім цього, це здається нормальним.
Лундін,

4
@felipemaia Ви впевнені, що це буде segfault? Може це просто невизначена поведінка?
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', щоб вказати на наступну позицію
Sujith R Kumar

Коротше кажучи, я думаю, що він використовує фразу "використовувати це" для більш офіційного терміна "призначити". Це все.
Апекшик

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)
Абхішек DK
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.