Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

1. Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

João Victor Vilela de Mendonça
Tyron

(usa Ubuntu)

Enviado em 14/06/2016 - 00:25h

Saudações a todos! Sou novo aqui no fórum, apesar de utilizar sempre pra pesquisar dúvidas, é a primeira vez que escrevo uma pergunta.

Vamos lá...

Estou escrevendo um código que cria uma matriz dinâmica (Gerada utilizando alocamento dinâmico de memória) e imprime essa matriz utilizando uma função. O problema é que, dependendo do tamanho da matriz (se passar de 5 linhas e 5 colunas) os primeiros valores são reescritos com números bem grandes, provavelmente algum lixo de memória. Eu testei o mesmo programa no Windows 10 utilizando o codeblocks e isso não acontece. Acontece apenas no linux (ubuntu).

Eis o código:

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

void printMatriz(int **m, int n);

int main() {
int **matriz;
int n, i, j;
printf("Digite o número de linhas e colunas da matriz: \n");
scanf("%d", &n);

//Gera uma matriz nxn dinâmica.
matriz = (int**)malloc(n*sizeof(int));
for (i = 0; i < n; i++) {
matriz[i] = (int*)malloc(n*sizeof(int));
for (j = 0; j < n; j++) {
printf("Digite o valor da posição [%d][%d]:\n", i, j);
scanf("%d", &matriz[i][j]);
}
}
printf("\n");
printMatriz(matriz, n);
printf("\n");

return 0;
}

void printMatriz(int **m, int n) {
int i, j;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
printf("%02d ", m[i][j]);
}
printf("\n");
}
}



Exemplo do programa rodando com uma matriz de 5 linhas e 5 colunas e preenchendo todos os espaços com 1 (veja que o primeiro e segundo espaço da matriz foram modificados):

20535504 00 01 01 01
01 01 01 01 01
01 01 01 01 01
01 01 01 01 01
01 01 01 01 01



  


2. MELHOR RESPOSTA

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/06/2016 - 10:22h

O erro está na alocação de matriz. Você está mandando alocar n vezes o tamanho de um int, mas os valores são do tipo int *.

Numa arquitetura de 64 bits (que suponho ter sido o que você usou no Linux), o tamanho de um inteiro é 4 bytes, e o de um ponteiro é 8 bytes, o que significa que você provavelmente está alocando memória de menos. Possivelmente você não viu o erro no Windows por estar usando com ele um compilador de 32 bits, para o qual os tamanhos de inteiros e ponteiros coincidem, com 4 bytes para ambos (ou então, o que é menos provável, um compilador de 64 bits que usa 8 bytes também para int).

Existe um jeito de você NUNCA errar o tamanho: em vez de passar o nome do tipo para sizeof, você passa a expressão usada para acesso a um dos elementos (por exemplo, o primeiro elemento), e deixa o compilador inferir o tipo e o respectivo tamanho. Veja:

int **matriz;
matriz=malloc(n*sizeof matriz[0]); // ou “n*sizeof *matriz”
for(int m=0; m<n; m++)
matriz[m]=malloc(n*sizeof matriz[m][0]); // ou “n*sizeof *matriz[m]”, ou “n*sizeof matriz[0][0]”, ou “n*sizeof **matriz”

(Note que no exemplo acima eu não usei coerção de tipos sobre o valor retornado por malloc(). Em C ela não é necessária, e, na verdade, não é vista com bons olhos por quem usa apenas C. C++ é que não aceita conversão automática de void * para outros tipos de ponteiros. Se o seu compilador reclamar da falta da conversão de tipos, mude a maneira de gerar código para usar C em lugar de C++. Se você quiser usar C++, não deveria usar malloc()/free(), mas sim new[]/delete[] -- ou, o que seria melhor ainda, std::vector.)

Quanto à abordagem, eu amistosamente discordo da preferência do listeiro_037. Reconheço que ela tem muitas vantagens (inclusive, em casos não otimizados, de velocidade), mas o fato de precisar fazer uma multiplicação e uma soma para obter o índice real no array linear me parece aumentar o esforço de manutenção e, consequentemente, a propensão a erros. Ele também falou em usar macros para tanto, mas elas, por sua vez, têm seus próprios conjuntos de problemas (veja, por exemplo, https://isocpp.org/wiki/faq/misc-technical-issues#macros-with-if).

3. Re: Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 14/06/2016 - 00:45h

Não gosto de montar matrizes com esse sistema que parece um varal, com memória alocada pendurada em memória alocada.
Prefiro montar um espaço alocado de tamanho NxN e usar algo semelhante a uma macro para me orientar por linha e coluna.

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden[/quote]


4. Re: Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

João Victor Vilela de Mendonça
Tyron

(usa Ubuntu)

Enviado em 14/06/2016 - 00:54h

listeiro_037 escreveu:

Não gosto de montar matrizes com esse sistema que parece um varal, com memória alocada pendurada em memória alocada.
Prefiro montar um espaço alocado de tamanho NxN e usar algo semelhante a uma macro para me orientar por linha e coluna.

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden


Realmente montar matriz dinâmica dessa forma é meio chato, mas estou realmente muito confuso quanto a esses espaços de memória que foram reescritos pelo sistema. E o fato de isso só acontecer no linux me deixa mais confuso ainda.




5. Re: Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 14/06/2016 - 10:56h

Não entendi qual pode ser o erro no site citado como exemplo.
Se puder explicar agradeceria.

Mas o que imaginei seria uma coisa bem assim:

#define NDX(I,J)  I*N+J 


Ou como meu professor ensinou, que macros possuem problemas de substituição de valores por extenso.
Devem ser colocados parêntesis na fórmula a ser inserida para não ocorrer erro na substituição.
Deixarei sem.

Outra possibilidade seria função inline, onde o compilador pode ou não substituir o código localmente. Ou não.

O pensamento parece ser mais simples para não criar o varal de memórias alocadas.
Inclusive no caso de hipermatrizes. NxNxNx ... xN.

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden[/quote]


6. Re: Espaços de matriz sendo reescritos com lixo de memória

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/06/2016 - 13:56h

listeiro_037 escreveu:

Não entendi qual pode ser o erro no site citado como exemplo.
Se puder explicar agradeceria.


A URL que eu coloquei realmente não foi a melhor. O C++ FAQ fala de quatro graves problemas com macros, o link que eu coloquei foi o do problema 2 (que foi o que apareceu primeiro no Google), e realmente não reflete os problemas na implementação que você propôs. Mas os links para os demais problemas estão ali.

Mas o que imaginei seria uma coisa bem assim:

#define NDX(I,J)  I*N+J 


O primeiro problema é que N deveria ser passado também como argumento à macro. Você pode até assumir N como global, mas isso seria um tiro do escuro -- qualquer função pode redeclarar N localmente, ou usar outro nome (ou mesmo nome nenhum, preferindo constantes) na hora de fazer suas alocações.

Ou como meu professor ensinou, que macros possuem problemas de substituição de valores por extenso.
Devem ser colocados parêntesis na fórmula a ser inserida para não ocorrer erro na substituição.
Deixarei sem.


Pois é... Nesse caso, deveria usar parênteses (ou, no mínimo, espaços). Imagine que você percorre o array em loop, e usa algo com a seguinte forma:

matriz[NDX(N, i, ++j)]=algum_valor; 


A expansão da macro vai resultar no seguinte:

matriz[i*N+++j]=algum_valor; 


Responda rápido -- quem vai ser incrementado: N ou j?

Parou para pensar? Na verdade, o incremento será sobre N, que certamente não era o que você queria. Esse é um dos motivos pelos quais eu coloquei o link para as postagens da C++ FAQ que dizem que “macros are evil”.

Outra possibilidade seria função inline, onde o compilador pode ou não substituir o código localmente. Ou não.

O pensamento parece ser mais simples para não criar o varal de memórias alocadas.
Inclusive no caso de hipermatrizes. NxNxNx ... xN.


Pois é... De certo modo, como disse em minha mensagem, há vantagens em fazer como você sugeriu. Mas há também vantagens em fazer com múltiplos níveis de ponteiros. A mais óbvia é usar a mesma sintaxe para acesso a elementos em matrizes declaradas como tais (e.g. “int matriz[10][20]”) e para matrizes emuladas através de ponteiro para ponteiro. Outra vantagem é poder apontar para uma linha inteira de uma só vez, permitindo passá-la como vetor para uma função que espere um vetor, referindo-se a ela simplesmente como “matriz[i]” (em vez de “matriz+N*i” ou “&matriz[N*i]”, que sua solução requereria). Outra operação que seria facilitada pelo duplo nível de alocações é que a eventual remoção ou inserção de uma linha no meio da matriz feita com ponteiro de ponteiro seria uma operação O(n) (deslocamento do ponteiro para o início de cada linha a ser movida), ao passo que com um array linear obrigaria a uma operação O(n²) (deslocamento de todos os elementos afetados).


7. Re: Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

Perfil removido
removido

(usa Nenhuma)

Enviado em 14/06/2016 - 14:10h

paulo1205 escreveu:

Outra operação que seria facilitada pelo duplo nível de alocações é que a eventual remoção ou inserção de uma linha no meio da matriz feita com ponteiro de ponteiro seria uma operação O(n) (deslocamento do ponteiro para o início de cada linha a ser movida), ao passo que com um array linear obrigaria a uma operação O(n²).


Faz sentido.
Valeu!

----------------------------------------------------------------------------------------------------------------
Nem direita, nem esquerda. Quando se trata de corrupção o Brasil é ambidestro.
(anônimo)

Encryption works. Properly implemented strong crypto systems are one of the few things that you can rely on. Unfortunately, endpoint security is so terrifically weak that NSA can frequently find ways around it. — Edward Snowden[/quote]


8. Re: Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

Paulo
paulo1205

(usa Ubuntu)

Enviado em 14/06/2016 - 21:30h

Note que minha intenção não é convencer que uma opção é melhor que a outra, pois ambas têm seus prós e contras, devendo um bom programador avaliar a mais adequada ao problema que ele tiverem maos. Nós acabamos nos estendendo por causa da discussão dos cuidados que cada uma delas requer.

De todo modo, creio que o importante mesmo era dizer o motivo pelo qual o programa original deu problema no Linux mas não no Windows. Isso foi feito na minha primeira resposta.


9. Re: Espaços de matriz sendo reescritos com lixo de memória [RESOLVIDO]

João Victor Vilela de Mendonça
Tyron

(usa Ubuntu)

Enviado em 20/06/2016 - 01:04h

Pessoal, muito obrigado! O paulo1205 estava totalmente certo, eu não estava alocando o espaço adequado para o ponteiro da matriz. Fiz como ele me indicou e funcionou perfeitamente!






Patrocínio

Site hospedado pelo provedor RedeHost.
Linux banner

Destaques

Artigos

Dicas

Tópicos

Top 10 do mês

Scripts