Mudanças entre as edições de "PRG029003 - Programação II - 2023-2"
Linha 56: | Linha 56: | ||
=Tópicos de Aula= | =Tópicos de Aula= | ||
− | == | + | ==Revisão da linguagem C== |
{{collapse top|Introdução ao C e funções de saída e entrada de dados}} | {{collapse top|Introdução ao C e funções de saída e entrada de dados}} | ||
Linha 317: | Linha 317: | ||
{{collapse bottom}} | {{collapse bottom}} | ||
− | {{collapse top| | + | {{collapse top|Laços de repetição em C}} |
*Vídeos | *Vídeos | ||
*O que é uma estrutura de repetição? Chris Bosh em Code.org {{#ev:youtube|G2hdlhDYICw}} | *O que é uma estrutura de repetição? Chris Bosh em Code.org {{#ev:youtube|G2hdlhDYICw}} | ||
Linha 1 066: | Linha 1 066: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | ==Acessando arquivos em C== | ||
+ | {{collapse top | Introdução}} | ||
+ | Arquivos em C são tratados como [https://en.wikipedia.org/wiki/Stream_(computing) "streams"] que literalmente significa córrego (riacho), em computação significa dados em fluxo. Utilizado para tratar quantidade desconhecida de informações (potencialmente infinita). Por esta característica arquivos são acessados através de ponteiros do tipo FILE * (um tipo de dados declarado na biblioteca <stdio.h>). | ||
+ | |||
+ | Ainda na stdio, há três streams padrão (stdin, stdout e stderror) que não precisam ser declarados e estão prontos para uso. Funcões como scanf e getchar, por exemplo, na prática estão obtendo dados do teclado (stdin) e funções como printf e putchar estão escrevendo da tela (stdout). Eventuais falhas serão também enviadas para a tela (stderr). | ||
+ | |||
+ | A quantidade de streams que um programa mantém aberto pode ser limitado pelo sistema operacional. | ||
+ | |||
+ | A biblioteca stdio.h suporta dois tipos de arquivos: [http://en.wikipedia.org/wiki/Binary_file binários] ou [http://en.wikipedia.org/wiki/Text_file texto]. Os arquivos tipo texto são compostos por caracteres humanamente compreensíveis. Já os arquivos binários são codificados. Arquivos binários podem armazenar mais informações em menor espaço e são normalmente mais fáceis de serem lidos por programas tornando-os também mais performáticos. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Operações de abertura e fechamento de arquivos}} | ||
+ | ;Operação abrir arquivo | ||
+ | A primeira ação será a de abertura de uma arquivo. É realizado com a função fopen que deve receber como parâmetros o nome do arquivo a ser aberto e o modo de abertura que especifica se será para leitura ou escrita. A diretiva restrict que aparece em ambos os argumentos não é muito relevante para o momento, basicamente está dizendo que estes espaços de memória não podem ser compartilhados (C99). | ||
+ | <syntaxhighlight lang=c> | ||
+ | FILE *fopen(const char * restrict filename, const char * restrict mode) | ||
+ | </syntaxhighlight> | ||
+ | ;Um exemplo de chamada seria: | ||
+ | <syntaxhighlight lang=c> | ||
+ | FILE * arquivo; | ||
+ | arquivo = fopen("IFSC.txt", "r"); | ||
+ | </syntaxhighlight> | ||
+ | Onde "r" significa somente leitura | ||
+ | ;Outro exemplo de chamada seria: | ||
+ | <syntaxhighlight lang=c> | ||
+ | arquivo = fopen("c:\\temp\\temp.txt", "w"); | ||
+ | </syntaxhighlight> | ||
+ | Onde "w" significa que o arquivo está sendo aberto para escrita | ||
+ | Observe também o uso de "\\" e não apenas "\". Isto de deve pois algo como "C:\temp\temp.txt" teria então um "\t" que significa TAB. O uso de "\\" previne esta má interpretação. Uma outra alternativa é escrever "C:/temp/temp.txt", ou seja, com as barras invertidas. | ||
+ | O operação fopen deve retornar um endereço válido se for bem sucedida, se falhar retornará NULL. Razões para falhas estão listadas [http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html aqui]. | ||
+ | |||
+ | ;Modos de acesso a arquivos (arquivos tipo texto) | ||
+ | *"r": Abre para leitura (arquivo precisa existir) | ||
+ | *"w": Abre para escrita (o arquivo não necessariamente precisa existir, o conteúdo possivelmente será sobrescrito caso não tratado, ver cursor) | ||
+ | *"a": Abre para escrita adicionando dados no final do arquivo (o arquivo não necessariamente precisa existir) | ||
+ | *"r+": Abre para leitura e escrita (arquivo precisa existir) | ||
+ | *"w+": Abre para leitura e escrita (trunca se o arquivo existir, apagando o conteúdo anterior. Permite leitura) | ||
+ | *"a+": Abre para leitura e escrita adicionando dados no final do arquivo (o arquivo não necessariamente precisa existir) | ||
+ | O caractere '+' representa o modo atualização (update mode). | ||
+ | Para arquivos binários devem ter a letra "b" associada ("rb", "wb", "ab", "r+b"/"rb+", "w+b"/"wb+", "a+b"/"ab+"). No sistema operacional Linux a especificação de abertura de arquivo como binário não trará mudança pois é utilizado apenas um caractere para representar nova linha (\n). Porém, para manter portabilidade com o Windows que utiliza dois caracteres de nova linha '\r\n' é importante utilizar o modo 'b' para arquivos binários. | ||
+ | ;Operação fechar arquivo | ||
+ | Permite o programa fechar um arquivo que não está mais sendo utilizado. Deve receber como parâmetro o ponteiro (FILE *) para o arquivo. Se fechar com sucesso retornará zero, caso ocorre erro retornará EOF (uma macro definida na stdio.h. | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fclose(FILE *stream); | ||
+ | </syntaxhighlight> | ||
+ | ;Um exemplo de chamada seria: | ||
+ | <syntaxhighlight lang=c> | ||
+ | fclose(arquivo); | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Operações de escrita de arquivos}} | ||
+ | ;Operação escrever texto formatado em arquivo | ||
+ | Escreve uma saída em um stream apontado. A string apontada no segundo argumento se parece as utilizadas na função "printf". | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fprintf(FILE * restrict stream, const char * restrict format, ...); | ||
+ | </syntaxhighlight> | ||
+ | ;Outras operações de escrita: | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fputc(int c, FILE *stream); //Escreve um caracter | ||
+ | int fputs(const char * restrict s, FILE restrict *stream); //Escreve uma string | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Exemplo: Abrindo e escrevendo um arquivo | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | FILE *p_arq; | ||
+ | int i; | ||
+ | |||
+ | if ((p_arq=fopen("IFSC.txt", "w")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return 0; | ||
+ | } else { | ||
+ | printf("Arquivo aberto com sucesso. Iniciando escrita no arquivo...\n"); | ||
+ | } | ||
+ | |||
+ | 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((fprintf(p_arq,"Linha %d\n",i))==EOF) { | ||
+ | printf("Erro ao escrever no arquivo!\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | } | ||
+ | printf("Fim da escrita, observe o arquivo 'IFSC.txt' criado na mesma pasta deste executável!\n"); | ||
+ | fclose(p_arq); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Note que se o arquivo IFSC.txt não existir, ele será criado. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Operações de leitura de arquivos}} | ||
+ | |||
+ | ;Operação escrever texto formatado em arquivo | ||
+ | Lê informações de um stream apontado em um formato definido. | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fscanf(FILE *stream, const char *format, ...); | ||
+ | </syntaxhighlight> | ||
+ | ;Outras operações de escrita: | ||
+ | <syntaxhighlight lang=c> | ||
+ | int fgetc(FILE *stream); //Lê um caracter e avança o cursor | ||
+ | char *fgets(char *str, int n, FILE *stream); //Lê um conjunto de caracteres limitado a n de tamanho, nova linha ou fim do arquivo | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | ;Exemplo: Abrindo e lendo um arquivo | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | FILE *p_arq; | ||
+ | int i,j; | ||
+ | char buff[100]; | ||
+ | |||
+ | if ((p_arq=fopen("IFSC.txt", "r")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo, o arquivo existe?\n"); | ||
+ | return 0; | ||
+ | } else { | ||
+ | printf("Arquivo aberto com sucesso. Inicio da leitura do arquivo...\n"); | ||
+ | } | ||
+ | |||
+ | while(1) { | ||
+ | if((fscanf(p_arq,"%s %d",buff,&j))==EOF) { | ||
+ | printf("Fim de leitura\n"); | ||
+ | break; | ||
+ | } | ||
+ | printf("%s %d\n",buff,j); | ||
+ | } | ||
+ | fclose(p_arq); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | |||
+ | Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caractere EOF (end-of-file) quando não existe mais dados a serem lidos. | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Adicionando uma linha no final de um arquivo}} | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <time.h> | ||
+ | |||
+ | int main(void) | ||
+ | { | ||
+ | time_t ltime; | ||
+ | FILE *fp; | ||
+ | |||
+ | if ((fp=fopen("leituras.log", "a")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | time(<ime); | ||
+ | if ((fputs(ctime(<ime), fp)) != EOF ) { | ||
+ | fclose(fp); | ||
+ | } else { | ||
+ | printf("Erro na escrita do arquivo!\n"); | ||
+ | } | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | Tipicamente, quando se abre uma arquivo para leitura/escrita, o "cursor" de acesso fica na posição 0 (início do arquivo). Se o arquivo for aberto em modo append ('a'), o "cursor" é posicionado no final. A cada acesso (leitura ou escrita), este cursor é incrementado conforme o número de dados lidos ou escritos. | ||
+ | Execute este código algumas vezes e vá observando o que ocorre com o arquivo 'leituras.log'. | ||
+ | |||
+ | ;Exemplo 2 | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <time.h> | ||
+ | #include <string.h> | ||
+ | int main(void) | ||
+ | { | ||
+ | time_t ltime; | ||
+ | FILE *fp; | ||
+ | struct tm *info; | ||
+ | char buffer[100],b2[100]; | ||
+ | |||
+ | if ((fp=fopen("tst.log", "a+")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return; | ||
+ | } | ||
+ | |||
+ | time(<ime); | ||
+ | info = localtime( <ime ); | ||
+ | strftime(buffer,sizeof(buffer),"%x-%I:%M%p", info); | ||
+ | printf("Formatted date & time : |%s|\n", buffer ); | ||
+ | strcat(buffer," Joao\n"); | ||
+ | |||
+ | if ((fputs(buffer, fp)) == EOF ) { | ||
+ | printf("Erro na escrita do arquivo!\n"); | ||
+ | } | ||
+ | |||
+ | fseek(fp,0,SEEK_SET); | ||
+ | while(1) { | ||
+ | if((fscanf(fp,"%s %s",buffer,b2))==EOF) { | ||
+ | printf("Fim de leitura\n"); | ||
+ | break; | ||
+ | } | ||
+ | printf("Marcação lida: %s %s\n",buffer,b2); | ||
+ | } | ||
+ | fclose(fp); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Alterando o cursor com fseek}} | ||
+ | Observe este exemplo que escreve em um arquivo e depois lê o conteúdo escrito. Neste caso é utilizado fseek para voltar o cursor para o início do arquivo. | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | int main(int argc, char *argv[]) | ||
+ | { | ||
+ | FILE *p_arq; | ||
+ | int i,j; | ||
+ | char buff[100]; | ||
+ | |||
+ | if ((p_arq=fopen("IFSC.txt", "w+")) == NULL) { | ||
+ | printf("Problemas na abertura do arquivo\n"); | ||
+ | return 0; | ||
+ | } else { | ||
+ | printf("Aberto com sucesso. Iniciando escrita no arquivo...\n"); | ||
+ | } | ||
+ | |||
+ | for (i = 0; i<10;i++) { | ||
+ | if((fprintf(p_arq,"LINHA %d\n",i))==EOF) { | ||
+ | printf("Erro ao escrever no arquivo!\n"); | ||
+ | return -1; | ||
+ | } | ||
+ | } | ||
+ | printf("Escrito! 'IFSC.txt' criado na pasta deste executável!\n"); | ||
+ | |||
+ | /*Muda a posição do cursor para o início do arquivo*/ | ||
+ | fseek(p_arq,0,SEEK_SET); | ||
+ | while(1) { | ||
+ | if((fscanf(p_arq,"%s %d",buff,&j))==EOF) { | ||
+ | printf("Fim de leitura\n"); | ||
+ | break; | ||
+ | } | ||
+ | printf("%s %d\n",buff,j); | ||
+ | } | ||
+ | fclose(p_arq); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Tratando erros de abertura de arquivo}} | ||
+ | Quando não é possível abrir um arquivo a função retorna NULL, porém para se saber o real motivo da falha é necessário consultar o "errno" que fica setado na biblioteca <errno.h>. Há diversos códigos de erro para váriadas funções, no caso da fopen, consulte [http://pubs.opengroup.org/onlinepubs/009695399/functions/fopen.html aqui] os erros possíveis. | ||
+ | <syntaxhighlight lang=c> | ||
+ | #include <stdio.h> | ||
+ | #include <errno.h> | ||
+ | |||
+ | int main() | ||
+ | { | ||
+ | FILE *fp; | ||
+ | |||
+ | printf("Para simular o erro certifique-se de não haver um arquivo 'testeErro.txt' na pasta\n"); | ||
+ | if ((fp = fopen("testeErro.txt","r")) == NULL) { | ||
+ | if (errno == ENOENT) | ||
+ | printf("Código de erro %d, arquivo não existe!\n",errno); | ||
+ | else | ||
+ | printf("Erro não previsto (código: %d)!\n",errno); | ||
+ | return 0; | ||
+ | } | ||
+ | fclose(fp); | ||
+ | return 0; | ||
+ | } | ||
+ | </syntaxhighlight> | ||
+ | {{collapse bottom}} | ||
+ | |||
+ | {{collapse top | Exercícios - avançado (série 7)}} | ||
+ | #Após criar um arquivo IFSC.txt com o código acima, modifique o texto deste arquivo manualmente, escreva seu nome, por exemplo, salve e feche. Execute novamente o executável acima. O que ocorreu? O que você observa que foi realizado com o modo 'w' utilizado acima? | ||
+ | #Modifique o programa exemplo para que abra o arquivo com modo "r+", Aumente e diminua a quantidade de iterações do loop para escrever mais e menos linhas. Observe o comportamento nos modos "w"/"w+" e "r+" . O que você conclui de diferença no comportamento dos modos 'r+' e 'w'/'w+'? | ||
+ | #Crie um programa que recebe como parâmetro de entrada o nome de um arquivo que deve ser criado e num segundo argumento um texto qualquer que deve ser inicializado neste arquivo. Os parâmetros de entrada são argc e argv - veja documentação aqui na wiki. | ||
+ | #Utilize as funções fputc e fputs para escrever em arquivos | ||
+ | #Implemente um programa em C que lê de um arquivo chamado "anagrama.txt" duas palavras (uma na primeira linha e a outra na segunda linha). O programa analisa estas palavras e diz se são ou não anagramas (palavra formada pela alteração da ordem ou transposição de letras) ex.: “America” e “Iracema”, “Roma” e “Amor”, “Semolina” e “Is no meal” são anagramas). Utilize o modo de acesso a arquivo "r".<br> | ||
+ | #Implemente um programa em C que dada uma frase obtida através de um scanf, torne todas as letras em maiúsculas (código 1), torne todas em minúsculas (código 2) ou a primeira letra de cada palavra em maiúscula e demais minúsculas (código 3) e escreve em tela o resultado. O limite para a frase digitada é 100 caracteres. Faça estas operações em funções distintas (tudoMaiusculo(), tudoMinusculo(), primeiraMaiuscula()), utilizando o vetor de caracteres como uma variável global. A escolha das opções dependerá de um numero inteiro lido de um arquivo chamado "config.cfg" este número deve estar escrito no início do arquivo e pode ser ser 1, 2 ou 3. O arquivo config.cfg deve ser criado manualmente e preenchido com diferentes opções para testar o algoritmo. Utilize o modo "r".<br> | ||
+ | #Implemente um algoritmo em C que conta a ocorrência de cada letra (incluindo números de 0 a 9) dada uma frase. Deve ignorar se a letra foi digitada em maiúscula ou minúscula (portando somar junto). Limite a 100 caracteres. Para cada palavra digitada deve gerar um log em uma arquivo chamado "ocorrencias.log" que contém a data/hora em que foi gerado o registro e quantas letras de cada tipo foram lidas. Utilize o modo "a" ou "a+".<br> | ||
+ | #Utilize a função rand() e gerador de semente srand() para gerar 6 números aleatórios distintos que variam de 1 a 60 e grava esta sequencia em um arquivo chamado "aposta.txt". Utilize o modo "w" ou "w+".<br> | ||
+ | #Crie uma estrutura chamada TReferencia que contém um campo "livro" de 100 caracteres e um campo "autores" de 100 caracteres. Crie um loop infinito para receber uma quantidade qualquer de livros que compõem as referencias bibliográficas de um trabalho acadêmico. O programa deve então receber duas string (que podem conter espaços em branco), uma sendo o nome do livro e a outra os autores. Recebidos estes dados devem ser instanciados na struct. Se recebido uma string contendo a palavra "sair" significa que deve-se então proceder com a finalização do programa gravando as instancias da struct em um arquivo chamado "referencias.txt". Como resultado este arquivo deve ter em cada linha o nome do livro entre aspas ("nome do livro") depois uma vírgula e o nome dos autores entre aspas também ("S. AUTOR1, S. AUTOR2").Utilize o modo "r+".<br> | ||
+ | #Utilize no exemplo acima o modo "w" ao invés de "w+". Qual sua conclusão? | ||
+ | #Teste agora com o modo append, da mesma maneira qual a diferença de "a" e "a+"? | ||
{{collapse bottom}} | {{collapse bottom}} |
Edição atual tal como às 16h02min de 15 de agosto de 2023
Dados importantes
Professor
- Professor da Disciplina: Cleber Jorge Amaral e Roberto Wanderley da Nóbrega
- Email: cleber.amaral@ifsc.edu.br e roberto.nobrega@ifsc.edu.br
- Atendimento paralelo / agenda do professor: Agenda IFSC
Turma virtual
- Acesse o sigaa bucando a disciplina PRG029003 - PROGRAMAÇÃO II (2023 .2 - T01)
Aulas síncronas presenciais
- Horários
- terças-feiras das 13:30 às 15:20
- sextas-feiras das 13:30 às 15:20
- Mais informações ver SIGAA
- Local
- Laboratório de Programação (LabProg)
Organização curricular
Plano de ensino
A unidade curricular se compõe de conhecimentos relacionados às estruturas de dados, com ênfase em sua utilização na escrita de programas.
- Usar as estruturas de dados fila, pilha, lista, tabela de dispersão e árvore binária na escrita de programas;
- Identificar as situações e necessidades em que cada estrutura de dados é apropriada;
- Conhecer o custo computacional das operações elementares das estruturas de dados, e de algoritmos de busca e ordenamento, para que se possam utilizá-los de forma eficiente;
- Conhecer o custo computacional das operações elementares das estruturas de dados, e de algoritmos de busca e ordenamento, para que se possam utilizá-los de forma eficiente;
Ementa
Metodologia
Os estudos serão guiados por leituras, exercícios, e projetos. O conteúdo da unidade curricular será apresentado por meio de aulas expositivas e aulas práticas de maneira articulada com aplicações do conhecimento.
Recursos auxiliares
- Utilização do sistema acadêmico SIGAA para avisos e registro de frequência
- Utilização do moodle para atividades complementares e registros de participação em aula.
Referências bibliográficas
Básica
- CORMEN, Thomas H. et al. Algoritmos: teoria e prática. LTC, 2012. (link para minha biblioteca - necessário logar via SIGAA primeiro)
- LORENZI, Fabiana; MATTOS, Patrícia de; CARVALHO, Tanisi de. Estruturas de dados. Cengage Learning, 2006. ISBN 978-8522105564.
Complementar
- BACKERS, André. Linguagem C: completa e descomplicada. Grupo GEN, 2023. (link para minha biblioteca - necessário logar via SIGAA primeiro)
- KERNIGHAN, Brian W.; RITCHIE, Dennis M. C. A Linguagem de Programação Padrão Ansi. Elsevier, 1989. ISBN 978-8570015860.
Material de apoio
- Slides Prof. Emerson Mello
- Apostila de C - UFMG
Tópicos de Aula
Revisão da linguagem C
Introdução ao C e funções de saída e entrada de dados |
---|
|
Condicionais em C |
---|
|
Exercícios - C (série 0) |
---|
|
Laços de repetição em C |
---|
|
Estruturas |
---|
Assim como o vetor a estrutura é um conjunto de dados, mas traz uma vantagem: a possibilidade de possuir "campos" de diferentes tipos de variáveis. Por exemplo, a struct TPessoa poderia ter os campos nome (char[40]) e idade (int). A declaração genérica da estrutura é: struct TNome_do_tipo { //variável 1 //variável 2 //variável N } nome_instancia;
#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.
#include <stdio.h>
#define NUM_MAX 3
struct TAluno {
char nome[30];
char matricula[11];
float b1,b2,b3,b4;
} Turma[NUM_MAX];
void print_aluno(struct TAluno aux)
{
printf("Nome -> %s\n", aux.nome);
printf("Matrícula -> %s\n", aux.matricula);
printf("Bimestre 1 -> %f\n", aux.b1);
printf("Bimestre 2 -> %f\n", aux.b2);
printf("Bimestre 3 -> %f\n", aux.b3);
printf("Bimestre 4 -> %f\n", aux.b4);
}
main()
{
int i;
for(i=0;i<NUM_MAX;i++) {
printf("Entre com o nome do aluno\n");
scanf("%s", Turma[i].nome);
printf("Entre com a matrícula do aluno\n");
scanf("%s", Turma[i].matricula);
printf("Entre com a nota do bimestre 1\n");
scanf("%f", &Turma[i].b1);
printf("Entre com a nota do bimestre 2\n");
scanf("%f", &Turma[i].b2);
printf("Entre com a nota do bimestre 3\n");
scanf("%f", &Turma[i].b3);
printf("Entre com a nota do bimestre 4\n");
scanf("%f", &Turma[i].b4);
}
for(i=0;i<NUM_MAX;i++) {
printf("=========== Aluno %d ============\n", i);
print_aluno(Turma[i]);
}
}
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);
}
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);
}
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];
int numero;
};
struct TCidadao{
char nome[50];
char cpf[20];
struct TEndereco endereco;
};
int main(void)
{
//Inicializando com parâmetros em sequencia (ordem tem que ser respeitada)
struct TCidadao CidadaoMaria = {"Maria","42342342234",{"Rua AlfaBeta",145}};
//Inicializando com parâmetros via campo (não é necessário respeitar qualquer ordem)
struct TCidadao CidadaoJose = {.cpf = "1234567890", .endereco.numero = 541,.nome = "Jose",.endereco.rua = "Rua GamaDelta"};
printf("Rua do cidadao %s = %s\n", CidadaoMaria.nome, CidadaoMaria.endereco.rua);
printf("Rua do cidadao %s = %s\n", CidadaoJose.nome, CidadaoJose.endereco.rua);
}
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);
}
|
Exercícios - C (série 5) |
---|
|
Unions |
---|
Union é um recurso do C que permite declarar um conjunto de dados que irá ocupar um mesmo espaço. É bastante empregado quando se deseja economizar espaço ou não se tem certeza sobre qual tipo de dado deve ser armazenado para determinada instancia. No exemplo a seguir é criada uma struct chamada TProduto e dentro destra estrutura há uma área de detalhamento do produto que é de uso genérico, para alguns produtos há campos específicos para preenchimento e outros não se tem ao certo os detalhes, portanto fica um campo de uso geral.
#include <stdio.h>
struct TRoupeiro{
char cor[20];
int volume;
float peso;
};
struct TProduto{
int id;
char nome[20];
union {
struct TRoupeiro roupeiro;
char descricao_generica[sizeof(int)+sizeof(float)+20];
};
};
int main(void)
{
struct TProduto vaso_decorativo = {
.id = 2,.nome = "Vaso decorativo 1",
.descricao_generica = "em vidro - peça única"
};
struct TProduto guarda_roupas_solteiro = {
.id = 1,.nome = "Roupeiro 3 portas",
.roupeiro.cor = "CZ", .roupeiro.volume = 304,.roupeiro.peso = 50.0
};
printf("nome = %s, \ndescrição = %s, \ncor = %s, \nvolume = %d, \npeso = %f\n\n\n",
guarda_roupas_solteiro.nome,
guarda_roupas_solteiro.descricao_generica,
guarda_roupas_solteiro.roupeiro.cor,
guarda_roupas_solteiro.roupeiro.volume,
guarda_roupas_solteiro.roupeiro.peso
);
printf("nome = %s, \ndescrição = %s, \ncor = %s, \nvolume = %d, \npeso = %f\n\n\n",
vaso_decorativo.nome,
vaso_decorativo.descricao_generica,
vaso_decorativo.roupeiro.cor,
vaso_decorativo.roupeiro.volume,
vaso_decorativo.roupeiro.peso
);
}
|
Ponteiros |
---|
A memória de um computador pode ser vista como um vetor de bytes. Neste espaço vimos a utilização de variáveis diversas que podem armazenar valores que podem ser obtidos do usuários, serem resultados de ariméticas e muitas outras operações. O ponteiro nada mais é que um tipo de dado igualmente armazenado em memória, porém este dado se refere a um endereço da memória, ou seja, a um outro objeto. Este recurso é muito útil para diversos propósitos, basta pensar na própria aplicação do conceito "endereço", imagine como seria localizar uma casa em uma cidade sem haver uma forma de endereçar e armazenar os endereços das casas. Explorando esta analogia, cada lote possui um endereço e pode ter um conteúdo de diferentes tipos como uma casa, um prédio ou um conjunto de lojas, enfim, trazendo para o C seria como os tipos int, char, vetores diversos, etc. Assim é a memória, 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.
#include <stdio.h>
int main(void)
{
int i = 10;
int *p;
long int li;
p = &i;
printf("Conteúdo de i: i = %d\n",i);
printf("Endereço de i: &i = %p\n",&i);
printf("Conteúdo de p: p = %p\n",p);
printf("Endereço de p: &p = %p\n",&p);
printf("Conteúdo apontado: *p = %d (conteúdo do endereço apontado por p)\n",*p);
printf("Tamanho do ponteiro = %li bytes\n",sizeof(p));
printf("Tamanho do lont int = %li bytes\n",sizeof(li));
printf("Tamanho do int = %li bytes\n",sizeof(i));
return 0;
}
Resposta obtida através do gcc em uma máquina Linux Ubuntu: Conteúdo de i: i = 10 Endereço de i: &i = 0x7ffeb25859e4 Conteúdo de p: p = 0x7ffeb25859e4 Endereço de p: &p = 0x7ffeb25859e8 Conteúdo apontado: *p = 10 (conteúdo do endereço apontado por p) Tamanho do ponteiro = 8 bytes Tamanho do lont int = 8 bytes Tamanho do int = 4 bytes
Conteúdo de i: i = 10 Endereço de i: &i = 0xbfadc2b8 Conteúdo de p: p = 0xbfadc2b8 Endereço de p: &p = 0xbfadc2bc Conteúdo apontado: *p = 10 (conteúdo do endereço apontado por p) Tamanho do ponteiro = 4 bytes Tamanho do lont int = 4 bytes Tamanho do int = 4 bytes
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
main()
{
int x=10;
int y, *p;
}
Complete o código para copiar o conteúdo de x para y, sem que qualquer variável apareçam no lado esquerdo de um sinal de atribuição. Ou seja, sem envolver diretamente x e y.
main()
{
int x,y,w,*p1,*p2;
x = 20;
w = 30;
p1 = &x;
p2 = &w;
y = *p1 + *p2;
}
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;
}
#include <stdio.h>
void main()
{
int x,y;
int *p;
y=0;
p=&y;
x=*p;
x=4;
(*p)++;
x--;
(*p) += x;
printf("\ny=%d x=%d\n",y,x);
}
Os ponteiro para char são muito utilizados pois permitem apontar para strings. A ideia é que ele aponte para o primeiro caracter (char) 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).
main()
{
char x[10]="ifsc";
char *p, y;
p = x + 2;
y= *p;
}
#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.
Ponteiros podem apontar para qualquer "objeto" de qualquer tipo. Vamos verificar como é possível apontar para uma estrutura: #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.
#include <stdio.h>
struct TRegistro {
char nome[20];
int idade;
} Tabela[4];
struct TRegistro *p;
main()
{
int i;
p = &Tabela[0]; /*p aponta para o registro 0 da tabela */
for (i=0;i<4;i++,p++)
{
printf("Digite o nome e idade da pessoa %d\n", i+1);
scanf("%s %d",p->nome,&(p->idade));
printf("\n\n>>>> O nome e idade da pessoa %d é: %s, %d \n\n\n", i+1, p->nome, p->idade);
}
}
No exemplo a abaixo a função RetornarStruct() retorna um ponteiro para uma estrutura. O cuidadado que se deve ter é que a função não deveria apontar para uma estrutura que foi criada localmente na 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);
}
#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);
}
A função main() pode ter parâmetros formais, mas o programador não pode escolhores quais serão eles. A declaração que se pode ter para a função main() é: int main (int argc, char *argv[]); Exemplo: Escreva um programa que faça uso dos parâmentros argv e argc. O programa deverá receber da linha de comando o dia, mês e ano correntes (dd/mm/aaaa), e imprimir a data em formato apropriado. Veja o exemplo, supondo que o executável se chame data: $ data 04 11 2016 O programa deverá imprimir: $ 04 de novembro de 2016#include <stdio.h>
#include <stdlib.h>
void main(int argc, char *argv[])
{
int mes;
char *nomemes [] = {"janeiro","fevereiro","março","abril","maio","junho","julho","agosto","setembro","outubro","novembro","dezembro"};
if(argc == 4) /* Testa se o numero de parametros fornecidos esta' (nome do programa, o dia, o mes e os dois ultimos algarismos do ano */
{
/* argv contem strings. A string referente ao mes deve ser
* transformada em um numero inteiro. A funcao atoi esta sendo
* usada para isto: recebe a string e transforma no inteiro equivalente
*/
mes = atoi(argv[2]);
if (mes<1 || mes>12) /* Testa se o mes e' valido */
printf("Erro!\nUso mes: mm, deve ser de 1 a 12.\n");
else
printf("\n%s de %s de %s\n\n", argv[1], nomemes[mes-1],argv[3]);
}
else
printf("Erro!\nUso: dd/mm/aaaa, devem ser inteiros, ou estão faltando.\n");
}
|
Usando ponteiros como parâmetros de entrada saída de funções |
---|
Enviar um ponteiro a uma função tem diversas aplicações, uma delas é a de evitar redundância de dados e realizar a leitura de informações "diretas da fonte". Estas informações quando são de grande volume também poderiam requisitar um grande volume de memória para copiar, então mais um motivo para se passar a referência (ponteiro). 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));
}
#include <stdio.h>
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);
}
|
Acessando arquivos em C
Introdução |
---|
Arquivos em C são tratados como "streams" que literalmente significa córrego (riacho), em computação significa dados em fluxo. Utilizado para tratar quantidade desconhecida de informações (potencialmente infinita). Por esta característica arquivos são acessados através de ponteiros do tipo FILE * (um tipo de dados declarado na biblioteca <stdio.h>). Ainda na stdio, há três streams padrão (stdin, stdout e stderror) que não precisam ser declarados e estão prontos para uso. Funcões como scanf e getchar, por exemplo, na prática estão obtendo dados do teclado (stdin) e funções como printf e putchar estão escrevendo da tela (stdout). Eventuais falhas serão também enviadas para a tela (stderr). A quantidade de streams que um programa mantém aberto pode ser limitado pelo sistema operacional. A biblioteca stdio.h suporta dois tipos de arquivos: binários ou texto. Os arquivos tipo texto são compostos por caracteres humanamente compreensíveis. Já os arquivos binários são codificados. Arquivos binários podem armazenar mais informações em menor espaço e são normalmente mais fáceis de serem lidos por programas tornando-os também mais performáticos. |
Operações de abertura e fechamento de arquivos |
---|
A primeira ação será a de abertura de uma arquivo. É realizado com a função fopen que deve receber como parâmetros o nome do arquivo a ser aberto e o modo de abertura que especifica se será para leitura ou escrita. A diretiva restrict que aparece em ambos os argumentos não é muito relevante para o momento, basicamente está dizendo que estes espaços de memória não podem ser compartilhados (C99). FILE *fopen(const char * restrict filename, const char * restrict mode)
FILE * arquivo;
arquivo = fopen("IFSC.txt", "r");
Onde "r" significa somente leitura
arquivo = fopen("c:\\temp\\temp.txt", "w");
Onde "w" significa que o arquivo está sendo aberto para escrita Observe também o uso de "\\" e não apenas "\". Isto de deve pois algo como "C:\temp\temp.txt" teria então um "\t" que significa TAB. O uso de "\\" previne esta má interpretação. Uma outra alternativa é escrever "C:/temp/temp.txt", ou seja, com as barras invertidas. O operação fopen deve retornar um endereço válido se for bem sucedida, se falhar retornará NULL. Razões para falhas estão listadas aqui.
O caractere '+' representa o modo atualização (update mode). Para arquivos binários devem ter a letra "b" associada ("rb", "wb", "ab", "r+b"/"rb+", "w+b"/"wb+", "a+b"/"ab+"). No sistema operacional Linux a especificação de abertura de arquivo como binário não trará mudança pois é utilizado apenas um caractere para representar nova linha (\n). Porém, para manter portabilidade com o Windows que utiliza dois caracteres de nova linha '\r\n' é importante utilizar o modo 'b' para arquivos binários.
Permite o programa fechar um arquivo que não está mais sendo utilizado. Deve receber como parâmetro o ponteiro (FILE *) para o arquivo. Se fechar com sucesso retornará zero, caso ocorre erro retornará EOF (uma macro definida na stdio.h. int fclose(FILE *stream);
fclose(arquivo);
|
Operações de escrita de arquivos |
---|
Escreve uma saída em um stream apontado. A string apontada no segundo argumento se parece as utilizadas na função "printf". int fprintf(FILE * restrict stream, const char * restrict format, ...);
int fputc(int c, FILE *stream); //Escreve um caracter
int fputs(const char * restrict s, FILE restrict *stream); //Escreve uma string
#include <stdio.h>
int main(void)
{
FILE *p_arq;
int i;
if ((p_arq=fopen("IFSC.txt", "w")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return 0;
} else {
printf("Arquivo aberto com sucesso. Iniciando escrita no arquivo...\n");
}
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((fprintf(p_arq,"Linha %d\n",i))==EOF) {
printf("Erro ao escrever no arquivo!\n");
return -1;
}
}
printf("Fim da escrita, observe o arquivo 'IFSC.txt' criado na mesma pasta deste executável!\n");
fclose(p_arq);
return 0;
}
Note que se o arquivo IFSC.txt não existir, ele será criado. |
Operações de leitura de arquivos |
---|
Lê informações de um stream apontado em um formato definido. int fscanf(FILE *stream, const char *format, ...);
int fgetc(FILE *stream); //Lê um caracter e avança o cursor
char *fgets(char *str, int n, FILE *stream); //Lê um conjunto de caracteres limitado a n de tamanho, nova linha ou fim do arquivo
#include <stdio.h>
int main()
{
FILE *p_arq;
int i,j;
char buff[100];
if ((p_arq=fopen("IFSC.txt", "r")) == NULL) {
printf("Problemas na abertura do arquivo, o arquivo existe?\n");
return 0;
} else {
printf("Arquivo aberto com sucesso. Inicio da leitura do arquivo...\n");
}
while(1) {
if((fscanf(p_arq,"%s %d",buff,&j))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("%s %d\n",buff,j);
}
fclose(p_arq);
return 0;
}
Note que o fscanf se comporta de forma similar ao scanf. A função retorna o caractere EOF (end-of-file) quando não existe mais dados a serem lidos. |
Adicionando uma linha no final de um arquivo |
---|
#include <stdio.h>
#include <time.h>
int main(void)
{
time_t ltime;
FILE *fp;
if ((fp=fopen("leituras.log", "a")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
time(<ime);
if ((fputs(ctime(<ime), fp)) != EOF ) {
fclose(fp);
} else {
printf("Erro na escrita do arquivo!\n");
}
}
Tipicamente, quando se abre uma arquivo para leitura/escrita, o "cursor" de acesso fica na posição 0 (início do arquivo). Se o arquivo for aberto em modo append ('a'), o "cursor" é posicionado no final. A cada acesso (leitura ou escrita), este cursor é incrementado conforme o número de dados lidos ou escritos. Execute este código algumas vezes e vá observando o que ocorre com o arquivo 'leituras.log'.
#include <stdio.h>
#include <time.h>
#include <string.h>
int main(void)
{
time_t ltime;
FILE *fp;
struct tm *info;
char buffer[100],b2[100];
if ((fp=fopen("tst.log", "a+")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return;
}
time(<ime);
info = localtime( <ime );
strftime(buffer,sizeof(buffer),"%x-%I:%M%p", info);
printf("Formatted date & time : |%s|\n", buffer );
strcat(buffer," Joao\n");
if ((fputs(buffer, fp)) == EOF ) {
printf("Erro na escrita do arquivo!\n");
}
fseek(fp,0,SEEK_SET);
while(1) {
if((fscanf(fp,"%s %s",buffer,b2))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("Marcação lida: %s %s\n",buffer,b2);
}
fclose(fp);
return 0;
}
|
Alterando o cursor com fseek |
---|
Observe este exemplo que escreve em um arquivo e depois lê o conteúdo escrito. Neste caso é utilizado fseek para voltar o cursor para o início do arquivo. #include <stdio.h>
int main(int argc, char *argv[])
{
FILE *p_arq;
int i,j;
char buff[100];
if ((p_arq=fopen("IFSC.txt", "w+")) == NULL) {
printf("Problemas na abertura do arquivo\n");
return 0;
} else {
printf("Aberto com sucesso. Iniciando escrita no arquivo...\n");
}
for (i = 0; i<10;i++) {
if((fprintf(p_arq,"LINHA %d\n",i))==EOF) {
printf("Erro ao escrever no arquivo!\n");
return -1;
}
}
printf("Escrito! 'IFSC.txt' criado na pasta deste executável!\n");
/*Muda a posição do cursor para o início do arquivo*/
fseek(p_arq,0,SEEK_SET);
while(1) {
if((fscanf(p_arq,"%s %d",buff,&j))==EOF) {
printf("Fim de leitura\n");
break;
}
printf("%s %d\n",buff,j);
}
fclose(p_arq);
return 0;
}
|
Tratando erros de abertura de arquivo |
---|
Quando não é possível abrir um arquivo a função retorna NULL, porém para se saber o real motivo da falha é necessário consultar o "errno" que fica setado na biblioteca <errno.h>. Há diversos códigos de erro para váriadas funções, no caso da fopen, consulte aqui os erros possíveis. #include <stdio.h>
#include <errno.h>
int main()
{
FILE *fp;
printf("Para simular o erro certifique-se de não haver um arquivo 'testeErro.txt' na pasta\n");
if ((fp = fopen("testeErro.txt","r")) == NULL) {
if (errno == ENOENT)
printf("Código de erro %d, arquivo não existe!\n",errno);
else
printf("Erro não previsto (código: %d)!\n",errno);
return 0;
}
fclose(fp);
return 0;
}
|
Exercícios - avançado (série 7) |
---|
|