Baixe Programas Associados a Botões: Ordenar e Inverter Sequências e outras Notas de estudo em PDF para Informática, somente na Docsity!
4.5.2. Exercício
Modifique o exemplo 4.5.1, para que a atualização dos EditBox sejam feitos através de um
procedimento.
4.5.3. Exemplo 2
Modifique o exemplo 4.5.1, para usar ponteiros na ordenação da matriz de frutas M.
Resposta
a) Variáveis Globais:
#define comp 8 #define linhas 5 char M[linhas][comp] = {"banana", "maça", "abacate", "pera", "uva"}; char * pM[linhas];
b) Linhas de programa associadas ao botão "ordena":
int nvezes, x; char aux; int fim = 3; for (x=0; x<5; pM[x] = M[x],x++); // apontar para elementos de M for (nvezes = 0; nvezes < 4; nvezes++) { for (x=0; x<=fim; x++) { if (pM[x] > *pM[x+1]) //[1]
Vetor de ponteiros
aux = pM[x]; pM[x] = pM[x+1]; pM[x+1]= aux; fim--; [2] } } } Edit1->Text = pM[0]; //[3] Edit2->Text = pM[1]; Edit3->Text = pM[2]; Edit4->Text = pM[3]; Edit5->Text = pM[4]; }
Observe que:
- As linhas de M apresentadas nos Edits, ainda seguem a mesma ordem (pM[0] até pM[4]). é
apresentado;
- Agora, os ponteiros apontam para as linhas de M. Note que as posições de M não são
alteradas.
4.5.4. Exercícios
1 - O que acontece se a linha [1] for modificada para if (pM[x] > pM[x+1]) ...
2 - Para que serve a linha [2]?
3 - O que acontece se as atribuições aos Edits a partir da linha [3] forem modificadas para
Edit1->Text = *pM[0]; ... Edit1->Text = *pM[4];
4 – Desenvolva o programa de busca binária (aula de vetores), agora usando ponteiros.
5 – Desenvolva uma função para somar o conteúdo de duas matrizes 3x3 usando ponteiros.
a) Dentro do botão "inverte", o trecho do programa que realiza a alocação:
void __fastcall TForm1::InverteClick( **TObject *** Sender) { **char *** s; char aux[2]; char buf[41]=""; int x=0; s = ( char *) malloc(40); if (!s) { Application->MessageBox("mensagem","erro de alocação",MB_OK); exit (1); }
// continua....
Na linha de código s = (char *) malloc(40); do exemplo acima, foi criado um ponteiro "s" do
tipo char. Este ponteiro vai apontar para o endereço inicial, da região de memória alocada por
malloc(). Note que o comando malloc recebe como parâmetro, o valor da quantidade de
memória requerida, neste caso é 40. Mas o que significa este valor "40"? Este tamanho
reserva na memória 40 espaços de tamanho ( char *), ou seja, 40 bytes, que estão reservados
pelo programa e podem, agora, ser acessados através do ponteiro "s".
Após a criação de "s", é necessário (e obrigatório) testar se o ponteiro existe, ou seja, se ele
aponta para algum endereço na memória não nulo. Se por algum motivo, o ponteiro não puder
ser criado, então ele conterá um valor nulo (NULL ou 0). Se isto ocorrer, a execução do
programa deve ser terminada, sob pena de tornar instáveis os outros aplicativos em
execução,ou até travar o sistema operacional. O comando if (!s) testa se o ponteiro existe, ou
seja, se contém um valor diferente de nulo. Se o ponteiro for nulo, é apresentado uma
mensagem de erro e o programa deve ser encerrado. O comando exit(1) encerra o programa
neste ponto.
As outras variáveis locais, definidas no exemplo acima, serão apresentadas a seguir.
// continuação....
strcpy(s,Edit1->Text.c_str()); [1] for (x = strlen(s)-1; x >= 0; x--) [2] { sprintf(aux,"%c",s[x]); [3] strcat(buf,aux); [4] } Edit2->Text = buf; [5] free(s); [6]
Seguindo o exemplo, na linha:
[1] O conteúdo do EditBox é passado para a região de memória apontada por "s".
[2] É executado um loop que varia de x (tamanho de s) até 0, que é a primeira posição da
string s (s[0]).
[3] Converte um caracter em uma string.
[4] A variável local buf concatena cada valor de aux.
[5] Buf é apresentado no formulário;.
Após a utilização da variável alocada dinamicamente, é extremamente importante liberar a
área de memória alocada por malloc, usando o comando free(). A linha
[6] libera a área de memória apontada pelo ponteiro "s".
[5] outra maneira de calcular o tamanho de uma string, usando ponteiros.
[6] "aux" aponta para uma nova região na memória, do tamanho de "s".
[7] testa se o ponteiro existe, ou seja, se "aux" aponta para um endereço diferente de nulo.
[8] encerra o programa se a memória não foi alocada.
[9] inverte o conteúdo da string "s" e armazena em "aux". Incrementa-se índice de y e move o
ponteiro de "s" para o início de "s".
[10] acrescenta finalizador de string.
[11] Edit2 recebe aux; na verdade, o Edit aponta para aux.
[12] libera a área apontada por "aux".
Como pode ser observado, neste exemplo não se fez necessário o uso de bibliotecas de
manipulação de string, ou seja, o programador tem maior controle sobre o que está sendo
executado. Muitas vezes, linguagens de mais alto nível, mascaram efetivamente o que
acontece após o comando ser executado. Da forma apresentada neste exemplo, o programador
consegue entender o que está acontecendo durante a execução do programa, evitando inserir
comandos desnecessários.
5.3. Exercícios
1- O que acontece se for alocado para aux um tamanho de memória diferente do valor de x?
2- Crie um EditBox no formulário para apresentar o conteúdo do ponteiro "aux", após ser
executada a linha [12]. O que será apresentado na tela? Que cuidados deve-se ter ao liberar a
memória apontada pelos ponteiros?
5.4. Portabilidade
O tamanho em bytes, de um dado tipo de variável, pode mudar de máquina para máquina.
Para evitar problemas com diferenças de tamanhos e assegurar a portabilidade de programas,
usa-se o operador sizeof(). sizeof informa ao programa o tamanho cujo tipo de variável ocupa
na memória.
5.4.1. Exemplo do uso de sizeof
int *pont; [1]
pont = ( int *) malloc(sizeof( int )); [2]
if (!pont) [3]
Application->MessageBox("mensagem","erro de alocacao",MB_OK);
exit(1); [4]
do exemplo acima, obsrva-se em:
[1] declaração de um ponteiro para inteiro chamado "pont";
[2] "pont" aponta para o endereço da posição de memória defina pelo comando malloc; note
que o tamanho a ser criado é especificado pelo sizeof(). Porque provavelmente, o inteiro
usado neste exemplo deve reservar 4 bytes por estar operando sobre o Windows; se o mesmo
programa fosse executado no DOS, provavelmente, seria reservado 2 bytes (Ansi C).
[3] realiza o teste de validade do ponteiro.
[4] se o ponteiro não for criado, aborta a execução do programa.
5.5. EXERCÍCIOS
1- Escreva um programa para ler N valores reais fornecidos por um usuário, e que calcule a
média destes valores e apresente o resultado. O programa deve usar ponteiros.
Resposta:
a) Variáveis globais usadas no programa:
2- Responda as seguintes perguntas:
a) O ponteiro "valores" do exercício acima é um vetor?
b) Para que serve o comando valores++?
c) O que acontece, se não for especificado um valor de N ao executar o programa?
d) A variável char msg[20] pode ser declara como char *msg?
3- Escreva um programa que leia uma matriz fornecida pelo usuário, e apresente na tela a
transposta desta matriz. Use alocação dinâmica para armazenar a matriz. E ponteiros para
acessar seu conteúdo.
int *M; // ponteiro para uma matriz declarado como global
__fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { M = (int *)malloc(9 * sizeof(int)); if (!M) { ShowMessage("falta memória"); exit(0); } }
//-------------------------------------------------------------------------
void __fastcall TForm1::ArmazenaClick(TObject *Sender) { *(M+0) = atoi(Edit1->Text.c_str()); *(M+1) = atoi(Edit2->Text.c_str()); *(M+2) = atoi(Edit3->Text.c_str()); *(M+3) = atoi(Edit4->Text.c_str()); *(M+4) = atoi(Edit5->Text.c_str()); *(M+5) = atoi(Edit6->Text.c_str()); *(M+6) = atoi(Edit7->Text.c_str()); *(M+7) = atoi(Edit8->Text.c_str()); *(M+8) = atoi(Edit9->Text.c_str()); }
//------------------------------------------------------------------------- void __fastcall TForm1::TranspostaClick(TObject *Sender) {
// ... inserir o código aqui.
}
//------------------------------------------------------------------------- void __fastcall TForm1::FormClose(TObject *Sender, TCloseAction &Action) { free(M); }
//-------------------------------------------------------------------------
4- Modifique o exercício 3, para que o espaço alocado dinamicamente para a matriz M, seja
criado de acordo com a dimensão da matriz, especificada pelo usuário.
5- Modifique o exercício 4.3, criando uma função que aloca espaço dinamicamente, e que
possa ser chamada sempre que for preciso alocar memória.
6. ARQUIVOS EM C
6.1. Ponteiro de arquivo
Arquivos são coleções de dados que recebem um nome único, pelo qual os dados podem ser
acessados e manipulados. Um ponteiro de arquivo é uma variável ponteiro do tipo FILE , que
é uma estrutura declarada em <stdio.h> e contém informações sobre o arquivo. Um
ponteiro para arquivo pode ser obtido da seguinte forma:
FILE *fp;
Para operar com arquivos é necessário realizar as seguintes operações:
6.2. Abrindo arquivos
Antes de qualquer operação com arquivos, é necessário que este arquivo exista. Para isto, é
preciso abrir um arquivo, que em C é dado pelo comando fopen() , da seguinte forma:
FILE * fopen (nomearquivo, modo)
Onde:
nomearquivo é uma string e pode incluir o caminho para o arquivo e a extensão.
modo determina como o arquivo será aberto. O arquivo pode ser do formato texto, ou do
formato binário. Os valores para modo são:
Tipo do ponteiro Nome do ponteiro
Abrir o
arquivo
Operações de
leitura/escrita
Fechar o
arquivo
Modo Descrição
rt abre arquivo texto para leitura
wt abre arquivo texto para escrita
at anexa dados a um arquivo texto
rb abre arquivo binário para leitura
wb abre arquivo binário para escrita
ab anexa elementos a um arquivo binário
r+t abre um arquivo texto para leitura/escrita
w+t cria um arquivo texto para leitura/escrita
a+t anexa dados a um arquivo texto para leitura/escrita
r+b abre um arquivo binário para leitura/escrita
w+b cria um arquivo binário para leitura/escrita
a+b anexa a um arquivo binário para leitura/escrita
6.2.1. Arquivos Tipo Texto
Qualquer arquivo cujo conteúdo está expresso pelos valores de caracteres da tabela ASCII.
Normalmente um arquivo texto não contém nenhum tipo de formatação, e pode ser aberto em
qualquer aplicativo.
Exemplo :
6.3.1. Observações
a) Se o arquivo "teste.txt" não existir, o comando fopen() cria este arquivo em branco. Se a
opção for "w", ele grava as informações neste arquivo recém criado.
b) Se o arquivo "teste.txt" já existir, o comando fopen() abre este arquivo. Se a opção for "w",
ele grava as novas informações sobre o conteúdo antigo. Ou seja, as informações anteriores
são perdidas. Para que isso não aconteça, deve-se usar o modo "a" para anexar dados ao
arquivo existente.
c) O que acontece se um arquivo não puder ser criado, como por exemplo, se não houver mais
espaço em disco, ou se um disquete não estiver inserido no drive especificado? Então, antes
de usar este arquivo, é necessário saber se o arquivo foi criado corretamente, para isso é
obrigatório testar o conteúdo do ponteiro de arquivo. Assim, o comando para abrir um
arquivo, pode ser escrito da seguinte forma:
FILE *fp; [1]
fp = fopen("teste.txt","wt"); [2] if (fp == NULL) [3]
exit(1);
Onde:
[1] é uma variável ponteiro do tipo FILE;
[2] abre o arquivo teste.txt tipo texto no modo de escrita (w);
[3] testa se o ponteiro é diferente ou igual a nulo, se for NULL interrompe a execução do
programa.
6.4. Abrindo um arquivo para leitura
Da mesma forma que é necessário abrir (ou criar) um arquivo para escrever algo, pode-se
abrí-lo apenas para leitura das informações. Desta maneira o conteúdo do arquivo não pode
ser alterado, e sim, apenas consultado. O comando para abrir um arquivo para leitura é dado
como mostra o exemplo abaixo:
FILE *fp; [1]
fp = fopen("teste.txt","rt"); [2]
if (fp == NULL) [3]
exit(1);
Onde:
[1] é uma variável ponteiro do tipo FILE;
[2] abre o arquivo teste.txt tipo texto no modo de leitura (r);
[3] testa se o ponteiro é diferente ou igual a nulo, se for NULL interrompe a execução do
programa.
6.5. Fechando um arquivo
O comando fclose() fecha um arquivo que foi aberto pela chamada de fopen(). Sempre, a
operação com arquivos acontece nesta sequência:
Um arquivo aberto, quando não for sofrer mais nenhum tipo de operação, obrigatoriamente
deve ser fechado. Se não for fechado, o arquivo pode ficar definitivamente inacessível.
Onde:
[1] é uma variável ponteiro do tipo FILE;
[3] abre o arquivo fputc.txt do tipo texto no modo de escrita (w);
[4] testa se o ponteiro é diferente ou igual a nulo, se for NULL interrompe a execução do
programa;
[5] enquanto o conteúdo da string apontada por str for diferente de '\0', cada caracter é
passado para o arquivo apontado por fp, se não ocorrer uma mensagem de final de arquivo,
EOF;
[6] fecha o arquivo apontado por fp.
Exemplo 2: Gravando um arquivo em modo binário.
FILE *fp; [1]
char *str = "texto de exemplo"; [2]
fp = fopen("c:/temp/fputc.txt","wb"); [3]
if (fp == NULL) [4]
exit(1);
while (*str)
if (!ferror(fp)) fputc(*str++,fp); [5]
fclose(fp); [6]
Onde:
[1] é uma variável ponteiro do tipo FILE;
[3] abre o arquivo fputc.txt do tipo binário no modo de escrita (w);
[4] testa se o ponteiro é diferente ou igual a nulo, se for NULL interrompe a execução do
programa;
[5] enquanto o conteúdo da string apontada por str for diferente de '\0', cada caracter é
passado para o arquivo apontado por fp, se não ocorrer uma mensagem de final de
arquivo,EOF. Entretanto, para arquivos abertos para operações binárias, um EOF pode ser um
caracter válido. Então é necessário usar a função ferror para determinar a ocorrência de um
erro;
[6] fecha o arquivo apontado por fp.
6.6.2. fgetc()
Esta função devolve o próximo caracter da stream de entrada na posição atual e incrementa o
indicador de posição do arquivo. Se o final do arquivo for alcançado, a função devolverá
EOF. Para arquivos binários, testar o final de arquivo com o comando feof(). A sintaxe é dada
por:
fgetc( FILE *pont);
Onde, pont é o ponteiro para o arquivo.
Exemplo1: Lendo um arquivo em modo texto.
FILE *fp; [1]
char *ch;
ch = (char *)malloc(20 * sizeof(char)); [2]
if (!ch) exit(1);
fp = fopen("c:/temp/fgetc.txt","rt"); [3]
if (fp == NULL) [4]
exit(1);
while ((*ch++ = fgetc(fp)) != EOF); [5]
fclose(fp); [6]
Onde:
[1] é uma variável ponteiro do tipo FILE;
[2] cria uma área para uma string;
[3] abre o arquivo fgetc.txt do tipo texto no modo de leitura (r);