Почему мне нужно определить ссылку на указатель в C с помощью *? - PullRequest
0 голосов
/ 28 апреля 2018

Почему это действительно:

int a = 5;
int *aPtr = &a;
printf("%i", *aPtr);

Но это не так:

int a = 5;
int aPtr = &a;
printf("%i", *aPtr);

Я не ищу ошибку, которую он выдает. Я пытаюсь понять, почему это не сработает концептуально.

Ответы [ 4 ]

0 голосов
/ 28 апреля 2018

int aPtr = &a; объявляет объект с именем aPtr с типом int, поэтому это не указатель.

Что еще хуже, sizeof(int) != sizeof(int *) на нескольких распространенных платформах (например, во всех 64-разрядных версиях Windows начиная с Windows XP), так что вы потеряете некоторую информацию, содержащуюся в указателе, возможно, сделав сохраненное значение бесполезным.

Однако, если он работает, aPtr по-прежнему является объектом типа int, поэтому вы не можете использовать косвенное указание для получения значения, хранящегося по адресу в aPtr, то есть выражение *aPtr будет не компилируется. Ничто не мешает вам привести aPtr к правильному типу указателя:

int a = 5;
int aPtr = &a;
printf("%i", *(int *)aPtr);

Опять же, это предполагает, что aPtr хранит полный адрес a. В любом случае, это всего лишь безопасность типов в игре, и это хорошо (см. sizeof(int) != sizeof(int *) параграф выше).

Если под «концептуально» вы подразумеваете «если адрес правильно хранится в aPtr», в вашем коде нет ничего плохого, кроме того факта, что он недопустим в C, как показано в приведенном выше примере приведения. Если адрес был 0x80, вы можете даже сохранить его в крошечном unsigned char объекте, если хотите. Он хранится по этому адресу в памяти, независимо от того, что С не позволяет вам делать с ним, отказываясь компилировать синтаксически неверный код. Конечно, гораздо проще просто использовать правильный тип переменной, вместо того, чтобы бороться с компилятором и приводить вещи.

С другой стороны, вы можете спросить: "Если &a хранится в aPtr, почему компилятор не может распознать это и просто понять, что *aPtr означает то же самое, что и *&a?" Если это вопрос, то ответ прост: как только вы сохраняете &a в объекте некоторого типа T, компилятор понимает, что этот объект используется для хранения значений типа T, и все. Он не знает, что он содержит адрес памяти, независимо от того, сохранен ли он в этом объекте или нет; он просто знает, что внутри него хранится значение типа T. Опять же, это просто система типов C., работающая как задумано.

0 голосов
/ 28 апреля 2018

Первый недопустим, потому что & является унарным оператором, который требует lvalue (переменную) и не может быть применен к rvalue (константе).

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

int a = 5;
int aPtr = &a;
printf("%i", *aPtr);

Затем статическая проверка типа C увидит, что aPtr имеет тип int, a имеет тип int (таким образом, &a имеет тип int*), а затем приведен (с предупреждением ) &a из int* в int, затем сохраните этот адрес как int в aPtr. Затем вы столкнетесь с ошибкой, когда * разыменует aPtr, поскольку aPtr имеет тип int. Поскольку вы произвели кастинг с int* на int, aPtr теперь является int, а не int*.

Если вы исправили это следующим образом:

int a = 5;
int aPtr = &a;
printf("%i", aPtr);

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

редактировать: избили до удара.

0 голосов
/ 28 апреля 2018

Ваш пример не работает, потому что:

sizeof(int)  == 4; // Bytes (usually)
sizeof(int*) == 8; // Bytes (assuming x86_64)

Следовательно, при назначении int aPtr = &a; самые старшие 4 байта будут потеряны.

Это будет работать только найти (хотя и не рекомендуется):

#include <stdint.h>
int main()
{
        int a = 5;
        uint64_t aPtr = (uint64_t)&a;
        printf("%i", *(int*)aPtr);
}
0 голосов
/ 28 апреля 2018

дело 1: -

int a = 5;
int *aPtr = &a; /*this is valid bcz aptr is pointer and it need address and `&a` is valid address*/
printf("%i", *aPtr);/* here you can do *aptr, bcz aptr is pointer and
                     we can dereference pointer variable */ 

Случай 2: - когда вы int aptr = &a; читаете предупреждение компилятора, компилируя с -Wall, оно говорит все.

int a = 5;
int aPtr = &a;/* aptr is normal variable, you are assigning address to it ,but you can't dereference it like *aptr bcz aptr is not pointer */     

Поскольку aptr является нормальной переменной, при выполнении *aptr выдается ошибка, так как оператор разыменования * применим к переменной-указателю, а не к нормальной переменной.

printf("%i", *aPtr); /* invalid */ 
...