Linguagem C Curso de Nivelamento LCG - 2004. AVISO Estas transparências contêm apenas o que eu me...

Post on 18-Apr-2015

108 views 6 download

Transcript of Linguagem C Curso de Nivelamento LCG - 2004. AVISO Estas transparências contêm apenas o que eu me...

Linguagem C

Curso de Nivelamento LCG - 2004

AVISO• Estas transparências contêm apenas

o que eu me lembro da linguagem• Ultimamente, minha memória tem

falhado bastante • Detalhes devem ser procurados nos

manuais

Linguagem C • Proposta por Kernighan e Ritchie (1970)• Linguagem para desenvolvimento de sistemas

▪ UNIX supostamente deveria ser todo escrito em “C”▪ Permite construções pouco seguras

• Ponteiros podem ser usados para endereçar toda a memória

• Muito popular e usada até hoje• Variantes mais atualizadas e +seguras foram

sendo propostas▪ ANSI C▪ Objective C (NextStep)▪ C++▪ C# (Microsoft .NET)

• 90% do software básico de hoje é escrito em C ou C++

Programa em C• Organizado em “unidades de

compilação” ▪ Cada unidade é um arquivo com

extensão .c podendo conter diversas diretivas de inclusão de outros arquivos-fonte:#include <xpto.h>

#include ”foobar.h”

▪ arquivo.h “include”▪ Nome entre < e >: include em diretórios-

padrão ▪ Nome entre “aspas”: include no mesmo

diretório que o arquivo .c

Programa em C• Um programa simples tem apenas

uma unidade• Unidade principal é a que contém a

função main

Declarações e Definições• Arquivos-fonte contêm

▪ Declarações• Informam nomes e tipos de variáveis e funções

▪ Diretivas para o pré-compilador• #include • #define • #if / #ifdef

▪ Definição de funções• Onde os comandos são efetivamente escritos

▪ Comentários• /* Comentário */• // Também aceito em alguns compiladores (não é

padrão)

Programa Exemplo#include <stdio.h>

char *titulo = "Programa Exemplo\n";/* * Programa para imprimir argumentos dados */int main (int argc, char * argv []) { int i; printf (titulo); printf ("Argumentos:\n"); for (i = 0; i < argc; ++i) { printf ("%d: %s\n", i, argv[i]); } return 0;}

Diretiva de inclusão

Declaração de variável global

Função main(Programa principal)

uso da função printf definida em

stdio.h

parâmetros da função mainargc: número de argsargv: array de strings

Variável local

função main retorna 0 para indicar sucesso

Pré-processador• Processa as diretivas #...• Compilador na verdade vê o

programa fonte pré-processado• Pré-processador não entende “C”,

apenas processa textos• #include inclui o arquivo indicado

como se tivesse sido digitado naquela linha

#define• Também chamado de macro• #define nome texto

▪ sempre que nome for citado no programa, será substituído por texto

▪ Exemplo:#define X a , b , c

f (X);

▪ ... é equivalente af (a , b, c);

#define• É possível ter macros parametrizadas• #define nome(arg1,...argN) texto

▪ texto contém os símbolos arg que são substituídos pelos valores passados quando do uso

▪ Exemplo:#define MAX(a,b) a > b ? a : b

x = MAX(b,c);

▪ ... é equivalente ax = b > c ? b : c;

#define• Cuidado! substituição é literal:

#define MAX(a,b) a > b ? a : b

x = MAX(x & 2, 1);

▪ ... é equivalente ax = x & 2 > 1 ? x & 2 : 1;

▪ ... que é equivalente ax = (x & (2 > 1)) ? (x & 2) : 1;

• Nesses casos, é melhor usar parênteses

#define MAX(a,b) ((a)>(b) ? (a) : (b))

Compilação condicional• Expediente para obter variações do

mesmo código adaptado a diversos ambientes

• Diretivas #if , #ifdef, #else, #endif• Exemplo:

#ifdef WIN... código que contorna bug do Windows

#else... código normal

#endif

Dados em C• Tipos nativos

▪ int, float, double, char▪ modificadores : unsigned, signed, long, short

• Ponteiros: tipo *• Arrays: tipo [tamanho]• Enumerações:

▪ enum { nomes }• Estruturas:

▪ struct { campos }▪ union { campos }

Declaração de variáveis em C?qualif??alocação? tipo var ? = valor?;

• qualif▪ volatile pode mudar inesperadamente

(multithreading)▪ const valor constante

• alocação ▪ extern definido em outra unidade de

compilação▪ static tempo de vida estático ▪ auto alocada na pilha de execução▪ register alocada em registradores

Declaração de variáveis em C• tipo é um nome de tipo nativo ou

previamente declarado • var é

▪ identificador▪ *var▪ var [tamanho]

• = valor▪ inicialização da variável▪ valor tem que ser uma expressão

constante • uma expressão constante é aquela que pode

ser avaliada pelo compilador

Exemplosregister int i, j, k;

auto unsigned char a[10];

static double *p[10];

float **f;

const double pi = 3.1416;

extern unsigned long int param;

struct { int s1; char c; } s;

enum { pera, limao, maca } fruta;

Declaração de tipostypedef decl;• decl tem o mesmo formato de uma

declaração de variáveis, mas sem qualificadores

• identificadores que aparecem em decl vão ser associados a nomes de tipos, e não variáveis

• nomes de tipos podem ser usados em declarações posteriormente

• Exemplo:typedef int * ponteiro;

ponteiro p, *pp;

Arrays• Coleção de valores do mesmo tipo

alocados em seqüência na memóriaint a [10];▪ 10 variáveis inteiras referenciadas como a[0],

a[1], ... a[9]

• Arrays multidimensionais são alocados com os índices mais à esquerda variando mais rápidoint b[2][2][2] = {5,2,3,9,1,2,3,4};

5 2 3 9 1 2 3 4

b[0][0][0] b[0][0][1] b[0][1][0] b[0][1][1] b[1][0][0] b[1][0][1] b[1][1][0] b[1][1][1]

Endereços crescentes de memória

Inicialização de arrays• Um array pode ser inicializado assim que

declarado• Lista de valores é posta entre chaves• Para arrays multidimensionais, a

inicialização é por linha• Exemplo

int i [10] = { 1, 2, 3, 4, 5, 5, 4, 3, 2, 1 };

int m [3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } };

Inicialização de arrays• Arrays de caracteres podem ser

inicializados com uma cadeia de caracteres entre aspas (string)▪ É assumido um caractere nulo no fim da

string• Exemplo

char s [10] = ”alo”;

• É o mesmo quechar s [10] = {’a’,’l’,’o’,′\0’};

• Observe que posições não inicializadas têm valor indefinido

Ponteiros• Variáveis que contêm o endereço de

outra variável• Recurso poderoso mas perigoso!int i = 5, *p = &i;

5 2124

i

2124

p

2126

Identificadores

Endereços

5i

p

Endereço de i

Ponteiros e Arrays• Um ponteiro e um array muitas vezes

se confundem uma vez que o nome de um array é interpretado como o endereço do primeiro elemento do array

• Por exemplo:int a [3] = {1,2,3};int *p, *q;p = a;q = p;

• Após este código, a, p e q referem-se todos ao mesmo endereço

Ponteiros e Arrays• De forma semelhante, ponteiros

podem ser indexados como arrays:p [1] = 7;

*(p+1) = 7;

• Entretanto, arrays e ponteiros não são a mesma coisa▪ Um ponteiro ocupa uma palavra de

memória e um array várias▪ Um array não pode mudar de endereço

• Qual o significado de a = p ?

Estruturas• Tipo agregado heterogêneo• Conjunto de campos com nomes• Imprescindível para construção de

estruturas de dados mais complexas• Base para o conceito de classe em

C++

Estruturas• Forma geral:

struct ?nome? {decl;...decl;

} var;onde▪ nome é o nome da estrutura▪ decl é uma declaração de campo (semelhante

às declarações de variáveis;▪ var é uma ou mais variáveis que se quer

declarar• podem ser simples, arrays ou ponteiros

Estruturas• Exemplo: a declaração

struct S {int a;

char b, *s, n[10];

struct S * ptr;

} v1, v2 [10], *q;▪ ... declara uma estrutura v1, um array

de 10 estruturas v2 e um ponteiro para estrutura q

Estruturas• Os campos da estrutura são acessados

escrevendo o nome da variável e o nome do campo separados por um ponto:v1.a = 1;

*v2[4].s = ’a’;

(*q).n[2] = ’b’;

• Se p é um ponteiro para uma estrutura, pode-se substituir a construção (*p).campo por p->campoq->n[2] = ’b’;

Uniões• Semelhantes a struct, mas todos os

campos ocupam a mesma área de memória

• Usar com muito cuidado!• Sintaxe: trocar struct por union• Exemplo:

union {int a; double b; char c;

} x;x.a = 10;/* Quanto vale x.b ??? */

Expressões• Compostas de operandos,

operadores e parênteses• Operandos

▪ variáveis: a, b, c, *p, t[b], t[a+5] ▪ constantes: 1, 1L, 2.4, ’a’, ”xxxxx” ▪ chamadas de função: f(), g(2, 4)

Expressões• Operadores

▪ Aritméticos: +, -, /,*▪ Comparação: ==, !=, >, <, >=, <=▪ Lógicos: &&, ||, !▪ Bit a Bit: &, |, ~, ^▪ Condicional: expr ? expr : expr▪ Referência e Dereferência: & , *▪ Cast: (tipo) expr

• Parênteses: servem para alterar a ordem de precedência▪ Precedência funciona quase sempre como se

espera:• a * b + c == d && *x = 1

▪ Na dúvida: olhar o livro ou usar parênteses

Expressões com efeito colateral• Ao avaliar a expressão, alguma variável

pode mudar de valor. Por exemplo: a = b = 4 ;▪ a recebe o valor da expressão b = 4▪ expressão vale 4, mas tem como efeito

colateral atribuir 4 a b• Chamar funções pode ter efeito colateral:

▪ Ex.: f(&a) pode alterar o valor de a (ou de qualquer variável global)

• Outros operadores com efeito colateral: ▪ Misturas de atribuição com operadores

binários: +=, *=, /=, &=, |= etc▪ Pré e Pós incremento/decremento: ++ e ––

• a = x++; a = x; x = x+1;• a = ++x; x = x+1; a = x;

Funções• Encapsulam os algoritmos• Forma geral:

tipo nomefunção (arg, ... arg) {declaração;...declaração;comando;...comando;

}

tipo do valor de retorno Lista de

parâmentros

Valor de retorno de Funções• O tipo de retorno pode ser

▪ um tipo simples: int, double, char, etc;▪ um ponteiro (endereço)

• Se a função é avaliada e não passa por nenhum comando return, então o valor retornado é indefinido

• Se a função não retorna valores, pode-se usar void como tipo de retorno

• Mesmo que uma função retorne um valor, este não necessariamente é usado por quem chama:i = g(5);g(9);

Argumentos de funções• Lista de argumentos é semelhante à

declaração de variáveis• Parâmetros são passados por valor, à

exceção de arrays e structs▪ Um parâmetro passado por valor é

equivalente a uma variável local inicializada com o valor passado

▪ Para alterar uma variável passada como argumento, usa-se ponteiros

Passagem de Parâmetrosint j = 4;

void f (int i) {i = 5;

}

int main () {f (j);

printf (”%d\n”, j);

}• Resultado: 4

Passagem de Parâmetrosint j[1] = { 4 };

void f (int i[]) {i[0] = 5;

}

int main () {f (j);

printf (”%d\n”, j[0]);

}• Resultado: 5

Passagem de Parâmetrosint j = 4;

void f (int *i) {*i = 5;

}

int main () {f (&j);

printf (”%d\n”, j);

}• Resultado: 5

if• Comando condicional fundamental• Formato1:

▪ if (expr) comando1▪ Se expr avaliar como qualquer valor

diferente de 0, comando1 é executado.

• Formato2: ▪ if (expr) comando1 else comando2 ▪ Se expr avaliar como qualquer valor

diferente de 0, comando1 é executado, senão, comando2 é executado

Comando composto• Vários comandos sintaticamente equivalentes

a um só• Formato: { decl; ... decl; comando; ....

comando; }• OBS.:

▪ Todo comando é terminado por um ponto-e-vírgula▪ Comandos compostos não precisam de ponto-e-

vírgula

• Ex.: if (a == b) {

c = d; printf (”OK”);

} else { c = b; printf (”Não OK”);

}

switch• Permite selecionar um entre vários

caminhos• Formato:

switch { expr } {case val : comando; ... comando; ...case val : comando; ... comando; default: comando; ... comando;

}• Avalia expr , procura o val igual e começa a

executar os comandos subseqüentes• Se não encontrar, executa os comandos

após a cláusula default (se existir)• OBS.: o switch não pula os demais case’s.

Para encerrar o case , usar o comando break

break• Interrompe o comando composto

atual e retoma a execução no próximo comando

• Muito usado em switches e laços de repetição

• Ex.:switch (i) {

case 1: case 2: i = 3;case 3: case 4: k = 4; break;case 5: k = 9; break;default: k = 10;

}

while ... do e do ... while• while (expr) comando

▪ enquanto expr é diferente de 0, comando é repetido

▪ pode não executar comando nenhuma vez

• do comando while (expr)▪ repete comando enquanto expr é

diferente de 0▪ sempre executa comando pelo menos

uma vez

for• for (expr1; expr2; expr3) comando

▪ equivale aexpr1;while (expr2) {

comando;expr3;

}▪ 90% das vezes, alguma coisa como

for (i = 0; i < n; i++) {a [i] = ...;

}

bibliotecas• fornecem o restante da funcionalidade da

linguagem▪ entrada e saída▪ controle de processos e multiprogramação▪ comunicação c/ sistema operacional▪ matemática▪ gráficos e multimídia▪ etc

• usar uma biblioteca requer:▪ inclusão dos headers da biblioteca no

programa cliente▪ “link-edição” da biblioteca com o executável

Entrada e Saída (I/O)• Duas bibliotecas padrão

▪ I/O de “baixo nível”• Usa chamadas diretas do Sistema

Operacional• entrada e saída sem “buferização”•#include <io.h>

▪ I/O de “alto nível”• Usa I/O de baixo nível• entrada e saída buferizadas• suporta formatação de dados binários em

texto e vice-versa: scanf, printf•#include <stdio.h>

Entrada e Saída c/ stdio• Dados são lidos/escritos em arquivos

▪ Estado de um arquivo é guardado em memória por um tipo struct chamado FILE

▪ RFunções que lidam com arquivos têm parâmetros do tipo FILE*

• Existem 3 arquivos de entrada/e saída default:▪ stdin arquivo de entrada default▪ stdout arquivo de saída default▪ stderr arquivo de saída de mensagens de erro▪ normalmente associados ao terminal

• (ver o tópico redirecionamento na documentação do seu Sistema Operacional)

printf• saída formatada• uso: printf (“formato”, expr1, ...

exprN);• igual a fprintf (stdout,

“formato”, ...);• expr são os valores que se quer

imprimir• formato é uma string comum, mas

intercalada com especificadores de formato

printf• Alguns especificadores de formato:

▪ %c escreve um caractere▪ %d escreve um inteiro em decimal▪ %x escreve um ▪ %ld escreve um inteiro longo em decimal▪ %g escreve um float ou double▪ %s escreve uma cadeia (string)

• Exemplo:int i = 10; char c = ‘a’; double d = 0.315155;

printf (”i vale %d, c vale %c e d vale %g”, i, c, d);

printf• Opcionalmente, especificadores

podem incluir modificadores entre o % e a letra. Ex.:▪ %10d escreve usando 10 caracteres

(preenchido com brancos)▪ %010d escreve usando 10 caracteres

(preenchido com zeros)▪ %10.3g escreve em 10 caracteres,

mantendo 3 casas decmais

scanf• análogo do printf para leitura• uso: scanf (“formato”, addr1, ... addr2);• retorna o número de valores lidos• addr são endereços de variáveis• Exemplo:

int i; char c; float f;if (scanf (”%d %c %f”, &i, &c, &f) != 3)

fprintf (stderr, ”falha ao converter algum dado\n”);

• Importante: como sempre ao manipular endereços, é preciso cuidado redobrado:scanf (”%d %c %g”, &i, &c, &f);

▪ scanf espera um double e não um float!

Alocação de Memória• variáveis estáticas (static)

alocadas de uma vez só quando da carga programa

• variáveis automáticas (auto) alocadas quando o fluxo de execução entra num bloco e desalocadas quando sai

• Para ter estruturas de dados de tamanho variável é necessário alocar memória dinâmicamente

malloc• #include <stdlib.h>• void * malloc (nbytes)

▪ retorna um ponteiro para uma área livre de nbytes bytes

▪ área não é inicializada (pode conter lixo)▪ para saber quantos bytes são

necessários para guardar um dado, usar a função sizeof(tipo)

▪ Exemplo: queremos alocar dinâmicamente um array de n doubles

double *a = (double*) malloc(sizeof(double)*n);

free• desfaz o trabalho do malloc• retorna a memória alocada para o

sistema• uso: free (p); onde p é um ponteiro

alocado por malloc, calloc ou realloc

Programa exemplo: listas#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

typedef struct NoStruct {

int val;

struct NoStruct * prox;

} NoLista, * Lista;

Programa exemplo: listasvoid push (Lista* pLista, int n) { Lista tmp = (Lista)

malloc (sizeof (NoLista));assert (pLista != NULL);tmp->val = n;tmp->prox = *pLista;*pLista = tmp;

}

int top (Lista lista) {assert (lista != NULL);return lista->val;

}

Programa exemplo: listasvoid pop (Lista* pLista) {

Lista tmp;assert (pLista != NULL);assert (*pLista != NULL);tmp = *pLista;*pLista = tmp->prox;free (tmp);

}

void print (Lista lista) {if (lista == NULL) {

printf ("\n");} else {

printf ("%d ", top(lista));print (lista->prox);

}}

Programa exemplo: listas

int main () {Lista l = NULL;push (&l, 1); push (&l, 2); push (&l, 3);print (l);pop (&l->prox);print (l);return 0;

}