Stack Overflow em Português Asked by Igor PTZ on December 30, 2021
Estou tendo um problema em relação a ponteiros para funções. Declarei os ponteiros e armazenei em um vetor, porém na hora de chamar as funções através dos ponteiros o compilador apresenta um erro que ainda não consegui identificar.
Estou utilizando a IDE Code::Blocks 17.12 no Windows 7.
Segue o código:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define PI 3.14
void circunferenciaCirculo(double raio);
void areaCirculo(double raio);
void volumeEsfera(double raio);
int main()
{
system("color 3");
void (*calculo[3])(double raio) =
{circunferenciaCirculo,areaCirculo,volumeEsfera};
int sentinela = 0;
double raio = 0.0;
while(sentinela != 4){
system("cls");
printf("Opcoes de calculonn1-Circunferencia de circulon2-Area de
circulon3-Volume de esferan4-SairnnOpcao:");
scanf("%d",&sentinela);
switch(sentinela){
case 1:
system("cls");
printf("Digite o raio:");
scanf("%lf",&raio);
calculo[0];
break;
case 2:
system("cls");
printf("Digite o raio:");
scanf("%lf",&raio);
calculo[1];
break;
case 3:
system("cls");
printf("Digite o raio:");
scanf("%lf",&raio);
calculo[2];
break;
default:
printf("Opcao invalida!n");
system("PAUSE");
break;
}
}
return 0;
}
Obs: postei apenas o código do main()
, se precisar colocar a declaração das funções eu edito depois.
Além desse problema que eu estou tendo, também tenho outra dúvidas sobre ponteiro para funções.
1) Existe alguma diferença de desempenho entre chamar a função normalmente e chamar através de ponteiros?
2) Em que situações ponteiros para funções são utilizados?
O maior problema é que você não está chamando função alguma, a chamada sempre ocorre com os parênteses, igual a uma função normal, neste caso o nome da variável é igual ao que seria o nome da função. Mas tem varias outras coisas que podem ser melhoradas no seu código, inclusive eliminar o switch
, assim faz um pouco mais de sentido em usar esta forma. O que eu não usaria em condições normais, não há necessidade neste caso, um switch
com as chamadas diretas seriam mais eficiente, legível sem comprometer funcionalidade, embora seja interessante como um exercício. Só espero que não torne um vício.
#include <stdio.h>
#include <math.h>
#define PI 3.14
void circunferenciaCirculo(double raio);
void areaCirculo(double raio);
void volumeEsfera(double raio);
int main() {
system("color 3");
void (*calculo[3])(double raio) = {circunferenciaCirculo, areaCirculo, volumeEsfera};
int sentinela = 0;
while (sentinela != 4) {
system("cls");
printf("Opcoes de calculonn1-Circunferencia de circulon2-Area de circulon3-Volume de esferan4-SairnnOpcao:");
scanf("%d", &sentinela);
if (sentinela < 1 || sentinela > 4) {
printf("Opcao invalida!n");
system("PAUSE");
continue;
}
if (sentinela == 4) break;
system("cls");
printf("Digite o raio:");
double raio = 0.0;
scanf("%lf", &raio);
calculo[sentinela - 1](raio);
}
}
Veja funcionando no ideone. E no repl.it. Também coloquei no GitHub para referência futura.
Dá para melhorar mais.
- Existe alguma diferença de desempenho entre chamar a função normalmente e chamar através de ponteiros?
Sim, há um indireção extra do ponteiro. Embora possa ser trivial em certas situações, pode custar bastante em outras, especialmente na primeira chamada ou em chamadas subsequentes depois de muito tempo, onde a carga do endereço onde está a função está na memória e não mais em cache, o que é uma operação bastante lenta. Otimização hoje em dia na maioria dos problemas que resolvemos é mais economizar acesso à memória do que economizar ciclos de processamento, e tende a piorar com memórias não voláteis que podem ter a RAM um pouco mais lenta em benefícios de ter persistência automática e muito rápida.
A chamada à função já é uma indireção, quase obrigatória.
- Em que situações ponteiros para funções são utilizados?
Sempre que você precisar determinar mais tarde qual é a função exata que deve executar.
Costumo dizer que só existem dois motivos para deixar uma execução dinâmica, ou seja que só seja resolvida em tempo de execução:
falta uma informação em tempo de compilação.
Ela só estará disponível quando o código estiver executando, seja por digitação do usuário, vindo de fontes externas como arquivos, bancos de dados, redes ou outras formas de entrada de dados, incluindo, é claro, dispositivos que convertem sinal analógicos para digital como câmeras, microfones e outros sensores.
Em alguns casos a informação pode estar disponível antes da execução mas depois da compilação normal. É possível fazer otimizações, mas geralmente não compensa. Em outros pode ser que sim. Há casos que usa-se profile guided optimization para mudar o código, isto pode ser aproveitado para otimizar outras coisas.
Há casos que é possível resolver antes, mas é preferível deixar para resolver depois para dar flexibilidade ao sistema, isto não é obrigatório resolver em tempo de execução, mas costuma ser uma solução adotada, pra mim de forma abusiva em muitos casos, mas tem alguma justificativa.
Muitas variações de execução que para resolver em tempo de compilação geraria muitos caminhos de execução que poderia até economizar ciclos de execução por eliminar algumas decisões e buscas por dados em tempo de execução, porém com muito mais código diferente para executar suja-se muito o chance fazendo que pequenas mudanças de contexto obrigue acesso à RAM quando a memória do processador que é muito mais rápida poderia ter o que precisa executar. Isso vale para repetições de laços, especializações de objetos e funções.
Se você sabe que terão poucas repetições de um corpo pequeno pode ser interessante desenrolar a repetição, e bons compiladores fazem isto para você, mas não compensa para muitos. O mesmo vale para trabalhar com diversos objetos com estruturas semelhantes mas não idênticas (herança). Pode ser mais rápido se tiver instâncias diferentes para cada tipo mas só se for certamente 1, 2, 3 ou 4, muito quem sabe 5. Se tiver muitos é melhor usar uma indireção. O mesmo vale para funções.
Se você sabe que função quer chamar não tem que pensar em ponteiro para função. Se criar algo genérico que pode ter N funções diferentes sendo chamadas em determinado momento pode ser melhor usar o ponteiro, assim existirá um só código que pega qual função deve executar naquele momento de acordo com o ponteiro e faz a chamada. Se você tiver só 4 possibilidades pode ser mais interessante criar 4 algoritmos distintos que fazem a mesma coisa mas na hora de chamar a função que deseja chame uma diferente em cada uma dessas instâncias de código. Tem linguagem que facilita muito isso, como C++. Outras tem que fazer na mão e gera repetição, em alguns casos até viola o DRY.
Essa decisão sempre é uma comodidade e/ou otimização e não uma necessidade absoluta como é o motivo anterior.
A chamada da função normal pode ser "otimizada" e eliminar todas indireções, porém teria que copiar o código da função em cada local onde ela seria chamada. Em alguns casos pode compensar por isso alguns compiladores fazem inline quando compensa, ou podem ser forçados fazer, mas se fizer em exagero o tiro pode sair pela culatra.
Então é uma forma de parametrizar o conteúdo, ou seja, você torna uma função que tem um endereço constante em endereço variável, como fez no seu código, ainda que sem ganho real, no meu ainda teve o ganho de algumas linhas à custa de menos legibilidade e performance, ainda que desprezível para esse tipo de código.
Este mecanismo é muito semelhante ao que se usa em orientação a objeto. No fundo o polimorfismo é assim por baixo dos panos.
Answered by Maniero on December 30, 2021
Suas declarações e definições estão corretas. O problema está na hora de fazer a chamada das funções por meio do ponteiro.
As seguintes linhas não estão surtindo efeito nenhum no seu codigo, veja:
calculo[0];
...
calculo[1];
...
calculo[2];
Deveriam ser, respectivamente, alteradas para:
calculo[0]( raio );
...
calculo[1]( raio );
...
calculo[2]( raio );
Answered by Lacobus on December 30, 2021
Get help from others!
Recent Questions
Recent Answers
© 2024 TransWikia.com. All rights reserved. Sites we Love: PCI Database, UKBizDB, Menu Kuliner, Sharing RPP