AULA 10 DIA 12/09/2013
Objetivos
- Vetores de char e strings
- Processamento de Strings
Tipo Char
Em aulas anteriores vimos que um caracter pode ser representado por uma sequência de bits. Utilizando um código é possível definir o significado da sequência. Um código amplamente usado é o ASCII. Com 8 bits (7 no Ascii original) tem-se então a possibilidade de representar qualquer letra, número, ou símbolo (vírgula, ponto-e-vírgula etc). Note que um número representado em ASCII NÂO serve para realizar operações aritméticas. Trata-se de representação textual, por exemplo, um dígito de um número telefone.
Em síntese, uma variável do tipo caracter é tratada como um número inteiro e declarada com o tipo char, que na prática é um número inteiro de byte.
Exemplo
#include <stdio.h>
main ()
{
char x='A',y=65,w=0x41,z; /* três formas de representar a mesma coisa */
scanf("%c",&z);
printf("Caracter lido = %c\n",z);
printf("Caracter lido = %d\n",z);
printf("Caracter lido = %x\n",z);
if (z==x)
printf("Iguais 1\n");
if (z==y)
printf("Iguais 2\n");
if (z==w)
printf("Iguais 3\n");
}
Armazenamento de cadeias de caracteres em vetores
Uma cadeia de caracteres ou string nada mais é que uma sequência de caracteres ASCII. Para mantermos coerência com a linguagem C, vamos assumir que uma string bem comportada termina com um zero (0, não o caracter '0' que é o número 48 decimal em ASCII). Em inglês chama-se "string NULL terminated".
Exemplo: A string "IFSC" armazenada em um vetor CADEIA, na memória de um computador, teria a seguinte implementação:
Estamos assumindo que cada caracter é armazenado em um byte. Uma string terminada em 0 facilita o seu processamento pois pode-se facilmente detectar o seu final. Note que no exemplo acima, a string está armazenada em um vetor CADEIA cujo tamanho excede ao da string. Os bytes que se seguem ao zero podem ser considerados lixo.
Processando cadeias de caracteres
Sabendo como uma string é armazenada na memória de um computador torna-se fácil processá-la. Por exemplo, vamos ver um algoritmo para contar o número de caracteres de uma string lida pelo teclado para dentro de um vetor CADEIA.
EXERCÍCIO 1: Colocar o procedimento acima na forma de um subprograma (função) que recebe como parâmetro o vetor e retorna um número inteiro correspondente ao tamanho da cadeia.
EXERCÍCIO 2: Elaborar um fluxograma para computador o número de ocorrências do caracter 'b' em uma string lida pelo teclado. Apresentá-lo na forma de um subprograma que retorna o número de ocorrências.
EXERCÍCIO 3: Estude a tabela ASCII e elabore um fluxograma para capitalizar todos os caracteres minusculos de uma string lida pelo teclado. Apresentá-lo como subprograma.
Vetor de Char
É possível definir vetores do tipo char. Tais vetores permitem definir cadeias de caracteres. Para marcar um final de cadeia usa-se o número 0 (NULL),
Exemplo:
Fazer um programa para computar o número de caracteres de uma cadeia (string) lida pelo teclado.Use o comando while.#include <stdio.h>
void main ()
{
char alfa[50];
int i=0;
printf ("Entre com a cadeia: ");
scanf("%s",alfa);
while(alfa[i]!=0)
i++;
printf ("\nNumero de caracteres em %s = %d \n", alfa, i);
}
Exercício
- Implementar um programa que computa o número de caracteres 'a' de uma string lida pelo teclado.
#include <stdio.h>
void main ()
{
char alfa[50];
int cont;
int i=0;
printf ("Entre com a cadeia: ");
scanf("%s",alfa);
cont=0;
while(alfa[i]!=0){
if (alfa[i]=='a')
cont++;
i++;
}
printf ("\nNumero de caracteres a em %s = %d \n", alfa, cont);
}
- implementar um programa que computa o número de ocorrências das subcadeias "ab" de uma string lida pelo teclado.
#include <stdio.h>
void main ()
{
char alfa[50];
int cont;
int i=0;
printf ("Entre com a cadeia: ");
scanf("%s",alfa);
cont=0;
while(alfa[i]!=0){
if (alfa[i]=='a'&&alfa[i+1]=='b') {
cont++;
i++; /* avançar uma posição */
}
i++;
}
printf ("\nNumero de subcadeias ab em %s = %d \n", alfa, cont);
}
- Implementar um programa que substitui todos os 'o' de uma cadeia por 'O.
#include <stdio.h>
void main ()
{
char alfa[50];
int cont;
int i=0;
printf ("Entre com a cadeia: ");
scanf("%s",alfa);
cont=0;
while(alfa[i]!=0){
if (alfa[i]=='o')
alfa[i]='O';
i++;
}
printf ("\nNova cadeia -> %s\n", alfa);
}
- Implementar um programa que lê duas cadeias e conta o número de caracteres iguais ocupando a mesma posição. Exemplo: suponha as cadeias "casa" e "amora". Não existem caracteres iguais na mesma posição. Já as cadeias "casa" e "cada" possuem três caracteres iguais na mesma posição
solução
|
#include <stdio.h>
void main ()
{
char alfa[50], beta[50];
int cont;
int i=0;
printf ("Entre com a cadeia:\n");
scanf("%s",alfa);
printf ("Entre com a cadeia:\n");
scanf("%s",beta);
cont=0;
while(alfa[i]!=0 && beta[i]!=0){
if (alfa[i]==beta[i])
cont++;
i++;
}
printf ("\nNumero de caracteres iguai em %s e %s na mesma posicao é %d \n", alfa, beta, cont);
}
|}
- Modificar o exercício anterior para que os caracteres não iguais sejam intercambiados (mas continue respeitando os finais das cadeias). Exemplo: "casa" e "malagueta" deve resultar em "mala" e "casagueta"
- Implementar um programa que lê duas cadeias e imprime uma mensagem caso as cadeias seja iguais.
|
AULA 11 DIA 16/09/2013
|
AULA 11 DIA 16/09/2013
Objetivos
O aluno deverá, após a aula;
- Saber operar sobre strings (sem uso de ponteiros), seja na função main, seja em funções com passagem de parâmetros do tipo vetor de char;
Iniciando uma cadeia na declaração
#include <stdio.h>
void main ()
{
char alfa[50]="IFSC-SJ";
printf ("\nNumero de caracteres em %s\n", alfa);
}
Como passar um vetor de caracteres como parâmetro
int str_len(char x[])
{
int i=0;
while (x[i]!=0)
i++;
return i;
}
Processamento de strings
Nas aulas anteriores vimos como definir e usar vetores. Vimos que é possível armazenar strings em vetores de char. O processamento de strings é de grande interesse em programação. Vamos continuar a ver alguns aspectos deste processamento.
Computando o tamanho de uma string
A função abaixo permite computar o tamanho de uma string.
int str_len(char x[])
{
int i=0;
while (x[i]!=0)
i++;
return i;
}
main()
{
char teste[]="IFSC-SJ";
int tamanho;
tamanho = str_len(teste);
}
Copiando strings
Implementar e testar uma função que copia uma cadeia de caracteres de um vetor de strings fonte para um vetor de destino, de forma similar a função strcpy.
#include <stdio.h>
void str_cpy(char auxs1[], char auxs2[])
{
int i;
for(i=0;auxs2[i]!=0;i++)
auxs1[i]=auxs2[i];
auxs1[i]=0;
}
main()
{
char str1[100], str2[100];
printf("Entre com a string => ");
scanf ("%s", str2);
str_cpy(str1, str2);
printf("\nString copiada = %s\n", str1);
}
ou #include <stdio.h>
void str_cpy(char auxs1[], char auxs2[])
{
int i=0;
do {
auxs1[i]=auxs2[i];
}while(auxs2[i++]!=0);
}
main()
{
char str1[100], str2[100];
printf("Entre com a string => ");
scanf ("%s", str2);
str_cpy(str1, str2);
printf("\nString copiada = %s\n", str1);
}
Concatenado strings
Da mesma forma que o exmplo anterior, implementar uma função similar a função strcat que permite concatenar duas strings passadas como parâmetro.
#include <stdio.h>
void str_cat(char auxs1[], char auxs2[])
{
int i=0;
/*localizar o final da string de destino*/
while(auxs1[i]!=0)
i++;
/* usando a função de cópia já implementada temos */
str_cpy(&auxs1[i], auxs2);
}
main()
{
char str1[100]="IFSC em ", str2[100]="Sao Jose", x[]="teste";
str_cat(str1, str2);
printf("\nString copiada = %s\n", str1);
}
Exercício:
Exercícios sobre strings
1.Implementar uma função chamada str_cmp que recebe duas strings como parâmetro e retona 0 se elas são iguais ou 1 se elas forem diferentes. A função é case sensitive.
2.Implementar uma função para adicionar (append) n caracteres iniciais de uma string2 para o final de uma string1. Retornar 0 se sucesso ou -1 se detectado algum problema.
int str_ncat(char string1[], char string2[], int n)
3.Implementar uma função para adicionar (append) a uma subcadeia da string2 para uma string1. A substring é iniciada no índice determinado por n.
int str_nicat(char string1[], char string2[], int n)
4.Implementar uma função para comparar até n primeiros caracteres de duas cadeias passadas como parâmetro. Retornar 0 se iguais, -1 se diferentes.
int str_ncmp(char string1[], char string2[], int n)
5.Implementar uma função para comparar uma substring da string2 com os n2 primeiros caracteres de string1. A substring é determinada pelo índice n1 com tamanho dado por n2. Retornar 0 se sucesso e -1 se problemas.
int str_nicmp(char string1[],char string2[], int n1, int n2)
6.Implementar uma versão não sensitiva a case da função str_cmp.
|
AULA 12 DIA 19/09/2013
|
AULA 12 DIA 19/09/2013
Objetivos
- Revisãos(exercícios) de vetores (arrays)
Exercícios
- Considere que as notas de matemática de alunos de uma escola são armazenadas por bimestre em 4 vetores: bim1, bim2,bim3 e bim4. Suponha que um aluno é identificado por um número inteiro que servirá como índice destes vetores. Construir funções que retornam: (1) a média anual da turma, (2) o desvio padrão das médias anuais da turma, (3) a média anual de um aluno dado seu índice na tabela. Na função main imprimir a média de cada aluno, a média da turma e o desvio padrão () da turma.
#define NUM_ALUNOS 6
float bim1[NUM_ALUNOS]={5.5,3.7,10.0,8.5,9.6,6.6};
float bim2[NUM_ALUNOS]={4.5,2.3,9.0,7.5,9.3,7.0};
float bim3[NUM_ALUNOS]={5.5,1.7,10.0,8.0,9.0,7.6};
float bim4[NUM_ALUNOS]={6.5,1.2,10.0,7.8,9.6,5.6};
float media_aluno(int aluno)
{
}
float media_turma()
{
}
float desvio_padrao()
{
}
main()
{
/* chamar aqui as funções e imprimir os retornos */
}
- Para o exercício anterior, construir na função main um código que calcula o número de alunos que estão dentro da faixa determinada pelo desvio padrão.
soluções
|
ex.1
Solução:#include <math.h>
#include <stdio.h>
#define NUM_ALUNOS 6
float bim1[NUM_ALUNOS]={5.5,3.7,10.0,8.5,9.6,6.6};
float bim2[NUM_ALUNOS]={4.5,2.3,9.0,7.5,9.3,7.0};
float bim3[NUM_ALUNOS]={5.5,1.7,10.0,8.0,9.0,7.6};
float bim4[NUM_ALUNOS]={6.5,1.2,10.0,7.8,9.6,5.6};
float media_aluno(int aluno)
{
float media;
media = (bim1[aluno]+bim2[aluno]+bim3[aluno]+bim4[aluno])/4;
return media;
}
float media_turma()
{
int i;
float media;
media = 0;
for (i=0;i<NUM_ALUNOS;i++)
media = media + media_aluno(i);
media = media/NUM_ALUNOS;
return media;
}
float desvio_padrao()
{
float media, prod;
int i;
media = media_turma();
prod=0;
for (i=0;i<NUM_ALUNOS;i++)
prod = powf(media_aluno(i)-media_turma(),2) + prod;
prod = sqrt(prod/NUM_ALUNOS);
return prod;
}
main()
{
/* chamar aqui as funções e imprimir os retornos */
printf("Media Anual do aluno 0 -> %.2f\n", media_aluno(0));
printf("Media Anual da Turma -> %.2f\n", media_turma());
printf("Desvio Padrão da Turma -> %.2f\n", desvio_padrao());
}
ex.2
main()
{
float desvp,lim_sup,lim_inf,media,aux;
int i,cont;
/* chamar aqui as funções e imprimir os retornos */
printf("Media Anual do aluno 0 -> %.2f\n", media_aluno(0));
printf("Media Anual da Turma -> %.2f\n", media=media_turma());
printf("Desvio Padrão da Turma -> %.2f\n", desvp=desvio_padrao());
cont=0;
lim_sup=media+desvp;
lim_inf=media-desvp;
for(i=0;i<NUM_ALUNOS;i++){
aux = media_aluno(i);
if (aux <= lim_sup && aux >= lim_inf)
cont++;
}
printf("Número de alunos dentro da faixa do desvio -> %d\n",cont);
}
|
|
AULA 14 DIA 26/09/2013
|
AULA 14 DIA 26/09/2013
Objetivos
- Definindo matrizes no C
- Operações com matrizes e passagem de parâmetros tipo matriz
Como definir e operar com matrizes no C
De forma similar ao vetor, basta definir a matriz usando colchetes para indicar a dimensão da variável.
Exemplo: Definir duas matrizes 2x3 já inicializadas e computar a soma das mesmas:
#include <stdio.h>
void main()
{
int mA[2][3]={ 11,12,13,
21,22,23},
mB[2][3]={1,2,3,
1,2,3},
mC[2][3];
int i,j;
for(i=0;i<2;i++){
for(j=0;j<3;j++) {
mC[i][j] = mA[i][j] + mB[i][j];
}
}
}
Exercício
- implementar um programa para calcular a média de todos elementos da matriz C do exemplo acima.
- IMplementar um programa para ler duas matrizes (matA e matB) e multiplicá-las, colocando o resultado em uma matriz matC.
Passando matrizes como parâmetro
#include <stdio.h>
void somar_mat(int aA[][3],int aB[][3], int cC[][3])
{
int i,j;
for(i=0;i<2;i++){
for(j=0;j<3;j++) {
cC[i][j] = aA[i][j] + aB[i][j];
}
}
}
void main()
{
int mA[2][3]={ 11,12,13,
21,22,23},
mB[2][3]={1,2,3,
1,2,3},
mC[2][3];
somar_mat(mA,mB,mC);
}
OBSERVE que matrizes são sempre passadas como referência.
Exercício
- Fazer uma função que recebe duas matrizes 2x3 como parâmetros e retorna a média entre todos elementos da matriz soma destas matrizes.
#include <stdio.h>
float media_soma_mat(int aA[][3],int aB[][3])
{
int i,j;
int soma_ac=0;
/*int cC[2][3];*/
for(i=0;i<2;i++){
for(j=0;j<3;j++) {
/*cC[i][j] = aA[i][j] + aB[i][j];*/
soma_ac = soma_ac +aA[i][j] + aB[i][j];
}
}
return (soma_ac/6.0);
}
void main()
{
int mA[2][3]={
11,12,13,
21,22,23},
mB[2][3]={
1,2,3,
1,2,3};
float media;
media = media_soma_mat(mA,mB);
}
- Modificar este exercício para que a função receba um parâmetro adicional do tipo inteiro. A função deve retornar a média de todos os valores da matriz soma que estão acima do valor passado como parâmetro.
Matrizes de caracteres e vetores de strings
Um vetor de strings pode ser construído usando matrizes de char. Cada string será armazenada em uma linha do vetor. Exemplo
#include <stdio.h>
main()
{
char TabelaUsuarios[4][10] = {
"joao",
"maria",
"jose",
"lara",
};
int i;
for (i=0;i<4;i++)
printf("%s\n",&TabelaUsuarios[i][0]);
}
Note a forma como é realizada a inicialização da matriz.
Exercícios
- Implementar um programa para "abrir uma porta" para um usuário que se encontra na tabela acima.
- Implementar uma tabela adicional com senhas dos usuários. O acesso deve ser concedido somente se o usuário for validado e a senha. Defina as tabelas como variáveis globais.
- Implementar uma modificação do exercício anterior que permite ao programa ficar em loop até que se entre com userID igual a "fim".
- No exercício anterior, acrescente uma tabela de contadores que permite armazenar o número de tentativas seguidas de um usuário, no caso de erro de senha. Se o número de tentativas for maior que 3 a porta não deverá mais ser aberta para o usuário (usuário bloqueado).
- No exercício anterior, acrecente a figura do administrador (armazenado separadamente como user "admin" e senha "12345". Ao logar o administrador será questionado por um usuário a ser desbloqueado. O administrador entra com o usuário a ser desbloquado e o sistema volta a perguntar por um userID.
soluções
|
ex.1
#include <stdio.h>
#include <string.h>
main()
{
char TabelaUsuarios[4][10] = {
"joao",
"maria",
"jose",
"lara",
};
int i;
char userId[50];
int userEncontrado=1;
scanf("%s",userId);
for (i=0;i<4 && userEncontrado; i++) {
if( strcmp(userId, &TabelaUsuarios[i][0])==0)
userEncontrado=0;
}
if (userEncontrado==0)
printf("Abrir porta!!!\n");
}
ex.2
Autora: Beatriz da Silveira
#include <stdio.h>
#include <string.h>
int main()
{
int i;
char userId[50];
int userEncontrado=1;
char TabelaUsuarios[5][10] = {
"joao",
"maria",
"jose",
"lara",
"fim" };
do{
printf("Entre com seu usuário");
scanf("%s", userId);
for (i=0;i<5 && userEncontrado; i++) {
if( strcmp(userId, &TabelaUsuarios[i][0])==0 && strcmp(userId,&TabelaUsuarios[4][0])==0){
userEncontrado=0;
printf("FIM Abrir porta\n");}
} }while(userEncontrado==1);
printf("O usuário digitado é : FIM");
return 0;
}
O código abaixo foi o código base para a implementação feita acima
#include <stdio.h>
#include <string.h>
main()
{
char TabelaUsuarios[4][10] = {
"joao",
"maria",
"jose",
"lara",
};
int i;
char userId[50];
int userEncontrado=1;
scanf("%s",userId);
for (i=0;i<4 && userEncontrado; i++) {
if( strcmp(userId, &TabelaUsuarios[i][0])==0)
userEncontrado=0;
}
if (userEncontrado==0)
printf("Abrir porta!!!\n");
}
ex.3
Autor: Beatriz da Silveira
#include <stdio.h>
#include <string.h>
char TabelaUsuarios[4][10] = {
"joao",
"maria",
"jose",
"lara",
};
char TabelaSenhas[4][10]={
"joaozinho",
"mari",
"josezinho",
"larinha",
};
int tratandoAutenticacao(char userId[50], char senha[50]){
int userEncontrado=1;
int senhaEncontrada=1;
int i;
int b=1;
for (i=0;i<4 && userEncontrado; i++) {
if( strcmp(userId, &TabelaUsuarios[i][0])==0)
userEncontrado=0;
}
if (userEncontrado==0){
for (i=0;i<4 && senhaEncontrada; i++) {
if( strcmp(senha, &TabelaSenhas[i][0])==0){
senhaEncontrada=0;}}
if(senhaEncontrada==0){
printf("Abrir porta!!!\n");
b=0;
}else{
printf("Senha inválida\n");
b=1;
}
}else{
printf("User inválido\n");
b=1;
}
return b;
}
int main()
{
char userId[50];
char senha[50];
int aut;
do
{printf("Digite seu userId: \n");
scanf("%s",userId);
printf("Entre com sua senha \n");
scanf("%s", senha);
aut =tratandoAutenticacao(userId, senha);}while(aut == 1);
printf("Usuário e senha corretos");
return 0;
}
|
|
AULA 15 DIA 30/09/2013
|
AULA 15 DIA 30/09/2013
Objetivos
- Definição de estruturas;
- Estruturas como parâmetros;
- Inicialização de estruturas;
- Cópia de estruturas;
- Exercícios com estruturas.
Estruturas
No C é possível criar tipos de dados que representam uma estrutura. Veja o exemplo seguinte
#include <stdio.h>
struct TUsuario /* struct TUsuario é o nome do tipo que está sendo criado */
{
char userID[20];
char senha[20];
} Usuario; /* aqui é definida uma variável do tipo struct TUsuario */
struct TUsuario TabelaUsuario[20];
main()
{
scanf("%s", Usuario.userId);
scanf("%s", Usuario.senha);
scanf("%s", TabelaUsuario[10].userID);
scanf("%s", TabelaUsuario[10].senha);
}
Neste exemplo, foi definido um tipo (modelo) para o registro (struct TUsuario) e foi criada uma variável chamada Usuario a partir deste tipo. Na sequência foi criada mais uma variável (um vetor de estruturas) chamada TabelaUsuario. Note que basta usar as palavras chave struct Usuario para criar novas variáveis. O tipo completo é definido uma única vez no início.
Estruturas dentro de estruturas
Vamos ver um exemplo com estruturas definidas dentro de estruturas:
#include <stdio.h>
struct TEndereco{
char rua[50];
char numero[10];
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
int num_filhos;
};
void main()
{
struct TCidadao Cidadao;
printf("Entre com o nome\n");
scanf ("%s",Cidadao.nome);
printf("Entre com o cpf\n");
scanf ("%s",Cidadao.cpf);
printf("Entre a rua\n");
scanf ("%s",Cidadao.endereco.rua);
printf("Entre a numero\n");
scanf ("%s",Cidadao.endereco.numero);
printf("Entre com o número de filhos\n");
scanf ("%d",&Cidadao.num_filhos);
}
Exercício 1: Faça um código adicional para imprimir o conteúdo lido na estrutura.
Iniciando structs na definição
Como toda variável, é possível dar valores para uma variável do tipo struct definida no programa:
#include <stdio.h>
struct TEndereco{
char rua[50];
char numero[10];
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
int num_filhos;
};
void main()
{
struct TCidadao Cidadao = {"Maria",
"42342342234",
{"Rua AlfaBeta","145"},
5;
};
printf("Rua do cidadao = %s\n", Cidadao.endereco.rua);
}
Passando estruturas como parâmetro e retornando estruturas
Se não for usado o operador "&" , um parâmetro que é estrutura será passado por cópia. Não apresentaremos agora a passagem por endereço pois necessita do conceita de ponteiro.
Observe o exercício abaixo.
#include <stdio.h>
struct TEndereco{
char rua[50];
char numero[10];
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
int num_filhos;
};
void print_struct (struct TCidadao aux)
{
printf("nome=%s cpf=%s\n", aux.nome, aux.cpf);
printf("endereço inicial do aux %p\n", &aux);
}
void main()
{
struct TCidadao Cidadao;
printf("Entre com o nome\n");
scanf ("%s",Cidadao.nome);
printf("Entre com o cpf\n");
scanf ("%s",Cidadao.cpf);
printf("Entre a rua\n");
scanf ("%s",Cidadao.endereco.rua);
printf("Entre a numero\n");
scanf ("%s",Cidadao.endereco.numero);
printf("Entre com o número de filhos\n");
scanf ("%d",&Cidadao.num_filhos);
print_struct(Cidadao);
printf("endereço inicial do Cidadao %p\n", &Cidadao);
}
O que podemos concluir com os endereços que foram mostrados??? Vamos a mais um exemplo.
#include <stdio.h>
struct TEndereco{
char rua[50];
char numero[10];
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
int num_filhos;
};
struct TCidadao ler_struct()
{
struct TCidadao aux;
printf("Entre com o nome\n");
scanf ("%s",aux.nome);
printf("Entre com o cpf\n");
scanf ("%s",aux.cpf);
printf("Entre a rua\n");
scanf ("%s",aux.endereco.rua);
printf("Entre a numero\n");
scanf ("%s",aux.endereco.numero);
printf("Entre com o número de filhos\n");
scanf ("%d",&aux.num_filhos);
return aux;
}
void print_struct (struct TCidadao aux)
{
printf("nome=%s cpf=%s\n", aux.nome, aux.cpf);
printf("endereço inicial do aux %p\n", &aux);
}
void main()
{
struct TCidadao Cidadao;
Cidadao = ler_struct();
print_struct(Cidadao);
printf("endereço inicial do Cidadao %p\n", &Cidadao);
}
Copiando structs
O exemplo a seguir demonstra como se pode copiar uma variável struct para outra do mesmo tipo.
#include <stdio.h>
struct THoras{
int hora;
int minuto;
int segundo;
};
struct THoras Ontem = {2,10,57};
void main()
{
struct THoras Hoje;
Hoje = Ontem;
printf("Hora hoje = %d, Minuto hoje = %d e Segundo hoje %d\n", Hoje.hora, Hoje.minuto, Hoje.segundo);
}
Aplicação no Controle de Acesso
O exemplo a seguir implementa uma parte do programa de controle de acesso usando estruturas. Neste exemplo
a tabela de usuários já vem inicializada nos campos UserID e Senha.
#include <stdio.h>
#include <string.h>
/**********************************************/
/*** PROGRAMA DE CONTROLE DE ACESSO **/
/** Autor: TurmaENG.TELECOM - 2012.2 */
/**********************************************/
/** VARIÁVEIS GLOBAIS DESTE MÓDULO ****/
struct TRegistroUsuario {
char UserId[10];
char Senha[10];
};
/* Tabela de Usuários */
struct TRegistroUsuario TabelaUsuarios[4] = {
{"joao","abcd"},
{"maria","xxxx"},
{"jose","yyyy"},
{"lara","zzzz"},
};
char userID[20];
/** FUNÇÔES DESTE MÓDULO ****/
void mostrar_menu_entrada_usuario()
{
printf("*******************************\n");
printf("Entre com o seu USERID para ter acesso\n");
printf("*******************************\n");
}
/** Função que implementa as tarefas do administrador **/
void administrar()
{
}
/** Função que valida um usuário e abre a porta **/
void tratar_usuario()
{
char senha[10];
int userEncontrado=1;
int i;
/*
Loop para encontrar o usuário na tabela.
Ao final do loop a variavel i conterá o índice do usuário (se ele estiver
na tabela
*/
for (i=0;i<4 && userEncontrado; i++) {
if( strcmp(userID, TabelaUsuarios[i].UserId)==0)
userEncontrado=0;
}
/* se usuário encontrado abre a porta */
if (userEncontrado==0) {
printf("Bom dia %s! Entre com a senha\n", userID);
scanf("%s",senha);
i--; /* o indice do sujeito é i-1 */
if(strcmp(senha,TabelaUsuarios[i].Senha)==0)
printf("Abrir porta!!!\n");
else
printf("Senha Inválida\n");
}
}
void main()
{
for(;;) {
mostrar_menu_entrada_usuario();
scanf("%s",userID);
if (strcmp(userID, "admin")==0) {
administrar();
} else {
tratar_usuario();
}
}
}
Exercício:
- Implementar um contador de acesso que permita bloquear o usuário após 3 tentativas seguidas. Note que caso o usuário acerte a senha, este contador deverá ser zerado.
- Implementar uma funcionalidade do administrador para desbloquear o usuário bloqueado.
- No programa de controle de senha inserir um campo na estrutura do usuário de forma a acomodar uma mensagem de boas vindas particularizada para cada usuário. A mensagem "DEFAULT" é Bom dia!
- Implementar na função administrar a inserção da mensagem no exercício anterior.
- Na solução acima criar uma função que procura usuário na tabela (já que este código é utilizado em mais do que um luga). A função deve receber o UserID a ser procurado e deve retornar um inteiro correspondente ao índice do usuário encontrado ou -1 se não for encontrado.
|
AULA 16 DIA 3/10/2013
|
Objetivos
- Uso do DEFINE
- Exercícios com structs e matrizes
USO de DEFINE
O define é um comando do pré-processador do C.
Observe abaixo como ele pode ser usado
#include <stdio.h>
#define PI 3.1416
main()
{
float x; /* declaração de uma variável real */
float y; /* declaração de uma variável real */
printf ("Entre com o valor de x ");
scanf("%f",&x);
y = 2*PI*x;
}
Observe que o define PI NÃO é uma variável. É simplesmente um texto que será substituído pelo número 3.1416 ANTES da compilação, em um processo que chamamos de PRÉ-COMPILAÇÃO.
Exercícios com structs
Exercício 1
Implementar uma função converte_para_polar que recebe como parâmetro um número complexo na forma retangular (representado por uma struct). A função deve retornar uma struct contendo o número complexo na forma polar.Usar as funções sqrtf e atanf da [matemáticas biblioteca matemática]. Como converter:
- ou
NOTE que as funções atanf e similares retornam em RADIANOS.
Exercício 2
Implementar um programa em C para registrar notas bimestrais de alunos de uma turma. O registro de cada aluno constará em uma tabela global RegistroAlunos. Em cada registro deve constar o nome do aluno, número de matrícula (8 dígitos), notas bimestrais. Implementar funções para: dado o nome do aluno retornar a média semestral, dado o número de matrícula retornar a média anual, dado o número de mátrícula retornar o index do aluno na tabela. Fazer um programa principal para testar as funções. #include <stdio.h>
#define NUM_ALUNOS 40
#define TAM_MAT 10
struct TRegistroAluno{
/* A FAZER */
};
struct TRegistroAluno TabelaNotasTurma[4] = {
/* INICIAR DADOS AQUI PARA NÃO PRECISAR ENTRAR PELO TECLADO */
};
float media_semestral(char nome[], int semestre)
{
/* A FAZER */
return media;
}
float media_anual(char matricula[])
{
/* A FAZER */
return media;
}
int retorna_index(char matricula[])
{
/* A FAZER */
return i;
}
main()
{
/* testar as funções aqui! */
}
Exercício 3
Implementar uma tabela global usando structs que permita representar as contas bancários de pessoas físicas. Cada registro deve conter nome, cpf, endereço e o saldo atual da conta. Contas podem estar negativas. Implementar uma função que permita:
- calcular o total contas em negativo;
- retornar o saldo de um usuário dado o seu nome;
- retornar o saldo de um usuário dado o seu CPF;
- calcular o total de dinheiro do banco em função do saldo das contas
#include <stdio.h>
#include <string.h>
#define TAM_CLIENTE 40
#define TAM_CPF 60
#define TAM_END 50
struct TRegistroCliente{
/* A FAZER */
};
struct TRegistroCliente TabelaClientes[4] = {
/* A FAZER: INICIAR COM VALORES*/
};
float saldoTotal()
{
/* A FAZER */
return saldoTotal;
}
int totalContasNegativas()
{
/* A FAZER */
return TotalContas;
}
float saldoUsuarioPorCPF(char cpf[])
{
return saldo;
}
float saldoUsuarioPorNome(char nome[])
{
return saldo;
}
int main()
{
int x;
char cpf[TAM_CPF];
while(1){
/* FAZER MENU DE OPÇÕES */
}
}
Exercícios com Matrizes
Exercício 4
Implementar uma função que recebe uma matriz de floats de dimensão 3x3 como parâmetro e retorna o determinante (um float) desta matriz.
Exercício 5
Implementar um programa que calcula a transposta de uma matriz qualquer (limitada ao tamanho de 10x10). O usuário deve fornecer as dimensões e, na sequência, a matriz. O programa deverá calcular a transposta da matriz. lembrar que onde b é um elemento da matriz transposta.
Exercício 6
Implementar um jogo da velha usando uma matriz 3x3 de inteiros. Um zero como elemento significa casa vazia. Os números 1 e 2 significam jogadas de jogadores.
int Jogo[3][3]={
0,0,0,
0,0,0,
0,0,0,
};
unsigned int jogador_vez=0; /* 0 e 1 representam os jogadores */
int jogo=1;
struct Tjogada{
int x,y;
} jogada;
void apresentar_jogo()
{
}
struct Tjogada ler_jogada()
{
}
int validar_jogada(struct Tjogada aux_jogada)
{
}
void verificar_termino(struct Tjogada aux_jogada)
{
}
void anunciar_vitoria()
{
}
main()
{
while (jogo==1) {
apresentar_jogo();
jogada=ler_jogada();
if (validar_jogada(jogada)) {
if (verificar_termino(jogada)==0) {
anunciar_vitoria();
jogo=0;
}
jogador_vez=++jogador_vez%2;
} else {
printf("Jogada inválida\n");
}
}
}
EXERCÍCIOS (nível médio)
- Implementar uma função que sorteia 10000 valores entre 1 e 6, simulando o "jogar de um dado". Armazenar o número de ocorrências de cada um dos valores em variáveis chamadas: val1, val2,...,val6. Ao final mostrar o resultado das ocorrências. Usar o comando for.
- Refazer o exercício anterior usando o comando while.
- Implementar um função que recebe um ângulo (float) e um caracter (char). Se o caracter for 'R' ou 'R' o ângulo será considerado em graus e será transformado para radianos. Se o caracter for 'G' ou 'g' será trasnformado para graus. Os ângulos sempre serão positivos e limitados a 360 graus ou 2PI radianos. A função deve retornar -1 caso haja erro nos valores passados como parâmetros.
- Implementar um programa para apoiar o projeto de instalações elétricas. O programa deve calcular a seção do fio necessário (seção de 1,5, 2.5 e 4 mm2 - ver padrões com o Jaci - ver Tabela aqui) dado o número de lâmpadas de 40, 60, 100W e de tomadas de 100W do circuito. O programa deve sugerir também o disjuntor a ser usado.
- Considere um circuito simples de uma malha com uma fonte de tensão V e duas resistências em série: R1 e R2. Elabore um programa que dado V, R1 e R2, seja determinada a potência transferida para R2.
- Faça uma modificação do exercício anterior para mostrar a curva de potência em função da variação de R2. Fixe V em 10V e R1 em 5ohms. Para facilitar o traçado da curva considere o eixo da potência na horizontal (linha) e na vertical (sentido crescente para baixo) as resistências.
#include <stdio.h>
#include <math.h>
#define RT 5 /* resistor da fonte */
#define VT 10 /* tensão da fonte */
/* INCOMPLETO - FALTA colocar unidades nos eixos */
void plotar(int x, int y)
{
int i,aux;
aux=y-1;
printf("%c",'|');
for(i=1;i<aux;i++)
printf("%c",' ');
printf("%c\n",'X');
}
void plotar_eixo_y()
{
int i;
for(i=1;i<120;i++)
printf("-");
printf(">\n");
}
main()
{
float pot, Rcarga;
int i;
plotar_eixo_y();
for(Rcarga=1;Rcarga<=40;Rcarga++) {
pot=(Rcarga/pow((RT+Rcarga),2))*powf(VT,2);
plotar(Rcarga,pot*20); /* multiplicado por 20 para se adaptar ao terminal */
}
printf("%c\n",'V');
}
EXERCÍCIOS (nível difícil)
- Implementar um programa que é uma variação do programa de sorteio e adivinhação dado em sala. O programa deve flexibilizar a variação da faixa 1 a 10, permitindo que o usuário a configure no início do programa (por exemplo, 1-100). Além disto, o programa deve mostrar o número de tentativas realizadas antes de mostrar a mensagem de Congratulations.
- Implementar uma variação do programa acima, onde o usuário também fornece no início uma margem de erro percentual para o acerto. Exemplo: suponha que a faixa de adivinhação é 1-100 e que o usuário forneça uma margem de erro de 10%. Suponha que o programa sorteie o número 30. Então serão considerados acertos quaisquer entradas entre 20 e 40.
- A corrente que passa por um capacitor é dada por , ou seja a corrente em um dado momento é dada pela taxa de variação da carga no capacitor. Suponha que a carga elétrica no tempo, nos terminais do capacitor é dada por em mC. Desenvolva um programa para calcular a corrente dado o tempo. Aproxime a derivada usando passos de tempo de 0.1, 0.01 e 0.001. (ver problema 1.2 do livro Fundamentos de circuitos elétricos de Charles Alexandre). Teste o programa calculando a corrente i para o tempo de 0.5s.
- Flexibilize o programa anterior para que o passo possa ser fornecido.
- Modifique o exercício anterior dado que . Faça um gráfico para i e q no intervalo t de [0-5s]. Use caracteres 'X' e 'Y'.
1.Implementar uma função que recebe como parâmetros dois vetores de floats cujos tamanhos são passados também como parâmetros. A função deve retornar 1 se a média dos elementos do primeiro vetor for maior que o segundo e 0 se for menor ou igual. Ver esqueleto da funçao: #include <stdio.h>
int compara_media_vetores(float vet1[], int tam1, float vet2[], int tam2)
{
/* implementar aqui */
}
main()
{
float x1[5]={2.4,,6.7,8.5,9.6,1.5};
float x2[4]={1.6,2.5,6.7,3.2};
int res;
/* exemplo de uso da função */
res = compara_media_vetores(x1,5,x2,4);
if (res==1)
printf("media de x1 maior que média de x2\n");
}
Autora: Beatriz da Silveira
Resposta da questão 1, da avaliação Lab2A
#include <stdio.h>
int compara_media_vetores(float vet1[], int tam1, float vet2[], int tam2)
{
/* implementar aqui */
int i;
float total1;
float total2;
for(i=0;i<tam1;i++)
total1 = total1 + vet1[i];
total1 = total1/tam1;
printf("valor de total1= %.2f\n", total1);
for(i=0;i<tam2;i++)
total2 = total2 + vet2[i];
total2 = total2/tam2;
printf("valor de total2=%.2f\n", total2);
if(total1>total2){
return 1;
}else{
return 0;
}
}
int main()
{
float x1[5]={2.4,6.7,8.5,9.6,1.5};
float x2[4]={1.6,2.5,6.7,3.2};
int res;
/* exemplo de uso da função */
res = compara_media_vetores(x1,5,x2,4);
if (res==1)
printf("media de x1 maior que média de x2\n");
return 0;
}
2.Implementar uma função que recebe um vetor de caracteres como parâmetro. Este vetor conterá uma string terminada em NULL. A função deve retornar o número de vogais 'a' que são precedidos pela letra b. Na função main apresentar um teste de uso da função int num_vog(char cadeia[])
{
/* implementar aqui */
}
main()
{
/* testar o uso da função aqui */
}
Explicação adicional:Para a cadeia "abacate" a função deveria retornar 1.
Autora: Beatriz da Silveira
Resposta da questão 2, avaliação Lab2A
#include <stdio.h>
int num_vog(char cadeia[])
{
int i;
int totalDeA=0;
for(i=0;cadeia[i]!= 'b'; i++)
{
if(cadeia[i]=='a')
totalDeA=totalDeA+1;
}
return totalDeA;
}
int main()
{
char x1[]={'a','b','a','c','a','t','e','\0'};
int x2 = num_vog(x1);
printf("Quantidade de A: %d\n", x2);
return 0;
}
AVALIAÇÂO LAB2B
1.Implementar um programa C (não precisa ser função) que lê valores (usando scanf) de uma matriz alfa de dimensão MxN de inteiros (onde M e N são dimensões que devem ser lidas no início). O programa deve mostrar (usando printf) quantas linhas desta matriz possuem valores entre 5 e 10 (inclusive). Exemplo:
A matriz:
12 5 3
1 4 17
13 14 10
Esta matriz possui 2 linhas com valores que estão entre 5 e 10.
2.Implementar uma função que recebe um vetor de caracteres como parâmetro. Este vetor conterá uma string terminada em NULL. A função deve retornar o número de vogais 'a' e 'i' que sejam sucedidas pela letra c. Na função main apresentar um teste de uso da função int num_vog(char cadeia[])
{
/* implementar aqui */
}
main()
{
/* testar o uso da função aqui */
}
Explicação adicional:Para a cadeia "abacate" a função deveria retornar 1.
Lista Exercícios
Autora: Beatriz da Silveira
Implementação do Jogo da Velha
#include <stdio.h>
#include <string.h>
#define linha 3
#define coluna 3
int Jogo[3][3]={
{0,0,0},
{0,0,0},
{0,0,0}
};
unsigned int jogador_vez=1; /*São os jogadores, que serão representados pelos nºs 1 e 5*/
int jogo=1;
struct Tjogada{
int x,y;
} jogada;
/*O método abaixo apresenta o tabuleiro ao jogador e caso todas as casas já estejam preenchidas
* finaliza o jogo*/
int apresentar_jogo()
{
int finaliza=9;
int i;
int j;
for(i=0;i<linha;i++)
{
for(j=0;j<coluna;j++)
{
printf("%d\t", Jogo[i][j]);
if(Jogo[i][j]!=0){
finaliza--;
}
}
printf("\n");
}return finaliza;
}
/*Armazena na struct a linha e a coluna que o jogador deseja inserir a jogada*/
struct Tjogada ler_jogada()
{
printf("Digite a linha que deseja inserir a jogada");
scanf("%d", &jogada.x);
printf("Digite a coluna que deseja inserir a jogada");
scanf("%d", &jogada.y);
return jogada;
}
/*Impede que o jogador tente jogar numa casa já preenchida, ou inserir numa
* linha ou coluna fora do escopo*/
int validar_jogada(struct Tjogada aux_jogada)
{
if(Jogo[aux_jogada.x][aux_jogada.y]==0 && aux_jogada.x<linha && aux_jogada.y<coluna){
Jogo[aux_jogada.x][aux_jogada.y]=jogador_vez;
return 0;
}
return 1;
}
int verificar_termino()
{
int i;
int j;
//Confere linha por linha;
for(i=0;i<linha;i++){
int soma=0;
for(j=0;j<coluna;j++)
{
soma = soma+Jogo[i][j];
if(soma==3 || soma == 15){
return 0;
break;
}
}
//Confere coluna por coluna;
}for(j=0;j<coluna;j++){
int soma=0;
for(i=0;i<linha;i++){
soma = soma+Jogo[i][j];
if(soma==3 || soma ==15){
return 0;
break;
}
}
}
//Confere a diagonal principal e a diagonal secundária
int diagonalPrincipal=0;
int diagonalSecundaria=0;
int l;
for(l=0 ; l < linha ; l++)
{
diagonalPrincipal = diagonalPrincipal+Jogo[l][l];
diagonalSecundaria =diagonalSecundaria+Jogo[l][linha-l-1];
if(diagonalPrincipal==3 || diagonalPrincipal==15 || diagonalSecundaria==3 || diagonalSecundaria==15){
return 0;
break;
}
}
return 1;
}
void anunciar_vitoria()
{
apresentar_jogo();
}
int main()
{
while (jogo==1) {
int velha=apresentar_jogo();
if(velha==0){
printf("Deu velha!");
exit(0);
}
jogada=ler_jogada();
if (validar_jogada(jogada)==0) {
if (verificar_termino()==0) {
anunciar_vitoria();
jogo=0;
printf("Terminou o jogo");
}
if (jogador_vez==1){
jogador_vez=5;
}else{
jogador_vez=1;
}
} else {
printf("Jogada inválida\n");
}
}
return 0;
}
Soluções:
Conteúdo extendido
|
Exercício 1
#include <math.h>
struct TComplexoRet {
float x,y;
};
struct TComplexoPolar {
float mod,ang;
};
struct TComplexoPolar convert_polar(struct TComplexoRet a)
{
struct TComplexoPolar aux;
aux.mod = sqrtf(powf(a.x,2)+powf(a.y,2));
aux.ang = atan(a.y/a.x);
return aux;
}
main()
{
struct TComplexoRet w;
struct TComplexoPolar y;
w.x = 11.5;
w.y = 4.6;
y = convert_polar(w);
}
Exercício 2
Autor: Beatriz da Silveira
#include <stdio.h>
#define NUM_ALUNOS 40
#define TAM_MAT 10
struct TRegistroAluno{
char nome[NUM_ALUNOS];
char matricula[TAM_MAT];
float bim1,bim2,bim3,bim4;
};
struct TRegistroAluno TabelaNotasTurma[4] = {
{"Lara","12222212", 10.0,10.0,9.5,7.6},
{"Joao","44434212", 7.6,4.5,7.8,9.7},
{"Maria","12234272", 8.7,9.0,9.8,9.6},
{"Jose","75656755", 4.5,6.6,5.5,8.9},
};
float media_semestral(char nome[], int semestre)
{
int i,AlunoEncontrado=1;
float media;
if (semestre!=1 && semestre!=2)
return -1;
for(i=0;i<NUM_ALUNOS && AlunoEncontrado;i++) {
if(strcmp(TabelaNotasTurma[i].nome, nome)==0)
AlunoEncontrado=0;
}
if (AlunoEncontrado==0){
i--;
if(semestre==1)
media=(TabelaNotasTurma[i].bim1 + TabelaNotasTurma[i].bim2)/2;
else
media=(TabelaNotasTurma[i].bim3 + TabelaNotasTurma[i].bim4)/2;
}
return media;
}
float media_anual(char matricula[])
{
int i,AlunoEncontrado=1;
float media;
for(i=0;i<NUM_ALUNOS && AlunoEncontrado;i++) {
if(strcmp(TabelaNotasTurma[i].matricula, matricula)==0)
AlunoEncontrado=0;
}
if (AlunoEncontrado==0){
i--;
media = (TabelaNotasTurma[i].bim1 + TabelaNotasTurma[i].bim2+
TabelaNotasTurma[i].bim3 + TabelaNotasTurma[i].bim4)/4;
}
return media;
}
int retorna_index(char matricula[])
{
int i,AlunoEncontrado=1;
for(i=0;i<NUM_ALUNOS && AlunoEncontrado;i++) {
if(strcmp(TabelaNotasTurma[i].matricula, matricula)==0)
AlunoEncontrado=0;
}
if (AlunoEncontrado==0)
i--;
else
i=-1;
return i;
}
void oQueDesejaFazer(int num){
char mat[TAM_MAT];
int semestre;
switch (num)
{
case 1:
printf ("Digite o nome do aluno para saber a média Semestral\n");
char aluno[NUM_ALUNOS];
scanf("%s", aluno);
//int semestre;
printf("Digite 1 0u 2 para saber a nota semestral");
scanf("%d", &semestre);
float media = media_semestral(aluno, semestre);
printf("A média semestral é: %.2f \n", media);
break;
case 2:
printf ("Digite o número da matrícula do aluno, para saber a média anual");
//char mat[TAM_MAT];
scanf("%s", mat);
float mediaAnual= media_anual(mat);
printf("A média anual do aluno é: %.2f\n", mediaAnual);
break;
case 3:
printf ("Digite o nº da matricula para saber o index do aluno\n");
//char mat[TAM_MAT];
scanf("%s", mat);
int index =retorna_index(mat);
printf("O index do aluno é %d", index);
break;
default:
printf ("Você não escolheu nenhuma das opções listadas.\n");
}
}
int main()
{
int opcao=0;
while(1){
printf("O que você deseja fazer\n 1-Para saber média semestral\n 2-Para saber média anual\n 3-Index do aluno na tabela\n 4-Para sair");
scanf("%d", &opcao);
if(opcao==4){
printf("Saindoo");
break;
}
oQueDesejaFazer(opcao);
}
return(0);
}
Exercício 3
Autora: Beatriz da Silveira
Resolução da questão 3, Avaliação Lab3A
#include <stdio.h>
#include <string.h>
#define TAM_CLIENTE 40
#define TAM_CPF 60
#define TAM_END 50
struct TRegistroCliente{
char nome[TAM_CLIENTE];
char cpf[TAM_CPF];
char endereco[TAM_END];
float saldo;
};
struct TRegistroCliente TabelaClientes[4] = {
{"Lara","12222212", "kobrasol", 192.97},
{"Joao","44434212", "Campinas", -69.0},
{"Maria","12234272", "Barreiros", 45.90},
{"Jose","75656755", "Centro", 75.00},
};
float totalDinheiro(){
int i;
float saldoTotal=0;
for(i=0; i<4; i++)
{
saldoTotal=TabelaClientes[i].saldo+saldoTotal;
}
return saldoTotal;
}
float saldoUsuario(char cpf[]){
int i,userEncontrado=1;
float saldo;
for(i=0;i<4 && userEncontrado;i++) {
if(strcmp(TabelaClientes[i].cpf, cpf)==0)
userEncontrado=0;
}
if (userEncontrado==0){
i--;
saldo = (TabelaClientes[i].saldo);
}
return saldo;
}
int main(){
int x;
char cpf[TAM_CPF];
while(1){
printf("----------------------------------------------------\n");
printf("Escolha a operação que você deseja realizar\n");
printf("1-saldo total do banco\n2-Saldo do usuário\n");
scanf("%d", &x);
printf("----------------------------------------------------\n");
if(x==1){
printf("O saldo total do banco é de: %.2f\n", totalDinheiro());
}if(x==2){
printf("Insira seu cpf digitando somente numeros\n");
scanf("%s",cpf);
printf("Saldo total do banco é de: %.2f\n", saldoUsuario(cpf));
}
}}
|
|
AULA 17 DIA 7/10/2013
|
AULA 17 DIA 7/10/2013
Objetivos
Referências
[2]
[3]
[4]
[5]
Ponteiros
A memória de um computador pode ser vista como um vetor de bytes.
Cada byte possui um endereço. O tamanho da memória é definido pelo tamanho do barramento de endereços usado para acessá-la.
Uma variável ocupa uma área da memória. Tipicamente uma variável to tipo char se utiliza de um byte. Já uma variável do tipo int pode (dependendo do sistema) usar 4 bytes contíguos.
Uma variável possui um endereço e um conteúdo (dados).
Uma variável ponteiro tem como conteúdo um endereço. Portanto a variável ponteiro possui um endereço e contém um endereço como conteúdo.
Ponteiro para inteiro
Observe o programa abaixo. A variável p é um ponteiro para inteiro. Isto significa que ela pode armazenar um endereço
de um inteiro.
#include <stdio.h>
main()
{
int x;
int *p;
x=5;
printf("Valor de x antes = %d\n", x);
p = &x;
*p=10;
printf("Valor de x depois = %d\n", x);
printf("Valor de p = %p\n", p);
}
Observe que para se referenciar o conteúdo da posição de memória apontada por p deve-se usar o asterisco: *p
EXERCÍCIO 1: Considere o programa:
main()
{
int x=10;
int y, *p, *w;
}
Faça um código para copiar o conteúdo de x para y, sem que estas variáveis apareçam no lado esquerdo de um sinal de atribuição.
EXERCÍCIO 2: Tente inferir qual seria o valor da variável y no final do programa abaixo.
main()
{
int x,y,w,*p1,*p2;
x = 20;
w = 30;
p1 = &x;
p2 = &w;
y = *p1 + *p2;
}
EXERCÍCIO 3: Tente inferir qual seria o valor da variável y no final do programa abaixo.
main()
{
int x,y,w,*p1,*p2, *p3;
x = 20;
w = 30;
p1 = &x;
p2 = &w;
y = *p1 + w;
p3 = &y;
*p3 = *p3 + 10;
y = *p1 + *p2 + *p3;
}
Ponteiro para char
Os ponteiro para char são muito utilizados pois permitem apontar para strings. A ideia é que ele aponte para o primeiro caracter da string. Veja o exemplo abaixo.
#include <stdio.h>
main()
{
char x[10]="ifsc";
char *p;
p = &x[2];
printf("x[2] = %c\n", *p);
p = x;
printf("string %s\n", p);
while (*p!=0) {
printf("Endereco %p conteúdo %c\n", p,*p);
p++;
}
}
Neste foi usado o incremento de um ponteiro, o que implica em adicionar ao endereço armazenado em p uma quantidade relativa ao tamanho do tipo apontado.
No caso é 1 (tamanho de um char é um byte).
EXERCÍCIO: Sem executar o programa abaixo, determine o valor de y no final do programa:
main()
{
char x[10]="ifsc";
char *p, y;
p = x + 2;
y= *p;
}
Apontando para um vetor de inteiros
Da mesma forma que usamos um ponteiro para char para apontar uma string, podemos fazer um ponteiro para int apontar para para um elemento de um vetor de inteiros.
#include <stdio.h>
main()
{
int x[10]= {0,1,2,3,4,5,6,7,8,9};
int *p;
int i;
p = x;
i=0;
while (i<10) {
printf(" endereco %p e conteudo %d\n", p, *p);
p++;
i++;
}
}
OBSERVE que p++ incrementa em 4 unidades.
|
AULA 18 DIA 9/10/2013
|
AULA 18 DIA 9/10/2013
Objetivos
Exercício modelo para uma das questões da avaliação
Seja uma matriz 10x4, onde nas duas primeiras colunas (0 e 1) estão armazenados por linha um número complexo no formato retangular. Computar as duas colunas finais onde na coluna 2 estão o modulo e na 3 o ângulo.
Esqueleto:
#define LINHA 10
#define COLUNA 4
float Matriz[LINHA][COLUNA] = {
{5.8,9.7},
{6.7,12.5},
{3.8,20.7},
{6.7,12.5},
{5.8,12.4},
{6.7,18.5},
{15.8,39.7},
{6.7,12.5},
{55.8,9.7},
{66.8,40.9}
};
float CalculaModulo(float parx, pary)
{
float modulo;
/* a fazer */
return modulo;
}
float CalculaAngulo(float parx, pary)
{
float angulo;
/* a fazer */
return angulo;
}
main()
{
int i;
for (i=0;i<LINHA;i++) {
Matriz[i][2] = CalculaModulo(Matriz[i][0], Matriz[i][1]);
Matriz[i][3] = CalculaAngulo(Matriz[i][0], Matriz[i][1]);
}
}
Exercício modelo para uma das questões da avaliação
Exercício 16 AULA 8
Exercício modelo para uma das questões da avaliação
AULA 10 - Exercício 2
|
AULA 20 DIA 14/10/2013
|
AULA 20 DIA 14/10/2013
Objetivos
- uso de ponteiros para passar parâmetros;
- vetor de ponteiros;
- argc e argv
Usando ponteiro na passagem de parâmetros
Observe como podemos usar ponteiros na passagem de parâmetros.
#include <stdio.h>
void str_cpy(char *pdest, char *pfonte)
{
while (*pfonte!=0) {
*pdest++ = *pfonte++;
}
*pdest = 0;
}
int str_len (char *p)
{
int i=0;
while (*p++!=0)
i++;
return i;
}
main()
{
char fonte[10]="ifsc";
char destino[10];
str_cpy(destino, fonte);
printf("string destino = %s\n", destino);
printf("tamanho de dest = %d\n", str_len(destino));
}
Um ponto interessante é que ponteiros permitem, na chamada de uma função, passar valores por referência:
void alfa(int *p)
{
*p=10;
}
main()
{
int x;
x =5;
printf("Valor de x antes da chamada de alfa = %d\n", x);
alfa(&x);
printf("Valor de x depois da chamada de alfa = %d\n", x);
}
EXERCíCIO 1: Implementar a função str_cat que concatena duas strings usando ponteiros.
Vetor de ponteiros
Como visto em aulas anteiriores, variáveis ponteiros possuem como conteúdo um endereço.
É perfeitamente possível construir vetores e matrizes de ponteiros. Por exemplo:
#include <stdio.h>
int main()
{
int i;
char *vp[4];
char alfa[5]="IFSC";
char beta[5]="TELE";
char delta[5]="RAC";
char gamma[5]="CGER";
vp[0] = alfa;
vp[1] = beta;
vp[2] = delta;
vp[3] = gamma;
for(i=0;i<4;i++)
printf("%s\n", vp[i]);
}
Observe que vp é um vetor de ponteiros para char e cada elemento aponta para uma cadeia de caracteres.
Argumentos de linha de comando
Um bom exemplo de vetor de ponteiros é a passagem de parâmetros na linha de comando. Cada parâmetro é tratado como uma cadeia de caracteres apontada por um elemento do vetor argv. O número de parâmetros é passado em argc. Note que argv[0] aponta para uma string que inidica o nome do programa.
Exemplo: Considere o programa abaixo:
#include <stdio.h>
main(int argc, char *argv[])
{
int i;
for (i=0;i<argc;i++) {
printf("%s\n", argv[i]);
}
printf("Numero de parametros passados = %d\n", argc-1); /* o primeiro é o nome do arquivo executavél" */
}
EXERCÍCIO 2: Implementar um programa chamado cmpcadeia que testa se duas strings passadas na linha de comando são iguais. O programa deve imprimir uma mensagem indicando se são iguais ou diferentes. Usar a função strcmp da biblioteca. Caso sejam passados mais ou menos que dois parâmetros o programa deve se encerrar mostrando uma indicão do tipo:
cmpcadeia: dois parametros devem ser passados
EXERCÍCIO 3: Renomeie o executável e veja seja a mensagem de erro mostra o nome correto do programa.
Ponteiros para qualquer coisa
Podemos criar ponteiros para apontar para qualquer objeto na memória.
Por exemplo, podemos apontar para variáveis do tipo float, double etc.
main()
{
float a, *p;
p=&a;
*p= 5.5;
}
Exercício 4
Implememtar um programa que recebe 3 parâmetros na linha de comando: dois números reais e um operador (char). Operador pode ser + ou menos. O programa deve mostrar o resultado da operação. Exemplo:
calcula 3.5 + 2.6
OBS: usar a função atof para converter string em float.
Soluções
|
Exercício 1
Implementação feita pela monitora de programação
#include <stdio.h>
void str_cpy(char *pdest, char *pfonte)
{
while (*pfonte!=0) {
*pdest++ = *pfonte++;
}
*pdest = 0;
}
void str_cat(char *destino, char *origem){
while(*destino!=0){
destino++;
}
while(*origem!=0){
*destino++=*origem++;
}
*destino=0;
}
int str_len (char *p)
{
int i=0;
while (*p++!=0)
i++;
return i;
}
main()
{
char fonte[10]="ifsc";
char destino[10];
str_cpy(destino, fonte);
printf("string destino = %s\n", destino);
str_cat(destino, "alunos");
printf("string destino = %s\n", destino);
printf("tamanho de dest = %d\n", str_len(destino));
}
EXERCÍCIO 2
Código implementado pela monitora de programação
#include <stdio.h>
main(int argc, char *argv[])
{
int comp;
if((argc-1)>2){
printf("cmpcadeia: dois parametros devem ser passados\n");
}else{
comp=strcmp(argv[1],argv[2]);
if(comp==0){
printf("palavras iguais\n");
}else{
printf("palavras diferentes\n");
}
}
}
|
|
AULA 21 DIA 16/10/2013
|
AULA 21 DIA 16/10/2013
Objetivos
- vetor de ponteiros;
- argc e argv
Vetor de ponteiros
Como visto em aulas anteriores, variáveis ponteiros possuem como conteúdo um endereço.
É perfeitamente possível construir vetores e matrizes de ponteiros. Por exemplo:
#include <stdio.h>
int main()
{
int i;
char *vp[4];
char alfa[5]="IFSC";
char beta[5]="TELE";
char delta[5]="RAC";
char gamma[5]="CGER";
vp[0] = alfa;
vp[1] = beta;
vp[2] = delta;
vp[3] = gamma;
for(i=0;i<4;i++)
printf("%s\n", vp[i]);
}
Observe que vp é um vetor de ponteiros para char e cada elemento aponta para uma cadeia de caracteres.
Argumentos de linha de comando
Um bom exemplo de vetor de ponteiros é a passagem de parâmetros na linha de comando. Cada parâmetro é tratado como uma cadeia de caracteres apontada por um elemento do vetor argv. O número de parâmetros é passado em argc. Note que argv[0] aponta para uma string que inidica o nome do programa.
Exemplo: Considere o programa abaixo:
#include <stdio.h>
main(int argc, char *argv[])
{
int i;
for (i=0;i<argc;i++) {
printf("%s\n", argv[i]);
}
printf("Numero de parametros passados = %d\n", argc-1); /* o primeiro é o nome do arquivo executavél" */
}
EXERCÍCIO 2: Implementar um programa chamado cmpcadeia que testa se duas strings passadas na linha de comando são iguais. O programa deve imprimir uma mensagem indicando se são iguais ou diferentes. Usar a função strcmp da biblioteca. Caso sejam passados mais ou menos que dois parâmetros o programa deve se encerrar mostrando uma indicação conforme abaixo:
$ cmpcadeia alfa beta gama delta
$ cmpcadeia: dois parâmetros devem ser passados
$ cmpcadeia alfa beta
$ cmpcadeia: as strings são diferentes
$ cmpcadeia alfa alfa
$ cmpcadeia: as strings são iguais
EXERCÍCIO 3: Renomeie o executável e veja seja a mensagem de erro mostra o nome correto do programa.
Exercício 4
Implememtar um programa que recebe 3 parâmetros na linha de comando: dois números reais e um operador (char). Operador pode ser '+' ou '-'. O programa deve mostrar o resultado da operação. Exemplo:
calcula 3.5 + 2.6
OBS: usar a função atof para converter string em float.
Ponteiros para qualquer coisa
Podemos criar ponteiros para apontar para qualquer objeto na memória.
Por exemplo, podemos apontar para variáveis do tipo float, double etc.
main()
{
float a, *p;
p=&a;
*p= 5.5;
}
Soluções
|
Exercício 1
Implementação feita pela monitora de programação
#include <stdio.h>
void str_cpy(char *pdest, char *pfonte)
{
while (*pfonte!=0) {
*pdest++ = *pfonte++;
}
*pdest = 0;
}
void str_cat(char *destino, char *origem){
while(*destino!=0){
destino++;
}
while(*origem!=0){
*destino++=*origem++;
}
*destino=0;
}
int str_len (char *p)
{
int i=0;
while (*p++!=0)
i++;
return i;
}
main()
{
char fonte[10]="ifsc";
char destino[10];
str_cpy(destino, fonte);
printf("string destino = %s\n", destino);
str_cat(destino, "alunos");
printf("string destino = %s\n", destino);
printf("tamanho de dest = %d\n", str_len(destino));
}
EXERCÍCIO 2
Código implementado pela monitora de programação
#include <stdio.h>
main(int argc, char *argv[])
{
int comp;
if((argc-1)>2){
printf("cmpcadeia: dois parametros devem ser passados\n");
}else{
comp=strcmp(argv[1],argv[2]);
if(comp==0){
printf("palavras iguais\n");
}else{
printf("palavras diferentes\n");
}
}
}
|
|
AULA 22 DIA 17/10/2013
|
AULA 22 DIA 17/10/2013
Objetivos
- comando switch e aplicação na construção do menu de opções do controle de acesso;
- Ponteiros (continuação);
Comando switch
O comando switch permite controlar o fluxo de um programa de forma condicional. O comando testa uma expressão que deve
resultar em um número inteiro. Uma sequência de cláusulas case permite executar uma sequência de instruções conforme o valor da expressão. Note que esta sequência de instruções pode ser interrompida por um break.
main () {
{
int opcao;
printf("Entre com uma opção (número inteiro)\n");
scanf ("%d",&opcao);
switch(opcao) {
case 1:
printf("opcao 1\n");
break; /* o break força o encerramento da instrução*/
case 2:
printf("opcao 2\n");
x++; /* instrução demonstrativa apenas */
printf("Note que pode existir ums lista de instruções");
break;
case 3:
printf("opcao 3\n"); /* note o efeito de não ter o break */
case 4:
printf("opcao 4\n");
break;
case 5:
printf("opcao 5\n");
break;
default:
printf("opcao default\n");
break; /* a opção default é opcional */
}
Exercício 1
Implementar um programa de calculadora similar ao da aula passada, onde dois operandos e um operador são fornecidos na linha de comando. O operador pode ser uma das 4 operações básicas. Usar um switch para testar o operador e implementar a operação.
Continuação de Ponteiros
Apontando para estruturas
Ponteiros podem apontar para qualquer "objeto" de qualquer tipo. Vamos verificar como é possível apontar para uma estrutura:
#include <stdio.h>
#include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4] = {
{"joao",18,},
{"maria",18,},
{"jose",19,},
{"lara",17,},
}
;
struct TRegistro *p;
main()
{
p = &Tabela[3]; /*p aponta para o registro 3 da tabela */
printf("O nome na posição 3 é %s e idade = %d\n", p->nome,p->idade);
}
Note que o uso de p->nome é uma alternativa ao uso de (*p).nome
No primeiro caso pode-se ler: o campo nome do objeto que é apontado por p.
Retornando uma estrutura em uma função
#include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4] = {
{"joao",18,},
{"maria",18,},
{"jose",19,},
{"lara",17,},
}
;
struct TRegistro *p;
struct TRegistro * RetornarStruct(int indice)
{
return &Tabela[indice];
}
main()
{
p = RetornarStruct(2); /*p aponta para o registro 3 da tabela */
printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
}
Passando uma estrutura como parâmetro
#include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4] = {
{"joao",18,},
{"maria",18,},
{"jose",19,},
{"lara",17,},
};
struct TRegistro *p;
void MudarStruct(struct TRegistro *p1, int indice)
{
Tabela[indice] = *p1;
}
main()
{
struct TRegistro aux = {"luisa",16};
MudarStruct(&aux,2);
p = &Tabela[2];
printf("O nome na posição 2 é %s e idade = %d\n", p->nome,p->idade);
}
Múltiplas indireções
#include <stdio.h>
void main()
{
int a, *b, **c, ***d;
a = 3;
b = &a;
c = &b;
d = &c;
printf("Valor de a = %d\n",***d);
}
Soluções
|
Exercício 1
Implementação feita pela monitora de programação
#include <stdio.h>
main () {
char opcao;
int opr1;
int opr2;
int result;
printf("Entre com a operação desejada\n");
opcao=getchar();
printf("Entre com o operando\n");
scanf("%d",&opr1);
printf("Entre com outro operando\n");
scanf("%d",&opr2);
switch(opcao) {
case '+':
result=opr1+opr2;
printf("Soma: %d\n",result);
break; /* o break força o encerramento da instrução*/
case '-':
result=opr1-opr2;
printf("Subtração: %d\n",result);
break;
case '*':
result=opr1*opr2;
printf("Multiplicação: %d\n",result);
break;
case '/':
result=opr1/opr2;
printf("Divisão: %d\n",result);
break;
default:
printf("Nenhuma das operações é válida!\n");
break;
}
}
|
|
AULA 23 DIA 21/10/2013
|
AULA 23 DIA 21/10/2013
Objetivos
- exercícios ponteiros;
- anatomia de um programa em execução;
- alocação dinâmica de memória.
Exercícios
Exercício 1
Implementar uma função que recebe dois números complexos no formato retangular e retorna a soma dos mesmos no no formato polar. Os parâmetros são passados por referência (usar ponteiro). Demonstre o funcionamento no programa main.
struct TComplexoRet {
float x;
float y;
};
struct TComplexoPolar {
float mod;
float ang;
};
struct TComplexoPolar conv_polar(struct TComplexoRet *p1, struct TComplexoRet *p2)
{
}
obs: é possível retornar um ponteiro para uma estrutura??
Exercício 2
Implemente uma função usando ponteiros que recebe duas strings como parâmetro e retorna o número de vezes que a segunda string está contida na primeira. Use a função da biblioteca strcmp() como apoio.
#include <string.h>
int conta_sub_strings(char *p1, char *p2)
{
}
Exercício 3
Seja um vetor de inteiros x definido globalmente. Implemente uma função para ler dados para este vetor, dado o endereço inicial do mesmo e o tamanho. Imprima na função main o vetor. Usar ponteiros no acesso ao vetor.
int x[10];
void ler_vetor(int *p, int tamanho)
{
}
Exercício 4
Implemente uma função que recebe uma string como parâmetro. Esta string contém um número inteiro. A função deve retornar um inteiro no formato int. Obs: é uma reimplementação do atoi da biblioteca.
A área de heap e o layout de memória de um programa C
Neste link podemos ter uma ideia da anatomia de um programa na memória do computador.
http://shivacherukuri.blogspot.com.br/2011/03/memory-layout-in-cdata-segmentbss-code.html
Podemos observar que existe as seguintes áreas:
- TEXT: área onde está o código;
- BSS: dados estaticamente alocados e não inicializados;
- DATA: dados estaticamente alocados e inicializados;
- STACK: área de pilha (variáveis locais);
- HEAP: área de dados alocados dinamicamente.
Quando declaramos uma variável global da forma:
int x;
a variável x é alocada em uma área chamada BSS (dados não incializados). Note
que x possui uma área de memória reservada a ela (4 bytes) e cuja existência é
o tempo de vida do programa em execução.
Da mesma forma, uma variável global da forma:
int y=10;
é alocada na área de DATA. A inicialização é definida normalmente na carga do programa. Os valores de inicialização são copiados para a área de DATA na carga do programa, a partir do arquivo executável.
Por vezes, o tamanho dos dados não são conhecidos antes da execução do programa.
Neste caso, pode ser interessante criá-los dinamicamente e é neste ponto que
entra a área de HEAP. Trata-se de uma área de memória, gerenciada a partir de funções da biblioteca do C.
As funções mais conhecidas são (http://en.wikipedia.org/wiki/C_dynamic_memory_allocation):
http://www.linuxjournal.com/article/4681
http://www.cs.cmu.edu/~guna/15-123S11/Lectures/Lecture08.pdf
Exemplo 1: Alocação dinâmica de números inteiros (exercício puramente didático):
#include <stlib.h>
main()
{
int *px, *py;
int resultado;
px = (int *) malloc(sizeof(int));
*px = 5;
py = (int *) malloc(sizeof(int));
*py = 2;
resultado = *px + *py;
free (px);
px = NULL;
free (py);
py = NULL;
}
Alocando uma estrutura
#include <stdio.h>
#include <stdlib.h>
void main()
{
struct TTeste{
int x;
int y;
};
struct TTeste *teste;
teste = (struct TTeste *) malloc (sizeof(struct TTeste));
if (teste==NULL) {
printf("erro de alocação");
exit(1);
}
teste->x=10;
free(teste);
teste=NULL;
}
Alocando dinamicamente uma tabela de estruturas
#include <stdio.h>
#include <stdlib.h>
void main()
{
struct TTeste{
int x;
int y;
} *teste;
if ((teste = (struct TTeste *) malloc (100*sizeof(struct TTeste)))==NULL) {
printf("erro de alocação");
exit(1);
}
teste[10].x= 5;
if ((teste = realloc(teste, 10000*sizeof(struct TTeste)))==NULL) {
printf("erro de alocação");
exit(1);
}
teste[9000].x=20;
free(teste);
}
Exercício:
Refazer o exemplo anterior para que a quantidade de memória a ser alocada pela tabela seja passada na linha de comando.
|
AULA 24 DIA 24/10/2013
|
AULA 24 DIA 24/10/2013
Exercícios
|
AULA 25 DIA 31/10/2013
|
AULA 25 DIA 31/10/2013
Exercícios de Ponteiros
Exercício 1 - Verificar qual o erro do código abaixo.
main()
{
struct TTeste {
int x;
int y;
} *teste;
t->y = 5;
}
O código correto está mostrado abaixo:
#include <stdio.h>
void main()
{
struct TTeste {
int x;
int y;
}teste;
teste.y = 5;
printf("Valor de y %d\n",teste.y);
}
Exercício 2 - Qual o valor a ser impresso no printf do programa abaixo.
#include <stdio.h>
struct TTeste {
int x;
int y;
struct TTeste *p;
};
main()
{
struct TTeste x = {4,7,NULL};
struct TTeste y = {10,8,NULL};
struct TTeste *teste;
x.p = &y;
teste = &x;
printf ("Valor = %d\n", teste->p->x);
}
Exercício 3
Considere uma tabela de usuários de um sistema de controle de acesso conforme código abaixo. Implemente as funções abaixo usando alocação dinâmica de memória. Observe que as entradas livres deve estar iniciadas com NULL.
#include <stdio.h>
#define TAM_MAX 10
struct TEndereco {
char *pRuaNumero;
char *pCidade;
char *pEstado;
};
struct TUsuario /* struct TUsuario é o nome do tipo que está sendo criado */
{
char *userID;
char *senha;
struct TEndereco *pEndereco;
} TabelaUsuarios[TAM_MAX]={
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
};
/* adiciona usuario - retorna 0 se tudo ok e -1 se não conseguiu alocar */
/* ler dados com scanf */
int add_user()
{
}
/* remove o usuário - perguntar com scanf */
void del_user()
{
}
main()
{
/* exemplo de uso */
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TAM_MAX 10
struct TEndereco {
char *pRuaNumero;
char *pCidade;
char *pEstado;
};
struct TUsuario /* struct TUsuario é o nome do tipo que está sendo criado */
{
char *userID;
char *senha;
struct TEndereco *pEndereco;
} TabelaUsuarios[TAM_MAX]={
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
{NULL,NULL,NULL},
};
/* adiciona usuario - retorna 0 se tudo ok e -1 se não conseguiu alocar */
/* ler dados com scanf */
int add_user()
{
char aux[100];
int i;
/* procurar a primeira entrada livre na tabela */
for(i=0;i<TAM_MAX;i++) {
if (TabelaUsuarios[i].userID==NULL)
break;
}
if (i==TAM_MAX)
return -1;
/* ler userid para variavel auxiliar */
printf("Entre com o userID\n");
scanf("%s", aux);
if( (TabelaUsuarios[i].userID = (char *) malloc(strlen(aux)+1)) == NULL) {
return -1;
}
strcpy(TabelaUsuarios[i].userID, aux);
printf("Entre com o senha\n");
scanf("%s", aux);
if((TabelaUsuarios[i].senha = (char *) malloc(strlen(aux)+1)) == NULL) {
free(TabelaUsuarios[i].userID);
TabelaUsuarios[i].userID = NULL;
return -1;
}
strcpy(TabelaUsuarios[i].senha, aux);
if((TabelaUsuarios[i].pEndereco = (struct TEndereco *) malloc(sizeof(struct TEndereco))) == NULL) {
free(TabelaUsuarios[i].userID);
free(TabelaUsuarios[i].senha);
TabelaUsuarios[i].userID = NULL;
TabelaUsuarios[i].senha = NULL;
return -1;
}
printf("Entre com a rua\n");
scanf("%s", aux);
if( (TabelaUsuarios[i].pEndereco->pRuaNumero = (char *) malloc(strlen(aux)+1)) == NULL) {
free(TabelaUsuarios[i].userID);
free(TabelaUsuarios[i].senha);
TabelaUsuarios[i].userID = NULL;
TabelaUsuarios[i].senha = NULL;
free(TabelaUsuarios[i].pEndereco);
TabelaUsuarios[i].pEndereco = NULL;
return -1;
}
strcpy(TabelaUsuarios[i].pEndereco->pRuaNumero, aux);
return 0;
}
/* remove o usuário - perguntar com scanf */
void del_user()
{
}
main()
{
/* exemplo de uso */
}
Listas Ligadas
Por vezes não se conhece o tamanho dos dados que se vai manipular e o uso de uma lista pode ser conveniente para
armazená-los. Um sistema de estoque de produtos, por exemplo, poderia ser armazenado na forma de uma lista.
O exercício a seguir explora esta estrutura.
Exercício de lista ligada
#include <stdlib.h>
#include <stdio.h>
/*========================*/
/** OPERAÇÔES COM LISTA LIGADA ******/
/*========================*/
/*
tipos e variáveis globais
*/
struct TProduto{
int codigo;
struct TProduto *next;
} *head, *tail;
/*
adiciona item a cabeça da lista
retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_head(int codigo)
{
}
/*
adiciona item ao final lista
retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_tail(int codigo)
{
struct TProduto *p = malloc (sizeof(struct TProduto));
if (!p)
return -1;
p->codigo = codigo;
p->next = NULL;
if (tail==NULL) {
/* lista vazia */
tail = head = p;
}else {
/*lista não vazia */
tail->next = p;
tail = p;
}
return 0;
}
/*
imprimir lista
*/
void print_list()
{
}
main()
{
int i;
head = tail = NULL;
print_list ();
for (i=0;i<5;i++)
add_nodo_tail(i);
print_list ();
}
Lição para casa
- Implementar a função add_node_head()
- Implementar a função print_list
- Implementar a função delete_node(int codigo)
Autora: Beatriz da Silveira
No código abaixo foi implementado as funções: add_node_head(), print_list, delete_node(int codigo)
#include <stdlib.h>
#include <stdio.h>
/*========================*/
/** OPERAÇÔES COM LISTA LIGADA ******/
/*========================*/
/*
tipos e variáveis globais
*/
struct TProduto{
int codigo;
struct TProduto *next;
} *head, *tail;
/*
adiciona item a cabeça da lista
retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_head(int codigo)
{
struct TProduto *p = malloc (sizeof(struct TProduto));
if (!p)
return -1;
p->codigo = codigo;
p->next = head;
if (head==NULL) {
/* lista vazia */
head = tail = p;
}else{
head=p;
}
return 0;
}
/*
adiciona item ao final lista
retorna 0 se tudo ok e -1 se erro
*/
int add_nodo_tail(int codigo)
{
struct TProduto *p = malloc (sizeof(struct TProduto));
if (!p)
return -1;
p->codigo = codigo;
p->next = NULL;
if (tail==NULL) {
/* lista vazia */
tail = head = p;
}else {
/*lista não vazia */
for(tail=head; tail->next !=NULL; tail=tail->next);
tail->next = p;
// tail = p;
}
return 0;
}
int delete_node(int codigo){
struct TProduto *anterior=NULL;
struct TProduto *p=NULL;
//Caso o código seja o primeiro da lista entrará no if, caso seja do meio ou do final entrará no else
if(head->codigo==codigo){
head=head->next;
free(p);
}else{
for(p=head;p->codigo!=codigo;p=p->next){
anterior=p;
}
anterior->next=p->next;
free(p);
}
return 0;
}
/*
imprimir lista
*/
void print_list( struct TProduto *inicio)
{
struct TProduto *p;
for (p = inicio; p != NULL; p = p->next)
printf( "Imprimindo estrutura: %d\n", p->codigo);
}
int main()
{
int i;
head = tail = NULL;
// print_list (head);
//Inserindo códigos no final da lista
for (i=0;i<5;i++)
add_nodo_tail(i);
//Inserindo códigos no início da lista
for(i=6;i<9;i++)
add_nodo_head(i);
print_list (head);
printf("-----------------\n");
//Deletando o código de número 8
delete_node(8);
print_list(head);
return 0;
}
|
AULA 25 DIA 31/10/2013
|
AULA 26 DIA 4/11/2013
Objetivos
- Divisão em duplas;
- Início de trabalho de final de disciplina.
Exercício 1
Considere um sistema de controle de ar condicionado que permite controlar salas de uma instituição.
Elaborar uma estrutura para servir como base de uma lista ligada capaz de representar estas salas.
Uma sala possui um nome e uma temperatura que deve ser controlada. Um administrador deve ter as seguintes funções:
- inserir nova sala;
- editar sala;
- remover sala;
- modificar temperatura da sala;
Duas salas não podem ter o mesmo nome. As temperaturas controladas somente podem estar na faixa de 0 a 30 graus.
Faça um programa que implemente estas funções.
#include <stdlib.h>
#include <stdio.h>
struct TSala {
char nome[30];
float temperatura;
struct TSala *next;
};
struct TSala *head=NULL,
*tail=NULL;
int adiciona_sala()
{
struct TSala *p;
p = (struct TSala *) malloc (sizeof(struct TSala));
if(!p)
return -1;
printf("Entre com o nome da sala\n");
scanf("%s",p->nome);
printf("Entre com a temperatura\n");
scanf("%f",&p->temperatura);
p->next = NULL;
if(head==NULL) {
head = tail = p;
} else {
tail->next = p;
tail = p;
}
return 0;
}
void imprime_lista()
{
struct TSala *p;
for(p=head;p!=NULL;p=p->next) {
printf("Sala %s\n", p->nome);
printf("Temperatura %f\n", p->temperatura);
}
}
main()
{
adiciona_sala();
adiciona_sala();
adiciona_sala();
imprime_lista();
}
|
AULA 26 DIA 7/11/2013
|
//Questão 1
//Considere a tabela abaixo. Implemente uma função para retornar a média das notas do semestre 1 de todos os alunos da turma.
#include <stdio.h>
struct TNotas{
float semestre1;
float semestre2;
};
struct TRegistroAluno{
char nome[30];
struct TNotas notas;
}TabelaTurma[6] = {
"joao",{4.5,8.9},
"maria",{5.5,8.3},
"jose",{3.5,8.6},
"lara",{6.5,3.9},
"luisa",{7.5,7.9},
"beatriz",{8.5,9.9},
};
float MediaSemestre1()
{
/* implementar aqui */
}
main()
{
/* testar aqui */
}
Implementação da função MediaSemestre1 feita pela monitora Beatriz
Uma possível solução para a questão, implementado abaixo.
#include <stdio.h>
struct TNotas{
float semestre1;
float semestre2;
};
struct TRegistroAluno{
char nome[30];
struct TNotas notas;
}TabelaTurma[6] = {
"joao",{4.5,8.9},
"maria",{5.5,8.3},
"jose",{3.5,8.6},
"lara",{6.5,3.9},
"luisa",{7.5,7.9},
"beatriz",{8.5,9.9},
};
float MediaSemestre1()
{
/* implementar aqui */
struct TRegistroAluno *treg;
float media=0;
int i=0;
//float notasem1;
while(i<6){
treg=&TabelaTurma[i];
media+=treg->notas.semestre1;
i++;
}
media=media/i;
return media;
}
void main()
{
/* testar aqui */
printf("Nota %.2f \n", MediaSemestre1());
}
// Questão 2
//Elabore uma função que recebe como parâmetro um ponteiro para uma string. A função deve retornar um ponteiro para uma substring da string passada e que comece com a primeira letra 'B' desta string. O esqueleto da função é dado:
char *retornaSubstring(char *p)
{
/* implementar aqui */
}
main()
{
char *aux="NOBODY";
/* exemplo de uso */
printf("%s", retornaSubstring(aux));
}
O printf deve imprimir "BODY"
//QUESTÂO 3
//Considere um sistema de estoque de uma livraria representado por uma tabela conforme abaixo:
//Elabore uma função para adicionar um livro na tabela. Considere que uma entrada livre possui o ///ponteiro titulo NULL.
#define TAM 10
struct TFornecedor{
char *editora;
char *endereco;
}
struct TLivro {
char *titulo;
char *autor;
float *preco;
int *estoque;
struct TFornecedor *fornecedor;
} Livros[TAM] ;
void adicionar_livro()
{
}
|
AULA 26 DIA 7/11/2013
|
PROVA B
Questão 1
Considere a tabela abaixo. Implemente uma função para retornar o índice da tabela correspondente a maior média anual dentre os alunos da turma.
#include <stdio.h>
struct TNotas{
float semestre1;
float semestre2;
};
struct TRegistroAluno{
char nome[30];
struct TNotas notas;
}TabelaTurma[6] = {
"joao",{4.5,8.9},
"maria",{5.5,8.3},
"jose",{3.5,8.6},
"lara",{6.5,3.9},
"luisa",{7.5,7.9},
"beatriz",{8.5,9.9},
};
int MaiorMédiaAnual()
{
/* implementar aqui */
}
main()
{
/* testar aqui */
}
Questão 2
Elabore uma função que recebe como parâmetro uma string e um char. A função deve retornar um ponteiro para a primeira substring da primeira string que comece com o caracter passado. O esqueleto da função é dado:
char *retornaSubstring(char *p1, char letra)
{
/* implementar aqui */
}
main()
{
char *aux="COLORADO";
/* exemplo de uso */
printf("%s", retornaSubstring(aux, 'A'));
}
O printf deve imprimir "ADO"
QUESTÂO 3
Considere um sistema de estoque de uma livraria representado por uma tabela conforme abaixo:
Elabore uma função para adicionar um livro na tabela. Considere que uma entrada livre possui o ponteiro titulo NULL. A função deve TESTAR se já existe um livro com mesmo título. Neste caso ela deve dar uma mensagem e retornar.
#define TAM 10
struct TFornecedor{
char *editora;
char *endereco;
}
struct TLivro {
char *titulo;
char *autor;
float *preco;
int *estoque;
struct TFornecedor *fornecedor;
} Livros[TAM] ;
void adicionar_livro()
{
}
|
AULA 27 DIA 11/11/2013
|
AULA 27 DIA 11/11/2013
Objetivos
- Tratamento de arquivos no C
- O sistema de arquivos no Linux
- O acesso com funções de alto nível
O sistema de arquivos no Linux
O Arquivo
Um arquivo é um conjunto de dados que pode ser referenciado por um nome e pode ter outros atributos tais como: permissão para leitura e escrita, data da criação, data da última modificação etc.
Note que um arquivo pode conter dados (um relatório, por exemplo), um programa C, um programa executável, música, fotos etc. Seja qual for a natureza dos dados o armazenamento será na forma de bits.
Normalmente, arquivos são armazenados em memórias secundárias, tais como CD, hard disk etc, mas eles podem se armazenados na memória principal também (RAM, FLASH).
Quanto a forma como os dados são armazenados, podemos dizer que os arquivos são binários ou texto. Qualquer um deles armazena bits mas os bytes de um arquivo texto representam códigos ASCII.
Sistema de Arquivos
Um sistema tal como o Linux possui milhares de arquivos. Para que arquivos possam ser acessados e armazenados de forma consistente, eles são organizados em um sistema de arquivos.
Tipicamente, um sistema de arquivos ocupa uma área de um disco (ou mídia de armazenamento).
Nesta área ficam armazenados blocos de armazenamento dos dados dos arquivos e também as estruturas chamadas de inodes.
Um inode é um estrutura que possui as propriedades do arquivo e ponteiros para os blocos que contém os dados do arquivo. Tipicamente um sistema de arquivos possui uma lista de inodes que permite "indexar" cada um dos arquivos do sistema de arquivos.
Existem vários formatos de sistema de arquivos, dependendo do sistema operacional. No linux os formatos mais conhecidos são: ext2, ext3, ext4 etc.
Um sistema de arquivos normalmente possui uma estrutura de dados inicial chamada de superbloco. O superbloco traz informações sobre o tamanho de blocos do sistema, o início da lista de inodes (/), etc.
+-------------+-----------------+------------------------+
|
lista de inodes | blocos dos arquivos |
+-------------+-----------------+------------------------+
</syntaxhighlight>
Diretórios
São arquivos especiais que contém basicamente uma lista contendo nome e inode correspondente dos arquivos que o diretório contém.
Em um sistema de arquivos, o diretório / é o diretório raiz do sistema, a partir do qual pode-se encontrar todos os demais arquivos.
Referência a um arquivo
A localização de um arquivo pode ser realizada pela referência absoluta, ou seja, desde o diretório / do sistema:
cat /etc/passwd
O comando cat tem por objetivo mostrar no terminal o conteúdo do arquivo passwd. Para que este arquivo seja encontrado na árvore de diretórios, deve-se fornecer a referência absoluta, desde o diretório /
Uma alternativa de acesso, é o uso da referência relativa ao diretório de trabalho. O conceito de diretório de trabalho é criado pelo interpretador de comandos (shell). Desta forma pode-se por exemplo fazer o comando:
cat passwd
O sistema procurará o arquivo passwd dentro do diretório de trabalho, que é referenciado armazenado pelo shell. Neste caso, o diretório de trabalho deveria ser /etc
cd /etc
cat passwd
No Linux/Unix tudo é arquivo
No Linux, qualquer referência/acesso ao hardware/dispositivos é realizada na forma de acesso a arquivo. Ver arquivos no diretório de dispositivos:
ls -l /dev
Como consequência, a partir de um programa em C, é possível abrir e escrever/ler em um dispositivo (desde que se tenha autorização).
Acessando arquivos a partir de programas C
Acesso a arquivos: funções de baixo e alto nível
Em sistemas do porte do Linux e Windows, por questões de segurança e controle, todo acesso a arquivo é realizado através de código do sistema operacional. Desta forma, um programa que deseja acessar um arquivo deve gerar uma CHAMADA AO SISTEMA.
O Linux (assim como outros sistemas) possui uma API (Application Programming Interface) bem definida, na forma de um conjunto de chamadas que permitem realizar uma série de funcionalidades (serviços) para os processos(programas em execução).
Um programa em C realiza uma CHAMADA AO SISTEMA com auxílio de funções da biblioteca C (a glib no caso do Linux).
Duas categorias de funções para acesso a arquivo são disponibilizadas (ver detalhes aqui):
- Funções de baixo nível: acessam diretamente o sistema;
- Funções de alto nível: as funções intermediam o acesso, criando buffers no espaço de endereçamento do processo. As funções de baixo nível são invocadas para ler e escrever dados no buffer.
Acesso a arquivo em MODO TEXTO através de funções de alto nível
O acesso em alto nível é realizado usando uma estrutura do tipo FILE definida no stdio.h.
Todo acesso passa inicialmente por abrir o arquivo (função fopen), ler/escrever (várias funções, tipo fread(), fwrite()) e fechar o arquivo (fclose()).
Exemplo 1: Escrevendo e lendo um arquivo texto de forma formatada
Nos exemplos que se seguem, serão usadas as funções fopen e fclose, para abrir e fechar arquivo e fprintf() e fscanf() para leitura e escrita.
A forma é similar ao printf e scanf(ver http://diuf.unifr.ch/pai/ip/tutorials/c/TC02_scanf.pdf), a não ser pelo fato de que a escrita e leitura é realizada no arquivo indicado
por p_arq
#include <stdio.h>
void main()
{
FILE *p_arq;
int i;
int res;
if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
for (i = 0; i<10;i++) {
/* A funcao fprintf devolve o número de bytes gravados ou EOF se houve erro na gravação */
if((res = fprintf(p_arq,"Linha %d\n",i))==EOF) {
printf("Erro\n");
break;
}
}
fclose(p_arq);
}
Note que se o arquivo IFSC.txt não existir, ele será criado.
Para ver o que fois escrito faça:
cat IFSC.txt
#include <stdio.h>
void main()
{
FILE *p_arq;
int i,j;
int res;
char buff[100];
if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
for (i = 0; i<10;i++) {
if((res = fscanf(p_arq,"%s %d",buff,&j))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("%s %d\n",buff,j);
}
fclose(p_arq);
}
Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caracter EOF (end-of-file) quando não existe mais dados a serem lidos.
- Implementar uma função que soma duas matrizes fornecidas em dois arquivos texto separados: MatA.dat e MatB.dat. Colocar o resultado em um arquivo chamado MatC.dat.
#include <stdio.h>
void main()
{
FILE *p_arq;
int i,j,res;
float MatA[5][5], MatB[5][5], MatR[5][5];
/* Ler a matriz MatA do arquivo */
if ((p_arq=fopen("MatA.dat", "r")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
for (i =0;i<5;i++) {
for (j=0;j<5;j++) {
if((res = fscanf(p_arq,"%f",&MatA[i][j]))==EOF){
printf("Fim de leitura\n");
break;
}
printf("%f ",MatA[i][j]);
}
printf("\n");
}
fclose(p_arq);
/* Ler a matriz MatB do arquivo */
printf("\n\n");
/* Ler a matriz MatB do arquivo */
if ((p_arq=fopen("MatB.dat", "r")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
for (i =0;i<5;i++) {
for (j=0;j<5;j++) {
if((res = fscanf(p_arq,"%f",&MatB[i][j]))==EOF){
printf("Fim de leitura\n");
break;
}
printf("%f ",MatB[i][j]);
}
printf("\n");
}
fclose(p_arq);
/* Calcular a soma das matrizes e colocar resultado em MatR */
for (i =0;i<5;i++) {
for (j=0;j<5;j++) {
MatR[i][j]=MatA[i][j]+MatB[i][j];
}
}
printf("Resultado da Adição\n");
for (i =0;i<5;i++) {
for (j=0;j<5;j++) {
printf("%f ", MatR[i][j]);
}
printf("\n");
}
/* Armazenar MatR em arquivo */
if ((p_arq=fopen("MatR.dat", "w")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
for (i =0;i<5;i++) {
for (j=0;j<5;j++) {
if((res = fprintf(p_arq,"%f ",MatR[i][j]))==EOF) {
printf("erro\n");
break;
}
}
fprintf(p_arq,"\n");
}
fclose(p_arq);
}
Exemplo2
Neste exemplo usaremos as funções fgetc e fputc para ler e cescrever caracteres ASCII nos arquivos. Seja um programa que conta o número de ocorrências do caracter 'a' em um arquivo e reescreve o arquivo sem os "as":
#include <stdio.h>
#include <stdlib.h>
main()
{
FILE *fp_fonte,*fp_destino;
int x,cont=0;
if((fp_fonte=fopen("teste.dat","r")) == NULL) {
puts("Não conseguiu abrir o arquivo\n");
exit(1);
}
if((fp_destino=fopen("dest.dat","w")) == NULL){
puts("Não conseguiu abrir o arquivo\n");
exit(1);
}
/* note que fgetc retorna um inteiro contendo o ASCII lido */
while ((x=fgetc(fp_fonte)) != EOF){
if(x=='a')
cont++;
else
if((x=fputc(x,fp_destino))==EOF) {
puts("Não conseguiu abrir o arquivo\n");
exit(1);
}
}
printf("ocorrências de a = %d\n", cont);
fclose(fp_fonte);
fclose(fp_destino);
}
Note que fputc recebe o caracter como um inteiro mas realiza um cast para
unsigned char com fins de escrevê-lo no arquivo.
Exemplo 3
Neste exemplo vamos explorar o modo de abertura do arquivo para fazer um append (escrever no final do arquivo).
#include <time.h>
#include <stdio.h>
int main(void)
{
time_t ltime;
FILE *fp;
int num;
time(<ime);
if ((fp=fopen("DATA.txt", "a")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
if ((num = fputs( ctime(<ime), fp )) != EOF ) {
fclose(fp);
} else {
printf("Erro na escrita do arquivo!\n");
}
}
Execute o programa da forma (suponha que se chame LOG_tempo:
log_tempo
cat DATA.txt
log_tempo
cat DATA.txt
log_tempo
cat DATA.txt
- Exercício: Substitua o modo de append por uma simples escrita (w). Reexecute o programa conforme especificado anteriormente.
Ainda funções de acesso a arquivos
A localização corrente do acesso a um arquivo pode ser modificada antes de uma leitura ou escrita. Tipicamente, quando se abre
uma arquivo para leitura/escrita, o "cursor" de acesso é 0, ou seja início do arquivo. Se o arquivo for aberto em modo append, o "cursor" é posicionado no final. A cada acesso (leitura ou esrita), este cursor é incrementado conforme o número de dados lidos ou escritos.
É possível entretanto modificar a posição deste cursor, tipicamente com as funções fseek() e rewind(). (ver http://www.gnu.org/software/libc/manual/html_node/File-Positioning.html#File-Positioning)
Considere o programa abaixo que escreve um vetor de 100 inteiros em um arquivo.
#include <stdio.h>
int x[100];
void main()
{
FILE *fp;
fp = fopen("teste.dat","w");
x[0]=32;
x[90]=11;
fwrite(x, sizeof(int),100,fp);
fclose(fp);
}
Note que o vetor, sendo uma variável global não inicialiazada, será zerado pelo procedimento de startup do programa.
Entretanto, a posição 90 recebe um valor (11).
Agora observe o programa seguinte que lê especificamente a posição 90 e na sequência restabelece o cursor no início. Note que o fread lê somente um inteiro na posição corrente do arquivo. A posição corrente foi determinada por fseek e depois retornada para o início com rewind.
#include <stdio.h>
int y;
void main()
{
FILE *fp;
fp = fopen("teste.dat","r");
fseek(fp, 90*sizeof(int), SEEK_CUR);
fread(&y, sizeof(int),1,fp);
printf("y=%d\n", y);
rewind(fp);
fread(&y, sizeof(int),1,fp);
printf("y=%d\n", y);
fclose(fp);
}
Execício
Considero o programa abaixo. Ele deve acessar uma tabela que se encontra em um arquivo binário. Cada item da tabela
se apresenta conforme o registro TRegistro. Implemente a função LerTab e crie um programa para escrever uma tabela iniciada com
um tabela de 5 registros a fim de testar a função implementada.
#include <stdio.h>
struct TRegistro {
char nome[30];
int idade;
} Registro, *pAux;
struct Tregistro *LerTab()
{
}
void main()
{
pAux=LerTab(3);
if (pAux!=0) {
printf("Nome lido %s\n", pAux->nome);
}
}
|
AULA 28 DIA 14/11/2013
|
Acesso a arquivo em MODO BINÁRIO através de funções de alto nível
Neste exemplo vamos escrever e ler uma struct (com dados binários) usando fwrite e fread. Note que os dados são gravados de forma binária.
#include <stdio.h>
#include <stdlib.h>
struct TNum {
float x;
float y;
} Num = { 1.5,
2.6,
};
main()
{
FILE *fp;
int num_itens;
fp = fopen("data.bin", "wb");
num_itens = fwrite((char*)&Num, sizeof(struct TNum), 1, fp);
fclose(fp);
Num.x =0;
Num.y = 0;
fp = fopen("data.bin", "rb");
num_itens = fread((char*)&Num, sizeof(struct TNum), 1, fp);
printf("Valor de x = %f\n", Num.x);
fclose(fp);
}
Referências
Material IBM
ex1
- include <stdio.h>
- include <stdlib.h>
struct TSala {
char nome[50];
float offset_temperatura;
};
main()
{
struct TSala *pSala;
FILE *pArquivo;
pSala = (struct TSala *) malloc(sizeof(struct TSala));
printf("Entre com o nome da sala\n");
scanf("%s", pSala->nome );
printf("Entre com a temperatura de offset\n");
scanf("%f", &pSala->offset_temperatura );
pArquivo = fopen("dados.dat", "a");
fprintf(pArquivo, "%s %f\n", pSala->nome, pSala->offset_temperatura);
fclose(pArquivo);
}
</syntaxhighlight>
#include <stdio.h>
#include <stdlib.h>
struct TSala {
char nome[50];
float offset_temperatura;
};
main()
{
struct TSala *pSala;
FILE *pArquivo;
pSala = (struct TSala *) malloc(sizeof(struct TSala));
printf("Entre com o nome da sala\n");
scanf("%s", pSala->nome );
printf("Entre com a temperatura de offset\n");
scanf("%f", &pSala->offset_temperatura );
pArquivo = fopen("dados.dat", "a");
fwrite(pSala, sizeof(struct TSala), 1, pArquivo);
fclose(pArquivo);
}
#include <stdio.h>
#include <stdlib.h>
struct TSala {
char nome[50];
float offset_temperatura;
};
main()
{
struct TSala *pSala;
FILE *pArquivo;
pSala = (struct TSala *) malloc(sizeof(struct TSala));
pArquivo = fopen("dados.dat", "r");
fread(pSala, sizeof(struct TSala), 1, pArquivo);
fclose(pArquivo);
printf("%s %f\n", pSala->nome, pSala->offset_temperatura);
}
Inicio da gravação de lista em arquivo
#include <stdio.h>
#include <stdlib.h>
struct TSala {
char nome[30];
float temperatura;
struct TSala *next;
} *head,*tail;
int adiciona_sala()
{
struct TSala *p;
p = (struct TSala *) malloc (sizeof(struct TSala));
if(!p)
return -1;
printf("Entre com o nome da sala\n");
scanf("%s",p->nome);
printf("Entre com a temperatura\n");
scanf("%f",&p->temperatura);
p->next = NULL;
if(head==NULL) {
head = tail = p;
} else {
tail->next = p;
tail = p;
}
return 0;
}
void save_list_to_file()
{
struct TSala *p;
FILE *pArquivo;
pArquivo = fopen("lista_salas.dat", "w");
for(p=head;p!=NULL;p=p->next) {
fwrite(p,sizeof(struct TSala), 1, pArquivo);
}
fclose(pArquivo);
}
void read_list_from_file()
{
}
main()
{
struct TSala *p;
FILE *pArquivo;
int bytes_lidos;
head=tail=NULL;
/* acrescenta 3 nodos na lista */
adiciona_sala();
adiciona_sala();
adiciona_sala();
save_list_to_file();
}
|
AULA 29 DIA 18/11/2013
|
Objetivos
- Funções de baixo nível;
- função select();
Funções de acesso a arquivos em baixo nível.
Alguns conceitos iniciais
No acesso a baixo nível, as funções da biblioteca basicamente chamam o sistema operacional (via SYSTEM CALLs)
sem qualquer intervenção. Qualquer arquivo será referenciado por um número inteiro que reflete o índice em uma TABELA DE ARQUIVOS
abertos, mantida pelo sistema operacional para cada processo.
Tipicamente, quando um processo é iniciado ele já possui a tabela de arquivos abertos populada nas três primeiros posições. A posição 0 corresponde a entrada padrão (normalmente será o terminal associado), a posição 1 (saída padrão, também o terminal) e a saída de erro padrão (também o terminal).
Exemplo: O comando ls é implementado na forma de um programa que quando se executa se torna um processo no sistema com os arquivos de entrada e saída padrão abertos. Faça:
ls -l
A listagem sai no arquivo de saída padrão. Você poderia redirecionar esta saída:
ls -l > lista.txt
Se você tentar listar algo que não existe é um erro. A mensagem de erro é remetida para saída de erro padrão.
ls -l /gdhfgjdkhsjdf
Não adianta redirecionar a saída padrão que a mensagem continua a aparecer.
ls -l /fsadasdfsadf 1> lista.txt
Note que o 1 no redirecionador 1> pode ser omitido.
Mas é possível redirecionar a saída de erros também:
ls -l /fdsdfgsdfgfsdg 2> erro.txt
Abra um outro terminal e verifique com o comando ps, o terminal virtual associado. Suponha que seja /dev/pts/1
Redirecione esta saída para p terminal virtual do outro terminal:
ls -l > /dev/pts/1
Funções de acesso
O acesso é basicamente realizado por funções open(), close(), read() e write() (Ver aqui o manual da glib).
Escrevendo na saída padrão e de erro usando write()
#include <unistd.h>
#include <stdlib.h>
int main()
{
write(1, "Escrevendo dados na saída padrão\n", 35);
write(2, "Agora escrevendo na saída de erros\n",37);
exit(0);
}
Agora execute testando as redireções:
./esc
./esc 1> lixo.dat
./esc 1> lixo.dat 2> erro.dat
Vamos fazer um exemplo de cópia de arquivos passados como parâmetro:
#include <unistd.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>
int main()
{
char c;
int in, out;
in = open("file.in", O_RDONLY);
out = open("file.out", O_WRONLY|O_CREAT, S_IRUSR|S_IWUSR);
while(read(in,&c,1) == 1)
write(out,&c,1);
exit(0);
}
Exercício 1: Implementar um programa de cópia similar ao que existe no linux. Capture o nome dos arquivos da linha de comando e inclua testes de erro na abertura de arquivo.
Exercício 2: Modificar o exercício anterior para realizar múltiplas cópias (um arquivo para vários).
Acesso a múltiplos arquivos usando select()
Por vezes é necessário esperar dados de várias fontes diferentes. A função select permite testar a existência de dados
em uma dada fonte de dados (arquivo). A deia geral é abrir os arquivos de interesse e colocar o descritor em conjunto do tipo
fd_set. O programa ao executar o select sobre este conjunto é BLOQUEADO até que exista dados disponíveis em um dos arquivos.
Uma macro FD_ISSET executada sobre o conjunto de descritores de arquivo permite detectar em qual arquivo foi alterado o status.
No exemplo a seguir, você deve fornecer para o programa quais terminais virtuais deseja escutar. O programa pergunta por um usuário
em cada terminal. O usuário fornece seu nome e recebe em troca uma mensagem de boas vindas.
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/select.h>
char *MsgLogin="Entre com USERID\n";
int main(int argc, char *argv[])
{
int fd;
int fdmax;
int i,j;
size_t bytes_read;
fd_set master_set, myset;
char buffer[256];
struct timeval timeout;
int ret,cont=0;
timeout.tv_sec=5;
timeout.tv_usec=0;
/* Limpa o conjunto de descritores */
FD_ZERO(&master_set);
/* abre cada arquivo passado na linha de comando e insere no conjunto master_set */
for (i=1;i<argc;i++) {
if ((fd=open(argv[i],O_RDWR))==-1) {
perror("open");
exit(1);
}
FD_SET(fd,&master_set);
write (fd,MsgLogin,strlen(MsgLogin));
}
/* registra o maior descritor */
fdmax=fd;
for (;;) {
myset=master_set; /* deve ser copiado sempre pois o conjunto é alterado */
if ((ret=select(fdmax+1,&myset,NULL,NULL, &timeout))==-1) {
perror("select");
exit(1);
}
if (ret==0) {
printf("Tempo esgotado: fazer alguma coisa...%d\n", cont++);
timeout.tv_sec=5;
timeout.tv_usec=0;
continue;
}
/* rastreia o descritor onde houve entrada de dados */
for (i=0;i<=fdmax;i++) {
if (FD_ISSET(i,&myset)) {
if ((bytes_read=read(i,buffer,sizeof(buffer)))==-1) {
perror("read");
exit(1);
}
if (bytes_read>0) {
write (i, "Bom Dia ", 8);
for (j=0;j<bytes_read;j++) {
write(i, &buffer[j], 1);
}
buffer[j-1]=0; /* eliminar o Line feed e colocar NULL no final da string */
if (!strcmp("f", buffer))
goto final; /* ok um goto para polemizar... */
write (i,"\n",1);
}
}
}
}
final:
for (i=0;i<=fdmax;i++) {
close(i);
}
return 0;
}
Para executar o programa faã o seguinte:
- abra um terminal, e execute o comando ps no terminal. Verifique qual TTY está associado ao seu terminal, por exemplo pts/0
- abra outro terminal e também verifique o terminal com ps. Por exemplo, pts/1
- neste último terminal bloqueio o shell fazendo: while true; do sleep 3600; done </dev/null
- volte ao terminal original e execute o programa da forma:
readmult /dev/pts/0 /dev/pts/1
- entre com um userid em cada terminal e verifique o resultado
- para finalizar tecle f como userid
|
AULA 30 DIA 21/11/2013
|
Objetivos
- Separação do programa em múltiplos arquivos;
- compilação condicional e uso de head guards;
- uso do make.
- ainda funções de alto nível para acesso a arquivos: rewind e fseek
Separação do programa em múltiplos arquivos
Muitas vezes o programa se torna grande demais e o uso de múltiplos arquivos fonte torna-se necessário. A divisão também permite
o desenvolvimento organizado do projeto, onde cada arquivo contém um grupo de instruções e variáveis globais relacionados com uma determinada parte
do sistema (subsistema ou módulo).
É necessário, no entanto, criar arquivos cabeçalho (headers ou .h) para declarar protótipos de funções e variáveis globais cuja visibilidade deve ser exportada para outros arquivos.
Exemplo:
Seja um projeto com dois arquivos. No arquivo t1.c existe o seguinte conteúdo:
#include <stdio.h>
#include "t2.h"
int y;
void alfa(int x)
{
printf("x=%d\n",x);
}
main()
{
y = 20;
alfa(2);
beta();
}
Observe que a função main() usa as funções a alfa() e beta(). Mas beta() não está implementada em t1.c. Ela está implementada em um outro arquivo t2.c.
Neste caso, para que o compilador possa validar os parâmetros e o retorno da função beta() é necessário incluir um arquivo header t2.h que possui tais informações.
Arquivo t2.h:
Note o uso da palavra extern para informação do PROTÓTIPO da função.
O arquivo t2.c possui a implementação de beta():
#include <stdio.h>
#include "t1.h"
void beta()
{
alfa(23);
printf ("y=%d\n",y);
}
Note que beta() usa a função alfa() que está implementada em t1.c. Neste caso ela inclui o arquivo t1.h que contém o protótipo de alfa():
extern int y;
extern void alfa(int x); /* extern aqui é opcional */
REGRAS GERAIS PARA CONSTRUÇÂO DO HEADER
- Se você definir uma variável global em arquivo fonte, digamos no t1.c, e deseja que outros arquivos "vejam" está variável então coloque uma declaração desta variável em um arquivo header (t1.h) com a palavra chave extern.
NOTA: note que existe uma diferença entre definir e declarar. Se você cria uma variável global, por exemplo, int x; no
arquivo fonte t1.c, você está definindo a variável. Será alocada uma área de memória para esta veriável. Se você declara
a variável no t1.h usando o extern, você simplesmente está informando que esta variável existe e qual tipo possui.
- Nunca defina a variável no header pois estará abrindo a possibilidade para que cada arquivo que inclua este header crie uma instância desta variável;
- Se você quer publicar (informar) outros arquivos fonte sobre funçṍes de um dado arquivo fonte, por exemplo, a função beta() do exemplo passado, então insira uma declaração da função no header. Neste caso a palavra chave extern é opcional.
- tipos que são usados de forma compartilhada por diversos arquivos fonte devem constar em um arquivo header a ser incluído por estes arquivos. (é o caso da struct TUsuario do trabalho).
Ver discussão em:
[6]
Compilando múltiplos arquivos
É possível compilar e linkar os múltiplos arquivos da forma:
gcc t1.c t2.c -o t1
A desvantagem desta abordagem é que os dois arquivos sempre serão compilados, independente de mudanças. Pode-se compilá-los de forma independente da forma:
gcc -c t1.c
gcc -c t2.c
E então linká-los:
gcc t1.o t2.o -o t1
Nas aulas posteriores veremos que é possível utilizar o utilitário make para controlar a compilação do projeto.
Você deve ter notado o uso de include com aspas e com < e >. Quando um arquivo include é fornecido entre aspas então o arquivo a ser incluído é procurado no próprio diretório onde está o fonte. Caso contrário, ele é procurado em diretórios que o gcc está configurado (consulta em variáveis do sistema) para procurá-lo.
Seja um arquivo avo.h:
E um arquivo pai.h:
Finalmente um arquivo filho.h:
#include "avo.h"
#include "pai.h"
Considere o fonte filho.c
Se este arquivo for compilado, teremos um erro porque devido a dupla inclusão do header avo.h a estrutura familia estará sendo duplicada.
As diretivas de pré-compilação #ifndef e #ifdef são usadas quando queremos compilar "condicionalmente" determinado bloco de código.
Por exemplo, podemos criar guard headers usando a diretiva #ifndef de forma a inserir unicamente um código de um header.
No exemplo anterior, o arquivo avo.c poderia ser "guardado" da forma:
#ifndef AVO_H
#define AVO_H
struct familia {
};
#endif
Neste caso, guando o arquivo filho.c for compilado, o arquivo avo.h é duplamente incluído. O compilador, ao encontrar a diretiva #ifndef AVO_H da primeira inclusão, observa que o símbolo AVO_H não foi definido ainda (com um #define). Neste caso, ele compila o código que se segue. No código que se segue o símbolo AVO_H é definido. Ao encontrar a segunda inclusão de de avo.h, o compilador novamente se depara com a diretiva #ifndef AVO_H. Neste momento, o símbolo AVO_H já foi definido e o compilador ignora o código que se segue até encontrar o #endif. É a compilação condicional...
É uma boa prática proteger os arquivos headers com uma guarda de forma a evitar problemas posteriores.
Uso do make
O utilitário make é muito usado para gerenciar a compilação de múltiplos arquivos. #endifamos ver o seu uso através de um exemplo.
Seja um programa executável programa_bin formado a partir de dois arquivos fontes: main.c e parte2.c
Seja main.h um header de main.c e parte2.h um header de parte2.c. Os arquivos são:
Arquivo main.h:
#ifndef MAIN_H
#define MAIN_H
extern void alfa(char *p);
#endif
Arquivo main.c:
#include <stdio.h>
#include "parte2.h"
#include "main.h"
void alfa(char *p)
{
printf("ALFA: %s\n", p);
}
main()
{
alfa ("Alo Mundo - Invocado de main");
beta ("Alo Mundo - Invocado de main");
}
Arquivo parte1.h:
#ifndef PARTE2_H
#define PARTE2_H
extern void beta(char *p);
#endif
Arquivo parte2.c:
#include <stdio.h>
#include "parte2.h"
#include "main.h"
void beta(char *p)
{
printf("BETA: %s\n", p);
alfa ("Alo Mundo - Invocado de BETA");
}
Seja o arquivo Makefile:
#arquivo Makefile
programa_bin: main.o parte2.o
gcc main.o parte2.o -o programa_bin
main.o: main.c main.h parte2.h
gcc -c main.c
parte2.o: parte2.c parte2.h main.h
gcc -c parte2.c
clean:
rm *.o
rm -r ./install
install:
mkdir install
mv programa_bin ./install
NOTA: SEMPRE use TAB antes dos comandos. Nunca use SPACE.
Para chamar o make basta fazer:
make
O make interpreta o Makefile do diretório corrente tenta construir o arquivo programa_bin usando a primeira regra do arquivo. Se este estiver atualizado nada será feito. Caso ele observe que main.o ou parte2.o estão desatualizados, ele tenta remontá-los usando as respectivas regras. Note que ao lado do nome da regra estão as dependências.
|
AULA 31 DIA 25/11/2013
|
Objetivos
Operações bit a bit disponíveis
Symbol |
Operator
|
& |
bitwise AND
|
l |
bitwise inclusive OR
|
^ |
bitwise exclusive OR
|
<< |
left shift
|
>> |
right shift
|
~ |
one's complement (unary)
|
Rotacionando a direita
#include <stdio.h>
main()
{
unsigned char i;
i=0x80;
printf("i=%d ou em hex i=0x%x\n", i, i);
i=i>>2; /* rotaciona dois bits a direita */
printf("i=%d ou em hex i=0x%x\n", i, i);
}
Note que na memória o valor 0x80 aparece como:
10000000
Rotacionando dois bits a esquerda este valor fica:
00100000
Que corresponde ao valor 0x40
Rotacionando a esquerda
#include <stdio.h>
main()
{
unsigned char i;
i=0x14;
printf("i=%d ou em hex i=0x%x\n", i, i);
i=i<<3; /* rotaciona três bits a esquerda */
printf("i=%d ou em hex i=0x%x\n", i, i);
}
Note que na memória o valor 0x80 aparece como:
00010100
Rotacionando três bits a esquerda este valor fica:
10100000
Que corresponde ao valor 0xA0
NOTE que uma rotação a direita corresponde em dividir o número por 2 enquanto uma rotação a esquerda corresponde a multiplicar o número por 2.
Operações lógicas bit a bit
Lembrar que :
bit a |
bit b |
a & b (a AND b)
|
0 |
0 |
0
|
0 |
1 |
0
|
1 |
0 |
0
|
1 |
1 |
1
|
bit a |
bit b |
a | b (a OR b)
|
0 |
0 |
0
|
0 |
1 |
1
|
1 |
0 |
1
|
1 |
1 |
1
|
#include <stdio.h>
main()
{
unsigned char i;
i=0xF2;
printf("Operação E com a máscará 0x03 -> %x\n", i&0x03);
printf("Operação OR com a máscará 0x03 -> %x\n", i|0x03);
printf("Operação OR EXCLUSIVO com a máscará 0x03 -> %x\n", i^0x03);
printf("Complementando i -> %x\n", (char)~i);
}
Note que as operações acima são realizadas em nível de bit e não conforme as operações
lógicas realizadas sobre expressões.
Exercício:
Implementar uma função que retorna o valor da operação lógica bita a bit sobre os valores de um vetor de bytes como parâmetro. Veja esqueleto abaixo.
unsigned char oplog (unsigned char pVetor[], int tam, char operador)
{
}
main()
{
unsigned char alfa[3] = { 0xFF,0xAA,0x00};
unsigned char valor;
valor = oplog(alfa, 3, '|'); /* calcula o OU bit a bit sobre o vetor */
valor = oplog(alfa, 3, '&'); /* calcula o E bit a bit sobre o vetor */
valor = oplog(alfa, 3, '^'); /* calcula o OU EXCLUSIVO bit a bit sobre o vetor */
}
|
AULA 34 DIA 5/11/2013
|
Objetivos
- Representando o tempo em uma forma apropriada para comparação de hora, minuto e segundo.
- Revisão de ponteiros
Representando o tempo em uma forma apropriada para comparação de hora, minuto e segundo
#include <stdio.h>
#include <time.h>
main()
{
time_t rawtime;
struct tm *tminfo;
time ( &rawtime );
tminfo = localtime ( &rawtime );
printf ( "hora: %d minuto: %d segundo: %d \n", tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
}
Exemplo de aplicação no projeto:
#include <stdio.h>
#include <time.h>
#include <stdlib.h>
typedef struct tipo_sala{
char salaId[20];
int hora_ent;
int hora_sai;
} TSala;
typedef struct tipo_aluno{
char userId[20];
TSala *pSala;
}TAluno;
main()
{
time_t rawtime;
struct tm *tminfo;
TAluno *pAluno = malloc(sizeof(TAluno));
printf("Entre com o userId do usuario\n");
scanf("%s", pAluno->userId );
pAluno->pSala = malloc(sizeof(TSala));
printf("Entre com o nome da sala\n");
scanf("%s", pAluno->pSala->salaId );
printf("Entre com a hora de entrada\n");
scanf("%d", &pAluno->pSala->hora_ent );
printf("Entre com a hora de saída\n");
scanf("%d", &pAluno->pSala->hora_sai );
/* Ler o tempo real no momento */
time ( &rawtime );
tminfo = localtime ( &rawtime );
printf ( "hora: %d minuto: %d segundo: %d \n", tminfo->tm_hour, tminfo->tm_min, tminfo->tm_sec);
if(pAluno->pSala->hora_ent <= tminfo->tm_hour && pAluno->pSala->hora_sai >= tminfo->tm_hour) {
printf("Abrir porta\n");
} else {
printf("Não abrir porta\n");
}
}
{{collapse bottom}}
{{collapse top}}
===Especificações Adicionais do Projeto ===
===Especificações Adicionais do Projeto ===
A ideia do projeto é construir um sistema capaz de controlar a temperatura e o controle de acesso a um conjunto de salas de uma instituição de ensino.
*O sistema deverá permitir editar (criar, remover, modificar) salas e pessoas (2 listas separadas).
*Cada pessoa possui um número de matrícula no formato LLLXXXXYY onde LLL são letras maúsculas, XXXX um números e YY o ano de entrada no curso;
*Cada sala possui uma lista de alunos que podem acessá-la. A senha é única para todas as salas. Cada aluno pode ter uma faixa de horário específico para acesso a uma sala;
{{collapse bottom}}
{{collapse top|AULA 35 DIA 12/12/2013}}
== AULA 35 DIA 12/12/2013==
EXERCÍCIOS
Implementar uma função que recebe um vetor de caracteres como parâmetro. Este vetor conterá uma string terminada em NULL. A função deve retornar o número de vogais 'a' que são precedidos pela letra b. Na função main apresentar um teste de uso da função
Implementar uma função que recebe um vetor de caracteres como parâmetro. Este vetor conterá uma string terminada em NULL. A função deve retornar o número de vogais 'a' e 'i' que sejam sucedidas pela letra c. Na função main apresentar um teste de uso da função <syntaxhighlight lang=c>
int num_vog(char cadeia[])
{
/* implementar aqui */
}
main()
{
/* testar o uso da função aqui */
}
Explicação adicional:Para a cadeia "abacate" a função deveria retornar 1.
|
|