Есть ли способ, с помощью которого мы можем отменить ссылку на адрес памяти в C? - PullRequest
0 голосов
/ 31 января 2019

Я понимаю работу указателей в C. Указатели в основном хранят адрес памяти (say addr1), а разыменование указателей дает нам значение, сохраненное по этому адресу памяти addr1.Есть ли какой-нибудь возможный способ, с помощью которого мы можем напрямую отменить ссылку на адрес памяти addr1, чтобы получить значение, хранящееся в этом месте?

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

Например:

#define LOCATION 0x68feac //Assuming that the memory location is available

int main()
{
    int a = 10;
    *(int *)LOCATION = (int)&a;
    printf("%x\n", (*(int*)(LOCATION)));    //It gives me the address of a
    printf("%x\n", *((*(int*)(LOCATION)))); /* I thought it would de-refer but
                                                it gives me compile time error 
                                                "invalid type argument of unary 
                                                '*'" */

}

Я пытался использовать

Как отменить ссылку LOCATION, чтобы мы могли получить значение 10?Любые идеи или предложения помогут.

Ответы [ 4 ]

0 голосов
/ 31 января 2019

Очень часто встречается во всех разработках по внедрению встроенного ПО (не забывайте ключевое слово volatile).

*(volatile int *)LOCATION = a;

и способ его печати

printf("a is : %d\n, *(volatile int *)LOCATION); 

или если выхотите сохранить адрес a

*(volatile int **)LOCATION = &a;

и как его распечатать

printf("Address if a is : %p, a is: %d\n, (void *)*(volatile int **)LOCATION, **(volatile int **)LOCATION); 

Пример из реальной жизни:

#define GPIOA_BASE 0x40000000

#define GPIOA ((GPIO_TypeDef *) GPIOA_BASE)

{
  __IO uint32_t MODER;        /*!< GPIO port mode register,                                  Address offset: 0x00 */
  __IO uint16_t OTYPER;       /*!< GPIO port output type register,                           Address offset: 0x04 */
  uint16_t RESERVED0;         /*!< Reserved,                                                                 0x06 */
  __IO uint32_t OSPEEDR;      /*!< GPIO port output speed register,                          Address offset: 0x08 */
  __IO uint32_t PUPDR;        /*!< GPIO port pull-up/pull-down register,                     Address offset: 0x0C */
  __IO uint16_t IDR;          /*!< GPIO port input data register,                            Address offset: 0x10 */
  uint16_t RESERVED1;         /*!< Reserved,                                                                 0x12 */
  __IO uint16_t ODR;          /*!< GPIO port output data register,                           Address offset: 0x14 */
  uint16_t RESERVED2;         /*!< Reserved,                                                                 0x16 */
  __IO uint32_t BSRR;         /*!< GPIO port bit set/reset registerBSRR,                     Address offset: 0x18 */
  __IO uint32_t LCKR;         /*!< GPIO port configuration lock register,                    Address offset: 0x1C */
  __IO uint32_t AFR[2];       /*!< GPIO alternate function low register,                Address offset: 0x20-0x24 */
  __IO uint16_t BRR;          /*!< GPIO bit reset register,                                  Address offset: 0x28 */
  uint16_t RESERVED3;         /*!< Reserved,                                                                 0x2A */
}GPIO_TypeDef;

и использование:

GPIOA -> MODER = value; 

__IO определяется как volatile

#define __IO volatile
0 голосов
/ 31 января 2019

Вы делаете вещи в основном правильно, но указатель - это не просто другое целое число, и вы не можете использовать произвольные значения, вы должны использовать некоторые, которые выделены (то есть, заданы ОС) для программы.

  1. Чтобы иметь возможность разыменовать указатель, мы храним тип значения, хранящегося в памяти.Это означает, что вместо определения вы должны объявить int *LOCATION;, чтобы указать, что вы хотите указать на область памяти, где хранится целое число.

  2. Следовательно, вы не должны приводить указатель к int, потому что &a уже имеет тот же тип: указатель на int.

    Кроме того, вы не можете присвоить значения чему-то, что вы define.Определение не является типом - я уверен, что вы можете найти другой вопрос о том, что это такое.

  3. Получение адреса a до разыменования является правильным, поэтому не присваивайте произвольные значения LOCATION заранее.В лучшем случае присвойте ему NULL, чтобы указатель не был инициализирован.

Ваш код становится:

int* LOCATION = NULL;
int main()
{
    int a = 10;
    LOCATION = &a;
    printf("address of a: %lx\n", LOCATION);    // accessing the pointer accesses the memory address
    printf("value of a: %x\n", *LOCATION); // dereferencing the pointer with * gives the value stored at that address
} 

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

#define LOCATION_ADDR 0x68feac
int volatile * const LOCATION = (int *) LOCATION_ADDR; 
  • volatile здесь означает, что память, хранящаяся в этом месте, может измениться из-за факторов, внешних по отношению к программе.Это изменяет оптимизацию компилятора, например, цикл, который читает это местоположение до тех пор, пока он не изменится, будет удален без изменения.
  • const здесь означает, что расположение указателя не изменится, хотя содержащиеся в нем данные могут (отличаться отconst int!).
  • Как только вы освоитесь с этим, вы можете использовать это непосредственно в определении, то есть #define LOCATION ((volatile int*)0x68feac)

Если вы хотите аппаратное обеспечениезарегистрируйтесь, чтобы содержать указатель на int, просто добавьте еще один *.

#define LOCATION 0x68feac //Assuming that the memory location is available

int main()
{
    int a = 10;
    // dereferencing the address at LOCATION, storing into it the address of a
    *((volatile int **)LOCATION) = &a;
    printf("%p\n", *((volatile int **)LOCATION));
    printf("%x\n", **((volatile int **)LOCATION));
    return 0;
}
0 голосов
/ 31 января 2019

То, что вы запрашиваете:

printf("%x\n", *((int *)(*(int*)(LOCATION))));

Поскольку вам нужно преобразовать значение int, полученное из *(int*)(LOCATION), в указатель, прежде чем снимать с него ссылку.

Но вы действительноне делай этого!Это уродливый синтаксис, и компиляторы C отлично подходят для оптимизации бесполезных переменных.Поэтому сделайте себе одолжение и назовите промежуточные указатели:

#define LOCATION 0x68feac //Assuming that the memory location is available

int main()
{
    int a = 10;
    int **loc = (int **)LOCATION;  // BEWARE only make sense in very special use cases!
    *loc = &a;
    printf("%p\n", *loc);    //It gives me the address of a
    printf("%x\n", **loc);
    return 0;
}

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

0 голосов
/ 31 января 2019

Чтобы разыменовать int из заданного местоположения:

int val = *((int*) LOCATION);

((int*) LOCATION) дает вам РАСПОЛОЖЕНИЕ, приведенное к указателю int, поэтому последний шаг - разыменование его.

Пример (где точное указание LOCATION указывается как существующий указатель на целое число, но оно будет работать так же):

#include <stdio.h>

#define LOCATION ((void*) &a)

int main(void){
    int a = 123;
    printf("a=%d, &a=%p\n", a, &a);

    printf("*LOCATION=%d, LOCATION=%p\n", *((int*) LOCATION), LOCATION);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...