C語言函數指針的應用——自制諧波分析軟件

函數指針簡介

如果在一個大型C語言程序中要反覆調用函數,而調用的函數又不明確時,函數指針就是一個非常有用的東西。如果你的函數體內可以傳遞不同的函數,那就非得用函數指針實現不可。下面我就用一個例子給大家分享一下C語言函數指針的妙用。

格式介紹

格式爲:【基類型】(*function)([參數表列])
C語言函數指針就是一個指向函數地址的指針,它和普通的指針一樣也具有基類型,例如int *p,double *q,void *malloc與普通指針不同的是,它指向的不是特定類型的變量,而是函數地址,因此它後面有函數的參數表列。使用的時候需要注意,由於指針運算符比括號運算符的優先級低,因此需要把(*function)括起來(否則就是返回指針的函數)。C語言的語法比較複雜,舉一個函數指針的例子:

#include <stdio.h>
#include <stdbool.h>
int max(int a,int b);//最大值函數
int min(int a,int b);//最小值函數
int main()
{
	int a,b;
	int (*p)(int,int);//函數指針p
	bool choice;
	char c[4];//字符串
	printf("Please enter two integer(separate by space):  ");
	scanf("%d%d",&a,&b); 
	printf("Please choose a function(0: max,1: min):  ");
	scanf("%d%d",&choice);
	if(choice){p=min;c="min"}else {p=max;c="max"};//讓函數指針指向選擇的函數(地址)
	printf("%s=%d",c,p(a,b));//完成選擇函數的輸出
	return 0;
}

函數指針主要有兩種使用方法:

  1. 用函數指針指向某個函數做選擇(類似於上述的例子)
  2. 當做函數的參數傳遞(類似於下面的代碼),使程序模塊化更強,耦合性更弱當做函數的參數傳遞,使程序模塊化更強,耦合性更弱

下面是我編寫的一個計算一個指定方波的程序,裏面的雙線性積分函數對每一次諧波變量(int n)都一個返回諧波分量double amplify,在這裏用到了函數指針,傳遞一個方波的函數給這個積分函數,如果以後這個函數不是方波而是其他函數,增添和修改代碼都會非常方便!
在程序裏面我指定的值是電力電子課程裏面的三相全橋整流電路帶阻感性負載的變壓器二次側電流波形的方波(一週期脈動6次的交流電平),方波應該沒有2次和3次諧波,這樣只剩下了1次諧波和6k±1(k∈N*)次諧波,每次諧波含有率(和1次諧波的比值)爲1/(6k±1),可以看到後面附圖裏面確實非常準確,1,0.2,0.14286,…分別是1,1/5,1/7,…!
如果我們分析三相電壓型橋式逆變電路的Unn’中點電壓波形(一週期脈動6次的交流方波信號),可以得出同樣的結果。爲了讓輸出的諧波分析更加的美觀,我們讓諧波含有率低於1e-4的所有的次諧波都標藍,剩下的白色的纔是真正含有的諧波,供我們進行計算機仿真實驗驗證。

顏色頭文件

詳見顏色頭文件的博客,裏面有源代碼及其使用說明

計算機仿真

使用說明

在程序的開始界面輸入時刻表和對應的電平表,輸入分析基頻以及最高的分析諧波次數,程序即可進入運算狀態。由於使用的是CFT(連續傅里葉積分),可能計算會有點慢,大概3秒算10次諧波。如果諧波含有率很低會用藍色標識出來,其餘的諧波用白色標註,代表實質有的諧波含量,方便大家發現規律。輸入時刻表列和電平表列的規則如下:
如果一個方波的電平以-1和1兩個電平變化,並且週期爲π,則輸入週期爲π,改變電平的時刻表列爲0(不包括2個端點),電平表列爲-1 1.
如果一個方波的電平以-1,0,1,0,-1,0,…變化,正負電平的持續時間是0電平的2倍,週期爲π,則電平的時刻表列爲-5π/12,-π/12,π/12,5π/12(不包括2個端點),電平表列爲0 -1 0 1 0。
由於輸入的時刻表列不包括兩個端點,因此時刻表列總是比電平表列多一個數值,但是如果時刻表列加上2個端點時刻,正好形成和電平表列數目一樣多的時間段和電平表列相對應。輸入規則就是這樣的。

完整代碼

//#include <stdio.h>	//color.h已包括
#include <math.h>
#include <ctype.h> 
#include <malloc.h>
//#include <windows.h>	//color.h已包括
#include "color.h"
#define pi 3.14159265
double amplify(double (*function)(double x),int n);//雙線性積分函數,參數爲函數指針、上界、下界 
double RMS(double (*function)(double x));//對一個函數在週期內求有效值 
double square_wave(double x);
double period;//被分析方波的週期
double time[30]={0};//方波的時間時刻數組,首地址是從0附近的週期最左邊的座標開始的依次改變的電平處的值,只保留一個週期的 
double level[30]={0};//方波的電平數組,表示與時間區別對應的電平
int fund_f;//指定傅里葉分解時的基頻 
int count=0;//統計輸入個數 
//以上爲聲明 
int main()
{
	char t;//用於檢測字符是否數字,實現動態輸入 
	int i=0;//掃描計數偏移量
	int Nc;//截止諧波次數 
	double *AP=NULL;//用於存放幅值數組的指針 
	//以上爲定義變量部分 
	printf("***對用戶定義的方波進行傅里葉級數展開***\n請輸入方波的週期: ");
	scanf("%lf",&period);
	fflush(stdin);
	printf("請輸入方波的在(-T/2,T/2)週期改變電平的時間時刻(不含±T/2的時刻,用空格分隔):\n");
	while((t=getchar())!='\n')
	{
		if(isdigit(t)||t=='-')//浮點數都是以負號或者數字開頭的 
		{
			ungetc(t,stdin);
			scanf("%lf",time+i);//掃描的浮點數一次存入 
			count++;//統計輸入時間節點的個數 
			i++; 
		}
	 } 
	 *(time+i+1)=period/2;
	printf("請輸入方波的在上述改變時間時刻對應的電平值\n【說明】第一個電平對應(-T/2,t0),最後一個電平對應(tn,T/2),用空格分隔:\n");
	fflush(stdin);//循環結束,刷新標準輸入口 
	i=0;//偏移量清零 
	while((t=getchar())!='\n')
	{
		if(isdigit(t)||t=='-')//浮點數都是以負號或者數字開頭的 
		{
			ungetc(t,stdin);
			scanf("%lf",level+i);//掃描的浮點數一次存入 
			i++;
		}
	 } 
	fflush(stdin);//循環結束,刷新標準輸入口  
	printf("請輸入傅里葉分解的基頻: ");
	scanf("%d",&fund_f);
	printf("請輸入分析的諧波最高次數: ");
	scanf("%d",&Nc);
	AP=(double *)calloc(Nc,sizeof(double));
	if(AP==NULL)
	{
		printf("\n\r意外錯誤:內存分配失敗!\n");
		return 1;
	}
	printf("\n\r動態內存分配成功!\n");
	 for(i=0;i<=Nc;i++)
	 {
	 	AP[i]=amplify(square_wave,i);
	 	printf("\r正在計算中...%.2lf%%",100*(float)i/Nc);
	 }
	 printf("\n\r|諧波次數:\t振幅\n");
	 for(i=0;i<=Nc;i++)
	 {
	 	printf("|%d:\t%8.5lf",i,AP[i]);
	 	if((i+1)%5==0)printf("\n");
	 }
	 printf("\n\r|諧波含有率如下:\n");
	 for(i=0;i<=Nc;i++)
	 {
	 	if(AP[i]/AP[1]<1e-4)
	 	{
	 		setcolor(LIGHTBLUE);//無該次諧波的顯示爲黃色 
	 		printf("|%d:\t%7.3lf%%",i,100*AP[i]/AP[1]);
	 		
		}
	 	else
		{
		 	setcolor(WHITE);//有該次諧波顯示爲藍色 
			printf("|%d:\t%7.3lf%%",i,100*AP[i]/AP[1]); 	
		} 
	 	if((i+1)%5==0)printf("\n");
	 }
	 setcolor(WHITE);//還原默認色 
	 printf("\n\rRMS=%7.4lf,Fundamental=%7.4lf",RMS(square_wave),AP[1]);
	 system("pause");
	return 0;
} 
//根據電平數組和時間間隔數組查找對應時間的方波的電平 
double square_wave(double x)
{
	int i;
	for(i=0;i<count;i++)
	{
		if(x<*(time+i))return *(level+i);
	}
	return *(level+count);//如果均不是,返回level數組的最後一個值,即[tn,T/2]的值 
} 
double amplify(double (*function)(double x),int n)
{
	double sin_integral=0,cos_integral=0;//積分值
	double x0,x1;//雙線性積分需要兩個變量迭代計算
	for(x1=-period/2;x1<period/2;x0=x1,x1+=1e-5)
	{
		sin_integral+=0.5*1e-5*((*function)(x1)*sin(n*2*pi*fund_f*x1/period)+(*function)(x0)*sin(n*2*pi*fund_f*x0/period));
		cos_integral+=0.5*1e-5*((*function)(x1)*cos(n*2*pi*fund_f*x1/period)+(*function)(x0)*cos(n*2*pi*fund_f*x0/period));
	 } 
	 return sqrt(pow(sin_integral,2)+pow(cos_integral,2))/period;
}
double RMS(double (*function)(double x))
{
	double integral=0;
	double x0,x1;//採用雙線性積分
	for(x1=-period/2;x1<period/2;x0=x1,x1+=1e-5)
	{
		integral+=0.5*1e-5*(pow((*function)(x1),2)+pow((*function)(x0),2));		
	}
	return sqrt(integral/period);	
} 

部分效果圖

輸入

輸出
希望本文對您有幫助,謝謝閱讀。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章