Baixe o app para aproveitar ainda mais
Prévia do material em texto
Carlos Cesar Ap. Eguti João Marcos S. Sakamoto Ilha Solteira, 2005 1 Curso Básico de Linguagem C Carlos César Aparecido Eguti e João Marcos Salvi Sakamoto Sumário Breve História ....................................................................................................................................3 Características Importantes.............................................................................................................3 Estrutura de um Programa em C....................................................................................................4 O pré-processador ........................................................................................................................4 O compilador ..................................................................................................................................4 O assembler ...................................................................................................................................5 O linker ............................................................................................................................................5 O código fonte de um programa em C ..........................................................................................5 Comandos do pré-processador ......................................................................................................6 #include...........................................................................................................................................6 #define ............................................................................................................................................6 Definições de tipos de dados ..........................................................................................................7 Literais.............................................................................................................................................8 Caracteres de Escape ..................................................................................................................8 Declaração de Variáveis, Vetores e Matrizes...............................................................................8 Definição de variáveis globais.....................................................................................................9 Operações aritméticas....................................................................................................................11 Operadores de comparação ou relacionais ................................................................................13 Operadores lógicos.........................................................................................................................14 Operadores bit a bit ........................................................................................................................14 Protótipo de funções .......................................................................................................................15 Funções ............................................................................................................................................15 Função sem retorno ou função void.........................................................................................16 Funções de arrays ......................................................................................................................16 Funções básicas de entrada e saída ...........................................................................................17 printf( )...........................................................................................................................................17 Especificadores de Formato..................................................................................................17 getche().........................................................................................................................................18 2 getch() ...........................................................................................................................................19 gets() .............................................................................................................................................19 scanf() ...........................................................................................................................................19 Execução condicional ou controle de fluxo .................................................................................20 if - else..........................................................................................................................................20 Operador ternário ........................................................................................................................20 while...............................................................................................................................................21 do while .........................................................................................................................................22 switch case ...................................................................................................................................22 for ...................................................................................................................................................23 Nota sobre o comando goto ..........................................................................................................24 Estruturas .........................................................................................................................................24 Noções de ponteiros em Linguagem C .......................................................................................25 Operadores de ponteiros ...........................................................................................................25 Aritmética com ponteiros............................................................................................................26 Ponteiros para ponteiros ............................................................................................................27 Problemas com ponteiros ..........................................................................................................27 Noções de operações com pilhas ................................................................................................28 Acessando arquivos externos .......................................................................................................29 Noções de Streams ....................................................................................................................29 Arquivos de dados ......................................................................................................................30 fopen..............................................................................................................................................30 fclose .............................................................................................................................................31 fprintf..............................................................................................................................................31 fscanf.............................................................................................................................................31 fgetc ...............................................................................................................................................31fptuc ...............................................................................................................................................31 rewind ............................................................................................................................................32 foef.................................................................................................................................................32 ferror ..............................................................................................................................................32 Troca de dados com outros periféricos .......................................................................................33 Bibliografia........................................................................................................................................34 3 Curso Básico de Linguagem C Carlos César Aparecido Eguti e João Marcos Salvi Sakamoto Breve História A linguagem C foi desenvolvida no Bell Laboratories em 1972 por Dennis Ritchie. Muitos de seus princípios e idéias foram aperfeiçoados da linguagem B e suas antecessoras como a BCPL – (Combined Programming Language). Sua finalidade era de criar uma linguagem de alto nível para ser usada em qualquer tipo de máquina, independente do sistema operacional utilizado e que permitisse ainda o controle do programa em níveis mais baixos, como por exemplo, o acesso de bits individuais dentro da memória do processador. O poder e a flexibilidade do C tornaram-se logo aparentes e devido a isso, o sistema operando Unix que foi escrito originalmente em Assembly, foi reescrito quase imediatamente em C. Dennis Ritchie Durante a década de 70, a Linguagem C propagou-se em muitas faculdades e universidades do EUA, uma vez que o sistema operacional Unix tornou-se um código livre em centros acadêmicos e, desta forma, muitas organizações diferentes começaram a usar suas próprias versões de C o que causou problemas de compatibilidade. Em 1983, o American National Standards Institute (ANSI) criou um comitê para estabelecer uma definição padrão do C que fico conhecida como ANSI C ou C padrão. A história completa pode ser obtida diretamente do site do Ritchie, no seguinte endereço: http://cm.bell-labs.com/cm/cs/who/dmr/chist.html. Características Importantes O C é uma linguagem poderosa, flexível que permite uma rápida execução de programa com poucas restrições de programação. Permite o acesso a níveis mais baixo de informação e comandos, ou seja, pode manipular bits de dados nos barramento de I/O e acessar códigos internos do processador e outros periféricos. Possui elevada portabilidade, podendo ser utilizada em diversos tipos de sistemas, apresentando uma estrutura de linguagem muito sofisticada e moderna. Estas qualidades fazem-lhe uma língua útil para a criação tanto de outras linguagens, quanto dos mais diversos tipos de programas, inclusive bancos de dados, programas gráficos e etc. A manipulação de ponteiros e o acesso a bits de dados diretamente da pilha do processador é uma vantagem poderosa para programadores experientes, porém um artifício perigoso para os novatos e não será tratada neste curso. Um outro ponto forte do C é a sua modularidade. As seções do código podem ser armazenadas em bibliotecas para serem utilizadas em diferentes programas. Este conceito de modularidade ajuda também com a portabilidade do C e na sua velocidade de execução. 4 Estrutura de um Programa em C Durante a criação de um programa em C, ocorre o seguinte esquema de compilação. O pré-processador O pré-processamento é a primeira fase da compilação de um programa em C. Ele modifica alguns pontos do arquivo e trabalha apenas com texto. As principais vantagens da utilização do pré-processador é a possibilidade de tornar os programas: · mais fáceis de desenvolver; · mais fáceis de ler e interpretar; · mais fáceis de modificar; · mais portáveis entre arquiteturas e máquinas diferentes. Suas funções principais são: · verificar a sintaxe do código fonte; · remover os comentários do programa; · interpretar directiva especiais a si dirigidas, que começam pelo caractere #. O compilador Alguns compiladores traduzem o código fonte (texto) recebido do pré-processador para linguagem assembly (também texto). No entanto são também comuns os 5 compiladores capazes de gerarem diretamente código objeto (instruções do processador já em código binário). O assembler O assembler traduz código em linguagem assembly (texto) para código objeto. Pode estar integrado ao compilador. O código objeto é geralmente armazenado em arquivos com a extensão .o (unix) ou .obj (ms-dos). O linker Se o programa referencia funções da biblioteca standard ou outras funções contidas em arquivos com código fonte diferentes do principal (que contém a função main() ), o linker combina todos os objetos com o resultado compilado dessas funções num único arquivo com código executável. As referências a variáveis globais externas também são resolvidas pelo linker. O código fonte de um programa em C A estrutura genérica de um programa em C é a que se apresenta a seguir, podendo alguns dos elementos não existir: · Comandos do pré-processador; · Definições de tipos de dados; · Protótipo de funções; · Variáveis globais; · Funções; Deverá existir sempre uma função main(). As funções têm a seguinte estrutura: tipo nome_da_funcão (parâmetros) { variáveis locais instruções em C } Primeiro programa em C. (PROG1.C) #include <stdio.h> void main(void) { printf("Hello World\n"); } Contém apenas uma função (a função main(), que é obrigatória), que não retorna nada (void) e que não tem parâmetros (outra vez void). 6 A única instrução da função é a chamada printf(), função da biblioteca standard que escreve no vídeo. Neste caso escreve uma frase simples Hello World (string). A combinação \n no fim da string indica uma mudança de linha (o carácter new line ou line feed). Notar que no final de cada instrução existe sempre um terminador - ; Comandos do pré-processador Todas as directivas ou comandos do pré-processador começam com o caráctere #. Basicamente o pré – processador possui dois comandos importantes, sendo: #include Inclui (copia) o conteúdo do arquivo especificado para dentro programa atual. Exemplos: #include <math.h> Insere o conteúdo do arquivo math.h com a declaração das funções matemáticas da biblioteca standard. #include <stdio.h> Idem para as funções standard de entrada/saída. Usado para arquivos próprios da linguagem com extensão .h (header) que contêm declaração das funções da biblioteca padrão, definição de tipos compostos e de macros. O arquivo é procurado no diretório padrão de includes do compilador. Deve-se incluir o arquivo apropriado sempre que for utilizada alguma função própria da linguagem. Alguns dos principais .h do C são: Arquivo Descrição stdio.h Funções de entrada e saída (I/O) string.h Funções de tratamento de strings math.h Funções Matemáticas ctype.h Funções de teste e tratamento de caracteres stdlib.h Funções genéricas #define Esta diretiva é usada para definir constantes ou substituições mais poderosas denominadas macros. A sintaxe geral é a seguinte #define nome texto_de_definição Exemplos: #define FALSE 0 #define TRUE !FALSE 7 É possível definir também pequenas "funções" com parâmetros (macros) cujas "chamadas" no texto do programa são substituídas pelo texto_de_definição com os parâmetros também substituídos.Por exemplo, seria possível definir uma macro max que nos dá o máximo de duas expressões: #define max(A, B) ((A) > (B) ? (A) : (B)) Note-se que esta diretiva não define uma verdadeira função do C, mas apenas uma substituição de texto com o aspecto de uma chamada a função. Definições de tipos de dados Existem 4 tipos básicos que podem ser utilizados para a declaração de variáveis: Tipo Descrição char Armazena um único caractere ou números inteiro pequeno int Números inteiros (sem casa decimais) float Números em ponto flutuante (com casas decimais) com precisão simples double Números em ponto flutuante (com casas decimais) com precisão dupla O C tem pré-definidos os seguintes tipos de dados simples: Tipo Bytes Intervalo Casas decimais unsigned char 1 0 a 255 0 signed char 1 -128 a 127 0 unsigned short int 2 0 a 65535 0 signed short int 2 -32726 a 32767 0 unsigned long int 4 0 a 4294967295 0 signed long int 4 -2147483648 a 2147483647 0 float 4 3.4e-38 a 3.4e38 7 double 8 1.7e-308 a 1.7e308 15 long double 10 3.4e-4932 a 1.1e4932 17 void - nenhum valor - O C não tem um tipo booleano pré-definido, no entanto, poderá usar-se um char (ou melhor um unsigned char) ou um int para o efeito. O prefixo unsigned pode também ser usado com os tipos int e long int. 8 Literais Os literais são usados para especificar valores constantes dentro de programas. A linguagem C possibilita uma grande variedade na codificação destes valores, seja quanto a base numérica utilizada ou quanto ao tamanho e formato utilizado para seu armazenamento. Formato Codificação Exemplo decimal Números sem 0 inicial 10 octal Prefixo 0 010 hexadecimal Prefixo 0x 0xFF ponto flutuante Iii . ddd 3.14159265 caractere Um caractere entre ‘ ‘ ou seu código ASCII ‘A’, 65 string Sequência de caracteres entre “ “ “engenharia” Caracteres de Escape Os caracteres de escape são usados para representar alguns caracteres que, ou não estão disponíveis diretamente no teclado do computador ou em determinadas situações não podem ser inseridos diretamente dentro de um programa fonte C. Outra vantagem do uso desta forma codificada é que a portabilidade do programa é garantida. Estes caracteres podem aparecer tanto dentro de literais do tipo caractere como string. Formato Exemplo \a Campainha ou Bell \b Retrocesso ou backspace \n Alimentação de linha ou line feed \r Retorno do carro ou return carrige \t Tabulação horizontal (TAB) \ ’ Aspa simples ou plicas \ ” Aspa dupla \ \ Contrabarra \ Caractere nulo (ASCII 0 ) Declaração de Variáveis, Vetores e Matrizes Toda a variável deve ser definida antes de ser usada como sendo de um tipo. O tipo vai definir o tamanho em bytes da variável e o intervalo de valores que ela poderá 9 armazenar. O nome da variável poderá ter até 32 caracteres, sendo que o primeiro deverá ser letra ou _ (under score) e os demais caracteres podem ser letras, números ou _. Todas as variáveis devem ser declaradas antes de qualquer comando executável na função. Um programa pode ter variáveis globais, que são definidas fora de qualquer função e podem ser referenciadas em qualquer uma delas, ou variáveis locais que são declaradas internamente a uma função, sendo visível apenas na função em que foi declarada. Funções distintas podem ter variáveis locais com o mesmo nome. Se uma função declarar uma variável com o mesmo nome de uma variável global, a prioridade é da variável local. Definição de variáveis globais As variáveis globais, visíveis em todas as funções de um programa, declaram-se fora e antes de todas as funções (só são visíveis a partir do local da declaração). Por exemplo: short numero, soma; int g_numero, g_soma; char letra; void main(void) { } É também possível inicializar as variáveis globais no momento da declaração. Usa- se para isso o operador de atribuição = Por exemplo: float soma = 0.0; int g_soma = 0; char letra = 'A'; void main(void) { } Poderíamos ter feito a mesma coisa de outra forma (mais ineficiente): float soma; int g_soma; char letra; void main(void) { soma = 0.0; g_soma = 0; letra = 'A'; } 10 O C também permite múltiplas atribuições (colocar o mesmo valor em várias variáveis ao mesmo tempo): a = b = c = d = 3; O C tem uma palavra chave que permite a definição de novos tipos - typedef. Esses novos tipos declaram-se utilizando a regra: typedef tipo_já_definido novo_tipo; Um exemplo muito simples: typedef float real; typedef char caracter; real sum = 0.0; /* o mesmo que float */ caracter ch = 'A'; /* o mesmo que char */ Todos os vetores em C começam com o índice 0 e não é verificado limite para um vetor. Vetores em C são freqüentemente chamados de array. int ivetor[10]; declara um vetor chamado de ivetor de 10 elementos do tipo inteiro float fmatriz[2][5]; declara uma matriz de 2 linhas com 5 colunas tipo real chamada fmatriz Strings em C são simplesmente vetores de caracteres. Toda string deve ser finalizada pelo caracter '\0'. String entre aspas duplas ("") o C coloca automaticamente \0 no final. Vetores também podem ser inicializados na declaração. Neste caso a declaração do tamanho é opcional, pois o compilador pode calcular. Para a inicialização de vetores, se coloca os valores entre { }. Para strings usa-se um literal entre " ". Exemplo. (PROG2.C) #include<stdio.h> main() { int vetor[5]={2,4,6,8,10}; int matriz[3][3]={{2,15,0},{33,4,900},{-1,12,50}}; char nome[]={'C','O','M','P','U','T','A','C','A','O','\0'}; char texto[10]="exemplo"; printf("%s\n",vetor[3]); printf("%d\n",matriz[2][0]); printf("%s\n",nome); printf("%s\n",texto); } 11 Operações aritméticas O C suporta as quatro operações aritméticas standard nas suas expressões que são representadas pelos símbolos habituais (+, -, *, /). Além destas operações aritméticas são suportadas outras. A atribuição é representada no C pelo operador =. (No C a atribuição é considerada uma operação aritmética). Exemplos: i = 4; ch = 'y'; Outros operadores aritméticos são os operadores de incremento e decremento, representados respectivamente por ++ e --. Em geral geram código mais eficiente do que a soma ou subtração de 1 unidade. Assim a atribuição: x = x + 1; pode ser substituída simplesmente por x++; Operador Função = Atribuição + Soma - Subtração * Multiplicação / Divisão (se valores inteiros, resultado sem casas decimais) % Resto da divisão + + Incremento pré ou pós-fixado - - Decremento pré ou pós-fixado Os operadores ++ e -- podem ser pósfixos (colocam-se após a variável a incrementar ou decrementar) ou podem ser préfixos (colocando-se antes da variável a incrementar ou decrementar. Quando o operador é pósfixo a expressão x++ tem como valor o conteúdo de x antes deste ser incrementado, enquanto que com o operador préfixo a expressão ++x representa já o valor de x depois de incrementado (em ambos os casos x é incrementado, o que muda é o valor que a combinação x++ ou ++x representa como conjunto). Vejamos o exemplo: (PROG3.C) 12 #include<stdio.h> #include<conio.h> main() { int n,x; clrscr(); n=0; x=n++; puts("x=n++;"); printf("x=%d, n=%d\n",x,n); n=0; x=++n; puts("x=++n;"); printf("x=%d, n=%d\n",x,n); } Um outro operador aritmético do C é o operador módulo %. Tem como resultado o resto da divisão inteira. Só pode ser utilizado com valores inteiros. (Ver osexemplos anteriores). O operador de divisão /, pode ser usado com inteiros e reais. No entanto, se ambos os operandos forem inteiros o resultado é a divisão inteira. Assim a atribuição: x = 3 / 2; coloca em x o valor 1, mesmo que x seja float ou double. Se quisermos colocar em x o resultado correto da divisão real (se x for float ou double) deveremos escrever: x = 3.0 / 2; ou x = 3 / 2.0; ou melhor ainda x = 3.0 / 2.0; (sem conversões de um inteiro para real). O C suporta também alguns modos expeditos de escrever atribuições. É comum termos de escrever nos programas expressões como i = i + 3; ou x = x * (y + 2); Expressões deste tipo podem ser abreviadas para qualquer coisa como variável op= expressão; e que é equivalente a variável = variável op (expressão); 13 Assim podemos reescrever as duas expressões anteriores como i += 3; e x *= y + 2; Note-se que esta última expressão vale sempre x = x * (y + 2); e nunca x = x * y + 2; A Tabela abaixo resume: Expressão normal Expressão simplificada a = a + b a + = b a = a – b a - = b a = a * b a * = b a = a / b a / = b Programa exemplo: (PROG4.C) #include <stdio.h> #include<conio.h> /* conversao de pes para metros */ main() { int pes; float metros; printf("Informe o valor em pes: "); scanf("%d", &pes); metros = pes * 0.3048; printf("%d pes = %f metros\n", pes, metros); getch(); } Operadores de comparação ou relacionais O operador de teste de igualdade no C é ==. Note que é muito fácil trocar o teste de igualdade pelo operador de atribuição (só um =), o que conduz invariavelmente a erros difíceis de detectar. A seguinte instrução condicional está sintaticamente correta: 14 if (i = j) ... ,no entanto o que faz é copiar o valor de j para i e tem como resultado o valor de j, que é interpretado como TRUE se for diferente de 0 e FALSE no caso contrário. O que se pretendia era comparar i com j com resultado TRUE se fossem iguais... O operador de teste de desigualdade é em C !=. Os operadores de comparação são mostrados na Tabela abaixo: Operador Função > maior que > = maior ou igual a < menor que < = menor ou igual a = = igual a ! = diferente de Operadores lógicos Os operadores lógicos servem para combinar resultados de comparações e são geralmente utilizados nas instruções condicionais. Os três operadores lógicos do C são: Operador Função & & AND lógico || OR lógico ! NOT lógico Operadores bit a bit Os operadores bit a bit realizam operações matemáticas booleanas. Podem ser aplicado para qualquer formato contudo, seu entendimento conceitual ocorre somente em operações com números binários. Operador Função & AND | OR ^ XOR (OR exclusivo) ~ Complemento de 1 << Deslocamento a esquerda >> Deslocamento a direita 15 Protótipo de funções Em ANSI C, antes de se poder chamar uma função é necessário declarar ao compilador o seu tipo de retorno, o número e natureza dos seus argumentos. Se a função que se chama estiver definida antes da chamada isso é suficiente. No caso contrário é necessário declarar, a nível global e antes da chamada, um protótipo da função. Um protótipo não é mais do que o cabeçalho da função seguido de ;. Estritamente não é necessário indicar no protótipo os nomes dos parâmetros, mas apenas os seus tipos. Embora não haja exigências quanto ao local de declaração dos protótipos (contanto que seja antes das chamadas) é de toda a conveniência agrupar os protótipos de todas as funções do programa (com a exceção de main) perto do seu início. Isso tornará a leitura e interpretação do programa mais fácil. Exemplo de protótipo: int exemplo(char); O protótipo acima indica que a função exemplo() retorna um inteiro e aceita um parâmetro que é um array de caracteres (string). Funções Unidade autônoma de código do programa que é utilizada para cumprir uma tarefa particular. Um programa em linguagem C é, como já se disse, essencialmente uma coleção de funções. O próprio programa principal em C é uma função - a função main(). Os parâmetros de recepção de valores devem ser separados por vírgulas. Sintaxe: tipo nome(parâmetros); { comandos } Exemplo: (PROG5.C) #include <stdio.h> #include <conio.h> float converte(float); /* prototipo da funcao */ void main(void) /* programa principal */ { float fahr; float resultado; /* declaracao das variaveis */ clrscr(); 16 printf("Digite a temperatura em Fahrenheit:"); scanf("%f", &fahr); resultado = converte(fahr); printf("%f Fahrenheit = %f Celsius\n",fahr, resultado); getch(); /* aguarda um toque no teclado para terminar */ } float converte(float x) /* subrotina do programa */ { float celsius; /* declaração de variaveis locais */ celsius=5*(x-32)/9; return(celsius); } Função sem retorno ou função void Quando uma função não retorna um valor para a função que a chamou ela é declarada como void. Na chamada de funções sem argumentos deve-se utilizar ( ) ou (void). Ao final da sub-rotina, é necessário colocar o comando return, caso a função não seja void. Funções de arrays É possível passar arrays como argumentos de funções. No entanto essa passagem tem um comportamento diferente dos outros tipos de variáveis. Quando se passa qualquer outro tipo de variável como parâmetro de uma função, é inicialmente criada uma cópia dessa variável e a função atua sobre essa cópia sem poder modificar a variável original que foi passada (chama-se a esse mecanismo passagem por valor). Quando se passa um array não se cria uma cópia; em vez disso é passado o endereço do primeiro elemento do array de forma automática e transparente para o utilizador. A conseqüência dessa forma de passagem é que os elementos do array que forem modificados na função mantêm essas modificações, mesmo depois da função terminar. Vetores e/ou matrizes consistem em posições contíguas de memória, sendo que o endereço mais baixo corresponde ao primeiro elemento e o endereço mais alto ao último elemento. No cabeçalho da função diz-se que o segundo argumento é um array de float's. Note-se que não é preciso especificar a dimensão quando da passagem como parâmetro. Compete ao programador assegurar de forma absoluta que nunca é acessado um elemento fora da real dimensão do array. Na passagem de arrays multidimensionais é necessários indicar todas as dimensões com exceção da primeira. Exemplo: (PROG6.C) 17 #include<stdio.h> void exibe_2d_matriz(int matriz[][10], int linhas) { int i,j; for (i=0;i<linhas;i++) for(j=0;j<10;j++) printf("Matriz[%d][%d] = %d\n",i,j,matriz[i][j]); } void main(void) { int a[1][10]={{1,2,3,4,5,6,7,8,9,10}}; int b[2][10]={{1,2,3,4,5,6,7,8,9,10},{11,12,13,14,15,16,17,18,19,20}}; int c[3][10]={{1,2,3,4,5,6,7,8,9,10},{11,12,13,14,15,16,17,18,19,20},{21,22,23,24, 25,26, 27,28,29,30}}; exibe_2d_matriz (a,1); exibe_2d_matriz (b,2); exibe_2d_matriz (c,3); } Funções básicas de entrada e saída printf( ) A função printf é uma função da biblioteca (stdio.h) que escreve valores no dispositivo padrão (tela). A expressão de controle pode conter caracteres que serão exibidos na tela e os códigos de formatação que indicam o formato em que os argumentos devem ser impressos. Cada argumento deve ser separado por vírgula, podendo receber diversos parâmetros sendo que o primeiro deverá ser uma string. Cada especificador de formato encontradonesta string é substituído pelo parâmetro seguinte na lista de argumentos. Tudo o que aparecer nesta string que não for um especificador de formato será impresso literalmente. Especificadores de Formato Pode-se especificar o tamanho do campo a ser impresso, colocando um número entre o % e o caractere de formato. Por exemplo %5d indica que o valor a ser impresso vai ocupa 5 posições na tela. As posições em excesso são preenchidas com brancos. Por padrão o valor é alinhado a direita do campo. Se o tamanho do campo for precedido por - (menos), então é alinhado à esquerda. Se for colocado um 0 em frente ao tamanho o campo é preenchido com zeros. Sempre que o tamanho do campo for insuficiente para exibir o valor solicitado, o tamanho é ignorado. Assim um valor nunca é truncado. 18 Formato descrição %d Número decimal %ld Numero decimal longo %o Notação octal sem sinal %x Hexadecimal sem sinal %u Decimal sem sinal %c Um único caractere %s String %f Ponto flutuante %% Imprime um % Nota: A tabela ASCII possui 256 códigos de 0 a 255, se imprimirmos em formato caractere um número maior que 255, será impresso o resto da divisão do número por 256; se o número for 3393 será impresso A pois o resto de 3393 por 256 é 65. Uma Tabela ASCII está em anexo ao curso. Exemplo: (PROG7.C) #include <stdio.h> #include <conio.h> void main(void) { char val_c = 'A'; /* define uma variável caractere com a letra A */ int val_i = 100; /* variável inteira com valor 100 */ double val_d = 50.5; /* variável double com valor 50.5 */ char frase[ ]="COMPUTACAO"; /* variable caractere com a frase computacao */ clrscr(); /*limpa a tela */ printf("[%c] [%c] [%d]\n",val_c, val_c+1 ,val_c); printf("[%d] [%5d] [%-5d] [%05d]\n",val_i, val_i, val_i, val_i); printf("[%5.2f]\n",val_d); printf("[%15s]\n", frase); printf("[%-15s]\n", frase); printf("[%3.4s]\n", frase); printf("[%15.3s]\n", frase); printf("[%-15.3s]\n", frase); } getche() Usada para a entrada de variáveis char, lê um caractere e exibe na tela o caractere digitado. Está definida na biblioteca (conio.h). 19 getch() Usada para a entrada de variáveis char, lê um caractere e não exibe na tela o caractere digitado (entrada invisível). Está definida em (conio.h) gets() Usada para a entrada de string. Está definida em stdio.h scanf() Deve ser utilizada para a entrada de variáveis numéricas (int, float e double). O modo de utilizar é semelhante à função printf, porém colocar obrigatoriamente o símbolo & em frente ao nome da variável sendo lida. Está definida em (stdio.h) Com a função scanf deve-se utilizar os seguintes especificadores de formato: Formato Tipo %d int %ld long int %u unsigned int %f Float %lf double Exemplo: (PROG8.C) #include <stdio.h> #include <conio.h> void main(void) { char varc; int vari; float varf; char nome[80]; clrscr(); printf("Digite seu nome: "); gets(nome); printf("Digite um numero inteiro: "); scanf("%d", &vari); printf("Digite um numero real: "); scanf("%f", &varf); printf("Digite um caracter: "); varc = getche(); printf("\n%s, voce digitou %d, %f e %c", nome, vari, varf, varc); getch(); } 20 Há inúmeras outras funções de manipulação de char complementares às que foram apresentadas. Maiores detalhes, consulte um manual de referência em anexo a este curso. Execução condicional ou controle de fluxo Os comandos de controle de fluxo são a essência de qualquer linguagem porque governam o fluxo da execução do programa. São ferramentas importantes tanto para tomada de decisão quanto a estruturar de execução do programa. Os comandos condicionais utilizam os operadores relacionais e lógicos já apresentados, além de combinações destes para sua execução. if - else Sintaxe: If (condição) { primeiro grupo de comandos; } else { segundo grupo de comandos; } Se condição for verdadeira (diferente de 0) são executados o primeiro grupo de comandos, caso contrários são executados o segundo grupo de comandos. Caso houver somente um único comando para ser executado, as chaves { } são dispensáveis. O comando else é opcional. Pode ser facilmente imbricado para o teste de múltiplas condições, como se vê no exemplo seguinte: Sintaxe: if (condição) instrução_1; else if (expressão) instrução_2; else instrução_3; Operador ternário É uma maneira compacta de expressar if-else. Sintaxe: Condição ? expressão1: expressão2 21 Exemplo: (PROG9.C) main() { int x, y, max; printf("Entre com dois números: "); scanf(“%d %d”,&x,&y); max=(x>y)?1:0; printf("max= %d\n",max); } Exemplo: (PROG10.C) main() { int i; printf("Digite sua idade: "); scanf("%d",&i); if (i > 70) printf("Esta Velho!"); else if (i > 21) printf("Adulto"); else printf("Jovem"); } while Executa o comando enquanto a condição for verdadeira. Como a condição é avaliada no início de cada passagem, o comando não será executado nenhuma vez se, ao entrar neste laço, a condição for falsa. Sintaxe: while(condição) { comandos; } Caso existir um único comando, as chaves não são necessárias. Se não existir condição, o laço será infinito e o programa fica com sua execução travada. Pode-se utilizar os seguintes comandos dentro de um laço para controlar a sua execução: · continue; Salta o restante do laço e testa novamente a condição. · break; Finaliza o laço atual, pulando para o primeiro comando após o final deste. 22 do while Da mesma forma que o anterior, também executa comandos repetitivos, porém a condição é avaliada somente no final da execução. Sintaxe: do { comandos; } while(condição); switch case Uma variável é testada sucessivamente contra uma lista de variáveis inteiras ou de caracteres. Depois de encontrar uma coincidência, o comando ou o bloco de comandos é executado. Podem existir quantos casos forem necessários ou até quanto o processamento permitir. Se nenhuma coincidência for encontrada o comando default será executado sendo este opcional. A seqüência de comandos é executada até que o comando break seja encontrado. A seqüência de comando não é executada quando o comando continue for encontrado. Sintaxe: switch(variável) { case constante1: { seqüência de comandos } case constante2: { seqüência de comandos } default: { seqüência de comandos } } Exemplo: (PROG11.C) 23 main( ) { char ch; printf("1. inclusão\n"); printf("2. alteração\n"); printf("3. exclusão\n"); printf(" Digite sua opção:"); do { ch=getchar(); switch(ch) { case '1': printf("escolheu inclusao\n"); break; case '2': printf("escolheu alteracao\n"); break; case '3': printf("escolheu exclusao\n"); break; case '4': printf("sair\n"); } } while (ch!='1' && ch!='2' && ch!='3' && ch!='4'); } for O comando for é de alguma maneira encontrado em todas linguagens procedurais de programação, sendo este um dos comandos mais importantes do C. Em sua forma mais simples, sua inicialização é um comando de atribuição que o compilador usa para estabelecer a variável de controle do laço. A condição é uma expressão de relação que testa a variável de controle do laço contra algum valor para determinar quando o loop terminará. O incremento define a maneira como a variávelde controle do loop será alterada cada vez que o computador repetir o laço. Os comando break e continue também são válidos para o comando for. Sintaxe: for ( inicialização; condição; incremento) comando; Para fazer um laço infinito basta colocar no programa a seguinte linha: for ( ; ; ) printf(“este laço será executado infinitamente /n”); 24 Exemplo: (PROG12.C) #include <stdio.h> void main(void) { int x; for(x=1; x<=100; x++) printf("%d ", x); } Este programa imprime os números de 1 até 100 na tela do computador. Nota sobre o comando goto De maneira geral o comando goto é considerado um grande vilão das linguagens de programação e geralmente seu uso é desaconselhado quando não proibido por equipes de desenvolvimento e por vários autores de manuais de linguagens. Quando incorretamente utilizado pode causar sérios danos ao programa e ao computador. Apesar de não ser imprescindível para escrita de programas em linguagem C e de ter seu uso restrito a condições particulares, alguns programadores de assembly fazem muito uso deste comando e outros da mesma classe, chamados de desestruturadores. Pode ajudar a tornar a execução de um programa mais rápida porém, o risco de falhas é elevado. O comando goto não será estudado neste curso e seu uso é desaconselhável. Estruturas Ao manusearmos dados muitas vezes deparamos com informações que não são fáceis de armazenar em variáveis escalares como são os tipos inteiros e pontos flutuantes, mas na verdade são conjuntos de coisas. Estes tipos de dados são compostos com vários dos tipos básicos do C. Uma estrutura é uma coleção de variáveis referenciadas por um nome. Suas variáveis são chamadas de elementos da estrutura e todos os seus elementos estão logicamente relacionados. As estruturas permitem uma organização dos dados dividida em campos e registros. Os elementos individuais de uma estrutura são referenciados através do operador ponto. Por exemplo: endereço.cep = 15385000 Atribui o valor 15385000 ao campo cep da variável estrutura chamada endereço. As variáveis do tipo estrutura devem ser declaradas conforme o exemplo abaixo: Exemplo (PROG13.C) 25 struct lapis { int dureza; char fabricante; int numero; }; main() { int i; struct lapis p[3]; p[0].dureza=2; p[0].fabricante='F'; p[0].numero=482; p[1].dureza=0; p[1].fabricante='G'; p[1].numero=33; p[2].dureza=3; p[2].fabricante='E'; p[2].numero=107; printf("Dureza Fabricante Numero\n"); for(i=0;i<3;i++) printf("%d\t%c\t\t%d\n",p[i].dureza,p[i].fabricante,p[i].numero); } Noções de ponteiros em Linguagem C Ponteiros são endereços, isto é, são variáveis que contém um endereço de memória. Se uma variável contém o endereço de outra, então a primeira (o ponteiro) aponta para a segunda. É desnecessário dizer, que estas são características dos assemblers próprios de cada equipamento, portanto a Linguagem C nos desobriga a aprender o assembler de cada microprocessador, porém não elimina a necessidade de conhecermos sua arquitetura. As estruturas também podem ser referenciadas por ponteiros. Assim, definindo-se por exemplo o ponteiro *p para a estrutura do exemplo anterior (lapis), pode-se usar a sintaxe (*p).dureza. Porém, para referenciar o ponteiro há ainda outra sintaxe, através do operador -> , como por exemplo, p->dureza. Operadores de ponteiros & Fornece o endereço de uma determinada variável. 26 Atribui o endereço de uma variável para um ponteiro. Não confundir com o operador lógico de operações de baixo nível, de mesmo símbolo. * Acessa o conteúdo de uma variável cujo endereço é o valor do ponteiro. Devolve o valor endereçado pelo ponteiro. Não confundir com o operador aritmético de multiplicação de mesmo símbolo. Exemplo: (PROG14.C) main() { int *pont, cont, valor; cont = 100; pont = &cont; valor = *pont; printf("%d",valor); /* o resultado é 100 pois valor recebe o dado do ponteiro pont. } Exemplo: (PROG15.C) main() { int x, y,*px; x = 100; px = &x; /* px recebe o endereco onde esta o valor de x */ y = *px; /* y vale 100, pois recebe o conteudo de x */ printf("o endereco do valor de x em hexadecimal e %X\n ",px); printf("o valor da variavel neste endereco vale %d", y); } Aritmética com ponteiros Por conterem somente endereços, ponteiros permitem apenas as operações de soma e subtração. Supondo que: int i,*pi; char c,*pc; float f,*pf; Supondo ainda que pi, pc e pf apontem para i, c e f que estariam com os seguintes endereços: 2340, 2345 e 2350. Se pc = pc + 1 então pc valerá 2346, pois variáveis caracteres possuem apenas 1 byte. pi = pi + 1 27 então pi valerá 2342, pois variáveis inteiras ocupam 2 bytes. pf = pf + 5 então pf valerá 2370, pois variáveis ponto flutuante ocupam quatro bytes. Os incrementos e decrementos dos endereços podem ser realizados com os operadores ++ e --, que possuem procedência sobre o * e operações matemáticas e são avaliados da direita para a esquerda: Por exemplo: *px++; sobe uma posição na memória . *(px--); desce uma posição de memória. No exemplo abaixo os parênteses são necessários, pois sem eles px seria incrementado em vez do conteúdo que é apontado, porque os operadores * e ++ são avaliados da direita para esquerda. (*px)++; equivale a x=x+1 ou *px+=1. Ponteiros para ponteiros Um ponteiro para um ponteiro é uma forma de indicação múltipla. Num ponteiro normal, o valor do ponteiro é o valor do endereço da variável que contém o valor desejado. Nesse caso o primeiro ponteiro contém o endereço do segundo, que aponta para a variável que contém o valor desejado. Exemplo: float **balanço; balanço é um ponteiro para um ponteiro float. Problemas com ponteiros O erro chamado de ponteiro perdido é um dos mais difíceis de se encontrar, pois a cada vez que a operação com o ponteiro é utilizada, poderá estar sendo lido ou gravado em posições desconhecidas da memória. Isso pode acarretar em sobreposições sobre áreas de dados ou mesmo área do programa na memória. int,*p; x=10; *p=x; Estamos atribuindo o valor 10 a uma localização desconhecida de memória. A conseqüência desta atribuição é imprevisível. 28 Noções de operações com pilhas Uma pilha é uma lista de variáveis do mesmo tipo (semelhantes a uma matriz, ou mesmo uma matriz), onde utilizamos o conceito de que "o primeiro que entra é o último a sair". Imaginemos um bloco de folhas. Normalmente utilizaremos primeiro a última folha do bloco ("a de cima"), enquanto que a primeira folha colocada ("a de baixo") será a última a ser utilizada. O conceito de pilha é utilizado principalmente em máquinas de calcular e calculadoras científicas a exemplo das calculadoras da Hewlett-Packard (HP 48, HP49, etc). Nos exemplos a seguir serão utilizadas duas funções: push() e pop(). Usaremos push() para inserir elementos na pilha e pop() para sacá-los. Exemplo de uma pilha de 10 elementos, onde números positivos empilhem elementos na pilha, 0 tira o último elemento da pilha e -1 cause o encerramento do programa: Exemplo (PROG16.C) #include <stdio.h> int pilha[10],*p1,*to; main() { int valor; p1 = pilha; to = p1; printf("Numero --> Pilha, 0 recupera e -1 finaliza \n"); do { scanf("%d",&valor); if (valor != 0) push(valor); else { valor = pop(); printf("%d\n",valor); } } while (valor != -1); } push(i) int i; { p1++; if (p1 == (to + 10)) { 29 puts("Estouroda Pilha (superior) "); exit(1); } *p1 = i; } pop() { if ((p1) == to) { puts("Estouro da Pilha (inferior) "); exit(1); } p1--; return *(p1+1); } As operações com a pilha do sistema requer o conhecimento prévio do tipo de processador, total de elementos manipuláveis na pilha e outros dados intrínsecos ao hardware do computador. A utilização da pilha em programas em C agilizam a resolução de funções matemáticas uma vez que a própria CPU realiza operações aritméticas na pilha do sistema. Contudo, a manipulação dos dados da pilha do sistema são procedimentos complexos e recomendados somente para programadores experimentes. Acessando arquivos externos Noções de Streams Os streams constituem uma forma portável de ler e escrever informação nos programas escritos em C. São também uma forma bastante eficiente e flexível de efetuar essa transferência de informação. Um stream é um arquivo ou outro dispositivo de entrada/saída (por exemplo: o monitor de vídeo, o teclado, a impressora, etc) que é manipulado através de um apontador para o stream. Na biblioteca standard os streams são representados por uma estrutura definida em stdio.h e com o nome de FILE. Os programas que manipulam streams usam então um apontador para esta estrutura (FILE *). Do ponto de vista do programa em C não é necessário conhecer mais nada acerca desta estrutura. Basta declarar um apontador para ela e usá-lo para efetuar operações de I/O (escrita e/ou leitura). Antes de se poderem efetuar propriamente as operações de transferência de informação para os streams é necessário executar uma operação de abertura (open). Quando já não houver mais necessidade de novas operações de transferência de informação podemos (e devemos) fechar o stream com uma operação de fechamento (close). 30 As operações de I/O em streams são bufferizadas, isto é, existe sempre uma área de memória intermédia (o buffer) para e de onde são efectuadas as transferências de informação para os dispositivos que os streams representam. A existência de um buffer leva a uma maior eficiência nas operações de I/O, no entanto os dados presentes no buffer não são imediatamente transferidos para o stream. Qualquer terminação anormal de um programa pode conduzir a perda de informação se esta ainda não tiver sido transferida. Arquivos de dados Os streams mais comuns são os que ficam associados a arquivos armazenados em disco. A primeira operação necessária para trabalhar com esse tipo de streams é uma operação de abertura, efetuada com a função fopen(): fopen É usado para abrir um arquivo para escrita ou leitura, retornando um apontador para uma estrutura FILE. Um arquivo preexistente com o nome fornecido será apagado e um novo arquivo iniciado. Caso não houver outro arquivo com o nome dado então um novo arquivo é criado. Para abrir um arquivo para leitura este arquivo precisa existir caso contrário, o comando retorna um erro ao programa. Se o arquivo especificado não puder ser acessado, por qualquer razão, a função retornará um apontador nulo (NULL). Sintaxe: FILE *fopen(char *arquivo, char *modo); O parâmetro arquivo é o nome do arquivo armazenado no disco que se pretende aceder. O parâmetro modo controla o tipo de acesso. Os modos de acesso aos arquivos são: Modo Significado "r" Abre Arquivo de Texto para Leitura "w" Cria Arquivo de Texto para Gravação "a" Anexa a um Arquivo de Texto "rb" Abre Arquivo Binário para Leitura "wb" Cria Arquivo Binário para Gravação "ab" Anexa a um Arquivo Binário "r+" Abre Arquivo de Texto para Leitura/Gravação "w+" Cria Arquivo de Texto para Leitura/Gravação "a+" Abre ou Cria Arquivo de Texto para Leitura/Gravação "r+b" Abre Arquivo Binário para Leitura/Gravação "w+b" Cria Arquivo Binário para Leitura/Gravação "a+b" Abre ou Cria Arquivo Binário para Leitura/Gravação 31 Exemplo de abertura de um arquivo chamado myfile.dat para leitura apenas: FILE *dados; dados = fopen("myfile.dat", "rb"); É sempre boa política verificar se os arquivos que se pretendem abrir, o foram efetivamente. Um exemplo disso é mostrado abaixo. if ( (dados = fopen("myfile.dat", "rb")) == NULL) { printf("Não é possível abrio o arquivo %s\n", "myfile.dat"); exit(1); } fclose Ela fecha o arquivo associado a estrutura FILE e após isso, esvazia seu buffer. Se o comando de fechamento do arquivo for bem sucedido, ela retorna um valor nulo, caso contrário, um valor diferente de zero. Sintaxe: int fclose(FILE *arquivo) fprintf Semelhante a função printf, porém trabalham com dados nos arquivos. Sua sintaxe é: int fprintf(FILE *arquivo,char *formato,...); fscanf Também similar a scanf, mas também voltada para manipular dados em arquivos. Sua sintaxe: int fscanf(FILE *arquivo, char *formato, ...); fgetc Lê um caractere do arquivo. Retorna o caractere lido ou EOF no final do arquivo ou em caso de erro. Sintaxe: int fgetc (FILE *arquivo); fptuc Escreve um caractere no arquivo. Retorna o caractere gravado ou EOF em caso de erro. int fputc(int caractere, FILE *arquivo); 32 rewind Reposiciona o indicador de posição de arquivo no início do arquivo especificado. Nitidamente “rebobina” o arquivo, permitindo que seja novamente lido ou gravado do começo. Sua sintaxe é: void rewind(FILE *pt) Onde pt é um ponteiro válido de arquivo. foef Determina quando o final físico do arquivo foi atingido na leitura de textos ou dados binários. Este comando devolve verdadeiro se o final foi atingido e nulo, caso contrário. Sua sintaxe: int feof(FILE *pt) Um exemplo de como ler um arquivo até seu final fica: while(!feof(pt)) ch = getc(pt); ferror Determina se uma operação com o arquivo produziu um erro qualquer. Ele retorna um valor verdadeiro se algum erro aconteceu durante a última operação no arquivo. Sua sintaxe: Int ferror(FILE *pt); Exemplo: (PROG18.C) #include <stdio.h> void main(void) { FILE *infile, *outfile; int ch, bytes=0; if((infile=fopen("prog1.exe","rb")) ==NULL) { perror ("Desculpa ai, mas nao encontrei prog1.exe"); exit(1); } if((outfile=fopen("hello.exe","wb"))==NULL) { perror ("Desculpa ai, nao da pra criar hello.exe"); exit(1); } 33 while((ch=getc(infile)) !=EOF) { putc(ch,outfile); ++ bytes; } fclose(infile); fclose(outfile); printf("\n Total de bytes copiados %d",bytes); getch(); } Troca de dados com outros periféricos Os computadores do padrão IBM-PC comunicam-se com seus periféricos através dos endereços das portas de dados. Cada porta é identificada com um número inteiro entre 0 a 65535. As portas podem ser as interface de comunicação seriais (COM), comunicação paralela (LTP), barramento ISA, barramento PCI, barramento AGP, etc. A maneira de se comunicar com um periférico dentro do PC consiste em ler ou gravar dados na memória da porta ligada ao respectivo periférico. Na linguagem C ANSI não existe funções próprias que façam a comunicação com as diversas portas de um PC porém, alguns compiladores disponibilizam alguns comando que podem auxiliar nesta comunicação. Para o compilador Turbo C da Borland, as seguintes funções estão disponíveis. Para leitura de dados na porta: char inportb(int porta); int inport(int porta); Para escrita de dados na porta: void outportb(int porta, char byte); void outport(int porta, int palavra) Como exemplo, vamos emitir um som pelo alto falante. Em geral, a porta a qual o alto falante do sistema está ligado em PC é a porta 61h hexadecimal (0x61). O bit zero deste byte é conectado diretamente ao altofalante. Para aciona-lo, é necessário escrever o valor zero neste bit e deixa-lo com este valor que o alto falante fique habilitado. O bit 1 desse byte aciona o alto falante e faz com que um som seja emitido, dependendo da duração do sinal. Variando-se este valor, pode-se variar a freqüência com que o alto falante é acionado e assim produzir som com tons musicais. Os demais bits deste byte da porta não devem se acessados. Programa exemplo: (PROG19.C) 34 #define porta_som 0x61 #define mascara 0xFE void main(void) { int I; char valor, valor_origem; valor_origem = valor = inportb(porta_som); valor = valor & mascara while(!kbhit()) { outportb(porta_som, valor); for (i=0;i<0x140;i++); valor = valor ^ 2; } outportb(porta_som, valor_origem); } Bibliografia RITCHIE, D. M.; KERNIGHAN, B. W. C Linguagem de Programação Padrão ANSI. 4º ed. 1991, editora Campus. 289p Introdução a Linguagem C. Centro de Computação da Unicamp, versão 2.0, 1999. 36p BRUSSO, M. J. Linguagem C. Universidade de Passo Fundo, Instituto de Ciências Exatas e Geociências, Curso de Ciência da Computação, Portugal. 1999. 42p JAMSA, K.; KLANDER, L. Programando em C/C++ “A Bíblia”. Editora Makron Books Ltda, 1998. 1012p. capa_curso_C Curso C
Compartilhar