Как напечатать в Serial адрес указателя на функцию?

Draghkon
Offline
Зарегистрирован: 17.09.2013

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

Как получить конкретный адрес указателя на функцию?

Суть: 

есть указатель на функцию, например такой

typedef void (*CONTROL) (void);

CONTROL control;

Ему присваивается значение (имя функции) например так:

control=func1;

НО, ведь реально ему присваивается не имя функции а её адрес в памяти. насколько  я знаю адрес выражается в шестнадцатеричном формате. 

Вот этот вот самый адрес и нужно как-то извлечь и напечатать в Serial, и соответственно затем выполнить обратную операцию:

получить в Serial некое значение (адрес) и присвоить его указателю.

попытка напечатать так Serial.println(control);

дает ошибку, что несовместимый тип (void) с функцией Serial.

Да и присваивание числа отличного от нуля, например так:

control=0x3B5F;

дает ошибку что пытаемся что-то присвоить void.

Вопрос - как быть?

com
Offline
Зарегистрирован: 06.09.2013

можно нескромный вопрос - а зачем это?

Draghkon
Offline
Зарегистрирован: 17.09.2013

для введения и сохранения настроек.

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

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

 

Предвижу предложение костыля: передавать некий идентификатор (1,2,3,4) и по нему уже в программе определять какое имя функции присвоить указателю.

Но я для того и затеял это, чтобы не расписывать полотна Switch case..  помоему это плохой стиль, да и влом.

Looka
Offline
Зарегистрирован: 24.04.2012

Хотя стиль это вещь  субъективна.

Тем не менее есть калссики Кернига Ритчи, Страуструп.    Посмотрите их примеры.   Switch case  - не признак плохого стиля. 

А вот задавать адрес чего то нибыло в ручную - это точно не в правилах хорошего тона...

 

Думаю если Вы вниматеотно поработаете с кодом то большинство проблемм рассосется.

Ато что Вы просите, сделать можно, такой пример думаю Вам поможет

int  fun( int i )
{   
   return  2*i;
}

int (*af)(int) = fun;


void setup() {
  // put your setup code here, to run once:

   Serial.begin( 57600 );
   
   

}

void loop() {
  // put your main code here, to run repeatedly: 
 
 Serial.println( (unsigned long int)af, HEX );
 Serial.println( (unsigned long int)&fun, HEX );
 
 Serial.println( fun(2) );
 Serial.println( af(2) );
 af = (int(*)(int))  0x9e; 
 Serial.println( af(2) );
 
 while( 1 );   
  
}

 

 

 

Looka
Offline
Зарегистрирован: 24.04.2012

Draghkon пишет:

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

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

Draghkon
Offline
Зарегистрирован: 17.09.2013

СПАСИБО Огромное! 

Поковырял ваш пример, и вышло то что нужно!

Естетственно я не собирался задавать адреса в ручную, они ведь меняются все время (при перекомпиляции), суть была сделать примерно так:



void  fun()
{   
   Serial.println("QWERTY!");
}

void (*af)() = fun;


void setup() {
  // put your setup code here, to run once:
delay(3000);
   Serial.begin( 57600 );
   
   

}

void loop() {
  // put your main code here, to run repeatedly: 
 
 Serial.println( (unsigned long int)af, HEX );
 Serial.println( (unsigned long int)&fun, HEX );
 unsigned long var=(unsigned long int)af;
 Serial.println(var,HEX);
 af = (void(*)(void)) var; 
 af();
 while( 1 );   
  
}

Все работает, т.е. мы узнаем адрес указателя, присваиваем его переменной а из переменной обратно указателю, и все работает, печатается QWERTY, осталось проверить это с EEPROM!!

Только не совсем понял, зачем выводить af и &af - выводится одно и тоже значение, в чем разница, и что правильнее использовать для присвоения адреса переменной в строке 23?

Looka
Offline
Зарегистрирован: 24.04.2012

Draghkon пишет:

Только не совсем понял, зачем выводить af и &af - выводится одно и тоже значение, в чем разница, и что правильнее использовать для присвоения адреса переменной в строке 23?

 

Это пример, показть как с указателями функции работать.

Draghkon
Offline
Зарегистрирован: 17.09.2013

Блин... понял в чем косяк теперь... все дело в том что адреса меняются всякий раз.. т.е. если через однин скетч записывать, а потом просто закоментить строку записи, то адреса всех функций изменятся, и соотв. записанные в EEPROM указатели будут не туда указывать..  А я столько парился.. и мне говорили что RAM не хватает или еще что.. а  выходит что сохранить в EEPROM актульные указатели вообще нельзя!

Тогда остается только такой вариант: передавать актуальные адреса функций на компьютер, и затем уже передавать их обратно ввиде параметров настроек, и сохранять в EEPROM. Тогда адреса останутся актуальными по идее, но уже получается не торт.. Хотя если есть способ сделать автоматический листинг всех функций в скетче, то еще можно поразмыслить.

axill
Offline
Зарегистрирован: 05.09.2011

Не стоит использовать указатели так как вы хотите их использовать. Нарушается целостность вашей программы. Сколько у вас функций? Обьявите enum и с его помощью пронумеруйте все функции, адреса функций поместите в массив. Общение с компом не по адресу как вы задумали, а просто по номеру функции. Получив номер функции ардуино сначала проверяет что номер не выходит за допустимые пределы, а потом достав по номеру адрес функции из массива передает управление нужной функции. Этот вариант сохранит целостность - что бы не передал на входе комп, управление будет гарантировано передано по правильному адресу

да, кстати массив пдресов лучше поместить в програмную память используя PROGMEM (изучите как эти пользоваться). Это еще улучшит стмбильность и избежите лишнего расхода RAM

Предложенный мной метод по сути тот же Switch, но при большом числе функций он удобнее

 

Draghkon
Offline
Зарегистрирован: 17.09.2013

Спасибо,  так и сделал, уместилось буквально в 3 строки.  Насчет Progmem почитаю.

QQEVSKIY
Offline
Зарегистрирован: 12.02.2014

axill пишет:

да, кстати массив адресов лучше поместить в програмную память используя PROGMEM (изучите как эти пользоваться). Это еще улучшит стмбильность и избежите лишнего расхода RAM

насчет экономии памяти верно, а какая стабильность и при чем она здесь ?