Problema com código [RESOLVIDO]

1. Problema com código [RESOLVIDO]

cicero silva
c1c3ru

(usa Ubuntu)

Enviado em 17/10/2014 - 08:51h

Bom dia,boa tarde,boa note!
Pessoal meu problema é o seguinte:Meu professor passou um código(trata-se de uma matriz dinâmica e que calcula a media local) em sala para termos como referência em um futuro trabalho,o problema é que eu consigo entender quase todo o código porém, há um trecho que tem me tirado o sono.
Até já tentei tirar essas dúvida via e-mail e pessoa com o professor ainda assim não consigo entender.Ficaria eternamente grato se alguém puder me ajudar ou, apontar um caminho,desde já agradeço!
 
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <cstdlib>
#include <time.h>

using namespace std;

int main()

{
int**m;
int**m2;

int linhas;
int colunas;

cout << "Indique o numero de linhas" << endl;
cin >> linhas;
cout << "Indique o numero de colunas" << endl;
cin >> colunas;

cout << " aqui pediu dados e mostrou" << endl;
//========================================================

m = new int*[linhas];
m2 = new int*[linhas];
for (int i = 0; i < linhas; i++) {
m[i] = new int[colunas]; <<=========== é esse o trecho do código :).
m2[i] = new int[colunas];

}
cout << "aqui inicializou os ponteiro" << endl;

//========================================================
srand(time(NULL));

int cont = 1;
for (int j = 0; j < linhas; j++) {
for (int k = 0; k < colunas; k++) {
m[j][k] = (5 + rand() % 10);
m2[j][k] = m[j][k];
}
}
cout << "m1" << endl;
for (int j = 0; j < linhas; j++) {
for (int k = 0; k < colunas; k++) {
cout << m[j][k] << "\t";
}
cout << endl;
}
cout << endl;
cout << "Aqui preencheu com numeros aleatorios" << endl;
//========================================================
int tamanho = 3;
int mask = (tamanho - 1) / 2;
int soma = 0, temp = 0, media = 0;

for (int j = mask; j < linhas - mask; j++) {
for (int k = mask; k < colunas - mask; k++) {
for (int x = -mask; x <= mask; x++) {
for (int y = -mask; y <= mask; y++) {
soma = m[x + j][k + y] + temp;
temp = soma;
}

}
media = soma / (tamanho * tamanho);
m2[j][k] = media;
soma = 0;
media = 0;
cout << m2[j][k] << endl;
}
cout << endl;

}
for (int l = 0; l < linhas; l++) {
for (int n = 0; n < colunas; n++) {
m[l][n] = 0;
cout << m[l][n] << endl;
}
cout << endl;
}
cout << "Aqui por fim a magica do algoritmo calcula a media e o termo medio"
<< endl;
//========================================================
delete[] m;
delete[] m2;
system("pause");
}




  


2. Re: Problema com código [RESOLVIDO]

Igor Morais
igormorais

(usa Gentoo)

Enviado em 17/10/2014 - 09:59h

Se você ainda não viu ponteiros e alocação dinâmica, não vai entender nunca.


3. Re: Problema com código [RESOLVIDO]

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 17/10/2014 - 10:09h

Amigo, vc copiou aquele trecho de algum lugar ?

Pq o erro que apareceu aki acontece quando o codigo é copiado de algum lugar.

Vc nao esta usando a var cont
Vc usou o stdio.h e o cstdio , que sao a mesma coisa , mas prefira sempre o cstdio ao inves do stdio.h (apenas C++),assim como a stdlib


#include <cstdio>
#include <iostream>
#include <string>// É diferente do string.h, é propria do C++
#include <string.h>
#include <cstring> // Igual ao string.h
#include <cstdlib>
#include <ctime>

using namespace std;

int main(){
int **m;
int**m2;

int linhas;
int colunas;

cout << "Indique o numero de linhas" << endl;
cin >> linhas;
cout << "Indique o numero de colunas" << endl;
cin >> colunas;

cout << " aqui pediu dados e mostrou" << endl;
//========================================================
m = new int*[linhas];
m2 = new int*[linhas];
for (int i = 0; i < linhas; i++){
m[i] = new int[colunas];
m2[i] = new int[colunas];
}
cout << "aqui inicializou os ponteiro" << endl;
//========================================================
srand((unsigned)time(NULL));

int cont = 1;
for (int j = 0; j < linhas; j++) {
for (int k = 0; k < colunas; k++) {
m[j][k] = (5 + rand() % 10);
m2[j][k] = m[j][k];
}
}

cout << "m1" << endl;
for (int j = 0; j < linhas; j++) {
for (int k = 0; k < colunas; k++) {
cout << m[j][k] << "\t";
}
cout << endl;
}
cout << endl;
cout << "Aqui preencheu com numeros aleatorios" << endl;
//========================================================
int tamanho = 3;
int mask = (tamanho - 1) / 2;
int soma = 0, temp = 0, media = 0;

for (int j = mask; j < linhas - mask; j++) {
for (int k = mask; k < colunas - mask; k++) {
for (int x = -mask; x <= mask; x++) {
for (int y = -mask; y <= mask; y++) {
soma = m[x + j][k + y] + temp;
temp = soma;
}
}
media = soma / (tamanho * tamanho);
m2[j][k] = media;
soma = 0;
media = 0;
cout << m2[j][k] << endl;
}
cout << endl;
}

for (int l = 0; l < linhas; l++){
for (int n = 0; n < colunas; n++) {
m[l][n] = 0;
cout << m[l][n] << endl;
}
cout << endl;
}
cout << "Aqui por fim a magica do algoritmo calcula a media e o termo medio" << endl;
//========================================================
delete[] m;
delete[] m2;

cout << "Pressione Enter para Continuar" << endl;
getchar();
}



Espero ter ajudado

[]'s

T+


4. Re: Problema com código [RESOLVIDO]

Igor Morais
igormorais

(usa Gentoo)

Enviado em 17/10/2014 - 10:27h

Thihup escreveu:

Amigo, vc copiou aquele trecho de algum lugar ?

Pq o erro que apareceu aki acontece quando o codigo é copiado de algum lugar.

Vc nao esta usando a var cont
Vc usou o stdio.h e o cstdio , que sao a mesma coisa , mas prefira sempre o cstdio ao inves do stdio.h (apenas C++),assim como a stdlib

Espero ter ajudado

[]'s

T+


O professor passou esse código pra ele. Ele não tá questionando os erros e sim a lógica do trecho destacado que o mesmo não consegue compreender.


5. Re: Problema com código [RESOLVIDO]

Thiago Henrique Hüpner
Thihup

(usa Manjaro Linux)

Enviado em 17/10/2014 - 10:49h

A ta ;)

Esse link explica bem :

http://www.inf.pucrs.br/~pinho/PRGSWB/Ponteiros/ponteiros.html

Esse trecho de codigo:


colunas = 10;
for(int i =0;i<linhas;i++)
m[ i ] = new int[colunas];


Para cada posicao do ponteiro m (m["i"]),tera tantas posicoes desejadas (nesse caso , colunas = 10 , entao p/ cada posicao do ponteiro sera alocado 10 espaços por exemplo)

Espero ter ajudado

Qualquer coisa me add no skype : thihup

[]'s

T+


6. Re: Problema com código [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 17/10/2014 - 23:24h

A explicação resumida é que não existe uma forma direta de alocar dinamicamente um objeto que tenha aparência e sintaxe de uma matriz em C ou em C++. Das duas, uma: ou você aloca de uma só vez um blocão para os N×M elementos (de uma matriz de N linhas por M colunas) num único vetor, e usa a sintaxe A[i*M+j] para ter acesso ao que seria o elemento A[i][j], ou pensa na matriz como um “vetor de linhas” e em cada linha como um “vetor de elementos de cada coluna nesta linha”, o que lhe permite usar a tal sintaxe A[i][j], ao custo de alocações de memória parciais. O programa que você mostrou usa a segunda opção.

Comentando linha a linha o trecho que você indicou.

/*
Aloca uma região de memória com espaço suficiente para guardar
uma quantidade igual ao valor de ‘linhas’ de valores do tipo
‘int *’ (i.e. ponteiro para inteiro), e salva o endereço dessa
região de memória (i.e. do primeiro elemento) no ponteiro “m”
(cujo tipo é ponteiro para ponteiros de inteiros, ‘int **’).
*/
m = new int*[linhas];
/* Mesma operação, mas salvando o endereço em “m2”. */
m2 = new int*[linhas];

/*
Cada elemento de “m” ou “m2” é um ponteiro para inteiro, mas eles
ainda não apontam para endereço válido nenhum. O loop abaixo vai
fazer com que passem a apontar.

“i” conta de 0 a ‘linhas-1’, que são os índices válidos para
“m” e “m2”, de acordo com as alocações feitas acima.
*/
for (int i = 0; i < linhas; i++) {
/*
Aloca uma região de memória com espaço suficiente para guardar
uma quantidade igual ao valor de ‘colunas’ de elementos do tipo
‘int’ (inteiro), e salva o endereço dessa região de memória no
ponteiro para inteiros que é o (i+1)-ésimo elemento de “m”.
*/
m = new int[colunas];
/*
Mesma operação, mas salvando o endereço no (i+1)-ésimo elemento
de “m2”.
*/
m2[i] = new int[colunas];
}


A explicação para “[i](i+1)-ésimo
” é que os índices de arrays começam a ser contados em 0. Logo, o primeiro elemento tem o índice 0, o segundo, o índice 1, e assim por diante. Não existe "zerésimo", certo?


7. Re: Problema com código [RESOLVIDO]

cicero silva
c1c3ru

(usa Ubuntu)

Enviado em 18/10/2014 - 18:29h

paulo1205 escreveu:

A explicação resumida é que não existe uma forma direta de alocar dinamicamente um objeto que tenha aparência e sintaxe de uma matriz em C ou em C++. Das duas, uma: ou você aloca de uma só vez um blocão para os N×M elementos de uma matriz de N linhas por M colunas, e usa a sintaxe A[i*M+j] para ter acesso ao que seria o elemento A[i][j], ou pensa na matriz como um “vetor de linhas” e em cada linha como um “vetor de colunas”, o que lhe permite usar a tal sintaxe A[i][j], ao custo de alocações de memória parciais. O programa que você mostrou usa a segunda opção.

Comentando linha a linha o trecho que você indicou.

/*
Aloca uma região de memória com espaço suficiente para guardar
uma quantidade igual ao valor de ‘linhas’ de valores do tipo
‘int *’ (i.e. ponteiro para inteiro), e salva o endereço dessa
região de memória (i.e. do primeiro elemento) no ponteiro “m”
(cujo tipo é ponteiro para ponteiros de inteiros, ‘int **’).
*/
m = new int*[linhas];
/* Mesma operação, mas salvando o endereço em “m2”. */
m2 = new int*[linhas];

/*
Cada elemento de “m” ou “m2” é um ponteiro para inteiro, mas eles
ainda não apontam para endereço válido nenhum. O loop abaixo vai
fazer com que passem a apontar.

“i” conta de 0 a ‘linhas-1’, que são os índices válidos para
“m” e “m2”, de acordo com as alocações feitas acima.
*/
for (int i = 0; i < linhas; i++) {
/*
Aloca uma região de memória com espaço suficiente para guardar
uma quantidade igual ao valor de ‘colunas’ de elementos do tipo
‘int’ (inteiro), e salva o endereço dessa região de memória no
ponteiro para inteiros que é o (i+1)-ésimo elemento de “m”.
*/
m = new int[colunas];
/*
Mesma operação, mas salvando o endereço no (i+1)-ésimo elemento
de “m2”.
*/
m2[i] = new int[colunas];
}


A explicação para “[i](i+1)-ésimo
” é que os índices de arrays começam a ser contados em 0. Logo, o primeiro elemento tem o índice 0, o segundo, o índice 1, e assim por diante. Não existe "zerésimo", certo?


ok! primeiramente gostaria de agradecer a todos que responderam ao tópico.
então deixa eu ver se entendi o que você está me explicando...

alocar de uma só vez eu faria assim:
m = |a,b,c,d|
|e,f,g,h|
|i,j,l,m|

vetor de linha:
[coluna]
[linha] [1][2][3][4] e esses mesmos índices seriam as minhas colunas,ok blz.

o trecho de ponteiro também que, o que ele ta fazendo é dizer que o endereço da matriz de ponteiro int**m;
vai recebe um vetor do tipo inteiro e de tamanho dinâmico(passado pelo usuário),int*[linha],correto?
agora não entendo que fez isso com linhas,mas colunas ele criou um loop.Pergunto não poderia ter feito a mesma coisa com as colunas,sem a necessidade do loop "for"(essa é minha real duvida)? e alguém conseguiu fazer rodar esse código?.



8. Re: Problema com código [RESOLVIDO]

cicero silva
c1c3ru

(usa Ubuntu)

Enviado em 18/10/2014 - 18:31h

Thihup escreveu:

Amigo, vc copiou aquele trecho de algum lugar ?

Pq o erro que apareceu aki acontece quando o codigo é copiado de algum lugar.

Vc nao esta usando a var cont
Vc usou o stdio.h e o cstdio , que sao a mesma coisa , mas prefira sempre o cstdio ao inves do stdio.h (apenas C++),assim como a stdlib


#include <cstdio>
#include <iostream>
#include <string>// É diferente do string.h, é propria do C++
#include <string.h>
#include <cstring> // Igual ao string.h
#include <cstdlib>
#include <ctime>

using namespace std;

int main(){
int **m;
int**m2;

int linhas;
int colunas;

cout << "Indique o numero de linhas" << endl;
cin >> linhas;
cout << "Indique o numero de colunas" << endl;
cin >> colunas;

cout << " aqui pediu dados e mostrou" << endl;
//========================================================
m = new int*[linhas];
m2 = new int*[linhas];
for (int i = 0; i < linhas; i++){
m[i] = new int[colunas];
m2[i] = new int[colunas];
}
cout << "aqui inicializou os ponteiro" << endl;
//========================================================
srand((unsigned)time(NULL));

int cont = 1;
for (int j = 0; j < linhas; j++) {
for (int k = 0; k < colunas; k++) {
m[j][k] = (5 + rand() % 10);
m2[j][k] = m[j][k];
}
}

cout << "m1" << endl;
for (int j = 0; j < linhas; j++) {
for (int k = 0; k < colunas; k++) {
cout << m[j][k] << "\t";
}
cout << endl;
}
cout << endl;
cout << "Aqui preencheu com numeros aleatorios" << endl;
//========================================================
int tamanho = 3;
int mask = (tamanho - 1) / 2;
int soma = 0, temp = 0, media = 0;

for (int j = mask; j < linhas - mask; j++) {
for (int k = mask; k < colunas - mask; k++) {
for (int x = -mask; x <= mask; x++) {
for (int y = -mask; y <= mask; y++) {
soma = m[x + j][k + y] + temp;
temp = soma;
}
}
media = soma / (tamanho * tamanho);
m2[j][k] = media;
soma = 0;
media = 0;
cout << m2[j][k] << endl;
}
cout << endl;
}

for (int l = 0; l < linhas; l++){
for (int n = 0; n < colunas; n++) {
m[l][n] = 0;
cout << m[l][n] << endl;
}
cout << endl;
}
cout << "Aqui por fim a magica do algoritmo calcula a media e o termo medio" << endl;
//========================================================
delete[] m;
delete[] m2;

cout << "Pressione Enter para Continuar" << endl;
getchar();
}



Espero ter ajudado

[]'s

T+

Isso foi um código passado pelo professor em sala de aula.
no caso de exercícios eu copio os cabeçalho nos código seguintes as vezes :)



9. Re: Problema com código [RESOLVIDO]

cicero silva
c1c3ru

(usa Ubuntu)

Enviado em 18/10/2014 - 18:33h

igormorais escreveu:

Se você ainda não viu ponteiros e alocação dinâmica, não vai entender nunca.

sim,estou vendo exatamente isso,mas hoje é pilha,fila,lista.



10. Re: Problema com código [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 19/10/2014 - 04:40h

c1c3ru escreveu:

ok! primeiramente gostaria de agradecer a todos que responderam ao tópico.
então deixa eu ver se entendi o que você está me explicando...

alocar de uma só vez eu faria assim:
m = |a,b,c,d|
|e,f,g,h|
|i,j,l,m|


Não. Supondo uma matriz 3×4, com elementos na forma
a b c d
e f g h
i j k l

, o primeiro modo de fazer a que me referi criaria um vetor de 12 (3×4) posições, com elementos na forma de vetor mesmo, i.e.
a b c d e f g h i j k l 

. Para obter o elemento na linha l e coluna c, você teria de calcular por conta própria, sem qualquer ajuda do compilador, a posição no vetor correspondente ao tal elemento. A fórmula para esse cálculo é l*M+c (onde M é o número de colunas da matriz).

vetor de linha:
[coluna]
[linha] [1][2][3][4] e esses mesmos índices seriam as minhas colunas,ok blz.

o trecho de ponteiro também que, o que ele ta fazendo é dizer que o endereço da matriz de ponteiro int**m;
vai recebe um vetor do tipo inteiro e de tamanho dinâmico(passado pelo usuário),int*[linha],correto?


Não entendi o que você quis dizer acima. Pode ser um pouco mais claro?

agora não entendo que fez isso com linhas,mas colunas ele criou um loop.Pergunto não poderia ter feito a mesma coisa com as colunas,sem a necessidade do loop "for"(essa é minha real duvida)?


Vamos esquecer a matriz por enquanto, e pensar primeiro na alocação dinâmica de vetor. Mas, antes disso, vamos olhar um pouquinho para os ponteiros isoladamente.

Observe a seguinte declaração.

int *pi; 


Seguindo a antiga regra de “ler declarações em C de trás para frente”, geralmente se diz o seguinte: “pi é um ponteiro (‘*’) para inteiro (‘int’)”.

Entretanto, quando aparece algo como “*pi” numa expressão, e não numa declaração, geralmente se lê, da esquerda para a direita mesmo, algo como “conteúdo de pi” ou “conteúdo da memória apontada por (ou referida por) pi”.

Às vezes eu penso que ajuda a entender melhor o papel de um ponteiro se lermos sempre da esquerda para a direita. Assim como uma declaração de variável não-ponteiro como
int i; 

é lida “i é (um valor) inteiro”, também algo como
int *pi; 

poderia ser lido como “o conteúdo de pi é (um valor) inteiro”. De fato, assim como eu posso atribuir a ou ler de i qualquer valor inteiro, eu posso fazer exatamente o mesmo com *pi -- com a única ressalva de que o valor de pi (usado sem o operador “*”) seja o de um endereço de memória válido para o programa.

A ressalva acima é importante: não se pode mesmo esperar que o programa consiga ler ou escrever valores num endereço de memória que não lhe pertence, certo? Por isso, existem algumas maneiras pelas quais se pode fazer com que um ponteiro qualquer aponte para memória válida. São elas:

a) fazer com que o ponteiro aponte para uma o endereço correspondente a uma variável já existente, como no exemplo abaixo (preste atenção aos comentários);


int i; // i é uma variável inteira.
int ai[10]; // ai é um vetor de 10 elementos inteiros.
int *pi, *pmi; // pi e pmi são ponteiros para inteiros, ou
// “o conteúdo de pi” e “o conteúdo de pmi”
// são inteiros.

pi=&i; // lê-se “pi é igual ao endereço de i”.
*pi=5; // lê-se “o conteúdo de pi é igual a 5”.
/* Agora i também é igual a 5. */

ai[0]=1; // lê-se “o elemento com índice zero do array
// (ou vetor) ai é igual a 1”.
pmi=ai; // lê-se “pmi é igual a ai” (oh! ;)); lembre-se
// que quando o nome de um array do C ou C++ é
// usado sem o operador [] e um índice, essa ex-
// pressão tem o valor do endereço de seu primeiro
// elemento, logo a atribuição é perfeitamente
// válida, pois pmi espera ter o endereço
// (ponteiro) de um inteiro, e o endereço do
// primeiro elemento de ai é justamente o endereço
// de um inteiro.
*pmi+=5; // lê-se “o conteúdo de pmi é acrescido de 5”.
/* Agora ai[0] vale 6. */


b) pedir ao ambiente de execução a alocação de mais memória, e receber o valor do primeiro endereço da região alocada num ponteiro.

int *pi, *pmi;

pi=new int; // Cria espaço para um único valor inteiro
// e faz com que pi aponte para ele.
pmi=new int[10]; // Cria espaço para múltiplos (10, no caso)
// valores inteiros contíguos e faz com que
// pmi aponte para o primeiro deles.
/*
Note que as alocações acima apenas reservam espaços em
memória e colocam os endereços desses espaços nos respec-
tivos ponteiros. O conteúdo dessa memória, no entanto,
é inicialmente desconhecido (exceto no caso da alocação
de classes ou estruturas que possuam construtores).
*/

/*
Quando os conteúdos referidos por pi e pmi não forem
mais necessários, a memória alocada para eles deve ser
liberada.
*/
delete[] pmi; // delete[] libera blocos de valores.
delete pi; // delete puro libera espaço de um valor só.


Nos exemplos acima, tanto em (a) quanto em (b), você viu aparecer o operador de acesso com deslocamento (ou de indexação, ou de índice) []. Certamente você está acostumado a vê-lo associado a arrays (ou vetores), mas a verdade é que ele é aplicável a qualquer relacionamento entre um endereço e um valor inteiro, denotando acesso ao elemento contido no endereço contido nesse deslocamento em relação ao endereço-base.

Como ponteiros são endereços, decorre que o operador [] pode ser aplicado a ponteiros. De fato, existe uma relação de equivalência entre o operador * (“conteúdo de”) e o operador de indexação [], que é a seguinte.

*pi==pi[0] 


Analogamente, segue que:

pi==&pi[0] 


(Os operadores & (“endereço de”) e * (“conteúdo de”) são como que inversos um do outro. A identidade acima é decorrência da aplicação do operador & aos dois lados da identidade anterior, i.e. “&*pi==&pi[0]”, sendo que o pedaço “&*” pode ser cancelado dos pontos de vista matemático, lógico, semântico e sintático.)

Mais genericamente, pode-se dizer o seguinte.

*(pi+n)==pi[n]  /* n é inteiro */
pi+n==&pi[n]


Tanto no acesso com deslocamento (operador []) quanto no deslocamento puro (endereço+inteiro), o compilador leva em consideração o tipo de dado contido no ponteiro ou elemento do array. Assim sendo, se, hipoteticamente falando, o endereço-base contido em pi é o da posição 1500 da memória e o tipo de dados é um inteiro que ocupa quatro bytes, pi+1 corresponderá ao endereço 1504, não 1501; pi+2 será o endereço 1508, e assim por diante. Por outro lado, se o tipo de dado apontado pelo ponteiro ou contido no elemento do array tiver tamanho um (tipo de dados char, por exemplo), os deslocamentos serão de um em um (por exemplo: se ac é um array de char disposto a partir da posição de memória 2000, ac+1 (ou &ac[1]) será 2001, ac+2 será 2002, e assim por diante).

Nos exemplos dados lá no começo (tanto no caso (a) quanto no caso (b)), eu mostro exemplos de ponteiros que apontam para um único dado (pi) ou para o primeiro de vários valores inteiros contíguos (pmi). Você, porém, deve ter notado que o tipo de dados dos dois ponteiros era exatamente o mesmo: int * (ponteiro para inteiro). Teoricamente, você só deve aplicar deslocamentos no segundo caso (pmi), como sugerido pelos próprios comandos de alocação e desalocação (caso (b)), mas o compilador não vai se queixar se você aplicar deslocamentos também sobre o que alocou espaço apenas para um (pi). Cabe a você ser prudente. O compilador pega alguns erros crassos, mas ele em geral confia que você sabe o que está fazendo.

(Até agora, não sei se estou ajudando ou confundindo mais ainda. Mas continuo.)

C e C++ admitem ponteiros para qualquer tipo de dados -- existe até mesmo um “ponteiro para void” (que, em princípio, seria meio estranho, mas que tem o sentido de permitir guardar um endereço de qualquer tipo de valor ou objeto, sem se preocupar, durante a compilação, com que tipo de dado existe realmente ali -- de novo, é o compilador confiando em você). Em particular, é possível criar ponteiros de ponteiros, ponteiros de ponteiros de ponteiros, e assim sucessivamente.

Uma declaração como
int **ppi 

pode ser lida de três maneiras, a saber:

* ppi é um ponteiro para ponteiro para inteiro;
* o conteúdo do conteúdo de ppi é um inteiro;
* o conteúdo de ppi é um ponteiro para inteiro.

A primeira forma é a leitura clássica da direita para a esquerda. A segunda, a aplicação direta do que eu disse anteriormente sobre ler o operador * como “conteúdo de” também no momento da declaração. Mas a terceira é um híbrido das duas, que penso que facilita ver o porquê da alocação dinâmica de matriz em duas fases (sendo a segunda fase em loop).

Aliás, acho que também ajudaria se eu mudasse o nome da variável e dissesse o seguinte.

int **pmpmi;  // "pmpmi" é um ponteiro para
// múltiplos ponteiros para
// múltiplos inteiros.


Lembra do que eu disse lá em cima sobre a alocação não definir valores iniciais para o dado ou bloco de dados alocado? Isso também é útil agora.

Pensando de fora para dentro, no momento em que você aloca o ponteiro para múltiplos ponteiros de inteiro, você aloca um bloco onde cada elemento é um ponteiro para inteiro. O conteúdo desses elementos, como você deve lembrar é desconhecido. Cada elemento desses é um ponteiro para múltiplos inteiros, então você precisa fazer com que eles realmente apontem para esses múltiplos inteiros, e faz isso através da alocação sucessiva de cada um deles.

O código em C++ que faz isso é, obviamente, o já conhecido:

int **pmpmi;
pmpmi=new int *[n_linhas]; // entrega o endereço (do
// primeiro elemento) de um
// bloco com n_linhas elementos
// do tipo “int *” (note que
// “endereço de int *” é equiva-
// lente a “ponteiro para int *”,
// logo a atribuição é compatí-
// vel com o tipo de pmpmi).
for(int i=0; i<n_linhas; i++)
// Aqui é útil lembrar da analogia entre [] e *,
// bem como pensar nos termos híbridos de “conteúdo
// de pmpmi é ponteiro para (múltiplos) inteiro(s)”.
pmpmi=new int[n_colunas]; // entrega sucessivos
// endereços (dos primeiros
// elementos) de blocos com
// n_colunas elementos do
// tipo “int” (note que
// “endereço de int” é equi-
// valente a “ponteiro para
// int”, logo a atribuição é
// compatível com o tipo de
// *pmpmi e pmpmi[i].


---

Eu não consigo imaginar outra forma de fazer. Você perguntou sobre fazer sem [i]loop
. Imaginou o quê? Algo como o que segue?

int **matriz;
matriz=new int[n_linhas][N_COLUNAS];


Se foi, isso não funcionaria porque os tipos de dados são incompatíveis: a alocação entrega um bloco com elementos do tipo “array com N_COLUNAS elementos inteiros”. Para fazer funcionar sem mexer na expressão de alocação, você teria de trocar a declaração de matriz para o seguinte.

int (*)matriz[N_COLUNAS]; 


Mas isso mudaria radicalmente o sentido do programa. A quantidade de colunas teria de ser constante ao longo de todo o programa, e você alocaria dinamicamente apenas a quantidade de linhas, cada uma delas com tamanho fixo.

------

e alguém conseguiu fazer rodar esse código?.


Não tentei. A explicação que você pediu não dependia do restante do código, e eu realmente não me preocupei com ele.


11. Re: Problema com código [RESOLVIDO]

cicero silva
c1c3ru

(usa Ubuntu)

Enviado em 20/10/2014 - 13:46h

paulo1205 escreveu:

c1c3ru escreveu:

ok! primeiramente gostaria de agradecer a todos que responderam ao tópico.
então deixa eu ver se entendi o que você está me explicando...

alocar de uma só vez eu faria assim:
m = |a,b,c,d|
|e,f,g,h|
|i,j,l,m|


Não. Supondo uma matriz 4×3, com elementos na forma
a b c d
e f g h
i j k l

, o primeiro modo de fazer a que me referi criaria um vetor de 12 (4×3) posições, com elementos na forma de vetor mesmo, i.e.
a b c d e f g h i j k l 

. Para obter o elemento na linha l e coluna c, você teria de calcular por conta própria, sem qualquer ajuda do compilador, a posição no vetor correspondente ao tal elemento. A fórmula para esse cálculo é l*M+c (onde M é o número de colunas da matriz).

vetor de linha:
[coluna]
[linha] [1][2][3][4] e esses mesmos índices seriam as minhas colunas,ok blz.

o trecho de ponteiro também que, o que ele ta fazendo é dizer que o endereço da matriz de ponteiro int**m;
vai recebe um vetor do tipo inteiro e de tamanho dinâmico(passado pelo usuário),int*[linha],correto?


Não entendi o que você quis dizer acima. Pode ser um pouco mais claro?

agora não entendo que fez isso com linhas,mas colunas ele criou um loop.Pergunto não poderia ter feito a mesma coisa com as colunas,sem a necessidade do loop "for"(essa é minha real duvida)?


Vamos esquecer a matriz por enquanto, e pensar primeiro na alocação dinâmica de vetor. Mas, antes disso, vamos olhar um pouquinho para os ponteiros isoladamente.

Observe a seguinte declaração.

int *pi; 


Seguindo a antiga regra de “ler declarações em C de trás para frente”, geralmente se diz o seguinte: “pi é um ponteiro (‘*’) para inteiro (‘int’)”.

Entretanto, quando aparece algo como “*pi” numa expressão, e não numa declaração, geralmente se lê, da esquerda para a direita mesmo, algo como “conteúdo de pi” ou “conteúdo da memória apontada por (ou referida por) pi”.

Às vezes eu penso que ajuda a entender melhor o papel de um ponteiro se lermos sempre da esquerda para a direita. Assim como uma declaração de variável não-ponteiro como
int i; 

é lida “i é (um valor) inteiro”, também algo como
int *pi; 

poderia ser lido como “o conteúdo de pi é (um valor) inteiro”. De fato, assim como eu posso atribuir a ou ler de i qualquer valor inteiro, eu posso fazer exatamente o mesmo com *pi -- com a única ressalva de que o valor de pi (usado sem o operador “*”) seja o de um endereço de memória válido para o programa.

A ressalva acima é importante: não se pode mesmo esperar que o programa consiga ler ou escrever valores num endereço de memória que não lhe pertence, certo? Por isso, existem algumas maneiras pelas quais se pode fazer com que um ponteiro qualquer aponte para memória válida. São elas:

a) fazer com que o ponteiro aponte para uma o endereço correspondente a uma variável já existente, como no exemplo abaixo (preste atenção aos comentários);


int i; // i é uma variável inteira.
int ai[10]; // ai é um vetor de 10 elementos inteiros.
int *pi, *pmi; // pi e pmi são ponteiros para inteiros, ou
// “o conteúdo de pi” e “o conteúdo de pmi”
// são inteiros.

pi=&i; // lê-se “pi é igual ao endereço de i”.
*pi=5; // lê-se “o conteúdo de pi é igual a 5”.
/* Agora i também é igual a 5. */

ai[0]=1; // lê-se “o elemento com índice zero do array
// (ou vetor) ai é igual a 1”.
pmi=ai; // lê-se “pmi é igual a ai” (oh! ;)); lembre-se
// que quando o nome de um array do C ou C++ é
// usado sem o operador [] e um índice, essa ex-
// pressão tem o valor do endereço de seu primeiro
// elemento, logo a atribuição é perfeitamente
// válida, pois pmi espera ter o endereço
// (ponteiro) de um inteiro, e o endereço do
// primeiro elemento de ai é justamente o endereço
// de um inteiro.
*pmi+=5; // lê-se “o conteúdo de pmi é acrescido de 5”.
/* Agora ai[0] vale 6. */


b) pedir ao ambiente de execução a alocação de mais memória, e receber o valor do primeiro endereço da região alocada num ponteiro.

int *pi, *pmi;

pi=new int; // Cria espaço para um único valor inteiro
// e faz com que pi aponte para ele.
pmi=new int[10]; // Cria espaço para múltiplos (10, no caso)
// valores inteiros contíguos e faz com que
// pmi aponte para o primeiro deles.
/*
Note que as alocações acima apenas reservam espaços em
memória e colocam os endereços desses espaços nos respec-
tivos ponteiros. O conteúdo dessa memória, no entanto,
é inicialmente desconhecido (exceto no caso da alocação
de classes ou estruturas que possuam construtores).
*/

/*
Quando os conteúdos referidos por pi e pmi não forem
mais necessários, a memória alocada para eles deve ser
liberada.
*/
delete[] pmi; // delete[] libera blocos de valores.
delete pi; // delete puro libera espaço de um valor só.


Nos exemplos acima, tanto em (a) quanto em (b), você viu aparecer o operador de acesso com deslocamento (ou de indexação, ou de índice) []. Certamente você está acostumado a vê-lo associado a arrays (ou vetores), mas a verdade é que ele é aplicável a qualquer relacionamento entre um endereço e um valor inteiro, denotando acesso ao elemento contido no endereço contido nesse deslocamento em relação ao endereço-base.

Como ponteiros são endereços, decorre que o operador [] pode ser aplicado a ponteiros. De fato, existe uma relação de equivalência entre o operador * (“conteúdo de”) e o operador de indexação [], que é a seguinte.

*pi==pi[0] 


Analogamente, segue que:

pi==&pi[0] 


(Os operadores & (“endereço de”) e * (“conteúdo de”) são como que inversos um do outro. A identidade acima é decorrência da aplicação do operador & aos dois lados da identidade anterior, i.e. “&*pi==&pi[0]”, sendo que o pedaço “&*” pode ser cancelado dos pontos de vista matemático, lógico, semântico e sintático.)

Mais genericamente, pode-se dizer o seguinte.

*(pi+n)==pi[n]  /* n é inteiro */
pi+n==&pi[n]


Tanto no acesso com deslocamento (operador []) quanto no deslocamento puro (endereço+inteiro), o compilador leva em consideração o tipo de dado contido no ponteiro ou elemento do array. Assim sendo, se, hipoteticamente falando, o endereço-base contido em pi é o da posição 1500 da memória e o tipo de dados é um inteiro que ocupa quatro bytes, pi+1 corresponderá ao endereço 1504, não 1501; pi+2 será o endereço 1508, e assim por diante. Por outro lado, se o tipo de dado apontado pelo ponteiro ou contido no elemento do array tiver tamanho um (tipo de dados char, por exemplo), os deslocamentos serão de um em um (por exemplo: se ac é um array de char disposto a partir da posição de memória 2000, ac+1 (ou &ac[1]) será 2001, ac+2 será 2002, e assim por diante).

Nos exemplos dados lá no começo (tanto no caso (a) quanto no caso (b)), eu mostro exemplos de ponteiros que apontam para um único dado (pi) ou para o primeiro de vários valores inteiros contíguos (pmi). Você, porém, deve ter notado que o tipo de dados dos dois ponteiros era exatamente o mesmo: int * (ponteiro para inteiro). Teoricamente, você só deve aplicar deslocamentos no segundo caso (pmi), como sugerido pelos próprios comandos de alocação e desalocação (caso (b)), mas o compilador não vai se queixar se você aplicar deslocamentos também sobre o que alocou espaço apenas para um (pi). Cabe a você ser prudente. O compilador pega alguns erros crassos, mas ele em geral confia que você sabe o que está fazendo.

(Até agora, não sei se estou ajudando ou confundindo mais ainda. Mas continuo.)

C e C++ admitem ponteiros para qualquer tipo de dados -- existe até mesmo um “ponteiro para void” (que, em princípio, seria meio estranho, mas que tem o sentido de permitir guardar um endereço de qualquer tipo de valor ou objeto, sem se preocupar, durante a compilação, com que tipo de dado existe realmente ali -- de novo, é o compilador confiando em você). Em particular, é possível criar ponteiros de ponteiros, ponteiros de ponteiros de ponteiros, e assim sucessivamente.

Uma declaração como
int **ppi 

pode ser lida de três maneiras, a saber:

* ppi é um ponteiro para ponteiro para inteiro;
* o conteúdo do conteúdo de ppi é um inteiro;
* o conteúdo de ppi é um ponteiro para inteiro.

A primeira forma é a leitura clássica da direita para a esquerda. A segunda, a aplicação direta do que eu disse anteriormente sobre ler o operador * como “conteúdo de” também no momento da declaração. Mas a terceira é um híbrido das duas, que penso que facilita ver o porquê da alocação dinâmica de matriz em duas fases (sendo a segunda fase em loop).

Aliás, acho que também ajudaria se eu mudasse o nome da variável e dissesse o seguinte.

int **pmpmi;  // "pmpmi" é um ponteiro para
// múltiplos ponteiros para
// múltiplos inteiros.


Lembra do que eu disse lá em cima sobre a alocação não definir valores iniciais para o dado ou bloco de dados alocado? Isso também é útil agora.

Pensando de fora para dentro, no momento em que você aloca o ponteiro para múltiplos ponteiros de inteiro, você aloca um bloco onde cada elemento é um ponteiro para inteiro. O conteúdo desses elementos, como você deve lembrar é desconhecido. Cada elemento desses é um ponteiro para múltiplos inteiros, então você precisa fazer com que eles realmente apontem para esses múltiplos inteiros, e faz isso através da alocação sucessiva de cada um deles.

O código em C++ que faz isso é, obviamente, o já conhecido:

int **pmpmi;
pmpmi=new int *[n_linhas]; // entrega o endereço (do
// primeiro elemento) de um
// bloco com n_linhas elementos
// do tipo “int *” (note que
// “endereço de int *” é equiva-
// lente a “ponteiro para int *”,
// logo a atribuição é compatí-
// vel com o tipo de pmpmi).
for(int i=0; i<n_linhas; i++)
// Aqui é útil lembrar da analogia entre [] e *,
// bem como pensar nos termos híbridos de “conteúdo
// de pmpmi é ponteiro para (múltiplos) inteiro(s)”.
pmpmi=new int[n_colunas]; // entrega sucessivos
// endereços (dos primeiros
// elementos) de blocos com
// n_colunas elementos do
// tipo “int” (note que
// “endereço de int” é equi-
// valente a “ponteiro para
// int”, logo a atribuição é
// compatível com o tipo de
// *pmpmi e pmpmi[i].


---

Eu não consigo imaginar outra forma de fazer. Você perguntou sobre fazer sem [i]loop
. Imaginou o quê? Algo como o que segue?

int **matriz;
matriz=new int[n_linhas][N_COLUNAS];


Se foi, isso não funcionaria porque os tipos de dados são incompatíveis: a alocação entrega um bloco com elementos do tipo “array com N_COLUNAS elementos inteiros”. Para fazer funcionar sem mexer na expressão de alocação, você teria de trocar a declaração de matriz para o seguinte.

int (*)matriz[N_COLUNAS]; 


Mas isso mudaria radicalmente o sentido do programa. A quantidade de colunas teria de ser constante ao longo de todo o programa, e você alocaria dinamicamente apenas a quantidade de linhas, cada uma delas com tamanho fixo.

------

e alguém conseguiu fazer rodar esse código?.


Não tentei. A explicação que você pediu não dependia do restante do código, e eu realmente não me preocupei com ele.
Cara obrigado por ter respondido e ajudado show de bola!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts