www.久久久久|狼友网站av天堂|精品国产无码a片|一级av色欲av|91在线播放视频|亚洲无码主播在线|国产精品草久在线|明星AV网站在线|污污内射久久一区|婷婷综合视频网站

當(dāng)前位置:首頁(yè) > 公眾號(hào)精選 > 嵌入式云IOT技術(shù)圈
[導(dǎo)讀]數(shù)據(jù)壓倒一切。如果選擇了正確的數(shù)據(jù)結(jié)構(gòu)并把一切組織的井井有條,正確的算法就不言自明。編程的核心是數(shù)據(jù)結(jié)構(gòu),而不是算法。 ——Rob Pike 說明 本文基于這樣的認(rèn)識(shí):數(shù)據(jù)是易變的,邏輯是穩(wěn)定的。 本文例舉的編程實(shí)現(xiàn)多為代碼片段,但不影響描述的完整性。

數(shù)據(jù)壓倒一切。如果選擇了正確的數(shù)據(jù)結(jié)構(gòu)并把一切組織的井井有條,正確的算法就不言自明。編程的核心是數(shù)據(jù)結(jié)構(gòu),而不是算法。

——Rob Pike

說明

本文基于這樣的認(rèn)識(shí):數(shù)據(jù)是易變的,邏輯是穩(wěn)定的。

本文例舉的編程實(shí)現(xiàn)多為代碼片段,但不影響描述的完整性。

本文例舉的編程雖然基于C語(yǔ)言,但其編程思想也適用于其他語(yǔ)言。

此外,本文不涉及語(yǔ)言相關(guān)的運(yùn)行效率討論。

1 概念提出

所謂表驅(qū)動(dòng)法(Table-Driven Approach)簡(jiǎn)而言之就是用查表的方法獲取數(shù)據(jù)。此處的“表”通常為數(shù)組,但可視為數(shù)據(jù)庫(kù)的一種體現(xiàn)。

根據(jù)字典中的部首檢字表查找讀音未知的漢字就是典型的表驅(qū)動(dòng)法,即以每個(gè)字的字形為依據(jù),計(jì)算出一個(gè)索引值,并映射到對(duì)應(yīng)的頁(yè)數(shù)。相比一頁(yè)一頁(yè)地順序翻字典查字,部首檢字法效率極高。

具體到編程方面,在數(shù)據(jù)不多時(shí)可用邏輯判斷語(yǔ)句(if…else或switch…case)來獲取值;但隨著數(shù)據(jù)的增多,邏輯語(yǔ)句會(huì)越來越長(zhǎng),此時(shí)表驅(qū)動(dòng)法的優(yōu)勢(shì)就開始顯現(xiàn)。

例如,用36進(jìn)制(A表示10,B表示11,…)表示更大的數(shù)字,邏輯判斷語(yǔ)句如下:

if(ucNum < 10)
{
ucNumChar = ConvertToChar(ucNum);
}
else if(ucNum == 10)
{
ucNumChar = 'A';
}
else if(ucNum == 11)
{
ucNumChar = 'B';
}
else if(ucNum == 12)
{
ucNumChar = 'C';
}
//... ...
else if(ucNum == 35)
{
ucNumChar = 'Z';
}

當(dāng)然也可以用switch…case結(jié)構(gòu),但實(shí)現(xiàn)都很冗長(zhǎng)。而用表驅(qū)動(dòng)法(將numChar存入數(shù)組)則非常直觀和簡(jiǎn)潔。如:

CHAR aNumChars[] = {'0', '1', '2', /*3~9*/'A', 'B', 'C', /*D~Y*/'Z'};
CHAR ucNumChar = aNumChars[ucNum % sizeof(aNumChars)];

像這樣直接將變量當(dāng)作下數(shù)組下標(biāo)來讀取數(shù)值的方法就是直接查表法。

注意,如果熟悉字符串操作,則上述寫法可以更簡(jiǎn)潔:

CHAR ucNumChar = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"[ucNum];

使用表驅(qū)動(dòng)法時(shí)需要關(guān)注兩個(gè)問題:一是如何查表,從表中讀取正確的數(shù)據(jù);二是表里存放什么,如數(shù)值或函數(shù)指針。前者參見1.1節(jié)“查表方式”內(nèi)容,后者參見1.2節(jié)“實(shí)戰(zhàn)示例”內(nèi)容。

1.1 查表方式

常用的查表方式有直接查找、索引查找和分段查找等。

1.1.1 直接查找

即直接通過數(shù)組下標(biāo)獲取到數(shù)據(jù)。如果熟悉哈希表的話,可以很容易看出這種查表方式就是哈希表的直接訪問法。

如獲取星期名稱,邏輯判斷語(yǔ)句如下:

if(0 == ucDay)
{
pszDayName = "Sunday";
}
else if(1 == ucDay)
{
pszDayName = "Monday";
}
//... ...
else if(6 == ucDay)
{
pszDayName = "Saturday";
}

而實(shí)現(xiàn)同樣的功能,可將這些數(shù)據(jù)存儲(chǔ)到一個(gè)表里:

CHAR *paNumChars[] = {"Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday",  "Saturday"};
CHAR *pszDayName = paNumChars[ucDay];

類似哈希表特性,表驅(qū)動(dòng)法適用于無(wú)需有序遍歷數(shù)據(jù),且數(shù)據(jù)量大小可提前預(yù)測(cè)的情況。

對(duì)于過于復(fù)雜和龐大的判斷,可將數(shù)據(jù)存為文件,需要時(shí)加載文件初始化數(shù)組,從而在不修改程序的情況下調(diào)整里面的數(shù)值。

有時(shí),訪問之前需要先進(jìn)行一次鍵值轉(zhuǎn)換。如表驅(qū)動(dòng)法表示端口忙閑時(shí),需將槽位端口號(hào)映射為全局編號(hào)。所生成的端口數(shù)目大小的數(shù)組,其下標(biāo)對(duì)應(yīng)全局端口編號(hào),元素值表示相應(yīng)端口的忙閑狀態(tài)。

1.1.2 索引查找

有時(shí)通過一次鍵值轉(zhuǎn)換,依然無(wú)法把數(shù)據(jù)(如英文單詞等)轉(zhuǎn)為鍵值。此時(shí)可將轉(zhuǎn)換的對(duì)應(yīng)關(guān)系寫到一個(gè)索引表里,即索引訪問。

如現(xiàn)有100件商品,4位編號(hào),范圍從0000到9999。此時(shí)只需要申請(qǐng)一個(gè)長(zhǎng)度為100的數(shù)組,且對(duì)應(yīng)2位鍵值。但將4位的編號(hào)轉(zhuǎn)換為2位的鍵值,可能過于復(fù)雜或沒有規(guī)律,最合適的方法是建立一個(gè)保存該轉(zhuǎn)換關(guān)系的索引表。采用索引訪問既節(jié)省內(nèi)存,又方便維護(hù)。比如索引A表示通過名稱訪問,索引B表示通過編號(hào)訪問。

1.1.3 分段查找

通過確定數(shù)據(jù)所處的范圍確定分類(下標(biāo))。有的數(shù)據(jù)可分成若干區(qū)間,即具有階梯性,如分?jǐn)?shù)等級(jí)。此時(shí)可將每個(gè)區(qū)間的上限(或下限)存到一個(gè)表中,將對(duì)應(yīng)的值存到另一表中,通過第一個(gè)表確定所處的區(qū)段,再由區(qū)段下標(biāo)在第二個(gè)表里讀取相應(yīng)數(shù)值。注意要留意端點(diǎn),可用二分法查找,另外可考慮通過索引方法來代替。

如根據(jù)分?jǐn)?shù)查績(jī)效等級(jí):

#define MAX_GRADE_LEVEL   (INT8U)5
DOUBLE aRangeLimit[MAX_GRADE_LEVEL] = {50.0, 60.0, 70.0, 80.0, 100.0};
CHAR *paGrades[MAX_GRADE_LEVEL] = {"Fail", "Pass", "Credit", "Distinction", "High Distinction"};

static CHAR* EvaluateGrade(DOUBLE dScore)
{
INT8U ucLevel = 0;
for(; ucLevel < MAX_GRADE_LEVEL; ucLevel++)
{
if(dScore < aRangeLimit[ucLevel])
return paGrades[ucLevel];
}
return paGrades[0];
}

上述兩張表(數(shù)組)也可合并為一張表(結(jié)構(gòu)體數(shù)組),如下所示:

typedef struct{
DOUBLE aRangeLimit;
CHAR *pszGrade;
}T_GRADE_MAP;

T_GRADE_MAP gGradeMap[MAX_GRADE_LEVEL] = {
{50.0, "Fail"},
{60.0, "Pass"},
{70.0, "Credit"},
{80.0, "Distinction"},
{100.0, "High Distinction"}
};

static CHAR* EvaluateGrade(DOUBLE dScore)
{
INT8U ucLevel = 0;
for(; ucLevel < MAX_GRADE_LEVEL; ucLevel++)
{
if(dScore < gGradeMap[ucLevel].aRangeLimit)
return gGradeMap[ucLevel].pszGrade;
}
return gGradeMap[0].pszGrade;
}

該表結(jié)構(gòu)已具備的數(shù)據(jù)庫(kù)的雛形,并可擴(kuò)展支持更為復(fù)雜的數(shù)據(jù)。其查表方式通常為索引查找,偶爾也為分段查找;當(dāng)索引具有規(guī)律性(如連續(xù)整數(shù))時(shí),退化為直接查找。

使用分段查找法時(shí)應(yīng)注意邊界,將每一分段范圍的上界值都考慮在內(nèi)。找出所有不在最高一級(jí)范圍內(nèi)的值,然后把剩下的值全部歸入最高一級(jí)中。有時(shí)需要人為地為最高一級(jí)范圍添加一個(gè)上界。

同時(shí)應(yīng)小心不要錯(cuò)誤地用“<”來代替“<=”。要保證循環(huán)在找出屬于最高一級(jí)范圍內(nèi)的值后恰當(dāng)?shù)亟Y(jié)束,同時(shí)也要保證恰當(dāng)處理范圍邊界。

1.2 實(shí)戰(zhàn)示例

本節(jié)多數(shù)示例取自實(shí)際項(xiàng)目。表形式為一維數(shù)組、二維數(shù)組和結(jié)構(gòu)體數(shù)組;表內(nèi)容有數(shù)據(jù)、字符串和函數(shù)指針?;诒眚?qū)動(dòng)的思想,表形式和表內(nèi)容可衍生出豐富的組合。

1.2.1 字符統(tǒng)計(jì)

問題:統(tǒng)計(jì)用戶輸入的一串?dāng)?shù)字中每個(gè)數(shù)字出現(xiàn)的次數(shù)。

普通解法主體代碼如下:

INT32U aDigitCharNum[10] = {0}; /* 輸入字符串中各數(shù)字字符出現(xiàn)的次數(shù) */
INT32U dwStrLen = strlen(szDigits);

INT32U dwStrIdx = 0;
for(; dwStrIdx < dwStrLen; dwStrIdx++)
{
switch(szDigits[dwStrIdx])
{
case '1':
aDigitCharNum[0]++;
break;
case '2':
aDigitCharNum[1]++;
break;
//... ...
case '9':
aDigitCharNum[8]++;
break;
}
}

這種解法的缺點(diǎn)顯而易見,既不美觀也不靈活。其問題關(guān)鍵在于未將數(shù)字字符與數(shù)組aDigitCharNum下標(biāo)直接關(guān)聯(lián)起來。

以下示出更簡(jiǎn)潔的實(shí)現(xiàn)方式:

for(; dwStrIdx < dwStrLen; dwStrIdx++)
{
aDigitCharNum[szDigits[dwStrIdx] - '0']++;
}

上述實(shí)現(xiàn)考慮到0也為數(shù)字字符。該解法也可擴(kuò)展至統(tǒng)計(jì)所有ASCII可見字符。

1.2.2 月天校驗(yàn)

問題:對(duì)給定年份和月份的天數(shù)進(jìn)行校驗(yàn)(需區(qū)分平年和閏年)。

普通解法主體代碼如下:

switch(OnuTime.Month)
{
case 1:
case 3:
case 5:
case 7:
case 8:
case 10:
case 12:
if(OnuTime.Day>31 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~31)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
break;
case 2:
if(((OnuTime.Year%4 == 0) && (OnuTime.Year%100 != 0)) || (OnuTime.Year%400 == 0))
{
if(OnuTime.Day>29 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~29)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
}
else
{
if(OnuTime.Day>28 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~28)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
}
break;
case 4:
case 6:
case 9:
case 11:
if(OnuTime.Day>30 || OnuTime.Day<1)
{
CtcOamLog(FUNCTION_Pon,"Don't support this Day: %d(1~30)!!!\n", OnuTime.Day);
retcode = S_ERROR;
}
break;
default:
CtcOamLog(FUNCTION_Pon,"Don't support this Month: %d(1~12)!!!\n", OnuTime.Month);
retcode = S_ERROR;
break;
}

以下示出更簡(jiǎn)潔的實(shí)現(xiàn)方式:

#define MONTH_OF_YEAR 12    /* 一年中的月份數(shù) */

/* 閏年:能被4整除且不能被100整除,或能被400整除 */
#define IS_LEAP_YEAR(year) ((((year) % 4 == 0) && ((year) % 100 != 0)) || ((year) % 400 == 0))

/* 平年中的各月天數(shù),下標(biāo)對(duì)應(yīng)月份 */
INT8U aDayOfCommonMonth[MONTH_OF_YEAR] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

INT8U ucMaxDay = 0;
if((OnuTime.Month == 2) && (IS_LEAP_YEAR(OnuTime.Year)))
ucMaxDay = aDayOfCommonMonth[1] + 1;
else
ucMaxDay = aDayOfCommonMonth[OnuTime.Month-1];

if((OnuTime.Day < 1) || (OnuTime.Day > ucMaxDay)
{
CtcOamLog(FUNCTION_Pon,"Month %d doesn't have this Day: %d(1~%d)!!!\n",
OnuTime.Month, OnuTime.Day, ucMaxDay);
retcode = S_ERROR;
}

1.2.3 名稱構(gòu)造

問題:根據(jù)WAN接口承載的業(yè)務(wù)類型(Bitmap)構(gòu)造業(yè)務(wù)類型名稱字符串。

普通解法主體代碼如下:

void Sub_SetServerType(INT8U *ServerType, INT16U wan_servertype)
{
if ((wan_servertype & 0x0001) == 0x0001)
{
strcat(ServerType, "_INTERNET");
}
if ((wan_servertype & 0x0002) == 0x0002)
{
strcat(ServerType, "_TR069");
}
if ((wan_servertype & 0x0004) == 0x0004)
{
strcat(ServerType, "_VOIP");
}
if ((wan_servertype & 0x0008) == 0x0008)
{
strcat(ServerType, "_OTHER");
}
}

以下示出C語(yǔ)言中更簡(jiǎn)潔的實(shí)現(xiàn)方式:

/* 獲取var變量第bit位,編號(hào)從右至左 */
#define GET_BIT(var, bit) (((var) >> (bit)) & 0x1)
const CHAR* paSvrNames[] = {"_INTERNET", "_TR069", "_VOIP", "_OTHER"};
const INT8U ucSvrNameNum = sizeof(paSvrNames) / sizeof(paSvrNames[0]);

VOID SetServerType(CHAR *pszSvrType, INT16U wSvrType)
{
INT8U ucIdx = 0;
for(; ucIdx < ucSvrNameNum; ucIdx++)
{
if(1 == GET_BIT(wSvrType, ucIdx))
strcat(pszSvrType, paSvrNames[ucIdx]);
}
}

新的實(shí)現(xiàn)將數(shù)據(jù)和邏輯分離,維護(hù)起來非常方便。只要邏輯(規(guī)則)不變,則唯一可能的改動(dòng)就是數(shù)據(jù)(paSvrNames)。

1.2.4 值名解析

問題:根據(jù)枚舉變量取值輸出其對(duì)應(yīng)的字符串,如PORT_FE(1)輸出“Fe”。

//值名映射表結(jié)構(gòu)體定義,用于數(shù)值解析器
typedef struct{
INT32U dwElem; //待解析數(shù)值,通常為枚舉變量
CHAR* pszName; //指向數(shù)值所對(duì)應(yīng)解析名字符串的指針
}T_NAME_PARSER;

/******************************************************************************
* 函數(shù)名稱: NameParser
* 功能說明: 數(shù)值解析器,將給定數(shù)值轉(zhuǎn)換為對(duì)應(yīng)的具名字符串
* 輸入?yún)?shù): VOID *pvMap :值名映射表數(shù)組,含T_NAME_PARSER結(jié)構(gòu)體類型元素
VOID指針允許用戶在保持成員數(shù)目和類型不變的前提下,
定制更有意義的結(jié)構(gòu)體名和/或成員名。
INT32U dwEntryNum :值名映射表數(shù)組條目數(shù)
INT32U dwElem :待解析數(shù)值,通常為枚舉變量
INT8U* pszDefName :缺省具名字符串指針,可為空
* 輸出參數(shù): NA
* 返回值 : INT8U *: 數(shù)值所對(duì)應(yīng)的具名字符串
當(dāng)無(wú)法解析給定數(shù)值時(shí),若pszDefName為空,則返回?cái)?shù)值對(duì)應(yīng)的16進(jìn)制格式
字符串;否則返回pszDefName。
******************************************************************************/
INT8U *NameParser(VOID *pvMap, INT32U dwEntryNum, INT32U dwElem, INT8U* pszDefName)
{
CHECK_SINGLE_POINTER(pvMap, "NullPoniter");

INT32U dwEntryIdx = 0;
for(dwEntryIdx = 0; dwEntryIdx < dwEntryNum; dwEntryIdx++)
{
T_NAME_PARSER *ptNameParser = (T_NAME_PARSER *)pvMap;
if(dwElem == ptNameParser->dwElem)
{
return ptNameParser->pszName;
}
//ANSI標(biāo)準(zhǔn)禁止對(duì)void指針進(jìn)行算法操作;GNU標(biāo)準(zhǔn)則指定void*算法操作與char*一致。
//若考慮移植性,可將pvMap類型改為INT8U*,或定義INT8U*局部變量指向pvMap。
pvMap += sizeof(T_NAME_PARSER);
}

if(NULL != pszDefName)
{
return pszDefName;
}
else
{
static INT8U szName[12] = {0}; //Max:"0xFFFFFFFF"
sprintf(szName, "0x%X", dwElem);
return szName;
}
}

以下給出NameParser的簡(jiǎn)單應(yīng)用示例:

//UNI端口類型值名映射表結(jié)構(gòu)體定義
typedef struct{
INT32U dwPortType;
INT8U* pszPortName;
}T_PORT_NAME;
//UNI端口類型解析器
T_PORT_NAME gUniNameMap[] = {
{1, "Fe"},
{3, "Pots"},
{99, "Vuni"}
};
const INT32U UNI_NAM_MAP_NUM = (INT32U)(sizeof(gUniNameMap)/sizeof(T_PORT_NAME));
VOID NameParserTest(VOID)
{
INT8U ucTestIndex = 1;

printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Unknown", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 0, "Unknown")) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("DefName", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 0, "DefName")) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Fe", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 1, "Unknown")) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Pots", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 3, "Unknown")) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Vuni", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 99, NULL)) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("Unknown", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 255, "Unknown")) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("0xABCD", NameParser(gUniNameMap, UNI_NAM_MAP_NUM, 0xABCD, NULL)) ? "ERROR" : "OK");
printf("[%s]<Test Case %u> Result: %s!\n", __FUNCTION__, ucTestIndex++,
strcmp("NullPoniter", NameParser(NULL, UNI_NAM_MAP_NUM, 0xABCD, NULL)) ? "ERROR" : "OK");
}

gUniNameMap在實(shí)際項(xiàng)目中有十余個(gè)條目,若采用邏輯鏈實(shí)現(xiàn)將非常冗長(zhǎng)。

1.2.5 取值映射

問題:不同模塊間同一參數(shù)枚舉值取值可能有所差異,需要適配。

此處不再給出普通的switch…case或if…else if…else結(jié)構(gòu),而直接示出以下表驅(qū)動(dòng)實(shí)現(xiàn):

typedef struct{
PORTSTATE loopMEState;
PORTSTATE loopMIBState;
}LOOPMAPSTRUCT;

static LOOPMAPSTRUCT s_CesLoop[] = {
{NO_LOOP, e_ds1_looptype_noloop},
{PAYLOAD_LOOP, e_ds1_looptype_PayloadLoop},
{LINE_LOOP, e_ds1_looptype_LineLoop},
{PON_LOOP, e_ds1_looptype_OtherLoop},
{CES_LOOP, e_ds1_looptype_InwardLoop}};

PORTSTATE ConvertLoopMEStateToMIBState(PORTSTATE vPortState)
{
INT32U num = 0, ii;

num = ARRAY_NUM(s_CesLoop);
for(ii = 0; ii < num; ii++)
{
if(vPortState == s_CesLoop[ii].loopMEState)
return s_CesLoop[ii].loopMIBState;
}
return e_ds1_looptype_noloop;
}

相應(yīng)地,從loopMIBState映射到loopMEState需要定義一個(gè)ConvertLoopMIBStateToMEState函數(shù)。更進(jìn)一步,所有類似的一對(duì)一映射關(guān)系都必須如上的映射(轉(zhuǎn)換)函數(shù),相當(dāng)繁瑣。

事實(shí)上,從抽象層面看,該映射關(guān)系非常簡(jiǎn)單。提取共性后定義帶參數(shù)宏,如下所示:

/**********************************************************
* 功能描述:進(jìn)行二維數(shù)組映射表的一對(duì)一映射,用于參數(shù)適配
* 參數(shù)說明:map -- 二維數(shù)組映射表
elemSrc -- 映射源,即待映射的元素值
elemDest -- 映射源對(duì)應(yīng)的映射結(jié)果
direction -- 映射方向字節(jié),表示從數(shù)組哪列映射至哪列。
高4位對(duì)應(yīng)映射源列,低4位對(duì)應(yīng)映射結(jié)果列。
defaultVal -- 映射失敗時(shí)置映射結(jié)果為缺省值
* 示例:ARRAY_MAPPER(gCesLoopMap, 3, ucLoop, 0x10, NO_LOOP);
則ucLoop = 2(LINE_LOOP)
**********************************************************/
#define ARRAY_MAPPER(map, elemSrc, elemDest, direction, defaultVal) do{\
INT8U ucMapIdx = 0, ucMapNum = 0; \
ucMapNum = sizeof(map)/sizeof(map[0]); \
for(ucMapIdx = 0; ucMapIdx < ucMapNum; ucMapIdx++) \
{ \
if((elemSrc) == map[ucMapIdx][((direction)&0xF0)>>4]) \
{ \
elemDest = map[ucMapIdx][(direction)&0x0F]; \
break; \
} \
} \
if(ucMapIdx == ucMapNum) \
{ \
elemDest = (defaultVal); \
} \
}while(0)

參數(shù)取值轉(zhuǎn)換時(shí)直接調(diào)用統(tǒng)一的映射器宏,如下:

static INT8U gCesLoopMap[][2] = {
{NO_LOOP, e_ds1_looptype_noloop},
{PAYLOAD_LOOP, e_ds1_looptype_PayloadLoop},
{LINE_LOOP, e_ds1_looptype_LineLoop},
{PON_LOOP, e_ds1_looptype_OtherLoop},
{CES_LOOP, e_ds1_looptype_InwardLoop}};

ARRAY_MAPPER(gCesLoopMap, tPara.dwParaVal[0], dwLoopConf, 0x01, e_ds1_looptype_noloop);

另舉一例:

#define  CES_DEFAULT_JITTERBUF        (INT32U)2000   /* 默認(rèn)jitterbuf為2000us,而1幀=125us */
#define CES_JITTERBUF_STEP (INT32U)125 /* jitterbuf步長(zhǎng)為125us,即1幀 */
#define CES_DEFAULT_QUEUESIZE (INT32U)5
#define CES_DEFAULT_MAX_QUEUESIZE (INT32U)7

#define ARRAY_NUM(array) (sizeof(array) / sizeof((array)[0])) /* 數(shù)組元素個(gè)數(shù) */
typedef struct{
INT32U dwJitterBuffer;
INT32U dwFramePerPkt;
INT32U dwQueueSize;
}QUEUE_SIZE_MAP;
/* gCesQueueSizeMap也可以(JitterBuffer / FramePerPkt)值為索引,更加緊湊 */
static QUEUE_SIZE_MAP gCesQueueSizeMap[]= {
{1,1,1}, {1,2,1}, {2,1,2}, {2,2,1},
{3,1,3}, {3,2,1}, {4,1,3}, {4,2,1},
{5,1,4}, {5,2,3}, {6,1,4}, {6,2,3},
{7,1,4}, {7,2,3}, {8,1,4}, {8,2,3},
{9,1,5}, {9,2,4}, {10,1,5}, {10,2,4},
{11,1,5}, {11,2,4}, {12,1,5}, {12,2,4},
{13,1,5}, {13,2,4}, {14,1,5}, {14,2,4},
{15,1,5}, {15,2,4}, {16,1,5}, {16,2,4},
{17,1,6}, {17,2,5}, {18,1,6}, {18,2,5},
{19,1,6}, {19,2,5}, {20,1,6}, {20,2,5},
{21,1,6}, {21,2,5}, {22,1,6}, {22,2,5},
{23,1,6}, {23,2,5}, {24,1,6}, {24,2,5},
{25,1,6}, {25,2,5}, {26,1,6}, {26,2,5},
{27,1,6}, {27,2,5}, {28,1,6}, {28,2,5},
{29,1,6}, {29,2,5}, {30,1,6}, {30,2,5},
{31,1,6}, {31,2,5}, {32,1,6}, {32,2,5}};
/**********************************************************
* 函數(shù)名稱:CalcQueueSize
* 功能描述:根據(jù)JitterBuffer和FramePerPkt計(jì)算QueueSize
* 注意事項(xiàng):配置的最大緩存深度
* = 2 * JitterBuffer / FramePerPkt
* = 2 * N Packet = 2 ^ QueueSize
* JitterBuffer為125us幀速率的倍數(shù),
* FramePerPkt為每個(gè)分組的幀數(shù),
* QueueSize向上取整,最大為7。
**********************************************************/
INT32U CalcQueueSize(INT32U dwJitterBuffer, INT32U dwFramePerPkt)
{
INT8U ucIdx = 0, ucNum = 0;

//本函數(shù)暫時(shí)僅考慮E1
ucNum = ARRAY_NUM(gCesQueueSizeMap);
for(ucIdx = 0; ucIdx < ucNum; ucIdx++)
{
if((dwJitterBuffer == gCesQueueSizeMap[ucIdx].dwJitterBuffer) &&
(dwFramePerPkt == gCesQueueSizeMap[ucIdx].dwFramePerPkt))
{
return gCesQueueSizeMap[ucIdx].dwQueueSize;
}
}

return CES_DEFAULT_MAX_QUEUESIZE;
}

1.2.6 版本控制

問題:控制OLT與ONU之間的版本協(xié)商。ONU本地設(shè)置三比特控制字,其中bit2(MSB)~bit0(LSB)分別對(duì)應(yīng)0x21、0x30和0xAA版本號(hào);且bitX為0表示上報(bào)對(duì)應(yīng)版本號(hào),bitX為1表示不上報(bào)對(duì)應(yīng)版本號(hào)。其他版本號(hào)如0x20、0x13和0x1必須上報(bào),即不受控制。

最初的實(shí)現(xiàn)采用if…else if…else結(jié)構(gòu),代碼非常冗長(zhǎng),如下:

pstSendTlv->ucLength = 0x1f;
if (gOamCtrlCode == 0)
{
vosMemCpy(pstSendTlv->aucVersionList, ctc_oui, 3);
pstSendTlv->aucVersionList[3] = 0x30;
vosMemCpy(&(pstSendTlv->aucVersionList[4]), ctc_oui, 3);
pstSendTlv->aucVersionList[7] = 0x21;
vosMemCpy(&(pstSendTlv->aucVersionList[8]), ctc_oui, 3);
pstSendTlv->aucVersionList[11] = 0x20;
vosMemCpy(&(pstSendTlv->aucVersionList[12]), ctc_oui, 3);
pstSendTlv->aucVersionList[15] = 0x13;
vosMemCpy(&(pstSendTlv->aucVersionList[16]), ctc_oui, 3);
pstSendTlv->aucVersionList[19] = 0x01;
vosMemCpy(&(pstSendTlv->aucVersionList[20]), ctc_oui, 3);
pstSendTlv->aucVersionList[23] = 0xaa;
}
else if (gOamCtrlCode == 1)
{
vosMemCpy(pstSendTlv->aucVersionList, ctc_oui, 3);
pstSendTlv->aucVersionList[3] = 0x30;
vosMemCpy(&(pstSendTlv->aucVersionList[4]), ctc_oui, 3);
pstSendTlv->aucVersionList[7] = 0x21;
vosMemCpy(&(pstSendTlv->aucVersionList[8]), ctc_oui, 3);
pstSendTlv->aucVersionList[11] = 0x20;
vosMemCpy(&(pstSendTlv->aucVersionList[12]), ctc_oui, 3);
pstSendTlv->aucVersionList[15] = 0x13;
vosMemCpy(&(pstSendTlv->aucVersionList[16]), ctc_oui, 3);
pstSendTlv->aucVersionList[19] = 0x01;
}
//此處省略gOamCtrlCode == 2~6的處理代碼
else if (gOamCtrlCode == 7)
{
vosMemCpy(&(pstSendTlv->aucVersionList), ctc_oui, 3);
pstSendTlv->aucVersionList[3] = 0x20;
vosMemCpy(&(pstSendTlv->aucVersionList[4]), ctc_oui, 3);
pstSendTlv->aucVersionList[7] = 0x13;
vosMemCpy(&(pstSendTlv->aucVersionList[8]), ctc_oui, 3);
pstSendTlv->aucVersionList[11] = 0x01;
}

以下示出C語(yǔ)言中更簡(jiǎn)潔的實(shí)現(xiàn)方式(基于二維數(shù)組):

/**********************************************************************
* 版本控制字?jǐn)?shù)組定義
* gOamCtrlCode: Bitmap控制字。Bit-X為0時(shí)上報(bào)對(duì)應(yīng)版本,Bit-X為1時(shí)屏蔽對(duì)應(yīng)版本。
* CTRL_VERS_NUM: 可控版本個(gè)數(shù)。
* CTRL_CODE_NUM: 控制字個(gè)數(shù)。與CTRL_VERS_NUM有關(guān)。
* gOamVerCtrlMap: 版本控制字?jǐn)?shù)組。行對(duì)應(yīng)控制字,列對(duì)應(yīng)可控版本。
元素值為0時(shí)不上報(bào)對(duì)應(yīng)版本,元素值非0時(shí)上報(bào)該元素值。
* Note: 該數(shù)組旨在實(shí)現(xiàn)“數(shù)據(jù)與控制隔離”。后續(xù)若要新增可控版本,只需修改
-- CTRL_VERS_NUM
-- gOamVerCtrlMap新增行(控制字)
-- gOamVerCtrlMap新增列(可控版本)
**********************************************************************/
#define CTRL_VERS_NUM 3
#define CTRL_CODE_NUM (1<<CTRL_VERS_NUM)
u8_t gOamVerCtrlMap[CTRL_CODE_NUM][CTRL_VERS_NUM] = {
/* Ver21 Ver30 VerAA */
{0x21, 0x30, 0xaa}, /*gOamCtrlCode = 0*/
{0x21, 0x30, 0 }, /*gOamCtrlCode = 1*/
{0x21, 0, 0xaa}, /*gOamCtrlCode = 2*/
{0x21, 0, 0 }, /*gOamCtrlCode = 3*/
{ 0, 0x30, 0xaa}, /*gOamCtrlCode = 4*/
{ 0, 0x30, 0 }, /*gOamCtrlCode = 5*/
{ 0, 0, 0xaa}, /*gOamCtrlCode = 6*/
{ 0, 0, 0 } /*gOamCtrlCode = 7*/
};
#define INFO_TYPE_VERS_LEN 7 /* InfoType + Length + OUI + ExtSupport + Version */

u8_t verIdx = 0;
u8_t index = 0;
for(verIdx = 0; verIdx < CTRL_VERS_NUM; verIdx++)
{
if(gOamVerCtrlMap[gOamCtrlCode][verIdx] != 0)
{
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = gOamVerCtrlMap[gOamCtrlCode][verIdx];
}
}
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = 0x20;
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = 0x13;
vosMemCpy(&pstSendTlv->aucVersionList[index], ctc_oui, 3);
index += 3;
pstSendTlv->aucVersionList[index++] = 0x01;

pstSendTlv->ucLength = INFO_TYPE_VERS_LEN + index;

1.2.7 消息處理

問題:終端輸入不同的打印命令,調(diào)用相應(yīng)的打印函數(shù),以控制不同級(jí)別的打印。

這是一段消息(事件)驅(qū)動(dòng)程序。本模塊接收其他模塊(如串口驅(qū)動(dòng))發(fā)送的消息,根據(jù)消息中的打印級(jí)別字符串和開關(guān)模式,調(diào)用不同函數(shù)進(jìn)行處理。常見的實(shí)現(xiàn)方法如下:

void logall(void)
{
g_log_control[0] = 0xFFFFFFFF;
}

void noanylog(void)
{
g_log_control[0] = 0;
}

void logOam(void)
{
g_log_control[0] |= (0x01 << FUNCTION_Oam);
}
void nologOam(void)
{
g_log_control[0] &= ~(0x01 << FUNCTION_Oam);
}
//... ...
void logExec(char *name, INT8U enable)
{
CtcOamLog(FUNCTION_Oam,"log %s %d\n",name,enable);
if (enable == 1) /*log*/
{
if (strcasecmp(name,"all") == 0) { /*字符串比較,不區(qū)分大小寫*/
logall();
} else if (strcasecmp(name,"oam") == 0) {
logOam();
} else if (strcasecmp(name,"pon") == 0) {
logPon();
//... ...
} else if (strcasecmp(name,"version") == 0) {
logVersion();
}
else if (enable == 0) /*nolog*/
{
if (strcasecmp(name,"all") == 0) {
noanylog();
} else if (strcasecmp(name,"oam") == 0) {
nologOam();
} else if (strcasecmp(name,"pon") == 0) {
nologPon();
//... ...
} else if (strcasecmp(name,"version") == 0) {
nologVersion();
}
else
{
printf("bad log para\n");
}
}

以下示出C語(yǔ)言中更簡(jiǎn)潔的實(shí)現(xiàn)方式:

typedef struct{
OAM_LOG_OFF = (INT8U)0,
OAM_LOG_ON = (INT8U)1
}E_OAM_LOG_MODE;
typedef FUNC_STATUS (*OamLogHandler)(VOID);
typedef struct{
CHAR *pszLogCls; /* 打印級(jí)別 */
E_OAM_LOG_MODE eLogMode; /* 打印模式 */
OamLogHandler fnLogHandler; /* 打印函數(shù) */
}T_OAM_LOG_MAP;

T_OAM_LOG_MAP gOamLogMap[] = {
{"all", OAM_LOG_OFF, noanylog},
{"oam", OAM_LOG_OFF, nologOam},
//... ...
{"version", OAM_LOG_OFF, nologVersion},

{"all", OAM_LOG_ON, logall},
{"oam", OAM_LOG_ON, logOam},
//... ...
{"version", OAM_LOG_ON, logVersion}
};
INT32U gOamLogMapNum = sizeof(gOamLogMap) / sizeof(T_OAM_LOG_MAP);

VOID logExec(CHAR *pszName, INT8U ucSwitch)
{
INT8U ucIdx = 0;
for(; ucIdx < gOamLogMapNum; ucIdx++)
{
if((ucSwitch == gOamLogMap[ucIdx].eLogMode) &&
(!strcasecmp(pszName, gOamLogMap[ucIdx].pszLogCls));
{
gOamLogMap[ucIdx].fnLogHandler();
return;
}
}
if(ucIdx == gOamLogMapNum)
{
printf("Unknown LogClass(%s) or LogMode(%d)!\n", pszName, ucSwitch);
return;
}
}

這種表驅(qū)動(dòng)消息處理實(shí)現(xiàn)的優(yōu)點(diǎn)如下:

1.增強(qiáng)可讀性,消息如何處理從表中一目了然。

2.增強(qiáng)可擴(kuò)展性。更容易修改,要增加新的消息,只要修改數(shù)據(jù)即可,不需要修改流程。

3.降低復(fù)雜度。通過把程序邏輯的復(fù)雜度轉(zhuǎn)移到人類更容易處理的數(shù)據(jù)中來,從而達(dá)到控制復(fù)雜度的目標(biāo)。

4.主干清晰,代碼重用。

若各索引為順序枚舉值,則建立多維數(shù)組(每維對(duì)應(yīng)一個(gè)索引),根據(jù)下標(biāo)直接定位到處理函數(shù),效率會(huì)更高。

注意,考慮到本節(jié)實(shí)例中l(wèi)ogOam/logPon或nologOam/nologPon等函數(shù)本質(zhì)上是基于打印級(jí)別的比特操作,因此可進(jìn)一步簡(jiǎn)化。以下例舉其相似實(shí)現(xiàn):

/* 日志控制類型定義 */
typedef enum
{
LOG_NORM = 0, /* 未分類日志,可用于通用日志 */
LOG_FRM, /* Frame,OMCI幀日志 */
LOG_PON, /* Pon,光鏈路相關(guān)日志 */
LOG_ETH, /* Ethernet,Layer2以太網(wǎng)日志 */
LOG_NET, /* Internet,Layer3網(wǎng)絡(luò)日志 */
LOG_MULT, /* Multicast,組播日志 */
LOG_QOS, /* QOS,流量日志 */
LOG_CES, /* Ces,TDM電路仿真日志 */
LOG_VOIP, /* Voip,語(yǔ)音日志 */
LOG_ALM, /* Alarm,告警日志 */
LOG_PERF, /* Performance,性能統(tǒng)計(jì)日志 */
LOG_VER, /* Version,軟件升級(jí)日志 */
LOG_XDSL, /* xDsl日志 */
LOG_DB, /* 數(shù)據(jù)庫(kù)操作日志 */
//新日志類型在此處擴(kuò)展,共支持32種日志類型
LOG_ALL = UINT_MAX /* 所有日志類型 */
}E_LOG_TYPE;

/*****************************************************************************
* 變量名稱:gOmciLogCtrl
* 作用描述:OMCI日志控制字,BitMap格式(比特編號(hào)從LSB至MSB依次為Bit0->BitN)。
* Bit0~N分別對(duì)應(yīng)E_LOG_TYPE各枚舉值(除LOG_ALL外)。
* BitX為0時(shí)關(guān)閉日志類型對(duì)應(yīng)的日志功能,BitX為1時(shí)則予以打開。
* 變量范圍:該變量為四字節(jié)整型靜態(tài)全局變量,即支持32種日志類型。
* 訪問說明:通過GetOmciLogCtrl/SetOmciLogCtrl/OmciLogCtrl函數(shù)訪問/設(shè)置控制字。
*****************************************************************************/
static INT32U gOmciLogCtrl = 0;

//日志類型字符串?dāng)?shù)組,下標(biāo)為各字符串所對(duì)應(yīng)的日志類型枚舉值。
static const INT8U* paLogTypeName[] = {
"Norm", "Frame", "Pon", "Ethernet", "Internet",
"Multicast", "Qos", "Ces", "Voip", "Alarm",
"Performance", "Version", "Xdsl", "Db"
};
static const INT8U ucLogTypeNameNum = sizeof(paLogTypeName) / sizeof(paLogTypeName[0]);

static VOID SetGlobalLogCtrl(E_LOG_TYPE eLogType, INT8U ucLogSwitch)
{
if(LOG_ON == ucLogSwitch)
gOmciLogCtrl = LOG_ALL;
else
gOmciLogCtrl = 0;
}
static VOID SetSpecificLogCtrl(E_LOG_TYPE eLogType, INT8U ucLogSwitch)
{
if(LOG_ON == ucLogSwitch)
SET_BIT(gOmciLogCtrl, eLogType);
else
CLR_BIT(gOmciLogCtrl, eLogType);
}

VOID OmciLogCtrl(CHAR *pszLogType, INT8U ucLogSwitch)
{
if(0 == strncasecmp(pszLogType, "All", LOG_TYPE_CMP_LEN))
{
SetGlobalLogCtrl(LOG_ALL, ucLogSwitch);
return;
}

INT8U ucIdx = 0;
for(ucIdx = 0; ucIdx < ucLogTypeNameNum; ucIdx++)
{
if(0 == strncasecmp(pszLogType, paLogTypeName[ucIdx], LOG_TYPE_CMP_LEN))
{
SetSpecificLogCtrl(ucIdx, ucLogSwitch);
printf("LogType: %s, LogSwitch: %s\n", paLogTypeName[ucIdx],
(1==ucLogSwitch)?"On":"Off");
return;
}
}

OmciLogHelp();
}

1.2.8 掩碼表

參見《采用掩碼方式簡(jiǎn)化產(chǎn)品國(guó)家地區(qū)支持能力的表示》一文。

該例實(shí)現(xiàn)中用到消息、掩碼、函數(shù)指針等概念。

2 編程思想

表驅(qū)動(dòng)法屬于數(shù)據(jù)驅(qū)動(dòng)編程的一種,其核心思想在《Unix編程藝術(shù)》和《代碼大全2》中均有闡述。兩者均認(rèn)為人類閱讀復(fù)雜數(shù)據(jù)結(jié)構(gòu)遠(yuǎn)比復(fù)雜的控制流程容易,即相對(duì)于程序邏輯,人類更擅長(zhǎng)于處理數(shù)據(jù)。

本節(jié)將由Unix設(shè)計(jì)原則中的“分離原則”和“表示原則”展開。

分離原則:策略同機(jī)制分離,接口同引擎分離

機(jī)制即提供的功能;策略即如何使用功能。

策略的變化要遠(yuǎn)遠(yuǎn)快于機(jī)制的變化。將兩者分離,可以使機(jī)制相對(duì)保持穩(wěn)定,而同時(shí)支持策略的變化。

代碼大全中提到“隔離變化”的概念,以及設(shè)計(jì)模式中提到的將易變化的部分和不易變化的部分分離也是這個(gè)思路。

表示原則:把知識(shí)疊入數(shù)據(jù)以求邏輯質(zhì)樸而健壯

即使最簡(jiǎn)單的程序邏輯讓人類來驗(yàn)證也很困難,但就算是很復(fù)雜的數(shù)據(jù),對(duì)人類來說,還是相對(duì)容易推導(dǎo)和建模的。數(shù)據(jù)比編程邏輯更容易駕馭。在復(fù)雜數(shù)據(jù)和復(fù)雜代碼中選擇,寧可選擇前者。更進(jìn)一步,在設(shè)計(jì)中,應(yīng)該主動(dòng)將代碼的復(fù)雜度轉(zhuǎn)移到數(shù)據(jù)中去(參考“版本控制”)。

在“消息處理”示例中,每個(gè)消息處理的邏輯不變,但消息可能是變化的。將容易變化的消息和不容易變化的查找邏輯分離,即“隔離變化”。此外,該例也體現(xiàn)消息內(nèi)部的處理邏輯(機(jī)制)和不同的消息處理(策略)分離。

數(shù)據(jù)驅(qū)動(dòng)編程可以應(yīng)用于:

1.函數(shù)級(jí)設(shè)計(jì),如本文示例。2.程序級(jí)設(shè)計(jì),如用表驅(qū)動(dòng)法實(shí)現(xiàn)狀態(tài)機(jī)。3.系統(tǒng)級(jí)設(shè)計(jì),如DSL。

注意,數(shù)據(jù)驅(qū)動(dòng)編程不是全新的編程模型,只是一種設(shè)計(jì)思路,在Unix/Linux開源社區(qū)應(yīng)用很多。數(shù)據(jù)驅(qū)動(dòng)編程中,數(shù)據(jù)不但表示某個(gè)對(duì)象的狀態(tài),實(shí)際上還定義程序的流程,這點(diǎn)不同于面向?qū)ο笤O(shè)計(jì)中的數(shù)據(jù)“封裝”。

3 附錄

3.1 網(wǎng)友觀點(diǎn)

(以下觀點(diǎn)摘自博客園網(wǎng)友“七心葵”的回帖,非常具有啟發(fā)性。)

Booch的《面向?qū)ο蠓治雠c設(shè)計(jì)》一書中,提到所有的程序設(shè)計(jì)語(yǔ)言大概有3個(gè)源流:結(jié)構(gòu)化編程;面向?qū)ο缶幊?;?shù)據(jù)驅(qū)動(dòng)編程。

我認(rèn)為數(shù)據(jù)驅(qū)動(dòng)編程的本質(zhì)是“參數(shù)化抽象”的思想,不同于OO的“規(guī)范化抽象”的思想。

數(shù)據(jù)驅(qū)動(dòng)編程在網(wǎng)絡(luò)游戲開發(fā)過程中很常用,但是少有人專門提到這個(gè)詞。

數(shù)據(jù)驅(qū)動(dòng)編程有很多名字:元編程,解釋器/虛擬機(jī),LOP/微語(yǔ)言/DSL等。包括聲明式編程、標(biāo)記語(yǔ)言、甚至所見即所得的拖放控件,都算是數(shù)據(jù)驅(qū)動(dòng)編程的一種吧。

數(shù)據(jù)驅(qū)動(dòng)編程可以幫助處理復(fù)雜性,和結(jié)構(gòu)化編程、OO 均可相容。(正交的角度)

將變和不變的部分分離,策略和機(jī)制分離,由此聯(lián)想到的還有:(數(shù)據(jù)和代碼的分離,微語(yǔ)言和解釋器的分離,被生成代碼和代碼生成器的分離);更近一步:(微內(nèi)核插件式體系結(jié)構(gòu))

元編程應(yīng)該說是更加泛化的數(shù)據(jù)驅(qū)動(dòng)編程,元編程不是新加入一個(gè)間接層,而是退居一步,使得當(dāng)前的層變成一個(gè)間接層。元編程分為靜態(tài)元編程(編譯時(shí))和動(dòng)態(tài)元編程(運(yùn)行時(shí)),靜態(tài)元編程本質(zhì)上是一種 代碼生成技術(shù)或者編譯器技術(shù);動(dòng)態(tài)元編程一般通過解釋器(或虛擬機(jī))加以實(shí)現(xiàn)。

數(shù)據(jù)驅(qū)動(dòng)編程當(dāng)然也不應(yīng)該說是“反抽象的”,但的確與“OO抽象”的思維方式是迥然不同,涇渭分明的,如TAOUP一書中所述:“在Unix的模塊化傳統(tǒng)和圍繞OO語(yǔ)言發(fā)展起來的使用模式之間,存在著緊張的對(duì)立關(guān)系”應(yīng)該說數(shù)據(jù)驅(qū)動(dòng)編程的思路與結(jié)構(gòu)化編程和OO是正交的,更類似一種“跳出三界外,不在五行中”的做法。

編程和人的關(guān)系

人類心智的限制,一切的背后都有人的因素作為依據(jù):

a 人同時(shí)關(guān)注的信息數(shù)量:7+-2 (所以要分模塊)

b 人接收一組新信息的平均時(shí)間5s (所以要簡(jiǎn)單,系統(tǒng)總的模塊數(shù)不要太多)

c 人思維的直觀性(人的視覺能力和模糊思維能力),這意味這兩點(diǎn):

A “直”——更善于思考自己能直接接觸把玩的東西;(所以要“淺平透”、使用具象的設(shè)計(jì),要盡量代碼中只有順直的流程),

B “觀”——更善于觀圖而不是推算邏輯;(所以要表驅(qū)動(dòng)法,數(shù)據(jù)驅(qū)動(dòng)編程,要UML,要可視化編程——當(dāng)然MDA是太理想化了)

d 人不能持續(xù)集中注意力(人在一定的代碼行數(shù)中產(chǎn)生的bug數(shù)量的比例是一定的,所以語(yǔ)言有具有表現(xiàn)力,要體現(xiàn)表達(dá)的經(jīng)濟(jì)性)

所以要機(jī)制與策略分離,要數(shù)據(jù)和代碼分離(數(shù)據(jù)驅(qū)動(dòng)編程),要微語(yǔ)言,要DSL,要LOP……

e 人是有創(chuàng)造欲,有現(xiàn)實(shí)利益心的(只要偶可能總是不夠遵從規(guī)范,或想創(chuàng)造規(guī)范謀利——只要成本能承受,在硬件領(lǐng)域就不行)

另外,開一個(gè)有意思的玩笑,Unix編程藝術(shù)藝術(shù)的英文縮寫為TAOUP,我覺得可以理解為UP之TAO——向上拋出之道——將復(fù)雜的易變的邏輯作為數(shù)據(jù)或更高層代碼拋給上層!

3.2 函數(shù)指針

“消息處理”一節(jié)示例中的函數(shù)指針有點(diǎn)插件結(jié)構(gòu)的味道??蓪?duì)這些插件進(jìn)行方便替換,新增,刪除,從而改變程序的行為。而這種改變,對(duì)事件處理函數(shù)的查找又是隔離的(隔離變化)。

函數(shù)指針非常有用,但使用時(shí)需注意其缺陷:無(wú)法檢查參數(shù)(parameter)和返回值(return value)的類型。因?yàn)楹瘮?shù)已經(jīng)退化成指針,而指針不攜帶這些類型信息。缺少類型檢查,當(dāng)參數(shù)或返回值不一致時(shí),可能會(huì)造成嚴(yán)重的錯(cuò)誤。

例如,定義三個(gè)函數(shù),分別具有兩個(gè)參數(shù):

int max(int x, int y)  {  return x>y?x:y;  }
int min(int x, int y) { return x<y?x:y; }
int add(int x, int y) { return x+y; }

而處理函數(shù)卻定義為:

int process(int x, int y, int (*f)())  {  return (*f)(x, y);  }

其中,第三個(gè)參數(shù)是一個(gè)沒有參數(shù)且返回int型變量的函數(shù)指針。但后面卻用process(a,b,max)的方式進(jìn)行調(diào)用,max帶有兩個(gè)參數(shù)。若編譯器未檢查出錯(cuò)誤,而又不小心將return (*f)(x,y);寫成return (*f)(x);,那么后果可能很嚴(yán)重。

因此在C語(yǔ)言中使用函數(shù)指針時(shí),一定要小心"類型陷阱"。

往期精彩

專為MCU項(xiàng)目開發(fā)提速的代碼框架BabyOS

編程修養(yǎng)(精品文,建議認(rèn)真品讀并實(shí)踐)

談?wù)勛霎a(chǎn)品、做項(xiàng)目以及標(biāo)準(zhǔn)化相關(guān)的話題

嵌入式C語(yǔ)言代碼優(yōu)化方案(深度好文,建議花時(shí)間研讀并收藏)

若覺得本次分享的文章對(duì)您有幫助,隨手點(diǎn)[在看]并轉(zhuǎn)發(fā)分享,也是對(duì)我的支持。



免責(zé)聲明:本文內(nèi)容由21ic獲得授權(quán)后發(fā)布,版權(quán)歸原作者所有,本平臺(tái)僅提供信息存儲(chǔ)服務(wù)。文章僅代表作者個(gè)人觀點(diǎn),不代表本平臺(tái)立場(chǎng),如有問題,請(qǐng)聯(lián)系我們,謝謝!

本站聲明: 本文章由作者或相關(guān)機(jī)構(gòu)授權(quán)發(fā)布,目的在于傳遞更多信息,并不代表本站贊同其觀點(diǎn),本站亦不保證或承諾內(nèi)容真實(shí)性等。需要轉(zhuǎn)載請(qǐng)聯(lián)系該專欄作者,如若文章內(nèi)容侵犯您的權(quán)益,請(qǐng)及時(shí)聯(lián)系本站刪除。
換一批
延伸閱讀

9月2日消息,不造車的華為或?qū)⒋呱龈蟮莫?dú)角獸公司,隨著阿維塔和賽力斯的入局,華為引望愈發(fā)顯得引人矚目。

關(guān)鍵字: 阿維塔 塞力斯 華為

倫敦2024年8月29日 /美通社/ -- 英國(guó)汽車技術(shù)公司SODA.Auto推出其旗艦產(chǎn)品SODA V,這是全球首款涵蓋汽車工程師從創(chuàng)意到認(rèn)證的所有需求的工具,可用于創(chuàng)建軟件定義汽車。 SODA V工具的開發(fā)耗時(shí)1.5...

關(guān)鍵字: 汽車 人工智能 智能驅(qū)動(dòng) BSP

北京2024年8月28日 /美通社/ -- 越來越多用戶希望企業(yè)業(yè)務(wù)能7×24不間斷運(yùn)行,同時(shí)企業(yè)卻面臨越來越多業(yè)務(wù)中斷的風(fēng)險(xiǎn),如企業(yè)系統(tǒng)復(fù)雜性的增加,頻繁的功能更新和發(fā)布等。如何確保業(yè)務(wù)連續(xù)性,提升韌性,成...

關(guān)鍵字: 亞馬遜 解密 控制平面 BSP

8月30日消息,據(jù)媒體報(bào)道,騰訊和網(wǎng)易近期正在縮減他們對(duì)日本游戲市場(chǎng)的投資。

關(guān)鍵字: 騰訊 編碼器 CPU

8月28日消息,今天上午,2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)開幕式在貴陽(yáng)舉行,華為董事、質(zhì)量流程IT總裁陶景文發(fā)表了演講。

關(guān)鍵字: 華為 12nm EDA 半導(dǎo)體

8月28日消息,在2024中國(guó)國(guó)際大數(shù)據(jù)產(chǎn)業(yè)博覽會(huì)上,華為常務(wù)董事、華為云CEO張平安發(fā)表演講稱,數(shù)字世界的話語(yǔ)權(quán)最終是由生態(tài)的繁榮決定的。

關(guān)鍵字: 華為 12nm 手機(jī) 衛(wèi)星通信

要點(diǎn): 有效應(yīng)對(duì)環(huán)境變化,經(jīng)營(yíng)業(yè)績(jī)穩(wěn)中有升 落實(shí)提質(zhì)增效舉措,毛利潤(rùn)率延續(xù)升勢(shì) 戰(zhàn)略布局成效顯著,戰(zhàn)新業(yè)務(wù)引領(lǐng)增長(zhǎng) 以科技創(chuàng)新為引領(lǐng),提升企業(yè)核心競(jìng)爭(zhēng)力 堅(jiān)持高質(zhì)量發(fā)展策略,塑強(qiáng)核心競(jìng)爭(zhēng)優(yōu)勢(shì)...

關(guān)鍵字: 通信 BSP 電信運(yùn)營(yíng)商 數(shù)字經(jīng)濟(jì)

北京2024年8月27日 /美通社/ -- 8月21日,由中央廣播電視總臺(tái)與中國(guó)電影電視技術(shù)學(xué)會(huì)聯(lián)合牽頭組建的NVI技術(shù)創(chuàng)新聯(lián)盟在BIRTV2024超高清全產(chǎn)業(yè)鏈發(fā)展研討會(huì)上宣布正式成立。 活動(dòng)現(xiàn)場(chǎng) NVI技術(shù)創(chuàng)新聯(lián)...

關(guān)鍵字: VI 傳輸協(xié)議 音頻 BSP

北京2024年8月27日 /美通社/ -- 在8月23日舉辦的2024年長(zhǎng)三角生態(tài)綠色一體化發(fā)展示范區(qū)聯(lián)合招商會(huì)上,軟通動(dòng)力信息技術(shù)(集團(tuán))股份有限公司(以下簡(jiǎn)稱"軟通動(dòng)力")與長(zhǎng)三角投資(上海)有限...

關(guān)鍵字: BSP 信息技術(shù)
關(guān)閉
關(guān)閉