Solucionado (ver solução)
Solucionado
(ver solução)
4
respostas

duvida comando while

Olá, eu tenho uma duvida referente ao comando while, eu entendo quando tem uma comparação dentro do comando while como por exemplo :

int main(){
int x =2;
while(x==2){
printf("digite 1 para sair e 2 para continuar\n");
scanf("%d",&x);
}
printf("saiu");
}

porem recentemente me deparei com um while desse jeito aqui :

while ((c =c/t))

. Como funciona o while no caso quando não se tem um operador de comparação como ==, <, >, etc, e sim um operador de atribuição como no caso em cima ? Agradeço a quem tirar essa duvida!

4 respostas

O código while ( ( c = c /t ) ) é interpretado sempre como verdadeiro e um loop infinito é criado.

Uma atribuição assume o valor "verdadeiro" como padrão quando usada no local de uma expressão lógica.

voce criou uma constante no comando while, compare ele com alguma coisa ex:

while(( c = c /t ) ! = 0){ ... ... ... }

O while funciona com uma condição para funcionar.

enquanto ( verdade ) {
    ...executar...
    *volta no começo
}

Ou seja enquanto o que existir no verdade ele executa o loop.

Muitas pessoas usam while (true) { ...codigo... } para que seu programa execute eternamente o bloco interno até que alguma ação faça sair do loop como uma condição break

Mas o que é verdadeiro. Verdadeiro em C é um valor válido, que tenha bytes.

valores como 0, NULL, char que não foi definido. Significam que a condição é falsa e logo o loop não é executado.

ou seja, na condição c = c / t se o resultado de c / t resultar em 0 (por exemplo) a variavel c terá o valor 0 e o loop não será executado.

exemplo:

int c = 8, t = 2;
while (c = c / t) {
    printf("valor de c: %d\n", c);
}

vai resultar em:

valor de c: 4
valor de c: 2
valor de c: 1

isso porque:

c = 8 / 2; // 4
c = 4 / 2; // 2
c = 2 / 2; // 1
c = 1 / 2; // 0.5 este é ignorado e se torna em 0 pelo fato da variável ser do tipo inteiro

E então fim do loop porque a condição é falsa.

solução!

Vamos por partes. Qualquer laço pode ser reescrito usando um if e um comando goto, de fato, isso é implicitamente feito pelo compilador. Portanto, o que vou explicar é válido para TODAS as estruturas de controle de fluxo.

Por exemplo, se quiséssemos re-escrever um while teríamos algo como:

label: if(x) {
    // código a ser executado...
    goto label;
}

Deixo como exercício pensar como reescrever as demais estruturas (for e do-while). No entanto, é sempre bom salientar: não torne o uso do goto uma prática habitual! Quando mal utilizado, tende a gerar código espaguete. Ele tem seus usos, este não é um deles.

Agora que reduzimos o problema ao if, vamos entender como o C verifica se algo é verdadeiro ou falso. Teste o código abaixo:


#include <stdio.h>

#define OP_FUNCTOR(NAME, OP) int NAME(int a, int b) { return a OP b; }

OP_FUNCTOR(maior,>)
OP_FUNCTOR(maiorOuIgual,>=)
OP_FUNCTOR(menor,<)
OP_FUNCTOR(menorOuIgual,<=)
OP_FUNCTOR(igual,==)
OP_FUNCTOR(difente,!=)

int print(const char* const toString, int (* const apply)(int, int)) {
    static const int x = 1, y = 1;

    int z = apply(x, y);

    return printf("%10s : %d %2s %d -> z = %d\n", 
                    z ? "VERDADEIRO" : "FALSO", 
                    x, 
                    toString, 
                    y, 
                    z);
}

int main(int argc, char *argv[]) {
    const struct {
        const char* const toString;
        int (* const apply)(int, int);
    } op[] = {
        { ">", maior},
        {">=", maiorOuIgual},
        {"==", igual},
        {"!=", difente},
        {"<=", menorOuIgual},
        { "<", menor}
    };

    for(int i = 0; i < 6; i++)
        print(op[i].toString, op[i].apply);

    return 0;
}

Em C não existem tipos booleanos, toda comparação retorna um inteiro. Sempre que for verdadeira, retorna 1, de outra forma, 0. Porém, qualquer valor diferente de 0 é verdadeiro. Apenas para dar um exemplo mais abrangente, a definição da macro NULL é a seguinte:

#define NULL ((void*) 0);

Portanto, com base no que eu disse até agora, qual o trecho de código será executado?


void *x = NULL;
if(x) {
    // Código A...
} else {
    // Código B...
}

Se escolheu a opção dentro do bloco else, acertou! Agora, o que acontece se fazemos o seguinte?


if(x = funcao()) {
    // Código A...
} else {
    // Código B...
}

Uma atribuição retorna o valor lógico do rvalue. O código acima funciona devido ao side-effect. O código é confuso, essa prática é, no melhor dos casos, de gosto duvidoso, pois torna o código menos legível devido a dependências obscuras. No pior dos casos, porém, revela um erro de sintaxe. Qual a intenção aqui? Atribuir o valor de retorno de funcao a x, ou verificar se x é igual a esse valor? Por este motivo, o compilador emite um warning! O compilador solicitará que a expressão seja envelopada em parênteses. O código ficará assim:


if((x = funcao())) {
    // Código A...
} else {
    // Código B...
}

Pronto! Agora ficou claro para o compilador que queremos atualizar o valor de x, e então testar esse novo valor. Vamos apenas adicionar a seguinte comparação, que, ainda que desnecessária, torna o código mais legível:

if((x = funcao()) != NULL) {
    // Código A...
} else {
    // Código B...
}

Nossa intenção é clara, tanto para o compilador quanto para os que forem ler o código. Adicionamos a informação de que x é um ponteiro, pois está sendo comparado com NULL, e tornamos a semântica dos blocos if e else mais rica, pois sabemos exatamente qual é o fluxo principal, e qual o de exceção.

Em nosso caso concreto:

while((c = c/t) != 0)

Estamos atualizando o valor de c pelo resultado da divisão de c por t, e verificando se esse resultado é diferente de 0. Poderíamos ainda usar a versão simplificada da expressão:

while((c /= t) != 0)

Como c e t são variáveis inteiras, contanto que o divisor seja diferente de 1, é garantido que o laço termina.

Ex.: a divisão inteira 1 / 2 resulta em 0.

Espero ter ajudado, qualquer dúvida remanescente, não hesite em perguntar.