Go-to

Тема в разделе "Флудилка", создана пользователем Рокки1945, 25 авг 2020.

  1. SergeiL

    SergeiL Оракул Модератор

    Все можно написать через функции, причем, будет красиво.
    Даже это:
    upload_2020-8-27_22-27-46.png
    Но вызов функции тянет за собой закидывание параметров на стек, как минимум, точки выхода.
    Вызов следующей функции из функции - еще пожирание стека...
    А при использовании goto нет вызова новой функции, и по этому нет пожирания стека как при вызове новой функции ;)
     
  2. a1000

    a1000 Гуру

    А при чём тут стек? При транслировании кода в asm компилятор заменит goto на JMP (как вариант RJMP), ибо это и есть безусловный переход. При выполнении этго в program counter запишется адрес по которому нужно перепрыгнуть и всё, через 2 тика тактового генератора выполнение программы прололжиться с нужного нам адреса. Стек при этом не задействован и флаги не меняются.
     
    Igor68 нравится это.
  3. SergeiL

    SergeiL Оракул Модератор

    Смотрим тут:
    Разбираемся.
    Думаем как написать без goto.
    Потом читаем это:
    И осознаем это:
    Это про стек.
     
    Airbus нравится это.
  4. b707

    b707 Гуру

    разве я предлагал функции?
    Пример Igor68 переписывается и без функций и без goto
     
  5. Airbus

    Airbus Радиохулиган Модератор

    Тоже кстати об этом подумал-не главная ли это причина игнора Goto?Но в общем и целом согласен Goto-инструмент!Хороший инструмент которым нужно уметь пользоваться.Как и delay незаслуженно переданный анафеме.Я например всегда им пользуюсь особенно когда надо чтоб устройство тупо заткнулось на какое то время и не жило своей жизнью.
     
  6. a1000

    a1000 Гуру

    Посмотрел, интересно и познавательно, по про стек ни слова.
    Согласен на сто и даже более процентов. Смутила фраза которую вы уже подправили в своём посте (модератор как-ни-как). Но цитата в моём посте осталась в первозданном виде. Поэтому привожу её так
    [​IMG]
    Смоё понимание как происходит трансляция Goto в ASM я изложил. В моём понимании стек не задействован. Если я чего-то недопонимаю - готов услышать конструктивную критику.
     
  7. Igor68

    Igor68 Гуру

    Ну а как с inline функциями? разве не то? А конкретный рабочий код не на этой машине (другую надо доставать)... хотя вот(может и не удачный пример)
    Код (C++):
    void mClientProcess(void * ind)
    {
        //uint8_t        rcv[_szbuf];
        //uint8_t        snd[_szbuf];
        uint8_t*     rcv        = (uint8_t*)malloc(_szbuf);
            uint8_t*     snd        = (uint8_t*)malloc(_szbuf);
        S7DATA*        ls7data        = &s7data;
        CWORK*        lcwork        = &cwork;
        F1DATA*        lf1data        = &f1data;
        SETPARAM_COOL*    lsetparam_cool    = &setparam_cool;
        SETPARAM_ALARM* lsetparam_alarm = &setparam_alarm;
        //time_t     rawtime;
        //struct     tm *u;
        //u_int8_t    res8;
        int        res;
        int        csock;
        int        idclient;
        //uint16_t    workU16;
        uint32_t    sz;
        int        cntlive        = _stime_life;    //счётчик времени жизни сокета

        //
        pthread_mutex_lock(&mServerMutex);
        MSERV * clserver    = ind;
        csock            = clserver->csock;
        idclient        = clserver->idclient;
        pthread_mutex_unlock(&mServerMutex);
        printf("===loop client process(sock:%i)=== \n",csock);
        while(1)
        {
            if(!clserver->work)
                goto mexit;
            sz    = 0;
            memset(snd, 0, _szbuf);
            memset(rcv, 0, _szbuf);
            pthread_mutex_lock(&mServerMutex);
            (*(uint64_t*)(snd + _PKT_TIME)) = (uint64_t)time(NULL);
            (*(uint64_t*)(snd +_PKT_CLCK)) = (uint64_t)clock();
            pthread_mutex_unlock(&mServerMutex);
            res = sReadSocket(csock, &rcv[0]);
            #if(_ssdeb == 1)
                printf("SIZE:%lli PKT_TIME:%lli PKT_CLCK:%lli PKT_CMD:%02X PKT_FTYPE:%02X\n",
                (*(uint64_t*)(rcv + _PKT_SIZE)), (*(uint64_t*)(rcv + _PKT_TIME)),
                (*(uint64_t*)(rcv + _PKT_CLCK)), (*(uint8_t*)(rcv + _PKT_CMD)), (*(uint8_t*)(rcv + _PKT_FTYPE])));
            #endif
            switch(res)
            {
                case 0:
                    goto mexit;
                case -1: //таймаут
                    #if(_ssdeb == 1)
                        printf("TCP recv.: timeout..\r\n");
                    #endif
                    //cntlive /= 2;
                    break;//goto mexit;
                case -2: //какая-то ошибка сокета
                    #if(_ssdeb == 1)
                        printf("TCP resv.: err?..\r\n");
                    #endif
                    goto mexit;
                case -3: //ошибка размера пакета
                    //передача сообщения о несоответствии
                    #if(_ssdeb == 1)
                        printf("TCP client: frame no correct..\r\n");
                    #endif
                    (*(uint32_t*)(snd + _PKT_SIZE))        = _PKT_DATA;
                    (*(uint8_t*)(snd + _PKT_CMD))        = (*(uint8_t*)(rcv + _PKT_CMD));
                    (*(uint8_t*)(snd + _PKT_FTYPE))        = (*(uint8_t*)(rcv + _PKT_FTYPE));
                    (*(uint8_t*)(snd + _PKT_ANS))        = _ANS_SZ_NO_CORRECT;
                    break;
                default: //нормально - данные приняты
                    //
                    #if(_ssdeb == 1)
                        printf("WORK CMD:%02X\r\n", (*(uint8_t*)(rcv + _PKT_CMD)));
                    #endif
                    switch((*(uint8_t*)(rcv + _PKT_CMD)))
                    {
                        case _CMD_RD:        //чтение блока параметров
                            #if(_ssdeb == 1)
                                printf("WORK FTYPE:%02X\r\n", (*(uint8_t*)(rcv + _PKT_FTYPE)));
                            #endif
                            switch((*(uint8_t*)(rcv + _PKT_FTYPE)))
                            {
                                case _DTYPE_S7: //
                                    pthread_mutex_lock(&mServerMutex);
                                    memcpy((uint8_t*)(snd + _PKT_DATA), ls7data, sizeof(S7DATA));
                                    pthread_mutex_unlock(&mServerMutex);
                                    (*(uint32_t*)(snd + _PKT_SIZE))        = _PKT_DATA + sizeof(S7DATA);
                                    (*(uint8_t*)(snd + _PKT_CMD))        = (*(uint8_t*)(rcv + _PKT_CMD));
                                    (*(uint8_t*)(snd + _PKT_FTYPE))        = (*(uint8_t*)(rcv + _PKT_FTYPE));
                                    (*(uint8_t*)(snd + _PKT_ANS))        = _ANS_OK;
                                    break;
                           
                                 //сокращено - огрничение по размеру (на сайте)
                                  ...
                                  ...
                                default:    //ошибка типа
                                    (*(uint32_t*)(snd + _PKT_SIZE))     = _PKT_DATA;
                                    (*(uint8_t*)(snd + _PKT_CMD))        = (*(uint8_t*)(rcv + _PKT_CMD));
                                    (*(uint8_t*)(snd + _PKT_FTYPE))        = (*(uint8_t*)(rcv + _PKT_FTYPE));
                                    (*(uint8_t*)(snd + _PKT_ANS))        = _ANS_NO_TYPE;
                                    break;
                            }
                            break;
                        case _CMD_RD_ONE:    //чтение одного параметра
                            #if(_ssdeb == 1)
                                printf("WORK FTYPE:%02X\r\n", (*(uint8_t*)(rcv + _PKT_FTYPE)));
                            #endif
                            switch((*(uint8_t*)(rcv + _PKT_FTYPE)))
                            {
                                 
                                //сокращено - огрничение по размеру (на сайте)
                                  ...
                                  ...    
     
                             

                            }
                            break;
                        case _CMD_WR_ONE:    //запись одного параметра
                            #if(_ssdeb == 1)
                                printf("WORK FTYPE:%02X\r\n", (*(uint8_t*)(rcv + _PKT_FTYPE)));
                            #endif
                            switch((*(uint8_t*)(rcv + _PKT_FTYPE)))
                            {
                       
                                  //сокращено - огрничение по размеру (на сайте)
                                  ...
                                  ...
                             

                                default:    //ошибка типа
                                    (*(uint32_t*)(snd + _PKT_SIZE))     = _PKT_DATA;
                                    (*(uint8_t*)(snd + _PKT_CMD))        = (*(uint8_t*)(rcv + _PKT_CMD));
                                    (*(uint8_t*)(snd + _PKT_FTYPE))        = (*(uint8_t*)(rcv + _PKT_FTYPE));
                                    (*(uint8_t*)(snd + _PKT_ANS))        = _ANS_NO_TYPE;
                                    break;
                            }
                            break;
                        case _CMD_DISCONNECT:    //ответ не отсылается
                            goto mexit;
                        default:        //команда не определена
                            (*(uint32_t*)(snd + _PKT_SIZE))     = _PKT_DATA;
                            (*(uint8_t*)(snd + _PKT_CMD))        = (*(uint8_t*)(rcv + _PKT_CMD));
                            (*(uint8_t*)(snd + _PKT_FTYPE))        = (*(uint8_t*)(rcv + _PKT_FTYPE));
                            (*(uint8_t*)(snd + _PKT_ANS))        = _ANS_CMD_UNCNOWN;
                            break;
                    }
                    sz = (*(uint32_t*)(snd + _PKT_SIZE));
                    break;
            }
            //отправка ответа клиенту
            if(sz > 0)
            {
                res = sWriteSocket(csock, &snd[0], sz);
                switch(res)
                {
                    case 0:
                        goto mexit;
                    case -1: //таймаут
                        #if(_ssdeb == 1)
                            printf("TCP -send timeout\r\n");
                        #endif
                        goto mexit;//goto mt1;
                    case -2: //ошибка передачи
                        #if(_ssdeb == 1)
                            printf("TCP send error\r\n");
                        #endif
                        goto mexit;
                    default:
                        if(res != sz)
                            goto mexit;
                        cntlive        = _stime_life;
                        break;
                }
            }
            //
            #if(_ssdeb == 1)
                printf("TCP cntlive:%i\r\n", cntlive);
            #endif
            if(cntlive > _mspr_ssleep)
                cntlive -= _mspr_ssleep;
            else
                goto mexit;
            #if(_ssdeb == 1)
                printf("--countlive:%i---\r\n",cntlive);
            #endif
            memset(&rcv[0], 0, _szbuf);

            usleep(_mspr_ssleep);
        }
    mexit:    shutdown(csock, SHUT_RDWR);
        close(csock);
        pthread_mutex_lock(&mServerMutex);
        if(clserver->work) //если всё остальное работает
        {
            clserver->midcl[idclient] = 0;
            clserver->cntclient--;
            #if(_ssdeb == 1)
                printf("---close client only---\r\n");
            #endif
        }
        pthread_mutex_unlock(&mServerMutex);
        #if(_ssdeb == 1)
            printf("---close client and exit---\r\n");
        #endif
        free(rcv);
        free(snd);
        pthread_exit(0);
    }
    Но уверяю что для контроллеров (не этот пример) помогает. Внутри switch ещё несколько вложенных switch... и всё это в цикле.
     
  8. Igor68

    Igor68 Гуру

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

    А пример (он и не пример вовсе) и не тянет на оптимальность если только не ускорять и не экономить
     
    Последнее редактирование: 29 авг 2020
  9. Igor68

    Igor68 Гуру

    Да и в Си исполняемый файл (бинарник) так же содержит эти самые JMP-ы в виде кода инструкции процессора. Всё логично! Хотя бы циклы эти же самые JMP-ы. И все остальные.
     
  10. Igor68

    Igor68 Гуру

    Вообще-то я не "Бык театральный" что бы что-то искать. Если что не в обиду!
     
  11. Un_ka

    Un_ka Гуру

    Какой?
    Может по другому стандарту си компилирует.
     
  12. Airbus

    Airbus Радиохулиган Модератор

    Как так? Стек задействован всегда. Нет? Кмк тут вопрос вот в чем— насколько в каждом конкретном случае (с goto или без онного).
     
    SergeiL нравится это.
  13. a1000

    a1000 Гуру

    В языках высокого уровня вся подноготная скрыта, а в ASM всё видно сразу. Не скажу, что я сильно в ASM разбираюсь, изложу своё видение процесса.
    goto в ASM это JMP и его разновидности. При его исполнении в program counter подставляется адрес перехода и программа прыгает на этот адрес. Адрес перехода хранится в програмной FLASH памяти, возврат после этого прыжка не планируется, так-что ложить в стек просто нечего.
    Вызов функции. В ASM это CALL и разнодидности - вызов подпрограммы(ПП). Тут у нас предусматривается возврат после исполнения ПП. По сему адрес с которого осуществляется вызов ПП процессор ложит в стек. Выполнение ПП заканчивается командой RET, которая забирает из стека адрес возврата и пихает его в program counter. Далее прыжок в точку вызова ПП. Значения регистров общего назначения (РОН) используемых в ПП обычно в стек не сохнаняют. Мы ведь ПП вызываем осознанно и к этому зарание готовимся.
    Прерывание. При возникновении события заканчивается выполнение текущей команды, адрес следующей ложится в стек и нас забрасывает в таблицу векторов прерываний а оттуда командой JMP в обработчик прерывания. Так как прерывание возникает внезапно, все РОН используеммые в обработчике надо тоже положить в стек. В конце обработчика мы извлекаем из стека начальные значения РОН и пишем команду RETI, которая работает так-же как и RET, только дополнительно востанавливает флаг I (глобальное резрешение прерываний).
    И не дай бог выходить из ПП или прерывания по JMP. Стек залезет на оперативку и всё рухнет.
    В моём понимании как-то так. Если вдруг по другому - готов выслушать.
     
  14. SergeiL

    SergeiL Оракул Модератор

    Поправил сразу, чтобы понятней было.
    А если так:
    Код (C++):
    int func_A(int  a, int b)
    {
        if (a< 10)
        {
            int c = 0;
            c = b-a;
            if (c >20)
                goto label_1;
        }
        return (a);
    label_1:
        return (b);
    }
    Переменная "с" - локальная, она в стеке. По goto мы выходим из блока где была выделена на стеке переменная "с".
    Мы вышли из блока, стек нужно вернуть до значения при котором был вход в блок?
    Руки не дошли проверить и приложить листинг. но так должно быть по определению.
    Может кто и проверит?
     
  15. Asper Daffy

    Asper Daffy Иксперд

    Кто Вам сказал? Обманули.
     
    a1000 нравится это.
  16. SergeiL

    SergeiL Оракул Модератор

    :)
    Именно в этом примере и для AVR, она может быть регистровая.
    Но если параметров и локальных переменных много, и процессор, например 8080, где регистров не так много, она будет на стеке. И никак по другому! ;)
     
  17. a1000

    a1000 Гуру

    Не скажу про то так всё устроено в 8080, не знаю не юзал. А в AVR дела обстоят так.
    Хранить переменные в РОН это моветон, особенно когда этих переменных много. Переменные хранят в оперативке. А в РОН их вытаскивают только для выполнения действий. С переменными находящимися в оперативке никаких действий кроме чтения и записи выполнить нельзя. А так как стек расположен на дне оперативки, то вышесказанное относится и к нему. Так, что действия

    Код (C++):
          ...........
          int c = 0;
           c = b-a;
          .............
    могут быть выполнены только в регистрах общего назначения. Где потом компилятор придумает хранить результат, сказать не могу, но очень сильно сомневаюсь, что в стеке. Да, стек иногда используют для хранения данных, но он устроен по принципу массива последовательного доступа. Как стопка книг. Работает так - что последним ПОЛОЖИЛ, то первым ЗАБРАЛ. Именно ПОЛОЖИЛ и ЗАБРАЛ. После однократного считывания данные становятся недоступными. Приходится постоянно следить за содержимым. А зачем этот геморой если есть куча оперативки с произвольным доступом. И читать данные оттуда можно неограниченное число раз.
     
  18. SergeiL

    SergeiL Оракул Модератор

    Вот если не знаете, так и не пишите.
    Все локальные переменные хранятся в регистрах, а если их НЕ хватает- в стеке.
     
    Последнее редактирование: 31 авг 2020
  19. parovoZZ

    parovoZZ Гуру

    В РОН и закинет.

    Это на сях. В ассемблере сам себе режиссёр.
     
  20. SergeiL

    SergeiL Оракул Модератор

    Красное - оно - красное.
    А как еще на ассемблере может быть?