程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> VC >> 關於VC++ >> C程序移植到VC開發環境下

C程序移植到VC開發環境下

編輯:關於VC++

說明

本文是作者學習計算方法時所做工作的總結。我們改寫了徐士良先生編著的《C常用算法程序集》(清華大學出版社出版)

數值計算部分-前15章所有程序,並全部在 VC6 + Windows2000 下調試通過。數組類和矩陣類有兩個版本:封裝成模板類、普通類,我個人認為後者可能更實用,但論文中以模板類形式給出。

本文已經在www.vchelp.net上發表過,該網站允許作者另投其他網站。

摘要

針對C程序的特點,給出將之移植到VC集成環境下的技術,對一個常用程序集實施了大規模的改寫,並提供了C++數組和矩陣模板類,對C程序進行面向對象的封裝。The Migration of Old C Code to Visual C++ IDE
Abstract: According to the character of C programs, this paper presents some techniques to migrate them to Visual C++ IDE,
as a implemention, it reprograms a set of numerical arithmetic programs for further engineering use.
關鍵詞:移植;數值計算;封裝;模板類Key words: Migration; Numerical Arithmetic, Encapsulation, Template Class 一、引言

由於C語言長期廣泛應用,現存有大量經過嚴格檢驗的實用C程序,它們可以用來很好地解決工程應用中的實際問題。但是舊的C程序往往有很多與現代編譯器不兼容的地方,因此我們要根據具體的代碼情況進行相應的移植處理。

本文以改寫清華大學出版社出版的C常用算法程序集(以下簡稱"程序集")為例,說明如何將舊的C程序移植到目前普遍使用的C/C++開發環境Visual C++下。除了列舉一些移植程序的方法和技巧,本文還給出兩個C++類:數組類和矩陣模板類,以例示如何對C程序進行面向對象的包裝處理。

二、基於C語言分析和改換

我們知道,Visual C++支持ANSI C,下面列舉源代碼影響編譯、不兼容的情況和相應解決方案,並給出基於ANSI C標准的函數的基本調用例子。

1、函數定義參數聲明沒有采用現代風格,例如全選主元高斯消去法:int agaus(a,b,n)
int n;
double a[],b[];
{……;}
參數聲明應改為數組形式:int agaus(double a[],double b[],int n) 或者改為指針形式:int agaus(double* a,double* b,int n); 調用方法:agaus(&a[0][0],&b[0],n);
/* a二維雙精度型數組、b一維雙精度型數組,n整型變量 */

C/C++中用下標法和指針法都可以訪問一個數組,設有數組a,則a[i]和*(a+i)無條件等價。如果指針變量p指向數組中的一個元素,則p+1指向同一數組的下一個元素。若p的初值為&a[0],則p+i和a+I都是a[i]的地址;*(p+i)和*(a+i)就是p+i或a+i所指向的數組元素,即a[i];指向數組的指針變量也可以帶下標,如p[i]與*(p+i)等價。所以,在實際使用該函數,如果遇到數組作形參,可以將數組第一個元素地址作為實參傳值調用函數。

2、動態存儲分配函數返回void*型指針變量,它指向一個抽象類型的數據,ANSI C標准規定在將它賦值給另一個指針變量時需要進行強制類型轉換,所以下面代碼Line1要用Line2替換:double* v;
v=malloc(n*m*sizeof(double));/* Line1 */
v=(double*)malloc(n*m*sizeof(double));
/* Line2 */
3、某些算法函數可能要調用一些用戶自定義函數,如最佳一致逼近的裡米茲方法:void hremz(a,b,p,n,eps)
int n;
double a,b,eps,p[];
{
  extern double hremzf();
  …
}
原方法使程序集與應用程序的耦合程度增加,缺乏靈活性,可以改為:void hremz(double a,double b,double p[],int n,double eps,double (*hremzf)(double x))
{…}
用函數指針作參數,調用時直接將函數名作實參即可: hremz(a,b,p,4,eps,hremzf); /* 假設各參數在主程序文件已定義 */

4、有的時候需要將一些函數的控制台輸出作為字符串值返回,比如:

printf("%c",xy[i][j]);我們可以用形似sprintf( buffer,"%c",xy[i][j]),
    strcat( str, buffer );
的合並語句(其中str是一個足夠大的字符串數組參數)代替printf("%c",xy[i][j]);例如:char* buffer;
    buffer =(char*)malloc(n*sizeof(char)); /*n作為參數傳遞,例如100 */
    sprintf( buffer,"%c",xy[i][j]),
    strcat( str, buffer );
    /*把終端輸出字符添加到str 串尾*/
    ......
    free(buffer)
如果用到了它們,調用方法以隨機樣本分析為例:

char str[1024];
    str[0]=''\0'';/*初始化為空串*/
    irhis(x,100,x0,h,10,1,&dt[0],&g[0],&q[0],str);

現在str數組保存了終端輸出文本,可以隨意使用它,比如在控制台程序裡輸出:

puts(str);

在使用MFC類庫時,str可以直接賦值給一個CString對象的實例。 經過以上的工作,我們得到基於ANSI C標准的程序版本,可以在C和C++開發環境下使用。

三. 基於C++語言分析和改換

由於C語言本身的弱點,程序集還存在的缺陷主要有

1、異常處理機制支持較弱;

2、程序沒有對數組下標是否越界的檢測。

如果編程人員對C/C++語言很生疏,並且不熟悉該程序集,那麼有可能由於編碼的失誤在調試過程中得到不可預知的荒謬結果。我們的解決方案是為程序集增加C++的異常處理機制,以及用類封裝技術,對數組進行面向對象的封裝和使用,用Array模板類對象替換一維數組,用Matrix模板類對象替換二維數組。下面給出兩個類的聲明部分,它們分別實現最基本的數組和矩陣數據結構和算法。template <class T=double>
class TArray
{
protected:
  T* pdata;
  unsigned int length;
public:
  TArray();
  TArray(unsigned int);
  TArray(TArray const&);
  virtual ~TArray();
  void operator = (TArray&);
  TArray<T>& operator + (TArray&);
  TArray<T>& operator - (TArray&);
  T const& operator [] (unsigned int)const;
  T& operator [](unsigned int);
  T const* GetData() const;
  unsigned int GetLenght();
  void SetLength(unsigned int,bool=true);
};
template <class T=double>
class TMatrix
{
protected:
  unsigned int numberOfRows;
  unsigned int numberOfColumns;
  TArray<T> array;
public:
  class Row
  {
    TMatrix<T>& matrix;
    unsigned int const row;
  public:
    Row (TMatrix<T>& _matrix,unsigned int _row):matrix(_matrix),row(_row){}
    T& operator [](unsigned int column)const
    {return matrix.Select(row,column);}
  };
  TMatrix();
  TMatrix(unsigned int, unsigned int);
  TMatrix(TMatrix<T>& mat);
  virtual ~TMatrix();
  T& Select(unsigned int, unsigned int);
  Row & operator[](unsigned int);
  TMatrix<T>& operator + (TMatrix<T>& mat);
  TMatrix<T>& operator - (TMatrix<T>& mat);
  TMatrix<T>& operator * (TMatrix<T>& mat);
  bool operator == (TMatrix<T>& mat);
  TArray<T>& GetData();
  unsigned int GetNumberOfRows();
  unsigned int GetNumberOfColumns();
  bool LoadFromArray(T [],unsigned int,unsigned int);
  bool LoadFromString(char*,char,char);
  bool ResetMatrix(unsigned int, unsigned int);
  bool ReverseMatrix();
  void ZeroMatrix();
  void RandomMatrix(int max);
};

舉例說明我們關於異常機制和數組越界的檢測方法的思路。TMatrix類的operator[]返回一個嵌套類Row的引用,它用來描述某一給定二維數組的一個特定行。Row類的operator[]則返回該行一個特定位置的T類型值。最終實現還是通過Matrix<T>::Select()函數,該函數體代碼如下:template <class T>
T& TMatrix<T>::Select(unsigned int i, unsigned int j)
{
  char ch[50];
  if(i>=numberOfRows)
  sprintf(ch," Error -- Invalid row: %d",i), throw (ch);
  if(j>=numberOfColumns)
  sprintf(ch," Error -- Invalid colum: %d",j),
  throw (ch);
  return array[i*numberOfColumns+j];
}
應用程序實例:unsigned int i,j;
unsigned int m=2,n=3;
TMatrix< > mat(m,n);//雙精度型矩陣
try
{
  for(i=0; i<m; i++)
  {
    for(j=0; j<n+1; j++)// Line3
      cout<<mat[i][j]<<"\t";
    cout<<endl;
  }
}
catch(char* str)  //捕獲異常
{cout<<str<<endl;}

終端輸出如下(注:類實例mat沒有初始化):-6.27744e+066  -6.27744e+066  -6.27744e+066  Error -- Invalid colum: 3 只輸出一行,根據出錯提示,把Line3改為:"for(j=0; j<n; j++)",重新編譯運行,輸出2行3列的正確結果:-6.27744e+066  -6.27744e+066  -6.27744e+066
-6.27744e+066  -6.27744e+066  -6.27744e+066

由於我們對operator[]進行了重載,所以數組模板類(矩陣模板類)完全兼容C/C++一維數組(二維數組)的存取操作,因此舊程序中數組變量直接可以用類實例變量替代。圖一是我們用Visual C++ 6開發的演示程序界面,左邊是所有算法的目錄樹,右邊是文本計算結果輸出,下部懸浮窗口是算法程序源代碼,可以拷貝粘貼,稍作修改即可重用。

圖一

四、結論

新的程序與原程序相比較的優點:

1. 遵從ISO C/C++標准,因此具有良好的可移植性。可以在大多數流行的C++開發環境下使用;

2. 利用一些技巧,改進了原程序不利於擴展和缺少靈活性的缺點;

3. 去除了原程序中幾個影響效率的Bug;

4. 增加異常處理機制和數組越界檢測,增強可調試性和健壯性;

5. 數組和矩陣操作得到了強有力的支持。

經過我們實際應用測試,新的程序集可以滿足一般工程應用的數值計算需要,並且能夠在原來的基礎上,方便地進行必要的改進和擴充。

本文配套源碼

  1. 上一頁:
  2. 下一頁:
欄目導航
Copyright © 程式師世界 All Rights Reserved