Buscar

apostila c

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 96 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 96 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 96 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

Versão antiga 
Em breve, versão revista
L I N G U A G E M
C
Otton Teixeira da Silveira Filho 
Registro N° 70.630 
Biblioteca Nacional - Escritório de Direitos Autorais
1997
Números de página
SUMÁRIO
1)Introdução
2)C, A linguagem
2.1) Identificadores 1
2.1) Palavras-chave 3
2.2) Tipos de variáveis 4
2.3) Modificadores 4
3)Operadores 4
de atribuição 6
vírgula 6
aritméticos 6
de incremento e decremento 7
de endereço 8
de bit 8
lógicos 8
relacionais 9
Precedência de operadores 9
Abreviação de operações 10
Conversão de tipos 11
4) Constantes 12
5)Controle de fluxo 13
if-else 13
? : 13
switch-case-default 13
goto 14
6)Laços 15
while 15
do-while 15
for 15
Palavras auxiliares 16
break 16
continue 16
7) Fatos e mitos - Velocidade de processamento17
8)Programas em C 18
7.1) Denteação 24
9)Funções 25
Forma clássica 
Variáveis Globais, Locais e Automáticas28
Um Comando do Preprocessador, cabeçalhos e arquivos 
Departamento de Ciência da Computação-UFF
Números de página
de inclusão 29
Algumas funções úteis 31
10) Ponteiros 36
Ponteiros e funções 36
Ponteiros e vetores 39
Aritmética de Ponteiros 42
Inicializando vetores 43
Ponteiros para funções 44
Mais Sobre Funções 46
11) O Preprocessador
O comando #define (do preprocessador)51
Mais comandos do preprocessador 54
12) Vetores Multidimensionais 56
13) Vetores de Ponteiros e Ponteiros de Vetores 58
Ponteiros de Ponteiros & Ponteiros de Ponteiros de 
Ponteiros e etc...59
10) Funções 60
Em notação moderna
Funções em Forma Moderna 60
Mais Algumas Funções 61
11) main() com parâmetros 63
12) Modificadores 65
Variáveis estáticas 65
Variáveis externas 67
Variáveis registrador 67
13) Reservando memória para uso temporário 69
Fragmentação 70
14) Estruturas e uniões 71
Estruturas 71
Estruturas e Manipulação de Bits 73
Estruturas e funções 74
Uniões 76
Inicializando estruturas e unimos 81
15) Determinando o tamanho de Estruturas, Uniões e etc82
15) Enumeração 83
16) Definição de Tipos 85
16) Trabalhando com arquivos em disco
Funções de Alto Nível 86
Funções de Baixo Nível 91
Departamento de Ciência da Computação-UFF
Números de página
17)Arquivos padrões e como usar a impressora 97
18) Respostas de problemas 99
19) Apêndices
Tabelas dos operadores lógicos101
Departamento de Ciência da Computação-UFF
Números de página
INTRODUÇÃO
C é uma linguagem de programação que tem como características principais grande flexibilidade,
escrita compacta, padronização bem feita e alta velocidade de processamento. Por estas características, ela é
chamada, muitas vezes, de uma Linguagem de Programador pois envolve aspectos, como os já citados, que
agradam a estes profissionais.
Foi desenvolvida no início da década de 70 por Dennis Ritchie influenciada pela linguagem B, esta
desenvolvida por Ken Thompson. A linguagem B teve como fonte inspiradora uma outra linguagem, o BCPL. B
é uma linguagem que só tem um tipo de dado que corresponde ao tamanho da palavra do computador no qual
trabalha. A linguagem C, por sua vez, tem uma gama relativamente grande de tipos de variáveis mas não é tão
fortemente tipada como Pascal. Permite ainda a criação de estruturas de dados não homogêneas. Apesar destas
diferenças, BCPL, B e C são filosoficamente semelhantes. A ideia é ter uma linguagem de escrita compacta,
com recursos de manipulação de dados a baixo nível mas com características de linguagem de alto nível. Criar
um compilador de C é uma tarefa consideravelmente mais fácil que criar um compilador Fortran, por exemplo,
além de poder ser bem pequeno existindo versões que completas e funcionais que ocupam menos de 100KB de
espaço em disco, exigem memória da ordem de 256KB, restricões que nos dias de hoje (1997) nos parecem
paleolíticas. As primeiras versões para micro (Apple II, por exemplo) funcionavam bem melhor que
compiladores de outras linguagens.
As idéias por trás de C são tão simplificadoras e "naturais" que ela serve de base para outras
linguagens. Dando apenas um exemplo, existe uma linguagem que se baseia simultaneamente em C e no
conceito de objetos[]. Esta linguagem, o C++ (pronuncia-se cê mais mais), é bem definida existindo o padrão
ANSI. 
Atualmente existem compiladores dos mais diversos fabricantes e ainda versões de domínio público
largamente usados em ensino e pesquisa no mundo inteiro. O caso mais notório é o GNU C (embora o Projeto
GNU não se limite à este compilador) e existe versões do mesmo para os mais variados ambientes
(UNIX/Linux, McIntosh, MS-DOS/Windows).
C originalmente foi uma linguagem construída para desenvolver programas de sistemas, ou seja : 
Sistemas operacionais
Assemblers
Editores
Interpretadores
Compiladores
Gerenciadores de rede, etc.
Por exemplo, o sistema operacional UNIX é escrito em C, estando esta linguagem e este sistema
operacional intimamente ligados inclusive sob o ponto de vista de filosofia de trabalho e construção de
programas.
O que faz esta linguagem aplicável a estas funções é que C permite um nível de manipulação de dados,
só conseguido anteriormente pelo uso de programas em Assembler. Estes últimos são de escrita difícil e tediosos
de escrever além de ter depuração geralmente problemática. Um fator que pode levar a adoção do Assembler é a
velocidade mas, nem sempre este é o fator principal e mesmo neste ponto C não faz feio. C, permite o uso de
recursos de "baixo nível" numa linguagem de "alto nível" e boa velocidade de execução. Por isso, muitas vezes
C é chamada de linguagem de "médio nível". Como se pode perceber, não há nenhuma conotação ruim nos
termos baixo ou médio. Para dar exemplos, Linguagens deBaixo Nível são os Assembly de cada máquina.
Linguagens de Alto Nível são FORTRAN, ALGOL, BASIC, PASCAL, etc. As Linguagens de Médio Nível
mais conhecidas são C e FORTH, esta última tendo uma estrutura mais flexível e poderosa do que C mas
geralmente é de execução mais lenta, por ser (nas suas versões mais comuns) parcialmente interpretada, além de
ser de programação confusa. 
É claro que não demorou muito tempo para que uma linguagem com este poder fosse se difundindo
fora da área de programas de sistemas. Hoje, boa parte das aplicações de alto desempenho são cada vez mais
escritas em C, sejam elas comerciais ou científicas.
Você poderá escrever em C qualquer programa que execute qualquer tarefa escrita originalmente em
linguagens como FORTRAN, Pascal, COBOL ou BASIC e coisas que estas não fazem ou que, para serem feitas,
exigem conhecimento de truques ou macetes confusos ou específicos do produto com o qual você estiver
trabalhando.
Mas nem tudo se resume a maravilhas. Em troca, C exige do programador muita responsabilidade e
atenção. Outras linguagens criam barreiras que protegem certas estruturas na memória do computador, mas C
não. Portanto se acostume com o lema de C : 
"O programador é inteiramente responsável pelo que faz."
A filosofia é de que o programador não precisa ser tutelado.
Departamento de Ciência da Computação-UFF
Números de página
C não menospreza a sua inteligência, mas também não se responsabiliza por descuidos. Não se esqueça
que um algoritmo mal feito vai funcionar mal em qualquer linguagem. Mas não se assuste. Há uma brincadeira
entre os não usuários de C e mesmo os que iniciaram o seu estudo de maneira descuidada. Estes dizem: "Você
gosta de viver perigosamente ? Programe em C!" Se fosse tão "perigosa" assim, C não seria tão usada.
Iniciaremos a discusão da linguagem C seguindo como referência o que é chamado "C padrão", ou seja,
a definição mínima contida no livro "A Linguagem de Programação C" escrito por Brian Kernigham e Dennis
Ritchie em sua primeira edição. Haverá ainda informações sobre uma complementaçãodefinida pelo American
National Standards Institute (ANSI). A esta chamaremos de "Padrão ANSI". 
Sobre o parágrafo anterior é bom chamar a atenção que cada fabricante de software que produz para
uma máquina específica, tende a incluir “extenções da linguagem” fora dos padrões acima. Nem sempre é
possível ignorar estas modificações do padrão. É o caso dos famigerados “modos de memória” em máquinas
microsoft/Intel. No entanto, por uma questão de produtividade, flexibilidade e transportabilidade devemos nos
ater da melhor maneira possível aos padrões não dependentes de fabricante. Esta preocupação em seguir padrões
facilita a construção de produtos muito mais do que limita a produção. Seguir uma padronização é considerada
uma atitude louvável sendo que no caso do Unix todo produto que carrega os dizeres “POSIX COMPLIANT” é
considerado de maneira mais “respeitosa”.
Como ponto ainda, temos versões “Integradas” e “não-integradas”, ou seja, versões onde temos um
sistema que integra os passos de edição, compilação e depuração e sistemas em que cada uma destas fases são
executadas separadamente. Os sistemas integrados são mais “confortáveis” mais o sistema de integração ocupa
espaço em memória e ainda é um elemento estranho ao processamento do programa (podemos ter reações
diferentes do programa se ele roda dentro ou fora do integrador). Muitas vezes (principalmente em programas
críticos) é mais seguro botar a preguiça de lado de sofrer o “imenso desconforto” de apertar uns botões a mais.
 O texto, no essencial, não se prende a um compilador ou a uma máquina específica. Quanto a
referências a C++, damos o livro de definição da linguagem de Stroutrop e ainda chamamos a atenção que as
versões mais recentes da versão de C do Projeto GNU já são efetivamente compiladores de C++. Produtores
tradicionais de compiladores C também estão criando produtos já admitindo C++.
Quero lembrar que este texto, pressupõe um conhecimento prévio de técnicas de programação em uma
linguagem como FORTRAN, Pascal, Assemblers, etc. Não é recomendável a adoção de C como primeira
linguagem de programação. Os programadores que tiverem alguma experiência com assemblers,
provavelmente, absolverão com mais facilidade alguns conceitos incomuns contidos em C e não em outras
linguagens. Como este curso presupõe tão variadas origens, alguns pontos podem aparentar ser redundantes,
óbvios ou (para alguns) um tanto quanto obscuros. Antes de mais nada cuidado com as aparências. O óbvio
pode não ser tão óbvio e o obscuro pode ter origem no “sotaque” que você carrega da linguagem de
programação que você está mais acostumado.
Quanto da forma do texto, fazemos inicialmente uma descrição da linguagem de uma maneira geral e
apresentamos exemplos simples que (ao se apresentar novas estruturas) vamos modificando sistematicamente
fazendo uma apresentação informal da linguagem. A medida que evoluimos, introduzimos mais conceitos novos
e sofisticamos os exemplos até englobar a linguagem como um todo. De certa forma, esta estratégia tem por
inspiração a origem de C. UNIX foi criado com a filosofia de construção que devemos ter ferramentas de
software simples, dedicadas a determinada operação e confiável, algo como:
Menos é mais
. Devemos então adotar esta como filosofia de programação.
Departamento de Ciência da Computação-UFF
Números de página
C, A LINGUAGEM
IDENTIFICADORES
Um ponto importante em C são as especificações dos identificadores e algumas convenções na criação
dos mesmos. Os identificadores em C, podem começar por qualquer letra maiúscula ou minúscula ou o sinal de
sublinhar (underscore _). No meio de um nome, poderam haver letras, números ou o sinal _ (sublinhado ou
underscore) e nada mais. O número de caracteres que difere um identificador de outro, pode variar de
compilador para compilador sendo que na definição de C padrão K&R são levadas em consideração 8 caracteres
e em C ANSI 32 caracteres.
Exemplos corretos e distintos (nem por isto todos são recomendáveis!) :
A
a
exemplo_de_identificador
OUTRO_exemplo_DE_identificador
MAIS_1_ExEmPlO
_e_outro
Exemplos incorretos :
1_exemplo (começa por número)
1a (idem)
um-exemplo-errado (hífem entre palavras)
+um_exemplo_errado (carater inválido "+")
esta_errado_tambem! (caráter inválido "!")
Chamamos a atenção para convenções adotadas para os identificadores :
"Variáveis tem seus identificadores em letras minúsculas"
"Constantes tem seus identificadores em maiúsculas"
"Tipos criados pelo usuário devem ter os seus identificadores começando por uma maiúscula".
Recomenda-se, por questões de padronização, que você adote esta convenção, além do que ela facilita a
visualização de "quem é quem" dentro do programa. Votaremos a falar dentro em pouco de constantes e mais
tarde de declarações de tipo. Variações podem ser feitas sempre que facilite o entendimento. Mas lembre-se:
estas são apenas recomendações. Caso violá-las facilite a compreensão de um programa, é um caso a se pensar.
Lembre-se novamente que C distingue entre letras maiúsculas e minúsculas, portanto, os
identificadores abaixo são diferentes entre si.
SOMA
soma
Soma
Fazemos ainda a recomendação que você ao definir os seus identificadores o faça da maneira mais
óbvia possível. Se uma variável acumula a soma de um orçamento, denomine-a soma_do_orcamento e não só,
xpto ou algo que o valha. Fica muito mais fácil de entender e é uma maior garantia de que, meses depois você
(ou outra pessoa) se localize com mais facilidade (o próprio nome da variável passa a fazer parte da
documentação).
Como veremos, C é uma linguagem de escrita compacta, o que facilita a sua digitação mas pode levar a
escrevermos programas incompreensíveis se não formos óbvios sempre que pudermos. Hoje o que mais vale
não é o programador cheio de truques e malandragens de programação mas aquele que prefere construir
programas claros e de fácil visualização. Isto facilita a manutenção do programa e permite uma produção mais
constante, ou seja, se você programa por hobby ou em desenvolvimento de projetos científicos, a atitude de ser
simples e claro lhe trará menos trabalho nos seus projetos. Caso você seja um profissional de produção de
software, isto significará menor dispêndio de tempo e, consequentemente, maior eficiência. 
Departamento de Ciência da Computação-UFF
Números de página
Palavras-chave
C será discutido em duas partes: as palavras-chave e as funções. As primeiras são palavras reservadas,
ou seja, identificadores que não podem ser usados pelo usuário. São identificadores que são ou constituem partes
de comandos ou declarações da linguagem. Quanto às funções, nada podemos fazer de prático em C sem elas.
Apesar disto, inicialmente só falaremos das palavras-chave.
As palavras-chave são bem poucas, se compararmos com o número de comandos de linguagens como
PASCAL e abaixo temos a relação de todas elas.
PALAVRAS-CHAVE
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
@ extensões ANSI
As palavras marcadas com @ são conhecidas como extensões ANSI e as demais são as palavras padrão K&R.
Variando de compilador para compilador, podemos ter algumas palavras extras. 
Atenção: O fato de usar palavras-chave fora do padrão, poderá criar problemas caso você queira que um
determinado programa construido por você possa ser levado de um compilador para outro ou mesmo para
outra máquina. A esta característica de podermos migrar um programa com um mínimo de modificações é
chamada de Portabilidade. Se você pretende fazer com que seu programa seja o mais portável possível, evite o
uso de palavras-chave diferentes das acima. Alémdisto, alguns compiladores mais antigos não aceitam as
extensões ANSI. Um bom conhecimento da documentação de seu compilador é vital. 
Toda palavra-chave em C se escreve em minúsculas, como se pode notar, mas o importante é chamar
novamente a atenção que C distingue duas palavras iguais mas escritas uma em maiúsculas e outra em
minúsculas, o que a maioria das linguagens não faz. Isto será fonte de erros no início dos seus trabalhos com C.
Outra fonte de erros surgirá principalmente para os de imaginação mais fértil e que gostam de fazer
paralelos entre linguagens, confiando em aparentes semelhanças. A maior parte das vezes, as dificuldades de
entender C tem origem exatamente devido a suposições do tipo:
"Este comando é "igual" a daquela outra linguagem e, portanto, deve funcionar de maneira parecida."
Toda vez que você pensar coisas deste tipo, REPRIMA! Muitas semelhanças são apenas aparentes.
Portanto, tenha em mente o lema: 
C se parece apenas com C.
Departamento de Ciência da Computação-UFF
Números de página
Tipos de Variáveis
Passemos a mais um ponto fundamental: os tipos de variáveis. Os tipos disponíveis ao programador
com a gama de valores correspondentes estão relacionadas abaixo : 
Variável Tamanho(Bytes) Gama
char 1 [0,255]
int 2 [-32768,32767]
short int 1 [-128,127]
unsigned int 2 [0,65535]
long int 4 [-2147483648,2147483647]
Variável Tamanho(Bytes) Gama Algarismos sig.
float 4 10 6 
Double 8 12
OBS: Os valores dados para os tipos float e double, poderão tomar valores negativos nos respectivos domínios.
É comum que se dispense a palavra int depois de short, unsigned e long. Observe que aqui temos várias
palavras-chave. Temos além destas algumas que atuam sobre o tipo da variável e elas são listadas abaixo e que
chamaremos aqui de modificadores :
Modificadores
Aqui chamamos de modificadores à palavras reservadas que modificam certas características de tipos
de variável definidas anteriormente
auto signed@
extern static
register volatile@
@ extensões ANSI
Apesar de termos vários tipos de variáveis, C permite muitas liberdades no trabalho destas. Se a
operação tem significado ou não, isto é deixado a você, o programador. Chamamos a atenção que o tipo inteiro
básico em C é geralmente o de dois bytes, embora isto possa variar (Não se esqueça sempre verifique a
documentação de seu compilador). Ainda temos a dizer que na definição padrão de C, todas as operações de
ponto flutuante são executadas em double. É bom frizar isto, já que em programas que usem pesadamente deste
tipo de operação, o processamento do mesmo será mais lento do que se fosse feito em linguagens que trabalhem
com variáveis de ponto flutuante de quatro bytes, por razões óbvias. No entanto, muitos compiladores permitem
que você use menor precisão como uma opção o que acelerará a execução embora com detrimento da precisão.
Departamento de Ciência da Computação-UFF
Números de página
OPERADORES 
Temos, além das palavras-chave, a definição de operadores de C. E eles são abundantes: 
Operador de atribuição '=' Ex.: a = b 
À variável a é atribuido o valor de b. No caso de atribuição de caracteres, o caracter deverá estar entre
aspas simples. Ex.: a = 'b'.
Operador vírgula ,
Este operador serve para separar várias operações a serem executadas dentro de parênteses. Se a
sequência de operações é atribuida a uma variável, o valor atribuido a variável será a da última expressão entre
parênteses. 
Ex : Seja a = 5 e b = 7, então a expressão
c = (a-b, b * 2)
fará que c tome o valor 14. 
É comum que encontremos este operador colocado separando operações dentro da declaração for, que
veremos abaixo.
Operadores aritméticos 
Suporemos aqui que b e c são inteiros e respectivamente iguais a 7 e 5.
* multiplicação Ex. : a = b * c (a = 35) 
 / divisão Ex. : a = b / c (a = 1) 
% modulo Ex. : a = b % c (a = 2) 
+ adição Ex. : a = b + c (a = 12) 
- subtração Ex. : a = b - c (a = 2) 
Como a maioria das linguagens de programação, C tem vários operadores sobrecarregados, ou seja,
um mesmo operador atua sobre vários tipos. Os operadores de multiplicação, divisão, soma e subtração atuam
indiscriminadamente sobre double, float, int e suas modificações. Note que o operador de módulo também é
sobrecarregado pois vale para qualquer inteiro.
Observe ainda que não existe em C um operador de potenciação. Isto não é um fato exclusivo desta
linguagem. Por exemplo, Pascal tem esta mesma ausência. Também é bom observar (embora seja óbvio) que o
operador % só tem sentido para operandos inteiros.
Operadores de incremento e de decremento 
++ incremento Ex. : a = b++ ou a = ++b (resultados diferentes!) 
-- decremento Ex. : a = b-- ou a = --b (resultados diferentes!) 
Para clarear o que ocorre, trabalhemos inicialmente somente com o operador de incremento. Ao
colocarmos o operador antes do identificador da variável, primeiro a variável será incrementada e então este
valor será usado em outras operações na linha onde se encontra. Caso o operador estiver depois do identificador,
então este valor será usado e só depois haverá o incremento. Vamos exemplificar o funcionamento destes
operadores com o caso abaixo onde temos uma variável com um determinado valor e esta, com o uso dos
operadores acima, é atribuida a outra variável. O valor da variável é dado ao lado de cada caso. 
Para b = 5
a = b++ (a = 5)
a = ++b (a = 6)
a = b-- (a = 5)
a = --b (a = 4)
Obs: Chamemos a atenção sobre uma questão de nomenclatura. Vamos tomar como exemplo o operador de
incremento. Se o operador é colocado antes da variável (por exemplo, ++b) dizemos estar fazendo um pré-
incremento. Se o operador está depois da variável (b++) dizemos então que fazemos um pós-incremento. Vale o
análogo para o operador de decremento.
Departamento de Ciência da Computação-UFF
Números de página
Operadores de endereço
Estes aqui serão meramente apresentados. A maneira de utilizar cada um será vista em detalhe no
decorrer do texto.
& "o endereço de" . Ex. : a = &b. a recebe o endereço da variável b. Vide ponteiros.
* "no endereço de". Ex. : c = *d. c recebe o valor da variável de endereço dado por d. Vide ponteiros.
[ ] "no endereço de ... mais um índice" . Ex. : c = a[1]. c recebe o elemento de a de índice 1. Vide
vetores.
."elemento da estrutura" (operador ponto). Vide estruturas. 
-> "elemento da estrutura apontada por" (operador seta). Vide estruturas. 
Operadores de Bit 
Estes operadores trabalham a nível de bits. Tais operadores são necessários a uma linguagem que
pretende substituir o assembler em projetos de software. 
Para ilustrar, suporemos aqui que a e b são inteiros e respectivamente iguais a 5 e 6, ou seja, 00000101
e 00000110 em base binária).
<< desloca para a esquerda. Ex. : c = a<<2 ( 20) (00010100)
>> desloca para a direita. Ex. : c = a>>1 ( 2) (00000010)
& E lógico Ex. : c = a & b ( 4) (00000100)
| OU lógico Ex. : c = a | b (7) (00000111)
^ OU Exclusivo Ex. : c = a ^ b (c = 3) (00000011)
~ NAO Ex.: c = ~b (c = -32761) (11111001)
Os operadores de deslocamento à direita e a esquerda, deslocam os bits dentro das variáveis nestas direções.
Estas operações, no caso de atuar sobre inteiros, é equivalente a multiplicar (deslocamento à esquerda) ou a
dividir (deslocamento à direita) por uma potência de dois. 
OBS.: Observe que o operador & aparentemente faz dois serviços: um como operador de endereços e outro
como E lógico. A diferença está que no caso do operador de endereço, ele atua somente sobre um elemento
(operador unário), enquanto no caso de ser o operador E lógico, ele precisa de dois elementos para obter um
resultado (operador binário).
Operadores lógicos
Em C não temos variáveis do tipo lógico e a definição do verdadeirológico é dada por qualquer valor
diferente de zero. Obviamente o falso lógico é o valor zero. Portanto estes operadores trabalharão sobre
quaisquer variáveis dando uma resposta correspondente a condição examinada.
&& E lógico Ex. : condição1 && condição2.
|| OU lógico Ex. : condição1 || condição2.
! NAO lógico Ex. : !condição1.
Departamento de Ciência da Computação-UFF
Números de página
Operadores relacionais
Os operadores abaixo trabalham fazendo comparações entre dois valores devolvendo como resultado
um falso ou um verdadeiro. 
> "maior que". Ex. : a > b 
< "menor que" Ex. : a < b 
== "igual a". Ex. : a == b 
>= "maior ou igual a". Ex. : a >= b 
<= "menor ou igual a". Ex. : a <= b 
!= "não igual a". Ex. : a != b 
OBS.: 
I) Não confunda o operador == (igual a) com o operador = (de atribuição).
II) Lembre-se novamente que o verdadeiro lógico é qualquer valor diferente de zero e o falso é o zero.
Não confunda os operadores acima com os operadores de bit.
Departamento de Ciência da Computação-UFF
Números de página
ABREVIAÇÕES
Outro ponto interessante de C, é que podemos abreviar algumas expressões. Por exemplo, se temos
algo do formato 
<variável1> = <variável1> <operador> <expressão>
podemos sempre escrever
<variável1> < operador> = <expressão>
Exemplos:
x += u é equivalente a x = x + u
x += delta * b é equivalente a x = x + delta * b
a >>= b é equivalente a a = a >> b
a &= b é equivalente a a = a & b
É óbvio que estas abreviações podem confundir quem se inicia na linguagem ou tem pouco
conhecimento da mesma. Tome os devidos cuidados, então.
PRECEDÊNCIA DOS OPERADORES
Abaixo temos a precedência de cada operador dentro de cada grupo e no caso geral.
OPERADORES ARITMÉTICOS
1) ++, --
2) - (UNÁRIO)
3) * , /, %
4) +, -
OPERADORES RELACIONAIS E LÓGICOS
1) !
2) > , >=, <, <=
3) ==, !=
4) &&
5) ||
OPERADORES DE ENDEREÇO
1) ., ->
2) *, &
Departamento de Ciência da Computação-UFF
Números de página
PRECEDÊNCIA GERAL
1)( ) Chamada de função
[ ] Elemento de matriz
-> Ponteiro para membro de estrutura
. Membro de estrutura
2) ! 
~
++
--
- Unário
(tipo) Moldagem
* "no endereço de"
& "o endereço de"
sizeof
3) * Multiplicação
/
%
4) +
-
5) <<
>>
6) <
<=
>=
>
7) ==
!=
8) & E de bit
9) ^ OU Exclusivo de bit
10) | OU de bit
11) &&
12) ||
13) ?:
14) =
*= /= %= += -= <<= >>= &= ^= |=
15) ,
Mudança de Precedência
A precedência poderá ser mudada pelo uso de expressões entre parênteses, tendo os parênteses mais
internos maior prioridade que os demais.
OBS. : Lembre-se: é preferível colocar parênteses sobrando para clarear o que ocorre do que permitir
interpretações dúbias. 
Departamento de Ciência da Computação-UFF
Números de página
CONVERSÕES DE TIPO E CONVERSÕES FORÇADAS (cast, ou moldagem) 
Agora que já falamos de tipos e operações, devemos nos preocupar com o uso de operações com tipos
mistos, por exemplo, calcular o produto de uma variável inteira com uma de ponto flutuante e este resultado ser
colocado numa variável de precisão dupla. 
Há algumas conversões que são feitas automaticamente. Em operações mistas odos os char e short int
são convertidos em int. Todos os float são convertidos em double. Sempre a conversão entre pares de
operandos é feita para o tipo do maior operando, ou seja, operando-se um double e um int, temos como
resultado um double e se operamos um long e um int, temos um long como resultado. O que queremos dizer
como maior é o tipo que puder representar o maior número. Portanto, operando um inteiro longo e um número
de ponto flutuante, ambos de 4 bytes de comprimento, teremos a conversão do resultado em um número de
ponto flutuante. No entanto, nem sempre o resultado que temos é o esperado.
Algumas vezes pode ser interessante forçar uma conversão de tipo. Podemos fazer isto escrevendo,
entre parênteses, o tipo ao qual queremos converter a variável a esquerda. Este construtor é denominado Cast ou
ainda Moldagem. Vamos a um exemplo. Suponha que i e j sejam duas variáveis inteiras de dois bytes (tipo int)
e x uma variável de ponto flutuante (tipo float). Poderíamos escrever o que se segue 
i = (int)x /j
Teríamos a divisão de x por j, mas não o ponto flutuante x mas o valor convertido para inteiro. 
A moldagem pode ser também parte da documentação. Mesmo que saibamos que determinadas
conversões serão feitas automaticamente, é sempre bom indicar explicitamente através da moldagem as nossas
“intenções”. Isto muitas vezes facilita a compreenção do programa.
Ainda devemos tomar cuidado ao forçar certas conversões. O resultado pode não só ser inútil como
gerador de dores de cabeça, caso seja feita de forma pouco cuidadosa. 
Departamento de Ciência da Computação-UFF
Números de página
CONSTANTES
C tem uma notação especial para constantes de forma a ficar claro o seu tipo. Caso seja atribuído a uma
variável um número de ponto flutuante, este será convertido primeiramente para double, seja a variável tipo
float ou não. Outras vezes é mais prático, por motivo de documentação, escrever uma constante em base
hexadecimal do que em base decimal. Para evitar conversões automáticas desnecessárias (algumas vezes
prejudiciais) além de facilitar a documentação, os criadores desta linguagem adotaram o padrão de colocar após
o último algarismo uma letra indicando o tipo de constante. Usa-se F para indicar que a constante é tipo float, L
para indicar que é do tipo long e U para unsigned. No caso dos números hexadecimais, usa-se 0x como prefixo.
Pode-se usar tanto letras maiúsculas quanto minúsculas. Podemos ainda fazer combinações com estes
modificadores. Portanto, teríamos que 
100F é o número 100 de ponto flutuante
120000L é o número 120000 inteiro longo
65000U é um número inteiro não assinalado
13500UL é um número inteiro longo não assinalado
0X130 é o número 130 na base hexadecimal
Não custa relembrar que se não houver nenhuma declaração que o negue, as constantes são do tipo int.
Além destas há uma série de constantes pré-definidas (usualmente chamadas Sequências Escape ou de
Fuga) que são caracteres de controle de dispositivos como impressoras, monitores, etc. Abaixo temos estas
constantes.
\a Alerta(bell)
\b Retrocesso
\f Alimentação de formulário
\t Tabulação horizontal
\v Tabulação vertical
\\ Barra invertida
\? Interrogação
\' Apóstrofo
\o Número octal
\x Número Hexadecimal
Departamento de Ciência da Computação-UFF
Números de página
COMANDOS
Um comando é uma expressão válida seguida de um ponto-e-vírgula. Uma expressão válida por sua
vez é um conjunto de variáveis e operadores colocados de forma coerente. Para dar sentido mais claro a estas
palavras vamos a alguns exemplos de comandos válidos :
 a = b; (É atribuido à variável a o valor contido em b)
 a = a + c * d; (a recebe o produto de c por d e mais o próprio valor corrente)
 ; (Comando nulo)
Para comandos não válidos teremos :
 a = c (falta o ponto-e-vírgula)
 a = a ^& b; (^& não é um operador)
 a = 1z3 * b; (variável inválida)
BLOCOS
Um bloco é definido como um conjunto de comandos logicamente conectados. Ou seja, eles são
trabalhados como se fossem um único comando. A indicação de um bloco é determinada por um abre chave no
seu início e um fecha chave ao seu final, ou seja,
 {
 COMANDO 1
 COMANDO 2
 COMANDO 3
 .
 .
 .
 COMANDO n
 }
Pela definição, um bloco pode conter blocos.
Departamento de Ciência da Computação-UFF
Números de página
CONTROLE DE FLUXO
Temos aqui as formas de controle de fluxo em C. 
a) if (CONDIÇÃO)
 COMANDO1
 else
 COMANDO2
Se CONDIÇÃOfor verdadeira (diferente de “zero”), COMANDO1 será executada. Caso seja falsa (ou
seja, igual a “zero”), então COMANDO2 será executada.
Atenção: O else é opcional.
b) ? :
É uma simplificação da declaração if. Exemplifiquemos seu uso : 
var = expr1 ? expr2 : expr3
Se a expressão 1 for verdadeira, então é avaliada a expressão 2, senão é avaliada a expressão 3 e o
resultado, num caso ou no outro, será atribuído à variável var. 
c) switch , default e case
Estas declarações trabalham juntas, sendo que default é opcional. 
switch (EXPRESSÃO)
 {
 case constante1 : COMANDO
 case constante2 : COMANDO
 .
 .
 .
 case constante n : COMANDO
 default : COMANDO
 }
Quando uma opção é tomada, a declaração correspondente é executada e TODAS as instruções abaixo
também o serão. Para que tal fato não ocorra, é necessário terminar cada bloco com um break, palavra que
veremos mais abaixo. Caso o valor que EXPRESSÃO tomar não seja igual a nenhuma das constantes após a
palavra case, então a declaração que estiver associada a palavra default será executada. Caso ela não for usada,
haverá interrupção do laço.
Atenção: Observe o fato que os blocos de instruções na declaração switch, depois do case, dispensarem o abre
e fecha colchetes. Pela própria natureza da execução, não há a necessidade desta explicitação. Muitos
programadores, por questão de padronização, colocam abaixo de cada case um par de chaves definindo um
bloco mesmo este não sendo necessário. Como sempre, faça o que você achar mais claro.
Departamento de Ciência da Computação-UFF
Números de página
d) goto
É uma declaração comum em outras linguagens, mas que deve ser evitada sempre que possível, pois
geralmente dificulta o entendimento do programa. Dificilmente você encontrará um bom programador usando-a
a não ser em último caso. Há situações nas quais esta declaração, ao contrário do habitual, facilita a
compreensão do programa, embora sejam muito raras. 
Para a sua utilização, é necessário a indicação de um rótulo que é um identificador seguido de : (dois
pontos). 
.
.
goto ok;
.
.
ok : ....;
.
Aqui não veremos exemplos com goto. Possivelmente você verá seu emprego muito raramente se é que
algum dia você verá.
OBS.: Embora, a estrutura básica do if e do switch-case se assemelhe a de outras linguagens, observe
bem as diferenças.
Departamento de Ciência da Computação-UFF
Números de página
LAÇOS
Como toda linguagem, C tem uma série de instruções que executam laços controlados. Novamente,
tome cuidado com semelhanças.
a) while (EXPRESSÃO) COMANDO
Enquanto a expressão entre parênteses for verdadeira (diferente de zero) COMANDO é executado. 
b) do COMANDO while (EXPRESSÃO);
Funciona de maneira semelhante ao ítem "a". A diferença é que aqui a decisão de parada é após a
execução da EXPRESSÃO. 
c) for (EXPRESSÃO; CONDIÇÃO; EXPRESSÃO) COMANDO
Esta declaração é provavelmente conhecida por todos, por aparecer em várias linguagens. É comum
que a EXPRESSÃO1 seja uma inicialização de uma variável, CONDIÇÃO a condição de parada e
EXPRESSÃO2 o modificador do valor da variável mas poderemos ter coisas mais complicadas e interessantes. 
Este laço é equivalente a um laço while com a seguinte estrutura :
expressao1;
while(condição)
{
comando
expressão2;
}
Alguns programadores mais radicais recomendam o "esquecimento" para o for. Não chegamos a tanto.
Uma ressalva importante é que CONDIÇÃO é avaliada no início do laço, como podemos observar da
equivalência mostrada acima. O esquecimento deste fato pode levar a ações inesperadas.
Para os que acham que “todo for é igual”, daremos um exemplo das particularidades deste laço. A
expressão abaixo é válida em C
for (; ; ) COMANDO
e define um laço sem fim.
Palavras Auxiliares
a) break;
É um comando auxiliar no controle de laços. Se você fizer uma construção com vários laços embutidos,
ao encontrar esta palavra chave o programa interrompe o laço no qual se encontra e salta para o imediatamente
externo. 
b) continue;
Ao encontrar este comando, o programa saltará diretamente para a condição do laço, desconsiderando
os comandos posteriores a ela. No caso de usado num for, ao encontrar um continue, o controle passará para a
declaração 2 dentro dos parênteses do for. Só pode ser usado dentro de laços. 
Departamento de Ciência da Computação-UFF
Números de página
Fatos e mitos - Velocidade de processamento
Como já foi dito acima, C é uma linguagem que gera programas de alta velocidade de processamento.
Algumas pessoas já usaram C e acham que isto é uma balela já que "Na linguagem xxx um programa meu foi
executado mais rapidamente que em C." 
Um fato que devemos desde já chamar a atenção com ênfase é que C faz todas as suas operações de
ponto flutuante com o tipo double se você não obrigar que estas sejam feitas com outra precisão. Obviamente
isto gera duas consequências: Maior precisão no cálculo e maior tempo de execução. 
C é uma linguagem que gera programas rápidos, continuo insistindo, mas a única maneira de se fazer
um programa o mais rápido possível é o conhecimento não só da linguagem na qual programamos como
também da implementação particular do compilador que usamos e o computador no qual estamos trabalhando.
Haverá situações em que um programa em C será mais lento que o mesmo programa em alguma outra
linguagem. Isto obviamente não é nenhum demérito. Se C ou outra linguagem qualquer solucionasse todos os
problemas de programação, é claro que não haveria a profusão de linguagens que existe, cada uma mais
adaptada a determinadas tarefas. Devemos ter em mente as vantagens e limitações de cada linguagem e a
adequação dela ao problema que queremos resolver. Para evitar problemas e tomar uma atitude mais realista e
madura quanto à programar lembre-se da Lei de Murphy[ ] aplicada à computação:
Se você quer fazer uma besteira é fácil, 
mas se quiser fazer uma grande besteira, 
você vai precisar de um computador.
Departamento de Ciência da Computação-UFF
Números de página
ESTRUTURAS DE PROGRAMAS EM C: A FUNÇÃO main()
Um programa em C é uma chamada de função, no caso uma função especial de nome main(), a qual
chamaremos de função principal. Ela é a única função obrigatória de existir num programa em C e inicialmente
trabalharemos apenas com ela. O mais simples programa em C, contendo apenas a função main() (mas que não
funcionará!), tem a forma que se segue
main()
{
DECLARAÇÃO DAS VARIÁVEIS
COMANDO1
COMANDO2
.
.
COMANDOn
}
Todas as variáveis contidas no programa deverão ser declaradas.
Como toda linguagem de programação, em C você poderá também inserir comentários. Isto é feito
colocando em qualquer coluna uma barra seguida de um asterisco depois escrevendo seu comentário e então, ao
acaba-lo, teclando outro asterisco seguido de uma barra, ou seja,
/* comentario */
Obs: Não é permitido o aninhamento de comentários no ANSI C mas outras versões poderão permitir. Em
alguns momentos o aninhamento de comentários é cômoda mas muitas vezes (dependendo da forma de
implementação) pode ser fonte de erros bobos.
Escrevamos o nosso primeiro programa que: 
a)Some dois números inteiros e coloque o valor da soma num outro número inteiro.
b)Se a soma for maior que 9, faça a variável que contém a soma, ser igual a 9. 
main()
{ /* Primeiro programa */
int a, b, c;
a = 5;
b = 3;
c = a + b;
if ( c > 9 )
 {
 c = 9;
 }
}
Temos acima a declaração das variáveis a, b e c como inteiras seguida da atribuição de valores e então sua soma
sendo atribuida a variável c. Logo após temos o teste do valor da soma. Como a = 5 e b = 3, temos que o
resultado finalserá c = 8. Portanto, depois do teste, c continuará com o valor 8.
Para marcar bem a diferença entre o operador de atribuição = em C e em outras linguagens, vamos
reescrever este programa. 
Departamento de Ciência da Computação-UFF
Números de página
main() 
{ /* Primeiro programa, segunda versão */ 
int a = 5, 
 b = 3, 
 c; 
if ( (c = a + b) > 9 ) c = 9; 
}
O operador de atribuição é usado para inicializar as variáveis no momento de sua definição. Dentro dos
parênteses do if, há a atribuição do valor da soma de a com b à variável c. Só após feito isto é que haverá a
comparação e avaliação da condição. Esta maneira de usar o operador de atribuição pode parecer estranha
inicialmente mas, indiscutivelmente, tem o seu charme. Observe ainda que como só temos um comando para o
if , não foi criado um bloco.
Uma nova versão pode ser feita, agora com o uso do operador ? :
main() 
{ /* primeiro programa, terceira versão */ 
int a = 5, 
 b = 3, 
 c; 
c = (c = a + b) > 9 ? 9 : c; 
}
O resultado é idêntico. Aqui usamos o operador ? : no lugar do if. Este operador trabalha, no programa
acima, da seguinte maneira: 
Inicialmente as variáveis a e b são somadas e depois o resultado é atribuido à variável c. Verifica-se se
o resultado é maior que 9. Se for, a c é atribuido o valor 9. Se não, a c é atribuido o próprio c, que já contém a
soma de a e b. Observe os parênteses obrigando que a atribuição da soma seja feita antes do teste.
Façamos mais um programa. Queremos somar um número de ponto flutuante de precisão simples a ele
mesmo, 10 vezes enquanto ele for menor que 2000. Ao final devemos ter uma variável inteira que contenha
quantas vezes o processo de soma foi feito. 
main()
{ /* Segundo programa */
int n;
float x;
x = 235.;
for (n = 1; n <= 10; n++)
 {
 x = x + x;
 if ( x >= 2000.0) break;
 }
}
Observe o uso do operador de incremento na sua versão de pós-incremento dentro do for. Lembre-se: queremos
dizer com esta expressão que o valor da variável será usada e só então haverá o incremento. Note ainda o uso do
break para interromper o laço do for. Como já foi descrito, no momento que a palavra-chave break for
encontrada, o laço, dentro do qual está o break, será interrompido. 
Vamos reescrever o programa.
Departamento de Ciência da Computação-UFF
Números de página
main()
{ /* Segundo programa, segunda versão */
int n;
float x;
x = 235.;
n = 0;
do
 {
 x += x;
 n++;
 }
while ( (x < 2000.) && (n < 10));
}
Aqui temos o mesmo programa só que agora a saida é feita através de uma condição composta. São
testadas as condições de parada de tal forma que se pelo menos uma das duas for falsa haverá a interrupção da
execução. Lembre-se que o while continua sendo executado enquanto a expressão dentro dos parênteses for
verdadeira. Temos ainda o uso do operador de pós-incremento usado isoladamente e também a utilização da
notação abreviada para o cálculo de x. Observe bem as diferenças entre as duas versões não esquecendo que são
totalmente equivalentes, embora o for ter uma equivalência com o while e não com o do-while. 
Vamos a mais um programa. Temos uma variável do tipo caracter (char) e vamos provocar as
seguintes reações :
a) Se for um 'a' ou um 'b', uma variável inteira tomará o valor 1; 
b) Se for um 'c', valor 2;
c) Se for um 'd', 3;
d) Se for um 'e', o valor 4.
main()
{ /* Terceiro programa */
int numero;
char letra;
if ((letra == 'a') || (letra == 'b'))
 {
 numero = 1;
 }
else
 {
 if (letra == 'c')
 {
 numero = 2;
 }
 else
 {
 if (letra == 'd')
 {
 numero = 3;
 }
 else
 {
 if (letra == 'e')
 {
 numero = 4;
 }
 }
 }
 }
}
Departamento de Ciência da Computação-UFF
Números de página
Observe mais uma condição composta no primeiro if. Neste caso basta uma das duas expressões serem
verdadeiras para que a variável numero tome o valor 1. Embora interessante como exemplo de uso de if, o
programa não é um bom exemplo de programação.
Reescrevendo este programa (como está se tornando hábito) temos:
main()
{
int numero;
char letra;
switch (letra)
 {
 case 'a' : numero = 1;
 break;
 case 'b' : numero = 1;
 break;
 case 'c' : numero = 2;
 break;
 case 'd' : numero = 3;
 break;
 case 'e' : numero = 4;
 }
}
Aqui substituimos uma sequência de if's por uma declaração do tipo switch-case. A função do break,
chamamos novamente a atenção, é interromper a execução do bloco do switch.
Nada nos impede de reescrevermos o programa como o que se segue: 
main()
{ /* Terceiro programa, terceira vercao */
int numero;
char letra;
switch (letra)
 {
 case 'a' :
 case 'b' : numero = 1;
 break;
 case 'c' : numero = 2;
 break;
 case 'd' : numero = 3;
 break;
 case 'e' : numero = 4;
 }
}
Como o valor tomado pela variável numero é igual para os casos do caracter ser a ou b, deixamos sem
nenhuma declaração após os :. No fluxo do programa, caso o caracter seja um a, automaticamente teremos
como execução a atribuição do valor 1 a variável numero seguida da execução do break, que interromperá o
switch-case. 
A maneira prolixa da primeira versão (com if) foi para dramatizar as vantagens do uso do swith-case.
Departamento de Ciência da Computação-UFF
Números de página
Vamos a mais um exemplo. Agora descreveremos um problema a partir de sequência de instruções, ou
seja, de forma algorítmica. No caso será o algoritmo de Euclides para calcular o MDC, ou seja, o Máximo
Divisor Comum (quem diria, voltamos à escolinha!). O algoritmo é dado a seguir :
i) Sejam dois números inteiros e positivos m e n dos quais queremos achar o mdc. Seja ainda uma
variável r.
ii) Faça r tomar o valor do resto da divisão de m por n, fazendo então m tomar o valor de n e este
último o valor de r. 
iii) Se r não for nulo, volte a ii). Caso r for nulo, m terá o valor do mdc.
O programa que faz tais operações afim de calcularmos o MDC é dado abaixo :
main()
{
/* programa que calcula o mdc de dois numeros m e n */
int m = 120,
 n = 9,
 r;
do
 {
 r = m % n;
 m = n;
 n = r;
 }
while(r != 0);
}
Usamos aqui mais um operador aritmético, o %, que dá o resto da divisão de dois números. No while
poderiamos usar no lugar da expressão r != 0 algo como !r. Funcionaria do mesmo jeito com uma aparente
vantagem de ser mais compacto sendo, no entanto, mais enigmático. Aqui se apostou em ser claro e, sempre que
possível, esta deve ser uma tática de programação.
Departamento de Ciência da Computação-UFF
Números de página
Indentação
Repare que em todos os programas já exibidos, cada palavra reservada, cada variável, não é escrita a
esmo mas obedece uma regra de posicionamento em cada linha. Esta técnica geralmente é chamada de
indentação e é usada em todas linguagens ditas estruturadas. A idéia por trás de tal comportamento não é só
fazer programas bonitinhos mas sim, fazer programas mais legíveis. C já é uma linguagem muito concisa e se
comecamos a teclar cada linha de qualquer jeito, teremos problemas de legibilidade que poderão dificultar a
depuração e manutenção de programas e sistemas. Notem ainda que são colocados espaços entre variáveis e
operadores e espaço entre as linhas em muitas situações. 
O estilo usado aqui é simplesmente um estilo, existindo maneiras diferentes de organizar as linhas de
um programa. A linguagem C permite esta liberdade no estilo da escrita de tal forma que podemos modificar
ligeiramente a forma de escrever um programa da maneira que mais nos agradar.Os programas estão escritos
neste texto de forma relativamente comum não sendo, obviamente, A Maneira Correta mas apenas (repito) uma
maneira de se escrever programas em C. É comum se usar de 2 à 5 espaços para marcar a indentação mas isto
pode variar de acordo com seu próprio estilo.
Por exemplo, podemos escrever uma declaração if como
if ( a > b) {
 c = a + b;
 d = a * c;
.}
else {
 c = a - b;
 d = a + c * b;
}
if (a > b) 
{
 c = a + b;
 d = a * c;
{
else
{
 c = a + b;
 d = a * c;
}
if (a > b) 
 {
 c = a + b;
 d = a * c;
 }
else
 {
 c = a + b;
 d = a * c;
 }
ou outra maneira qualquer.
No entanto, seja lá qual estilo você adotar, tenha em mente melhorar a legibilidade do programa. Para
perceber o que queremos dizer, vamos a um caso extremo: o programa do MDC poderia ser escrito como abaixo
main(){int m=120,n=9,r;do{r=m%n;m=n;n=r;}while(r!=0);}
As vantagens de escrever assim são ainda desconhecidas mas alguns insistem.....
Departamento de Ciência da Computação-UFF
Números de página
FUNÇÕES
Forma Clássica
Até o momento, sobre funções só foi dito que existiam e que main() era uma função. Agora falaremos
de funções de forma mais genérica. Em C as funções podem ser escritas da seguinte forma, (chamada forma
clássica)
TIPO nome_da_função(parâmetro 1,parâmetro 2,...,parâmetro n)
DECLARAÇÃO DE PARÂMETROS
{
DECLARAÇÃO DE VARIÁVEIS
COMANDO1
COMANDO2
.
.
COMANDOn
return(parâmetro de saida);
}
onde TIPO diz respeito ao tipo de variável que será devolvida pela função. Observe mais uma palavra chave,
return. Podemos encontrar esta palavra chave sendo usada de duas maneiras: com o parâmetro de saida entre
parênteses e com o mesmo parâmetro logo após o return. Esta palavra é optativa, não havendo necessidade de
aparecer em nenhum ponto do bloco principal da função. Também podemos ter vários return dentro de uma
função embora isto não seja recomendável. 
A semelhança da definição de uma função com um programa em C é por uma razão óbvia: o programa
principal de C é uma função, como já foi dito. Dependendo da forma de definição de função, você pode colocar
a declaração de função antes ou depois da função main(). Aqui colocaremos as funções antes, de forma que um
programa em C pode ter a seguinte estrutura (mas ainda não funcionará!): 
 TIPO nome_da_função(parâmetro 1,parâmetro 2,...,parâmetro n)
 DECLARAÇÃO DE PARÂMETROS
 {
 DECLARAÇÃO DE VARIÁVEIS
 COMANDOS
 }
 main()
 {
 DECLARAÇÃO DAS VARIÁVEIS
 COMANDOS
 }
É bom chamar a atenção para que as variáveis declaradas dentro das funções são "invisíveis" para o
programa principal ou para as outras funções (daqui a pouco falaremos mais sobre esta "invisibilidade").
As funções sempre devolvem algum valor, mesmo que não haja um return. Se este valor tem
significado ou não, quem decide é o programador. Além disto, não há obrigatoriedade de usarmos o valor
retornado. Quanto aos argumentos, eles são passados por valor e não por referência. Isto significa que os
valores dados através de variáveis do programa principal não serão modificados no programa principal mesmo
que você as manipule dentro da função. Se tiver como parâmetros, por exemplo, x e y que valem
respectivamente 2 e 3, se houver uma multiplicação entre um e outro atribuindo a x o resultado dentro da
função, veremos que o valor de x continuará igual a 2 no programa principal. 
Isto aparentemente limita o uso mas, como veremos, este fato não se trata de uma limitação mas apenas
uma maneira de termos, explicitamente, conhecimento do alcance das operações que serão feitas através das
funções.
Departamento de Ciência da Computação-UFF
Números de página
Como exemplo faremos do primeiro programa uma função a qual chamaremos tetof(). 
int tetof(a,b,teto)
int a,b,teto;
 {
 int c;
 if ( (c = a + b) > teto) c = teto;
 
 return(c);
 }
main ()
{
int a = 5,
 b = 3,
 c,
 tetof();
/* Primeiro programa, quarta vercao */
c = tetof(a, b, 9);
}
Novamente o funcionamento é igual.
Outro detalhe a saber é que se nada for indicado, ficará pressuposto que a função devolverá um inteiro.
A razão disto já sabemos: C trabalha preferencialmente com variáveis do tipo inteiro. Portanto se nada for
especificado quanto ao tipo de valor que uma função devolverá, se considerará que o resultado será um inteiro.
Se a função devolver um valor não compatível com inteiro, você precisa avisar a função que a chama, o tipo de
resultado que ela irá devolver. Isto é feito junto ao definir a função e com a declaração de variáveis. Vamos
dizer que uma função é do tipo char e seu nome é funcao1(). Então, na função que a chama deve haver junto, a
declaração de variáveis, o que se segue: 
char funcao1();
Observe que não é necessário colocar os parâmetros da função mas é necessário colocar os parênteses para que
o compilador saiba que é uma definição do tipo de valor devolvido pela função e não a declaração de uma
variável.
Como mais um exemplo, daremos uma função que calcula o fatorial de um número. 
Lembre-se que o fatorial de um número inteiro positivo não nulo é o produto dele por todos os números
positivos não nulos menores que ele.
int fatorial(n) 
int n; 
 { 
 int i, fat; 
 for (i = 1; i <= n; i++) 
 {
 fat = fat * i; 
 }
 return(fat); 
 } 
Departamento de Ciência da Computação-UFF
Números de página
main() 
{ 
int fatorial(), 
 n = 5,
 f; 
f = fatorial(n); 
} 
Repare que na lista de declaração de variáveis encontramos fatorial() colocado como outra variável
qualquer. Mas lembre-se, o que queremos dizer é que o valor retornado é inteiro.
Chamemos a atenção para alguns aspectos sobre funções em C. Ao contrário de outras linguagens
(Pascal, por exemplo) C não permite aninhamento de funções, ou seja, não admite que tenhamos funções
declaradas dentro de funções. Ainda temos que todas as funções em C são “visíveis” umas pelas outras. Tal
propriedade, no jargão habitual, se traduz como: 
As funções em C são globais.
EXERCÍCIO
 I) O programa acima só calcula corretamente fatoriais até o de 7 (sete). Porque ? 
Departamento de Ciência da Computação-UFF
Números de página
VARIÁVEIS GLOBAIS, LOCAIS E AUTOMÁTICAS
Já foi dito que as variáveis declaradas dentro de uma função são "invisíveis" para outras funções.
Algumas vezes queremos que uma determinada variável "exista" não só para uma função ou para o programa
principal, mas para todas as funções constituintes do programa, ou seja, seja de conhecimento GLOBAL.
Partindo da nossa definição imperfeita de programa em C (que ainda não funcionará...), apresentamos como
definir tais variáveis: 
DECLARAÇÃO DE VARIÁVEIS GLOBAIS
 TIPO nome_da_função(PARÂMETRO 1,PARÂMETRO 2,...,PARÂMETRO n)
 DECLARAÇÃO DE PARÂMETROS
 {
 DECLARAÇÃO DE VARIÁVEIS
 DECLARAÇÕES
 }
 main()
 {
 DECLARAÇÃO DAS VARIÁVEIS
 DECLARAÇÕES
 }
Aqui devemos dar uma parada para dar nomes aos bois. As variáveis declaradas fora das funções
(incluindo a main()) são chamadas GLOBAIS e as declaradas dentro das chaves são chamadas de LOCAIS,
DINÂMICAS ou AUTOMÁTICAS. Usaremos aqui o termo LOCAIS. Não há uma palavra reservada para
declarar variáveis como globais mas existe uma para declarar como automática: ela é auto. Como todas as
variáveis em C são criadas como automáticas, raramente (para não dizer nunca) você verá esta palavra (auto)
em uso. Outra coisa a ser dita é que as variáveis globais ocupam espaço na memória o tempo todo enquanto as
variáveis locais só ocupam espaço enquanto a execução do programa estiver dentro dos blocos nos quais estas
variáveis estão declaradas. Quando a execução sai destesblocos, as variáveis locais "evaporam", poupando
memória.
Além disto, é sempre boa política usar o mínimo de variáveis globais, já que estas são ativas para toda
função dentro de um programa podendo, assim, gerar reações inesperadas por ter um trecho de programa
alterando uma variável global que em outro trecho era esperada com outro valor. Uso de variáveis globais de
forma indiscriminada leva à dificuldades no reaproveitamento de código e dificuldades no acompanhamento do
processamento de um programa já construido. Os usuários de FORTRAN são particularmente tentados à usar
variáveis globais a todo isntante. Se for o seu caso lembre-se que aqui você está programando em C, pensando
em C e que FORTRAN é outro departamento. 
Departamento de Ciência da Computação-UFF
Números de página
PREPROCESSADOR: CABEÇALHOS E ARQUIVOS DE INCLUSÃO 
Imagine que você criasse um conjunto de funções que trabalhasse com a tela do computador, outro
conjunto de funções que contivesse todas as funções de entrada e saída de dados e outro conjunto e outro e mais
outro. Se você é um programador organizado, talvez achasse interessante que pudesse separar estas funções em
coleções de funções e invocar apenas as coleções que fossem necessárias. Em C podemos fazer isto de maneira
simples: Basta criar arquivos contendo as funções, constantes e definições relativas às mesmas. Assim, um
programa em C poderá ser constituído de uma coleção de vários arquivos. 
No entanto, tais arquivos podem depender de definições em comum. Como então fazer com que todos
os arquivos compartilhem estas definições? O que temos é um tipo especial de arquivo denominado Arquivo de
Cabeçalho e é justamente nele que guardamos tais definições. Quanto ao nome deste arquivo, existe a
convenção de usar a extensão .h para marcar o tipo deste arquivo. Se você tem uma vasta biblioteca de funções
para os mais variados fins (teclado, gráficos, gerenciar arquivos em disco, etc.) poderá invocar apenas as que
necessitar. 
Como é que eu invoco tais coleções é uma coisa ainda não dita. Para fazer esta invocação, usaremos
uma declaração do chamado preprocessador, que é mais uma parte integrante da linguagem C. Toda declaração
do preprocessador é indicada por começar pelo caracter # (“cardinal” ou “velha”). A que veremos aqui é a
declaração #include que se responsabilizará por carregar o cabeçalho invocado, também chamado arquivo de
inclusão.
A forma geral deste comando é o que se segue
#include "nome_do_cabecalho.h"
mas poderá ser encontrada também nesta forma
#include <nome_do_cabecalho.h>
A diferença entre um e outro é que o primeiro (entre aspas duplas) o arquivo de inclusão se encontra no
diretório de trabalho corrente, enquanto o segundo (entre < e >) se encontra no diretório padrão onde se
localizam os arquivos de inclusão.
Obs.:
I) Um aspecto que devemos chamar a atenção é que dentro de um arquivo de cabeçalho podemos ter
outros #include. O número de aninhamentos destas declarações é dependente de cada compilador. 
II) Alguns compiladores automaticamente incluem alguns cabeçalhos automaticamente. Antes de ser
uma comodidade isto é um problema. Corremos o risco de ter um programa compilando e funcionando numa
máquina e não funcionando em outra. Para evitar vícios e (com isto) problemas futuros, seja explícito sempre e
configure seu compilador para não aceitar “bibliotecas default”. 
Com a inclusão do preprocessador, um programa em C pode ter a seguinte forma (que, finalmente,
funcionará!): 
Departamento de Ciência da Computação-UFF
Números de página
COMANDOS DO PREPROCESSADOR 
DECLARAÇÃO DAS VARIÁVEIS GLOBAIS 
FUNÇÃO1(PARÂMETROS) 
DECLARAÇÃO DE PARÂMETROS 
{ 
 DECLARAÇÃO DAS VARIÁVEIS LOCAIS 
 CORPO DA FUNÇÃO 
} 
FUNÇÃO2(PARÂMETROS) 
 
.
.
main() 
{ 
 DECLARAÇÃO DE VARIÁVEIS LOCAIS 
 COMANDOS
} 
A estrutura acima é muito comum de se encontrar, embora possamos colocar comandos do
preprocessador no meio de um programa.
Falaremos detalhadamente sobre o preprocessador mais adiante. 
OBS.: Uma pergunta que deve estar surgindo na cabeça do leitor mais atento é: Se toda função retorna um
valor a função main() devolve o que? E para quem? Um pouco mais a frente teremos estas respostas.
Departamento de Ciência da Computação-UFF
Números de página
ALGUMAS FUNÇÕES ÚTEIS (Da stdio.h)
A linguagem C não tem comandos para impressão. C deixa isto a cargo de funções e aqui vamos
aprender como funcionam algumas para podermos trabalhar um pouco mais satisfeitos. Abaixo temos algumas
que se encontram em stdio.h, ou seja, entrada e saída padrão (stardard in/out). Primeiro uma que escreve
caracteres na tela :
 printf("CONTROLE", PARÂMETRO1,PARÂMETRO2,....,PARÂMETROn);
onde CONTROLE, contém declarações de formato e caracteres. O formato tem as opções: 
 %c caracter simples
 %d decimal inteiro
 %f ponto flutuante
 %e ponto flutuante em notação científica)
 %o octal
 %s cadeia de caracteres
 %u decimal inteiro sem sinal
 %x hexadecimal
Há ainda caracteres especiais (alguns já apresentados quando falamos de constantes), alguns dos quais estão
listados abaixo:
 \b retrocesso
 \f saltar página (ou limpar tela)
 \n saltar para próxima linha
 \r retorno de carro
 \t tabulação horizontal
 \0 nulo
\\ Barra reversa
Um exemplo.
printf(" i = %d \n x = %f -- y = %f", i,x,y);
 Se i for igual a 1, x igual a 2.345679 e y igual a 3.141582, a saída será da forma : 
i = 1
x = 2.345679 -- y = 3.141582
Observe: o que não for especificação de formato ou caracter especial é escrito como foi colocado.
Os valores impressos são ajustados para a esquerda. Além disto, se passamos um número de ponto
flutuante para esta função, ele sairá com todos os algarismos que o seu tipo permitir. Podemos formatar a saída
limitando o número de casas decimais num ponto flutuante ou posicionando as variáveis de maneira mais
conveniente. O formato para ponto flutuante é o que se segue
[-]m.d
onde o sinal - indicaria ajuste a esquerda, m o espaço total e d o número de casa decimais que devem parecer. O
simples fato de querermos formatar a saída, faz com que o ajuste se faça pela direita, daí a necessidade do sinal
negativo para indicarmos que queremos que o ajuste continue pela esquerda. 
Isto é feito colocando antes do símbolo de porcentagem um número que corresponderá ao espaço que
será reservado para a variável em questão. No caso de um número de ponto flutuante, podemos dar não só o
espaço para o número a ser impresso como também o número de casas decimais depois do ponto decimal
adicionando após o número de espaços reservados para o número a ser impresso um ponto seguido do número
de casas decimais. Abaixo temos várias opções de formato escritas a partir do exemplo acima não formatado e
com suas correspondentes saídas.
a) printf("\n i = %10d x = %10f -- y = %10f", i,x,y);
 i = 1 x = 2.345679 -- y = 3.141582
Departamento de Ciência da Computação-UFF
Números de página
b) printf("\n i = %-10d x = %-10f -- y = %-10f", i,x,y);
 i = 1 x = 2.345679 -- y = 3.141582
c) printf("\n i = %-10.4d x = %-10.4f -- y = %-10.4f", i,x,y);
 i = 0001 x = 2.3456 -- y = 3.1415
Outras funções úteis são especificadas abaixo:
a) ch = getch() - Devolve em ch um inteiro sem sinal correspondente ao caracter lido no teclado. (Esta
função se encontra definida em conio.h (complementar in/out))
b) putchar(ch) - Manda um caracter para a tela. 
c) exit(i) - Interrompe a execução do programa. Se i = 0, indica uma finalização normal.
Façamos mais um programa. Este deverá, inicialmente, escrever na tela um menu com o seguinte
conteúdo : 
<1> : imprime em formato ASCII
<2> : imprime em octal
<3> : imprime em decimal
<4> : imprime em hexadecimal<Outras> : sai do programa
e após escrever uma mensagem para entrar com um caracter. Feito isto, leia o caracter, imprima no formato
pedido e volte ao menu. 
Departamento de Ciência da Computação-UFF
Números de página
#include <stdio.h> 
#include <conio.h>
main() 
{ 
do 
 { 
 int ch, opcao; 
 printf(" <1> : imprime em formato ASCII \n"); /* Impressao do cabecalho */
 printf(" <2> : imprime em octal \n"); 
 printf(" <3> : imprime em decimal \n"); 
 printf(" <4> : imprime em hexadecimal \n"); 
 printf("<Outras> : sai \n"); 
 printf(" Entre com uma opção "); 
 opcao = getch(); 
 printf("\n"); 
 printf(" Entre com um caracter "); 
 ch = getch(); 
 switch (opcao) 
 { 
 case '1' : printf("\n ASCII - %c\n",ch); 
 break; 
 case '2' : printf("\n octal - %o\n",ch); 
 break; 
 case '3' : printf("\n decimal - %d\n",ch); 
 break; 
 case '4' : printf("\n hexadecimal - %x\n",ch); 
 break; 
 default : exit(0); 
 } 
 }
while(1); 
} 
Observe o uso do while com a expressão de controle permanentemente verdadeira (diferente de zero) e,
portanto, criando um laço sem fim. Assim, a única forma de saída do programa é através do apertar de qualquer
tecla, menos as válidas, pois isto forcaria a execução da função exit() que, como já vimos, interrompe o
programa.
Faça alguns testes e você verá que se você entrar com, por exemplo, uma letra teremos um número
como resposta, se pedirmos uma opção diferente da de número 1. Isto não deve causar estranheza pois os
formatos de saída do printf(), nas outras opções, são do tipo numérico. Lembramos que ao apertarmos a tecla
correspondente a um caracter ou outra qualquer, não estamos na realidade "mandando" uma letra para o
computador mas simplesmente um código. Como os caracteres tem nos computadores uma representação
interna numérica dada por um determinado padrão, fica fácil de entender os números que vão surgindo durante
o uso deste programa. Você encontrará tabelas de algumas destas representações num dos apêndices deste texto. 
Se examinarmos o programa acima, encontraremos um pequeno defeito. Se você quiser sair do
programa o mesmo pedirá para você entrar com um caracter, o que é totalmente desnecessário. Vamos criar uma
versão deste programa no qual evitaremos este problema.
Departamento de Ciência da Computação-UFF
Números de página
#include <stdio.h> 
#include <conio.h>
int le_char()
 {
 printf(" Entre com um caracter "); 
 return (getch()); 
 }
main() 
{ 
do 
 { 
 char ch, opcao,
 le_char(); 
/* Impressao do cabecalho */
 printf(" <1> : imprime em formato ASCII \n"); 
 printf(" <2> : imprime em octal \n"); 
 printf(" <3> : imprime em decimal \n"); 
 printf(" <4> : imprime em hexadecimal \n"); 
 printf("<Outras> : sai \n"); 
/* --------------------- */
 printf(" Entre com uma opcao "); 
 opcao = getch(); 
 printf("\n"); 
switch (opção) 
 { 
 case '1' : ch = le_char();
 printf(" ASCII - %c\n",ch); 
 break; 
 case '2' : ch = le_char();
 printf(" octal - %o\n",ch); 
 break; 
 case '3' : ch = le_char();
 printf(" decimal - %d\n",ch); 
 break; 
 case '4' : ch = le_char();
 printf("hexadecimal - %x\n",ch); 
 break; 
 default : exit(0); 
 } 
 }
while(1); 
} 
Criamos uma função com o objetivo de mandar uma mensagem para a tela e ler o caracter. Daqui a pouco
faremos uma outra versão um pouco mais interessante. 
OBS.: A forma que usamos o while (colocando uma constante dentro dos parênteses) NÃO DEVE SER
IMITADA. Observe que 1 não tem significado nenhum em si. Devemos sempre usar uma variável ou uma
definição (isto veremos abaixo) no lugar de colocar constantes “mágicas”. Comentários ao lado podem até
esclarecer o funcionamento mas o ideal é que deixemos a variável "falar por si", ou seja, que ela tenha um
"nome" que já seja o suficiente para mostrar a sua função.
Departamento de Ciência da Computação-UFF
Números de página
Mais um exemplo será útil para marcar as diferenças entre o for de C e laços semelhantes de outras
linguagens. Aqui teremos duas variáveis sendo “contadas” pelo mesmo for mas em “ritmos” diferentes.
main()
{
int impr, i, 
 nimpr, nt;
nt = 100;
nimpr = 10;
for (impr = 1, i = 1; impr < nt; impr += nimpr, i++)
 {
 printf("\n impr = %d ### i = %d \n", impr, i);
 }
}
Neste caso, serão impressos os valores de impr que irão de 1 até 91 de 10 em 10, ou seja, serão gerados
os números 1, 11, 21,...,91 enquanto i irá de 1 até 10 estando o laço sob controle da condição sobre impr.
EXERCÍCIOS
I)Experimente não usar a palavra reservada break no programa três, terceira versão.
II) Use a função printf() para observar as saídas de todos os programas dados anteriormente como
exercício. (Não se esqueça de colocar #include <stdio.h>).
III) Desenvolva uma função que imprima um inteiro ou um caracter em forma binária. Feito isto,
utilize-a no programa acima, criando outra opção no menu.
Departamento de Ciência da Computação-UFF
Números de página
PONTEIROS E FUNÇÕES
Vamos agora falar de um tipo de variável extremamente poderosa: o ponteiro, também chamado
apontador. Recordemos que existem dois operadores chamados operadores de endereço e estes são
representados por: 
& "o endereço de" . Ex. : a = &b. a recebe o endereço da variável "b". 
* "no endereço de". Ex. : c = *d. c recebe o valor da variável "apontada" por d. 
Observemos que num computador, quando escrevemos algo como :
a = b;
O computador faz as seguintes operações: pega o conteúdo da posição da memória correspondente a variável b,
coloca este valor na posição da memória correspondente a variável a. Ou seja, é uma transferência do conteúdo
de endereço para conteúdo de endereço. Os operadores de endereço nos permite obtermos endereços de
variáveis ou tendo endereço de uma variável, saber seu conteúdo. 
As variáveis que contém os endereços de outras variáveis denominamos PONTEIROS ou
APONTADORES. É claro que aqui surge uma suspeita: Como as variáveis tem estruturas diferentes,
provavelmente teremos tipos diferentes de ponteiros para cada tipo de variável. Para usarmos o ponteiro certo
com a variável certa, teremos que definir os ponteiros tal como fazemos com as outras variáveis. A notação é a
que se segue 
 TIPO *nome_d_ponteiro_1, *nome_do_ponteiro_2, ...;
Podemos fazer, se quisermos, a definição dos ponteiros junto com as das variáveis.
Lembre-se sempre que um ponteiro não tem "nenhuma" informação, o que ele tem é o endereço de
uma informação.
Mas, neste ponto, alguém pode perguntar: Porque o título desta parte é PONTEIROS E FUNÇÕES? É
justamente aqui que veremos uma dos usos dos operadores de endereço. Para demonstrar esta utilidade, daremos
um exemplo clássico de uma função que NÃO FUNCIONA. Abaixo temos escrita uma função que faria a
permuta entre duas variáveis. 
#include <stdio.h>
permutar(a,b) 
int a,b; 
 { 
 int auxiliar;
 auxiliar = a; 
 a = b; 
 b = auxiliar; 
 } 
main() 
{ 
int a = 5, 
 b = 3, 
 c = 7; 
permutar(a,b); 
permutar(a,c); 
printf(" %d, %d, %d .",a,b,c); 
} 
O que aconteceria se executássemos este programa? As variáveis a e b não teriam os seus valores
trocados. Lembrem-se que as variáveis, como estão declaradas são locais e que funções em C só trabalham
passando valores. O que fizemos dentro da função permutar() não afetará o conteúdo das variáveis a, b e c do
programa principal. 
Departamento de Ciência da Computação-UFF
Números de página
A saída desta situação não esta em declarar a, b e c como variáveis globais, já que assim você sópoderia intercambiar estas variáveis. A função ficaria restrita ao programa que você esta fazendo e a certas
variáveis de entrada. 
A saída esta no fato de que a diferença entre as variáveis locais de cada função (a permutar() e a main
()) é que elas ocupam posições diferentes na memória. Se passarmos o endereço da variável em vez do valor da
variável, temos uma maneira de afetar este valor. Vejamos uma versão modificada do programa anterior:
#include <stdio.h>
permutar(a, b)
int *a,*b;
 {
 int auxiliar;
 auxiliar = *a;
 *a = *b;
 *b = auxiliar;
 }
main()
{
int a = 5,
 b = 3,
 c = 7;
permutar(&a, &b);
permutar(&a, &c);
printf(" %d, %d, %d .", a, b, c);
} 
Através do operador & ("o endereço de"), passamos para a função permutar() os endereços de a e b
do programa principal. Ao entrar na função, observemos que os parâmetros foram declarados com o uso do
operador * ("no endereço de"). Portanto *a e *b são os conteúdos das variáveis a e b do programa principal. No
momento que eu estou fazendo as trocas dentro da função, estamos na verdade trocando os conteúdos das
variáveis a e b do programa principal!
Uma pergunta a ser feita é:
Já que toda função em C retorna algo, como fica permutar() que não precisa devolver nada? A
intenção em não colocar nada na declaração do tipo de saída é tentar passar a idéia de que qualquer coisa
retornada pela função não tem significado. No entanto, esta não é uma boa maneira. O ANSI C apresenta uma
solução e a veremos mais a frente. 
Repare ainda o formato de saída das variáveis. 
Departamento de Ciência da Computação-UFF
Números de página
PONTEIROS E VETORES
Vetores são encontradas em muitas linguagens e C não escapa disto. Se declara um vetor em C da
seguinte maneira: 
TIPO nome_do_vetor [TAMANHO]
com uma ressalva: vetores em C começam do índice 0 (zero) e acabam em TAMANHO menos um. Por exemplo
int vetor[10];
float evento[4];
Acima estamos declarando um vetor inteiro de dez posições e um vetor de ponto flutuante de quatro posições. O
elemento vetor[0] é o primeiro e vetor[9] é o último. Analogamente para o vetor evento, o primeiro elemento
será evento[0] e o último evento[3]. 
No caso de cadeia de caracteres temos uma situação especial. Se declararmos o vetor
char titulo[10] 
teremos uma cadeia de caracteres de no máximo 9 (NOVE) caracteres e não dez. Uma das características de C é
que toda cadeia de caracteres tem seu fim marcado por um caracter nulo. (Como atribuir à um vetor uma cadeia
de caracteres? Um momentinho só! Já veremos isto.) Então lembre-se sempre : 
"Para trabalhar com cadeias de caracteres, devemos deixar espaço para o caracter terminador da
cadeia (o nulo). "
Outra particularidade :
"Não há verificação dos limites de um vetor. "
O esquecimento deste fato é uma grande fonte de erros. 
Mas o que os vetores tem com os ponteiros? Para explicar isto, de novo vamos falar de como o
computador trabalha só que agora com vetores. Um vetor é geralmente armazenado na memória do computador
em posições contíguas. Tendo a primeira posição de memória (que chamaremos de posição zero), acrescentando
o índice do vetor a esta posição multiplicado pelo tamanho do tipo de variável, teremos a posição do elemento
do vetor correspondente ao do índice. 
Em C um vetor, na verdade, é um ponteiro para o início da seqüência de elementos do vetor. Podemos,
então, referenciarmos elementos do vetor tanto pelos índices como através de ponteiros. 
Devemos aqui chamar a atenção que, o trabalho com ponteiros é mais rápido que o trabalho com os
índices. Esta opção dupla existe já que certos algoritmos (como os de ordenação) são mais simples de serem
implementados se a referência é feita por índice enquanto outros ficam mais simples com o uso de ponteiros.
Como exemplo, criaremos uma função que escreve na tela uma cadeia de caracteres usando a função
putchar(), já descrita. 
#include <stdio.h>
putstr(s,n)
/* Setimo programa */
char *s;
int n;
 {
 int i;
 for (i = 0; i < n; ++i)
 {
 putchar(s[i]);
 }
 }
main()
Departamento de Ciência da Computação-UFF
Números de página
{
char s[10];
.
.
putstr(s,10);
}
Você terá que passar como parâmetros não só o vetor como também o seu tamanho. Na verdade, não
há a necessidade de passar o número de elementos. Poderíamos usar uma palavra chave que no informa o
tamanho de uma variável, mas não iremos nesta direção. Mas, e se lembramos que uma cadeia de caracteres
termina por nulo? Vejamos uma versão do programa acima que se utiliza disto. 
#include <stdio.h>
putstr(s)
char *s;
 {
 int i;
 for (i = 0; s[i]; ++i)
 {
 putchar(s[i]);
 }
 }
main()
{
char s[10];
.
.
putstr(s);
}
Observe a condição de parada do for feita usando o fato que em C, uma cadeia de caracteres termina num nulo.
Ficou bem elegante, embora um tanto quanto desagradável. Ainda podemos melhora-la. 
#include <stdio.h>
putstr(s)
char *s;
 {
 while (*s) putchar(*s++);
 }
main()
{
char s[10];
.
.
putstr(s);
}
Observe que, com ponteiros, a função fica mais simples e ainda mais elegante.
Esta função, ou melhor, sua equivalente, está na biblioteca padrão de C com o nome de puts(). 
Departamento de Ciência da Computação-UFF
Números de página
ARITMÉTICA DE PONTEIROS
O número de operações aritméticas que podem ser feitas com os ponteiros é bem restrita, pela própria
natureza deles. Lembre-se que o ponteiro em si não tem nenhum significado. Ele apenas nos diz onde se
encontra a informação que queremos acessar. É bom sempre ter na cabeça que :
" Ponteiros não são inteiros."
Como a estrutura de endereçamento pode variar de computador para computador, não necessariamente
o endereço pode ser representado por um número inteiro. Além disto, mesmo que pudéssemos representar os
ponteiros como inteiros, obviamente os conceitos envolvidos são totalmente diferentes e, lembro mais uma vez,
não devem ser misturados. Abaixo tabelamos os operadores que são válidos em operações com ponteiros.
Suponhamos dois ponteiros p e q e um inteiro i: 
++p pré-incrementa o valor do ponteiro
--p pré-decrementa o valor do ponteiro
p++ pós-incrementa o valor do ponteiro
p-- pós-decrementa o valor do ponteiro
*p acessa o conteúdo do endereço apontado por p
p+i Soma de um ponteiro com um inteiro
p-i Subtração de um inteiro sobre o ponteiro
p-q Subtração entre ponteiros desde que apontem para o mesmo vetor
Os operadores de incremento e de decremento com ponteiros, trabalham de tal forma que levam em
consideração o tipo de ponteiro que está sendo usado. Portanto se estamos usando um destes operadores com um
ponteiro de inteiro, automaticamente o incremento (ou decremento) será de dois bytes. Se temos um ponteiro
para ponto flutuante do tipo float, o incremento (ou decremento) será de quatro bytes.
Algumas vezes é interessante compararmos ponteiros. Imagine que tenhamos dois ponteiros p1 e p2
(p1 > p2), que referenciam um determinado vetor. Logo p1 - p2 nos dará o número de elementos entre estes
ponteiros. Observe, no entanto, que não há sentido em compararmos ponteiros que não estejam referenciando a
mesma estrutura de dados. 
Departamento de Ciência da Computação-UFF
Números de página
INICIALIZANDO VETORES 
Podemos inicializar os valores dos vetores no momento da declaração desde que eles tenham sido
declarados globais. A forma geral é a seguinte
TIPO nome[TAMANHO] = {ELEMENTO0, ELEMENTO1,..,ELEMENTO(n-1)); 
e para o caso especial de vetores do tipo char
char nome[TAMANHO + 1] = "cadeia de caracteres";
ou 
char nome[] = "cadeia de caracteres";
com o fato interessante de não determinarmos o tamanho da cadeia. Mais uma gentileza do compilador que
dimensiona por você. Se a entrada da cadeia de caracteres

Outros materiais