Почему x [0]!= X [0] [0]!= X [0] [0] [0]?

Я изучаю немного С++, и я сражаюсь с указателями. Я понимаю, что у меня может быть 3 уровня указателей, объявляя:

int *(*x)[5];

так что *x является указателем на массив из 5 элементов, которые являются указателями на int. Также я знаю, что x[0] = *(x+0);, x[1] = *(x+1) и т.д....

Итак, учитывая приведенное выше выражение, почему x[0] != x[0][0] != x[0][0][0]?

Ответ 1

x - это указатель на массив из 5 указателей на int.
x[0] - массив из 5 указателей на int.
x[0][0] является указателем на int.
x[0][0][0] является int.

                       x[0]
   Pointer to array  +------+                                 x[0][0][0]         
x -----------------> |      |         Pointer to int           +-------+
               0x500 | 0x100| x[0][0]---------------->   0x100 |  10   |
x is a pointer to    |      |                                  +-------+
an array of 5        +------+                        
pointers to int      |      |         Pointer to int                             
               0x504 | 0x222| x[0][1]---------------->   0x222                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x508 | 0x001| x[0][2]---------------->   0x001                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x50C | 0x123| x[0][3]---------------->   0x123                    
                     |      |                                             
                     +------+                                             
                     |      |         Pointer to int                              
               0x510 | 0x000| x[0][4]---------------->   0x000                    
                     |      |                                             
                     +------+                                             

Вы можете видеть, что

  • x[0] - это массив и преобразуется в указатель на его первый элемент при использовании в выражении (за некоторыми исключениями). Поэтому x[0] даст адрес своего первого элемента x[0][0], который равен 0x500.
  • x[0][0] содержит адрес int, который 0x100.
  • x[0][0][0] содержит значение int 10.

Итак, x[0] равно &x[0][0] и, следовательно, &x[0][0] != x[0][0].
Следовательно, x[0] != x[0][0] != x[0][0][0].

Ответ 2

x[0] != x[0][0] != x[0][0][0]

согласно вашему собственному сообщению,

*(x+0) != *(*(x+0)+0) != *(*(*(x+0)+0)+0)`  

который упрощен

*x != **x != ***x

Почему это должно быть равным?
Первый - адрес некоторого указателя.
Второй - адрес другого указателя.
И третий - это значение int.

Ответ 3

Вот макет памяти вашего указателя:

   +------------------+
x: | address of array |
   +------------------+
            |
            V
            +-----------+-----------+-----------+-----------+-----------+
            | pointer 0 | pointer 1 | pointer 2 | pointer 3 | pointer 4 |
            +-----------+-----------+-----------+-----------+-----------+
                  |
                  V
                  +--------------+
                  | some integer |
                  +--------------+

x[0] дает "адрес массива",
x[0][0] дает "указатель 0",
x[0][0][0] дает "некоторое целое число".

Я считаю, теперь должно быть очевидно, почему все они разные.


Вышеприведенное достаточно близко для базового понимания, поэтому я написал его так, как я его написал. Однако, как справедливо указывает гак, первая строка не точна на 100%. Итак, вот все мелкие детали:

Из определения языка C значение x[0] - это весь массив целых указателей. Тем не менее, массивы - это то, с чем вы не можете ничего делать в C. Вы всегда манипулируете либо своим адресом, либо их элементами, а не целым массивом в целом:

  • Вы можете передать x[0] оператору sizeof. Но это не действительно использование значения, его результат зависит только от типа.

  • Вы можете взять свой адрес, который дает значение x, i. е. "адрес массива" с типом int*(*)[5]. Другими словами: &x[0] <=> &*(x + 0) <=> (x + 0) <=> x

  • Во всех других контекстах значение x[0] будет распадаться на указатель на первый элемент в массиве. То есть указатель со значением "адрес массива" и тип int**. Эффект такой же, как если бы вы нажали x на указатель типа int**.

Из-за разложения указателя на массив в случае 3. все применения x[0] в конечном итоге приводят к указателю, указывающему начало массива указателей; вызов printf("%p", x[0]) будет печатать содержимое ячеек памяти, помеченных как "адрес массива".

Ответ 4

  • x[0] разделяет внешний указатель ( указатель на массив размера 5 указателя на int) и приводит к массиву размера 5 указателя на int;
  • x[0][0] разделяет внешний указатель и индексирует массив, приводя к указателю на int;
  • x[0][0][0] разыгрывает все, что приводит к конкретному значению.

Кстати, если вы когда-нибудь смущаетесь тем, что означают эти объявления, используйте cdecl.

Ответ 5

Давайте рассмотрим пошаговые выражения x[0], x[0][0] и x[0][0][0].

Как x определяется следующим образом:

int *(*x)[5];

тогда выражение x[0] представляет собой массив типа int *[5]. Учтите, что выражение x[0] эквивалентно выражению *x. Это разыменование указателя на массив, который мы получаем сам массив. Обозначим его так же, как у, мы имеем объявление

int * y[5];

Выражение x[0][0] эквивалентно y[0] и имеет тип int *. Обозначим его как z, что мы имеем объявление

int *z;

выражение x[0][0][0] эквивалентно выражению y[0][0], которое в свою очередь эквивалентно выражению z[0] и имеет тип int.

Итак, мы имеем

x[0] имеет тип int *[5]

x[0][0] имеет тип int *

x[0][0][0] имеет тип int

Таким образом, это объекты разных типов и разных размеров.

Запустите, например,

std::cout << sizeof( x[0] ) << std::endl;
std::cout << sizeof( x[0][0] ) << std::endl;
std::cout << sizeof( x[0][0][0] ) << std::endl;

Ответ 6

Прежде всего, я должен сказать, что

x [0] = * (x + 0) = * x;

x [0] [0] = * (* (x + 0) + 0) = * * x;

x [0] [0] [0] = * (* (* (x + 0) + 0)) = * * * x;

So * x ≠ * * x ≠ * * * x

Из следующего рисунка все ясно.

  x[0][0][0]= 2000

  x[0][0]   = 1001

  x[0]      = 10

enter image description here

Это просто пример, где значение x [0] [0] [0] = 10

и адрес x [0] [0] [0] 1001

этот адрес хранится в x [0] [0] = 1001

и адрес x [0] [0] 2000

и этот адрес хранится в x [0] = 2000

So x [0] [0] [0] x [0] [0] x [0]

.

РЕДАКТИРОВАНИЕ

Программа 1:

{
int ***x;
x=(int***)malloc(sizeof(int***));
*x=(int**)malloc(sizeof(int**));
**x=(int*)malloc(sizeof(int*));
***x=10;
printf("%d   %d   %d   %d\n",x,*x,**x,***x);
printf("%d   %d   %d   %d   %d",x[0][0][0],x[0][0],x[0],x,&x);
}

Выход

142041096 142041112 142041128 10
10 142041128 142041112 142041096 -1076392836

Программа 2:

{
int x[1][1][1]={10};
printf("%d   %d   %d   %d \n ",x[0][0][0],x[0][0],x[0],&x);
}

Выход

10   -1074058436   -1074058436   -1074058436 

Ответ 7

Если бы вы рассматривали массивы с реальной точки зрения, это выглядело бы так:

x[0] - грузовой контейнер, полный ящиков.
x[0][0] - это единый ящик с полными коробками для обуви в грузовом контейнере.
x[0][0][0] представляет собой единую коробку для обуви внутри ящика, внутри грузового контейнера.

Даже если это единственный ботинок в единственном ящике в грузовом контейнере, он все еще является обувной коробкой, а не грузовым контейнером

Ответ 8

В С++ существует принцип, так что: объявление переменной указывает точно способ использования переменной. Рассмотрите свое выражение:

int *(*x)[5];

который можно переписать как (для более четкого):

int *((*x)[5]);

В силу принципа мы имеем:

*((*x)[i]) is treated as an int value (i = 0..4)
→ (*x)[i] is treated as an int* pointer (i = 0..4)
→ *x is treated as an int** pointer
→ x is treated as an int*** pointer

Таким образом:

x[0] is an int** pointer
→ x[0][0] = (x[0]) [0] is an int* pointer
→ x[0][0][0] = (x[0][0]) [0] is an int value

Итак, вы можете понять разницу.

Ответ 9

Быть p указателем: вы выполняете разбиение на разделы с помощью p[0][0], что эквивалентно *((*(p+0))+0).

В ссылке C (&) и разыменовании (*):

p == &p[0] == &(&p[0])[0] == &(&(&p[0])[0])[0])

Является эквивалентным:

p == &*(p+0) == &*(&*(p+0))+0 == &*(&*(&*(p+0))+0)+0

Посмотрите, что & * может быть реорганизовано, просто удалив его:

p == p+0 == p+0+0 == p+0+0+0 == (((((p+0)+0)+0)+0)+0)

Ответ 10

Вы пытаетесь сравнить разные типы по значению

Если вы берете адреса, вы можете получить больше того, что ожидаете

Имейте в виду, что ваша декларация имеет значение

 int y [5][5][5];

позволит выполнить сравнения, поскольку y, y[0], y[0][0], y[0][0][0] будут иметь разные значения и типы, но тот же адрес

int **x[5];

не занимает смежного пространства.

x и x [0] имеют один и тот же адрес, но x[0][0] и x[0][0][0] находятся в разных адресах

Ответ 11

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

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

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

ПРИМЕЧАНИЕ: Поведение в этой программе undefined, но я размещаю его здесь исключительно как интересную демонстрацию того, что могут сделать указатели, но не должно.

#include <stdio.h>

int main () {
  int *(*x)[5];

  x = (int *(*)[5]) &x;

  printf("%p\n", x[0]);
  printf("%p\n", x[0][0]);
  printf("%p\n", x[0][0][0]);
}

Это компилируется без предупреждений как на C89, так и на C99, а выход следующий:

$ ./ptrs
0xbfd9198c
0xbfd9198c
0xbfd9198c

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

Объявляем x как указатель на массив из 5 элементов, где каждый элемент имеет указатель на тип int. Это объявление выделяет 4 байта в стеке выполнения (или больше в зависимости от вашей реализации, на моих указателях машины - 4 байта), поэтому x относится к фактической ячейке памяти. В семействе языков C содержимое x - это всего лишь мусор, что-то осталось от предыдущего использования местоположения, поэтому x сам не указывает нигде &mdash, конечно, не на выделенное пространство.

Итак, естественно, мы можем взять адрес переменной x и поместить его где-нибудь, так что именно то, что мы делаем. Но мы перейдем к самому себе. Поскольку &x имеет другой тип, чем x, нам нужно сделать бросок, чтобы мы не получали предупреждения.

Модель памяти будет выглядеть примерно так:

0xbfd9198c
+------------+
| 0xbfd9198c |
+------------+

Итак, 4-байтовый блок памяти по адресу 0xbfd9198c содержит шаблон бита, соответствующий шестнадцатеричному значению 0xbfd9198c. Достаточно просто.

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

Мы можем видеть, что значения одинаковы, но только в очень низком уровне... их битовые шаблоны идентичны, но данные типа, связанные с каждым выражением, означают, что их интерпретируемые значения различны. Например, если мы напечатали x[0][0][0], используя строку формата %d, мы получили бы огромное отрицательное число, поэтому "значения" на практике различны, но бит-шаблон одинаков.

Это на самом деле очень просто... на диаграммах стрелки указывают только на тот же адрес памяти, а не на разные. Однако, хотя нам удалось принудительно вывести ожидаемый результат из поведения undefined, это просто:— undefined. Это не производственный код, а просто демонстрация для полноты.

В разумной ситуации вы будете использовать malloc для создания массива из 5 указателей int и снова для создания int, на которые указывает этот массив. malloc всегда возвращает уникальный адрес (если у вас не хватает памяти, и в этом случае он возвращает NULL или 0), поэтому вам никогда не придется беспокоиться о самореферентных указателях, подобных этому.

Надеюсь, что полный ответ вы ищете. Вы не должны ожидать, что x[0], x[0][0] и x[0][0][0] равны, но они могут быть принудительными. Если что-нибудь пойдет тебе на голову, дайте мне знать, чтобы я мог уточнить!