Ponto de sequência, cuidado!

O Thiago abriu uma questão em nossas mentes (minha e dele) e um ponto de interrogação ficou até então. Eu disse até então.

Falarei um pouco sobre ponto de sequência, mas não abordando o assunto a fundo e sim o problema apresentado pelo Thiago, usando os recursos presente nessa postagem.

Seguindo o código abaixo, qual o resultado esperado a ser impresso?

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int x = 1;
    printf("%d, %d", ++x, x++);
    system("pause");
    return 0;
}

Pra quem disse “2, 2“. A resposta está EXATAmente errada.
Isso mesmo, está errada. O resultado é “3, 1“.

E porque diabos isso ocorreu? As operações feita em x são indefinidas (como visto aqui) mesmo no nosso ponto de vista sendo lógicas, sendo assim a perca do controle sobre a operação e seu valor final.

Analise rápida (pensando como a máquina)

Depurando o código compilado e juntando umas idéias dá para concluir que:
Esse resultado ocorreu porque o empilhamento dos parâmetros ocorre do último para o primeiro (da direita para a esquerda) e o cálculo foi feito pelo compilador como visto na imagem abaixo:

Vamos então…

Assumindo o valor de x como 1, e o modo de como é feito o empilhamento temos então a seguinte lógica:

x++ o valor de x é retornado e depois incrementado — o valor inicial de x é empilhado (valor 1) e depois incrementado a 2. Por isso o MOV DWORD PTR SS[ESP+8], 1

++x o valor de x é incrementado antes de ser retornado — Como até aqui x tem o valor de 2, ele é incrementado antes de ser empilhado tendo assim o valor de 3. Por isso o MOV DWORD PTR SS:[ESP+4],3

Por isso o valor de “3, 1”

Outro problema

Segue o código:

#include <stdlib.h>
#include <stdio.h>

int main()
{
    int x = 1;
    printf("%d, %d, %d", x++, ++x, x);
    system("pause");
    return 0;
}

O resultado esperado é 1, 3, 3 né? — É, ele é apenas esperado — O real valor é 2, 3, 3.

Como numa declaração indefinida não sabemos quem vai ser avaliado primeiro, o valor da nossa variável  foge ao controle (ou não caso saiba realmente o que está fazendo).

Vamos pensar na verdadeira lógica? Certo, irei classificar os parâmetros como x++ como %3, ++x como %2 e x como %1 apenas para não nos perdemos nos cálculos. Mas todos os valores apontam para x.

Sendo a primeira operação em %2 (++x) temos os seguintes valores antes da chamada:

%1 = 1 — aponta pro valor inicial de x
%2 = indefinido
%3 = indefinido

Após a chamada:

%1 = 2 — O valor desse parâmetro mudou pois o valor de x foi incrementado por ++x
%2 = 2 — valor de x incrementado
%3 = indefinido

A segunda operação em %3 (x++) temos após a chamada:
— Os cálculos terminam aqui sendo assim o compilador sabe os valores para empilhar —

%1 = 3 — aponta pro valor de x
%2 = 3 — aponta pro valor de x
%3 = 2 — como o cálculo termina aqui, o compilador não faz referência a x com ele incrementado como os outros valores, ele apenas empilha o valor até então.

Por isso temos o valor de 2, 3, 3.

Conclusão

O compilador faz seus cálculos internos para ter seus valores fixos na hora de empilhar quando a variável é local.
Depois de depurar os códigos compilado e ver os warnings disparados pelo IDE resolvi pesquisar sobre.
Encontrei bastante conteúdo sobre ponto de sequência, mas o mais interessante foi esse http://c-faq.com/expr/seqpoints.html (em inglês).

Então tenha consciência em seus códigos e declarações.

Espero que tenham entendido! 🙂

Numa próxima falarei sobre o mesmo problema mas com variáveis globais ou ponteiros.

NOTA: Não sou nenhum expert no assunto ou similiar, caso eu tenha escrito algo errado ou irrelevante favor avisar para correção.

O que são os Loops e pra que servem?

Agora que eu já falei o que fazem as condições, vamos entender um pouco sobre o que são esses tais “Loops”.

Os loops não nada mais do que comandos repetitivos resumidos. Primeiro vamos ver os loops existentes e depois farei alguns exemplos. Não se preocupem, eles são bem simples.

O loop FOR

Sintaxe: for (declarações de variáveis; condição; ocorrência até a condição) { comandos; }
NOTA: Todos os parâmetros são opcionais.
Exemplo: for (int i = 0; i < 10; i++) { comandos; }

Isso fará com que execute o comando de i igual a 0 até 9.

O loop WHILE

Enquanto a condição for verdadeira o bloco de códigos dele é executado.

Sintaxe: while (condição) { comandos; }
Exemplo: int i = 0; while (i < 10) { comandos; i++; }

Nesse caso você precisará declarar a variável antes e não pode esquecer de incrementar a variável dentro do while.

Mas por que usar while se o for não tem perigo de esquecer o incremento da variável? É que o while é mais usado quando você quer usar condições dentro do loop, mas nada te impede de usar qualquer uma das duas funções.

O loop DO WHILE

Sintaxe: do { comandos; } while (condição);
Exemplo: int i = 0; do { comandos; i++; } while (i < 10);

E qual a diferença dos dois? Você deve ter notado que em um caso o while fica no começo e no outro ele aparece no final. A diferença é que no DO WHILE o loop será executado pelo menos uma vez enquanto que no WHILE ele primeiro verifica a condição antes de executar o bloco de códigos.

Agora vejamos alguns exemplos:

Se você quisesse escrever uma função que escreve na tela os números de 1 a 100. Imagina digitar 100 comandos printf()? Seria um código e um trabalho imensos né? Com um loop FOR isso se torna bem simples. Vejamos:

#include <cstdio>

int main()
{
    printf("Usando for\n");
    for (int i = 1; i <= 100; i++)
    {
        printf("%d\n", i);
    }
    // for sem paramametros
    printf("Usando for sem parametros");
    int t = 1;
    for (;;)
    {
        if (t > 100)
        {
            break;
        }
        printf("%d\n", t);
        t++;
    }
    // o mesmo com while
    int y = 1;
    printf("Usando while\n");
    while(y <= 100)
    {
        printf("%d\n", y);
        y++;
    }
    // usando do while
    printf("Usando do ... while\n");
    int x = 1;
    do
    {
        printf("%d\n", x);
        x++;
    } while(x <= 100);

}

Dentro dos loops podem ir dois comandos: continue e o break.

O break faz com que o loop seja finalizado instantaneamente.
O continue faz com que o loop passe para sua próxima iteração avaliando a condição caso ela exista.

Um exemplo do continue pode ser usado para escrever os números pares de 1 a 100.

#include <cstdio>

int main()
{
	for (int i = 1; i <= 100; i++)
	{
		if (i % 2)
			continue;
		printf("%d\n", i);
	}
}

Os loops tem várias útilidades, basta você ver quando será preciso usar cada um deles.

Até a próxima pessoal! 😀

As condições e o switch

Bom dia pessoal, eu ia escrever primeiro sobre ponteiros e matrizes mas resolvi falar primeiro sobre as condições e os loops.

As condições acho que a maioria de vocês já devem conhecer, pois ela deve estar presente em 99.99% dos códigos.

Mas o que são essas condições?
Como o nome já diz serve pra fazer uma verificação antes de exeucutar algum comando.

Antes de vermos um exemplo, vamos ver algumas das conições existentes:

Condição de relação Explicação
v1 == v2 v1 é igual a v2
v1 != v2 v1 é diferente v2
v1 >= v2 v1 é maior ou igual a v2
v1 <= v2 v1 é menor ou igual a v2
v1 > v2 v1 é menor que v2
v1 < v2 v1 é menor que v2
Condição lógica Explicação
!(v1 == v2) v1 é diferente de v2
(v1 > v2 || v1 < v3) v1 é maior que v2 ou v1 é menor que v3
(v1 > v2 && v1 < v3) v1 é maior que v2 e v1 é menor que v3

Vejamos o exemplo:

#include <cstdio>

int main()
{
	int x = 5;
	// Verifica se x é menor que zero
	if (x < 0)
	{
		printf("x é negativo\n");
	}
	// Se x não for menor que zero
	// Verifica se x é igual a zero
	else if (x = 0)
	{
		printf("x é nulo\n");
	}
	// Se x também não for igual a zero
	// Essa função será executada
	else
	{
		printf("x é positivo\n");
	}
	return 0;
}

Vejamos. Dentre todas as condições, a primeira que for verdadeira é executada e todo o resto do código é deixado de lado. A estrutura das condições são “if (condição) { comando; } else if (condição2) { comando2; } else { comando3; }”.

Mas podemos ter uma série de “ifs” que serão todos analisados. Vejamos:

#include <cstdio>

int main()
{
	int x = 5;
	if (x > 0) { printf("x é maior que 0\n"); }
	if (x > 3) { printf("x é maior que 3\n"); }
	if (x > 6) { printf("x é maior que 6\n"); }
	return 0;
}

Note que só é verdadeiro as duas primeiras condições, logo, o terceiro comando não será executado.

Existem outras formas de escrever essas condições. Condições que só tem um comando não precisam usar os brackets {}.

#include <cstdio>

int main()
{
	int x = 5;
	if (x < 0)
		printf("x é negativo\n");
	else if (x = 0)
		printf("x é nulo\n");
	else
		printf("x é positivo\n");
	return 0;
}

Também é possível usar um if/else de uma linha só:

#include <cstdio>

int main()
{
	int x = 5;
	printf((x < 5) ? "Menor que 5" : "Maior ou igual a 5");
}

Fica a diga do Gilson: "Esse tipo de if/else é chamado de condição ou operação ternária"

Os switchs não deixam de ser uma condição também, eles resumem uma condição cheia de “elses” deixando o código mais fácil de ler. Vejamos:

O código a seguir é feito cheio de condições:

#include <cstdio>

int main()
{
	int x = 1;
	if (x == 1)
		printf("X é igual a 1");
	else if (x == 2)
		printf("X é igual a 2");
	else
		printf("X não é nem 1 nem 2");
	return 0;
}

E o código abaixo faz a mesma coisa que o de cima só que usando o método do switch:

#include <cstdio>

int main()
{
	int x = 1;
	switch(x)
	{
		case 1:
			printf("X é igual a 1");
			break;
		case 2:
			printf("X é igual a 2");
			break;
		default:
			printf("X não é nem 1 nem 2");
	}
	return 0;
}

Um outro exemplo de switch que também é muito usado:

#include <cstdio>

int main()
{
	int x = 1;
	switch(x)
	{
		case 1:
		case 2:
			printf("X é igual a 1 ou 2");
			break;
		default:
			printf("X não é nem 1 nem 2");
	}
	return 0;
}

Espero que tenham gostado e até a próxima! 😀

Primeiro programa, uso de funções e variáveis

Boa tarde pessoal!

Hoje eu vim para ajudar a vocês a fazerem o primeiro primeiro programa em C++.

Para criar o seu programa você vai precisar de um IDE, que é um editor de textos com melhorias para a programação, e um compilador. Você encontra tudo isso aqui.

E para criar seu primeiro programa você precisa saber como é o padrão inicial de um código em C++, vejamos a seguir:

  1. Você precisa incluir as livrarias (libs) que serão usadas em seu código com o comando “#include”;
  2. E vai precisar também da função principal chamada pelo comando “int main() { }”.

Vejamos o exemplo mais conhecido, o “Hello World!”.

// Inclusão da lib cstdio
#include <cstdio>

// Função principal
int main()
{
	// Imprime na tela o texto entre aspas
	printf("Hello World!\n");
	// Deve ter um "return" pois a função é do tipo int
	// e tem que retornar um inteiro
	return 0;
}

Veja que foi impresso na tela o texto “Hello World!”. Se você está usando o “build target: debug” você vai ver o texto aparecendo. Mas o programa final é a versão “release” e esse texto aparecerá e o programa irá fechar automaticamente (quase que instantâneo).

Para que seja necessário o pressionamento de alguma tecla você pode usar a lib cstdlib e o comando system.

#include <cstdio>
#include <cstdlib>

int main()
{
	printf("Hello World!\n");
	// PAUSE é uma função do Windows
	// que faz esperar o pressionamento de alguma tecla
	system("PAUSE");
	return 0;
}

Em algumas ocasiões a lib pode vir entre <> ou entre “”. Entre <> significa que a lib é interna do compilador e entre “” é algum arquivo que você tenha no seu pc que seja desconhecido pelo compilador.

Agora vamos ver a criação de uma nova função para o seu código e o uso de variáveis. Vejamos o código a seguir: (O * significa que a variável hw aponta para um endereço na memória, veremos isso num próximo post sobre ponteiros e arrays)

As variáveis globais devem ser declaradas antes da função principal. E as funções podem ser criadas antes da função principal ou somente declaradas e criadas depois da função principal.

#include <cstdio>
#include <cstdlib>
#include <cstring>

// Variável global. Pode ser usada dentro de qualquer função
char hw[100];

// Função helloworld que muda o conteúdo da variável global acima
// Repare que essa função é do tipo void, logo não precisa retornar nada
void helloworld()
{
	// Função que copia uma string para a outra
	strcpy(hw,"Hello World!\n");
}

int main()
{
	helloworld();
	printf("%s", hw);
	system("PAUSE");
	// Note que inteiros não precisam estar entre ""
	int numeros = 1234567890;
	printf("%d\n", numeros);
	system("PAUSE");
	return 0;
}

Espero que vocês tenham entendido o funcionamento de novas funções ao seu programa. Essas funções são úteis para e diminuição do código evitando diversas partes repetidas no código princial.

Num próximo post estarei falando sobre “Ponteiros e Arrays” e um próximo sobre os “While Loops”.

As variáveis

Boa tarde pessoal,

Hoje eu vou falar um pouco sobre as variáveis básicas do C/C++.

Primeiro falaremos sobre as regras de declaração:

  1. As variáveis podem conter os caracteres de a a z, A a Z, 0 a 9 e _;
  2. As variáveis nunca podem começar por números;
  3. As letras maiúsculas e minúsculas são diferenciadas, ou seja, a variável var é diferente de VAR que também é diferente de Var.

Mas antes de declarar uma variável, precisamos saber qual o tipo de conteúdo ela vai ter. Esses são os tipos básicos existentes:

  1. int (Inteiro) que é usada para colocar valores numéricos inteiros;
  2. float (Real) que é usada para colocar valores reais;
  3. double (Real) que é usada para colocar valores reais mas como o dobro de precisão do float;
  4. bool (Lógico) que é usada para armazenar verdadeiro ou falso;
  5. char (Caractere) que é usada para armazenar valores do ASCII.

As variáveis podem ter três parâmetros a mais também: short, long e unsigned.

Veja a tabela abaixo:

Tipo Tamanho Signed Unsigned
int 4 bytes -2147483648 a 2147483647 0 a 4294967295
short int (short) 2 bytes -32768 a 32767 0 a 65535
long int (long) 4 bytes -2147483648 a 2147483647 0 a 4294967295
float 4 bytes +/- 3.4e +/- 38 (~7 dígitos)
double 8 bytes +/- 1.7e +/- 308 (~15 dígitos)
long double 8 bytes +/- 1.7e +/- 308 (~15 dígitos)
bool 1 byte true ou false
char 1 byte -128 a 127 0 a 255

As variáveis podem receber também operações matemáticas.

int soma = 5 + 5; // a variável soma vai conter o resultado 10
int divisao = 10 / 2; // a variável divisao vai conter o resultado 5

Veja alguns operadores que podem ser utilizados nas variáveis:

Símbolo Operação
+ Soma
Subtração
* Multiplicação
/ Divisão
% Módulo (resto)
& Operador E (and)
| Operador Ou (or)
^ Operador Ou Exclusivo (xor)

Algumas simplificações que também podem ser usadas:

Simplificação Operação
a++ a = a + 1
a- a = a – 1
a *= b a = a * b
a %= b a = a % b

Espero que esta aula seja de boa ajuda pra vocês… Até a próxima! 😀