A maior rede de estudos do Brasil

Grátis
Estruturas_de_Dados_e_Algoritmos_em_C.erivanildo- Blog - conhecimentovaleouro.blogspot.com by @viniciusf666

Pré-visualização | Página 5 de 50

tipo de dados lista de variáveis ; 
 
 tipo de dados ::= qualquer tipo de dados válido na linguagem C 
 
 lista de variáveis ::= identificador de variável genérico | 
 lista de variáveis , identificador de variável genérico 
 
 identificador de variável genérico ::= identificador de variável | 
 identificador de variável = expressão de inicialização 
 
 identificador de variável ::= identificador válido na linguagem C 
PROGRAMAÇÃO ESTRUTURAS DE DADOS E ALGORITMOS EM C 12 
1.6 Sequenciação 
Tal como foi referido anteriormente, o corpo de uma função é delimitado pelos 
separadores { e }, correspondentes, respectivamente, aos separadores begin e end do 
Pascal, e contém a declaração das variáveis locais, a alusão a funções usadas na sequência 
de instruções e a sequência de instruções propriamente dita. A Figura 1.12 apresenta a 
definição formal de uma sequência de instruções. Ao contrário do que se passa em Pascal, 
na linguagem C cada instrução simples é obrigatoriamente terminada com o separador ;, a 
menos que o último símbolo da instrução seja o separador }. 
 
 
Figura 1.12 - Definição formal da sequência de instruções. 
Em Pascal, a invocação de uma função, sendo uma expressão, não pode ser considerada 
uma instrução. Na linguagem C, contudo, o conceito de procedimento não tem uma 
existência separada. Define-se como sendo uma função de um tipo especial, o tipo void. 
Pelo que, a invocação de um procedimento é, por isso, em tudo semelhante à invocação de 
uma função de qualquer outro tipo, quando o valor devolvido não é tido em consideração. 
Logo, nestas circunstâncias, na linguagem C, a pura e simples invocação de uma função de 
qualquer tipo é considerada uma instrução. 
 
A sequenciação de instruções é a forma mais simples de controlo de fluxo num programa, 
em que as instruções são executadas pela ordem em que aparecem no programa. Dentro 
das instruções simples, apenas as instruções de atribuição, de entrada-saída e de invocação 
de uma função, são verdadeiramente instruções de sequenciação, já que, as instruções 
decisórias e repetitivas permitem alterar a ordem do fluxo do programa. 
 
Tal como no Pascal, na linguagem C também existe o conceito de instrução composta, cuja 
definição formal se apresenta na Figura 1.13, e que é composta por uma sequência de 
instruções simples encapsuladas entre os separadores { e }. Uma instrução composta é um 
bloco de instruções simples que se comporta como uma instrução única e é usada em 
instruções decisórias e repetitivas. 
 
 
Figura 1.13 - Definição formal da instrução composta. 
1.6.1 Expressões 
A Figura 1.14 apresenta a definição formal de uma expressão. Uma expressão é uma 
fórmula que produz um valor. Pode assumir as seguintes formas: ser uma constante; ser 
uma variável; ser o resultado da invocação de uma função; ser uma expressão composta 
por operandos e operadores, sendo que existem operadores unários e binários; e ser uma 
expressão entre parênteses curvos. 
 instrução composta ::= { sequência de instruções simples }
 sequência de instruções simples ::= instrução simples | 
 sequência de instruções simples instrução simples 
 
 instrução simples ::= instrução de atribuição | 
 instrução decisória | 
 instrução repetitiva | 
 instrução de entrada-saída | 
 invocação de uma função
13 CAPÍTULO 1 : INTRODUÇÃO AO C 
 
 
 
Figura 1.14 - Definição formal de uma expressão. 
Os primeiros três tipos de expressões são a fórmula mais simples de produzir um valor e 
resumem-se à atribuição a uma variável, do valor de uma constante, ou do valor de uma 
variável ou do valor devolvido por uma função. As expressões mais complexas envolvem 
operadores unários ou binários. Uma expressão pode ser composta por uma expressão 
colocada entre parênteses curvos. Este tipo de expressão usa-se para compor expressões 
mais complexas, ou para modificar a prioridade do cálculo de expressões parcelares. 
 
Vamos agora analisar as expressões aritméticas. O cálculo de uma expressão complexa 
supõe um processo de decomposição prévio em expressões mais simples, que é 
determinado pela precedência e pela associatividade dos operadores presentes. 
Precedência significa importância relativa entre os operadores. Operadores de maior 
precedência forçam a ligação a si dos operandos, antes dos operadores de menor 
precedência Por exemplo, como a multiplicação tem precedência sobre a adição, então a 
expressão a + b * c tem subjacente o agrupamento a + (b * c). 
 
Quando numa expressão todos os operadores têm a mesma precedência, a associatividade 
permite determinar a ordem de ligação dos operandos aos operadores, da direita para a 
esquerda, ou da esquerda para a direita. Por exemplo, como a associatividade da adição é da 
direita para a esquerda, então a expressão a + b + c tem subjacente o agrupamento 
(a + b) + c. 
 
No entanto, qualquer que seja o agrupamento imposto pela precedência e pela 
associatividade dos operadores presentes, ele pode ser sempre alterado pela introdução de 
parênteses curvos. 
 
No caso de aritmética inteira, sempre que numa expressão surgem constantes, variáveis, ou 
se invocam funções de tipo char ou short, os seus valores são automaticamente 
convertidos pelo compilador em quantidades de tipo int. Do mesmo modo, constantes, 
variáveis, ou funções de tipo unsigned char ou unsigned short, são automaticamente 
convertidas pelo compilador em quantidades de tipo int, ou de tipo unsigned int, se o 
primeiro tipo não tiver capacidade de armazenamento suficiente. No caso de aritmética 
real, sempre que numa expressão surgem constantes, variáveis, ou se invocam funções de 
tipo float, os seus valores são automaticamente convertidos pelo compilador em 
quantidades de tipo double. 
 
Os operadores binários supõem normalmente operandos do mesmo tipo. Quando são de 
tipo diferente, a expressão do tipo com menor capacidade de armazenamento, é 
automaticamente convertida no tipo da outra expressão. A hierarquia dos diferentes tipos 
de dados, expressa por ordem decrescente da sua capacidade de armazenamento, é a que se 
apresenta a seguir. 
 
long double o double o float o unsigned long o long o unsigned int o int 
 
Além das conversões automáticas que foram referidas, a conversão de uma expressão num 
tipo específico qualquer pode ser sempre forçada através do operador cast. 
 
( qualquer tipo de dados escalar válido em C ) ( expressão numérica ) 
 
 expressão ::= constante | variável | invocação de uma função | 
 operador unário expressão | expressão operador unário 
 expressão operador binário expressão | ( expressão ) 
PROGRAMAÇÃO ESTRUTURAS DE DADOS E ALGORITMOS EM C 14 
A Figura 1.15 apresenta um exemplo da utilização do operador cast. A divisão de duas 
variáveis inteiras dá um resultado inteiro. Para forçar a divisão real é preciso forçar um dos 
operandos a double, fazendo um cast de um dos operandos, neste caso da variável A. 
 
 
Figura 1.15 - Exemplo da utilização do operador cast. 
Se a conversão se efectua no sentido crescente da hierarquia, não há risco de overflow ou de 
perda de precisão. Caso contrário, estes problemas podem ocorrer. Concretamente, quando 
a conversão se efectua no sentido decrescente da hierarquia, podemos ter as situações 
apresentadas na Figura 1.16. 
 
 
Figura 1.16 - Situações possíveis se a conversão se efectuar no sentido decrescente da hierarquia. 
Vamos agora apresentar alguns exemplos representativos destes problemas. A Figura 1.17 
apresenta um exemplo da situação em que se atribui um valor negativo de uma variável int 
a uma variável unsigned int. O valor armazenado na memória em binário vai ser 
interpretado como sendo positivo, pelo que, há uma mudança na interpretação do valor. 
 
 
Figura 1.17 - Mudança na interpretação do valor. 
A Figura 1.18 apresenta um exemplo