Вернуть массив из функции

Dima_gal
Offline
Зарегистрирован: 12.02.2013

Не подскажите, как вернуть массив из функции?

Пжл. Очень надо))

ustas
Offline
Зарегистрирован: 12.03.2012

http://arduino.ru/forum/programmirovanie/funktsiya-vozvrashchayushchaya-... - я тут про структуру спрашивал. Принцип тот же. 

Dima_gal
Offline
Зарегистрирован: 12.02.2013

Если честно не очень понял..Мне нуджно внутри функции формировать массив...и возвращать его значения.

medossa
Offline
Зарегистрирован: 10.07.2012

указатель на массив возвращать надо

Dima_gal
Offline
Зарегистрирован: 12.02.2013

Т.е как то вот так?

void myfun(float *mas)
{
//код программы изменяем массив
return *mas;
}

 

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

Уже не один раз писал, еще раз:
typedef struct ABC
{
uint8_t first;
uint8_t second;
};
void foo(ABC *p_par)
{
p_par->first = 5;
}

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

Если массив, то:
uint8_t art[5];

void foo(uint8_t Parr[])
{
Parr[3]=6;
}

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

Массив это просто область памяти.  Когда Вы объявляетe int M[10]  кампилятор просто резервирует область памяти  опрделенного размера  (размер массива * на размер элемента массива, что опрееляется типом).

Соответсвенно когда вы ишите  a = M[2],  то вы обращаетесь к конкретному элементу, к содержимому памяти.
А когда вы пишите,  например при вызове функции,   fun( M )  (функция должна быть объявлна как void fun( int *)  то функции будет передан указатель, иожно сказать адрес массива.   Соответсвенно в теле функции ВЫ работаете как с обычным массивом.   Только имейте ввиду что контроля за размером массива нет.

А вообще почитайте книжки по С, классику, очень увлекательно.

 

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

medossa пишет:

указатель на массив возвращать надо

А вот этого лучше никогда не делать.

Указатель на какой массив возвращать? На локально объявленный или глобальный?

Если на глобальный, то зачем его возвращать, мы его и так знаем? Если на локальный, то здравствуй глюк.

Самое выгодное и правильное - передавать указатель на массив в виде параметра функции, тогда:

1. Достаточно универсально (можно использовать как локальные, так и глобальные массивы)
2. Можно использовать разные массивы

 

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

kisoft пишет:

 

Если на глобальный, то зачем его возвращать, мы его и так знаем? Если на локальный, то здравствуй глюк.

malloc()?

P.S. Но, согласен, что, при этом, "выстрелить себе в ногу" - проще простого.

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

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

mixail844
Offline
Зарегистрирован: 30.04.2012

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









int* massive(int argument1,int argument2...int argumentN)
{
int *arr = (int*)malloc(sizeof(int)*N);
int i=0;
for(i=0;i<N;i++)
arr[i]=argument[i];
return arr;}//в данном случае функция вернет указатель на первую ячейку массива,и затем из функции main() будет доступ к любму элементу данного массива

 

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

 

mixail844 пишет:

насколько я помню,то динамически созданный массив создаеться не в области stack,а в отдельной области,кажеться называеться heap 

Все верно.  Просто когда kisoft говорил про "здравствуй глюк" - он имел ввиду, если попытатся вернуть ссылку на обыкновенный, локальный массив. Созданный не через malloc() . 

И так же, согласен с ним, если можно обойтись без динамических массивов - лучше обойтись. Скорее всего новичок не знающий "как вернуть массив" - не знает и про разницу стека и кучи. Даже зная - и то это резкое повышение потенциальных проблем. Это в теории все просто "выделил/освободи". А в сложной логике, когода еще эта ссылка потом протаскивается через 10-ток слоев абстракции - уследить за тем что-бы "количество взлетов и посадок совпадало" - очень не просто. И очень сложно искать такие ошибки (даже догадатся в чем причина). Нет, нет да и будет, то не освободил, то лишний раз выделил, то освободил раньше времени... А учитывая что у нас в ардуино нет отладчика - все сложности фикса таких багов умножаются на 10-ть.

Вообщем именно эти malloc()/free() являются, наверное самыми главными источниками багов в C/C++ програмах. И настолько они коварны, что одним из главных мотивов создания управляемых языков (Java/C#) было именно желания навсегда убрать необходимость вручную выделять/освобождать память.

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

 

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

leshak, да, уже с полуслова иногда понимаем друг друга ;) К тому же ты прекрасно разжевал подтекст моего сообщения. Спасибо

 

sbelb
Offline
Зарегистрирован: 16.06.2016

Уважаемый kisoft!

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

Смотрел на Ваш пример, где используется  структура, указатели, функции (по вытаскиванию времени), но хотелось бы поподробнее, язык си, никак не разберемся. Спасибо.

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

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

sbelb
Offline
Зарегистрирован: 16.06.2016

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

 

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

"Разные аргументы" - разные типы или еще что-то под этим подразумевается? Можно конкретней, с примером?

Проще было бы не придумывать, а конкретней расписать задачу. Например, нужна функция для разрезания фрукта. На вход могут подаваться аргументы: Яблоко, Груша (разные типы!). На выходе функция должна возвращать разрезанный продукт, время реза. Либо другой вариант, на выходе должно быть, для Яблока - количество семечек, для Груши - толщина шкурки.

Почему нужно расписывать задачу подробно:

1. Для того, чтобы Закзчик сам понял, как это будет происходить, чтобы понял, какие ошибки он совершит, если опишет задачу неправильно.

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

3. Заказчик может попытаться выполнить задачу разработчика, т.е. решить, что нужна именно функция с такими то параметрами (входные и выходные), хотя разработчик может решить это много проще, написав две функции. Это как пример.

Практически все пытаются делать п.3. не показывая подробностей задачи, а ставят задачу, что нужна функция с такими то параметрами т.п. тем самым заранее обрекают себя на геморрой с использованием именно такой функции. Хотя разработчик мог предложить сделать две функции, которые легко можно было бы использовать для решения задачи.

Это всё абстрактно и в каждом конкретном случае может быть по-разному.

Не вдаваясь в подробности (я не знаю исходной задачи), чтобы, например, вернуть три пременных (они всегда одного и того же типа, т.е. каждая переменная имеет всегда один и тот же тип) можно поступить по-разному:

1. Передать указатели на три переменных. Внутри функции их значение изменить.

2. Поместить эти  переменные в структуру и передать в функцию указатель на эту структуру. Внутри функции менять содержимое структуры.

Остальные случаи (а их еще есть несколько разных) рассматривать нет особого смысла. Будет день - будет пища.

Вроде бы всё конкретно и понятно написал. Надеюсь.

 

sbelb
Offline
Зарегистрирован: 16.06.2016

а так можно ?  и что неправильно? ведь работает вроде

void F1 (int a1, int a2, int &a3){

if (a1>a2){

a3=a1;

}

void F2 (int b1, int b2, int &b3){

 

F1 (b1,b2,b3);

}

void main{

int c;

F2(1,0,c);

}

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

(Просьба найти и почитать, как правильно вставлять исходники в сообщения, это несложно).

Так тоже можно, однако есть минусы:

1. Передача параметров через ссылку:

Плюсы: удобно, не надо париться с указателями.

Минусы: по исходнику не видно, какой из параметров может измениться, например: void foo(int a1, int2, int &a3), вызываем так: foo(par1, par2, par3) - скажи, как догадаться, что переменная par3 может измениться внутри функции? Сегодня помнишь, завтра - забудешь. Такие случаи достаточно сложно ловить. Чтобы этого избежать, правильней (рекомендуется) использовать для третьего параметра указатель.

2. Передача параметров через указатель:

Минусы: при передаче нужно передавать указатель (добавлять & к имени переменной). Внутри функции писать *par3 = par2 несколько не удобно.

Плюсы: При вызове видно, что передаваемая переменная может измениться, например: void foo(int a1, int2, int *a3), вызываем так: foo(par1, par2, &par3). Здесь сразу видно, что par3 "подозрительно" передается как указатель, значит она может измениться в функции. Это позволяет уменьшить риск наступания на грабли. И поверь, это достаточно веская причина использовать именно этот вариант.

3. Есть и другие варианты: ссылка на указатель, но в данном случае - это уже перебор, потому не расматриваю.

Разумеется есть и другие варианты. Есть люди, которые рекомендуют использовать глобальные переменные, однако у этого способа есть довольно значительные минусы, плюс про это уже на форуме написано не мало, потому не буду на этом останавливаться.

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

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

 

sbelb
Offline
Зарегистрирован: 16.06.2016

а так можно ?  и что неправильно? ведь работает вроде

void F1 (int a1, int a2, int &a3){

if (a1>a2){

a3=a1;

}

void F2 (int b1, int b2, int &b3){

if (b3>1){

b1 = b3;

}

F1 (b1,b2,b3);

}

void main{

int c;

F2(1,0,c);

}

есть проблема ... что делать если выходная (ссылка) переменная в F1  - b3,

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

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

 

 

Jeka_M
Jeka_M аватар
Offline
Зарегистрирован: 06.07.2014

Вам же написали:

kisoft пишет:

(Просьба найти и почитать, как правильно вставлять исходники в сообщения, это несложно).

http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukommentarii

sbelb
Offline
Зарегистрирован: 16.06.2016

Вот 

void F1 (int a1, int a2, int &a3){
if (a1>a2){
a3=a1;
}
void F2 (int b1, int b2, int &b3){
if (b3>1){
b1 = b3;
}
F1 (b1,b2,b3);
}
void main{
int c;
F2(1,0,c);
}

 

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

sbelb пишет:

есть проблема ... что делать если выходная (ссылка) переменная в F1  - b3,

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

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

Если ты не нальешь в чайник воду, но включишь его в розетку, что будет? В F2 сразу проверяется значение b3, а там мусор. Если это генератор случайных чисел, ради Бога, но если нужна адекватно работающая функция, то нужно проинициализировать эту переменную (b3) ДО вызова F2.

Ссылка - это не ВЫХОДНАЯ переменная, это IN/OUT переменная. Т.е. она получает извне какое то значение и может поменяться внутри функции.

 

sbelb
Offline
Зарегистрирован: 16.06.2016

И как же быть?

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

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

sbelb
Offline
Зарегистрирован: 16.06.2016

Инициализировать ДО - желательно бы один раз и больше этого не делать, а от какого события никак не сообразим.

sbelb
Offline
Зарегистрирован: 16.06.2016

Наливать в чайник будем каждый раз когда int main() вызовет инициализацию???? Перельем.

 

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

sbelb пишет:

Инициализировать ДО - желательно бы один раз и больше этого не делать, а от какого события никак не сообразим.

Ну это тебе видней. Задача мне неизвестна.

Инициализировать нужно всегда ДО использования, иначе будет лотерея.

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

 

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

sbelb пишет:

Наливать в чайник будем каждый раз когда int main() вызовет инициализацию???? Перельем.

 

Реально у нас main отсутствует, у нас есть setup и есть loop, последний (loop) постоянно в цикле вызывается из main. Сам main от нас скрыт. Потому если инициализировать в loop локальную переменную, это можно сравнить с тем, что каждый раз чайник новый, поскольку после loop локальная переменная исчезает в никуда. Любая локальная переменная (если она НЕ static) будет уничтожена. Более того, локальные переменные НУЖНО инициализировать ДО их использования, иначе что там хранится, никто не знает. Перед стартом программы инициализируются только глобальные переменные. Это если по простому. Если копать глубже там еще много нюансов, но сейчас это не важно.

 

vladimirneo
Offline
Зарегистрирован: 09.12.2020

В первых шести байтах EEPROM содержится мак-адрес: DE AD BE EF FE EE. Подскажите пожалуйста, каким образом пустой arrMac[6] получает тоже значение что и arrLocal[6] ? Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк походит конечно, но хотелось бы понять в чем проблема, т.к. данный костыль будет мешать в дальнейшем.

#include <avr/eeprom.h>

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

void loop() {
}

void printHexArr(byte *arr, byte arrSize){
  for (byte i = 0; i < arrSize; i++) {
    if (arr[i] < 16) Serial.print(0);
    Serial.print(arr[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

void eepromRead(){
  Serial.println("eepromRead()");
  byte arrLocal[6];
  eeprom_read_block((void*)&arrLocal, 0, 6);
  printHexArr(arrLocal, 6); // выводит DE AD BE EF FE EE
}

void getMac(){
  byte arrMac[6];
  //byte arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF};
  eepromRead();
  Serial.println("getMac()");
  printHexArr(arrMac, 6); // выводит DE AD BE EF FE EE - Почему, откуда?
}

 

b707
Онлайн
Зарегистрирован: 26.05.2017

vladimirneo пишет:

Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк проходит конечно, но хотелось бы понять в чем проблема, т.к. данный костыль будет мешать в дальнейшем.

это не "костыль", а стандартное поведение компилятора. Локальные переменные не инициализуруются нулями или какими-то иными начальными значениями. Если вы сами их не инициализировали. в них будет содержаться случайный мусор. То что в данном случае в массив попадают байты, прочитанные из ЕЕПРОМ - не более чем случайность.

Так что идея выполнить сначала

arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}

и есть самая правильное средство от этого "глюка"

b707
Онлайн
Зарегистрирован: 26.05.2017

vladimirneo пишет:

В первых шести байтах EEPROM содержится мак-адрес: DE AD BE EF FE EE. Подскажите пожалуйста, каким образом пустой arrMac[6] получает тоже значение что и arrLocal[6] ? Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк походит конечно, но хотелось бы понять в чем проблема

Владимир, если бы вы не просто поднимали старые темы. а еще и читали их - вы бы увидели, что прямо в самом последнем сообщении ветки написано следующее:

kisoft пишет:

локальные переменные НУЖНО инициализировать ДО их использования, иначе что там хранится, никто не знает. Перед стартом программы инициализируются только глобальные переменные.

vladimirneo
Offline
Зарегистрирован: 09.12.2020

b707 пишет:

Локальные переменные не инициализуруются нулями или какими-то иными начальными значениями. 

Чтобы не плодить константы заполнил его нулями в цикле.

  for (byte i = 0; i < sizeof(arrMac); i++) {
    arrMac[i] = 0;
  }

Благодарю! Теперь все работает.

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

В функции eepromRead() происходит работа с массивом arrLocal[6], т.е. начиная с адреса N заполняется шестью байтами данными. Затем, по завершению функции, память с адреса N не очищается, и более не резервируется под локальный массив.

Следующее использование памяти , начиная с адреса N, происходит при вызове printHexArr(arrMac, 6). Та же самая область памяти, которая была массивом arrLocal[6], попадает "в распечатку".

До вызова printHexArr() резервировать заранее 6 байт под arrMac смысла нет, ведь реальная работа с ним начинается на стр. 32. Компилятор это понимает и немного экономит ресурс, используя один и тот же фрагмент памяти последовательно.

Я так это понимаю.

b707
Онлайн
Зарегистрирован: 26.05.2017

vladimirneo пишет:

Чтобы не плодить константы заполнил его нулями в цикле.

  for (byte i = 0; i < sizeof(arrMac); i++) {
    arrMac[i] = 0;
  }

Благодарю! Теперь все работает.

нулями можно проще:

byte arrMac[6] = {0};

 

Feofan
Онлайн
Зарегистрирован: 28.05.2017

b707 пишет:

нулями можно проще:

byte arrMac[6] = {0};

Будут инициализированы все элементы массива или только нулевой?

b707
Онлайн
Зарегистрирован: 26.05.2017

Feofan пишет:
Будут инициализированы все элементы массива или только нулевой?

все

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

b707 пишет:

Feofan пишет:
Будут инициализированы все элементы массива или только нулевой?

все

Я тоже так думал, но инициализируется только нулевой, в таком контексте

Feofan
Онлайн
Зарегистрирован: 28.05.2017

b707 пишет:

все
Не спора ради...

void setup() {
  Serial.begin(38400);
  Serial.print("zero:");
  byte zero[6] = {1};
  //byte zero[6];
  for (int8_t i = 5; i >= 0; i--) {
    Serial.print(zero[i]);
    Serial.print(" ");
  }
}

void loop() {}

Результат: zero:0 0 0 0 0 1
Получается, что при инициализации нулевого элемента обнулятся все.
Т. е.:
byte zero[6] = {N} поставит N в zero[0] и обнулит остальные (или обнулит и поставит).

vladimirneo
Offline
Зарегистрирован: 09.12.2020

DetSimen пишет:

b707 пишет:

Feofan пишет:
Будут инициализированы все элементы массива или только нулевой?

все

Я тоже так думал, но инициализируется только нулевой, в таком контексте

Больше похоже на то, что инициализируются все. В нулевом байте вводимое значение, в остальных нули.

void setup() {
  Serial.begin(9600);
  byte forHeap1[12] = {9,9,9,9,9,9,9,9,9,9,9,9};
  arrInit0();
  arrInit3();
  arrIndefined();
}

void loop() {
}

void printHexArr(byte *arr, byte arrSize){
  for (byte i = 0; i < arrSize; i++) {
    if (arr[i] < 16) Serial.print(0);
    Serial.print(arr[i], HEX);
    Serial.print(" ");
  }
  Serial.println();
}

void arrInit0(){
  byte localArr[6] = {0};
  printHexArr(localArr, sizeof(localArr)); // выводит 00 00 00 00 00 00
}

void arrInit3(){
  byte localArr[6] = {3};
  printHexArr(localArr, sizeof(localArr)); // выводит 03 00 00 00 00 00
}

void arrIndefined(){
  byte localArr[6];
  printHexArr(localArr, sizeof(localArr)); // выводит 02 C1 02 C0 02 BF 

}

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Ребята, RTFM однако:)
обратите внимание, что в #35 я написал, что это способ для нулей, заполнение массива единицами я не обещал:)

mykaida
mykaida аватар
Offline
Зарегистрирован: 12.07.2018

Dima_gal пишет:

Не подскажите, как вернуть массив из функции?

Никак!

ТС после энтого ни разу не влез в обсуждение. Видимо ему похер.  

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

b707 пишет:
Ребята, RTFM однако:) обратите внимание, что в #35 я написал, что это способ для нулей, заполнение массива единицами я не обещал:)

Если массив обьявлен глобально, он и так обнулится, без скобочек.  Аптамуш, он попадает в сегмент инициализированных данных.  А вот с локальными массивами всё не так просто. :)

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

 

b707
Онлайн
Зарегистрирован: 26.05.2017

Так все просто. Сколько значений в скобках поставишь - столько и проинициализируется. А ВСЕ ОСТАЛЬНОЕ - дополняется нулями.

andyparker
Онлайн
Зарегистрирован: 20.12.2020

Почему это никак? Можно использовать Struct или Tuple (насчёт последнего не уверен, что в Arduino он есть). А ТС, по всей видимости, реально забил :)

b707
Онлайн
Зарегистрирован: 26.05.2017

andyparker пишет:

Можно использовать Struct или Tuple

что это??

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