Функция, возвращающая несколько переменных

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

Здравствуйте. Делаю простое устройство- часы с температурой и большим светодиодным индикатором. Написал первый вариант скетча, заработало. 

#include <Wire.h>/Подключим библиотеку для АйТуСи
#include <QuadDisplay.h>
#include <OneWire.h>
#define PIN 12 //Дисплей на 12 ноге
OneWire  ds(8);//Датчик температуры на 8 ноге
byte addr[8]={0x28,0xFF,0xE9,0x6B,0x65,0x15,0x02,0x5A};// адрес датчика DS18B20 
volatile int celsius;
volatile int counter=0;          

void setup() {
        Serial.begin(9600); //Инициализируем  скорость сериала
              WDTCSR=(1<<WDCE)|(1<<WDE); //установить биты WDCE WDE (что б разрешить запись в другие биты
              WDTCSR=(1<<WDIE)| (1<<WDP2)|(1<<WDP1); // разрешение прерывания + выдержка 1 секунда
             // (55 страница <a href="<a href="http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf" rel="nofollow">http://www.atmel.com/images/Atmel-8271-8-bit-AVR-Microcontroller-ATmega48A-48PA-88A-88PA-168A-168PA-328-328P_datasheet_Complete.pdf</a>" rel="nofollow">даташита</a>)
             // снять все ремарки если нужно поменять разрешение
             //  ds.reset(); // сброс шины
             //  ds.select(addr); //выставить адрес
             //  ds.write(0x4E); // разрешение записать конфиг
             //  ds.write(0x7F); // Th контроль температуры макс 128грд
             //  ds.write(0xFF); //Tl контроль температуры мин -128грд
             //  ds.write(0x60); // 0x60 12-бит разрешение, 0x00 -9бит разрешение
  
  
                        Wire.begin(); //Инициализируем хронометр ДС3231
                        Wire.beginTransmission(0x68);
                        Wire.write(0x0E); //В контрольном регистре включаем генератор и выводим 1Герц на ЭсКуВэ
                        Wire.write(0b00000000);
                        Wire.endTransmission();
             }

             


             

void loop() {

          float mytemp= (celsius/16.0);
          Serial.print("Temperatura = "); //Выводим в сериал 
          Serial.println(mytemp);
  
  
  byte binsec; //секунды в БЦД для считывания из регистра хронорметра
  byte binmin;
  byte binhour;
  
  
        Wire.beginTransmission(0x68); //Читаем секунды из байта 00h
        Wire.write(0x00);  
        Wire.endTransmission();
        Wire.requestFrom(0x68, 3);
        //while(Wire.available()) 
         binsec = Wire.read();
         binmin = Wire.read();
         binhour = Wire.read();
  
  byte star=(binsec&0b11110000); //преобразуем секунды в десятичное значение, разбивая байт на тетрады
  star=star>>4;
  byte mlad=(binsec&0b00001111);
  int dessec=(star*10+mlad);
  
          Serial.print("binsec = "); //Выводим в сериал отладочную информацию
          Serial.println(binsec, BIN);
          Serial.print("star = ");
          Serial.println(star, DEC);
          Serial.print("mlad = ");
          Serial.println(mlad, DEC);
          Serial.print("sec = ");
          Serial.println(dessec, DEC);
          Serial.println("******");

               

                       star=(binmin&0b11110000); //преобразуем минуты в десятичное значение, разбивая байт на тетрады
                       star=star>>4;
                       mlad=(binmin&0b00001111);
                       int desmin=(star*10+mlad);
  
                       Serial.print("binmin = "); //Выводим в сериал отладочную информацию
                       Serial.println(binmin, BIN);
                       Serial.print("star = ");
                       Serial.println(star, DEC);
                       Serial.print("mlad = ");
                       Serial.println(mlad, DEC);
                       Serial.print("min = ");
                       Serial.println(desmin, DEC);
                       Serial.println("*******");


                                  star=(binhour&0b11110000); //преобразуем часы в десятичное значение, разбивая байт на тетрады
                                  star=star>>4;
                                  mlad=(binhour&0b00001111);
                                  int deshour=(star*10+mlad);
  
                                  Serial.print("binhour = "); //Выводим в сериал отладочную информацию
                                  Serial.println(binhour, BIN);
                                  Serial.print("star = ");
                                  Serial.println(star, DEC);
                                  Serial.print("mlad = ");
                                  Serial.println(mlad, DEC);
                                  Serial.print("deshour = ");
                                  Serial.println(deshour, DEC);
                                  Serial.println("xxxxxxxx");

                                  //Serial.print(deshour); //Выводим в сериал время в привычном формате
                                  Serial.print(deshour, DEC);
                                  Serial.print(" : ");
                                  Serial.print(desmin, DEC);
                                  Serial.print(" : ");
                                  Serial.println(dessec, DEC);
                                  //Serial.print("deshour = ");
                                  //Serial.println(deshour, DEC);
                                  Serial.println("_____________________________________");
            if(counter==2) {
               displayInt(PIN, (mytemp*10) ,false,0b00001000);
                           }
            else           {               
            int hourmin = ( deshour*100+desmin);
            displayInt(PIN, hourmin , true);
                           }







  
  delay(1000);
}


ISR (WDT_vect){ //вектор прерывания WD
static boolean n=0; // флаг работы: запрос температуры или её чтение
n=!n;
counter++;
if (counter==5) {counter=0;}
if (n) {ds.reset();  // сброс шины
        ds.select(addr); // выбор адреса
        ds.write(0x44); // начать преобразование (без паразитного питания)
        }
else   {ds.reset();
        ds.select(addr);    
        ds.write(0xBE); // Read Scratchpad (чтение регистров)  
        celsius =  ds.read() | (ds.read()<<8); //прочитаны 2 байта 
        //return  t>>4;//целые *C, десятые отброшены
        //return  (t+8)>>4;//целые *С с округлением
        //return  (t*10)>>4;//целое в десятых *C (214=>21,4*C)

              
        }
}


Скетч ещё допиливать, как минимум, вызывать из обработчика прерывания функцию запроса и расчёта времени из RTC. А Delay- уничтожить. Так вот и вопрос. Мне нужно построить функцию, которая вернёт ну пусть 3 параметра (часы, минуты, секунды). Пусть эти параметры будут int.  тогда как записать функцию 

int TimeCalc (//что писать здесь, если нет входных параметров?) {
//Здесь сама функция;
return min;
return sec;
return hr;

Или нельзя несколько return подряд ставить?

Может ли функция возврящать переменные разных типов? Например, int counter и ещё float value, или и счётчик надо во флоат засовывать?

Переменные, которые будет возвращать функция, необходимо объявлять глобальными, чтобы видеть их из остальных функций?

С примером создания функции умножения в разделе Программирование ознакомился, но своих вопросов там не нашёл. Расскажите, пожалуйста, что да как, а если с примерами- то совсем здорово!

arduinec
Offline
Зарегистрирован: 01.09.2015

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

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

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

arduinec
Offline
Зарегистрирован: 01.09.2015

kisoft пишет:
Зачем учить плохому? Для возврата из функции существуют нормальные способы, например: Передать в функцию указатели переменных, которые будут изменены в этой функции. Передать указатель на структуру, в которой содержатся переменные любых типов и которые могут быть изменены.

Почему сразу плохому? Указатель со структурой съедят несколько байтов оперативной памяти, которой в Ардуино и так немного. К тому же, в исходной постановке вопроса ТС получает в переменные значения часов, минут, секунд - гораздо проще получать их в глобальные переменные и использовать в любом месте скетча.

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

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

void TempCalc(){


}

А её вызов просто

TempCalc();

А условием выхода из функции (и возврата к точке вызова) будет являться просто последовательный перебор всех её строк программой. И return мне пока не нужен.

Верно???

 

 

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

arduinec пишет:

kisoft пишет:
Зачем учить плохому? Для возврата из функции существуют нормальные способы, например: Передать в функцию указатели переменных, которые будут изменены в этой функции. Передать указатель на структуру, в которой содержатся переменные любых типов и которые могут быть изменены.

Почему сразу плохому? Указатель со структурой съедят несколько байтов оперативной памяти, которой в Ардуино и так немного. К тому же, в исходной постановке вопроса ТС получает в переменные значения часов, минут, секунд - гораздо проще получать их в глобальные переменные и использовать в любом месте скетча.


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

Tomasina
Tomasina аватар
Offline
Зарегистрирован: 09.03.2013

kisoft, а примеры можно?

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

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

Дабы понимать, как программировать дальше моргания сетодиодом, заказал себе книжку Керниган Ритчи Программирование С. Не люблю я с монитора читать, Книга лучше, как говорится. Пусть пока мой скетч будет неоптимальным, но рабочим. Выше я описал пример вызова функции и условие возврата из неё, как я это вижу. Подскажите, там верно?

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

Тарас Петрович пишет:

Дабы понимать, как программировать дальше моргания сетодиодом, заказал себе книжку Керниган Ритчи Программирование С. 

И правильно сделал! Нормальный мужской подход!

arduinec
Offline
Зарегистрирован: 01.09.2015

Тарас Петрович пишет:

Видимо функция должна выглядеть так:

void TempCalc(){


}

А её вызов просто

TempCalc();

А условием выхода из функции (и возврата к точке вызова) будет являться просто последовательный перебор всех её строк программой. И return мне пока не нужен.

Верно???

Верно.

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

Tomasina пишет:

kisoft, а примеры можно?

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

Приведу простой пример, когда данные хранятся в виде структуры. В данном примере - это время.

Пример простой, возможно где-то не оптимальный и не претендует на идеал, но как пример сгодится. Компилируется и работает. ArduinoIDE v1.6.7.
В примере использовано два типа передачи параметров, как указатель на структуру (например, incrementSecs) и как константная ссылка (printTimeData). Передача параметров в виде ссылки позволяет понять, что в функции данные могут изменить, а как константная ссылка - гарантия того, что данные в функции не меняются. Как это выглядит при вызове хорошо видно в loop, там вызов обоих вариантов.

Макрос __TIME__ - время компиляции. Вместо него можно подставить строку в формате "hh:mm:ss".

/*
  Пример выдачи нескольких параметров из функции
*/

typedef struct TIME_DATA
{
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
};

TIME_DATA temp_time;

/* Инициализация pp_time_data временем компиляции */
void initTimeData(TIME_DATA *pp_time_data, const char *pp_time)
{
  if (pp_time_data && pp_time)
  {
    pp_time_data->hour = atoi(pp_time);
    pp_time_data->minute = atoi(pp_time + 3);
    pp_time_data->second = atoi(pp_time + 6);
  }
}

void setup()
{
  Serial.begin(57600);
  initTimeData(&temp_time, __TIME__);
}

/* Вывод в Serial значения p_value в виде двух цифр */
void print2Digits(uint8_t p_value)
{
  if (p_value < 10)
  {
    Serial.print("0");
  }
  Serial.print(p_value);
}

/* Выводим в Serial время из p_time_data */
void printTimeData(const TIME_DATA &p_time_data)
{
  Serial.print("Current time: ");
  print2Digits(p_time_data.hour);
  Serial.print(":");
  print2Digits(p_time_data.minute);
  Serial.print(":");
  print2Digits(p_time_data.second);
  Serial.println();
}

/* Увеличение времени в pp_teim_data на указанное количество секунд p_secs. p_secs не может быть больше 59 */
void incrementSecs(TIME_DATA *pp_time_data, int p_secs)
{
  if (pp_time_data && p_secs < 60)
  {
    pp_time_data->second += p_secs;
    if (pp_time_data->second >= 60)
    {
      pp_time_data->second -= 60;
      pp_time_data->minute++;
      if (pp_time_data->minute >= 60)
      {
        pp_time_data->minute -= 60;
        pp_time_data->hour++;
        if (pp_time_data->hour >= 24)
        {
          pp_time_data->hour -= 24;
        }
      }
    }
  }
}

void loop()
{
  printTimeData(temp_time);
  incrementSecs(&temp_time, 1);
  delay(1000);
}

Если сверхсложно или просто сложно, могу упростить пример.

 

Тарас Петрович
Offline
Зарегистрирован: 21.11.2015

Может, не упростить, а более подробно откомментировать. Например мне не понятно, что за конструкции присутствуют в строке  pp_time_data->hour = atoi(pp_time);

 

Что есть знак ->, чем является  atoi

Yarik.Yar
Offline
Зарегистрирован: 07.09.2014

-> - это ссылка на метод класса при обращении через указатель. Пример: обычное обращение к выводу по UART - Serial.print(string). Если бы нам Serial передавался как указатель, в функции типа void print_it_all(Serial *serial), то обращение было бы serial->print(string)...как-то так)
С atoi всё проще- это функция Array TO Int, массив в целое число переводит.
.

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

Уже всё объяснили, что -> это когда есть указатель на объект, то используем "->", а если есть сам объект, то используем ".". Оба примера есть в моём скетче.

Чтобы найти что такое atoi ищем "C++ atoi" и находимо писание.

 

alexvs
Offline
Зарегистрирован: 22.07.2014
А если проще (для понимания)
void loop() 
{  
  int hr,mn,sc;
  TimeCalc(hr,mn,sc);
}

void TimeCalc (int &h, int &m, int &s) 
{
  // Код изменяющий переменные
  h = hour();
  m = minute();
  s = second();
}

Т.е. в функцию передаем ссылки на переменные.

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

Существуют рекомендации (не мои) о том, что если параметр внутри функции меняется, то передавать лучше указатель на него, а не использовать ссылку. Увы, URL рекомендации у меня нет. Вот простой пример, скажите, в какой из этих функций может измениться параметр par?

void foo1(int *pp_par);
void foo2(int &p_par);

...
foo1(&par);
...
foo2(par);
...

Если не смотреть на объявление функции, то понять, что в foo2 может измениться значение параметра невозможно, а в foo1 можно задуматься, что параметр может измениться.

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

С глобалами получится примерно так:

/**
 *  Пример выдачи нескольких параметров из функции
 * modified by Arhat109. Using global variables
*/

typedef struct TIME_DATA
{
  uint8_t hour;
  uint8_t minute;
  uint8_t second;
};

TIME_DATA temp_time;  // Тут объявили глобал (так было)

/**
 * Установка времени в глобал
 *
 * @global TIME_DATA temp_time
 * @param  char *    pp_time   -- строка времени в формате: HH:MM:SS
 */
void initTimeData(const char *pp_time)
{
  if (pp_time)
  {
    temp_time.hour = atoi(pp_time);
    temp_time.minute = atoi(pp_time + 3);
    temp_time.second = atoi(pp_time + 6);
  }
}

/**
 * Вывод в Serial значения в виде двух цифр
 *
 * @param uint8_t p_value
 */
void print2Digits(uint8_t p_value)
{
  if (p_value < 10){ Serial.print("0"); }

  Serial.print(p_value);
}

/**
 * Выводим в Serial время из глобала
 *
 * @global TIME_DATA temp_time
 */
void printTimeData()
{
  Serial.print("Current time: ");
  print2Digits(temp_time.hour);
  Serial.print(":");
  print2Digits(temp_time.minute);
  Serial.print(":");
  print2Digits(temp_time.second);
  Serial.println();
}

/**
 * Увеличение времени глобала на указанное количество секунд
 *
 * @global TIME_DATA temp_time
 * @param int        p_secs        [-59 .. 59]
 */
void incrementSecs(int p_secs)
{
    temp_time.second += p_secs;
    if (temp_time.second >= 60)
    {
      temp_time.second -= 60;
      if ( ++temp_time.minute >= 60)
      {
        temp_time.minute -= 60;
        if ( ++temp_time.hour >= 24)
        {
          temp_time.hour -= 24;
        }
      }
    } else if( temp_time.second < 0 )
    {
       /* тот же код по выравниванию в случае сложения с отрицательным значением */
    }
}

void setup()
{
  Serial.begin(57600);
  initTimeData(__TIME__);
}

void loop()
{
  printTimeData();
  incrementSecs(1);
  delay(1000);
}

Разница:

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

2. Необходимость писать вменяемый комментарий с использованием того или иного Документора. .. который вам ещё и документацию сделает автоматически.

Собственно и все "предубеждения" против глобалов.

А, учитывая, что у МК оперативной памяти "с гулькин нос" - использование глобалов - КАТЕГОРИЧЕСКИ рекомендуется. Вот, как раз ради ТАКОЙ экономии памяти и повышения скорости исполненния.

Да, и заметьте, что в исходном варианте ГЛОБАЛ СОЗДАЕТСЯ статически .. чуть более чем "точно также". :)

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

kisoft пишет:

Существуют рекомендации (не мои) о том, что если параметр внутри функции меняется, то передавать лучше указатель на него, а не использовать ссылку. Увы, URL рекомендации у меня нет. Вот простой пример, скажите, в какой из этих функций может измениться параметр par?

void foo1(int *pp_par);
void foo2(int &p_par);

...
foo1(&par);
...
foo2(par);
...

Если не смотреть на объявление функции, то понять, что в foo2 может измениться значение параметра невозможно, а в foo1 можно задуматься, что параметр может измениться.

Сразу тупой вопрос. Указатели и ссылки для меня темный лес.
То есть так как впримере ниже в принципе правильно, но в функции foo2 мы не видим что par это переданная нам ссыла, и это плохо. Так? Но работать все будет нормально переменную pp_par функция будет менять.

void loop() 
{  
  int pp_par;
  void foo2(&pp_par);
}

void foo2(par);
{
  // Код изменяющий переменные
  par = 15;
}

А тут смысл тот же но визуально мы видим в самой функции foo1 что переменная par переданная это ссылка. А значит и pp_par изменится тоже. Или есть еще какойто более глубокий смысл?

void loop() 
{  
  int pp_par;
  void foo1(*pp_par);
}

void foo1(&par);
{
  // Код изменяющий переменные
  par = 15;
}

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

 

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

Andrey12 пишет:

kisoft пишет:

 

void foo1(int *pp_par);
void foo2(int &p_par);

...
foo1(&par);
...
foo2(par);
...

Сразу тупой вопрос. Указатели и ссылки для меня темный лес.
То есть так как впримере ниже в принципе правильно, но в функции foo2 мы не видим что par это переданная нам ссыла, и это плохо. Так? Но работать все будет нормально переменную pp_par функция будет менять.

void loop() 
{  
  int pp_par;
  void foo2(&pp_par);
}

void foo2(par);
{
  // Код изменяющий переменные
  par = 15;
}

А тут смысл тот же но визуально мы видим в самой функции foo1 что переменная par переданная это ссылка. А значит и pp_par изменится тоже. Или есть еще какойто более глубокий смысл?

void loop() 
{  
  int pp_par;
  void foo1(*pp_par);
}

void foo1(&par);
{
  // Код изменяющий переменные
  par = 15;
}

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

 

Спасибо за вопрос.

С точки зрения С++, смысл указателя и ссылки практически одинаковый (по большому счету это одно и то же). Однако чисто визуально (и только с этой точки зрения) для указателя требуется перед объектом указывать "&", что визуально (!) позволяет видеть и задуматься, что здесь что то особое есть. А при передаче ссылки, амперсанд не указывается, для сравнения:

void foo(OBJECT &obj, int32_t par, uint8_t &flag);
...
OBJECT obj;
int32_t value;
uint8_t opened;
...
  foo(obj, value, opened);
...

Здесь нельзя с точностью сказать, какой из этих трех параметров может измениться в функции foo.

Тогда как здесь:

void foo(OBJECT *pp_obj, int32_t p_par, uint8_t *pp_flag);
...
OBJECT obj;
int32_t value;
uint8_t opened;
...
  foo(&obj, value, &opened);
...

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

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

 

Andrey12
Andrey12 аватар
Offline
Зарегистрирован: 26.12.2014

kisoft пишет:

Спасибо за вопрос.

С точки зрения С++, смысл указателя и ссылки практически одинаковый (по большому счету это одно и то же). Однако чисто визуально (и только с этой точки зрения) для указателя требуется перед объектом указывать "&", что визуально (!) позволяет видеть и задуматься, что здесь что то особое есть. А при передаче ссылки, амперсанд не указывается, для сравнения:

void foo(OBJECT &obj, int32_t par, uint8_t &flag);
...
OBJECT obj;
int32_t value;
uint8_t opened;
...
  foo(obj, value, opened);
...

Здесь нельзя с точностью сказать, какой из этих трех параметров может измениться в функции foo.

Тогда как здесь:

void foo(OBJECT *pp_obj, int32_t p_par, uint8_t *pp_flag);
...
OBJECT obj;
int32_t value;
uint8_t opened;
...
  foo(&obj, value, &opened);
...

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

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

Спасибо за подробное разъяснение.

Знаний программирования у меня мало, поэтому пытаюсь писать так чтобы было понятно не только мне. Хотя и мне в первую очередь, занимаюсь Ардуино редко, поэтому если с "выкрутасами" писать то через месяц и не вспомню, что я этим кодом "хотел сказать".

А такой вот пример использования ссылок и указателей кажется мне довольно наглядным.

Появится время почитаю про указатели. Кнжку скачал, все руки не дойдут.

 

Arhat109-2
Offline
Зарегистрирован: 24.09.2015

Не совсем так.

Эпиграф: "Песня, имя песни, название песни. Имя названия песни" (с) Баррон, 1969г. :)

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

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

* - это ОПЕРАЦИЯ доступа к содержимому (песне), имя  которой хранится в указателе с этим названием.

*ptr = xxx; -- по адресу памяти, хранящемуся в ptr сохранить xxx.

xxx = *ptr; -- по адресу памяти, названному xxx сохранить то, что содержится по адресу памяти, хранящемуся с названием ptr.

xxx = ptr; -- адрес памяти, хранящийся в ptr сохранить ещё и в xxx.

& - это ОБРАТНАЯ операция к операции доступа: получение "имени названия песни". Только и всего, никакой "мистики".

ptr = &xxx; -- получить адрес (текущий!) названия xxx и сохранить его по адресу с названием ptr.

Поскольку имена песен в С/С++ типизированы, то есть адрес памяти имеет некую структуру и не позволяется хранить "что попало и как попало" (кроме волшебного слова void - чё попало), то операция доступа ОДНОВРЕМЕННО используется для указания типа указателя:

char  *ptr;

char * (*myFunc)(char, char *, char &);

С первой строкой вопросов как правило не возникает. В ней наглядно видно, что доступ по ptr будет обращаться к песням типа char. Собственно для этго оно так и введено. Наглядно видно к какому типу памяти обращается данный указатель.

Со второй строкой уже не так очевидно: что объявлено и почто так. На самом деле объявлен указатель на функцию с названием myFunc - именно к нему может применятся операция доступа. Но указатель на вполне конкретный вид функций, а именно только тех, которые возвращают указатель на char И имеют 3 параметра: char; указатель на char, и .. нечто (не указатель!) которое является .. именем типа char.

Разница промеж второго и третьего параметра в том, что второй параметр - это обычная передача по значению, но только указателя - адреса памяти. А третий параметр - это передача по ССЫЛКЕ - имени. Отсюда и различия в вызове:

xxx = *(myFunc('a', &xxx, xxx));

В первый параметр передается литеральная константа, вторым параметром нам ПРИХОДИТСЯ получать имя названия xxx, а третьим параметром мы просто передаем это название. Получение его имени компилятор сделает сам согласно типу третьего параметра.

Дабы прочуствовать лучше, рекомендую написать свой класс, перекрывающий операцию присваивания. :)

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

Про песни просто замечательно. Только вот если взять три первые строки примеров:

*ptr = xxx; // по адресу памяти, хранящемуся в ptr сохранить xxx.
xxx = *ptr; // по адресу памяти, названному xxx сохранить то, что содержится по адресу памяти, хранящемуся с названием ptr.
xxx = ptr; // адрес памяти, хранящийся в ptr сохранить ещё и в xxx.

то, кажется, басни начинаются.

Проконсультируйте пожалуйста, каких типов должны быть ххх, и ptr, чтобы эта "песня" хотя бы скомпилировалась без "плясок"? 

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

Andrey12 пишет:

Спасибо за подробное разъяснение.

Знаний программирования у меня мало, поэтому пытаюсь писать так чтобы было понятно не только мне. Хотя и мне в первую очередь, занимаюсь Ардуино редко, поэтому если с "выкрутасами" писать то через месяц и не вспомню, что я этим кодом "хотел сказать".

А такой вот пример использования ссылок и указателей кажется мне довольно наглядным.

Появится время почитаю про указатели. Кнжку скачал, все руки не дойдут.

 

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

Возможно Вам подойдет изучение C++ в Visual Studio от Microsoft. Это полноценная среда, в частности хороша для изучения и отладки программ. В ней можно по шагам пройти всю программу, посмотреть значения переменных, указателей, объектов. Конечно там своя специфика и размеры типов будут отличаться от тех, что в МК, но если об этом помнить, то всё нормально. Я вообще начинал с ассемблера, потом чистый С, а уже только потом был С++. Правда не думаю, что это будет хорошим вариантом сейчас, начинать с ассемблера (кто нибудь щас тапками начнёт бросаться).

Чтобы помнить, что написал месяц назад, пишутся комментарии в "тонких" местах, не везде и не там, где всё понятно, например: "irCodeSended = true;" комментария не требует, а там, где "сложный" алгоритм (можно в начале формулу расписать), документрировать функции (описывать что делает функция, какие параметры и что возвращает) и т.п. Тут нет таблетки на все случаи жизни, приходит со временем. Эти же комментарии помогут разобраться в программе не только Вам, а и другим, если понадобится.