Почему я должен использовать переменную двойного указателя для получения адреса другого указателя(&ptr1)

int num = 45,*ptr1,*ptr2;
ptr1=#
ptr2=&ptr1;
printf("%dn",*ptr1);

Я думал об этом вопросе некоторое время,но не мог найти способ понять его, почему &ptr1 не может быть назначено ptr2 в строке 3, &ptr1 - это адрес указателя,этот адрес ничем не отличается от других адрес как адрес целого числа, скажем

int a=1;
ptr2=&a;

что означает, что я могу назначить адрес целого числа указателю, но не адрес указателя указателю, какие различия между этими двумя "адресами" могут сделать их разными? Адрес общих переменных может быть назначен одному указателю, но адрес указателей не может быть назначен одному указателю?

Я знаю, что правильный способ сделать это использовать двойной указатель, чтобы объявить ptr2,но почему один указатель не может?

7 ответов


проще говоря, указатели не являются адресами, они представляют собой переменные, представляющие адрес с типом. Таким образом, типы должны быть совместимы для указателей для назначения (за исключением void * универсальный указатель).

ptr2 = &ptr1;

ptr1 имеет вид int *, Так что &ptr1 имеет вид int **, это не то же самое с ptr2, который имеет вид int *.

Ссылка: C99 6.5.16.1 простое задание

оба операнда указатели на квалифицированные или неквалифицированные версии совместимых типов, А тип, на который указывает левый, имеет все квалификаторы типа, на который указывает правый.


да can назначьте адрес указателя указателю, но он должен быть указатель на указатель переменной.

int **ptr3;
ptr3 = &ptr1;

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


ваш код неверен. Это выражение:

ptr2 = &ptr1;

пытается int * С int ** без приведения. Стандарт с ++ запрещает такие преобразования без явного приведения.

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

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

int num = 45;
void *ptr1, *ptr2;
ptr1 = #
ptr2 = &ptr1;

но это потребует от вас носить с собой всю информацию о типе каким-то другим способом:

printf("%d\n",*(int *)ptr1);
printf("%d\n",*(int **)ptr2);

короткий ответ заключается в том, что тип имеет значение; указатель на int другой, несовместимо тип указатель на указатель на int. Как уже упоминалось, разные типы указателей могут иметь разные размеры и представлений.

значение указателя-это не просто адрес, это имеет дополнительную семантику типа. Например, выражение ptr++ продвинет указатель на адрес следующего объекта базового типа. Если базовый тип char, тогда указатель расширен на 1 байт. Если базовый тип int указатель дополнительно sizeof (int) байт.


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

стандарт языка сообщает компилятору, что если есть int *

перейдите по адресу, хранящемуся в этой переменной, и используйте его.

В случае int ** тогда он говорит это

перейдите по адресу в этой переменной. Ты еще не закончил. и адрес. Идите туда и используйте то, что там есть.

Это продолжается и продолжается для int *** и так далее.

надеюсь, это поможет вам преодолеть эту основную путаницу.


Если вы можете назначить любой адрес любому указателю независимо от типа, на том основании, что один адрес так же, как и любой другой адрес, подумайте о проблеме, в которую вы могли бы попасть, если бы следующее стало законным:

int n = 40;
int * p = &n;
int ** pp = &n;  /* Typeless address assignment as you would like */
printf("%d\n", **pp); /* Bad Things happen here */

или наоборот:

int n = 40;
int * p = &n;
int * p2 = &p; /* More typeless address assignment */
printf("%d\n", *p2); /* Definitely not what you want */

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

причина, по которой вы не можете делать то, что вы предполагаете, что информация о типе, которую вы потеряете по вашему предложению, необходима для разыменования. Если бы все, что вам нужно было сделать, это сохранить и получить адреса, у вас был бы смысл, но они не просто используются для этого. Если бы это было так,мы могли бы просто ... --2-- > указатели и покончить с этим.


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

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

есть 3 пункта:

 1. The bits that are used while storing integer values differ from machine to machine.
    i.e. 32-bit, 64-bit and further more complications may add-up.

 2. Memory occupied i.e. bytes of data stored in it. Reason is : somewhere even the pointer variable is stored in memory. Right?

 3. There are certain operations associated with pointers like ++ or --.

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