Как передать структуру в функцию.
- Войдите на сайт для отправки комментариев
Чт, 24/01/2013 - 14:17
Здравствуйте. Возможно, вопрос вызван моими "плавающими" познаниями в Си. Однако, беглый гуглёж мне не помог и я прошу вашей помощи.
Есть такой код:
struct Joint
{
byte i;
byte j;
byte foo;
};
Далее я в loop() делаю так:
for (int i=0; i<module_count; i++) {
Joint &joint = joints[i];
........
if (bar()==true) {
....
}
В функции bar я хочу сравнить joint.i с joint.j , вернуть ложь или истину, и некоторым образом изменить joint.foo.
Если бы речь шла только о сравнении, я бы передал i и j параметрами. Но как мне передать foo так, что бы иметь возможность изменить его внутри функции?
Можете передать ссылку на .foo (оператором &) или ссылку на всю стуктуру. При передаче переметра по ссылке параметр меняется из функции.
и лучше, наверно, будет
Не совсем понятно, что такое joints, массив указателей или массив структур.
Если массив указателей, то
Если это массив структур, то
Тогда вызов bar должен быть примерно такой, как
boolean bar( Joints *p_joint ) { boolean res = false; if( p_joint->i == 50 ) { p_joint->foo = 0x55; res = true; } ... return res; }Вы самое главное в исходнике не показали, потому приходится придумывать.
Я так понял, что у него это массив структур. Потому что массив указателей - это весьма непросто (нужно самому заботиться о размещении самих структур), и если бы он это умел, этого вопроса бы не было.
Значит выберет второй вариант ;) А вообще ссылочный тип тоже не каждый знает, возможно и ошибка, потому я просто перестраховался.
Да вобщем-то, в случае массивов и параметров - оно само догадывается "где тут указатели использовать" и можно пользоватся как обычной переменными. Только у функции указать ключевое слово struct
struct Joint_t { byte i; byte j; byte foo; }; Joint_t joins[]={ {1,1,3}, {2,2,5}, {3,4,7} }; #define TOTAL sizeof(joins)/sizeof(Joint_t) // сколько всего элементов bool bar(struct Joint_t jns){ return jns.i==jns.j; } void setup(){ Serial.begin(57600); for(byte i=0;i<TOTAL;i++){ if(bar(joins[i])){ Serial.print("Is BAR, foo="); } else { Serial.print("Not BAR, foo="); } Serial.print(joins[i].foo); Serial.print(", foo*10="); Joint_t localJoin=joins[i]; // а можем, для удобства и в локальную переменную вытащить Serial.println(localJoin.foo*10); } } void loop(){ }Вывод:
Данный код работает и компилится, но бывает что компилятору ардуины сносит мозги и он не хочет воспринимать структуру как параметр функции. Матюкается странными ошибками и не дает скомпилировать. Вообщем "известная ошибка".
Лечение - простое. Выносим объявление типа структуры Joint_t в отдельный .h и делаем ему #include
В этом случае компилятор начинает работать со структурами в качесве параметров без вытребенек. Да и в любом случае - это хороший тон, выносить всякие подобные объявления в отдельный файл. Так что вроде как и "не критичная проблема".
Нет, здесь не то, что нужно вопрошающему.
В функции bar я хочу сравнить joint.i с joint.j , вернуть ложь или истину, и некоторым образом изменить joint.foo.
Если бы речь шла только о сравнении, я бы передал i и j параметрами. Но как мне передать foo так, что бы иметь возможность изменить его внутри функции?
То есть передача "по значению" ему не подходит, нужно передавать "по ссылке". Тут нужно через указатель.
Не совсем понятно, что такое joints, массив указателей или массив структур.
Массив структур.
Покажу реальный кусок скетча:
struct Joint { byte modul_num; byte sens_type; byte code; byte sensor_pin; byte detect; byte event; unsigned long detect_delay; unsigned long last_on_timestamp; }; Joint joints[] = { {0, 0, 0, //modul_num, sens_type, code 2, 0, HIGH, //sensor_pin, detect, event 1000, 0 //detect_delay, last_on_timestamp }, };void loop() { for (int i=0; i<module_count; i++) { Joint &joint = joints[i]; if (joint.sens_type == 0) { if (digitalRead(joint.sensor_pin) == joint.event){ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (detect_approved(&joint)) {joint.detect = 1;} //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } else { ...} } } } boolean detect_approved(Joints *p_joint) { if ( p_joint->last_on_timestamp == 0 ) { p_joint->last_on_timestamp=millis(); return false; } if (millis()-p_joint->last_on_timestamp>p_joint->detect_delay) { return true; } else { return false;} }Компилятор ругается:
То есть передача "по значению" ему не подходит, нужно передавать "по ссылке". Тут нужно через указатель.
ну "просто добавь амперсанд" :)
struct Joint_t { byte i; byte j; byte foo; }; Joint_t joins[]={ {1,1,3}, {2,2,5}, {3,4,7} }; #define TOTAL sizeof(joins)/sizeof(Joint_t) // сколько всего элементов void bar(struct Joint_t &jns){ if(jns.i==jns.j) jns.foo=jns.foo*10; else jns.foo=jns.foo*20; } void setup(){ Serial.begin(57600); for(byte i=0;i<TOTAL;i++){ bar(joins[i]); Serial.print("foo="); Serial.println(joins[i].foo); } } void loop(){ }Вывод:
Всё нормально, ничего особенного. Нужно написать
boolean detect_approved(struct Joint *p_joint) {Номер строки - 20, как у Вас. Кроме этого тип структуры Joint а не Joints.
У меня это скомпилировалось.
Рекомендую форматировать текст, иначе очень сложно разбираться. (Ctrl-T помогает).
struct Joint { byte modul_num; byte sens_type; byte code; byte sensor_pin; byte detect; byte event; unsigned long detect_delay; unsigned long last_on_timestamp; }; Joint joints[] = { {0, 0, 0, //modul_num, sens_type, code 2, 0, HIGH, //sensor_pin, detect, event 1000, 0 //detect_delay, last_on_timestamp }, }; #define module_count sizeof(joints)/sizeof(Joint) void setup(){ } void loop() { for (int i=0; i<module_count; i++) { Joint joint = joints[i]; if (joint.sens_type == 0) { if (digitalRead(joint.sensor_pin) == joint.event){ //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! if (detect_approved(joint)) {joint.detect = 1;} //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! } else { // ... } } } } boolean detect_approved(struct Joint &p_joint) { if ( p_joint.last_on_timestamp == 0 ) { p_joint.last_on_timestamp=millis(); return false; } if (millis()-p_joint.last_on_timestamp>p_joint.detect_delay) { return true; } else { return false;} }Да, передавать структуру с помощью ссылки - не рекомендую (и не только я, в книгах пишут).
По ссылкам передаются только константные структуры, которые не меняются, в этом случае пишется:
Если нужно что то менять внутри структуры, передается по ссылке:
Спорить и переубеждать не собираюсь, писать и отлаживать программы вам.
Спорить и переубеждать не собираюсь, писать и отлаживать программы вам.
А зря. Раз уж взялись "учить стилю", то стоит и объяснить почему (если сами знаете) через указатель более "хороший стиль" (несмотря на большую громоздкость). Иначе это "магия авторитета", а не понимание. И начнут лепится указатели везде, где нужно и не нужно. Или наоборот "да вроде же и так работает" и забъют, на ваш, вообщем-то полезный совет. "Я сказал" - не самый лучий метод обучения програмированию.
А суть тут вот в чем, когда вы используете указатели, то в вызывающем коде вы ВЫНУЖЕНЫ писать амперсанд
И тот кто будет читать код позже, сразу по тому что функция вызывается с амперсандом видит что "функция может изменить содержимое структуры". Это сразу "намек" что параметр изменяемый даже не глядя на реализацию/объявление самой функции. Тот случай когда "лишние буквы" - являеются полезными/информативными при дальнейшем сопровождении кода.
В случае же использования ампераснда в объявлении самой функции, вызов выглядит как
И то что join после этого может изменится - может стать неприятным сюрпризом при отладке (пока не полезешь смотреть реазацию функции detect_approved и не хлопнешь себя по лбу). Из вызывающего кода его "изменчивость" - не видна.
Но знать про такой способ - тоже нужно. Во первых что-бы понимать чужой код, во вторых бывает что "уже написана куча кода", когда структура была не изменяемой, а вот тут добавили одно поле которое будем менять. Встает альтернатива "через указатели что-бы по красивому" (и кучу замен делать, а не всегда это "код который дозволено менять" или просто добавить амперсанд в объявление самой функции. Сделать изменение в одном месте нечего больше не переписывая.
Обучать? У меня такой цели нет. Подтолкнуть к самостоятельному изучению или понять, почему так советуют - это да. Вы обучаете - на мой взгляд этого более чем достаточно. Забьют на совет? Не вопрос, один забьёт, другой подумает, тема то останется и её прочитают не 2-3, а много больше народу. Более того, один хочет учиться, а другой - нет и это уже никакой учебой не заставишь сделать. Стараюсь отвечать на вопросы может не совсем коротко, но и не очень многословно, чтобы не наскучило читать (без намеков и подттекста).
Ардуино достаточно малообъемный контроллер, потому программы, как правило, не очень большие (разумеется бывают и монстры), отсюда, мой опыт программирования бОльших систем (несколько миллионов строк - не только моих, понятно) не всегда корректен к Ардуино программированию, это да, вполне возможно. Надеюсь понятно, что я не хвастаюсь, а просто привожу пример для сравнения, не более того. К тому же программирование в одиночку и в команде - совершенно разные стили и требования, одному проще. Вот и всё, что я хотел сказать.
Ух, клёво!
Я поясню, что происходит:
Весь мой опыт программирования начинается и заканчивается на скриптах на bash'е и питоне. Так вот в питоне, я бы передал в функцию обьект ( detect_approved(joint)), а внутри функции делал бы с ним всё, что захочу. В Си с этим сложнее (или проще? в общем, по-другому), а читать с нуля чем указатель отличается от ссылки и как с ними работать мне лень (зато честно, да). А на таком вот конкретном примере некое понимание появилось. Всем спасибо.
Прошу помощи, есть код в нём структура
struct ControlSignal { bool MODE; bool CONTROL; bool INTERLOCK; }; struct Status { bool inAuto; bool isRun; }; struct Parametr { float PV; byte Pin; byte EEPROM_ADR; }; struct add { ControlSignal CS; //вкладываем одну структуру в определение второй Status STS; Parametr PAR; String s_name; }; void list_object(add s_name); add DO[8]; //создаю массив объектов void structures() //присваиваю им имена { DO[0].s_name = "D_Out0"; DO[1].s_name = "D_Out1"; DO[2].s_name = "D_Out2"; DO[3].s_name = "D_Out3"; DO[4].s_name = "D_Out4"; DO[5].s_name = "D_Out5"; DO[6].s_name = "D_Out6"; DO[7].s_name = "D_Out7"; } void list_object(add s_name){ int m = sizeof(s_name) / sizeof(add*); display.clearDisplay(); display.setCursor(0,0); display.setTextSize(1); display.println(m); //выводит сколько объектов в массиве display.println(s_name.s_name[0] // ничего не выводит display.display(); } void loop() { list_object(*DO); }Так вот в функции list_object
void list_object(add s_name){ int m = sizeof(s_name) / sizeof(add*); display.clearDisplay(); display.setCursor(0,0); display.setTextSize(1); display.println(m); //выводит сколько объектов в массиве display.println(s_name[0].s_name);Строчка
Выводит размер массива корректно.
а вот строчка
выводит ошибку
screen.ino: In function 'void list_object(add)':
screen.ino:14:25: error: no match for 'operator[]' (operand types are 'add' and 'int')
Ничего не выводит, хотя по замыслу должна вывести:
Где ошибка? как правильно передавать массив объектов структуры в функцию?
dancnik , вы спутали структуры с классами. http://www.c-cpp.ru/books/struktury
Для того что бы объявить переменную предствителя структуры надо писать 3 (три ) слова , а не 2 (два) слова как в классе.
structdatabase functionName();// так не надо записывать struct ControlSignal { bool MODE; bool CONTROL; bool INTERLOCK; }; // лучше так struct str_ControlSignal { // <-- добавил для отличия тип структуры и представитель струтуры bool MODE; bool CONTROL; bool INTERLOCK; }; // тогда можно написать так struct str_ControlSignal ControlSignal={0,0,0};http://learnc.info/c/structures.html
Да все проще, он думает, что передает в функцию массив, а передает копию первого элемента массива.
Между структурой и классом очень маленькая разница в области видимости по умолчанию.
вы спутали структуры с классами.
Сиё невозможно, т.к. элементы определённые ключевыми словами class, struct и union - все являются классами.
del
И здесь весна, обострение. Не форум, а сплошное словоблудие стало.
Всем удачи.
Аллаху Акбар!