2D манипулирование указателем массива в C ++ - PullRequest
4 голосов
/ 06 марта 2012
int main(){
    int a[10][10];
    int **ptr =(int **)a;
    cout<<a<<endl<<ptr<<endl;
    cout<<*a<<endl<<*ptr<<endl;
    return 0;
}

Вывод этого кода на моем компьютере

0021FC20
0021FC20
0021FC20
CCCCCCCC

Почему " a " равно "* a "?почему * не равно * ptr?

Ответы [ 5 ]

4 голосов
/ 06 марта 2012

Почему a равно *a?

При использовании в контексте, который требует указатель, массив будет преобразован в указатель на его первый элемент. a - массив массивов; поэтому он будет распадаться на указатель на первый массив. *a - это первый массив, который будет указывать на первое целое число в этом массиве. Оба они существуют в одном месте, поэтому два указателя будут иметь одинаковые значения.

почему * не равен * ptr?

Поскольку преобразование из массива массивов в указатель на указатель недопустимо. Вы форсировали преобразование, используя приведение - опасное приведение в стиле C, которое в этом случае действует как reinterpret_cast - поэтому *ptr будет считывать первые несколько байтов массива и интерпретировать это как указатель (вероятно, поведение здесь не определено, поэтому в принципе все может произойти). В памяти нет указателя, на который указывает ptr, поэтому *ptr определенно не даст вам действительный указатель.

2 голосов
/ 06 марта 2012

Почему равен * а?

Поскольку вы не можете напечатать массив, a неявно преобразуется из int[10][10] в int(*)[10]. Так что вместо a печатается указатель на первую строку из a.

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

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

1 голос
/ 07 марта 2012

Механика двумерных массивов на самом деле проще, чем кажется на первый взгляд, но обычно она плохо изучена. Тем не менее, чтобы быстро расшифровать вещи, требуется большой опыт, и даже опытным разработчикам иногда приходится останавливаться и думать на мгновение (или два). По сути, когда вы видите 2D-массив, например int a [5] [10] (я изменяю размер вашего массива на 5, чтобы упростить задачу), вам нужно перестать думать об этом как о 2D. Думайте об этом как 1D вместо этого. Первый нижний индекс определяет количество элементов в этом одномерном массиве, как и любой другой одномерный массив. Когда вы индексируете в массив, например, [3], вы получаете доступ к объекту по индексу 3. Это ничем не отличается от любого другого массива. Допустим, вместо этого «а» было определено так:

// Note: The size of the array, 5, can be omitted
int a[5] = {5, 10, 15, 20, 25};

После подписки получается следующее:

a[0] = 5
a[1] = 10
a[2] = 15
a[3] = 20
a[4] = 25

Это достаточно просто, и большинство понимают это. Но если вы сейчас сделаете это:

// Note: Like above, the size of the array, 5, can be omitted
int a[5][10] = {{  5,  10,  15,  20,  25,  30,  35,  40,  45,  50},
                { 55,  60,  65,  70,  75,  80,  85,  90,  95, 100},
                {105, 110, 115, 120, 125, 130, 135, 140, 145, 150},
                {155, 160, 165, 170, 175, 180, 185, 190, 195, 200},
                {205, 210, 220, 225, 230, 235, 240, 245, 250, 255}};

У вас все еще есть одномерный массив, и вы можете добавить в него индекс, как и в предыдущем примере, получив следующее (не настоящий синтаксис, как вы должны думать об этом):

a[0] =   5  10  15  20  25  30  35  40  45  50
a[1] =  55  60  65  70  75  80  85  90  95 100
a[2] = 105 110 115 120 125 130 135 140 145 150
a[3] = 155 160 165 170 175 180 185 190 195 200
a[4] = 205 210 220 225 230 235 240 245 250 255

Единственное отличие состоит в том, что вместо каждого элемента, являющегося "int", как в предыдущем примере, каждый элемент представляет собой массив (из 10 целых чисел). Поэтому, когда вы добавляете в него, как видно, каждый индекс (от 0 до 4) возвращает массив из 10 целых чисел для этой строки. Например, a [3] возвращает массив из 10 целых чисел, содержащий значения от 155 до 200. Сам элемент является "int [10]", и поэтому вы можете обращаться с ним таким образом. IOW, вы получаете обратно эквивалент этого:

int b[10] = {155, 160, 165, 170, 175, 180, 185, 190, 195, 200};

И так, например, так же, как b [7] = 190, a [3] [7] = 190, так как a [3] эффективно возвращает эквивалент b (массив из 10 целые числа), а затем индекс [7] захватывает элемент с индексом 7 этого массива, как и b [7]. Более того, точно так же, как b является указателем на первый элемент массива (поскольку все имена массивов превращаются в указатель на первый элемент массива), который в этом случае равен 155, a [3] также выполняет то же самое. Зачем? Поскольку он возвращает эквивалент b, как описано, и точно так же, как b, он превращается в указатель на свой первый элемент. IOW, так же, как это правда:

int *p = b; // "b" decays into a pointer to its first element (an "int")
int val = *p; // Equals 155

Это также верно:

int *p = a[3]; // a[3] returns the equivalent of "b", an int[10], and this decays into a pointer to its first element, just like "b"
int val = *p; // Equals 155

И, наконец, точно так же, как упомянуто FredOverflow, это также верно:

int (*p)[10] = a;

Синтаксис требует привыкания, но имеет смысл. Поскольку каждое имя массива превращается в указатель на его первый элемент, как обсуждалось, «a» превращается в указатель на его первый элемент. Что это за элемент? Итак, каждый элемент «a» представляет собой массив из 10 целых чисел, то есть «int [10]», поэтому «a» должен распадаться на указатель на первый из этих элементов «int [10]». Синтаксис чуть выше, как вы объявляете этот указатель. Он определяет «p» как указатель на массив из 10 целых чисел. (Круглые) скобки требуются из-за правил приоритета C ++. Если вы удалите их, вы получите это:

int *p[10] = a; // Compiler error!

Который объявляет "p" как массив из 10 элементов, где каждый элемент является указателем на int. Это не то же самое, поэтому скобки требуются для изменения приоритета (и, следовательно, смысла этого объявления). Используя приведенный выше правильный синтаксис, «p» будет указывать на первый элемент (массив «int [10]», содержащий элементы с 5 по 50), p + 1 будет указывать на второй элемент («int [10]»). "массив, содержащий элементы от 55 до 100) и т. д. Бонусные баллы: Что будет делать следующее?

(*(p + 3))[5]

Возвращает 180, потому что (p + 3) возвращает указатель на массив из 10 целых чисел в «a [3]», а при разыменовании с помощью оператора * вы получаете фактический массив «int [10]» в этом месте , Затем индекс [5] возвращает значение 180 в этом массиве.

Без сомнения, все еще потребуется много практики, чтобы разобраться с этим, но я надеюсь, что это поможет.

1 голос
/ 06 марта 2012

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

0 голосов
/ 06 марта 2012

2D C массив НЕ является указателем на указатель.По сути, это указатель со строками * элементами столбцов.

int main(){
    int a[10][10];
    int *ptr =(int *)a;
    cout<<a<<endl<<ptr<<endl;
    cout<<*a<<endl<<*ptr<<endl;
    return 0;
}

Вышеприведенное даст вам то, что вам нужно.

Хотя, если вы отмените ссылку на a или ptr, вы найдетевы получите "неопределенное значение"

, если вы установите [4] [4], чтобы сказать 5

Тогда вы найдете значение, хранящееся в

ptr [(строка* 10) + column) вернет это значение, где row = 4 и column = 4.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...