Curso de C
Começando a Programar
Você está em: MarMSX >> Cursos >> C
Primeiramente, será apresentada a estruturação padrão de um programa em C, de modo que o leitor possa entender como organizar o código de seus programas.
Em seguida, a parte teórica principal e indispensável para a linguagem, como fluxo do programa, entrada e saída de dados e estruturas de controle.
Ao final, serão propostos alguns exercícios para por em prática o que foi aprendido.
Estrutura do Programa em C
Parte do programa |
Descrição |
#include <stdio.h> |
Chama as bibliotecas necessárias ao seu
programa. |
struct molde_ficha
{
char *nome;
char *endereco;
int idade;
}ficha1; ficha[500];
|
Definição de estruturas. |
int i, contador=0;
char tecla;
float media;
|
Declaração das variáveis globais. |
int area(int a, int b, int c)
{
return a*b*c;
) |
Criação de funções. Podem ser criadas
n funções aqui. |
main(int total_de_argumentos, char
*argumento[])
{
ficha1.nome = "Marcelo";
ficha[0].nome = "MarMSX";
}
|
Corpo principal do programa. |
Descrições gerais da linguagem C
O programa em C é dividido em declaração de variáveis, funções e corpo principal.
O corpo principal é onde o programa começa a executar, enquanto que as funções são sub-rotinas isoladas que têm como objetivo executar determinadas tarefas.
Assim como em outras linguagens, o programa executa as instruções linha a linha, sempre começando pelas linhas do topo de cada estrutura.
As instruções são delimitadas pelo caractere ponto e vírgula ";". Dessa forma, quando há duas expressões separadas por ponto e vírgula na mesma linha, é como se fossem duas linhas diferentes. Ex:
a=1; b=b+2;
equivale a:
a=1;
b=b+2;
Também podemos colocar uma expressão espalhada em diversas linhas. Como o caractere ponto e vírgula é quem delimita as instruções, o compilador entenderá o bloco todo como sendo a mesma instrução. Ex:
int a[] = { 1,2,
3,4 };
equivale a:
int a[] = { 1,2,3,4 };
As chaves ("{" e "}") delimitam o corpo de uma função ou estrutura. São semelhantes ao BEGIN e END do Pascal.
Quando uma estrutura tiver somente uma instrução (linha), não é necessário indicar o seu inicio e fim com as chaves. Ex:
if (a == 8)
c = c + 1;
Como fazer para rodar programas em C?
Por exemplo, a linguagem Basic já se encontra residente na ROM do MSX, possuindo assim um ambiente nativo de edição e execução dos programas nessa linguagem.
No caso da linguagem C, é necessário possuir um pacote de software contendo um compilador C para gerar o arquivo executável, conforme foi visto no primeiro capítulo. Além disso, é necessário ter em mãos o sistema operacional MSX-DOS, pois tanto o compilador como os programas compilados necessitam desse sistema para funcionar.
O C é uma linguagem compilada, ou seja, o código fonte (programa em C) é convertido para código de máquina (Assembly) e depois é executado.
Diferente do Basic, onde a listagem do programa reside na memória, o C (e também o Pascal) armazenam a listagem do programa arquivo em um arquivo no formato texto. Então, esse arquivo contendo a listagem do programa em C é utilizado pelo compilador para gerar o arquivo executável.
Ao executar o compilador Hi-Tech C no MSX-DOS, utilizamos o comando "c" para compilar um código fonte em C, no qual geralmente irá possuir uma extensão ".c".
No exemplo a seguir, vamos compilar um código fonte chamado "programa.c". Para esse compilador, DEVEMOS omitir a extensão ".c", sob pena de ele sobrescrever o arquivo original.
A> c programa
Irá gerar o arquivo "programa.com". Execute ele no MSX-DOS, chamando-o pelo nome sem a extensão:
A> programa
Os arquivos fonte do C possuem a extensão ".c", enquanto que os executáveis possuem a extensão ".com".
O C não possui funções e comandos básicos presentes diretamente no compilador como nas outras linguagens. Eles estão sempre localizados em bibliotecas, que são chamadas através do comando "#include". A biblioteca básica para o funcionamento do C é a "stdio.h".
Um programa mínimo no C necessita apenas do corpo principal. Exemplo:
main()
{
// Esse programa não faz nada
}
Veja esse outro exemplo:
main()
{
int a=5;
a = a+3;
}
Entretanto, quando utilizarmos funções mais sofisticadas como a E/S de informações na tela (comando printf), é necessário chamar a biblioteca "stdio.h". Exemplo:
#include <stdio.h>
main()
{
int a=5;
a = a+3;
printf("Valor de a: %d\n", a);
}
Comentários
Os comentários servem para auxiliar os programadores a compreender o código do programa. Eles são feitos, utilizando-se duas formas:
- Duas barras "//" antes do texto, que valem somente em uma linha
- Barra mais asterisco "/*" antes do texto e asterisco barra "*/" ao final do texto
Exemplos:
// Esta função calcula o produto de dois números
int produto(int a, int b)
{
return a*b;
}
/* Esta função calcula o produto de dois números */
int produto(int a, int b)
{
return a*b;
}
O texto dos comentários é totalmente ignorado pelo compilador.
Exemplos em várias linhas:
// Esta função calcula
// o produto de dois
// números
/* Esta função calcula
o produto de dois
números */
Obs: o compilador Hi-Tech C do MSX somente aceita os comentários na forma "/*" e "*/".
Corpo principal
O corpo principal de um programa em C é o primeiro trecho de código do programa a ser executado. Ele tem a estrutura de uma função, possui o nome "main" e pode ser declarado das seguintes formas:
int main(void)
{
// Código
return 1;
}
void main(void)
{
// Código
}
main()
{
// Código
}
Os programas em C (assim como em Pascal) permitem que o usuário passem parâmetros através da linha de comando do MSX-DOS, quando o programa é chamado. O número de parâmetros são em tese ilimitados e eles são separados pelos espaços em branco.
Por exemplo o comando a seguir executado no DOS:
A> teste azul grande
Chama o programa teste e envia os parâmetros "azul" e "grande".
Assim, quando desejamos obter dados da linha de comando do DOS, devemos acrescentar ao nosso programa em C os seguintes parâmetros à função main, em substituição a palavra "void":
main(int total_de_argumentos, char *argumento[])
Onde "total_de_argumentos" é o número de argumentos passados quando o programa é chamado pelo DOS e "*argumento[]" é um vetor contendo as strings dos parâmetros passados.
Por exemplo, seja o arquivo teste.com:
A> teste azul grande
O valor passado para total_de_argumentos é 3, variando de 0 a 2. temos as seguintes configurações para o parâmetros argumento:
argumento[0] = teste.com
argumento[1] = azul
argumento[2] = grande
Caso não trabalhemos com passagem de parâmetros pela linha de comando, simplesmente colocamos main(void), conforme já tinha sido feito até o presente momento.
O exemplo a seguir irá receber o nome de uma pessoa como argumento e escrevê-la na tela.
#include <stdio.h>
main(int argc, char *arg[])
{
printf("Seu nome e': %s\n", arg[1]);
}
Compilando:
A> c nome
Executando:
A> nome MarMSX
Seu nome e': MarMSX
Se você tentar usar strings com espaço em branco, somente a primeira string será escrita. Ex:
A> nome Rio de Janeiro
Seu nome e': Rio
Para contornar isso, utilize as aspas duplas para delimitar a string:
A> nome "Rio de Janeiro"
Seu nome e': Rio de Janeiro
Variáveis
As variáveis servem para armazenar dados durante a execução de um programa. Assim como no Pascal, a declaração da variável é obrigatória antes do seu uso. Deve-se indicar o tipo de dado armazenado na variável em sua declaração e a alteração do tipo de dado declarado no decorrer do programa não é permitida.
O capítulo 2 apresentou os principais tipos de variáveis do C.
Estruturas
As estruturas permitem a criação de variáveis mais complexas. Elas são abordadas no capítulo 7.
Includes
O C permite a inclusão de bibliotecas, contendo funções específicas para resolver determinados problemas. O capítulo 9 aborda esse assunto em detalhes.
Funções
As funções são sub-rotinas que executam tarefas específicas. Elas funcionam como uma "caixa-preta", onde recebem dados de entrada, realizam algum processamento e produzem uma resposta. Elas são abordadas no capítulo 12.
Fluxo de execução
Seja o programa abaixo:
#include <stdio.h>
int b;
main()
{
b = 3+2;
printf("Resultado: %d\n", b);
}
O fluxo de execução desse programa, assinalado pelos números ao final da linha, é:
#include <stdio.h>
int b;
main()
{
b = 3+2; 1
printf("Resultado: %d\n", b); 2
}
Saída:
Resultado: 5
Seja esse outro programa abaixo:
#include <stdio.h>
int b;
int calcula(int a, int b)
{
a = a+3;
return a*b;
}
main()
{
b = calcula(2,3);
printf("Resultado: %d\n", b);
}
O fluxo de execução desse programa, assinalado pelos números ao final da linha, é:
#include <stdio.h>
int b;
int calcula(int a, int b)
{
a = a+3; 2
return a*b; 3
}
main()
{
b = calcula(2,3); 1
printf("Resultado: %d\n", b); 4
}
Saída:
Resultado: 15
Entrada e saída de dados - PRINTF e SCANF
Saída de dados - PRINTF
A função "printf" serve para imprimir dados formatados na tela.
Sintaxe:
printf("formatação", variavel_1, variável_2, ..., variavel_n);
Na formatação, podemos colocar:
- Strings
- Uma referência a uma variável que é passada como parâmetro para a função "scanf"
- Caracteres especiais
Veja o exemplo a seguir:
printf("Funcionario %d: %s\n", i, nome);
Os símbolos "%d" e "%s" fazem referência às variáveis "i" e "nome", respectivamente. Esses símbolos serão substituídos pelo conteúdo das variáveis.
Tipos de dados possíveis na formatação:
Símbolo |
Significado |
"%c" |
Caractere |
"%s" |
String |
"%d" |
Inteiro |
"%hd" |
Inteiro short |
"%ld" |
Inteiro long |
"%o" |
Octal |
"%x" |
Hexadecimal |
"%f" |
Ponto flutuante |
Exemplo:
int n=15;
printf("O valor de %d em hexadecimal é: %x\n", n, n);
Saída:
O valor de 15 em hexadecimal é: F
O símbolo "\n" é um caractere especial que faz o cursor saltar para a próxima linha.
Principais caracteres especiais são:
Símbolo |
Significado |
\n |
Nova linha. |
\t |
Tabulação. |
\b |
Backspace. |
\f |
Salta página de formulário. |
\0 |
Caractere nulo. |
\xhh |
Caractere representado pelo ASCII hh, em hexa. |
\nnn |
Byte na casa octal. |
\\ |
Imprime "\" |
Exemplo:
printf("Isto será \n impresso assim \\");
Saída:
Isto será
impresso assim \
Entrada de dados - SCANF
A função "scanf" serve para ler dados do teclado e necessita que seja indicado o tipo de variável que será lida.
Sintaxe:
scanf(tipo_de_dado, nome_da_variavel);
Tipos de variáveis possíveis:
Símbolo |
Significado |
"%c" |
Caractere |
"%s" |
String |
"%d" |
Inteiro |
"%f" |
Ponto flutuante |
O "scanf" exige o símbolo "&" antes antes do nome da variável estática, uma vez que o parâmetro é do tipo ponteiro. Entretanto, como uma string já é um ponteiro, ela não leva o símbolo. Exemplo:
#include <stdio.h>
main()
{
char *nome;
int idade;
printf("Entre com o nome: ");
scanf("%s", nome);
printf("Entre com a idade: ");
scanf("%d", &idade);
printf("%s tem %d anos.\n", nome, idade);
}
Saída:
Entre com o nome: Felix
Entre com a idade: 12
Felix tem 12 anos.
Estruturas de controle
IF
A estrutura IF testa se uma determinada condição é verdadeira ou falsa, executando um trecho de código para cada situação.
Sintaxe:
if (condição)
{
// Código para a condição verdadeira
}
else
{
// Código para a condição falsa
}
Regras para o IF no C:
- A condição é um teste lógico que poderá ter diversas entradas e testes lógicos, mas deverá fornecer apenas uma saída.
- O uso do parêntesis para o IF é obrigatório.
- O tratamento do IF é obrigatório, mas o tratamento do ELSE é facultativo.
- O uso das chaves é facultativo quando qualquer um dos tratamentos possuir apenas uma sentença.
Exemplo para um teste lógico simples:
if (salario > 1500.0)
Exemplo para um teste lógico mais complexo:
if (salario > 1500.0 && idade < 60)
Ordem dos testes:
1. a = salario > 1500.0
2. b = idade < 60
3. a E b
Neste exemplo, basta que um dos teste lógicos seja falso para invalidar a condição.
Exemplo de tratamento com apenas uma sentença:
if (a == 4)
printf("Valor correto.\n");
Observe que a situação a seguir requer o uso das chaves.
/* Errado: */
if (a == 4)
printf("Valor correto.\n"); printf("Muito bem.\n");
/* Certo: */
if (a == 4)
{
printf("Valor correto.\n");
printf("Muito bem.\n");
}
A sentença errada no exemplo anterior fará com que a mensagem "Muito bem" seja impressa de qualquer maneira, mesmo quando "a" for diferente de 4, pois ela é equivalente ao seguinte código:
if (a == 4)
printf("Valor correto.\n");
printf("Muito bem.\n");
O teste lógico de igualdade no C é um duplo sinal de igual "==". Veja mais sobre os operadores do C no capítulo 4.
IF-ELSE-IF
É possível realizar testes em cascata, onde cada tratamento ELSE necessita de mais um teste. Exemplo:
#include <stdio.h>
main()
{
int temp;
printf("Entre a temperatura: ");
scanf("%d", &temp);
if (temp < 20)
printf("Frio.\n");
else if (temp < 30)
printf("Agradável.\n");
else
printf("Calor.\n");
}
Saídas:
Entre a temperatura: 19
Frio.
Entre a temperatura: 20
Agradável.
Entre a temperatura: 30
Calor.
O IF recebe valores menor que 20.
O primeiro ELSE recebe valores maiores ou iguais a 20. Quando testamos se "temp<30", na verdade fazemos com que sejam tratados os valores iguais ou maiores que 20 e menores que 30.
O último ELSE trata valores maiores ou iguais a 30.
Repetição - FOR, WHILE e DO-WHILE
O C tem 3 tipos de laços de repetição: FOR, WHILE e DO-WHILE . Para estas estruturas, devemos colocar os delimitadores "{" e "}", toda vez que o código dentro da estrutura tiver mais que uma linha, conforme visto nos exemplos acima.
for (inicio; condição; passo)
{
bla, bla, bla ....
}
while (condição)
{
bla, bla, bla ...
}
do
{
bla, bla, bla ...
} while (condição);
A condição é um teste lógico para a continuação do loop, ou seja, o laço deve continuar enquanto a condição for verdadeira. Os testes lógicos podem ser feitos como os exemplos a seguir.
- a > 10
- b < c
- idade == 15
- (salario > 1000) && (tempo_servico > 5)
FOR
Sintaxe para o FOR:
for (variável_de_controle = valor_inicial; teste_lógico; incremento/decremento)
{
// Código
}
Onde:
- Variável de controle - variável que controla a execução do laço.
- valor_inicial - valor inicial da variável de controle.
- teste_logico - teste realizado em cima da variável de controle, onde o loop é mantido enquanto o resultado for verdadeiro.
- incremento/decremento - indica de quanto a variável de controle deverá ser aumentada ou diminuída a cada iteração do loop.
Exemplo:
#include <stdio.h>
int a;
int main(void)
{
for (a=1; a<10; a=a+2)
printf("%d\n", a);
return 1;
}
Saída:
1
3
5
7
9
No exemplo, temos:
- a - variável de controle.
- a<=10 - teste_lógico (enquanto verdadeiro, continue).
- a=a+2 - incremento.
O comando Basic correspondente é:
FOR a=1 TO 10 STEP 2
Deve-se tomar cuidado com o teste lógico, uma vez que situações absurdas podem ser criadas. Veja o exemplo:
unsigned int i;
for (i=1; i>0; i++)
printf("%d\n", i);
Irá rodar infinitamente, uma vez que a condição de continuidade (teste lógico) nunca se tornará falso.
WHILE e DO-WHILE
Também são estruturas de repetição como o FOR. Entretanto, o valor inicial e o incremento é feito pelo programador.
O que ambas estruturas fazem é separar um trecho de código, realizar um loop, enquanto uma dada condição for verdadeira.
Sintaxes:
while (condição)
{
// Código
}
do
{
// Código
} while (condição);
O WHILE e DO-WHILE geralmente são utilizados em estruturas de repetição onde não é necessária uma variável de controle, mas apenas uma condição de permanência.
Então, como o laço é abandonado? Assim que violamos a condição de permanência.
Por exemplo, queremos ler dados de entrada até que um determinado valor seja digitado para sair.
#include <stdio.h>
main()
{
int i=0;
do
{
printf("Entre com um número: ");
scanf("%d", &i);
} while (i!=5);
printf("Você digitou 5 !\n");
}
Saída:
Entre com um número: 3
Entre com um número: 6
Entre com um número: 7
Entre com um número: 1
Entre com um número: 5
Você digitou 5 !
A condição de permanência do laço DO-WHILE no programa anterior é que o número digitado pelo usuário seja DIFERENTE de 5. Assim, quando digitamos o valor 5, violamos essa condição e o laço é abandonado.
Podemos também utilizar o WHILE com uma função semelhante a do FOR.
No exemplo a seguir, vamos reescrever o programa do FOR adaptado ao WHILE:
#include <stdio.h>
int a=1;
main()
{
while (a < 10)
{
printf("%d\n", a);
a = a + 2;
}
}
Saída:
1
3
5
7
9
A variável "a" no programa anterior realiza o mesmo papel da "variável de controle" do FOR.
A diferença entre o WHILE e o DO-WHILE é que o primeiro realiza o teste no inicio do bloco de repetição, enquanto que o segundo faz o teste no final do bloco.
Assim, para entrar e também para sair do bloco do WHILE, a condição deverá ser verdadeira. Ex:
int a=11;
while (a<10)
printf("%d\n",a);
Não imprime nada, pois a condição de entrada é falsa.
Já a mesma situação para o DO-WHILE imprime o valor, mesmo sendo maior que a condição de saída:
int a=11;
do
{
printf("%d\n",a);
} while (a<10);
Saída:
11
Saindo do laço ou passando para a próxima iteração - BREAK e CONTINUE
A instrução BREAK permite abandonar qualquer laço repetitivo. Exemplo:
#include <stdio.h>
main()
{
int i;
for (i=1; i<=10; i++)
{
if (i==5)
break;
printf("%d ",i);
}
}
Saída:
1 2 3 4
Já a função CONTINUE ignora o restante do código contido dentro de um laço repetivo e passa para a próxima iteração. Exemplo:
#include <stdio.h>
main()
{
int i;
for (i=1; i<=10; i++)
{
if (i==5)
continue;
printf("%d ",i);
}
}
Saída:
1 2 3 4 6 7 8 9 10
Quando i for 5, a instrução "printf" é ignorada, passando para a próxima iteração, onde i=6.
EXERCÍCIOS
PROBLEMA 1 - Fazer um programa que receba as horas e dê bom dia, boa tarde ou boa noite.
Solução:
#include <stdio.h>
int horas;
int main(void)
{
printf("Qual a hora? ");
scanf("%d",&horas);
if ((horas >= 6) && (horas < 12))
printf("Bom dia.");
if ((horas >=12) && (horas < 19))
printf("Boa tarde.");
if ((horas >=19) || (horas < 6))
printf("Boa noite.");
return 1;
}
Comentários:
A "condição" pode receber vários testes como entrada, mas deve produzir uma única saída. É isso que é feito no exercício 1, onde são realizados três testes lógicos, produzindo uma saída. Veja como:
teste 1: horas >= valor_1 // Horas é maior ou igual ao valor_1 ?
teste 2: horas < valor_2 // Horas é menor que o valor_2 ?
teste 3: teste_1 E teste_2 // teste_1 e teste_2 são ambos verdadeiros ?
Este teste verifica se o valor de "horas" está dentro de uma faixa que começa por valor_1 e termina por valor_2.
PROBLEMA 2 - Fazer um programa que calcula a média aritmética de 3 notas fornecidas.
Solução:
#include <stdio.h>
float a, b, c, media;
int main(void)
{
printf("Entre com a nota 1: ");
scanf("%f",&a);
printf("Entre com a nota 2: ");
scanf("%f",&b);
printf("Entre com a nota 3: ");
scanf("%f",&c);
media = (a+b+c)/3.0;
printf("A média final é: %.1f\n", media);
}
Saída:
Entre com a nota 1: 7
Entre com a nota 2: 8
Entre com a nota 3: 9
A média final é: 8.0
Comentários:
O programa lê e armazena três notas em três variáveis, depois calcula a média aritmética e imprime.
E se tivéssemos 100 notas? Podemos tornar o programa mais eficiente, criando um loop para calcular a média. Veja como:
#include <stdio.h>
float n, media=0.0;
int i, num_alunos=3;
int main(void)
{
for (i=0; i<num_alunos; i++)
{
printf("Entre com a nota %d: ", i+1);
scanf("%f", &n);
media = media + n;
}
media = media/num_alunos;
printf("A média final é: %.1f\n", media);
}
PROBLEMA 3 - Escreva "EU AMO O MSX" 100 vezes na tela.
Solução:
#include <stdio.h>
void main(void)
{
int i;
for (i=0; i<100; i++)
printf("EU AMO O MSX\n");
}
PROBLEMA 4 - Fazer um programa que calcula o tamanho de uma palavra. Veja a biblioteca string.h.
Solução:
#include <stdio.h>
#include <string.h>
char nome[20];
void main(void)
{
printf("Entre o seu nome: ");
scanf("%s",nome);
printf("Seu nome tem %d letras.\n", strlen(nome));
}
Saída:
Entre o seu nome: Felipe
Seu nome tem 6 letras.
PROBLEMA 5 - Fazer um programa que calcule o valor da hipotenusa e a área de um triângulo retângulo.
Solução:
#include <stdio.h>
#include <math.h>
void main(void)
{
float cateto_adjacente, cateto_oposto, hipotenusa, area;
printf("Entre com o comprimento do cateto adjacente: ");
scanf("%f", &cateto_adjacente);
printf("Entre com o comprimento do cateto oposto: ");
scanf("%f", &cateto_oposto);
/* Calcula */
hipotenusa = sqrt(pow(cateto_adjacente, 2) + pow(cateto_oposto, 2));
area = (cateto_adjacente * cateto_oposto) / 2.0;
printf("Valor da hipotenusa: %.2f\n", hipotenusa);
printf("Valor da área: %.2f\n", area);
}
Saída:
Entre com o comprimento do cateto adjacente: 3
Entre com o comprimento do cateto oposto: 4
Valor da hipotenusa: 5.00
Valor da área: 6.00
Comentários:
Cálculos matemáticos mais avançados normalmente fazem uso da biblioteca "math.h". No caso do programa acima, é necessário fazer uso de duas funções presentes nessa biblioteca: "sqrt", que calcula a raiz quadrada de um número e "pow", que calcula um número elevado a enésima potência.
No MSX, é necessário utilizar a opção "-lf" na compilação do programa com o Hitech-C quando há pontos flutuantes.
A> c -lf programa
Caso o nome do programa fonte seja "programa.c".