Buscar

Apostila C UFU

Prévia do material em texto

Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 1
 
 
 
 
 
 
 
 
 
 
 
 
Apostila Linguagem C 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 2
Sumário 
INTRODUÇÃO ........................................................................................................................................ 3 
EXPRESSÕES ........................................................................................................................................... 4 
TIPOS BÁSICOS DE DADOS .............................................................................................................................. 4 
NOMES DE IDENTIFICADORES .......................................................................................................................... 5 
VARIÁVEIS ................................................................................................................................................... 5 
OPERADORES ............................................................................................................................................... 9 
ABREVIAÇÕES EM C ..................................................................................................................................... 12 
COMANDOS DE CONTROLE .................................................................................................................. 13 
COMANDOS DE SELEÇÃO .............................................................................................................................. 13 
COMANDOS DE ITERAÇÃO ............................................................................................................................ 18 
COMANDOS DE DESVIOS .............................................................................................................................. 22 
MATRIZES E STRINGS ........................................................................................................................... 26 
MATRIZES UNIDIMENSIONAIS........................................................................................................................ 26 
PASSANDO VETORES PARA FUNÇÕES ............................................................................................................... 27 
STRINGS .................................................................................................................................................... 28 
MATRIZES BIDIMENSIONAIS .......................................................................................................................... 30 
MATRIZES DE STRINGS ................................................................................................................................. 31 
INICIALIZAÇÃO DE MATRIZES ......................................................................................................................... 32 
PONTEIROS .......................................................................................................................................... 33 
VARIÁVEIS PONTEIROS ................................................................................................................................. 33 
OPERADORES DE PONTEIROS......................................................................................................................... 33 
ATRIBUIÇÃO DE PONTEIROS .......................................................................................................................... 34 
INCREMENTANDO E DECREMENTANDO PONTEIROS ........................................................................................... 35 
COMPARAÇÃO DE PONTEIROS ....................................................................................................................... 36 
PONTEIROS E VETORES ................................................................................................................................ 36 
PONTEIROS E STRINGS ................................................................................................................................. 37 
ALOCAÇÃO DINÂMICA DE MEMÓRIA .............................................................................................................. 38 
PONTEIROS E MATRIZES ............................................................................................................................... 40 
VETORES DE PONTEIROS .............................................................................................................................. 41 
PONTEIROS PARA PONTEIROS ........................................................................................................................ 42 
FUNÇÕES ............................................................................................................................................. 44 
FUNÇÕES RECURSIVAS ................................................................................................................................. 45 
FUNÇÕES QUE RETORNAM PONTEIROS ............................................................................................................ 46 
ESTRUTURAS ........................................................................................................................................ 47 
REFERECIANDO ELEMENTOS DE ESTRUTURAS ................................................................................................... 48 
MATRIZES DE ESTRUTURAS ........................................................................................................................... 49 
PASSANDO ESTRUTURAS PARA FUNÇÕES .......................................................................................................... 49 
PONTEIROS PARA ESTRUTURAS ...................................................................................................................... 51 
ESTRUTURAS ANINHADAS ............................................................................................................................. 52 
ENTRADA/SAÍDA PELO CONSOLE ......................................................................................................... 53 
FUNÇÃO: PRINTF() ...................................................................................................................................... 54 
FUNÇÃO: SCANF() ....................................................................................................................................... 56 
ENTRADA/SAÍDA COM ARQUIVO ......................................................................................................... 58 
STREAMS E ARQUIVOS ................................................................................................................................. 58 
SISTEMA DE ARQUIVOS ................................................................................................................................ 59 
FUNÇÕES PARA ARQUIVOS ........................................................................................................................... 61 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 3
 
Capítulo 1 – Introdução 
 
 A linguagem C foi inventada na década de 70. Ela é considerada uma linguagem 
de médio nível por combinar elementos de linguagens de alto nível com a simplicidade 
e funcionalidade de linguagens de baixo nível. 
 Os códigos feitos pela linguagem C são bastante portáveis porque podem se 
adaptar a um software escrito de um tipo de computador a outro. 
 Em geral, o C é considerado uma linguagem estruturada, o que permite a 
compartimentalização do código e dos dados. Sua principal componente estrutural é a 
função. Funções são blocos de construção em que toda a atividade do programa ocorre, 
e elas admitem que você defina e codifique separadamente as diferentes tarefas de um 
programa,permitindo, então, que seu programa seja modular. Após uma função ter sido 
criada, você pode contar com que ela trabalhe adequadamente em várias situações, sem 
criar efeitos inesperados em outras partes do programa. O fato de você poder criar 
funções isoladas é extremamente importante em projetos maiores onde um código de 
um programador não deve afetar acidentalmente o de outro. 
 Outra maneira de estruturar e compartimentalizar o código em C é através do 
uso de blocos de código. Um bloco de código é um grupo de comandos de programa 
conectado logicamente que é tratado como uma unidade. Em C, um bloco de código é 
criado colocando-se uma seqüência de comandos entre chaves. Nesse exemplo 
 
 
 
 
 
 
 
os dois comandos após o if e entre chaves são executados se x for igual a 4. Esses dois 
comandos, junto com as chaves, representam um bloco de código, o que permite que 
muitos algoritmos sejam implementados com clareza, elegância e eficiência. 
 Todo compilador C vem com uma biblioteca (que é um arquivo contendo as 
funções padrão que seu programa pode usar) C padrão de funções que realizam as 
tarefas necessárias mais comuns. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
if (x == 4) { 
 printf(“Parabens, voce acertou, x é 4”); 
 _getch(); 
} 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 4
Capítulo 2 - Expressões 
 
Tipos Básicos de Dados 
 
 Todas as linguagens de programação de alto nível suportam o conceito de tipos 
de dados, onde um tipo de dado define um conjunto de valores que uma variável pode 
armazenar e o conjunto de operações que pode ser executado com essa variável. No C, 
existem 5 tipos básicos de dados, que são: caractere (char), inteiro (int), ponto flutuante 
(float), ponto flutuante de precisão dupla (double) e sem valor (void). 
 
O tipo char é utilizado para especificar valores definidos pelo conjunto de 
caracteres ASCII, este conjunto contém letras, símbolos e até mesmo algarismos, porém 
os algarismos não poderão ser manipulados matematicamente. 
 
O tipo int é utilizado para especificar algarismos inteiros, sendo que estes sim 
poderão ser manipulados matematicamente. 
 
Os tipos float e double são utilizados para especificar algarismos contínuos 
(fracionários), sendo que o double aborda uma faixa de números maior do que o float e 
com maior precisão. O padrão ANSI especifica que a faixa mínima de um valor em 
ponto flutuante é de 1e-37 a 1e+37. 
 
O tipo void declara explicitamente uma função que não retorna valor algum ou 
cria ponteiros genéricos, ou seja, utiliza-se void sempre em que não há necessidade de 
retornar algum valor. 
 
Tipo Tamanho Intervalo 
unsigned char 8 bits 0 até 255 
char 8 bits -128 até 127 
short int 16 bits -32,768 até 32,767 
unsigned int 32 bits 0 até 4,294,967,295 
int 32 bits -2,147,483,648 até 2,147,483,647 
unsigned long 32 bits 0 até 4,294,967,295 
enum 16 bits -2,147,483,648 até 2,147,483,647 
long 32 bits -2,147,483,648 até 2,147,483,647 
float 32 bits 3.4 x 10-38 até 3.4 x 10+38 
double 64 bits 1.7 x 10-308 até 1.7 x 10+308 
long double 80 bits 3.4 x 10-4932 até 1.1 x 10+4932 
 
Obs.: O tipo float possui 6 dígitos de precisão, enquanto que os tipos double e long 
double possuem 10 dígitos de precisão. 
 
 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 5
Nomes de Identificadores 
 
São considerados identificadores: os nomes de variáveis, funções, rótulos e 
vários outros objetos definidos pelo usuário. Esses identificadores podem variar de um a 
diversos caracteres. O primeiro caractere deve ser uma letra ou um sublinhado e os 
caracteres subseqüentes devem ser letras, números ou sublinhados. Abaixo se 
encontram alguns exemplos: 
 
Correto Incorreto 
valor 1valor 
Teste1 Ola&todos 
calculo_fatorial calculo...fatorial 
 
A linguagem C diferencia as letras maiúsculas e minúsculas, portanto: valor, 
Valor e VALOR são três identificadores distintos. 
Um identificador não pode ser igual a uma palavra-chave de C e não deve ter o 
mesmo nome que as funções que você escrever ou as que estão na biblioteca C. 
 
Variáveis 
 
 Uma variável é uma posição de memória com um nome, que é usada para 
guardar um valor que pode ser modificado pelo programa. Todas as variáveis em C 
devem ser declaradas antes de serem usadas. A forma geral de uma declaração é: 
 
tipo nome_da_variável; 
 
 Onde, tipo deve ser um ser um tipo válido em C, como aqueles vistos 
anteriormente; e nome_da_variável deve obedecer às regras vistas na seção nome 
de identificadores, podendo consistir em um ou mais nomes de identificadores 
separados por vírgulas. Abaixo estão alguns exemplos: 
 
 int i, j, l; 
 char letra; 
 double valor; 
 float peso, altura; 
 
 Todas as variáveis são constituídas de dois valores, o primeiro é a posição de 
memória, este é um valor hexadecimal. O segundo é o valor contido nele, este pode ser 
manipulado. Exemplo: 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int y = 25; /*y assume o valor 25*/ 
 
 /* imprime o valor da posição de memoria em hexadecimal */ 
 printf("A posicao de memoria (hexadecimal) e: %X", &y); 
 
 /* imprime o valor da posição de memoria em decimal */ 
 printf("\n\nA posicao de memoria (decimal) e: %d", &y); 
 
 /* imprime o valor de definido pelo programador */ 
 printf("\n\nO valor contido em x e: %d", y); 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 6
 
 
As variáveis serão declaradas em três lugares básicos: dentro de funções 
(chamadas de variáveis locais), na definição de parâmetros das funções (chamadas de 
parâmetros formais) e fora de todas as funções (chamadas de variáveis globais). 
 
• Variáveis locais são aquelas declaradas dentro de um determinado bloco, 
portanto elas não são reconhecidas fora de seu próprio bloco de código. Lembrando que 
um bloco de código começa com um abre-chave e termina com uma fecha-chave. 
Estas variáveis existem apenas dentro deste bloco onde foram declaradas, 
portanto ela é criada na entrada do seu bloco e destruída na saída. 
 
Exemplo: 
 
 
 
 
A variável x foi declarada duas vezes, uma vez em funcao1( ) e outra em 
funcao2( ). O x na primeira função não tem nenhuma relação com a segunda, isso 
ocorre porque ela é uma variável local, e funciona somente dentro do bloco em que foi 
declarada. Quando sai da primeira função esta variável é destruída, acontecendo o 
mesmo na saída da segunda. 
 
A maioria dos programadores declara todas as variáveis usadas por uma função 
imediatamente após o abre-chaves da função e antes de qualquer outro comando. 
Porém, as variáveis locais podem ser declaradas dentro de qualquer bloco de código. 
Exemplo: 
 
 
 
 
 
 
 
void funcao1() 
{ 
int x; 
x = 15; 
} 
 
void funcao2() 
{ 
int x; 
x = -33; 
} 
void funcao() 
{ 
 int x; 
 scanf(“%d”, &x); 
 
 if( x = = 1) /*não há espaço entre os sinais de igual*/ 
 { 
 int y = 5; /*esta variável só é criada na entrada 
deste bloco*/ 
 printf(“O resultado é: %d”, (x+y)); 
 } 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 7
Nesta função, a variável y é criada na entrada do bloco de código if e destruída 
na saída. Além disso, y é reconhecida apenas dentro do bloco if e não pode ser 
diferenciada em qualquer outro lugar. 
 
• Parâmetros formais: Se uma função usa argumentos, ela deve declarar 
variáveis que receberão os valores dos argumentos, estas variáveis são chamadas de 
parâmetros formais. Elas se comportam como qualquer outra variável local dentro da 
função. Exemplo: 
 
 
 
 
 
 
 
 
Nos próximos capítulosserá ensinado como realizar chamada de funções, 
passando variáveis como parâmetros. Neste exemplo, prestemos atenção na função 
soma(), ela tem como parâmetros de entrada as variáveis a e b (ambos inteiros), estas 
variáveis funcionam apenas neste bloco, sendo destruídas após a saída da função, se 
comportando assim como variáveis locais. 
Importante: Você deve ter certeza de que os parâmetros formais que estão declarados 
são do mesmo tipo dos argumentos que você utiliza para chamar a função. Se há uma 
discordância de tipos, resultados inesperados podem ocorrer. 
 
• Variáveis globais são, ao contrário das variáveis locais, reconhecidas pelo 
programa inteiro e podem ser usadas por qualquer pedaço do código. Elas guardam seus 
valores durante toda a execução do programa. Elas são criadas declarando-as fora de 
qualquer função e podem ser acessadas por qualquer expressão independente de qual 
bloco de código contém a expressão. 
 
Vide no programa seguinte, valor foi declarada fora de todas as funções, 
portanto é uma variável global. Exemplo: 
 
/* declaração da função soma, declarando também a e b como 
parâmetros formais da função*/ 
int soma(int a, int b) 
 return (a+b); 
 
void main() 
{ 
 int x = 1, y = 5, z; 
 z = soma(x,y); /* aqui ocorre uma chamada de função, passando x e 
y como argumentos*/ 
 printf(“%d”, z); /* imprime 6 na tela */ 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 8
 
 
 
 
 
 
 
 
 
 
A imagem abaixo é o resultado do programa apresentado acima: 
 
 
 
Observe este programa. As funções main( ) e funcao1( ) usaram a variável 
valor mesmo sem tê-la declarado. Já a funcao2( ) declarou uma variável local chamada 
valor, ou seja, com o mesmo nome com da variável global. Quando isso acontece, todas 
as referências ao nome da variável dentro do bloco onde a variável local foi declarada 
dizem respeito somente a ela mesma e não tem qualquer efeito sobre a variável global. 
 Notamos isso no programa acima, quando a funcao1( ) faz a chamada da 
funcao2( )¸ valor recebe a atribuição 40, que é imprimido na tela, mas como é uma 
variável local, essa é destruída assim que sai do bloco, voltando assim para a 
funcao1( ), bloco onde valor é igual a 50. 
 
 Variáveis globais são úteis quando o mesmo dado é usado em muitas funções em 
seu programa. No entanto, você deve evitar usar variáveis globais desnecessárias. Elas 
ocupam memória durante todo o tempo em que seu programa está executando, não 
apenas quando são necessárias. 
 
 
 
 
 
int valor; 
void funcao1(); 
void funcao2(); 
 
void main() 
{ 
 valor = 100; 
 printf(“%d : na funcao main”, valor); 
 funcao1(); 
 getch(); 
} 
 
void funcao1() 
{ 
 valor = 50; 
 printf(“\n%d : na funcao 1 depois de valor = 50”, valor);
 funcao2(); 
 printf(“\n%d: na funcao 1 depois de ter passado pela funcao 
2”, valor); 
} 
 
void funcao2() 
{ 
 int valor; 
 valor = 40; 
 printf(“\n%d : na funcao 2 depois de declara valor = 40 como 
variavel local”, valor); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 9
Operadores 
 
 A linguagem C possui quatro classes de operadores: aritméticos, relacionais, 
lógicos e bit a bit. Além de ter alguns operadores especiais para tarefas particulares. 
 
Operador de Atribuição 
 
 Você pode usar o operador de atribuição dentro de qualquer expressão válida de 
C. A forma geral do operador de atribuição é: 
 
Nome_da_variável = expressão; 
 
 Onde expressão pode ser uma simples constante ou uma expressão tão complexa 
quanto você necessite. 
 Você verá dois termos: lvalue e rvalue, onde lvalue se refere ao termo do lado 
esquerdo do igual, e rvalue do lado direito. O lvalue é o destino da atribuição, ele 
sempre deve ser uma variável ou um ponteiro, enquanto que o rvalue deve ser uma 
constante ou função. Exemplos: 
 
X = 40; // atribuição de uma constante inteira. 
Y = 3.1234; // atribuição de uma constante tipo float. 
Z = x + 2y; // atribuição de uma função. 
 
 
Conversão de Tipos em Atribuições 
 
 Conversão de tipos refere-se à situação em que variáveis de um tipo são 
misturadas com variáveis de outro tipo. Em um comando de atribuição, o valor do lado 
direito (rvalue) de uma atribuição é convertido no tipo do lado esquerdo (lvalue), como 
segue no exemplo abaixo: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int x = 2; 
 char ch; 
 float f = 30.6954; 
 
 ch = x; /* linha 1 */ 
 printf("ch = %c", ch); 
 
 x = f; /* linha 2 */ 
 printf("\nx = %d", x); 
 
 f = ch; /* linha 3 */ 
 printf("\nf = %f", f); 
 
 f = x; /* linha 4 */ 
 printf("\nf = %f", f); 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 10
Se executarmos o programa, teremos os seguintes resultados: 
 
 
 
 Na linha 1, x está entre 0 e 156, portando ch e x possuem valores idênticos, que 
nesse caso é 2. De outra forma, o valor de ch reflete apenas os bits menos significativos 
de x. Na linha 2, x recebe a parte inteira de f. Na linha 3, f converte o valor inteiro de 8 
bits armazenado em ch no mesmo valor em formato de ponto flutuante. Isso também 
ocorre na linha 4, exceto por f converter um valor inteiro de 16 bits no formato de ponto 
flutuante. 
 
Operadores Aritméticos 
 
 A tabela abaixo lista os operadores aritméticos de C, e suas ações. 
 
Operadores Ações 
- Subtração, também menos unário 
+ Adição 
* Multiplicação 
/ Divisão 
% Módulo da divisão (resto) 
-- (dois -) Decremento 
++ (dois +) Incremento 
 
 O menos unário multiplica seu único operando por -1. Isto é, qualquer número 
precedido por um sinal de menos troca de sinal. 
 
 
Incremento e Decremento 
 
 C inclui dois operadores úteis geralmente não encontrados em outras linguagens. 
São os operadores de incremento e decremento, ++ e --. O operador ++ soma 1 ao seu 
operando, e -- subtrai 1. Em outras palavras: 
 
 
 
 
 
Ambos os operadores de incremento e decremento podem ser utilizados como prefixo 
ou sufixo do operando. 
 
Prefixo Sufixo 
++x; x++; 
 
Expressão É o mesmo que Ou ainda 
x = x + 1; ++x; x++; 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 11
 Porém, há uma diferença quando esses operadores são usados em uma 
expressão. Quando um operador de incremento ou decremento precede seu operando, C 
executa a operação de incremento ou decremento antes de usar o valor do operando. Se 
o operador estiver após seu operando, C usa o valor do operando antes de incrementá-lo 
ou decrementá-lo. Considere o seguinte: 
 
X = 10; 
Y = ++x; 
 
Coloca 11 em y. Porém se o código fosse escrito como 
 
X = 10; 
Y = x++; 
 
y receberia 10. Em ambos os casos, x recebe 11, a diferença está em quando isso 
acontece. 
 
Operadores Relacionais e Lógicos 
 
 No termo operador relacional, relacional refere-se às relações que os valores 
podem ter uns com os outros. No termo operador lógico, lógico refere-se às maneiras 
que essas relações podem ser conectadas. Estes freqüentemente trabalham juntos. 
 
Operadores Relacionais 
Operador Ação 
> Maior que 
>= Maior ou igual que 
< Menor que 
<= Menor ou igual que 
= = Igual 
!= Diferente 
 
Operadores Lógicos 
Operador Ação 
&& AND 
|| OR 
! NOT 
 
 A idéia de verdadeiro e falso está por trás dos conceitos dos operadores lógicos 
e relacionais. Em C, falso é zero, enquanto que verdadeiro é qualquer valor diferente 
de zero. Abaixo temos a tabela verdade dos operadores lógicos, usando 1s e 0s. 
 
x y x&&y x||y !x 
0 0 0 0 1 
0 1 0 1 1 
1 0 1 1 0 
1 1 0 1 0 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 12
Ambos os operadores são menores em precedência do que os operadores 
aritméticos. Isto é, uma expressão como 12 > 1 + 12 é avaliada como se fosse escrita 
12 > (1 + 12). O resultado é, obviamente, falso. 
 É permitido combinar diversasoperações em uma expressão como mostrado 
aqui: 
10 > 5 && !(10 < 9) || 3 <= 4 
 Neste caso, o resultado é verdadeiro. 
 
Casts 
 
É possível forçar uma expressão a ser de um tipo usando uma construção 
chamada cast. A forma geral de um cast é 
 
 (tipo) expressão 
 
onde tipo é um tipo de dado padrão de C. Por exemplo, para ter certeza de que a 
expressão x/2 será do tipo float, escreva 
 
 (float) x / 2; 
 
Exemplo: 
 
 
 
Abreviações em C 
 
 C oferece uma abreviação especial que simplifica a codificação de certos tipos 
de comandos de atribuição. A forma geral de uma abreviação C é 
 
variavel = variavel operador expressão; 
 
é o mesmo que 
 
variavel operador = expressão; 
 
 Exemplos: 
 
Expressão É o mesmo que 
x = x + 8; x += 8; 
y = y / 6; y /= 6; 
z = z * y; z *= y; 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int x = 10; 
 printf("\nX/3 e igual a: %f", (float) x/3); 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 13
Capítulo 3 – Comandos de Controle 
 
 No C existe vários comando de controle do programa. O padrão ANSI divide os 
comandos nestes grupos: 
 
• Seleção 
• Iteração 
• Desvio 
• Rótulo 
• Expressão 
• Bloco 
 
Comandos de Seleção 
 
if ... else 
 
 A forma geral da sentença if é 
 
 if ( expressão) comando; 
 else comando; 
 
onde comando pode ser um único comando, um bloco de comandos ou nada. Enquanto 
que else é opcional, seu uso não é obrigatório. 
 
 Se a expressão é verdadeira (algo diferente de 0), o comando ou bloco que forma 
o corpo do if é executado; caso contrário, o comando ou bloco que é o corpo do else (se 
existir) é executado. Lembre-se de que apenas o código associado ao if ou código 
associado ao else será executado, nunca ambos. 
 Abaixo vemos o fluxograma correspondente a esta estrutura de decisão. 
 
 
 
 
 
 
 
 
 
 
 
 Condição? 
 Bloco1 Bloco2 
V F 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 14
O comando condicional controlando o if deve produzir um resultado escalar. Um 
escalar é um inteiro (int), um caractere (char) ou tipo de ponto flutuante (float). No 
entanto, é raro usar um número de ponto flutuante para controlar um comando 
condicional, porque isso diminui consideravelmente a velocidade de execução. (A CPU 
executa diversas instruções para efetuar uma operação em ponto flutuante. Ela usa 
relativamente poucas instruções para efetuar uma operação com caractere ou inteiro). 
 
 Veja um exemplo do uso do if: 
 
 
 
 
 
 
 
ifs Aninhados 
 
 Um if aninhado é um comando if que é o objeto de outro if ou else. ifs 
aninhados são muito comuns em programação. Em C, um comando else sempre se 
refere ao comando if mais próximo, que está dentro do mesmo bloco do else e não está 
associado a outro if. Por exemplo 
 
 
 
 
 Como observado, o último else não está associado a if(j) porque não pertence ao 
mesmo bloco. Em vez disso, último else está associado ao if(i). O else interno está 
associado ao if(k), que é o if mais próximo. 
 O padrão ANSI especifica que pelo menos 15 níveis de aninhamento devem ser 
suportados. Na prática, a maioria dos compiladores permite substancialmente mais. 
 
 
 
 
if(i){ 
 if(j) comando 1; 
 if(k) comando 2; //este if... 
 else comando 3; //..está associado a este else 
} 
else comando 4; // associado a if(i) 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int valor; 
 printf("Entre com um valor inteiro: "); 
 scanf("%d", &valor); 
 
 if(valor > 0) 
 printf("\nO valor que voce digitou e positivo"); 
 else 
 printf("\nO valor que voce digitou e negativo"); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 15
if – else – if 
 
 Uma construção comum em programação é a forma if–else–if, algumas vezes 
chamada de escada if-else-if devido a sua aparência. A sua forma geral é 
 
if (expressão1) comando 1; 
else if (expressão2) comando 2; 
else if (expressão3) comando 3; 
. 
. 
. 
else comando N; 
 
 As condições são avaliadas de cima para baixo. Assim que uma condição 
verdadeira é encontrada, o comando associado a ela é executado e o resto da escada é 
contornado. Se nenhuma das condições for verdadeira, então o último else é executado. 
Isto é, se todos os outros testes condicionais falham, o último comando else é efetuado. 
Se o último else não está presente, nenhuma ação ocorre se todas as condições são 
falsas. 
 
? 
 
 C contém um operador muito poderoso e conveniente que substitui certas 
sentenças da forma if-else. O ? é um operador ternário (que requer três operandos) que 
tem a forma geral 
 
Exp1 ? Exp2 : Exp3; 
 
 Onde Exp1, Exp2 e Exp3 são expressões. Note o uso e o posicionamento dos 
dois pontos. 
 O operador ? funciona desta forma: Exp1 é avaliada. Se ela for verdadeira, 
então Exp2 é avaliada e se torna o valor da expressão. Se Exp1 é falso, então Exp3 é 
avaliada e se torna o valor da expressão. Por exemplo, 
 
X = 10; 
Y = X > 9 ? 100 : 200; 
 
a Y é atribuído um valor 100. Se X fosse menor que 9, Y teria recebido o valor 200. O 
mesmo código, usando o comando if-else é 
 
X = 10; 
if ( X > 9) Y = 100; 
else Y = 200; 
 
 Exemplo: 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 16
 
 
 
 
 
 
 
 Este programa é análogo ao da seção if-else, incorporando agora os conceitos do 
operador ?. Observe os dois e faça as comparações. 
 
Switch 
 
 C tem um comando interno de seleção múltipla, switch, que testa 
sucessivamente o valor de uma expressão contra uma lista de constantes inteiras ou de 
caractere. Quando o valor coincide, os comandos associados àquela constante são 
executados. A forma geral do comando switch é 
 
switch (expressão) { 
 case constante1: 
 comandos 
 break; 
 case constante2: 
 comandos 
 break; 
 . 
 . 
 . 
 default: 
 seqüência de comandos 
} 
 
 Abaixo vemos o fluxograma correspondente a esta estrutura de decisão. 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int valor; 
 printf("Entre com um valor inteiro: "); 
 scanf("%d", &valor); 
 
 valor > 0 ? 
 printf("\nO valor que voce digitou e positivo") 
 : printf("\nO valor que voce digitou e negativo"); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 17
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 O valor da expressão é testado, na ordem, contra os valores das constantes 
especificadas nos comandos case. Quando uma coincidência for encontrada, a seqüência 
de comandos associada àquele case será executada até que o comando break ou o fim 
do comando swtich seja alcançado. O comando default é executado se nenhuma 
coincidência for detectada. O default é opcional e, se não estiver presente, nenhuma 
ação será realizada se todos os testes falharem. 
 
 O comando switch é freqüentemente utilizado na construção de menus, veja o 
exemplo abaixo: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Expressão 
 Conjunto 1 
 Conjunto 2 
 ... 
 Conjunto N 
 Conjunto D 
Rótulo 1 
Rótulo 2 
Rótulo N 
Rótulo D 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 18
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Comandos de Iteração 
 
 Os comandos de iteração, conhecidos também como laços, permitem que 
um conjunto de instruções seja executado até que ocorra uma certa condição. Essa 
condição pode ser predefinida (como ocorre no laço for) ou com o final em aberto 
(como ocorre nos laços while e do-while). 
 
Laço for 
 
 O laço for é uma estrutura de repetição, chamada de estrutura de repetição com 
contador. Ele executa um bloco de instruções em uma quantidade de vezes predefinida. 
A forma geral do comando for é 
 
 for(inicialização; condição; incremento) { 
 bloco 
 } 
 
onde inicialização é uma expressão de inicializaçãodo contador, geralmente é um 
comando de atribuição que é usado para colocar um valor inicial na variável de controle 
do laço. A condição é uma expressão relacional que determina quando o laço termina. O 
incremento define como a variável de controle do laço varia cada vez que o laço é 
repetido. Estas três seções devem ser separadas por ponto-e-vírgula (;). O bloco é 
executado enquanto a condição for verdadeira, ou seja, uma vez que a condição se torne 
falsa, o laço é encerrado. 
 
#include <conio.h> 
#include <stdio.h> 
#include <stdlib.h> 
 
void funcao1(); // função para conversão de temperatura 
void funcao2(); // função para calculo de fatorial 
 
void main() 
{ 
 int opcao; 
 printf("1: Conversao de Temperatura"); 
 printf("\n2: Calcular Fatorial"); 
 printf("\nEscolha uma opcao e tecle ENTER: "); 
 scanf("%d",&opcao); // entrada de variavel 
 
 switch(opcao) // analisa a opcao do usuario 
 { 
 case 1: // caso seja 1... 
 funcao1(); // ... executa função 1 
 break; 
 
 case 2: // caso seja 2... 
 funcao2(); // ... executa função 2 
 break; 
 
 default: // caso não seja nenhuma... 
 exit(0); // ...o programa é encerrado 
 break; 
 } 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 19
 Por exemplo, o programa abaixo imprime, na tela, os números de 1 a 100: 
 
 
 
 
 
 
 
 
 
 
Podem existir mais de uma expressão de inicialização e de incremento na 
estrutura for. Estas expressões devem ser separadas por vírgulas (,). Mas não pode 
haver mais de uma expressão de condição. Por exemplo: 
 
for(i = 0, j = 10; i < 10; i++, j--) { ... } 
 
Laço while 
 
 Outra estrutura de repetição disponível no C é o laço while. Sua forma geral é: 
 
 While(condição){ 
 bloco 
 } 
 
 A condição pode ser qualquer expressão. Este laço se repete quando a condição 
for verdadeira, ou seja, qualquer valor diferente de zero. Quando a condição for falsa, o 
programa pula este laço e o bloco não é executado. 
 
 O fluxograma desta estrutura é mostrado abaixo: 
 
 
 
 
 
 
 
 
Exemplo, o programa abaixo imprime, na tela, os números de 1 a 100: 
 
 
 Condição? 
 bloco 
V 
F 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 for(int i = 1; i<=100; i++) 
 printf("%d ",i); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 20
 
 
 
 
 
 
 
 
 
 
 
Laço do-while 
 
 Ao contrário dos laços for e while, que testam a condição do laço no começo, o 
laço do-while sempre será executado ao menos uma vez. Sua forma geral é: 
 
 do{ 
 bloco 
 }while(condição); 
 
 A condição é qualquer expressão relacional e/ou lógica. O bloco será executado 
uma vez, se a condição for falsa, ou seja, igual a zero, o programa encerra o laço. 
Porém, se a condição for verdadeira, ou seja, qualquer número diferente de zero, o bloco 
é executado novamente. 
 
 O fluxograma desta estrutura é mostrado abaixo: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 bloco 
 Condição? 
V 
F 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int i = 1; 
 while(i<=100) 
 { 
 printf("%d ", i); 
 i++; 
 } 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 21
Exemplo, o programa abaixo imprime, na tela, os números de 1 a 100: 
 
 
 
 
 
 
 
 
 
 
 
 
 
O comando do-while é uma boa escolha na construção de menus, porque sempre 
se deseja que as opções do menu execute ao menos uma vez. Depois que as opções 
forem mostradas, o programa será executado até que uma opção válida seja selecionada. 
 
 Abaixo se encontra o exemplo utilizado na seção do switch, agora utilizando os 
conceitos de do-while: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
#include <stdlib.h> 
 
void funcao1(); //função para conversão de temperatura 
void funcao2(); //função para calculo de fatorial 
 
void main() 
{ 
 int opcao; 
 do{ 
 
 printf("1: Conversao de Temperatura"); 
 printf("\n2: Calcular Fatorial"); 
 printf("\n3: sair"); 
 printf("\nEscolha uma opcao e tecle ENTER: "); 
 scanf("%d",&opcao); // entrada de variavel 
 
 }while(opcao!=1 && opcao!=2); 
 /*este bloco será executado sempre que opcao for 
diferente de 1 e de 2*/ 
 
 switch(opcao) // analisa a opcao do usuario 
 { 
 case 1: // caso seja 1... 
 funcao1(); // ... executa função 1 
 break; 
 
 case 2: // caso seja 2... 
 funcao2(); // ... executa função 2 
 break; 
 
 case 3: // caso seja 3... 
 exit(0); // ...o programa é encerrado 
 break; 
 } 
} 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int i = 1; 
 do{ 
 printf("%d ", i); 
 i++; 
 }while(i<=100); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 22
Comandos de Desvios 
 
 A linguagem C possui quatro comandos que realizam um desvio incondicional: 
return, goto, break e continue. 
 
Comando return 
 
 O comando return é utilizado para retornar de uma função. Ele é um comando 
de desvio, pois faz com que a execução retorne ao ponto em que a função foi chamada. 
A sua forma geral é 
 
 return expressão; 
 
 A expressão é opcional, se houver alguma expressão contendo algum valor 
associado ao return, este é retornado da função para onde ela foi chamada. Se nenhum 
valor de retorno for especificado, assume-se que apenas lixo é retornado. 
 Você pode usar quantos comandos return quiser dentro de uma função. 
Entretanto, a função deixará de executar tão logo ela encontre o primeiro return. 
 Uma função do tipo void não pode ter um comando return. 
 
 
Comando goto 
 
 No C, devido ao seu rico conjunto de estruturas de controle, há pouca 
necessidade da utilização do goto. A grande preocupação da maioria dos programadores 
sobre o goto é sua tendência de tornar os programas ilegíveis, mas se este for utilizado 
prudentemente, pode ser uma vantagem em certas situações na programação. 
 O comando goto requer um rótulo para sua operação, o qual rótulo é um 
identificador válido em C seguido de dois pontos. O rótulo deve estar na mesma função 
do goto que o utiliza, senão você não poderá efetuar o desvio. A forma geral do goto é 
 
 goto rótulo; 
 ... 
 rótulo: 
 ... 
 
 Por exemplo, o programa abaixo imprime, na tela, os números de 1 a 100: 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int i = 1; 
 
 repetir: // rótulo 
 printf("%d ",i); 
 i++; // incremento 
 if(i <= 100) 
 goto repetir; // chamada do rótulo 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 23
Comando break 
 
 O comando break pode ser usado de duas formas. Ele pode ser usado em 
conjunto com switch...case como visto anteriormente, ou pode também ser usado em 
conjunto com um laço de repetição (for, do-while, while) que força a interrupção deste 
laço independentemente da condição de controle. 
 
 Por exemplo, o programa abaixo imprime, na tela, os números de 1 a 10: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Porém, o comando break força a saída apenas do laço mais interno de onde ele 
se encontra. Por exemplo, o programa abaixo imprime, na tela, os números de 1 a 10, 10 
vezes. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 for(int i = 1; i <= 100; i++) 
 { 
 printf("%d ",i); 
 if(i == 10) 
 break; // sai do laço quando i = 10 
 } 
 _getch(); 
} 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 for(int i = 1; i <= 10; i++) 
 { 
 for(int j = 1; j <= 100; j++) 
 { 
 printf("%d ",j); 
 if(j == 10) 
 break; // sai do laço mais interno quando i=10 
 } 
 printf("\n"); 
 } 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 24
Função exit() 
 
 A função exit() provoca uma terminaçãoimediata do programa inteiro, forçando 
um retorno ao sistema operacional, ela age como se estivesse finalizando o programa. A 
forma geral é 
 
 void exit(int código_de_saída); 
 
 O valor código_de_saída é um inteiro que será passado para o Sistema 
Operacional. O zero é geralmente usado como um código de retorno que indica uma 
terminação normal do programa, enquanto que outros argumentos são usados para 
indicar algum tipo de erro. 
 
 O exemplo abaixo é idêntico ao utilizado na seção do-while: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
#include <stdlib.h> 
 
void funcao1(); //função para conversão de temperatura 
void funcao2(); //função para calculo de fatorial 
 
void main() 
{ 
 int opcao; 
 do{ 
 
 printf("1: Conversao de Temperatura"); 
 printf("\n2: Calcular Fatorial"); 
 printf("\n3: sair"); 
 printf("\nEscolha uma opcao e tecle ENTER: "); 
 scanf("%d",&opcao); // entrada de variavel 
 
 }while(opcao!=1 && opcao!=2); 
 /*este bloco será executado sempre que opcao for 
diferente de 1 e de 2*/ 
 
 switch(opcao) // analisa a opcao do usuario 
 { 
 case 1: // caso seja 1... 
 funcao1(); // ... executa função 1 
 break; 
 
 case 2: // caso seja 2... 
 funcao2(); // ... executa função 2 
 break; 
 
 case 3: // caso seja 3... 
 exit(0); // ...o programa é encerrado 
 break; 
 } 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 25
Neste exemplo, se o usuário escolher sair do programa, caso 3, o comando 
exit(0) encerrará o programa. Note que o argumento utilizado é o inteiro zero, que 
indica saída normal do programa. As funções funcao1() e funcao2() devem ser 
definidas pelo programador. 
 
Comando continue 
 
 O comando continue funciona de uma forma um tanto quanto similar ao 
comando break. Só que, ao invés de forçar a terminação do laço, continue força que 
ocorra a próxima iteração deste, pulando qualquer código intermediário. Para o laço for, 
este comando faz com que o teste condicional e a porção de incremento do laço sejam 
executados. Para os laços while e do-while, o controle de programa passa para o teste 
condicional. Veja o exemplo a seguir, o programa imprime, na tela, os números ímpares 
de 1 a 100: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int resultado; 
 for(int i = 1; i <= 100; i++) 
 { 
 resultado = i%2; // resto da divisão i/2 
 if(resultado == 0) // se o resto for 0 
 continue; // continue força a proxima iteração do laço 
 
 printf("%d ",i); 
 } 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 26
Capítulo 4 – Matrizes e Strings 
 
 Uma matriz é um conjunto de variáveis de um mesmo tipo. Em C, todas as 
matrizes consistem em posições contíguas na memória. O endereço mais baixo 
corresponde ao primeiro elemento e o mais alto, ao último elemento. Matrizes podem 
ter de uma a várias dimensões. A matriz mais comum em C é a de string, que é 
simplesmente uma matriz de caracteres terminada por um nulo. 
 
Matrizes Unidimensionais 
 
 A forma geral para se declarar uma matriz unidimensional é 
 
 tipo nome[tamanho]; 
 
onde tamanho deve ser um inteiro que define quantos elementos a matriz irá armazenar 
e tipo é o tipo de cada elemento da matriz. Matrizes devem ser explicitamente 
declaradas, juntamente com seu tamanho, para que o compilador possa alocar espaço 
para elas na memória. Por exemplo, se você quiser declarar um vetor (matriz 
unidimensional) do tipo float, chamada notas, e com 50 elementos, este vetor deve ser 
declarado da seguinte forma: 
 
 float notas[50]; 
 
 Para vetores, cada elemento possui um índice, sendo que o primeiro elemento 
possui índice zero e o último possui em seu índice o tamanho do menos um 
(tamanho-1). Por exemplo, considere o vetor 
 
 int x[10]; 
 
aqui você está declarando um vetor com dez elementos, x[0] até x[9]. 
 
 
No seguinte código, o programa faz o carregamento de um vetor com os 
números de 0 a 99: 
 
 
 
 
 
 
 
 
 
 
Para um vetor, o tamanho total em bytes é calculado da seguinte forma: 
 
 Total em bytes = sizeof(tipo) * tamanho do vetor 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int x[100]; 
 for(int i = 0; i < 100; i++) 
 x[i] = i; // x[0]=0... x[1]=1... x[2]=2... etc. 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 27
A linguagem C não possui verificação de limites em matrizes. Você poderia 
ultrapassar o fim de uma matriz e escrever nos dados de alguma outra variável. Como 
programador, você deve prover verificação dos limites onde for necessário. 
 
A tabela abaixo exemplifica como um vetor apareceria na memória começando 
na posição 100 e fosse declarado na seguinte forma: 
 
double y[7]; 
 
Elemento y[0] y[1] y[2] y[3] y[4] y[5] y[6] 
Endereço 100 101 102 103 104 105 106 
 
 
Passando vetores para funções 
 
Considere o seguinte fragmento de programa que passa o endereço de x para 
função(): 
 
void main() 
{ 
 int x[15]; 
 funcao(x); 
 . 
 . 
 . 
} 
 
Se uma função recebe um vetor, você pode declarar o parâmetro formal em uma 
entre três formas: como um ponteiro, como uma matriz dimensionada ou como uma 
matriz adimensional. Exemplos de como funcao pode receber o vetor x: 
 
funcao(int *x) /* ponteiro */ 
{ 
 . 
 . 
 . 
} 
Ou 
 
funcao(int x[10]) /* matriz dimensionada */ 
{ 
 . 
 . 
 . 
} 
 
Ou ainda 
 
funcao(int x[]) /* matriz adimensional */ 
{ 
 . 
 . 
 . 
} 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 28
Todos os três métodos possuem resultados idênticos, pois cada um diz ao 
compilador q um ponteiro inteiro vai ser recebido. A primeira declaração usa, de fato, 
um ponteiro. A segunda emprega a declaração de matriz padrão. A última declaração 
simplesmente especifica que uma matriz do tipo int, de algum tamanho, será recebida. 
 
Strings 
 
 Uma string é definida como um vetor de caracteres que é terminada por um nulo. 
Um nulo é especificado como ‘\0’ e geralmente é zero. Por essa razão, você precisa 
declarar matrizes de caracteres como sendo um caractere mais longo que a maior string 
que elas devem guardar. Por exemplo, para declarar um vetor string que guarda uma 
string de 10 caracteres, deve-se declarar desta forma 
 
 char string[11]; 
 
 Com isso reserva espaço para o nulo no final da string. 
 Embora a linguagem C não tenha o tipo de dado string, ela permite constantes 
string. Uma constante string é uma lista de caracteres entre aspas. Por exemplo, 
 
 “ola mundo” 
 
 Você não precisa adicionar o nulo no final das constantes string manualmente, o 
compilador C faz isso automaticamente. 
 
 C apresenta uma gama de funções de manipulação de strings. Abaixo lista as 
mais comuns com seus respectivos efeitos, considere s1 e s2 duas strings, e ch um 
caractere. 
 
Função Efeito 
strcpy(s1,s2) Copia s2 em s1 
strcat(s1,s2) Concatena s2 ao final de s1 
strlen(s1,s2) Retorna o tamanho de s1 
strcmp(s1,s2) Retorna 0 se s1 e s2 são iguais; menor que 0 se s1 < s2; maior que 
0 se s1 > s2 
strchr(s1,ch) Retorna um ponteiro para a primeira ocorrência de ch em s1 
strstr(s1,s2) Retorna um ponteiro para a primeira ocorrência de s2 em s1 
 
 Essas funções utilizam o cabeçalho STRING.H. 
 
O programa a seguir ilustra o uso dessas funções: 
 
 
 
 
 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 29
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Deve-se lembrar que strcmp() retorna zero em caso das strings serem iguais, e 
zero é falso por isso deve-se usar o operador ! para reverter a condição, assim se tornará 
verdadeiro e o bloco dentro do if será executado. 
 Se executarmos o programa teremos o seguinte resultado:#include <conio.h> 
#include <stdio.h> 
#include <string.h> 
 
void main() 
{ 
 char string1[80]; // declaração da string1 
 char string2[80]; // declaração da string2 
 
 printf("Entre com a primeira string: "); 
 gets(string1); // recebe a string1 
 
 printf("Entre com a segunda string: "); 
 gets(string2); // recebe a string2 
 
 printf("\nO tamanho da primeira string e: %d", strlen(string1)); 
// imprime o tamanho da string1 
 
 printf("\nO tamanho da segunda string e: %d", strlen(string2)); 
// imprime o tamanho da string2 
 
 if(!strcmp(string1,string2)) // faz a comparação 
 printf("\nAs strings sao idênticas"); 
 else 
 printf("\nAs strings sao diferentes"); 
 
 printf("\n%s",strcat(string1,string2)); // concatenação 
 printf("\n%s",string1); // imprime a string1 concatenada 
 
 strcpy(string1,"teste"); // copia “teste” para string1 
 printf("\n%s",string1); 
 
 if(strchr(string2,'a')) // procura pela letra a na string2 
 printf("\nA segunda string contem a letra a"); 
 
 if(strstr("ola mundo","ola")) 
 printf("\nA expressao \"ola mundo\" contem a palavra \"ola\""); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 30
Matrizes Bidimensionais 
 
 A declaração de uma matriz bidimensional é semelhante ao de unidimensional. 
Por exemplo, se você quiser declarar uma matriz bidimensional chamada x, do tipo 
inteiro e de tamanho 5,10, você escreveria 
 
 int x[5][10]; 
 
 Similarmente, para acessar o elemento 0,1 você usaria 
 
 X[0][1]; 
 
 O exemplo abaixo carrega uma matriz bidimensional com os números de 1 a 9 e 
o imprime em forma matricial: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Construindo uma tabela dessa matriz com seus valores, temos a seguinte disposição: 
 
 Coluna 
Linha 0 1 2 
0 1 2 3 
1 4 5 6 
2 7 8 9 
 
Neste exemplo, o elemento matriz[0][0] possui o valor 1, o elemento 
matriz[0][1] possui o valor 2, e assim por diante. O valor do último elemento 
matriz[2][2] será 9. 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int matriz[3][3]; 
 
 for(int i=0; i<3; i++) 
 for(int j=0; j<3; j++) 
 matriz[i][j] = i*3+j+1; 
 
 for(int i=0; i<3; i++) 
 { 
 for(int j=0; j<3; j++) 
 printf("%d",matriz[i][j]); 
 printf("\n"); 
 } 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 31
Matrizes bidimensionais são armazenadas em uma matriz linha-coluna, onde o 
primeiro índice indica a linha e o segundo, a coluna. 
No caso da matriz bidimensional, a seguinte fórmula fornece o número de bytes 
de memória necessários para armazená-la: 
 
Total em bytes = tamanho do 1º índice * tamanho do 2º índice * sizeof(tipo) 
 
Portando, a matriz exemplificada acima, que possui as dimensões 3, 3 teria 
 
 3 * 3 * 2 
 
ou 18 bytes alocados, já que cada inteiro ocupa 2 bytes. 
 
 Quando passamos uma matriz bidimensional é passada como um argumento 
para uma função, apenas um ponteiro para o primeiro elemento é realmente passado. No 
entanto, uma função que recebe uma matriz bidimensional como um parâmetro formal 
deve definir ao menos o comprimento da segunda dimensão. Você pode especificar a 
primeira dimensão, se quiser, mas não é necessário. 
 
 Por exemplo, uma função que recebe uma matriz bidimensional de inteiros com 
dimensões 10, 10 é declarada dessa forma: 
 
 funcao(int matriz[][10]) 
 { 
 . 
 . 
 . 
 } 
 
Matrizes de Strings 
 
 É muito comum no C a utilização das matrizes de strings. Para criar uma, use 
uma matriz bidimensional de caracteres. O tamanho do índice esquerdo indica a 
quantidade de strings que deseja e o tamanho do índice do lado direito indica o 
comprimento máximo de cada string. Por exemplo, se você quiser criar uma matriz de 
strings com a capacidade para 50 strings, sendo que, cada uma suporte no máximo 80 
caracteres, você escreverá 
 
 char nome[50][80]; 
 
 Para acessar uma string individualmente, você simplesmente especifica apenas o 
índice esquerdo. Por exemplo, se você possuir uma lista com os nomes de cinqüenta 
alunos, e quiser imprimir o nome do 4º aluno, você deverá escrever 
 
 printf(“%s”, nome[3]); 
 
 Com isso, toda string da 4ª linha será impressa na tela. 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 32
Inicialização de Matrizes 
 
 C permite a inicialização de matrizes no momento da declaração. A forma geral 
de uma inicialização de matriz é semelhante à de outras variáveis, como mostrado aqui: 
 
especificador_de_tipo nome_da_matriz[tamanho1 ...[tamanhoN] = {lista_de_valores}; 
 
 A lista_de_valores é uma lista separada por vírgulas de constantes cujo tipo é 
compatível com especificador_de_tipo. A primeira constante é colocada na primeira 
posição da matriz, a segunda, na segunda posição e assim por diante. 
 
 No exemplo abaixo uma matriz inteira de dez elementos é inicializada com os 
números de 1 a 10: 
 
 int i[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; 
 
 Isso significa que i[0] terá valor 1 e i[9] terá valor 10; 
 
 Matrizes de caracteres que contêm strings permitem uma inicialização abreviada 
que toma a forma: 
 
 char nome_da_matriz[tamanho] = “string”; 
 
 No código abaixo, a matriz de string chamada string é inicializada com a frase 
“ola mundo”. 
 
 char string[] = “ola mundo” 
 
Note que não foi definido o tamanho da matriz, quando se realiza uma 
inicialização de matriz isso pode ser feito, pois o compilador conta os números de 
elementos e aloca o espaço automaticamente. 
Este código é o mesmo que 
 
char string[10] = “ola mundo” 
 
 Não esqueça de contar um espaço extra para o nulo (‘\0’). 
 
 Inicialização de matrizes multidimensionais é equivalente ao de matrizes 
unidimensionais. Por exemplo, a matriz abaixo é inicializada com os números de 1 a 9: 
 
 int i[3][3] = {1,2,3,4,5,6,7,8,9}; 
 
Que também pode ser escrita desta forma: 
 
int i[][3] = {1,2,3,4,5,6,7,8,9}; 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 33
Capítulo 5 – Ponteiros 
 
 Um ponteiro é uma variável que contém um endereço de memória. Esse 
endereço é normalmente a posição de outra variável na memória. Se uma variável 
contém o endereço de outra, então a primeira variável é dita para apontar para a 
segunda. 
 
Variáveis Ponteiros 
 
 Se uma variável irá conter um ponteiro, ela deve ser declarada da seguinte 
forma: 
 
 tipo *nome; 
 
onde tipo é qualquer tipo válido em C e nome é o nome da variável ponteiro. 
 
Operadores de Ponteiros 
 
 Existem dois operadores para ponteiros: * e &. O & é um operador unário 
(operador unário é aquele quer requer apenas um operando) que devolve o endereço na 
memória de seu operando. Por exemplo, 
 
 m = &var; 
 
coloca o endereço da memória da variável var em m, daí dizemos que m está apontando 
para var. O endereço não tem relação alguma com o valor de var. O operador & pode 
ser imaginado como retornando “o endereço de”. O comando de atribuição anterior 
significa “m recebe o endereço de var”. 
 
 O segundo operador de ponteiro, *, é o complemento de &. É um operador 
unário que devolve o valor da variável localizada no endereço que o segue. Neste caso, 
se m contém o endereço da variável var, 
 
 q = *m; 
 
coloca o valor de var em q. 
 
Vamos supor que var usa o endereço 100 na posição de memória e que esta 
variável tenha o valor 5. Portanto com a primeira atribuição m terá o valor 100, e com a 
segunda atribuição q terá o valor 5, porque 5 estava armazenado na posição 100, que é o 
endereço que estava armazenado em m. O operador * pode ser imaginado como “no 
endereço”. Nesse caso, o comando anterior significa “q recebe o valor que está no 
endereço m”. 
 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 34
Atribuição de Ponteiros 
 
 Do mesmo modo que umavariável comum o conteúdo de um ponteiro 
pode ser passado para outro ponteiro do mesmo tipo. 
As variáveis ponteiro devem sempre apontar para os tipos de dados corretos. 
Uma variável ponteiro declarada como apontador de dados inteiros deve sempre apontar 
para dados deste tipo. 
 
Observar que em C é possível atribuir qualquer endereço a uma variável 
ponteiro. Deste modo é possível atribuir o endereço de uma variável do tipo float a um 
ponteiro inteiro. No entanto, o programa não irá funcionar da maneira correta. 
 
Veja o exemplo abaixo, o endereço do terceiro elemento do vetor v é carregado 
em p1 e o endereço da variável i é carregado em p2. Além disso, no final o endereço 
apontado por p1 é carregado em p2. Os comandos printf() imprimem os valores e os 
endereços apontados pelos ponteiros respectivos. %p imprime o valor em hexadecimal 
assim como é usado pelo computador. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Com a execução deste programa, temos o seguinte resultado na tela: 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main(void) 
 
{ 
 
 int vetor[] = { 10, 20, 30, 40, 50 }; 
 int *p1, *p2; 
 int i = 100; 
 
 p1 = &vetor[2]; 
 printf("Endereco de p1 e %p e seu valor e %d\n", p1, *p1); 
 
 p2 = &i; 
 printf("\nEndereco de p2 e %p e seu valor e %d\n", p2, *p2); 
 
 p2 = p1; 
 printf("\nEndereco de p2 e %p e seu valor e %d\n", p2, *p2); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 35
Incrementando e Decrementando Ponteiros 
 
 O exemplo abaixo mostra que operações de incremento e decremento podem ser 
aplicadas em operandos. O primeiro printf imprime 30 o segundo 40 e o terceiro 50. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Pode parecer estranho que um endereço que aponte para um número inteiro que 
é armazenado em dois bytes seja incrementado por um e passe para apontar para o 
próximo número inteiro. A resposta para isto é que sempre que um ponteiro é 
incrementado (ou decrementado) ele passa a apontar para a posição do elemento 
seguinte (ou anterior). Do mesmo modo somar três a um ponteiro faz com que ele passe 
apontar para o terceiro elemento após o atual. Portanto, um incremento em um ponteiro 
que aponta para um valor que é armazenado em n bytes faz que n seja somado ao 
endereço. 
 É possível usar o seguinte comando: 
 
*(p+1)=10; 
 
Este comando armazena o valor 10 na posição de memória seguinte àquela 
apontada por p. É possível somarem-se e subtraírem-se inteiros de ponteiros. A 
operação abaixo faz com que o ponteiro p passe a apontar para o terceiro elemento após 
o atual. 
 
p = p + 3; 
 
 A diferença entre ponteiros fornece quantos elementos do tipo do ponteiro 
existem entre os dois ponteiros. No exemplo abaixo é impresso o valor 3. 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main(void) 
{ 
 int vetor[] = { 10, 20, 30, 40, 50 }; 
 int *p1; 
 
 p1 = &vetor[2]; 
 printf("%d\n", *p1); 
 p1++; 
 printf("%d\n", *p1); 
 p1 = p1 + 1; 
 printf("%d\n", *p1); 
 
 _getch(); 
} 
#include <conio.h> 
#include <stdio.h> 
 
void main(void){ 
 float vetor[] = { 1.0, 2.0, 3.0, 4.0, 5.0 }; 
 float *p1, *p2; 
 
 p1 = &vetor[2];/* endereco do terceiro elemento */ 
 p2 = &vetor[0];/* endereco do primeiro elemento */ 
 printf("Diferenca entre ponteiros %d\n", p1-p2); 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 36
Não é possível multiplicar ou dividir ponteiros. 
 
Comparação de Ponteiros 
 
 É possível comparar ponteiros em uma expressão relacional. Só é possível 
comparar ponteiros de mesmo tipo. O trecho de programa abaixo ilustra um exemplo 
deste tipo de operações. 
 
 if (c == v) 
 printf("As variáveis estao na mesma posicao.\n"); 
 else 
 printf("As variaveis nao estao na mesma posicao.\n"); 
 
 Sendo c e v dois ponteiros declarados e inicializados anteriormente. 
 
 
Ponteiros e Vetores 
 
Ponteiros e Vetores estão fortemente relacionados na linguagem C. O nome de 
um vetor é um ponteiro que aponta para a primeira posição do vetor e todas as 
operações já mencionadas para ponteiros podem ser executadas com um nome de vetor. 
Por exemplo, a declaração 
 
int v[100]; 
 
declara um vetor de inteiros de 100 posições, e a partir dela temos que v é um ponteiro 
equivalente ao da declaração abaixo 
 
int *v; 
 
Por esta razão as seguintes declarações são idênticas e podem ser intercambiadas 
independentemente do modo como v foi declarado: 
 
v[i] = *(v+i); 
&v[i] = v+i; 
 
 O exemplo ilustrado abaixo mostra as duas notações sendo usadas para imprimir 
o mesmo vetor. 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 float v[] = {1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0}; 
 int i; 
 
 for (i=0; i<9; i++) 
 printf("%.1f ", v[i]); 
 
 printf("\n"); 
 for (i=0; i<9; i++) 
 printf("%.1f ", *(v+i)); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 37
Existe uma diferença fundamental entre declarar um conjunto de dados como um 
vetor ou através de um ponteiro. Na declaração de vetor, o compilador automaticamente 
reserva um bloco de memória para que o vetor seja armazenado. Quando apenas um 
ponteiro é declarado, a única coisa que o compilador faz é alocar um ponteiro para 
apontar para a memória, sem que espaço seja reservado. 
 
 O nome de um vetor é chamado de ponteiro constante e, portanto, não pode ter o 
seu valor alterado. Assim, os comandos abaixo não são válidos: 
 
 
 
 
 
 
 
 
 
 
 
Ponteiros e Strings 
 
 Na linguagem C, este tipo de inicialização de ponteiro é perfeitamente válido: 
 
 char *str = “Esta e uma string” 
 
 Como você pode observar, o ponteiro str não é um vetor. Todo compilador C 
cria o que é chamada de tabela de string, que é usada internamente pelo compilador 
para armazenar as constranges strings usadas pelo programa. Assim, o comando de 
declaração anterior coloca o endereço de “Esta e uma string”, armazenado na tabela de 
strings no ponteiro str. 
 Veja o exemplo abaixo, este programa imprime na tela o conteúdo da string e de 
trás para frente: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
 
void main() 
{ 
 int list[5], i; 
 
 /* O ponteiro list nao pode ser modificado recebendo o endereco de i */ 
 list = &i 
 
 /* O ponteiro list nao pode ser incrementado */ 
 list++; 
 
 _getch(); 
} 
#include <conio.h> 
#include <stdio.h> 
#include <string.h> 
 
void main() 
{ 
 char *str = "Esta e uma string"; 
 
 printf("%s\n\n", str); 
 
 for(int i = strlen(str)-1; i>=0; i--) 
 printf("%c", str[i]); 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 38
Alocação Dinâmica de Memória 
 
 A alocação dinâmica é o meio pelo qual um programa pode obter memória 
enquanto está em execução. Como você sabe, as variáveis globais e locais devem ser 
declaradas de executar um programa, elas não podem ser acrescentadas durante o tempo 
de execução, portanto elas são constantes. Porém, haverá momentos em que um 
programa precisa usar quantidades de armazenamento variáveis, não constantes 
 
As funções básicas de alocação de memória são malloc(), calloc() e free(). Estas 
funções são encontradas na biblioteca stdlib.h. 
 As funções malloc() e calloc() alocam memória (neste volume utilizaremos 
malloc()) e free() libera. Toda vez que você alocar espaço na memória deverá liberá-la. 
 
 A função malloc() tem o seguinte protótipo: 
 
 void *malloc(size_t numero_de_bytes); 
 
 Aqui, numero_de_bytes é o número de bytes de memória que você quer alocar. 
A função malloc() devolve um ponteiro do tipo void, o que significa que você pode 
atribuí-lo a qualquer tipo de ponteiro, utilizando um cast.O fragmento de código seguinte aloca 1000 bytes na memória: 
 
 char *str; 
 str = (char *) malloc(1000); 
 
 Ou ainda: 
 
 char *str; 
 str = (char *) malloc(1000*sizeof(char)); 
 
 Como cada elemento do tipo char corresponde a 1 byte, então teremos 1000 
bytes alocados. Se quisermos 150 elementos do tipo int: 
 
 int *ptr; 
 ptr = (int *) malloc(150*sizeof(int)); 
 
 Mas infelizmente a memória disponível para o desenvolvimento de nosso 
software não é infinita, portanto sempre que for feita a alocação dinâmica de memória 
devemos testar o valor devolvido por malloc(). Se houver um erro de alocação ele 
devolverá zero (0). Exemplo: 
 
 int *ptr; 
 ptr = (int *) malloc(150*sizeof(int)); 
 if(!ptr) 
{ 
 printf(“Erro! Memoria Insuficiente”); 
 _getch(); 
 exit(1); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 39
 Como já foi dito, a função free() libera a memória alocada. Ela possui o seguinte 
protótipo: 
 
 void free(void *p); 
 
 É muito importante que você nunca usar free() com um argumento inválido; 
isso destruiria a lista de memória livre. 
 
 Veja o programa abaixo utilizando os conceitos de alocação dinâmica de 
memória: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Note que foi utilizado um do-while para o recebimento da variável tam, isso 
porque esta variável não pode assumir um valor menor ou igual a zero, já que não se 
pode realizar uma alocação de memória negativa ou igual a zero. 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
#include <stdlib.h> 
 
void main(void) 
{ 
 float *ptr; 
 int tam; 
 
 do{ 
 printf("Entre com a quantidade de elementos que deseja para 
seu vetor: "); 
 scanf("%d", &tam); 
 }while(tam<=0); /* tam não pode ser menor ou igual a 0 */ 
 
 ptr = (float *) malloc(tam*sizeof(float)); /* aloca memoria */ 
 if(!ptr) /* verificação de erro na alocação */ 
 { 
 printf("Erro! Memoria Insuficiente"); 
 _getch(); 
 exit(1); 
 } 
 
 for(int i = 0; i < tam; i++) 
 { 
 printf("Entre com o %d elemento: ", i+1); 
 scanf("%f", ptr+i); /* recebe o elemento do usuario */ 
 } 
 
 system("cls"); 
 printf("Seu vetor e: "); 
 for(int i = 0; i < tam; i++) 
 printf("%.2f ", *(ptr+i)); /* imprime o elemento */ 
 
 free(ptr); /* libera memoria */ 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 40
Ponteiros e Matrizes 
 
 Nesta seção abordaremos a relação entre ponteiros e matrizes bidimensionais, já 
que matrizes unidimensionais (vetores) já foram tratadas anteriormente. Não há muita 
diferença destes, apenas na forma como o ponteiro é tratado para acessar um local 
específico da memória. 
 
Sabemos que um ponteiro aponta para uma área de memória que é endereçada 
de maneira linear. Deste modo, é necessário mapear o endereço de cada elemento na 
matriz, que é dado por linha coluna, em um endereço linear. 
 
Considere uma matriz chamada matriz de tamanho LIN, COL que poderia ser 
declarada e ter um de seus elementos lidos da seguinte maneira: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Caso o programa utilizasse ponteiros ao invés de notação de matrizes o trecho de 
programa ficaria da seguinte maneira, observe que o endereço de cada elemento da 
matriz teve de ser calculado explicitamente: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
#define LIN 3 
#define COL 4 
 
void main() 
{ 
 int matriz[LIN][COL]; 
 
 for(i=0; i<LIN; i++) 
 { 
 for (j=0; j<COL; j++) 
 { 
 printf("Elemento %d %d = ", i, j); 
 scanf("%d", matriz[i][j]); 
 } 
 } 
} 
 
#include <conio.h> 
#include <stdio.h> 
#define LIN 3 
#define COL 4 
 
void main(void){ 
 int *matriz; 
 int i, j; 
 
 matriz = (int *) malloc(LIN*COL*sizeof(int)); 
 if (!matriz){ 
 printf("Erro! memoria suficiente.\n"); 
 _getch(); 
 exit(1); 
 } 
 
 for(i=0; i<LIN; i++){ 
 for (j=0; j<COL; j++){ 
 printf("Elemento %d %d = ", i, j); 
 scanf("%d", matriz+(i*COL+j)); 
 } 
 } 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 41
Vetores de Ponteiros 
 
 Como ponteiros também são variáveis é possível então criar vetores de ponteiros 
e utilizá-los. O programa abaixo mostra um programa onde é utilizado um vetor de 
ponteiros para linhas de caracteres. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Observe esta declaração: 
 
 char *linha[LINHAS]; 
 
 Ela define um vetor, cujos elementos são ponteiros do tipo char que apontam 
para posições de memória. Até este momento temos apenas posições reservadas para 
armazenar os ponteiros. O espaço na memória só é efetivamente alocado após a 
chamada da função malloc(). 
 Neste programa fizemos a alocação linha por linha, onde cada linha possui 
capacidade para 60 elementos do tipo char, se comportando portando como uma matriz 
de strings (visto no capítulo 4), só que utilizando o conceito de ponteiros. 
 
 
 
 
#include <conio.h> 
#include <stdio.h> 
#include <stdlib.h> 
#define LINHAS 10 
#define COLUNAS 60 
 
void main(void) 
{ 
 char *linha[LINHAS]; 
 
 for(int i = 0; i < LINHAS; i++) 
 { /* aloca memoria, linha por linha */ 
 if(!(linha[i] = (char *)malloc(COLUNAS*sizeof(int)))) 
 { 
 printf("Nao consegui alocar o vetor %d.\n", i); 
 exit(i); 
 } 
 } 
 
 for(int i = 0; i < LINHAS; i++) 
 { 
 printf("Entre com a linha %d.\n", i); 
 gets(linha[i]); /* recebe as strings */ 
 } 
 
 for(int i = 0; i < LINHAS; i++) /* imprime as strings */ 
 printf("Linha %d: %s\n", i, linha[i]); 
 
 for(int i = 0; i < LINHAS; i++) 
 free(linha[i]); /* libera a memoria, linha por linha */ 
 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 42
Ponteiros para Ponteiros 
 
 No exemplo da seção anterior, observamos que o número de linhas da matriz é 
fixo, e, portanto, há uma mistura de notação de ponteiros com matrizes. Vamos 
considerar um exemplo onde tanto o número de linhas como o de colunas é 
desconhecido. Neste exemplo iremos criar um vetor de ponteiros que irá armazenar o 
endereço inicial de cada linha. Portanto, para obter um elemento da matriz primeiro 
devemos descobrir onde está a linha no vetor que armazena os endereços das linhas, em 
seguida procuramos na linha o elemento. 
 
 O programa abaixo pede ao usuário que digite o número de linhas e colunas da 
matriz. Em seguida lerá todos os elementos da matriz e a imprimirá na tela. Observe que 
agora foi criado um ponteiro para ponteiro chamado de **matriz. 
 
O programa primeiro pergunta o número de linhas da matriz para poder alocar 
espaço para armazenar os ponteiros para cada uma das linhas. Em seguida é alocado 
espaço para armazenar cada uma das linhas. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <stdio.h> 
#include <conio.h> 
#include <stdlib.h> 
 
void main () 
{ 
 int **matriz; /* matriz de ponteiros */ 
 int lin, col; /* número de linhas e colunas */ 
 
 do { 
 printf("Entre com o numero de linhas: "); 
 scanf("%d", &lin); /* recebe o numero de linhas */ 
 } while (lin<=0); 
 
 /* aloca as linhas da matriz */ 
 matriz = (int **) malloc (lin * sizeof(int *)); 
 if (!matriz) 
 { 
 printf("Erro! Memoria Insuficiente!"); 
 _getch(); 
 exit(1); 
 } 
 
 do{ 
 printf("Entre com o numero de colunas: "); 
 scanf("%d", &col); /* recebe o numero de colunas */ 
 }while (col<=0); 
 
 for (int i = 0; i < lin; i++) 
 { /* alocaas colunas de cada linha da matriz */ 
 *(matriz+i) = (int *) malloc(col * sizeof (int)); 
 if(! *(matriz+i) ){ 
 printf("Erro! Memoria Insuficiente!"); 
 exit(1); 
 } 
 } 
 
 printf("Entre com os elementos da matriz\n"); 
 for(int i = 0; i < lin; i++) 
 { 
 for(int j = 0; j < col; j++) 
 { 
 printf("\nElemento %d %d: ", i, j); 
 scanf("%d", *(matriz +i) +j); 
 } /* recebe os elementos */ 
 } 
 
 system("cls"); 
 printf("Os elementos da sua matriz sao\n\n"); 
 
 for(int i = 0; i < lin; i++) 
 { 
 for(int j = 0; j < col; j++) 
 printf("%d ", *(*(matriz +i) +j)); 
 printf("\n"); /* imprime os elementos na tela */ 
 } 
 
 free(matriz); 
 _getch(); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 44
Capítulo 6 – Funções 
 
 Funções são os blocos de construção de C, e é o local onde toda atividade 
ocorre. Elas são umas das características mais importantes de C. 
 
A forma geral de uma função é 
 
 especificador_de_tipo nome_da_funções(lista_de_parâmetros) 
{ 
corpo da função 
} 
 
especificador_de_tipo se refere ao tipo de valor que a função retorna. Este pode 
ser qualquer tipo válido em C. 
 
lista_de_parâmetros é uma lista de variáveis separadas por vírgulas e seus tipos 
associados, se caso a sua função não precisar de parâmetros, a lista será vazia. No 
entanto, os parênteses ainda são necessários. Exemplos: 
 
 
 
 
 
 
 
 
 
 
 
Também pode ocorrer, como já foi discutido no capítulo 2, a necessidade da 
declaração de variáveis internamente, esse tipo de variável é chamada de variável local, 
essas variáveis vem a existir na entrada da função e são destruídas ao sair, ou seja, não 
podem ser acessadas após o fim da função. 
 
Em geral os argumentos podem ser passados de duas maneiras, chamada por 
valor ou chamada por referência. No primeiro caso, é copiado o valor de um argumento 
para o parâmetro de uma função, assim alterações feitas nos parâmetros formais não 
possuem efeito nas variáveis utilizadas. No segundo caso, chamada por referência, é 
repassada para a função o endereço da variável como argumento, assim as operações 
ocorrem diretamente no argumento, ou variável global. 
 
 
 
 
 
 
 
 
 
 
void funcao1(char y, int h) 
{ 
bloco 
} 
 
float funcao2(float x, double *z) 
{ 
bloco 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 45
Funções Recursivas 
 
Um tipo especial de função é a função recursiva, ou recursão, que ocorre quando 
um comando no corpo da função a chama. Um exemplo clássico de função recursiva é o 
calculo de fatorial. Veja o exemplo abaixo: 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
O mesmo exemplo pode ser feito de forma não-recursiva, veja o trecho do programa: 
 
 int fatorial(int x) 
 { 
 int resultado = 1; 
 
 for(int t = 1; t <= x; t++) 
 resultado = resultado*(t); 
 
 return resultado; 
 } 
 
 
 
 
 
 
 
#include <stdio.h> 
#include <conio.h> 
 
int fatorial(int x); 
 
void main () 
{ 
 int num; 
 
 do{ 
 printf("Entre com um numero natural para o calculo do 
fatorial: "); 
 scanf("%d", &num); 
 }while(num < 0); 
 
 printf("\n\nO fatorial de %d e %d", num, fatorial(num)); 
 _getch(); 
} 
 
int fatorial(int x) 
{ 
 int resultado; 
 if(x == 1) 
 return 1; 
 else if(x == 0) 
 return 1; 
 else 
 return resultado = x*fatorial(x-1); 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 46
Funções que retornam ponteiros 
 
 As funções que devolvem ponteiros são manipuladas da mesma forma, embora 
precisem de atenção especial. Ponteiros para variáveis não são variáveis, eles são o 
endereço na memória de um certo tipo de dado. Para se retornar um ponteiro, a função 
deve ter o tipo de retorno igual a um ponteiro. Veja um exemplo de uma função que 
devolve um ponteiro para a primeira ocorrência do caractere c na string s 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 Lembrando que quando se incrementa um ponteiro, ele aponta para o próximo 
elemento da memória. 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
#include <stdio.h> 
#include <conio.h> 
 
char *match(char c, char *s); 
 
void main () 
{ 
 char ch, *s = "ola mundo", *string; 
 
 printf("Entre com um caractere: "); 
 scanf("%c", &ch); 
 
 string = match(ch, s); 
 printf(string); 
 
 _getch(); 
} 
 
char *match(char c, char *s) 
{ 
 while(c != *s) 
 s++; 
 return s; 
} 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 47
Capítulo 7 – Estruturas 
 
 Em C, uma estrutura é uma coleção de variáveis referenciadas por um nome, 
fornecendo uma maneira conveniente de se ter informações relacionadas agrupadas. 
Uma definição de estrutura forma um modelo que pode ser usado para criar variáveis de 
estruturas. As variáveis que compreendem a estrutura são chamadas elementos da 
estrutura. 
 
 Por exemplo, se quisermos uma estrutura com dados de um aluno, com 
informação deste como seu nome, número de matrícula, entre outros, devemos usar o 
seguinte fragmento de código que mostra como criar um modelo de estrutura. A 
palavra-chave struct informa ao compilador que um modelo de estrutura está sendo 
definido. 
 
 struct alunos 
{ 
 char nome[80]; 
 char numero_matricula[15]; 
 int numero_de_faltas; 
 float notas[4]; 
}; 
 
Note que a definição termina com um ponto-e-vírgula. Isso ocorre porque uma 
definição de estrutura é um comando. Além disso, o nome (ou rótulo) da estrutura 
alunos identifica essa estrutura de dados em particular e é o seu especificador de tipo. 
 
Com este código, nenhuma variável foi de fato declarada. Foi feito apenas uma 
definição do formato da estrutura. Para declarar uma variável com essa estrutura escreva 
 
struct alunos informacao; 
 
Isso declara uma variável do tipo estrutura alunos chamada informacao. 
O compilador C aloca automaticamente memória suficiente para acomodar todas 
as variáveis que formam a variável estrutura. Neste caso, esta estrutura ocupa na 
memória o espaço de 113 bytes. 
 
Você também pode declarar uma ou mais variáveis enquanto a estrutura é 
definida. Por exemplo, 
 
struct alunos 
{ 
 char nome[80]; 
 char numero_matricula[15]; 
 int numero_de_faltas; 
 float notas[4]; 
} informacao, dados, variavel; 
 
 
 
 
 
 
Universidade Federal De Uberlândia 
Faculdade de Engenharia Elétrica e Biomédica 
 48
O nome da estrutura pode ser omitido se você precisar apenas de uma variável 
estrutura. Isso significa que 
 
struct alunos 
{ 
 char nome[80]; 
 char numero_matricula[15]; 
 int numero_de_faltas; 
 float notas[4]; 
} informacao; 
 
Declara uma variável chamada informacao como definido pela estrutura que a precede. 
 
 Portanto, a forma geral de uma definição de estrutura é 
 
 struct nome 
 { 
 tipo nome_da_variável1; 
 tipo nome_da_variável2; 
 . 
 . 
 . 
 tipo nome_da_variávelN; 
 }variáveis_estrutura; 
 
Onde nome ou variáveis_estrutura podem ser omitidos, mas não ambos. 
 
Refereciando Elementos de Estruturas 
 
 Os elementos individuais de estruturas são referenciados através do operador 
ponto (.). Por exemplo, digamos que você deseja acessar o número de faltas de um 
aluno, atribuindo a esse um valor, escreva 
 
 informacao.numero_de_faltas = 2; 
 
 O nome da variável estrutura seguido por um ponto e pelo nome do elemento 
referencia esse elemento individual da estrutura. A forma geral para acessar um 
elemento de estrutura é 
 
 nome_da_variável_estrutura.nome_do_elemento 
 
 Portanto, para escrever o numero de faltas na tela escreva 
 
 printf("%d",infomrmacao.numero_de_faltas); 
 
 Analogamente, pode ser usada a função gets() para receber o nome do aluno 
 
 gets(informacao.nome); 
 
 
 
 
 
 
Universidade Federal De Uberlândia

Continue navegando