Laboratório de Programação Prof. Oscar Luiz Monteiro de Farias [email protected] Ementa: Equivalente ao sumário do livro: The C Programming Language, Second edition. Autores: Brian W. Kernighan, Dennis M. Ritchie Bibliografia The C Programming Language, Second edition. Brian W. Kernighan, Dennis M. Ritchie. Internet hyperlinks Capítulo 1 Sucinta introdução à linguagem C Analisar programas em C com os elementos essenciais da linguagem: Variáveis e constantes Aritmética Fluxo de controle Funções Rudimentos de E/S Primeiro programa Imprima as palavras: “hello, world” Primeiro programa – obstáculos Aonde criar o texto do programa? Como compilá-lo? Como carregá-lo? Como executá-lo? Aonde e como ver o resultado de sua execução? Código de helloworld.c #include <stdio.h> main() { printf("hello, world\n"); } --------------------------prog01-chap01-pg09.c Etapas: Usar um editor (ex. gedit) para criar o texto do programa, salvando-o no arquivo helloworld.c Compilar o programa (usamos o compilador gcc) $ gcc helloword.c -o nome_do_executavel <enter> Executar o programa $ nome_do_executavel <enter> Verificar a saída no terminal Integrate Development Environment Para facilitar o desenvolvimento de programas: • • • • • • Netbeans http://netbeans.org/ Eclipse http://www.eclipse.org/ Anjuta http://www.anjuta.org/ Geany http://www.geany.org/ Code::Blocks http://www.codeblocks.org/ … Programas em C Um programa consiste de funções e variáveis As funções contêm comandos que especificam as operações a serem realizadas As variáveis armazenam os valores usados nas operações delas resultantes main () é uma função especial. É o entry-point de execução do programa. Os programas em C iniciam aí a sua execução. Todo programa deve ter uma função main (). Detalhes do primeiro programa #include <stdio.h> informa ao compilador para incluir informações da biblioteca padrão de E/S. Os parênteses após o nome da função (ex. main (...)) delimitam a lista de argumentos. Os comandos de uma função são delimitados por chaves → { statement } Invoca-se uma função, indicando-se o nome da função, seguido da lista de argumentos delimitada por parênteses. Ex. printf(“helloworld\n”); prog02-chap01-pg12 (1) Comentário: quaisquer caracteres situados entre /* e */ são ignorados pelo compilador Podem aparecer em qualquer lugar do programa aonde seja válido um espaço em branco, uma marca de tabulação ou uma nova linha. prog02-chap01-pg12 (2) Em C deve-se declarar todas as variáveis antes de usá-las; Normalmente no início das funções e antes de qualquer comando executável; A declaração indica o nome e o tipo da variável; Os valores assumidos por um determinado tipo (ex. int) depende das características de hardware da máquina; prog02-chap01-pg12 (3) Alguns tipos de dados básicos em C: int -16bits -32768<=valor<=32767 float - 32 bits - com pelo menos 6 dígitos significativos 10-38 <=valor<=10+38 char - um único byte - caracter short - inteiro curto long - inteiro longo double – ponto flutuante com dupla precisão A faixa de valores assumidos depende do hw; prog02-chap01-pg12 (4) Outros tipos de dados em C: Vetores Estruturas Pointers Funções Em C os comandos individuais são terminados por ; prog02-chap01-pg12 (5) Semântica do loop while while (fahr <= upper) { statement } A condição entre parênteses é testada. Se verdadeira, o corpo do while é executado. A condição é novamente testada, e se verdadeira, o corpo do while é executado novamente, e assim por diante... Quando a condição se tornar falsa o corpo do while não será executado e a execução continuará no comando que se segue ao loop. prog02-chap01-pg12 (6) A organização lógica do programa é enfatizada pela identação (não faz parte da linguagem); Facilita o entendimento dos programas; Recomendações: um comando por linha / deixar espaços em torno dos operadores; Na divisão entre inteiros o resultado é truncado. Assim 2/3 = 0; prog02-chap01-pg12 (7) printf é uma função geral de conversão de formatos. Seu primeiro argumento é uma cadeia de caracteres a ser impressa, onde cada sinal % indica aonde um dos outros argumentos (segundo, terceiro, …) deverá ser substituído, e também sob que formato deverá ser impresso. %d – inteiro decimal %s – cadeia de caracteres %f – ponto flutuante prog04-chap01-pg14 (1) O comando for (outra forma de loop) for (expr1; expr2; expr3) statement É equivalente a: expr1; while (expr2) { statement expr3; } prog04-chap01-pg14 (2) expr1 é executada uma vez, antes do loop ser iniciado; expr2 é o teste ou condição para o controle do loop; expr3 corresponde ao passo de incremento e eventalmente altera o valor de expr2, podendo ocasionar o fim do loop; prog04-chap01-pg14 (3) Escolhendo entre os loops while e for: for é mais apropriado para loops nos quais a inicialização e incrementos são comandos simples e logicamente relacionados, pois é mais compacto que o while e mantém os comandos de controle do loop juntos e no mesmo local. prog05-chap01-pg17 (1) O uso de números mágicos em um programa não é boa prática de programação (ex. 0, 300, 20, etc.) Dificultam o entendimento do código Difíceis de serem alterados de forma sistemática Solução: substituí-los por nomes simbólicos ou constantes simbólicas prog05-chap01-pg17 (2) # define nome texto substituto … # define LOWER 0 /* lower limit of table */ #define UPPER 300 /* upper limit */ #define STEP 20 /* step size */ LOWER, UPPER e STEP são constantes simbólicas e não variáveis Por convenção são escritos em MAIÚSCULAS para serem facilmente identificados Entrada e Saída de caracteres (i) No modelo de E/S suportado pela biblioteca padrão o texto de E/S é manipulado como um fluxo de caracteres, não importando a sua origem ou destino. Um fluxo de texto é uma seqüência de caracteres particionada em linhas. Cada linha consiste em zero ou mais caracteres seguidos por um caracter de nova linha (\n) A biblioteca é responsável por fazer com que cada linha da E/S esteja de acordo com o modelo. Entrada e Saída de caracteres (2) A biblioteca padrão dispõe de uma série de funções para E/S As mais simples são: getchar e putchar getchar – lê o próximo caracter de entrada do fluxo de texto e o retorna como seu valor Após c = getchar (); a variável c contém o caracter lido O fluxo de textos de entrada normalmente tem origem no teclado, mas pode vir de arquivos. Entrada e Saída de caracteres (3) putchar (c) imprime um caracter (coloca-o no fluxo de saída) a cada vez que é invocada O fluxo de saída é normalmente direcionado para o terminal O valor impresso é o conteúdo de c, sob a forma de um inteiro Invocações a putchar e a printf podem ser intercaladas e a saída corresponderá à ordem de invocação das funções prog06-chap01-pg18.c (1) Programa que copia a entrada (teclado) para a saída (terminal), um caracter por vez read a character while (charater is not end-of-file indicator) output the character just read read a character prog06-chap01-pg18.c (2) O operador != significa não é igual a O caracter que aparece na tela é armazenado internamente apenas como um padrão de bits O tipo char foi desenvolvido para armazenar o padrão de bits correspondente a um caracter Em C pode-se usar um tipo int para esta finalidade O programa usa int para distingüir quando não há mais caracteres na entrada. Neste caso, getchar retorna EOF, um valor distinto de qualquer caracter real EOF é um inteiro definido em <stdio.h> prog07-chap01-pg19.c Segunda versão do programa que copia a entrada (teclado) para a saída (terminal), um caracter por vez Entrada centralizada Mais fácil de se ler (para os programadores em C) Os parênteses envolvendo a atribuição (c = getchar()) são necessários, pois a precedência de != é superior à de =. Efeito não desejado de se atribuir a c o valor 0 ou 1, dependendo se a invocação a getchar encontrou ou não o fim de arquivo. prog08-chap01-pg19.c Programa que conta quantos caracteres há no texto de entrada Operador ++, que significa incremento de um ++nc é equivalente a nc = nc + 1 ++nc é diferente de nc++, embora ambos incrementem c de um O tipo long representa inteiros que ocupam pelo menos 32 bits (em algumas máquinas o valor de um int é no máximo de 32767) %ld informa a printf que o argumento correspondente é um long prog09-chap01-pg20.c Outra versão do programa que conta quantos caracteres há no texto de entrada Possibilita contar muito mais caracteres, por armazenar o número de caracteres lidos em um double (ponto flutuante de dupla precisão) A contagem dos caracteres é realizada na definição das condições de contorno do for e não em seu corpo As regras do C exigem que o comando for tenha um corpo. No caso, o comando nulo: ; prog10-chap01-pg20.c (1) Programa para contar quantas linhas há no texto de entrada. Lembra que em C um fluxo de texto é uma seqüência de caracteres particionada em linhas. Cada linha consiste em zero ou mais caracteres seguidos por um caracter de nova linha (\n) Assim, contar linhas, é equivalente a contar o numero de caracteres de new line (\n) no texto de entrada. prog10-chap01-pg20.c (2) O comando if if (condition) statement Significado do if A condição entre parênteses é testada. Se verdadeira, executa statement (ou grupo de statements entre chaves) == é a notação de C para “é igual a” = é a notação de C para o comando de atribuição prog10-chap01-pg20.c (3) Um caracter escrito entre aspas simples – 'c' representa um valor inteiro igual ao valor numérico do caracter no conjunto de caracteres da máquina (constante de caracter). 'A' é uma constante de caracter. No conjunto de caracteres ASCII seu valor é 65, a representação interna do caracter A. O valor de '\n' é 10 em ASCII. '\n' representa um único caracter (lembrar da seqüência de escape). prog11-chap01-pg21.c (1) Programa para contar linhas, palavras e caracteres Uma palavra é qualquer seqüência de caracteres que não contém um caracter de espaço, tabulação ou nova linha. Versão simplificada do programa wc do unix. nl = nw = nc = 0; /* atribui às três variáveis o valor zero */ Uma atribuição tem um valor e é associativa, da direita para a esquerda. Equivalente a nl = (nw = (nc = 0)); prog11-chap01-pg21.c (2) O operador || significa o ou lógico Analogamente && significa o e lógico As expressões conectadas por && ou || são avaliadas da esquerda para a direita. A avaliação terminará assim que a veracidade ou falsidade for conhecida. Forma geral do comando else if (expression) statement1 else statement2 statementi pode ser {statementi*} Vetores em C Indica uma coleção de variáveis do mesmo tipo, referenciada por um nome comum. O acesso a um elemento específico dentro desta coleção de variáveis se dá através de um índice. Ex.: int ndigito[10]; ndigito[i] Em C, os subscritos (índices) dos vetores sempre iniciam de 0 (zero). Um subscrito pode ser qualquer expressão inteira, variável inteira ou constante inteira. prog12-chap01-pg24.c Programa para contar o número de ocorrências de cada dígito, de caracteres de espaço em branco (space, tab, new line) e de todos os outros caracteres. Usa um vetor – int ndigito[10] - para armazenar o número de ocorrências de cada dígito. O programa usa propriedades da representação em caracteres dos dígitos. Se o caracter armazenado em c é um dígito o seu valor numérico é c – '0' Decisões múltiplas em C (1) if (condition1) statement1 else if (condition2) statement2 ... ... else statementn+1 Decisões múltiplas em C (2) As condições são avaliadas em ordem, a partir do topo, até que uma das condições (conditioni )seja satisfeita; nesse ponto statementi é executado e a construção inteira é terminada. (statementi pode representar vários statements envolvidos por chaves). Se nenhuma das condições for satisfeita, o statementn após o else é executado, se houver. Se o else e statementn forem omitidos nenhuma ação é tomada. Alternativa para decisões múltiplas: o comando switch. prog13-chap01-pg26.c (1) C não possui operador de exponenciação. O programa define uma função power(m, n) que eleva um inteiro positivo m à potència inteira positiva n. Biblioteca padrão: pow(x, y) Protótipo → int power(int m, int n); parâmetros prog13-chap01-pg26.c (2) argumentos printf("%d %d %d\n", i, power(2,i), power(-3,i)); parâmetros int power(int base, int n) { … } Funções (1) Forma de se encapsular alguma computação. Equivalente a uma sub-rotina ou função em Fortran e a procedure ou função em Pascal. Idéia: não se preocupar como uma tarefa é realizada e sim o que é realizado. Permite efetuar a decomposição de uma grande tarefa em várias tarefas menores. Essencial no desenvolvimento de sistemas mais complexos. Funções (2) Forma geral de definição de uma função: tipo-retornado nome-da-função (lista de declarações de parâmetros) { declarações statements } As definições de funções podem aparecer em qualquer ordem em um único arquivo fonte ou em arquivos diversos. Funções (3) Protótipo de função: declaração que deve combinar com a definição da função. Envolve apenas tipo-retornado nome-da-função (lista de declarações de parâmetros); Parâmetro (argumento formal): variável nomeada na lista entre parênteses em uma definição de função. Argumento (argumento real): variável ou valor usado na invocação (chamada) de uma função. Funções (4) Os nomes dos parâmetros e argumentos não precisam ser idênticos. Os nomes dos parâmetros são opcionais no protótipo da função. A função retorna o seu valor (que é de um tipo especificado) por meio de um comando return expressão; Uma função não precisa retornar um valor. Neste caso, o tipo-retornado é void. Funções (5) return; faz com que o controle, mas não um valor, seja retornado ao trecho de código que invocou a função. Equivalente a chegar a }, o delimitador de fim da função. main é uma função e pode possuir um comando return ao seu final (retorna um valor ao ambiente em que o programa em C foi executado. Código de retorno 0 → término normal. Código de retorno != 0 → indica situações incomuns ou erros. Funções (6) Diferenças da definição e declaração de funções entre o ANSI C e as versões anteriores de C (ver prog13a-chap01-pg28.c). Os parâmetros são nomeados entre parênteses -( ) - e os seus tipos são declarados antes do abrechave - { . No protótipo da função não era permitido a declaração dos tipos de parâmetros (resultado: o compilador não tinha como checar se a invocação da função estava correta). Funções - call by value (1) Chamadas por valor (call by value) é fornecida à função uma cópia dos argumentos em variáveis temporárias (os parâmetros), de tal forma que os valores das variáveis no trecho do programa que invocou a função não são alterados. Logo, em C, uma função chamada não pode alterar o valor de uma variável na função que a invoca. Só pode alterar os valores da sua própria cópia. Funções - call by value (2) Call by value é uma vantagem → propicia o encapsulamento do código da função, evitando reflexos nas variáveis do código que invoca a função. Os parâmetros comportam-se como variáveis locais à função que foi invocada. São inicializados com os valores assumidos pelos argumentos no trecho do código que invocou a função (ver prog14-chap01-pg28.c). Funções - call by reference Em C, quando necessário, é possível a uma função, alterar o valor de uma variável na função que a invoca. Na invocação da função deve-se fornecer, como argumento, o endereço da variável a ser alterada (pointer para a variável). A função invocada deve declarar, como parâmetro um pointer e deve referenciar indiretamente o valor da variável. Funções – vetores como argumentos Quando o nome do vetor é usado como argumento, o valor passado à função é o local ou endereço de início do vetor. Não há uma cópia para os elementos do vetor. A função pode ter acesso e alterar qualquer elemento do vetor com o uso de subscritos. Constante do tipo cadeia de caracteres Para armazenar a constante ''dia\n'' o compilador C cria um vetor com um '\0', ao seu final. d i a \n \0 prog15-chap01-pg30.c (1) Programa para ilustrar o uso de vetores de caracteres e funções que o manipulam Lê um conjunto de linhas e imprime a maior delas. while (there's another line) if (it's longer than the previous longest) (save it) (save its length) print longest line prog15-chap01-pg30.c (2) Desenvolver uma função getline para obter a próxima linha de entrada getline deve ser útil em outros contextos getline retorna o tamanho da linha obtida, ou zero, se for encontrado o fim de arquivo Lembrar que zero não é um tamanho válido para uma linha, pois, mesmo uma linha-vazia possui o caracter de new line \n prog15-chap01-pg30.c (3) A maior linha encontrada até o momento deve ser salva em algum lugar. Desenvolver a função copy, para copiar a nova linha (no caso de ser maior que a anterior) para um local seguro. Na função main deve-se ter um código para controlar getline e copy. Os protótipos das funções getline e copy são declarados no início do arquivo, anterior à função main. prog15-chap01-pg30.c (4) main e getline trocam informações por intermédio de um par de argumentos/parâmetros e pelo valor retornado por getline. Getline declara os parâmetros em int getline(char s[], int lim) o primeiro parâmetro s é um vetor. Seu tamanho não é necessário em getline, pois já foi definido em main. O valor retornado por getline é um int (o tamanho da linha obtida), prog15-chap01-pg32.c (5) O tipo retornado por copy é void, indicando explicitamente que nenhum valor é retornado. getline insere o caracter ' \0' ao final do vetor, para sinalizar o fim da cadeia de caracteres (mesma convenção utilizada pelo compilador C nas constantes do tipo cadeia de caracteres). A especificação de formato %s em printf sinaliza que o argumento correspondente deve ser uma cadeia de caracteres. Testando o tamanho retornado por getline e o último caracter retornado no vetor, main poderia determinar se a linha obtida é maior que um valor máximo pré-determinado e prosseguir da forma desejada (ignorou-se esse caso). Variáveis externas e escopo (1) prog16-chap01-pg32.c As variáveis declaradas em uma função são privativas ou locais à função. Outras funções não têm acesso a elas (isto vale também para as variáveis declaradas em main). Cada variável local a uma função existe somente enquanto a função está ativa e deixa de existir quando a função termina. Por isso são também conhecidas como variáveis automáticas. Variáveis externas e escopo (2) As variáveis automáticas devem ser explicitamente inicializadas a cada chamada. É possível definir-se variáveis externas a todas as funções – as variáveis globais. Elas são visíveis a todas as funções e podem ser por elas alteradas. As variáveis externas são permanentes e retêm os seus valores entre as diversas chamadas a uma função. Variáveis externas e escopo (3) Tornam o programa mais acoplado, o que dificulta alterações futuras e a própria compreensão do programa (lembrar que o todo é maior que as partes). Uma variável externa deve ser definida exatamente uma vez, fora de qualquer função. Isto aloca área de armazenamento para ela. Cada função que desejar ter acesso à variável externa deve declará-la explicitamente com extern ou implicitamente pelo contexto. Variáveis externas e escopo (4) Antes que uma função possa usar uma variável externa, o nome da variável deve tornar-se conhecido pela função. Uma alternativa é inserir uma declaração extern na função extern char line[], longest[]; Em certos casos pode-se omitir a declaração extern. Ex.: se a definição da variável extern ocorrer no arquivo-fonte antes do seu uso na função. Variáveis externas e escopo (5) Uma prática comum é inserir a definição de todas as variáveis externas no início do arquivo fonte e omitir as declarações extern dentro das funções. Se o programa está distribuído por vários arquivos fontes e uma variável extern é definida em arquivo1 e usada em arquivo2 e arquivo3, então as declarações extern em arquivo2 e arquivo3 são necessárias. Uma alternativa é concentrar todas as declarações extern em um arquivo separado (header) e incluí-lo por um #include no início de cada arquivo fonte. #include ''myheader.h'' Variáveis externas e escopo (6) Definição – refere-se ao local aonde se cria a variável ou se destina área de armazenamento para a mesma. Declaração – refere-se ao local em que a natureza da variável é estabelecida (seu tipo), mas não há alocação de área para a mesma. A keyword void deve ser usada em lista explicitamente vazias. Tem a ver com a checagem de argumentos nos compiladores e a compatibilidade com versões mais antigas do C.