• Nenhum resultado encontrado

O que não fazer em C C

N/A
N/A
Protected

Academic year: 2018

Share "O que não fazer em C C"

Copied!
15
0
0

Texto

(1)

Material original por WaltP, disponibilizado no site GIDNetwork

1

.

Adaptação, tradução e revisão por Gutierrez PS

2

Os programadores de C/C++ não são impedidos de fazer certas coisas que não deveriam. Sejam funções que parecem ser úteis e não o são, devido a falhas ocultas, sejam métodos ruins, errados ou desnecessários que foram ensinados. Este artigo discorrerá sobre vários desses problemas.

O código mostrado está escrito particularmente em C. Foi utilizada a IDE Code::Blocks, configurada com o compilador GNU GCC3. Os resultados podem ser diferentes para cada código projetado para exibir os erros. Porém os problemas existem na maioria, senão em todos os compiladores. E se a maioria dos compiladores mostrarem essas anomalias, você ainda usará o recurso se funcionar no seu compilador?

gets()

Nunca use a função gets(). Nunca. Nunca. Nunca. Espero ter sido claro. NUNCA!

gets() é uma função que se comporta muito mal. Ela não possui nenhuma verificação interna, o que significa que ela lerá qualquer coisa fornecida. Defina um buffer de 10 caracteres e a gets() aceitará com prazer a Constituição Federal como entrada.

Eis um exemplo. Crie e compile este programa:

#include <stdio.h>

int main() {

char b1[] = "ABCD";

char b2[] = "LMNO";

char b3[] = "ZYXW";

puts(b1);

puts(b2);

puts(b3);

putchar('\n');

puts("Digite alguns caracteres:");

gets(b2);

putchar('\n');

puts(b1);

puts(b2);

puts(b3);

return(0); }

1 Disponível em: <http://www.gidnetwork.com/b-56.html>. Acesso em 12/05/2012, às 20h. 2http://about.me/gutierrezps

3 O autor original utilizou o compilador Borland 5.5. Resolvi utilizar o Code::Blocks para verificar os erros,

(2)

2

Veja que há 3 arrays de caracteres de 5 caracteres cada não esqueça o \0 no final de cada um . Agora, execute o programa e digite 1234 quando for pedido. Minha saída:

D:\>gets.exe ABCD

LMNO ZYXW

Digite alguns caracteres: 1234

ABCD 1234 ZYXW

D:\>

Tudo bem até agora. Execute de novo e digite todos os números:

D:\>gets ABCD LMNO ZYXW

Digite alguns caracteres: 1234567890

67890 1234567890 ZYXW

D:\>

Opa! O que aconteceu com b1?!? Bem, a gets() aceitou com prazer o que foi digitado e colocou na memória, começando por b2 e não dando a mínima pra nada a não ser a entrada de caracteres. Ela sobrescreveu um pedaço de memória que não deveria.

Tenha em mente que os resultados podem ser diferentes, mas o conceito é o mesmo. Esta função é perigosa! Modifique esse programa, e poderão aparecer bugs. Algumas vezes pode-se perceber que serão aceitos muitos caracteres. Quem sabe o que ela está fazendo

com sua memória, o que está sobrescrevendo?

Evite a função gets() ao máximo, como se ela fosse a peste bubônica. Ao invés dela, utilize a fgets():

fgets(buffer, BufLength, stdin);

No programa anterior, use:

puts("Digite alguns caracteres: ");

fgets(b2, 5, stdin); // 5 é o tamanho do buffer b2

(3)

3

Situação nº 1:

Foram digitados mais caracteres do que o buffer armazena. a) O buffer contém BufLength-1 caracteres

b) O fluxo de entrada ainda contém o resto e será lido no próximo fgets()

Os caracteres restantes no fluxo de entrada terão que ser tratados. Isso será discutido adiante.

Situação nº 2:

Foram digitados menos caracteres do que o buffer armazena.

a) Seu buffer contém BufLength-1 caracteres, incluindo o \n ao fim b) O fluxo de entrada está vazio

O fluxo de entrada está limpo, então não há nenhum problema de I/O4. Mas é preciso tratar o \n. Para removê-lo, inclua a biblioteca <string.h> no seu programa e adicione uma linha:

puts("Digite alguns caracteres:");

fgets(b2, 5, stdin); // 5 é o tamanho do buffer b2

if (b2[strlen(b2)-1] == '\n') b2[strlen(b2)-1] = '\0';

O comando if testará o último caractere (b2[strlen(b2)-1] == '\n') e, se for \n, substituirá por \0.

Para lidar com ambas as situações de uma vez, tanto para remover o \n quanto para limpar o buffer, defina uma variável temporária de, digamos, 50 caracteres (defina o tamanho) e use esse código:

if (b2[strlen(b2)-1] == '\n')

b2[strlen(b2)-1] = '\0'; // remove o caractere '\n'

else

fgets(lixo, 50, stdin); // captura o restante dos caracteres // do fluxo de entrada

OK, não é tão fácil. Mas é melhor do que ter um problema de overflow5, não é? De qualquer forma, faça bom uso do código acima.

4Input/Output, Entrada/Saída

(4)

4

fflush() para limpar o fluxo de entrada

Recomenda-se nunca utilizar fflush() para limpar o fluxo de entrada. Segundo os Padrões do C:

int fflush(FILE* stream);

Esvazia o fluxo de entrada e retorna zero se bem-sucedido, ou EOF se houver erro. Efeito não-definido no fluxo de entrada. fflush(NULL) esvazia todos os fluxos de entrada.6

Isso significa que os compiladores não precisam definir o esvaziamento dos fluxos de entrada. Alguns definiram, de fato, esse recurso. Ainda assim, não deve ser utilizado. O motivo:

Você esteve programando por alguns anos e possui o hábito de esvaziar o stdin

porque sempre usa a scanf() (outra função que deve ser evitada7). Você entra numa

empresa que usa um compilador que não possui esse recurso particular. Seu programa

não funciona mais. Você gastará dias tentando descobrir o que há errado porque sabe

que a fflush() não pode ser o problema - usou-a por anos.

Portanto, esta função deve ser evitada daqui em diante.

O que fazer então? Bem, a primeira recomendação é não usar funções como a scanf()8, que adoram deixar lixo no fluxo de entrada. Encontre outros meios para ler sua entrada e não se apoie em funções que possuem anomalias inesperadas ou apresentam resultados inconsistentes. Fora isso, comece a verificar os retornos de todas as suas funções de I/O (incluindo scanf()) e comece a estudar o que elas fazem quando a entrada não é exatamente a esperada.

A melhor opção, que está além dos conhecimentos básicos e, portanto, deve ser almejada, é a leitura e verificação do buffer de entrada por conta própria. Fora isso, dificilmente é possível criar uma entrada à prova de balas .

6 http://www.infosys.utas.edu.au/info/documentation/C/CStdLib.html#fflush 7 O porquê será discutido adiante.

(5)

5

feof() para sair de um loop

Recomenda-se nunca utilizar feof() como indicador de saída de um loop. feof() retornará 1 só depois de receber o valor EOF9, e não ao alcançá-lo. Para testar isso, crie um arquivo de texto comum (feof.txt no meu caso):

4 Andando 3 Trabalhando 2 Comendo 1 Dormindo

Depois, compile e execute este código:

#include <stdio.h>

int main() {

FILE *st;

char buf[20];

st = fopen("feof.txt", "r");

if (st != NULL) {

while (!feof(st)) {

fgets(buf, 20, st);

puts(buf);

}

fclose(st);

}

return 0; }

A seguinte saída pode ser obtida10:

D:\>feof 4 Andando

3 Trabalhando

2 Comendo

1 Dormindo

1 Dormindo

D:\>

O que aconteceu foi: a última linha do arquivo foi lida, mas não o EOF. Quando o loop foi executado mais uma vez, o fgets() tentou ler mais uma linha e falhou. Já que nenhuma verificação de erros foi feita, o que restava no buffer ainda estava lá e o loop continuou.

Pode-se resolver isso de duas maneiras: verificar erros na leitura.

9 A macro EOF é um valor inteiro retornado por um ponteiro para indicar o término de um arquivo, ou outras condições de erro.

(6)

6

Solução 1A:

while (!feof(st)) {

if (fgets(buf, 20, st) != NULL) { // processa o buffer

puts(buf);

} }

fclose(st);

Solução 1B:

while (!feof(st)) {

if (fgets(buf, 20, st) == NULL) break;

// sai do loop quando terminar

else puts(buf); }

fclose(st);

Note que um bom pedaço de código foi inserido para resolver o problema, e agora são executados dois testes: um para o feof() no while, e outro parra o fgets() dentro do loop. A maneira mais recomendada (assumindo que a primeira coisa do loop é a leitura) é a número 2:

while (fgets(buf, 20, st) != NULL) puts(buf); // processa o buffer

fclose(st);

(7)

7

system pause

Nunca entendi o porquê da system(“pause”) ser tão popular. Claro que a execução do programa será pausada antes dele ser terminado. Esta pausa é muito útil quando sua IDE não mantém a janela aberta após a execução do programa, o que impede a leitura dos dados.

Mas usar a system(“pause”) é como queimar a mobília para se aquecer quando você tem um ótimo termostato na parede.

Muitas pessoas, incluindo instrutores, por alguma razão inexplicável acham razoável chamar o sistema operacional e executar um comando do sistema para interromper momentaneamente um programa. Não faço a menor ideia de onde tiraram isso. Motivos:

x Não é portátil. Isso funciona apenas em sistemas que possuem o comando PAUSE

no nível do sistema, como DOS ou Windows. Não é aplicável ao Linux nem à maioria dos outros...

x É uma função muito dispendiosa e que demanda muitos recursos. É como usar

um trator para abrir a porta da frente. Funciona, mas usar a chave é mais limpo, fácil e barato. O que a função system() faz:

1. Suspende o programa

2. Chama o sistema operacional

3. Abre um terminal (executa novamente o sistema operacional num sub-processo)

4. O sistema operacional busca o comando PAUSE 5. Reserva a memória para executar o comando

6. Executa o comando e espera o pressionamento da tecla 7. Desfaz a reserva da memória

8. Sai do sistema operacional 9. Continua o programa

Há meios muito mais eficientes inclusos na própria linguagem que tornam tudo isso desnecessário.

x Inclui-se um cabeçalho inicialmente desnecessário: stdlib.h ou cstdlib

É um mau hábito que deve, sem dúvidas, ser evitado.

(8)

8

scanf()

Por onde começar? scanf() é uma função famosa por se comportar mal.

Na maioria das vezes, ela deixa coisas no buffer de entrada que bagunçam a próxima entrada de dados. Não é nada gentil também ao encontrar erros no fluxo de entrada, deixando o caractere ofensivo para a próxima leitura. Agora, para ser justo, os problemas obtidos com essa função geralmente são devido a:

x Falta de conhecimento x Uso indevido

x Formatos de string muito complexos

Além de erros no código da própria função. Mas a função é tão complexa que, para novos programadores (e provavelmente para a maioria dos veteranos), essa função é um enigma.

A scanf() é um canivete suíço. Foi projetada para lidar com diversos tipos de entrada, desde simples caracteres até valores em notação científica e complexa. E, por causa disso, o projeto possui falhas em algumas áreas.

Não sei a chave do sucesso, mas a chave do fracasso é tentar agradar a todos.

~Bill Cosby

É como eu me sinto quanto à scanf().

Admito que nunca estudei essa função por completo. Já vi alguns parâmetros sugeridos para suavizar alguns dos problemas, e são, no mínimo, assustadores. Então, aonde essa função é utilizada? Primeiro e mais importante, não a use para caracteres e strings. Os próximos tópicos explicarão o porquê. Já para números, ela não é tão ruim, o que será explorado também.

Mas a falta de conhecimento é o que acontece com a maioria dos programadores. Eles querem ler um caractere do teclado e, logicamente, utilizam o formato %c. Depois se perguntam o porquê de a próxima leitura estar com problemas. Como eles saberiam que, depois do caractere, havia um \n deixado para trás? Eu raramente nunca? vi uma documentação que explicasse esse fato, mas através da experimentação e do pensamento lógico (e um bom debugger), pode-se descobrir o que está acontecendo.

Outro problema é que novos programadores tendem a utilizar esse canivete suíço quando uma simples colher seria suficiente. scanf() é uma função grande! Veja o trabalho executado por ela:

1) Começa a verificar o formato da string 2) Procura por um %

3) Descobre todos os atributos no designador do formato (%7.31f, por exemplo) 4) Lê os caracteres a partir do buffer do teclado até que um caractere não compatível

com o formato é encontrado

5) Converte esses caracteres no formato binário apropriado

(9)

9

É um bocado de trabalho! Lembre-se desses passos ao longo da análise da função scanf(). Eles serão mencionados novamente como Os Passos.

Para examinar o tamanho dela, temos os seguintes programas para verificar o tamanho dos arquivos executáveis.

Programa básico vazio:

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

char c;

return 0; }

Turbo C++ 1.0: 6,177 bytes Code::Blocks 10.05: 24.412 bytes Borland C++ 5.5: 47.104 bytes Visual C++ 6.0: 151.611 bytes

Entrada de caracteres com a getchar()

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

char c;

c = getchar();

return 0; }

Turbo C++ 1.0: 7.197 bytes (1020 bytes a mais, 16% maior) Code::Blocks 10.05: 25.091 bytes (679 bytes a mais, 3% maior) Borland C++ 5.5: 48.128 bytes (1024 bytes a mais, 2% maior) Visual C++ 6.0: 155,707 bytes (4096 bytes a mais, 3% maior)

Entrada de caractere com a scanf():

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

char c;

scanf("%c", &c);

return 0; }

Turbo C++ 1.0: 9560 bytes ( 3383 bytes a mais, 55% maior) Code::Blocks 10.05: 25.125 bytes (713 bytes a mais, 3% maior) Borland C++ 5.5: 61,952 bytes (14848 bytes a mais, 32% maior) Visual C++ 6.0: 163,899 bytes (12288 bytes a mais, 8% maior)

(10)

10

scanf() / lendo caracteres e strings

Duas das maiores desvantagens do uso da scanf() para ler um único caractere são:

x A quantidade de trabalho (lembre-se d Os Passos . Essa função executa muitos

passos para fazê-lo. Então por que não utilizar uma função que economiza todo esse trabalho e simplesmente:

i. Lê um caractere do buffer do teclado ii. Retorna o caractere para o programa

É exatamente o que a getchar() faz. Ela opera do mesmo modo que a scanf(), sem todo o processamento extra.

x O tamanho da função em si. Veja os tamanhos no tópico anterior. Ela é 3 ou mais

vezes maior do que a getchar().

Um dos problemas que ambas as funções possuem é que as duas leem 1 caractere, mas a entrada do teclado possui mais de um caractere. Se você digitar a como um caractere de entrada, o buffer terá, na verdade, dois caracteres: o a seguido do \n. Ambas as funções deixarão o \n no buffer para a próxima leitura, e cabe ao programador ler o resto dos caracteres e limpar o buffer para a próxima leitura. Compare esses dois trechos de código que aliviam o problema:

char c, lixo;

// usando getchar()

c = getchar();

while (getchar() != '\n');

// usando scanf()

scanf("%c", &c);

do scanf("%c", &lixo); while (lixo != '\n');

Com a scanf(), Os Passos seriam processados múltiplas vezes. Imagine se fosse digitado abcdef?

Portanto, ler um caractere com a scanf() é como usar um bate-estacas quando tudo o que você precisa é um martelo. Use getchar() no lugar dela. É mais fácil e projetada exatamente para essa tarefa. Apenas lembre-se de limpar o buffer do teclado. Em ambos os casos, o caractere \n será deixado no buffer.

(11)

11

scanf() / números

É aqui onde a função scanf() não trabalha tão mal. Não é maravilhosa, mas também não é tão má. O que ela faz direito é ler o valor inteiro, ignorando espaços em branco e novas linhas também. O que ela faz é bagunçar o programa se algum caractere ilegal for fornecido. Mas, pra falar a verdade, ela diz que isso acontece. Geralmente. A maioria dos programadores não dá atenção.

Assim como os outros formatos, quando a scanf() lê um número, ela para assim que detecta um não-número, deixando espaços e novas linhas no fluxo de entrada. Mas, na próxima leitura, esses caracteres são lidos antes do próximo número ser detectado. Uma boa notícia.

Crie o seguinte programa:

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

int v; // valor lido pela scanf

int r; // valor retornado pela scanf

int e; // contagem de erros

e = 0; // inicialização, para evitar erros

do {

r = scanf("%d", &v);

if (!r) e++; // conta os erros encontrados

printf(" Leu %3d, retornou %d \n", v, r);

} while ((v != 0) && (e < 4));

return 0; }

O programa (loop) é terminado quando 0 for digitado ou quando forem contados quatro erros. A scanf() também retorna um valor inteiro que mostra quantos valores foram convertidos. Então, vamos entrar com os valores:

D:\>scanf-n1 2

Leu 2, retornou 1 45

Leu 45, retornou 1 8 7 34 510

Leu 8, retornou 1 Leu 7, retornou 1 Leu 34, retornou 1 Leu 510, retornou 1 43 0

Leu 43, retornou 1 Leu 0, retornou 1

D:\>

(12)

12

D:\>scanf-n1 23 65.3 17 4.2

Leu 23, retornou 1 Leu 65, retornou 1 Leu 65, retornou 0 Leu 65, retornou 0 Leu 65, retornou 0 Leu 65, retornou 0

D:\>

Vejamos... Ela leu 23 e 65 sem problemas. Mas veja que o código de retorno foi 0, e entrou em loop. Isso aconteceu porque o ponto decimal é um caractere ilegal para o tipo int. Ele é deixado no buffer, e nunca removido, e todas as leituras a partir daí falham, deixando o antigo valor na variável. Faz-se necessário, portanto, após cada chamada do scanf() que não lê caracteres (apenas dados do tipo int, float, etc) uma verificação do valor de retorno para ver se funcionou. Se não, o fluxo de entrada permanecerá contaminado . Seria difícil consertar o erro (o problema poderia ser mais de um caractere), daí é mais fácil abortar o procedimento de entrada e pedir ao usuário para entrar com os dados novamente.

Vamos fazer algumas leves mudanças no programa:

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

int v1,v2,v3,v4; // valores lidos pela scanf

int r; // valor retornado pela scanf

int e; // contagem de erros

e = 0; // inicialização, para evitar erros

do {

r = scanf("%d %d %d %d", &v1, &v2, &v3, &v4);

if (r != 4) e++; // conta os erros encontrados

printf(" Leu %3d %3d %3d %3d, retornou %d \n\n",

v1, v2, v3, v4, r);

} while ((v1*v2*v3*v4 != 0) && (e < 4));

return 0; }

Agora, o programa lê 4 valores ints antes de continuar, e se qualquer uma deles for 0, o programa termina:

D:\>scanf-n2 1 2 3 4

Leu 1 2 3 4, retornou 4

23 34 45 56

Leu 23 34 45 56, retornou 4

23 45 65 76

Leu 23 45 65 76, retornou 4

23 5 0 7

Leu 23 5 0 7, retornou 4

(13)

13

Observe que, na terceira tentativa, o programa esperou que os quatro valores fossem digitados. Observe também que o valor de retorno foi 4. Esse valor mostra a quantidade de valores lidos.

D:\>scanf-n2 23 65 23.1 9

Leu 23 65 23 -2, retornou 3

Leu 23 65 23 -2, retornou 0

Leu 23 65 23 -2, retornou 0

Leu 23 65 23 -2, retornou 0

D:\>

Neste teste, o terceiro valor contém um ponto decimal. Até o decimal, todos os valores foram lidos e convertidos, depois veio o erro. Isso mostra que apenas 3 valores foram lidos do buffer de entrada, mas o 4º não. Apenas 3 das quatro variáveis possuem dados válidos.

Ao menos a scanf() mostrará o que fez, e dirá até onde foi. Mas ainda há uma coisa. E quanto a isso:

D:\>scanf-n2 23 98 54 67.5

Leu 23 98 54 67, retornou 4

Leu 23 98 54 67, retornou 0

Leu 23 98 54 67, retornou 0

Leu 23 98 54 67, retornou 0

Leu 23 98 54 67, retornou 0

D:\>

A primeira leitura parece ter funcionado! Não houve erros até processar o 67.5 como 67, e nossos dados não estão corrompidos. Então, neste caso, o valor de retorno é inútil como indicativo de erro. E porque, depois da primeira leitura, o erro ainda está sendo exibido no buffer de entrada (não está no programa ainda), não teremos ideia se:

 Há um problema (até a próxima leitura)  Qual é o problema (após a próxima leitura)

Até que seja encontrada outra maneira de ler o resto do buffer.

(14)

14

Conclui-se que a melhor maneira de criar uma entrada à prova de balas é verificar manualmente a entrada do teclado do usuário. Supere isso, e use funções de conversão testadas e verdadeiras, não se apoiando na scanf() para fazer esse trabalho.

A scanf() não foi projetada para código útil, e sim para testar o funcionamento das outras partes do programa. Ela substituirá as rotinas de entrada complicadas para que os outros módulos possam ser exercitados e aperfeiçoados. Só depois é que a scanf() é substituída pelas reais funções de entrada, que obterão os dados a partir de um arquivo, dispositivo ou qualquer lugar onde estejam armazenados.

E se os dados de entrada forem, de fato, fornecidos pelo usuário, os módulos de entrada provavelmente serão compostas de outros módulos que verificam os bits e pedaços da string fornecida (provavelmente lida pela fgets()), podendo lidar com diferentes erros do usuário sem entrar em loop ou parar o programa.

Ao ler qualquer coisa, a scanf() pulará qualquer espaço em branco. Depois selecionará todos os caracteres que forem de acordo com o formato especificado, e parará ao alcançar o primeiro caractere inválido, deixando-o para a próxima entrada. No caso das strings, caracteres inválidos são todos os espaços em branco, definidos como:

Hex Dec Ctrl Nome 0x09 9 ^I Tab

0x0A 10 ^J Line Feed 0x0B 11 ^K Verticle Feed 0x0C 12 ^L Form Feed

0x0D 13 ^M Carriage Return 0x1A 26 ^Z End of File (EOF) 0x20 32 Espaço

A entrada de caracteres é a pior, uma vez que serão lidos quaisquer caracteres digitados. Consequentemente, após a leitura feita pela scanf(), seguindo o caractere lido estará qualquer coisa deixada, e não o que você esperava. Então,

 %d funciona bem  %f funciona bem

x %s ignora espaços em branco, lendo apenas uma palavra x %c raramente funciona como o esperado

A leitura de números funciona, e de palavras também, mas não a de frases. E tentar ler um caractere é como pedir problemas sem uma função especial.

Eis uma função que pode ser utilizada para ler um único caractere. Seria uma substituta da

scanf(“%c”, &ch), para se ter certeza que foi lido o valor esperado:

int getCharacter() {

int ch; // define o caractere

do ch = getchar(); // entra em loop até ler // um caractere adequado

while ((ch == 0x20) || // verifica espaços

((ch >= 0x09) && // verifica outros tipos

(ch <= 0x0D)) // de espaço

);

(15)

15

Ela continuará lendo até que um caractere não espaço seja lido, limpando efetivamente o buffer dos possíveis restos deixados pelas chamadas anteriores da scanf().

Já a função abaixo aceita também caracteres não espaço imprimíveis mais ’til :

int getCharacter() {

int ch; // define o caractere

do ch = getchar(); // entra em loop até ler

// um caractere adequado

while ((ch <= 0x20) || // verifica antes dos espaços

(ch >= 0x7F) // verifica caracteres depois

); // do último da tabela ASCII

return ch; }

Agora não há mais desculpas para dizer minha scanf() não funciona! . Você sabe o porquê. Evite-a, exceto para testes, principalmente para a entrada de caracteres.

void main()

main() é uma função do tipo int, e deve ser especificada como tal. Basicamente, por três motivos:

 Porque o padrão determina que assim o seja

 Porque as rotinas de inicialização que executam a main() podem assumir que ela retornará um valor que será usado posteriormente. Se isso não acontecer, a rotina de execução poderá ser comprometida, causando o fim prematuro do programa.

 Porque, caso não o faça, provavelmente será retornado um valor aleatório para o sistema. E isso é ruim, pois se alguém quiser verificar se seu programa funcionou ou não, ou se for usá-lo em outra aplicação, não poderá saber se o valor não-nulo retornado corresponde a um erro de execução.

Referências

Documentos relacionados

Chora Peito Chora Joao Bosco e Vinicius 000 / 001.. Chão De Giz Camila e

De seguida, vamos adaptar a nossa demonstrac¸ ˜ao da f ´ormula de M ¨untz, partindo de outras transformadas aritm ´eticas diferentes da transformada de M ¨obius, para dedu-

O objetivo, tal como visto anteriormente, era traçar um modelo de quadro descritivo para a emissão da ‘Opinião Desfavorável’ em português do Brasil que pudesse servir de suporte

6 Consideraremos que a narrativa de Lewis Carroll oscila ficcionalmente entre o maravilhoso e o fantástico, chegando mesmo a sugerir-se com aspectos do estranho,

Com o objetivo de compreender como se efetivou a participação das educadoras - Maria Zuíla e Silva Moraes; Minerva Diaz de Sá Barreto - na criação dos diversos

1) As Operadoras obrigam-se a cumprir o que foi declarado na CPI da Telefonia Móvel em Santa Catarina quanto à execução de investimentos previstos nos termos do Plano de Melhoria

A prova do ENADE/2011, aplicada aos estudantes da Área de Tecnologia em Redes de Computadores, com duração total de 4 horas, apresentou questões discursivas e de múltipla

3259 21 Animação e dinamização de actividades lúdico-expressivas - expressão musical 50 3260 22 Animação e dinamização de actividades lúdico-expressivas -