Может ли указатель когда-либо указывать на себя?
Мой вопрос: если переменная-указатель имеет тот же адрес, что и ее значение, действительно ли она указывает на себя?
Например - в следующем фрагменте кода, является указатель на себя? a
#include<stdio.h>
int main(){
int* a;
int b = (int)&a;
a = b;
printf("address of a = %d\n", &a);
printf(" value of a = %d\n", a);
}
Если a
не является указателем на себя, то снова возникает тот же вопрос: может ли указатель на себя указать?
Кроме того, как полезен самонаправленный указатель?
Ответы
Ответ 1
То, что вы на самом деле делаете, - не, указатель указывает на себя. Вы используете пространство памяти, выделенное для указателя, чтобы сохранить местоположение указателя. Указатель на int указывает на ints - никогда другим указателям на ints, включая себя.
Например, предположим, что вы создаете указатель a
:
int * a;
Он получает свое место в памяти:
4 a (5) 6
[....][00000000][....]
В этом простом примере предположим, что a находится в ячейке памяти "5".
Если вы должны это сделать:
a = (int*)&a;
... произойдет следующее:
4 a (5) 6
[....][00000005][....]
Что здесь происходит, так это то, что a
указывает на то, что, по его мнению, является целым числом в местоположении 5. Это также является тем же самым местом памяти, на которое указывает &a
, но в контексте того, что a
указывает на то, что теперь он указывает на целое число в местоположении 5 - и это целое число равно 5.
Например, оба из них будут работать:
cout<<(int)a;//outputs 5
cout<<*a;//Outputs the integer at memory location 5 - which is 5.
Если вы хотите создать указатель на a, вы определенно можете - любой из этих способов:
int **b = (int**)a;
или
int ** b = &a;
Но очень важно понять, что a
не является указателем на себя. Это указатель на целое число в том месте, где он хранится - это просто так же, как и его собственное местоположение.
Чтобы показать (через еще более простой пример), что происходит, что-то подобное может произойти с int
. То есть вы можете сохранить ячейку памяти int
внутри себя:
int a=999;
a
теперь имеет место в памяти и имеет значение 999 (мы сделаем вид, что оно помещено в ячейку памяти 46):
45 a (46) 47
[....][00000999][....]
В месте "46" - если бы мы хотели, мы могли бы сохранить это число как целое число в пределах a
:
a=(int)&a;
45 a (46) 47
[....][00000046][....]
а теперь a
равен &a
по значению, но не в типе - a
- это просто целое число, оно не указывает на себя магическим образом только потому, что мы использовали его для хранения своего собственного места памяти.
Ответ 2
void* p = &p;
Это не очень полезно, но структуры, указывающие на себя, полезны в круговых списках длины 1:
typedef struct A {
struct A* next;
} A;
A a = { &a };
В вашем конкретном примере, я полагаю, вы имели в виду:
int* a;
int b = (int)&a;
a = (int*)b;
// which can be simplified to:
int* a = (int*)&a;
Ответ 3
Адрес памяти в FFFFFFFF
может хранить значение FFFFFFFF
Ответ 4
Ну, сначала я бы изменил код:
int **a;
a = (int **)&a; // otherwise you get a warning, since &a is int ***
Я не уверен, почему вы это сделаете, но это разрешено.
printf("The address of a is %p\n", &a);
printf("a holds the address %p\n", a);
printf("The value at %p is %p\n", a, *a); // the *a is why we made a an int **
Они должны распечатать то же самое.
The address of a is 0x7fffe211d078
a holds the address 0x7fffe211d078
The value at 0x7fffe211d078 is 0x7fffe211d078
Обратите внимание, что это не очень хорошая идея, так как это первая броска a = (int **)&a
- это взломать, чтобы заставить a
удерживать значение, которое оно не должно удерживать. Вы объявляете его int **
, но пытаетесь принудительно вставить в него int ***
. Технически размеры одного и того же, но в целом этого не делают, потому что люди ожидают, что int *
содержит адрес того, что может использоваться как int
, и так далее.
Ответ 5
Да и нет, потому что тип указателя почти так же важен, как и значение указателя.
Да, указатель может содержать позицию указателя на себя; даже длинный может содержать позицию указателя на себя. (Интс обычно может, но я не знаю, гарантировано ли это везде.)
Однако для этого отношения не существует. Если у вас есть указатель, указывающий на себя, у вас на самом деле есть другой тип, когда вы разыгрываете его. Итак:
void *p = &p;
// *p is illegal, even though you probably wanted it to equal 'p'
if( *p != p ) {
printf("Something wrong");
}
int *i = (int*)&i;
// The following statement is still illegal
if( *i == i ) {
printf("The universe works!");
}
Я бы сказал, что ответ "нет", потому что он не будет работать, если вы не будете злоупотреблять системой типов. Я считаю это показателем того, что вы делаете что-то неправильно (хотя иногда это обязательно необходимо).
Ответ 6
Выделение указателя приводит к значению его типа значения (например, разыменование int*
дает вам тип значения int
, int*
). Для переменной, указывающей на указатель, ее тип значения должен быть int*
, что не соответствует случаю int*
, как указано ранее. Поэтому для указателя, указывающего на себя, нужно было бы сделать какой-то бросок, чтобы получить его компилятором:
int* a;
a = reinterpret_cast<int*>(&a);
Чтобы разыскать a
, тогда у вас будет int
, значением которого является адрес, на котором находится a
(усечение мод адреса, соответствующее типу), но оно по-прежнему является int
, а не int*
, для чего потребуется другой литье.
Указатель на указатель часто известен как дескриптор, который является другим типом (int**
), чем указатель (int*
). (Обратите внимание, что дескриптор int**
имеет тип значения int*
.)
Ответ 7
Да, указатель может указать на себя, как указано в ответах.
Возможный случай использования - мы можем использовать этот трюк вместо указателей NULL. Вы можете инициализировать int * p = (int *) & p и проверить это позже, чтобы увидеть, является ли указатель действительным или нет. Это можно было бы использовать, если сказать в системе, где мы хотели, чтобы 0x0 и весь диапазон адресов были действительными адресами.
Вы также можете использовать этот трюк в специальных программах на C, которые не сбой при использовании NULL-указателя, потому что вы избегаете их вообще, а система разыменования не будет разбиваться. Это приведет к ошибочному поведению, но может помочь отладить в определенных ситуациях.
Ответ 8
Да, можно указать на себя.
int* a;
a = &a;
Но не используйте, по крайней мере, так явно.
Ответ 9
Просто:
int *a = (int*)&a;
cout<<"checking: \n"<<&a<<"\n"<<a;
Это указывает на себя.