Вложенные структуры и объявление функции - ошибка :(

str-ms
Offline
Зарегистрирован: 08.12.2013

Привет всем!

Никогда не программировал на С++, сейчас пытаюсь разобраться. Наткнулся на ошибку, которую не могу объяснить. Спецы, взгляните, пожалуйста, где я накосячил?

struct input_device
{                  
    uint8_t node;
    uint8_t addr; // ОШИБКА ТУТ
    uint8_t comptype;
    uint8_t data;
    uint8_t priority;
};

struct struct_rules
{                  
    char *ruleName;
    uint8_t node;
    uint8_t out_addr;
    uint8_t action;
    input_device input1;
    input_device input2;
    uint8_t priority;
    uint8_t duration;
    uint32_t dimmer;
};

struct_rules rules[5];


void setup() {
...
}


void loop() {
...
}


void addrule(struct_rules rule) {
  rules[1] = rule;
}

Что получается:

Структура со вложенной подструктурой активно используется, ошибок компилятора не вызывает (еще не отлаживалось, прога недописана). При попытке объявления функции addrule() компилятор выдает ошибку на строке объявления вложенной структуры:

SH_Node.ino:21:14: error: variable or field 'addrule' declared void
SH_Node.ino:21:14: error: 'struct_rules' was not declared in this scope

Была глупая мысль насчет совпадения первых 4 символов функции и переменной, но не подтвердилось, переименование функции ничего не дает, ошибка та же.

 

И второй глупый вопрос.

Если я создаю переменную типа struct_rules, заполняю её, в том числе заполняю данные подчиненной структуры:

struct_rules rules[5];
rule[1].node = 1;
...
rule[1].input1.node = 0;
rule[1].input1.addr = 5;
...

могу я потом в другую переменную передать данные только вложенной подструктуры?

input_device device1;
device1 = rules[1].input1;

Простите за глупые вопросты, прошу сильно не пинать начинающего ;)

str-ms
Offline
Зарегистрирован: 08.12.2013

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

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

По второму вопросу, в device1 будет копия из rules[1].input1

str-ms
Offline
Зарегистрирован: 08.12.2013

kisoft пишет:
По второму вопросу, в device1 будет копия из rules[1].input1

Спасибо, я так на это надеялся!

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

Другой вопрос, зачем держать копию, если можно использовать указатель.

str-ms
Offline
Зарегистрирован: 08.12.2013

kisoft пишет:
Другой вопрос, зачем держать копию, если можно использовать указатель.

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

А вообще я указатели понимаю пока только в общих чертах, практически еще с ними работать не умею.

А по первому вопросу - пока приходится использовать линейное программирование, дабы обойти ошибку :(

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

По первому вопросу много писать с телефона неудобно. Подобные темы есть на форуме.

ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015
str-ms,
 
по Вашему первому вопросу: так, как у Вас объявлено, выражение struct_rules не является полноценным типом данных и его не везде можно использовать как тип. Полное название типа - "struct struct_rules". Вот такая программа откомпилируется нормально.
struct input_device
{                  
    uint8_t node;
    uint8_t addr; 
    uint8_t comptype;
    uint8_t data;
    uint8_t priority;
};

struct struct_rules
{                  
    char *ruleName;
    uint8_t node;
    uint8_t out_addr;
    uint8_t action;
    input_device input1;
    input_device input2;
    uint8_t priority;
    uint8_t duration;
    uint32_t dimmer;
};

struct_rules rules[5];


void setup() {
//...
}


void loop() {
//...
}


void addrule(struct struct_rules rule) {	// обратите вниание на тип аргумента
  rules[1] = rule;
}
Только, знаете, Вы уверены, что хотите сделать именно это? При такой передаче параметра у Вас в функции addrule будет создаваться собственная копия аргумента. Это можно делать, если Вам это действительно нужно.
 
Если Вы, например,  хотите в этой функции менять структуру-аргумент, то не выйдет - Вы будете менять собственную копию. Если Вы хотите менять (или если у Вас нет причин создавать копию и Вам всё равно), то лучше передавать по ссылке, чтобы не тратить лишнюю память на копию и время на копирование.
 
По второму вопросу Вам уже ответили. Для Ваших типов данных такое присваивание сработает нормально.
kisoft
kisoft аватар
Offline
Зарегистрирован: 13.11.2012

Что бы не писать слово struct, можно использовать typedef в объявлении структуры.

str-ms
Offline
Зарегистрирован: 08.12.2013

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

по Вашему первому вопросу: так, как у Вас объявлено, выражение struct_rules не является полноценным типом данных и его не везде можно использовать как тип. Полное название типа - "struct struct_rules". Вот такая программа откомпилируется нормально.

Опа, как много я еще не знаю... Спасибо!

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

Только, знаете, Вы уверены, что хотите сделать именно это? При такой передаче параметра у Вас в функции addrule будет создаваться собственная копия аргумента. Это можно делать, если Вам это действительно нужно.

Если Вы, например,  хотите в этой функции менять структуру-аргумент, то не выйдет - Вы будете менять собственную копию. Если Вы хотите менять (или если у Вас нет причин создавать копию и Вам всё равно), то лучше передавать по ссылке, чтобы не тратить лишнюю память на копию и время на копирование.
 
Да, лишняя копия мне не нужна. Если не сложно, как будет выглядеть передача аргумента в виде ссылки? Пока не вкурил синтаксис указателей :(
ЕвгенийП
ЕвгенийП аватар
Offline
Зарегистрирован: 25.05.2015

str-ms пишет:

как будет выглядеть передача аргумента в виде ссылки? Пока не вкурил синтаксис указателей :(

В объявлении функции пишете знак & перед именем аргумента.

void addrule(struct struct_rules & rule)

Это не указатель, а именно ссылка, т.е. при вызове функции просто пишете имя переменной (без каких-либо значков), а внутри функции пользуетесь тоже как обычно "rule. ..." (безо всяких "->" и прочих страстей). 

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

Да, и ещё, в дополнение.

Если Вы реально НЕ хотите менять структуру аргумент внутри функции, то имеет смысл сказать об этом компилятору. Примерно так:

void addrule(const struct struct_rules & rule)

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

Это даёт сразу две выгоды:

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

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

 

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

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

str-ms
Offline
Зарегистрирован: 08.12.2013

Спасибо, получилось! Интересно, кто быстрее поумнеет, Умный дом с моей помощью или я с его? ;)

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

Не за что.

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