Ir ao conteúdo

Posts recomendados

Postado

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
Postado

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 4
Postado

@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 2
Postado

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.

 

  • Obrigado 1
  • Amei 1
Postado

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 2
Postado
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 2
  • 2 anos depois...
Postado
Em 30/09/2021 às 14:35, Midori disse:

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.

São tipos com aritmética especifica.

 

Ponteiro é um tipo como int, só que derivado de int, não somente de int, de

todas as primitivas e dos tipos que o programador definir e construir a partir

do que já existe.

 

O valor de um ponteiro precisa ser sempre, sempre um endereço, porque

desta maneira será avaliado, senão tem algo errado.

 

[Filosofia]
int *p, p é uma variável 
      , sendo que 'p' pode aponta para outra
      , porque seu valor é também um endereço

 

  • Curtir 1

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...