Dúvidas e erros no EP2

Dúvidas e erros no EP2

por José Coelho de Pina -
Número de respostas: 12

Salve,

Criei este fórum para que vocês coloquem aqui as dúvidas e erros no EP2 que não conseguiram
resolver, ou mesmo aqueles que resolveram mas não entederam o que mudou para funcionar.

Começo este tópico reproduzindo uma mensagem que postei no tópico "função setPixel" criado pela Thais.


O programa fonte do EP2 já foi disponibilizado, mas seria legal que as pessoas colocassem aqui códigos com problemas.
Por exemplo, tem gente que fez o seguinte no main

    Linha *cab;
     [...]

    cab = mallocSafe(sizeof(Linha));

    leMondrian(argv[1], &altura, &largura, cab);

Aparentemente dava problema no Windows, mas não dava no linux.
O código tem de fato um erro, o certo é:

    Linha *cab;
     [...]

    cab = mallocSafe(sizeof(Linha));
    cab->prox = NULL; /* o mesmo que (*cab).prox = NULL; */
    leMondrian(argv[1], &altura, &largura, cab);

como está descrito nas notas de aula  (inicialização de listas encadeadas com cabeça).
Infelizmente, parece que quando a struct *cab era
alocada no Linux o campo prox estava com NULL (==0),
ou seja (*cab).prox == NULL (o mesmo que cab->prox == NULL).

Nesse caso o problema não era no Windows.
Uma inicíalização que não está especificada na linguagem mas era feita no linux,
fazia com que o programa, apesar de errado, "funcionasse". . .

Mora da história: vejam na notas de aula como deve ser feita a inicialização de uma lista
encadeada com e sem cabeça.

E ai? Mais algume tem um trecho de código para darmos uma olhada aqui?

Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

O main que fizemos e que gera um monte de imagens, uma para testar cada
passo/fase durante o desenvolvimento do EP2 está logo abaixo. É necessário colocar o seguinte include no início do programa.

#include  <string.h> 

/* 
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
  5  M A I N 

////////////////////////////////////////////////////////////////////// 
*/

int main(int argc, char** argv)
{
  Imagem *tela;   /* apontador para a imagem que sera criada */
  int nLins;      /* numero de linhas da tela/imagem */
  int nCols;      /* numero de colunas da tela/imagem */

  Linha  llinhas; /* cabeça da lista de linhas */
  Linha *lin;     /* ponteiro para uma linha */

  CelRegiao cabecas[MAX_REGIOES];

  int nRegioes;   /* numero de regioes */
  int k;          /* contador de regioes */

  char *nomeImagem; /* nomes das imagens intermediarias salvas */

  Imagem *R;  /* intensidade de vermelho */
  Imagem *G;  /* intensidade de verde */
  Imagem *B;  /* intensidade de azul */

  float cores[12][3]=
    {
      {1.0, 0.0, 0.0}, /*  0 red     */
      {0.0, 1.0, 0.0}, /*  1 green   */
      {0.0, 0.0, 1.0}, /*  2 blue    */
      {1.0, 1.0, 0.0}, /*  3 yellow  */
      {1.0, 0.0, 1.0}, /*  4 magenta */
      {0.0, 0.0, 0.0}, /*  5 black   */
      {0.2, 0.7, 0.4}, /*  6 green 2 */
      {0.7, 0.4, 0.2}, /*  7 marrom  */
      {0.0, 1.0, 1.0}, /*  8 cyan    */
      {0.5, 0.5, 0.5}, /*  9 cinza   */
      {1.0, 1.0, 1.0}, /* 10 branco  */
      {0.0, 0.0, 0.0}  /* 11 preto   */
    };


  if (argc < 3) 
    {
      mostreUso(argv[0]);
      exit(EXIT_FAILURE);
    }

  nomeImagem = malloc(1024 * sizeof(char));
  strcpy(nomeImagem,argv[2]);

  /* 0 leia a comfiguracao do mondrian */
  llinhas.prox = NULL; /* inicialize a cabeca */
  leMondrian(argv[1],&nLins,&nCols,&llinhas);
  
  /* 1 crie a tela (imagem) em que desenharemos o mondrian */
  tela = criaImagem(nLins,nCols); 

  salvaImagem(strcat(nomeImagem,"-tela"),tela);

  /* 2 pinte a tela de COR_FUNDO */ 
  pintaImagem(tela,COR_FUNDO); 

  salvaImagem(strcat(nomeImagem,"-fundo"),tela);

  /* 3 pinte a borda */
  desenhaBorda(tela,COR_BORDA);

  salvaImagem(strcat(nomeImagem,"-borda"),tela);

  /* 4 percorra a lista encadeada de linhas e desenhe as linhas 
   *   na tela 
   */
  lin = llinhas.prox;
  while (lin != NULL) 
    {
      printf("lin: tipo='%c' pos=%d, ini=%d, fim=%d\n",
	     lin->tipo, lin->pos,lin->ini,lin->fim);
      desenhaLinha(tela,lin,COR_BORDA);       
      desenhaLinha(tela,lin,COR_BORDA);       
      desenhaLinha(tela,lin,COR_BORDA);       
      desenhaLinha(tela,lin,COR_BORDA);       
      lin = lin->prox;
    }

  salvaImagem(strcat(nomeImagem,"-linhas"),tela);

  nRegioes = segmentaRegioes(tela,cabecas);

  salvaImagem(strcat(nomeImagem,"-regioes"),tela);

  R = criaImagem(nLins,nCols); 
  G = criaImagem(nLins,nCols); 
  B = criaImagem(nLins,nCols); 

  copiaImagem(R,tela);
  copiaImagem(G,tela);
  copiaImagem(B,tela);

  for (k = 0; k < nRegioes; k++) 
    {
      pintaRegiao(&cabecas[k].cabpix,R,G,B,cores[k%12]);
    }

  salvaImagemRGB(argv[2],R,G,B);

  return 0;
}

/* 
\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
  8  IMPLEMENTACAO DE FUNCOES EXTRAS

////////////////////////////////////////////////////////////////////// 
*/


void mostreUso (char *nomePrograma)
{
  fprintf(stderr,"%s: Uso \n"
	  "meu_prompt> %s  \n",
	  nomePrograma, nomePrograma);
}
Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

Alguém pode me dizer qual o erro no trecho de código a seguir?

  char *nomeArquivo;
  
  nomeArquivo = mallocSafe(128*sizeof(char));
  nomeArquivo = argv[2];
Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

Niguém ainda se aventurou a responder a pergunta acima.

Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por Carlos Eduardo Elmadjian -

O problema não está no fato de você alocar um endereço para o ponteiro nomeArquivo e depois sobrescrever essa alocação por outro endereço?

Usando o header string.h e a função strcpy() você poderia copiar o conteúdo do endereço de uma string para outra sem esse problema.

Tá certo isso?

Em resposta à Carlos Eduardo Elmadjian

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

Oi Eduardo,

Sim, você tem razão.
Foi alocado 128 bytes para serem usados e a atribuição

  nomeArquivo = argv[2];

copia para nomeArquivo o valor da variável argv[2] que é um mero endereço.
Não estamos fazendo copia de strings (=vetor de caracteres + '\0' no final).
Com isto perdemos a localização dos 128 bytes alocados (=memory leaking =vazamento de memória) O certo seria

  
  char *nomeArquivo;
  
  nomeArquivo = mallocSafe(128*sizeof(char));
  strcpy(nomeArquivo,argv[2]);

"A memory leak, in computer science (or leakage, in this context), occurs when a computer program consumes memory but is unable to release it back to the operating system..."

http://en.wikipedia.org/wiki/Memory_leak


Vejam como é facil escrever uma função strcpy.

 char *strcpy(char *dest, const char *src)
 {
   int i;

   for (i = 0; src[i] != '\0'; i++)
     dest[i] = src[i];

   dest[i] = '\0';

return dest; /* linha acrescentada aqui depois da msg do Renato */
}
Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por Renato Cordeiro Ferreira -

Neste caso não precisaríamos adicioar um 'return' na função, visto que ela é do tipo char * ? E além disso, quais outras funções posso encontrar na string.h? Já ouvi falar da strcmp para fazer comparações. 

Além disso, as implementações dessas funções são no geral simples como esta ou envolvem algo mais complexo e eficiente?

Em resposta à Renato Cordeiro Ferreira

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

Neste caso não precisaríamos adicioar um 'return' na função, visto que ela é do tipo char * ? 

Opa! Você tem razão. Acrescentei o "return dest;"

Man page do strcpy diz:

...
The strcpy() and strncpy() functions return a pointer to the destination string dest.

Só de bricadeira, na prompt, digite:

man string
man strcpy
Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por Renato Cordeiro Ferreira -

A pessoa alocou um espaço na memória para ser apontada pelo ponteiro 'nomeArquivo'. Na linha 3, fizeram uma atribuição do ponteiro para um vetor, que na forma argv[2] está se referenciando ao valor (uma string, no caso) dele, e não para o endereço de memória. Seria mais correto colocar:

nomeArquivo = &argv[2]

Mas aproveitando a oportunidade: seria possível colocar "nomeArquivo = (v+2)" para se referenciar à região da memória 2 bytes (tamanho do char) para frente do primeiro elemento (e nesse caso, pegar a região da memória correspondente a argv[2]) ?

Em resposta à Renato Cordeiro Ferreira

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

Oi Renato,

O certo seria

 nomeArquivo = argv[2];

argv (tipo char**)= endereço de um vetor de strings = endereço de um vetor em que cada posição aponta para um string
argv[2] (tipo char*)= endereço de um string
&argv[2] (tipo char**) = argv+2 = endereço da posição de índice 2 do vetor argv.

Mas aproveitando a oportunidade: seria possível colocar "nomeArquivo = (v+2)" para se referenciar à região da memória 2 bytes (tamanho do char) para frente do primeiro elemento (e nesse caso, pegar a região da memória correspondente a argv[2]) ?

Sim, é possível.
"argv[2]" e "argv+2" são equivalentes (= são o mesmo valor = são o mesmo endereço na memória).

Em resposta à José Coelho de Pina

Re: Dúvidas e erros no EP2

por Evandro Sanches -

Uhhh, se entendi direito...

Como o programador pensou em criar uma string a partir de um "vetor" de char, ele atribuiu diretamento o conteudo de uma variavel, argv[2], para a outra, nomeArquivo.

Da maneira que foi escrito no programa, o ponteiro nomeArquivo vai realmente apontar para o local na memória que está o conteudo desejado, mas a alocação anterior, com o malloc, será perdida. Também ao tentar mudar a variável nomeArquivo[0]= 'c'; estaríamos mudando argv[2][0] = 'c'; ao mesmo tempo.

digamos que nomeArquivo aponte para o local separado por malloc e argv[2]  aponte para o lugar na memoria que esta a string assim:

nomeArquivo ----> |_ _ _ _ _ _ _ _ _ _ _ _| digamos que o endereço seja 8b5e, então nomeArquivo = 8b5e

argv[2] -----> |_ _ _ _ |digamos que o endereço seja 02EF e a string seja "talo\0",

logo argv[2] = 02EF  e  agrv[2][0] = 't' agrv[2][1] = 'a' ...

na linha nomeArquivo = argv[2] o programa  faz a seguinte atribuição

nomeArquivo = argv[2] , mas argv[2] é 02EF então nomeArquivo = 02EF.

Viu aponta pro mesmo lugar e perdemos o local reservado por malloc.

Depois desta atribuição ao tentar mudar  nomeArquivo[0] ='c', se tentarmos imprimir todo conteudo de argv[2] teremos a palavra "calo".

Não sei se ajudei, mas é o que eu entendi.

Se alguém achar algum erro comenta aí!

ps: para copiar a alternativa seria tirar a linha 3 e copiar conteudo a conteudo, algo como nomeArquivo[0] = argv[2][0]; até o final da string que é '/0' se não me engano.

Abraços

 

Em resposta à Evandro Sanches

Re: Dúvidas e erros no EP2

por José Coelho de Pina -

Oi Evandro,

Da maneira que foi escrito no programa, o ponteiro nomeArquivo vai realmente apontar para o local na memória que está o conteudo desejado, mas a alocação anterior, com o malloc, será perdida. Também ao tentar mudar a variável nomeArquivo[0]= 'c'; estaríamos mudando argv[2][0] = 'c'; ao mesmo tempo.
[...]

É isso ai.