個人的に私は、非常に良い記事を感じるトップ、再版ので、
序文
以前の私たちは「について語った関数ポインタ機能のジャンプテーブル- 」、今日はプログラミング技術を参照してください。私たちは、最初に簡単な計算を実装する方法を見て。
初期リリース
まずは、我々は方法を考えることができるものすべての、シンプルな電卓を実装してみましょうか?switchステートメントやelseステートメント場合。はい、初心者には二つの方法、この実装で見てみましょうと考えるでしょう。ここでは、操作の種類の定義をswitch文を選択し、ユーザーは次のようにcalc1.cコードは、選択されたハンドラ・プロセスに対応し、操作の種類に合わせて、操作の種類を選択します。
/*calc1.c*/ する#include <stdio.hに> する#include <STDLIB.H> / *操作は列挙型として定義されている* / typedefを列挙 { OP_ADD = 0、 OP_SUB、 OP_MUL、 OP_DIV、 } op_type; / *演算処理機能* / ダブルADD(ダブルOP1、OP2ダブル) { リターンOP1 + OP2; } ダブルSUB(ダブルOP1、OP2ダブル) { 戻りOP1、OP2; } ダブルMUL(ダブルOP1、OP2ダブル) { リターン* OP2 OP1; } ダブルDIV(ダブルOP1、OP2ダブル) { 戻りOP1 / OP2; } ダブル計算値(int型のOP、ダブルOP1、OP2ダブル) { / *用いるスイッチは、操作の種類に応じて、オプションを選択します* / 二重の結果= 0; スイッチ(OP) { ケースOP_ADD: { 結果= ADD(OP1、OP2)。 ブレーク; } ケースOP_SUB: { 結果= SUB(OP1、OP2)。 ブレーク; } ケースOP_MUL: { 結果= MUL(OP1、OP2)。 ブレーク; } ケースOP_DIV: { 結果= DIV(OP1、OP2)。 ブレーク; } デフォルト: { のprintf( "未サポートopration \ n"); ブレーク; } } 結果を返します。 } int型のmain(int型ARGC、CHAR * ARGV []) { IF(4> ARGC) { のprintf( "用法:OP NUM1 NUM2 \ n"); printf( "OP [0:追加、1:副2:MUL; 3:DIV] \ n"); 0を返します。 } int型のOP = ATOI(ARGV [1])。 OP1 = atof(argvの[2])ダブル。 OP2 = atof(argvの[3])ダブル。 printf( "OP:%D、OP1:%1F、OP2:%1F \ nを。"、OP、OP1、OP2)。 二重結果= CALC(OP、OP1、OP2)。 printf( "結果は%.1f \ nは"、結果)。 0を返します。 }
次のように私の結果は以下のとおりです。
PS C:\ユーザー\ NL \デスクトップ\ ziln \ cTEST収録> \ CALC.EXE 0 2 3。
OP:0、OP1:2.0、OP2:3.0
結果は5.0であります
~~非常に完璧なように見えます
コンパイラスイッチケースの最適化を外したのは、この設計手法にはいくつかの欠点があります。
- 時の動作が増加すると、コードが増加すると、case文は、冗長となるであろう。
- 操作増加は、分枝の増加、動作時間に対応する処理が増加します。
- 保守性が悪いです。
操作より多くの時間CALC機能には長く維持することが難しくなるとき私たちは、コードは各追加操作は、あなたがブランチを追加する必要があることがわかります観察します。各case文の必要性を見つける前の最後の試合を実行するため、そして、いや、コンパイラの最適化場合は、ので、長い実行時間につながる可能性があります。
ファンクションジャンプテーブルのバージョン
各操作は、機能に対応しているので、あなたはすぐに対応する機能を見つけ、ちょうど添字の値を知って、操作への関数ポインタの配列、および各指標値の対応を定義することができます。配列の添字アクセスデータの効率が非常に高いことを私たちは皆知っています。次のようにこのバージョンでは実装さ:
する#include <stdio.hに> する#include <STDLIB.H> / *操作は列挙型として定義されている* / typedefを列挙 { OP_ADD = 0、 OP_SUB、 OP_MUL、 OP_DIV、 } op_type; / *参照ダブル、参考として二重機能ポインタ* / typedefは二重(* OP_FUNC)(二重、二重); typedefは構造体OP_STRUCT { op_typeのOPTYPE; //操作タイプ OP_FUNC opFun; //操作機能 } OP_STRUCT; / *演算処理機能とCALC1。 Cと同様、ここでは省略され、追加して自由である* / / *機能ジャンプテーブル* / 静的OP_STRUCT g_opStruct [] = { {OP_ADD、ADDを}、 {OP_SUB、SUB}、 {OP_MUL、MUL}、 {OP_DIV、DIV} }。 / *操作の最大数* / INT =はsizeof g_opNum静的(g_opStruct)/はsizeof(OP_STRUCT); ダブルCalcの(int型のOP、ダブルOP1、OP2ダブル) { IF(OP> OP = g_opNum || <0) { のprintf( "unknow opration \ N-")。 0リターン; } / * *機能ダイレクト選択動作/アクションタイプ リターンg_opStruct [OP] .opFun(OP1、OP2); } 同じ関数* /省略する/ *メインcalc1.c INTメイン(int型ARGC、CHAR * ARGV []) { IF(4> ARGC)。 { のprintf( "使用方法:OP NUM1 NUM2 \ N-"); のprintf( "OP [0:追加,. 1:小、2:MUL ;. 3:DIV] \ N-" ); 戻り0; } int型のOP = ATOI(ARGV [1]); ダブルatof OP1 =(ARGV [2])。 ダブルOP2 atof =(ARGV [3])。 printf( "OP:%D、OP1:%1F、OP2:%1F \ nを。"、OP、OP1、OP2)。 二重結果= CALC(OP、OP1、OP2)。 printf( "結果は%.1f \ nは"、結果)。 0を返します。 }
CALC機能は、指示や操作の種類に応じて所望の演算処理機能を選択します。時間の複雑さはO(1)です。さらに、時には、操作の一種で、唯一の関数テーブルg_opStructの増加に作動するのCALC機能を変更する必要はありませんを追加することが望まれます。そして、戻り値の演算処理は、2つのパラメータの使用の二重の機能二重です:
1
|
typedefは、二重(* OP_FUNC)(ダブル、ダブル);
|
関数ポインタはOP_FUNC、後者の態様を使用するタイプとして定義されます。
また、あなたはまた、キーコード1行だけで、CALC機能は非常にシンプルである見ることができます。
概要
そこでは、例えば、0を返す例外を場所を最適化することができ、この記事では多くの例があり、同じように、結果として処理することができる、本明細書のジャンプテーブルと簡単な例を除いて、switch文。そして、分岐の同じタイプのため、ジャンプテーブルを使用する方法を検討することができ、ノートにも、ポイントのジャンプテーブルを使用して配列の境界です。
ジャンプテーブルは、それがすべての場合に実際の状況の使用を必要とするかどうかを決定する、switch文を置き換えることができないが、単なるアイデアです。
考えます
なぜ強調シンプルな電卓の最初のバージョンの説明に:削除コンパイラスイッチをケースに最適化されますか?