588

Click here to load reader

Tratado Da Linguagem c

Embed Size (px)

Citation preview

Page 1: Tratado Da Linguagem c

THE RED BOOK

Tratado da Linguagem

C

Engenharia Elétrica Engenharia Eletrônica

Engenharia de Computadores Engenharia de Automação e Controle

COCIAN, L.F.E BRUSAMARELLO, V.J. LORENZI, C.R.

Com

puta

dore

s Pes

soai

s M

icro

cont

rola

dore

s Int

el

8051

e M

icro

chip

PIC

Page 2: Tratado Da Linguagem c

LUIS FERNANDO ESPINOSA COCIAN Engenheiro Eletricista, ME.. – Professor do Curso de Engenharia Elétrica da Universidade Luterana do Brasil

VALNER JOÃO BRUSAMARELLO Engenheiro Eletricista , Dr... - Professor do Curso de Engenharia Elétrica da Universidade Luterana do Brasil

CÍCERO RENATO LORENZI Engenheiro Eletricista , Esp... – Diretor de Tecnologia da Mater Matris Technae Ltda..

LEONICE LESINA ESPINOSA Licenciada em Letras. Revisão Lingüística

TTrraattaaddoo ddaa LLiinngguuaaggeemm CC EEnnggeennhhaarriiaa EEllééttrriiccaa

EEnnggeennhhaarriiaa EElleettrrôônniiccaa

EEnnggeennhhaarriiaa ddee CCoommppuuttaaddoorreess

EEnnggeennhhaarriiaa ddee AAuuttoommaaççããoo ee CCoonnttrroollee

Page 3: Tratado Da Linguagem c

L U I S F E R N A N D O E S P I N O S A C O C I A N

V A L N E R J O A O B R U S A M A R E L L O

C Í C E R O R E N A T O L O R E N Z I

TTTrrraaatttaaadddooo dddaaa LLLiiinnnggguuuaaagggeeemmm CCC EEEnnngggeeennnhhhaaarrriiiaaa EEElllééétttrrriiicccaaa

EEEnnngggeeennnhhhaaarrriiiaaa EEEllleeetttrrrôôônnniiicccaaa EEEnnngggeeennnhhhaaarrriiiaaa dddeee CCCooommmpppuuutttaaadddooorrreeesss

EEEnnngggeeennnhhhaaarrriiiaaa dddeee AAAuuutttooommmaaaçççãããooo eee CCCooonnntttrrrooollleee

Tratado da Linguagem C - Engenharia Eletrônica Miguel Tostes, 101 • Canoas, RS. Brasil

Telefone (51) 477 4000 • Fax (51) 477 1313 www.ulbra.br

Page 4: Tratado Da Linguagem c

PPrreeffáácciioo A maioria dos livros de programação em linguagem C, enfoca os temas dos seus

capítulos, no conhecimento básico da linguagem, as palavras chaves e em exemplos simples de processamento da informação. Estes livros têm como público alvo, pessoas que desejam obter somente uma noção básica da linguagem. Por causa disto são muito deficientes no que se refere à especificação e planejamento de projetos de software e ao acesso ao hardware, temas de conhecimento indispensável para engenheiros e técnicos da área eletroeletrônica, que trabalham no desenvolvimento de sistemas de aquisição de dados, de controle automático, de comunicação de dados e de processamento digital de sinais.

Esta grande lacuna existente de livros de programação que enfatizem o controle do hardware, motivou a recopilação de idéias e projetos elaborados ao longo dos anos nas salas de aula, de forma a repassar esta informação aos técnicos e engenheiros que estão iniciando a aprendizagem da linguagem.

A linguagem de programação C é padronizada, mas pode diferir em alguns pontos quando for utilizada em compiladores para plataformas diferentes de software e hardware. Nestes casos deverão ser estudadas as modificações a serem efetuadas, no código fonte gerado por cada compilador.

Este livro aborda a programação utilizando como hardware um PC IBM compatível com sistema operacional MSDOS® e Windows®, compiladores TurboC® da Borland Inc.(Inprise Inc.) e Microsoft Visual C++® da Microsoft Inc. Também são mostrados alguns exemplos utilizando os compiladores Franklin C® (Keil para a família de microcontroladores 8051) e PCW (CCS para microcontroladores PIC da Microchip).

Os exemplos de programação foram escolhidos de forma que estejam adequados ao escopo deste livro. Nos anexos foi colocada a informação referente às portas de comunicação do PC, chips de suporte e outras informações consideradas relevantes. Alguns problemas requerem pequenas montagens em hardware, cuja implementação também é mostrada através de diagramas esquemáticos.

Este livro contém informação suficiente para uma disciplina de um semestre, apresentando exercícios de programação no final de cada capítulo.

Page 5: Tratado Da Linguagem c

SSuummáárriioo 1. INTRODUCÃO ..................................................................................................................................1

1.1. POR QUE ESTUDAR A LINGUAGEM C ? ...........................................................................................1 1.2. QUANDO ESTA LINGUAGEM FICARÁ OBSOLETA ? ..........................................................................2 1.3. BREVE HISTÓRIA DA LINGUAGEM C..............................................................................................2

2. OS COMPUTADORES .....................................................................................................................4

2.1. O QUE SÃO OS COMPUTADORES ? ..................................................................................................4 2.2. COMO OS COMPUTADORES FUNCIONAM ?......................................................................................4 2.3. TIPOS DE COMPUTADORES ............................................................................................................4 2.4. SOFTWARE BÁSICO E O SISTEMA OPERACIONAL...........................................................................5

2.4.1. A Memória ............................................................................................................................6 2.4.2. Os Barramentos....................................................................................................................7

2.5. A UNIDADE CENTRAL DE PROCESSAMENTO - CPU.......................................................................7 2.6. PROGRAMAS DE COMPUTADOR .....................................................................................................9 2.7. LINGUAGENS DE PROGRAMAÇÃO ................................................................................................10

2.7.1. A Linguagem de Máquina...................................................................................................10 2.7.2. A Linguagem Assembly.......................................................................................................11 2.7.3. Linguagens de Alto-Nível ...................................................................................................11 2.7.4. Linguagens Orientadas a Objetos ......................................................................................12 2.7.5. Algoritmos ..........................................................................................................................12 2.7.6. Exemplos de Codificação de Instruções .............................................................................13

2.8. CONCLUSÕES...............................................................................................................................14

3. A CODIFICAÇÃO DA INFORMAÇÃO .......................................................................................15

3.1. SISTEMAS DE NUMERAÇÃO .........................................................................................................16 3.1.1. Valores de acordo com a Posição ......................................................................................17 3.1.2. Sistema Binário ..................................................................................................................18 3.1.3. Formatos Binários..............................................................................................................20

3.2. ORGANIZAÇÃO DOS DADOS.........................................................................................................20 3.2.1. Bits......................................................................................................................................21 3.2.2. Nibbles................................................................................................................................21 3.2.3. Bytes ...................................................................................................................................22 3.2.4. Words..................................................................................................................................22 3.2.5. Double Words .....................................................................................................................22 3.2.6. Números com Ponto Flutuante ...........................................................................................22

3.3. O SISTEMA DE NUMERAÇÃO HEXADECIMAL...............................................................................23 3.4. OPERAÇÕES LÓGICAS..................................................................................................................25 3.5. OPERAÇÕES LÓGICAS EM NÚMEROS BINÁRIOS E CADEIAS DE BITS ............................................28 3.6. NÚMEROS COM SINAL E SEM SINAL.............................................................................................28 3.7. EXTENSÃO DE SINAL E DE ZEROS................................................................................................30 3.8. A CODIFICAÇÃO ASCII...............................................................................................................32

3.8.1. O Grupo Padrão de Caracteres .........................................................................................32

4. PROJETO DE SISTEMAS DE SOFTWARE ...............................................................................38

4.1. TOP DOWN ..................................................................................................................................39 4.1.1. Estilo...................................................................................................................................39 4.1.2. Abstração............................................................................................................................39 4.1.3. Formalismo.........................................................................................................................40 4.1.4. O Projeto em Top-Down.....................................................................................................40

4.2. ALGORITMOS...............................................................................................................................41 4.3. FLUXOGRAMAS ...........................................................................................................................41 4.4. COMPONENTES BÁSICOS DE UM PROGRAMA ...............................................................................42

Page 6: Tratado Da Linguagem c

4.5. PROCEDIMENTO GERAL...............................................................................................................43 4.5.1. Compilação.........................................................................................................................43

5. FUNDAMENTOS DA LINGUAGEM C........................................................................................45

5.1. CARACTERÍSTICAS DA LINGUAGEM C .........................................................................................45 5.2. PONTOS POSITIVOS DA LINGUAGEM............................................................................................45 5.3. PONTOS NEGATIVOS....................................................................................................................46 5.4. O PADRÃO ANSI C.....................................................................................................................47 5.5. PALAVRAS RESERVADAS DA LINGUAGEM C ...............................................................................47 5.6. ESTRUTURA DE UM PROGRAMA EM C..........................................................................................47 5.7. CONJUNTO DE CARACTERES ........................................................................................................48 5.8. COMENTÁRIOS ............................................................................................................................48 5.9. DIRETIVAS DE COMPILAÇÃO .......................................................................................................49 5.10. DECLARAÇÃO DE VARIÁVEIS ..................................................................................................49 5.11. UMA INTRODUÇÃO ÀS ENTRADA E SAÍDA DE DADOS..............................................................50

5.11.1. Caracteres ..........................................................................................................................50 5.11.2. As Strings............................................................................................................................51 5.11.3. printf ...................................................................................................................................53 5.11.4. scanf....................................................................................................................................53

5.12. UMA INTRODUÇÃO AOS COMANDOS DE CONTROLE DE FLUXO ...............................................54 5.12.1. if..........................................................................................................................................54 5.12.2. for .......................................................................................................................................55

5.13. UMA BREVE INTRODUÇÃO ÀS FUNÇÕES..................................................................................57 5.13.1. Os Argumentos ...................................................................................................................57 5.13.2. O Retorno de Valores .........................................................................................................59

5.14. PRIMEIROS PASSOS..................................................................................................................60 5.15. EXERCÍCIOS.............................................................................................................................66 5.16. AVALIAÇÃO ............................................................................................................................66

6. ELEMENTOS DA LINGUAGEM..................................................................................................68

6.1. TOKENS DA LINGUAGEM C..........................................................................................................68 6.1.1. Caracteres de Espaço em Branco.......................................................................................68 6.1.2. Comentários em C ..............................................................................................................69 6.1.3. Avaliação dos Tokens .........................................................................................................70

6.2. KEYWORDS .................................................................................................................................71 6.3. IDENTIFICADORES .......................................................................................................................71

6.3.1. Caracteres Multibyte e Caracteres Longos ........................................................................72 6.3.2. Trigraphs ............................................................................................................................72

6.4. CONSTANTES...............................................................................................................................73 6.4.1. Constantes de Ponto Flutuante...........................................................................................73 6.4.2. Constantes Inteiras .............................................................................................................74 6.4.3. Constantes Caractere .........................................................................................................75 6.4.4. Strings Literais ...................................................................................................................77

6.5. CARACTERES ESPECIAIS E PONTUAÇÃO ......................................................................................78 6.6. EXERCÍCIOS.................................................................................................................................78

7. ESTRUTURA DE UM PROGRAMA ............................................................................................81

7.1. ARQUIVOS FONTE E PROGRAMAS FONTE ....................................................................................81 7.1.1. Diretivas de Pré-Processador ............................................................................................81 7.1.2. Pragmas..............................................................................................................................82 7.1.3. Declarações e Definições ...................................................................................................82 7.1.4. Declarações e Definições de Funções ................................................................................83 7.1.5. Blocos .................................................................................................................................83

7.2. A FUNÇÃO MAIN E A EXECUÇÃO DO PROGRAMA ........................................................................83 7.2.1. Os Argumentos argc e argv ................................................................................................84 7.2.2. Descrição dos Argumentos .................................................................................................85

7.3. TEMPO DE VIDA, ESCOPO, VISIBILIDADE E CONEXÕES ...............................................................86 7.3.1. Tempo de Vida....................................................................................................................86 7.3.2. Escopo e Visibilidade .........................................................................................................87 7.3.3. Encadeamentos...................................................................................................................88

Page 7: Tratado Da Linguagem c

7.4. ESPAÇOS DE NOMES....................................................................................................................89

8. TIPOS E DECLARAÇÕES.............................................................................................................92

8.1. DECLARAÇÕES ............................................................................................................................92 8.2. CLASSES DE ARMAZENAMENTO ..................................................................................................93

8.2.1. Especificadores de Classe de Armazenamento para Declarações de Nível Externo..........94 8.2.2. Especificadores de Classe de Armazenamento para Nível Interno ....................................96

8.3. ESPECIFICADORES DE TIPO..........................................................................................................99 8.3.1. Especificadores de Tipos de Dados e seus Equivalentes..................................................100

8.4. QUALIFICADORES DE TIPO ........................................................................................................101 8.5. DECLARAÇÕES DE VARIÁVEIS...................................................................................................102

8.5.1. Declaração de Variáveis Simples .....................................................................................103 8.5.2. Declarações de Enumeração............................................................................................103 8.5.3. Declarações de Estruturas ...............................................................................................106 8.5.4. Declarações de Unions.....................................................................................................109 8.5.5. Declarações de Arrays .....................................................................................................111 8.5.6. Declarações de Ponteiros.................................................................................................112 8.5.7. Declarações Abstratas......................................................................................................114

8.6. INTERPRETANDO DECLARADORES COMPLEXOS ........................................................................114 8.7. INICIALIZAÇÃO..........................................................................................................................117

8.7.1. Inicializando Tipos Escalares...........................................................................................117 8.7.2. Inicializando Tipos Compostos.........................................................................................118 8.7.3. Inicializando Strings.........................................................................................................121

8.8. ARMAZENAMENTO DE TIPOS BÁSICOS ......................................................................................121 8.8.1. Tipo char ..........................................................................................................................122 8.8.2. Tipo int .............................................................................................................................122 8.8.3. Tipo float ..........................................................................................................................123 8.8.4. Tipo double.......................................................................................................................125 8.8.5. Tipo long double...............................................................................................................125

8.9. TIPOS INCOMPLETOS .................................................................................................................125 8.10. DECLARAÇÕES TYPEDEF .......................................................................................................126 8.11. CONVERSÃO DE TIPOS...........................................................................................................127

9. OPERADORES E EXPRESSÕES................................................................................................129

9.1. INTRODUÇÃO.............................................................................................................................129 9.2. INTRODUÇÃO AOS OPERADORES ...............................................................................................129

9.2.1. Operadores Aritméticos e de Atribuição ..........................................................................129 9.2.2. Introdução aos Operadores Relacionais e Lógicos..........................................................131 9.2.3. Introdução aos Operadores Lógicos Bit a Bit ..................................................................132

9.3. EXPRESSÕES..............................................................................................................................133 9.3.1. Conversão Temporária de Tipos ......................................................................................134 9.3.2. Expressões Abreviadas .....................................................................................................134 9.3.3. Encadeamento de Expressões: o Operador “,” ...............................................................135 9.3.4. Precedências de Operadores............................................................................................135 9.3.5. Expressões Primárias em C..............................................................................................135 9.3.6. Expressões L-Value e R-Value..........................................................................................137 9.3.7. Expressões Constantes em C ............................................................................................137 9.3.8. Avaliação de Expressões em C .........................................................................................138 9.3.9. Pontos de Seqüência em C................................................................................................139

9.4. MODELADORES (CASTS) ............................................................................................................139 9.4.1. Conversões Cast ...............................................................................................................140

9.5. OPERADORES DA LINGUAGEM C ...............................................................................................141 9.5.1. Precedência e Ordem de Avaliação..................................................................................141 9.5.2. Conversões Aritméticas Usuais ........................................................................................144 9.5.3. Operadores Postfixados ...................................................................................................144 9.5.4. Operadores Unários em C................................................................................................149 9.5.5. Operadores Multiplicativos ..............................................................................................153 9.5.6. Operadores de Adição ......................................................................................................154 9.5.7. Operadores de Deslocamento de Bits...............................................................................155 9.5.8. Operadores Relacionais e de Igualdade...........................................................................156

Page 8: Tratado Da Linguagem c

9.5.9. Operadores Lógicos entre Bits .........................................................................................158 9.5.10. Operadores Lógicos .........................................................................................................158 9.5.11. Operadores de Expressão Condicional ............................................................................159 9.5.12. Operadores de Atribuição ................................................................................................160 9.5.13. Operador de Avaliação Seqüencial ..................................................................................162

9.6. CONVERSÕES DE TIPOS .............................................................................................................162 9.6.1. Conversões de Atribuição.................................................................................................163 9.6.2. Conversões de Outros Tipos.............................................................................................167

9.7. CONVERSÕES EM CHAMADAS DE FUNÇÃO ................................................................................167

10. CONTROLE DE FLUXO DE EXECUÇÃO – INSTRUÇÕES GERAIS..............................170

10.1. INSTRUÇÕES GERAIS .............................................................................................................170 10.2. EXPRESSÕES..........................................................................................................................171 10.3. A INSTRUÇÃO “IF” ................................................................................................................171

10.3.1. A instrução else ................................................................................................................172 10.3.2. O if-else-if .........................................................................................................................173 10.3.3. ifs Aninhados ....................................................................................................................173

10.4. O OPERADOR “? :” ................................................................................................................174 10.5. AS INSTRUÇÕES COMPOSTAS ................................................................................................175 10.6. A INSTRUÇÃO “SWITCH” .......................................................................................................176 10.7. A INSTRUÇÃO “FOR” .............................................................................................................178 10.8. A INSTRUÇÃO “BREAK”.........................................................................................................181 10.9. A INSTRUÇÃO “WHILE” .........................................................................................................181 10.10. A INSTRUÇÃO “DO - WHILE” .................................................................................................182 10.11. A INSTRUÇÃO “CONTINUE” ...................................................................................................184 10.12. A INSTRUÇÃO “GOTO” E OS LABELS .....................................................................................185 10.13. A INSTRUÇÃO NULL..............................................................................................................187 10.14. A INSTRUÇÃO “RETURN”.......................................................................................................187

11. TRABALHANDO COM ARRAYS ..........................................................................................193

11.1. ARRAYS ................................................................................................................................193 11.2. STRINGS ................................................................................................................................194

11.2.1. gets....................................................................................................................................195 11.2.2. strcpy ................................................................................................................................196 11.2.3. strcat .................................................................................................................................196 11.2.4. strlen .................................................................................................................................196 11.2.5. strcmp ...............................................................................................................................197

11.3. MATRIZES .............................................................................................................................197 11.3.1. Matrizes bidimensionais ...................................................................................................197 11.3.2. Matrizes de strings............................................................................................................198 11.3.3. Matrizes multidimensionais ..............................................................................................198 11.3.4. Uso de Arrays em Sistemas com Microcontroladores ......................................................199

12. PONTEIROS ..............................................................................................................................200

12.1. FUNCIONAMENTO..................................................................................................................200 12.2. DECLARAÇÃO E UTILIZAÇÃO ................................................................................................201 12.3. PONTEIROS E VETORES..........................................................................................................203

12.3.1. Ponteiros como Vetores....................................................................................................205 12.3.2. Strings...............................................................................................................................205 12.3.3. Endereços de elementos de vetores ..................................................................................205

12.4. VETORES DE PONTEIROS........................................................................................................206 12.5. PONTEIROS PARA PONTEIROS ................................................................................................206 12.6. CUIDADOS A SEREM TOMADOS AO SE USAR PONTEIROS.......................................................206 12.7. PONTEIROS E FUNÇÕES..........................................................................................................207 12.8. ALOCAÇÃO DINÂMICA DE MEMÓRIA ....................................................................................207

12.8.1. malloc ...............................................................................................................................208 12.8.2. calloc ................................................................................................................................208 12.8.3. realloc...............................................................................................................................209 12.8.4. free....................................................................................................................................209

12.9. ALOCAÇÃO DINÂMICA DE VETORES E MATRIZES .................................................................210

Page 9: Tratado Da Linguagem c

12.9.1. Alocação Dinâmica de Vetores ........................................................................................210 12.9.2. Alocação Dinâmica de Matrizes.......................................................................................210

13. AS FUNÇÕES.............................................................................................................................216

13.1. INTRODUÇÃO ÀS FUNÇÕES ....................................................................................................216 13.2. DEFINIÇÃO DE UMA FUNÇÃO.................................................................................................217

13.2.1. Atributos das Funções – Funções inline...........................................................................218 13.3. CLASSES DE ARMAZENAMENTO ............................................................................................219 13.4. TIPO DE RETORNO DE UMA FUNÇÃO......................................................................................219 13.5. PARÂMETROS DE UMA FUNÇÃO.............................................................................................222 13.6. CORPO DE UMA FUNÇÃO .......................................................................................................222 13.7. PROTÓTIPOS DE UMA FUNÇÃO...............................................................................................223 13.8. CHAMADAS DE FUNÇÃO........................................................................................................224

13.8.1. Argumentos.......................................................................................................................226 13.8.2. Chamadas com Número Variável de Argumentos ............................................................227

13.9. RECURSIVIDADE....................................................................................................................228 13.10. MODIFICADORES DE FUNÇÕES ..............................................................................................229

13.10.1. pascal............................................................................................................................230 13.10.2. cdecl..............................................................................................................................230 13.10.3. interrupt ........................................................................................................................230

13.11. PONTEIROS PARA FUNÇÕES ...................................................................................................230 13.12. ALGUMAS CONSIDERAÇÕES GERAIS .....................................................................................231 13.13. ESCOPO DE VARIÁVEIS..........................................................................................................231

13.13.1. Variáveis locais ............................................................................................................231 13.13.2. Parâmetros formais ......................................................................................................232 13.13.3. Variáveis globais ..........................................................................................................232

13.14. PASSAGEM DE PARÂMETROS POR VALOR E REFERÊNCIA ......................................................233 13.15. VETORES COMO ARGUMENTOS DE FUNÇÕES.........................................................................234 13.16. ARQUIVOS DE CABEÇALHO ...................................................................................................234

14. DIRETIVAS DE COMPILAÇÃO ............................................................................................240

14.1. A DIRETIVA #INCLUDE..........................................................................................................240 14.2. AS DIRETIVAS #DEFINE E #UNDEF .........................................................................................240 14.3. AS DIRETIVAS #IFDEF E #ENDIF.............................................................................................242 14.4. A DIRETIVA #IFNDEF.............................................................................................................243 14.5. A DIRETIVA #IF.....................................................................................................................243 14.6. A DIRETIVA #ELSE ................................................................................................................243 14.7. A DIRETIVA #ELIF .................................................................................................................244

15. ENTRADAS E SAÍDAS – I/O ...................................................................................................245

15.1. LENDO E ESCREVENDO CARACTERES....................................................................................245 15.1.1. getche e getch ...................................................................................................................245 15.1.2. putchar..............................................................................................................................246

15.2. LEITURA E ESCRITA DE STRINGS ...........................................................................................246 15.2.1. gets....................................................................................................................................246 15.2.2. puts ...................................................................................................................................247

15.3. ENTRADA E SAÍDA FORMATADA ...........................................................................................247 15.3.1. printf .................................................................................................................................247 15.3.2. scanf..................................................................................................................................248

15.4. OPERANDO COM ARQUIVOS ..................................................................................................249 15.4.1. fopen .................................................................................................................................249 15.4.2. fclose.................................................................................................................................250 15.4.3. exit ....................................................................................................................................251

15.5. LENDO E ESCREVENDO CARACTERES EM ARQUIVOS ............................................................251 15.5.1. putc() ................................................................................................................................251 15.5.2. getc ...................................................................................................................................252 15.5.3. feof ....................................................................................................................................252

15.6. OUTROS COMANDOS DE ACESSO A ARQUIVOS......................................................................253 15.6.1. Arquivos Predefinidos ......................................................................................................253 15.6.2. fgets ..................................................................................................................................253

Page 10: Tratado Da Linguagem c

15.6.3. fputs ..................................................................................................................................254 15.6.4. ferror e perror ..................................................................................................................254 15.6.5. fread..................................................................................................................................255 15.6.6. fwrite.................................................................................................................................255 15.6.7. fseek ..................................................................................................................................256 15.6.8. rewind ...............................................................................................................................256 15.6.9. remove ..............................................................................................................................256

15.7. FLUXOS PADRONIZADOS .......................................................................................................257 15.7.1. fprintf ................................................................................................................................257 15.7.2. fscanf ................................................................................................................................257

15.8. FUNÇÕES DE I/O - PORTAS ....................................................................................................258 15.8.1. _inp (Microsoft C e Visual C++) .....................................................................................258 15.8.2. _out (Microsoft C e Visual C++) .....................................................................................258 15.8.3. inportb (Borland C++).....................................................................................................258 15.8.4. outportb (Borland C++)...................................................................................................259

16. TIPOS DE DADOS ESPECIAIS...............................................................................................262

16.1. ESTRUTURAS (STRUCT) .........................................................................................................262 16.1.1. Passando para Funções....................................................................................................264 16.1.2. Ponteiros...........................................................................................................................264

16.2. UNION ...................................................................................................................................265 16.3. ENUMERAÇÕES (ENUM).........................................................................................................266 16.4. O COMANDO SIZEOF..............................................................................................................267 16.5. O COMANDO TYPEDEF ..........................................................................................................267 16.6. LISTAS SIMPLESMENTE ENCADEADAS...................................................................................268

17. INTERRUPÇÕES ......................................................................................................................271

17.1. INTRODUÇÃO.........................................................................................................................271 17.2. INTERRUPÇÕES E A ARQUITETURA INTEL ..............................................................................271 17.3. INTERRUPÇÕES DE HARDWARE .............................................................................................272 17.4. INSTRUÇÕES DE INTERRUPÇÃO DE HARDWARE .....................................................................274

17.4.1. Borland C++ - 80x86.......................................................................................................274 17.4.2. Franklin C – 8x51.............................................................................................................274 17.4.3. PCW CCS – Microchip PIC .............................................................................................274

17.5. IMPLEMENTANDO UMA ROTINA DE ATENDIMENTO A INTERRUPÇÃO (ISR) ...........................274 17.6. USANDO UMA NOVA ROTINA DE ATENDIMENTO A INTERRUPÇÃO - ISR ...............................276 17.7. O CONTROLADOR PROGRAMÁVEL DE INTERRUPÇÕES - PIC .................................................277

17.7.1. Redirecionamento IRQ2/IRQ9..........................................................................................278 17.8. ENDEREÇOS DOS CONTROLADORES PROGRAMÁVEIS DE INTERRUPÇÃO................................280

17.8.1. Initialization Command Word 1 (ICW1) ..........................................................................280 17.8.2. Initialization Command Word 2 (ICW2) ..........................................................................281 17.8.3. Initialization Command Word 3 (ICW3) ..........................................................................281 17.8.4. Initialization Command Word 4 (ICW4) ..........................................................................282 17.8.5. Operation Control Word 1 (OCW1) .................................................................................282 17.8.6. Operation Control Word 2 (OCW2) .................................................................................282 17.8.7. Operation Control Word 3 (OCW3) .................................................................................283

17.9. PROGRAMAÇÃO DO TIMER DO PC .........................................................................................283

18. A INTERFACE PARALELA....................................................................................................286

18.1. AS INTERFACES – UMA INTRODUÇÃO ...................................................................................286 18.2. A INTERFACE PARALELA.......................................................................................................286 18.3. INTERFACEAMENTO COM A PORTA PARALELA PADRÃO........................................................287 18.4. PROPRIEDADES DO HARDWARE.............................................................................................288 18.5. CENTRONICS .........................................................................................................................289 18.6. ENDEREÇOS DAS PORTAS .....................................................................................................290

18.6.1. Endereços Iniciais ............................................................................................................290 18.7. REGISTRADORES DA STANDARD PARALLEL PORT (SPP).......................................................291 18.8. PORTAS BIDIRECIONAIS.........................................................................................................293 18.9. USANDO A PORTA PARALELA PARA LER 8 BITS. ...................................................................295

18.9.1. Modo Nibble .....................................................................................................................296

Page 11: Tratado Da Linguagem c

18.10. USANDO A INTERRUPÇÃO DA PORTA PARALELA...................................................................298 18.11. MODOS DA PORTA PARALELA NO BIOS................................................................................300 18.12. MODOS DA PORTA PARALELA E EXTENDED CONTROL REGISTER DO MODO ECP .................301

18.12.1. Bit Function ..................................................................................................................301 18.12.2. Modos de Operação......................................................................................................301

19. INTERFACES SERIAIS ...........................................................................................................303

19.1. INTRODUÇÃO.........................................................................................................................303 19.2. PROPRIEDADES DE HARDWARE DOS PCS...............................................................................304

19.2.1. Conectores Seriais (DB-25 e DB-9) .................................................................................304 19.2.2. Funções dos Pinos ............................................................................................................305

19.3. NULL MODEMS .....................................................................................................................305 19.3.1. Conexão LoopBack...........................................................................................................306 19.3.2. Velocidades DTE / DCE ...................................................................................................307

19.4. CONTROLE DE FLUXO - FLOW CONTROL...............................................................................307 19.5. A UART (8250 E COMPATÍVEIS) ..........................................................................................308 19.6. TIPOS DE UARTS (PCS) ........................................................................................................310 19.7. REGISTRADORES DAS PORTAS SERIAIS (PCS) – ENDEREÇOS DAS PORTAS E IRQS................310 19.8. TABELA DE REGISTRADORES.................................................................................................311 19.9. DLAB ...................................................................................................................................312 19.10. INTERRUPT ENABLE REGISTER (IER) ....................................................................................313

19.10.1. Interrupt Identification Register (IIR) ..........................................................................313 19.10.2. First In / First Out Control Register (FCR)..................................................................314 19.10.3. Line Control Register (LCR) ........................................................................................315 19.10.4. Modem Control Register (MCR)...................................................................................316 19.10.5. Line Status Register (LSR)............................................................................................317 19.10.6. Modem Status Register (MSR)......................................................................................318 19.10.7. Register de uso Geral (Scratch)....................................................................................319

19.11. PROGRAMANDO (PCS) – RECEPÇÃO POR POLLING E POR INTERRUPÇÃO...............................319 19.12. VETORES DE INTERRUPÇÕES .................................................................................................321

19.12.1. Interrupt Service Routine (ISR) ....................................................................................322 19.13. CONFIGURAÇÃO DA UART ...................................................................................................323

19.13.1. Rotina Main (Loop) ......................................................................................................324 19.13.2. Determinando o Tipo de UART ....................................................................................324

19.14. INTERFACEANDO DISPOSITIVOS COM AS PORTAS RS-232 .....................................................324 19.14.1. Conversores de Nível RS-232 .......................................................................................325

19.15. INTERFACES COM MICROCONTROLADORES...........................................................................327 19.16. AS PORTAS NO SISTEMA WINDOWS ......................................................................................327 19.17. A ANTIGA MAS ÚTIL BIBLIOTECA DE COMUNICAÇÃO DA BORLAND .....................................327

APÊNDICE A - PADRÕES DE TRANSMISSÃO SERIAL...........................................................332

INTRODUÇÃO.........................................................................................................................................332 TRANSMISSÃO DE DADOS EM SISTEMAS DESBALANCEADOS (SINGLE-ENDED).....................................333 TRANSMISSÃO DE DADOS EM SISTEMAS BALANCEADOS (DIFERENCIAIS) .............................................334 COMPARAÇÃO ENTRE SISTEMAS BALANCEADOS E DESBALANCEADOS ..................................................335 DRIVERS DE LINHA DESBALANCEADOS ................................................................................................336 DRIVERS DE LINHA BALANCEADOS ......................................................................................................337 RECEPTORES BALANCEADOS ................................................................................................................337 PADRÕES DE TRANSMISSÃO TIA/EIA ...................................................................................................338 PADRÕES TIA/EIA DESBALANCEADOS.................................................................................................339

Padrão TIA/EIA-232-F (RS-232) .....................................................................................................339 TIA/EIA-423-B..................................................................................................................................340 TIA/EIA-562 .....................................................................................................................................341 TIA/EIA-694 .....................................................................................................................................342

PADRÕES TIA/EIA BALANCEADOS (DIFERENCIAL)..............................................................................342 TIA/EIA-422-B..................................................................................................................................342 TIA/EIA-485-A..................................................................................................................................345 TIA/EIA-612 .....................................................................................................................................348 TIA/EIA-644 (LVDS) ........................................................................................................................349

OUTROS PADRÕES TIA/EIA..................................................................................................................349

Page 12: Tratado Da Linguagem c

TIA/EIA-232-F..................................................................................................................................349 EIA-334-A.........................................................................................................................................349 EIA-363 ............................................................................................................................................350 EIA-404-A.........................................................................................................................................350 EIA-449 ............................................................................................................................................350 TIA/EIA-530-A..................................................................................................................................350 TIA/EIA-561 .....................................................................................................................................350 TIA/EIA-574 .....................................................................................................................................350 TIA/EIA-613 .....................................................................................................................................350 TIA/EIA-687 .....................................................................................................................................351 TIA/EIA-688 .....................................................................................................................................351 TIA/EIA-723 .....................................................................................................................................351

RECOMENDAÇÕES CCITT/ITU-T .........................................................................................................351 Recomendação V.10 .........................................................................................................................351 Recomendação V.11 .........................................................................................................................352 Recomendação V.24 .........................................................................................................................352 Recomendação V.28 .........................................................................................................................352 Recomendação V.35 .........................................................................................................................352

OUTROS PADRÕES.................................................................................................................................353 IEEE488 ...........................................................................................................................................353

CONFIGURAÇÃO DE SISTEMAS EIA-422 E EIA-485 ..............................................................................353 Controle Tristate de um dispositivo RS-485 utilizando o sinal RTS.................................................353 Configurações ..................................................................................................................................354 Sistemas a Dois ou Quatro Fios .......................................................................................................355 Terminação.......................................................................................................................................356 Polarizando uma rede RS-485..........................................................................................................358 Estendendo a Especificação .............................................................................................................360

SELEÇÃO DOS CABOS PARA RS-422 E RS-485 ......................................................................................360 Número de condutores......................................................................................................................360 Blindagem.........................................................................................................................................360 Características do Cabo...................................................................................................................360

PROTEÇÃO CONTRA TRANSIENTES NOS SISTEMAS RS-422 E RS-485.....................................................362 Com o que se parecem os surtos ? ...................................................................................................362 Modo Comum versus Modo Diferencial...........................................................................................364 Um terra não é igual a outro............................................................................................................364 Proteção contra Transientes usando Isolação .................................................................................365 Proteção contra Transientes utilizando Derivações Shunt ..............................................................366 Conectando os sinais de terra ..........................................................................................................367 Dispositivos de Derivação................................................................................................................367 Combinando Isolação com Derivação .............................................................................................367 Considerações Especiais para as Condições de Falha ....................................................................368 Escolha da proteção certa................................................................................................................369

SOFTWARE ............................................................................................................................................370 Considerações Básicas.....................................................................................................................370 Sistemas Master-Slave (Mestre-Escravo).........................................................................................371 Sistemas RS-485 Multimestre ...........................................................................................................372

APÊNDICE B - MICROCONTROLADORES MICROCHIP PIC DA FAMILIA 16FXXX .....373

APLICAÇÕES..........................................................................................................................................373 CARACTERÍSTICAS DOS MICROCONTROLADORES PIC ...........................................................................373 PIC 16F87X - CARACTERÍSTICAS..........................................................................................................373 PIC 16F87X - PERIFÉRICOS...................................................................................................................374 PINAGEM...............................................................................................................................................375 ARQUITETURA MICROCHIP PIC.............................................................................................................375 ORGANIZAÇÃO DA MEMÓRIA DE PROGRAMA .......................................................................................376 REGISTRADORES INTERNOS E MEMÓRIA DE DADOS..............................................................................377 MPLAB DA MICROCHIP .......................................................................................................................377 O COMPILADOR PCW DA CCS .............................................................................................................378 GRAVADOR ...........................................................................................................................................379 REGRAS DO COMPILADOR PCW DA CCS..............................................................................................379

Page 13: Tratado Da Linguagem c

Tipos de Variáveis Simples...............................................................................................................379 Biblioteca de Funções ......................................................................................................................379 Expressões Constantes .....................................................................................................................380 Identificadores: ................................................................................................................................380

DIRETIVAS DE COMPILADOR .................................................................................................................380 Algumas Diretivas ............................................................................................................................381

SEQÜÊNCIA DE PASSOS PARA IMPLEMENTAÇÃO DO SOFTWARE (FIRMWARE).......................................381 PORTAS DE I/O DIGITAIS .......................................................................................................................381

PORTA .............................................................................................................................................381 PORTB .............................................................................................................................................382 PORTC .............................................................................................................................................383 Outras Portas ...................................................................................................................................384

EXERCÍCIO 1 - OSCILADOR....................................................................................................................384 EXERCÍCIO 2 - OSCILADOR CONTROLADO ............................................................................................385 EXERCÍCIO 3 - ONDA QUADRADA .........................................................................................................386 TIMER 0.................................................................................................................................................386

Características do Timer 0 ...............................................................................................................386 Registradores....................................................................................................................................387 Funcionamento do Timer 0 ..............................................................................................................388

EXERCÍCIO 4 - OSCILADOR UTILIZANDO O TIMER 0 ..............................................................................388 EXERCÍCIO 5 - OSCILADOR UTILIZANDO A INTERRUPÇÃO DO TIMER 0..................................................389 TIMER 1.................................................................................................................................................391

Características .................................................................................................................................391 Esquema Elétrico .............................................................................................................................392 Registradores Associados ao Timer1 ...............................................................................................392

EXERCÍCIO 6 - OSCILADOR UTILIZANDO A INTERRUPÇÃO DO TIMER 1..................................................393 USART OU SCI ....................................................................................................................................394

Modos de Operação .........................................................................................................................394 Registradores Associados.................................................................................................................395

USART BAUD RATE GENERATOR (BRG) ............................................................................................396 TRANSMISSÃO ASSÍNCRONA .................................................................................................................397 EXERCÍCIO 7 - TRANSMISSOR SERIAL....................................................................................................398 EXERCÍCIO 8 - TRANSMISSOR SERIAL 2 .................................................................................................399 EXERCÍCIO 9 - RECEPTOR SERIAL..........................................................................................................399 TIMER 2.................................................................................................................................................401

Características .................................................................................................................................401 Diagrama do Timer 2 .......................................................................................................................402 Registradores Associados ao Timer 2 ..............................................................................................402

MÓDULO CCP (COMPARE, CAPTURE AND PWM MODULE)..................................................................403 Características Gerais......................................................................................................................403 Recursos ...........................................................................................................................................403 Registradores Associados ao Módulo CCP......................................................................................403 Modo Capture...................................................................................................................................404 Modo Compare.................................................................................................................................405 Modo PWM.......................................................................................................................................405 PWM - Aplicações ............................................................................................................................406

EXERCÍCIO 10 - PWM...........................................................................................................................406 PORTA SERIAL SÍNCRONA (SSP) ...........................................................................................................409

Características .................................................................................................................................409 Registradores Associados à Porta Serial Síncrona..........................................................................410 Modo SPI..........................................................................................................................................411 SPI - Operação.................................................................................................................................412 SPI - Registradores Associados........................................................................................................414

EXERCÍCIO 11 - SPI...............................................................................................................................414 CONVERSOR ANALÓGICO-DIGITAL (ADC) ...........................................................................................416

ADC - Características ......................................................................................................................416 Diagrama do ADC............................................................................................................................417 Registradores Associados ao ADC...................................................................................................418

WATCHDOG TIMER E O MODO SLEEP (POWER DOWN)..........................................................................419 Watchdog Timer ...............................................................................................................................419

Page 14: Tratado Da Linguagem c

Sleep - Power Down .........................................................................................................................419 Acordando o Dispositivo ..................................................................................................................420

ICSP (IN CICUIT SERIAL PROGRAMMING).............................................................................................420 Circuito Típico .................................................................................................................................420

EXERCÍCIO 12........................................................................................................................................420

APÊNDICE C - MICROCONTROLADORES DA FAMÍLIA INTEL 8051................................421

APLICAÇÕES..........................................................................................................................................421 MICROCONTROLADORES 8X5X..............................................................................................................421 FABRICANTES........................................................................................................................................421 AT89C52 - CARACTERÍSTICAS .............................................................................................................421 APPLICATION BUILDER DA INTEL®.......................................................................................................422 PINAGEM DO 8051.................................................................................................................................422 ARQUITETURA.......................................................................................................................................424 ORGANIZAÇÃO DA MEMÓRIA DE PROGRAMA .......................................................................................425 REGISTRADORES INTERNOS E MEMÓRIA DE DADOS..............................................................................425 INTERRUPÇÕES......................................................................................................................................427 O SIMULADOR - JSIM ...........................................................................................................................427

Ferramenta de simulação.................................................................................................................427 O JENS EDITOR - JFE ............................................................................................................................428 ADICIONAR A CHAMADA AO COMPILADOR ...........................................................................................429 ADICIONAR A CHAMADA AO LINKER .....................................................................................................429 ADICIONAR A CHAMADA AO SIMULADOR JSIM.....................................................................................430 ADICIONAR A CHAMADA AO CONVERSOR (ABS TO HEX) OHS51...........................................................430 ADICIONAR A CHAMADA AO CONVERSOR (HEX TO BIN) HEXBIN ...........................................................431 O COMPILADOR C51 DA FRANKLIN SOFTWARE....................................................................................431 GRAVADOR ...........................................................................................................................................431 REGRAS DO COMPILADOR C51 DA KEIL ...............................................................................................431 BIBLIOTECA DE FUNÇÕES......................................................................................................................431 SEQÜÊNCIA DE PASSOS PARA IMPLEMENTAÇÃO....................................................................................432 PORTAS DE I/O DIGITAIS .......................................................................................................................432

Características .................................................................................................................................432 Porta P0 ...........................................................................................................................................432 Porta P1 ...........................................................................................................................................432 Porta P2 ...........................................................................................................................................433 Porta P3 ...........................................................................................................................................433

PROBLEMA 1 - OSCILADOR ...................................................................................................................434 PROBLEMA 2 - OSCILADOR CONTROLADO ............................................................................................435 EXERCÍCIO 3 - ONDA QUADRADA .........................................................................................................436 OS TEMPORIZADORES TIMER 0, TIMER 1 E TIMER 2..............................................................................436

Características do Timer 0 e Timer 1...............................................................................................436 Timer 0 e 1 - Operação ....................................................................................................................436 Timer 0 e 1- Modo 0 .........................................................................................................................436 Timer 0 e 1 - Modo 1 ........................................................................................................................437 Timer 0 e 1 - Modo 2 ........................................................................................................................437 Timer 0 e 1- Modo 3 .........................................................................................................................437 Registradores Associados ao Timer/Counter ...................................................................................438 Timer 2 - Características..................................................................................................................440

EXERCÍCIO 4 - OSCILADOR UTILIZANDO O TIMER 0 ..............................................................................440 EXERCÍCIO 5 - OSCILADOR UTILIZANDO A INTERRUPÇÃO DO TIMER 0..................................................441 USART (UNIVERSAL SYNCHRONOUS ASYNCHRONOUS RECEIVER TRANSMITTER)..............................443

Modos de Operação .........................................................................................................................443 Funcionamento.................................................................................................................................443 Registradores Associados.................................................................................................................444 USART - Modo 0 ..............................................................................................................................445 USART - Modo 1 ..............................................................................................................................445 USART - Modo 2 ..............................................................................................................................445 USART - Modo 3 ..............................................................................................................................445

EXERCÍCIO 6 - TRANSMISSOR SERIAL....................................................................................................445 EXERCÍCIO 7 - TRANSMISSOR SERIAL 2 .................................................................................................448

Page 15: Tratado Da Linguagem c

EXERCÍCIO 8 - RECEPTOR SERIAL..........................................................................................................449 EXERCÍCIO 9 – COMUNICAÇÃO SERIAL.................................................................................................450

APÊNDICE D - AS PORTAS DE COMUNICAÇÃO NO SISTEMA MS WINDOWS...............451

OS RECURSOS DE COMUNICAÇÃO NO WINDOWS® ...............................................................................451 MANIPULADORES DE RECURSOS DE COMUNICAÇÃO.............................................................................452 MODIFICAÇÕES DOS PARÂMETROS DOS RECURSOS DE COMUNICAÇÃO ................................................452 UTILIZANDO AS FUNÇÕES DE COMUNICAÇÃO .......................................................................................454 A ESTRUTURA DCB..............................................................................................................................456 CONFIGURAÇÃO DOS RECURSOS DE COMUNICAÇÃO.............................................................................460 CONFIGURAÇÃO DO MODEM .................................................................................................................461 OPERAÇÕES DE LEITURA E ESCRITA......................................................................................................463 OPERAÇÕES SOBREPOSTAS ...................................................................................................................464 TIMEOUTS .............................................................................................................................................465 ERROS DE COMUNICAÇÃO.....................................................................................................................467 EVENTOS DE COMUNICAÇÃO.................................................................................................................468 REFERÊNCIA DE FUNÇÕES DE COMUNICAÇÃO.......................................................................................469 EXEMPLO FUNCIONAL...........................................................................................................................470

APÊNDICE E - OS CONECTORES................................................................................................479

PORTAS DE TECLADO E MOUSE (PS/2,AT,XT) .....................................................................................479 PS/2 - ATX........................................................................................................................................479 Teclado AT .......................................................................................................................................479

PORTA DE JOGOS (JOYSTICK) ................................................................................................................480 PORTAS SERIAIS COM (RS-232)...........................................................................................................481

Porta Serial (DB-25) ........................................................................................................................481 Porta Serial (DB-9) ..........................................................................................................................482 Porta Serial (RJ-45) .........................................................................................................................483 Adaptador serial de 9 pinos para 25 ................................................................................................484

PORTA PARALELA - LPT / IMPRESSORA................................................................................................484 Conector de Porta Paralela ECP.....................................................................................................485

USB ......................................................................................................................................................486 PORTAS DE DISPLAY .............................................................................................................................487

RGBI Digital (MDA,CGA,EGA,NEC) .............................................................................................487 RGB Analógico (VGA,SVGA,VESA).................................................................................................487

INTERFACE DE REDE ETHERNET (AUI,BNC,TP,FL).............................................................................488 AUI ...................................................................................................................................................488 BNC (Coaxial) ..................................................................................................................................488 TP - Twisted-Pair Ethernet ..............................................................................................................489

ISA .......................................................................................................................................................490 EISA.....................................................................................................................................................492 IDE INTERNO ........................................................................................................................................494 SLOT DE EXPANSÃO PCI .......................................................................................................................495 PCMCIA...............................................................................................................................................499 CONECTORES DE ALIMENTAÇÃO DO PC................................................................................................501

Conector de Alimentação ATX .........................................................................................................501 Conector de Alimentação XT/AT......................................................................................................501 Conector de Alimentação 5¼ ...........................................................................................................501 Conector de Alimentação 3½ ...........................................................................................................502

TABELA DE REFERENCIA DE CONECTORES............................................................................................503

APÊNDICE F - EXEMPLOS DE APLICAÇÃO ............................................................................509

ADC .....................................................................................................................................................509 DAC .....................................................................................................................................................511 TECLADO...............................................................................................................................................511 LCD......................................................................................................................................................512 PWM ....................................................................................................................................................514 RELÉ .....................................................................................................................................................516 MOTOR DE PASSO .................................................................................................................................516 CALENDÁRIO.........................................................................................................................................520

Page 16: Tratado Da Linguagem c

MANIPULAÇÃO DE STRINGS...................................................................................................................521 CÓPIA DE STRINGS.................................................................................................................................523 INVERSAO DE STRINGS ..........................................................................................................................524 CONCATENAÇÃO DE STRINGS ................................................................................................................524 NÚMEROS COMPLEXOS .........................................................................................................................525 INTEGRAÇÃO.........................................................................................................................................526 RUNGE KUTTA ......................................................................................................................................535 AUTOVALORES DE UMA MATRIZ - HOUSEHOLDER ................................................................................537 CALCULO DE π PELO MÉTODO DE MONTECARLO..................................................................................540 BÁSKARA ..............................................................................................................................................542 FÓRMULA..............................................................................................................................................542 FATORIAL..............................................................................................................................................543 DIFERENÇA DE ÂNGULOS ......................................................................................................................543 ORDEM CRESCENTE ..............................................................................................................................544

APÊNDICE G - PRIMEIROS PASSOS...........................................................................................545

MICROSOFT VISUAL C++4.0, 5.0 E 6.0 ................................................................................................545 BORLAND BUILDER C++ 3.0, 4.0 E 5.0 .................................................................................................551 BORLAND C++ 3.0 E 3.1 .......................................................................................................................556

GLOSSÁRIO ..........................................................................................................................................560

REFERÊNCIAS .....................................................................................................................................561

BIBLIOGRAFIA.......................................................................................................................................561 WEB......................................................................................................................................................561

ÍNDICE ...................................................................................................................................................562

Page 17: Tratado Da Linguagem c

ÍÍnnddiiccee ddee FFiigguurraass FIGURA 2-1 – PROCESSO DE GRAVAÇÃO .......................................................................................................7 FIGURA 2-2 – PROCESSADOR 8051 FX INTEL ...............................................................................................9 FIGURA 2-3 - EXEMPLO DE FLUXOGRAMA..................................................................................................13 FIGURA 3-1 – ANTIGOS SISTEMAS DE NUMERAÇÃO.....................................................................................17 FIGURA 3-2 – PORTA AND .........................................................................................................................25 FIGURA 3-3 – PORTA OR ............................................................................................................................26 FIGURA 3-4 – PORTA XOR .........................................................................................................................27 FIGURA 3-5 – PORTA NOT..........................................................................................................................27 FIGURA 3-6 – PORTAS NAND E NOR ........................................................................................................27 FIGURA 4-1 – BLOCO DE PROCESSO ............................................................................................................41 FIGURA 4-2 – BLOCO DE TOMADA DE DECISÃO...........................................................................................42 FIGURA 4-3 – FLUXOGRAMA GENÉRICO .....................................................................................................42 FIGURA 4-4 – PROCEDIMENTO DE GERAÇÃO DE UM PROGRAMA EXECUTÁVEL ...........................................44 FIGURA 5-1 – ESTRUTURAÇÃO DE UM PROGRAMA EM C.............................................................................61 FIGURA 17-1 – CONTROLADOR PROGRAMÁVEL DE INTERRUPÇÕES..........................................................277 FIGURA 17-2 – REDIRECIONAMENTO DE IRQ2/IRQ9................................................................................279 FIGURA 18-1 – CONECTOR DE DB-25 .......................................................................................................288 FIGURA 18-2 - HANDSHAKE DO PADRÃO CENTRONICS .............................................................................289 FIGURA 18-3- PORTA PARALELA BIDIRECIONAL........................................................................................294 FIGURA 18-4 – USANDO A PORTA PARALELA PARA LER OITO BITS ............................................................295 FIGURA 18-5 – OITO ENTRADAS UTILIZANDO UM MULTIPLEXADOR 74LS157 ..........................................297 FIGURA 19-1 – DIAGRAMA DE CONEXÃO DE UM NULL MODEM................................................................305 FIGURA 19-2 – DIAGRAMA DE CONEXÃO LOOPBACK................................................................................306 FIGURA 19-3 – PINAGEM DAS UARTS 16550, 16450 E 8250 ...................................................................308 FIGURA 19-4 – FORMA DE ONDA TTL/CMOS PARA O CANAL SERIAL......................................................325 FIGURA 19-5 – FORMA DE ONDA NO PADRÃO RS-232...............................................................................325 FIGURA 19-6 – PINAGEM E DIAGRAMA ESQUEMÁTICO DO DRIVER TRANSCEPTOR MAX232.....................326 FIGURA 19-7 – CONVERSOR DE NÍVEIS TTL/CMOS PARA RS-232...........................................................327 FIGURA 20-1 - TRANSMISSÃO DE DADOS EM SISTEMAS DESBALANCEADOS - 3 CANAIS, 4 LINHAS ..........333 FIGURA 20-2 - SISTEMA BALANCEADO DE TRANSMISSÃO - 3 CANAIS, 6 LINHAS E SINAL DE TERRA ........334 FIGURA 20-3 – CIRCUITO DE INTERFACE RS-232 .....................................................................................336 FIGURA 20-4 – DRIVER DE LINHA DIFERENCIAL BALANCEADO................................................................337 FIGURA 20-5 – RECEPTOR DE LINHA DIFERENCIAL BALANCEADO ...........................................................338 FIGURA 20-6 - APLICAÇÃO TÍPICA TIA/EIA-232-F ..................................................................................339 FIGURA 20-7 - APLICAÇÃO TÍPICA TIA/EIA-423-B..................................................................................340 FIGURA 20-8 - APLICAÇÃO TÍPICA TIA/EIA-561 .....................................................................................341 FIGURA 20-9- APLICAÇÃO TÍPICA TIA/EIA-694 .......................................................................................342 FIGURA 20-10 - APLICAÇÃO TIA/EIA-422-B PONTO A PONTO ...............................................................343 FIGURA 20-11 - APLICAÇÃO TÍPICA TIA/EIA-422-B MULTIDROP............................................................344 FIGURA 20-12 – REDE RS-422 DE 4 FIOS TÍPICA.......................................................................................344 FIGURA 20-13 – RELAÇÕES ENTRE OS TERMINAIS ‘A’ E ‘B’ NOS DISPOSITIVOS RS-422 OU RS-485 E OS

IDENTIFICADORES CONVENCIONAIS ‘+’ E ‘-‘. ....................................................................................345 FIGURA 20-14 - APLICAÇÃO TÍPICA TIA/EIA-485-A................................................................................346 FIGURA 20-15 – REDE MULTIDROP RS-485 A DOIS FIOS ...........................................................................347 FIGURA 20-16 – REDE MULTIDROP RS-485 A QUATRO FIOS .....................................................................348 FIGURA 20-17 - APLICAÇÃO TÍPICA TIA/EIA-612 E TIA/EIA-644 PONTO A PONTO................................349 FIGURA 20-18 – DIAGRAMA DE TEMPOS PARA UM CONVERSOR RS-232 PARA RS-485 COM CONTROLE RTS

DO DRIVER E RECEPTOR RS-485........................................................................................................354 FIGURA 20-19 – TOPOLOGIAS DE REDE.....................................................................................................354 FIGURA 20-20 – CONFIGURAÇÃO TÍPICA DE REDE MULTIDROP RS-485 A QUATRO FIOS ..........................355 FIGURA 20-21 – CONFIGURAÇÃO TÍPICA DE REDE MULTIDROP RS-485 A DOIS FIOS ................................356 FIGURA 20-22 - TERMINAÇÃO EM PARALELO E COM ACOPLAMENTO AC..................................................357 FIGURA 20-23 – TERMINAÇÃO DE REDES..................................................................................................358 FIGURA 20-24 - TRANSCEPTOR COM RESISTORES DE POLARIZAÇÃO .........................................................359

Page 18: Tratado Da Linguagem c

FIGURA 20-25 – TAXA DE TRANSMISSÃO DE DADOS VERSUS COMPRIMENTO DO CABO PARA INTERFACES

BALANCEADAS, UTILIZANDO UM CABO DE PAR TRANÇADO 24 AWG................................................361 FIGURA 20-26 – TAXA DE DADOS POR COMPRIMENTO DE CABO, COMO CONSEQÜÊNCIA DOS ATRASOS

FINITOS DE PROPAGAÇÃO DA LINHA DE TRANSMISSÃO ......................................................................361 FIGURA 20-27 – ATENUAÇÃO VERSUS FREQÜÊNCIA PARA VÁRIOS TIPOS DE CABOS .................................362 FIGURA 20-28 – FORMA DE ONDA DE TENSÃO DO SINAL DE COMBINAÇÃO ...............................................363 FIGURA 20-29 – FORMA DE ONDA DE CORRENTE DO SINAL DE COMBINAÇÃO ...........................................363 FIGURA 20-30 – RING WAVE DE 100 KHZ ................................................................................................363 FIGURA 20-31 – UM TERRA NÃO É IGUAL A OUTRO...................................................................................364 FIGURA 20-32 – DISPOSITIVO RS-485 ISOLADO .......................................................................................365 FIGURA 20-33 – DISPOSITIVO RS-485 COM SINAL DE TERRA CONECTADO NA MASSA ..............................365 FIGURA 20-34 – CONEXÃO DO SINAL DE TERRA ENTRE DOIS NÓS COM UM RESISTOR DE 100 Ω................367 FIGURA 20-35 – NÓ ISOLADO COM PROTEÇÃO SHUNT PARA O TERRA .......................................................368 FIGURA 20-36 – PORTA ISOLADA COM PROTEÇÃO SHUNT NÃO ATERRADA ...............................................368 FIGURA 20-37 – PROTEÇÃO DA PORTA COM FUSÍVEIS ...............................................................................369 FIGURA 20-38 – EXEMPLO DE CIRCUITO COM PROTEÇÃO DE LINHA..........................................................369 FIGURA 20-39 – EXEMPLO DE CIRCUITO COMUM PARA PROTEÇÃO DE LINHA............................................370 FIGURA 21-1 – PINAGEM DO MICROCONTROLADOR PIC 16F877..............................................................375 FIGURA 21-2 – ARQUITETURA DO MICROCONTROLADOR PIC 16F877......................................................375 FIGURA 21-3 – ORGANIZAÇÃO DA MEMÓRIA DE PROGRAMA DO MICROCONTROLADOR PIC 16F877........376 FIGURA 21-4 – REGISTRADORES INTERNOS DO MICROCONTROLADOR PIC 16F877..................................377 FIGURA 21-5 – APARÊNCIA DO SOFTWARE MPLAB .................................................................................378 FIGURA 21-6 – APARÊNCIA DO COMPILADOR PCW ..................................................................................379 FIGURA 21-7 – PORTA A...........................................................................................................................382 FIGURA 21-8 – PINO RA4 .........................................................................................................................382 FIGURA 21-9 – PORTA B ...........................................................................................................................383 FIGURA 21-10 – PORTA C .........................................................................................................................384 FIGURA 21-11 – EXERCÍCIO 1 NO MPLAB ...............................................................................................385 FIGURA 21-12 – EXERCÍCIO 2 NO MPLAB ...............................................................................................386 FIGURA 21-13 – TIMER 0 ..........................................................................................................................387 FIGURA 21-14 – TEMPORIZAÇÃO DO TIMER 0: CLOCK INTERNO SEM PRESCALER.....................................388 FIGURA 21-15 – TEMPORIZAÇÃO DO TIMER 0: CLOCK INTERNO COM PRESCALER 1:2 ..............................388 FIGURA 21-16 – OSCILADOR UTILIZANDO O TIMER 0 NO MPLAB ...........................................................389 FIGURA 21-17 – OSCILADOR UTILIZANDO A INTERRUPÇÃO DO TIMER 0 NO MPLAB ...............................391 FIGURA 21-18 – DIAGRAMA DO TIMER 1 ..................................................................................................392 FIGURA 21-19 – OSCILADOR UTILIZANDO A INTERRUPÇÃO DO TIMER 1 NO MPLAB ...............................394 FIGURA 21-20 – DIAGRAMA DE BLOCOS DO TRANSMISSOR DA USART....................................................397 FIGURA 21-21 – TEMPORIZAÇÃO DA TRANSMISSÃO ASSÍNCRONA DO MESTRE..........................................397 FIGURA 21-22 – DIAGRAMA DO TIMER 2 ..................................................................................................402 FIGURA 21-23 – SAÍDA PWM ...................................................................................................................405 FIGURA 21-24 – CONEXÃO SPI MESTRE-ESCRAVO ..................................................................................412 FIGURA 21-25 – FORMAS DE ONDA NO MODO SPI MESTRE ......................................................................413 FIGURA 21-26 – FORMAS DE ONDA NO MODO SPI ESCRAVO (MODO DE SELEÇÃO DE ESCRAVO COM CKE =

1........................................................................................................................................................413 FIGURA 21-27 – DIAGRAMA DE BLOCOS DO CONVERSOR A/D DE 8 BITS ..................................................417 FIGURA 21-28 – MODELO DE ENTRADA ANALÓGICA.................................................................................419 FIGURA 21-29 – FUNÇÃO DE TRANSFERÊNCIA DO A/D .............................................................................419 FIGURA 21-30 – CIRCUITO TÍPICO DE APLICAÇÃO ICSP............................................................................420 FIGURA 22-1 – APARÊNCIA DO APPLICATION BUILDER DA INTEL ............................................................422 FIGURA 22-2 – PINAGEM DOS MICROCONTROLADORES 8051, ENCAPSULAMENTO PQFF..........................422 FIGURA 22-3 – PINAGEM DOS MICROCONTROLADORES 8051, ENCAPSULAMENTO PDIP ..........................423 FIGURA 22-4 – PINAGEM DOS MICROCONTROLADORES 8051, ENCAPSULAMENTO PLCC .........................423 FIGURA 22-5 – ARQUITETURA DOS MICROCONTROLADORES 8052............................................................424 FIGURA 22-6 – ORGANIZAÇÃO DA MEMÓRIA DE PROGRAMA DOS MICROCONTROLADORES 8052..............425 FIGURA 22-7 – MEMÓRIA DE DADOS DOS MICROCONTROLADORES 8052..................................................425 FIGURA 22-8 – REGISTRADORES INTERNOS DOS MICROCONTROLADORES 8052........................................426 FIGURA 22-9 – INTERRUPÇÕES DOS MICROCONTROLADORES 8052...........................................................427 FIGURA 22-10 – APARÊNCIA DO SOFTWARE JSIM ....................................................................................428 FIGURA 22-11 – APARÊNCIA DO SOFTWARE JFE.......................................................................................428 FIGURA 22-12 – ADICIONANDO A CHAMADA AO COMPILADOR.................................................................429

Page 19: Tratado Da Linguagem c

FIGURA 22-13 – ADICIONANDO A CHAMADA AO LINKER ..........................................................................429 FIGURA 22-14 – ADICIONANDO A CHAMADA AO JSIM .............................................................................430 FIGURA 22-15 – ADICIONANDO A CHAMADA AO CONVERSOR PARA HEX ................................................430 FIGURA 22-16 – ADICIONANDO A CHAMADA O CONVERSOR PARA BINÁRIO..............................................431 FIGURA 22-17 – BUFFERS DE I/O E LATCHES DA PORTA P0.......................................................................432 FIGURA 22-18 – DIAGRAMAS DAS PORTAS P1 E P3...................................................................................433 FIGURA 22-19 – DIAGRAMAS DA PORTA P2 ..............................................................................................433 FIGURA 22-20 – OSCILADOR UTILIZANDO O MS VISUAL C++ 5.0 COMO EDITOR.....................................434 FIGURA 22-21 – OSCILADOR CONTROLADO ..............................................................................................435 FIGURA 22-22 – TEMPORIZADOR/CONTADOR 0 NO MODO 0: CONTADOR DE 13 BITS ...............................436 FIGURA 22-23 – TEMPORIZADOR/CONTADOR 0 NO MODO 1 .....................................................................437 FIGURA 22-24 – TEMPORIZADOR/CONTADOR 1 NO MODO 2: CONTADOR DE 8 BITS COM AUTO-RECARGA437 FIGURA 22-25 – TEMPORIZADOR/CONTADOR 0 NO MODO 3: DOIS CONTADORES DE 8 BITS .....................438 FIGURA 22-26 – REGISTRADOR TMOD ....................................................................................................438 FIGURA 22-27 – REGISTRADOR TCON .....................................................................................................439 FIGURA 22-28 – REGISTRADOR IE ............................................................................................................439 FIGURA 22-29 - OSCILADOR UTILIZANDO O TIMER 0 ................................................................................441 FIGURA 22-30 - OSCILADOR UTILIZANDO A INTERRUPÇÃO DO TIMER 0 USANDO O JFE............................443 FIGURA 22-31 – REGISTRADOR SCON .....................................................................................................444 FIGURA 22-32 – REGISTRADOR SCON (CONT.) ........................................................................................444 FIGURA 22-33 – FORMA DE ONDA NO CANAL SERIAL DO 8051..................................................................445 FIGURA 22-34 - TRANSMISSOR SERIAL......................................................................................................449 FIGURA 24-1 - CONECTOR FÊMEA DE 6 PINOS MINI-DIN6........................................................................479 FIGURA 24-2 – TECLADO AT - CONECTOR FÊMEA DE 5 PINOS DIN5 (DIN41524) NO COMPUTADOR.......480 FIGURA 24-3 – CONECTOR DA PORTA DE JOYSTICK ..................................................................................480 FIGURA 24-4 - CONECTOR DE 25 PINOS SUB-D MACHO NO DTE (COMPUTADOR)...................................481 FIGURA 24-5 - CONECTOR DE 25 PINOS SUB-D FÊMEA NO DCE (MODEM)..............................................481 FIGURA 24-6 - NO COMPUTADOR, CONECTOR DE 9 PINOS SUB-D MACHO ...............................................482 FIGURA 24-7 – CONECTOR SERIAL RJ-45..................................................................................................483 FIGURA 24-8 - CONECTOR DB9 FÊMEA PARA CONEXÃO COM O PC: .........................................................484 FIGURA 24-9 - CONECTOR DB25 MACHO PARA CONEXÃO COM O CABO SERIAL: ......................................484 FIGURA 24-10 - CONECTOR DB25 NA PORTA PARALELA ..........................................................................484 FIGURA 24-11 - CONECTOR DB25 NA PORTA PARALELA ECP ..................................................................486 FIGURA 24-12 - CONECTOR RGBI ............................................................................................................487 FIGURA 24-13 - CONECTOR RGB ANALÓGICO (VGA, SVGA, VESA) ....................................................487 FIGURA 24-14 – CONECTORES DE REDE BNC, AUI E TP..........................................................................488 FIGURA 24-15 – CONECTOR TP FÊMEA NA PLACA DA INTERFACE ............................................................489 FIGURA 24-16 – CONECTOR TP MACHO NO CABO.....................................................................................489 FIGURA 24-17 – CONECTOR DA PLACA PERIFÉRICA ISA ...........................................................................490 FIGURA 24-18 – CONECTOR ISA NA PLACA MÃE ......................................................................................490 FIGURA 24-19 – CONECTOR DA PLACA PERIFÉRICA EISA.........................................................................492 FIGURA 24-20 – CONECTOR EISA NA PLACA MÃE....................................................................................492 FIGURA 24-21 – CONECTOR IDE MACHO.................................................................................................494 FIGURA 24-22 – CONECTOR IDE FÊMEA..................................................................................................495 FIGURA 24-23 – CONECTOR PCI ...............................................................................................................496 FIGURA 24-24 – CONECTOR PCI UNIVERSAL 32/64 BITS..........................................................................496 FIGURA 24-25 – CONECTOR PCI 5 V 32/64 BITS ......................................................................................496 FIGURA 24-26 – CONECTOR PCI 3.3 V 32/64 BITS ...................................................................................496

Page 20: Tratado Da Linguagem c

ÍÍnnddiiccee ddee TTaabbeellaass TABELA 3-1 – SISTEMA BINÁRIO.................................................................................................................19 TABELA 3-2 – EQUIVALÊNCIA ENTRE OS SISTEMAS BINÁRIO E HEXADECIMAL............................................24 TABELA 3-3 - EXEMPLOS DE EXTENSÃO DE SINAL ......................................................................................31 TABELA 3-4 – MAIS EXEMPLOS DE EXTENSÃO DE SINAL.............................................................................31 TABELA 3-5 – SUBGRUPO DE BITS DE CONTROLE........................................................................................33 TABELA 3-6 – VALORES ASCII DOS DÍGITOS NUMERAIS ............................................................................34 TABELA 3-7 – TABELA ASCII.....................................................................................................................37 TABELA 6-1 – EXEMPLOS DE TRIGRAPHS ....................................................................................................73 TABELA 6-2 – SEQÜÊNCIAS DE ESCAPE.......................................................................................................76 TABELA 7-1– EXEMPLO DE DIRETIVAS DE PRÉ-PROCESSADOR. ...................................................................82 TABELA 7-2 – TEMPO DE VIDA E ESCOPO ....................................................................................................88 TABELA 8-1 – ESPECIFICADORES DE TIPO.................................................................................................100 TABELA 8-2 – TIPOS DE VARIÁVEIS ..........................................................................................................102 TABELA 8-3 – TIPOS DE DADOS DE UM COMPILADOR PARA PROCESSADORES DA FAMÍLIA 8X86 DE 32 BITS

(VISUAL C++ E BORLAND C++ 4.0): ................................................................................................122 TABELA 8-4 – TIPOS DE DADOS DE UM COMPILADOR PARA PROCESSADOR PIC DE 8 BITS CCS PCW: .....122 TABELA 8-5 - TIPOS DE DADOS DE UM COMPILADOR PARA PROCESSADOR DA FAMÍLIA 8051 DE 8 BITS (EX.

FRANKLIN, KEIL E ARCHIMEDES): ....................................................................................................122 TABELA 8-6 – TIPOS DE DADOS DE UM COMPILADOR PARA PROCESSADOR DA FAMÍLIA 8X86 DE 16 BITS (EX.

BORLAND TURBO C++ 3.X E MICROSOFT C): ...................................................................................122 TABELA 8-7 – CARACTERÍSTICAS DOS TIPOS FLOAT E DOUBLE .................................................................123 TABELA 8-8 - COMPRIMENTO DO EXPOENTE E MANTISSA .........................................................................124 TABELA 8-9 - VALORES MÁXIMOS E MÍNIMOS EM PONTO FLUTUANTE ......................................................124 TABELA 8-10 - CONVERSÕES COM PERDA DE PRECISÃO............................................................................128 TABELA 9-1 – OPERADORES ARITMÉTICOS...............................................................................................130 TABELA 9-2 -OPERADORES RELACIONAIS .................................................................................................131 TABELA 9-3 - OPERADORES LÓGICOS .......................................................................................................131 TABELA 9-4 - TABELA VERDADE DOS OPERADORES LÓGICOS E RELACIONAIS ..........................................131 TABELA 9-5 - OPERADORES LÓGICO BIT A BIT ..........................................................................................132 TABELA 9-6 -ABREVIAÇÃO DE EXPRESSÕES .............................................................................................135 TABELA 9-7 - PRECEDÊNCIA DE OPERADORES..........................................................................................135 TABELA 9-8 - CONVERSÕES DE TIPOS DE DADOS.......................................................................................140 TABELA 9-9 - OPERADORES UNÁRIOS .......................................................................................................141 TABELA 9-10 – OPERADORES BINÁRIOS....................................................................................................141 TABELA 9-11 – OPERADORES POR ORDEM DE PRECEDÊNCIA ....................................................................142 TABELA 9-12 - EXEMPLO DE RELACIONAMENTOS AUTOMÁTICOS.............................................................143 TABELA 9-13 - OPERADORES ARITMÉTICOS UNÁRIOS ...............................................................................151 TABELA 9-14 - OPERADORES DE MULTIPLICAÇÃO, DIVISÃO E RESTO........................................................153 TABELA 9-15 - OPERADORES RELACIONAIS .............................................................................................157 TABELA 9-16 - OPERADORES LÓGICOS ENTRE BITS...................................................................................158 TABELA 9-17 - OPERADORES LÓGICOS .....................................................................................................159 TABELA 9-18 - OPERADORES DE ATRIBUIÇÃO...........................................................................................161 TABELA 9-19 - CONVERSÕES DE TIPOS INTEIROS COM SINAL ...................................................................164 TABELA 9-20 - CONVERSÕES DE TIPOS INTEIROS SEM SINAL ...................................................................165 TABELA 9-21 - CONVERSÃO DE TIPOS PONTO FLUTUANTE.......................................................................166 TABELA 14-1 – DIRETIVAS DE COMPILAÇÃO ............................................................................................240 TABELA 15-1 - TABELA DE CÓDIGOS DE FORMATOS .................................................................................248 TABELA 15-2 -EXEMPLOS DA FORMATOS DA FUNÇÃO PRINTF...................................................................248 TABELA 15-3 - EXEMPLOS DA FUNÇÃO PRINTF .........................................................................................248 TABELA 15-4 - ESPECIFICADORES DE FORMATO DE ENTRADA ..................................................................249 TABELA 15-5 -MODOS DE ABERTURA DE ARQUIVOS .................................................................................250 TABELA 17-1 – VETORES DE INTERRUPÇÕES ............................................................................................272 TABELA 17-2 – BITS DE CONFIGURAÇÃO DA IRQ0 A IRQ7 (PIC 1 – ENDEREÇO 0X21) ...........................273

Page 21: Tratado Da Linguagem c

TABELA 17-3 - BITS DE CONFIGURAÇÃO DA IRQ8 A IRQ15 (PIC 2 – ENDEREÇO 0XA1).........................273 TABELA 17-4 – ENDEREÇOS/REGISTRADORES PARA O PIC1.....................................................................280 TABELA 17-5 – ENDEREÇOS/REGISTRADORES PARA O PIC2.....................................................................280 TABELA 17-6 – ICW1 ...............................................................................................................................281 TABELA 17-7 – ICW2 ...............................................................................................................................281 TABELA 17-8 – ICW3 PARA O PIC MESTRE .............................................................................................281 TABELA 17-9 – ICW3 PARA DISPOSITIVOS ESCRAVOS ..............................................................................281 TABELA 17-10 – ICW4 .............................................................................................................................282 TABELA 17-11 – OCW1 ...........................................................................................................................282 TABELA 17-12 – OCW2 ...........................................................................................................................283 TABELA 17-13 – OCW3 ...........................................................................................................................283 TABELA 18-1 – PINAGEM DO CONECTOR DE PORTA PARALELA, DO TIPO DB-25 ......................................289 TABELA 18-2 – ENDEREÇOS DAS PORTAS PARALELAS ..............................................................................290 TABELA 18-3 – ENDEREÇOS DE MEMÓRIA DAS PORTAS PARALELAS .........................................................291 TABELA 18-4 – REGISTRADOR DADOS DA PORTA PARALELA ....................................................................291 TABELA 18-5 – REGISTRADOR DE STATUS DA PORTA PARALELA ..............................................................292 TABELA 18-6 – REGISTRADOR DE STATUS DA PORTA PARALELA ..............................................................292 TABELA 18-7 - ECR – REGISTRADOR DE CONTROLE ESTENDIDO.............................................................301 TABELA 19-1 – PINAGEM DOS CONECTORES SERIAIS DB25 E DB9...........................................................304 TABELA 19-2 - FUNÇÕES DOS PINOS NUM CANAL SERIAL PADRÃO ..........................................................305 TABELA 19-3 – PINAGEM DA UART 16550A...........................................................................................309 TABELA 19-4 – ENDEREÇOS PADRONIZADOS DA PORTA SERIAL................................................................310 TABELA 19-5 – ENDEREÇOS DAS PORTAS COM NA ÁREA DE DADOS DA BIOS.........................................311 TABELA 19-6 – REGISTRADORES ASSOCIADOS AO CANAL DE COMUNICAÇÃO SERIAL...............................311 TABELA 19-7 - TAXAS DE TRANSMISSÃO MAIS COMUNS E OS VALORES DO DIVISOR .................................312 TABELA 19-8 – REGISTRADOR DE HABILITAÇÃO DE INTERRUPÇÕES .........................................................313 TABELA 19-9 – REGISTRADOR DE IDENTIFICAÇÃO DA INTERRUPÇÃO .......................................................314 TABELA 19-10 - REGISTRADOR DE CONTROLE DA FIFO ...........................................................................315 TABELA 19-11 – REGISTRADOR DE CONTROLE DA LINHA DE TRANSMISSÃO .............................................316 TABELA 19-12 – REGISTRADOR DE CONTROLE DO MODEM.......................................................................317 TABELA 19-13 – REGISTRADOR DE STATUS DA LINHA DE COMUNICAÇÃO ................................................318 TABELA 19-14 – REGISTRADOR DE STATUS DO MODEM............................................................................319 TABELA 19-15 – ARGUMENTO DAS FUNÇÕES BIOSCOM E _BIOS_SERIALCOM ...........................................328 TABELA 19-16 – VALORES DE RETORNO DAS FUNÇÕES BIOSCOM E _BIOS_SERIALCOM ............................328 TABELA 19-17 – SIGNIFICADO DO VALOR DE RETORNO DEPENDENDO DE CMD.........................................329 TABELA 19-18 – VALORES PARA CMD ......................................................................................................329 TABELA 19-19 – VALORES PARA ABYTE...................................................................................................329 TABELA 20-1 – COMPARAÇÃO ENTRE SISTEMAS BALANCEADOS E DESBALANCEADOS .............................336 TABELA 20-2 - ESPECIFICAÇÕES ELÉTRICAS TIA/EIA-232-F...................................................................340 TABELA 20-3 - ESPECIFICAÇÕES ELÉTRICA TIA/EIA-423-B ....................................................................341 TABELA 20-4 - PRINCIPAIS ESPECIFICAÇÕES ELÉTRICAS TIA/EIA-562....................................................342 TABELA 20-5 - - PRINCIPAIS ESPECIFICAÇÕES ELÉTRICAS TIA/EIA-694 .................................................342 TABELA 20-6 - ESPECIFICAÇÕES TIA/EIA-422-B.....................................................................................343 TABELA 20-7 – ESPECIFICAÇÕES ELÉTRICAS DO PADRÃO RS-422 ............................................................345 TABELA 20-8 - ESPECIFICAÇÕES ELÉTRICAS TIA/EIA-485-A ..................................................................346 TABELA 20-9 - CARACTERÍSTICAS ELÉTRICAS DO PADRÃO RS-485.........................................................348 TABELA 20-10 - ESPECIFICAÇÕES ELÉTRICAS TIA/EIA-612 ....................................................................349 TABELA 20-11 - ESPECIFICAÇÕES ELÉTRICAS TIA/EIA-644 LVDS .........................................................349 TABELA 20-12 - REFERENCIA CRUZADA ENTRE A SERIE V E X .................................................................351 TABELA 20-13 - CARACTERÍSTICAS ELÉTRICAS IEEE488.........................................................................353 TABELA 20-14 – COMPARAÇÃO ENTRE AS TÉCNICAS DE PROTEÇÃO.........................................................369 TABELA 21-1 - BIBLIOTECA DE FUNÇÕES .................................................................................................380 TABELA 21-2 – REGISTRADOR OPTION_REG.........................................................................................387 TABELA 21-3 – REGISTRADOR INTCON ..................................................................................................390 TABELA 21-4 – REGISTRADORES ASSOCIADOS AO TIMER 1 ......................................................................392 TABELA 21-5 – REGISTRADOR T1CON ....................................................................................................393 TABELA 21-6 – REGISTRADOR TXSTA ....................................................................................................395 TABELA 21-7 – REGISTRADOR RCSTA ....................................................................................................396 TABELA 21-8 – FORMULA PARA O BAUDRATE..........................................................................................396 TABELA 21-9 – REGISTRADORES ASSOCIADOS COM O BAUDRATE GENERATOR .......................................397

Page 22: Tratado Da Linguagem c

TABELA 21-10 – REGISTRADORES ASSOCIADOS COM A TRANSMISSÃO ASSÍNCRONA ................................398 TABELA 21-11 – REGISTRADORES ASSOCIADOS COM O TIMER 2 ..............................................................402 TABELA 21-12 – REGISTRADOR T2CON ..................................................................................................402 TABELA 21-13 – MODO CCP – RECURSOS DE TEMPORIZAÇÃO................................................................403 TABELA 21-14 – NOMENCLATURA CCP ESPECIFICA E GENÉRICA.............................................................403 TABELA 21-15 – REGISTRADOR CCPXCON .............................................................................................404 TABELA 21-16 – REGISTRADOR SSPSTAT...............................................................................................410 TABELA 21-17 – REGISTRADOR SSPCON ................................................................................................411 TABELA 21-18 – REGISTRADORES ASSOCIADOS AO SPI............................................................................414 TABELA 21-19 – REGISTRADOR ADCON0 ...............................................................................................418 TABELA 21-20 – REGISTRADOR ADCON1 ...............................................................................................418 TABELA 23-1 – VALORES PARA O PARÂMETRO BAUDRATE......................................................................457 TABELA 23-2 – VALORES PARA O PARÂMETRO FDTRCONTROL................................................................457 TABELA 23-3 – VALORES PARA O PARÂMETRO FRTSCONTROL.................................................................458 TABELA 23-4 – VALORES PARA O PARÂMETRO PARITY ............................................................................459 TABELA 23-5 – VALORES PARA O PARÂMETRO STOPBITS ........................................................................459 TABELA 23-6 – COMPORTAMENTO DAS OPERAÇÕES DE LEITURA BASEADAS NOS VALORES ESPECIFICADOS

PARA OS TIMEOUTS TOTAIS E DE INTERVALO.....................................................................................467 TABELA 23-7 – EVENTOS A SEREM MONITORADOS ...................................................................................468 TABELA 23-8 – GETCOMMMODEMSTATUS ..............................................................................................469 TABELA 23-9 – FUNÇÕES DE COMUNICAÇÃO ...........................................................................................469 TABELA 23-10 – ESTRUTURAS DE COMUNICAÇÃO ...................................................................................470 TABELA 24-1 – PS/2 ATX ........................................................................................................................479 TABELA 24-2 – TECLADO AT ...................................................................................................................480 TABELA 24-3 – JOYSTICK .........................................................................................................................480 TABELA 24-4 – CONECTOR SERIAL DB9...................................................................................................483 TABELA 24-5 – CONECTOR SERIAL DB9...................................................................................................483 TABELA 24-6 – CONVERSOR DB9 PARA DB25.........................................................................................484 TABELA 24-7 – CONECTOR DB25 DA PORTA PARALELA...........................................................................485 TABELA 24-8 – CONECTOR DB25 DA PORTA PARALELA ECP...................................................................486 TABELA 24-9 – CONECTOR USB...............................................................................................................486 TABELA 24-10 – PINAGEM DO CONECTOR RGBI......................................................................................487 TABELA 24-11 – PINAGEM DO CONECTOR RGB ANALÓGICO...................................................................488 TABELA 24-12 – CONECTOR AUI .............................................................................................................488 TABELA 24-13 –CONECTOR BNC .............................................................................................................488 TABELA 24-14 – PINAGEM DO CONECTOR TP...........................................................................................489 TABELA 24-15 – CABOS USADOS NA REDE................................................................................................489 TABELA 24-16 – CONEXÕES USADAS NOS CABOS .....................................................................................490 TABELA 24-17 – PINAGEM DO CONECTOR ISA .........................................................................................492 TABELA 24-18 – PINAGEM DO CONECTOR EISA.......................................................................................494 TABELA 24-19 – PINAGEM DO CONECTOR IDE .........................................................................................495 TABELA 24-20 – PINAGEM DO CONECTOR PCI .........................................................................................499 TABELA 24-21 – PINAGEM DO CONECTOR PCMCIA.................................................................................501 TABELA 24-22 – PINAGEM DO CONECTOR DE ALIMENTAÇÃO ATX...........................................................501 TABELA 24-23 – PINAGEM DO CONECTOR DE ALIMENTAÇÃO XT/AT.......................................................501 TABELA 24-24 – PINAGEM DO CONECTOR DE ALIMENTAÇÃO 5 ¼.............................................................502 TABELA 24-25 – PINAGEM DO CONECTOR DE ALIMENTAÇÃO 3 ½.............................................................502

Page 23: Tratado Da Linguagem c

1111

11.. IINNTTRROODDUUCCÃÃOO

11..11.. PPoorr qquuee eessttuuddaarr aa lliinngguuaaggeemm CC ??

Basicamente todas as linguagens de programação conseguem os mesmos efeitos, algumas de forma mais eficiente que outras, sempre dependendo do tipo de aplicação para o qual será destinada.

A linguagem C de programação tem se tornado muito popular, devido à sua versatilidade e ao seu poder. Uma das grandes vantagens do C é a sua característica de "alto nível" e de "baixo nível" ao mesmo tempo, permitindo o controle total da máquina (hardware e software) por parte do programador, permitindo efetuar ações sem depender do sistema operacional utilizado.

Em algumas aplicações de engenharia, é necessário manter o controle total do hardware através do software, para efetuar acionamentos e temporizações precisas em tempo real, basicamente sistemas determinísticos.

A linguagem C foi projetada para a construção de sistemas operacionais, com o conseqüente controle do hardware. Em aplicações de engenharia, a linguagem C é utilizada freqüentemente para implementar: 1. Programas executivos e aplicativos em CLPs. 2. Firmware e software aplicativo em coletores de dados. 3. Controle eletrônico em automóveis. 4. Instrumentos inteligentes 5. Gateways de comunicação. 6. Modems. 7. Programadores de FPGAs (alternativa para a linguagem VHDL). 8. Periféricos em geral. 9. Software básico. 10. Interfaces Homem-Máquina 11. Sistemas operacionais. 12. Drivers de comunicação e de dispositivos. 13. Firmware de telefones celulares. 14. Programas do tipo Vírus e antivírus. 15. Firmware em satélites artificiais e veículos espaciais. 16. Processamento digital de sinais 17. Processamento de Imagens 18. Programas de Inteligência Artificial e redes neurais. 19. Modelagem numérica de sistemas físicos para simulação de efeitos dinâmicos em

eletromagnetismo, fenômenos de transporte e termodinâmica.

A linguagem C é a indicada em sistemas que envolvem software e hardware, e onde se deseja ter o controle total da máquina digital.

Capítulo

1

Page 24: Tratado Da Linguagem c

I N T R O D U Ç Ã O

2222

1111 Alguns engenheiros preferem utilizar a linguagem assembly ainda hoje por

conhecimento limitado da linguagem C ou pela falta de espaço de memória disponível, devido à manutenção de projetos de hardware antigos ou mal elaborados.

O assembly é a melhor linguagem de programação ?. A resposta é que a eficiência do assembly para sistemas grandes e complexos é muito pobre, além do código não poder ser reutilizado para repetir a aplicação em outros microprocessadores que não o de origem e de ser de difícil depuração.

Ante esta resposta, freqüentemente os engenheiros respondem que o código gerado pelo assembly é mais rápido e utiliza menos recursos da máquina, o que otimizaria o seu desempenho. A isto pode ser complementado que a maior rapidez na execução e o menor uso de recursos para efetuar uma tarefa vá depender do programador. A probabilidade de que os programadores, de uma empresa que produz compiladores, consigam obter o código mais eficiente em C do que o nosso próprio, em assembly, é muito maior, já que eles, sem lugar a dúvidas, gastaram muitas horas e dias procurando gerar o código mais eficiente possível. Isto é comparado com a escolha do tipo de câmbio quando a compra de um veículo: Câmbio manual ou automático ? Alguns preferem o manual, dando como desculpa de que é possível a mudança mais rápida das marchas. Mas tem muita gente que não tem a habilidade motora suficiente para efetuar esta tarefa de modo eficiente o tempo inteiro. Obviamente um piloto profissional efetuará as marchas de forma muito mais rápida do que uma pessoa comum.

11..22.. QQuuaannddoo eessttaa lliinngguuaaggeemm ffiiccaarráá oobbssoolleettaa ??

Como qualquer ferramenta tecnológica, a linguagem C deverá ficar obsoleta algum dia, mas pode-se antecipar de que isto não ocorrerá até pelo menos o fim da segunda década dos 2000, devido à quantidade enorme de linhas de código produzidas e que normalmente são re-aproveitadas.

Por ser uma linguagem extremamente simples, fácil de aprender, clara e objetiva, aplicável à maioria dos problemas de engenharia, esta linguagem provavelmente sobreviverá por mais 30 anos.

11..33.. BBrreevvee hhiissttóórriiaa ddaa LLiinngguuaaggeemm CC

A linguagem C foi inventada na década de 70. Seu inventor, Dennis Ritchie, implementou-o pela primeira vez usando um DEC PDP-11 rodando o sistema operacional UNIX. A linguagem C é derivada de uma outra: a B, criado por Ken Thompson. O histórico a seguir mostra a evolução das linguagens de programação que certamente influenciaram a linguagem C.

• Algol 60 – Projetado por um comitê internacional. • CPL – Combined Programming Language. Desenvolvida em Cambridge e na Universidade de

Londres em 1963. • BCPL – Basic Combined Programming Language. Desenvolvida em Cambridge por Martin

Richards em 1967. • B – Desenvolvida por Ken Thompson, nos Laboratórios Bell em 1970, a partir da

linguagem BCPL. • C – Desenvolvida por Dennis Ritchie, nos Laboratórios Bell em 1972. Aparece também a

figura de Brian Kerningham como colaborador.

Page 25: Tratado Da Linguagem c

I N T R O D U Ç Ã O

3333

1111 • ANSI C – O comitê ANSI (American National Standards Institute) foi reunido com a

finalidade de padronizar a linguagem C em 1983. • C++ - A linguagem C se torna ponto de concordância entre teóricos do desenvolvimento

da teoria de Object Oriented Programming (programação orientada a objetos): surge a linguagem C++ com alternativa para a implementação de grandes sistemas. Esta linguagem consegue interpretar linhas de código escritas em C.

A linguagem Algol apareceu alguns anos depois da linguagem Fortran. Esta era bem sofisticada e sem lugar a dúvidas, influenciou muito o projeto das linguagens de programação que surgiram depois. Seus criadores deram especial atenção à regularidade da sintaxe, estrutura modular e outras características associadas com linguagens estruturadas de alto nível.

Os criadores do CPL pretendiam fazer baixar, até a realidade de um computador real, os elevados intentos do Algol. Isto tornou a linguagem de difícil aprendizagem e implementação. Desta surge o BCPL como um aperfeiçoamento da CPL.

No início da linguagem B, o seu criador Ken Thompson projetando a linguagem para o sistema UNIX, tenta simplificar a linguagem BCPL. A linguagem B não ficou bem coesiva boa somente para o controle do hardware.

Logo após de ter surgido a linguagem B, surge uma nova máquina, o PDP-11. O sistema operacional Unix e o compilador B foram adaptados para esta máquina. A linguagem B começa a ser questionada devido à sua relativa lentidão, por causa do seu desenho interpretativo. Além disto a linguagem B era orientada a palavra enquanto o PDP-11 era orientado a byte. Por estas razões começou-se a trabalhar numa linguagem sucessora da B.

A criação da linguagem C é atribuída a Dennis Ritchie, que restaurou algumas das generalidades perdidas pela BCPL e B. Isto foi conseguido através do hábil uso dos tipos de dados, enquanto mantinha a simplicidade e o contato com o computador.

Page 26: Tratado Da Linguagem c

4444

22.. OOSS CCOOMMPPUUTTAADDOORREESS

22..11.. OO qquuee ssããoo ooss ccoommppuuttaaddoorreess ??

Não há como controlar os computadores sem conhecer o que são e como funcionam. Os computadores são basicamente máquinas que executam tarefas, tais como cálculos matemáticos e comunicações eletrônicas de informação, sob o controle de um grupo de instruções inserido de antemão, denominado programa. Os programas usualmente residem dentro do computador e são lidos e processados pela eletrônica do sistema que compõe o computador. Os resultados do processamento do programa são enviados a dispositivos eletrônicos de saída, tais como um monitor de vídeo, uma impressora ou um modem. Estas máquinas são utilizadas para efetuar uma ampla variedade de atividades com confiabilidade, exatidão e velocidade.

22..22.. CCoommoo ooss ccoommppuuttaaddoorreess ffuunncciioonnaamm ??

A parte física do computador é conhecida como hardware. O hardware do computador inclui a memória, que armazena tanto os dados quanto as instruções, a unidade central de processamento (CPU) que executa as instruções armazenadas na memória, o barramento que conecta os vários componentes do computador, os dispositivos de entrada, tais como o mouse ou o teclado, que permitem ao usuário poder comunicar-se com o computador, e os dispositivos de saída, tais como impressoras e monitores de vídeo, que possibilitam a visualização das informações processadas pelo computador. O programa que é executado pelo computador é chamado de software. O software é geralmente projetado para executar alguma tarefa particular, por exemplo, controlar o braço de um robô para a soldagem de um chassis de automóvel ou desenhar um gráfico.

22..33.. TTiippooss ddee CCoommppuuttaaddoorreess

Os computadores podem ser digitais ou analógicos. A palavra digital refere-se aos processos que manipulam números discretos (por exemplo, sistemas binários: 0s e 1s), que podem ser representados por interruptores elétricos que abrem ou fecham (implementados por transistores trabalhando na região de saturação e de corte respectivamente). O termo analógico refere-se a valores numéricos que tem faixa de variação contínua. 0 e 1 são números analógicos, assim como 1.5 ou o valor da constante π. Como exemplo, considere uma lâmpada incandescente que produz luz em um momento e não a produz em outro quando manipulado um interruptor (iluminação digital). Se o interruptor for substituído por um dimmer, então a iluminação ficará analógica uma vez que a intensidade de luz pode variar continuamente entre os estados de ligada e desligada.

Capítulo

2

Page 27: Tratado Da Linguagem c

O S C O M P U T A D O R E S

5555

2222 Os primeiros computadores eram analógicos mas devido à sensibilidade a

perturbações externas e pelas necessidades de serem sistemas confiáveis, foram substituídos por computadores digitais que trabalham com informação codificada de forma discreta. Estes são mais imunes a interferências externas e internas.

A natureza dos sinais utilizados na codificação da informação pode ser na forma de campos elétricos gerados por cargas (circuitos que utilizam transistores como chaves), campos eletromagnéticos (computadores que utilizam fótons), fenômenos eletroquímicos (computadores orgânicos), forças hidráulicas, pneumáticas e outros.

22..44.. SSooffttwwaarree BBáássiiccoo ee oo SSiisstteemmaa OOppeerraacciioonnaall

Quando um computador é ligado, a primeira coisa que ele faz é a procura de instruções armazenadas na sua memória. Usualmente o primeiro grupo de instruções é um programa especial que permite o inicio da operação. Estas instruções mandam o computador executar um outro programa especial chamado sistema operacional, que é o software que facilita a utilização da máquina por parte do usuário. Ele faz o computador esperar por instruções do usuário (ou de outras máquinas) por comandos de entrada, relata os resultados destes comandos e outras operações, armazenamento e gerenciamento de dados e controla a seqüência das ações do software e do hardware. Quando o usuário requisita a execução de um programa, o sistema operacional o carrega na memória de programa do computador e o instrui para executar o mesmo.

MundoExterior

Microprocessador

CPUControle eAritmética

Memória dePrograma

Memória deDados

Entradas

Dispositiv os deSaída

MundoExterior

Barramentode

Endereços

Barramentode

Dados

Figura 2. Componentes básicos de um sistema computador

Page 28: Tratado Da Linguagem c

O S C O M P U T A D O R E S

6666

2222 22..44..11.. AA MMeemmóórriiaa Para processar a informação eletronicamente, os dados são armazenados no

computador na forma de dígitos binários, ou bits, cada um tendo duas possíveis representações (0 ou 1 lógicos). Se adicionarmos um segundo bit a unidade única de informação, o número de representações possíveis é dobrado, resultando em quatro possíveis combinações: 00, 01, 10, 11. Um terceiro bit adicionado a esta representação de dois bits duplica novamente o número de combinações, resultando em oito possibilidades: 000, 001, 010, 011, 100, 101, 110, ou 111. Cada vez que um bit é adicionado, o número possível de combinações é duplicado.

Um conjunto de 8 bits é chamado de byte. Cada byte possui 256 possíveis combinações de 0 e 1’s. O byte é uma quantidade freqüentemente utilizada como unidade de informação porque possibilita a representação do alfabeto ocidental completo, incluindo os símbolos das letras em maiúsculas e minúsculas, dígitos numéricos, sinais de pontuação e alguns símbolos gráficos. Como alternativa, o byte poderá representar simplesmente uma quantidade numérica positiva entre 0 e 255.

O computador divide o total da memória disponível em dois grupos lógicos, a memória de programa e a memória de dados.

A memória física do computador pode ser do tipo RAM (Random Access Memory) que pode ser tanto lida quanto escrita, ROM (Read Only Memory) que somente pode ser lida. Em geral as memórias RAM são voláteis, isto é, são apagadas quando o sistema é desenergizado. Outros tipos de memórias são: PROMs (OTPs), EPROMs, EEPROMs, Flash-EEPROMs, NVRAM que são não voláteis, isto é, os dados permanecem inalterados mesmo depois de desligar o sistema.

A Figura 2-1 mostra o processo de gravação da letra ‘A’ (com código ASCII igual a 41H em hexadecimal, 01000001 em binário ou 65 em decimal) na posição de memória número 0. Este chip de memória hipotético, possui 16 posições de memória de 1 byte cada, endereçáveis por 4 bits (24 = 16 posições possíveis). No processo de escrita, a CPU coloca o dado a ser gravado no barramento de endereços, logo coloca o endereço onde os dados serão armazenados, no barramento de endereços, e finalmente indica ao chip que a operação é de escrita ativando o sinal WR (WRITE).

Page 29: Tratado Da Linguagem c

O S C O M P U T A D O R E S

7777

2222 D0 ADD0

D4

D7

D6

D5

D3

D2

D1

WR

RD

ADD3

ADD2

ADD1

0100 0001

1

0

0

0

0

0

0

1

0

0

0

0

0

1

Figura 2-1 – Processo de gravação

No processo de leitura a CPU coloca o endereço da posição de memória que deseja ser lida, no barramento de endereços e indica que o processo é de leitura, acionando o pino RD (READ). A memória posteriormente coloca o dado armazenado naquela memória no barramento de dados. Após um certo intervalo de tempo, a CPU faz a leitura desses dados.

22..44..22.. OOss BBaarrrraammeennttooss O barramento (bus) é usualmente um conjunto paralelo de fios que interconecta os

vários dispositivos componentes do hardware do sistema, tais como a CPU e a memória, habilitando e gerenciando a comunicação de dados entre estes.

Usualmente existem três tipos de barramentos, o barramento de controle que controla o funcionamento dos componentes do sistema, o barramento de endereços, onde é colocada a informação de origem ou destino para a informação a ser enviada ou recuperada, e o barramento de dados, onde trafegam os dados.

O número de linhas componentes do barramento limita a capacidade de endereçamento de memória para o computador. Por exemplo, o 8051 possui 16 linhas de endereços no seu barramento externo, e portanto ele poderá endereçar no máximo 216 = 65536 posições de memória. Já um barramento de 32 bits conseguirá endereçar 232 = 4294967296 posições no máximo.

22..55.. AA UUnniiddaaddee CCeennttrraall ddee PPrroocceessssaammeennttoo -- CCPPUU

A informação originária de um dispositivo de entrada ou da memória é transferida através do barramento para a CPU, sendo esta a unidade que interpreta os comandos e executa os programas. A CPU é um circuito microprocessador, constituído de uma peça única feita em silício e óxidos contendo milhões de componentes eletrônicos numa área muito pequena. A informação é armazenada em posições de memórias especiais colocadas

Page 30: Tratado Da Linguagem c

O S C O M P U T A D O R E S

8888

2222 dentro do microprocessador chamadas de Registradores. Estes são pequenas memórias de armazenamento temporário para instruções ou dados.

Enquanto um programa estiver sendo executado, existe um registrador especial que armazena o próximo lugar da memória de programa que corresponde à próxima instrução a ser executada; freqüentemente chamado de Contador de Programa1. A unidade de controle situada no microprocessador coordena e temporiza as funções da CPU e recupera a próxima instrução da memória a ser executada.

Numa seqüência típica de operação, a CPU localiza a instrução seguinte no dispositivo apropriado da memória. A instrução é transferida por meio do barramento para um registrador especial de instruções dentro da CPU. Depois disto, o contador de programa é incrementado para se preparar para a próxima instrução. A instrução corrente é analisada pelo decodificador, que determina o que esta determina que deve ser feito. Os dados necessários pela instrução serão recuperados através do barramento e colocados em outros registradores da CPU. A CPU então executa a instrução e o resultado é armazenado também em outros registradores especiais ou copiado para lugares específicos da memória.

Uma característica importante das CPUs é o tamanho dos dados (em número de bits) que esta pode manipular com uma única instrução. Assim temos atualmente CPUs de 8 bits (ex. PIC16C877, 8051), de 16 bits (ex. 8088, 80286, 80196), 32 bits (80386, 80486, 80586, Pentium) e algumas menos comuns de 64 bits. O tamanho indica por exemplo, que um processador 80486 consegue somar dois números representados com 32 bits utilizando uma instrução de código de máquina, sendo que a mesma operação deverá ser repartida em várias instruções para ser efetuada em uma CPU com um número de bits de menor.

A Figura 2-2 mostra um processador 8051FX da Intel com os blocos componentes da pastilha de circuito integrado.

1 Do inglês Program Counter ou simplesmente PC.

Page 31: Tratado Da Linguagem c

O S C O M P U T A D O R E S

9999

2222

Figura 2-2 – Processador 8051 FX Intel

22..66.. PPrrooggrraammaass ddee CCoommppuuttaaddoorr

Um programa de computador é basicamente uma lista de instruções que este deverá executar. Uma vez que as máquinas digitais só conseguem interpretar informações na forma de sinais elétricos, e que os humanos interpretam na forma de imagens ou sons, é necessária uma ferramenta que implemente uma interface simplificada para a programação. Estas ferramentas são chamadas de linguagens de programação. As linguagens de programação contêm uma série de comandos que formam o software. Em geral, a linguagem que é diretamente codificada em números binários interpretáveis pelo hardware do computador é mais rapidamente entendida e executada. As linguagens que usam palavras e outros comandos que refletem o pensamento lógico humano são mais fáceis de utilizar mas são mais lentas, já que esta deve ser traduzida antes, para que o computador possa interpretá-la.

O software de um computador consiste em programas ou lista de instruções que controla a operação da máquina. O termo pode ser referido a todos os programas utilizados com um computador específico ou simplesmente a um único programa.

O software é a informação abstrata armazenada como sinais elétricos na memória do computador, em contraste como os componentes de hardware tais como a unidade

Page 32: Tratado Da Linguagem c

O S C O M P U T A D O R E S

10101010

2222 central de processamento e os dispositivos de entrada e saída. Estes sinais são decodificados pelo hardware e interpretados como instruções, sendo que cada tipo de instrução guia ou controla o hardware por um breve intervalo de tempo.

Uma grande variedade de softwares são utilizados num computador. Uma forma fácil de entender esta variedade é pensar em níveis. O nível mais baixo é o que está mais perto do hardware da máquina. O mais alto está mais perto do operador humano. Os humanos raramente interagem com o computador no nível mais baixo, mas o fazem utilizando tradutores chamados Compiladores. Um compilador é um programa de software cujo propósito é converter programas escritos em uma linguagem de alto nível, numa de baixo nível que possa ser interpretado pelo hardware. Os compiladores eliminam o processo entediante de conversar com um computador na sua própria linguagem binária.

Uma camada acima do nível mais baixo, poderá existir um software chamado de Sistema Operacional, que controla o sistema em si. Ele organiza as funções de hardware tais como a leitura e escrita em dispositivos de entrada e saída (teclado, monitor de vídeo, discos magnéticos, etc.), interpretando comandos do usuário e administrando o tempo e os recursos para os programas de aplicação

O software de aplicação adapta o computador a um propósito especial tal como o processamento e monitoração de variáveis de controle de uma fábrica ou para efetuar a modelagem de uma parte de uma máquina. As aplicações são escritas em qualquer uma das várias linguagens compiladas que forem mais apropriadas, para a aplicação específica. Estas linguagens, inventadas pelos seres humanos, não podem ser diretamente entendidas pelo hardware do computador. Ela deve ser traduzida por um compilador apropriado que a converta em códigos de zeros e uns que a máquina possa processar.

O software é usualmente distribuído em discos magnéticos ou ópticos. Quando o disco é lido pela unidade de leitura apropriada, o software será copiado na memória. Então o sistema operacional do computador passa o controle para a aplicação no processo que ativa o programa. Quando o programa é terminado, o sistema operacional reassume o controle da máquina e espera alguma requisição por parte do usuário.

22..77.. LLiinngguuaaggeennss ddee PPrrooggrraammaaççããoo

Como foi visto anteriormente, um programa de computador é um conjunto instruções que representam um algoritmo para a resolução de algum problema. Estas instruções são escritas através de um conjunto de códigos (símbolos e palavras). Este conjunto de códigos possui regras de estruturação lógica e sintática própria. Dizemos que este conjunto de símbolos e regras forma uma linguagem de programação.

22..77..11.. AA LLiinngguuaaggeemm ddee MMááqquuiinnaa Os programas de computador que podem ser executados por um sistema

operacional são freqüentemente chamados de executáveis. Um programa executável é composto de uma seqüência de um grande número de instruções extremamente simples conhecida como código de máquina. Estas instruções são específicas para cada tipo especial de CPU e ao hardware à qual se dedicam (por exemplo, microprocessadores Pentium®, 80486, 8051, PIC16F877, Z80, etc.) e que têm diferentes linguagens de máquina e requerem diferentes grupos de códigos para executar a mesma tarefa. O número de instruções de código de máquina normalmente é pequeno (de 20 a 200 dependendo do

Page 33: Tratado Da Linguagem c

O S C O M P U T A D O R E S

11111111

2222 computador e da CPU). Instruções típicas são para copiar dados de um lugar da memória e adicionar o conteúdo de dois locais de memória (usualmente registradores internos da CPU). As instruções de código de máquina são informações binárias não compreensíveis facilmente por humanos, e por causa disto as instruções não são usualmente escritas diretamente em código de máquina.

22..77..22.. AA LLiinngguuaaggeemm AAsssseemmbbllyy A linguagem Assembly utiliza comandos que são mais fáceis de entender pelos

programadores que a linguagem de máquina. Cada instrução da linguagem de máquina tem um comando equivalente na linguagem assembly. Por exemplo, na linguagem assembly, o comando “MOV A,B” instrui ao computador a copiar dados de um lugar para outro. A mesma instrução em linguagem de máquina poderá ser uma cadeia de 8 bits binários ou mais dependendo do tipo de CPU (por exemplo 0011 1101). Uma vez que o programa em assembly é escrito, deve ser convertido em um programa em linguagem de máquina através de um outro programa chamado de Assembler. A linguagem assembly é a mais rápida e poderosa devido a sua correspondência com a linguagem de máquina. É uma linguagem muito difícil de utilizar. Às vezes, instruções em linguagem assembly são inseridas no meio de instruções de alto nível para executar tarefas específicas de hardware ou para acelerar a execução de algumas tarefas.

22..77..33.. LLiinngguuaaggeennss ddee AAllttoo--NNíívveell As linguagens de Alto-Nível foram desenvolvidas devido às dificuldades de

programação utilizando linguagens assembly. As linguagens de alto nível são mais fáceis de utilizar que as linguagens de máquina e a assembly, devido a que seus comandos lembram a linguagem natural humana. Ainda que estas linguagens independem da CPU a ser utilizada, contendo comandos gerais que trabalham em diferentes CPUs da mesma forma. Por exemplo, um programador escrevendo na linguagem C para mostrar uma saudação num dispositivo de saída (por exemplo um monitor de vídeo) somente teria que colocar o seguinte comando: printf(“Bom dia, engenheiro !”);

Este comando direcionará a saudação para o dispositivo de saída, e funcionará sem importar que tipo de CPU o computador utiliza. De forma análoga à linguagem assembly, as linguagens de alto nível devem ser traduzidas, e para isto é utilizado um software chamado Compilador. Um compilador transforma um programa escrito numa linguagem de alto nível num programa em código de máquina específico. Por exemplo, um programador pode escrever um programa em uma linguagem de alto nível tal como C e então prepará-lo para ser executado em diferentes máquinas, tais como um supercomputador Cray Y-MP ou simplesmente um PC, usando compiladores projetados para cada uma destas máquinas. Esta característica acelera a tarefa de programação e faz o software mais portável para diferentes usuários e máquinas.

A oficial naval e matemática americana, Grace Murray Hopper, ajudou a desenvolver a primeira linguagem de software de alto nível comercialmente disponível, a FLOW-MATIC, em 1957. É creditada a ela a invenção do termo bug, para indicar que o computador apresenta um defeito no funcionamento. Em 1945 ela descobriu uma falha do hardware num computador Mark II ocasionada por um inseto que ficou preso entre os relés eletromecânicos componentes do sistema lógico.

Page 34: Tratado Da Linguagem c

O S C O M P U T A D O R E S

12121212

2222 Na década de 1950 (1954 a 1958), o cientista de computação Jim Backus da

International Business Machines, Inc. (IBM) desenvolveu a linguagem FORTRAN (FORmula TRANslation). Esta permanece até hoje, especialmente no mundo científico, como uma linguagem padrão de programação já que facilitava o processamento de fórmulas matemáticas.

Em 1964 foi criada a linguagem BASIC (Beginner’s All-purpose Symbolic Instruction Code), desenvolvida por dois matemáticos: o americano John Kemeny e o húngaro Thomas Kurtz, no Dartmouth College. A linguagem era muito fácil de aprender comparada com as predecessoras e ficou popular devido a sua simplicidade, natureza interativa e a sua inclusão nos computadores pessoais. Diferente de outras linguagens que requerem que todas as suas instruções sejam traduzidas para linguagens de máquina antes de serem executadas, estas são interpretadas, isto é, são convertidas em linguagem de máquina, linha a linha, enquanto o programa está sendo executado. Os comandos em BASIC tipificam a linguagem de alto nível devido a sua simplicidade e semelhança com a linguagem natural humana. Um exemplo de programa que divide um número por dois, pode ser escrito como: 10 INPUT “ENTRE COM O NÚMERO,” X 20 Y=X/2 30 PRINT “A metade do número é ,” Y

Outras linguagens de alto nível em uso hoje em dia incluem C, C++, Ada, Pascal, LISP, Prolog, COBOL, HTML, e Java, entre outras.

22..77..44.. LLiinngguuaaggeennss OOrriieennttaaddaass aa OObbjjeettooss As linguagens de programação orientada a objetos (OOP – Object Oriented

Programming) tais como o C++ são baseadas nas linguagens tradicionais de alto nível, mas elas habilitam ao programador a pensar em termos de coleções de objetos cooperativos no lugar de uma lista de comandos. Os objetos, tais como um círculo, tem propriedades tais como o raio do círculo e o comando que o desenha na tela. Classes de objetos podem ter características inerentes de uma outra classe de objetos. Por exemplo, uma classe que define quadrados pode herdar características tais como ângulos retos de uma classe que define os retângulos. Estes grupos de classes de programação simplificam a tarefa de programação, resultando em programas mais eficientes e confiáveis.

22..77..55.. AAllggoorriittmmooss Um algoritmo é o procedimento para resolver um problema complexo, utilizando

uma seqüência precisa e bem determinada de passos simples e não ambíguos. Tais procedimentos eram utilizados originalmente em cálculos matemáticos e hoje são usados em programas de computador e em projetos de hardware. Usualmente são auxiliados por fluxogramas que são utilizados para facilitar o entendimento da seqüência dos passos. Os fluxogramas representam a seqüência de passos implementada pelo algoritmo, de forma gráfica. A Figura 2-3 mostra o exemplo de um fluxograma.

Page 35: Tratado Da Linguagem c

O S C O M P U T A D O R E S

13131313

2222 Início

Configuração deperiféricos

Modo de descargade dados?

Modo de apaga?

Descarga dados

Apaga dados

Sleep

Não

Não

Sim

Sim

Modo de descargade status?

Não

Descarga statusSim

Figura 2-3 - Exemplo de Fluxograma

22..77..66.. EExxeemmppllooss ddee CCooddiiffiiccaaççããoo ddee IInnssttrruuççõõeess Existem muitas linguagens de programação. Pode-se escrever um algoritmo para

resolução de um problema por intermédio de qualquer linguagem. A seguir são mostrados alguns exemplos de trechos de códigos escritos utilizando algumas linguagens de programação.

Exemplo: trecho de um algoritmo escrito numa pseudo-linguagem de alto nível que recebe um número (armazenado na variável num), calcula e mostra os valores múltiplos de 1 a 10 para o mesmo. ler num para n de 1 até 10 passo 1 fazer

tab ← num * n imprime tab

fim fazer

Exemplo: trecho do mesmo programa escrito em linguagem C: unsigned int num,n,tab; scanf("%d",&num); for(n = 1; n <= 10; n++) tab = num * n; printf("\n %d", tab);

Exemplo: trecho do mesmo programa escrito em linguagem Basic: 10 INPUT num 20 FOR n = 1 TO 10 STEP 1 30 LET tab = num * n 40 PRINT chr$ (tab) 50 NEXT n

Exemplo: trecho do mesmo programa escrito em linguagem Fortran: read (num); do 1 n = 1:10

Page 36: Tratado Da Linguagem c

O S C O M P U T A D O R E S

14141414

2222 tab = num * n write(tab) 10 continue

Exemplo: trecho do mesmo programa escrito em linguagem Assembly para INTEL 8088: MOV CX,0 ; coloca zero no registrador CX IN AX,PORTA ; coloca um valor do buffer de teclado no registrador AX MOV DX,AX ; copia o valor de AX para DX LABEL: INC CX ; incrementa em um o valor armazenado em CX MOV AX,DX ; copia o valor de DX para AX MUL CX ; multiplica o valor armazenado em CX pelo valor em AX OUT AX, PORTB ; o valor resultante é enviado ao buffer de saída de display CMP CX,10 ; o valor armazenado em CX é comparado com 10 JNE LABEL ; se a contagem ainda não chegou a 10, pula para LABEL e repete

22..88.. CCoonncclluussõõeess

Pode-se dividir as linguagens de programação em dois grandes grupos: as linguagens de baixo nível e as de alto nível:

Linguagens de baixo nível: São linguagens voltadas para a máquina, isto é, são escritas usando as instruções do microprocessador do computador. São genericamente chamadas de linguagens Assembly.

Vantagens: Programas são executados com maior velocidade de processamento. Os programas ocupam menos espaço na memória.

Desvantagens: Em geral, os programas em Assembly têm pouca portabilidade, isto é, um código gerado para um tipo de processador não serve para outro. Códigos Assembly não são estruturados, tornando a programação mais difícil.

Linguagens de alto nível: São linguagens parecidas com o modo de comunicação dos seres humano. Em geral utilizam sintaxe estruturada tornando seu código mais legível. Necessitam de compiladores ou interpretadores para gerar instruções do microprocessador. Interpretadores fazem a interpretação de cada instrução do programa fonte executando-a dentro de um ambiente de programação, Basic e AutoLISP por exemplo. Compiladores fazem a tradução de todas as instruções do programa fonte gerando um programa executável. Estes programas executáveis (*.exe) podem ser executados fora dos ambientes de programação, C e Pascal por exemplo. As linguagens de alto nível podem se distinguir quanto a sua aplicação em genéricas como C, Pascal e Basic ou específicas como Fortran (cálculos matemáticos), GPSS (simulação), LISP (inteligência artificial) ou o antigo e obsoleto, CLIPPER (banco de dados).

Vantagens: Por serem compiladas ou interpretadas, em geral, têm maior portabilidade podendo ser executadas em várias plataformas com poucas modificações. Em geral, a programação torna-se mais fácil por causa do maior ou menor grau de estruturação de suas linguagens.

Desvantagens: Em geral, as rotinas geradas (em linguagem de máquina) são mais genéricas e portanto mais complexas e por isso são mais lentas e ocupam mais espaço de memória.

Page 37: Tratado Da Linguagem c

15151515

33.. AA CCOODDIIFFIICCAAÇÇÃÃOO DDAA IINNFFOORRMMAAÇÇÃÃOO Durante séculos, o ser humano vem codificando e armazenando a informação

mediante símbolos gráficos (linguagem escrita) e regras de montagem (gramática). A informação pode ser definida como o conhecimento derivado do estudo ou a experiência, basicamente uma coleção de fatos ou dados. Provavelmente esta capacidade abstrata de armazenar a informação ao longo dos séculos, permitiu ao ser humano se impor sobre as outras espécies animais.

A informação em si é um conceito abstrato que para ser compartilhado com outras pessoas deve ser representado de forma coerente e simples. A linguagem escrita é um meio coerente, onde temos uma série de símbolos que associados de formas diferentes representam informações diferentes. A representação da informação abstrata numa série de símbolos (podem ser gráficos, sonoros, elétricos, luminosos) é conhecida como codificação. A informação codificada depende fortemente do contexto em que esta for transmitida, por exemplo, o código ELÉTRON pode significar uma referência a um componente da matéria, ao nome de um cão de estimação ou simplesmente a um conjunto de caracteres que implementa uma senha de acesso.

Existem infinitas possibilidades de codificar uma informação. A informação escrita é codificada utilizando letras do alfabeto (existem inúmeros alfabetos diferentes no planeta) e regras de construção (existem inúmeros idiomas com gramáticas totalmente diferentes). A informação repassada na forma de ondas de deslocamento de ar, possui suas regras (língua falada). No século XX começaram a ser utilizados outros tipos de codificação como os sinais de radiofreqüência (ondas de rádio), onde um mecanismo eletrônico codificava o sinal sonoro ou visual em ondas eletromagnéticas e no receptor estes sinais são decodificados, ou seja, convertidos novamente em sinais de imagem ou som. Nos computadores, a informação é codificada na forma de campos magnéticos (nos discos rígidos ou disquetes) ou de campos elétricos (nas memórias). Para o ser humano colocar as informações num computador precisa de ferramentas que executem esta tradução de sinais gráficos em sinais elétricos por exemplo. Para criar estas ferramentas ou utilizá-las com maior eficiência deve-se conhecer como os computadores codificam a informação.

Os computadores eletrônicos codificam a informação de forma discreta utilizando sistemas binários. Estes sistemas trabalham com a unidade mínima de informação, o bit. O conceito de quantidade de informação, está relacionado diretamente com a probabilidade de acontecer uma determinada informação. Por exemplo, se existirem duas informações possíveis, a probabilidade é de 50% para cada uma delas. Se existirem 3 informações possíveis, a probabilidade diminui para 33.3%. Se somente existir uma informação, aí a probabilidade de acontecer é 100%, ou seja não há informação a ser repassada ou transmitida. Disto deduz-se que para existir alguma informação deverão existir no mínimo duas informações possíveis, basicamente sistemas binários, onde o mínimo de informação é representado por uma variável que pode assumir um de dois

Capítulo

3

Page 38: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

16161616

3333

valores possíveis, sendo a natureza deste sinal, um campo elétrico, magnético, eletromagnético, diferença de pressão, temperatura, força, etc..

No caso de ter que codificar um conjunto maior de informações (mais que duas), procede-se a aumentar o número de unidades de informação (bits) que as compõem, analogamente como é feito com as letras do alfabeto.

Como exemplo pode-se imaginar a codificação da informação abstrata “UM” como quantidade. Isto pode ser codificado pela mente humana como símbolos: “UM”, “um”, ”Um”, “uM”, “1”, “1” , “01”, “1.0”, ”1.100”, “One”, “Un”, “Uno”, “Une”, “Eins”, etc. Nos computadores binários devido aos limitados recursos, quando comparados com os da mente humana, procura-se sempre a compactação da informação. A informação de quantidade pode ser armazenada na sua forma mais compacta na memória do computador como uma seqüência de estados de um conjunto específico de transistores que estariam, por exemplo, nos estados “0000 0001”, onde um estado representado por 0 identifica um transistor operando na região de saturação, e um 1 significa um transistor na região de corte. Este conjunto de bits forneceria a informação abstrata de quantidade.

Alguns exemplos de codificação binária: Quantidade abstrata 2 num computador de 8 bits: 0000 0010 Quantidade abstrata 2 num computador de 16 bits: 0000 0000 0000 0010 Símbolo gráfico ‘2’ (Codificação ASCII): 0011 0010 Quantidade abstrata 0 num computador de 8 bits: 0000 0000 Quantidade abstrata 0 num computador de 16 bits: 0000 0000 0000 0000 Símbolo gráfico ‘0’ (Codificação ASCII): 0011 0000

O número total de bits utilizado na representação da informação, limita o número máximo de informações possíveis. Assim, se for escolhido um conjunto de 8 bits para representar um conjunto de informações, o máximo possível será de 28 = 256 possíveis informações. A regra geral é que o número máximo de informações possíveis representáveis por n bits será de 2n. Por exemplo, se o nosso conjunto de informações fossem os símbolos numéricos do sistema decimal, do 0 ao 9 (10 informações ao todo), somente serão necessários 4 bits (24 = 16) e ainda sobrariam 6 possíveis informações.

33..11.. SSiisstteemmaass ddee NNuummeerraaççããoo

Os sistemas de numeração [4] são os vários sistemas de notação que são ou têm sido usados para representar quantidades abstratas chamadas de números. Um sistema de numeração é definido pela base que utiliza, ou seja o número de símbolos diferentes requeridos para que o sistema possa representar qualquer série infinita de números. Desta forma, o sistema decimal, utilizado por quase todos os habitantes do planeta (exceto para aplicações de computadores) requer de dez símbolos diferentes, também chamados de dígitos, para representar os números sendo um sistema de base 10.

Ao longo da história, muitos sistemas de numeração diferentes têm sido usados, de fato, qualquer número acima de 1 pode ser usado como base. Algumas culturas têm usado sistemas baseados nos números 3, 4 e 5. Os babilônios usavam o sistema sexagesimal, baseado no número 60, e os romanos usavam (para certos propósitos), o sistema duodecimal, baseado no número 12. Os Maias usava um sistema bigecimal, baseados no número 20. O sistema binário, baseado no número 2, foi utilizado por algumas tribos e,

Page 39: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

17171717

3333

junto com o sistema octal baseado no 8 e no hexadecimal, baseado no 16, são usados hoje em dia, em sistemas microprocessados.

Figura 3-1 – Antigos sistemas de numeração1

33..11..11.. VVaalloorreess ddee aaccoorrddoo ccoomm aa PPoossiiççããoo O sistema universalmente adotado para a notação matemática hoje em dia é o

sistema decimal (exceto para sistemas de computação). A posição do símbolo num sistema de base 10 denota o valor deste em termos de valores exponenciais da sua base (regra também válida em outros sistemas). Isto é, no sistema decimal a quantidade representada pela combinação dos dez símbolos utilizados (0, 1, 2, 3, 4, 5, 6, 7, 8, e 9) depende da posição no número. Assim, o número 3098323 é uma abreviação para:

(3 × 106) + (0 × 105) + (9 × 104) + (8 × 103) + (3 × 102) + (2 × 101) + (3 × 100).

O primeiro “3” (lendo da direita para a esquerda) representa três unidades; o segundo “3” representa trezentas unidades e o terceiro “3”, três milhões de unidades. Neste sistema o zero representa duas funções muito importantes, indica a não existência ou nada, e também serve para indicar os múltiplos de base 10, 100, 1000 e assim sucessivamente. Também é usado para indicar as frações de valores inteiros: 1/10 pode ser escrito como 0.1, 1/100 como 0.01 e assim sucessivamente.

Dois dígitos são suficientes para representar um número no sistema binário; 6 dígitos (0, 1, 2, 3, 4, 5) são necessários para representar um número no sistema sexagesimal; e 12 dígitos (0, 1, 2, ,3 , 4, 5, 6, 7, 8, 9, d (símbolo para o dez), z (símbolo para o onze)) são necessários para representar um número no sistema duodecimal. O número 30155 no sistema sexagesimal equivale a:

(3 × 64) + (0 × 63) + (1 × 62) + (5 × 61) + (5 × 60) = 3959 no sistema decimal.

O número 2zd no sistema duodecimal equivale a (2 × 122) + (11 × 121) + (10 × 120) = 430 no sistema decimal.

1 Adaptado de Microsoft ® Encarta Encyclopedia 97.

Page 40: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

18181818

3333

Para escrever um número n de base 10 como um número de base b, deve-se dividir (no sistema decimal) n por b desprezando os valores fracionários, depois dividir o quociente por b novamente, e assim sucessivamente até que o quociente seja obtido um quociente igual a zero. Os restos sucessivos da divisão são os dígitos da expressão de n no sistema de base b. Por exemplo para expressar o número 3959 decimal no seu equivalente de base 6 temos:

Multiplicador2 Divisor Resto

3959 6

659 5

109 5

18 1

3 0

0 3

Assim temos que 395910 = 301556. A base normalmente é escrita na forma de subscrito do número). Quanto maior for a base, maior o número de símbolos requeridos, mas menos dígitos serão necessários para expressar um dado número. O número 12 é conveniente como base devido que e exatamente divisível por 2, 3, 4 e 6, por esta razão alguns matemáticos tem adotado o sistema de base 12 no lugar do sistema de base 10.

33..11..22.. SSiisstteemmaa BBiinnáárriioo O sistema binário tem um papel muito importante na tecnologia da computação.

Qualquer número decimal pode ser expresso no sistema binário pela soma das diferentes potências de dois. Por exemplo, começando pela direita 10101101 representa (1 × 20) + (0 × 21) + (1 × 22) + (1 × 23) + (0 × 24) + (1 × 25) + (0 × 26) + (1 × 27) = 173.

Esse exemplo pode ser utilizado para converter números binários em decimais. Para a conversão de números decimais em binários, procede-se pelo método de divisões sucessivas, armazenando os restos das divisões, como visto anteriormente.

As operações aritméticas de sistemas binários são extremamente simples. As regras básicas são : 1 + 1 = 10, e 1 × 1 = 1. O zero funciona como no sistema decimal: 1 × 0 = 0, e 1 + 0 = 1. A soma, a subtração e a multiplicação são executadas de forma similar ao sistema decimal:

Já que somente existem dois dígitos (ou bits) envolvidos, os sistemas binários são utilizados em computadores, uma vez que qualquer número binário pode ser representado, por exemplo, pela posição de uma série de chaves liga-desliga. A posição “liga” poderia

2 Consid6erar somente a parte inteira.

Page 41: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

19191919

3333

corresponde ao 1, e a posição “desliga” poderia corresponder ao 0. No lugar de chaves, podem ser utilizados pontos magnetizados de um disco ferromagnético ou magneto-óptico, onde a magnetização numa direção indica um 1, e na outra um 0. Também podem ser utilizados flip-flops, que são dispositivos eletrônicos que podem ter somente uma tensão elétrica (ou estado) de saída e podem ser chaveados para o outro estado através de um pulso elétrico. Os circuitos lógicos nos computadores executam as diferentes operações com números binários, sendo que a conversão de números decimais para binários e vice-versa é feita eletronicamente.

Base 2 (Binário) Base 10 (Decimal)

8 4 2 1

0 0 0 0

0 0 0 1

0 0 1 0

0 0 1 1

0 1 0 0

0 1 0 1

0 1 1 1

5 = 0 1 0 1

Base 10 Base 2

8 4 2 1 5 0 1 0 1

5 = (0 x 8) + (1 x 4) + (0 x 2) + (1 x 1)

1 0 0 0

1 0 0 1

1 0 1 0

1 0 1 1

1 1 0 0

1 1 0 1

1 1 1 0

0

1

2

3

4

5

7

8

9

10

11

12

13

14

15 1 1 1 1

Na base 10 as colunas são organizadas pelo peso das potências de 10 (unidades = 100, dezenas = 101, centenas = 102, e milhares 103).

Na base 2, as colunas estão organizadas pelo peso das potências de 2 (unidades = 20, duplas = 21, quádruplas = 22 e óctuplas = 23. Este formato é chamado de 8-4-2-1 e é usado também nos computadores.

Tabela 3-1 – Sistema binário

A maioria dos computadores opera usando lógica binária. O computador representa valores usando dois níveis de tensão elétrica (usualmente 0 e +5V). Com estes dois níveis pode-se representar exatamente dois valores diferentes. Por convenção nomeamos estes dois valores possíveis como zero e um. Estes dois valores, coincidentemente, correspondem aos dois dígitos usados no sistemas de numeração binário. Uma vez que existe uma correspondência entre os níveis lógicos usados nos microprocessadores e os dois dígitos usados em sistemas binários, existirá uma identificação perfeita entre o sistema de numeração e o sistema físico.

Page 42: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

20202020

3333

33..11..33.. FFoorrmmaattooss BBiinnáárriiooss Teoricamente, um número binário pode conter um número infinito de dígitos (ou

bits). Por exemplo, pode-se representar o número 5 por:

101000001010000000000101...000000000000101

Ainda, qualquer número 0 pode preceder o número binário sem mudar o seu valor. Por analogia ao sistema decimal podemos ignorar os zeros colocados à esquerda. Por exemplo, 1012 representa o número 5 por convenção. Num dispositivo microprocessador, por exemplo um 8051 que trabalha com grupos de 8 bits, a interpretação dos números binários é facilitada representando-os com um conjunto múltiplo de 4 ou 8 bits. Assim, seguindo a convenção, podemos representar o número cinco como 01012 ou 000001012.

Quando o número de dígitos for muito grande, no sistema decimal, é usual separar os múltiplos de 1000 através de pontos para facilitar a leitura. Por exemplo, o numero 4.294.967.296 fica mais fácil de interpretar que 4294967296. Da mesma forma, para números binários extensos é comum adotar uma técnica semelhante, que consiste em separar os dígitos em grupos de 4 bits separados por um espaço, o que é adequado para a a representação no sistema hexadecimal. Por exemplo, o número binário 1010111110110010 será escrito como 1010 1111 1011 0010.

Freqüentemente são compactadas informações diferentes no mesmo número binário. Por exemplo, uma das formas da instrução MOV do processador 80x86 utiliza o código de 16 bits 1011 0rrr dddd dddd para empacotar três informações no mesmo número: cinco bits para o código de operação (10110), um campo de três bits para indicar o número do registrador (rrr) e um valor numérico de oito bits (dddd dddd). Por conveniência, designa-se um valor numérico às posições de cada bit:

O bit que fica na extremidade direita do número binário é o bit da posição zero, também chamado de bit menos significativo ou LSB (Least Significant Bit).

A cada bit à esquerda é atribuído um número sucessivo até o bit que fica na extremidade esquerda do número, também chamado de bit mais significativo ou MSB (Most Significant Bit).

Um número de 8 bits tem posições que vão do zero até sete. Bit x x x x x x x x Posição 7 6 5 4 3 2 1 0

Um número de 16 bits tem posições que vão do zero até quinze. Bit x x x x x x x x x x x x x x x x Posição 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0

33..22.. OOrrggaanniizzaaççããoo ddooss DDaaddooss

Na matemática pura, um valor pode ser representado por um valor arbitrário de bits. Por outro lado, em aplicações de computadores, geralmente se trabalha com um grupo especifico de bits dependendo do microprocessador utilizado. Coleções de bits comuns são grupos de quatro bits (chamados nibbles), de oito bits (chamados bytes), grupo de n bits (chamados de words). Por exemplo, os microprocessadores Pentium utilizam

Page 43: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

21212121

3333

words de 32 bits (4 bytes), alguns microcontroladores PIC utilizam 14 bits de palavra de código e 1 byte (8 bits) de dados.

33..22..11.. BBiittss A menor unidade de dados em um computador binário é um único bit. Foi visto

que um bit é capaz de representar somente um de dois valores diferentes (tipicamente zero e um). Baseado nesta suposição, pode-se ter a impressão de que existe um número muito pequeno de itens que pode ser representado com um único bit. Isto não é necessariamente verdade, desde que há um número infinito de itens que podem ser representados por um único bit.

Com um único bit, podem ser representados quaisquer dois itens diferentes. Exemplos incluem zero ou um, verdadeiro ou falso, ligado ou desligado, macho ou fêmea, direito ou errado, azul e vermelho, etc.. Desta forma, não há limite para representar tipos de dados em binário (i.e. aqueles objetos que podem ter somente um de dois valores distintos). Pode ser utilizado um único bit para representar os números 966 e 1326, ou no lugar destes, 6242 e 6. Também podem ser representados dois objetos não relacionados entre si, com um único bit, como por exemplo a cor vermelha e o número 3926.

Generalizando ainda mais, bits diferentes podem representar coisas diferentes, por exemplo, um bit pode ser utilizado para representar os valores zero e um, enquanto o bit adjacente pode ser utilizado para representar os valores verdadeiro e falso. Agora, como podemos saber o que os bits significam somente olhando para eles ?. A resposta é, que não há como. Mas isto mostra tudo o que está por trás das estruturas de dados do computador: os dados são o que o programador define que sejam. Se um programador utilizar um bit para representar o valor booleano (verdadeiro ou falso), então aquele bit (por definição do programador) representará verdadeiro ou falso. Para que este bit tenha significado, o programador deverá ser consistente, ou seja, se utilizar um bit para representar verdadeiro ou falso em um ponto do seu programa, não poderá usar os valores verdadeiro ou falso usados naquele bit para representar vermelho ou azul depois.

A maioria dos itens que podem ser modelados, requer de mais de dois valores diferentes, desta forma, bits isolados são o tipo de dados menos utilizado quando há informações mais complexas.

33..22..22.. NNiibbbblleess Um nibble é uma coleção de 4 bits. Não é necessariamente uma estrutura de dados

interessante exceto por duas coisas: os números BCD3 e os números hexadecimais. São necessários de 4 bits para representar um único dígito BCD ou hexadecimal. Com um único nibble pode-se representar até 16 valores distintos (24). No caso dos números hexadecimais, os valores 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, e F são representados por quatro bits. Os números em BCD utilizam dez dígitos diferentes dígitos (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) e também requerem de quatro bits. De fato, quaisquer dezesseis valores distintos podem ser representados por um nibble..

3 BCD: Binary Coded Decimal – Binário Codificado em Decimal.

Page 44: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

22222222

3333

33..22..33.. BByytteess Sem dúvidas, a estrutura mais importante usada nos computadores é o byte. Um

byte consiste de um grupo de oito bits.

Como um byte contém 8 bits, ele pode representar 28 ou seja 256 valores diferentes. Geralmente, é usado um byte para representar valores numéricos na faixa de 0 a 255, ou números com sinal na faixa de –128 a +127. Os caracteres ASCII são outro tipo especial de dados que requerem mais de 256 valores diferentes para identificar as letras, números e principais símbolos do alfabeto ocidental.

33..22..44.. WWoorrddss Uma word é um grupo de n bits. A maioria dos novos sistemas trabalha com

words de 16 bits, mas também existem sistemas com words de 8, 14, 32, 64 e 128 bits.

As words são usualmente subdivididas em nibbles ou em bytes de acordo com a conveniência, devido ao seu grande número de bits.

Uma word de 16 bits pode representar 65536 valores diferentes (216), que poderão ser valores numéricos positivos (unsigned) na faixa de 0 a 65535, ou valores com sinal (signed) de –32768 a +32767, ou ainda qualquer tipo de dado com não mais de 65536 valores. Alguns usos típicos para este tipo de variável são para representar valores numéricos inteiros, offsets e segmentos de endereços de áreas de memória ou endereços de I/O.

33..22..55.. DDoouubbllee WWoorrddss Uma double word é um tipo de dado com o tamanho de duas words.

Normalmente dividida em uma word de ordem maior ou mais significativa, e uma word menos significativa.

Os sistemas 80x86 utilizam por exemplo, words de 16 bits e double words de 32 bits. Uma double word de 32 bits pode representar um número inteiro sem sinal na faixa de 0 a 4294967295 ou um número inteiro com sinal na faixa de –2147483648 a 2147483647.

33..22..66.. NNúúmmeerrooss ccoomm PPoonnttoo FFlluuttuuaannttee Em essência, os computadores são máquinas que operam números inteiros, mas

são capazes de representar números reais pela utilização de códigos complexos. O código mais popular para representar números reais é o padrão definido pela IEEE.

Os computadores processam a informação em conjuntos de bits. Não há como colocar um ponto fracionário pela natureza física do armazenamento. Um número real pode ser representado em notação exponencial, i.e. por um valor inteiro multiplicado pela base elevada a um expoente. Por exemplo, o número 3.26 pode ser representado por 326 x 10-2. Utilizando este tipo de representação podemos definir um número real utilizando somente números inteiros, i.e. armazenando a informação da mantissa (326) e o expoente (-2) em um conjunto de bits.

Page 45: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

23232323

3333

O termo “ponto flutuante” deriva do fato de que não há um número fixo de dígitos antes ou depois do ponto decimal; i.e. o ponto decimal pode flutuar. Existem também representações nas quais o número de dígitos, antes e depois do ponto, é fixo. Estas são chamadas de representações de ponto fixo. Em geral, as representações de ponto flutuante são lentas e menos exatas que as representações de ponto fixo, mas elas podem manipular uma faixa maior de números.

Assim deve ser notado que a maioria dos números em ponto flutuante que um computador pode representar são somente aproximações. Um dos desafios da programação com variáveis de ponto flutuante, é de assegurar que as aproximações levem a resultados precisos. Se o programador não tiver cuidado, pequenas discrepâncias nas aproximações podem levar a resultados finais errados.

Devido ao fato que a matemática de ponto flutuante requerer grande parte dos recursos do computador (memória e tempo de processamento), muitos microprocessadores são equipados com um chip chamado de FPU (floating point unit), especializado em executar a aritmética de ponto flutuante, sendo também chamados de coprocessadores matemáticos.

Usualmente os números de ponto flutuante são representados em 32 bits (4 bytes), mas também há representações em 64 (double, 8 bytes), 80 (long double, 10 bytes) e 128 bits (16 bytes). Um número float representado em 32 bits pode armazenar valores na faixa de 3.4E+/-38 (7 dígitos), um double de 64 bits, 1.7E+/-308 (15 dígitos) e um long double de 80 bits, 1.2E+/-4932 (19 dígitos).

33..33.. OO SSiisstteemmaa ddee NNuummeerraaççããoo HHeexxaaddeecciimmaall

O grande problema de trabalhar com o sistema binário é o grande número de dígitos para representar valores relativamente pequenos. Para representar o valor 202 precisamos de 8 dígitos binários. A versão decimal requer somente de três dígitos decimais, e desta forma, pode representar os números de forma muito mais compacta que o sistema de numeração binário. Este fato não era muito aparente quando os engenheiros projetavam os primeiros sistemas computacionais binários.

Quando se começa a manipular grandes valores, os números binários rapidamente ficam muito extensos. Desafortunadamente, os computadores trabalham em binário, de modo que na maioria das vezes é conveniente usar o sistema de numeração binário. Uma solução seria converter os números binários para o sistema decimal, mas a conversão não é uma tarefa trivial. O sistema hexadecimal (de base 16) oferece as duas características desejadas: são compactos e simples de convertê-los em binário e vice-versa. Por causa disto, a maioria dos computadores de hoje em dia, usam a representação hexadecimal.

Uma vez que a base do sistema hexadecimal é dezesseis, cada dígito à esquerda do ponto decimal representa algum valor vezes as potências sucessivas de 16. Por exemplo o número 123416 é equivalente a:

1 x 163 + 2 x 162 + 3 x 161 + 4 x 160

ou

4096 + 512 + 48 + 4 = 4660 (decimal).

Page 46: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

24242424

3333

Cada dígito hexadecimal pode representar um de dezesseis valores entre 0 e 15. Uma vez que existem somente dez dígitos decimais, é necessário adicionar seis símbolos adicionais para os dígitos que representarão os valores de 10 a 15. Ao invés de criar novos símbolos para estes dígitos, foram redefinidas as letras A à F para simbolizar as novas quantidades. O exemplo que segue é um número hexadecimal válido:

1234 DEAD BEEF 0AFB FEED DEAF

Usualmente é colocada a letra H (ou h) no final do número em hexadecimal, para deixar a base explícita. A seguir alguns exemplos de números em hexadecimal.

1234h

0DEADH

0BEEFh

2AFBh

6567FEEDh

Como pode ser observado, os números hexadecimais são compactos e fáceis de ler. Além disto a relação entre os números hexadecimais e binários é direta, isto é devido ao fato da base 16 ser uma potência exata da base 2. Um dígito em hexadecimal é representado por um nibble. A conversão é feita segundo a seguinte tabela:

Binário Hexadecimal

0000 0 0001 1 0010 2 0011 3 0100 4 0101 5 0110 6 0111 7 1000 8 1001 9 1010 A 1011 B 1100 C 1101 D 1110 E 1111 F

Tabela 3-2 – Equivalência entre os sistemas binário e hexadecimal

A tabela mostra toda a informação necessária para converter qualquer número hexadecimal em um número binário e vice-versa.

Para converter um número hexadecimal em números binários, simplesmente substituem-se os quatro bits correspondentes para cada dígito hexadecimal. Por exemplo, para converter 0ABCDh em um valor binário, simplesmente converte-se cada dígito hexadecimal utilizando a tabela acima, assim:

0 A B C D Hexadecimal

Page 47: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

25252525

3333

0000 1010 1011 1100 1101 Binário

Para converter um número binário no formato hexadecimal procede-se da seguinte forma: o primeiro passo é deixar o número binário com o número de dígitos divisível por quatro colocando zeros à esquerda se for necessário. Por exemplo, dado o número binário 1011001010 com dez dígitos; acrescentam-se 2 dígitos à esquerda para obter um número de dígitos divisível por quatro (12 bits), obtendo o número 001011001010. O passo seguinte é separar o número binário em grupos de quatro bits; assim tem-se 0010 1100 1010. Finalmente compara-se cada grupo de quatro bits na tabela e substitui-se pelos dígitos equivalentes em hexadecimal, ficando assim 2CAh.

A tabela de conversão normalmente é memorizada em poucos minutos, o que agiliza o processo de conversão entre estes dois sistemas.

33..44.. OOppeerraaççõõeess LLóóggiiccaass

Existem quatro operações lógicas principais para trabalhar com números binários: AND, OR, XOR 4 e NOT. O operador AND precisa de pelo menos duas variáveis binárias, e o seu resultado é mostrado a seguir:

0 AND 0 = 0

0 AND 1 = 0

1 AND 0 = 0

1 AND 1 = 1

Uma forma mais compacta de representação é a forma tabular, chamada de tabela verdade. Usualmente o operador AND pode ser substituído pelo símbolo “.”.

As funções lógicas também são implementadas em hardware (portas lógicas) possuindo símbolos especiais como mostra na Figura 3-2.

Figura 3-2 – Porta AND

“Se ambos os operandos de uma função AND são verdadeiros, o resultado será verdadeiro; caso contrário o resultado será falso”.

Um fato importante de notar acerca do operador lógico AND é que o mesmo pode ser utilizado para forçar um resultado zero. Se um dos operandos for zero, o resultado será sempre zero independente do valor do segundo operando. Esta característica da operação

4 OR exclusivo ou “exclusive-OR”.

Page 48: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

26262626

3333

AND é muito importante, especialmente quando se trabalha com conjuntos de bits e for necessário forçar certos bits para zero, mantendo os outros intactos.

O operador OR lógico possui a seguinte característica:

0 OR 0 = 0

0 OR 1 = 1

1 OR 0 = 1

1 OR 1 = 1

Usualmente o operador OR pode ser substituído pelo símbolo “+”.

Figura 3-3 – Porta OR

“Se um dos operandos da função OR for verdadeiro, o resultado será verdadeiro. O resultado falso será obtido somente quando os dois operadores forem falsos”.

Um fato importante de notar-se acerca do operador lógico OR é que o mesmo pode ser utilizado para forçar um resultado “1”. Se um dos operandos for “1”, o resultado será sempre “1” independente do valor do segundo operando. Esta característica da operação OR é muito importante, especialmente quando se trabalha com conjuntos de bits e for necessário forçar alguns destes para “1”, mantendo os outros intactos.

O operador lógico XOR é definido como segue:

0 XOR 0 = 0

0 XOR 1 = 1

1 XOR 0 = 1

1 XOR 1 = 0

Usualmente o operador XOR pode ser substituído pelo símbolo “⊕”. A tabela verdade e o símbolo de hardware é mostrado a seguir.

Page 49: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

27272727

3333

Figura 3-4 – Porta XOR

“O resultado da operação XOR é falso se ambos os operandos forem iguais; caso contrário o resultado é verdadeiro”

O resultado da operação XOR pode também ser definido como tendo valor um quando qualquer um dos operandos for igual a um e não ambos ao mesmo tempo.

Esta característica do operador XOR é útil para inverter bits de forma seletiva em uma cadeia de bits.

O operador NOT é de operando único e é definido como mostra a seguir:

NOT 0 = 1

NOT 1 = 0

Usualmente o operador NOT pode ser substituído por uma barra encima da variável. A tabela verdade e o símbolo de hardware são mostrados a seguir.

Figura 3-5 – Porta NOT

“O operando NOT inverte a entrada, a saída será verdadeira se a entrada for falsa e vice-versa”.

Outros operadores usuais são o NAND (NOT AND) e NOR (NOT OR), cujos símbolos e tabelas verdade são mostrados a seguir.

Figura 3-6 – Portas NAND e NOR

Page 50: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

28282828

3333

33..55.. OOppeerraaççõõeess LLóóggiiccaass eemm NNúúmmeerrooss BBiinnáárriiooss ee CCaaddeeiiaass ddee BBiittss

Como descrito na seção anterior, os operadores lógicos trabalham com operandos de um único bit. Os microprocessadores em geral utilizam grupos de 8, 16 ou 32 bits para representar a informação. Os conceitos anteriores podem ser expandidos para o seu uso com variáveis de mais de um bit. Usualmente as operações lógicas implementadas no processador operam na base do bit a bit (bitwise). Dados dois valores representados com um conjunto de bits maior que um, os operadores operam com o primeiro bit de cada valor, dando o primeiro bit do resultado, e assim sucessivamente para os posteriores. Por exemplo, se tiver que calcular um AND lógico de dois valores representados por oito bits cada, a operação AND será feita em cada coluna independente da outra: 1011 0101 1110 1110 ________ 1010 0100

Esta forma de execução bit a bit pode ser facilmente aplicada as outras operações lógicas.

A capacidade de forçar bits para “0” e para “1” usando os operadores AND e OR, junto com a capacidade de inverté-los usando o operador XOR, são muito importantes quando se trabalha com conjuntos de bits. Cada bit (ou subgrupo de bits) tem um significado diferente e independente dos outros. Estes operadores permitem ao programador manipular os bits de forma seletiva, i.e. sem afetar bits não desejados.

Por exemplo, tendo um valor ‘X’ de oito bits e precisa-se garantir que o os bits quarto até o sétimo, contenham zeros, sendo que os quatros primeiros não devem ser afetados. Neste caso poderá ser utilizada a função lógica AND do valor ‘X’ com o valor binário 0000 1111. O operador AND lógico forçará os quatro bits mais significativos de ‘X’ para zero, mantendo os quatro bits menos significativos não modificados.

De forma análoga, por exemplo, pode ser forçado o bit menos significativo de ‘X’ para 1 utilizando o operador OR com o número binário 0000 0001. Utilizar operadores lógicos AND, OR, XOR e NOT, para manipular cadeias de bits, é um processo conhecido como mascaramento de bits. O termo mascaramento é utilizado quando são usados certos valores (um para AND, zero para OR e XOR) para mascarar certos bits a partir de operações que forçam estes para “0”, “1” ou os invertem.

33..66.. NNúúmmeerrooss ccoomm SSiinnaall ee sseemm SSiinnaall

Nas seções anteriores os números formam tratados como sendo valores sem sinal. O número binário ....00000 representa 0, ....000001 representa 1, ...00000010 representa 2, e assim sucessivamente até o infinito. A questão é, como representar números negativos ?. Os valores com sinal (signed) têm sido mencionados nas seções anteriores mas não foram definidas as regras de representação dos números binários.

Para representar números com sinal usando o sistema de numeração binário devem ser colocadas certas restrições, já que estes possuem um número finito de bits quando se trabalha com computadores que possuem recursos bastante limitados. Usualmente estas limitações são definidas pelo tipo de dado, e portanto o seu tamanho em número de bits.

Page 51: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

29292929

3333

Um número fixo de bits, pode representador um certo número de objetos. Por exemplo, com oito bits pode-se representar 256 objetos diferentes. Os valores negativos são objetos também, da mesma forma que os números positivos. Desta forma, podemos utilizar alguns dos 256 valores diferentes para representar os números negativos. Em outras palavras, teremos que sacrificar alguns números positivos para representar os números negativos.

Para executar isto da forma mais lógica, será designada uma das metades das possíveis combinações para os valores negativos, e a segunda metade para os positivos. Assim pode-se representar os valores negativos de –128 a –1 e os valores positivos de 0 a +127 com um único conjunto de 8 bits. Com uma palavra de 16 bits poderão ser representados valores na faixa de –32768 a + 32767. Com uma palavra de 32 bits pode-se representar valores na faixa de 2147483648 a +2147483647. De forma geral com n bits podem ser representados valores com sinal na faixa de -2(n-1) a +2(n-1)-1.

Existem muitas formas de organizar os números negativos em binário para a faixa definida, mas a maioria dos sistemas microprocessados adota o padrão chamado de “complemento de dois”. Num sistema que utiliza o complemento de dois, o bit mais significativo é o bit de sinal. Se o bit mais significativo for zero, o número é positivo, caso contrário será negativo. Observar os seguintes exemplos:

Para números de 16 bits com sinal: 8000h é negativo pois o bit de sinal está em 1 (1000 0000 0000 0000). 0100h é positivo porque o bit mais significativo está em 0 (0000 0001 0000 0000). 7FFFh é positivo (0111 1111 1111 1111). FFFFh é negativo (1111 1111 1111 1111). 0FFFh é positivo (0000 1111 1111 1111).

Se o bit mais significativo é “0”, então o número é positivo e armazena um valor em binário padrão. Se o bit mais significativo for “1”, então o número é negativo e é armazenado na forma de complemento de dois. Para converter um número positivo no seu equivalente negativo na forma de complemento de dois, utilizar o seguinte algoritmo:

1. Inverter todos os bits no número, i.e. deve-se aplicar o operador NOT bit a bit.

2. Adicionar um ao número invertido resultante.

Por exemplo, para calcular o valor de oito bits equivalente a –5: 0000 0101 Cinco (em binário). 1111 1010 Invertendo todos os bits. 1111 1011 Somando um obtem-se o resultado (-5 em complemento de dois).

Tomando o resultado obtido (-5) e procedendo-se da mesma maneira, obtém-se novamente o valor original, com era esperado: 1111 1011 Complemento de dois para -5. 0000 0100 Invertendo todos os bits. 0000 0101 Somando um obtemos o resultado (+5).

Os exemplos a seguir ilustram alguns valores positivos e negativos de 16 bits: 7FFFh: +32767, o maior número positivo com sinal de 16-bit. 8000h: -32768, o menor número (com sinal) de 16 bits. 4000h: +16,384.

Para converter números acima siga os passos do algoritmo, assim:

Page 52: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

30303030

3333

7FFFh: 0111 1111 1111 1111 +32,767 1000 0000 0000 0000 Inverter todos os bits (8000h) 1000 0000 0000 0001 Adicione “1” (8001h ou -32,767)

8000h: 1000 0000 0000 0000 -32,768

0111 1111 1111 1111 Inverter todos os bits (7FFFh) 1000 0000 0000 0000 Adicione “1” (8000h ou -32768)

4000h: 0100 0000 0000 0000 16,384

1011 1111 1111 1111 Inverter todos os bits (BFFFh) 1100 0000 0000 0000 Adicionar “1” (0C000h ou -16,384)

O valor 8000h invertido fica 7FFFh. Depois de adicionar “1” obtem-se 8000h!. Ou seja +32767 + 1 = -32768 !. Fica claro que com um sistema de numeração com sinal de 16 bits não se pode representar o valor +32768, assim como também não se pode representar valores negativos menores que –32768. Se o programador não tiver cuidado com isto, poderão acontecer erros na implementação difíceis de serem detectados. Usualmente os microprocessadores vêm equipados com um flag que indica este tipo de ocorrências, chamado de flag de overflow.

Alguns questionamentos comuns são os seguintes : Porque usar a notação em complemento de dois ?. Porque não usar simplesmente um bit como sinal e usando os bits restantes para armazenar o

equivalente positivo do número ?

A resposta está no hardware. Tornar valores positivos em negativos pode ser um trabalho tedioso, mas usando o complemento de dois a maioria das outras operações ficam mais fáceis de serem executadas. Por exemplo, suponha que devem ser somados dois números 5 + (-5) representados com oito bits. O resultado é zero. Considere o que acontece quando são somados estes dois valores num sistema que usa o complemento de dois: 00000101 11111011 _________ 1 00000000

No final da soma existe um vai-um (carry) que deverá ocupar o nono bit, sendo que os outros são iguais a zero. Se inicialmente se bit de vai-um for ignorado, o resultado da soma de dois valores com sinal sempre produzem o resultado correto quando usam o sistema de complemento de dois. Isto permite que possa ser utilizado o mesmo hardware para somas e subtrações de números com sinal e sem sinal. Para outros tipos de representação isto não é verdade.

De qualquer maneira, deve ser notado que os dados representados por um grupo binário de bits depende inteiramente do contexto. O valor binário de oito bits 1100 0000 pode representar um caractere ASCII, um valor decimal (192), o valor –64, uma cor, etc.. O programador tem como responsabilidade a utilização destes dados de forma consistente.

33..77.. EExxtteennssããoo ddee SSiinnaall ee ddee ZZeerrooss

Uma vez que o formato de complemento de dois tem comprimento fixo, surge um pequeno problema: o que acontecerá se for necessário converter um número de oito bits em complemento de dois para um valor de 16 bits ?. Este problema e o seu inverso

Page 53: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

31313131

3333

(converter um valor de 16 bits num outro de 8 bits) podem ser tratados via operações de extensão de sinal e contrações. A extensão de zeros permite converter pequenos valores sem sinal em grandes valores também sem sinal.

Considere o valor –64. O valor em oito bits em complemento de dois para este número é C0h. O equivalente de 16 bits para o mesmo valor é FFC0h. Agora considere o valor +64. As versões deste valor para oito e dezesseis bits são respectivamente 40h e 0040h. A diferença entre os números de oito e dezesseis bits pode ser descrita pela seguinte regra: “Se o número for negativo, o byte mais significativo do número de 16 bits conterá FFh; se o número é positivo, o byte mais significativo conterá 00h”. Isto é válido convertendo um número com sinal de oito bits num equivalente de dezesseis e não ao contrário.

Para fazer a extensão de sinal de um valor para o seu equivalente com maior número de bits simplesmente copiar o bit de sinal em todos os bits adicionais do novo formato. Por exemplo, para estender o sinal em um número de oito bits par um de dezesseis, simplesmente deve-se copiar o sétimo bit (mais significativo) do número de oito bits nos bits 8 a 15 do novo número de 16 bits. Para estender o sinal de um número de 16 bits para um número de 32, simplesmente deve-se copiar o bit 15 nos bits 16 a 31 do novo formato.

A extensão de sinal é necessária quando se manipulam valores com sinal de tamanhos diferentes. Freqüentemente é necessário adicionar um byte com uma word. Para isto deve-se estender o sinal do byte antes de efetuar a operação. Outras operações tais como multiplicação e divisão especialmente, podem requerer uma extensão de sinal de 32 bits. A extensão de sinal não vale para sistemas de números sem sinal (unsigned).

Exemplos de extensão de sinal: f Dezesseis bits Trinta e dois bits 80h FF80h FFFFFF80h 28h 0028h 00000028h 9Ah FF9Ah FFFFFF9Ah 7Fh 007Fh 0000007Fh - 1020h 00001020h - 8088h FFFF8088h

Tabela 3-3 - Exemplos de extensão de sinal

Para estender um número sem sinal (unsigned) deve ser utilizada a extensão de zeros. A extensão de zeros é direta, onde os zeros são diretamente são colocados nos lugares vagos mais significativos. Por exemplo, para estender os zeros do valor 82h de oito bits, para o equivalente de dezesseis, simplesmente deve-se adicionar zeros no byte mais significativo, obtendo 0082h.

Oito bits Dezesseis bits Trinta e dois bits 80h 0080h FFFFFF80h 28h 0028h 00000028h 9Ah 009Ah FFFFFF9Ah 7Fh 007Fh 0000007Fh - 1020h 00001020h - 8088h 00008088h

Tabela 3-4 – Mais exemplos de extensão de sinal

A contração de sinal, convertendo um valor em outro idêntico com número menor de bits, é um pouco mais complicada. A extensão de sinal vista anteriormente nunca falha. Dado um número com sinal de m bits sempre poderá ser convertido num número de n bits,

Page 54: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

32323232

3333

se n > m, usando a extensão de sinal. Desafortunadamente, dado um número de n bits, não pode ser sempre convertido num número de m bits se m < n. Por exemplo, considerar o valor –448. Usando 16 bits a sua representação é 0FE40h. A magnitude deste número é maior da que pode ser comportada num valor de oito bits, de forma que não pode ser contraído em oito bits. Este é um exemplo de uma condição de overflow que pode ocorrer durante a conversão.

Para poder contrair apropriadamente um valor em outro, deve ser verificado o(s) byte(s) mais significativo(s) que se desejam descartar. O byte mais significativo que se deseja remover deverá conter 00h ou FFh. Se tiver qualquer outro valor, o número não poderá ser contraído sem efetuar um overflow. Finalmente, o bit mais significativo do valor resultante deve ter o mesmo valor que os que foram removidos do número.

Exemplos para números de 16 bits. FF80h pode ser contraído em 80h 0040h pode ser contraído em 40h FE40h não pode ser contraído em 8 bits. 0100h não pode ser contraído em 8 bits.

33..88.. AA CCooddiiffiiccaaççããoo AASSCCIIII

ASCII são as siglas para American Standard Code for Information Interchange. É um código que designa valores numéricos às letras, números, sinais de pontuação e outros símbolos especiais. Este padrão foi criado para organizar e compatibilizar a troca de informação entre vários computadores e sistemas.

O ASCII define 256 códigos divididos em dois conjuntos, o padrão e o estendido, com 128 códigos cada. Estes grupos representam o total de representações possíveis de 8 bits (1 byte). O conjunto básico ou padrão, utiliza 7 bits para cada código, usando do código 0 até o 127 (00h a 7Fh), sendo que o conjunto estendido utiliza os códigos de 128 a 255 (80h a FFh).

No conjunto padrão, os primeiros 32 caracteres são designados para os códigos de comunicação e controle de impressão (basicamente caracteres não imprimíveis), tais como retorno de posição, retorno de carro, nova linha e tabulação, que são utilizados para controlar a forma com que a informação é transferida de um computador para outro sistema. Os 96 códigos remanescentes são designados aos sinais de pontuação, os dígitos 0 ao 9, e as letras maiúsculas e minúsculas do alfabeto romano.

O conjunto estendido de códigos é designado para grupos variáveis de caracteres para serem usados pelos fabricantes de computadores e engenheiros de software. Estes códigos não são intercambiáveis entre diferentes programas de computador como o conjunto padrão de caracteres ASCII.

33..88..11.. OO GGrruuppoo PPaaddrrããoo ddee CCaarraacctteerreess O grupo padrão de caracteres pode ser dividido em quatro subgrupos de 32

caracteres. Os primeiros 32 caracteres (código 00h a 1Fh) formam um grupo especial de caracteres não imprimíveis chamados caracteres de controle, porque executam várias funções de controle de impressão. Desafortunadamente, os caracteres de controle

Page 55: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

33333333

3333

executam diferentes operações em dispositivos diferentes de saída de dados. Existe uma padronização muito fraca nos dispositivos de saída em geral.

O segundo subgrupo consiste em vários símbolos de pontuação, caracteres especiais e dígitos numéricos. Os caracteres mais notáveis deste grupo incluem o caractere “espaço” (código 20h) e os dígitos numéricos (códigos 30h a 39h). Deve ser notado que os dígitos numéricos diferem dos seus valores numéricos somente no nibble mais significativo. Subtraindo 30h do código em ASCII, pode ser obtido o equivalente numérico para aquele dígito.

O terceiro subgrupo de 32 caracteres é reservado para os símbolos das letras maiúsculas do alfabeto. Os códigos ASCII para os caracteres de ‘A’ a ‘Z’ ficam na faixa de 41h a 5Ah. Como existem somente 26 caracteres alfabéticos definidos, os seis códigos remanescentes servem para representar outros símbolos especiais.

O quarto e último subgrupo de 32 caracteres é reservado para os símbolos das letras minúsculas do alfabeto, cinco símbolos especiais e um outro caractere de controle (delete). Notar que os símbolos dos caracteres minúsculos usam os códigos 61h a 7Ah. Convertendo os códigos para os caracteres maiúsculos e minúsculos, pode ser notado que a diferença entre ambos tipos diferem da posição de somente um bit. Por exemplo, considerar o código para as letras ‘E’ (45h) e ‘e’ (65h)

‘E’: 0100 0101

‘e’: 0110 0101

Ambos códigos diferem somente no bit cinco. Os caracteres em maiúsculas sempre contêm um “0” no bit número cinco e os caracteres em minúsculas, em um. Esta característica pode ser facilmente usada para converter rapidamente maiúsculas em minúsculas e vice-versa. Tendo um caractere em maiúsculas pode ser forcado para minúsculas setando o bit número cinco. Estas tarefas são facilmente executadas utilizando as funções lógicas vistas nas seções anteriores.

Em resumo, os bits número cinco e seis determinam o subgrupo: Bit 6 Bit 5 Subgrupo 0 0 Caracteres de Controle 0 1 Dígitos e Pontuação 1 0 Maiúsculas e especiais 1 1 Minúsculas & especiais

Tabela 3-5 – Subgrupo de bits de controle

Desta maneira, podem ser convertidos quaisquer caracteres maiúsculos ou minúsculos nos seus equivalentes caracteres de controle resetando os bits número cinco e seis.

Considere no momento, os códigos ASCII para os dígitos numéricos: "0" 48 30h "1" 49 31h "2" 50 32h "3" 51 33h "4" 52 34h "5" 53 35h "6" 54 36h "7" 55 37h

Page 56: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

34343434

3333

"8" 56 38h "9" 57 39h Char Dec Hex

Tabela 3-6 – Valores ASCII dos dígitos numerais

A representação decimal destes códigos não é muito clara em relação ao símbolo que representam. A representação hexadecimal deste código ASCII revela algumas características importantes como que o nibble menos significativo do código é equivalente ao valor do número representado. Desta forma, resetando para zero o nibble menos significativo do código numérico do caractere numérico, pode ser convertido no seu significado em binário correspondente. De forma análoga, pode ser convertido um valor numérico binário na sua representação em código ASCII simplesmente setando para “1” os dois primeiros bits do nibble mais significativo. Notar que pode ser utilizado um operador lógico AND para forçar os bits mais significativos para zero, ou OR para forçá-los para um.

Deve ser notado que não é possível converter uma cadeia (string) de caracteres numéricos para a sua representação equivalente em binário simplesmente ajustando o nibble mais significativo para cada dígito da string. Se for convertido o número 123 (31h 32h 33h) desta forma obteremos três bytes: 010203h, diferente do valor correto, que deveria ser 7Bh. A conversão de cadeias de dígitos em números inteiros, requer maior sofisticação, sendo que a conversão sugerida anteriormente funciona somente com dígitos isolados.

Embora é dado o nome de padrão ASCII, o simples uso desta codificação não garante a compatibilidade entre sistemas. Enquanto é verdade que a letra ‘A’ numa máquina é freqüentemente uma ‘A’ na outra máquina, não existe uma verdadeira padronização entre máquinas com respeito aos caracteres de controle. Do total de 32 caracteres de controle mais o “delete”, existem somente quatro códigos de controle comumente suportados: o retorno de cursor (backspace - BS), a tabulação, retorno de carro (CR) e nova linha (LF). O que é pior, diferentes máquinas freqüentemente utilizam estes códigos de controle de formas diferentes. O fim de linha é um exemplo particularmente problemático. Enquanto os sistemas MS-DOS, CP/M e outros sistemas marcam o final de uma linha com uma seqüência de dois caracteres (CR e LF), os sistemas Apple Macintosh, Apple II e outros marcam o final de linha com um único caractere (CR).

Os sistemas UNIX marcam o final de uma linha com um único caractere LF. Não é necessário dizer que tentando intercambiar simples arquivos de texto entre estes sistemas pode ser frustrante. Se forem utilizados os caracteres padrão ASCII em todos seus arquivos, será necessário converter os dados para intercambiar dados com um outro que não tem o mesmo padrão. Felizmente tal conversão é bastante simples.

Outro tipo de formato que pode ser usado é o formato ANSI. Ambos os sistemas, ASCII e ANSI, foram desenvolvidos para padronizar a comunicação entre computadores.

Os países cujo idioma não é o inglês, têm desenvolvido outros métodos de codificação. É interessante considerar outros idiomas, tais como o japonês, o chinês e o coreano, que possuem mais caracteres que os do alfabeto inglês. Este problema requer um sistema de codificação diferente do ASCII com seus 127 caracteres ou o ANSI com seus 256 caracteres. Os japoneses por exemplo usam a codificação EUC, JIS, S-JIS, and JASCII para manipular caracteres. A internacionalização tenta definir um padrão para todos os

Page 57: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

35353535

3333

códigos de caracteres num padrão universal. Um destes esquemas de codificação é chamado UNICODE.

A seguir é apresentada a tabela de códigos ASCII padrão. Character Name Char Code Decimal Binary Hex Null NUL Ctrl @ 0 00000000 00 Start of Heading SOH Ctrl A 1 00000001 01 Start of Text STX Ctrl B 2 00000010 02 End of Text ETX Ctrl C 3 00000011 03 End of Transmit EOT Ctrl D 4 00000100 04 Enquiry ENQ Ctrl E 5 00000101 05 Acknowledge ACK Ctrl F 6 00000110 06 Bell BEL Ctrl G 7 00000111 07 Back Space BS Ctrl H 8 00001000 08 Horizontal Tab TAB Ctrl I 9 00001001 09 Line Feed LF Ctrl J 10 00001010 0A Vertical Tab VT Ctrl K 11 00001011 0B Form Feed FF Ctrl L 12 00001100 0C Carriage Return CR Ctrl M 13 00001101 0D Shift Out SO Ctrl N 14 00001110 0E Shift In SI Ctrl O 15 00001111 0F Data Line Escape DLE Ctrl P 16 00010000 10 Device Control 1 DC1 Ctrl Q 17 00010001 11 Device Control 2 DC2 Ctrl R 18 00010010 12 Device Control 3 DC3 Ctrl S 19 00010011 13 Device Control 4 DC4 Ctrl T 20 00010100 14 Negative Acknowledge NAK Ctrl U 21 00010101 15

Synchronous Idle SYN Ctrl V 22 00010110 16 End of Transmit Block ETB Ctrl W 23 00010111 17

Cancel CAN Ctrl X 24 00011000 18 End of Medium EM Ctrl Y 25 00011001 19 Substitute SUB Ctrl Z 26 00011010 1A Escape ESC Ctrl [ 27 00011011 1B File Separator FS Ctrl \ 28 00011100 1C Group Separator GS Ctrl ] 29 00011101 1D Record Separator RS Ctrl ^ 30 00011110 1E Unit Separator US Ctrl _ 31 00011111 1F Space 32 00100000 20 Exclamation Point ! Shift 1 33 00100001 21 Double Quote " Shift ‘ 34 00100010 22 Pound/Number Sign # Shift 3 35 00100011 23 Dollar Sign $ Shift 4 36 00100100 24 Percent Sign % Shift 5 37 00100101 25 Ampersand & Shift 7 38 00100110 26 Single Quote ‘ ‘ 39 00100111 27 Left Parenthesis ( Shift 9 40 00101000 28 Right Parenthesis ) Shift 0 41 00101001 29 Asterisk * Shift 8 42 00101010 2A Plus Sign + Shift = 43 00101011 2B Comma , , 44 00101100 2C Hyphen / Minus Sign - - 45 00101101 2D Period . . 46 00101110 2E Forward Slash / / 47 00101111 2F Zero Digit 0 0 48 00110000 30 One Digit 1 1 49 00110001 31 Two Digit 2 2 50 00110010 32

Page 58: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

36363636

3333

Three Digit 3 3 51 00110011 33 Four Digit 4 4 52 00110100 34 Five Digit 5 5 53 00110101 35 Six Digit 6 6 54 00110110 36 Seven Digit 7 7 55 00110111 37 Eight Digit 8 8 56 00111000 38 Nine Digit 9 9 57 00111001 39 Colon : Shift ; 58 00111010 3A Semicolon ; ; 59 00111011 3B Less-Than Sign < Shift , 60 00111100 3C Equals Sign = = 61 00111101 3D Greater-Than Sign > Shift . 62 00111110 3E Question Mark ? Shift / 63 00111111 3F At Sign @ Shift 2 64 01000000 40 Capital A A Shift A 65 01000001 41 Capital B B Shift B 66 01000010 42 Capital C C Shift C 67 01000011 43 Capital D D Shift D 68 01000100 44 Capital E E Shift E 69 01000101 45 Capital F F Shift F 70 01000110 46 Capital G G Shift G 71 01000111 47 Capital H H Shift H 72 01001000 48 Capital I I Shift I 73 01001001 49 Capital J J Shift J 74 01001010 4A Capital K K Shift K 75 01001011 4B Capital L L Shift L 76 01001100 4C Capital M M Shift M 77 01001101 4D Capital N N Shift N 78 01001110 4E Capital O O Shift O 79 01001111 4F Capital P P Shift P 80 01010000 50 Capital Q Q Shift Q 81 01010001 51 Capital R R Shift R 82 01010010 52 Capital S S Shift S 83 01010011 53 Capital T T Shift T 84 01010100 54 Capital U U Shift U 85 01010101 55 Capital V V Shift V 86 01010110 56 Capital W W Shift W 87 01010111 57 Capital X X Shift X 88 01011000 58 Capital Y Y Shift Y 89 01011001 59 Capital Z Z Shift Z 90 01011010 5A Left Bracket [ [ 91 01011011 5B Backward Slash \ \ 92 01011100 5C Right Bracket ] ] 93 01011101 5D Caret ^ Shift 6 94 01011110 5E Underscore _ Shift - 95 01011111 5F Back Quote ` ` 96 01100000 60 Lower-case A a A 97 01100001 61 Lower-case B b B 98 01100010 62 Lower-case C c C 99 01100011 63 Lower-case D d D 100 01100100 64 Lower-case E e E 101 01100101 65 Lower-case F f F 102 01100110 66 Lower-case G g G 103 01100111 67 Lower-case H h H 104 01101000 68 Lower-case I I I 105 01101001 69 Lower-case J j J 106 01101010 6A Lower-case K k K 107 01101011 6B Lower-case L l L 108 01101100 6C Lower-case M m M 109 01101101 6D

Page 59: Tratado Da Linguagem c

C O D I F I C A Ç Ã O D A I N F O R M A Ç Ã O

37373737

3333

Lower-case N n N 110 01101110 6E Lower-case O o O 111 01101111 6F Lower-case P p P 112 01110000 70 Lower-case Q q Q 113 01110001 71 Lower-case R r R 114 01110010 72 Lower-case S s S 115 01110011 73 Lower-case T t T 116 01110100 74 Lower-case U u U 117 01110101 75 Lower-case V v V 118 01110110 76 Lower-case W w W 119 01110111 77 Lower-case X x X 120 01111000 78 Lower-case Y y Y 121 01111001 79 Lower-case Z z Z 122 01111010 7A Left Brace Shift [ 123 01111011 7B Vertical Bar | Shift \ 124 01111100 7C Right Brace Shift ] 125 01111101 7D Tilde ~ Shift ` 126 01111110 7E Delta ∆ 127 01111111 7F

Tabela 3-7 – Tabela ASCII

Page 60: Tratado Da Linguagem c

38383838

44.. PPRROOJJEETTOO DDEE SSIISSTTEEMMAASS DDEE SSOOFFTTWWAARREE

Normalmente, o projeto de um sistema não envolve somente os passos da programação e o código fonte, mas uma série de etapas importantes que devem ser levadas em conta antes de começar o projeto.

Qualquer projeto pode ser dividido quatro etapas principais, uma etapa de especificação do sistema (software e hardware), uma etapa de implementação, uma etapa de depuração e uma etapa de validação.

Todo projeto começa com uma especificação escrita e detalhada de todo os sistema desejado, usualmente chamado de Especificação de Sistema. Nele deverão estar descritos da forma mais detalhada possível todos as características de hardware desejadas, as funções do software, os protocolos de comunicação a serem utilizados e os cronogramas completos, entre outros detalhes. Qualquer detalhe omitido ou esquecido nesta etapa pode inutilizar o projeto todo. A etapa de especificação é a mais importante do projeto e deve ser alocado o tempo suficiente para ela. É comum que projetos de software de grande porte tenham a metade do tempo de projeto alocado somente para a especificação.

Na etapa de implementação são construídos e definidos os algoritmos de software1 durante a especificação. A etapa de depuração serve para a detecção e correção de erros que aconteceram nas interfaces entre as sub-etapas do projeto.

A etapa de validação serve para avaliar os objetivos alcançados em relação aos propostos na etapa de especificação, e a de encontrar problemas não detectados nas etapas anteriores. Usualmente devido a validação deverá ser executada novamente uma etapa de depuração para correção de erros e melhorias de funcionamento, cujos resultados deverão ser validados novamente.

As etapas mencionadas mais acima possuem uma sub-etapa de documentação que normalmente é executada em paralelo, e que incluem, descrições de software e hardware, relatórios de validação, lista de erros e melhorias a serem efetuadas, etc..

Os projetos de engenharia eletrônica, normalmente devem ser projetados os sistemas de hardware e de software. Nos projetos de grandes sistemas há uma necessidade de estilo, abstração e formalismo.

1 Os conceitos desenvolvidos neste capítulo, também se aplicam a projetos de hardware.

Capítulo

4

Page 61: Tratado Da Linguagem c

P R O J E T O D E S I S T E M A S

39393939

4444

44..11.. TToopp DDoowwnn

44..11..11.. EEssttiilloo A mente humana precisa de ajuda quando efetua tarefas grandes ou complexas. O

estilo é o método de particionar um problema grande em sub-unidades manipuláveis de uma forma sistemática e inteligível. A necessidade de ter-se um bom estilo fica mais aparente quando o problema é maior. Os programas de computador são um dos grandes tipos de projetos complexos implementados pelo homem, alguns deles excedendo 5000000 de linhas de código, sendo estes tão grandes que nenhuma pessoa pode acompanhar o programa completo, e nem uma parte significativa do mesmo.

O estudo do estilo de programação tem forçado aos projetistas a encarar o particionamento do problema em uma arte, como sendo um caminho para ganhar controle sobre os seus projetos. O estilo em programação pode ser definido por um grupo de técnicas chamadas de “top-down” e “estruturada”. O hardware de grandes computadores envolve uma complexidade que fica na mesma escala daqueles programas gigantes. Podemos citar algumas regras para obter um bom estilo em projetos de sistemas: Projete de cima para baixo (top for down): O projeto começa com a especificação do sistema

completo de forma suficientemente compacta para que uma pessoa possa rapidamente compreende-la. O projeto procede pelo seccionamento do sistema em subsistemas e sub-unidades com as suas inter-relações bem definidas. Depois disto, cada subsistema poderá ser descrito em detalhes mantendo a capacidade de compreender os detalhes da unidade e do sistema como um todo. Este processo continua até o sistema ter sido especificado completamente e no detalhe, podendo prosseguir com a elaboração do cronograma. Sempre se devem utilizar técnicas que mantenham o projetista no caminho correto, dentro do

processo da implementação (técnicas foolproof). O hardware permite um alto grau de flexibilidade no projeto. Esta excessiva flexibilidade permite aos projetistas de utilizar circuitos complexos e inusuais. O uso incontrolado desta flexibilidade promove a implementação de forma indisciplinada, não-inteligente e incorreta. Este fenômeno tem a contrapartida (de forma menos severa) em software de computadores, onde a linguagem assembly permite o acesso a todo o poder do computador. A experiência na solução de problemas de software e hardware tem mostrado que se deve restringir as ferramentas e técnicas de projeto nas que mostram uma capacidade funcional e interpretativa, sobre uma variedade de circunstâncias. Usar técnicas de documentação para o nível de sistema e para nível dos circuitos ou rotinas de

software (descrições de software e hardware) que mostrem claramente o que o projetistas estava pensando quando o problema foi primeiramente abstraído para uma solução abstrata e depois para uma solução de software e hardware. A violação deste preceito atua contra o princípio da “cortesia comum”. Durante a documentação o projetista deve-se colocar no lugar do usuário ou mantenedor do seu projeto, mantendo uma documentação clara e completa.

44..11..22.. AAbbssttrraaççããoo Neste contexto, a abstração permite encarar o problema num nível conceptual. O

conceito de memória é um exemplo de abstração. Quando o projeto começar é necessário encará-lo com elementos conceituais a as suas inter-relações. Somente mais tarde, durante o processo de implementação será necessário trabalhar com conceitos reais. Esta liberdade é absolutamente essencial para um começo apropriado de um projeto de complexidade considerável. Começa-se de cima e continua-se reduzindo o problema, até seus elementos conceituais básicos. Por exemplo um computador precisará de uma memória, um sistema de entrada e saída, uma unidade aritmética e outros subsistemas.

Page 62: Tratado Da Linguagem c

P R O J E T O D E S I S T E M A S

40404040

4444

Comumente começa-se o projeto neste nível de abstração, e prossegue-se descendo a níveis inferiores, um por um, sempre no ponto de vista conceitual. Desta forma, no próximo nível será desenhado um diagrama de blocos de uma unidade aritmética, pela interconexão das suas unidades funcionais, tais como registradores, unidades de controle, e barramento de dados. A abstração inicial é a parte crítica de qualquer projeto, desde que planejamento errado nas fases iniciais do mesmo levará inevitavelmente a implementações erradas. Normalmente não há forma de resgatar um projeto mal planejado utilizando circuitos exóticos ou rotinas de ajuste.

44..11..33.. FFoorrmmaalliissmmoo O formalismo é a teoria do comportamento do sistema. Em um projeto, o

formalismo ajuda a estabelecer regras e procedimentos sistemáticos com características conhecidas. Os formalismos são importantes em todos os níveis do projeto. Os formalismos de alto nível não são de grande importância para o bom desenvolvimento do projeto e servem somente para adotar métodos sistemáticos em todos os níveis onde se espera a transformação correta dos conceitos em hardware ou software. No nível de implementação, o formalismo é extremamente necessário e deve ser rígido o suficiente para evitar erros na implementação.

Duas naves espaciais destinadas a cartografar o planeta Marte, foram perdidas no espaço em 1999 porque alguns dos algoritmos projetados, utilizavam unidades do sistema inglês enquanto que outros utilizavam unidades no sistema internacional. Como o erro era muito pequeno para ser detectado nos auto-testes, resultaram no exemplo de um dos erros de formalismo mais caros da história.

44..11..44.. OO PPrroojjeettoo eemm TToopp--DDoowwnn O projeto começa com o estudo cuidadoso do problema geral. Deliberadamente

devem ser ignorados os detalhes neste estágio, e devem ser feitas perguntas como: O problema está claramente definido ? É possível remodelar o problema para obter mais clareza ou simplificá-lo ? Se estivesse trabalhando com um subsistema de um sistema maior; quais são as relações com o

subsistema de hierarquia maior ?. Poderá um particionamento diferente do sistema inteiro simplificar a estrutura ?

Neste estágio o entendimento é global e se deve permanecer neste nível até ter esmiuçado sensivelmente e digerido o problema, até o ponto onde haja o convencimento de que este pode ser resolvido. Isto é essencial, desde que qualquer dificuldade neste nível é seria e pode ser insolúvel.

Após ter especificado claramente o problema no nível global, procede-se ao particionamento racional do problema em pequenas peças com inter-relações claramente definidas. O objetivo é de escolher as peças “naturais” de tal forma que cada peça possa ser compreendida como uma unidade e sejam bem compreendidas as interações entre as unidades. Este processo de particionamento continua para níveis inferiores, até a escolha final das funções a serem utilizadas, circuitos integrados a serem empregados, etc..

Page 63: Tratado Da Linguagem c

P R O J E T O D E S I S T E M A S

41414141

4444

44..22.. AAllggoorriittmmooss

Nas ciências matemáticas, um algoritmo é um método de resolver um problema pelo uso repetitivo de métodos computacionais simples. Um exemplo básico é o processo da divisão de números grandes. O termo algoritmo hoje em dia é aplicado para vários tipo de soluções de problemas que empregam uma seqüência mecânica de passos, como nos programas de computador. A seqüência pode ser representada na forma de um diagrama de fluxo ou fluxograma para facilitar o entendimento da seqüência.

Assim como os algoritmos utilizados na aritmética, os algoritmos para computadores podem ser simples ou altamente complexos. Em todos os casos, a tarefa que o algoritmo vai desempenhar deve ser bem definida. Isto é, as definições podem envolver termos matemáticos ou lógicos ou um conjunto de dados ou instruções escritas, mas a tarefa em si deve ser de tal forma que possa ser representada de uma maneira.

Em dispositivos tais como computadores, a lógica é a forma de algoritmo. Como os computadores aumentam em complexidade, mais e mais algoritmos de programas de software tomam a forma do que é chamado de “hard software”. Isto é, há um aumento da parte básica do circuito elétrico dos computadores que facilitam a inserção de lógicas em hardware. Muitos algoritmos de aplicação diferentes estão disponíveis, e sistemas altamente avançados tais como algoritmos de inteligência artificial ficarão muito comuns num futuro próximo.

44..33.. FFlluuxxooggrraammaass

Um fluxograma [4] ou diagrama de fluxo, é um diagrama seqüencial empregado em várias áreas da tecnologia para mostrar os procedimentos, passo a passo, que devem ser executados para a implementação de uma certa tarefa ou para a geração de um produto; por exemplo, para descrever processos em manufaturas, ou para resolver um determinado problema, em algoritmos.

Um fluxograma é basicamente a representação gráfica por meio de símbolos geométricos, da solução algorítmica de um problema. Os fluxogramas são compostos de blocos ou caixas, conectadas por setas. Para descrever o processo descrito em um diagrama de fluxo, começa-se pelo bloco inicial e segue-se de bloco em bloco seguindo as setas e executando as ações indicadas. A forma de cada bloco indica o tipo de ação que esta representa, tais como processamento, tomadas de decisão e controle.

Os blocos de processo indicam a execução de uma determinada ação.

Processo 1

Figura 4-1 – Bloco de processo

Page 64: Tratado Da Linguagem c

P R O J E T O D E S I S T E M A S

42424242

4444

As caixas de decisões indicam o caminho a ser tomado de acordo com uma condição.

Condição

Verdadeira

Falsa

Figura 4-2 – Bloco de tomada de decisão

Os blocos mais as setas de conexão, são suficientes para representar qualquer diagrama de fluxo. Existem outros tipos de blocos que especificam tipos de processos específicos, mas que não são imprescindíveis.

Processo 1

Condição Processo 2

Processo 3

Inicio

Fim

Verdadeira

Falsa

Figura 4-3 – Fluxograma Genérico

44..44.. CCoommppoonneenntteess BBáássiiccooss ddee uumm PPrrooggrraammaa

Um programa de computador é baseado no fluxograma do algoritmo e implementa a solução de software para o problema.

Um programa em qualquer linguagem normalmente é composto pelos seguintes itens::

Page 65: Tratado Da Linguagem c

P R O J E T O D E S I S T E M A S

43434343

4444

Os programas devem obter informação de alguma fonte de entrada. Os programadores devem decidir a forma em que os dados de entradas serão armazenados e

dispostos. Os programas devem utilizar uma série de instruções para manipular as entradas. Estas

instruções são do tipo simples, condicionais, laços e funções ou sub-rotinas. Os programas devem apresentar os resultados da manipulação dos dados das entradas. Uma aplicação correta incorpora os fundamentos acima listados, expressos através da utilização

de um projeto modular incluindo uma especificação completa, uma codificação devidamente documentada e um esquema de apresentação apropriado.

44..55.. PPrroocceeddiimmeennttoo GGeerraall

Um programa codificado usando uma linguagem de programação, deverá ser transformado no seu equivalente em código de máquina. Dependendo da forma com que isto é feito, existe uma classificação em linguagens compiladas e interpretadas. Nas linguagens interpretadas, cada linha de programa é interpretada e posteriormente executada durante a execução do programa, por meio de um software chamado interpretador. Um exemplo deste tipo de linguagem é o BASIC. As linguagens compiladas são transformadas em equivalentes de linguagem de máquina, antes da execução. Exemplo deste tipo de linguagens são a linguagem C e Pascal. As linguagens compiladas são executadas mais rapidamente do que suas equivalentes interpretadas, com a desvantagem de que as compiladas não possuem verificação de erros em tempo de execução.

O primeiro passo no processo de construção de um programa é a criação dos arquivos de código fonte, com as instruções desejadas[2]. Após isto deve ser invocado o Compilador que antes de efetuar a compilação executa um outro programa chamado Pré-processador que criará a entrada para o compilador. O compilador então efetua a checagem da sintaxe e cria um arquivo objeto que contém código de máquina, diretivas de ligação, seções, referências externas, nome de funções e de dados gerados a partir do código fonte. Finalmente o Linker combina o código objeto com as bibliotecas estáticas utilizadas e outros códigos objeto, define os recursos necessários e cria um arquivo executável. Tipicamente, um arquivo de construção chamado makefile coordena a combinação dos elementos e ferramentas necessárias para a criação do arquivo executável.

44..55..11.. CCoommppiillaaççããoo O compilador é um tipo de software projetado para traduzir os programas escritos

em linguagem de alto nível em instruções de linguagem de máquina elementares para um tipo de computador em particular. Uma linguagem de alto nível particular pode ser utilizada por muitos tipos de computadores independendo do hardware. Por outro lado, os compiladores são projetados para operar num tipo de máquina em particular sendo dependente do hardware.

Os compiladores são basicamente programas de computador capazes de transformar um grupo de símbolos em outro diferente segundo um grupo de regras sintáticas e semânticas bem definidas.

Os compiladores dedicados a microcontroladores, e que são executados em computadores pessoais, são freqüentemente chamados de “Cross Compilers”. A Figura 4-4 mostra o procedimento para a geração de um programa executável.

Page 66: Tratado Da Linguagem c

P R O J E T O D E S I S T E M A S

44444444

4444

Editor

Arquivo Fonte (hello.c)

Makefile

Pré-Processador

Compilador

Arquivos de Cabeçalho (stdio.h)

Arquivo Objeto (hello.obj)

Linker (hello.obj)

Bibliotecas (*.lib)

Arquivo Executável (hello.exe)

Outros Arquivos Objeto (*.obj)

Figura 4-4 – Procedimento de geração de um programa executável

Page 67: Tratado Da Linguagem c

45454545

55.. FFUUNNDDAAMMEENNTTOOSS DDAA LLIINNGGUUAAGGEEMM CC Neste capítulo serão vistos os fundamentos da linguagem C. O conceito de

linguagem de programação, linguagens de alto e baixo nível, linguagens genéricas e especificas. Será visto um pouco do histórico da criação da linguagem e a descrição das características mais importantes da linguagem C. Finalmente, será visto o aspecto geral de um código fonte escrito em C. No Apêndice G - pode ser visto um exemplo de utilização de alguns compiladores tradicionais.

55..11.. CCaarraacctteerrííssttiiccaass ddaa LLiinngguuaaggeemm CC

Entre as principais características da linguagem C, pode-se citar: É uma linguagem de alto nível de sintaxe estruturada e flexível, tornando sua programação

bastante simplificada. Os programas em C são compilados, gerando programas executáveis depois de montados

(linker). A linguagem C compartilha recursos de alto e de baixo nível, pois permite acesso e

programação direta do hardware do computador. Assim, as rotinas cuja dependência de tempo seja crítica, podem ser facilmente implementadas usando instruções em Assembly. Por esta razão a linguagem C, é a preferida dos engenheiros programadores de aplicativos. O C é uma linguagem estruturalmente simples e portável. O compilador C gera códigos

menores e mais velozes do que outras linguagens de programação. Embora estruturalmente simples (poucas funções intrínsecas) a linguagem C não perde

funcionalidade pois permite a inclusão de uma farta quantidade de rotinas do usuário. Os fabricantes de compiladores fornecem uma ampla variedade de rotinas pré-compiladas em bibliotecas.

55..22.. PPoonnttooss PPoossiittiivvooss ddaa LLiinngguuaaggeemm

Tamanho Pequeno: A linguagem C possui poucas regras de sintaxe, quando comparada com outras linguagens. Um compilador C pode ser implementado com apenas 256 KB de memória.

Poucos Comandos: A linguagem C é extremamente pequena. O número total de palavras-chave é de 43. Isto faz dela uma linguagem extremamente simples de aprender.

Velocidade: A combinação de uma linguagem pequena com regras de sintaxe simples, a falta de verificação durante a execução e uma linguagem parecida com o assembly faz que o código gerado seja executado em velocidades próximas do assembler.

Linguagem Estruturada: Contém todas as estruturas de controle utilizadas nas linguagens de programação mais modernas. Têm recursos de escopo utilizando variáveis locais.

Capítulo

5

Page 68: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

46464646

5555

Não Fortemente Figurada: Os dados são tratados de maneira muito flexível, o que permite uma grande versatilidade.

Suporte de Programação Modular: Suporta a compilação e montagem (linker) separadas, o que permite re-compilar somente as partes de um programa que tenham sido alteradas durante o desenvolvimento.

Manipulação de Bits: Uma vez que a linguagem foi criada para a implementação de sistemas operacionais, esta linguagem foi dotada de uma vasta série de operadores para a manipulação direta de bits.

Interface para Rotinas em Assembly: Suporta a inclusão de rotinas em assembly diretamente no mesmo código fonte em C.

Variáveis Ponteiros: Um sistema operacional deve ser capaz de endereçar áreas específicas da memória ou dispositivos de I/O. A linguagem C utiliza variáveis ponteiro permitindo manipulá-los aritmeticamente. Uma variável ponteiro guarda no seu conteúdo uma informação de endereço da informação.

Estruturas Flexíveis: Os arranjos de dados são unidimensionais. Os arranjos multidimensionais são construídos a partir de arranjos unidimensionais.

Bibliotecas de Funções: Existem vastas bibliotecas de funções prontas que podem ser anexadas aos executáveis durante a montagem (linker).

Uso Eficiente da Memória: Os programas em C tendem a ser mais eficientes em termos de memória devido à falta de funções embutidas que não são necessárias à aplicação.

Portabilidade: A portabilidade indica a facilidade de se converter um programa feito para um hardware específico e sistema operacional em um equivalente que possa ser executado em outro hardware ou sistema operacional. Atualmente ainda pode ser considerada como uma das linguagens mais portáveis.

55..33.. PPoonnttooss NNeeggaattiivvooss

Não fortemente Figurada: Este fato é também um ponto negativo da linguagem. Trata-se do processamento dos dados de acordo com a sua natureza ou tipo de dado. Este processamento é chamado de tipagem que indica o quanto a linguagem permite a troca de dados entre duas variáveis de tipos diferentes. O uso acidental de misturas de tipos de dados pode gerar erros de execução no programa.

Verificação em Tempo de Execução: A linguagem C não possui verificação em tempo de execução, de forma que podem acontecer problemas cujas origens são muito difíceis de detectar.

Page 69: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

47474747

5555

55..44.. OO PPaaddrrããoo AANNSSII CC

O comitê ANSI1 desenvolveu padrões para a linguagem C. Anteriormente a única referência da linguagem era o livro The C Programming Language (V. Kernighan e D. Ritchie, Laboratórios Bell, 1988). Este livro não é muito específico em certos detalhes da linguagem, o que levou a divergências entre os fabricantes de compiladores, prejudicando a portabilidade. O padrão ANSI surgiu para remover ambigüidades, embora nem todas tenham sido corrigidas, permanece como a melhor alternativa para produzir um código C portátil.

O comitê ANSI adotou como norma várias frases que foram denominadas “o espírito do C”, dentre elas: “Não impeça que o programador faça aquilo que precisa ser feito”. “Confie no programador”. “Mantenha a linguagem pequena e simples”.

Além disso, a comunidade internacional foi consultada para garantir que o padrão ANSI C americano seria idêntico à versão do padrão ISO (International Standards Organization)

55..55.. PPaallaavvrraass RReesseerrvvaaddaass ddaa LLiinngguuaaggeemm CC

Todas as linguagens de programação têm palavras reservadas. As palavras reservadas não podem ser usadas a não ser nos seus propósitos originais, i.e., não podem ser declaradas funções ou variáveis com os mesmos nomes. Como a linguagem C é "case sensitive" (sensível a maiúsculas e minúsculas) podemos declarar uma variável For, apesar de haver uma palavra reservada for, mas isto não é recomendável pois gera confusão no código.

A seguir são listadas as palavras reservadas do ANSI C, ao todo 32 palavras. Veremos o significado destas palavras chave à medida que os demais conceitos forem apresentados.

auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned

continue for signed void default goto sizeof volatile

do if static while

55..66.. EEssttrruuttuurraa ddee uumm PPrrooggrraammaa eemm CC

Um programa em C é constituído de: um cabeçalho contendo as diretivas de compilador onde se definem o valor de constantes

simbólicas, declaração de variáveis e funções, inclusão de bibliotecas, macros, etc.; um bloco de instruções chamado de função principal (main) e outros blocos de funções

secundárias; comentários do programa que constituem a documentação in situ.

1 ANSI: American National Standards Institute. – www.ansi.org.

Page 70: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

48484848

5555

Programa Exemplo: O paralel.c é um programa que calcula a resistência equivalente de um par de resistores conectado em paralelo. #include <stdio.h> /* Biblioteca padrão de I/O (ex. video e teclado) */ void main(void) /* funcao principal */ float RA; /* Declaração da variável para a Resistência A */ float RB; /* Declaração da variável para a Resistência B */ float Req; printf("Programa que calcula o equivalente de duas resistências"); printf(" conectadas em paralelo"); printf("\n"); /* pula uma linha do monitor de video */ printf("Entre com o valor de RA:"); /* imprime a string no vídeo */ scanf("%f",&RA); /* espera o valor do teclado */ printf("\n"); /* pula uma linha do monitor de video */ printf("Entre com o valor de RB:"); /* imprime a string no vídeo */ scanf("%f",&RB); /* espera o valor do teclado */ Req = (RA * RB)/(RA + RB); /* Calcula a resistencia equivalente */ printf("\n"); /* pula uma linha do monitor de video */ printf("A Resistencia Equivalente para RA//RB = "); printf("%f",Req); /* Fim da função principal e do programa */

Código 5-1

55..77.. CCoonnjjuunnttoo ddee ccaarraacctteerreess

Um programa fonte em C é um texto não formatado escrito utilizando um software editor de textos usando um conjunto padrão de caracteres ASCII. A seguir estão os caracteres válidos utilizados em C:

Caracteres válidos: a b c d e f g h i j k l m n o p q r s t u v w x y z A B C D E F G H I J K L M N O P Q R S T U V W X Y Z 1 2 3 4 5 6 7 8 9 0 + - * / \ = | & ! ? # % ( ) [ ] _ ‘ “ . , : < >

Exemplos de caracteres inválidos: @ $ ¨ á é õ ç

Os caracteres acima são válidos apenas em strings. Maiores detalhes serão tratados no capítulo

55..88.. CCoommeennttáárriiooss

Em C, comentários do programa, podem ser escritos em qualquer lugar do texto para facilitar a interpretação do algoritmo. Para que um comentário seja identificado como tal, ele deve ter um conjunto de símbolos /* antes do comentário, e um outro conjunto */ depois do mesmo. Observe que no exemplo paralel.c.

Exemplo: /* esta e uma linha de comentário em C */

Observação: O C++ permite que comentários sejam escritos de outra forma: colocando duas barras (//) precedendo o comentário de uma linha. O compilador entenderá que tudo que estiver a direita do símbolo composto é um comentário. Alguns

Page 71: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

49494949

5555

compiladores C aceitam algumas regras do C++, como por exemplo, definir comentários com dupla barra.

Alguns compiladores aceitam colocar caracteres acentuados no meio dos comentários, outros não.

Exemplo: // este e’ um comentário valido para todos os compiladores C++ // e alguns compiladores C mais novos

O uso de comentários torna o código do programa mais legível de ser entendido. Os comentários do C devem começar com o símbolo composto /* e terminar com */. A linguagem C padrão não permite comentários aninhados (um dentro do outro), até porque seria incoerente, mas existem alguns compiladores que os aceitam sem gerar erros de sintaxe.

55..99.. DDiirreettiivvaass ddee CCoommppiillaaççããoo22

Em C, existem comandos que são processados antes da compilação do programa. Estes comandos são genericamente chamados de diretivas de compilação e servem para informar ao compilador, quais são as constantes simbólicas usadas no programa e quais bibliotecas devem ser anexadas ao programa executável entre outras funções.

A diretiva #include instrui ao compilador para incluir na compilação do programa o conteúdo de outros arquivos. Normalmente estes arquivos contem declarações de funções da biblioteca ou rotinas do usuário.

A diretiva #define diz ao compilador quais são as constantes simbólicas usadas no programa código fonte.

55..1100.. DDeeccllaarraaççããoo ddee VVaarriiáávveeiiss33

Em C, como na maioria das linguagens, as variáveis devem ser declaradas antes de serem utilizadas. Existem dois tipos de variáveis de acordo com o escopo em que estas podem ser acessadas: as variáveis globais e as locais. Variáveis globais devem ser declaradas no início do programa e fora de qualquer função. As variáveis locais devem ser declaradas no inicio da função onde elas serão válidas.

As variáveis podem ser de vários tipos: int (inteiras), float (real de simples precisão) e outras que serão vistas no capítulo a seguir. No exemplo acima Req, RA e RB são declaradas como variáveis float (reais).

Os nomes das variáveis indicam o endereço de memória onde está um determinado dado. Cabe ressaltar que os nomes das variáveis não são armazenados no código executável, e que elas fazem sentido só para o instante da compilação e linker. Isto denota que não pode ser interpretado um código fonte a partir de um arquivo executável.

2 Este tema será tratado em mais detalhes no capítulo 14. 3 Este tema será tratado em mais detalhes no capítulo 8.

Page 72: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

50505050

5555

55..1111.. UUmmaa IInnttrroodduuççããoo ààss EEnnttrraaddaa ee SSaaííddaa ddee DDaaddooss44

Em C existem várias maneiras de fazer a leitura e escrita de informações. Estas operações são chamadas de operações de entrada e saída, ou simplesmente operações de I/O. No capítulo a seguir serão vistas algumas funções padronizadas. Um exemplo típico de dispositivo de saída é o monitor de vídeo, e um dispositivo de entrada, o teclado. Existem também funções padronizadas de I/O em arquivos, portas seriais e paralelas. Um exemplo de função padronizada é a função printf que é uma rotina de envio de dados formatados, utilizada para enviar caracteres ASCII para a placa de vídeo, para o canal serial, ou para qualquer função definida na sua chamada. A função scanf é uma função padronizada de leitura formatada de caracteres ASCII de um dispositivo de entrada de dados, tipicamente o teclado ou um canal serial.

55..1111..11.. CCaarraacctteerreess Os caracteres são um tipo de dado: o char. As variáveis char são armazenadas em

memórias de um byte. Os inteiros (int) podem possuir um número maior de bytes. Dependendo da implementação do compilador e do microprocessador alvo, eles podem ter 1 byte (8 bits), 2 bytes (16 bits), 4 bytes (32 bits) ou mais.

De forma geral também se pode usar uma variável do tipo char para armazenar valores numéricos inteiros de 8 bits.

Para indicar um caractere de texto devem ser usadas apóstrofes. No exemplo a seguir pode-se visualizar o uso de variáveis do tipo char. #include <stdio.h> void main (void) char car; car = 'D'; /* car armazena o equivalente em ASCII da letra D */ printf ("%c",car);

Código 5-2

No programa anterior, %c indica à função printf() que deverá enviar um caractere para o dispositivo de saída.

Como foi visto antes, uma variável char também pode ser usada para armazenar um número inteiro de oito bits. Observar o seguinte programa. #include <stdio.h> void main (void) char car; car = 'D'; printf ("%d",car); /* Imprime o caractere como inteiro */

Código 5-3

Este programa colocará o número 68 no dispositivo de saída (memória da placa de vídeo ou canal serial, por exemplo), que é o código ASCII correspondente ao caractere 'D'.

Algumas vezes é necessário capturar um caractere único fornecido pelo usuário do sistema ou por um dispositivo de entrada qualquer. Para efetuar esta tarefa existem duas funções na biblioteca chamadas getch() e getche(). Ambas retornam o caractere

4 Este tema será tratado em mais detalhes no capítulo 15.

Page 73: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

51515151

5555

pressionado, se for num teclado, ou um caractere que chegou pelo canal serial, em várias aplicações de microcontroladores. A função getche() imprime o caractere na tela antes de retorná-lo (num compilador para PC) e getch() apenas retorna o caractere pressionado sem imprimi-lo na tela. Ambas as funções são declaradas no arquivo de cabeçalho conio.h. Geralmente estas funções não estão disponíveis em ambiente Unix (compiladores cc e gcc) e podem ser substituídas pela função scanf(), porém sem a mesma funcionalidade. A seguir um exemplo que usa a função getch(), e seu correspondente em ambiente Unix: #include <stdio.h> #include <conio.h> void main (void) char car; car=getch(); printf ("Foi pressionada a tecla %c",car);

Equivalente para o ambiente Unix do programa acima, sem usar getch(): #include <stdio.h> void main (void) char Ch; scanf("%c",&Ch); printf ("Foi pressionada a tecla %c",Ch);

Código 5-4

Na maioria dos compiladores C para 8051, PIC, Motorola, etc., as funções scanf e printf utilizam o canal serial (ou um par de pinos de I/O digital) como dispositivo padrão para a entrada e saída de dados. Os compiladores para PC normalmente utilizam o teclado e o monitor de vídeo como dispositivo padrão.

A principal diferença da versão que utiliza getch() para a versão que não a utiliza é que no primeiro caso, o usuário simplesmente seleciona a tecla e o sistema a lê diretamente do buffer de teclado (compiladores para PCs). No segundo caso, é necessário pressionar também a tecla <ENTER>.

55..1111..22.. AAss SSttrriinnggss55 Para a linguagem C, uma string é definida como sendo um conjunto de caracteres

terminado com um caractere nulo (00h). O caractere nulo é um caractere com valor inteiro igual a zero (código ASCII igual a 0). O terminador nulo também pode ser representado usando a convenção de barra invertida como sendo '\0'. Embora uma string seja um vetor de variáveis do tipo char, e que o assunto vetores será discutido posteriormente, serão vistos nesta seção os fundamentos necessários para que possam ser utilizadas estas cadeias de caracteres. Para declarar uma string pode-se utilizar o seguinte formato geral:

char identificador-da-string[tamanho];

A expressão acima declara um vetor de caracteres (uma string) com número de posições igual a tamanho. Observar que devido ao terminador nulo, deve-se declarar o comprimento da string como sendo, no mínimo, um caractere maior que a maior string que se pretende armazenar. Supondo que seja declarada uma string de 7 posições e colocando a palavra Diodo nela, tem-se na memória:

‘D’ ‘i’ ‘o’ ‘d’ ‘o’ ‘\0’ ...

5 Este tema será tratado em mais detalhes no capítulo 11.

Page 74: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

52525252

5555

No caso acima, as células de memória posteriores não utilizadas conterão valores indeterminados (usualmente chamado de lixo6 de memória pelos programadores). Isto acontece porque a linguagem C não inicializa automaticamente as suas variáveis, cabendo ao programador esta tarefa caso seja necessária. Caso seja necessária a leitura de uma string fornecida por um sistema ou pelo usuário através do teclado, pode ser usada a função gets(). Um exemplo do uso desta função é apresentado abaixo. A função gets() coloca o terminador nulo na string, quando no final da mesma aparece o equivalente em ASCII da tecla <ENTER>.

Uma vantagem da representação de uma string pela finalização de um caractere nulo, é que esta não precisa ter um tamanho pré-definido, e em operações de leitura, a string pode ser lida caractere a caractere até o terminador nulo. #include <stdio.h> void main (void) char string[100]; printf ("Digitar uma string: "); gets(string); printf ("\n Foi digitada %s",string);

Código 5-5

Neste programa, o tamanho máximo da string que pode ser inserida é 99 caracteres. Caso a string inserida tiver um tamanho maior poderá levar a resultados desastrosos.

Como as strings são basicamente vetores de caracteres, para se acessar um determinado caractere, basta utilizar um índice relacionado ao caractere desejado dentro da string. Supondo uma string chamada str pode-se acessar o segundo caractere (‘t’) de str da seguinte forma: str[1] = 'a';

Tanto na linguagem C como no hardware, a primeira posição de um vetor é a posição zero. Desta forma, o primeiro caractere de uma string estará na posição 0 do vetor; a segunda letra na posição 1 e assim sucessivamente. Segue um exemplo que imprimirá o segundo caractere da string "Diodo". Em seguida, o programa troca o caractere e apresenta a string resultante. #include <stdio.h> void main(void) char str[10] = "Diodo"; printf("\n String: %s", str); printf("\n Segundo caractere: %c", str[1]); str[1] = 'U'; /*Troca o caractere ‘i’ por ‘U’ */ printf("\n Agora o segundo caractere é: %c", str[1]);

printf("\n A string resultante: %s", str);

Código 5-6

Nesta string, o terminador nulo está na posição número cinco. Das posições 0 a 5, sabe-se que existem caracteres válidos, e portanto podem ser impressos. Deve-se notar a forma como a string str foi inicializada com os caracteres ‘D’ ‘i’ ‘o’ ‘d’ ‘o’ e ‘\0’ simplesmente declarando char str[10] = "Diodo".

No programa acima, o símbolo %s indica à função printf() que deve colocar uma string no dispositivo de saída. A continuação será feita uma abordagem inicial mais detalhada sobre as duas funções que já têm sido utilizadas para implementar entradas e saídas de dados. 6 Na gíria de programação, a palavra lixo de memória, significa um conjunto de valores desconexos e sem sentido.

Page 75: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

53535353

5555

55..1111..33.. pprriinnttff A função printf() tem a seguinte forma geral:

printf (string-de-controle,lista-de-argumentos);

A string de controle serve como modelo do que vai ser enviado para o dispositivo de saída, por exemplo a placa de vídeo. A string de controle contém os caracteres que devem ser colocados no dispositivo de saída e os espaços reservados para os valores das variáveis e suas respectivas posições. A colocação de conteúdos de variáveis é feita usando-se os códigos de controle, que usam a uma notação especial começando com o caractere %. Na string de controle podem ser indicadas quais variáveis terão o conteúdo enviado, de que tipo e ainda em que posição serão enviadas. Para cada código de controle deve-se ter um argumento na lista de argumentos. Na tabela abaixo estão apresentados alguns códigos:

Código Significado %d Inteiro %f Float %c Caractere %s String %x Hexadecimal %% Coloca na tela um %

Mais abaixo são mostrados alguns exemplos de uso da função printf() e o que eles exibem: printf ("\n Teste %% %%"); printf ("\n %f",40.345); printf ("\n Um caractere %c e um inteiro %d",'D',120); printf ("\n %s e um exemplo","Este"); printf ("\n %s%d%%","Tolerância de ",10);

Resultado: Teste % % 40.345 Um caractere D e um inteiro 120 Este e um exemplo Tolerância de 10%

Maiores detalhes sobre a função printf() incluindo demais códigos de controle serão vistos posteriormente.

55..1111..44.. ssccaannff O formato geral da função scanf() é:

scanf (string-de-controle,lista-de-argumentos);

Usando a função scanf() pode-se capturar dados de um dispositivo de entrada (canal serial ou teclado, por exemplo). O número de argumentos deve ser igual ao de códigos de controle na string de controle. É importante lembrar o uso do símbolo & antes das variáveis da lista de argumentos (endereço da variável). Maiores detalhes sobre a função scanf() serão vistos posteriormente.

Uma desvantagem do uso desta função é que o sistema pára na função até que seja inserido um caractere equivalente ao <ENTER>. Na maioria das aplicações de engenharia

Page 76: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

54545454

5555

este tipo de comportamento não pode ser tolerado e os dados devem ser capturados utilizando interrupções. O uso de interrupções será tratado em outros capítulos.

55..1122.. UUmmaa IInnttrroodduuççããoo aaooss CCoommaannddooss ddee CCoonnttrroollee ddee FFlluuxxoo77

A linguagem C permite uma ampla variedade de estruturas de controle de fluxo de processamento. Estas estruturas serão vistas em detalhes nos capítulos seguintes. Existem duas estruturas básicas (decisão e iteração) que são similares às estruturas usadas nas pseudolinguagens algorítmicas:

Estrutura de Tomada de Decisão: Permite direcionar o fluxo lógico para dois blocos distintos de instruções, de acordo com uma condição de controle. Pseudolinguagem Linguagem C se condição então bloco 1 senão bloco 2 fim se

if(condição) bloco 1; else bloco 2; ;

Estrutura de Iteração ou Repetição: Permite executar repetidamente um bloco de instruções até que a condição de controle seja satisfeita. Pseudolinguagem Linguagem C faça bloco enquanto condição

do bloco; while(condição);

A linguagem C é sensível a maiúsculas e minúsculas (case sensitive), assim se for declarada uma variável chamada soma, a mesma será interpretada de forma diferente de outras que forem declaradas com nomes parecidos, tais como Soma, SOMA, SoMa ou sOmA. Da mesma forma, os comandos (palavras chave ou instruções), if e for, por exemplo, só poderão ser escritos com letras minúsculas, caso contrário, não serão interpretados como comandos, e sim como identificadores.

Os comandos de controle de fluxo permitem ao programador alterar a seqüência de execução de um programa. Nesta seção será feita uma breve introdução a dois comandos de controle de fluxo mais utilizados. Outros comandos serão estudados posteriormente.

55..1122..11.. iiff O comando if representa uma tomada de decisão do tipo "SE (isto for verdadeiro)

ENTÃO (execute o seguinte...)". A forma geral do comando é:

if (condição) instrução;

A condição do comando if é uma expressão que será avaliada como sendo verdadeira ou falsa. Em C, uma expressão é falsa se o seu valor é igual a zero. Se o resultado for zero a declaração não será executada. Se o resultado for qualquer coisa diferente de zero a instrução será executada.

7 Este tema será tratado em mais detalhes no capítulo 10.

Page 77: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

55555555

5555

A instrução pode ser um bloco de código ou apenas um comando. É interessante notar que, no caso da declaração ser um bloco de código, não é necessário o uso do símbolo ‘;’ no final do bloco. Isto é uma regra geral para blocos de código. Abaixo é mostrado um exemplo de utilização. #include <stdio.h> void main (void) int res; printf ("\nDigite o valor do resistor: "); scanf("%d",&res); if (res>100) printf ("\nO resistor é maior que 100 ohms"); if (res==100) printf ("\n\nO resistor é de 100 ohms\n"); printf ("O numero e igual a 100."); if (res<100) printf ("\n\nO resistor tem valor menor que 100 ohms");

Código 5-7

No programa acima a expressão res>100 é previamente avaliada e retornará um valor diferente de zero se verdadeira, ou zero se for falsa. Deve ser observada a utilização do símbolo composto ‘==’ dentro da condição do segundo if. Este é um operador de comparação cujo resultado é verdadeiro, se ambos operandos forem iguais, ou zero se forem diferentes. O símbolo simples ‘=’ é um operador de atribuição, onde o operando esquerdo recebe uma cópia de um valor relacionado como o operando colocado à direita. Caso for colocada a seguinte condição: if (res = 10) ... /* Isto não é uma comparação */

Neste caso o compilador gerará um código que atribuirá a quantidade 10 à variável res e a expressão res = 10 retornará 10 (diferente de zero, portanto verdadeiro), fazendo com que a declaração fosse executada sempre. Este problema gera erros lógicos freqüentes mas geralmente fáceis de serem detectados durante a depuração.

Deve-se evitar efetuar uma comparação de igualdade entre dois números em ponto flutuante. Observar o seguinte exemplo. #include<stdio.h> void main(void) float pi = 3.1415926536; if( pi == 3.1415926506 ) printf(“Os valores são diferentes, mas foram considerados iguais ”); printf(“por causa da precisão”);

Código 5-8

Os operadores de comparação ou relacionais são: == igual != diferente de > maior que < menor que >= maior ou igual <= menor ou igual

55..1122..22.. ffoorr O comando for é utilizado para repetir a execução de um comando, ou bloco de

comandos, de forma iterativa. O uso deste comando é essencial para executar tarefas de cálculo numérico.

Page 78: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

56565656

5555

Sua forma de utilização é:

for (inicialização ; condição ; incremento) instrução;

A instrução no comando for também pode ser um bloco ‘’ e neste caso o ; pode ser omitido. O algoritmo equivalente de funcionamento pode ser colocado como: inicialização; se (condição for verdadeira)

instrução; incremento; "Voltar para o comando se e comparar novamente a condição"

“continuar daqui para frente quando a condição for falsa” ...

Pode-se observar que o for executará a inicialização incondicionalmente e testará a condição. Se a condição for falsa ele não faz mais nada e o programa continua. Se a condição for verdadeira ele executará a declaração, incrementará uma variável e voltará novamente a testar a condição. A declaração será executada em laços (loops) até que a condição imposta dê um valor falso. A seguir é mostrado um programa que coloca os primeiros 100 números num dispositivo de saída: #include <stdio.h> void main (void) int count; for (count=0 ; count<100 ; count = count + 1) printf ("\n%d ",count);

Código 5-9

Outro exemplo interessante é mostrado a seguir: o programa lê uma string e conta quantos dos caracteres desta string são iguais à letra 'c' . Esta é uma tarefa comum quando se trabalha com drivers de comunicação com protocolos em ASCII. Este exemplo utiliza a função gets()8 que recebe uma string como parâmetro, armazenando-a no endereço de memória indicado pelo argumento. #include <stdio.h> void main (void) char string[101]; /* string de 100 caracteres */ int i, cont; printf("\n\n Digite uma frase de código: "); gets(string); /* Le a string */ printf("\n\n Frase digitada:\n%s", string); cont = 0; for (i = 0 ; string[i] != '\0' ; i = i + 1) if (string[i] == 'c' ) /* Se for a letra 'c' */ cont = cont + 1; /* Incrementa o contador de caracteres */ printf("\nNumero de caracteres iguais a ‘c’ = %d", cont);

Código 5-10

Deve ser notado que na condição imposta à instrução for, o caractere armazenado em string[i] é comparado com '\0' (final da string). Caso o caractere seja diferente de '\0', a condição é verdadeira e o bloco de instruções da declaração será executado. Dentro do bloco existe um comando if que testa se o caractere é igual a 'c'. Caso esta condição seja verdadeira, o contador de caracteres c (cont) será incrementado.

8 “Get string...”

Page 79: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

57575757

5555

55..1133.. UUmmaa BBrreevvee IInnttrroodduuççããoo ààss FFuunnççõõeess

Uma função é um bloco de código de programa que pode ser usado diversas vezes em sua execução. O uso de funções permite que o programa torne-se estruturado. Um programa em C consiste basicamente de uma coleção de funções, onde umas chamam as outras para obter um determinado resultado ou para efetuar alguma tarefa específica.

Exemplo de função: #include <stdio.h> void ImprimeMensagem (void); /* Declaração da função criada ImprimeMensagem */ void main (void) ImprimeMensagem(); /* chamada da função*/ /* Função que imprime uma mensagem */ void ImprimeMensagem (void) /* Definição da função */ printf ("Filtro Passa-Baixas");

Código 5-11

Este programa terá o mesmo resultado que o primeiro exemplo da seção anterior. O que ele faz é definir uma função ImprimeMensagem() que coloca uma string na tela e não retorna nada (void). Em seguida, esta função é chamada a partir de main() (que também é uma função).

Da mesma maneira que as variáveis, as funções devem ser declaradas antes de serem utilizadas na seqüência do código fonte. Isto é feito colocando o tipo de retorno da função, o nome e os seus tipos de parâmetros entre parênteses e separados por vírgulas (quando existirem), finalizando com o símbolo ‘;’.

Assim como nas variáveis, os nomes das funções armazenam o endereço de memória onde existe a primeira instrução de execução de um bloco de instruções delimitado no código fonte pelos símbolos ‘’.

55..1133..11.. OOss AArrgguummeennttooss99 Argumentos são as informações que uma função pode receber. É através dos

argumentos que são repassados os parâmetros para a função poder executar algum tipo de tarefa. Nos exemplos anteriores já foram mostradas algumas funções com argumentos, tais como printf() e scanf(). A seguir é mostrado um exemplo de uma função que calcula o e imprime o quadrado de um valor enviado como parâmetro: #include <stdio.h> void ImprimeQuadrado (int); /* Declaração da função que calcula e imprime o quadrado de um valor passado como parâmetro */ void main () int num; printf ("Entre com um numero: "); scanf ("%d",&num); ImprimeQuadrado(num); /* Função que calcula e imprime o quadrado de um valor passado como parâmetro */ void ImprimeQuadrado (int x) int quadrado; quadrado = x * x;

printf ("\n O quadrado de %d = %d",x,quadrado);

9 Este tema será tratado em mais detalhes no capítulo 13.

Page 80: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

58585858

5555

Código 5-12

Na definição de ImprimeQuadrado() é especificado que a função deverá receber um argumento inteiro. Quando acontece a chamada à função, o inteiro num é passado como argumento, através da cópia da variável numa área específica da memória de dados, normalmente denominada de pilha ou stack. Neste caso particular, o conteúdo da variável num é copiado para a pilha, e após o salvamento do conteúdo dos registradores internos da CPU e o salto da execução para a função chamada, será novamente copiada numa nova posição da memória de dados identificada pelo nome da variável local x, neste caso.

Alguns pontos devem ser observados: em primeiro lugar deve-se satisfazer os requisitos da função quanto ao tipo e à quantidade de argumentos de chamada. Apesar de existirem algumas conversões de tipo, que o C faz automaticamente, é importante ficar atento. Em segundo lugar, não é importante o nome da variável que se passa como argumento, ou seja, a variável num, ao ser passada como argumento para ImprimeQuadrado() é copiada para a variável x. Dentro da função ImprimeQuadrado() trabalha-se apenas com x, já que num é uma variável local da função main() e portanto não pode ser acessada pela função ImprimeQuadrado(). Se mudarmos o valor da variável x dentro da função ImprimeQuadrado(), o valor de num na função main() permanece inalterado.

A seguir, é mostrado o uso de uma função, que possui mais de um parâmetro. Deve-se observar que os parâmetros do argumento devem ser separados por vírgula, sendo que cada um deve ter um tipo explicitamente declarado. Deve-se notar também que os argumentos passados para a função não precisam necessariamente ser variáveis porque mesmo sendo constantes serão copiados para a pilha, e daí para a variável de entrada da função. #include <stdio.h> void multiplica (float a, float b, float c); /* Declaração da função que multiplica 3 números */ void main (void) float x,y; x=23.5; y=12.9; multiplica(x,y,3.87); void multiplica(float a, float b,float c) /* Multiplica 3 números */ printf ("%f",a*b*c);

Código 5-13

A função multiplica(x,y,3.87) recebe três parâmetros. Observar o seguinte código: #include <stdio.h> void multiplica (float a, float b, float c); /* Declaração da função que multiplica 3 números */ void main () float a=2.0,b=3.5,c=4.3; multiplica(a,b,c); printf("main.a = %f main.b = %f main.c = %f",a,b,c); void multiplica(float a, float b,float c) /* Multiplica 3 números */ printf ("a*b*c = %f \n",a*b*c); a = 0.0; b = 0.0; c = 0.0; printf("mult.a = %f mult.b = %f mult.c = %f \n",a,b,c);

Page 81: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

59595959

5555

Código 5-14

O exemplo anterior mostra que as funções locais podem ter o mesmo nome mas terão endereços de memória diferentes, i.e., a variável a da função main() (main.a) tem um endereço de memória diferente da variável a da função mult() (mult.a).

55..1133..22.. OO RReettoorrnnoo ddee VVaalloorreess Por definição, uma função deve retornar um valor. As funções vistas anteriormente

não retornam nada, pois foi especificado um retorno void, assim elas somente servem para executar uma tarefa.

Para retornar um valor (diferente de void), deve-se especificar o tipo antes do nome da função. Para executar o retorno de um valor deve-se usar a função pré-definida chamada return(). No exemplo a seguir foi definida uma função que calcula e retorna o quadrado de uma variável ou número passado como parâmetro. #include <stdio.h> int CalculaQuadrado (int x); /* Declaração da função que calcula x^2 */ void main () int num; int res; printf ("Entre com um numero: "); scanf ("%d",&num); res = CalculaQuadrado(num); printf ("\n O quadrado de %d = %d",num,res); /* Função que calcula e retorna o quadrado de um valor passado como parâmetro */ int CalculaQuadrado (int x) /* Calcula o quadrado de x */ int quadrado; quadrado = x * x; return (quadrado);

Código 5-15

Deve-se observar que a função CalculaQuadrado() retornará a cópia do valor armazenado temporariamente na variável local quadrado para qualquer função que a chame. No programa é feita a atribuição do resultado à variável res, que posteriormente foi enviada a um dispositivo de saída através da função printf(). Caso o retorno não seja especificado, o tipo de retorno default para uma função é o tipo inteiro. Porém, não é uma boa prática não se especificar o valor de retorno.

Mais um exemplo de função, que agora recebe dois float e retorna um valor float. Deve-se observar que neste exemplo foi especificado um valor de retorno para a função main(int), retornando zero pelo programa. Normalmente é isto que se faz com a função main, que retornará zero quando tiver sido executada sem qualquer tipo de erro. #include <stdio.h> float prod (float x,float y); int main (void) float saida; saida = prod(45.2,0.0067); printf ("A saida e: %f\n",saida); return(0); float prod (float x,float y) return (x*y);

Código 5-16

A forma geral de uma função:

Page 82: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

60606060

5555

tipo-de-retorno identificador-da-função (lista-de-argumentos) código-da-função

55..1144.. PPrriimmeeiirrooss PPaassssooss

Veja o menor programa em C que pode gerar um executável. main()

Código 5-17

É um código fonte que não executa absolutamente nada. Ao compilar este programa provavelmente aparecerá uma mensagem de aviso10: Compiling... main.c d:\FONTES\Nada\main.c(1) : warning C4035: 'main' : no return value Linking... MainProg.exe - 0 error(s), 1 warning(s)

Os warnings indicam possíveis erros lógicos na implementação, mas não é necessariamente um erro de programa. Um programa profissional não deverá conter nenhum warning. O warning deste exemplo, aparece do fato de que na linguagem C qualquer função deve retornar algum valor. Se o tipo de retorno não for definido explicitamente, o default é o retorno de um valor inteiro. Todo programa em C possui pelo menos uma função, chamada de main (principal), e qualquer programa começa e termina nesta função.

A estrutura de um programa em C é formado por funções, onde a primeira função a ser chamada é a função main, e a partir desta são chamadas as outras componentes do código, que na sua vez podem chamar outras, e assim sucessivamente, executando tarefas específicas e retornando valores para a função que a ativou. O programa finaliza na função main. Na Figura 5-1 é ilustrada a estruturação de um programa C em relação às funções componentes.

10 As mensagens de aviso são identificadas em inglês pela palavra warning.

Page 83: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

61616161

5555

/* Inicio do programa */void main() x = func1(); .... .... .... func2(); ... ... ... ... func3() ... ... ... ... ... /* fim do programa */

func1() a = func4(); b = func5(); c = func6(); .... return(d)

func2() func3();

func3()

func4() return(xx);

func5()

func6()

Atendimento aInterrupção, se

tiver sidohabilitada noprograma

IRQ

Figura 5-1 – Estruturação de um programa em C

Voltando ao exemplo anterior, para remover o warning pode-se modificar o código fonte para: main() return (0);

Código 5-18

Obtendo do compilador Compiling... main.c Linking... MainProg.exe - 0 error(s), 0 warning(s)

Aqui foi forçado o retorno de um valor através de uma outra função padronizada chamada return. Usualmente o valor de retorno da função main pode ser usado para indicar o status da saída, retornando códigos para erros ou eventos que aconteceram durante a execução, por exemplo. Uma outra solução é a de indicar de forma explícita que a função não retorna nada. O nada em C é chamado de void (vazio). void main()

Código 5-19

Compilando obtém-se: Compiling... main.c Linking... MainProg.exe - 0 error(s), 0 warning(s)

Seria por exemplo uma incoerência definir a função main como retornando void e ainda utilizar a função return; void main() return(0);

Page 84: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

62626262

5555

Código 5-20

Alguns compiladores mostram incoerências como estas na forma de avisos (warnings). Compiling... main.c d:\FONTES\Nada\main.c(2) : warning C4098: 'main' : 'void' function returning a value Linking... MainProg.exe - 0 error(s), 1 warning(s)

É interessante notar que o símbolo “;” indica fim de instrução ou de conjunto de instruções e deve ser obrigatoriamente colocado no final de cada instrução exceto quando são utilizados delimitadores de bloco “”. É importante também notar que os compiladores C ignoram os espaços ou linhas deixadas em branco, ou tabulações entre operandos.

Os símbolos “” delimitam um bloco de instruções, e os símbolos “( )” delimitam operações lógicas e aritméticas, definindo prioridades, e nas funções delimitam os parâmetros a serem enviados para as mesmas.

Considere o programa que calcula a variação da resistência elétrica de um componente, com a variação da temperatura, segundo a seguinte regra:

R = Ro [1 + α(T - To)] Ω

onde Ro = 1KΩ; To = 25 Celsius; T = 75 Celsius; α = 0.0035 Ω.Celsius-1 void main(void) float Ro = 1000.0; float R; float alfa = 0.0035; float To = 25.0; float T = 75.0; R = Ro * (1 + alfa * (T - To) );

Código 5-21

Este programa efetua um cálculo aritmético com valores em ponto flutuante. Os dados de entrada para o cálculo estão armazenados na memória, e o resultado de saída também será armazenado na memória. Estes dados são válidos enquanto o programa estiver em execução (alguns microssegundos neste programa). Pode-se perceber a necessidade de ter um outro meio de armazenamento, que comporte os dados quando o programa não estiver mais em execução. Uma forma de armazenar os dados seria apresentando-os na tela do monitor de vídeo de forma que possam ser visualizados e memorizados; uma outra alternativa é a do armazenamento em meio magnético, por exemplo.

Observar abaixo o código em assembly gerado a partir do código C pelo Microsoft Visual C++. 1: void main(void) 00401010 push ebp 00401011 mov ebp,esp 00401013 sub esp,14h 2: float Ro = 1000.0; 00401016 mov dword ptr [Ro],447A0000h 3: float R; 4: float alfa = 0.0035; 0040101D mov dword ptr [alfa],3B656042h 5: float To = 25.0;

Page 85: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

63636363

5555

00401024 mov dword ptr [To],41C80000h 6: float T = 75.0; 0040102B mov dword ptr [T],42960000h 7: 8: R = Ro * (1 + alfa * (T - To) ); 00401032 fld dword ptr [T] 00401035 fsub dword ptr [To] 00401038 fmul dword ptr [alfa] 0040103B fadd dword ptr 00401041 fmul dword ptr [Ro] 00401044 fstp dword ptr [R] 9: 00401047 mov esp,ebp 00401049 pop ebp 0040104A ret

Código 5-22

Observe a linha de assembly abaixo da instrução C identificada com o símbolo 2:, 00401016 mov dword ptr [Ro],447A0000h

O primeiro número que aparece é o endereço de memória de programa que foi alocado para a instrução. O símbolo mov é uma instrução em assembly para a família 8x86 que copia dados ou constantes de um lugar da memória para outra. A instrução assembly precisa dos parâmetros de origem e destino. Nesta linha a origem é o número 447A0000h que é a representação do valor 1000 em ponto flutuante, e o destino é a posição apontada por um apontador chamado ptr com offset de Ro unidades de memória, como sendo o índice de um vetor. Na realidade Ro em si não armazena um valor, mas o offset de endereços de memória onde está armazenado o valor.

Nesse exemplo pode-se observar também a ordem com que são executadas as operações aritméticas de uma expressão complexa. Como primeiro passo, é efetuada a subtração entre T e To (fsub), a seguir a multiplicação do resultado parcial vezes alfa (fmul), depois a soma (fadd) com 1 e finalmente a multiplicação vezes Ro (fmul); somente então o resultado é copiado para R (fstp). Os operadores que são executados inicialmente, são os que estiverem entre parênteses, começando pelos mais internos dentro da expressão complexa, continuando com a multiplicação e a divisão, soma e a subtração, nesta ordem.

É importante ressaltar que nem todos os processadores possuem operações de ponto flutuante integrado no seu hardware, por exemplo, o 8051 e microcontroladores em geral, sendo que as rotinas de cálculo devem ser implementadas por software. Em geral para pequenos sistemas de hardware dedicados, deve ser evitada a utilização de número em ponto flutuante, devido ao grande tamanho das bibliotecas necessárias. Por exemplo uma biblioteca com operações de ponto flutuante, para um microcontrolador 8051 com 8 Kbytes de memória de programa, pode ocupar até 1KB (mais de 10% do recurso disponível), sem contar com o código.

Nos casos em que é necessária a utilização de números decimais por causa da exatidão, costuma-se utilizar o ponto fixo flutuante, ou seja, trabalha-se com todos os números multiplicados por 100 para obter 2 casas decimais de precisão, 1000 para 3 casas, e assim sucessivamente. O tempo de processamento das funções de ponto flutuante deve ser levado em conta, especialmente nas aplicações onde a temporização é crítica na faixa de dezenas de microssegundos.

Uma forma de colocar o resultado num dispositivo de saída é a função padrão ANSI chamada printf. Esta função é útil para enviar caracteres ASCII para a memória de vídeo nos PCs, e usando o canal serial usando compiladores para microcontroladores. Esta

Page 86: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

64646464

5555

função é normalmente distribuída junto com o pacote do compilador, numa biblioteca de funções sendo declarada junto com outras, no arquivo stdio.h.

Considerar o seguinte programa C: #include <stdio.h> void main () /* Um primeiro programa com saída de dados */

printf ("Teste do dispositivo de saída \n");

Código 5-23

Compilando e executando este programa você verá que ele coloca a string Teste do dispositivo de saída no monitor de vídeo se estiver compilando um programa para PC. O mesmo código compilado para um microcontrolador 8051 ou PIC, poderia enviar a mesma string (cadeia de caracteres) pelo canal serial. Este programa pode ser analisado por partes, como descrito a seguir.

A linha #include <stdio.h> indica ao pré-processador que deverá ser incluído o arquivo de cabeçalho (header file) stdio.h. Neste arquivo existem declarações de funções úteis para entrada e saída de dados padronizada (std = standard, padrão em inglês; io = Input/Output, entrada e saída). Toda vez que você quiser usar uma destas funções no código, deve-se incluir este comando. Esta linha é uma diretiva de pré-compilador. A linguagem C possui diversos arquivos de cabeçalho que definem as diversas funções disponíveis na biblioteca.

Durante a codificação de um programa devem ser colocados os comentários que ajudem a entender a lógica do programador. No último exemplo foi colocado um comentário: /* Um Primeiro programa com saída de dados */. O compilador C desconsidera qualquer string que comece com os símbolos /* e termine com */. Um comentário poderá ter mais de uma linha.

A linha void main() define uma função de nome main. Todos os programas em C têm que ter uma função main, pois esta é a função que será chamada quando o programa for executado. O conteúdo de uma função (conjunto de instruções, variáveis, etc.) é delimitado por chaves . O código que estiver dentro das chaves será executado seqüencialmente quando a função for chamada. A palavra void indica que a função não retorna nenhum valor, isto é, seu retorno é vazio, como foi visto anteriormente.

A única coisa que este programa realmente executa é chamar a função printf(), passando a string "Teste do dispositivo de saída \n" como argumento. É por causa do uso da função printf() pelo programa que deve-se incluir o arquivo-cabeçalho stdio.h . A função printf() neste caso irá apenas copiar a string na memória de vídeo do sistema e na próxima varredura, a mesma aparecerá no monitor de vídeo. O conjunto de símbolos \n é um caractere especial de controle comumente chamado de LF ou no nova linha. Alguns compiladores geram o código para este símbolo como uma combinação dos caracteres de controle LF e CR (carriage return ou retorno de carro), outros não. Assim por exemplo nos compiladores para PC ele será interpretado como um comando de quebra de linha, isto é, após imprimir Teste do dispositivo de saída o cursor passará para a próxima linha.

A função printf pode ser também utilizada para o envio de valores de variáveis. Assim, no programa abaixo pode ser visualizado o resultado de um cálculo. #include <stdio.h> void main(void) float Ro = 1000.0; float R;

Page 87: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

65656565

5555

float alfa = 0.0035; float To = 25.0; float T = 75.0; R = Ro * (1 + alfa * (T - To) ); printf(“R = %f”,R);

Código 5-24

Dentro da string a ser enviada para o dispositivo de saída, pode ser reservado um espaço para o envio de um valor armazenado na memória de forma binária, e que deverá ser convertido pela própria função para o seu equivalente em ASCII. Para a função executar isto corretamente deverá ser informada do tipo de dado e o valor do mesmo. O caractere ‘ %’ indica que o que vem a continuação é um valor armazenado em memória. Após este caractere a função espera que seja definido qual o tipo para efetuar a conversão, f para float, d para inteiro, por exemplo. Após a finalização da string deverá ser repassado como parâmetro a variável que contém o valor a ser previamente convertido para ASCII e posteriormente enviado ao dispositivo de saída. Maiores detalhes sobre a função printf serão vistos mais adiante.

Tendo um mecanismo básico de saída, será necessário um mecanismo básico de entrada de dados. Uma função básica é scanf. Nos compiladores para PC, esta função executa a leitura do buffer dedicado ao teclado, esperando que o usuário digite algumas teclas mais um ENTER. Os parâmetros necessários para esta função capturar os dados do buffer de forma adequada são o tipo de dado e o lugar da memória onde será armazenado.

Observe o programa anterior modificado onde é permitido carregar os valores das variáveis durante a execução. #include <stdio.h> void main(void) float Ro; float R; float alfa; float To; float T; printf(“\n Entre com Ro: ”); scanf(“%f”,&Ro); printf(“\n Entre com a temperatura atual T: ”); scanf(“%f”,&T); printf(“\n Entre com To: ”); scanf(“%f”,&To); printf(“\n Entre com o coeficiente térmico alfa: ”); scanf(“%f”,&alfa); R = Ro * (1 + alfa * (T - To) ); /* calcula o valor da resistência */ printf(“O valor da resistência R = %f a uma temperatura de %f C”,R,T);

Código 5-25

O primeiro parâmetro da função scanf, "%f", refere-se à informação necessária para a interpretação sobre o tipo de dado. O segundo parâmetro passado à função indica que o dado lido deverá ser armazenado na variável Ro. Como cada função poderá possuir as suas próprias variáveis, a função scanf poderá não visualizar a variável Ro, no caso de uso de variáveis locais. A solução para isto é enviar como parâmetro o endereço da própria variável ou do local onde existe uma cópia da mesma. Isto é feito através do operador & que quando colocado antes do nome de uma variável transforma-a no seu endereço de memória.

Page 88: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

66666666

5555

55..1155.. EExxeerrccíícciiooss

1. Escreva um programa que leia um caractere digitado pelo usuário, imprima o caractere digitado e o código ASCII correspondente a este caractere em representação decimal e hexadecimal.

2. Escreva um programa que leia duas strings e as coloque na tela, e que imprima o segundo caractere de cada string.

3. Modifique o programa que calcula as raízes de uma equação de segundo grau, usando a fórmula de Báskara, para que seja capaz de calcular e mostrar raízes complexas.

4. Escreva uma função que dado um horário em horas, minutos e segundos, retorne o valor equivalente em segundos a partir da meia noite. Ex. 20:10:15. O equivalente em segundos será: 72615 segundos.

5. Baseado no exemplo do cálculo da resistência com a variação da temperatura, implementar um programa que calcule as raízes de um polinômio de grau dois pela fórmula de Báskara. Dado um polinômio de segundo grau, sendo a variável independente s a, b e c são constantes: 0)( 2 =++ cbsas tendo as duas soluções

dadas pela fórmula: a

acbbs

2

42

2,1

−±−= . Sugestão: Utilizar a função sqrt (raiz

quadrada) ou pow (potência), da biblioteca de funções definida no arquivo math.h. Caso o seu compilador não possua funções equivalentes na sua biblioteca, as mesmas deverão ser implementadas pela utilização de algoritmos numéricos. Como os microprocessadores somente executam funções aritméticas simples com números inteiros, tais como soma, subtração, multiplicação e divisão de números, as funções mais complexas deverão ser implementadas utilizando funções simples, usando séries de Taylor ou tabelas normalizadas de dados11.

6. Escreva um programa que coloque os números de 0 a 100 num dispositivo de saída na ordem inversa (começando em 100 e terminando em 0).

7. Escreva um programa que leia uma string, conte quantos caracteres possui e quantos são iguais ao caractere 'a'. Ainda, substitua os caracteres que forem iguais a 'a' por 'A'. O programa deverá imprimir o número de caracteres totais da string, o número de caracteres modificados e a string modificada.

55..1166.. AAvvaalliiaaççããoo

1. O programa abaixo lê uma variável pelo teclado e a imprime na tela

#include <stdio.h> main() int x; scanf("%d",&x); printf("%d",x); a. Verdadeiro b. Falso

11 Na internet existe um sítio onde podem ser carregadas algoritmos em C das funções mais comumente utilizadas, em http://www.netlib.org/fdlibm.

Page 89: Tratado Da Linguagem c

F U N D A M E N T O S D A L I N G U A G E M C

67676767

5555

2. Uma string, é uma seqüência de caracteres terminada com um '\0'. Uma string pode ser armazenada em um vetor de caracteres

a. Verdadeiro b. Falso

3. Os comentários na linguagem C só podem ter uma linha de comprimento

a. Verdadeiro b. Falso

4. A linguagem C tem este nome porque foi a sucessora da linguagem B.

a. Verdadeiro b. Falso

5. Em C, variáveis com nomes abc e Abc representam a mesma variável .

a. Verdadeiro b. Falso

6. A instrução #include <stdio.h> no programa anterior é colocada para que possamos utilizar as funções scanf e printf

a. Verdadeiro b. Falso

7. Sendo i uma variável inteira, a seguinte chamada a scanf é válida: scanf("%d", i);

a. Verdadeiro b. Falso

8. O comando printf ("%s%d%%","Tolerância de ",10); imprime:

a. Tolerância de 10% b. %s%d%% Tolerância de 10 c. % Tolerância de 10 d. 10 Tolerância de e. Nenhuma das anteriores

9. O que faz o seguinte programa em C?

#include <stdio.h> void main(void) int i = 2; printf ("\n O valor de i = %d ", i);

a. Nada b. Imprime: O valor de i = 2 c. Imprime: \n O valor de i = %d d. Salta uma linha e imprime: O valor de i = 2

10. O que é uma função em C ?

a. Parte de um programa b. Um bloco de código que pode ser utilizado diversas vezes na execução de um programa c. Uma estrutura da linguagem C que pode ser utilizada para que um programa fique mais

organizado d. Um bloco de código que pode receber parâmetros, processá-los e retornar alguma coisa e. Todas opções acima

11. O laço for de uma única instrução termina com:

a. Virgula b. Chave de abertura c. Chave de fechamento d. Ponto e virgula

12. A expressão de inicialização de um laço for

a. Nunca é executada; b. É executada uma única vez a cada iteração c. É executada enquanto o laço não termina d. É executada uma vez antes do laço ser iniciado

Page 90: Tratado Da Linguagem c

68686868

66.. EELLEEMMEENNTTOOSS DDAA LLIINNGGUUAAGGEEMM Este capítulo descreve os elementos da linguagem de programação C, incluindo os

nomes, números, e caracteres utilizados para implementar um programa em C.

A sintaxe ANSI C denomina os elementos componentes de "tokens". Este capítulo explica como são definidos os tokens e como o compilador os avalia.

Os seguintes tópicos serão discutidos nesta seção: Tokens Comentários Keywords Identificadores Constantes Strings literais Pontuação e caracteres especiais

Este capítulo inclui tabelas de referência para trigraphs, constantes de ponto flutuante, constantes inteiras e seqüências de escape.

Os operadores são símbolos (caracteres simples ou combinações destes) que especificam a forma em que os dados serão manipulados. Cada símbolo é interpretado como uma única unidade, denominado token.

66..11.. TTookkeennss ddaa LLiinngguuaaggeemm CC

Num programa fonte em C, o elemento básico reconhecido pelo compilador é o “token”. Um token é um pedaço de texto que o compilador não consegue dividir em outros elementos menores. Os tokens podem ser divididos em : Keywords Identificadores Constantes Strings literais Pontuações

As palavras-chave, identificadores, constantes, strings e operadores descritos neste capítulo são exemplos de tokens. Caracteres de pontuação tais como colchetes ([ ]), parênteses (( )), chaves ( ), e vírgulas (,) são também tokens.

66..11..11.. CCaarraacctteerreess ddee EEssppaaççoo eemm BBrraannccoo Os caracteres espaço, tabulação, retorno de carro, nova página e nova linha são

chamados de “caracteres de espaço em branco”, por servir ao mesmo propósito que os

Capítulo

6

Page 91: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

69696969

6666

espaços entre palavras e linhas em uma página impressa, facilitando a leitura. Os tokens são delimitados por espaços em branco e por outros tokens, tais como operadores e pontuação. Durante a compilação, o compilador C ignora os caracteres de espaços em branco a não ser que sejam utilizados como separadores ou como componentes de caracteres constantes ou strings literais. O uso de espaços em branco torna o programa mais legível. Deve ser notado que o compilador trata os comentários como espaços em branco.

66..11..22.. CCoommeennttáárriiooss eemm CC Um comentário é uma seqüência de caracteres que começa com a combinação de

caracteres, barra asterisco (/*) que é tratada pelo compilador como um único caractere espaço em branco e desta forma é ignorado. Um comentário pode incluir qualquer combinação de caracteres do grupo representável, incluindo caracteres de nova linha, mas excluindo o delimitador de “final de comentário” (*/). Os comentários podem ocupar mais que uma linha mas não podem estar aninhados (comentário dentro de comentário).

Os comentários podem aparecer em qualquer lugar onde um caractere espaço em branco for permitido. Desde que o compilador trata um comentário como um único caractere espaço em branco, não podem ser incluídos comentários dentro de tokens. O compilador ignora os caracteres nos comentários.

Os comentários devem ser utilizados para documentar o código. A seguir um exemplo de comentário aceito pelo compilador. /* Os comentários podem conter keywords tais como for e while sem gerar erros. */

Os comentários podem aparecer na mesma linha de uma instrução de código: printf( "Inicializando\n" ); /* Comentários podem ser colocados aqui */

Os comentários podem ser usados precedendo funções ou módulos de programa com contendo um bloco de descrição: /* MATHERR.C Mostra os códigos de erro para * funções matemáticas. */

Uma vez que os comentários não podem ser aninhados, tem-se um exemplo que gera um erro de compilação: /* Comentário da rotina de teste /* Abre arquivo */ fh = _open( "myfile.c", _O_RDONLY ); . . . */

O erro ocorre porque o compilador reconhecerá o primeiro */, depois das palavras Abre arquivo, com o final do comentário. Então tenta processar o texto restante, produzindo um erro quando encontra o conjunto */ fora do comentário.

Podem ser utilizados os comentários para deixar certas linhas de código inativas para propósitos de teste. Uma alternativa muito utilizada para esta tarefa é a utilização das diretivas de pré-processador #if e #endif como métodos mais adequados. Maiores informações podem ser vistas no capítulo 14.

Page 92: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

70707070

6666

Alguns compiladores suportam o comentário de linha única que é precedido de duas barras (//). Os comentários não podem se estender até a segunda linha. // Este é um comentário válido em alguns compiladores C

Os comentários que começam com duas barras (//) são terminados pelo seguinte caractere de nova linha. No exemplo abaixo, o caractere nova linha é precedido de uma barra invertida (\), criando uma “seqüência de escape”. Esta seqüência de escape faz com que o compilador trate a seguinte linha como sendo parte da primeira. // my comment \ i++;

Assim, a instrução i++ também está comentada.

66..11..33.. AAvvaalliiaaççããoo ddooss TTookkeennss Quando o compilador interpreta os tokens, o compilador tenta incluir tantos

caracteres quantos forem possíveis num único token antes de continuar para o próximo. Devido a esta característica, o compilador pode não interpretar os tokens corretamente se não estiverem separados apropriadamente por espaços em branco. Considerar a seguinte expressão: i+++j;

Nesse exemplo, o compilador primeiro tenta localizar o maior operador possível (++)1 a partir dos três sinais mais, então processa o símbolo restante como um operador de soma (+). Desta maneira a expressão é interpretada como

(i++) + (j) – caso 1

e não

(i) + (++j) – caso 2

No primeiro caso, será efetuado o incremento em uma unidade na variável i e o resultado será somado ao valor da variável j. Já no segundo caso o valor contido em i é somado ao valor contido em j. Após a soma, o valor de j será incrementado em uma unidade. A seguir são mostrados os códigos referentes aos dois casos. void main(void)

int i=5,j=1,z=0; z = i+++j;

Código 6-1

O código acima resulta em i=6, j=1 e z=6. Este código é equivalente ao primeiro caso. void main(void)

int i=5,j=1,z=0; z = i+(++j);

Código 6-2

Já o código anterior resulta em i=5, j=2 e z=7. Este código é equivalente ao segundo caso.

1 Na linguagem C, i++ equivale a i = i +1.

Page 93: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

71717171

6666

Nestes casos e em similares, é recomendado utilizar espaços em branco e parênteses para evitar ambigüidades e assegurar uma avaliação adequada para a expressão.

66..22.. KKeeyywwoorrddss

As "keywords" ou palavras chaves são palavras que possuem significados especiais para o compilador C. A linguagem C utiliza as seguintes keywords:

auto double int struct break else long switch case enum register typedef char extern return union const float short unsigned continue for signed void default goto sizeof volatile do if static while

As keywords não podem ser redefinidas. Ainda pode ser especificado um determinado texto para substituir as keywords antes da compilação utilizando diretivas de pré-processador.

66..33.. IIddeennttiiffiiccaaddoorreess

Os “identificadores” ou “símbolos” são os nomes definidos para variáveis, tipos, funções e labels (ou rótulos) do programa. Os nomes dos identificadores devem ser diferentes entre si e das keywords. As keywords não podem ser utilizadas como identificadores, já que elas são reservadas para usos especiais. O programador pode criar um identificador especificando-o na declaração da variável, tipo ou função. No exemplo a seguir, result é um identificador de uma variável inteira, main e printf são identificadores de funções. void main() int result; if ( result != 0 ) printf( "Arquivo corrupto\n" );

Código 6-3

Uma vez declarado, o programador pode utilizar o identificador nas seguintes instruções do programa para se referir ao valor associado.

Um tipo especial de identificador , chamado de instrução label, pode ser utilizado com a instrução goto. Recomenda-se a não utilização desta instrução por motivos estruturais.

O primeiro caractere de um nome de identificador deve ser um não-dígito (i.e. o primeiro caractere deve ser um símbolo “_” – underscore 2– ou uma letra). O padrão ANSI permite seis caracteres significativos para nomes identificadores externos, e 31 para nomes identificadores internos (dentro de uma função). Os identificadores externos (declarados com escopo global ou declarados com a classe de armazenamento extern) podem estar sujeitos a restrições adicionais porque estes identificadores devem ser processados por outros programas tais como os linkers.

2 Por exemplo: _var

Page 94: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

72727272

6666

O compilador C considera as letras maiúsculas e minúsculas como sendo caracteres diferentes. Esta característica é chamada de case sensitive, e possibilita a criar diferentes identificadores que tenham a mesma fonética mas diferentes nos seus conteúdos. Por exemplo, cada um dos seguintes identificadores é único: add ADD Add aDD

A seguir, exemplos de identificadores válidos. j count temp1 top_of_page skip12 LastNum

Um dado identificador tem um “escopo”, que é a região do programa na qual é conhecida ou relacionada. Maiores informações serão vistas mais adiante.

66..33..11.. CCaarraacctteerreess MMuullttiibbyyttee ee CCaarraacctteerreess LLoonnggooss Um caractere multibyte é um caractere composto de uma seqüência de um ou mais

bytes. Cada seqüência de byte representa um caractere único no grupo de caracteres estendido. Os caracteres multibyte são utilizados em grupos de caracteres como o Kanji.

Os caracteres longos são códigos de caracteres multilingual que possuem 16 bits de largura.

66..33..22.. TTrriiggrraapphhss O grupo de caracteres de um programa em C está descrito dentro do grupo de

caracteres ASCII representados por 7 bits, mas é um supergrupo do padrão ISO 646-1983 Invariant Code Set. As seqüências trigraph, permitem que os programas em C possam ser escritos somente utilizando somente a norma ISO3 Invariant Code Set. Os trigraphs são seqüências de três caracteres (dois caracteres de interrogação) que o compilador substitui pelo seu caractere correspondente de pontuação. Assim podem ser usados os caracteres trigraphs em arquivos fonte quando o grupo de caracteres não contém as representações gráficas convenientes para alguns caracteres de pontuação.

A tabela a seguir mostra as nove seqüências de trigraphs. Todas as ocorrências no arquivo fonte da primeira coluna, serão substituídas pelo caractere correspondente na segunda coluna. É importante ressaltar que nem todos os compiladores aceitam os trigraphs.

Trigraph Caracteres de pontuação ??= # ??( [ ??/ \ ??) ] ??' ^ ??<

3 ISO: International Standards Organization

Page 95: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

73737373

6666

??! | ??> ??- ~

Tabela 6-1 – Exemplos de trigraphs

66..44.. CCoonnssttaanntteess

Uma “constante” é um número, caractere, ou string que pode ser usada como um valor em um programa. As constantes são utilizadas para representar valores em ponto flutuante, inteiros, enumerações ou caracteres que não podem ser modificados. As constantes são caracterizadas por ter um valor e um tipo.

66..44..11.. CCoonnssttaanntteess ddee PPoonnttoo FFlluuttuuaannttee Uma constante de ponto flutuante é um número decimal que represente um

número real com sinal. A representação de um número real com sinal inclui uma porção inteira, uma porção fracionária e um expoente. Deve-se utilizar constantes de ponto flutuante para representar valores de ponto flutuante que não devem ser modificados.

Podem ser omitidos os dígitos antes do ponto decimal (a parte inteira do valor) ou os dígitos depois do ponto decimal (parte fracionária), mas não ambos. O ponto decimal pode ser ignorado somente se for colocado o expoente. Não é permitido o uso de caractere espaços em branco separando os dígitos ou caracteres da constante.

O seguinte exemplo mostra algumas formas de constantes do tipo ponto flutuante e expressões: 15.75 1.575E1 /* = 15.75 */ 1575e-2 /* = 15.75 */ -2.5e-3 /* = -0.0025 */ 25E-4 /* = 0.0025 */

As constantes de ponto flutuante são positivas, a menos que sejam precedidas pelo símbolo menos (-). Neste caso o símbolo menos é tratado como um operador negação aritmético unário. As constantes de ponto flutuante podem ser do tipo float, double ou long double.

Em geral, uma constante de ponto flutuante que não tenha o sufixo f, F, l ou L será do tipo double. Se for utilizada a letra F ou f como sufixo, a constante será do tipo float.

Por exemplo: 100L /* tem o tipo long double */ 100F /* é do tipo float */ 100D /* é do tipo double */

Como foi visto, pode-se omitir uma parte da constante de ponto flutuante, como mostra o seguinte exemplo. O número .75 pode ser expresso de várias formas, incluindo as seguintes: .0075e2 0.075e1 .075e1

Page 96: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

74747474

6666

75e-2

66..44..22.. CCoonnssttaanntteess IInntteeiirraass Uma "constante inteira" é um número decimal (base 10), octal (base 8), ou

hexadecimal (base 16) que representa um valor inteiro. As constantes inteiras podem ser usadas para representar valores de inteiro que não podem ser mudados.

As constantes inteiras são positivas a menos que sejam precedidas por um sinal de menos (-). O sinal de menos é interpretado como um operador negação aritmético unário. Os operadores aritméticos unários serão discutidos mais para frente.

Se uma constante inteira começar com os caracteres 0x ou 0X, o sistema assumirá que está representada no sistema hexadecimal. Se começar com o dígito 0, será considerada como uma representação no sistema octal. Caso contrário, é assumido que o valor está sendo representado no sistema decimal.

As linhas seguintes são equivalentes: 0x1C /* = representação hexadecimal para o valor 28 decimal */ 034 /* = representação octal para o valor 28 decimal */

Nenhum caráter de espaço em branco pode separar os dígitos de uma constante de inteira. Os exemplos a continuação mostram constantes decimais, octais e hexadecimais válidas.

Constantes Decimais 10 132 32179

Constantes Octais 012 0204 076663

Constantes Hexadecimais 0xa ou 0xA 0x84 0x7dB3 ou 0X7DB3

TTiippooss ddee IInntteeiirrooss

Toda constante inteira é determinada por um tipo, baseado no seu valor e na forma em que for expressa. Pode-se forçar qualquer constante inteira a ser do tipo long adicionando a letra l ou L no final da constante; também pode-se forçar um tipo unsigned, adicionando a letra u, ou U, ao valor. O caractere minúsculo l pode ser confundido com o dígito 1 e por este motivo deve ser evitado. Algumas formas de constantes de inteiro longas são mostradas a seguir.

Constantes Decimais Unsigned 99U

Constantes Decimais Long

Page 97: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

75757575

6666

10L 79L

Constantes Octais Long 012L 0115L

Constantes Hexadecimais Long 0xaL ou 0xAL 0X4fL ou 0x4FL

Constantes Decimais Unsigned Long 776745UL 778866LU

O tipo que for designado a uma constante depende do valor que esta representa. O valor de uma constante deve estar na faixa de valores representáveis para seu tipo. O tipo de uma constante determina quais conversões serão executadas quando a ela for usada em uma expressão, ou quando o sinal de menos (-) for aplicado. A lista a seguir resume as regras de conversão para constantes inteiras.

O tipo para uma constante decimal sem um sufixo é um int, long int, ou unsigned long int. O primeiro destes três tipos nos quais o valor da constante pode ser representado é o tipo nomeado para a constante.

O tipo designado para as constantes octal e hexadecimal sem sufixos são int, unsigned int, long int, ou unsigned long int dependendo do tamanho da constante.

O tipo designado para constantes com o sufixo u ou U é unsigned int ou unsigned long int dependendo do seu tamanho.

O tipo designado para as constantes com sufixo l ou L pode ser long int ou unsigned long int dependendo do seu tamanho.

O tipo designado com sufixos u ou U e l ou L é int longo não assinado é unsigned long int.

66..44..33.. CCoonnssttaanntteess CCaarraacctteerree Uma "constante caractere" é formada envolvendo o caractere em aspas simples (' ').

TTiippooss ddee CCaarraacctteerreess

Uma constante caractere inteira não precedida pela letra L é do tipo int. O valor de uma constante de caráter de inteiro que contém um único caractere, é o valor numérico do caractere interpretado como um inteiro. Por exemplo, o valor numérico do caractere 'a' é 97 em decimal e 61 em hexadecimal.

Sintaticamente, uma "constante caractere longa" é uma constante de caractere prefixada pela letra L. char schar = 'x'; /* Uma constante de caractere */

Page 98: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

76767676

6666

CCoonnjjuunnttoo ddee CCaarraacctteerreess ddee EExxeeccuuççããoo

O conjunto de caracteres de execução não é necessariamente o mesmo que é usado para escrever programas de C. O conjunto de caracteres de execução inclui todos os caracteres tais como o caractere nulo, caractere de newline, retrocesso, tabulação horizontal, tabulação vertical, retorno de carro, e seqüências de escape.

SSeeqqüüêênncciiaass ddee EEssccaappee

As combinações de caracteres que consistem em uma barra invertida (\) seguidos de uma letra ou uma combinação de dígitos são chamadas "seqüências de escape". Para representar um caractere de newline, aspas simples, ou certos caracteres especiais constantes, devem ser utilizadas as seqüências de escape. Uma seqüência de escape é considerada como um único caractere e é válido como um caractere constante.

As seqüências de escape são tipicamente usadas para especificar ações tais como retornos de carro e tabulações em terminais e impressoras. Estas seqüências também permitem efetuar representações literais de caracteres não imprimíveis, e de caracteres que normalmente têm significados especiais, tais como as aspas duplas (“). A tabela a seguir mostra as seqüências de escape ANSI e o que elas representam.

Seqüência de Escape

Representa

\a Bell (alert) – Campainha \b Backspace – Retorno \f Formfeed – Alimentação de página \n New line – Nova linha \r Carriage return – Retorno de carro \t Horizontal tab – Tabulação horizontal \v Vertical tab – Tabulação vertical \' Single quotation mark – Aspas simples \" Double quotation mark – Aspas duplas \\ Backslash – Barra invertida \? Sinal de interrogação \ooo Caractere ASCII em notação octal \xhhh Caractere ASCII em notação hexadecimal

Tabela 6-2 – Seqüências de escape

EEssppeecciiffiiccaaççõõeess ddee CCaarraacctteerreess eemm OOccttaall ee HHeexxaaddeecciimmaall

A seqüência \ooo permite especificar qualquer caractere do grupo de caracteres ASCII como um código de caractere de três dígitos em notação octal. O valor numérico do inteiro em notação octal especifica o valor do caractere desejado.

De forma análoga, a seqüência \xhhh permite especificar qualquer caractere ASCII como um código de caractere em notação hexadecimal. Por exemplo, para caractere de retrocesso (backspace) pode ser utilizada a seqüência de escape (\b), ou codificar como \010 (octal) ou \x008 (hexadecimal).

Numa seqüência de escape em octal só podem ser usados os dígitos 0 a 7. Uma seqüência de escape octal não pode ter mais três dígitos. Quando não é necessário usar todos os três dígitos, deve-se usar pelo menos um. Por exemplo, a representação em octal para o caractere backspace é \10 e \101 para a letra A.

Page 99: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

77777777

6666

De forma análoga deve-se usar um dígito pelo menos para uma seqüência de escape hexadecimal, mas poderão ser omitidos o segundo e terceiros dígitos. Desta maneira poderia especificar a seqüência de escape hexadecimal para o caractere backspace como4 \x8, \x08, ou \x008.

De forma diferente das seqüências de escape octais, o número de dígitos hexadecimais em uma seqüência de escape não é limitado. Uma seqüência de escape hexadecimal terminará com o primeiro caráter que não é um dígito hexadecimal. Uma vez que os dígitos hexadecimais incluem as letras a até f, deve-se ter cuidado para assegurar que a seqüência de escape termina num dígito intencional. Para evitar confusão, podem-se colocar as definições de caracteres hexadecimais numa definição de macro: #define Bell '\x07'

66..44..44.. SSttrriinnggss LLiitteerraaiiss Uma "string literal" é uma seqüência de caracteres inseridos entre aspas duplas (“ ”).

As strings literais são usadas para representar uma seqüência de caracteres que, juntos, formam uma string com terminador nulo.

O exemplo a seguir é uma string literal simples: char amessage = "Esta é uma string literal.";

Todos os códigos de escape, listados na tabela anterior, são válidos em strings literais. Para representar as aspas simples numa string literal, deve ser usada a seqüência de escape \". As aspas simples podem ser representadas sem a necessidade de uma seqüência de escape. A barra invertida (backslash ‘\’) deve ser seguida de uma segunda barra quando colocada dentro da string. Quando aparecer uma barra invertida no fim de uma linha, será sempre interpretada como um caractere de continuação de linha.

TTiippooss ddee SSttrriinnggss LLiitteerraaiiss

As strings literais são do tipo arranjo de char (char []). As strings de caracteres longos são do tipo arranjo de wchar_t (wchar_t []). Isto indica que uma string é um arranjo de elementos do tipo char. O número de elementos do arranjo é igual ao número de caracteres da string mais o caractere de terminação nulo (null).

AArrmmaazzeennaammeennttoo ddee LLiitteerraaiiss

Os caracteres de uma string literal são armazenados na seqüência em posições consecutivas de memória. Uma seqüência de escape (tais como \\ ou \”) dentro de uma string literal contará como um caractere único. Um caractere null (representado pela seqüência de escape \0) será automaticamente anexado e indicará o final da string literal.

CCoonnccaatteennaaççããoo ddee SSttrriinnggss LLiitteerraaiiss

Para formar strings literais de mais de uma linha, podem ser concatenadas duas strings. Para fazer isto, deve ser digitada uma barra invertida, e então pressionada a tecla

4 Os valores das seqüências de escape octal ou hexadecimal devem estar na faixa de valores representáveis pelo tipo unsigned char e tipo wchar_t para uma constante longa, como será visto mais adiante.

Page 100: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

78787878

6666

ENTER. A barra invertida instrui a o compilador a ignorar o caractere newline. Por exemplo, a string literal "Strings longas podem ser queb\ radas em duas ou mais partes."

é idêntica à string “Strings longas podem ser quebradas em duas ou mais partes."

A concatenação de strings pode ser usada em qualquer lugar sempre que seguido de um caractere de nova linha (newline) para colocar strings maiores que uma linha.

Para forçar uma nova linha dentro de uma string literal, entre a seqüência de escape de nova linha (\n) no ponto da string onde se que haja uma quebra de linha: "Entre em um número entre 1 e 100\n ou pressione ENTER"

Uma vez que as strings podem começar em qualquer coluna do código fonte e as strings longas podem continuar em qualquer coluna da linha seguinte, estas poderão ser posicionadas para melhorar a legibilidade do código fonte. De qualquer forma, a representação no dispositivo de saída não permanecerá inalterada. Por exemplo: printf ( "Esta é a primeira metade da string, " "esta é a segunda metade") ;

Contanto que cada parte da string seja incluída entre aspas duplas, as partes serão concatenadas produzindo uma única string no dispositivo de saída. "Esta é a primeira metade da string, esta é a segunda metade”

Um ponteiro para string, inicializado como dois strings literais separadas somente por espaços em branco, será armazenado como uma única string5. Quando corretamente referenciado, como no exemplo seguinte, o resultado é idêntico ao exemplo anterior char *string = "Esta é a primeira metade da string, " "esta é a segunda metade"; printf("%s" , string ) ;

66..55.. CCaarraacctteerreess EEssppeecciiaaiiss ee PPoonnttuuaaççããoo

A pontuação e os caracteres especiais da linguagem C possuem vários usos, desde organizar o texto de programa até definir as tarefas, que o compilador ou o programa compilado deverão efetuar. Eles não especificam uma operação a ser executada. Alguns símbolos de pontuação também são operadores. O compilador determinará o seu uso de acordo com o contexto. [ ] ( ) * , : = ; ... #

Estes caracteres possuem significados especiais em C. O seu uso é descrito ao longo deste documento. O caractere cardinal (#) pode ser usado somente nas diretivas de pré-processador.

66..66.. EExxeerrccíícciiooss

1. Se dois fios metálicos de materiais diferentes forem conectados em uma das suas extremidades, tendo uma temperatura T1 na junção e T2 nas extremidades livres, é gerada uma força eletromotriz (efeito Seebeck), cujo valor é dado pela expressão:

5 Os ponteiros serão discutidos no capítulo 12.

Page 101: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

79797979

6666

( ) ( )22

212211 TTCTTCE −+−= [V]

Os sensores de temperatura que fazem uso deste efeito são chamados de Termopares ou Pares Termoelétricos. Para termopares do tipo T (Cobre-Constantan) por exemplo, C1 = 62.1 e C2 = -0.045.

Faça um programa que : a. Pergunte o número de dados a serem entrados via teclado (valores de T1). Utilize este valor para fazer a alocação dinâmica de memória (usar ponteiros). Além disto o programa deverá permitir a entrada dos valores dos parâmetros C1, C2, T2. b. Calcule a tensão E, para cada valor de T1. c. Calcule quantos valores calculados de E estão acima da média. d. Permita a gravação e leitura dos dados em arquivo, com nome e caminho definido pelo usuário via teclado. O programa deverá armazenar também os parâmetros.

2. Faça um programa que permita a entrada de um número inteiro (32 bits); uma função que permita setar ou resetar um bit, do número entrado via teclado. Ex.: unsigned int set_bit (unsigned int var,unsigned int bitnum, unsigned int val) onde a função retorna o valor resultante depois da operação, e recebe os parâmetros var, que é o valor ou variável, bitnum é o número do bit, e val define se o bit vai para zero ou um. Se chamarmos a função fazendo (sabendo que y = 0) x = setbit (y,4,1) vai setar o bit 4 (quinto bit porque o primeiro é o bit 0) do valor contido na variável y e colocar o resultado na variável x, o resultado será neste caso x = 32. O valor do número deverá ser apresentado na tela, em binário decimal e hexadecimal, antes e depois da operação. Faça também uma função que permita a inversão de todos os bits do número inteiro, e que apresente os resultados de maneira idêntica aos itens anteriores.

3. Converta os seguintes números para a representação nos sistemas binário, decimal e hexadecimal. Demonstre o desenvolvimento aritmético.

552D 0000010101111B 191H

4. Um termistor é um resistor que varia a sua resistência com a variação da temperatura segundo a relação abaixo:

e oTToRR

−=

11β

Onde os valores de ββββ é o coeficiente de variação da resistência com a temperatura, Ro é a resistência a uma determinada temperatura pré-definida, e To é a temperatura que define os dois valores anteriores. Estas grandezas devem ser definidas pelo usuário via teclado.

Elabore um programa com duas opções de menu:

Dados n valores de T calcula n valores de R

T1

Forno

T2

+

-

Força Eletromotriz

Page 102: Tratado Da Linguagem c

E L E M E N T O S D A L I N G U A G E M

80808080

6666

Dados n valores de R calcula as n T

Obs. Para ambos os itens apresentar o valores máximo, mínimo e a média.

Lembrar que: xe x =ln ; bab

alnlnln −=

; ( ) baab lnlnln +=

5. Faça um programa que calcule o fatorial de 10000 números armazenados num vetor (com ponto flutuante), calcule a média do vetor, o valor máximo e o valor mínimo.

Page 103: Tratado Da Linguagem c

81818181

77.. EESSTTRRUUTTUURRAA DDEE UUMM PPRROOGGRRAAMMAA O objetivo deste capítulo é de fornecer uma visão geral da programação em C e a

execução de programas. Também serão vistos alguns termos e características importantes para o entendimento dos programas em C e os seus componentes. Os tópicos que serão discutidos aqui incluem: Arquivos fonte e programas fonte. A função main e a execução dos programas. Argumentos de linha de comando para verificação. Tempo de vida, escopo, visibilidade e encadeamentos (linkage). Espaço de Nomes

77..11.. AArrqquuiivvooss FFoonnttee ee PPrrooggrraammaass FFoonnttee

Um programa fonte pode ser dividido em um ou mais "arquivos fonte". O arquivo de entrada para o compilador é chamado de "unidade para tradução".

Os componentes de uma unidade para tradução são declarações externas que incluem definições de funções e declarações de identificadores. Estas declarações e definições podem estar em arquivos de fonte, arquivos de cabeçalho (header), bibliotecas, e outros arquivos que o programa precisar. Cada unidade para tradução deve ser compilada. O arquivo objeto resultante deve passar pelo processo de linker para formar um programa executável.

Um "programa fonte em C" é uma coleção de diretivas, pragmas, declarações, definições, blocos de instruções e funções. Porém, a localização destes componentes num programa afetará a utilização das variáveis e funções usadas no programa.

Os arquivos fonte não precisam ter instruções executáveis. Por exemplo, pode-se achar útil colocar definições de variáveis em um arquivo fonte e então declarar as referências para estas variáveis em outros arquivos fonte que as utilizam. Esta técnica faz com que as definições sejam fáceis de serem encontradas e atualizadas quando necessário. Pela mesma razão, as constantes e macros1 são organizadas em arquivos separados chamados "include files" ou "header files" (arquivos de cabeçalho), que podem ser referenciados no arquivo fonte quando requerido.

77..11..11.. DDiirreettiivvaass ddee PPrréé--PPrroocceessssaaddoorr A "diretiva" instrui ao pré-processador C para executar uma ação específica no

texto do programa antes de compilação. Este exemplo usa a diretiva de pré-processador #define:

1 Macro: Seqüência de comandos ou funções que podem ser executados como programas.

Capítulo

7

Page 104: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

82828282

7777

#define MAX 100

Esta declaração diz para o compilador, que substitua cada ocorrência de MAX por 100 antes de compilação. As diretivas2 de pré-processador em C são mostradas a seguir como exemplo.

#define #endif #ifdef #line #elif #error #ifndef #pragma #else #if #include #undef

Tabela 7-1– Exemplo de diretivas de pré-processador.

77..11..22.. PPrraaggmmaass Cada compilador C suporta algumas características únicas, relacionadas com o tipo

de processador ou com o sistema operacional, aos quais são dedicados. Alguns programas, por exemplo, precisam efetuar um controle preciso sobre áreas de memória, onde são colocados os dados, ou precisam controla a forma em que certas funções recebem os parâmetros. As diretivas #pragma oferecem uma maneira de efetuar tarefas especificas para cada processador ou sistema operacional, enquanto mantém a compatibilidade total com a linguagem C.

As diretivas #pragma, por definição, são especificas a um processador ou a uma maquina, e são usualmente diferentes para cada compilador. A sintaxe é a seguinte: #pragma string-token

Onde string-token é uma serie de caracteres que fornece a instrução de compilador especifica e os argumentos, se existirem. O caractere numeral (#) deve ser o primeiro. Espaços em branco podem separar o caractere da palavra pragma. Seguidos da expressão #pragma, escrever qualquer texto que o tradutor possa processar. O argumento para #pragma pode estar sujeita a expansão em macros.

Se o compilador encontrar uma diretiva pragma que não é reconhecida, emitirá uma mensagem de aviso, mas não deterá a compilação.

As diretivas pragma podem ser usadas em declarações condicionais, para fornecer nova funcionalidade de pré-processamento, ou para fornecer informação especifica para um determinado compilador. A seguir, alguns exemplos de #pragmas. #pragma optimize // Visual C 5.0 #pragma pack // Visual C 5.0 #pragma device PIC18F877 // PCW para Microchip PIC

77..11..33.. DDeeccllaarraaççõõeess ee DDeeffiinniiççõõeess Uma "declaração" estabelece uma associação entre uma variável particular, função,

ou tipo e os seus atributos. . Uma declaração também especifica onde e quando um identificador pode acessado. .

Uma "definição" de uma variável estabelece as mesmas associações que como uma declaração, mas também define a alocação de memória para esta variável.

Por exemplo, as funções main, find e count, e as variáveis var e val, são definidas num arquivo fonte, nesta ordem: void main()

2 Maiores detalhes no capítulo 14.

Page 105: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

83838383

7777

... int var = 0; double val[MAXVAL]; char find( fileptr ) ... int count( double f ) ...

Código 7-1

As funções var e val podem ser usadas nas funções find e count; não são necessárias declarações posteriores. Estas variáveis não são visíveis (não podem ser acessadas) pela função main por estarem declaradas após.

77..11..44.. DDeeccllaarraaççõõeess ee DDeeffiinniiççõõeess ddee FFuunnççõõeess Os protótipos de funções estabelecem o nome da função, seu tipo de retorno, o

tipo e número de parâmetros formais. Uma definição de função inclui o corpo de função.

Declarações de funções e variáveis podem aparecer dentro ou fora de uma definição de função. Qualquer declaração dentro de uma definição de função é dita como sendo de nível "interno" ou "local ". Qualquer declaração colocada fora das definições da função, é dita de nível "externo", "global", ou de "escopo de arquivo". As definições das variáveis, como nas declarações, podem aparecer em nível interno (dentro de uma definição de função) ou num nível externo (fora de todas as definições de função). Definições de função sempre deverão ocorrer no nível externo.

77..11..55.. BBllooccooss Uma sucessão de declarações, definições, e instruções encerradas dentro de chaves

(...) são chamadas de "bloco". Existem dois tipos de blocos em C. Uma "instrução composta" de uma ou mais declarações, é um tipo de bloco. O outro bloco, a "definição de função", consiste em um conjunto de instruções (o corpo da função) mais o cabeçalho associado à função (nome de função, tipo de retorno e seus parâmetros). Um bloco que esta dentro de um outro é dito ser aninhado.

Deve ser notado que enquanto todas as instruções que estão inclusas dentro de chaves, nem tudo o que está entre chaves constitui um conjunto de instruções. Por exemplo, embora as especificações de ordem, estrutura (struct), ou elementos de enumeração (enum) podem aparecer dentro de chaves, eles não são um conjunto de instruções3.

77..22.. AA FFuunnççããoo mmaaiinn ee aa EExxeeccuuççããoo ddoo PPrrooggrraammaa

Todo programa em C tem uma função principal que deve ser nomeada com o nome de main. . A função principal serve como o ponto de partida para execução de programa. Normalmente, ela controla a execução do programa direcionando as chamadas para outras funções no programa. Um programa normalmente pára a execução no final da 3 Maiores detalhes no capítulo 16.

Page 106: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

88884444

7777

função main, embora possa terminar em outros pontos no programa devido a uma variedade de razões. Às vezes, quando um certo erro é detectado durante a execução, pode-se querer forçar a terminação de um programa. Para fazer isto, pode ser utilizada a função exit.

As funções dentro de um programa fonte executam uma ou mais tarefas específicas. A função main pode chamar estas funções para executar as suas tarefas respectivas. Quando a função main chama outra função, ela passa o controle da execução para a função, de forma que execução começa na primeira instrução da mesma. Uma função retorna o controle para a função main quando for executada uma função return ou quando o fim da função é encontrado.

Qualquer função, incluindo a main, pode ser declarada como tendo parâmetros. O termo "parâmetro" ou "parâmetro formal" se refere ao identificador que recebe um valor passado para a função. Quando uma função chama outra, a função chamada recebe valores por seus parâmetros da função que a chamou. Estes valores são chamados "argumentos". Podem-se declarar parâmetros formais para a função main de forma que possa receber argumentos através da linha de comando, usando o seguinte formato: main( int argc, char *argv[ ], char *envp[ ] )

Para passar informações para a função main, os parâmetros são tradicionalmente nomeados como argc e argv, embora o compilador C não requer estes nomes. Os tipos para argc e argv são definidos pela linguagem C. Tradicionalmente, se um terceiro parâmetro é passado para a função main, será chamado envp. O tipo para o parâmetro envp é padronizado pelo ANSI, mas não o seu nome. As seções seguintes explicam estes parâmetros com maiores detalhes.

77..22..11.. OOss AArrgguummeennttooss aarrggcc ee aarrggvv A função main() pode ter parâmetros formais. Mas o programador não pode

escolher quais serão eles. A declaração mais geral da função main() é: int main (int argc,char *argv[]);

Os parâmetros argc e argv dão ao programador acesso à linha de comando com a qual o programa foi chamado.

O argc (argument count) é um inteiro e possui o número de argumentos com os quais a função main() foi chamada na linha de comando. Ele é, no mínimo 1, pois o nome do programa é contado como sendo o primeiro argumento.

O argv (argument values) é um ponteiro para uma matriz de strings. Cada string desta matriz é um dos parâmetros da linha de comando. O argv[0] sempre aponta para o nome do programa (que, como já foi dito, é considerado o primeiro argumento). É para saber quantos elementos temos em argv que temos argc.

Exemplo: Escreva um programa que faça uso dos parâmetros argv e argc. O programa deverá receber da linha de comando o dia, mês e ano correntes, e imprimir a data em formato apropriado. Veja o exemplo, supondo que o executável se chame data:

data 19 04 99

O programa deverá imprimir:

Page 107: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

85858585

7777

19 de abril de 1999 #include <stdio.h> #include <stdlib.h> void main(int argc, char *argv[]) int mes; char *nomemes [] = "Janeiro", "Fevereiro", "Março", "Abril", "Maio", "Junho", "Julho", "Agosto", "Setembro", "Outubro", "Novembro", "Dezembro"; if(argc == 4) /* Testa se o numero de parametros fornecidos esta' correto o primeiro parametro e' o nome do programa, o segundo o dia o terceiro o mes e o quarto os dois ultimos algarismos do ano */ mes = atoi(argv[2]); /* argv contem strings. A string referente ao mes deve ser transformada em um numero inteiro. A funcao atoi esta sendo usada para isto: recebe a string e transforma no inteiro equivalente */ if (mes<1 || mes>12) /* Testa se o mes e' valido */ printf("Erro!\nUso: data dia mes ano, todos inteiros"); else printf("\n%s de %s de 19%s", argv[1], nomemes[mes-1], argv[3]); else printf("Erro!\nUso: data dia mes ano, todos inteiros");

Código 7-2

77..22..22.. DDeessccrriiççããoo ddooss AArrgguummeennttooss O parâmetro argc da função main4 é um inteiro que especifica quantos

argumentos são passados ao programa a partir da linha de comando. Uma vez que o nome do programa é considerado um argumento, o valor de argc deve ser pelo menos um.

O parâmetro argv é um arranjo de ponteiros para strings terminadas em nulo, que representam os argumentos de programa. Cada elemento do array aponta a uma string de um argumento passado para a função main (ou wmain). O parâmetro argv pode ser declarado como um array de ponteiros para os dados do tipo char (char *argv []) ou como um ponteiro para ponteiros do tipo char (char **argv). . A primeira string (argv[0]) é o nome do programa. O último ponteiro (argv[argc]) é NULL.

O parâmetro envp é um ponteiro para um array de strings terminadas em null que representam os valores colocados pelo usuário nas variáveis do ambiente. O parâmetro envp pode ser declarado como um array de ponteiros para char (char *envp []) ou como um ponteiro para ponteiros para char (char * *envp). . O final do array é indicado por um ponteiro NULL. Notar que o bloco de ambiente passado para a função main ou wmain será uma "cópia instantânea" do ambiente atual. A seguir um exemplo5. #include <stdio.h> #include <string.h> void main( int argc, char *argv[], char *envp[] )

int iNumberLines=0,i; /* O padrao é sem número de linhas. */ /* Se forem colocados mais dados além do nome do arquivo executavel e se */ /* foi especificado o comando de linha /n, a lista de variaveis do ambiente */ /* será enumerada por linha. */

if( argc == 2 && stricmp( argv[1], "/n" ) == 0 ) iNumberLines = 1;

/* Move-se atraves da lista de strings até encontrar um caractere NULL. */ for(i = 0; envp[i] != NULL; ++i ) if( iNumberLines )

4 Também existe a função wmain. Pode-se utilizada wmain no lugar de main, para obter um código compatível com o UNICODE, onde os argumentos serão repassados no formato wide-caracter, apropriado para sistemas multi-linguais. 5 Uma vez gerado o arquivo executável, execute-o na linha de comando da seguinte maneira (suponha que se chame teste.exe): teste /n

Page 108: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

86868686

7777

printf(“%d : %s \n”,i,envp[i]);

Código 7-3

77..33.. TTeemmppoo ddee VViiddaa,, EEssccooppoo,, VViissiibbiilliiddaaddee ee CCoonneexxõõeess

Para entender como um programa em C trabalha, deve-se entender as regras que determinam como podem ser usadas as variáveis e funções dentro do programa. Vários conceitos são cruciais para o entendimento destas regras: Tempo de vida Escopo e visibilidade Conexões

77..33..11.. TTeemmppoo ddee VViiddaa O "tempo de vida" é o período durante execução de um programa no qual uma

variável ou função existe. A duração do armazenamento do identificador determina o seu tempo de vida.

Um identificador declarado como o especificador de classe de armazenamento static, tem duração armazenamento estática. Os identificadores com duração de armazenamento estática (também chamados "globais") têm o seu local de armazenamento e valor definido, preservado durante a execução do programa. O local de armazenamento é reservado e o valor armazenado do identificador é inicializado uma vez, antes do início do programa. Um identificador declarado como tendo conexões externas ou internas também tem duração estática.

Um identificador declarado sem o especificador de classe de armazenamento static, tem duração de armazenamento automatic se for declarado dentro de uma função. Um identificador que possui duração de armazenamento automática (identificador local), tem local de armazenamento e valor definido, somente dentro do bloco onde o mesmo está definido ou declarado. Para um identificador automático será alocado um novo local de armazenamento cada vez que entra no bloco, e perde seu local de armazenamento (e o seu valor) quando o programa encerra o bloco. Os identificadores declarados numa função sem conexões, também têm duração de armazenamento automática.

As regras seguintes especificam se um identificador tem tempo de vida global (estático) ou local (automático): Todas as funções têm tempo de vida estático. Portanto elas existem durante toda a execução do

programa. Os identificadores declarados no nível externo (i.e., fora de todos os blocos no programa, no mesmo nível de definições de funções) sempre terão tempo de vida global (estático). Se uma variável local possuir um inicializador, a mesma será inicializada toda vez que for criada

(a menos que seja declarada como static). Os parâmetros de função também têm tempo de vida local. É possível especificar um tempo de vida global para um identificador dentro de um bloco, incluindo o especificador de classe de armazenamento static na sua declaração. Se uma variável for declarada estática, a mesma reterá seu valor desde uma entrada de bloco até a próxima.

Mesmo que um identificador ou variável, com tempo de vida global, exista ao longo da execução do programa fonte (por exemplo, uma variável externamente declarada ou

Page 109: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

87878787

7777

uma variável local declarada com a palavra chave static), o mesmo poderá não ser visível em todas as partes do programa.

A memória pode ser alocada quando necessário (alocação dinâmica) utilizando rotinas específicas da biblioteca de funções, tais como a função malloc. Considerando que a alocação dinâmica de memória deverá utilizar rotinas da biblioteca, não é considerada parte da linguagem. .

77..33..22.. EEssccooppoo ee VViissiibbiilliiddaaddee A "visibilidade" de um identificador determina as porções do programa no qual

pode ser referenciado; o seu escopo. Um identificador é visível (i.e., usado de ser de pode) somente em porções de um programa incluídas no seu escopo, que pode ser limitado (de maneira a aumentar as suas restrições) para o arquivo, função, bloco, ou protótipo de função no qual ele aparece. O escopo de um identificador é a parte do programa no qual o seu nome pode ser usado. Há quatro tipos de escopo: função, arquivo, bloco e protótipo de função.

Todos os identificadores têm o seu escopo determinado pelo nível no qual a declaração ocorre. As regras para cada tipo de escopo que controla a visibilidade dos identificadores dentro de um programa são colocadas a seguir: Escopo de Arquivo: A declaração ou especificação do tipo para um identificador com escopo

de arquivo, aparece fora de qualquer bloco ou lista de parâmetros, e estará acessível de qualquer lugar na unidade de tradução depois da sua declaração. Os nomes de identificadores com escopo de arquivo são chamados freqüentemente "globais" ou "externos". O escopo de um identificador global começa no ponto de sua definição ou declaração e termina no final unidade de tradução. Escopo de função: Um label é um identificador único no escopo de uma função. Um label é

implicitamente declarado pelo seu uso numa instrução. Os labels devem ser únicos dentro de uma função. Escopo de bloco: O declarador ou especificador de tipo para um identificador com escopo de

bloco aparece dentro de um bloco ou uma lista de declarações de parâmetros formais numa definição de função. Só é visível do ponto de sua declaração ou definição, até o final do bloco que contém sua declaração ou definição. Seu escopo é limitado àquele bloco e para qualquer bloco aninhado naquele bloco, finalizando no fechamento das chaves que delimitam o bloco associado. Tais identificadores são chamados de "variáveis locais". Escopo de protótipo de função: O declarador ou especificador de tipo para o identificador

com escopo de protótipo de função aparece dentro da lista de declarações de parâmetro em um protótipo de função (não na declaração de função). Seu escopo termina no final do declarador da função.

As variáveis e funções declaradas no nível externo com o especificador de classe de armazenamento static, são visíveis somente dentro do arquivo de fonte no qual elas são definidas. Todas as outras funções são globalmente visíveis.

RReessuummoo ddee TTeemmppoo ddee VViiddaa ee VViissiibbiilliiddaaddee

A tabela a seguir resume as características de visibilidade (escopo) e tempo de vida para a maioria dos identificadores. As primeiras três colunas mostram os atributos que definem vida e visibilidade. Um identificador com os atributos dados pelas primeiras três colunas tem um tempo de vida e visibilidade mostrada na quarta e quinta coluna. Porém, esta tabela não cobre todos os casos possíveis. .

Page 110: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

88888888

7777

Nível

Item

Especificador de Classe de Armazenamento

Resultado: Tempo de Vida

Visibilidade (escopo)

Escopo de Arquivo

Definição de variável

static Global Resto do arquivo fonte no qual ela ocorre.

Declaração de variável

extern Global Resto do arquivo fonte na qual ela ocorre

Protótipo de função ou definição

static Global Num único arquivo fonte

Protótipo de função

extern Global Resto do arquivo fonte

Escopo de Bloco

Declaração de variável

extern Global Bloco

Definição de variável

static Global Bloco

Definição de variável

auto ou register Local Bloco

Tabela 7-2 – Tempo de vida e escopo

No seguinte exemplo são mostrados blocos, aninhamentos e visibilidade de variáveis. #include <stdio.h> /* Biblioteca padrão de funções de entrada e saída */ int i = 1; /* i é definida no nível externo */ int main() /* a função main é definida no nível externo */ printf( "%d\n", i ); /* Imprime 1 (valor de nível externo i) */ /* Início do primeiro bloco aninhado */ int i = 2, j = 3; /* i e j definidas em nível interno */ printf( "%d %d\n", i, j ); /* Imprime 2, 3 */ /* Inicio do segundo bloco aninhado */ int i = 0; /* i é redefinido */ printf( "%d %d\n", i, j ); /* Imprime 0, 3 */ /* Fim do segundo bloco aninhado */ printf( "%d\n", i ); /* Imprime 2 (a outra definição */ /* é restaurada) */ /* Fim do primeiro bloco aninhado */ printf( "%d\n", i ); /* Imprime 1(a definição de nível externo*/ /* é restaurada) */ return 0;

Código 7-4

Nesse exemplo existem quatro níveis de visibilidade: o nível externo e três níveis de bloco. Os valores são enviados para o dispositivo de saída como indicado nos comentários que acompanham a cada linha de instrução.

77..33..33.. EEnnccaaddeeaammeennttooss Os nomes dos identificadores podem-se referir a diferentes identificadores em

diferentes escopos. Um identificador declarado em diferentes escopos ou no mesmo mais de uma vez, pode-se referir ao identificador ou função por um processo chamado "encadeamento". O encadeamento determina as porções do programa no qual um identificador pode ser referenciado (visível). Existem três tipos de encadeamento: interno, externo, e sem encadeamento.

Page 111: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

89898989

7777

EEnnccaaddeeaammeennttoo IInntteerrnnoo

Se a declaração de um identificador de escopo de arquivo para um objeto ou função contém o especificador de classe de armazenamento static, o identificador terá encadeamento interno. Caso contrário, o identificador tem encadeamento externo. .

Dentro de uma unidade de tradução, cada instância de um identificador com encadeamento interno denota o mesmo identificador ou função. Os identificadores conectados internamente são únicos para a unidade de tradução.

EEnnccaaddeeaammeennttoo EExxtteerrnnoo

Se a primeira declaração a nível de escopo de arquivo, para um identificador não usa o especificador de classe de armazenamento static, o objeto terá encadeamento externo.

Se a declaração de um identificador para uma função não tem nenhum especificador de classe de armazenamento, seu encadeamento será exatamente determinado como se fosse declarada com o especificador de classe de armazenamento extern. Se a declaração de um identificador para um objeto tem escopo de e nenhum especificador de classe de armazenamento, seu encadeamento será externo.

O nome de um identificador com encadeamento externo designa a mesma função ou objeto de dados, como faz qualquer outra declaração para o mesmo nome com encadeamento externo. As duas declarações podem estar na mesma unidade de tradução ou em unidades de tradução diferentes. Se o objeto ou função também tem tempo de vida global, o objeto ou função será compartilhado pelo programa inteiro.

SSeemm EEnnccaaddeeaammeennttoo

Se uma declaração para um identificador dentro de um bloco não inclui o especificador de classe de armazenamento extern, o identificador não terá nenhum encadeamento e será único para a função.

Os seguintes identificadores não possuem nenhum encadeamento: Um identificador declarado para ser qualquer coisa diferente de um objeto ou uma função Um identificador declarado para ser um parâmetro de função Um identificador de escopo de bloco para um objeto declarado sem o especificador de classe

armazenamento extern.

Se um identificador não tem nenhum encadeamento, e for declarado o mesmo nome novamente (em um declarador ou especificador de tipo) no mesmo nível de escopo, será gerado um erro de redefinição de símbolo.

77..44.. EEssppaaççooss ddee NNoommeess

O compilador define um espaço de nomes para poder distinguir os identificadores usados em diferentes itens. Os nomes dentro de cada espaço de nomes, devem ser únicos para evitar conflitos, mas um nome idêntico pode aparecer em mais de um espaço de nomes. Isto permite que possa ser utilizado o identificador para dois ou mais itens diferentes, contanto que os itens estejam em espaços diferentes. O compilador pode definir as referências, baseado no contexto sintático do identificador dentro do programa.

Page 112: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

90909090

7777

A lista a seguir descreve os espaços de nomes usados em C.

Labels: Os labels fazem parte das instruções. A definição dos labels· é seguida de dois pontos (:) mas este não faz parte do mesmo, exceto no label pré-definido case. O uso do label já declarado deve acontecer sempre imediatamente seguido da instrução goto. Os labels devem usar nomes diferentes daqueles já utilizados como identificadores, ou de outros labels em outras funções. Por exemplo, um trecho de programa: ... FasePrincipal: ..... FaseSecundaria: ... if(fase == 2) goto FasePrincipal; ....

Estruturas, uniões e variáveis de enumeração: Estes labels são parte de estrutura, uniões e especificadores do tipo enumeração e, se presentes, sempre imediatamente seguidos das palavras reservadas struct, union, ou enum. Os nomes dos labels devem ser distintos de todos os que definem uma estrutura, enumeração, ou union com a mesma visibilidade.

Membros de estruturas ou unions: São alocados no mesmo espaço de nomes associados com cada tipo de estrutura e union. I.e., o mesmo identificador pode ser, ao mesmo tempo, um nome de componente em qualquer número de estruturas ou unions. As definições de nomes de componente sempre acontecem dentro dos especificadores de tipo para as estruturas ou unions. Sempre devem ser usados os nomes de componentes imediatamente seguidos do operador de seleção de membro (-> e .). O nome de um membro deve ser único dentro da struct ou union, mas pode não ser diferente de outros nomes no programa, inclusive os nomes de membros de diferentes structs e unions, ou o nome da própria struct em si.

Identificadores ordinários: Todos os outros nomes entram em um espaço de nomes que inclui variáveis, funções (incluindo parâmetros formais e variáveis locais), e constantes de enumeração. Os nomes dos identificadores possuem visibilidade aninhada, de forma que possam ser redefinidos dentro de blocos de instruções.

Nomes typedef: Os nomes do tipo typedef não podem ser usados como identificadores no mesmo escopo.

Por exemplo, uma vez que os labels de structs, membros de structs, e nomes variáveis estão distribuídos em três espaços de nomes diferentes, os três itens nomeados como resistor neste exemplo não conflitam entre si.. O contexto de cada item permite a interpretação correta de cada ocorrência de resistor no programa6. struct resistor char referencia[20]; int tolerancia; int resistor; resistor;

Quando resistor aparece depois do keyword struct, o compilador reconhece-o como sendo o label da struct. Quando resistor aparece depois de um operador de seleção de membro (-> ou.), o nome se refere ao membro da struct. Em outros contextos, resistor se refere à variável struct. Porém, não é recomendado sobrecarregar o espaço de nomes já que isto dificulta o discernimento do programa.

6 As estruturas serão tratadas no capítulo 16.

Page 113: Tratado Da Linguagem c

E S T R U T U R A D E U M P R O G R A M A

91919191

7777

Page 114: Tratado Da Linguagem c

92929292

88.. TTIIPPOOSS EE DDEECCLLAARRAAÇÇÕÕEESS Este capítulo descreve a declaração e inicialização de variáveis, funções e tipos. A

linguagem C inclui um grupo padrão de tipos de dados. O programador também pode adicionar os seus próprios tipos de dados, chamados de "tipos derivados" pela declaração de novos tipos baseados nos já existentes. Os seguintes tópicos serão discutidos: Declarações Classes de armazenamento Especificadores de tipo Qualificadores de tipo Declaradores e declarações de variáveis. Interpretação de declaradores mais complexos. Inicialização Tipos básicos de armazenamento. Tipos incompletos Declarações typedef Atributos de classes estendidas de armazenamento.

88..11.. DDeeccllaarraaççõõeess

Uma "declaração" especifica a interpretação e os atributos de um grupo de identificadores. A declaração que ocasiona o armazenamento de dados (sendo estes reservados para o objeto ou função nomeada pelo identificador), é chamada de "definição". As declarações em C para variáveis, funções e tipos, apresentam a seguinte sintaxe:

Na forma geral de uma declaração de variável, o especificador de tipo indica o tipo de dado para que será armazenado pela variável. O especificador de tipo pode ser um composto, como quando o tipo é modificado pelas keywords const ou volatile. O declarador fornece o nome da variável, possivelmente modificado para declarar uma ordem ou um tipo de ponteiro. Por exemplo, int const *fp;

declara uma variável chamada fp como um ponteiro para um valor int não modificável (const). Podem ser definidas várias variáveis numa mesma declaração, usando declaradores múltiplos separados por vírgulas.

Uma declaração deve ter pelo menos declarador, ou seu especificador de tipo deve declarar um label de uma struct, union, ou membros de um objeto enum1. Os declaradores provêem qualquer informação restante sobre um identificador. Um declarador é um identificador que pode ser modificado com colchetes ([ ]), asteriscos (*), ou parênteses (( )) para declarar um array, ponteiro, ou tipo de função, respectivamente. Quando forem

1 Estes tipos serão vistos em detalhe no capítulo 16.

Capítulo

8

Page 115: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

93939393

8888

declaradas variáveis simples (tais como caracteres, inteiro, e de ponto flutuante), ou structs e unions de variáveis simples, o declarador é o próprio identificador. .

Todas as definições são declarações implícitas, mas não todas as declarações são definições. Por exemplo, as declarações de variáveis que começam com o especificador de classe de armazenamento extern estão "referenciando", e não "definindo". Se uma variável externa é referenciada antes de ser definida, ou se ela estiver definida em outro arquivo fonte, será necessária uma declaração extern. O local de armazenamento não será alocado somente através de "referencias", nem há inicializações de variáveis nas declarações.

Uma classe de armazenamento ou um tipo (ou ambos) é requerido nas declarações variáveis. Em geral, somente um tipo de classe de armazenamento será permitido em uma declaração, e não todos o especificadores de classe de armazenamento, serão permitidos em todo contexto. O especificador de uma classe de armazenamento de uma declaração, afeta a forma em que o item declarado, armazenado e inicializado, e define quais as partes do programa que poderão referenciá-la.

Os especificadores de classe de armazenamento incluem: auto, extern, register, static e typedef.

A localização da declaração dentro do programa fonte e a presença ou ausência de outras declarações da variável, são fatores importantes na determinação do tempo de vida das variáveis. Poderá haver múltiplas re-declarações mas somente uma definição. Porém, uma definição pode aparecer em mais de uma unidade de tradução. Para objetos com encadeamento interno, esta regra se aplica separadamente para cada unidade de tradução, porque os objetos encadeados internamente são únicos para a unidade de tradução. Para objetos com encadeamento externo, esta regra se aplica ao programa inteiro.

Os especificadores de tipo fornecem alguma informação sobre os tipos de dados dos identificadores. O especificador de tipo default é int. Os especificadores de tipo também podem definir rótulos de tipo, nomes de componentes de structs e unions, e constantes de enumeração2.

Existem dois tipos de qualificadores de tipo: const e volatile. Estes qualificadores especificam propriedades adicionais dos tipos que só são relevantes, quando forem acessados objetos deste tipo por l-values3.

88..22.. CCllaasssseess ddee AArrmmaazzeennaammeennttoo

A "classe de armazenamento" de uma variável determina se o item tem tempo de vida "global" ou "local". A linguagem C chama estes dois tempos de vida como "static" e "automatic". Uma variável com tempo de vida global, existe e tem valor ao longo da execução do todo o programa. Todas as funções têm tempo de vida global.

As variáveis automáticas, ou variáveis com tempo de vida local, tem o seus locais de memória alocados cada vez que o controle da execução passa para o bloco no qual elas estão definidas. Quando a execução retorna, as variáveis já não existirão.

A linguagem C possui os seguintes especificadores de classes de armazenamento:

2 Maiores detalhes no capítulo 16. 3 Maiores detalhes de l-values no capítulo 9.

Page 116: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

94949494

8888

auto register static extern typedef

Somente pode ser utilizado um especificador de classe de armazenamento como declarador em uma declaração. Se nenhum especificador de classe de armazenamento for utilizado, as declarações dentro de um bloco criarão objetos automáticos.

Os itens declarados com os especificadores auto ou register têm tempo de vida local. Os itens declarados com os especificadores static ou extern têm tempo de vida global.

O lugar de colocação das declarações de variáveis e funções dentro de arquivos fonte também afeta a classe de armazenamento e a visibilidade. As declarações que ficam fora das definições de função são ditas de "nível externo". As declarações dentro de definições de função são de "nível interno".

O significado exato de cada especificador de classe de armazenamento, depende de dois fatores: Se a declaração aparece no nível externo ou interno Se o item que está sendo declarado é uma variável ou uma função

88..22..11.. EEssppeecciiffiiccaaddoorreess ddee CCllaassssee ddee AArrmmaazzeennaammeennttoo ppaarraa DDeeccllaarraaççõõeess ddee NNíívveell EExxtteerrnnoo

Variáveis externas são variáveis de escopo de arquivo. Elas são definidas fora de qualquer função, e estão potencialmente disponíveis para muitas funções. As funções podem somente ser definidas em nível externo e, portanto, não podem ser aninhadas. Por default, todas as referências para variáveis externas e funções do mesmo nome são referenciadas ao mesmo objeto, o que permite ter um "encadeamento" externo.

As declarações de variáveis no nível externo são definições de variáveis (declarações que definem), ou referências para variáveis definidas em outro lugar (declarações que referenciam).

Uma declaração de variável externa que também inicializa a mesma (implicitamente ou explicitamente) é uma declaração de definição da variável. Uma definição no nível externo pode tomar várias formas: Uma variável declarada com o especificador de armazenamento static. Uma variável static

pode ser explicitamente inicializada com uma expressão constante, como descrito na seção 8.7 Inicialização. Caso o inicializador fosse omitido, a variável é inicializada com 0 por default. Por exemplo, estas duas declarações são ambas consideradas definições da variável k.

static int k = 16; static int k;

Uma variável que é inicializada explicitamente de nível externo. Por exemplo, int j = 3; é uma definição para a variável j.

Em declarações variáveis em nível externo (quer dizer, fora de todas as funções), podem ser usados os especificadores de classes de armazenamento extern ou static, ou serem omitidos completamente. Os especificadores auto e register não podem ser utilizados em nível externo.

Page 117: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

95959595

8888

Uma vez uma variável é definida em nível externo, ela será visível ao longo do resto da unidade de tradução. Esta variável não será visível antes da sua declaração, no mesmo arquivo fonte. Também, não será visível em outros arquivos fonte do programa, a menos que uma declaração de referencia faça-a visível, como descrito a seguir.

As regras relativas a static incluem: As variáveis declaradas fora de todos os blocos, e sem a keyword static, retêm os seus valores

durante toda a execução do programa. Para restringir o acesso a uma unidade de tradução particular, a keyword static deve ser utilizada. Isto fornece um "encadeamento interno". Para deixá-las globais para o programa inteiro, deve-se omitir a classe de armazenamento explícita, ou usar a keyword extern. Isto resultará num "encadeamento externo". O programador poderá definir uma variável de nível externo apenas uma vez dentro do

programa. Poderão ser definidas outras variáveis com o mesmo nome, usando o especificador de classe de armazenamento static em uma unidade de tradução diferente. Considerando que cada definição estática somente será visível dentro de sua própria unidade de tradução, nenhum conflito acontecerá. Isto fornece um modo útil para esconder nomes de identificadores que devem ser compartilhados entre funções de uma única unidade de tradução, mas que não serão visíveis a outras unidades de tradução. O especificador de classe de armazenamento static também pode ser aplicado a funções. Caso

uma função for declarada static, o seu nome será invisível fora do arquivo no qual é declarada.

As regras para usar extern são: O especificador de classe de armazenamento extern, declara uma referência a uma variável

definida em outro lugar. A declaração extern pode ser utilizada para fazer uma definição de outro arquivo de fonte, visível, ou fazer uma variável antes da sua definição no mesmo arquivo fonte. Uma vez declarada uma referência para a variável em nível externo, esta ficará visível ao restante da unidade de tradução, na qual ocorreu a declaração de referência. Para uma referência extern ser válida, a variável a que se refere, deve estar definida uma vez, e

só uma vez, no nível externo. Esta definição (sem a classe de armazenamento extern) pode estar em quaisquer das unidades de tradução que compõem o programa

O exemplo a seguir mostra as declarações externas. /****************************************************************** Arquivo Fonte UM *******************************************************************/ #include<stdio.h> extern int i; /* Referencia a i, definida mais abaixo */ void next( void ); /* Prototipo de funcao */ extern void other(void); /* Referencia de funcao em outro fonte */ void main() i++; printf( "%d\n", i ); /* i igual a 4 */ next(); int i = 3; /* Definicao de i */ void next( void ) i++; printf( "%d\n", i ); /* i igual a 5 */ other();

Código 8-1

/****************************************************************** Arquivo Fonte DOIS *******************************************************************/ #include<stdio.h> extern int i; /* Referencia a i do */ /* primeiro arquivo fonte */ void other(void) i++;

Page 118: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

96969696

8888

printf( "%d\n", i ); /* i igual a 6 */

Código 8-2

Os dois arquivos fonte deste exemplo contêm um total de três declarações externas de i. Somente uma declaração é uma "declaração de definição". A declaração: int i = 3;

define a variável global i e a inicializa com valor inicial 3. A "declaração de referencia" de i no topo do primeiro arquivo fonte, usando extern, faz a variável global visível antes da sua definição no arquivo. A declaração de referencia de i no segundo arquivo fonte também a faz visível naquele arquivo fonte. Se numa instância a definição de uma variável não for fornecida na unidade de tradução, o compilador assume o tipo, extern int x;

como declaração de referencia e ainda a define como, int x = 0;

sendo visível em outra unidade de tradução do programa.

Todas as três funções, main, next e other, executam a mesma tarefa: incrementam i e a enviam para o dispositivo de saída. Os valores 4, 5, e 6 serão enviados.

Caso a variável i não tivesse sido inicializada explicitamente, teria sido fixada automaticamente com valor 0. Neste caso, os valores 1, 2, e 3 seriam enviados.

88..22..22.. EEssppeecciiffiiccaaddoorreess ddee CCllaassssee ddee AArrmmaazzeennaammeennttoo ppaarraa NNíívveell IInntteerrnnoo

No nível interno, pode ser usado qualquer um dos quatro especificadores de classe de armazenamento para a declaração de variáveis. Quando é omitido o especificador da classe de armazenamento de uma declaração, o especificador default é auto. Por isto, a keyword auto raramente é vista em um programa em C.

OO EEssppeecciiffiiccaaddoorr ddee CCllaassssee ddee AArrmmaazzeennaammeennttoo aauuttoo

O especificador de classe de armazenamento auto, declara uma variável automática, i.e. uma variável com tempo de vida local. Uma variável auto somente será visível no bloco na qual é declarada. As declarações de variáveis auto, podem incluir inicializadores, como será visto seção 8.7 Inicialização. Uma vez que as variáveis com classe de armazenamento auto não são inicializadas automaticamente, pode-se inicializá-la explicitamente na própria declaração, ou designar valores iniciais em instruções dentro do bloco. Os valores de variáveis auto não inicializadas serão indefinidos. (Uma variável local auto ou register é inicializada cada vez que se encontra no escopo em que o inicializador é definido).

Uma variável interna static (com escopo local ou de bloco) pode ser inicializado com o endereço de qualquer item externo ou static, mas não com o endereço de um item auto, já que o endereço deste tipo não é constante.

Page 119: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

97979797

8888

OO EEssppeecciiffiiccaaddoorr ddee CCllaassssee ddee AArrmmaazzeennaammeennttoo rreeggiisstteerr

As variáveis (assim como o programa) são armazenadas na memória. O modificador register indica ao compilador que a variável em questão deve ser, se possível, armazenada num registrador interno da CPU.

As variáveis nos registradores da CPU são acessadas em um tempo muito menor, pois o acesso aos registradores é mais rápido que o acesso à memória. Uma consideração importante é que o especificador não pode ser usado em variáveis globais, já que isto implicaria que um registrador da CPU ficaria o tempo todo reservado para uma variável. Os tipos de dados onde é mais apropriado o uso do especificador register, são os tipos char e int, mas pode-se usá-lo em qualquer tipo de dado. Observar o exemplo a seguir. void main (void) register int count; for (count = 0;count<10;count++) ...

Código 8-3

Nesse exemplo, o loop do for será executado mais rapidamente do que seria se não usássemos o especificador register. Este é o uso mais recomendável para o register, no caso de uma variável que será usada muitas vezes de forma seguida.

AA CCllaassssee ddee AArrmmaazzeennaammeennttoo ssttaattiicc

Uma variável declarada em nível interno com o especificador de classe de armazenamento static tem um tempo de vida global, mas somente será visível dentro do bloco no qual é declarada. Para strings constantes, o uso de static é adequado porque alivia a sobrecarga das freqüentes inicializações em funções que são freqüentemente chamadas.

Se uma variável static não for inicializada explicitamente, será inicializada para 0 por default. Dentro de uma função, a classe static ocasiona a alocação de memória e serve como definição. As variáveis internas static, provêem armazenamento privado permanente, e visibilidade para uma única função.

O funcionamento das variáveis declaradas como static depende se estas foram declaradas como globais ou como locais.

As variáveis globais static funcionam como variáveis globais dentro de um módulo, ou seja, são variáveis globais que não serão conhecidas em outros módulos. Este modificador é útil para isolar trechos de um programa, evitando mudanças acidentais em variáveis globais.

As variáveis locais static conservam o seu valor entre chamadas da mesma função. Observar o exemplo a seguir. int count (void) static int num = 0; num++; return num;

A função count() retorna o número de vezes que ela foi chamada. Pode-se observar que a variável local num é inicializada. Esta inicialização só é efetuada na primeira vez que a função é chamada pois num deverá manter o seu valor de uma chamada para a

Page 120: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

98989898

8888

outra. O que a função executa, é o incremento de num a cada chamada, retornando o seu valor atual.

AA CCllaassssee ddee AArrmmaazzeennaammeennttoo eexxtteerrnn

Uma variável declarada com o especificador de classe de armazenamento extern, é uma referência para uma variável com o mesmo nome definido em nível externo, em qualquer arquivo fonte do programa. A declaração interna extern é usada para fazer a definição da variável de nível externo, visível dentro do bloco. A menos que a variável seja declarada a nível externo, a variável declarada com a keyword extern somente será visível no bloco no qual está declarada.

Este exemplo ilustra as declarações de nível interno e externo: #include <stdio.h> /* biblioteca padrão de funções de entrada e saída de dados */ int i = 1; /* declaração e definição da variável i */ void other( void ); /* declaração de função a ser definida posteriormente */ void main() extern int i; /* Referencia à variável i, definida acima: */ static int a; /* O valor inicial é zero; a é visível somente dentro da main: */ register int b = 0; /* b é armazenada em um registrador, se possível: */ int c = 0; /* A classe de armazenamento default é auto: */ printf( "%d\n%d\n%d\n%d\n", i, a, b, c );

/* Os valores impressos serão 1, 0, 0, 0: */ other(); return; void other( void ) static int *external_i = &i; /* O endereço da variável global i é designado a uma variável ponteiro: */ int i = 16; /* i é redefinida; a variável i global não é mais visível: */ static int a = 2; /* Esta variável a é visível somente dentro desta função: */ a += 2; printf( "%d\n%d\n%d\n", i, a, *external_i ); /* O valore impresso serão 16, 4, e 1: */

Código 8-4

Nesse exemplo, a variável i é definida em nível externo com valor inicial 1. Uma declaração extern na função main é usada para declarar a referência à variável de nível externo i. A variável static a é inicializada para 0 por default, uma vez que o inicializador foi omitido. A chamada de printf imprime os valores 1, 0, 0, e 0.

Na função other, o endereço da variável global i é usado para inicializar o ponteiro static para a variável externa i. Isto funciona porque a variável global tem tempo de vida static, o que significa que seu endereço não muda durante a execução do programa. Logo após, a variável i é redefinida como uma variável local, com valor inicial igual a 16. Esta redefinição não afeta o valor da variável de nível externo i, que fica escondida para o uso de seu nome pela variável local. O valor da i global fica acessível somente de forma indireta dentro deste bloco, através do ponteiro externo external_i. Se for tentado designar um endereço para a variável auto i para um ponteiro, isto não funcionará, uma vez que este endereço pode ser diferente cada vez que o programa inicia a execução do bloco. A variável

Page 121: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

99999999

8888

a está declarada como uma variável static e inicializado com valor igual a 2. Esta variável a não conflita com a variável a da função main, uma vez que variáveis static de nível interno são visíveis somente dentro do bloco no qual são declaradas.

A variável a é incrementada de 2, dando o valor 4 como resultado. Se a função other fosse chamada novamente no mesmo programa, o valor inicial de a seria 4. As variáveis internas static mantêm os seus valores quando o programa sai e novamente entra no bloco no qual estão declaradas.

O especificador extern define variáveis que serão usadas em um arquivo fonte apesar de terem sido declaradas em outro. Programas grandes podem ser divididos em vários arquivos (módulos) que serão compilados separadamente. Diga-se por exemplo, que para um programa grande existam duas variáveis globais: um inteiro count e um float sum. Estas variáveis são declaradas normalmente em um dos módulos do programa. Por exemplo: /*Módulo 1*/ int count; float sum; main (void) ... return 0;

Código 8-5

Num outro módulo do mesmo programa, há uma rotina que deve usar as variáveis globais do módulo acima. Por exemplo, a rotina RetornaCount() retorna o valor atual de count. Para o segundo módulo poder acessar as variáveis do primeiro, estas deverão ser declaradas com o modificador de armazenamento extern. /*Módulo 2*/ extern int count; /* variável do módulo 1 */ extern float sum; /* variável do módulo 1 */ int RetornaCount (void) return count;

Código 8-6

Desta forma, o compilador saberá que count e sum estão sendo usados neste módulo, mas foram declarados em outro.

88..33.. EEssppeecciiffiiccaaddoorreess ddee TTiippoo

Os especificadores de tipo nas declarações, definem o tipo da variável ou declaração de função. Os especificadores de tipo são listados a seguir. void char short int long float double signed unsigned especificador-de-struct especificador-de-union

Page 122: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

100100100100

8888

especificador-de-enum nome-de-typedef

Os tipos char, signed int, signed short int e signed long int, junto com as suas contrapartidas unsigned, são chamados "tipos inteiros". Os especificadores de tipo float, double e long double são referenciados como "flutuantes" ou de "ponto flutuante". Pode-se utilizar qualquer especificador inteiro ou de ponto flutuante em uma variável, ou na declaração de uma função. Se o especificador de tipo não for definido na sua declaração, o tipo default é o int.

As keywords signed e unsigned são opcionais e podem preceder qualquer um dos tipos inteiros, exceto enum, e podem também ser usados como único tipo de especificador e neste caso fica subentendido como um unsigned int e signed int respectivamente. Quando usado só o especificador int, é assumido sendo signed. Quando usados os especificadores long e short somente, o compilador entenderá como sendo long int e short int.

Os tipos enum são considerados tipos básicos. Os especificadores para o tipo enum serão discutidos no capítulo 16.

A keyword void tem três usos: especificar um tipo de retorno de função, especificar uma lista de tipos de argumentos para uma função que não possui argumentos, e especificar um ponteiro para um tipo de dado não especificado. Pode-se usar o tipo void para declarar funções que não retornam nenhum valor, ou para declarar um ponteiro para um tipo não especificado. .

Podem ser criados especificadores de tipo adicionais utilizando a declaração typedef, como será visto mais adiante.

88..33..11.. EEssppeecciiffiiccaaddoorreess ddee TTiippooss ddee DDaaddooss ee sseeuuss EEqquuiivvaalleenntteess

Na tabela a seguir são listados os especificadores de tipos de dados e seus equivalentes.

Especificador de Tipo

Equivalente(s)

signed char char signed int signed, int signed short int short, signed short signed long int long, signed long unsigned char — unsigned int unsigned unsigned short int unsigned short unsigned long int unsigned long float — double — long double Double

Tabela 8-1 – Especificadores de Tipo

Alguns compiladores para microcontroladores, tais como o PCW da CCS para microcontroladores PIC, não seguem este padrão. Por exemplo, no compilador citado acima, o tipo default é unsigned, ou seja, se for declarada uma variável ou função sem colocar explicitamente o tipo signed, o default será unsigned, ao contrário dos compiladores para PC.

Page 123: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

101101101101

8888

Os tipos double e long double também podem não existir em compiladores para microcontroladores, ou simplesmente são tratados como sendo do tipo float.

88..44.. QQuuaalliiffiiccaaddoorreess ddee TTiippoo

Os qualificadores atribuem uma de duas propriedades para um identificador. O qualificador de tipo const declara um objeto que não pode ser modificado. O qualificador de tipo volatile declara um item cujo valor pode ser mudado legitimamente por qualquer elemento além do controle do programa no qual ele aparece, tal como uma tarefa concorrente de execução.

Os dois qualificadores de tipo, const e volatile, só podem aparecer uma vez em uma declaração. Os qualificadores de tipo podem aparecer juntamente com qualquer outro especificador de tipo; porém, eles não podem aparecer depois da primeira vírgula em uma declaração múltipla de variáveis. Por exemplo, as seguintes declarações estão corretas: typedef volatile int VI; const int ci;

As seguintes declarações são incorretas: typedef int *i, volatile *vi; float f, const cf;

Os qualificadores de tipo são os seguintes: const volatile

As declarações seguintes são válidas: int const *p_ci; /* Ponteiro para uma constante int */ int const (*p_ci); /* Ponteiro para uma constante int */ int *const cp_i; /* Ponteiro constante para um int */ int (*const cp_i); /* Ponteiro constante para um int */ int volatile vint; /* Inteiro volátil */

Se a especificação de um tipo array inclui qualificadores de tipo, o elemento será qualificado, não o tipo de array. Se a especificação do tipo de função incluir qualificadores, o comportamento é indefinido. Nem volatile nem const afetarão a faixa valores ou propriedades aritméticas do objeto. A lista a seguir descreve como usar const e volatile. A keyword const poderá ser usada para modificar qualquer tipo fundamental ou agregado, ou

um ponteiro para um objeto de qualquer tipo, ou um typedef. Se uma variável for declarada com qualificador const, seu tipo será considerado como sendo const int. Uma variável const pode ser inicializada ou pode ser colocada numa região de memória de armazenamento, somente de leitura. A keyword const é útil para declarar ponteiros para const, já que isto evitará que a função consiga mudar o ponteiro. O compilador assume que, para qualquer ponto no programa, uma variável volátil pode ser

acessada por um processo desconhecido, que usa ou modifica seu valor. Se a keyword volátil for usada sem o especificador de tipo, será assumido o tipo int. O

especificador volatile pode ser usado para prover acesso adequado a posições especiais de memória. Deve-se utilizar volatile em objetos de dados que podem ser acessados ou alterados por manipuladores (handlers), programas concorrentes, ou por um hardware especial tal como registradores de controle de I/O mapeados em memória. Uma variável pode ser declarada como volatile para o seu tempo de vida, ou pode ser convertida (cast) uma única referência para ser volatile. Uma variável pode ser const e volátil, neste caso, a variável não poderá ser modificada

legitimamente por seu próprio programa, mas poderá ser modificada por algum outro processo assíncrono.

Page 124: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

102102102102

8888

Observar o seguinte exemplo: const float PI = 3.1415926536;

Pode-se observar no exemplo que as variáveis com o modificador const podem ser inicializadas, mas não alteradas. Neste caso, a variável PI não poderia ser alterada em qualquer outra parte do programa. Se o programador tentar modificar PI o compilador gerará um erro de compilação.

A utilidade mais importante de const, não é a de declarar variáveis constantes no programa. Seu uso mais comum é evitar que um parâmetro de função seja alterado pela mesma. Isto é muito útil no caso de um ponteiro, pois o conteúdo de um ponteiro pode ser alterado por uma função. Para tanto, basta declarar o parâmetro como const. Observar o seguinte exemplo: #include <stdio.h> void sqr (const int *num); main (void) int a = 10; int b; b = sqr(&a); int sqr (const int *num) return ((*num)*(*num));

Código 8-7

Nesse exemplo, num está protegida contra alterações. Isto quer dizer que, caso se tente fazer *num=10;

dentro da função sqr() o compilador geraria uma mensagem de erro.

O modificador volatile indica ao compilador que a variável em questão pode ser alterada por outros aplicativos sem que o programa seja avisado. Por exemplo, no caso de uma variável que o BIOS do computador altera de minuto em minuto (um relógio). Seria apropriado que esta variável seja declarada com o especificador de acesso volatile.

88..55.. DDeeccllaarraaççõõeess ddee VVaarriiáávveeiiss

O resto deste capítulo descreve a forma e significado das declarações para tipos variáveis resumidos na Tabela 8-2. Em particular, as seções seguintes explicam como declarar os itens que seguem: Tipo de Variável

Descrição

Variáveis simples

Variáveis de valor único de tipo inteiro ou de ponto flutuante

Arrays Variáveis compostas de coleção de elementos do mesmo tipo Ponteiros Variáveis que apontam para outras variáveis e armazenam a localização das

variáveis (na forma de endereços) em vez dos valores destas. Variáveis de enumeração (enum)

Variáveis simples do tipo inteiro que armazenam um valor de um grupo de constantes de inteiro em seqüência

Estruturas (structs)

Variáveis compostas de uma coleção de valores que podem ter tipos diferentes

Unions Variáveis compostas de vários valores de tipos diferentes que ocupam o mesmo espaço de memória de armazenamento

Tabela 8-2 – Tipos de variáveis

Page 125: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

103103103103

8888

O declarador é a parte de uma declaração que especifica o nome que será introduzido no programa.

Os declaradores podem ser usados para declarar arrays de valores, ponteiros para valores, e funções que devolvem valores de um tipo especificado. .

Quando um declarador consiste em um identificador que não pode ser modificado, o item sendo declarado terá um tipo de base. Se um asterisco (*) aparece à esquerda de um identificador, o tipo é modificado para ser um tipo de ponteiro. Se o identificador é seguido de colchetes ([ ]), o tipo é modificado para um tipo array. Se o identificador é seguido de parênteses, o tipo é modificado para um tipo função.

Cada declarador declara pelo menos um identificador. Um declarador deve incluir um especificador de tipo para formar uma declaração completa. O tipo de especificador dá o tipo de elementos para um array, o tipo de objeto endereçado por um ponteiro, ou o tipo de retorno de uma função.

Os exemplos seguintes ilustram algumas formas simples de declaradores: int list[20]; / * Declara um array de 20 valores do tipo int chamado list * / char *cp; /* Declara um ponteiro para um valor do tipo char */ double func(void); /* Declara uma função chamada func, sem argumentos, que retorna um valor do tipo double */ int *aptr[10] /* Declara um array de 10 ponteiros */

88..55..11.. DDeeccllaarraaççããoo ddee VVaarriiáávveeiiss SSiimmpplleess A declaração de uma variável simples, especifica o nome da variável e o tipo. Isto

também especifica a classe de armazenamento da variável e o tipo de dado.

As classes de armazenamento ou o tipo (ou ambos) são requeridos nas declarações de variáveis. Se não for declarado o tipo de variável, o tipo default é o int.

Pode ser utilizada uma lista de identificadores separados por vírgulas (,) para especificar várias variáveis na mesma declaração. Todas as variáveis definidas nesta declaração devem ser do mesmo tipo. Por exemplo: int x, y; /* Declara duas variáveis simples do tipo int */ int const z = 1; /* Declara um valor constante do tipo int com nome de z */

As variáveis x e y podem armazenar qualquer valor no grupo definido para o tipo int. A variável simples z é inicializada com o valor 1 e não pode ser modificada pelo programa por causa da classe de armazenamento const.

Se a declaração de z for para uma variável estática não inicializada ou com escopo de arquivo, receberá o valor inicial de 0, e depois não poderá ser modificada. unsigned long reply, flag; /* Declara duas variáveis chamadas reply e flag */

Nesse exemplo, ambas variáveis, reply e flag, são do tipo unsigned long e armazenam valores inteiros sem sinal.

88..55..22.. DDeeccllaarraaççõõeess ddee EEnnuummeerraaççããoo44 Uma enumeração consiste em um grupo de constantes inteiras nomeadas. Uma

declaração de tipo enumeração dá o nome do (opcional) label de enumeração e define o 4 Maiores detalhes serão tratados na seção 16.3.

Page 126: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

104104104104

8888

grupo de identificadores inteiros nomeados (chamado de "grupo de enumeração", "constantes de enumeração", "enumeradores", ou "membros").

Uma variável do tipo enumeração armazena um dos valores do grupo de enumeração, definido por aquele tipo. As variáveis do tipo enum podem ser usadas em expressões indexadas e como operandos de qualquer operador aritmético ou relacional.

As enumerações proporcionam uma alternativa para a diretiva de pré-processador #define, com a vantagem de que os valores podem ser gerados pelo programador obedecendo as regras de normais de escopo.

No padrão ANSI C, as expressões que definem o valor de uma constante enum sempre serão do tipo int; assim, o espaço de armazenamento associado com uma variável de enumeração é o espaço requerido para um único valor de int. Uma constante de enumeração ou um valor de um tipo enumerado pode ser usado em qualquer lugar da linguagem C que permita uma expressão inteira.

O identificador (que é opcional) nomeia o tipo de enumeração definido pela lista de enumeração. Este identificador é chamado freqüentemente de "tag" da enumeração especificada pela lista. Um especificador da forma

enum identificador lista-de-enumeração

declara o identificador como sendo o tag da enumeração, definida pela lista de enumeração. A lista de enumeração define o "conteúdo enumerador". A lista de enumeração é descrita em detalhes a seguir.

Se a declaração de um tag é visível, as declarações subseqüentes que usem o tag mas omitam a lista de enumeração, especificarão o tipo enum previamente declarado. O tag tem que se referir ao tipo de enumeração definido, o mesmo deve estar em no escopo. Considerando que o tipo de enumeração possa ser definido em outro lugar, a lista de enumeração não aparece nesta declaração. As declarações de tipos derivadas de enumerações, e declarações typedef para tipos de enumeração podem usar os tags de enumeração antes que o tipo de enumeração seja definido.

Cada constante de enumeração de uma lista de enumeração nomeia um valor do conjunto de valores. Por default, à primeira constante de enumeração é associado o valor 0. A próxima constante de enumeração da lista será associada como valores consecutivos, a menos que seja explicitamente associada com outro valor. O nome de uma constante de enumeração é equivalente a seu valor.

Podem ser usado constantes-de-enumeração = expressão-constante para sobrescrever a seqüência padrão de valores. Assim, se a constante-de-enumeração = expressão-constante aparecer na lista de enumeradores, a constante-de-enumeração será associada com o valor dado pela expressão-constante. A expressão-constante deve ser do tipo int e não pode ser um número negativo.

As regras seguintes se aplicam aos membros de um grupo de enumeração: Um conjunto de enumerações pode conter valores constantes duplicados. Por exemplo, se

poderia associar o valor 0 com dois identificadores diferentes, talvez nomeados nulo e zero, no mesmo conjunto.

Page 127: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

105105105105

8888

Os identificadores na lista de enumeração devem ser distintos de outros identificadores no mesmo escopo e com a mesma visibilidade, inclusive nomes de variáveis ordinárias e identificadores de outras listas de enumeração. Os tags de enumeração obedecem as regras de escopo normais. Eles devem ser diferentes de

qualquer outra enumeração, tag de estrutura ou de união que tenham a mesma visibilidade.

Estes exemplos ilustram declarações de enumeração: enum DIA /* Define um tipo de enumeração */ Sábado, /* chamado DIA e declara uma */ Domingo = 0, /* variável chamada dias_uteis com */ Segunda_feira, /* este tipo */ Terça_feira, Quarta_feira, /* Quarta_feira é associada ao valor 3 */ Quinta_feira, Sexta_feira dias_uteis;

O valor 0 é associado com Sabado por default. O identificador Domingo é fixado explicitamente em 0. Para os identificadores restantes são determinados os valores de 1 até 5 por default.

Neste exemplo, um valor do grupo DIA é designado para a variável hoje. enum DIA hoje = Quarta_feira;

Notar que o nome da constante de enumeração, é utilizada para designar o valor. Considerando que o tipo de enumeração DIA foi previamente declarado, somente será necessário o tag DIA para a utilização.

Para designar um valor inteiro explícito para uma variável de um tipo de dado enumerado, pode ser usado o seguinte cast: 5 dia_util = ( enum DIA ) ( valor_do_dia - 1 );

Este cast é recomendado mas não é necessário. enum BOOLEAN /* Declara tipo de dado de enumeração chamado BOOLEAN */ false, /* false = 0, true = 1 */ true ; enum BOOLEAN end_flag, match_flag; /* Duas variáveis do tipo BOOLEAN */

Esta declaração também pode ser especificada como enum BOOLEAN false, true end_flag, match_flag;

ou como enum BOOLEAN false, true end_flag; enum BOOLEAN match_flag;

Um exemplo que usa estas variáveis pode ser visto a seguir. if ( match_flag == false ) . . /* instruções */ . end_flag = true;

Também podem ser declarados tipos de dados enum sem nome. O nome do tipo de dado é omitido, mas as variáveis devem ser declaradas. A variável resposta é uma variável do tipo definido: enum sim, nao resposta;

5 O cast ou modelador, é basicamente um conversor de tipo. Ele pode ser aplicado a uma expressão ou a uma variável em particular. O cast força a expressão ao tipo especificado, sem mudar o valor original das variáveis envolvidas. Este item será tratado com maiores detalhes na seção 9.4.

Page 128: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

106106106106

8888

88..55..33.. DDeeccllaarraaççõõeess ddee EEssttrruuttuurraass66 Uma declaração de estrutura nomeia um tipo e especifica uma sucessão de valores

variáveis (chamados de membros, atributos ou campos da estrutura), que podem ser de tipos diferentes. Um identificador opcional, também chamado tag dá o nome ao tipo estrutura e pode ser usado em referências subseqüentes para o tipo. Uma variável daquele tipo de estrutura armazenará uma seqüência inteira definida pelo próprio tipo. As estruturas em C são semelhantes aos tipos conhecidos como "registros" em outras linguagens de programação e em bancos de dados.

A declaração de um tipo de estrutura não aloca espaço de memória para a estrutura. Esta define somente um modelo para declarações posteriores de variáveis deste tipo de estrutura.

Pode ser utilizado um identificador previamente definido (tag) para se referenciar ao tipo de estrutura definido. Neste caso, a declaração da lista da estrutura não pode ser repetida enquanto a sua definição seja visível. Declarações de ponteiros para estruturas e typedefs para tipos de estrutura podem ser usados antes da definição do tipo de estrutura. Porém, a definição de estrutura deve ser encontrada antes de qualquer uso. Esta é uma definição incompleta do tipo e do tag. Para que a definição seja completa, a definição do tipo deverá aparecer mais tarde no mesmo escopo.

A lista da declaração da struct e nomes dos membros da estrutura. Um argumento da lista de declaração contém uma ou mais variáveis ou declarações de campos de bits.

Cada variável declarada na lista é definida como uma membro do tipo de estrutura. As declarações de variáveis dentro da lista têm a mesma forma que as outras declarações de variáveis discutidas neste capítulo, exceto que as declarações não podem conter especificadores de classe de armazenamento nem inicializadores. Os membros da estrutura podem ser qualquer tipo variável exceto o tipo void, um tipo incompleto, ou um tipo função.

Um membro não pode ser declarado para ser do tipo da estrutura na qual ele aparece. Porém, um membro pode ser declarado como um ponteiro ao tipo de estrutura no qual aparece contanto que o tipo de estrutura tenha um tag. Isto permite criar listas encadeadas de estruturas.

A estruturas seguem o mesmo escopo que os outros identificadores. Os identificadores da estrutura devem ser diferentes de outros tags de structs, unions, e enums com a mesma visibilidade.

Cada declaração struct em uma lista deve ser única dentro dela. Porém, os nomes dos identificadores numa declaração de struct não precisam ser diferentes dos nomes de variáveis comuns ou de identificadores de outras listas de declaração de estrutura.

Estruturas aninhadas podem ser acessadas como se elas fossem declarados em nível de escopo de arquivo. Por exemplo, colocando esta declaração: struct a int x; struct b int y; var2;

6 Maiores detalhes sobre struct na seção 16.1.

Page 129: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

107107107107

8888

var1;

estas declarações serão válidas: struct a var3; struct b var4;

O exemplo a seguir ilustra uma declaração de struct: struct CanalSerial /* Define uma variável estrutura chamada com1 */ char nome[20]; long baudrate; int bits; com1;

A estrutura CanalSerial possui três membros: nome, baudrate e bits. O nome é um membro do tipo array com vinte elementos char, baudrate e bits são variáveis simples do tipo long int e int, respectivamente. O identificador CanalSerial é o identificador da estrutura. struct CanalSerial com2,com3,com4;

O exemplo acima define três variáveis struct: com2, com3 e com4. Cada estrutura tem a mesma lista de três membros. Os membros são declarados para ter o tipo de struct CanalSerial definido no exemplo anterior. struct /* Define uma struct anônima e uma */ /* variável struct chamada complex */ float x, y; complex;

A estrutura complex possui dois membros com tipo float, x e y. O tipo de estrutura não possui um tag e portanto é anônima. struct sample /* Define uma variável de estrutura chamada x */ char c; float *pf; struct sample *next; x;

Os primeiros dois membros da estrutura são uma variável char e um ponteiro para um valor float. O terceiro membro é declarado como sendo um ponteiro para uma estrutura do mesmo tipo no qual está sendo definido (sample). Isto pode ser interessante, já que permitiria, que uma variável do tipo sample, armazene o seu próprio endereço de armazenamento, por exemplo. Isto é extremamente útil, quando se trabalha com listas encadeadas ou na criação dinâmica de objetos, que é a base da programação orientada a objetos.

As estruturas anônimas são usuais quando não é necessária a utilização de um tag. Isto acontece quando uma única declaração define todas as instâncias da estrutura. Por exemplo: struct int x; int y; mystruct;

Estruturas integradas são freqüentemente anônimas. struct somestruct struct /* estrutura anónima */ int x, y; point; int type; w;

Page 130: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

108108108108

8888

CCaammppooss ddee BBiittss

Além dos declaradores para os membros de uma estrutura ou union, um declarador de estrutura pode também especificar um número de bits, chamado de "campos de bits". O seu comprimento é definido do declarador para o nome do campo seguido do símbolo de dois pontos (:). O campo de bits será um tipo inteiro.

A expressão constante especifica a largura do campo em bits. O especificador de tipo do declarador deve ser unsigned int, signed int, ou int, e a expressão constante deve ser um valor positivo. Se o valor é zero, a declaração não possuirá declarador. Não é permitido na maioria dos compiladores arrays de campos de bits, ponteiros para campos de bits e funções que retornem campos de bits. O declarador opcional nomeia o campo de bits. Os campos de bits só podem ser declarados como parte de uma estrutura. O operador endereço (&) não pode ser aplicado a componentes de campos de bit.

Campos de bits sem nome não poderão ser referenciados, e o seu conteúdo em tempo de execução é impossível de predizer. Os campos de bits podem ser usados como "campos reservados", para propósitos de alinhamento. Um campo de bits anônimo cuja largura é especificada como sendo 0, garante que o espaço de armazenamento para o membro seguinte da lista começará no início de um int.

Os campos de bits serão tão longos quanto for suficiente para conter o conjunto de bits.

Por exemplo, estas duas declarações são incorretas: short a:17; /* incorreto! */ int long y:33; /* incorreto! */

O exemplo a seguir define um array de duas dimensões chamado screen. struct unsigned short icon : 8; unsigned short color : 4; unsigned short underline : 1; unsigned short blink : 1; screen[25][80];

O array contém 2000 elementos. Cada elemento é uma estrutura individual contendo quatro membros de campos de bits: icon, color, underline e blink. O tamanho de cada estrutura é de dois bytes.

Os campos de bits têm a mesma semântica que os tipos integrais. Isto permite que um campo de bit possa ser usado em expressões da mesma forma que uma variável do mesmo tipo, com a diferença de quantos bits estarão no campo de bits.

Em geral os campos de bits são definidos como inteiros e tratados como unsigned, mas pode mudar de compilador para compilador. Alguns compiladores permitem o uso dos tipos char e long (signed ou unsigned) para os campos de bits.

Na maioria dos compiladores, os campos de bits são alocados começando como bit menos significativo até o bit mais significativo, como mostra o seguinte código: struct mybitfields unsigned short a : 4; unsigned short b : 5; unsigned short c : 7; test; void main( void )

Page 131: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

109109109109

8888

test.a = 2; test.b = 31; test.c = 0;

Código 8-8

Os bits serão arranjados da seguinte maneira:

00000001 11110010

cccccccb bbbbaaaa

Na família de processadores 80x86 o armazenamento de um valor inteiro é feito de forma que o byte menos significativo é armazenado antes do mais significativo, ou seja, o inteiro 0x01F2 acima será armazenado na memória física como 0xF2 seguido de 0x01.

Os campos de bits são muito úteis quando se trabalha com sistemas onde a várias informações diferentes são codificadas num único byte, como por exemplo, em protocolos de comunicação (CAN, Profibus e outros), em palavras de status, etc..

Em geral, os membros da estrutura são armazenados seqüencialmente na ordem em que eles são declarados: o primeiro membro tem um endereço de memória menor, e o último membro o maior.

88..55..44.. DDeeccllaarraaççõõeess ddee UUnniioonnss77 Uma declaração de union especifica um conjunto de valores e, opcionalmente, um

tag que nomeia a mesma. Os valores variáveis são chamados de membros da union e podem ter diferentes tipos.

Uma variável do tipo union armazena um dos valores definidos pelo seu tipo. As mesmas regras que regem as estruturas são aplicadas às declarações das unions. As unions também podem ter campos de bits.

Os membros das unions não podem ser do tipo incompleto, void e nem tipo função. Assim os membros não podem ser uma instância da própria union mas podem ser ponteiros para o tipo union no qual são declarados.

A declaração do tipo union é somente um modelo. Não será reservada memória até que uma variável seja declarada.

Os membros de uma variável union, compartilham o mesmo espaço de memória. Se for declarada uma union com dois tipos, e um valor for armazenado, mas a union for acessada pelo outro tipo, o resultado armazenado será incorreto. Por exemplo, considerar a declaração da union de um float e um int. Um valor float é armazenado, mas o programa depois acessa este valor como um int. Neste tipo de situações, o valor dependerá da forma de armazenamento interno do valor float. O valor inteiro não será o correto. Ver o exemplo a seguir. union uni int i; float f; ; void main(void)

7 Maiores detalhes serão tratados na seção 16.2.

Page 132: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

110110110110

8888

union uni x; x.f = 6000.0; printf("Valor de uni.f = %d",x.i);

Código 8-9

Usando o compilador BC3.1 (16 bits) o valor na tela será –32768. Veja a seguir mais um exemplo. union sign /* Uma definição e uma declaração*/ int svar; unsigned uvar; number; void main (void) number.svar = -66; printf("\n O valor de number.svar = %d",number.svar); printf("\n O valor de number.uvar = %u",number.uvar);

Código 8-10

Nesse exemplo é definida a variável union com o tipo sign e declarada uma variável chamada number que possui dois membros: svar, um signed int, e uvar, que é um unsigned int. Esta declaração permite que o valor corrente do número seja armazenado como signed ou como unsigned. O tag associado a este tipo de union é sign. union /* Define um array de duas dimensões chamado screen */ struct unsigned int icon : 8; unsigned color : 4; window1; int screenval; screen[25][80];

O array screen contém 2000 elementos. Cada elemento do array é uma union individual de dois membros: window1 e screenval. O membro window1 é uma estrutura com dois membros com campos de bits, icon e color. O membro screenval é um int. Em qualquer momento, cada elemento da union, armazena ou um inteiro representado por screenval ou uma estrutura representada por window1.

AArrmmaazzeennaammeennttoo ddaass UUnniioonnss

O espaço de armazenamento associado com uma variável union é o espaço requerido para o maior membro da mesma. Quando o menor dos membros for armazenado, a variável union poderá conter espaço de memória não utilizado. Todos os membros são armazenados no mesmo espaço de memória e começam no mesmo endereço. O valor armazenado é sobrescrito cada vez que um valor é designado para membros diferentes. Por exemplo: union /* Define uma union chamada x */ char *a, b; float f[20]; x;

Os membros da union x são, na ordem da sua declaração, um ponteiro para um valor char, um valor char, e um array para vinte valores do tipo float. O espaço alocado para x é o espaço requerido para o array de 20 elementos f, já que f é o maior membro da union. Nesse exemplo não foi associado nenhum tag para a union, e portanto será anônima.

Page 133: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

111111111111

8888

88..55..55.. DDeeccllaarraaççõõeess ddee AArrrraayyss88 Uma declaração de array identifica o array e especifica o tipo dos seus elementos.

Também pode definir o número de elementos componentes do mesmo. Uma variável do tipo array é considerada como um ponteiro para o tipo dos elementos do array.

A sintaxe tem duas formas: A primeira forma define uma variável array. O argumento da expressão constante, se presente,

deve ser do tipo inteira, maior que zero. Cada elemento tem o tipo dado pelo especificador de tipo, que pode ser de qualquer tipo exceto void. Um elemento de um array não pode ser do tipo função. A sintaxe básica pode ser:

especificador-de-tipo declarador [expressão-constante] A segunda forma declara uma variável que tem sido definida em outro lugar do código. Esta

forma omite o argumento da expressão constante entre colchetes, mas não os colchetes. Esta forma pode ser usada somente se o array foi previamente inicializado, declarado como parâmetro ou declarado com referência para um array explicitamente definido em outro lugar do programa.

especificador-de-tipo declarador [ ]

Em ambas formas, os declaradores diretos nomeiam a variável, e podem modificar o seu tipo. Os colchetes seguido pelo declarador direto modifica o declarador para um tipo array.

Os qualificadores de tipo podem aparecer na declaração de um objeto do tipo array, mas os qualificadores se aplicam aos elemento no lugar do array em si.

Pode-se declarar um array de arrays (array multidimensional) pela colocação no declarador de array com uma lista de constantes encerradas em colchetes desta forma:

especificador-de-tipo declarador [expressão-constante] [expressão-constante] ...

Cada expressão constante entre colchetes define o número de elementos em uma dada dimensão: arrays de duas dimensões tem duas expressões entre colchetes; arrays em três dimensões têm três; e assim sucessivamente. A primeira expressão constante pode ser omitida se o array foi previamente inicializado, declarado como um parâmetro, ou declara do como uma referência para um array explicitamente definido em alguma parte do programa.

Podem ser definidos arrays de ponteiros para vários tipos de objetos pelo uso de declarações mais complexas como descrito em outras seções.

Os arrays são armazenados em linhas. Por exemplo, o seguinte array consiste de duas linhas com três colunas cada:

char A[2][3];

As três colunas da primeira linha são armazenadas inicialmente, seguidas pelas três colunas da segunda linha. Isto permite que o último subscrito varie de forma mais rápida.

8 Maiores detalhes serão vistos no capítulo 11.

Page 134: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

112112112112

8888

Os exemplos a seguir ilustram algumas declarações de arrays. float matrix[10][15];

Este array bidimensional nomeado matrix possui 150 elementos do tipo float. O primeiro elemento do array seria matrix[0][0], e o ultimo seria matrix[9][14]. struct float x, y; complex[100];

Esta é uma declaração de um array de estruturas. Este array possui 100 elementos (complex[0] a complex[99]); cada elemento é uma estrutura contendo dois membros do tipo float.9 extern char *name[];

Esta instrução declara o tipo e o nome de um array de ponteiros para char. A definição do array ocorre em outro lugar.

AArrmmaazzeennaammeennttoo ddee AArrrraayyss

O espaço de armazenamento associado a um tipo array é o espaço requerido para armazenar todos os seus elementos. Os elementos de uma array são armazenados em posições de memória contígua e crescente a partir do primeiro elemento até o último.

88..55..66.. DDeeccllaarraaççõõeess ddee PPoonntteeiirrooss Uma declaração de ponteiro define uma variável do tipo ponteiro, e especifica o

tipo de objeto para o qual a mesma aponta. Uma variável declarada como ponteiro armazena um endereço de memória.

A sintaxe básica é a seguinte:

especificador-de-tipo * declarador;

O especificador de tipo indica o tipo de objeto que será apontado, que pode ser um tipo de variável simples, estrutura ou union. As variáveis do tipo ponteiro, podem apontar para funções, arrays e outros ponteiros.

Fazendo o especificador de tipo void, pode-se deixar em aberto a especificação do tipo para a qual o ponteiro se refere. Este tipo de especificação é referido com o nome de “ponteiro para void” e é escrito como void *. Uma variável declarada como um ponteiro para void pode ser usado para apontar para qualquer tipo de objeto. Porém, executar a maioria das operações no ponteiro ou no objeto para o qual aponta, o tipo para qual aponta, devem ser especificados “pontos”, explicitamente para cada operação (variáveis do tipo char* e tipo void* são geralmente compatíveis sem necessidade de casts). Tais conversões podem ser feitas pela utilização de casts, que serão vistos na seção 9.4.

O qualificador de tipo pode ser const, volatile, ou ambos. Estes especificam, respectivamente, que o ponteiro não pode ser modificado pelo próprio programa (const), ou que o ponteiro pode ser modificado por algum processo externo ao controle do programa (volatile). 9 Quando se trabalha com arrays, não deve ser esquecido que o elemento zero é sempre o primeiro, e conta como uma posição de armazenamento. Por exemplo, se for definido o array matriz[56], o último elemento será matriz[55]. A regra geral é, o índice do último elemento será o valor definido de posições menos uma, que é a posição zero.

Page 135: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

113113113113

8888

O declarador nomeia a variável e pode incluir um modificador de tipo. Por exemplo, se o declarador representa um array, o tipo do ponteiro é modificado para ser um ponteiro a um array.

Podem ser declarados ponteiros para tipos struct, union, e enum antes destes serem definidos. Pode-se declarar o ponteiro usando o tag da estrutura ou union como mostrado nos exemplos a seguir. Tais declarações são permitidas porque o compilador não precisa saber o tamanho da estrutura ou union para alocar espaço para a variável ponteiro

Os seguintes exemplos ilustram algumas declarações para ponteiros. char *message; /* Declara uma variável ponteiro chamda message */

O ponteiro message aponta para uma variável do tipo char. int *pointers[10]; /* Declara um array de ponteiros*/

O array de ponteiros possui 10 elementos, cada elemento é um ponteiro para uma variável do tipo int. int (*pointer)[10]; /* Declara um ponteiro para um array de 10 elementos */

A variável pointer aponta para um array com 10 elementos. Cada elemento do array é do tipo int. int const *x; /* Declara a variável ponteiro x, para um valor constante */

O ponteiro x pode ser modificado para apontar um valor int diferente, mas o valor para o qual aponta não pode ser modificado. const int some_object = 5 ; int other_object = 37; int *const y = &fixed_object; const volatile *const z = &some_object; int *const volatile w = &some_object;

A variável y é declarada como um ponteiro constante para um valor int. O valor para o qual aponta pode ser modificado, mas o próprio ponteiro em si, sempre tem que apontar para a mesma localização: o endereço de fixed_object. Analogamente z é um ponteiro constante, mas também é declarado para apontar a um int cujo valor não pode ser modificado pelo programa. O especificador adicional volatile indica que embora o valor do const int apontado por z não pode ser modificado pelo programa, mas poderia ser modificado por um processo concorrente com o programa. A declaração de w especifica que o programa não pode mudar o valor apontado e que o programa não pode modificar o ponteiro. struct list *next, *previous; /* Usa o tag da estrutura chamada list */

Esse exemplo declara duas variáveis ponteiro, next e previous, que apontam para uma tipo de estrutura chamado list. Esta declaração pode aparecer antes da definição do tipo de estrutura, sempre que a definição do tipo tenha a mesma visibilidade que a própria declaração. Observar o exemplo a seguir: struct list char *token; int count; struct list *next; line;

A variável line é do tipo struct chamada list. A estrutura list possui três membros: o primeiro membro é um ponteiro para valores do tipo char, o segundo é um valor int, e o terceiro é um ponteiro para uma outra estrutura do tipo list. struct id unsigned int id_no;

Page 136: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

114114114114

8888

struct name *pname; record;

No exemplo acima, a variável record é do tipo estrutura chamado id. Deve-se notar que pname é declarada como um ponteiro para um outro tipo de estrutura chamado name. Esta declaração pode aparecer antes que o tipo name seja definido.

AArrmmaazzeennaammeennttoo ddee EEnnddeerreeççooss

O espaço de memória necessário para o armazenamento de um endereço e o significado do mesmo, depende da implementação do compilador. Não se pode garantir que os ponteiros para tipos diferentes de dados, tenham a mesma largura em bits. Desta forma, sizeof(char*) não é necessariamente igual a sizeof(int *), embora isto sejam iguais na maioria dos compiladores.

88..55..77.. DDeeccllaarraaççõõeess AAbbssttrraattaass Um declarador abstrato é um declarador sem identificador, que consiste de um ou

mais ponteiros, arrays ou modificadores de função. O modificador ponteiro (*) sempre precede o identificador em um declarador; os modificadores array ([ ]) e função (( )) são colocados após o identificador. Conhecendo isto, pode-se determinar se o identificador tem que aparecer em um declarador abstrato de forma a interpretá-lo de forma correta.

Os declaradores abstratos podem ser complexos. Os parênteses em um declarador abstrato complexo especificam uma interpretação particular, da mesma forma que especificam como funcionam.

Os exemplos a seguir ilustram algumas declarações abstratas10. int * /* O nome do tipo para um ponteiro para o tipo int */ int *[3] /* Um array de três ponteiros para int */ int (*) [5] /* Um ponteiro para um array de cinco inteiros */ int *() /* Uma função sem especificação de parâmetros que retorna um ponteiro para um int */ int (*) ( void ) /* Um ponteiro para uma função sem argumentos e que retorna um int */ int (*const []) ( unsigned int, ... ) /* Um array de ponteiros constantes de número não especificado , para funções cada uma das quais como um parâmetro que tem o tipo unsigned int, e um número não especificado de outros parâmetros, que retornam um int */

88..66.. IInntteerrpprreettaannddoo DDeeccllaarraaddoorreess CCoommpplleexxooss

Qualquer declarador pode ser incluído entre parênteses para especificar uma interpretação particular de um "declarador complexo". Um declarador complexo é um identificador qualificado por mais de um modificador de array, ponteiro, ou função. Pode-se aplicar várias combinações de modificadores de arrays, ponteiros, e funções para um único identificador. Geralmente pode ser usada a keyword typedef, para simplificar as declarações. . 10 O declarador abstrato que consiste de um grupo de parênteses vazios ( ), não é permitido por ser ambíguo. É impossível determinar se o identificador será colocado dentro dos parênteses (neste caso seria um tipo não modificável), ou antes deles (neste caso seria uma função).

Page 137: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

115115115115

8888

Na interpretação de declaradores complexos, os colchetes e parênteses (i.e., os modificadores à direita do identificador) tomam precedência sobre os asteriscos (i.e., modificadores à esquerda do identificador). Os colchetes e parênteses têm a mesma precedência e são associados da esquerda para a direita. Depois que o declarador tenha sido interpretado completamente, o tipo de especificador é aplicado como o último passo. Usando parênteses pode-se sobrescrever a ordem de associação default e forçar uma interpretação particular. Nunca devem ser usados parênteses ao redor do nome do próprio identificador. Isto poderia ser mal interpretado como uma lista de parâmetros.

Um modo simples para interpretar declaradores complexos é lendo-os de dentro para fora, usando os quatro passos a seguir:

1. Começar com o identificador e procurar diretamente, para a direita por colchetes ou parênteses (se houverem).

2. Interpretar estes colchetes ou parênteses, então procurar no lado esquerdo por asteriscos.

3. Se for encontrado um parêntesis de fechamento ‘)’, voltar aos passos 1 e 2 para tudo o que for encontrado dentro dos parênteses.

4. Aplicar o especificador de tipo.

char *( *(*var)() )[10]; ^ ^ ^ ^ ^ ^ ^ 7 6 4 2 1 3 5

Nesse exemplo, os passos foram numerados na ordem e podem ser interpretados como segue:

1. O identificador var é declarado como ....

2. um ponteiro para ....

3. uma função que retorna ...

4. um ponteiro para ...

5. um array de 10 elementos que são do tipo ...

6. ponteiros para valores do tipo ...

7. char.

Os seguintes exemplos ilustram outras declarações complexas e mostram como os parênteses podem afetar o significado de uma declaração. int *var[5]; /* Array de ponteiros para valores do tipo int */

O modificador de array ([ ]) tem uma prioridade maior que o modificador ponteiro (*), de forma que var é declarada como sendo um array. O modificador de ponteiro é aplicado ao tipo dos elementos do array; assim, os elementos do array são ponteiros para valores int. int (*var)[5]; /* Ponteiro para um array de valores do tipo int */

Page 138: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

116116116116

8888

Nesta declaração para var, os parênteses dão ao modificador ponteiro uma prioridade maior que o modificador array, e var é declarada como sendo um ponteiro para um array de cinco valores int. long *var( long, long ); /* Função que retorna um ponteiro para um valor long */

O modificador função também tem prioridade maior que o modificador ponteiro, de forma que esta declaração para var declara que é uma função que retorna um ponteiro para um valor long. A função é declarada como tendo dois argumentos do tipo long. long (*var)( long, long ); /* Ponteiro para uma função que retorna uma long */

Nesse exemplo, os parênteses dão ao modificador ponteiro maior prioridade que para o modificador função, e var é declarada como sendo um ponteiro para uma função que retorna um valor long. A função possui dois argumentos do tipo long. struct both /* Array de ponteiros para funções */ /* que retornam uma tipo de estrutura */ int a; char b; ( *var[5] )( struct both, struct both );

Os elementos de um array não podem ser funções, mas esta declaração demonstra como declarar um array de ponteiros para funções no lugar de um array de funções. Nesse exemplo, a variável var é declarada como sendo um array de cinco ponteiros para função que retorna uma estrutura de dois membros. Os argumentos da função são declarados como sendo duas estruturas com o mesmo tipo de estrutura. Deve ser notado que os parênteses ao redor de *var[5] são requeridos, já que sem eles, a declaração não será correta, tentando declarar um array de funções como mostrado a seguir. /* Declaração incorreta */ struct both *var[5]( struct both, struct both );

A seguinte instrução declara um array de ponteiros. unsigned int *(* const *name[5][10] ) ( void );

O array name tem 50 elementos organizados num array multidimensional. Os elementos são ponteiros para um ponteiro que é constante. Este ponteiro constante aponta para uma função que não tem parâmetros e retorna um ponteiro para um tipo unsigned int.

O exemplo a seguir é a declaração de uma função que retorna um ponteiro para um array de três valores double. double ( *var( double (*)[3] ) )[3];

Nesta declaração, a função retorna um ponteiro para um array. Funções que retornam arrays não são permitidas. Aqui var é declarada como sendo um ponteiro para o retorno da função, que aponta para um array de três elementos double. O tipo de argumento é dado pelo declarador abstrato complexo. Os parênteses ao redor do asterisco no tipo de argumento são necessários; sem eles, o tipo de argumento deverá ser um array de três ponteiros para valores double. union sign /* Array de arrays de ponteiros */ /* para ponteiros para unions */ int x; unsigned y; **var[5][5];

Como mostra o exemplo abaixo, um ponteiro pode apontar para outro ponteiro, e um array pode conter arrays como elementos. Aqui var é um array de cinco elementos. Cada elemento é um array de ponteiros para ponteiros para unions com dois membros. union sign *(*var[5])[5]; /* Array de ponteiros para arrays de ponteiros para unions */

Page 139: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

117117117117

8888

Esse exemplo mostra como a localização dos parênteses muda o significado da declaração. Nesse exemplo, var é um array de cinco elementos de ponteiros para arrays de cinco elementos para ponteiros para unions.

88..77.. IInniicciiaalliizzaaççããoo

Um inicializador é um valor ou seqüência de valores a serem atribuídos para variáveis que estão sendo declaradas. Pode-se setar uma variável para um valor inicial aplicando inicializador no declarador na declaração da variável. O valor ou valores do inicializador será atribuído à variável.

As seções seguintes descrevem como inicializar variáveis de tipo escalares, compostos e strings. Os tipos escalares incluem todos os tipos aritméticos, mais os ponteiros. Os tipos compostos incluem arrays, estruturas e unions.

88..77..11.. IInniicciiaalliizzaannddoo TTiippooss EEssccaallaarreess Quando for inicializado um valor de tipo escalar, o valor da expressão será

atribuído à variável. Existem regras de conversão de tipos de dados que poderão ser aplicadas como será visto mais adiante.

A sintaxe básica de uma inicialização para o tipo escalar é:

especificador-de-tipo identificador = inicializador;

Qualquer tipo de variável pode ser inicializada, sempre que obedecidas as seguintes regras: As variáveis declaradas com nível de escopo de arquivo podem ser inicializadas. Caso uma

variável de nível externo não seja inicializada explicitamente, ela será inicializada com 0 por default. Uma expressão constante pode ser usada para inicializar qualquer variável global declarada com

o especificador de classe de armazenamento static. As variáveis declaradas para ser static são inicializadas quando o começar a execução do programa. Caso estas não forem explicitamente inicializadas, estas também serão inicializadas em 0 por default, e para cada membro que seja do tipo ponteiro, será atribuído um ponteiro null. As variáveis declaradas com a classe de armazenamento auto ou register, são inicializadas cada

vez que o controle da execução passa para o bloco no qual estão declaradas. Se for omitido o inicializador na declaração da variáveis auto ou register, o valor seu inicial será indefinido. Para este tipo de variáveis, o inicializador não é restringido a ser um valor constante; também pode ser uma expressão envolvendo valores previamente definidos, assim como chamadas de função. Os valores iniciais para declarações de variáveis externas (extern) e para todas as variáveis

estáticas (static), devem ser expressões constantes. Uma vez que o endereço de qualquer variável declarada externamente ou como static é constante, ele pode ser utilizado para inicializar uma variável ponteiro static declarada internamente. Entretanto, o endereço de uma variável auto não pode ser usado como um inicializador static porque ele pode ser diferente para cada execução do bloco. Podem ser usados valores constantes ou variáveis para inicializar variáveis do tipo auto e register. Se a declaração de um identificador tem escopo de bloco, e o identificador tem ligação externa,

a declaração não pode ter uma inicialização.

Os seguintes exemplos ilustram algumas inicializações:

Page 140: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

118118118118

8888

int x = 10;

A variável inteira x é inicializada com a expressão constante 10. register int *px = 0;

O ponteiro px é inicializado para 0, produzindo um ponteiro null. const int c = (3 * 1024);

No exemplo foi usada a expressão constante (3 * 1024) para inicializar c para um valor constante que não poderá ser modificado devido a keyword const. int *b = &x;

Esta instrução inicializa o ponteiro b com o endereço de uma outra variável, x. int *const a = &z;

O ponteiro a é inicializado com o endereço da variável z. Entretanto, uma vez que ele foi especificado como sendo classe const, a variável a pode somente ser inicializada, mas nunca modificada. Desta forma, o ponteiro apontará sempre para o mesmo endereço. int GLOBAL ; int function( void ) int LOCAL ; static int *lp = &LOCAL; /* Delcaração ilegal */ static int *gp = &GLOBAL; /* Declaração legal */ register int *rp = &LOCAL; /* Declaração legal */

A variável global chamada GLOBAL, é declarada de nível externo, de forma que possui tempo de vida global. A variável local chamada LOCAL, tem classe de armazenamento auto e somente possui um endereço durante a execução da função na qual é declarada. Desta maneira, ao tentar inicializar o ponteiro static lp com o endereço de LOCAL, haverá a geração de um erro. O ponteiro static gp pode ser inicializada com o endereço de GLOBAL porque o endereço dela é sempre o mesmo. De forma análoga, *rp pode ser inicializada porque rp é uma variável local e pode ter um inicializador não constante. Cada vez que o bloco é executado, a variável LOCAL tem um novo endereço, que será então designado a rp.

88..77..22.. IInniicciiaalliizzaannddoo TTiippooss CCoommppoossttooss Um tipo composto é um tipo struct, union ou array. Se um tipo composto contém

membros de tipos também compostos, as regras de inicialização se aplicam recursivamente.

A sintaxe básica da inicialização é:

especificador-de-tipo identificador = inicializador ou lista-de-inicializadores,...

A lista de inicializadores é um conjunto de inicializadores separados por vírgulas. Cada inicializador do conjunto é uma expressão constante ou uma lista de inicializadores. Assim, as listas de inicializadores podem ser aninhadas. Esta forma é muito usada para inicializar membros compostos de um tipo composto como mostram os exemplos desta seção.

Entretanto, se o inicializador para um identificador automático é uma única expressão, não necessita ser uma expressão constante; necessita meramente ter o tipo apropriado para a atribuição ao identificador.

Para cada lista de inicializadores, os valores das expressões constantes são atribuídos, em ordem, aos membros correspondentes da variável composta.

Page 141: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

119119119119

8888

Se a lista de inicializadores tiver menos valores que o tipo composto, os membros ou elementos restantes do tipo composto serão inicializados a 0, para variáveis externas e static. O valor inicial de um identificador automático não explicitamente inicializado será indefinido. Se a lista de inicializadores tiver mais valores do que o tipo composto, resultará em um erro de compilação. Estas regras aplicam-se a cada lista embutida de inicializadores, como para os tipos compostos em si.

Um inicializador de estrutura pode ser uma expressão do mesmo tipo, ou uma lista de inicializadores para os seus membros fechados entre chaves ( ). Campos de bits não nomeados não serão inicializados.

Quando uma union é inicializada, a lista de inicializadores deve ser uma única expressão constante. O valor da expressão constante é atribuído ao primeiro membro da union.

Se um array tem tamanho desconhecido, o número de inicializadores determina o tamanho do mesmo. Não há forma de especificar uma repetição de um inicializador na linguagem C, nem como inicializar um elemento na metade de um array, sem prover todos os valores precedentes.

É importante notar que o número de inicializadores pode definir o tamanho do array.: int x[ ] = 0, 1, 2

Caso seja especificado o tamanho, e dado um número errado de inicializadores (a mais), o compilador gerará um erro.

Segue um exemplo de inicialização para um array: int P[4][3] = 1, 1, 1 , 2, 2, 2 , 3, 3, 3,, 4, 4, 4,, ;

Esta instrução declara P como um array quatro por três, e inicializa os elementos da primeira linha com 1, a segunda linha com 2, e assim sucessivamente até a quarta linha. Deve-se notar que a lista de inicialização para a terceira e quarta linhas contém vírgulas após a última expressão constante. A última lista de inicialização (4, 4, 4,,) também acaba numa vírgula. Estas vírgulas a mais são permitidas mas não são necessárias; somente são necessárias as vírgulas que separam expressões constantes umas de outras, e aquelas que separam uma lista de inicializadores de outra.

Se um membro composto não possuir uma lista de inicializadores agrupada, os valores serão simplesmente atribuídos na ordem em que aparecem, para cada membro do subconjunto. Desta forma, a inicialização do exemplo anterior é equivalente à seguinte: int P[4][3] = 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4 ;

Podem ser colocadas chaves em torno de cada inicializador individual da lista.

Quando forem inicializadas variáveis compostas, deve se ter cuidado ao usar as chaves de modo a colocar a lista de inicializadores de forma apropriada. O seguinte exemplo ilustra a interpretação do compilador, com respeito as chaves, com mais detalhes: typedef struct

Page 142: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

120120120120

8888

int n1, n2, n3; triplet; triplet nlist[2][3] = 1, 2, 3 , 4, 5, 6 , 7, 8, 9 , /* Linha 1 */ 10,11,12 , 13,14,15 , 16,17,18 /* Linha 2 */ ;

No exemplo, nlist é declarada como um array 2x3 de estruturas, cada uma delas tendo três valores. A linha 1 da inicialização atribui valores para a primeira linha de nlist, como segue: 1. A abertura de chaves na linha 1 sinaliza ao compilador que a inicialização do primeiro membro

composto de nlist (que é, nlist[0]) está começando. 2. A segunda abertura de chaves à esquerda indica que a inicialização do primeiro membro

composto de nlist[0] (que é, a estrutura em nlist[0][0]) está começando. 3. O primeiro fechamento de chaves finaliza a inicialização da estrutura nlist[0][0]; o próximo

abre chaves começa a inicialização de nlist[0][1]. 4. O processo continua até o final da linha, onde o fechamento de chaves finaliza a inicialização

de nlist[0].

A linha 2 atribui valores para a segunda linha de nlist de forma análoga. Deve-se notar no código, que as chaves externas que envolvem os inicializadores das linhas 1 e 2 são necessários. Na seguinte construção, onde são omitidas as chaves externas, haverá erro: triplet nlist[2][3] = /* Isto causa um erro de compilação */ 1, 2, 3 , 4, 5, 6 , 7, 8, 9 , /* Linha 1 */ 10,11,12 , 13,14,15 , 16,17,18 /* Linha 2 */ ;

Nesta construção, o primeiro abre chaves na linha 1 começa o inicializador de nlist[0], que é um array de três estruturas. Os valores 1, 2 e 3 serão atribuídos aos três membros da primeira estrutura. Quando for encontrado o próximo fecha chaves (depois do valor 3), a inicialização de nlist[0] estará completa, e as duas estruturas restantes do array de três estruturas serão automaticamente inicializadas em 0. De forma análoga, o conjunto 4,5,6 inicializa a primeira estrutura da segunda linha de nlist. As duas estruturas restantes de nlist[1] serão setadas a 0. Quando o compilador encontrar a seguinte lista de inicialização ( 7,8,9 ), tentará inicializar nlist[2]. Já que nlist possui somente duas linhas, será originado um erro de compilação.

No exemplo seguinte, os três membros inteiros de x serão inicializados para 1, 2 e 3 respectivamente. struct list int i, j, k; float m[2][3]; x = 1, 2, 3, 4.0, 4.0, 4.0 ;

Na lista de inicializadores de estrutura acima, os três elementos da primeira linha de m são inicializados para 4.0; os elementos restantes da linha de m serão zerados por default. union char x[2][3]; int i, j, k; y = '1', '4'

Page 143: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

121121121121

8888

;

A variável union y do exemplo, é inicializada. O primeiro elemento da union é um array, de forma que o inicializador é composto. A lista de inicialização ‘1’ atribui o valor à primeira linha do array. Uma vez que somente um valor aparece na lista, o elemento na primeira coluna é inicializado com o caractere 1, e os dois elementos restantes da linha são inicializados com valor 0. De forma similar, o primeiro elemento da segunda linha de x é inicializado com o caractere 4, sendo que os dois elementos restantes da linha serão inicializados em zero.

88..77..33.. IInniicciiaalliizzaannddoo SSttrriinnggss Os arrays de caracteres (strings) podem ser inicializadas com uma string literal. Por

exemplo: char code[ ] = "abc";

inicializa a variável code como um array de quatro elementos do tipo caractere. O quarto elemento é o caractere null, que finaliza uma string literal.

Uma lista de inicializadores deve ser do mesmo tamanho que o número de elementos a serem inicializados. Caso seja especificado um tamanho para o array que é menor do tamanho da string, os caracteres extras serão ignorados. Por exemplo, a seguinte declaração inicializa um array de três elementos do tipo caractere: char code[3] = "abcd";

Somente os três primeiros caracteres do inicializador serão atribuídos a code. O caractere d e o caractere null de terminação serão descartados. Deve-se notar que isto cria uma string indeterminada (i.e., sem o null que indica o seu final) e provavelmente o compilador gerará uma mensagem indicando esta condição.

A declaração char s[] = "abc", t[3] = "abc";

é idêntica a char s[] = 'a', 'b', 'c', '\0', t[3] = 'a', 'b', 'c' ;

Se a string é menor que o tamanho especificado para o array, os elementos restantes serão inicializados em 0.

Dependendo do compilador, a inicialização de strings poderá ser limitada em tamanho. Por exemplo no Microsoft C, as strings literais podem ter até 2048 bytes. No compilador PCW para microcontroladores PIC (Microchip), até 16 bytes.

88..88.. AArrmmaazzeennaammeennttoo ddee TTiippooss BBáássiiccooss

As tabelas a seguir mostram o espaço de armazenamento associado a cada tipo básico.

Tipo Espaço de Armazenamento (bytes)

char, unsigned char, signed char 1 short, unsigned short 2 int, unsigned int 4 long, unsigned long 4 Float 4

Page 144: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

122122122122

8888

Double 8 long double 10

Tabela 8-3 – Tipos de dados de um compilador para processadores da família 8x86 de 32 bits (Visual C++ e Borland C++ 4.0):

Tipo Espaço de Armazenamento (bytes)

char, unsigned char, signed char 1 short, unsigned short 1 bit int, unsigned int 1 long, unsigned long 2 Float 4 Double não Long double não

Tabela 8-4 – Tipos de dados de um compilador para processador PIC de 8 bits CCS PCW:

Tipo Espaço de Armazenamento (bytes)

Char, unsigned char, signed char 1 short, unsigned short 1 int, unsigned int 2 long, unsigned long 4 Float 4 Double 4 long double 4

Tabela 8-5 - Tipos de dados de um compilador para processador da família 8051 de 8 bits (ex. Franklin, Keil e Archimedes):

Tipo Espaço de Armazenamento (bytes)

char, unsigned char, signed char 1 short, unsigned short 1 int, unsigned int 2 long, unsigned long 4 Float 4 Double 8 long double 10

Tabela 8-6 – Tipos de dados de um compilador para processador da família 8x86 de 16 bits (ex. Borland Turbo C++ 3.x e Microsoft C):

Os tipos de dados em C podem ser agrupados em categorias gerais: os tipos “inteiros” que incluem char, int, short, long, signed, unsigned e enum. A segunda categoria é a dos tipos “ponto flutuante”, que incluem o float, double e long double. Os tipos “aritméticos” incluem todos os tipos inteiros e de ponto flutuante.

88..88..11.. TTiippoo cchhaarr O tipo char é usado para armazenar o valor inteiro de um membro de um grupo de

caracteres representáveis. O valor inteiro é o código em ASCII correspondente ao caractere especificado.

Este tipo é bastante usado também para armazenar números de 8 bits para manipulação de portas de comunicação

88..88..22.. TTiippoo iinntt O tamanho de um item do tipo int com sinal ou sem sinal, é o tamanho padrão de

um inteiro num processador ou sistema operacional particular. Por exemplo, em sistemas operacionais de 16 bits, o tipo int é usualmente de 16 bits, ou dois bytes. Em sistemas operacionais de 32 bits, o tipo int é usualmente de 32 bits, ou quatro bytes. O mesmo

Page 145: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

123123123123

8888

acontece com processadores de tamanho de palavra diferente. Assim, o tipo int num sistema de 32 bits pode ser equivalente ao long int de um sistema de 16 bits, e um short int de um sistema de 32 bits pode ser equivalente ao int de um sistema de 16 bits, dependendo do ambiente alvo. Os tipos int em geral representam valores com sinal (signed) a menos que seja especificado de outra forma (unsigned). No compilador PCW da CCS para PIC o padrão é unsigned.

Os especificadores de tipo int e unsigned int (ou simplesmente unsigned) definem certas características da linguagem C (por exemplo, o tipo enum). Nestes casos, as definições de int e unsigned int para uma implementação particular, determinarão o atual espaço de armazenamento11.

Em geral os inteiros com sinal são representados e tratados na forma de complemento de dois. O bit mais significativo armazena o sinal: 1 se for negativo, 0 para positivos12.

88..88..33.. TTiippoo ffllooaatt Para representar os números de ponto flutuante, a maioria dos compiladores usa o

formato IEEE13, que consiste em um bit de sinal, um expoente de 8 bits (excesso 127) e uma mantissa de 23 bits. A mantissa representa um número entre 1.0 e 2.0. Desde que o bit mais significativo da mantissa é sempre 1, este não é armazenado no número. Esta representação permite uma faixa aproximada de valores que vai de 3.4E–38 a 3.4E+38 para o tipo float.

Uma variável pode ser declarada como float ou double, dependendo das necessidades da aplicação. A principal diferença entre estes dois tipos, é a significância que estes podem representar, o espaço de memória necessário, a faixa de valores e a velocidade de processamento. A tabela a seguir permite observara algumas relações entre esses dois tipos.

Tipo Dígitos Significativos

Número de bytes

float 6 – 7 4 double 15 – 16 8

Tabela 8-7 – Características dos tipos float e double

As variáveis de ponto flutuante são representadas pela mantissa, que contém o valor do número, e o expoente, que contém a ordem de grandeza do número.

A tabela a seguir mostra o número de bits alocados para a mantissa e o expoente para cada tipo de dado de ponto flutuante. O bit mais significativo de qualquer float ou double é sempre o bit de sinal. Se for igual a 1, o número é considerado negativo; caso contrário, será considerado um número positivo.

Tipo Comprimento do Expoente Comprimento da Mantissa float 8 bits 23 bits

11 Os especificadores de tipo int e unsigned int são muito utilizados nos programas em C porque permitem, a uma determinada máquina, manipular valores inteiros da forma mais eficiente para esta máquina. Desta forma, uma vez que os tamanhos dos tipos int e unsigned int variam entre máquinas diferentes, os programas que dependem de um tamanho específico para as variáveis int, não serão portáveis para outras máquinas. Para implementar programas mais portáveis, podem ser usadas expressões com o operador sizeof (que será discutido na seção 16.4) ao invés de utilizar rotinas complexas para armazenamento de dados. 12 Lembrar que o zero é considerado como número positivo. 13 IEEE: Institute of Electrical and Electronics Engineers – www.ieee.org

Page 146: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

124124124124

8888

double 11 bits 52 bits

Tabela 8-8 - Comprimento do expoente e mantissa

Devido a que o expoente é armazenado na forma unsigned, é colocado um offset da metade do seu valor máximo. Para o tipo float, o offset é de 127 unidades; para o tipo double é 1023. O valor atual do expoente pode ser calculado subtraindo o valor do offset do valor do expoente. O valor de offset permite a representação de expoentes negativos, que são necessários para representar números menores que a unidade.

A mantissa é armazenada como sendo uma fração binária maior ou igual a 1, e menor que 2. Para os tipos float e double, existe um bit 1 implícito na posição mais significativa, de forma que a mantissa possui efetivamente 24 e 53 bits de comprimento respectivamente. O bit mais significativo da mantissa não é armazenado na memória.

A tabela a seguir mostra os valores máximos e mínimos que podem ser armazenados em variáveis de cada tipo. Os valores listados nesta tabela se aplicam a números de ponto flutuante normalizado.

Os números retidos nos registradores de um coprocessador matemático 80x87, são sempre representados na forma normalizada de 80 bits.

Tipo Valor Mínimo Valor Máximo float 1.175494351 E – 38 3.402823466 E + 38 double 2.2250738585072014 E – 308 1.7976931348623158 E + 308

Tabela 8-9 - Valores máximos e mínimos em ponto flutuante

Se a precisão necessária for mais baixa que o float pode representar, considere a utilização deste tipo de variável. Caso a precisão elevada for o critério mais importante, deve-se utilizar o tipo double.

As variáveis de ponto flutuante podem ser convertidas para um tipo com significância maior (do tipo float para o tipo double). Estas conversões ocorrem freqüentemente quando são executadas operações aritméticas em variáveis de ponto flutuante. A aritmética é sempre executada num grau de precisão mais elevado que a variável com o mais alto grau de precisão. Por exemplo, considerar as seguintes declarações: float f_short; double f_long; f_short = f_short * f_long;

No exemplo acima, a variável f_short é convertida para o tipo double e multiplicada pela f_long; então o resultado é arredondado para o tipo float antes de ser atribuída a f_short.

No exemplo a seguir, a aritmética é feita nas variáveis com precisão de float (32 bits); o resultado então é convertido para o tipo double. float f_short; double f_longer; f_longer = f_short * f_short;

Page 147: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

125125125125

8888

88..88..44.. TTiippoo ddoouubbllee Os valores do tipo double (dupla precisão) são representados e armazenados

usando 8 bytes. O formato é similar ao formato float, exceto que possui um expoente de 11 bits (com excesso 1023) e uma mantissa de 52 bits, mais o bit mais significativo implícito em 1. O formato permite uma faixa aproximada de valores que vão de 1.7E–308 to 1.7E+308 para este tipo.

Poucos compiladores para microcontroladores suportam este formato, devido ao tamanho de código necessário para fazer aritmética, e ao tamanho da memória de dados necessária para armazená-los, já que estes dispositivos possuem recursos bastante limitados, quando comparados com um computador desktop.

88..88..55.. TTiippoo lloonngg ddoouubbllee A faixa de valores para uma variável é limitada pelos valores máximos e mínimos

que pode ser representado internamente com um determinado número de bits. Desta forma, devido às regras de conversão da linguagem C, nem sempre pode ser utilizado o valor máximo ou mínimo para uma constante de um tipo particular dentro de uma expressão.

Por exemplo, a expressão constante –32768 que consiste do operador de negação aritmética (-) aplicada ao valor constante 32768. Como o valor 32768 é muito grande para ser representado como um valor de 16 bits (ex. int), a variável deverá ser do tipo de 32 bits (ex. long int). Conseqüentemente, a expressão constante –32768 é do tipo long. Pode-se representar –32768 como um tipo de 16 bits (ex. int) pela utilização de um modelador de tipo (cast) para o tipo de 16 bits. Desta forma nenhuma informação será perdida no tipo convertido, desde que –32768 possa ser representado internamente em 2 bytes.

O valor 65000 em notação decimal é considerado como uma constante signed. Será armazenado como sendo do tipo de 32 bits porque a sua representação não cabe em 16 bits com sinal. Um valor como 65000 pode ser representado como um valor de 16 bits somente pelo uso de um conversor de tipo (cast) para o tipo de 16 bits, ou pela especificação do número como 65000U. Pode-se converter este valor de 32 bits em 16 bits sem perda de informação devido a que o valor 65000 pode ser armazenado em 2 bytes em representação sem sinal (unsigned).

A maioria dos compiladores para sistemas de 32 bits suporta o tipo long double de 80 bits (10 bytes): 1 bit para o sinal, 15 para o expoente e 64 para a mantissa. A faixa possível vai de 1.2E-4932 a 1.2E+4932 com no mínimo de 19 dígitos de precisão. Embora os tipos double e long double sejam tipos diferentes, a representação é idêntica.

88..99.. TTiippooss IInnccoommpplleettooss

Um tipo incompleto é um tipo que descreve um identificador mas não fornece a informação necessária para determinar o tamanho do identificador. Um tipo “incompleto” pode ser: Um tipo de estrutura cujos membros não tenham ainda sido especificados. Um tipo union cujos membros não tenham ainda sido especificados. Um array cujas dimensões não tenham ainda sido especificadas.

Page 148: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

126126126126

8888

O tipo void é um tipo incompleto que não pode ser completado. Para completar um tipo incompleto, deve ser especificada a informação faltante. Os seguintes exemplos mostram como criar e completar tipos incompletos. Para criar um tipo estrutura incompleta, declarar o tipo de estrutura sem especificar os seus

membros. No exemplo a seguir, o ponteiro pres aponta para um tipo estrutura incompleto chamado resistor.

struct resistor *pres;

Para completar um tipo estrutura incompleto, declarar a mesmo tipo de estrutura mais tarde no mesmo escopo com os seus membros especificados, como em

struct resistor int num; /* estrutura resistor agora está completa */

Para criar um tipo array incompleto, declarar o array sem especificar o seu tamanho. Por exemplo:

char a[]; /* a é um tipo incompleto */

Para completar um array incompleto, declarar o mesmo nome depois no mesmo escopo com o tamanho especificado, como em:

char a[25]; /* a agora esta’completa */

88..1100.. DDeeccllaarraaççõõeess ttyyppeeddeeff

Uma declaração typedef é uma declaração que usa a keyword typedef como classe de armazenamento. O declarador aparecerá como sendo um novo tipo de dado. Pode-se utilizar as declarações typedef para construir tipos com nomes menores ou mais significativos, para tipos previamente definidos pela linguagem ou para novos tipos que se desejam criar. Os nomes typedef permitem encapsular detalhes de implementação que podem mudar.

Uma declaração typedef é interpretada da mesma forma que uma declaração de variável ou função, mas o identificador, no lugar de assumir o tipo especificado pela declaração, fica como um sinônimo para o tipo.

É importante notar que uma declaração typedef não cria tipos. Ela cria sinônimos para tipos existentes, ou nomes para tipos que possam ser especificados de maneiras diferentes. Quando um nome typedef é utilizado como um especificador de tipo, ele pode ser combinado com um certo tipo de especificadores, mas não com todos. São aceitos os modificadores tais como const e volatile.

Os nomes typedef compartilham o mesmo espaço com os identificadores ordinários. Desta forma, um programa pode ter um nome typedef e um identificador com escopo local do mesmo nome. Por exemplo: typedef char Flag; int main() int myproc( int ) int Flag;

Código 8-11

Quando for declarado um identificador de escopo local com o mesmo nome que um typedef, ou quando for declarado um membro de uma estrutura ou union no mesmo escopo ou em um escopo interno, o especificador de tipo deverá ser definido. Este exemplo ilustra este evento: typedef char Flag;

Page 149: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

127127127127

8888

const Flag x;

Para poder reutilizar o nome Flag para um identificador, membro de estrutura, ou union, o tipo deve ser declarado. const int Flag; /* O especificador de tipo é requerido */

Não será suficiente escrever const Flag; /* Especificação incompleta */

já que Flag é interpretado como sendo parte do tipo, não um identificador que está sendo redeclarado. A declaração é interpretada como sendo uma declaração ilegal parecida com int; /* Declaração ilegal */

Qualquer tipo pode ser declarado com typedef, incluindo ponteiros, funções e tipos array. Pode-se declarar um nome typedef para um ponteiro para uma estrutura ou union antes de definir o tipo da própria estrutura ou union, ou tão logo a definição tenha a mesma visibilidade que a declaração.

O seguinte exemplo ilustra uma declaração typedef: typedef int INTEIRO; /* Declara INTEIRO para ser sinônimo para int */

Deve-se notar que INTEIRO pode agora ser usado em declarações de variáveis tais como INTEIRO i; ou const INTEIRO i. Entretanto, a declaração de long INTEIRO i será ilegal. typedef struct transistor char partnumber[30]; int tipo, beta; TRANSISTOR;

Estas linhas declaram que TRANSISTOR é uma tipo de estrutura com três membros. Desde que são especificados o tag e os membros, poderão ser usados o nome typedef (TRANSISTOR) ou o tag da estrutura, em declarações posteriores. Pode-se usar a keyword struct com o tag, caso não queira-se usar a keyword struct com o nome typedef. typedef TRANSISTOR *PG; /* Usa o nome typedef declarado previamente para declarar um ponteiro */

O tipo PG é declarado como um ponteiro para uma variável tipo TRANSISTOR, que é definida como sendo um tipo estrutura. typedef void DRAWF( int, int );

O exemplo a seguir define o tipo DRAWF par uma função que não retorna valores e possui dois argumentos. Isto permitirá, por exemplo, a seguinte declaração: DRAWF box;

é equivalente a declarar: void box( int, int );

88..1111.. CCoonnvveerrssããoo ddee TTiippooss

Nas atribuições de valores na linguagem C, tem-se o seguinte formato:

destino = origem;

Se o destino e a origem são de tipos diferentes o compilador fará uma conversão entre os tipos. Nem todas as conversões são possíveis de serem efetuadas. O primeiro

Page 150: Tratado Da Linguagem c

T I P O S E D E C L A R A Ç Õ E S

128128128128

8888

ponto a ser ressaltado é que o valor de origem é convertido para o valor de destino antes de ser atribuído e não o contrário.

É importante lembrar que quando se converter um tipo numérico para outro não se melhora a precisão. Pode-se perder precisão ou no máximo manter-la.

A seguir é mostrada uma tabela de conversões numéricas com perda de precisão, para um compilador de 16 bits:

De Para Informação Perdida unsigned char char Valores maiores que 127 são alterados short int char Os 8 bits de mais alta ordem int char Os 8 bits de mais alta ordem long int char Os 24 bits de mais alta ordem long int short int Os 16 bits de mais alta ordem long int int Os 16 bits de mais alta ordem float int Precisão - resultado arredondado double float Precisão - resultado arredondado long double double Precisão - resultado arredondado

Tabela 8-10 - Conversões com perda de precisão

AAvvaalliiaaççããoo

1. Considerando o conceito e finalidade dos modificadores de tipo, relacione as afirmativas com as palavras reservadas correspondentes (todas as afirmativas devem ser preenchidas com o número relacionado ao modificador correspondente, e existe pelo menos uma afirmativa para cada modificador):

(1) const (2) volatile (3) extern (4) static (5) register (6) auto (7) void ( ) informa ao compilador que o valor da variável não pode ser alterado por nenhum comando do programa, mas que pode ser inicializado ( ) informa ao compilador que nenhum valor será devolvido pela função ( ) informa ao compilador que a variável pode ser modificada por algum evento que não está sob o controle do programa ( ) avisa ao compilador que as variáveis que o seguem já foram declaradas em outro lugar ( ) torna a variável permanente, mantendo seu valor entre chamadas ( ) útil ao escrever funções generalizadas e funções de biblioteca que podem ser usadas por outros programadores, pois permite esconder porções do programa de outras partes do código, evitando assim o uso de variável global ( ) quando apontadores forem passados para a função, garante que nenhum código na função poderá modificar os objetos apontados ( ) armazena o valor da variável em um registrador da CPU, acelerando operações ( ) usada para declarar variáveis locais automáticas, mas muito pouco usada por já ser o padrão (default) ( ) avisa ao compilador que a variável em questão será largamente usada e deve permanecer acessível da forma mais eficiente possível ( ) permite ao compilador conhecer a variável sem criar armazenamento para ela novamente em outro módulo.

Page 151: Tratado Da Linguagem c

129129129129

99.. OOPPEERRAADDOORREESS EE EEXXPPRREESSSSÕÕEESS Este capítulo descreve como formar expressões e atribuir valores na linguagem C.

Constantes, identificador, strings, e chamadas de função são os operandos que são manipulados nas expressões. A linguagem C possui todos os operadores habituais das linguagens de programação. Este capítulo cobre esses operadores assim como também os operadores que são únicos da linguagem C. Os tópicos que serão discutidos incluem: Expressões l-values e r-values Expressões constantes Efeitos colaterais Pontos de seqüência Operadores Precedência dos operadores Conversões de tipo Casts

99..11.. IInnttrroodduuççããoo

Um "operando" é uma entidade na qual um operador pode atuar, por exemplo: c = a + b ;

No exemplo, a, b e c são os operandos, = e + são os operadores.

Uma "expressão" é uma seqüência de operadores e operandos que executam qualquer combinação das seguintes ações: Cálculo de um valor Designação de um objeto ou função Geração de efeitos colaterais

Os operandos em C incluem constantes, identificadores, strings, chamadas de função, expressões subscritas, expressões de seleção de membros, e expressões complexas formadas pela combinação de operandos, ou pelo fechamento de operandos entre parênteses. A sintaxe para estes operandos é ilustrada na seção 9.3

99..22.. IInnttrroodduuççããoo aaooss OOppeerraaddoorreess

99..22..11.. OOppeerraaddoorreess AArriittmmééttiiccooss ee ddee AAttrriibbuuiiççããoo Os operadores aritméticos são usados para desenvolver operações matemáticas. A

Tabela 9-1 mostra a lista dos operadores aritméticos da linguagem C: Operador Ação + Soma (inteira e ponto flutuante) - Subtração ou Troca de sinal (inteira e ponto flutuante)

Capítulo

9

Page 152: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

130130130130

9999

* Multiplicação (inteira e ponto flutuante) / Divisão (inteira e ponto flutuante) % Resto de divisão (inteiros) ++ Incremento (inteiro e ponto flutuante) -- Decremento (inteiro e ponto flutuante)

Tabela 9-1 – Operadores Aritméticos

A linguagem C possui operadores unários e binários. Os operadores unários agem sobre uma variável apenas, modificando ou não o seu valor, e retornam o valor final da mesma. Os operadores binários utilizam duas variáveis e retornam um terceiro valor, sem alterar os valores das variáveis originais. A soma é um operador binário que utiliza duas variáveis como operandos, soma seus valores sem alterar as mesmas, e retorna o resultado da operação. Outros operadores binários são os operadores - (subtração), *, / e %. O operador ‘-‘ quando utilizado para troca de sinal, é um operador unário que não altera o valor da variável sobre a qual é aplicado, retornando o valor da variável multiplicado por -1.

O operador / (divisão) quando aplicado a variáveis inteiras, fornece o resultado da divisão inteira; quando aplicado a variáveis em ponto flutuante fornece o resultado da divisão em ponto flutuante. Assim seja o seguinte código: int a = 17, b = 3; int x, y; float z = 17.0 , z1, z2; x = a / b; y = a % b; z1 = z / b; z2 = a/b;

No final da execução destas linhas, os valores calculados seriam x = 5, y = 2, z1 = 5.666666 e z2 = 5.0 . Deve-se notar que na linha correspondente a z2, primeiro será feita uma divisão inteira (pois os dois operandos são inteiros). Somente depois de efetuada a divisão, o resultado será atribuído a uma variável do tipo float.

Os operadores de incremento e decremento são unários, alterando o valor da variável sobre a qual são aplicados. A função destes operadores é a de incrementar ou decrementar o valor da variável, sobre a qual estão aplicados, de uma unidade. Assim: x++; x--;

são equivalentes a x=x+1; x=x-1;

Estes operadores podem ser pré-fixados ou pós- fixados. A diferença é que quando são pré-fixados eles incrementam e retornam o valor da variável já incrementada. Quando são pós-fixados eles retornam o valor da variável sem o incremento e depois incrementam a variável. Assim, no exemplo a seguir: x=23; y=x++;

obter-se, no final, y=23 e x=24. E no exemplo seguinte: x=23; y=++x;

obter-se, no final, y=24 e x=24.

O operador de atribuição da linguagem C é o ‘=’. A função deste operador é a de copiar o valor (ou resultado de uma expressão) à direita, e atribuir à variável da sua

Page 153: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

131131131131

9999

esquerda. Além disto ele retorna o valor atribuído. Isto faz com que as seguintes expressões sejam válidas: x=y=z=1.5; /* Expressao 1 */ if (k=w) ... /* Expressao 2 */

A expressão 1 é válida, pois quando é executada a instrução z=1.5 ela retorna 1.5, que é repassado para a próxima parte da expressão. A expressão 2 será verdadeira se w for diferente de zero, pois este será o valor retornado por k=w. Deve-se evitar o uso de atribuições dentro de comandos onde usualmente existem operações de comparação, para evitar erros de interpretação. Não expressão 2 por exemplo, não está-se comparando k e w, mas o valor de w está sendo atribuído à variável k, e o valor desta última está sendo utilizado para tomar uma decisão.

99..22..22.. IInnttrroodduuççããoo aaooss OOppeerraaddoorreess RReellaacciioonnaaiiss ee LLóóggiiccooss

Os operadores relacionais, da linguagem C, avaliam relações entre variáveis. Estes operadores são usualmente utilizados para efetuar comparações de igualdade e ordem de grandeza. Os operadores relacionais estão listados na Tabela 9-2.

Operador Ação > Maior do que >= Maior ou igual a < Menor do que <= Menor ou igual a == Igual a != Diferente de

Tabela 9-2 -Operadores relacionais

Os operadores relacionais retornam valores lógicos binários, verdadeiro (diferente de zero) ou falso (igual a 0). Todos os operadores relacionais são operadores binários (operam sobre dois operandos).

Os operadores lógicos efetuam as operações AND, OR e NOT. Estes operadores são mostrados na Tabela 9-3.

Operador Ação && AND (E) || OR (OU) ! NOT (NÃO)

Tabela 9-3 - Operadores lógicos

Os operadores AND e OR são binários (precisam de dois operandos). O operador NOT é unário (opera sobre um operando).

O uso dos operadores relacional e lógico pode-se realizar uma grande gama de testes. A tabela-verdade destes operadores é dada na Tabela 9-4.

P q p AND q p OR q Falso falso falso falso Falso verdadeiro falso verdadeiro verdadeiro falso falso verdadeiro verdadeiro verdadeiro verdadeiro verdadeiro

Tabela 9-4 - Tabela verdade dos operadores lógicos e relacionais

No trecho de programa abaixo a instrução if será executada, já que o resultado da expressão lógica será verdadeiro:

Page 154: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

132132132132

9999

int i = 5, j = 7; if ( (i > 3) && (j <= 7) && (i != j) )

j++;

(verdadeiro) AND (verdadeiro) AND (verdadeiro) = verdadeiro

99..22..33.. IInnttrroodduuççããoo aaooss OOppeerraaddoorreess LLóóggiiccooss BBiitt aa BBiitt A linguagem C permite que operações lógicas “bit a bit” (bitwise) em números. Ou

seja, neste caso, o número é representado por sua forma binária e as operações são feitas em cada bit por separado.

Como exemplo pode-se considerar um número inteiro de 16 bits, identificado por i, armazenando o valor 2. int i = 2;

A representação binária de i, será: 0000 0000 0000 0010. Pode-se fazer operações em cada um dos bits deste número. Se for feita fizermos a negação do número (operação binária NOT, ou operador binário ‘~’), isto é, ~i, o número se transformará em 1111 1111 1111 1101 que equivale a 0xFFFD ou –3. void main(void)

int i = 2; i = ~i;

Código 9-1

As operações binárias ajudam programadores efetuar funções de operação sobre bits por separado. As operações lógicas bit a bit só podem ser usadas nos tipos inteiros. Os operadores são listados na Tabela 9-5.

Operador Ação & AND | OR ^ XOR (OR exclusivo) ~ NOT >> Deslocamento de bits a direita << Deslocamento de bits a esquerda

Tabela 9-5 - Operadores lógico bit a bit

Os operadores &, |, ^ e ~ são operadores lógicos bit a bit. A forma geral dos operadores de deslocamento é:

valor>>número-de-bits-de-deslocamentos

valor<<número-de-bits-de-deslocamentos

O número-de-bits-de-deslocamento indica de quantas posições de um bit o valor da variável deverá ser deslocado. Por exemplo, para a variável i do exemplo anterior, armazenando um valor igual a 2 e fazendo: void main(void)

int i = 2; i << 3;

Código 9-2

Page 155: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

133133133133

9999

Este programa fará com que i agora tenha a representação binária: 0000000000010000, isto é, o valor original de i será deslocado de três bits à esquerda resultando no novo valor igual a 0010H ou 16.

O deslocamento para a esquerda de cada bit equivale à multiplicação do valor original por dois, e o deslocamento para a direita equivale à divisão do valor por dois. Isto decorre da característica do sistema de numeração binário.

99..33.. EExxpprreessssõõeess

As expressões são combinações de variáveis, constantes e operadores. Quando são definidas as expressões deve ser levada em consideração, a ordem com que os operadores serão executados, conforme a tabela de precedências da linguagem C, mostrada mais adiante.

Alguns exemplos de expressões: Anos = Dias/365.25; i = i+3; c = a * b + d / e; c = a * (b+d)/ e;

Em operações aritméticas complexas, deve-se ter o cuidado de que o resultado das diversas operações dentro da expressão, não ultrapasse a faixa de valores possíveis para o maior tipo que está sendo usado, nem do tipo da atribuição. A não observância deste critério pode levar a resultados errados e imprevisíveis, especialmente quando são utilizados compiladores para pequenos microcontroladores. Deve-se sempre lembrar que erros de cálculo em aplicações que usam microcontroladores para controlar máquinas, podem prejudicar aos seres vivos.

Um exemplo é mostrado a seguir, considerando um compilador com variáveis inteiras de 16 bits. #include<dos.h> void main() int num; /* inteiro de 16 bits */ int x = 64535; num = x + 1000;

Código 9-3

No exemplo, o resultado final para num é -1 enquanto que o valor esperado seria 65535. Neste código, ainda que não sendo aparente, existe um erro grave que não é percebido pelo compilador nem o linker, já que é um erro lógico de programação. O erro é a da atribuição do valor 64535 para a variável x que é do tipo signed int, sendo que este valor está fora da faixa de valores positivos para o tipo, e será interpretado como –1001.

Avaliar o seguinte código. #include<dos.h> void main() unsigned int num; unsigned int x = 6553; num = x / 10 * 100 + 5;

Código 9-4

Page 156: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

134134134134

9999

O valor esperado para num é 65535, mas o valor final destas linhas de código para num é 65505. O fato é que a seqüência em que serão executados os operadores dentro de uma expressão, depende da implementação interna do compilador que estiver sendo usado. Neste caso é executado o operador divisão x/10 antes da multiplicação, perdendo um dígito (resultado igual a 655); multiplicado posteriormente por 100 (resultado igual a 65500) e posterior adição com a constante 5 (resultado igual a 65505). A expressão mais correta para a última linha de código seria: num = ( (x * 100) / 10 ) + 5; ainda que dependendo do compilador deverão ser testados todos os valores possíveis. O grande problema é quando as variáveis não são constantes, e sim variáveis de um processo, como por exemplo, valores provenientes de um sensor de temperatura, pressão ou vazão, e em ações que devem ser tomadas de acordo com estas informações, tais como atuar em válvulas proporcionais, ou no ativação de bombas pneumáticas ou hidráulicas. Desta forma, deve-se ter cuidado com expressões como : safe = temperature / 10 * pressure + flux; if (safe >= 65535) /* Condição de alarme – Situação de Perigo !*/

alarm(); /* Para valores iguais aos colocados no exemplo anterior o valor de safe será menor que a necessária para o alarme. O alarme não será ativado e seres vivos podem ser prejudicados */

Onde as variáveis temperature, pressure e flux provêm da leitura de conversores analógico-digitais.

99..33..11.. CCoonnvveerrssããoo TTeemmppoorráárriiaa ddee TTiippooss Quando a linguagem C avalia expressões onde há variáveis de tipos diferentes, o

compilador verificará se as conversões são possíveis. Se não forem possíveis, será gerada uma mensagem de erro. Se as conversões forem possíveis estas serão feitas de acordo com as regras seguintes: Todas as variáveis do tipo char e short int poderão eventualmente ser convertidas para int.

Todas as variáveis do tipo float poderão ser convertidas para double. Para pares de operandos de tipos diferentes: se um deles for long double, o outro será

convertido para long double; se um deles for double o outro é convertido para double; se um é long o outro é convertido para long; se um é unsigned o outro é convertido para unsigned.

Em geral, sempre serão feitas as conversões para o tipo de maior precisão. Deve ser evitado o uso de operadores aritméticos com tipos de variáveis diferentes; em certos compiladores, os resultados poderão ser incorretos. Se for extremamente necessário fazer operações com tipos diferentes devem ser usados os modeladores cast1.

99..33..22.. EExxpprreessssõõeess AAbbrreevviiaaddaass A linguagem C permite abreviações para certas expressões, que podem ser usadas

para simplificar a representação ou para facilitar o entendimento de um programa. Da mesma forma que as abreviações de expressões agilizam a leitura de um programa por um programador experiente, estas podem dificultar o entendimento global do algoritmo implementado. Na Tabela 9-6 são mostrados alguns exemplos de abreviações permitidas pela linguagem C.

Expressão Original Expressão Equivalente x=x+k; x+=k; x=x-k; x-=k; x=x*k; x*=k;

1 Veja na seção 9.4.

Page 157: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

135135135135

9999

x=x/k; x/=k; x=x>>k; x>>=k; x=x<<k; x<<=k; x=x&k; x&=k;

Tabela 9-6 -Abreviação de Expressões

99..33..33.. EEnnccaaddeeaammeennttoo ddee EExxpprreessssõõeess:: oo OOppeerraaddoorr ““,,”” O operador ‘,’ determina uma lista de expressões que devem ser executadas de

forma seqüencial. O valor retornado por uma expressão com o operador ‘,’ é sempre dado pela expressão mais à direita. No exemplo abaixo: x=(y=2,y+3);

o valor 2 vai ser atribuído a y, se somará 3 a y e o retorno (5) será atribuído à variável x . Pode-se encadear quantos operadores , forem necessários.

Não é recomendado o uso deste operador em instruções complexas abreviadas, já que dificultam o entendimento do código, como no exemplo anterior. Ficaria mais claro escrever: y = 2; y = y + 3; x = y;

99..33..44.. PPrreecceeddêênncciiaass ddee OOppeerraaddoorreess A precedência indica quais os operadores possuem maior prioridade numa

expressão complexa. A Tabela 9-7 mostra a relação de precedência dos operadores da linguagem C.

Maior precedência () [] ->

! ~ ++ -- . -(unário) (cast) *(unário) &(unário) sizeof

* / % + - << >> <<= >>= == != & ^ | && || ? = += -= *= /= Menor precedência ,

Tabela 9-7 - Precedência de Operadores

Caso não se conheça a tabela de precedências, para evitar cálculos incorretos separe a expressão complexa em blocos separados entre parênteses (maior prioridade), de forma a ter certeza de como será efetuada a avaliação da expressão, e de tornar o programa mais legível.

99..33..55.. EExxpprreessssõõeess PPrriimmáárriiaass eemm CC Os operandos em expressões são chamados de "expressões primárias".

Page 158: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

136136136136

9999

IIddeennttiiffiiccaaddoorreess eemm EExxpprreessssõõeess PPrriimmáárriiaass

Os identificadores podem ser do tipo inteiro, float, enum, struct, union, array, ponteiro ou função. Um identificador é uma expressão primária colocada para designar um objeto2 ou uma função3.

O valor do ponteiro representado pelo identificador do array não é uma variável, de forma que um identificador de array não pode constituir um operando à esquerda (left-hand operator) para uma operação de atribuição, e desta forma não será um l-value válido. int vetor[5]; vetor = 5; /* errado */

Um identificador declarado como função representa um ponteiro cujo valor é o endereço da função. O ponteiro endereça uma função que retorna um valor de um determinado tipo. Desta forma, os identificadores de funções também não podem ser l-values em operações de atribuição. int nFuncSoma(int x, int y); /* declaração da função nFuncSoma*/ main()

nFuncSoma = 5; /* errado */

Código 9-5

CCoonnssttaanntteess eemm EExxpprreessssõõeess PPrriimmáárriiaass

Um operando constante possui o valor e o tipo do valor constante que este representa. Uma constante caractere é do tipo int. Uma constante inteira pode ter o tipo int, long, unsigned int ou unsigned long, dependendo do tamanho do inteiro e a forma em que o valor é especificado.

SSttrriinngg LLiitteerraaiiss eemm EExxpprreessssõõeess PPrriimmáárriiaass

Uma string literal é um caractere simples, caractere longo, ou seqüência de caracteres adjacentes encerrados entre aspas duplas. Uma vez que as strings literais não são variáveis, nenhum dos seus elementos podem ser operandos l-values em operações de atribuição. O tipo de uma string literal é um array de caracteres. Os arrays nas expressões são convertidos em ponteiros.

EExxpprreessssõõeess eennttrree PPaarrêênntteesseess

Qualquer operando pode ser encerrado entre parênteses sem mudar o tipo ou valor da expressão encerrada. Por exemplo, na expressão ( 10 + 5 ) / 5

os parênteses entre 10 + 5 permitem que o valor de 10 + 5 seja avaliado antes, e cujo resultado é um operando de lado esquerdo (left-hand) para o operador da divisão (/). O resultado de (10 + 5) / 5 é 3. Sem os parênteses 10 + 5 / 5 resultará em 11.

Embora os parênteses afetem a forma em que os operandos são agrupados nas expressões, eles não podem garantir uma ordem particular de avaliação em todos os casos.

2 Neste caso é um l-value, como será visto mais adiante. 3 Neste caso designando uma função.

Page 159: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

137137137137

9999

Por exemplo, nem os parênteses nem o agrupamento de esquerda para direita da seguinte expressão pode garantir que valor terá aa variável i em cada uma das subexpressões: ( i++ +1 ) * ( 2 + i )

Os compiladores poderão avaliar os dois lados da multiplicação em qualquer ordem. Se o valor inicial de i é zero, a expressão poderá ser avaliada como qualquer destas duas formas, por exemplo: ( 0 + 1 + 1 ) * ( 2 + 1 ) ( 0 + 1 + 1 ) * ( 2 + 0 )

99..33..66.. EExxpprreessssõõeess LL--VVaalluuee ee RR--VVaalluuee As expressões que referenciam locais da memória são chamadas de expressões l-

value (left-value). Um l-value representa uma região de armazenamento, implicando que este poderá aparecer no lado esquerdo do operador de atribuição (=). Os l-values são freqüentemente identificadores.

As expressões que se referem a lugares de armazenamento modificáveis são chamados l-values modificáveis. Estes não podem ser do tipo array, tipos incompletos, nem tipos declarados com o atributo const. Para que as estruturas e unions possam ser modificáveis, estas não podem ter nenhum dos seus membros com o atributo const. O nome do identificador denota o lugar de armazenamento, enquanto que o valor da variável é o valor armazenado naquela posição.

Um identificador é um valor l-value modificável. O mesmo identifica uma posição de memória e o seu tipo, no caso, se for aritmético, union ou ponteiro. Por exemplo, se ptr é um ponteiro para uma região de armazenamento, então *ptr é um l-value modificável que designa a região de armazenamento para o qual ptr aponta.

Qualquer uma das seguintes expressões em C pode ser uma expressão l-value: Um identificador de tipo inteiro, float, ponteiro, estrutura ou union Uma expressão entre colchetes ([ ]) que não é avaliada para um array Uma expressão de seleção de membro de estrutura ou union (-> ou .) Uma expressão de indireta unária (*) que não se refira a um array Uma expressão l-value entre parênteses Um objeto const (l-value não modificável)

O termo r-value é às vezes utilizado para descrever o valor de uma expressão e para distinguí-la de um l-value. Todos os l-values são r-values, mas o recíproco nem sempre é verdadeiro.

99..33..77.. EExxpprreessssõõeess CCoonnssttaanntteess eemm CC Uma expressão constante é avaliada em tempo de compilação, não no tempo de

execução, e pode ser utilizada em qualquer lugar em que uma constante deva ser usada. As expressões constantes deverão resultar em valores que estão na faixa de valores representáveis para um tipo determinado. Os operandos de uma expressão constante podem ser constantes inteiras, constantes caractere, constantes de ponto flutuante, casts, expressões sizeof e outras expressões constantes.

Page 160: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

138138138138

9999

Uma constante inteira deve ser usada para especificar o tamanho do campo de bits de um membro de uma estrutura, o valor de uma constante de enumeração, o tamanho de um array, ou o valor de uma constante case.

As expressões constantes usadas nas diretivas de preprocessador estão sujeitas a restrições adicionais. Conseqüentemente são conhecidas como expressões constantes restringidas. Uma expressão constante restringida não pode ter expressões sizeof, constantes de enumeração, cast de qualquer tipo nem constantes de ponto flutuante. Porém elas podem conter expressões constantes especiais predefinidas (identificadores).

99..33..88.. AAvvaalliiaaççããoo ddee EExxpprreessssõõeess eemm CC As expressões que envolvem atribuição, incremento unário, decremento unário ou

chamadas de função podem ter conseqüências incidentais das suas avaliações (efeitos colaterais). Quando um ponto de seqüência é encontrado, tudo o que o precede, incluindo qualquer efeito colateral, se assegura que tenha sido avaliado antes que a avaliação continue para o próximo ponto de seqüência.

Os efeitos colaterais são mudanças ocasionadas pela avaliação de uma expressão. Estes efeitos acontecem quando o valor de uma variável é mudado pela avaliação da expressão. Todas as operações de atribuição possuem efeitos colaterais. As chamadas de função também podem criar estes efeitos se mudam o valor de um item visível externamente, pela designação direta ou indireta através de ponteiros.

EEffeeiittooss CCoollaatteerraaiiss

A ordem de avaliação das expressões é definida pela forma específica da implementação do programador, exceto quando a linguagem garante uma ordem particular de avaliação. Por exemplo, alguns efeitos colaterais ocorrem na seguinte chamada de função: add( i + 1, i = j + 2 ); myproc( getc(), getc() );

Os argumentos da chamada da função podem ser avaliados em qualquer ordem. A expressão i + 1 pode ser avaliada antes de i = j + 2, ou vice-versa. O resultado será diferente em cada caso. De modo análogo, na instrução posterior, não é possível garantir quais caracteres serão passados para a função myproc, se o primeiro ou o segundo4.

Como os operadores unários de incremento e decremento envolvem atribuições, tais operadores podem também causar efeitos colaterais como mostra o seguinte exemplo: x[i] = i++;

No exemplo, o valor de x que é modificado é imprevisível. O valor do subscrito pode ser tanto o novo quanto o velho valor de i. O resultado pode variar com compiladores diferentes ou diferentes níveis de otimização do mesmo compilador.

Uma vez que a padronização da linguagem C não define a ordem de avaliação dos efeitos colaterais, ambos os métodos de avaliação discutidos anteriormente são corretos e ambos podem ser implementados. Para ter certeza de que o código seja portável e claro, devem se evitar expressões que dependam de uma ordem particular de avaliação que possa gerar efeitos colaterais. 4 A função getc() está definida na biblioteca stdio.h.

Page 161: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

139139139139

9999

99..33..99.. PPoonnttooss ddee SSeeqqüüêênncciiaa eemm CC Entre dois pontos de seqüência consecutivos, o valor de um objeto pode ser

modificado somente por uma expressão. A linguagem C define os seguintes pontos de seqüência: O operando do lado esquerdo de um operador AND lógico (&&). O operando do lado

esquerdo de um operador lógico AND é completamente avaliado e todos os seus efeitos colaterais serão completados antes de continuar. Se o operando do lado esquerdo foi avaliado como falso (0), o outro operando não será avaliado. O operando do lado esquerdo de um operador OR lógico (||). O operando do lado esquerdo

de um operador OR lógico é completamente avaliado e seus efeitos colaterais completados antes de continuar. Caso o operando do lado esquerdo seja avaliado como verdadeiro (não falso ou diferente de zero), o outro operando não será avaliado. O operando do lado esquerdo do operador (,). O operando do lado esquerdo será

completamente avaliado junto com seus efeitos colaterais antes de continuar. Ambos operandos do operador vírgula serão avaliados. Notar que o operador vírgula numa chamada de função, não garante a ordem de avaliação. Operador de chamada de função. Todos os argumentos de uma função serão avaliados e todos

os efeitos colaterais serão completados antes da entrada na funcão. Nenhuma ordem de avaliação entre os argumentos foi especificada. Primeiro operando de um operador condicional. O primeiro operando de um operador

condicional será completamente avaliado e seus efeitos colaterais completados antes de continuar. O final de um expressão completa de inicialização (i.e., uma expressão que não faz parte de

outra expressão tal como a finalização de uma inicialização em uma linha de declaração). A expressão em uma linha de instrução. As expressões de instrução consistem de uma

expressão opcional seguida de um ponto e vírgula (;). A expressão será avaliada para os seus efeitos colaterais e haverá um ponto de seqüência seguindo a avaliação. A expressão de controle de um comando de seleção (if ou switch). A expressão será

completamente avaliada e todos os seus efeitos colaterais completados antes de que o código dependente desta expressão seja executado. A expressão de controle de um comando while ou do. A expressão será completamente

avaliada e todos os seus efeitos colaterais completados antes que qualquer outra instrução na seguinte iteração seja executada. As três expressões de uma instrução for. As expressões serão completamente avaliadas e todos

os seus efeitos colaterais completados antes que qualquer outra instrução na seguinte iteração seja executada. A expressão num comando return. A expressão será completamente avaliada e todos os seus

efeitos colaterais completados antes que o controle retorne á função requerente.

99..44.. MMooddeellaaddoorreess ((ccaassttss))

Um modelador é basicamente um conversor de tipo. Ele pode ser aplicado a uma expressão ou a uma variável em particular. O cast força a expressão ao tipo especificado, sem mudar o valor original das variáveis envolvidas. A forma geral é:

(tipo) expressão

A expressão poderá também estar entre parênteses. Observar o exemplo mostrado a seguir. #include <stdio.h> void main (void) int num; float f;

Page 162: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

140140140140

9999

num=10; f=(float)num/7; printf ("%f",f);

Código 9-6

Se não fosse usado o cast no exemplo, o código gerado pelo compilador executaria uma divisão inteira entre 10 e 7. O resultado seria 1 (um inteiro) e este seria depois convertido para float para 1.0. Com o cast obtém-se o resultado correto.

O tipo cast fornece um método de conversão explícita do tipo de um objeto em uma situação específica.

A sintaxe pode se dar da seguinte forma:

( nome-do-tipo ) expressão-cast

Os compiladores tratam as expressões cast como um tipo (dado pelo nome do tipo) depois de que a conversão tenha sido feita. Os casts podem ser usados para converter objetos de um dado tipo escalar para qualquer outro tipo também escalar. Os casts são regidos pelas mesmas regras que determinam os efeitos das conversões implícitas discutidas anteriormente. Algumas restrições adicionais nos casts podem resultar dos tamanhos atuais ou a representação de certos tipos específicos.

99..44..11.. CCoonnvveerrssõõeess CCaasstt As conversões tipo cast podem ser usados para converter tipos de forma explícita,

com a seguinte sintaxe:

(nome-de-tipo) expressão-cast

O nome-do-tipo é o tipo e a expressão-cast é o valor a ser convertido para tal tipo. Uma expressão com um tipo cast não é um l-value. A expressão-cast é convertida como tem sido atribuída à variável do tipo nome-do-tipo. As regras de conversão são as mesmas às das conversões implícitas. A tabela a seguir mostra os tipos que podem ser modificados para qualquer outro 5.

Tipo Destino Fontes Potenciais Tipos Inteiros Qualquer tipo inteiro, de ponto flutuante, ou ponteiro para um

objeto. Ponto Flutuante Qualquer tipo aritmético. Um ponteiro para um objeto, ou ponteiro void (void *)

Qualquer tipo inteiro, ponteiro void, ponteiro para um objeto ou ponteiro para função.

Ponteiro para Função Qualquer tipo inteiro, ponteiro para um objeto ou ponteiro para função.

Estrutura, Union ou Array Nenhum tipo Tipo void Qualquer tipo

Tabela 9-8 - Conversões de tipos de dados

Qualquer identificador pode ser casteado para o tipo void. Assim, se o tipo especificado na expressão não for do tipo void, então o identificador a ser casteado para este tipo não poderá ser uma expressão void. Qualquer expressão pode ser casteada para void, mas uma expressão de tipo void não pode ser casteada para qualquer outro tipo. Por

5 Na gíria dos programadores, a mudança temporária de tipos é chamada de “castear”.

Page 163: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

141141141141

9999

exemplo, uma função com tipo de retorno void, não pode ter seu retorno casteado para qualquer outro tipo 6.

99..55.. OOppeerraaddoorreess ddaa LLiinngguuaaggeemm CC

Existem três tipos de operadores. Uma expressão unária consiste de um operador unário que atua sobre um operando, ou um comando sizeof seguido de uma expressão. A expressão pode ser o nome de uma variável ou uma expressão cast. Se a expressão é uma expressão cast, com fio visto na seção 9.4, deverá estar encerrada entre parênteses. Uma expressão binária consiste de dois operandos unidos por um operador binário. Uma expressão ternária consiste de três operandos ligados por um operador de expressão condicional.

A linguagem C implementa os seguintes operadores unários: Símbolo Nome – ~ ! Operadores de negação e complemento * & Operadores indiretos e de endereçamento sizeof Operador de tamanho + Operador plus unário ++ –– Operadores incrementador e decrementador unários

Tabela 9-9 - Operadores unários

Os operadores binários associam-se da esquerda para a direita. A linguagem C implementa os seguintes operadores binários:

Símbolo Nome * / % Operadores de multiplicação, divisão

e resto da divisão + – Operadores de adição e subtração << >> Operadores de deslocamento < > <= >= == !=

Operadores relacionais

& | ^ Operadores bit a bit && || Operadores lógicos , Operadores de avaliação seqüencial

Tabela 9-10 – Operadores binários

Os operadores de expressão condicionais têm precedência menor que as expressões binárias e diferem em que são associados pela direita.

As expressões com operadores também incluem as expressões de atribuição, que usam os operadores de atribuição unários ou binários. Os operadores de atribuição unária são os operadores incremento (++) e o decremento (--); os operadores de atribuição são o operador simples de atribuição (=) e o operador de atribuição composto. Cada operador de atribuição composto é uma combinação de outro operador binário com o operador simples de atribuição.

99..55..11.. PPrreecceeddêênncciiaa ee OOrrddeemm ddee AAvvaalliiaaççããoo A precedência e associatividade dos operadores C afetam o agrupamento e a

avaliação dos operandos nas expressões. A precedência de um operador só é significativa se 6 Notar que uma expressão do tipo void * é do tipo ponteiro para void, e não do tipo void. Se um objeto é casteado para o tipo void, a expressão resultante não poderá ser atribuída a nenhum item. Similarmente, um objeto do tipo cast não é um valor l-value válido, de forma que não poderão ser feitas atribuições a um objeto do tipo cast.

Page 164: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

142142142142

9999

outros operadores com precedência mais alta ou mais baixa estão presentes. As expressões com maior ordem de precedência, serão avaliados primeiro. A precedência pode também ser entendida como uma força de ligação entre operandos. Os operadores com precedência maior podem ser entendidos como conectando os seus operandos mais fortemente.

A tabela a seguir resume a precedência e associação (a ordem na qual os operandos são avaliados) dos operadores em C, listando-os na ordem de precedência da mais alta até a mais baixa. Quando muitos operadores são colocados juntos, estes terão igual precedência e serão avaliados de acordo com a sua associatividade. Os operadores da tabelas serão descritos em detalhes nas seções que seguem. Precedência Símbolo7 Tipo de Operação Associatividade Maior Precedência

[ ] ( ) . –> ++ postfixado e – postfixado

Expressão Esquerda para direita

++ prefixado e -- prefixado sizeof & * + – ~ !

Unário Direita para esquerda

Casts Unário Direita para esquerda * / % Multiplicação Esquerda para direita + – Adição Esquerda para direita << >> Deslocamento de bits Esquerda para direita < > <= >= Relacional Esquerda para direita == != Igualdade Esquerda para direita & AND entre bits Esquerda para direita ^ XOR entre bits Esquerda para direita | OR entre bits Esquerda para direita && AND lógico Esquerda para direita || OR lógico Esquerda para direita ? : Expressão condicional Direita para esquerda = *= /= %=

+= –= <<= >>= &= ^= |=

Atribuição simples e composta8 Direita para esquerda

Menor Precedência

, Avaliação seqüencial Esquerda para direita

Tabela 9-11 – Operadores por ordem de precedência

Somente os operadores de avaliação seqüencial (,), AND lógico (&&), OR lógico (||), de expressão condicional (? :) e o operador de chamada de função, constituem pontos de seqüência e garantem uma ordem particular de avaliação dos seus operandos. O operador de chamada de função é o conjunto de parênteses que seguem ao identificador da função. O operador de avaliação seqüencial (,) garante avaliar os seus operandos de esquerda para direita9.

Os operadores lógicos também garantem a avaliação dos seus operandos da esquerda para direita. Porém, eles avaliam o menor número de operandos necessários para determinar o resultado da expressão. Isto é chamado de avaliação de “curto-circuito”. 7 Os operadores estão listados na ordem descendente de precedência. Se vários operadores aparecerem na mesma linha ou em um grupo, terão igual precedência. 8 Os operadores de atribuição simples e compostos têm igual precedência. Uma expressão pode conter vários operadores com igual precedência. Quando vários de tais operadores aparecem no mesmo nível de uma expressão, a avaliação procede de acordo com a associatividade do operador, de direita a esquerda ou vice-versa. A direção da avaliação não afeta o resultado da expressão que inclui mais de um operador multiplicação (*), adição (+) ou operador binário entre bits (& | ^) no mesmo nível. A ordem das operações não é definida pela padronização. O compilador é livre para avaliar tais expressões em qualquer ordem, sempre e quando garante um resultado consistente. 9 Notar que o operador vírgula em uma chamada de função não é o mesmo que o operador seqüencial de avaliação e não fornece as mesmas garantias. Para maiores informações, ver na seção 9.3.9.

Page 165: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

143143143143

9999

Desta forma, alguns operandos de uma expressão poderão não ser avaliados. Por exemplo, na expressão x && y++

o segundo operando, y++, será avaliado somente se x for verdadeiro (não zero). Assim, y não será incrementado caso x seja falso (0).

A Tabela 9-12 mostra como a maioria dos compiladores automaticamente conecta algumas expressões simples:

Expressão Interpretação automática

a & b || c (a & b) || c a = b || c a = (b || c) q && r || s-- (q && r) || s––

Tabela 9-12 - Exemplo de relacionamentos automáticos

Na primeira expressão, o operador AND entre bits (&) tem precedência maior em comparação com o operador OR lógico (||), de forma que a & b forma o primeiro operando da operação OR lógica.

Na segunda expressão, o operador lógico OR (||) tem precedência maior que o operador simples de atribuição (=), de forma que b || c é agrupado como o operando da direita na atribuição10.

A terceira expressão mostra uma expressão corretamente formada que pode produzir um resultado não esperado. O operador lógico AND (&&) tem precedência sobre o operador OR lógico (||), de forma que N q && r seja avaliada antes de s--. Porém, se q && r resulte num valor verdadeiro, s— não será avaliada, e s não será decrementada. Se não houver o decremento, poderá ocasionar um problema no programa, sendo que s—deverá aparecer como o primeiro operando da expressão, ou s deverá ser decrementada em uma operação separada.

A seguinte operação é incorreta e produzirá uma mensagem de diagnóstico em tempo de compilação:

Expressão incorreta Agrupamento por default P == 0 ? p += 1: p += 2 ( p == 0 ? p += 1 : p ) += 2

Nesta expressão, o operador de igualdade (==) tem a mais alta precedência, de forma que p == 0 é agrupado como um operando. O operador de expressão condicional (? :) possui a próxima mais alta precedência. O seu primeiro operando é p == 0, e o seu segundo operando é p += 1. Porém, o último operando do operador de expressão condicional é considerado sendo p no lugar de p += 2, uma vez que a ocorrência de p conecta com força maior ao operador de expressão condicional que ao operador de atribuição composto.

Um erro de sintaxe deverá ocorrer porque += 2 não possui operando de lado esquerdo. Para evitar este tipo de problemas e produzir um código mais claro devem ser usados parênteses. Por exemplo: ( p == 0 ) ? ( p += 1 ) : ( p += 2 )

10 Notar que o valor atribuído será 0 ou 1.

Page 166: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

144144144144

9999

99..55..22.. CCoonnvveerrssõõeess AArriittmmééttiiccaass UUssuuaaiiss A maioria dos operadores C efetua conversões de tipos para deixar os operandos de

uma expressão num tipo comum ou para estender os valores short para o tamanho do inteiro usado pelo microprocessador em questão. As conversões efetuadas pelos operadores dependem de cada operador e do tipo de operando ou operandos. Porém, muitos operadores efetuam conversões similares em operandos do tipo inteiro ou ponto flutuante. As conversões de um valor de operando para um tipo compatível não ocasionam mudanças no seu valor original.

As conversões aritméticas mais comuns são resumidas a seguir. Estes passos são aplicados freqüentemente pelos operadores binários que esperam tipos aritméticos e somente se os dois operandos não são do mesmo tipo. O propósito e deixar os valores num tipo comum que também é do mesmo tipo que o resultado. Para determinar que conversões serão efetuadas, os compiladores aplicam em geral o seguinte algoritmo para as operações binárias em expressões. Os passos a seguir não têm ordem de precedência. Se um dos operandos é do tipo long double, o outro será convertido para long double. Se a condição acima não é verdadeira e um dos operandos for do tipo double, o outro

operando será convertido no tipo double. Se as condições acima não correspondem, e um dos operandos for do tipo float, o outro

operando será convertido ao tipo float. Se as três condições acima não correspondem (nenhum dos operandos são do tipo ponto

flutuante) então serão executadas as conversões inteiras nos operandos como segue: Se um dos operandos for do tipo unsigned long, o segundo operando será convertido

para este tipo. Se a condição acima não for verdadeira, e um dos operandos for do tipo long e o outro do

tipo unsigned int, ambos operandos serão convertidos para o tipo unsigned long. Se as duas condições acima não forem verdadeiras, e um dos operandos for do tipo long,

o outro será convertido no tipo long. Se nenhuma das três condições anterior for verdadeira, e um dos operandos for do tipo

unsigned int, o outro operando será convertido no mesmo tipo. Se nenhuma das condições acima for verdadeira, ambos operandos serão convertidos ao

tipo int.

O código que segue mostra as regras de conversão: float fVal; double dVal; int iVal; unsigned long ulVal; dVal = iVal * ulVal; /* iVal convertida para unsigned long * passo 4. * Resultado da multiplicação convertido em double */ dVal = ulVal + fVal; /* ulVal convertida para float * passo 3. * Resultado da adição convertida em double */

99..55..33.. OOppeerraaddoorreess PPoossttffiixxaaddooss Os operadores postfixados têm a mais alta precedência (maior força de ligação) na

avaliação de expressões.

Os operadores neste nível de precedência são os subscritos de array, chamadas de função, membros de unions e estruturas, e os operadores de incremento e decremento postfixado.

Page 167: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

145145145145

9999

AArrrraayyss ddee uummaa DDiimmeennssããoo

Uma expressão postfixada seguida por uma outra encerrada entre colchetes ([ ]) é uma representação subscrita de um elemento de um objeto do tipo array. A expressão subscrita representa o valor para o endereço que a expressão representa, da forma:

expressão postfixada [ expressão ]

Usualmente o valor representado pela expressão postfixada é um valor de ponteiro, tal como um identificador de array, e a expressão é um valor inteiro. Porém, todo o que é requerido sintaticamente é que uma das expressões seja do tipo ponteiro e a outra do tipo inteiro, independente da ordem. Assim o valor inteiro pode estar também como expressão postfixada e o valor do ponteiro pode estar entre os colchetes na expressão (ou expressão subscrita). Por exemplo, o seguinte código é válido: int sum, *ptr, a[10]; void main()

ptr = a; a[4] = 9;

sum = 4[ptr]; /* mesmo que sum = ptr[4] ou sum = a[4] */

Código 9-7

O resultado na variável sum será o valor 9.

As expressões subscritas são geralmente usadas para referenciar os elementos de um array, mas pode também ser aplicado o subscrito para qualquer ponteiro. Qualquer seja a ordem de colocação dos valores, a expressão deve ser encerrada entre colchetes 11.

A expressão subscrita é avaliada pela adição do valor inteiro ao valor do ponteiro, e então é aplicado o operador indireto (*) ao resultado 12. De fato, para um array de uma dimensão, as seguintes quatro expressões são equivalentes, assumindo que a é um ponteiro e b é um inteiro: a[b] *(a + b) *(b + a) b[a]

Ver o seguinte exemplo de código: void main(void)

int a[10],b=3,res1,res2, res3,res4; a[b] = 9; res1 = a[b]; res2 = *(a + b); res3 = *(b + a); res4 = b[a];

Código 9-8

O resultado nas variáveis res1, res2, res3 e res4, é igual a 9.

11 É importante ressaltar que a maioria dos compiladores C para microcontroladores é de implementação mais simples e raramente chegam a este nível de complexidade sintática. 12 Ver seção 9.5.4.

Page 168: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

146146146146

9999

De acordo com as regras de conversão para o operador de adição 13, o valor inteiro é convertido para um offset de endereço, multiplicando-o pelo comprimento (em bytes) do tipo de dado endereçado pelo ponteiro.

Por exemplo, suponha que o identificador line se refere a um array de valores inteiros. O seguinte procedimento será usado para avaliar a expressão subscrita line[i]: O valor inteiro i é multiplicado pelo número de bytes definido como sendo o tamanho de um

item int. O valor convertido de i representa a posição do inteiro referenciado por i. O valor convertido é adicionado ao do ponteiro original (line), para encontrar o endereço cujo

offset é de i posições do tipo int a partir da posição origem line O operador indireto será então aplicado ao novo endereço. O resultado é o valor do elemento

do array na posição indicada.

A expressão subscrita line[0] representa o valor do primeiro elemento de line já que o offset a partir do endereço representado por line é 0. Similarmente, uma expressão tal como line[5] se refere ao elemento que possui um offset de 5 posições a partir de line, ou seja, o sexto elemento do array. Por exemplo, se o sistema comporta valores int de 2 bytes, line[5] terá um offset de 5 x 2 bytes = 10 bytes a partir de line (ou line[0]).

AArrrraayyss MMuullttiiddiimmeennssiioonnaaiiss

A expressão subscrita pode também ter múltiplos subscritos, como segue:

expressão1 [expressão2] [expressão3]...

As expressões subscritas associam de esquerda para direita. A expressão subscrita mais à esquerda, expressão1 [expressão2], é avaliada primeiro. O endereço resultante da adição de expressão1 e expressão2 forma uma expressão ponteiro; então expressão3 é somado à expressão ponteiro para formar uma nova expressão ponteiro, a assim sucessivamente até que a última expressão subscrita tenha sido adicionada. Finalmente é aplicado o operador indireto (*), a menos que o valor final do ponteiro esteja endereçando um tipo array (ver exemplos a seguir).

As expressões com múltiplos subscritos se referenciam a elementos de arrays multidimensionais. Um array multidimensional é um array cujos elementos são arrays. Por exemplo, o primeiro elemento de um array tridimensional é um array de duas dimensões.

Exemplos

Nos seguintes exemplos, foi declarado um array chamado prop com três elementos, cada um destes sendo um array 4 x 6 de valores int. int prop[3][4][6]; int i, *ip, (*ipp)[6];

Uma referencia a um elemento do array prop pode ser da seguinte forma: i = prop[0][0][1];

O exemplo a seguir mostra como referenciar o segundo elemento int de prop. Os arrays são armazenados por linha, de forma que o último subscrito varia mais rapidamente, de modo que a expressão prop[0][0][2] se referencia ao próximo (terceiro) elemento do array, e assim sucessivamente. i = prop[2][1][3];

13 Ver seção 9.5.6.

Page 169: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

147147147147

9999

A linha acima é uma referência um pouco mais complicada para um elemento individual de prop. A expressão será avaliada como segue: 1. O primeiro subscrito 2, é multiplicado pelo tamanho do array 4x6 de int e adicionado ao valor

do ponteiro prop. O resultado aponta para o terceiro array 4x6 de prop. 2. O segundo subscrito, 1, é multiplicado pelo tamanho do array de ints de 6 elementos e então

adicionado ao endereço representado por prop[2]. 3. Cada elemento do array de 6 elementos é um valor do tipo int, de forma que o subscrito 3, é

multiplicado pelo tamanho de um int antes, e então adicionado a prop[2][1]. O ponteiro resultante endereça o quarto elemento de um array de 6 elementos.

4. Finalmente é aplicado o operador indireto ao valor do ponteiro resultante. O resultado é o elemento inteiro armazenado naquele endereço.

Os próximos dois exemplos mostram casos onde o operador indireto não é aplicado. ip = prop[2][1]; ipp = prop[2];

Na primeira instrução, a expressão prop[2][1] é uma referência válida para o array tridimensional prop; ela se refere a um array de 6 elementos (declarados no exemplo anterior). Uma vez que o valor do ponteiro endereça um array, o operador indireto não será aplicado.

Similarmente, o resultado da expressão prop[2] na segunda instrução ipp = prop[2] onde o resultado é um valor de ponteiro endereçando um array bidimensional.

CChhaammaaddaass ddee FFuunnççããoo

Uma chamada de função é uma expressão que inclui o nome da função que está sendo chamada ou o valor de um ponteiro para a função e, opcionalmente, os argumentos que estão sendo passados para esta.

Uma expressão de chamada de função tem o valor e o tipo do valor de retorno da mesma. Uma função não pode retornar um objeto do tipo array. Se o tipo de retorno da função é void (i.e., a função foi declarada para não retornar valores), a expressão da chamada de função também será do tipo void.

MMeemmbbrrooss ddee EEssttrruuttuurraass ee UUnniioonnss1144

Uma expressão de seleção de membro refere-se aos membros de estruturas e unions. Tal expressão possui o valor e o tipo do membro selecionado.

A sintaxe tem duas formas possíveis:

expressão-postfixada . identificador expressão-postfixada –> identificador

Na primeira forma a expressão postfixada representa o valor de um tipo estrutura ou union, e o identificador seleciona um determinado membro da estrutura ou union em questão. O valor da operação é o do identificador que é um l-value se a expressão postfixada é um l-value 15.

14 Maiores detalhes no capítulo 16. 15 Ver Expressões L-Value e R-Value para maiores informações, na seção 9.3.6..

Page 170: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

148148148148

9999

Na segunda forma, a expressão postfixada representa um ponteiro para uma estrutura ou union, e o identificador seleciona o membro da mesma. O valor é o do identificador e é um l-value. As duas formas de expressões de seleção de membros possuem efeitos similares.

De fato, uma expressão envolvendo o operador de seleção de membro (->) é uma versão simplificada da expressão que usa o operador ponto (.) se a expressão anterior ao operador ponto consiste de um operador indireto (*) aplicado ao valor de um ponteiro. Assim, expressão –> identificador

é equivalente a (*expressão) . identificador

quando a expressão é um valor ponteiro.

Exemplos

Os seguintes exemplos se referenciam a uma declaração de estrutura 16. struct pair int a; int b; struct pair *sp; item, list[10];

Uma expressão de seleção de membro para a estrutura item pode aparecer como: item.sp = &item;

No exemplo acima, o endereço da estrutura item é atribuído ao membro sp da mesma estrutura. Isto permite que item contenha um ponteiro para si mesma. (item.sp)–>a = 24;

No exemplo, a expressão ponteiro item.sp é usada com o operador de seleção de membro (->) para atribuir um valor ao membro a. list[8].b = 12;

Esta instrução mostra como selecionar um membro de uma estrutura individual de um array de estruturas.

OOppeerraaddoorreess ddee IInnccrreemmeennttoo ee DDeeccrreemmeennttoo PPoossttffiixxaaddooss

Os operadores de incremento e decremento postfixado são tipos escalares que implementam l-values modificáveis.

A sintaxe básica pode ser das seguintes formas:

Expressão-postfixada ++ Expressão-postfixada --

O resultado da operação de incremento ou decremento é o valor do operando. Depois que o resultado é obtido, o valor do operando é incrementado (ou decrementado). O código que segue mostra o operador de incremento postfixado. if( var++ > 0 )

*p++ = *q++;

16 Para maiores informações sobre o operador indireção (*), ver Operadores de Indireção e Endereçamento

Page 171: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

149149149149

9999

No exemplo, a variável var é comparada com 0, depois é incrementada. Se var for positiva antes de ser incrementada, a próxima instrução será executada. Primeiro, o valor do objeto apontado por q é atribuído ao objeto apontado por p. Logo então, q e p serão incrementados.

99..55..44.. OOppeerraaddoorreess UUnnáárriiooss eemm CC Os operadores unários aparecem antes que o operando e associam de direita a

esquerda. Os operadores unários são: & * + – ~ !.

As formas de utilização podem ser como segue:

++ expressão-unária -- expressão-unária operador-unário expressão-cast sizeof expressão-unária sizeof ( tipo )

OOppeerraaddoorreess ee IInnccrreemmeennttoo ee DDeeccrreemmeennttoo PPrreeffiixxaaddooss

Os operadores unários de incremento e decremento (++ e --) são chamados de prefixados quando aparecem antes do operando. O incremento e decremento postfixado tem uma precedência maior que o prefixado. O operando deve ser do tipo inteiro, ponto flutuante ou ponteiro, e deve ser uma expressão l-value modificável (uma expressão sem o atributo const). O resultado será um l-value.

Quando o operador aparece antes do operando, o operando é incrementado ou decrementado e seu novo valor é o resultado da expressão.

Um operando do tipo inteiro ou ponto flutuante é incrementado ou decrementado pelo valor inteiro 1. O tipo do resultado é do mesmo tipo do operando. Um operando do tipo ponteiro é incrementado ou decrementado pelo tamanho do objeto que ele endereça 17. Um ponteiro incrementado aponta para o próximo objeto, e um ponteiro decrementado aponta para o objeto anterior.

O exemplo a seguir ilustra o funcionamento do operador unário decrementador prefixado: if( line[--i] != '\n' ) return;

No exemplo, a variável i é decrementada antes de se utilizada como um subscrito para line.

OOppeerraaddoorreess IInnddiirreettooss ee ddee EEnnddeerreeççaammeennttoo

O operador indireto (*) acessa um valor de forma indireta através de um ponteiro. O operando deve ser um valor do tipo ponteiro. O resultado da operação é um valor endereçado pelo operando, isto é, o valor no endereço para o qual o operando aponta. O tipo de resultado é o tipo que o operando endereça.

17 O tamanho é dado em bytes.

Page 172: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

150150150150

9999

Se o operando aponta para uma função, o resultado é um designador de função. Se aponta para uma posição de armazenamento, o resultado é um l-value designando o local de armazenamento.

Se o valor do ponteiro for inválido, o resultado será indefinido. A lista que segue inclui algumas das condições mais comuns que podem invalidar o valor de um ponteiro. O ponteiro é um ponteiro null. O ponteiro especifica o endereço de um elemento local que não é visível no momento da

referência (fora do escopo). O ponteiro especifica um endereço que está inapropriadamente alinhada para o tipo de objeto

apontado. O ponteiro especifica um endereço que não é utilizado pelo programa em execução.

O operador de endereço (&) fornece o endereço do operando. O operando de um operador de endereço pode ser um designador de função ou um l-value que designa um objeto que não é um campo de bit nem um objeto que foi declarado com o especificador de classe de armazenamento register.

O resultado da operação de endereçamento é um ponteiro para o operando. O tipo endereçado pelo ponteiro é do mesmo tipo do operando.

O operador de endereçamento pode ser somente aplicado a variáveis dos tipos fundamentais, estruturas ou unions que foram declarados no nível de escopo de arquivo, ou em referências subscritas de arrays. Nestas expressões, uma expressão constante que não inclui o endereço do operador pode ser adicionada ou subtraída da expressão do endereço.

Os seguintes exemplos mostram estas declarações: int *pa, x; int a[20]; double d;

A seguinte instrução utiliza o operador de endereço: pa = &a[5];

O operador de endereço (&) fornece o endereço do sexto elemento do array a. O resultado é armazenado na variável ponteiro pa. x = *pa;

O operador indireto (*) é utilizado no exemplo para acessar o valor inteiro armazenado no endereço apontado por pa. O valor é então atribuído à variável inteira x. if( x == *&x ) printf( "True\n" );

Esse exemplo imprime a palavra TRUE, demonstrando que o resultado da aplicação do operador indireto no endereço de x é o mesmo que x. int roundup( void ); /* Declaração de Função */ int (*proundup)(void) = roundup; int (*pround)(void) = &roundup;

Depois que a função roundup é declarada, dois ponteiros para a função são declarados e inicializados. O primeiro ponteiro proundup é inicializado usando somente o nome da função, enquanto que o segundo, pround, usa o operador de endereço para a inicialização. Ambas inicializações são equivalentes.

Page 173: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

151151151151

9999

Análise o seguinte código18: int roundup( void ); /* Declaração de Função */ int (*proundup)(void) = roundup; int (*pround)(void) = &roundup; void main() (*proundup)(); /* mesmo efeito que roundup() */ (*pround)(); /* mesmo efeito que roundup() */ roundup(void) int x = 2; x++; return x;

Código 9-9

OOppeerraaddoorreess AArriittmmééttiiccooss UUnnáárriiooss

Os operadores plus unário, negação aritmética (inversão de sinal), complemento e negação lógica serão discutidos nas linhas que seguem. Operador Descrição +

O operador plus unário precedendo uma expressão entre parênteses, força o agrupamento das operações encerradas. É utilizado em expressões que envolvem mais de um operador binário associativo ou comutativo. O operando deve ser de tipo aritmético. O resultado é o valor do operando.

O operador de negação aritmética produz o valor negativo (complemento de dois) do operando. O operando deve ser do tipo inteiro ou ponto flutuante. Este operador executa as conversões aritméticas usuais.

~

O operador complemento de bits (NOT entre bits) produz o complemento dos bits individuais do seu operando. O operando deve ser do tipo inteiro. Este operador efetua as conversões aritméticas usuais. O resultado é do tipo do operando depois da conversão.

!

O operador de negação lógica (NOT lógico) produz o valor 0 se o operando for verdadeiro (diferente de zero), e o valor 1 (um lógico 19) se o operando for falso (0). O resultado é do tipo int. O operando deve ser do tipo inteiro, ponto flutuante ou ponteiro.

Tabela 9-13 - Operadores aritméticos unários

As operações aritméticas unárias não são permitidas com ponteiros.

Os seguintes exemplos ilustram o uso dos operadores unários aritméticos: short x = 987; x = -x;

No exemplo acima, o novo valor de x é o negativo de 987, ou seja –987. unsigned short y = 0xAAAA; y = ~y;

No exemplo, o novo valor atribuído a y é o complemento de um (bits complementados individualmente) do valor unsigned de 0xAAAA, ou seja 0x5555. if( !(x < y) )

Se o valor de x for maior ou igual ao de y, o resultado da expressão será 1 (verdadeira). Se o valor de x for menor que o de y, o resultado será 0 (falso).

18 Maiores informações ver a seção 13.11. 19 Em operações lógicas, o valor do 1 lógico pode ser dado por qualquer valor diferente de zero. Em geral depois do uso do operador, o valor verdadeiro é dado por um int com todos os bits em 1; ex. 0xFFFF em sistemas de 16 bits.

Page 174: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

152152152152

9999

OO OOppeerraaddoorr ssiizzeeooff2200

O operador sizeof fornece a quantidade de bytes de memória de armazenamento, necessários para um objeto do tipo do operando. Este operador permite que sejam especificados tamanhos de dados que dependem do sistema em uso.

A sintaxe para o uso deste operador possui duas formas:

sizeof ( tipo )

O operando pode ser um identificador (que é uma expressão unária), ou uma expressão cast (i.e., um especificador de tipo colocado entre parênteses). A expressão unária não pode representar a um objeto do tipo campos de bits, um tipo incompleto nem um designador de função. O resultado é uma constante inteira sem sinal. O arquivo de cabeçalho padronizado STDDEF.H define este tipo como size_t.

Quando for aplicado o operador sizeof num identificador do tipo array ou union, o resultado será o tamanho do array inteiro no lugar do tamanho do ponteiro representado pelo identificador do array.

Quando for aplicado o operador a um nome do tipo estrutura ou union, ou a um identificador de estrutura ou union, o resultado será o número de bytes da estrutura ou union, incluindo os espaços de ajuste que não são utilizados reservados para alinhar os membros da estrutura ou union, nos limites da memória. Assim, o resultado pode não corresponder ao tamanho calculado pela adição dos espaços necessários pelos membros individuais.

Se um array sem tamanho definido for o último elemento de uma estrutura, o operador retornará o tamanho da estrutura sem o array. buffer = calloc(100, sizeof (int) );

O exemplo utiliza o operador sizeof para passar o tamanho de um int, que varia de acordo com a máquina e o sistema utilizado, como um argumento para uma função chamada calloc durante o tempo de execução. O valor retornado pela função é armazenado em buffer. static char *strings[] = "string um", "string dois", "string três", ; const int string_no = ( sizeof strings ) / ( sizeof strings[0] );

Nesse exemplo, strings é um array de ponteiros para char. O número de ponteiros é o número de elementos no array, mas não foi explicitamente especificado. É mais fácil determinar o número de ponteiros pelo uso do operador sizeof para calcular o número de elementos existentes no array. O valor inteiro constante string_no é inicializado para este valor. Devido a ser um valor constante, o valor de string_no não poderá ser modificado.

20 Mais detalhes na seção 16.4.

Page 175: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

153153153153

9999

99..55..55.. OOppeerraaddoorreess MMuullttiipplliiccaattiivvooss Os operadores multiplicativos efetuam operações de multiplicação (*), divisão (/) e

resto de divisão (%).

Os operandos do operador resto de divisão (%) devem ser inteiros. Os operandos dos operadores de multiplicação (*) e divisão (/) podem ser inteiros ou de ponto flutuante; os tipos dos operandos podem ser diferentes.

Os operadores multiplicativos executam as conversões aritméticas usuais nos operandos. O tipo do resultado é o tipo dos operandos após a conversão 21.

Os operadores multiplicativos da linguagem C são descritos a continuação: Operador Descrição

* O operador multiplicação faz com que os dois operandos sejam multiplicados. / O operador divisão faz com que o primeiro operando seja dividido pelo segundo. Se

dois operandos inteiros são divididos e o resultado não é um inteiro, este será truncado de acordo com as seguintes regras:

O resultado da divisão por 0 é indefinido de acordo com o padrão ANSI C. Dependendo do compilador poderá ser gerado um erro em tempo de compilação ou de execução.

Se ambos os operandos são positivos ou sem sinal, o resultado é truncado em direção ao 0.

Se ambos os operandos forem negativos, e se o resultado da operação é o maior inteiro menor ou igual que o quociente algébrico, ou se for o menor inteiro maior ou igual ao quociente algébrico, o resultado final dependerá da implementação do compilador.

% O resultado do operador resto da divisão é o resto quando o primeiro operando é divido pelo segundo. Quando a divisão é inexata, o resultado é determinado pelas seguintes regras:

Se o operando direito é zero, o resultado será indefinido. Se ambos os operandos forem positivos ou sem sinal, o resultado será positivo. Se ambos os operando forme negativos e o resultado for inexato, o resultado dependerá

da implementação do compilador.

Tabela 9-14 - Operadores de multiplicação, divisão e resto

As declarações mostradas a seguir são usadas pelos seguintes exemplos: int i = 10, j = 3, n; double x = 2.0, y;

A instrução a seguir usa o operador multiplicação: y = x * i;

Neste caso, x é multiplicada por i para dar o valor 20.0. O resultado é do tipo double. n = i / j;

No exemplo, 10 é dividido por 3. O resultado é truncado para baixo, dando o valor inteiro 3. n = i % j;

Esta instrução atribui a n o resto inteiro 1 da divisão entre 10 e 3.

21 Devido a que as conversões são efetuadas pelos operadores multiplicativos podem não funcionar corretamente em condições de overflow ou underflow, poderá ser perdida a informação do resultado, caso o resultado não possa ser representado no tipo de operando após a conversão.

Page 176: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

154154154154

9999

99..55..66.. OOppeerraaddoorreess ddee AAddiiççããoo Os operadores de adição efetuam somas (+) e subtrações (-). Os operandos podem

ser inteiros ou de ponto flutuante. Algumas operações aditivas podem ser efetuadas em valores ponteiro como será visto mais adiante.

Os operadores de adição efetuam as conversões aritméticas usuais nos operandos inteiros e de ponto flutuante. O tipo do resultado é o mesmo tipo dos operandos após a conversão 22.

OOppeerraaddoorr ddee AAddiiççããoo oouu SSoommaa ((++))

O operador de adição (+) faz com que os dois operandos sejam adicionados. Ambos operandos podem ser tanto do tipo inteiro ou ponto flutuante, ou ainda um dos operandos pode ser um ponteiro e o outro um inteiro.

Quando um inteiro é somado a um ponteiro, o valor inteiro, por exemplo i, é convertido multiplicando-o pelo tamanho (em bytes) do tipo de dado que o ponteiro aponta. Após a conversão, o valor inteiro representa i posições de memória, onde cada posição tem o comprimento especificado pelo tipo de ponteiro. Quando o valor inteiro convertido é somado ao valor do ponteiro, o resultado é um novo valor de ponteiro representando o endereço de i posições a partir do endereço original. O novo valor de ponteiro endereça o valor do mesmo tipo que o valor original do ponteiro e desta forma funciona igual que a indexação de um array 23.

OOppeerraaddoorr ddee SSuubbttrraaççããoo ((––))

O operador de subtração (-) subtrai o segundo operando do primeiro. Ambos operandos podem ser tanto do tipo inteiro, ponto flutuante ou um dos operandos pode ser um ponteiro e o outro um inteiro.

Quando dois ponteiros são subtraídos, a diferença é convertida em um valor integral com sinal dado pela divisão da diferença entre o tamanho e o valor do tipo de dados que os ponteiros apontam. O tamanho do valor inteiro é definido pelo tipo ptrdiff_t no arquivo de cabeçalho padrão STDDEF.H. O resultado representa o número de posições de memória do tipo entre dois endereços. O resultado é útil para dois elementos do mesmo array como será discutido em Aritmética de Ponteiros.

Quando um valor inteiro for subtraído de um valor de ponteiro, o operador de subtração converterá o valor inteiro, por exemplo i, multiplicando-o pelo tamanho em bytes do tipo de valor que o ponteiro aponta. Após a conversão, o valor inteiro representa i posições de memória, onde cada posição tem o comprimento especificado pelo tipo de ponteiro. Quando o valor inteiro for subtraído do valor do ponteiro, o resultado é o endereço de memória i posições antes do endereço original. O novo ponteiro aponta para um valor do tipo endereçado pelo valor original do ponteiro.

22 Uma vez que as conversões são efetuadas pelos operadores aditivos, os mesmos podem não funcionar corretamente em condições de overflow ou underflow. A informação poderá ser perdida, se o resultado de uma operação de adição, não puder ser representada no tipo dos operandos após a conversão. 23 Caso o ponteiro adicionado aponte para fora do array, o resultado será indefinido. Para maiores informações ver Aritmética de Ponteiros.

Page 177: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

155155155155

9999

UUssaannddoo ooss OOppeerraaddoorreess ddee AAddiiççããoo

Os seguintes exemplos, que ilustram o uso dos operadores de adição e subtração, utilizam as seguintes declarações: int i = 4, j; float x[10]; float *px;

As instruções abaixo são equivalentes: px = &x[4 + i]; px = &x[4] + i;

O valor de i é multiplicado pelo tamanho de um float e adicionado a &x[4]. O valor do ponteiro resultante é o endereço de x[8]. j = &x[i] - &x[i-2];

Noexemplo, o endereço do terceiro elemento de x (dado por x[i-2]) é subtraído do endereço do quinto elemento de x (dado por x[i]). A diferença é dividida pelo tamanho de um float; o resultado é o valor inteiro 2.

AArriittmmééttiiccaa ddee PPoonntteeiirrooss2244

As operações de adição que envolve um ponteiro e um inteiro são úteis somente se o operando ponteiro endereça um membro de um array, e o valor inteiro produz um offset entre os limites do mesmo array. Quando o valor inteiro é convertido para um offset de endereços, os compiladores assumem que somente existem posições de memória do mesmo tamanho entre o endereço original e o endereço original mais o offset.

Esta suposição é válida para membros de array. Por definição, um array; é uma série de valores do mesmo tipo; seus elementos estão armazenados em posições de memória contígua. Porém, o armazenamento de dados de qualquer tipo, exceto os arrays, não precisamente conterão o mesmo tipo de dados. Isto é, podem aparecer espaços vagos entre posições de memória, ainda com o mesmo tipo de dados. Desta maneira, os resultados da adição ou subtração a partir de endereços de um valor qualquer que não são elementos de array são indefinidos.

De forma similar, quando dois valores de ponteiros são subtraídos, a conversão assume que somente existem valores do mesmo tipo, sem espaços vagos, entre os endereços dados pelos operandos.

99..55..77.. OOppeerraaddoorreess ddee DDeessllooccaammeennttoo ddee BBiittss Os operadores de deslocamento de bits (shift) deslocam o primeiro operando para

a esquerda (<<) ou direita (>>) o número de posições que o segundo operando especifica.

Ambos operandos devem ser do tipo inteiro. Estes operadores efetuam as conversões aritméticas usuais; o tipo de resultado é o tipo do operando da esquerda após a conversão.

A posição mais à direita de um deslocamento de bits para a esquerda é preenchida com 0 a cada deslocamento de um bit. Em deslocamentos para a direita, os bits vagos serão preenchidos de acordo com o tipo do primeiro operando após a conversão. Se o tipo 24 Os ponteiros são tratados com mais detalhes no capítulo 12.

Page 178: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

156156156156

9999

for unsigned, serão setados em 0. Caso contrário, serão preenchidos com cópias do bit de sinal.

Em operações de deslocamento de bits para a esquerda sem overflow, a expressão expr1 << expr2

é equivalente a multiplicar expr1 por 2expr2. Em operações de deslocamentos à direita, expr1 >> expr2

é equivalente a dividir expr1 por 2expr2 caso expr1 seja unsigned ou um valor positivo.

O resultado das operações de deslocamento será indefinido se o segundo operando for negativo, ou se for maior ou igual ao tamanho em bits do primeiro operando 25. unsigned int x, y, z; x = 0x00AA; y = 0x5500; z = ( x << 8 ) + ( y >> 8 );

No exemplo, x é deslocado à esquerda de oito posições e y é deslocado à direita de oito posições. Os valores deslocados são somados, resultando em 0xAA55 sendo atribuído a z.

O deslocamento de um valor negativo para a direita de um bit fornece a metade do seu valor absoluto, arredondado para baixo. Por exemplo, -253 (binário 11111111 00000011) deslocado à direita de uma posição produz o valor –127 (binário 11111111 10000001). Um valor positivo 253 deslocado de um bit para a direita resulta no valor +126.

Os deslocamentos à direita preservam o bit de sinal. Quando um inteiro com sinal for deslocado para a direita, o bit mais significativo permanecerá setado. Quando um inteiro sem sinal for deslocado para a direita, o bit mais significativo será zerado.

Por exemplo, se o valor 0xF000 for sem sinal (unsigned), o resultado do deslocamento de um bit para a direita resultará em 0x7800. Se o valor 0xF0000000 for com sinal (signed), um deslocamento de um bit para a direita resultará em 0xF8000000. O deslocamento à direita de um valor inteiro positivo de todas as suas posições resulta em 0x00...00. O deslocamento à direita de um valor inteiro negativo de todas as suas posições resultará em 0xFF...FF.

99..55..88.. OOppeerraaddoorreess RReellaacciioonnaaiiss ee ddee IIgguuaallddaaddee Os operadores binários relacionais e de igualdade comparam o primeiro operando

com o segundo para testar a validade da relação especificada. O resultado de uma expressão relacional é “1” lógico se a relação testada for verdadeira e “0” lógico se for falsa. O tipo do resultado é um int.

Os operadores relacionais e de igualdade testam as seguintes relações: Operador Relação Testada < Primeiro operando menor que o segundo

25 Uma vez que as conversões são efetuadas pelos operadores de deslocamento podem não funcionar corretamente em condições de overflow ou underflow, a informação poderá ser perdida caso o resultado da operação de deslocamento não possa ser representada no tipo do primeiro operando após a conversão.

Page 179: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

157157157157

9999

> Primeiro operando maior que o segundo <= Primeiro operando menor ou igual que o segundo >= Primeiro operando maior ou igual que o segundo == Primeiro operando igual ao segundo != Primeiro operando diferente ao segundo

Tabela 9-15 - Operadores Relacionais

Os primeiros quatro operadores da lista acima tem precedência mais alta que os operadores de igualdade (== e !=).

Os operandos podem ser do tipo inteiro, ponto flutuante ou ponteiro. Os tipos de operandos podem ser diferentes. Os operadores relacionais efetuam as conversões aritméticas usuais em operandos inteiros e de ponto flutuante. Além disto, podem ser usadas as seguintes combinações de tipos de operandos com os operadores relacionais e de igualdade: Ambos os operandos de qualquer operador relacional ou de igualdade pode ser ponteiros para

o mesmo tipo. Para os operadores de igualdade (==) e desigual (!=), o resultado da comparação indicará se os dois ponteiros endereçam as mesmas posições de memória. Para os outros operadores relacionais (<, >, <=, e >=), o resultado da comparação indica a posição relativa dos dois endereços de memória para os objetos sendo apontados. Os operadores relacionais comparam somente os offsets. A comparação entre ponteiros é definida somente para partes do mesmo objeto. Se o ponteiro

se refere a membros de um array, a comparação é equivalente à comparar os subscritos correspondentes. O endereço do primeiro endereço do array é sempre menor que o endereço do último elemento. No caso de estruturas, os ponteiros para membros de estruturas declarados depois são maiores que os ponteiros para membros declarados antes na estruturas. Os ponteiros para membros da mesma union são iguais. O valor de um ponteiro pode ser comparação com o valor constante 0 usando os operadores

de igualdade (==) ou desigualdade (!=). Um ponteiro com valor 0 é chamado de ponteiro “null”, isto é, ele não está apontando para uma posição válida de memória. Os operadores de igualdade seguem as mesmas regras que os operadores relacionais, mas

permitem algumas tarefas adicionais: um ponteiro pode ser comparado com uma expressão inteira constante com valor 0, ou com um ponteiro para void. Se dois ponteiros forem ponteiros null, estes serão comparados como sendo iguais. Os operadores de igualdade comparam segmentos e offsets de memória.

Os exemplos a seguir ilustram o uso de operadores relacionais e de igualdade. int x = 0, y = 0; if ( x < y )

Uma vez que x e y são iguais. A expressão do exemplo resulta em 0 lógico (falso). char array[10]; char *p; for ( p = array; p < &array[10]; p++ ) *p = '\0';

O fragmento de código no exemplo seta cada elemento do array com a constante caractere null. enum color red, white, green col; . . . if ( col == red ) . . .

Page 180: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

158158158158

9999

As linhas acima declaram uma variável de enumeração chamada col que é do tipo enum color. A qualquer instante, a variável pode conter o valor inteiro 0, 1 ou 2, que representa um dos elementos do grupo de cores, red, white e green, respectivamente. Se col contém o valor 0 então quando o comando if for executado, qualquer comando dependendo deste também será executado.

99..55..99.. OOppeerraaddoorreess LLóóggiiccooss eennttrree BBiittss Os operadores de bits executam as operações AND entre bits (&), OR entre bits

(|) e XOR 26 entre bits (^).

Os operandos de bits devem ser do tipo inteiro, mas estes tipos podem ser diferentes. Estes operadores efetuam as conversões aritméticas usuais; o tipo do resultado é o tipo dos operandos após a conversão 27.

Os operadores entre bits são descritos na .Tabela 9-16 Operador Descrição & O operador AND entre bits compara cada bit do primeiro operando com o bit

correspondente do segundo operando. Se ambos bits estiverem em 1, o resultado do bit correspondente será setado em 1. De outra forma, o resultado do bit correspondente será setado para 0.

^ O operados OR exclusivo (XOR) entre bits compara cada bit do primeiro operando com o bit correspondente do segundo operando. Se um dos bits é 0 e o outro bit é 1, o bit resultante correspondente será setado para 1. Caso contrário, o resultado do bit correspondente será setado para 0.

| O operador OR 28 entre bits compara cada bit do primeiro operando com o bit correspondente do segundo operando. Se ambos bits estiverem em 0, o bit resultante correspondente será setado para 0. Caso contrário, o bit resultante correspondente será setado para 1.

Tabela 9-16 - Operadores lógicos entre bits

As declarações a seguir são utilizadas nos seguintes exemplos: short i = 0xAB00; short j = 0xABCD; short n; n = i & j;

O resultado atribuído a n neste primeiro exemplo é o mesmo que o valor de i (0xAB00 hexadecimal). n = i | j;

O operador OR entre bits resulta no valor 0xABCD. n = i ^ j;

O operador XOR entre bits produzirá o valor 0x00CD

99..55..1100.. OOppeerraaddoorreess LLóóggiiccooss Os operadores lógicos executam as operações AND lógico (&&) e OR lógico (||).

26 Exclusive-OR. 27 O resultado das operações entre bits com inteiros signed dependem da implementação do compilador de acordo com o padrão ANSI C. 28 Também conhecido como OR inclusivo.

Page 181: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

159159159159

9999

Os operadores lógicos não executam as conversões aritméticas usuais. No lugar disto, eles avaliam cada operando em termos de equivalência com 0. O resultado das operações lógicas é 0 lógico ou 1 lógico. O tipo de resultado é int.

Os operadores lógicos da linguagem C são descritos na Tabela 9-17. Operador Descrição && O operador AND lógico produz o valor 1 lógico se ambos os operandos forem valores

diferentes de zero. Se algum dos operandos for igual a 0, o resultado será 0. Se o primeiro operando da operação AND lógico for igual a 0, o segundo operando não será avaliado.

|| O operador OR lógico efetua uma operação OR nos seus operandos. O resultado é 0 se ambos os operandos tiverem valores iguais a zero. Se algum dos operandos possuir um valor diferente de zero, o resultado é 1 lógico. Se o primeiro operando de uma operação OR lógico for um valor diferente de 0, o segundo operando não será avaliado.

Tabela 9-17 - Operadores lógicos

Os operandos das expressões lógicas AND e OR são avaliados de esquerda para direita. Se o valor do primeiro operando for suficiente para determinar o resultado da operação, o segundo operando não será avaliado 29. Existe um ponto de seqüência depois do primeiro operando 30.

Os seguintes exemplos ilustram o uso dos operadores lógicos: int w, x, y, z; if ( x < y && y < z ) printf( "x é menor que z\n" );

No exemplo, a função printf é chamada para imprimir a mensagem se x for menor que y e, y for menor que z. Se x for maior que y, o segundo operando (y < z) não será avaliado e nada será impresso. É importante notar que isto pode ocasionar problemas nos casos em que o segundo operando possui efeitos colaterais que podem não ser efetuados por alguma outra razão. printf( "%d" , (x == w || x == y || x == z) );

No exemplo, se x é igual a uma das variáveis w, y ou z, o segundo argumento da função printf será avaliado como sendo verdadeiro, e o valor 1 lógico será impresso. Caso contrário, o segundo argumento será avaliado como falso e será impresso o valor 0. Tão cedo quanto uma das condições seja avaliada como verdadeira, a avaliação do resto da expressão é cancelada.

99..55..1111.. OOppeerraaddoorreess ddee EExxpprreessssããoo CCoonnddiicciioonnaall A linguagem C possui um operador ternário: o operador de expressão condicional

(? :).

A sintaxe é definida como:

expressão-OR-lógica ? expressão : expressão-condicional

A expressão OR lógica deve ser do tipo inteiro, ponto flutuante ou ponteiro. Esta é avaliada em termos de equivalência com 0. Um ponto de seqüência segue à expressão OR lógica. A avaliação dos operandos procede como segue:

29 Usualmente chamado de avaliação em “curto-circuito”. 30 Ver Pontos de Seqüência para maiores informações.

Page 182: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

160160160160

9999

Se a expressão OR lógica for verdadeira, a expressão será avaliada. O resultado da avaliação da expressão é dado por uma expressão não terminal 31. Se a expressão OR lógica for igual a 0, a expressão condicional será avaliada. O resultado da

expressão é o valor da expressão condicional 32.

É importante notar que uma das expressões será avaliada, mas não ambas.

O tipo do resultado da operação condicional depende do tipo de operando da expressão ou da expressão condicional de acordo com as seguintes regras: Se a expressão lógica ou a expressão condicional forem de tipo inteiro ou ponto flutuante

(ambos os tipos podem ser diferentes), o operador executa as conversões aritméticas usuais. O tipo do resultado é o tipo dos operandos após a conversão. Se ambas expressões possuem o mesmo tipo de estrutura, union ou ponteiro, o tipo do

resultado é o mesmo tipo da estrutura, union ou ponteiro. Se ambos operandos forem do tipo void, o resultado será do tipo void. Se um dos operandos for um ponteiro para um objeto de qualquer tipo, e o outro operando é

um ponteiro para void, o ponteiro para o objeto é convertido para um ponteiro para void e o resultado é um ponteiro para void. Se a expressão ou a expressão condicional for um ponteiro e o outro operando for uma

expressão constante com valor 0, o tipo do resultado é igual ao tipo do ponteiro.

No caso de comparações de tipos para ponteiros, qualquer qualificador de tipo (const ou volatile) no tipo para o qual o ponteiro aponta, será insignificante, mas o tipo resultante herdará o qualificador de ambos os componentes da expressão condicional.

Os seguintes exemplos mostram o uso do operador condicional: j = ( i < 0 ) ? ( -i ) : ( i );

O exemplo atribui o valor absoluto de i a j. Se i for menor que 0, então –i será atribuído a j. Se i for maior ou igual a 0, então i será atribuído a j. void f1( void ); void f2( void ); int x; int y; . . . ( x == y ) ? ( f1() ) : ( f2() );

No exemplo foram declaradas duas funções (f1 e f2) e duas variáveis (x e y). Mais tarde no programa, se ambas as variáveis tiverem o mesmo valor, a função f1 será chamada. Caso contrário será chamada f2.

99..55..1122.. OOppeerraaddoorreess ddee AAttrriibbuuiiççããoo Um operador de atribuição atribui o valor do operando direito para a posição de

armazenamento nomeada pelo operando esquerdo. Assim, o operando esquerdo de uma operação de atribuição deverá ser um l-value modificável. Após a atribuição, a expressão de atribuição tem o valor do operando esquerdo mas não é um l-value.

Os operadores de atribuição em C podem transformar e atribuir valores num única operação. A linguagem C fornece os seguintes operadores de atribuição:

Operador Operação Executada = Atribuição simples

31 Isto permite que a expressão seja avaliada somente se a expressão OR lógica seja verdadeira. 32 Isto permite que a expressão condicional seja avaliada somente se a expressão OR lógica seja falsa.

Page 183: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

161161161161

9999

*= Atribuição com multiplicação /= Atribuição com divisão %= Atribuição com resto de divisão += Atribuição com adição –= Atribuição com subtração <<= Atribuição com deslocamento de

bits à esquerda >>= Atribuição com deslocamento de

bits à direita &= Atribuição com AND entre bits ^= Atribuição com XOR entre bits |= Atribuição com OR entre bits

Tabela 9-18 - Operadores de atribuição

Na atribuição, o tipo do operando à direita é convertido no tipo do operando da esquerda, e o valor é armazenado no operando esquerdo depois que a atribuição tenha sido efetuada. O operando à esquerda não pode ser um array, uma função e nem uma constante 33.

AAttrriibbuuiiççããoo SSiimmpplleess

O operador de atribuição simples atribui o valor do operando da direita para o da esquerda. O valor do operando da direita é convertido para o tipo da expressão de atribuição e substitui o valor armazenado no objeto designado pelo operando esquerdo. São aplicadas as regras de conversão antes da atribuição 34. double x; int y; x = y;

No exemplo, o valor de y é convertido para o tipo double e então atribuído a x.

AAttrriibbuuiiççõõeess CCoommppoossttaass

O operador de atribuições compostas combina o operador de atribuição simples com algum outro operador binário. Os operadores compostos de atribuição efetuam a operação especificada pelo operador adicional e depois, atribuem o resultado ao operando da esquerda. Por exemplo, uma expressão composta de atribuição tal como expressão1 += expressão2

pode ser entendida como expressão1 = expressão1 + expressão2

Contudo, a expressão de atribuição composta não é equivalente à sua versão equivalente porque a expressão de atribuição composta avalia expressão1 somente uma vez, enquanto que a versão expandida avalia esta variável duas vezes: na operação de adição e na operação de atribuição.

Os operandos de um operador de atribuição composto devem ser do tipo inteiro ou ponto flutuante. Cada operador de atribuição composto executa a conversão correspondente ao operador binário e restringe os tipos de operandos conseqüentemente. Os operadores de atribuição com adição (+=) e atribuição com subtração podem também operar com variáveis do tipo ponteiro como operando da esquerda, sendo que neste caso,

33 O processo da conversão, que depende dos tipos, é detalhado na seção 9.6. 34 Ver Conversões de Atribuição.

Page 184: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

162162162162

9999

o operando da direita deve ser do tipo inteiro. O resultado da operação de atribuição composta tem o tipo e valor igual ao do operando da esquerda. #define MASK 0xff00 n &= MASK;

No exemplo e efetuada uma operação AND entre bits entre a variável n e a macro MASK, cujo resultado é atribuído a n. A macro MASK é definida como uma constante usando a diretiva de pré-processador #define35.

99..55..1133.. OOppeerraaddoorr ddee AAvvaalliiaaççããoo SSeeqqüüeenncciiaall O operador de avaliação seqüencial, também chamado de operador “vírgula”, avalia

seus dois operandos seqüencialmente da esquerda para a direita.

O operando à esquerda do operador de avaliação seqüencial é avaliado como uma expressão void. O resultado da operação tem o mesmo valor e tipo que o operando da direita. Cada operando pode ser de qualquer tipo. O operador seqüencial de avaliação não efetua conversões de tipos entre os seus operandos e não fornece um l-value. Existe um ponto de seqüência depois do primeiro operando, que permite que todos os efeitos colaterais da avaliação do primeiro operando sejam completados antes de continuar a avaliação do operando da direita 36.

O operador de avaliação seqüencial é tipicamente usado, para avaliar duas ou mais expressões em contextos onde somente uma expressão é permitida.

As vírgulas podem ser usadas como separadores em alguns contextos. Porém, recomenda-se ser cuidadoso para não confundir o uso de vírgulas como um separador ao uso como um operador, sendo que os dois usos são completamente diferentes.

Este exemplo ilustra o uso do operador de avaliação seqüencial: for ( i = j = 1; i + j < 20; i += i, j-- );

No exemplo, com relação à terceira expressão da instrução (i+=i,j--) será comparado de forma independente.. O operando da esquerda (i += i) será comparado primeiro; logo então será comparado o operando da direita (j--). func_one( x, y + 2, z ); func_two( (x--, y + 2), z );

Na chamada da função func_one, são passados três argumentos separados por vírgulas: x, y + 2 e z. Na chamada da função func_two os parênteses forçam ao compilador a interpretar a primeira vírgula como um operador de avaliação seqüencial. Esta chamada de função passa dois argumentos para func_two. O primeiro argumento é o resultado da operação de avaliação seqüencial (x--, y + 2), que tem o valor e o tipo da expressão y + 2; o segundo argumento é z.

99..66.. CCoonnvveerrssõõeess ddee TTiippooss

As conversões de tipos dependem do operador especificado e do tipo de operando ou operadores. As conversões de tipos são executadas nos seguintes casos:

35 As diretivas de pré-processador são tratadas no capítulo 14. 36 Para maiores informações ver Pontos de Seqüência.

Page 185: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

163163163163

9999

Quando o valor de um tipo é atribuído a uma variável de um tipo diferente, ou se um operador converter o tipo de seu operando ou operandos antes de efetuar a operação. Quando o valor de um tipo é explicitamente casteado para um tipo diferente. Quando um valor é passado como um argumento para uma função ou quando o um

determinado tipo é retornado de uma função.

Um caractere, um inteiro short, ou um campo de bits inteiros, todos estes com ou sem sinal, ou um objeto do tipo enumeração, podem ser usados em qualquer lugar que um inteiro possa ser utilizado. Se um int pode representar todos os valores do tipo original, então o valor será convertido para int, caso contrário será convertido para unsigned int 37. As promoções para inteiros preservam o valor. Isto é, o valor depois da promoção é garantido ser o mesmo que antes da promoção 38.

99..66..11.. CCoonnvveerrssõõeess ddee AAttrriibbuuiiççããoo Nas operações de atribuição o tipo do valor sendo atribuído é convertido para o

tipo da variável que receberá a atribuição. A linguagem C permite conversões por atribuição entre tipos inteiros e ponto flutuante, mesmo que seja perdida informação na conversão. O método de conversão usado dependerá dos tipos envolvidos na atribuição podendo ser os seguintes : Conversões de tipos inteiros com sinal. Conversões de tipos inteiros sem sinal. Conversões de tipos ponto flutuante. Conversões de tipos de ponteiros Conversões de outros tipos.

Os qualificadores de tipo não afetam as conversões, exceto que um const l-value não pode ser utilizado no lado esquerdo da atribuição.

CCoonnvveerrssõõeess ddee ttiippooss IInntteeiirrooss ccoomm SSiinnaall

Quando um inteiro com sinal é convertido para um inteiro sem sinal com tamanho igual ou maior, e o valor do inteiro com sinal é positivo, o valor permanece inalterado. A conversão é feita pela extensão do sinal do inteiro com sinal. Um inteiro com sinal é convertido num inteiro com sinal de menor tamanho pelo truncamento dos bits de maior ordem. O resultado é interpretado como um valor sem sinal como mostra o seguinte exemplo: int i = -3; unsigned short u; u = i; printf( "%hu\n", u ); /* Imprime 65533 */

Nenhuma informação é perdida quando um inteiro com sinal é convertido para um valor de ponto flutuante, exceto que alguma precisão seja perdida quando forem utilizados inteiros longos, do mesmo tamanho do ponto flutuante em questão.

A Tabela 9-19 resume as conversões efetuadas em tipos inteiros com sinal. Esta tabela assume que o tipo char é com sinal por default 39.

De Para Método char short 40 Extensão de sinal

37 Isto é também conhecido como “integral promotion” ou promoção para int. 38 Ver Conversões Aritméticas Usuais na seção 9.5.2. 39 Isto não é verdade para todos os compiladores, as vezes é uma opção de ambiente e compilação.

Page 186: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

164164164164

9999

char long 41 Extensão de sinal char unsigned char Preserva o padrão; o bit mais significativo perde a sua função

como dígito de sinal. char unsigned short Extensão de sinal para short; converte short em unsigned short. char unsigned long Extensão de sinal para long; converte long para unsigned long. char float Extensão de sinal para long; converte long para float. char double 42 Extensão de sinal para long; converte long para double. char long double Extensão de sinal para long; converte long para double. short char Preserva o byte de menor ordem. short long Extensão de sinal short unsigned char Preserva o byte de menor ordem. short unsigned short Preserva o padrão de bits; o bit mais significativo perde a função

de bit de sinal. short unsigned long Extensão de sinal para long; converte long em unsigned long. short float Extensão de sinal para long; converte long para float. short double Extensão de sinal para long; converte long para double. short long double Extensão de sinal para long; converte long em double. long char Preserva o byte de menor ordem. long short Preserva a word de menor ordem. long unsigned char Preserva o byte de menor ordem. long unsigned short Preserva a word de menor ordem. long unsigned long Preserva o padrão de bits; o bit mais significativo perde a função

de bit de sinal. long float Representado como float. Se o valor long não pode ser

representado exatamente, alguma precisão será perdida. long double Representado como double. Se o valor long não pode ser

representado exatamente como um double, alguma precisão será perdida.

long long double Representado como double. Se o valor long não pode ser representado exatamente como um double, alguma precisão será perdida.

Tabela 9-19 - Conversões de Tipos inteiros com sinal

CCoonnvveerrssõõeess ddee TTiippooss IInntteeiirrooss sseemm SSiinnaall

Um inteiro sem sinal é convertido para um inteiro de tamanho menor com ou sem sinal pelo truncamento dos bits de maior ordem. Um inteiro sem sinal é convertido em um inteiro de tamanho maior com ou sem sinal pela extensão de zeros 43.

Quando o valor do tipo integral é degradado a um inteiro com sinal de menor tamanho, ou um inteiro sem sinal é convertido no seu correspondente inteiro com sinal, o valor fica inalterado somente se pode ser representado no novo tipo. Porém, o valor que este representa muda se o bit de sinal estiver setado como mostra o seguinte exemplo 44: int j; unsigned short k = 65533; j = k; printf( "%hd\n", j ); /* Imprime -3 */

Os valores sem sinal são convertidos de forma a preservar o seu valor. A única exceção é a conversão de um unsigned long para float, que perde os bits de menor ordem. Nos outros casos, o valor é preservado sendo signed ou unsigned. Quando um valor de um tipo inteiro é convertido em ponto flutuante, e o valor está fora da faixa representável, o resultado será indefinido. 40 O tamanho em bytes do tipo short depende da implementação do compilador. O tipo int poderá ser deste tipo. 41 O tamanho em bytes do tipo long int depende da implementação do compilador. O tipo int poderá ser deste tipo. 42 Alguns compiladores para microcontroladores tratam o double como float. 43 Preenchimento com zeros do bit mais significativos no inteiro estendido. 44 Caso o valor não possa ser representado, o resultado dependerá da implementação do compilador. Ver Conversões de Tipo Cast na seção 9.6, para maiores informações. O mesmo comportamento resulta nas conversões entre inteiros usando cast.

Page 187: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

165165165165

9999

A Tabela 9-20 resume as conversões dos tipos inteiros sem sinal. De Para Método unsigned char char Preserva o padrão de bits; o bit mais significativo

será o bit de sinal. unsigned char short 45 Extensão com zeros. unsigned char long Extensão com zeros. unsigned char unsigned short Extensão com zeros. unsigned char unsigned long Extensão com zeros. unsigned char float Conversão para long; conversão de long em float. unsigned char double 46 Conversão para long; conversão de long em double. unsigned char long double Conversão para long; conversão de long em double. unsigned short char Preserva o byte de menor ordem. unsigned short short Preserva o padrão de bits; o bit mais significativo

será o bit de sinal. unsigned short long Extensão com zeros. unsigned short unsigned char Preserva o byte de menor ordem. unsigned short unsigned long Extensão com zeros. unsigned short float Conversão para long; conversão de long em float. unsigned short double Conversão para long; conversão de long em double. unsigned short long double Conversão para long; conversão de long em double. unsigned long char Preserva o byte de menor ordem. unsigned long short Preserva a word de menor ordem. unsigned long long Preserva o padrão de bits; o bit mais significativo

será o bit de sinal. unsigned long unsigned char Preserva o byte de menor ordem. unsigned long unsigned short Preserva a word de menor ordem. unsigned long float Conversão para long; conversão de long em float. unsigned long double Converte diretamente para double unsigned long long double Conversão para long; conversão de long em double.

Tabela 9-20 - Conversões de Tipos Inteiros sem Sinal

CCoonnvveerrssããoo ddee TTiippooss PPoonnttoo FFlluuttuuaannttee

Um valor float convertido em double ou long double, ou um double convertido em um long double, não sofrerá mudanças no valor. Um valor double, convertido para um valor float é representado o mais exatamente possível. Poderá ser perdida certa precisão se o valor não possa ser representado exatamente. Se o resultado estiver fora da faixa, o comportamento será indefinido.

Um valor de ponto flutuante é convertido num valor inteiro primeiro pela conversão para long, então a partir deste valor para o tipo específico como descrito nas seções anteriores. A porção decimal do valor de ponto flutuante é descartada na conversão para long Se o resultado for muito grande para caber num long, o resultado da conversão será indefinido.

A Tabela 9-21 resume as conversões de tipos ponto flutuante. De Para Método float char Converte para long; converte long em char. float short 47 Converte para long; converte long em short. float long Trunca o ponto decimal. Se o resultado for muito grande para ser

representado como long, o resultado é indefinido. float unsigned Converte para long; converte long em unsigned short.

45 O tamanho em bytes dos tipos short e long dependem da implementação do compilador. O tipo int poderá ser de qualquer um destes tipos. 46 Alguns compiladores para microcontroladores tratam o double como float. 47 O tamanho em bytes dos tipos short e long dependem da implementação do compilador. O tipo int poderá ser de qualquer um destes tipos.

Page 188: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

166166166166

9999

short float unsigned

long Converte para long; converte long em unsigned long.

float double 48 Muda a codificação interna. float long

double Muda a codificação interna.

double char Converte para float; converte float em char. double short Converte para float; converte float em short. double long Trunca o ponto decimal. Se o resultado for muito grande para ser

representado como long, o resultado é indefinido. double unsigned

short Converte para long; converte long em unsigned short.

double unsigned long

Converte para long; converte long em unsigned long.

double float Representa como um float. Se o valor double não pode ser representado exatamente como um float, ocorrerá perda de precisão. Se o valor for muito grande para ser representado por um float, então o resultado será indefinido.

long double char Converte para float; converte float em char. long double short Converte para float; converte float em short. long double long Trunca o ponto decimal. Se o resultado for muito grande para ser

representado como long, o resultado é indefinido. long double unsigned

short Converte para long; converte long em unsigned short.

long double unsigned long

Converte para long; converte long em unsigned long.

long double float Representa como um float. Se o valor double não pode ser representado exatamente como um float, ocorrerá perda de precisão. Se o valor for muito grande para ser representado por um float, então o resultado será indefinido.

long double double O valor long double é tratado como double.

Tabela 9-21 - Conversão de Tipos Ponto Flutuante

As conversões de valores float, double, ou long double para unsigned long não serão exatas se o valor sendo convertido for maior que o máximo valor positivo do tipo long.

CCoonnvveerrssõõeess ddee TTiippoo PPoonntteeiirrooss4499

Um ponteiro para um tipo de valor pode ser convertido em um ponteiro para um tipo diferente. Porém o resultado pode ser indefinido devido aos requerimentos de alinhamento e tamanho dos diferentes tipos no armazenamento. Um ponteiro para um objeto pode ser convertido em um ponteiro para um objeto cujo tipo requeira um espaço de armazenamento compatível.

Um ponteiro para void pode ser convertido em um ponteiro de qualquer tipo, sem restrições nem perda de informação. Assim também um ponteiro de qualquer tipo pode ser convertido num tipo void. Se o resultado for convertido novamente ao tipo original, o ponteiro original será recuperado.

Se um ponteiro é convertido em outro do mesmo tipo mas tendo qualificadores adicionais ou diferentes, o novo ponteiro será o mesmo que o anterior exceto pelas restrições impostas pelo novo qualificador.

O valor de um ponteiro pode também ser convertido num valor inteiro. A forma de conversão depende do tamanho do ponteiro e do tamanho do tipo inteiro de acordo com as seguintes regras: 48 Alguns compiladores para microcontroladores tratam o double como float. 49 Os ponteiros serão tratados com mais detalhes no capítulo 12.

Page 189: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

167167167167

9999

Se o tamanho do ponteiro for maior ou igual que o tamanho do tipo integral, o ponteiro se comporta como um valor sem sinal na conversão, exceto que este não poderá ser convertido num valor de ponto flutuante. Caso o ponteiro for menor que o tipo inteiro, o mesmo será convertido inicialmente num

ponteiro com o tamanho do tipo inteiro, e então convertido ao tipo inteiro em questão.

De forma análoga, um tipo inteiro pode ser convertido em um tipo ponteiro de acordo com as seguintes regras: Se o tipo inteiro tem o mesmo tamanho que o tipo ponteiro, a conversão simplesmente faz

com que o valor seja tratado como um ponteiro (inteiro sem sinal). Se o tamanho do tipo inteiro for diferente do tamanho do tipo ponteiro, o tipo inteiro será

primeiro convertido para o tamanho do ponteiro usando as regras de conversão vistas anteriormente em Conversão de Tipos Inteiros.

Uma expressão constante com valor 0 ou uma expressão casteada para o tipo void * pode ser convertida pelo cast, por atribuição, ou por comparação com um ponteiro de qualquer tipo. Isto produz um ponteiro null que é igual a qualquer ponteiro null do mesmo tipo, mas este ponteiro null não é igual a qualquer outro ponteiro para uma função ou para um objeto. Valores inteiros diferentes da constante 0 podem ser convertidos em tipo ponteiro, mas o resultado não será portável.

99..66..22.. CCoonnvveerrssõõeess ddee OOuuttrrooss TTiippooss Uma vez que um valor enum50 é um valor inteiro por definição, as conversões de

valores de e para este tipo, são as mesmas que aquelas para o tipo int.

99..77.. CCoonnvveerrssõõeess eemm CChhaammaaddaass ddee FFuunnççããoo

O tipo de conversões executadas nos argumentos de uma chamada de função depende da presença do protótipo da função (declaração da função) com a declaração dos tipos de argumentos para a função correspondente.

Se o protótipo da função estiver presente incluindo a declaração dos tipos de argumentos, o compilador conferirá os tipos.

Caso o protótipo da função não estiver presente, somente serão efetuadas as conversões aritméticas usuais nos argumentos na chamada da função. Isto permite que um valor float seja convertido em double, um valor char ou short seja convertido em um int, e um valor unsigned char ou unsigned short seja convertido num unsigned int.

EEXXEERRCCÍÍCCIIOOSS

1. Avalie o resultado das variáveis x, y e z depois da seguinte seqüência de operações: int x,y,z; x=y=10; z=++x; x=-x; y++; x=x+y-(z--);

2. Avalie o resultado das seguintes expressões: 50 O tipo enum é tratado com mais detalhes na seção 16.3.

Page 190: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

168168168168

9999

((10>5)||(5>10)) (!(5==6)&&(5!=6)&&((2>1)||(5<=4)))

3. Compile o exemplo acima sem usar o modelador, e verifique os resultados. Compile-o novamente usando o modelador e compare a saída com os resultados anteriores.

4. Implemente um programa em C que imprima um valor inteiro fornecido através de um dispositivo de entrada (ex. teclado), e apresente a sua representação em binário num dispositivo de saída (ex. monitor de vídeo).

5. Implemente um programa em C que peça um número inteiro do teclado, e que efetue a rotação à direita de n bits (também fornecido via teclado), ou à esquerda, caso o valor de n inserido via teclado for negativo.

6. Implemente uma função que efetue a inversão de posição de todos os bits de uma variável de 8 bits, como se fosse olhada através de um espelho. Por exemplo, o valor 0101 1100 deverá ficar após a conversão como 0011 1010. Este tipo de função é bastante usado em processamento digital de sinais (DSP) sendo chamada de bit reversion.

AAvvaalliiaaççããoo

Responda as perguntas abaixo, escolhendo a alternativa adequada para cada questão.

1. Em um compilador para um hardware de 16 bits uma variável double ocupa o mesmo espaço que _____ variáveis do tipo char

a. Uma b. Duas c. Quatro d. Oito e. Dezesseis

2. O trecho de programa a seguir é

main() char condicao; condicao = 'D'; int i = 1; a. Válido na linguagem C b. Não válido na linguagem C

3. Escolha a opção que inclui somente nomes válidos para variáveis na linguagem C.

a. If, a_b_2, H789, _yes b. i, j, int, obs c. 9xy, a36, x*y, --j d. 2_ou_1, \fim, *h, j e. Nenhuma das opções anteriores

4. 0101 é uma constante __________ na linguagem C.

a. Binária b. Hexadecimal c. Inteira d. Octal e. Ponto Flutuante

5. Qual das instruções abaixo está errada?

a. int i; b. long float x; c. long double y; d. long ijk;

Page 191: Tratado Da Linguagem c

O P E R A D O R E S E E X P R E S S Õ E S

169169169169

9999

e. short int a; f. unsigned b;

6. Qual o trecho de programa que inicializa a variável z?

a. float z , z1=10.; b. float z; z = 10; c. float z = 10; d. z = 10;

7. Em C, "t" e 't' representam a mesma constante.

a. Verdadeiro b. Falso

8. Calcule o resultado das variáveis x, y e z depois da seguinte seqüência de operações:

int x,y,z; x=y=10; z=++x; x=-x; y++; x=x+y-(z--); a. x = 11, y = 11, z = 11 b. x = -11, y = 11, z = 10 c. x = -10, y = 11, z = 10 d. x = -10, y = 10, z = 10 e. Nenhuma das opções anteriores

9. A operação lógica (-5 || 0)&&(3 >= 2)&&(1 != 0)||(3 < 0) é:

a. Verdadeira b. Falsa c. Inválida, pois sua sintaxe está errada. d. Nem Verdadeira nem Falsa e. Nenhuma das opções anteriores

10. Calcule o resultado das variáveis x, y e z depois da seguinte seqüência de operações:

int x,y; int a = 14, b = 3; float z; x = a/b; y = a%b; z = y/x; a. x = 4.66666, y = 2, z = 0.4286 b. x = 5, y =2, z= 0.4 c. x = 5, y = 2, z = 0. d. x = 4, y = 2, z = 0.5 e. x = 4, y =2, z = 0. f. Nenhuma das opções anteriores

11. Qual o valor das variáveis v, x, y e z após a execução do seguinte trecho de código

int v = 0, x = 1, y = 2, z = 3; v += x+y; x *= y = z + 1; z %= v + v + v; v += x += y += 2;

a. v=11, x=8, y=6, z=3 b. v=0, x=1, y=2, z=3 c. v=10, x=7, y=6, z=3 d. v=13, x=10, y=6, z=3 e. Nenhuma das opções anteriores

12. Quais os valores de a, b e c após a execução do código abaixo?

int a = 10, b = 20, c; c = a+++b; a. a = 11, b = 20, c =30 b. a = 10 , b = 21, c = 31 c. a = 11, b = 20, c = 31 d. a = 10, b = 21, c = 30 e. Nenhuma das opções anteriores

Page 192: Tratado Da Linguagem c

170170170170

1100.. CCOONNTTRROOLLEE DDEE FFLLUUXXOO DDEE EEXXEECCUUÇÇÃÃOO –– IINNSSTTRRUUÇÇÕÕEESS GGEERRAAIISS

As estruturas de controle de fluxo são fundamentais para qualquer linguagem de programação. Sem elas só haveria uma maneira do programa ser executado: do início até o fim, comando por comando. Não haveria condições de execução, repetições ou saltos. A linguagem C possui diversos comandos de controle de fluxo que permitem tomadas de decisão para o seqüenciamento das instruções, e cálculos iterativos.

1100..11.. IInnssttrruuççõõeess GGeerraaiiss

A linguagem C é composta de um conjunto de Instruções Gerais ou comandos1. As instruções gerais de um programa em C controlam o fluxo da execução do programa. Na linguagem C, como em outras linguagens de programação, estão disponíveis vários tipos de instruções para a execução de laços, seleção de outras instruções a serem executadas, e para transferências de controle.

As instruções consistem de tokens, expressões e outros elementos. Uma instrução que compõe uma outra instrução é chamada de “corpo”. As instruções podem ser agrupadas como:

expressões instruções de seleção instruções de iteração instruções compostas instruções de salto

Freqüentemente as instruções do “corpo” são instruções compostas. Uma instrução composta consiste de outras instruções que podem incluir palavras-chave. As instruções compostas são delimitadas por chaves (). Todas as outras instruções da linguagem terminam com ponto e vírgula (;). O ponto e vírgula é o indicador de fim de instrução.

As expressões contêm expressões em C que podem conter os operadores lógicos ou aritméticos vistos no capítulo anterior. A instrução null é uma instrução vazia.

Qualquer instrução em C pode começar com um label identificador consistindo de um nome e o caractere dois pontos (:). 1 Chamados em inglês de statements.

Capítulo

10

Page 193: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

171171171171

10101010

1100..22.. EExxpprreessssõõeess

Quando uma expressão é executada a mesma é avaliada de acordo com as regras vistas no capítulo anterior. Todos os efeitos colaterais da avaliação serão completados antes da execução da próxima instrução. Qualquer instrução vazia é chamada de instrução null.

Os seguintes exemplos demonstram algumas expressões possíveis. x = ( y + 3 ); /* x é atribuído com o valor de y + 3 */ x++; /* x é incrementado */ x = y = 0; /* x e y são zerados */ proc(arg1, arg2); /* Chamada de função que retorna void */ y = z = ( f( x ) + 3 ); /* Expressão de chamada de função */

Na última instrução, na expressão de chamada de função, o valor da expressão, que inclui qualquer valor retornado pela mesma, é somado a 3 e então atribuído às variáveis y e z.

1100..33.. AA IInnssttrruuççããoo ““iiff””

A instrução if controla o desvio condicional. O corpo de uma instrução if é executado se o valor da expressão for diferente de zero. A sintaxe para esta instrução possui duas formas:

if ( expressão ) instrução if (expressão) instrução else instrução

Em ambas as formas da instrução, a expressão, que pode ter qualquer valor exceto uma estrutura, será avaliada incluindo todos os seus efeitos secundários.

Na primeira forma da sintaxe, se a expressão for verdadeira (diferente de zero), a instrução (ou bloco de instruções) será executada. Se a expressão for falsa, a instrução será ignorada. Na segunda forma da sintaxe, a que utiliza o comando else, a segunda instrução é executada caso a expressão seja falsa. Com ambas formas, o controle então passa de uma instrução do if para a instrução seguinte a menos que uma das instruções contenha um comando break, continue ou goto, que serão vistos mais adiante neste capítulo.

A seguir um exemplo do uso da instrução if: if ( i > 0 ) y = x / i; else x = i;

y = f( x );

No exemplo, a instrução y = x/i, é executada se i é maior que 0. Se i for menor ou igual que 0, então i será atribuído a x e f(x) é atribuído a y 2.

As instruções if e else podem ser aninhadas. Podem ser usadas chaves () para denotar instruções compostas. Se não forem usadas as chaves, os compiladores resolvem as ambigüidades pela associação de cada else com o if mais próximo. if ( i > 0 ) /* Sem chaves */ if ( j > i ) x = j;

2 Notar que as instruções que formam o bloco da instrução if finalizam com ponto e vírgula.

Page 194: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

111172727272

10101010

else x = i;

A instrução else neste caso é associada com a instrução if mais interna que tenha falta de um else. Se i for menor ou igual a 0, nenhum valor será atribuído a x. if ( i > 0 ) /* Com chaves */ if ( j > i ) x = j; else x = i;

No exemplo as chaves encerram a instrução if interna, fazendo que a instrução else ser parte da instrução if mais externa. Caso i seja menor ou igual a 0, i será atribuído a x.

Observar o exemplo a seguir. Observe que a variável resistor deve ser um inteiro de 32 bits para poder ser comparada corretamente com o valor 1000000. #include <stdio.h> void main () int resistor; /* inteiro de 32 bits */ printf ("Digite o valor do resistor: "); scanf ("%d",&resistor); if (resistor <= 0) printf ("\n\nNão é um valor válido"); printf(“\nEste programa avalia valores entre 1 e 1000000 ohms”); if (resistor > 1000000) printf ("\n\nValor muito elevado para este programa\n"); printf ("Inserir um valor menor."); if ( (resistor > 0) && (resistor < 1000000) ) printf ("\n\nValor adequado para o proseguimento do programa");

Código 10-1

1100..33..11.. AA iinnssttrruuççããoo eellssee O comando else é um complemento do comando if. Este comando delimita um

bloco que deve ser executado caso a condição da instrução if não seja verdadeira. O comando composto if-else tem a seguinte forma geral:

if(expressão_condicional) ... else ...

É importante notar que, quando é usado o comando composto if-else, garante-se que uma das duas declarações será executada. Nunca serão executadas ambas as declarações ou nenhuma delas. O comando else não pode aparecer sem o comando if, mas o contrário é válido. A seguir é mostrado o programa anterior modificado para utilizar o comando composto if-else. #include <stdio.h> void main () int resistor; /* inteiro de 32 bits */ printf ("Digite o valor do resistor: "); scanf ("%d",&resistor); if ( (resistor > 0) && (resistor < 1000000) ) printf ("\n\nValor adequado para o proseguimento do programa");

Page 195: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

173173173173

10101010

else printf ("\n\nNão é um valor válido"); printf(“\nEste programa avalia valores entre 1 e 1000000 ohms”);

Código 10-2

1100..33..22.. OO iiff--eellssee--iiff O comando composto numa conjunção if-else-if é apenas uma extensão da

estrutura if-else. Sua forma geral pode ser escrita como sendo:

if (expressão_1) instrução_1; else if (expressão _2) instrução _2; else if (expressão _3) instrução _3; . . else if (expressão _n) instrução _n; else instrução_default;

A composição funciona da seguinte maneira: o programa começa a testar as condições começando pela primeira e continua a testar até encontrar uma condição cujo resultado for diferente de zero. Neste caso será executada a declaração correspondente. De todas as declarações, somente uma será executada. A última declaração (default) será executada no caso de nenhuma das condições tenha sido satisfeita. Observar um exemplo do uso da composição: #include <stdio.h> void main () int resistor; /* inteiro de 32 bits */ printf ("Digite o valor do resistor: "); scanf ("%d",&resistor); if (resistor <= 0) printf ("\n\nNão é um valor válido"); printf(“\nEste programa avalia valores entre 1 e 1000000 ohms”); else if (resistor > 1000000) printf ("\n\nValor muito elevado para este programa\n"); printf ("Inserir um valor menor."); else if((resistor > 0) && (resistor < 1000000)) printf ("\n\nValor adequado para o proseguimento do programa");

Código 10-3

1100..33..33.. iiffss AAnniinnhhaaddooss O if aninhado é simplesmente um comando if dentro da declaração de um outro

comando if externo. Deve-se ter o cuidado de saber exatamente a qual if um determinado else pertence. Observar o seguinte exemplo: #include <stdio.h> void main () int resistor; /* inteiro de 32 bits */ printf ("Digite o valor do resistor: "); scanf ("%d",&resistor); if (resistor <= 0) printf ("\n\nNão é um valor válido"); printf(“\nEste programa avalia valores entre 1 e 1000000 ohms”); else

Page 196: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

174174174174

10101010

if (resistor > 1000000) printf ("\n\nValor muito elevado para este programa\n"); printf ("Inserir um valor menor."); else printf ("\n\nValor adequado para o prosseguimento do programa");

Código 10-4

1100..44.. OO OOppeerraaddoorr ““?? ::””

Como foi visto na seção 9.5.11 o operador ‘ ? ’ pode ser usado como instrumento de tomada de decisão.

Uma expressão tal como: if (a>0) b=-150; else b=150;

pode ser simplificada usando-se o operador ? da seguinte forma: b = a > 0 ? –150 : 150;

Esta expressão avalia o operador (ou simplesmente a variável) do lado esquerdo do operador ‘?’ como se fosse uma pergunta (a>0 ?). Se o resultado for verdadeiro então executará a operação b=-150, o caso contrário, por meio do operador ‘:’ executará b=150.

De uma maneira geral as expressões do tipo:

if (condição) expressão_1; else expressão_2;

podem ser abreviadas por:

condição ? expressão_1 : expressão_2;

O operador ‘?’ é limitado (não atende a uma gama muito grande de casos) mas pode ser usado para simplificar expressões complicadas. Uma aplicação interessante é a de um contador circular. Observar o exemplo: #include <stdio.h> void main() int index = 0, contador; char codigo[] = "Erro de Timeout "; for (contador=0; contador < 100; contador++) printf("\n%c",codigo[index]); index = (index==14) ? index=0: ++index;

Código 10-5

A string “Erro de Timeout” será enviada para o dispositivo de saída caractere por caractere, seguido de um caractere de LF (e mais um CR se for um compilador para PC) até a variável contador determinar o término do programa. Enquanto isto a variável index assumirá valores entre 0 e 14, de forma progressiva, voltando ao 0 novamente.

Page 197: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

175175175175

10101010

1100..55.. AAss IInnssttrruuççõõeess CCoommppoossttaass

Uma instrução composta 3 tipicamente aparece como sendo o corpo de uma outra instrução, tal como a instrução if 4.

A sintaxe é a seguinte:

instrução-composta lista-de-declarações lista-de-instruções

Se existirem as declarações, estas devem ficar antes das instruções. O escopo de cada identificador declarado no inicio de uma instrução composta, se estende a partir do seu ponto de declaração até o final do bloco. Este será visível em todo o bloco a não ser que exista uma declaração do mesmo identificador num bloco interno.

Os identificadores de uma instrução composta são presumidos auto, a não ser que sejam explicitamente declarados como o qualificadores register, static ou extern, excetuando as funções que somente podem ser extern. Em declarações de função não é necessário colocar o especificador extern que a função ainda será extern.

As declarações numa instrução composta não alocam espaço de memória nem permitem a inicialização caso a variável ou função sejam declaradas com a classe de armazenamento extern. As declarações neste caso se referenciam a variáveis ou funções definidas em outro lugar.

As variáveis declaradas em um bloco com as keywords auto ou register são realocadas e, se necessário, inicializadas cada vez que a instrução composta estiver ativa. Estas variáveis não permanecem definidas depois da saída do controle da instrução composta. Se a variável é declarada dentro do bloco com a classe static, será inicializada quando a execução do programa começa e o seu valor será mantido através de todo o programa 5.

O exemplo a seguir ilustra o uso de uma instrução composta: void main() int line[3]; int i = 1; if ( i > 0 ) int x; /* Declaração de variável dentro do bloco */ line[i] = x; x++; i--; /* x++; /* A variável x não é visível fora do bloco onde for declarada */ */

Código 10-6

3 Também conhecida como bloco ou bloco de instruções. 4 O capítulo 8 descreve as formas permitidas de declarações que podem aparecer no cabeçalho de uma instrução composta. 5 Ver Classes de Armazenamento para maiores informações sobre o atributo static.

Page 198: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

176176176176

10101010

No exemplo, se i for maior que 0, todas as instruções dentro da instrução composta serão executados na ordem.

1100..66.. AA IInnssttrruuççããoo ““sswwiittcchh””

A instrução switch trabalha junto com labels case. Estas instruções ajudam a controlar complexas operações condicionais e de desvio de fluxo. A instrução switch transfere o controle para uma instrução (ou bloco de instruções) dentro do seu corpo.

A sintaxe é a seguinte:

switch ( expressão ) case expressão-constante : instrução1 case : instrução2 default 6 : instrução N

O controle passa para a instrução cuja expressão-constante do case confere com o valor da expressão entre os parênteses do switch. a instrução switch pode incluir qualquer número de instancias case, mas não podem existir duas expressões constantes com o mesmo valor. A execução da instrução do corpo começa no label case selecionado e procede até o final do corpo ou até que uma instrução break transfere o controle para fora do corpo.

O uso da instrução switch usualmente aparece da seguinte maneira:

switch ( expressão ) declarações . . . case expressão-constante : instruções executadas se a expressão for igual ao valor à esta expressão-constante . . . break; default : instruções executadas se a expressão não é igual a nenhuma expressão-constante de nenhum case

A instrução break pode ser utilizada para finalizar o processamento de um case em particular dentro da instrução switch e enviar o controle para o fim da instrução. Sem o uso da instrução break, o programa continuará executando o próximo case, executando as instruções até encontrar uma instrução break ou o final da instrução switch 7.

O label default será executado se nenhuma constante dos labels case for igual ao valor da expressão do switch. Se o label default for omitido e nenhum case igual à expressão do switch for encontrado, nenhuma das instruções do corpo da instrução 6 A instrução default é opcional. 7 Em algumas situações, a continuação do programa para a execução de outro case pode ser indesejável.

Page 199: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

177177177177

10101010

switch será executada. Só poderá haver um label default. Este label não precisa ficar no final do switch podendo aparecer em qualquer lugar do corpo da instrução. De fato, freqüentemente pode ser mais eficiente se o label default aparecer no inicio da instrução switch.

O tipo da expressão do switch e a expressão-constante do case devem ser do tipo inteiro. O valor de cada expressão constante nos labels case devem ser únicos dentro do corpo da instrução.

Os labels case e default da instrução switch são significativos somente nos testes iniciais que determinam onde a execução inicia no corpo da instrução. As instruções switch podem ser aninhadas. Quaisquer variáveis static serão inicializadas antes da execução em qualquer instrução switch 8.

Os seguintes exemplos ilustram o uso da instrução: switch( c ) case 'A': Maia++; case 'a': mina++; default : total++;

As três instruções do corpo do switch desse exemplo, serão executadas se c for igual a ‘A’ desde que a instrução break não aparece antes do seguinte case. O controle da execução é transferido para a primeira instrução (Maia++) e continua na seqüência pelo resto do corpo. Se c for igual a ‘a’, serão incrementadas somente as variáveis mina e total. Se c não for igual a ‘A’ nem a ‘a’ então somente será incrementada a variável total switch( i ) case -1: n++; break; case 0 : z++; break; case 1 : p++; break;

Nesse exemplo, a instrução break segue a cada instrução do corpo do switch. A instrução break força a saída do corpo da instrução depois de que uma instrução tenha sido executada. Se i for igual a –1, somente n será incrementada. A instrução break seguindo à instrução n++, ocasiona que o controle da execução ultrapasse o corpo da instrução, ignorando os elementos remanescentes. De forma análoga, se i for igual a 0, somente z será incrementada; se i for igual a 1, somente p será incrementada. A instrução break final não é estritamente necessária, desde que controle já está no final do corpo da instrução composta, mas ela pode ser incluída para ter maior consistência.

Uma única instrução pode abranger múltiplos labels case, como mostra o exemplo a seguir: case 'a' : case 'b' : case 'c' : case 'd' :

8 Poderão aparecer declarações no cabeçalho da instrução composta formando o corpo da instrução switch, mas nenhuma inicialização poderá será efetuada. A instrução switch transfere o controle diretamente para uma instrução executável dentro do corpo, pulando as linhas que contenham inicializações.

Page 200: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

178178178178

10101010

case 'e' : case 'f' : hexcvt(c);

No exemplo, se a expressão-constante for igual a qualquer letra entre ‘a’ e ‘f’, a função hexcvt será chamada 9.

O comando switch é também um comando de tomada de decisão. O uso deste comando é apropriado quando deve-se tomar uma decisão de acordo com o valor de uma variável, cujos possíveis valores já foram preestabelecidos.

Pode-se fazer uma analogia entre a instrução switch e a composição if-else-if apresentada em seções anteriores. A diferença fundamental é que a instrução switch não aceita expressões complexas, mas apenas variáveis. A instrução switch testará a variável e executará a declaração cujo case corresponda ao valor atual da mesma.

A instrução break, faz com que a execução do switch seja interrompida assim que uma das declarações seja executada. Se após a execução da declaração de um case em particular, não for colocado um comando um break, o programa continuará executando a declaração do próximo case sem importar o valor definido para este. Isto pode ser útil em certas situações, mas deve-se ter cuidado com este tipo de utilização. Observar um exemplo de utilização para o comando switch: #include <stdio.h> void main () int erro; printf ("Digite o código de erro: "); scanf ("%d" ,&erro); switch(erro) case 0: printf("\n Operação OK"); break; case 1: printf("\n Erro de Timeout"); break; case 2: printf("\n Erro de Checksum"); break; case 3:printf("\n Escrita não permitida"); break; default: printf("\n Código de erro não definido"); break;

Código 10-7

1100..77.. AA IInnssttrruuççããoo ““ffoorr””

A instrução for permite repetir uma instrução ou um bloco de instruções um especificado número de vezes. O corpo de uma instrução for é executado nenhuma ou mais vezes até que uma condição opcional seja falsa. Podem ser usadas expressões opcionais dentro da instrução for para inicializar e mudar valores durante a execução da instrução.

A sintaxe da instrução é como segue, sendo que as três expressões são opcionais:

for ( expressão-inicial ; expressão-condicional ; expressão-de-loop ) instrução

A execução de uma instrução for procede como segue:

9 Em alguns compiladores para microcontroladores, o número de labels case permitido, pode ser extremamente limitado devido aos recursos do microprocessador alvo.

Page 201: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

179179179179

10101010

5. A expressão-inicial, se existir, é avaliada. Esta, especifica a inicialização para o loop. Não há restrições no tipo da expressão.

6. A expressão condicional, se existir, será avaliada. Esta expressão deve ser do tipo aritmético ou ponteiro. Será avaliada antes de cada iteração. São possíveis três resultados: Se a expressão condicional for verdadeira (diferente de zero), a instrução será executada; logo

então a expressão de loop será avaliada depois de cada iteração. Não há restrições de tipo. Os efeitos colaterais serão executados na ordem. O processo então recomeça com a reavaliação da expressão condicional. Se a expressão condicional for omitida, a expressão condicional será considerada verdadeira, e a

execução procederá exatamente como foi descrito no parágrafo anterior. Uma instrução for sem expressão condicional termina somente quando seja executada uma instrução break ou return dentro do corpo da instrução, ou através de uma instrução goto (para um label fora do corpo da instrução for). Caso a expressão condicional seja falsa (0), a execução da instrução for termina e o controle

passa para a próxima instrução do programa.

Uma instrução for também termina quando for executada uma instrução break, goto ou return dentro do corpo da instrução. Uma instrução continue em um loop for faz com que a expressão dentro do loop seja avaliada novamente. Quando uma instrução break for executada dentro de um loop for, a expressão de loop não será avaliada nem executada.

A instrução for( ;; );

é a forma usual de produzir um loop infinito do qual o controle pode sair somente com uma instrução break, goto ou return.

O exemplo a seguir ilustra o uso da instrução for: for ( i = space = tab = 0; i < MAX; i++ ) if ( line[i] == ' ' ) space++; if ( line[i] == '\t' ) tab++; line[i] = ' ';

O exemplo conta caracteres espaços (‘ ‘) e tabulações (‘\t’) de um array de caracteres chamado line e substitui cada caractere de tabulação por um espaço. Primeiro i, space e tab são inicializadas em 0. Então i é comparada com a constante MAX; se i for menor que MAX, o corpo da instrução será executado. Dependendo do valor de line[i], o corpo de alguma ou nenhuma instrução if será executado. Então i é incrementado e testado novamente com MAX; o corpo da instrução é executado de forma repetitiva tantas vezes quando i for menor que MAX.

A instrução for é a primeira de um conjunto de três instruções, que permitem efetuar cálculos iterativos (loops de repetição). Provavelmente é o comando mais apropriado para a execução de algoritmos de cálculo numérico em operações com arrays. As outras instruções de iteração são os comandos while e do. Este conjunto compõe o segundo conjunto de comandos de controle de fluxo. Pode-se pensar neste conjunto como sendo comandos de repetição controlada de código.

A seguir se pode ver um programa que coloca os primeiros 100 números inteiros num dispositivo de saída: #include <stdio.h> void main ()

Page 202: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

180180180180

10101010

int count; for (count=0; count<100; count++) printf ("%d ",count);

Código 10-8

Notar que, no exemplo acima, é utilizado o operador incremento (++) no lugar de count = count + 1.

O comando for é bastante flexível já que se tem acesso à inicialização, à condição e ao tamanho do incremento (ou decremento).

OO lloooopp iinnffiinniittoo

Em geral todos os programas são executados na forma de loops infinitos, até que o operador decida encerrá-lo. Por exemplo, o sistema operacional DOS, fica em execução em um loop infinito (prompt) esperando a instrução do operador. O sistema operacional Windows e os seus aplicativos também operam desta forma, esperando que o usuário clique no mouse acima de um objeto previamente definido (menu ou ícone). Os programas em microcontroladores dedicados ficam efetuando as suas tarefas na forma de um loop contínuo, até o sistema ser desligado.

O loop infinito tem a seguinte forma:

for (inicialização ; ; incremento) instrução;

Uma outra forma pode ser:

for ( ; ; ) instrução;

Este tipo de loop chama-se loop infinito porque será executado para sempre (não existindo a condição, ela será sempre considerada verdadeira), a não ser que seja interrompido dentro da declaração. Para interromper um loop como este, pode ser usado o comando break. O comando break interrompe o loop infinito e o programa continuará sua execução normalmente. Além deste comando, as interrupções de hardware também podem interromper o loop de forma temporária.

Como exemplo tem-se um programa que faz a leitura de uma tecla e sua impressão na tela, até o usuário apertar uma tecla sinalizadora de final (um FLAG). O FLAG definido é a letra 'X'. #include <stdio.h> #include <conio.h> void main () char ch; for ( ; ; ) printf(“\n Digitar X para sair ”); ch = getch(); if (ch == 'X') break; printf("\n Escolha: %c",ch);

Código 10-9

OO lloooopp SSeemm CCoonntteeúúddoo

Um loop sem conteúdo é aquele no qual foi omitida a instrução. A forma geral é :

Page 203: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

181181181181

10101010

for (inicialização ; condição ; incremento);

Uma das aplicações desta estrutura é gerar tempos de espera, por exemplo em delays de microsegundos. É importante notar que enquanto o programa estiver executando a instrução for, o sistema poderá ficar travado na aplicação, mesmo em sistemas operacionais multitarefa, como o Windows® por exemplo. Observar o seguinte programa. #include <stdio.h> void main () long int i; /* inteiro de 32 bits */ printf("\a"); /* Imprime o caracter de beep */ for (i=0; i<30000000; i++); /* Espera 30.000.000 de iterações */ printf("\a"); /* Imprime outro caracter de alerta */

Código 10-10

1100..88.. AA IInnssttrruuççããoo ““bbrreeaakk””

A instrução break termina a execução da mais próxima instrução do, for, switch ou while na qual aparecer. O controle passa para a instrução seguinte à instrução finalizada.

Esta instrução é normalmente usada para terminar um processo de um case particular dentro de uma instrução switch. O uso desta instrução fora de uma instrução iterativa (do, for ou while) ou switch gerará um erro de compilação.

Dentro de instruções aninhadas, a instrução break termina somente a instrução do, for, switch ou while que a inclui. Podem ser usadas as instruções return ou goto para transferir o controle para fora da estrutura aninhada.

O exemplo a seguir mostra o uso da instrução break: for ( i = 0; i < LENGTH; i++ ) /* A execução retorna aqui quando */ /* a instrução break for executada */ for ( j = 0; j < WIDTH; j++) if ( lines[i][j] == '\0' ) lengths[i] = j; break;

O exemplo processa um array de strings de comprimento variável armazenadas em lines. A instrução break ocasiona a saída do loop for interior depois de encontrar o caractere null de terminação de string, armazenando a posição deste em lengths[i].

A variável j não é incrementada quando o break ocasiona a saída do loop interno. O controle então retorna para o loop for mais externo. A variável i é incrementada e o processo repetido até que i seja maior ou igual a LENGTH.

1100..99.. AA IInnssttrruuççããoo ““wwhhiillee””

A instrução while permite repetir uma instrução até que a expressão especificada seja falsa.

A sintaxe da instrução é a seguinte:

Page 204: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

182182182182

10101010

while ( expressão ) instrução

A expressão deve ser do tipo aritmético ou um ponteiro. A execução procede da seguinte maneira: A expressão é avaliada. Se a expressão é inicialmente falsa, o corpo da instrução while nunca será executado, e o

controle passará para a próxima instrução do programa. Se a expressão for verdadeira (diferente de zero), o corpo da instrução será executado e o

processo será repetido começando no passo 1.

A instrução while também termina quando for executada uma instrução break, goto, ou return dentro do corpo. A instrução continue termina a iteração sem sair do loop do while. A instrução continue passa o controle para a próxima iteração da instrução while.

A seguir um exemplo da instrução while: while ( i >= 0 ) string1[i] = string2[i]; i--;

O exemplo copia caracteres da string2 para a string1. Se i for maior ou igual a 0, o valor string2[i] é atribuído a string1[i] e então i é decrementado. Quando i chegar a 0 ou menor, a execução da instrução while terminará.

Pode-se notar que a instrução while testa uma condição. Se esta for verdadeira a declaração será executada e a condição será testada novamente, e assim sucessivamente até a condição ser falsa. Assim como no caso do comando for, pode-se implementar um loop infinito. Para isto basta colocar uma expressão que seja permanentemente verdadeira na condição. Pode-se também omitir a declaração e fazer um loop sem conteúdo.

Observar o exemplo do uso do while a continuação. O programa espera que o usuário digite a tecla 'q' para finalizar a execução. #include <stdio.h> void main () char ch = '\0'; while (ch != 'q') /* Enquanto ch for diferente de q */ ch = getch();

Código 10-11

1100..1100.. AA IInnssttrruuççããoo ““ddoo -- wwhhiillee””

A instrução composta do-while permite repetir a execução de uma instrução ou conjunto de instruções até que uma expressão ficar falsa.

A sintaxe da instrução é a seguinte:

do instrução while ( expressão ) ;

Page 205: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

183183183183

10101010

A expressão da instrução do-while será avaliada após de que o corpo do loop seja executado. Desta forma, o corpo do loop é sempre executado pelo menos uma vez 10.

A expressão deve ser do tipo aritmético ou ponteiro. A execução procede como seque: As instruções do corpo são executadas. A expressão é avaliada. Se a expressão for falsa, a instrução do-while termina e o controle

passa para a próxima instrução do programa. Se a expressão for verdadeira (diferente de zero), o processo se repete voltando ao passo 1.

A instrução do-while pode também terminar com a execução de uma instrução break, goto, ou return dentro do corpo.

A seguir é mostrado um exemplo de uso da instrução: do y = f( x ); x--; while ( x > 0 );

No exemplo, as duas instruções y = f( x ); e x--; serão executadas independentes do valor inicial de x. Então a expressão x > 0 será avaliada. Se x for maior que 0, o corpo da instrução será executado e a expressão x > 0 será reavaliada. O corpo da instrução é executado de forma repetida tantas vezes enquanto x seja maior que 0. A execução da instrução do-while terminará quando x for negativo ou igual a 0. O corpo do loop será executado pelo menos uma vez.

O terceiro conjunto de comandos de repetição (iteração) é o conjunto do-while. A forma geral:

Um exemplo típico de aplicação do comando do-while é em menus, nos quais deve-se garantir que o valor digitado pelo usuário seja válido, conforme apresentado abaixo: #include <stdio.h> void main () int i; do printf ("\n\n Escolha o tipo de componente: \n\n"); printf ("\t(1)...Resistor \n"); printf ("\t(2)...Capacitor \n"); printf ("\t(3)...Indutor \n \n"); scanf("%d", &i); while ((i<1)||(i>3)); switch (i) case 1: printf ("\t\t Resistor .\n"); break; case 2: printf ("\t\t Capacitor .\n"); break; case 3: printf ("\t\t Indutor. \n"); break;

Código 10-12

10 Mesmo que a instrução seja constituída de apenas um comando, é um bom estilo colocar sempre as chaves para delimitar um bloco. O ponto-e-vírgula final é obrigatório.

Page 206: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

184184184184

10101010

1100..1111.. AA IInnssttrruuççããoo ““ccoonnttiinnuuee””

A instrução continue passa o controle para a seguinte iteração das instruções do, for ou while na qual aparece, desviando de qualquer instrução remanescente no corpo da instrução composta. O uso típico desta instrução é para retornar para o início de um loop a partir de um loop mais internamente aninhado.

A seguinte iteração de uma instrução do, for ou while é determinada como segue: Dentro de uma instrução do ou while, a seguinte iteração começa pela avaliação da expressão

de controle. Uma instrução continue em uma instrução for força a que a primeira expressão seja avaliada.

Então o compilador re-avaliará a expressão condicional e, dependendo do resultado poderá terminar ou repetir a execução do corpo da instrução.

O exemplo a seguir mostra o funcionamento da instrução: while ( i-- > 0 ) x = f( i ); if ( x == 1 ) continue; y += x * x;

No exemplo, o corpo da função será executado enquanto i for maior ou igual a 0. Primeiro o valor de f(i) é atribuído a x, se x for igual a 1 então a instrução continue será executada. As demais instruções do corpo serão ignoradas, e a execução termina no reinicio do loop com avaliação da expressão de controle.

Como foi visto no exemplo anterior, a instrução continue força a continuação uma nova iteração do loop a partir da linha onde este é encontrado. Este comando só funciona dentro de um loop. Quando a instrução continue é encontrada, o loop pula para a próxima iteração, sem o abandono do mesmo, como na instrução break.

Mais um exemplo da instrução continue. #include <stdio.h> void main() int opcao; while (opcao != 5) printf("\n\n Escolha uma opção entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5)||(opcao <1)) continue; /* Opção invalida: volta ao inicio do loop */ switch (opcao) case 1: printf("\n --> Primeira opção.."); break; case 2: printf("\n --> Segunda opção.."); break; case 3: printf("\n --> Terceira opção.."); break; case 4: printf("\n --> Quarta opcao.."); break; case 5: printf("\n --> Abandonando.."); break;

Código 10-13

Page 207: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

185185185185

10101010

O programa acima ilustra uma aplicação simples para o comando continue. O programa recebe uma opção que caso seja inválida, o comando continue faz com que o fluxo seja desviado de volta ao início do loop. Caso a opção escolhida seja válida o programa prosseguirá de forma normal.

1100..1122.. AA IInnssttrruuççããoo ““ggoottoo”” ee ooss LLaabbeellss

A instrução goto transfere o controle para um label 11. Um determinado label deve residir na mesma função em que é feita a referencia ao mesmo, podendo aparecer somente uma vez dentro desta.

A sintaxe é a seguinte:

Label :

goto identificador_do_label ;

Um label é útil somente para a instrução goto; para qualquer outro contexto, o label será ignorado.

A instrução de “salto” (goto) deve residir na mesma função do label, e somente pode existir uma delas.

O grupo de nomes de identificadores que seguem uma instrução goto tem o seu próprio espaço de nomes, de forma que os nomes escolhidos não interferem com os outros identificadores. Os labels não podem ser redeclarados 12.

É aconselhável utilizar as instruções break, continue e return no lugar da instrução goto sempre que possível. Desde que a instrução break somente permite sair de um nível do loop, a instrução goto pode ser necessária para sair de um conjunto de loops aninhados.

O exemplo a seguir mostra o uso da instrução goto: void main() int i, j; for ( i = 0; i < 10; i++ ) printf( "Loop externo de execução. i = %d\n", i ); for ( j = 0; j < 3; j++ ) printf( " Loop interno de execução. j = %d\n", j ); if ( i == 5 ) goto stop; /* A instrução imediatamente abaixo não será executada */ printf( "Loop de saída. i = %d\n", i ); stop: printf( "Salto para parar. i = %d\n", i );

Código 10-14

No exemplo, a instrução goto transfere o controle para o ponto rotulado como stop: quando i for igual a 5.

11 Labels ou rótulos, são identificadores que definem uma determinada posição dentro de um programa. 12 Ver Espaços de Nomes para maiores informações.

Page 208: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

186186186186

10101010

A instrução goto pertence a uma classe diferente: a dos comandos de salto incondicional. O goto realiza um salto para um local especificado por um label (rótulo). Um rótulo, na linguagem C, é uma marca no programa.

Deve-se declarar o nome do label na posição para a qual que se dar o salto na execução, seguido de ‘:’. O comando goto pode saltar para um local indicado por um label que esteja mais à frente ou para trás no seqüenciamento de execução do programa. O label e o comando goto devem estar dentro da mesma função.

O comando goto deve ser utilizado com parcimônia, pois a sua utilização tende a tornar o código confuso. O goto não é um comando necessário, podendo sempre ser substituído por outras estruturas de controle. É extremamente não recomendada a utilização deste comando pois promove a desorganização e indisciplina na implementação do código 13.

Porém uma recomendação prática é de que ele nunca seja usado (alguns programadores profissionais dizem que ele não existe na linguagem C), existem casos isolados em aplicações com microcontroladores, onde por motivos de espaço reduzido de memória de programa ou por velocidade de execução, ele possa ser usado como um patch (remendo), para sair de um conjunto de if ou for aninhados no lugar da utilização de vários breaks. Isto pode reduzir o tamanho do código e aumentar a velocidade de execução. Em aplicações de programas para PCs e equivalentes, onde há excesso de recursos, não há motivo algum para este comando ser necessário.

O exemplo da seção anterior pode ser rescrito usando-se o comando goto: #include <stdio.h> void main() int opcao; while (opcao != 5) REINICIAR: printf("\n\n Escolha uma opcao entre 1 e 5: "); scanf("%d", &opcao); if ((opcao > 5)||(opcao <1)) goto REINICIAR; /* Opcao invalida: volta ao label REFAZ */ switch (opcao) case 1: printf("\n --> Primeira opcao.."); break; case 2: printf("\n --> Segunda opcao.."); break; case 3: printf("\n --> Terceira opcao.."); break; case 4: printf("\n --> Quarta opcao.."); break; case 5: printf("\n --> Abandonando.."); break;

Código 10-15

13 Alguns programadores mais formais gostam de pensar que este comando não existe na linguagem, mas que foi deixado sem querer não podendo ser removido posteriormente. Este comando é uma herança das linguagens assembly e outras como o BASIC. Para sistemas pequenos e não estruturados a instrução goto pode ser bem utilizada.

Page 209: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

187187187187

10101010

1100..1133.. AA IInnssttrruuççããoo NNuullll

A instrução “null” é uma instrução contendo somente ponto e vírgula (;). Ela pode aparecer em qualquer lugar onde uma instrução for esperada. Nada acontece quando é executada uma instrução null.

As instruções tais como do, for, if e while requerem que haja uma instrução executável no seu corpo. A instrução null satisfaz este requerimento nos casos em que não é necessário um corpo com instruções mais elaboradas.

Como em qualquer outra instrução em C, pode ser incluído um label antes de uma instrução null. Para rotular um item que não for uma instrução, tal como o fechamento de chaves () de uma instrução composta, deve-se rotular uma instrução null e inseri-la imediatamente antes do item para obter o mesmo efeito.

O exemplo a seguir ilustra o uso da instrução null: for ( i = 0; i < 10; line[i++] = 0 ) ;

No exemplo, a expressão do loop line[i++] = 0 inicializa os primeiros 10 elementos de line com valor igual a 0. O corpo da instrução é uma instrução null, desde que não são necessárias outras instruções.

1100..1144.. AA IInnssttrruuççããoo ““rreettuurrnn””

A instrução return termina a execução de uma função e retorna o controle para a função invocadora. A execução continua na função invocadora no ponto que segue imediatamente à chamada da função invocada. A instrução return pode retornar um valor da função invocada 14.

A sintaxe da instrução é como segue:

return expressão ;

A expressão é opcional. O valor da expressão, se existir, é retornado para a função invocadora. Se a expressão for omitida, o valor de retorno da função é indefinido. Se a expressão estiver presente, é convertida para o tipo de retorno da função. Se a função for declarada com o tipo de retorno void, a instrução return que contenha uma expressão gera uma mensagem de aviso (warning), e a expressão não será avaliada.

Se nenhuma instrução return aparecer na definição da função, o controle retornará automaticamente para a função invocadora após a execução da última instrução da função invocada. Neste caso, o valor de retorno da função invocada será indefinido. Se o valor de retorno não for requerido, a função pode ser declarada para ter tipo de retorno void; de outra forma, o tipo de retorno default (por omissão) será int 15.

O exemplo a continuação mostra a instrução return: void draw( int I, long L ); long sq( int s );

14 Ver Tipos de Retorno para maiores informações. 15 Muitos programadores usam parênteses encerrando a expressão do argumento da instrução return. Porém, a linguagem C não requer o uso destes.

Page 210: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

188188188188

10101010

int main() long y; int x; y = sq( x ); draw( x, y ); return(0); long sq( int s ) return( s * s ); void draw( int I, long L ) /* As instruções que definem a função draw serão colocadas aqui */ return;

Código 10-16

No exemplo, a função main invoca duas funções: sq e draw. A função sq retorna o valor de x * x para a função main, onde o valor de retorno é atribuído a y. A função draw é declarada como sendo uma função void e não retorna um valor. Uma tentativa de atribuir o valor de retorno de draw ocasionará uma mensagem de erro16.

Quando a execução do programa encontra uma instrução return, a função que está sendo executada será encerrada imediatamente e, caso seja informado um valor de retorno, a função retornará este valor, para a função que a chamou. É importante lembrar que o valor de retorno fornecido tem que ser compatível com o tipo de retorno declarado para a função.

Uma função pode ter mais de uma instrução return. A seguir verificar os dois exemplos de uso do return: #include <stdio.h> int quadrado (int a) return (a*a); int main () int num; printf ("Entre com um numero: "); scanf ("%d",&num); num = quadrado(num); printf ("\n\nO seu quadrado vale: %d\n",num); return (0);

Código 10-17

#include <stdio.h> int EPar (int a) if (a%2) /* Verifica se a e divisivel por dois */ return 0; /* Retorna 0 se nao for divisivel */ else return (1); /* Retorna 1 se for divisivel */ int main () int num; printf ("Entre com numero: "); scanf ("%d",&num); if (EPar(num)) printf ("\n\nO numero e par.\n"); else printf ("\n\nO numero e impar.\n"); return (0);

16 Alguns compiladores poderão gerar uma mensagem de erro se for usada a instrução return() sem argumentos, como no caso da função main do exemplo. Como foi definida sendo uma função int, o compilador pode esperar um valor explícito de retorno, no caso foi usada a instrução return(0).

Page 211: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

189189189189

10101010

Código 10-18

Nestes exemplos foram utilizados parênteses nos valores de retorno, mas isto não é necessário, somente recomendado para deixar o programa mais legível. A instrução return pode ser considerada uma função que efetua o envio de dados para a área de troca (pilha ou stack) e que encerra a execução da função, liberando a memória das variáveis auto 17.

Uma possível utilização de função seria: x = func(a,b)

Caso o valor de retorno de uma função não for utilizado, este será descartado. Por exemplo, a função printf() retorna um valor inteiro que nos exemplos anteriores não foi utilizado.

EEXXEERRCCÍÍCCIIOOSS

1. Escreva um programa, que utilizando os comandos for e switch, leia uma string (use gets()) e substitua todos os espaços e tabulações ('\t') por caracteres de nova linha ('\n'). O for deve ser encerrado quando o caractere de final da string ('\0') for encontrado.

2. Escrever um programa que receba três valores inteiros, correspondentes a dia , mês e ano. Solicitar os valores até conseguir que estes estejam na faixa correta (dias entre 1 e 31, mês entre 1 e 12 e ano entre 1900 e 2100). Verifique se o mês e o número de dias conferem (incluindo verificação de anos bissextos). Se estiver tudo correto imprima o número que aquele dia corresponde no ano. Comentar o programa. Obs. Um ano é bissexto se for divisível por 4 e não for divisível por 100, exceto para os anos divisíveis por 400, que também são bissextos. Este tipo de função é bastante utilizado em programas supervisórios usados em automação industrial e predial, para a elaboração de relatórios.

3. Implemente uma função que execute um delay de 100 microssegundos. Pesquise na documentação do compilador sendo utilizado, sobre funções de biblioteca que forneçam a informação de tempo transcorrido. Usualmente os compiladores incluem funções de tempo que são declaradas no arquivo time.h.

4. Refaça o programa a seguir, usando o while, e sem utilizar o comando do. do y = f( x ); x--; while ( x > 0 );

AAvvaalliiaaççããoo

Responder as perguntas a seguir, escolhendo a alternativa adequada para cada questão.

1. Qual o valor de x após a seguinte seqüência de instruções:

a = 10; b = 20; x = 0; x = (b > a) ? b : a;

17 É importante notar que, já que as funções retornam valores, podem ser aproveitadas para efetuar operações de atribuição, ou combiná-las em expressões mais complexas.

Page 212: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

190190190190

10101010

a. 0 b. 2 c. 10 d. 20 e. 40 f. Nenhuma das opções anteriores

2. Qual o valor de x após a seqüência de comandos:

a = 1; b = 2; c = 3; x = 0; x = a < b ? a < c ? a : c : b < c ? b : c;

3. if(num) ...; é equivalente a if(num!=0) ...;

a. Verdadeiro b. Falso

(DICA: antes de tentar resolver, colocar parênteses na expressão acima, indicando a ordem de precedência dos operadores) a. 0 b. 1 c. 2 d. 3 e. Nenhuma das opções anteriores

4. Os trechos de programa a seguir são equivalentes entre si, sob o ponto de vista do que é impresso:

for (i = 0 ; i < 10; i++) printf("%d", i);

e for (i = 0 ; i < 10; ++i) printf("%d", i); a. Verdadeiro b. Falso

5. Sendo num uma variável inteira, o que imprime o trecho de código a seguir?

num = 1; switch(num) case 1: printf("O numero e 1 "); case 2: printf("O numero e 2 "); default: printf("O numero e diferente de 1 e 2");

a. O numero e 1 b. O numero e 2 c. O numero e diferente de 1 e 2 d. O numero e 1 O numero e 2 e. O numero e 1 O numero e 2 O numero e diferente de 1 e 2

6. O trecho de programa a seguir é

switch(num) case 1; printf("O numero e 1 "); break; case 2; printf("O numero e 2 "); break; default; printf("O numero e diferente de 1 e 2"); break;

a. Válido na linguagem C b. Não válido na linguagem C

7. Qual a saída produzida pelo extrato de código a seguir:

Page 213: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

191191191191

10101010

int x; for ( x = 35 ; x > 0 ; x/=3) printf("%d " , x) ;

a. 35 11 3 1 b. 11 3 1 c. 11 3 1 0 d. 35 11 3 e. Nenhuma das opções anteriores

8. Os dois blocos de código a seguir produzem o mesmo resultado:

for( i = 0 ; i < 3 ; i++) for ( j =0 ; j < 3; j++) printf("i+j = %d \n", i+j);

e for( i = 0 , j=0 ; i < 3 ; i++) for ( ; j < 3 ; j++) printf("i+j = %d \n", i+j);

a. Verdadeiro b. Falso

9. Os extratos de código a seguir são equivalentes entre si:

int x = 10; while (--x > 9) printf("%d", x);

e int x = 10; do printf("%d", x); while(--x > 9);

a. Verdadeiro b. Falso

10. Sendo i declarado e inicializado como:

int i = 0;

os seguintes extratos de código: while (i = 5) printf("%d %d %d \n", i, i+2, i+4); i = 0;

e if (i = 5) printf ("%d %d %d \n", i, i+2, i+4);

a. São idênticos sob o ponto de vista do que imprimem na tela b. Não imprimem nada na tela c. Têm sintaxe errada d. Um deles imprime 5, 7 e 9 uma única vez e o outro entra em loop, imprimindo estes

valores indefinidamente e. Nenhuma das opções anteriores

11. O laço for a seguir

int i; for ( i = 0 ; i <= 5; i++ , printf("%d ", i));

a. Imprime 0 1 2 3 4 5 b. Não funciona, pois tem sintaxe errada c. Imprime 1 2 3 4 5 6 d. Imprime 1 2 3 4 5 e. Nenhuma das opções anteriores

12. A estrutura do switch abaixo é:

switch (t) case t < 10: printf("Hoje ta' fazendo muito frio"); break; case t < 25: printf("A temperatura está agradavel");

Page 214: Tratado Da Linguagem c

C O N T R O L E D E F L U X O D E E X E C U Ç Ã O

192192192192

10101010

break; default: printf("Hoje ta' quente pra chuchu");

a. Válida na linguagem C b. Não válida na linguagem C

Page 215: Tratado Da Linguagem c

193193193193

1111.. TTRRAABBAALLHHAANNDDOO CCOOMM AARRRRAAYYSS Neste capítulo serão vistos os seguintes tópicos:

• Arrays • Strings • Matrizes

1111..11.. AArrrraayyss

Os arrays são compostos de uma coleção de dados de um determinado tipo. Os arrays são basicamente vetores ou matrizes unidimensionais. Vetores são conjuntos de dados que são referenciados pelo mesmo nome de identificador. É importante notar que vetores e matrizes multidimensionais são caracterizadas por terem todos os seus elementos, pertencentes ao mesmo tipo de dado. Para se declarar um vetor pode-se utilizar a seguinte forma geral:

especificador_de_tipo nome_da_variável [tamanho];

Quando o compilador analisa uma declaração deste tipo gerará um código que efetuará a reserva de espaço de memória de dados, que seja suficiente para armazenar o número de elementos especificados no seu índice. Por exemplo, se for declarado o array de floats: float exemplo [20];

O código gerado tentará reservar 4 x 20 = 80 bytes de memória. Estes bytes serão reservados de maneira contígua. Não deve-se esquecer que na linguagem C, assim como na eletrônica digital, a numeração começa sempre em zero (o zero é considerado positivo). Isto significa que, no exemplo anterior, os dados serão indexados de 0 a 19. Para acessar cada elemento pode-se escrever:

exemplo[0] exemplo[1] exemplo[19]

Também será permitido escrever:

exemplo[30] exemplo[103]

O compilador C não verifica se o índice que está sendo usado, está dentro dos limites válidos. Este é um cuidado que o programador deve tomar. Se o programador não tiver atenção com os limites de validade para os índices corre-se o risco de ter variáveis sobrescritas ou de ver o computador travar; podendo ocorrer mal funcionamento e até o travamento da máquina. Observar um exemplo de utilização de arrays:

Capítulo

11

Page 216: Tratado Da Linguagem c

T R A B A L H A N D O C O M A R R A Y S

194194194194

11111111

#include <stdio.h> void main () int num[100]; /* Declara um vetor de inteiros de 100 posicoes */ int count=0; int totalnums; do printf ("\nEntre com um numero (-999 p/ terminar): "); scanf ("%d",&num[count]); count++; while (num[count-1]!=-999); totalnums = count - 1; printf ("\n\n\n\t Os números armazenados são:\n\n"); for (count=0;count < totalnums; count++) printf (" %d",num[count]);

Código 11-1

No exemplo acima, o inteiro count é inicializado em 0. O programa pede pela entrada de números até que o usuário entre com o valor -999. Os números são armazenados no vetor num. A cada número armazenado, o contador do vetor é incrementado para na próxima iteração escrever na próxima posição do vetor. Quando o usuário digita o flag (valor indicador), o programa abandona o primeiro loop e armazena o total de números gravados. No final, todos os números armazenados serão impressos.

É importante lembrar que nenhuma restrição será feita quanto a quantidade de números digitados. Se o usuário digitar mais de 100 números, o programa tentará ler normalmente, mas o programa os escreverá em uma parte não reservada de memória (para esta variável), pois o espaço alocado foi para somente 100 inteiros. Isto pode resultar nos mais variados erros durante a execução do programa.

Observar o exemplo a seguir: #include<stdio.h> void main() float temperatura[10]; temperatura[0] = 1; temperatura[1] = temperatura[0]; printf("\nValor armazenado em temperatura:%lx",(long)temperatura); printf("\nEndereco de temperatura[0]:%lx",(long)&temperatura[0]); printf("\nEndereco de temperatura[1]:%lx",(long)&temperatura[1]);

Código 11-2

Nesta declaração, a variável temperatura fornece a referência do local da memória onde serão armazenados 10 elementos de 4 bytes cada (40 bytes). Desta forma, o nome da variável fornece o endereço de memória da primeira posição do array, sendo que as outras serão subseqüentes a cada 4 bytes. Desta forma pode-se ter como resultado: Valor armazenado em temperatura:8d88ffce Endereco de temperatura[0]: 8d88ffce Endereco de temperatura[1]: 8d88ffd2

1111..22.. SSttrriinnggss

Strings são arrays de variáveis do tipo char. Deve-se ficar atento para o fato de que nas strings têm o seu último elemento definido com um caractere null ('\0' ou 0). A declaração geral para uma string é:

char nome_da_string [tamanho];

Page 217: Tratado Da Linguagem c

T R A B A L H A N D O C O M A R R A Y S

195195195195

11111111

A biblioteca padrão da linguagem C possui diversas funções que manipulam strings. Estas funções são úteis já que não é possível, por exemplo, igualar duas strings da seguinte forma: string1 = string2; /* erro lógico */

Fazer isto pode ser desastroso, já que o nome da string, identifica um endereço de memória e não o conteúdo da mesma. Para igualar strings devera-se proceder elemento a elemento, como em qualquer array.

Observar o uso de strings no programa a seguir, onde é feita a cópia de uma string em outra: #include <stdio.h> void main () int count; char str1[100] = “Termistor”, str2[100]; /* Aqui o programa le str1 que sera copiada para str2 */ for (count=0;str1[count];count++) str2[count] = str1[count]; str2[count]='\0'; printf(“\nstr2 armazena agora: %s”,str2);

Código 11-3

A condição na instrução for utiliza o fato de que a string que está sendo copiada termina em '\0' (null ou zero). Quando o elemento encontrado em str1[count] é o '\0', o valor retornado para o teste condicional é falso. Desta forma a expressão que vinha sendo verdadeira (não zero) continuamente, torna-se falsa.

A seguir serão vistas algumas funções básicas da biblioteca padrão que ajudam na manipulação de strings. Estas funções são bastante utilizadas para analisar e modificar strings recebidas, ou a serem transmitidas, em drivers de comunicação serial.

1111..22..11.. ggeettss11 A função gets() lê uma string de um dispositivo de entrada (ex. teclado). A sua

forma geral é:

gets (nome_da_string);

O programa a seguir mostra o funcionamento da função gets(): #include <stdio.h> int main () char string[100]; printf ("Entre com uma string: "); gets(string); printf ("\n\n A string inserida foi %s",string);

Código 11-4

Deve-se notar que é válido passar para a função printf() o nome da variável string (endereço da string). Também é válido escrever: printf(string);

isto simplesmente imprimirá a string. A função gets() monitora o buffer de teclado na espera de que o usuário pressione a tecla <ENTER>2, efetuando então a leitura do buffer de teclado e armazenando a string na memória. 1 gets: Definida na biblioteca stdio.h.

Page 218: Tratado Da Linguagem c

T R A B A L H A N D O C O M A R R A Y S

196196196196

11111111

1111..22..22.. ssttrrccppyy33 Esta função copia uma string em outra. Os tamanhos de ambas devem ser

compatíveis. Sua forma geral é:

strcpy (string_destino , string_origem);

A função strcpy() copia a string_origem para a string_destino. O seu funcionamento é semelhante ao da rotina apresentada na seção anterior. As funções apresentadas nas seções seguintes, estão declaradas no arquivo cabeçalho string.h. A seguir um exemplo de uso da função strcpy(): #include <stdio.h> #include <string.h> void main () char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets(str1); strcpy (str2,str1); /* Copia str1 em str2 */ strcpy (str3,"Foi inserida a string: "); /* Copia "Foi inserida a string: " na string str3 */ printf ("\n\n%s%s",str3,str2);

Código 11-5

1111..22..33.. ssttrrccaatt44 A função strcat() concatena o conteúdo de uma string em outra. Os tamanhos

deve ser compatíveis. A função tem a seguinte forma geral:

strcat (string_destino, string_origem);

A string de origem permanecerá inalterada e uma cópia desta, será anexada ao fim da string de destino. Um exemplo pode ser visto a seguir. #include <stdio.h> #include <string.h> void main () char str1[100],str2[100]; printf ("Entrar com uma string: "); gets (str1); strcpy (str2,"Foi digitada a string: "); strcat (str2,str1); /* str2 armazenara' -Foi digitada a string:- mais o conteudo armazenado referenciado por str1 */ printf ("\n\n%s",str2);

Código 11-6

1111..22..44.. ssttrrlleenn55 A função strlen retorna o tamanho de uma string (em número de caracteres). A

sua forma geral é:

strlen (string); 2 O pressionar da tecla <ENTER>, ocasiona o envio de 2 bytes, um é o CR (Carriage Return) e outro LF (Line Feed). Os códigos em ASCII equivalentes podem ser vistos na Tabela 3-7. 3 strcpy: definida na biblioteca string.h 4 strcat: definida na biblioteca string.h 5 strlen: definida na biblioteca string.h

Page 219: Tratado Da Linguagem c

T R A B A L H A N D O C O M A R R A Y S

197197197197

11111111

A função strlen() retorna o comprimento da string fornecida como parâmetro. O terminador nulo não é contado. Isto quer dizer que, de fato, o comprimento do vetor da string deve ser de uma unidade a mais que o valor de retorno. Um exemplo do seu uso: #include <stdio.h> #include <string.h> void main () int size; char str[100]; printf ("Entre com uma string: "); gets (str); size = strlen (str); printf ("\n\nA string digitada possui %d caracteres",size);

Código 11-7

1111..22..55.. ssttrrccmmpp66 A função strcmp() compara duas strings. Se ambas forem idênticas, a função

retornará zero; se forem diferentes, retornará diferente de zero. A sua forma geral é:

strcmp (string1,string2);

Um exemplo da sua utilização: #include <stdio.h> #include <string.h> void main () char str1[100],str2[100]; printf ("Entre com uma string: "); gets (str1); printf ("\n\nEntre com outra string: "); gets (str2); if (strcmp(str1,str2)) printf ("\n\nAs duas strings são diferentes."); else printf ("\n\nAs duas strings são iguais.");

Código 11-8

1111..33.. MMaattrriizzeess

As matrizes são usualmente chamadas de vetores com mais de uma dimensão ou vetores multidimensionais.

1111..33..11.. MMaattrriizzeess bbiiddiimmeennssiioonnaaiiss A forma geral da declaração de uma matriz bidimensional é muito parecida com a

declaração de um vetor:

tipo_da_variável nome_da_variável [número_de-linhas][número_de_colunas];

No preenchimento ou leitura de uma matriz na linguagem C, o índice mais à direita varia de forma mais rápida que o da esquerda. Vale a pena reiterar que na linguagem C, os índices começam com o valor zero, e que o compilador não verificará as consistências de índices. A manutenção dos índices dentro da faixa permitida, é tarefa do programador. A seguir é mostrado um exemplo de uso de matrizes.

6 strcmp: definida na biblioteca string.h

Page 220: Tratado Da Linguagem c

T R A B A L H A N D O C O M A R R A Y S

198198198198

11111111

#include <stdio.h> void main () int mtrx [20][10]; int i,j,count; count=1; for (i=0;i<20;i++) for (j=0;j<10;j++) mtrx[i][j]=count; count++;

Código 11-9

No exemplo acima, a matriz mtrx é preenchida, seqüencialmente por linhas, com os números de 1 a 200.

1111..33..22.. MMaattrriizzeess ddee ssttrriinnggss Matrizes de strings são matrizes bidimensionais. Uma string é basicamente um

vetor de variáveis tipo char. Um vetor de strings seria uma matriz bidimensional ou uma lista de vetores. Da mesma forma que as matrizes pode-se criar uma matriz de strings da forma:

char nome_da_variável [num_de_strings][compr_das_strings];

Para acessar cada string individual, simplesmente deve ser usado o primeiro índice, assim pode ser usada da forma:

nome_da_variável [índice]

A seguir um exemplo de programa que lê 5 strings e as envia para um dispositivo de saída: #include <stdio.h> void main () char strings [5][100]; int count; for (count=0;count<5;count++) printf ("\n\nDigite uma string: "); gets (strings[count]); printf ("\n\n\nAs strings digitadas foram:\n\n"); for (count=0;count<5;count++) printf ("%s\n",strings[count]);

Código 11-10

1111..33..33.. MMaattrriizzeess mmuullttiiddiimmeennssiioonnaaiiss O uso de matrizes multidimensionais na linguagem C é relativamente simples. A

sua forma geral é:

tipo_da_variável nome_da_variável [dimensão1][dimensão2] ... [dimensãoN];

Uma matriz N-dimensional funciona basicamente como outros tipos de matrizes. Deve-se lembrar que o índice que varia mais rapidamente é o índice colocado mais à direita.

Page 221: Tratado Da Linguagem c

T R A B A L H A N D O C O M A R R A Y S

199199199199

11111111

1111..33..44.. UUssoo ddee AArrrraayyss eemm SSiisstteemmaass ccoomm MMiiccrrooccoonnttrroollaaddoorreess

Em sistemas dedicados que utilizam microcontroladores, há uma limitação muito forte no que se refere a espaço de memória de dados e de programa. Considerar a seguinte instrução: printf(“Timeout Error”);

A execução desta instrução enviará uma cadeia de caracteres para um dispositivo de saída, provavelmente um canal serial. A string “Timeout Error” é uma string constante, e será armazenada na memória de programa, o que equivale dizer, diminuirá o espaço da memória de programa em 14 bytes. Caso se precisem enviar muitas mensagens constantes, deve-se preferir o uso de códigos pequenos para representá-las. Exemplo: printf(“%c”,error_code);

O dado contido na instrução, não precisará ocupará nenhum byte adicional da memória de programa nem da memória de dados, já que ela não é uma constante. A instrução enviará um caractere somente para o dispositivo de saida.

EEXXEERRCCÍÍCCIIOOSS

1. Modificar o programa a seguir, realizando a cada leitura um teste para ver se a dimensão do vetor não foi ultrapassada. Caso o usuário entre com 100 números, o programa deverá abortar o loop de leitura automaticamente. O uso do valor de indicação (-999) não deve ser retirado.

#include<stdio.h> void main() float temperatura[10]; temperatura[0] = 1; temperatura[1] = temperatura[0]; printf("\nValor armazenado em temperatura:%lx",(long)temperatura); printf("\nEndereco de temperatura[0]:%lx",(long)&temperatura[0]); printf("\nEndereco de temperatura[1]:%lx",(long)&temperatura[1]);

2. Avaliar o funcionamento do programa a seguir. #include <stdio.h> void main() int t, i, M[3][4]; for (t=0; t<3; ++t) for (i=0; i<4; ++i) M[t][i] = (t*4)+i+1; for (t=0; t<3; ++t) for (i=0; i<4; ++i) printf ("%3d ", M[t][i]); printf ("\n");

3. Faça um programa que leia quatro palavras pelo teclado, e armazene cada palavra em uma string. Depois, concatene todas as strings lidas numa única string. Por fim apresente esta como resultado ao final do programa.

Page 222: Tratado Da Linguagem c

200200200200

1122.. PPOONNTTEEIIRROOSS Neste capítulo serão abordados os seguintes tópicos:

Funcionamento dos ponteiros. Utilização dos ponteiros. Ponteiros e arrays. Inicialização e declaração Ponteiros para ponteiros.

Os ponteiros fazem da linguagem C uma ferramenta poderosa para a implementação de códigos de máquina. A linguagem C é altamente dependente dos ponteiros. O programador deve ter um bom conhecimento e domínio deles. Por isto, recomenda-se prestar especial atenção neste capítulo.

Os programas em C são constituídos basicamente de ponteiros. Estes são variáveis que ao invés de armazenar valores, armazenam referências para estes, ou seja endereços. As variáveis simples e arrays, podem ser consideradas ponteiros cujos endereços são constantes, e que indicam lugares da memória de dados. As funções também podem ser consideradas ponteiros, que armazenam um endereço da memória de programa. O uso descuidado de ponteiros pode levar a sérios bugs de programação, podendo travar a máquina.

1122..11.. FFuunncciioonnaammeennttoo

Os ints armazenam números inteiros; os floats, valores de ponto flutuante; os chars, caracteres; e os ponteiros armazenam endereços de memória (referências). Com um ponteiro pode ser feita a varredura de toda a memória do sistema microprocessado. Pode-se fazer uma analogia com os emails enviados. Todos eles deve ter um destino, ex. [email protected], que contém a informação do provedor da conta na forma de um endereço codificado. O e-mail ainda tem um conteúdo, mas este poderá ainda ser apontado (enviado) para um outro endereço.

Um ponteiro ao ser uma variável, também deve ter um determinado tipo. Continuando com a analogia, o conteúdo de um e-mail pode ser diferente ao de outro, sendo que um ponteiro pode apontar para uma mensagem de 10KB e um outro para uma de 200KB.

Na declaração de um ponteiro, deve-se declarar o tipo de dado para o qual este irá apontar. Um ponteiro para int, apontará para um endereço onde está armazenado um valor inteiro, isto é, armazenará o endereço de um inteiro. A declaração do tipo correto de ponteiro é importante na hora de efetuar a aritmética de ponteiros, o que dá uma grande versatilidade à linguagem.

Capítulo

12

Page 223: Tratado Da Linguagem c

P O N T E I R O S

201201201201

12121212

1122..22.. DDeeccllaarraaççããoo ee UUttiilliizzaaççããoo

Para declarar um ponteiro tem-se a seguinte forma geral:

tipo_do_ponteiro * nome_da_variável;

O operador (*) na declaração, indica ao compilador que a variável declarada não armazenará um valor, mas sim um endereço, para o tipo especificado. Observar alguns exemplos de declarações: int *pt; char *temp,*pt2;

A primeira linha declara um ponteiro para um inteiro. A segunda, declara dois ponteiros para caracteres. Estes ponteiros ainda não foram inicializados (como qualquer variável que é apenas declarada). Isto significa que eles apontam para um lugar indefinido. Este lugar pode estar, por exemplo, na porção da memória reservada ao sistema operacional do computador. Usar o ponteiro nestas circunstâncias pode levar ao travamento do sistema, ou a algo pior, como a modificação de variáveis externas ou até o código de máquina do sistema operacional. Os ponteiros devem ser inicializados (apontados para alguma variável) antes de serem usados.

Para atribuir um valor a um ponteiro recém criado, pode-se igualá-lo a um valor de memória apropriado. Entretanto os endereços de memória não são determinados pelo compilador e linker, mas realocados na hora da execução, pode-se inicializar um ponteiro utilizando o endereço de uma variável previamente definida, usando o operador ‘&’. Observar o exemplo a seguir. int count=10; int *pt; pt=&count;

No exemplo, criou-se uma variável inteira count inicializada com valor igual a 10, e um ponteiro para um inteiro chamado pt. A expressão &count retorna o endereço de count, o qual será armazenado em pt.

Após a última instrução, o ponteiro pt aponta para a variável count. Agora o ponteiro pode ser utilizado para modificar o valor de count, de forma indireta. Para isto pode ser usado o operador ‘*’ conteúdo. Caso for escrita a instrução: *pt = 12;

o conteúdo de count mudará para 12, já que pt modificou o valor armazenado naquele endereço.

Os ponteiros podem ser modificados para apontar para outros endereços de variáveis, de acordo com a necessidade, o que simplifica a implementação de funções genéricas, que possam operar a partir de qualquer variável do próprio programa (ou programas externos) independente do escopo.

O operador (*) pode representar até três significados diferentes, dependendo do contexto. O primeiro caso é o seu uso numa multiplicação, onde atua como operador binário. No caso de uso na declaração de ponteiros, ele é unário pré-fixado, e é interpretado como a declaração de uma variável do tipo ponteiro. O terceiro caso é quando utilizado como operador de conteúdo, que também é unário pré-fixado, e será interpretado como o conteúdo do endereço apontado pelo ponteiro.

Page 224: Tratado Da Linguagem c

P O N T E I R O S

202202202202

12121212

Alguns exemplos de uso de ponteiros podem ser verificados a seguir, para um compilador de 16 bits. #include <stdio.h> void main () int num,valor; int *p; num=55; p=&num; /* Atribui o endereco de num */ valor=*p; /* Valor e igualado a num de uma maneira indireta */ printf ("\n\n%d\n",valor); printf ("Endereco para onde o ponteiro aponta: %lx\n",p); printf ("Valor da variavel apontada: %d\n",*p);

Código 12-1

Um outro exemplo é mostrado a seguir. #include <stdio.h> void main () int num,*p; num=55; p=&num; /* Pega o endereco de num */ printf ("\nValor inicial: %d\n",num); *p=100; /* Muda o valor de num de forma indireta */ printf ("\nValor final: %d\n",num);

Código 12-2

Nos exemplos acima vemos um primeiro exemplo do funcionamento dos ponteiros. No primeiro exemplo, o código %lx usado na função printf() indica à função que ela deve imprimir um valor em hexadecimal. Caso for usado um compilador de 32 bits, utilizar simplesmente %x.

Pode-se fazer algumas operações aritméticas com ponteiros. A primeira, e mais simples, é igualar dois ponteiros. Se tivermos dois ponteiros p1 e p2, estes podem ser igualados fazendo p1=p2. Reparar que esta operação faz com que p1 aponte para o mesmo lugar que p2. Se for necessário que a variável apontada por p1 tenha o mesmo conteúdo da variável apontada por p2 pode-se fazer *p1=*p2.

As operações de incremento e o decremento também são bastante úteis, especialmente quando os ponteiros apontam para arrays. Quando um ponteiro for incrementado ele passará a apontar para o próximo valor do mesmo tipo de variável, para o qual foi definido. Isto é, se um ponteiro para um inteiro for incrementado ele passa a apontar para o próximo inteiro (num sistema de 16 bits, o novo endereço apontado será dois bytes superior ao anterior, por exemplo). Esta é a razão pela qual o compilador precisa saber o tipo de um ponteiro: caso for incrementado um ponteiro para char* ele incrementará 1 byte na memória e se for incrementado um ponteiro double* ele incrementará 8 bytes na memória. O decremento funciona de forma semelhantemente. Supondo que p é um ponteiro, as operações de incremento e decremento podem ser escritas como: p++; p--;

A situação descrita acima não é de operações com o conteúdo das variáveis para as quais eles apontam, mas sim os endereços. Por exemplo, para incrementar o conteúdo da variável apontada pelo ponteiro p, pode-se escrever: (*p)++;

Page 225: Tratado Da Linguagem c

P O N T E I R O S

203203203203

12121212

Outras operações aritméticas úteis são a soma e subtração de inteiros com ponteiros. Supor que é necessário incrementar um ponteiro de 10 bytes: p = p + 15; /* ou p += 15; */

Caso seja necessário utilizar o valor armazenado 15 posições adiante do endereço do ponteiro: *(p + 15);

A subtração funciona da mesma maneira.

Uma operação, às vezes útil, é a comparação de dois ponteiros. Com operações de comparação pode-se saber se dois ponteiros são iguais ou diferentes (== e !=). No caso de operações do tipo >, <, >= e <= pode-se comparar qual ponteiro aponta para uma posição mais alta na memória. Então uma comparação entre ponteiros pode fornecer a informação de qual dos dois está "mais adiante" na memória. A comparação entre dois ponteiros se escreve como a comparação entre outras duas variáveis quaisquer1: p1 > p2

1122..33.. PPoonntteeiirrooss ee VVeettoorreess

Quando é declarada uma matriz da seguinte forma:

tipo_da_variável nome_da_variável [tam1][tam2] ... [tamN];

o compilador C calcula o tamanho, em bytes, necessário para armazenar esta matriz. Este tamanho será igual a:

tam1 x tam2 x tam3 x ... x tamN x tamanho_do_tipo

O compilador gera um código para alocar este número de bytes em um espaço livre de memória. O nome_da_variável que for declarado é na verdade um ponteiro constante para o tipo da variável da matriz. Este conceito é fundamental. Tendo alocado na memória o espaço para a matriz, esta toma o nome da variável (que é um ponteiro) e aponta para o primeiro elemento da matriz, ou seja, armazena o endereço do primeiro elemento da matriz.

Desta forma a notação:

nome_da_variável[índice]

é equivalente a se escrever:

*(nome_da_variável+índice)

Fica claro, por exemplo, porque é que, na linguagem C, a indexação começa com zero. É porque, ao se obter o valor do primeiro elemento de um vetor, deseja-se de fato, *nome_da_variável e então devemos ter um índice igual a zero, ou um offset igual a zero. Assim pode-se dizer que *nome_da_variável é equivalente a nome_da_variável[0]

Apesar de, na maioria dos casos, não fazer muito sentido, poderiam-se ter índices negativos. Neste caso seriam indexadas posições antes do vetor. Isto explica também 1 Há entretanto operações que não podem ser efetuadas ponteiro, tais como divisão e multiplicação direta, adicionar dois ponteiros, adicionar ou subtrair ponteiros para floats ou doubles.

Page 226: Tratado Da Linguagem c

P O N T E I R O S

204204204204

12121212

porque o compilador não verifica a validade dos índices. O compilador não conhece o tamanho do vetor, apenas aloca a memória, ajusta o ponteiro do nome do vetor para o início do mesmo e, quando o programador utilizar os índices, gera um código para encontrar os elementos requisitados.

A varredura seqüencial de uma matriz é um dos usos mais comuns dos ponteiros. Quando tem-se que varrer todos os elementos de uma matriz, de forma seqüencial, pode-se usar um ponteiro, o qual vai sendo incrementado. Considere o seguinte programa para zerar uma matriz: void main () float matrx [50][50]; int i,j; for (i=0;i<50;i++) for (j=0;j<50;j++) matrx[i][j]=0.0;

Código 12-3

Pode-se escrever um programa equivalente usando ponteiros: void main () float matrx [50][50]; float *p; int count; p=matrx[0]; for (count=0;count<2500;count++) *p=0.0; p++;

Código 12-4

No primeiro programa, cada vez que se é executado matrx[i][j] o programa tem que calcular o deslocamento para a ser dado encima ao ponteiro (ainda que este não muda de valor). Ou seja, o programa tem que calcular 2500 deslocamentos. No segundo programa o único cálculo que deve ser feito é o de um incremento de ponteiro. Fazer 2500 incrementos em um ponteiro é muito mais rápido que calcular 2500 deslocamentos completos.

Há uma diferença entre o nome de um vetor e um ponteiro que deve ser frisada: um ponteiro é uma variável que aponta para qualquer endereço, mas o nome de um vetor é um ponteiro que aponta para um endereço fixo. Observar os seguintes exemplos: int vetor[10]; int *ponteiro, i; ponteiro = &i; /* as operacoes a seguir sao invalidas */ vetor = vetor + 2; /* ERRADO: vetor nao e' variavel */ vetor++; /* ERRADO: vetor nao e' variavel */ vetor = ponteiro; /* ERRADO: vetor nao e' variavel */

Tentando compilar estas linhas aparecerão mensagens de erro. Alguns compiladores indicarão que vetor não é um L-value. L-value, significa "Left value", um símbolo que pode ser colocado do lado esquerdo de uma expressão de atribuição, isto é, uma variável. Outros compiladores poderão indicar que há um "incompatible types in assignment", ou seja, tipos incompatíveis em uma atribuição. /* as operacoes abaixo sao validas */ ponteiro = vetor; /* CERTO: ponteiro e' variavel */ ponteiro = vetor+2; /* CERTO: ponteiro e' variavel */

Page 227: Tratado Da Linguagem c

P O N T E I R O S

205205205205

12121212

1122..33..11.. PPoonntteeiirrooss ccoommoo VVeettoorreess Podemos falar que o nome de um vetor é um ponteiro constante. Sabe-se também

que pode-se indexar o nome de um vetor. Como conseqüência existe a possibilidade de indexar um ponteiro qualquer. O programa mostrado a seguir funciona de forma correta. #include <stdio.h> void main () int matrx [10] = 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ; int *p; p = matrx; printf ("O terceiro elemento do vetor e: %d",p[2]);

Código 12-5

Pode-se observar que p[2] equivale a *(p+2).

1122..33..22.. SSttrriinnggss Seguindo o raciocínio desenvolvido na seção anterior, os nomes de strings, são do

tipo char* (leia-se char pointer). Isto nos permite escrever a função StrCpy(), que funcionará de forma semelhante à função strcpy() da biblioteca: #include <stdio.h> void StrCpy (char *destino,char *origem); /* declaração da função */ void main () char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets (str1); StrCpy (str2,str1); StrCpy (str3,"Foi colocada a string "); printf ("\n\n%s%s",str3,str2); void StrCpy (char *destino,char *origem) while (*origem) *destino=*origem; origem++; destino++; *destino='\0';

Código 12-6

Há vários pontos a destacar no programa acima. Observar que pode-se passar ponteiros como argumentos de funções. Na verdade é assim que funções como gets() e strcpy() funcionam. Passando o ponteiro você possibilita à função alterar o conteúdo das strings, já que pode visualizar o seu endereço na memória. Na instrução while (*origem) está-se usando o fato de que a string termina com '\0' como critério de parada. Quando se faz origem++ e destino++ não se altera o valor do ponteiro-base da string, já que este é fixo e não pode ser modificado. Desta maneira, quando se altera o ponteiro origem na função StrCpy() o ponteiro str2 permanece inalterado na função main().

1122..33..33.. EEnnddeerreeççooss ddee eelleemmeennttooss ddee vveettoorreess Nesta seção vamos apenas ressaltar que a notação

nome_da_variável[índice]

Page 228: Tratado Da Linguagem c

P O N T E I R O S

206206206206

12121212

É válida e retorna o endereço do ponto do vetor indexado por índice. Isto seria equivalente a nome_da_variável + índice. É interessante notar que, como conseqüência, o ponteiro nome_da_variável tem o endereço &nome_da_variável[0], que indica onde na memória está guardado o valor do primeiro elemento do vetor.

1122..44.. VVeettoorreess ddee ppoonntteeiirrooss

Pode-se construir vetores de ponteiros, como vetores de qualquer outro tipo. Uma declaração de um vetor de ponteiros inteiros poderia ser: int *pmatrx [10];

No caso acima, pmatrx é um vetor que armazena 10 ponteiros para inteiros.

1122..55.. PPoonntteeiirrooss ppaarraa PPoonntteeiirrooss

Um ponteiro para um ponteiro é uma variável que contém o endereço de uma outra variável, que contém um outro endereço de um valor. Pode-se declarar um ponteiro para um ponteiro com a seguinte notação:

tipo_da_variável **nome_da_variável;

Algumas considerações: **nome_da_variável é o conteúdo final da variável apontada; *nome_da_variável é o conteúdo do ponteiro intermediário.

Na linguagem C podem ser declarados ponteiros para ponteiros para ponteiros, ou então, ponteiros para ponteiros para ponteiros para ponteiros, e assim por diante. Para fazer isto basta aumentar o número de asteriscos na declaração. A lógica continua de forma análoga.

Para acessar o valor desejado apontado por um ponteiro para ponteiro, o operador asterisco deve ser aplicado duas vezes, como mostrado no exemplo a seguir. #include <stdio.h> void main() float fpi = 3.1415, *pf, **ppf; pf = &fpi; /* pf armazena o endereco de fpi */ ppf = &pf; /* ppf armazena o endereco de pf */ printf("%f", **ppf); /* Imprime o valor de fpi */ printf("%f", *pf); /* Tambem imprime o valor de fpi */

Código 12-7

1122..66.. CCuuiiddaaddooss aa SSeerreemm TToommaaddooss aaoo ssee UUssaarr PPoonntteeiirrooss

O principal cuidado ao se usar um ponteiro deve ser, conhecer sempre para onde o ponteiro está apontando. Um ponteiro não inicializado não deve ser usado. Um pequeno programa que demonstra a forma errada de usar um ponteiro. int main () /* Errado - Nao Execute */ /* se estiver num sistema multitarefa, salve todo o seu trabalho antes de executar este programa */ int x,*p; x=13; *p=x;

Page 229: Tratado Da Linguagem c

P O N T E I R O S

207207207207

12121212

Código 12-8

Este programa compilará sem erros e poderá ser executado. O resultado é totalmente imprevisível. O ponteiro p pode estar apontando para qualquer lugar, armazenando valor 13 em um lugar desconhecido. Com um número apenas, você provavelmente não poderá ser observado nenhum efeito imediato, mas para um grande número posições (de um vetor por exemplo) provavelmente irá travar o sistema.

1122..77.. PPoonntteeiirrooss ee FFuunnççõõeess

A passagem dos parâmetros para as funções, é feita através de cópias, numa área especial da memória de dados chamada stack ou pilha. Por exemplo, para passar uma variável float para uma função, será copiado o seu valor para a pilha, e depois novamente para uma variável local da mesma. A passagem de arrays através da pilha seria uma operação complicada e não muito eficaz. Passando através da pilha o endereço de um array, seria o suficiente para que a função possa ler ou modificar o seu conteúdo, de forma rápida e eficiente. A seguir um programa exemplo. float soma(float *); /* declaração de função */ void main(void) float x[100]; float somatotal = 0.0; int i; x[0] = 0; for(i=1;i<100;i++) x[i] = x[i-1] + 1; somatotal = soma(x); float soma(float * ptr) float res = 0; int i;

for(i=0;i<100;i++) res +=ptr[i];

return res;

Código 12-9

1122..88.. AAllooccaaççããoo DDiinnââmmiiccaa ddee MMeemmóórriiaa

A alocação dinâmica permite ao programador alocar memória para variáveis quando o programa está sendo executado. Assim, pode-se definir, por exemplo, um vetor ou uma matriz cujo tamanho será calculado em tempo de execução. O padrão C ANSI define apenas 4 funções para o sistema de alocação dinâmica, disponíveis na biblioteca stdlib.h: malloc calloc realloc free

No entanto, existem diversas outras funções que são amplamente utilizadas, mas dependentes do ambiente e compilador.

Page 230: Tratado Da Linguagem c

P O N T E I R O S

208208208208

12121212

1122..88..11.. mmaalllloocc A função malloc() serve para alocar memória e tem o seguinte protótipo:

void *malloc (unsigned int num);

A função toma o número de bytes de memória necessários (num), aloca na memória e retorna um ponteiro void * que aponta para o primeiro byte alocado. O ponteiro void * pode ser atribuído a qualquer tipo de ponteiro. Se não houver memória suficiente para alocar a memória requisitada a função malloc() retornará um ponteiro nulo. Observar um exemplo de alocação dinâmica que utiliza a função malloc(): #include <stdio.h> #include <stdlib.h> /* Para usar malloc() */ void main (void) int *p; int a; int i; /*... Determina o valor de a em algum lugar. ex. a = 19; */ p = (int *)malloc(a*sizeof(int)); /* Aloca a números inteiros p pode agora ser tratado como um vetor com a posicoes */ if (!p) printf ("** Erro: Memoria Insuficiente **"); exit; for (i=0; i<a ; i++) /* p pode ser tratado como um vetor com a posicoes */ p[i] = i*i; /*...*/

Código 12-10

No exemplo anterior, é alocada memória suficiente para se armazenar a números inteiros. O operador sizeof() retorna o número de bytes de um inteiro. Este comando é útil para se calcular o tamanho de tipos. O ponteiro void* que a função malloc() retorna, é convertido para um int* pelo cast e é atribuído a p. A declaração seguinte testa se a operação foi bem sucedida. Se não tiver sido, p terá um valor nulo, o que fará com que !p retorne verdadeiro. Se a operação tiver sido bem sucedida, podemos usar o vetor de inteiros alocados normalmente, por exemplo, indexando-o de p[0] a p[(a-1)].

1122..88..22.. ccaalllloocc A função calloc() serve para alocar memória, mas possui um protótipo diferente:

void *calloc (unsigned int num, unsigned int size);

A função aloca uma quantidade de memória igual a num * size, isto é, aloca memória suficiente para um vetor de num objetos de tamanho size. Retorna um ponteiro void * para o primeiro byte alocado. O ponteiro void * pode ser atribuído a qualquer tipo de ponteiro. Se não houver memória suficiente para alocar a memória requisitada a função calloc() retorna um ponteiro nulo. Observar um exemplo de alocação dinâmica com calloc(): #include <stdio.h> #include <stdlib.h> /* declaração de calloc() */ void main (void) int *p; int a; int i; /*... Determina o valor de a em algum lugar */ p = (int *)calloc(a,sizeof(int)); /* Aloca a números inteiros p pode agora ser tratado como um vetor com a posicoes */ if (!p) printf ("** Memória Insuficiente **"); exit;

Page 231: Tratado Da Linguagem c

P O N T E I R O S

209209209209

12121212

for (i=0; i < a ; i++)/* p pode ser tratado como um vetor com a posicoes */ p[i] = i*i; /*...*/

Código 12-11

No exemplo acima, é alocada memória suficiente para se colocar a números inteiros. O operador sizeof() retorna o número de bytes de um inteiro. O ponteiro void * que calloc() retorna é convertido para um int * pelo cast e é atribuído a p.

1122..88..33.. rreeaalllloocc A função realloc() serve para realocar memória e tem o seguinte protótipo:

void *realloc (void *ptr, unsigned int num);

A função modifica o tamanho da memória previamente alocada apontada por *ptr para aquele especificado por num. O valor de num pode ser maior ou menor que o original. Um ponteiro para o bloco é devolvido porque realloc() pode precisar mover o bloco para aumentar seu tamanho. Se isso ocorrer, o conteúdo do bloco antigo é copiado no novo bloco, e nenhuma informação é perdida. Se ptr for nulo, alocará size bytes e retornará um ponteiro; se size é zero, a memória apontada por ptr é liberada. Se não houver memória suficiente para a alocação, um ponteiro nulo será retornado devolvido e o bloco original permanecerá inalterado. #include <stdio.h> #include <stdlib.h> /* declaração de malloc() e realloc*/ void main (void) int *p; int a; int i; /*... Determina o valor de a em algum lugar */ a = 30; p = (int *)malloc(a*sizeof(int));/* Aloca a números inteiros p pode agora ser tratado como um vetor com a posicoes */ if (!p) printf ("** Erro: Memoria Insuficiente **"); exit; for (i=0; i<a ; i++)/* p pode ser tratado como um vetor com a posicoes */ p[i] = i*i; /* O tamanho de p deve ser modificado, por algum motivo ... */ a = 100; p = realloc (p, a*sizeof(int)); for (i=0; i<a ; i++) /* p pode ser tratado como um vetor com a posicoes */ p[i] = a*i*(i-6); /*...*/

Código 12-12

1122..88..44.. ffrreeee Quando a memória for alocada de forma dinâmica, é necessário liberá-la quando

não for mais necessária. Para efetuar esta tarefa existe a função free() cujo protótipo é: void free (void *p);

Basta passar como parâmetro o ponteiro que aponta para o início da memória alocada. O programa saberá o tamanho da memória a ser liberada, já que esta informação é armazenada numa tabela de alocação interna. Caso a memória não for liberada no final do

Page 232: Tratado Da Linguagem c

P O N T E I R O S

210210210210

12121212

programa, esta ficará reservada e não poderá ser utilizada por outros programas, diminuindo os recursos da máquina. Observar um exemplo de utilização. #include <stdio.h> #include <stdlib.h> /* Declarações de malloc e free */ void main (void) int *p; int a; /*...*/ p = (int *)malloc(a*sizeof(int)); if (!p) printf ("Memoria Insuficiente); exit; /*...*/ free(p); /*...*/

Código 12-13

1122..99.. AAllooccaaççããoo DDiinnââmmiiccaa ddee VVeettoorreess ee MMaattrriizzeess Alocação Dinâmica de Vetores Alocação Dinâmica de Matrizes

1122..99..11.. AAllooccaaççããoo DDiinnââmmiiccaa ddee VVeettoorreess A alocação dinâmica de vetores utiliza os conceitos vistos na seção anterior. Um

exemplo de implementação para vetor real é fornecido a seguir: #include <stdio.h> #include <stdlib.h> float *Alocar_vetor_real (int n) float *v; /* ponteiro para o vetor */ if (n < 1) /* verifica parametros recebidos */ printf ("** Erro: Parametro invalido **\n"); return (NULL); /* aloca o vetor */ v = (float *) calloc (n, sizeof(float)); if (v == NULL) printf ("** Memoria Insuficiente **"); return (NULL); return (v); /* retorna o ponteiro para o vetor */ float *Liberar_vetor_real (float *v) if (v == NULL) return (NULL); free(v); /* libera o vetor */ return (NULL); /* retorna o ponteiro */ void main (void) float *p; int a; /* ... outros comandos, inclusive a inicializacao de a */ p = Alocar_vetor_real (a); /* ... outros comandos, utilizando p[] normalmente */ p = Liberar_vetor_real (p);

Código 12-14

1122..99..22.. AAllooccaaççããoo DDiinnââmmiiccaa ddee MMaattrriizzeess A alocação dinâmica de memória para matrizes é realizada da mesma forma que

para vetores, com a diferença que se terá um ponteiro apontando para outro ponteiro, que

Page 233: Tratado Da Linguagem c

P O N T E I R O S

211211211211

12121212

por sua vez, aponta para o valor final. Ou seja é um ponteiro para ponteiro, o que é denominado indireção2 múltipla. A indireção múltipla pode ser levada a qualquer dimensão desejada, mas raramente é necessário mais de um ponteiro para um ponteiro. Um exemplo de implementação para matriz real bidimensional é fornecido a seguir. A estrutura de dados utilizada no exemplo é composta por um vetor de ponteiros (correspondendo ao primeiro índice da matriz), sendo que cada ponteiro aponta para o início de uma linha da matriz. Em cada linha existe um vetor alocado dinamicamente, como descrito anteriormente (compondo o segundo índice da matriz). #include <stdio.h> #include <stdlib.h> float **Alocar_matriz_real (int m, int n) float **v; /* ponteiro para a matriz */ int i; /* variavel auxiliar */ if (m < 1 || n < 1) /* verifica parametros recebidos */ printf ("** Parametro invalido **\n"); return (NULL); /* aloca as linhas da matriz */ v = (float **) calloc (m, sizeof(float *));/*Um vetor de m ponteiros para float*/ if (v == NULL) printf ("** Memoria Insuficiente **"); return (NULL); /* aloca as colunas da matriz */ for ( i = 0; i < m; i++ ) v[i] = (float*) calloc (n, sizeof(float)); /* m vetores de n floats */ if (v[i] == NULL) printf ("** Memoria Insuficiente **"); return (NULL); return (v); /* retorna o ponteiro para a matriz */ float **Liberar_matriz_real (int m, int n, float **v) int i; /* variavel auxiliar */ if (v == NULL) return (NULL); if (m < 1 || n < 1) /* verifica parametros recebidos */ printf ("** Parametro invalido **\n"); return (v); for (i=0; i<m; i++) free (v[i]); /* libera as linhas da matriz */ free (v); /* libera a matriz (vetor de ponteiros) */ return (NULL); /* retorna um ponteiro nulo */ void main (void) float **mat; /* matriz a ser alocada */ int l, c; /* numero de linhas e colunas da matriz */ int i, j; /*... outros comandos, inclusive inicializacao para l e c */ mat = Alocar_matriz_real (l, c); for (i = 0; i < l; i++) for ( j = 0; j < c; j++) mat[i][j] = i+j; /* ... outros comandos utilizando mat[][] normalmente */ mat = Liberar_matriz_real (l, c, mat); /* ... */

EEXXEERRCCÍÍCCIIOOSS

1. Explicar a diferença entre: p++; (*p)++;

2 Também é conhecido como “vias indiretas”, ou acesso indireto.

Page 234: Tratado Da Linguagem c

P O N T E I R O S

212212212212

12121212

*(p++);

2. Explicar o significado de: *(p+10);

3. Explique o que você entendeu da comparação entre ponteiros

4. Qual o valor de y no final do programa? Tente primeiro descobrir e depois verifique no computador o resultado. A seguir, escreva um /* comentário */ em cada comando de atribuição explicando o que ele faz e o valor da variável à esquerda do '=' após sua execução.

void main() int y, *p, x; y = 0; p = &y; x = *p; x = 4; (*p)++; x--; (*p) += x; printf ("y = %d\n", y);

5. Faca um programa que multiplique duas matrizes. O programa deverá' estar estruturado de maneira que: o usuário forneça as dimensões das matrizes (teste se as dimensões são compatíveis, isto é, se as

matrizes podem ser multiplicadas); as matrizes sejam alocadas dinamicamente; as matrizes sejam lidas pelo teclado (implementar uma função para leitura das matrizes); as matrizes sejam, então, multiplicadas (implementar uma função para a multiplicação); a matriz resultante seja apresentada em tela (implementar uma função para apresentar a matriz

na tela). Utilizar alocação dinâmica de memória para a matriz resultante. Suponha as matrizes C = Am x n * Bn x t

mnmm

n

n

aaa

aaa

aaa

A

...

............

...

...

21

22221

11211

=

ntnn

t

t

bbb

bbb

bbb

B

...

............

...

...

21

22221

11211

=

O elemento [ij] da matriz C, é resultante da multiplicação da linha i de A pela coluna j de B. Portanto, a matriz Cm x t = A*B será da seguinte forma:

ntmntmtmnmnmmnmnmm

ntnttnnnn

ntnttnnnn

bababababababababa

bababababababababa

bababababababababa

C

×++×+××++×+××++×+×

×++×+××++×+××++×+××++×+××++×+××++×+×

=

............

............

............

............

221122221211212111

222212122222212211221221121

121211121221212111121121111

6. A seguir é mostrado a implementação da função StrCpy() equivalente à fornecida na biblioteca de funções. Implemente funções equivalentes para strlen() e strcat() definidas em string.h.

Page 235: Tratado Da Linguagem c

P O N T E I R O S

213213213213

12121212

#include <stdio.h> void StrCpy (char *destino,char *origem); /* declaração da função */ void main () char str1[100],str2[100],str3[100]; printf ("Entre com uma string: "); gets (str1); StrCpy (str2,str1); StrCpy (str3,"Foi colocada a string "); printf ("\n\n%s%s",str3,str2); void StrCpy (char *destino,char *origem) while (*origem) *destino=*origem; origem++; destino++; *destino='\0';

7. Verifique o programa abaixo. Encontre o seu erro e corrija-o para que escreva o numero 10 na tela.

#include <stdio.h> void main() int x, *p, **q; p = &x; q = &p; x = 10; printf("\n%d\n", &q);

8. Refazer os exemplos da seção 0, mas ao invés de trabalhar com um vetor de inteiros, use um vetor de floats. Faça leituras e apresente os resultados na tela.

9. Escreva um programa que declare uma matriz 100x100 de inteiros. Você deve inicializar a matriz com zeros usando ponteiros para endereçar seus elementos. Preencha depois a matriz com os números de 1 a 10000, também usando ponteiros.

AAuuttoo--AAvvaalliiaaççããoo

Responda as perguntas abaixo, escolhendo a alternativa adequada para cada questão.

1. A instrução abaixo acessa corretamente os elementos deste vetor?

for (j=0; j <= 30; j++) vet[j] = j*j;

a. Sim b. Não

2. Seja um vetor declarado por

int vet[10];

3. Qual elemento deste vetor é acessado quando se escreve vet[2] ?

a. Primeiro elemento b. Segundo elemento c. Terceiro elemento d. Quarto elemento e. Nenhuma das opções anteriores

4. Se declararmos um vetor como:

int vet[30]

5. Seja a matriz matrx declarada e inicializada por:

int matrx[][4] = 1,2,3,4,5,6,7,8,9,10,11,12;

O que conterá o elemento matrx[1][2] ? a. 2

Page 236: Tratado Da Linguagem c

P O N T E I R O S

214214214214

12121212

b. 5 c. 6 d. 7 e. Nenhuma das opções anteriores

6. Se uma string for declarada como:

char str[20];

o número máximo de caracteres que poderão ser lidos e armazenados nela é: a. 18 b. 19 c. 20 d. 21

7. Qual função pode ser usada para determinar o comprimento de uma string?

a. gets b. strcpy c. strcat d. strlen e. strcmp

8. Seja a seguinte seqüência de instruções em um programa C:

int *pti; int i = 10; pti = &i;

Qual afirmativa é falsa? a. pti armazena o endereço de i b. *pti é igual a 10 c. ao se executar *pti = 20; i passará a ter o valor 20 d. ao se alterar o valor de i, *pti será modificado e. pti é igual a 10

9. Qual das instruções abaixo é correta para declarar um ponteiro para inteiro?

a. *int pti; b. *pti; c. &i; d. int_pti pti; e. int *pti;

10. Se i e j são variáveis inteiras e pi e pj são ponteiros para inteiro, qual atribuição é ilegal?

a. pi = &i; b. *pj = &j; c. pj = &*&j; d. i = *&*&j; e. i = (*pi)+++*q;

11. Seja a seguinte seqüência de instruções em um programa C:

int *pti; int veti[]=10,7,2,6,3; pti = veti;

12. Qual afirmativa é falsa?

a. *pti é igual a 10 b. *(pti+2) é igual a 2 c. pti[4] é igual a 3 d. pti[1] é igual a 10 e. *(veti+3) é igual a 6

13. Na seqüência de instruções abaixo:

float f; float *pf; pf = &f; scanf("%f", pf);

a. Efetuamos a leitura de f b. Não efetuamos a leitura de f c. Temos um erro de sintaxe d. Deveríamos estar usando &pf no scanf e. Nenhuma das opções anteriores

14. Seja a seguinte seqüência de instruções

Page 237: Tratado Da Linguagem c

P O N T E I R O S

215215215215

12121212

int i=10, j=20; int *pti, *ptj; pti = &i; ptj = &j;

Qual expressão não é válida? a. j = pti == ptj; b. i = pti-ptj; c. pti += ptj; d. pti++; e. i = pti || ptj;

15. Seja a declaração:

int matr[][4] = 1,2,3,4,5,6,7,8,9,10,11,12

Qual afirmativa é falsa? a. **matr é igual a 1 b. *(*(matr+1)+2) é igual a 7 c. *(matr[2]+3) é igual a 12 d. (*(matr+2))[2] é igual a 11 e. *((*matr)+1) é igual a 5

Page 238: Tratado Da Linguagem c

216216216216

1133.. AASS FFUUNNÇÇÕÕEESS As funções são as unidades fundamentais da linguagem C. Uma função é

usualmente projetada para executar alguma tarefa específica, e o seu nome freqüentemente reflete essa tarefa. Uma função contém declarações e instruções. Este capítulo descreve como declarar, definir e chamar funções em C. Outros tópicos que serão discutidos são: Introdução às Funções Atributos de uma Função Convenções de chamada Funções inline Classes de Armazenamento Tipos de retorno Argumentos de uma Função Parâmetros de uma Função Corpo de uma Função Protótipo de uma Função Chamadas de Função Recursividade

1133..11.. IInnttrroodduuççããoo ààss FFuunnççõõeess

Toda função deve ter uma definição e poderá ter uma declaração, embora que a definição possa servir como declaração se esta aparecer antes que a função seja chamada. A definição da função inclui o corpo da função, que é o código que será executado quando a função for invocada.

A declaração da função estabelece o nome, o tipo de retorno, e os atributos da função que é definida em algum outro lugar do programa. Uma declaração de função deve preceder à chamada da função. No caso das funções da biblioteca, os arquivos de cabeçalho contêm as declarações das funções de runtime que serão incluídas no código antes que estas sejam usadas. Se as declarações possuem os tipos e número de parâmetros, a declaração é denominada de protótipo 1.

Os compiladores utilizam os protótipos para comparar os tipos de argumentos nas subseqüentes chamadas da função com os parâmetros da função, e para a conversão de tipos de argumentos para os tipos de parâmetros quando necessário.

Uma chamada de função passa o controle da execução da função invocadora para a função invocada. Os argumentos, se existirem, são passados por valor para a função invocada. A execução de uma instrução return na função invocada retorna o controle e possivelmente um valor para a função invocadora.

1 Ver na seção 13.7 para maiores informações.

Capítulo

13

Page 239: Tratado Da Linguagem c

A S F U N Ç Õ E S

217217217217

13131313

Funções são as estruturas que permitem ao programador separar os programas em blocos menores, permitindo a programação estruturada. A implementação de programas grandes e complexos é facilitada pela divisão em programas menores. As funções também evitam a repetição de código, já que as funções podem ser aproveitadas pelo mesmo programa, ou por outros.

Uma função tem a seguinte forma geral:

tipo_de_retorno nome_da_função (declaração_de_parâmetros) corpo_da_função

O tipo-de-retorno é o tipo de variável que a função retorna. O tipo default é int., i.e. se o tipo de retorno não for declarado, o compilador assumirá o tipo int. A declaração de parâmetros é uma lista com a seguinte forma geral:

tipo nome1, tipo nome2, ... , tipo nomenN

O corpo da função, também conhecido como “definição da função”, define o que a função executará quando chamada. É na definição que as entradas serão processadas, as saídas serão geradas ou algumas ações executadas.

1133..22.. DDeeffiinniiççããoo ddee uummaa FFuunnççããoo

A definição de uma função especifica o nome da função, os tipos e o número de parâmetros que esta espera receber, e o seu tipo de valor de retorno. Uma definição de função também inclui o corpo da função com as declarações das suas variáveis locais e as instruções que determina as tarefas que a função executará.

Os únicos especificadores de classe de armazenamento que podem modificar a declaração da função são static e extern. O especificador extern significa que a função poderá ser referenciada de outros arquivos; isto é, o nome da função será exportado para o linker. O especificador static significa que a função não pode ser referenciada em outros arquivos; isto é, o nome não será exportado pelo linker. Se não aparecerem definidas as classes de armazenamento em uma definição de função, o especificador extern será assumido. De qualquer forma, a função será sempre visível a partir do ponto da definição até o final do arquivo.

Os especificadores de declaração opcionais e os declaradores obrigatórios, juntos especificam o tipo de retorno e o nome. O declarador é uma combinação entre o identificador que nomeia a função e os parênteses que seguem ao nome da função.

O declarador direto especifica o nome da função que está sendo definida e os identificadores de seus parâmetros. Se o declarador direto inclui uma lista de tipos de parâmetros, a lista especifica os tipos de todos os parâmetros. Estas declarações também servem como um protótipo de função para as chamadas posteriores da mesma.

A declaração na lista de declarações nas definições de função não pode conter o especificador de classe de armazenamento diferente de register. O especificador de tipo nos especificadores de declaradores poderá ser omitido somente se a classe de armazenamento register for especificada para o valor de um tipo int.

Page 240: Tratado Da Linguagem c

A S F U N Ç Õ E S

218218218218

13131313

O corpo da função é uma instrução composta contendo declarações de variáveis locais, referências a itens declarados externamente e instruções.

1133..22..11.. AAttrriibbuuttooss ddaass FFuunnççõõeess –– FFuunnççõõeess iinnlliinnee Alguns compiladores permitem o uso de atributos especiais tais como o atributo

inline2. Usualmente a keyword inline informa ao compilador que substitua o código dentro da função da definição em cada instância de invocação da função. Desta maneira, as substituições ocorrerão a critério do compilador. Por exemplo, o compilador não implementará a função como inline se o seu endereço é necessário ser fixo ou se for muito grande para uma função inline3.

O uso de funções inline gera um código mais rápido e às vezes podem gerar códigos menores que as equivalentes chamadas de função gerariam pelas seguintes razões: Este atributo poupa o tempo requerido para as chamadas da função. Pequenas funções inline, em volta de três ou menos linhas, criam um código menor que a

chamada da função equivalente já que o compilador não precisa criar código para manipular argumentos e o valor de retorno. As funções geradas inline estão sujeitas a otimizações de código não disponíveis para as

funções normais porque os compiladores não efetuam nelas os processos de otimização interprocedural4.

OO AAsssseemmbblleerr IInnlliinnee

O “Assembler Inline” permite adicionar instruções em linguagem assembly diretamente no programa em C, sem ter que efetuar passos extras nem processos de linker. O assembler inline é disponível na maioria dos compiladores C, e usualmente o compilador assembler já está incluído no compilador C.

Uma vez que o assembler inline não requer de qualquer processo especial, o seu uso é mais conveniente que um assembler separado. O assembly inline pode usar qualquer variável C ou nome de função que estiver no seu escopo, de forma que é fácil integrá-lo ao programa em C. Como o código em assembly pode ser misturado com as instruções em C, podem ser efetuadas tarefas que seriam incômodas ou demoradas somente com a linguagem C.

A keyword asm5 invoca o assembler inline e pode aparecer em qualquer lugar onde pode ser colocada uma instrução em C. Esta keyword não pode aparecer isolada, mas deve ser seguida de instruções em assembly, um grupo de instruções entre chaves ou, no pior caso, um par vazio de chaves.

O código a seguir é um exemplo de um bloco em assembly. _asm push ebp mov ebp, esp sub esp, LOCAL_SIZE

2 Ver a documentação do compilador a ser usado para maiores informações sobre os atributos permitidos. 3 Em alguns compiladores para microprocessadores, o atributo inline é o default, para melhorar a velocidade de execução e espaço de pilha a custas de espaço de memória de programa. Usualmente é uma opção de grau e tipo de otimização. 4 As funçõs que usam o atributo inline não devem ser confundidas com o código inline assembler como será visto mais adiante. 5 Dependendo da implementação do compilador a keyword poderá ser _asm, __asm ou até uma diretiva de compilador tal como #asm. Ver a documentação do compilador em uso para maiores informações.

Page 241: Tratado Da Linguagem c

A S F U N Ç Õ E S

219219219219

13131313

De forma alternativa, pode-se colocar asm na frente de cada instrução assembly: _asm push ebp _asm mov ebp, esp _asm sub esp, __LOCAL_SIZE

Como a keyword asm é um separador de instruções, poderão ser colocadas várias instruções na mesma linha: __asm push ebp __asm mov ebp, esp __asm sub esp, __LOCAL_SIZE

1133..33.. CCllaasssseess ddee AArrmmaazzeennaammeennttoo

O especificador de classe de armazenamento pode ser do tipo extern ou static. A sintaxe pode ser como segue:

extern tipo-de-retorno nome-da–função (argumentos) static tipo-de-retorno nome-da–função (argumentos)

Se a definição da função não incluir o especificador de classe de armazenamento, o especificador default será extern. O especificador extern pode ser explicitamente declarado, mas não é necessário.

Caso a declaração da função contenha a o especificador extern, o identificador terá a mesma capacidade de conexão que qualquer declaração visível do identificador no escopo do arquivo. Se não houver declaração visível no escopo do arquivo, o identificador será conectado externamente. Se um identificador tem o escopo de arquivo e não tiver especificador de classe de armazenamento, terá conexões externas através do linker. As conexões externas através do linker permitem que cada instancia do identificador denote o mesmo objeto ou função6.

As declarações de funções com escopo de bloco com especificador de classe de armazenamento diferente de extern geram erros.

A função definida com classe de armazenamento static, é visível somente no arquivo fonte na qual é definida. Todas as outras funções, com ou sem a classe de armazenamento extern, são visíveis através de todos os arquivos fontes do programa. Caso seja necessária a classe de armazenamento static, esta deve ser declarada na primeira ocorrência da declaração da função, se houver, e na definição da mesma.

1133..44.. TTiippoo ddee RReettoorrnnoo ddee uummaa FFuunnççããoo

O tipo de retorno da função estabelece o tamanho e o tipo do valor retornado pela função, e pode ser um dos tipos mostrados a seguir ou um ponteiro para estes tipos:

void char short int long float double

6 Ver a seção 0 para maiores informações sobre escopo de arquivo e conexões de linker.

Page 242: Tratado Da Linguagem c

A S F U N Ç Õ E S

220220220220

13131313

signed unsigned struct ou union enum typedef

O especificador de tipo pode especificar qualquer tipo fundamental, estrutura ou union. Caso não seja incluído o especificador de tipo, o tipo de retorno assumido por default é o int.

O tipo de retorno dado na definição da função deve coincidir com o tipo de retorno nas declarações da função em qualquer lugar do programa. A função retorna um valor quando for executada uma instrução return contendo uma expressão. A expressão será avaliada, convertida para o tipo de valor de retorno se necessário, e retornada para o ponto no qual a função foi invocada. Se a função foi declarada com o tipo de retorno void, a instrução return que contenha uma expressão gerará um aviso ou um erro de compilação, e a expressão nem será avaliada.

Os seguintes exemplos ilustram os valores de retorno de função. typedef struct char name[20]; int id; long class; USER; /* O tipo de retorno é USER: */ USER sortuser( USER a, USER b ) return ( (a.id < b.id) ? a : b );

O exemplo define o tipo USER com a declaração typedef e define a função sortuser como tendo o tipo de retorno USER. A função seleciona e retorna um dos dois argumentos do tipo estrutura. Nas chamadas subseqüentes da função, o compilador verifica que os argumentos sejam do tipo USER.

A eficiência do processo pode ser melhorada pela passagem de ponteiros para estrutura, no lugar de passar a estrutura completa para a função. Esta forma melhora o uso do recurso tempo e de pilha. Observar o seguinte exemplo: char *smallstr( char s1[], char s2[] ) int i; i = 0; while ( s1[i] != '\0' || s2[i] != '\0' ) i++; if ( s1[i] == '\0' ) return ( s1 ); else return ( s2 );

O exemplo define uma função que retorna um ponteiro para um array de caracteres. A função toma dois arrays de caracteres (strings) como argumento e retorna um ponteiro para a string mais curta das duas. Um ponteiro para um array aponta para o primeiro elemento do array e têm o mesmo tipo; assim, o tipo de retorno da função é um ponteiro para o tipo char.

Page 243: Tratado Da Linguagem c

A S F U N Ç Õ E S

221221221221

13131313

Não é necessário declarar o retorno de funções do tipo int antes de chamá-las, embora os protótipos são recomendados para a verificação correta do tipo para os argumentos e os seus valores de retorno, se houverem.

OO TTiippoo ddee RReettoorrnnoo vvvvvvvvooooooooiiiiiiiidddddddd

A palavra inglesa void significa vazio. Este tipo permite implementar funções que não retornam valores e nem possuem parâmetros, deixando esta característica de forma explícita, já que o default é o tipo int. Pode-se assim escrever o protótipo de uma função que não retorna valor:

void nome_da_função (lista-de-parâmetros);

Numa função, como a acima, não temos valor de retorno na declaração return. Desta forma, o comando return não é necessário na função.

Para funções que não têm parâmetros:

tipo_de_retorno nome_da_função (void);

ou, ainda, funções sem valor de retorno nem parâmetros:

void nome_da_função (void);

Um exemplo de funções que usam o tipo void: #include <stdio.h> void Mensagem (void); int main () Mensagem(); printf ("\tRepetir:\n"); Mensagem(); return 0; void Mensagem (void) printf ("Digitar a sua senha \n");

Código 13-1

O retorno de uma função quando este não for necessário pode ser útil para indicar o status da execução. É uma regra geral, retornar um valor de status 0 para a execução da função sem problemas, valores positivos, para indicar algum tipo de evento, e valores negativos para indicar erros de operação, sendo que o valor é codificado pelo programador. No caso da função main() pode-se usar um valor de retorno para o sistema operacional.

As duas funções main() a seguir são válidas: main (void) .... if(erro) return –1; return 0;

Código 13-2

void main (void) ....

Page 244: Tratado Da Linguagem c

A S F U N Ç Õ E S

222222222222

13131313

1133..55.. PPaarrââmmeettrrooss ddee uummaa FFuunnççããoo

Os argumentos são os nomes dos valores passados para a função pela invocação da mesma. Os parâmetros são os valores que a função espera receber. Em um protótipo de função, os parênteses que seguem ao nome da função, contêm a lista completa dos parâmetros da função e seus tipos. As declarações dos parâmetros especificam os tipos, tamanhos e identificadores dos valores armazenados em tais parâmetros.

Os parâmetros da função declarados com o atributo auto, gerarão erros de compilação. Os identificadores dos parâmetros são usados no corpo da função e se referem aos valores passados para a função. Os nomes dos parâmetros podem ser colocados no protótipo, mas estes nomes estarão fora do escopo no final da declaração. Desta forma, os nomes dos parâmetros podem ser atribuídos de forma diferente na definição da função. Estes identificadores não podem ser redefinidos nos blocos mais externos do corpo da função, mas sim em blocos internos e aninhados.

Cada parâmetro da lista de tipos deve ser precedido de seu especificador de tipo apropriado, como mostra o seguinte exemplo: void new( double x, double y, double z ) /* Corpo da função deve ser colocado aqui */

Se existir pelo menos um parâmetro na lista de parâmetros, a lista pode terminar com uma vírgula seguida de três pontos sucessivos (, ...). Esta construção7, indica um número variável de argumentos para a função8. Desta forma, a chamada de função deverá ter tantos argumentos quanto parâmetros antes da última vírgula.

Se nenhum argumento é passado para a função, a lista de parâmetros é substituída pela keyword void. O uso desta keyword é diferente do uso como especificador de tipo.

A ordem e o tipo de parâmetro, incluindo qualquer uso da notação com reticências, deve ser a mesma em todas as declarações (se existir) e na definição da função. Os tipos dos argumentos após as conversões aritméticas usuais devem ser compatíveis com os tipos dos parâmetros correspondentes9. Os argumentos que seguem uma declaração com reticências não serão verificados. Um parâmetro pode ter qualquer tipo fundamental, estrutura, union, ponteiro ou array.

Os compiladores efetuam as conversões aritméticas usuais de forma independente em cada parâmetro e argumento, quando necessário. Caso existe uma conversão, nenhum parâmetro será menor que um int, e nem do tipo float a menos que esteja explicitamente especificado no protótipo da função. Isto permite, por exemplo, que declarando um parâmetro tal como um char tenha o mesmo efeito que a declaração do mesmo como um int.

1133..66.. CCoorrppoo ddee uummaa FFuunnççããoo

O corpo da função é uma instrução composta contendo as instruções que especificam o que a função faz.

7 Usualmente chamada de notação “ellipsis” ou reticências. 8 Ver Chamadas com Número Variável de Argumentos. 9 Ver Conversões Aritméticas Usuais.

Page 245: Tratado Da Linguagem c

A S F U N Ç Õ E S

223223223223

13131313

As variáveis declaradas no corpo da função, chamadas de variáveis “locais”, possuem a classe de armazenamento auto a menos que outra seja especificada. Quando a função é chamada, o armazenamento é criado para as variáveis locais e são iniciadas as inicializações. O controle da execução passa para a primeira instrução e continua até que seja executada uma instrução return ou se tenha encontrado o final do corpo da função. O controle retorna então ao ponto no qual a função foi invocada.

A instrução return contém uma expressão que deve ser executada se a função retorna um valor. O valor de retorno da função é indefinido se a instrução return não for executada ou se a instrução não inclui uma expressão.

1133..77.. PPrroottóóttiippooss ddee uummaa FFuunnççããoo

A declaração de uma função precede à definição da mesma e especifica o seu nome, tipo de retorno, classe de armazenamento e outros atributos. Para ser um protótipo, a declaração da função deve ter estabelecido de forma explícita todos os tipos e identificadores para os argumentos da função.

O protótipo tem a mesma forma que a definição da função, exceto que é terminada por um ponto e vírgula seguindo imediatamente após o fechamento dos parênteses e desta forma não tem corpo. Em qualquer caso, o tipo de retorno deve ser de acordo com o tipo especificado na definição da função.

Os protótipos de função têm os seguintes usos: Elas estabelecem o tipo de retorno para a função que retorna tipos diferentes de um int.

Embora as funções que retornam valores do tipo int não requeiram de protótipos, o seu uso é recomendado. Sem protótipos completos, as conversões padrões serão efetuadas, mas não serão verificados

os tipos ou números de argumentos comparando com o número de parâmetros. Os protótipos são usados para inicializar ponteiros para funções antes que estas forem

definidas. A lista de parâmetros é usada para verificar a correspondência dos argumentos nas chamadas

da função com os parâmetros da definição da mesma.

O tipo convertido de cada parâmetro determina a interpretação dos argumentos que a chamada de função coloca no stack. Um tipo diferente entre um argumento e um parâmetro pode ocasionar que os argumentos no stack sejam mal interpretados. Por exemplo, em um sistema de 16 bits, se um ponteiro de 16 bits é passado como um argumento, então declarada como um parâmetro long (de 32 bits), os primeiros 32 bits do stack serão interpretados como um parâmetro long. Este erro gera problemas não somente com o parâmetro long, mas com qualquer outro parâmetro que o seguir. Estes erros podem ser evitados pela declaração completa dos protótipos de função para todas as funções.

Um protótipo estabelece o atributo da função de forma que as chamadas precedentes à sua definição (ou em outros arquivos fonte) possam ser verificadas no que se refere a tipos de argumentos, número de argumentos e tipo de retorno. Por exemplo, se for especificada a classe de armazenamento static no protótipo, deve-se também especificar a mesma classe de armazenamento na definição da função.

Declarações completas dos parâmetros podem ser misturadas com as declaraçòes abstratas na mesma declaração. Por exemplo, a seguinte declaração é correta:

Page 246: Tratado Da Linguagem c

A S F U N Ç Õ E S

224224224224

13131313

int add( int a, int );

O protótipo pode incluir ambos os tipos e os identificadores para cada expressão que é passada como argumento. Porém, tais identificadores têm escopo somente até o final da declaração. O protótipo pode também mostrar o fato de que o número de argumentos é variável, ou não há argumentos. Sem esta lista explicita de argumentos, as inconsistências poderão não ser reveladas, de forma que o compilador não poderá gerar os avisos pertinentes10 a estas.

Observar o seguinte exemplo: struct S; void func1( struct S * );

Nos exemplos apresentados nas seções anteriores, as funções eram definidas antes da função main(). Isto é, as funções estão fisicamente antes da função main(). O compilador C deve conhecer as funções (tipos de parâmetros e de valor de retorno), antes de efetuar a verificação das funções em que as primeiras serão utilizadas, para poder efetuar as consistências necessárias. Devido a isto, todas as funções devem ser declaradas antes de serem usadas (aparecer no código), de forma análoga às variáveis. As declarações de funções são também chamadas de protótipos de função. Nos exemplos anteriores, como as funções eram definidas antes de serem utilizadas, não é necessário declará-las, embora isto seja sempre recomendável.

Observar o exemplo a seguir: #include <stdio.h> float quadrado (float a); /* protótipo ou declaração da função */ int main () float num; printf ("Entre com um numero: "); scanf ("%f",&num); num = quadrado(num); printf ("\n\nO seu quadrado vale: %f\n",num); return 0; float quadrado (float a) return (a*a);

Código 13-3

Observar que a função quadrado() está colocada depois da função main(), mas o seu protótipo está declarado antes. Sem isto este programa gerará um erro de compilação, avisando que a função que está sendo usada não é conhecida ainda. É uma característica dos compiladores C efetuar uma varredura do código de cima para baixo, dentro de um programa fonte.

Usando protótipos podem-se construir funções que retornam quaisquer tipos de variáveis. Os protótipos não somente ajudam ao compilador poder efetuar as consistências, mas também ajudam ao programador avisando do uso de tipo e número de parâmetros errado, ou, tipo de retorno errado.

1133..88.. CChhaammaaddaass ddee FFuunnççããoo

Uma chamada de função é uma expressão que passa o controle e argumentos (se houverem) para a função e tem a seguinte forma

10 Ver Argumentos para maiores informações acerca de verificação de tipos.

Page 247: Tratado Da Linguagem c

A S F U N Ç Õ E S

225225225225

13131313

expressão ( lista-de-expressões )

Onde a expressão é o nome da função ou a avaliação do endereço da função, e a lista de expressões é a lista de expressões separadas por vírgulas. Os valores da lista de expressões são os argumentos passados para as funções. Se a função não retornar um valor, então deve ser declarada como função que retorna void.

Se existir uma declaração antes da chamada da função, mas nenhuma informação concernente aos parâmetros for dada, qualquer argumento não declarado simplesmente sofrerá as conversões aritméticas usuais11.

O único requerimento em qualquer chamada de função é que a expressão antes dos parênteses seja avaliada para um endereço de função. Isto permite que a função possa ser chamada através de qualquer expressão de ponteiro para função.

O exemplo a seguir ilustra a chamada de função a partir de uma instrução switch: void main( ) /* Protótipos de funções */ long lift( int ), step( int ), drop( int ); void work( int number, long (*function)(int i) ); int select, count; . . . select = 1; switch( select ) case 1: work( count, lift ); break; case 2: work( count, step ); break; case 3: work( count, drop ); default: break; /* Definição de função */ void work( int number, long (*function)(int i) ) int i; long j; for ( i = j = 0; i < number; i++ ) j += ( *function )( i ); long lift( int x ) return 1; long step( int x ) return 2; long drop( int x) return 3;

Código 13-4

No exemplo, a função chamada pela função main

11 As expressões na lista de argumentos poderá ser avaliada em qualquer ordem, de forma que argumentos cujos valores possam mudar por efeitos colaterais a partir de algum outro argumento, terá o seu valor indefinido. Um ponto de seqüência definido pelo operador de chamada de função garante somente que todos os efeitos colaterais da lista de arguemntos serão avaliados antes de que o controle passe para a função invocada. Ver Pontos de Seqüência para maiores informações.

Page 248: Tratado Da Linguagem c

A S F U N Ç Õ E S

226226226226

13131313

work( count, lift );

passa um valor inteiro, count, e o endereço da função lift para a função work 12.

O parâmetro do tipo função da função work é declarado como sendo um ponteiro para função que toma um int como argumento e retorna um valor long. Os parênteses em volta do nome do parâmetro são requeridos; sem estes, a declaração iria especificar o retorno da função, ou seja um ponteiro para um valor long.

A função work invoca a função selecionada de dentro do loop for usando a seguinte instrução: ( *function )( i );

Um argumento é passado para a função invocada (i).

1133..88..11.. AArrgguummeennttooss Como já foi visto anteriormente, a chamada de função pode ter a seguinte sintaxe:

nome-da-função ou expressão (lista-de-expressões)

Numa chamada de função, a lista de expressões é uma lista de expressões separadas por vírgulas. Os valores destas expressões são os argumentos passados para a função. Se a função não tem argumentos, a lista de expressões deverá conter a keyword void.

Um argumento pode ser qualquer valor do tipo fundamental, estrutura, union ou ponteiro. Todos os argumentos são passados pelo valor. Isto permite que a cópia de um argumento seja atribuída ao seu correspondente parâmetro. A função não sabe a posição atual da memória do argumento passado. A função usa esta cópia sem afetar a variável a partir da qual o valor originalmente deriva.

Embora não possam ser passados arrays ou funções como argumentos, poderão ser passados ponteiros para estes itens. Desde que um ponteiro para uma variável armazena o endereço da mesma, a função pode usar este endereço para acessar o valor da variável. Os argumentos do tipo ponteiro permitem a uma função acessar arrays e funções, ainda que estes não possam ser passados como argumentos.

A ordem em que os argumentos são avaliados pode variar dependendo do compilador e o nível de otimização escolhido. Embora, os argumentos e qualquer efeito colateral são completamente avaliados antes da entrada da função 13.

A cada chamada de função a lista de expressões será avaliada e efetuadas as conversões aritméticas usuais em cada argumento. Se o protótipo estiver disponível, o tipo de argumento resultante será comparado com o correspondente parâmetro do protótipo. Se não forem coincidentes, ou será executada uma conversão, ou será emitida uma mensagem de aviso ou de erro. Os parâmetros também sofrerão as conversões aritméticas usuais.

12 Notar que o endereço da função é passado simplesmente colocando o identificador da função, desde que o mesmo avalia para uma expressão do tipo ponteiro. Para usar o identificador de função desta maneira, a função deve ser declarada ou definida antes que o seu identificador seja usado; de outra forma, o identificador não será reconhecido. Neste caso o protótipo para a função work é dado no inicio da função main. 13 Ver Efeitos Colaterais para maiores informações sobre estes efeitos.

Page 249: Tratado Da Linguagem c

A S F U N Ç Õ E S

227227227227

13131313

O número de expressões em uma lista de expressões deve coincidir com o número de parâmetros, a menos que o protótipo da função ou sua definição especifique explicitamente um número variável de argumentos. neste caso, o compilador verifica tantos argumentos quantos nomes de tipos houver na lista de parâmetros convertendo-os, se necessário, como descrito anteriormente14.

Se a lista de parâmetros do protótipo conter somente a keyword void, o compilador esperará nenhum argumento na chamada da função e nenhum parâmetro na sua definição. Uma mensagem de diagnóstico poderá aparecer caso seja colocado algum argumento.

O seguinte exemplo utiliza ponteiros como argumentos: void main() /* Protótipo da função */ void swap( int *num1, int *num2 ); int x, y; . . . swap( &x, &y ); /* Chamada de função */ /* Definição da função */ void swap( int *num1, int *num2 ) int t; t = *num1; *num1 = *num2; *num2 = t;

Código 13-5

No exemplo, a função swap é declarada na função main como tendo dois argumentos, representados pelos identificadores num1 e num2, ambos os quais são ponteiros para valores int. Os parâmetros num1 e num2, na definição também são declarados como ponteiros para valores do tipo int.

Na chamada de função swap( &x, &y )

o endereço de x é armazenado em num1 e o endereço de y é armazenado em num2. Agora existem dois nomes que referenciam as mesmas posições de memória. As referências a *num1 e *num2 na função swap são efetivamente referências a x e y. Desta forma, nenhuma instrução de retorno é necessária.

O compilador efetua a verificação do tipo nos argumentos da função swap porque o seu protótipo inclui os tipos de argumento para cada parâmetro. Os identificadores dentro dos parênteses do protótipo e da definição podem ser iguais ou diferentes. O importante é que os tipos de argumentos coincidam com a lista de parâmetros tanto no protótipo quanto na definição.

1133..88..22.. CChhaammaaddaass ccoomm NNúúmmeerroo VVaarriiáávveell ddee AArrgguummeennttooss

Uma lista parcial de parâmetros pode ser terminada pela notação com reticências 15, para indicar que pode haver mais argumentos passados para a função, mas nenhuma 14 Ver a seção 13.8.2, Chamadas com Número Variável de Argumentos para maiores informações. 15 Uma vírgula seguida de três pontos (, ...).

Page 250: Tratado Da Linguagem c

A S F U N Ç Õ E S

228228228228

13131313

informação é dada ao respeito deles. Neste caso não será executada a verificação de tipo em tais argumentos. Pelo menos um parâmetro deverá preceder as reticências, e esta deve ser o último token na lista de parâmetros. Sem o uso das reticências, o comportamento da função é indefinido se receber mais parâmetros que os declarados na lista de parâmetros.

Para invocar uma função com número variável de argumentos, simplesmente deve-se especificar qualquer número de argumentos na chamada da função. Um exemplo é a função printf da biblioteca padrão de funções da linguagem C. A chamada da função deve incluir um argumento para cada nome de tipo declarado na lista de parâmetros ou na lista de argumentos.

Todos os argumentos especificados na chamada de função são colocados na pilha a menos que seja especificado de forma diferente16. O número de parâmetros declarados para a função determina quantos argumentos serão colocados no stack e atribuídos aos parâmetros. O programador é responsável pela recuperação de qualquer argumento adicional a partir do stack e de determinar quantos argumentos estão presentes 17.

A linha seguinte mostra uma declaração de uma função com número variável de argumentos: int average( int first, ...);

1133..99.. RReeccuurrssiivviiddaaddee

Qualquer função de um programa em C pode ser chamada de forma recursiva, i.e., ela pode chamar a si mesma. O número de chamadas recursivas, é limitado ao tamanho do stack e depende do compilador18. Cada vez que a função é chamada, será alocado um novo espaço de armazenamento para os parâmetros e para as variáveis auto e register, de forma que os seus valores nas chamadas prévias, e ainda não acabadas, não serão sobrescritos. Os parâmetros são somente acessados diretamente na instância da função na qual foram criados.

As variáveis declaradas com armazenamento static não requerem de nova alocação a cada chamada recursiva. O seu armazenamento existe durante o tempo de vida do programa. Cada referência a tal variável acessa a mesma área de armazenamento.

O exemplo a seguir ilustra a recursividade. int factorial( int num ); /* Protótipo de função */ void main() int result, number; . . . result = factorial( number ); int factorial( int num ) /* Definição da função */ . . . if ( ( num > 0 ) || ( num <= 10 ) ) return( num * factorial( num - 1 ) );

16 Depende de cada compilador. 17 O arquivo de cabeçalho STDARGS.H contém as macros no estilo ANSI para acessar argumentos de funções que possuam um número variável de argumentos. 18 Alguns compiladores para microcontroladores não fornecem este recurso devido a limitações no stack de alguns chips.

Page 251: Tratado Da Linguagem c

A S F U N Ç Õ E S

229229229229

13131313

Código 13-6

Observe um programa equivalente que calcula o fatorial de um número inteiro n (n!): #include <stdio.h> int fat(int n) if (n) return n*fat(n-1); else return 1; int main() int n; printf("\n\nDigite um valor para n: "); scanf("%d", &n); printf("\nO fatorial de %d e' %d", n, fat(n)); return 0;

Código 13-7

Note que, enquanto n não for igual a 0, a função fat() chama a si mesma, cada vez com um valor menor. Assim, n = 0 é critério de parada para esta função.

Há certos algoritmos que são mais eficientes quando feitos de maneira recursiva, mas a recursividade é algo a ser evitado sempre que possível, pois, se usada incorretamente, tende a consumir muita memória e deixar a execução muito lenta. As funções quando são chamadas, utilizam recursos de memória (stack), desta forma, a recursividade pode esgotar estes recursos rapidamente. Todo cuidado é pouco ao se fazer funções recursivas. A primeira coisa a se providenciar é um critério de parada. Este vai determinar quando a função deverá parar de chamar a si mesma. Isto impede que a função se chame infinitas vezes. Em compiladores para microcontroladores, a recursividade poderá ser limitada ou inexistente.

1133..1100.. MMooddiiffiiccaaddoorreess ddee FFuunnççõõeess pascal cdecl interrupt

A forma geral de uma função é, como já foi visto anteriormente:

tipo_de_retorno nome_da_função (declaração_de_parâmetros) corpo_da_função

Uma função pode aceitar um modificador de tipo. Este modificará o modo como a função opera, no que se refere à passagem de parâmetros. A forma geral da função ficaria então:

modificador_de_tipo tipo_de_retorno nome_da_função (declaração_de_parâmetros) corpo_da_função

Page 252: Tratado Da Linguagem c

A S F U N Ç Õ E S

230230230230

13131313

1133..1100..11.. ppaassccaall Este modificador permite que a função use a convenção das funções da linguagem

Pascal. Isto faz com que as funções sejam compatíveis com programas feitos em Pascal. Basicamente muda a ordem na passagem de parâmetros de uma função, de acordo à forma com que os parâmetros são colocados na memória de troca (stack).

1133..1100..22.. ccddeeccll O modificador de tipo cdecl faz com que a função use a convenção para funções

da linguagem C. É o modificador default.

1133..1100..33.. iinntteerrrruupptt Este modificador indica ao compilador que a função em questão será usada como

um manipulador de interrupções (rotinas de atendimento a interrupção). Isto faz com que o compilador preserve os registradores da CPU antes e depois da chamada à função.

1133..1111.. PPoonntteeiirrooss ppaarraa FFuunnççõõeess

A linguagem C permite o acesso à funções através de ponteiros. Por exemplo, pode-se passar uma função como argumento para outra função. Um ponteiro para uma função tem a seguinte declaração:

tipo_de_retorno (*nome_do_ponteiro)();

ou

tipo_de_retorno (*nome_do_ponteiro)(declaração_de_parâmetros);

Reparar nos parênteses que devem ser colocados obrigatoriamente.

Não é obrigatória a declaração dos parâmetros da função. Observar um exemplo do uso de ponteiros para funções: #include <stdio.h> #include <string.h> void PrintString (char *str, int (*func)(const char *)); void main (void) char String [20] = "Curso de C."; int (*p)(const char *); /* Declaracao do ponteiro para função Funcao apontada e' inteira e recebe como parametro uma string constante */ p = puts; /* O ponteiro p passa a apontar para a função puts que tem o seguinte prototipo: int puts(const char *) */ PrintString (String, p); /* O ponteiro é passado como parametro para PrintString */ void PrintString (char *str, int (*func)(const char *)) (*func)(str); /* chamada a função através do ponteiro para função */ func(str); /* maneira também válida de se fazer a chamada a função puts através do ponteiro para função func */

Código 13-8

Observar que foi feita uma atribuição de puts a p simplesmente usando:

Page 253: Tratado Da Linguagem c

A S F U N Ç Õ E S

231231231231

13131313

p = puts;

Assim, pode-se concluir que o nome de uma função é uma variável, que contém o seu endereço. Pode-se notar também que há duas formas alternativas de se chamar uma função através de um ponteiro. No programa do exemplo anterior a função foi chamada por: (*func)(str);

e func(str);

Estas formas são equivalentes entre si.

Além disto, neste programa, a função PrintString() usa uma função qualquer func para imprimir a string na tela. O programador poderá então fornecer não somente a string mas também o nome da função que deverá ser usada para imprimi-la na tela. Na função main() pode-se observar como pode ser feita a atribuição para o ponteiro de funções p, ao endereço da função puts().

Ao declarar um ponteiro para função, pode-se atribuir a este, o endereço de uma função e pode-se também chamar a função apontada através dele19.

1133..1122.. AAllgguummaass CCoonnssiiddeerraaççõõeess GGeerraaiiss

Uma função, é um bloco de construção muito útil. Na linguagem C, as funções são bastante flexíveis. A flexibilidade dá poder, mas exige cuidado.

Funções devem ser implementadas, quando possível, da maneira mais geral possível. Isto as torna mais fáceis de serem reutilizadas e entendidas. Deve-se evitar, sempre que possível, funções que usem variáveis globais, já que isto diminui a modularidade.

Se houver uma rotina que deve ser executada da forma mais rápida possível, é melhor implementá-la com um mínimo de chamadas a outras funções, já que as chamadas de função consomem tempo e memória.

1133..1133.. EEssccooppoo ddee VVaarriiáávveeiiss

uma introdução ao escopo de variáveis. O escopo é o conjunto de regras que determinam o uso e a validade de variáveis nas diversas partes do programa.

1133..1133..11.. VVaarriiáávveeiiss llooccaaiiss Como já foi visto em capítulos anteriores, as variáveis locais só têm validade dentro

do bloco no qual são declaradas. Pode-se declarar variáveis dentro de qualquer bloco. Um bloco começa quando se abre uma chave e termina quando se fecha. Um comando for pode ter variáveis locais e que não serão conhecidas no bloco definido para a instrução. A declaração de variáveis locais é a primeira coisa que se deve colocar num bloco. Pode-se ter tantos blocos quanto forem necessários, com uma variável local chamada x, por exemplo, e esta não apresentará conflitos de compilação. 19 Não é permitido executar aritmética de ponteiros para ponteiros para funções.

Page 254: Tratado Da Linguagem c

A S F U N Ç Õ E S

232232232232

13131313

A seguir pode-se observar um exemplo de uso de variáveis locais: func1 (...) int abc,x; ... func (...) int abc; ... void main () int a,x,y; for (...) float a,b,c; ... ...

Código 13-9

No programa acima existem três funções. As variáveis locais de cada uma delas não irão interferir com as variáveis locais de outras funções. Assim, a variável abc de func1() não tem relação (e pode ser tratada independentemente) com a variável abc de func2(). A variável x de func1() é também completamente independente da variável x de main(). As variáveis a, b e c são locais ao bloco for. Isto quer dizer que só são conhecidas dentro deste bloco for e são desconhecidas no resto da função main(). Quando usarmos a variável a dentro do bloco for estaremos usando a variável a local ao for e não a variável a da função main().

1133..1133..22.. PPaarrââmmeettrrooss ffoorrmmaaiiss Estes parâmetros são declarados como sendo as entradas de uma função. Os

parâmetros formais são variáveis locais da função. O valor de um parâmetro formal também pode ser alterado, pois esta alteração não terá efeito na variável que foi passada à função. Isto tem sentido, pois quando são passados os parâmetros para uma função, são passadas apenas cópias das variáveis. Isto é, os parâmetros formais existem independentemente das variáveis que foram passadas para a função. Eles tomam apenas uma cópia dos valores passados para a função.

1133..1133..33.. VVaarriiáávveeiiss gglloobbaaiiss Variáveis globais são declaradas, fora de todas as funções do programa. Elas são

conhecidas e podem ser alteradas por todas as funções do programa dentro do mesmo arquivo. Quando uma função tem uma variável local com o mesmo nome de uma variável global a função dará preferência à variável local. Observar o seguinte exemplo: int z,k; func1 (...) int x,y; ... func2 (...) int x,y,z; ... z=10; ... main () int count; ...

Page 255: Tratado Da Linguagem c

A S F U N Ç Õ E S

233233233233

13131313

No exemplo acima as variáveis z e k são globais. Observar que func2() tem uma variável local chamada z. Quando temos então, em func2(), o comando z=10 quem recebe o valor de 10 é a variável local, não afetando o valor da variável global z.

Deve ser evitado o uso de variáveis globais, já que estas ocupam memória durante o tempo da execução do programa (as variáveis locais só ocupam memória enquanto estão no escopo de execução) e tornam o programa mais difícil de ser depurado e deixando-o menos estruturado e modular.

1133..1144.. PPaassssaaggeemm ddee PPaarrââmmeettrrooss ppoorr VVaalloorr ee RReeffeerrêênncciiaa

Na linguagem C, quando uma função é chamada, os seus parâmetros formais copiam os valores dos parâmetros que foram passados. Isto quer dizer que não serão alterados os valores que os parâmetros têm fora da função. Este tipo de chamada de função é denominado chamada por valor. Isto ocorre porque são passados para a função apenas os valores dos parâmetros e não os próprios parâmetros. Observar o exemplo a seguir: #include <stdio.h> float sqr (float num); void main () float num,sq; printf ("Entre com um numero: "); scanf ("%f",&num); sq = sqr(num); printf ("\n\nO numero original e: %f\n",num); printf ("O seu quadrado vale: %f\n",sq); float sqr (float num) num = num*num; return num;

Código 13-10

No exemplo acima o parâmetro formal num da função sqr() sofre alterações dentro da função, mas a variável num da função main() permanece inalterada: é uma chamada por valor.

Outro tipo de passagem de parâmetros para uma função ocorre quando alterações nos parâmetros formais, dentro da função, alteram os valores dos parâmetros que foram passados para a função. Este tipo de chamada de função tem o nome de "chamada por referência". Este nome vem do fato de que, neste tipo de chamada, não se passam para a função os valores das variáveis, mas sim suas referências ou endereços (a função usa as referências para alterar os valores das variáveis fora da função).

A linguagem C somente efetua chamadas por valor. Entretanto, para efetuar a passagem de parâmetros por referência podem ser usados ponteiros. Os ponteiros são a "referência" necessária para alterar uma variável fora da função. Na chamada, a referência da variável deve ser passada como sendo o seu endereço. O parâmetro que receberá a cópia do endereço deverá ser um ponteiro, para poder com este, acessar o dado. Observar o seguinte exemplo: #include <stdio.h> void Swap (int *a,int *b); void main (void) int num1,num2; num1 = 100; num2 = 200;

Page 256: Tratado Da Linguagem c

A S F U N Ç Õ E S

234234234234

13131313

Swap (&num1,&num2); printf ("\n\nAgora valem %d %d\n",num1,num2); void Swap (int *a,int *b) int temp; temp = *a; *a = *b; *b = temp;

No exemplo foi passado para a função Swap o endereço das variáveis num1 e num2. Estes endereços serão copiados nos ponteiros a e b. Através do operador * pode-se acessar o conteúdo apontado pelos ponteiros e portanto estes conteúdos podem ser modificados. Os conteúdos destas posições são os valores armazenados em num1 e num2.

1133..1155.. VVeettoorreess ccoommoo AArrgguummeennttooss ddee FFuunnççõõeess

Para passar um vetor como argumento de uma função, pode-se declarar a função de três maneiras equivalentes. Seja o vetor: int matrx [50];

e precisa-se passá-lo como argumento de uma função func(), pode-se declarar func() das três maneiras seguintes: void func (int matrx[50]); void func (int matrx[]); void func (int *matrx);

Nos três casos, teremos como parâmetro de func() um int* (ponteiro para inteiros) chamado matrx. Ao se passar um vetor para uma função, na realidade se passa a informação de um ponteiro. Neste ponteiro é armazenado o endereço do primeiro elemento do vetor. Isto significa que não é feita uma cópia, elemento a elemento do vetor. Assim será possível alterar o valor dos elementos do vetor dentro da função.

1133..1166.. AArrqquuiivvooss ddee CCaabbeeççaallhhoo

Os arquivos de cabeçalho (ou header files) são aqueles que serão incluídos no início dos programas, onde for indicado com a diretiva #include. Usualmente utilizam a extensão .h ou .c. Alguns exemplos são os arquivos STDIO.H, CONIO.H e STRING.H da biblioteca de funções fornecida junto com o compilador. Estes arquivos, não possuem a definição das funções, mas somente a declaração dos protótipos de funções contidas na biblioteca. O compilador lerá estes protótipos e, baseado nas informações contidas, no caso de funções da biblioteca, gerará o código correto. O corpo das funções cujos protótipos estão nos arquivos de cabeçalho, no caso das funções da biblioteca, já estão compilados e normalmente são incluídos no programa durante o processo de conexão através do linker. Este é o instante em que todas as referências a funções cujos códigos não estão nos arquivos fontes serão resolvidas, efetuando a procura destes códigos nos arquivos das bibliotecas do compilador.

Podem-se criar algumas funções para aproveitá-las em programas futuros, ou módulos de programas, e escrever arquivos de cabeçalhos próprios. Em geral, qualquer tipo de arquivo, com qualquer extensão, poderá ser inserido como arquivo de cabeçalho, e estes poderão também conter instruções.

Page 257: Tratado Da Linguagem c

A S F U N Ç Õ E S

235235235235

13131313

Supor que a função int EPar(int a), do exemplo visto em seções anteriores, possa ser reaproveitada por outros programas, pode-se declará-la num módulo separado. Pode ser criado um arquivo de cabeçalho chamado, por exemplo, de funcao.h, assim pode-se ter a seguinte declaração: /* arquivo funcao.h */ int EPar(int a);

O código da função poderá ser escrito num arquivo separado. Por exemplo, funcao.c. Neste arquivo será colocada a definição da função: /* arquivo funcao.c */ int EPar (int a) if (a%2) /* Verifica se a e divisivel por dois */ return 0; else return 1;

Finalmente, no arquivo do programa principal, chamando-o de princip.c, por exemplo: /* arquivo princip.c */ #include <stdio.h> #include "funcao.h" /* inclusão do arquivo funcao.h */ void main () int num; printf ("Entre com numero: "); scanf ("%d",&num); if (EPar(num)) printf ("\n\nO numero e par.\n"); else printf ("\n\nO numero e impar.\n");

Código 13-11

Agora para compilar o arquivo princip.c deverá ser efetuada a conexão com o arquivo funcao.c. Esta operação dependerá do compilador. Usualmente são criados projetos com vários arquivos fonte, sendo que o compilador os conecta automaticamente. A forma mais primitiva de efetuar esta tarefa é usando uma linha de comando, que dependerá do compilador. Se for usado um compilador para linux “gcc princip.c funcao.c -o saida”, onde 'saida' seria o arquivo executável gerado.

EExxeerrccíícciiooss

1. Observe um programa equivalente que calcula o fatorial de um número inteiro n (n!). O programa não consegue calcular o fatorial de números grandes, nem negativos. Corrigir o programa para evitar estes erros. Utilizar variáveis de ponto flutuante.

#include <stdio.h> int fat(int n) if (n) return n*fat(n-1); else return 1; int main() int n; printf("\n\nDigite um valor para n: "); scanf("%d", &n); printf("\nO fatorial de %d e' %d", n, fat(n)); return 0;

Page 258: Tratado Da Linguagem c

A S F U N Ç Õ E S

236236236236

13131313

2. Implementar a função EDivisivel(int a, int b) (tomar como base EPar(int a)). A função deverá retornar 1 se o resto da divisão de a por b for zero. Caso contrário, a função deverá retornar zero.

3. Quando trabalhamos com sistemas de potência é de fundamental importância o conhecimento das grandezas denominadas Potência Ativa, Potência Reativa e Potência Aparente. A potência Ativa é a potência real transferida a uma carga. A potência reativa é uma potência de transferência vai e volta, mas que não é transformada. A potência aparente é a soma vetorial entre a potência ativa e reativa e podem ser representadas pelo diagrama vetorial apresentado a seguir.

O co-seno do ângulo φ é chamado Fator de Potência. Sabendo-se que :

2

Re2

ativaAtivaAparente PotPotPot +=

Pot.Ativa = Pot.Aparente * cos(φ) Pot.Reativa = Pot.Aparente * sen(φ)

= −

AtivaPot

ativaPot

.

Re.tan 1φ

A potência aparente pode ser representada por um número complexo, com a parte real representando a potência ativa, e a parte imaginária a reativa. Criar as seguintes estruturas:

struct COMPLEXO double real; double imaginaria; ; struct POTENCIA double dPotAparente; double dFatorPotencia; ;

4. Faça um programa que peça via teclado o valor de n valores de potências ativa e reativa. O programa deverá calcular e armazenar na memória os valores calculados da potência aparente e do fator de potência. Utilizar: vetores, estruturas, ponteiros, alocação dinâmica de memória. Criar as funções:

double Calcula_FP(struct COMPLEXO *) double Calcula_Potencia_Aparente(struct COMPLEXO *)

5. Ambas recebem como parâmetro uma estrutura com os valores representando a parte ativa e a reativa, retornando o valor do fator de potência e da potência aparente, respectivamente. Implementar:

o fluxograma da solução. o código fonte. os comentários no programa. Obs.: Funções predefinidas: exp(double x), log(double x), cos(double x), sin(double x), atan(double x), pow(double x, double y), sqrt(double x) definidas em MATH.H. Todas retornam uma variável do tipo double.

6. Divida o programa que calcula a potência aparente e o fator de potência, em dois programas diferentes. Um programa principal e um programa fonte secundário, onde serão colocadas as definições das funções.

7. Analisar o seguinte programa e indicar o valor de cada variável sempre que solicitado:

#include <stdio.h> int num; int func(int a, int b) a = (a+b)/2; /* Qual e o valor de a apos a atribuicao? */

Page 259: Tratado Da Linguagem c

A S F U N Ç Õ E S

237237237237

13131313

num -= a; return a; main() int first = 0, sec = 50; num = 10; num += func(first, sec); /* Qual e o valor de num, first e sec */ /* antes e depois da atribuicao? */ printf("\n\nConfira! num = %d\tfirst = %d\tsec = %d",num, first, sec);

8. Escreva uma função que receba os endereços de duas variáveis inteiras as coloque em zero.

9. Escreva um programa que leia um vetor de inteiros pelo teclado e o apresente na tela. Crie uma função (void levetor(int *vet, int dimensao)) para fazer a leitura do vetor.

AAvvaalliiaaççããoo

Responda as perguntas abaixo, escolhendo a alternativa adequada para cada questão.

1. Qual a afirmativa verdadeira?

a. Você pode retornar para um programa quantas variáveis de uma função desejar através do comando return

b. Uma função só pode ter um comando return c. Os protótipos de função servem para declarar as funções, isto é, indicar para o

compilador qual o seu nome, tipo de retorno e o número e tipos dos parâmetros

d. Uma função não pode retornar um ponteiro e. Nenhuma das opções anteriores

2. Qual das seguintes razões não é uma razão válida para o uso de funções em C?

a. Funções usam menos memória do que se repetirmos o mesmo código várias vezes

b. Funções rodam mais rápido

c. Funções fornecem um meio de esconder cálculos em uma "caixa preta" que pode ser usada sem a preocupação de detalhes internos de implementação

d. Funções mantêm variáveis protegidas das outras partes do programa

3. Qual a afirmativa falsa?

a. Se uma função não retorna nada ela deve ser declarada como void b. O retorno da função main é feito para o sistema operacional c. stdio.h e string.h contêm o protótipo de algumas funções da biblioteca do C d. Funções podem ser definidas dentro de outras funções e. Uma das opções anteriores é falsa

4. Qual a afirmativa verdadeira?

a. stdio.h e string.h contêm o corpo de algumas funções da biblioteca do C b. Funções podem ser chamadas por outras funções c. Em um programa C todas as funções de um programa devem estar em um único arquivo .c d. Variáveis declaradas em uma função são acessíveis por todas as outras funções e. Nenhuma das opções anteriores

5. Qual a afirmativa verdadeira?

a. A palavra reservada auto é utilizada para dizer que uma variável é local (automática). Porém, ela pode ser omitida dentro de uma função, pois todas as variáveis são locais por default.

b. Não se pode utilizar variáveis com o mesmo nome em funções diferentes.

c. Os parâmetros recebidos por uma função têm o mesmo endereço das variáveis usadas na chamada à função

d. Quando uma variável local tem o mesmo nome de uma variável global, a variável local se torna inacessível e a variável global é acessível

e. Nenhuma das opções anteriores

Page 260: Tratado Da Linguagem c

A S F U N Ç Õ E S

238238238238

13131313

6. Qual a afirmativa falsa?

a. Os parâmetros recebidos por uma função armazenam cópias das variáveis usadas na chamada da função

b. Variáveis globais são conhecidas e podem ser alteradas por todas as funções do programa

c. Quando queremos alterar as variáveis que são passadas como parâmetros para uma função, devemos declará-las como ponteiros na função

d. A função scanf necessita receber como parâmetro o endereço da variável de entrada, porque ela precisa alterar esta variável.

e. Uma das opções anteriores é falsa

7. O que imprime o programa abaixo?

#include <stdio.h> void func(); int i = 10; void main() int i=20; func(); printf("i= %d ", i); int i = 30; func(); printf("i= %d ", i); void func() printf("i = %d ", i);

a. i= 20 i= 20 i= 30 i= 30 b. i= 10 i= 20 i=10 i= 30 c. i= 20 i=10 i=10 i=30 d. i= 10 i=10 i=10 i=10 e. Nenhuma das opções anteriores

8. Ao se utilizar um vetor como parâmetro para uma função que informação está sendo passada à função?

a. Uma cópia de todos elementos do vetor b. Uma cópia do primeiro elemento do vetor c. O endereço do primeiro elemento do vetor d. O endereço de todos os elementos do vetor e. Nenhuma das opções anteriores

9. Sejam par1, par2 e par3 variáveis inteiras. Se chamarmos uma função pela instrução:

func(&par1,&par2,&par3);

10. Para que servem &par1, &par2 e &par3 ?

a. São valores inteiros passados para a função b. Servem para armazenar os endereços da função e das funções que chamaram c. São os endereços das variáveis da função que chamou. Nestes endereços iremos

armazenar os valores a serem modificados pela função d. Armazenam os endereços das funções de biblioteca usados na função e. Nenhuma das opções anteriores

11. O que imprime o programa a seguir?

#include <stdio.h> func(int *a, int b) int temp; temp = *a; *a = b; b = temp; void main() int a = 10, b = 20; func(&a, b); printf("a = %d, b = %d", a, b);

a. a = 10, b = 20 b. a = 20, b = 10 c. a = 10, b = 10

Page 261: Tratado Da Linguagem c

A S F U N Ç Õ E S

239239239239

13131313

d. a = 20, b = 20 e. Nenhuma das opções anteriores

12. Qual expressão não é válida para:

int i=10, j=20; int *pti, *ptj; pti = &i; ptj = &j;

a. j = pti == ptj; b. i = pti-ptj; c. pti += ptj; d. pti++; e. i = pti || ptj;

13. Seja a declaração:

int matr[][4] = 1,2,3,4,5,6,7,8,9,10,11,12

14. Qual afirmativa é falsa?

a. **matr é igual a 1 b. *(*(matr+1)+2) é igual a 7 c. *(matr[2]+3) é igual a 12 d. (*(matr+2))[2] é igual a 11 e. *((*matr)+1) é igual a 5

Page 262: Tratado Da Linguagem c

240240240240

1144.. DDIIRREETTIIVVAASS DDEE CCOOMMPPIILLAAÇÇÃÃOO O pré-processador C é um programa que examina o programa fonte escrito em C,

que efetua modificações no mesmo, baseado nas Diretivas de Compilação. As diretivas de compilação são comandos que não são compilados, sendo dirigidos ao pré-processador, que são executados pelo compilador antes da execução do processo de compilação propriamente dito.

Portanto, o pré-processador modifica o programa fonte, entregando para o compilador um programa modificado. Todas as diretivas de compilação são iniciadas pelo caractere #. As diretivas podem ser colocadas em qualquer parte do programa. Já vimos, e usamos muito, a diretiva #include. Sabemos que ela não gera código mas instrui ao compilador que ele deve incluir um arquivo externo na hora da compilação. As diretivas do C são identificadas por começarem por #. As diretivas que estudaremos são definidas pelo padrão ANSI:

#if #ifdef #ifndef #else #elif #endif #include #define #undef

Tabela 14-1 – Diretivas de Compilação

1144..11.. AA DDiirreettiivvaa ##iinncclluuddee

A diretiva #include já foi usada durante o nosso curso diversas vezes. Ela diz ao compilador para incluir, na hora da compilação, um arquivo especificado. Sua forma geral é: #include "nome_do_arquivo" ou #include <nome_do_arquivo>

A diferença entre se usar " " e < > é somente a ordem de procura nos diretórios pelo arquivo especificado. Se você quiser informar o nome do arquivo com o caminho completo, ou se o arquivo estiver no diretório de trabalho, use " ". Se o arquivo estiver nos caminhos de procura pré-especificados do compilador, isto é, se ele for um arquivo do próprio sistema (como é o caso de arquivos como stdio.h, string.h, etc..) use < >.

Observe que não há ponto e vírgula após a diretiva de compilação. Esta é uma característica importante de todas as diretivas de compilação e não somente da diretiva #include

1144..22.. AAss DDiirreettiivvaass ##ddeeffiinnee ee ##uunnddeeff

A diretiva #define tem a seguinte forma geral:

#define nome_da_macro sequência_de_caracteres

Capítulo

14

Page 263: Tratado Da Linguagem c

D I R E T I V A S D E C O M P I L A Ç Ã O

241241241241

14141414 Quando for utilizada esta diretiva, está instruindo-se ao compilador para que, toda

vez que ele encontrar o nome_da_macro no programa a ser compilado, ele deve substituí-lo pela sequência_de_caracteres fornecida. Isto é muito útil para deixar o programa mais geral. Veja um exemplo: #include <stdio.h> #define PI 3.1416 #define VERSAO "2.02" void main () printf ("Programa versao %s",VERSAO); printf ("O numero pi vale: %f",PI);

Código 14-1

Se quisermos mudar o nosso valor de PI, ou da VERSAO, no programa acima, basta mexer no início do programa. Isto torna o programa mais flexível. Há quem diga que, em um programa, nunca se deve usar constantes como 10, 3.1416, etc., pois estes são números que ninguém sabe o que significam (muitas pessoas os chamam de "números mágicos"). Ao invés disto, deve-se usar apenas #defines. É uma convenção de programação (que deve ser seguida, pois torna o programa mais legível) na linguagem C que as macros declaradas em #defines devem ser todas em maiúsculas.

Um outro uso da diretiva #define é o de simplesmente definir uma macro1. Neste caso usa-se a seguinte forma geral:

#define nome_da_macro

Neste caso o objetivo não é usar a macro no programa (pois ela seria substituída por nada), mas, sim, definir uma macro para ser usada como uma espécie de flag. Isto quer dizer que estamos definindo um valor como sendo "verdadeiro" para depois podermos testá-lo.

Também é possível definir macros com argumentos. Veja o exemplo a seguir: #define max(A,B) ((A>B) ? (A):(B)) #define min(A,B) ((A<B) ? (A):(B)) ... x = max(i,j); y = min(t,r);

Embora pareça uma chamada de função, o uso de max (ou min) simplesmente substitui, em tempo de compilação, o código especificado. Cada ocorrência de um parâmetro formal (A ou B, na definição) será substituída pelo argumento real correspondente. Assim, a linha de código: x = max(i,j);

será substituída pela linha: x = ((i)>(j) ? (i):(j));

A linha de código: x = max(p+q,r+s);

será substituída pela linha: x = ((p+q)>(r+s) ? (p+q):(r+s));

Isto pode ser muito útil. Verifique que as macros max e min não possuem especificação de tipo. Logo, elas trabalham corretamente para qualquer tipo de dado,

1 Macro: Seqüência de comandos ou funções que podem ser executados como programas.

Page 264: Tratado Da Linguagem c

D I R E T I V A S D E C O M P I L A Ç Ã O

242242242242

14141414 enquanto os argumentos passados forem coerentes. Mas isto pode trazer também algumas armadilhas. Veja que a linha x = max(p++,r++);

será substituída pelo código x = ((p++)>(r++) ? (p++):(r++));

e em conseqüência, incrementará o maior valor duas vezes.

Outra armadilha em macros está relacionada com o uso de parênteses. Seja a macro: #define SQR(X) X*X

Imagine que você utilize esta macro na expressão abaixo: y = SQR(A+B);

Ao fazer isto, a substituição que será efetuada não estará correta. A expressão gerada será: y = A+B*A+B;

que obviamente é diferente de (A+B)*(A+B) !

A solução para este problema é incluir parênteses na definição da macro: #define SQR(X)(X)*(X)

Quando você utiliza a diretiva #define nunca deve haver espaços em branco no identificador. Por exemplo, a macro: #define PRINT (i) printf(" %d \n", i)

não funcionará corretamente porque existe um espaço em branco entre PRINT e (i). Ao se tirar o espaço, a macro funcionará corretamente e poderá ser utilizada para imprimir o número inteiro i, saltando em seguida para a próxima linha.

A diretiva #undef tem a seguinte forma geral:

#undef nome_da_macro

Ela faz com que a macro que a segue seja apagada da tabela interna que guarda as macros. O compilador passa a partir deste ponto a não conhecer mais esta macro.

1144..33.. AAss DDiirreettiivvaass ##iiffddeeff ee ##eennddiiff

Nesta seção, e até mais a frente, veremos as diretivas de compilação condicional. Elas são muito parecidas com os comandos de execução condicional do C. As duas primeiras diretivas são as #ifdef e #endif. Suas formas gerais são:

#ifdef nome_da_macro sequência_de_declarações #endif

A seqüência de declarações será compilada apenas se o nome da macro estiver definido. A diretiva de compilação #endif é útil para definir o fim de uma seqüência de declarações para todas as diretivas de compilação condicional. #define PORT_0 0x378 ...

Page 265: Tratado Da Linguagem c

D I R E T I V A S D E C O M P I L A Ç Ã O

243243243243

14141414 /* Linhas de codigo qualquer... */ ... #ifdef PORT_0 #define PORTA PORT_0 #include "../sys/port.h" #endif

As linhas acima demonstram como estas diretivas podem ser utilizadas. Caso PORT_0 tenha sido previamente definido, a macro PORTA é definida e o header file port.h é incluído.

1144..44.. AA DDiirreettiivvaa ##iiffnnddeeff

A diretiva #ifndef funciona ao contrário da diretiva #ifdef. Sua forma geral é:

#ifndef nome_da_macro sequência_de_declarações #endif

A seqüência de declarações será compilada se o nome da macro não tiver sido definido.

1144..55.. AA DDiirreettiivvaa ##iiff

A diretiva #if tem a seguinte forma geral:

#if expressão_constante sequência_de_declarações #endif

A seqüência de declarações será compilada se a expressão-constante for verdadeira. É muito importante ressaltar que a expressão fornecida deve ser constante, ou seja, não deve ter nenhuma variável.

1144..66.. AA DDiirreettiivvaa ##eellssee

A diretiva #else tem a seguinte forma geral:

#if expressão_constante sequência_de_declarações #else sequência_de_declarações #endif

Ela funciona como seu correspondente, o comando else.

Imaginar que esteja trabalhando num sistema, e deseja que todo o código possa ser compilado em duas diferentes plataformas (i.e. Unix e Dos). Para obter isto, você "encapsula" toda a parte de entrada e saída em arquivos separados, que serão carregados de acordo com o header file carregado. Isto pode ser facilmente implementado da seguinte forma: #define SISTEMA DOS

Page 266: Tratado Da Linguagem c

D I R E T I V A S D E C O M P I L A Ç Ã O

244244244244

14141414 ... /*linhas de codigo..*/ ... #if SISTEMA == DOS #define CABECALHO "dos_io.h" #else #define CABECALHO "unix_io.h" #endif #include CABECALHO

1144..77.. AA DDiirreettiivvaa ##eelliiff

A diretiva #elif serve para implementar a estrutura if-else-if. Sua forma geral é:

#if expressão_constante_1 sequência_de_declarações_1 #elif expressão_constante_2 sequência_de_declarações_2 #elif expressão_constante_3 sequência_de_declarações_3 . . . #elif expressão_constante_n sequência_de_declarações_n #endif

O funcionamento desta estrutura é idêntico ao funcionamento apresentado anteriormente.

EEXXEERRCCÍÍCCIIOOSS

1. Escreva uma macro que retorne 1 se o seu argumento for um número ímpar e 0 se for um número par.

Page 267: Tratado Da Linguagem c

245245245245

1155.. EENNTTRRAADDAASS EE SSAAÍÍDDAASS –– II//OO O sistema de entrada e saída da linguagem C está estruturado na forma de uma

biblioteca de funções. Já foram apresentadas algumas destas funções, e agora elas serão revistas com mais detalhes. Novas funções também serão apresentadas.

Não é objetivo deste curso explicar, em detalhes, todas as possíveis funções da biblioteca de entradas e saídas da linguagem C. A sintaxe completa destas funções pode ser encontrada no manual do seu compilador e nos arquivos de ajuda.

Um conceito importante da linguagem C é o conceito de fluxo. Seja qual for o dispositivo de entrada e saída (discos, terminais, teclados, acionadores de fitas) que estiver sendo manipulado, a linguagem C vai tratá-lo como um fluxo de dados, que nada mais é que um dispositivo lógico de entrada ou saída. Todos os fluxos são similares em seu funcionamento e independentes do dispositivo ao qual estão associados. Assim, as mesmas funções que descrevem o acesso aos discos podem ser utilizadas para se acessar um terminal de vídeo. Todas as operações de entrada e saída são realizadas por meio de fluxos.

Na linguagem C, um arquivo é entendido como um conceito que pode ser aplicado a arquivos em disco, terminais, modems, etc. Um fluxo é associado a um arquivo através da realização de uma operação de abertura. Uma vez aberto, informações podem ser trocadas entre o arquivo e o programa. Um arquivo é dissociado de um fluxo através de uma operação de fechamento de arquivo.

1155..11.. LLeennddoo ee EEssccrreevveennddoo CCaarraacctteerreess getche e getch putchar

Uma das funções básicas de um sistema é a entrada e saída de informações em dispositivos. Estes podem ser: um monitor, uma impressora ou um arquivo em disco. A seguir, será visto algumas funções que a biblioteca padrão oferece para o tratamento de troca de dados.

1155..11..11.. ggeettcchhee ee ggeettcchh As funções getch() e getche() não são definidas pelo padrão ANSI. Porém, elas

geralmente são incluídas em compiladores baseados no DOS e em cross-compilers para microcontroladores, e se encontram declaradas no arquivo de cabeçalho conio.h.

Protótipos: int getch (void); int getche (void);

Capítulo

15

Page 268: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

246246246246

15151515 A função getch() espera que o usuário digite uma tecla e retorna este caractere

(armazenado no buffer de teclado), ou espera a chegada de dados num canal serial, em compiladores para microcontroladores (Observar que os teclados são dispositivos seriais). A função getche() funciona exatamente como getch(). A diferença é que getche() gera um "eco" na tela antes de retornar a tecla. No caso de cross-compilers, vai depender da implementação.

Se a tecla pressionada (ou valor inserido pelo receptor do canal serial) for um caractere especial, estas funções retornam geralmente zero. Neste caso pode-se usar as funções novamente para capturar o código da tecla estendida pressionada.

A função equivalente a getche() no padrão ANSI é a getchar(). O inconveniente da função getchar() é que o caractere lido é colocado em uma área intermediária até a chegada do caractere ENTER, o que pode ser extremamente inconveniente em ambientes interativos.

1155..11..22.. ppuuttcchhaarr Protótipo:

int putchar (int c);

A função putchar() envia um caractere para a memória de vídeo, ou para um dispositivo serial. O caractere será colocado na posição atual do cursor, no caso da memória de vídeo. Esta função é usualmente declarada em stdio.h.

1155..22.. LLeeiittuurraa ee EEssccrriittaa ddee SSttrriinnggss gets puts

1155..22..11.. ggeettss Protótipo:

char *gets (char *s);

A função força a espera do ingresso de uma string, que será armazenada numa área de memória apontada pelo ponteiro (constante ou não) s. O ponteiro que a função retorna é o próprio s. A função gets não é uma função segura, já que não há verificação da quantidade de posições. A verificação destas deve ser feita pelo programador.

Usualmente esta função é concluída na entrada de um caractere null ou de um CR (‘\r’), ou ainda um LF (‘\n’) ou uma combinação entre estes dois últimos. Observar o exemplo a seguir. #include <stdio.h> void main() char buffer[10]; printf("Entre com a sua senha"); gets(buffer); printf("A sua senha é: %s", buffer);

Código 15-1

Se o usuário digitar como entrada:

Page 269: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

247247247247

15151515 x26wydw3as1970

ou seja, um total de 15 caracteres: 14 posições (incluindo o '\0' ) serão utilizadas para armazenar a string. Como a string buffer[ ] só tem 10 posições reservadas, os 5 caracteres adicionais serão colocados na área de memória subseqüente à reservada para a string, escrevendo numa região de memória imprópria. Este efeito é conhecido como "estouro de buffer" e pode causar problemas imprevisíveis. Uma forma de se evitar este problema é usar a função fgets, ou alocação dinâmica de memória, como será visto na seção 15.6.2..

1155..22..22.. ppuuttss Protótipo:

int puts (char *s);

A função puts() coloca uma string s na memória de vídeo ou num canal serial.

1155..33.. EEnnttrraaddaa ee SSaaííddaa FFoorrmmaattaaddaa printf scanf

As funções que resumem todas as funções de entrada e saída formatada, em ASCII, são as funções printf() e scanf().

1155..33..11.. pprriinnttff Protótipo:

int printf (char *str,...);

As reticências no protótipo da função indicam que esta função tem um número de argumentos variável. Este número está diretamente relacionado com a string de controle str, que deve ser fornecida como primeiro argumento. A string de controle tem dois componentes. O primeiro componente está constituído dos caracteres a serem impressos na tela. O segundo componente são os comandos de formato. Como já foi visto, os últimos determinam uma exibição de variáveis na saída. Os comandos de formato são precedidos de % e indicam como será feita a conversão de valores para caracteres representados em ASCII. A cada comando de formato deve corresponder um argumento na função printf(). Se isto não ocorrer podem acontecer erros imprevisíveis no programa.

Na Tabela 15-1 são apresentados os códigos de formato1. Código Formato %c Um caractere (char) %d Um número inteiro decimal (int) %i O mesmo que %d %e Número em notação científica com o "e" minúsculo %E Número em notação científica com o "e" maiúsculo %f Ponto flutuante decimal %g Escolhe automaticamente o melhor entre %f e %e %G Escolhe automaticamente o melhor entre %f e %E %o Número octal %s String %u Decimal "unsigned" (sem sinal)

1 Os códigos de formato podem depender da implementação de cada compilador.

Page 270: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

248248248248

15151515 %x Hexadecimal com letras minúsculas %X Hexadecimal com letras maiúsculas %% Imprime um % %p Ponteiro %ld Long %lf Double %ul Unsigned long

Tabela 15-1 - Tabela de códigos de formatos

Vamos ver alguns exemplos: Código Saída printf ("Um %%%c %s",'c',"char"); Um %c char printf ("%X %f %e",107,49.67,49.67); 6B 49.67 4.967e1 printf ("%d %o",10,10); 10 12

Tabela 15-2 -Exemplos da formatos da função printf

É possível também indicar o tamanho do campo, justificação e o número de casas decimais. Para isto usam-se códigos colocados entre o símbolo ‘%’ e a letra que indica o tipo de formato.

Um inteiro indica o tamanho mínimo, em caracteres, que deve ser reservado para a saída. Se for colocado o símbolo %5d indica-se que o campo terá cinco caracteres de comprimento no mínimo. Se o inteiro precisar de mais de cinco caracteres para ser exibido então o campo terá o comprimento necessário para exibi-lo. Se o comprimento do inteiro for menor que cinco então o campo terá cinco de comprimento e será preenchido com espaços em branco. Se for necessário o preenchimento com zeros, pode-se colocar um zero antes do número. Temos então que %05d reservará cinco casas para o número e se este for menor então se fará o preenchimento com zeros.

O alinhamento padrão é à direita. Para se alinhar um número à esquerda pode ser usado um sinal ‘-‘ antes do número de casas. Então %-5d será um inteiro com o número mínimo de cinco casas, só que justificado a esquerda.

Pode-se indicar o número de casas decimais de um número de ponto flutuante. Por exemplo, a notação %10.4f indica um ponto flutuante de comprimento total dez e com 4 casas decimais. Entretanto, esta mesma notação, quando aplicada a tipos como inteiros e strings indica o número mínimo e máximo de casas. Então %5.8d é um inteiro com comprimento mínimo de cinco e máximo de oito caracteres.

Observar alguns exemplos: Código Saída printf ("%-5.2f",456.671); | 456.67| printf ("%5.2f",2.671); | 2.67| printf ("%-10s","Res"); |Res |

Tabela 15-3 - Exemplos da função printf

Nos exemplos o "pipe" ( | ) indica o início e o fim do campo mas não são escritos na tela, servindo somente como indicação de justificação.

1155..33..22.. ssccaannff Protótipo:

int scanf (char *str,...);

Page 271: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

249249249249

15151515 A string de controle str determina, assim como na função printf(), quantos

parâmetros a função vai necessitar. Deve-se lembrar que a função scanf() deve receber endereços de memória como parâmetros. Isto significa que as variáveis que não forem por natureza ponteiros, devem ser passadas precedidas do operador &. Os especificadores de formato de entrada são propositadamente parecidos com os da função printf():

Código Formato %c Um único caractere (char) %d Um número decimal (int) %i Um número inteiro %e Um ponto flutuante %f Um ponto flutuante %h Inteiro curto %o Número octal %s String %x Número hexadecimal %p Ponteiro %ld long int %lf double %ul Unsigned long

Tabela 15-4 - Especificadores de formato de entrada

Os caracteres de conversão d, i, u e x podem ser precedidos por h para indicarem que um apontador para short ao invés de int aparece na lista de argumento, ou pela letra l para indicar que um apontador para long aparece na lista de argumentos. Analogamente, os caracteres de conversão e, f e g podem ser precedidos por l para indicarem um apontador para double ao invés de float está na lista de argumentos.

1155..44.. OOppeerraannddoo ccoomm AArrqquuiivvooss fopen fclose exit

O sistema de entrada e saída do ANSI C é composto de uma série de funções, cujos protótipos estão reunidos em stdio.h . Estas funções trabalham com o conceito de "ponteiro para arquivo". Este não é um tipo propriamente dito, mas uma definição usando o comando typedef. Esta definição também está no arquivo stdio.h. Pode-se declarar um ponteiro de arquivo da seguinte maneira: FILE *p;

O ponteiro p será então um ponteiro para um arquivo. Usando este tipo de ponteiro podem-se manipular arquivos na linguagem C.

1155..44..11.. ffooppeenn É uma função de abertura de arquivo. O seu protótipo é:

FILE *fopen (char *nome_do_arquivo,char *modo);

O nome_do_arquivo determina qual arquivo deverá ser aberto e o seu caminho. Este nome deve ser válido no sistema operacional que estiver sendo utilizado. O modo de abertura indica à função fopen() qual o modo de utilização para o arquivo a ser aberto. A abertura correta de um arquivo implica a cópia deste numa área de memória reservada para a operação. A Tabela 15-5 mostra os valores de modo: Modo Significado "r" Abre um arquivo texto para leitura. O arquivo deve existir antes de ser aberto.

Page 272: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

250250250250

15151515 "w" Abrir um arquivo texto para gravação. Se o arquivo não existir, ele será criado. Se já existir, o

conteúdo anterior será destruído.

"a" Abrir um arquivo texto para gravação. Os dados serão adicionados no fim do arquivo ("append"), se ele já existir, ou um novo arquivo será criado, no caso de arquivo não existente anteriormente.

"rb" Abre um arquivo binário para leitura. Igual ao modo "r" anterior, só que o arquivo é binário. "wb" Cria um arquivo binário para escrita, como no modo "w" anterior, só que o arquivo é binário.

"ab" Acrescenta dados binários no fim do arquivo, como no modo "a" anterior, só que o arquivo é binário.

"r+" Abre um arquivo texto para leitura e gravação. O arquivo deve existir e pode ser modificado.

"w+" Cria um arquivo texto para leitura e gravação. Se o arquivo existir, o conteúdo anterior será destruído. Se não existir, será criado.

"a+" Abre um arquivo texto para gravação e leitura. Os dados serão adicionados no fim do arquivo se ele já existir, ou um novo arquivo será criado, no caso de arquivo não existente anteriormente.

"r+b" Abre um arquivo binário para leitura e escrita. O mesmo que "r+" acima, só que o arquivo é binário.

"w+b" Cria um arquivo binário para leitura e escrita. O mesmo que "w+" acima, só que o arquivo é binário.

"a+b" Acrescenta dados ou cria um arquivo binário para leitura e escrita. O mesmo que "a+" acima, só que o arquivo é binário

Tabela 15-5 -Modos de abertura de arquivos

Para abrir um arquivo binário no modo de escrita, pode-se fazer: FILE *fp; /* Declaração da estrutura fp=fopen ("exemplo.bin","wb"); /* o arquivo se chama exemplo.bin e está localizado no diretório corrente */ if (!fp) printf ("Erro na abertura do arquivo.");

A condição !fp testa se o arquivo foi aberto com sucesso, já que no caso de algum erro, a função fopen() retornará um ponteiro null.

Uma vez aberto um arquivo, este poderá ser lido ou escrito utilizando as funções que serão apresentadas nas seções que seguem.

Toda vez que estamos trabalhando com arquivos, há uma espécie de posição atual no arquivo. Esta é a posição de onde será lido ou escrito o próximo caractere. Normalmente, num acesso seqüencial a um arquivo, não temos que mexer nesta posição pois quando lemos um caractere a posição no arquivo é automaticamente atualizada. Num acesso aleatório teremos que mexer nesta posição (ver fseek()).

1155..44..22.. ffcclloossee Depois de ter operado num arquivo com a função fopen(), este deverá ser fechado

usando a função fclose(): int fclose (FILE *fp);

O ponteiro fp passado à função fclose() determina o arquivo a ser fechado. A função retorna zero no caso de sucesso.

O fechamento de um arquivo faz com que qualquer caracter que tenha permanecido no "buffer" associado ao fluxo de saída seja gravado. Quando dados são enviados para serem gravados em um arquivo, estes dados são armazenados temporariamente em uma área de memória (buffer) em vez de serem escritos em disco imediatamente. Quando o buffer estiver cheio, seu conteúdo é escrito no disco de uma vez. A razão disto é a otimização nas leituras e gravações de arquivos. Se para cada dado a ser gravado, tivesse que ser posicionada a cabeça de gravação em um ponto específico do

Page 273: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

251251251251

15151515 disco, as gravações seriam muito lentas. Assim, as gravações somente serão efetuadas quando houver um volume razoável de informações, ou quando o arquivo for fechado.

A função exit() fecha todos os arquivos que um programa tiver aberto.

1155..44..33.. eexxiitt A função exit() aborta a execução de um programa. O seu protótipo é:

void exit (int codigo_de_retorno);

Esta função está normalmente declarada no arquivo stdlib.h. Pode ser chamada de qualquer ponto no programa e faz com que o programa termine e retorne, para o sistema operacional, o código_de_retorno. A convenção mais usada é que um programa retorne zero no caso de um término normal e retorne um número não nulo no caso de ter ocorrido algum problema. A função exit() se torna importante em casos como alocação dinâmica de memória, e abertura de arquivos pois nestes casos, se o programa não conseguir a memória necessária ou abrir o arquivo, a melhor saída pode ser terminar a execução do programa. Podería-se rescrever o exemplo da seção anterior usando agora a função exit() para garantir que o programa não deixará de abrir o arquivo caso aconteça algum tipo de erro. #include <stdio.h> #include <stdlib.h> /* Para a função exit() */ void main (void) FILE *fp; fp=fopen ("exemplo.bin","wb"); if (!fp) printf ("Erro na abertura do arquivo. Fim de programa."); exit (1);

Código 15-2

1155..55.. LLeennddoo ee EEssccrreevveennddoo CCaarraacctteerreess eemm AArrqquuiivvooss putc getc feof

1155..55..11.. ppuuttcc(()) A função putc() coloca um caractere num arquivo. Seu protótipo é:

int putc (int ch,FILE *fp);

O programa a seguir lê uma string do teclado e escreve-a, caractere por caractere num arquivo no disco (o arquivo arquivo.txt, que será aberto no diretório corrente). A função retorna o valor do caractere enviado em caso de sucesso. No caso de algum erro, retorna o caractere EOF (-1). #include <stdio.h> #include <stdlib.h> void main() FILE *fp; char string[100]; int i; fp = fopen("arquivo.txt","w"); /* Arquivo ASCII, para escrita */ if(!fp) printf( "Erro na abertura do arquivo"); exit(-1); printf("Entre com a string a ser gravada no arquivo:");

Page 274: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

252525252222

15151515 gets(string); for(i=0; string[i]; i++) putc(string[i], fp); /* Grava a string, caractere a caractere */ fclose(fp);

Código 15-3

Depois de executar este programa, verificar o conteúdo do arquivo arquivo.txt usando qualquer programa editor ou visualizador de textos.

1155..55..22.. ggeettcc Retorna um caractere lido de um arquivo especificado. O protótipo é:

int getc (FILE *fp);

1155..55..33.. ffeeooff O valor EOF ("End of file") indica o fim de um arquivo. Às vezes, é necessário

verificar se um arquivo chegou ao fim. Para isto pode-se usar a função feof(). Esta retorna não-zero se o arquivo chegou ao EOF, caso contrário retorna zero. Seu protótipo é: int feof (FILE *fp);

Outra forma de verificar se o final do arquivo foi atingido é comparando o caractere lido por getc() com EOF. O programa a seguir abre um arquivo já existente e lê o seu conteúdo, caractere a caractere, até que o final do arquivo seja atingido. Os caracteres lidos são apresentados na tela: #include <stdio.h> #include <stdlib.h> void main() FILE *fp; char c; fp = fopen("arquivo.txt","r"); /* Arquivo ASCII, para leitura */ if(!fp) printf( "Erro na abertura do arquivo"); exit(-1); while((c = getc(fp) ) != EOF) /* Enquanto não chegar ao final do arquivo */ printf("%c", c); /* imprime o caracter lido */ fclose(fp);

Código 15-4

A seguir é apresentado um programa no qual são realizadas várias operações com arquivos usando as funções vistas neste capítulo. Primeiro é aberto um arquivo para a escrita, e colocam-se informações nele. Em seguida, o arquivo é fechado e novamente aberto para a leitura. Verificar o exemplo. #include <stdio.h> #include <stdlib.h> #include <string.h> void main() FILE *p; char c, str[30], frase[80] = "Este e um arquivo chamado: "; int i; /* Le um nome para o arquivo a ser aberto: */ printf("\n\n Entre com um nome para o arquivo:\n"); gets(str); if (!(p = fopen(str,"w"))) /* Caso ocorra algum erro na abertura do arquivo o programa aborta automaticamente */ printf("Erro! Impossivel abrir o arquivo!\n"); exit(1);

Page 275: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

253253253253

15151515 /* Se nao houve erro, imprime no arquivo e o fecha ...*/ strcat(frase, str); for (i=0; frase[i]; i++) putc(frase[i],p); fclose(p); /* Abre novamente para leitura */ p = fopen(str,"r"); c = getc(p); /* Le o primeiro caracter */ while (!feof(p)) /* Enquanto não se chegar no final do arquivo */ printf("%c",c); /* Imprime o caracter na tela */ c = getc(p); /* Le um novo caracter no arquivo */ fclose(p); /* Fecha o arquivo */

Código 15-5

1155..66.. OOuuttrrooss CCoommaannddooss ddee AAcceessssoo aa AArrqquuiivvooss fgets fputs ferror e perror fread fwrite fseek rewind remove

1155..66..11.. AArrqquuiivvooss PPrreeddeeffiinniiddooss Quando começa a execução de um programa, o sistema automaticamente abre

alguns arquivos predefinidos: stdin: dispositivo de entrada padrão (geralmente o teclado) stdout: dispositivo de saída padrão (geralmente o vídeo) stderr: dispositivo de saída de erro padrão (geralmente o vídeo) stdaux: dispositivo de saída auxiliar (em muitos sistemas, associado à porta serial) stdprn : dispositivo de impressão padrão (em muitos sistemas, associado à porta paralela)

Cada uma destas constantes pode ser utilizada como um ponteiro para FILE, para acessar os periféricos associados a eles. Desta maneira, pode-se, por exemplo, usar: ch = getc(stdin);

para efetuar a leitura de um caractere a partir do teclado, ou : putc(ch, stdout);

para imprimi-lo na tela.

1155..66..22.. ffggeettss Para ler uma string de um arquivo, pode-se usar a função fgets() cujo protótipo é:

char *fgets (char *str, int size,FILE *fp);

A função recebe três argumentos: a string aonde será armazenada a string lida, o número máximo de caracteres a serem lidos e o ponteiro para FILE, que está associado ao arquivo de onde a string será lida. A função lê a string até que um caractere de nova linha seja lido ou (size-1) caracteres tenham sido lidos. Se o caractere de nova linha ('\n') for lido,

Page 276: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

254254254254

15151515 ele fará parte da string, o que não acontece com a função gets(). A string resultante sempre terminará com '\0'.

A função fgets() é semelhante à função gets(), porém, além de poder efetuar a leitura a partir de um arquivo de dados e incluir o caractere de nova linha na string, ela ainda especifica o tamanho máximo da string de entrada. Como foi visto, a função gets() não tinha este controle, o que poderia acarretar erros como o estouro de buffer. Portanto, levando em conta que o ponteiro fp pode ser substituído por stdin, como vimos acima, uma alternativa ao uso de gets é usar a seguinte construção: fgets (str, size, stdin);

onde str é a string que se está lendo, e size deve ser igual ao tamanho alocado para a string subtraído de 1, por causa do '\0'.

1155..66..33.. ffppuuttss Esta função escreve uma string num arquivo. O seu protótipo é:

char *fputs (char *str,FILE *fp);

1155..66..44.. ffeerrrroorr ee ppeerrrroorr Protótipo de ferror:

int ferror (FILE *fp);

A função retorna zero, se nenhum erro ocorreu e um número diferente de zero se algum erro ocorreu durante o acesso ao arquivo.

A função ferror() se torna muito útil quando é necessário verificar se o acesso a um arquivo teve sucesso, de modo que se consiga garantir a integridade dos dados. Na maioria dos casos, se um arquivo pode ser aberto, ele pode ser lido ou gravado. Porém, existem situações em que isto não ocorre. Por exemplo, pode acabar o espaço em disco enquanto acontece a gravação, ou o disco pode estar com problemas e não é possível ler novamente, etc.

Uma função que pode ser usada em conjunto com ferror() é a função perror() (print error), cujo argumento é uma string que indicará em que parte do programa o problema ocorreu.

No exemplo a seguir, mostra-se o uso das funções ferror, perror e fputs #include <stdio.h> #include <stdlib.h> #include <string.h> void main() FILE *pf; char string[100]; if((pf = fopen("arquivo.txt","w")) == NULL) printf("\nNao é possível abrir o arquivo ! "); exit(1); do printf("\nDigite uma nova string. Para terminar, digite <enter>: "); gets(string); fputs(string, pf); putc('\n', pf); if(ferror(pf)) perror("Erro na gravacao"); fclose(pf); exit(1);

Page 277: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

255255255255

15151515 while (strlen(string) > 0); fclose(pf);

Código 15-6

1155..66..55.. ffrreeaadd Pode-se escrever e ler blocos de dados. Para tanto, existem as funções fread() e

fwrite(). O protótipo de fread() é: unsigned fread (void *buffer, int size, int n, FILE *fp);

O buffer é a região de memória na qual serão armazenados os dados lidos; o size é o tamanho de cada unidade a ser lida; e n indica quantas unidades deverão ser lidas. Isto significa que o número total de bytes lidos é:

size * n

A função retorna o número de unidades efetivamente lidas. Este número pode ser menor que n, quando o fim do arquivo for encontrado, ou ocorrer algum erro.

Quando o arquivo for aberto para dados binários, fread() pode ler qualquer tipo de dados.

1155..66..66.. ffwwrriittee A função fwrite() funciona como a fread(), porém escrevendo no arquivo. Seu

protótipo é: unsigned fwrite(void *buffer,int size,int n,FILE *fp);

A função retorna o número de itens escritos. Este valor será igual a n a menos que ocorra algum erro.

O exemplo a seguir ilustra o uso de fwrite e fread para gravar e posteriormente ler uma variável float em um arquivo binário. #include <stdio.h> #include <stdlib.h> void main() FILE *pf; float pi = 3.1415; float pi_file; if((pf = fopen("arquivo.bin", "wb")) == NULL) /* Abre arquivo binário para escrita */ printf("Erro na abertura do arquivo"); exit(1); if(fwrite(&pi, sizeof(float), 1,pf) != 1) /* Escreve a variável pi */ printf("Erro na escrita do arquivo"); fclose(pf); /* Fecha o arquivo */ if((pf = fopen("arquivo.bin", "rb")) == NULL) /* Abre o arquivo novamente para leitura */ printf("Erro na abertura do arquivo"); exit(1); if(fread(&pi_file, sizeof(float), 1,pf) != 1) /* Le em pi_file o valor da variável armazenada anteriormente */ printf("Erro na leitura do arquivo");

Page 278: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

256256256256

15151515 printf("\nO valor de PI, extraído do arquivo e': %f", pi_file); fclose(pf);

Código 15-7

Note-se o uso do operador sizeof, que retorna o tamanho em bytes da variável ou do tipo de dado.

1155..66..77.. ffsseeeekk Para efetuar procuras e acessos aleatórios em arquivos, pode-se utilizar a função

fseek(). Esta função move a posição corrente de leitura ou escrita no arquivo de um valor especificado, a partir de um ponto previamente definido. Seu protótipo é: int fseek (FILE *fp,long offset,int origem);

O parâmetro origem determina a partir de onde, o offset de movimentação serão contados como posições. Os valores possíveis são definidos por macros no arquivo stdio.h e são:

Nome Valor Significado SEEK_SET 0 Início do arquivo SEEK_CUR 1 Ponto corrente no arquivo SEEK_END 2 Fim do arquivo

Tendo-se definido a partir de onde irá se contar, offset determina quantos bytes de deslocamento serão dados na posição atual.

1155..66..88.. rreewwiinndd A função rewind() retorna a posição corrente do arquivo, para o início.

void rewind (FILE *fp);

é equivalente a fseek(stream, 0L, SEEK_SET);

1155..66..99.. rreemmoovvee Apaga um arquivo especificado.

int remove (char *nome_do_arquivo);

Observar o funcionamento do seguinte programa: #include <stdio.h> #include <string.h> #include <stdlib.h> void main() FILE *p; char str[30], frase[] = "Este e um arquivo chamado: ", resposta[80]; int i; /* Le um nome para o arquivo a ser aberto: */ printf("\n\n Entre com um nome para o arquivo:\n"); fgets(str,29,stdin); /* Usa fgets como se fosse gets */ for(i=0; str[i]; i++) if(str[i]=='\n') str[i]=0;/* Elimina o \n da string lida */ if (!(p = fopen(str,"w")))/* Caso ocorra algum erro na abertura do arquivo..*/ /* o programa aborta automaticamente */ printf("Erro! Impossivel abrir o arquivo!\n"); exit(1);

Page 279: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

257257257257

15151515 /* Se nao houve erro, imprime no arquivo, e o fecha ...*/ fputs(frase, p); fputs(str,p); fclose(p); /* abre novamente e le */ p = fopen(str,"r"); fgets(resposta, 79, p); printf("\n\n%s\n", resposta); fclose(p); /* Fecha o arquivo */ remove(str); /* Apaga o arquivo */

Código 15-8

1155..77.. FFlluuxxooss PPaaddrroonniizzaaddooss fprintf fscanf

Os fluxos padronizados para arquivos, permitem ao programador ler e escrever em arquivos, da forma padrão.

1155..77..11.. ffpprriinnttff A função fprintf() funciona como a função printf(). A diferença é que a saída de

fprintf() é um arquivo e não a tela do computador. O protótipo é: int fprintf (FILE *fp,char *str,...);

A diferença dos protótipos das funções fprintf() e printf(), é a especificação do arquivo destino através do ponteiro de arquivo.

1155..77..22.. ffssccaannff A função fscanf() funciona como a função scanf(). A diferença é que fscanf() lê

de um arquivo e não do teclado do computador. O seu protótipo é: int fscanf (FILE *fp,char *str,...);

A diferença dos protótipos das funções fscanf() e scanf(), é a especificação do arquivo destino através do ponteiro de arquivo.

Observar o programa a seguir. #include <stdio.h> #include <stdlib.h> void main() FILE *p; char str[80],c; /* Le um nome para o arquivo a ser aberto: */ printf("\n\n Entre com um nome para o arquivo:\n"); gets(str); if (!(p = fopen(str,"w"))) /* Caso ocorra algum erro na abertura do arquivo..*/ /* o programa aborta automaticamente */ printf("Erro! Impossivel abrir o arquivo!\n"); exit(1); /* Se nao houve erro, imprime no arquivo, fecha ...*/ fprintf(p,"Este e um arquivo chamado:\n%s\n", str); fclose(p); /* abre novamente para a leitura */ p = fopen(str,"r"); while (!feof(p))

Page 280: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

258258258258

15151515 fscanf(p,"%c",&c); printf("%c",c); fclose(p);

Código 15-9

1155..88.. FFuunnççõõeess ddee II//OO -- PPoorrttaass

As funções de I/O permitem a leitura e escrita nas portas de entrada e saída de hardware. As rotinas de I/O não são compatíveis entre compiladores diferentes.

Estas rotinas são úteis, quando se tenta enviar ou receber dados pela porta paralela ou pelo canal serial. Também servem como meio de programação dos chips periféricos dos computadores. Como exemplo, serão colocadas algumas funções de I/O que serão utilizadas nos capítulos que tratam de interrupções, programação das portas paralelas e seriais.

1155..88..11.. __iinnpp ((MMiiccrroossoofftt CC ee VViissuuaall CC++++)) Lê um byte de uma porta de hardware. A função está declarada no arquivo

CONIO.H. A sintaxe é a seguinte:

int _inp( unsigned short port );

O parâmetro port é o endereço da porta a ser lida. O valor de retorno é o valor lido da porta.

Existem outras funções de leitura, tais como _inpw e _inpd que lêem uma word e uma double word respectivamente, a partir de uma porta de entrada especifica. O valor de entrada é do tipo unsigned short int, com valores entre 0 e 65535.

1155..88..22.. __oouutt ((MMiiccrroossoofftt CC ee VViissuuaall CC++++)) Possibilita a saída de um byte, através de uma porta de hardware. A função está

declarada no arquivo CONIO.H. A sintaxe é como segue.

int _outp( unsigned short port, int databyte );

O parâmetro port é o endereço da porta de hardware, e o databyte é o valor a ser copiado para a porta.

O valor de retorno é o próprio valor copiado para a porta.

1155..88..33.. iinnppoorrttbb ((BBoorrllaanndd CC++++)) Lê um byte de uma porta de hardware. A função está declarada no arquivo

DOS.H. A implementação é na forma de macro, e o seu código expandido como inline. A sintaxe é a seguinte:

unsigned char inportb (unsigned portid);

Page 281: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

259259259259

15151515 O parâmetro portid é o endereço da porta a ser lida. O valor de retorno é o valor

lido da porta, com comprimento de 1 byte.

Existe outra função de leitura, inport(), que funciona como a instrução assembly 80x86 IN.

A seguir é mostrado um programa que lê um byte da porta serial 1. #include <stdio.h> #include <dos.h> int main(void) unsigned char result; int port = 0; /* porta serial 1 */ result = inportb(port); printf("Byte lido da porta %d = 0x%X\n", port, result); return 0;

Código 15-10

1155..88..44.. oouuttppoorrttbb ((BBoorrllaanndd CC++++)) Permite escrever dados numa porta de hardware. A função está declarada no

arquivo DOS.H. A implementação é na forma de macro, e o seu código expandido como inline. A sintaxe é a seguinte:

void outportb(unsigned portid, unsigned char value);

O parâmetro portid é o endereço da porta a ser lida.O parâmetro value é o valor unsigned char a ser copiado para a porta respectiva. A função não retorna valor.

Existe outra função de leitura, outport(), que funciona como a instrução assembly 80x86 OUT.

A seguir é mostrado um escreve um caractere na porta com endereço 0. #include <stdio.h> #include <dos.h> int main(void) int port = 0; char value = 'C'; outportb(port, value); printf("Valor %c enviado para a porta número %d\n", value, port); return 0;

Código 15-11

EEXXEERRCCÍÍCCIIOOSS

1. Escreva um programa que implemente uma lista de pontos da função )602(2127)( tsintf π= para um intervalo de 1 segundo, com uma simulação de

amostragem a cada 100 µs. Armazene estes dados num arquivo ASCII com nome dado pelo usuário e no seguinte formato: 0.0000 0.000000000 0.0001 6.769349872 0.0002 13.52908001

Page 282: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

260260260260

15151515 ... ...

2. Idem a especificação do programa anterior, com gravação em binário, no lugar de representação em ASCII. Compare o tamanho do arquivo resultante em relação ao do exercício anterior.

3. Escreva um programa que leia nomes pelo teclado e os imprima na tela. Use as funções puts e gets para a leitura e impressão na tela.

4. Escreva um programa que leia (via teclado) e apresente uma matriz 4x4 na tela. Utilize os códigos de formato vistos neste capítulo para que a matriz se apresente corretamente indentada. Altere os tipos de dados da matriz (int, float, double) e verifique a formatação correta para a indentação. Verifique também a leitura e impressão de números hexadecimais.

5. Escreva um programa que abra um arquivo texto e conte o número de caracteres presentes nele. Imprima o número de caracteres na tela.

AAuuttoo--aavvaalliiaaççããoo

Responda as perguntas abaixo, escolhendo a alternativa adequada para cada questão.

1. Alguns compiladores indicam que a função gets(string) é uma função que deve ser utilizada com cuidado. Qual a razão disto?

a. A função não pode ler inteiros b. Se string for um vetor de caracteres com poucas posições, o usuário pode

digitar mais caracteres do que a string pode armazenar, causando sérios problemas

c. O usuário pode digitar algo que não seja caracter e o sistema não perceber d. A função não trabalha bem em conjunto com a função scanf e. Nenhuma das opções anteriores

2. O que não se pode especificar com a função fopen?

a. O nome do arquivo a ser aberto; b. Que arquivo será aberto, para acrescentar dados c. Que arquivo será aberto, para trabalhar em modo texto d. Qual é o ponteiro para o arquivo e. Nenhuma das opções anteriores

3. Como todos arquivos precisam ser lidos seqüencialmente, não há como ler dados no meio do arquivo, sem ter feito a leitura desde o seu início.

a. Verdadeiro b. Falso c. Depende do tipo de arquivo d. Depende do sistema operacional e. Nenhuma das opções anteriores

4. Seja a variável double f; qual seria a forma de se efetuar a leitura de f

a. scanf("%f", &f); b. scanf("%6f", &f); c. scanf("%lf", &f); d. scanf("%10.6f", &f); e. Nenhuma das opções anteriores

5. O comando printf("%d, %u, %p", i, i, &i); imprimirá:

a. O endereço de i e o valor de i; b. O valor de i, de i sem sinal e um ponteiro para i; c. O valor de i, o valor de i e um ponteiro para i d. O valor de i, o valor de i sem sinal e o endereço de i e. Nenhuma das opções anteriores

Page 283: Tratado Da Linguagem c

E N T R A D A S E S A Í D A S P A D R O N I Z A D A S - A R Q U I V O S

261261261261

15151515 6. Para ler um caractere por vez de um arquivo, qual das seguintes funções você usaria?

a. getch(); b. getche(); c. getc(); d. fgets(); e. fread();

7. Seja o seguinte trecho de programa

FILE *fp; fp = fopen("arquivo.txt", "r+");

8. O que está sendo feito?

a. O arquivo arquivo.txt está sendo aberto em modo texto e somente pode ser lido.

b. O arquivo arquivo.txt está sendo fechado para escrita

c. O arquivo arquivo.txt está sendo aberto em modo binário, e pode ser lido ou escrito

d. O arquivo arquivo.txt está sendo aberto em modo binário e somente pode ser lido

e. O arquivo arquivo.txt está sendo aberto em modo texto e pode ser lido ou escrito

9. Qual a afirmativa errada?

a. A função fgets() lê uma string de um arquivo, mas também pode ser utilizada para substituir gets() com segurança

b. fputs() escreve uma string em um arquivo c. fputs(string, stdprn) imprimirá a string na impressora padrão d. fseek() pode ser utilizada para procurar um determinado caractere em um

arquivo e. Nenhuma das opções anteriores

10. Para gravar várias variáveis de tipos diferentes em um arquivo texto, a função mais apropriada é::

a. fwrite() b. fprintf() c. fputs() d. fscanf() e. Nenhuma das opções anteriores

11. Seja o seguinte programa:

#include <stdio.h> void main() FILE *fp; fp = fopen("arquivo.txt", "r"); fprintf(fp, "%d", 10); fclose(fp);

12. Qual afirmativa é verdadeira?

a. Este código compila sem erros b. Ao final da execução, o arquivo arquivo.txt conterá uma linha com o número 10

impresso nela. c. A linha #include <stdio.h> não é necessária neste código d. Não se deve usar return 0; neste código, pois a função main não e' int e. Nenhuma das opções anteriores

Page 284: Tratado Da Linguagem c

262262262262

1166.. TTIIPPOOSS DDEE DDAADDOOSS EESSPPEECCIIAAIISS Estruturas Matrizes de estruturas Ponteiros Uniões Enumerações O comando sizeof O comando typedef

1166..11.. EEssttrruuttuurraass ((ssttrruucctt))

Uma estrutura agrupa várias variáveis numa só. Uma struct pode ser utilizada para agrupar um conjunto de características não similares que descrevem um objeto abstrato, formando um novo tipo de dados.

Para se criar uma estrutura deve ser usado o comando struct. A forma geral é:

struct nome_do_tipo_da_estrutura tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; variáveis_estrutura;

O nome_do_tipo_da_estrutura é o nome para a estrutura. As variáveis_estrutura são opcionais e seriam nomes de variáveis declaradas pelo programador, e que seriam do tipo nome_do_tipo_da_estrutura. Observar o exemplo a seguir. struct est int i; float f; a, b;

Neste caso, est é uma estrutura com dois campos (também conhecidoos como atributos ou membros da estrutura), i e f. Foram também declaradas duas variáveis, a e b que são do tipo da estrutura, isto é, a possui os campos i e f, o mesmo acontecendo com b.

Observar a criação da estrutura CanalSerial: struct CanalSerial char label [50]; int baudrate; int paridade; int dados; int stop; int start; int valor; COM1,COM2;

Capítulo

ErErErErCapítulo

16

Page 285: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

263263263263

16161616

No exemplo, CanalSerial é um modelo de estrutura, e as variáveis COM1 e COM2 são variáveis do tipo struct CanalSerial. Observar o seguinte exemplo. #include <stdio.h> #include <string.h> struct CanalSerial char label [50]; int baudrate; int paridade; int dados; int stop; int start; int valor; ; main (void) struct CanalSerial COM1; strcpy (COM1.label,"COM1"); COM1.baudrate = 9600; COM1.paridade = 1; COM1.dados = 8; COM1.stop = 1; COM1.start = 1; COM1.valor = 0xBB;

Código 16-1

O programa declara uma COM1 ficha do CanalSerial e preenche os seus atributos. O exemplo mostra a forma de acessar um elemento de uma estrutura: basta usar o ponto (.). Assim, para acessar o campo baudrate de COM1, pode ser escrito: COM1.baudrate = 9600;

Pode-se atribuir duas estruturas sempre que estas sejam do mesmo tipo. Neste caso, serão copiados os campos um a um. Observar o seguinte programa. #include<stdio.h> struct est1 int i; float f; ; void main() struct est1 primeira, segunda; /* Declara primeira e segunda como structs do tipo est1 */ primeira.i = 10; primeira.f = 3.1415; segunda = primeira; /* A segunda struct e' agora igual a primeira */ printf(" Os valores armazenados na segunda struct são : %d e %f ",segunda.i, segunda.f);

Código 16-2

Aqui foram declaradas duas estruturas do tipo est1, uma chamada primeira e outra chamada segunda. Atribuem-se valores aos dois campos da struct primeira. Os valores de primeira são copiados na segunda apenas com o operador de atribuição: segunda = primeira;

Todos os campos de primeira serão copiados na segunda. Porém, devemos tomar cuidado na atribuição de structs que contenham campos ponteiros. Observar o programa a seguir. #include <stdio.h> #include <string.h> #include <stdlib.h> struct componente char *part_number; /* A struct possui um campo que é um ponteiro */ int valor; ;

Page 286: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

264264264264

16161616

void main() struct componente comp1,comp2; char buffer[50]; printf("\nEntre o Part Number:"); gets(buffer); /* Le o Part Number em uma string de buffer */ comp1.part_number = (char *) malloc((strlen(buffer)+1)*sizeof(char)); /* Aloca a quantidade de memoria suficiente para armazenar a string */ strcpy(comp1.part_number, buffer); /* Copia a string */ printf("\nEntre o valor:"); scanf("%d", &comp1.valor); comp2 = comp1; /* ERRADO comp2.part_number e comp2.part_number estao apontando para a mesma regiao de memoria */ printf("Depois da atribuicao:\n Part Number em comp1 %s %d \n Part Number em” “comp2 %s %d", comp1.part_number,comp1.valor,comp2.part_number, comp2.valor); strcpy(comp2.part_number, "DS232C"); /* Uma modificacao na memoria apontada por comp2.part_number causara' a modificacao do que e' apontado por comp1.part_number, o que, no está correto */ comp2.valor = 1100; /* Nesta atribuicao nao ha problemas */ printf(" \n\nApos modificar o Part Number em comp2:\n Part Number em comp1” “ %s %d \n Part Number em comp2 %s %d", comp1.part_number, comp1.valor, comp2.part_number, comp2.valor);

Código 16-3

Neste programa há um erro lógico, pois ao se fazer a atribuição comp2 = comp1, o campo part_number de comp2 estará apontando para a mesma posição de memória que o campo part_number de comp1. Desta forma, ao modificar o conteúdo apontado por comp2.part_number estará modificando o conteúdo apontado por comp1.part_number.

1166..11..11.. PPaassssaannddoo ppaarraa FFuunnççõõeess As estruturas podem também ser parâmetros de funções. Deve-se observar que,

como em qualquer outra função em C, a passagem da estrutura é feita por valor. A estrutura que está sendo passada, vai ser copiada, campo por campo através da pilha, em uma variável local da função. Isto significa que alterações na estrutura dentro da função não terão efeito na variável fora da função. Para estruturas grandes, deve ser preferida a passagem de valores através de ponteiros para a estrutura.

1166..11..22.. PPoonntteeiirrooss Pode-se ter um ponteiro para uma estrutura. Um ponteiro para uma struct pode

ser declarado como: struct componente *p;

Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados. Para usá-lo, podem ser consideradas duas possibilidades. A primeira é apontando para uma variável struct já existente, da seguinte forma: struct componente comp1; struct componente *p; p = &comp1;

A segunda é alocando memória para comp1 usando, por exemplo, a função malloc(): #include <stdlib.h> #include <stdio.h> #include <string.h>

Page 287: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

265265265265

16161616

struct componente char *part_number; /* A struct possui um campo que é um ponteiro */ int valor; ; void main() struct componente *p; int a = 10; /* Alocação dinâmica de 10 componentes */ p = (struct componente *) malloc (a * sizeof(struct componente)); p[0].valor = 3266969; /* Exemplo de acesso ao campo valor do primeiro componente apontada por p */ free(p);

Código 16-4

Se o ponteiro p apontar para uma estrutura qualquer (como por exemplo p = &comp1;) e for necessário acessar um elemento da estrutura: (*p).valor = 10;

Os parênteses são necessários, porque o operador ‘.’ tem precedência maior que o operador ‘*’ . Porém, este formato não é muito usado. O que é comum é utilizar o operador valor através do operador seta, que é formado por um sinal “menos" (‘-‘) seguido por um sinal "maior que" (‘>’), isto é: ‘->’ . Assim: p->nome = 10;

1166..22.. UUnniioonn

Uma declaração union determina uma única localização de memória onde podem estar armazenadas várias variáveis diferentes. A declaração de uma union é semelhante à declaração de uma estrutura:

union nome_do_tipo_da_union tipo_1 nome_1; tipo_2 nome_2; ... tipo_n nome_n; variáveis_union;

Como exemplo, considerar a seguinte union: union angulo float graus; float radianos; ;

Nesta, existem duas variáveis (graus e radianos) que, apesar de terem nomes diferentes, ocupam o mesmo local da memória. Isto quer dizer que somente será usado o espaço de memória equivalente a um único float. As unions podem ser feitas também com variáveis de diferentes tipos. Neste caso, a memória alocada corresponde ao tamanho da maior variável da union. Observar o exemplo: a seguir. #include <stdio.h> #define GRAUS 'G' #define RAD 'R' union angulo int graus; float radianos; ; void main() union angulo ang; char op; printf("\nNumeros em graus ou radianos? (G/R):"); scanf("%c",&op);

Page 288: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

266266266266

16161616

if (op == GRAUS) ang.graus = 180; printf("\nAngulo: %d\n",ang.graus); else if (op == RAD) ang.radianos = 3.1415; printf("\nAngulo: %f\n",ang.radianos); else printf("\nEntrada invalida!!\n");

Código 16-5

Alguns cuidados devem ser tomados quando se trabalha com unions. Por exemplo, observar o programa a seguir. #include <stdio.h> union numero char Ch; int I; float F; ; void main (void) union numero N; N.I = 123; printf ("%f",N.F); /* Vai imprimir algo que nao e' necessariamente 123 ...*/

Código 16-6

O programa acima mostra a leitura de uma região da memória, que foi gravada como um inteiro, como se fosse um ponto flutuante. O resultado pode não ser o correto.

1166..33.. EEnnuummeerraaççõõeess ((eennuumm))

Numa enumeração pode-se indicar ao compilador quais os valores que uma determinada variável pode assumir. Sua forma geral é:

enum nome_do_tipo_da_enumeração lista_de_valores lista_de_variáveis;

Considerar o seguinte exemplo: enum dias_da_semana segunda, terca, quarta, quinta, sexta, sabado, domingo;

O programador indica ao compilador que qualquer variável do tipo dias_da_semana somente poderá ter os valores enumerados. Observar o seguinte programa: #include <stdio.h> enum dias_da_semana segunda, terca, quarta, quinta, sexta, sabado, domingo; void main (void) enum dias_da_semana d1,d2; d1 = segunda; d2 = sexta; if (d1==d2) printf ("O dia e o mesmo."); else printf ("São dias diferentes.");

Código 16-7

Page 289: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

267267267267

16161616

O compilador gerará um valor inteiro seqüencial a cada membro da union. Assim, ao primeiro da lista, é associado o número zero, o segundo ao número 1 e assim por diante. As variáveis declaradas são variáveis do tipo int.

1166..44.. OO CCoommaannddoo ssiizzeeooff

O operador sizeof é usado para se calcular o tamanho de variáveis ou de tipos. Ele retorna o tamanho do tipo ou variável, em bytes. Deve-se usá-lo para garantir portabilidade. Por exemplo, o tamanho de um inteiro pode depender do sistema para o qual se está compilando. O sizeof é um operador porque ele é substituído pelo tamanho do tipo ou variável no momento da compilação. Este comando não é uma função de runtime. O sizeof admite duas formas:

sizeof nome_da_variável

e

sizeof (nome_do_tipo)

Se for necessário conhecer o tamanho de um float pode-se utilizar: sizeof(float). Se for declarada uma variável f como float e for necessário conhecer o seu tamanho pode ser fazer diretamente: sizeof f.

O operador sizeof também funciona com estruturas, uniões e enumerações. Outra aplicação importante do operador sizeof é para se conhecer o tamanho de tipos definidos pelo usuário. Seria, por exemplo, uma tarefa um tanto complicada a de alocar a memória para um ponteiro para a estrutura componente, mostrada nos exemplos anteriores, se não fosse o uso de sizeof. Observar o exemplo a seguir. #include <stdio.h> struct tipo_endereco char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; ; struct ficha_pessoal char nome [50]; long int telefone; struct tipo_endereco endereço; ; void main(void) struct ficha_pessoal *ex; ex = (struct ficha_pessoal *) malloc(sizeof(struct ficha_pessoal)); /*... */ free(ex);

Código 16-8

1166..55.. OO CCoommaannddoo ttyyppeeddeeff

O comando typedef permite ao programador definir um novo nome para um determinado tipo. Sua forma geral é:

typedef antigo_nome novo_nome;

Page 290: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

268268268268

16161616

Como exemplo vamos dar o nome de inteiro para o tipo int: typedef int inteiro;

Agora poderá declarar o tipo inteiro. O comando typedef também pode ser utilizado para dar nome a tipos complexos, como as estruturas. As estruturas criadas no exemplo anterior poderiam ser definidas como tipos através do comando typedef. O exemplo ficaria: #include <stdio.h> typedef struct tipo_endereco char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; TEndereco; typedef struct ficha_pessoal char nome [50]; long int telefone; TEndereco endereço; TFicha; void main(void) TFicha *ex; /* ... */

Código 16-9

Observar que não é mais necessária a palavra chave struct para declarar variáveis do tipo ficha_pessoal. Basta agora usar o novo tipo definido TFicha.

1166..66.. LLiissttaass SSiimmpplleessmmeennttee EEnnccaaddeeaaddaass

Várias estruturas de dados complexas podem ser criadas utilizando simultaneamente structs e ponteiros. Uma destas estruturas é a lista encadeada. Uma lista encadeada é uma seqüência de structs, que são os nós da lista, ligados entre si através de ponteiros. Esta seqüência pode ser acessada através de um ponteiro para o primeiro nó, que é a cabeça da lista. Cada nó contém um ponteiro que aponta para a struct que é a sua sucessora na lista. O ponteiro da última struct da lista aponta para NULL, indicando que se chegou ao final da lista.

Esta estrutura de dados é criada dinamicamente na memória (utiliza-se malloc() e free()), de modo que se torna simples introduzir nós nela, retirar nós, ordenar os nós, etc.

Supondo que seja necessária a criação de uma lista encadeada para armazenar os produtos disponíveis em uma empresa. Pode-se criar um nó desta lista usando a seguinte struct: struct Produto int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ struct Produto *proximo; /* Proximo elemento da lista encadeada de Produtos */ ;

Note que esta struct possui, além dos campos de dados codigo e preco, um campo adicional que é um ponteiro para uma struct do tipo Produto. É este campo que será utilizado para apontar para o próximo nó da lista encadeada. O programa a seguir faz uso desta struct, através de um novo tipo criado por um typedef, para criar uma lista de produtos de uma loja: #include <stdio.h> #include <stdlib.h>

Page 291: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

269269269269

16161616

/* Estrutura que será usada para criar os nós da lista */ typedef struct tipo_produto int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ struct tipo_produto *proximo; /*Proximo elemento da lista encadeada de Produtos */ TProduto; /* Prototipos das funcoes para inserir e listar produtos */ void inserir(TProduto **cabeca); void listar (TProduto *cabeca); void main() TProduto *cabeca = NULL; /* Ponteiro para a cabeca da lista */ TProduto *noatual; /* Ponteiro a ser usado para percorrer a lista no momento de desalocar seus elementos*/ char q; /* Caractere para receber a opcao do usuario */ do printf("\n\nOpcoes: \nI -> para inserir novo produto;\nL -> para listar” “os produtos; \nS -> para sair \n:"); scanf("%c", &q); /* Le a opcao do usuario */ switch(q) case 'i': case 'I': inserir(&cabeca); break; case 'l': case 'L': listar(cabeca); break; case 's': case 'S': break; default: printf("\n\n Opcao nao valida"); fflush(stdin); /* Limpa o buffer de entrada */ while ((q != 's') && (q != 'S') ); /* Desaloca a memoria alocada para os elementos da lista */ noatual = cabeca; while (noatual != NULL) cabeca = noatual->proximo; free(noatual); noatual = cabeca; /* Lista todos os elementos presentes na lista encadeada */ void listar (TProduto *noatual) int i=0; while( noatual != NULL) /* Enquanto nao chega no fim da lista */ i++; printf("\n\nProduto numero %d\nCodigo: %d \nPreco:R$%.2lf", i, noatual- >codigo, noatual->preco); noatual = noatual->proximo; /* Faz noatual apontar para o proximo no */ /* Função para inserir um novo no, ao final da lista */ void inserir (TProduto **cabeca) TProduto *noatual, *novono; int cod; double preco; printf("\n Codigo do novo produto: "); scanf("%d", &cod); printf("\n Preco do produto:R$"); scanf("%lf", &preco); if (*cabeca == NULL) /* Se ainda nao existe nenhum produto na lista */ /* cria o no cabeca */ *cabeca = (TProduto *) malloc(sizeof(TProduto)); (*cabeca)->codigo = cod; (*cabeca)->preco = preco; (*cabeca)->proximo = NULL; else /* Se ja existem elementos na lista, deve percorre-la ate' o seu final e inserir o novo elemento */ noatual = *cabeca; while(noatual->proximo != NULL) noatual = noatual->proximo; /* Ao final do while, noatual aponta para o ultimo no */ novono = (TProduto *) malloc(sizeof(TProduto));

Page 292: Tratado Da Linguagem c

T I P O S D E D A D O S E S P E C I A I S

270270270270

16161616

/* Aloca memoria para o novo no */ novono->codigo = cod; novono->preco = preco; novono->proximo = NULL; noatual->proximo = novono; /* Faz o ultimo no apontar para o novo no */

Código 16-10

É interessante notar que, no programa anterior não existe limite para o número de produtos que se vai armazenar na lista. Toda vez que for necessário criar um novo produto, memória para ele será alocada e ele será criado no final da lista. Note que a função inserir recebe o endereço do ponteiro cabeça da lista. Qual a razão disto? A razão é que o endereço para o qual a cabeça da lista aponta poderá ser modificado caso se esteja inserindo o primeiro elemento na lista.

É importante notar que várias outras estruturas de dados complexas podem ser criadas com structs contendo ponteiros que apontam para outras structs.

EEXXEERRCCÍÍCCIIOOSS

1. Crie uma struct para descrever os componentes de hardware de uma placa microprocessada. Os campos devem armazenar o nome do componente, part number, valor e nome do fornecedor. Criar uma lista encadeada com esta struct e escreva um programa que: Insira um novo componente na lista; Leia uma lista de componentes a partir de um arquivo; Grave a lista de componentes para um arquivo; Liste todos os componentes na tela; Crie uma lista de produtos, onde cada um possui uma lista de componentes, com as mesmas

características descritas nos itens anteriores.

2. Escrever um programa fazendo o uso de structs. Deverá ser criada uma struct chamada Ponto, contendo apenas a posição x e y (inteiros) do ponto. Declarar 2 pontos, ler a posição (coordenadas x e y) de cada um e calcular a distância entre eles. Apresentar como resultado final a distância entre os dois pontos.

3. Seja a seguinte struct que é utilizada para descrever os produtos que estão no estoque de uma empresa que produz instrumentos de medida:

struct Produto char nome[30]; /* Nome do produto */ int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ ;

Escreva uma instrução que declare uma matriz de Produto com 10 itens de produtos; Atribua os valores "Voltímetro", 13205 e R$200 aos membros da posição 0 e os valores

"Amperímetro", 15202 e R$210,00 aos membros da posição 1 da matriz anterior; Faça as mudanças que forem necessárias para usar um ponteiro para Produto ao invés de uma

matriz de Produtos. Faça a alocação de memória de forma que se possa armazenar 10 produtos na área de memória apontada por este ponteiro e refaça as atribuições do item anterior b; Escreva as instruções para imprimir os campos que foram atribuídos no item anterior.

Page 293: Tratado Da Linguagem c

271271271271

1177.. IINNTTEERRRRUUPPÇÇÕÕEESS

1177..11.. IInnttrroodduuççããoo

Quando são recebidos dados e houver mudança no status das portas de I/O, existem basicamente dois métodos disponíveis de utilização. Pode-se "perguntar" (Poll) à porta, efetuando uma leitura do status da mesma, em certos intervalos fixos de tempo, para determinar se algum dado tem sido recebido ou se houve alguma mudança em relação ao estado anterior. Caso tenha acontecido alguma alteração, então o programa deve ser desviado para as rotinas de serviço apropriadas.

Como se pode imaginar, o método anterior, chamado de polling, pode consumir muito tempo. Este tempo, pode ser utilizado para efetuar alguma outra tarefa, como por exemplo, repintar a tela do monitor, mostrar as horas num relógio, etc.. Uma melhor alternativa é a de utilizar as interrupções. Neste tipo de método, o processador efetua as suas tarefas comuns, tais como a atualização da tela, atualização do relógio, etc., e quanto acontecer algum evento num dispositivo de I/O, no caso da chegada de um byte, ou a mudança no status da porta, este solicitará a atenção do processador enviando um sinal de requisição de interrupção (Interrupt Request).

Quando o processador receber um sinal de requisição de interrupção, este finaliza a instrução corrente, armazena algumas informações prévias numa área de memória chamada de pilha ou stack para posterior restauração, e executa as rotinas de atendimento a interrupção (Interrupt Service Routine - ISR), que por exemplo, poderá capturar o byte da porta e colocá-lo num buffer. Uma vez acabada a rotina ISR, o processador retorna a execução que tinha sido interrompida, utilizando a informação deixada no stack.

Usando este método, o processador não perde tempo, procurando ver se um dispositivo de I/O precisa da sua atenção, mas no lugar disto, o próprio dispositivo irá solicitar interromper a execução quando for necessário.

1177..22.. IInntteerrrruuppççõõeess ee aa AArrqquuiitteettuurraa IInntteell

As interrupções não precisam ser todas associadas com dispositivos de I/O. A família de microprocessadores 8086, possui 256 interrupções, muitas destas são utilizadas somente para interrupções de software.

A série de microprocessadores 8086 possui uma tabela de vetores de interrupção, denominada Interrupt Vector Table, iniciando no endereço de memória 0000:0000, estendendo-se até 1024 bytes. A posição de tabela de vetor de interrupções, armazena os endereços das rotinas de serviço de interrupção (ISR), cada uma delas com quatro bytes de extensão, dando lugar a 256 vetores de interrupções.

Capítulo

17

Page 294: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

272272272272

17171717

INT (Hex) IRQ Usos mais Comuns

00 - 01 Manipuladores de Exceção (Exception Handlers) 02 IRQ não mascarável Erros de paridade, etc. 03 - 07 Manipuladores de Exceção 08 IRQ0 - Hardware Timer do Sistema 09 IRQ1 - Hardware Teclado 0A IRQ2 - Hardware Redirecionada 0B IRQ3 - Hardware Comunicação Serial COM2/COM4 0C IRQ4 - Hardware Comunicação Serial COM1/COM3 0D IRQ5 - Hardware Reservado/Placa de Som 0E IRQ6 - Hardware Controlador de disco flexível 0F IRQ7 - Hardware Comunicação Paralela 10 - 6F Interrupções de Software 70 IRQ8 - Hardware Relógio de Tempo Real 71 IRQ9 - Hardware IRQ2 redirecionada 72 IRQ10 - Hardware Reservada 73 IRQ11- Hardware Reservada 74 IRQ12 - Hardware Mouse PS/2 75 IRQ13 - Hardware Co-Processador Matemático 76 IRQ14 - Hardware Disco Rígido 77 IRQ15 - Hardware Reservado 78 - 7F Interrupções de Software

Tabela 17-1 – Vetores de Interrupções

Em resumo, existem somente 15 interrupções (IRQs) de hardware e uma interrupção não-mascarável. Os demais vetores de interrupção são utilizados como interrupções de software e por manipuladores de exceções. Os manipuladores de exceções são rotinas parecidas com as ISRs, que podem ser chamadas ou interrompidas quando acontecer tipo de erro. Um exemplo deste é o primeiro vetor de interrupção que armazena o endereço da Divisão por Zero. Quando ocorrer uma divisão por zero, o microprocessador encontra o endereço armazenado na posição 0000:0000 e inicia a execução do código colocado neste endereço.

1177..33.. IInntteerrrruuppççõõeess ddee HHaarrddwwaarree

O Controlador Programável de Interrupções (PIC) manipula as interrupções de hardware. A maioria dos PCs possui dois destes chips, localizados em endereços diferentes. Um destes cuida das IRQs 0 a 7, e o outro, as IRQs 8 a 15, totalizando em 15 linhas de IRQ individuais, sendo o segundo PIC cascateado com o primeiro, utilizando a IRQ2.

A maior parte da inicialização do PIC é feita pela BIOS, de forma a facilitar a utilização, sendo que o programador somente tem que se preocupar com duas instruções iniciais. O PIC possui algumas facilidades implementadas, como o mascaramento de IRQs individuais, de forma a que as requisições específicas não interrompam o processador. A primeira instrução de inicialização a ser feita pelo programador é a inicialização do registrador OCW1 (Operation Control Word) para selecionar quais as IRQs serão mascaradas e quais não.

Como existem dois circuitos PIC localizados em endereços diferentes, primeiro deve-se determinar qual PIC será utilizado. O primeiro, localizado no endereço base 0x20H, controla a IRQ0 a IRQ7. O formato de bits de controle do registrador OCW1 deste PIC é mostrado na tabela que segue.

Bit Desabilita IRQ Função 7 IRQ7 Porta Paralela

Page 295: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

273273273273

17171717

6 IRQ6 Controlador de disco flexível 5 IRQ5 Reservado/Placa de Som 4 IRQ4 Porta Serial 3 IRQ3 Porta Serial 2 IRQ2 PIC2 1 IRQ1 Teclado 0 IRQ0 Timer do Sistema

Tabela 17-2 – Bits de configuração da IRQ0 a IRQ7 (PIC 1 – Endereço 0x21)

Notar que a IRQ2 está conectada ao Segundo PIC (PIC2), de forma a que mascarando este bit, desabilitará as IRQ8 a IRQ15.

O Segundo PIC está localizado no endereço base 0xA0H, controlando as IRQs 8 a 15. A seguir, a tabela mostra os bits de controle necessários para as habilitações independentes.

Bit Desabilita IRQ Função 7 IRQ15 Reservado 6 IRQ14 Disco Rígido 5 IRQ13 Co-Processador Matemático 4 IRQ12 Mouse PS/2 3 IRQ11 Reservado 2 IRQ10 Reservado 1 IRQ9 IRQ2 redirecionada 0 IRQ8 Relógio de Tempo Real

Tabela 17-3 - Bits de configuração da IRQ8 a IRQ15 (PIC 2 – Endereço 0xA1)

Como a tabela acima mostra os bits necessários para desabilitar uma IRQ, no caso de precisar habilita-la, os bits correspondentes deverão ser setados para zero. Por exemplo, se for necessário habilitar a IRQ 3 então deverá ser enviado o byte 0xF7 como OCW1 para o PIC1. Mas o que aconteceria se existir a possibilidade de que uma destas IRQs já esteja habilitada e em uso ?

Neste caso, deve-se proceder primeiramente ao mascaramento, usando uma função AND, para poder modificar somente aquele bit que interessa, deixando os outros na sua condição anterior. Para conhecer a condição anterior, deve-se ler antes o valor do registrador, e logo então proceder a modificação do bit correspondente através da mascara. Por exemplo, pode-se habilitar somente a IRQ3 da seguinte forma: outportb(0x21,(inportb(0x21) & 0xF7);

Lembrar que o registrador OCW1 fica no registrador com endereço Base + 1.

O mesmo procedimento deve ser usado para mascarar (desabilitar) uma IRQ. Embora que neste caso, deverá ser utilizada uma máscara com a função OR, usando o byte 0x08 sobre o conteúdo original do registrador OCW1. Um exemplo disto é mostrado a seguir. outportb(0x21,(inportb(0x21) | 0x08);

Uma outra instrução do PIC que deve ser executada é o Fim da Interrupção (End of Interrupt - EOI). Esta deve ser enviada para o PIC no final da rotina de atendimento a interrupção (ISR), de forma que o PIC possa resetar o seu registrador In Service Register.

Uma EOI pode ser enviada para o PIC1 usando o seguinte código: outportb(0x20,0x20);

ou para o PIC2 outportb(0xA0,0x20);

Page 296: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

274274274274

17171717

1177..44.. IInnssttrruuççõõeess ddee IInntteerrrruuppççããoo ddee HHaarrddwwaarree

As instruções de interrupção não são padronizadas, sendo que cada compilador possui a sua própria implementação. A seguir são comentadas as instruções de interrupção para três compiladores diferentes.

1177..44..11.. BBoorrllaanndd CC++++ -- 8800xx8866 A instrução interrupt define uma função como sendo um manipulador de

interrupção. A sintaxe é mostrada a seguir.

interrupt <definição-da-função> ;

Todos os registradores da CPU são gravados e o código da função é finalizado por uma instrução assembly IRET. Um exemplo de utilização é mostrado a seguir. void interrupt nova_isr()

1177..44..22.. FFrraannkklliinn CC –– 88xx5511 A instrução interrupt define uma função como sendo um manipulador de

interrupção. Este compilador implementa a sintaxe da seguinte forma: void Serial_Interrupt () interrupt 4 ....

A função de atendimento a interrupção foi chamada de Serial_Interrupt, e o número depois da instrução interrupt, identifica qual a interrupção de hardware, que é tabelada de acordo com as especificações estabelecidas no manual.

1177..44..33.. PPCCWW CCCCSS –– MMiiccrroocchhiipp PPIICC Este compilador implementa a definição dos manipuladores de interrupção através

da seguinte sintaxe:

#int_NOME_DA_INT

A isto vai em seguida a definição da função de atendimento a interrupção. A seguir um exemplo de codificação. #int_timer1 timer1_interrupt()

1177..55.. IImmpplleemmeennttaannddoo uummaa RRoottiinnaa ddee AAtteennddiimmeennttoo aa IInntteerrrruuppççããoo ((IISSRR))

Em C pode ser implementada uma ISR da seguinte forma1: void interrupt nova_isr()

onde nova_isr é um ponteiro longo2, apontando para o endereço que a Rotina de Atendimento a Interrupção irá estar colocada na memória. Este endereço será colocado

1 Usar um compilador da Borland.

Page 297: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

275275275275

17171717

depois na Tabela de Vetores de Interrupção de forma que, esta será chamada quando acontecer a interrupção.

O seguinte código mostra a implementação básica de uma ISR. void interrupt nova_isr() /* Rotina de Atendimento a Interrupcao (ISR) */ disable(); /* O corpo da ISR a partir daqui */ oldhandler(); outportb(0x20,0x20); /* Envia EOI para o PIC1 */ enable();

A função void interrupt nova_isr() define a função como sendo uma rotina de atendimento a interrupção. A função disable()3 limpa o flag de interrupção, de forma que nenhuma outra interrupção de hardware, exceto que a NMI (Non-Maskable Interrupt – Interrupção não Mascarável) possa ocorrer. Se não for feito desta forma, uma interrupção com prioridade maior, poderá interromper a execução da ISR. Embora, isto não seja um problema na maioria dos casos, é uma situação que merece ser analisada.

O corpo da ISR deverá incluir o código que se deseja executar quando a requisição de interrupção seja ativada. A maioria das portas e UARTs4 podem interromper o processador por um grande motivo de razões, por exemplo, um byte recebido, timeouts, buffer da FIFO cheio, overruns, etc., de forma que a natureza da interrupção tem que ser determinada. Isto normalmente é feito pela leitura dos registradores de status da porta que está em uso. Uma vez estabelecido a causa da interrupção, pode-se efetuar as tarefas necessárias a esta.

Caso devam ser lidos quaisquer dados de uma porta, é uma prática comum, coloca-los em um buffer, no lugar de escreve-los diretamente na tela do monitor de vídeo, por exemplo, inibindo que outras interrupções posteriores de serem processadas. A maioria das Portas possui buffers FIFO que podem conter mais de um byte, de forma que a rotina de leitura deste buffer deverá ser repetida, até que a FIFO fique vazia, e somente então, finalizar a ISR.

Em alguns casos pode ser apropriado redirecionar o código para a ISR original para execução previa. Um exemplo é a interrupção do Clock (Clock Interrupt – Interrupção do Relógio do Sistema). Alguns programas residentes (TSR) podem precisar utilizar a rotina original de atendimento, de forma que se esta rotina original for interceptada e capturada para uso exclusivo do programa próprio, os outros programas poderão ocasionar efeitos colaterais, como por exemplo ocasionar um desajuste do relógio do sistema operacional que esteja sendo utilizado. Normalmente, isto não é problema com as portas seriais e paralelas. Para redirecionar temporariamente a ISR própria para a original ou anterior, pode ser utilizada uma função oldhandler(), onde esta aponta para a ISR original.

Não esquecer que antes de retornar da interrupção, deverá ser informado ao PIC (Programmable Interrupt Controller) o encerramento da rotina, enviando uma instrução EOI (End of Interrupt 0x10). Como existem dois PICs (no caso de um PC), primeiro deverá ser estabelecido para qual destes será enviado. Pode ser utilizado outportb(0x20,0x20); para o PIC 1 (IRQ 0 -7) ou outportb(0xA0,0x20); para o PIC 2 (IRQ 8 - 15)5.

2 Nos compiladores de 32 bits todos os ponteiros têm esse tamanho. Nos cross-compilers, os ponteiros normalmente são de 8 ou 16 bits. 3 A função disable() está declarada em DOS.H nos compiladores da Borland. 4 UART: Universal Asynchronous Receiver Transmitter 5 Se estiver em uso o PIC2, então o comando EOI tem que ser enviado para ambos, PIC1 e PIC2.

Page 298: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

276276276276

17171717

1177..66.. UUssaannddoo uummaa nnoovvaa RRoottiinnaa ddee AAtteennddiimmeennttoo aa IInntteerrrruuppççããoo -- IISSRR

Uma vez que tenha sido escrita uma nova rotina de atendimento de interrupção, pode-se analisar como será executada. O segmento de código que segue, mostra o uso de uma nova ISR. Neste caso foi escolhido o uso da IRQ 3. #include <dos.h> #define INTNO 0x0B /* Numero da Interrupçao – Ver Tabela 1 */ void main(void) oldhandler = getvect(INTNO); /* Salva o Vetor de Interrupçao anterior */ setvect(INTNO, nova_isr); /* Seta uma nova entrada no Vetor de Interrupções */ outportb(0x21,(inportb(0x21) & 0xF7)); /* Habilita a IRQ3 */ /* Seleciona a placa e seta a porta para gerar interrupções */ /* Corpo do programa começa aqui */ /* Reseta a placa para que a porta pare de gerar interrupçoes */ outportb(0x21,(inportb(0x21) | 0x08)); /* Mascara (Desabilita) IRQ3 */ setvect(INTNO, oldhandler); /* Restaura o Vetor de Interrupções anterior antes de sair */

Código 17-1

Antes de colocar o endereço da nova ISR na tabela de vetores de interrupção, deve-se primeiro salvar o vetor de interrupção original, de forma que possa ser restaurado novamente na saída do programa. Isto pode ser efetuado utilizando oldhandler = getvect(INTNO); onde INTNO é o numero do vetor de interrupção que se desejará restaurar posteriormente. Antes que oldhandler possa ser utilizado, primeiro deve-se declarar usando void interrupt (*oldhandler)();.

Uma vez que o vetor de interrupções original foi armazenado, pode-se proceder a instalação da nova ISR no tabela de vetores de interrupção. Isto pode ser feito utilizando a linha setvect(INTNO, nova_isr); onde nova_isr aponta para a nova rotina de atendimento da interrupção. A IRQ a ser utilizada, agora deverá ser habilitada.

A maioria das portas e UARTS6 precisa ser inicializadas para serem capazes de gerar interrupções. Por exemplo, a porta paralela padrão (Standard Parallel Port - SPP) requer que seja setado o bit 4 do registrador Control Port (Enable IRQ Via ACK) no endereço Base + 2. As portas seriais padrão requerem o ajuste apropriado dos bits 0 a 4 do registrador IER (Interrupt Enable Register) localizado no endereço Base + 1. O corpo do programa normalmente consiste de poucas tarefas simples dependendo da sua aplicação. Aqui pode ser monitorado se novas teclas forem pressionadas, menus que estão sendo selecionados, atualização de relógios, verificação da chegada de dados nos buffers de recepção, etc., sabendo que qualquer dado proveniente de uma porta poderá ser automaticamente lido e processado pela ISR.

Por exemplo, pode-se implementar uma ISR própria na Interrupção de Teclado, de forma que qualquer tecla que seja pressionada, seja automaticamente manipulada por uma outra ISR, por exemplo a ISR do clock. A cada 18.2 ticks o programa poderá atualizar os segundos no monitor de vídeo. As possibilidades das ISRs são enormes.

Antes de sair do programa, sempre deve-se restaurar o vetor original de interrupções, de forma, que o computador não fique instável. Isto pode ser feito utilizando setvect(INTNO, oldhandler);, onde oldhandler aponta para a rotina de atendimento a

6 UART: Universal Asynchronous Receiver Transmitter

Page 299: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

277277277277

17171717

interrupção original, cujo endereço foi armazenado utilizando oldhandler = getvect(INTNO);.

1177..77.. OO CCoonnttrroollaaddoorr PPrrooggrraammáávveell ddee IInntteerrrruuppççõõeess -- PPIICC

Como discutido anteriormente, as requisições de interrupção (Interrupt ReQuests - IRQ's) de um PC são manipuladas por dois Controladores de Interrupções Programáveis 8259 (Programmable Interrupt Controllers - PIC). Os antigos sistemas XTs e ATs possuíam dois chips DIP de 28 pinos, mas como se pode imaginar, a tecnologia tem mudado dramaticamente desde então. Enquanto que a operacionalidade dos PICs ainda é a mesma, agora estes se encontram integrados em algum dos chips dos novos sistemas, junto com muitos outros dispositivos.

Figura 17-1 – Controlador Programável de Interrupções

O diagrama de blocos básicos de um PIC é mostrado acima. As oito linhas individuais de requisição de interrupção são armazenadas no Interrupt Mask Register (IMR) para verificar se estas estão mascaradas ou não. Se estiverem mascaradas, então a requisição não será processada. Agora, se não estiverem mascaradas, então este registrador irá sinalizar a interrupção para o Interrupt Request Register (IRR).

O Interrupt Request Register irá armazenar todas as requisições das IRQs até que tenham sido manipuladas apropriadamente. Se necessário, o registrador pode ser lido setando alguns bits apropriados do Operation Control Word 3. O bloco Priority Resolver simplesmente seleciona a IRQ com a mais alta prioridade. As interrupções de mais alta prioridade são as identificas com o menor número. Por exemplo, a IRQ 0 é a interrupção com mais alta prioridade, seguida pela IRQ 1 e assim sucessivamente.

Uma vez que o PIC tenha determinado qual IRQ processar, chegou a hora de indicar ao processador, de forma que ele possa chamar a ISR programada. Este processo é feito enviando uma INT ao processador, i.e. a linha INT do processador será ativada. O processador irá então finalizar a instrução corrente que estiver sendo processada e responderá à requisição INT com um pulso em INTA (Interrupt Acknowledge).

Page 300: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

278278278278

17171717

Na recepção do sinal INTA do processador, a IRQ que o PIC estiver processando no momento, será armazenada no In Service Register (ISR) que como o nome diz, indica qual IRQ está em serviço. Os bits das IRQs são resetados no Interrupt Request Register, já que neste ponto não há mais requisição de interrupção, mas sim atendimento a interrupção.

Um outro pulso INTA será enviado pelo processador, para indicar ao PIC que deve colocar um apontador de 8 bits no barramento de dados, correspondente ao número da IRQ. Se estiver sendo solicitado um serviço de atendimento de uma IRQ controlada pelo PIC2, então este deverá enviar o apontador para o processador.. O PIC mestre (PIC1) neste estágio, selecionará o PIC2 para enviar o apontador, pela colocação da identificação do escravo nas linhas de cascateamento (CAS0 – CAS2), que são um barramento de três linhas entre o sistema dos PICs.

Os 5 bits mais significativos deste apontador são setados utilizando a Initialization Command Word 2 (ICW2). Esta deverá ser 00001 para o PIC1 e 01110 para o PIC2. Os três bits menos significativos, contêm a informação de qual IRQ está sendo atendida. Por exemplo, se a IRQ3 estiver requerendo atendimento, então o apontado de 8 bits estará com valor 00001 nos 5 bits mais significativos, e com valor 011 (IR3) nos bits menos significativos.

Colocando todos os bits juntos, tem-se 00001011 ou 0x0B que representa o vetor de interrupção da IRQ3. Para o PIC2, o mesmo procedimento é aplicado. Se a IRQ10 requer atendimento, então será enviado 01110010, que representa a interrupção 72h. A IRQ10 está conectada a IR2 no segundo PIC, desta forma os bits 010 são usados como bits menos significativos.

Uma vez que a ISR tenha efetuado todas as tarefas necessárias, esta envia um sinal de final de interrupção EOI par ao PIC, o qual resetará o seu In-Service Register. No caso de que a requisição tenha partido do PIC2, então a EOI deverá ser enviada a ambos os PICs. O PIC então determinará a próxima interrupção com mais alta prioridade e repetirá desta forma todo o processo. Se não houver outras requisições de interrupção, então o PIC esperará pela próxima requisição antes de interromper o processador novamente.

1177..77..11.. RReeddiirreecciioonnaammeennttoo IIRRQQ22//IIRRQQ99 O redirecionamento da IRQ2 ocasiona alguma confusão, e será discutida nesta

seção. Nos equipamentos originais XT, existia somente um PIC, implementando somente oito IRQs. Os usuários rapidamente esgotaram os recursos, de forma que mais um PIC com 8 IRQs foi adicionado ao PC. Isto envolveu a conexão de um outro chip PIC com o já existente no XT. A compatibilidade sempre ocasiona problemas como nesta nova configuração que tinha que ser compatível com o velho software e hardware. A “nova” configuração é mostrada na figura que segue.

Page 301: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

279279279279

17171717

Figura 17-2 – Redirecionamento de IRQ2/IRQ9

A CPU somente possui uma linha de interrupção, de forma que o segundo controlador tinha que ser conectado com o primeiro, em uma configuração de mestre-escravo. A IRQ2 foi selecionada para isto. Pela utilização da IRQ2 pelo segundo controlador, sendo que nenhum outro dispositivo poderia utilizar a IRQ2, que aconteceria com todos os dispositivos que utilizavam a IRQ2 ?

A resposta é nada, já que a linha de requisição de interrupção localizada no barramento, foi simplesmente desviada como entrada da IRQ9. Como nenhum dispositivo utilizado até então ainda usava o segundo PIC ou IRQ9, isto poderia ser feito sem problemas.

O problema seguinte foi que os dispositivos de hardware que utilizavam a IRQ2 deveriam instalar as suas ISRs nas INT 0x0A. Então uma rotina ISR foi utilizada em INT 71h, que enviaria a EOI para o PIC2 e então chamaria a ISR na INT 0x0A. Caso seja dessasemblada a ISR para a IRQ9, pareceria com o seguinte código: MOV AL,20 OUT A0,AL ; Envia EOI para PIC2 INT 0A ; Chama a ISR para a IRQ2 IRET

Esta rotina somente tinha que enviar a EOI para o PIC2, como era esperado que a rotina ISR escrita para a IRQ2 enviaria o EOI para o PIC1. O exemplo destrói os conteúdos do registrador AL, de forma que para evitar isto, deve ser copiado antes na pilha. Como o PIC2 é inicializado como escravo em IRQ2, qualquer requisição no PIC2 não chamará as rotinas ISR da IRQ2. Um apontador de 8 bits será enviado pelo PIC2.

Page 302: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

280280280280

17171717

1177..88.. EEnnddeerreeççooss ddooss CCoonnttrroollaaddoorreess PPrrooggrraammáávveeiiss ddee IInntteerrrruuppççããoo

Os dois PICs encontrados em um sistema IBM compatível, são inicializados através da BIOS, de forma que não é necessário se preocupar com todos os seus registradores. De qualquer forma para algumas pessoas, que possuem mentes inquisitivas, a informação que segue pode ser de alguma utilidade, para re-programar a BIOS por exemplo. A seguir é mostrada uma tabela de todas as palavras de comando do PIC 8259 e compatíveis, enquanto a tabela seguinte à primeira mostra os endereços do PIC2.

Endereço Leitura/Escrita Função Escrita Initialization Command Word 1 (ICW1) Escrita Operation Command Word 2 (OCW2) Escrita Operation Command Word 3 (OCW3) Leitura Interrupt Request Register (IRR)

20h

Leitura In-Service Register (ISR) Escrita Operation Command Word 2 (OCW2) Escrita Operation Command Word 3 (OCW3) Escrita Operation Command Word 4 (OCW4)

21h

Leitura/Escrita Interrupt Mask Register (IMR)

Tabela 17-4 – Endereços/Registradores para o PIC1

Endereço Leitura/Escrita Função Escrita Initialization Command Word 1 (ICW1) Escrita Operation Command Word 2 (OCW2) Escrita Operation Command Word 3 (OCW3) Leitura Interrupt Request Register (IRR)

A0h

Leitura In-Service Register (ISR) Escrita Operation Command Word 2 (OCW2) Escrita Operation Command Word 3 (OCW3) Escrita Operation Command Word 4 (OCW4)

A1h

Leitura/Escrita Interrupt Mask Register (IMR)

Tabela 17-5 – Endereços/Registradores para o PIC2

O PIC 8259 oferece várias outras características que não são utilizadas pelo PC. Este também oferece suporte para os microprocessadores MCS-80/85. Todo o cuidado que se deve ter com o seu uso nos PCs é se o sistema está sendo executado no modo de PIC único, ou no modo Cascateado (mais de um PIC), e se a ICW4 é necessária. Caso o ICW4 não esteja sendo utilizado, então todos os seus bits devem ser setados para zero. No caso de estar utilizando no modo 8086, deve ser enviado um ICW4.

1177..88..11.. IInniittiiaalliizzaattiioonn CCoommmmaanndd WWoorrdd 11 ((IICCWW11)) Caso o PIC tenha sido resetado, deverá ser inicializado com os comandos de

inicialização 2 a 4 (Initialization Command Words - ICW) antes que este aceite e processe as requisições de interrupção. A Tabela 17-6 mostra as possibilidades de programação de ICW1.

Bit(s) Função 7:5 Modo de Vetor de Endereços de Interrupção para MCS 80/85 4 Deve ser setado para 1 para ICW1

1 Interrupções disparadas por nível 3 0 Interrupções disparadas por borda 1 Intervalo entre endereços de 4 2 0 Intervalo entre endereços de 8 1 PIC único 1 0 PIC cascateado

0 1 ICW4 deve ser enviado

Page 303: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

281281281281

17171717

0 ICW4 não necessário

Tabela 17-6 – ICW1

1177..88..22.. IInniittiiaalliizzaattiioonn CCoommmmaanndd WWoorrdd 22 ((IICCWW22)) A Initialization Command Word 2 (ICW2) seleciona qual informação do vetor será

liberada no barramento, durante o segundo pulso INTA. Usando no modo 8086, somente os bits 7:3 serão necessários. No caso 00001000 (0x08) para o PIC1 e 01110000 (0x70) para o PIC2. No caso em que seja necessário realocar a Tabela de Vetor de IRQ, poderá ser utilizado este registrador.

Bit Modo 8086/80/8080 Modo MCS 80/85 7 I7 A15 6 I6 A14 5 I5 A13 4 I4 A12 3 I3 A11 2 - A10 1 - A9 0 - A8

Tabela 17-7 – ICW2

1177..88..33.. IInniittiiaalliizzaattiioonn CCoommmmaanndd WWoorrdd 33 ((IICCWW33)) Existem dois diferentes ICW3s. Uma é utilizada, se o PIC é mestre, enquanto que

a outra é utilizada pelos escravos. A tabela a seguir mostra o ICW3 para o mestre. Bit Função 7 IR7 está conectado num escravo 6 IR6 está conectado num escravo 5 IR5 está conectado num escravo 4 IR4 está conectado num escravo 3 IR3 está conectado num escravo 2 IR2 está conectado num escravo 1 IR1 está conectado num escravo 0 IR0 está conectado num escravo

Tabela 17-8 – ICW3 para o PIC Mestre

Para o dispositivo escravo: Bits Função 7 Reservado. Setado para zero 6 Reservado. Setado para zero 5 Reservado. Setado para zero 4 Reservado. Setado para zero 3 Reservado. Setado para zero

Identificação do Escravo 2:0 000 Escravo 0 001 Escravo 1 010 Escravo 2 011 Escravo 3 100 Escravo 4 101 Escravo 5 110 Escravo 6

111 Escravo 7

Tabela 17-9 – ICW3 para dispositivos escravos

Page 304: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

282282282282

17171717

1177..88..44.. IInniittiiaalliizzaattiioonn CCoommmmaanndd WWoorrdd 44 ((IICCWW44)) Como foi visto anteriormente, muitas das funções especiais do PIC 8259 não são

utilizadas em um PC. Não é utilizado o modo Special Fully Nested Mode, de forma que o bit é setado em zero. Assim também é utilizado o modo não-bufferizado e EOI normal, setando os bits correspondentes em zero. O único item que deve ser setado é o modo 8086/8080 Mode, que está localizado no bit 0.

Bits Função 7 Reservado. Setado para zero 6 Reservado. Setado para zero 5 Reservado. Setado para zero

1 Specially Fully Nested Mode 4 0 Not Specially Fully Nested Mode

3:2 0x Non-Buffered Mode 10 Buffered Mode Slave 11 Buffered Mode Master

1 Auto EOI 1 0 Normal EOI 1 8086/8080 Mode 0 0 MCS-80/85

Tabela 17-10 – ICW4

1177..88..55.. OOppeerraattiioonn CCoonnttrrooll WWoorrdd 11 ((OOCCWW11)) Uma vez que tenham sido enviadas todas as palavras de comando de inicialização

para o PIC, então podem-se enviar as palavras de controle de operação (Operation Control Words), em qualquer ordem e a qualquer momento durante a operação do PIC. Estas palavras de comandos de controle são mostradas nas seguintes seções.

A Operation Control Word 1 mostrada a seguir, é utilizada para mascarar as entradas do PIC.

Bit PIC2 PIC1 7 Mascara IRQ15 Mascara IRQ7 6 Mascara IRQ14 Mascara IRQ6 5 Mascara IRQ13 Mascara IRQ5 4 Mascara IRQ12 Mascara IRQ4 3 Mascara IRQ11 Mascara IRQ3 2 Mascara IRQ10 Mascara IRQ2 1 Mascara IRQ09 Mascara IRQ1 0 Mascara IRQ08 Mascara IRQ0

Tabela 17-11 – OCW1

1177..88..66.. OOppeerraattiioonn CCoonnttrrooll WWoorrdd 22 ((OOCCWW22)) O registrador Operation Control Word 2 seleciona como o funcionará o

Fechamento de Interrupção (End of Interrupt – EOI). A única coisa que interessa realmente neste registrador é o comando EOI não-específico, o qual deve ser enviado na finalização das ISRs novas.

Bits Função 7:5 000 Rotate in Auto EOI Mode (Clear) 001 Non specific EOI 010 Reserved 011 Specific EOI

Page 305: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

283283283283

17171717

100 Rotate on Auto EOI Mode (Set) 101 Rotate on Non-Specific EOI 110 Set Priority Command (Use bits 2:0) 111 Rotate on Specific EOI (Use bits 2:0) 4 Deve ser zero 3 Deve ser zero 2:0 000 Act on IRQ 0 or 8

001 Act on IRQ 1 or 9 010 Act on IRQ 2 or 10 011 Act on IRQ 3 or 11 100 Act on IRQ 4 or 12 101 Act on IRQ 5 or 13 110 Act on IRQ 6 or 14

111 Act on IRQ 7 or 15

Tabela 17-12 – OCW2

1177..88..77.. OOppeerraattiioonn CCoonnttrrooll WWoorrdd 33 ((OOCCWW33)) Os bits 0 e 1 do Operation Control Word 3 são os mais importantes para quem

programa o PC. Estes dois bits habilitam a leitura do status do registrador de requisição de interrupção, Interrupt Request Register (IRR) e do In-Service Register (ISR).

Isto é feito setando os bits apropriados de forma correta, e pela leitura do registrador no endereço de Base. Por exemplo, se for necessária a leitura do ISR (In-Service Register), então pode-se setar ambos os bits 1 e 0 para 1. A leitura seguinte no registrador de base, (0x20 para o PIC1 ou 0xA0 para o PIC2) retornará o status do registrador In-Service.

Bits Função 7 Deve ser setado para zero 6:5 00 Reservado 01 Reservado 10 Reset Special Mask 11 Set Special Mask 4 Deve ser setado para zero 3 Deve ser setado para zero 2 1 Poll Command 0 No Poll Command 1:0 00 Reservado 01 Reservado 10 Next Read Returns Interrupt Request Register 11 Next Read Returns In-Service Register

Tabela 17-13 – OCW3

1177..99.. PPrrooggrraammaaççããoo ddoo TTiimmeerr ddoo PPCC

Observar o seguinte exemplo de programação do timer do PC, para gerar interrupções em intervalos constantes de tempo. Este tipo de rotina é adequado para efetuar tarefas de forma periódica tais como transmissão e aquisição de dados, e temporizações especiais. #include <dos.h> #include <stdio.h> #include <conio.h> /*.........................................................................*/ /* Variaveis e definicoes globais */ /* Programacao do timer 8253 040h - counter 0 041h - counter 1 042h - counter 2

Page 306: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

284284284284

17171717

043h - control register Programacao da 8259 020h - 021h - mascara de interrupcoes: 0-habilita, 1-mascara. */ #define INTMASK 0x021 /* Localizacao da mascara de interrupcoes da 8259 */ #define TTICK 0x01C /* Localizacao do vetor de int da IRQ0 (timer tick) */ #define CTRL_TIMER_PORT 0x043 /* Porta de controle do timer 8253 */ #define DATA_TIMER0_PORT 0x040 /* Porta de dados do timer 0 do 8253 */ void ConfigTimer(unsigned int); void RestoreConfig(void); unsigned int hbyte,ch; /* Usados apenas nas IRQ */ static char flag = 0 ; void main(void)

unsigned int i = 0; unsigned int segundo = 0;

ConfigTimer(100);

while(!kbhit()) if(flag)

// printf("%u\n",i); i++;

flag = 0; if(i==99) printf("Segundo:%u\n",segundo++); i = 0; RestoreConfig(); /* Funcao: IRQ0 */ /* Entrada: nada */ /* Proposito: atender interrupcao IRQ0 gerada pelo timer do PC */ void interrupt IRQ0 (void) flag = 1; /*........................................................................*/ /* Funcao: ConfigTimer Configura o timer 0 para gerar interrupcoes em intervalos constantes. Reprograma a frequencia do timer 0 do 8253 do XT. Atencao: esta rotina altera o relogio de tempo real do DOS, para voltar a ter a hora certa, resetar o micro. Desvia vetor de interrupcao (001Ch : timer tick) da IRQ 0 para a rotina IRQ0. Entrada: frequencia de aquisicao desejada (19Hz <= f <= 65535Hz), */ void interrupt (*vetor_original) (void); void ConfigTimer (unsigned int freq_aq) unsigned int n; unsigned char msb,lsb; unsigned long freqck0 = 1193182L; /* freq de clock do timer 0 */ /* maior freq aquis possivel: ? - depende da velocidade do micro */ /* menor freq aquis possivel: 19Hz */ vetor_original = getvect(TTICK); /* salva vetor de interrupcoes */ // disable(); /* desabilita interrupcoes na CPU */ outportb(INTMASK,0x01); /* mascara IRQ0 na 8259 */ /* altera o vetor de interrupcoes 01Ch */ setvect(TTICK,IRQ0); n = (unsigned int)(freqck0/freq_aq+0.5);/* calcula divisor por n do timer */ msb = (unsigned char)(n/256); /* calcula msb e lsb de n */ lsb = (unsigned char)(n-msb*256); outportb(CTRL_TIMER_PORT,0x36); /* reprograma freq do timer */ outportb(DATA_TIMER0_PORT,lsb); outportb(DATA_TIMER0_PORT,msb); // enable(); /* habilita interrupcoes na CPU */ outportb(INTMASK,0x00); /* habilita todas as int de hw na 8259 */ void RestoreConfig(void)

Page 307: Tratado Da Linguagem c

I N T E R R U P Ç Õ E S

285285285285

17171717

outportb(INTMASK,0x01); /* mascara e desabilita int de hw */ // disable(); outportb(CTRL_TIMER_PORT,0x36); /* Reprograma freq original do timer */ outportb(DATA_TIMER0_PORT,0x00); /* (18.2 interrupcoes por segundo) */ outportb(DATA_TIMER0_PORT,0x00); setvect(TTICK,vetor_original); /* Restaura vetor original */ outportb(INTMASK,0x00); /* Habilita as interrupcoes de hardware */ // enable();

Código 17-2

EExxeerrccíícciiooss

1. Utilizando como exemplo o programa acima implementar um programa que funcione como relógio, mostrando informações de horas, minutos, segundos e centésimos de segundo.

2. Implementar um programa que gere uma onda quadrada no pino D0 da porta paralela, com freqüência programável via teclado, e cuja reprogramação seja de forma online e que utilize interrupções.

Page 308: Tratado Da Linguagem c

286286286286 Luis Fernando Espinosa Cocian

18181818

1188.. AA IINNTTEERRFFAACCEE PPAARRAALLEELLAA

1188..11.. AAss IInntteerrffaacceess –– UUmmaa IInnttrroodduuççããoo

A interface pode ser descrita como sendo um dispositivo ou software que conecta duas entidades separadas. Por exemplo, uma interface de usuário é a parte de um programa que conecta o computador com o operador humano.

Existem também interfaces para conectar programas, para conectar dispositivos, e para conectar programas com dispositivos. Uma interface pode ser um programa ou um dispositivo, tal como um conector elétrico.

Os computadores pessoais possuem vários tipos de portas. Internamente, existem varias portas para poder conectar drives, monitores de vídeo e teclados. Externamente, existem portas para conectar modems, impressoras, mouse e outros dispositivos periféricos.

A maioria dos PCs vem equipada com uma porta serial RS-232C ou RS-422, para conectar um modem ou um mouse, e uma porta paralela para conectar uma impressora. Nos PCs, a porta paralela possui uma interface Centronics, que utiliza um conector de 25 pinos. As portas SCSI (Small Computer System Interface) suportam altas velocidades de transmissão, maiores que as portas convencionais, e possibilitam a conexão de até sete dispositivos na mesma porta. Todos os computadores Apple Macintosh possuem uma porta SCSI.

1188..22.. AA IInntteerrffaaccee PPaarraalleellaa

Neste capítulo serão tratadas as portas paralelas de comunicação de dados, dando ênfase às portas dos computadores pessoais compatíveis com IBM PC.

A interface paralela serve de porta de comunicação entre um sistema microprocessador e sistemas externos. A maioria dos PCs possui uma porta paralela e uma ou mais portas seriais. O termo “paralela” significa que a transmissão de dados é feita na forma de vários meios de comunicação (por exemplo, vários fios condutores), onde cada um deles, transmite uma parte da informação, ao mesmo tempo. A principal vantagem deste tipo de interface é a velocidade de transmissão de dados. Entre as desvantagens, o grande número de fios condutores e a relativa baixa imunidade a ruído, limitando o comprimento dos cabos de comunicação.

Nos PCs, a porta paralela usa um conector de 25 pinos (DB-25). Esta porta é usada normalmente para a conexão de impressoras, scanners, zip drives, computadores e outros dispositivos que precisam de grande velocidade. Às vezes é mencionada com o

Capítulo

18

Page 309: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

287287287287

18181818

nome de interface Centronics, porque este era o nome da empresa que projetou o padrão original para a comunicação paralela entre computadores e impressoras1.

Os mais novos tipos de porta paralela, que suportam os mesmos conectores da interface Centronics, são a EPP (Enhanced Parallel Port) e a ECP (Extended Capabilities Port). Ambas suportam comunicação bidirecional e taxas de transferência dez vezes mais rápidas que a porta padrão Centronics.

1188..33.. IInntteerrffaacceeaammeennttoo ccoomm aa PPoorrttaa PPaarraalleellaa PPaaddrrããoo

A porta paralela é muito utilizada em projetos de eletrônica. Esta porta permite a entrada de até 9 bits ou a saída de 12 bits em qualquer instante de tempo, requerendo um circuito externo muito simples para implementar qualquer tarefa. A porta é composta de 4 linhas de controle, 5 linhas de status e 8 linhas de dados. No PC é normalmente conectada a um conector DB-25 fêmea. Também pode existir no PC um conector DB-25 macho que usualmente é conectado à porta serial (RS-232), e portanto incompatível com o canal paralelo.

As novas portas paralelas são padronizadas pela norma IEEE 1284. Esta norma define 5 modos de operação: 1. Modo de Compatibilidade 2. Modo de Nibble. (Protocolo não descrito neste documento) 3. Modo de Byte. (Protocolo não descrito neste documento) 4. Modo EPP (Enhanced Parallel Port). 5. Modo ECP (Extended Capabilities Port).

O objetivo foi que os novos drivers e dispositivos fossem compatíveis uns com os outros, e que consigam interfacear com a porta paralela padrão2.

O modo de compatibilidade (ou modo Centronics como é comumente conhecido), pode somente enviar dados em uma direção (saída de dados) com uma velocidade típica de 50 Kbytes por segundo, mas podendo chegar a 150 KB por segundo. Para poder receber dados, deve-se mudar o modo para o Nibble ou Byte. O modo Nibble pode ler um nibble (4 bits). O modo Byte usa o recurso de bidirecionalidade (encontrado somente em algumas placas) para ler um byte de dados. Pode-se ainda utilizar as linhas de controle bidirecionais.

Os modos ECP e EPP utilizam hardware adicional para poder gerar e gerenciar o handshaking (diálogo entre dispositivos). Para poder enviar um byte a uma impressora (ou algum outro dispositivo) usando o modo de Compatibilidade, o software deverá: 1. Escrever um byte na porta de dados. 2. Verificar se a impressora ou outro dispositivo, não está ocupado (busy). Caso o dispositivo

esteja ocupado, este não aceitará qualquer dado, de forma que o dado escrito será perdido. 3. Levar o pino Strobe (pino 1) para 0. Isto indica ao dispositivo periférico que os dados estão

disponíveis na linha de dados (pinos 2 a 9). 4. Colocar o pino strobe para 1 depois de esperar aproximadamente 5 µs depois de ter colocado

o mesmo em zero.

1 A interface paralela moderna é baseada no projeto feito pela Epson. 2 O interfaceamento das portas paralelas a ser visto nesta seção, mantém a compatibilidade com o padrão Standard Parallel Port (SPP). Os modos de compatibilidade, nibble e byte necessitam somente do hardware padrão das portas paralelas originais, enquanto que os modos EPP e ECP requerem hardware adicional, porém mantém a compatibilidade com o padrão SPP.

Page 310: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

288288288288

18181818

Estes passos limitam a velocidade na qual a porta pode transferir dados. As portas EPP e ECP contornam este problema deixando que o hardware verifique se o periférico está ocupado e gera o sinal de strobe efetuando o handshake adequado. Isto permite que somente uma instrução de I/O seja necessária, aumentando a velocidade.

Estas portas podem enviar dados na taxa de 1 a 2 MBytes por segundo. A porta ECP também tem a vantagem de utilizar canais de DMA (Direct Memory Addressing) e buffers FIFO, de forma que os dados podem ser armazenados antes de serem enviados em posições de memória, sem a necessidade de utilizar instruções de I/O.

1188..44.. PPrroopprriieeddaaddeess ddoo HHaarrddwwaarree

Na seção 0 são mostrados os conectores utilizados comumente nesta interface, um conector DB25 fêmea de 25 pinos (usualmente no PC) e o conector Centronics, de 36 pinos (usualmente na impressora). O padrão IEEE 1284 especifica ainda três tipos diferentes de conector para uso com portas paralelas. O primeiro é o 1284A, do tipo DB25 encontrado na maioria dos computadores. O segundo é o 1284B, conector Centronics de 36 pinos encontrado na maioria das impressoras. E o 1284C, que é um conector de 36 pinos parecido com o conector Centronics, mas menor. Este conector possui melhor fechamento, melhores propriedades elétricas e é mais fácil de montar. Também contém dois pinos a mais que podem ser usados para ver se o outro dispositivo está energizado. O conector 1284C é recomendado para os novos projetos.

Na tabela a seguir é utilizada a letra ‘n’ na frente do nome que denota um sinal, para deixar explícito de que este ficará ativo em zero. Por exemplo o sinal nError; se ocorrer um erro na impressora, então este sinal ficará em zero. Normalmente este sinal está em 1, quando o funcionamento estiver corretamente acontecendo. A inversão do sinal ainda é invertida pelo hardware da interface paralela. Um exemplo é o sinal Busy. Se for aplicado +5V (1 lógico) a este pino e for lido o Registrador de Status, ele mostrará um zero no bit 7.

A saída da porta paralela normalmente usa níveis lógicos TTL. A corrente de dreno varia de porta para porta. A maioria das portas paralela, implementadas em ASIC, pode drenar e fornecer correntes da ordem de 12 mA. Alguns valores típicos extraídos dos manuais: Sink/Source (absorção/fornecimento) de 6mA, Source 12mA/Sink 20mA, Sink 16mA/Source 4mA, Sink/Source 12mA.

Figura 18-1 – Conector de DB-25

Como se pode observar, existem pequenas variações. Para acionamento de cargas maiores que10 mA, é recomendado o uso de um buffer, de forma a drenar a menor corrente possível da interface. Pino Pino

Centronics Sinais SPP Direção In/Out Registrador Inversão por

Hardware (Negação)

1 1 NStrobe In/Out Control Sim 2 2 Data 0 Out Data 3 3 Data 1 Out Data 4 4 Data 2 Out Data

Page 311: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

289289289289

18181818

5 5 Data 3 Out Data 6 6 Data 4 Out Data 7 7 Data 5 Out Data 8 8 Data 6 Out Data 9 9 Data 7 Out Data 10 10 NAck In Status 11 11 Busy In Status Sim 12 12 Paper-Out Paper-End In Status 13 13 Select In Status 14 14 nAuto-Linefeed In/Out Control Sim 15 32 nError/nFault In Status 16 31 NInitialize In/Out Control 17 36 Nselect-Printer Nselect-In In/Out Control Sim 18-25 19-30 Ground GND

Tabela 18-1 – Pinagem do conector de porta paralela, do tipo DB-25

1188..55.. CCeennttrroonniiccss

O padrão Centronics foi um dos primeiros padrões para transferência de dados de um computador host para uma impressora. A maioria das impressoras utiliza este protocolo. O protocolo é normalmente implementado usando uma SPP sob um software de controle. A seguir é mostrado um diagrama simplificado do protocolo Centronics.

Figura 18-2 - Handshake do padrão Centronics

Primeiro, os dados são colocados pela porta paralela nos pinos 2 a 7. O computador hospedeiro (host) então testa o sinal Busy para ver se o periférico está ocupado, isto é , para o prosseguimento, este sinal deve estar em nível baixo. O programa então ajusta o sinal Strobe nível baixo, esperando no mínimo 1 ms, e então ajusta novamente o sinal para o nível alto. Os dados serão normalmente lidos pelo periférico na borda de subida do sinal Strobe. O periférico deverá indicar que está ocupado processando os dados através da linha Busy. Após a leitura dos dados pelo periférico, este enviará um sinal de reconhecimento do byte, através de um pulso em zero de aproximadamente 5 ms na linha nAck.

Freqüentemente o computador host ignora a linha nAck para poupar tempo. Mais adiante, na seção ECP, descreve o modo Centronics rápido, que deixa ao hardware efetuar o handshaking. Neste modo o programador só deve-se preocupar em escrever o dado na porta de I/O. O hardware verificará se o periférico está ocupado, e gerará o sinal de strobe de forma automática. Neste modo, o hardware geralmente não testará o sinal nAck.

Page 312: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

290290290290

18181818

1188..66.. EEnnddeerreeççooss ddaass PPoorrttaass

A porta paralela possui três endereços de base possíveis, e estão listados na tabela a seguir.

O endereço de base 3BCh foi originalmente introduzido para o uso de portas paralelas nas primeiras placas de vídeo. Este endereço não é mais utilizado, uma vez que foram removidas as portas paralelas das placas de vídeo, e conectadas diretamente no barramento. Ultimamente este endereço tem aparecido como uma opção para portas paralelas integradas nas placas mãe, configuráveis através da BIOS3.

À LPT1 é normalmente designado o endereço de base 378h, enquanto que à LPT2 o 278h. Embora estes endereços podem variar, são os mais comumente usados. É importante notar que os endereços para as portas podem mudar de máquina para máquina.

Endereço Nota 3BCH-3BFH Utilizado nas portas paralelas que foram

incorporadas às placas de vídeo, e hoje em dia ficam disponíveis para portas controladas pela BIOS. Não suporta endereços ECP.

378H-37FH Endereço usual para LPT 1 278H-27FH Endereço usual para LPT 2

Tabela 18-2 – Endereços das portas paralelas

Quando o computador é energizado, o programa armazenado na BIOS determinará o número de portas existentes no hardware e designará os labels LPT1, LPT2 e LPT3 a elas. A BIOS primeiro manda verificar o endereço 3BCh. Caso seja encontrada uma porta nele, será designada como LPT1, então procurará no endereço 378h. Se uma porta paralela for encontrada no endereço, será designado o próximo label livre para o dispositivo. Assim deverá ser LPT1, se não for encontrada uma interface em 3BCh, ou LPT2 caso contrário. O último endereço de procura, é o 278h, seguindo o mesmo procedimento descrito anteriormente. Ainda é possível se ter uma LPT2 no endereço 378h, já que o endereço depende da implementação do hardware.

Alguns fabricantes de interfaces paralelas, disponibilizam jumpers que permitem configurar a porta para LPT1, LPT2 e LPT3, mas sem indicar qual o endereço que será atribuído a elas. Isto pode gerar confusão, já que quem define o label é a BIOS (de acordo com o endereço) e não o hardware de forma direta. Diferentes implementações no software de BIOS poderão atribuir labels diferentes aos indicados nestes jumpers.

Os labels designados LPT1, LPT2 e LPT3, não devem preocupar os programadores que desejam interfacear com dispositivos periféricos. A maioria das vezes, é utilizado diretamente o endereço de base para interfacear com a porta no lugar do label LPT1.

Os endereços para estes dispositivos são providos pelo BIOS. Quando o BIOS designa os endereços para estas interfaces, estes são armazenados em locais específicos de memória, de maneira que possam ser achados.

1188..66..11.. EEnnddeerreeççooss IInniicciiaaiiss Endereço de Memória Label – Endereço de Base para 0000:0408 LPT1

3 BIOS: Basic Input/Output System.

Page 313: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

291291291291

18181818

0000:040A LPT2

0000:040C LPT3

0000:040E LPT4 4

Tabela 18-3 – Endereços de memória das portas paralelas

A tabela acima, mostra os endereços de memória nos quais podem ser encontrados os endereços de base das portas paralelas (endereços de I/O) na área de dados da BIOS. Cada endereço é de dois bytes. O programa seguinte, mostra como ler estas posições de memória para obter os endereços das interfaces paralelas. Este programa será bem executado em ambientes DOS, mas poderá apresentar problemas de violação de acesso em ambientes multitarefa, tais como o Windows 9x. Isto é porque em sistemas multitarefa, os recursos são compartilhados, e os sistemas operacionais protegem o acesso a estas áreas da memória para proteger o funcionamento do sistema. Caso se esteja usando o sistema operacional Windows, este programa deverá ser um aplicativo 16 bits, para ser executado numa janela DOS. Caso seja necessário implementar um aplicativo win32, devem-se seguir as regras do sistema, utilizando funções de acesso às portas através de drivers, tais como a função CreateFile() da API. #include <stdio.h> #include <dos.h> void main(void) unsigned int far *ptraddr; /* Ponteiro para os endereços das portas paralelas */ unsigned int address; /* Endereço da porta */ int a; ptraddr = (unsigned int far *)0x00000408; for (a = 0; a < 3; a++) address = *ptraddr; if (address == 0) printf("Nãofoi encontrada a porta LPT%d \n",a+1); else printf("O endereço designado para a LPT%d e’ %Xh\n",a+1,address); *ptraddr++;

Código 18-1

1188..77.. RReeggiissttrraaddoorreess ddaa SSttaannddaarrdd PPaarraalllleell PPoorrtt ((SSPPPP))

O endereço de base, usualmente chamado de Data Port ou Data Register (ou simplesmente registrador de dados), é usado para enviar dados para os pinos da porta paralela (pinos 2 a 9). Este registrador é normalmente um registrador somente de escrita (write only). Caso ele seja lido, a informação será do último byte enviado. No caso de portas bidirecionais, também podem ser recebidos dados neste endereço. Ver a seção Portas Bidirecionais para maiores detalhes.

Offset Nome Leitura/Escrita Número do Bit Propriedades Bit 7 Data 7 (Pino 9) Bit 6 Data 6 (Pino 8) Bit 5 Data 5 (Pino 7) Bit 4 Data 4 (Pino 6) Bit 3 Data 3 (Pino 5) Bit 2 Data 2 (Pino 4) Bit 1 Data 1 (Pino 3)

Base + 0 Data Port (Porta de Dados)

Escrita5

Bit 0 Data 0 (Pino 2)

Tabela 18-4 – Registrador dados da porta paralela

4 O endereço 0000:040E na área de dados da BIOS pode existir em algumas implementações da BIOS. 5 Se a porta for bidirecional, poderão ser executadas funções de leitura e escrita no registrador de dados (Data).

Page 314: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

292292292292

18181818

A porta de Status (endereço de base + 1) é um registrador somente de leitura. Qualquer dado escrito neste endereço será ignorado. A porta de status consiste de 5 linhas de bits de entrada (pinos 10, 11, 12, 13 e 15), uma linha de status de registrador de IRQ e dois bits reservados. Notar que o bit 7 (Busy) é uma entrada ativa quando baixa. Por exemplo, se o bit 7 aparece com um zero lógico, significa que há +5V no pino 11. De forma análoga, o bit 2 (nIRQ), se este bits está com valor 1 lógico então ocorreu uma interrupção.

Offset Nome Leitura/Escrita Número do Bit Propriedades Bit 7 Busy Bit 6 Ack Bit 5 Paper Out Bit 4 Select In Bit 3 Error Bit 2 IRQ (negado) Bit 1 Reserva

Base + 1 Status Port (Porta de Status)

Somente Leitura

Bit 0 Reserva

Tabela 18-5 – Registrador de status da porta paralela

A porta de Controle (endereço de base + 2) foi implementada como uma porta somente de escrita. Quando uma impressora é conectada a uma porta paralela, serão usados quatro controles. Eles são, o Strobe, Auto Line Feed, Initialize e Select Printer, todos com lógica invertida, exceto o Initialize.

Offset Nome Leitura/Escrita Número do Bit Propriedades Bit 7 Não usado Bit 6 Não usado Bit 5 Habilita a porta

bidirecional Bit 4 Habilita a IRQ

através da linha Ack

Bit 3 Select Printer (Seleciona Impressora)

Bit 2 Initialize Printer (Inicializa a impressora - reset)

Bit 1 Auto Linefeed (Alimentação automática de linha)

Base + 2 Control Port (Porta de Controle)

Leitura/Escrita

Bit 0 Strobe

Tabela 18-6 – Registrador de status da porta paralela

As quatro saídas também podem ser usadas como entradas. Se o computador coloca um pino em nível alto (+5V) e o dispositivo tenta colocá-lo em nível baixo, haverá efetivamente um curto circuito na porta, ocasionando um conflito em tal pino. Para evitar isto, estas linhas são saídas de open collector (ou open drain para dispositivos CMOS). Isto permite que possa ter dois estados, um estado de nível baixo (0V) e um estado de alta impedância (circuito aberto).

Usualmente as interfaces paralelas possuem resistores internos de pull-up, mas não é regra geral. Algumas podem ter saídas de coletor aberto, enquanto que outra podem ter saídas na configuração totem pole. Para fazer que os dispositivos trabalhem corretamente na maioria das interfaces paralelas, devem ser utilizados resistores externos de pull-up. Caso o hardware já possua resistores internos, então os colocados externamente ficarão em paralelo com estes, ou caso seja uma saída em totem pole, funcionarão como uma carga.

Page 315: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

293293293293

18181818

Um resistor externo de 4.7K pode ser usado como pull-up. Deve-se evitar o uso de resistências menores devido ao excesso de corrente, no caso de existência de resistores internos. Enquanto um pino estiver no estado de alta impedância, terá nível +5V, e neste estado o periférico pode forçar o pino para o nível baixo e ter o controle dos dados para operações de leitura. Desta forma os quatro pinos da porta de controle podem ser usados para transferências bidirecionais de dados. Porém a porta de controle deve ser ajustada para xxxx 0100 para ser capaz de ler dados, isto é, deixando todos os pinos em +5V de forma que o periférico possa forçá-los para 0V.

Os bits 4 e 5 são controles internos. O bit 4 habilita a IRQ e o bit 5 habilita a porta bidirecional, permitindo a entrada de 8 bits colocados nos pinos de dados (DATA0-7, pinos 2 a 8). Este modo somente é possível se a interface paralela o suportar. Os bits 6 e 7 são reservados. Qualquer escrita nestes dois bits será ignorada.

Avalie o seguinte exemplo, implementado com o compilador Borland C++ 3.1 (16 bits - PC). #include<dos.h> void main(void) unsigned long int i; for(i=0;i<256;i++) outportb(0x378,(int)i); printf(“&02x\n”,(unsigned int)i); delay(10);

Código 18-2

Este programa implementa um contador binário de 8 bits. Pode-se montar um circuito para visualizar a contagem, composto de leds e resistências de 470 ohms conectados a cada pino da porta de dados.

Um programa equivalente, usando o Visual C++ 5.0. #include<dos.h> #include <conio.h> #include <time.h> void delay( clock_t wait ); void main() int i; for (i=0;i<256;i++) _outp(0x378,i); delay(30); /* Pausa por um número específico de ms. */ void delay( clock_t wait ) clock_t goal; goal = wait + clock(); while(goal > clock());

Código 18-3

1188..88.. PPoorrttaass BBiiddiirreecciioonnaaiiss

O diagrama esquemático a seguir (Figura 18-3), mostra uma implementação simplificada de um registrador de porta paralela de dados. As interfaces paralelas originais

Page 316: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

294294294294

18181818

eram implementadas com lógica TTL 74LS. Hoje em dia, são implementadas usando um ASIC6 ou um FPGA7, mas a teoria de operação permanece a mesma.

1D

3

2D

4

3D

7

4D

8

5D

13

6D

14

7D

17

8D

18

~OC

1

CLK

11

1Q2

2Q5

3Q6

4Q9

5Q12

6Q15

7Q16

8Q19

U1

74LS374N

U2A

74LS244N

18

16

14

12

2

4

6

8

1

U2B

74LS244N

9

7

5

3

11

13

15

17

19

D0D1

D2

D3

D4

D5

D6

D7

CONTROL_BIT3

IOW_BASE

DATA0_P2

DATA1_P3

DATA2_P4

DATA3_P5

DATA4_P6

DATA5_P7

DATA6_P8

DATA7_P9

IOR_BASE

Figura 18-3- Porta paralela bidirecional

As portas não bidirecionais eram fabricadas com o pino de habilitação de saída (output enable) do 74LS374 permanentemente habilitado (em zero), de forma que os dados só poderiam sair. Quando for efetuada uma leitura desta forma, os dados voltam a partir do 74LS244 que está conectado aos pinos de dados. Agora, se for possível isolar o 74LS374 terá-se uma porta efetivamente bidirecional (ou uma porta somente de leitura, caso seja desabilitada a saída).

Dependendo das correntes envolvidas, serão necessários alguns transistores ou buffers para reforço de corrente. As portas bidirecionais usam o bit de controle 5 conectado ao pino OE do 74LS374 de forma que os drivers de saída são desligados. Desta forma podem ser lidos os dados presentes nos pinos de dados, sem conflito de barramento nem dreno de corrente elevado.

O bit 5 da porta de controle habilita ou desabilita a função bidirecional da porta paralela. Esta função está disponível somente em portas verdadeiramente bidirecionais. Quando este bit é setado para 1, os pinos 2 a 9 ficam no estado de alta impedância. Neste estado podem ser inseridos dados nas linhas, e lidos através da porta de dados (no endereço de base). Qualquer dado que seja escrito na porta de dados será armazenado mas 6 ASIC: Application Specific Integrated Circuit. 7 FPGA: Field Programmable Array.

Page 317: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

295295295295

18181818

não estará disponível nos pinos de dados. Para desabilitar o modo bidirecional, o bit 5 deve ser setado para 0.

Embora nem todas as portas se comportam da mesma maneira, algumas podem requerer setar o bit 6 da porta de controle, para habilitar o modo bidirecional e ainda setar o bit 5 para desabilitá-lo. Diferentes fabricantes implementam as suas próprias portas bidirecionais de formas diferentes. Caso seja necessário usar a porta bidirecional para entrada de dados, deve-se testar com um indicador lógico ou multímetro, antes para ter certeza de que a porta está no modo bidirecional.

1188..99.. UUssaannddoo aa PPoorrttaa PPaarraalleellaa ppaarraa LLeerr 88 BBiittss..

Caso o PC em uso não disponha de uma porta bidirecional, ainda poderão ser lidos até 9 bits, num determinado intervalo de tempo. Para fazer isto se podem usar as 5 linhas de entrada da porta de Status e 4 linhas da porta de Controle (open collector).

U1A74LS05N

2 1

U1B74LS05N

4 3

U1C74LS05N

6 5

U1D74LS05N

8 9

11_BUSY

10_ACK

12_PAPER_OUT

13_SELECT

17_SELECT_PRINT_NEG

16_INIT

14_AUTO_LINEFEED

1_STROBE

D7

D6

D5

D3

D2

D4

D0

D1

Figura 18-4 – Usando a porta paralela para ler oito bits

As entradas da porta paralela foram escolhidas de forma a facilitar o seu uso. O sinal Busy aparece como sendo o bit mais significativo da porta de Status (bit 7), seguindo em ordem descendente o sinal Ack, Paper Out e Select completando o nibble mais significativo desta porta. As barras são usadas para representar que as entradas são invertidas pelo hardware, i.e. +5V será colocado como 0 no registrador, enquanto que 0V será colocado como 1. A porta de Status somente possui uma entrada invertida.

A porta de Controle é usada para ler o nibble mais significativo. Como descrito anteriormente, a porta de controle possui saídas open collector, i.e. dois possíveis estados, alta impedância e 0V. Se forem conectadas as entradas diretamente à porta (por exemplo, um conversor analógico-digital National ADC0804 com saídas em totem pole), resultará em um conflito se a entrada estiver em nível alto e a porta tentar colocar este pino para o nível baixo. Para evitar este problema devem ser usados inversores open collector, embora isto não é inteiramente necessário. Caso sejam conectadas chaves simples com resistores de pull-up, não será necessário o uso de circuitos open collector.

Se o software inicializa a porta de controle com o valor xxx0100, de forma que todos os pinos de controle ficam em nível alto, o circuito open collector pode não ser

Page 318: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

296296296296

18181818

necessário. No caso em que o dispositivo seja conectado na porta paralela antes que o software inicializar a porta de controle, poderão não ocorrer o funcionamento esperado.

Outro inconveniente a ser contornado é o da existência dos resistores de pull-up da porta de controle. Freqüentemente são utilizados resistores de 4.7K. Para poder colocar o pino em nível baixo, o dispositivo periférico deverá fornecer uma corrente de pelo menos 1 mA. Caso a interface possuir resistores de 1K, serão necessários 5 mA. Por causa deste problema e dos outros, recomenda-se o uso de inversores open collector como mostra a Figura 18-4.

Os inversores open collector foram escolhidos no lugar de buffers open collector, porque são mais fáceis de obter. Não há motivos pelos quais os buffers não possam ser utilizados da mesma forma. Uma outra possibilidade é o uso de transistores TJBs ou FETs.

A entrada D3 está conectada através do inversor na linha Select Printer. Este sinal aparece no bit 3 da porta de Controle. D2, D1 e D0 estão conectadas às linhas Init, Auto linefeed e Strobe, respectivamente, formando o nibble menos significativo. Para ler os oito bits de entrada, o byte deve ser montado pelo software.

A primeira coisa a fazer seria escrever xxxx0100 na porta de controle. Isto colocará todos as linhas da porta de controle em nível alto, de forma que possam ser forçadas para nível baixo pelos pinos de entrada. #define BASE 0x278 #define STATUS BASE+1 #define CONTROL BASE+2 outportb(CONTROL, inportb(CONTROL) & 0xF0 | 0x04);

Uma vez feito isto, o nibble mais significativo poderá ser lido. Uma operação similar, mas não idêntica deverá ser implementada para a leitura do nibble mais significativo da porta de Status. Neste caso será efetuada uma operação AND com a constante 0xF0, de forma que o nibble menos significativo fique em zero. O sinal Busy é invertido, mas poderá ser modificado mais tarde, junto com o bit de Init que também é invertido. Assim pode ser lido o registrador de Status. a = (inportb(STATUS) & 0xF0); /* Leitura do nibble mais significativo do Status */

Agora se pode ler o nibble menos significativo da porta de Controle. O nibble mais significativo não interessa e portanto pode-se efetuar uma operação AND com a constante 0x0F para zerar este nibble. Uma vez feito isto, pode-se combinar os dois bytes num só, usando um operador OR. Posteriormente8 poderão ser invertidos os bits Init e Busy (bits 2 e 7) numa única instrução XOR com a constante 0x84. a = a |(inportb(CONTROL) & 0x0F); /* Leitura do nibble mais significativo do Controle */ a = a ^ 0x84; /* Inverte os bits 2 e 7 */

1188..99..11.. MMooddoo NNiibbbbllee O modo Nibble é a forma recomendada de ler oito bits de dados sem colocar a

porta no modo reverso e usando as linhas de dados. O modo Nibble utiliza um multiplexador de dois para um, para ler um nibble de um dado ao mesmo tempo. Então,

8 Algumas portas de Controle não são open collector, mas tem saídas totem pole. Este é o caso de portas EPP e ECP. Normalmente quando a porta for configurada para estes modos, a porta de Controle ficará com saídas totem pole. Não se pode antecipar o comportamento neste caso. Recomenda-se a utilização do circuito mostrado na seção que segue, onde deve ser feita uma leitura para cada nibble, usando um multiplexador, um bit de saída da porta de controle e a porta de Status como entradas.

Page 319: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

297297297297

18181818

ele é chaveado para o outro nibble e o lê. O software pode então construir o byte composto os dois nibbles. A única desvantagem desta técnica é a de ser mais lenta. Este método requer de poucas instruções de I/O para ler um byte.

11_BUSY

10_ACK

12_PAPER_OUT

13_SELECT

1_STROBE

D7

D6

D5

D3

D2

D4

D0

D1

U2

1Y4

2Y7

3Y9

4Y12

1A2

1B3

2A5

2B6

3A11

3B10

4A14

4B13

~A/B1

~G15

74LS157N

Figura 18-5 – Oito entradas utilizando um multiplexador 74LS157

A operação do 74LS157, que é um conjunto de quatro multiplexadores duas linhas para uma é bastante simples. Quando a entrada A/B está em nível baixo, as entradas A são selecionadas. Por exemplo, 1A é passada para 1Y, 2A passa para 2Y, etc.. Quando A/B estiver em nível alto, as entradas B serão as selecionadas. As saídas Y estão conectadas à porta de status da paralela, de tal forma que o seu valor estará representado no nibble mais significativo do registrador de status.

Para utilizar este circuito primeiro deve-se inicializar o multiplexador para chavear ambas entradas A e B. Então poderá ser lido o nibble menos significativo, colocando o pino A/B em nível baixo. O sinal de strobe é invertido pelo hardware, assim o bit 0 da porta de controle deverá ser setado para ter um nível baixo no pino 1. outportb(CONTROL, inportb(CONTROL) | 0x01); /*Seleciona o Nibble menos signif. (A)*/

Quando o nibble menos significativo for selecionado, então pode ser lido no nibble menos significativo da porta de status. Deve-se lembrar que a linha Busy é invertida. A informação está armazenada no nibble mais significativo do registrador, e poderá ser efetuada uma operação AND com 0xF0, para limpar o nibble menos significativo. a = (inportb(STATUS) & 0xF0); /* Le o nibble baixo */

O valor armazenado deverá ser deslocado de um nibble para formar o nibble menos significativo da variável a. a = a >> 4; /* Desloca 4 Bits para a direita */

Uma vez feito isto, então deverá ser capturado o nibble mais significativo, selecionando as entradas B do multiplexador. Assim poderá ser lindo o nibble mais significativo, reconstruindo o byte. outportb(CONTROL, inportb(CONTROL) & 0xFE); /* Seleciona o nibble alto (B)*/ a = a |(inportb(STATUS) & 0xF0); /* Lê o nibble alto */ byte = byte ^ 0x88;

A última linha chaveia os dois bits invertidos que foram lidos na linha Busy. Esta linha também acrescenta um delay no processo de leitura, que pode ser necessário caso aconteçam resultados incorretos.

Page 320: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

298298298298

18181818

1188..1100.. UUssaannddoo aa IInntteerrrruuppççããoo ddaa PPoorrttaa PPaarraalleellaa

A requisição de interrupção da porta paralela não é usada nas rotinas de impressão no DOS nem no Windows. As interrupções são interessantes quando deve-se efetuar a monitoração de dispositivos tais como geradores de alarmes, quando não é conhecido o instante em que estes serão ativados. É mais eficiente usar requisições de interrupções do que fazer polling de software na porta de forma regular, para saber se alguma coisa mudou. É sempre melhor deixar o computador ser avisado do evento, e deixá-lo livre para a execução de outras tarefas, como num sistema multitarefa por exemplo.

A requisição de interrupção da porta paralela é normalmente a IRQ 5 ou IRQ 7. Também pode ser possível que estas interrupções estejam desabilitadas na própria interface. Estas interrupções podem ser habilitadas usando o bit 4 do registrador de controle, Enable IRQ Via Ack Line. Uma vez habilitado, a interrupção irá ocorrer cada vez que haja uma transição para o nível alto no pino nAck. Ainda que, dependendo da placa, a interrupção poderá ser gerada na transição do nível alto para o nível baixo.

O código que segue é um testador de polaridade de interrupção, que serve para duas efetuar duas tarefas. Primeiro determina qual a polaridade da interrupção da porta paralela, e ainda mostra um exemplo de uso da interrupção desta porta. Este programa verifica se a interrupção é gerada na borda de subida ou de descida do sinal na linha nAck. Para usar este programa, simplesmente deve-se conectar uma das linhas de dados (pinos 2 a 9) no pino Ack (pino 10). /* Verificador de polaridade da interrupção */ #include <dos.h> #define PORTADDRESS 0x378 /* Colocar o endereço da porta aqui */ #define IRQ 7 /*número da IRQ */ #define DATA PORTADDRESS+0 #define STATUS PORTADDRESS+1 #define CONTROL PORTADDRESS+2 #define PIC1 0x20 #define PIC2 0xA0 int interflag; /* Flag de interrupção */ int picaddr; /*Endereço de base do Programmable Interrupt Controller (PIC) */ void interrupt (*oldhandler)(); void interrupt parisr() /*Rotina de atendimento - Interrupt Service Routine (ISR) */ interflag = 1; outportb(picaddr,0x20); /* Fim da interrupção (EOI) */ void main(void) int c; int intno; /* Número do vetor de interrupções */ int picmask; /* Máscara do PIC */ /* Calcula o vetor de interrupção, endereço do PIC e a máscara. */ if (IRQ >= 2 && IRQ <= 7) intno = IRQ + 0x08; picaddr = PIC1; picmask = 1; picmask = picmask << IRQ; if (IRQ >= 8 && IRQ <= 15) intno = IRQ + 0x68; picaddr = PIC2; picmask = 1; picmask = picmask << (IRQ-8); if (IRQ < 2 || IRQ > 15) printf("IRQ Fora da faixa – Valores válidos de 0 a 15\n"); exit();

Page 321: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

299299299299

18181818

outportb(CONTROL, inportb(CONTROL) & 0xDF); /* Assegura que a porta está configurada como saída */ outportb(DATA,0xFF); oldhandler = getvect(intno); /* Salva o vetor de interrupções original */ setvect(intno, parisr); /* Seta o novo vetor de interrupções */ outportb(picaddr+1,inportb(picaddr+1) & (0xFF - picmask)); /* desmascara o PIC */ outportb(CONTROL, inportb(CONTROL) | 0x10); /* Habilita a interrupção da porta paralela */ clrscr(); printf("Testador de polaridade de interrupção da porta paralela\n"); printf("IRQ %d : INTNO %02X : PIC Addr 0x%X : Mask 0x%02X\n",IRQ,intno,picaddr,picmask); interflag = 0; /* Reseta o flag de interrupção */ delay(10); outportb(DATA,0x00); /* Transição de descida */ delay(10); /* Esperar */ if (interflag == 1) printf("A interrupção ocorreu na descida do pino ACK.\n"); else outportb(DATA,0xFF); /* Transição de subida */ delay(10); /* esperar */ if (interflag == 1) printf("A interrupção ocorreu na subida do ACK.\n"); else printf("Não ocorreu interrupção. \n Verifique o número de IRQ, o endereço da porta e a conexão"); outportb(CONTROL, inportb(CONTROL) & 0xEF); /* Desabilita a interrupção */ outportb(picaddr+1,inportb(picaddr+1) | picmask); /* Mascara o Pic */ setvect(intno, oldhandler); /* Restaura o vetor de interrupções original antes de sair */

Código 18-4

Em tempo de compilação poderão ser gerados alguns warnings, mas não criam um problema geral. Para entender o exemplo acima, deve-se ter um conhecimento básico de como funcionam as interrupções e os serviços de atendimento às interrupções9.

A primeira parte da rotina principal calcula o Vetor de Interrupção, endereço do PIC e a máscara, de forma a poder utilizar a interrupção da porta paralela. Depois que a rotina de atendimento a interrupção (ISR) tiver sido setada, assim como o PIC, poderá ser habilitado a interrupção da porta paralela. Isto pode ser feito setando o bit 4 do registrador de controle da porta (Control Register) utilizando por exemplo: outportb(CONTROL, inportb(CONTROL) | 0x10);

Antes de habilitar a interrupção, deve-se escrever 0xFF na porta paralela, para colocar as 8 linhas de dados num nível conhecido. Neste ponto do programa, todos as linhas de dados devem estar em nível alto. A rotina de atendimento a interrupção simplesmente seta um flag (interflag), de forma que se pode determinar quando ocorreu uma IRQ. A partir de então, pode-se escrever 0x00 na porta de dados, de foram a ocasionar uma transição de nível alto para o baixo no sinal Acknowledge (confirmação) da porta paralela, que está conectado a uma das linhas de dados.

Se a interrupção ocorrer na transição de nível alto para o baixo, o flag (interflag) deverá ficar setado.

Pode-se testar o programa e ver como o mesmo informa ao usuário. De qualquer forma se o flag não for setado, então a interrupção ainda não tem acontecido. Podes-se então escrever 0xFF na porta de dados, o que ocasionará uma transição de nível baixo para

9 Maiores detalhes, veja o capítulo 17.

Page 322: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

300300300300

18181818

o alto na linha nAck, e verificar novamente o se o flag de interrupção tem sido setado. Se ficar setado, então a interrupção ocorre nas transições de nível baixo para o alto.

Se mesmo assim, o flag de interrupção não tiver sido setado, então se supõe que a interrupção não está funcionando. Deve-se verificar se a IRQ e o endereço Base são os corretos, além de verificar o hardware.

1188..1111.. MMooddooss ddaa PPoorrttaa PPaarraalleellaa nnoo BBIIOOSS

A maioria das portas paralelas é do tipo multímodo. Normalmente são configuráveis por software para um dos modos, a partir da BIOS. Os modos típicos são: Modo Printer Mode (Às vezes chamados de modo Default ou Normal Mode) Modos Standard & Bidirectional (SPP) Modos EPP e SPP Modos ECP Modos ECP e EPP

O modo Printer Mode é o modo mais básico. Coloca a porta paralela padrão no modo unidirecional (forward), desabilitando a característica bidirecional da porta, de forma que o bit 5 da Control Port, não irá responder.

Os modos Standard e Bi-directional (SPP) são bidirecionais. Usando este modo, o bit 5 do Control Port possuirá a direção reversa da porta, de forma que pode ser lido um valor das linhas de dados.

O modo EPP e SPP são uma combinação dos modos EPP 1.7 (Enhanced Parallel Port) e SPP. Neste modo de operação pode-se ter acesso aos registradores SPP (Data, Status e Controle) e acessar os registradores EPP. Este modo possibilita a direção reversa da porta usando o bit 5 do registrador de controle, e acesso ao bit de Timeout do EPP..

O modo ECP implementa uma porta com capacidade estendida (Extended Capabilities Port). Este modo pode ser setado utilizando o registrador de controle do ECP, o Extended Control Register (ECR).

Os modos ECP e EPP fornecem uma porta com capacidade estendida, parecida com o modo anterior, embora o modo EPP no registrador ECR do ECP não estará disponível.

Os modos acima são configuráveis através do BIOS. Estes podem ser reconfigurados pelo uso do próprio software, mas isto não é recomendado. Os registradores de software encontram-se tipicamente nos endereços 0xFA, 0x3F0, 0x3F1, etc., e foram pensados para serem acessados pelo BIOS. Não há um grupo de padrões para estes registradores de configuração, de forma que o se o novo software usar estes registradores, pode-se perder a portabilidade. Com os novos sistemas operacionais multitarefa de hoje em dia, não seria uma boa idéia mudar estes registradores, podendo gerar efeitos colaterais.

A melhor opção é selecionar o modo ECP e EPP a partir da BIOS e então utilizar os registradores ECP para selecionar o modo preferido através do software.

Page 323: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

301301301301

18181818

1188..1122.. MMooddooss ddaa PPoorrttaa PPaarraalleellaa ee EExxtteennddeedd CCoonnttrrooll RReeggiisstteerr ddoo mmooddoo EECCPP

Como discutido nas seções anteriores, a melhor forma de proceder é a de setar a Porta Paralela no modo ECP10 e EPP, e utilizar o registrador ECR11 para selecionar os diferentes modos de operação.

Quando for setado o modo ECP, um novo grupo de registradores ficará disponível no endereço Base + 0x400h. O registrador ECR está mapeado no endereço Base + 0x402. Deve ser ressaltado que os registradores ECP não estarão disponíveis para portas cujo endereço base seja 0x3BCh.

1188..1122..11.. BBiitt FFuunnccttiioonn A tabela a seguir mostra os bits do ECR. O interesse maior neste momento é nos

três bits mais significativos que selecionam o modo de operação. Existem 7 modos possíveis de operação, mas nem todas as portas suportam todos os modos. O modo EPP é um exemplo, não estando disponível em algumas portas.

Bits Função Seleciona o modo de operação. 7:5 000 Modo Standard

001 Modo Byte 010 Modo FIFO 011 Modo ECP FIFO 100 Modo EPP 101 Reservado 110 Modo FIFO Test 111 Modo de Configuração 4 Bit de interrupção ECP 3 Bit de habilitação de DMA 2 Bit de serviço de ECP 1 FIFO cheia 0 FIFO vazia

Tabela 18-7 - ECR – Registrador de Controle Estendido

1188..1122..22.. MMooddooss ddee OOppeerraaççããoo

OO mmooddoo SSttaannddaarrdd

A seleção deste modo força a porta ECP a se comportar como uma porta padrão (Standard Parallel Port), sem a capacidade de bidirecionalidade.

MMooddoo BByyttee // PPSS//22

Nesta seleção, comporta-se como uma SPP em modo bidirecional. O bit 5 colocará a porta no modo reverso.

10 Os registradores ECP são padronizados pela Microsoft, como especificado no documento Extended Capabilities Port Protocol and ISA Interface Standard, de forma que não existirá o problema de que cada fabricante possua o seu próprio conjunto de registradores. (www.microsoft.com) 11 ECR: Extended Control Register.

Page 324: Tratado Da Linguagem c

I N T E R F A C E P A R A L E L A

302302302302

18181818

MMooddoo FFIIFFOO

Neste modo, qualquer dado escrito na memória FIFO de dados, será enviada para o periférico utilizando o handshake SPP. O hardware gerará o handshaking requerido. Muito útil com dispositivos não ECP tais como impressoras. Podem ser utilizadas algumas das característica ECP tais como os buffers FIFO e a geração do handshaking SPP (Centronics) no lugar do handshake.ECP.

MMooddoo EECCPP FFIIFFOO

É o modo padrão de utilização do modo ECP. Estes modo utiliza o handshake ECP. Quando no modo ECP através do BIOS e o registrador ECR estiver setado para o modo ECP FIFO (011), os registradores SPP podem desaparecer.

MMooddoo EEPPPP //RReesseerrvveedd

Esta seleção habilita o modo EPP, se disponível. Sob o BIOS, se o modo ECP for setado então é provável que este modo não seja uma opção. Ainda se o BIOS estiver setado para o modo ECP e EPP, então o modo EPP será habilitado.

MMooddoo FFIIFFOO TTeesstt

Neste modo, qualquer dado escrito no registrador Test FIFO, será colocado na FIFO e qualquer dado lido do mesmo registrador será lido do buffer FIFO. Os bits de status, Full e Empty, indicarão os seus valores verdadeiros, de forma que o espaço da FIFO, entre outras coisas, pode ser determinado neste modo.

MMooddoo ddee CCoonnffiigguurraaççããoo

Neste modo, dois registradores de configuração cnfgA e cnfgB ficam disponíveis para o uso. Caso esteja em uso o modo ECP sob o BIOS, ou a placa estiver ajustada para utilizar ECP, então pode ser uma boa opção inicializar a porta no modo pré-definido antes de utiliza-la. Caso esteja usando o modo SPP, então a porta pode ser setada no modo Standard, como primeira coisa a fazer. Não dá para assumir que a porta já esteja no modo SPP.

Em alguns modos, os registradores SPP podem desaparecer ou não funcionar corretamente. Se o programa estiver usando o modo SPP, então deve-se selecionar o ECR para o modo Standard (SPP).

Page 325: Tratado Da Linguagem c

303303303303 Luis Fernando Espinosa Cocian

1199.. IINNTTEERRFFAACCEESS SSEERRIIAAIISS As interfaces seriais são aquelas que podem transmitir somente um único bit de

cada vez. Informações mais complexas são compreendidas pela retenção de conjuntos destes bits que são enviados em série, um após o outro.

A maioria das portas serial dos PCs utiliza os padrões RS-232C ou RS-422. Uma porta serial é uma interface genérica que pode ser utilizada por qualquer tipo de dispositivo, incluindo modems, mouses1 e impressoras.

1199..11.. IInnttrroodduuççããoo

A porta serial é mais trabalhosa de interfacear comparada com a porta paralela, a pesar disto possui as suas vantagens. Na maioria dos casos, qualquer dispositivo conectado na porta serial precisará converter a transmissão serial em paralela, para poder utilizar os dados. Isto pode ser feito utilizando uma UART2.

A porta serial possui muitos mais registradores de controle que a porta paralela padrão (SPP). Algumas vantagens da transferência de dados de forma serial são descritas a seguir. 1. Os cabos seriais podem ser mais longos (quilômetros) que os de comunicação paralela

(metros). A porta serial padrão RS-232 transmite um ‘1’ como um sinal de –3V a –25V, e um ‘0’ como um sinal de +3V a +25V, enquanto que a porta paralela transmite um ‘0’ como 0V e ‘1’ como +5V. Assim a porta serial pode ter uma faixa de variações de 50V, enquanto a porta paralela de 5V. Desta forma as perdas no cabo não serão um grande problema para cabos seriais em comparação com os cabos para comunicação paralela. Esta diferença entre as tensões para os níveis lógicos tende a atenuar o efeito de interferências eletromagnéticas nos cabos de comunicação.

2. A comunicação serial precisa de menos fios que a paralela. Caso um dispositivo precise ser colocado a uma longa distância do computador, então um cabo de três fios (Configuração Null Modem) tende a ficar muito mais econômico que um outro de 19 ou 25 fios. Embora deva ser levado em consideração o custo do interfaceamento em cada ponta do sistema de comunicação.

3. Os dispositivos infravermelhos de comunicação são basicamente interfaces seriais. Estas interfaces podem ser vistas na maioria das agendas eletrônicas, computadores palmtops e notebooks e telefones celulares. Nestes dispositivos, um bit é enviado a cada instante. A primeira especificação de interfaces infravermelhas padronizadas IrDA-1, suporta uma taxa de 115.2 KBaud sendo interfaceada com uma UART. A duração de um pulso desta interface, foi reduzido a 3/16 avos da duração de um bit RS-232 com o objetivo de poupar o consumo de energia, considerando que estes dispositivos são utilizados de forma muito freqüente em sistemas portáteis alimentados por baterias.

1 No inglês, o plural de “mouse” é “mice”. 2 USART: Universal Synchronous Asynchronous Receiver Transmitter.

Capítulo

19

Page 326: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

304304304304

19191919

4. Vários tipos de microcontroladores também possuem interfaces seriais embutidas, chamadas de SCI3, que podem ser conectadas com qualquer dispositivo serial. A comunicação serial diminui o número de pinos necessários para a transferência de dados. Usualmente são utilizados somente dois pinos, um para a transmissão (TXD) e um para a recepção (RXD), comparada com o mínimo de oito pinos que usa o método paralelo, mas tudo isto dependerá do padrão de transmissão serial escolhido e da forma de controle desejada.

1199..22.. PPrroopprriieeddaaddeess ddee HHaarrddwwaarree ddooss PPCCss

Os dispositivos que utilizam comunicação serial podem ser divididos em duas categorias: DCE4 e DTE5. Os equipamentos DCE são dispositivos tais como os modems, plotters, etc., enquanto que os DTEs seriam um computador ou um terminal.

As especificações elétricas da porta serial estão contidas no padrão EIA6 RS-232C. Esta norma define alguns parâmetros tais como7: 1. Um "Space" (espaço ou 0 lógico) é compreendido entre +3 e +25 Volts. 2. Um "Mark" (marca ou 1 lógico) é compreendido entre -3 e -25 Volts. 3. A região entre +3 e -3 Volts é indefinida. 4. Um circuito aberto nunca deverá exceder de 25 Volts (em relação a GND). 5. A corrente de curto-circuito não deverá exceder de 500mA. O driver deverá ser capaz de

suportar esta corrente sem se danificar.

Os conectores das portas seriais são padronizados em dois tamanhos. O conector DB-25 e o DB-9., ambos conectores machos, estes inseridos na parte traseira do PC. Isto faz com que os dispositivos periféricos sejam do mesmo tamanho, mas do tipo fêmea. Os novos dispositivos USB também são dispositivos seriais, mas podem utilizar outro tipo de conectores tais como o mini-DIN. A seguir é mostrada a tabela com a conexão dos pinos para os conectores DB-9 e DB-25.

1199..22..11.. CCoonneeccttoorreess SSeerriiaaiiss ((DDBB--2255 ee DDBB--99)) A pinagem dos conectores seriais DB9 e DB25 é mostrada a seguir, na Tabela 19-1.

Conector Tipo DB25 - Número do Pino

Conector Tipo DB9 - Número do Pino

Abreviação Nome

Pino 2 Pino 3 TD Transmit Data (dados de transmissão) Pino 3 Pino 2 RD Receive Data (dados de recepção) Pino 4 Pino 7 RTS Request to Send (requisição para envio) Pino 5 Pino 8 CTS Clear to Send (Pronto para enviar) Pino 6 Pino 6 DSR Data Set Ready (Conjunto de dados

pronto) Pino 7 Pino 5 SG Signal Ground (Terra de sinal) Pino 8 Pino 1 CD Carrier Detect (Detetor de portadora) Pino 20 Pino 4 DTR Data Terminal Ready (Terminal de

dados pronto) Pino 22 Pino 9 RI Ring Indicator (Indicador de toque)

Tabela 19-1 – Pinagem dos conectores seriais DB25 e DB9

3 SCI: Serial Communication Interface. 4 DCE: Data Communication Equipment 5 DTE: Data Terminal Equipment. 6 EIA: Electronics Industry Association. 7 Os itens aqui descritos não mostram a especificação completa do padrão EIA. Para maiores informações consultar a norma EIA RS-232C-E.

Page 327: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

305305305305

19191919

1199..22..22.. FFuunnççõõeess ddooss PPiinnooss As funções dos pinos dos conectores seriais DB9 e DB25 é mostrada a seguir, na

Tabela 19-2. Abreviação Nome Função TD Transmit Data (dados de

transmissão) Dados de saída serial (TXD)

RD Receive Data (dados de recepção) Dados de entrada serial (RXD) RTS Request to Send (requisição para

envio) Indica ao modem que a UART está pronta para intercambiar dados.

CTS Clear to Send (Pronto para enviar) Indica que o Modem está pronto para intercambiar dados

DSR Data Set Ready (Conjunto de dados pronto)

Informa a UART que o modem está pronto para estabelecer uma conexão

CD Carrier Detect (Detector de portadora)

Quando o modem detecta a portadora proveniente de um modem localizado na outra extremidade da linha telefônica, esta linha fica ativa

DTR Data Terminal Ready (Terminal de dados pronto)

Funciona de forma oposta a DSR. Indica ao modem que a UART está pronta para a conexão

RI Ring Indicator (Indicador de toque) Fica ativo quando o modem detecta o sinal da campainha do terminal telefônico

Tabela 19-2 - Funções dos Pinos num canal serial padrão

1199..33.. NNuullll MMooddeemmss

Um Null Modem é utilizado para conectar dois dispositivos DTE8 entre si. Isto é comumente utilizado para a troca de arquivos entre computadores utilizando protocolos tais como Zmodem ou Xmodem, por exemplo.

Na realidade um Null Modem equivale a uma conexão sem modem, já que os dados não serão modulados nem demodulados para a transmissão, mas serão transmitidos de forma digital direta. A forma de conexão dos cabos e pinos é mostrada na Figura 19-1. Usualmente são necessários unicamente os sinais de RXD, TXD e a referência, no caso de não ser utilizado nenhum protocolo de hardware.

Figura 19-1 – Diagrama de conexão de um Null Modem

Este tipo de conexão requer somente de três fios (TD, RD e SG – que são o TxD, RxD e a referência do sinal) a serem conectados, implementando a solução mais econômica para cabos longos. A teoria de operação é bastante simples. O objetivo é fazer com que o computador pense que está se comunicando com um modem no lugar de um outro computador. Qualquer dado enviado a partir do primeiro computador será recebido no segundo, já que o sinal TxD do primeiro computador é conectado ao pino RxD do

8 DTE: Data Terminal Equipment

Page 328: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

306306306306

19191919

segundo computador e vice-versa. As referências de sinal (SG) devem estar conectadas entre si.

O pino DTR9 é conectado com o DSR10 e o CD11 em ambos computadores. Quando o DTR é ativado, então o DSR e CD automaticamente ficarão também ativados. Desta forma o computador assumirá que o modem virtual ao qual está conectado, está pronto e já tem detectado a portadora do outro modem.

Uma vez que a comunicação serial padrão dos computadores pessoais é assíncrona, a velocidade de comunicação entre os equipamentos deve ser a mesma, e caso os pinos RTS e CTS estiverem conectados entre si, não haverá necessidade de controlar o fluxo de informações. Quando o computador precisa enviar um dado, ele seta o sinal RTS para nível alto, setando também o sinal CTS pela conexão, simulando que o modem remoto está pronto para receber o dado.

Deve-se notar que o sinal RI12 não é conectado. Esta linha é somente usada para indicar ao computador que á um sinal de chamada na linha telefônica. Como não existe um modem conectado à linha telefônica então deverá ficar desconectado.

1199..33..11.. CCoonneexxããoo LLooooppBBaacckk A conexão loopback pode ser extremamente útil para testar programas de

comunicação serial RS-232. Ele permite receber e transmitir dados no mesmo canal serial, já que as linhas de transmissão e recepção ficam conectadas entre si. Um dado transmitido pela porta serial será imediatamente recebido pela mesma porta. Caso seja conectado este conector à porta serial e carregado o programa HyperTerminal13, qualquer tecla apertada será transmitida e recebida no canal configurado e mostrado no monitor de vídeo.

É importante notar que a conexão loopback mostrada a seguir não é apropriada para o uso com programas de diagnóstico. Para estes programas especiais, podem ser necessárias conexões diferentes às apresentadas nesta seção e que podem variar de programa para programa.

Figura 19-2 – Diagrama de conexão Loopback

9 Data Terminal Ready. 10 Data Set Ready. 11 Carrier Detect. 12 Ring Indicator. 13 O programa HyperTerminal é distribuído com o sistema operacional MS Windows.

Page 329: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

307307307307

19191919

1199..33..22.. VVeelloocciiddaaddeess DDTTEE // DDCCEE Um DTE típico é um computador, e um DCE típico é um modem. As velocidades

de comunicação entre um DTE e um DCE são conhecidas como “velocidade DTE para DCE”. A velocidade de comunicação entre DCEs é conhecida como “velocidade DCE para DEC” ou velocidade de conexão.

Muitos equipamentos possuem modems de 33.6Kbps ou 56Kbps. Desta forma se espera que a velocidade entre DCEs seja de 33.6Kbps ou 56Kbps respectivamente. Considerando a máxima velocidade do modem, pode-se esperar que a velocidade de DTE para DCE seja de até 115200 bps (máxima velocidade da UART 16C550A), muito mais rápida que a velocidade entre DCEs.

Os modems mais modernos efetuam a compressão dos dados a serem enviados de forma interna. As taxas de compressão chegam a 4:1 ou maiores para arquivos no formato texto. Caso desejar-se transmitir um arquivo texto com uma taxa de 28.8K (DCE para DCE), então o modem o compactará (numa relação de quatro) e estará transferindo o arquivo a 115.2 Kbps entre computadores e portanto tem uma velocidade equivalente de DCE para DTE, de 115.2Kbps

Isto mostra o motivo pelo qual a velocidade DCE para DTE pode ser muito maior que a velocidade de conexão do modem. Alguns fabricantes de modems conseguem taxas de compressão de 1:8. Por exemplo, para um modem de 33.6Kbps poderá ter uma taxa de transferência máxima de 268000 bps entre o modem e a UART. Se o hardware do computador utilizar uma UART 16C550A cuja taxa máxima é de 115200 bps, então o desempenho será limitado nesta velocidade. A solução para este problema será a utilização de uma UART 16C650 que tem uma taxa máxima de 230400 bps.

Em alguns casos, por exemplo tentando enviar um arquivo já comprimido, o modem pode levar mais tempo tentando compactá-lo novamente, de forma que a velocidade de transmissão pode ficar mais lenta que a velocidade de conexão. Caso isto ocorra, deverá ser desabilitada a compressão de dados, na configuração do modem. Alguns arquivos são mais fáceis de compactar que outros, e os arquivos mais fáceis e rápidos de compactar normalmente possuem taxas de compressão mais elevadas.

1199..44.. CCoonnttrroollee ddee FFlluuxxoo -- FFllooww CCoonnttrrooll

Ainda que a velocidade do DTE para DCE é muitas vezes mais rápida que a velocidade de DCE para DCE, o PC enviará os dados para o modem a uma velocidade de 115200 bps, por exemplo. Caso estes dados sejam enviados sem controle, o buffer poderá lotar. Para que isto não aconteça é necessário usar o controle de fluxo. O controle de fluxo pode ser feito via software ou via hardware.

O controle de fluxo por software, às vezes chamado de Xon/Xoff, usa dois caracteres. Xon é normalmente indicado pelo caractere ASCII 17, e o Xoff pelo ASCII 19. O modem possui um buffer pequeno, de forma a que quando estiver cheio, envia um caractere Xoff para indicar ao computador para não enviar mais dados. Este tipo de controle de fluxo tem a vantagem de não requerer mais fios já que os caracteres são enviados através das linhas TxD e RxD. Cada caractere requer usualmente de 10 bits, e em conexões muito lentas, este controle pode diminuir bastante o desempenho.

Page 330: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

308308308308

19191919

O controle de fluxo por hardware é conhecido como controle de fluxo RTS/CTS. Este utiliza dois fios do cabo serial no lugar de caracteres extras transmitidos nas linhas de dados. Desta forma o controle de hardware não diminui a taxa de comunicação. Quando o computador precisar enviar dados, ativará a linha RTS. Se o modem tem espaço para aceitar este dado, então responde ativando o sinal CTS e o computador começa a enviar os dados. Se o modem não tem espaço para novos dados então não ativará a linha CTS.

1199..55.. AA UUAARRTT ((88225500 ee CCoommppaattíívveeiiss))

Uma UART (Universal Asynchronous Receiver Transmitter) é um circuito integrado que efetua a comunicação serial entre equipamentos digitais. Os circuitos integrados encontrados na maioria dos PCs incluem a família Intel 8250 e que são as UARTs 16450, 16550, 16650, e 16750 UARTs.

Figura 19-3 – Pinagem das UARTS 16550, 16450 e 8250

A UART 16550 é compatível com 8250 e 16450. As diferenças estão na implementação de duas novas funções nos pinos 24 e 29, chamados de Transmit Ready e Receive Ready (TXRDY e RXRDY), que podem ser implementados com a interface DMA (Direct Access Memory). Estes pinos possuem dois modos diferentes de operação, o Modo 0 suporta transferências únicas de DMA, enquanto que o Modo 1 suporta multi-transferência.

O Modo 0 é também conhecido como modo 16450. Este modo é selecionado quando os buffers FIFO forem desabilitados através do bit 0 do Registrados de Controle FIFO, ou quando os buffers da FIFO (memória do tipo First In – First Out) estão habilitados mais o modo de seleção de DMA for igual a zero (DMA Mode Select – bit 3 do registrador FCR). Neste modo RXRDY é ativado em nível baixo quando pelo menos um caractere (byte) estiver presente no buffer de recepção. RXRDY ficará inativo em nível alto quando não há mais caracteres no buffer de recepção.

TXRDY ficará ativo em nível baixo quando não há mais caracteres no buffer de transmissão. Este ficará desativado em nível alto quando do primeiro caractere (byte) é carregado no buffer de transmissão.

Page 331: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

309309309309

19191919

O Modo 1 é selecionado quando os buffers FIFO estão ativados e o sinal DMA Mode Select estiver em 1. No Modo 1, RXRDY ficará ativo em nível baixo quando o trigger for ativado ou quando existir um erro de Timeout, retornando ao estado inativo quando não existem mais caracteres na FIFO. O sinal TXRDY ficará ativo quando não há mais caracteres no buffer de transmissão e ficará inativo quando o buffer de transmissão estiver completamente preenchido.

Todas as UARTs são compatíveis com os níveis TTL, incluindo as linhas TD, RD, RI, DCD, DSR, CTS, DTR e RTS, embora o uso de conversores de nível para RS-232 seja recomendado. Estes conversores, tais como o receptor DS1489 e o transmissor DS1488, ou os conversores duais MAX232 e DS232, permitem a excursão do sinal entre +12V e –12V. Os conversores RS-232 convertem os sinais TTL em níveis lógicos RS-232. Pino Nome Descrição 1:8 D0:D7 Barramento de Dados 9 RCLK Entrada de clock de recepção. A freqüência desta entrada deverá ser igual

a baudrate * 16 10 RD Recepção de dados 11 TD Transmissão de dados 12 CS0 Chip Select 0 – Ativo quando alto 13 CS1 Chip Select 1 – Ativo quando alto 14 nCS2 Chip Select 2 – Ativo quando baixo 15 nBAUDOUT Baud Output – Saída do gerador de freqüência programável de baudrate.

(Baudrate * 16) 16 XIN External Crystal Input (entrada de cristal externo) – utilizada pelo oscilador

gerador de baudrate 17 XOUT External Crystal Output (Saída de oscilador externo) 18 nWR Write Line (linha de escrita) – Negada 19 WR Write Line – Não negada 20 VSS Conexão ao terra comum 21 RD Read Line (linha de leitura) – Não Negada 22 nRD Read Line (linha de leitura) – Negada 23 DDIS Driver Disable (inabilitação do driver) – Este pino vai para nível baixo

quando a CPU estiver lendo a UART. 24 nTXRDY Transmit Ready (pronto para transmitir) 25 nADS Address Strobe (sinalizador de endereço) – Usado quando os sinais

estiverem instáveis durante os ciclos de leitura ou escrita. – ativo quando baixo

26 A2 Bit 2 de endereço 27 A1 Bit 1 de endereço 28 A0 Bit 0 de endereço 29 nRXRDY Receive Ready (pronto para receber) 30 INTR Interrupt Output (saída de interrupção) 31 nOUT2 Saída 2 do usuário – ativo quando baixo 32 nRTS Request to Send (requisição para envio) – ativo quando baixo 33 nDTR Data Terminal Ready (terminal de dados pronto) – ativo quando baixo 34 nOUT1 Saída 1 do usuário – ativo quando baixo 35 MR Master Reset (reset mestre) 36 nCTS Clear to Send– ativo quando baixo 37 nDSR Data Set Ready (conjunto de dados pronto) – ativo quando baixo 38 nDCD Data Carrier Detect– ativo quando baixo 39 nRI Ring Indicator– ativo quando baixo 40 VDD + 5V

Tabela 19-3 – Pinagem da UART 16550A

As UARTs requerem de um Clock para funcionar. Usualmente são utilizados cristais de 1.8432 MHz ou 18.432 MHz. O cristal é conectado nos pinos XIN e XOUT da UART, utilizando alguns capacitores de filtro14. O clock será utilizado pelo gerador de baudrate programável, que interfaceia com os circuitos internos de transmissão, mas não 14 Para maiores informações dirija-se à National Semiconductors (www.natsemi.com) ou Texas Instruments (www.ti.com).

Page 332: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

310310310310

19191919

diretamente na temporizarão da recepção. Para isto deverá ser feita uma conexão externa a partir do pino 15 (BaudOut) com o pino 9 (Receiver Clock In)15.

1199..66.. TTiippooss ddee UUAARRTTss ((PPCCss))

O integrado 8250 foi o primeiro da série. Ele não contém registradores de uso genérico. O 8250A é uma versão melhorada e que opera de forma mais rápida no barramento. 8250B Muito similar à 8250, somente mais rápido. 16450 usado nos ATs (Melhora a velocidade de acesso no barramento em relação aos 8250s).

Opera facilmente a 38.4 Kbps. 16550 foi a primeira geração de UARTs com buffers integrados. Possuía um buffer de 16 bytes,

sendo substituído pelo 16550A. 16550A é uma UART usada para taxas de comunicação relativamente elevada em modems de

14.4K e 28.8K. Implementam buffers FIFO. 16650 contém uma FIFO de 32 bytes, X-On / X-Off programáveis, e suportam gerenciamento

de energia 16750 produzido pela Texas Instruments. Contém uma FIFO de 64 bytes.

1199..77.. RReeggiissttrraaddoorreess ddaass PPoorrttaass SSeerriiaaiiss ((PPCCss)) –– EEnnddeerreeççooss ddaass PPoorrttaass ee IIRRQQss

A tabela a seguir mostra os endereços de hardware das portas de I/O seriais, e o número das IRQs correspondentes. Estas são encontradas na maioria dos PCs. Caso o computador possua barramentos USB poderá haver mudanças nos endereços e IRQs. De forma análoga às portas paralelas, os endereços para as portas COM podem ser lidos da área de dados da BIOS.

Nome Endereço IRQ

COM 1 3F8 4

COM 2 2F8 3

COM 3 3E8 4

COM 4 2E8 3

Tabela 19-4 – Endereços padronizados da porta serial

A Tabela 19-5 mostra os endereços nos quais podem ser encontrados os endereços das portas de comunicação (COM) na área de dados da BIOS. Cada endereço ocupa dois bytes.

Endereço Inicial Função

0000:0400 Endereço Base da COM1

0000:0402 Endereço Base da COM2

15 Notar que o sinal de clock deve ser baudrate * 16.

Page 333: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

311311311311

19191919

0000:0404 Endereço Base da COM3

0000:0406 Endereço Base da COM4

Tabela 19-5 – Endereços das portas COM na área de dados da BIOS

O programa que segue mostra como ler estas posições para obter os endereços das portas de comunicação. Este programa funciona de forma adequada quando compilado num compilador para DOS. No caso de compiladores para Windows deverão ser utilizadas as funções específicas da API. #include <stdio.h> #include <dos.h> void main(void) unsigned int far *ptraddr;/* Ponteiro para o endereço da porta */ unsigned int address; /* Endereço da porta */ int a; ptraddr = (unsigned int far *)0x00000400; for (a = 0; a < 4; a++) address = *ptraddr; if (address == 0) printf("Não foi encontrada a porta para COM%d \n",a+1); else printf("O endereço designado para a COM%d é ” “%Xh\n",a+1,address); *ptraddr++;

Código 19-1

1199..88.. TTaabbeellaa ddee RReeggiissttrraaddoorreess

A Tabela 19-6 mostra os registradores associados ao canal serial.

Endereço Base DLAB Leitura/Escrita Abreviação Nome do Registrador

= 0 Escrita - Transmitter Holding Buffer

= 0 Leitura - Receiver Buffer

+0

= 1 Leitura/Escrita - Divisor Latch Low Byte

= 0 Leitura/Escrita IER Interrupt Enable Registrer +1

= 1 Leitura/Escrita - Divisor Latch High Byte

- Leitura IIR Interrupt Identification Register +2

- Escrita FCR FIFO Control Register

+3 - Leitura/Escrita LCR Line Control Register

+4 - Leitura/Escrita MCR Modem Control Register

+5 - Leitura LSR Line Status Register

+6 - Leitura MSR Modem Status Register

+7 - Leitura/Escrita - Scratch Register

Tabela 19-6 – Registradores associados ao canal de comunicação serial

Page 334: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

312312312312

19191919

1199..99.. DDLLAABB

Como pode ser notado na tabela de registradores da seção anterior existe uma coluna chamada DLAB. Quando DLAB for setada em 0 ou 1 alguns registradores mudarão. Assim é que a UART é capaz de ter 12 registradores (incluindo o registrador de uso genérico) somente com oito endereços de porta. DLAB é a abreviação de Divisor Latch Access Bit. Quando DLAB é colocado em 1 através do registrador de controle, dois registradores ficarão disponíveis, a partir dos quais pode ser definida a velocidade da comunicação em bits por segundo.

A UART deverá ter um cristal que deverá oscilar em torno de 1.8432 MHz. A UART incorpora um contador divisor opor 16 que simplesmente divide o sinal de clock por 16. Assumindo a existência de um sinal de clock de 1.8432 MHz, definirá um máximo de 115200 Hz, fazendo deste o limite máximo em bps. Ainda as UARTs são equipadas com um gerador programável de baudrate que é controlado por dois registradores.

Por exemplo, se for desejado comunicar dados numa taxa de 2400 bps, o divisor deverá ser programado para 48 (115200/48 = 2400). O divisor deverá ser armazenado nos dois registradores controlados pelo Divisor Latch Access Bit. Este divisor pode armazenar qualquer valor que possa ser representado em 16 bits. A UART possui um barramento de oito bits de dados. O primeiro registrador (Base + 0) quando DLAB é igual a 1, armazena a parte menos significativa do divisor, e o segundo registrador (Base + 1) quando DLAB é igual a 1, armazena a parte mais significativa do divisor.

A seguir é mostrada na Tabela 19-7 as taxas de transmissão mais comuns e os valores do divisor. Todos os valores do divisor estão apresentados em notação hexadecimal.

Taxa de Transmissão (BPS)

Divisor (em decimal) Divisor Latch High Byte (parte mais significativa do

divisor)

Divisor Latch Low Byte (parte menos

significativa do divisor)

50 2304 09h 00h

300 384 01h 80h

600 192 00h C0h

2400 48 00h 30h

4800 24 00h 18h

9600 12 00h 0Ch

19200 6 00h 06h

38400 3 00h 03h

57600 2 00h 02h

115200 1 00h 01h

Tabela 19-7 - Taxas de transmissão mais comuns e os valores do divisor

Page 335: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

313313313313

19191919

1199..1100.. IInntteerrrruupptt EEnnaabbllee RReeggiisstteerr ((IIEERR))

O registrador de habilitação de interrupções possui uma forma simplificada de funcionamento. Quando o bit 0 for setado para 1 habilitará a geração de interrupção quando houver dados no buffer de recepção prontos para serem lidos pela CPU.

O bit 1 habilita a geração de interrupção quando o buffer de recepção ficar vazio. O bit 2 habilita a geração de interrupção pela linha de status da recepção. A CPU será interrompida quando houver mudanças na linha de status de recepção. De forma parecida, o bit 3 habilita a interrupção de status do modem.

Bit Descrição

7 Reservado

6 Reservado

5 Enables Low Power Mode (habilita o modo de baixo consumo - 16750)

4 Enables Sleep Mode (habilita o modo Sleep -16750)

3 Enable Mode Status Interrupt (habilita a interrupção de status do modem)

2 Enable Receiver Line Status Interrupt (habilita a interrupção pela linha de status de recepção)

1 Enable Transmitter Holding Register Empty Interrupt (habilita a interrupção pelo esvaziamento do registrador de retenção na transmissão)

0 Enable Received Data Available Interrupt (habilita a interrupção na recepção de dados)

Tabela 19-8 – Registrador de habilitação de interrupções

1199..1100..11.. IInntteerrrruupptt IIddeennttiiffiiccaattiioonn RReeggiisstteerr ((IIIIRR)) O registrador de identificação de interrupção é um registrador somente de leitura.

Os bits 6 e 7 fornecem o status do buffer FIFO. Quando ambos bits estão em 0, os buffers estarão ativos. Este deve ser o resultado quando usados os módulos 8250 e 16450. Se o bit 7 estiver ativo, mas o bit 6 não, então a UART tem o seu buffer ativo mas não pode ser usado. Isto ocorre nas UARTs 16550 quando um bug no buffer FIFO deixa a mesma sem poder ser utilizada. Se ambos os bits estiverem em 1 então os buffers estarão habilitados e operacionais.

Os bits 4 e 5 são reservados. O bit 3 mostra o status da interrupção de timeout nos integrados 16550 e posteriores.

O bit 0 mostra se ocorreu uma interrupção. Se uma interrupção ocorreu, o seu status é mostrado pelos bits 1 e 2. Estas interrupções funcionam com prioridades. A interrupção de status de linha tem a maior prioridade, seguida da interrupção por dados recebidos, e finalmente a interrupção de dados transmitidos (buffer de transmissão vazio) e a interrupção de status do modem, com a menor prioridade.

Bit Descrição

0 : 7 Bit 6 Bit 7

Page 336: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

314314314314

19191919

0 0 Sem FIFO

0 1 FIFO habilitada mas não utilizável

1 1 FIFO habilitada

5 Habilitação da FIFO de 64 bytes – somente no 16750

4 Reservado

0 Reservado nos chips 8250 e 16450 3

1 16550 Timeout Interrupt Pending

Bit 2 Bit 1

0 0 Modem Status Interrupt (interrupção de status do modem)

0 1 Transmitter Holding Register Empty Interrupt (interrupção do registrador do transmissor)

1 0 Received Data Available Interrupt (interrupção por recepção de dados)

1 : 2

1 1 Receiver Line Status Interrupt (interrupção da linha de recepção)

0 Interrupt Pending (interrupção pendente) 0

1 No Interrupt Pending (não há interrupção pendente)

Tabela 19-9 – Registrador de identificação da interrupção

1199..1100..22.. FFiirrsstt IInn // FFiirrsstt OOuutt CCoonnttrrooll RReeggiisstteerr ((FFCCRR)) O registrador FIFO é um registrador somente de leitura. Este registrador é usado

para controlar os buffers FIFO (First In / First Out) encontrados nos 16550 e posteriores.

O bit 0 habilita a operação de recepção e transmissão das FIFOs. Escrevendo um 0 neste bit, desabilitará a operação destes buffers, de forma que os dados armazenados nestes buffers serão perdidos.

Os bits 1 e 2 controlam o apagamento das FIFOs. O bit 1 é responsável pelo buffer de recepção enquanto que o bit 2 é responsável pelo buffer de transmissão. Setando estes bits para 1 limparão o conteúdo do buffer e não afetarão os registradores de deslocamento. Estes dois bits são auto resetados, de forma que o programador não precisa colocá-los em 0 novamente.

O bit 3 habilita a seleção de modo de DMA, encontrado nas UARTs 16550 e posteriores.

Page 337: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

315315315315

19191919

Os bits 6 e 7 são usados para setar o nível do trigger da FIFO de recepção. Por exemplo, se o bit 7 for setado em 1 e o bit 6 setado em 0, então o nível de trigger é de 8 bytes. Quando existirem 8 bytes de dados na FIFO de recepção então será gerada uma interrupção de recepção (caso estiver habilitada).

Bit Descrição

Bit 7 Bit 6 Interrupt Trigger Level (nível do trigger de interrupção)

0 0 1 byte

0 1 4 bytes

1 0 8 bytes

6 : 7

1 1 14 bytes

5 Enable 64 Byte FIFO (somente no 16750)

4 Reservado

3 DMA Mode Select. Muda o estado dos pinos RXRDY e TXRDY do modo 1 para o modo 2.

2 Clear Transmit FIFO (limpa a FIFO de transmissão)

1 Clear Receive FIFO (limpa a FIFO de recepção)

0 Enable FIFO’s (habilita as FIFOs)

Tabela 19-10 - Registrador de controle da FIFO

1199..1100..33.. LLiinnee CCoonnttrrooll RReeggiisstteerr ((LLCCRR)) O registrador Line Control seta os parâmetros básicos da comunicação. O bit 7 é o

DLAB (Divisor Latch Access Bit). O bit 6 permite abortar a comunicação, de forma que quando estiver ativo, a linha TxD vai para o estado zero, ocasionando uma desconexão na UART de recepção. Colocando este bit em 0 desabilita a desconexão.

Os bits 3, 4 e 5 selecionam a paridade. Se o bit 3 for setado em zero, não será usada a paridade, caso setado em 1, será usada a paridade. O bit 5 controla a paridade sticky16. A paridade sticky é simplesmente quando o bit de paridade é sempre transmitido e checado como 0 ou 1. Isto pode ser pouco efetivo na checagem de erros se os primeiros 4 bits contém erros mas o bit de paridade sticky contém o bit correto, de forma que o erro de paridade ficará mascarado. Paridade sticky alta indica o uso de 1 para o bit de paridade, enquanto que a paridade sticky baixa utiliza um 0 como bit de paridade.

Como o bit 5 controla a paridade sticky, desligando este bit, se usará a paridade normal provida pelo bit 3 ainda setado em 1. A paridade ímpar é quando o bit de paridade é transmitido como um 1 ou 0 formando um número ímpar de 1s. A paridade par acontece quando o bit de paridade produz um número par de 1s. Isto prove uma melhor 16 Sticky: pegajosa

Page 338: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

316316316316

19191919

detecção de erros mas não é um método perfeito, e por causa disto é freqüentemente usado o método CRC-32 para correções por software. Se um bit for invertido com a paridade par ou ímpar setada, então ocorrerá um erro de paridade, porém se dois bits forem invertidos de forma que produzem a paridade correta, então o erro de paridade ficará mascarado.

O bit 2 seta a quantidade de bits de parada (stop bit). Quando setado em 0 produzirá um stop bit, em 1 produzirá 1.5 ou 2 stop bits dependendo do tamanho da palavra de dados. Deve-se notar que o receptor checará somente o primeiro bit de parada.

Os bits 0 e 1 selecionam o tamanho da palavra de dados. Usualmente são usadas palavras de 8 bits.

Bit Descrição

1 Divisor Latch Access Bit (bit de acesso ao divisor) 7

0 Access to Recive buffer, Transmitter buffer and Interrupt Enable Register (acesso aos buffers de transmissão e recepção, e ao registrador de habilitação da interrupção)

6 Set Break Enable (seleciona o ajuste de fechamento de conexão)

Bit 5 Bit 4 Bit 3 Parity Select (seleção de paridade)

X X 0 Sem paridade

0 0 1 Paridade Impar

0 1 1 Paridade Par

1 0 1 Paridade Alta (sticky)

3 : 5

1 1 1 Paridade Baixa (sticky)

Comprimento do stop bit

0 Um bit de parada

2

1 Dois bits de parada para palavras com comprimento de 6, 7 ou 8 bits, ou 1.5 bit para palavras de 5 bits

0 : 1 Bit 0 Bit 1 Comprimento da palavra

0 0 5 bits

0 1 6 bits

1 0 7 bits

1 1 8 bits

Tabela 19-11 – Registrador de controle da linha de transmissão

1199..1100..44.. MMooddeemm CCoonnttrrooll RReeggiisstteerr ((MMCCRR)) O registrador de controle de modem é um registrador de leitura e escrita. O bit 4

ativa o modo loopback. Neste modo o transmissor é colocado no modo Mark (1 lógico) e o receptor serial é desativado. O transmissor é conectado no receptor. Os sinais DSR,

Page 339: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

317317317317

19191919

CTS, RI e DCD são desconectados. DTR, RTS, OUT1 e OUT2 são conectados às entradas de controle do modem. Os pinos de saída de controle do modem são desativados. Neste modo qualquer dado colocado nos registradores do transmissor para envio de dados, será recebido pelo circuito receptor no mesmo chip e ficará disponível no buffer de recepção. Isto pode ser utilizado para testar a operação da UART.

O bit Aux Output2 pode ser conectado a um circuito externo para controlar o processo de interrupção entre a UART e a CPU.

O bit Aux Output 1 é normalmente desconectado, mas em algumas placas é utilizado para chaveamento entre cristais de 1.8432 e 4 MHz quando usadas para MIDI. Os bits 0 e 1 controlam as suas linhas de dados. Por exemplo, setando o bit 1 para 1 efetuar a requisição para ativar a linha de envio.

Bit Descrição

7 Reservado

6 Reservado

5 Autoflow Control Enabled (somente no 16750)

4 LoopBack Mode

3 Aux Output 2

2 Aux Output 1

1 Force Request to Send

0 Force Data Terminal Ready

Tabela 19-12 – Registrador de controle do modem

1199..1100..55.. LLiinnee SSttaattuuss RReeggiisstteerr ((LLSSRR)) O registrador de status de linha é um registrador somente de leitura. O bit 7 indica

um erro na FIFO de recepção. Este bit fica em nível alto quando ocorreu pelo menos um erro de break, paridade ou framing, em algum byte contido na FIFO.

Quando o bit 6 estiver setado, os registradores de armazenamento (holding register) e de deslocamento (shift register) estarão vazios. O registrador de armazenamento armazena o próximo byte de dados a ser enviado, de forma paralela. O registrador de deslocamento é usado para converter o byte para o formato serial, de forma que possa ser transmitido através de uma linha. Quando o bit 5 estiver setado, então o registrador de armazenamento do transmissor está vazio. Quando o bit 6 estiver setado, o registrador de armazenamento do transmissor e o registrador de deslocamento estiverem vazios, não será feita a conversão serial. Quando o bit 5 estiver setado, o registrador de armazenamento do transmissor estará vazio, e outro byte poderá ser enviado pela porta de dados e o a conversão usando o registrador de deslocamento poderá ser efetuada.

A interrupção de break (bit 4) ocorre quando a linha de dados de recepção está fixa no nível lógico 0 (espaço) por um tempo maior que a duração de uma palavra completa. Isto inclui o tempo para os bits de start, data, parity e stop.

Page 340: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

318318318318

19191919

O erro de framing (bit 3) ocorre quando o último bit não é um stop bit válido. Isto pode ocorrer devido a erro de temporização. Usualmente acontece este tipo de erros quando se usa um null modem conectando dois computadores, ou um analisador de protocolos com baudrates diferentes, ou com tamanho de palavras diferentes.

Um erro de overrun normalmente ocorre se o programa não pode ler os dados da porta com a velocidade suficiente. Caso o buffer fique lotado ocorre a sobreposição de novos dados que chegam no canal serial.

O bit 0 mostra que os dados estão prontos, indicando que um byte foi recebido pela UART e está pronto para ser lido do buffer.

Bit Descrição

7 Error in Received FIFO

6 Empty Data Holding Registers

5 Empty Transmitter Holding Register

4 Break Interrupt

3 Framing Error

2 Parity Error

1 Overrun Error

0 Data Ready

Tabela 19-13 – Registrador de status da linha de comunicação

1199..1100..66.. MMooddeemm SSttaattuuss RReeggiisstteerr ((MMSSRR)) O bit 0 do registrador de status do modem mostra as informações de Delta Clear to

Send, Delta Meaning e suas mudanças, diretamente da linha de transmissão. De forma análoga, os bits 1 e 3. O bit 1 mostra a mudança na linha DSR onde o bit 3 mostra a mudança no Data Carrier Detect Line. O bit 2 é o Trailing Edge Ring Indicator que indica a mudança de um estado baixo para o alto na linha Ring Indicator.

Os bits 4 e 7 mostram o estado corrente da linha de dados quando lido. O bit 7 mostra o Carrier Detect, a linha 6 indica o Ring Indicator, o bit 5 mostra o DSR e o bit 4 a linha CTS.

Bit Descrição

7 Carrier Detect

6 Ring Indicator

5 Data Set Ready

4 Clear To Send

3 Delta Data Carrier Detect

Page 341: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

319319319319

19191919

2 Trailing Edge Ring Indicator

1 Delta Data Set Ready

0 Delta Clear to Send

Tabela 19-14 – Registrador de status do modem

1199..1100..77.. RReeggiisstteerr ddee uussoo GGeerraall ((SSccrraattcchh)) O registrador de uso geral não é usado para a comunicação mas pode ser utilizado

para armazenar um byte de dados.

1199..1111.. PPrrooggrraammaannddoo ((PPCCss)) –– RReecceeppççããoo ppoorr PPoolllliinngg ee ppoorr IInntteerrrruuppççããoo

Quando se implementam programas de comunicação de dados, existem dois métodos alternativos. O programa pode perguntar (polling) a UART para saber se algum dado está disponível, ou pode configurar um manipulador de interrupções para remover o dado da UART quando ela gerar uma interrupção avisando da chegada do mesmo. O método de polling é muito mais lento e ineficaz, já que ocupa muito tempo de CPU, de forma que se conseguem velocidades máximas de captura de 38.4 Kbps antes de começar a perder dados.

Somente alguns novos processadores conseguem melhorar estas taxas, tais como a geração Pentium Pro e MMX. A alternativa mais apropriada é a utilização de um manipulador de interrupção. Taxas da ordem de 115.2 Kbps são facilmente suportadas em computadores relativamente lentos.

A seguir é mostrado um programa exemplo, que mostra a transmissão pelo canal serial, e a recepção utilizando polling para comunicar dados a 9600 bps, 8 bits de dados, 1 start bit, 1 stop bit e sem paridade. #include <dos.h> #include <stdio.h> #include <conio.h> #define PORT2 0x2F8 /* Define o endereco base da porta serial */ /* COM1 0x3F8 */ /* COM2 0x2F8 */ /* COM3 0x3E8 */ /* COM4 0x2E8 */ void main(void) int c; int ch; outportb(PORT2 + 1 , 0); /* Desabilita as interrupcoes da porta - Port1 */ /* PORT 2 – Parametros de comunicação */ outportb(PORT2 + 3 , 0x80); /* Ativa DLAB */ outportb(PORT2 + 0 , 0x0C); /* Seleciona o baudrate - Divisor Latch Low Byte */ /* Default 0x03 = 38,400 BPS */ /* 0x01 = 115,200 BPS */ /* 0x02 = 56,700 BPS */ /* 0x06 = 19,200 BPS */ /* 0x0C = 9,600 BPS */ /* 0x18 = 4,800 BPS */ /* 0x30 = 2,400 BPS */ outportb(PORT2 + 1 , 0x00); /* Seleciona o baudrate - Divisor Latch High Byte */ outportb(PORT2 + 3 , 0x03); /* 8 Bits, Sem Paridade, 1 Stop Bit */ outportb(PORT2 + 2 , 0xC7); /* FIFO Control Register */ outportb(PORT2 + 4 , 0x0B); /* Ativa DTR, RTS, e OUT2 */

Page 342: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

320320320320

19191919

printf("\nPressionar ESC para sair \n"); do c = inportb(PORT2 + 5); /* Verifica se algum dado foi recebido. */ if (c & 1) ch = inportb(PORT2); /* Se chegou, então o captura do buffer de recepção numa variável */ printf("%c",ch); /* Imprime-o na tela */ if (kbhit()) ch = getch(); /* Se for pressionada alguma tecla, então o captura do buffer do teclado numa variável*/ outportb(PORT2, ch);/*Envia o dado pela porta serial*/ while (ch !=27); /* Sai quando for pressionada a tecla ESC (ASCII 27) */

Código 19-2

O método de polling pode ser adequado para programas de teste e diagnóstico. Caso não se conheça o endereço em que a placa está ou qual IRQ está utilizando, pode ser feito um polling na UART para os vários endereços possíveis, para encontrar primeiro em qual porta ela está conectada.

Conhecendo esta informação, então podem ser configuradas as rotinas de interrupção para as IRQs disponíveis mais comuns e então habilitar uma IRQ de cada vez, usando o controlador programável de interrupções, tudo isto sem ter que abrir o gabinete.

A seguir é mostrado um exemplo de transmissão serial e recepção usando interrupções, para comunicar dados a 9600 bps, 8 bits de dados, 1 start bit, 1 stop bit e sem paridade. #include <dos.h> #include <stdio.h> #include <conio.h> #define PORT2 0x2F8 /* Endereço da porta */ /* Define o endereço de base da porta serial */ /* COM1 0x3F8 */ /* COM2 0x2F8 */ /* COM3 0x3E8 */ /* COM4 0x2E8 */ #define INTVECT 0x0B /* IRQ da porta serial */ /* (Isto vai mudar a configuração do PIC) */ int bufferin = 0; int bufferout = 0; char ch; char buffer[1025]; void interrupt (*oldport2isr)(); void interrupt PORT2INT() /* Interrupt Service Routine (ISR) para a PORT2 */ int c; do c = inportb(PORT2 + 5); if (c & 1) buffer[bufferin] = inportb(PORT2); bufferin++; if (bufferin == 1024) bufferin = 0; while (c & 1); outportb(0x20,0x20); void main(void) int c; outportb(PORT2 + 1 , 0); /* Desabilita as interrupções na - Port2 */ oldport2isr = getvect(INTVECT); /* Salva o Vetor de Interrupções antigo para */ /* poder restaurá-lo depois */ setvect(INTVECT, PORT2INT); /* Seta uma entrada no Vetor de Interrupções */ /* COM1 - 0x0C */ /* COM2 - 0x0B */ /* COM3 - 0x0C */ /* COM4 - 0x0B */

Page 343: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

321321321321

19191919

/* PORT 2 – Configuração da Comunicação */ outportb(PORT2 + 3 , 0x80); /* Ativa o DLAB */ outportb(PORT2 + 0 , 0x0C); /* Seleciona o baudrate - Divisor Latch Low Byte*/ /* Default 0x03 = 38,400 BPS */ /* 0x01 = 115,200 BPS */ /* 0x02 = 56,700 BPS */ /* 0x06 = 19,200 BPS */ /* 0x0C = 9,600 BPS */ /* 0x18 = 4,800 BPS */ /* 0x30 = 2,400 BPS */ outportb(PORT2 + 1 , 0x00); /* Seleciona o baudrate - Divisor Latch High Byte */ outportb(PORT2 + 3 , 0x03); /* 8 Bits, sem Paridade, 1 Stop Bit */ outportb(PORT2 + 2 , 0xC7); /* FIFO Control Register */ outportb(PORT2 + 4 , 0x0B); /* Ativa DTR, RTS, e OUT2 */ outportb(0x21,(inportb(0x21) & 0xF7)); /* Configura o Controlador Programável de Interrupções – PIC */ /* COM1 (IRQ4) - 0xEF */ /* COM2 (IRQ3) - 0xF7 */ /* COM3 (IRQ4) - 0xEF */ /* COM4 (IRQ3) - 0xF7 */ outportb(PORT2 + 1 , 0x01); /* Interrupção quando seja recebido um dado */ printf("\nPressionar ESC para sair \n"); do if (bufferin != bufferout) ch = buffer[bufferout]; bufferout++; if (bufferout == 1024) bufferout = 0; printf("%c",ch); if (kbhit()) c = getch(); outportb(PORT2, c); while (c !=27); outportb(PORT2 + 1 , 0); /* Desabilita as interrupções – Port2 */ outportb(0x21,(inportb(0x21) | 0x08)); /* Mascara a IRQ usando o PIC */ /* COM1 (IRQ4) - 0x10 */ /* COM2 (IRQ3) - 0x08 */ /* COM3 (IRQ4) - 0x10 */ /* COM4 (IRQ3) - 0x08 */ setvect(INTVECT, oldport2isr); /* Restaura o vetor de interrupções antigo */

Código 19-3

Os códigos fonte mostrados neste livro podem não ser os melhores exemplos de programação, uma vez que o objetivo é mostrar programas simples e concisos, que fornecem resultados rápidos e fáceis de compreender. Nos programas de comunicação profissionais, é recomendado salvar também os registradores de status da UART, para serem restaurados após a finalização do programa. Isto pode causar menos transtornos a outros programas que tentem acessar a porta de comunicação posteriormente.

O primeiro passo para o uso de interrupções é trabalhar com os serviços da placa serial. A Tabela 17-1 mostra os endereços de base e IRQs para as portas padrão. As IRQs 3 e 4 são as mais usualmente utilizadas em portas seriais, e mais raramente IRQ 5 e 7.

1199..1122.. VVeettoorreess ddee IInntteerrrruuppççõõeess

Uma vez conhecida a IRQ o próximo passo é encontrar o seu vetor de interrupção. Basicamente qualquer processador 8086 tem um grupo de 256 vetores de interrupção numerados de 0 a 255. Cada um destes vetores contém um código de 4 bytes que é o endereço da rotina de atendimento à interrupção (Interrupt Service Routine - ISR)17.

17 Para maiores informações veja o capítulo 17.

Page 344: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

322322322322

19191919

Por exemplo, se for usada a COM3 que utiliza a IRQ 4, então o vetor de interrupção deverá ser 0x0C. Utilizando a linguagem C pode-se setar o vetor usando a seguinte instrução: setvect(0x0C, PORT1INT);

onde PORT1INT permite setar o nome da função que atenderá a interrupção.

Ainda antes de proceder, é interessante guardar o endereço antigo do vetor de interrupções para restaurá-lo na finalização do programa. Para fazer isto pode-se escrever: oldport1isr = getvect(INTVECT);

onde oldport1isr é definido usando: void interrupt (*oldport1isr)();

Além da salvar o vetor antigo, é recomendável salvar também o status da UART, já que foram usualmente o programa modifica toda a configuração da mesma, e se acontecer algum tipo de erro, o programa a ser executado mais tarde, poderá não capturar a porta para a nova utilização, até que o computador seja reinicializado.

Desta maneira. salvando a configuração antiga, pode-se reverter os valores modificados antes de deixar a UART ser manipulada por outros programas.

1199..1122..11.. IInntteerrrruupptt SSeerrvviiccee RRoouuttiinnee ((IISSRR)) Agora, PORT1INT é o label para o manipulador de atendimento à interrupção

chamado de Interrupt Service Routine (ISR). Nesta função devem ser colocadas as instruções que executarão o serviço apropriado.

Contudo a chamada de algumas rotinas DOS podem dar alguns problemas. void interrupt PORT1INT() int c; do c = inportb(PORT1 + 5); if (c & 1) buffer[bufferin] = inportb(PORT1); bufferin++; if (bufferin == 1024) bufferin = 0; while (c & 1); outportb(0x20,0x20);

No exemplo acima, se verifica para ver se há um caractere para receber e se pode ser removido da UART e colocado no buffer de memória. Deve-se ter o cuidado de verificar a UART, no caso da FIFO estar habilitada, para capturar todos os dados disponíveis no momento da interrupção.

A última linha contém a instrução outportb(0x20,0x20); que indica ao Controlador Programável de Interrupções que a interrupção acabou. A partir daí as ações são centradas no PIC. Todas as rotinas acima, assumem que o PIC e a UART estão corretamente configurados. O PIC manipula as interrupções de hardware. A maioria dos PCs tem dois PICs localizados em diferentes endereços de memória. Um deles manipula as IRQs 0 a 7 e o outro as IRQs 8 a 15. Muitas interrupções de comunicação serial residem nas IRQs abaixo de 7, no PIC1, que é localizado no endereço 0x0020. A Tabela 17-2 mostra o registrador de controle do PIC 1.

Page 345: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

323323323323

19191919

As portas multiseriais são bastante comuns, e utilizam normalmente o PIC2, encontrado em 0xA0 como mostra a tabela a seguir. O PIC2 é responsável pelas IRQs 8 a 15. Este controlador opera da mesma forma que o PIC1, exceto pelo EOI (end of interrupt) é feita pela porta 0xA0 enquanto que a desabilitação (mascaramento) é feito usando a porta 0xA1. A Tabela 17-3 mostra o registrador de controle do PIC 2.

A inicialização dos PICs é normalmente feita pela BIOS. A inicialização se resume em duas instruções: outportb(0x21,(inportb(0x21) & 0xEF);

que seleciona quais interrupções serão desabilitadas (mascarada). Assim se for necessário habilitar a IRQ4 (estaria desabilitada com um valor para o bit correspondente em 1, por exemplo de 0x10) e desabilitar as outras IRQs bastaria usar o valor 0xEF. Isto permitiria desabilitar as IRQs 0, 1, 2, 3, 5, 6 e 7, e habilitar somente a IRQ 4.

A outra instrução é outportb(0x20,0x20);

que sinaliza o final da interrupção para o PIC. Este comando deve ser utilizado para finalizar a rotina de atendimento à interrupção, de forma que as interrupções de menor prioridade possam ser aceitas.

1199..1133.. CCoonnffiigguurraaççããoo ddaa UUAARRTT

É conveniente desabilitar a interrupção da UART na primeira instrução, de forma que a nova inicialização não seja interrompida pela UART. A próxima ação poderia ser a configuração do vetor de interrupções. O próximo passo é setar a taxa de comunicação desejada, setando o bit 7 (DLAB) do LCR, e então os divisores de clock (Divisor Latch High e Low). Nos exemplos mostrados, utiliza-se uma taxa de 9600 bps que pode ser implementada pelas UARTs 16450 e 16550. Isto requer um divisor de 12 (115200 / 9600). Desta forma, a parte mais significativa do divisor deverá ser 0x00 e a menos significativa 0x0C.

O próximo passo deve ser desativar o bit de acesso ao latch divisor de forma que possa ser lido o registrado de habilitação de interrupções e os buffers de transmissão e recepção. Isto pode ser feito escrevendo 0x00 no registrador, limpando-o, mas considerando que este deverá ser configurado com o tamanho da palavra de dados, a paridade, etc. no registrador de controle de linha, então poderá ser feito ao mesmo tempo. Nos exemplos anteriores foi escolhido o uso de 8 bits de dados, sem paridade e um stop bit. Depois foi colocado 0x03 no registrador de controle de linha que desativará o DLAB para poupar mais uma instrução de I/O.

A próxima linha de código, ativa os buffers FIFO. Foi utilizado um nível de trigger de 14 bytes, assim os bits 6 e 7 estão ativos. Deve-se então habilitar as FIFOs (bit 0). É também recomendado limpar os buffers FIFO na inicialização, removendo os valores do último programa que utilizou o canal. Devido ao fato de que estes dois bits são auto-resetados, não é necessário restaurar os seus valores.

Os sinais DTR, RTS e OUT2 são ativados pela instrução: outportb(PORT1 + 4, 0x0B);.

Page 346: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

324324324324

19191919

Algumas interfaces requerem do sinal OUT2 ativo para as requisições de interrupção. É recomendável deixá-lo em nível alto. A partir deste ponto devem ser configuradas as interrupções que tem sido deliberadamente deixadas para o final, para evitar uma interrupção na inicialização.

1199..1133..11.. RRoottiinnaa MMaaiinn ((LLoooopp)) A rotina principal foi colocada como

do if (bufferin != bufferout) ch = buffer[bufferout]; bufferout++; if (bufferout == 1024) bufferout = 0; printf("%c",ch); if (kbhit()) c = getch(); outportb(PORT1, c); while (c !=27);

que é repetida até c = 27. Isto ocorre quando a tecla ESC for pressionada.

A próxima instrução if verifica se alguma tecla tem sido pressionada com (kbhit()). Se for, então captura o caractere com getch() e o coloca no buffer de recepção. A UART então transmite o caractere para o modem. Neste caso foi assumido que o operador não poderá digitar tão rapidamente quanto a UART pode enviar os dados. Caso o programa possa exigir enviar a qualquer velocidade, então deverá ser verificado o bit 5 do registrador de status de linha (LSR), antes de tentar enviar um byte para o registrador de transmissão.

1199..1133..22.. DDeetteerrmmiinnaannddoo oo TTiippoo ddee UUAARRTT O tipo de UART instalado no sistema, pode ser determinado sem necessidade de

abrir o gabinete na maioria dos casos. Como foi visto na seção, Tipos de UARTS, pode-se ver que as UARTs mais comuns possuem mínimas diferenças. O que deve ser feito é testar as diferenças.

O primeiro procedimento é setar o bit 0 para ‘1’ no registrador de controle da FIFO. Isto tentará habilitar os buffers FIFO. Então devem ser lidos os bits 6 e 7 do registrador de identificação de interrupções. Se ambos bits estiverem em ‘1’ então os buffers FIFO estarão habilitados, significando que a UART é uma 16550A. Se a FIFO estiver habilitada mas não pode ser usada, então é uma UART 16550. Se não for habilitada, então poderá ser uma UART 16450, ou as mais antigas 8250, 8250A ou 8250B.

A UART 16750 possui uma FIFO de 64 bytes, de forma que o caminho mais fácil para testá-la é habilitando-a usando o registrador de controle da FIFO e então ler o status do registrador de identificação de interrupções.

1199..1144.. IInntteerrffaacceeaannddoo DDiissppoossiittiivvooss ccoomm aass PPoorrttaass RRSS--223322

A comunicação do padrão RS-232 é assíncrona. Isto é, o sinal de clock não é enviado com os dados. Cada palavra é sincronizada usando um bit de start, e um clock interno de cada lado, se encarregará da temporização para o correto funcionamento.

Page 347: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

325325325325

19191919

Figura 19-4 – Forma de Onda TTL/CMOS para o canal serial

A Figura 19-4 mostra o sinal esperado, gerado por uma UART quando utilizado o formato 8N1. O formato 8N1 significa 8 bits de Dados, sem Paridade e um bit de Stop. A linha RS-232, quando inativa, fica no estado Mark (1 lógico). A transmissão começa com o bit de start, que é sempre em ‘0’. Cada bit então, serão enviados de forma consecutiva na mesma linha. O bit menos significativo (LSB) é enviado primeiro. O bit de Stop está sempre em ‘1’ lógico, e será adicionado no final do bloco de dados.

O diagrama mostra o que o bit seguinte ao bit de Stop é um ‘0’ lógico. Isto deverá significar que outra palavra vem seguindo à primeira, isto é, um novo bit de Start. Se não houver mais dados chegando, então a linha de recepção deverá permanecer no estado idle (‘1’ lógico). Às vezes poderá ser encontrado um sinal chamado de “Break”, que força a linha ao estado ‘0’ lógico por um tempo suficiente para enviar uma palavra inteira. Desta forma, se o programador não colocar a linha no modo idle, então o receptor poderá interpretar isto como um sinal de break.

Os dados enviados desta forma, são conhecidos como frames. Os dados são colocados entre os bits de start e de stop. Se o bit de stop recebido for um ‘0’ lógico, então ocorrerá um erro de frame. Isto é comum, quando ambos lados da comunicação estão configurados para diferentes baudrate.

A Figura 19-5 é relevante somente para sinais com níveis lógicos compatíveis com as UARTs RS-232, que utilizam +3 a +25 Volts para identificar um ‘0’ lógico, e de –3 a –25 Volts para identificar um ‘1’ lógico. Qualquer diferença de potencial entre estas duas faixas de valores (i.e. entre +3 e –3 Volts) poderá ficar indefinida. Algumas interfaces funcionam com valores de tensão de até 1 V para o ‘1’ lógico, e de mais de 3V para o ‘0’ lógico.

Figura 19-5 – Forma de onda no padrão RS-232

A forma de onda acima se aplica às linhas de transmissão e de recepção, para as portas RS-232. Estas linhas carregam os dados de forma serial. Existem outras linhas na porta RS-232, que são basicamente linhas paralelas. Estas linhas (RTS, CTS, DCD, DSR, DTR, RTS e RI) seguem também o padrão de níveis lógicos da RS-232.

1199..1144..11.. CCoonnvveerrssoorreess ddee NNíívveell RRSS--223322 A maioria dos dispositivos digitais funciona com níveis lógicos TTL ou CMOS.

Desta forma o primeiro passo para conectar um dispositivo numa porta RS-232C é transformando os níveis RS-232 em níveis de 0 a 5 Volts, e vice-versa. É importante observar que a lógica do padrão RS-232 é invertida, ou seja, o nível de tensão maior é o ‘0’ lógico.

Page 348: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

326326326326

19191919

Dois conversores comuns de nível RS-232 são os drivers 1488 e os receptores 1489. Cada integrado contém 4 inversores de um dos tipos. O transmissor requer duas fontes de alimentação, de +7.5 a 15V e –7.5 a –15V. Isto pode ser um problema quando somente se dispõe de uma fonte de alimentação de +5V. A vantagem destes integrados é o seu baixo custo.

Figura 19-6 – Pinagem e diagrama esquemático do driver transceptor MAX23218

Um outro tipo de dispositivo é o MAX-232 (e equivalentes DS232 e ICL232). Estes incluem duplicadores de tensão que geram tensões de +10V e –10V a partir de uma fonte única de +5V. Estes dispositivos também incluem dois receptores e dois transmissores no mesmo encapsulamento. A única diferença entre os equivalentes é o valor dos capacitores necessários.

Uma solução alternativa é o uso de transistores, que aproveita a tensão alta do sinal DTR (aproximadamente +10V). Deve-se assegurar pelo software que o sinal DTR permanecerá neste estado, que é o estado padrão. Os transistores poderão ser trocados por qualquer CMOS19, ou até por TJBs20 com o único cuidado de observar que as correntes de polarização sejam suficientes, sendo que os resistores deverão ser recalculados. Observar o esquema a seguir.

18 Maiores detalhes em www.maxim.com 19 CMOS: Complementary Metal Oxyde Semiconductor. 20 TJB: Transistor de Junção Bipolar.

Page 349: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

327327327327

19191919

Figura 19-7 – Conversor de níveis TTL/CMOS para RS-232

1199..1155.. IInntteerrffaacceess ccoomm MMiiccrrooccoonnttrroollaaddoorreess

É comum usar microcontroladores para transmitir e receber sinais na forma serial. Usualmente os microcontroladores já possuem UARTs (chamadas comumente SCI – Serial Controller Interface) incorporadas entre outras interfaces. Pode-se imaginar o caso de um sensor inteligente, que deve monitorar alguma grandeza analógica usando um ADC (conversor análogico-digital), e deverá enviar o valor filtrado ao PC. Neste caso, os pinos de interface serial deverão ser conectados a um conversor de nível. Maiores detalhes serão tratados nos Apêndices B e C.

1199..1166.. AAss PPoorrttaass nnoo SSiisstteemmaa WWiinnddoowwss

Maiores informações com relação à manipulação das portas de comunicação utilizando o sistema operacional MS Windows, serão tratadas no Apêndice D.

1199..1177.. AA AAnnttiiggaa mmaass úúttiill BBiibblliiootteeccaa ddee CCoommuunniiccaaççããoo ddaa BBoorrllaanndd

O compilador Borland C++ 3.1 possui uma biblioteca de funções que facilitam a comunicação serial, em sistemas DOS. A utilização de sistemas DOS é hoje quase inexistente, mas ainda persiste a compatibilidade entre a maioria dos sistemas MS Windows e os códigos gerados para DOS. A utilização destas ferramentas, relativamente antigas, pode ser interessante quando se trata de elaborar programas de teste rápidos e funcionais, e por causa disto, este assunto será tratado brevemente nesta seção como forma de exemplo. Caso não esteja disponível um compilador da Borland, existe uma biblioteca parecida no compilador Microsoft Turbo C, também antigo. Os compiladores novos, provavelmente não implementam as bibliotecas DOS para comunicação de dados.

A biblioteca serial para comunicações RS-232 está declarada no arquivo BIOS.H, possuindo duas funções especiais cuja declaração é a seguinte: int bioscom(int cmd, char abyte, int port); unsigned _bios_serialcom(int cmd, int port, char abyte);

Page 350: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

328328328328

19191919

Ambas funções utilizam a interrupção 0x14 da BIOS para executar a comunicação RS-232 numa porta serial especifica.

Argumento Significado

abyte Combinação OR de bits que especificam a configuração da porta COM (ignorado se cmd = 2 ou 3)

cmd Especifica o tipo de operação a ser efetuada

port Identifica a porta de I/O; 0 = COM1, 1 = COM2, etc.

Tabela 19-15 – Argumento das funções bioscom e _bios_serialcom

Para todos os valores de cmd, ambas funções retornam um inteiro de 16 bits.

Os 8 bits mais significativos são os bits de status. Se um ou mais bits de status estiverem setados em ‘1’, indica o acontecimento de algum tipo de erro. Se nenhum bit de status estiver com valor igual a ‘1’, o byte foi recebido sem erro.

Bit Descrição Divisão

15 Time out (setado para 1 se o valor de abyte não pode ser enviado)

14 Transmit shift register empty

13 Transmit holding register empty

12 Break detect

11 Framing error

10 Parity error

9 Overrun error

8 Data ready

Byte mais significativos

7 Change in clear to send

6 Change in data set ready

5 Trailing edge ring detector

4 Change in receive line signal detector

3 Clear to send

2 Data set ready

1 Ring indicator

0 Received line signal detect

Byte menos significativo (depende do valor especificado para cmd)

Tabela 19-16 – Valores de retorno das funções bioscom e _bios_serialcom

Os 8 bits menos significativos do valor de retorno dependem do valor especificado para cmd.

Valor de cmd Significado dos 8 bits menos significativos do valor de retorno

Page 351: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

329329329329

19191919

0 (_COM_INIT) ou

3 (_COM_STATUS)

Os bits menos significativos são definidos como mostra a tabela.

1 (_COM_SEND) ...

2 (_COM_RECEIVE) O byte lido está nos oito bits menos significativos do valor de retorno – isto no caso de não haver erro (nenhum dos oito bits mais significativos setados em ‘1’)

Tabela 19-17 – Significado do valor de retorno dependendo de cmd

Os valores de cmd, podem ser definidos de acordo como segue:

bioscom _bios_serialcom Função

0 _COM_INIT Seleciona os parâmetros de comunicação em abyte

1 _COM_SEND Envia o caractere em abyte pela linha de comunicação

2 _COM_RECEIVE Recebe um caractere da linha de comunicação

3 _COM_STATUS Retorna o status da porta de comunicação corrente

Tabela 19-18 – Valores para cmd

Quando cmd = 2 ou 3 (_COM_RECEIVE ou _COM_STATUS), o argumento abyte será ignorado.

Quando cmd = 0 (_COM_INIT), abyte é uma combinação OR dos seguintes bits (um para cada grupo):

bioscom _bios_serialcom Significado Grupo 0x02 0x03

_COM_CHR7 _COM_CHR8

7 bits de dados 8 bits de dados

Número de bits de dados

0x00 0x04

_COM_STOP1 _COM_STOP2

1 stop bit 2 stop bits

Número de Stop Bits

0x00 0x08 0x18

_COM_NOPARITY _COM_ODDPARITY _COM_EVENPARITY

Sem paridade Paridade Ímpar Paridade Par

Paridade

0x00 0x20 0x40 0x60 0x80 0XA0 0XC0 0XE0

_COM_110 _COM_150 _COM_300 _COM_600 _COM_1200 _COM_2400 _COM_4800 _COM_9600

110 baud 150 baud 300 baud 600 baud 1200 baud 2400 baud 4800 baud 9600 baud

Baudrate

Tabela 19-19 – Valores para abyte

Por exemplo, se abyte = 0xEB = (0xE0 | 0x08 | 0x00 | 0x03) = (_COM_9600 | _COM_ODDPARITY | _COM_STOP1 | _COM_CHR8), a porta de comunicação será configurada para: 9600 baud (0xE0 = _COM_9600) Paridade ímpar (0x08 = _COM_ODDPARITY) 1 stop bit (0x00 = _COM_STOP1) 8 bits de dados (0x03 = _COM_CHR8)

Page 352: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

330330330330

19191919

A seguir são mostrados dois exemplos de códigos utilizando as funções da biblioteca BIOS.H. #include <bios.h> #include <conio.h> #define COM1 0 #define DATA_READY 0x100 #define TRUE 1 #define FALSE 0 #define SETTINGS ( 0x80 | 0x02 | 0x00 | 0x00) int main(void) int in, out, status, DONE = FALSE; bioscom(0, SETTINGS, COM1); cprintf("... BIOSCOM [ESC] to exit ...\n"); while (!DONE) status = bioscom(3, 0, COM1); if (status & DATA_READY) if ((out = bioscom(2, 0, COM1) & 0x7F) != 0) putch(out); if (kbhit()) if ((in = getch()) == '\x1B') DONE = TRUE; bioscom(1, in, COM1); return 0;

Código 19-4

#include <bios.h> #include <conio.h> #define COM1 0 #define DATA_READY 0x100 #define TRUE 1 #define FALSE 0 #define SETTINGS (_COM_1200 | _COM_CHR7 | _COM_STOP1 | _COM_NOPARITY) int main(void)

unsigned in, out, status;

_bios_serialcom(_COM_INIT, COM1, SETTINGS); cprintf("... _BIOS_SERIALCOM [ESC] to exit ...\r\n"); for (;;) status = _bios_serialcom(_COM_STATUS, COM1, 0);

if (status & DATA_READY) if ((out = _bios_serialcom(_COM_RECEIVE, COM1, 0) & 0x7F) != 0) putch(out); if (kbhit()) if ((in = getch()) == '\x1B') break; _bios_serialcom(_COM_SEND, COM1, in); return 0;

Código 19-5

EExxeerrccíícciiooss

1. Usando como exemplo o último programa de comunicação serial, implemente um programa que permita a configuração do canal serial via teclado. O baudrate, número de bits de dados, tipo de paridade, endereço da porta serial e número de IRQ devem ficar configuráveis. Caso não sejam inseridos valores corretos, deverá prevalecer uma condição default. Criar três funções, uma de leitura de dados, uma de escrita e uma de

Page 353: Tratado Da Linguagem c

I N T E R F A C E S S E R I A I S

331331331331

19191919

inicialização do canal serial, cujos valores de retorno seja o status da operação. Por exemplo

struct PORT unsigned int baudrate; unsigned int PortAddress; unsigned int DataBits; unsigned int Parity; unsigned int IRQ; unsigned char *pTxBuff; unsigned char *pRxBuff; Port; int ReadData(char data); int WriteData(char data); int InitSerialPort(struct Port);

2. Implemente a comunicação serial entre dois computadores que possam trocar informação de até 256 registros de 2 bytes cada. As operações definidas são as de leitura e escrita. O sistema deverá ser do tipo Master-Slave, onde pode haver um mestre e vários escravos, cada um destes deverá ser identificado pelo seu endereço, que deve ser configurável. Somente o Master pode iniciar a comunicação, solicitando dados ou forçando a escrita num dispositivo escravo. Os escravos somente ficam ouvindo a linha, e quando são interrompidos, verificam se o endereço enviado pelo mestre é o próprio. O protocolo a ser utilizado é mostrado a seguir:

Pedido de leitura de dados pelo Mestre: Endereço do Escravo

Código da Operação

Registrador Inicial H

Registrador Inicial L

Número de Registradores H

Número de Registradores L

Checksum

xx 03 00 00 00 0A xx

Nesse exemplo, o mestre pede ao escravo com endereço ‘xx’ enviar os valores dos registros 0 a 9. Observar que os valores estão em hexadecimal. O escravo deverá responder o seguinte:

Endereço do Escravo

Código da Operação

Número de bytes de dados H

Número de bytes de dados L

Registro 0 H

Registro 0 L

... ... Registro 9 H

Registro 9 L

Checksum

xx 03 00 14 xx xx .. ... xx xx xx

Uma operação de escrita deverá ser da seguinte forma: Endereço do Escravo

Código da Operação

Registrador inicial H

Registrador inicial L

Número de Registros H

Número de Registros L

Valor Registrador 0 H

Valor Registrador 0 L

... ... Valor Registrador 9 H

Valor Registrador 9 L

Checksum

xx 05 00 14 xx xx xx xx .. ... xx xx xx

A resposta do escravo deverá ser: Endereço do Escravo

Código da Operação

Checksum

xx 05 Xx

O checksum é calculado de forma que a soma de todos os bytes (soma de 8 bits) mais o valor do checksum seja sempre igual a zero. Caso haja um erro de checksum, o escravo deverá ignorar a mensagem. Caso o mestre detectar um erro de checksum, deverá fazer a pergunta novamente. Caso o escravo não responder após 100 ms, o mestre deverá emitir uma mensagem de erro de timeout. Implemente um programa único para mestre ou escravo, sendo que esta característica seja selecionada pelo usuário, junto com o endereço. O programa deve permitir a alteração de valores dos registros de forma local no mestre e no escravo, e de forma remota a partir do mestre.

Page 354: Tratado Da Linguagem c

332332332332 Luis Fernando Espinosa Cocian

AAppêênnddiiccee AA -- PPaaddrrõõeess ddee TTrraannssmmiissssããoo SSeerriiaall

O projeto de uma interface entre sistemas não é uma tarefa simples e direta. Vários parâmetros devem ser levados em conta, sendo alguns deles: a taxa de transmissão, o comprimento dos cabos, o modo de transmissão, a terminação, a faixa de modo comum do barramento, o tipo de conector e a configuração do sistema. Pode ser notado que o grande número de parâmetros ilustra o quão complexa é esta tarefa. De forma adicional, a compatibilidade com sistemas de outros fabricantes é também de importância critica. I.e. a necessidade de padronização fica evidente. Os padrões de interface resolvem os problemas de compatibilidade e facilitam o projeto através de drivers e receptores genéricos padronizados.

IInnttrroodduuççããoo

Os drivers e os receptores de linha são comumente utilizados para intercambiar dados entres dois ou mais pontos (nós) de uma rede. O intercambio de dados confiáveis pode ser dificultado pela presença de ruído induzido, diferença de níveis entre os sinais de terra, casamento de impedâncias, falhas de polarização nas condições de não operação (estado idle), e outros problemas relacionados com a instalação da rede.

A linha de transmissão utilizada na conexão entre dois ou mais elementos (drivers e receptores) deverá ser levada em consideração, especialmente quando os tempos de subida ou descida dos níveis de tensão serem maiores que a metade do tempo que o sinal leva para trafegar do transmissor ate o receptor.

Esta seção trata de forma breve os padrões de interface mais populares. Na maioria dos casos, são mostradas as tabelas dos requisitos elétricos assim como aplicações típicas. As organizações tratadas nesta seção, que estabeleceram alguns padrões de interfaces são as seguintes: TIA/EIA Telecommunications Industry Association/Electronics Industry Association ITU International Telecommunications Union CCITT International Telegraph and Telephone Consultative Committee — substituída pela

ITU IEEE Institute of Electrical and Electronics Engineers

Existem dois modos básicos de operação para os drivers de linha (geradores ou transmissores) e receptores. Estes modos são o modo Desbalanceado (Single-Ended) e Balanceado (Diferencial).

Apêndice

A

Page 355: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

333333333333

AAAA

TTrraannssmmiissssããoo ddee DDaaddooss eemm SSiisstteemmaass DDeessbbaallaanncceeaaddooss ((SSiinnggllee--EEnnddeedd))

A transmissão desbalanceada utiliza um condutor único, com a sua tensão referenciada ao sinal de terra (comum) para identificar o estado lógico. A vantagem da transmissão desbalanceada de dados é quando são requeridos múltiplos canais, utilizando um terra comum (ver Figura 20-1). Isto minimiza o tamanho do conector e do cabo, resultando numa diminuição do custo do sistema. A desvantagem dos sistemas desbalanceados é a incapacidade de envio de dados de forma confiável em sistemas com ruído. Isto é devido à margem limitada de rejeição ao ruído. As fontes de ruído do sistema podem incluir o ruído induzido, interferências cruzadas e diferenças de potencial entre os terras.

Figura 20-1 - Transmissão de Dados em Sistemas Desbalanceados - 3 canais, 4 linhas

A comunicação eletrônica de dados entre dois ou mais dispositivos, pode ser classificada em duas grandes categorias: de referencia única (single-ended), ou diferencial. O padrão RS-232 (referencia única) foi criado em 1962, a pesar dos rumores que previam a sua rápida desaparição, tem permanecido amplamente usado na industria até os presentes dias. As especificações permitem a transmissão de dados a partir de um transmissor para um receptor em taxas relativamente lentas (até 20 Kbps) e em curtas distancias (de até 150 metros para taxa máxima).

Para poder efetuar a comunicação full-duplex1 (em duas vias simultâneas) foram estabelecidos dois canais independentes. Os sinais RS-232 são representados pelos níveis de tensão referenciados ao sinal de terra (sinal comum lógico). O estado idle (inativo ou MARK) possui nível negativo com respeito ao Comum, e o estado ativo (SPACE) possui nível positivo. O padrão RS-232 possui varias linhas de handshaking (normalmente utilizadas pelos modems), e também especifica um protocolo de comunicações básico. Em geral se não estiver sendo utilizado um modem, as linhas de handshaking poderão representar um problema caso não sejam desabilitadas pelo software ou levadas em consideração no hardware (interconectadas ou conectadas em nível alto de tensão através de um resistor). O padrão RS-423 é uma outra especificação de referencia única que

1 Full-duplex: Transmissão de sinais entre dispositivos, em ambos os sentidos e de forma simultânea.

Page 356: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

334334334334

AAAA

melhora a operação do sistema RS-232, embora, esta solução não tenha sido adotada pela indústria.

TTrraannssmmiissssããoo ddee ddaaddooss eemm SSiisstteemmaass BBaallaanncceeaaddooss ((DDiiffeerreenncciiaaiiss))

A transmissão de dados em sistemas balanceados requer de dois condutores por sinal. Neste tipo de sistema duas linhas são chaveadas. O estado lógico é identificado pela diferença de potencial entre as linhas, e não em relação ao sinal de terra. Este fato faz dos drivers e receptores diferenciais, ideais para o uso em ambientes ruidosos (ver Figura 20-2). Os sistemas diferenciais anulam o efeito do ruído acoplado e das diferenças de potencial entre os sinais de terra. Em geral este ruído é visto como tensões de modo comum (em ambas linhas), não diferenciais, e são rejeitados pelos receptores. Diferente dos drivers não balanceados, a maioria dos drivers balanceados possui como característica um pequeno tempo de transição, permitindo o seu uso em taxas maiores de transmissão.

Figura 20-2 - Sistema Balanceado de transmissão - 3 Canais, 6 Linhas e sinal de terra

Em sistemas onde é necessária a comunicação de dados em altas taxas, ou para longas distâncias, os métodos de referencia única são inadequados. Os sistemas de transmissão diferencial (sinais diferenciais balanceados) oferecem um desempenho superior na maioria das aplicações. Os sinais diferenciais podem ajudar a minimizar os efeitos das diferenças entre os sinais de terra e os sinais de ruído induzidos que podem aparecer como tensões de modo comum em uma rede.

O padrão RS-422 (diferencial) foi projetado para operar em grandes distâncias e para os maiores taxas (baudrates). Podem ser utilizadas taxas de até 100 Kbps em distâncias de até 1200 metros. Este sistema pode implementar um barramento multi-drop (linha compartilhada), onde somente pode existir um driver (master) conectado com até 10 receptores (slaves).

Enquanto as aplicações multi-drop possuem muitas vantagens, os dispositivos, os dispositivos RS-422 não podem ser usados para construir uma rede multiponto. Uma rede multiponto consiste de múltiplos drivers e receptores conectados num único bus, onde qualquer nó pode transmitir ou receber dados.

Page 357: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

335335335335

AAAA

Com dispositivos RS-422 podem ser construídas redes pseudo-multi-drop (a 4 fios). Estas redes são usadas freqüentemente no modo half-duplex2, onde o único mestre (master) do sistema, envia um comando a um dos muitos escravos (slaves) da rede. Tipicamente somente um dispositivo (nó) é endereçado pelo computador mestre, e a resposta é recebida a partir do dispositivo endereçado. Os sistemas deste tipo (4 fios, half-duplex) são freqüentemente implementados para evitar o problema de colisão de dados (contenção do barramento) da rede multi-drop.

O sistema RS-485 preenche os requisitos para a implementação de uma rede de comunicação multiponto real, e o padrão especifica até 32 drivers e 32 receptores em um barramento único de 2 fios. Com a introdução de repetidores automáticos e transmissores e receptores de alta impedância, esta limitação pode ser estendida para centenas (ou ate milhares) de nós em uma rede. O padrão RS-485 estende a faixa de modo comum para os drivers e receptores no modo tristate e sem alimentação. Os drivers RS-485 são capazes de resistir aos problemas de colisão de dados (colisão de barramento) e às condições de falha de barramento.

Para resolver o problema da colisão de dados, freqüentes nos sistemas multi-drop, poderão ser construídos dispositivos de hardware (conversores, repetidores, controles microprocessados) para permanecer em modo de recepção até que estejam prontos para transmitir os seus dados. Os sistemas mono-mestre oferecem um meio direto e simples de evitar as colisões de dados em sistemas típicos a dois fios, half-duplex e multi-drop. O mestre inicia a requisição de comunicação para um nó escravo pelo endereçamento de tal unidade. O hardware detecta o bit de inicio da transmissão e automaticamente habilita o seu transmissor RS-485. Assim que os dados requisitados forem enviados, o hardware voltará ao modo de recepção em 1 ou 2 s.

Qualquer numero de caracteres poderá ser enviado, e o transmissor será automaticamente ativado a cada novo caractere. Quando um dispositivo escravo for endereçado, este será capaz de responder imediatamente, devido ao pequeno tempo necessário para que o transmissor desconecte o seu transmissor. Não é necessário introduzir longos atrasos (delays) na rede para evitar a colisão de dados. Uma vez que estes atrasos não são necessários, as redes podem ser implementadas utilizando a largura de banda máxima permitida pelo canal, pela codificação e forma do sinal.

CCoommppaarraaççããoo eennttrree ssiisstteemmaass bbaallaanncceeaaddooss ee ddeessbbaallaanncceeaaddooss

Especificações RS-232 RS-423 RS-422 RS-485 Modo de Operação Desbalanceado Desbalanceado Diferencial Diferencial Numero total de drivers e receptores em uma linha (um driver ativo por vez nas redes RS-485)

1 Driver 1 Receptor

1 Driver 10 Receptores

1 Driver 10 Receptorwa

32 Driver 32 Receptores

Comprimento máximo do cabo 15 m. 1200 m. 1200 m. 1200 m. Taxa máxima de transmissão de dados (120 m. – 1200m. para RS-422/RS-485)

20 kbps 100 kbps 10 Mbps-100 Kbps

10 Mbps-100 Kbps

Tensão máxima de saída do Driver

±25V ±6V -0.25V a +6V -7V a +12V

Nível do sinal de saída do Driver ±5V a ±15V ±3.6V ±2.0V ±1.5V

2 Half-duplex: mecanismo de comunicação que permite a transmissão e recepção de dados no mesmo meio, mas nultiplexados no tempo.

Page 358: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

336336336336

AAAA

com carga. Nível do sinal de saída do driver sem carga.

±25V ±6V ±6V ±6V

Impedância de carga do Driver (Ohms) 3k a 7k >=450 100 54

Máxima corrente do Driver no estado de alta impedância (Ligado)

N/A N/A N/A ±100uA

Máxima corrente do Driver no estado de alta impedância (Desligado)

±6mA @ ±2v ±100uA ±100uA ±100uA

Slew Rate (Max.) 30V/uS Ajustável N/A N/A Faixa de Tensões de Entrada do Receptor

±15V ±12V -10V a +10V -7V a +12V

Sensibilidade de Entrada do Receptor

±3V ±200mV ±200mV ±200mV

Resistência de Entrada do Receptor (Ohms). (1 carga padrão para o RS-485)

3k a 7k 4k min. 4k min. >=12k

Tabela 20-1 – Comparação entre sistemas balanceados e desbalanceados

DDrriivveerrss ddee LLiinnhhaa DDeessbbaallaanncceeaaddooss

Cada sinal transmitido em um sistema de transmissão desbalanceado RS-232, aparece no conector de interface como um nível de tensão, referenciado ao sinal de terra. Por exemplo, o dado transmitido (TD) a partir de um dispositivo DTE3 aparecerá na diferença de potencial entre os pinos 2 (TX) e 7 (GND) de um conector DB-25. Esta diferença de potencial será negativa se a linha estiver inativa (idle) e alternará entre os níveis negativos e positivos enquanto estiverem sendo enviados os dados, entre níveis de ±5 a ±15 volts. Um receptor RS-232 típico, opera dentro da faixa de tensões de +3 a +12 volts, e de –3 a –12 volts.

O padrão RS-232 estabelece a faixa de níveis negativos como representação para o 1 lógico, e os níveis positivos para a representação do 0 lógico, como mostra a Figura 20-3.

D RInterface RS-232

Faixa para o nível lógico 0 (Space)

Faixa para o nível

lógico 1 (Mark)

+25V

+3V

-3V

-25V

Região de

Transição

Faixa do Receptor– Faixa máxima de operação

Figura 20-3 – Circuito de Interface RS-232

3 DTE: Data Terminal Equipment

Page 359: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

337337337337

AAAA

DDrriivveerrss ddee LLiinnhhaa BBaallaanncceeaaddooss

Num sistema diferencial balanceado, as tensões produzidas pelo driver aparecem através de um par de linhas de sinal que transmitem somente um sinal. A Figura 20-4 seguir mostra o símbolo esquemático de um driver de linha balanceado e as tensões esperadas. Um driver de linha balanceado pode produzir tensões de 2 a 6 volts entre os terminais de saída A e B, e ainda possuindo uma conexão como o sinal de terra (C). Embora seja apropriada uma conexão com o sinal de terra, esta não é utilizada pelo driver de linha balanceado na determinação do estado lógico da linha. Um driver de linha balanceado poderá ter também um sinal de entrada para habilitação, denominado sinal de ENABLE. O propósito deste sinal é o de permitir a conexão do driver com os pinos de saída A e B. Se o sinal de ENABLE estiver inativo, pode-se considerar que o driver está desconectado da linha de transmissão. Um driver RS-485 deverá ter obrigatoriamente o sinal de controle ENABLE. Um driver RS-422 poderá ter este sinal, mas nem sempre é necessário. A condição de desconectado (ou desabilitado) do driver, é referenciada como sendo uma condição de tristate ou seja, em alta impedância.

A

B

C Habilita (Opcional para RS-422 - Obrigatório para RS-485)

Faixa permissível

Faixa permissível

+6V

+2V

-2V

-6V

Tensão

VAB

Figura 20-4 – Driver de Linha Diferencial Balanceado

RReecceeppttoorreess BBaallaanncceeaaddooss

Um receptor de linha diferencial balanceada detecta o nível de tensão da linha de transmissão através de duas linhas de entrada, A e B. Este também possui um sinal de terra (C) que é necessário para efetuar uma conexão apropriada com a interface. A Figura 20-5 mostra o esquema de um receptor de linha diferencial balanceado. Neste esquema podem ser observados os níveis de tensão de operação. Se a tensão de entrada diferencial Vab for maior que +200 mV, o receptor reconhecerá um estado lógico específico no seu terminal de saída. Se a tensão de entrada estiver menor que –200 mV, o receptor criará uma tensão de estado lógico inverso no seu terminal de saída. Na figura são mostrados os níveis de tensão de entrada do receptor. Para prever atenuações na linha de transmissão, é requerida uma faixa de operação entre 200 mV e 6 V.

Page 360: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

338338338338

AAAA

A ½Vi

½Vi

VCM

B

C

VCM = Tensão de entrada de Modo comum

Faixa permitida para VCM : -7V < VCM < +7

+Vi

-Vi

+6V

+200mV

-6V

-200mV

Figura 20-5 – Receptor de Linha Diferencial Balanceado

PPaaddrrõõeess ddee TTrraannssmmiissssããoo TTIIAA//EEIIAA

A Electronic Industry Association (EIA) e a Telecommunications Industry Association (TIA) são as associações que desenvolveram padrões para simplificar as interfaces nos sistemas de comunicação de dados. Os padrões são destinados ao uso de interfaces em Data Terminal Equipment/Data Circuit-terminating Equipment (DTE/DCE - Equipamentos terminais de dados). O exemplo clássico de uma interface DTE/DCE é uma interface de um terminal para uma interface serial de modem. Embora, os padrões não são limitados ao uso exclusivo das interfaces DTE/DCE. De fato, muitos dos padrões são usualmente utilizados em uma ampla variedade de aplicações. Alguns exemplos incluem as interfaces de discos rígidos, barramentos de controle de chão-de-fábrica, e barramentos genéricos de I/O.

Inicialmente a EIA classificou os padrões com o prefixo "RS", que os identificava como Recommended Standard (padrão recomendado). Atualmente o prefixo foi substituído como "TIA/EIA", para ajudar a identificar a fonte do padrão. Além disto foi colocada uma letra no sufixo representando o nível de revisão do padrão. Por exemplo, TIA/EIA-232-E representa a quinta revisão do RS-232.

Os padrões de transmissão de dados TIA/EIA cobrem as seguintes áreas: Padrões Completos de interface, Padrões Elétricos e Padrões de Qualidade do sinal. Os padrões Completos definem as especificações funcionais, mecânicas e elétricas. Os padrões Elétricos, como seu nome indica, definem as especificações elétricas. Os padrões de Qualidade do Sinal definem os termos e métodos para a medição da qualidade do sinal. Exemplos de cada tipo são listados abaixo. Padrões de Interface DTE/DCE Completos TIA/EIA-232-F TIA/EIA-530-A TIA/EIA-561 TIA/EIA-574 TIA/EIA-613 TIA/EIA-687 TIA/EIA-688 TIA/EIA-723

Padrões Elétricos Padrões Desbalanceados TIA/EIA-232-F (Seção 2) TIA/EIA-423-B TIA/EIA-562

Page 361: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

339339339339

AAAA

TIA/EIA-694 Padrões Balanceados TIA/EIA-422-B TIA/EIA-485-A TIA/EIA-612 TIA/EIA-644

Signal Quality Standards EIA-334-A EIA-363 EIA-404-A

Os padrões RS-232, RS-422 e RS-485 implementam sistemas digitais de transmissão de dados, sendo RS-232 um sistema desbalanceado, enquanto que os dois últimos, diferenciais balanceados.

O propósito deste capítulo é o de descrever os principais elementos dos padrões RS-232, RS-422 e RS-485.

PPaaddrrõõeess TTIIAA//EEIIAA DDeessbbaallaanncceeaaddooss

PPaaddrrããoo TTIIAA//EEIIAA--223322--FF ((RRSS--223322)) TIA/EIA-232-F é o padrão de interface mais antigo e mais amplamente conhecido.

Este é um padrão Completo especificando os requisitos mecânicos (conectores), elétricos (características driver/receptor), e funcional (definição dos circuitos) para uma interface binária serial. Nas especificações elétricas, a norma especifica uma interface desbalanceada, unidirecional para comunicações ponto a ponto. O driver apresenta um slew-rate controlado, permitindo que o cabo seja visto como uma carga no lugar de uma linha de transmissão. Isto é devido ao tempo de transição do driver ser substancialmente maior que o atraso do cabo (velocidade x comprimento). A caga máxima capacitiva vista pelo driver é especificada como sendo de 2500 pF. A norma permite a operação de até 20 Kbps (19.2 Kbps). Para taxas maiores de transmissão são recomendadas as normas TIA/EIA-562 ou TIA/EIA-423-B. A Figura 20-6 ilustra uma aplicação típica, e a Tabela 20-2 lista os principais requisitos elétricos.

Algumas características importantes deste padrão são: Terminação Única Interface Ponto a Ponto Grande faixa de saída entre uma polaridade e outra Slew-rate controlado Interface totalmente definida Taxa máxima de transmissão de 20 Kbps.

Figura 20-6 - Aplicação Típica TIA/EIA-232-F

Page 362: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

340340340340

AAAA

EIA RS-232 Parâmetro Condições Min Max Unidade

Tensão de Saída a circuito aberto 25 V

Tensão de saída com carga 3 KΩ <=RL<=7 KΩ 5 15 V V

Resistência de saída do driver, Desconectado -2V<=Vo<=2V 300 Ω

Corrente de saída do driver de curto-circuito 500 mA

Slew Rate da saída do driver 30 V/µs Capacitância máxima de carga 2500 pF Resistência de entrada do receptor 3V <= VIN<=25V 3000 7000 Ω Limiar de entrada do Receptor Saída = Mark Saída = Space

-3

3

V V

Tabela 20-2 - Especificações Elétricas TIA/EIA-232-F

TTIIAA//EEIIAA--442233--BB A norma TIA/EIA-423-B, parecida a TIA/EIA-232-F, reduz o balanço da saída do

driver, e suporta taxas maiores de transmissão. Esta norma especifica um driver desbalanceado e um receptor balanceado. É um padrão Elétrico, especificando somente os requisitos do driver e do receptor. Os requisitos do receptor são idênticos aos especificados na norma TIA/EIA-422-B. A TIA/EIA-422-B é uma norma que serve de referência para uma norma completa, tal como TIA/EIA-530-A. A norma TIA/EIA-423-B especifica uma interface unidirecional e multidrop (até 10 receptores). Algumas vantagens deste padrão sobre o TIA/EIA-232-F são: operação com múltiplos receptores, maiores taxas de transmissão de dados e fontes de alimentação comum (tipicamente ±5V). A Figura 20-7 mostra uma aplicação típica, e a Tabela 20-3 mostra os principais requisitos elétricos.

Figura 20-7 - Aplicação Típica TIA/EIA-423-B

Algumas características importantes deste padrão são: Drivers desbalanceados e receptores balanceados Sistema multidrop (múltiplos receptores) Controle da forma de onda (saída do driver) Faixa de modo comum do receptor em ±7V Sensibilidade do receptor de ±200 mV Máxima taxa de transmissão em 100 Kbps (@12m) Máximo comprimento do cabo de 1200 m (@ 1 Kbps)

EIA RS-423

Parâmetro Condições Min Max Unidade

Tensão de Saída a circuito aberto

4 -4

6 -6

V V

Page 363: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

341341341341

AAAA

Tensão de saída com carga RL= 450 Ω 3.6 6 V Resistência de Saída do Driver -2V<=Vo<=2V 50 Ω Corrente de saída do driver de curto-circuito ±150 mA

Tempo de subida e descida do Driver Baudrate <= 1 K Baud Baudrate >= 1 K Baud 300

30

µs %

Unidade de

Intervalo Sensibilidade do receptor Vcm <= ±7V ±200 mV Resistência de entrada do receptor 4000 Ω

Tabela 20-3 - Especificações Elétrica TIA/EIA-423-B

TTIIAA//EEIIAA--556622 O padrão TIA/EIA-562 é uma norma muito parecida com a TIA/EIA-232-F, mas

que suporta maiores taxas de transmissão (64 Kbps). É um padrão Elétrico, que serve de referência para o padrão completo, tal como o TIA/EIA-561. A norma TIA/EIA-561 especifica uma interface desbalanceada, unidirecional e de ponto a ponto. Esta norma suporta interoperabilidade com dispositivos TIA/EIA-232-F. A Figura 20-8 mostra uma aplicação típica, e a Tabela 20-4 mostra os principais requisitos elétricos.

Figura 20-8 - Aplicação Típica TIA/EIA-561

Algumas características importantes deste padrão são: Driver e receptor são balanceados Comunicação Ponto a Ponto Interoperabilidade com dispositivos TIA/EIA-232-F Taxa máxima de transmissão de 64 Kbps

Parâmetro Limites e Unidades Driver Loaded Output Voltages (Min. Level) 4

≥|3.3 V|

Driver Open Circuit Voltage 5 ≤|13.2 V| Driver Loaded Output Voltages (3kΩ) 6 ≥|3.7 V| Driver Short Circuit Current 7 ≤|60 mA| Driver Transition Time8 Controlado Maximum Driver Slew Rate 9 ≤ 30 V/µs Driver Output Resistance (Power Off) 10 ≥ 300 Ω Receiver Input Resistance 11 3 kΩ a 7kΩ Maximum Receiver Input Voltage 12 ± 25 V

4 Tensão de saída do driver carregado. 5 Tensão de saída de circuito aberto. 6 Tensão de saída do driver carregado. 7 Corrente de curto-circuito. 8 Tempo de transição. 9 Taxa de máxima de variação do sinal. 10 Resistência de saída com alimentação desligada. 11 Resistência de entrada do receptor. 12 Máxima tensão de entrada do receptor.

Page 364: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

342342342342

AAAA

Receiver Thresholds 13 ± 3 V

Tabela 20-4 - Principais Especificações Elétricas TIA/EIA-562

TTIIAA//EEIIAA--669944 A norma TIA/EIA-694 é um padrão elétrico relativamente novo, mas muito

similar com o TIA/EIA-232-F, suporta maiores taxas de transmissão (512 Kbps). É um padrão Elétrico, que se destina a referenciar uma norma completa, tal como a TIA/EIA-723. A norma TIA/EIA-694 especifica uma interface desbalanceada, unidirecional e ponto a ponto. Esta norma suporta a interoperabilidade com dispositivos TIA/EIA-232-F. A Figura 20-9 mostra uma aplicação típica, e a Tabela 20-5 mostra os principais requisitos elétricos.

Figura 20-9- Aplicação típica TIA/EIA-694

Algumas características importantes deste padrão são: Driver e receptor desbalanceado Comunicação Ponto a Ponto Interoperabilidade com dispositivos TIA/EIA-232-F Taxa máxima de transmissão de dados de 512 Kbps

Parâmetro Limites e Unidades Driver Open Circuit Voltage 14 ≤|5.5 V| Driver Loaded Output Voltages (3kΩ) 15 ≥|3.0 V| Driver Short Circuit Current 16 ≤|100 mA| Driver Transition Time17 Controlado Driver Output Resistance 18 ≥ 300 Ω Receiver Input Resistance 19 3 kΩ Maximum Receiver Input Voltage 20 ± 12 V Receiver Thresholds 21 ± 2 V

Tabela 20-5 - - Principais Especificações Elétricas TIA/EIA-694

PPaaddrrõõeess TTIIAA//EEIIAA BBaallaanncceeaaddooss ((DDiiffeerreenncciiaall))

TTIIAA//EEIIAA--442222--BB A norma TIA/EIA-422-B é um padrão Elétrico, que especifica um driver

balanceado e receptores balanceados. Os requisitos dos receptores são idênticos aos especificados na norma TIA/EIA-423-B. Esta norma especifica uma interface com 13 limiar de detecção do receptor. 14 Tensão de saída de circuito aberto. 15 Tensão de saída do driver carregado. 16 Corrente de curto-circuito. 17 Tempo de transição. 18 Resistência de saída. 19 Resistência de entrada do receptor. 20 Máxima tensão de entrada do receptor. 21 limiar de detecção do receptor.

Page 365: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

343343343343

AAAA

terminação, unidirecional, de único driver e de múltiplos receptores. A Figura 20-10 mostra uma aplicação típica de ponto a ponto, com terminação localizada na entrada do receptor (extremidade final do cabo). A Figura 20-11 ilustra uma interface TIA/EIA-422-B totalmente carregada. Novamente a terminação é localizada na extremidade do cabo, além disto, o comprimento total de vê ser minimizado para limitar as reflexões. A Tabela 20-6 mostra os principais requisitos elétricos.

Algumas características importantes deste padrão são: Interface balanceada Multidrop (Operação com múltiplos receptores) Taxa de transmissão máxima de 10 Mbps (@ 12 m) Comprimento Máximo de cabo de 1200 m (@ 100 Kbps)

Parâmetro Limites e Unidades Driver Open Circuit Voltage 22 ≤|10 V| Driver Loaded Output Voltages 23 ≥|2 V| Balance of Loaded Output Voltage 24 ≤ 400 mV Driver Output Offset Voltage 25 ≤ 3 V Balance of Offset Voltage 26 ≤ 400 mV Driver Short Circuit Current 27 ≤|150 mA| Driver Leakage Current28 ≤|100 µA| Driver Output Impedance 29 ≤ 100 Ω Receiver Input Resistance 30 ≥ 4 kΩ Driver Transition Time31 Controlado Receiver Thresholds 32 ± 200 mV Receiver Internal Bias 33 ≤ 3.0 V Maximum Receiver Input Current 34 3.25 mA Receiver Common Mode Range 35 ± 7 V (± 10 V) Receiver Operating Differential Range 36 ± 200 mV a ± 6 V Maximum Differential Input Voltage 37 ± 12 V

Tabela 20-6 - Especificações TIA/EIA-422-B

Figura 20-10 - Aplicação TIA/EIA-422-B Ponto a Ponto

22 Tensão de saída de circuito aberto. 23 Tensão de saída do driver carregado. 24 Balanço de tensões de saída carregada 25 Tensão de offset de saída. 26 Balanço da tensão de offset. 27 Corrente de curto-circuito. 28 Corrente de dreno do driver. 29 Impedância de Saída do Driver. 30 Resistência de entrada do receptor. 31 Tempo de transição. 32 limiar de detecção do receptor. 33 Polarização interna do receptor 34 Máxima corrente de entrada do receptor. 35 Faixa de modo comum do receptor. 36 Faixa de operação diferencial do receptor. 37 Tensão máxima de entrada diferencial.

Page 366: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

344344344344

AAAA

Figura 20-11 - Aplicação típica TIA/EIA-422-B Multidrop

O padrão EIA-422 foi intitulado “Electrical Characteristics of Balanced Voltage Digital Interface Circuits”, e define as características dos circuitos de interface RS-422. A Figura 20-12 mostra uma interface típica RS-422 a 4 fios. Deve-se notar que são utilizados 5 fios no total, já que um dos fios é o sinal de terra. Cada transmissor ou driver pode alimentar até 10 receptores. Os dois estados lógicos das linhas, são definidos como segue:

Quando o terminal A do driver for negativo com respeito ao terminal B, a linha estará no estado de 0 lógico (MARK ou OFF).

Quando o terminal A do driver for positivo com respeito ao terminal B, a linha estará no estado de 1 lógico (SPACE ou ON).

Figura 20-12 – Rede RS-422 de 4 fios típica

No circuito da Figura 20-12 G é o gerador ou driver, R é o receptor, GWG é o fio de aterramento de todo o sistema.

A Figura 20-13 mostra a condição de tensão da linha balanceada para um conversor RS-232 para RS-422 quando a linha estiver na condição idle (estado inativo). Também pode ser visualizada a relação entre os terminais A e B do sistema RS-422 e as marcas + e - utilizadas na maioria dos equipamentos. A mesma relação pode ser aplicada aos sistemas RS-485. Os sistemas RS-422 podem suportar tensões de modo comum (Vcm) de ±7 volts. A tensão de modo comum é definida como sendo a média dos terminais A e B com respeito ao terra.

Page 367: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

345345345345

AAAA

Figura 20-13 – Relações entre os terminais ‘A’ e ‘B’ nos dispositivos RS-422 ou RS-485 e os identificadores convencionais ‘+’ e ‘-‘.38

EIA RS-422 Parâmetro Condições Min Max Unidade

Tensão de Saída a circuito aberto

10 -10

V V

Tensão de saída com carga RT = 100 Ω

2 -2 V

V Resistência de saída do driver A para B 100 Ω Corrente de saída do driver de curto-circuito

Por pino de saída para o comum ±150 mA

Tempo de subida da saída do driver RT = 100 Ω 10 % da largura de bit

Tensão de modo comum do driver RT = 100 Ω ±3 V Sensibilidade do receptor Vcm <= ±7 ±200 mV Tensão de modo comum do receptor -7 +7 V Resistência de entrada do receptor 4000 Ω

Tensão diferencial do receptor Operacional: Suportável: ±10

±12 V V

Tabela 20-7 – Especificações elétricas do padrão RS-422

TTIIAA//EEIIAA--448855--AA A norma TIA/EIA-485-A é um padrão elétrico, que especifica drivers e receptores

balanceados. A mesma fornece todas as vantagens da norma TIA/EIA-422-A além de suportar a operação com múltiplos drivers. Esta norma permite o uso de configurações de barramento compartilhado. A norma especifica uma interface bidirecional (half-duplex) multiponto. A Figura 20-14 ilustra uma aplicação multiponto típica, e a Tabela 20-8 lista as principais características elétricas.

Algumas características importantes deste padrão são: Interface Balanceada Operação Multiponto Operação com fonte de alimentação única de+5V Faixa de modo comum de barramento de 0.7V a +12V Carga de até 32 Transceptores (Unidades de carga) Taxa máxima de 10 Mbps (@ 12 m)

38 DVM: Digital Voltmeter. Em português, Voltmetro digital.

Page 368: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

346346346346

AAAA

Comprimento máximo do cabo de 1200 m (@ 100 kbps) Parâmetro Limites e Unidades Driver Open Circuit Voltage 39 ≤|6 V| Driver Loaded Output Voltages 40 ≥|1.5 V| Balance of Driver Loaded Output Voltage 41 ≤ |200 mV| Maximum Driver Offset Voltage 3.0 V Balance of Driver Offset Voltage 42 ≤ |200 mV| Driver Transition Time43 ≤ 30% Tui Driver Short Circuit Current (-7V a + 12V) 44 ≤|250 mA| Receiver Thresholds 45 ± 200 mV Maximum Bus Input Current +12V/-7V ≤ 1.0 mA/≤ 0.8 mA Max. Unit Loads 46 32

Tabela 20-8 - Especificações elétricas TIA/EIA-485-A

Figura 20-14 - Aplicação típica TIA/EIA-485-A

O padrão RS-485 permite que a linha de transmissão balanceada seja compartilhada. No total, 32 pares de receptores/transmissores podem compartilhar a rede multidrop. Muitas das características dos drivers e receptores são os mesmos que o padrão RS-422. A faixa de tensões de modo comum Vcm que os receptores e drivers podem tolerar, foi expandida para a faixa de +12V a -7V. Quando algum driver for desconectado do barramento, este deverá suportar as tensões de modo comum enquanto permanecer no estado tristate. Alguns drivers RS-422 não suportam esta faixa, mesmo tendo a capacidade de operar no modo tristate.

39 Tensão de saída de circuito aberto. 40 Tensão de saída do driver carregado. 41 Balanço de tensões de saída carregada 42 Balanço da tensão de offset. 43 Tempo de transição. 44 Corrente de curto-circuito. 45 limiar de detecção do receptor. 46 Número máximo de cargas de entrada.

Page 369: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

347347347347

AAAA

A Figura 20-15 mostra uma rede multidrop típica. Notar que a linha de transmissão possui duas terminações nas suas extremidades e nenhuma no meio da rede. A terminação deve ser utilizada em altas taxas de dados e para distâncias grandes. A utilização da linha do sinal de terra é recomendada nos sistemas RS-485 para assegurar a tensão de modo comum que o receptor deverá suportar entre -7V e +12V.

Figura 20-15 – Rede multidrop RS-485 a dois fios

Uma rede RS-485 poderá também ser conectada no modo de 4 fios, como mostra a Figura 20-16. Notar que também é utilizado um fio de terra adicional, além das 4 linhas de dados. Numa rede de 4 fios é necessário que um nó seja o mestre e todos os outros sejam escravos. A rede é conectada de forma que o nó mestre se comunica com todos os nós escravos. Todos os nós escravos podem-se comunicar somente com o nó mestre.

Page 370: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

348348348348

AAAA

1200 m

Figura 20-16 – Rede multidrop RS-485 a quatro fios

EIA RS-485 Parâmetro Condições Min Max Unidade

Tensão de Saída a circuito aberto

1.5 -1.5

6 -6

V V

Tensão de saída com carga RLOAD = 54 Ω

1.5 -1.5

5 -5

V V

Corrente de saída do driver de curto-circuito Per output to +12V or -7V ±250 mA

Tempo de subida da saída do driver RLOAD = 54 Ω CLOAD = 50 pF 30 % da largura de bit

Tensão de modo comum do driver RLOAD = 54 Ω -1 3 V Sensibilidade do receptor -7 <= Vcm <= +12 ±200 mV Tensão de modo comum do receptor -7 +12 V Resistência de entrada do receptor 12K Ω

Tabela 20-9 - Características elétricas do padrão RS-485

TTIIAA//EEIIAA--661122 A norma TIA/EIA-612 é um padrão elétrico, que especifica um driver e um

receptor balanceado. Esta norma especifica taxas de até 52 Mbps utilizando a tecnologia ECL. Este padrão especifica uma interface unidirecional ponto a ponto. A Figura 20-17 ilustra uma aplicação típica com a terminação localizada na entrada do receptor (extremidade do cabo). A Tabela 20-10 mostra os requisitos elétricos. Este padrão é referenciado pela TIA/EIA-613, e juntas implementam a HSSI (High Speed Serial Interface - Interface serial de alta velocidade).

Parâmetro Limites e Unidades Driver Open Circuit Voltage 47 ≤|1.5 V| Driver Loaded Output Voltages48 ≥|590 mV|

47 Tensão de saída de circuito aberto. 48 Tensão de saída do driver carregado.

Page 371: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

349349349349

AAAA

Balance of Loaded Output Voltage 49 ≤ |100 mV| Driver Output Offset Voltage ≤ 0 V e ≥ - 1.6 V Balance of Offset Voltage ≤ |100 mV| Driver Short Circuit Current ≤ 50 mA Receiver Thresholds ± 150 mV Receiver Input Range -0.5 V a –2.0 V Receiver Input Current ≤ 350 µA Maximum Differential Input Voltage ≤ 1.5 V

Tabela 20-10 - Especificações Elétricas TIA/EIA-612

TTIIAA//EEIIAA--664444 ((LLVVDDSS)) A norma TIA/EIA-644 é um padrão elétrico, que especifica driver e receptores

balanceados. Este padrão especifica taxas de transmissão de até 655 Mbps (maiores taxas são possíveis dependendo da aplicação e do equipamento) utilizando a tecnologia LVDS (Low Voltage Differential Signaling). A Tabela 20-11 lista os requisitos elétricos. Esta norma é destinada a ser referenciada por outros padrões que especifiquem uma interface completa.

Parâmetro Limites e Unidades Driver Open Circuit Voltage 247 mV ≤ Vdiff ≤ 454 mV Driver Offset Voltage 1.125V ≤ Vos ≤ 1.375 V Driver Short Circuit Current ≤ 24 mA Receiver Thresholds ± 100 mV Receiver Differential Input Range 0 V a +2.4 V Receiver Input Current ± 20 µA

Tabela 20-11 - Especificações elétricas TIA/EIA-644 LVDS

Figura 20-17 - Aplicação típica TIA/EIA-612 e TIA/EIA-644 Ponto a Ponto

OOuuttrrooss PPaaddrrõõeess TTIIAA//EEIIAA

TTIIAA//EEIIAA--223322--FF A norma TIA/EIA-232-F é um padrão que especifica uma interface serial

DTE/DCE. É um padrão completo que especifica as funções das linhas (dados, temporização e controle) e um conector de 25 pinos. Esta norma suporta taxas de até 20 Kbps. São previstas duas opções de conector, um conector tipo D de 25 pinos, e um conector menos de 26 pinos.

EEIIAA--333344--AA A norma EIA-334-A define a qualidade do sinal para as interfaces seriais síncronas

DTE/DCE. Esta norma é referenciada por um padrão completo de sistemas síncronos.

49 Balanço de tensões de saída carregada

Page 372: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

350350350350

AAAA

EEIIAA--336633 A norma EIA-363 define a qualidade do sinal para as interfaces seriais assíncronas

DTE/DCE. Esta norma é referenciada por um padrão completo de sistemas assíncronos.

EEIIAA--440044--AA A norma EIA-404-A define a qualidade do sinal para interfaces assíncronas

DTE/DCE do tipo start-stop.

EEIIAA--444499 A norma EIA-449 especifica uma interface serial DTE/DCE de propósito geral. É

uma norma completa que especifica a função das linhas (dados, temporização e controle) e um conector de 37 pinos. Esta norma referencia os padrões 422 e 423 para os requisitos dos drivers e receptores. Esta norma suporta taxas de até 2 Mbps. Os novos projetos utilizam TIA/EIA-530-4 no lugar de EIA-449 (RS-449).

TTIIAA//EEIIAA--553300--AA A norma TIA/EIA-530-A especifica uma interface serial DTE/DCE de alta

velocidade. É uma norma completa que especifica a função das linhas (dados, temporização e controle) e um conector de 25 pinos. Esta norma referencia os padrões TIA/ EIA-422-B e TIA/EIA-423-B para os requisitos dos drivers e receptores. Esta norma suporta taxas de até 2.1 Mbps. São especificadas duas opções de conector, um conector tipo D de 25 pinos, e um menor de 26 pinos. Obs.: Existem diferenças nas conexões dos pinos entre os padrões EIA-530 e TIA/EIA-530-A.

TTIIAA//EEIIAA--556611 A norma TIA/EIA-561 especifica uma interface serial DTE/DCE. É uma norma

completa que especifica a função das linhas (dados, temporização e controle) e um pequeno conector de 8 pinos (MJ8). Esta norma referencia os padrões TIA/EIA-562 para os requisitos dos drivers e receptores. Esta norma suporta taxas de até 38.4 Kbps.

TTIIAA//EEIIAA--557744 A norma TIA/EIA-574 especifica uma interface serial DTE/DCE assíncrona. É

uma norma completa que especifica a função das linhas (dados, temporização e controle) e um conector de 9 pinos. Esta norma referencia os padrões TIA/EIA-562 para os requisitos dos drivers e receptores. Esta norma suporta taxas de até 38.4 Kbps.

TTIIAA//EEIIAA--661133 A norma TIA/EIA-613 especifica uma interface serial DTE/DCE de propósito

geral, para taxas de transmissão de até 52 Mbps. É uma norma que especifica o funcionamento e o conector, utilizando como referencia os padrões TIA/EIA-562 para os requisitos elétricos. As normas TIA/EIA-612 e TIA/EIA-613 implementam a interface HSSI.

Page 373: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

351351351351

AAAA

TTIIAA//EEIIAA--668877 A norma TIA/EIA-687 especifica uma interface serial DTE/DCE de velocidade

média. É uma norma completa que especifica a função das linhas (dados, temporização e controle) e um conector de 25 ou 26 pinos. Esta norma referencia os padrões TIA/EIA-423-B para os requisitos dos drivers e receptores.

TTIIAA//EEIIAA--668888 A norma TIA/EIA-688 especifica uma interface serial DTE/DCE para

equipamentos telefônicos celulares digitais. É uma norma completa que especifica a função das linhas (dados, temporização e controle) e um conector de 12 pinos. Esta norma referencia os padrões TIA/EIA-694 para os requisitos dos drivers e receptores. Esta norma suporta taxas de até 512 Kbps.

TTIIAA//EEIIAA--772233 A norma TIA/EIA-723 é um padrão completo que especifica uma interface serial

DTE/DCE de alta velocidade. É uma norma completa que especifica a função das linhas (dados, temporização e controle) e um conector de 25 ou 26 pinos. Esta norma referencia os padrões TIA/EIA-694 para os requisitos dos drivers e receptores. Esta norma suporta taxas de até 512 Kbps.

RReeccoommeennddaaççõõeess CCCCIITTTT//IITTUU--TT

O CCITT50 cria e mantêm padrões que se destinam a ajudar a padronizar os serviços de telecomunicações internacionais. Estes padrões são recomendações técnicas práticas e métodos, embora, em alguns paises elas são consideradas obrigatórias. O CCITT revisa as suas normas num ciclo de 4 anos. A maioria dos padrões de interface está localizada no volume oitavo da série V do CCITT. Este volume é intitulado como “Data Communication over the Telephone Network”. Alguns dos padrões de interface estão descritos na série X. O prefixo CCITT tem sido substituído pelo prefixo ITU de International Telecommunications Union. Uma referencia cruzada poder ser vista na Tabela 20-12.

Série V Série X V.10 X.26 V.11 X.27

Tabela 20-12 - Referencia cruzada entre a serie V e X

RReeccoommeennddaaççããoo VV..1100

A recomendação V.10 define as características elétricas para uma interface desbalanceada. A recomendação V.11 especifica drivers e receptores balanceados. Com exceção da especificação de tensão de saída a circuito aberto do driver (transmissor), os requisitos são similares aos da norma TIA/EIA-423-B. A V.11 requer de uma carga diferencial de 3.9 KΩ para a saída do driver em circuito aberto, enquanto que as condições de teste da TIA/EIA-423-B é a circuito aberto sem carga. As especificações do receptor 50 International Telegraph and Telephone Consultative Committee

Page 374: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

352352352352

AAAA

são também similares, com exceção da especificação de limiar de entrada. A recomendação V.11 define limiares de ±300 mV enquanto a TIA/EIA-423-B define uma especificação de ±200 mV.

RReeccoommeennddaaççããoo VV..1111 A recomendação V.11 define as características elétricas para uma interface

balanceada. A recomendação V.11 especifica drivers e receptores balanceados. Com exceção da especificação de tensão de saída a circuito aberto do driver (transmissor), os requisitos são similares aos da norma TIA/EIA-422-B. A V.11 requer de uma carga diferencial de 3.9 KΩ para a saída do driver em circuito aberto, enquanto que as condições de teste da TIA/EIA-422-B é a circuito aberto. As especificações do receptor são também similares, com exceção da especificação de limiar de entrada. A recomendação V.11 define limiares de ±300 mV enquanto a TIA/EIA-422-B define uma especificação de ±200 mV.

RReeccoommeennddaaççããoo VV..2244 A recomendação V.24 define a função dos circuitos de intercâmbio para as

interfaces DTE/DCE. A classe do circuito (dados, temporização ou controle), direção e outras definições são descritas nesta recomendação.. A V.24 destina-se a ser referenciada por outras recomendações.

RReeccoommeennddaaççããoo VV..2288 A recomendação V.28 define as características elétricas para interfaces

desbalanceadas. Este padrão especifica as características das saídas do driver e das entradas do receptor. O padrão é muito similar à seção de características elétricas do padrão TIA/EIA-232-F. Uma das diferenças são os requisitos para o driver na especificação de slew rate. O padrão TIA/EIA-232-F especifica um limite mais baixo de V/µs (@20 Kbps), (medida enter os níveis de +3V e 0.3V), enquanto que na recomendação V.28 o limite mais baixo é de 4 V/µs (@20 kbps). Ambos padrões especifica o mesmo limite mais alto de 30 V/µs baixo condições de pouca carga. O padrão TIA/EIA-232-F define uma interface completa, enquanto que a V.28 somente define a seção de características elétricas da TIA/EIA-232-F. O padrão de interface completo é coberto pelas recomendações CCITT V.28 (Elétrica), V.24 (funcional) e a ISSO 2110 & 4902 (mecânica).

RReeccoommeennddaaççããoo VV..3355 A recomendação V.35 é o padrão de modems que também define uma interface

balanceada. Enquanto muitas aplicações operam em taxas substancialmente maiores que 45 Kbps (tipicamente > 1 Mbps), a interface é somente definida para operar em até 48 Kbps. Para linhas de controle de baixa velocidade, a norma recomenda o uso de drivers e receptores V.28. Para o uso em altas taxas de dados e de linhas com temporização, a norma recomenda o uso de um único driver V.35 balanceado. Os drivers ser caracterizam pela pequena faixa de operação de ±0.55V através de uma carga de terminação de 100 Ω. O driver é também especificado para ter uma faixa de variação perto do potencial terra seguindo uma tensão de 0 V de tensão de offset. A maioria das implementações utiliza drivers de corrente de modo diferencial com resistores externos, para implementar drivers balanceados V.35. Esta norma já foi rescindida, sendo recomendados como substituição os drivers V.10 e V.11.

Page 375: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

353353353353

AAAA

OOuuttrrooss PPaaddrrõõeess

IIEEEEEE448888 O IEEE51 também possui um órgão de desenvolvimento de padrões. De forma

geral, os padrões IEEE tratam das especificações totais do barramento. A IEEE488 é uma norma completa de descrição de Barramentos (Bus) relacionando as especificações elétricas, mecânicas e funcionais de um barramento paralelo de instrumentação.

Os barramentos são comumente utilizados para a comunicação entre equipamentos de teste e o controle de máquinas. O padrão permite que 15 dispositivos possam ser conectados juntos, através de um cabo com comprimento de até 18 metros. O padrão define 16 linhas compostas de 3 linhas de controle, 5 de gerenciamento e 8 linhas de dados. As principais especificações são mostradas na Tabela 20-13.

Símbolo Parâmetro Condições Min Max Unidade VOH Driver Output Voltage IOH = -5.2 mA 2.4 V VOL Driver Output Voltage IOL = 48 mA 0.4 V IOZ Driver Output Leakage Current VO = 2.4 V ±40 µA IOH Driver Output Current Open Collector VO = 5.25 V 250 µA VIH Receiver Input Voltage 2.0 V VIL Receiver Input Voltage 0.8 V IIH Receiver Input Current VIN = 2.4 V 40 µA IIL Receiver Input Current VIN = 0.4 V -1.6 mA ICC Receiver Clamp Current VIN = -1.5 V 12 mA RL1 Termination Resistor VCC = 5 V ± 5% 2850 3150 Ω RL2 Termination Resistor VCC = GND 5890 6510 Ω

Tabela 20-13 - Características elétricas IEEE488

CCoonnffiigguurraaççããoo ddee SSiisstteemmaass EEIIAA--442222 ee EEIIAA--448855

CCoonnttrroollee TTrriissttaattee ddee uumm ddiissppoossiittiivvoo RRSS--448855 uuttiilliizzaannddoo oo ssiinnaall RRTTSS

Como foi visto anteriormente, um sistema RS-485 poderá estar desconectado da linha de transmissão quando o nó respectivo não estiver transmitindo. Em um conversor RS-232 para RS-485 ou em uma placa serial RS-485 poderá ser feito o controle tristate utilizando o sinal de controle RTS da porta serial assíncrona, para habilitar o driver RS-485. A linha RTS é conectada ao sinal de habilitação do driver de forma que setando a linha em nível lógico 1, habilitará o driver de transmissão. Setando a linha RTS para nível lógico 0, colocará o driver na condição tristate. Isto de fato, desconecta o driver do barramento, permitindo que outros nós possam transmitir sobre o mesmo par de fios. A Figura 20-18 mostra o diagrama de tempos de um conversor típico RS-232 para RS-485. Quando for utilizado o sinal de controle RTS, é importante que este esteja setado e nível lógico alto antes que os dados sejam enviados. Também, a linha RTS deverá ser setada em nível baixo depois que o último bit de dados seja enviado. Esta temporização é feita pelo software utilizado no controle da porta serial e não pelo conversor.

Quando uma rede RS-485 é conectada em uma linha de dois fios compartilhada, o receptor de cada nó será conectado à linha (ver Figura 20-15). O receptor poderá ser

51 IEEE: Institute of Electrical and Electronics Engineers

Page 376: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

354354354354

AAAA

configurado para receber um eco das suas próprias transmissões de dados. Isto pode ser desejável em alguns sistemas, mas problemáticos em outros.

Figura 20-18 – Diagrama de tempos para um conversor RS-232 para RS-485 com controle RTS do driver

e receptor RS-485

CCoonnffiigguurraaççõõeess

TTooppoollooggiiaass ddee RReeddee

A configuração da rede não é definida na especificação RS-422 e RS-485. Na maioria dos casos o projetista pode utilizar a configuração que achar mais adequada para o sistema e requisitos físicos.

Figura 20-19 – Top

Conversor RS-232 para TTL

Conversor TTL para RS-485

Barramento com derivações (aceitável)

Barramento com estrelas ou agrupamentos (evitar)

Rede e estrela (evitar)

Barramento encadeado (melhor)

Anel (evitar)

ologias de rede

Page 377: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

355355355355

AAAA

SSiisstteemmaass aa DDooiiss oouu QQuuaattrroo FFiiooss Os sistemas RS-422 requerem um par de fios dedicados para cada sinal, um par

transmissor, um par receptor, e um par adicional para cada sinal de controle ou handshake utilizado (se requerido). A capacidade de operação no modo tristate do RS-485 permite que um simples par de fios seja compartilhado para transmitir e receber sinais em comunicações half-duplex. Esta configuração a "dois fios" (notar que é recomendada a utilização de um condutor de sinal de terra) reduz o custo do cabeamento. Os dispositivos RS-485 podem ser interna ou externamente configurados para sistemas a dois fios. Os dispositivos configurados internamente, simplesmente provêm às conexões A e B (às vezes denominadas de "+" e "-").

Os dispositivos configurados a quatro fios possuem as conexões A e B (ver Figura 20-11) para o par transmissor e o par receptor. O usuário pode conectar as linhas do transmissor com as linhas do receptor para criar uma configuração a dois fios. Este tipo de dispositivo prove ao projetista de uma grande flexibilidade. Pode-se notar que a linha do sinal de terra deve também ser conectado ao sistema. Esta conexão é necessária para manter a tensão de modo comum no receptor, dentro de uma faixa segura. O circuito de interface pode operar sem a conexão do sinal de terra, mas pode sacrificar a confiabilidade e a imunidade ao ruído. A Figura 20-20 e a Figura 20-21, mostram as conexões para os sistemas a dois e quatro fios.

Figura 20-20 – Configuração típica de rede multidrop RS-485 a Quatro Fios

1200 m

Page 378: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

Figura 20-21 – Configuração típ

TTeerrmmiinnaaççããoo A terminação (terminais de rede) p

de um nó com a impedância da linha dimpedâncias não estiverem casadas, o sinapela carga e uma porção deste será refimpedâncias da fonte, da linha de transmieliminadas. A terminação também possucarga para os drivers, aumento da complpolarização e diminuição da flexibilidade d

A decisão de utilizar a terminaçãotaxa de dados utilizada pelo sistema. Umda linha de dados é muito menor que a lanecessária. Esta regra assume que as rreceptora amostrará os dados na metade dsinal seja consistente neste instante. Por ede dados, o atraso de propagação pode cabo pela velocidade de propagação do sida velocidade da luz (c), é especificado pel

Por exemplo, uma conexão de idaUtilizando uma velocidade de propagaçãode 6.2 µs. Assumindo que as reflexões irãdo cabo, o sinal irá estabilizar em 18.6 µs um bit possui uma largura de 104 µs. As rbit, portanto a terminação não é necessária

Resistores de te

1200 m

356356356356

AAAA

ica de rede multidrop RS-485 a Dois Fios

ode ser utilizada para casamento de impedâncias e transmissão que está sendo utilizada. Se as l transmitido não será completamente absorvido

letido de volta na linha de transmissão. Se as ssão e da carga forem iguais, estas reflexões serão i algumas desvantagens, tais como o aumento da exidade do circuito, mudanças nos requisitos de e modificações no sistema.

deve ser baseada no comprimento do cabo e na a boa regra é verificar se o atraso de propagação rgura de um bit. Neste caso, a terminação não é eflexões amortecerão rapidamente. A UARTs e cada bit, portanto é importante que o nível do xemplo, em um sistema com 600 metros de linha ser calculado multiplicando o comprimento do

nal no mesmo. Este valor, tipicamente 66 a 75% o fabricante do cabo.

e volta cobre um total de 1200 metros de cabo. de 0.66 × c, o tempo para o sinal ir e voltar será o amortecer em 3 idas e voltas pelo comprimento após a borda do bit. Para uma taxa de 9600 bps, eflexões irão amortecer muito antes do centro do .

rminação somente nas extremidades

Page 379: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

357357357357

AAAA

Existem vários métodos de terminação para as linhas de dados. Um método recomendado é a terminação em paralelo. Um resistor é adicionado em paralelo com as linhas de recepção A e B, de forma a compatibilizar a impedância característica da linha de dados, especificada pelo fabricante do cabo (um valor comum é 120 ohms). Este valor descreve a impedância intrínseca da linha de transmissão e não é função do comprimento da linha. Neste caso não deverá ser utilizado um resistor de terminação com menos de 90Ω. Os resistores de terminação devem ser colocados somente nas extremidades da linha de dados, e não mais do que duas terminações em qualquer sistema que não utilize repetidores. Este tipo de terminação claramente carrega o sistema.

Outro tipo de terminação é o acoplamento AC, colocando um pequeno capacitor em série com o resistor de terminação para eliminar o efeito da carga em CC. Embora este método elimine o carregamento, a seleção do capacitor é altamente dependente das propriedades do sistema.

A Figura 20-22 ilustra ambos tipos de terminação, a paralela e o acoplamento AC para um nó RS-485 a dois fios. No caso de sistemas a quatro fios, a terminação é colocada no receptor do nó.

Figura 20-22 - Terminação em paralelo e com acoplamento AC

Terminação em paralelo Terminação com acoplamento AC

Page 380: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

358358358358

AAAA

Figura 20-23 – Terminação de red

PPoollaarriizzaannddoo uummaa rreeddee RRSS--448855 Quando uma rede RS-485 estiver no estado idle, to

escuta (recepção). Nesta condição não haverá drivers atestarão em estado tristate. Sem qualquer driver atuando desconhecido. Se os níveis de tensão nas entradas recept±200mV o nível lógico da saída dos receptores será o vaforma a manter o estado apropriado de tensão de idle, podforçar as linhas de dados para a condição de repouso. Essão nada mais que resistores de pull-up na linha de dados B(a terra) na linha A.

A Figura 20-24 ilustra a colocação dos resistores dede uma configuração a dois fios. Notar que na configu

Sem terminação

Terminação em paralelo

Terminação bidirecional

Terminação AC

es

dos os nós estarão no modo de ivos na rede. Todos os drivers na rede, o estado da linha será oras A e B forem menores que lor do ultimo bit recebido. De em ser utilizados resistores para

tes resistores de polarização não (tipicamente 5V) e de pulldown

polarização em um transceptor ração RS-485 a quatro fios, os

Page 381: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

359359359359

AAAA

resistores de polarização devem ser colocados nas linhas de recepção. O valor dos resistores de polarização depende da terminação e o número de nós do sistema. O objetivo é de gerar a corrente suficiente de polarização na rede para manter um mínimo de 200mV entre as linhas de dados A e B. Considerar os seguintes dois exemplos de cálculo de resistor de polarização.

Figura 20-24 - Transceptor com resistores de polarização

Exemplo 1. Rede RS-485 de 10 nós com dois resistores de terminação de 120 ohms.

Cada nó RS-485 possui uma impedância de 12kΩ. 10 nós em paralelo fornecem uma carga de 1200Ω. Adicionalmente, os dois resistores de terminação de 120Ω são responsáveis pela maior parte da carga. De forma a manter um mínimo de 200 mV entre a linha A e B, é necessária uma corrente de polarização de 3.5 mA através da carga. Para gerar esta polarização a partir de uma fonte de 5V, é necessária uma resistência total em série de no máximo 1428Ω. Subtraindo os 57Ω que fazem parte da carga52, e ficando com 1371Ω. Colocando a metade deste valor como pullup para 5V e metade para pulldown à terra, dá uma resistência máxima de 685Ω para cada resistor de polarização.

Exemplo 2. Rede RS-485 com 32 nós sem terminação.

Cada nó RS-485 possui uma impedância de 12kΩ. Os 32 nós em paralelo somam uma caga total de 375Ω. De forma a manter um mínimo de 200 mV em 375Ω, será necessária uma corrente de 0.53 mA. Para gerar esta corrente de uma fonte de 5 V, se requer uma resistência total máxima de 9375 ohms. Subtraindo do total, a resistência de 375, o resistor de polarização deverá adicionar no máximo 9k. Notar que para sistemas sem terminação, a corrente requerida é muito pequena.

Os resistores de polarização podem ser colocados em qualquer ponto da rede ou podem ser distribuídos ao longo dos nós. A combinação em paralelo de todos os resistores em um sistema deve ser igual ou menor que os requisitos calculados. Usualmente podem ser utilizados resistores de polarização de 4,7 kΩ. Este valor é adequado para a maioria dos sistemas sem terminação. O projetista do sistema deverá ainda calcular os requisitos de polarização da rede. Alguns sintomas da falta de polarização são a diminuição da imunidade ao ruído e até a falha total dos dados. O excesso de polarização possui menor efeito no sistema, sendo o resultado principal o aumento da carga dos drivers e das fontes.

52 ((120||120)||1200)

Page 382: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

360360360360

AAAA

EEsstteennddeennddoo aa EEssppeecciiffiiccaaççããoo Alguns sistemas requerem de maiores distâncias e um número maior de nós do que

aqueles suportados pelos sistemas RS-485 e RS-422. Usualmente são utilizados repetidores para superar estas barreiras. Um repetidor RS-485 pode ser colocado em um sistema para dividir a carga em múltiplos segmentos. Cada sinal reforçado é capaz de alimentar outros 1200 metros de cabo e com o aumento da capacidade em mais 31 cargas RS-485.

Outro método de aumentar o número de nós RS-485 é utilizando receptores RS-485 de carga mínima. Estes receptores possuem uma impedância de entrada elevada, o que reduz a carga nos drivers RS-485, permitindo o aumento do total do número de nós. Estes possuem geralmente da metade a um quarto da carga, estendendo o número total de nós para 64 e 128.

SSeelleeççããoo ddooss CCaabbooss ppaarraa RRSS--442222 ee RRSS--448855

O processo para a seleção de cabos para os sistemas RS-485 e RS-422 é freqüentemente menosprezado. Deve ser tomada especial atenção em alguns detalhes do processo de seleção, para prever a possibilidade de ocorrência de custos consideráveis devido a ter que efetuar a troca de milhares de metros de cabo.

NNúúmmeerroo ddee ccoonndduuttoorreess O condutor de sinal de terra é freqüentemente esquecido na hora do pedido de

compra. Um par trançado extra deve ser especificado, de forma a ter condutores suficientes para transportar o sinal de terra. Um sistema a dois fios requer de dois pares trançados, e um sistema a quatro fios, requer três pares trançados.

BBlliinnddaaggeemm Freqüentemente é difícil decidir quando é necessária a utilização de um cabo

blindado, para ser utilizado na aplicação. Considerando que o custo agregado de um cabo blindado usualmente é mínimo, geralmente vale a pena fazer este investimento.

CCaarraacctteerrííssttiiccaass ddoo CCaabboo Quando for escolhida a linha de transmissão para o sistema RS-422 ou RS-485, é

necessário examinar a distância requerida para o cabo e a taxa de transmissão de dados. O padrão RS-422 apresenta uma curva empírica que relaciona o comprimento do cabo com a taxa de transferência de dados para um par trançado de cabo telefônico, que possui uma capacitância shunt de 48 pF.m-1 e terminada com um resistor de 100 Ω (ver Figura 20-25). Esta curva é baseada nos seguintes requisitos de qualidade do sinal: Tempos de subida e descida do sinal, iguais a, ou menores que a metade do intervalo da

unidade para a taxa de modulação aplicada. Perda máxima de tensão entre o driver e a carga de 6 dB.

Page 383: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

361361361361

AAAA

Figura 20-25 – Taxa de transmissão de dados versus comprimento do cabo para interfaces balanceadas, utilizando um cabo de par trançado 24 AWG

Figura 20-26 – Taxa de dados por comprimento de cabo, como conseqüência dos atrasos finitos de

propagação da linha de transmissão

As perdas em uma linha de transmissão são a combinação das perdas AC (efeito skin), DC nos condutores, fugas, e perdas AC no dielétrico. Num cabo de alta qualidade, as perdas no condutor e as perdas no dielétrico são da mesma ordem de magnitude. A Figura 20-27 mostra a diferencia no desempenho de diferentes tipos de cabos. Este gráfico mostra a atenuação versus a freqüência para três tipos diferentes de cabos. Notar que os cabos de polietileno oferecem menor atenuação que os cabos PVC.

Velocidade de transmissão em bits/s

Com

prim

ento

do

cabo

em

pés

Velocidade de transmissão

Comprimento do cabo em pés

Page 384: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

362362362362

AAAA

Figura 20-27 – Atenuação versus freqüência para vários tipos de cabos

PPrrootteeççããoo ccoonnttrraa ttrraannssiieenntteess nnooss ssiisstteemmaass RRSS--442222 ee RRSS--448855

O primeiro passo da proteção contra transientes nos sistemas RS-422 e RS-485, é o entendimento da natureza da energia a ser prevenida. A energia transiente pode ser originada de várias formas, a maioria proveniente das condições ambientais ou induzidas pelo chaveamento de cargas altamente indutivas.

CCoomm oo qquuee ssee ppaarreecceemm ooss ssuurrttooss ??

EEssppeecciiffiiccaaççõõeess ddooss SSuurrttooss

Enquanto os transientes não podem se adequar às especificações da indústria, o IEEE53 e o IEC54 desenvolveram modelos transientes para o uso na avaliação da imunidade a surtos em equipamentos elétricos e eletrônicos. Estes modelos podem oferecer alguma informação sobre os tipos de energia que podem ser controlados para evitar danos nos sistemas.

As especificações IEC 1000-4-5: 1995 “Surge Immunity Test” e IEEE C62.41-1991 “IEEE Recommended Practice on Surge Voltages in Low-Voltage AC Power Circuits” definem o “sinal de combinação de 1.2/50µs - 8/20µs" que possui 1.2 µs de tempo de subida com um decaimento de 50 µs a circuito aberto. A corrente especificada para a onda possui 8 µs de tempo de subida e 20 µs de decaimento em curto-circuito. Os níveis de tensão a circuito aberto são de 1 a 6 kV, e são comumente usados com polaridades positivas e negativas, embora, sob algumas circunstancias, as tensões aplicadas podem chegar a 20 kV. A Figura 20-28 e a Figura 20-29 ilustram a combinação das características das ondas. Acrescentando a estas, a IEEE C62.41 especifica uma "ring wave" de testes, de 100 kHz. Esta onda possui 0.5 µs de tempo de subida e oscilação de decaimento com a

53 Institute of Electrical and Electronics Engineers 54 International Electrotechnical Commission

Ate

nuaç

ão –

dB/1

00 ft

Freqüência - MHz

Page 385: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

363363363363

AAAA

impedância da fonte de 12 ohms, como mostra a Figura 20-30. As amplitudes típicas da onda variam na faixa de 1 a 6 kV.

Figura 20-28 – Forma de onda de tensão do sinal de combinação

Figura 20-29 – Forma de onda de corrente do sinal de combinação

Figura 20-30 – Ring Wave de 100 kHz

Tempo - µs

Tempo - µs Onda de corrente de 8/20 µs

Onda de tensão de 1.2/50 µs

Tempo - µs

Tempo - µs

Onda de Ring de 100 kHz

Page 386: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

AAAA

MMooddoo CCoommuumm vveerrssuuss MMooddoo DDiiffeerreenncciiaall A identificação do tipo de surto que pode pôr em risco um sistema é uma parte

importante na seleção dos níveis e métodos apropriados para a proteção aos transientes. Já que cada condutor em um cabo de dados atravessa o mesmo espaço físico, é razoável esperar que ocorram transientes de modo comum, ocasionados pelo ambiente ou pelo chaveamento de correntes, em todos os condutores dentro do cabo de dados., i.e. presente em todos os condutores de dados e de terra. Em algumas instalações, existem outras fontes de energia não desejadas a serem consideradas. Se existirem cabos de alta tensão perto dos cabos de dados, o potencial para a condição de falha existe com resultado de falhas na isolação ou de contacto inadvertido pelo instalador. Este tipo de surto pode afetar qualquer número de condutores no cabo de dados, apresentando um surto diferencial no equipamento de dados. Embora as tensões e correntes associadas com este tipo de surto sejam muito menores que os tipos de surtos modelados pelo ANSI ou IEC, possuem uma qualidade particularmente destrutiva. No lugar de dissipação em poucos milisegundos, estes podem existir na condição de estado permanente na rede de dados.

UUmm tteerrrraa nnããoo éé iigguuaall aa oouuttrroo Entende-se que a energia transiente em altas freqüências pode ocasionar distorções

nos sistemas. Em altas freqüências, é difícil efetuar uma conexão de baixa impedância entre dois pontos devido à indutância do caminho entre os mesmos. Se houver uma distância de vários metros de cabo entre as várias centenas de metros de terra entre os sistemas de aterramento, durante o evento transiente poderão haver centenas ou até milhares de volts de potencial entre os diferentes "terras". Nestes casos não pode-se assumir que os dois pontos conectados pelo mesmo fio estão no mesmo potencial elétrico. Para o projetista do sistema isto permite que embora os sistemas RS-485/422 utilizem 5V de diferença de potencial, um nó remoto poderá ter os 5V de sinal superposto em um transiente de milhares de volts com respeito ao terra local. É mais intuitivo referenciar o comumente denominado "sinal de terra" como "sinal de referência".

OS

VV =

Figura

Como se pode conectardiferenças de potencial entre osassegurar de que cada dispositivterra, eliminando os caminhos

Vruído

Vruído

Voa

Vo

364364364364

2oboa V+

; GPDruídooscm VVVV ++=

20-31 – Um terra não é igual a outro

os nós do sistema, sabendo que poderão existir grandes terras ? O primeiro passo para prover uma proteção é o do sistema esteja referenciado a somente um sinal de através dos dispositivos para as correntes de surto no

b

Page 387: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

365365365365

AAAA

retorno. Existem dois métodos para criar um estado de terra confiável. O primeiro método é de isolar o terra de dados do terra da alimentação do dispositivo, isto pode ser feito por transformadores ou isoladores ópticos, como mostra a Figura 20-32. O segundo método é conectar todos os terras em um dispositivo só (tipicamente o terra da alimentação e o terra dos dados) com uma conexão de baixa impedância, como mostra a Figura 20-33. Estas duas técnicas levam a formar os dois métodos básicos de proteção contra transientes.

Figura 20-32 – Dispositivo RS-485 Isolado

Figura 20-33 – Dispositivo RS-485 com sinal de terra conectado na massa

PPrrootteeççããoo ccoonnttrraa TTrraannssiieenntteess uussaannddoo IIssoollaaççããoo

TTeeoorriiaa ddaa IIssoollaaççããoo

O método universal para proteger contra transientes é a isolação galvânica da porta de dados e dos circuitos do dispositivo. Este método separa a referência dos sinais de qualquer terra fixo. Comumente são utilizados isoladores ópticos, transformadores e fibras ópticas, para implementar vários tipos de redes de dados que isolam as portas de I/O do resto do circuito.

Dispositivo Porta Fonte de energia isolada

Linhas de dados

Dispositivo Porta

Linhas de dados

Linha de terra

Conexão a terra da carcaça

Page 388: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

366366366366

AAAA

Nas aplicações RS-422 e RS-485 os opto-isoladores são os mais utilizados. Um isolador óptico é um circuito integrado que converte um sinal elétrico em luz, e vice-versa, eliminando o contato elétrico. Com uma porta isolada, o circuito elétrico completo flutua com o nível do transiente sem distorcer a comunicação de dados. Enquanto o nível de flutuação não exceder o limite de isolação (tipicamente 1000 a 2500 volts) a porta não será danificada. Este tipo de proteção não absorve nem desvia o excesso de energia, de forma que não é sensível à duração do transiente. Mesmo que as diferenças de potencial sejam contínuas, isto não danifica os dispositivos isolados. É importante notar que os isoladores operam com transientes em modo comum, sendo que estes não podem proteger grandes diferenças de potencial entre os condutores do cabo de dados, tais como aquelas causadas pelo curto-circuito entre os circuitos de dados e de potência.

DDiissppoossiittiivvooss ddee IIssoollaaççããoo

A isolação óptica pode ser implementada de varias formas. Se for feita a conversão entre padrões RS-422 ou RS-485 para RS-232, existem isoladores ópticos comerciais disponíveis. Uma placa serial isolada, com barramento ISA ou PCI poderá substituir as portas existentes num PC. No caso de sistemas RS-422 ou RS-485 já instalados, poderá ser utilizado um repetidor isolado.

PPrrootteeççããoo ccoonnttrraa TTrraannssiieenntteess uuttiilliizzaannddoo DDeerriivvaaççõõeess SShhuunntt

TTeeoorriiaa ddaa DDeerriivvaaççããoo

A criação de um terra comum no dispositivo principal fornece um lugar apropriado para o desvio de energia, assim como implementar uma referência de tensão para colocar os componentes para a supressão de surtos de tensão. O trabalho de desviar as correntes perigosas para o terminal de terra, antes que estas cheguem na porta de dados, é feito por componentes específicos tais como os TVS55 (freqüentemente referenciados pelo nome de TranZorb), os MOV56 ou tubos de descarga de gás. Estes dispositivos operam grampeando uma determinada tensão, e quando o potencial excede deste valor, o dispositivo fornece uma conexão de baixa impedância através dos seus terminais.

Como estes dispositivos desviam grande quantidade de energia, não podem tolerar uma duração muito longa de transientes contínuos. Os dispositivos de desvio são mais freqüentemente instalados a partir de cada linha de dados para o terra local, e devem ser selecionados para continuar conduzindo corrente numa tensão o mais perto possível dos níveis normais do sistema de comunicação. Para os sistemas RS-422 e RS-485, a faixa de tensões selecionadas é tipicamente de 6 a 8 volts. Estes dispositivos adicionam alguma carga capacitiva nas linhas de dados. Esta impedância deve ser considerada quando o sistema for projetado, podendo ser compensada pela diminuição do comprimento total da linha para compensar a carga adicionada, às vezes com várias dezenas de metros.

Para a utilização correta deste tipo de dispositivo, deverão ser instalados o mais perto possível da porta a ser protegida, e o projetista deverá fornecer uma conexão com o terra local, com impedância extremamente baixa. Esta conexão a terra é crucial para

55 Transient Voltage Suppressor - TVSs são dispositivos de silício que utilizam um mecanismo não destrutivo, de ruptura por avalanche para grampear altas tensões. Normalmente são formados por dois diodos zeners ligados em anti-série, e que podem momentaneamente dissipar centenas ou até milhares de watts, sem se danificar. 56 Metal Oxide Varistors

Page 389: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

367367367367

AAAA

permitir a operação adequada do dispositivo de proteção. A conexão a terra deverá ser feita com um fio grosso e o mais curto possível.

CCoonneeccttaannddoo ooss ssiinnaaiiss ddee tteerrrraa Quando a conexão local a terra é requerida em nós com proteção do tipo derivação,

a conseqüência de conectar terras remotos deverá ser considerada. Durante os eventos transientes, poderão existir grandes diferenças de potencial entre estes terras remotos. A impedância dos fios conectando os terras será a única limitação da corrente, impulsionada pela diferença de potencial. A especificação dos sistemas RS-422 e RS-485 recomenda a utilização de resistores de 100 Ω em série com o fio do sinal de terra, para limitar as correntes entre os diferentes terras. A Figura 20-34 ilustra a conexão a terra recomendada pela especificação.

Figura 20-34 – Conexão do sinal de terra entre dois nós com um resistor de 100 Ω

DDiissppoossiittiivvooss ddee DDeerriivvaaççããoo5577 Existem dois tipos de dispositivos de derivação. O tipo mais barato é o de estágio

único, que usualmente consiste de um único dispositivo TVS em cada linha. Também estão disponíveis dispositivos de três estágios. O primeiro estágio consiste de um tubo de descarga a gás, que pode desviar correntes extremamente altas, mas que possui uma tensão de chaveamento bastante elevada e um tempo de reação bastante lento para proteger os circuitos de estado sólido. O segundo estágio é uma pequena impedância em série que limita a corrente e cria uma queda de tensão entre o primeiro e o terceiro estagio. O estagio final é um dispositivo TVS que é rápido o suficiente para proteger os dispositivos de estado sólido e grampeia a tensão para os níveis seguros de operação dos circuitos de dados.

CCoommbbiinnaannddoo IIssoollaaççããoo ccoomm DDeerriivvaaççããoo A combinação de ambos tipos de proteção pode oferecer uma maior confiabilidade

e robustez do sistema. A Figura 20-35 e a Figura 20-36 ilustram duas formas de implementar este nível de proteção.

57 Shunting Devices

Page 390: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

368368368368

Figura 20-35 – Nó isolado com proteção

Figura 20-36 – Porta isolada com proteção

O método mostrado na Figura 20-35 é o recomcircuito de qualquer queda de tensão na conexão a teproteger do surto em caso em que a tensão exceisoladores, assim como poderá derivar qualquer surto dilustra o método recomendado para os casos onde nconexão a terra. Neste caso, o dispositivo de derivaporta de surtos diferenciais, sendo que este será badispositivo de derivação, e convertido ao modo comutransientes de modo comum remanescentes.

CCoonnssiiddeerraaççõõeess EEssppeecciiaaiiss ppaarraa aass CCoonnddOs sistemas de dados que devem ficar exposto

de alimentação, requerem uma medida adicional de proadicionar dispositivos fusíveis aos derivadores de suprQuando ocorre um curto-circuito, o supressor de derivderivação em si não poderá resistir as correntes de regDeverá ser escolhido um fusível de pequeno valor maantes que o dispositivo de derivação seja danificado. U

Dispositivo

Porta

Fonte isolada

Linhas de Dados

Fonte isolada

Porta

Dispositivo

Dispositivo Derivador(Shunt)

shunt para o terra

Aterramento

Dispositivo Derivador(Shunt)

AAAA

shunt não aterrada

endado já que a isolação protege o rra. O dispositivo de derivação irá da o máximo permissível para os iferencial no cabo. A Figura 20-36 ão existe a forma de efetuar uma

ção tem com função de proteger a lanceado entre os condutores pelo m. A isolação provê proteção dos

iiççõõeess ddee FFaallhhaa s a curtos-circuitos com condutores teção. Nestes casos é recomendado essão, como mostra a Figura 20-37. ação continuará conduzindo, mas a

ime permanente deste tipo de surto. s suficiente para que abra o circuito m valor típico é de 125 mA.

Sinal de Terra

Linhas de Dados

Page 391: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

369369369369

AAAA

Figura 20-37 – Proteção da porta com fusíveis

EEssccoollhhaa ddaa pprrootteeççããoo cceerrttaa Embora seja difícil de predizer que tipo e nível de isolação é a correta para um

determinado sistema, pode ser feita uma suposição aproximada baseada no ambiente do circuito, as condições físicas e o custo das falhas e da manutenção. Os sistemas conectados entre duas fontes de alimentação, tais como as de um edifício para outro, do escritório para o chão de fabrica, ou em qualquer sistema que envolva longas distâncias, deverão precisar de algum nível de proteção contra transientes. A Tabela 20-14 a seguir compara as técnicas de proteção contra transientes. Isolação Óptica Derivação

Requer um referencial a terra Deverá possuir um caminho de baixa impedância para o terra

Não carrega as linhas de dados Carrega as linhas de dados na forma capacitiva Complexidade Elevada Baixa complexidade - Utiliza componentes passivos Efetivo contra transientes de modo comum Efetivo contra transientes de modo comum e diferencial Não depende da qualidade da instalação Pode ser impropriamente instalado pelo usuário Requer de fonte de alimentação externa Não requer de fonte de alimentação Não é afetado por transientes contínuos ou de longa duração Sujeito a danos para transientes de longa duração

Tabela 20-14 – Comparação entre as técnicas de Proteção

O circuito da Figura 20-38 mostra um sistema com TVSs ligados diretamente na linha de dados para fornecer a proteção de mais alto nível e carga mais capacitiva na linha de transmissão.

Figura 20-38 – Exemplo de circuito com proteção de linha

Dispositivo

Linhas de Dados

Terra de sinal

Aterramento

Fusíveis de 125 mA

L1, L2, L3, L4, de ferrita

Para a rede

Page 392: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

370370370370

AAAA

A Figura 20-39 a seguir mostra um circuito comum de proteção para linhas de dados de altas taxas de transmissão.

Figura 20-39 – Exemplo de circuito comum para proteção de linha

SSooffttwwaarree

CCoonnssiiddeerraaççõõeess BBáássiiccaass Os sistemas RS-422 e RS-485 são especificações de hardware, sendo que o

protocolo de software não é discutido em nenhuma especificação. O projetista do sistema definirá qual o protocolo adequado para o seu sistema. Nesta seção não se tentará definir um padrão de protocolo, mas serão tratados alguns parâmetros que deverão ser considerados pelo projetista no momento de projetar ou comprar um software.

SSiisstteemmaass RRSS--442222

Os sistemas RS-422 diferem um pouco da comunicação RS-232 ponto a ponto. Os sistemas RS-422 são freqüentemente utilizados para estender a distância entre nós acima da capacidade do RS-232. O RS-422 pode também ser utilizado como nó mestre em uma rede de quatro fios mestre-escravo. Quando for implementado o software para os sistemas RS-422 o projetista ou comprador deverá levar em consideração quais sinais serão ou deverão ser utilizados pelo hardware. Muitos sistemas RS-422 não implementam as linhas de handshake através do hardware, que são freqüentemente utilizadas nos sistemas RS-232, devido ao custo adicional dos condutores no caso de longas distancias.

CCoonnttrroollee ddoo DDrriivveerr RRSS--448855

A principal diferença entre RS-422 e RS-485 é que o driver RS-485 pode ser colocado no modo de alta impedância (tristate), permitindo que outros drivers transmitam no mesmo par de fios. Existem dois métodos de colocar o driver RS-485 no estado de alta impedância.

L1, L2, L3, L4, de ferrita

Para a rede

Page 393: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

371371371371

AAAA

O primeiro método é utilizar uma linha de controle, freqüentemente a linha RTS, para habilitar e desabilitar o driver. Este método requer que o software de controle, levante a linha RTS antes de começa a transmissão, para habilitar o driver, e depois abaixar a linha RTS após ter completado a transmissão. Desta forma, somente um único driver RS-485 poderá estar habilitado na rede num determinado instante de tempo, e é importante que este seja desabilitado o mais rápido possível após a transmissão, para evitar que dois drivers tentem controlar as linhas de forma simultânea, condição esta denominada de conflito de linha.

O segundo método de controle é referenciado como Controle Automático de Envio de Dados. Este tipo de controle envolve um circuito especial que sente quando os dados estão sendo transmitidos e automaticamente habilita o driver, desabilitando-o após o último caractere ser enviado. Este método de controle é o preferido já que reduz o tamanho do software e o número de falhas potenciais.

CCoonnttrroollee ddoo RReecceeppttoorr RRSS--448855

O receptor RS-485 também possui um sinal de habilitação. Visto que os sistemas RS-485 utilizam uma configuração a dois fios, conectando o driver com o receptor na forma de um loop, sendo que esta característica é usualmente utilizada para desabilitar o receptor durante a transmissão, de forma a prevenir o eco dos dados locais. Outra alternativa é a de deixar o receptor RS-485 habilitado, monitorando os dados de retorno, de forma que se ocorrer a recepção de dados errados, indicará que ocorreu um conflito na linha. Embora a recepção correta dos sinais não garanta a integridade dos dados, oferece um certo grau de detecção de erros.

SSiisstteemmaass MMaasstteerr--SSllaavvee ((MMeessttrree--EEssccrraavvoo)) Um sistema do tipo mestre-escravo possui um nó que distribui os comandos para

cada um dos nós escravos, e processa as respectivas respostas. Um nó escravo não poderá transmitir dados sem ter sido requisitado pelo nó mestre, e não se poder comunicar diretamente com outro nó escravo. Cada escravo deverá ter um endereço único, de forma que possa ser endereçado independentemente dos outros nós. Este tipo de sistema pode estar configurado a dois ou quatro fios. Os sistemas de quatro fios, utilizam freqüentemente um mestre RS-422 (com o driver sempre habilitado) e escravos RS-485 para reduzir a complexidade do sistema.

SSiisstteemmaass MMaasstteerr--SSllaavvee aa QQuuaattrroo ffiiooss

Esta configuração reduz a complexidade de software no dispositivo mestre, já que tanto o driver e o receptor não ficam habilitados constantemente, a custas de ter que instalar dois condutores adicionais no sistema. O nó mestre simplesmente envia os comandos para o endereço do escravo apropriado. Não haverá eco de dados nem atrasos de retorno a serem considerados. Visto que os transmissores dos escravos compartilham o mesmo par de fios, deve-se tomar o cuidado para que o mestre nunca requisite dados de múltiplos nós de forma simultânea, podendo ocorrer colisão entre os dados.

Page 394: Tratado Da Linguagem c

A P Ê N D I C E A - P A D R Õ E S D E T R A N S M I S S Ã O S E R I A L

372372372372

AAAA

SSiisstteemmaass MMaasstteerr--SSllaavvee aa DDooiiss ffiiooss

As configurações a dois fios adicionam uma pequena complexidade ao sistema. O driver RS-485 deverá ser colocado no modo tristate quando não estiver em uso, de forma a permitir que outros nós possam utilizar o par de fios compartilhado. O tempo de atraso entre o final da transmissão e a condição tristate é um parâmetro muito importante neste tipo de sistema. Se um escravo tentar responder antes que o mestre tenha colocado a linha no modo tristate, ocorrerá uma colisão e os dados serão perdidos. O projetista do sistema deverá conhecer o tempo de resposta ou inserir um atraso para cada nó escravo para assegurar que o mestre colocará o seu driver no modo tristate dentro do tempo estabelecido.

SSiisstteemmaass RRSS--448855 MMuullttiimmeessttrree Cada nó em um sistema RS-485 multimestre poderá iniciar a sua própria

transmissão, gerando uma colisão de dados em potencial. Este tipo de sistema requer que o projetista implemente um método mais sofisticado de detecção de erros, incluindo métodos de reconhecimento de transmissão e sistemas de re-envio de dados, no caso de corrupção. Também poderão ser implementados sistemas lógicos de troca de token, sendo que somente um mestre poderá estar ativo num determinado instante de tempo, tendo que repassar o token para um outro mestre após tenha efetuado as requisições pertinentes.

SSiisstteemmaass CCoonnvveerrssoorreess ccoomm ffoonnttee ddee aalliimmeennttaaççããoo pprróópprriiaa

Os conversores RS-232 para RS-422 ou RS-485 que derivam a sua alimentação de uma porta RS-232 são freqüentes nos sistemas de dados. Uma boa pratica de programação é a de setar as saídas de handshake para um nível alto de tensão nos sistemas que utilizam qualquer tipo de conversor RS-232, RS-485 ou RS-422. Isto irá assegurar as melhores condições possíveis de operação para todos os conversores utilizados.

No caso da aquisição de um dispositivo para um sistema RS-485, muitos problemas podem ser evitados pela determinação das características de comunicação antes que o projeto do sistema esteja completo. O conhecimento de quais são as questões mais relevantes, poderão resolver muitos problemas em campo. Algumas das questões que deverão ser formuladas no estagio inicial de projeto do sistema, podem ser:

O dispositivo está configurado para operar num sistema de dois ou quatro fios ? A conexão para o sinal de terra está disponível ? O dispositivo é isolado ? Possui um mecanismo para a supressão de surtos ? Quais os valores dos resistores de polarização (se existentes) que estão sendo utilizados

pelo dispositivo ? Estão acessíveis para poder modificar o seu valor ? O dispositivo possui terminação ? Está a terminação acessível para modificação ? Qual o tempo de resposta do dispositivo (possui algum atraso) ? Qual é a faixa programável de endereços do dispositivo ? Qual é o baudrate, e qual a faixa de variação?

Se possível, é interessante obter o diagrama esquemático da porta serial de cada dispositivo do sistema. O esquema poder fornecer informação adicional que pode ser bastante útil no caso de localização de defeitos ou reparação no sistema de dados.

Page 395: Tratado Da Linguagem c

373373373373 Luis Fernando Espinosa Cocian

AAppêênnddiiccee BB -- MMiiccrrooccoonnttrroollaaddoorreess MMiiccrroocchhiipp PPIICC ddaa FFaammiilliiaa 1166FFXXXXXX

Os microcontroladores são dispositivos que integram um microprocessador e os seus periféricos, tornando-o um sistema completo. Normalmente são utilizados para efetuar tarefas dedicadas, reduzindo o custo em relação aos sistemas microprocessados para uso genérico. Timers/Counters, Capture e Compare USARTs1 Controlador de Interrupções Interfaces e protocolos SPI, I2C, etc. PWMs, Watchdog timers, PUT Conversores Analógico-Digitais EEPROM

AApplliiccaaççõõeess Sistemas de Controle Dedicados (automóveis, eletrodomésticos, etc.) Coletores de Dados Instrumentação de Processos Sistemas de Acesso e Segurança Sistemas de Transmissão de Dados

CCaarraacctteerrííssttiiccaass ddooss mmiiccrrooccoonnttrroollaaddoorreess PPIICC Tecnologia RISC Disponibilidade Ferramentas de desenvolvimento Confiabilidade Facilidade de uso e programação Ampla gama de dispositivos Baixo custo Periféricos

PPIICC 1166FF8877xx -- CCaarraacctteerrííssttiiccaass CPU RISC de alto desempenho Somente 35 instruções para aprender Todas as instruções são executadas em um único ciclo, com exceção dos saltos de programa

que são executados em dois ciclos de máquina. Velocidade de operação: DC a 20 MHz de clock de entrada Ciclo de instrução de DC a 200 ns

1 USART: Universal Synchronous Asynchronous Receiver Transmitter

Apêndice

B

Page 396: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

374374374374

BBBB

Até to 8K palavras x 14 bits de memória de Programa FLASH Até 368 palavras x 8 bits de memória de dados (RAM) Até 256 palavras x 8 bits de memória de dados EEPROM Compatibilidade de pinagem entre famílias, ex. PIC16C73B/74B/76/77 Capacidade de interrupção (até 14 fontes possíveis) Oito níveis de pilha de hardware Modos de endereçamento, direto, indireto e relativo. Power-on Reset (POR) Power-up Timer (PWRT) Oscillator Start-up Timer (OST) Watchdog Timer (WDT) com o seu próprio oscilador RC integrado, para operação confiável Proteção do código programável Modo de baixo consumo SLEEP Opções selecionáveis de tipo de oscilador Tecnologia CMOS FLASH/EEPROM de baixo consumo e alta velocidade de processamento In-Circuit Serial Programming (ICSP) através de dois pinos Capacidade de In-Circuit Serial Programming com fonte única de 5V Depuração In-Circuit Debugging através de dois pinos Acesso do processador para leitura e escrita na memória de programa Ampla faixa de tensões de operação: 2.0V a 5.5V Alta corrente de dreno: 25 mA Faixas de temperatura Comercial e Industrial Baixo consumo: - < 2 mA típico @ 5V, 4 MHz -20 mA típico @ 3V, 32 kHz -< 1 mA corrente

típica de funcionamento.

PPIICC 1166FF8877xx -- PPeerriifféérriiccooss Timer0: 8-bit timer/ contador com prescaler de 8 bits Timer1: 16-bit timer/contador com prescaler, pode ser incrementado durante o modo Sleep

através de um cristal ou clock externo. Timer2: 8-bit timer/ contador com registrador de período de 8 bits, prescaler e postscaler Dois módulos de Captura, Comparação e PWM Capturador de 16 bit, resolução de 12.5 ns Comparador de 16-bit, resolução máxima de 200 ns PWM com resolução máxima de 10 bits. Conversor Analógico Digital multicanal de 10 bits Porta Serial Síncrona (SSP) com SPI (modo mestre) e I2C (Master/Slave) Universal Synchronous Asynchronous Receiver Transmitter (USART/SCI) com detecção de

endereço de 9 bits Porta Paralela Escrava (PSP) com 8 bits, com controle externo RD, WR e CS Circuito de detecção de Brown-out para Brown-out Reset (BOR)

Page 397: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

375375375375

BBBB

PPiinnaaggeemm

Figura 21-1 – Pinagem do microcontrolador PIC 16F877

AArrqquuiitteettuurraa MMiiccrroocchhiipp PPIICC

Figura 21-2 – Arquitetura do microcontrolador PIC 16F877

Page 398: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

376376376376

BBBB

OOrrggaanniizzaaççããoo ddaa MMeemmóórriiaa ddee PPrrooggrraammaa

Figura 21-3 – Organização da memória de programa do microcontrolador PIC 16F877

Page 399: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

377377377377

BBBB

RReeggiissttrraaddoorreess IInntteerrnnooss ee MMeemmóórriiaa ddee DDaaddooss

Figura 21-4 – Registradores internos do microcontrolador PIC 16F877

MMPPLLAABB ddaa MMiiccrroocchhiipp Ferramenta integrada de desenvolvimento Freeware Editor Compilador/Linker Assembler Simulador Integração com outros compiladores, gravadores e emuladores

Page 400: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

378378378378

BBBB

Figura 21-5 – Aparência do software MPLAB

OO CCoommppiillaaddoorr PPCCWW ddaa CCCCSS Integração com o MPLAB Baixo custo Versão demo para PIC16C63

Page 401: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

379379379379

BBBB

Figura 21-6 – Aparência do compilador PCW

GGrraavvaaddoorr Propic 2 (Tato2) PicStart Plus (Microchip) Outros (free on the web)

RReeggrraass ddoo CCoommppiillaaddoorr PPCCWW ddaa CCCCSS

O compilador PCW não diferencia as letras maiúsculas das minúsculas.

TTiippooss ddee VVaarriiáávveeiiss SSiimmpplleess short: 1 bit (0 ou 1) int: 8 bits sem sinal (0 a 255) signed int: 8 bits com sinal (-128 a +127) long int: 16 bits sem sinal (0 a 65535) float: 32 bits char: 8 bits

BBiibblliiootteeccaa ddee FFuunnççõõeess ABS LOG10 SETUP_COUNTERS ACOS LOG SETUP_LCD ASIN MEMCPY SETUP_PORT_A ATAN MEMSET SETUP_PSP

2 http://www.propic2.com

Page 402: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

380380380380

BBBB

ATOI OUTPUT_BIT SETUP_SPI ATOL OUTPUT_FLOAT SETUP_SPI BIT_CLEAR OUTPUT_HIGH SETUP_TIMER_1 BIT_SET OUTPUT_LOW SETUP_TIMER_2 BIT_TEST PORT_B_PULLUPS SETUP_VREF CEIL PRINTF SHIFT_LEFT DELAY_CYCLES PSP_INPUT_FULL SHIFT_RIGHT DELAY_MS PSP_OUTPUT_FULL SLEEP DELAY_US PSP_OVERFLOW SPI_READ DISABLE_INTERRUPTS PUTCHAR SPI_WRITE ENABLE_INTERRUPTS PUTS SPI_WRITE EXP READ_ADC STRCAT EXT_INT_EDGE READ_BANK STRCHR FLOOR READ_EEPROM STRCMP GET_RTCC READ_EEPROM STRCPY GET_TIMER0 RESTART_CAUSE STRCSPN GET_TIMER1 RESTART_WDT STRICMP GET_TIMER2 ROTATE_LEFT STRLEN GETCH ROTATE_RIGHT STRLWR GETS SET_ADC_CHANNEL STRNCMP I2C_POLL SET_PWM1_DUTY STRNCMP I2C_READ SET_PWM2_DUTY STRNCPY I2C_START SET_RTCC STRPBRK I2C_STOP SET_TIMER0 STRRCHR I2C_WRITE SET_TIMER1 STRSPN INPUT SET_TIMER2 STRSTR INT_LCD SET_TRIS_A STRTOK ISALNUM SET_TRIS_B SWAP ISALPHA SET_TRIS_C TOLOWER ISDIGIT SET_TRIS_D TOUPPER ISLOWER SET_TRIS_E WRITE_BANK ISSPACE SETUP_ADC WRITE_EEPROM ISUPPER SETUP_CCP1 ISXDIGIT SETUP_CCP2 KBHIT SETUP_CCP LCD_SYMBOL SETUP_COMPARATOR

Tabela 21-1 - Biblioteca de Funções

EExxpprreessssõõeess CCoonnssttaanntteess 123 Decimal 0123 Octal 0x123 Hexadecimal 0b010010 Binário 'x’ Caractere '\x’ Caractere especial. x pode ser: n, t, b, r, f, etc. “abcdef” String (é adicionado um caractere nulo no final)

IIddeennttiiffiiccaaddoorreess:: ABCDE Até 32 caracteres ID[X] Subscrito simples ID[X][X] Subscritos múltiplos ID.ID Referência a membro de estrutura ou union

DDiirreettiivvaass ddee CCoommppiillaaddoorr

O pré-processador C é um programa que examina o programa fonte e executa baseado nas Diretivas de Compilação. As diretivas de compilação são comandos que não são compilados, sendo dirigidos ao pré-processador, que é executado pelo compilador antes da execução do processo de compilação propriamente dito.

Page 403: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

381381381381

BBBB

AAllgguummaass DDiirreettiivvaass #ASM #BIT #BYTE #DEFINE #DEVICE #ELSE #ENDASM #ENDIF #FUSES

#USE RS-232 #USE

STANDARD_IO #ZERO_RAM #IFDEF #IF #IFNDEF #INCLUDE #INLINE

#INT_GLOBAL #INT_ #PRIORITY #ROM #SEPARATE #USE FAST_IO #USE FIXED_IO

SSeeqqüüêênncciiaa ddee PPaassssooss ppaarraa IImmpplleemmeennttaaççããoo ddoo SSooffttwwaarree ((FFiirrmmwwaarree))

Definir claramente o problema Definir os pinos de entrada e saída necessários no microcontrolador Usando diretamente o MPLAB ou o compilador, edite o código fonte que solucione o

problema. Compile e verifique a não existência de erros. Simule se for necessário, e grave o chip

PPoorrttaass ddee II//OO ddiiggiittaaiiss

PPOORRTTAA

RReeggiissttrraaddoorreess

PORTA (valor no pino) TRISA (direção IN (1) ou OUT(0)) RA4 open-collector output/Schmitt Trigger input

Page 404: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

382382382382

BBBB

Figura 21-7 – Porta A1

Figura 21-8 – Pino RA42

PPOORRTTBB

RReeggiissttrraaddoorreess

PORTB (valor no pino) TRISB (direção IN (1) ou OUT(0)) RB4~RB7 interrupts on change RB0 external interrupt

1 Os pinos de I/O possuem um diodo de proteção entre VCC e VSS. 2 Os pinos de I/O possuem diodos de proteção somente para VSS.

Page 405: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

383383383383

BBBB

3,4

Figura 21-9 – Porta B

PPOORRTTCC

RReeggiissttrraaddoorreess

PORTC (valor no pino) TRISC (direção IN (1) ou OUT(0))

3 Os pinos de I/O possuem um diodo de proteção entre VCC e VSS. 4 Para habilitar os pullups setar os bits correspondentes no registrador TRIS e resetar o bit RBPU (OPTION<7>).

Page 406: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

384384384384

BBBB

Figura 21-10 – Porta C

OOuuttrrooss PPeerriifféérriiccooss AAssssoocciiaaddooss

USART SPI I2C

OOuuttrraass PPoorrttaass PORTD PORTE PORTF (driver LCD)

EExxeerrccíícciioo 11 -- OOsscciillaaddoorr Implemente um firmware que produza uma onda quadrada num pino de I/O. Simular no MPLAB. Gravar um chip e verifique o funcionamento utilizando um osciloscópio.

// define o chip #device PIC16C63 // define o clock #use delay(clock=20000000) #bit bpisca = 5.0 // define o bit da porta de I/O #byte TRISA = 0x85 // define a variável de controle de direção void main(void) short bPinoRA0 = 0; // variável auxiliar TRISA = 0b11111110; // programa o pino 0 de RA para saída while(1) // loop eterno bPinoRA0 = !bPinoRA0; bpisca = bPinoRA0;

Page 407: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

385385385385

BBBB

Código 21-1 – Exercício 1 - Oscilador

Figura 21-11 – Exercício 1 no MPLAB

EExxeerrccíícciioo 22 -- OOsscciillaaddoorr CCoonnttrroollaaddoo Implemente um firmware que produza uma onda quadrada num pino de I/O somente quando

é apertada uma tecla (valor igual a 0) conectada ao pino RB7. Simule no MPLAB. Grave um chip e verifique o funcionamento utilizando um osciloscópio.

// define o chip #device PIC16C63 // define macros #define true 1 // define o clock #use delay(clock=20000000) #bit bpisca = 5.0 // define variável para o bit 0 da porta A de I/O #byte TRISA = 0x85 // define a variável de controle de direção #bit bTecla = 6.7 // define variável para o bit 7 da porta B de I/O #byte TRISB = 0x86 // define a variável de controle de direção void main(void) short bPinoRA0 = 0; // variável auxiliar port_b_pullups(true); // habilita os pullups da porta B TRISA = 0b11111110; // programa o pino 0 de RA para saída TRISB = 0b11111111; // programa o pino 7 de RB como entrada while(1) // loop eterno

Page 408: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

386386386386

BBBB

if(bTecla == 0) bPinoRA0 = !bPinoRA0; bpisca = bPinoRA0;

Código 21-2 – Exercício 2 – Oscilador Controlado

Figura 21-12 – Exercício 2 no MPLAB

EExxeerrccíícciioo 33 -- OOnnddaa QQuuaaddrraaddaa

Faça um programa que implemente um gerador de onda quadrada no pino RA0, com período de 2 segundos e de 0.5 segundos (1 segundo por estado) quando a tecla em RB7 estiver pressionada. Utilizar a função delay_ms(val)

TTiimmeerr 00

CCaarraacctteerrííssttiiccaass ddoo TTiimmeerr 00 8-bit timer/counter Readable and writable 8-bit software programmable prescaler Clock source selectable to be external or internal Interrupt on overflow from FFh to 00h Edge select for external clock

Page 409: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

387387387387

BBBB

Figura 21-13 – Timer 0

Tabela 21-2 – Registrador OPTION_REG

RReeggiissttrraaddoorreess TMR0 OPTION_REG

Page 410: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

388388388388

BBBB

FFuunncciioonnaammeennttoo ddoo TTiimmeerr 00

Figura 21-14 – Temporização do Timer 0: Clock interno sem prescaler

Figura 21-15 – Temporização do Timer 0: Clock interno com prescaler 1:2

EExxeerrccíícciioo 44 -- OOsscciillaaddoorr uuttiilliizzaannddoo oo TTiimmeerr 00 Faça um programa que implemente um gerador de onda quadrada de 40 µs de período no

pino RA0 utilizando o timer0 (20 µs para cada estado). Simular e depois verificar o funcionamento. Obs.: Clock: 20 MHz - Clock/4: 5 MHz Cada contagem: 1/5MHz = 200 ns Precisamos de 20 µs: 100 contagens do timer.

// define o chip #device PIC16C63 // define macros #define true 1 // define o clock #use delay(clock=20000000) #bit bpisca = 5.0 // define variável para o bit 0 da porta A de I/O #byte TRISA = 0x85 // define a variável de controle de direção #byte OPTION_REG = 0x81 #byte TMR0 = 0x01 // variável que contém o valor do timer0 void main(void) short bPinoRA0 = 0; // variável auxiliar OPTION_REG = 0b00001000; // sem prescaler para o timer0 TRISA = 0b11111110; // programa o pino 0 de RA para saída TMR0 = 0; // força o timer0 para 0 while(1) // loop eterno if(TMR0 >= 100)

Page 411: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

389389389389

BBBB

TMR0 = 0; bPinoRA0 = !bPinoRA0; bpisca = bPinoRA0;

Código 21-3 - Oscilador utilizando o Timer 0

Figura 21-16 – Oscilador utilizando o Timer 0 no MPLAB

EExxeerrccíícciioo 55 -- OOsscciillaaddoorr uuttiilliizzaannddoo aa iinntteerrrruuppççããoo ddoo TTiimmeerr 00

Faça um programa que implemente um gerador de onda quadrada de 2 ms de período no pino RA0 (1 ms cada estado) , utilizando a interrupção do timer0. Simular e depois verificar o funcionamento. Clock: 20 MHz - Clock/4: 5 MHz Cada contagem: 1/5MHz = 200 ns A interrupção acontece automaticamente a cada 256 contagens, ou 51.2 µs. Precisamos de aproximadamente 20 (19.53) interrupções para completar 1 ms.

// define o chip #device PIC16C63 // define macros #define true 1 // define o clock #use delay(clock=20000000) #bit bpisca = 5.0 // define variável para o bit 0 da porta A de I/O #byte TRISA = 0x85 // define a variável de controle de direção

Page 412: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

390390390390

BBBB

#byte OPTION_REG = 0x81 #byte INTCON = 0x0B // variável que contém o valor do registrador de interrupções int cont_ints = 0; // variável que conta o número de interrupções short bPinoRA0 = 0; // variável auxiliar //////////////////////////////////////////////////////////////////////// void main(void) OPTION_REG = 0b00001000; // sem prescaler para o timer0 TRISA = 0b11111110; // programa o pino 0 de RA para saída INTCON = 0b10100000; // habilita as interrupções do timer0 while(1) // loop eterno // Função que atende a interrupção do timer 0 #int_rtcc timer0_interrupt() cont_ints++; if(cont_ints >= 20) bPinoRA0 = !bPinoRA0; bpisca = bPinoRA0; cont_ints = 0;

Código 21-4 - Oscilador utilizando a interrupção do Timer 0

Tabela 21-3 – Registrador INTCON

Page 413: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

391391391391

BBBB

Figura 21-17 – Oscilador utilizando a interrupção do Timer 0 no MPLAB

TTiimmeerr 11

CCaarraacctteerrííssttiiccaass Timer/Counter de 16 bits (TMR1H e TMR1L) Permite leitura e escrita Interrupção no overflow de FFFFh a 0000h Três modos de operação: As a synchronous timer As a synchronous counter As an asynchronous counter

Page 414: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

392392392392

BBBB

EEssqquueemmaa EEllééttrriiccoo

Figura 21-18 – Diagrama do Timer 1

RReeggiissttrraaddoorreess AAssssoocciiaaddooss aaoo TTiimmeerr11

Tabela 21-4 – Registradores associados ao Timer 1

Page 415: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

393393393393

BBBB

Tabela 21-5 – Registrador T1CON5

EExxeerrccíícciioo 66 -- OOsscciillaaddoorr uuttiilliizzaannddoo aa iinntteerrrruuppççããoo ddoo TTiimmeerr 11

Faça um programa que implemente um gerador de onda quadrada de 2 s de período no pino RA0 (1 s em cada estado) , utilizando a interrupção do timer1. Simular e depois verificar o funcionamento. Clock: 20 MHz - Clock/4: 5 MHz Cada contagem: 1/5MHz = 200 ns A interrupção acontece automaticamente a cada 65536 contagens, ou 13.11 ms. Precisamos de aproximadamente 76 (76.29) interrupções para completar 1 s.

// define o chip #device PIC16C63 // define macros #define true 1 // define o clock #use delay(clock=20000000) #bit bpisca = 5.0 // define variável para o bit 0 da porta A de I/O #byte TRISA = 0x85 // define a variável de controle de direção #byte INTCON = 0x0B // variável que contém o valor do registrador de interrupções #byte PIE1 = 0x8C #byte T1CON = 0x10 #bit TMR1ON = T1CON.0 int cont_ints = 0; // variável que conta o número de interrupções short bPinoRA0 = 0; // variável auxiliar //////////////////////////////////////////////////////////////////////// void main(void)

TRISA = 0b11111110; // programa o pino 0 de RA para saída

5 Timer 1 Control Register

Page 416: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

394394394394

BBBB

INTCON = 0b11000000; // habilita as interrupções dos periféricos PIE1 = 0b00000001; // habilita a interrpção do timer1 TMR1ON = 1; // habilita o timer 1 while(1) // loop eterno // Função que atende a interrupção do timer 1 #int_timer1 timer1_interrupt() cont_ints++; if(cont_ints >= 76) bPinoRA0 = !bPinoRA0; bpisca = bPinoRA0; cont_ints = 0;

Código 21-5 - Oscilador utilizando a interrupção do Timer 1

Figura 21-19 – Oscilador utilizando a interrupção do Timer 1 no MPLAB

UUSSAARRTT oouu SSCCII

MMooddooss ddee OOppeerraaççããoo Assíncrono (full duplex) Síncrono - Master (half duplex) Síncrono - Slave (half duplex) O bit do registrador SPEN (RCSTA<7>), e do TRIS, devem ser setados na ordem para

configura os pinos TX/CK e RX/DT para a USART. Interrupções na transmissão e na recepção

Page 417: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

395395395395

BBBB

RReeggiissttrraaddoorreess AAssssoocciiaaddooss

Tabela 21-6 – Registrador TXSTA6

6 Transmit Status and Control Register

Page 418: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

396396396396

BBBB

Tabela 21-7 – Registrador RCSTA7

UUSSAARRTT BBaauudd RRaattee GGeenneerraattoorr ((BBRRGG)) Timer dedicado de 8 bits

Tabela 21-8 – Formula para o Baudrate

7 Receive Status and Control Register.

Page 419: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

397397397397

BBBB

Tabela 21-9 – Registradores associados com o Baudrate Generator

TTrraannssmmiissssããoo AAssssíínnccrroonnaa

Figura 21-20 – Diagrama de blocos do transmissor da USART

Siga os seguintes passos para ajustar a transmissão assíncrona:

1. Inicializar o registrador SPBRG para o baudrate apropriado. Se for necessário utilizar um baudrate elevado, setar o bit BRGH.

2. Habilitar a porta serial assíncrona, setando a 0 o bit SYNC e setando a 1 o bit SPEN.

3. Se desejar utilizar interrupção, setar a 1 os bits TXIE, GIE e PEIE.

4. Se for necessária a transmissão do 9o. bit, então o bit TX9 deve ser setado a 1.

5. Habilitar a transmissão setando o bit TXEN, que também setará o bit TXIF.

6. Se for selecionada a transmissão do 9o. bit, o nono bit deverá ser carregado no bit TX9D.

7. Carregue os dados no registrador TXREG (início da transmissão)

Figura 21-21 – Temporização da transmissão assíncrona do mestre

Page 420: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

398398398398

BBBB

Tabela 21-10 – Registradores associados com a transmissão assíncrona

EExxeerrccíícciioo 77 -- TTrraannssmmiissssoorr sseerriiaall

Faça um programa que transmita constantemente a letra ’Y’ de forma serial pelo pino Tx.

Utilize o canal serial configurado para 57600 bps, 8 bits de dados, 1 start bit, um stop bit e sem bit de paridade.

Para testar, utilize o osciloscópio e o software HyperTerminal® do Windows // define o chip #device PIC16C63 // define macros #define true 1 // define o clock #use delay(clock=20000000) #byte TRISA = 0x85 // define a variável de controle de direção #byte INTCON = 0x0B // variável que contém o valor do registrador de interrupções #byte TXSTA = 0x98 #byte RCSTA = 0x18 #byte PIE1 = 0x8C #byte TXREG = 0x19 #byte SPBRG = 0x99 #bit BRGH = TXSTA.2 #bit SYNC = TXSTA.4 #bit SPEN = RCSTA.7 #bit TX9 = TXSTA.0 #bit TXEN = TXSTA.5 #bit TRMT = TXSTA.1 #bit TXIE = PIE1.4 //////////////////////////////////////////////////////////////////////// void main(void) // inicializar o registrador SPBRG para o baudrate apropriado SPBRG = 21; // 21 decimal - tabla 12-1 pg.107 // baudrate = fosc/(16(X+1)) = 56818 bps - 20 MHz BRGH = 1; // High speed SYNC = 0; // em modo assíncrono SPEN = 1; // serial port enabled // Se for necessária a interrupção, setar o bit TXIE // TXIE = 0; // Se for necessário o 9o.bit setar o bit TX9. // TX9 = 0; // Habilitar a transmissão setando o bit TXEN TXEN = 1; // habilita a transmissão INTCON = 0b11000000; // habilita as interrupções dos periféricos

while(1) // loop eterno if(TRMT) // espera esvaziar o buffer de transmissão TXREG = 'Y';

Page 421: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

399399399399

BBBB

Código 21-6 - Transmissor serial

EExxeerrccíícciioo 88 -- TTrraannssmmiissssoorr sseerriiaall 22

Repita o Exercício 7 utilizando: #use RS-232 (baud=57600, xmit=PIN_C6, rcv=PIN_C7, PARITY=N, BITS=8, BRGH1OK) printf(“Y”);

Para testar, utilize o osciloscópio e o software HyperTerminal® do Windows.

EExxeerrccíícciioo 99 -- RReecceeppttoorr sseerriiaall

Faça um programa que transmita pelo pino Tx o seu nome completo, caso seja recebido no pino Rx o caractere ‘n’. Caso o caractere seja diferente de n, envie-lo de volta pelo pino Tx.

Utilize o canal serial configurado para 57600 bps, 8 bits de dados, 1 start bit, um stop bit e sem bit de paridade, utilizando interrupção da recepção.

Para testar, utilize o osciloscópio e o software HyperTerminal® do Windows.

RRXX..CC // define macros #define true 1 #include "RX.H" #byte RCREG = 0x1A #byte TRISC = 0x87 #int_rda void data_received() printf("\r\nRecebi = %c\r\n",RCREG); main() enable_interrupts(GLOBAL); enable_interrupts(int_rda); TRISC = 0b10111111; // coloca o pino Tx (RC6) como saída while( TRUE ) restart_wdt();

Código 21-7 – RX.C

RRXX..HH #device PIC16C63 #use delay(clock=20000000) #nolist /////////////////////////////// I/O definitions for INPUT() and OUTPUT_xxx() #define PIN_A0 40 #define PIN_A1 41 #define PIN_A2 42 #define PIN_A3 43 #define PIN_A4 44 #define PIN_A5 45 #use fixed_io(A_OUTPUTS=) #byte port_A=5

Page 422: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

400400400400

BBBB

#define PIN_B0 48 #define PIN_B1 49 #define PIN_B2 50 #define PIN_B3 51 #define PIN_B4 52 #define PIN_B5 53 #define PIN_B6 54 #define PIN_B7 55 #use fixed_io(B_OUTPUTS=) #byte port_B=6 #define PIN_C0 56 #define PIN_C1 57 #define PIN_C2 58 #define PIN_C3 59 #define PIN_C4 60 #define PIN_C5 61 #define PIN_C6 62 #define PIN_C7 63 #use fixed_io(C_OUTPUTS=PIN_C6) #byte port_C=7 #use RS-232(baud=57600,xmit=PIN_C6,rcv=PIN_C7,PARITY=N,BITS=8,BRGH1OK) /////////////////////////////// Useful defines #define FALSE 0 #define TRUE 1 #define BYTE int #define BOOLEAN short int #define getc getch #define getchar getch #define puts(s) printf(s); putchar(13); putchar(10); #define putc putchar /////////////////////////////// Constants used for RESTART_CAUSE() #define WDT_FROM_SLEEP 0 #define WDT_TIMEOUT 8 #define MCLR_FROM_SLEEP 16 #define NORMAL_POWER_UP 24 /////////////////////////////// Constants used for SETUP_COUNTERS() #define RTCC_INTERNAL 0 #define RTCC_EXT_L_TO_H 32 #define RTCC_EXT_H_TO_L 48 #define RTCC_DIV_2 0 #define RTCC_DIV_4 1 #define RTCC_DIV_8 2 #define RTCC_DIV_16 3 #define RTCC_DIV_32 4 #define RTCC_DIV_64 5 #define RTCC_DIV_128 6 #define RTCC_DIV_256 7 #define WDT_18MS 8 #define WDT_36MS 9 #define WDT_72MS 10 #define WDT_144MS 11 #define WDT_288MS 12 #define WDT_576MS 13 #define WDT_1152MS 14 #define WDT_2304MS 15 #define L_TO_H 0x40 #define H_TO_L 0 #define RTCC_ZERO 0x0B20 // Used for ENABLE/DISABLE INTERRUPTS #define RB_CHANGE 0x0B08 // Used for ENABLE/DISABLE INTERRUPTS #define EXT_INT 0x0B10 // Used for ENABLE/DISABLE INTERRUPTS #define GLOBAL 0x0BC0 // Used for ENABLE/DISABLE INTERRUPTS ///////////////////////////////////// Constants used for Timer1 and Timer2 #define T1_DISABLED 0 #define T1_INTERNAL 5 #define T1_EXTERNAL 7 #define T1_EXTERNAL_SYNC 3 #define T1_CLK_OUT 8 #define T1_DIV_BY_1 0 #define T1_DIV_BY_2 0x10

Page 423: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

401401401401

BBBB

#define T1_DIV_BY_4 0x20 #define T1_DIV_BY_8 0x30 #byte TIMER_1_LOW= 0x0e #byte TIMER_1_HIGH= 0x0f #define T2_DISABLED 0 #define T2_DIV_BY_1 4 #define T2_DIV_BY_4 5 #define T2_DIV_BY_16 6 #byte TIMER_2= 0x11 #define INT_TIMER1 0x8C01 // Used for ENABLE/DISABLE INTERRUPTS #define INT_TIMER2 0x8C02 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used for SETUP_CCP1() #define CCP_OFF 0 #define CCP_CAPTURE_FE 4 #define CCP_CAPTURE_RE 5 #define CCP_CAPTURE_DIV_4 6 #define CCP_CAPTURE_DIV_16 7 #define CCP_COMPARE_SET_ON_MATCH 8 #define CCP_COMPARE_CLR_ON_MATCH 9 #define CCP_COMPARE_INT 0xA #define CCP_COMPARE_RESET_TIMER 0xB #define CCP_PWM 0xC #define CCP_PWM_PLUS_1 0x1c #define CCP_PWM_PLUS_2 0x2c #define CCP_PWM_PLUS_3 0x3c #byte CCP_1_LOW= 0x15 #byte CCP_1_HIGH= 0x16 #define INT_CCP1 0x8C04 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used for SETUP_CCP2() #byte CCP_2_LOW= 0x1B #byte CCP_2_HIGH= 0x1C #define INT_CCP2 0x8D01 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used in SETUP_SSP() #define SPI_MASTER 0x20 #define SPI_SLAVE 0x24 #define SPI_L_TO_H 0 #define SPI_H_TO_L 0x10 #define SPI_CLK_DIV_4 0 #define SPI_CLK_DIV_16 1 #define SPI_CLK_DIV_64 2 #define SPI_CLK_T2 3 #define SPI_SS_DISABLED 1 #define INT_SSP 0x8C08 // Used for ENABLE/DISABLE INTERRUPTS #define INT_SSP 0x8C08 // Used for ENABLE/DISABLE INTERRUPTS #define INT_RDA 0x8C20 // Used for ENABLE/DISABLE INTERRUPTS #define INT_TBE 0x8C10 // Used for ENABLE/DISABLE INTERRUPTS #list

Código 21-8 – RX.H

TTiimmeerr 22

CCaarraacctteerrííssttiiccaass 8-bit timer com prescaler, postscaler, e registrador de período Utilizando o prescaler e postscaler com os valores máximos, o tempo de overflow é igual ao de

um timer de 16 bits. O Timer2 é a base de tempo para o modulador PWM quando o módulo CCP for programado

para isso.

Page 424: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

402402402402

BBBB

DDiiaaggrraammaa ddoo TTiimmeerr 2288

Figura 21-22 – Diagrama do Timer 2

RReeggiissttrraaddoorreess AAssssoocciiaaddooss aaoo TTiimmeerr 22

Tabela 21-11 – Registradores associados com o Timer 2

Tabela 21-12 – Registrador T2CON 8 A saída do registrador TMR2 pode ser selecionada por software pelo modulo SSP como gerador de clock para controlar o baudrate.

Page 425: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

403403403403

BBBB

MMóódduulloo CCCCPP ((CCoommppaarree,, CCaappttuurree aanndd PPWWMM mmoodduullee))

CCaarraacctteerrííssttiiccaass GGeerraaiiss Cada módulo contém 3 registradores Três modos de funcionamento: Capturador Comparador PWM

RReeccuurrssooss Modo CCP Recursos de Temporização

Capture Timer 1

Compare Timer 1

PWM Timer 2

Tabela 21-13 – Modo CCP – Recursos de Temporização

RReeggiissttrraaddoorreess AAssssoocciiaaddooss aaoo MMóódduulloo CCCCPP

Tabela 21-14 – Nomenclatura CCP especifica e genérica

Page 426: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

404404404404

BBBB

Tabela 21-15 – Registrador CCPxCON

MMooddoo CCaappttuurree No modo Capture, CCPRxH:CCPRxL captura o valor de 16 bits do registrador do

TMR1 quando um evento ocorrer no pino CCPx. Os possíveis eventos são: A cada borda de descida A cada borda de subida A cada 4 bordas de subida A cada 16 bordas de subida

//// Programa que implementa a medida da largura de um pulso utilizando CCP #include <16C63.H> #use Delay(Clock=4000000) #use RS-232(baud=9600,xmit=PIN_C6,rcv=PIN_C7,brgh1ok) long rise,fall,pulse_width; #int_ccp2 void isr() rise = CCP_1; fall = CCP_2; pulse_width = fall - rise; // CCP_1 is the time the pulse went high // CCP_2 is the time the pulse went low pulse_width/(clock/4) is the time main() printf("\r\nTempo em ON (amostragem a cada segundo:\r\n"); setup_ccp1(CCP_CAPTURE_RE); // Configura CCP1 para capturar a subida setup_ccp2(CCP_CAPTURE_FE); // Configura CCP2 para capturar a descida setup_timer_1(T1_INTERNAL); // Inicia o timer 1 enable_interrupts(INT_CCP2); // Seleciona a interrupção na descida enable_interrupts(GLOBAL); while(TRUE) delay_ms(1000); printf("\r%lu us ", pulse_width/5 );

Page 427: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

405405405405

BBBB

MMooddoo CCoommppaarree Neste modo o registrador CCPRx de 16 bits é constantemente comparado com o

TMR1 (16 bits). Quando a igualdade ocorrer, o pino CCPx poderá ficar (de acordo com a configuração escolhida): Nível alto Nível baixo Sem modificações Ao mesmo tempo também será gerada uma interrupção.

// Programa que gera um pulso de tempo predefinido em resposta ao aperto de uma tecla #include <16C63.H> #use Delay(Clock=20000000) main() setup_ccp1(CCP_COMPARE_CLR_ON_MATCH); // Configure CCP1 in //COMPARE mode setup_timer_1(T1_INTERNAL); // Set up timer to instruction clk while(TRUE) while(input(PIN_B7)) ; // Wait for keypress setup_ccp1(CCP_COMPARE_SET_ON_MATCH); // Configure CCP1 to set CCP_1=0; // C2 high now set_timer1(0); CCP_1 = 500; // Set high time limit to 100 uslimit is time/(clock/4) // 500 = .0001*(20000000/4) // Configure CCP1 in COMPAREmode and to pull pin C2 low on a match with //timer1 setup_ccp1(CCP_COMPARE_CLR_ON_MATCH); delay_ms(1000); // Debounce - Permit only one pulse per sec

Código 21-9 – Modo Capture

MMooddoo PPWWMM Freqüência constante. Tempo em ON variável (valor médio variável) Resolução de até 10 bits

Figura 21-23 – Saída PWM

Período PWM = [(PR2)+1] * 4 * TOSC * (valor do prescaler TMR2) unidades de tempo.

Duty Cycle PWM = (valor dos bits DCxB9:DCxB0) * TOSC * (valor do prescaler TMR2); unidades de tempo

Page 428: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

406406406406

BBBB

Máxima resolução PWM para uma determinada freqüência )2log(

log

= PWM

OSC

F

F

bits.

Exercício 1: É desejado um PWM com freqüência de 78.125 kHz. Calcular o valor de PR2.

Fosc = 20 MHz

TMR2 prescaler = 1

1/78125 = [(PR2) + 1] * 4 * 1/20000000 * 1

12.8 µs = [(PR2) + 1] * 4 * 50 ns * 1

PR2 = 63

Exercício 2: Encontrar a máxima resolução para o duty cycle, de que pode ser utilizado com uma freqüência de 78.125 kHz e com um oscilador de 20 MHz.

1/78.125 kHz = 2 ResoluçãoPWM * 1/20 MHz * 1

12.8 µs = 2 ResoluçãoPWM * 50 ns

256 = 2 ResoluçãoPWM

log(256) = ResoluçãoPWM * log(2)

8.0 = ResoluçãoPWM

PPWWMM -- AApplliiccaaççõõeess Saídas analógicas de tensão e corrente (ex. 4 a 20 mA). Transmissão de dados Controle de motores DC

EExxeerrccíícciioo 1100 -- PPWWMM

Faça um programa que implemente uma saída PWM cujo Duty Cycle deve ser programável via canal serial, através das teclas ‘+’ e ’-’ do teclado do PC usando o HyperTerminal®. Programar para uma freqüência de 78.125 kHz. Utilizar um osciloscópio para os testes.

PPWWMM..CC // define macros #define true 1 #include "PWM.H" #byte RCREG = 0x1A #byte TRISC = 0x87 int nDutyCycle = 0;

Page 429: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

407407407407

BBBB

#int_rda void data_received() if (RCREG=='+') nDutyCycle++; else nDutyCycle--; printf("\r\nRecebi = %c\r\n",RCREG); main() enable_interrupts(GLOBAL); enable_interrupts(int_rda); setup_ccp1(CCP_PWM); // Configura CCP1 como uma saída PWM // O periodo de tempo será (1/clock)*4*t2div*(pr2+1) // Neste programa o clock = 20000000 e PR2 = 63 // (1/20000000)*4*63*1 = 12.8 us ou 78.125 khz TRISC = 0b10111011; setup_timer_2(T2_DIV_BY_1, 63, 0); while( TRUE ) restart_wdt(); printf("\r\n%2X",nDutyCycle); delay_ms(2); set_pwm1_duty(nDutyCycle);

Código 21-10 - PWM.C

PPWWMM..HH #device PIC16C63 #use delay(clock=20000000) #nolist /////////////////////////////// I/O definitions for INPUT() and OUTPUT_xxx() #define PIN_A0 40 #define PIN_A1 41 #define PIN_A2 42 #define PIN_A3 43 #define PIN_A4 44 #define PIN_A5 45 #use fixed_io(A_OUTPUTS=) #byte port_A=5 #define PIN_B0 48 #define PIN_B1 49 #define PIN_B2 50 #define PIN_B3 51 #define PIN_B4 52 #define PIN_B5 53 #define PIN_B6 54 #define PIN_B7 55 #use fixed_io(B_OUTPUTS=) #byte port_B=6 #define PIN_C0 56 #define PIN_C1 57 #define PIN_C2 58 #define PIN_C3 59 #define PIN_C4 60 #define PIN_C5 61 #define PIN_C6 62 #define PIN_C7 63 #use fixed_io(C_OUTPUTS=PIN_C6) #byte port_C=7 #use RS-232(baud=57600,xmit=PIN_C6,rcv=PIN_C7,PARITY=N,BITS=8,BRGH1OK) /////////////////////////////// Useful defines #define FALSE 0 #define TRUE 1 #define BYTE int

Page 430: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

408408408408

BBBB

#define BOOLEAN short int #define getc getch #define getchar getch #define puts(s) printf(s); putchar(13); putchar(10); #define putc putchar /////////////////////////////// Constants used for RESTART_CAUSE() #define WDT_FROM_SLEEP 0 #define WDT_TIMEOUT 8 #define MCLR_FROM_SLEEP 16 #define NORMAL_POWER_UP 24 /////////////////////////////// Constants used for SETUP_COUNTERS() #define RTCC_INTERNAL 0 #define RTCC_EXT_L_TO_H 32 #define RTCC_EXT_H_TO_L 48 #define RTCC_DIV_2 0 #define RTCC_DIV_4 1 #define RTCC_DIV_8 2 #define RTCC_DIV_16 3 #define RTCC_DIV_32 4 #define RTCC_DIV_64 5 #define RTCC_DIV_128 6 #define RTCC_DIV_256 7 #define WDT_18MS 8 #define WDT_36MS 9 #define WDT_72MS 10 #define WDT_144MS 11 #define WDT_288MS 12 #define WDT_576MS 13 #define WDT_1152MS 14 #define WDT_2304MS 15 #define L_TO_H 0x40 #define H_TO_L 0 #define RTCC_ZERO 0x0B20 // Used for ENABLE/DISABLE INTERRUPTS #define RB_CHANGE 0x0B08 // Used for ENABLE/DISABLE INTERRUPTS #define EXT_INT 0x0B10 // Used for ENABLE/DISABLE INTERRUPTS #define GLOBAL 0x0BC0 // Used for ENABLE/DISABLE INTERRUPTS ///////////////////////////////////// Constants used for Timer1 and Timer2 #define T1_DISABLED 0 #define T1_INTERNAL 5 #define T1_EXTERNAL 7 #define T1_EXTERNAL_SYNC 3 #define T1_CLK_OUT 8 #define T1_DIV_BY_1 0 #define T1_DIV_BY_2 0x10 #define T1_DIV_BY_4 0x20 #define T1_DIV_BY_8 0x30 #byte TIMER_1_LOW= 0x0e #byte TIMER_1_HIGH= 0x0f #define T2_DISABLED 0 #define T2_DIV_BY_1 4 #define T2_DIV_BY_4 5 #define T2_DIV_BY_16 6 #byte TIMER_2= 0x11 #define INT_TIMER1 0x8C01 // Used for ENABLE/DISABLE INTERRUPTS #define INT_TIMER2 0x8C02 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used for SETUP_CCP1() #define CCP_OFF 0 #define CCP_CAPTURE_FE 4 #define CCP_CAPTURE_RE 5 #define CCP_CAPTURE_DIV_4 6 #define CCP_CAPTURE_DIV_16 7 #define CCP_COMPARE_SET_ON_MATCH 8 #define CCP_COMPARE_CLR_ON_MATCH 9 #define CCP_COMPARE_INT 0xA #define CCP_COMPARE_RESET_TIMER 0xB #define CCP_PWM 0xC #define CCP_PWM_PLUS_1 0x1c #define CCP_PWM_PLUS_2 0x2c #define CCP_PWM_PLUS_3 0x3c #byte CCP_1_LOW= 0x15 #byte CCP_1_HIGH= 0x16

Page 431: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

409409409409

BBBB

#define INT_CCP1 0x8C04 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used for SETUP_CCP2() #byte CCP_2_LOW= 0x1B #byte CCP_2_HIGH= 0x1C #define INT_CCP2 0x8D01 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used in SETUP_SSP() #define SPI_MASTER 0x20 #define SPI_SLAVE 0x24 #define SPI_L_TO_H 0 #define SPI_H_TO_L 0x10 #define SPI_CLK_DIV_4 0 #define SPI_CLK_DIV_16 1 #define SPI_CLK_DIV_64 2 #define SPI_CLK_T2 3 #define SPI_SS_DISABLED 1 #define INT_SSP 0x8C08 // Used for ENABLE/DISABLE INTERRUPTS #define INT_SSP 0x8C08 // Used for ENABLE/DISABLE INTERRUPTS #define INT_RDA 0x8C20 // Used for ENABLE/DISABLE INTERRUPTS #define INT_TBE 0x8C10 // Used for ENABLE/DISABLE INTERRUPTS #list

Código 21-11 - PWM.H

PPoorrttaa SSeerriiaall SSíínnccrroonnaa ((SSSSPP))

CCaarraacctteerrííssttiiccaass O módulo porta Serial Síncrona é uma interface serial amplamente utilizada para a

comunicação com outros periféricos ou microcontroladores. Estes dispositivos podem ser EEPROMS, shift registers9, drivers de display, conversores AD10 e DA11, RTCs12, e muitos outros.

O módulo SSP pode operar em um de dois modos possíveis: Serial Peripheral Interface (SPI™) Inter-Integrated Circuit (I2C™) Modo Slave I/O slope control, detecção de bit de Start e Stop para facilitar a implementação de

software nos modos Master e Multi-master.

9 Registradores de deslocamento. 10 Analog to Digital. 11 Digital to Analog. 12 Real Time Clocks.

Page 432: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

410410410410

BBBB

RReeggiissttrraaddoorreess AAssssoocciiaaddooss àà PPoorrttaa SSeerriiaall SSíínnccrroonnaa

Tabela 21-16 – Registrador SSPSTAT13

13 Synchronous Serial Port Status Register

Page 433: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

411411411411

BBBB

Tabela 21-17 – Registrador SSPCON14

MMooddoo SSPPII Este modo permite a transmissão e recepção simultânea de 8 bits de dados.

14 Synchronous Serial Port Control Register.

Page 434: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

412412412412

BBBB

São suportados os 4 modos SPI assim como também o padrão Microwire™ (amostrado na borda) quando o SPI é configurado no modo Master Para efetuar a comunicação são utilizados 3 pinos: Serial Data Out (SDO) Serial Data In (SDI) Serial Clock (SCK)

Poderá ser utilizado um quarto pino se for necessário, no modo Slave: Slave Select (SS)

SSPPII -- OOppeerraaççããoo Quando o SPI for inicializado, várias opções precisam ser configuradas (SSPCON

[SSPCON<5:0>] e SSPSTAT<7:6>). Master Mode (SCK é a saída de clock) Slave Mode (SCK é a entrada de clock) Polaridade de Clock Polarity (Estado inativo do SCK) Borda ativa de Clock (dados saindo na borda de descida/subida de SCK) Data Input Sample Phase Velocidade de Clock (modo Master somente) Slave Select Mode (Modo Slave somente)

CCoonneexxããoo MMeessttrree--EEssccrraavvoo

Figura 21-24 – Conexão SPI Mestre-Escravo

Page 435: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

413413413413

BBBB

SSPPII MMaasstteerr

Figura 21-25 – Formas de onda no modo SPI Mestre

SSPPII SSllaavvee

Figura 21-26 – Formas de onda no modo SPI Escravo (Modo de seleção de escravo com CKE = 1

Page 436: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

414414414414

BBBB

SSPPII -- RReeggiissttrraaddoorreess AAssssoocciiaaddooss

Tabela 21-18 – Registradores associados ao SPI

EExxeerrccíícciioo 1111 -- SSPPII Implemente uma interface SPI Master que transmita constantemente um valor enviado pelo

PC. Verifique o funcionamento colocando ponteiras do osciloscópio nos terminais SCK e SDO.

SSPPII..CC // define macros #define true 1 #include "SPI.H" #byte RCREG = 0x1A #byte TRISC = 0x87 int nval = 0; #int_rda void data_received() nval = RCREG; printf("\r\nEnviando %d ('%c') (0x%2X) pelo SPI",nval,nval,nval); main() enable_interrupts(GLOBAL); enable_interrupts(int_rda); TRISC = 0b11010111; setup_spi(SPI_MASTER | SPI_H_TO_L | SPI_CLK_DIV_4); while(true) spi_write(nval); restart_wdt();

Código 21-12 - SPI.C

SSPPII..HH #device PIC16C63 #use delay(clock=20000000) #nolist /////////////////////////////// I/O definitions for INPUT() and OUTPUT_xxx() #define PIN_A0 40 #define PIN_A1 41 #define PIN_A2 42 #define PIN_A3 43

Page 437: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

415415415415

BBBB

#define PIN_A4 44 #define PIN_A5 45 #use fixed_io(A_OUTPUTS=) #byte port_A=5 #define PIN_B0 48 #define PIN_B1 49 #define PIN_B2 50 #define PIN_B3 51 #define PIN_B4 52 #define PIN_B5 53 #define PIN_B6 54 #define PIN_B7 55 #use fixed_io(B_OUTPUTS=) #byte port_B=6 #define PIN_C0 56 #define PIN_C1 57 #define PIN_C2 58 #define PIN_C3 59 #define PIN_C4 60 #define PIN_C5 61 #define PIN_C6 62 #define PIN_C7 63 #use fixed_io(C_OUTPUTS=PIN_C6) #byte port_C=7 #use RS-232(baud=57600,xmit=PIN_C6,rcv=PIN_C7,PARITY=N,BITS=8,BRGH1OK) /////////////////////////////// Useful defines #define FALSE 0 #define TRUE 1 #define BYTE int #define BOOLEAN short int #define getc getch #define getchar getch #define puts(s) printf(s); putchar(13); putchar(10); #define putc putchar /////////////////////////////// Constants used for RESTART_CAUSE() #define WDT_FROM_SLEEP 0 #define WDT_TIMEOUT 8 #define MCLR_FROM_SLEEP 16 #define NORMAL_POWER_UP 24 /////////////////////////////// Constants used for SETUP_COUNTERS() #define RTCC_INTERNAL 0 #define RTCC_EXT_L_TO_H 32 #define RTCC_EXT_H_TO_L 48 #define RTCC_DIV_2 0 #define RTCC_DIV_4 1 #define RTCC_DIV_8 2 #define RTCC_DIV_16 3 #define RTCC_DIV_32 4 #define RTCC_DIV_64 5 #define RTCC_DIV_128 6 #define RTCC_DIV_256 7 #define WDT_18MS 8 #define WDT_36MS 9 #define WDT_72MS 10 #define WDT_144MS 11 #define WDT_288MS 12 #define WDT_576MS 13 #define WDT_1152MS 14 #define WDT_2304MS 15 #define L_TO_H 0x40 #define H_TO_L 0 #define RTCC_ZERO 0x0B20 // Used for ENABLE/DISABLE INTERRUPTS #define RB_CHANGE 0x0B08 // Used for ENABLE/DISABLE INTERRUPTS #define EXT_INT 0x0B10 // Used for ENABLE/DISABLE INTERRUPTS #define GLOBAL 0x0BC0 // Used for ENABLE/DISABLE INTERRUPTS ///////////////////////////////////// Constants used for Timer1 and Timer2 #define T1_DISABLED 0 #define T1_INTERNAL 5 #define T1_EXTERNAL 7

Page 438: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

416416416416

BBBB

#define T1_EXTERNAL_SYNC 3 #define T1_CLK_OUT 8 #define T1_DIV_BY_1 0 #define T1_DIV_BY_2 0x10 #define T1_DIV_BY_4 0x20 #define T1_DIV_BY_8 0x30 #byte TIMER_1_LOW= 0x0e #byte TIMER_1_HIGH= 0x0f #define T2_DISABLED 0 #define T2_DIV_BY_1 4 #define T2_DIV_BY_4 5 #define T2_DIV_BY_16 6 #byte TIMER_2= 0x11 #define INT_TIMER1 0x8C01 // Used for ENABLE/DISABLE INTERRUPTS #define INT_TIMER2 0x8C02 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used for SETUP_CCP1() #define CCP_OFF 0 #define CCP_CAPTURE_FE 4 #define CCP_CAPTURE_RE 5 #define CCP_CAPTURE_DIV_4 6 #define CCP_CAPTURE_DIV_16 7 #define CCP_COMPARE_SET_ON_MATCH 8 #define CCP_COMPARE_CLR_ON_MATCH 9 #define CCP_COMPARE_INT 0xA #define CCP_COMPARE_RESET_TIMER 0xB #define CCP_PWM 0xC #define CCP_PWM_PLUS_1 0x1c #define CCP_PWM_PLUS_2 0x2c #define CCP_PWM_PLUS_3 0x3c #byte CCP_1_LOW= 0x15 #byte CCP_1_HIGH= 0x16 #define INT_CCP1 0x8C04 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used for SETUP_CCP2() #byte CCP_2_LOW= 0x1B #byte CCP_2_HIGH= 0x1C #define INT_CCP2 0x8D01 // Used for ENABLE/DISABLE INTERRUPTS //////////////////////////////////// Constants used in SETUP_SSP() #define SPI_MASTER 0x20 #define SPI_SLAVE 0x24 #define SPI_L_TO_H 0 #define SPI_H_TO_L 0x10 #define SPI_CLK_DIV_4 0 #define SPI_CLK_DIV_16 1 #define SPI_CLK_DIV_64 2 #define SPI_CLK_T2 3 #define SPI_SS_DISABLED 1 #define INT_SSP 0x8C08 // Used for ENABLE/DISABLE INTERRUPTS #define INT_SSP 0x8C08 // Used for ENABLE/DISABLE INTERRUPTS #define INT_RDA 0x8C20 // Used for ENABLE/DISABLE INTERRUPTS #define INT_TBE 0x8C10 // Used for ENABLE/DISABLE INTERRUPTS #list

Código 21-13 - SPI.H

CCoonnvveerrssoorr AAnnaallóóggiiccoo--DDiiggiittaall ((AADDCC))

AADDCC -- CCaarraacctteerrííssttiiccaass O módulo de conversão analógico-digital (A/D) permite de 5 a 8 entradas

analógicas.

Page 439: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

444417171717

BBBB

O conversor A/D permite a conversão de um sinal de entrada analógico num número digital correspondente de 8 bits (alguns, 10 bits).

A saída de um circuito “sample and hold” é a entrada do ADC do tipo “aproximações sucessivas”.

A tensão de referencia é selecionável por software para o nível de tensão do pino VREF ou da tensão de alimentação VDD

O ADC trabalha durante o modo sleep.

3 Registradores A/D Result Register (ADRES) A/D Control Register0 (ADCON0) A/D Control Register1 (ADCON1)

ADRES armazena o valor digital da última aquisição.

ADCON0 controla a operação do modulo

ADCON1 configura o funcionamento dos pinos da porta. Os pinos de I/O podem ser configurados como entradas analógicas (um deles pode ser a tensão de referência) ou como simples pinos de I/O digitais.

DDiiaaggrraammaa ddoo AADDCC

Figura 21-27 – Diagrama de blocos do conversor A/D de 8 bits15

15 Em alguns dispositivos existe um pino separado denominado AVDD. Isto permite que o VDD do A/D seja conectado a uma fonte de tensão de referência.

Page 440: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

418418418418

BBBB

RReeggiissttrraaddoorreess AAssssoocciiaaddooss aaoo AADDCC

Tabela 21-19 – Registrador ADCON0

Tabela 21-20 – Registrador ADCON116

16 Quando AN3 é selecionado como VREF, a referência do A/D é a tensão no pino AN2. Quando AN3 é selecionado como uma entrada analógica (A), entao a referência de tensão para o A/D é VDD.

Page 441: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

419419419419

BBBB

Figura 21-28 – Modelo de entrada analógica

Figura 21-29 – Função de transferência do A/D

WWaattcchhddoogg TTiimmeerr1177 ee oo MMooddoo SSlleeeepp ((PPoowweerr DDoowwnn))

WWaattcchhddoogg TTiimmeerr Timer com circuito oscilador RC interno independente. O seu uso aumenta a confiabilidade do sistema. Se acontecer timeout, reseta o dispositivo.

SSlleeeepp -- PPoowweerr DDoowwnn Mínimo consumo de energia. Aplicações com baterias.

17 Cão-de-Guarda.

Page 442: Tratado Da Linguagem c

A P Ê N D I C E B - M I C R O C O N T R O L A D O R E S M I C R O C H I P P I C

420420420420

BBBB

AAccoorrddaannddoo oo DDiissppoossiittiivvoo Qualquer tipo de reset do dispositivo. Watchdog Timer Wake-up (se WDT estiver habilitado). Qualquer módulo periférico que possa setar o seu flag de interrupção enquanto dorme tais

como: Interrupção externa no pino INT Variações nos pinos da porta B Comparadores A/D Timer1 LCD SSP Capture

IICCSSPP ((IInn CCiiccuuiitt SSeerriiaall PPrrooggrraammmmiinngg)) Programação do chip on-board. Montagem da placa com dispositivos não gravados. Firmware sempre atualizado em estoques de placas já montadas.

CCiirrccuuiittoo TTííppiiccoo

Figura 21-30 – Circuito típico de aplicação ICSP18

EExxeerrccíícciioo 1122 Faça um programa que leia ou escreva nas portas A,B,C,D quando requisitado por um PC

seguindo o seguinte protocolo: (operação)(porta).(bit)=(valor se for escrita) Ex: Ler o bit 2 da porta B => LB.2 Escrever 1 no bit 5 da porta A => EA.5=1 Caso um dos caracteres seja diferente de uma das combinações possíveis envie “Erro, tente

novamente”.

18 In-Circuit Serial Programming

Page 443: Tratado Da Linguagem c

421421421421 Luis Fernando Espinosa Cocian

AAppêênnddiiccee CC -- MMiiccrrooccoonnttrroollaaddoorreess ddaa FFaammíílliiaa IInntteell 88005511

AApplliiccaaççõõeess Sistemas de Controle Dedicados (automóveis, eletrodomésticos, etc.) Coletores de Dados Instrumentação de Processos Sistemas de Acesso e Segurança Sistemas de Transmissão de Dados

MMiiccrrooccoonnttrroollaaddoorreess 88xx55xx Disponibilidade Ferramentas de desenvolvimento Confiabilidade Facilidade de uso e programação Ampla gama de dispositivos Baixo custo Periféricos integrados. Vários fabricantes.

FFaabbrriiccaanntteess Atmel Intel Philips Winbond Dallas Outros

AATT8899CC5522 -- CCaarraacctteerrííssttiiccaass Compatível com MCS-51™ 8K Bytes de memória de programa Flash (In-System Reprogrammable Flash Memory) Durabilidade: 1000 ciclos de Leitura/Escrita Operação totalmente estática: 0 Hz a 24 MHz Proteção de memória em três níveis. RAM interna de 256 x 8 Bits 32 linhas de I/O programáveis Três Timer/Counters de 16 bits Oito fontes de interrupção Canal serial programável Modos de Low Power Idle e Power Down

Apêndice

C

Page 444: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

422422422422

CCCC

AApppplliiccaattiioonn BBuuiillddeerr ddaa IInntteell®®

Figura 22-1 – Aparência do Application Builder da Intel

PPiinnaaggeemm ddoo 88005511

Figura 22-2 – Pinagem dos microcontroladores 8051, encapsulamento PQFF

Page 445: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

423423423423

CCCC

Figura 22-3 – Pinagem dos microcontroladores 8051, encapsulamento PDIP

Figura 22-4 – Pinagem dos microcontroladores 8051, encapsulamento PLCC

Page 446: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

424424424424

CCCC

AArrqquuiitteettuurraa

Figura 22-5 – Arquitetura dos microcontroladores 8052

Page 447: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

425425425425

CCCC

OOrrggaanniizzaaççããoo ddaa MMeemmóórriiaa ddee PPrrooggrraammaa

Figura 22-6 – Organização da memória de programa dos microcontroladores 8052

RReeggiissttrraaddoorreess IInntteerrnnooss ee MMeemmóórriiaa ddee DDaaddooss

Figura 22-7 – Memória de dados dos microcontroladores 8052

Page 448: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

426426426426

CCCC

Figura 22-8 – Registradores internos dos microcontroladores 8052

Page 449: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

427427427427

CCCC

IInntteerrrruuppççõõeess

Figura 22-9 – Interrupções dos microcontroladores 8052

OO ssiimmuullaaddoorr -- JJSSIIMM

FFeerrrraammeennttaa ddee ssiimmuullaaççããoo Freeware1 Simula canal serial através de um terminal virtual Simulação de outras interrupções

1 http://home.t-online.de/home/Jens.Altmann

Page 450: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

428428428428

CCCC

Figura 22-10 – Aparência do software JSIM

OO JJeennss EEddiittoorr -- JJFFEE Freeware Permite a inclusão de botões para chamada de outros programas.

Figura 22-11 – Aparência do software JFE

Page 451: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

429429429429

CCCC

AAddiicciioonnaarr aa cchhaammaaddaa aaoo CCoommppiillaaddoorr

No menu Setting + Add Tool...

Figura 22-12 – Adicionando a chamada ao compilador

AAddiicciioonnaarr aa cchhaammaaddaa aaoo lliinnkkeerr $PATH\$NAME.obj to $PATH\$NAME.abs DEBUGLINES DEBUGPUBLICS

DEBUGSYMBOLS ramsize(256) idata(80H) No campo Start Directory coloque o caminho onde estão os arquivos a serem linkados. O

linker gera arquivos com formato *.abs

Figura 22-13 – Adicionando a chamada ao linker

Page 452: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

430430430430

CCCC

AAddiicciioonnaarr aa cchhaammaaddaa aaoo ssiimmuullaaddoorr JJSSIIMM

Figura 22-14 – Adicionando a chamada ao JSIM

AAddiicciioonnaarr aa cchhaammaaddaa aaoo ccoonnvveerrssoorr ((aabbss ttoo hheexx)) OOHHSS5511

Figura 22-15 – Adicionando a chamada ao conversor para HEX

Page 453: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

431431431431

CCCC

AAddiicciioonnaarr aa cchhaammaaddaa aaoo ccoonnvveerrssoorr ((hheexx ttoo bbiinn)) HHeexxbbiinn

Figura 22-16 – Adicionando a chamada o conversor para binário

OO CCoommppiillaaddoorr CC5511 ddaa FFrraannkklliinn SSooffttwwaarree Versão freeware para DOS Versão de baixo custo para Windows, incluindo ambiente integrado de depuração

GGrraavvaaddoorr Antrax Technology Ltda. – Mater Matris Technae Ltda. (AT89C52 e compatíveis) Software DOS e Windows 9x

RReeggrraass ddoo CCoommppiillaaddoorr CC5511 ddaa KKeeiill

Tipos Especiais: sbit: (special bit) 1 bit da porta de I/O sfr: (special function register) 8 bits, indica um registrador específico.

Exemplo.: sfr PSW = 0xD0; sbit CY = PSW^7;

BBiibblliiootteeccaa ddee FFuunnççõõeess abs acos asin atan

atoi atol _testbit_ exp

getchar gets log10 log

memcpy memset strcat strchr

Page 454: Tratado Da Linguagem c

A P Ê N D I C E C - M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

432432432432

CCCC

strcmp strcpy strcspn stricmp strlen isalnum

isalpha isdigit islower isspace isupper isxdigit

strlwr strncmp strncmp strncpy strpbrk strrchr

strspn strstr strtok swap tolower toupper

SSeeqqüüêênncciiaa ddee PPaassssooss ppaarraa IImmpplleemmeennttaaççããoo Definir claramente o problema Definir os pinos de entrada e saída necessários no microcontrolador Usando um editor de textos, edite o código fonte que solucione o problema. Compile e verifique a não existência de erros. Linkar o programa resultante. Simule se for necessário, e grave o chip

PPoorrttaass ddee II//OO ddiiggiittaaiiss

CCaarraacctteerrííssttiiccaass Quatro portas de I/O digitais de 8 bits As quatro portas são bidirecionais (P0 a P3). As portas P0 e P2 são utilizadas para acessar memória externa ou como pinos de I/O. As portas P1 e P3 são multifuncionais

PPoorrttaa PP00 P0.0/AD0 to P0.7/AD7 Byte multiplexado de Endereços/Dados para acessar a Memória

Externa.

Figura 22-17 – Buffers de I/O e latches da porta P0

PPoorrttaa PP11 P1.0/T2 Timer 2 External Clock Input/Clock-Out P1.1/T2EX Timer 2 Reload/Capture/Direction Control P1.2/ECI PCA External Clock Input P1.3/CEX0 PCA Module 0 Capture Input, Compare/PWM Output

Page 455: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

433433433433

CCCC

P1.4/CEX1 PCA Module 1 Capture Input, Compare/PWM Output P1.5/CEX2 PCA Module 2 Capture Input, Compare/PWM Output P1.6/CEX3 PCA Module 3 Capture Input, Compare/PWM Output P1.7/CEX4 PCA Module 4 Capture Input, Compare/PWM Output

Figura 22-18 – Diagramas das portas P1 e P3

PPoorrttaa PP22 P2.0/A8 P2.7/A15 Byte Alto do Endereço para a Memória Externa

Figura 22-19 – Diagramas da porta P2

PPoorrttaa PP33 P3.0/RXD Entrada da Porta Serial P3.1/TXD Saída da Porta Serial P3.2/INT0# Interrupção Externa 0 P3.3/INT1# Interrupção Externa 1 P3.4/T0 Entrada de Clock Externa do Timer 0 P3.5/T1 Entrada de Clock Externa do Timer 1

Page 456: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

434434434434

CCCC

P3.6/WR# Sinalizador de Escrita para a Memória Externa P3.7/RD# Sinalizador de Leitura para a Memória Externa

PPrroobblleemmaa 11 -- OOsscciillaaddoorr Implemente um firmware que produza uma onda quadrada num pino de I/O. Simule no JSIM. Grave um chip e verifique o funcionamento utilizando um osciloscópio.

/*/ Opcoes para el compilador y el linker Franklin C //*/ #pragma DEBUG SYMBOLS OBJECTEXTEND CODE /*/ O Linker deve ter as seguintes opcoes para simular no JSim $(FileName).obj to $(FileName).abs DEBUGLINES DEBUGPUBLICS DEBUGSYMBOLS ramsize(256) idata(80H) */ sfr P1 = 0x90; sbit bOnda = P1^0; void main (void) bit bAux = 0; while(1) bAux = !bAux; bOnda = bAux;

Código 22-1 – Oscilador com 8052

Figura 22-20 – Oscilador utilizando o MS Visual C++ 5.0 como editor

Page 457: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

435435435435

CCCC

PPrroobblleemmaa 22 -- OOsscciillaaddoorr CCoonnttrroollaaddoo Implemente um firmware que produza uma onda quadrada num pino de I/O (P1.0) somente

quando é apertada uma tecla (valor igual a 0) conectada ao pino P1.7. Simule no JSIM. Grave um chip e verifique o funcionamento utilizando um osciloscópio.

/*/ Opcoes para el compilador y el linker Franklin C //*/ #pragma DEBUG SYMBOLS OBJECTEXTEND CODE /*/ O Linker deve ter as seguintes opcoes para simular no JSim $(FileName).obj to $(FileName).abs DEBUGLINES DEBUGPUBLICS DEBUGSYMBOLS ramsize(256) idata(80H) */ sfr P1 = 0x90; sbit bOnda = P1^0; sbit bTecla = P1^7; void main (void) bit bAux = 0; while(1) if(bTecla) bAux = !bAux; bOnda = bAux; else bOnda = 0;

Código 22-2 – Oscilador controlado com 8052

Figura 22-21 – Oscilador controlado

Page 458: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

436436436436

CCCC

EExxeerrccíícciioo 33 -- OOnnddaa QQuuaaddrraaddaa

Faça um programa que implemente um gerador de onda quadrada no pino P1.0, com período de 2 segundos e de 0.5 segundos (1 segundo por estado) quando a tecla em P1.7 estiver pressionada.

OOss tteemmppoorriizzaaddoorreess TTiimmeerr 00,, TTiimmeerr 11 ee TTiimmeerr 22

CCaarraacctteerrííssttiiccaass ddoo TTiimmeerr 00 ee TTiimmeerr 11 Timers/counters de 16 bits Fonte de clock selecionável externa ou interna. 4 Modos de operação: timer/counter de 13 bits timer/counter de 16 bits timer/counter de 8 bits com auto-reload dois timers de 8 bits.

TTiimmeerr 00 ee 11 -- OOppeerraaççããoo As funções de timer ou counter são selecionadas pelos bits no registrador TMOD. Os quatro

modos de operação são selecionados pelos pares de bits M1 e M0. O modo 0, 1 e 2 são os mesmos para ambos timers, sendo o modo 3 diferente. O Timer 1 pode ser utilizado como gerador de baudrate.

TTiimmeerr 00 ee 11-- MMooddoo 00 Neste modo ambos são contadores de 8 bits com prescaler /32 O registrador do timer é configurado como um registrador de 13 bits. Quando há estouro é

setado o flag de interrupção TFx do registrador TCON. A entrada contadora está habilitada quando TRx = 1, GATE = 0 ou INTx = 1 (colocando GATE = 1 permite que o timer seja controlado pelo pino de entrada de interrupção externa, facilitando a medida de larguras de pulso). TRx e TFx são bits de controle do registrador TCON. GATE é um bit do TMOD. O registrador de 13 bits consiste de todos os 8 bits de THx e os 5 bits menos significativos de

TLx.

Figura 22-22 – Temporizador/Contador 0 no modo 0: Contador de 13 bits

Page 459: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

437437437437

CCCC

TTiimmeerr 00 ee 11 -- MMooddoo 11 O modo 1 é parecido ao modo 0, exceto que o registrador do timer utiliza todos os

16 bits. Neste modo não existe prescaler.

Figura 22-23 – Temporizador/Contador 0 no modo 1

TTiimmeerr 00 ee 11 -- MMooddoo 22 Este modo configura o registrador do timer como um contador de 8 bits com

recarga automática. O estouro em TLx não somente seta TFx, mas também recarrega TLx com o conteúdo de THx.

Figura 22-24 – Temporizador/Contador 1 no modo 2: Contador de 8 bits com auto-recarga

TTiimmeerr 00 ee 11-- MMooddoo 33 O Timer 1 simplesmente armazena a contagem. O efeito é o mesmo que setar TR1 = 0. O Timer 0 funciona como dois contadores independentes de 8 bits (TL0 e TH0). TL0 utiliza

os bits de controle do timer 0: C/T#, GATE, TR0, INT0#, e TF0. TH0 é configurado como

Page 460: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

438438438438

CCCC

timer (contador de ciclos de máquina) e utiliza os bits TR1 e TF1 do timer 1. Desta maneira TH0 controla a interrupção do Timer 1. O modo 3 é indicado em aplicações que requerem um timer ou counter extra de 8 bits. Enquanto o timer 0 estiver no modo 3, o timer 1 pode ser ligado ou desligado colocando-o no

modo 3 também, ou ser utilizado pela porta serial como gerador de baudrate, ou em alguma aplicação que não requeira de interrupção.

Figura 22-25 – Temporizador/Contador 0 no modo 3: Dois contadores de 8 bits

RReeggiissttrraaddoorreess AAssssoocciiaaddooss aaoo TTiimmeerr//CCoouunntteerr

Figura 22-26 – Registrador TMOD1

1 Timer/Counter Mode Control Register

Page 461: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

439439439439

CCCC

Figura 22-27 – Registrador TCON2

Figura 22-28 – Registrador IE3

2 Timer/Counter Control Register 3 Interrupt Enable Register

Page 462: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

440440440440

CCCC

TTiimmeerr 22 -- CCaarraacctteerrííssttiiccaass É um Timer/Counter de 16 bits que pode operar como um timer ou como um contador de

eventos. O tipo de operação é selecionado pelo bit C/T2 em T2CON. O timer 2 tem três modos de operação: Captura Auto-reload Gerador de baudrate

O modo de operação é selecionado pelos bits no registrador T2CON. O Timer 2 consiste de dois registradores de 8 bits, TH2 e TL2. Na função de timer, o registrador TL2 é incrementado a cada ciclo de máquina ou 1/12 da

freqüência do oscilador.

EExxeerrccíícciioo 44 -- OOsscciillaaddoorr uuttiilliizzaannddoo oo TTiimmeerr 00 Faça um programa que implemente um gerador de onda quadrada de 40 ms de período no

pino P1.0 utilizando o timer0 (20 ms para cada estado). Simular e depois verificar o funcionamento. Clock: 20 MHz - Clock/12: 1.6667 MHz Cada contagem: 1/1.67MHz = 600 ns Precisamos de 20 ms: 33333 contagens do timer (33333.333).

/*/ Opcoes para el compilador y el linker Franklin C //*/ #pragma DEBUG SYMBOLS OBJECTEXTEND CODE /*/ O Linker deve ter as seguintes opcoes para simular no JSim $(FileName).obj to $(FileName).abs DEBUGLINES DEBUGPUBLICS DEBUGSYMBOLS ramsize(256) idata(80H) */ void init_timer0(void); sfr P1 = 0x90; sbit bOnda = P1^0; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A; sfr TH0 = 0x8C; sbit TR0 = TCON^4; void main (void) unsigned long int lnAux = 0; bit bAux = 0; init_timer0(); while(1) lnAux = ((unsigned long int)(TH0)<<8) + (unsigned long int)(TL0); if(lnAux >= 33333) bAux = !bAux; bOnda = bAux; TH0 = 0; TL0 = 0; void init_timer0(void) TMOD &= 0xF0; // limpa o Timer 0 TMOD |= 0x01; TL0 = 0x00; // valor setado pelo usuario TH0 = 0x00; // valor setado pelo usuario TR0 = 1; // TCON.4 dispara o timer

Código 22-3 - Oscilador utilizando o Timer 0

Page 463: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

441441441441

CCCC

Figura 22-29 - Oscilador utilizando o Timer 0

EExxeerrccíícciioo 55 -- OOsscciillaaddoorr uuttiilliizzaannddoo aa iinntteerrrruuppççããoo ddoo TTiimmeerr 00

Faça um programa que implemente um gerador de onda quadrada de 800 ms de período no pino P1.0 (400 ms cada estado), utilizando a interrupção do timer 0. Simular e depois verificar o funcionamento. Clock: 20 MHz - Clock/12: 1.6667 MHz Cada contagem: 1/1.67MHz = 600 ns A interrupção acontece automaticamente a cada 65536 contagens, ou 39.3 ms. Precisamos de aproximadamente 10 (10.18) interrupções para completar 400 ms.

EEXX55..HH /*/ Opcoes para el compilador y el linker Franklin C */ #pragma DEBUG SYMBOLS OBJECTEXTEND CODE /*/ O Linker deve ter as seguintes opcoes para simular no JSim */ /*/ $(FileName).obj to $(FileName).abs DEBUGLINES DEBUGPUBLICS DEBUGSYMBOLS ramsize(256) idata(80H)*/ sfr P1 = 0x90; sbit bOnda = P1^0; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A; sfr TH0 = 0x8C; sbit TR0 = TCON^4; sfr IE = 0xA8; sbit ET0 = IE^1; sfr IP = 0xB8; sfr IPH = 0xB7;

Código 22-4 – EX5.H

Page 464: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

442442442442

CCCC

EEXX55..CC #include <ex5.h> void init_timer0(void); void init_special_interrupts(void); unsigned int wTimer0_ovs = 0; void main (void) bit bAux = 0; init_timer0(); init_special_interrupts(); while(1) if(wTimer0_ovs >= 10) bAux = !bAux; bOnda = bAux; wTimer0_ovs = 0; void init_timer0(void) TMOD &= 0xF0; // limpa o Timer 0 TMOD |= 0x01; TL0 = 0x00; // valor setado pelo usuario TH0 = 0x00; // valor setado pelo usuario ET0 = 1; // IE.1 TR0 = 1; // TCON.4 dispara o timer ET0 = 1; // IE.1 void Timer0_Interrupt () interrupt 1 // Para um clock de 20000000 ocorre uma interrupcao a cada 39.3 ms aproximadamente. wTimer0_ovs++; // conta mais um overflow void init_special_interrupts(void) IE=0x82; IP=0x00; IPH=0x00;

Código 22-5 - Oscilador utilizando a interrupção do Timer 0

Page 465: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

443443443443

CCCC

Figura 22-30 - Oscilador utilizando a interrupção do Timer 0 usando o JFE

UUSSAARRTT ((UUnniivveerrssaall SSyynncchhrroonnoouuss AAssyynncchhrroonnoouuss RReecceeiivveerr TTrraannssmmiitttteerr))

MMooddooss ddee OOppeerraaççããoo Full-duplex 1 modo de operação síncrona. 3 modos de operação assíncrona. Detecção de erros de frame. Reconhecimento automático de frame de endereço. Interrupções na transmissão e na recepção.

FFuunncciioonnaammeennttoo Permite transmitir e receber simultaneamente. Os dados para a transmissão são colocados no registrador SBUF. Na recepção o dado é armazenado no SBUF. O registro SBUF de transmissão é diferente ao

da recepção, mas são acessados no mesmo endereço. O controle e status do canal serial está no registrador SCON. Este registrador contém os bits

de seleção de modo (SM0 e SM1), o bit SM2 para o modo multiprocessador, o bit de habilitação de recepção REN, o nono bit de dados para transmissão e recepção (TB8 e RB8), e os bits de interrupção serial (TI e RI).

Page 466: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

444444444444

CCCC

RReeggiissttrraaddoorreess AAssssoocciiaaddooss

Figura 22-31 – Registrador SCON4

Figura 22-32 – Registrador SCON (cont.)

4 Serial Port Control Register

Page 467: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

445445445445

CCCC

UUSSAARRTT -- MMooddoo 00 O dado serial entra e sai através de RXD. TXD fornece o clock. São transmitidos/recebidos 8 bits (LSB primeiro). O baudrate é fixo na freqüência 1/12 do oscilador.

UUSSAARRTT -- MMooddoo 11 São transmitidos 10 bits (através de TXD) ou recebidos (através de RXD): um bit de start (0), 8

bits de dados (LSB primeiro) e um bit de stop (1). Na recepção, o bit de stop fica no RB8 do SCON. O baudrate é variável

UUSSAARRTT -- MMooddoo 22 São transmitidos ou recebidos 11 bits: um bit de start, 8 bits de dados, um nono bit

programável e um bit de stop. O baudrate é programável para 1/32 ou 1/64 da freqüência do oscilador.

UUSSAARRTT -- MMooddoo 33 Similar ao modo 1 mas com baudrate variável

Figura 22-33 – Forma de onda no canal serial do 8051

EExxeerrccíícciioo 66 -- TTrraannssmmiissssoorr sseerriiaall Faça um programa que transmita constantemente a letra ’Z’ de forma serial pelo pino TXD. Utilize o canal serial configurado para 19200 bauds, 8 bits de dados, 1 start bit, um stop bit e

sem bit de paridade. Para testar, utilize o osciloscópio e o software HyperTerminal do Windows.

RREEGG5522..HH #pragma DEBUG SYMBOLS OBJECTEXTEND CODE /* 8052 Processor Declarations */ /* BYTE Registers */ sfr P0 = 0x80; sfr P1 = 0x90; sfr P2 = 0xA0; sfr P3 = 0xB0; sfr PSW = 0xD0; sfr ACC = 0xE0; sfr B = 0xF0; sfr SP = 0x81; sfr DPL = 0x82; sfr DPH = 0x83; sfr PCON = 0x87; sfr TCON = 0x88; sfr TMOD = 0x89; sfr TL0 = 0x8A; sfr TL1 = 0x8B; sfr TH0 = 0x8C; sfr TH1 = 0x8D; sfr IE = 0xA8;

Page 468: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

446446446446

CCCC

sfr IPH = 0xB7; sfr IP = 0xB8; sfr SCON = 0x98; sfr SBUF = 0x99; /* 8052 Extensions */ sfr T2CON = 0xC8; sfr T2MOD = 0xC9; sfr RCAP2L = 0xCA; sfr RCAP2H = 0xCB; sfr TL2 = 0xCC; sfr TH2 = 0xCD; /* BIT Registers */ /* PSW */ sbit CY = 0xD7; sbit AC = 0xD6; sbit F0 = 0xD5; sbit RS1 = 0xD4; sbit RS0 = 0xD3; sbit OV = 0xD2; sbit P = 0xD0; /* TCON */ sbit TF1 = 0x8F; sbit TR1 = 0x8E; sbit TF0 = 0x8D; sbit TR0 = 0x8C; sbit IE1 = 0x8B; sbit IT1 = 0x8A; sbit IE0 = 0x89; sbit IT0 = 0x88; /* IE */ sbit EA = 0xAF; sbit ES = 0xAC; sbit ET1 = 0xAB; sbit EX1 = 0xAA; sbit ET0 = 0xA9; sbit EX0 = 0xA8; /* IP */ sbit PS = 0xBC; sbit PT1 = 0xBB; sbit PX1 = 0xBA; sbit PT0 = 0xB9; sbit PX0 = 0xB8; /* P3 */ sbit RD = 0xB7; sbit WR = 0xB6; sbit T1 = 0xB5; sbit T0 = 0xB4; sbit INT1 = 0xB3; sbit INT0 = 0xB2; sbit TXD = 0xB1; sbit RXD = 0xB0; /* SCON */ sbit SM0 = 0x9F; sbit SM1 = 0x9E; sbit SM2 = 0x9D; sbit REN = 0x9C; sbit TB8 = 0x9B; sbit RB8 = 0x9A; sbit TI = 0x99; sbit RI = 0x98; /* 8052 Extensions */ /* IE */ sbit ET2 = 0xAD; /* IP */ sbit PT2 = 0xBD; /* P1 */

Page 469: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

447447447447

CCCC

sbit T2EX = 0x91; sbit T2 = 0x90; /* T2CON */ sbit TF2 = 0xCF; sbit T2IP = 0xCE; sbit T2IE = 0xCD; sbit T2RSE = 0xCC; sbit BGEN = 0xCB; sbit TR2 = 0xCA; sbit C_T2 = 0xC9; sbit CP_RL2= 0xC8; // Puerto 0 sbit P0res0 = P0^0; /* reserva */ sbit P0res1 = P0^1; /* reserva */ sbit P0res2 = P0^2; /* reserva */ sbit P0res3 = P0^3; /* reserva */ sbit P0res4 = P0^4; /* reserva */ sbit P0res5 = P0^5; /* reserva */ sbit P0res6 = P0^6; /* reserva */ sbit P0res7 = P0^7; /* reserva */ // Puerto 1 sbit P1res0 = P1^0; /* reserva */ sbit P1res1 = P1^1; /* reserva */ sbit P1res2 = P1^2; /* reserva */ sbit P1res3 = P1^3; /* reserva */ sbit P1res4 = P1^4; /* reserva */ sbit P1res5 = P1^5; /* reserva */ sbit P1res6 = P1^6; /* reserva */ sbit P1res7 = P1^7; /* reserva */ // Puerto 2 sbit P2res0 = P2^0; /* reserva */ sbit P2res1 = P2^1; /* reserva */ sbit P2res2 = P2^2; /* reserva */ sbit P2res3 = P2^3; /* reserva */ sbit P2res4 = P2^4; /* reserva */ sbit P2res5 = P2^5; /* reserva */ sbit P2res6 = P2^6; /* reserva */ sbit P2res7 = P2^7; /* reserva */ // Puerto 3 sbit bRx = P3^0; /* Serial Rx */ sbit bTx = P3^1; /* Serial TX */ sbit bINT0 = P3^2; /* Pedido de interrupción 0 */ sbit bINT1 = P3^3; /* Pedido de interrupción 1 */ sbit P3res4 = P3^4; /* reserva */ sbit P3res5 = P3^5; /* reserva */ sbit P3res6 = P3^6; /* reserva */ sbit P3res7 = P3^7; /* reserva */ #define TRUE 1 #define FALSE 0 #define true TRUE #define false FALSE

Código 22-6 – REG52.H

EEXX66..CC #include <reg52.h> void init_serial(void); void init_special_interrupts(void); void init_timer2(void); bit bTxEmpty = true; void main (void) init_special_interrupts(); init_timer2(); init_serial(); while(1) if(bTxEmpty) bTxEmpty = false;

Page 470: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

448448448448

CCCC

SBUF = 'Z'; void init_serial(void) SCON = 0x40; /*Timer 2 is being used to generate baud rates.*/ RCAP2L = 0xF5; RCAP2H = 0xFF; T2CON = 0x34; ES = 1; //IE.4 SBUF = 0x00; TI = 0; //SCON.1 void Serial_Interrupt () interrupt 4 if(TI) TI = false; bTxEmpty = true; void init_special_interrupts(void) IE=0x90; IP=0x00; IPH=0x00; void init_timer2(void) T2CON = 0x34; T2MOD = 0x00; TR2 = 1; // T2CON.2 start timer

Código 22-7 - Transmissor serial

EExxeerrccíícciioo 77 -- TTrraannssmmiissssoorr sseerriiaall 22 Repita o Exercício 7 utilizando: printf(“Z”); Para testar, utilize o osciloscópio e o software HyperTerminal do Windows.

RREEGG5522..HH55

EEXX77..CC #include <reg52.h> #include "e:\c51\inc\stdio.h" void init_serial(void); void init_timer2(void); void main (void) init_serial(); init_timer2(); while(1) printf("Z"); void init_serial(void) SCON = 0x40; /*Timer 2 is being used to generate baud rates.*/ RCAP2L = 0xF5; RCAP2H = 0xFF; T2CON = 0x34; // ES = 1; //IE.4 SBUF = 0x00; TI = 0; //SCON.1 void init_timer2(void) T2CON = 0x34;

5 Ver problema anterior

Page 471: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

449449449449

CCCC

T2MOD = 0x00; TR2 = 1; // T2CON.2 start timer

Código 22-8 - Transmissor serial 2

Figura 22-34 - Transmissor serial

EExxeerrccíícciioo 88 -- RReecceeppttoorr sseerriiaall Faça um programa que transmita pelo pino TXD o seu nome completo, caso seja recebido no

pino RXD o caractere ‘n’. Caso o caractere seja diferente de n, envie-lo de volta pelo pino TXD. Utilize o canal serial configurado para 19200 bps, 8 bits de dados, 1 start bit, um stop bit e sem

bit de paridade, utilizando interrupção da recepção. Para testar, utilize o osciloscópio e o software HyperTerminal do Windows.

RREEGG5522..HH66

EEXX88..CC #include <reg52.h> #include "e:\c51\inc\stdio.h" void init_serial(void); void init_special_interrupts(void); void init_timer2(void); bit bByteReceived = false; char cRec; void main (void) init_special_interrupts();

6 Ver problema anterior

Page 472: Tratado Da Linguagem c

M I C R O C O N T R O L A D O R E S D A F A M Í L I A I N T E L 8 0 5 1

450450450450

CCCC

init_timer2(); init_serial(); while(1) if(bByteReceived) bByteReceived = 0; if(cRec == 'n') printf("Luis Fernando"); else printf("%c",cRec); void init_serial(void) SCON = 0x50; /*Timer 2 is being used to generate baud rates.*/ RCAP2L = 0xF5; RCAP2H = 0xFF; T2CON = 0x34; ES = 1; //IE.4 SBUF = 0x00; TI = 0; //SCON.1 RI = 0; //SCON.0 void Serial_Interrupt () interrupt 4 if(RI) RI = 0; bByteReceived = true; cRec = SBUF; void init_special_interrupts(void) IE=0x90; IP=0x00; IPH=0x00; void init_timer2(void) T2CON = 0x34; T2MOD = 0x00; TR2 = 1; // T2CON.2 start timer

Código 22-9 - Receptor serial

EExxeerrccíícciioo 99 –– CCoommuunniiccaaççããoo SSeerriiaall Faça um programa que leia ou escreva na porta 1 quando requisitado por um PC seguindo o

seguinte protocolo: (operação)(porta).(bit)=(valor se for escrita) Ex: Ler o bit 2 da porta 1 => L1.2 Escrever 1 no bit 5 da porta 1 => E1.5=1 Caso um dos caracteres seja diferente de uma das combinações possíveis envie “Erro, tente

novamente”

Page 473: Tratado Da Linguagem c

451451451451 Luis Fernando Espinosa Cocian

AAppêênnddiiccee DD -- AAss PPoorrttaass ddee CCoommuunniiccaaççããoo nnoo SSiisstteemmaa MMSS WWiinnddoowwss

O sistema Windows é um sistema operacional multitarefa, que permite a execução de vários programas de forma simultânea, mantendo o controle dos recursos do computador, tais como portas e memórias. Para possibilitar o acesso aos recursos compartilhados, a Microsoft ® fornece os recursos de software para o acesso controlado ao hardware através de uma interface de programação denominada API1. A seguir é descrito o processo de acesso utilizando as funções da API relativas ao acesso às portas seriais e paralelas.

OOss RReeccuurrssooss ddee CCoommuunniiccaaççããoo nnoo WWiinnddoowwss®®

O sistema operacional Windows trata as portas e arquivos como recursos. Um recurso de comunicação é um dispositivo físico ou lógico que fornece um fluxo de dados bidirecional assíncrono. Alguns exemplos de recursos de comunicação são as portas seriais, paralelas, máquinas de fax e modems. Para cada recurso de comunicação, existe um provedor de serviço, que consiste de uma biblioteca ou driver, que habilita a aplicação a acessar tal recurso.

Para prover a interface básica que possibilite abrir e fechar um manipulador de recursos de comunicação e para efetuar as operações de leitura e escrita num dispositivo, são utilizadas as funções básicas de entrada e saída (I/O) tais como: CreateFile, CloseHandle, ReadFile, ReadFileEx, WriteFile, e WriteFileEx. As portas são tratadas como arquivos de destino e origem dos dados.

A API Microsoft® Win32® também inclui um conjunto de funções que fornece o acesso aos recursos de comunicação. Esta seção descreve brevemente o uso de I/O de arquivos e as funções de comunicação que habilitam as aplicações a efetuar as seguintes tarefas: Abrir um manipulador (handle) para um recurso específico de comunicação. Setar e requisitar a configuração de um recurso de comunicação serial. Ler ou escrever num recurso de comunicação serial. Monitorar um conjunto específico de eventos que possam ocorrer num recurso de

comunicação serial. Enviar um comando de controle ao driver do dispositivo associado com o recurso específico

de comunicação, forçando ao driver a executar uma função estendida.

1 API: Application Program Interface

Apêndice

D

Page 474: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

452452452452

DDDD

MMaanniippuullaaddoorreess ddee RReeccuurrssooss ddee CCoommuunniiccaaççããoo

Um processo utiliza a função CreateFile para abrir um manipulador (handle) para um recurso de comunicação. Por exemplo, especificando COM1 pode-se abrir um manipulador para a porta serial, e LPT1 pode-se abrir um handle para a porta paralela. Se o recurso especificado estiver sendo utilizado por um outro processo, a função CreateFile falhará. Qualquer subprocesso do aplicativo poderá utilizar o handle retornado por CreateFile para identificar o recurso em qualquer função que acesse o mesmo. A declaração da função CreateFile pode ser vista a seguir. HANDLE CreateFile(

LPCTSTR lpFileName, // ponteiro para o nome do recurso (porta ou arquivo) DWORD dwDesiredAccess, // modo de acesso (leitura-escrita) DWORD dwShareMode, // modo de compartilhamento LPSECURITY_ATTRIBUTES lpSecurityAttributes, // ponteiro para os atributos de //segurança

DWORD dwCreationDistribution, // modo de criaçao DWORD dwFlagsAndAttributes, // atributos do arquivo (ou porta) HANDLE hTemplateFile // manipulador para o arquivo com os

//atributos de copia );

Quando for utilizada CreateFile para abrir um handle para um dispositivo devem ser utilizados caracteres especiais " \\ .\" para identificar o dispositivo. Por exemplo, para abrir um handle para o drive A, deve-se especificar " \\ .\a:" para o parâmetro lpszName de CreateFile. O processo chamador poderá utilizar o handle na função DeviceIoControl para enviar os códigos de controle ao dispositivo. A declaração da função DeviceIoControl pode ser vista a seguir. BOOL DeviceIoControl( HANDLE hDevice, // handle para o dispositivo de interesse DWORD dwIoControlCode, // código da operaçao de controle a ser executada LPVOID lpInBuffer, // ponteiro para o buffer que fornece os dados de entrada DWORD nInBufferSize, // tamanho do buffer de entrada LPVOID lpOutBuffer, // ponteiro para o buffer receptor dos dados de saída DWORD nOutBufferSize, // tamanho do buffer de saída LPDWORD lpBytesReturned, // ponteiro para a variável receptora da contagem de //bytes de saída LPOVERLAPPED lpOverlapped // ponteiro para a estrutura para operaçoes assíncronas );

Quando o processo chama a função CreateFile para abrir um recurso de comunicação, deverá especificar os seguintes atributos: Qual tipo de acesso de leitura-escrita existirá para o recurso especificado. Se o handle pode ser herdado por um processo filho (C++). Se o handle pode ser usado em operações de I/O sobrepostas (assíncronas).

Quando o processo chama a função CreateFile para abrir um recurso de comunicação, este deverá especificar certos valores para os seguintes parâmetros: O parâmetro dwShareMode deverá ser zero, abrindo o recurso para acesso exclusivo. O parâmetro dwCreate deverá especificar o flag OPEN_EXISTING. O parâmetro hTemplateFile deverá ser NULL.

MMooddiiffiiccaaççõõeess ddooss PPaarrââmmeettrrooss ddooss RReeccuurrssooss ddee CCoommuunniiccaaççããoo

Quando a função CreateFile abrir um handle para um recurso de comunicação serial, o sistema inicializará e configurará o recurso de acordo com os valores selecionados na última vez que o recurso foi aberto. A preservação dos ajustes prévios permite ao usuário de reter estes ajustes especificados através dos comandos no DOS quando o

Page 475: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

453453453453

DDDD

dispositivo for reaberto. Os valores herdados de uma operação de abertura prévia incluem os parâmetros de configuração definidos na estrutura DCB2 e os valores de timeout utilizados nas operações de I/O. Se o dispositivo nunca foi aberto, este será configurado com as opções default.

Para determinar a configuração inicial de um recurso de comunicação serial, pode ser utilizada uma função denominada GetCommState, que retorna uma estrutura DCB com a configuração corrente da porta. A seguir é mostrada a declaração da função GetCommState. BOOL GetCommState( HANDLE hFile, // handle do dispositivo de comunicação LPDCB lpDCB // endereço da estrutura de controle de dispositivo );

Os membros da estrutura DCB especificam os parâmetros de configuração tais como o baudrate, o número de bits de dados por byte e o número de bits de stop. Outros membros especificam os caracteres especiais e habilitam o teste de paridade e o controle de fluxo. Quando um processo precisar modificar somente algum dos parâmetros, pode primeiro ser chamada a função GetCommState para preencher a estrutura DCB com a configuração corrente e então ajustar os valores importantes a serem reconfigurados. Finalmente, pode ser utilizada a função SetCommState para modificar os parâmetros da porta.. Este procedimento assegura que os membros não modificados da estrutura DCB contenham os valores apropriados. Por exemplo, um erro comum é o de configura o dispositivo com a estrutura DCB na qual o membro XonChar é igual a XoffChar. Alguns membros da estrutura DCB são diferentes daquelas das versões previas do Microsoft Windows®. Por exemplo, os flags para o controle do fluxo tais como do RTS (request-to-send) e DTR (data-terminal-ready) mudaram. A seguir é mostrada a declaração da função SetCommState. BOOL SetCommState( HANDLE hFile, // handle do dispositivo de comunicação LPDCB lpDCB // endereço da estrutura de controle de dispositivo );

A função BuildCommDCB fornece outra forma de modificar a estrutura DCB. A função BuildCommDCB utiliza uma string com a mesma forma dos argumentos de comandos de linha do modo de comando para especificar o baudrate, a paridade, o número de stop bits e o número de bits de dados. Os membros restantes da estrutura DCB não serão mudados por esta função, exceto aqueles relacionados com o controle de fluxo de hardware, XON/XOFF. A função BuildCommDCB somente modifica a estrutura DCB sem reconfigurar o dispositivo. A seguir é mostrada a declaração da função BuildCommDCB. BOOL BuildCommDCB( LPCTSTR lpDef, // ponteiro para a string de controle de dispositivo LPDCB lpDCB // ponteiro para o bloco de controle de dispositivo );

Um aplicativo pode reconfigurar um recurso de comunicação utilizando a função GetCommProperties para ler a informação de um driver de dispositivo, no que se refere aos ajustes de configuração que este suporta. O aplicativo poderá utilizar esta informação para evitar uma especificação que não seja suportada pelo sistema. A seguir é mostrada a declaração da função GetCommProperties. BOOL GetCommProperties( HANDLE hFile, // handle do dispositivo de comunicação LPCOMMPROP lpCommProp // endereço da estrutura das propriedades de comunicação );

2 DCB: Device Control Block.

Page 476: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

454454454454

DDDD

A função SetCommState reconfigura um recurso de comunicação, mas não afeta os buffers internos de saída e entrada de um determinado driver. Os buffers não serão esvaziados, sendo que as leituras e escritas pendentes não serão interrompidas de forma prematura.

Um aplicativo poderá reinicializar um recurso de comunicação utilizando a função SetupComm, a qual efetua as seguintes tarefas: Termina as operações pendentes de leitura e escrita, mesmo que estas não tenham sido

completadas. Descarta os caracteres não lidos e esvazia os buffers de entrada e saída do driver associado com

o recurso especificado. Realoca os buffers internos de entrada e saída.

Um determinado aplicativo não precisa necessariamente chamar a função SetupComm. Caso o aplicativo não a utilize, o driver do recurso inicializará o dispositivo com os parâmetros default na primeira vez que o handle do recurso é utilizado. A seguir é mostrada a declaração da função SetupComm. BOOL SetupComm(

HANDLE hFile, // handle do dispositivo de comunicação DWORD dwInQueue, // tamanho do buffer de entrada DWORD dwOutQueue // tamanho do buffer de saída );

UUttiilliizzaannddoo aass FFuunnççõõeess ddee CCoommuunniiccaaççããoo

CCoonnffiigguurraannddoo uumm RReeccuurrssoo ddee CCoommuunniiccaaççaaoo

O seguinte exemplo abre um handle para a COM1 e preenche a estrutura DCB com a configuração corrente. A estrutura DCB é então modificada e utilizada para reconfigurar o dispositivo. DCB dcb; HANDLE hCom; DWORD dwError; BOOL fSuccess; hCom = CreateFile("COM1",

GENERIC_READ | GENERIC_WRITE, 0, /* dispositivos comm devem ser abertos com acesso exclusivo */ NULL, /* sem atributos de segurança */ OPEN_EXISTING, /* dispositivos de comunicação devem usar OPEN_EXISTING */ 0, /* I/O sem sobreposição */ NULL /* hTemplate deve ser NULL para dispositivos de comunicação */ );

if (hCom == INVALID_HANDLE_VALUE) dwError = GetLastError(); /* erro de handle */ /* * Omite a chamada de SetupComm para utilizar o tamanho padrão da queue. * Lê a configuração corrente. */ fSuccess = GetCommState(hCom, &dcb); if (!fSuccess) /* Trata o erro. */ /* Preenche DCB: baudrate=9600, 8 bits de dados, sem paridade, 1 bit de parada. */

Page 477: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

455455455455

DDDD

dcb.BaudRate = 9600; dcb.ByteSize = 8; dcb.Parity = NOPARITY; dcb.StopBits = ONESTOPBIT; fSuccess = SetCommState(hCom, &dcb); if (!fSuccess) /* Trata o erro. */

MMoonniittoorraaççããoo ddee EEvveennttooss ddee CCoommuunniiccaaççããoo

O código que segue abre uma porta serial para I/O sobreposto, cria uma mascara de eventos para monitorar os sinais CTS e DSR, e então espera que um evento chegue a ocorrer.

A função WaitCommEvent espera pelo acontecimento de algum evento num determinado dispositivo de comunicação. O conjunto de eventos que são monitorados por esta função está definido na máscara de eventos associada ao handle de dispositivo. A declaração da função WaitCommEvent é mostrada a seguir. BOOL WaitCommEvent( HANDLE hFile, // handle do dispositivo de comunicação LPDWORD lpEvtMask, // endereço da variável para o evento ocorrido LPOVERLAPPED lpOverlapped, // endereço da estrutura de sobreposição );

A função WaitCommEvent deverá ser executada como uma operação sobreposta de forma que outras tarefas do processo não possam executar operações de I/O durante a espera. HANDLE hCom; OVERLAPPED o; BOOL fSuccess; DWORD dwEvtMask; hCom = CreateFile(

"COM1", GENERIC_READ | GENERIC_WRITE, 0, /* acesso exclusivo */ NULL, /* sem atributos de segurança */ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL );

if (hCom == INVALID_HANDLE_VALUE) /* Trata o erro. */ /* Seta a mascara de eventos. */ fSuccess = SetCommMask(hCom, EV_CTS | EV_DSR); if (!fSuccess) /* trata o erro */ /* Cria um objeto de eventos para utilizar em WaitCommEvent. */ o.hEvent = CreateEvent(NULL, /* sem atributos de segurança */ FALSE, /* evento de auto reset */ FALSE, /* não sinalizado */ NULL /* sem nome */ ); assert(o.hEvent); if (WaitCommEvent(hCom, &dwEvtMask, &o))

if (dwEvtMask & EV_DSR)

Page 478: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

456456456456

DDDD

/* * . . . */

if (dwEvtMask & EV_CTS) /* * . . . */

AA EEssttrruuttuurraa DDCCBB

A estrutura DCB define os ajustes de controle para um dispositivo de comunicação serial. typedef struct _DCB // dcb DWORD DCBlength; // sizeof(DCB) DWORD BaudRate; // baudrate DWORD fBinary: 1; // modo binário, sem teste de EOF DWORD fParity: 1; // habilita o teste de paridade DWORD fOutxCtsFlow:1; // Controle de fluxo de saída CTS DWORD fOutxDsrFlow:1; // controle de fluxo DSR DWORD fDtrControl:2; // tipo de controle DTR DWORD fDsrSensitivity:1; // sensibilidade DSR DWORD fTXContinueOnXoff:1; // XOFF segue Tx DWORD fOutX: 1; // XON/XOFF out flow control DWORD fInX: 1; // XON/XOFF in flow control DWORD fErrorChar: 1; // habilita substituição de erro DWORD fNull: 1; // habilita esvaziamento de null DWORD fRtsControl:2; // controle de fluxo RTS DWORD fAbortOnError:1; // aborta as leituras e escritas com erros DWORD fDummy2:17; // reservado WORD wReserved; // não utilizado - reservado WORD XonLim; // limite XON de transmissão WORD XoffLim; // limite de XOFF de transmissão BYTE ByteSize; // número de bits por byte, 4-8 BYTE Parity; // 0-4=no,odd,even,mark,space - bit de paridade BYTE StopBits; // Bits de Stop - 0,1,2 = 1, 1.5, 2 char XonChar; // caractere Tx e Rx para XON char XoffChar; // caractere Tx e Rx para XOFF char ErrorChar; // caractere de substituição de erro char EofChar; // caractere de final de entrada char EvtChar; // caractere de recepção de evento WORD wReserved1; // reservado - não utilizar DCB;

DDeessccrriiççããoo ddooss MMeemmbbrrooss ddee DDCCBB

DCBlength

Especifica o comprimento, em bytes, da estrutura DCB

BaudRate

Especifica o baudrate no qual o dispositivo de comunicação irá operar. Este membro pode armazenar diretamente o valor atual do baudrate, ou um.dos seguintes índices:

CBR_110 CBR_4800 CBR_56000 CBR_300 CBR_9600 CBR_57600 CBR_600 CBR_14400 CBR_115200 CBR_1200 CBR_19200 CBR_128000 CBR_2400 CBR_38400 CBR_256000

Page 479: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

457457457457

DDDD

Tabela 23-1 – Valores para o parâmetro BaudRate

fBinary

Especifica se o modo binário está habilitado. A API Win32 não suporta modos de transferência não binários, de forma que este membro deve ser sempre TRUE. A tentativa de colocar o seu valor me FALSE não irá mudar o funcionamento.

No Windows 3.1, se este membro for FALSE, será habilitado o modo não-binário, e o caractere especificado no membro EofChar será reconhecido na entrada e entendido como indicando o final dos dados.

fParity

Especifica se está habilitado o teste de bit de paridade. Se este membro for TRUE, será efetuado o teste e os erros que acontecerem serão informados.

fOutxCtsFlow

Especifica se o sinal CTS (clear-to-send) está sendo monitorado pelo controle de fluxo de saída. Se este membro for TRUE e o CTS estiver desligado, a saída ficará suspensa até que o sinal CTS seja enviado novamente.

fOutxDsrFlow

Especifica se o sinal DSR (data-set-ready) está sendo monitorado pelo controle de fluxo de saída. Se este membro for TRUE e o DSR estiver desligado, a saída ficará suspensa até que o sinal DSR seja enviado novamente.

fDtrControl

Especifica o controle de fluxo DTR (data-terminal-ready). Este membro poderá ter um dos seguintes valores: Valor Significado DTR_CONTROL_DISABLE Desabilita a linha DTR quando o dispositivo está aberto,

deixando-o desabilitado DTR_CONTROL_ENABLE Habilita a linha DTR quando o dispositivo é aberto,

permanecendo ligado posteriormente DTR_CONTROL_HANDSHAKE Habilita o handshaking do DTR.

Tabela 23-2 – Valores para o parâmetro fDtrControl

fDsrSensitivity

Especifica se o driver de comunicação é sensível ao estado do sinal DSR. Se este membro for TRUE, o driver ignorará qualquer byte recebido, a menos que a linha de entrada DSR do modem estiver ativa.

fTXContinueOnXoff

Especifica se a transmissão irá parar quando o buffer de entrada estiver cheio e o driver terá que transmitir o caractere XoffChar. Se este membro for TRUE, a transmissão continua depois que o buffer de entrada esteja a XoffLim bytes de ficar cheio e que o

Page 480: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

458458458458

DDDD

driver tenha transmitido o caractere XoffChar para parar de receber outros bytes. Se o membro for FALSE, a transmissão não irá continuar ate que o buffer de entrada esteja a XonLim bytes de ficar vazio e que o driver tenha transmitido o caractere XonChar para continuar com a recepção.

fOutX

Especifica se o controle de fluxo XON/XOFF está sendo utilizado durante a transmissão. Se o membro tiver valor TRUE, a transmissão parará quando seja recebido o caractere XoffChar e recomeçará quando for recebido o caractere XonChar.

fInX

Especifica se o controle de fluxo XON/XOFF está sendo utilizado durante a recepção. Se o membro tiver valor TRUE, será enviado o caractere XoffChar quando o buffer de entrada estiver a XoffLim bytes de ficar cheio, e o caractere XonChar será enviado quando o buffer de entrada estiver a XonLim bytes de ficar vazio.

fErrorChar

Especifica se os bytes recebidos com erros de paridade serão substituídos pelo caractere especificado no membro ErrorChar. Se este membro e o iParity forem TRUE, ocorrerá a substituição.

fNull

Especifica se os caracteres nulos serão descartados. Se este membro for TRUE, todos os bytes null serão descartados quando recebidos.

fRtsControl

Especifica o controle de fluxo RTS (request-to-send). Se o seu valor for zero, o default é RTS_CONTROL_HANDSHAKE. Este membro poderá ter um dos seguintes valores: Valor Significado RTS_CONTROL_DISABLE Desabilita a linha de RTS quando o dispositivo estiver aberto,

mantendo-a desabilitada. RTS_CONTROL_ENABLE Habilita a linha RTS quando o dispositivo for aberto,

deixando-a habilitada RTS_CONTROL_HANDSHAKE Habilita o handshaking do RTS. O driver levanta a linha RTS

quando o buffer de entrada estiver preenchido menos que a metade, e abaixa a linha RTS quando o buffer estiver preenchido na sua três quarta parte. Se o handshaking estiver habilitado, estará errado se o aplicativo tentar ajustar a linha utilizando a função EscapeCommFunction.

RTS_CONTROL_TOGGLE Especifica que a linha RTS estará ativa se houverem bytes disponíveis a serem transmitidos. Após que todos os bytes armazenados forem enviados, a linha RTS ficará inativa.

Tabela 23-3 – Valores para o parâmetro fRtsControl

fAbortOnError

Especifica se as operações de leitura e escrita estão finalizadas no caso de acontecer algum erro. Se este membro for TRUE, o driver termina todas as operações de leitura e

Page 481: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

459459459459

DDDD

escrita com um status de erro, caso tenha acontecido um erro. O driver não aceitará qualquer operação subseqüente até que o aplicativo tenha reconhecido o erro pela chamada da função ClearCommError.

fDummy2

Reservado. Não utilizar.

wReserved

Não utilizado, dever ser setado para zero.

XonLim

Especifica o numero mínimo de bytes permitidos no buffer de entrada antes de que seja enviado o caractere XON.

XoffLim

Especifica o numero mínimo de bytes permitidos no buffer de entrada antes de que seja enviado o caractere XOFF. O numero máximo de bytes permitido é calculado subtraindo este valor do tamanho do buffer de entrada em bytes.

ByteSize

Especifica o numero de bits nos bytes transmitidos e recebidos.

Parity

Especifica o esquema de paridade a ser utilizado. Este membro pode ter um dos seguintes valores:

Valor Significado EVENPARITY Even (par) MARKPARITY Mark NOPARITY No parity (Sem paridade) ODDPARITY Odd (ímpar)

Tabela 23-4 – Valores para o parâmetro Parity

StopBits

Especifica o numero de bits de parada a ser utilizado. Este membro poderá ter um dos seguintes valores:

Valor Significado ONESTOPBIT 1 stop bit ONE5STOPBITS 1.5 stop bits TWOSTOPBITS 2 stop bits

Tabela 23-5 – Valores para o parâmetro StopBits

XonChar

Especifica o valor do caractere XON para a transmissão e a recepção.

Page 482: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

460460460460

DDDD

XoffChar

Especifica o valor do caractere XOFF para a transmissão e a recepção.

ErrorChar

Especifica o valor do caractere a ser utilizado para substituir os bytes recebidos com erro de paridade.

EofChar

Especifica o valor do caractere a ser utilizado como sinal de finalização de dados.

EvtChar

Especifica o valor do caractere a ser utilizado para sinalizar um evento.

wReserved1

Reservado. Não utilizar.

OObbsseerrvvaaççõõeess

Quando for utilizada a estrutura DCB para configura um chip 8250, são aplicadas as seguintes restrições aos valores especificados para os membros ByteSize e StopBit: O numero de bits de dados deverá ser de 5 até 8. O uso de 5 bits de dados com 2 bits de parada será uma combinação invalida, assim como 6, 7,

ou 8 bits de dados com 1.5 bits de parada.

CCoonnffiigguurraaççããoo ddooss RReeccuurrssooss ddee CCoommuunniiccaaççããoo

A estrutura COMMCONFIG define a configuração do recurso de comunicação, seja serial ou qualquer outro. O formato da estrutura varia dependendo do tipo de recurso de comunicação (ou subtipo). Os primeiros poucos membros da estrutura são comuns a todos os recursos de comunicação, sendo que os membros adicionais são definidos para cada subtipo especifico.

Um aplicativo poderá ler ou modificar a configuração de um recurso de comunicação, pelo uso das funções GetCommConfig e SetCommConfig. Quando for aberto um recurso de comunicação, este inicializará com a configuração padrão para o seu subtipo. Para ler ou modificar a configuração padrão de um subtipo, pode-se utilizar as funções GetDefaultCommConfig e SetDefaultCommConfig. Os protótipos das funções são mostrados a seguir. BOOL GetCommConfig( HANDLE hCommDev, // handle do dispositivo de comunicação LPCOMMCONFIG lpCC, // endereço da estrutura de configuraçao LPDWORD lpdwSize // endereço do tamanho do buffer );

Código 23-1 - GetCommConfig

BOOL SetCommConfig( HANDLE hCommDev, // handle do dispositivo de comunicação LPCOMMCONFIG lpCC, // endereço dos serviçoes de configuraçao

Page 483: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

461461461461

DDDD

DWORD dwSize // tamanho da estrutura ); BOOL GetDefaultCommConfig( LPCSTR lpszName, LPCOMMCONFIG lpCC, LPDWORD lpdwSize );

Código 23-2 - GetDefaultCommConfig

BOOL SetDefaultCommConfig( LPCSTR lpszName, LPCOMMCONFIG lpCC, DWORD dwSize );

Código 23-3 - SetDefaultCommConfig

Para permitir a entrada de dados de configuração por parte do usuário, pode ser utilizada a função CommConfigDialog. Esta função mostra uma janela de dialogo definida pelo provedor do serviço e preenche a estrutura COMMCONFIG baseada na entrada do usuário. A seguir é mostrada a declaração da função CommConfigDialog e da estrutura COMMCONFIG. BOOL CommConfigDialog( LPTSTR lpszName, // ponteiro para a string do nome do dispositivo HWND hWnd, // handle para a janela LPCOMMCONFIG lpCC // pontirio para a estrutura de configuraçao da porta );

Código 23-4 - CommConfigDialog

typedef struct _COMM_CONFIG DWORD dwSize; WORD wVersion; WORD wReserved; DCB dcb; DWORD dwProviderSubType; DWORD dwProviderOffset; DWORD dwProviderSize; WCHAR wcProviderData[1]; COMMCONFIG, *LPCOMMCONFIG;

Código 23-5 - COMM_CONFIG

CCoonnffiigguurraaççããoo ddoo MMooddeemm

As funções de configuração do modem permitem ao programador configurar o mesmo antes de efetuar uma conexão. Um aplicativo pode ajustar as opções do modem e determinar as suas características sem utilizar comandos específicos de um dispositivo de modem. A seguir são colocados os parâmetros gerais que um aplicativo deve verificar antes de efetuar uma conexão: Modo primário de operação primário (síncrono, assíncrono e se o controle de erros está

habilitado). Controle de erro V.42 (definido pela recomendação CCITT3), incluindo os parâmetros

específicos. V.42bis (definido pela recomendação CCITT) e compressão de dados MNP5 Opções de Timeout, incluindo a configuração, inatividade de transmissão de dados do buffer.

Antes de ajustar a configuração do modem, um aplicativo deverá determinar as características do dispositivo de modem, utilizando a função GetCommProperties. Esta função preenche a estrutura COMMPROP, que contem a porção comum que se aplica a todos os dispositivos de comunicação, e a porção específica de cada fabricante. Para os

3 International Telegraph and Telephone Consultative Committee

Page 484: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

462462462462

DDDD

dispositivos de modem, a parte específica da estrutura COMMPROP é a estrutura MODEMDEVCAPS. BOOL GetCommProperties( HANDLE hFile, // handle do dispositivo de comunicação LPCOMMPROP lpCommProp // endereço da estrutura das propriedades da comunicação );

Código 23-6 - GetCommProperties

typedef struct _COMMPROP // cmmp WORD wPacketLength; // tamanho do pacote em bytes WORD wPacketVersion; // versao do pacote DWORD dwServiceMask; // serviços implementados DWORD dwReserved1; // reservado DWORD dwMaxTxQueue; // tamanho máximo do buffer Tx em bytes DWORD dwMaxRxQueue; // tamanho máximo do buffer Rx em bytes DWORD dwMaxBaud; // máximo baudrate, em bps DWORD dwProvSubType; // tipo especifico do fabricante DWORD dwProvCapabilities; // capacidades suportadas DWORD dwSettableParams; // parametros ajustáveis DWORD dwSettableBaud; // baudrates permissíveis WORD wSettableData; // tamanho de byte permitidos WORD wSettableStopParity; // permissao de bits de stop e paridade DWORD dwCurrentTxQueue; // tamanho do buffer Tx, em bytes DWORD dwCurrentRxQueue; // tamanho do buffer Rx, em bytes DWORD dwProvSpec1; // dados especificos do fabricante DWORD dwProvSpec2; // dados especificos do fabricante WCHAR wcProvChar[1]; // dados especificos do fabricante COMMPROP;

Código 23-7 - COMMPROP

typedef struct modemdevcaps_tag DWORD dwActualSize; // tamanho do retorno de dados em bytes DWORD dwRequiredSize; // tamanho total da estrutura DWORD dwDevSpecificOffset; // offset dos dados definidos pelo fabricante DWORD dwDevSpecificSize; // tamanho dos dados definidos pelo fabricante // identificação de Produto e versão DWORD dwModemProviderVersion; // número da versão do fabricante DWORD dwModemManufacturerOffset; // offset do nome do fabricante DWORD dwModemManufacturerSize; // comprimento do nome do fabricante DWORD dwModemModelOffset; // offset do nome do modelo DWORD dwModemModelSize; // comprimento do nome do modelo DWORD dwModemVersionOffset; // offset do nome da versão DWORD dwModemVersionSize; // comprimento do nome da versão // Opçoes de capacidade local DWORD dwDialOptions; // mapa de bits dos valores suportados DWORD dwCallSetupFailTimer; // máximo em segundos DWORD dwInactivityTimeout; // máximo em decimos de segundo DWORD dwSpeakerVolume; // mapa de bits dos valores suportados DWORD dwSpeakerMode; // mapa de bits dos valores suportados DWORD dwModemOptions; // mapa de bits dos valores suportados DWORD dwMaxDTERate; // valor máximo em bit/s DWORD dwMaxDCERate; // valor máximo em bit/s // Parte variável para strings e dados especíificosdo fabricante BYTE abVariablePortion [1]; // dados de comprimento variável MODEMDEVCAPS, *PMODEMDEVCAPS, *LPMODEMDEVCAPS;

Código 23-8 - MODEMDEVCAPS

O aplicativo poderá ler e escrever encima da configuração corrente de um modem, utilizando as funções GetCommConfig e SetCommConfig, ambas as quais utilizam a estrutura COMMCONFIG. BOOL GetCommConfig(

HANDLE hCommDev, // handle do dispositivo de comunicação LPCOMMCONFIG lpCC, // endereço da configuração da estrutura de comunicação LPDWORD lpdwSize // endereço do tamanho do buffer );

Código 23-9 - GetCommConfig

BOOL SetCommConfig(

Page 485: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

463463463463

DDDD

HANDLE hCommDev, // handle do dispositivo de comunicação LPCOMMCONFIG lpCC, // endereço dos serviçoes de configuraçao do canal DWORD dwSize // tamanho da estrutura );

Código 23-10 - SetCommConfig

Após a configuração do modem, o aplicativo poderá utilizar a interface programável de aplicação para telefonia (TAPI) que estabelecerá a conexão.

As funções de configuração do modem não fornecem gerenciamento continuo e manutenção do modem. Os fabricantes do modem deverão fornecer as janelas de configuração do modem, especiais para este propósito.

OOppeerraaççõõeess ddee LLeeiittuurraa ee EEssccrriittaa

A API Win32 suporta operações de I/O de arquivos síncrona e assíncrona (sobrepostas) nos recursos de comunicação serial. As operações sobrepostas habilitam ao um processo a execução de outras tarefas enquanto a operação é executada em background4. Um aplicativo utiliza as funções ReadFile ou ReadFileEx para ler de um recurso de comunicação, e WriteFile ou WriteFileEx para escrever a partir do mesmo. As funções ReadFile e WriteFile podem ser executadas de forma síncrona ou assíncrona. As funções ReadFileEx e WriteFileEx poderão ser executadas somente de forma assíncrona. BOOL ReadFile(

HANDLE hFile, // handle para o arquivo a ser lido LPVOID lpBuffer, // endereço do buffer que receberá os dados DWORD nNumberOfBytesToRead, // número de bytes a serem lidos LPDWORD lpNumberOfBytesRead, // endereço do número de bytes lidos LPOVERLAPPED lpOverlapped // endereço da estrutura de dados );

Código 23-11 - ReadFile

BOOL ReadFileEx( HANDLE hFile, // handle para o arquivo a ser lido

LPVOID lpBuffer, // endereço do buffer que receberá os dados DWORD nNumberOfBytesToRead, // número de bytes a serem lidos LPDWORD lpNumberOfBytesRead, // endereço do número de bytes lidos LPOVERLAPPED lpOverlapped // endereço da estrutura de dados LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // endereço da rotina de // finalizaçao );

Código 23-12 - ReadFileEx

BOOL WriteFile( HANDLE hFile, // handle para o arquivo a ser escrito

LPCVOID lpBuffer, // ponteiro para os dados de escrita DWORD nNumberOfBytesToWrite, // número de bytes a serem escritos LPDWORD lpNumberOfBytesWritten, // ponteiro para o número de bytes escritos LPOVERLAPPED lpOverlapped // ponteiro para a estrutura necessáraia para a //sobreposiçao de I/O );

Código 23-13 – WriteFile

BOOL WriteFileEx( HANDLE hFile, // handle para o arquivo de saída LPCVOID lpBuffer, // ponteiro para o buffer de entrada DWORD nNumberOfBytesToWrite, // número de bytes a escrever LPOVERLAPPED lpOverlapped, // ponteiro para oas dados de I/O assincrono LPOVERLAPPED_COMPLETION_ROUTINE lpCompletionRoutine // pontiro para a rotina de //finalizaçao );

4 Background: Aplicativos que são executados no segundo plano.

Page 486: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

464464464464

DDDD

Código 23-14 - WriteFileEx

O comportamento das funções de leitura e escrita, é afetado no caso em que a função esteja sendo executada como uma operação sobreposta, se os parâmetros de timeout estão associados com o manipulador, e se os parâmetros de controle de fluxo estão associados com o mesmo manipulador.

Um processo do aplicativo poderá também escrever num recurso de comunicação utilizando a função TransmitCommChar, que transmite um caractere especificado na frente de qualquer dado pendente no buffer de saída. Esta função é útil para transmitir um caractere sinalizado de mais alta prioridade para o dispositivo de recepção. A transmissão do caractere de alta prioridade ainda está sujeito ao controle de fluxo e aos timeouts de escrita, sendo que a operação será executada de forma síncrona. BOOL TransmitCommChar( HANDLE hFile, // handle do dispositivo de comunicação char cChar // caractere a ser transmitido );

Código 23-15 - TransmitCommChar

Um processo poderá utilizar a função PurgeComm para descartar todos os caracteres dos buffers de entrada ou saída do dispositivo. A função PurgeComm também poderá terminar as operações pendentes de leitura e escrita, mesmo que as operações não tenham sido completadas. Se um aplicativo utilizar PurgeComm para limpar um buffer de saída, os caracteres apagados não serão transmitidos. Para limpar um buffer de saída enquanto se assegura que o conteúdo está sendo transmitido, o aplicativo poderá chamar a função FlushFileBuffers (operação síncrona). Notar, embora, que a função FlushFileBuffers está sujeita ao controle de fluxo mas não aos timeouts de escrita, e esta não ira retornar até que todas as operações de escrita tenham sido efetuadas. BOOL PurgeComm( HANDLE hFile, // handle do dispositivo de comunicação DWORD dwFlags // ação a ser executada );

Código 23-16 - PurgeComm

BOOL FlushFileBuffers( HANDLE hFile // handle aberto para o arquivo cujos buffers serão esvaziados );

Código 23-17 - FlushFileBuffers

OOppeerraaççõõeess SSoobbrreeppoossttaass

As operações sobrepostas permitem ao aplicativo executar operações de I/O que levam algum tempo, no segundo plano (background), deixando o processo livre para efetuar outras tarefas. Para habilitar as operações de I/O sobrepostas em um recurso de comunicação, o aplicativo devera especificar o flag FILE_FLAG_OVERLAPPED na função CreateFile quando o handle seja aberto. Para executar as funções ReadFile e WriteFile como operações sobrepostas, o aplicativo deverá especificar um ponteiro para uma estrutura OVERLAPPED. A estrutura OVERLAPPED deverá conter um handle para um reset manual (não automático) objeto de eventos. O sistema setará o estado do objeto evento para não-sinalizado quando a chamada da função de I/O retorna antes que a operação tenha sido completada. O sistema setará o estado do objeto evento para sinalizado, quando a operação for completada. O aplicativo utiliza uma função de espera para testar o estado corrente do objeto de eventos ou esperar até que o estado seja sinalizado.

Page 487: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

465465465465

DDDD

typedef struct _OVERLAPPED // o DWORD Internal; DWORD InternalHigh; DWORD Offset; DWORD OffsetHigh; HANDLE hEvent; OVERLAPPED;

Código 23-18 - OVERLAPPED

As funções ReadFileEx e WriteFileEx podem ser executadas somente como operações sobrepostas. O aplicativo especifica um ponteiro para a função FileIOCompletionRoutine, que é executada quando a operação sobreposta estiver completada. A rotina de finalização é executada somente se o processo que a chama executar alguma operação com suspeita de erro. VOID WINAPI FileIOCompletionRoutine( DWORD dwErrorCode, // código de finalizaçao DWORD dwNumberOfBytesTransfered, // número de bytes transferidos LPOVERLAPPED lpOverlapped // ponteiro para a estrutura com a informaçao de I/O );

Código 23-19 - FileIOCompletionRoutine

TTiimmeeoouuttss

O handle para um recurso de comunicação possui um conjunto de parâmetros de timeout associado que afeta o comportamento das operações de leitura e escrita. Os timeouts podem ocasionar a finalização das funções ReadFile, ReadFileEx, WriteFile, ou WriteFileEx, no caso de que o intervalo de operação ultrapasse o período de timeout, a pesar de que o numero de caracteres especificados ainda não tenham sido lidos ou escritos. A quantidade de bytes lida ou escrita é indicada pelas funções ReadFile ou WriteFile (ou pelas funções GetOverlappedResult ou FileIOCompletionRoutine no caso de que o I/O seja executado como uma operação sobreposta). BOOL GetOverlappedResult( HANDLE hFile, // handle do arquivo ou dispositivo de comunicação LPOVERLAPPED lpOverlapped, // endereço da estrutura de sobreposição LPDWORD lpNumberOfBytesTransferred, // endereço do contador de bytes BOOL bWait // flag de espera );

Código 23-20 - GetOverlappedResult

Quando um aplicativo abre um recurso de comunicação, o sistema operacional setará os valores de timeout do recurso para os valores que foram utilizados na ultima vez que o dispositivo foi utilizado. Se o recurso de comunicação não tenha sido aberto ainda, o sistema operacional setará os valores de timeout para valores pré-definidos. Em ambos casos, um aplicativo deverá verificar os valores correntes de timeout após a abertura do recurso, e então explicitamente ajusta-los de acordo com a necessidade. Para determinar os valores correntes de timeout do recurso de comunicação, deve-se utilizar a função GetCommTimeouts. Para mudar os valores de timeout pode-se utilizar a função SetCommTimeouts. BOOL GetCommTimeouts( HANDLE hFile, // handle do dispositivo de comunicação LPCOMMTIMEOUTS lpCommTimeouts // endereço da estrutura de timeout );

Código 23-21 - GetCommTimeouts

BOOL SetCommTimeouts( HANDLE hFile, // handle do dispositivo de comunicação LPCOMMTIMEOUTS lpCommTimeouts // endereço da estrutura de timeout );

Page 488: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

466466466466

DDDD

Código 23-22 - SetCommTimeouts

Dois tipos de timeout são habilitados pelos parâmetros de timeout. Um timeout de intervalo ocorre quando o tempo entre a recepção de quaisquer dois caracteres exceder um número especificado de milisegundos. A temporização começa quando o primeiro caractere for recebido e reinicia a cada novo caractere recebido. O timeout total ocorre quando a quantidade total de tempo consumido por cada operação de leitura exceder de um numero calculado de milisegundos. A temporização inicia imediatamente quando inicia a operação de I/O. As operações de escrita suportam somente os timeouts totais. As operações de leitura suportam ambos timeouts, de intervalo e totais, sendo que estes podem ser utilizados separadamente ou combinados.

O tempo, em milisegundos, de um período total de timeout para as operações de leitura ou escrita, é calculado utilizando um multiplicador e valores constantes da estrutura COMMTIMEOUTS especificada na função GetCommTimeouts ou SetCommTimeouts. A seguinte formula é usada:

Timeout = (MULTIPLICADOR * número_de_bytes) + CONSTANTE

Utilizando ambas variáveis, o multiplicado e a constante, permite que o período de timeout total varie, dependendo da quantidade de dados que serão requeridos. O aplicativo poderá utilizar somente o valor constante, setando o multiplicador a zero, ou utilizar somente o multiplicador, setando a constante a zero. Se ambos, a constante e o multiplicador forem iguais a zero, o timeout total não será utilizado.

Se todos os parâmetros de timeout de leitura forem iguais a zero, estes não serão usados, e as operações de leitura não serão completadas até que tenha sido lido o total de bytes requeridos, ou que tenha acontecido algum tipo de erro. De forma análoga, se todo os parâmetros de timeout de escrita forem iguais a zero, a operação de escrita não será completada até que o número total de bytes solicitado não tenha sido escrito ou no caso de acontecer algum erro.

Se o parâmetro de intervalo de timeout de leitura tiver o valor MAXDWORD e ao mesmo tempo o parâmetro de timeout total for igual a zero, a operação de leitura será completada imediatamente após a leitura, sem importar que existam caracteres disponíveis no buffer de entrada, mesmo se este estiver vazio.

A temporização de intervalos força a operação de leitura a retornar quando existir uma pausa na recepção. Um processo que utilizar os timeouts de intervalo poderá setar um tempo de intervalo o suficientemente curto, de forma que possa responder rapidamente a pequenos fluxos isolados de um ou poucos caracteres, ainda que podendo ainda juntar grandes buffers de caracteres com uma simples chamada quando os dados estiverem sendo recebidos em um fluxo continuo.

Os timeouts para as operações de escrita podem ser úteis quando a transmissão é bloqueada por algum tipo de controle de fluxo, ou quando tenha sido chamada a função SetCommBreak para suspender a transmissão de caracteres. BOOL SetCommBreak(

HANDLE hFile // handle do dispositivo de comunicação );

Código 23-23 - SetCommBreak

Page 489: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

467467467467

DDDD

A Tabela 23-6 resume o comportamento das operações de leitura baseadas nos valores especificados para os timeouts totais e de intervalo. Total Intervalo Comportamento 0 0 Retorna quando o buffer estiver completamente preenchido. Os timeouts não são

utilizados. T 0 Retorna quando o buffer estiver completamente preenchido ou quando tenham

transcorrido T milisegundos desde o inicio da operação. 0 Y Retorna quando o buffer estiver completamente preenchido ou quando tenham

transcorrido Y milisegundos entre a recepção de quaisquer dois caracteres. A temporização não inicia até que o primeiro caractere seja recebido.

T Y Retorna quando o buffer estiver completamente preenchido ou quando ocorrer algum tipo de timeout.

Tabela 23-6 – Comportamento das operações de leitura baseadas nos valores especificados para os timeouts totais e de intervalo

Notar de qualquer forma que a temporização é relativa ao sistema que controla o dispositivo físico. Para um dispositivo remoto tal como um modem, a temporização é relativa ao sistema servidor no qual o modem estiver conectado. Qualquer atraso de propagação de rede não está previsto. Por exemplo, numa aplicação cliente deve-se especificar um tempo total de timeout calculado em 500 ms. Quando tiverem passado os 500 ms no servidor, um erro de timeout será retornado ao cliente. Se houver um atraso de propagação de rede de 50 ms, o cliente será notificado do timeout que ocorreu a 50 ms.

Os parâmetros de timeout afetam o comportamento das operações de leitura e escrita sobrepostas num dispositivo de comunicação. Quando forem utilizadas as funções sobrepostas ReadFile, WriteFile, ReadFileEx, ou WriteFileEx, poderá haver um retorno antes de serem totalmente executadas. Os parâmetros de timeout podem determinar quando a operação foi completada.

EErrrrooss ddee CCoommuunniiccaaççããoo

Existem outras circunstâncias quando as operações de leitura ou escrita podem ser finalizadas com menor número de caracteres que os requisitados, embora não tenha ocorrido um timeout. Seguem alguns exemplos: Alguns drivers suportam o uso de caracteres especiais, que finalizam a operação de leitura

imediatamente somente com os caracteres que tenham sido lidos até o ponto em que estes foram recebidos. A função PurgeComm pode ser chamada de forma prematura para terminar as operações

pendentes de leitura ou escrita. Esta função também apaga os conteúdos dos buffers de leitura ou escrita, ou ambos. Se ocorrer um erro de comunicação durante uma operação de leitura ou escrita, todas as

operações de I/O no recurso de comunicação serão terminadas. As condições de cancelamento, erros de paridade ou de frame são exemplos de tais erros. Quando um erro ocorre, o processo deverá chamar a função ClearCommError para limpar os flags de erro antes de continuar com as operações adicionais de I/O. A função ClearCommError informa o tipo especifico de erro que aconteceu e o estado atual do dispositivo.

BOOL ClearCommError( HANDLE hFile, // handle para o dispostivo de comunicação LPDWORD lpErrors, // ponteiro para a variável de código de erro de recepção LPCOMSTAT lpStat // ponteiro para o buffer para status da comunicação );

Código 23-24 - ClearCommError

Page 490: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

468468468468

DDDD

EEvveennttooss ddee CCoommuunniiccaaççããoo

Um processo pode monitorar um conjunto de eventos que ocorrem em um recurso de comunicação. Por exemplo, uma aplicação poderá utilizar a monitoração de eventos para determinar quando o sinal CTS (clear-to-send) e DSR (data-set-ready) mudarem de estado.

Um processo pode monitorar eventos em um dado recurso de comunicação pelo uso da função SetCommMask que cria uma mascara de evento. Para determinar a mascara corrente de um recurso de comunicação, o processo pode utilizar a função GetCommMask. A Tabela 23-7 especifica os eventos que podem ser monitorados. BOOL SetCommMask( HANDLE hFile, // handle para o dispositivo de comunicação DWORD dwEvtMask // mascara que identifica os eventos habilitados );

Código 23-25 - SetCommMask

BOOL GetCommMask( HANDLE hFile, // handle para o dispositivo de comunicação LPDWORD lpEvtMask // endereço da variável para adquirir a mascara de eventos );

Código 23-26 - GetCommMask

Valor Significado EV_BREAK Foi detectada uma ruptura (lógica) na entrada. EV_CTS O sinal CTS (clear to send) mudou de estado. EV_DSR O sinal DSR (data-set-ready) mudou de estado. EV_ERR Ocorreu um erro de estado de lina. Os erros de estado de linha são CE_FRAME,

CE_OVERRUN, e CE_RXPARITY. EV_RING Foi detectado um indicador de sinal de conexão. EV_RLSD O sinal RLSD (receive-line-signal-detect) mudou de estado. EV_RXCHAR Um caractere foi recebido e colocado no buffer de entrada. EV_RXFLAG O caractere de evento foi recebido e colocado no buffer de entrada. O caractere de

evento é especificado na estrutura DCB, a qual é aplicada à porta serial utilizando a função SetCommState.

EV_TXEMPTY Foi enviado o ultimo caractere do buffer de saída.

Tabela 23-7 – Eventos a serem monitorados

Depois que o conjunto de eventos seja especificado, um processo pode utilizar a função WaitCommEvent para esperar pela ocorrência de algum destes. A função WaitCommEvent pode ser utilizada de forma síncrona ou como uma operação sobreposta.

Quando ocorrer um dos eventos especificados na máscara de eventos, o processo finaliza a operação de espera e seta uma variável da máscara de evento. Se for chamada a função SetCommMask para o recurso de comunicação enquanto uma espera estiver pendente para tal recurso, a função WaitCommEvent retornará um erro.

A função WaitCommEvent detecta eventos que tenham ocorrido desde a última chamada a SetCommMask ou WaitCommEvent. Por exemplo, se foi especificado o evento EV_RXCHAR como um evento de espera satisfatório, a chamada a WaitCommEvent será satisfeita se houverem caracteres no buffer de entrada do driver que tenham chegado desde a ultima chamada a SetCommMask ou WaitCommEvent. Desta forma, dado o seguinte peudo-código, while (...)

WaitCommEvent T1: // Lê os bytes // processa-os

Page 491: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

469469469469

DDDD

T2:

Código 23-27

... quaisquer caracteres recebidos entre T1 e T1 satisfarão a seguinte chamada de WaitCommEvent.

Quando for monitorado um evento que ocorre quando o sinal muda de estado (CTS, DSR, e outros), a função WaitCommEvent indica a mudança, mas não o estado corrente. Para pesquisar o estado corrente dos sinais CTS (clear-to-send), DSR (data-set-ready), RLSD (receive-line-signal-detect), e o indicador de conexão (Ring Indicator), deve ser usada a função GetCommModemStatus. BOOL GetCommModemStatus( HANDLE hFile, // handle do dispositivo de comunicaçao LPDWORD lpModemStat // endereço dos valores do registrador de controle );

Tabela 23-8 – GetCommModemStatus

RReeffeerrêênncciiaa ddee FFuunnççõõeess ddee CCoommuunniiccaaççããoo

As seguintes funções e estruturas são utilizadas em dispositivos de comunicação.

FFuunnççõõeess ddee CCoommuunniiccaaççããoo

A seguir estão listadas as funções utilizadas com os dispositivos de comunicação. BuildCommDCB BuildCommDCBAndTimeouts ClearCommBreak ClearCommError CommConfigDialog DeviceIoControl EscapeCommFunction GetCommConfig GetCommMask GetCommModemStatus GetCommProperties GetCommState GetCommTimeouts GetDefaultCommConfig PurgeComm SetCommBreak SetCommConfig SetCommMask SetCommState SetCommTimeouts SetDefaultCommConfig SetupComm TransmitCommChar WaitCommEvent

Tabela 23-9 – Funções de Comunicação

EEssttrruuttuurraass ddee CCoommuunniiccaaççããoo

As seguintes estruturas são utilizadas com dispositivos de comunicação. COMMCONFIG COMMPROP COMMTIMEOUTS COMSTAT DCB

Page 492: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

470470470470

DDDD

MODEMDEVCAPS MODEMSETTINGS

Tabela 23-10 – Estruturas de Comunicação

EExxeemmpplloo FFuunncciioonnaall

O seguinte exemplo implementa as funções de comunicação serial utilizando o protocolo MODBUS5.

UUMMOODDBBUUSS..HH //--------------------------------------------------------------------------- #ifndef UModbusH #define UModbusH //--------------------------------------------------------------------------- //Definiçoes para código de erro #define ERROR_NO_ERROR 0 #define ERROR_VERIFY_ADDRESS 1 #define ERROR_DATA_IN_INVALID 2 #define ERROR_REG_ADDR_INVALID 3 #define ERROR_RETURN_DATA_WRITE 4 #define ERROR_EXCEPTION_RESP 5 #define ERROR_INVALID_COMMAND 6 #define ERROR_VERIFY_CRC 7 #define ERROR_TIME_OUT 8 //Definiçoes para frames RTU e RTU extendido #define RTU_SLAVE_ADD 0 #define RTU_FUNCTION 1 #define RTU_BYTES 2 #define RTU_DATA 3 #define RTU_PARAM1 2 #define RTU_PARAM2 4 #define RTU_EXT_WORDS 2 #define RTU_EXT_DATA 6 #define REQUEST_SIZE 8 //--------------------------------------------------------------------------- //Prototypes bool OpenPort(DRVPAR *); void ClosePort(void); bool ReadWords(DRVPAR *); bool WriteWord(DRVPAR *); void SendBuff(DRVPAR *, Byte); void ApplyCRC(Byte); bool VerifyCRC(void); bool WaitResp(DRVPAR *); short GetIntervalTimeOut(int); short GetMultiplierTimeOut(int); short GetConstantTimeOut(int); void DisableTX(void); void EnableTX(void); void WaitmSec(unsigned int); void InterpretCommand(DRVPAR *); bool VerifyAddress(int); void Trace(AnsiString); //--------------------------------------------------------------------------- #endif

Código 23-28 – UMODBUS.H

DDRRIIVVEERR..HH //--------------------------------------------------------------------------- #ifndef DriverH #define DriverH //--------------------------------------------------------------------------- // Value union union VAL

5 Código implementado em Builder C++ 3.0

Page 493: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

471471471471

DDDD

short sVal; long lVal; double dVal; char cVal[256]; ; // Driver Parameter Struct struct DRVPAR char cPort; //Porta COM Word wBaudRate; //Baudrate Word wAddr; //Endereço do dispositivo escravo Word wCmdType; //Tipo de comando unsigned int unInitReg; //Registrador inicial unsigned int unNroReg; //Numero de registradores VAL Value; //Valor a escrever void *DataList; //Ponteiro void para os valores recebidos Word wError; //Status de erro Word wReserved2; //Reservado- short void *vReserved3; //Reservado – ponteiro ; //--------------------------------------------------------------------------- // Data Struct List struct DATA_INFO_LIST int nOffSet; // Offset do parâmetro int * pnData; // Dados de leitura ; //--------------------------------------------------------------------------- #endif DriverH

Código 23-29 – DRIVER.H

UUMMOODDBBUUSS..CCPPPP //--------------------------------------------------------------------------- #include <vcl.h> #pragma hdrstop #include "Driver.h" #include "UModbus.h" //--------------------------------------------------------------------------- #pragma package(smart_init) //DRVPAR *Driver; //Variables HANDLE hComm; //Handle do dispositivo de comunicação Byte BuffTx[25]; //Buffer de saida Byte BuffRx[300]; //Buffer de entrada Word wRegCRC; //Registrador acumulador de CRC para leitura/escrita long lNumCh; //Numero de bytes recebidos no buffer de Entrada/Saída char cCodError; //Código de erro //--------------------------------------------------------------------------- //--------------------------------------------------------------------------- // Rotina: OpenPort // Função: Conecta a porta serial // Retorno: true->erro; false->OK //--------------------------------------------------------------------------- bool OpenPort(DRVPAR *Driver) BOOL bError; char szBuf[20]; // String para formatação de configuração de porta DCB Dcb; COMMTIMEOUTS CommTimeOuts; //Timeout struct // Driver = Drv; // Abre o dispositivo de comunicação wsprintf(szBuf, "COM%d", Driver->cPort); hComm = CreateFile(szBuf, GENERIC_READ | GENERIC_WRITE, 0, // exclusive access NULL, // no security attrs OPEN_EXISTING, 0, NULL ); if(hComm==INVALID_HANDLE_VALUE) return true; // Monta a estrutura de Dcb lstrcat(szBuf, ":9600,n,8,1");

Page 494: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

472472472472

DDDD

bError = BuildCommDCB(szBuf, &Dcb); if(bError==FALSE) return true; // Seta os parametros gerais Dcb.BaudRate = (Driver->wBaudRate==1200) ? CBR_1200 : (Driver->wBaudRate==2400) ? CBR_2400 : (Driver->wBaudRate==4800) ? CBR_4800 : (Driver->wBaudRate==9600) ? CBR_9600 : CBR_19200; Dcb.ByteSize = 8; Dcb.Parity = NOPARITY; Dcb.StopBits = ONESTOPBIT; // Parametros gerais Dcb.DCBlength = sizeof(Dcb); Dcb.fBinary = 1; Dcb.fParity = 0; Dcb.fOutxDsrFlow = 0; Dcb.fOutxCtsFlow = 0; Dcb.fDtrControl = DTR_CONTROL_ENABLE; Dcb.fRtsControl = RTS_CONTROL_TOGGLE; Dcb.fOutX = 0; Dcb.fInX = 0; Dcb.fErrorChar = 0; Dcb.fNull = 0; Dcb.EvtChar = 0; Dcb.ErrorChar = 0; // Seta os parametros de Dcb no hardware bError = SetCommState(hComm, &Dcb); if(bError==FALSE) return true; // Seta para gerar o evento EV_RXCHAR (chegou byte na queue de recepção) // e o evento EV_TXEMPTY (queue de transmissao vazia => já enviou tudo) SetCommMask(hComm, EV_RXCHAR | EV_TXEMPTY); // Configura os buffers das filas de transmissão e recepção SetupComm(hComm, 300, 128); DisableTX(); //RTS=0 // Inicializa o dispositivo (limpa buffers) PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); CommTimeOuts.ReadIntervalTimeout=GetIntervalTimeOut(Dcb.BaudRate); CommTimeOuts.ReadTotalTimeoutMultiplier=GetMultiplierTimeOut(Dcb.BaudRate); CommTimeOuts.ReadTotalTimeoutConstant=GetConstantTimeOut(Dcb.BaudRate); CommTimeOuts.WriteTotalTimeoutMultiplier=0; CommTimeOuts.WriteTotalTimeoutConstant=0; SetCommTimeouts(hComm, &CommTimeOuts); return false; //--------------------------------------------------------------------------- // Rotina: ClosePort // Função: Desconecta a porta serial // Retorno: nenhum //--------------------------------------------------------------------------- void ClosePort(void) // Desliga o transmissor DisableTX(); // Zera todos os eventos SetCommMask(hComm, 0); // purge any outstanding reads/writes and close device handle PurgeComm(hComm, PURGE_TXABORT | PURGE_RXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR ); // Encerra o dispositivo de comunicacao CloseHandle(hComm); //--------------------------------------------------------------------------- // Rotina: ReadWords // Função: Lê uma tabela de words // Retorno: true->erro; false->OK //--------------------------------------------------------------------------- bool ReadWords(DRVPAR *Driver)

Page 495: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

473473473473

DDDD

int i=0; wRegCRC = 0xFFFF; PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); BuffTx[i++]=Byte(Driver->wAddr); BuffTx[i++]=Byte(Driver->wCmdType); switch(Driver->wCmdType) case 3: BuffTx[i++]=Byte(Driver->unInitReg>>8); BuffTx[i++]=Byte(Driver->unInitReg); BuffTx[i++]=Byte(Driver->unNroReg>>8); BuffTx[i++]=Byte(Driver->unNroReg); break; case 0x72: BuffTx[i++]=Byte(Driver->unInitReg>>24); BuffTx[i++]=Byte(Driver->unInitReg>>16); BuffTx[i++]=Byte(Driver->unInitReg>>8); BuffTx[i++]=Byte(Driver->unInitReg); BuffTx[i++]=Byte(Driver->unNroReg>>24); BuffTx[i++]=Byte(Driver->unNroReg>>16); BuffTx[i++]=Byte(Driver->unNroReg>>8); BuffTx[i++]=Byte(Driver->unNroReg); break; case 5: case 6: BuffTx[i++]=Byte(Driver->unInitReg>>8); BuffTx[i++]=Byte(Driver->unInitReg); BuffTx[i++]=Byte(__int16(Driver->Value.lVal)>>8); BuffTx[i++]=Byte(Driver->Value.lVal); break; default: break; SendBuff(Driver, (Byte)i); Driver->wError = 0; bool bRet = WaitResp(Driver); Driver->wError = cCodError; return bRet; //--------------------------------------------------------------------------- // Rotina: CDriver::WriteWord // Função: Escreve uma word // Retorno: true->erro; false->OK //--------------------------------------------------------------------------- bool WriteWord(DRVPAR *Driver) int i=0; wRegCRC = 0xFFFF; PurgeComm(hComm, PURGE_TXABORT | PURGE_TXCLEAR | PURGE_RXCLEAR); BuffTx[i++]=Byte(Driver->wAddr); BuffTx[i++]=Byte(Driver->wCmdType); if(Driver->wCmdType != 6) return true;

BuffTx[i++]=Byte(Driver->unInitReg>>8); BuffTx[i++]=Byte(Driver->unInitReg); BuffTx[i++]=Byte(__int16(Driver->Value.sVal)>>8); BuffTx[i++]=Byte(Driver->Value.sVal); SendBuff(Driver, (Byte)i); bool bRet = WaitResp(Driver); Driver->wError = cCodError; return bRet; //--------------------------------------------------------------------------- // Rotina: SendBuff // Função: Envia o buffer de dados // Retorno: nenhum //--------------------------------------------------------------------------- void SendBuff(DRVPAR *Driver, Byte BQty)

DWORD dwLength, dwEvent; //Aplica o CRC aos bytes de dados for(int i=0; i<BQty; i++) ApplyCRC(BuffTx[i]);

BuffTx[BQty++]=(Byte)wRegCRC;

Page 496: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

474474474474

DDDD

BuffTx[BQty++]=(Byte)(wRegCRC>>8); EnableTX();

WriteFile(hComm, &BuffTx, BQty, &dwLength, NULL); while(1) WaitCommEvent(hComm, &dwEvent, NULL); if(dwEvent&EV_TXEMPTY) break; switch(Driver->wBaudRate) case 1200: WaitmSec(8); /* Atrasar 6 bits */ break; case 2400: WaitmSec(3); /* Atrasar 6 bits */ break; case 4800: WaitmSec(2); /* Atrasar 6 bits */ break; case 9600: WaitmSec(1); /* Atrasar 6 bits */ break; case 19200: WaitmSec(1); /* Atrasar 6 bits */ break; DisableTX(); //--------------------------------------------------------------------------- // Rotina: ApplyCRC // Função: Aplica o algoritmo CRC16 ao acumulador wRegCRC // Retorno: nenhum //--------------------------------------------------------------------------- void ApplyCRC(Byte BData) WordBool bLSB; char i; wRegCRC = wRegCRC^(Word)BData; i=8; while(i--) bLSB = wRegCRC&(Word)0x0001; wRegCRC = Word(wRegCRC>>1); if(bLSB) wRegCRC ^= 0xA001; //--------------------------------------------------------------------------- // Rotina: VerifyCRC // Função: Calcula o CRC do comando e compara com o CRC recebido no framming // Entrada: nQuant - Número de caracteres recebidos no buffer // Saida: 0-False CRC errado // 1-True CRC correto //--------------------------------------------------------------------------- bool VerifyCRC(void) long i,j; bool bLSB; j=0; if(lNumCh<8) bLSB=0; wRegCRC = 0xFFFF; while(j<(lNumCh-2)) wRegCRC = wRegCRC^(Word)BuffRx[j++]; i=8; while(i--) bLSB = wRegCRC&(Word)0x0001; wRegCRC = Word(wRegCRC>>1); if(bLSB) wRegCRC ^= 0xA001; if(BuffRx[lNumCh-2]==LOBYTE(wRegCRC) && BuffRx[lNumCh-1]==HIBYTE(wRegCRC)) return true; return false;

Page 497: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

475475475475

DDDD

//--------------------------------------------------------------------------- // Rotina: WaitResp // Função: Envia o buffer de dados // Retorno: true->erro; false->OK //--------------------------------------------------------------------------- bool WaitResp(DRVPAR *Driver) DWORD dwNrToRec,dwLength; switch(Driver->wCmdType) case 3: dwNrToRec = (Driver->unNroReg*2)+5; break; case 6: dwNrToRec = 8; break; default: break; ReadFile(hComm, BuffRx, dwNrToRec+REQUEST_SIZE, &dwLength, NULL) ; //Remove resquícios de eco for(int i=0; (DWORD)i<dwLength; i++) BuffRx[i]=BuffRx[i+(dwLength-dwNrToRec)]; lNumCh = (long)dwNrToRec; if(dwLength<dwNrToRec) cCodError=ERROR_TIME_OUT; return true; InterpretCommand(Driver); return false; //--------------------------------------------------------------------------- // Rotina: GetIntervalTimeOut // Função: Calcula o tempo de recepção entre 2 caracteres para dar time-out // Retorno: Time-out, em mseg //--------------------------------------------------------------------------- short GetIntervalTimeOut(int iBaud) short sTout; switch(iBaud) case 1200: sTout = 640; //160*4 break; case 2400: sTout = 320; //80*4 break; case 4800: sTout = 160; //40*4 break; case 9600: sTout = 80; //20*4 break; case 19200: sTout = 40; //10*4 break; return(sTout); //--------------------------------------------------------------------------- // Rotina: GetMultiplierTimeOut // Função: Calcula o tempo de recepção de 1 byte, dependente do baud rate // Retorno: Tempo de 1 byte, em mseg //--------------------------------------------------------------------------- short GetMultiplierTimeOut(int iBaud) short sTout; switch(iBaud) case 1200: sTout = 32; break; case 2400:

Page 498: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

476476476476

DDDD

sTout = 16; break; case 4800: sTout = 8; break; case 9600: sTout = 4; break; case 19200: sTout = 4; break; return(sTout); //--------------------------------------------------------------------------- // Rotina: GetConstantTimeOut // Função: Calcula o tempo de recepção do cabeçalho fixo e mais o tempo de // resposta do aparelho // Retorno: Tempo de recepcao do cabecalho e resposta, em mseg //--------------------------------------------------------------------------- short GetConstantTimeOut(int iBaud)

short sTout; switch(iBaud) case 1200: sTout = 48; break; case 2400: sTout = 24; break; case 4800: sTout = 12; break; case 9600: sTout = 6; break; case 19200: sTout = 3; break; sTout += short(2000); return(sTout); //--------------------------------------------------------------------------- // Rotina: DisableTX // Função: Desabilita a transmissão e permite apenas a recepção pela RS 485 // Retorno: nenhum //--------------------------------------------------------------------------- void DisableTX(void) EscapeCommFunction(hComm, CLRRTS); //--------------------------------------------------------------------------- // Rotina: EnableTX // Função: Habilita a transmissão pela RS 485 // Retorno: nenhum //--------------------------------------------------------------------------- void EnableTX(void) EscapeCommFunction(hComm, SETRTS); //--------------------------------------------------------------------------- // Rotina: WaitmSec // Função: Espera o tempo fornecido em milissegundos. Rotina de delay. // Retorno: nenhum //--------------------------------------------------------------------------- void WaitmSec(unsigned int uMSeg) DWORD dwContIni,dwContFim; dwContIni = GetTickCount(); do dwContFim = GetTickCount(); // Verifica se existem outras mensagens na fila

Page 499: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

477477477477

DDDD

while(dwContFim<(dwContIni+(DWORD)uMSeg)); //--------------------------------------------------------------------------- // Rotina: InterpretCommand // Função: Interpreta o comando recebido no buffer de recepção. // Retorno: nenhum //--------------------------------------------------------------------------- void InterpretCommand(DRVPAR *Driver) Word wPar1; // unsigned int unPar; cCodError = ERROR_NO_ERROR; if(lNumCh<=2) cCodError = ERROR_DATA_IN_INVALID; return; if(!VerifyCRC()) /* Erro de CRC */ cCodError = ERROR_VERIFY_CRC; return; if(!VerifyAddress(Driver->wAddr)) /* Não respondeu endereço correto*/ cCodError = ERROR_VERIFY_ADDRESS; return; char cCmd = BuffRx[RTU_FUNCTION]; switch(cCmd) case 03: /* Leitura de Registradores Retentivos */ wPar1 = 0; wPar1 = BuffRx[RTU_BYTES];/*Consiste o número de bytes recebidos*/ if(wPar1==0) cCodError = ERROR_DATA_IN_INVALID; return; int j=0; __int16 *pData = (__int16 *)Driver->DataList; for(int k=0; k<(wPar1/2); k++) *pData = (__int16)(((BuffRx[RTU_DATA+j]&0xFF)<<8) | BuffRx[RTU_DATA+1+j]); j+=2;

pData++; return; case 06: /* Escrita de Registrador Retentivo */ wPar1 = Word((BuffRx[RTU_PARAM1]<<8) | BuffRx[RTU_PARAM1+1]); /* Consiste o endereço de registrador recebido */ if(wPar1>255) cCodError = ERROR_REG_ADDR_INVALID; return; __int16 iPar2 = __int16((BuffRx[RTU_PARAM2]<<8) | BuffRx[RTU_PARAM2+1]); /* Consiste o valor forçado recebido */ if(iPar2!=Driver->Value.sVal) cCodError = ERROR_RETURN_DATA_WRITE; return; return; case 0x83: /* Resposta de exceção */ case 0x86: cCodError = ERROR_EXCEPTION_RESP; /* Resposta de exceção do escravo */ return; default: /* Não nenhum dos comandos anteriores */ cCodError = ERROR_INVALID_COMMAND; return; //--------------------------------------------------------------------------- // Rotina: VerifyAddress // Função: Verifica se o endereco recebido da solicitação é do escravo solicitado // Saida: 0-False Não é o endereço pela qual se solicitou // 1-True É este endereço do escravo solicitado

Page 500: Tratado Da Linguagem c

A P Ê N D I C E D - A S P O R T A S D E C O M U N I C A Ç Ã O N O S I S T E M A M S W I N D O W S

478478478478

DDDD

//--------------------------------------------------------------------------- bool VerifyAddress(int iAddr) if(BuffRx[RTU_SLAVE_ADD]!=iAddr) return false; return true; //--------------------------------------------------------------------------- // Rotina: Trace // Função: Insere um texto na janela de TraceWin // Saida: nenhum //--------------------------------------------------------------------------- void Trace(AnsiString ASText) void (__stdcall * Trace)(char *); int HLib = (int)LoadLibrary("TraceWin.dll"); if(HLib > 32) Trace = (void (__stdcall *)(char *)) GetProcAddress((HINSTANCE)HLib, "Trace"); if(Trace) Trace(ASText.c_str());

Código 23-30 – UMODBUS.CPP

Page 501: Tratado Da Linguagem c

479479479479 Luis Fernando Espinosa Cocian

AAppêênnddiiccee EE -- OOss CCoonneeccttoorreess

Conectores em Computadores Pessoais e outros sistemas comuns

Conectores Externos

PPoorrttaass ddee TTeeccllaaddoo ee MMoouussee ((PPSS//22,,AATT,,XXTT))

PPSS//22 -- AATTXX A maioria das máquinas que possui uma porta USB, suporta dispositivos de entrada

usando esta porta.

Figura 24-1 - Conector fêmea de 6 pinos Mini-DIN6

Pino Nome Direção 1 Descrição 1 DATA Key Data 2 n/c - Not connected 3 GND Gnd 4 VCC Power , +5 VDC 5 CLK Clock 6 n/c - Not connected S N/c - Not connected

Tabela 24-1 – PS/2 ATX

TTeeccllaaddoo AATT Nas maquinas AT, o mouse é usualmente conectado utilizando uma porta serial, ou

nas mais atuais, usando uma porta de mouse PS/2

1 Direção relativa doo computador para o mouse.

Apêndice

E

Page 502: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

480480480480

EEEE

Figura 24-2 – Teclado AT - Conector fêmea de 5 pinos DIN5 (DIN41524) no computador

Pino Nome Descrição Caracteristicas Técnicas 1 CLOCK Clock CLK/CTS, Open-collector 2 DATA Data RxD/TxD/RTS, Open-collector 3 n/c Não conectado Reset em alguns teclados antigos 4 GND Ground 5 VCC +5 VDC S N/c - -

Tabela 24-2 – Teclado AT

PPoorrttaa ddee JJooggooss ((JJooyyssttiicckk))

Conector fêmea DB15

Figura 24-3 – Conector da porta de joystick

Descrição Nome Pino2 Pino Nome Descrição +5V Vcc 1 9 Vcc +5V Botão 0 B0 2 10 B2 Botão 2 Posição 0 P0 3 11 P2 Posição 2 Terra GND 4 12 / (MIDI Out) Terra GND 5 13 P3 Posição 3 Posição 1 P1 6 14 B3 Botão 3 Botão 1 B1 7 15 / (Entrada MIDI) +5V Vcc 8

S NC Não conectado

Tabela 24-3 – Joystick

SSiinnaaiiss

P0 (Posição 0) Controle A, eixo X.

P1 (Posição 1) Controle A, eixo Y.

B0 (Botão 0) Controle A, Botão 1.

B1 (Botão 1) Controle A, Botão 2.

P2 (Posição 2) Controle B, eixo X.

P3 (Posição 3) Controle B, eixo Y.

B2 (Botão 2) Controle B, Botão 1 (ou Controle A, Botão 3).

B3 (Botão 3) Controle B, Botão 2 (ou Controle A, Botão 4).

PPrrooggrraammaaççããoo

Endereços de I/O3: 0200-0207 2 Os pinos 12 e 15 são reservados para a interface MIDI. 3 Alguns adaptadores de jogo não testam o ultimo byte do endereçamento podendo utilizar os endereços 0200H-020FH.

Page 503: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

481481481481

EEEE

PPoorrttaass SSeerriiaaiiss CCOOMM ((RRSS--223322))

PPoorrttaa SSeerriiaall ((DDBB--2255)) A porta serial DB25 foi projetada para mais sinais que a versão DB9, mas a maioria

dos dispositivos seriais utiliza somente os pinos disponíveis na versão DB9. Os fabricantes começaram a fazer conectores DB25 com os sinais dos conectores DB9 (os flat-cables utilizados para conectar o DB25 na placa mãe, são exatamente iguais ao do DB9). Quando foi lançado o sistema ATX, foi decidido parar a utilização do DB25 e substitui-lo pelo DB9.

No computador conector de 25 pinos SUB-D macho.

Figura 24-4 - Conector de 25 pinos SUB-D macho no DTE (Computador).

Figura 24-5 - Conector de 25 pinos SUB-D fêmea no DCE (Modem).

Pino Nome ITU-T Direção4 Descrição5 1 GND 101 Shield Ground 2 TXD 103 Transmit Data 3 RXD 104 Receive Data 4 RTS 105 Request to Send 5 CTS 106 Clear to Send 6 DSR 107 Data Set Ready 7 GND 102 System Ground 8 CD 109 Carrier Detect 9 - - RESERVADO 10 - - RESERVADO 11 STF 126 Select Transmit Channel 12 S.CD ? Secondary Carrier Detect 13 S.CTS ? Secondary Clear to Send 14 S.TXD ? Secondary Transmit Data

15 TCK 114 Transmission Signal Element Timing

16 S.RXD ? Secondary Receive Data 17 RCK 115 Receiver Signal Element Timing 18 LL 141 Local Loop Control 19 S.RTS ? Secondary Request to Send 20 DTR 108 Data Terminal Ready 21 RL 140 Remote Loop Control 22 RI 125 Ring Indicator 23 DSR 111 Data Signal Rate Selector 24 XCK 113 Transmit Signal Element Timing 25 TI 142 Test Indicator

4 A direção é do DTE (Computador) para o DCE (Modem). 5 Não conectar SHIELD(1) com GND(7).

Page 504: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

482482482482

EEEE

Pino Nome Direção6 Descrição 1 SHIELD - Shield Ground7 2 TXD Transmit Data 3 RXD Receive Data 4 RTS Request to Send 5 CTS Clear to Send 6 DSR Data Set Ready 7 GND - System Ground 8 CD Carrier Detect 9 n/c - 10 n/c - 11 n/c - 12 n/c - 13 n/c - 14 n/c - 15 n/c - 16 n/c - 17 n/c - 18 n/c - 19 n/c -

20 DTR Data Terminal Ready

21 n/c - 22 RI Ring Indicator 23 n/c - 24 n/c - 25 n/c -

Uma pinagem alternativa:

D Descrição Nome Pino Pino Nome Descrição D / Signal Ground GND 1 14 TX 2 Transmit Data 2 ? O Transmit Data TX 2 15 ? Transmitter Clock ? I Receive Data RX 3 16 RX 2 Receive Data 2 ? O Request To Send RTS 4 17 ? Receiver Clock ? I Clear To Send CTS 5 18 NC Not connected / I Data Set Ready DSR 6 19 RTS 2 Request To Send 2 ? / Signal Ground GND 7 20 DTR Data Terminal Ready O I Data Carrier Detect DCD 8 21 ? Signal Quality Detect ? ? Test voltage + ? 9 22 RI Ring Indicator O ? Test voltage - ? 10 23 ? Data Rate Select ? ? Select Transmit Freq. ? 11 24 ? Transmitter Clock ? ? Data Carrier Detect 2 DCD 2 12 25 BUSY Busy ? ? Clear To Send 2 CTS 2 13

S GND Chassis Ground /

PPoorrttaa SSeerriiaall ((DDBB--99))

Figura 24-6 - No computador, conector de 9 pinos SUB-D macho

6 A direção é do DTE (Computador) para o DCE (Modem). 7 Não conectar SHIELD(1) com GND(7).

Page 505: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

483483483483

EEEE

Pino Nome Direção 8 Descrição 1 CD Carrier Detect 2 RXD Receive Data 3 TXD Transmit Data

4 DTR Data Terminal Ready

5 GND System Ground 6 DSR Data Set Ready 7 RTS Request to Send 8 CTS Clear to Send 9 RI Ring Indicator S GND Chassis Ground

Tabela 24-4 – Conector serial DB9

PPoorrttaa SSeerriiaall ((RRJJ--4455))

Figura 24-7 – Conector serial RJ-45

A porta serial RJ-45 é utilizada em sistemas seriais Multiport, Cisco, etc.. Este conector é especifico e deve ser usado somente em casos especiais.

Pino Nome Descrição Direção 1 RTS Request To Send O 2 DTR Data Terminal Ready O 3 GND Ground / 4 TX Transmit Data O 5 RX Receive Data I 6 DCD Data Carrier Detect I 7 DSR Data Set Ready I 8 CTS Clear To Send I

Tabela 24-5 – Conector serial DB9

SSiinnaaiiss

A porta serial foi inicialmente projetada para os modems. DCD (Data Carrier Detect) high active input: Conexão estabelecida com outro computador. RX (Receive Data) input: Dados de entrada no dispositivo. TX (Transmit Data) output: Dados de saída do dispositivo. DTR (Data Terminal Ready) active high output: Indica ao outro computador que a porta está

pronta para receber dados. DSR (Data Set Ready) active high input: Indica que o outro computador que está pronto para

enviar dados. RTS (Ready To Send) active high output: Indica ao outro computador que deseja enviar dados. CTS (Clear To Send) active high input:: Indica que o outro computador que está pronto para

enviar dados. RI (Ring Indicator) active high input: Indica que outro computador deseja começar uma

conexão.

8 A direção é relativa do DTE (Computador) para o DCE (Modem).

Page 506: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

484484484484

EEEE

AAddaappttaaddoorr sseerriiaall ddee 99 ppiinnooss ppaarraa 2255 Este adaptador possibilita a conexão de um cabo serial com conector de 25 pinos a

um conector de 9 pinos, normalmente utilizado nos PCs.

Figura 24-8 - Conector DB9 fêmea para conexão com o PC:

Figura 24-9 - Conector DB25 macho para conexão com o cabo serial:

9-Pin 25-Pin Carrier Detect 1 8 Receive Data 2 3 Transmit Data 3 2 Data Terminal Ready 4 20 System Ground 5 7 Data Set Ready 6 6 Request to Send 7 4 Clear to Send 8 5 Ring Indicator 9 22

Tabela 24-6 – Conversor DB9 para DB25

PPoorrttaa PPaarraalleellaa -- LLPPTT // IImmpprreessssoorraa

No computador, conector fêmea DB-25

Figura 24-10 - Conector DB25 na porta paralela

Pino Nome Direção9 Descrição 1 /STROBE Strobe 2 D0 Data Bit 0 3 D1 Data Bit 1 4 D2 Data Bit 2 5 D3 Data Bit 3 6 D4 Data Bit 4 7 D5 Data Bit 5 8 D6 Data Bit 6 9 D7 Data Bit 7 10 /ACK Acknowledge

9 A direção é relativa ao computador.

Page 507: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

485485485485

EEEE

11 BUSY Busy 12 PE Paper End 13 SEL Select 14 /AUTOFD Autofeed 15 /ERROR Error 16 /INIT Initialize 17 /SELIN Select In

18 GND Signal Ground

19 GND Signal Ground

20 GND Signal Ground

21 GND Signal Ground

22 GND Signal Ground

23 GND Signal Ground

24 GND Signal Ground

25 GND Signal Ground

Tabela 24-7 – Conector DB25 da porta paralela

SSiinnaaiiss

STROBE (Strobe) saída ativa quando baixa. Avisa a impressora que os dados disponíveis em D0 a D7 são validos.

D0 - D7 (Data Bus) saídas Byte de dados enviados a impressora. Unicamente saída no modo de compatibilidade e

bidirecional nos modos mais atuais. ACK (Acknoledge) entrada ativa quando baixa. Avisa ao computador que a impressora está pronta para receber o próximo dado.

BUSY (Busy) entrada ativa quando baixa. O buffer da impressora está cheio ou a impressora está ocupada. O computador deverá

esperar que este sinal fique alto novamente para continuar enviando os dados. PE (Paper End) entrada ativa quando alta. A impressora ficou sem papel.

SLCT (Select Out) entrada ativa quando alta. A impressora está pronta (On-line).

AUTO-FEED (Auto-Feed) saída ativa quando alta. Indica uma nova linha para a impressora.

ERROR (Error) entrada ativa quando baixa. Erro detectado pela impressora.

INIT (Reset) saída ativa quando baixa. Inicializa a impressora (reset).

SLCT IN (Select In) saída ativa quando baixa. Envia uma requisição a impressora.

CCoonneeccttoorr ddee PPoorrttaa PPaarraalleellaa EECCPP1100 Conector DB25 fêmea no PC

10 Extended Capabilities Port – Porta de capacidade estendida.

Page 508: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

486486486486

EEEE

Figura 24-11 - Conector DB25 na porta paralela ECP

Pino Nome Dir11 Descrição 1 nStrobe Strobe 2 data0 Address, Data or RLE Data Bit 0 3 data1 Address, Data or RLE Data Bit 1 4 data2 Address, Data or RLE Data Bit 2 5 data3 Address, Data or RLE Data Bit 3 6 data4 Address, Data or RLE Data Bit 4 7 data5 Address, Data or RLE Data Bit 5 8 data6 Address, Data or RLE Data Bit 6 9 data7 Address, Data or RLE Data Bit 7 10 /nAck Acknowledge 11 Busy Busy 12 PError Paper End 13 Select Select 14 /nAutoFd Autofeed 15 /nFault Error 16 /nInit Initialize 17 /nSelectIn Select In 18 GND Signal Ground 19 GND Signal Ground 20 GND Signal Ground 21 GND Signal Ground 22 GND Signal Ground 23 GND Signal Ground 24 GND Signal Ground 25 GND Signal Ground

Tabela 24-8 – Conector DB25 da porta paralela ECP

UUSSBB1122

Conector de 4 pinos macho no controlador. Conector de 4 pinos fêmea nos periféricos.

Pino Nome Descrição Cor 1 VCC +5 VDC Vermelho 2 D- Data - Branco 3 D+ Data + Verde 4 GND Ground Preto S GND Chassis Ground

Tabela 24-9 – Conector USB

CCaarraacctteerriissttiiccaass::

Plug in Play. Conexão e desconexão a “quente” Baixo Custo Fácil de utilizar

11 Direção relativa ao PC. 12 Universal Serial Bus – Desenvolvido pela Compaq, Digital Equipment Corp, IBM PC Co., Intel, Microsoft, NEC e Northern Telecom.

Page 509: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

487487487487

EEEE

127 dispositivos físicos Cabos e conectores de baixo custo

LLaarrgguurraa ddee BBaannddaa::

Alta velocidade: 12 Mbps (com cabo blindado) Baixa velocidade: 1.5 Mbps.

PPoorrttaass ddee DDiissppllaayy

RRGGBBII DDiiggiittaall ((MMDDAA,,CCGGAA,,EEGGAA,,NNEECC)) Conector fêmea DB9

Figura 24-12 - Conector RGBI

MDA (Hercules) & CGA Pino MDA CGA Descrição

1 GND GND Terra

2 GND GND Terra

3 NC R Vermelho (somente CGA)

4 NC G Verde (somente CGA)

5 NC B Azul (somente CGA)

6 I I Intensidade

7 V NC Video (somente MDA)

8 H-Sync

H-Sync

Sincronismo Horizontal (+)

9 V-Sync

V-Sync

Sincronismo Vertical (-)

Nota : Muitos monitores possuem todos os sinais (R, G, B & V) e podem ser usados nos sistemas MDA and CGA.

EGA Pino EGA Descrição

1 GND Terra

2 R* Terra

3 R Vermelho

4 G Green

5 B Azul

6 G* Verde

7 B* Video

8 H-Sync

Sincronismo Horizontal (+)

9 V-Sync

Sincronismo Vertical (-)

NEC Pino NEC Descrição

1 I Intensidade

2 R Vermelho

3 G Verde

4 B Azul

5 GND Terra

6 GND Terra

7 H-Sync

Sincronismo Horizontal (+)

8 V-Sync

Sincronismo Vertical (-)

9

Tabela 24-10 – Pinagem do Conector RGBI

RRGGBB AAnnaallóóggiiccoo ((VVGGAA,,SSVVGGAA,,VVEESSAA)) Conector fêmea DB15 de alta densidade, na placa de vídeo. Conector macho

DB15 de alta densidade no cabo do monitor.

VGA13

VESA DDC14

Figura 24-13 - Conector RGB Analógico (VGA, SVGA, VESA)

13 VGA - Video Graphics Adapter or Video Graphics Array. 14 DDC - Display Data Channel

Page 510: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

488488488488

EEEE

Pino VGA VESA DDC Direção15 Descrição

1 Red-out Red-out Saída Vermelha (75 ohm, 0.7 V p-p)

2 Green-out Green-out Saída Verde (75 ohm, 0.7 V p-p)

3 Blue-out Blue-out Saída Azul (75 ohm, 0.7 V p-p)

4 ID 2 Reserved Monitor ID Bit 2 5 GND GND Terra 6 R-return R-GND Terra do Vermelho 7 G-return G-GND Terra do Verde 8 B-return B-GND Terra do Azul 9 Key (no pin) +5V - +5V. Sem pino 10 Sync-return SGND Terra de Sincronismo 11 ID 0 ID 0 Monitor ID Bit 0

12 ID 1 Das Monitor ID Bit 1 / Linha de dados seriais DDC

13 H-Sync H-Sync / CSYNC

Sincronismo Horizontal (ou Sincronismo Composto)

14 V-Sync V-Sync Sincronismo Vertical

15 Reservado SCL Monitor ID Bit 3 – Linha de Clock de Dados DDC

Tabela 24-11 – Pinagem do Conector RGB Analógico

IInntteerrffaaccee ddee RReeddee EEtthheerrnneett ((AAUUII,,BBNNCC,,TTPP,,FFLL))

BNC AUI TP

Figura 24-14 – Conectores de rede BNC, AUI e TP

AAUUII O conector AUI é de fato uma porta para um transceptor externo. Este então é

conectado a qualquer meio ethernet. Este conector é raramente utilizado.

Taxa : 10 Mbps Tipo : 10-BASE-5 Conector : DB15

Tabela 24-12 – Conector AUI

BBNNCC ((CCooaaxxiiaall)) Taxa : 10 Mbps Tipo : 10-BASE-2 Conector16 : BNC Cabo : RG-58A/U ou RG-58C/U

Tabela 24-13 –Conector BNC

15 A direção é relativa ao computador. 16 Alguns dispositivo possuem um conector BNC para uma antena externa wireless (sem fio).

Page 511: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

489489489489

EEEE

TTPP -- TTwwiisstteedd--PPaaiirr EEtthheerrnneett Os conectores são os mesmos para ambos sistemas, 10Base-T e 100Base-TX.

Conector RJ45 fêmea, na placa de interface de rede (hub)

Figura 24-15 – Conector TP fêmea na placa da interface

Conector RJ45 macho nos cabos

Figura 24-16 – Conector TP macho no cabo

Pino17 Nome Descrição 1 TX+ Tranceive Data+ 2 TX- Tranceive Data- 3 RX+ Receive Data+ 4 n/c Não conectado 5 n/c Não conectado 6 RX- Receive Data- 7 n/c Não conectado 8 n/c Não conectado

Tabela 24-14 – Pinagem do Conector TP

Taxa : 10 Mbps (20 Mbps no modo full-duplex)

100 Mbps (200 Mbps no modo full-duplex) 1000 Mbps

Tipo : 10-BASE-T 100BASE-TX 1000BASE-T Conector : RJ-45 RJ-45 RJ-45

Cabo : UTP ou STP categoria 3 (ou superior) UTP ou STP categoria 5 UTP ou STP categoria

5

Tabela 24-15 – Cabos usados na rede

CCoonneexxõõeess ddoo CCaabboo

Direta RJ-45 Cor RJ-

45 Cor

Cruzada RJ-45 Cor RJ-

45 Cor

17 Os sinais TX e RX são cruzados nos Hub's

Page 512: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

490490490490

EEEE

6 Verde 6 verde

7 branco –marrom 7 branco -

marrom

8 Marrom 8 marrom

7 (branco - marrom) 7 (branco -

marrom)

8 (marrom) 8 (marrom)

Tabela 24-16 – Conexões usadas nos cabos

Slots de Expansão

IISSAA1188

Conector macho avançado de 62+36 pinos na placa.

Figura 24-17 – Conector da placa periférica ISA

Conector fêmea de 62+36 pinos no computador.

Figura 24-18 – Conector ISA na placa mãe

Pino Nome Direção19 Descrição A1 /I/O CH CK I/O channel check; active low=parity error A2 D7 Data bit 7 A3 D6 Data bit 6 A4 D5 Data bit 5 A5 D4 Data bit 4 A6 D3 Data bit 3 A7 D2 Data bit 2 A8 D1 Data bit 1

18 ISA - Industry Standard Architecture 19 A direção é relativa à placa mãe.

Page 513: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

491491491491

EEEE

A9 D0 Data bit 0 A10 I/O CH RDY I/O Channel ready, pulled low to lengthen memory cycles A11 AEN Address enable; active high when DMA controls bus A12 A19 Address bit 19 A13 A18 Address bit 18 A14 A17 Address bit 17 A15 A16 Address bit 16 A16 A15 Address bit 15 A17 A14 Address bit 14 A18 A13 Address bit 13 A19 A12 Address bit 12 A20 A11 Address bit 11 A21 A10 Address bit 10 A22 A9 Address bit 9 A23 A8 Address bit 8 A24 A7 Address bit 7 A25 A6 Address bit 6 A26 A5 Address bit 5 A27 A4 Address bit 4 A28 A3 Address bit 3 A29 A2 Address bit 2 A30 A1 Address bit 1 A31 A0 Address bit 0 B1 GND Ground B2 RESET Active high to reset or initialize system logic B3 +5V +5 VDC B4 IRQ2 Interrupt Request 2 B5 -5VDC -5 VDC B6 DRQ2 DMA Request 2 B7 -12VDC -12 VDC B8 /NOWS No WaitState B9 +12VDC +12 VDC B10 GND Ground B11 /SMEMW System Memory Write B12 /SMEMR System Memory Read B13 /IOW I/O Write B14 /IOR I/O Read B15 /DACK3 DMA Acknowledge 3 B16 DRQ3 DMA Request 3 B17 /DACK1 DMA Acknowledge 1 B18 DRQ1 DMA Request 1 B19 /REFRESH Refresh B20 CLOCK System Clock (67 ns, 8-8.33 MHz, 50% duty cycle) B21 IRQ7 Interrupt Request 7 B22 IRQ6 Interrupt Request 6 B23 IRQ5 Interrupt Request 5 B24 IRQ4 Interrupt Request 4 B25 IRQ3 Interrupt Request 3 B26 /DACK2 DMA Acknowledge 2

B27 T/C Terminal count; pulses high when DMA term. count reached

B28 ALE Address Latch Enable B29 +5V +5 VDC B30 OSC High-speed Clock (70 ns, 14.31818 MHz, 50% duty cycle) B31 GND Ground C1 SBHE System bus high enable (data available on SD8-15) C2 LA23 Address bit 23 C3 LA22 Address bit 22 C4 LA21 Address bit 21 C5 LA20 Address bit 20 C6 LA18 Address bit 19

Page 514: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

492492492492

EEEE

C7 LA17 Address bit 18 C8 LA16 Address bit 17 C9 /MEMR Memory Read (Active on all memory read cycles) C10 /MEMW Memory Write (Active on all memory write cycles) C11 SD08 Data bit 8 C12 SD09 Data bit 9 C13 SD10 Data bit 10 C14 SD11 Data bit 11 C15 SD12 Data bit 12 C16 SD13 Data bit 13 C17 SD14 Data bit 14 C18 SD15 Data bit 15 D1 /MEMCS16 Memory 16-bit chip select (1 wait, 16-bit memory cycle) D2 /IOCS16 I/O 16-bit chip select (1 wait, 16-bit I/O cycle) D3 IRQ10 Interrupt Request 10 D4 IRQ11 Interrupt Request 11 D5 IRQ12 Interrupt Request 12 D6 IRQ15 Interrupt Request 15 D7 IRQ14 Interrupt Request 14 D8 /DACK0 DMA Acknowledge 0 D9 DRQ0 DMA Request 0 D10 /DACK5 DMA Acknowledge 5 D11 DRQ5 DMA Request 5 D12 /DACK6 DMA Acknowledge 6 D13 DRQ6 DMA Request 6 D14 /DACK7 DMA Acknowledge 7 D15 DRQ7 DMA Request 7 D16 +5 V D17 /MASTER Used with DRQ to gain control of system D18 GND Ground

Tabela 24-17 – Pinagem do conector ISA

EEIISSAA2200 +---------------------------------------------+ | (lado dos componentes | | | |___________ ISA-16bit __ ISA-8bit __| ||||||||||| ||||||||||||||||||| A1(frente)/B1(trás) | | | | | | | | | | | | | | EISA:E1(frente)/F1(trás) C1/D1 G1/H1

A,C,E,G = lado dos componentes

A,B,F,H = lado da solda

Figura 24-19 – Conector da placa periférica EISA

Conector de 62+38 pinos no computador

Figura 24-20 – Conector EISA na placa mãe

Pino Nome Descrição E1 CMD# Fase de Comando

20 Extended Industry Standard Architecture. – Desenvolvida pela Compaq, AST, Zenith, Tandy e outras.

Page 515: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

493493493493

EEEE

E2 START# Fase de Inicio E3 EXRDY EISA Ready E4 EX32# EISA Slave Size 32 E5 GND Terra E6 KEY Access Key E7 EX16# EISA Slave Size 16 E8 SLBURST# Slave Burst E9 MSBURST# Master Burst E10 W/R# Write/Read E11 GND Ground E12 RES Reserved E13 RES Reserved E14 RES Reserved E15 GND Ground E16 KEY Access Key E17 BE1# Byte Enable 1 E18 LA31# Latchable Addressline 31 E19 GND Ground E20 LA30# Latchable Addressline 30 E21 LA28# Latchable Addressline 28 E22 LA27# Latchable Addressline 27 E23 LA25# Latchable Addressline 25 E24 GND Ground E25 KEY Access Key E26 LA15 Latchable Addressline 15 E27 LA13 Latchable Addressline 13 E28 LA12 Latchable Addressline 12 E29 LA11 Latchable Addressline 11 E30 GND Ground E31 LA9 Latchable Addressline 9 F1 GND Ground F2 +5V +5 VDC F3 +5V +5 VDC F4 --- F5 --- F6 KEY Access Key F7 --- F8 --- F9 +12V +12 VDC F10 M/IO# Memory/Input-Output F11 LOCK# Lock bus F12 RES Reserved F13 GND Ground F14 RES Reserved F15 BE3# Byte Enable 3 F16 KEY Access Key F17 BE2# Byte Enable 2 F18 BE0# Byte Enable 0 F19 GND Ground F20 +5V +5 VDC F21 LA29# Latchable Addressline 29 F22 GND Ground F23 LA26# Latchable Addressline 26 F24 LA24# Latchable Addressline 24 F25 KEY Access Key F26 LA16 Latchable Addressline 16 F27 LA14 Latchable Addressline 14 F28 +5V +5 VDC F29 +5V +5 VDC F30 GND Ground F31 LA10 Latchable Addressline 10

Page 516: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

494494494494

EEEE

G1 LA7 Latchable Addressline 7 G2 GND Ground G3 LA4 Latchable Addressline 4 G4 LA3 Latchable Addressline 3 G5 GND Ground G6 KEY Access Key G7 D17 Data 17 G8 D19 Data 19 G9 D20 Data 20 G10 D22 Data 22 G11 GND Ground G12 D25 Data 25 G13 D26 Data 26 G14 D28 Data 28 G15 KEY Access Key G16 GND Ground G17 D30 Data 30 G18 D31 Data 31 G19 MREQx Master Request H1 LA8 Latchable Addressline 8 H2 LA6 Latchable Addressline 6 H3 LA5 Latchable Addressline 5 H4 +5V +5 VDC H5 LA2 Latchable Addressline 2 H6 KEY Access Key H7 D16 Data 16 H8 D18 Data 18 H9 GND Ground H10 D21 Data 21 H11 D23 Data 23 H12 D24 Data 24 H13 GND Ground H14 D27 Data 27 H15 KEY Access Key H16 D29 Data 29 H17 +5V +5 VDC H18 +5V +5 VDC H19 MAKx Master Acknowledge

Tabela 24-18 – Pinagem do conector EISA

IIDDEE2211 IInntteerrnnoo

Conector macho IDC de 40 pinos no controlador e periféricos (HE40).

Figura 24-21 – Conector IDE macho

Conector fêmea IDC de 40 pinos no cabo.

21 Integrated Drive Electronics – Desenvolvida pela Compaq e Western Digital.

Page 517: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

495495495495

EEEE

Figura 24-22 – Conector IDE fêmea

Pino Nome Dir22 Descrição 1 /RESET Reset 2 GND Ground 3 DD7 Data 7 4 DD8 Data 8 5 DD6 Data 6 6 DD9 Data 9 7 DD5 Data 5 8 DD10 Data 10 9 DD4 Data 4 10 DD11 Data 11 11 DD3 Data 3 12 DD12 Data 12 13 DD2 Data 2 14 DD13 Data 13 15 DD1 Data 1 16 DD14 Data 14 17 DD0 Data 0 18 DD15 Data 15 19 GND Ground 20 KEY - Key 21 n/c - Not connected 22 GND Ground 23 /IOW Write Strobe 24 GND Ground 25 /IOR Read Strobe 26 GND Ground 27 IO_CH_RDY 28 ALE Address Latch Enable 29 n/c - Not connected 30 GND Ground 31 IRQR Interrupt Request 32 /IOCS16 - IO ChipSelect 16 33 DA1 Address 1 34 n/c - Not connected 35 DA0 Address 0 36 DA2 Address 2 37 /IDE_CS0 (1F0-1F7) 38 /IDE_CS1 (3F6-3F7) 39 /ACTIVE Led driver 40 GND Ground

Tabela 24-19 – Pinagem do conector IDE

SSlloott ddee EExxppaannssããoo PPCCII2233

O barramento original PCI possuía 32 bits para operação em 33 MHz. Atualmente existem barramentos de 64 bits em 66 MHz. O encaixe no slot de 32 bits depende da freqüência do barramento, sendo que as placas PCI que suportam ambas freqüências possuem ranhuras de forma que possam ser inseridas em qualquer slot PCI.

22 A direção é relativa ao controlador (hard disk). 23 PCI - Peripheral Component Interconnect.

Page 518: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

496496496496

EEEE

33MHz/32-bit

33MHz/64-bit

66MHz/64-bit

Figura 24-23 – Conector PCI

No computador, conector de avanço de 98+22 pinos

PPllaaccaa PPCCII UUnniivveerrssaall 3322//6644 bbiitt

---------------------------------------------------------------- | PCI Lado dos componentes (lado B) | | | | | | opcional | | ____ obrigatório 32-bit pinos 64-bit pino _____| |___| |||||||--|||||||||||||||||--|||||||--|||||||||||||| ^ ^ ^ ^ ^ ^ ^ ^ b01 b11 b14 b49 b52 b62 b63 b94

Figura 24-24 – Conector PCI Universal 32/64 bits

PPllaaccaa PPCCII 55VV 3322//6644 bbiitt

| opcional | | ____ obrigatório 32-bit pinos 64-bit pino _____| |___| ||||||||||||||||||||||||||--|||||||--||||||||||||||

Figura 24-25 – Conector PCI 5 V 32/64 bits

PPllaaccaa PPCCII 33..33VV 3322//6644 bbiitt

| opcional | | ____ obrigatório 32-bit pins 64-bit pinos _____| |___| |||||||--||||||||||||||||||||||||||--||||||||||||||

Figura 24-26 – Conector PCI 3.3 V 32/64 bits

Pino24 +5V +3.3V Universal Descrição A1 TRST Test Logic Reset A2 +12V +12 VDC A3 TMS Test Mde Select A4 TDI Test Data Input A5 +5V +5 VDC A6 INTA Interrupt A A7 INTC Interrupt C A8 +5V +5 VDC A9 RESV01 Reserved VDC A10 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) A11 RESV03 Reserved VDC A12 GND03 (OPEN) (OPEN) Ground or Open (Key) A13 GND05 (OPEN) (OPEN) Ground or Open (Key) A14 RESV05 Reserved VDC A15 RESET Reset

24 Os pinos 63 a 94 existem somente nas implementações PCI de 64 bits.

Page 519: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

497497497497

EEEE

A16 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) A17 GNT Grant PCI use A18 GND08 Ground A19 RESV06 Reserved VDC A20 AD30 Address/Data 30 A21 +3.3V01 +3.3 VDC A22 AD28 Address/Data 28 A23 AD26 Address/Data 26 A24 GND10 Ground A25 AD24 Address/Data 24 A26 IDSEL Initialization Device Select A27 +3.3V03 +3.3 VDC A28 AD22 Address/Data 22 A29 AD20 Address/Data 20 A30 GND12 Ground A31 AD18 Address/Data 18 A32 AD16 Address/Data 16 A33 +3.3V05 +3.3 VDC A34 FRAME Address or Data phase A35 GND14 Ground A36 TRDY Target Ready A37 GND15 Ground A38 STOP Stop Transfer Cycle A39 +3.3V07 +3.3 VDC A40 SDONE Snoop Done A41 SBO Snoop Backoff A42 GND17 Ground A43 PAR Parity A44 AD15 Address/Data 15 A45 +3.3V10 +3.3 VDC A46 AD13 Address/Data 13 A47 AD11 Address/Data 11 A48 GND19 Ground A49 AD9 Address/Data 9 A52 C/BE0 Command, Byte Enable 0 A53 +3.3V11 +3.3 VDC A54 AD6 Address/Data 6 A55 AD4 Address/Data 4 A56 GND21 Ground A57 AD2 Address/Data 2 A58 AD0 Address/Data 0 A59 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) A60 REQ64 Request 64 bit ??? A61 VCC11 +5 VDC A62 VCC13 +5 VDC A63 GND Ground A64 C/BE[7]# Command, Byte Enable 7 A65 C/BE[5]# Command, Byte Enable 5 A66 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) A67 PAR64 Parity 64 ??? A68 AD62 Address/Data 62 A69 GND Ground A70 AD60 Address/Data 60 A71 AD58 Address/Data 58 A72 GND Ground A73 AD56 Address/Data 56 A74 AD54 Address/Data 54 A75 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) A76 AD52 Address/Data 52 A77 AD50 Address/Data 50 A78 GND Ground

Page 520: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

494949498888

EEEE

A79 AD48 Address/Data 48 A80 AD46 Address/Data 46 A81 GND Ground A82 AD44 Address/Data 44 A83 AD42 Address/Data 42 A84 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) A85 AD40 Address/Data 40 A86 AD38 Address/Data 38 A87 GND Ground A88 AD36 Address/Data 36 A89 AD34 Address/Data 34 A90 GND Ground A91 AD32 Address/Data 32 A92 RES Reserved A93 GND Ground A94 RES Reserved B1 -12V -12 VDC B2 TCK Test Clock B3 GND Ground B4 TDO Test Data Output B5 +5V +5 VDC B6 +5V +5 VDC B7 INTB Interrupt B B8 INTD Interrupt D B9 PRSNT1 Reserved B10 RES +V I/O (+5 V or +3.3 V) B11 PRSNT2 ?? B12 GND (OPEN) (OPEN) Ground or Open (Key) B13 GND (OPEN) (OPEN) Ground or Open (Key) B14 RES Reserved VDC B15 GND Reset B16 CLK Clock B17 GND Ground B18 REQ Request B19 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) B20 AD31 Address/Data 31 B21 AD29 Address/Data 29 B22 GND Ground B23 AD27 Address/Data 27 B24 AD25 Address/Data 25 B25 +3.3V +3.3VDC B26 C/BE3 Command, Byte Enable 3 B27 AD23 Address/Data 23 B28 GND Ground B29 AD21 Address/Data 21 B30 AD19 Address/Data 19 B31 +3.3V +3.3 VDC B32 AD17 Address/Data 17 B33 C/BE2 Command, Byte Enable 2 B34 GND13 Ground B35 IRDY Initiator Ready B36 +3.3V06 +3.3 VDC B37 DEVSEL Device Select B38 GND16 Ground B39 LOCK Lock bus B40 PERR Parity Error B41 +3.3V08 +3.3 VDC B42 SERR System Error B43 +3.3V09 +3.3 VDC B44 C/BE1 Command, Byte Enable 1 B45 AD14 Address/Data 14

Page 521: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

499499499499

EEEE

B46 GND18 Ground B47 AD12 Address/Data 12 B48 AD10 Address/Data 10 B49 GND20 Ground B50 (OPEN) GND (OPEN) Ground or Open (Key) B51 (OPEN) GND (OPEN) Ground or Open (Key) B52 AD8 Address/Data 8 B53 AD7 Address/Data 7 B54 +3.3V12 +3.3 VDC B55 AD5 Address/Data 5 B56 AD3 Address/Data 3 B57 GND22 Ground B58 AD1 Address/Data 1 B59 VCC08 +5 VDC B60 ACK64 Acknowledge 64 bit ??? B61 VCC10 +5 VDC B62 VCC12 +5 VDC B63 RES Reserved B64 GND Ground B65 C/BE[6]# Command, Byte Enable 6 B66 C/BE[4]# Command, Byte Enable 4 B67 GND Ground B68 AD63 Address/Data 63 B69 AD61 Address/Data 61 B70 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) B71 AD59 Address/Data 59 B72 AD57 Address/Data 57 B73 GND Ground B74 AD55 Address/Data 55 B75 AD53 Address/Data 53 B76 GND Ground B77 AD51 Address/Data 51 B78 AD49 Address/Data 49 B79 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) B80 AD47 Address/Data 47 B81 AD45 Address/Data 45 B82 GND Ground B83 AD43 Address/Data 43 B84 AD41 Address/Data 41 B85 GND Ground B86 AD39 Address/Data 39 B87 AD37 Address/Data 37 B88 +5V +3.3V Signal Rail +V I/O (+5 V or +3.3 V) B89 AD35 Address/Data 35 B90 AD33 Address/Data 33 B91 GND Ground B92 RES Reserved B93 RES Reserved B94 GND Ground

Tabela 24-20 – Pinagem do conector PCI 25

PPCCMMCCIIAA2266 Pino Nome Direção27 Descrição

25 Os pinos 63 a 94 somente existem nas implementações PCI de 64 bits. +V I/O é 3.3 V nas placas de 3.3 V; 5 V nas placas de 5 V, e definem as linhas de sinais nas placas universais. 26 PCMCIA - Personal Computer Memory Card International Association. 27 A direção é relativa ao computador.

Page 522: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

500500500500

EEEE

1 GND Ground 2 D3 Data 3 3 D4 Data 4 4 D5 Data 5 5 D6 Data 6 6 D7 Data 7 7 /CE1 Card Enable 1 8 A10 Address 10 9 /OE Output Enable 10 A11 Address 11 11 A9 Address 9 12 A8 Address 8 13 A13 Address 13 14 A14 Address 14 15 /WE:/P Write Enable : Program 16 /READY:/IREQ Ready : Busy (IREQ) 17 VCC +5V 18 VPP1 Programming Voltage (EPROM) 19 A16 Address 16 20 A15 Address 15 21 A12 Address 12 22 A7 Address 7 23 A6 Address 6 24 A5 Address 5 25 A4 Address 4 26 A3 Address 3 27 A2 Address 2 28 A1 Address 1 29 A0 Address 0 30 D0 Data 0 31 D1 Data 1 32 D2 Data 2 33 /WP:/IOIS16 Write Protect : IOIS16 34 GND Ground 35 GND Ground 36 /CD1 Card Detect 1 37 D11 Data 11 38 D12 Data 12 39 D13 Data 13 40 D14 Data 14 41 D15 Data 15 42 /CE2 Card Enable 2 43 /VS1 Refresh 44 /IORD - I/O Read 45 /IOWR - I/O Write 46 A17 Address 17 47 A18 Address 18 48 A19 Address 19 49 A20 Address 20 50 A21 Address 21 51 VCC +5V

52 VPP2 Programmeing Voltage 2 (EPROM)

53 A22 Address 22 54 A23 Address 23 55 A24 Address 24 56 A25 Address 25 57 /VS2 - RFU 58 RESET - RESET 59 /WAIT - WAIT 60 /INPACK - 61 /REG Register Select

Page 523: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

501501501501

EEEE

62 /BVD2:SPKR Battery Voltage Detect 2 : SPKR

63 /BVD1:STSCHG Battery Voltage Detect 1 : STSCHG

64 D8 Data 8 65 D9 Data 9 66 D10 Data 10 67 /CD2 Card Detect 2 68 GND Ground

Tabela 24-21 – Pinagem do conector PCMCIA

Conectores Internos

CCoonneeccttoorreess ddee AAlliimmeennttaaççããoo ddoo PPCC

CCoonneeccttoorr ddee AAlliimmeennttaaççããoo AATTXX Descrição Nome Cor Pino Pino Cor Nome Descrição Energia +3.3V +3.3V Marrom 1 11 Marrom +3.3V Energia +3.3V Energia +3.3V +3.3V Marrom 2 12 Azul -12V Energia -12V Terra GND Preto 3 13 Preto GND Terra

Energia +5V +5V Vermelho 4 14 Cinza PS-ON Controle remoto da alimentação On/Off Control

Terra GND Preto 5 15 Preto GND Terra Energia +5V +5V Vermelho 6 16 Preto GND Terra Terra GND Preto 7 17 Preto GND Terra Nível satisfatório de Energia

PWRGD Laranja 8 18 Branco +5V Energia +5V

+5V VSB +5V VSB Violeta 9 19 Vermelho +5V Energia +5V Energia +12V +12V Amarelo 10

20 Vermelho +5V Energia +5V

Tabela 24-22 – Pinagem do conector de alimentação ATX

CCoonneeccttoorr ddee AAlliimmeennttaaççããoo XXTT//AATT Pino Cor Nome Descrição 1 Vermelho +5V Energia +5V 2 Vermelho +5V Energia +5V 3 Vermelho +5V Energia +5V 4 Branco -5V Energia -5V 5 Preto GND Terra 6 Preto GND Terra 7 Preto GND Terra 8 Preto GND Terra 9 Azul -12V Energia -12V 10 Amarelo +12V Energia +12V 11 Vermelho +5V Energia +5V 12 Laranja PWRGD Nível satisfatório de Energia

Tabela 24-23 – Pinagem do conector de alimentação XT/AT

CCoonneeccttoorr ddee AAlliimmeennttaaççããoo 55¼¼ Utilizado para alimentar os Hard-discs, unidades de CD-ROM e a maioria dos

dispositivos de armazenamento nos computadores pessoais.

Pino Cor Nome Descrição

Page 524: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

502502502502

EEEE

1 Amarelo +12V Energia +12V 2 Preto GND Terra 3 Preto GND Terra 4 Vermelho +5V Energia +5V

Tabela 24-24 – Pinagem do conector de alimentação 5 ¼

CCoonneeccttoorr ddee AAlliimmeennttaaççããoo 33½½ Utilizado para alimentar os drives de disco flexível de 3½ nos computadores

pessoais.

Pino Cor Nome Descrição 1 Vermelho +5V Energia +5V 2 Preto GND Terra 3 Preto GND Terra 4 Amarelo +12V Energia +12V

Tabela 24-25 – Pinagem do conector de alimentação 3 ½

Page 525: Tratado Da Linguagem c

503503503503 Luis Fernando Espinosa Cocian

TTaabbeellaa ddee RReeffeerreenncciiaa ddee CCoonneeccttoorreess Descrição Macho Fêmea Vista Lateral Aplicações mais

Comunis Amphenol 24 Pinos

IEEE-488

Amphenol 36 Pinos

Centronics

Amphenol 50 Pinos

Telco, SCSI

MDR36

IEEE1284

Half Pitch Centronics 50 Pinos

SCSI

Half Pitch Centronics 68 Pinos

SCSI

BNC

LAN, Video

Page 526: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

504504504504

Tipo N

LAN, Video

TNC

Twinax

Twinax

DIN 5 Pinos

AT Keyboard, Audio, MIDI

Mini-DIN 3 Pinos

LocalTalk (AppleTalk)

Mini-DIN 4 Pinos

Apple Destop Bus, SVHS, S-Video

Mini-DIN 6 Pinos

PS/2 Keyboard/Mouse

Mini-DIN 8 Pinos

Apple Macintosh Serial Port

Page 527: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

505505505505

Conector de Dados IBM

IBM Cabling System

M/34

V.35

M/50

Data Products Printers

RJ10

Telephone

RJ11

Telephone

Page 528: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

506506506506

RJ12

Telephone

RJ45

ISDN, LAN

RJ48

MMJ

DecNET

Sub-D 9 Pinos

RS-232, RS-449, EGA, CGA

Sub-D 15 Pinos

X.21, Mac Video

Sub-D 25 Pinos

RS-232, Centronics

Sub-D 37 Pinos

RS-449

Sub-D 40 Pinos

MII

Page 529: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

507507507507

Sub-D 50 Pinos

SCSI

Sub-D 15 Pinos – Alta densidade

VGA, SVGA, XGA

Sub-D 26 Pinos - Alta densidade

Sub-D 62 Pinos - Alta densidade

Micro Sub-D 68 Pinos

SCSI

Micro Sub-D 50 Pinos

SCSI

13C3

Sun Work Station

Page 530: Tratado Da Linguagem c

A P Ê N D I C E E - O S C O N E C T O R E S

508508508508

SCART

Video

SMA905

SMA906

ST

MIC

FDDI

Page 531: Tratado Da Linguagem c

509509509509 Luis Fernando Espinosa Cocian

AAppêênnddiiccee FF -- EExxeemmppllooss ddee AApplliiccaaççããoo

AADDCC

O seguinte exemplo, mostra o código gerado para implementar a leitura de tensão (proporcional a temperatura), utilizando um conversor ADC e a porta paralela de um PC. Foi utilizado um compilador Borland C++ 3.1 (16 bits). O hardware é composto de um LM34 ou LM35 /* FILE: ADC.C - USE ADC TO READ VOLTAGE AND TEMPERATURE DESC: PERFORMS SINGLE-ENDED A/D ON CHANNEL 0 */ #include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb defined here */ #include<conio.h> /* formatted text functions defined here */ /* PROTOTYPES */ void LoadMux(int, int); void StartFirst(int, int); void main(void) float RV, VOLTAGE0, TEMP0; int BASEADDR, PORTA, PORTB, PORTC, CNTRL; int CHANNEL0; int CONVERSIONS; clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608\n"); gotoxy(40,1); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR+3; outportb(CNTRL, 192); /* configure PORTA BI, PORTB OUT */ LoadMux(PORTA, PORTB); StartFirst(PORTA, PORTB); CONVERSIONS = 0; /* USED TO MONITOR NUMBER OF CONVERSIONS PASSED */ RV = 5.0; /* USES A +5V REFERENCE VOLTAGE */ gotoxy(1,18); cprintf("< HIT ANY KEY TO QUIT >\n"); while (!kbhit()) CONVERSIONS = CONVERSIONS + 1; /* CONTINUOUSLY READ CHANNEL0 */ outportb(PORTB, 7); /* RD, CS, WR ALL HIGH */ delay(1); outportb(PORTB, 3); /* BRING CS LOW */ delay(1); outportb(PORTB, 2); /* BRING RD LOW */ delay(1); outportb(PORTB, 0); /* BRING WR LOW */ delay(1); /* ADC'S INTERRUPT SHOULD GO PULSE, I.E. HIGH TO LOW */ /* RESULT OF PREVIOUS CONVERSION ON D0-D7 */ outportb(PORTB, 2); /* SET WR HIGH */ delay(1); outportb(PORTB, 3); /* SET RD HIGH TOO */ delay(1); outportb(PORTB, 7); /* SET CS HIGH TOO */ delay(1);

Apêndice

F

Page 532: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

510510510510

FFFF

/* ADC'S INTERRUPT WILL GO HIGH TO LOW BACK TO HIGH */ outportb(PORTB, 3); /* BRING CS LOW */ delay(1); outportb(PORTB, 2); /* BRING RD LOW */ delay(1); /* ADC'S INTERRUPT WILL GO LOW TO HIGH */ outportb(PORTB, 3); /* BRING RD HIGH AGAIN */ delay(1); outportb(PORTB, 7); /* BRING CS HIGH TOO */ delay(1); /* READING THE RESULT OF LAST CONVERSION */ CHANNEL0 = inportb(PORTA); delay(1); VOLTAGE0 = CHANNEL0 / 255.0 * RV; TEMP0 = VOLTAGE0 * 100.0; /* SINCE LM34 IS 100 DEG.F PER VOLT */ gotoxy(1,12); cprintf("CHANNEL 0 READS = %d\n", CHANNEL0); gotoxy(1,13); cprintf("VOLTMETER READS = %+3.3f V", VOLTAGE0); gotoxy(1,14); cprintf("TEMPERATURE IS = %+3.3f DEG F", TEMP0); delay(10); /* 10 msec delay */ gotoxy(1,16); cprintf("NO. OF CONVERSIONS = %d", CONVERSIONS); ; gotoxy(1,20); cprintf("FINISHED\n"); ; /* end of main */ void LoadMux(int PORTA, int PORTB) outportb(PORTA, 8); /* load MUX onto ADC, ch 0, single-ended = 8 dec */ outportb(PORTB, 7); /* RD, WR, CS all high */ outportb(PORTB, 3); /* CS brought low */ delay(1); outportb(PORTB, 1); /* now WR brought low */ delay(1); outportb(PORTB, 3); /* now WR broght hi */ delay(1); outportb(PORTB, 7); /* now XS brough hi, MUX should be on ADC */ /* NB: ADC interrupt will pulse low then high */ /* this pulses C.6 (ACK) and toggles 8255's ACK line */ delay(1); ; /* end of LoadMux */ void StartFirst(int PORTA, int PORTB) /* Start 1st convers., read PORT A */ float RV; /* USES A +5V REFERENCE VOLTAGE ON ADC */ int CHANNEL0; /* Temperature sensor on channel 0 */ float VOLTAGE0; /* Analog voltage read on channel 0 */ RV = 5.0; gotoxy(1,5); cprintf("STARTING FIRST CONVERSION...\n"); outportb(PORTB, 3); /* BRING CS LOW */ delay(1); outportb(PORTB, 2); /* BRING RD LOW */ delay(1); outportb(PORTB, 3); /* BRING RD HIGH */ delay(1); outportb(PORTB, 7); /* BRING CS HIGH */ delay(1); CHANNEL0 = inportb(PORTA); VOLTAGE0 = (CHANNEL0 / 255.0) * RV; gotoxy(1,6); cprintf("CHANNEL 0 READS = %d", CHANNEL0); gotoxy(1,7); cprintf("VOLTMETER READS = %+3.3f V", VOLTAGE0); gotoxy(1,9); cprintf("<HIT A KEY>\n"); getche(); ; /* end of StartFirst */

Código 25-1 – Programa ADC

Page 533: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

511511511511

FFFF

DDAACC

Este programa pede a inserção de um número decimal. O valor é enviado para uma porta onde deverá estar devidamente conectado um conversor D/ª /* FILE: DAC.C DESC: Prompt user for decimal word. Returns associated analog voltage */ #include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb defined here */ #include<conio.h> /* formatted text functions defined here */ void main(void) int Volt; int BASEADDR; int PORTA, PORTB, PORTC; int CNTRL; int Choice; int PORT; clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608\n"); gotoxy(1,2); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR+3; outportb(CNTRL, 128); /* configure all ports for output */ do gotoxy(1,4); cprintf("Enter Port Choice\n"); gotoxy(1,5); cprintf("(1) Port A\n"); gotoxy(1,6); cprintf("(2) Port B\n"); gotoxy(1,7); cprintf("(3) Port C\n"); gotoxy(1,8); scanf("%d", &Choice); if(Choice < 1 || Choice > 3) clrscr(); gotoxy(1,1); cprintf("Enter 1, 2 or 3\n"); ; while (Choice < 1 || Choice > 3); switch (Choice) case 1 : PORT = PORTA; break; case 2 : PORT = PORTB; break; case 3 : PORT = PORTC; break; ; gotoxy(1,18); cprintf("Enter 999 to quit\n"); gotoxy(1,10); cprintf("Input Decimal word (0 = -5V, 255= +5V \n"); gotoxy(1,11); while(1) scanf("%d", &Volt); if(Volt == 999) clrscr(); gotoxy(1,1); cprintf("Goodbye!\n"); outportb(PORT, 0); /* quitting */ exit(0); ; outportb(PORT, Volt); ; /* end of main */

Código 25-2 – Programa DAC

TTeeccllaaddoo

Efetua a leitura de um teclado telefônico conectado à porta paralela. /* FILE: KEYPAD.C

Page 534: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

512512512512

FFFF

DESC: Reads keypad */ #include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb defined here */ #include<conio.h> /* formatted text functions defined here */ void WhichKey(int); void main(void) int BASEADDR; int PORTA, PORTB, PORTC; int CNTRL; int NUMBER; clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608\n"); gotoxy(1,2); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR+3; outportb(CNTRL, 155); /* configure all ports for input */ gotoxy(1,15); cprintf("Press ANY key on your keyboard to quit\n"); gotoxy(1,4); cprintf("HIT A KEY ON YOUR KEYPAD\n"); while(!kbhit()) do NUMBER = inportb(PORTC); while ( (NUMBER <= 8) ); /* Data Available */ WhichKey(PORTA); ; /* end of while */ clrscr(); gotoxy(1,1); cprintf("Goodbye!\n"); /* end of main * /* WhichKey */ void WhichKey(int PORTA) int k; char KeyPushed; k = inportb(PORTA); switch (k) case 0 : KeyPushed = '1'; break; case 1 : KeyPushed = '2'; break; case 2 : KeyPushed = '3'; break; case 4 : KeyPushed = '4'; break; case 5 : KeyPushed = '5'; break; case 6 : KeyPushed = '6'; break; case 8 : KeyPushed = '7'; break; case 9 : KeyPushed = '8'; break; case 10: KeyPushed = '9'; break; case 13: KeyPushed = '0'; break; case 14: KeyPushed = '#'; break; ; gotoxy(1,5); cprintf("KEY PUSHED = %c\n", KeyPushed); ; /* end of WhichKey */

Código 25-3 – Programa Teclado

LLCCDD

Escreve caracteres num display de LCD, conectado a uma porta paralela. /* FILE: LCD.C DESC: Tests LCD NOTE: delay is Turbo C function defined in dos.h delay(milliseconds) is needed to ensure toggling */

Page 535: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

513513513513

FFFF

#include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb, delay defined here */ #include<conio.h> /* formatted text functions defined here */ /* PROTOTYPES */ void SendChar(int, int, char); void NewLine(int, int, int); void main(void) int Sgnal; int BASEADDR; int PORTA, PORTB, PORTC; int CNTRL; int k; char D; /* for alphanumeric characters */ int E = 10; /* 10 DECIMAL - ENABLE (PORT C BIT 5) */ int RW = 12; /* 12 DECIMAL - READ/WRITE (PORT C BIT 6) */ int RS = 8; /* 8 DECIMAL - RESET (PORT C BIT 4) */ int I; /* for selecting line 1 or line 2 */ clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608\n"); gotoxy(1,2); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR + 3; outportb(CNTRL, 128); /* 128 dec - all ports output */ outportb(PORTA, 0); outportb(PORTB, 0); outportb(PORTC, 0); outportb(CNTRL, E); /* ENABLE */ outportb(CNTRL, RW); /* READ/WRITE */ outportb(CNTRL, RS); /* RESET */ gotoxy(1,4); cprintf("INITIALIZING LCD...\n"); for(k = 1; k <= 3; k++) outportb(PORTA, 48); outportb(CNTRL, E + 1); /* TOGGLE ENABLE */ delay(1); outportb(CNTRL, E); ; gotoxy(20,4); cprintf("...INITIALIZED\n"); outportb(PORTA, 56); outportb(CNTRL, E + 1); outportb(CNTRL, E); outportb(PORTA, 12); outportb(CNTRL, E + 1); outportb(CNTRL, E); outportb(PORTA, 1); /* THE DISPLAY IS CLEARED */ outportb(CNTRL, E + 1); delay(2); /* WAIT 2 MSEC TO ENSURE */ outportb(CNTRL, E); /* LCD IS CLEARED */ outportb(PORTA, 6); outportb(CNTRL, E + 1); outportb(CNTRL, E); gotoxy(1,11); I = 128; /* SET CURSOR TO LINE 1 POSITION */ NewLine(PORTA, CNTRL, I); D = 'R'; SendChar(PORTA,CNTRL, D); D = 'O'; SendChar(PORTA,CNTRL, D); D = 'W'; SendChar(PORTA,CNTRL, D); D = ' '; SendChar(PORTA,CNTRL, D); D = '1'; SendChar(PORTA,CNTRL, D); I = 192; /* SET CURSOR TO LINE 2 POSITION */ NewLine(PORTA, CNTRL, I); D = 'R'; SendChar(PORTA,CNTRL, D); D = 'O'; SendChar(PORTA,CNTRL, D); D = 'W'; SendChar(PORTA,CNTRL, D); D = ' '; SendChar(PORTA,CNTRL, D); D = '2'; SendChar(PORTA,CNTRL, D); gotoxy(1,23); cprintf("Goodbye!\n"); /* end of main *

Page 536: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

514514514514

FFFF

/* SendChar function */ void SendChar(int PORTA, int CNTRL, char D) int E = 10; /* 10 DECIMAL - ENABLE (PORT C BIT 5) */ int RW = 12; /* 12 DECIMAL - READ/WRITE (PORT C BIT 6) */ int RS = 8; /* 8 DECIMAL - RESET (PORT C BIT 4) */ outportb(CNTRL, RS + 1); outportb(CNTRL, RW); outportb(PORTA, D); outportb(CNTRL, E + 1); delay(1); outportb(CNTRL, E); ; /* end of SendChar */ /* NewLine function */ void NewLine(int PORTA, int CNTRL, int I) int E = 10; /* 10 DECIMAL - ENABLE (PORT C BIT 5) */ int RS = 8; /* 8 DECIMAL - RESET (PORT C BIT 4) */ outportb(CNTRL, RS); outportb(PORTA, I); outportb(CNTRL, E + 1); delay(1); outportb(CNTRL, E); ; /* end of NewLine */

Código 25-4 – Programa LCD

PPWWMM

Controla a velocidade e direção de um motor DC usando PWM. /* FILE: PWM.C DESC: Control speed and direction of a DC motor using PWM */ #include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb defined here */ #include<conio.h> /* formatted text functions defined here */ void pwm(int, int, int, int); void main(void) int BASEADDR; int PORTA, PORTB, PORTC; int CNTRL; int DutyCycle; int SpeedOption; int Enable, Phase; /* Enable on A.0, Phase on A.1 */ clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608 =>\n"); gotoxy(42,1); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR+3; outportb(CNTRL, 128); /* configure all ports for output */ outportb(PORTA, 0); /* motor should be off */ gotoxy(1,3); cprintf("Speed Options"); gotoxy(1,4); cprintf("(f)aster"); gotoxy(1,5); cprintf("(s)lower"); gotoxy(1,6); cprintf("(r)everse direction"); gotoxy(1,7); cprintf("(q)uit"); gotoxy(1,9); cprintf("Selection => "); gotoxy(14,9); DutyCycle = 0; /* start off at zero velocity */ Enable = 1;

Page 537: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

515515515515

FFFF

Phase = 0; do while (!kbhit()) pwm(PORTA, DutyCycle, Enable, Phase); ; /* end of while */ SpeedOption = getch(); switch(SpeedOption) case 102 : /* 102 ASCII is f */ /* increase speed by 10% DC */ DutyCycle = DutyCycle + 5; if(DutyCycle > 100) DutyCycle = 100; gotoxy(1,20); cprintf("Can't go faster"); delay(200); gotoxy(1,20); cprintf(" "); ; gotoxy(1,13); cprintf("Going faster...\n"); Enable = 1; pwm(PORTA, DutyCycle, Enable, Phase); break; case 115 : /* 115 ASCII is s */ /* decrease speed by 10% DC */ DutyCycle = DutyCycle - 5; if(DutyCycle < 0) DutyCycle = 0; gotoxy(1,20); cprintf("Can't go slower"); delay(200); gotoxy(1,20); cprintf(" "); ; gotoxy(1,13); cprintf("Going slower...\n"); Enable = 1; pwm(PORTA, DutyCycle, Enable, Phase); break; case 114 : /* 114 ASCII is r */ /* reverse direction at present DC */ Enable = 1; if(Phase == 0) Phase = 2; else if(Phase ==2) Phase = 0; ; pwm(PORTA, DutyCycle, Enable, Phase); gotoxy(1,13); cprintf("Reversing.....\n"); break; case 113 : /* 113 ASCII is q */ gotoxy(1,22); cprintf("Quitting"); outportb(PORTA, 0); outportb(PORTB, 0); outportb(PORTC, 0); exit(0); break; ; /* end of switch */ gotoxy(14,9); while(1); /* end of do */ ; /* end of main */ void pwm(int PORTA, int DutyCycle, int Enable, int Phase) int OnTime, OffTime, TotalTime; TotalTime = 100; /* 100 msec */ OnTime = (int)(TotalTime * DutyCycle / 100); OffTime = (int)(TotalTime - OnTime); outportb(PORTA, Enable+Phase); delay(OnTime); Enable = 0; outportb(PORTA, Enable+Phase); delay(OffTime); gotoxy(1,10); cprintf("Duty Cycle is %3d", DutyCycle); return; ; /* end of pwm */

Código 25-5 – Programa PWM

Page 538: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

516516516516

FFFF

RReelléé

Utiliza um latch 74LS374 para ligar um relé na porta paralela. /* FILE: RELAY.C DESC: Uses the 74374 latch to turn on a relay linked with port A.0 */ #include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb defined here */ #include<conio.h> /* formatted text functions defined here */ void main(void) int Sgnal; int BASEADDR; int PORTA, PORTB, PORTC; int CNTRL; clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608\n"); gotoxy(1,2); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR + 3; outportb(CNTRL, 128); /* configure all ports for output */ outportb(PORTB, 2); /* (B.0) CLK low and (B.1) OE is high */ while(1) gotoxy(1,4); cprintf("Port A.1 has normally open relay\n"); gotoxy(1,5); cprintf("Enter (1) to turn on\n"); gotoxy(1,6); cprintf("Enter (2) to turn off\n"); gotoxy(1,7); cprintf("Enter (3) to turn off and quit\n"); gotoxy(1,8); cprintf("Selection => "); scanf("%d", &Sgnal); switch (Sgnal) case 1 : outportb(PORTA, 0); /* A.0 is low thus relay enabled */ outportb(PORTB, 1); /* CLK high and OE low */ gotoxy(1,10); cprintf("Load (Motor) is on \n"); break; case 2 : outportb(PORTA, 1); /* A.0 is high thus relay off */ outportb(PORTB, 2); /* CLK low and OE high */ gotoxy(1,10); cprintf("Load (Motor) is off\n"); break; case 3 : outportb(PORTB, 2); /* OE is now high */ gotoxy(1,20); cprintf("Exiting\n"); exit(0); break; ; /* end of select */ if(Sgnal<1 || Sgnal>3) gotoxy(1,15); cprintf("Enter 1, 2 or 3\n"); ; ; /* end of while */ ; /* end of main */

Código 25-6 – Programa Relé

MMoottoorr ddee PPaassssoo

Controla um motor de passo /* FILE: STEPPER.C DESC: Speed and Positioning options for Stepper Motor Port A has 5 lines attached and described as follows: OP = PHASE Line A.0 1 enable / 0 disable HS = HALF STEP Line A.1 2 enable / 0 disable SI = STEP INPUT Line A.2 4 enable / 0 disable

Page 539: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

517517517517

FFFF

DR = DIRECTION Line A.3 8 enable / 0 disable OE : OUTPUT ENABLE Line A.4 16 enable / 0 disable */ #include<stdio.h> #include<stdlib.h> #include<dos.h> /* outportb, inportb defined here */ #include<conio.h> /* formatted text functions defined here */ int Calibrate(int); void StepTheMotor(int, int, int); void Selection(int, int, int); void MoveToFixedAngle(int, int, int); void Continuous(int, int, int); void main(void) int Sgnal; int BASEADDR; int PORTA, PORTB, PORTC; int CNTRL; int Choice; int StepsPerRev; int DR = 0; clrscr(); /* clear screen */ window(5,5,75,30); /* set up text window */ gotoxy(1,1); cprintf("Enter Base Address (decimal) e.g. 608\n"); gotoxy(1,2); scanf("%d", &BASEADDR); PORTA = BASEADDR; PORTB = BASEADDR + 1; PORTC = BASEADDR + 2; CNTRL = BASEADDR + 3; outportb(CNTRL, 128); /* configure all ports for output */ outportb(PORTB, 0); /* Ports B and C not used, so just set to 0 */ outportb(PORTC, 0); StepsPerRev = Calibrate(PORTA); Selection(PORTA, StepsPerRev, DR); /* end of main */ int Calibrate(int PORTA) int GetKey; int StepsPerRev; int DR = 0; int WaitSomeTime = 0; /* determine how many steps per revolution stepper motor */ gotoxy(1,5); cprintf("--CALIBRATION--\n"); gotoxy(1,7); cprintf("Hit the <ENTER> key to step the motor.\n"); gotoxy(1,9); cprintf("Count the number of times you hit <ENTER>\n"); gotoxy(1,10); cprintf("for motor to rotate 1 revolution\n"); gotoxy(1,12); cprintf("Hit q then <ENTER> to quit\n"); do StepTheMotor(PORTA, DR, WaitSomeTime); gotoxy(1,14); GetKey = getch(); while (GetKey != 113 && GetKey != 81); /* 113 is ASCII q */ gotoxy(1,16); cprintf("Number of times did you hit <ENTER> => \n"); gotoxy(40,16); scanf("%d", &StepsPerRev); gotoxy(1,18); cprintf("Is %d Correct? (y)es, (n)o \n", StepsPerRev); gotoxy(30,18); GetKey = getche(); if(GetKey == 121 || GetKey == 89) /* 121 is y and 89 is Y in ASCII */ clrscr(); return(StepsPerRev); else clrscr(); Calibrate(PORTA); ; ; /* end of Calibrate */ void Selection(int PORTA, int StepsPerRev, int DR) /* Motor speed and position options */ int DegPerStep; int Choice; clrscr(); gotoxy(1,4); cprintf("Motor requires %d steps per rev\n", StepsPerRev);

Page 540: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

518518518518

FFFF

DegPerStep = (int)(360 / StepsPerRev); gotoxy(40,4); cprintf(" => thus 1 step = %d degrees\n", DegPerStep); do gotoxy(1,6); cprintf("(1) To rotate to a certain angle clockwise\n"); gotoxy(1,7); cprintf("(2) To rotate to a certain angle counter-clockwise\n"); gotoxy(1,8); cprintf("(3) To rotate continuously - with speed options\n"); gotoxy(1,9); cprintf("(4) To quit\n"); gotoxy(1,10); cprintf("Selection =>\n"); gotoxy(14,10); scanf("%d", &Choice); switch(Choice) case 1 : DR = 0; MoveToFixedAngle(PORTA, DR, DegPerStep); break; case 2 : DR = 8; MoveToFixedAngle(PORTA, DR, DegPerStep); break; case 3 : Continuous(PORTA, DR, StepsPerRev); break; case 4 : outportb(PORTA, 16); /* shutdown all signals */ gotoxy(1,22); cprintf("Quitting\n"); exit(0); default : gotoxy(1,14); cprintf("Choose 1, 2, 3, or 4\n"); break; ; /* end select */ while (1); /* can only exit Selection if user selects 4 */ ; /* end of Selection */ void MoveToFixedAngle(int PORTA, int DR, int DegPerStep) int Degrees; int NumberOfSteps; char *GetKey[2]; int WaitSomeTime = 0; int i; clrscr(); if(DR == 0) gotoxy(1,4); cprintf("Will step CW fixed number of degrees\n"); else /* DR is 8 */ gotoxy(1,4); cprintf("Will step CCW fixed number of degrees\n"); ; gotoxy(1,5); cprintf("Enter number of degrees => "); gotoxy(28,5); scanf("%d", &Degrees); gotoxy(1,7); cprintf("Will step %d degrees", Degrees); gotoxy(1,8); cprintf("Hit a key to start"); while (!kbhit()); NumberOfSteps = (int)(Degrees / DegPerStep); for(i = 1; i <= NumberOfSteps; i++) StepTheMotor(PORTA, DR, WaitSomeTime); ; gotoxy(1,10); cprintf("Finished!"); gotoxy(1,11); cprintf("Hit a key then <ENTER> to do another angle"); gotoxy(42,11); cprintf("or q then <ENTER> to quit to main menu"); scanf("%s", &GetKey); if(GetKey[0] == 'q' || GetKey[0] == 'Q') clrscr(); return; else MoveToFixedAngle(PORTA, DR, DegPerStep); ; ; /* end of MoveToFixedAngle */ void Continuous(int PORTA, int DR, int StepsPerRev) int WaitSomeTime; int TapKey; int SpeedGrade; int OE; clrscr(); gotoxy(1,2); cprintf("Tap a key:"); gotoxy(1,3); cprintf("(f)aster"); gotoxy(1,4); cprintf("(s)lower"); gotoxy(1,5); cprintf("(r)everse"); gotoxy(1,6); cprintf("(q)quit to main menu"); gotoxy(1,7); WaitSomeTime = 0;

Page 541: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

519519519519

FFFF

SpeedGrade = 1; do do StepTheMotor(PORTA, DR, WaitSomeTime); while(!kbhit()); TapKey = getch(); switch(TapKey) case 102 : /* hit f key */ WaitSomeTime = WaitSomeTime + 5; if(WaitSomeTime >= 100) WaitSomeTime = 100; gotoxy(1,10); cprintf("Can't go faster"); SpeedGrade = 20; else gotoxy(1,10); cprintf("Going faster..."); SpeedGrade = SpeedGrade + 1; ; gotoxy(1,12); cprintf("speed grade = %2d", SpeedGrade); StepTheMotor(PORTA, DR, WaitSomeTime); break; case 115 : /* hit s key */ WaitSomeTime = WaitSomeTime - 5; if(WaitSomeTime <= 0) WaitSomeTime = 0; gotoxy(1,10); cprintf("Won't go slower"); SpeedGrade = 0; else gotoxy(1,10); cprintf("Going slower..."); SpeedGrade = SpeedGrade - 1; ; gotoxy(1,12); cprintf("speed grade = %2d", SpeedGrade); StepTheMotor(PORTA, DR, WaitSomeTime); break; case 114 : /* hit the r key */ gotoxy(1,14); cprintf("Reversing direction"); delay(500); gotoxy(1,14); cprintf(" "); gotoxy(1,7); if(DR == 0) DR = 8; else DR = 0; ; StepTheMotor(PORTA, DR, WaitSomeTime); break; case 113 : /* hit the q key */ OE = 16; outportb(PORTA, OE); clrscr(); Selection(PORTA, StepsPerRev, DR); break; default : break; ; while (1); ; /* end of Continuous */ void StepTheMotor(int PORTA, int DR, int WaitSomeTime) int i; int SI = 4; int OE = 0; int FixedTime = 100; /* 100 msec */ outportb(PORTA, SI + DR + OE); SI = 0; OE = 0; delay(FixedTime-WaitSomeTime); /* 100 msec */ outportb(PORTA, SI + DR + OE); return; ; /* --- end of StepTheMotor --- */

Código 25-7 – Programa Motor de Passo

Page 542: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

520520520520

FFFF

CCaalleennddáárriioo

Programa que calcula o calendário de qualquer ano entre 0 e 9999 #include "conio.h" #include "stdio.h" #include "stdlib.h" int merd[30],ca[14]; main(int argc,char *argv[])

int i,j,k,m,nd[14],nn[14],r,c,ye[5],y,b,g; float y2; char *ch[5]; clrscr(); ca[1]=4; ca[2]=0; ca[3]=0; ca[4]=3; ca[5]=5; ca[6]=1; ca[7]=3; ca[8]=6; ca[9]=2; ca[10]=4; ca[11]=0; ca[12]=2; for (i=1;i<13;i+=2) nd[i]=31; nd[i+1]=30; if (i==7) i=6; nd[2]=28; c=3; j=1; for (i=0;i<29;i++) merd[i]=c; c++; if (c==8) c=1; j++; if (j==5) j=1; c++; if (c==8) c=1; printf("Este programa mostra o calend%crio de qualquer ano entre 0 e 9999.\n",160); if (argc<2) printf("Qual o ano desejado ? "); scanf("%d",&y); else y=atoi(argv[1]); if (y<0 || y>9999) printf("Escolha errada.\n"); exit(0); clrscr(); y2=(float) y/4; i=y/4; if (y2==i) nd[2]=29; printf("%41d\n",y); for (j=1;j<13;j++) nn[j]=1; printf(" Janeiro Fevereiro Marco\n"); for (b=0;b<12;b++) for (j=1;j<3;j++) printf("D S T Q Q S S "); printf("D S T Q Q S S\n"); for (k=1;k<7;k++) for (j=1;j<4;j++) if (k==1) ye[j]=detdia(y,j+b*3); ye[0]=(ye[j]-1)*3; if (ye[j]!=1) for (i=1;i<=ye[0];i++) printf(" "); for (i=1;i<9-ye[j];i++) printf("%d ",i); nn[j]=9-ye[j]; if (j!=3) printf(" "); else printf("\n");

Page 543: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

521521521521

FFFF

else for (i=nn[j];i<nn[j]+7;i++) if (i<nd[j+b*3]+1) if (i<10) printf("%d ",i); else printf("%d ",i); else printf(" "); nn[j]=7+nn[j]; if (j!=3) if (nn[j]<11) printf(" "); else printf(" "); else if (b==2 && k==6) getch(); printf(" "); /* if (nn[j]<9) printf(" "); */ if (nn[j]>33 && k==6 && b==3) for (;;) if(kbhit()) exit(0); switch (b) case 0 : printf(" Abril Maio Junho\n"); break; case 1 : printf(" Julho Agosto Setembro\n"); break; case 2 : clrscr(); printf(" Outubro Novembro Dezembro\n"); break; return; detdia(y,m) int y,m; int y1,j,yr,r,c; float y2; y1=y/28; r=y-y1*28; j=y/4; c=ca[m]; y2=(float) y/4; if (j==y2 && m==1) c=3; if (j==y2 && m==2) c=6; yr=merd[r]; yr+=c; if (yr>7) yr-=7; return(yr);

MMaanniippuullaaççããoo ddee ssttrriinnggss

Programa que manipula strings. #include<stdio.h> #include<stdlib.h> #include<conio.h> #define TAMANHO_MAXIMO 50 void left_str( char string[],int cont1,int esq); void right_str( char string[],int cont1,int dir); void mid_str(char string[],int offset,int quant);

Page 544: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

522522522522

FFFF

int cont_car(char palavra[]); void comp_str(); void convert_maius(); /************************************************************/ void main() int k,cont=0,esq=0,dir=0,offset=0,numcar=0; char palavra[TAMANHO_MAXIMO],tecla=0; do clrscr(); puts("[c] Compara duas Strings"); puts("[r] RIGHT$(i)"); puts("[l] LEFT$(i)"); puts("[m] MID$(offset,i)"); puts("[a] Converte minusculas em maiusculas"); puts("[e] EXIT"); tecla=getch(); if ( (tecla=='r') | (tecla=='l') | (tecla=='m') ) clrscr(); printf("\nENTRE COM A CADEIA DE CARACTERES\t"); scanf("%s",palavra); cont=cont_car(palavra); switch(tecla) case 'c' : clrscr(); comp_str(); break; case 'r' : printf("\n\n\t RIGHT$ (num.de caract.) =\t"); scanf("%d",&dir); right_str(palavra,cont,dir); break; case 'l' : printf("\n\n\t LEFT$ (num.de caract.) =\t"); scanf("%d",&esq); left_str(palavra,cont,esq); break; case 'm' : printf("\n\n\t MID$ offset =\t"); scanf("%d",&offset); printf("\t MID$ numero de caracteres =\t"); scanf("%d",&numcar); mid_str(palavra,offset,numcar); break; case 'a' : clrscr(); convert_maius(); break; case 'e' : clrscr(); exit(0); break; /*fim do switch */ while(tecla!='e'); /*fim do do/while */ /* fim do main */ /************************************************************/ void left_str(char string[],int cont1,int esq) int i=0; char *p; p=string; printf("\t Resultado de LEFT$(%d) : ",esq); for (i=0;(i<esq)&(esq<=cont1);i++) printf("%c",*p++); getch(); /************************************************************/ void right_str(char string[],int cont1,int dir) int i=0; char *p; p=string+(cont1-dir); printf("\t Resultado de RIGHT$(%d) : ",dir); for (i=(cont1-dir);(i<cont1)&(dir<=cont1);i++) printf("%c",*p++); getch(); /*************************************************************/ void mid_str(char string[],int offset,int numcar) int i=0; char *p; p=string+offset-1; printf("\t Resultado de MID$(%d,%d) : ",offset,numcar);

Page 545: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

523523523523

FFFF

for (i=0;(i<numcar)&(*p!='\x0');i++) printf("%c",*p++); getch(); /*************************************************************/ int cont_car(char palavra[]) int cont=0; while(palavra[cont]!='\x0') cont++; printf("O numero de caracteres e' %3.0d\n",cont); return(cont); /*************************************************************/ void comp_str() char string1[TAMANHO_MAXIMO],string2[TAMANHO_MAXIMO]; int cont1=0,cont2=0,i=0; clrscr(); printf("\nENTRE COM A PRIMEIRA CADEIA DE CARACTERES S1 : "); gets(string1); cont1=cont_car(string1); printf("\nENTRE COM A SEGUNDA CADEIA DE CARACTERES S2 : "); gets(string2); cont2=cont_car(string2); if (cont1!=cont2) printf("\nCadeias com numero de elementos diferentes \n"); for (i=0;(i<cont1)||(i<cont2);i++) if (string1[i]!=string2[i]) printf("Diferenca encontrada no elemento %d \n",i+1); else if(cont1!=cont2) printf("Elementos %d Iguais",i); getch(); /*************************************************************/ void convert_maius() int i; char string3[TAMANHO_MAXIMO]; clrscr(); printf("\nENTRE COM A CADEIA DE CARACTERES : "); gets(string3); for(i=0;i<TAMANHO_MAXIMO;i++) if(string3[i]>='a' && string3[i]<='z') string3[i]+='A'-'a'; printf("%s",string3); getch();

CCóóppiiaa ddee ssttrriinnggss

Programa que copia uma string em outra usando ponteiros /* Uso de ponteiros na manipulação de strings */ #include<stdio.h> #include<conio.h> void copstr() char string1[80],string2[80],tecla=0,*ptr; int i; while(tecla!=27) i=0; clrscr(); puts(" PROGRAMA QUE COPIA UMA STRING EM OUTRA "); printf("String1 = "); gets(string1); ptr=string1; while (*ptr!='\0') string2[i]=*ptr; ptr++; i++; string2[i]='\0'; printf("String2 = %s",string2); puts("\n ESC para sair "); tecla=getch();

Page 546: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

524524524524

FFFF

IInnvveerrssaaoo ddee ssttrriinnggss

Programa que escreve uma string ao contrário /* Uso de ponteiros na manipulação de strings */ #include<stdio.h> #include<conio.h> void tras() char string[80],tecla=0,*ptr; int i=0,cont; while(tecla!=27) cont=0; clrscr(); puts(" PROGRAMA QUE COPIA ESCREVE UMA STRING AO CONTRARIO "); printf("String = "); gets(string); ptr=string; while (*ptr!='\0') *ptr=string[cont]; ptr++; cont++; for(i=cont;i>=0;) printf("%c",*ptr--); i--; puts("\n ESC para sair "); tecla=getch();

CCoonnccaatteennaaççããoo ddee ssttrriinnggss

Uso de ponteiros para concatenar duas strings. /* Uso de ponteiros na manipulação de strings */ #include<stdio.h> #include<conio.h> void apend_string(char string1[],char string2[]); void apend() char string1[80],string2[80],tecla=0; while(tecla!=27) clrscr(); puts(" PROGRAMA QUE CONCATENA DUAS STRINGS "); printf("String1 = "); gets(string1); printf("String2 = "); gets(string2); apend_string(string1,string2); puts("\n ESC para sair "); tecla=getch(); void apend_string (str1,str2) char *str1;char *str2; char *str3; char *base; base=str3; while (*str1!='\0') *str3=*str1; if(*str1!='\0') str1++; str3++; while (*str2!='\0') *str3=*str2; str2++;

Page 547: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

525525525525

FFFF

str3++; *str3='\0'; str3=base; while(*str3!='\0') printf("%c",*str3++);

NNúúmmeerrooss CCoommpplleexxooss

Programa que manipula números complexos #include<stdio.h> #include<math.h> #include<conio.h> struct complejo input_data(void); struct complejo multiplicacao(struct complejo,struct complejo); struct complejo divisao(struct complejo,struct complejo); struct complejo raiz(struct complejo); double angulo(struct complejo); double modulo(struct complejo); struct complejo double real; double imaginario; ; /********************************************************************/ void main() char operacao; struct complejo z1,z2,z3; do clrscr(); z1=input_data(); printf("\nEntre com o tipo de operacao : +,-,*,/,r :"); operacao=getche(); if( operacao!='r') z2=input_data(); switch(operacao) case '+':printf("\nA soma e' igual a %lf +j (%lf)", z1.real+z2.real,z1.imaginario+z2.imaginario); break; case '-':printf("\nA diferenca e' igual a %lf +j (%lf)", z1.real-z2.real,z1.imaginario-z2.imaginario); break; case '*':z3=multiplicacao(z1,z2); printf("\nA multiplicacao a %lf +j (%lf)", z3.real,z3.imaginario); break; case '/':z3=divisao(z1,z2); printf("\nA divisao e' igual a %lf +j (%lf)", z3.real,z3.imaginario); break; case 'r':z3=raiz(z1); printf("\nA raiz e' igual a %lf +j (%lf)", z3.real,z3.imaginario); break; default: puts("\nESCOLHA ERRADA"); break; operacao=getch(); while(operacao!=27); /********************************************************************/ struct complejo input_data() struct complejo z; printf("\nEntre com o numero : real,imaginario :"); scanf("%lf,%lf",&z.real,&z.imaginario); return(z); /********************************************************************/ struct complejo multiplicacao(z1,z2) struct complejo z1,z2; struct complejo z; double m1,m2,a1,a2,prod,ang;

Page 548: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

526526526526

FFFF

m1=modulo(z1); m2=modulo(z2); prod=m1*m2; a1=angulo(z1); a2=angulo(z2); ang=a1+a2; z.real=prod*cos(ang); z.imaginario=prod*sin(ang); return(z); /********************************************************************/ double modulo(z) struct complejo z; double modulo; modulo=pow(z.real*z.real+z.imaginario*z.imaginario,0.5); return(modulo); /********************************************************************/ double angulo(z) struct complejo z; double angulo; angulo=atan(z.imaginario/z.real); return(angulo); /********************************************************************/ struct complejo divisao(z1,z2) struct complejo z1,z2; struct complejo z; double m1,m2,a1,a2,div,ang; m1=modulo(z1); m2=modulo(z2); div=m1/m2; a1=angulo(z1); a2=angulo(z2); ang=a1-a2; z.real=div*cos(ang); z.imaginario=div*sin(ang); return(z); /********************************************************************/ struct complejo raiz(zz) struct complejo zz; struct complejo z; z.real=pow(modulo(zz),0.5)*cos(angulo(zz)/2.0); z.imaginario=pow(modulo(zz),0.5)*sin(angulo(zz)/2.0); return(z); /********************************************************************/

IInntteeggrraaççããoo

Calculo de integrais pelos metodos de Simpson, Trapezios, Gauss-Legendre e Romberg #include <stdio.h> #include <conio.h> #include <dos.h> #include <graphics.h> #include <math.h> #define PI 3.1415926536 void rombergmenu(); void trapezmenu(); void gausslmenu(); void simpsonmenu(); void entr_vetort(); void entr_funct(); float trapezd(float,int); void entr_vetors(); void entr_funcs(); float simpson(float,int); void menut(); void menus(); float fun(float ); float romberg(float,float,float); float a,b,deltaaux,err,funcion[100],x,integral=0,delta;

Page 549: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

527527527527

FFFF

char c,esc; double I=0,II=0,sum1=0,sum2=0,o,oo,ooo; float err; int i,ii,n=0,np,naba; double I,II; float lli,lls; int mm,m; float ek[4],wk[4],x1,x2,x3,x4,cm,cn,INTE; float y,T[10][10],X,sum,eps; int j,k,l; /**************** ROMBERG ***************************************/ float romberg(float a,float b,float eps) T[1][1]=(b-a)/2*(fun(a)+fun(b)); T[1][2]=T[1][1]/2+(b-a)/2*(fun((a+b)/2)); T[2][1]=(4*T[1][2]-T[1][1])/3; j=2; it: j++; delta=(b-a)/pow(2,(j-1)); x=a-delta; n=pow(2,(j-2)); sum=0; for(i=1;i<=n;i++) x=x+2*delta; sum=sum+fun(x); T[1][j]=T[1][j-1]/2+delta*sum; for(l=2;l<=j;l++) k=j+1-l; T[l][k]=(pow(4,(l-1))*T[l-1][k+1]-T[l-1][k])/(pow(4,l-1)-1); if( fabs((T[j][1]-T[j-1][1])/T[j][1]) > eps ) goto it; return(T[j][1]); /*******************************************************************/ float fun(float x) y= /****/ ( cos(x)-1)/x ; /****** Funcao f(x) ********/ return(y); /********************************************************************/ void rombergmenu() window(1,1,80,25); textbackground(0); ii: clrscr(); printf(" CALCULO DE INTEGRACAO PELO METODO DE ROMBERG \n\n "); printf(" Funcao deve estar no programa fonte \n\n"); printf("Entre com o limite inferior a="); scanf("%f",&a); printf("Entre com o limite superior b="); scanf("%f",&b); printf("entre com o erro (epsilon)= "); scanf("%f",&eps); I=romberg(a,b,eps); printf("Valor da integral = %f\n",I); printf("Novos dados ?? S ou N \n"); esc=getch(); if (esc=='s') goto ii; clrscr(); prt_menu();

Page 550: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

528528528528

FFFF

/******************** GAUSS-LEGENDRE ********************************/ float func(float x) float y; y=/*****/ pow(x,2)*cos(x) ; /******** FUNCION f(x) ****************/ return(y); /************************************************************************/ float gaussleg( float lli,float lls,int mm) cm=(lls+lli)/2; cn=(lls-lli)/2; if (mm==2) ek[0]=-.5773502692; ek[1]=.5773502692; wk[0]=1; wk[1]=1; x1=cm+cn*ek[0]; x2=cm+cn*ek[1]; I=cn*(func(x1)+func(x2)); if (mm==3) ek[0]=-.7745966692; ek[1]=0; ek[2]=.7745966692; wk[0]=.5555555556; wk[1]=.8888888889; wk[2]=.5555555556; x1=cm+cn*ek[0]; x3=cm+cn*ek[2]; x2=cm; I=cn*(func(x1)*wk[0]+func(x3)*wk[2]+func(x2)*wk[1]); if (mm==4) ek[0]=-.8611363116; ek[1]=-.3399810436; ek[2]=.3399810436; ek[3]=.8611363116; wk[0]=.3478548451; wk[1]=.6521451549; wk[2]=.6521451549; wk[3]=.3478548451; x1=cm+cn*ek[0]; x2=cm+cn*ek[1]; x3=cm+cn*ek[2]; x4=cm+cn*ek[3]; I=cn*(func(x1)*wk[0]+func(x2)*wk[1]+func(x3)*wk[2]+func(x4)*wk[3]); return (I); /***********************************************************************/ void gausslegmenu()

Page 551: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

529529529529

FFFF

window(1,1,80,25); textbackground(0); a=0; b=0; INTE=0; m=0; clrscr(); printf(" INTEGRACAO PELO METODO DE GAUSS-LEGENDRE \n "); getch(); printf(" Funcao deve estar no programa fonte \n\n"); printf(" LIMITE INFERIOR DA INTEGRAL A= "); scanf("%f",&a); printf(" LIMITE SUPERIOR DA INTEGRAL B= "); scanf("%f",&b); printf(" ORDEM DO POLINOMIO DE LEGENDRE (2,3,4) M= "); scanf("%d",&m); INTE=gaussleg(a,b,m); printf(" O Valor aproximado da integral e I= %f",INTE); getch(); prt_menu(); /*************** TRAPEZIOS ***************************************/ float trapezd(float delta,int n) integral=funcion[0]+funcion[n]; for(i=1;i<n;i=i+1 /*delta*/) integral=integral+2*funcion[i]; integral=integral*delta/2; return(integral); /**************************************************************/ void entr_vetort() void menut(); clrscr(); printf(" CALCULO DA INTEGRAL PELO METODO DOS TRAPEZIOS \n\n "); printf("Limite inferior a="); scanf("%f",&a); printf("Limite superior b="); scanf("%f",&b); eer: printf("(Intervalos constantes) delta (step) = "); scanf("%f",&delta); n= (int) ((b-a)/delta+.5); /* n e o numero de intervalos */ if (n>=100) err=(b-a)/(100-0.5); clrscr(); printf(" ERRO NO DIMENSIONAMENTO DA MATRIZ\n"); printf(" Entre com o novo Delta minimo = %f \n",err); goto eer; delta=(b-a)/n; clrscr(); deltaaux=a; for(i=0;i<n;i++) printf("f[%f]= ",deltaaux);

Page 552: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

530530530530

FFFF

deltaaux+=delta; scanf("%f",&funcion[i]); printf("f[%f]= ",b); scanf("%f",&funcion[i]); I=trapezd(delta,n); printf("Integral= %f\n ",I); II=I-delta/12*((funcion[n]-funcion[n-1])-(funcion[1]-funcion[0])); printf("Integral com ajuste nas pontas= %f\n ",II); getch(); menut(); /*********************************************************************/ void menut() init: clrscr(); gotoxy(10,2); printf("[0] Entrada da funcao matematica\n"); gotoxy(10,3); printf("[1] Entrada de pontos tabulados igualmente\n"); gotoxy(10,4); printf("[3] EXIT \n"); c=getch(); switch(c) case '0': entr_funct(); break; case '1': entr_vetort(); break; case '3': break; default : menut(); break; prt_menu(); /*****************************************************************/ void entr_funct() ii: clrscr(); printf(" CALCULO DA INTEGRAL PELO METODO DOS TRAPEZIOS \n\n "); printf(" Funcao deve estar no programa fonte \n\n"); printf("Entre com o limite inferior a="); scanf("%f",&a); printf("Entre com o limite superior b="); scanf("%f",&b); ee: printf("entre com o deltax = "); scanf("%f",&delta); n= (int) ((b-a)/delta+.5); /* n e o numero de intervalos */ if (n>=100) err=(b-a)/(100-0.5); clrscr(); printf(" ERRO NO DIMENSIONAMENTO DA MATRIZ !\n"); printf(" Entre com o novo Delta minimo = %f \n",err); goto ee; delta=(b-a)/n; ii=0; for(x=a;x<b;x+=delta)

Page 553: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

531531531531

FFFF

funcion[ii]=/*****/ sin(x) ;/*** f(x) ***** FUNCAO ***/ ii++; funcion[ii]=/*****/ sin(b) ;/*** f(b) ***** FUNCAO ***/ I=trapezd(delta,n); printf("Integral = %f \n",I); II=I-delta/12*((funcion[n]-funcion[n-1])-(funcion[1]-funcion[0])); printf("Integral com ajuste nas pontas= %f\n ",II); getch(); clrscr(); printf("Novos dados ?? S ou N \n"); esc=getche(); if (esc=='s') goto ii; menut(); /**********************************************************************/ void trapezmenu() window(1,1,80,25); textbackground(0); clrscr(); menut(); /******************* SIMPSON ***************************************/ float simpson(float delta,int n) sum1=sum2=integral=0; for(i=1;i<=(n-1);i=i+2) sum1=sum1+funcion[i]; for(i=2;i<=(n-2);i=i+2) sum2=sum2+funcion[i]; integral=delta/3*(funcion[0]+funcion[n]+4*sum1+2*sum2); return(integral); /*****************************************************************/ void entr_funcs() ii: clrscr(); printf(" CALCULO DA INTEGRAL PELO METODO DE SIMPSON \n\n "); printf(" Funcao deve estar no programa fonte \n\n"); printf("Limite inferior a="); scanf("%f",&a); printf("Limite superior b="); scanf("%f",&b); ee: printf(" deltax = "); scanf("%f",&delta); n= (int) ((b-a)/delta+.5); /* n e o numero de intervalos */ if( n&0x01 ) n=n+1; /* n deve ser par para este metodo */ if (n>=100) err=(b-a)/(n-0.5);

Page 554: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

532532532532

FFFF

clrscr(); printf(" ERRO NO DIMENSIONAMENTO DA MATRIZ !\n"); printf(" Entre com o novo Delta minimo = %f \n",err); goto ee; delta=(b-a)/n; x=a; for(ii=0;ii<n;ii++) funcion[ii]=/*****/ sin(x) ; /*** f(x) ***** FUNCAO ***/ x=x+delta; funcion[n]=/*****/ sin(b) ;/*** f(b) ***** FUNCAO ***/ I=simpson(delta,n); printf("Integral = %f \n",I); o=14*(.5*(funcion[0]+funcion[n])+sum2); oo=16*sum1; ooo=-funcion[0]+funcion[1]+funcion[n-1]-funcion[n]; II=delta/15*(o+oo+ooo); /*(delta/15)*( 14*( .5*( funcion[0]+funcion[n]) + sum2) + 16*sum1+ (funcion[1]-funcion[0]-funcion[n]+funcion[n-1]));*/ printf("Integral com ajuste nas pontas= %f\n ",II); getch(); clrscr(); printf("Novos dados ?? S ou N \n"); esc=getche(); if (esc=='s') goto ii; menus(); /**********************************************************************/ void entr_vetors() clrscr(); printf(" CALCULO DA INTEGRAL PELO METODO DE SIMPSON \n\n "); printf("Entre com o limite inferior a="); scanf("%f",&a); printf("Entre com o limite superior b="); scanf("%f",&b); eer: printf("(intervalos constantes) delta = "); scanf("%f",&delta); n= (int) ((b-a)/delta+.5); /* n e o numero de intervalos */ if( n&0x01 ) n=n+1; /* n deve ser par para este metodo */ if (n>=100) err=(b-a)/(n-0.5); clrscr(); printf(" ERRO NO DIMENSIONAMENTO DA MATRIZ !\n"); printf(" Entre com o novo Delta minimo = %f \n",err); goto eer; delta=(b-a)/n; clrscr(); deltaaux=a; for(i=0;i<n;i++) printf("f[%f]= ",deltaaux); deltaaux+=delta; scanf("%f",&funcion[i]);

Page 555: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

533533533533

FFFF

printf("f[%f]= ",b); scanf("%f",&funcion[i]); I=simpson(delta,n); printf("Integral= %f\n ",I); II=delta/15*(14*(.5*(funcion[0]+funcion[n])+sum2)+16*sum1+(funcion[1]-funcion[0]-funcion[n]+funcion[n-1])); printf("Integral com ajuste nas pontas= %f\n ",II); getch(); menus(); /*********************************************************************/ void menus() init: clrscr(); gotoxy(10,2); printf("[0] Entrada da funcao matematica\n"); gotoxy(10,3); printf("[1] Entrada de pontos tabulados igualmente\n"); gotoxy(10,4); printf("[3] EXIT \n"); c=getch(); switch(c) case '0': entr_funcs(); break; case '1': entr_vetors(); break; case '3': break; default : menus(); break; prt_menu(); /********************************************************************/ void simpsonmenu() window(1,1,80,25); textbackground(0); clrscr(); menus(); /************************** MAIN *******************************/ void main() clrscr(); presentacion(); prt_menu(); window(1,1,80,25); textbackground(0); clrscr(); /***************************************************************************/ prt_menu() char tecla; inicio:

Page 556: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

534534534534

FFFF

window(1,1,80,25); textbackground(0); clrscr(); window(22,6,64,16); textbackground(5); clrscr(); window(24,7,66,17); textbackground(7); textcolor(0); clrscr(); gotoxy(14,3); textcolor(0); cputs(" CALCULO DE INTEGRAIS "); gotoxy(6,5); textcolor(1); cputs("[ ] Metodo dos Trapezios "); gotoxy(7,5); textcolor (5); cputs("T"); gotoxy(6,6); textcolor(1); cputs("[ ] Metodo de Simpson "); gotoxy(7,6); textcolor (5); cputs("S"); gotoxy(6,7); textcolor(1); cputs("[ ] Metodo de Romberg "); gotoxy(7,7); textcolor (5); cputs("R"); gotoxy(6,8); textcolor(1); cputs("[ ] Metodo de Gauss-Legendre "); gotoxy(7,8); textcolor (5); cputs("G"); gotoxy(6,9); textcolor(1); cputs("[ ] EXIT"); gotoxy(7,9); textcolor (5); cputs("E"); textcolor(7); tecla=getch(); switch(tecla) case ('T'|'t') : trapezmenu(); break; case ('S'|'s') : simpsonmenu(); break; case ('R'|'r') : rombergmenu(); break; case ('G'|'g') : gausslegmenu(); break; case ('E'|'e') : window(1,1,80,25); textbackground(0); clrscr(); textcolor(15); normvideo(); exit(); break; default : goto inicio; return(0);

Page 557: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

535535535535

FFFF

/***************************************************************************/ presentacion() window(1,1,80,25); textbackground(0); clrscr(); window(20,5,64,15); textbackground(7); clrscr(); window(18,4,62,14); textbackground(9); clrscr(); gotoxy(6,2); textcolor(11); cputs("Luis F. Espinosa Cocian &"); gotoxy(6,3); textcolor(11); cputs("Miriam Caceres Nene "); gotoxy(6,6); textcolor(2); cputs("INMETAL SET,1993"); gotoxy(6,7); textcolor(4); cputs("Copyright."); getch(); return(0); /****************************************************************************/

RRuunnggee KKuuttttaa

Cálculo de solução de equações diferenciais usando o metodo de Runge-Kutta. /* METODO DE RUNGE-KUTTA PARA SOLUCAO DE EQUACOES DIFERENCIAIS */ #include<stdio.h> #include<math.h> #include<conio.h> float funcy(float y,float z,float v,float w,float t); float funcz(float y,float z,float v,float w,float t); float funcv(float y,float z,float v,float w,float t); float funcw(float y,float z,float v,float w,float t); void main(void) int equacoes,i,ni; float ddtt=0,dt=0,t = 0; float y = 0,ya1,ya12,yaa12,y1[50]; float z = 0,za1,za12,zaa12,z1[50]; float v = 0,va1,va12,vaa12,v1[50]; float w = 0,wa1,wa12,waa12,w1[50]; clrscr(); textcolor(3); cprintf("\n Solucao de Equacoes Diferenciais pelo Metodo de Runge-Kutta\n\r"); textcolor(14); cprintf("\nNumero de Equacoes ou Sistemas ?= "); scanf("%d",equacoes); cprintf("\nValor do Step ? = "); scanf("%e",&dt); cprintf("\nNumero de Iteracoes? = "); scanf("%d",&ni); cprintf("\nValor Inicial y(0)? = ");

Page 558: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

536536536536

FFFF

scanf("%e",&y); cprintf("\nValor Inicial dy/dt (0)? = "); scanf("%e",&z); cprintf("\nValor Inicial d^2(y)/dt^2 (0)? = "); scanf("%e",&v); cprintf("\nValor Inicial d^3(y)/dt^3 (0)? = "); scanf("%e",&w); for(i = 0;i < ni;i++) ya12 = (dt/2) * funcy(y,z,v,w,t) + y; za12 = (dt/2) * funcz(y,z,v,w,t) + z; va12 = (dt/2) * funcv(y,z,v,w,t) + v; wa12 = (dt/2) * funcw(y,z,v,w,t) + w; yaa12 = (dt/2) * funcy(ya12,za12,va12,wa12,t+dt/2) + y; zaa12 = (dt/2) * funcz(ya12,za12,va12,wa12,t+dt/2) + z; vaa12 = (dt/2) * funcv(ya12,za12,va12,wa12,t+dt/2) + v; waa12 = (dt/2) * funcw(ya12,za12,va12,wa12,t+dt/2) + w; ya1 = (dt) * funcy(yaa12,zaa12,vaa12,waa12,t+dt/2) + y; za1 = (dt) * funcz(yaa12,zaa12,vaa12,waa12,t+dt/2) + z; va1 = (dt) * funcv(yaa12,zaa12,vaa12,waa12,t+dt/2) + v; wa1 = (dt) * funcw(yaa12,zaa12,vaa12,waa12,t+dt/2) + w; z1[i] = z+dt*(funcz(y,z,v,w,t)/6+funcz(ya12,za12,va12,wa12,t+dt/2)/3+funcz(yaa12,zaa12,vaa12,waa12,t+dt/2)/3+funcz(ya1,za1,va1,wa1,t+dt)/6); y1[i] = y+dt*(funcy(y,z,v,w,t)/6+funcy(ya12,za12,va12,wa12,t+dt/2)/3+funcy(yaa12,zaa12,vaa12,waa12,t+dt/2)/3+funcy(ya1,za1,va1,wa1,t+dt)/6); v1[i] = v+dt*(funcv(y,z,v,w,t)/6+funcv(ya12,za12,va12,wa12,t+dt/2)/3+funcv(yaa12,zaa12,vaa12,waa12,t+dt/2)/3+funcv(ya1,za1,va1,wa1,t+dt)/6); w1[i] = w+dt*(funcw(y,z,v,w,t)/6+funcw(ya12,za12,va12,wa12,t+dt/2)/3+funcw(yaa12,zaa12,vaa12,waa12,t+dt/2)/3+funcw(ya1,za1,va1,wa1,t+dt)/6); y = y1[i]; z = z1[i]; v = v1[i]; w = w1[i]; t = t + dt; ddtt=dt*(i+1); printf("y[%f]=%f\n",ddtt,y1[i]); getch(); if(equacoes >= 1) printf("\n\ny = %f",y); if(equacoes >= 2) printf("\n\nz = %f",z); if(equacoes >= 3) printf("\n\nv = %f",v); if(equacoes == 4) printf("\n\nw = %f",w); getch(); float funcy(float y,float z,float v,float w,float t) float rsd; rsd = z; /*************** DERIVADA DE ORDEM 3 **************/ return(rsd); float funcz(float y,float z,float v,float w,float t) float rsd; rsd = v; /*************** DERIVADA DE ORDEM 2 **************/ return(rsd); float funcv(float y,float z,float v,float w,float t) float rsd; rsd = 10 * sin(6 * t) - 3 * y + z * z - 2 * v; /*** DERIVADA ORDEM 1 ***/

Page 559: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

537537537537

FFFF

return(rsd); float funcw(float y,float z,float v,float w,float t) float rsd; rsd = 0; /**************** DERIVADA DE ORDEM 0 **************/ return(rsd);

AAuuttoovvaalloorreess ddee uummaa mmaattrriizz -- HHoouusseehhoollddeerr

Calculo dos autovalores de uma matriz pelo método de Householder. /* Calculo dos autovalores de uma matriz pelo metodo de householder's */ #include <stdio.h> #include <conio.h> #include <stdlib.h> #include<graphics.h> #include "calcnum.h" char inigraficos(void); float axx,axy,mdy,mdx; char tecla; /******* Funcoes *********/ /* realiza o produto entre duas matrizes */ void prod_mat( MATF a, MATF b, MATF c, int ord) int i,j,ii; for (i=0;i<ord;i++) for (j=0;j<ord;j++) c[i][j]=0; for (ii=0;ii<ord;ii++) c[i][j]+=a[i][ii]*b[ii][j]; /* Algoritmo de householder */ void househld(MATF h, int n) int k,m,i,j; double b,s,d,e; MATF t,a; VETF u; t=(MATF)malloc(sizeof(VETF)*n); for (i=0;i<n;i++) t[i]=(VETF)malloc(sizeof(double)*n); a=(MATF)malloc(sizeof(VETF)*n); for (i=0;i<n;i++) a[i]=(VETF)malloc(sizeof(double)*n); u=(VETF)calloc(n,sizeof(double)); for (k=0;k<(n-2);k++) m=k+1; b=0; for (i=m;i<n;i++) b+=(h[i][k]*h[i][k]); s=sqrt(b); if (sqrt(pow(h[m][k]+s,2)) >=sqrt(pow(h[m][k]-s,2))) u[m]=h[m][k]+s; d=(s*s)+h[m][k]*s; else u[m]=h[m][k]+s; d=(s*s)+h[m][k]*s; for(i=m+1;i<n;i++) u[i]=h[i][k];

Page 560: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

538538538538

FFFF

for(j=m;j<n;j++) t[j][k]=0; t[k][j]=0; t[k][k]=1; for(i=m;i<n;i++) for(j=m;j<n;j++) if(i==j) e=1; else e=0; t[i][j]=e-u[i]*u[j]/d; prod_mat(t,h,a,n); prod_mat(a,t,h,n); return; void ql(MATF h, int n) int i,l; VETF d,e; d=(MATF)malloc(n*sizeof(VETF)); for(i=0;i<n;i++) d=(VETF)calloc(n,sizeof(double)); e=(MATF)malloc(n*sizeof(VETF)); for(i=0;i<n;i++) e=(VETF)calloc(n,sizeof(double)); for (i=0;i<n;i++) d[i]=h[i][i]; for (i=0;i<n-1;i++) e[i]=h[i][i+1]; e[n]=0; for(l=0;l<n;l++) void main(void) int i,ii; int n; FILE *arq; MATF h; double aux; char n_arq[13]; clrscr(); n_arq[0]=inigraficos(); puts(""); textcolor(3); cprintf("\n\n Tridiagonalizacao de Matrizes pelo Metodo de Householder\r\n"); switch(n_arq[0]) case 's': case 'S': printf("\n\nNome do arquivo :"); scanf("%s",n_arq); printf("Lendo %s\n",n_arq); if((arq=fopen(n_arq,"r"))==NULL) printf("Arquivo %s nao encontrado",n_arq); exit(1); /* lendo ordem do sistema */ fscanf(arq,"%d",&n); h=(MATF)malloc(sizeof(VETF)*n); for (i=0;i<n;i++) h[i]=(VETF)malloc(sizeof(double)*n); /* leitura dos dados */ for (i=0;i < (n);i++) for (ii=0;ii < n;ii++)

Page 561: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

539539539539

FFFF

fscanf(arq,"%lf",&aux); h[i][ii]=aux; fclose(arq); printf("\n\nMatriz de ordem:%d\n",n); puts("Coeficientes da matriz:\n"); for (i=0;i<(n);i++) for (ii=0;ii<(n);ii++) printf("%f ",h[i][ii]); printf("\n"); break; default: printf("Ordem da matriz?:"); scanf("%d",&n); h=(MATF)malloc(sizeof(VETF)*n); for (i=0;i<n;i++) h[i]=(VETF)malloc(sizeof(double)*n); puts("Entre com os coeficientes da matriz:"); for (i=0;i <(n);i++) for (ii=0;ii < n;ii++) printf("A[%d,%d]:",i,ii); scanf("%lf",&aux); h[i][ii]=aux; printf("Gravar arquivo(s/N):"); n_arq[0]=getche(); puts(""); if (n_arq[0]=='s'||n_arq[0]=='S') printf("Nome do arquivo :"); scanf("%s",n_arq); if ((arq=fopen(n_arq,"wa"))==NULL) puts("Erro na escrita do arquivo"); break; fprintf(arq,"%d\n",n); for (i=0;i <(n);i++) for (ii=0;ii < n;ii++) fprintf(arq,"%f ",h[i][ii]); fclose(arq); break; househld(h,n); puts("\n\nCoeficientes da matriz solucao:\n"); if (h[0][1]<0) h[0][1]*=-1; if (h[1][0]<0) h[1][0]*=-1; if (h[1][2]<0) h[1][2]*=-1; if (h[2][1]<0) h[2][1]*=-1; if (h[2][3]<0) h[2][3]*=-1; if (h[3][2]<0) h[3][2]*=-1; if(n>=5) if (h[3][4]<0) h[3][4]*=-1; if (h[4][3]<0) h[4][3]*=-1; if (n>=6) if (h[4][5]<0) h[4][5]*=-1; if (h[5][4]<0) h[5][4]*=-1; if (n>=7) if (h[5][6]<0) h[5][6]*=-1; if (h[6][5]<0) h[6][5]*=-1; if (n>=8) if (h[6][7]<0) h[6][7]*=-1; if (h[7][6]<0) h[7][6]*=-1; for (i=0;i<(n);i++)

Page 562: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

540540540540

FFFF

for (ii=0;ii<(n);ii++) printf("%f ",h[i][ii]); printf("\n"); puts(""); getch(); char inigraficos(void) int graphdriver, graphmode; graphdriver = DETECT; initgraph(&graphdriver,&graphmode, "d:\\lpro\\tcpp\\bgi"); setbkcolor(LIGHTGRAY); setcolor(BLUE); axx=getmaxx()/2; axy=getmaxy()/2; mdy=axy/10; mdx=axx/10; settextjustify(CENTER_TEXT,TOP_TEXT); settextstyle(GOTHIC_FONT,HORIZ_DIR,2); moveto (axx,axy/4);outtext("Programa de Householder"); settextstyle(DEFAULT_FONT,HORIZ_DIR,1); moveto (axx,axy/8*3);outtext("Miriam Nene & Luis Fernando"); moveto (axx,axy/8*5);outtext("INMETAL, setembro 93"); settextjustify(LEFT_TEXT,TOP_TEXT); moveto (axx,mdy*11);outtext(" Ler Arquivo s/n ??"); moveto (axx,mdy*12);outtext(""); moveto (axx,mdy*13);outtext(""); tecla=getch(); closegraph(); return(tecla); /* calcnum.h */ /* Tipos de dados e prototipos de funcoes */ /* Usados em programas de calculo numerico */ #include <math.h> #include <alloc.h> /******** Tipos de Dados *********************/ typedef double *VETF; /* ponteiro para vetor de floats */ typedef VETF *MATF; /* ponteiro para matriz de floats */ typedef struct int ord; MATF c ; VETF ti; SISL; /* ponteiro para estrutura de sistema linear */ typedef struct /* tabela de dados*/ int n_dados; VETF x; VETF fx; SETDATA;

CCaallccuulloo ddee ππ ppeelloo MMééttooddoo ddee MMoonntteeccaarrlloo

Calculo do valor aproximado de π pelo método de Montecarlo. /* CALCULO DO VALOR APROXIMADO DE pi PELO METODO DE MONTECARLO */ #include <stdio.h> #include <conio.h> #include <graphics.h> #include <stdlib.h> #include <time.h> #include <math.h> #include <string.h> #define sq(x) ((x)*(x))

Page 563: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

541541541541

FFFF

#define ESQ 220 #define SUP 80 #define RAIO 120 int ii=0; void desenha_quadrado(void) /* desenha um quadrado com um circulo inserido */ setcolor(EGA_WHITE); setlinestyle(SOLID_LINE,0,NORM_WIDTH); rectangle(ESQ,SUP,ESQ+2*RAIO,SUP+2*RAIO); circle(ESQ+RAIO,SUP+RAIO,RAIO); return; void main (void) int cor; int graphdriver=VGA, graphmode=VGAHI; /* definindo o modo grafico */ int x,y; /* coordenadas sorteadas */ float dentro_circulo=0; /* contador dos pontos internos ao circulo */ float n_sort,pi_calc,i; /* resultado */ char aux[20],aux1[10],aaux[20],aaux1[20]; /* Primeiro vou rodar o bgiobj egavga e outros e botar num projeto junto ao programa*/ randomize(); clrscr(); gotoxy(0,0); puts(" \n CALCULO DO VALOR DE PI PELO METODO DE MONTE CARLO"); printf("\n\nNUMERO DE PONTOS = "); scanf("%f",&n_sort); registerbgifont(gothic_font); registerbgifont(sansserif_font); registerbgidriver(EGAVGA_driver); initgraph(&graphdriver,&graphmode,""); settextstyle(GOTHIC_FONT,HORIZ_DIR,4); setbkcolor(BLUE); cleardevice(); desenha_quadrado(); outtextxy(20,20,"Calculo de pi pelo metodo de Monte Carlo"); settextstyle(SANS_SERIF_FONT,HORIZ_DIR,1); outtextxy(200,330,"Calculando iteracao "); for (i=0;i<n_sort;i++) x=random(2*RAIO+1); y=random(2*RAIO+1); if (sqrt(sq(x-RAIO)+sq(y-RAIO))<=(float)RAIO) dentro_circulo++; cor=EGA_LIGHTRED; else cor=EGA_YELLOW; putpixel(x+ESQ,y+SUP,cor); ii++; gotoxy(48,22); printf("%d",ii); desenha_quadrado(); pi_calc=4*dentro_circulo/n_sort; gcvt(pi_calc,8,aux1); strcpy(aux,"Pi calculado = "); strcat(aux,aux1); outtextxy(200,390,aux); gcvt(fabs(100-(pi_calc/3.1415926536)*100),8,aaux1); strcpy(aaux,"Erro de = "); strcat(aaux,aaux1); outtextxy(200,370,aaux); outtextxy(450,370,"%"); getch(); closegraph();

Page 564: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

542542542542

FFFF

BBáásskkaarraa

Programa que calcula as raizes de um polinômio de segundo grau. #include <stdio.h> #include <math.h> #include <conio.h> void main(void) double dA,dB,dC,dRaiz1,dRaiz2,dBeta; puts("Programa que calcula raizes de um polinomio de grau 2 pelo metodo de Baskara "); puts("Entre com os coeficientes do polinomio Ax^2 + Bx + C = 0"); // entrada de dados printf("\nA = "); scanf("%lf",&dA); printf("\nB = "); scanf("%lf",&dB); printf("\nC = "); scanf("%lf",&dC); // cálculo das raízes -b +- sqrt(b^2 - 4ac) / 2a dBeta = pow(dB,2.0) - 4.0 * dA * dC; if (dBeta < 0) // verifica se o valor dentro da raíz é negativo puts("Polinomio possui raizes complexas"); return; else dRaiz1 = (- dB + sqrt(dBeta)) / (2.0 * dA); dRaiz2 = (- dB - sqrt(dBeta)) / (2.0 * dA); printf("\nA 1a. raiz e: %f",dRaiz1); printf("\nA 2a. raiz e: %f",dRaiz2); getch();

FFóórrmmuullaa

Programa que calcula a formula Aac

CxBxAxT

tanln

32

+++= para dez valores de x.

#include <stdio.h> #include <math.h> #include <conio.h> void main(void) double dA,dB,dC,dT,dx[10]; int i; puts("Programa que calcula a formula T = (Ax + Bx^2 + Cx^3)/ (ln(C) + atan(A)) "); puts("para 10 valores de x"); puts("Entre com os coeficientes da formula"); // entrada de dados printf("A = "); scanf("%lf",&dA); printf("B = "); scanf("%lf",&dB); printf("C = "); scanf("%lf",&dC); // entrada dos valores de x puts("Entre com os valores de x "); for(i=0;i<9;i++) printf("dx[%d] = ",i); scanf("%lf",&dx[i]);

Page 565: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

543543543543

FFFF

// cálculo dos valores de T puts(""); puts("========"); puts("RESPOSTA"); puts("========"); // cout for(i=0;i<10;i++) dT = (dA * dx[i] + dB * pow(dx[i],2) + dC * pow(dx[i],3))/(log(dC) + atan(dA)); printf("\nO valor de T(x = %lf) = %lf",dx[i],dT); getch();

FFaattoorriiaall

Programa que calcula o fatorial de um número. #include <stdio.h> #include <conio.h> void main(void) int nfatorial = 0; int nCont, nresult = 1; puts("Programa que calcula o fatorial de um numero"); // entrada de dados printf("Entre com um numero "); scanf("%d",&nfatorial); if(nfatorial <= 0) puts("O número deve ser positivo e maior que zero !"); return; else // calcula o fatorial for(nCont = nfatorial; nCont >0;nCont--) nresult = nresult * nCont; // vai multiplicando // apresenta dados printf("\n %d! = %d",nfatorial,nresult); getch();

DDiiffeerreennççaa ddee âânngguullooss

Programa que calcula a diferença entre dois ângulos. #include <stdio.h> #include <conio.h> void main(void) // declaração de variáveis locais double dgraus, dminutos, dsegundos, dSegundos_total; // entrada de dados puts("Entre com o valor do angulo da forma GG:MM:SS"); scanf("%lf:%lf:%lf",&dgraus,&dminutos,&dsegundos); // calcula dSegundos_total = dgraus * 3600.0 + dminutos * 60.0 + dsegundos; // apresenta o valor calculado printf ("\nO valor total de segundos e' %lf",dSegundos_total); // espera que o usuário aperte uma tecla getch();

Page 566: Tratado Da Linguagem c

A P Ê N D I C E F - E X E M P L O S D E A P L I C A Ç Ã O

544544544544

FFFF

OOrrddeemm CCrreesscceennttee

Programa que ordena dados de forma crescente. #include <stdio.h> #include <conio.h> void main(void) int nCont; // contador de loops int nDado[5]; // vetor com os dados int nMenor = 0; // variável temporária int nRef; // indice de referencia para a comparação int nInd; // indice para varrer o vetor em busca do menor valor puts("Programa que pede a entrada de 5 valores e apresenta estes em ordem crescente"); // entrada de dados for(nCont = 0;nCont<5;nCont++) printf("\n Dado [%d] = ",nCont); scanf("%d",&nDado[nCont]); // ordena o vetor for(nRef = 0;nRef<5;nRef++) nMenor = nDado[nRef]; for (nInd = nRef+1;nInd < 5;nInd++) if(nDado[nInd] < nMenor) nMenor = nDado[nInd]; nDado[nInd] = nDado[nRef]; nDado[nRef] = nMenor; // apresenta dados puts("Os dados foram ordenados na seguinte maneira: "); for(nCont = 0;nCont<5;nCont++) printf("\n Dado[%d] = %d",nCont,nDado[nCont]); getch();

Page 567: Tratado Da Linguagem c

545545545545 Luis Fernando Espinosa Cocian

AAppêênnddiiccee GG -- PPrriimmeeiirrooss PPaassssooss Este apêndice trata de forma breve e simples, a utilização dos compiladores mais

comuns da linguagem C, para PCs.

MMiiccrroossoofftt VViissuuaall CC++++44..00,, 55..00 ee 66..00

1. Abrir o Visual C++

2. Criar um novo projeto do tipo usando as opções de menu File + New.

Apêndice

G

Page 568: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

546546546546

GGGG

3. Selecionar a opção Win32 Console Application

4. Colocar um nome para o projeto no campo Project Name

5. Selecionar a pasta onde serão colocados os arquivos de projeto no campo Location.

6. Selecionar o botão OK.

7. Neste ponto devem ser criados os arquivos fonte. Novamente seleciona a opção de menu File + New, selecionando a opção C++ Source File.

Page 569: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

547547547547

GGGG 8. Colocar o nome do primeiro arquivo do projeto no campo File Name.

9. Selecionar o botão OK.

10. Na janela direita pode ser digitado o seu programa.

11. Para primeiro teste, de forma a verificar se todos os caminhos das bibliotecas estão corretos, usar o seguinte programa exemplo:

#include <stdio.h> void main (void) int i; for(i = 0; i<50; i++) printf(“Testando: %d”, i);

Page 570: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

548548548548

GGGG

12. Para iniciar coloque um ponto de parada na função printf, colocando o cursor na linha da função, e clicando no ícone correspondente à inserção do ponto de parada.

13. Ao fazer isto, o Visual C++ colocará uma bola vermelha no canto esquerdo da linha correspondente, indicando, que a execução irá continuar até este ponto, onde serão esperados os novos comando por parte do programador.

Compilar

Montar o projeto

Montar o projeto

Iniciar ou continuar ou programa

Insere ou remove um ponto de parada

Page 571: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

549549549549

GGGG

14. Executar o programa usando o seguinte ícone:

15. O compilador indicará que o programa ainda não foi montado, depois das últimas modificações.

16. Selecionar a opção SIM.

17. O programa será montado, gerando um executável, e posteriormente executado até o ponto de parada selecionado. Se pode observar a parada, pelo aparecimento de uma seta amarela encima do ponto vermelho, do breakpoint.

Page 572: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

550550550550

GGGG

18. Pode-se observar duas janelas inferiores que são utilizadas para depuração das variáveis envolvidas no código, e que podem ser editadas pelo programador. Também existe uma nova barra de ferramentas de depuração, que são muito úteis, e que permitem a continuação da execução passo a passo, executando funções diretamente, ou entrando nelas, a verificação do valor dos registradores internos do microprocessador, os conteúdos da memória, e outros itens.

19. Para efetuar a execução passo a passo e poder verificar o funcionamento da instrução for, utilizar o seguinte

ícone:

20. O programa resultará em:

Page 573: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

551551551551

GGGG

21. Para obter ajuda específica, colocar o cursor encima do comando ou função no código fonte, e apertar a tecla F1.

BBoorrllaanndd BBuuiillddeerr CC++++ 33..00,, 44..00 ee 55..00

1. Inicie o programa compilador.

2. No menu selecione a opção File + New, escolhendo a opção Console Wizard. Clique no botão OK.

Page 574: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

552552552552

GGGG 3. Aparecerá uma segunda janela, onde deverá selecionar a seleção C em Source Type. Clicar em OK novamente.

4. Na janela direita pode ser digitado o seu programa.

5. Apagar o conteúdo da janela Unit1.c.

6. Para primeiro teste, de forma a verificar se todos os caminhos das bibliotecas estão corretos, usar o seguinte programa exemplo:

#include <stdio.h> void main (void) int i; for(i = 0; i<50; i++) printf(“Testando: %d”, i);

Page 575: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

553553553553

GGGG

7. Clicar duas vezes na linha da função printf, para colocar um breakpoint.

8. Os comandos para compilar e executar o programa estão disponíveis na barra de menu.

Page 576: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

554554554554

GGGG

9. Clicando no comando Run (Executar o Programa), será efetuada a compilação e a montagem. A execução do programa irá parar no breakpoint, indicando com uma seta verde claro.

10. Pode-se continuar a execução do programa usando os ícones de execução passo a passo. Deixando o cursor do mouse encima de uma variável por um segundo, o compilador indica o valor da variável, o que é bastante útil para efeitos de depuração do programa.

Executar o programa

Passo a passo entrando nas funçoes

Passo a passo executando as funções de forma direta

Page 577: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

555555555555

GGGG

11. Sempre há disponibilidade da utilização de outras ferramentas de depuração, no menu Run, opções Inspect ou Add Watch. Estas opções podem ser acessadas através do mouse, clicando no botão direito.

12. O resultado do programa é o seguinte:

13. O acesso à ajuda pode ser feita diretamente colocando o cursor sobre a instrução for, por exemplo, e clicando na tecla F1.

Page 578: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

556556556556

GGGG BBoorrllaanndd CC++++ 33..00 ee 33..11

1. Usualmente este programa pode ser executado diretamente do diretório \bin\bc.exe. Este compilador é relativamente antigo, ainda na época em que recém começava a ser utilizado o sistema MS Windows. É um compilador excelente, rápido de ser utilizado, barato de ser adquirido e ainda funcional, no mínimo bastante didático.

2. Execute o programa do compilador. Os atalhos neste programa, respondem aos comandos de teclado do antigo Wordstar. Para copiar e colar utilizar as teclas <Control+Insert> e <Shift+Insert> respectivamente. Para apagar uma linha usar <Control+y>, ou use a barra de comando do Windows Console.

3. No menu File + New, criar um novo código fonte.

4. Utilizar a opção de comando File As, para salvar o código com o nome desejado, deixando a extensão do arquivo como sendo .c.

Page 579: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

557557557557

GGGG

5. Para primeiro teste, de forma a verificar se todos os caminhos das bibliotecas estão corretos, usar o seguinte programa exemplo:

#include <stdio.h> void main (void) int i; for(i = 0; i<50; i++) printf(“Testando: %d”, i);

6. Com o cursor na linha da função printf, aperte as teclas <Control+F8> para colocar um breakpoint.

Page 580: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

558558558558

GGGG

7. Para executar o código selecionar a opção de menu Run ou pelo atalho <Control+F9>, isto efetivará a compilação e a montagem do programa, interrompendo a execução no breakpoint.

8. Quando o programa parar no breakpoint, será indicado por uma linha com cor ciano.

9. Com o uso das teclas F8 e F7 pode-se executar passo a passo.

10. Para depuração pode ser usado o menu Debug.

11. Para ajuda, se for a busca de um comando, por exemplo, deixar o cursor encima da palavra a ser procurada e apertar as teclas <Ctrl+F1>.

12. Para examinar a tela de saída durante a depuração, alternar com as teclas <Alt+F5>.

13. O resultado do programa ficará:

Page 581: Tratado Da Linguagem c

A P Ê N D I C E G - P R I M E I R O S P A S S O S

559559559559

GGGG

Page 582: Tratado Da Linguagem c

560560560560 Luis Fernando Espinosa Cocian

GGLLOOSSSSÁÁRRIIOO Macro Seqüência de comandos ou funções que podem ser executados como programas. As

macros possuem algumas vantagens, tais como a de serem mais rapidamente executadas que as funções, já que se comportam como funções inline, não provocam overhead da memória comparadas às chamadas de função. Em contrapartida, ocupam maior espaço de código de programa, e como o código correspondente é montado de forma direta, sem a verificação de tipo de parâmetro ou variável, deixa o programa vulnerável a erros de programação e execução. Na linguagem C, as macros podem ser implementadas pela diretiva #include.

Buffer Informática: Dispositivo de armazenamento de caráter transitório, utilizado durante uma operação de transferência ou transmissão de dados, entre unidades de armazenamento, ou de processamento que operam com tempo de acesso, velocidades ou formatos distintos. Eletrônica: Circuito com alta impedância de entrada e baixa impedância de saída, e eu se interpõe entre dois outros circuitos para minimizar o efeito de carregamento da impedância de entrada de um deles na saída do outro. Circuito isolador. Amplificador seguido de emissor, ou amplificador seguidor de fonte, ou amplificador seguidor de tensão.

UART Universal Asynchronous Receiver Transmitter – Transmissor e Receptor Assíncrono Universal. Refere-se a o dispositivo de comunicação.

USART Universal Asynchronous Receiver Transmitter – Transmissor e Receptor Síncrono Assíncrono Universal. Refere-se a o dispositivo de comunicação.

Handshaking checksum

:

Glossário

Page 583: Tratado Da Linguagem c

561561561561 Luis Fernando Espinosa Cocian

RREEFFEERRÊÊNNCCIIAASS

BBiibblliiooggrraaffiiaa [1] Buarque de Holanda – Novo Aurélio Século XXI: O Dicionário da Língua Portuguesa – Nova Fronteira

S.A., 1999. [2] Grolier Electronic Publishing Inc. – GrolierMultimedia Encyclopedia – V. 8.01, 1996. [3] Kernighan, B. & Ritchie, D. C - A linguagem de programação padrão ANSI - Editora Campus, 1990. [4] Microsoft Encarta 97 Encyclopedia – Microsoft Corporation - 1997 [5] Mizrahi, V. V. - Treinamento em linguagem C - Módulos 1 e 2. Editora McGraw-Hill, 1990. [6] Norton, Peter; Aitken Peter; Wilton Richard - A Bíblia do Programador - Editora Campus, 1993. [7] Schildt, H. - Turbo C - Guia do Usuário - Editora McGraw-Hill, 1988. [8] Schildt, H. – C Completo e Total - Editora McGraw-Hill, 1990. [9] Pappas, Chris H., Murray, William H. – Turbo C++ Completo e Total – McGraw-Hill – Makron Books

do Brasil Editora Ltda., 1991. [10] Tokheim, Roger L. – Introdução aos Microprocessadores – McGraw-Hill, 1985. [11] Winkel, David; Prosser, Franklin – The Art of Digital Design – An Introduction to Top-Down Design –

Prentice-Hall, Inc, 1980.

WWeebb [12] Carpenter, V. - Learn C/C++ today - http://www.cyberdiem.com/vin/learn.html [13] Hyde, Randall - The Art of Assembly Language Programming -

http://webster.cs.ucr.edu/Page_asm/ArtofAssembly/CH01/CH01-1.html [14] St. Michaels University School -

http://www.smus.bc.ca/depts/compstud/ds/glossary/reftable/ascii.htm [15] Teletechnics Afield - http://www.teletechnics.com/reference/ascii_codes.html [16] http://margo.student.utwente.nl/stefan/hwb/co_RS-232.html [17] http://www.senet.com.au/~cpeacock [18] http://www.geocities.com/SiliconValley/Bay/8302/

Referências

Page 584: Tratado Da Linguagem c

562562562562 Luis Fernando Espinosa Cocian

ÍÍnnddiiccee

8

8051 · 4, 7, 8, 9, 10, 20, 51, 63, 64, 122, 421, 422, 423, 445

A

Abstração · 39 ADC · 416 Algoritmos · 41 Alocação Dinâmica

Alocação Dinâmica de Vetores e Matrizes · 210

Alocação Dinâmica de Memória · 207 ANSI · 3, 34, 47, 63, 68, 71, 76, 84, 104, 153,

158, 207, 228, 240, 245, 246, 249, 364, 561 Application Builder · 422 argc · 84 Argumentos · 57 argv · 84 Armazenamento de Tipos Básicos · 121 Arquivos · 249 Arquivos Fonte · 81 Arrays · 111 ARRAYS · 193 As Portas de Comunicação no Sistema MS

Windows · 451 ASCII · 32 assembly · 2, 11, 39, 45, 46, 62, 63, 186, 218,

219, 259, 274 Assembly · 11, 14, 45, 46 auto · 96

B

BCPL · 2, 3 BIOS · 102, 272, 280, 290, 291, 300, 302, 310,

311, 323, 327, 328, 330 Bits · 21 Blocos · 83 Borland C++ - 80x86 · 274 Bytes · 22

C

Cabos para RS422 e RS485 · 360 calloc · 208 Caracteres Especiais · 78 casts · 139 CCITT · 351 CCS · 4, 100, 122, 123, 274, 378, 379 Centronics · 289 Classes de Armazenamento · 93 CODIFICAÇÃO · 15 COM1 · 262, 263, 272, 310, 319, 320, 321, 328,

330, 452, 454, 455 Comentários · 48 Compilação · 43 Compiladores · 10 Conectores · 479 Conectores de Alimentação do PC · 501 Constantes · 73 continue · 184 Controle de Fluxo · 54 CONTROLE DE FLUXO DE EXECUÇÃO ·

170 Conversão

Conversão de Tipos Ponto Flutuante · 165 Conversão de Tipos · 127 Conversão Temporária de Tipos · 134 Conversões · 163

Conversões de Tipo Ponteiros · 166 Conversões de tipos Inteiros com Sinal · 163 Conversões de Tipos Inteiros sem Sinal · 164 Conversões em Chamadas de Função · 167

Conversões Aritméticas · 144 Conversões de Tipos · 162 Conversores de Nível RS-232 · 325 CPL · 3 CPU · 4, 6, 7, 8, 10, 11, 58, 97, 128, 230, 274,

279, 284, 309, 313, 317, 319, 373 CreateFile · 452 Cuidados a Serem Tomados ao se Usar

Ponteiros · 206

D

DB-25 · 481 DB-9 · 482

Índice

Page 585: Tratado Da Linguagem c

563563563563

DCB · 456 DCE · 307 Declaração de Variáveis · 49 Declarações · 92 DECLARAÇÕES · 92 Declarações Abstratas · 114 Declarações de Nível Externo · 94 Declarações de Variáveis · 102 Declarações e Definições · 82 Declaradores Complexos · 114 Diretivas de Compilação · 49 DIRETIVAS DE COMPILAÇÃO · 240 Diretivas de Pré-Processador · 81 DLAB · 312 do - while · 182 Drivers · 1, 336, 337, 340 DTE · 307 Duty Cycle · 405, 406, 515

E

ECP · 300, 301 Efeitos Colaterais · 138 EIA · 338 EIA-232-F · 349 EIA-334 · 349 EIA-363 · 350 EIA-404 · 350 EIA-422 · 342, 353 EIA-423 · 340 EIA-449 · 350 EIA-485 · 345, 353 EIA-530 · 350 EIA-561 · 350 EIA-562 · 341 EIA-574 · 350 EIA-612 · 348 EIA-613 · 350 EIA-644 · 349 EIA-687 · 351 EIA-688 · 351 EIA-694 · 342 EIA-723 · 351 EISA · 492 else · 172 Encadeamento de Expressões · 135 Encadeamentos · 88 ENTRADAS E SAÍDAS · 245 enum · 266 Enumeração · 103 EPP · 300 Escopo · 87 Espaços de Nomes · 89 Especificadores de Tipo · 99 Estilo · 39 Estrutura de um Programa · 47 ESTRUTURA DE UM PROGRAMA · 81 Estruturas · 106

Campos de Bits · 108 Ethernet · 488

Expressões · 133, 171 Expressões Primárias · 135

EXPRESSÕES · 129 Expressões Abreviadas · 134 extern · 98

F

Firmware · 1, 381, 420 fluxogramas · 12 Fluxogramas · 41 for · 55, 178 Formalismo · 40 Fortran · 3, 13, 14 Franklin C · 4, 274, 434, 435, 440, 441 free · 209 Funções · 57 FUNÇÕES · 216

G

gets · 195 goto · 185

H

hardware · 4, 1, 2, 3, 4, 5, 7, 9, 10, 11, 12, 25, 26, 27, 30, 38, 39, 40, 41, 43, 45, 46, 52, 63, 101, 168, 180, 258, 259, 270, 272, 274, 275, 278, 279, 285, 287, 288, 289, 290, 292, 295, 297, 300, 302, 305, 307, 308, 310, 322, 333, 335, 370, 374, 451, 453, 472, 509

Hardware · 272, 304 Hexadecimal · 23 história da Linguagem C · 2

I

I/O · 245 I/O - Portas · 258 IDE · 494 Identificadores · 71 IEEE488 · 353 if · 54, 171 Inicialização · 117 Instrução Null · 187 Instruções

Instruções Compostas · 175 INTERFACE PARALELA · 286 Interfaces · 286 Interfaces com Microcontroladores · 327 INTERFACES SERIAIS · 303 Interrupção da Porta Paralela · 298 INTERRUPÇÕES · 271 ISA · 490

Page 586: Tratado Da Linguagem c

564564564564

J

Joystick · 480

K

Keywords · 71

L

Labels · 185 Linguagem de Máquina · 10 Linguagens de Programação · 10 Linker · 43, 377, 434, 435, 440, 441 Listas Simplesmente Encadeadas · 268 LPT · 290, 291, 484 L-Value · 137

M

main · 83 malloc · 208 Matrizes · 197

Matrizes multidimensionais · 198 Memória · 6, 46, 207, 208, 290, 376, 377, 425,

432, 433, 434 Microcontroladores · 199 Microcontroladores da Família Intel 8051 · 421 Modems · 1, 305 Mouse · 479 MPLAB · 377

N

nibbles · 20 Nibbles · 21 Nível Interno · 96 Null Modems · 305

O

Operações Lógicas · 25 Operações Lógicas em Números Binários e

Cadeias de Bits · 28 Operador “? :” · 174 Operadores

Operador de Avaliação Seqüencial · 162 Operadores Aritméticos e de Atribuição · 129 Operadores de Adição · 154 Operadores de Atribuição · 160 Operadores de Deslocamento de Bits · 155 Operadores de Expressão Condicional · 159 Operadores Lógicos · 158 Operadores Lógicos Bit a Bit · 132 Operadores Lógicos entre Bits · 158 Operadores Multiplicativos · 153 Operadores Postfixados · 144

Operadores Relacionais e de Igualdade · 156 Operadores Relacionais e Lógicos · 131 Operadores Unários · 149

OPERADORES · 129

P

Padrões de Transmissão Serial · 332 Palavras Reservadas · 47 PCI · 495 PCMCIA · 499 PCW · 4, 82, 100, 121, 122, 123, 274, 378, 379 PIC · 4, 21, 51, 64, 82, 100, 121, 122, 123, 272,

273, 274, 275, 277, 278, 279, 280, 281, 282, 298, 299, 320, 321, 322, 323, 373, 374, 375, 376, 377 Arquitetura Microchip PIC · 375

Polarizando uma rede RS-485 · 358 Ponteiros · 112 PONTEIROS · 200 Ponteiros e Funções · 207 Ponteiros para Ponteiros · 206 Ponto Flutuante · 22 Pontos de Seqüência · 139 Pontuação · 78 Porta Paralela · 287 Portas Bidirecionais · 293 Portas no Sistema Windows · 327 Portas Seriais COM · 481 Pragmas · 82 Precedências

Precedências de Operadores · 135 Pré-Processador · 81 printf · 53 Programas Fonte · 81 PROJETO DE SISTEMAS DE SOFTWARE ·

38 Proteção contra transientes · 362 PS/2 · 479 PWM · 374, 401, 403, 405, 406, 407, 408, 409,

416, 432, 433, 514, 515

Q

Qualificadores de Tipo · 101

R

realloc · 209 Receptores Balanceados · 337 Redirecionamento IRQ2/IRQ9 · 278 Referencia de Conectores · 503 register · 97 Retorno de Valores · 59 return · 187 RGB Analógico · 487 RGBI · 487 RJ-45 · 483

Page 587: Tratado Da Linguagem c

565565565565

RS-232 · 324, 339 R-Value · 137

S

scanf · 53 Seqüências de Escape · 76 Sistema Binário · 18 Sistema de Numeração Hexadecimal · 23 Sistema Operacional · 5, 10 Sistemas Balanceados · 334 Sistemas de Numeração · 16 Sistemas Desbalanceados · 333 sizeof · 152 SPI · 373, 374, 380, 384, 401, 409, 411, 412,

413, 414, 416 SPP · 291, 300 stack · 207 static · 97 strcat · 196 strcmp · 197 strcpy · 196 Strings · 51, 194

Inicializando Strings · 121 Strings Literais · 77 strlen · 196 struct · 262 Surtos · 362 SVGA · 487 switch · 176

T

Teclado · 479 Tempo de Vida · 86 Teoria da Derivação · 366 Teoria da Isolação · 365 Terminação · 356

TIA · 338 Timeouts · 465 Timer do PC · 283 TIPOS · 92 TIPOS DE DADOS ESPECIAIS · 262 tokens · 68, 69, 70, 170 Tokens da Linguagem · 68 Top Down · 39 Top-Down · 40, 561 Topologias de Rede · 354 Trigraphs · 72 typedef · 126, 267

U

UART · 308 Union · 265 Unions · 109 USART 8051 · 443 USB · 486

V

V.10 · 351 V.11 · 352 V.24 · 352 V.35 · 352 Vetores de ponteiros · 206 Visibilidade · 87

W

warning · 60 Watchdog · 373, 374, 419, 420 while · 181 Words · 22

Page 588: Tratado Da Linguagem c

566566566566

RED BOOK

Tratado da Linguagem C Cocian

Brusamarello Lorenzi