tft每日頭條

 > 科技

 > 這4種統計代碼執行耗時才足夠優雅

這4種統計代碼執行耗時才足夠優雅

科技 更新时间:2024-08-09 13:32:39

這4種統計代碼執行耗時才足夠優雅?前言前面我們講到了《函數指針》,今天我們看一個編程技巧-函數跳轉表我們先來看如何實現一個簡易計算器,下面我們就來說一說關于這4種統計代碼執行耗時才足夠優雅?我們一起去了解并探讨一下這個問題吧!

這4種統計代碼執行耗時才足夠優雅(讓你的代碼更加優雅的編程技巧-跳轉表)1

這4種統計代碼執行耗時才足夠優雅

前言

前面我們講到了《函數指針》,今天我們看一個編程技巧-函數跳轉表。我們先來看如何實現一個簡易計算器。

初始版本

讓我們實現一個簡易計算器,我們首先能想到的方式是什麼?switch語句或者if else語句。沒錯,初學就會想到的兩種方式,我們來看看這種實現方式。這裡我們選擇switch語句,定義一個操作類型,用戶選擇操作類型與操作類型匹配時,選擇對應的處理函數進行處理,calc1.c代碼如下:

/*calc1.c*/ #include<stdio.h> #include<stdlib.h> /*将操作定義為枚舉類型*/ typedef enum { OP_ADD = 0, OP_SUB, OP_MUL, OP_DIV, }OP_TYPE; /*加減乘除處理函數*/ double ADD(double op1,double op2) { return op1 op2; } double SUB(double op1,double op2) { return op1-op2; } double MUL(double op1,double op2) { return op1*op2; } double DIV(double op1,double op2) { return op1/op2; } double calc(int op,double op1,double op2) { /*使用switch,根據操作類型,選擇操作*/ double result = 0; switch(op) { case OP_ADD: { result = ADD(op1,op2); break; } case OP_SUB: { result = SUB(op1,op2); break; } case OP_MUL: { result = MUL(op1,op2); break; } case OP_DIV: { result = DIV(op1,op2); break; } default: { printf("unsupport opration\n"); break; } } return result; } int main(int argc,char *argv[]) { if(4 > argc) { printf("usage:op num1 num2\n"); printf("op[0:add,1:sub,2:mul;3:div]\n"); return 0; } int op = atoi(argv[1]); double op1 = atof(argv[2]); double op2 = atof(argv[3]); printf("op:%d,op1:%.1f,op2:%.1f\n",op,op1,op2); double result = calc(op,op1,op2); printf("result is %.1f\n",result); return 0; }

除去編譯器對switch進行優化的情況,這種設計方式有以下幾個缺點:

  • 操作增加時,代碼增加,case語句将變得冗長。
  • 操作增加時,分支增加,處理對應操作的時間将會增長。
  • 代碼可維護性較差。

我們觀察代碼會發現,每增加一種操作,就需要增加一個分支,當操作越來越多的時候calc函數将會變得冗長且不易維護。并且如果沒有編譯器優化,由于在找到最終匹配的之前,每一個case語句都需要執行,因此可能将導緻運行時間變長。

函數跳轉表版本

既然每一個操作對應一個函數,那麼完全可以定義一個函數指針數組,而每個操作對應一個下标值,隻要知道下标值,很快就可以找到對應的函數。我們都知道,數組下标方式訪問數據效率是很高的。該版本實現如下:

calc2.c #include<stdio.h> #include<stdlib.h> /*将操作定義為枚舉類型*/ typedef enum { OP_ADD = 0, OP_SUB, OP_MUL, OP_DIV, }OP_TYPE; /*入參為double,出參為double的函數指針*/ typedef double (*OP_FUNC)(double,double); typedef struct OP_STRUCT { OP_TYPE opType;//操作類型 OP_FUNC opFun; //操作函數 }OP_STRUCT; /*加減乘除處理函數與calc1.c相同,這裡省略,可自行添加*/ /*函數跳轉表*/ static OP_STRUCT g_opStruct[] = { {OP_ADD ,ADD}, {OP_SUB ,SUB}, {OP_MUL ,MUL}, {OP_DIV ,DIV}, }; /*最大操作數量*/ static int g_opNum = sizeof(g_opStruct)/sizeof(OP_STRUCT); double calc(int op,double op1,double op2) { if(op >= g_opNum || op < 0) { printf("unknow opration\n"); return 0; } /*根據操作類型直接選擇操作函數*/ return g_opStruct[op].opFun(op1,op2); } /*main函數與calc1.c相同,這裡省略*/

calc函數中,直接根據操作類型而選擇需要的操作處理函數。時間複雜度為O(1)。另外,當需要新增一種操作時,不需要修改calc函數,隻需要在函數表g_opStruct中增加一種操作即可。而操作處理是一個返回值為double,入參為兩個double的函數,因此使用:

typedef double (*OP_FUNC)(double,double);

将該類型函數指針定義為OP_FUNC,方面後面的使用。

另外,還可以看到calc函數很簡潔,關鍵代碼隻有一行。

總結

本文的例子有很多可以優化的地方,例如異常時返回0,可能被當成結果等等,這裡隻是用switch語句和跳轉表作簡單的示例。而對于同類型的分支處理,完全可以考慮使用跳轉表的方式,使用跳轉表還需要注意的一點就是數組越界

跳轉表隻是一種思路,它并不是在所有情況下都可以替代switch語句,可根據實際情況決定是否需要使用。

思考

為什麼在說明第一個版本的簡易計算器的時候,強調:除去編譯器對switch進行優化的情況?

微信公衆号【編程珠玑】:專注但不限于分享計算機編程基礎,Linux,C語言,C ,算法,數據庫等編程相關[原創]技術文章,号内包含大量經典電子書和視頻學習資源。歡迎一起交流學習,一起修煉計算機“内功”,知其然,更知其所以然。

,

更多精彩资讯请关注tft每日頭條,我们将持续为您更新最新资讯!

查看全部

相关科技资讯推荐

热门科技资讯推荐

网友关注

Copyright 2023-2024 - www.tftnews.com All Rights Reserved