11/10/12 Página de Evy Salcedo Física Computacional FSC5705 Súmula Aulas Lista Conc. Freq. Outros Ensino Estrutura de um programa em C 01 02 03 04 05 06 07 08 09 10 #include <stdio.h> int main(void){ /* A linha embaixo imprime na tela */ printf("Ola, mundo\n"); return 0; } [usuario@pclabfis: ]# gcc exemplo01.c o exemplo01 std=c99 [usuario@pclabfis: ]# ./exemplo01 Ola, mundo Na aula anterior mostramos o nosso primeiro programa escrito em C. Esse primeiro programa, que imprime a frase ola, mundo, foi escrito para dar seguimento à tradição introduzida por Brian Kernighan no seu livro "The C Programming Language" o qual é um livro de referencia para programação em C. Na figura acima reproduzo o programa ao qual me refiro a fim de analisar a estrutura dos programas em C. Na primeira linha temos uma diretiva que é lida pelo preprocessador do compilador. Essa diretiva informa ao compilador que inclua o conteúdo do arquivo de cabeçalho stdio.h (o .h vem de header que significa cabeçalho). Nesse arquivo estão as definições das funções que dizem respeito a entrada e saída de dados do programa, como por exemplo a função printf utilizada para imprimir na tela, outras funções tratam de saída de outros dispositivos, como o HD do computador (salvar no HD). Todo os programas em C são constituídos por uma o mais funções. A principal de essas funções é a função main (que se traduz literalmente como principal). O código associado a essa função é contido em um bloco de código. Cada bloco é delimitado por um par de chave: { para abre o bloco, e } para fechar o bloco. Quando um programa é executado o sistema operacional procura a função main para executar o seu código (delimitado pelas chave). A palavra reservada int colocada antes do main diz para o compilador que deve informar ao sistema operativo esperar um retorno do tipo inteiro da função main quando esta termine. Uma outra opção seria utilizar void (que significa vazio) no lugar do int, em esse caso o sistema operativo não esperaria uma resposta e não seria necessário colocar return da linha 8 . Os parenteses, ( e ), depois da palavra main é onde são colocadas as variáveis que receberam possíveis dados de entrada para a função main. Em especial a função main admite dois variáveis de entrada, tradicionalmente são nomeados de argc e argv. A essas variáveis se lhes chama argumentos da função, em um aula futura trataremos em detalhe os argumentos das funções. Neste exemplo não esperamos nenhum dado de entrada e por isso utilizamos o palavra reservada void para informar que não é necessário esperar por dados (dai que se use void que significa vazio). No corpo (bloco) da função temos três linhas. Na linha 5 temos um comentário. Comentários são pedaços do código que não são interpretados pelo compilador. Um comentário se da inicio com /* e termina com */. Com o advento do C++ uma outra forma de comentário foi introduzida o barra-barra, //, mas a diferença do anterior o barra-barra só comenta a linha onde foi colocado enquanto que o /* */ comenta tudo o que estiver dentro do operador (varias linhas, até) /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula104.html 1/6 11/10/12 Página de Evy Salcedo Na linha 6 é utilizada a função printf cujas definições estão dentro do cabeçalho stdio.h. Essa função permite imprimir o valor de uma variável no terminal. Ela admite um ou mais parâmetros de entrada, o primeiro parâmetro pode ser uma sequencia de caráteres ou uma string de controle que informa à função como tratar os dados de entrada, os outros argumentos são as variáveis a serem impressas. No exemplo mostrado acima só é passado um argumento que é uma variável de tipo carácter a qual deve ser impressa. Na linha 8 utilizamos o comando return para devolver o resultado da função, que no caso é zero. Observe que além da string (caráteres) a ser impressa (Ola, mundo) temos uns outros caráteres, \n. A barra nesses caráteres se denomina de escape; escape n (ou barra n) diz para o compilador introduzir nova lina. Além de \n temos \t (tabular), \r (retorno do carro), \a (alerta) \\ (a barra) e \" (aspas). Tipo de variáveis C é uma linguagem fortemente tipada, assim todas as variáveis a serem utilizadas no programa devem ser previamente definidas. A definição das variáveis em C segue o seguinte modelo Tipo nome_da_variável Onde o Tipo pode ser int, float, char. Essa definição prévia, permite que o compilador negocie com o sistema operacional quanta memoria RAM ele vai precisar para armazenar as variáveis que um dado programa vai utilizar. A origem disto está em que cada tipo de variável precisa de um tamanho de memoria específico (medido em bits ou bytes). Tipo Tamanho (bits) Faixa char 8 -127 a 127 unsigned char 8 0 to 255 short 16 -32,767 to 32,767 unsigned short 16 0 a 65,535 int 32 -2147483647 a 2147483647 unsigned int 32 0 a 4294967295 long 32 -2147483647 a 2147483647 unsigned long 32 0 a 4294967295 long long 64 -9223372036854775807 a 9223372036854775807 unsigned long long 64 0 to 18446744073709551615 float 32 ~6 dígitos de precisão double 64 ~15 dígitos de precisão int: armazena variáveis do tipo inteiro, por exemplo: sem parte decimal. , A = 1 int A, F , g float: armazena variáveis com parte decimal, exemplo: float F = 2 , , g = 1000 , i = 10.1 k = 92.4 , etc, isto é, J = 1011.0 , etc. i, k, J char armazena caracteres, exemplo: ′′ char M ateria = ′′ Quantic a ′′ , 2.0 , , , etc. Observe que os caracteres são definidos entre aspas duplas. Quando definida uma ′′ char C lasse = M ecanic a ′′ ′′ char C ientista = P lanck /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula104.html ′′ char N umero = ′′ 2/6 11/10/12 Página de Evy Salcedo variável de tipo character devemos informar a quantidade máxima de caracteres que ′′ ′′ a variável poderá assumir, assim no exemplo M ateria = Quantica temos 8 caracteres. char M ateria[8], C ientista[6], N umero[3] Devemos como em string são à string, se observar que a manipulação de strings (caráteres) no C não é trivial Fortran, por exemplo. É necessário utilizar a biblioteca string.h isto porque vetores. Não é possível utilizar o comando de atribuição e assinar um valor não é valido escrever simplesmente no meio do código algo como ′′ ′′ M ateria = Quantic a , só é valido na definição da variável: ′′ ′′ char M ateria = Quantic a . De forma que neste primeiro momento deixemos as variáveis string para depois. Além dos tipos aqui apontados existem outros que são possíveis de serem utilizados. unsigned char, unsigned int, long int, unsigned long int, long double. Agora sabemos que existem 4 tipos básicos no C: char, int, float, double; esses tipos podem ser estendidos mediante o uso da biblioteca de string, da biblioteca complex para trabalhar com números complexos ou definindo nosso proprios tipo de variáveis contudo, antes de dar esse passo em direção à complexidade vamos entender os tipos básicos analisando um exemplo: 01 02 03 04 05 06 07 08 09 #include <stdio.h> int main(void){ float nota01; /* declara nota01 como uma variável do tipo float */ float nota02; /* declara nota02 como uma variável do tipo float */ float total; /* declara total como uma variável do tipo float */ float media; /* declara media como uma variável do tipo float */ 10 11 nota01 = 85.5; /* constante tipo float 85.5 atribuido ah nota01 */ /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula104.html 3/6 11/10/12 Página de Evy Salcedo 12 13 14 15 16 17 18 19 nota02 = 97.0; /* constante tipo float 97.0 atribuido ah nota02 */ total = nota01 + nota02; /* resultado da soma atribuido ah total */ media = total / 2.0; /* resultado da divisao atribuido ah media */ printf("A media das notas eh %f\n", media); /* impressao em tela do valor armazenado em media */ return 0; } [usuario@pclabfis: ]# gcc exemplo02.c o exemplo02 std=c99 [usuario@pclabfis: ]# ./exemplo02 A media das notas eh 91.250000 No exemplo fica claro que você primeiro define a variável e depois pode atribuir um valor a ela. Utilizamos a palavra atribuir porque o = entre a variável e o valor constante a ser atribuído não deve ser entendido como um igual matemático sinão como um comando (de atribuição) que informa para colocar um dado numa determinada posição da memoria. Note que nos comentários do programa anterior chamei de constantes de tipo float ao número que foram assinado às variáveis. Essa forma de declaração tem o problema de ocupar uma linha para cada variável, o C permite definir variável do mesmo tipo na mesma linha, dessa forma o programa anterior seria reescrito como 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 #include <stdio.h> int main(void){ float nota01, node01, total, media; nota01 = 85.5; nota02 = 97.0; total = nota01 + nota02; media = total / 2.0; printf("A media das notas eh %f\n", media); return 0; } De fato, o C permite definir atribuir valores às variáveis no momento da definição: 01 02 03 04 05 06 07 #include <stdio.h> int main(void){ float nota01 = 85.5, node01 = 97.0, total, media; 08 09 10 11 12 13 14 total = nota01 + nota02; media = total / 2.0; printf("A media das notas eh %f\n", media); return 0; } Conversão de tipo Para converter uma variável de um tipo numa de outro tipo simplesmente atribuímos essa variável à outra do tipo diferente ou podemos utilizar a operação chamada de cast. Para realizar um cast simplesmente colocamos à frente da variável o tipo a que desejamos converter ela entre parenteses, por exemplo suponhamos que a variável a seja do tipo int, a operação de cast b = (float) a é tal que o valor da variável a é convertido em float e assinado à variável b. 01 #include <stdio.h> 02 03 int main(void){ /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula104.html 4/6 11/10/12 Página de Evy Salcedo 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 int i1, i2; float f1; i1 = 1; i2 = 2; f1 = i1 / i2; printf("f1 = %f\n", f1); f1 = (float) i1 / (float) i2; printf("f1 = %f <‐‐‐casting\n", f1); return 0; } [usuario@pclabfis: ]# gcc exemplo03.c o exemplo03 std=c99 [usuario@pclabfis: ]# ./exemplo03 f1 = 0.000000 f1 = 0.500000 <casting No exemplo de acima vemos que na linha 12 é realizada uma divisão de duas variáveis inteiras e o resultado é armazenado na variável f1. Note que como resultado dessa operação é impressa na tela o valor de 0.000000 e não de 0.500000, como é esperado. A razão disso é que como foi realizada uma operação com inteiros o resultado dessa operação é um inteiro que é atribuído a um float. Para correger o problemas devemos realizar um cast das variáveis, isso é feito na linha 16 onde cada variável é promovida para float, nesse caso o resultado é o esperado. 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 #include <stdio.h> int main (void){ int i1; float f1; double d1, d2; d1 = 1.947288475921421; /*15 numeros depois do ponto*/ printf("d1 = %.15f\n", d1); f1 = d1; printf("f1 = %.15f <‐‐ truncada na VI casa decimal\n", f1); i1 = f1; printf("i1 = %.15f <‐‐ formato errado\n", i1); printf("i1 = %d <‐‐ truncada parte decima\n", i1); return 0 } [usuario@pclabfis: ]# gcc exemplo04.c o exemplo04 std=c99 exemplo04.c: Na função 'main': exemplo04.c:20: aviso: format '%.15f' expects type 'double', but argument 2 has type 'int' [usuario@pclabfis: ]# ./exemplo04 d1 = 1.947288475921421 f1 = 1.947288513183594 < truncada na VI casa decimal i1 = 1.947288513183594 < formato errado i1 = 1 < truncada parte decima No exemplo acima se mostra os cuidados que devem ser tomados no caso de conversão de tipos. Nesse exemplo é definida uma variável de dupla precisão d1 onde é atribuído o valor contante 1.947288475921421 (C trata todos os valores contantes que possuem parte decimal como doubles, diferente do fortran que os trata como float.). O valor dessa variável é impresso /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula104.html 5/6 11/10/12 Página de Evy Salcedo na tela, repare que foi solicitado à função printf imprimir 15 números depois do ponto utilizando o formato: %.15f. Depois atribuímos o valor da variável double a uma variável float, consequentemente o compilador de C fará uma conversão de tipo. A fim de verificar isso pedimos para o imprimir na tela o valor da variável float e se observe que até a VI casa decimal o valor do float coincide com o do double, além dessa posição os números divergem. De onde saem esse números extra? A origem deles está no fato de que computadores trabalham na base 2 e nos tratamos de representar um número da base 10 (nossa base diária) na base 2. Depois atribuímos o valor a uma variável do tipo inteira e mandamos imprimir na tela, mantendo a formatação de float, note que o resultado está errado. Para corrigir deve ser mudado o especificado de formato para %d, como é feito na linha 22. Conversões entre tipos de dados sempre envolvem perda de informação, de alguma forma. É necessário prestar atenção a esses fatos o podemos obter resultados desastrosos. Como exemplo desse tipo de calamidades cito o problema do Ariane 5 e do sistema de misseis patriot. No primeiro caso era convertido um double para um int, quando o double era 32768.alguma coisa a conversão resultou em outro número que não era 32768, isso porque o int vai até 32767. No caso do sistema Patriot foi o fato do tempo ser medido em decimas de segundo: 1.0 / 10.0, em binário esse numero é igual a 0.0001100110011001100110011001100 … o problema foi que esse numero era armazenado num float (de 24 bits) assim representava o número como 0.00011001100110011001100 o que introduzia um erro de 0.0000000000000000000000011001100 … que é em decimal 0.000000095, assim, depois de 100 horas de ligado o sistema o erro aumentava para 0.34 segundos e resultava no calculo errado da trajetória. /media/ext4_evy/Ensino/UFSC/Ano2012/IIsemestre/public_html/…/aula104.html 6/6