Різниця між * ptr + = 1 і * ptr ++ у С


122

Я щойно почав вивчати C, і, роблячи один приклад про передачу вказівника на покажчик як параметр функції, я виявив проблему.

Це мій зразок коду:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int* allocateIntArray(int* ptr, int size){
    if (ptr != NULL){
        for (int i = 0; i < size; i++){
            ptr[i] = i;
        }
    }
    return ptr;
}

void increasePointer(int** ptr){
    if (ptr != NULL){
        *ptr += 1; /* <----------------------------- This is line 16 */
    }
}

int main()
{
    int* p1 = (int*)malloc(sizeof(int)* 10);
    allocateIntArray(p1, 10);

    for (int i = 0; i < 10; i++){
        printf("%d\n", p1[i]);
    }

    increasePointer(&p1);
    printf("%d\n", *p1);
    p1--;
    free(p1);
    fgets(string, sizeof(string), stdin);
    return 0;
}

Проблема виникає в рядку 16, коли я змінюю *ptr+=1до *ptr++. Очікуваний результат повинен бути весь масив і номер 1, але коли я використовую *ptr++результат, це 0.

Чи є різниця між +=1і ++? Я подумав, що вони обоє однакові.


2
Зауважте, що даний код не буде компілюватися, як ви не заявили string.
Spikatrix

6
Інші зауваження: 1) allocateIntArray- це неправильне ім'я, оскільки, здається, ви mallocмасив з функції, але ви цього не робите. Я пропоную fillIntArrayзамість цього. 2) Ви не використовуєте повернене значення allocateIntArray. Я пропоную вам змінити тип повернення на void. 3) Не слід чи if (ptr != NULL)в функції increasePointerбути if (*ptr != NULL)? 4) У ролях mallocне потрібно. Дивіться коментар Сурава вище. 5) Це: for (int i = 0; i < 10; i++){ printf("%d\n", p1[i]); }і printf("%d\n", *p1); p1--;його потрібно докласти if(p1 != NULL). 6) string.hне використовується.
Spikatrix

9
p+=1це як ++p, не такp++
Кос

5
це питання було задано 4 роки тому: чи ++ те саме, що і + = 1 для покажчиків
ren

3
@ren Майже, але не зовсім. Пов'язане запитання не стосується оператора розвідки, що є сутнім питанням ОП тут.
Джейсон C

Відповіді:


289

Різниця обумовлена ​​перевагою оператора.

Оператор після інкремента ++має більш високий пріоритет, ніж оператор дереференції *. Так що *ptr++еквівалентно *(ptr++). Іншими словами, приріст допису змінює покажчик, а не те, на що він вказує.

Оператор присвоєння +=має нижчий пріоритет, ніж оператор відміни *, тому *ptr+=1еквівалентний (*ptr)+=1. Іншими словами, оператор призначення змінює значення, на яке вказує вказівник, і не змінює сам покажчик.


3
Для початківців мнемонічність - це подібність між *p++і *++p. Операторський пріоритет останнього зрозумілий, наступний з перших.
Вальтер Трос

21

Порядок пріоритетності для 3-х операторів, які беруть участь у вашому запитанні, такий:

пост-інкремент ++> дереференція *> призначення+=

Ви можете перевірити цю сторінку, щоб отримати детальнішу інформацію про цю тему.

Під час розбору виразу оператор, який вказаний у якомусь рядку, буде прив’язаний більш жорстко (як би в дужках) до своїх аргументів, ніж будь-який оператор, який вказаний у рядку далі під ним. Наприклад, вираз *p++аналізується як *(p++), а не як (*p)++.

Короткий опис короткого оповідання, для того, щоб висловити це завдання *ptr+=1за допомогою оператора після збільшення, вам потрібно додати круглі дужки до оператора перенавантаження, щоб надати цій операції перевагу над ++цим у цьому(*ptr)++


3
Цікаво, що наразі це єдина відповідь, яка містить рішення ... (* ptr) ++
hyde

7

Застосуємо дужки, щоб показати порядок операцій

a + b / c
a + (b/c)

Давайте зробимо це ще раз

*ptr   += 1
(*ptr) += 1

І знову с

*ptr++
*(ptr++)
  • В *ptr += 1, ми збільшуємо значення змінного нашого покажчика точку на.
  • В *ptr++, ми збільшуємо покажчик після всього нашого заяви (рядок коду) виконується, і повертає посилання на змінну наш покажчик точки на.

Останнє дозволяє робити такі речі, як:

for(int i = 0; i < length; i++)
{
    // Copy value from *src and store it in *dest
    *dest++ = *src++;

    // Keep in mind that the above is equivalent to
    *(dest++) = *(src++);
}

Це звичайний метод, який використовується для копіювання srcмасиву в інший destмасив.


"і повернути посилання на змінну, на яку вказує наш покажчик." C не має посилань.
Майлз Рут

@MilesRout Можливо, називати це рівнем може бути точнішим? Але я не впевнений, як це зробити, не додаючи жаргону.
Mateen Ulhaq

3

Дуже гарне запитання.

У мові програмування K & R "C" "5.1 Покажчики та адреси" ми можемо отримати відповідь на це.

"Одинарні оператори * і & зв'язуються більш щільно, ніж арифметичні оператори"

*ptr += 1      //Increment what ptr points to.

"Унарні оператори, такі як * і ++, асоціюються праворуч ліворуч ."

*ptr++        //Increment prt instead of what ptr point to.

// Це працює як * (ptr ++).

Правильний спосіб:

(*ptr)++      //This will work.

Це перший раз, коли я коментую переповнення стека. Я оновив формат коду. ^^ Дякую за вашу пропозицію.
Nick.Sang

2

* ptr + = 1: дані збільшення, на які вказує ptr. * ptr ++: покажчик збільшення, який вказує на наступне місце пам'яті замість даних, на які вказує вказівник.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.