передача структуры в функцию и её возврат с изменёнными членами

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Здравствуйте, уважаемые! Захотелось красивого кода, а главное удобного. Ситуация моя такова: есть 2 переменные типа float rpm; и uint16_t cnt_rpm, которые взаимосвязаны. Есть функция, которая вычисляет их значения. Моя идея объединить rpm и cnt_rpm в структуру, чего ранее не практиковал (прилагаю сначала рабочий код) 

struct dataT1{
  float rpm;    // начальное возможное число оборотов двигателя при clk/1
  uint16_t cnt_rpm;     // количество ячеек массива при делителе clk/1
} clk_1;

struct dataT1 *ptr;

void setup() 
{ Serial.begin(9600);

  ptr = &clk_1;

  clk_1.rpm = 1.0;
  clk_1.cnt_rpm = 11;
  Serial.print("clk_1.rpm = " + String(clk_1.rpm) + ";    ");
  Serial.println("clk_1.cnt_rpm = " + String(clk_1.cnt_rpm) + "; ");

  Serial.println("  ");

  get_struct(&clk_1);

  Serial.print("clk_1.rpm = " + String(clk_1.rpm) + "; ");
  Serial.println("clk_1.cnt_rpm = " + String(clk_1.cnt_rpm) + "; ");
}

void loop() 
{
}

void get_struct(struct dataT1 *clk1){
  clk1->rpm = 0.1;
  clk1->cnt_rpm = 22;
  //return clk1;
}

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

clk_1 = get_struct(&clk_1);

а описание ее такое:

dataT1 get_struct(struct dataT1 *clk1){
  clk1->rpm = 0.1;
  clk1->cnt_rpm = 22;
  return clk1;
}

Так у меня выдаёт ошибку... 

Подскажите, как правильно синтаксически написать такой код для функции

clk_1 = get_struct(&clk_1);

Может через указатель 

ptr = &clk_1;

как-то это делается...?

Спасибо!

sadman41
Offline
Зарегистрирован: 19.10.2016

Синтаксически правильно возвращать переменную того же типа, что и в декларации функции. А у Вас выходит, что по декларации подлежит возврату структура, но фактически возвращается указатель.

Декларируйте функцию на возврат указателя.

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017
dataT1 *get_struct(dataT1 *clk1){
  clk1->rpm = 0.1;
  clk1->cnt_rpm = 22;
  return clk1;
}

 

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

или 

void set_struct(dataT1 &clk1){
  clk1.rpm = 0.1;
  clk1.cnt_rpm = 22;
}

но, это лучше объявить методом самой этой структуры

 

b707
Offline
Зарегистрирован: 26.05.2017

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

DIYMan
DIYMan аватар
Offline
Зарегистрирован: 23.11.2015

Вот так:

dataT1* get_struct(struct dataT1 *clk1)
{
...
return clk1;
}

 

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Всем спасибо! Вот мой рабочий код:


struct dataT1{
  float rpm;        // начальное возможное число оборотов двигателя при clk/1
  uint16_t cnt_rpm;     // количество ячеек массива при делителе clk/1
} clk_1;

struct dataT1 *ptr;

void setup() 
{ Serial.begin(9600);

  ptr = &clk_1;

  clk_1.rpm = 1.0;
  clk_1.cnt_rpm = 11;
  Serial.print("clk_1.rpm = " + String(clk_1.rpm) + ";    ");
  Serial.println("clk_1.cnt_rpm = " + String(clk_1.cnt_rpm) + "; ");

  Serial.println("  ");

  ptr = get_struct(&clk_1);
    
  Serial.print("clk_1.rpm = " + String(clk_1.rpm) + "; ");
  Serial.println("clk_1.cnt_rpm = " + String(clk_1.cnt_rpm) + "; ");
}

void loop() 
{
}

dataT1 *get_struct(struct dataT1 *clk1){
  clk1->rpm = 0.1;
  clk1->cnt_rpm = 22;
  return clk1;
}

Я правильно всё сделал, так вроде все делают (просмотрел примеры в инете)? Выводит правильные значения членов структуры. Я так понял, чтобы, как писал b707, не создавать копию структуры, с указателем мой код единственный возможный?

DetSimen
DetSimen аватар
Offline
Зарегистрирован: 25.01.2017

AlexBajdin59rus пишет:

Я правильно всё сделал, ..... с указателем мой код единственный возможный?

Нет

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Приведите пожалуйста другой пример.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

так, у меня снова вопрос, 2 кода, оба работают:

  ptr = &clk_1;
  ptr = get_struct(ptr);
  ptr = &clk_1;
  ptr = get_struct(&clk_1);

где лучше/хуже/нюансы...? Я так понимаю 2-ой нагляднее показывает, что я передаю функции.

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

так, у меня снова вопрос, 2 кода, оба работают:

  ptr = &clk_1;
  ptr = get_struct(ptr);
  ptr = &clk_1;
  ptr = get_struct(&clk_1);

где лучше/хуже/нюансы...? Я так понимаю 2-ой нагляднее показывает, что я передаю функции.

это одно и то же. только во втором случае строка 1 лишняя

sadman41
Offline
Зарегистрирован: 19.10.2016

Конпилятору вообще пофиг на это жонглирование. 

Скорее всего оба варианта сведутся к условному

ptr = get_struct(&clk_1);

 

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

Я так понял, чтобы, как писал b707, не создавать копию структуры, с указателем мой код единственный возможный?

ошибок в нем нет, но он... несколько странный. Переменная ptr в этом коде вообще лишняя, вы ее ни разу не используете

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Хорошо, понял. Теперь вопрос вот в чём. Есть пример

uint8_t a, b;
uint16_t c;

void setup() {
  a = 10;
  b = 20;
  c = func(a, b);     // с будет равна 30
}

void loop() {

}

uint16_t func(uint8_t I, uint8_t J)
{
  return (I + J);
}

Как мне сделать что-то подобное, если у меня например структура с парами типами float и uint16_t

struct dataT1{
  float rpm1;        // начальное возможное число оборотов двигателя при clk/1
  float rpm8;        // начальное возможное число оборотов двигателя при clk/8
  uint16_t cnt_rpm1;     // количество ячеек массива при делителе clk/1
  uint16_t cnt_rpm8;     // количество ячеек массива при делителе clk/8
} clk_1;

Как мне описать функцию

dataT1 *get_struct(struct dataT1 *clk1){
  clk1->rpm = 0.1;
  clk1->cnt_rpm = 22;
  return clk1;
}

чтоб при первом вызове изменялись, например, члены rpm1 и cnt_rpm1, f при втором rpm8 и cnt_rpm8... Как я могу отличить в описании фунукции rpm1 от rpm8?  Я про  пример с 

uint16_t func(uint8_t I, uint8_t J)
{
  return (I + J);
}

 

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

Как мне описать функцию чтоб при первом вызове изменялись, например, члены rpm1 и cnt_rpm1, f при втором rpm8 и cnt_rpm8... Как я могу отличить в описании фунукции rpm1 от rpm8?

В изложенном примере - никак. Нужно создать две функции, одна будет менять rpm1 и cnt_rpm1, а другая rpm8 и cnt_rpm8

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

b707 пишет:

AlexBajdin59rus пишет:

Я так понял, чтобы, как писал b707, не создавать копию структуры, с указателем мой код единственный возможный?

ошибок в нем нет, но он... несколько странный. Переменная ptr в этом коде вообще лишняя, вы ее ни разу не используете

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

ptr = &clk_1;

 

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

AlexBajdin59rus, Вы неполностью задали вопрос, а коллеги почему-то повелись и начали отвечать неизвестно на что.

Я вот не знаю как Вам отвечать, т.к. Вы не сказали что должна возвращать Ваша функция - ту же самую структуру или отдельную копию с правильными значениями? Вы разницу понимаете? Если я начну менять поля у возвращённой - должны ли меняться у исходной?

И зачем Вы прицепились к указателям? Это не С, это С++. Используйте ссылки - всё красиво безо всякого синтаксического геморроя.

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

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

Вы завели переменную ptr , которая должна указывать на вашу структуру. Но вместо того чтоб использовать ptr при передаче ссылки в функцию и при доступе к полям структуры - вы везде используете не указатель ptr. а непосредственно адрес структуры &clk1. зачем тогда вообще нужен ptr? - если вы выкинете из кода строчки 6 и 11, где упоминается ptr , а строку 20 перепишете без возвращаемого значения:

get_struct(&clk_1);

у вас программа будет работать как ни в чем ни бывало без ptr

поэтому я и говорю, что ptr тут вообще лишний.

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

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

ЕвгенийП пишет:

AlexBajdin59rus, Вы неполностью задали вопрос, а коллеги почему-то повелись и начали отвечать неизвестно на что.

Я вот не знаю как Вам отвечать, т.к. Вы не сказали что должна возвращать Ваша функция - ту же самую структуру или отдельную копию с правильными значениями? Вы разницу понимаете? Если я начну менять поля у возвращённой - должны ли меняться у исходной?

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

ЕвгенийП пишет:

Если я начну менять поля у возвращённой - должны ли меняться у исходной?

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

ЕвгенийП пишет:

И зачем Вы прицепились к указателям? Это не С, это С++. Используйте ссылки - всё красиво безо всякого синтаксического геморроя.

Я пока не проффи в программировании и поэтому пока не знаю разницы в Си и Си++...

Спасибо! Можно тогда от Вас подробное разъяснение, если вас не затруднит.

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

Нет. с ссылками возвращается таже самая тоже. Новая не создаётся. Но при этом синтаксис гораздо приятнее.

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

b707 пишет:

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

Тогда как понимать это...?

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

У меня осталось 15 мин, вечером буду пробовать разбираться, но хотелось бы от Вас увидеть рабочий пример, если есть время конечно :). Пример с поста №1, самый простой

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Просто хотелось больше наглядности в коде, допустим 

clk_1 = get_struct(здесь теперь не знаю что вставить);

то есть сразу видно, что именно структура (левый операнд) изменяет значения своих членов функцией get_struct();

Mожет быть я ввожу в заблуждение названием функцией (нужно написать set_struct(); а не get_struct();

sadman41
Offline
Зарегистрирован: 19.10.2016

Напишите так: void changeParameters(dataStruct_t& _dataStruct) {...}. Зачем ptr-то лохматить... Сами потом запутаетесь на кой он сдался, а вдобавок кучу ворнингов от компилятора получите.

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

b707 пишет:

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

Тогда как понимать это...?

Я имел в виду, что в процедуру следует передавать или указатель (ptr), или адрес структуры (&clk_1) - а не саму структуру - clk_1, как вы это делали в самом первом своем примере

Добавка - впрочем, читаю других и вижу, что во многом тоже плаваю, так что лучше слушайте Садмана и Евгения

 

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

b707 пишет:

AlexBajdin59rus пишет:

b707 пишет:

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

Тогда как понимать это...?

Я имел в виду, что в процедуру следует передавать или указатель (ptr), или адрес структуры (&clk_1) - а не саму структуру - clk_1, как вы это делали в самом первом своем примере

 

Простите, но я же вроде и передал адрес структуры (&clk_1) в самом первом примере...

get_struct(&clk_1);

разве нет...?

b707
Offline
Зарегистрирован: 26.05.2017

AlexBajdin59rus пишет:

Простите, но я же вроде и передал адрес структуры (&clk_1) в самом первом примере...

get_struct(&clk_1);

разве нет...?

прошу прощения, неточно выразился - передали то вы адрес, а возвращать пытались саму структуру

AlexBajdin59rus
Offline
Зарегистрирован: 17.12.2019

Ага, понял, тогда мой самый первый пример правильный для работы кода по ссылкам? Вопрос Евгению. Просто убрать 6 и 11 строки