Capítulo – 5
Introdução à programação C
Introdução
'C' é uma das linguagens de programação mais populares no mundo da computação moderna. A linguagem de programação C foi projetada e criada por Brian Kernighan e Dennis Ritchie no Bell Research Labs em 1972.
'C' é uma linguagem projetada especificamente para dar ao programador acesso a quase todos os componentes internos de uma máquina: registradores, slots de E/S e endereços absolutos. Ao mesmo tempo, 'C' permite tanto processamento de dados e modularização de texto programado quanto for necessário para que projetos de multiprogramação muito complexos possam ser construídos de forma organizada e oportuna.
Embora a linguagem tenha sido originalmente concebida para rodar no UNIX, houve grande interesse em rodá-la no sistema operacional MS-DOS no IBM PC e compatíveis. É uma linguagem excelente para esse ambiente devido à sua simplicidade de expressão, compacidade de código e ampla gama de aplicabilidade.
Além disso, devido à simplicidade e facilidade de escrever um compilador C, ele geralmente é a primeira linguagem de alto nível disponível em qualquer computador novo, incluindo microcomputadores, minicomputadores e mainframes.
Por que usar C na programação de recuperação de dados
No mundo atual da programação de computadores, há muitas linguagens de alto nível disponíveis. Essas linguagens são boas porque têm muitos recursos adequados para a maioria das tarefas de programação. No entanto, há vários motivos pelos quais C é a primeira escolha de programadores que desejam fazer programação de recuperação de dados, programação de sistemas, programação de dispositivos ou programação de hardware:
- C é uma linguagem popular preferida por programadores profissionais. Como resultado, uma ampla gama de compiladores C e acessórios úteis estão disponíveis.
- C é uma linguagem portátil . Um programa C escrito para um sistema de computador pode ser compilado e executado em outro sistema com pouca ou nenhuma modificação. A portabilidade é melhorada pelo padrão ANSI para C, um conjunto de regras para compiladores C.
- C permite uso extensivo de módulos na programação. O código C pode ser escrito em sub-rotinas chamadas funções. Essas funções podem ser reutilizadas em outros aplicativos ou programas. Você não precisa fazer esforço extra ao programar um novo aplicativo para criar o mesmo módulo que você desenvolveu em outro aplicativo anteriormente.
Você pode usar esse recurso no novo programa sem nenhuma alteração ou com algumas pequenas alterações. No caso da programação de recuperação de dados, você achará essa qualidade muito útil quando precisar executar as mesmas funções várias vezes em diferentes aplicativos de diferentes programas.
- C é uma linguagem poderosa e flexível. É por isso que C é usado para projetos tão diversos, como sistemas operacionais, processadores de texto, gráficos, planilhas e até mesmo compiladores para outras linguagens.
- C é uma linguagem de poucas palavras, contendo apenas alguns termos, chamados palavras-chave, que servem como base sobre a qual a funcionalidade da linguagem é construída. Essas palavras-chave, também chamadas de palavras reservadas, tornam a linguagem mais poderosa e oferecem um amplo escopo de programação, fazendo com que o programador se sinta capaz de fazer qualquer tipo de programação em C.
Deixe-me supor que você não saiba nada sobre C.
Imagino que você não saiba nada sobre programação em C e não tenha ideia sobre programação. Começarei com os conceitos mais básicos da linguagem C e levarei você até a programação C de alto nível, incluindo os conceitos geralmente intimidadores de ponteiros, estruturas e alocação dinâmica de memória.
Levará muito tempo e esforço para você entender completamente esses conceitos, porque eles não são fáceis de entender, mas são ferramentas muito poderosas.
A programação em C é um grande trunfo em áreas onde você pode precisar usar a linguagem assembly, mas prefere mantê-la simples de escrever e manter. O tempo economizado pela codificação em C pode ser enorme nesses casos.
Embora a linguagem C tenha um bom histórico quando programas são transportados de uma implementação para outra, há diferenças nos compiladores que você encontrará sempre que tentar usar outro compilador.
A maioria das diferenças se torna aparente quando você usa extensões não padronizadas, como chamadas para o BIOS do DOS ao usar o MS-DOS, mas mesmo essas diferenças podem ser minimizadas pela escolha cuidadosa de construções de programação.
Quando ficou evidente que a linguagem de programação C estava se tornando uma linguagem muito popular disponível em uma ampla gama de computadores, um grupo de indivíduos preocupados se reuniu para propor um conjunto padrão de regras para o uso da linguagem de programação C.
O grupo representava todos os setores da indústria de software e, após muitas reuniões e muitos rascunhos preliminares, eles finalmente escreveram um padrão aceitável para a linguagem C. Ele foi aceito pelo American National Standards Institute (ANSI) e pela International Standards Organization (ISO) .
Ele não é imposto a nenhum grupo ou usuário, mas, como é tão amplamente aceito, seria suicídio econômico para qualquer redator de compiladores se recusar a se conformar ao padrão.
Os programas escritos neste livro são principalmente para uso em um IBM-PC ou computador compatível, mas podem ser usados com qualquer compilador padrão ANSI, pois ele está em conformidade com o padrão ANSI.
Vamos começar
Antes de poder fazer qualquer coisa em qualquer linguagem e começar a programar, você precisa saber como nomear um identificador. Um identificador é usado para qualquer variável, função, definição de dados, etc. Na linguagem de programação C, um identificador é uma combinação de caracteres alfanuméricos, sendo o primeiro uma letra do alfabeto ou um sublinhado, e o restante sendo qualquer letra do alfabeto, qualquer dígito numérico ou o sublinhado.
Duas regras devem ser mantidas em mente ao nomear identificadores.
- O caso de caracteres alfabéticos é significativo. C é uma linguagem sensível a maiúsculas e minúsculas. Isso significa que Recovery é diferente de recovery e rEcOveRY é diferente de ambos mencionados antes.
- De acordo com o padrão ANSI-C, pelo menos 31 caracteres significativos podem ser usados e serão considerados significativos por um compilador ANSI-C em conformidade. Se mais de 31 forem usados, todos os caracteres além do 31º podem ser ignorados por qualquer compilador fornecido.
Palavras-chave
Existem 32 palavras definidas como palavras-chave em C. Elas têm usos predefinidos e não podem ser usadas para nenhuma outra finalidade em um programa C. Elas são usadas pelo compilador como um auxílio para compilar o programa. Elas são sempre escritas em letras minúsculas. Segue uma lista completa:
auto |
quebrar |
caso |
Caracteres |
constante |
continuar |
padrão |
fazer |
dobro |
outro |
enumeração |
externo |
flutuador |
para |
Vá para |
se |
Inteiro |
longo |
registrar |
retornar |
curto |
assinado |
tamanho de |
estático |
estrutura |
trocar |
tipo definido |
união |
não assinado |
vazio |
volátil |
enquanto |
Aqui vemos a mágica de C. A maravilhosa coleção de apenas 32 palavras-chave oferece um amplo uso em diferentes aplicações. Qualquer programa de computador tem duas entidades a considerar, os dados e o programa. Eles são altamente dependentes um do outro e o planejamento cuidadoso de ambos leva a um programa bem planejado e bem escrito.
Vamos começar com um programa C simples:
/* Primeiro programa a aprender C */
#incluir <stdio.h>
vazio principal()
{
printf("Este é um programa C\n"); // imprimindo uma mensagem
}
Embora o programa seja muito simples, alguns pontos são dignos de nota. Vamos examinar o programa acima. Tudo o que está dentro de /* e */ é considerado um comentário e será ignorado pelo compilador. Você não deve incluir comentários dentro de outros comentários, então algo como isto não é permitido:
/* este é um /* comentário */ dentro de um comentário, o que está errado */
Há também uma maneira de documentação que funciona dentro de uma linha. Usando // podemos adicionar uma pequena documentação dentro dessa linha.
Todo programa C contém uma função chamada main. Este é o ponto inicial do programa. Toda função deve retornar um valor. Neste programa, a função main não retorna nenhum valor de retorno, portanto, escrevemos void main. Também poderíamos escrever este programa como:
/* Primeiro programa a aprender C */
#incluir <stdio.h>
principal()
{
printf("Este é um programa C\n"); // imprimindo uma mensagem
retornar 0;
}
Ambos os programas são iguais e realizam a mesma tarefa. O resultado de ambos os programas imprimirá a seguinte saída na tela:
Este é um programa C
#include<stdio.h> permite que o programa interaja com a tela, teclado e sistema de arquivos do seu computador. Você o encontrará no início de quase todos os programas em C.
main() declara o início da função, enquanto as duas chaves mostram o início e o fim da função. Chaves em C são usadas para agrupar instruções como em uma função, ou no corpo de um loop. Tal agrupamento é conhecido como uma instrução composta ou um bloco.
printf("Este é um programa C\n"); imprime as palavras na tela. O texto a ser impresso é colocado entre aspas duplas. O \n no final do texto diz ao programa para imprimir uma nova linha como parte da saída. A função printf() é usada para monitorar a exibição da saída.
A maioria dos programas em C está em letras minúsculas. Você normalmente encontrará letras maiúsculas usadas em definições de pré-processador que serão discutidas mais tarde, ou dentro de aspas como partes de strings de caracteres.
Compilando o programa
Deixe o nome do nosso programa ser CPROG.C. Para entrar e compilar o programa C, siga estes passos:
- Crie o diretório ativo dos seus programas C e inicie seu editor. Para isso, qualquer editor de texto pode ser usado, mas a maioria dos compiladores C, como o Turbo C++ da Borland, tem um ambiente de desenvolvimento integrado (IDE) que permite que você insira, compile e vincule seus programas em uma configuração conveniente.
- Escreva e salve o código-fonte. Você deve nomear o arquivo CPROG.C.
- Compile e vincule CPROG.C. Execute o comando apropriado especificado pelos manuais do seu compilador. Você deve receber uma mensagem informando que não houve erros ou avisos.
- Verifique as mensagens do compilador. Se você não receber erros ou avisos, tudo deve estar bem. Se houver algum erro na digitação do programa, o compilador o pegará e exibirá uma mensagem de erro. Corrija o erro, exibido na mensagem de erro.
- Seu primeiro programa em C agora deve estar compilado e pronto para rodar. Se você exibir uma listagem de diretórios de todos os arquivos chamados CPROG, você obterá os quatro arquivos com extensões diferentes descritos a seguir:
- CPROG.C, o arquivo de código-fonte
- CPROG.BAK, o arquivo de backup do arquivo de origem que você criou com o editor
- CPROG.OBJ, contém o código objeto para CPROG.C
- CPROG.EXE, o programa executável criado quando você compilou e vinculou CPROG.C
- Para executar, ou executar, CPROG.EXE, simplesmente digite cprog. A mensagem, This is a C program é exibida na tela.
Agora vamos examinar o seguinte programa:
/* Primeiro programa a aprender C */ // 1
// 2
#incluir <stdio.h> // 3
// 4
principal() // 5
{
// 6
printf("Este é um programa C\n"); // 7
// 8
retornar 0; // 9
} // 10
Ao compilar este programa, o compilador exibe uma mensagem semelhante à seguinte:
cprog.c(8) : Erro: `;' esperado
vamos dividir essa mensagem de erro em partes. cprog.c é o nome do arquivo onde o erro foi encontrado. (8) é o número da linha onde o erro foi encontrado. Erro: `;' esperado é Uma descrição do erro.
Esta mensagem é bem informativa e informa que na linha 8 de CPROG.C o compilador esperava encontrar um ponto e vírgula, mas não encontrou. No entanto, você sabe que o ponto e vírgula foi omitido da linha 7, então há uma discrepância.
Por que o compilador relata um erro na linha 8 quando, na verdade, um ponto e vírgula foi omitido da linha 7. A resposta está no fato de que C não se importa com coisas como quebras entre linhas. O ponto e vírgula que pertence depois da declaração printf() poderia ter sido colocado na próxima linha, embora fazer isso seria uma programação ruim na prática.
Somente após encontrar o próximo comando (return) na linha 8 o compilador tem certeza de que o ponto e vírgula está faltando. Portanto, o compilador relata que o erro está na linha 8.
Pode haver uma série de possibilidades de diferentes tipos de erros. Vamos discutir mensagens de erro de vinculação. Erros de vinculador são relativamente raros e geralmente resultam de erros de grafia no nome de uma função de biblioteca C. Nesse caso, você obtém uma mensagem de erro Error: undefined symbols:, seguida pelo nome com erro de grafia. Depois que você corrigir a grafia, o problema deve desaparecer.
Números de impressão
Vejamos o seguinte exemplo:
// Como imprimir os números //
#incluir<stdio.h>
vazio principal()
{
int num = 10;
printf(“O número é %d”, num);
}
A saída do programa será exibida na tela da seguinte forma:
O número é 10
O sinal % é usado para sinalizar a saída de muitos tipos diferentes de variáveis. O caractere após o sinal % é ad, que sinaliza a rotina de saída para obter um valor decimal e emiti-lo.
Usando Variáveis
Em C, uma variável deve ser declarada antes de poder ser usada. Variáveis podem ser declaradas no início de qualquer bloco de código, mas a maioria é encontrada no início de cada função. A maioria das variáveis locais é criada quando a função é chamada e são destruídas no retorno dessa função.
Para usar variáveis em seus programas C, você deve conhecer as seguintes regras ao dar nomes a variáveis em C:
- O nome pode conter letras, dígitos e o caractere sublinhado (_).
- O primeiro caractere do nome deve ser uma letra. O sublinhado também é um primeiro caractere legal, mas seu uso não é recomendado.
- C diferencia maiúsculas de minúsculas, portanto o nome da variável num é diferente de Num.
- Palavras-chave C não podem ser usadas como nomes de variáveis. Uma palavra-chave é uma palavra que faz parte da linguagem C.
A lista a seguir contém alguns exemplos de nomes de variáveis C legais e ilegais:
Nome da Variável |
Legal ou não |
Num |
Jurídico |
Ttpt2_t2p |
Jurídico |
Tt ponto |
Ilegal: Espaço não é permitido |
_1990_imposto |
Legal, mas não aconselhável |
Jack_telefone# |
Ilegal: Contém o caractere ilegal # |
Caso |
Ilegal: É uma palavra-chave C |
1 livro |
Ilegal: O primeiro caractere é um dígito |
A primeira coisa nova que se destaca é a primeira linha do corpo de main():
int num = 10;
Esta linha define uma variável chamada 'num' do tipo int e a inicializa com o valor 10. Isso também poderia ter sido escrito como:
int num; /* define variável não inicializada 'num' */
/* e depois de todas as definições de variáveis: */
num = 10; /* atribui valor 10 à variável 'num' */
Variáveis podem ser definidas no início de um bloco (entre as chaves {e}), geralmente no início do corpo de uma função, mas também pode ser no início de outro tipo de bloco.
Variáveis que são definidas no início de um bloco assumem o status 'auto' por padrão. Isso significa que elas só existem durante a execução do bloco. Quando a execução da função começa, as variáveis serão criadas, mas seus conteúdos serão indefinidos. Quando a função retorna, as variáveis serão destruídas. A definição também poderia ter sido escrita como:
automático int num = 10;
Como a definição com ou sem a palavra-chave auto é completamente equivalente, a palavra-chave auto é obviamente bastante redundante.
No entanto, às vezes isso não é o que você quer. Suponha que você queira que uma função mantenha a contagem de quantas vezes ela é chamada. Se a variável fosse destruída toda vez que a função retornasse, isso não seria possível.
Portanto, é possível dar à variável o que é chamado de duração estática, o que significa que ela permanecerá intacta durante toda a execução do programa. Por exemplo:
estático int num = 10;
Isso inicializa a variável num para 10 no começo da execução do programa. Daí em diante o valor permanecerá intocado; a variável não será reinicializada se a função for chamada múltiplas vezes.
Às vezes, não é suficiente que a variável seja acessível apenas por uma função ou pode não ser conveniente passar o valor por meio de um parâmetro para todas as outras funções que precisam dele.
Mas se você precisar acessar a variável de todas as funções no arquivo de origem inteiro, isso também pode ser feito com a palavra-chave static, mas colocando a definição fora de todas as funções. Por exemplo:
#incluir <stdio.h>
static int num = 10; /* será acessível de todo o arquivo de origem */
int principal(vazio)
{
printf("O número é: %d\n", num);
retornar 0;
}
E também há casos em que uma variável precisa ser acessível de todo o programa, que pode consistir em vários arquivos de origem. Isso é chamado de variável global e deve ser evitado quando não for necessário.
Isso também é feito colocando a definição fora de todas as funções, mas sem usar a palavra-chave static:
#incluir <stdio.h>
int num = 10; /* será acessível de todo o programa! */
int principal(vazio)
{
printf("O número é: %d\n", num);
retornar 0;
}
Há também a palavra-chave extern, que é usada para acessar variáveis globais em outros módulos. Há também alguns qualificadores que você pode adicionar às definições de variáveis. O mais importante deles é const. Uma variável que é definida como const não pode ser modificada.
Há mais dois modificadores que são menos comumente usados. O modificador volátil e de registro. O modificador volátil requer que o compilador realmente acesse a variável toda vez que ela for lida. Ele pode não otimizar a variável colocando-a em um registro ou algo assim. Isso é usado principalmente para fins de processamento de multithreading e interrupção, etc.
O modificador register solicita ao compilador que otimize a variável em um register. Isso só é possível com variáveis auto e, em muitos casos, o compilador pode selecionar melhor as variáveis para otimizar em registers, então essa palavra-chave é obsoleta. A única consequência direta de fazer um register variável é que seu endereço não pode ser obtido.
A tabela de variáveis, fornecida na próxima página, descreve a classe de armazenamento de cinco tipos de classes de armazenamento.
Na tabela vemos que a palavra-chave extern é colocada em duas linhas. A palavra-chave extern é usada em funções para declarar uma variável externa estática que é definida em outro lugar.
Tipos de variáveis numéricas
C fornece vários tipos diferentes de variáveis numéricas porque diferentes valores numéricos têm requisitos de armazenamento de memória variados. Esses tipos numéricos diferem na facilidade com que certas operações matemáticas podem ser executadas neles.
Pequenos inteiros requerem menos memória para armazenar, e seu computador pode executar operações matemáticas com tais números muito rapidamente. Grandes inteiros e valores de ponto flutuante requerem mais espaço de armazenamento e mais tempo para operações matemáticas. Ao usar os tipos de variáveis apropriados, você garante que seu programa seja executado da forma mais eficiente possível.
As variáveis numéricas de C se enquadram nas duas categorias principais a seguir:
- Variáveis inteiras
- Variáveis de ponto flutuante
Dentro de cada uma dessas categorias, há dois ou mais tipos específicos de variáveis. A tabela a seguir mostra a quantidade de memória, em bytes, necessária para manter uma única variável de cada tipo.
O tipo char pode ser equivalente a signed char ou unsigned char, mas é sempre um tipo separado de qualquer um deles.
Em C não há diferença entre armazenar caracteres ou seus valores numéricos correspondentes em uma variável, então também não há necessidade de uma função para converter entre um caractere e seu valor numérico ou vice-versa. Para os outros tipos inteiros, se você omitir signed ou unsigned o padrão será signed, então, por exemplo, int e signed int são equivalentes.
O tipo int deve ser maior ou igual ao tipo short, e menor ou igual ao tipo long. Se você simplesmente precisa armazenar alguns valores que não são enormemente grandes, geralmente é uma boa ideia usar o tipo int; geralmente é o tamanho com o qual o processador consegue lidar mais facilmente, e portanto mais rápido.
Com vários compiladores, double e long double são equivalentes. Isso, combinado com o fato de que a maioria das funções matemáticas padrão funcionam com o tipo double, é um bom motivo para sempre usar o tipo double se você tiver que trabalhar com números fracionários.
A tabela a seguir descreve melhor os tipos de variáveis:
Tipos de uso especial comumente usados:
Tipo de variável |
Descrição |
tamanho_t |
tipo não assinado usado para armazenar os tamanhos de objetos em bytes |
tempo_t |
usado para armazenar resultados da função time() |
relógio_t |
usado para armazenar resultados da função clock() |
ARQUIVO |
usado para acessar um fluxo (geralmente um arquivo ou dispositivo) |
ptrdiff_t |
tipo assinado da diferença entre 2 ponteiros |
div_t |
usado para armazenar resultados da função div() |
ldiv_t |
usado para armazenar resultados da função ldiv() |
fpos_t |
usado para armazenar informações de posição de arquivo |
lista_de_vontade |
usado no tratamento de argumentos variáveis |
wchar_t |
tipo de caractere amplo (usado para conjuntos de caracteres estendidos) |
sig_atomic_t |
usado em manipuladores de sinais |
Jmp_buf |
usado para saltos não locais |
Para entender melhor essas variáveis, vejamos um exemplo:
/* Programa para informar o intervalo e o tamanho em bytes da variável C */
#incluir <stdio.h>
int principal()
{
int a; /* tipo inteiro simples */
long int b; /* tipo inteiro longo */
short int c; /* tipo inteiro curto */
unsigned int d; /* tipo inteiro sem sinal */
char e; /* tipo de caractere */
float f; /* tipo de ponto flutuante */
double g; /* ponto flutuante de precisão dupla */
a = 1023;
b = 2222;
c = 123;
d = 1234;
e = 'X';
f = 3,14159;
g = 3,1415926535898;
printf( "\nUm caractere tem %d bytes", sizeof( char ));
printf( "\nUm int tem %d bytes", sizeof( int ));
printf( "\nUm short é %d bytes", sizeof( short ));
printf( "\nUm comprimento é %d bytes", sizeof( long ));
printf( "\nUm caractere sem sinal tem %d bytes",
sizeof(caractere sem sinal));
printf( "\nUm unsigned int tem %d bytes",
sizeof(inteiro sem sinal));
printf( "\nUm short sem sinal tem %d bytes",
sizeof(sem sinal curto));
printf( "\nUm unsigned long tem %d bytes",
sizeof( longo sem sinal ));
printf( "\nUm float tem %d bytes", sizeof( float ));
printf( "\nUm duplo tem %d bytes\n", sizeof( double ));
printf("a = %d\n", a); /* saída decimal */
printf("a = %o\n", a); /* saída octal */
printf("a = %x\n", a); /* saída hexadecimal */
printf("b = %ld\n", b); /* saída decimal longa */
printf("c = %d\n", c); /* saída curta decimal */
printf("d = %u\n", d); /* saída sem sinal */
printf("e = %c\n", e); /* saída de caracteres */
printf("f = %f\n", f); /* saída flutuante */
printf("g = %f\n", g); /* saída de ponto flutuante duplo */
printf("\n");
printf("a = %d\n", a); /* saída int simples */
printf("a = %7d\n", a); /* use uma largura de campo de 7 */
printf("a = %-7d\n", a); /* justificar à esquerda em
campo de 7 */
c = 5;
d = 8;
printf("a = %*d\n", c, a); /* use uma largura de campo de 5*/
printf("a = %*d\n", d, a); /* use uma largura de campo de 8 */
printf("\n");
printf("f = %f\n", f); /* saída float simples */
printf("f = %12f\n", f); /* usar largura de campo de 12 */
printf("f = %12.3f\n", f); /* use 3 casas decimais */
printf("f = %12.5f\n", f); /* use 5 casas decimais */
printf("f = %-12.5f\n", f); /* justificar à esquerda no campo */
retornar 0;
}
O resultado do programa após a execução será exibido como:
Um char tem 1 byte
Um int tem 2 bytes
Um curto tem 2 bytes
Um longo tem 4 bytes
Um char sem sinal tem 1 byte
Um unsigned int tem 2 bytes
Um short sem sinal tem 2 bytes
Um unsigned long tem 4 bytes
Um float tem 4 bytes
Um duplo tem 8 bytes
um = 1023
a = 1777
a = 3ff
b = 2222
c = 123
d = 1234
e = X
f = 3,141590
g = 3,141593
um = 1023
um = 1023
um = 1023
um = 1023
um = 1023
f = 3,141590
f = 3,141590
f = 3,142
f = 3,14159
f = 3,14159 |
Antes de seu uso, uma variável em um programa C, ela deve ser declarada. Uma declaração de variável informa ao compilador o nome e o tipo de uma variável e, opcionalmente, inicializa a variável para um valor específico.
Se seu programa tentar usar uma variável que não foi declarada, o compilador gera uma mensagem de erro. Uma declaração de variável tem o seguinte formato:
nome do tipo nome da variável;
typename especifica o tipo de variável e deve ser uma das palavras-chave. varname é o nome da variável. Você pode declarar múltiplas variáveis do mesmo tipo em uma linha separando os nomes das variáveis com vírgulas:
int count, number, start; /* três variáveis inteiras */
float percent, total; /* duas variáveis float */
A palavra-chave typedef
A palavra-chave typedef é usada para criar um novo nome para um tipo de dado existente. Na verdade, typedef cria um sinônimo. Por exemplo, a declaração
typedef int inteiro;
aqui vemos que typedef cria integer como um sinônimo para int. Você pode então usar integer para definir variáveis do tipo int, como neste exemplo:
contagem inteira;
Portanto, typedef não cria um novo tipo de dado, ele apenas permite que você use um nome diferente para um tipo de dado predefinido.
Inicializando Variáveis Numéricas
Quando qualquer variável é declarada, o compilador é instruído a reservar espaço de armazenamento para a variável. No entanto, o valor armazenado naquele espaço, o valor da variável, não é definido. Pode ser zero, ou pode ser algum valor "lixo" aleatório. Antes de usar uma variável, você deve sempre inicializá-la para um valor conhecido. Vamos pegar este exemplo:
int count; /* Reservar espaço de armazenamento para count */
count = 0; /* Armazena 0 em count */
Esta declaração usa o sinal de igual (=), que é o operador de atribuição de C. Você também pode inicializar uma variável quando ela é declarada. Para fazer isso, siga o nome da variável na declaração com um sinal de igual e o valor inicial desejado:
contagem int = 0;
taxa dupla = 0,01, complexidade = 28,5;
Tenha cuidado para não inicializar uma variável com um valor fora do intervalo permitido. Aqui estão dois exemplos de inicializações fora do intervalo:
int quantidade = 100000;
comprimento int sem sinal = -2500;
O compilador C não pega tais erros. Seu programa pode compilar e vincular, mas você pode obter resultados inesperados quando o programa for executado.
Vamos pegar o seguinte exemplo para calcular o número total de setores em um disco:
// Programa modelo para calcular setores em um disco //
#incluir<stdio.h>
#define SETOR_POR_LADO 63
#define LADO_POR_CILINDRO 254
vazio principal()
{
int cilindro=0;
clrscr();
printf("Digite o número de cilindros no disco \n\n\t");
scanf("%d",&cylinder); // Obter o valor do usuário //
printf("\n\n\t Número total de setores no disco = %ld", (long)SETOR_POR_LADO*LADO_POR_CILINDRO* cilindro);
obter();
}
A saída do programa é a seguinte:
Insira o número de cilindros no disco
1024
Número total de setores no disco = 16386048
Neste exemplo, vemos três coisas novas para aprender. #define é usado para usar constantes simbólicas no programa ou, em alguns casos, para economizar tempo definindo palavras longas em símbolos pequenos.
Aqui definimos o número de setores por lado que é 63 como SECTOR_PER_SIDE para tornar o programa fácil de entender. O mesmo caso é verdadeiro para #define SIDE_PER_CYLINDER 254. scanf() é usado para obter a entrada do usuário.
Aqui estamos tomando o número de cilindros como entrada do usuário. * é usado para multiplicar dois ou mais valores, conforme mostrado no exemplo.
A função getch() basicamente obtém uma entrada de caractere único do teclado. Ao digitar getch(); aqui, paramos a tela até que qualquer tecla seja pressionada no teclado.
Operadores
Um operador é um símbolo que instrui C a executar alguma operação, ou ação, em um ou mais operandos. Um operando é algo em que um operador atua. Em C, todos os operandos são expressões. Os operadores C são das quatro categorias a seguir:
- O operador de atribuição
- Operadores matemáticos
- Operadores relacionais
- Operadores lógicos
Operador de Atribuição
O operador de atribuição é o sinal de igual (=). O uso do sinal de igual na programação é diferente do seu uso em relações algébricas matemáticas regulares. Se você escrever
x = y;
Em um programa C, não significa "x é igual a y". Em vez disso, significa "atribuir o valor de y a x". Em uma declaração de atribuição C, o lado direito pode ser qualquer expressão, e o lado esquerdo deve ser um nome de variável. Assim, o formato é o seguinte:
variável = expressão;
Durante a execução, a expressão é avaliada e o valor resultante é atribuído à variável.
Operadores Matemáticos
Os operadores matemáticos de C realizam operações matemáticas como adição e subtração. C tem dois operadores matemáticos unários e cinco operadores matemáticos binários. Os operadores matemáticos unários são assim chamados porque eles pegam um único operando. C tem dois operadores matemáticos unários.
Os operadores de incremento e decremento podem ser usados somente com variáveis, não com constantes. A operação realizada é adicionar um ou subtrair um do operando. Em outras palavras, as instruções ++x; e --y; são equivalentes a estas instruções:
x = x + 1;
y = y - 1;
operadores matemáticos binários pegam dois operandos. Os quatro primeiros operadores binários, que incluem as operações matemáticas comuns encontradas em uma calculadora (+, -, *, /), são familiares para você. O quinto operador Módulo retorna o resto quando o primeiro operando é dividido pelo segundo operando. Por exemplo, 11 módulo 4 é igual a 3 (11 é dividido por 4, duas vezes e sobra 3).
Operadores Relacionais
Os operadores relacionais de C são usados para comparar expressões. Uma expressão que contém um operador relacional é avaliada como true (1) ou false (0). C tem seis operadores relacionais.
Operadores Lógicos
Operadores lógicos de C permitem que você combine duas ou mais expressões relacionais em uma única expressão que avalia como true ou false. Operadores lógicos avaliam como true ou false, dependendo do valor true ou false de seus operandos.
Se x for uma variável inteira, expressões usando operadores lógicos podem ser escritas das seguintes maneiras:
(x > 1) e (x < 5)
(x >= 2) && (x <= 4)
Operador |
Símbolo |
Descrição |
Exemplo |
Operadores de atribuição |
igual |
= |
atribuir o valor de y a x |
x = y |
Operadores matemáticos |
Incremento |
++ |
Incrementa o operando em um |
++x, x++ |
Decremento |
-- |
Decrementa o operando em um |
--x, x-- |
Adição |
+ |
Adiciona dois operandos |
x + y |
Subtração |
- |
Subtrai o segundo operando do primeiro |
x - e |
Multiplicação |
* |
Multiplica dois operandos |
x * e |
Divisão |
/ |
Divide o primeiro operando pelo segundo operando |
x / e |
Módulo |
% |
Fornece o resto quando o primeiro operando é dividido pelo segundo operando |
x % e |
Operadores relacionais |
Igual |
= = |
Igualdade |
x = = y |
Maior que |
> |
Maior que |
x > y |
Menor que |
< |
Menor que |
x < y |
Maior ou igual a |
>= |
Maior ou igual a |
x >= y |
Menor ou igual a |
<= |
Menor ou igual a |
x <= y |
Não é igual |
!= |
Não é igual a |
x != y |
Operadores lógicos |
E |
&& |
Verdadeiro (1) somente se exp1 e exp2 forem verdadeiros; falso (0) caso contrário |
exp1 e& exp2 |
OU |
|| |
Verdadeiro (1) se exp1 ou exp2 for verdadeiro; falso (0) somente se ambos forem falsos |
exp1 || exp2 |
NÃO |
! |
Falso (0) se exp1 for verdadeiro; verdadeiro (1) se exp1 for falso |
!exp1 |
Coisas para lembrar sobre expressões lógicas
x * = y |
é o mesmo que |
x = x * y |
y - = z + 1 |
é o mesmo que |
y = y - z + 1 |
um / = b |
é o mesmo que |
um = um / b |
x + = y / 8 |
é o mesmo que |
x = x + y / 8 |
e % = 3 |
é o mesmo que |
y = y % 3 |
O operador vírgula
A vírgula é frequentemente usada em C como um simples sinal de pontuação, para separar declarações de variáveis, argumentos de funções, etc. Em certas situações, a vírgula atua como um operador.
Você pode formar uma expressão separando duas subexpressões com uma vírgula. O resultado é o seguinte:
- Ambas as expressões são avaliadas, com a expressão da esquerda sendo avaliada primeiro.
- A expressão inteira é avaliada como o valor da expressão correta.
Por exemplo, a instrução a seguir atribui o valor de b a x, depois incrementa a e, em seguida, incrementa b: x = (a++, b++);
Precedência do operador C (Resumo dos operadores C)
Classificação e associatividade |
Operadores |
1 (da esquerda para a direita) |
() [] -> . |
2 (da direita para a esquerda) |
! ~ ++ -- * (indireção) & (endereço-de) (tipo) sizeof + (unário) - (unário) |
3 (da esquerda para a direita) |
* (multiplicação) / % |
4 (da esquerda para a direita) |
+ - |
5 (da esquerda para a direita) |
<< >> |
6 (da esquerda para a direita) |
< <= > >= |
7 (da esquerda para a direita) |
= = != |
8 (da esquerda para a direita) |
& (bit a bit AND ) |
9 (da esquerda para a direita) |
^ |
10 (da esquerda para a direita) |
| |
11 (da esquerda para a direita) |
&& |
12 (da esquerda para a direita) |
|| |
13 (da direita para a esquerda) |
?: |
14 (da direita para a esquerda) |
= += -= *= /= %= &= ^= |= <<= >>= |
15 (da esquerda para a direita) |
, |
() é o operador de função; [] é o operador de matriz. |
|
Vejamos um exemplo de uso de operadores:
/* Uso de operadores */
int principal()
{
int x = 0, y = 2, z = 1025;
flutuar a = 0,0, b = 3,14159, c = -37,234;
/* incrementando */
x = x + 1; /* Isso incrementa x */
x++; /* Isso incrementa x */
++x; /* Isso incrementa x */
z = y++; /* z = 2, y = 3 */
z = ++y; /* z = 4, y = 4 */
/* decrementando */
y = y - 1; /* Isso diminui y */
y--; /* Isso diminui y */
--y; /* Isso diminui y */
e = 3;
z = y--; /* z = 3, y = 2 */
z = --y; /* z = 1, y = 1 */
/* operação aritmética */
a = a + 12; /* Isso adiciona 12 a a */
a += 12; /* Isso adiciona mais 12 a a */
a *= 3.2; /* Isso multiplica a por 3.2 */
a -= b; /* Isso subtrai b de a */
a /= 10.0; /* Isso divide a por 10.0 */
/* expressão condicional */
a = (b >= 3,0 ? 2,0 : 10,5 ); /* Esta expressão */
se (b >= 3.0) /* E esta expressão */
a = 2.0; /* são idênticos, ambos */
senão /* causará o mesmo */
a = 10,5; /* resultado. */
c = (a > b ? a : b); /* c terá o máximo de a ou b */
c = (a > b ? b : a); /* c terá o mínimo de a ou b */
printf("x=%d, y=%d, z= %d\n", x, y, z);
printf("a=%f, b=%f, c= %f", a, b, c);
retornar 0;
}
e o resultado deste programa será exibido na tela como:
x=3, y=1, z=1
a=2,000000, b=3,141590, c=2,000000
Mais alguma coisa sobre printf() e Scanf()
Considere as duas instruções printf a seguir
printf(“\t %d\n”, num);
printf(“%5.2f”, fração);
na primeira instrução printf \t solicita o deslocamento da tabulação na tela, o argumento %d informa ao compilador que o valor de num deve ser impresso como um inteiro decimal. \n faz com que a nova saída comece em uma nova linha.
Na segunda declaração printf %5.2f diz ao compilador que a saída deve estar em ponto flutuante, com cinco casas no total e duas casas à direita do ponto decimal. Mais sobre o caractere de barra invertida foi mostrado na tabela a seguir:
Constante |
Significado |
'\um' |
Alerta sonoro (campainha) |
'\b' |
Retrocesso |
'\f' |
Alimentação de formulário |
'\n' |
Nova linha |
'\r' |
Retorno de carro |
'\t' |
Aba horizontal |
'\v' |
Aba vertical |
'\'' |
Aspas simples |
'\”' |
Aspas duplas |
'\?' |
Ponto de interrogação |
'\\' |
Barra invertida |
'\0' |
Nulo |
Vamos considerar a seguinte declaração scanf
scanf(“%d”, &num);
Os dados do teclado são recebidos pela função scanf. No formato acima, o símbolo & (e comercial) antes de cada nome de variável é um operador que especifica o endereço do nome da variável.
Ao fazer isso, a execução para e espera que o valor da variável num seja digitado. Quando o valor inteiro é inserido e a tecla return é pressionada, o computador prossegue para a próxima instrução. Os códigos de formato scanf e printf estão listados na tabela a seguir:
Código |
Lê... |
%c |
Caractere único |
%d |
Número inteiro decimal |
%e |
Valor de ponto flutuante |
%f |
Valor de ponto flutuante |
%g |
Valor de ponto flutuante |
%h |
Inteiro curto |
%eu |
Número inteiro decimal, hexadecimal ou octal |
%o |
Número inteiro octal |
%s |
Corda |
%em |
Inteiro decimal sem sinal |
%x |
Número inteiro hexadecimal |
Declarações de controle
Um programa consiste em uma série de instruções que são geralmente executadas em sequência. Os programas podem ser muito mais poderosos se pudermos controlar a ordem em que as instruções são executadas.
As declarações se dividem em três tipos gerais:
- Atribuição, onde valores, geralmente resultados de cálculos, são armazenados em variáveis.
- Entrada/Saída, os dados são lidos ou impressos.
- Controle, o programa toma uma decisão sobre o que fazer em seguida.
Esta seção discutirá o uso de instruções de controle em C. Mostraremos como elas podem ser usadas para escrever programas poderosos por:
- Repetindo seções importantes do programa.
- Selecionando entre seções opcionais de um programa.
A declaração if else
Isso é usado para decidir se deve fazer algo em um ponto específico ou para decidir entre dois cursos de ação.
O teste a seguir decide se um aluno foi aprovado em um exame com uma nota de aprovação de 45
se (resultado >= 45)
printf("Passe\n");
outro
printf("Falha\n");
É possível usar a parte if sem o else.
se (temperatura < 0)
imprimir("Congelado\n");
Cada versão consiste em um teste, na declaração entre colchetes após o if. Se o teste for verdadeiro, a próxima declaração será obedecida. Se for falso, a declaração após o else será obedecida, se presente. Depois disso, o restante do programa continua normalmente.
Se desejarmos ter mais de uma declaração seguindo o if ou o else, elas devem ser agrupadas entre chaves. Tal agrupamento é chamado de declaração composta ou bloco.
se (resultado >= 45)
{ printf("Aprovado\n");
printf("Parabéns\n");
}
outro
{ printf("Falha\n");
printf("Boa sorte na próxima vez\n");
}
Às vezes, desejamos tomar uma decisão multidirecional com base em várias condições. A maneira mais geral de fazer isso é usando a variante else if na instrução if.
Isso funciona por meio de cascata de várias comparações. Assim que uma delas der um resultado verdadeiro, a seguinte declaração ou bloco é executado, e nenhuma outra comparação é realizada. No exemplo a seguir, estamos concedendo notas dependendo do resultado do exame.
se (resultado <=100 && resultado >= 75)
printf("Aprovado: Nota A\n");
senão se (resultado >= 60)
printf("Aprovado: Nota B\n");
senão se (resultado >= 45)
printf("Aprovado: Nota C\n");
outro
printf("Falha\n");
Neste exemplo, todas as comparações testam uma única variável chamada result. Em outros casos, cada teste pode envolver uma variável diferente ou alguma combinação de testes. O mesmo padrão pode ser usado com mais ou menos else if's, e o else final sozinho pode ser deixado de fora.
Cabe ao programador elaborar a estrutura correta para cada problema de programação. Para entender melhor o uso de if else, vejamos o exemplo
#incluir <stdio.h>
int principal()
{
número inteiro;
para(num = 0 ; num < 10 ; num = num + 1)
{
se (num == 2)
printf("num agora é igual a %d\n", num);
se (num < 5)
printf("num agora é %d, que é menor que 5\n", num);
outro
printf("num agora é %d, que é maior que 4\n", num);
} /* fim do loop for */
retornar 0;
}
Resultado do programa
num agora é 0, que é menor que 5
num agora é 1, que é menor que 5
num agora é igual a 2
num agora é 2, que é menor que 5
num agora é 3, que é menor que 5
num agora é 4, que é menor que 5
num agora é 5, que é maior que 4
num agora é 6, que é maior que 4
num agora é 7, que é maior que 4
num agora é 8, que é maior que 4
num agora é 9, que é maior que 4
A declaração switch
Esta é outra forma de decisão multidirecional. É bem estruturada, mas só pode ser usada em certos casos em que;
- Apenas uma variável é testada, todos os ramos devem depender do valor dessa variável. A variável deve ser um tipo integral. (int, long, short ou char).
- Cada valor possível da variável pode controlar um único branch. Um branch final, catch all, default pode ser usado opcionalmente para capturar todos os casos não especificados.
O exemplo dado abaixo esclarecerá as coisas. Esta é uma função que converte um inteiro em uma descrição vaga. É útil quando estamos preocupados apenas em medir uma quantidade quando ela é bem pequena.
estimativa(número)
número inteiro;
/* Estime um número como nenhum, um, dois, vários, muitos */
{ switch(número) {
caso 0:
printf("Nenhum\n");
quebrar;
caso 1:
printf("Um\n");
quebrar;
caso 2:
printf("Dois\n");
quebrar;
caso 3:
caso 4:
caso 5:
printf("Vários\n");
quebrar;
padrão :
printf("Muitos\n");
quebrar;
}
}
Cada caso interessante é listado com uma ação correspondente. A instrução break impede que quaisquer outras instruções sejam executadas ao deixar o switch. Como o caso 3 e o caso 4 não têm break subsequente, eles continuam permitindo a mesma ação para vários valores de number.
Ambas as construções if e switch permitem que o programador faça uma seleção a partir de uma série de ações possíveis. Vejamos um exemplo:
#incluir <stdio.h>
int principal()
{
número inteiro;
para (num = 3 ; num < 13 ; num = num + 1)
{
interruptor (num)
{
caso 3:
printf("O valor é três\n");
quebrar;
caso 4:
printf("O valor é quatro\n");
quebrar;
caso 5:
caso 6:
caso 7:
caso 8:
printf("O valor está entre 5 e 8\n");
quebrar;
caso 11:
printf("O valor é onze\n");
quebrar;
padrão :
printf("É um dos valores indefinidos\n");
quebrar;
} /* fim da troca */
} /* fim do loop for */
retornar 0;
}
A saída do programa será
O valor é três
O valor é quatro
O valor está entre 5 e 8
O valor está entre 5 e 8
O valor está entre 5 e 8
O valor está entre 5 e 8
É um dos valores indefinidos
É um dos valores indefinidos
O valor é onze
É um dos valores indefinidos
A declaração break
Já conhecemos break na discussão da instrução switch. Ela é usada para sair de um loop ou switch, controlando a passagem para a primeira instrução além do loop ou switch.
Com loops, break pode ser usado para forçar uma saída antecipada do loop, ou para implementar um loop com um teste para sair no meio do corpo do loop. Um break dentro de um loop deve sempre ser protegido dentro de uma instrução if que fornece o teste para controlar a condição de saída.
A declaração continue
Isso é similar a break, mas é encontrado com menos frequência. Ele só funciona dentro de loops onde seu efeito é forçar um salto imediato para a declaração de controle do loop.
- Em um loop while, pule para a instrução de teste.
- Em um loop do while, pule para a instrução de teste.
- Em um loop for, pule para o teste e execute a iteração.
Assim como um break, continue deve ser protegido por uma instrução if. É improvável que você a use com muita frequência. Para entender melhor o uso de break e continue, vamos examinar o seguinte programa:
#incluir <stdio.h>
int principal()
{
valor int;
para(valor = 5; valor < 15; valor = valor + 1)
{
se (valor == 8)
quebrar;
printf("No loop de interrupção, o valor agora é %d\n", valor);
}
para(valor = 5; valor < 15; valor = valor + 1)
{
se (valor == 8)
continuar;
printf("No loop continue, o valor agora é %d\n", valor);
}
retornar 0;
}
A saída do programa será a seguinte:
No loop de interrupção, o valor agora é 5
No loop de interrupção, o valor agora é 6
No loop de interrupção, o valor agora é 7
No loop continue, o valor agora é 5
No loop continue, o valor agora é 6
No loop continue, o valor agora é 7
No loop continue, o valor agora é 9
No loop continue, o valor agora é 10
No loop continue, o valor agora é 11
No loop continue, o valor agora é 12
No loop continue, o valor agora é 13
No loop continue, o valor agora é 14
Laços
O outro tipo principal de declaração de controle é o loop. Loops permitem que uma declaração, ou bloco de declarações, seja repetido. Computadores são muito bons em repetir tarefas simples muitas vezes. O loop é a maneira de C conseguir isso.
C oferece a você a escolha de três tipos de loop: while, do-while e for.
- O loop while continua repetindo uma ação até que um teste associado retorne false. Isso é útil quando o programador não sabe com antecedência quantas vezes o loop será percorrido.
- Os loops do while são semelhantes, mas o teste ocorre após o corpo do loop ser executado. Isso garante que o corpo do loop seja executado pelo menos uma vez.
- O loop for é frequentemente usado, geralmente onde o loop será percorrido um número fixo de vezes. Ele é muito flexível, e programadores novatos devem tomar cuidado para não abusar do poder que ele oferece.
O loop while
O loop while repete uma declaração até que o teste no topo prove ser falso. Como exemplo, aqui está uma função para retornar o comprimento de uma string. Lembre-se de que a string é representada como um array de caracteres terminado por um caractere nulo '\0'.
int string_length(caractere string[])
{ int i = 0;
enquanto (string[i] != '\0')
eu++;
retornar(i);
}
A string é passada para a função como um argumento. O tamanho do array não é especificado, a função funcionará para uma string de qualquer tamanho.
O loop while é usado para olhar os caracteres na string um de cada vez até que o caractere nulo seja encontrado. Então o loop é encerrado e o índice do nulo é retornado.
Enquanto o caractere não for nulo, o índice é incrementado e o teste é repetido. Iremos nos aprofundar em arrays mais tarde. Vamos ver um exemplo para loop while:
#incluir <stdio.h>
int principal()
{
contagem interna;
contagem = 0;
enquanto (contagem < 6)
{
printf("O valor de count é %d\n", count);
contagem = contagem + 1;
}
retornar 0;
}
e o resultado é exibido da seguinte forma:
O valor de count é 0
O valor de count é 1
O valor de count é 2
O valor de count é 3
O valor da contagem é 4
O valor de count é 5
O loop do while
Isso é muito parecido com o loop while, exceto que o teste ocorre no final do corpo do loop. Isso garante que o loop seja executado pelo menos uma vez antes de continuar.
Tal configuração é frequentemente usada onde dados devem ser lidos. O teste então verifica os dados e faz um loop para ler novamente se eles forem inaceitáveis.
fazer
{
printf("Digite 1 para sim, 0 para não :");
scanf("%d", &valor_de_entrada);
} enquanto (valor_de_entrada != 1 && valor_de_entrada != 0)
Para entender melhor o loop do while, vejamos o seguinte exemplo:
#incluir <stdio.h>
int principal()
{
int eu;
eu = 0;
fazer
{
printf("O valor de i agora é %d\n", i);
eu = i + 1;
} enquanto (i < 5);
retornar 0;
}
O resultado do programa é exibido da seguinte forma:
O valor de i agora é 0
O valor de i agora é 1
O valor de i agora é 2
O valor de i agora é 3
O valor de i agora é 4
O loop for
O loop for funciona bem onde o número de iterações do loop é conhecido antes que o loop seja inserido. O cabeçalho do loop consiste em três partes separadas por ponto e vírgula.
- O primeiro é executado antes do loop ser inserido. Geralmente é a inicialização da variável do loop.
- O segundo é um teste, o loop é encerrado quando isso retorna falso.
- A terceira é uma declaração a ser executada toda vez que o corpo do loop for concluído. Isso geralmente é um incremento do contador do loop.
O exemplo é uma função que calcula a média dos números armazenados em um array. A função pega o array e o número de elementos como argumentos.
média flutuante (array flutuante [], contagem int)
{
total flutuante = 0,0;
int eu;
para(i = 0; i < contagem; i++)
total += matriz[i];
retornar(total / contagem);
}
O loop for garante que o número correto de elementos da matriz seja adicionado antes de calcular a média.
As três instruções no início de um loop for geralmente fazem apenas uma coisa cada, no entanto, qualquer uma delas pode ser deixada em branco. Uma primeira ou última instrução em branco significará nenhuma inicialização ou incremento em execução. Uma instrução de comparação em branco sempre será tratada como verdadeira. Isso fará com que o loop seja executado indefinidamente, a menos que seja interrompido por algum outro meio. Pode ser uma instrução return ou break.
Também é possível espremer várias instruções na primeira ou terceira posição, separando-as com vírgulas. Isso permite um loop com mais de uma variável de controle. O exemplo abaixo ilustra a definição de tal loop, com as variáveis hi e lo começando em 100 e 0, respectivamente, e convergindo.
O loop for fornece uma variedade de abreviações para serem usadas nele. Observe a seguinte expressão, nesta expressão o loop único contém dois loops for nele. Aqui hi-- é o mesmo que hi = hi - 1 e lo++ é o mesmo que lo = lo + 1,
para(oi = 100, lo = 0; oi >= lo; oi--, lo++)
O loop for é extremamente flexível e permite que muitos tipos de comportamento de programa sejam especificados de forma simples e rápida. Vamos ver um exemplo de loop for
#incluir <stdio.h>
int principal()
{
índice int;
para(índice = 0 ; índice < 6 ; índice = índice + 1)
printf("O valor do índice é %d\n", index);
retornar 0;
}
O resultado do programa é exibido da seguinte forma:
O valor do índice é 0
O valor do índice é 1
O valor do índice é 2
O valor do índice é 3
O valor do índice é 4
O valor do índice é 5
A declaração goto
C tem uma declaração goto que permite que saltos não estruturados sejam feitos. Para usar uma declaração goto, você simplesmente usa a palavra reservada goto seguida pelo nome simbólico para o qual você deseja pular. O nome é então colocado em qualquer lugar do programa seguido por dois pontos. Você pode pular quase para qualquer lugar dentro de uma função, mas não tem permissão para pular para dentro de um loop, embora tenha permissão para pular para fora de um loop.
Este programa em particular é realmente uma bagunça, mas é um bom exemplo do porquê os desenvolvedores de software estão tentando eliminar o uso da declaração goto o máximo possível. O único lugar neste programa onde é razoável usar o goto é onde o programa salta dos três loops aninhados em um salto. Neste caso, seria bastante bagunçado configurar uma variável e saltar sucessivamente para fora de cada um dos três loops aninhados, mas uma declaração goto tira você de todos os três de uma maneira muito concisa.
Algumas pessoas dizem que a declaração goto nunca deve ser usada sob nenhuma circunstância, mas isso é pensamento tacanho. Se houver um lugar onde um goto claramente fará um fluxo de controle mais limpo do que alguma outra construção, sinta-se livre para usá-lo, no entanto, como está no resto do programa em seu monitor. Vejamos o exemplo:
#incluir <stdio.h>
int principal()
{
int cachorro, gato, porco;
ir para real_start;
em algum lugar:
printf("Esta é outra linha da bagunça.\n");
vá para stop_it;
/* a seção a seguir é a única com um goto utilizável */
início_real:
para(cachorro = 1 ; cachorro < 6 ; cachorro = cachorro + 1)
{
para(gato = 1 ; gato < 6 ; gato = gato + 1)
{
para(porco = 1 ; porco < 4 ; porco = porco + 1)
{
printf("Cachorro = %d Gato = %d Porco = %d\n", cachorro, gato, porco);
se ((cachorro + gato + porco) > 8 ) goto suficiente;
}
}
}
suficiente: printf("Já chega de animais por enquanto.\n");
/* este é o fim da seção com uma instrução goto utilizável */
printf("\nEsta é a primeira linha do código.\n");
vá lá;
onde:
printf("Esta é a terceira linha do código.\n");
ir para algum_lugar;
lá:
printf("Esta é a segunda linha do código.\n");
ir para onde;
pare com isso:
printf("Esta é a última linha desta bagunça.\n");
retornar 0;
}
Vamos ver os resultados exibidos
Cão = 1 Gato = 1 Porco = 1
Cão = 1 Gato = 1 Porco = 2
Cão = 1 Gato = 1 Porco = 3
Cachorro = 1 Gato = 2 Porco = 1
Cachorro = 1 Gato = 2 Porco = 2
Cão = 1 Gato = 2 Porco = 3
Cachorro = 1 Gato = 3 Porco = 1
Cão = 1 Gato = 3 Porco = 2
Cão = 1 Gato = 3 Porco = 3
Cachorro = 1 Gato = 4 Porco = 1
Cachorro = 1 Gato = 4 Porco = 2
Cão = 1 Gato = 4 Porco = 3
Cão = 1 Gato = 5 Porco = 1
Cão = 1 Gato = 5 Porco = 2
Cão = 1 Gato = 5 Porco = 3
Já chega de animais por enquanto.
Esta é a primeira linha do código.
Esta é a segunda linha do código.
Esta é a terceira linha do código.
Essa é outra linha da bagunça.
Esta é a última linha desta confusão.
Ponteiros
Às vezes queremos saber onde uma variável reside na memória. Um ponteiro contém o endereço de uma variável que tem um valor específico. Ao declarar um ponteiro, um asterisco é colocado imediatamente antes do nome do ponteiro.
O endereço do local de memória onde a variável está armazenada pode ser encontrado colocando um "e" comercial na frente do nome da variável.
int num; /* Variável inteira normal */
int *numPtr; /* Ponteiro para uma variável inteira */
O exemplo a seguir imprime o valor da variável e o endereço na memória dessa variável.
printf("O valor %d está armazenado no endereço %X\n", num, &num);
Para atribuir o endereço da variável num ao ponteiro numPtr, você atribui o endereço da variável, num, como no exemplo dado a seguir:
numPtr = #
Para descobrir o que está armazenado no endereço apontado por numPtr, a variável precisa ser desreferenciada. A desreferenciação é obtida com o asterisco com o qual o ponteiro foi declarado.
printf("O valor %d está armazenado no endereço %X\n", *numPtr, numPtr);
Todas as variáveis em um programa residem na memória. As instruções fornecidas abaixo solicitam que o compilador reserve 4 bytes de memória em um computador de 32 bits para a variável de ponto flutuante x, então coloque o valor 6,5 nela.
flutuar x;
x = 6,5;
Como a localização do endereço na memória de qualquer variável é obtida colocando o operador & antes de seu nome, portanto &x é o endereço de x. C nos permite ir um estágio além e definir uma variável, chamada de ponteiro que contém o endereço de outras variáveis. Em vez disso, podemos dizer que o ponteiro aponta para outra variável. Por exemplo:
flutuar x;
flutuador* px;
x = 6,5;
px = &x;
define px como um ponteiro para objetos do tipo float, e o define como igual ao endereço de x. Assim, *px se refere ao valor de x:

Vamos examinar as seguintes afirmações:
int var_x;
int* ptrX;
onde_x = 6;
ptrX = &var_x;
*ptrX = 12;
printf("valor de x : %d", var_x);
A primeira linha faz com que o compilador reserve um espaço na memória para um inteiro. A segunda linha diz ao compilador para reservar espaço para armazenar um ponteiro.
Um ponteiro é um local de armazenamento para um endereço. A terceira linha deve lembrá-lo das instruções scanf. O operador de endereço "&" diz ao compilador para ir ao local em que armazenou var_x e, em seguida, fornecer o endereço do local de armazenamento para ptrX.
O asterisco * na frente de uma variável diz ao compilador para desreferenciar o ponteiro e ir para a memória. Então você pode fazer atribuições para variáveis armazenadas naquele local. Você pode referenciar uma variável e acessar seus dados por meio de um ponteiro. Vamos ver um exemplo de ponteiros:
/* ilustração do uso do ponteiro */
#incluir <stdio.h>
int principal()
{
int índice, *pt1, *pt2;
index = 39; /* qualquer valor numérico */
pt1 = &index; /* o endereço do índice */
pt2 = pt1;
printf("O valor é %d %d %d\n", index, *pt1, *pt2);
*pt1 = 13; /* isso altera o valor do índice */
printf("O valor é %d %d %d\n", index, *pt1, *pt2);
retornar 0;
}
A saída do programa será exibida da seguinte forma:
O valor é 39 39 39
O valor é 13 13 13
Vejamos outro exemplo para entender melhor o uso de ponteiros:
#incluir <stdio.h>
#incluir <string.h>
int principal()
{
char strg[40], *ali, um, dois;
int *pt, lista[100], índice;
strcpy(strg, "Esta é uma sequência de caracteres.");
/* a função strcpy() é copiar uma string para outra. leremos sobre a função strcpy() na seção String mais tarde */
um = strg[0]; /* um e dois são idênticos */
dois = *ctrl;
printf("A primeira saída é %c %c\n", um, dois);
um = strg[8]; /* um e dois são idênticos */
dois = *(ctrl+8);
printf("A segunda saída é %c %c\n", um, dois);
lá = ctrl+10; /* strg+10 é idêntico a &strg[10] */
printf("A terceira saída é %c\n", strg[10]);
printf("A quarta saída é %c\n", *lá);
para (índice = 0; índice < 100; índice++)
lista[índice] = índice + 100;
pt = lista + 27;
printf("A quinta saída é %d\n", list[27]);
printf("A sexta saída é %d\n", *pt);
retornar 0;
}
A saída do programa será assim:
A primeira saída é TT
A segunda saída é aa
A terceira saída é c
A quarta saída é c
A quinta saída é 127
A sexta saída é 127
Matrizes
Um array é uma coleção de variáveis do mesmo tipo. Elementos individuais do array são identificados por um índice inteiro. Em C, o índice começa em zero e é sempre escrito entre colchetes.
Já conhecemos matrizes unidimensionais que são declaradas assim
int resultados[20];
Os arrays podem ter mais dimensões, caso em que podem ser declarados como
int resultados_2d[20][5];
int resultados_3d[20][5][3];
Cada índice tem seu próprio conjunto de colchetes. Um array é declarado na função principal, geralmente tem detalhes de dimensões incluídos. É possível usar outro tipo chamado ponteiro no lugar de um array. Isso significa que as dimensões não são fixadas imediatamente, mas o espaço pode ser alocado conforme necessário. Essa é uma técnica avançada que só é necessária em certos programas especializados.
Como exemplo, aqui está uma função simples para somar todos os números inteiros em uma matriz unidimensional.
int add_array(int array[], int tamanho)
{
int eu;
int total = 0;
para(i = 0; i < tamanho; i++)
total += matriz[i];
retornar(total);
}
O programa dado a seguir criará uma string, acessará alguns dados nela, imprimirá. Acessará novamente usando ponteiros e então imprimirá a string. Ele deve imprimir “Hi!” e “012345678” em linhas diferentes. Vamos ver a codificação do programa:
#incluir <stdio.h>
#define STR_LENGTH 10
vazio principal()
{
char Str[STR_LENGTH];
char* pStr;
int eu;
Str[0] = 'H';
Str[1] = 'i';
Str[2] = '!';
Str[3] = '\0'; // caractere especial de fim de string NULL
printf("A string em Str é: %s\n", Str);
pStr = &Str[0];
para (i = 0; i < STR_LENGTH; i++)
{
*pStr = '0'+i;
pStr++;
}
Str[STR_LENGTH-1] = '\0';
printf("A string em Str é: %s\n", Str);
}
[] (chaves quadradas) são usadas para declarar o array. A linha do programa char Str[STR_LENGTH]; declara um array de dez caracteres. Esses são dez caracteres individuais, que são todos colocados juntos na memória no mesmo lugar. Eles podem ser acessados por meio do nome da nossa variável Str junto com um [n] onde n é o número do elemento.
Deve-se sempre ter em mente quando se fala de array que quando C declara um array de dez, os elementos que você pode acessar são numerados de 0 a 9. Acessar o primeiro elemento corresponde a acessar o 0º elemento. Então, no caso de Arrays, sempre conte de 0 até o tamanho do array - 1.
Em seguida, observe que colocamos as letras "Hi!" no array, mas depois colocamos um '\0'. Você provavelmente está se perguntando o que é isso. "\0" significa NULL e representa o fim da string. Todas as strings de caracteres precisam terminar com este caractere especial '\0'. Se não terminarem, e alguém chamar printf na string, printf começaria no local de memória da sua string e continuaria imprimindo se encontrar '\0' e, portanto, você acabará com um monte de lixo no fim da sua string. Portanto, certifique-se de terminar suas strings corretamente.
Matrizes de Caracteres
Uma constante de string, como
"Eu sou uma corda"
é um array de caracteres. É representado internamente em C pelos caracteres ASCII na string, ou seja, “I”, blank, “a”, “m”,…ou a string acima, e terminado pelo caractere nulo especial “\0” para que os programas possam encontrar o fim da string.
Constantes de string são frequentemente usadas para tornar a saída do código inteligível usando printf:
printf("Olá, mundo\n");
printf("O valor de a é: %f\n", a);
Constantes de string podem ser associadas a variáveis. C fornece a variável de tipo de caractere, que pode conter um caractere (1 byte) por vez. Uma string de caractere é armazenada em um array de tipo de caractere, um caractere ASCII por local.
Nunca se esqueça de que, como as strings são convencionalmente terminadas pelo caractere nulo “\0”, precisamos de um local de armazenamento extra na matriz.
C não fornece nenhum operador que manipule strings inteiras de uma vez. Strings são manipuladas por meio de ponteiros ou por rotinas especiais disponíveis na biblioteca de strings padrão string.h.
Usar ponteiros de caracteres é relativamente fácil, já que o nome de um array é apenas um ponteiro para seu primeiro elemento. Considere o programa dado a seguir:
#incluir<stdio.h>
vazio principal()
{
char texto_1[100], texto_2[100], texto_3[100];
caracteres *ta, *tb;
int eu;
/* define a mensagem como um array */
/* de caracteres; inicializá-lo */
/* para a string constante "..." */
/* deixe o compilador decidir */
/* seu tamanho usando [] */
char message[] = "Olá, sou uma string; o que são
você?";
printf("Mensagem original: %s\n", mensagem);
/* copie a mensagem para text_1 */
eu=0;
enquanto ( (texto_1[i] = mensagem[i]) != '\0' )
eu++;
printf("Texto_1: %s\n", texto_1);
/* usar aritmética de ponteiro explícita */
sua=mensagem;
tb=texto_2;
enquanto ( ( *tb++ = *ta++ ) != '\0' )
;
printf("Texto_2: %s\n", texto_2);
}
A saída do programa será a seguinte:
Mensagem original: Olá, eu sou uma string; o que você é?
Texto_1: Olá, eu sou uma corda; o que é você?
Texto_2: Olá, eu sou uma corda; o que é você?
A biblioteca padrão “string” contém muitas funções úteis para manipular strings, que aprenderemos na seção de strings mais tarde.
Acessando os Elementos
Para acessar um elemento individual no array, o número do índice segue o nome da variável entre colchetes. A variável pode então ser tratada como qualquer outra variável em C. O exemplo a seguir atribui um valor ao primeiro elemento no array.
x[0] = 16;
O exemplo a seguir imprime o valor do terceiro elemento em uma matriz.
printf("%d\n", x[2]);
O exemplo a seguir usa a função scanf para ler um valor do teclado no último elemento de uma matriz com dez elementos.
scanf("%d", &x[9]);
Inicializando elementos do array
Arrays podem ser inicializados como quaisquer outras variáveis por atribuição. Como um array contém mais de um valor, os valores individuais são colocados entre chaves e separados por vírgulas. O exemplo a seguir inicializa um array de dez dimensões com os dez primeiros valores da tabela de multiplicação tripla.
int x[10] = {3, 6, 9, 12, 15, 18, 21, 24, 27, 30};
Isso evita a atribuição de valores individualmente, como no exemplo a seguir.
int x[10];
x[0] = 3;
x[1] = 6;
x[2] = 9;
x[3] = 12;
x[4] = 15;
x[5] = 18;
x[6] = 21;
x[7] = 24;
x[8] = 27;
x[9] = 30;
Percorrendo um array
Como o array é indexado sequencialmente, podemos usar o loop for para exibir todos os valores de um array. O exemplo a seguir exibe todos os valores de um array:
#incluir <stdio.h>
int principal()
{
int x[10];
contador int;
/* Randomizar o gerador de números aleatórios */
srand((sem sinal)tempo(NULL));
/* Atribuir valores aleatórios à variável */
para (contador=0; contador<10; contador++)
x[contador] = rand();
/* Exibe o conteúdo do array */
para (contador=0; contador<10; contador++)
printf("o elemento %d tem o valor %d\n", contador, x[contador]);
retornar 0;
}
embora a saída imprima valores diferentes todas as vezes, o resultado será exibido mais ou menos assim:
o elemento 0 tem o valor 17132
o elemento 1 tem o valor 24904
o elemento 2 tem o valor 13466
o elemento 3 tem o valor 3147
o elemento 4 tem o valor 22006
o elemento 5 tem o valor 10397
o elemento 6 tem o valor 28114
o elemento 7 tem o valor 19817
o elemento 8 tem o valor 27430
o elemento 9 tem o valor 22136
Matrizes multidimensionais
Um array pode ter mais de uma dimensão. Permitir que o array tenha mais de uma dimensão proporciona maior flexibilidade. Por exemplo, planilhas são construídas em um array bidimensional; um array para as linhas e um array para as colunas.
O exemplo a seguir usa uma matriz bidimensional com duas linhas, cada uma contendo cinco colunas:
#incluir <stdio.h>
int principal()
{
/* Declare uma matriz multidimensional 2 x 5 */
int x[2][5] = { {1, 2, 3, 4, 5},
{2, 4, 6, 8, 10} };
int linha, coluna;
/* Exibir as linhas */
para (linha=0; linha<2; linha++)
{
/* Exibir as colunas */
para (coluna=0; coluna<5; coluna++)
printf("%d\t", x[linha][coluna]);
coloque('\n');
}
retornar 0;
}
A saída deste programa será exibida da seguinte forma:
1 2 3 4 5
2 4 6 8 10
Cordas
Uma string é um grupo de caracteres, geralmente letras do alfabeto, para formatar sua exibição impressa de forma que ela tenha uma boa aparência, tenha nomes e títulos significativos e seja esteticamente agradável para você e para as pessoas que usam a saída do seu programa.
Na verdade, você já usou strings nos exemplos dos tópicos anteriores. Mas não é a introdução completa de strings. Existem muitos casos possíveis na programação, onde o uso de strings formatadas ajuda o programador a evitar muitas complicações no programa e muitos bugs, é claro.
Uma definição completa de uma string é uma série de dados do tipo caractere terminados por um caractere nulo ('\0').
Quando C vai usar uma sequência de dados de alguma forma, seja para compará-la com outra sequência, gerá-la, copiá-la para outra sequência ou qualquer outra coisa, as funções são configuradas para fazer o que foram chamadas a fazer até que um valor nulo seja detectado.
Não há um tipo de dado básico para uma string em C. Em vez disso, strings em C são implementadas como um array de caracteres. Por exemplo, para armazenar um nome, você pode declarar um array de caracteres grande o suficiente para armazenar o nome e, então, usar as funções de biblioteca apropriadas para manipular o nome.
O exemplo a seguir exibe a string na tela, inserida pelo usuário:
#incluir <stdio.h>
int principal()
{
char name[80]; /* Cria uma matriz de caracteres
chamado nome */
printf("Digite seu nome: ");
obtém(nome);
printf("O nome que você digitou foi %s\n", name);
retornar 0;
}
A execução do programa será:
Digite seu nome: Tarun Tyagi
O nome que você digitou foi Tarun Tyagi
Algumas funções comuns de string
A biblioteca padrão string.h contém muitas funções úteis para manipular strings. Algumas das funções mais úteis foram exemplificadas aqui.
A função strlen
A função strlen é usada para determinar o comprimento de uma string. Vamos aprender o uso de strlen com exemplo:
#incluir <stdio.h>
#incluir <string.h>
int principal()
{
nome do caractere[80];
comprimento int;
printf("Digite seu nome: ");
obtém(nome);
comprimento = strlen(nome);
printf("Seu nome tem %d caracteres\n", length);
retornar 0;
}
E a execução do programa será a seguinte:
Digite seu nome: Tarun Subhash Tyagi
Seu nome tem 19 caracteres
Digite seu nome: Preeti Tarun
Seu nome tem 12 caracteres
A função strcpy
A função strcpy é usada para copiar uma string para outra. Vamos aprender o uso dessa função com um exemplo:
#incluir <stdio.h>
#incluir <string.h>
int principal()
{
char primeiro[80];
char segundo[80];
printf("Digite a primeira string: ");
obtém(primeiro);
printf("Digite a segunda string: ");
obtém(segundo);
printf("primeiro: %s, e segundo: %s Antes de strcpy()\n "
, primeiro, segundo);
strcpy(segundo, primeiro);
printf("primeiro: %s, e segundo: %s Depois de strcpy()\n",
primeiro, segundo);
retornar 0;
}
e a saída do programa será como:
Insira a primeira string: Tarun
Insira a segunda string: Tyagi
primeiro: Tarun e a segunda: Tyagi Antes de strcpy()
primeiro: Tarun e segundo: Tarun Depois de strcpy()
A função strcmp
A função strcmp é usada para comparar duas strings juntas. O nome da variável de um array aponta para o endereço base daquele array. Portanto, se tentarmos comparar duas strings usando o seguinte, estaríamos comparando dois endereços, que obviamente nunca seriam os mesmos, pois não é possível armazenar dois valores no mesmo local.
if (first == second) /* Nunca é possível comparar strings */
O exemplo a seguir usa a função strcmp para comparar duas strings:
#incluir <string.h>
int principal()
{
char primeiro[80], segundo[80];
int t;
para(t=1;t<=2;t++)
{
printf("\nDigite uma string: ");
obtém(primeiro);
printf("Digite outra string: ");
obtém(segundo);
se (strcmp(primeiro, segundo) == 0)
puts("As duas strings são iguais");
outro
puts("As duas strings não são iguais");
}
retornar 0;
}
E a execução do programa será a seguinte:
Insira uma string: Tarun
Insira outra string: tarun
As duas cordas não são iguais
Insira uma string: Tarun
Insira outra string: Tarun
As duas strings são iguais
A função strcat
A função strcat é usada para unir uma string a outra. Vamos ver como? Com a ajuda do exemplo:
#incluir <string.h>
int principal()
{
char primeiro[80], segundo[80];
printf("Digite uma string: ");
obtém(primeiro);
printf("Digite outra string: ");
obtém(segundo);
strcat(primeiro, segundo);
printf("As duas strings unidas: %s\n",
primeiro);
retornar 0;
}
E a execução do programa será a seguinte:
Insira uma string: Data
Insira outra string: Recovery
As duas strings unidas: DataRecovery
A função strtok
A função strtok é usada para encontrar o próximo token em uma string. O token é especificado por uma lista de delimitadores possíveis.
O exemplo a seguir lê uma linha de texto de um arquivo e determina uma palavra usando os delimitadores, espaço, tabulação e nova linha. Cada palavra é então exibida em uma linha separada:
#incluir <stdio.h>
#incluir <string.h>
int principal()
{
ARQUIVO *em;
linha char[80];
char *delimitadores = " \t\n";
char *token;
se ((em = fopen("C:\\texto.txt", "r")) == NULO)
{
puts("Não foi possível abrir o arquivo de entrada");
retornar 0;
}
/* Leia cada linha uma de cada vez */
enquanto(!feof(em))
{
/* Obter uma linha */
fgets(linha, 80, em);
se (!feof(em))
{
/* Divida a linha em palavras */
token = strtok(linha, delimitadores);
enquanto (token != NULL)
{
coloca(token);
/* Obtenha a próxima palavra */
token = strtok(NULL, delimitadores);
}
}
}
fclose(em);
retornar 0;
}
O programa acima, in = fopen("C:\\text.txt", "r"), abre um arquivo existente C:\\text.txt. Se o arquivo não existir no caminho especificado ou por qualquer razão, o arquivo não puder ser aberto, uma mensagem de erro será exibida na tela.
Considere o exemplo a seguir, que usa algumas dessas funções:
#incluir <stdio.h>
#incluir <string.h>
vazio principal()
{
char linha[100], *sub_texto;
/* inicializar string */
strcpy(line,"olá, eu sou uma string;");
printf("Linha: %s\n", linha);
/* adicionar ao final da string */
strcat(line," o que você é?");
printf("Linha: %s\n", linha);
/* encontra o comprimento da string */
/* strlen traz de volta */
/* comprimento como tipo size_t */
printf("Comprimento da linha: %d\n", (int)strlen(line));
/* encontra ocorrência de substrings */
se ((sub_texto = strchr(linha, 'W'))!= NULL)
printf("String começando com \"W\" ->%s\n",
sub_texto);
se ( ( sub_texto = strchr ( linha, 'w' ) )!= NULL )
printf("String começando com \"w\" ->%s\n",
sub_texto);
se ( ( sub_texto = strchr ( sub_texto, 'u' ) )!= NULL )
printf("String começando com \"w\" ->%s\n",
sub_texto);
}
A saída do programa será exibida da seguinte forma:
Linha: olá, eu sou uma corda;
Linha: Olá, eu sou uma corda; o que é você?
Comprimento da linha: 35
String começando com "w" ->o que você é?
String começando com "w" ->u?
Funções
A melhor maneira de desenvolver e manter um programa grande é construí-lo a partir de pedaços menores, cada um dos quais é mais fácil de gerenciar (uma técnica às vezes chamada de Dividir e Conquistar). Funções permitem que o programador modularize o programa.
Funções permitem que programas complicados sejam divididos em pequenos blocos, cada um dos quais é mais fácil de escrever, ler e manter. Já encontramos a função main e fizemos uso de printf da biblioteca padrão. Podemos, é claro, fazer nossas próprias funções e arquivos de cabeçalho. Uma função tem o seguinte layout:
tipo de retorno nome-da-função (lista de argumentos se necessário)
{
declarações locais;
declarações ;
retornar valor de retorno;
}
Se return-type for omitido, o C assume como padrão int. O return-value deve ser do tipo declarado. Todas as variáveis declaradas dentro de funções são chamadas de variáveis locais, pois são conhecidas apenas na função para a qual foram definidas.
Algumas funções têm uma lista de parâmetros que fornece um método de comunicação entre a função e o módulo que chamou a função. Os parâmetros também são variáveis locais, pois não estão disponíveis fora da função. Os programas cobertos até agora têm main, que é uma função.
Uma função pode simplesmente executar uma tarefa sem retornar nenhum valor, caso em que ela tem o seguinte layout:
void function-name (lista de argumentos se necessário)
{
declarações locais;
declarações;
}
Os argumentos são sempre passados por valor em chamadas de função C. Isso significa que cópias locais dos valores dos argumentos são passadas para as rotinas. Qualquer alteração feita aos argumentos internamente na função é feita apenas para as cópias locais dos argumentos.
Para alterar ou definir um argumento na lista de argumentos, esse argumento deve ser passado como um endereço. Você usa variáveis regulares se a função não alterar os valores desses argumentos. Você DEVE usar ponteiros se a função alterar os valores desses argumentos.
Vamos aprender com exemplos:
#incluir <stdio.h>
troca nula (int *a, int *b)
{
temperatura interna;
temperatura = *a;
*a = *b;
*b = temperatura;
printf("Da função exchange: ");
printf("a = %d, b = %d\n", *a, *b);
}
vazio principal()
{
int um, b;
a = 5;
b = 7;
printf("Do principal: a = %d, b = %d\n", a, b);
troca(&a, &b);
printf("De volta ao principal: ");
printf("a = %d, b = %d\n", a, b);
}
E a saída deste programa será exibida da seguinte forma:
Do principal: a = 5, b = 7
Da troca de funções: a = 7, b = 5
De volta ao principal: a = 7, b = 5
Vamos ver outro exemplo. O exemplo a seguir usa uma função chamada square que escreve o quadrado dos números entre 1 e 10.
#incluir <stdio.h>
int square(int x); /* Protótipo de função */
int principal()
{
contador int;
para (contador=1; contador<=10; contador++)
printf("O quadrado de %d é %d\n", counter, square(counter));
retornar 0;
}
/* Defina a função 'quadrado' */
int quadrado(int x)
{
retornar x * x;
}
A saída deste programa será exibida da seguinte forma:
O quadrado de 1 é 1
O quadrado de 2 é 4
O quadrado de 3 é 9
O quadrado de 4 é 16
O quadrado de 5 é 25
O quadrado de 6 é 36
O quadrado de 7 é 49
O quadrado de 8 é 64
O quadrado de 9 é 81
O quadrado de 10 é 100
O protótipo de função square declara uma função que recebe um parâmetro inteiro e retorna um inteiro. Quando o compilador alcança a chamada de função para square no programa principal, ele é capaz de verificar a chamada de função em relação à definição da função.
Quando o programa alcança a linha que chama a função square, o programa salta para a função e executa essa função antes de retomar seu caminho através do programa principal. Programas que não têm um tipo de retorno devem ser declarados usando void. Assim, os Parâmetros para a função podem ser Pass By Value ou Pass By Reference.
Uma função Recursiva é uma função que chama a si mesma. E esse processo é chamado de recursão.
Funções de passagem por valor
Os parâmetros da função square no exemplo anterior são passados por valor. Isso significa que apenas uma cópia da variável foi passada para a função. Quaisquer alterações no valor não serão refletidas de volta para a função de chamada.
O exemplo a seguir usa passagem por valor e altera o valor do parâmetro passado, o que não tem efeito na função de chamada. A função count_down foi declarada como void, pois não há tipo de retorno.
#incluir <stdio.h>
contagem_regressiva vazia(int x);
int principal()
{
contador int;
para (contador=1; contador<=10; contador++)
contagem_regressiva(contador);
retornar 0;
}
contagem_regressiva vazia(int x)
{
contador int;
para (contador = x; contador > 0; contador--)
{
printf("%d ", x);
x--;
}
coloque('\n');
}
A saída do programa será exibida da seguinte forma:
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
6 5 4 3 2 1
7 6 5 4 3 2 1
8 7 6 5 4 3 2 1
9 8 7 6 5 4 3 2 1
10 9 8 7 6 5 4 3 2 1
Vamos ver outro Exemplo de Passagem por Valor em C para entender melhor. O exemplo a seguir converte um número entre 1 e 30.000 digitado pelo usuário em palavras.
#incluir <stdio.h>
vazio do_units(int num);
vazio do_tens(int num);
vazio do_teens(int num);
int principal()
{
int num, resto;
fazer
{
printf("Digite um número entre 1 e 30.000: ");
scanf("%d", &num);
} enquanto (num < 1 || num > 30000);
resto = número;
printf("%d em palavras = ", num);
do_tens(resíduo/1000);
se (num >= 1000)
printf("mil ");
resíduo %= 1000;
do_units(resíduo/100);
se (resíduo >= 100)
{
printf("cem ");
}
se (num > 100 && num%100 > 0)
printf("e ");
resíduo %=100;
do_tens(resíduo);
coloque('\n');
retornar 0;
}
vazio do_units(int num)
{
interruptor(num)
{
caso 1:
printf("um ");
quebrar;
caso 2:
printf("dois ");
quebrar;
caso 3:
printf("três ");
quebrar;
caso 4:
printf("quatro ");
quebrar;
caso 5:
printf("cinco ");
quebrar;
caso 6:
printf("seis ");
quebrar;
caso 7:
printf("sete ");
quebrar;
caso 8:
printf("oito ");
quebrar;
caso 9:
printf("nove ");
}
}
vazio do_tens(int num)
{
alternar(num/10)
{
caso 1:
do_teens(num);
quebrar;
caso 2:
printf("vinte ");
quebrar;
caso 3:
printf("trinta ");
quebrar;
caso 4:
printf("quarenta ");
quebrar;
caso 5:
printf("cinquenta ");
quebrar;
caso 6:
printf("sessenta ");
quebrar;
caso 7:
printf("setenta ");
quebrar;
caso 8:
printf("oitenta ");
quebrar;
caso 9:
printf("noventa ");
}
se (num/10 != 1)
do_units(num%10);
}
vazio do_teens(int num)
{
interruptor(num)
{
caso 10:
printf("dez ");
quebrar;
caso 11:
printf("onze ");
quebrar;
caso 12:
printf("doze ");
quebrar;
caso 13:
printf("treze ");
quebrar;
caso 14:
printf("quatorze ");
quebrar;
caso 15:
printf("quinze ");
quebrar;
caso 16:
printf("dezesseis ");
quebrar;
caso 17:
printf("dezessete ");
quebrar;
caso 18:
printf("dezoito ");
quebrar;
caso 19:
printf("dezenove ");
}
}
e a saída do programa será a seguinte:
Digite um número entre 1 e 30.000: 12345
12345 por extenso = doze mil trezentos e quarenta e cinco
Chamada por referência
Para fazer uma chamada de função por referência, em vez de passar a variável em si, passe o endereço da variável. O endereço da variável pode ser obtido usando o operador &. O seguinte chama uma função swap passando o endereço das variáveis em vez dos valores reais.
trocar(&x, &y);
Desreferenciação
O problema que temos agora é que a função swap recebeu o endereço em vez da variável, então precisamos desreferenciar as variáveis para que possamos olhar os valores reais em vez dos endereços das variáveis para trocá-las.
A desreferenciação é obtida em C usando a notação de ponteiro (*). Em termos simples, isso significa colocar um * antes de cada variável antes de usá-la para que ela se refira ao valor da variável em vez de seu endereço. O programa a seguir ilustra a passagem por referência para trocar dois valores.
#incluir <stdio.h>
troca vazia(int *x, int *y);
int principal()
{
inteiro x=6, y=10;
printf("Antes da troca de função, x = %d e y =
%d\n\n", x, y);
trocar(&x, &y);
printf("Após a troca de funções, x = %d e y =
%d\n\n", x, y);
retornar 0;
}
troca vazia(int *x, int *y)
{
int temperatura = *x;
*x = *y;
*y = temperatura;
}
Vamos ver a saída do programa:
Antes da troca de funções, x = 6 e y = 10
Após a troca de funções, x = 10 e y = 6
Funções podem ser recursivas, ou seja, uma função pode chamar a si mesma. Cada chamada para si mesma requer que o estado atual da função seja empurrado para a pilha. É importante lembrar desse fato, pois é fácil criar um estouro de pilha, ou seja, a pilha ficou sem espaço para colocar mais dados.
O exemplo a seguir calcula o fatorial de um número usando recursão. Um fatorial é um número multiplicado por todos os outros inteiros abaixo dele, até 1. Por exemplo, o fatorial do número 6 é:
Fatorial 6 = 6 * 5 * 4 * 3 * 2 * 1
Portanto, o fatorial de 6 é 720. Pode-se ver no exemplo acima que fatorial 6 = 6 * fatorial 5. Da mesma forma, fatorial 5 = 5 * fatorial 4, e assim por diante.
A seguir está a regra geral para calcular números fatoriais.
fatorial(n) = n * fatorial(n-1)
A regra acima termina quando n = 1, pois o fatorial de 1 é 1. Vamos tentar entender melhor com a ajuda de um exemplo:
#incluir <stdio.h>
longo int fatorial(int num);
int principal()
{
número inteiro;
int longo f;
printf("Digite um número: ");
scanf("%d", &num);
f = fatorial(num);
printf("fatorial de %d é %ld\n", num, f);
retornar 0;
}
longo int fatorial(int num)
{
se (num == 1)
retornar 1;
outro
retornar num * fatorial(num-1);
}
Vamos ver a saída da execução deste programa:
Insira um número: 7
fatorial de 7 é 5040
Alocação de memória em C
O compilador C tem uma biblioteca de alocação de memória, definida em malloc.h. A memória é reservada usando a função malloc, e retorna um ponteiro para o endereço. Ele pega um parâmetro, o tamanho da memória necessária em bytes.
O exemplo a seguir aloca espaço para a string "hello world".
ptr = (char *)malloc(strlen("Olá mundo") + 1);
O byte extra é necessário para levar em conta o caractere de terminação de string, '\0'. O (char *) é chamado de cast e força o tipo de retorno a ser char *.
Como os tipos de dados têm tamanhos diferentes e malloc retorna o espaço em bytes, é uma boa prática, por motivos de portabilidade, usar o operador sizeof ao especificar um tamanho a ser alocado.
O exemplo a seguir lê uma string no buffer de matriz de caracteres e então aloca a quantidade exata de memória necessária e a copia para uma variável chamada "ptr".
#incluir <string.h>
#incluir <malloc.h>
int principal()
{
char *ptr, buffer[80];
printf("Digite uma string: ");
obtém(buffer);
ptr = (char *)malloc((strlen(buffer) + 1) *
tamanho de(caractere));
strcpy(ptr, buffer);
printf("Você digitou: %s\n", ptr);
retornar 0;
}
A saída do programa será a seguinte:
Insira uma sequência: A Índia é a melhor
Você inseriu: A Índia é a melhor
Realocando Memória
É possível que muitas vezes, enquanto você estiver programando, você queira realocar memória. Isso é feito com a função realloc. A função realloc pega dois parâmetros, o endereço base da memória que você quer redimensionar e a quantidade de espaço que você quer reservar e retorna um ponteiro para o endereço base.
Suponha que reservamos espaço para um ponteiro chamado msg e queremos realocar espaço para a quantidade de espaço que ele já ocupa, mais o comprimento de outra string. Então poderíamos usar o seguinte.
msg = (char *)realloc(msg, (strlen(msg) + strlen(buffer) + 1)*sizeof(char));
O programa a seguir ilustra o uso de malloc, realloc e free. O usuário insere uma série de strings que são unidas. O programa para de ler strings quando uma string vazia é inserida.
#incluir <string.h>
#incluir <malloc.h>
int principal()
{
char buffer[80], *msg;
int primeiroTempo=0;
fazer
{
printf("\nDigite uma frase: ");
obtém(buffer);
se (!primeiraVez)
{
msg = (char *)malloc((strlen(buffer) + 1) *
tamanho de(caractere));
strcpy(msg, buffer);
primeiraVez = 1;
}
outro
{
mensagem = (char *)realloc(msg, (strlen(msg) +
strlen(buffer) + 1) * sizeof(char));
strcat(mensagem, buffer);
}
coloca(msg);
} enquanto(strcmp(buffer, ""));
grátis(msg);
retornar 0;
}
A saída do programa será a seguinte:
Digite uma frase: Era uma vez
Era uma vez
Digite uma frase: havia um rei
Era uma vez havia um rei
Digite uma frase: o rei era Era
uma vez havia um rei o rei era
Insira uma frase:
Era uma vez um rei, o rei era
Liberando a Memória
Quando você tiver terminado com a memória que foi alocada, você nunca deve esquecer de liberar a memória, pois isso liberará recursos e melhorará a velocidade. Para liberar a memória alocada, use a função free.
livre(ptr);
Estruturas
Assim como os tipos básicos de dados, C tem um mecanismo de estrutura que permite que você agrupe itens de dados que são relacionados entre si sob um nome comum. Isso é comumente chamado de Tipo Definido pelo Usuário.
A palavra-chave struct inicia a definição da estrutura, e uma tag fornece o nome exclusivo para a estrutura. Os tipos de dados e nomes de variáveis adicionados à estrutura são membros da estrutura. O resultado é um modelo de estrutura que pode ser usado como um especificador de tipo. A seguir está uma estrutura com uma tag de mês.
estrutura mês
{
nome do caractere[10];
char abrev[4];
int dias;
};
Um tipo de estrutura geralmente é definido próximo ao início de um arquivo usando uma instrução typedef. typedef define e nomeia um novo tipo, permitindo seu uso em todo o programa. typedef geralmente ocorre logo após as instruções #define e #include em um arquivo.
A palavra-chave typedef pode ser usada para definir uma palavra para se referir à estrutura em vez de especificar a palavra-chave struct com o nome da estrutura. É comum nomear o typedef em letras maiúsculas. Aqui estão os exemplos de definição de estrutura.
typedef estrutura {
nome do char[64];
curso de char[128];
idade int;
ano int;
} estudante;
Isso define um novo tipo de variável de aluno do tipo aluno que pode ser declarada da seguinte forma.
estudante st_rec;
Observe como isso é similar a declarar um int ou float. O nome da variável é st_rec, ela tem membros chamados nome, curso, idade e ano. Similarmente,
elemento struct typedef
{
dados char;
struct elemento *próximo;
} ELEMENTO DE PILHA;
Uma variável do tipo struct element definido pelo usuário agora pode ser declarada da seguinte maneira.
ELEMENTOEMPILHADEIRA *pilha;
Considere a seguinte estrutura:
estrutura do aluno
{
char *nome;
grau interno;
};
Um ponteiro para struct student pode ser definido da seguinte forma.
estrutura aluno *hnc;
Ao acessar um ponteiro para uma estrutura, o operador de ponteiro de membro, -> é usado em vez do operador de ponto. Para adicionar uma nota a uma estrutura,
nota s = 50;
Você pode atribuir uma nota à estrutura da seguinte maneira.
s->nota = 50;
Assim como com os tipos de dados básicos, se você quiser que as alterações feitas em uma função para parâmetros passados sejam persistentes, você tem que passar por referência (passar o endereço). O mecanismo é exatamente o mesmo dos tipos de dados básicos. Passe o endereço e refira-se à variável usando notação de ponteiro.
Tendo definido a estrutura, você pode declarar uma instância dela e atribuir valores aos membros usando a notação de ponto. O exemplo a seguir ilustra o uso da estrutura month.
#incluir <stdio.h>
#incluir <string.h>
estrutura mês
{
nome do caractere[10];
abreviação char[4];
int dias;
};
int principal()
{
estrutura mês m;
strcpy(m.name, "Janeiro");
strcpy(m.abreviação, "Jan");
m.dias = 31;
printf("%s é abreviado como %s e tem %d dias\n", m.nome, m.abreviação, m.dias);
retornar 0;
}
A saída do programa será a seguinte:
Janeiro é abreviado como Jan e tem 31 dias
Todos os compiladores ANSI C permitem que você atribua uma estrutura a outra, realizando uma cópia membro a membro. Se tivéssemos estruturas de mês chamadas m1 e m2, poderíamos atribuir os valores de m1 a m2 com o seguinte:
- Estrutura com membros de ponteiro.
- Estrutura inicializa.
- Passando uma estrutura para uma função.
- Ponteiros e Estruturas.
Estruturas com membros de ponteiro em C
Manter strings em um array de tamanho fixo é um uso ineficiente de memória. Uma abordagem mais eficiente seria usar ponteiros. Ponteiros são usados em estruturas exatamente da mesma forma que são usados em definições normais de ponteiros. Vejamos um exemplo:
#incluir <string.h>
#incluir <malloc.h>
estrutura mês
{
char *nome;
char *abreviação;
int dias;
};
int principal()
{
estrutura mês m;
m.name = (char *)malloc((strlen("Janeiro")+1) *
tamanho de(caractere));
strcpy(m.name, "Janeiro");
m.abreviação = (char *)malloc((strlen("Jan")+1) *
tamanho de(caractere));
strcpy(m.abreviação, "Jan");
m.dias = 31;
printf("%s é abreviado como %s e tem %d dias\n",
m.nome, m.abreviação, m.dias);
retornar 0;
}
A saída do programa será a seguinte:
Janeiro é abreviado como Jan e tem 31 dias
Inicializadores de Estrutura em C
Para fornecer um conjunto de valores iniciais para a estrutura, Initialisers podem ser adicionados à declaração. Como os meses começam em 1, mas os arrays começam em zero em C, um elemento extra na posição zero chamado junk foi usado no exemplo a seguir.
#incluir <stdio.h>
#incluir <string.h>
estrutura mês
{
char *nome;
char *abreviação;
int dias;
} detalhes_do_mês[] =
{
"Lixo", "Lixo", 0,
"Janeiro", "Jan", 31,
"Fevereiro", "Fev", 28,
"Março", "Mar", 31,
"Abril", "Abr", 30,
"Maio", "Maio", 31,
"Junho", "Jun", 30,
"Julho", "Jul", 31,
"Agosto", "Agosto", 31,
"Setembro", "Set", 30,
"Outubro", "Out", 31,
"Novembro", "Nov", 30,
"Dezembro", "Dez", 31
};
int principal()
{
contador int;
para (contador=1; contador<=12; contador++)
printf("%s é abreviado como %s e tem %d dias\n",
detalhes_do_mês[contador].nome,
detalhes_do_mês[contador].abreviação,
detalhes_do_mês[contador].dias);
retornar 0;
}
E a saída será exibida da seguinte forma:
Janeiro é abreviado como Jan e tem 31 dias
Fevereiro é abreviado como fev e tem 28 dias
Março é abreviado como Mar e tem 31 dias
Abril é abreviado como Apr e tem 30 dias
Maio é abreviado como maio e tem 31 dias
Junho é abreviado como Jun e tem 30 dias
Julho é abreviado como Jul e tem 31 dias
Agosto é abreviado como Aug e tem 31 dias
Setembro é abreviado como Sep e tem 30 dias
Outubro é abreviado como out e tem 31 dias
Novembro é abreviado como Nov e tem 30 dias
Dezembro é abreviado como Dec e tem 31 dias
Passando Estruturas para Funções em C
Estruturas podem ser passadas como um parâmetro para uma função, assim como qualquer um dos tipos básicos de dados. O exemplo a seguir usa uma estrutura chamada date que é passada para uma função isLeapYear para determinar se o ano é bissexto.
Normalmente você passaria apenas o valor do dia, mas toda a estrutura é passada para ilustrar a passagem de estruturas para funções.
#incluir <stdio.h>
#incluir <string.h>
estrutura mês
{
char *nome;
char *abreviação;
int dias;
} detalhes_do_mês[] =
{
"Lixo", "Lixo", 0,
"Janeiro", "Jan", 31,
"Fevereiro", "Fev", 28,
"Março", "Mar", 31,
"Abril", "Abr", 30,
"Maio", "Maio", 31,
"Junho", "Jun", 30,
"Julho", "Jul", 31,
"Agosto", "Agosto", 31,
"Setembro", "Set", 30,
"Outubro", "Out", 31,
"Novembro", "Nov", 30,
"Dezembro", "Dez", 31
};
data de estrutura
{
dia inteiro;
int mês;
ano int;
};
int éAnoBissexto(struct data d);
int principal()
{
estrutura data d;
printf("Digite a data (ex.: 11/11/1980): ");
scanf("%d/%d/%d", &d.dia, &d.mês, &d.ano);
printf("A data %d %s %d é ", d.day,
detalhes_do_mês[d.mês].nome, d.ano);
se (isLeapYear(d) == 0)
printf("não ");
puts("um ano bissexto");
retornar 0;
}
int éAnoBissexto(struct data d)
{
se ((d.ano % 4 == 0 && d.ano % 100 != 0) ||
d.ano % 400 == 0)
retornar 1;
retornar 0;
}
E a Execução do programa será a seguinte:
Insira a data (por exemplo: 11/11/1980): 9/12/1980
A data 9 de dezembro de 1980 é um ano bissexto
O exemplo a seguir aloca dinamicamente um array de estruturas para armazenar nomes e notas de alunos. As notas são então exibidas de volta ao usuário em ordem crescente.
#incluir <string.h>
#incluir <malloc.h>
estrutura do aluno
{
char *nome;
grau interno;
};
troca vazia(struct aluno *x, struct aluno *y);
int principal()
{
estrutura aluno *grupo;
buffer de caracteres[80];
int espúrio;
int interno, externo;
int contador, numAlunos;
printf("Quantos alunos há no grupo: ");
scanf("%d", &numAlunos);
grupo = (struct aluno *)malloc(numAlunos *
tamanho de(struct aluno));
para (contador=0; contador<numEstudantes; contador++)
{
espúrio = getchar();
printf("Digite o nome do aluno: ");
obtém(buffer);
grupo[contador].nome = (char *)malloc((strlen(buffer)+1) * sizeof(char));
strcpy(grupo[contador].nome, buffer);
printf("Digite a nota: ");
scanf("%d", &grupo[contador].nota);
}
para (externo=0; externo<numEstudantes; externo++)
para (interno=0; interno<externo; interno++)
se (grupo[externo].grau <
grupo[interno].grau)
swap(&grupo[externo], &grupo[interno]);
puts("O grupo em ordem crescente de notas ...");
para (contador=0; contador<numEstudantes; contador++)
printf("%s atingiu a nota %d \n”,
grupo[contador].nome,
grupo[contador].nota);
retornar 0;
}
troca vazia(struct aluno *x, struct aluno *y)
{
estrutura aluno temp;
temp.nome = (char *)malloc((strlen(x->nome)+1) *
tamanho de(caractere));
strcpy(temp.nome, x->nome);
temp.grade = x->grade;
x->nota = y->nota;
x->nome = (char *)malloc((strlen(y->nome)+1) *
tamanho de(caractere));
strcpy(x->nome, y->nome);
y->nota = temp.nota;
y->nome = (char *)malloc((strlen(temp.nome)+1) *
tamanho de(caractere));
strcpy(y->nome, temp.nome);
}
A execução da saída será a seguinte:
Quantos alunos há no grupo: 4
Digite o nome do aluno: Anuraaj
Digite a série: 7
Digite o nome do aluno: Honey
Digite a série: 2
Digite o nome do aluno: Meetushi
Digite a série: 1
Digite o nome do aluno: Deepti
Digite a série: 4
O grupo em ordem crescente de notas ...
Meetushi alcançou grau 1
Honey alcançou grau 2
Deepti alcançou grau 4
Anuraaj alcançou grau 7
União
Uma união permite que você olhe para os mesmos dados com tipos diferentes, ou use os mesmos dados com nomes diferentes. Uniões são semelhantes a estruturas. Uma união é declarada e usada das mesmas maneiras que uma estrutura.
Uma união difere de uma estrutura porque apenas um de seus membros pode ser usado por vez. A razão para isso é simples. Todos os membros de uma união ocupam a mesma área de memória. Eles são colocados uns sobre os outros.
As uniões são definidas e declaradas da mesma forma que as estruturas. A única diferença nas declarações é que a palavra-chave union é usada em vez de struct. Para definir uma união simples de uma variável char e uma variável integer, você escreveria o seguinte:
união compartilhada {
caracteres c;
int eu;
};
Esta união, compartilhada, pode ser usada para criar instâncias de uma união que pode conter um valor de caractere c ou um valor inteiro i. Esta é uma condição OR. Diferentemente de uma estrutura que conteria ambos os valores, a união pode conter apenas um valor por vez.
Uma união pode ser inicializada em sua declaração. Porque somente um membro pode ser usado por vez e somente um pode ser inicializado. Para evitar confusão, somente o primeiro membro da união pode ser inicializado. O código a seguir mostra uma instância da união compartilhada sendo declarada e inicializada:
união compartilhada generic_variable = {`@'};
Observe que a união generic_variable foi inicializada da mesma forma que o primeiro membro de uma estrutura seria inicializado.
Membros individuais da união podem ser usados da mesma forma que membros da estrutura podem ser usados usando o operador de membro (.). No entanto, há uma diferença importante no acesso a membros da união.
Apenas um membro do union deve ser acessado por vez. Como um union armazena seus membros um em cima do outro, é importante acessar apenas um membro por vez.
A palavra-chave união
etiqueta de união {
membro(s) do sindicato;
/* declarações adicionais podem ser inseridas aqui */
}exemplo;
A palavra-chave union é usada para declarar uniões. Uma união é uma coleção de uma ou mais variáveis (union_members) que foram agrupadas sob um único nome. Além disso, cada um desses membros da união ocupa a mesma área de memória.
A palavra-chave union identifica o início de uma definição de union. Ela é seguida por uma tag que é o nome dado à union. Após a tag estão os membros da union entre chaves.
Uma instância, a declaração real de uma união, também pode ser definida. Se você definir a estrutura sem a instância, ela será apenas um modelo que pode ser usado mais tarde em um programa para declarar estruturas. O seguinte é o formato de um modelo:
etiqueta de união {
membro(s) do sindicato;
/* declarações adicionais podem ser inseridas aqui */
};
Para usar o modelo, você usaria o seguinte formato: instância da tag union;
Para usar este formato, você deve ter declarado previamente uma união com a tag fornecida.
/* Declare um modelo de união chamado tag */
etiqueta de união {
número inteiro;
Alpes char;
}
/* Use o modelo de união */
tag de união variável_mista;
/* Declarar uma união e uma instância juntas */
união generic_type_tag {
caracteres c;
int eu;
flutuar f;
duplo d;
} genérico;
/* Inicializa uma união. */
união data_tag {
char data_completa[9];
estrutura part_date_tag {
char mês[2];
char quebra_valor1;
dia do char[2];
char break_value2;
char ano[2];
} data_da_parte;
}data = {"09/12/80"};
Vamos entender melhor com a ajuda de exemplos:
#incluir <stdio.h>
int principal()
{
união
{
int value; /* Esta é a primeira parte da união */
estrutura
{
char first; /* Esses dois valores são a segunda parte dele */
char segundo;
} metade;
} número;
índice longo;
para (índice = 12; índice < 300000L; índice += 35231L)
{
número.valor = índice;
printf("%8x %6x %6x\n", número.valor,
número.metade.primeiro,
número.meio.segundo);
}
retornar 0;
}
E a saída do programa será exibida da seguinte forma:
cc 0
89ab fabuloso ff89
134a 4a 13
9ce9 ffe9 ff9c
2688 ff88 26
b027 27 ffb0
39c6 ffc6 39
c365 65 ffc3
4d04 4 4d
Um uso prático de uma união na recuperação de dados
Agora, vamos ver um uso prático de union na programação de recuperação de dados. Vamos dar um pequeno exemplo. O programa a seguir é o pequeno modelo de programa de escaneamento de setores defeituosos para uma unidade de disquete (a: ), no entanto, não é o modelo completo de software de escaneamento de setores defeituosos.
Vamos examinar o programa:
#include<dos.h>
#incluir<conio.h>
int principal()
{
int rp, cabeça, trilha, setor, status;
char *buf;
união REGS dentro, fora;
estrutura SREGS s;
clrscr();
/* Redefina o sistema de disco para inicializar o disco */
printf("\n Redefinindo o sistema de disco....");
para(rp=0;rp<=2;rp++)
{
em.h.ah = 0;
em.h.dl = 0x00;
int86(0x13,&entrada,&saída);
}
printf("\n\n\n Agora testando o disco para setores defeituosos....");
/* verificar setores defeituosos */
para(trilha=0;trilha<=79;trilha++)
{
para(cabeça=0;cabeça<=1;cabeça++)
{
para(setor=1;setor<=18;setor++)
{
em.h.ah = 0x04;
pol.h.al = 1;
em.h.dl = 0x00;
in.h.ch = trilha;
in.h.dh = cabeça;
in.h.cl = setor;
em.x.bx = FP_OFF(buf);
s.es = FP_SEG(buf);
int86x(0x13,&entrada,&saída,&s);
se(fora.x.cflag)
{
status=fora.h.ah;
printf("\n trilha:%d Cabeçalho:%d Setor:%d Status ==0x%X",trilha,cabeçalho,setor,status);
}
}
}
}
printf("\n\n\nConcluído");
retornar 0;
}
Agora vamos ver como será a saída se houver setores defeituosos no disquete:
Redefinindo o sistema de disco....
Agora testando o disco para setores defeituosos...
trilha:0 Cabeça:0 Setor:4 Status ==0xA
trilha:0 Cabeça:0 Setor:5 Status ==0xA
trilha:1 Cabeça:0 Setor:4 Status ==0xA
trilha:1 Cabeça:0 Setor:5 Status ==0xA
faixa:1 Cabeça:0 Setor:6 Status ==0xA
faixa:1 Cabeça:0 Setor:7 Status ==0xA
faixa:1 Cabeça:0 Setor:8 Status ==0xA
faixa:1 Cabeça:0 Setor:11 Status ==0xA
faixa:1 Cabeça:0 Setor:12 Status ==0xA
faixa:1 Cabeça:0 Setor:13 Status ==0xA
faixa:1 Cabeça:0 Setor:14 Status ==0xA
faixa:1 Cabeça:0 Setor:15 Status ==0xA
faixa:1 Cabeça:0 Setor:16 Status ==0xA
faixa:1 Cabeça:0 Setor:17 Status ==0xA
faixa:1 Cabeça:0 Setor:18 Status ==0xA
trilha:1 Cabeça:1 Setor:5 Status ==0xA
trilha:1 Cabeça:1 Setor:6 Status ==0xA
faixa:1 Cabeça:1 Setor:7 Status ==0xA
faixa:1 Cabeça:1 Setor:8 Status ==0xA
faixa:1 Cabeça:1 Setor:9 Status ==0xA
faixa:1 Cabeça:1 Setor:10 Status ==0xA
trilha:1 Cabeça:1 Setor:11 Status ==0xA
faixa:1 Cabeça:1 Setor:12 Status ==0xA
faixa:1 Cabeça:1 Setor:13 Status ==0xA
faixa:1 Cabeça:1 Setor:14 Status ==0xA
faixa:1 Cabeça:1 Setor:15 Status ==0xA
faixa:1 Cabeça:1 Setor:16 Status ==0xA
faixa:1 Cabeça:1 Setor:17 Status ==0xA
faixa:1 Cabeça:1 Setor:18 Status ==0xA
faixa:2 Cabeça:0 Setor:4 Status ==0xA
faixa:2 Cabeça:0 Setor:5 Status ==0xA
faixa:14 Cabeça:0 Setor:6 Status ==0xA
Feito
Pode ser um pouco difícil entender as funções e interrupções usadas neste programa para verificar se há setores defeituosos no disco e redefinir o sistema de disco, etc., mas você não precisa se preocupar, aprenderemos todas essas coisas nas seções de programação do BIOS e interrupções mais adiante nos próximos capítulos.
Manipulação de arquivos em C
O acesso a arquivos em C é obtido associando um fluxo a um arquivo. C se comunica com arquivos usando um novo tipo de dado chamado ponteiro de arquivo. Esse tipo é definido em stdio.h e escrito como FILE *. Um ponteiro de arquivo chamado output_file é declarado em uma declaração como
ARQUIVO *arquivo_de_saída;
Os modos de arquivo da função fopen
Seu programa deve abrir um arquivo antes de poder acessá-lo. Isso é feito usando a função fopen, que retorna o ponteiro de arquivo necessário. Se o arquivo não puder ser aberto por qualquer motivo, o valor NULL será retornado. Você normalmente usará fopen da seguinte forma
se ((arquivo_de_saída = fopen("arquivo_de_saída", "w")) == NULL)
fprintf(stderr, "Não é possível abrir %s\n",
"arquivo_de_saída");
fopen recebe dois argumentos, ambos são strings, o primeiro é o nome do arquivo a ser aberto, o segundo é um caractere de acesso, que geralmente é r, a ou w etc. Os arquivos podem ser abertos em vários modos, conforme mostrado na tabela a seguir.
Modos de arquivo |
r |
Abra um arquivo de texto para leitura. |
Em |
Crie um arquivo de texto para escrita. Se o arquivo existir, ele será sobrescrito. |
um |
Abra um arquivo de texto no modo append. O texto é adicionado ao final do arquivo. |
rb |
Abra um arquivo binário para leitura. |
uau |
Crie um arquivo binário para escrita. Se o arquivo existir, ele será sobrescrito. |
sobre |
Abra um arquivo binário no modo append. Os dados são adicionados ao final do arquivo. |
r+ |
Abra um arquivo de texto para leitura e escrita. |
em+ |
Crie um arquivo de texto para leitura e escrita. Se o arquivo existir, ele será sobrescrito. |
um+ |
Abra um arquivo de texto para leitura e escrita no final. |
r+b ou rb+ |
Abra o arquivo binário para leitura e escrita. |
w+b ou wb+ |
Crie um arquivo binário para leitura e escrita. Se o arquivo existir, ele será sobrescrito. |
a+b ou ab+ |
Abra um arquivo de texto para leitura e escrita no final. |
Os modos de atualização são usados com as funções fseek, fsetpos e rewind. A função fopen retorna um ponteiro de arquivo, ou NULL se ocorrer um erro.
O exemplo a seguir abre um arquivo, tarun.txt, em modo somente leitura. É uma boa prática de programação testar se o arquivo existe.
se ((em = fopen("tarun.txt", "r")) == NULL)
{
puts("Não foi possível abrir o arquivo");
retornar 0;
}
Fechando Arquivos
Os arquivos são fechados usando a função fclose. A sintaxe é a seguinte:
fclose(em);
Lendo arquivos
A função feof é usada para testar o fim do arquivo. As funções fgetc, fscanf e fgets são usadas para ler dados do arquivo.
O exemplo a seguir lista o conteúdo de um arquivo na tela, usando fgetc para ler o arquivo um caractere por vez.
#incluir <stdio.h>
int principal()
{
ARQUIVO *em;
chave int;
se ((em = fopen("tarun.txt", "r")) == NULL)
{
puts("Não foi possível abrir o arquivo");
retornar 0;
}
enquanto (!feof(in))
{
chave = fgetc(in);
/* O último caractere lido é o marcador de fim de arquivo, então não o imprima */
se (!feof(em))
putchar(chave);
}
fclose(em);
retornar 0;
}
A função fscanf pode ser usada para ler diferentes tipos de dados do arquivo, como no exemplo a seguir, desde que os dados no arquivo estejam no formato da string de formato usada com fscanf.
fscanf(in, "%d/%d/%d", &dia, &mês, &ano);
A função fgets é usada para ler um número de caracteres de um arquivo. stdin é o fluxo de arquivo de entrada padrão, e a função fgets pode ser usada para controlar a entrada.
Escrevendo em arquivos
Os dados podem ser gravados no arquivo usando fputc e fprintf. O exemplo a seguir usa as funções fgetc e fputc para fazer uma cópia de um arquivo de texto.
#incluir <stdio.h>
int principal()
{
ARQUIVO *entrada, *saída;
chave int;
se ((em = fopen("tarun.txt", "r")) == NULL)
{
puts("Não foi possível abrir o arquivo");
retornar 0;
}
out = fopen("cópia.txt", "w");
enquanto (!feof(in))
{
chave = fgetc(in);
se (!feof(em))
fputc(chave, saída);
}
fclose(em);
fclose(fora);
retornar 0;
}
A função fprintf pode ser usada para gravar dados formatados em um arquivo.
fprintf(out, "Data: %02d/%02d/%02d\n",
dia, mês, ano);
Argumentos de linha de comando com C
A definição ANSI C para declarar a função main( ) é:
int main() ou int main(int argc, char **argv)
A segunda versão permite que argumentos sejam passados da linha de comando. O parâmetro argc é um contador de argumentos e contém o número de parâmetros passados da linha de comando. O parâmetro argv é o vetor de argumentos que é um array de ponteiros para strings que representam os parâmetros reais passados.
O exemplo a seguir permite que qualquer número de argumentos seja passado da linha de comando e os imprime. argv[0] é o programa real. O programa deve ser executado a partir de um prompt de comando.
#incluir <stdio.h>
int principal(int argc, char **argv)
{
contador int;
puts("Os argumentos para o programa são:");
para (contador=0; contador<argc; contador++)
puts(argv[contador]);
retornar 0;
}
Se o nome do programa fosse count.c, ele poderia ser chamado da seguinte maneira na linha de comando.
conte 3
ou
conte 7
ou
conte 192 etc.
O próximo exemplo usa as rotinas de manipulação de arquivo para copiar um arquivo de texto para um novo arquivo. Por exemplo, o argumento da linha de comando poderia ser chamado como:
txtcpy um.txt dois.txt
#incluir <stdio.h>
int principal(int argc, char **argv)
{
ARQUIVO *entrada, *saída;
chave int;
se (argc < 3)
{
puts("Uso: txtcpy origem destino\n");
puts("A fonte deve ser um arquivo existente");
puts("Se o arquivo de destino existir, ele será
sobrescrito");
retornar 0;
}
se ((em = fopen(argv[1], "r")) == NULL)
{
puts("Não foi possível abrir o arquivo a ser copiado");
retornar 0;
}
se ((fora = fopen(argv[2], "w")) == NULL)
{
puts("Não foi possível abrir o arquivo de saída");
retornar 0;
}
enquanto (!feof(in))
{
chave = fgetc(in);
se (!feof(em))
fputc(chave, saída);
}
fclose(em);
fclose(fora);
retornar 0;
}
Manipuladores Bitwise
Em um nível de hardware, os dados são representados como números binários. A representação binária do número 59 é 111011. O bit 0 é o bit menos significativo e, neste caso, o bit 5 é o bit mais significativo.
Cada conjunto de bits é calculado como 2 elevado à potência do conjunto de bits. Operadores bitwise permitem que você manipule variáveis inteiras no nível de bit. O seguinte mostra a representação binária do número 59.
representação binária do número 59 |
bit 5 4 3 2 1 0 |
2 potência n 32 16 8 4 2 1 |
conjunto 1 1 1 0 1 1 |
Com três bits, é possível representar os números de 0 a 7. A tabela a seguir mostra os números de 0 a 7 em sua forma binária.
Dígitos binários |
000 |
0 |
001 |
1 |
010 |
2 |
011 |
3 |
100 |
4 |
101 |
5 |
110 |
6 |
111 |
7 |
A tabela a seguir lista os operadores bit a bit que podem ser usados para manipular números binários.
Dígitos binários |
& |
E bit a bit |
| |
OU bit a bit |
^ |
OU exclusivo bit a bit |
~ |
Complemento bit a bit |
<< |
Deslocamento bit a bit para a esquerda |
>> |
Deslocamento bit a bit para a direita |
E bit a bit
O bitwise AND é True somente se ambos os bits estiverem definidos. O exemplo a seguir mostra o resultado de um bitwise AND nos números 23 e 12.
10111 (23)
01100 (12) E
____________________
00100 (resultado = 4) |
Você pode usar um valor de máscara para verificar se certos bits foram definidos. Se quiséssemos verificar se os bits 1 e 3 foram definidos, poderíamos mascarar o número com 10 (o valor dos bits 1 e 3) e testar o resultado contra a máscara.
#incluir <stdio.h>
int principal()
{
int num, máscara = 10;
printf("Digite um número: ");
scanf("%d", &num);
se ((num & máscara) == máscara)
puts("Bits 1 e 3 estão definidos");
outro
puts("Os bits 1 e 3 não estão definidos");
retornar 0;
}
OU bit a bit
O bitwise OR é verdadeiro se qualquer um dos bits estiver definido. O seguinte mostra o resultado de um bitwise OR nos números 23 e 12.
10111 (23)
01100 (12) OU
_____________________________________
11111 (resultado = 31) |
Você pode usar uma máscara para garantir que um bit ou bits foram definidos. O exemplo a seguir garante que o bit 2 esteja definido.
#incluir <stdio.h>
int principal()
{
int num, máscara = 4;
printf("Digite um número: ");
scanf("%d", &num);
num |= máscara;
printf("Após garantir que o bit 2 esteja definido: %d\n", num);
retornar 0;
}
OU exclusivo bit a bit
O bitwise Exclusive OR é True se qualquer um dos bits estiver definido, mas não ambos. O seguinte mostra o resultado de um bitwise Exclusive OR nos números 23 e 12.
10111 (23)
01100 (12) OU exclusivo (XOR)
_____________________________
11011 (resultado = 27) |
O OU Exclusivo tem algumas propriedades interessantes. Se você fizer um OU Exclusivo de um número por si só, ele se definirá como zero, pois os zeros permanecerão zero e os uns não podem ser definidos, então são definidos como zero.
Como resultado disso, se você fizer um OR exclusivo de um número com outro número, então fizer um OR exclusivo do resultado com o outro número novamente, o resultado será o número original. Você pode tentar isso com os números usados no exemplo acima.
23 OU 12 = 27
27 OU 12 = 23
27 OU 23 = 12
Este recurso pode ser usado para criptografia. O programa a seguir usa uma chave de criptografia de 23 para ilustrar a propriedade em um número inserido pelo usuário.
#incluir <stdio.h>
int principal()
{
int num, chave = 23;
printf("Digite um número: ");
scanf("%d", &num);
num ^= chave;
printf("OU exclusivo com %d resulta em %d\n", key, num);
num ^= chave;
printf("OU exclusivo com %d resulta em %d\n", key, num);
retornar 0;
}
Complemento Bitwise
O bitwise Compliment é um operador de complemento de um que alterna o bit ligado ou desligado. Se for 1, será definido como 0, se for 0, será definido como 1.
#incluir <stdio.h>
int principal()
{
int num = 0xFFFF;
printf("O complemento de %X é %X\n", num, ~num);
retornar 0;
}
Deslocamento bit a bit para a esquerda
O operador Bitwise Shift Left desloca o número para a esquerda. Os bits mais significativos são perdidos à medida que o número se move para a esquerda, e os bits menos significativos vagos são zero. O seguinte mostra a representação binária de 43.
0101011 (decimal 43)
Ao deslocar os bits para a esquerda, perdemos o bit mais significativo (nesse caso, um zero), e o número é preenchido com um zero no bit menos significativo. O seguinte é o número resultante.
1010110 (decimal 86)
Deslocamento bit a bit para a direita
O operador Bitwise Shift Right desloca o número para a direita. Zero é introduzido nos bits mais significativos desocupados, e os bits menos significativos desocupados são perdidos. O seguinte mostra a representação binária do número 43.
0101011 (decimal 43)
Ao deslocar os bits para a direita, perdemos o bit menos significativo (nesse caso, um), e o número é preenchido com um zero no bit mais significativo. O seguinte é o número resultante.
0010101 (decimal 21)
O programa a seguir usa o Bitwise Shift Right e o Bitwise AND para exibir um número como um número binário de 16 bits. O número é deslocado para a direita sucessivamente de 16 para zero e o Bitwise ANDed com 1 para ver se o bit está definido. Um método alternativo seria usar máscaras sucessivas com o operador Bitwise OR.
#incluir <stdio.h>
int principal()
{
int contador, num;
printf("Digite um número: ");
scanf("%d", &num);
printf("%d é binário: ", num);
para (contador=15; contador>=0; contador--)
printf("%d", (num >> contador) & 1);
coloque('\n');
retornar 0;
}
Funções para conversões binárias e decimais
As duas funções fornecidas a seguir são para conversão de binário para decimal e de decimal para binário. A função fornecida a seguir para converter um número decimal para o número binário correspondente suporta até 32 – Número binário de bits. Você pode usar isso ou o programa fornecido antes para conversão conforme suas necessidades.
Função para conversão de decimal para binário:
void Decimal_para_Binário(void)
{
int entrada =0;
int eu;
contagem int = 0;
int binário [32]; /* 32 bits, MÁXIMO 32 elementos */
printf ("Digite o número decimal para converter em
Binário :");
scanf ("%d", &entrada);
fazer
{
i = input%2; /* MOD 2 para obter 1 ou 0*/
binary[count] = i; /* Carregar elementos no array binário */
input = input/2; /* Dividir input por 2 para decrementar via binário */
count++; /* Conta quantos elementos são necessários*/
}enquanto (entrada > 0);
/* Reverter e emitir dígitos binários */
printf ("A representação binária é: ");
fazer
{
printf ("%d", binário[contagem - 1]);
contar--;
} enquanto (contagem > 0);
printf ("\n");
}
Função para conversão de binário para decimal:
A função a seguir é converter qualquer número binário em seu número decimal correspondente:
void Binário_para_Decimal(void)
{
char bináriohold[512];
char *binário;
int i=0;
int dec = 0;
int z;
printf ("Por favor, digite os dígitos binários.\n");
printf ("Dígitos binários são apenas 0 ou 1");
printf ("Entrada binária: ");
binário = obtém(bináriomanter);
i=strlen(binário);
para (z=0; z<i; ++z)
{
dec=dec*2+(binary[z]=='1'? 1:0); /* se Binary[z] for
igual a 1,
então 1 senão 0 */
}
printf ("\n");
printf ("O valor decimal de %s é %d",
binário, dec);
printf ("\n");
}
Depuração e Teste
Erros de sintaxe
Sintaxe se refere à gramática, estrutura e ordem dos elementos em uma declaração. Um erro de sintaxe ocorre quando quebramos as regras, como esquecer de terminar uma declaração com um ponto e vírgula. Quando você compila o programa, o compilador produzirá uma lista de quaisquer erros de sintaxe que ele possa encontrar.
Um bom compilador exibirá a lista com uma descrição do erro e pode fornecer uma possível solução. Corrigir os erros pode resultar em mais erros sendo exibidos quando recompilado. A razão para isso é que os erros anteriores alteraram a estrutura do programa, o que significa que mais erros foram suprimidos durante a compilação original.
Da mesma forma, um único erro pode resultar em vários erros. Tente colocar um ponto e vírgula no final da função principal de um programa que compila e executa corretamente. Quando você recompila, você obterá uma lista enorme de erros, e ainda assim é apenas um ponto e vírgula mal colocado.
Assim como erros de sintaxe, compiladores também podem emitir avisos. Um aviso não é um erro, mas pode causar problemas durante a execução do seu programa. Por exemplo, atribuir um número de ponto flutuante de precisão dupla a um número de ponto flutuante de precisão simples pode resultar em perda de precisão. Não é um erro de sintaxe, mas pode levar a problemas. Neste exemplo em particular, você pode mostrar intenção ao converter a variável para o tipo de dados apropriado.
Considere o exemplo a seguir, onde x é um número de ponto flutuante de precisão simples e y é um número de ponto flutuante de precisão dupla. y é explicitamente convertido para um float durante a atribuição, o que eliminaria quaisquer avisos do compilador.
x = (flutuante)y;
Erros de lógica
Erros de lógica ocorrem quando há um erro na lógica. Por exemplo, você pode testar se um número é menor que 4 e maior que 8. Isso nunca poderia ser verdade, mas se estiver sintaticamente correto, o programa será compilado com sucesso. Considere o seguinte exemplo:
se (x < 4 && x > 8)
puts("Isso nunca vai acontecer!");
A sintaxe está correta, então o programa será compilado, mas a instrução puts nunca será impressa, pois o valor de x não poderia ser menor que quatro e maior que oito ao mesmo tempo.
A maioria dos erros de lógica são descobertos por meio do teste inicial do programa. Quando ele não se comporta como você esperava, você inspeciona as declarações lógicas mais de perto e as corrige. Isso só é verdade para erros lógicos óbvios. Quanto maior o programa, mais caminhos haverá através dele, mais difícil se torna verificar se o programa se comporta como esperado.
Testando
No processo de desenvolvimento de software, erros podem ser injetados em qualquer estágio durante o desenvolvimento. Isso ocorre porque os métodos de verificação das fases anteriores do desenvolvimento de software são manuais. Portanto, o código desenvolvido durante a atividade de codificação provavelmente terá alguns erros de requisitos e erros de design, além de erros introduzidos durante a atividade de codificação. Durante o teste, o programa a ser testado é executado com um conjunto de casos de teste, e a saída do programa para os casos de teste é avaliada para determinar se a programação está executando o esperado.
Assim, teste é o processo de analisar um item de software para detectar a diferença entre condições existentes e necessárias (ou seja, bugs) e avaliar os recursos dos itens de software. Então, teste é o processo de analisar um programa com a intenção de encontrar erros.
Alguns princípios de teste
- Os testes não podem mostrar a ausência de defeitos, apenas sua presença.
- Quanto mais cedo um erro for cometido, mais custoso ele será.
- Quanto mais tarde um erro for detectado, mais caro ele será.
Agora vamos discutir algumas técnicas de teste:
Teste de caixa branca
O teste de caixa branca é uma técnica pela qual todos os caminhos através do programa são testados com todos os valores possíveis. Essa abordagem requer algum conhecimento de como o programa deve se comportar. Por exemplo, se seu programa aceitasse um valor inteiro entre 1 e 50, um teste de caixa branca testaria o programa com todos os 50 valores para garantir que ele estivesse correto para cada um e, em seguida, testaria todos os outros valores possíveis que um inteiro pode assumir e testaria se ele se comportou conforme o esperado. Considerando o número de itens de dados que um programa típico pode ter, as permutações possíveis tornam o teste de caixa branca extremamente difícil para programas grandes.
O teste de caixa branca pode ser aplicado a funções críticas de segurança de um programa grande, e muito do restante testado usando o teste de caixa preta, discutido abaixo. Devido ao número de permutações, o teste de caixa branca é geralmente realizado usando um test harness, onde intervalos de valores são alimentados ao programa rapidamente por meio de um programa especial, registrando exceções ao comportamento esperado. O teste de caixa branca é algumas vezes chamado de teste de caixa estrutural, claro ou aberto.
Teste de caixa preta
O teste de caixa preta é semelhante ao teste de caixa branca, exceto que em vez de testar todos os valores possíveis, valores selecionados são testados. Nesse tipo de teste, o testador conhece as entradas e quais devem ser os resultados esperados, mas não necessariamente como o programa chegou a eles. O teste de caixa preta às vezes é chamado de teste funcional.
Os casos de teste para testes de caixa preta geralmente são desenvolvidos imediatamente após a conclusão das especificações do programa. Os casos de teste são baseados em classes de equivalência.
Classes de equivalência
Para cada entrada, uma classe de equivalência define estados válidos e inválidos. Normalmente, há três cenários a serem planejados ao definir classes de equivalência.
Se os dados de entrada especificarem um intervalo ou um valor específico, um estado válido e dois estados inválidos serão definidos. Por exemplo, se um número deve estar entre 1 e 20, o estado válido estará entre 1 e 20, haverá um estado inválido para valores menores que 1 e um estado inválido maiores que 20.
Se os dados de entrada excluírem um intervalo ou um valor específico, dois estados válidos e um estado inválido serão definidos. Por exemplo, se um número não deve estar entre 1 e 20, os estados válidos seriam menores que 1 e maiores que 20, e os estados inválidos estariam entre 1 e 20.
Se a entrada especificar um valor booleano, haverá apenas dois estados: um válido e um inválido.
Análise de valor limite
A análise de valor limite considera apenas os valores no limite dos dados de entrada. Por exemplo, no caso de um número de 1 a 20, os casos de teste podem ser 1, 20, 0 e 21. A ideia é que se o programa funcionar como esperado com esses valores, outros valores também funcionarão como esperado.
A tabela a seguir fornece uma visão geral dos limites típicos que você pode querer definir.
Intervalos de teste |
Tipo de entrada |
Valores de teste |
Faixa |
- x[limite_inferior]-1
- x[limite_inferior]
- x[limite_superior]
- x[limite_superior]+1
|
Booleano |
|
Desenvolvendo um plano de teste
Defina classes de equivalência e determine os limites para cada classe. Depois de definir os limites da classe, escreva uma lista de valores aceitáveis e inaceitáveis no limite e qual deve ser o comportamento esperado. O testador pode então executar o programa com os valores limite e indicar o que aconteceu quando o valor limite foi testado em relação ao resultado necessário.
Abaixo está um plano de teste típico usado para validar idades de entrada, onde valores aceitáveis variam de 10 a 110.
Classe de equivalência |
Válido |
Incorreto |
Entre 10 e 110 |
> 110 |
|
< 10 |
Tendo determinado nossa classe de equivalência, agora podemos desenvolver um plano de teste para idade.
Plano de teste |
Valor |
Estado |
Resultado esperado |
Resultado real |
10 |
Válido |
Continue executando para obter o nome |
|
110 |
Válido |
Continue executando para obter o nome |
|
9 |
Incorreto |
Pergunte a idade novamente |
|
111 |
Incorreto |
Pergunte a idade novamente |
|
A coluna "Resultado real" é deixada em branco, pois será preenchida durante o teste. Se o resultado for o esperado, a coluna será verificada. Caso contrário, você deve inserir um comentário indicando o que aconteceu.