Ir ao conteúdo
  • Cadastre-se

C Ponteiro como parâmetro de função em C


kampa896

Posts recomendados

Bom dia, pessoal. Estou com uma dúvida em uma questão da faculdade e a tutoria não está conseguindo me ajudar. Vou postar aqui com meus questionamentos e apontamentos do professor, pra ver se alguém consegue me explicar de uma outra forma. A questão está em anexo.

 

A minha dúvida foi a seguinte:

"Boa tarde, professor. Na atividade prática, a prática 3 pede para que seja executado um programa que calcula a área e o perímetro de um hexágono. E já pede que uma função siga um determinado protótipo. Porém, como parâmetro de entrada da função pré-determinada, já temos a própria área e perímetro como parâmetro de entrada. Não seria a função que teria que calcular esses valores? E o único parâmetro de entrada seria o lado do hexágono?"

 

A primeira resposta do tutor:

"Boa Tarde!

Verifique que o perimetro e a área são passados por referência.

Isso permite que a função retorne os dois valores calculados.

Dessa forma estão correto os parâmetros.

Bons Estudos;"

 

E eu continuei sem entender:

"Professor, boa tarde. Eu entendi que o perímetro e a área são passados por referência. Mas a minha dúvida é a seguinte. O exercício pede que a função calcule o perímetro e a área. Se esses duas informações, já são passadas pra função como parâmetros de entrada, mesmo que por referência, não é a função que calcula, a função ou imprimi ou faz outra coisa com essas informações. Se a função deve receber apenas o lado, como diz no enunciado, seria apenas o lado como parâmetro de entrada. A função que deveria retornar os valores do perímetro e da área, e não receber como parâmetros de entrada. O senhor conseguiu entender a minha dúvida? O protótipo da função deveria receber apenas o lado como parâmetro de entrada, pra poder retornar a área e o perímetro. Mas se a área e o perímetro são parâmetros de entrada, mesmo que através de ponteiros, essas duas informações são calculadas fora da função, em outra parte do programa. Como a função calcularia essas duas informações, se elas já são usadas como valores de entrada?"

 

E a segunda resposta, ainda sem entender...
Compreendi sua dúvida.

A função deve somente calcular a área e o perímetro e atribuir as variáveis passadas por referência.

A função é do tipo void ela não tem retorno pelo comando return devido ao tipo. 

Não deve-se interpretar que as variáveis passadas são parametros de entrada. Elas são os argumentos da função.

A função scanf() funciona da mesma maneira você informa o endereço da variável que irá receber a entrada do usuário.

O calculo ocorre de forma simples. Você indica que deseja alterar o conteúdo do ponteiro e atribui o valor desejado.

Qualquer dúvida, estou à disposição.

Bons Estudos;"

 

Desta forma, respondi novamente, com um exemplo de código:

Professor, boa noite. Eu vou assistir a aula online se eu conseguir sair a tempo do trabalho, e vejo se o senhor pode me ajudar. Porque eu ainda não estou entendendo a questão. O protótipo da função, pede três argumentos, correto? Uma variável tipo float 'l' e dois ponteiros, '*area' e '*perimetro'. Esses argumentos são colocados na função no momento que chamamos ela no programa principal, no 'int main()'. Os argumentos são de entrada, são utilizados para chamar a função no programa. Independente se serão utilizados valores ou referência, eles são colocados na função. O senhor disse na explicação: "A função scanf() funciona da mesma maneira você informa o endereço da variável que irá receber a entrada do usuário". É exatamente isso que estou perguntando. O scanf informa o endereço da variável que receber de entrada. Ou seja, se o argumento da função já pede três argumentos (lado, *area e *perimetro), independentes se são valores ou referência, depende da entrada que receber do usuário. Ou seja, o programa não calcula, já que pede inicialmente a entrada do usuário. Entendi que a função é void, não oferece retorno. Mas não entendi como a função pede três argumentos de entrada para chamar a função, sendo que dois deles deveria ser a própria função a calcular. Vou postar um código para o senhor ver, para tentar explicar mais uma vez a minha dúvida."

 

#include <stdio.h>
#include <math.h>

void calc_hexa(float la);

int main() {

	
	float lado;
	printf("Digite o lado do hexágono: ");
	scanf("%f", &lado);
	while(lado > 0){
		calc_hexa(lado);
		printf("Digite o lado do hexágono: ");
		scanf("%f", &lado);
	}
    return 0;
}

void calc_hexa(float la){
	float a = (3 * la * la * sqrt(3))/2;
	float p = 6 * la;
	printf("%.2f %.2f\n", a, p);
}

 

Se o programa tem que calcular a área e o perímetro, eles não podem estar nos argumentos da função, entendeu? Porque esses argumentos da função, ou o usuário tem que fornecer de entrada, ou tem que ter sido calculado em outra parte do programa, para ser usado como valor ou como referência. A função não pode ter em seus argumentos um dado que ela mesma tem que calcular. É essa a minha dúvida.

 

duvida.jpg

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

você já programa em alguma outra linguagem? É que algumas linguagens, como java por exemplo, os parametros só servem de entrada. E a saida é usando return dentro da função.

o senhor está com a ideia errada da coisa para o C. Em C, os parametros da função não quer dizer que só é entrada. Pode ser saida também, quando for ponteiro. Quer dizer que o ponteiro é um endereço (ou conjunto de entedeços) de memoria.

E a função, se você quiser mudar o conteudo desse endereço, vai permitir que esse valor alterado fique visivel de qualquer parte do programa, pois esse endereço pode ser obtido se a variavel onde você guardou esse endereço for acessivel.

Como no exemplo mostrado quando lhe fiz o exemplo fatorial3 do exercicio de recursividade que o senhor passou da ultima vez.

void calc_hexa(float la, float *a, float *p)
{
	*a = (3 * la * la * sqrt(3))/2;
	*p = 6 * la;
}

Não etstei, mas acredito que funcione e seja só isso...

  • Curtir 3
Link para o comentário
Compartilhar em outros sites

@codigo rápido tive algoritmo e lógica de programação em C e portugol. Mas não tinha visto ponteiros ainda. E o professor não conseguiu explicar da forma que você disse, simples. Obrigado!

Testei em um compilador online e deu alguns erros. Quando chegar em casa vou testar na IDE, obrigado!

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Do seu código
 

7 horas atrás, kampa896 disse:
void calc_hexa(float la){
	float a = (3 * la * la * sqrt(3))/2;
	float p = 6 * la;
	printf("%.2f %.2f\n", a, p);
}

 

Eu sempre escrevo essas coisas aqui e talvez até já tenha escrito para você mesmo: uma função que retorna void muitas vezes é um erro e quase sempre um desperdício. Se a função tem um propósito é preciso retornar algum resultado.

 

No seu código a função faz algo, e retorna um resultado: 

  • recebe um valor
  • calcula dois a partir deste
  • mostra na tela


E está tudo bem. No entanto é quase inútil.
 

  • E se não tiver uma tela?
  • E se o propósito for repetir esse cálculo para milhares de hexágonos, e passar para outra função apenas os polígonos que atendam a algum critério, como um valor máximo para a área?

Sobre sua expressão da dúvida

 

7 horas atrás, kampa896 disse:

Se o programa tem que calcular a área e o perímetro, eles não podem estar nos argumentos da função, entendeu? Porque esses argumentos da função, ou o usuário tem que fornecer de entrada, ou tem que ter sido calculado em outra parte do programa, para ser usado como valor ou como referência. A função não pode ter em seus argumentos um dado que ela mesma tem que calcular. É essa a minha dúvida.

 

Isso é curioso: para quem tem uma dúvida você parece estar com grande convicção, e essa convicção seria de que não podem existir argumentos "de saída" na função exceto pelo valor de retorno que ela não tem.

 

7 horas atrás, kampa896 disse:

Mas não entendi como a função pede três argumentos de entrada para chamar a função, sendo que dois deles deveria ser a própria função a calcular

 

Pois é: não existem argumentos de entrada. Só argumentos

 

7 horas atrás, kampa896 disse:

O scanf informa o endereço da variável que receber de entrada. Ou seja, se o argumento da função já pede três argumentos (lado, *area e *perimetro), independentes se são valores ou referência, depende da entrada que receber do usuário. Ou seja, o programa não calcula, já que pede inicialmente a entrada do usuário. Entendi que a função é void, não oferece retorno

 

Em seu programa
 

7 horas atrás, kampa896 disse:
float lado;
	printf("Digite o lado do hexágono: ");
	scanf("%f", &lado);
	while(lado > 0){
		calc_hexa(lado);

 

Não é a mesma coisa? Você usou o operador address-of, o popular & para pegar o endereço de lado e passar como argumento para a função.

 

Como faria para ler esses valores do teclado, área e parâmetro?

 

	float area;
	float parametro;
	printf("Digite area e parametro: ");
	int res = scanf("%f %f", &area, &parametro);
	if ( res != 2 ) {
        printf("não leu direito\n");
        return -1;
    }
	// ...

 

E não é a mesma coisa?

 

Sobre o protótipo do enunciado

 

image.png.2e6d2e671639a1bac0b8689038409639.png

 

Essa maneira de declarar as coisas é responsável por muitos problemas, para iniciantes e profissionais, e mascara as coisas.

 

Numa declaração você declara um nome e um tipo. Assim é C. 

 

Na linha acima está declarando l, area e perimetro. Esses são os nomes

 

E os tipos?

 

l é float, area é float* e perimetro é float*. Se tivesse declarado assim estaria mais claro o que é o que. Mas o asterisco tem nada menos que 3 usos em C. Para o compilador tanto faz.Ele não vai ler mesmo os espaços.

 

Na realidade tipo/nome de C o protótipo de calc_hexa é

 

    calc_hexa( float,float*,float* );

 

E poderia escrever

 

void calc_hexa( float lado, float*    area, float*    perimetro )
{
    *area      = (3 * lado * lado * sqrt(3)) / 2;
    *perimetro = 6 * lado;
}

 

Onde você pode ver em 5 linhas os 3 usos do asterisco em C.

 

E calc_hexa? calc_hexa é um nome então deve ter um tipo, certo? É claro. É C. 

Que tipo é esse? calc_hexa é void()(float,float*,float*), o que seu compilador pode te dizer a qualquer hora. Vou deixar no exemplo uma declaração.

 

Você poderia escrever

 

#include <math.h>
#include <stdio.h>

typedef void FuncaoEx(float, float*, float*);

FuncaoEx calc_hexa;

int main(void)
{
    float       area      = 0.;
    float       perimetro = 0.;
    const float lado[]    = {2., 3., 4., 5.};
    int         N         = sizeof(lado) / sizeof(lado[0]);
    for (int i = 0; i < N; i += 1)
    {
        calc_hexa(lado[i], &area, &perimetro);
        printf(
            "\
    Teste %d de %d:\n\
    Lado: %f Area: %f Perimetro: %f\n\
\n",
            1 + i, N, lado[i], area, perimetro);
    }
    return 0;
}

void calc_hexa(float lado, float* area, float* perimetro)
{
    *area      = (3 * lado * lado * (float)sqrt(3)) / 2;
    *perimetro = 6 * lado;
}

 

Que mostra
 

    Teste 1 de 4:
    Lado: 2.000000 Area: 10.392304 Perimetro: 12.000000

    Teste 2 de 4:
    Lado: 3.000000 Area: 23.382685 Perimetro: 18.000000

    Teste 3 de 4:
    Lado: 4.000000 Area: 41.569218 Perimetro: 24.000000

    Teste 4 de 4:
    Lado: 5.000000 Area: 64.951904 Perimetro: 30.000000

 

E pode ajudar a entender a mecânica disso.

 

Para ilustrar, em outra linguagem (Ada) você poderia escrever
 

procedure calc_hexa( lado: float in,
                     area: float out,
                     perimetro: float out) 
is
begin
    area      = (3 * lado * lado * Sqrt(3)) / 2;
    perimetro = 6 * lado;
end

 

E tem esses atributos in, out e pode usar para deixar claro o que é entrada e o que é saída. Acho que é disso que você sente falta aqui.

 

De volta a C

 

O comum em C é usar encapsulamento e colocar os parâmetros dentro de uma estrutura e retornar uma estrutura ou o endereço dela para maior performance. Assim é melhor porque 

  • pode mudar o conteúdo da estrutura sem mudar o protótipo da função
  • pode retornar megabytes de coisas usando só um ponteiro

 

TL;DR
 

typedef struct
{
    float area;
    float perimetro;

}   Resultado;

Resultado calc(float);

 

Algo assim.

 

usando recortar e colar no exemplo que te mostrei, pode ver como seria dos dois modos

 

#include <math.h>
#include <stdio.h>

typedef void FuncaoEx(float, float*, float*);

FuncaoEx calc_hexa;

typedef struct
{
    float area;
    float perimetro;

}   Resultado;

Resultado calc(float);

int main(void)
{
    float       area      = 0.;
    float       perimetro = 0.;
    const float lado[]    = {2., 3., 4., 5.};
    int         N         = sizeof(lado) / sizeof(lado[0]);
    for (int i = 0; i < N; i += 1)
    {
        calc_hexa(lado[i], &area, &perimetro);
        printf(
            "\
    Teste %d de %d:\n\
    Lado: %f Area: %f Perimetro: %f\n\
\n",
            1 + i, N, lado[i], area, perimetro);
    }

    printf("\n\nDo outro jeito\n\n");
    Resultado A;
    for (int i = 0; i < N; i += 1)
    {
        A = calc(lado[i]);
        printf(
            "\
    Teste %d de %d:\n\
    Lado: %f Area: %f Perimetro: %f\n\
\n",
            1 + i, N, lado[i], A.area, A.perimetro);
    }
    return 0;
}

void calc_hexa(float lado, float* area, float* perimetro)
{
    *area      = (3 * lado * lado * (float)sqrt(3)) / 2;
    *perimetro = 6 * lado;
}

Resultado calc(float lado)
{
    float area      = (3 * lado * lado * (float)sqrt(3)) / 2;
    float perimetro = 6 * lado;
    return (Resultado){area, perimetro};
}

 

E é claro que o resultado é o mesmo.

 

Note que podia escrever tambem calc() como

 

Resultado calc(float lado)
{
    return (Resultado)
    {
        (float)((3 * lado * lado * (float)sqrt(3)) / 2),
        (float)(6*lado)
    };
}

 

Porque não precisa mesmo de variáveis locais. Basta o return.

 

Rode isso em sua máquina e procure entender o exemplo e a construção. Isso é importante em C.

 

  • Amei 1
Link para o comentário
Compartilhar em outros sites

Não estou desmerecendo os tutores da faculdade, longe de mim julgar o conhecimento de alguém, mas a didática de vocês está me ensinando muito mais.

 

Obrigado a todos, meus amigos. @arfneto e @codigo rápido.

 

Entendi agora. Quando estudei algoritmos e lógica de programação, mesmo usando C, não tive ponteiros. E o professor desta vez não conseguiu explicar da forma que vocês fizeram, e eles não entendiam a minha dúvida. Agora ficou claro que argumentos em C, em uma função, não quer dizer que precisa ser parâmetro de entrada.

 

Obrigado mais uma vez!

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

9 horas atrás, kampa896 disse:

Se o programa tem que calcular a área e o perímetro, eles não podem estar nos argumentos da função

Seria assim no caso da passagem por valor quando a função recebe apenas uma cópia do valor passado como argumento. Mas como esses dois parâmetros são ponteiros, a função vai receber o endereço das variáveis (declaradas em main) e portanto qualquer atribuição que fizer a elas dentro de calc_hexa terá efeito efetivo nelas e por isso terão seus valores alterados após a chamada da função.

  • Curtir 1
Link para o comentário
Compartilhar em outros sites

Crie uma conta ou entre para comentar

Você precisa ser um usuário 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 comunidades 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

×
×
  • Criar novo...