Baixe o app para aproveitar ainda mais
Prévia do material em texto
1. Introdução........................................................................................................................................... 4 1.1. Características................................................................................................................... 4 1.2. Histórico ........................................................................................................................... 4 1.3. Características de um programa........................................................................................ 4 1.4. Formato de um programa em C ........................................................................................ 4 1.5. Exemplo de um programa................................................................................................. 5 2. Identificadores .................................................................................................................................... 6 3. Tipos de dados para variáveis e funções ............................................................................................ 7 3.1. Standard (já definidos na linguagem) ............................................................................... 7 3.2. Modificadores de tipo ....................................................................................................... 7 4. Declaração de variáveis...................................................................................................................... 8 4.1. Variáveis locais................................................................................................................. 8 4.2. Parâmetros formais ........................................................................................................... 9 4.3. Variáveis globais .............................................................................................................. 9 4.4. Modificadores de classe de armazenamento..................................................................... 10 4.4.1. Variáveis automáticas - auto ........................................................................... 10 4.4.2. Variáveis externas - extern.............................................................................. 10 4.4.3. Variáveis estáticas - static ............................................................................... 11 4.4.4. Variáveis register ............................................................................................ 11 5. Operadores.......................................................................................................................................... 23 5.1. aritméticos......................................................................................................................... 23 5.2. lógicos ou boleanos........................................................................................................... 23 5.3. relacionais ......................................................................................................................... 23 5.4. outros operadores.............................................................................................................. 24 5.5. precedência (da maior para a menor)................................................................................ 24 6. Funções de entrada e saída ................................................................................................................. 12 6.1. printf()............................................................................................................................... 12 6.2. putchar()............................................................................................................................ 12 6.3. puts() ................................................................................................................................. 12 6.4. scanf() ............................................................................................................................... 12 6.5. getchar()............................................................................................................................ 12 6.2. gets() ................................................................................................................................. 12 7. Instuções de controle .......................................................................................................................... 14 7.1. while ................................................................................................................................. 14 7.2. for...................................................................................................................................... 15 7.3. do while ............................................................................................................................ 15 7.4. if e derivados..................................................................................................................... 16 7.5. switch................................................................................................................................ 17 7.6. continue............................................................................................................................. 18 7.7. break ................................................................................................................................. 18 8. Funções............................................................................................................................................... 21 9. Arrays unidimensionais (vetores)....................................................................................................... 25 9.1. Strings ............................................................................................................................... 26 10. Arrays bidimensionais (matrizes bidimensionais) .............................................................................. 31 11. Pointers (ponteiros) ............................................................................................................................ 33 11.1. Definição........................................................................................................................... 33 11.2. Inicialização de pointers na definição............................................................................... 34 11.3. Passagem de parâmetros por endereço (referência) .......................................................... 34 12. A relação entre pointers e arrays ........................................................................................................ 36 13. Pointers e arrays bidimensionais ........................................................................................................ 40 13.1. Arrays de pointers (ragged arrays).................................................................................... 42 14. Definição de novos tipo...................................................................................................................... 44 15. Ponteiro para percorrer array de ponteiros. ........................................................................................ 45 16. Ponteiros para funções........................................................................................................................ 47 17. Pré-processador e diretivas................................................................................................................. 49 17.1 #define .............................................................................................................................. 49 17.2 #include............................................................................................................................. 5017.3. compilação condicional .................................................................................................... 50 2 18. Tipos construídos ............................................................................................................................... 53 18.1. Tipo enumerado ................................................................................................................ 53 18.2. Estruturas .......................................................................................................................... 54 18.3. Inicialização de estruturas................................................................................................. 56 18.4. Campos de bits.................................................................................................................. 56 18.5. Uniões ............................................................................................................................... 57 19. Pointer para estruturas ........................................................................................................................ 60 20. Arquivos ............................................................................................................................................. 61 20.1. Funções para abertura e fechamento de arquivos ............................................................. 61 20.2. Principais funções para leitura e gravação sequenciais .................................................... 63 20.3. Principais funções para leitura e gravação direta (binário)............................................... 65 21. Alocação dinâmica de memória.......................................................................................................... 71 3 1. Introdução 1.1. Características • linguagem de médio nível, que foi criada para substituir o ASSEMBLY • linguagem estruturada; • versátil (aplicação em várias áreas); • portável, o mesmo programa funciona em várias plataformas; • código eficiente; • compilação condicional; • compilação separada (funções em vários arquivos); • linguagem para o programador. 1.2. Histórico A linguagem C foi desenvolvida em 1971 por Kernighan e Ritchie (K & R), nos Laboratórios Bell; A princípio seus nomes foram BCPL, B e posteriormente foi chamada de C, mais recentemente, foi criada a sucessora da linguagem C, a linguagem orientada a objetos C++ 1.3. Características de um programa • um programa em C é composto por pelo menos uma função, a função "main"; • todo programa em C começa a sua execução a partir da função "main"; • normalmente, um programa em C é uma coleção de chamadas de funções, as quais são de pequeno tamanho; • toda função tem um tipo; • as palavras reservadas e funções da biblioteca do C são sempre escritas com letras minúsculas. 1.4. Formato de um programa em C "definições" função_1 função_2 ... função_n funcão_main A ordem das funções dentro de um programa pode ser qualquer. 1.5. Exemplo de um programa #include <stdio.h> /* instrução para o pré-processador */ void main() /* início da função main */ { /* início de bloco */ int i; /* define */ i = 10; /* inicializa */ printf("i = %d\n", i); /* chama função de impressão */ } /* fim de bloco */ • toda instrução termina por ';' 4 • protótipo de printf está em stdio.h (standard IO) 2. Identificadores São usados em nomes de funções e variáveis. ANSI: 31 caracteres para variáveis locais 6 para funções e variáveis globais Nos PC's, a maioria dos compiladores reconhece 31 caracteres tanto para variáveis quanto para funções. • devem começar por letra ou '_'; • normalmente se usam letras minúsculas; • não podem ser iguais às palavras reservadas; • minúsculas diferentes de maiúsculas: ABC # Abc # ABc # abc 3. Tipos de dados para variáveis e funções 3.1. Standard (já definidos na linguagem) caracter char (1 caracter) inteiro int(2 ou 4 bytes; PC = 2 bytes) real float (4 bytes) double(8 bytes) nada void (0 bytes) O tipo inteiro possui o tamanho da palavra da CPU. Para o PC temos: Tipo Tamanho em Bits Faixa char 8 0 a 255 int 16 -32768 a 32767 float 32 6 dígitos double 64 12 dígitos void 0 sem valor 3.2. Modificadores de tipo Alteram o significado do tipo standard. • signed (com sinal) • unsigned (sem sinal) • long (passa inteiro de 2 bytes para 4 bytes) • short (passa inteiro de 4 bytes para 2 bytes) No PC: 5 long int = long ⇒ 4 bytes short int = int ⇒ 2 bytes Ex.: signed char ⇒ -128 a 127 unsigned int ⇒ 0 a 65535 4. Declaração de variáveis Variáveis devem ser declaradas antes de serem usadas. Formato: <tipo> <lista de variáveis); Ex.: int a, b, c; float soma; double somatorio, raiz_quadrada; char sim, nao; As variáveis podem ser definidas dentro de qualquer bloco: { int x, y; comandos; } As variáveis podem ser locais, globais e parâmetros formais 4.1. Variáveis locais São as declaradas dentro de blocos e só são reconhecidas dentro do bloco de declaração. Ex.: #include <stdio.h> void main() { int i, j; i = 2; j = 2 * i; printf("O dobro de %d = %d\n", i, j); } No exemplo acima, 'i' e 'j' são variáveis locais da função "main". Ex.: void f1() { int x; x = 5; ... if (a > 10) { int x, y; x = 20; y = 40; ... } 6 } No exemplo, a variável 'x' tem o valor 5 quando sair do bloco mais interno. 4.2. Parâmetros formais São os parâmetros das funções. Obs.: O C só passa parâmetros por valor, isto é, o que é passado para a função é uma cópia do conteúdo da variável. Ex.: int soma(int a, int b) { return (a + b); } No exemplo, 'a' e 'b' são parâmetros formais e a função "soma" poderia ser chamada: j = soma(2, 3); x = soma(a, b); 4.3. Variáveis globais São reconhecidas por todas as funções de um programa ou de um arquivo. Ex.: #include <stdio.h> int cont; void f(void); void main() { cont = 100; printf("cont em main = %d\n", cont); cont = cont + 1; f(); } void f() { printf("cont em f = %d\n", cont); } 4.4. Modificadores de classe de armazenamento Indicam como as variáveis devem ser armazenadas. Exemplo de mapa da memória de um programa em C em um equipamento genérico: Área de Código Área de Variáveis Estáticas e Globais Stack 7 Heap 4.4.1. Variáveis automáticas - auto São as variáveis definidas normalmente. Ex.: int x; <=> auto int x; A palavra auto não é necessária. 4.4.2. Variáveis externas - extern São as variáveis globais definidas em arquivos diferentes. Ex.: arquivo 1: int x, y; void main() { y = 30; x = 10; f(); ... } arquivo 2: ... extern int x, y; void f() { ... printf("%d %d\n", x, y); ... } 4.4.3. Variáveis estáticas - static A variável não perde o seu valor dentro da função ou arquivo. Este tipo de variável é mais empregado para definição de strings. Quando uma função é definida como static, ela só pode ser ativada por uma função que esteja no mesmo arquivo. 4.4.4. Variáveis register A variável é colocada em um dos registradores da CPU. Deve ser bem escolhida (PC sópermite duas) Ex.: 8 register int i, j; Este tipo de varável é mais usada em casos nos quais a velocidade é importante, como por exemplo em variáveis que controlam loops. 5. Operadores Um operador é um caracter ou grupo de caracteres que causará uma manipulação matemática ou lógica. Em C, os operadores podem ser aritméticos, relacionais e lógicos e entre bits. 5.1. aritméticos + ⇒ adição - ⇒ subtração e menos unário * ⇒ multiplicação / ⇒ divisão % ⇒ resto divisão -- ⇒ decremento ++ ⇒ incremento += ⇒ soma -= ⇒ subtração *= ⇒ multiplicação /= ⇒ divisão 5.2. lógicos ou boleanos && ⇒ e || ⇒ ou ! ⇒ não 5.3. relacionais > ⇒ maior < ⇒ menor == ⇒ igual != ⇒ diferente >= ⇒ maior ou igual <= ⇒ menor ou igual 5.4. Outros operadores * ⇒ retorna o valor contido em um endereço & ⇒ retorna o endereço de uma variável sizeof ⇒ retorna o tamanho de uma variável ou tipo ? ⇒ avalia expressão verdadeira , ⇒ equivalente a "faça isto e isto e isto ..." . ⇒ membro de estrutura ou união -> ⇒ pointer para membro de estrutura ou união 9 (type) ⇒ cast (altera tipo do dado) 5.5. precedência (da maior para a menor) () [] -> . ! ~ ++ -- (type) * & sizeof * / % + - << >> < <= > >= == != & ^ | && || ? = += -= *= /= , 6. Funções de entrada e saída Ao contrário de outras linguagens, no C as operaçães de leitura e impressão, tanto na tela quanto em arquivos, é totalmente feita por funções. Os protótipos destas funções se encontram no arquivo stdio.h, o qual deve sempre ser incluído quando forem usadas funções de IO (entrada e saída). 6.1. printf() Principal função de impressão no vídeo. Formato: int printf(formato, argumentos); A função printf retorna o número de caracteres impressos. Obs1.: Em C, o valor de retorno de uma função pode ser ignorado; Obs2.: Em C, as funções podem ter um número variável de parâmetros. Ex.: printf("\nFIM\n"); Principais formatos de impressão: • %d - inteiro • %c - caracter • %f - float • %ld - long int • %lf - double • %s - string • %e - notação científica 10 A saída pode ser formatada através do tamanho do campo: Exemplo: • %3d - inteiro com 3 dígitos • %7.2f - float com dois dígitos após o ponto decimal 6.2. putchar() Imprime um caracter. Formato: int putchar(variável); Ex.: putchar(a); ⇒ imprime o conteúdo da variável a. 6.3. puts() Imprime um cadeia de caracteres (string). Formato: int puts(variável); Ex.: puts(st); ⇒ imprime o conteúdo da string st. 6.4. scanf() Principal função de leitura do teclado Formato: int scanf(formato, parâmetros); Retorna o número de variáveis lidas corretamente ou EOF em caso de erro grave. Principais formatos: • %d - inteiro • %c - caracter • %f - float • %ld - long int • %lf - double • %s - string Para a função scanf, o branco é considerado delimitador. Ex.: scanf("%d %d", &a, &b); 1b3 ou 1b...b3 scanf("%d , %d", &a, &b); 1 , 3 Exercício: Ler dois números e imprimir a soma. #include <stdio.h> void main() 11 { int a, b, c; printf("Entre 2 numeros: "); scanf("%d %d", &a, &b); c = a + b; printf("soma = %d\n", c); /* printf("soma = %d\n", a + b); */ } 6.5. getchar() Lê um caracter do teclado. Formato: int getchar(void); Ex.: c = getchar(); ⇒ guarda na variável c o caracter lido. 6.6. gets() Lê uma cadeia de caracteres (string) do teclado. Formato: char gets(variável); Ex.: gets(s); ⇒ guarda na variável s a string lida. 7. Instuções de controle 7.1. while Estrutura de repetição com teste no início. Formato: while (condição) bloco; Executa o bloco enquanto a condição for verdadeira. Obs.: Em C, verdadeiro é tudo o que for diferente de zero. Ex.: Imprimir a soma dos números de 1 a 5 com while Solução 1: #include <stdio.h> void main() { int i, soma; i = 1; soma = 0; while (i <= 5) { soma = soma + i; i = i + 1; } 12 printf("\nSoma = %d\n", soma); } Solução 2: #include <stdio.h> void main() { int i = 1, soma = 0; while (i <= 5) { soma += i; i++; } printf("\nSoma = %d\n", soma); } Obs.: i++; ⇔ ++i; se a = 1: para b = ++a; ⇒ b = 2 e a = 2 para b = a++; ⇒ b = 1 e a = 2 var = var op exp; ⇔ var op = exp; x = x + 5; x += 5; x = x / (a + b); x /= a + b; 7.2. for Estrutura de repetição com a quantidade já conhecida. Formato: for (inicialização; condição; incremento) bloco; Exemplo: Imprimir os números de 1 a 10 #include <stdio.h> void main() { int i; for (i = 1; i <= 10; i++) printf("%d\n", i); } Obs.: Todos os campos do for são opcionais: #include <stdio.h> void main() { int i = 1; for (; i <= 10;) printf("%d\n", i++); } 13 7.3. do while Estrutura de repetição com o teste no final. Formato: do bloco; while (condição); Repete o bloco enquanto a condição for verdadeira. Exemplo.: Imprimir os números de 1 a 10 #include <stdio.h> void main() { int i = 1; do { printf("%d\n", i) i++; } while (i <= 10); } Exercício: Calcular o fatorial de um número lido. Solução 1: #include <stdio.h> void main() { int n, i, fator = 1; printf("Entre o numero: "); scanf("%d", &n); for (i = 1; i <= n; i++) fator *= i; printf("Fatorial de %d = %d\n", n, fator); } Solução 2: #include <stdio.h> void main() { int n, i, fat; printf("Entre o numero: "); scanf("%d", &n); i = n; for (fat = 1; n > 1; n--) fat *= n; printf("Fatorial de %d = %d\n", i, fat); } 7.4. if e derivados Estrutura de decisão Formato: if (teste) 14 bloco; if (teste) bloco1; else bloco2; if (teste1) bloco1; else if (teste2) bloco2; else if (teste3) bloco3; else bloco4; Obs.: Se bloco possuir mais de uma instrução é necessário {} Exercício: ler 2 números e indicar o maior #include <stdio.h> void main() { int a, b; printf("Entre 2 numeros: "); scanf("%d %d", &a, &b); if (a == b) /* teste é com == */ printf("Sao iguais\n"); else if (a > b) printf("O primeiro é maior\n"); else printf("O segundo é maior\n"); } Exercício: ler uma série de números reais e indicar o maior e o menor. #include <stdio.h> void main() { float min, max, x; printf("Entre os numeros:\n"); scanf("%f", &x); min = max = x; /* atribuição múltipla */ while (scanf("%f", &x) == 1) if (min > x) min = x; else if (max < x) max = x; printf("O maior é: %f\n", max); printf("O menor é: %f\n", min); } 7.5. switch Estrutura de decisão que substitui grandes if's. Formato: switch (exp) 15 { case cte1: bloco1; break;case cte2: bloco2; break; case cte3: bloco3; break; ... case cten: blocon; break; default: bloco; break; /* não é necessário break aqui */ } Obs.: A expressão 'exp' sempre deverá fornecer um resultado inteiro. 7.6. continue Passa para o próximo incremento em um for ignorando as instruções restantes dentro do bloco do for. Exemplo: ... for (i = 0; i < 10; i++) { if (i == 0) continue; /* evita divisão por zero */ printf("%f\n", (float) 10 / i); } 7.7. break Interrompe a execução de um for, while, do ... while ou switch. O fluxo de execução do programa é desviado para primeira instução fora do bloco que possui o break. Exemplo: while (1) /* loop infinito */ { scanf("%d" , &a); if (a != 0) processa(); else break; } Exercício: Fazer um programa que simule uma calculadora, com as quatro operações, teste a divisão por zero e sinal inválido. Solução: #define FALSE 0 /* definição de constantes */ #define TRUE 1 16 #include <stdio.h> void main() { float v1, v2, res; char op; int erro = 1; /* usa int como variável lógica */ printf("Entre sua expressão:\n"); scanf("%f %c %f", &v1, &op, &v2); switch (op) { case '+': res = v1 + v2; break; case '-': res = v1 - v2: break; case '*': res = v1 * v2; break; case '/': if (v2 == 0.0) { printf("\nErro: Divisão por zero\n"); erro = TRUE; } else res = v1 / v2; break; default: erro = TRUE; printf("\nSinal inválido\n"); break; } if (erro==1) printf("\nO resultado é: %10.4f\n", res); } Exercício: Fazer um programa que leia um valor n e que leia e imprima o quadrado de n números. #include <stdio.h> void main() { float x; int n; printf("Numero de valores a serem lidos: "); scanf("%d", &n); while (n--) { printf("Valor: "); scanf("%f", &x); printf("Quadrado de %f = %f\n", x, x * x); 17 } } 8. Funções Pequeno programa que retorna um valor. Formato: tipo nome_função (parâmetros) { declaração de variáveis locais; corpo da função; } • tipo é o tipo do valor a ser retornado pela função; • se uma função não retorna nada, o seu tipo é void; • os parâmetros são sempre passados por valor, isto é, uma cópia do valor da variável é passado para a função; • para arrays (vetores e matrizes) é passado como valor o endereço da primeira posição; • deve-se sempre usar o protótipo da função; • variáveis 'char' são transformadas em 'int' e variáveis 'float' são transformadas em 'double'. Notas: O formato da função varia entre o padrão K & R e o ANSI, sendo que o padrão K & R pode ser usado no ANSI. formato K & R | formato ANSI int soma (a, b) | int soma (int a, int b) int a, b; | { { | return (a+b); return (a+b); | } } | O valor retornado por uma função pode ou não ser utilizado, como por exemplo na função printf(). Uma função pode retornar de qualquer ponto da função, bastando para isto se usar a instrução return. Exercício: Fazer um programa para ler um número e imprimir o seu fatorial. #include <stdio.h> long fatorial(long); long leval(void); void printfat(long); void main() { long fat, val; val = leval(); fat = fatorial(val); printfat(fat); } long leval() { 18 long x; printf("Entre o valor a ser calculado: "); scanf("%ld", &x); return(x); } void printfat(long y) { printf("Fatorial = %ld\n", y); } long fatorial(long a) { long fator = 1; long i; for (i = 1; i <= a; i++) fator *= i; return(fator); } 9. Arrays unidimensionais (vetores) Definição de vetores unidimensionais: Formato: <tipo> <nome>[<tam>]; Exemplo: int vet[5]; ⇒ define vet como um vetor inteiro de 5 posições A posição do primeiro elemento é vet[0] e do último vet[4]. Exemplos de acesso: int vet[5]; vet[0]=10; vet[1]=20; vet[2]=40; for (i = 0; i < 5; i++) /* zera o vetor */ vet [i]=0; Inicialização na definição: para vetor local: static int vet[] = {1, 3, 5}; assume que o vetor 'vet' possui 3 elementos static float vet[10]; assume que o vetor 'vet' tem 10 elementos, os quais são inicializados com zero (0) static int vet[5] = {1, 2, 3}; assume que o vetor 'vet' tem 5 elementos, sendo que o quarto e o quinto são inicializados com zero (0) 19 para vetor global: int vet[] = {10,20,40,50}; assume que o vetor 'vet' tem 4 elementos Quando um vetor 'extern' ou 'static' for inicializado sem dimensão, a dimensão será a do número de valores da inicialização. Caso seja fornecida a dimensão e faltem elementos na inicialização, os elementos não inicializados valer,o zero. Exercício: Fazer uma função que retorne o maior número de um vetor double. #include <stdio.h> double maxval(double [], int); void levet(double [], int); void main() { double vet[100]; int i, n; printf("Numero de elementos do vetor: "); scanf("%d", &n); levet(vet, n); printf("Maior valor = %lf", maxval(vet, n)); } void levet(double v[], int n) { /* double v[]; não precisa dar a dimensão */ int i; double aux; printf("Entre os elementos do vetor:"); for (i = 0; i < n; i++) { scanf("%lf", &aux); v[i] = aux; } } double maxval(double v[], int n) { int i; double maior; maior = v[0]; /* maior começa como sendo o primeiro */ for (i = 1; i < n; i++) if (v[i] > maior) maior = v[i]; return(maior); } 9.1. Strings São arrays de caracteres. Obs.: Em C, toda string acaba com o caracter '\0'. 20 Definição de um vetor de caracteres: char str[7]; A definição acima, define str como sendo um vetor de 6 caracteres mais o caracter de final de string. Exemplo: char str[8]; str[0] = 'R'; str[1] = 'i'; str[2] = 'n'; str[3] = 'a'; str[4] = 'l'; str[5] = 'd'; str[6] = 'o'; str[7] = '\0'; Na memória teríamos: R 0 i 1 n 2 a 3 l 4 d 5 o 6 \0 7 Podemos inicializar uma string de duas formas: 1. Como um vetor: static char str[] = {'R', 'i', 'n', 'a', 'l', 'd', 'o', '\0'}; assume 7 caracteres mais '\0' = 8 2. Deixar que o compilador inicialize: static char str[]="Rinaldo"; assume 7 caracteres mais '\0' = 8 Obs.: Não é possível a atribuição direta: char str[7]; str = "Rinaldo"; /* Erro: não vale a atribuição */ Ex.: Fazer um programa para ler um nome e dar um bom dia para o dono do nome. 21 Solução 1: #include <stdio.h> #define TAM 100 void main() { char linha[TAM]; int c, i; printf("\nQual é o seu nome? "); for (i = 0; (c = getchar()) != '\n'; i++) linha[i]=c;linha[i] = '\0'; printf("Tenha um bom dia "); for (i = 0; linha[i] != '\0'; i++) putchar(linha[i]); putchar('\n'); } A função getchar retorna um caracter do teclado e a função putchar coloca um caracter no vídeo. Estas funções, bem como as outras de IO podem ser redirecionadas. Em caso de erro, a função getchar retorna EOF. Solução 2: #include <stdio.h> #define TAM 100 void main() { char linha[TAM]; printf("\nQual é o seu nome? "); gets(linha); printf("\nTenha um bom dia "); puts(linha); } A função gets tem por finalidade ler uma string e a puts imprimir uma string. A função puts fornece automaticamente uma mudança de linha após a impressão. Para atribuição de strings, usa-se a função strcpy, que possui o formato: char *strcpy(char *destino, char *origem); Exemplo: strcpy(str, "casa"); str passa a ter "casa" Para se saber o tamanho de uma função, usa-se a função strlen, que possui o formato: int strlen(char *st); Exercício: Construir a função strlen. Solução 1: int strlen(char cadeia[]) 22 { int len=0, i=0; while (cadeia[i] != '\0') { ++len; ++i; } return(len); } Solução 2: int strlen(char cadeia[]) { int i; for (i = 0; cadeia[i] != '\0'; i++); /* bloco nulo */ return (i); } Exercício: Construir a função stingcpy Solução 1: void stringcpy(char[], char[]); void stringcpy(char s1[], char s2[]) { int i; for (i = 0; s2[i] != '\0'; i++) s1[i] = s2[i]; s1[i] = '\0'; } Solução 2: void stringcpy(char s1[], char s2[]) { int i = 0; while ((s1[i] = s2[i]) != '\0') i++; } 10. Arrays bidimensionais (matrizes bidimensionais) Formato: <tipo> <nome>[<d1><d2>]; onde: d1 ⇒ número de linhas (varia entre 0 e d1 - 1) d2 ⇒ número de colunas (varia entre 0 e d2 - 1) Exemplo: int b[3][5]; ⇒3 linhas e 5 colunas Inicialização na declaração: 23 static int b[][3] = | static int b[][3] = { | { 1, 1, 1, | {1, 1, 1}, 2, 2, 2, | {2, 2, 2}, 3, 3, 3 | {3, 3, 3} }; | }; A inicialização também pode ser feito de forma esparsa. Por exemplo: static int b[3][3] = { {1, 1}, {1} }; A matriz gerada pela inicialização acima será: 1 1 0 1 0 0 0 0 0 Note que para este tipo de inicialização é necessário que se forneça o número de linhas e de colunas. Nota: O armazenamento em C é feito por linhas. Passagem de um array bidimensional para uma função: Ao se passar uma matriz bidimensional, a única dimensção fixa é o número de colunas (ao contrário do Fortran que fixa o número de linhas). Exemplo: Dado: void main() { int mat[10][20]; ... f(mat, 10, 20); ... } A função f() poderia receber a matriz de duas formas: void f(int mat[10][20], int lin, int col) ou void f(int mat[][20], int lin, int col) { } Exemplo para zerar uma matriz: void zera(float mat[][20], int, int); /* protótipo */ 24 void main() { float mat[10][20]; zera(mat, 10, 20); } void zera(float mat[][20], int lin, int col) { register int i, j; for (i = 0; i < lin; i++) for (j = 0; j < col; j++) mat[i][j] = 0.0; } 11. Pointers (ponteiros) 11.1. Definição Um pointer é uma variável que contém como valor um endereço. Para definirmos uma variável como ponteiro, devemos fornecer o tipo da variável para qual apontará e um nome. Embora os endereços de variáveis de um tipo, por exemplo int, sejam semelhantes aos de outros tipos como float, char ou double, é importante não misturá-los. Para a declaração de um ponteiro tem-se que indicar o tipo de variável para o qual ele irá apontar, pois ao serem feitas as operações aritméticas com os ponteiros eles irão variar em função do tipo. Principais vantagens do uso: • superar as restrições de passagem de argumento, por valor, para funções; • acessar elementos de arrays de modo alternativo ao uso de índices; • permitir a criação de estruturas dinâmicas, como árvores e listas encadeadas bem como a sua manipulação. Forma de definição: <tipo> *<nome>; Exemplos: int *p; ⇒ p é um pointer para inteiro float *pf; ⇒ pf é um pointer para float int i, *pt; ⇒ pt é um pointer para inteiro pt = &i; ⇒ pt recebe o endereço de 'i' 25 5 1002 i p 1002 2000 t *pt ⇒ 5 (conteúdo do endereço dado por pt) void *pvoid; ⇒ pointer para qualquer tipo Dado: float x, y, *p, *q; Temos como operações possíveis: p = &x; ⇒ p aponta para x y = *p; ⇒ y recebe o valor que está no endereço dado por p q = p; ⇒ q e p apontam para o mesmo endereço Erros comuns: int *p; *p = 10; não apontou para nenhuma variável float val; int *p; p = &val; tipos incompatíveis 11.2. Inicialização de pointers na definição int i = 7, *p = &i; a inicialização é de 'p' e não de '*p' 11.3. Passagem de parâmetros por endereço (referência) Para que se possa passar uma variável por referência, devemos passar um pointer para esta variável ou, em outras palavras, o seu endereço. /* ** Exemplo: ler dois valores e trocá-los de ordem */ #include <stdio.h> void swap(int *, int *); /* protótipo de swap */ void main() { int a, b; scanf("%d %d", &a, &b); /* le os dados */ swap(&a, &b); /* chama a função swap */ printf("%d : %d\n", a, b); /* imprime resultado */ } void swap(int *x, int *y) 26 { int tmp; tmp = *x; *x = *y; *y = tmp; } Exercício: fazer uma função que tenha o seguinte protótipo: void soma(int *r, int a, int b); #include <stdio.h> void soma(int *r, int a, int b); void main() { int x, y, z; printf("\nEntre dois números:\n"); scanf("%d %d", &x, &y); soma(&z, x, y); printf("\nA soma é: %d\n", z); } void soma(int *r, int a, int b) { *r = a + b; } 12. A relação entre pointers e arrays Um nome de array é um endereço ou pointer, que é fixo. Exemplo: int k, a[100], *p; p = a; ⇒ 'p' possui o endereço do primeiro elemento de 'a' ou p = &a[0]; ⇒'p' possui o endereço do primeiro elemento de 'a' Para que 'p' aponte para o elemento seguinte, basta incrementar o endereço de 1. p = a + 1; ⇔ p = &a[1]; p++; ⇒ 'p' aponta para o próximo elemento k = *p++; ⇒ 'k' recebe o valor do elemento apontado por 'p' e 'p'aponta para o próximo elemento Exemplo para zerar um vetor: for (i = 0; i < 100; i++) /* com índice */ a[i]=0; 27 ou for (i = 0, p = a; i < 100; i++) /* com ponteiro */ *p++ = 0; Exercício: Criar a função strlen usando pointer. Solução 1: #include <stdio.h> int strlen(char* str); void main() { static char str[] = "Esta string tem 30 caracteres."; printf("\nTamanho da string: %d caracteres.\n", strlen(str)); } int strlen(register char *s) { register int n; for (n = 0; *s != '\0'; s++) ++n; return(n); } Solução 2: #include <stdio.h>int strlen(char *str); void main() { static char str[] = "Esta string tem 30 caracteres."; printf("\nTamanho da string : %d caracteres.\n", strlen(str)); } int strlen(register char *s) { register int n=0; while (*s++) ++n; return(n); } Solução 3: #include <stdio.h> int strlen(char *str); void main() { static char str[] = "Esta string tem 30 caracteres."; printf("\nTamanho da string : %d caracteres.\n", strlen(str)); } int strlen(register char *s) { register char *ps; ps = s; /* ps aponta para o inicio da string */ while (*ps) ps++; return(ps - s); } 28 Exercício: Criar a função strcpy, que tem como finalidade copiar o conteúdo de uma string em outra. Solução: #include <stdio.h> char *strcpy(char *d, char *o); void main() { static char orig[] = "teste", dest[20]; strcpy(dest, orig); printf("origem = %s ; destino = %s\n", dest, orig); } char *strcpy (char *dest, char *origem) { char *d = dest; /* ** O while a seguir faz a copia e incrementa os ** ponteiros de cada string, até aparecer um caracter ** nulo significando fim de string. ** Alguns compiladores darão um aviso (warning), achando ** que se deseja fazer um teste ao invés de atribuição ** com teste, que é o que se pretende fazer realmente. */ while (*d++ = *origem++); /* sem bloco */ return(dest); } Exercício: Criar a função strcmp que tem como finalidade comparar o conteúdo de uma string com o de outra. A função tem o seguinte formato: int strcmp(char *s, char *t); Esta função retorna: • 0 ⇒ se as strings forem iguais • < 0 ⇒ se s < t • > 0 ⇒ se s > t Solução: #include <stdio.h> int strcmp(char *s, char *d); void main() { char s1[20], s2[20]; printf("Entre a primeira string: "); gets(s1); printf("Entre a segunda string: "); gets(s2); if (strcmp (s1, s2)) 29 printf ("\nDiferentes.\n"); /* <> de zero */ else printf ("\nIguais.\n"); } int strcmp (char *s1, char *s2) { while (*s1 == *s2) if (*s1++) s2++; else return(0); return(*s1 - *s2); } Para definição e inicialização de strings, também é possível o seguinte tipo de instrução: char *mens = "Divisao por zero"; A definição acima, define mens como sendo um pointer para a string "Divisao por zero". Exercício: Fazer um programa que utilize uma função que retorne o maior elemento de um vetor. 13. Pointers e arrays bidimensionais Como para vetores unidimensionais, o nome de um array é um pointer fixo. Assim, as seguintes instruções são equivalentes e geram o mesmo código: b[i][j] *(*(b + i) + j) *(b + i * ncolun + j) Como para vetores unidimensionais, existem formas de se acessar matrizes de uma forma mais eficiente, como veremos a seguir. Definição de pointers para matrizes. int mat[10][20], (*plin)[20]; plin = mat; Nas instruções acima, definimos plin como sendo um pointer para uma linha de um array com 20 colunas e mandamos que ele aponte para o primeiro elemento. Forma de armazenamento na memória: ... ... ... 20 elementos 20 elementos 20 elementos Para acessar um elemento da matriz via ponteiro: (*plin)[j] ou *(*(plin) + j) Para se passar para a próxima linha: plin++; 30 20 20 20 plin plin + 1 plin + 10 Nota: (*plim)[20] define um ponteiro para linha de um vetor com 20 colunas, enquanto que *plin[20] define um vetor de 20 elementos do tipo ponteiro. Exercício: Calcular a média dos elementos de uma coluna de uma matriz de 3 colunas. Solução: #include <stdio.h> double test_avg(int[][3], int, int); void main() { double res; static int board[][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; res = test_avg(board, 4, 2); printf("%lf\n", res); } double test_avg(int tab[][3], int maxlin, int col) { long sum = 0; int i, (*ptr)[3] = tab; for (i = 0; i < maxlin; i++) sum += (*ptr++)[col]; return ((double) sum / maxlin); } Exercício: calcular a média dos elementos de uma linha de uma matriz de 3 colunas. Solução: #include <stdio.h> double test2_avg(int[][3], int); void main() { static int board[][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; double res; res = test2_avg(board,1); /* calcular para linha 1. */ 31 printf("%lf\n", res); } double test2_avg(int tab[][3], int linha) { long soma = 0; int i; int *p = &tab[linha][0]; for (i = 0; i < 3; i++) soma += *p++; return ((double) soma / 3); } Notas: • &tab[n][0] - endereço do elemento (n, 0) em tab • tab[n] - linha n de tab O nome de um array bidimensional quando usado com um único índice é um ponteiro para o primeiro elemento de uma determinada linha. 13.1. Arrays de pointers (ragged arrays) São arrays com linhas de tamanho variável. Um array deste tipo é composto por ponteiros para outras variáveis e o seu uso mais comum é para acessar tabelas de strings. Exemplo: definir um vetor de pointers para uma tabela de dias da semana. static char *dias[] = { "segunda", "terça", "quarta", "quinta", "sexta", "sábado", "domingo" }; Para esta definição, dias é um array de 7 ponteiros para caracteres. O esquema a seguir apresenta a organização interna da tabela de dias da semana. ... dias 0 1 6 s e g u n d a \0 \0 \0 t e r ç a d o m i n g o 32 Os elementos de 'dias' podem ser acessados de duas maneira: 1. dias[i] printf("%s\n", dias[2]); será impresso "quarta" 2. dias[i][j] printf("%c\n", dias[1][2]); será impresso 'r' (dias[1] ⇒ "terça" e dias[1][2] ⇒ 'r' de "terça" Exercício: Fazer uma função para imprimir 'dias'. Solução: #include <stdio.h> void printab(char*[], int); void printab(char *tab[], int n) { int i; for (i = 0; i <= n; i++) printf("%s\n",tab[i]); } Nota: Um elemento em um array de pointers pode ser usado como se ele fosse um pointer para o primeiro elemento de uma linha de um array bidimensional (tab[i]). 14. Definição de novos tipo Para definição de novos tipos, é utilizada a instrução typedef. Formato: typedef <tipo existente> <novo tipo>; Exemplos: para se definir o tipo 'contador': typedef int contador; contador i, j; (int i, j) para se definir o tipo 'string': typedef char string[81]; string text; (char text[81]) para se definir o tipo 'boolean': typedef int boolean; #define FALSE 0 #define TRUE 1 ... 33 boolean ok = TRUE; if (ok) ... para se definir o tipo 'ptrchar' (pointer para caracter): typedef char *ptrchar; ptrchar pchar; (char *pchar) 15. Ponteiro para percorrer array de ponteiros. Como para os outros arrays, o nome de um array de ponteiros é um ponteiro para o primeiro elemento do array. Quando se passaum array de ponteiros como parâmetro, o que realmente se passa é um pointeiro para o primeiro elemento do array. Exemplo: Imprimir tabela 'dias'. typedef char *CHARPTR; /* define tipo pointer para char */ void print_tab(CHARPTR *tab_ptr, int n) { CHARPTR *end_ptr = tab_ptr + (n - 1); while (tab_ptr <= end_ptr) printf ("%s\n", *tab_ptr++); } Esquema dos ponteiros: ... 0 1 6 s e g u n d a \0 \0 \0 t e r ç a d o m i n g o tab_ptr end_ptr 16. Ponteiros para funções Permitem passar funções como argumentos. Forma de definição: <tipo> (*func_ptr)(<tipos dos argumentos>); Nota: se não forem colocados os parênteses, estará sendo defininda uma função que retorna um pointer para <tipo>. Exemplo: int soma (int, int); void main() { 34 int (*ptr_soma)(int, int); ptr_soma = soma; /* pointer para função soma */ x = (*ptr_soma)(3,5); /* ⇒ x = soma(3,5); */ } Exercício: Fazer um programa que leia duas strings de dados numéricos ou alfanuméricos e as compare conforme o seu tipo. #include <stdio.h> #include <stdlib.h> #include <string.h> int numcmp(char *, char *); void check(char *, char *, int (*cmp)(char *, char *)); void main() { char s1[80], s2[80]; gets(s1); /* obtem primeira string */ gets(s2); /* obtem a segunda string */ if (isalpha(*s1)) check(s1 ,s2, strcmp); /* comp. como strings */ else check(s1, s2, numcmp); /* comp. como números */ } void check(char *a, char *b, int (*cmp)(char *, char *)) { if (!(*cmp)(a, b)) printf("iguais.\n"); else printf("diferentes.\n"); } int numcmp(char *a, char *b) { if (atoi(a) == atoi(b)) return(0); else return(1); } Observações sobre o exemplo: • a função strcmp compara strings • a função isalpha retorna <> 0 para letra do alfabeto • a função atoi transforma uma string em número inteiro 17. Pré-processador e diretivas O pré-processador é um programa que age antes do compilador da linguagem C, aumentando sua capacidade e auxiliando a portabilidade. Ele é comandado por diretivas (que começam sempre pelo sinal #). As diretivas só valem no arquivo onde estão definidas ou arquivos incluídos abaixo destas diretivas As principais diretivas são o #define e o #include. Além destas existem diretivas para compilação condicional (#if, #else, #elif, #endif, #ifdef e #ifndef) e outras utilizadas apenas por determinados compiladores como por exemplo para 35 montagem de trechos de programa em linguagem Assembly (#asm e #endasm). Entretanto, estas últimas não são portáveis, pois não são encontradas em todos os compiladores C. 17.1 #define Serve para definir uma constante simbólica. Por exemplo: #define TRUE 1 #define FALSE 0 #define MENS_ERRO "Erro. Divisão por zero.\n" #define MAX_TAM 100 As constantes assim definidas podem ser usadas para atribuir valores a variáveis, passadas como parâmetros e para comparações dentro do programa. Exemplo: int erro; ... erro = FALSE; if (a == 0) flag = TRUE; ... if (erro) printf (MENS_ERRO); Outra finalidade é definir macros. Por exemplo: #define streq(x, y) (strcmp((x), (y)) == 0) /* iguais */ #define strlt(x, y) (strcmp((x), (y)) < 0) /* x < y */ #define strgt(x, y) (strcmp((x), (y)) > 0) /* x > y */ Para que se possa reutilizar uma constante devemos torná-la antes indefinida, usando a diretiva #undef e a seguir redefiní-la novamente usando outro #define. Por exemplo: a) #define NOME "teste" ... ... #undef NOME #define NOME "teste" b) #define TAM 100 #define COMP 100 char array [TAM][COMP] #undef COMP #define COMP 200 char arrayb [TAM][COMP] 17.2 #include Permite incluir o conteúdo de um arquivo no programa. Por exemplo: #include <stdio.h> procura stdio.h no diretório de arquivos header #include "stdio.h" procura stdio.h no diretório corrente #include "b:auxiliar.h" procura auxiliar.h no drive b Esta última forma deve ser evitada, pois gera dependência do sistema operacional. 36 17.3. Compilação condicional Os comandos para compilação condicional são: #if, #else, #elif, #ifdef, #ifndef e #endif #if, #else, #elif e #endif: São comandos do tipo 'if then else' que indicarão para o compilador os trechos do programa que serão ou não compilados. Exemplo de formatos possíveis: #if expressão constante sequência de comandos #endif #if expressão constante sequência de comandos 1 #else sequência de comandos 2 #endif #if expressão constante 1 sequência de comandos 1 #elif expressão constante 2 sequência de comandos 2 #else sequência de comandos 3 #endif Exemplos de uso: a) #define MAX 100 ... ... void func() { #if MAX > 99 printf("Compilado para array > 99.\n"); #endif ... ... } b) #if PC == 1 #define COMP 1 #else #define COMP 2 #endif #ifdef e #ifndef 37 Usados para testar a existência ou não do símbolo que tem como parâmetro. Exemplo de formatos possíveis: #ifdef símbolo sequência de comandos #endif #ifdef símbolo sequência de comandos 1 #else sequência de comandos 2 #endif #ifndef símbolo sequência de comandos #endif #ifndef símbolo sequência de comandos 1 #else sequência de comandos 2 #endif Exemplos de uso: a) #ifdef DEBUG printf ("O valor de x : %d\n", x); #endif b) #ifndef DEBUG printf ("Sem debug.\n"); #endif 18. Tipos construídos 18.1. Tipo enumerado Faz com que uma variável possa ter somente um valor dentre um conjunto determinado de valores. Forma de definição: enum <nome> {<conjunto de valores separados por vírgulas>}; Exemplo: enum dia {segunda, terça, quarta, quinta, sexta, sábado, domingo}; 0 1 2 3 4 5 6 Este exemplo define o tipo 'dia'. Uma variável deste tipo somente poderá receber um dos valores dados entre parênteses. 38 enum dia, dia1, dia2, todos[7]; /* define variáveis */ dia1 = segunda; todos[6] = domingo; Note que os valores entre parênteses não são strings. Uma outra forma de definição possível é: enum {segunda, ..., domingo} dia1, dia2; Se usarmos as definições acima, a cada valor do tipo enumerado será associado um valor inteiro, a partir de zero (0). Entretanto, podemos indicar os valores inteiros que desejamos associar: enum frutas {laranja = 7, lim,o = 6, jaca = 0}; enum frutas {pera = 2, laranja, abacate, lim,o = 7}; Apesar das definições acima, os valores dos tipos enumerados não são inteiros. Erros: dia1 = 1.0; dia2 = 2; Para usarmos o valor inteiro associado, devemos usar um "cast" para inteiro: i = (int) dia1; 18.2. Estruturas Estruturas (struct) são usadas quando há necessidade de se combinar dados de um mesmo tipo ou de tipos diferentes em um único objeto ou registro. Forma de definição: struct <nome> { <tipo1> <campo1>; <tipo2> <campo2>; ... <tipon> <campon>; }; Exemplos: struct s1 /* Define estruturachamada s1 */ { /* contendo os seguintes campos:*/ int a; /* inteiro - a */ char nome[81]; /* array de caracteres - nome */ char ender[100]; /* array de caracteres - endereço */ }; struct s1 passa a ser um tipo. /* ** utiliza a estrutura s1 para definir ** duas variáveis estruturadas: info1 e info2 39 */ struct s1 info1, info2; Outra forma de definirmos variáveis é: struct { int x; int y; } v1, v2; No exemplo acima, não foi criado um tipo. Também é possível o uso do typedef em conjunto com as estruturas: typedef struct s1 s_s1; s_s1 info1, info2; Acesso aos membros de uma estrutura: <variável>.<campo>; Exemplos: info1.a = 10; strcpy(info1.nome, "teste"); struct s2 { int a; char *nome; }; struct s2 info3; info3.a = 10; info3.nome = "José da Silva"; /* nome é um ponteiro */ Operações possíveis entre estruturas: A partir da definição: struct s1 v1, v2; temos: - atribuição: v1 = v2; - comparação: (somente igualdade e desigualdade) if (v1 == v2) { ... } É possível perguntarmos se duas estruturas são iguais ou diferentes, mas não se uma é maior ou menor do que a outra. No entanto, os campos (membros) de uma estrutura podem ser comparados com os de outra. 40 Apesar de na versão ANSI podermos passar estruturas por valor para as funções, isto deve ser evitado, já que torna a chamada da função mais lenta e aumenta a área ocupada no stack. A forma mais indicada para se passar estruturas é utilizar pointers, como será visto mais adiante. 18.3. Inicialização de estruturas Para alguns compiladores, é necessário que as variáveis locais sejam estáticas. struct s_carta { int carta; char naipe; /* Ouro, Copas, Paus e Espadas */ }; struct s_carta c = { 12, 'O' /* inicializa como dama de ouros */ }; 19. Pointer para estruturas Como vimos anteriormente, devemos passar as estruturas através de pointers e não por valor. Isto deve ser feito mesmo que não alteremos nenhum dos campos da estrutura. Exemplo: #include <stdio.h> struct s_data { int dia, mes, ano; }; typedef struct s_data sdata; void prtdata(sdata *); void main() { sdata data; data.dia = 15; data.mes = 3; data.ano = 1988; prtdata(&data); /* passa o endereço da estrutura */ } void prtdata(sdata *data) { printf("%d/%d/%d\n", (*data).dia, (*data).mes, (*data).ano); /* também pode usar: data->dia, data->mes, data->ano */ } Existem duas formas de se acessar os valores de uma estrutura através de pointers: 41 *(pointer).componente; ou pointer->componente; Exemplo: sdata data, *pdata; pdata = &data; (*pdata).dia = 31; ou pdata->dia = 31; (*pdata).mes = 12; ou pdata->mes = 12; (*pdata).ano = 1989; ou pdata->ano = 1989; 20. Arquivos A linguagem C possui alguns arquivos pré-definidos e que são automaticamente abertos quando um programa começa a ser executado. Eles são: stdin standard input (default = teclado) stdout standard output (default = vídeo) stderr standard error (default = vídeo) stdprn standard printer (somente alguns compiladores) Os arquivos pré-definidos stdin, stdout e stderr podem ser redirecionados pelo usuário. O arquivo stdprn é dependente do compilador, podendo existir ou não. Para estes arquivo pré-definidos, a leitura e impressão se passam como se estivéssemos utilizando um arquivo texto (leitura e gravação sequenciais). O conceito de arquivos em C é um pouco diferente do de outras linguagens. Na maioria das linguagens, o tipo de acesso (direto ou sequencial) está diretamente ligado à forma de abertura do arquivo, o que não acontece em C. Em C, o acesso a um arquivo pode ser direto ou sequencial, sendo que esta escolha é feita conforme as necessidades do usuário. O C possui funções que permitem o acesso tanto direto quanto sequencial e permitem um acesso bastante eficiente à arquivos. Forma de definição: FILE *fp; Para que esta definição esteja correta, é necessário que o arquivo stdio.h tenha sido incluído. 20.1. Funções para abertura e fechamento de arquivos Abertura de arquivos: FILE *fopen(char *, char *); fp = fopen(nome, modo); 42 Esta função abre o arquivo de nome "nome" com o modo "modo"."nome" e "modo" são strings de caracteres. Os modos de abertura possíveis são apresentados na tabela abaixo: Modo Tipo Read Write Cria Append r t s n n n r+ t s s n n rb b s n n n rb+ b s s n n w t n s s n w+ t s s s n wb b n s s n wb+ b s s s n a t n s s s a+ t s s s s ab b n s s s ab+ b s s s s Onde: • r ⇒ read • w ⇒ write • a ⇒ append • t ⇒ texto • b ⇒ binário • s ⇒ sim • n ⇒ não Em caso de sucesso na abertura, a função fopen retorna um ponteiro para o arquivo, o qual será utilizado como nome lógico do arquivo dentro do programa e, em caso de erro, retorna NULL. É responsabilidade do usuário verificar se o arquivo foi aberto ou não. Exemplo: fp = fopen("teste.dat", "r"); if (fp == NULL) /* ou if (!fp) */ { printf("\nErro: abertura de arquivo para leitura"); exit(1); /* retorna para o sistema operacional */ } O nome do arquivo pode indicar um nome simples ou um nome com path (caminho). Exemplos de nomes: "teste.dat" 43 "C:\programas\testes\teste.dat" A diferença básica entre os modos texto e binário reside no fato de que quando os arquivos são abertos no modo texto para gravação, o caracter NL (New Line) é convertido em dois: CR (Carriage Return) e LF (LineFeed) e quando são abertos no modo texto para leitura, ocorre o inverso. Para os aquivos abertos no modo binário, esta conversão não existe, havendo, portanto, uma correspondência de 1 para 1. Fechamento de arquivos: int fclose(FILE *); fclose(fp); Em caso de sucesso, a função retorna NULL e em caso de erro, um valor diferente de zero (0). Exemplo: if ((fp = fopen("teste.dat", "r")) == NULL) { printf("\nErro de abertura para leitura"); exit(1); } ... ... fclose(fp); 20.2. Principais funções para leitura e gravação seqüenciais Leitura: char *fgets(char *, int, FILE *); pchar = fgets(buffer, num, fp); Le caracteres do arquivo dado pof 'fp'. A leitura termina quando forem lidos 'num - 1' caracteres, ou quando encontrar o caracter NL (New Line) ou EOF (End Of File). Os caracteres lidos são colocados em 'buffer'. Se a leitura foi correta, a função retorna um pointer para 'buffer' e em caso de erro retorna NULL. O buffer deve ser grande o suficiente para que sejam colocados os caracteres '\0' e NL. Exemplo: char *pchar, buffer[100]; FILE *fp; ... pchar = fgets(buffer, 98, fp); ... int fgetc(FILE *); v_int = fgetc(fp); 44 Le um caracter do arquivo dado por 'fp' e retorna o seu código ASCII. Se a leitura chegar ao final do arquivo ou houver erro de leitura, é retornado EOF. Entretanto, deve-se usar esta função com certo cuidado, já que EOF pode ser um caracter válido em arquivos abertos no modo binário. int fscanf(FILE *, char *, ...); fscanf(fp, fmt, args); Funciona de forma semelhante à função scanf. Le as variáveis em 'args' com o formato 'fmt' do arquivo dado por'fp'. Em caso de sucesso, retorna o número de variáveis que foram lidas. Caso se chegue ao final do arquivo, é retornado EOF. Gravação: int fputs(char *, FILE *); fputs(str, fp); Grava a string 'str' no arquivo dado por 'fp'. A função retorna zero (0) se gravou bem e outro valor em caso de erro. int fputc(int, FILE *); fputc(carac, fp); Grava o caracter 'carac' no arquivo dado por 'fp'. Em caso de erro, retorna EOF, porém se o arquivo for aberto em modo binário, EOF poderá ser um caracter válido. int fprintf(FILE *, char *, ...); fprintf(fp, fmt, args); Funciona de forma semelhante à função printf. Grava as variáveis em 'args' com o formato 'fmt' no arquivo dado por 'fp'. Em caso de sucesso, retorna o número de caracteres que foram gravados e em caso de erro, retorna um número negativo. 20.3. Principais funções para leitura e gravação direta (binário) Normalmente, para acesso direto se abre o arquivo como binário, para que a correspondência de caracteres seja de 1 para 1. Funções para posicionamento: void rewind(FILE *); rewind(fp); Coloca o ponteiro interno de I/O no início do arquivo dado por 'fp'. int fseek(FILE *, long, int); fseek(fp, offset, origem); 45 Move o ponteiro interno de I/O do arquivo dado por 'fp' conforme 'offset' e 'origem'. 'offset' indica o número de bytes , a partir da origem 'origem', que o ponteiro deve se deslocar. Os valores possíveis para 'origem' são: 0 ou SEEK_SET - início do arquivo 1 ou SEEK_CUR - posição atual do ponteiro 2 ou SEEK_END - final do arquivo Note que a variável 'offset' é do tipo long int. Por exemplo, a instrução fseek(fp, 0l, 0) é equivalente a rewind(fp). A função fseek retorna zero (0) para sucesso e um valor diferente de zero (0) para erro. int fread(void *, int, int, FILE *); fread(buf, tam, nblocos, fp); Le para 'buf' 'nblocos' de 'tam' bytes do arquivo dado por 'fp'. A função fread retorna o número de blocos lidos. int fwrite(void *, int, int, FILE *); int fwrite(buf, tam, nblocos, fp); Funciona como fread, só que para gravação. A função retorna o número de blocos que foram gravados. Exemplo: Gravar e ler a variável double x de um arquivo. ... double x; ... fwrite(&x, sizeof(double), 1, fp); ... fread(&x, sizeof(double), 1, fp); Exercício: Fazer um programa inicialize um vetor de 100 elementos com os números de 0 a 99, o grave em um arquivo e o leia para outro vetor. #include <stdio.h> #include <stdlib.h> void main() { FILE *fp; int i; float v1[100], v2[100], *pv; for (pv = v1, i = 0; i < 100; i++) *pv++ = (float) i; /* inicializa o vetor */ /* abre o arquivo */ if ((fp = fopen("teste.dat", "wb")) == NULL) 46 { printf("\nErro de abertura: gravação"); exit(1); } fwrite(v1, sizeof(float), 100, fp); /* grava vetor */ fclose(fp); /* fecha o arquivo */ /* reabre o arquivo teste */ if ((fp = fopen("teste.dat", "rb")) == NULL) { printf("\nErro de abertura: leitura"); exit(2); } fread(v2, sizeof(float), 100, fp); /* le p/ novo vet */ fclose(fp); /* fecha o arquivo */ for(pv = v2, i = 0; i < 100; i++) printf("%.2f\n", *pv++); /* imprime vetor lido */ } Exercício: Fazer um programa que grave em um arquivo três vetores de 100 elementos cada, sendo o primeiro formado pelos números de 0 a 99, o segundo os 100 primeiros números pares e o terceiro os 100 primeiros múltiplos de 3. A seguir fechar o arquivo e reabrí-lo lendo e imprimindo cada um dos vetores. #include <stdio.h> #include <stdlib.h> #define TAM 100 void main() { FILE *fp; int i; float v1[TAM], *pv; /* Abre arquivo testando se houve erro. */ if ((fp = fopen("teste.vet", "wb")) == NULL) { printf("\nErro ao abrir arquivo inicial.\n"); exit(1); } for (pv = v1, i = 0; i < TAM; i++) *pv++ = (float)i; /* gera num. de 0 a 99 */ /* ** Aqui grava apenas um bloco com o tamanho de v1, ** o que equivale a gravar sizeof(float) x TAM bytes. ** Testa se gravou tudo. */ if (fwrite(v1, sizeof(v1), 1, fp) != 1) { printf ("\nErro ao gravar vetor 1.\n"); exit(2); } for (pv = v1, i = 0; i < TAM; i++) 47 *pv++ = (float) i * 2; /* vet: 0,2,4,...,198 */ /* ** Aqui grava TAM blocos com o tamanho de float, ** o que equivale a gravar sizeof(float) x TAM bytes. ** Testa se gravou tudo. */ if (fwrite(v1, sizeof(float), TAM, fp) != TAM) { printf ("\nErro ao gravar vetor 2.\n"); exit(3); } for (pv = v1, i = 0; i < TAM; i++) *pv++ = (float) i * 3; /* vet: 0,3,6,...,297 */ /* ** Aqui grava TAM blocos com o tamanho de float, ** o que equivale a gravar sizeof(float) x TAM bytes. ** Testa se gravou tudo. */ if (fwrite(v1, sizeof(float), TAM, fp) != TAM ) { printf ("\nErro ao gravar vetor 3.\n"); exit(4); }; fclose(fp); /* fecha o arquivo */ /* Reabre arquivo testando se houve erro.*/ if ((fp = fopen("teste.vet", "rb")) == NULL) { printf("\nErro ao reabrir o arquivo.\n"); exit(5); } fread(v1, sizeof(v1), 1, fp); /* le primeiro vetor */ for (pv = v1, i = 0; i < TAM; i++) printf("%f ", *pv++); /* imprime */ printf("\n\n"); /* pula 2 linhas */ rewind(fp); /* volta inicio arquivo */ /* ** a partir do inicio e com deslocamento v1, aponta ** para o segundo vetor */ fseek(fp, (long) sizeof(v1), 0); fread(v1, sizeof(float), TAM, fp); /* le seg. vetor */ for (pv = v1, i = 0; i < TAM; i++) printf("%f ", *pv++); /* imprime */ printf("\n\n"); /* pula 2 linhas */ rewind(fp); /* volta inicio arquivo */ /* ** a partir do início e com deslocamento de 2 x v1, ** aponta para o terceiro vetor */ fseek(fp, (long) sizeof(v1) * 2,0); fread(v1, sizeof(v1), 1, fp); /* le terceiro vetor */ 48 for (pv=v1, i=0; i<TAM; i++) printf("%f ", *pv++); /* imprime */ printf("\n\n"); /* pula duas linhas */ fclose(fp); /* fecha arquivo */ } Exemplo do uso de estruturas em arquivos: struct s_prop { char nome[20]; double mod; double coef; }; typedef struct s_prop sprop; sprop prop; sprop variasprops[100]; ... /* grava uma propriedade */ fwrite(&prop, sizeof(sprop), 1, fp); /* grava um elemento da estrutura */ /* grava vetor com várias propriedades */ fwrite(variasprops, sizeof(sprop), 100, fp); 21. Alocação dinâmica de memória A linguagem C possui uma série de funções que permitem alocar e liberar áreas de memória, fazendo com que se possa usar a memória gasta por variáveis, vetores, matrizes, listas, pilhas, etc. de uma forma mais eficiente. Quando necessário, o usuário pede uma área e a utiliza e, quando esta área não for mais necessária, ela é devolvida ao sistema operacional, de forma a poder serutilizada em outra etapa do programa. O esquema abaixo apresenta o mapa da memória de um programa em C. Área de Código Área de Variáveis Estáticas e Globais Stack Heap A área disponível para alocação dinâmica se encontra na "Heap". As principais funções para manipulação de memória estão definidas no header stdlib.h e são: 49 • malloc • calloc • realloc • free void *malloc(unsigned int); ptr = malloc(tam); Aloca uma área de 'tam' bytes. Se alocou corretamente, retorna um pointer para o primeiro byte da área alocada e em caso de erro retorna NULL. Exemplo 1: int *ptr; ... ptr = malloc(sizeof(int)); if (ptr == NULL) { printf("Erro de alocação"); exit(1); } *ptr = 10; ... Exemplo 2: int *ptr, *ptraux, i; ... ptr = malloc(sizeof(int) * 100); if (ptr == NULL) { printf("Erro de alocação"); exit(1); } ptraux = ptr; for (i = 0; i < 100; i++) *ptr++ = i; ptr = ptraux; ... Exemplo 3: /* ** Obtem área para um vetor do tipo float. ** O número de elementos do vetor é pedido pelo programa. ** Após a obtenção da área necessária s,o pedidos dados ** para o seu preenchimento. */ #include <stdio.h> #include <stdlib.h> void privet(float, int); void main() 50 { int num, i; float *pvet, *pinic; printf("\nEntre com número de elementos:\n"); scanf("%d", &num); pvet = (float*) malloc(num * sizeof(float)); if (!pvet) { printf("\nErro. Falta de memória.\n"); exit(1); } pinic = pvet; for (i = 0; i < num; i++) { printf("\n%d -> ", i + 1); scanf("%f", pvet++); /* não funciona no Turbo C */ } /* ** para o Turbo C fazer: for (i = 0; i < num; i++) { float aux; printf("\n%d -> ", i + 1); scanf("%f", &aux); *pvet++ = aux; } */ pvet = pinic; prtvet(pvet, num); /* imprime o vetor */ free(pvet); } void prtvet(float *p, int n) { int i; for (i = 0; i < n; i++) printf("\n\%d -> %f", i + 1, *pv++); } void *calloc(unsigned int, unsigned int); ptr = calloc(num, size); Aloca uma área de 'tam' * 'num' bytes. Se alocou corretamente, retorna um pointer para o primeiro byte da área alocada e em caso de erro retorna NULL. A área alocada é automaticamente zerada. Exemplo 1: int *ptr; ... ptr = calloc(1, sizeof(int)); if (ptr == NULL) 51 { printf("Erro de alocação"); exit(1); } *ptr = 10; ... Exemplo 2: int *ptr, *ptraux, i; ... ptr = calloc(100, sizeof(int)); if (ptr == NULL) { printf("Erro de alocação"); exit(1); } ptraux = ptr; for (i = 0; i < 100; i++) *ptr++ = i; ptr = ptraux; ... void free(void *); free(ptr); Libera a área alocada por malloc, calloc ou realloc. Exemplo: int *ptr, *ptraux, i; ... ptr = malloc(sizeof(int) * 100); if (ptr == NULL) { printf("Erro de alocação"); exit(1); } ptraux = ptr; for (i = 0; i < 100; i++) *ptr++ = i; ptr = ptraux; ... free(ptr); ... void *realloc(void *, unsigned int) nptr = realloc(old, tam); A função realloc altera o tamanho da área alocada por malloc ou calloc. O novo tamanho pode ser maior ou menor do que o tamanho original. Quando chamada, retorna um pointer para a nova área alocada, sendo 'old' um pointer para a área antiga e 'tam' o novo tamanho em bytes. Em caso de erro, a função retorna NULL e o programa deve ser interrompido, já que a área original poderá estar comprometida. Exemplo: 52 #include <stdio.h> #include <stdlib.h> void prtvet(float*, int); void main() { int num, i; char *p; p = (char*) malloc(23); strcpy (p, "AQUI TEM 22 CARACTERES"); printf("\n%s\n", p); p = realloc(p, 24); strcat(p, "."); printf("\n%s\n", p); free(p); } 53 1. Introdução 1.1. Características 1.2. Histórico 1.3. Características de um programa 1.4. Formato de um programa em C 1.5. Exemplo de um programa 2. Identificadores 3. Tipos de dados para variáveis e funções 3.1. Standard (já definidos na linguagem) 3.2. Modificadores de tipo 4. Declaração de variáveis 4.1. Variáveis locais 4.2. Parâmetros formais 4.3. Variáveis globais 4.4. Modificadores de classe de armazenamento 4.4.1. Variáveis automáticas - auto 4.4.2. Variáveis externas - extern 4.4.3. Variáveis estáticas - static 4.4.4. Variáveis register 5. Operadores 5.1. aritméticos 5.2. lógicos ou boleanos 5.3. relacionais 5.4. Outros operadores 5.5. precedência (da maior para a menor) 6. Funções de entrada e saída 6.1. printf() 6.2. putchar() 6.3. puts() 6.4. scanf() 6.5. getchar() 6.6. gets() 7. Instuções de controle 7.1. while 7.2. for 7.3. do while 7.4. if e derivados 7.5. switch 7.6. continue 7.7. break 8. Funções 9. Arrays unidimensionais (vetores) 9.1. Strings 10. Arrays bidimensionais (matrizes bidimensionais) 11. Pointers (ponteiros) 11.1. Definição 11.2. Inicialização de pointers na definição 11.3. Passagem de parâmetros por endereço (referência) 12. A relação entre pointers e arrays 13. Pointers e arrays bidimensionais 13.1. Arrays de pointers (ragged arrays) 14. Definição de novos tipo 15. Ponteiro para percorrer array de ponteiros. 16. Ponteiros para funções 17. Pré-processador e diretivas 17.1 #define 17.2 #include 17.3. Compilação condicional 18. Tipos construídos 18.1. Tipo enumerado 18.2. Estruturas 18.3. Inicialização de estruturas 19. Pointer para estruturas 20. Arquivos 20.1. Funções para abertura e fechamento de arquivos 20.2. Principais funções para leitura e gravação seqüenciais 20.3. Principais funções para leitura e gravação direta (binário) 21. Alocação dinâmica de memória
Compartilhar