elementos de programação em c respostas dos exercícios ... · elementos de programação em c...

44
Elementos de programação em C Respostas dos exercícios selecionados Francisco A. C. Pinheiro, Elementos de programação em C, Bookman Editora, Porto Alegre, 2012. Visite o sítio da editora para obter mais informações sobre o livro: www.bookman.com.br As respostas de todos os exercícios cuja solução envolve codificação são apresentadas aqui. Algumas vezes o código é comentado para ressaltar pontos importantes da solução adotada ou esclarecer o seu funcionamento. É importante salientar que não existem gabaritos absolutos em programação, pois uma solução pode ser codificada de inúmeros modos — as soluções apresentadas aqui têm um caráter didático, e nem sempre traduzem o modo mais eficiente ou elegante de resolver o problema. O leitor é estimulado a conceber soluções alternativas e compará-las com as que lhe são fornecidas. A maioria dos exercícios teóricos, que não envolvem código, é de solução imediata, requerendo ape- nas uma leitura atenta do texto. Para esses, as respostas são apresentadas apenas em alguns casos, principalmente quando se trata de representação de valores ou quando há necessidade de cálculo (para determinação de endereços, por exemplo), mas na maioria das vezes elas são deixadas a cargo do leitor. 1

Upload: vuongcong

Post on 15-Dec-2018

237 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

Elementos de programação em CRespostas dos exercícios selecionados

Francisco A. C. Pinheiro, Elementos de programação em C, Bookman Editora,Porto Alegre, 2012.

Visite o sítio da editora para obter mais informações sobre o livro:www.bookman.com.br

As respostas de todos os exercícios cuja solução envolve codificação são apresentadas aqui. Algumasvezes o código é comentado para ressaltar pontos importantes da solução adotada ou esclarecer o seufuncionamento. É importante salientar que não existem gabaritos absolutos em programação, pois umasolução pode ser codificada de inúmeros modos — as soluções apresentadas aqui têm um caráter didático,e nem sempre traduzem o modo mais eficiente ou elegante de resolver o problema. O leitor é estimuladoa conceber soluções alternativas e compará-las com as que lhe são fornecidas.

A maioria dos exercícios teóricos, que não envolvem código, é de solução imediata, requerendo ape-nas uma leitura atenta do texto. Para esses, as respostas são apresentadas apenas em alguns casos,principalmente quando se trata de representação de valores ou quando há necessidade de cálculo (paradeterminação de endereços, por exemplo), mas na maioria das vezes elas são deixadas a cargo do leitor.

1

Page 2: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

2 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

Capítulo 2

2.4. a) gcc -c prgA.c prgB.c. Pré-processa, compila e monta os códigos-fontes prgA.c e prgB.c,gerando os códigos-objetos prgA.o e prgB.o.

b) gcc -c prgA.c prgB.o. Pré-processa, compila e monta o código-fonte prgA.c, gerando ocódigo-objeto prgA.o. Como a diretiva -c apenas gera códigos-objetos (a partir de códigos-fontes ou assembler) nada é feito com o código-objeto prgB.o.

c) gcc prgA.c -o prg -std=c99. Pré-processa, compila, monta e liga o código-fonte prgA.c,gerando o código executável prg. A compilação utiliza o padrão ISO/IEC 9899:1999.

d) gcc -E prgA.c -o prgA.s. Pré-processa o código-fonte prgA.c, armazenando a saída no ar-quivo prgA.s.

e) gcc -E prgA.c prgB.c. Pré-processa o código-fonte prgA.c, gerando a saída no monitor devídeo e, logo após, pré-processa o código-fonte prgB.c, gerando a saída no monitor de vídeo.

f) gcc -S prgA.c -o prg. Pré-processa e compila o código-fonte prgA.c, armazenando o códigoassembler no arquivo prg.

g) gcc -S prgA.c prgB.c. Pré-processa e compila o código-fonte prgA.c, gerando o arquivoassembler prgA.s e, logo após, pré-processa e compila o código-fonte prgB.c, gerando o arquivoassembler prgB.s

h) gcc prgA.c prgB.o prgC.c -o prg -lm. Pré-processa, compila e monta os códigos-fontesprgA.c e prgC.c. Os códigos-objetos gerados para os arquivos prgA.c e prgC.c são ligados aocódigo-objeto prgB.o e aos códigos-objetos da biblioteca libm.a (apenas as funções que foramreferidas no programa). O código executável é armazenado no arquivo prg.

2.6. Cinco variáveis. As variáveis a, b e c são do tipo int e as variáveis v e s são do tipo double.2.8. a) 5 3 8.500000

b) a = 5, b = 3, c = 8.500000c) dobro de 5 = 10d) 8.500000 / 5 = 1.700000

2.10. O programa realiza 4 leituras, modificando o valor das variáveis a, s e t. O valor de a é modificadoduas vezes: na primeira e na última leitura.2.12. #include <stdio.h>

int main(void) {int a, b;printf (" Digite primeiro num: ");scanf ("%d", &a);printf (" Digite segundo num: ");scanf ("%d", &b);printf ("%d x %d = %d\n", a, b, (a * b));return 0;

}

2.13. #include <stdio.h>int main(void) {

double x, y;printf (" Digite lado a: ");scanf ("%lf", &x);printf (" Digite lado b: ");scanf ("%lf", &y);printf (" perimetro = %f\n", (2 * x) + (2 * y));printf ("area = %f\n", x * y);return 0;

}

2.15. No programa ao lado, o valor de π é arma-zenado na variável pi como 3, 1416.

#include <stdio.h>int main(void) {

double r, pi = 3.1416;printf (" Digite o raio: ");scanf ("%lf", &r);printf ("area = %f\n", pi * r * r);printf (" perimetro = %f\n", 2 * pi * r);return 0;

}

Page 3: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

3

2.16. O programa define 3 funções: imp_media (linhas 3-5), imp_maior (linhas 6-13) e main (linhas14-22). A função main chama 4 funções: a função printf é chamada nas linhas 15 e 17, a função scanf échamada nas linhas 16 e 18, a função imp_media é chamada na linha 19 e a função imp_maior é chamadana linha 20. Se o usuário digitar 5, 6 e 3, 4, o programa produzirá a seguinte saída5:

Digite o primeiro numero: 5.6Digite o segundo numero: 3.4media: 4.5000005.600000 maior que 3.400000

2.17. Duas, as variáveis a e b, declaradas na linha 2: são globais porque estão declaradas fora de qualquerfunção.

2.18. O arquivo prg01.c ao lado declara as fun-ções imp_media e imp_maior com o qualificadorextern para permitir que a referência a elas sejaverificada pelo compilador. As variáveis a e b sãodeclaradas globais para que possam ser acessadasa partir das demais unidades, onde devem ser de-claradas com o qualificador extern.

prg01.c

#include <stdio.h>double a, b;extern void imp_media(void);extern void imp_maior(void);int main(void) {

printf (" Digite o primeiro numero: ");scanf ("%lf", &a);printf (" Digite o segundo numero: ");scanf ("%lf", &b);imp_media ();imp_maior ();return 0;

}

prg01a.c

#include <stdio.h>extern double a, b;void imp_media(void) {

printf ("media: %f\n", (a + b) / 2);}

prg01b.c

#include <stdio.h>extern double a, b;void imp_maior(void) {

if (a > b) {printf ("%f maior que %f\n", a, b);

} else {printf ("%f maior ou igual a %f\n",

b, a);}

}

2.19. Os arquivos prg01a.c e prg01b.c ficam divididos em arquivos de implementação (.c) e arquivos-cabeçalhos (.h), conforme mostrado a seguir:

prg01a.h

void imp_media(void);

prg01a.c

#include <stdio.h>#include "prg01a.h"#include "prg01.h"void imp_media(void) {

printf ("media: %f\n", (a + b) / 2);}

prg01b.h

extern void imp_maior(void);

prg01b.c

#include <stdio.h>#include "prg01b.h"#include "prg01.h"void imp_maior(void) {

if (a > b) {printf ("%f maior que %f\n", a, b);

} else {printf ("%f maior ou igual a %f\n",

b, a);}

}

5A digitação e impressão dos valores reais com o ponto decimal, no lugar da vírgula decimal, depende do ambiente deexecução: em alguns ambientes será necessário digitar a vírgula, em outros, o ponto.

Page 4: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

4 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

O arquivo prg01a.c importa seu próprio cabeçalho, além do cabeçalho prg01.h que contém a de-claração das variáveis globais a e b que ele utiliza. O mesmo acontece com o arquivo prg01b.c. Já oarquivo prg01.c mostrado a seguir, declara em seu cabeçalho as variáveis globais a e b que são utilizadasnas demais unidades de compilação. Ele importa seu próprio cabeçalho, além dos cabeçalhos prg01a.he prg01b.h que contêm as declarações das funções a que ele se refere. Esta solução ilustra as seguintespráticas:

(1) O uso de um arquivo-cabeçalho contendo as declarações das variáveis globais que deve ser impor-tado por toda unidade de compilação cujo código faça referência a elas.

(2) A prática do arquivo implementação (que contém o código das variáveis e funções) importar o seupróprio arquivo-cabeçalho. Essa prática facilita a manutenção da consistência entre o que está definidona implementação e o que está declarado no cabeçalho.

prg01.h

extern double a, b;

prg01.c

#include <stdio.h>#include "prg01.h"#include "prg01a.h"#include "prg01b.h"double a, b;int main(void) {

printf (" Digite o primeiro numero: ");scanf ("%lf", &a);printf (" Digite o segundo numero: ");scanf ("%lf", &b);imp_media ();imp_maior ();return 0;

}

2.20. No código a seguir as variáveis lA e lB e as funções imp_area e imp_perimetro estão declaradaslogo no início, antes de serem referidas no restante do código. Assim, as declarações duplicadas, com oqualificador extern, são desnecessárias.

#include <stdio.h>double lA, lB;void imp_area(void) {

printf ("area = %f\n", lA * lB);}void imp_perimetro(void) {

printf (" perimetro = %f\n",2 * lA + 2 * lB);

}

int main(void) {printf (" tamanho do lado A: ");scanf ("%lf", &lA);printf (" tamanho do lado B: ");scanf ("%lf", &lB);imp_area ();imp_perimetro ();return 0;

}

2.21. Os arquivos a seguir são as versões das duas unidades, com cabeçalho.

prg02a.h

extern double lA, lB;

prg02a.c

#include <stdio.h>#include "prg02a.h"#include "prg02b.h"double lA, lB;int main(void) {

printf (" tamanho do lado A: ");scanf ("%lf", &lA);printf (" tamanho do lado B: ");scanf ("%lf", &lB);imp_area ();imp_perimetro ();return 0;

}

prg02b.h

extern void imp_area(void);extern void imp_perimetro(void);

prg02b.c

#include <stdio.h>#include "prg02b.h"#include "prg02a.h"void imp_area(void) {

printf ("area = %f\n", lA * lB);}void imp_perimetro(void) {

printf (" perimetro = %f\n",2 * lA + 2 * lB);

}

Page 5: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

5

Capítulo 3

3.3. Não. O padrão apenas especifica que os caracteres do conjunto básico de caracteres do ambiente deexecução devem poder ser armazenados em uma variável do tipo char.3.4. Não. O tipo char é implementado de modo idêntico ao tipo signed char ou unsigned char.3.5. Não, nesse caso o tipo char é não sinalizado. Se o valor do menor código é não negativo, todos osdemais também serão não negativos.3.6. A faixa de representação para o tipo signed char é [−(26 − 1), 26 − 1] = [−63, 63] e para o tipounsigned char é [0, 27 − 1] = [0, 127].3.8. Caracteres multibytes são caracteres representados por combinações de um ou mais caracteres doconjunto básico de caracteres, isto é, armazenados em um ou mais bytes. Caracteres estendidos sãocaracteres armazenados em uma quantidade fixa de bytes, suficiente para armazenar o código de todos oscaracteres do alfabeto considerado.3.10. Sim. o tipo long int será sempre maior ou igual a int e este será sempre maior ou igual a shortint.3.14. O trecho de código do exemplo possui três tipos. As variáveis vel e acel são do mesmo tipo,distinto dos demais. As variáveis pes, fam e grp são do mesmo tipo, distinto dos demais.3.17. Não. Com alinhamento igual a 3 bytes, se a alocação do primeiro inicia no byte de ordem 6, a dopróximo terá que iniciar no byte de ordem 9.3.20. Com CHAR_BIT e o alinhamento iguais a 7 bits e 12 bits de tamanho, existem 2 bits de preenchimento:

a) 10 010011001011 = 210 + 27 + 26 + 23 + 21 + 20 = 1.227b) 11 001011100000 = 29 + 27 + 26 + 25 = 736c) 10 100100001110 = 211 + 28 + 23 + 22 + 21 = 2.318

3.21. Com CHAR_BIT e o alinhamento iguais a 7 bits e 11 bits de tamanho, existem 3 bits de preenchimento:a) 1 001 0011011011 = −(1100100101) = −(29 + 28 + 25 + 22 + 20) = −805b) 0 101 1011100000 = 29 + 27 + 26 + 25 = 736c) 0 010 1100011110 = 29 + 28 + 24 + 23 + 22 + 21 = 798

3.23. O espaço de uma união é igual ao espaço necessário para armazenar seu maior componente,mas tanto esse componente quanto a própria união podem ter bits de preenchimento em função de seusalinhamentos. Como o tamanho de um tipo não inclui os bits de preenchimento, a resposta é sim.3.24. Como o tipo destino é não sinalizado, a conversão se dá por meio da redução do valor originalmódulo 28 = 256. O resultado é igual a 219 = 731− 256− 256.

Capítulo 4

4.2. Apenas os literais em (a), (b), (c) e (f) são válidos. O literal em (d) possui o dígito g, que não é umdígito hexadecimal; o literal em (e) não contém dígitos e o literal em (g) emprega erradamente o caracterex, que deve se parte do prefixo 0x.4.3. São octais os literais em (b) e (d); decimais os literais em (a) e (e); e os demais são hexadecimais.4.5. Literal Tipo Literal Tipo

064L long int 315ul unsigned long int0x6a2u unsigned int 3000000000 long long int0466600LL long long int 0x9233l long int421ULL unsigned long long int

4.6. Apenas os literais em (a), (b), (e), (f), (h), (j) e (k) são válidos. Em (c) expoente não é inteiro; em(d) o sufixo L está mal empregado: um literal do tipo float não pode ser long; em (g) falta o indicadorde expoente p ou P que todo literal real hexadecimal deve ter; em (i) o indicador p está mal empregado:só pode ser usado com literais reais hexadecimais (iniciados com 0x ou 0X); e em (l) o dígito A só podeser usado em literais reais hexadecimais (iniciados com 0x ou 0X).4.7. a) 0.05 = 0, 05 b) 3. = 3, 0

c) 0x23p1 = 2× 161 + 1× 160 × 21 = 70, 0 d) 4e-2 = 4× 10−2 = 0, 04e) 0xabp4 = 10× 161 + 11× 160× 24 = 2.736, 0 f) -3.75e+2 = −3, 75× 102 = −375, 0g) 0x1.bP-2 = 1× 160 + 11× 16−1 × 2−2 = 0, 421875

4.8. a) 76.8, double b) 30.f, float c) 0x12p1f, float d) 76.5e-6L, long double e) 0x4P-2, double

Page 6: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

6 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

4.9. Os literais nos itens (a), (f), (g) e (k) são literais caracteres válidos, representando caracteres dopadrão ASCII. O literal L’y’ representa o caractere ‘y’, interpretado como um valor do tipo wchar_t. Osliterais em (d), (e), (j) e (o) são válidos, mas não representam caracteres do padrão ASCII — seus códigosestão fora da faixa [0, 127].

O literais em (b) e (p) representam caracteres multibytes e o literal em (c) é uma cadeia de caracteres.Já os literais em (l), (m) e (n) são inválidos: a letra l não pode ser usada como prefixo nem, tampouco,a letra L pode ser usada como sufixo. Também são inválidos os literais em (h), (i) e (q): quando usadoscomo literais caracteres, os códigos Unicode iniciados com u devem ter exatamente 4 dígitos e não podemser menores que \u00A0 (exceto os código \u0024, \u0040 e \u0060), e os iniciados com U devem terexatamente 8 dígitos.4.10. Os literais ’\115’, ’M’ e ’\x4D’ representam o caractere ‘M’, código decimal igual a 77. Os literais’\x13’ e ’\023’ representam o caractere de controle cujo código decimal igual a 19. Os literais ’\150’,’\x68’ e ’h’ representam o caractere ‘h’, código decimal 104.4.11. As cadeias (a), (b), (c) e (d) são válidas, contendo apenas caracteres do padrão ASCII. A cadeiaem (d) contém três caracteres: \06, 8 e 9; o caractere \06 embora válido, não tem representação gráfica.Na cadeia em (f) o caractere \x128 não é um caractere ASCII, assim como não são os caracteres ‘ç’ e ‘ã’na cadeia (h).

As cadeias em (e), (g) (i) e (j) contêm caracteres inválidos: \8 não é um código octal válido, \xg nãoé um código hexadecimal válido, \u12 deveria ter 4 dígitos e \u0001 está fora da faixa aceitável paraliterais caracteres Unicode (é menor do que \u00A0).4.13. (a) Um vetor de 6 inteiros iniciado com os valores 0, 12, 4, −2, 12 e 3. (b) o valor inteiro 298. (c)o caractere ‘b’. (d) a união do tipo union exem com o primeiro componente iniciado com o valor 12, 43.4.15. a) a = 3, b = 4, c = 5, d = 10, e = 0 e f = 1

b) pri = 0, seg = 1, ter = 2 e qua = 3c) rei = 0, rainha = 1, bispo = -1, cavalo =0 e torre = 1d) x = 1, y = 1 e z = 1

4.16. Apenas o item (c) declara uma enumeração válida. Em (a) o valor 2e20 não é uma constante dotipo int (extrapola o limite para o tipo int); em (b) o valor 2.3 não é do tipo int; em (d) a constantex já está declarada no mesmo escopo da enumeração; em (e) o valor x não é uma expressão constante; eem (f) a enumeração está vazia.4.18. Não, se elas estiverem no mesmo escopo. Se estiverem em escopos distintos devem ter a mesmaetiqueta para serem compatíveis.

Capítulo 5

5.1. É uma localização específica da memória, possuindo um nome e um valor associado. Algumasvariáveis podem ser não nomeadas, referidas através de índices.5.2. Uma variável é uma localização da memória e um identificador é uma expressão usada designarelementos do programa, que podem ser variáveis, funções ou outros elementos do código, como rótulose etiquetas. Pode-se dizer que as variáveis são identificadores usados para designar uma localizaçãoespecífica da memória.5.3. Os identificadores em (a), (c), (d), (e), (g), (h), (j) e (m) são válidos. Os demais são inválidos:os identificadores em (f) e (l) iniciam com um dígito, os identificadores em (b) e (i) contêm caracteresinválidos e o identificador em (k) é uma palavra-chave.5.5. O escopo de bloco vai do ponto da declaração até o fim do bloco no qual a variável é declarada (se avariável for paramétrica, seu escopo vai até o fim do bloco que delimita a função). O escopo de protótipode função vai do ponto da declaração até o fim da declaração à qual a variável pertence.5.6. Apenas os itens (c), (f) e (i) são inválidos, pelas seguintes razões:

c) long const float x, y; O tipo long float não existef) float extern valor, dif = 3.4f; Variáveis extern não podem ser iniciadasi) extern static signed short int w; Múltiplas classes de armazenamento

O item (g) é válido, mas nele ocorre uma conversão restritiva.5.8. A variável qtd é global e seu escopo vai da linha 7, onde é declarada, até o fim da unidade decompilação, na linha 16. Como é global de programa, ela poderia ser referida em outras (possíveis)

Page 7: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

7

unidades de compilação, mas deveria ser declarada nessas outras unidades. As demais variáveis sãolocais. O escopo da variável x vai do ponto de declaração na linha 2 até o fim do bloco que delimita ocorpo da função dobro. O escopo da variável dois vai da linha 3 à linha 5, e o escopo de num vai da linha10 à linha 16.5.9. As variáveis cujos identificadores possuem ligação externa denotam um mesmo espaço de memória.São declaradas com a classe de armazenamento extern, ou sem classe de armazenamento, se forem globais.5.10. São declaradas 6 variáveis. As variáveis a declaradas na linha 1 das unidades A e B referem-se aum mesmo espaço de memória, (seus identificadores) possuem ligação externa. As variáveis b declaradasna linha 5 da unidade A e na linha 6 da unidade C também referem-se a um mesmo espaço de memória,possuem ligação externa. Já as variáveis a declaradas na unidade C referem-se a um único objeto epossuem ligação interna (apesar da segunda declaração ser feita com a classe extern). As outras trêsvariáveis denotam espaços próprios da memória e possuem ligação local.5.11. As variáveis com alocação estática são alocadas e iniciadas uma única vez no início do programa,permanecendo alocadas até o fim do programa.5.12. As variáveis r e t possuem alocação automática; as demais, alocação estática.5.16. As declarações das variáveis y, r, s e t são definições. As declarações da variável x são defini-ções provisórias. A declaração da variável v na linha 3 também é uma definição provisória. As demaisdeclarações são apenas declarações.5.17. Classe static: a e i. Classe extern: b, j e c. Classe auto: e, g e h. Classe register: d e f.5.20.

Escopo Ligação Alocação Armz Escopo Ligação Alocação Armza arquivo interna estático static b arquivo externa estático externc arquivo externa estático extern d bloco local automático registere bloco local automático auto f bloco local automático registerg bloco local automático auto h bloco local automático autoi bloco local estático static j bloco externa estático extern

Capítulo 6

6.3. As expressões em (a) e (b) estão na forma infixada, em (c) e (f), na forma prefixada, e em (d) e (e)na forma pós-fixada. Cada expressão é analisada independentemente das demais, pois em uma mesmalinguagem um operador não deve ser ao mesmo tempo prefixado e pós-fixado, por exemplo.

6.4. O programa produz a saída ao lado. Naprimeira impressão as variáveis d e e são indefini-das, pois não foram iniciadas nem houve atribuiçãode valores. Portanto, seus valores podem variar acada execução.

66 -66 13 13026469 -107848093666 -66 13 26 -132

6.5. O programa produz a seguinte saída: -271 3 -30 -2A expressão de atribuição da variável b equivale a (13 * 4) % 7, que resulta em 3. Assim, as ope-rações para atribuir o valor de c equivalem a (-271 / 3) / 3, que resulta em −30. Já a variável drecebe o resultado de (-269 % -30) % 3 que é igual a −2 (o resultado de -269 % -30 é igual −29,pois −269 = ((-269 / -30) * -30) + (-29); e o resultado de -29 % 3 é igual a −2, pois −29 =((-29 / 3) * 3) + (-2)).6.6. A primeira impressão imprime o conteúdo das variáveis b e c como valores do tipo char e a segundacomo valores do tipo int. O programa produz a seguinte saída:

142 * N142 42 78

A variável b é iniciada com 42, que é o código ASCII do caractere ‘*’ e a variável a recebe o valor 148,que é o resultado da expressão 2 * ’c’ + b - ’b’, pois o código do caractere ‘c’ é 99 e o do caractere‘b’ é 98. Já o resultado da expressão ’a’ * (’p’ - ’b’) é equivalente a 97× (112− 98), pois o códigodo caractere ‘a’ é 97, o do caractere ‘p’ é 112 e o do caractere ‘b’ é 98. O valor resultante, 1.358, não

Page 8: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

8 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

pode ser atribuído ao tipo char (considerando que ele é implementado como um inteiro sinalizado de 8bits): a conversão implica na redução módulo 28 = 256 até a obtenção de um valor na faixa [−128, 127];o valor 78 é então atribuído a c.6.7. Apenas as quatro primeiras impressões são bem definidas. Na atribuição do valor de k, tanto ovalor da própria variável k quanto o valor da variável j são indefinidos, pois a ordem de avaliação não édeterminada (o termo j pode ser avaliado antes de ++j, por exemplo). A seguinte saída pode ser produzida(considerando que a ordem de avaliação é da esquerda para a direita), entretanto os dois últimos valoresda segunda linha podem variar:

201 201 100 99200 100 403

6.8. a) 1, 0 b) −0, 0 c) 0, 0d) inf e) −inf f) −nan

6.9. Seguindo a definição para a soma e multiplicação de números complexos, o programa imprime-2.000000 -1.000000i e, em outra linha, 22.000000 34.000000i.6.10. O programa imprime 1 1 0 0 porque as expressões atribuídas às variáveis a e b são verdadeiras, eas atribuídas a c e d são falsas.6.12. O programa imprime 84 13 4 93 89 -85. O conteúdo das variáveis é dado pela seguinte tabela(considerando um ambiente em que o tamanho de short int é 16 e o de int é 32 bits):Expr Valor binário Valor dec.a = 84 0000000001010100 84b = 13 0000000000001101 13c = a & b 00...0000000000000100 4d = a | b 00...0000000001011101 93e = a ^ b 00...0000000001011001 89f = ~a 11...1111111110101011 −85

Os pontos na notação X...X indicam a repetição do valor X até completar os bits do tipo a que o valor serefere.6.13. O programa imprime 302 1208 604 -1. A expressão a « 3 » 2 equivale a a « 1, já que é equi-valente a (a « 3) » 2. O conteúdo das variáveis após cada atribuição é fornecido pela seguintes tabela:Expr Valor binário Valor dec.a = 302 00...000000000100101110 302b = a << 2 00...000000010010111000 1.208c = a << 3 >> 2 00...000000001001011100 604-2 11...111111111111111110 −2d = -2 >> 2 1111...1111111111111111 −1

6.14. O programa imprime 57 44. Na primeira atribuição, como i é menor que trinta, a expressão 2 *–i é executada, fazendo com que 38 seja armazenado em j e i fique com o valor 19. Logo, a primeiraimpressão imprime o valor 57. Na segunda atribuição, como i é maior do que 5, a expressão i > 15 ?44 : 17 é executada, e como i também é maior do que 15, o valor 44 é produzido e armazenado em j.6.15. Apenas a primeira atribuição é bem formada. Na segunda, o valor de i é indefinido, pois nãose pode garantir a ordem de sequenciamento entre as duas atribuições envolvendo i. Assumindo que aavaliação ocorre estritamente da esquerda para a direita o programa imprime a seguinte saída: 76 17.Na primeira atribuição, a expressão (i /= 2) equivale a i = i / 2, que atribui o valor 2 a i e resulta novalor 2. Desse modo, a atribuição de j é equivalente a j = j * (2 + 2), que é igual a 16. Na segundaatribuição, a expressão (i += ++j) equivale a i = i + ++j, que incrementa o valor de j, atribui o valor19 a i e resulta no valor 19. A atribuição externa de i equivale a i = i + (3 * 19), que resulta no valor76.6.16. O programa imprime 12 3 -3 14. A expressões do operador vírgula são executadas em sequên-cia. Em um ambiente em que o tipo int e short int são implementados com 4 e 2 bytes, a expressãosizeof(short int) resulta no valor 2 e a expressão sizeof 2 * 3 equivale a 4 * 3, pois o literal 2 é dotipo int. A última expressão, cujo valor é 12 + 2, pois o valor de a terá sido atualizado nesse ponto, éatribuída a d.

Page 9: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

9

6.17. a) Valor de retorno, tipo double. Argumentos, tipo time_t.b) Valor de retorno, tipo float. Argumento, tipo float.c) Valor de retorno, tipo long double. Primeiro argumento, tipo long double. Segundoargumento, tipo int.d) Valor de retorno, tipo double. Primeiro argumento, tipo double. Segundo argumento, tipolong double.

6.18. A variável a é do tipo int; b, do tipo char; c, do tipo sc_t; d e e são do tipo struct reg; f e g,do tipo str_t1; e h e i são do tipo str_t2.O tipo sc_t é sinônimo de signed char. Logo, a variável b e c são (de tipos) compatíveis, se char forimplementado como signed char. O tipo str_t2 é sinônimo de str_t1, que é sinônimo de struct reg.Logo, as variáveis d, e, f, g, h e i são (de tipos) compatíveis entre si.6.19. a) (2 + ((((++soma) / aux) / val) % 2)) - (-3)

b) (2 > (3 + 5)) && ((12 - 23) <= 17)c) (2 > 3) ? 6 : aux + (sin(14) / 2.0)d) (3 - ((--aux) / fmin(num,14)))== (2.0 * 23.0)

6.21. a) double b) float c) int d) inte) int f) long int g) long long int h) double

Capítulo 7

7.2. Se o número lido fora) Igual a 5, apenas a mensagem “fim” é impressa. Como 5 não é menor que 5 todos os

comandos da cláusula-então são desconsiderados e o controle passa para o primeirocomando após o if: o comando de impressão da linha 10.

b) Igual a 2, são impressos os números 2 e 3 e o literal “fim”. O controle passa para oprimeiro comando da cláusula-então, na linha 6, prosseguindo a partir deste pontoe imprimindo em sequência os valores 2 e 3. Após a execução dos comandos dacláusula-então o comando if termina normalmente e o controle passa para o comandode impressão da linha 10, onde a mensagem “fim” é impressa.

7.3. Se o número lido fora) Igual a 3, são impressos os números 3 e 4 e o literal “fim”, um em cada linha. Após o

teste da condição, na linha 5, o controle passa para o primeiro comando da cláusula-então, na linha 6, e a execução prossegue a partir desse ponto. O número 3 éimpresso pelo comando da linha 6, o conteúdo de a é incrementado de uma unidadepelo comando da linha 7, e o número 4 é impresso pelo comando da linha 8. Aofim da cláusula-então o comando if termina normalmente e o controle passa para ocomando da linha 13.

b) Igual a 5, são impressos o número 8 e o literal “fim”, um em cada linha. Como nestecaso a condição é falsa, o controle passa para o primeiro comando da cláusula-senão,na linha 10, prosseguindo a partir desse ponto com a adição de 3 ao conteúdo dea e a consequente impressão do número 8 pelo comando da linha 11. Ao términoda cláusula-senão o comando if termina normalmente e o controle passa para ocomando da linha 13.

7.4. No programa ao lado, o comando if é usadopara atribuir o valor 15 à variável a, caso o nú-mero lido seja menor ou igual a 230. Desse modo,o único comando de impressão imprimirá o valorcorreto de acordo com a especificação do problema.

#include <stdio.h>int main(void) {

int a;scanf ("%d", &a);if (a <= 230) {

a = 15;}printf ("%d\n", a);return 0;

}

Page 10: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

10 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

7.5. No programa ao lado, a constante PI, para ocálculo da área do círculo, é declarada com apenas6 casas decimais. O correto seria calculá-la atra-vés da função arco tangente ou usar uma macropredefinida (não padronizada, mas disponível se acompilação usar outros padrões que não o ISO/IEC9899:1990), como discutido na Seção 16.6.12.

#include <stdio.h>int main(void) {

int l;const double PI = 3.141593;printf (" digite um numero: ");scanf ("%d", &l);if (l > 0) {

printf ("area quadrado = %d\n", l * l);} else {

if (l < 0) {printf ("area circulo = %f\n",

PI * l * l);}

}return 0;

}

7.6. A condição de existência de um triângulo étestada no if após as três leituras (ele existirá se asoma de quaisquer dois lados for maior que o ter-ceiro — a condição do if testa a negação dessaafirmação para determinar a não existência). Casoo triângulo exista, os lados são comparados doisa dois, com o incremento da variável qtd paracada dois lados iguais. A variável qtd armazenaa quantidade de comparações iguais e é usada nocomando switch para imprimir o tipo do triân-gulo.

Este programa não testa se os números lidossão positivos e está formatado com mais de umcomando por linha para economizar espaço.

#include <stdio.h>int main(void) {

int qtd = 0;double a, b, c;printf (" Digite 3 numeros reais: ");scanf ("%lf", &a);scanf ("%lf", &b);scanf ("%lf", &c);if (((a + b) <= c) || ((a + c) <= b)

|| ((b + c) <= a)) {printf ("nao forma triangulo\n");

} else {if (a == b) { qtd ++; }if (a == c) { qtd ++; }if (b == c) { qtd ++; }printf (" triangulo ");switch (qtd) {case 3:

printf (" equilatero\n"); break;case 1:

printf (" isoceles\n"); break;default:

printf (" escaleno\n"); break;}

}return 0;

}

7.7. Três pontos (x1, y1), (x2, y2) e (x3, y3) são colineares se∣∣∣∣∣∣x1 y1 1x2 y2 1x3 y3 1

∣∣∣∣∣∣ = 0.

No programa a seguir, a variável det recebe o valor do determinante e a mensagem apropriada éimpressa testando se esse valor é igual a 0. Em virtude da imprecisão dos valores reais, o teste daigualdade é feito comparando-se o resultado com um valor muito pequeno (a função fabs retorna o valorabsoluto do seu argumento).

#include <stdio.h>#include <math.h>#include <float.h>int main(void) {

double x1, y1, x2 , y2 , x3 , y3;double det;scanf ("%lf", &x1);scanf ("%lf", &y1);scanf ("%lf", &x2);scanf ("%lf", &y2);scanf ("%lf", &x3);

scanf ("%lf", &y3);det = (x1 * y2 + x2 * y3 + x3 * y1) -

(x2 * y1 + x3 * y2 + x1 * y3);if (fabs(det) <= fabs(det * DBL_EPSILON )){

printf (" pontos colineares\n");} else {

printf (" pontos nao colineares\n");}return 0;

}

A especificação do problema é omissa quanto ao valores que podem ser digitados. O programa assumeque o tipo double é apropriado, mas, para problemas reais, decisões desse tipo devem ser baseadas na

Page 11: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

11

especificação.7.8. #include <stdio.h>

int main(void) {int a, b;printf (" Digite dois numeros: ");scanf ("%d", &a);scanf ("%d", &b);if (a >= b) {

printf ("ordem lida: %d %d", a, b);} else {

printf ("ordem inversa: %d %d", b, a);}return 0;

}

7.9. #include <stdio.h>int main(void) {

int a, b, c;printf (" Digite tres numeros: ");scanf ("%d", &a);scanf ("%d", &b);scanf ("%d", &c);if ((a <= b) && (b <= c)) {

printf ("ordem crescente\n");

} else {if ((a >= b) && (b >= c)) {

printf ("ordem decrescente\n");} else {

printf ("fora de ordem\n");}

}return 0;

}

7.11. A seguinte tabela mostra para cada comando if os comandos existentes em suas respectivascláusulas:

Comando if Cláusula-então Cláusula-senão

linha 7 linha 8 (printf) linha 15 (if)linha 9 (if)

linha 9 linha 10 (=) linha 13 (printf)linha 11 (printf)

linha 15 linha 16 (printf) —linha 17 (printf)

Os comandos são identificados pelas linhas em que ocorrem. Por exemplo, a cláusula-então do comandoif da linha 7 possui apenas 2 comandos, o comando de impressão, na linha 8, e um outro comando if,na linha 9. Este último if, por sua vez, possui dois comandos em sua cláusula-então e um único comandoem sua cláusula-senão. O comando if da linha 15 não possui a cláusula-senão.

7.13. A tabela abaixo mostra na coluna resultado os valores impressos em cada uma das situações:

Situação Resultado

(a > b) e (b > c) 4, 5 e 6.(a > b) e (b < c) 6.(a < b) e (b == c) 1, 2 e 6.(a == b) e (b > c) 4, 5 e 6.

Page 12: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

12 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

7.14. #include <stdio.h>int main(void) {

double preco , pagto;printf (" Digite o preco e o pagto: ");scanf ("%lf", &preco);scanf ("%lf", &pagto);if (preco > pagto) {

printf ("falta %f\n", preco - pagto );} else {

if (preco < pagto) {printf ("troco de %f\n", pagto - preco);

} else {printf ("valor correto\n");

}}return 0;

}

7.15. Se o valor lido for igual a 2 o programa imprimirá os números 2, 6 e 4, nesta ordem. Quando ovalor de a é igual a 2, a expressão do switch externo é igual a 6. O controle será desviado para o primeirocomando de impressão, que imprime o valor 2. Em seguida, o valor 6 é atribuído à variável a e a execuçãoprossegue com o segundo comando de impressão, imprimindo o valor 6. A expressão do switch interno éigual a 3 e nenhuma de suas cláusulas é acionada; o controle passa para o segundo comando de atribuição(a = a - 2) e o valor de a é modificado para 4, que é o valor impresso pelo último comando de impressão.

Os demais itens desta questão são respondidos de modo semelhante.7.16. Todos os itens contêm erros. No item (a) a condição do if será sempre verdadeira. No item (b) acondição do if externo será sempre falsa. No item (c) a mensagem “faixa tres” nunca será impressa. Oswitch do item (4) contém duas cláusulas com rótulos iguais.7.17. Dois programas são funcionalmente equivalentes se produzem o mesmo resultado para todas assituações idênticas. Comparando (a) com (b). O trecho de programa do item (a) imprime o conteúdo dea apenas se a for maior que b e menor que c. Nos demais casos, o conteúdo de b é impresso. O trecho deprograma do item (b) não imprime nada se a for menor que b. Portanto, não é funcionalmente equivalenteao trecho (a).

As demais comparações devem ser feitas usando-se um raciocínio semelhante.7.19. Em uma P.A. a diferença entre quaisquer dois números adjacentes é igual à razão. Desse modo,basta comparar a razão entre o primeiro e o segundo números com a razão entre o segundo e o terceironúmeros. A solução é mostrada lado a lado com a solução do próximo exercício.7.20. O código à esquerda é a solução sem switch enquanto que o código à direita é a solução sem if:

#include <stdio.h>int main(void) {

int a, b, c;scanf ("%d", &a);scanf ("%d", &b);scanf ("%d", &c);if ((b - a) == (c - b)) {

printf (" formam pa\n");} else {

printf ("nao formam pa\n");}return 0;

}

#include <stdio.h>int main(void) {

int a, b, c;scanf ("%d", &a);scanf ("%d", &b);scanf ("%d", &c);switch ((b - a) - (c - b)) {

case 0: printf (" formam pa\n");break;

default: printf ("nao formam pa\n");break;

}return 0;

}

Page 13: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

13

Capítulo 8

8.1. O programa ao lado usa no mínimo dois es-paços para imprimir o numerador e o denomina-dor (diretiva %2d). Para um melhor efeito visual, aquantidade de traços separando um do outro deve-ria ser calculada em função dos valores impressos.

#include <stdio.h>int main(void) {

int n;do {

scanf ("%d", &n);} while (n <= 0);printf ("%2d\n--\n%2d\n", n, n + 1);return 0;

}

8.2. Cada termo da sequência tem o numeradorigual a duas vezes o numerador do termo anteriormais um, e o denominador igual ao próprio nume-rador mais um. A atribuição de num no corpo dofor atualiza o numerador em função do seu valoranterior.

#include <stdio.h>int main(void) {

int n, num = 0;do {

scanf ("%d", &n);} while (n <= 0);for (int i = 0; i < n; i++) {

num = 2 * num + 1;printf ("%d/%d ", num , (num + 1));

}return 0;

}

8.3. O programa ao lado considera a sequência ori-ginal, 2/1, 1/1, 2/2, 1/2, ..., como duas sequênciasintercaladas: a primeira consistindo dos termos deordem ímpar, 2/1, 2/2, 2/3, ..., e a segunda consis-tindo dos termos de ordem par, 1/1, 1/2, 1/3, ...

Nessa solução são usadas três variáveis: di ar-mazena os denominadores da primeira sequência,dp armazena os denominadores da segunda sequên-cia e n controla a ordem dos termos.

#include <stdio.h>int main(void) {

for (int dp = 1, di = 1, n = 1;n <= 1350;n++)

{if ((n % 2) == 0)

printf ("1/%d ", dp++);else

printf ("2/%d ", di++);}return 0;

}

Quando n é ímpar, imprime-se um termo da primeira sequência, e quando n é par, imprime-se umtermo da segunda sequência. O numerador e o denominador de cada termo são impressos separadamente,com a diretiva "1/%d " ou "2/%d ". Os denominadores di e dp são incrementados após a impressão dostermos de suas respectivas seqüências.

8.4. No programa ao lado, quando a soma ultra-passa o valor 215 o comando for é interrompidopor um comando break. O mesmo teste é feitoapós o comando for, dessa vez usando o continuepara reiniciar o processamento com a leitura de umnovo número.

O incremento da variável qtd, indicando que onúmero lido foi aceito e teve sua soma calculada,ocorre apenas se a soma for menor ou igual a 215.

#include <stdio.h>int main(void) {

int num , soma , qtd =0;while (qtd < 6) {

printf (" Digite um numero par > 0: ");scanf ("%d", &num);if ((num % 2) != 0 || (num <= 0))

continue;soma = 0;for (int i = 1; i <= num; i++) {

soma = soma + i;if (soma > 215)

break;}if (soma > 215)

continue;qtd ++;printf ("x = %d, s(x) = %d",

num , soma);}printf ("fim\n");return 0;

}

Page 14: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

14 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

8.5. Os comandos do aninhados apenas garantemque os valores inicial e final serão maiores que zero,e que o final será maior ou igual ao inicial. O co-mando for produz a impressão dos termos.

#include <stdio.h>int main(void) {

int i, f;do {

do {scanf ("%d", &i);

} while (i <= 0);do {

scanf ("%d", &f);} while (f <= 0);

} while (i > f);for (int den = i; den <= f; den ++) {

printf ("1/%d ", (den + 1));}return 0;

}

8.6. O primeiro e segundo termo da sequência deFibonacci são iguais a 1. Cada um dos demaistermos é igual à soma dos dois termos anteriores:tn = tn−1 + tn−2.

No programa ao lado, a variável t armazenao termo a ser impresso, a variável t_ant, o termoanterior e a variável t_ant_ant, o termo anteriordo anterior.

O comando for controla a quantidade de ter-mos impressos. Para os dois primeiros termos (i< 2) a variável t recebe o valor 1; para os demais,seu valor é calculado somando-se os dois termosanteriores.

#include <stdio.h>int main(void) {

int n, t = 0, t_ant = 0, t_ant_ant = 0;do {

scanf ("%d", &n);} while (n <= 0);for (int i = 0; i < n; i++) {

if (i < 2) {t = 1;

} else {t = t_ant + t_ant_ant;

}printf ("%d ", t);t_ant_ant = t_ant;t_ant = t;

}return 0;

}

Após a impressão de cada termo, as variáveis t_ant e t_ant_ant são atualizadas, de modo que napróxima iteração o termo recém impresso seja o anterior e o anterior seja o anterior do anterior. Esteprograma pode ser otimizado iniciando t_ant com o valor 0 e t_ant_ant com o valor 1, de modo que otermo t possa ser sempre calculado com a fórmula do termo geral, eliminando-se o comando if no interiordo for.8.7. A solução deste exercício é muito semelhante à do exercício anterior, basta fazer as seguintes altera-ções: declarar uma variável soma e iniciá-la com o valor 0, substituir a impressão de cada termo por suaadição à soma (substituir printf("%d ", t); por soma = soma + t;) e imprimir o valor de soma apósas iterações do for.

8.8. A combinação(np

)é igual a n!

(n−p!)×p! =

(n×(n−1)×. . .×(n−p+1))/(p×(p−1)×. . .×1).O programa ao lado, após as leituras de n e

p, usa o primeiro for para calcular a expressão(n× (n− 1)× . . .× (n− p+ 1)) e o segundo paracalcular o fatorial de p.

#include <stdio.h>int main(void) {int n, p, np_1 = 1, fatp = 1, n_ult;

do {printf (" Digite \’n\’ e \’p\’: ");do {

scanf ("%d", &n);} while (n <= 0);do {

scanf ("%d", &p);} while (p < 0);

} while (p > n);n_ult = n - p + 1;for (np_1 = 1; n >= n_ult; n--) {

np_1 = np_1 * n;}for (fatp = 1; p > 1; p--) {

fatp = fatp * p;}printf ("%d\n", np_1 / fatp);return 0;

}

Page 15: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

15

8.9. O arranjo simples An,p é igual a n× (n−1)×(n− 2)× · · · × (n− p+ 1). Desse modo, a soluçãodeste exercício é idêntica ao cálculo da primeiraexpressão feito no exercício anterior.

#include <stdio.h>int main(void) {

int n, p, arr = 1, n_ult;do {

printf (" Digite \’n\’ e \’p\’: ");do {

scanf ("%d", &n);} while (n <= 0);do {

scanf ("%d", &p);} while (p < 0);

} while (p > n);n_ult = n - p + 1;for ( ; n >= n_ult; n--) {

arr = arr * n;}printf ("%d\n", arr);return 0;

}

8.10. No programa ao lado, a variável boolianapositivo é usada para controlar se o número lidodeve ser positivo (valor verdadeiro — true) ou ne-gativo (valor falso — false).

O primeiro número é lido na linha 6 e a variá-vel positivo é atualizada apropriadamente com ocomando if das linhas 7-11. Após a leitura doprimeiro número os demais são lidos no interior docomando while (linhas 12-21). A cada número, ocomando if (linhas 13-19) verifica se o seu sinalcorresponde ao esperado. Caso não corresponda,o número é rejeitado (linha 18), permanecendo avariável positivo com o mesmo valor. Caso o nú-mero possua o sinal esperado, a mensagem de acei-tação é impressa (linha 15) e a indicação do sinal éinvertida (linha 16), já que o próximo número deveter o sinal contrário ao atual.

1 #include <stdio.h>2 #include <stdbool.h>3 int main(void) {4 int num;5 bool positivo;6 scanf ("%d", &num);7 if (num > 0) {8 positivo = true;9 } else {

10 positivo = false;11 }12 while (num != 0) {13 if (((num > 0) && positivo) ||14 ((num < 0) && !positivo )) {15 printf (" aceito: %d\n", num);16 positivo = !positivo;17 } else {18 printf (" rejeitado: %d\n", num);19 }20 scanf ("%d", &num);21 }22 return 0;23 }

8.11. Nesta versão o programa é menor porquenão utiliza o comando break e as operações de de-cremento são colocadas na cláusula final dos co-mandos for.

#include <stdio.h>#include <stdbool.h>int main(void) {

int n, b, soma;scanf ("%d", &n);for ( ; n > 0; n--) {

soma = 1; b = 1;printf ("%d", b++);for ( ; b <= n; b++) {

printf (" + %d", b);soma = soma + b;

}printf (" = %d\n", soma);

}return 0;

}

8.12. Por sua definição, uma árvore de asteriscos de tamanho N tem 2×N − 1 asteriscos em sua base.Como a linha superior contém um único asterisco centralizado, ela terá (2 × N − 1) − 1)/2 espaços àesquerda (e a mesma quantidade à direita).

No programa ao lado, a quantidade de espaços à esquerda para a primeira linha é armazenada emesq e a quantidade de asteriscos em cada linha é armazenada em aster, iniciando com 1 para a primeiralinha.

O comando for externo controla a quantidade de linhas da árvore. Para cada linha são impressos osespaços à esquerda, impressão controlada pelo primeiro for interno, e, em seguida, os asteriscos da linha,

Page 16: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

16 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

impressão controlada pelo segundo for interno.

#include <stdio.h>int main(void) {

int n, esq , aster;do {

scanf ("%d", &n);} while (n <= 0);esq = ((2 * n - 1) - 1) / 2;aster = 1;for (int i = 0; i < n; i++) {

for (int j = 0; j < esq; j++) {printf (" ");

}for (int j = 0; j < aster; j++) {

printf ("*");}printf ("\n");esq = esq - 1;aster = aster + 2;

}return 0;

}

Após a impressão de uma linha, a quantidade de espaços à esquerda é diminuída de de 1 e a deasteriscos, aumentada de 2, para a impressão da próxima linha.8.13. O primeiro comando for sempre imprime a primeira linha, o segundo comando for imprime aslinhas intermediárias se n for mair do que 2, e o terceiro imprime a última linha se n for maior do que 1.

#include <stdio.h>int main(void) {

int n;do {

scanf ("%d", &n);} while (n <=0);for (int i = 0; i < n; i++) {

printf ("*");}for (int i = 0; i < n - 2; i++) {

printf ("\n*");for (int j = 0; j < n - 2; j++) {

printf (" ");}printf ("*");

}if (n > 1) {

printf ("\n");for (int i = 0; i < n; i++) {

printf ("*");}

}return 0;

}

8.14. No programa a seguir, o comando for controla a quantidade de números randômicos impressos.

#include <stdio.h>#include <stdlib.h>int main(void) {

int n, lim , r;do {

scanf ("%d", &n);} while (n <= 0);do {

scanf ("%d", &lim);} while ((lim <= 0) || (lim >= 5));

for (int i = 0; i < n; ) {r = (rand() % 8) + 2;if (r > lim) {

printf ("%d\n", r);i++;

}}return 0;

}

A cada iteração um número randômico é obtido com a função rand e reduzido à faixa [2, 9] (a operaçãomod 8 resulta em valores na faixa [0, 7] e a adição de 2 resulta em valores na faixa [2, 9]). A impressão eo incremento da quantidade de números impressos ocorre apenas se o número gerado é maior do que lim.

Esse programa seria mais eficiente se os números randômicos fosse reduzidos diretamente à faixa(lim, 10), evitando a execução do comando if a cada iteração. Ele também produz sempre os mesmovalores, pois a semente utilizada na geração dos números randômicos permanece a mesma a cada execução.A função rand é discutida no Capítulo 16.8.15. Nenhum trecho é equivalente a um outro. A opção (a) compara aux com num, num−2, num−4, . . ., esoma os valores num−1, num−3, num−5, . . . a cada comparação verdadeira. A opção (b) compara aux comnum, num−2, num−4, . . ., e soma os valores comparados a cada comparação verdadeira. A opção (c) efetuauma soma mesmo que num não seja maior que aux e a opção (d) compara aux com num, num−1, num−2, . . .,e soma os valores num− 1, num− 2, num− 3, . . . a cada comparação verdadeira.8.16. Os comandos em (a) e (b) são equivalentes. O comando em (d) pode resultar em infinitas iterações,pois se num for maior que 1 no início do for, será sempre maior que 1, já que seu conteúdo nunca émodificado. Na alternativa (c), num terá seu conteúdo decrementado antes da primeira multiplicação.

Page 17: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

17

Capítulo 9

9.7. Apenas as funções main e funE estão definidas. As declarações das funções funB, funC e funE sãoapenas declarações (não são protótipos). As declarações das funções funA, main, funD, funF e funG sãoprotótipos. A declaração da função funE e o protótipo da função main são induzidos por suas definições.9.8. A função funD tem escopo de bloco, indo do ponto de declaração, na linha 5, até o fim do bloco noqual está declarada, na linha 8. As demais funções têm escopo de arquivo, indo do ponto de declaraçãoaté o fim da unidade de compilação na qual estão declaradas.9.9. Todas as funções têm modo de alocação estático. A seguinte tabela mostra o modo de ligação decada uma:

Função Ligação Função Ligação Função Ligação

funA interna funB externa funC internafunD externa funE externa funF interna

9.10. A função funA tem valor de retorno do tipo double, sem parâmetros. A função funB tem valorde retorno do tipo void, com dois parâmetros: o primeiro do tipo int e o segundo do tipo short int,qualificado como const. A função funC tem valor de retorno do tipo int, com três parâmetros: o primeirodo tipo float, o segundo do tipo char e o terceiro do tipo int. A função funD tem valor de retorno do tipolong int, sem parâmetros. A função funE tem valor de retorno do tipo double, com dois parâmetros:ambos do tipo int, o primeiro qualificado como const e o segundo como register. A função funF temvalor de retorno do tipo void e um parâmetro do tipo int.9.12. funA Declaração com nome de parâmetro duplicado.

funB Declaração com parâmetro qualificado como static.funC Declaração com parâmetro com valor inicial.funD Definição com parâmetro do tipo vetor de tamanho variável não especificado.funE Função com tipo do valor de retorno void retornando valor.funF Função com tipo do valor de retorno float terminando sem retornar valor.funG Função retornando endereço de variável local.funH Declaração com valor de retorno do tipo vetor.funI Declaração com valor de retorno do tipo função.

9.13. Apenas as chamadas às funções funC, na linha 11, e funD, na linha 13, estão corretas. As chamadasà função funA, nas linhas 7 e 8, geram código (compilam), embora sejam claramente incompatíveis: comonão existe um protótipo para a função, ela pode ser chamada com qualquer número de argumento, masem tempo de execução o comportamento será indefinido se a quantidade de argumentos for diferente daquantidade de parâmetros, ou se o tipo dos argumentos for incompatível com o tipos dos respectivosparâmetros. O mesmo ocorre com a chamada à função funB, na linha 9: sua declaração não especificauma quantidade de parâmetros, aceitando qualquer número de argumentos. A chamada à função funC,na linha 10 está errada porque sua lista de argumentos não é compatível com os parâmetros declaradosno protótipo. As chamadas à função funD, nas linhas 12 e 14, também estão erradas pela mesma razão.9.14. O programa imprime o valor 4, 3, que corresponde à média dos números 3, 0 e 5, 6, e, em seguida, ovalor 4, 9, que corresponde à média dos números 4, 2, 5, 6 e 4, 9. Como aos argumentos da parte variávelé aplicada a promoção padrão de argumentos, na função media os valores reais são obtidos como valoresdo tipo double.9.16. A função funA tem tipo int (int, char *); o tipo de funB é int *(void), o de funC é float(double, char) e o de funD é void (short int, char).9.17. funA_t corresponde ao tipo int (void); funB_t, ao tipo short int (int, float); funC_T, aotipo void (double, short int); e funD_t, ao tipo double ().9.18. As seguintes chamadas estão erradas: na linha 7, funA1 é do tipo void (int) e deve ser chamadacom um argumento; na linha 11, funB2 é do tipo short int (int, float) e, portanto, seu primeiroargumento não pode ser void, que é o tipo do valor de retorno da função funA2; e na linha 13, funC1 édo tipo void (double, short int) e, portanto, seu segundo argumento não pode ser void, que é o tipodo valor de retorno da função funC1.

As demais chamadas estão corretas, embora em algumas haja conversão de valores. Na chamada àfunção funB1, na linha 10, por exemplo, o segundo argumento, do tipo int, é convertido em um valor dotipo float.

Page 18: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

18 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

9.19. O código a seguir mostra a função fib ea função main que a chama. A função fib im-plementa a produção dos termos da sequência deFibonacci de modo diferente do adotado no Exercí-cio 8.5. Aqui os termos anterior (t_ant) e anteriordo anterior (t_antant) são iniciados de forma quenão é necessário tratamento diferenciado para osdois primeiros.

#include <stdio.h>void fib(int);int main(void) {

int n;do {

scanf ("%d", &n);} while (n <= 0);fib(n);return 0;

}void fib(int n) {

int t_ant = 0, t_antant = 1, t;while (n-- > 0) {

t = t_ant + t_antant;printf ("%d ", t);t_antant = t_ant;t_ant = t;

};}

9.20. O código ao lado mostra apenas a funçãofib_soma.

long int fib_soma(int n) {int t_ant = 0, t_antant = 1, t;long int soma = 0;while (n-- > 0) {

t = t_ant + t_antant;soma = soma + t;t_antant = t_ant;t_ant = t;

};return soma;

}

9.22. Na função ao lado, o valor de retorno para otermo N é dado como a soma do valor dos termosN − 1 e N − 2. Desse modo a função é chamadanovamente até que um termo que tenha valor 1seja produzido. Existem duas condições de parada:para a chamada indevida, com N menor ou iguala 0, e para a produção do termo cujo valor é 1.

int fib_elm(int n) {if (n <= 0) {

return -1;}if (n <= 2) {

return 1;}return fib_elm(n - 1) + fib_elm(n - 2);

}

9.23. O programa a seguir implementa o Exercício 9.20 usando a função recursiva fib_elm, que é chamadarepetidas vezes para gerar os números da sequência de Fibonacci.

Esta solução é extremamente ineficiente! Osmesmos valores são produzidos diversas vezes: achamada fib_elm(4), por exemplo, chama e pro-duz os valores dos terceiro e segundo termos, quejá foram produzidos.

O uso de fib_elm para o Exercício 9.19 é se-melhante.

#include <stdio.h>int fib_elm(int);int main(void) {

int n; long int soma = 0;do {

scanf ("%d", &n);} while (n <= 0);for (int i = 1; i <= n; i++) {

soma = soma + fib_elm(i);}printf ("soma = %ld\n", soma);return 0;

}

9.24. Se o usuário digitar o valor 0, 5, o programa produz a seguinte saída:

0.500000 radianossen 0.500000 = 0.479426log sen 0.500000 = -0.735167

0.700000 radianossen 0.700000 = 0.644218log sen 0.700000 = -0.439719

Page 19: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

19

Na primeira iteração o valor de setjmp é 0 e o programa desvia o fluxo de execução para a linha 13, ondeimprime o valor lido, chama a função funA e interrompe o switch, indo para a linha 20. Nessa linha, seo resultado de funA (armazenado em res) tiver sido menor que −0, 7, o valor de a é incrementado de 0.2e a função longjmp é executada, provocando o desvio para a última execução de setjmp que registrou oestado do ambiente em estado: o fluxo é desviado para a função setjmp da linha 10, com um valor deretorno igual a 1, reiniciando o processo.

Na função funA ocorre a chamada à função funB, onde é calculado e impresso o seno do argumento dafunção. Se o valor do seno for negativo, ocorre a chamada a longjmp, com o desvio do fluxo de controlepara a linha 10, produzindo o valor de retorno 2. Isso faz com que seja impressa a mensagem “ senonegativo”, reiniciando o processo. Se o valor do seno não for negativo, o fluxo de execução volta à funçãofunA, onde o logaritmo natural do seno é calculado, impresso e usado como valor de retorno.

9.25. O programa ao lado imprime o próprionome, já que o primeiro argumento da linha decomando é sempre o nome do programa.

#include <stdio.h>int main(int qtd , char *args []) {

printf ("%s\n", args [0]);return 0;

}

Quando a função main é definida com dois parâmetros sempre registrará a linha de comando utilizadapara executar o programa, armazenando a quantidade de argumentos da linha no primeiro parâmetro e arelação de argumentos no segundo.

Capítulo 10

10.2. De cima para baixo a numeração da segunda coluna é: 3, 6, 1, 5, 4, 2.10.3. A variável ptr1 é um ponteiro para int, podendo armazenar endereços de variáveis do tipo int.Logo, a atribuição da linha 10 é correta e a da linha 11 incorreta, pois 23 não é um endereço. A atribuiçãoda linha 12 é correta, pois a expressão *ptr1 refere-se à variável apontada por ptr1, que pode ter seuconteúdo modificado.

A variável ptr2 é um ponteiro para const int, podendo armazenar endereços de variáveis do tipoint cujo conteúdo não pode ser modificado. A atribuição da linha 13 é correta e a da linha 14 é incorreta.

A variável ptr3 é constante e não pode ter seu conteúdo modificado (apenas iniciado, como ocorre coma variável ptr4). Porém, o conteúdo apontado por ela é do tipo int e pode ser modificado. A atribuiçãoda linha 15 é incorreta e as das linhas 16 e 17 são corretas.

A variável ptr5 é um ponteiro para ponteiro constante para int, podendo armazenar endereços deponteiros constantes, isto é, o conteúdo de ptr5 não pode ser modificado, pois é um ponteiro constante.A atribuição da linha 18 é incorreta, já que &a não é um ponteiro para ponteiro. As atribuições daslinhas 19 e 20 são corretas e a da linha 21 é incorreta porque o conteúdo apontado por ptr5 não pode sermodificado.

A variável ptr6 é um ponteiro para ponteiro para int. A atribuição da linha 22 é incorreta porque&ptr2 é um ponteiro para ponteiro para const int. Já a atribuição da linha 23 é correta.

10.4. O programa imprime a saída ao lado. Apósa atribuição da linha 5, c aponta para a variávela e após a atribuição da linha 6, d aponta para c(que aponta para a). Na primeira impressão tanto*c quanto **d referem-se a a.

6 8 6 66 8 8 88 12 8 812 16 12 1212 16 16 16

Após a atribuição da linha 8, c aponta para b. Como d continua apontando para c, o resultado é queagora tanto *c quanto **d referem-se a b.

Após as atribuições das linhas 10 e 11, a passa a ter o valor 8 e b passa a ter o valor 12. Após aatribuição da linha 12, c aponta para a. Como d continua apontando para c, o resultado é que agoratanto *c quanto **d referem-se a a.

Após a atribuição da linha 14, a (especificado pela expressão *c) passa a ter o valor de b, 12. Após aatribuição da linha 15, b passa a ter o valor 16. Tanto *c quanto **d continuam referindo-se a a.

Page 20: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

20 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

Após a atribuição da linha 17, c (especificado pela expressão *d) aponta para b. Como d continuaapontando para c, o resultado é que agora tanto *c quanto **d referem-se a b.

10.5. O programa imprime a saída ao lado. Nafunção main são impressos os conteúdos das variá-veis a e b após cada chamada às outras funções.Em cada função chamada as variáveis paramétri-cas assumem uma cópia dos argumentos usados nachamada.

107 12415 1213 4713 9513 23

Na função funA a modificação dos valores de x e y não afeta as variáveis a e b usadas como argumentosna chamada. Na chamada a funB o primeiro argumento é um ponteiro para a. Assim, a atribuição aoendereço apontado por x é, de fato, uma atribuição à variável a (apontada por x). Por isso, a atribuição*x = y corresponde a armazenar o valor 415 em a; o valor de b continua inalterado. Em funC tanto oconteúdo de a (através da expressão *x) quanto o conteúdo de b (através da expressão *y) são alterados.A atribuição x = y apenas modifica a variável local x. Na chamada a funD, o segundo argumento éo endereço do ponteiro que aponta para b. Ao iniciar a execução de funD, a variável x recebe o valordo endereço apontado por y, isto é recebe o ponteiro para b. Assim, a atribuição *x = 95 equivale aarmazenar 95 em b. Em funE a variável x recebe o endereço do endereço apontado por y (que é o ponteiropara b). Assim, x torna-se um ponteiro para o ponteiro para b e a atribuição **x = 23 armazena 23 emb.

10.6. O programa imprime os valores ao lado. 9 12 20

A variável fun é declarada como um ponteiro para uma função de int retornando int e a variável fun2é declarada como um ponteiro para uma função retornando int, cujo primeiro parâmetro é um ponteiropara uma função de int retornando int, e o segundo é um valor do tipo int.

Na atribuição da linha 8, fun recebe o endereço de funA. Assim, a impressão da linha 9 imprime oresultado da chamada a funA com o argumento 3. Já na atribuição da linha 10, fun recebe o endereço defunB, fazendo com que a impressão da linha 11 imprima o resultado da chamada a funB com o argumento6 (que é o resultado de funB(3)). A impressão da linha 13 imprime o resultado da chamada a funCusando como primeiro argumento o ponteiro para funB e como segundo argumento o valor 10 (resultadode funB(5)). Nesse caso, a função funC retorna o resultado da chamada a funB com o argumento 10.10.7. Variável Tipo

v_a int [3], tipo vetor completo.v_b int.v_c int *[12], tipo vetor completo.v_d int *.v_e int [12], tipo vetor completo.v_f int [2][2 * x], tipo vetor completo, tamanho variável.v_g int *.v_h char [3][2], tipo vetor completo.v_i char *[], tipo vetor incompleto, tamanho indefinido.v_j short [][*], tipo vetor incompleto, tamanho indefinido.v_k long [*], tipo vetor completo, tamanho não especificado.

As duas últimas declarações só podem ocorrer em protótipos de função. Portanto, v_j e v_k não sãopropriamente variáveis, e sim nomes de parâmetros.10.8. Declaração Tipo do vetor Tipo do elemento

int vet_a[2]; int [2] intint *vet_b[2]; int *[2] int *int vet_c[x]; int [x], variável intint vet_d[]; int [], incompleto intint vet_e[2][3]; int [2][3] int [3]int vet_f[][3]; int [][3], incompleto int [3]int vet_g[x][3]; int [x][3], variável int [3]int vet_h[x][y]; int [x][y], variável int [y], variável.

Page 21: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

21

10.9. Todas as declarações contêm erros. A seguinte tabela sumariza os tipos de erros, mostrando paracada um a unidade e a linha de código em que ocorrem:Erro Unidade/LinhasO asterisco para indicar tamanho não especificado só pode ser usado emprotótipos de função.

A/linha 3, B/linha 12.

Vetores incompletos só podem ser declarados com ligação externa (paraserem completados por outra declaração).

A/linhas 4 e 9.

Os elementos de um vetor não podem ter tipo incompleto. A/linhas 5 e 6.Vetores de tamanho variável não podem ser estáticos (não devem ter liga-ção externa ou interna).

A/linha 7, B/linhas 2 e 3.

Os parâmetros na definição de função devem ser nomeados. A/linha 13.A expressão que especifica o tamanho de um vetor deve ser determinada,não pode ter variáveis não declaradas, por exemplo.

B/linhas 6 e 9.

10.10. Todas estão corretas. Na unidade A, linhas 3 e 5, os vetores estão declarados com ligação externae não possuem definições explícitas que completem o seu tamanho.Portanto, o compilador assume que elespossuem 1 elemento. Já os vetores vet_c, declarado na linha 4, e vet_d, declarado na linha 8, tambémsão declarados com ligação externa e possuem definições que completam o seu tamanho na unidade B(linhas 2 e 3, respectivamente).

10.12. O programa produz a saída ao lado. Nainiciação da linha 3, após atribuir o valor ‘h’ aoelemento vetA[1][0], a relação {‘e’, ‘q’} é asso-ciada ao próximo elemento, vetA[1][1], fazendocom que ele receba o valor ‘e’.

|a|w|e||||t||u||c|b|a|e|||10|

Os elementos vetA[2][0] e vetA[2][1] recebem o caractere nulo, pois não possuem valores na ex-pressão de iniciação. Na iniciação da linha 4, após atribuir o valor {‘s’, ‘t’, ‘n’} ao elemento vetB[2], ovalor {’u’, [2] = ’u’} é atribuído ao próximo elemento, vetB[3] (aqui, o elemento vetB[3][1] recebe ocaractere nulo). O elemento vetB[1] fica sem atribuição e seus componentes recebem o caractere nulo.

10.13. O programa produz a saída ao lado. Como,por conta das atribuições das linhas 7 e 8, ptrAequivale a num+ 3 e ptrB equivale a num+ 10, en-tão ptrA + 2 equivale a num + 5, ptrB - 2 equi-vale a num+ 8 e num + (ptrB - ptrA) equivale anum+ (10− 3).

4 114 116 97 8

10.14. As expressões dos itens (a), (c), (e), (f) e (i) são equivalentes e apontam para o elementomat[0][1], assim como as expressões dos itens (d) (h) (g) e (j), que apontam para o elemento mat[1][0].As expressões dos itens (b) (k) e (l) também são equivalentes e apontam para o elemento mat[1][1].10.15. O protótipo da linha 5 e a definição da linha 16 utilizam o qualificador const para qualificarelementos que não pertencem à primeira dimensão do vetor. Na linha 10, o tipo do terceiro argumentode funA não é compatível com o tipo especificado para o terceiro parâmetro da função. O vetor matB édeclarado na linha 8 como do tipo int [3][2] (seu tipo é completado pela expressão de iniciação) e areferência a ele, na linha 10, é convertida em um ponteiro para int [2] (o tipo do seu primeiro elemento).Já o terceiro parâmetro na declaração de funA é especificado como sendo do tipo const int [const][*].Isto é, a função deve ser chamada tendo para esse parâmetro um argumento do tipo ponteiro (constante)para const int [].10.16. O vetor é criado na linha 10, com m elementos. A leitura dos elementos do vetor é realizada nocorpo do comando for das linhas 11-13. O comando for das linhas 14-16 é usado para imprimir todosos elementos do vetor e o comando for das linhas 18-20, para imprimi-los a partir do elemento de ordemn (índice n - 1).

Page 22: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

22 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

1 #include <stdio.h>2 int main(void) {3 int n, m;4 do {5 scanf ("%d", &m);6 } while (m <= 0);7 do {8 scanf ("%d", &n);9 } while ((n < 1) || (n > m));

10 int num[m];11 for (int i = 0; i < m; i++) {

12 scanf ("%d", &num[i]);13 }14 for (int i = 0; i < m; i++) {15 printf ("%d ", num[i]);16 }17 printf ("\n");18 for (int i = n - 1; i < m; i++) {19 printf ("%d ", num[i]);20 }21 return 0;22 }

10.17. O comando for das linhas 15-19 é usado para imprimir n elementos. O índice de cada elementoé controlado por duas variáveis: iesq é usada para imprimir os elementos de índices 0, 1, 2 etc., e idiré usada para imprimir os elementos de índices m − 1 (último), m − 2 (penúltimo), m − 3 (antepenúltimo)etc. Essas variáveis são usadas alternadamente: a cada iteração ou iesq é usada (e incrementada, para apróxima vez) ou idir é usada (e decrementada, para a próxima vez).

1 #include <stdio.h>2 int main(void) {3 int n, m;4 do {5 scanf ("%d", &m);6 } while (m <= 0);7 do {8 scanf ("%d", &n);9 } while ((n < 1) || (n > m));

10 int num[m];11 for (int i = 0; i < m; i++) {

12 printf (" digite num %d: ", i + 1);13 scanf ("%d", &num[i]);14 }15 for (int iesq = 0, idir = m - 1,16 ind = 0, i = 0; i < n; i++) {17 ind = (i % 2 == 0) ? iesq ++: idir --;18 printf ("%d ", num[ind]);19 }20 return 0;21 }

10.18. Na função f_pg a seguir assume-se que x é maior que 0 e que o vetor v possui x elementos. Apartir do terceiro, verifica-se se cada elemento dividido pelo anterior é igual à razão da P.G. Na primeiravez que essa propriedade não for satisfeita, a função retorna false, sem que os demais elementos precisemser verificados. Ao final do for utilizado para percorrer o vetor, a função retorna true, pois a propriedadefoi satisfeita para todos os elementos do vetor.

bool f_pg(int x, int v[x]) {double q, qaux;if (x == 1) {

return true;}q = (double)v[1]/v[0];for (int i = 2; i < x; i++) {

qaux = (double)v[i]/v[i-1];

if (fabs(qaux - q) >fabs(qaux * DBL_EPSILON )) {

return false;}

}return true;

}

Como a razão da P.G. pode ser real, ela é calculada e armazenada como um valor do tipo double. Porisso, também se usa a divisão real de um elemento pelo elemento anterior e verifica-se a igualdade entreqaux e q comparando sua diferença com um valor muito pequeno, qaux * DBL_EPSILON. Essa função deveser compilada com os cabeçalhos stdbool.h, float.h e math.h.10.19. Na função a seguir a variável paramétrica v é declarada como um ponteiro para int e todareferência a v[ind] é substituída por *(v + ind).

bool f_pg(int x, int *v) {double q, qaux;if (x == 1) {

return true;}q = (double )*(v + 1)/ *v;for (int i = 2; i < x; i++) {

qaux = (double )*(v + i)/ *(v + i-1);

if (fabs(qaux - q) >fabs(qaux * DBL_EPSILON )) {

return false;}

}return true;

}

10.20. Na função a seguir, percorre-se o vetor vA enquanto ele tiver elementos válidos. Cada elementoválido de vA é comparado com o elemento correspondente de vB, apenas se existir elemento correspondenteem vB: tão logo o valor FLT_MIN seja encontrado em vB ou vA o comando for é interrompido.

Page 23: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

23

int comp_vet(float vA[], float vB[]) {int qtd = 0;for (int i = 0; vA[i] != FLT_MIN &&

vB[i] != FLT_MIN; i++) {if (vA[i] > vB[i]) {

qtd ++;}

}return qtd;

}

10.21. int comp_vet(float *vA, float *vB) {int qtd = 0;for (int i = 0; *(vA + i) != FLT_MIN &&

*(vB + i) != FLT_MIN; i++) {if (*(vA + i) > *(vB + i)) {

qtd ++;}

}return qtd;

}

10.22. No programa a seguir, a cada número lido a variável soma é atualizada, de modo que ao final dasleituras ela contém a soma de todos os números lidos. No comando for, a cada impressão um número dasequência é subtraído da soma. Assim, são impressas a soma de todos os números, a soma do segundo atéo último (após a subtração do primeiro), a soma do terceiro até o último (após a subtração do segundo),e assim por diante.

#include <stdio.h>#define MAX (50)int main(void) {

int qtd = 0, seq[MAX], soma = 0;do {

scanf ("%d", &seq[qtd]);if (seq[qtd] == 0) {

break;}

soma = soma + seq[qtd];} while (++ qtd < MAX);for (int i = 0; i < qtd;

soma = soma - seq[i], i++) {printf ("%d ", soma);

}return 0;

}

10.23. O comando while das linhas 12-17 é usado para realizar a leitura dos números e incrementar oelemento do vetor histo correspondente a cada número lido, na faixa [0, QTD).

1 #include <stdio.h>2 #include <stdbool.h>3 #define QTD (11)4 int main(void) {5 bool houve_imp = false;6 int num = 0;7 int histo[QTD];8 for (int i = 0; i < QTD; i++) {9 histo[i] = 0;

10 }11 scanf ("%d", &num);12 while (num >= 0) {13 if ((num >= 0) && (num < QTD)) {14 histo[num ]++;15 }16 scanf ("%d", &num);17 }18 for (int i = 0; i < QTD; i++) {

19 printf ("%3d ", i);20 }21 printf ("\n");22 do {23 houve_imp = false;24 for (int i = 0; i < QTD; i++) {25 if (histo[i] > 0) {26 printf (" X ");27 histo[i]--;28 houve_imp = true;29 } else {30 printf (" ");31 }32 }33 printf ("\n");34 } while (houve_imp );35 return 0;36 }

O comando for da linhas 18-20 apenas imprime o cabeçalho do histograma. O comando do (linhas22-34) é usado para percorrer os elementos do vetor histo. A cada iteração, todos os elementos sãopercorridos (pelo comando for das linhas 24-32) e, se o elemento é maior que 0, um X é impresso e ocontador do elemento é decrementado; em caso contrário, um espaço é impresso. As iterações terminamquando o vetor é percorrido e nenhuma impressão de X é realizada.

Page 24: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

24 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

10.24. Na função ao lado, os parâmetros do tipovetor de int são convertidos em ponteiros para oprimeiro elemento do vetor (ponteiros para int).Desse modo, a referência vC[i] é um ponteiro parao endereço vC + i: quando se altera o elementovC[i] está se alterando o elemento do vetor usadocomo argumento.

void combina_vet(int x, int const vA[x],int const vB[x],int vC[const x]) {

for (int i = 0; i < x; i++) {if (i % 2 == 0) {

vC[i] = vA[i] + vB[i];} else {

vC[i] = vA[i] - vB[i];}

}}

10.25. No programa a seguir, os comandos for das linhas 14-18 são usados para ler os números da matrizde l linhas e c colunas. Após a leitura dos números p e q, o comando for das linhas 27-30 é usado paraimprimir os números da sequência especificada.

1 #include <stdio.h>2 int main(void) {3 int l, c, p, q;4 do {5 printf (" Digite L > 0): ");6 scanf ("%d", &l);7 } while (l <= 0);8 do {9 printf (" Digite C > 0): ");

10 scanf ("%d", &c);11 } while (c <= 0);12 int mat[l][c];13 printf (" Digite nums. matriz LxC)\n");14 for (int i = 0; i < l; i++) {15 for (int j = 0; j < c; j++) {16 scanf ("%d", &mat[i][j]);

17 }18 }19 do {20 printf (" Digite P (1 <= P <= M): ");21 scanf ("%d", &p);22 } while ((p < 1) || (p > l));23 do {24 printf (" Digite Q (1 <= Q <= M): ");25 scanf ("%d", &q);26 } while ((q < 1) || (q > l));27 for (int i = 0; i < c; i++) {28 printf ("%4d", mat[p-1][i] +29 mat[q-1][i]);30 }31 return 0;32 }

10.26. Como as funções são do mesmo tipo, osmesmos comandos for são usados para imprimirambas. Para cada índice i controlado pelo for ex-terno (linha 4-13), todos os elementos mA[i][j] daprimeira matriz são impressos (linhas 5-7), segui-dos de todos os elementos mB[i][j] da segundamatriz (linhas 9-11).

1 void imp_dupla(int l, int c, int mA[l][c],2 int mB[l][c])3 {4 for (int i = 0; i < l; i++) {5 for (int j = 0; j < c; j++) {6 printf ("%4d", mA[i][j]);7 }8 printf (" ");9 for (int j = 0; j < c; j++) {

10 printf ("%4d", mB[i][j]);11 }12 printf ("\n");13 }14 }

10.27. Na versão ao lado os parâmetros do tipoint [l][c] foram substituídos por ponteiros paraint [c] (o tipo dos elementos dos vetores mA e mB)e as referências a mA[i][j] foram substituídas por*(*(mA + i) + j) (do mesmo modo, para mB).

void imp_dupla(int l, int c, int (*mA)[c],int (*mB)[c])

{for (int i = 0; i < l; i++) {

for (int j = 0; j < c; j++) {printf ("%4d", *(*(mA + i) + j));

}printf (" ");for (int j = 0; j < c; j++) {

printf ("%4d", *(*(mB + i) + j));}printf ("\n");

}}

Page 25: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

25

10.28. A função ao lado retorna falso tão logo al-gum elemento da diagonal principal seja diferentede 1, ou algum elemento fora da diagonal princi-pal seja diferente de 0. Após percorrer todos oselementos da matriz, a função retorna verdadeiro,pois nesse caso todos os elementos satisfazem acondição estabelecida.

bool unidade(int l, int m[l][l]) {for (int i = 0; i < l; i++) {

for (int j = 0; j < l; j++) {if (((i == j) && (m[i][j] != 1)) ||

((i != j) && (m[i][j] != 0))) {return false;

}}

}return true;

}

10.29. Os dois primeiros comandos for (linhas4-13 e 5-11) são usados para percorrer todos oselementos de uma matriz hipotética C, imprimindocada um de seus elementos Cij. Antes de imprimi-lo, cada elemento é calculado (pelo comando fordas linhas 7-9, que implementa o somatório da mul-tiplicação dos elementos da linha i de mA pelos ele-mentos correspondentes da coluna j de mB).

1 void imp_mult(int l, int k, int c,2 int mA[l][k], int mB[k][c]){3 int Cij;4 for (int i = 0; i < l; i++) {5 for (int j = 0; j < c; j++) {6 Cij = 0;7 for (int x = 0; x < k; x++) {8 Cij = Cij + mA[i][x] * mB[x][j];9 }

10 printf ("%4d", Cij);11 }12 printf ("\n");13 }14 }

10.30. Os dois comandos for são usados parapercorrer os elementos da matriz m. Tão logo,identifica-se que a linha é a linha que deve ser su-primida, a iteração do formais externo é interrom-pida, iniciando uma nova linha. Do mesmo modo,tão logo identifica-se que a coluna é a coluna quedeve ser suprimida, o comando for mais interno éinterrompido, iniciando uma nova coluna. Os de-mais elementos são armazenados na matriz mc, quetem os índices dos seus elementos controlados demodo independente dos índices i e j.

void mat_mc(int l, int m[l][l],int mc[l-1][l-1],int li, int lj) {

for (int i = 0, imc = 0; i < l; i++) {if (i == li - 1) {

continue;}for (int j = 0, jmc = 0; j < l; j++) {

if (j == lj - 1) {continue;

}mc[imc][jmc ++] = m[i][j];

}imc ++;

}}

10.31. Na função ao lado, fixa-se a primeira linha(índice 0) da matriz m para calcular o cofator decada um de seus elementos m[0][j] (linhas 9-10).Para esse cálculo usa-se o determinante da matrizque se obtém suprimindo-se a linha 0 e coluna jda matriz original (a matriz de ordem menor é de-clarada na linha 7 e obtida chamando-se a funçãomat_mc do exemplo anterior). O cálculo do deter-minante desse nova matriz é uma chamada recur-siva à função det, que reinicia o processo até quea ordem da matriz recebida como argumento sejaigual a 1.

1 int det(int l, int m[l][l]) {2 int d = 0;3 if (l == 1) {4 return m[0][0];5 }6 for (int j = 0; j < l; j++) {7 int mc[l-1][l-1];8 mat_mc(l, m, mc, 1, j + 1);9 d = d + m[0][j] * pow(-1,j) *

10 det(l-1,mc);11 }12 return d;13 }

Capítulo 11

11.2. As atribuições dos itens (a) e (d) são inválidas, pois os tipos são incompatíveis. As demais sãoválidas.11.3. Os itens (c) e (f) são válidos. Os demais são inválidos pelas seguintes razões: item (a), regB é dotipo estrutura; o operador -> só pode ser usado com tipos ponteiros. Item (b) &regC.valor equivale a&(regC.valor); não se pode atribuir valor ao resultado do operador &. Item (d) (&regA) é um ponteiro

Page 26: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

26 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

(o endereço de regA); o operador . não pode ser aplicado a ponteiros. Item (e) &regA->valor equivale a&(regA->valor); o operador -> não pode ser aplicado a variáveis do tipo estrutura, apenas a ponteirospara estruturas ou uniões.11.4. O item (b) é inválido, pois o operador . não pode ser aplicado a um ponteiro. Pela mesma razão oitem (c) é inválido, já que a expressão *ptrD.num equivale a *(ptrD.num). Os demais itens são válidos.11.7. Os itens (a), (b) e (f) são equivalentes; assim como (c) e (e); e também (d), (g) e (h).

11.8. O programa produz a saída ao lado. Na li-nha 12, após a impressão do componente num daestrutura apontada por vA, o ponteiro é incremen-tada, passando a apontar para o próximo elemento.

10 10 2010 11 1111 20 20

Na linha 16, o incremento é aplicado ao componente num, o ponteiro não é modificado; é o mesmoelemento que é impresso na linha 17, agora já incrementado de 1. Na linha 20, o ponteiro é incrementadoantes do acesso, passando a apontar para o segundo elemento do vetor.

11.9. O programa produz a saída ao lado. Nainiciação da linha 9, após a atribuição de valor aocomponente prazo, o próximo valor é atribuídoao componente seguinte: desc — o componenteval recebe o valor padrão para o seu tipo. Já nainiciação da linha 10 é o componente prazo querecebe o valor padrão.

2.3 5 40 9 214.77 0 9

11.10. Na tabela a seguir, ‘’ representa o caractere nulo.seq

Val tp num tp num tp num fixovest[0]: 2, 3 ‘a’ 26 ‘b’ 28 ‘c’ 31 4vest[1]: 69, 0 ‘e’ 0 ‘d’ 13 ‘’ 0 77vest[2]: 0, 0 ‘’ 0 ‘’ 34 ‘f’ 0 82

11.11. Apenas os itens (b) e (d) são válidos. Os demais representam atribuições entre tipos incompatíveis.O item (e) é uma atribuição entre ponteiros, e é realizada, apesar do tipo do valor de &unC ser incompatívelcom o tipo de unB — o acesso através da variável unB, entretanto, torna-se indefinido.11.12. Apenas os itens (b), (e) e (f) são equivalentes. Os demais produzem valores que nem sempre sãoiguais.11.13. Na linha 7 um valor é armazenado na união usando-se o componente b, do tipo int, e na linha9 verifica-se se o o valor do componente a é menor do que zero. Como o tipo de a é unsigned int essacomparação nunca será verdadeira. O mesmo problema — de acessar o valor de uma união usando umcomponente diferente do usado para armazená-lo — ocorre na leitura e comparação das linhas 14 e 16.Nesse caso, se um valor positivo muito grande for digitado, ele pode ser considerado negativo.

11.14. O programa produz a saída ao lado. Osegundo argumento da segunda impressão é o con-teúdo do endereço da união varA, convertido emum valor do tipo int.

234 234Josefa linda234

11.15. O programa produz a saída ao lado. Oscampos de bits a e b são do tipo int, com 3 bits.Portanto, armazenam valores na faixa

[−(22), 22 − 1] = [−4, 3]

.

0 0 0-3 3 5-86

Por isso, o valor 5, resultante da operação 0 ^ 5, na linha 13, é armazenado como −3, e impressodessa forma pelo comando da linha 16. Na linha 18, a expressão val.a ^ val.c corresponde à operação

Page 27: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

27

ou-exclusivo entre (as representações binárias d)os valores −3 e 5, convertidos em valores do tipo int:daí o resultado −8. Na linha 19 a operação ocorre entre os valores 3 e 5, produzindo o resultado 6.11.16. void imp_aprovados(int n, aln_tp al[n]) {

for (int i = 0; i < n; i++) {if ((al[i].n1 + al[i].n2)/2 >= 5.0) {

printf ("%s\n", al[i].nome);}

}}

11.17. int comp_media(aln_tp alA , aln_tp alB) {float mA = (alA.n1 + alA.n2) / 2;float mB = (alB.n1 + alB.n2) / 2;if (mA > mB) {

return 1;} else {

if (mB > mA) {return -1;

} else {return 0;

}}

}

Capítulo 12

12.6. a) Correta.b) %d espera int *, &b é do tipo unsigned int *.c) Correta.d) Correta.e) %c espera char *, &b é do tipo unsigned int *.f) %f espera float *, &d é do tipo double *.g) %s espera char *, &s é do tipo char (*)[20].

12.6. h) Correta.i) %hd espera short int *, &a é do tipo int *.j) Correta.k) %lf espera double *, &f é do tipo float *.l) Modificador %ll não pode ser aplicado ao especificador g.

12.7. O primeiro número digitado pelo usuário é interpretado como um número decimal, octal ou hexa-decimal, dependendo do prefixo. O segundo é interpretado como um número octal, mesmo sem o prefixo0, e o terceiro é interpretado como hexadecimal, mesmo sem o prefixo 0x ou 0X. O programa produz asseguintes saídas:a) a=32 b=26 c=50.b) a=18 b=28 c=137. Na segunda leitura apenas os dígitos 3 e 4 são usados porque o dígito 8 nãopode compor um número octal. Na terceira leitura o número hexadecimal é composto pelos dígitos8 e 9. O dígitos 1 e 0 não são lidos.c) a=50 b=0 c=0. Após a leitura do hexadecimal 0x32, o número 0 (octal) é lido, ficando o cursorposicionado no dígito ‘x’. A terceira leitura não é realizada, já que ‘x’ não é nem o início de umprefixo nem um dígito hexadecimal válido.d) a=4 b=0 c=8. Na primeira leitura, por causa do prefixo 0, apenas o dígito 4 é considerado, ficandoo cursor de leitura posicionado no dígito 8. A segunda leitura não ocorre porque 8 não é um dígitooctal válido. A terceira leitura lê apenas o dígito 8, pois a leitura termina com o primeiro espaço.Os demais dígitos não são lidos.

12.8. Na leitura do item a) são considerados os caracteres de ‘a’ a ‘h’, de ‘t’ a ‘v’, além dos caracteresespaço, ‘o’, ‘s’ e ‘r’. Na leitura do item b) são considerados todos, exceto os caracteres de ‘d’ a ‘n’ e de‘x’ a ‘z’. Na leitura do item c) são considerados todos, exceto o caractere ‘d’ e na leitura do item d)

Page 28: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

28 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

são considerados todos, exceto o caractere de fim de linha. A leitura é interrompida quando o primeirocaractere fora do conjunto válido é encontrado. As seguintes saídas são produzidas: a) vou casar, b) voucasar, c) vou casar no fim e d) vou casar no fim do ano.12.9. O indicador de tamanho provoca a leitura de no máximo 2 caracteres na primeira leitura e 4 carac-teres na segunda leitura. No item (a) tanto o número 23 quanto o 78, 9 são lidos completamente. No item(b), após a leitura do número 17, apenas o valor 34.5 é lido, o caractere ‘8’ permanece na via de comuni-cação, pois excede os 4 caracteres indicados na diretiva. No item (c) os dois primeiros caracteres de 7362são lidos na primeira leitura e os dois caracteres seguintes são lido na próxima leitura, que termina como espaço. No item (d) o caractere ‘9’ é lido na primeira leitura e os quatro caracteres iniciais do número459.75 são lidos na segunda leitura, os caracteres ‘7’ e ‘5’ permanecem na via de comunicação. As seguin-tes saídas são produzidas: a) 23 78.900002, b) 17 34.500000, c) 73 62.000000 e d) 9 359.000000. Oerro na impressão do item (a) se deve à imprecisão na representação binária do decimal 78, 9.12.10. Se o tipo short int for implementado com 16 bits, na primeira impressão ocorre a redução mó-dulo 216 do valor 357.821.

Chamada à função Saídaprintf("%hd %9.7hd", a, a); 30141 0030141printf("%d %9.7d", a, a); 357821 0357821printf("%d %d", a, a); 357821 357821printf("%f %4.2f", b, b); 6789.522000 6789.52printf("%f %4.7f", b, b); 6789.522000 6789.5220000printf("%f %f", b, b); 6789.522000 6789.522000printf("%s\n%s", c, d); Aqui trago minha roupa

que recorda meu malfeitoprintf("%.10s\n%11.4s", c, d); Aqui trago

que12.11. Antes da leitura de cada nome, a cadeia nome é iniciada com o caractere nulo (linhas 10 e 21)porque se o usuário digitar apenas a tecla Enter nada será lido e nada será atribuído a ela, que permanececom o conteúdo que já possuía.

A função limpa_linha é chamada na linha 14, para descartar possíveis caracteres remanescentes, casoo usuário digite mais do que 30 caracteres, e na linha 22, para suprimir o caractere de fim de linha quenão é lido na leitura do salário (linha 16). A variável sal_maior é iniciada na linha 9 com o menor valornegativo do tipo double.

1 #include <stdio.h>2 #include <stdbool.h>3 #include <float.h>4 void atualiza_nome(char [], char []);5 bool nulo(char [*]);6 void limpa_linha(void);7 int main(void) {8 char nome [31] ="", nome_maior [31] ="";9 double sal , sal_maior = -DBL_MAX;

10 nome [0] = ’\0’;11 printf ("Nome: ");12 scanf ("%30[^\n]", nome);13 while (!nulo(nome)) {14 limpa_linha ();15 printf (" Salario: ");16 scanf ("%lf", &sal);17 if (sal >= sal_maior) {18 sal_maior = sal;19 atualiza_nome(nome , nome_maior );20 }21 nome [0] = ’\0’;22 limpa_linha ();23 printf ("Nome: ");24 scanf ("%30[^\n]", nome);25 }26 if (nome_maior [0] != ’\0’) {27 printf ("maior sal: %s, %.2f\n",

28 nome_maior , sal_maior );29 }30 return 0;31 }32 void atualiza_nome(char n[], char nm[]) {33 int i = 0;34 while (n[i] != ’\0’) {35 nm[i] = n[i];36 i++;37 }38 nm[i] = ’\0’;39 }40 void limpa_linha(void) {41 scanf ("%*[^\n]");42 scanf ("%*c");43 }44 bool nulo(char n[]) {45 int i = 0;46 while (n[i] == ’ ’) {47 i++;48 }49 if (n[i] == ’\0’) {50 return true;51 } else {52 return false;53 }54 }

Este programa pode ser melhorado com o uso de funções da biblioteca padrão, como a função para

Page 29: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

29

copiar cadeias de caracteres, em substituição à função atualiza_nome, e a função para verificar se umcaractere é espaço, que tornaria a função nulo mais adequada — como está, o caractere de tabulação,por exemplo, não é considerado espaço.12.12. As leituras nas linhas 5 e 12 utilizam a diretiva %i para permitir que o usuário digite o númerono formato decimal, octal ou hexadecimal. Como a leitura de valores numéricos descarta os caracteresespaços e o caractere de fim de linha iniciais, o usuário pode digitar os números em uma mesma linha ouem linhas separadas.

1 #include <stdio.h>2 #include <limits.h>3 int main(void) {4 int ant = INT_MIN , soma = 0, num;5 scanf ("%i", &num);6 if (num > 0) {7 do {8 if (ant > num) {9 soma = soma + ant;

10 }11 ant = num;12 scanf ("%i", &num);13 } while (num > 0);14 soma = soma + ant;15 }16 printf ("%d\n", soma);17 return 0;18 }

12.13. Na função ao lado, os elementos do vetorsão impressos alinhados à esquerda, se forem de or-dem par (índices 0, 2, ...), ou à direita, se forem deordem ímpar (índices 1, 3, ...). Entretanto, a saídaficará desalinhada caso algum elemento necessitede mais do que 15 caracteres para ser impresso.

voidalinha_margens(int n, long double v[n]) {

for (int i = 0; i < n; i++) {if (i % 2 == 0) {

printf ("|% -15.2Lf|", v[i]);} else {

printf (" |%15.2 Lf|\n", v[i]);}

}}

Capítulo 13

13.11. Os dados de cada empregado são digitados no corpo do comando do (linhas 16-34). O nome é lidocom a função fgets, na linha 24. O tamanho do nome lido é obtido com a função strlen e usado paraverificar se o caractere de fim de linha foi incluído no nome (linha 25), caso em que será removido. A áreade armazenamento temporário do teclado é limpa após a digitação da matrícula, na linha 22, para evitarque o caractere de fim de linha seja considerado parte do nome, e quando o caractere de fim de linha nãofoi incluído no nome, na linha 28, para evitar que ele e os demais caracteres remanescentes interfiram naspróximas leituras.

1 #include <stdio.h>2 #include <string.h>3 #include <stdbool.h>4 #define TAM (25)5 void limpa_linha(void);6 int main(void) {7 FILE *arq;8 int mat = 0;9 char nome[TAM + 1];

10 double sal = 0.0;11 arq = fopen(" empregados.cad", "a");12 if (arq == NULL) {13 printf (" arquivo nao pode ser aberto ");14 return -1;15 }16 do {17 printf (" Matricula: ");18 scanf ("%d", &mat);19 if (mat <= 0) {20 break;21 }

22 limpa_linha ();23 printf ("Nome: ");24 fgets(nome , TAM + 1, stdin);25 if (nome[strlen(nome) - 1] == ’\n’) {26 nome[strlen(nome) - 1] = ’\0’;27 } else {28 limpa_linha ();29 }30 printf (" Salario: ");31 scanf ("%lf", &sal);32 fprintf(arq , "%d %-25s %.2f\n",33 mat , nome , sal);34 } while (true);35 fclose(arq);36 return 0;37 }38 void limpa_linha(void) {39 scanf ("%*[^\n]");40 scanf ("%*c");41 }

A diretiva %-25s, da função fprintf, na linha 32, garante que o nome é sempre gravado com 25caracteres, alinhados à esquerda.13.12. A função atualiza_sal (linhas 31-37) é usada para atualizar a matrícula, o nome e o salário,

Page 30: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

30 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

servindo tanto para o menor (linha 19) quanto para o maior salário (linha 22). A atualização do nomenessa função seria mais sucinta se utilizasse a função strcpy para cópia de cadeias de caracteres, em vezdo comando for das linhas 33-35.

1 #include <stdio.h>2 #include <stdlib.h>3 #include <stdbool.h>4 #define TAM (25)5 void atualiza_sal(int , int *, char *, char *, double , double *);6 int main(void) {7 int mat , mat_menor = 0, mat_maior = 0;8 double sal , sal_menor = 0.0, sal_maior = 0.0;9 char nome[TAM + 1], nome_menor[TAM + 1], nome_maior[TAM + 1];

10 bool primeira_atu = true;11 FILE *arq = fopen(" empregados.cad", "r");12 if (arq == NULL) {13 printf (" Falha ao abrir arquivo\n");14 return EXIT_FAILURE;15 }16 while (fscanf(arq , "%d%*c%25c%lf\n", &mat , nome , &sal) != EOF) {17 nome[TAM] = ’\0’;18 if (primeira_atu || (sal < sal_menor )) {19 atualiza_sal(mat , &mat_menor , nome , nome_menor , sal , &sal_menor );20 }21 if (primeira_atu || (sal > sal_maior )) {22 atualiza_sal(mat , &mat_maior , nome , nome_maior , sal , &sal_maior );23 }24 primeira_atu = false;25 }26 fclose(arq);27 printf ("menor sal: %d %-25s %.2f\n", mat_menor , nome_menor , sal_menor );28 printf ("maior sal: %d %-25s %.2f\n", mat_maior , nome_maior , sal_maior );29 return EXIT_SUCCESS;30 }31 void atualiza_sal(int m, int *m_atu , char *n, char *n_atu , double s, double *s_atu) {32 *m_atu = m;33 for (int i = 0; i < TAM + 1; i++) {34 n_atu[i] = n[i];35 }36 *s_atu = s;37 }

Na primeira leitura a função de atualização é sempre chamada porque a variável primeira_atu é verda-deira; nas leituras seguintes, apenas se há modificação no menor ou no maior salário.

Na linha 16, o espaço existente entre a matrícula e o nome é lido com a diretiva %*c, que lê (sem realizaratribuição) exatamente um caractere. Se houvesse um espaço entre as diretivas, como em %d %25c, nãoapenas o espaço entre a matrícula e o nome seria lido, mas também possíveis espaços que fossem parteinicial do nome, prejudicando a leitura dos 25 caracteres subsequentes.

Como os nomes foram gravados sem o caractere nulo delimitando o fim da cadeia, após cada leituraesse caractere é inserido no fim da cadeia nome (linha 17).13.13. O arquivo é aberto para gravação na linha 5. A leitura e gravação dos números, e dos sequenciaiscorrespondentes, são realizadas no corpo do comando while (linhas 13-20), exceto a primeira leitura, queé realizada na linha 12.

1 #include <stdio.h>2 int main(void) {3 int seq = 0;4 double num = 0.0;5 FILE *arq = fopen(" valores.val", "wb");6 if (arq == NULL) {7 printf ("erro ao abrir o arquivo\n");8 return -1;9 }

10 printf (" Digite valores reais ");11 printf (" (0 p/terminar )\n");12 scanf ("%lf", &num);

13 while (num != 0.0) {14 ++seq;15 fwrite ((void *)&seq , sizeof(int),16 1, arq);17 fwrite ((void *)&num , sizeof(double),18 1, arq);19 scanf ("%lf", &num);20 }21 fclose(arq);22 return 0;23 }

As funções fwrite utilizam um ponteiro para a variável a ser gravada, em vez de um ponteiro para(o primeiro elemento de) um vetor: essa solução é possível quando se grava apenas um valor de cada vez.

Page 31: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

31

Na linha 13, a variável num é comparada diretamente com o valor 0, 0 porque ela é obtida do tecladocomo um valor do tipo double. Como zero é um valor que pode ser precisamente armazenado e não existeoperação realizada sobre a variável, não há erro de arredondamento.

13.14. No programa ao lado, a leitura do sequen-cial é feita como parte da condição do while, quedetermina o fim do arquivo. A leitura do númerocorrespondente a cada sequencial é realizada ape-nas se a leitura do sequencial for bem-sucedida.

Neste programa as referências às variáveis seqe num são usadas para calcular o tamanho dos va-lores a serem lidos. O uso de uma expressão comoargumento do comando sizeof é equivalente aouso do tipo da expressão; nesse caso, sizeof(num)é equivalente a sizeof(double).

#include <stdio.h>int main(void) {

int seq;double num;FILE *arq = fopen(" valores.val", "rb");while (fread((void *)&seq , sizeof(seq),

1, arq) == 1) {fread ((void *)&num ,

sizeof(num), 1, arq);printf ("%d %f\n", seq , num);

}fclose(arq);return 0;

}

Este programa, assim como os demais programas que leem o arquivo gerado no Exercício 13.13, podeapresentar um comportamento errado se executado em uma arquitetura diferente daquela na qual oarquivo foi gerado. Isso porque os tamanhos dos valores gravados foram calculados com base nos tiposint e double, cujas implementações podem variar de ambiente para ambiente.13.15. No arquivo lido pelo programa a seguir, cada registro é constituído de um inteiro e um real, e seutamanho é calculado na linha 5.

1 #include <stdio.h>2 #include <stdlib.h>3 int main(void) {4 long int pos , pos_ultimo;5 size_t tam_reg = sizeof(int) +6 sizeof(double );7 int seq = 0;8 double num = 0.0;9 FILE *arq = fopen(" valores.val", "rb");

10 if (arq == NULL) {11 printf ("erro ao abrir o arquivo\n");12 return EXIT_FAILURE;13 }14 fseek(arq , 0, SEEK_END );15 pos_ultimo = ftell(arq) - tam_reg;16 do {17 printf (" Digite um sequencial: ");

18 scanf ("%d", &seq);19 if (seq == 0) {20 break;21 }22 pos = tam_reg * (seq - 1);23 if ((pos < 0) || (pos > pos_ultimo )) {24 printf (" sequencial invalido\n");25 } else {26 fseek(arq , pos + sizeof(int),27 SEEK_SET );28 fread(&num , sizeof(double), 1, arq);29 printf ("%f\n", num);30 }31 } while (seq != 0);32 fclose(arq);33 return EXIT_SUCCESS;34 }

O tamanho do arquivo, até o início do último registro nele gravado, é calculado na linha 15, após oposicionamento do cursor em seu final. Esse tamanho é usado para determinar se a posição do registrosolicitado pelo usuário extrapola os limites do arquivo (linha 23).

A cada sequencial válido, a posição em que o registro correspondente deve iniciar é calculada (linha22) e, se estiver nos limites do arquivo, usada para posicionar o cursor e realizar a leitura (linhas 26-28).13.16. A estrutura do seguinte programa é semelhante à do exercício anterior. A diferença é que, casoo sequencial digitado seja válido, após a leitura e impressão do número correspondente, um novo valor élido e gravado em substituição ao antigo (linhas 30-35).

1 #include <stdio.h>2 #include <stdlib.h>3 int main(void) {4 long int pos , pos_ultimo;5 size_t tam_reg = sizeof(int) +6 sizeof(double );7 int seq = 0;8 double num = 0.0, valor;9 FILE *arq = fopen(" valores.val", "r+b");

10 if (arq == NULL) {11 printf ("erro ao abrir o arquivo\n");

12 return EXIT_FAILURE;13 }14 fseek(arq , 0, SEEK_END );15 pos_ultimo = ftell(arq) - tam_reg;16 do {17 printf (" Digite um sequencial: ");18 scanf ("%d", &seq);19 if (seq == 0) {20 break;21 }22 pos = tam_reg * (seq - 1);

Page 32: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

32 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

23 if ((pos < 0) || (pos > pos_ultimo )) {24 printf (" sequencial invalido\n");25 } else {26 fseek(arq , pos + sizeof(int),27 SEEK_SET );28 fread (&num , sizeof(double), 1, arq);29 printf ("Valor gravado: %f\n", num);30 printf ("Novo valor: ");31 scanf ("%lf", &valor);

32 fseek(arq , pos + sizeof(int),33 SEEK_SET );34 fwrite ((void *)&valor ,35 sizeof(valor), 1, arq);36 }37 } while (seq != 0);38 fclose(arq);39 return EXIT_SUCCESS;40 }

13.17. O comando while (linhas 3-16) é usado para percorrer a cadeia orig. A variável pos_i é usadapara determinar a posição a partir da qual cada valor é lido e a variável qtd_i armazena os caractereslidos. Antes da próxima leitura o índice pos_i é atualizado com a quantidade de caracteres lidos naleitura anterior (linha 15).1 int grv_div3(char *orig , int n, char *dest){2 int val , qtd_i , pos_i = 0, pos_j = 0, qtd_j = -1, resultado = 1;3 while (sscanf(orig + pos_i , "%d%n", &val , &qtd_i) != EOF) {4 if ((val % 3) == 0) {5 qtd_j = (( pos_j == 0) ?6 snprintf(dest + pos_j , n - pos_j , "%d", val) :7 snprintf(dest + pos_j , n - pos_j , " %d", val));8 if (qtd_j < (n - pos_j)) {9 pos_j = pos_j + qtd_j;

10 } else {11 resultado = 0;12 break;13 }14 }15 pos_i = pos_i + qtd_i;16 };17 dest[pos_j] = ’\0’;18 return resultado;19 }

Se o valor lido é divisível por 3 (linha 4), ele é gravado na cadeia dest a partir da posição pos_j. Aquantidade de caracteres gravados é armazenada em qtd_j e comparada com a quantidade de caracteresremanescentes de dest (linha 8). Se a gravação for bem-sucedida, o indicador de posição de início degravação é atualizado para a próxima gravação (linha 9). A primeira gravação difere das demais por nãoincluir o espaço antes do valor gravado. Caso algum valor divisível por 3 não possa ser gravado, o processoé interrompido, com a indicação do erro (linhas 11-12).13.18. A lista de argumentos variáveis é iniciada na linha 7 e cada argumento é obtido com o comandoda linha 9, onde já é adicionado à variável soma, que será gravada na cadeia res. Cada gravação é feita apartir do endereço res+ i, em que i contém a quantidade de caracteres gravados anteriormente. Caso asoma acumulada não possa ser gravada na quantidade de caracteres restantes (teste do if, na linha 15),a função retorna com o valor false. Se todas as somas forem gravadas corretamente, a função retornacom o valor true.

1 bool2 acumula(int n, char *res , int qtd , ...) {3 double soma = 0.0;4 int i = 0, r;5 va_list args;6 res [0] = ’\0’;7 va_start(args , qtd);8 for (int ind = 0; ind < qtd; ind ++) {9 soma = soma + va_arg(args , double );

10 r = ((i == 0) ?11 snprintf(res + i, n - i,

12 "%g", soma) :13 snprintf(res + i, n - i,14 " %g", soma ));15 if (r >= n - i) {16 return false;17 }18 i = i + r;19 }20 return true;21 }

Do mesmo modo que no exercício anterior, a primeira gravação difere das demais por não incluir oespaço antes do valor gravado. Os cabeçalhos stdarg.h e stdbool.h são necessários para compilar estafunção.13.19. Se o nome não for vazio, o arquivo é aberto para verificar sua existência (linhas 16 e 17). Apenasse o arquivo existir um novo nome é solicitado. O novo nome deve ser não vazio e diferente de qualquerarquivo já existente. Isso é verificado pelo comando do das linhas 20-31. Antes de ser renomeado, oarquivo é fechado (linha 32).

Page 33: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

33

1 #include <stdio.h>2 #include <stdbool.h>3 #include <string.h>4 bool vazio(char []);5 int main(void) {6 char nome [80], n_nome [80];7 FILE *arq , *arq_aux;8 do {9 nome [0] = ’\0’;

10 printf (" Digite o nome do arquivo: ");11 scanf ("%79[^\n]", nome);12 scanf ("%*[^\n]"); scanf ("%*c");13 if (vazio(nome)) {14 break;15 }16 arq = fopen(nome , "r");17 if (arq == NULL) {18 printf (" arquivo inexistente\n");19 } else {20 do {21 n_nome [0] = ’\0’;22 printf (" Digite novo nome: ");23 scanf ("%79[^\n]", n_nome );24 scanf ("%*[^\n]"); scanf ("%*c");25 arq_aux = fopen(n_nome , "r");26 if (arq_aux != NULL) {

27 printf (" Existe :%s\n", n_nome );28 fclose(arq_aux );29 }30 } while (vazio(n_nome) ||31 (arq_aux != NULL ));32 fclose(arq);33 rename(nome , n_nome );34 printf ("nome modificado ");35 printf ("de |%s| para |%s|\n",36 nome , n_nome );37 }38 } while (true);39 return 0;40 }41 bool vazio(char n[]) {42 if (n[0] == ’\0’) {43 return true;44 };45 for (size_t i = 0; i < strlen(n); i++) {46 if (n[i] != ’ ’) {47 return false;48 }49 }50 return true;51 }

Antes de cada leitura a cadeia que armazena os caracteres digitados é iniciada com o primeiro caracterenulo (linhas 9 e 21). Isso é necessário porque, caso o usuário digite apenas a tecla Enter, a leitura falharáe a cadeia ficará com o conteúdo original. Após cada leitura a área de armazenamento temporário deentrada é esvaziada (linhas 12 e 24), para permitir que uma nova leitura ocorra na próxima iteração. Afunção vazio considera nulas as cadeias sem caracteres ou apenas com o caractere espaço — seria maiseficiente se usasse a função isspace, vista no Capítulo 15.

Capítulo 14

14.9. Se o arquivo não puder ser aberto, o ponteiro arq será nulo e o programa é finalizado apósa impressão da mensagem. Na leitura do arquivo, se houver algum erro o valor de retorno será EOF,terminando as iterações do comando while. O comando if após o while usa a função feof para verificarse o término foi normal ou anormal.

#include <stdio.h>#include <stdlib.h>int main(void) {

char nome [31];int seq;double n1, n2, media;FILE *arq = fopen(" alunos.txt", "r");if (arq == NULL) {

printf ("Arq. alunos.txt inexiste\n");return EXIT_FAILURE;

}nome [30] = ’\0’;while (fscanf(arq , "%d %30c%lf%lf\n",

&seq , nome , &n1, &n2) != EOF){

media = (n1 + n2) / 2.0;if (media >= 7.0) {

printf ("%2d %30s %5.2f %5.2f %5.2f\n",seq , nome , n1 , n2, media);

}}if (feof(arq)) {

fclose(arq);return EXIT_SUCCESS;

} else {printf ("Erro E/S: %d\n", ferror(arq ));return EXIT_FAILURE;

}}

14.11. Na expressão tgamma(INFINITY) não ocorre erro, pois o resultado HUGE_VAL (representando oinfinito) não é produzido por aproximação a partir de valores finitos. Já na expressão tgamma(DBL_MAX)ocorre erro porque o valor DBL_MAX, embora extremamente grande, é finito, sendo o resultado produzidopor aproximação.14.13. As leituras terminam com o fechamento do teclado (valor de retorno EOF). As leituras comsupressão de atribuição servem para esvaziar a área de armazenamento temporário do teclado. O valor deerrno é zerado antes da execução da função atanh. O teste MATH_ERRNO & math_errhandling asseguraque a variável errno é usada na indicação dos erros.

Page 34: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

34 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

#include <stdio.h>#include <math.h>#include <errno.h>#include <stdbool.h>void verifica_erro(void);int main(void) {

double x = 0.0, ath = 0.0;while (true) {

printf (" Digite x (Ctrl -d p/fim): ");if (scanf ("%lf", &x) == EOF) {

break;}scanf ("%*[^\n]"); scanf ("%*c");errno = 0;ath = atanh(x);printf ("arco tanh(%f) = %f", x, ath);

verifica_erro ();}return 0;

}void verifica_erro(void) {

if (MATH_ERRNO & math_errhandling) {if (errno == EDOM) {

printf (" (erro de dominio )");}if (errno == ERANGE) {

printf (" (erro de imagem )");}

}printf ("\n");

}

14.14. O programa é semelhante ao anterior, apenas usando as exceções de ponto flutuante para averificação dos erros. O estado das exceções é restaurado antes da execução da função atanh e todas asexceções que indicam erros de imagem são verificadas de uma única vez.

#include <stdio.h>#include <math.h>#include <fenv.h>#include <stdbool.h>void verifica_erro(void);int main(void) {

double x = 0.0, ath = 0.0;while (true) {

printf (" Digite x (Ctrl -d p/fim): ");if (scanf ("%lf", &x) == EOF) {

break;}scanf ("%*[^\n]"); scanf ("%*c");feclearexcept(FE_ALL_EXCEPT );ath = atanh(x);printf ("arco tanh(%f) = %f", x, ath);verifica_erro ();

}return 0;

}void verifica_erro(void) {

if (MATH_ERREXCEPT & math_errhandling) {if (fetestexcept(FE_INVALID )) {

printf (" (erro de dominio )");}if (fetestexcept(FE_DIVBYZERO |

FE_OVERFLOW |FE_UNDERFLOW )) {

printf (" (erro de imagem )");}

}printf ("\n");

}

14.15. bool armazena(double x, int *y) {feclearexcept(FE_ALL_EXCEPT );*y = x;if (fetestexcept(FE_INVALID) != 0) {

return false;} else {

return true;}

}

14.21. Na função soma_elms a única modificação é o registro da função trata_fpe para tratar o sinalSIGFPE. Na função trata_fpe, após a impressão da mensagem, o sinal é registrado novamente com otratamento padrão, e relançado. O uso da função printf torna a função de tratamento de sinal nãopadronizada.

int soma_elms(int v[], unsigned int qtd) {int soma = 0;signal(SIGFPE , trata_fpe );for (unsigned int i = 1u; i < qtd; i++) {

soma = soma + v[i] / v[i - 1u];}return soma;

}

void trata_fpe(int s) {printf ("erro sinal %d\n", s);signal(SIGFPE , SIG_DFL );raise(s);

}

14.22. Na função de tratamento de sinal ocorre o registro do sinal novamente e o retorno ao processamentocom a execução da função longjmp. O ponto de retorno é marcado com a função setjmp, no início decada iteração. A soma é realizada apenas se o resultado de setjmp é igual a zero, indicando a execuçãoda macro a partir do fluxo normal da execução.

Page 35: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

35

int soma_elms(int v[], unsigned int qtd) {int soma = 0;signal(SIGFPE , trata_fpe );for (unsigned int i = 1u; i < qtd; i++) {

if (setjmp(estado) == 0) {soma = soma + v[i] / v[i - 1u];

}}return soma;

}

/* variavel global:* jmp_buf estado;*/

void trata_fpe(int s) {signal(s, trata_fpe );longjmp(estado , 1);

}

A variável estado, do tipo jmp_buf, deve ser declarada global.14.23. As chamadas f(2, 3, 4) e f(9, 4, 6) provocam o aborto do programa.14.24. Os seguintes trechos mostram as modificações feitas na abertura do arquivo e ao final, antes dotérmino do programa:

if (arq == NULL) {perror ("Erro ao abrir arquivo ");return EXIT_FAILURE;

}

if (feof(arq)) {fclose(arq);return EXIT_SUCCESS;

} else {printf ("Erro E/S: %s\n",

strerror(ferror(arq )));return EXIT_FAILURE;

}

O uso de perror pode não resultar na mensagem correta: funciona com o compilador gcc, versão4.4.3, mas o padrão não requer que a variável errno seja atualizada quando a função fopen falha.

Capítulo 15

15.3. Sendo n menor ou igual a strlen(s), oponteiro s + strlen(s) - n aponta para a cadeiaformada pelos últimos n caracteres da cadeia apon-tada por s. O valor de strncmp é zero, se as cadeiassão iguais, ou diferente de zero, se são diferentes.Assim, !strncmp resulta no valor verdadeiro, se ascadeias são iguais, e falso, se são diferentes.

bool extremos_ncmp(const char *s, int n) {if ((n <= 0) || (n > strlen(s))) {

return false;} else {

return!strncmp(s, (s + strlen(s) - n), n);

}}

15.4. Como ptr é o ponteiro para a primeira ocorrência de sub em orig, ptr - orig é a quantidade deelementos de orig antes dessa ocorrência (isto é, a distância entre o início da cadeia apontada por ptr e oinício da cadeia apontada por orig). De modo semelhante, ptr + strlen(sub) aponta para o caracterede orig seguinte à primeira ocorrência de sub. Com esses valores, basta copiar para dest as subcadeiasde orig antes e após a primeira ocorrência de sub.

O qualificador restrict indica que os elemen-tos apontados por dest não devem ser acessadosatravés de outros ponteiros. Esta restrição é pre-servada tanto pelo código da função suprime_subquanto pelas funções strcpy e strncpy (que a in-cluem em seus protótipos). A função suprime_subpressupõe que a cadeia destino é grande o bastantepara receber os caracteres copiados.

void suprime_sub(char * restrict dest ,const char *orig ,const char *sub) {

char *ptr = strstr(orig , sub);if (ptr == NULL) {

strcpy(dest , orig);} else {

strncpy(dest , orig , ptr - orig);strcpy(dest + (ptr - orig),

ptr + strlen(sub));}

}

Page 36: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

36 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

15.5. Uma vez definida qual a menor, a cadeiaque deve aparecer à esquerda é copiada para sr, edepois esta é concatenada com a que deve aparecerà direita.

void ordena_cat(const char * restrict sa ,const char * restrict sb,char * restrict sr) {

if (strcmp(sa , sb) < 0) {strcpy(sr, sa);strcat(sr, sb);

} else {strcpy(sr, sb);strcat(sr, sa);

}}

A restrição sugerida pelos qualificadores restrict é atendida, já que é incluída no protótipo dasfunções chamadas no código de ordena_cat (a função strcmp não requer que os ponteiros usados comoargumentos sejam restritos, mas garante que os elementos por eles apontados não são modificados).

15.6. Como esq aponta para a subcadeia que ini-cia com o primeiro caractere c, a expressão esq -cadeia resulta na quantidade de caracteres à es-querda do primeiro c (linha 17). Do mesmo modo,como dir aponta para a subcadeia que inicia como último caractere c, a expressão dir - cadeiaresulta na quantidade de caracteres à esquerda doúltimo c e, consequentemente, strlen(cadeia) -(dir - cadeia) - 1 resulta na quantidade de ca-racteres à direita do último c (linhas 18-19).

Se as distâncias dos caracteres em relação àsrespectivas extremidades são iguais, a quantidadede pares equidistantes (qtd_pares) é incrementadae a cadeia modificada para uma nova verificação:seu início passa a ser o elemento seguinte ao apon-tado por esq e seu fim o elemento anterior ao apon-tado por dir (linhas 23-25). A verificação é inter-rompida quando o caractere não mais existe na ca-deia ou quando os caracteres encontrados não sãoequidistantes.

Como a cadeia apontada por cda deve ser cons-tante, ela é copiada na linha 4 para que possa sermodificada sem infringir a especificação.

1 bool equidchar(const char *cda , char c) {2 char *esq , *dir , *cadeia , *ptr_free;3 int qtd_pares = 0, qtd_char = 0;4 cadeia = malloc(sizeof(char) *5 strlen(cda));6 ptr_free = cadeia;7 strcpy(cadeia , cda);8 esq = cadeia;9 dir = cadeia + strlen(cadeia );

10 while (esq < dir) {11 esq = strchr(cadeia , c);12 if (esq == NULL) {13 break;14 }15 qtd_char ++;16 dir = strrchr(cadeia , c);17 if ((esq - cadeia !=18 strlen(cadeia) -19 (dir - cadeia) - 1))20 {21 break;22 }23 qtd_pares ++;24 cadeia = ++esq;25 *dir = ’\0’;26 }27 free(ptr_free );28 if (qtd_char == 0) {29 return false;30 } else {31 return (qtd_pares == qtd_char );32 }33 }

O ponteiro ptr_free é declarado na linha 6 para ser usado na liberação do espaço alocado pela funçãomalloc (que é discutida, juntamente com a função free, no Capítulo 16).15.7. A função valida_hora verifica cada uma das possíveis ocorrências de erro, retornando o valor falsose alguma for verdadeira. Inicialmente, verifica-se a existência de um delimitador iniciando a cadeia (coma função strspn) ou de um delimitador duplo (com a função strstr), já que esses delimitadores sãoignorados na composição dos formantes. O passo seguinte é obter os formantes da hora, minuto e segundoe verificar se algum deles é nulo. Para cada formante obtido, realiza-se sua conversão em um valor inteiro(com a função strtol), verificando-se se o valor obtido é válido, possui no máximo 2 dígitos e está dentroda faixa esperada. Finalmente, verifica-se se os minutos e segundos são zeros para a hora 24. A funçãoretorna verdadeiro se não encontra erro em nenhum das verificações realizadas.

bool valida_hora(char *hora_dig) {long int hh, mm, ss;char *resto , *hora , *min , *seg;if (( strspn(hora_dig , ":") > 0) ||

(strstr(hora_dig , "::") != NULL)) {return false;

}hora = strtok(hora_dig , ":");min = strtok(NULL , ":");seg = strtok(NULL , "");if ((hora == NULL) || (min == NULL) ||

(seg == NULL)) {

Page 37: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

37

return false;}errno = 0;hh = strtol(hora , &resto , 10);if (erro_conv(hora , resto)) {

return false;}if (( strlen(strtok(hora ," ")) > 2) ||

(hh < 0L) || (hh > 24L)) {return false;

}mm = strtol(min , &resto , 10);if (erro_conv(min , resto)) {

return false;}if (( strlen(strtok(min ," ")) > 2) ||

(mm < 0L) || (mm > 59L)) {return false;

}ss = strtol(seg , &resto , 10);if (erro_conv(seg , resto)) {

return false;}if (( strlen(strtok(seg ," ")) > 2) ||

(ss < 0L) || (ss > 59L)) {return false;

}if ((hh == 24L) &&

((mm != 0L) || (ss != 0L))) {return false;

}return true;

}bool erro_conv(char *cda , char *resto) {

if ((errno != 0) ||(cda == resto) ||(strtok(resto ," ") != NULL)) {return true;

} else {return false;

}}

A função erro_conv, chamada a cada conversão, verifica se a conversão foi bem-sucedida analisandoo valor de errno e as cadeias dos caracteres convertidos e remanescentes (o teste strtok(resto," ")!= NULL assegura que a cadeia de caracteres remanescentes não contém caracteres diferentes de espaço).15.10. No programa a seguir, os comandos de leitura com supressão de atribuição servem para esvaziara área de armazenamento temporário do teclado, no caso do usuário digitar mais do que 80 caracteres.

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdbool.h>#include <math.h>#include <errno.h>#define TAM (81)bool real_valido(char *, double *);int main(void) {

char linha[TAM];double num = 0.0;do {

printf (" Digite um valor real: ");scanf ("%80[^\n]", linha);scanf ("%*[^\n]"); scanf ("%*c");

} while (! real_valido(linha , &num));

printf ("Maior inteiro <= num: %d\n",(int)floor(num));

return 0;}bool real_valido(char *cda , double *n) {

char *resto;errno = 0;*n = strtod(cda , &resto );if ((errno != 0) ||

(resto == cda) ||(strtok(resto ," ") != NULL)) {return false;

}return true;

}

A função real_valido verifica se a cadeia de caracteres recebida como argumento contém um úniconúmero real válido. Após a conversão da cadeia em um valor do tipo double, verifica-se a ocorrênciade erros de imagem (errno != 0), erros de conversão (resto == cda) e se o usuário digitou caracteresdiferentes de espaço, além do número real (strtok(resto," ")).15.11. A leitura do número inteiro é realizada considerando toda a linha digitada pelo usuário — umúnico número deve existir na linha para o inteiro ser considerado válido. As leituras dos números reaissão feitas obtendo-se individualmente cada número digitado pelo usuário (com a diretiva %s da funçãoscanf). Se o número real obtido é válido, a comparação com o maior já armazenado é feita e o contadorde números lidos é atualizado.

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdbool.h>#include <float.h>#include <errno.h>#define TAM (81)bool real_valido(char *, double *);bool int_valido(char *, long int *);int main(void) {

char linha[TAM];long int n = 0, qtd = 0;double num = 0.0, maior = -DBL_MIN;

do {printf (" Digite um inteiro (> 0): ");scanf ("%80[^\n]", linha);scanf ("%*[^\n]"); scanf ("%*c");

} while ((! int_valido(linha , &n)) ||(n <= 0));

printf (" Digite %ld numeros reais:\n", n);while (qtd < n) {

scanf ("%s", linha );if (real_valido(linha , &num)) {

if (num > maior) {maior = num;

}

Page 38: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

38 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

qtd ++;}

}printf ("Maior num: %f\n", maior );return 0;

}bool int_valido(char *cda , long int *n) {

char *resto;errno = 0;

*n = strtol(cda , &resto , 10);if ((errno != 0) ||

(resto == cda) ||(strtok(resto ," ") != NULL)) {return false;

}return true;

}

A função int_valido é semelhante à função real_valido, que não é mostrada por ser igual à doexercício anterior.15.12. A leitura da base termina com a digitação de um valor negativo ou na faixa [2, 36]. Após a leiturada base, a leitura do número é realizada apenas se o valor da base estiver na faixa válida (se for maior ouigual a 2, será necessariamente menor ou igual a 36).

A leitura da base é feita com a função scanf. Isso significa que se o usuário digitar, por exemplo, 23.a2,o valor 23 será obtido permanecendo a cadeia “.a2” na área de armazenamento temporário do teclado(os valores remanescentes da área de armazenamento são eliminados com as leituras com supressão deatribuição). O valor 0 é atribuído à base antes de cada leitura para que uma nova iteração seja realizada,caso a leitura falhe (se o usuário digitar apenas letras, por exemplo).

#include <stdio.h>#include <stdlib.h>#include <string.h>#include <stdbool.h>#include <errno.h>bool num_valido(char *, int , long int*);#define TAM (81)int main(void) {

int b;long int num = 0L;char linha[TAM];do {

do {printf (" Digite a base ");printf (" (negativo p/terminar ): ");b = 0;scanf ("%d", &b);scanf ("%*[^\n]"); scanf ("%*c");

} while ((b >= 0) &&((b < 2) || (b > 36)));

if (b >= 2) {printf (" Digite num. base %d: ", b);scanf ("%80[^\n]", linha);scanf ("%*[^\n]"); scanf ("%*c");

if (num_valido(linha , b, &num)) {printf ("%s (base %d) ", linha , b);printf ("== %ld (base 10)\n", num);

} else {printf ("%s, invalido na base %d\n",

linha , b);}

}} while (b > 0);return 0;

}bool num_valido(char *cda , int b,

long int *num) {char *resto;errno = 0;*num = strtol(cda , &resto , b);if ((errno != 0) ||

(resto == cda) ||(strtok(resto ," ") != NULL)) {return false;

}return true;

}

A função num_valido é semelhante à função int_valido do exercício anterior, usando, entretanto, abase informada pelo usuário para realizar a conversão.15.13. Como a função de conversão produz um valor do tipo long int, é necessário verificar se esse valorestá dentro dos limites do tipo-alvo [INT_MIN, INT_MAX].

bool hex_para_int(const char *cda , int *n){char *resto;long int num;errno = 0;num = strtol(cda , &resto , 16);if ((errno != 0) || (resto == cda) ||

(strtok(resto ," ") != NULL)) {return false;

}if ((num < INT_MIN) || (num > INT_MAX )){

return false;}*n = num;return true;

}

15.14. Na função ao lado a comparação é feita caractere a caractere. O primeiro caractere de uma cadeiamaior que o da outra é suficiente para definir esta cadeia como a maior. Se uma das cadeias terminar,com todos os caracteres iguais ao da outra, a maior será a de maior tamanho.

Todos os caracteres são convertidos em minúsculos antes de serem comparados.

Page 39: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

39

intcompcad(const char *cA, const char *cB) {

int i = 0;while ((cA[i] != ’\0’) &&

(cB[i] != ’\0’)) {if (tolower(cA[i]) > tolower(cB[i])) {

return 1;}

if (tolower(cA[i]) < tolower(cB[i])) {return -1;

}i++;

}return (strlen(cA) - strlen(cB));

}

Capítulo 16

16.1. A criação do vetor se faz com a alocação de qtd elementos de tamanho sizeof(int), nas linhas7-8 (o programa não testa se a alocação foi bem-sucedida). O armazenamento (linhas 9-12) e a impressãodos números (linhas 13-15) usam a variável vetnum, do tipo ponteiro para int.

1 #include <stdio.h>2 #include <stdlib.h>3 int main(void) {4 int qtd;5 printf (" Digite a qtd elms: ");6 scanf ("%d", &qtd);7 int *vetnum =8 (int *) malloc(qtd * sizeof(int));9 for (int i = 0; i < qtd; i++) {

10 printf (" digite elm %d: ", i);11 scanf ("%d", &vetnum[i]);12 }13 for (int i = 0; i < qtd; i++) {14 printf ("%d ", *( vetnum + i));15 }16 return 0;17 }

16.2. O programa a seguir usa a função malloc para alocar o espaço necessário para armazenar um valordo tipo struct r_aluno (linhas 11-13).

1 #include <stdio.h>2 #include <stdlib.h>3 struct r_aluno {4 int matr;5 float nota1;6 float nota2;7 };8 void ler_aluno(struct r_aluno *);9 void imp_aluno(struct r_aluno *);

10 int main(void) {11 struct r_aluno *aluno =12 (struct r_aluno *) malloc(13 sizeof(struct r_aluno ));14 ler_aluno(aluno);15 imp_aluno(aluno);16 return 0;

17 }18 void ler_aluno(struct r_aluno *al) {19 printf (" Digite os dados do aluno\n");20 printf (" Matricula: ");21 scanf ("%d", &al->matr);22 printf (" Primeira nota: ");23 scanf ("%f", &al->nota1);24 printf (" Segunda nota: ");25 scanf ("%f", &al->nota2 );26 }27 void imp_aluno(struct r_aluno *al) {28 printf ("Matr: %d Notas: %5.2f %5.2f ",29 al->matr , al->nota1 , al->nota2);30 printf ("Media: %5.2f\n",31 (al ->nota1 + al->nota2) / 2);32 }

As chamadas às funções ler_aluno e imp_aluno, nas linhas 14-15, usam a própria variável alunocomo argumento, e não mais o seu endereço (isso porque a variável aluno é um ponteiro).16.3. No programa a seguir, o primeiro parâmetro da função inclui_elm é declarado como um ponteiropara ponteiro para elem_t porque a variável lista que se deseja modificar já é um ponteiro para elem_t.Portanto, na função inclui_elm, as referências ao conteúdo de lst são referências ao ponteiro para oprimeiro elemento da lista, cujo endereço foi utilizado na chamada à função.

#include <stdio.h>#include <stdlib.h>typedef struct elem elem_t;struct elem {

int val;elem_t *prox;

};void inclui_elm(elem_t **, int);int main(void) {

int num;elem_t *lista = NULL;elem_t *elem;scanf ("%d", &num);while (num != 0) {

inclui_elm (&lista , num);

scanf ("%d", &num);}elem = lista;while (elem != NULL) {

printf ("%d -> ", elem ->val);elem = elem ->prox;

}printf ("//");return 0;

}void inclui_elm(elem_t **lst , int num) {

elem_t *elem;elem = (elem_t *) calloc (( size_t)1,

sizeof(elem_t ));if (elem == NULL) {

Page 40: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

40 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

return; /* erro nao tratado */}elem ->val = num;

elem ->prox = *lst;*lst = elem;

}

16.4. A lista é percorrida do modo convencional:iniciando com o primeiro, a variável lst apontapara o elemento atual, sendo atualizada a cada ite-ração com o valor do próximo elemento. Toda alista é percorrida, mas apenas os elementos paressão impressos.

void lista_pares(elem_t *lst) {while (lst != NULL) {

if ((lst ->val % 2) == 0) {printf ("%d ", lst ->val);

}lst = lst ->prox;

}}

16.5. No início da função, a variável ant, que ar-mazena o elemento anterior, é atualizada com oprimeiro elemento (se existir primeiro elemento).Os elementos restantes da lista são obtidos e com-parados com o elemento anterior — a cada iteraçãoa variável ant é atualizada, antes da obtenção dopróximo elemento da lista.

void lista_menorant(elem_t *lst) {int ant = 0;if (lst != NULL) {

ant = lst ->val;lst = lst ->prox;

}while (lst != NULL) {

if (lst ->val < ant) {printf ("%d ", lst ->val);

}ant = lst ->val;lst = lst ->prox;

}}

16.6. O teste para verificar se os índices das li-nhas que devem ser trocadas pertencem à matrizé realizado no início da função. Caso não sejamválidos, a função retorna sem realizar a troca. Atroca é efetuada com o auxílio do vetor aux con-tendo a quantidade de elementos necessária paraarmazenar uma linha. Primeiro, a linha de índicex (cujo endereço é dado por m + x) é copiada paraaux (os c elementos da linha vezes o tamanho decada elemento). Segundo, a linha de índice y (cujoendereço é m + y) é copiada para a linha de índicex. Por último, a linha salva em aux é copiada paraa linha de índice y.

void move_linha(int l, int c, int m[l][c],int x, int y) {

int aux[c];if ((x < 0) || (x >= l) ||

(y < 0) || (y >= l)) {return; /* indices x, y invalidos */

}memcpy ((void *)aux , (void *)(m + x),

(c * sizeof(int )));memcpy ((void *)(m + x), (void *)(m + y),

(c * sizeof(int )));memcpy ((void *)(m + y), (void *)aux ,

(c * sizeof(int )));}

16.7. A primeira linha é comparada com as res-tantes, da segunda em diante; em seguida, a se-gunda linha é comparada com as restantes, da ter-ceira em diante; e assim por diante, até que sejamencontradas duas linhas iguais ou que todas as li-nhas tenham sido comparadas. A comparação éfeita com os endereços que correspondem às linhas,considerando-se os c elementos da linha vezes o ta-manho de cada elemento.

bool linhas_iguais(int l, int c,int m[l][c]) {

for (int i = 0; i < l-1; i++) {for (int j = i + 1; j < l; j++) {

if (memcmp ((void *)(m + i),(void *)(m + j),(c * sizeof(int ))) == 0){

return true;}

}}return false;

}

16.8. Na função ordena_linha, o endereço de cada linha (como um ponteiro para void: (void *)(m +i)), juntamente com a quantidade de elementos da linha e o tamanho de cada elemento, é fornecido àfunção qsort. A função comp apenas compara o conteúdo dos argumentos como valores do tipo int.

Page 41: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

41

void ordena_linha(int l, int c,int m[l][c]) {

for (int i = 0; i < l; i++) {qsort ((void *)(m + i),

c, sizeof(int), comp);}

}int comp(const void *x, const void *y) {

if (*(int *)x == *(int *)y) {

return 0;};if (*(int *)x > *(int *)y) {

return 1;} else {

return -1;}

}

16.9. Não. A função compE compara os argumentos como valores do tipo int, enquanto a função compD,ao usar memcmp, compara os argumentos convertidos em valores do tipo unsigned int. Desse modo, osvalores negativos são considerados pela função compD como maiores que os positivos.16.10. A quantidade de bytes do arquivo é obtida com a função ftell, após o posicionamento do cursorde leitura no fim do arquivo, e a quantidade de registros é calculada dividindo-se o tamanho do arquivopelo tamanho do registro. O vetor alunos é criado com essa quantidade de registros. Os registros doarquivo são lidos e armazenados diretamente no vetor alunos: o primeiro no elemento cujo endereço éalunos + 0, o segundo no elemento cujo endereço é alunos + 1, e assim por diante. Após a leitura, ovetor é ordenado e impresso. A função de comparação compara os componentes nome de dois elementosdo vetor, usando a função strcmp.

#include <stdio.h>#include <string.h>#include <stdlib.h>#define QTD (31)struct reg {

int seq;char nome[QTD];double n1, n2;

};void imp_alunos(struct reg [], int);int comp(const void *, const void *);int main(void) {

FILE *arq = NULL;size_t tamreg = sizeof(struct reg);size_t qtd = 0, qtdreg = 0;arq = fopen(" alunos.bin", "r");fseek(arq , 0, SEEK_END );qtdreg = ftell(arq) / tamreg;struct reg alunos[qtdreg ];fseek(arq , 0, SEEK_SET );while (fread(alunos + qtd++,

tamreg , 1, arq) == 1) {}fclose(arq);qsort ((void *)alunos , qtdreg ,

tamreg , comp);imp_alunos(alunos , qtdreg );return 0;

}void imp_alunos(struct reg alunos[],

int qtd) {for (int i = 0; i < qtd; i++) {

printf ("%d %-35s %.2f %.2f %.2f\n",alunos[i].seq , alunos[i].nome ,alunos[i].n1, alunos[i].n2 ,(alunos[i].n1 + alunos[i].n2 )/2);

}}int comp(const void *rA , const void *rB) {

return strcmp ((( struct reg *)rA)->nome ,(( struct reg *)rB)->nome);

}

16.11. A primeira parte deste programa é semelhante à do exercício anterior. Após a leitura e o ordena-mento do vetor de alunos, o comando do é usado para repetidamente ler um nome do teclado e pesquisaro nome lido no vetor, até que seja digitado o nome vazio.

A comparação usada pela função bsearch é diferente da usada pela função qsort. Na função qsortcompara-se elementos do vetor de alunos. Já na função bsearch, a comparação ocorre entre a cadeiadigitada pelo usuário e o componente nome dos elementos do vetor de alunos.

#include <stdio.h>#include <string.h>#include <stdlib.h>#include <stdbool.h>#define QTD (31)struct reg {

int seq;char nome[QTD];double n1, n2;

};void imp_aluno(struct reg *);int comp(const void *, const void *);int compnome(const void *, const void *);char *le_linha(char *, int);int main(void) {

FILE *arq;size_t tamreg = sizeof(struct reg);

size_t qtd = 0, qtdreg;char nome[QTD];struct reg *al;arq = fopen(" alunos.bin", "r");fseek(arq , 0, SEEK_END );qtdreg = ftell(arq) / tamreg;struct reg alunos[qtdreg ];fseek(arq , 0, SEEK_SET );while (fread(alunos + qtd++,

tamreg , 1, arq) == 1) {}fclose(arq);qsort ((void *)alunos , qtdreg ,

tamreg , comp);do {

printf (" Digite um nome: ");printf ("(<Enter > para terminar ): ");

Page 42: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

42 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

le_linha(nome , QTD);if (nome [0] == ’\0’) {

break;}al = (struct reg *)

bsearch ((void *)nome ,(void *)alunos , qtdreg ,

tamreg , compnome );if (al != NULL) {

imp_aluno(al);} else {

printf ("Nome inexistente .\n");}

} while (true);return 0;

}

void imp_aluno(struct reg *aluno) {printf ("%d %-35s %.2f %.2f %.2f\n",

aluno ->seq , aluno ->nome ,aluno ->n1, aluno ->n2,(aluno ->n1 + aluno ->n2)/2);

}int comp(const void *rA , const void *rB) {

return strcmp ((( struct reg *)rA)->nome ,(( struct reg *)rB)->nome);

}intcompnome(const void *rA , const void *rB) {

return strcmp ((char *)rA,(( struct reg *)rB)->nome);

}

A função le_linha não é mostrada por ser idêntica à do Exemplo 13.9.16.12. A obtenção da estrutura de formatação deve ocorrer apenas após a adoção da localização doambiente pelo programa. Do contrário, a estrutura de localização refletirá a localização padrão, e não ado ambiente.

#include <stdio.h>#include <locale.h>#include <string.h>int main(void) {

double num;char *sd;struct lconv *formato;setlocale(LC_ALL ,"");formato = localeconv ();sd = formato ->decimal_point;printf ("Neste programa , na digitacao"

" de valores reais ,\n");if (sd == NULL) {

printf ("nao use separador decimal\n");} else {

if (strcmp(sd , ",") == 0) {

printf ("use a virgula ");} else {

if (strcmp(sd , ".") == 0) {printf ("use o ponto ");

} else {printf ("use o simbolo %s", sd);

}}printf (" como separador decimal .\n");

}printf (" Digite um valor real: ");scanf ("%lf", &num);printf (" Numero lido: %f\n", num);return 0;

}

16.13. Na função ao lado, a chamada a mbrtowcusa a variável p_conv, iniciada no início da execu-ção, para controlar o estado de conversão.

Após a conversão de um caractere, se o retornoé igual a −1 ou −2, a cadeia de caracteres estendi-dos é terminada com o caractere nulo, e a execuçãoda função é interrompida. Nos demais aspectos,esta função é equivalente à do Exemplo 16.17

voidconverte(wchar_t *ce , const char *mb) {

size_t res = 9;mbstate_t p_conv;memset (&p_conv , 0, sizeof(p_conv ));for (int i_mb = 0, i_ce = 0;

res != (size_t) 0;i_mb += res , i_ce ++) {

res = mbrtowc(ce + i_ce , mb + i_mb ,MB_CUR_MAX , &p_conv );

if ((res == (size_t) -2) ||(res == (size_t) -1)) {

*(ce + i_ce) = L’\0’;break;

}}

}

16.14. Na função ao lado, inicialmente a quanti-dade de caracteres estendidos necessária para con-verter toda a cadeia mb é calculada, sem realizar aconversão. Se o resultado é maior ou igual a −2(convertido em um valor do tipo size_t), a funçãoretorna sem realizar a conversão.

voidconverte(wchar_t *ce , const char *mb) {

size_t qtd = mbstowcs(NULL , mb, 0);if (qtd >= (size_t) -2) {

return;}mbstowcs(ce, mb , qtd + (size_t) 1);

}

Se a conversão pode ser realizada sem erros, mbstowcs é chamada para converter qtd + 1 caracteres(o que inclui o caractere nulo). Assume-se que o tamanho da cadeia apontada por ce é suficiente paraarmazenar os caracteres estendidos.

Page 43: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

43

16.15. Na função ao lado, a função mbrlen é cha-mada para cada caractere da cadeia apontada porcda_mb. A cada conversão bem-sucedida a quan-tidade de caracteres estendidos é incrementada de1, e o ponteiro cda_mb é atualizado para apontarpara o próximo caractere multibyte (ultrapassandoa quantidade já convertida).

Quando o resultado é −1 ou −2 a condição doif é satisfeita e a função retorna o valor −1.

size_t qtd_wc (const char *cda_mb) {mbstate_t p_conv;size_t res , qtd = 0;memset (&p_conv , 0, sizeof(p_conv ));while ((res = mbrlen(cda_mb , MB_CUR_MAX ,

&p_conv )) > 0) {if (res >= (size_t) -2) {

return (size_t) -1;}cda_mb += res;qtd ++;

}return qtd + 1;

}

Quando o resultado é 0, a função retorna a quantidade calculada de caracteres estendidos mais 1(referente ao caractere nulo).

A solução deste exercício é adaptada da documentação da biblioteca glibc. Pode-se obter resultadossemelhantes com a execução de, por exemplo, mbstowcs(NULL, linha, 0).

16.18. A função ao lado apenas retorna o resul-tado da comparação do dia da semana com os va-lores correspondentes ao sábado e domingo.

bool fim_semana(const struct tm *t) {return ((t->tm_wday == 0) ||

(t->tm_wday == 6));}

16.19. A função a seguir converte as datas recebidas em valores do tipo time_t e utiliza a funçãodifftime para calcular a diferença entre elas. A quantidade de dias é obtida dividindo-se a quantidadede segundos pela quantidade de segundos em um dia.

A função assert garante que a execução seja interrompida caso as datas sejam inválidas (ou nãopossam ser convertidas em um valor do tipo time_t).

intdif_dias(struct tm *dA, struct tm *dB) {

time_t dtA , dtB;dtA = mktime(dA);dtB = mktime(dB);

assert ((dtA != (time_t) -1) &&(dtB != (time_t) -1));

return round(difftime(dtA , dtB) /(24 * 60 * 60));

}

16.20. Na função ao lado, a cada iteração do co-mando while, o tempo atual é obtido e sua dife-rença em relação ao tempo de início da execução écomparada com o número de segundos informado.

void pausa(int seg) {time_t ini = time(NULL);while (difftime(time(NULL), ini) < seg){}

}

16.21. Na função ao lado, o comando do repetea geração da mensagem solicitada enquanto o re-sultado for 0. Inicialmente, o tamanho da cadeiaque armazenará a mensagem é definido como 68(60 da iniciação e 8 da soma no corpo do comandodo). A cada vez que a função strftime falha, acadeia anterior é liberada e uma nova tentativa érealizada, adicionando-se 8 caracteres ao tamanhoda cadeia.

char *msg_data(const struct tm dt) {size_t qtd = (size_t )60;char *linha = NULL;do {

if (linha != NULL) {free(linha);

}qtd = qtd + 8;linha = (char *) calloc(qtd ,

sizeof(char ));} while (strftime(linha , qtd ,

"Aos %j dias do ano de %Y, ""em uma %A, as %H h %M min ""%S s\n", &dt) == 0);

return linha;}

Page 44: Elementos de programação em C Respostas dos exercícios ... · Elementos de programação em C Respostas dos exercícios selecionados FranciscoA.C.Pinheiro,Elementos de programação

44 RESPOSTAS DOS EXERCÍCIOS SELECIONADOS

Capítulo 17

17.1. Nas opções (a) e (b) o programa imprime 234 e nas opções (c) e (d) ele imprime 16735. Observe,na opção (c), que para arquivos do usuário a diretiva -iquote tem precedência sobre -I.

17.2. O texto ao lado mostra como ficam as de-clarações das variáveis p1, p2, d e n, após a subs-tituição das macros.

double p1 = (log(s) - log(p)) / log (1.0 + i);double p2 = (log(s) - log(p)) / log (1.0 + i);double d = log (1.0 + i);double n = (log(s) - log(p));

Este exercício mostra que as substituições ocorrem no momento da referência. Embora a definição deDEN seja posterior a PERA e PERB, que a utilizam, ela já está definida no momento em que PERA e PERB sãoexpandidas.17.3. a) a + b / (a - b)

b) 2 + x + 4 / (2 + x - 4)c) y + y / (y - y)d) if (4 > r++) 4 -= 4 - --y; else r++ -= --ye) if (x + y > y - x) x + y -= x + y - -4; else y - x -= -4f) if (x > ) x -= x - x; else -= xg) int num[4] = {3, 6, 7};h) int sal[b][c] = {{2, 2}, {3, 4}};i) int x[y] = {};j) int x[] = {};

17.4. a) tab_A[4];b) tab_[];c) tab_2[3 * x];d) printf("3 * 2 = %d\n", 3 * 2);e) printf("valor( , 2 + 6) = %d\n", tab_[2 + 6]);f) printf("valor(A, valor(A, 1)) = %ld\n", tab_A[tab_A[1]]);

17.5. O programa imprime a cadeia “cte fp_zero definida”. Quando o identificador FP_ZERO é avaliadocomo parte da diretiva #if ele é considerado uma macro, que é substituída por sua definição, novamenteo identificador FP_ZERO, que agora não sofre mais substituição, sendo avaliado como 0. A expressão re-sultante é #if 0 == 2, falsa. Já como parte da expressão do if, o identificador FP_ZERO é consideradouma constante enumerada, cujo valor é 2.17.6. O código à esquerda é produzido se a macro __cplusplus estiver definida e o código à direita, senão estiver:

struct __exception{

int type;char *name;double arg1;double arg2;double retval;

};extern intmatherr (struct __exception *__exc) throw ();

struct exception{

int type;char *name;double arg1;double arg2;double retval;

};extern intmatherr (struct exception *__exc);

17.7. O código à esquerda é produzido se a macro __NO_LONG_DOUBLE_MATH estiver definida e o código àdireita, se não estiver:

(sizeof (2.3L) == sizeof (float) ?__fpclassifyf (2.3L) :__fpclassify (2.3L));

(sizeof (2.3L) == sizeof (float) ?__fpclassifyf (2.3L) :

sizeof (2.3L) == sizeof (double) ?__fpclassify (2.3L) :__fpclassifyl (2.3L));