Почему 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]
равны, но они могут быть принудительными. Если что-нибудь пойдет тебе на голову, дайте мне знать, чтобы я мог уточнить!