Baixe o app para aproveitar ainda mais
Prévia do material em texto
Universidade Estácio de Sá Linguagem de Programação C Notas de aula Aula 10 Profª. Paula Faragó farago@infolink.com.br Observações: 1. todas as notas de aula passadas aos alunos não substituem a leitura dos conteúdos presentes nas referências bibliográficas indicadas pelo professor; 2. os alunos devem complementar as notas de aula com as anotações feitas em sala de aula e participar da elaboração dos exercícios propostos no fim de cada arquivo e nas listas de exercícios fornecidas pelo professor. 3. fontes de referência: • notas de aula do Prof. Prof. Márcio Santi (PUC-RJ) e Rogério (UNESA); • curso de C da UFMG. Objetivo do item 20: Apresentar uma introdução ao estudo de ponteiros na linguagem C. Objetivo do item 21: Fazer uso de tipos de dados definidos pelo usuário, primeiramente abordando o uso de estruturas. 20 – PONTEIROS 20.1 - Introdução A linguagem C permite o armazenamento e a manipulação de valores de endereços de memória. Para cada tipo existente, há um "tipo" que pode armazenar um endereço de uma variável do tipo correspondente. Por exemplo, quando se escreve: int a; declara-se uma variável com nome a que pode armazenar valores inteiros. Automaticamente, o compilador reserva dois bytes da memória para este armazenamento. De maneira análoga, pode-se declarar uma variável que, ao invés de servir para armazenar números inteiros, serve para armazenar endereços de memória onde há variáveis inteiras armazenadas. C não reserva uma outra palavra para a declaração de ponteiros. Usa-se a mesma palavra do tipo com as variáveis precedidas pelo caractere *. Assim, pode-se escrever: int *p; Neste caso, declara-se uma variável com nome p que pode armazenar endereços de memória onde existe um inteiro armazenado. Analisa-se um exemplo simples, ilustrando o que ocorre na pilha de execução esquemática. O operador unário & retorna o endereço de memória onde uma variável está armazenada e o operador unário * trata seu operando como endereço e acessa este endereço para buscar o conteúdo do objeto alvo. Tem-se, por exemplo: /*a é uma variável do tipo inteiro */ /*p é uma variável do tipo ponteiro p/ inteiro */ a p - - 101 103 107 int a; int *p; Neste ponto, ambas as variáveis a e p armazenam valores "lixo", pois não foram inicializadas. Pode-se fazer: Página 1 de 12 Página 2 de 12 /* a recebe o valor 5 */ /* p recebe o endereço de a (diz-se p aponta para a) */ a p 5 - 101 103 107 /* conteúdo de p recebe o valor 6 */ a p 5 101 101 103 107 a p 6 101 101 103 107 a = 5; p = &a; *p = 6; Assim, a variável a recebe, indiretamente, o valor 6. A possibilidade de manipular ponteiros de variáveis é uma das maiores potencialidades de C. Por outro lado, o uso indevido desta manipulação é o maior causador de programas que "voam", isto é, que não funcionam e que, pior ainda, podem gerar efeitos colaterais não previstos. Em micro computadores, é comum um ponteiro mal utilizado afetar o próprio sistema operacional, pois neste ambiente não há proteção do sistema. A seguir, ilustra-se outros exemplos de uso de ponteiros. void main ( void ) { int a; int *p; p = &a; *p = 2; printf ( " %d ", a ); } Será impresso o valor 2. Agora, nota-se o exemplo abaixo: void main ( void ) { int a, b, *p; a = 2; *p = 3; b = a + (*p); printf ( " %d ", b ); } Este exemplo é um típico ERRO na manipulação de ponteiros. Pior, constitui-se num programa que, embora incorreto, às vezes pode funcionar. O erro está em usar a memória apontada por p para armazenar o valor 3. Ora, a variável p não tinha sido inicializada e, portanto, tinha armazenado um valor (no caso, endereço) "lixo". Assim, a atribuição *p = 3 armazena 3 num espaço de memória desconhecido. Este espaço desconhecido tanto pode ser um espaço de memória não utilizado, e aí o programa aparentemente funciona bem, como pode ser um espaço que armazena outras informações fundamentais, por exemplo, o espaço de memória utilizado pelo sistema operacional. E, neste caso, o erro pode ter efeitos colaterais indesejados. Portanto, só se pode preencher o conteúdo de um ponteiro se este tiver sido devidamente inicializado, isto é, ele deve apontar para um espaço de memória onde já se prevê o armazenamento de valores do tipo em questão. De maneira análoga, pode-se declarar ponteiros de qualquer tipo: float *m; char *s; 20.2 Aritmética de ponteiros C permite aplicar as operações aritméticas, adição e subtração, em variáveis do tipo ponteiro. Assim, se porventura p é um ponteiro válido de float (4 bytes) e faz-se: p = p + 1; incrementa-se o valor armazenado em p de 4 (bytes). Ele passa a apontar para um possível próximo float armazenado na memória. Deve-se ter atenção. Considera-se o código: void main ( void ) { int a, b; int *p = &a; p++; } p apontava para a variável a. Após o incremento (p é incrementado de 2 (bytes)), nada garante que p passe a apontar para b. Isto porque a e b são independentes e o compilador não necessariamente as armazena continuamente na memória. Mais uma vez, salienta-se que o modelo de pilha ilustrado é apenas uma abstração que retrata didaticamente o funcionamento interno, não necessariamente na ordem de armazenamento ilustrada. Aritmética de ponteiros só é útil quando se trabalha com vetores, o que será discutido mais adiante. 20.3 Passando ponteiros para função Conforme apresentado, numa chamada de função, os parâmetros são copiados para a pilha e assim a função nunca altera os valores das variáveis originais da função que chama. Por exemplo, uma função para trocar os valores de duas variáveis: void troca (int x, int y ) { int temp; temp = x; x = y; y = temp; } void main ( void ) { int a = 5, b = 7; troca ( a, b ); printf ( "%d %d \n", a, b ); } não funciona desta forma (será impresso 5 e 7), pois os valores de a e b da função main não são alterados. A alternativa é fazer com que a função receba os endereços das variáveis e, assim, alterar seus valores indiretamente. Reescrevendo: void troca (int *px, int *py ) { int temp; temp = *px; *px = *py; *py = temp; Página 3 de 12 } void main ( void ) { int a = 5, b = 7; troca ( &a, &b ); printf ( "%d %d \n", a, b ); } Acompanhando através do modelo de pilha, tem-se: main > 1 -Declaração das variáveis: a, b main > 2 - Chamada da função: passa endereços 3 - Declaração da variável local: temp 4 - temp recebe conteúdo de px 5 -Conteúdo de px recebe conteúdo de py 6 -Conteúdo de py recebe temp a b 101 103 5 7 a b 101 103 5 7troca > px py 101 103 main > a b 101 103 5 7troca > px py 101 103 temp - 105 105 109 113 105 109 113 main > a b 101 103 5 7troca > px py 101 103 temp 5 105 109 113 main > a b 101 103 7 7troca > px py 101 103 temp 5 105 109 113 main > a b 101 103 7 5troca > px py 101 103 temp 5 105 109 113 E assim, consegue-se o efeito desejado. Agora fica explicado porque se passa o endereço das variáveis para a função scanf, pois, caso contrário, a função não conseguiria devolver os valores lidos. Compilando vários fontes: Original em: http://www.linux.trix.net/prog2.htm gcc - O Compilador Sem dúvida o compilador mais utilizado e importante dos sistemas Unix. Embora existam outros compiladores para a linguagem C disponíveis, o gcc é, de longe, o mais famoso e completo. Ele foi criado e mantido pela comunidade GNU, e está em estágio de ser considerado um dos melhores compiladores existentes para a linguagem C. Existem inúmeros detalhes que você não precisa saber no momento sobre o projeto do gcc, como codinomes, versões otimizadas, versões em desenvolvimento, etc... Simplesmente o chame de "gcc" e viva (ou melhor, programe) feliz. A utilização é simples: após criar um arquivo com o código corretamente escrito, você deveinvocar o gcc da seguinte forma: $ gcc fonte.c O resultado é a criação de um arquivo executavel de nome "a.out" (esse é o nome dado a novos executáveis por razões históricas, que não vem ao caso agora). Se você quer que o gcc crie um arquivo executável com um nome específico (e geralmente você quer), você deve utilizar a opção (que chamaremos de "flag") "-o". Ela significa "outupt" (saída), e é utilizada antes do nome do arquivo que será criado. Abaixo refaremos a compilação do exemplo anterior, mas criando um arquivo de nome "executavel": $ gcc fonte.c -o executavel Como você deve estar ciente, em linux não estamos presos à extensões de nome de arquivo (.exe, .com, .bin, etc) e, Página 4 de 12 http://www.linux.trix.net/prog2.htm portanto, não há necessidade de colocarmos uma em nosso programa recém criado. Para compilar programas que estejam em vários arquivos, você deve primeiramente compilar os módulos como objetos para então compilar o programa principal ou invocar o gcc com todos os arquivos de fonte ao mesmo tempo. Vamos ver como exemplo o caso da compilação de um programa que esteja separado em três partes (arquivos): interface.c, processamento.c e principal.c A sequencia para a criação de nosso programa, que chamaremos de "teste" seria: $ gcc interface.c processamento.c principal.c -o teste ou $ gcc -c inteface.c -o interface.o $ gcc -c processamento.c -o processamento.o $ gcc interface.o processamento.o principal.c -o teste A flag "-o" já é por nós conhecida. A novidade fica por conta da flag "-c" (compilar mas não linkar) que é utilizada para a criação do arquivo objeto. O processo de gerar os objetos para então criar o executável se torna cansativo e demais trabalhoso para ser feito sem uma automação. Para essa tarefa veremos o utilitário "make" logo abaixo. Outras flags bastante importantes são as de "warnings" (alertas). São elas que vão detectar problemas em nosso código que vão desde falta de estilo até problemas com passagem de parâmetros e, principalmente, ponteiros e estruturas complexas. As mais utilizadas e por mim recomendadas são: -pedantic -Wall -W -Wtraditional -Wshadow -Wpointer-arith -Wbad-function-cast -Wcast-qual -Wcast-align -Wwrite-strings -Wconversion -Waggregate-return -Wmissing-prototypes -Wmissing-declarations -Wnested-externs -Winline -Wwrite-strings A explicação de cada uma dessas opções é muito extensa para constar aqui. Para isso é altamente recomendado que você faça uma consulta à documentação do gcc (páginas manuais e info). Note que algumas dessas opções podem se tornar incômodas na criação de programas que, por um motivo ou outro, precisam quebrar certas "regras" para chegar a algum resultado ou tenham um grau de complexidade alto. 21 - ESTRUTURAS 21.1 - Introdução Uma estrutura agrupa várias variáveis numa só. Funciona como uma ficha pessoal que tenha nome, telefone e endereço. A ficha seria uma estrutura. A estrutura, então, serve para agrupar um conjunto de dados não similares, formando um novo tipo de dados. 21.2 - Criando Para se criar uma estrutura usa-se o comando struct. Sua forma geral é: struct nome_do_tipo_da_estrutura { tipo_1 nome_1; tipo_2 nome_2; ... Página 5 de 12 tipo_n nome_n; } variáveis_estrutura; O nome_do_tipo_da_estrutura é o nome para a estrutura. As variáveis_estrutura são opcionais e seriam nomes de variáveis que o usuário já estaria declarando e que seriam do tipo nome_do_tipo_da_estrutura. Um primeiro exemplo: struct est{ int i; float f; } a, b; Neste caso, est é uma estrutura com dois campos, i e f. Foram também declaradas duas variáveis, a e b que são do tipo da estrutura, isto é, a possui os campos i e f, o mesmo acontecendo com b. Vamos criar uma estrutura de endereço: struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; Vamos agora criar uma estrutura chamada ficha_pessoal com os dados pessoais de uma pessoa: struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; Vemos, pelos exemplos acima, que uma estrutura pode fazer parte de outra ( a struct tipo_endereco é usada pela struct ficha_pessoal). 21.3 Usando Vamos agora utilizar as estruturas declaradas na seção anterior para escrever um programa que preencha uma ficha. #include <stdio.h> #include <string.h> struct tipo_endereco { char rua [50]; int numero; char bairro [20]; char cidade [30]; char sigla_estado [3]; long int CEP; }; struct ficha_pessoal { char nome [50]; long int telefone; struct tipo_endereco endereco; }; main (void) { struct ficha_pessoal ficha; Página 6 de 12 strcpy (ficha.nome,"Luiz Osvaldo Silva"); ficha.telefone=4921234; strcpy (ficha.endereco.rua, "Rua das Flores"); ficha.endereco.numero=10; strcpy (ficha.endereco.bairro,"Cidade Velha"); strcpy (ficha.endereco.cidade,"Belo Horizonte"); strcpy (ficha.endereco.sigla_estado,"MG"); ficha.endereco.CEP=31340230; return 0; } O programa declara uma variável ficha do tipo ficha_pessoal e preenche os seus dados. O exemplo mostra como podemos acessar um elemento de uma estrutura: basta usar o ponto (.). Assim, para acessar o campo telefone de ficha, escrevemos: ficha.telefone = 4921234; Como a struct ficha pessoal possui um campo, endereco, que também é uma struct, podemos fazer acesso aos campos desta struct interna da seguinte maneira: ficha.endereco.numero = 10; ficha.endereco.CEP=31340230; Desta forma, estamos acessando, primeiramente, o campo endereco da struct ficha e, dentro deste campo, estamos acessando o campo numero e o campo CEP. 21.4 - Matrizes de estruturas Um estrutura é como qualquer outro tipo de dado no C. Podemos, portanto, criar matrizes de estruturas. Vamos ver como ficaria a declaração de um vetor de 100 fichas pessoais: struct ficha_pessoal fichas [100]; Poderíamos então acessar a segunda letra da sigla de estado da décima terceira ficha fazendo: fichas[12].endereco.sigla_estado[1]; Analise atentamente como isto está sendo feito ... 21.5 Atribuindo Podemos atribuir duas estruturas que sejam do mesmo tipo. O C irá, neste caso, copiar uma estrutura, campo por campo, na outra. Veja o programa abaixo: struct est1 { int i; float f; }; void main() { struct est1 primeira, segunda; /* Declara primeira e segunda como structs do tipo est1 */ primeira.i = 10; primeira.f = 3.1415; segunda = primeira; /* A segunda struct e' agora igual a primeira */ printf(" Os valores armazenasdos na segunda struct sao : %d e %f ", segunda.i , segunda.f); } São declaradas duas estruturas do tipo est1, uma chamada primeira e outra chamada segunda. Atribuem-se valores aos dois campos da struct primeira. Os valores de primeira são copiados em segunda apenas com a expressão de atribuição: Página 7 de 12 segunda = primeira; Todos os campos de primeira serão copiados na segunda. Note que isto é diferente do que acontecia em vetores, onde, para fazer a cópia dos elementos de um vetor em outro, tínhamos que copiar elemento por elemento do vetor. Nas structs é muito mais fácil! Porém, devemos tomar cuidado na atribuição de structs que contenham campos ponteiros. Veja abaixo: #include <stdio.h> #include <string.h> #include <stdlib.h> struct tipo_end { char *rua; /* A struct possui um campo que é um ponteiro */ int numero; }; void main() { struct tipo_end end1, end2; char buffer[50]; printf("\nEntre o nome da rua:"); gets(buffer); /* Le o nome da rua em uma string de buffer */ /* Aloca a quantidade de memoria suficiente para armazenar a string */end1.rua = (char *) malloc((strlen(buffer)+1)*sizeof(char)); strcpy(end1.rua, buffer); /* Copia a string */ printf("\nEntre o numero:"); scanf("%d", &end1.numero); end2 = end1; /* ERRADO end2.rua e end1.rua estao apontando para a mesma regiao de memoria */ printf("Depois da atribuicao:\n Endereco em end1 %s %d \n Endereco em end2 %s %d", end1.rua,end1.numero,end2.rua, end2.numero); /* Uma modificacao na memoria apontada por end2.rua causara' a modificacao do que e' apontado por end1.rua, o que, esta' errado !!! */ strcpy(end2.rua, "Rua Mesquita"); end2.numero = 1100; /* Nesta atribuicao nao ha problemas */ printf(" \n\nApos modificar o endereco em end2:\n Endereco em end1 %s %d \n Endereco em end2 %s %d", end1.rua, end1.numero, end2.rua, end2.numero); } Neste programa há um erro grave, pois ao se fazer a atribuição end2 = end1, o campo rua de end2 estará apontando para a mesma posição de memória que o campo rua de end1. Assim, ao se modificar o conteúdo apontado por end2.rua estaremos também modificando o conteúdo apontado por end1.rua !!! Passando para funções No exemplo apresentado no item usando, vimos o seguinte comando: strcpy (ficha.nome,"Luiz Osvaldo Silva"); Neste comando um elemento de uma estrutura é passado para uma função. Este tipo de operação pode ser feita sem maiores considerações. Podemos também passar para uma função uma estrutura inteira. Veja a seguinte função: void PreencheFicha (struct ficha_pessoal ficha) { ... } Como vemos acima é fácil passar a estrutura como um todo para a função. Devemos observar que, como em qualquer outra função no C, a passagem da estrutura é feita por valor. A estrutura que está sendo passada, vai ser Página 8 de 12 copiada, campo por campo, em uma variável local da função PreencheFicha. Isto significa que alterações na estrutura dentro da função não terão efeito na variável fora da função. Mais uma vez podemos contornar este pormenor usando ponteiros e passando para a função um ponteiro para a estrutura. Ponteiros Podemos ter um ponteiro para uma estrutura. Vamos ver como poderia ser declarado um ponteiro para as estruturas de ficha que estamos usando nestas seções: struct ficha_pessoal *p; Os ponteiros para uma estrutura funcionam como os ponteiros para qualquer outro tipo de dados no C. Para usá-lo, haveria duas possibilidades. A primeira é apontá-lo para uma variável struct já existente, da seguinte maneira: struct ficha_pessoal ficha; struct ficha_pessoal *p; p = &ficha; A segunda é alocando memória para ficha_pessoal usando, por exemplo, malloc(): #include <stdlib.h> main() { struct ficha_pessoal *p; int a = 10; /* Faremos a alocacao dinamica de 10 fichas pessoais */ p = (struct ficha_pessoal *) malloc (a * sizeof(struct ficha_pessoal)); /* Exemplo de acesso ao campo telefone da primeira ficha apontada por p */ p[0].telefone = 3443768; free(p); } Há mais um detalhe a ser considerado. Se apontarmos o ponteiro p para uma estrutura qualquer (como fizemos em p = &ficha; ) e quisermos acessar um elemento da estrutura poderíamos fazer: (*p).nome Os parênteses são necessários, porque o operador . tem precedência maior que o operador * . Porém, este formato não é muito usado. O que é comum de se fazer é acessar o elemento nome através do operador seta, que é formado por um sinal de "menos" (-) seguido por um sinal de "maior que" (>), isto é: -> . Assim faremos: p->nome A declaração acima é muito mais fácil e concisa. Para acessarmos o elemento CEP dentro de endereco faríamos: p->endereco.CEP Fácil, não? Exercícios 1 - Jogo da Senha O jogo da senha consiste em: o computador gera um número secreto de quatro dígitos e você tenta adivinhá-los. Os zeros não são admitidos e nenhum dígito pode ser repetido. Mesmo com essas restrições, há 3024 códigos possíveis, tornando escassas suas chances de adivinhar o número na primeira tentativa. Sua aposta é contada pelo computador que apresenta uma resposta do tipo "N.R", onde N é o número de dígitos do seu "chute" que está na ordem correta e R o número de dígitos que, embora presente no número secreto, estão em posições erradas. Por exemplo, se o número gerado pelo computador for 8261 e sua tentativa for 6285, a resposta deverá ser 1.2. Quando a resposta for 4.0 o número foi descoberto. Página 9 de 12 Faça um programa que implemente este jogo. Divirta-se! 2 - Por que o código abaixo não funciona? char *resposta; printf ( " Entre com uma palavra :\n " ); scanf ( "%s", resposta ); printf ( " Voce entrou com: %s \n ", resposta ); 3 - O que faz o programa abaixo? #include <stdio.h> int main(int argc, char *argv[]) { int i; if( argc < 2 ) { printf("Voce não passou argumento algum.\n"); } else { printf( "Voce passou %d argumento(s):\n\n", argc-1 ); for(i = 1; i < (argc); i++) printf("%s\n", argv[i]); } system ("pause"); return 0; } 4. Escreva um programa fazendo o uso de struct's. Você deverá criar uma struct chamada Ponto, contendo apenas a posição x e y (inteiros) do ponto. Declare 2 pontos, leia a posição (coordenadas x e y) de cada um e calcule a distância entre eles. Apresente no final a distância entre os dois pontos. 5. Seja a seguinte struct que é utilizada para descrever os produtos que estão no estoque de uma loja : struct Produto { char nome[30]; /* Nome do produto */ int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ }; a) Escreva uma instrução que declare uma matriz de Produto com 10 itens de produtos; b) Atribua os valores "Pe de Moleque", 13205 e R$0,20 aos membros da posição 0 e os valores "Cocada Baiana", 15202 e R$0,50 aos membros da posição 1 da matriz anterior; c) Faça as mudanças que forem necessárias para usar um ponteiro para Produto ao invés de uma matriz de Produtos. Faça a alocação de memória de forma que se possa armazenar 10 produtos na área de memória apontada por este ponteiro e refaça as atribuições da letra b; d) Escreva as instruções para imprimir os campos que foram atribuídos na letra c. 6. Usando o compilador do linux , complete o programa abaixo de modo a fazer funcionar todas as opções. #include <stdio.h> #include <string.h> Página 10 de 12 void incluir(void); void excluir(void); void listar(void); void pesquisar(void); struct data { int dia, mes, ano; }; struct { char nome[20]; float sal; int idade; struct data aniversario; }func[100]; int cont = 0; int main() { char resp; do { system ("cls"); printf ("Cadastro de Funcionários \n"); printf ("a - Incluir \n"); printf ("b - Excluir \n"); printf ("c - Listar todos \n"); printf ("d - Pesquisar \n"); printf ("f - Fim \n"); printf ("Selecione a opção desejada : "); resp = getch(); switch (resp) { case 'a' : incluir(); break; case 'b' : excluir(); break; case 'c' : listar() ; break; case 'd' : pesquisar(); break; case 'f' : break; default : printf ("\nOpção inválida !!! "); system ("pause"); } } while (resp != 'f'); return 0; } void incluir(void) { int k = 0; if (cont == 99) { printf ("\n\nLista cheia \n\n"); system ("pause"); return; } while (func[k].idade != 0) k++; printf ("\nNome = "); scanf ("%s", &func[k].nome); printf ("\nIdade = "); scanf ("%d", &func[k].idade); printf ("\nSalario = "); scanf("%f", &func[k].sal); cont++; } void excluir(void) { printf ("\n\nSelecionou opção b\n\n"); Página 11 de 12 system ("pause"); } void listar(void) { int i; if (cont == 0) { printf ("\n\nLista vazia \n\n"); system ("pause"); return; } printf ("\n\n"); for (i=0;i<100;i++) { if (func[i].idade != 0) { printf ("%20s", func[i].nome); printf ("%8d", func[i].idade); printf ("%10.1f\n", func[i].sal); } } system ("pause"); } void pesquisar(void) { printf ("\n\nSelecionou opção d\n\n"); system ("pause"); } 7. Explique a diferença entre: p++; (*p)++; *(p++) e *(p+10); p++ // p = p+1; A variável p é acrescida de uma unidade. (*p)++ // *p = *p + 1; O valor apontado por p é acrescido de uma unidade. *(p++) // O ponteiro p é acrescido de uma unidade (aponta para o próximo endereço). *(p+10) // O ponteiro p é acrescido de dez (10) unidades (aponta para o décimo endereço a frente). 7. Escreva o resultado do programa em C abaixo: void main() { int y, *p, x; y = 0; p = &y; x = *p; x = 4; *p (*p)++; x--; (*p) += x; printf ("y = %d\n", y); *p } Resultado: y = 4 x y 0 0 0 0 4 0 4 1 3 1 3 4 Página 12 de 12 9. Considere a seguinte declaração de variáveis struct box { int id; float dx; float dy; } struct box c; struct box *pc; a) Demonstre como os campos das variáveis c e pc são acessados. b) Defina um novo tipo denominado Conta como sendo uma estrutura conta. 10. Considere a seguinte declaração de variáveis struct box { int id; float dx; float dy; } struct box c; struct box *pc; a) Demonstre como os campos das variáveis c e pc são acessados. b) Defina um novo tipo denominado Conta como sendo uma estrutura conta. 11. Considere a seguinte estrutura que é utilizada para descrever os produtos que estão no estoque de uma loja: struct produto { int codigo; /* Codigo do produto */ double preco; /* Preco do produto */ }; a) Escreva uma instrução para definir um vetor da estrutura produto com 1000 itens; b) Escreva os comandos necessários para alocar dinamicamente um vetor da estrutura produto com 1000 itens; c) Escreva um trecho de código para ler de um arquivo uma lista com 1000 itens de produtos. 12. Considere a seguinte estrutura: /* Núme #define N 100 ro máximo de vértices */ /* Estrutura Ponto */ struct ponto { float x, y; }; typedef struct ponto Ponto; a) Defina um novo tipo Triangulo, o qual armazenará as coordenadas dos vértices de um triângulo; b) Defina um vetor elem de dimensão N de estruturas do tipo Triangulo, c) Elabore um programa que leia de um arquivo as coordenadas dos vértices dos triângulos, armazene-as em elem. d) Repita os passos anteriores utilizando alocação dinâmica, onde o número de Página 13 de 12 elementos do vetor elem, também é lido do arquivo. 13. Considere o trecho de código abaixo: #include <stdio.h> #include <stdlib.h> struct coord { float x; float y; } ponto; void main() { int a, b; int *p1, *p2; float x, y; float vet[] = {1.0,2.0,3.0,4.0,5.0}; struct coord *p0; a = b = 1; p1 = &a; p2 = &b; ponto.x = vet[0]/2; ponto.y = vet[2]/2; p0 = &ponto; /* trecho de código de cada opção entra aqui */ printf(" x = %6.2f\n",x); printf(" y = %6.2f\n",y); } Em cada uma das opções abaixo, escreva o resultado que será impresso pelo programa caso a linha em comentário seja substituída pelo de trecho de código correspondente: (a) x = (*p1)/2; y = 3*(*p2); Resp: x = 0.00 y = 3.00 (b) x = *(vet + 2)/2; y = a + vet[4]/2; Resp: x = 1.50 y = 3.50 (c) x = p0->x; y = ponto.y; Resp: x = 0.50 y = 1.50 14. Considere a seguinte declaração de variáveis: #define NMAX 1000 #define NPTS 100 struct coord { double x; double y; }; s Página 14 de 12 truct poligono { int np; struct coord *pt; }; int i,j; struct poligono *poli; Escreva um trecho de código para realizar os seguintes procedimentos: (a) alocar dinamicamente um vetor de dimensão NMAX e associa-lo ao ponteiro poli; (b) para cada elemento i do vetor poli, alocar dinamicamente um vetor de NPTS elementos do tipo struct coord e associa-lo ao campo pt do elemento poli[i]; (c) inicializar o campo np de cada elemento i do vetor poli; com o valor NPTS; (d) inicializar com o valor 0.0 os campos x e y de cada elemento j do vetor pt, associado ao elemento i do vetor poli; // item (a) poli = (struct poligono *) malloc(NMAX*sizeof(struct poligono)); f or (i = 0; i < NMAX; i++) { // item (b) poli[i].pt = (struct coord *) malloc(NPTS*sizeof(struct coord)); // item (c) poli[i].np = NPTS; // item (d) for (j = 0; j < NPTS; j++) { poli[i].pt[j].x = 0.0; poli[i].pt[j].y = 0.0; } } Página 15 de 12 15. Página 16 de 12
Compartilhar