c模板編程
⑴ 在c語言中如何實現函數模板
各種用
C
語言實現的模板可能在使用形式上有所不同。現以一個求和函數
Sum
為例,用
C++
Template
可寫如下:
template
R
Sum(const
T
*array,
int
n)
{
R
sum
=
0;
for
(int
i
=
0
;
i
<
n
;
++i)
sum
+=
i;
return
sum;
}
如果不是內置類型,該模板隱式地需要
有R
R::operator+=(T)運算符可用。
1.
使用函數指針作為
Functor
替換者
Typedef
struct
tagAddClass
{
Void
(*add)(char*
r1,
const
char*
r2);
Int
elemSize;
Char
sum[MAX_ELEM_SIZE];
}
AddClass;
void
Sum(AddClass*
self,
const
char*
array,
int
n)
{
for
(int
i
=
0
;
i
<
n
;
++i)
self->add(self->sum,
array
+
i*self->elemSize);
}
使用時:
Void
AddInt(char*
r1,
const
char*
r2)
{
*(long*)r1
+=
*(int*)r2;
}
AddClass
addClass
=
{AddInt,
2,
0
};
Int
array[100];
Read(array);
Sum(&addClass,
array,
100);
…..
2.
用宏作為Functor的替換者
#define
GenSumFun(SumFunName,
Add,
RetType,
ElemType)
RetType
SumFunName
(const
ElemType
*array,
int
n)
\
{
RetType
sum
=
0;
for
(int
i
=
0
;
i
<
n
;
++i)
Add(sum,
i);
return
sum;
}
使用時:
#define
AddInt(x,
y)
((x)
+=
(y))
GenSumFun(SumInt,
AddInt,
long,
int)
…..
Int
array[100];
Read(array);
Long
sum
=
SumInt(array,
100);
…..
3.
所有可替換參數均為宏
至少需要一個額外的文件(實現文件)為
impsum.c
/*
impsum.c
*/
RetType
FunName(const
ElemType
*array,
int
n)
{
RetType
sum
=
0;
for
(int
i
=
0
;
i
<
n
;
++i)
Add(sum,
i);
return
sum;
}
使用時:
#undef
RetType
#undef
FunName
#undef
ElemType
#undef
Add
#define
AddInt(x,
y)
((x)
+=
(y))
#define
RetType
long
#define
FunName
SumInt
#define
ElemType
int
#define
Add
AddInt
#include
impsum.c
…..
Int
array[100];
Read(array);
Long
sum
=
SumInt(array,
100);
4.
總結:
第一種方法,易於跟蹤調試,但是效率低下,適用於對可變函數(函數指針)的效率要求不高,但程序出錯的可能性較大(復雜),模板函數(Sum)本身很復雜,模板參數也比較復雜(add)的場合。
第二種方法,效率高,但很難跟蹤調試,在模板函數和模板參數本身都很復雜的時候更是如此。
第三種方法,是我最近幾天才想出的,我認為是最好的,在模板參數(Add)比較復雜時可以用函數(第二種也可以如此),簡單時可以用宏,並且,易於調試。在模板函數本身很復雜,而模板參數比較簡單時更為優越。但是,可能有點繁瑣。
⑵ 用C++語言編寫程序實現:設計一個函數模板,實現用冒泡法對10 個某類型數據按升序排序
冒泡排序演算法的運作如下:
1、比較相鄰的元素。如果第一個比第二個大,就交換他們兩個。
2、對每一對相鄰元素作同樣的工作,從開始第一對到結尾的最後一對。在這一點,最後的元素應該會是最大的數。
3、針對所有的元素重復以上的步驟,除了最後一個。
4、持續每次對越來越少的元素重復上面的步驟,直到沒有任何一對數字需要比較。
這里只需套用函數模板,將元素的類型設為模板變數即可,代碼如下:
template<typenameItem>
voidsort(Item*arr)
{
inti,j;
Itemtemp;
for(j=0;j<9;j++)
for(i=0;i<9-j;i++)
{
if(arr[i]>arr[i+1])
{
temp=arr[i];
arr[i]=arr[i+1];
arr[i+1]=temp;
}
}
}
⑶ C中的模板怎樣使用 最好給個例子~
看看這個吧
http://www.njcc.e.cn/njhysite/njhygao_js/xuebao/xuebao0402/zhjm.doc
其他的見
C語言中實現模板函數的方法
在C語言中實現模板函數的方法:
各種用C語言實現的模板可能在使用形式上有所不同。
現以一個求和函數Sum為例,用C++Template可寫如下:
template<classT,classR> RSum(constT*array,intn)
{
Rsum=0;
for(inti=0;i<n;++i)
sum+=i;
returnsum;
}
如果不是內置類型,該模板隱式地需要有RR::operator+=(T)運算符可用。
1. 使用函數指針作為Functor替換者
TypedefstructtagAddClass
{
Void(*add)(char*r1,constchar*r2);
IntelemSize;
Char sum[MAX_ELEM_SIZE];
}AddClass;
voidSum(AddClass*self,constchar*array,intn)
{
for(inti=0;i<n;++i)
self->add(self->sum,array+i*self->elemSize);
}
使用時:
…..
VoidAddInt(char*r1,constchar*r2)
{
*(long*)r1+=*(int*)r2;
}
AddClassaddClass={AddInt,2,0};
Intarray[100];
Read(array);
Sum(&addClass,array,100);
…..
2. 用宏作為Functor的替換者
#define GenSumFun(SumFunName,Add,RetType,ElemType) \
RetTypeSumFunName(constElemType*array,intn) \
{ \
RetTypesum=0; \
for(inti=0;i<n;++i) \
Add(sum,i); \
returnsum; \
}
使用時:
#defineAddInt(x,y) ((x)+=(y))
GenSumFun(SumInt,AddInt,long,int)
…..
Intarray[100];
Read(array);
Longsum=SumInt(array,100);
…..
3. 所有可替換參數均為宏
至少需要一個額外的文件(實現文件)為impsum.c
/*impsum.c*/
RetTypeFunName(constElemType*array,intn)
{
RetTypesum=0;
for(inti=0;i<n;++i)
Add(sum,i);
returnsum;
}
使用時:
#undef RetType
#undef FunName
#undef ElemType
#undef Add
#defineAddInt(x,y) ((x)+=(y))
#defineRetTypelong
#defineFunNameSumInt
#defineElemTypeint
#defineAdd AddInt
#includeimpsum.c
…..
Intarray[100];
Read(array);
Longsum=SumInt(array,100);
…..
4. 總結:
第一種方法,易於跟蹤調試,但是效率低下,適用於對可變函數(函數指針)的效率要求不高,但程序出錯的可能性較大(復雜),模板函數(Sum)本身很復雜,模板參數也比較復雜(add)的場合。
第二種方法,效率高,但很難跟蹤調試,在模板函數和模板參數本身都很復雜的時候更是如此。
第三種方法,是我最近幾天才想出的,我認為是最好的,在模板參數(Add)比較復雜時可以用函數(第二種也可以如此),簡單時可以用宏,並且,易於調試。在模板函數本身很復雜,而模板參數比較簡單時更為優越。但是,可能有點繁瑣。
一般情況下,沒有必要做如此勞心的工作,一切交給編譯器去做就行了。但是本人在開發一個文件系統時,由於是基於一種少見的平台,沒有可用的C++編譯器,有幾個函數,除了其中的類型不同(uint16和uint32),和幾個可參數化的宏不同,其它地方完全相同,而函數本身很復雜(兩百多行代碼)。Copy出幾個完全類似的函數副本,維護起來特別煩人。非常需要如此的編程模式,故此,分享出來,大家共同探討。
⑷ c++函數模板替代函數編寫
一樓的很有意思,你這篇文章其實是很經典的,但與本問題無關...
問題在這一句:
string e = myfunc("aa", "bbb");
編譯器已經很明確地告訴你了,不能把2個指針相加,因為在用雙引號括起來的字元都是所謂的C風格字元串,即以一個指針來標明其首個字元並以'/0'作為結束信號的字元串,比如:
const char* p = "hello, world!";
如果你將其打出,如:
cout << p;
則指針p便會把其指向的字元逐個列印出來,再遇到'\0'時結束。
在你的問題中,由於"aa"和"bbb"無法被轉化為string類,所以編譯器不會調用這個函數:
string myfunc(string a, string b)
{
string c = a + b;
return c;
}
而是為它們生成了特定的模版:
char* myfunc(char* a, char* b);
由於兩個指針的相加是沒有被定義的,所以編譯器報錯。
由於string e = myfunc("aa", "bbb")中的"aa"和"bbb"被默認作為了C風格的字元串,而編譯器沒有定義一種隱式的轉換方法可以把C風格字元串轉化為string類,所以你必須通過顯式地把它們轉換為string類字元串,可以這樣:
string e = myfunc(string("aa"),string("bbb"));
或者用更加一目瞭然且安全但煩瑣而冗長的C++式cast句法:
string e = myfunc(static_cast<string>("aa"), static_cast<string>("bbb"));
另外說下用了模版的話你的這個函數就可以砍掉了:
string myfunc(string a, string b)
{
string c = a + b;
return c;
}
另外如果你不想煩瑣地每次調用myfunc來實現2個字元的相加都要通過cast的話也可以把myfunc模板實例化:
#include <iostream>
#include <string>
#include <cmath>
#include <cstring>
using namespace std;
template<class T>
T myfunc(T a,T b){
T c=a+b;
return c;
}
// 模板實例化
template<> const char* myfunc<const char*>(const char* a, const char* b)
{
string temp = string(a) + string(b);
return temp.c_str(); // 結果可能出乎你的預料~
}
// 或者
// 模板實例化
/* template<> const char* myfunc<const char*>(const char* a, const char* b)
{
char* pa = new char[sizeof a + sizeof b];
const char* pc = strcat(strcpy(pa, a), b);
delete [] pa;
return pc; // 同上
}*/
int main(){
int c;
float d;
c=myfunc(3,4);
cout<<c<<endl;
d=myfunc(3.4,4.7);
cout<<d<<endl;
string e=myfunc("aa","bbb");
cout<<e<<endl;
system("pause");
return 0;
}
你運行這個程序得到的e可能是亂碼,原因可見一樓的部分提示:
8. 不要再寫 char* p = "XXX" 這種語句,要寫成 const char* p = "XXX",編譯器之所以讓前者通過編譯是為了兼容以前的大量的舊代碼。
BTW:const TYPE* p 和 TYPE const* p 是一樣的,風格不同而已。
BTW:C語言中也有const關鍵字。
詳細可見:
http://community.csdn.net/Expert/TopicView3.asp?id=5085350
⑸ 在c語言中如何實現函數模板
如果要寫個函數支持多種數據類型,首先想到的就是C++的模板了,但是有時候只能用C語言,比如在linux內核開發中,為了減少代碼量,或者是某面試官的要求…
考慮了一陣子後,就想到了qsort上.qsort的函數原型:
void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) );
快排時,只要自己實現相應數據類型的比較函數cmpare就可以了.如果比較int型時,一個典型的compare函數如下:
那麼,就是說可以利用void *. void *意指未指定類型,也可以理解為任意類型。其他類型的指針可以直接賦值給void *變數,但是void *變數需要強制類型轉換為其它指針類型。這個相信大家都知道。那麼下面以一個簡單的題目為例,來探討如何在C語言中實現模板函數。
方法1: 利用void *.
在看下面的源程序之前,需要了解幾點。首先,在32位平台上,任何類型的指針所佔的位元組都是4個位元組,因為32位機器虛擬內存一般為4G,即2的32次方,只要32位即4個位元組就可以足夠定址,sizeof(void *)=4; 其次,雖然各種不同類型的指針所佔的空間都為4個位元組,但是不同類型的指針所指的空間的位元組數卻不同(這一點尤為重要,下面的程序我在開始沒有調通就因為這點意識不強)。所以,如果你將一個指針強制轉換為另一個類型的指針,指針本身所佔的位元組是不變的,但是,如果對這個指針進行運算,比如 *p,p++,p-=1等一般都是不同的。 再次,函數指針應該了解下,這里不多說。 最後,因為Sandy跟我說,C++開始的時候模板的實現其實就是利用宏替換,在編譯的時候確定類型。所以,為了方便,類型也用了預編譯指令#define。
<span>#include"stdio.h"</span>
<span>#include"stdlib.h"</span>
<span>//typedefintT;//或者下面的也可以.</span>
<span>#defineTint</span>
//這個FindMin是Sandy寫的.felix021也寫了個,差不多的就不貼出來的.
voidFindMin(constvoid*arr,intarr_size,intarrmembersize,int*index,
int(*cmp)(constvoid*,constvoid*b)){
inti;
*index=0;
char*p=(char*)arr;
char*tmp=p;
for(i=1;i<arr_size;i++){
if(cmp(tmp,p)>0){
tmp=p;
}
p+=arrmembersize;
}
(*index)=((int)(tmp-arr))/arrmembersize;
}
*/</span>
可以把指針看作是char*,如果轉換為int*,那下面的位移就不正確了.</span>
index<span>=</span>i<span>;</span>
<span>}</span>
<span>}</span>
<span>return</span>index<span>;</span>
<span>}</span>
<span>int</span>result<span>;</span><span>//result保存的是最小值索引.</span>
result<span>=</span>FindMin<span>(</span>arr,<span>12</span>,
⑹ c語言編程
//計劃做的腳本引擎的一部分
//參考的 C++編程藝術
//總地來說會有一些難度
//我的是C++應該會給你一些啟發
//TypeDef.h
#include "windows.h"
#ifndef B_TYPE_DEF_H
#define B_TYPE_DEF_H
const int MAX_T_LEN = 128;//可以分析的最大符號長度(同時決定了一個字元變數的最大長度為128位元組)
const int MAX_ID_LEN = 31;//允許的最大的標識長度(一個標識符是指一個變數名或函數名)
const int MAX_BUF_LEN = 1024;//解釋緩沖區1024位元組
const int NUM_PARAMS = 32;//最大參數數目
const int MAX_DIM_NUM = 65536//數組最大維數
//需要分析的所有東西
enum Token_Item { UNDEF=1, //未定義
E_TEMP,//當模板使用
E_CHAR,//char關鍵字
E_INT,//int關鍵字
E_FLOAT,//float關鍵字
E_SWITCH,//switch關鍵字
E_CASE,//case關鍵字
E_IF,//if關鍵字
E_ELSE,//else關鍵字
E_FOR,//for關鍵字
E_DO,//do關鍵字
E_WHILE,//while關鍵字
E_BREAK,//break關鍵字
E_RETURN,//return關鍵字
E_COUT,//cout關鍵字
E_CIN,//cin關鍵字
LBLOCK, //{
RBLOCK,//}
DOU,//,
EOS,//;
MAO,//:
SFEN,//'已舍棄,不含'分析
LT,//<
LE,//<=
GT,//>
GE,//>=
EQ,//==
NE,//!=
FF,//.
LK,//(
NOT,//!
INC,//++
DEC,//--
ADD,//+
SUB,//-
RK,//)
LZK,//[
RZK,//]
LS,//<<
RS,//>>
ASS,//=
AND,//&&
OR,//||
MUL,//*
DIV,///
MOD,//%
POW,//^
NUMBER, //數字
IDENTIFIER,//標識
STRING,//字元串
END//文件結束
};//需要分析的全部符號
enum Token_Type{
UNK,//未知類型
KEY,//關鍵字
FJF,//分界符
CMP,//比較運算符
OPE,//運算符
NUM,//數字
IDE,//標識符
STR,//字元串
NON,//結束符號
UDF//未定義
};
typedef struct Token_Table{//符號表
char name[MAX_T_LEN];
Token_Item token;
Token_Type type;
} TOKEN_TABLE,*PTOKEN_TABLE;
enum error_msg //錯誤類型
{ SYNTAX=1000, NO_EXP, NOT_VAR, DUP_VAR, DUP_FUNC,
SEMI_EXPECTED, UNBAL_BRACES, FUNC_UNDEF,
TYPE_EXPECTED, RET_NOCALL, PAREN_EXPECTED,
WHILE_EXPECTED, QUOTE_EXPECTED, DIV_BY_ZERO,
BRACE_EXPECTED, COLON_EXPECTED,FAIL_OPEN,ERROR_SIZE,
NO_MAIN,ERROR_ASSIGN,ERROR_RZK,ERROR_DIM};
class InterpExc { //錯誤類
error_msg err;
public:
InterpExc(error_msg e) { err = e; }
error_msg get_err() { return err; }
};
enum Vars{類型
V_Int,
V_Float,
V_String,
V_pInt,
V_pFloat,
V_pString,
V_Udef
};
#endif
#ifndef V_NULL
#define V_NULL (-1)
#endif
//Cfenxi.h
#include "TypeDef.h"
#include <iostream>
#include <vector>
#include <stack>
#include <algorithm>
#include <string>
using namespace std;
//Fenxi類說明
//curr_pt始終指向將要分析的地址
//prev_pt為前一個分析的地址
//可以使用函數GotoPt來改變當前分析地址
//分析結果放在變數stoken,item,type
//在Cfenxi.cpp中定義了一個文件級變數TOKEN_TABLE tokentable[];
//在使用的時候必須聲明這個變數
#ifndef B_CFENXI_H
#define B_CFENXI_H
class Fenxi{
public:
char stoken[MAX_T_LEN+1];//分析出來的符號名
char buff[MAX_BUF_LEN+1];//緩沖區
Token_Item item;//分析出來的具體符號
Token_Type type;//符號大類
long curr_pt;//當前分析點
long prev_pt;//前一個分析點
char pFileName[256];//腳本文件名
PTOKEN_TABLE pTokenTable;//符號表
public:
Fenxi(){};
~Fenxi(){};
void Create(char*,PTOKEN_TABLE,int);//創建分析對象
void GetToken();//分析一步
void GotoPt(long);//跳分析點
void PutBack();//回退一個分析點
private:
int nTableItem;//分析表中的分析數目
long iFileLength;//腳本文件長度
int iBlock;//當前所在區塊
int iYouBiao;//當前游標
char cbuff;//當前所指向的字元
char cbuff1;//超前搜索的字元
void MoveNext();//向下移動
void MovePrev();//向前移動
void LoadBlock();//裝入一個塊
long GetPt(int i,int n){return (long)(i*MAX_BUF_LEN+n);};//計算位置
bool KeyLookUp(char*,Token_Item &);//查找是不是關鍵詞
bool isdelim(char);
};
//解釋類
class var_value{
public:
char string_value[MAX_T_LEN+1];
int int_value;
float float_value;
Vars v_type;
public:
var_value()
{
int_value=0;
float_value=0;
string_value[0]=0;
v_type=Udef;
}
var_value(const var_value&);
set_type(Vars type){v_type=type;}
~var_value(){}
friend bool operator == (const var_value& _X, const var_value& _Y);
friend bool operator < (const var_value& _X, const var_value& _Y);
};
class temp_var_value{
public:
char string_value[MAX_T_LEN+1];
int int_value;
float float_value;
int p_int;
int p_float;
int p_string;
vector<int> dim;
Vars v_type;
public:
temp_var_value()
{
int_value=0;
float_value=0;
string_value[0]=0;
p_int=p_float=p_string=V_NULL;
v_type=Udef;
};
temp_var_value(const temp_var_value&);
temp_set_type(Vars type){v_type=type;}
~temp_var_value(){}
friend bool operator == (const temp_var_value& _X, const temp_var_value& _Y);
friend bool operator < (const temp_var_value& _X, const temp_var_value& _Y);
};
struct var_type { //變數類型
char var_name[MAX_ID_LEN+1]; // 變數名
Vars v_type;//數據類型
vector<var_value> value; //變數值
vector<int> v_dim;//變數維數
int v_max;//變數的最大數目
};
struct func_type {
char func_name[MAX_ID_LEN+1]; //函數名
Vars ret_type; //返回值類型
long loc; // 函數入口點,函數的入口點是指分析點指向函數括弧後第一個字元
};
class Script{
public:
Fenxi theFx;//詞法分析對象,負責對腳本文件的操作
char FileName[256];//腳本文件名
var_value ret_value;//返回值
bool breakfound;//中斷
public:
Script(){};
~Script(){};
void Create(char*,PTOKEN_TABLE,int);//創建腳本對象
void ExecuteScript();//開始解釋腳本
private:
void PreScan();//預掃描
void decl_global();//聲明全局變數
long find_func(char*);//返回函數的入口點
void ItemToVar(Token_Item,Vars&);//根據一個項,得到相當的變數類型
void CallFun();//執行一個函數
void get_args();//得到函數的形式參數名
void Interp();//具體解釋
private:
void eval_exp (var_value &value);
void eval_exp0(var_value &value);
void eval_exp1(var_value &value);
void eval_exp2(var_value &value);
void eval_exp3(var_value &value);
void eval_exp4(var_value &value);
void eval_exp5(var_value &value);
void eval_exp6(var_value &value);
void eval_exp7(var_value &value);
void eval_exp8(var_value &value);
bool is_var(char *s);
// 變數名,變數的維數,變數的值,變數的類型
void assign_var(char *var_name,int idx, var_value value);
void find_var_value(char *var_name,int idx,var_value& value);
int find_idx(vector<int>,vector<int>);// 計算[][]
void find_vector(vector<int> &);//讀取[]
int cal_idx(vector<int>);
Vars is_var_type;//使用is_var的時候如果返回值是真那麼這個變數存儲了變數類型
public:
//每執行一個函數的時候就把進入前的局部變數數目
//放到函數結點棧,函數執行完的時候就根據棧里的
//數據改變局部函數表裡的變數,從而實現變數的靈活使用
//同理塊結點棧的原理也一樣
//變數表
vector<var_type> global_vars; //全局變數表
vector<var_type> local_var_stack; //局部變數表(函數參數作為局部變數處理)
vector<func_type> func_table; //函數表
stack<int> func_call_stack;//函數結點棧
stack<int> nest_scope_stack;//塊結點棧
};
#endif
//Fenxi.cpp
#include "CFenxi.h"
#include <cstring>
#include <cctype>
#include <fstream>
#include <cstdio>
#include <cmath>
using namespace std;
///////////////////////////////////////////////////////////////////////
/////////////////////////詞法分析類的函數定義//////////////////////////
///////////////////////////////////////////////////////////////////////
extern TOKEN_TABLE tokentable[]={
"char",E_CHAR,KEY,
"int",E_INT,KEY,
"float",E_FLOAT,KEY,
"switch",E_SWITCH,KEY,
"case",E_CASE,KEY,
"if",E_IF,KEY,
"else",E_ELSE,KEY,
"for",E_FOR,KEY,
"do",E_DO,KEY,
"while",E_WHILE,KEY,
"break",E_BREAK,KEY,
"return",E_RETURN,KEY,
"cout",E_COUT,KEY,
"cin",E_CIN,KEY,
"{",LBLOCK,FJF,
"}",RBLOCK,FJF,
",",DOU,FJF,
";",EOS,FJF,
"<",LT,CMP,
"<=",LE,CMP,
">",GT,CMP,
">=",GE,CMP,
"==",EQ,CMP,
"!=",NE,CMP,
".",FF,OPE,
"(",LK,OPE,
")",RK,OPE,
"[",LZK,OPE,
"]",RZK,OPE,
"++",INC,OPE,
"--",DEC,OPE,
"<<",LS,OPE,
">>",RS,OPE,
"=",ASS,OPE,
"!",NOT,OPE,
"&&",AND,OPE,
"||",OR,OPE,
"+",ADD,OPE,
"-",SUB,OPE,
"*",MUL,OPE,
"/",DIV,OPE,
"%",MOD,OPE,
"^",POW,OPE,
};
var_value::var_value(const var_value& p)
{
int_value=p.int_value;
float_value=p.float_value;
strcpy(string_value,p.string_value);
v_type=p.v_type;
}
bool operator == (const var_value& _X, const var_value& _Y)
{
if (_X.v_type != _Y.v_type)
{
return false;
}
else
{
switch (_X.v_type)
{
case V_Float:
return (abs(_X.float_value - _Y.float_value) < 0.0001);
break;
case V_Int:
return (_X.int_value == _Y.int_value);
break;
case V_Int:
return !(strcmp(_X.string_value, _Y.string_value));
break;
default:
return false;
}
}
}
bool operator < (const var_value& _X, const var_value& _Y)
{
if (_X.v_type != _Y.v_type)
{
return false;
}
else
{
switch (_X.v_type)
{
case V_Float:
return (_X.float_value < _Y.float_value);
break;
case V_Int:
return (_X.int_value < _Y.int_value);
break;
case V_Int:
return !(strcmp(_X.string_value, _Y.string_value));
break;
default:
return false;
}
}
temp_var_value::temp_var_value(const temp_var_value& p)
{
int_value=p.int_value;
float_value=p.float_value;
strcpy(string_value,p.string_value);
p_int=p.p_int;
p_float=p.p_float;
p_string=p.p_string;
v_type=p.v_type;
}
void Fenxi::Create(char* p,PTOKEN_TABLE ptt,int n)
{
strcpy(pFileName,p);
ifstream fin(pFileName,ios::in|ios::binary);
fin.seekg(0,ios::end);
iFileLength=fin.tellg();
fin.close();
if(iFileLength==0)
throw InterpExc(ERROR_SIZE);
iBlock=0;
LoadBlock();
MoveNext();//指向第一個字元
iYouBiao=0;//置游標於文件頭
curr_pt=0;
prev_pt=0;
cbuff=buff[0];//當前應該分析字元
cbuff1=buff[1];//超前搜索字元
pTokenTable=ptt;
nTableItem=n;//分析表設置
}
void Fenxi::MoveNext()
{
if(iYouBiao==MAX_BUF_LEN-1)//如果當前游標在緩沖區尾
{
iBlock++;
LoadBlock();
cbuff=buff[0];
cbuff1=buff[1];//超前搜索
}
else
{
iYouBiao++;
cbuff=buff[iYouBiao];
if(iYouBiao==MAX_BUF_LEN-1)//超前搜索
{
char temp[2];
temp[1]=0;
ifstream fin(pFileName,ios::in|ios::binary);
fin.seekg(MAX_BUF_LEN*(iBlock+1));
fin.read(temp,1);
int i=fin.gcount();
temp[i]=0;
fin.close();
cbuff1=temp[0];
}
else
cbuff1=buff[iYouBiao+1];
}
curr_pt=GetPt(iBlock,iYouBiao);
}
void Fenxi::MovePrev()
{
if(iYouBiao==0)//如果當前游標在緩沖區頭
{
cbuff1=cbuff;//超前搜索
iBlock--;
LoadBlock();
iYouBiao=MAX_BUF_LEN-1;
cbuff=buff[iYouBiao];
}
else
{
cbuff1=cbuff;//超前搜索
iYouBiao--;
cbuff=buff[iYouBiao];
}
curr_pt=GetPt(iBlock,iYouBiao);
}
void Fenxi::PutBack()
{
GotoPt(prev_pt);
}
void Fenxi::LoadBlock()//裝入一個塊
{
ifstream fin(pFileName,ios::in|ios::binary);
fin.seekg(MAX_BUF_LEN*iBlock);
fin.read(buff,MAX_BUF_LEN);
int i=fin.gcount();
buff[i]=0;
iYouBiao=0;
fin.close();
}
void Fenxi::GotoPt(long pt)
{
if(pt/MAX_BUF_LEN==curr_pt/MAX_BUF_LEN)//如果是在同一個塊內的話
{
curr_pt=pt;
iYouBiao=curr_pt-iBlock*MAX_BUF_LEN;
cbuff=buff[iYouBiao];
}
else//否則要重新裝入內存
{
curr_pt=pt;
iBlock=curr_pt/MAX_BUF_LEN;
LoadBlock();
iYouBiao=curr_pt-iBlock*MAX_BUF_LEN;
cbuff=buff[iYouBiao];
}
if(iYouBiao==MAX_BUF_LEN-1)//超前搜索
{
char temp[2];
temp[1]=0;
ifstream fin(pFileName,ios::in|ios::binary);
fin.seekg(MAX_BUF_LEN*(iBlock+1));
fin.read(temp,1);
int i=fin.gcount();
temp[i]=0;
fin.close();
cbuff1=temp[0];
}
else
cbuff1=buff[iYouBiao+1];
}
void Fenxi::GetToken()
{
prev_pt=curr_pt;//保存前一個的位置
char *temp; //利用一個指針向字元里寫內容
item=UNDEF;type=UDF;
temp = stoken;
*temp = '\0';
// 如果當前字元是空格且未到文件末
while(isspace(cbuff) && cbuff) MoveNext();
// 跳過行
while(cbuff == '\r') {
MoveNext();
MoveNext();
while(isspace(cbuff) && cbuff) MoveNext();
}
// 是否結尾
if(cbuff == '\0') {
*stoken = '\0';
item = END;
type=NON;
return ;
}
// 檢查{}標識符
if(strchr("{}", cbuff)) {
stoken[0]=cbuff;
stoken[1]='\0';
type=FJF;
if(cbuff=='{')
item=LBLOCK;
else
item=RBLOCK;
MoveNext();//指向下一個字元
return ;
}
// 檢查注釋信息
if(cbuff == '/')
if(cbuff1 == '*') { // /*注釋符
MoveNext();
MoveNext();
do { // 找到結尾
while(cbuff != '*') MoveNext();
MoveNext();
} while (cbuff != '/');
MoveNext();
GetToken();
return;
} else if(cbuff1 == '/') { // is a // CMPment
MoveNext();
MoveNext();
// Find end of CMPment.
while(cbuff != '\r' && cbuff != '\0') MoveNext();
if(cbuff == '\r') {MoveNext();MoveNext();}
GetToken();
return;
}
// 檢查雙操作符
if(strchr("!<>=+-&|", cbuff)) {
switch(cbuff) {
case '|':
if(cbuff1 == '|') {
MoveNext();MoveNext();
*temp = '|';
temp++;
*temp = '|';
temp++;
*temp = '\0';
item=OR;
type=OPE;
}
break;
case '&':
if(cbuff1 == '&') {
MoveNext();MoveNext();
*temp = '&';
temp++;
*temp = '&';
temp++;
*temp = '\0';
item=AND;
type=OPE;
}
break;
case '=':
if(cbuff1 == '=') {
MoveNext();MoveNext();
*temp = '=';
temp++;
*temp = '=';
temp++;
*temp = '\0';
item=EQ;
type=CMP;
}
break;
case '!':
if(cbuff1 == '=') {
MoveNext();MoveNext();
*temp = '!';
temp++;
*temp = '=';
temp++;
*temp = '\0';
item=NE;
type=CMP;
}
break;
case '<':
if(cbuff1 == '=') {
MoveNext();MoveNext();
*temp = '<';
temp++;
*temp = '=';
item=LE;
type=CMP;
}
else if(cbuff1 == '<') {
MoveNext();MoveNext();
*temp = '<';
temp++;
*temp = '<';
item=LS;
type=OPE;
}
else {
MoveNext();
*temp = '<';
item=LT;
type=CMP;
}
temp++;
*temp = '\0';
break;
case '>':
if(cbuff1 == '=') {
MoveNext();MoveNext();
*temp = '>';
temp++;
*temp = '=';
item=GE;
type=CMP;
} else if(cbuff1 == '>') {
MoveNext();MoveNext();
*temp = '>';
temp++;
*temp = '>';
item=RS;
type=OPE;
}
else {
MoveNext();
*temp = '>';
item=GT;
type=CMP;
}
temp++;
*temp = '\0';
break;
case '+':
if(cbuff1 == '+') {
MoveNext();MoveNext();
*temp = '+';
temp++;
*temp = '+';
temp++;
*temp = '\0';
item=INC;
type=OPE;
}
break;
case '-':
if(cbuff1 == '-') {
MoveNext();MoveNext();
*temp = '-';
temp++;
*temp = '-';
temp++;
*temp = '\0';
item=DEC;
type=OPE;
}
break;
}
if(*stoken) return;
}
// 其它運算符號
if(strchr("+-*^/=().[]|!%", cbuff)) {
type=OPE;
switch(cbuff){
case '+':
item=ADD;break;
case '-':
item=SUB;break;
case '*':
item=MUL;break;
case '/':
item=DIV;break;
case '=':
item=ASS;break;
case '(':
item=LK;break;
case ')':
item=RK;break;
case '[':
item=LZK;break;
case ']':
item=RZK;break;
case '.':
item=FF;break;
case '|':
item=UNDEF;type=UDF;break;
case '!':
item=NOT;break;
case '%':
item=MOD;break;
}
*temp = cbuff;
MoveNext();
temp++;
*temp = '\0';
return ;
}
// 分界符號
if(strchr(";,#:", cbuff)) {
type=FJF;
switch(cbuff){
case ';':
item=EOS;break;
case ',':
item=DOU;break;
case ':':
item=MAO;break;
}
*temp = cbuff;
MoveNext();
temp++;
*temp = '\0';
return ;
}
// 讀取一個字元串
if(cbuff == '"') {
MoveNext();
while(cbuff != '"' && cbuff != '\r' && cbuff) {
// Check for \n escape sequence.
if(cbuff == '\\') {
if(cbuff1 == 'n') {
MoveNext();
*temp++ = '\n';
}
}
else if((temp - stoken) < MAX_T_LEN)
*temp++ = cbuff;
MoveNext();
}
if(cbuff == '\r' || cbuff == 0)
throw InterpExc(SYNTAX);
MoveNext(); *temp = '\0';
item=STRING;
type=STR;
return ;
}
// 讀取一個數字
if(isdigit(cbuff)) {
while((cbuff>='0'&&cbuff<='9')||(cbuff=='.')) {
if((temp - stoken) < MAX_T_LEN)
*temp++ = cbuff;
MoveNext();
}
*temp = '\0';
item=NUMBER;
type=NUM;
return ;
}
// Read identifier or keyword.
if(isalpha(cbuff)) {
while(!isdelim(cbuff)) {
if((temp - stoken) < MAX_T_LEN)
*temp++ = cbuff;
MoveNext();
}
item=E_TEMP;
}
*temp = '\0';
// Determine if token is a keyword or identifier.
if(item == E_TEMP) { // convert to internal form
if(KeyLookUp(stoken,item)) type=KEY; // is a keyword
else {type = IDE;item=IDENTIFIER;}
}
if(type==UDF)
throw InterpExc(SYNTAX);
}
bool Fenxi::KeyLookUp(char *s,Token_Item &it){
int i;
// char *p;
// 轉為小寫字母
// p = s;
// while(*p) { *p = tolower(*p); p++; }
for(i=0; i<nTableItem; i++) {
if((tokentable[i].type==KEY)&&!strcmp(tokentable[i].name, s))
{
it=tokentable[i].token;
return true;
}
}
return false;
}
// 符號檢查
bool Fenxi::isdelim(char c)
{
if(strchr(" !:;,+-<>/*^=().|&[]\"%", c) || c == 9 ||
c == '\r' || c == 0) return true;
return false;
}
⑺ c++模板概念是什麼詳細點
有以下這樣3個求加法的函數:
int Add(int x,int y)
{
return x+y;
}
double Add(double x,double y)
{
return x+y;
}
long Add(long x,long y)
{
return x+y;
}
它們擁有同一個函數名,相同的函數體,卻因為參數類型和返回值類型不一樣,所以是3個完全不同的函數。即使它們是二元加法的重載函數,但是不得不為每一函數編寫一組函數體完全相同的代碼。如果從這些函數中提煉出一個通用函數,而它又適用於多種不同類型的數據,這樣會使代碼的重用率大大提高。那麼 C++的模板就可解決這樣的問題。模板可以實現類型的參數化(把類型定義為參數),從而實現了真正的代碼可重用性。C++中的模板可分為函數模板和類模板,而把函數模板的具體化稱為模板函數,把類模板的具體化成為模板類。
⑻ 如何學習C++編程
大一學習的c++,工作之後也一直用的c++。
1. 涵蓋c++入門到精通的圖書列表
《The C programming language》必讀
《C++ Primer》,號稱是一本可以讓你從C或java程序員轉為一個真正的C++程序員的入門參考書,必讀。
《The C++ programming language》,C++之父,人稱B教主著作,在看過C++ primer後,應該可以跳章選讀。
《Think in c++》,網上說此書的中文版翻譯質量奇差,推薦看影印版,選讀。
《Effective c++》,類似 Effective java,講的是最佳實踐,程序員必讀。
《More effective c++》,上書的補充。
《The C++ standard library》,會寫C,不會用標准庫怎麼行。這就跟java程序員不會用java.util包一樣,必讀。
《Effective STL》,STL庫的最佳實踐。Effective C++作者又一力作,必讀。
《The annotated STL source》,STL源碼分析,這本書應該算是深入/精通類了,選讀。
《Generic programming and STL》,號稱C++編程里,就是跟模板,泛型打交道,那麼精通泛型是勢在必行。
《C++ Template》,C++模板編程,代碼復用的經驗之道,必讀。
《Exceptional C++》,跟Effective C++類似,屬於最佳實踐和難題解析,書中列出了許多應用場景和實例代碼供讀者揣摩,選讀。
《More Exceptional C++》,上書的補充。
《Exceptional C++ Style》,上上書的補充
《Inside The C++ Object Model》,有了上面這些書做鋪墊,那麼終於可以讀此神書了。它會帶你游覽C++對象模型的底層實現機制。讀完此書,任何C++代碼看起來如同行雲流水,必讀。
2. 優秀的C/C++開源項目(閱讀代碼)
OS:Linux kernel LVS、Linux應用程序
DB:Mysql、PostgreSQL
Complier:VM、GCC
Framework:OpenSip、SipProxy、
Net:ACE(Java Mina、Netty)、TCP/IP、HTTP協議棧
Cache:Memcached、Redis、
Library:STL(java util package)、Boost、Qt(UI)、
balance:Apache、Nginx
GSL
地址:https://github.com/microsoft/GSL
Boost文檔
地址:https://www.boost.org/doc/libs/
wxWidgets官網
地址:http://wxwidgets.org/
gtkmm
地址:https://www.gtkmm.org/en/
CopperSpice
地址:https://www.copperspice.com/
Qt
地址:https://www.qt.io/Eigen
地址:http://eigen.tuxfamily.org/index.php?title=Main_Page#Documentation
Plot utils
地址:https://www.gnu.org/software/plotutils/
Asio
地址:https://think-async.com/Asio/
POCO
地址:https://pocoproject.org/
abseil
地址:https://abseil.io/
C++開源庫匯總列表
地址:https://en.cppreference.com/w/cpp/links/libs
除了這些開源項目,也可以找一些免費的公開課,那這里也推薦一個ACM金牌大佬講授的免費C++課程,可以去體驗一下:
學好C++才是入職大廠的敲門磚! 當年要是有這課,我的C++也不至於這樣
已失效
3.C++語法講解
語言基礎
詳細介紹變數、表達式、語句、指針、數組、流程式控制制、函數、文件組織等。抽象機制 - 面向對象編程
深入講解C++的抽象機制,封裝(類)、繼承、多態;操作符重載、函數對象、異常處理等。模板 - 泛型編程
詳細介紹C++的模板機制,類模板、函數模板、模板特化等方面的內容。引用和指針:為什麼引用很重要
const關鍵字:為什麼const很重要
名字空間 (namespace)
關於C++對象
內置類型的對象,如int、double對象,自定義類型的對象對象類型的定義
關鍵字class和struct
類成員:成員函數和數據成員
靜態數據成員
成員的訪問控制
對象的size
關於this指針
onst成員函數、const究竟修飾什麼
mutable數據成員
4種特殊成員函數:constructor、destructor、 constructor、operator=
對象的構造、初始化列表
對象的析構
對象的復制
什麼情況下有必要顯式定義4種特殊函數
C++對象生命周關於C++中操作符重載機制
重要操作符重載
算術運算:+, -, *, /, %, ++, --, ...
關系運算:>, <, ==, !=
下標存取:[ ]
函數調用:(),函數對象
類型轉換、單參數構造函數與隱式轉換、阻止隱式轉換 -- explicit關鍵字友元與成員
基類與派生類
再談對象的構造與析構
虛函數、純虛函數
派生類的內存布局、虛函數表
多態、多態類型、如何體現多態
虛析構、為什麼虛析構很重要
多繼承
虛繼承與虛基類
對基類的訪問、public / protected / private繼承
Down cast:static_cast<>和dynamic_cast<>
運行期類型識別 (RTTI)
C++語言機制提供了完整的OOP支持
超越繼承
OOP若干法則和設計模式
- 《C++ Primer》
- 最新版本:第三版(第四版國外已上架, 國內一些網上書店也在預訂中)
- 適合有豐富C經驗,缺乏C++經驗的。不過我個人一直認為此書帶著過於強烈的C語言的痕跡,對於C++的學習未必是 好事。
- 《The C++ Programming Language》/《C++程序設計語言》
- 最新版本:第三版特別版
- 簡稱 TC++PL,有其他語言的豐富經驗的。(也有人簡稱之為「TCPL」,但需與另一本《The C Programmer Language》區分開來)
- 《Essential C++》
- 《Accelerated C++》
- 這兩本薄一些,都是不錯的選擇。《Accelerated C++》本人沒有讀過,從各方面的評價來看,完全值得推薦。
- 以上幾本書都有相應的中文版,而且翻譯的質量都不錯。上面的書未必都需要讀一遍,但無論如何,TC++PL是應該閱讀的。
- 《Effective C++》
- 最新版本:第二版(第三版國外已上架,國內一些網上書店也在預訂中)
- 簡稱EC。C++程序員必讀!很多時候,我們說C++聖經不是指TC++PL,而是這一本。《The Pragmatic Programmer》一書中寫到:「一旦你發現自己要參與C++項目的開發,趕快跑(不要走)到書店去購買Scott Mayer的《Effective C++》,可能還要《More Effective C++》」。
- 《C++ Coding Standards: 101 Rules, Guidelines, and Best Practices》/《C++ 編程規范》
- 個人認為此書應為C++程序員必備的案頭書。幾乎Effective系列和Exceptional系 列都在這里得到了總結。最新的模版、異常的業界經驗都在這里的到了體現。可能的唯一缺陷就是對一個新手而言,關於「為什麼這么做」的問題,解釋的不夠。
- 我 的看法是:如果你不理解其中的條款,記憶,並且照做;如果你理解其中的條款,我猜你一定會同意書中的觀點。我認為這本書中的內容至少在2009年以前都不 會過時,人們將廣為傳誦它制定的101條戒律。
- 還不知道他的簡稱,也許「101」會成為一個候選者?
- 提到《Effective C++》,那麼另外三本書一一浮出水面:
- 《More Effective C++》
- 《Exceptional C++》
- 《More Exceptional C++》。
- 新書《Exceptional C++ Style》也是值得一看的好書。
- 上 述幾本書,一本也不應該放過。
- 個人建議上述書籍按順序閱讀。並且,在將來反復閱讀這幾本書。
4.深入c++面向對象
4.1、從C到C++
4.2、深入C++對象
4.3、操作符重載
4.4、面向對象基礎 -- 繼承
4.5、關於繼承更多的話題
4.6、C++與面向對象設計
5.C++泛型編程與STL
5.1、C++ 模板機制
5.2、STL 概要
5.3、STL容器
5.4、STL迭代
5.5、STL演算法
5.6、預與定義STL數對象
5.7、STL適配器
6. C++進階
《Thinking in C++》/《C++編程思想》
這本書及其中文版傳言好壞都有,沒有認真看過,不做評價,如果確有興趣,不妨嘗試 一下該書。
以下幾本書基本上涉及的都是語言本身,大體上可以按照以下的順序閱讀。
《C++必知必會》
如果早一年,這本書將是重量級的,然而它被101和《Exceptional C++ Style》蓋過一頭。
《C++ Gotchas: Avoiding Common Problems in Coding and Design》/《C++程序設計陷阱》
這又是一本我未曾讀過,而且廣受好評的書。
《STL 源碼剖析》
這本書我剛到手,就被人"借"走,以至於到現在也沒有看過。看過這本書的朋友,可以給一個合適的評價。
7. C++進階之數據結構基礎
這是所有編程語言中最應該學習的部分,程序組成的基礎之一。
順序存儲、鏈式存儲、循環鏈表;
雙向鏈表、棧(順序和鏈式)、隊列(順序和鏈式);
棧的應用、樹基本概念及遍歷、二叉樹;
排序演算法、並歸演算法、選擇、插入、快速、希爾。
以上這些內容你知道嗎?
8. C++進階之UI界面開發
掌握QT類庫構架,圖形界面開發模型;
掌握QT開發技巧,消息機制,圖形處理;
掌握QT網路編程,UDP,TCP使用方式;
掌握QT文件處理方式,序列化;
掌握QT在windows,linux,ios,android不同平台下的移植技術。
9. C++進階之Unix/Linux網路伺服器
掌握Unix/Linux平台開發方式;
熟練使用系統調用;
熟練Unix/Linux內存管理,進程,線程調度;
熟悉網路伺服器開發方式,熟練編寫TCP,UCP網路服務程序;
掌握同步/非同步IO模型在網路編程中的使用方式。
10.C++進階之資料庫開發
掌握SQL語言的實用技巧。Oracle,MySQL資料庫的使用方式。
如果你能熟練掌握以上列出的技能,具備解決復雜問題和技術難點的能力,而且你能獨立開發一些比較復雜的功能模塊,那麼很榮幸地告訴你,你已經達到中級水平,薪資過萬對你來說簡直是小菜一碟。
11.C++標准參考
C++ reference
地址:https://en.cppreference.com/w/
C++ Coding Standard
地址:http://www.possibility.com/Cpp/CppCodingStandard.html
Standard C++
地址:https://isocpp.org/
State of C++ Evolution
地址:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2597.html
The C++ Resources Network
地址:http://www.cplusplus.com/
Draft C++ Standard: Contents
地址:http://eel.is/c++draft/
⑼ c語言編程時的模板裡面{跟//是什麼意思
{和}是匹配的
表示一個單獨的
程序段
。
當if
while
for等
這類
控制語句
,要包含多餘一句的語句時
需要用{}括起來。
//表示注釋
。
//之後到行末
只給編寫程序的人看,
編譯器
不會處理。
⑽ 求大神解釋一下:在c++編程中,一個函數模板可以應用在多個函數中嗎 例如:template<ty
如果你使用模板函數的話,就只能每個函數一個模板參數,例如;
template<typename T> void a(T t){...}
template<typename T> void b(T t){...}
template<typename T> void c(T t){...}
a,b,c三個函數的模板參數T互不相關,如果你想幾個函數使用共同的模板參數的話,你應當使用模板類,例如:
template<typename T> class MyClass
{
void a(T t){...}
void b(T t){...}
void c(T t){...}
};
這樣a,b,c三個函數的參數類型T就是統一類型了。在這里T對應a,b,c三個函數來說僅僅是參數類型,而不是模板參數了。