Prévia do material em texto
RESUMO – PROGRAMAÇÃO DE SOFTWARE BÁSICO EM C TEMA 1. BIBLIOTECAS E APIS PARA LINGUAGEM C ❖ ESTRUTURA DE UM PROGRAMA EM C Introdução ao Programa em C: o A estrutura de um programa em C é organizada para otimizar seu funcionamento, da inclusão de bibliotecas até a execução de comandos. o Cada parte tem uma função específica que permite criar programas eficientes e precisos. Estrutura Básica do Programa: o Inclusão de Bibliotecas: Facilita tarefas comuns, como entrada e saída de dados. Exemplo: #include // inclui definições para funções de entrada e saída, que permitem realizar operações como ler dados do teclado e exibir informações na tela do computador. o Função Principal: int main() contém as instruções do programa, sendo o ponto de partida de execução. Exemplo: 1 int main() { 2 printf("Olá, mundo!"); 3 return 0; 4 } Diretivas de Compilação: o São instruções especiais para o compilador definir recursos ou comportamentos específicos. o Fornecem informações ao computador para alterar o comportamento da compilação. Executadas durante a fase de pré-processamento. o Incluem em outros arquivos ponte no processo de compilação. Definem constante simbólicas. o Exemplo: #define MAXIMO 100 substitui MAXIMO por 100 no código, como uma instrução fixa. Processo de Compilação: Transforma o código em C em um executável, através das seguintes etapas: ▪ Pré-processamento: Executa diretivas e remove comentários. ▪ Compilação: Converte o código em linguagem de montagem ou código objeto. ▪ Montagem: Transforma o código objeto em binário. ▪ Link-edição: Combina módulos e bibliotecas em um arquivo executável. Variáveis e Constantes: o Variáveis: Armazenam valores que podem ser alterados durante a execução do programa (exemplo: int idade = 30;). o Constantes: Valores que não mudam após serem definidos, como const float PI = 3.14;. Questão 1. João está desenvolvendo um programa em C que calcula a área de um círculo. Ele precisa utilizar o valor de PI e garantir que ele não seja alterado acidentalmente em seu código. Além disso, ele quer definir um valor máximo para o raio do círculo que pode ser calculado. Com base nos conceitos de variáveis, constantes e diretivas de compilação em C, qual das seguintes opções João deveria implementar corretamente em seu programa? A. int PI = 3.14; #define RAIO_MAX 100 B. const float PI = 3.14; #define RAIO_MAX 100 C. #define PI 3.14; int RAIO_MAX = 100 D. float PI = 3.14; const int RAIO_MAX = 100 E. #define PI 3.14; #define RAIO_MAX 100 RESPOSTA: Constante PI: a declaração const float PI = 3.14; assegura que o valor de PI seja tratado como uma constante, ou seja, um valor que não pode ser alterado depois de inicializado. Diretiva RAIO_MAX: #define RAIO_MAX 100 define valor máximo para o raio que não precisa ser alterado e pode ser facilmente acessado em todo o código. As outras opções apresentam problemas, Confira: • int PI = 3.14; permite que PI seja alterado, o que não é desejável. • #define PI 3.14; trata PI como uma macro, o que é menos seguro e pode levar a problemas de precisão devido à falta de tipo. • float PI = 3.14; também permite que PI seja alterado, e const int RAIO_MAX = 100; usa const quando #define seria mais adequado para diretivas de compilação. Tipos básicos de dados, classes de armazenamento, especificadores e operadores Tipos Básicos de Dados o Inteiros (int): Armazenam números inteiros (sem casas decimais), como contagens. o Ponto Flutuante (float, double): Para números decimais, ideais para cálculos de precisão, como juros. o Caracteres (char): Armazenam um único caractere, importante no processamento de texto. Classes de Armazenamento: Controlam o tempo de vida e a visibilidade de variáveis ou funções. o auto: Usado para variáveis locais que desaparecem após o uso. o static: Mantém o valor entre chamadas de função, útil para contadores. o extern: Declara que a variável é definida em outro arquivo, comum em programas com múltiplos arquivos. o register: Sugere que a variável seja guardada em um registro da CPU para acesso rápido. Especificadores de Tipo: Refinam a definição de tipos de dados. o unsigned int: Apenas valores positivos, com maior alcance que int. o long: Aumenta a capacidade de armazenamento para valores grandes. Operadores e Precedência: Permitem operações em variáveis e expressões. o Aritméticos (+, -, *, /): Para cálculos básicos. o Lógicos (&&, ||, !): Para operações lógicas. o Comparação (==, !=, >,direta do usuário. Questão 1. Você está desenvolvendo um software educacional que requer interação frequente do usuário por meio do teclado e do mouse. Para implementar essa funcionalidade, você decide utilizar a biblioteca windows.h em um ambiente Dev-C++, no sistema operacional Windows. Considerando a função WindowProcedure da biblioteca windows.h, verifique a asserção e a razão a seguir e a relação entre elas. Asserção: a função WindowProcedure é essencial para gerenciar eventos de teclado e mouse em um aplicativo Windows usando C. Razão: a função WindowProcedure permite ao desenvolvedor definir o ícone e o cursor da aplicação. Selecione a seguir a alternativa correta. A. Ambas são verdadeiras, e a razão é a explicação correta da asserção. B. Ambas são verdadeiras, mas a razão não é a explicação correta da asserção. C. A asserção é verdadeira, mas a razão é falsa. D. A asserção é falsa, mas a razão é verdadeira. E. Ambas são falsas. RESPOSTA: A asserção é verdadeira, pois a função WindowProcedure realmente é importante para o manejo de eventos de teclado e mouse em aplicativos Windows. Essa função é encarregada de responder a eventos como pressionamentos de tecla e cliques de mouse. No entanto, a razão, embora verdadeira, não justifica a asserção. A capacidade de definir ícones e cursores na aplicação é configurada na estrutura WNDCLASSEX e passada durante o registro da classe de janela, não sendo uma funcionalidade diretamente relacionada ao gerenciamento de eventos de entrada, que é o papel principal da WindowProcedure. Captura de eventos de teclado e mouse com OpenGL e GLUT • Objetivo: Facilitar a criação de interfaces gráficas interativas em OpenGL, permitindo o gerenciamento eficiente de eventos de teclado e mouse em C com a biblioteca GLUT. • GLUT: Facilita a criação de programas que utilizam OpenGL. Simplifica o gerenciamento de eventos de teclado e mouse. Proporciona uma interação fluida e responsiva ao usuário. • Relevância: Essencial para desenvolver aplicativos dinâmicos, como jogos e softwares de visualização, permitindo uma interação fluida e responsiva ao usuário. • Funções de Captura de Eventos na GLUT: o glutKeyboardFunc: Captura entrada de teclado. Requer um callback, que é uma função acionada ao pressionar uma tecla. o glutSpecialFunc: Semelhante à glutKeyboardFunc, mas para teclas especiais, como setas do teclado. O callback recebe a tecla pressionada e a posição atual do mouse (x, y). o glutMouseFunc: Registra um callback para eventos de clique do mouse, permitindo detectar o botão pressionado, o tipo de ação (clique ou liberação) e a posição do mouse. o glutMotionFunc e glutPassiveMotionFunc: ▪ glutMotionFunc: Acionada quando o mouse é movido com um botão pressionado. ▪ glutPassiveMotionFunc: Detecta movimentos do mouse sem botões pressionados. • Conclusão: O uso das funções GLUT para eventos em OpenGL permite capturar de forma organizada e eficiente a interação do usuário, sendo fundamental para desenvolver aplicações interativas em C. Questão 1. Em um projeto de desenvolvimento de um jogo 2D utilizando OpenGL e a biblioteca GLUT, um programador deseja implementar um sistema de controle em que o personagem principal se move de acordo com as teclas direcionais pressionadas. Considerando as funções do GLUT, qual delas deve ser usada para capturar os eventos de teclado especial e mover o personagem de acordo com o esperado? A. glutKeyboardFunc B. glutSpecialFunc C. glutMouseFunc D. glutMotionFunc E. glutPassiveMotionFunc RESPOSTA: A função correta para capturar teclas especiais, como as setas direcionais do teclado, é glutSpecialFunc. Ela permite registrar um callback que é chamado sempre que uma tecla especial é pressionada. Roteiro Prático: Como interagir com o jogo • Pressione ENTER para iniciar ou reiniciar o jogo após alguém ganhar. Lembre-se: ele começa em estado de pausa. • Use as teclas W (para cima) e S (para baixo) para mover o jogador 1. • Utilize as teclas de seta ↑ (para cima) e ↓ (para baixo) para mover o jogador 2. • Pressione ESC para sair do jogo. Explicação do código 1. Configurações iniciais e variáveis globais • Inicializam as constantes para largura e altura da janela, velocidade dos movimentos e outras configurações, como o número de rodadas. • Definem variáveis para controlar a movimentação da bola, a direção de movimento e os pontos dos jogadores. 2. Função main • Configura e cria a janela do jogo e a subjanela do placar. • Registra funções de callback para desenho na tela, eventos de teclado e temporizadores para movimentação dos jogadores e da bola. 3. Funções de desenho (display e JanelaPlacar) • É função do display desenhar as bordas do campo, os jogadores como linhas verticais e a bola como um ponto. • É atribuição da JanelaPlacar mostrar o placar atual e as instruções e anuncia o vencedor quando um jogador alcançar o número necessário de pontos. 4. Manipulação de eventos (keyboard e KeySpecial) • É função do keyboard lidar com eventos de teclado para iniciar o jogo, mover o jogador 1 e sair do jogo. • É papel de KeySpecial lidar com as teclas de seta para mover o jogador 2. 5. Movimentação (moveJogador, jogador1, jogador2, bola) • Atualizar a posição vertical do jogador com base em seu movimento atual é função de moveJogador. • Jogador1 e jogador2 são funções temporizadas que chamam moveJogador para atualizar a posição de cada jogador. • É característica de bola atualizar a sua posição, verificar colisões com os jogadores ou as bordas do campo e ajustar o placar. 6. Utilidades (DesenhaTexto, DesenhaPtsPlacar, random e setPause) • São características de DesenhaTexto e DesenhaPtsPlacar serem funções auxiliares para desenhar texto na tela. • É função de random gerar um ângulo aleatório para o movimento da bola após colidir com um jogador. • É atribuição de setPause reiniciar as posições e as configurações quando o jogo é pausado ou reiniciado. #include #include #include #include #define WIDTH 600 #define HEIGHT 500 #define VELOCIDADE_BOLA 30 #define VELOCIDADE_JOGADORES 1 #define NUM_RODADAS 10 GLint movebolax=0; GLint movebolay=0; GLint direcao=1; GLint direcao_vertical=1; GLint move_jogador1Y=0; GLint move_jogador2Y=0; int TYJ1 = 0; int TYJ2 = 0; int janelaPlacar; int janela; static char label[200]; int pts_jogador1 = 0; int pts_jogador2 = 0; int movimento_jogador1 = 2; int movimento_jogador2 = 2; double angulo = 0.0; bool pause = true; void init(void); void display(void); void bola(int passo); void keyboard(unsigned char key, int x, int y); void KeySpecial(int key, int x, int y); void DesenhaTexto(char *s); void JanelaPlacar(void); void moveJogador(int &move_jogadorY, int &movimento_jogador); void jogador1(int passo); void jogador2(int passo); void displayAll(void); int random(void); void setPause(void); int main(int argc, char** argv){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); glutInitWindowSize (WIDTH, HEIGHT); glutInitWindowPosition (100, 100); janela = glutCreateWindow ("Telejogo"); glutIdleFunc(displayAll); init(); glutDisplayFunc(display); glutTimerFunc(VELOCIDADE_JOGADORES,jogador1, 1); glutTimerFunc(VELOCIDADE_JOGADORES,jogador2, 1); glutTimerFunc(10,bola,1); glutKeyboardFunc(keyboard); glutSpecialFunc(KeySpecial); janelaPlacar = glutCreateSubWindow (janela, 5, 5, WIDTH - 10, 85); glutDisplayFunc (JanelaPlacar); glutMainLoop(); return 0; } void displayAll(void){ glutSetWindow(janela); glutPostRedisplay(); glutSetWindow(janelaPlacar); glutPostRedisplay(); } void init(void){ glClearColor(0.0,0.0, 0.0, 0.0); glOrtho (0, WIDTH, 0, HEIGHT, -1 ,1); } void display(void){ glClear(GL_COLOR_BUFFER_BIT); // glPolygonMode(GL_BACK, GL_LINE); glColor3f(1.0, 1.0, 1.0); glLineWidth(15.0); /*MARGENS DO CAMPO*/ glBegin(GL_LINE_LOOP); glVertex2i(2,2); glVertex2i(599,2); glVertex2i(599,399); glVertex2i(2,399); glEnd(); /*JOGADORES*/ //JOGADOR-1 glColor3f(1.0, 1.0, 1.0); glBegin(GL_LINES); glVertex2i(100,190+move_jogador1Y); glVertex2i(100,240+move_jogador1Y); //barra do jogador 1 glEnd(); //JOGADOR-2 glColor3f(1.0, 1.0, 1.0); glBegin(GL_LINES); glVertex2i(500,190+move_jogador2Y); glVertex2i(500,240+move_jogador2Y); //barra do jogador 2 glEnd(); //BOLA glColor3f(1.0, 1.0, 1.0); glPointSize(15.0); glBegin(GL_POINTS); glVertex2i(movebolax+200,movebolay+210); glEnd(); glutSwapBuffers(); } void jogador1(int passo){ moveJogador(move_jogador1Y,movimento_jogador1); glutPostRedisplay(); glutTimerFunc(VELOCIDADE_JOGADORES,jogador1, 1); } void jogador2(int passo){ moveJogador(move_jogador2Y,movimento_jogador2); glutPostRedisplay(); glutTimerFunc(VELOCIDADE_JOGADORES,jogador2, 1); } void keyboard(unsigned char key, int x, int y){ switch (key) { case 13: pause = false; if(pts_jogador1==NUM_RODADAS || pts_jogador2==NUM_RODADAS){ pts_jogador1 = pts_jogador2 = 0; } direcao=1; break; case 27: exit(0); break; case 'w': case 'W': movimento_jogador1 = 1; break; case 's': case 'S': movimento_jogador1 = 3; break; } } void KeySpecial(int key, int x, int y){ switch(key){ case 101: movimento_jogador2 = 1; break; case 103: movimento_jogador2 = 3; break; } } void moveJogador(int &move_jogadorY, int &movimento_jogador){ switch(movimento_jogador){ case 1: move_jogadorY +=30; if(move_jogadorY+240 >= 399){ movimento_jogador = 2; } break; case 2: break; case 3: move_jogadorY -=30; if(move_jogadorY+190 = 399) move_jogadorY=156; else if(move_jogadorY+190 move_jogador2Y+240){ pts_jogador1++; }else{ direcao = 0; angulo = random(); } }else{ if(movebolax==390){ setPause(); } } movebolay += sin(angulo) * direcao_vertical * 5; if(movebolay+210 >= 388 || movebolay+200 move_jogador1Y+240){ pts_jogador2++; }else{ direcao = 1; angulo = random(); } }else{ if(movebolax==-195){ setPause(); } } movebolay += sin(angulo) * direcao_vertical * 5; if(movebolay+210 >= 388 || movebolay+200levando a bola a se mover com maior velocidade, o que, consequentemente, torna o jogo mais difícil. Portanto, diminuir o valor de 30 para 10 significa que a bola será atualizada mais frequentemente, aumentando sua velocidade no jogo. TEMA 2. ENTRADAS E SAÍDAS EM LINGUAGEM C Entrada (Leitura) pelo Console em C • Objetivo: Explorar métodos de entrada de dados (input) pelo console em C, abordando funções essenciais e práticas seguras. • Métodos de Leitura de Caracteres: o getchar(): Forma básica para ler um caractere. Limitação: armazena dados em buffer até o pressionamento de enter, o que pode reduzir a interatividade em ambientes com buffer de linha. o getch(): Alternativa para leitura de caracteres em ambientes Windows, permitindo leitura sem pressionar enter. Disponível na biblioteca conio.h, que não faz parte do padrão ANSI C. • Leitura de Strings: o gets(): Método inseguro para leitura de strings, com risco de buffer overflow. o fgets(): Alternativa segura a gets(), que permite limitar o número de caracteres lidos, evitando problemas de segurança. • Entrada Formatada com scanf(): o Utilizada para ler diferentes tipos de dados com formatação especificada (e.g., %s para strings, %d para inteiros). o Retorna o número de itens lidos, possibilitando o controle de formatos e tipos variados de entrada pelo usuário. o Observação: As funções apresentadas são voltadas para operações básicas de E/S pelo teclado e tela, sendo ideais para entradas simples em console e para aprendizado de manipulação de dados textuais sem gráficos. Questão 1. Considerando que a linguagem C utiliza funções específicas para realizar a entrada de dados (input), como getchar() e getch(), essas funções se comportam de diferentes maneiras na forma como lidam com a entrada de caracteres. A função getchar() lê caracteres do teclado e os armazena em um buffer até que o enter seja pressionado, enquanto getch() lê caracteres diretamente, sem ecoar na tela e sem armazenar em buffer de linha. Esses detalhes são importantes quando se deseja implementar programas que requerem interatividade imediata com o usuário. Com base na descrição das funções getchar() e getch(), qual é a principal diferença no comportamento dessas funções que afeta a interatividade do usuário em ambientes como UNIX? A. getchar() lê caracteres diretamente sem armazenar em buffer, enquanto getch() armazena em buffer até que o enter seja pressionado. B. Ambas as funções leem caracteres sem armazenar em buffer de linha, promovendo máxima interatividade. C. getchar() usa um buffer de linha, o que pode reduzir a interatividade, enquanto getch() faz a leitura dos caracteres de forma direta, sem exibi-los na tela nem utilizar um buffer de linha. D. getch() lê caracteres e armazena-os em um buffer até que o enter seja pressionado, e getchar() não utiliza buffer de linha. E. Ambas as funções ecoam caracteres na tela conforme são digitados pelo usuário. RESPOSTA: Enquanto getchar() utiliza um buffer de linha, aguardando o pressionamento da tecla enter para processar os dados, o que pode reduzir a interatividade, getch() lê caracteres diretamente sem ecoá-los na tela nem utilizar um buffer de linha, o que permite interação imediata. Portanto, A alternativa descreve com precisão a diferença no comportamento entre getchar() e getch() que influencia a interatividade do usuário. Saída pelo Console em C Principais funções de saída: • putchar(): o Exibe um único caractere no console. o Simples e ideal para manipulação de caracteres individuais, como em loops. • puts(): o É mais eficiente para exibir strings, adicionando automaticamente uma nova linha ao final. o Rápido e eficiente, especialmente em saídas simples e otimizadas. • printf(): o Oferece uma gama de opções de formatação para diversos tipos de dados, o que a torna extremamente versátil e poderosa para produzir saídas formatadas complexas, como números em formatos específicos, além de tabelas alinhadas e dados em notação científica. o Função mais útil para saídas formatadas. o Permite combinar texto e variáveis com controle preciso da formatação. Especificadores de formato usados em printf() • Tipos de dados : o %d ou %i: Inteiro decimal com sinal. o %u: Inteiro sem sinal. o %o: Inteiro em base octal. o %x/ %X: Inteiro em base hexadecimal (letras minúsculas/maiúsculas). o %f: Ponto flutuante (float ou double). o %e/ %E: Notação científica (letras minúsculas/maiúsculas). o %g/ %G: Seleciona automaticamente entre %fe %e. o %p: Endereço de memória (ponteiro). o %c: Caractere correspondente ao código ASCII. o %s: Sequência de caracteres (string). o %%: Imprimir o caractere %. Formatadores e alinhamento • Largura e precisão : o %05d: Preenche com zeros para garantir 5 dígitos. o %8d: Justifica à direita com largura de 8 espaços. o %-8d: Justifica à esquerda. • Exemplo de tabela formatada : #include int main(void) { int i; for (i = 1; i int main(void) { printf("Eu gosto %s de %c", "muito", 'C'); } // Imprime: Eu gosto muito de C • Exemplo de Alinhamento de valores: #include int main(void) { printf("justificado a direita: %8d\n", 100); printf("justificado a esquerda: %-8d\n", 100); } // Imprime: //justificado a direita: 100 //justificado a esquerda: 100 Resumo: As funções de saída em C, especialmente printf(), oferecem controle detalhado para formatar e alinhar dados, tornando as saídas mais legíveis e profissionais. Ferramentas como especificadas, largura e precisão são essenciais para criar apresentações organizadas em aplicativos. Atividade 2. Considerando as funções de saída de dados no console em C, qual delas seria a mais apropriada para gerar uma saída formatada que inclui strings, caracteres e números, permitindo especificar o formato de cada elemento incluído na saída? A. A função putchar() seria a mais apropriada, pois permite uma saída detalhada de caracteres individuais. B. A função puts() é a mais indicada, pois pode exibir strings inteiras e adicionar automaticamente uma nova linha no final. C. A função printf() é a escolha correta, pois permite a formatação complexa de strings, caracteres e números, com controle detalhado sobre o formato de cada elemento. D. A função fputs() deve ser usada, já que permite escrever em diferentes dispositivos de saída. E. Todas as funções são igualmente apropriadas para produzir saídas formatadas complexas. RESPOSTA: A função printf() é extremamente versátil, pois possibilita especificar formatos de maneira detalhada para combinar texto literal com variáveis e especificadores de formato para uma variedade de tipos de dados. Essa funcionalidade torna printf() ideal para saídas formatadas complexas, permitindo controle preciso sobre a apresentação de strings, caracteres e números. Roteiro de prática: Imagine que você está desenvolvendo um sistema para uma livraria que necessita de um programa para registrar novas aquisições de livros. Como o sistema deve solicitar ao usuário que insira o nome do livro, o autor e o número de páginas e, em seguida, confirmar a entrada, exibindo as informações de volta ao usuário? • Utilizando as funções de alto nível scanf() e printf(). • O programa pedirá ao usuário que digite o nome do livro, o nome do autor e o número de páginas. Vamos captar esses dados com a função scanf(). • Após a entrada dos dados, com o uso da função printf( ), o programa exibirá as informações inseridas de volta ao usuário, para confirmá-las. Esse processo ajuda a garantir que as informações digitadas estão exatas e completas, facilitando a gestão eficaz das novas aquisições da livraria. • A solução parao problema demonstra um uso das funções de entrada e saída em C que é prático, relevante e adequado para aplicações no mundo real, as quais requerem interação direta com o usuário. #include int main() { char livro[100], autor[100]; // Declara arrays de caracteres para armazenar nome do livro e autor int paginas; // Declara uma variável inteira para armazenar número de páginas printf("Digite o nome do livro: "); scanf("%99[^\n]", livro); // Lê 99 string até encontrar uma nova linha (permite adicionar espaços entre palavras) printf("Digite o nome do autor: "); scanf("%99s", autor); // Lê uma string até encontrar o espaço (não permite adicionar espaços entre palavras) printf("Digite o número de páginas: "); scanf("%d", &paginas); printf("\nVoce registrou o livro: '%s' de %s, com %d páginas.\n", livro, autor, paginas); return 0; } Questão3. Suponha que um sistema semelhante ao da livraria está em uso, mas os usuários reclamam que não conseguem inserir nomes de livros ou autores com espaços. Qual das seguintes modificações você deveria realizar no código para permitir a leitura de strings com espaços? A. Substituir %99s por %99[^\n] no scanf para livro e autor. B. Aumentar o tamanho do array para 200 caracteres. C. Usar gets() em vez de scanf(). D. Dividir o input em muitas variáveis. E. Nenhuma alteração é necessária. RESPOSTA: Para permitir que os usuários insiram nomes de livros ou autores que contenham espaços, você deve substituir %99s por %99[^\n] no scanf, pois isso permite a leitura de uma linha inteira até encontrar uma quebra de linha, incluindo espaços, adequando-se à necessidade de inserir nomes completos de livros e autores. Padrão ANSI/ISO C ❖ Definição e Importância do Padrão ANSI/ISO C • Padrões ANSI e ISO C : o Publicados pela ANSI, ISO e IEC para padronizar a linguagem C, promovendo portabilidade e eficiência. o Versões iniciais: C89 (ANSI C) e C90 (ISO C) . o Programas aderentes ao padrão podem ser compilados em diferentes plataformas com maior facilidade. • Benefícios do padrão: o Maior portabilidade entre compiladores. o Redução de dependências de hardware ou compiladores específicos. ❖ Configurações e Extensões do Compilador • Modo estrito : Garante conformidade total com o padrão C. • GNU C : o Não adere estritamente aos padrões da linguagem C, conhecida como GNU C não padrão. o Extensões GNU C são usadas no kernel do Linux, não adotam o padrão C puro. o Para garantir que um programa seja compilado de acordo com um padrão específico de C, como o C99 ou o C17, é necessário especificar isso na linha de comando do compilador. ❖ Origem e Evolução do C • Origens do C : o Nasceu após o fracasso do projeto Multics (1969), como parte do desenvolvimento do UNIX por Ken Thompson e Dennis Ritchie. o Evoluiu a partir da linguagem B (que não foi bem-sucedida devido às restrições de memória) e foi projetado para o PDP-11, com um sistema robusto de tipos. o Dennis Ritchie, aprimorou o B no PDP-11, criando o Novo B, que rapidamente evoluiu para o C, que era compilado e tinha um sistema robusto de tipos. o Em 1978, o compilador portátil de C (pcc) foi desenvolvido por Steve Johnson ; o A adoção generalizada dessa linguagem, conhecida como K&R C, demonstrou sua eficácia e popularidade. o A primeira versão do C, tinha limitações que complicavam o desenvolvimento e a portabilidade de programas. Para resolver isso, o ANSI estabeleceu um padrão para a linguagem C, que foi aprovado em 1989. o O padrão ANSI C melhorou a eficiência e a portabilidade do C por meio da clarificação de tipos de dados e da definição de funções de biblioteca padrão, além de um sistema de arquivos independente do sistema operacional. • Evolução histórica : o 1972-1989 (K&R C): Foi a primeira versão do C. O nome K&R C é uma referência associada ao livro The C Programming Language . o 1989 (C89) : Foi o primeiro padrão oficial pelo ANSI. o 1990 (C90) : Adoção internacional pela ISO. o 1995 (C95) : Introduziu uma pequena atualização, que permitiu suporte ampliado a caracteres. o 1999 (C99) : Revisão significativa, com introdução a suporte a múltiplos processamentos e novas capacidades de threading. o 2011 (C11) : Introduziu funcionalidades avançadas, como suporte nativo a multi-threading e outras melhorias modernas. o 2017/2018 (C17/C18) : Última versão, focada em correções de erros e estabilidade. • Estrutura e Bibliotecas no Padrão C • A linguagem é enxuta e depende de bibliotecas padrão para funções adicionais. o Exemplo: stdio fornece funções como printf(). • Bibliotecas externas ou não padrão (ex.: GNU C) ampliam funcionalidades, mas podem limitar a portabilidade. • Impacto do Padrão ANSI C • Melhorou a eficiência e portabilidade por meio de: o Definição clara de tipos de dados. o Estabelecimento de funções padrão e sistema de arquivos independentes do SO. • Adoção generalizada consolidou o C como linguagem fundamental na programação moderna. Resumo: O padrão ANSI/ISO C unificou e padronizou a linguagem C, facilitando seu uso em diferentes plataformas e garantindo portabilidade. A evolução contínua do C reflete sua relevância no desenvolvimento de sistemas e software, com versões que atendem às crescentes demandas de computação. Atividade 1. A evolução da linguagem de programação C, desde sua concepção até os padrões modernos, é um exemplo fascinante de desenvolvimento tecnológico e colaboração. A jornada começou nos anos 1960 e 1970, quando Ken Thompson e Dennis Ritchie criaram a linguagem, inicialmente para uso no sistema operacional UNIX, no PDP-7. Esse desenvolvimento inicial, embora promissor, logo encontrou limitações que necessitavam de melhorias significativas, culminando na criação dos padrões ANSI e ISO para garantir a portabilidade e eficiência. Qual foi o impacto significativo do padrão ANSI C, estabelecido em 1989, na evolução da linguagem de programação C? A. O padrão ANSI C introduziu a programação orientada a objetos, o que revolucionou a escrita de software em C. B. O padrão ANSI C, aprovado em 1989, melhorou a eficiência e a portabilidade do C por meio da clarificação de tipos de dados e das definições de funções de biblioteca. C. ANSI C reduziu a popularidade da linguagem C ao impor restrições rígidas que limitaram a criatividade dos programadores. D. ANSI C foi responsável pela adição de recursos de processamento gráfico avançado, alinhando C com linguagens, como Python e Java. E. O padrão estabelecido em 1989 removeu características essenciais, como ponteiros e gerenciamento manual de memória, para simplificar a linguagem. Resposta: O padrão ANSI C, estabelecido em 1989, foi um marco significativo na evolução da linguagem de programação C. A padronização realizada pelo ANSI trouxe várias melhorias que impactaram profundamente a forma como a linguagem era utilizada e desenvolvida. Padrão UNIX 1. Definição e características principais do UNIX • Sistema operacional reconhecido por: o Simplicidade e modularidade . o Ferramentas de linha de comando altamente eficientes. o Implementação inicial em assembly e posteriormente reescrita em C, tornando-se o primeiro sistema operacional escrito nessa linguagem. 2. O Papel do POSIX • Definição : Portable Operating System Interface (POSIX), introduzida em 1988, é um padrão que: o Uniformiza chamadas de sistema entre variantes do tipo Unix. o Facilita a portabilidade de programas em múltiplos ambientes. o Amplia as capacidades do ANSI-C para criar uma interface rica em programação. • Importância : o Possibilidade de aplicativos desenvolvidos sob o padrão POSIX rodarem em qualquer sistema operacional compatível. o Padroniza as interfaces de programação, mesmo em sistemas com diferenças importantes, como Windows (WinAPI). 3. Programação de Sistema noPadrão UNIX • Componentes fundamentais : o Interpretadores de linha de comando. o Funções de entrada e saída (como stream I/O em C). o Compiladores, carregadores e sistemas operacionais. • Shells como exemplos de programas em C : o Bourne Shell (sh) : Simplicidade e robustez, amplamente utilizada para scripts e automação. o C Shell (csh) : Sintaxe inspirada em C, com funcionalidades adicionais (histórico de comandos e variáveis). o Outros shells também baseados em C oferecem interatividade e funcionalidades extensíveis. • Acesso ao Kernel : o Feito via chamadas de sistema (system calls ), que permitem aos programas acessar recursos fundamentais de forma eficiente. o Essas chamadas são geralmente rompidas como funções em C e documentadas no manual do programador UNIX: ▪ Seção 2 : Interface das chamadas do sistema. ▪ Seção 3 : Funções de uso geral que podem utilizar chamadas do sistema. 4. Impacto do ANSI-C e POSIX • O ANSI-C expandiu e o POSIX transformou o C em uma poderosa "máquina virtual", que: o Executa instruções de alto nível. o Acesse recursos do sistema operacional de maneira eficiente. • Evolução na década de 1980 : o A diversificação de versões do UNIX levou à necessidade de padronização, resultando nos padrões ANSI-C e POSIX. 5. Comparação com Outros Sistemas • Enquanto o POSIX visa padronizar interfaces do tipo Unix, sistemas como o Windows utilizam APIs próprias, como o WinAPI, que introduzem variações em cada nova versão. Resumo: O padrão UNIX e o POSIX representam marcos importantes na uniformização e na eficiência da programação de sistemas. Juntos, eles garantem portabilidade, acessibilidade aos recursos do sistema operacional e um ambiente de programação robusto e consistente para aplicações modernas. Atividade 2. A linguagem de programação C desempenha papel crítico em sistemas operacionais, proporcionando uma interface padrão que aumenta a portabilidade e a funcionalidade em diversos ambientes computacionais. Com a adoção do padrão POSIX, um marco em 1988, a linguagem C estendeu sua aplicabilidade, permitindo uma interface de programação mais rica, que abrange várias plataformas de sistema operacional. Esse padrão busca unificar e facilitar o desenvolvimento de software em diferentes sistemas Unix-like, estendendo a flexibilidade do ANSI-C. Qual é o papel do padrão POSIX na interface de programação de sistemas operacionais? A. O padrão POSIX permite a criação exclusiva de interfaces gráficas de usuário, diferenciando-o completamente de outras APIs, como WinAPI. B. O POSIX foca primariamente aumentar a segurança dos sistemas operacionais, restringindo o acesso ao nível do kernel por meio de chamadas de sistema padronizadas. C. A principal função do POSIX é substituir completamente o ANSI-C, estabelecendo um novo padrão para todas as linguagens de programação, e não apenas o C. D. O POSIX elimina a necessidade de usar a linguagem C para programação de sistemas, promovendo outras linguagens mais modernas, como Python e Java. E. POSIX padroniza chamadas de sistema para unificar diversas versões do Unix, permitindo que aplicações escritas para um sistema possam ser executadas em outros com suporte POSIX. Resposta: O padrão POSIX foi criado para uniformizar as interfaces de sistemas operacionais, especificamente as chamadas de sistema, a fim de garantir que programas desenvolvidos para um ambiente Unix-like sejam compatíveis com outros que também implementaram esse padrão. E/S ANSI/ISO C versus UNIX C 1. Critérios para Escolha • E/S ANSI/ISO C : o Base padronizada e amplamente suportada. o Ideal para portabilidade entre diferentes sistemas operacionais. o Alinha-se a padrões internacionais. • E/S UNIX C : o Oferece acesso direto e otimizado às funcionalidades específicas de sistemas do tipo Unix. o Preferível para desempenho elevado e integração direta com o kernel Unix. • A decisão de adoção depende de : o Ambiente-alvo. o Requisitos de desempenho. o Necessidade de conformidade com padrões. 2. Diferenças entre E/S ANSI/ISO C e E/S UNIX C • E/S ANSI/ISO C (Alto nível): o Baseada em funções como fopen, fprintf, getc. o Utiliza buffer intermediário, que gerencia o fluxo de dados em diferentes níveis de abstração. o Melhor para manipulações de dados ou arquivos e com maior abstração. • E/S UNIX C (baixo nível): o Baseada diretamente em chamadas de sistema, como open, write, read. o Sem buffer intermediário , com controle direto sobre os dados. o Ideal para aplicações que exigem alto desempenho e interação direta com o sistema operacional. 3. Exemplos de Implementação • E/S de Alto Nível : o Abordagem com fprintf, manipulando dados formatados. FILE *file = fopen("exemplo.txt", "w"); // Aberto para escrita fprintf(file, "Hello, %s!\n", "World"); • E/S de Baixo Nível : o Abordagem com write(), oferecendo maior controle sobre os dados binários. int fd = open("exemplo.txt", O_CREAT | O_WRONLY | O_TRUNC, 0644); const char *text = "Hello, World!\n"; ssize_t bytes_written = write(fd, text, strlen(text)); 4. Diferenças Técnicas Fundamentais • E/S de Baixo Nível : o Possibilidade de ativar E/S direto (O_DIRECT) , ignorando o cache de páginas. o Proporciona acesso direto ao sistema de entrada/saída. • E/S Tradicional (Alto Nível) : o Use o Page Cache do kernel (ex.: write-back ). o Dados gravados inicialmente no cache e sincronizados com o disco posteriormente. o Otimiza acessos ao disco, mas pode impactar a velocidade de leitura/escrita. 5. Considerações de Desempenho • E/S de Baixo Nível : o Indicado para aplicações críticas em desempenho. o Controle detalhado dos modos/sinalizadores de operação. • E/S Tradicional : o Melhor para aplicações que exigem simplicidade e facilidade de uso. o Beneficiada pelo gerenciamento de cache automático do kernel. Atividade 3. A linguagem C é notória por seu papel integral no desenvolvimento do sistema operacional UNIX e em seu e funcionamento, proporcionando uma interface direta e eficiente para manipulação de recursos do sistema. Desde os anos 1980, a linguagem tem sido amplamente utilizada para adicionar novos serviços ao kernel Unix, refletindo sua capacidade de lidar com operações críticas, como gerenciamento de recursos e suporte à rede. A interação entre o padrão ANSI C e as especificações POSIX permitiu que o C oferecesse interfaces de programação compatíveis com UNIX em qualquer ambiente que suporte POSIX. Como o padrão POSIX influenciou a capacidade do C de interagir com funções do sistema operacional UNIX? A. A inclusão das especificações POSIX no padrão C permitiu que ele oferecesse uma interface semelhante à UNIX como serviço opcional, aumentando sua portabilidade entre diferentes sistemas operacionais. B. O padrão POSIX permitiu a criação de novas chamadas de sistema específicas para C, que antes eram incompatíveis com outros sistemas operacionais além do Unix. C. Ao alinhar-se ao POSIX, o C foi limitado em suas capacidades de interação com o sistema operacional, sendo restrito apenas a funções não essenciais do kernel Unix. D. O POSIX restringiu o uso de C para aplicações de baixo nível, como a manipulação direta de hardware, o que reduziu sua relevância em sistemas operacionais modernos. E. Com o POSIX, o C se tornou obsoleto, sendo substituído por linguagens de programação mais modernas que oferecem melhor suporte a interfaces gráficas e multimídia. Resposta: A inclusão das especificações POSIX no padrão C teve impacto significativo na portabilidade da linguagem e em sua utilidade. O padrão POSIX (portable operating system interface) foi desenvolvido para garantir a compatibilidade entre diferentes sistemas operacionais que se baseiam em UNIX, proporcionando uma base comum para a programação de sistemas. Fundamentos do Sistema de Arquivos em C 1. Conceitos Básicos • Arquivo : Unidade lógicade dados criada por processos. • Sistema de Arquivos (FS) : Organiza e facilita o acesso aos arquivos, permitindo compartilhamento eficiente entre aplicativos e acesso aleatório rápido. • Exemplos de sistemas de arquivos: o FAT e exFAT : Desenvolvidos pela Microsoft para dispositivos de armazenamento. o NTFS : Padrão do Windows, com suporte a grandes arquivos e recursos avançados de segurança. o ext4 : Comum em distribuições Linux, projetado para desempenho e escalabilidade. • Funções em C permitem operações básicas em arquivos, como criar, ler, escrever e mover. 2. Tipos de Arquivos • Arquivos de Texto : o Salvos com extensão .txt. o Use caracteres ASCII. o Ideais para salvar textos simples. • Arquivos Binários : o Armazenam dados no formato binário (sequência de 0s e 1s). o Usados para informações numéricas como int, floate double. 3. Operações em Arquivos • Principais Funções : o fopen: Abre um arquivo existente. o fclose: Fecha um arquivo. o fscanf ou fgets: Lê um arquivo. o fprintf ou fputs: Escrever em um arquivo o fseek ou rewind: Move o ponto de vista dentro do arquivo para uma posição especifica. • Modos de Abertura : o r : Leitura. o rb : Leitura em modo binário. o w : Escrita (cria ou sobrescreve). o wb : Escrita em modo binário. o a : Adição ao final. o ab : Adição em modo binário. o Combinações: r+, w+, a+, etc., permitem leitura e escrita, em modos binários ou normais. 4. Uso e Modos • Modos como rb, wb, ab, etc., são usados para manipular arquivos binários. • Escolher o modo correto é essencial para atender às necessidades específicas de leitura, escrita ou modificação dos arquivos. 5. Ponteiro de Arquivo • Declaração: Para trabalhar com arquivos, usamos um ponteiro especial (ponteiro de arquivo), que pode ser declarado da seguinte forma: FILE *file_ptr; file_ptr = fopen("fileName.txt", "w"); • Ponteiro de Arquivo é usado para manipular arquivos e suas operações. Atividade 1. Em um curso de programação em C, um estudante está usando essa linguagem para aprender sobre a manipulação de arquivos. Durante uma sessão de laboratório, ele precisa criar um programa que lide com arquivos binários para armazenar e recuperar dados numéricos. O aluno entende os fundamentos do sistema de arquivos e sabe que precisa escolher o modo correto de abertura para completar sua tarefa com sucesso. Considerando que o estudante pretende ler e escrever dados em um arquivo binário existente, qual dos modos de abertura de arquivo ele deve utilizar para abrir o arquivo corretamente? A. r B. rb C. w+ D. rb+ E. ab Resposta: rb+: abre o arquivo para leitura e escrita em formato binário, mantendo o conteúdo existente. Isso permite que o estudante leia e escreva dados numéricos no arquivo sem perder as informações já armazenadas. Esse modo é ideal para manipular arquivos binários em que é necessário preservar o conteúdo ao abrir o arquivo. E/S por Arquivos de Texto em C 1. Importância • Manipular arquivos de texto é fundamental para criar, modificar e gerenciar dados persistentes. • A biblioteca stdio.h em C fornece funções essenciais para essas operações. 2. Abertura e Criação de Arquivos • Função fopen() : o Sintaxe: FILE *p = fopen("nome_do_arquivo", "modo_de_abertura"); o Exemplo: FILE *p = fopen("Hello.txt", "r"); o Modos comuns: ▪ "r": Leitura (arquivo deve existir). ▪ "w": Escrita (cria ou sobrescreve). ▪ "a": Anexação (adiciona ao final ou cria, se não existir). • Exemplo para criar um arquivo: FILE *p = fopen("./Hello.txt", "w"); 3. Escrita em Arquivos • Função fprintf() : Escrever dados formatados em um arquivo. • Função fputs() : Inserir strings diretamente. • Exemplo: // Programa em C para escrever em um arquivo #include #include int main() { FILE *ptr; // Ponteiro para o arquivo ptr = fopen("./Hello.txt", "w+"); // Abre o arquivo para escrita if (ptr == NULL) { printf("Erro ao escrever no arquivo!"); // Imprime no terminal se houver erro exit(1); } char str[] = "Dados a serem inseridos no arquivo."; // Dados a serem inseridos no arquivo fputs(str, ptr); // Escreve os dados no arquivo fclose(ptr); // Fecha o arquivo printf("Dados escritos no arquivo\n\n"); // Imprime no terminal return 0; } 4. Leitura de Arquivos • Função fgets() : o Lê linhas de texto até um limite de caracteres ou uma quebra de linha. o O arquivo deve ser fechado ao final do processo para liberar recursos. o Exemplo: #include #include int main() { char str[80]; // Declara uma string de 80 caracteres FILE* ptr; // Declara um ponteiro para o arquivo ptr = fopen("Hello.txt", "r"); // Abre o arquivo Hello.txt para leitura if (ptr == NULL) { printf("Erro ao abrir o arquivo"); // Exibe uma mensagem de erro se o ponteiro retornar NULL exit(1); } if (fgets(str, 80, ptr) != NULL) { // Le uma string do arquivo e armazena em str puts(str); // Exibe a string lida } fclose(ptr); // Fecha o arquivo return 0; } 5. Boas Práticas • Verifique se o arquivo foi aberto corretamente ( ptr == NULL). • Fechar o arquivo após a manipulação para garantir a liberação de recursos. Essa estrutura básica oferece uma base sólida para manipulação eficiente de arquivos de texto em C, sendo aplicável para leitura e escrita de dados em projetos de diversas complexidades. Atividade 2. Em um curso de programação, uma aluna está aprendendo a manipular arquivos em C. Ela compreende a utilização da função fopen() para abrir ou criar arquivos e está familiarizada com os diferentes modos de abertura e suas especificidades. Durante um exercício prático, ela deve escolher o modo de abertura correto para executar uma operação específica no arquivo Hello.txt. Se o objetivo da estudante é verificar se o arquivo Hello.txt existe e, em caso negativo, criá-lo sem escrever imediatamente nele, qual modo de abertura de arquivo ela deve usar com a função fopen(), a fim de minimizar o risco de perder dados existentes? A. r B. w C. a D. r+ E. w+ RESPOSTA: O modo a (anexação) abre o arquivo para escrita no final do conteúdo existente, se ele existir, ou criar um novo se ele não existir. Esse modo não sobrescreve o conteúdo existente, minimizando o risco de perda de dados E/S por Arquivos Binários em C 1. Características dos Arquivos Binários • Armazenamos dados em formato bruto, diferentemente dos arquivos de texto que são legíveis por humanos. • Proporcionam maior eficiência, rapidez e economia de espaço em operações de leitura e escrita. 2. Escrita em Arquivos Binários o Função fwrite() : Escrever blocos de dados diretamente no arquivo binário. o Sintaxe: fwrite(const void *ptr, size_t tamanho_dos_elementos, size_t numero_de_elementos, FILE *arquivo); o Exemplo de uso: // Programa C para gravar em um arquivo binário #include #include // Declara uma estrutura struct Num { int n1, n2; // Declara duas variáveis do tipo inteiro }; int main() { struct Num obj; // Declara uma variável do tipo estrutura FILE *fptr; // Declara um ponteiro do tipo FILE if ((fptr = fopen("temp.bin", "wb")) == NULL) { printf("Erro ao abrir o arquivo"); // Verifica se o arquivo pode ser aberto exit(1); } // Grava os dados no arquivo for (int n = 1; nBinários • Função fread() : o Lê blocos de dados do arquivo binário. o Sintaxe: fread(void *ptr, size_t tamanho_dos_elementos, size_t numero_de_elementos, FILE *arquivo); o Exemplo de uso: #include #include struct Num { int n1, n2; }; int main() { struct Num obj; FILE *fptr; if ((fptr = fopen("temp.bin", "rb")) == NULL) { printf("Erro ao abrir o arquivo"); exit(1); } for (int n = 1; n int main() { FILE *fpTexto, *fpBinario; char nome[100]; int contador = 0; // Abertura dos arquivos fpTexto = fopen("participantes.txt", "a+"); fpBinario = fopen("contador.bin", "rb+"); if (fpBinario == NULL) { // Arquivo binário não existe, criá-lo fpBinario = fopen("contador.bin", "wb+"); fwrite(&contador, sizeof(int), 1, fpBinario); } else { // Ler o contador existente fread(&contador, sizeof(int), 1, fpBinario); } printf("Digite o nome do participante: "); gets(nome); // Usando gets para simplificação, ideal usar fgets fprintf(fpTexto, "%s\n", nome); // Escreve o nome no arquivo de texto contador++; rewind(fpBinario); fwrite(&contador, sizeof(int), 1, fpBinario); // Atualiza o contador no arquivo binário // Fechamento dos arquivos fclose(fpTexto); fclose(fpBinario); printf("Total de participantes registrados: %d\n", contador); return 0; } Questão 6. Imagine que o programa de nossa demonstração prática esteja em uso. Qual modificação deveria ser realizada para a leitura do nome no programa sem prejudicar que o nome dos participantes possa incluir espaços? A. Alterar gets(nome) para scanf("%[^\n]", nome); B. Mudar de FILE * para fstream. C. Usar puts(nome); em vez de gets(nome); D. Implementar uma função personalizada para leitura de strings. E. Nenhuma das alterações é necessária. RESPOSTA: O uso de scanf("%[^\n]", nome); permite a leitura de uma linha completa, incluindo espaços, até encontrar uma nova linha, o que resolve a limitação da função gets(nome). A função gets() lê uma linha inteira de entrada até encontrar um caractere de nova linha ('\n'). No entanto, gets() foi removida das versões mais recentes do C devido a problemas de segurança relacionados a estouro de buffer. Além disso, gets() não oferece proteção contra strings longas demais para o buffer. TEMA 3. PORTAS DE HARDWARE E COMUNICAÇÃO EM C PORTA SERIAL E PORTAS DE MICROCONTROLADORES ❖ Acesso à Porta Serial RS232 1. Contexto e Relevância • Criada no início da computação, a RS232 foi crucial para conectar dispositivos periféricos como modems e mouses. • Apesar do declínio com o advento do USB, mantém relevância em: o Ambientes industriais pela robustez. o Integração com sistemas legados e microcontroladores. 2. Características do Protocolo RS232 • Comunicação serial com transferência sequencial de dados. • Continua utilizada em automação industrial, eletrônica e robótica. • Pinos principais do conector DB-9: o TXD (Transmit Data): Envia dados sequencialmente. o RXD (Receive Data): Recebe dados para comunicação bidirecional. o SG (Signal Ground): Referência de terra. 3. Uso de Conversores USB-Serial • Solução para dispositivos modernos sem portas RS232. • Facilitam: o Comunicação entre equipamentos legados e dispositivos USB. o Desenvolvimento e depuração de hardware (ex.: microcontroladores). o Diagnósticos e programação via USB. 4. Relação entre RS232 e UART • UART (Universal Asynchronous Receiver/Transmitter): o Componente de hardware que converte dados entre formas serial e paralela. o Transmissão: Dados paralelos → sequência serial. o Recepção: Sequência serial → dados paralelos. • RS232: o Protocolo que define os níveis de voltagem e a transmissão efetiva dos dados. o Complementa o UART, permitindo comunicação entre dispositivos. 5. Vantagens do RS232 • Confiável em condições adversas: Resistência a altas temperaturas e interferência eletromagnética. • Simplicidade e baixo custo de manutenção. • Capacidade de operar diretamente com equipamentos antigos, sem necessidade de redesenho ou conversores. Conclusão: A RS232 segue como uma solução robusta e acessível para integraçãoentre tecnologias modernas e sistemas legados, especialmente em setores industriais e de automação. ❖ Acesso às Portas de Microcontroladores 1. Função dos Microcontroladores • Microcontroladores combinam CPU, memória e circuitos de comunicação em um único chip. • Usados em aplicações diversas como eletrodomésticos inteligentes e sistemas industriais. • O conhecimento prático das portas de E/S (entrada/saída) é crucial para engenheiros e desenvolvedores em TI. 2. Portas de Entrada e Saída (E/S) • Função: Permitem interação com o ambiente externo, coletando dados de sensores e controlando dispositivos. • Tipos principais: o Digitais: Leem e escrevem dois estados (alto/baixo), ideais para dispositivos simples (LEDs, relés, interruptores). o Analógicas: Lêem uma gama de valores, úteis para dados de sensores (temperatura, luz, pressão). 3. Exemplos de Portas e suas Capacidades • Portas Digitais: o No Arduino UNO, 14 pinos digitais, configuráveis como entrada ou saída com funções como pinMode(), digitalWrite(), e digitalRead(). • Portas Analógicas: o No Arduino UNO, 6 pinos analógicos (valores de 0 a 1023, voltagem de 0 a 5V). o No ESP32, suporte para mais pinos e maior resolução. 4. Portas Seriais e Barramentos • Portas Seriais (TX/RX): Facilitam a comunicação entre microcontroladores e outros dispositivos. • Barramentos: o I2C: Conecta múltiplos dispositivos de baixa velocidade (usa dois fios: SCL e SDA). o SPI: Barramento mais rápido que usa quatro fios: MISO, MOSI, SCK e SS (cada dispositivo requer pino SS separado). Conclusão: O acesso eficiente às portas de E/S em microcontroladores é fundamental para interagir com sensores e dispositivos, além de viabilizar a comunicação entre sistemas. A compreensão dos tipos de portas e barramentos disponíveis, como digitais, analógicas, TX/RX, I2C e SPI, permite o desenvolvimento de soluções em sistemas embarcados. CONTROLE DE DISPOSITIVOS E AQUISIÇÃO DE DADOS COM MICROCONTROLADORES ❖ Controle de Dispositivos com Microcontroladores 1. Papel dos Microcontroladores • Processam dados de sensores e controlam atuadores em diversos contextos. • São essenciais em projetos de eletrônica e automação, oferecendo controle preciso por meio de técnicas como modulação por largura de pulso (PWM). 2. Formas Usuais de Controle • Controle de sensores e atuadores: Por exemplo, um Arduino pode ativar uma bomba com base em leituras de umidade do solo. • Automação residencial: O ESP32 permite controle remoto de dispositivos domésticos via Wi-Fi. • Comunicação entre dispositivos: Microcontroladores como o ESP32 enviam dados de sensores para servidores via Bluetooth ou Wi-Fi. • Robótica: Controlam motores e processam informações de sensores para ações autônomas. • Interfaces Homem-Máquina (IHM): Usados em displays e painéis de controle para interação com usuários. 3. Controle por Modulação por Largura de Pulso (PWM) • Definição: Técnica para regular energia fornecida a dispositivos eletrônicos ajustando o ciclo de trabalho de uma onda quadrada. o Ciclo de trabalho: Porcentagem do tempo em que o sinal está em nível alto em um ciclo. o Frequência: Número de ciclos por segundo, que afeta o comportamento do dispositivo. • Vantagens: Permite controle eficiente da energia e otimiza o uso de recursos. 4. Aplicações do Controle PWM • Motores DC: Regulação de velocidade, essencial em robótica e sistemas industriais. • Iluminação LED: Ajuste de brilho sem alterar tensão, usado em painéis, decoração e iluminação automotiva. • Controle de temperatura: Regulação precisa em elementos de aquecimento, útil em aplicações industriais e comerciais. 5. Implementação de Controle PWM com Microcontroladores • IDEs como a Arduino IDE simplificam a geração de sinais PWM. o Exemplo: A função analogWrite(pin, value) ajusta o ciclo de trabalho para controlar dispositivos. • Microcontroladores tornam o desenvolvimento de sistemas PWM acessível e eficiente. ❖ Aquisição de Dados com Microcontroladores 1. Importância da Aquisição de Dados • Essencial para setores industriais, científicos e cotidianos, permitindo coleta e análise de informações. • Microcontroladores atuam como interfaces entre dados físicos e digitais. • Integram sensores, realizam protocolos complexos (como I2C) e conversões analógico-digitais (ADC), viabilizando sistemas inovadores. 2. Papel dos Microcontroladores • Conversores Analógico-Digitais (ADCs): Transformam sinais analógicos (ex.: temperatura) em dados digitais processáveis. • Monitoramento contínuo: Integram dados de múltiplas fontes e suportam controle eficiente em sistemas embarcados. 3. Exemplos Práticos de Sensores • Sensor PIR (movimento) o Detecta radiação infravermelha, mudando o estado entre ON e OFF ao identificar movimento. o Código: Controla um LED que pisca ao detectar movimento • Sensor BMP180 (pressão e temperatura) o Opera com protocolo I2C, calcula altitude e é útil em drones, meteorologia e fitness. o Código: Usa biblioteca Adafruit para obter temperatura, pressão e altitude. • Sensor TMP36 (temperatura analógica) o Baixo custo, saída em tensão proporcional à temperatura. o Código: Converte tensão em temperatura e exibe via comunicação serial. 4. Protocolos e Ferramentas • I2C: Ideal para comunicação com múltiplos dispositivos de baixa velocidade. • ADC: Fundamental para processar dados contínuos, como sinais de temperatura ou luz. • IDEs, como Arduino IDE, fornecem bibliotecas e funções que facilitam a programação e integração de sensores. 5. Aplicações e Benefícios • Indústria e automação: Monitoramento contínuo de processos. • Projetos tecnológicos: Controle preciso e integração de múltiplos dispositivos. • Custo e eficiência: Sensores acessíveis e programação simplificada aumentam a viabilidade de soluções práticas. ❖ Simulador de Microcontrolador Arduino (Tinkercad) 1. Importância dos Simuladores de Microcontroladores • Facilitam o aprendizado e desenvolvimento em sistemas embarcados e eletrônica. • Permitem simulação e programação de microcontroladores sem necessidade de hardware físico. • Ideais para educação e experimentação prática em um ambiente seguro. 2. Tinkercad: Visão Geral • Plataforma online gratuita desenvolvida pela Autodesk. • Simula circuitos eletrônicos e permite a programação de microcontroladores, como o Arduino. • Interface intuitiva adequada para iniciantes e profissionais. • Oferece: o Ambiente de desenvolvimento integrado (IDE). o Recursos educacionais e comunidade ativa para suporte. o Capacidade de criar, simular e compartilhar projetos. • Transição para hardware físico: o Códigos simulados podem ser transferidos para a IDE do Arduino com poucas alterações, facilitando a implementação em dispositivos reais. 3. Etapas para Usar o Tinkercad 1. Criação de conta: o Acesse tinkercad.com e clique em Join Now ou Sign Up. 2. Acesso à área de circuitos: o Faça login, escolha Circuits e clique em Create new Circuit. 3. Configuração do ambiente: o Arraste componentes, como o Arduino, para a área de trabalho. 4. Montagem do circuito: o Adicione componentes (ex.: LEDs, resistores) e conecte-os usando as ferramentas de fiação. 5. Programação do Arduino: o Escreva ou cole o código em C/C++ na aba Code. Exemplos estão disponíveis. 6. Simulação: o Clique em Start Simulation para testar o circuito. o Ajuste o código e o design conforme necessário e repita a simulação. 4. Benefícios do Tinkercad • Acessibilidade: Não requer componentes físicos, reduzindo custos e complexidade. • Educação prática: Permite experimentação e aplicação de conceitos teóricos. http://tinkercad.com/ • Colaboração: Possibilita compartilhar projetos com outros usuários e aprender com a comunidade. SOCKETS E CLIENTE/SERVIDOR TCP E UDP ❖ Introdução aos Socketsna Linguagem C 1. Importância dos Sockets • São fundamentais para comunicação em rede bidirecional entre dois programas, seja na mesma máquina ou em máquinas diferentes. • Permitem manipulação de dados em redes, facilitando integração entre sistemas. • A linguagem C usa APIs específicas para criar e gerenciar conexões, como: o POSIX (para sistemas Unix). o Winsock (para Windows). 2. Conceito de Sockets • São pontos finais de comunicação em uma rede, associando-se a: o Endereço IP: Identifica o dispositivo. o Porta: Direciona o tráfego ao aplicativo correto. • Funcionam como "caixas de correio" da rede, recebendo e enviando dados através de portas específicas. 3. APIs de Sockets em C As APIs fornecem regras e funções para criar e gerenciar conexões de rede. • API POSIX (Unix/Linux/macOS): o Baseada nos sockets BSD (Berkeley Software Distribution). o Oferece um conjunto uniforme de funções para redes TCP/IP: ▪ Criação de sockets. ▪ Configuração de portas específicas. ▪ Aceitação de conexões. ▪ Envio/recebimento de mensagens (síncrono ou assíncrono). o Facilita portabilidade de código entre sistemas baseados em Unix. • API Winsock (Windows): o Inspirada nos sockets BSD, mas adaptada ao modelo do Windows. o Oferece funcionalidades semelhantes às da POSIX, com características adicionais: ▪ Integração com o modelo de mensagens do Windows. ▪ Operações específicas para redes assíncronas. o Facilita a transição de aplicativos de Unix para Windows, promovendo consistência no código. 4. Analogias para Entendimento • Rede como sistema de correios: o Dispositivos: Casas. o Endereços IP: Endereços físicos. o Portas: Caixas de correio, direcionando informações ao destino correto. 5. Aplicabilidade • Compreender e usar sockets em C ajuda programadores a: o Criar aplicativos de rede eficientes. o Unir teoria à prática no desenvolvimento de sistemas embarcados e comunicação em rede. ❖ Transferência de Dados com Sockets 1. Importância dos Sockets na Transferência de Dados • Facilitam a comunicação entre cliente e servidor, garantindo eficiência e velocidade. • Permitem interoperabilidade entre sistemas em tecnologias web e móveis. • Atuam como um "tubo" que conecta cliente e servidor, permitindo o fluxo bidirecional de dados. 2. Exemplos de Aplicações Práticas • Chats on-line: Mensagens são transmitidas rapidamente entre dispositivos usando sockets. • Jogos on-line: Sockets sincronizam ações de jogadores em tempo real, garantindo fluidez. • Transmissão de vídeo ao vivo: Pacotes de dados são enviados do servidor para dispositivos, possibilitando streaming em tempo real. 3. Ordem dos Bytes na Comunicação via Sockets • Refere-se à forma como os bytes de dados numéricos são organizados na memória. o Big-endian: Byte mais significativo (MSB) é armazenado no menor endereço de memória. o Little-endian: Byte menos significativo (LSB) é armazenado no menor endereço de memória. 4. Relevância da Ordem dos Bytes • Diferentes arquiteturas podem usar ordens de bytes distintas. • Para garantir que os dados sejam interpretados corretamente em redes heterogêneas, é adotada a ordem de bytes da rede (big-endian). • Conversões entre a ordem do host e a ordem da rede são necessárias para: o Enviar dados na ordem de bytes correta. o Receber e interpretar dados com precisão. 5. Funções para Conversão de Ordem de Bytes • htonl() (host to network long) e ntohl() (network to host): Convertem longos inteiros (32 bits), como um endereço IPv4, de ordem de bytes do host para ordem de bytes da rede e vice-versa. • htons() (host to network short) e ntohs() (network to host short): Convertem inteiros curtos (16 bits), como a porta TCP/UDP, de ordem de bytes do host para ordem de bytes da rede e vice-versa. 6. Garantia de Consistência • O uso dessas funções assegura a compatibilidade entre sistemas de diferentes arquiteturas, evitando distorções nos dados transmitidos. ❖ Cliente/Servidor, TCP e UDP 1. Modelo Cliente/Servidor • Definição: Relacionamento em que o cliente solicita recursos ao servidor, que processa e retorna os dados. o Exemplo: Navegar em um site envolve o cliente (navegador) solicitando dados ao servidor web. • Analogia: Cliente em um restaurante faz um pedido ao garçom (servidor), que entrega a solicitação após o processamento. 2. Protocolos de Transporte: TCP e UDP • TCP (Transmission Control Protocol): o Características: ▪ Confiável: Garante entrega e ordem dos pacotes. ▪ Confirmação de recebimento e reorganização de pacotes. ▪ Controle de fluxo para evitar sobrecarga no receptor. o Usos: ▪ Transferências de arquivos. ▪ Navegação na web. ▪ Envio de e-mails. • UDP (User Datagram Protocol): o Características: ▪ Rápido e leve: Sem garantia de entrega ou ordem. ▪ Foco na velocidade, sacrificando confiabilidade. o Usos: ▪ Streaming de vídeo e áudio. ▪ Jogos on-line em tempo real. 3. Sockets: Pontos de Conexão • Definição: Conectores virtuais na camada de transporte que possibilitam a comunicação entre cliente e servidor. • Cada socket é identificado por: o Endereço IP. o Número de porta. • Funcionamento em ambiente cliente/servidor: o O servidor "escuta" em uma porta/IP específicos. o O cliente usa o IP/porta do servidor para estabelecer conexão via socket. • APIs de Sockets: o Trabalham com a camada de transporte (TCP/UDP) para gerenciar confiabilidade e sequência dos pacotes. 4. Relação entre Camadas de Rede e Sockets • Camada de Aplicação: o Onde o usuário interage com programas (ex.: navegadores, aplicativos de mensagens). o Utiliza a API de sockets para enviar/receber dados. • Sistema Operacional: o A API de sockets é integrada ao Kernel, que gerencia solicitações de comunicação. • Camada de Transporte: o Usa TCP ou UDP para garantir comunicação fim a fim. • Camadas de Enlace de Dados e Física: o Responsáveis pela transmissão física dos dados (cabos, fibra óptica, ondas de rádio) até o destino. 5. Resumo • TCP é confiável e orientado à entrega precisa, enquanto UDP é rápido, priorizando a velocidade. • Sockets são fundamentais para a comunicação entre cliente e servidor, permitindo flexibilidade no uso de TCP ou UDP conforme a necessidade da aplicação. ❖ Sockets UDP Elementares 1. Características do Protocolo UDP • Definição: Protocolo da camada de transporte no modelo OSI/TCP-IP, baseado no modelo "envie e esqueça". o Sem garantias: Não assegura entrega, ordem ou ausência de duplicação de pacotes. o Alta velocidade: Ideal para aplicações que priorizam baixa latência em detrimento da confiabilidade completa. • Usos principais: o Jogos on-line e chamadas de vídeo: Permite manter fluidez mesmo com pequenas perdas de dados. o Streaming de vídeo e áudio: Tolera perda de frames ou falhas no áudio para evitar atrasos. o IoT e telemetria: Simplicidade e eficiência para envio frequente de pequenos pacotes com baixa sobrecarga. 2. Vantagens e Desafios do UDP • Vantagens: o Simples e eficiente. o Comunicação rápida sem necessidade de confirmação. • Desafios: o Ausência de confirmação de recebimento. o Possibilidade de perda, duplicação ou chegada fora de ordem dos pacotes. o Sem controle de fluxo ou congestionamento, podendo sobrecarregar redes ou receptores. • Consideração importante: Cabe à aplicação gerenciar esses desafios e decidir entre UDP, TCP ou ambos, dependendo dos requisitos do projeto. 3. Trabalhando com Sockets UDP em C • Funções principais: o socket(): ▪ Esta função cria um socket. ▪ Para UDP: SOCK_DGRAM (datagramas, pacotes não confiáveis de tamanho fixo). int sockfd = socket(AF_INET, SOCK_DGRAM, 0); ▪ Para TCP: SOCK_STREAM (conexões confiáveis, orientadas a byte). int sockfd = socket(AF_INET, SOCK_STREAM, 0); o bind(): Associa o socket a um endereço IP e porta, permitindo receberdados no endereço especificado. o sendto(): Envia dados para um endereço específico, sem necessidade de estabelecer conexão. o recvfrom(): Recebe dados de um endereço; cada chamada pode receber pacotes de diferentes remetentes. o connect(): ✓ Para o UDP (é opcional): Define um endereço padrão para envio de dados, simplificando o uso de funções como send() e recv(). ✓ Para o TCP: connect() é usado para estabelecer uma conexão e iniciar a comunicação. • Funções exclusivas ao TCP: o listen() e accept(): Colocam o socket em modo de escuta e aceitam conexões, respectivamente. 4. Conclusão • O UDP é ideal para aplicações que valorizam baixa latência, enquanto o TCP é mais indicado para cenários que exigem confiabilidade e controle. • Sockets UDP em C fornecem flexibilidade para implementar comunicação rápida, com funções específicas que simplificam a troca de dados sem conexão. CLIENTE/SERVIDOR HTTP E CLIENTE MQTT PARA IOT ❖ Arquitetura Cliente/Servidor HTTP 1. Fundamentos da Arquitetura HTTP • Base da web: Sustenta o diálogo entre cliente (navegador) e servidor por meio do protocolo HTTP. • Finalidade: Facilita a interação dinâmica e a distribuição global de conteúdos e serviços. • Características principais: Escalabilidade, flexibilidade e eficiência. 2. Funcionamento do HTTP 1. Início da conexão: o O cliente (navegador) inicia uma conexão TCP com o servidor. o Segurança opcional: uso de SSL/TLS para HTTPS. o Portas padrão: 80 (HTTP) e 443 (HTTPS). 2. Requisição HTTP: o O navegador envia: ▪ Método (e.x., GET, POST). ▪ Caminho do recurso solicitado. ▪ Cabeçalhos adicionais com informações sobre a requisição. 3. Processamento da requisição: o O servidor realiza ações como acessar arquivos, bancos de dados ou executar cálculos. 4. Resposta do servidor: o Inclui: ▪ Linha de status (e.x., 200 OK, 404 Not Found). ▪ Cabeçalhos (informações sobre servidor e conteúdo). ▪ Corpo da mensagem com os dados solicitados. 5. Gerenciamento da conexão TCP: o Dependendo do protocolo (e.x., HTTP/1.1), a conexão pode ser: ▪ Fechada após a resposta. ▪ Mantida aberta para múltiplas requisições (conexões persistentes). 6. Processamento da resposta pelo navegador: o Exibe o HTML. o Solicita e carrega recursos adicionais (e.x., imagens, scripts) até finalizar o conteúdo. 3. Importância da Arquitetura HTTP • Base da web: Permite a entrega de conteúdo e o funcionamento de aplicações interativas globalmente acessíveis. • Vantagens: o Escalabilidade: Atende simultaneamente milhares ou milhões de usuários. o Flexibilidade: Suporta diversas tecnologias no lado do cliente e do servidor. o Facilidade de manutenção: A separação entre cliente e servidor simplifica atualizações e ajustes. o Eficiência: ▪ Servidores gerenciam a lógica de negócios e dados. ▪ Clientes se concentram na apresentação e na interação com o usuário. ❖ Arquitetura MQTT para IoT 1. Definição e Aplicações do MQTT • MQTT: Protocolo de mensagens leve e eficiente, ideal para internet das coisas (IoT). • Uso: Aplicações em automação residencial, monitoramento agrícola, cidades inteligentes. • Características: Projetado para dispositivos com baixo poder de processamento e redes limitadas. 2. Estrutura do MQTT • Broker MQTT: o Servidor central que gerencia a troca de mensagens. o Filtra e distribui mensagens para dispositivos conforme tópicos. • Clientes: o Publicadores: Enviam mensagens para tópicos. o Subscritores: Recebem mensagens de tópicos nos quais estão inscritos. • Tópicos: Hierarquicamente organizados, funcionam como canais para publicação e subscrição, facilitando a comunicação. 3. Qualidade de Serviço (QoS) • QoS 0: Entrega no máximo uma vez, sem garantia de recebimento. Usado quando perdas de mensagens são toleráveis. • QoS 1: Garante que a mensagem chegue ao menos uma vez, podendo ocorrer duplicatas. • QoS 2: Garante a entrega de uma mensagem exatamente uma vez, sendo o modo mais seguro, mas mais lento. 4. Por que o MQTT é Ideal para IoT? • Eficiência de Banda Larga: Protocolo com baixo overhead, ideal para redes com largura de banda limitada. • Baixo Consumo de Energia: Suporta "sleep mode", economizando energia em dispositivos com bateria. • Escalabilidade: Suporta crescimento no número de dispositivos IoT sem degradar o desempenho. • Segurança: Criptografia SSL/TLS e autenticação de clientes. • Flexibilidade e Integração: Estrutura de tópicos e protocolo simples facilitam a integração com diversos dispositivos e sistemas. TEMA 4. PROCESSOS E TAREFAS NA LINGUAGEM C CRIAÇÃO DE PROCESSOS COM FORK ❖ Operação com Processos 1. Definição de Processo • Processo: Programa em execução, realizando tarefas conforme uma sequência de instruções. • Estados de um Processo: Os processos passam por vários estados durante sua execução, gerenciados pelo Bloco de Controle de Processo (PCB). o Pilha: Armazena dados temporários, como parâmetros de função e variáveis locais. o Heap: Memória dinâmica utilizada durante a execução. o Dados: Variáveis globais e estáticas. o Texto: Contém a instrução atual e informações do contador de programa e registros. 2. Ciclo de Vida de um Processo • Estados do Processo: o Iniciado: Processo criado. o Pronto: Espera para ser designado a um processador. o Executando: Processo sendo executado. o Esperando/Bloqueado: Aguardando algum recurso (ex. entrada do usuário). o Terminado: Processo finalizado e aguardando remoção da memória. 3. Bloco de Controle de Processo (PCB) • Função: Estrutura de dados que armazena todas as informações necessárias para gerenciar o processo durante seu ciclo de vida. • Elementos do PCB: o Estado do Processo: Situação atual (ex. pronto, executando). o Privilégios: Permissões de acesso aos recursos do sistema. o ID do Processo (PID): Identificação única. o Ponteiro: Aponta para o processo-pai. o Contador de Programa: Acompanha a execução do processo. o Registradores de CPU: Informações dos registradores necessários para a execução. o Informações de Agendamento: Prioridade e dados para agendamento. o Gerenciamento de Memória: Informações sobre a tabela de páginas e limites de memória. o Informações Contábeis: Uso de CPU, limites de tempo, entre outros. o Status de E/S: Lista de dispositivos de E/S alocados ao processo. ❖ Função Fork 1. Definição da Função Fork • fork: Chamada de sistema que cria um novo processo, clonando o processo atual. • Processo-filho: Recebe um valor de retorno diferente do processo-pai, permitindo a continuidade da execução dos dois a partir do mesmo ponto do programa. 2. Como a Função Fork Cria um Novo Processo • Criação do Processo: O sistema operacional cria um novo processo e adiciona uma nova entrada na tabela de processos. • Clonagem: O novo processo é uma cópia do processo-pai, incluindo os descritores de arquivo abertos e a imagem da memória executável. • Execução após o fork: O pai e o filho continuam a execução a partir do mesmo ponto, mas com valores de retorno diferentes: o Pai: Recebe o ID do processo-filho. o Filho: Recebe o retorno 0. o Falha: Se fork retornar -1, o processo não foi criado. 3. Comportamento Pós-Fork • Memória e Descritores de Arquivo: Após o fork, pai e filho não compartilham dados de memória ou descritores de arquivo, apesar de estarem inicialmente com as mesmas cópias. Alterações na memória do filho não afetam o pai. • Ponteiros de Busca: Mesmo que compartilhem arquivos abertos, o ponteiro de busca (posição no arquivo) é mantido separadamente para cada processo. 4. Exemplo de Código • Programa Exemplo: Demonstra o uso de fork() para criar um processo-filho. o O pai imprime seu ID de processo e o ID do filho. o O filho imprime seu próprio ID de processo. o Ambos os processos saem após a execução. #include #includerepetições não é conhecido. Executa enquanto a condição for verdadeira. 1 while (condição) { 2 // Código a ser repetido 3 } o do-while (faça - enquanto): Garante que o bloco seja executado pelo menos uma vez, pois verifica a condição após a execução. Seu uso é menos comum. 1 do { 2 // Código a ser repetido 3 } while (condição); Essas estruturas são fundamentais para a criação de programas flexíveis e eficientes, permitindo decisões condicionais e repetições controladas. Questão 3. Você está desenvolvendo um software para um caixa eletrônico. O sistema deve perguntar repetidamente ao usuário se deseja continuar realizando saques até que o usuário decida parar. Qual das seguintes opções de código em C implementa corretamente essa funcionalidade? A. } while (continuar == 's' | continuar == 'S') B. } while (continuar == 'n' || continuar == 'N') C. } while (continuar != 'n' && continuar != 'N') D. } while (continuar != 's' && continuar != 'S') E. } while (continuar == 'y' || continuar == 'Y') RESPOSTA: A condição (continuar == 's' || continuar == 'S') garante que o loop prossiga apenas se o usuário explicitamente indicar que deseja fazer outro saque, inserindo 's' ou 'S'. As demais alternativas ou invertem a lógica necessária (B e C), usam uma condição que finalizaria o loop incorretamente (se o usuário quiser continuar (D)) ou utilizam uma resposta ('y' ou 'Y') que não foi especificada como válida no contexto do problema (E). Vetores e Matrizes 1. Vetores ou Arrays: Sequências de elementos do mesmo tipo acessados por índices (ex.: int numeros[10]; para um vetor de 10 inteiros). 1 #include 2 int main(){ 3 4 int numeros[10]; 5 for(int i = 0; i int main(){ int numeros[4][5]; for(int i = 0; i 2 #include 3 4 int main(){ 5 6 int *numeros = (int*) malloc(10 * sizeof(int)); 7 if (numeros == NULL) 8 { 9 printf("Erro ao alocar memoria\n"); 10 return 1; 11 } 12 for (int i = 0;i #include int main() { int *array; int size, i; printf("Quantos numeros você deseja inserir? "); scanf("%d", &size); // Alocação dinâmica de memória array = (int*) malloc(size * sizeof(int)); // Verificação de alocação bem-sucedida if (array == NULL) { printf("Erro de alocacao de memoria.\n"); return -1; // Encerra o programa com erro } // Inserção de números no array for (i = 0; i // Biblioteca de entrada e saída de dados #include // Biblioteca de manipulação de datas e horas int main() { time_t agora; time(&agora); printf("Data e hora atual: %s\n",#include int main(int argc, char **argv) { int pid; /* process ID */ switch (pid = fork()) { case 0: /* processo-filho */ printf("Eu sou o processo-filho: pid=%d\n", getpid()); break; default: /* processo-pai */ printf("Eu sou o processo-pai: pid=%d, child pid=%d\n", getpid(), pid); break; case -1: /* erro ao criar o processo */ perror("fork"); exit(1); } exit(0); } 5. Considerações sobre o Código de Saída • exit(): A função exit() recebe um código que indica o status de saída do programa: o 0: Indica sucesso. o Código diferente de zero: Indica falha, útil para scripts de programas. ❖ Função Fork Básica com Soquetes 1. Comunicação Cliente-Servidor com Soquetes • Modelo Cliente-Servidor: o Cliente solicita informações ao servidor, semelhante a uma chamada telefônica (cliente chama, servidor atende). o O cliente conhece o endereço do servidor previamente; o servidor conhece o cliente após a conexão. o Ambos os processos criam seus próprios soquetes, que são as extremidades do canal de comunicação. 2. Passos para Estabelecer Conexões com Soquetes • No Cliente: 1. Criar o soquete com socket(). 2. Conectar ao endereço do servidor usando connect(). 3. Enviar e receber dados com read() e write(). • No Servidor: 1. Criar o soquete com socket(). 2. Vincular o soquete a um endereço com bind(). 3. Escutar conexões com listen(). 4. Aceitar uma conexão com accept(). 5. Enviar e receber dados. 3. Tipos de Soquetes • Stream Sockets: o Comunicação como fluxo contínuo de caracteres. o Baseados no protocolo TCP, que é confiável e orientado a fluxo. • Datagram Sockets: o Comunicação por mensagens individuais que precisam ser lidas por inteiro. o Baseados no protocolo UDP, que é não confiável e orientado a mensagens. 4. Aplicação da Função Fork em Soquetes • Uso em Comunicação entre Processos: o A função fork é utilizada para criar novos processos, possibilitando que o servidor atenda múltiplos clientes de forma independente. o Cada processo-filho gerado pelo servidor utiliza seu próprio soquete para comunicação com um cliente específico. ❖ Vantagens e Desvantagens da Chamada de Sistema Fork 1. Vantagens do Uso de Fork • Execução Simultânea: Criação de novos processos para multitarefas, aumentando a eficiência do sistema. • Reutilização de Código: O processo-filho herda o código do processo-pai, facilitando o desenvolvimento de sistemas complexos. • Otimização de Memória: Utiliza o método copy-on-write, onde pai e filho compartilham páginas de memória até que uma alteração ocorra, economizando recursos. • Isolamento de Processos: Cada processo tem sua própria área de memória e recursos, promovendo maior segurança e estabilidade ao sistema. 2. Desvantagens do Uso de Fork • Sobrecarga de Memória: Apesar da otimização, há uma cópia inicial de todo o processo-pai, aumentando o consumo de memória. • Duplicação de Recursos: O processo-filho herda descritores de arquivos e conexões, o que pode levar a desperdício de recursos. • Complexidade de Comunicação: Processos independentes exigem mecanismos como pipes ou memória compartilhada para trocar informações, aumentando a complexidade. • Impacto no Desempenho: A duplicação de memória e gerenciamento de recursos pode degradar o desempenho, especialmente em sistemas com alta frequência de criação de processos. FUNÇÕES PARA THREADS BÁSICOS ❖ Operação com Threads 1. Conceito de Threads • Definição: Threads são unidades de execução dentro de um processo, permitindo multitarefas. • Single-threaded: Programas com um único thread, executando uma tarefa de cada vez. • Multi-threaded: Programas com múltiplos threads que realizam tarefas simultaneamente, acelerando a execução. 2. Vantagens do Uso de Threads • Melhoria de Desempenho: Especialmente em sistemas multiprocessadores e tarefas de entrada/saída (I/O). • Eficiência: Criar threads é mais rápido e consome menos recursos do que criar processos. • Escalabilidade: Permite aproveitar melhor sistemas com múltiplos núcleos. 3. Tipos de Threads • Threads de Usuário: o Gerenciados por bibliotecas no nível do usuário. o Menor dependência do sistema operacional. o Problema: Chamada de bloqueio pode atrasar todo o processo. • Threads do Kernel: o Controlados pelo sistema operacional, com o kernel sendo multithread. o Suportam multitarefa real. o Podem causar sobrecarga no sistema. 4. Modelos de Mapeamento Threads Usuário-Kernel • Um para Muitos: o Vários threads de usuário mapeados para um thread do kernel. o Problema: Bloqueio de um thread afeta todos. • Um para Um: o Cada thread de usuário é mapeado para um thread do kernel. o O escalonador do kernel controla os threads. o Problema: Há limitação no número de threads devido à sobrecarga. • Muitos para Muitos: o Muitos threads de usuário multiplexados em menos threads do kernel. o Oferece paralelismo real, mas é complexo de implementar. o Supera os problemas dos demais modelos. 5. Estrutura do Thread (Thread Control Block - TCB) • Thread ID: Identificador exclusivo, gerado pelo sistema operacional. • Thread state (Estado do Thread): Situação atual (ex.: pronto, executando). • Informações da CPU: Fornece dados necessários sobre o thread, como contagem de programas e conteúdo de registro. • Prioridade do thread: Define a ordem de execução de acordo com a prioridade em relação a outros threads. • Ponteiro do Processo: Aponta para o processo que criou o thread. • Ponteiros de Threads: Lista de ponteiros para outros threads criados pelo thread atual. 6. Aplicações e Benefícios do Multithreading • Exemplo prático: Em servidores web, threads são mais eficientes que processos para atender múltiplas solicitações. • Tarefas de entrada-saída: Multithreading é especialmente útil em problemas que envolvem operações de IO. • Tarefas computacionais intensivas: Em alguns casos, múltiplos processos podem ser mais eficazes. • Limitações: Problemas de sincronização e condições de corrida. • Custo-benefício: Avaliar a complexidade do código frente ao ganho de desempenho esperado. 7. Considerações para o Desenvolvedor • Desempenho e eficiência: Threads aceleram o código e melhoram o desempenho de aplicações em sistemas multiprocessadores. • Gerenciamento de complexidade: É essencial evitar problemas como condições de corrida e sincronizar corretamente os dados compartilhados, a fim de manter a eficiência. • Decisão consciente: Cabe ao desenvolvedor avaliar se o uso de threads compensa os desafios de implementação. ❖ Biblioteca Pthreads 1. O que são Pthreads? • Definição: Interface padronizada (IEEE POSIX 1003.1c) para threads em sistemas Unix-like, garantindo portabilidade entre diferentes sistemas. • Objetivo: Resolver a fragmentação causada por implementações proprietárias de threads (ex.: IBM, Apple, Intel). • Descrição: Biblioteca em C/C++ para gerenciar threads com base no padrão Posix. 2. Grupos de Funções da Biblioteca Pthreads 1. Thread Management: Rotinas que permitem o gerenciamento de threads, incluindo criação, junção e definição de atributos. 2. Mutexes (é abreviação de exclusão mútua): São rotinas que tratam de sincronização. As funções mutex permitem criar, destruir, bloquear e desbloquear mutexes (exclusões mútuas). 3. Variáveis de Condição: São rotinas de comunicação entre threads que compartilham um mutex, por meio de condições definidas. Inclui funções para criar, destruir, esperar e sinalizar com base em valores de variáveis especificados, bem como para definir/consultar atributos de variáveis. 4. Sincronização: Gerenciamento de bloqueios e barreiras para leitura/gravação. 3. Tipos Fornecidos pelo Pthreads • Identificação e atributos: o pthread_t: Identificadorde thread. o pthread_attr_t: Atributos de thread. • Sincronização: o pthread_mutex_t: Mutex (exclusão mútua). o pthread_cond_t: Variável de condição. • Outros: o pthread_key_t: Chave de armazenamento local. o pthread_once_t: Controle de inicialização única. 4. Criação de Threads • Função principal: pthread_create cria threads com quatro argumentos: 1. Thread: Retorna o identificador do thread. 2. Atributos: Configurações opcionais do thread (ou NULL para padrões). 3. Start Routine: Função que o thread executará. 4. Argumento: Argumento para a função, passado como ponteiro (ou NULL). • Exemplo básico: Um programa cria um thread para imprimir "Hello World" e utiliza pthread_join para aguardar sua conclusão. int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start)(void *), void *arg); 5. Encerramento de Threads • Formas de terminar um thread: 1. Retorno normal da função inicial. 2. Chamando pthread_exit explicitamente. 3. Cancelamento por outro thread via pthread_cancel. 4. Encerramento do processo com exit() ou exec(). Nota: Se main() terminar sem chamar pthread_exit, todos os threads ativos são encerrados abruptamente. 6. Exemplo de Programa Multithread • Criação múltipla: Threads são criados em um laço para executar uma função que imprime o identificador de cada thread. • Finalização: O programa utiliza pthread_exit para aguardar a conclusão de todos os threads antes de encerrar. • Exemplo básico de um programa multithread: #include // Biblioteca-padrão de entrada e saída #include // Biblioteca-padrão de funções utilitárias #include // Biblioteca para chamadas ao sistema Posix (como sleep) #include // Biblioteca para manipulação de threads // Uma função normal em C que é executada como um thread // quando seu nome é especificado em pthread_create() void *helloWorld(void *vargp) { sleep(1); // Faz o thread dormir por 1 segundo printf("Alo mundo \n"); // Imprime "Hello World" na tela return NULL; // Retorna Null ao terminar a execução do thread } int main() { pthread_t thread_id; // Declaração de uma variável para armazenar o ID do thread printf("Before Thread\n"); // Imprime antes de criar o thread pthread_create(&thread_id, NULL, helloWorld, NULL); // Cria um novo thread que executa a função helloWorld pthread_join(thread_id, NULL); // Espera até que o thread especificado termine printf("Após a Thread\n"); // Imprime após o thread ter terminado exit(0); // Encerra o programa } • Exemplo básico de um programa multithread, como o método pthread_exit. #include // Biblioteca para manipulação de threads #include // Biblioteca-padrão de entrada e saída #define NUM_THREADS 5 // Define o número de threads como 5 // Função que será executada como um thread void *PrintHello(void *threadid) { long tid; tid = (long)threadid; // Converte o identificador do thread para um long printf("Alo mundo! Sou eu, thread #%ld!\n", tid); // Imprime o identificador do thread pthread_exit(NULL); // Encerra o thread } int main (int argc, char *argv[]) { pthread_t threads[NUM_THREADS]; // Declaração de um array para armazenar os IDs dos threads int rc; long t; // Cria múltiplos threads for (t = 0; to Requisitos para solução da seção crítica: ▪ Exclusão mútua: Apenas um processo por vez, deve estar na seção crítica. ▪ Progresso: Processos aguardando decidem sem atraso quem entra na seção crítica. ▪ Espera limitada: Deve haver um limite para o tempo de espera de um processo para entrar na seção crítica. o Exemplo de Implementação de exclusão mútua: faça { Enquanto (TestAndSetBloqueio(& bloqueio)) ; //faça nada Seção crítica Bloqueio=FALSO; Restante da seção }enquanto(VERDADEIRO) 4. Soluções para o Problema da Seção Crítica: Há dois tipos de solução: Solução de Peterson e Semáforos. 1. Solução de Peterson: o Solução baseada em software, que utiliza duas variáveis compartilhadas: ▪ Sinalizador booleano[i]: Indica intenção de entrar na seção crítica. ▪ Turn: Determina a vez de cada processo. o Limitações: Garante exclusão mútua, progresso e espera limitada, mas: ▪ Envolve espera ocupada. ▪ É limitada a dois processos. ▪ Não é ideal para arquiteturas modernas. o Exemplo: Estrutura geral da solução de Perterson: faça { Flag[i]=VERDADEIRO Rodada=j Enquanto(flag[j] && rodada==j) Seção crítica Flag[i]=FALSO; Restante da seção }enquanto(VERDADEIRO) 2. Semáforos: o São mecanismos de sinalização que usam operações atômicas (wait e signal), para sincronização de processos. o Técnicas de sincronização, como semáforos e mutexes, são utilizadas para coordenar a execução de processos e proteger seções críticas do código o Tipos de semáforos: ▪ Semáforos Binário: Podem ter valores 0 ou 1, funcionando como bloqueios mutex para garantir exclusão mútua. ▪ Semáforos de contagem: Podem ter qualquer valor e controlam o acesso a recursos com limitações no número de acessos simultâneos. o Estrutura geral da exclusão mútua com semáforo: faça { Aguarde(mutex); Seção crítica Sinal(mutex); Restante da seção }enquanto(VERDADEIRO); 5. Vantagens e Desvantagens da Sincronização de Processos • Vantagens: o Garante consistência e integridade dos dados. o Previne condições de corrida. o Promove uso eficiente de recursos compartilhados. • Desvantagens: o Aumenta sobrecarga e complexidade do sistema. o Pode levar à degradação do desempenho. o Risco de impasses, se mal implementada. ❖ Deadlock em Sistemas Operacionais 1. Definição de Deadlock • Conceito: Situação em que processos ficam bloqueados, aguardando recursos uns dos outros, sem possibilidade de progresso. • Exemplo clássico: Dois trens em sentidos opostos no mesmo trilho, incapazes de se mover até que o outro saia do caminho. 2. Condições para ocorrência de Deadlock 1. Exclusão mútua: Recursos não podem ser compartilhados. 2. Hold and wait: Processos mantêm recursos enquanto aguardam outros. 3. Sem preempção: Recursos não podem ser retirados à força. 4. Espera circular: Um conjunto de processos forma um ciclo, onde cada um aguarda um recurso retido pelo próximo. 3. Métodos para lidar com Deadlocks A. Prevenção de Deadlock • Garante que pelo menos uma das quatro condições não ocorra, por meio de: o Eliminação da exclusão mútua. o Resolução do hold and wait. o Permissão de preempção de recursos. o Prevenção da espera circular. B. Detecção e Recuperação 1. Detecção: Verifica a existência de deadlocks no sistema. 2. Recuperação: Aplica estratégias para resolver o deadlock: o Encerramento de processos: ▪ Abortar todos os processos em deadlock (rápido, mas causa perda de dados). ▪ Abortar processos individualmente até resolver o deadlock (mais gradual, mas envolve sobrecarga). o Preempção de recursos: ▪ Seleção de vítimas: Escolhe processos para liberar recursos minimizando custos. ▪ Reversão: Retorna processos a um estado seguro, abortando e reiniciando conforme necessário. ▪ Prevenção da fome (starvation): Evita que um mesmo processo seja repetidamente escolhido como vítima. C. Ignorar Deadlocks: Estratégia usada quando os deadlocks forem raros. • Algoritmo da avestruz: Ignora deadlocks, reiniciando o sistema caso ocorram. Estratégia adotado por sistemas como Windows e Unix. 4. Estado Seguro • Um sistema está em estado seguro se: o Os processos podem esperar até que recursos sejam liberados. o Todos os recursos solicitados podem ser eventualmente alocados sem causar deadlock. ❖ Sincronização de Threads com Mutex 1. Threads Posix (conhecidos como Pthreads) • Threads Posix (Pthreads): Permitem múltiplos fluxos de execução dentro de um processo, compartilhando dados globais e heap. • Necessidade de sincronização: Previne acessos simultâneos a dados compartilhados, garantindo: o Exclusão mútua: Apenas um thread executa a seção crítica por vez. o Sincronização adequada: Coordenação entre threads para acesso seguro. 2. Problema da Exclusão Mútua • Exemplo: Em um aplicativo bancário, dois threads (ex.: thread_verificar_liberação e thread_sacar_dinheiro) acessando simultaneamente a variável saldo_da_conta podem causar inconsistências. • Solução para evitar o problema de exclusão mútua: Utilizar mutexes para garantir que apenas um thread acesse a seção crítica por vez. 3. Mutexes em Pthreads • Definição: São objetos usados para gerenciar exclusão mútua. • Um mutex é bloqueado no início e desbloqueado no final da seção crítica. • Para variáveis alocadas dinamicamente: 1. Inicializacao: É inicializado o mutex com pthread_mutex_init. 2. Bloqueio: O mutex é travado no início da seção crítica usando pthread_mutex_lock. 3. Desbloqueio: O mutex é destravado no final da seção crítica usando pthread_mutex_unlock. 4. Variáveis de Condição em Pthreads • Objetivo: Gerenciar sincronização entre múltiplos threads para acesso a recursos compartilhados. • Componentes: o Variável de condição: Indica a disponibilidade do recurso. o Mutex: Garante acesso seguro à variável de condição. o Predicado: Representa a condição necessária para um recurso ser acessado. • Funcionamento: 1. Um thread que libera o recurso sinaliza a variável de condição. 2. Outro thread, que espera pelo recurso, aguarda o sinal para prosseguir. /* * * pthreads-synch.c: Programa para demonstrar a sincronização de Pthreads * usando mutex e variáveis de condição em C no Linux * (Problema do Produtor-Consumidor) */ #include #include #include #include #include #include #include #include // Estruturas de dados do buffer #define MAX_BUFFERS 10 char buf[MAX_BUFFERS][100]; int buffer_index; int buffer_print_index; pthread_mutex_t buf_mutex = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t buf_cond = PTHREAD_COND_INITIALIZER; pthread_cond_t spool_cond = PTHREAD_COND_INITIALIZER; int buffers_available = MAX_BUFFERS; int lines_to_print = 0; void *producer(void *arg); void *spooler(void *arg); int main(int argc, char **argv) { pthread_t tid_producer[10], tid_spooler; int i, r; // Inicialização buffer_index = buffer_print_index = 0; // Cria a thread spooler if ((r = pthread_create(&tid_spooler, NULL, spooler, NULL)) != 0) { fprintf(stderr, "Erro = %d (%s)\n", r, strerror(r)); exit(1); } // Cria 10 threads produtoras int thread_no[10]; for (i = 0; i} // Não há mais strings para imprimir? while (lines_to_print) sleep(1); // Termina a thread spooler if ((r = pthread_cancel(tid_spooler)) != 0) { fprintf(stderr, "Erro = %d (%s)\n", r, strerror(r)); exit(1); } exit(0); } // Função produtora: produz strings para impressão // Pode haver múltiplas threads produtoras void *producer(void *arg) { // Cria 10 strings e termina int i, r; int my_id = *((int *)arg); int count = 0; for (i = 0; ictime(&agora)); return 0; } // Imprime: Data e hora atual: Mon Nov 11 12:42:42 2024 #include // Biblioteca de entrada e saída de dados #include // Biblioteca de manipulação de datas e horas int main() { time_t agora; // A variável “agora” armazena o tempo atual em segundos struct tm *tempo; char buffer[80]; // Declara array que armazena até 80 caracteres, armazena a string formatada com a data e hora time(&agora); // Obtém o tempo atual do sistema e o armazena na variável agora tempo = localtime(&agora); // Converte o valor de “agora” (segundos) para o horário local, retornando um ponteiro data e hora strftime(buffer, 80, "Data e hora atual: %d-%m-%Y %H:%M:%S", tempo); printf("%s\n", buffer); return 0; } // Imprime: Data e hora atual: 11-11-2024 12:59:25 #include #include #include int main() { // Declara duas variáveis do tipo time_t time_t inicio, fim; // Captura o tempo atual e armazena na variável inicio time(&inicio); printf("Tempo inical capturado.\n"); // Simula uma pausa de 5 segundos printf("Esperando por 5 segundos...\n"); for (int i = 0; i e . Considere as seguintes afirmações sobre programação em C, especificamente sobre o uso das bibliotecas padrão stdio.h e time.h: I. A função printf() encontrada na biblioteca stdio.h é usada para imprimir dados na saída padrão. II. time_t é um tipo definido na biblioteca time.h que representa o tempo em milissegundos desde o Unix Epoch. III. localtime() é uma função da time.h que converte um valor time_t para a hora local, representada por uma estrutura struct tm. Selecione a seguir a alternativa correta. A. Apenas I e II estão corretas. B. Apenas II e III estão corretas. C. Apenas I e III estão corretas. D. Apenas I está correta. E. Apenas II está correta. RESPOSTA: A função printf() é amplamente utilizada para imprimir dados formatados na saída padrão (geralmente o console). Ela é uma das funções mais comuns e fundamentais da biblioteca , permitindo a exibição de texto e variáveis em diferentes formatos. O tipo time_t realmente é definido na biblioteca , mas ele representa o tempo em segundos (e não em milissegundos) desde o Unix Epoch (00:00:00 UTC em 1 de janeiro de 1970). A definição precisa do tipo time_t pode variar entre implementações, mas, por padrão, ele conta o tempo em segundos. Por isso a afirmação II está incorreta. A função localtime() é usada para converter um valor do tipo time_t (que representa o tempo em segundos desde o Unix Epoch) em uma estrutura struct tm, que contém a data e a hora local. Essa função ajusta o tempo de acordo com o fuso horário local e o horário de verão, se aplicável. Bibliotecas para gráficos : • Padrão ANSI C e gráficos: O padrão ANSI C não inclui funções gráficas, recorrendo a bibliotecas externas como OpenGL e SDL para adicionar gráficos a programas em C. • graphics.h: Apesar de desatualizada, a biblioteca graphics.h ainda é usada para ensino de programação gráfica por introduzir conceitos básicos de forma simples. #include int main (){ initwindow (800, 800); int left=100,top=100,right=200,bottom=200,x= 300,y=150,radius=50; rectangle(left, top, right, bottom); circle(x, y, radius); bar(left + 300, top, right + 300, bottom); line(left - 10, top + 150, left + 410, top + 150); ellipse(x, y + 200, 0, 360, 100, 50); outtextxy(left + 100, top + 325, "Meu programa grafico"); getch(); } Estrutura do código acima usando graphics.h: 1. #include: Inclui a biblioteca graphics.h para habilitar funções gráficas. 2. initwindow(800, 800): Cria uma janela de 800x800 pixels para exibir gráficos. 3. Variáveis de coordenadas: Define coordenadas para posicionar figuras geométricas: o left, top, right, bottom para o retângulo. o x, y para o centro do círculo e radius para o raio. 4. Desenho de formas básicas: o rectangle(left, top, right, bottom): Desenha um retângulo. o circle(x, y, radius): Desenha um círculo. o bar(left + 300, top, right + 300, bottom): Desenha um retângulo preenchido. o line(left - 10, top + 150, left + 410, top + 150): Desenha uma linha horizontal. o ellipse(x, y + 200, 0, 360, 100, 50): Desenha uma elipse completa. o outtextxy(left + 100, top + 325, "Meu programa grafico"): Exibe texto nas coordenadas especificadas. 5. getch(): Mantém a janela aberta até que uma tecla seja pressionada. Outras bibliotecas gráficas populares em C: • SDL: Amplamente utilizada para gráficos e áudio, ideal para desenvolvimento de jogos. • Allegro e SFML: Usadas em multimídia e interfaces gráficas. • Cairo: Voltada para gráficos vetoriais precisos. • Plplot: Especializada em gráficos científicos. • GTK: Comumente usada para criar interfaces gráficas no Linux. Questão 1. Enquanto bibliotecas modernas, como OpenGL e SDL, são frequentemente usadas para aplicações avançadas e jogos, a biblioteca graphics.h, embora antiga, ainda é utilizada em contextos educacionais para ensinar conceitos básicos de gráficos em C. Com base nessa afirmativa, analise as informações a seguir e assinale a alternativa correta. Asserção (A): a biblioteca graphics.h é frequentemente utilizada em contextos educacionais para introduzir conceitos de programação gráfica em C. Razão (R): a biblioteca graphics.h é parte do antigo Borland's Turbo C e do compilador Borland C++. A. A e R são verdadeiras, e R é a justificativa correta de A. B. A e R são verdadeiras, mas R não é a justificativa correta de A. C. A é verdadeira, mas R é falsa. D. A é falsa, mas R é verdadeira. E. A e R são falsas. RESPOSTA: A biblioteca graphics.h é amplamente usada em ambientes educacionais para ensinar conceitos básicos de programação gráfica. Embora seja antiga e limitada em comparação com bibliotecas mais modernas, como OpenGL e SDL, sua simplicidade faz com que seja adequada para iniciantes. Por isso (A) está correta. A biblioteca graphics.h faz parte do antigo Borland's Turbo C e do compilador Borland C++. Essas ferramentas foram populares em ambientes educacionais nas décadas passadas, e a biblioteca graphics.h era frequentemente utilizada para ensinar gráficos em C. Embora ambas asafirmações sejam verdadeiras, (R) não justifica diretamente (A). A Razão explica a origem da biblioteca graphics.h, mas não por que ela é usada em contextos educacionais. A verdadeira justificativa para o uso da graphics.h em educação é sua simplicidade e facilidade de uso, que descomplica a introdução de conceitos básicos de gráficos a estudantes, e não apenas o fato de ela fazer parte do Turbo C e do Borland C++. Principais APIs para C Contexto e Importância: o Para a programação gráfica em C, compreender o uso de bibliotecas e APIs gráficas é essencial, pois isso melhora a eficácia e portabilidade do software. o Escolher a ferramenta certa para renderização de imagens, manipulação gráfica e interação com hardware otimiza o desenvolvimento de aplicações. Diferença entre Bibliotecas e APIs Gráficas: o Bibliotecas Gráficas: São coleções de funções prontas para realizar tarefas comuns, focadas em operações como renderização de imagens e manipulação de texturas. o A biblioteca disponibiliza funcionalidades prontas para uso imediato, facilitando a implementação de gráficos básicos (ex.: SDL, Allegro, Cairo). o APIs Gráficas: São conjuntos de regras e especificações que definem métodos de interação entre programas e software/hardware. Exemplos incluem OpenGL e DirectX, que permitem abstrair a manipulação gráfica 3D e a criação de sombreados sem a necessidade de detalhes específicos do hardware. o A API fornece uma estrutura detalhada para desenvolver e controlar componentes gráficos avançados, podendo ser complementada por várias bibliotecas gráficas. API DirectX: o Um conjunto de APIs da Microsoft, especializado no desenvolvimento de jogos e multimídia em Windows. o Suas funções englobam gráficos 2D e 3D, áudio e dispositivos de entrada, com destaque para o Direct3D, que acessa diretamente a GPU (Unidade de Processamento Gráfico), o que permite otimizar a execução de jogos e aplicações multimidea e para gráficos 3D complexos. o Amplamente usada em jogos para Windows e consoles Xbox, o DirectX se destaca pela otimização e eficiência em ambiente Windows, facilitando a interação rápida com o hardware. OpenGL: o É multiplataforma, o que permite trabalhar com Windows, Linus, iOS, Mac, Android. o A OpenGL facilita a migração para outra plataforma. o Ambas as APIs, OpenGL e DirectX (via Direct3D), utilizam a GPU para renderizar gráficos de forma eficiente e criar visuais detalhados, especialmente em aplicações como jogos e simulações. Cada uma tem características específicas, mas ambas são amplamente utilizadas para otimizar o uso do hardware gráfico. o Também permite o uso da GPU para processamentos não gráficos como o machine learning e inteligência artificial Questão 1. Ao desenvolver jogos para plataformas Windows, um programador deve escolher entre várias APIs gráficas. Qual API é altamente recomendada para esse propósito, pois oferece interação direta com o hardware de vídeo e áudio, otimizando a execução de jogos e aplicações multimídia, e é especialmente eficiente devido à sua capacidade de manipular diretamente a GPU? A. OpenGL B. SDL C. DirectX D. Allegro E. Cairo RESPOSTA: DirectX é altamente recomendada para o desenvolvimento de jogos em plataformas Windows. Essa escolha se deve à sua capacidade de interação direta com o hardware de vídeo e áudio, o que otimiza significativamente a execução de jogos e aplicações multimídia. Ela é projetada para ser especialmente eficiente no ambiente Windows, aproveitando ao máximo os recursos do sistema operacional e da GPU. Suas funcionalidades abrangem renderização gráfica 2D e 3D, áudio e gerenciamento de dispositivos de entrada, tornando-a uma solução completa e integrada para desenvolvedores de jogos. Bibliotecas básicas para Jogos • As bibliotecas em C são essenciais para a criação de jogos devido ao controle direto sobre os recursos do sistema e a capacidade de otimizações específicas, tornando os jogos mais eficientes e responsivos. • A portabilidade e maturidade da linguagem C favorecem a adaptação para diversas plataformas, o que é especialmente útil para desenvolvedores focados em desempenho superior e otimização. Características Essenciais de uma Biblioteca para Jogos: o que uma biblioteca essencial para desenvolvimento de jogos deve abranger: • Sistema de Janelas: Controle para criar e gerenciar janelas, incluindo ajustes de tamanho e resposta a eventos. • Gráficos e Renderização: Ferramentas para desenhar imagens e texturas, com suporte para gráficos 2D e 3D, muitas vezes integrado a APIs como OpenGL ou DirectX. • Manipulação de Eventos: Resposta a interações de dispositivos como teclado, mouse e joystick. • Áudio: Suporte para reprodução de sons e músicas. • Rede: Facilita a comunicação para jogos multiplayer. • Temporização e Controle de Frames: Gerenciamento da taxa de atualização para garantir fluidez. • Sistema de Arquivos: Funções para leitura e gravação de arquivos, essenciais para carregar recursos e salvar estados de jogo. • Matemática e Física: Rotinas matemáticas para cálculos geométricos e simulação de física, fundamentais para movimentação e interações. Principais Bibliotecas para Jogos em C : • SDL (Simple DirectMedia Layer): Oferece acesso de baixo nível a sistemas de vídeo, áudio e dispositivos de entrada, com suporte para gráficos 3D via OpenGL ou Direct3D. • Allegro: Inclui funcionalidades para gráficos, áudio, eventos e matemática, sendo gratuita para uso comercial. • SFML (Simple and Fast Multimedia Library): Biblioteca orientada a objetos, com módulos para gráficos, áudio, rede e sistema, simplificada para fácil uso e multiplataforma. • Raylib: Voltada para prototipagem rápida, ideal tanto para iniciantes quanto para desenvolvedores avançados. Questão 1. Ao desenvolver aplicações multimídia e jogos, é essencial escolher uma biblioteca que ofereça acesso de baixo nível aos diversos componentes de hardware, como vídeo, áudio, teclado, mouse e joystick. Além disso, a capacidade de integrar gráficos 3D de alto desempenho é importante para criar experiências imersivas. Qual biblioteca em C é amplamente reconhecida por proporcionar essa gama de funcionalidades? A. SFML (simple and fast multimedia library) B. Allegro C. SDL (simple directmedia layer) D. Raylib E. Irrlicht engine RESPOSTA: A SDL é uma biblioteca muito popular entre desenvolvedores de jogos em C, devido ao seu acesso direto e de baixo nível aos recursos do hardware. Ela suporta sistemas de vídeo e áudio e manipulação de entrada de dispositivos, sendo capaz de integrar gráficos 3D com o uso de OpenGL ou Direct3D. Ela é ideal para desenvolvedores que buscam controle detalhado sobre o hardware do sistema em suas aplicações de jogo. Roteiro Pratico Nesta prática, vamos explorar o uso da biblioteca graphics.h em C para criar uma animação simples: um círculo com bordas vermelhas se move horizontalmente de um lado para o outro na tela. A cada interação do círculo com as bordas da tela, há inversão na direção do movimento. 1. Baixe o driver graphics.h. Disponível aqui. 2. Descompacte o arquivo Graphics in Dev C++.zip. 3. Copie os arquivos 6-ConsoleAppGraphics.template e ConsoleApp_cpp_graph.txt (conforme demonstra a imagem a seguir) para a pasta template dentro daquela em que o Dev C++ está instalado. Em geral, isso ocorre nos arquivos de programa do Windows (C:\Program Files (x86)\Dev-Cpp\Templates). https://drive.google.com/file/d/14BLoBy0PA_A7_gdgO6UWiLEzrMPr7Hua/view Tela do explorador de arquivos do Windows 10. 4. Copie os arquivos graphics.h e winbgim.h para a pasta include do compilador que será usado (32 bits) nos arquivos de programa do Windows (C:\Program Files (x86)\Dev-Cpp\MinGW64\x86_64-w64-mingw32\include). 5. Copie o arquivo libbgi.a para as pastas lib e lib32 do compilador que será usado (32 bits) nos arquivos de programa do Windows (C:\ProgramFiles (x86)\Dev-Cpp\MinGW64\x86_64-w64-mingw32\lib) e (C:\Program Files (x86)\Dev-Cpp\MinGW64\x86_64-w64-mingw32\lib32). 6. Crie um projeto no Dev C++ com as opções Console Graphics e C++ Project, como mostrado na imagem a seguir (apesar de ser uma biblioteca de C, a opção C project não funciona). Tela do Dev C++. 7. Escolha o compilador de 32 bits, como demonstrado na próxima imagem (TDM-GCC 4.9.2 32-bit Release). #include #include int main() { int gd = DETECT, gm; int x = 100, y = 200; // Posição inicial do círculo int radius = 30; // Raio do círculo int maxX; int step = 5; // Passo do movimento do círculo initgraph(&gd, &gm, NULL); // Inicializa o sistema gráfico maxX = getmaxx(); // Pega a largura máxima da tela while (!kbhit()) { // Executa até que uma tecla seja pressionada cleardevice(); // Limpa a tela setcolor(RED); // Define a cor do círculo para vermelho circle(x, y, radius); // Desenha o círculo na posição atual // Atualiza a posição x do círculo x += step; // Inverte a direção quando atinge as bordas da janela if (x > maxX - radius || x #include // Para rand() e srand() #include // Para time() int main() { int gd = DETECT, gm; int x = 300, y = 200; // Posição inicial do círculo int radius = 30; // Raio do círculo int maxX, maxY; int dx = 4, dy = 4; // Componentes de velocidade iniciais srand(time(NULL)); // Inicializa o gerador de números aleatórios initgraph(&gd, &gm, NULL); // Inicializa o sistema gráfico maxX = getmaxx(); // Pega a largura máxima da tela maxY = getmaxy(); // Pega a altura máxima da tela while (!kbhit()) { // Executa até que uma tecla seja pressionada cleardevice(); // Limpa a tela setcolor(RED); // Define a cor do círculo para vermelho circle(x, y, radius); // Desenha o círculo na posição atual // Atualiza a posição do círculo x += dx; y += dy; // Verifica se o círculo atinge as bordas e ajusta a direção if (x > maxX - radius || x maxY - radius || y maxX - radius || x maxY - radius || ya aparência ambiental, essenciais para criar a atmosfera desejada no jogo. As outras opções, embora sejam características importantes do OpenGL, não se concentram especificamente na aplicação de efeitos atmosféricos. Biblioteca GLUT: Interfaces de Janelas • O que é GLUT? Biblioteca GLUT ou OpenGL utility toolkit • Propósito da GLUT: Ferramenta para desenvolvedores focada em facilitar o trabalho com gráficos 3D usando OpenGL, permitindo foco no design gráfico, ou seja, na renderização gráfica sem precisar lidar com detalhes do sistema operacional. • GLUT não gerencia a interação com o sistema operacional ou dispositivos de entrada, como mouse e teclado. • Importância da GLUT: ✓ Ferramenta para desenvolvedores que trabalham com gráficos 3D usando OpenGL. ✓ Simplifica a interação com sistemas operacionais. ✓ Oferece suporte para gerenciar dispositivos de entrada (teclado, mouse, etc.). • Importância da GLUT: ✓ Atuar como camada intermediaria entre OpenGL e o sistema operacional. ✓ Facilita a criação e o gerenciamento de janelas e eventos • Funcionalidade da GLUT: ✓ Criação e gerenciamento de janelas; ✓ Simplifica a abertura de múltiplas janelas; ✓ Gerencia eventos como redimensionamento e interações do usuário. ✓ Suporte a Callbacks; ✓ Funções acionadas por eventos específicos; ✓ Essenciais para aplicações interativas coo jogos. ✓ Tratamento intuitivo de coordenadas de janela; ✓ Facilita o posicionamento dos elementos gráficos. • Benefícios: ✓ Permite que programadores se concentrem no desenvolvimento gráfico em vez de aspectos técnicos de plataformas específicas. ✓ Essencial para desenvolvedores que querem criar aplicações gráficas interativas e sofisticadas. ✓ Simplifica a configuração de um contexto gráfico para renderização das primitivas do OpenGL. ✓ Simplicidade de uso; ✓ Requer poucas rotinas para exibir uma cena gráfica. ✓ Evita complicações comuns como o uso extensivo de ponteiros ✓ Estável: amplamente utilizada e testada proporcionando robustez e confiabilidade • Importância: Dominar a GLUT é fundamental para quem deseja explorar aplicações avançadas de renderização e design gráfico com OpenGL. Questão 1. João é um desenvolvedor de jogos digitais e decidiu usar o OpenGL para criar gráficos 3D em seu novo projeto. Ele optou pelo uso da biblioteca GLUT. Analise as alternativas a seguir: Asserção: o uso da biblioteca GLUT por João é adequado para simplificar a criação e o gerenciamento de janelas e eventos de entrada em seu projeto de jogo. Razão: a biblioteca GLUT oferece uma camada de abstração que permite aos desenvolvedores se concentrarem na renderização gráfica sem se preocuparem com detalhes específicos do sistema operacional ou gerenciamento de janelas. Selecione, a seguir, a alternativa correta. A. Ambas são verdadeiras, e a razão é a explicação correta da asserção. B. Ambas são verdadeiras, mas a razão não é a explicação correta da asserção. C. A asserção é verdadeira, mas a razão é falsa. D. A asserção é falsa, mas a razão é verdadeira. E. Ambas são falsas. RESPOSTA: A asserção corretamente identifica que a biblioteca GLUT é uma escolha adequada para João, pois ela simplifica a interação com o sistema de janelas e o tratamento de eventos de entrada, o que é importante no desenvolvimento de jogos. A razão justifica adequadamente essa escolha, explicando que a GLUT fornece uma interface que abstrai complexidades específicas de sistemas operacionais, permitindo que o desenvolvedor se concentre mais na parte gráfica do projeto. O ambiente Dev C++ Quem deseja se aprofundar na programação gráfica usando um IDE gratuito e leve deve explorar a configuração do Dev-C++ para utilizar a biblioteca OpenGL e GLUT. Dev-C++ oferece um ambiente desenvolvido e intuitivo para configurar essas bibliotecas, permitindo que desenvolvedores criem gráficos 2D e 3D com facilidade. O Dev-C++ na versão 5.11 é um IDE gratuito e de código aberto para C/C++, ideal para estudantes e iniciantes devido à sua simplicidade e eficácia. Ele inclui compilador, depurador, editor de código e suporta bibliotecas como OpenGL para gráficos 3D. Para usar GLUT e OpenGL, são necessárias configurações iniciais no Dev-C++, facilitando o desenvolvimento de aplicações gráficas. Acompanhe o passo a passo a seguir! 1. Instalação da biblioteca GLUT: • Baixe a biblioteca GLUT, disponível aqui. • Extraia os arquivos baixados para a pasta C:\GLUT (como demonstrado na imagem a seguir). Isso inclui arquivos essenciais como glut.h (header file). Tela do explorador de arquivos do Windows 10. 2. Configuração do Dev-C++: • Abra o Dev-C++ e navegue até Ferramentas -> Opções do compilador. • Adicione, Na aba Diretórios, a pasta C:\GLUT às pastas de busca do compilador. • Clique no ícone com uma seta (como demonstrado na tela a seguir) e selecione C:\GLUT. Tela do Dev C++. • Adicione a pasta tanto nas abas Bibliotecas como em C Includes para que o compilador saiba onde buscar os arquivos de biblioteca e os headers durante a compilação. 3. Adição do arquivo DLL: Copie o arquivo glut32.dll para a pasta C:\Windows\system32 ou C:\Windows\SysWOW64 dependendo da versão do seu sistema operacional (32 bits ou 64 bits, respectivamente). Você também pode colocar o arquivo diretamente no diretório do projeto para evitar problemas de permissão. 4. Criação e configuração do projeto: • Crie, no Dev-C++, um novo projeto selecionando Arquivo -> Novo -> Projeto e escolha Console application e Projeto C. https://drive.google.com/file/d/1yhcITDxmy4E7ozE4nzuj3KRsYZK4JmR_/view?usp=sharing • Acesse Projeto -> Opções do projeto, e na aba Parâmetros, na coluna Linkers, adicione os seguintes comandos: -lglut32 -lglu32 -lopengl32 Tela do Dev C++. Os comandos vistos garantem que o linker saiba quais bibliotecas adicionar ao compilar seu projeto. 5. Teste sua configuração: Experimente o ambiente com um código básico de OpenGL para verificar se tudo está funcionando corretamente. No arquivo principal do projeto (main.c), acrescente o código a seguir. Deverá aparecer uma janela (com fundo preto e um quadrado branco) e o título Ola Mundo: #include void display(void) { glClear(GL_COLOR_BUFFER_BIT); glBegin(GL_POLYGON); glVertex2f(-0.3, -0.3); glVertex2f(-0.3, 0.3); glVertex2f(0.3, 0.3); glVertex2f(0.3, -0.3); glEnd(); glFlush(); } int main(void) { glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB); glutCreateWindow("Ola Mundo"); glutDisplayFunc(display); glutMainLoop(); } Explicação do Código Exemplo ANTERIOR com OpenGL e GLUT 1. Inclusão da Biblioteca GLUT o #include : Importa funções para criação de janelas e interações com o usuário, facilitando o uso do OpenGL. 2. Função display: o Define a cena a ser desenhada na janela: ▪ glClear(GL_COLOR_BUFFER_BIT): Limpa o buffer de cor, preparando a janela para um novo desenho. ▪ glBegin(GL_POLYGON) ... glEnd(): Define um polígono usando vértices com glVertex2f. Controla o estado de renderização, especificando o tipo de geometria. ▪ glFlush(): Garante a execução imediata dos comandos de renderização. 3. Função main: o Configura a janela e o loop principal: ▪ glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB): Define o modo de exibição (buffer único e modelo de cor RGB). ▪ glutCreateWindow("Ola Mundo"): Cria a janela com título "Ola Mundo". ▪ glutDisplayFunc(display): Registra a função display para redesenhar a janela. ▪ glutMainLoop(): Inicia o loop de eventos, mantendo a aplicação ativa e gerenciando interações. Questão 1. Um programador está trabalhando em uma aplicação de design assistido por computador (CAD) que requer renderização gráfica avançada. Ele escolheu usar a biblioteca OpenGL pelo ambiente de desenvolvimento Dev-C++. Depois de configurar corretamente o Dev-C++ para incluir a biblioteca GLUT, qual éo próximo passo para garantir que os programas OpenGL sejam executados sem problemas? A. Compilar o código fonte usando o modo de compatibilidade do Windows. B. Copiar o arquivo glut32.dll para uma das pastas do sistema Windows adequadas. C. Alterar as configurações de energia do computador para alto desempenho. D. Reinstalar o Dev-C++ para garantir que todas as configurações sejam padrão. E. Converter todos os projetos antigos para a versão mais recente do Dev-C++. RESPOSTA: O passo essencial após configurar a biblioteca GLUT no ambiente Dev-C++ é garantir que o arquivo DLL, especificamente o glut32.dll, esteja na pasta adequada do sistema Windows. Isso é necessário porque o sistema operacional precisa acessar essa biblioteca dinâmica durante a execução de aplicativos que usam OpenGL. Colocar o glut32.dll na pasta do sistema ou no diretório do projeto evita erros de arquivo não encontrado ao iniciar o aplicativo. Mecanismo de máquina de estados do OpenGL Conceito de Máquina de Estados: • O OpenGL opera como uma máquina de estados, onde variáveis de contexto definem o comportamento da renderização. • Alterações nos estados (como modos de desenho) afetam diretamente a forma como a API renderiza gráficos. Gerenciamento do Estado: • A programação com OpenGL envolve modificar estados e usá-los para operações de renderização. • O gerenciamento eficaz de estados evita mudanças desnecessárias, otimizando o desempenho gráfico. Resumo do Mecanismo de Máquina de Estados • OpenGL opera como uma máquina de estados complexa • Gerenciamento eficiente dos estados é crucial para otimizar o desempenho • O entendimento da maquina de estados do OpenGL é fundamental para a criação de aplicações gráficas sofisticadas e interativas. • Uso dos Estados: Cada chamada de função (como glClear, glBegin, glVertex2f) altera o estado do OpenGL para configurar a renderização. • Persistência do Estado: O estado permanece ativo até ser modificado, demonstrando o princípio da máquina de estados da OpenGL. Questão 1. Em um projeto de desenvolvimento de jogos usando OpenGL, um programador está tentando otimizar o processo de renderização. Ele precisa decidir qual estratégia adotar para minimizar a frequência de alterações de estado, conhecidas por potencialmente reduzirem a eficiência do sistema. Qual das seguintes abordagens é a mais apropriada para otimizar o desempenho da renderização no OpenGL? A. Alterar o estado de renderização após cada chamada de desenho para garantir a precisão. B. Manter o número mínimo de mudanças de estado, agrupando operações similares. C. Utilizar múltiplos contextos de OpenGL para cada tipo de objeto a ser renderizado. D. Redefinir frequentemente o estado de renderização para evitar conflitos de buffer. E. Ignorar o estado da máquina até que ocorram erros de renderização, para, só então, ajustar. RESPOSTA: A eficiência na renderização no OpenGL pode ser severamente afetada por mudanças frequentes e desnecessárias no estado. Portanto, a melhor abordagem é minimizar as mudanças agrupando operações que compartilham configurações de estado similares. Isso reduz o overhead associado à configuração de estados e melhora o desempenho geral do sistema. ROTEIRO PRÁTICO: configurar um ambiente de desenvolvimento para OpenGL usando a biblioteca GLUT e criará um aplicativo simples que renderiza um cubo 3D rotativo. Você configurará a janela, definirá parâmetros de iluminação e câmera e implementará a animação do cubo por meio de rotações contínuas. Para essa prática em OpenGL com GLUT, assumiremos que você já tem o ambiente Dev-C++ configurado para as APIs em seu computador. Usando essa ferramenta, você aprenderá a criar um aplicativo que renderiza um cubo 3D rotativo. O resultado pode ser visto na imagem a seguir. #include // Função chamada para desenhar void display() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); // Posiciona a câmera gluLookAt(3.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); // Rotação simples ao redor do eixo Y static float angle = 0.0; glRotatef(angle, 0.0, 1.0, 0.0); // Desenha o cubo glutSolidCube(1.5); // Atualiza o ângulo angle += 0.2; // quanto maior o ângulo mais rápido esse cubo irá girar ex. 0.5 gira rápido, 0.1 gira lento if (angle > 360) na gle = 0; glutSwapBuffers(); } // Inicializa parâmetros de rendering void initRendering() { glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_NORMALIZE); glEnable(GL_COLOR_MATERIAL); // Define a cor do fundo glClearColor(0.1, 0.1, 0.1, 1.0); // Configuração da iluminação GLfloat lightPos[] = {1.0, 1.0, 1.0, 0.0}; glLightfv(GL_LIGHT0, GL_POSITION, lightPos); } // Chamado quando a janela é redimensionada void handleResize(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (double)w / (double)h, 1.0, 200.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char** argv) { // Inicializa GLUT e processa os argumentos do usuário glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); // Configura o tamanho e a posição inicial da janela glutInitWindowSize(800, 600); glutInitWindowPosition(100, 100); // Cria a janela glutCreateWindow("Exemplo de Cubo 3D OpenGL"); // Inicializa alguns parâmetros de rendering initRendering(); // Configura as funções de callback glutDisplayFunc(display); glutIdleFunc(display); glutReshapeFunc(handleResize); // Começa o loop principal glutMainLoop(); return 0; } amos analisar o código em detalhes. Vamos lá! 1. Função display • glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT): limpa os buffers de cor e profundidade para preparar para um novo quadro, garantindo que não haja resíduos visuais de desenhos anteriores. • glLoadIdentity(): restaura a matriz de transformação para o estado padrão (identidade), garantindo que as transformações não se acumulem entre os quadros. • gluLookAt(3.0, 3.0, 3.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0): define a posição da câmera. Os três primeiros valores são a posição da câmera no espaço, os três seguintes são as coordenadas para onde ela está olhando, e os últimos três definem a orientação vertical da câmera. • glRotatef(angle, 0.0, 1.0, 0.0): aplica uma rotação em torno do eixo Y (vertical) ao cubo, no qual angle é o ângulo de rotação. • glutSolidCube(1.5): desenha um cubo sólido com comprimento de aresta 1.5 unidades. • angle += 0.2: atualiza o ângulo para a próxima renderização, incrementando-o pela velocidade definida. • if (angle > 360) angle -= 36: garante que o valor do ângulo não cresça indefinidamente. • glutSwapBuffers(): troca os buffers de fundo para os de frente, exibindo o quadro renderizado e iniciando o desenho do próximo quadro no buffer oculto. 2. Função initRendering • glEnable(GL_DEPTH_TEST): habilita o teste de profundidade, que ajuda a garantir que superfícies sejam corretamente ocultadas por outras que estão à frente delas. • glEnable(GL_LIGHTING) e glEnable(GL_LIGHT0): habilitam a iluminação e a primeira fonte de luz, respectivamente, para adicionar efeitos de luz e sombra. • glEnable(GL_NORMALIZE) e glEnable(GL_COLOR_MATERIAL): as normais são vetores perpendiculares à superfície dos objetos em um cenário 3D. Eles são essenciais para o cálculo da iluminação, pois determinam como a luz interage com a superfície. Em OpenGL, quando um objeto é transformado (escalado, rotacionado ou transladado), suas normais também sofrem transformações. Para assegurar que elas sejam corretamente ajustadas e continuem a representarvetores unitários (de comprimento 1), utiliza-se o comando glEnable(GL_NORMALIZE). A função glEnable(GL_COLOR_MATERIAL) permite que as cores dos materiais dos objetos reajam corretamente à iluminação. • glClearColor(0.1, 0.1, 0.1, 1.0): define a cor de fundo. • glLightfv(GL_LIGHT0, GL_POSITION, lightPos): estabelece a posição da fonte de luz. 3. Função handleResize • glViewport(0, 0, w, h): ajusta a área de desenho para o tamanho atual da janela. • glMatrixMode(GL_PROJECTION) e glMatrixMode(GL_MODELVIEW): alternam entre modificar a matriz de projeção e a matriz de modelo/vista, respectivamente. • gluPerspective(45.0, (double)w / (double)h, 1.0, 200.0): define a perspectiva da câmera com um campo de visão de 45 graus e um aspecto baseado no tamanho da janela. 4. Função main • glutInit e funções relacionadas configuram a janela e o modo de exibição. • glutDisplayFunc(display) e glutIdleFunc(display): definem a função display como a função de callback para desenhar e atualizar a janela. • glutMainLoop(): inicia o loop de eventos da GLUT. ❖ EVENTOS DE TECLADO E MOUSE • Importância dos eventos: Essenciais em programação, especialmente em C, para criar software interativo que responda dinamicamente a ações do usuário, como cliques e pressionamentos de teclas. • Captura de eventos em C: Importante para aplicações de controle de hardware, possibilita o desenvolvimento de interfaces interativas, como caixas de loja que registram produtos com escaneamento. • Exemplo de captura de eventos: Quando o produto é escaneado, um evento é gerado, que o programa deve capturar para processar informações, adicionar o produto à lista e calcular o total. • Uso da Windows API (WinAPI): o É uma ferramenta amplamente usada no Windows para captura de eventos de entrada (teclado, mouse, scanners). o Fornece funções para capturar e processar eventos de hardware, adequada para entradas que emulam teclados, como scanners. #include #include // Callback para tratar eventos de teclado LRESULT CALLBACK KeyboardProc(int nCode, WPARAM wParam, LPARAM lParam) { if (nCode == HC_ACTION) { if (wParam == WM_KEYDOWN) { KBDLLHOOKSTRUCT *p = (KBDLLHOOKSTRUCT *)lParam; printf("Key pressed: %d\n", p->vkCode); // Processar evento de escaneamento aqui } } return CallNextHookEx(NULL, nCode, wParam, lParam); } int main() { // Configurar um hook de teclado global HHOOK keyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardProc, NULL, 0); if (keyboardHook == NULL) { printf("Failed to install hook!\n"); return 1; } // Loop de mensagens para manter o hook ativo MSG msg; while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } // Remover o hook quando terminar UnhookWindowsHookEx(keyboardHook); return 0; } • Exemplo de implementação: o A função SetWindowsHookEx configura um hook de teclado para capturar eventos de teclado (teclas pressionadas). o A função de callback KeyboardProc executa uma ação ao detectar uma tecla pressionada, permitindo processar o evento do scanner. • Vantagens da WinAPI: Oferece uma abordagem robusta e flexível para capturar e processar eventos de hardware em sistemas Windows, promovendo a criação de aplicações interativas e integradas a dispositivos de entrada, como scanners de código de barras. Questão 1. Em uma aplicação de controle de segurança desenvolvida em C para uma empresa, é essencial que o software responda a eventos específicos, como a detecção de um cartão RFID apresentado a um leitor. O programa deve capturar e processar esse evento para permitir ou negar o acesso. Considerando a importância de responder a esses eventos, qual das seguintes bibliotecas da API do Windows seria a melhor para ajudar na captura e no tratamento desses eventos? A. Windows Forms B. WinAPI C. MFC (Microsoft foundation classes) D. DirectX E. OpenGL RESPOSTA: A WinAPI (Windows application programming interface) é fundamental para desenvolver aplicações que precisam interagir de forma direta e eficiente com o hardware e o sistema operacional Windows. Ela permite manipulação detalhada de eventos de hardware, como a leitura de um cartão RFID. Interrupção em programação • Definição de Interrupção: É um sinal que avisa o processador para parar temporariamente o que está fazendo e atender algo mais importante. É usado principalmente para lidar com eventos de hardware • É um "alarme" urgente que interrompe temporariamente a execução do programa para que o processador trate de um evento prioritário, especialmente de hardware, por meio de uma rotina de serviço de interrupção (ISR). • Operações de Interrupção: o Ocorre o envio de sinal ao processador seja de software ou hardware; o Interrompe o fluxo normal de execução do programa; o O processador salva seu estado atual e executa uma rotina de serviço de interrupção-ISR. • Aplicação prática: Exemplo: Em sistemas embarcados (como drones), uma interrupção pode sinalizar ao processador para ajustar a altitude com base no sensor, se o aparelho estiver próximo ao chão. • Diferença entre interrupções e eventos: o Interrupções: São imediatas e de alta prioridade, exigindo resposta rápida do processador, como em sistemas críticos (ex.: paradas de emergência). Podem ocorrer a qualquer momento e precisam ser tratadas adequadamente. o Eventos: Podem ser processados no fluxo normal do programa, ideais para tarefas que não exigem resposta imediata, como atualizar uma interface de usuário. Processados de forma mais flexível. • A interrupção em programação: o Uso de interrupções: Garantem resposta pronta a condições críticas. Exemplo: Paradas de emergência de máquinas industriais ou sinais de sensores de em dispositivos médicos. o Uso de eventos: Criação de interface de usuário interativas Gerenciamento de tarefas que não exigem resposta imediata Exemplo: Atualizar um display ou processar dados de entrada em um formulário. • Uso em Sistemas Operacionais: o Em sistemas modernos (Windows, Linux), o gerenciamento direto de interrupções de hardware requer privilégios elevados e é geralmente feito pelo próprio sistema operacional. o Normalmente gerenciado pelo próprio sistema operacional. • Relevância para desenvolvedores de sistemas embarcados: Entender e usar interrupções é essencial para criar sistemas que respondam com precisão a eventos críticos de hardware, enquanto eventos são adequados para operações de software menos urgentes. Questão 1. No contexto de desenvolvimento de sistemas embarcados, considere um drone que utiliza interrupções para responder a sinais críticos, como a proximidade ao solo. Qual das seguintes afirmações descreve corretamente a função de uma interrupção no gerenciamento de hardware? A. Interrupções são ativadas apenas por eventos de software. B. Interrupções e eventos têm a mesma urgência e forma de tratamento pelo processador. C. Interrupções processam eventos não urgentes, como atualizações de display. D. Uma interrupção faz o processador parar para responder urgentemente a condições de hardware, como sensores de proximidade. E. Interrupções são desnecessárias em sistemas embarcados modernos, já que o hardware os gerencia automaticamente. RESPOSTA: A função de uma interrupção é forçar o processador a pausar suas atividades atuais e responder a situações de hardware urgentes. As outras alternativas são incorretas por distorcerem a natureza e a urgência das interrupções em comparação aos eventos ou por sugerirem redução gradual incorreta das interrupções em tecnologias modernas. Captura de eventos de teclado e mouse • Objetivo da Captura de eventos de teclado e mouse: Capturar eventos de teclado e mouse para criar aplicativos interativose responsivos no Windows usando a linguagem C. • Relevância: • Essencial para o desenvolvimento de interfaces que respondem às ações do usuário. • Base para aplicativos interativos, como jogos ou programas que precisam de input do usuário. • Uso da Biblioteca windows.h: • A biblioteca fornece as funções necessárias para interagir com o sistema Windows e capturar eventos de entrada. • Importante configurar corretamente o ambiente de desenvolvimento, como o Dev-C++, para garantir o funcionamento da API. • Captura de Eventos: • Procedimento de Janela (WindowProcedure): Função que intercepta e manipula eventos, como cliques de mouse e pressionamentos de teclas. • Estruturação do Código: o Define-se uma classe de janela (WNDCLASSEX) e cria-se a janela com CreateWindowEx. o Exemplo: Eventos são capturados e exibidos via mensagens, como um clique do botão esquerdo do mouse ou a tecla "A" pressionada. #include /* inclui o arquivo de cabeçalho windows.h, que contém declarações para todas as funções da API do Windows, tipos de dados e constantes necessárias para criar aplicações Windows.*/ // Protótipo da função de tratamento de mensagens da janela LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM); /*Declara um protótipo para a função de procedimento da janela. Esta função é chamada pela API do Windows para tratar mensagens direcionadas para a janela, como cliques do mouse, movimentos, fechamento da janela, etc.*/ char szClassName[] = "WindowsApp"; /*WinMain é o ponto de entrada para uma aplicação Windows, equivalente à função main em programas C padrão. Esta função é chamada pelo sistema operacional quando a aplicação é iniciada.*/ int WINAPI WinMain(HINSTANCE hThisInstance, HINSTANCE hPrevInstance, LPSTR lpszArgument, int nCmdShow) { HWND hwnd; MSG messages; WNDCLASSEX wincl; /*Define uma estrutura que contém informações sobre a classe da janela. Esta estrutura é usada para registrar a classe da janela e definir algumas propriedades importantes, como o estilo da janela, o ícone, o cursor e a função de procedimento da janela.*/ // Estrutura de definição da janela wincl.hInstance = hThisInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbSize = sizeof(WNDCLASSEX); wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor(NULL, IDC_ARROW); wincl.lpszMenuName = NULL; wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = (HBRUSH)COLOR_BACKGROUND; /*Registra a classe da janela com a API do Windows. Se o registro falhar, o programa termina imediatamente.*/ if (!RegisterClassEx(&wincl)) return 0; // Criação da janela /*Cria uma janela usando a classe registrada. Define o estilo da janela, o título, as dimensões e outros parâmetros importantes.*/ hwnd = CreateWindowEx( 0, szClassName, "Clique do Mouse Detector", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, hThisInstance, NULL ); /*ShowWindow exibe a janela na tela em um estado especificado (nCmdShow). UpdateWindow força a janela a se redesenhar.*/ ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // Loop de mensagens /*O loop de mensagens é fundamental em aplicações Windows. Ele aguarda por mensagens do sistema, como eventos de entrada do usuário, e despacha essas mensagens para a função de procedimento da janela apropriada.*/ while (GetMessage(&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } return messages.wParam; } // Função de tratamento de mensagens /*Trata as mensagens enviadas para a janela. WM_LBUTTONDOWN e WM_RBUTTONDOWN são tratados para detectar cliques nos botões esquerdo e direito do mouse, respectivamente. WM_DESTROY é usado para terminar o loop de mensagens e fechar a aplicação.*/ LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_LBUTTONDOWN: MessageBox(hwnd, "Clique esquerdo detectado!", "Evento do Mouse", MB_OK | MB_ICONINFORMATION); break; case WM_RBUTTONDOWN: MessageBox(hwnd, "Clique direito detectado!", "Evento do Mouse", MB_OK | MB_ICONINFORMATION); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } O próximo código detecta cliques no botão esquerdo do mouse e na tecla A, indicando no nome da janela se é um evento de teclado ou de mouse. #include // Protótipo do procedimento de janela LRESULT CALLBACK WindowProcedure(HWND, UINT, WPARAM, LPARAM); // Nome da classe para a janela char szClassName[] = "WindowClass"; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { HWND hwnd; MSG messages; WNDCLASSEX wincl; // Estrutura de classe de janela wincl.hInstance = hInstance; wincl.lpszClassName = szClassName; wincl.lpfnWndProc = WindowProcedure; wincl.style = CS_DBLCLKS; wincl.cbSize = sizeof (WNDCLASSEX); // Ícones e ponteiros padrão wincl.hIcon = LoadIcon(NULL, IDI_APPLICATION); wincl.hIconSm = LoadIcon(NULL, IDI_APPLICATION); wincl.hCursor = LoadCursor(NULL, IDC_ARROW); wincl.lpszMenuName = NULL; // Sem menu wincl.cbClsExtra = 0; wincl.cbWndExtra = 0; wincl.hbrBackground = (HBRUSH) COLOR_BACKGROUND; if (!RegisterClassEx(&wincl)) return 0; // Criação da janela hwnd = CreateWindowEx( 0, szClassName, "Captura de Eventos de Teclado e Mouse", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 544, 375, HWND_DESKTOP, NULL, hInstance, NULL ); ShowWindow(hwnd, nCmdShow); UpdateWindow(hwnd); // Loop de mensagens while (GetMessage(&messages, NULL, 0, 0)) { TranslateMessage(&messages); DispatchMessage(&messages); } return messages.wParam; } // Função de callback para eventos da janela LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) { case WM_KEYDOWN: if (wParam == 'A') { MessageBox(hwnd, "Tecla A pressionada", "Evento de Teclado", MB_OK); } break; case WM_LBUTTONDOWN: MessageBox(hwnd, "Botão esquerdo do mouse clicado", "Evento de Mouse", MB_OK); break; case WM_CLOSE: DestroyWindow(hwnd); break; case WM_DESTROY: PostQuitMessage(0); break; default: return DefWindowProc(hwnd, message, wParam, lParam); } return 0; } • Código de Exemplo: • A função WindowProcedure trata eventos específicos, como WM_KEYDOWN (teclado) e WM_LBUTTONDOWN (clique do mouse). • O código exibe mensagens na tela para informar qual evento foi detectado, útil para verificar a funcionalidade do programa. • Conclusão: • A captura de eventos em C no Windows é um recurso fundamental para desenvolvedores interessados em criar aplicações interativas e reativas. • Dominar essas técnicas em C é útil tanto para interfaces gráficas quanto para aplicações que necessitam de interação