Dúvida em realação a alocação e endereçamento de matrizes [RESOLVIDO]

1. Dúvida em realação a alocação e endereçamento de matrizes [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 09/06/2018 - 15:02h




  


2. Re: Dúvida em realação a alocação e endereçamento de matrizes

Fernando
phoemur

(usa Debian)

Enviado em 09/06/2018 - 17:27h

Portanto, QUE DIABOS TEM NESSE CÓDIGO?


Quando você faz isso:
int **matrix=(int**)malloc(ROWS*sizeof(int*)); //Alocando as linhas da matriz

for(unsigned int i=0; i<ROWS; i++){

matrix[i]=(int*)malloc(COLUMNS*sizeof(int)); //Alocando as colunas
}


Cada linha da matriz vai ser alocada em um endereço que muito provavelmente não vai ser contíguo ao da linha anterior.
O SO pode dar qualquer endereço a cada malloc que vc chama....

De forma que isso aqui não tem sentido nenhum:
int *ptr=matrix[0];

//Exibindo valores
for(unsigned int i=0; i<(ROWS*COLUMNS); i++){

printf("[%d] ", ptr[i]);
}

Pois você está lendo apenas a primeira linha da matriz e depois saindo pra fora do limite dela, lendo valores aleatórios da memória...
Na versão usando alocação automática (stack) isso dá certo pois quando acaba uma linha obrigatoriamente vai começar a outra pois um array obrigatoriamente tem endereços contiguos de memória.
Mas isso não significa que é uma boa prática de programação mesmo que funcione.

O certo seria algo assim:
    //Exibindo valores
for(unsigned int row=0; row<ROWS; row++){
int* ptr = matrix[row]; // Nem é necessário isso, assumo que vc queira o ponteiro pra outra coisa
for(unsigned int column=0; column<COLUMNS; column++){

printf("[%d] ", ptr[column]);
}
printf("\n");
}


Em resumo: Alocação automática (stack) os elementos vão estar todos um depois do outro. Já na alocação dinâmica (heap) a cada malloc que você chama vai ser obtido um endereço qualquer...




3. Re: Dúvida em realação a alocação e endereçamento de matrizes

Fernando
phoemur

(usa Debian)

Enviado em 09/06/2018 - 17:54h

Agora utilizando os preceitos de POG (Programação Orientada a Gambiarras)

Note que se você utilizar apenas um malloc todos endereços vão ser contíguos, mesmo usando alocação dinâmica.

Aqui um código igual ao seu, que apesar de não ser correto, funciona:


#include <stdio.h>
#include <stdlib.h>

#define ROWS 4
#define COLUMNS 4

int main(void){

// Inicializando com 1 malloc pra tudo
int **matrix=(int**)malloc(sizeof(int*) * ROWS);
matrix[0]=(int*)malloc(sizeof(int) * ROWS * COLUMNS);

for(int i = 0; i < ROWS; i++) // Arrumando os ponteiros
matrix[i] = (*matrix + COLUMNS * i);

//Preenchendo a matriz
for(unsigned int row=0; row<ROWS; row++){

for(unsigned int column=0; column<COLUMNS; column++){

matrix[row][column]=42; //42? Seria isso a resposta para "tudo"?
}
}

int *ptr=matrix[0];

//Exibindo valores
for(unsigned int i=0; i<(ROWS*COLUMNS); i++){
if (i % ROWS == 0)
printf("\n");

printf("[%d] ", ptr[i]);
}


free(matrix[0]);
free(matrix); //Desalocando

return 0;
}


Espero que vc tenha entendido a diferença.
Abração


4. Re: Dúvida em realação a alocação e endereçamento de matrizes

Fernando
phoemur

(usa Debian)

Enviado em 11/06/2018 - 22:03h

Talvez até não tenha nada de errado. Mas não me parece certo... Sei lá, costume é difícil de mudar...

Digamos que você tenha uma matriz[4][4]

O ponteiro começa com matriz[0]...
Você começa imprimindo em matriz[0][0], matriz[0][1], matriz[0][2].... e assim por diante
Quando chega em matriz[0][3] ele depois vai para matriz[0][4] pois está lendo apenas a linha matriz[0].
Porém matriz[0][4] não existe (aqui existe porque eu aloquei, mas no codigo dele não e da forma que geralmente é feito também não) e você estaria lendo pra fora dos limites do array (que tem indices de 0 a 3)
Acontece que por uma coincidência - forçada propositalmente neste caso - o endereço de matriz[1][0] na linha seguinte vai ser o mesmo. Pois os endereços são contíguos na memória e por isso o código funciona imprimindo a matriz inteira.

Eu forcei a barra pra explicar para o autor da pergunta o que estava acontecendo...


Acredito que não deva dar problema se você desalocar da mesma forma que alocou...
Se alocar de um jeito e desalocar de outro daí acho que não é bom...
Eu considero POG porque você está mascarando uma matrix em um array, daí a pessoa que for usar vai estar usando uma coisa achando que é outra...
Comprando gato por lebre ;)


5. Re: Dúvida em realação a alocação e endereçamento de matrizes

Paulo
paulo1205

(usa Ubuntu)

Enviado em 12/06/2018 - 01:59h

Não considero POG. É uma maneira de fazer mais eficiente em espaço e em tempo, por alocar (e gastar espaço de gerenciamento para) apenas 2 ponteiros, em vez de N+1. Você até tem os N+1 ponteiros, mas N-1 deles reaproveitam pedaços da segunda alocação.

Nessa ideia, apenas duas coisas me incomodam, uma muito pouco, e outra um bocado mais. O que me incomoda pouco é a ligeira assimetria, que se deve ao fato de a primeira “linha” da matriz ser construída de um jeito, e as demais, de outro.

A segunda coisa, que me incomoda mais, é a falta de encapsulamento. Como você está mostrando o “como fazer”, não há muito como fugir disso. Mas ter esses ponteiros todos visíveis é algo que provavelmente seria melhor evitar.

Para encapsular, eu pensei na seguinte ideia em C++. Note que em momento nenhum eu devolvo um objeto que possa ser usado para chegar ao ponteiro alocado para conter os dados (isto é: nem como ponteiro diretamente, nem como referência direta para algum elemento do array usado internamente — especialmente o primeiro elemento da primeira linha, cujo endereço é igual ao do array). Para isso eu tive de criar um tipo para encapsular ponteiros para as linhas, e outro para encapsular elementos individuais, bem como os devidos operadores.

#include <iostream>
#include <cstddef>

class matriz {
private:
size_t _lins, _cols;
int *_elementos;

class linha {
private:
friend class matriz;
int *_elementos;
size_t _cols;

class elemento {
private:
friend class matriz::linha;
friend std::ostream &operator<<(std::ostream &, const elemento &);
int *_elemento;

elemento(int *p): _elemento(p) { }
elemento(const elemento &) = delete; // Impede construtor de cópia default.
elemento(elemento &&old): elemento(old._elemento) { }

public:
operator int() const { return *_elemento; } // Retorna cópia do inteiro, não referência.

elemento &operator=(int val){
*_elemento=val;
return *this; // Retorna referência para o encapsulador, não para o dado.
}

elemento &operator=(const elemento &outro){
*_elemento=*outro._elemento;
return *this; // Retorna referência para o encapsulador, não para o dado.
}

template <class T> operator T() const { return T(*_elemento); }
};

friend std::ostream &operator<<(std::ostream &, const elemento &);

linha(int *elems_linha, size_t cols): _elementos(elems_linha), _cols(cols) { }
linha(const linha &) = delete;
linha(linha &&old): linha(old._elementos, old._cols) { }

public:
size_t cols(){ return _cols; }

const elemento operator[](size_t col) const { return _elementos+col; }
elemento operator[](size_t col){ return _elementos+col; }

/*
Outros recursos:
- iterator/const_iterator sobre elementos da linha;
- funções begin()/cbegin()/end()/cend() etc.
*/
};

friend std::ostream &operator<<(std::ostream &, const linha::elemento &);

public:
matriz(size_t lins, size_t cols):
_lins(lins), _cols(cols), _elementos(new int [lins*cols])
{
}

virtual ~matriz(){ delete [] _elementos; }

size_t lins() const { return _lins; }
size_t cols() const { return _cols; }

const linha operator[](size_t lin) const { return linha(_elementos+lin*_cols, _cols); }
linha operator[](size_t lin){ return linha(_elementos+lin*_cols, _cols); }

/*
Outros recursos:
- iterator/const_iterator sobre linhas da matriz;
- funções begin()/cbegin()/end()/cend() etc.
*/
};

std::ostream &operator<<(std::ostream &os, const matriz::linha::elemento &e){
return os << int(e);
}


// Utiliza versões que retornam “const matriz::linha” e “const matriz::linha::elemento”.
void print(const matriz &m){
for(size_t l=0; l<m.lins(); l++){
for(size_t c=0; c<m.cols(); c++)
std::cout << '\t' << m[l][c];
std::cout << '\n';
}
}


int main(){
matriz m(3, 4);

int i=0;
for(size_t l=0; l<m.lins(); l++)
for(size_t c=0; c<m.cols(); c++)
m[l][c]=i++;

print(m);
}



6. Re: Dúvida em realação a alocação e endereçamento de matrizes [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 12/06/2018 - 11:44h

paulo1205 escreveu:

Não considero POG. É uma maneira de fazer mais eficiente em espaço e em tempo, por alocar (e gastar espaço de gerenciamento para) apenas 2 ponteiros, em vez de N+1. Você até tem os N+1 ponteiros, mas N-1 deles reaproveitam pedaços da segunda alocação.


Eu quis dizer eficiente em termos da alocação e algumas operações.

Para outros tipos de operação com matrizes, porém, essa forma de trabalhar é pouco conveniente. Há um outro tópico em discussão corrente na comunidade que fala sobre escalonamento de matrizes. Nesse caso, uma operação comum é trocar a ordem de linhas inteiras. Se a primeira linha é a única que tem uma alocação real, e as demais apontam para dados que são parte dessa alocação, a primeira linha deixa de poder ser diretamente trocada com qualquer das demais, e teriam de ser trocados os elementos em cada linha individualmente.


7. Re: Dúvida em realação a alocação e endereçamento de matrizes [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 16/06/2018 - 12:06h

Nada exato...não é como o stackoverflow...






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts