Ir ao conteúdo
  • Comunicados

    • Gabriel Torres

      Seja um moderador do Clube do Hardware!   12-02-2016

      Prezados membros do Clube do Hardware, Está aberto o processo de seleção de novos moderadores para diversos setores ou áreas do Clube do Hardware. Os requisitos são:   Pelo menos 500 posts e um ano de cadastro; Boa frequência de participação; Ser respeitoso, cordial e educado com os demais membros; Ter bom nível de português; Ter razoável conhecimento da área em que pretende atuar; Saber trabalhar em equipe (com os moderadores, coordenadores e administradores).   Os interessados deverão enviar uma mensagem privada para o usuário @Equipe Clube do Hardware com o título "Candidato a moderador". A mensagem deverá conter respostas às perguntas abaixo:   Qual o seu nome completo? Qual sua data de nascimento? Qual sua formação/profissão? Já atuou como moderador em algo outro fórum, se sim, qual? De forma sucinta, explique o porquê de querer ser moderador do fórum e conte-nos um pouco sobre você.   OBS: Não se trata de função remunerada. Todos que fazem parte do staff são voluntários.
    • DiF

      Poste seus códigos corretamente!   21-05-2016

      Prezados membros do Fórum do Clube do Hardware, O Fórum oferece um recurso chamado CODE, onde o ícone no painel do editor é  <>     O uso deste recurso é  imprescindível para uma melhor leitura, manter a organização, diferenciar de texto comum e principalmente evitar que os compiladores e IDEs acusem erro ao colar um código copiado daqui. Portanto convido-lhes para ler as instruções de como usar este recurso CODE neste tópico:  
Pedrohtico

Programa em C: malloc() dentro de outra função

Recommended Posts

Olá, eu estou fazendo um programa que lê imagens PPM, mas estou tendo um problema. A seguinte função lê a imagem e guarda dentro de variáveis criadas na main. O problema está no ponteiro de ponteiro, ou seja, a matriz "pixel":

typedef struct{
    int r, g, b;
}matriz_pixel;

void ler_imagem(matriz_pixel **pixel, int *largura, int *altura, int *max, char *code, char *argv[]){  // Lê os primeiros valores do arquivo PPM

    int i, j;
    FILE *imagem;
    char nome[101];

    scanf("%s", nome);

    imagem = fopen(nome, "r");   // Abre o arquivo da imagem PPm
    if(imagem == NULL){
        printf("Erro na abertura do arquivo!\n");
        exit(1);
    }

    fscanf(imagem, "%s", code);
    fscanf(imagem, "%d", largura);
    fscanf(imagem, "%d", altura);
    fscanf(imagem, "%d", max);

    pixel = (matriz_pixel**) malloc (*altura * sizeof(matriz_pixel*));  // Aloca a matriz de struct que guardará os valores RGB
    if(pixel == NULL){
        printf("Erro de memoria!\n");
        exit(1);
    }
    for(i = 0; i < *altura; i++){
        pixel[i] = (matriz_pixel*) malloc (*largura * sizeof(matriz_pixel));
        if(pixel[i] == NULL){
            printf("Erro de memoria!\n");
            exit(1);
        }
    }

    printf("PIXEL 1: R: %d G: %d B: %d\n", pixel[0][0].r, pixel[0][0].g, pixel[0][0].b);

    for(i = 0; i < *altura; i++){
        for(j = 0; j < *largura; j++){
            fscanf(imagem, "%d", &pixel[i][j].r);
            fscanf(imagem, "%d", &pixel[i][j].g);
            fscanf(imagem, "%d", &pixel[i][j].b);
        }
    }

    printf("PIXEL 1: R: %d G: %d B: %d\n", pixel[0][0].r, pixel[0][0].g, pixel[0][0].b);

    fclose(imagem);
}

Essa segunda função eu fiz apenas para saber se os valores estavam sendo guardados corretamente:

 

void escrever_variaveis(matriz_pixel **pixel, int largura, int altura, int max, char *code){

    printf("Code: %s\n", code);
    printf("Largura: %d\n", largura);
    printf("Altura: %d\n", altura);
    printf("Tonalidade max: %d\n", max);
    printf("PIXEL 1: R: %d G: %d B: %d\n", pixel[0][0].r, pixel[0][0].g, pixel[0][0].b);

}

E então, tem a função main(), que apenas inicializa as variáveis e chama as duas funções anteriores:

 

int main(int argc, char *argv[]){

    int i, j;

    matriz_pixel **pixel;            // Variáveis dos atributos da imagem PPM
    int max, largura, altura;
    char code[3];

    ler_imagem(pixel, &largura, &altura, &max, code, argv);

    escrever_variaveis(pixel, largura, altura, max, code);

    free(pixel);
    for(i = 0; i < altura; i++){
        free(pixel[i]);
    }

    return 0;
}

Eu coloquei um printf para mostrar o valor RGB guardado dentro do primeiro pixel da matrix de struct "pixel". Esse printf foi colocado 3 vezes. A primeira vez ela printa o lixo logo depois de alocar a matriz pixel. Na segunda o valor após ler a imagem PPM. E a terceira, que fica na segunda função, deveria mostrar os mesmos valores que a segunda vez. Entretanto, o cmd dá mal funcionamento nesse printf e encerra o programa. Eu imagino que seja algum problema com a alocação dentro da função, como por exemplo se ela desalocasse quando acabasse a função, pois os valores são printados na segunda vez de forma correta. Apenas a terceira vez dá problema. Agradeço a ajuda desde já.

 

Edit 1: Ao retirar o terceiro printf, a variável (matriz_pixel **pixel) no nome da função e (pixel) na chamada dela, ela contina dando esse erro. Então eu descobri que tanto esse terceiro printf quanto o free(pixel) no final do main estão dando esse erro (se eu retirar essas duas partes, o programa funciona normalmente), o que me faz acreditar que é justamente algum problema com a alocação da matriz "pixel" dentro da função "ler_imagem".

 

Edit 2: Se eu inicializo a variável "matriz_pixel pixel**" globalmente, o programa para de dar erro e funciona normalmente.

Editado por Pedrohtico
  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites
  • Autor do tópico
  • Aqui tem 5 imagens PPM, pode tentar com qualquer uma delas. Na segunda, ela dá erro de memória, ou seja, já tem algum problema com a alocação. Na primeira, terceira, quarta e quinta imagem, dá esse problema descrito anteriormente.

     

    Imagens_PPM.rar

    Editado por Pedrohtico

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Todo os erros do seu programa se devem a causa de não entender corretamente o uso dos ponteiros.
    você deve pensar que um int* é a direção de um int, e que um int** é a direção de um int*, pois no caso dos ponteiros duplos triplos, etc, trata-se de ponteiro a ponteiros. Siga o exemplo:

    int x = 10;

    int* p = &x;

    int** q = &p;

    Seguindo essa lógica você deveria passar um ponteiro duplo para a função porém sua função deveria ter um ponteiro triplo. Por exemplo em main quando você invoca a função:

    ler_imagem(pixel, &largura, &altura, &max, code, argv);

     

    você está passando pixel que é um duplo ponteiro porém a função está declarada como:
    void ler_imagem(matriz_pixel **pixel, int *largura, int *altura, int *max, char *code, char *argv[])


    Se estiver fazendo isso dentro do main equivaleria a fazer:
    matriz_pixel **pixel1;
    matriz_pixel **pixel2;

    pixel1 = pixel2;

    Da para ver algo errado aí?
    Claro! você está fazendo pixel1 apontar ao valor de pixel2 não a pixel2. O problema é que pixel2 aponta a LIXO, pois pixel2 NÃO APONTA NADA.

    Vamos fazer uma prova:

    Primeiro faremos passando o resultado para a função:

    #include<stdio.h>
    
    void reserva( int **m2 ){
        printf("Na funcao: %d\n", m2  );
     
    }
    
    int main(){
        int**m1;
        printf("Em main: %d\n", m1);
        
        reserva(m1);
        
        return 0;
    }

    O importante não é o que imprime, e sim o valor que se repete.
    Agora vamos ver a mesma coisa em main:

    #include<stdio.h>
    
    int main(){
        int **m1;
        int **m2;
        printf("m1: %d\n", m1);
        printf("m2: %d\n", m2);
    
        return 0;
    }

    Se repete verdade? Claro que sim! Ambas formas são equivalentes. Qual problema vemos nisso? Pois existe muita diferença entre a direção de um ponteiro e o valor que guarda um ponteiro. A direção de um ponteiro é a direção de si mesmo, e o valor que guarda um ponteiro é a direção de outro objeto apontado já seja esse lixo na memoria, null, etc.

    Quando eu tenho:
    int **m;

    Se eu quero saber a direção do objeto m[0][0] eu faço &m[0][0] pondo o & adiante, porém existe uma notação mais fácil para isso, e é fazendo simplesmente m. COMO? A forma &m[0][0] aponta ao objeto da linha 0 e coluna 0, porém é exatamente a mesma coisa fazer m, sem nada. Veja o exemplo:

     

    #include<stdio.h>
    
    int main(){
        int m[3][3];
        printf("Forma normal: %d\n", &m[0][0]);
        
        printf("Forma semelhante porém simplificada: %d\n", m);
        
        printf("Ambas imprimem a mesma coisa\n");    
    
        return 0;
    }


    Então você ta passando a direção zero zero da matriz para a função, e ta guardando a direção que malloc retorna onde começa o array em vez de guardar no ponteiro. E pior ainda é que como falei esse ponteiro ainda nem é uma matriz, ele aponta um lugar aleatório da memória, pois quando você declara o ponteiro em main você nem dá um valor de nullo para ele, ou seja ela aponta a qualquer porcaria na memória e nem é um array2d ainda.
    x-x-everywhere-bugs-bugs-everywhere.jpg


    O que você quer é modificar o ponteiro e não o que aponta o ponteiro por isso para manipular um ponteiro duplo você precisa de um ponteiro triplo, e não há nada mais que falar.

     

    Neste tópico você pode encontrar o que anda buscando que é a forma correta de reservar memória para uma matriz ao passar ponteiro:
    https://stackoverflow.com/questions/15062718/allocate-memory-2d-array-in-function-c/15062765#15062765

     

    Basicamente existe 2 formas, a primeira aloca a memória de forma aleatória, é dizer, malloc vai por o seu array espalhado pela memória, ou seja de forma fragmentada. O problema da memória fragmentada é que a memória fica pegando saltos para poder ler o array, isso traduz em perda de tempo, um programa mais lento. Ao se tratar de um programa que manipula imagem lhe interessa a segunda forma que é reservar memória em um só bloco ou seja a matriz será continua.

    Deixo claro que a forma de liberar memória também é importante e está também nessa pagina.
    Vamos deixar aqui a forma de se fazer a coisa corretamente, cabe a você adapta-lo ao seu projeto.

    A primeira forma é essa que vou por a continuação, é a forma na qual malloc reserva memória de forma Não contigua. A vantagem disso é que o sistema vai encontrar memória mais facilmente pois vai usar a memória mais eficientemente desde o ponto de vista de que aproveita pequenos pedaços que estão espalhados pela ram, encontrar um pedaço grande pode não ser possível, ou pelo menos mais difícil, porém isso não é do todo muito certo pois hoje em dia os computadores tem de 2 a mais gigas, mais que suficiente para alojar qualquer imagem.

    Reserva de memória forma não contigua:

    void allocate_mem(int*** arr, int n, int m){
      *arr = (int**)malloc(n*sizeof(int*));
      for(int i=0; i<n; i++)
        (*arr)[i] = (int*)malloc(m*sizeof(int));
    } 

    Liberar memória de forma não contigua:

    void deallocate_mem(int*** arr, int n){
        for (int i = 0; i < n; i++)
            free((*arr)[i]);
        free(*arr); 
    }


    Agora veremos como reservar de forma contigua a memória:

    int* allocate_mem(int*** arr, int n, int m){
      *arr = (int**)malloc(n * sizeof(int*));
      int *arr_data = malloc( n * m * sizeof(int));
      for(int i=0; i<n; i++)
         (*arr)[i] = arr_data + i * m ;
      return arr_data; //free point
    } 


    E por último como liberar a memória de forma contigua:

    void deallocate_mem(int*** arr, int* arr_data){
        free(arr_data);
        free(*arr);
    }


    Como falei se você quer manipular um ponteiro duplo você precisa de um ponteiro triplo, se manipular um ponteiro triplo você precisa de um quádruplo, e assim por diante.

    Agora falando desde o ponto de vista sobre eficiência no seu programa, saiba que um vetor é muito mais rápido que uma matriz pois no caso da matriz a memória pegaria muitos saltos no caso das matrizes, coisa que como já falei pode ser melhorado através de certas técnicas. E não falo qualquer técnicas não to falando em técnicas usadas por programas profissionais tipo:
    int linhas = 10;
    int colunas = 10;
    int array[ linhas*colunas ];

    Nesse caso você teria um vetor porém funcionaria como matriz mas com a eficiência de um vetor. O mesmo poderia ser feito com malloc:
    int *array = malloc( linhas*colunas*sizeof(int) );

     

    Talvez você se pergunte como faria para manipular isso com um for:

    int i, z;
    for ( i = 0, i < 10; i++ ) {
        for ( z = 0; z < 10; z++ ) {
            printf ( "%d", array[ i * z ] );
        }
    }

    Como falei essa forma tem a funcionalidade de uma matriz porém trata-se de um vetor, muito mais rápido na hora de percorrer cada casinha, mais fácil de reservar memória e liberar, somente são vantagens. :lol:

    Saiba também que as matrizes podem ser tratadas como vetor:
     

    #include<stdio.h>
    
    int main(){
        
        int m[3][3]={
            { 1,2,3 },
            { 4,5,6 },
            { 7,8,9}
        };
        int i;
        for ( i=0; i<3*3 ; i++ ) {
            printf("%d, ", m[0][i]);
        }
    
        return 0;
    }

    Como você pode ver com um só for passo por todas as casinhas da matriz :eek:. Não sei se funciona com matrizes dinâmicas, porém certamente se funciona será com a forma de reservar dinamicamente de forma contigua como expliquei antes, pois da forma não contigua a matriz estará espalhada pela memória e não funcionaria, pois esse tipo de laço só funciona por que a matriz é alojada na memória como um array ja que a memória na pilha é alojada de forma contigua, caso contrario não funcionaria. Se quiser saber mais sobre o tema pergunte. :thumbsup:

    Bom.. espero ter ajudado, faça suas provas, tome seu tempo, consulte o google, pergunte a seu professor... :atirador:

    Sorte!

    • Curtir 2

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
  • Autor do tópico
  • Então vangodp, ajudou bastante e acho que entendi, mas não está dando certo na prática:

     

    Eu mudei para um vetor, como você mostrou ser mais prático (e realmente vai ser, em outras funções que vou utilizar no programa) e, pela lógica, quando eu passasse para a função, a função deveria receber um **pixel, já que foi criado um *pixel. Ficou dessa forma:

     

    void ler_imagem(matriz_pixel **pixel, int *largura, int *altura, int *max, char *code, char *argv[]){  // Lê os primeiros valores do arquivo PPM
    
        int i, j;
        FILE *imagem;
        char nome[101];
    
        scanf("%s", nome);
    
        imagem = fopen(nome, "r");   // Abre o arquivo da imagem PPM
        if(imagem == NULL){
            printf("Erro na abertura do arquivo!\n");
            exit(1);
        }
    
        fscanf(imagem, "%s", code);
        fscanf(imagem, "%d", largura);
        fscanf(imagem, "%d", altura);
        fscanf(imagem, "%d", max);
    
        *pixel = (matriz_pixel*) malloc((*largura)*(*altura)*sizeof(matriz_pixel));
        if(*pixel == NULL){
            printf("Erro de memoria!\n");
            exit(1);
        }
    
        printf("PIXEL 1: R: %d G: %d B: %d\n", pixel[0]->r, pixel[0]->g, pixel[0]->b);
    
        for(i = 0; i < *altura; i++){
            for(j = 0; j < *largura; j++){
                fscanf(imagem, "%d", &pixel[(*altura)*i + j]->r);
                fscanf(imagem, "%d", &pixel[(*altura)*i + j]->g);
                fscanf(imagem, "%d", &pixel[(*altura)*i + j]->b);
                printf("PIXEL 1: R: %d G: %d B: %d  (%d) (%d)\n", pixel[(*altura)*i + j]->r, pixel[(*altura)*i + j]->g, pixel[(*altura)*i + j]->b, i, j);
            }
        }
    
        printf("PIXEL 1: R: %d G: %d B: %d\n", pixel[0]->r, pixel[0]->g, pixel[0]->b);
    
        fclose(imagem);
    }
    
    int main(int argc, char *argv[]){
    
        int i, j; // Variáveis auxiliares
    
        matriz_pixel *pixel;
        int max, largura, altura; // Variáveis dos atributos da imagem PPM
        char code[3];
    
        ler_imagem(&pixel, &largura, &altura, &max, code, argv);
    
        escrever_variaveis(&pixel, largura, altura, max, code);
    
        free(pixel);
    
        return 0;
    }

     

    O que acontece agora é que ele não está lendo os valores do arquivo e guardando nas variáveis. Na verdade, quando executa, ele lê os três primeiros valores, ou seja, o primeiro pixel, sendo os valores R, G, B, e guarda na primeira posição do vetor, que contém a struct matriz_pixel. E logo depois ele para de funcionar. Então eu creio que é algum problema com a alocação talvez, já que ele guarda os valores apenas na posição 0 do vetor e não nas outras.

     

    P.S.: Quando você falou que para manipular o vetor, teria que ser [i*j], acredito eu que teria que ser [10*i + j]. Pois apenas multiplicando o valor de i*j, você teria um problema na linha e na coluna 0, onde todos os valores dariam na posição 0 do vetor.

     

    Edit 1: Eu testei guardando apenas na posição 0 do vetor e realmente está funcionando desse jeito, ou seja, os valores não estão sendo guardados apenas dentro da função, mas posso usá-los em outras funções também. O problema é que ele está alocando apenas a posição 0 do vetor e não as demais.

    Editado por Pedrohtico

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Abrir essa bagaça é tão simples como fazer isso

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    typedef struct Dados {
        char tipo[3];
        int nPixels;
        int nLinhas;
        int tomMax;
    } Dados;
    
    typedef struct Pixel {
        int r, g, b;
    } Pixel;
    
    FILE* fileOpen( char* name, char* mode);                    //Abre arquivo  
    void allocate_mem(Pixel*** arr, int nLinhas, int nPixels); //Reserva memoria
    void deallocate_mem(Pixel*** arr, int nLinhas);           //Libera memoria
    
    //Ler imagem
    void lerImagem( FILE *file, Dados *d, Pixel ***pImagem ){
        //Usamos uma só instrução para ler as 3 linhas(ja temos o cabeçalho)
        fscanf ( file, "%s\n%d %d\n%d\n", d->tipo, &d->nPixels, &d->nLinhas, &d->tomMax );
        
        //Alocaremos memoria
        allocate_mem(pImagem,d->nLinhas, d->nPixels);
        
        if (pImagem == NULL) { printf("falhou"); return; }
        
        //Ler arquivo
        int i, j;
        for (i=0; i<d->nLinhas; i++ ) {
            for (j=0; j<d->nPixels ; j++) {
                fscanf(file, "%d %d %d", &(*pImagem)[i][j].r, &(*pImagem)[i][j].g, &(*pImagem)[i][j].b );
            }
        }
    }
    
    
    
    int main() {
        //Uma só linha para abrir o arquivo em main(mais fácil entender)
        FILE* file = fileOpen("1.ppm", "r");
        
        //Se o arquivo abriu chegamos a esse ponto, agora vamos declarar as variaveis que usaremos, não há lógica criar variaveis sem ter um arquivo que ler.
        Dados dados; //Dados do cabeçalho com o tamanho da imagem entre outras coisas. Usaremos para fazer a reserva
        Pixel **imagem=NULL; //Ponteiro que apontará a nossa matriz.
        int i, j;
    
    
        //Ler arquivo
        lerImagem(file, &dados, &imagem);
        
        //mandamos os dados para um arquivo
        fclose(file); //fechamos o ponteiro a arquivo anterior para reaproveitar o ponteiro para um novo arquivo
        
        file = fileOpen("saida.ppm", "w"); //abrimos um novo arquivo em modo escritura
        
        //gravamos o cabeçalho
        fprintf ( file, "%s\n%d %d\n%d\n", dados.tipo, dados.nPixels, dados.nLinhas, dados.tomMax );
        
        //gravamos a matriz
        for (i=0; i<dados.nLinhas; i++ ) {
            for (j=0; j<dados.nPixels ; j++) {
                fprintf(file, "%3d %3d %3d\t",   imagem[i][j].r,  imagem[i][j].g,  imagem[i][j].b);
            }
        }
        
        fclose(file);
        deallocate_mem(&imagem, dados.nLinhas); //Liberar memoria
        return 0;
    }
    
    FILE* fileOpen( char* name, char* mode){
        FILE* file = fopen( name, mode);			
        if ( file == NULL ){
            perror("Erro: ");
            getchar();
            exit(1);							
        }
        return file; 
    }
    
    //Reserva memoria
    void allocate_mem(Pixel*** arr, int nLinhas, int nPixels){
      *arr = (Pixel**)malloc(nLinhas*sizeof(Pixel*));
      for(int i=0; i<nLinhas; i++)
        (*arr)[i] = (Pixel*)malloc(nPixels*sizeof(Pixel));
    }   
    
    //Libera memoria
    void deallocate_mem(Pixel*** arr, int nLinhas){
        for (int i = 0; i < nLinhas; i++)
            free((*arr)[i]);
        free(*arr); 
    }

     

    Como falei... ponteiros triplos ;)
     

    adicionado 0 minutos depois

    Prove abrir o arquivo de saída. Deve ver a mesma imagem que o de entrada.

    Editado por vangodp

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites
  • Autor do tópico
  • Deu certo.

     

    Na verdade eu só mudei essa parte:

     

    for(i = 0; i < *altura; i++){
            for(j = 0; j < *largura; j++){
                fscanf(imagem, "%d", &pixel[(*altura)*i + j]->r);
                fscanf(imagem, "%d", &pixel[(*altura)*i + j]->g);
                fscanf(imagem, "%d", &pixel[(*altura)*i + j]->b);
                printf("PIXEL 1: R: %d G: %d B: %d  (%d) (%d)\n", pixel[(*altura)*i + j]->r, pixel[(*altura)*i + j]->g, pixel[(*altura)*i + j]->b, i, j);
            }
        }

    para isso:

     

    for(i = 0; i < *altura; i++){
            for(j = 0; j < *largura; j++){
                fscanf(imagem, "%d", &(*pixel)[(*altura)*i + j].r);
                fscanf(imagem, "%d", &(*pixel)[(*altura)*i + j].g);
                fscanf(imagem, "%d", &(*pixel)[(*altura)*i + j].b);
                printf("PIXEL 1: R: %d G: %d B: %d  (%d) (%d)\n", (*pixel)[(*altura)*i + j].r, (*pixel)[(*altura)*i + j].g, (*pixel)[(*altura)*i + j].b, i, j);
            }
    }

    E o printf eu mudei do mesmo jeito:

     

    De:
    
    pixel[(*altura)*i + j]->r
    
    Para:
    
    (*pixel)[(*altura)*i + j].r

    E deu certo dessa forma. Antes eu usei o debug e estava dando "Segmentation Fault". Mas pra mim essas 2 formas são equivalentes, não sei o porque da primeira dar erro e a segunda não. 

    De qualquer forma, muito obrigado pela ajuda. :)

    Compartilhar este post


    Link para o post
    Compartilhar em outros sites

    Crie uma conta ou entre para comentar

    Você precisar ser um membro para fazer um comentário






    Sobre o Clube do Hardware

    No ar desde 1996, o Clube do Hardware é uma das maiores, mais antigas e mais respeitadas publicações sobre tecnologia do Brasil. Leia mais

    Direitos autorais

    Não permitimos a cópia ou reprodução do conteúdo do nosso site, fórum, newsletters e redes sociais, mesmo citando-se a fonte. Leia mais

    ×