Ir ao conteúdo

C++ Como retornar ao menu do meu programa em um 'switch'?


Posts recomendados

Postado

Boa noite, eu fiz um programa que começa no menu principal, onde o usuário escolhe qual função ele deseja acessar.

Aí tem um comando 'switch' e após a execução do 'case', eu crio um comando 'IF' perguntando se o usuário quer voltar ao menu principal.

 

Aí que vem a dúvida, como eu posso fazer o programa retornar ao menu principal caso o usuário queira voltar? Eu costumo usar o comando 'goto' porque ele é bem prático, mas todo mundo diz que ele é uma gambiarra, então quais outras maneiras que eu posso fazer isso?

 

O código do programa tá aqui embaixo:

#include <iostream>
#include <locale.h>

using namespace std;

int main ()
{
    setlocale(LC_ALL,"");

    char opcao;
    cout << "Você está no menu." << endl << endl;
    cout << "R ---> Registro" << endl;
    cout << "C ---> Calculadora" << endl << endl;
    cout << "Digite uma das teclas acima para acessar a função desejada: ";
    cin >> opcao;
    system ("cls");

    switch (opcao)
    {
        case 'R':
            char retornar1;
            cout << "Você está na função 'Registro'." << endl << endl;
            cout << "Retornar ao menu? ---> S/N" << endl;
            cin >> retornar1;
            system ("cls");
            if (retornar1 == 'S') {
                //comando para retornar ao menu.
            } if (retornar1 == 'N') {
                cout << "Saindo do programa." << endl;
            }
            break;
        case 'C':
            char retornar2;
            cout << "Você está na função 'Calculadora'." << endl << endl;
            cout << "Retornar ao menu? ---> S/N" << endl;
            cin >> retornar2;
            system ("cls");
            if (retornar2 == 'S') {
                //comando para retornar ao menu.
            } if (retornar2 == 'N') {
                cout << "Saindo do programa." << endl << endl;
            }
            break;
    }

    return 0;
}

 

Valeu aí pra quem puder ajudar.

Postado
6 horas atrás, Ian Vianna disse:

Aí tem um comando 'switch' e após a execução do 'case', eu crio um comando 'IF' perguntando se o usuário quer voltar ao menu principal.

 

Aí que vem a dúvida, como eu posso fazer o programa retornar ao menu principal caso o usuário queira voltar? Eu costumo usar o comando 'goto' porque ele é bem prático, mas todo mundo diz que ele é uma gambiarra, então quais outras maneiras que eu posso fazer isso?

 

Isso vem desse artigo, de 1968 ;)  

Uma opinião mais recente em CPPCON 2020 cita coisas como essa, mitos da programação. Pode ver os dois e tirar alguma conclusão. Mas veja o exemplo que está bem aqui no seu post...

 

Não precisa ser muito observador para entender por exemplo que 

  • goto implica em desviar a execução do programa para o comando seguinte a um label
  • switch implica em uma série de labels, ao menos um. Claro, pode não ter nenhum mas aí também não faz nada
  • switch tendo labels desvia a execução para o comando depois do case correspondente
  • um break desvia a execução para o comando seguinte ao switch
  • um switch então equivale a dois goto "estilizados"

Exemplo em C++

 

#include <iostream>
int main (void)
{
    int valor = 0;
    
    pontoA: switch(valor)
    {
        case 0:
            valor = 1;
            break;
        case 10:
            valor = 355;
            break;
        default:
            valor = 0;
            break;
    };

    pontoB:  if ( valor == 1 ) goto pontoA;

    std::cout << "saindo com valor = " << 
        valor << std::endl;
    return 0;
}

 

E o código gerado no gcc 9.3 por exemplo onde se vê o que vai ser executado de verdade, com as instruções que o compilador gera na saída:

 

.LC0:
        .string "saindo com valor = "
main:
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], 0
.L2:
        cmp     DWORD PTR [rbp-4], 0
        je      .L3
        cmp     DWORD PTR [rbp-4], 10
        je      .L4
        jmp     .L9
.L3:
        mov     DWORD PTR [rbp-4], 1
        jmp     .L6
.L4:
        mov     DWORD PTR [rbp-4], 355
        jmp     .L6
.L9:
        mov     DWORD PTR [rbp-4], 0
        nop
.L6:
        cmp     DWORD PTR [rbp-4], 1
        jne     .L7
        jmp     .L2
.L7:
        mov     esi, OFFSET FLAT:.LC0
        mov     edi, OFFSET FLAT:_ZSt4cout
        call    std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)
        mov     rdx, rax
        mov     eax, DWORD PTR [rbp-4]
        mov     esi, eax
        mov     rdi, rdx
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(int)
        mov     esi, OFFSET FLAT:_ZSt4endlIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_
        mov     rdi, rax
        call    std::basic_ostream<char, std::char_traits<char> >::operator<<(std::basic_ostream<char, std::char_traits<char> >& (*)(std::basic_ostream<char, std::char_traits<char> >&))
        mov     eax, 0
        leave
        ret
__static_initialization_and_destruction_0(int, int):
        push    rbp
        mov     rbp, rsp
        sub     rsp, 16
        mov     DWORD PTR [rbp-4], edi
        mov     DWORD PTR [rbp-8], esi
        cmp     DWORD PTR [rbp-4], 1
        jne     .L12
        cmp     DWORD PTR [rbp-8], 65535
        jne     .L12
        mov     edi, OFFSET FLAT:_ZStL8__ioinit
        call    std::ios_base::Init::Init() [complete object constructor]
        mov     edx, OFFSET FLAT:__dso_handle
        mov     esi, OFFSET FLAT:_ZStL8__ioinit
        mov     edi, OFFSET FLAT:_ZNSt8ios_base4InitD1Ev
        call    __cxa_atexit
.L12:
        nop
        leave
        ret
_GLOBAL__sub_I_main:
        push    rbp
        mov     rbp, rsp
        mov     esi, 65535
        mov     edi, 1
        call    __static_initialization_and_destruction_0(int, int)
        pop     rbp
        ret

 

 

E você vê que

  • o switch() está em .L2 --- label 2 :) --- e lá compara valor com 0 e vai para .L3, o label case 0: je é um goto: Jump on Equal. 
  • Se não for 0 compara com 10 e se for igual vai para .L4, outro goto 
  • Se não for 10 então é o label default. E faz valor = 0 e usa JMP o goto incondicional, jump, e vai para o ponto B
.L2:
        cmp     DWORD PTR [rbp-4], 0
        je      .L3
        cmp     DWORD PTR [rbp-4], 10
        je      .L4
        jmp     .L9
  • calma, já vou terminar
     
  • veja o código em pontoB
     
        pontoB:  if ( valor == 1 ) goto pontoA;


    E o que vira
     

  • .L6:
            cmp     DWORD PTR [rbp-4], 1
            jne     .L7
            jmp     .L2
    .L7:
            mov     esi, OFFSET FLAT:.LC0
            mov     edi, OFFSET FLAT:_ZSt4cout
    ...

     

Pois é: dois goto: um que está no programa, o goto pontoA, e outro que vai ger gerado para o programa seguir pro cout afinal...

 

Conclusão

 

Desconfie. Muito disso é frescura. Assista à conferência CPPCON de que falei por exemplo. Esse artigo sobre os goto foi super impulsionado porque o editor da CACM na época (1968), onde saiu o artigo, o prof Wirth, criador do Pascal, reforçava totalmente o uso da tal programação estruturada, o hit da época, em que a gente programava em FORTRAN ou COBOL ou Assembler, e alguns em Algol, que o prof Wirth "baseou" a linguagem Pascal que ele "criou". Política também conta. Ele queria divulgar os conceitos que ele achava que eram essenciais para a vida humana e que estão no prefácio do Pascal User Manual And Report, a bíblia do Pascal, escrita por... ele mesmo.

 

Porque não se usa goto afinal?

 

Porque há opções melhores. O switch por exemplo é bonitinho.

 

Porque se usa goto afinal?

 

Porque em alguns casos ainda é perfeitamente justificável e melhor. Exemplos? Assista a conferência. Ou apenas continue programando e deve encontrar um ou outro. E entenda que um CALL por exemplo é um par de GOTO, um switch como eu mostrei é também. 

 

E sem JMP ou JE não existe programa afinal :) 

 

image.png

  • Amei 1
Postado

@vangodp Evitar goto ao máximo é bom, mas não à todo custo.

Nesse código do autor realmente não é necessário, mas acredito que aumentar a complexidade em nome da regra em certos casos possa ser inconveniente.

O uso parcimonioso desse comando por quem sabe usar de fato não causa problemas nem mesmo de legibilidade.

Postado
34 minutos atrás, Lucca Rodrigues disse:

@vangodp Evitar goto ao máximo é bom, mas não à todo custo.

Nesse código do autor realmente não é necessário, mas acredito que aumentar a complexidade em nome da regra em certos casos possa ser inconveniente.

O uso parcimonioso desse comando por quem sabe usar de fato não causa problemas nem mesmo de legibilidade.

 

Sim!

 

A vida não começa com um programa. Começa com um problema a ser resolvido, um projeto, um orçamento, uma aposta, um exercício, um prazo...

 

A partir daí, se isso vai gerar um programa, se cria uma abstração, uma representação para o problema. Aí vem uma linguagem, umas pessoas, uns comandos. E em algum momento um resultado.

 

E pode ser que o particular algoritmo seja bem representado em C ou C++ ou C# ou FORTRAN ou COBOL ou java ou Python ou Go ou Assembler... Exceto Python toda linguagem tem esse comando. E não faltam linguagens.

 

É possível fazer coisas sérias em Python, já que a linguagem tem bibliotecas para TUDO, escritas em C ou C++.

 

Exemplo

 

Um tipo de problema sério que é muito adequado de implementar com goto é uma máquina de estados, conhecida como FSM --- finite state machine --- na literatura. Isso é importante pra c@r@lh0 porque? Porque representa qualquer coisa que tenha, quem diria, um número finito de estados. Como um compilador, um parser, um protocolo de comunicações, um processo administrativo...

 

E implementar uma FSM dentro de um switch() ou if() não acrescenta absolutamente nada positivo: só fica artificial e mais difícil de ler. Na dúvida, escreva uma.

3 horas atrás, vangodp disse:

Lhe sugiro que faça um programinha cheio de gotos para ter uma ideia do "problema"

 

Trata-se de uma inversão de papéis: primeiro vem "a ideia do problema", depois a abstração, o modelo conceitual, depois a implementação. E em alguns casos a implementação é mais legível e melhor escrita usando esses condenados e criminosos e harmful goto, em que pese a opinião do prof. Wirth, do prof Dijkstra e outros. A realidade prevalece. Na dúvida, escreva e teste, por exemplo, um  autômato.

Crie uma conta ou entre para comentar

Você precisa ser um usuário para fazer um comentário

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora

Sobre o Clube do Hardware

No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas comunidades sobre tecnologia do Brasil. Leia mais

Direitos autorais

Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

×
×
  • Criar novo...

GRÁTIS: ebook Redes Wi-Fi – 2ª Edição

EBOOK GRÁTIS!

CLIQUE AQUI E BAIXE AGORA MESMO!