реклама на сайте
подробности

 
 
2 страниц V  < 1 2  
Reply to this topicStart new topic
> указатель на указатель, помочь разобраться
Arlleex
сообщение Jul 27 2018, 15:41
Сообщение #16


Местный
***

Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264



Попробую внести ясности-понятности.
Код
// допустим, Data разместилась по адресу 0x5000
// значение по адресу 0x5000 после присвоения будет равно 10
int Data = 10;

// допустим, pData разместилась по адресу 0x10000
// значение по адресу 0x10000 после присвоения будет равно 0x5000 (адрес Data)
int *pData = &Data;

// чтобы работать на чтение/запись с содержимым Data через pData, используем операцию разыменовывания
int a = *pData; // a == 10
*pData = 20;   // теперь Data == 20

// допустим, ppData разместилась по адресу 0x20000
// значение по адресу 0x20000 после присвоения будет равно 0x10000 (адрес pData)
int **ppData = &pData;

// чтобы работать на чтение/запись с содержимым pData через ppData, используем операцию разыменовывания
int *a = *ppData; // a == 0x10000
// чтобы получить доступ к переменной Data, используется двойное разыменовывание
int b = **ppData; // b == 20

У меня, например, двойной уровень косвенного обращения (указатель на указатель) используется в кольцевом буфере.
Имеется автомат, который расшифровывает принимаемые символы от фреймера (байт-стаффинг) по UART и складывает их в кольцевой буфер. Когда фрейм данных полностью принят, нужно как-то сообщить пользователю о размере принятого сообщения. Поэтому, перед тем, как я складирую принимаемые символы в кольцевой буфер, я резервирую в нем один байт для записи в него размера посылки после того, как весь фрейм будет принят. Чтобы не городить лишних телодвижений, я использую что-то наподобие вот такой конструкции:
Код
char *pMessageSize; // адрес элемента в кольцевом буфере, содержащий информацию о размере принятого сообщения

...

RingBufferReserveByte(&pMessageSize); // прототип RingBufferReserveByte(char **Address)

...

// после приема всей посылки прямой записью в память добавил в заранее зарезервированное место в кольцевом буфере информацию о размере сообщения, не думая по положениях head- и tail-указателей буфера
*pMessageSize = ByteCounter;

// ну а тут уже можно выдать семафор в основную программу, где она считывает первый элемент - видит размер сообщения - считывает его, перемещает позицию следующего чтения и, (если там еще что-то уже успело придти) двигается так дальше
Go to the top of the page
 
+Quote Post
Kabdim
сообщение Jul 27 2018, 15:42
Сообщение #17


Знающий
****

Группа: Свой
Сообщений: 558
Регистрация: 26-11-14
Из: Зеленоград
Пользователь №: 83 842



2Метценгерштейн: Видимо я утерял нить рассуждений. rolleyes.gif
Цитата(DASM @ Jul 27 2018, 18:22) *
Почему?

Соглашения по стилю обычно это определяют. Что по ссылке передаются данные которые долго копировать, но не нужно менять, а по указателям можно делать что угодно и это хорошо будет видно в коде благодаря звездочке/стрелке.
Go to the top of the page
 
+Quote Post
Herz
сообщение Jul 27 2018, 17:32
Сообщение #18


Гуру
******

Группа: Модераторы
Сообщений: 10 983
Регистрация: 23-11-05
Пользователь №: 11 287



Цитата(Kabdim @ Jul 27 2018, 18:02) *
В стеке аллокируется кадр под все аргументы

Простите, что делается? 05.gif
Go to the top of the page
 
+Quote Post
Arlleex
сообщение Jul 27 2018, 18:21
Сообщение #19


Местный
***

Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264



Цитата(Herz @ Jul 27 2018, 20:32) *
Простите, что делается? 05.gif

Выделяется (англ. allocate).
Но что там куда выделяется зависит еще все-таки от соглашения вызовов используемого компилятора.
Go to the top of the page
 
+Quote Post
Метценгерштейн
сообщение Jul 27 2018, 19:42
Сообщение #20


Профессионал
*****

Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079



Arlleex, спасибо за подробное изложение. Прошлись по основам. Подвели все к общему знаменателю.
Могли бы в том же духе первый пост разложить? Именно оригинальный пример из кода.
Go to the top of the page
 
+Quote Post
x893
сообщение Jul 27 2018, 19:46
Сообщение #21


Профессионал
*****

Группа: Свой
Сообщений: 1 333
Регистрация: 27-10-08
Из: Планета Земля
Пользователь №: 41 226



Для АРМ параметры передаются через регистры (пока хватает). Так что Ваши параметры после выхода просто пропадут. Проще посмотреть отладчиком, если лень книги читать.
Go to the top of the page
 
+Quote Post
Arlleex
сообщение Jul 28 2018, 11:15
Сообщение #22


Местный
***

Группа: Участник
Сообщений: 492
Регистрация: 12-11-11
Пользователь №: 68 264



Цитата(Метценгерштейн @ Jul 27 2018, 22:42) *
Могли бы в том же духе первый пост разложить? Именно оригинальный пример из кода.

Ну, попробую.
Код
// определение типа данных ble_advdata_tk_value_t
typedef struct
{
  uint8_t tk[BLE_GAP_SEC_KEY_LEN];
}ble_advdata_tk_value_t;


Код
// oob_key - указатель на любой объект в памяти, имеющий тип ble_advdata_tk_value_t
ble_advdata_tk_value_t *oob_key;

Допустим, для общего случая, что oob_key разместился по адресу 0x1000 и был объявлен в функции, поэтому без явной инициализации в нем будет лежать мусор. Значит по адресу 0x1000 сейчас лежит мусор.

В вызове
Код
err_code = nfc_tk_value_get(oob_key); // прототип ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t *pp_tk_value)

мы передаем копию значения oob_key (на данный момент мусор) в функцию nfc_tk_value_get(), а это значит, что, во-первых, в функцию передался мусор, а во-вторых, функция не сможет изменить значение по адресу 0x1000 (значение oob_key), поскольку она не знает адрес oob_key.
Поэтому повышается уровень косвенного обращения вызовом
Код
err_code = nfc_tk_value_get(&oob_key); // прототип ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t **pp_tk_value)

Здесь уже функции передается число 0x1000, и эта функция может что угодно делать с этим числом (это адрес oob_key) - например, инициализировать его адресом динамически созданного объекта типа ble_advdata_tk_value_t. В Вашем случае
Код
*pp_tk_value = &m_device_tk; // записать по адресу 0x1000 (то есть в ячейку oob_key) адрес объекта m_device_tk типа ble_advdata_tk_value_t


Go to the top of the page
 
+Quote Post
Метценгерштейн
сообщение Jul 28 2018, 16:59
Сообщение #23


Профессионал
*****

Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079



Спасибо. Упустил из вида момент, что
Код
ble_advdata_tk_value_t *oob_key;

это просто объявление переменной, но она непроинициализирована. А здесь
Код
err_code = nfc_tk_value_get(&oob_key);

мы просто передаем адрес (0х1000) этой переменной. Как обычно.
Go to the top of the page
 
+Quote Post
Метценгерштейн
сообщение Jul 29 2018, 05:19
Сообщение #24


Профессионал
*****

Группа: Свой
Сообщений: 1 357
Регистрация: 12-04-05
Из: Петербург
Пользователь №: 4 079



Спасибо. Упустил из вида момент, что
Код
ble_advdata_tk_value_t *oob_key;

это просто объявление переменной, но она непроинициализирована. А здесь
Код
err_code = nfc_tk_value_get(&oob_key);

мы просто передаем адрес (0х1000) этой переменной. Как обычно.

а вот здесь зачем указатель на указатель?
Код
ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t ** pp_tk_value)

Разве не достаточно было так:
Код
ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t * pp_tk_value)
{
    pp_tk_value = &m_device_tk;
}


Давайте аналогию приведем.

int a;
int *p;
p = &a;
Go to the top of the page
 
+Quote Post
andrew_b
сообщение Jul 29 2018, 07:27
Сообщение #25


Профессионал
*****

Группа: Свой
Сообщений: 1 975
Регистрация: 30-12-04
Из: Воронеж
Пользователь №: 1 757



Цитата(Метценгерштейн @ Jul 29 2018, 08:19) *
Давайте аналогию приведем.

int a;
int *p;
p = &a;

Давйте.
Код
int a;
int *p;
p = &a;
Это "снаружи" функции.
В самой функции параметр (например, p1) является копией p. Изменяя p1, вы не можете изменить p.
Код
   /* При вызове функции происходит следующее */
   int *p1 = p; /* в параметр копируется указатель, передаваемый в функцию */
  
   int b; /* локальная переменная функции */
   p1 = &b; /* изменяется p1, т.е копия p, но сам p не изменяется */
Go to the top of the page
 
+Quote Post
GeorgK
сообщение Aug 31 2018, 17:37
Сообщение #26





Группа: Участник
Сообщений: 9
Регистрация: 23-08-18
Пользователь №: 107 025



Возможно (вроде этого не было), стоит ещё упомянуть о передаче аргумента в функцию по адресу?

Код
void func(int &k) { k = 5};

....

int k = 0;
func(k);
//Здесь k равно 5.


Иногда это удобнее, если только не забывать, что изменение аргумента скажется на вызывающей функции.

Впрочем, для передачи сложных типов часто используется запись вроде func(const int &arg),
позволяющая не тащить при вызове в стек весь тип, а обойтись его адресом. А компилятор проследит, чтобы по ходу функции аргумент не был изменён.
Go to the top of the page
 
+Quote Post
DASM
сообщение Sep 1 2018, 04:17
Сообщение #27


Гуру
******

Группа: Свой
Сообщений: 3 644
Регистрация: 28-05-05
Пользователь №: 5 493



Давайте давайте учтите плохому.
"Within function parameter lists all references must be const:

void Foo(const string &in, string *out);
In fact it is a very strong convention in Googlecode that input arguments are values or const references while output arguments
are pointers." https://google.github.io/styleguide/cppguid...tput_Parameters
И это абсолютно правильно, делать ф-цию, вызов которой семантически выглядит как принимающая параметр по значении, а на деле давать ей неконстантную ссылку - форменное свинство
Go to the top of the page
 
+Quote Post
Professor Chaos
сообщение Sep 1 2018, 13:53
Сообщение #28


Участник
*

Группа: Участник
Сообщений: 60
Регистрация: 25-08-17
Пользователь №: 98 970



Цитата(Метценгерштейн @ Jul 29 2018, 08:19) *
а вот здесь зачем указатель на указатель?
Код
ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t ** pp_tk_value)

Разве не достаточно было так:
Код
ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t * pp_tk_value)
{
    pp_tk_value = &m_device_tk;
}

Вам надо понять (один раз и на всю жизнь), для чего в качестве аргумента функции передавать указатель на указатель.
Поймёте это - поймёте и ваш код.

То, что вы знаете, и о чём пишется в любом учебнике по С
1.Передавая объекты в функцию по-значению, вы сможете использовать их (значения), но не сможете их изменить. Все изменения аргументов внутри функции изменят лишь копии объектов, переданных в функцию, но не сами объекты. Т.е. изменение копии никак не отражается на оригинале, с которого копия была снята.
2. Чтобы можно было изменять значения объекта изнутри функции, в функцию в качестве аргумента надо передать не сам объект, а указатель на него. В этом случае в функцию передастся копия указателя на объект. Но т.к. копия указателя на объект указывает на тот же самый объект, что и исходный указатель, то этом случае функция через косвенную адресацию будет иметь доступ к такому объекту. Ведь в качестве параметра ей придёт указатель (т.е. фактически адрес) объекта. Зная адрес объекта (т.е. имея копию указателя на него) всегда можно изменить и сам объект, разыменовав указатель и присвоив ему новое значение.

А теперь то, что из этого следует, но на чём не акцентируется внимание в учебниках или вообще не говорится.
3. А что делать, если вам надо внутри функции изменить значение объекта имеющего тип указателя? Т.е. надо внутри функции присвоить указателю адрес другого объекта. Как такое сделать? Если передать в функцию сам указатель, то можно будет изменять тот объект, на который он указывает, но не сам указатель. А нам то этого не надо. Нам надо, чтобы указатель после выхода из функции указывал на другой объект (содержал его адрес). Вот для этого и приходится передавать в качестве аргументов функциям указатель на указатель.

Очень простой пример для иллюстрации сказанного в п. 3
Код
// Произвольный тип данных пользователя (предположим, int)
typedef int usertype_t;

// Функция, изменяющая значение указателя на объект типа usertype_t
// PtrToPtrToObj -указатель на указатель на объект типа usertype_t
// PtrToNewObj - указатель на новый объект
void SetPtrToNewObj (usertype_t** PtrToPtrToObj, usertype_t* PtrToNewObj) {
  *PtrToPtrToObj=PtrToNewObj;
}

int main (){    
    // Объекты типа usertype_t
  usertype_t ObjA, ObjB;
  
  // Указатель на объект типа usertype_t
  usertype_t* ObjPtr=&ObjA;       // Теперь ObjPtr указывает на объект ObjA
  
  // Меняем значение указателя. Нам нужно, чтобы он теперь указывал на другой объект
  // Эквивалент строки: ObjPtr=&ObjB;
  SetPtrToNewObj (&ObjPtr,&ObjB); // Теперь ObjPtr указывает на объект ObjB
}


А теперь зная это посмотрим на код:
Код
ret_code_t nfc_tk_value_get(ble_advdata_tk_value_t * pp_tk_value)
{
    pp_tk_value = &m_device_tk;
}

Что он делает?
pp_tk_value - это аргумент функции. Т.е. при вызове функции в него копируется значение типа ble_advdata_tk_value_t *. Т.е. это копия указателя на объект типа ble_advdata_tk_value_t. КОПИЯ указателя, а не сам указатель!
pp_tk_value = &m_device_tk; - что вы сделали этой строкой?
Вы присвоили КОПИИ УКАЗАТЕЛЯ новое значение. Но изменение КОПИИ никак не повлияло на оригинал. Оригинальный указатель остался прежним. А после выхода из функции pp_tk_value вышел из области видимости. Т.е. эта строка не изменила никаких объектов программы.
А должна была присвоить указателю новое значение - адрес объекта m_device_tk типа ble_advdata_tk_value_t.

Сообщение отредактировал Professor Chaos - Sep 1 2018, 15:08
Go to the top of the page
 
+Quote Post

2 страниц V  < 1 2
Reply to this topicStart new topic
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 


RSS Текстовая версия Сейчас: 18th April 2024 - 23:17
Рейтинг@Mail.ru


Страница сгенерированна за 0.01799 секунд с 7
ELECTRONIX ©2004-2016