官术网_书友最值得收藏!

1.6 C語言基礎

Fluent UDF采用C語言進行編寫,本節簡單介紹在UDF中經常會用到的C語言知識,本節部分內容來自UDF手冊。

1.6.1 C語言中的注釋

C語言中的注釋利用/**/來實現。例如:

/*這是一個注釋*/

注釋也可以跨行實現,如:

/*這是一個
跨行注釋*/

注 意

在編寫UDF的過程中,不能把DEFINE宏(如DEFINE_PROFILE)放置在注釋中,否則會引起編譯錯誤。

1.6.2 基本數據類型

Fluent UDF解釋器支持的標準C數據類型如下:

Int:整型,存儲形如1、2、3之類的整數。

long:長整型,存儲數據與int類似,但范圍更廣。

float:浮點型,存儲小數,如1.234等。

double:雙精度浮點型,與float類似。

char:字符型,如’a’、’b’、’c’等。

Fluent UDF中還有real型,其實這是Fluent自定義的數據類型,在雙精度求解器中,real類型與double類型相同,而在單精度求解器中,real類型等同于float類型。UDF自動進行轉換,因此在需要浮點數時,可以全部采用real類型。

1.6.3 常數

在C語言中可以利用#define來定義常數。需要注意的是,定義為常數類型后,該變量的值不能改變。如:

#define WALL_ID 5
#define YMIN 0.0
#define YMAX 0.4

這樣定義完畢后,WALL_ID的值不能再發生改變,因此如下的語句會引發編譯錯誤:

WALL_ID = WALL_ID +1;

1.6.4 全局變量和局部變量

變量用于存儲數據。所有變量都包含類型、名稱以及值,有時候還包含存儲標記,如靜態變量和外部變量。C語言中所有的變量在使用之前都必須聲明,這樣C編譯器才會知道該如何為此變量分配內存。

C語言中的全局變量定義在函數的外部,該變量可以被源文件中所有的函數引用。全局變量如果未被聲明為靜態變量,還可以被外部函數引用。如下面程序中的全局變量聲明:

#include ”udf.h”
real volume; /*此處定義的是全局變量*/
DEFINE_ADJUST(vol,domain)
{
    /*此處可以訪問變量volume*/
}

局部變量一般定義在函數體內,其只在函數體內起作用,在函數體外無法被訪問到。如下面程序中的局部變量定義:

DEFINE_PROPERTY(cell_viscosity, cell, thread)
{
    real mu_lam;      /*局部變量 */
    real temp = C_T(cell, thread);  /* 局部變量 */
    if (temp > 288.)
      mu_lam = 5.5e-3;
    else if (temp > 286.)
      mu_lam = 143.2135 - 0.49725 * temp;
    else
      mu_lam = 1.;
    return mu_lam;
}

1.6.5 外部變量

當在某個源文件中定義了一個未加static的全局變量后,若想在另一個源文件中調用此變量,此時可以使用外部變量聲明來實現。采用如下聲明:

extern real volume;

注 意

extern聲明只能用于編譯型UDF中。

以下是一個利用extern的案例。

假設在源文件file1.c中定義了全局變量:

#include ”udf.h”
real volume;
DEFINE_ADJUST(compute_volume, domain)
{
    volume = ....
}

若其他的源文件想要利用此全局變量 volume,此時可以創建頭文件,并將變量 volume聲明為extern變量,如創建頭文件extfile.h,寫入內容:

extern real volume;

之后就可以在其他的源文件中使用此變量volume了,如在源文件file2.c中:

#include ”udf.h”
#include ”extfile.h”
DEFINE_SOURCE(heat_source,c,t,ds,eqn)
{
     real total_source = ...;
     real source;
     source = total_source/volume;
     return source;
}

提示:外部變量使用起來很麻煩,也很容易出錯,如果對其不甚了解,建議不要使用。

1.6.6 靜態變量

靜態變量(聲明時添加static關鍵字)在用于局部變量或全局變量時具有不同的作用。局部變量被聲明為static時,當函數返回后變量并不銷毀,變量的值依舊被保留。全局變量被聲明為static時,該變量能夠被此源文件中的所有函數調用,但不能被其他源文件中的函數調用。實際上是變量被隱藏了。

例如在文件mysource.c中有如下代碼:

#include ”udf.h”
static real abs_coeff = 1.0; /*靜態全局變量*/
/* 此變量只能被本文件中的其他函數調用 */
DEFINE_SOURCE(energy_source, c, t, dS, eqn)
{
   real source; /* 局部變量*/
   int P1 = ....; /* 局部變量*/
/*變量只能被當前函數調用,但在函數返回時變量并不釋放 */
   dS[eqn] = -16.* abs_coeff;
   source =-abs_coeff *(4.* SIGMA_SBC );
   return source;
}
DEFINE_SOURCE(p1_source, c, t, dS, eqn)
{
   real source;
   int P1 = ...;
   dS[eqn] = -abs_coeff;
   source = abs_coeff *(4.* SIGMA_SBC);
   return source;
}

提示:與全局變量類似,靜態變量也盡量少用,容易造成不必要的麻煩。

1.6.7 用戶自定義數據類型

C語言允許用戶自己定義數據類型,通過使用結構體及typedef關鍵字。如定義類型:

typedef struct list
{
    int a;
    real b;
    int c;
}mylist;
mylist x,y,z;

上例定義了一個結構體類型mylist,并定義了三個結構體變量x、y、z。

1.6.8 強制轉換

在C語言中,有時需要對類型進行強制轉換,如將浮點型強制轉換為整型,如下例程序:

int x =1;
real y=3.1415926;
int z=x+(int)y;

計算完畢后,z=4。

1.6.9 函數

C語言中的函數執行獨立的任務。函數能夠被同一源文件中的其他函數調用,也可以由源文件之外的函數調用。

函數定義包含函數名以及被傳遞給函數的零個或多個參數列表。函數包含一個包含在大括號內的主體,主體中包含執行任務的指令。函數可以返回特定類型的值。

函數返回特定數據類型的值(例如,實數),如果類型為void,則不返回任何值。要確定DEFINE宏的返回數據類型,可查看udf.h文件中宏的相應#define語句。

1.6.10 數組

C語言中數組變量定義為name[size],其中name為數組變量的名稱,size為數組中存儲的單元數量。C語言中數組索引從0開始。

int a[10], b[10][10];
real rad[5];
a[0] = 1;
rad[4] = 3.14159265;
b[10][10] = 4;

1.6.11 指針

指針是一種存儲變量內存地址的變量。換句話說,指針是一個變量,這個變量指向另外一個變量的內存地址。指針變量的聲明: 

int *ip;/*定義指針變量ip*/

定義了指針變量后,可以利用取址運算符將其他變量的地址賦予指針變量,如:

int *ip;
ip =&a;

也可以為指針變量賦值,如:

*ip =4;

當指針作為函數的參數,此時為傳址調用,在函數體內修改指針參數的值,會改變調用函數時傳遞的參數的值。此功能可以實現一個函數返回多個值。

如下的C程序:

#include <stdio.h>
int add(int *a,int b)
{
    int sum = 0;
    sum = *a + b;
    *a = 5;
    return sum;
}
int main() {
    int *ip;
    int a = 1;
    int b = 2;
    int sum = 0;
    ip = &a;
    sum = add(ip,b);
    printf(“sum=%d,a=%d\n”,sum,a);
    return 0;
}

輸出結果:

sum=3,a=5

傳遞的參數值被函數體內的程序改變。

1.6.12 流程控制

C語言中可以用邏輯判斷和循環來進行流程控制。

(1)if語句

if語句用于邏輯判斷。可寫成:

if(邏輯判斷表達式)
{
    語句塊;
}

例如:

if(q!=1)
{
    a=0;
    b=1;
}

若邏輯判斷存在多個分支,可以采用if-else結構,如:

if(x<0)
{
    y = x/50;
}
else(x>=0 && x<3)
{
    x=-x;
    y = x/25;
}
else
{
    x= 0;
    y = 0;
}
(2)for循環

for語句常用于循環表達。

int i,j,n=10;
for(i=1;i<n;i++)
{
    j = i*i;
    printf(“%d%d\n”,i,j)
}

除此以外,C語言中還包含while、do…while循環,以及switch開關判斷等流程控制。關于此方面更詳細內容可參閱專業的C語言類圖書。

1.6.13 操作符

(1)常用的代數操作符(表1-1)

1-1 常用的代數操作符

(2)常用的邏輯操作符(表1-2)

1-2 常用的邏輯操作符

1.6.14 C語言庫函數

C語言中包含了一些常用的庫函數,這些函數可以在UDF中直接調用。

(1)常用的三角函數
double acos (double x);
double asin (double x);
double atan (double x);
double atan2 (double x, double y);
double cos (double x);
double sin (double x);
double tan (double x);
double cosh (double x);
double sinh (double x);
double tanh (double x);
(2)常用的數學函數
double sqrt (double x);
double pow(double x, double y);
double exp (double x);
double log (double x);
double log10 (double x);
double fabs (double x);
double ceil (double x);
double floor (double x);
(3)常用的標準輸入輸出函數
FILE *fopen(char *filename, char *mode);
int fclose(FILE *fp);
int printf(char *format,...); 
int fprintf(FILE *fp, const char *format,...);
int fscanf(FILE *fp, char *format,...);

1.6.15 預處理命令

在UDF的各種頭文件中(文件路徑D:\Program Files\ANSYS Inc\v180\fluent\fluent18.0.0\src),存在各種以#開頭的語句,如圖1-3所示。

#ifndef _FLUENT_UDF_H
#define _FLUENT_UDF_H
#ifdef __cplusplus
extern ”C” {
#endif
#define _UDF 1
#define _CRT_SECURE_NO_DEPRECATE 1
#define _CRT_NONSTDC_NO_DEPRECATE 1
#ifdef UDFCONFIG_H
# include UDFCONFIG_H
#endif
#include ”global.h”

圖1-3 頭文件示例

這些以#開頭的語句就是C語言的預處理命令。

C語言的預處理工作由一個預處理程序來完成。任何C系統都有一個預處理程序,負責處理源程序中的所有預處理命令,從而生成不含預處理命令的源程序。C語言的預處理目的是為了方便編程。

預處理命令以獨立的預處理命令行的形式出現在源程序中,# 是其特殊的引導符號。如果源程序中某一行的第一個非空格符號是#,這就是一個預處理命令行。預處理命令的作用是要求預處理程序完成一些操作。

(1)文件包含命令

文件包含命令是以#include開始的行,其作用是把特定文件的內容復制到當前源文件中。其存在兩種形式如下:

# include <文件名>
# include ”文件名”

兩者的差異在于文件搜索方式的不同。

第一種形式,預處理程序直接到系統指定的某些目錄中去查找所需文件,目錄指定方式由具體系統確定,通常指定幾個系統目錄。

第二種形式,預處理程序先在源文件所在目錄中查找,若沒找到文件,則再到系統指定的目錄中去查找。

文件包含命令的處理過程:首先查找所需文件,找到后就用該文件的內容取代這個包含命令行。替換進來的文件中若有預處理命令,也將被處理。

(2)宏定義和宏替換

以#define開始的行稱為宏定義命令行。宏定義包含兩種形式:簡單宏定義;帶參數的宏定義。

① 簡單宏定義

簡單宏定義的形式為:

#define 宏名字 替代文本 

其中宏名字是任意標識符,替代文本可以是任意一段正文,其中可以包括程序中能出現的任何字符(包括空格等),一直延續到本行結束。如果需要寫多行的替代文本,可以在行末寫一個反斜杠\,這將使下一行內容繼續被當作替代文本。

宏定義的作用就是為宏名字定義替代。

如果一個宏名字的替代文本是數值或可以靜態求值的表達式,當這個宏名字在程序某處出現,就相當于在那里寫了這個數值或表達式。

例如,如果進行了如下定義:

#define SLD static long double

此后,宏名字SLD就代表static long double。若程序中出現:

SLD x=2.4, y=9.16;

經過預處理后,源代碼被翻譯為:

static long double x=2.4,y=9.16;

預處理并不檢查宏定義中的替代文本是否為合法的C語言結構,也不檢查替換之后的結果是否為正確的C語言程序段,其只是簡單地完成文本替換工作。

② 帶參數的宏定義

帶參數的宏定義形式為:

#define 宏名字(參數列表)替代正文

使用帶參數的宏定義時,不但要給出宏的名字,還要用類似函數實參的形式給出各宏參數的替代段,多個替代段之間用逗號分隔。這種形式也成為一個宏調用。

對宏調用的替換分兩部分進行:首先用替代代碼段填充宏參數,然后將替換的結果(展開后的替代正文)代入到主程序中實現程序代碼的替換。

例如,定義求兩個數據中較小數,可定義宏:

#define min(A,B) ((A)<(B)?(A):(B))

若程序中出現如下語句:

z = min(x+y,x*y)

則宏展開后為:

z = ((x+y)<(x*y)?(x+y):(x*y));

帶參數的宏定義與函數看起來很類似,但實際上有很大的不同。切記宏定義只是簡單的文本替換。

(3)條件編譯命令

條件編譯的作用是在源程序中劃出一些片段,使預處理程序可根據條件保留或丟掉一段,或從幾段中選擇一段保留。實現條件編譯的預處理命令有四個,分別是:

#if
#else
#elif
#endif

其中,#if和#elif命令以一個能靜態求出整型值的表達式為參數,另外兩個沒有參數。條件編譯命令的常見使用形式有三種。

① 形式一

#if 整型表達式
…… /*代碼片段,條件成立時保留*/
#endif

② 形式二

#if 整型表達式
…… /*條件成立時保留*/
#else
…… /*條件不成立時保留*/
#endif

③ 形式三

#if 整型表達式
…… /*條件成立時保留*/
#elif 整型表達式
…… /*elif部分,可以有多個*/
#elif 整型表達式
……
#else
…… /*條件都不成立時保留*/
#endif

其中整型表達式是預處理條件,值為0表示條件不成立,否則條件成立。這里常用==、!=等做判斷,例如判斷宏定義的符號是不是等于某個值等。

為了方便,C語言提供了一個特殊謂詞define,其使用形式有兩種:

define 標識符
define (標識符)

當標識符是有定義的宏名字時,define(標識符)將得到1,否則得到0。這種表達式常被作為條件編譯的條件。此外還有兩個預處理命令#ifdef和ifndef,它們相當于#if和#define混合的簡寫形式。

#ifdef 標識符 /*相當于#if define(標識符)*/
#ifndef 標識符 /*相當于#if !define(標識符)*/
主站蜘蛛池模板: 澎湖县| 来安县| 冷水江市| 前郭尔| 绥化市| 宁南县| 焉耆| 闸北区| 湛江市| 香格里拉县| 吴江市| 木兰县| 鄯善县| 宁波市| 本溪市| 东城区| 乐清市| 乳山市| 芦山县| 墨脱县| 灵台县| 察哈| 井研县| 仙桃市| 武胜县| 依兰县| 宁蒗| 柳河县| 都匀市| 景德镇市| 西华县| 新巴尔虎右旗| 四会市| 霍邱县| 仁化县| 琼结县| 来凤县| 迁西县| 寿宁县| 云龙县| 龙井市|