Может ли указатель когда-либо указывать на себя? - PullRequest
39 голосов
/ 28 марта 2010

Мой вопрос: Если переменная-указатель имеет тот же адрес, что и ее значение, действительно ли она указывает на себя?

Например, - в следующем фрагменте кода есть 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 не является указателем на себя, то тот же вопрос возникает снова: Может ли указатель на себя указать?
Кроме того, чем полезен самонаправленный указатель?

Ответы [ 9 ]

38 голосов
/ 28 марта 2010
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;
33 голосов
/ 29 марта 2010

То, что вы на самом деле делаете, это не с указателем на себя. Вы используете пространство памяти, выделенное для указателя, для хранения местоположения указателя . Указатель на int указывает на целые, а не на другие указатели на целые, включая самого себя.

Например, допустим, вы создали указатель 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 это просто целое число, оно не указывает на себя магическим образом только потому, что мы его использовали хранить собственную ячейку памяти.

16 голосов
/ 05 мая 2010

Адрес памяти на FFFFFFFF может хранить значение FFFFFFFF

6 голосов
/ 28 марта 2010

Ну, во-первых, я бы изменил код:

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, и т. Д.

4 голосов
/ 29 марта 2010

Да и нет, потому что тип указателя почти так же важен, как и значение указателя.

Да, указатель может содержать позицию указателя на себя; даже long может содержать позицию указателя на себя. (Интс обычно может, но я не знаю, гарантировано ли это везде.)

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

void *p = &p;
// *p is illegal, even though you probably wanted it to equal 'p'
if( *p != p ) {
    printf("Something's wrong");
}

int *i = (int*)&i;
// The following statement is still illegal
if( *i == i ) {
    printf("The universe works!");
}

Я бы сказал, что ответ "нет", потому что он не сработает, если вы не собираетесь злоупотреблять системой типов. Я думаю, это признак того, что вы делаете что-то не так (хотя иногда это, безусловно, необходимо).

3 голосов
/ 29 марта 2010

Разыменование указателя приводит к значению его типа значения (например, разыменование int* дает вам int, int* s тип значения). Чтобы переменная указывала на указатель, ее тип значения должен был бы быть int*, что не так для int*, как указано ранее. Таким образом, чтобы указатель указывал на себя, нужно выполнить какое-то приведение, чтобы получить его компилятором:

int* a;
a = reinterpret_cast<int*>(&a);

Для разыменования a, тогда у вас будет int, значением которого будет адрес, по которому расположен a (усечение мода для адреса, соответствующего типу), но это все еще int, а не int*, что потребовало бы другого приведения.

Указатель на указатель часто известен как handle , который отличается от (int**) типа, чем указатель (int*). (Обратите внимание, что дескриптор int** имеет тип значения int*.)

1 голос
/ 15 ноября 2017

Да, указатель может указывать на себя, как указано в ответах.

Возможный вариант использования: мы можем использовать этот прием вместо указателей NULL. Вы можете инициализировать int * p = (int *) & p и проверить это позже, чтобы увидеть, действителен ли указатель или нет. Это можно было бы использовать, если, скажем, в системе, где мы хотели, чтобы 0x0 и весь диапазон адресов были действительными адресами.

Вы также можете использовать этот трюк в специальных программах на Си, которые не будут аварийно завершать работу при использовании указателя NULL, потому что вы будете избегать их вообще, а система разыменования не будет аварийно завершать работу. Это приведет к ошибочному поведению, но может помочь в отладке в определенных ситуациях.

0 голосов
/ 22 мая 2019

Просто:

int *a = (int*)&a;
cout<<"checking: \n"<<&a<<"\n"<<a;

Это указывает на себя.

0 голосов
/ 28 марта 2010

Да, можно указать на себя.

int* a;
a = &a;

Но не имеет никакого смысла, по крайней мере, явно так.

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