Арифметика указателя? В чем разница между этими тремя выражениями? - PullRequest
1 голос
/ 13 мая 2011

Итак, я пытаюсь понять две вещи:

  1. В чем различия между этими тремя выражениями?
  2. Что эквивалентно первому выражению (A)?

Вот код ( фиксированный ):

#include "stdafx.h"

void someFunc(double** pArray, int length)
{
    for(int i = 0; i < length; i++)
    {
        //this works perfectly
        double expressionA = *(*pArray + i);
        //this crashes : Unhandled exception at 0x00da13ff in pptrtest.exe: 0xC0000005: Access violation reading location 0xcccccccc.
        double expressionB = **(pArray + i);
        //this crashes : Unhandled exception at 0x00da13ff in pptrtest.exe: 0xC0000005: Access violation reading location 0xcccccccc.
        double expressionC = *pArray[i];
    }
}
int main()
{
    double arr[] = { 1, 2, 3, 4, 5 };

    double* pArr = arr;

    someFunc(&pArr, sizeof arr / sizeof arr[0]);

    return 0;
}

Ответы [ 5 ]

3 голосов
/ 13 мая 2011

Я думаю, проблема в том, что [] применяется до *, поэтому в выражении C вы делаете

*(ppDoubleArray[i])

фактически эквивалентен выражению B, но выражение A делает

(*ppDoubleArray)[i]

(при условии, что 1 в выражении должно быть i, иначе все равно будет иначе).

1 голос
/ 13 мая 2011
double expressionB = **(ppDoubleArray + 1);

Давайте посмотрим на память этого парня, в стеке у вас есть указатель на указатель на двойное число. Так что, если это адрес в памяти для 32-битного процессора, стек может выглядеть примерно так:

stack:  |ppDoubleArray ptr    |local vars, other args, or garbage |more locals/etc
addr:   |0 bytes from stack   |4 bytes from stack                 |8bytes...

Итак, когда мы смотрим на первую часть выражения

  (ppDoubleArray + 1)

Здесь написано "пройти один указатель мимо ppDoubleArray". Перейти к следующему указателю, перейдя к следующему месту в памяти. Какая следующая точка в памяти после ppDoubleArray? Посмотрите на стек выше, это, вероятно, некоторые локальные переменные или другой аргумент. Итак, теперь у вас есть кто знает, что (возможно, содержимое длины? Один из двойников? Мусор?), И вы будете обращаться с ним, как если бы это был допустимый адрес где-то. Затем вы следуете этому предполагаемому указателю, разыменовывая:

   **(ppDoubleArray + 1)

И сбой!

Так, например, если длина равна 5, ppDoubleArray + 1 может получить 5 и разыменовать «5», ища что-то по этому адресу.

Этот код:

   double expressionC = *ppDoubleArray[i];

работает так же, [] имеет приоритет над разыменованием. Итак, вы идете в i-ю ячейку памяти после ppDoubleArray и предполагаете, что она указывает на ваш массив.

Изменение порядка операций с родителем будет работать:

   (*ppDoubleArray)[i]
1 голос
/ 13 мая 2011

У вас есть указатель на двойной указатель. Думайте об этом как указатель на массив двойников. В любом случае, их значения следующие:

  1. double expressionA = *(*ppDoubleArray + 1); совпадает с: double expressionA = *((*ppDoubleArray) + 1);
  2. double expressionB = **(ppDoubleArray + 1); - это то же самое, что и double expressionB = *(*(ppDoubleArray + 1));, что означает, что вы хотите отменить ссылку ppDoubleArray[1], который, по-моему, не существует.
  3. double expressionC = *ppDoubleArray[i]; - это то же самое, что и double expressionC = *(ppDoubleArray[i]); - опять вы отменяете ссылку на ppDoubleArray[i], который не существует.

Как и все остальные, вам нужно помнить о порядке предпочтений. По сути, сначала разыменуйте, а затем индексируйте ваш массив.

0 голосов
/ 13 мая 2011

ASCII art Время:
---> = разыменование

double expressionA = *(*ppDoubleArray + 1);

ppDoubleArray  ---> .  
                    +1  
                    . ---> expressionA

double expressionB = **(ppDoubleArray + 1);

ppDoubleArray
     +1
      . ---> . ---> expressionB

double expressionC = *ppDoubleArray[i];

ppDoubleArray
     +i
      . ---> . ---> expressionC
0 голосов
/ 13 мая 2011

Предполагается, что ppDoubleArray является указателем на массив значений типа double:

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

третий сбой из-за приоритета операторов.Вы выбираете элемент i, который не является массивом (если я не равен 0), а затем разыменовываете его.

Здесь важно помнить, как обращаться с указателем на массив (илиуказатель на указатель) Вы разыменовываете его один раз, чтобы попасть в массив, после чего вы можете выполнить арифметику, а затем разыменовать его снова, чтобы добраться до элемента.

...