Linguagens e Ambientes de Programação (2016/2017)



Teórica 10 (06/abr/2017)


Revisão da linguagem C.
Características e história. Padronizações do C.
Porque vamos trabalhar com a linguagem C?
Vamos usar o paradigma imperativo.
Elementos Básicos. Tipos básicos.
Variáveis.
Estruturas de controlo.



Introdução à linguagem C

Dennis Ritchie Brian Kernighan

Nota prévia

Esta é a segunda cadeira em que os alunos lidam com a linguagem C. Nas próximas aulas faremos um percurso pela maioria dos mecanismos da linguagem C, discutido-os com base nos conceitos gerais apresentados na cadeira.

Mas a ênfase será colocada nas seguintes partes, onde se espera que os alunos ganhem novas competências:

Nesta linha:

Vamos usar o paradigma imperativo

Em C deve-se programar de forma assumida usando o paradigma imperativo. Entre programar um algoritmo usando um ciclo ou uma função recursiva, muitas vezes devemos optar pelo ciclo, mesmo que porventura a função recursiva fosse mais legível. Em geral estamos dispostos a pagar alguma coisa pela maior legibilidade, mas neste caso o preço pode ser excessivo.

Ao contrário do OCaml, a linguagem C (e também o Java, por exemplo) não está otimizada para suportar recursividade de forma económica. Em OCaml é possível escrever funções recursivas que funcionam praticamente sem gastar pilha de execução (complexidade espacial constante). Mas na generalidade das implementações de C, as funções recursivas gastam muita pilha de execução (complexidade espacial linear, na melhor das hipóteses).

Claro que há muitas situações especiais em que, por exemplo, o uso da técnica de divisão e conquista nos conduz à criação de funções recursivas e devemos aceitar isso. Ou até alguém pode querer, por opção pessoal, escrever código recursivo mais legível, aceitando o preço de ter de usar muito mais memória para correr os programas. No entanto, nesta disciplina queremos mesmo praticar a escrita de código imperativo e de ciclos mais ou menos complexos.

Algumas características do C

Exemplo 1

Exemplo 2

Padronizações do C

O GCC é uma das implementações de C atualmente mais usadas. Suporta a maioria do C99 e mais algumas extensões, das quais a mais notável é a possibilidade de definir funções locais a outras funções, algo que sempre foi proibido no padrão do C.

Como o suporte para C99 no GCC ainda não é completo, por omissão o GCC ainda se baseia no C89. Para forçar o GCC a reconhecer o C99, tanto quanto possível, é necessário invocar o GCC assim:

Documentação sobre o compilador de C

Olhamos o que diz o início do manual do comando cc, no Linux:

Porque vamos trabalhar com a linguagem C?

Na nossa cadeira, vamos estudar e trabalhar com a linguagem C por diversas razões. As principais:

Elementos Básicos

Programa em C

Sequência de constantes variáveis, definições de tipos e funções, possivelmente distribuídas por vários ficheiros.

Identificadores

Começam por uma letra podendo conter letras, algarismos e ainda o carácter sublinhado '_'. É feita distinção entre maiúsculas e minúsculas.

Delimitadores de comentário

Terminador de instrução

Todas as declarações e todas as instruções são terminadas por um ponto e vírgula ';'. Exceção: as funções são terminadas por uma chaveta a fechar '}'.

Literais

Há literais dos tipos carácter, inteiro, real, string e booleano. Exemplos:

Definição de constantes

Inicialização de variáveis

As variáveis estáticas são inicializadas a zero por omissão.

Atribuição a variáveis


Tipos básicos

Tipos numéricos

A linguagem suporta os seguintes cinco tipos básicos numéricos: Um char ocupa 1 byte, um short ocupa pelo menos 2 bytes, um int ocupa pelo menos 2 bytes (normalmente tem 4 bytes em máquinas de 32 bits).

O ficheiro <limits.h> contém informação sobre o tamanho exato de cada tipo, na implementação de C que estiver a ser usada.

O operador sizeof também pode ser usado para saber qual o número de bytes ocupados por um valor de qualquer tipo.

Qualificadores dos tipos numéricos

Alguns dos tipos numéricos podem ter a sua semântica modificada por meio dos seguintes qualificadores: Um long int ocupa pelo menos 4 bytes; um long long int ocupa pelo menos 8 bytes.

Exemplos de tipos numéricos qualificados:

Por omissão todos os tipos são signed exceto o tipo char cujas características dependem da implementação.

O nome do tipo int pode ser omitido, quando qualificado. Portanto os seguintes são tipos válidos:

Tipo booleano

Tradicionalmente, o C não costumava ter um tipo booleano explícito, embora o conceito sempre tenha existido na linguagem. O valor de verdade é representado por qualquer valor diferente de zero, e o valor de falsidade é representado por zero. Existem três operadores lógicos que interpretam os argumentos como "booleanos": &&, ||, !.

Foi introduzido no padrão C99 um tipo chamado bool com os literais false e true, mas o uso desse tipo requer a inclusão do ficheiro <stdbool.h>. O false é simplesmente representado usando o inteiro 0 e o true é representado usando o inteiro 1.

Tipos enumerados

Os tipos enumerados foram introduzidos no padrão C89. São úteis para especificar um número de opções para um atributo. Exemplos de possíveis atributos: cor, mês do ano, dia da semana, etc.

Exemplo:

Os valores dos tipos enumerados são representados usando inteiros e a representação não é oculta. Por defeito os valores começam em zero e são incrementados sucessivamente - portanto JANUARY vale 0, FEBRUARY vale 1, etc. Veja a seguinte função: O C permite mesmo ao programador especifique a representação inteira de cada valor enumerado. Exemplo:

Tipos derivados

Os tipos derivados serão discutidos mais tarde:

Variáveis

Definição

As variáveis podem ser definidas globalmente, fora de qualquer função, ou definidas localmente, dentro duma função, logo no primeiro nível ou então dentro dum bloco interno.

Como se sabe, em termos de estilo de programação, é bastante mau usar variáveis globais. Isso cria dependências mútuas, o que aumenta a complexidade e dificuldade de leitura do código. No entanto, como acontece com muitas regras, esta também pode ter algumas exceções bem justificadas.

Inicialização

As variáveis podem ser inicializadas no momento da sua definição. Na ausência de qualquer expressão de inicialização, as variáveis globais e as variáveis locais estáticas são inicializadas a zero.

As variáveis locais não têm qualquer inicialização por omissão, ficando indefinidas (com um valor aleatório) enquanto não se fizer a primeira atribuição.

Atributos das variáveis locais

Eis os atributos disponíveis para as variáveis locais, e o seu significado:

Atributos das variáveis globais

Eis os atributos disponíveis para as variáveis globais, e o seu significado: Relativamente aos diversos tipos de variáveis, classifique as respetivas ligações em função do momento de ligação e diga qual é o tempo de vida de cada uma delas.

Estruturas de controlo

Observe as posições onde se escreve o terminador ';'.

Quais são as diferenças relativamente ao Java?



#120