Массив это просто область памяти. Когда Вы объявляетe int M[10] кампилятор просто резервирует область памяти опрделенного размера (размер массива * на размер элемента массива, что опрееляется типом).
Соответсвенно когда вы ишите a = M[2], то вы обращаетесь к конкретному элементу, к содержимому памяти.
А когда вы пишите, например при вызове функции, fun( M ) (функция должна быть объявлна как void fun( int *) то функции будет передан указатель, иожно сказать адрес массива. Соответсвенно в теле функции ВЫ работаете как с обычным массивом. Только имейте ввиду что контроля за размером массива нет.
А вообще почитайте книжки по С, классику, очень увлекательно.
насколько я помню,то динамически созданный массив создаеться не в области 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() будет доступ к любму элементу данного массива
насколько я помню,то динамически созданный массив создаеться не в области stack,а в отдельной области,кажеться называеться heap
Все верно. Просто когда kisoft говорил про "здравствуй глюк" - он имел ввиду, если попытатся вернуть ссылку на обыкновенный, локальный массив. Созданный не через malloc() .
И так же, согласен с ним, если можно обойтись без динамических массивов - лучше обойтись. Скорее всего новичок не знающий "как вернуть массив" - не знает и про разницу стека и кучи. Даже зная - и то это резкое повышение потенциальных проблем. Это в теории все просто "выделил/освободи". А в сложной логике, когода еще эта ссылка потом протаскивается через 10-ток слоев абстракции - уследить за тем что-бы "количество взлетов и посадок совпадало" - очень не просто. И очень сложно искать такие ошибки (даже догадатся в чем причина). Нет, нет да и будет, то не освободил, то лишний раз выделил, то освободил раньше времени... А учитывая что у нас в ардуино нет отладчика - все сложности фикса таких багов умножаются на 10-ть.
Вообщем именно эти malloc()/free() являются, наверное самыми главными источниками багов в C/C++ програмах. И настолько они коварны, что одним из главных мотивов создания управляемых языков (Java/C#) было именно желания навсегда убрать необходимость вручную выделять/освобождать память.
Так что в итоге kirsoft прав. Если есть возможность - лучше массив пихать снаружи. А возвращать, только когда без динамики "ну вообще никак", или к примеру функции пихается три массива (уже готовых), а она должна вернуть ссылку на какой-то один из них по своей логике (но не создавать заново).
Подскажите как правильнее реализовать универсальный алгоритм управления мотора с входными переменными (готовность, автоматический режим, ручной режим, внешние аварии, т.д.) и выходными переменными(пуск мотора, ошибка мотора), чтобы правильно было.
Смотрел на Ваш пример, где используется структура, указатели, функции (по вытаскиванию времени), но хотелось бы поподробнее, язык си, никак не разберемся. Спасибо.
Спасибо за вопрос, к сожалению с моторами я не работал, потому помочь не смогу, однако готов помочь в конкретном вопросе с куском исходника. Массивы, массивы структур, массивы объектов, указатели, давайте конкретней, попробую помочь.
Если говорить проще - правильный исходник для функции, которая бы вызывалась несколько раз с разными аргументами и возвращала, к примеру три переменных. Как то так наверно это должно звучать. По моему тут должны использоваться указатели структуры и еще и массив для выходных, но как правильнее это дело оформить - стопор. Спасибо
"Разные аргументы" - разные типы или еще что-то под этим подразумевается? Можно конкретней, с примером?
Проще было бы не придумывать, а конкретней расписать задачу. Например, нужна функция для разрезания фрукта. На вход могут подаваться аргументы: Яблоко, Груша (разные типы!). На выходе функция должна возвращать разрезанный продукт, время реза. Либо другой вариант, на выходе должно быть, для Яблока - количество семечек, для Груши - толщина шкурки.
Почему нужно расписывать задачу подробно:
1. Для того, чтобы Закзчик сам понял, как это будет происходить, чтобы понял, какие ошибки он совершит, если опишет задачу неправильно.
2. Заказчик, в отличии от разработчика, может не знать каких то тонкостей инструмента (языка программирования).
3. Заказчик может попытаться выполнить задачу разработчика, т.е. решить, что нужна именно функция с такими то параметрами (входные и выходные), хотя разработчик может решить это много проще, написав две функции. Это как пример.
Практически все пытаются делать п.3. не показывая подробностей задачи, а ставят задачу, что нужна функция с такими то параметрами т.п. тем самым заранее обрекают себя на геморрой с использованием именно такой функции. Хотя разработчик мог предложить сделать две функции, которые легко можно было бы использовать для решения задачи.
Это всё абстрактно и в каждом конкретном случае может быть по-разному.
Не вдаваясь в подробности (я не знаю исходной задачи), чтобы, например, вернуть три пременных (они всегда одного и того же типа, т.е. каждая переменная имеет всегда один и тот же тип) можно поступить по-разному:
1. Передать указатели на три переменных. Внутри функции их значение изменить.
2. Поместить эти переменные в структуру и передать в функцию указатель на эту структуру. Внутри функции менять содержимое структуры.
Остальные случаи (а их еще есть несколько разных) рассматривать нет особого смысла. Будет день - будет пища.
Вроде бы всё конкретно и понятно написал. Надеюсь.
(Просьба найти и почитать, как правильно вставлять исходники в сообщения, это несложно).
Так тоже можно, однако есть минусы:
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. Есть и другие варианты: ссылка на указатель, но в данном случае - это уже перебор, потому не расматриваю.
Разумеется есть и другие варианты. Есть люди, которые рекомендуют использовать глобальные переменные, однако у этого способа есть довольно значительные минусы, плюс про это уже на форуме написано не мало, потому не буду на этом останавливаться.
В чем удобство структур, в том, что если параметров немало, то удобней хранить их "в однои месте", например, в структуре и передавать указатель на эту структуру, как параметр. Из плюсов - сокращение количества передаваемых параметров, из минусов: что там изменится внутри функции в этой структуре - Бог знает.
Вообще это всё философские вопросы, потому мои "советы" носят только рекомендательный ххарактер. Впрочем, кто не верит, тот, прогулявшись по граблям, узнает на практике, что не всегда лучше написать удобно, за то потом пользоваться с граблями, лучше один раз написать чуть больше значков и символов, так сказать, один раз помаяться, зато потом пользоваться с удовольствием.
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, т.е. как бы она не инициализирована,
а будет инициализирована только после выполнения ниже по коду при вызове функции, что делать?
Если ты не нальешь в чайник воду, но включишь его в розетку, что будет? В F2 сразу проверяется значение b3, а там мусор. Если это генератор случайных чисел, ради Бога, но если нужна адекватно работающая функция, то нужно проинициализировать эту переменную (b3) ДО вызова F2.
Ссылка - это не ВЫХОДНАЯ переменная, это IN/OUT переменная. Т.е. она получает извне какое то значение и может поменяться внутри функции.
Наливать в чайник будем каждый раз когда int main() вызовет инициализацию???? Перельем.
Реально у нас main отсутствует, у нас есть setup и есть loop, последний (loop) постоянно в цикле вызывается из main. Сам main от нас скрыт. Потому если инициализировать в loop локальную переменную, это можно сравнить с тем, что каждый раз чайник новый, поскольку после loop локальная переменная исчезает в никуда. Любая локальная переменная (если она НЕ static) будет уничтожена. Более того, локальные переменные НУЖНО инициализировать ДО их использования, иначе что там хранится, никто не знает. Перед стартом программы инициализируются только глобальные переменные. Это если по простому. Если копать глубже там еще много нюансов, но сейчас это не важно.
В первых шести байтах 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 - Почему, откуда?
}
Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк проходит конечно, но хотелось бы понять в чем проблема, т.к. данный костыль будет мешать в дальнейшем.
это не "костыль", а стандартное поведение компилятора. Локальные переменные не инициализуруются нулями или какими-то иными начальными значениями. Если вы сами их не инициализировали. в них будет содержаться случайный мусор. То что в данном случае в массив попадают байты, прочитанные из ЕЕПРОМ - не более чем случайность.
В первых шести байтах EEPROM содержится мак-адрес: DE AD BE EF FE EE. Подскажите пожалуйста, каким образом пустой arrMac[6] получает тоже значение что и arrLocal[6] ? Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк походит конечно, но хотелось бы понять в чем проблема
Владимир, если бы вы не просто поднимали старые темы. а еще и читали их - вы бы увидели, что прямо в самом последнем сообщении ветки написано следующее:
kisoft пишет:
локальные переменные НУЖНО инициализировать ДО их использования, иначе что там хранится, никто не знает. Перед стартом программы инициализируются только глобальные переменные.
В функции eepromRead() происходит работа с массивом arrLocal[6], т.е. начиная с адреса N заполняется шестью байтами данными. Затем, по завершению функции, память с адреса N не очищается, и более не резервируется под локальный массив.
Следующее использование памяти , начиная с адреса N, происходит при вызове printHexArr(arrMac, 6). Та же самая область памяти, которая была массивом arrLocal[6], попадает "в распечатку".
До вызова printHexArr() резервировать заранее 6 байт под arrMac смысла нет, ведь реальная работа с ним начинается на стр. 32. Компилятор это понимает и немного экономит ресурс, используя один и тот же фрагмент памяти последовательно.
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] и обнулит остальные (или обнулит и поставит).
Ребята, RTFM однако:) обратите внимание, что в #35 я написал, что это способ для нулей, заполнение массива единицами я не обещал:)
Если массив обьявлен глобально, он и так обнулится, без скобочек. Аптамуш, он попадает в сегмент инициализированных данных. А вот с локальными массивами всё не так просто. :)
Upd. Проверил, да ты прав, локальный массив можно скобочками инициализировать, но только нулём. Как только в скобочки ставишь другое значение - оно попадает в 1й элемент, а остальные инициализируются всёравно нулями.
http://arduino.ru/forum/programmirovanie/funktsiya-vozvrashchayushchaya-... - я тут про структуру спрашивал. Принцип тот же.
Если честно не очень понял..Мне нуджно внутри функции формировать массив...и возвращать его значения.
указатель на массив возвращать надо
Т.е как то вот так?
Уже не один раз писал, еще раз:
typedef struct ABC
{
uint8_t first;
uint8_t second;
};
void foo(ABC *p_par)
{
p_par->first = 5;
}
Если массив, то:
uint8_t art[5];
void foo(uint8_t Parr[])
{
Parr[3]=6;
}
Массив это просто область памяти. Когда Вы объявляетe int M[10] кампилятор просто резервирует область памяти опрделенного размера (размер массива * на размер элемента массива, что опрееляется типом).
Соответсвенно когда вы ишите a = M[2], то вы обращаетесь к конкретному элементу, к содержимому памяти.
А когда вы пишите, например при вызове функции, fun( M ) (функция должна быть объявлна как void fun( int *) то функции будет передан указатель, иожно сказать адрес массива. Соответсвенно в теле функции ВЫ работаете как с обычным массивом. Только имейте ввиду что контроля за размером массива нет.
А вообще почитайте книжки по С, классику, очень увлекательно.
указатель на массив возвращать надо
А вот этого лучше никогда не делать.
Указатель на какой массив возвращать? На локально объявленный или глобальный?
Если на глобальный, то зачем его возвращать, мы его и так знаем? Если на локальный, то здравствуй глюк.
Самое выгодное и правильное - передавать указатель на массив в виде параметра функции, тогда:
1. Достаточно универсально (можно использовать как локальные, так и глобальные массивы)
2. Можно использовать разные массивы
Если на глобальный, то зачем его возвращать, мы его и так знаем? Если на локальный, то здравствуй глюк.
malloc()?
P.S. Но, согласен, что, при этом, "выстрелить себе в ногу" - проще простого.
Такой вариант возможен, но предпочту создать массив вне функции, меньше шансов на ошибку. Вцелом создавать и освобождать память лучше на одном уровне.
насколько я помню,то динамически созданный массив создаеться не в области stack,а в отдельной области,кажеться называеться heap - может я селгка напутал с названиями но суть в том что выходя из функции,динамически созданный массив никак не "повреждаеться"- он остаеться в глобальном секторе памяти.и можно возвращать на него указатель
насколько я помню,то динамически созданный массив создаеться не в области stack,а в отдельной области,кажеться называеться heap
Все верно. Просто когда kisoft говорил про "здравствуй глюк" - он имел ввиду, если попытатся вернуть ссылку на обыкновенный, локальный массив. Созданный не через malloc() .
И так же, согласен с ним, если можно обойтись без динамических массивов - лучше обойтись. Скорее всего новичок не знающий "как вернуть массив" - не знает и про разницу стека и кучи. Даже зная - и то это резкое повышение потенциальных проблем. Это в теории все просто "выделил/освободи". А в сложной логике, когода еще эта ссылка потом протаскивается через 10-ток слоев абстракции - уследить за тем что-бы "количество взлетов и посадок совпадало" - очень не просто. И очень сложно искать такие ошибки (даже догадатся в чем причина). Нет, нет да и будет, то не освободил, то лишний раз выделил, то освободил раньше времени... А учитывая что у нас в ардуино нет отладчика - все сложности фикса таких багов умножаются на 10-ть.
Вообщем именно эти malloc()/free() являются, наверное самыми главными источниками багов в C/C++ програмах. И настолько они коварны, что одним из главных мотивов создания управляемых языков (Java/C#) было именно желания навсегда убрать необходимость вручную выделять/освобождать память.
Так что в итоге kirsoft прав. Если есть возможность - лучше массив пихать снаружи. А возвращать, только когда без динамики "ну вообще никак", или к примеру функции пихается три массива (уже готовых), а она должна вернуть ссылку на какой-то один из них по своей логике (но не создавать заново).
leshak, да, уже с полуслова иногда понимаем друг друга ;) К тому же ты прекрасно разжевал подтекст моего сообщения. Спасибо
Уважаемый kisoft!
Подскажите как правильнее реализовать универсальный алгоритм управления мотора с входными переменными (готовность, автоматический режим, ручной режим, внешние аварии, т.д.) и выходными переменными(пуск мотора, ошибка мотора), чтобы правильно было.
Смотрел на Ваш пример, где используется структура, указатели, функции (по вытаскиванию времени), но хотелось бы поподробнее, язык си, никак не разберемся. Спасибо.
Спасибо за вопрос, к сожалению с моторами я не работал, потому помочь не смогу, однако готов помочь в конкретном вопросе с куском исходника. Массивы, массивы структур, массивы объектов, указатели, давайте конкретней, попробую помочь.
Если говорить проще - правильный исходник для функции, которая бы вызывалась несколько раз с разными аргументами и возвращала, к примеру три переменных. Как то так наверно это должно звучать. По моему тут должны использоваться указатели структуры и еще и массив для выходных, но как правильнее это дело оформить - стопор. Спасибо
"Разные аргументы" - разные типы или еще что-то под этим подразумевается? Можно конкретней, с примером?
Проще было бы не придумывать, а конкретней расписать задачу. Например, нужна функция для разрезания фрукта. На вход могут подаваться аргументы: Яблоко, Груша (разные типы!). На выходе функция должна возвращать разрезанный продукт, время реза. Либо другой вариант, на выходе должно быть, для Яблока - количество семечек, для Груши - толщина шкурки.
Почему нужно расписывать задачу подробно:
1. Для того, чтобы Закзчик сам понял, как это будет происходить, чтобы понял, какие ошибки он совершит, если опишет задачу неправильно.
2. Заказчик, в отличии от разработчика, может не знать каких то тонкостей инструмента (языка программирования).
3. Заказчик может попытаться выполнить задачу разработчика, т.е. решить, что нужна именно функция с такими то параметрами (входные и выходные), хотя разработчик может решить это много проще, написав две функции. Это как пример.
Практически все пытаются делать п.3. не показывая подробностей задачи, а ставят задачу, что нужна функция с такими то параметрами т.п. тем самым заранее обрекают себя на геморрой с использованием именно такой функции. Хотя разработчик мог предложить сделать две функции, которые легко можно было бы использовать для решения задачи.
Это всё абстрактно и в каждом конкретном случае может быть по-разному.
Не вдаваясь в подробности (я не знаю исходной задачи), чтобы, например, вернуть три пременных (они всегда одного и того же типа, т.е. каждая переменная имеет всегда один и тот же тип) можно поступить по-разному:
1. Передать указатели на три переменных. Внутри функции их значение изменить.
2. Поместить эти переменные в структуру и передать в функцию указатель на эту структуру. Внутри функции менять содержимое структуры.
Остальные случаи (а их еще есть несколько разных) рассматривать нет особого смысла. Будет день - будет пища.
Вроде бы всё конкретно и понятно написал. Надеюсь.
а так можно ? и что неправильно? ведь работает вроде
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);
}
(Просьба найти и почитать, как правильно вставлять исходники в сообщения, это несложно).
Так тоже можно, однако есть минусы:
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. Есть и другие варианты: ссылка на указатель, но в данном случае - это уже перебор, потому не расматриваю.
Разумеется есть и другие варианты. Есть люди, которые рекомендуют использовать глобальные переменные, однако у этого способа есть довольно значительные минусы, плюс про это уже на форуме написано не мало, потому не буду на этом останавливаться.
В чем удобство структур, в том, что если параметров немало, то удобней хранить их "в однои месте", например, в структуре и передавать указатель на эту структуру, как параметр. Из плюсов - сокращение количества передаваемых параметров, из минусов: что там изменится внутри функции в этой структуре - Бог знает.
Вообще это всё философские вопросы, потому мои "советы" носят только рекомендательный ххарактер. Впрочем, кто не верит, тот, прогулявшись по граблям, узнает на практике, что не всегда лучше написать удобно, за то потом пользоваться с граблями, лучше один раз написать чуть больше значков и символов, так сказать, один раз помаяться, зато потом пользоваться с удовольствием.
а так можно ? и что неправильно? ведь работает вроде
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, т.е. как бы она не инициализирована,
а будет инициализирована только после выполнения ниже по коду при вызове функции, что делать?
Вам же написали:
(Просьба найти и почитать, как правильно вставлять исходники в сообщения, это несложно).
http://arduino.ru/forum/obshchii/vstavka-programmnogo-koda-v-temukommentarii
Вот
есть проблема ... что делать если выходная (ссылка) переменная в F1 - b3,
используется для присвоения значения переменной b1, т.е. как бы она не инициализирована,
а будет инициализирована только после выполнения ниже по коду при вызове функции, что делать?
Если ты не нальешь в чайник воду, но включишь его в розетку, что будет? В F2 сразу проверяется значение b3, а там мусор. Если это генератор случайных чисел, ради Бога, но если нужна адекватно работающая функция, то нужно проинициализировать эту переменную (b3) ДО вызова F2.
Ссылка - это не ВЫХОДНАЯ переменная, это IN/OUT переменная. Т.е. она получает извне какое то значение и может поменяться внутри функции.
И как же быть?
Может всё таки налить в чайник воду, прежде чем включать его в розетку, не?
Инициализировать ДО - желательно бы один раз и больше этого не делать, а от какого события никак не сообразим.
Наливать в чайник будем каждый раз когда int main() вызовет инициализацию???? Перельем.
Инициализировать ДО - желательно бы один раз и больше этого не делать, а от какого события никак не сообразим.
Ну это тебе видней. Задача мне неизвестна.
Инициализировать нужно всегда ДО использования, иначе будет лотерея.
PS Мы говорим про конкретный случай, про приведенный кусок кода, потому мои слова из контекста не нужно вырывать.
Наливать в чайник будем каждый раз когда int main() вызовет инициализацию???? Перельем.
Реально у нас main отсутствует, у нас есть setup и есть loop, последний (loop) постоянно в цикле вызывается из main. Сам main от нас скрыт. Потому если инициализировать в loop локальную переменную, это можно сравнить с тем, что каждый раз чайник новый, поскольку после loop локальная переменная исчезает в никуда. Любая локальная переменная (если она НЕ static) будет уничтожена. Более того, локальные переменные НУЖНО инициализировать ДО их использования, иначе что там хранится, никто не знает. Перед стартом программы инициализируются только глобальные переменные. Это если по простому. Если копать глубже там еще много нюансов, но сейчас это не важно.
В первых шести байтах EEPROM содержится мак-адрес: DE AD BE EF FE EE. Подскажите пожалуйста, каким образом пустой arrMac[6] получает тоже значение что и arrLocal[6] ? Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк походит конечно, но хотелось бы понять в чем проблема, т.к. данный костыль будет мешать в дальнейшем.
Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк проходит конечно, но хотелось бы понять в чем проблема, т.к. данный костыль будет мешать в дальнейшем.
это не "костыль", а стандартное поведение компилятора. Локальные переменные не инициализуруются нулями или какими-то иными начальными значениями. Если вы сами их не инициализировали. в них будет содержаться случайный мусор. То что в данном случае в массив попадают байты, прочитанные из ЕЕПРОМ - не более чем случайность.
Так что идея выполнить сначала
arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}
и есть самая правильное средство от этого "глюка"
В первых шести байтах EEPROM содержится мак-адрес: DE AD BE EF FE EE. Подскажите пожалуйста, каким образом пустой arrMac[6] получает тоже значение что и arrLocal[6] ? Если предварительно заполнить к примеру arrMac[] = {0xAA, 0xBB, 0xCC, 0xDD, 0xEE, 0xFF}, то глюк походит конечно, но хотелось бы понять в чем проблема
Владимир, если бы вы не просто поднимали старые темы. а еще и читали их - вы бы увидели, что прямо в самом последнем сообщении ветки написано следующее:
локальные переменные НУЖНО инициализировать ДО их использования, иначе что там хранится, никто не знает. Перед стартом программы инициализируются только глобальные переменные.
Локальные переменные не инициализуруются нулями или какими-то иными начальными значениями.
Чтобы не плодить константы заполнил его нулями в цикле.
Благодарю! Теперь все работает.
В функции eepromRead() происходит работа с массивом arrLocal[6], т.е. начиная с адреса N заполняется шестью байтами данными. Затем, по завершению функции, память с адреса N не очищается, и более не резервируется под локальный массив.
Следующее использование памяти , начиная с адреса N, происходит при вызове printHexArr(arrMac, 6). Та же самая область памяти, которая была массивом arrLocal[6], попадает "в распечатку".
До вызова printHexArr() резервировать заранее 6 байт под arrMac смысла нет, ведь реальная работа с ним начинается на стр. 32. Компилятор это понимает и немного экономит ресурс, используя один и тот же фрагмент памяти последовательно.
Я так это понимаю.
Чтобы не плодить константы заполнил его нулями в цикле.
Благодарю! Теперь все работает.
нулями можно проще:
нулями можно проще:
все
все
Я тоже так думал, но инициализируется только нулевой, в таком контексте
все
Результат: zero:0 0 0 0 0 1
Получается, что при инициализации нулевого элемента обнулятся все.
Т. е.:
byte zero[6] = {N} поставит N в zero[0] и обнулит остальные (или обнулит и поставит).
все
Я тоже так думал, но инициализируется только нулевой, в таком контексте
Больше похоже на то, что инициализируются все. В нулевом байте вводимое значение, в остальных нули.
Ребята, RTFM однако:)
обратите внимание, что в #35 я написал, что это способ для нулей, заполнение массива единицами я не обещал:)
Не подскажите, как вернуть массив из функции?
Никак!
ТС после энтого ни разу не влез в обсуждение. Видимо ему похер.
Если массив обьявлен глобально, он и так обнулится, без скобочек. Аптамуш, он попадает в сегмент инициализированных данных. А вот с локальными массивами всё не так просто. :)
Upd. Проверил, да ты прав, локальный массив можно скобочками инициализировать, но только нулём. Как только в скобочки ставишь другое значение - оно попадает в 1й элемент, а остальные инициализируются всёравно нулями.
Так все просто. Сколько значений в скобках поставишь - столько и проинициализируется. А ВСЕ ОСТАЛЬНОЕ - дополняется нулями.
Почему это никак? Можно использовать Struct или Tuple (насчёт последнего не уверен, что в Arduino он есть). А ТС, по всей видимости, реально забил :)
Можно использовать Struct или Tuple
что это??
кортеж в std::
https://en.cppreference.com/w/cpp/utility/tuple