Ir ao conteúdo
  • Cadastre-se
diogo moura

C Por que esse código retorna um vetor

Recommended Posts

Não sou muito bom com ponteiro, porém meu professor mostrou um código e até agora não entendi por que a função retornou um vetor com os valores alterados

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

int *funcao(int vetor[]);

int main(int argc, char** argv)
{
	int vetor[10], *ptr;
	for(int i = 0; i < 10; i++)
	{
		scanf("%d", &vetor[i]);
	}
	ptr = funcao(vetor);
	for(int i = 0; i < 10; i++)
	{
		printf("%3d",ptr[i]);
	}
	printf("\n");
	return 0;
}

int *funcao(int vetor[])
{
	int *n;
	n = (int*)malloc(sizeof(int) * 10);
	for(int i = 0; i < 10; i++)
	{
		n[i] = vetor[i] * 2;
	}
	return n;
}

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Primeiramente existem 2 erros nesse código:

 

1- Não se usa casting no retorno do malloc().

 

No código postado está usando cast para ponteiro de inteiro (int*) antes do malloc, isso é desnecessário, o tipo já é estabelecido pelo tipo do ponteiro quando ele foi definido como "int *n;" implicando que é do tipo inteiro. Ou seja:

Não fazer:
n = (int*)malloc(sizeof(int) * 10);

O correto:
n = malloc(sizeof(int) * 10);

Melhor ainda:
n = malloc(10 * sizeof *n);

Nesse caso acima estamos usando o tamanho do próprio ponteiro,
note que isso é genérico e evita erros, visto que não repete o
tipo, pois seria um problema se colocar o tipo errado no malloc
por engano, como:
long int *n;
n = malloc(10 * sizeof(int)); //Errado, o tipo certo seria long int

Isso é um engano comum, e infelizmente consta em muitos livros e textos didáticos. Mas essa é primeira das dúvidas mais frequentes sobre C no fórum stackoverflow (um dos maiores fórum de programação): https://stackoverflow.com/questions/605845/do-i-cast-the-result-of-malloc

 

Se usado corretamente, com o tipo certo, o casting não causará problemas, mas não é necessário e aumenta o risco de erro, logo não o inclua e evite dores de cabeça.

 

 

 

2- SEMPRE se deve liberar a memória que foi alocada dinamicamente, ou seja que foi alocada usando malloc, quando não precisar mais dela, usando a função free().

 

No caso deste programa deveria haver uma linha contendo o comando "free(ptr);" antes do "return 0;" no main.

 

Não liberar a memória reservada é o que chamamos de vazamento de memória (memory leak), e é uma das maiores causas de problemas com programas usando memória demais no computador, pois o programador programou errado e deixou espaço da memória ocupado sem estar utilizando de fato. Isso pode sair do controle rapidamente quando fizer programas complexos que reusam partes de código várias vezes, com um erro desse tipo no meio. Então de preferência já coloque o free() em algum lugar subsequente do código assim que colocar o malloc(), para não esquecer depois.

 

 

 

O código corrigido:

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

int *funcao(int vetor[]);

int main(int argc, char** argv)
{
	int vetor[10], *ptr;
	for(int i = 0; i < 10; i++)
	{
		scanf("%d", &vetor[i]);
	}
	ptr = funcao(vetor);
	for(int i = 0; i < 10; i++)
	{
		printf("%3d",ptr[i]);
	}
	printf("\n");
	free(ptr);
	return 0;
}

int *funcao(int vetor[])
{
	int *n;
	n = malloc(sizeof *n * 10);
	for(int i = 0; i < 10; i++)
	{
		n[i] = vetor[i] * 2;
	}
	return n;
}

 

 

 

Agora vamos à explicação:

 

O que a função faz é retornar um ponteiro, e esse ponteiro está apontando para a primeira posição de um espaço da memória que foi alocado dinamicamente (usando malloc).

 

Normalmente o espaço da memória que é alocado para as variáveis dentro de uma função é liberado quando se retorna desta função, por exemplo se você criar um vetor normal int vetor[100] dentro de uma função, o espaço onde estes dados são guardados na memória será liberado quando executar o comando return na função, e então esse espaço da memória poderá ser utilizado para armazenar outros dados.

 

Isso não acontece com memória que é alocada dinamicamente, esse espaço só é liberado quando você dá a instrução para que ele seja liberado (usando free()), o que faz essencial que você sempre se certifique de liberá-lo.

 

Isto nos permite alocar memória dinamicamente dentro de uma função ( malloc(sizeof(int) * 10) reserva 10 posições da memória com tamanho de int, ou seja um vetor de inteiros com 10 posições), fazer um ponteiro apontar para esse espaço da memória, e depois retornar o ponteiro para ser armazenado em outra variável ponteiro na função que chamou esta função, resultando que esse ponteiro também apontará para este espaço da memória, e portanto pode acessar os dados que estão armazenados lá.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Ola ... !

Como mencionado, o vetor permanece com seus valores inalterados porque 'um novo vetor' recebe o produto de vetor [ ] por 2.

Ponteiros não são vetores e vetores não são ponteiros; cada tipo com sua aritmética apropriada, contudo não são vetores (array), mas desempenha maior parte de suas funções. Principal diferença é que quando bem definido um vetor (array) tem tamanho fixo de dados.

 

Fragmento_de_Código

6 horas atrás, diogo moura disse:

int *funcao(int vetor[]) 
{ 
  int *n; 
  n = (int*)malloc(sizeof(int) * 10); 
  for(int i = 0; i < 10; i++) 
  { 
    n[i] = vetor[i] * 2; 
  } 
  return n; 
}

 

 

Analisando função com relação seguração de dados um risco é a inexistência de parâmetro Size Length que costumeiramente acompanha esse tipo de protótipo. De modo geral visto desta forma:

//Protótipos mais comuns para se declara com segurança rotinas de vetores

int * copiar_( int vetor [], unsigned len );
int * copiar_( unsigned len, int vetor [len] );
int * copiar_( int vetor [static 10] );
  • essa última você encontrará em alguns compêndios clássicos de C' de boa qualidade.

 

De maneira bastante criteriosa a rotina do professor fica também assim:

int * funcao( unsigned tam, int vetor [tam] )
{
  int * n;
  n = malloc( (sizeof * n) * tam );
  for(unsigned i = 0; i < tam; i++)
  {
    * (n + (i)) = vetor [i] * 2;
  }
  return n;
}

Observe que para ressaltar as devidas notações busquei deixar bem claro que n é um ponteiro dando-lhe a escrita mais correta que pude aprender, tudo com parenteses fazendo alusão aos vetores. Enquanto vetor faço o mesmo deixando da maneira correta encontrada na função do professor.

 

~~ / ~~

Compartilhar este post


Link para o post
Compartilhar em outros sites

@isrnick Opa. Parece q me deparei com algo novo até então p mim:

Em 07/05/2018 às 19:30, isrnick disse:

1- Não se usa casting no retorno do malloc().

 

No código postado está usando cast para ponteiro de inteiro (int*) antes do malloc, isso é desnecessário, o tipo já é estabelecido pelo tipo do ponteiro quando ele foi definido como "int *n;" implicando que é do tipo inteiro

 

Isso vale para todos os casos? 

Se é isso então o q acontece é um casting implícito para o tipo do ponteiro?

adicionado 4 minutos depois

Pelo que vi no link tem o seguinte comentário com respeito ao casting explícito para o retorno do malloc():

"É desnecessário, pois void * é promovido automaticamente e com segurança para qualquer outro tipo de ponteiro neste caso."

Seria isso mesmo?

Compartilhar este post


Link para o post
Compartilhar em outros sites

@giu_d Malloc já retorna um ponteiro, veja o protótipo do malloc:

void* malloc( size_t size );

Fonte: http://en.cppreference.com/w/c/memory/malloc

 

E a variável o tipo do ponteiro que armazena o retorno do malloc já diz o comprimento dos dados:

int *n = malloc( 10 * sizeof *n );

Nesse caso o comprimento é int, e n[3] ou *(n + 3) por exemplo usa 3 comprimentos de int para achar a posição do dado na memória.

Compartilhar este post


Link para o post
Compartilhar em outros sites

@isrnick  Essa mesma regra é indiferente do padrão usado para um código em C?

adicionado 1 minuto depois

Ou seja: vale tanto para o padrão ANSI como qualquer outro?

adicionado 5 minutos depois

Na prática:

Fazer algo como:

Lista *novo = (Lista *) malloc(sizeof(Lista));

Seria o mesmo q (com uma melhor abordagem):

 

Lista *novo = malloc(sizeof(Lista));

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
2 minutos atrás, giu_d disse:

@isrnick  Essa mesma regra é indiferente do padrão usado para um código em C?

Você diz a versão do padrão C (C90, C99, C11) ? Sim, é indiferente, isso sempre funcionou assim.

 

Aliás, o site cppreference indica as diferenças entre versões, é um bom site pra consultar (não há nenhuma anotação sobre mudança de comportamento disso na página do malloc).

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@isrnick então, esse comentário q peguei do site anterior q passou está correta:

"É desnecessário, pois void * é promovido automaticamente e com segurança para qualquer outro tipo de ponteiro neste caso."

??

adicionado 5 minutos depois

Esse comentário é do site stackoverflow q mencionou antes

adicionado 9 minutos depois

Para entender melhor o exemplo que passei tem um typedef aí:

typedef struct lista Lista;

 

Compartilhar este post


Link para o post
Compartilhar em outros sites
4 minutos atrás, giu_d disse:

@isrnick então, esse comentário q peguei do site anterior q passou está correta:

"É desnecessário, pois void * é promovido automaticamente e com segurança para qualquer outro tipo de ponteiro neste caso."

??

Você está falando do post no fórum stackoverflow...

Sim, não é à toa que a resposta dele tem 1877 votos positivos. ;)

Essa é primeira das dúvidas mais frequentes sobre C no stackoverflow, o tópico de C com mais votos em um dos maiores, se não o maior, fórum de programação.

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites

@isrnick   Maravilha! Bom ter aprendido a respeito disso. Até então considerava q era algo como "obrigatório" o uso do casting explícito

adicionado 4 minutos depois

só para ter uma ideia esse exemplo q passei é de um tópico da PUC - Rio sobre lista encadeada

adicionado 8 minutos depois

Nesse tutorial (não tópico) da PUC é mostrado assim:

typedef struct lista Lista;

....
  
  
Lista *novo = (Lista *) malloc(sizeof(Lista));  

 

  • Curtir 1

Compartilhar este post


Link para o post
Compartilhar em outros sites
26 minutos atrás, giu_d disse:

só para ter uma ideia esse exemplo q passei é de um tópico da PUC - Rio sobre lista encadeada

adicionado 8 minutos depois

Nesse tutorial (não tópico) da PUC é mostrado assim:


typedef struct lista Lista;

....
  
  
Lista *novo = (Lista *) malloc(sizeof(Lista));  

 

Sim, infelizmente tem muito material didático explicando assim, esse é o problema, e o motivo dessa dúvida ser a mais frequente no stackoverflow.

  • Curtir 1

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

Criar uma conta

Crie uma nova conta em nossa comunidade. É fácil!

Crie uma nova conta

Entrar

Já tem uma conta? Faça o login.

Entrar agora





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

×