Как передать структуру в функцию.

uscr
Offline
Зарегистрирован: 17.08.2012

Здравствуйте. Возможно, вопрос вызван моими "плавающими" познаниями в Си. Однако, беглый гуглёж мне не помог и я прошу вашей помощи.

Есть такой код:

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 так, что бы иметь возможность изменить его внутри функции?

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Можете передать ссылку на  .foo (оператором &) или ссылку на всю стуктуру. При передаче переметра по ссылке параметр меняется из функции.

и лучше, наверно, будет

Joint *joint = &joint[i];

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Не совсем понятно, что такое joints, массив указателей или массив структур.

Если массив указателей, то

if( bar( joints[i] ) == true )

Если это массив структур, то

if( bar( &joints[i] == true )

Тогда вызов bar должен быть примерно такой, как

boolean bar( Joints *p_joint )
{
  boolean res = false;
  if( p_joint->i == 50 )
  {
    p_joint->foo = 0x55;
    res = true;
  }
  ...
  return res;
}

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

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Я так понял, что у него это массив структур. Потому что массив указателей - это весьма непросто (нужно самому заботиться о размещении самих структур), и если бы он это умел, этого вопроса бы не было.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Значит выберет второй вариант ;) А вообще ссылочный тип тоже не каждый знает, возможно и ошибка, потому я просто перестраховался.

leshak
Offline
Зарегистрирован: 29.09.2011

Да вобщем-то, в случае массивов и параметров - оно само догадывается "где тут указатели использовать" и можно пользоватся как обычной переменными. Только у функции указать ключевое слово 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(){
}

Вывод:

Is BAR, foo=3, foo*10=30
Is BAR, foo=5, foo*10=50
Not BAR, foo=7, foo*10=70

Данный код работает и компилится, но бывает что компилятору ардуины сносит мозги и он не хочет воспринимать структуру как параметр функции. Матюкается странными ошибками и не дает скомпилировать. Вообщем "известная ошибка".

Лечение - простое. Выносим объявление типа структуры Joint_t в отдельный .h и делаем ему #include

В этом случае компилятор начинает работать со структурами в качесве параметров без вытребенек. Да и в любом случае - это хороший тон, выносить всякие подобные объявления в отдельный файл. Так что вроде как и "не критичная проблема".

 

AlexFisher
AlexFisher аватар
Offline
Зарегистрирован: 20.12.2011

Нет, здесь не то, что нужно вопрошающему.

uscr пишет:

В функции bar я хочу сравнить joint.i с joint.j , вернуть ложь или истину, и некоторым образом изменить joint.foo.

Если бы речь шла только о сравнении, я бы передал i и j параметрами. Но как мне передать foo так, что бы иметь возможность изменить его внутри функции?

То есть передача "по значению" ему не подходит, нужно передавать "по ссылке". Тут нужно через указатель.

uscr
Offline
Зарегистрирован: 17.08.2012

kisoft пишет:

Не совсем понятно, что такое 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;}
}

Компилятор ругается:

 

detector_v5.cpp:13: ошибка: нет декларации ‘Joints’ в этой области видимости
detector_v5.cpp:13: ошибка: нет декларации ‘p_joint’ в этой области видимости
detector_v5.cpp: In function ‘void loop()’:
detector_v5.cpp:82: ошибка: ‘detect_approved’ нельзя использовать как функцию
detector_v5.cpp: At global scope:
detector_v5.cpp:92: ошибка: redefinition of ‘boolean detect_approved’
detector_v5.cpp:13: ошибка: ‘boolean detect_approved’ previously defined here
detector_v5.cpp:92: ошибка: нет декларации ‘Joints’ в этой области видимости
detector_v5.cpp:92: ошибка: нет декларации ‘p_joint’ в этой области видимости
 
Такие дела...
leshak
Offline
Зарегистрирован: 29.09.2011

AlexFisher пишет:

То есть передача "по значению" ему не подходит, нужно передавать "по ссылке". Тут нужно через указатель.

ну "просто добавь амперсанд" :)

 

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(){
}

 

Вывод:

foo=30
foo=50
foo=140

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Всё нормально, ничего особенного. Нужно написать

boolean detect_approved(struct Joint *p_joint) {

Номер строки - 20, как у Вас. Кроме этого тип структуры Joint а не Joints.

У меня это скомпилировалось.

Рекомендую форматировать текст, иначе очень сложно разбираться. (Ctrl-T помогает).

 

leshak
Offline
Зарегистрирован: 29.09.2011
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;}
}

 

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Да, передавать структуру с помощью ссылки - не рекомендую (и не только я, в книгах пишут).

По ссылкам передаются только константные структуры, которые не меняются, в этом случае пишется:



boolean foo( const struct Joint &joint )

Если нужно что то менять внутри структуры, передается по ссылке:

boolean foo( struct Jont *p_joint )

Спорить и переубеждать не собираюсь, писать и отлаживать программы вам.

 

leshak
Offline
Зарегистрирован: 29.09.2011

 

kisoft пишет:

Спорить и переубеждать не собираюсь, писать и отлаживать программы вам.

А зря. Раз уж взялись "учить стилю", то стоит и объяснить почему (если сами знаете) через указатель более "хороший стиль" (несмотря на большую громоздкость). Иначе это "магия авторитета", а не понимание. И начнут лепится указатели везде, где нужно и не нужно. Или наоборот "да вроде же и так работает" и забъют, на ваш, вообщем-то полезный совет. "Я сказал" - не самый лучий метод обучения програмированию.

А суть тут вот в чем, когда вы используете указатели, то в вызывающем коде вы ВЫНУЖЕНЫ писать амперсанд

detect_approved(&joint)

 

И тот кто будет читать код позже, сразу по тому что функция вызывается с амперсандом видит что "функция может изменить содержимое структуры". Это сразу "намек" что параметр изменяемый даже не глядя на реализацию/объявление самой функции. Тот случай когда "лишние буквы" - являеются полезными/информативными при дальнейшем сопровождении кода.

В случае же использования ампераснда в объявлении самой функции, вызов выглядит как

detect_approved(joint)

И то что join после этого может изменится - может стать неприятным сюрпризом при отладке (пока не полезешь смотреть реазацию функции detect_approved и не хлопнешь себя по лбу). Из вызывающего кода его "изменчивость" - не видна.

Но знать про такой способ - тоже нужно. Во первых что-бы понимать чужой код, во вторых бывает что "уже написана куча кода", когда структура была не изменяемой, а вот тут добавили одно поле которое будем менять. Встает альтернатива "через указатели что-бы по красивому" (и кучу замен делать, а не всегда это "код который дозволено менять" или просто добавить амперсанд в объявление самой функции. Сделать изменение в одном месте нечего больше не переписывая.

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Обучать? У меня такой цели нет. Подтолкнуть к самостоятельному изучению или понять, почему так советуют - это да. Вы обучаете - на мой взгляд этого более чем достаточно. Забьют на совет? Не вопрос, один забьёт, другой подумает, тема то останется и её прочитают не 2-3, а много больше народу. Более того, один хочет учиться, а другой - нет и это уже никакой учебой не заставишь сделать. Стараюсь отвечать на вопросы может не совсем коротко, но и не очень многословно, чтобы не наскучило читать (без намеков и подттекста).
Ардуино достаточно малообъемный контроллер, потому программы, как правило, не очень большие (разумеется бывают и монстры), отсюда, мой опыт программирования бОльших систем (несколько миллионов строк - не только моих, понятно) не всегда корректен к Ардуино программированию, это да, вполне возможно. Надеюсь понятно, что я не хвастаюсь, а просто привожу пример для сравнения, не более того. К тому же программирование в одиночку и в команде - совершенно разные стили и требования, одному проще. Вот и всё, что я хотел сказать.

uscr
Offline
Зарегистрирован: 17.08.2012

Ух, клёво!

Я поясню, что происходит:

Весь мой опыт программирования начинается и заканчивается на скриптах на bash'е и питоне. Так вот в питоне, я бы передал в функцию обьект ( detect_approved(joint)), а внутри функции делал бы с ним всё, что захочу. В Си с этим сложнее (или проще? в общем, по-другому), а читать с нуля чем указатель отличается от ссылки и как с ними работать мне лень (зато честно, да). А на таком вот конкретном примере некое понимание появилось. Всем спасибо.

dachnik
Offline
Зарегистрирован: 26.07.2013

Прошу помощи, есть код в нём структура

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); 

Строчка

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')

Ничего не выводит, хотя по замыслу должна вывести:

D_Out0

Где ошибка? как правильно передавать массив объектов структуры в функцию?

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

dancnik , вы спутали структуры с классами. http://www.c-cpp.ru/books/struktury

Для того что бы объявить переменную предствителя структуры надо писать 3 (три ) слова , а не 2 (два) слова как в классе.

struct database 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

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

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

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

qwone пишет:

вы спутали структуры с классами. 

Сиё невозможно, т.к. элементы определённые ключевыми словами class, struct и union - все являются классами.

qwone
qwone аватар
Offline
Зарегистрирован: 03.07.2016

del

kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

И здесь весна, обострение. Не форум, а сплошное словоблудие стало.
Всем удачи.

Клапауций 112
Клапауций 112 аватар
Offline
Зарегистрирован: 01.03.2017

kisoft пишет:
И здесь весна, обострение. Не форум, а сплошное словоблудие стало. Всем удачи.

Аллаху Акбар!