Linguagem C
Disciplina: AAM
Prof. Eduardo Henrique Couto
2014/01
Linguagem C
• Objetivo da aula:
• Revisão da Linguagem;
• Otimização de programa em C para Sistemas
Embarcados.
• Exercício
Revisão da Linguagem C
• Surgiu nos anos 70 de uma linguagem
chamada B. criada por Dennis Ritchie.
• Qualquer programa C ANSI pode ser
compilado em qualquer compilador C
ANSI não importando a máquina na qual
o programa vá ser executado.
• Portanto => portabilidade, a escolha
acaba recaindo sobre a linguagem C.
Revisão da Linguagem C
• Esta linguagem foi criada, influenciada e
testada em campo por programadores.
Ela oferece ao programador exatamente
o que ele quer: poucas restrições, código
rápido e eficiência. Por isso ela é a
linguagem mais popular entre os
programadores profissionais altamente
qualificados.
Revisão da Linguagem C
• Programação estruturada: A linguagem C é
uma linguagem estruturada em bloco simples
=> habilidade de uma linguagem tem de
seccionar e esconder do resto do programa
todas as instruções necessárias para a
realização de uma determinada tarefa.
Revisão da Linguagem C
• Declaração de variáveis: Variáveis devem ser
declaradas antes de serem usadas, permitindo
assim, que o compilador saiba de antemão
• informações como tipo e espaço gasto
• em memória podendo fazer checagem
• durante o processo de compilação.
Revisão da Linguagem C
• Funções: Blocos de Código: Conjunto de
comandos que executa uma determinada
operação no programa. Normalmente é útil
quando se necessita repetir um mesmo trecho
de código várias vezes em um programa.
Revisão da Linguagem C
• Sensitive case: Significa que na linguagem C
as letras maiúsculas e minúsculas são
tratadas como caracteres diferenciados.
• Ex.: Podemos declarar uma variável For,
apesar de haver uma palavra reservada for,
mas isto não é uma coisa recomendável de se
fazer pois pode gerar confusão!!
Revisão da Linguagem C
• Comentários:
• /* comentário de uma ou várias linhas.
O padrão não permite comentários aninhados
(um dentro do outro), mas alguns
compiladores os aceitam.
*/
• // comentário de uma linha!!!!
Revisão da Linguagem C
Palavras reservadas:
auto
break
case
char
const
continue
default
do
double
else
enum
extern
float
for
goto
if
int
long
register
return
short
signed
sizeof
static
struct
switch
typedef
union
unsigned
void
volatile
while
Revisão da Linguagem C
•
ESTRUTURA BÁSICA DE UM PROGRAMA:
1)
2)
3)
4)
5)
INCLUSÃO DE ARQUIVOS EXTERNOS
DECLARAÇÃO DE VARIÁVEIS GLOBAIS
DECLARAÇÃO DE PROTÓTIPOS DE FUNÇÕES
FUNÇÃO PRINCIPAL (main)
FUNÇÕES
Revisão da Linguagem C
• Protótipos de Funções:
• Normalmente escrevemos as funções antes de
escrevermos a função main().
• Isto é, as funções estão fisicamente antes da função
main().
• Isto é feito por uma razão. Imagine-se na “pele” do
compilador. Se você fosse compilar a função main(),
onde são chamadas as funções, você teria que saber
com antecedência quais são os tipos de retorno e quais
são os parâmetros das funções para que você pudesse
gerar o código corretamente.
Revisão da Linguagem C
• Protótipos de Funções:
• Foi por isto que as funções foram colocadas
antes da função main(): quando o compilador
chegasse à função main() ele já teria
compilado as funções e já saberia seus
formatos.
Revisão da Linguagem C
• Protótipos de Funções:
• Muitas vezes teremos o nosso programa
espalhado por vários arquivos. Ou seja,
estaremos chamando funções em um arquivo
que serão compiladas em outro arquivo.
Como manter a coerência?
Revisão da Linguagem C
• Protótipos de Funções:
• A solução são os protótipos de funções. Protótipos
são nada mais, nada menos, que declarações de
funções. Isto é, você declara uma função que irá
usar. O compilador toma então conhecimento do
formato daquela função antes de compilá-la. O
código correto será então gerado. Um protótipo
tem o seguinte formato:
• tipo_de_retorno nome_da_função (declaração_de_parâmetros);
Revisão da Linguagem C
• tipo_de_retorno nome_da_função (declaração_de_parâmetros);
• onde o tipo_de_retorno, o nome_da_função e a
declaração_de_parâmetros são os mesmos que
você pretende usar quando realmente escrever a
função. Repare que os protótipos têm uma nítida
semelhança com as declarações de variáveis.
Revisão da Linguagem C
#include <stdio.h>
float Square (float a);
int main ()
{
float num;
num=Square(num);
return 0;
}
float Square (float a)
{
return (a*a);
}
// prototipo
Revisão da Linguagem C
Tipos de Dados:
• char ( sinalizados –positivo ou negativo)
ou não sinalizado – positivo)
• inteiro ( sinalizados –positivo ou negativo)
ou não sinalizado – positivo)
• real ( valores inteiros ou fracionários – float ou
double)
• nulo ( void – tipo vazio, ou um "tipo sem
(tipo". Não retornam e/ou recebem
valores – parâmetros)
Revisão da Linguagem C
Modificadores de Tipos de dados:
• short (reduz a faixa de representação do
tipo)
• long (ampliar a faixa de representação do
tipo)
• signed (para especificar a representação de
valores sinalizados – compl. de dois)
• unsigned (para especificar a representação de
valores não sinalizados)
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
http://sergioprado.org/otimizacao-de-codigo-em-linguagem-c-parte-1
Escrever código para estes dispositivos requer
atenção especial quanto a utilização e
gerenciamento eficiente dos recursos
disponíveis. Por que?
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Sistemas embarcados têm como uma de suas
principais características a limitação de
recursos computacionais (quantidade de
memória, capacidade de processamento e
dispositivos de I/O).
• Aplicações bem específicas => projetadas na
medida para cumprir sua função.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
Vantagens da otimização:
•
•
•
•
Diminuir custos do projeto;
Tempo de desenvolvimento;
Tamanho do dispositivo;
Consumo elétrico.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
Vantagens da otimização:
•
•
•
•
Diminuir custos do projeto;
Tempo de desenvolvimento;
Tamanho do dispositivo;
Consumo elétrico.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• O compilador C é a ferramenta que irá transformar
seus algoritmos (código-fonte C), na sua aplicação.
• O compilador realiza uma série de transformações
no código-fonte para gerar o melhor código
possível, como por exemplo salvar variáveis em
registradores ao invés de usar a memória para
melhorar o tempo de acesso (otimização de
processamento) ou remover código inútil do
programa (otimização de espaço).
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Em um compilador C moderno, o processo de
compilação envolve basicamente os seis passos, na
ordem apresentada:
• 1. Pré-processamento: Conversão do código-fonte
em uma linguagem intermediária. É aqui que as
diretivas de compilação são processadas e a
sintaxe do código é verificada.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• 2. Otimização de alto nível: É nesta etapa que o
compilador realiza a primeira otimização do
código, em cima do código-fonte da aplicação.
Veremos mais adiante algumas das técnicas para
auxiliar o compilador neste trabalho de
otimização.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• 3. Geração do código: Geração do código de
máquina para a arquitetura-alvo. Nesta fase o
compilador faz todas as conversões necessárias
para a arquitetura em questão. Expressões
aritméticas de 32 bits em processadores de 8 bits
são convertidas em expressões aritméticas de 8
bits, por exemplo.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• 4. Otimização de baixo nível: Nesta fase a
otimização é realizada em cima do código-objeto
gerado na fase anterior, como por exemplo
removendo instruções não usadas ou
redundantes.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• 5. Assembler: Nesta etapa o compilador gera o
arquivo-objeto correspondente ao arquivo-fonte C
compilado.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• 6. Linkagem: Por último, o compilador faz a
linkagem de todos os arquivos-objeto em um único
arquivo binário para ser carregado na memória do
dispositivo.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Percebe-se que, dentre as seis etapas no processo
de compilação descritas, quando pensamos em
otimização de código, deve--se focar nas etapas 2
(Otimização de alto nível) e 4 (Otimização de
baixo nível).
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
Técnicas que podem auxiliar e instruir o
compilador na tarefa de otimizar o
código gerado:
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
A) O tempo de acesso aos registradores da CPU é
normalmente muito mais rápido do que o tempo
de acesso à memória de dados, e o processador
possui melhor performance quando realiza
cálculos utilizando registradores ao invés de
memória.
• Porisso, o compilador procura alocar variáveis
locais e parâmetros para funções em registradores
para melhorar a performance do sistema.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• O compilador precisa decidir quais variáveis irão
ser acessadas através de registradores e quais
deverão ser mantidas em memória.
• Pode-se instruir o compilador através da palavrachave register, conforme mostra a listagem:
int exp(register int base, register int expoente) {
register int resultado = 1;
for (; expoente; expoente--)
resultado *= base;
return(resultado); }
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• O compilador precisa decidir quais variáveis irão
ser acessadas através de registradores e quais
deverão ser mantidas em memória.
• Pode-se instruir o compilador através da palavrachave register, conforme mostra a listagem:
int exp(register int base, register int expoente) {
register int resultado = 1;
for (; expoente; expoente--)
resultado *= base;
return(resultado); }
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Devemos ressaltar que nem sempre será possível alocar
todas as variáveis em registradores.
• A palavra-chave register é apenas uma orientação para
auxiliar o compilador a decidir quais variáveis são mais
prioritárias para a alocação em registradores.
• Por este motivo é importante ter um código modular, com
funções pequenas e específicas, para que a quantidade de
variáveis locais também seja pequena, aumentando as
chances de que estas sejam otimizadas através do
armazenamento em registradores.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
B) Funções
• Uma chamada de função é um processo complexo e
deve ser levado em consideração quando pensamos
em performance. Basicamente, antes de uma
chamada à função o compilador salva em memória
(ou registradores) os argumentos da função e o
endereço de retorno.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Essa região de memória é chamada de stack.
Dentro da função chamada, os registradores são
salvos, parâmetros são lidos do stack ou de
registradores, e variáveis locais são alocadas em
memória ou em registradores (se disponíveis).
• Podemos perceber que, para funções com uma
quantidade grande de parâmetros, o custo de uma
chamada pode ser enorme para a performance do
sistema.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Uma das técnicas para melhorar a performance é
diminuir a quantidade de parâmetros passados para
funções.
• Podemos perceber comparando as duas funções
abaixo, que o custo de chamada da função
atualiza_dados(), com 5 parâmetros, é muito maior
que o custo de chamada da função
atualiza_dados_otimizada(), que recebe apenas um
ponteiro para uma estrutura de dados como
parâmetro.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
void atualiza_dados(char *nome, char *endereco, char *cidade, char *telefone,
double salario)
{
...
}
struct Dados {
char *nome;
char *endereco;
char *cidade;
char *telefone;
double salario; };
void atualiza_dados_otimizada(struct *Dados)
{
...
}
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
C) Inlining de funções:
• A chamada à função é substituída por uma cópia do
seu código. Em código C, podemos empregar este
recurso através da definição de macros.
• No primeiro exemplo, a função soma() será
chamada 100 vezes, diminuindo a performance do
sistema. No segundo exemplo, usa-se a macro
SOMA(), eliminando a chamada à função e
conseqüentemente aumentando a performance do
sistema.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
#define SOMA(a, b) (a + b)
int soma(int a, int b) {
return(a + b);
}
int main() {
int i, res, a = 1;
/* primeiro exemplo */
for (i = 0; i < 100; i++)
res = soma(a, res);
/* segundo exemplo */
for (res = 0, i < 100; i++)
res = SOMA(a, res);
...
}
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
D) Otimização do compilador
O compilador pode ser instruído para compilar o
programa priorizando tempo de processamento
(speed optimization) ou tamanho de código (size
optimization).
• Como exemplo, vamos acompanhar a tabela, que
lista informações sobre a compilação de dois
programas distintos:
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• O programa 1 ficou menor quando selecionada a
otimização por tamanho de código;
• O programa 2 ficou menor quando selecionada a
otimização por tempo de processamento!
• Isso significa que é sempre bom testar as duas opções no
seu código e verificar os resultados, antes de definir a
otimização ótima para seu projeto.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
E) Tamanho das variáveis
• o compilador é preparado para compilar o código
otimizado para a arquitetura-alvo do seu sistema.
• Isso significa que se você está trabalhando com um
microprocessador de 8 bits, os cálculos terão maior
performance se realizados com 8 bits de dados, em
variáveis do tipo char.
• Da mesma forma, se você estiver trabalhando com
processadores de 32 bits, os cálculos terão maior
performance se realizados com variáveis do tipo int.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
• Isso acontece porque o compilador realiza os
cálculos com base no tamanho de seus
registradores, que por sua vez são baseados na
arquitetura da CPU.
• Se a sua CPU é de 32 bits, seus registradores serão
de 32 bits, e se você está realizando cálculos com
variáveis char, de 8 bits, nesta arquitetura, o
compilador terá o esforço extra de converter o
resultado final, de 32 bits, em uma variável de
8 bits.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
F) Incremento de variáveis
• É muito mais eficiente incrementar variáveis com o
uso de operadores de incremento (ex: ind++) do
que o comando de atribuição (ex: ind = ind + 1).
• Isso porque a maioria dos compiladores usam , se
disponível, a instrução de incremento do
processador, o que torna o código-objeto gerado
muito mais otimizado. Exemplo:
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
/* incremento com atribuicao */
x = x + 1;
/* codigo gerado em assembly
mov A, x ; carrega o valor de x no registrador A
add A, 1 ; soma 1 ao registrador A
mov x, A ; carrega de volta o valor do registrador A em x */
/* operador de incremento */
x++;
/* codigo gerado em assembly
inc x; incrementa x em 1 */
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
G) Protótipos para funções
• Se o protótipo da função não é devidamente
definido, o compilador precisará “adivinhar” quais
os tipos de dados com que sua função trabalha, o
que diminui bastante a performance do sistema.
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
H) Códigos muito “inteligentes”
• Alguns programadores acreditam que, ao escrever
em poucas linhas de código fazendo uso
“inteligente” de construções complexas em
linguagem C, deixarão o código menor e mais
rápido.
• Cuidado nem sempre o compilador vai criar códigos
menores e pode tornar o código quase indecifrável!
OTIMIZAÇÃO DE PROGRAMA EM C
PARA SISTEMAS EMBARCADOS
I) Uso de bibliotecas
• Algumas vezes, o uso de apenas uma função de uma
biblioteca, como printf() pode trazer junto com ela,
implicitamente, outras funções necessárias para sua
utilização, e isso pode aumentar bastante o tamanho do
seu código.
• Quando se trata de sistemas embarcados com poucos
recursos disponíveis, às vezes é melhor implementar uma
função simples do que utilizar a de uma biblioteca que
pode fazer o tamanho do código crescer.
Exercício
• Completar a tabela dos tipos de dados do
JM60 e JM128.
• Utilize o CodeWarrior vrs. 10.2
JM60
JM128
Download

Linguagem C aula 1 [Modo de Compatibilidade]