10 things you can do to write better code 写好代码的十个秘诀

80
1 10 Things You Can Do To Wr ite Better Code 写写写写写写写写写 写写 Development Manager Microsoft Research, China

Upload: odin

Post on 17-Jan-2016

130 views

Category:

Documents


0 download

DESCRIPTION

10 Things You Can Do To Write Better Code 写好代码的十个秘诀. 林斌 Development Manager Microsoft Research, China. 一流代码. 一流代码的特性. 鲁棒 - S olid and Robust Code 简洁 - Maintainable and Simple Code 高效 - Fast Code 简短 - Small Code 共享 - Re-usable Code 可测试 - Testable Code 可移植 - Portable Code. - PowerPoint PPT Presentation

TRANSCRIPT

Page 1: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

1

10 Things You Can Do To Write Better Code

写好代码的十个秘诀

林斌Development Manager

Microsoft Research, China

Page 2: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

2

一流代码的特性

• 鲁棒 - Solid and Robust Code

• 简洁 - Maintainable and Simple Code

• 高效 - Fast Code

• 简短 - Small Code

• 共享 - Re-usable Code

• 可测试 - Testable Code

• 可移植 - Portable Code

一流代码

Page 3: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

3

Why is this code bad?

void MyGirlFriendFunc(CORP_DATA InputRec, int CrntQtr, EMP_DATA EmpRec, float EstimRevenue, float YTDRevenue, int ScreenX, int ScreenY, COLOR_TYPE newColor, COLOR_TYPE PrevColor, STATUS_TYPE status, int ExpenseType)

{ int i;for (i=1; i<100; i++) InputRec.revenue[i] = 0;

for (i=1; i<100; i++)InputRec.expense[i]= CorpExpense[i];

UpdateCorpDatabase(EmpRec);EstimRevenue =YTDRevenue*4.0/(float)CrntQtr;

NewColor = PreColor;Status = Success;if (ExpenseType== 1) for (i=1;i<12;i++) Profit[i] = Revenue[i]-Expense.Type1[i];

else if (ExpenseType == 2) Profit[i] = Revenue[i] - Expense.Type2[i];

else if (ExpenseType==3) { Profit[i] =Revenue[i] -Expense.Type3[i];

}

Page 4: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

4

Why is this code bad?

void MyGirlFriendFunc(CORP_DATA InputRec, int CrntQtr, EMP_DATA EmpRec, float EstimRevenue, float YTDRevenue, int ScreenX, int ScreenY, COLOR_TYPE newColor, COLOR_TYPE PrevColor, STATUS_TYPE status, int ExpenseType)

{ int i;for (i=1; i<100; i++) InputRec.revenue[i] = 0;

for (i=1; i<100; i++)InputRec.expense[i]= CorpExpense[i];

UpdateCorpDatabase(EmpRec);EstimRevenue =YTDRevenue*4.0/(float)CrntQtr;

NewColor = PreColor;Status = Success;if (ExpenseType== 1) for (i=1;i<12;i++) Profit[i] = Revenue[i]-Expense.Type1[i];

else if (ExpenseType == 2) Profit[i] = Revenue[i] - Expense.Type2[i];

else if (ExpenseType==3) { Profit[i] =Revenue[i] -Expense.Type3[i];

}

Page 5: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

5

Because…

• Bad function name – Maintainability• Crash if CrntQtr equals 0 – Robustness• No comments – Maintainability• Unnecessary for loop – Performance• The function has no single purpose –

Reusability• Bad layout – Simplicity & Maintainability• None testable if ExpenseType is 1 – Testability• Many more: too many arguments, abuse usage

of 100, 4.0, etc, un-use parameters, none-documented parameters…

Page 6: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

6

代码高手十大秘诀

1. 集百家之长 , 归我所用 - Follow Basic Coding Style

2. 取个好名字 - Use Naming Conventions

3. 凌波微步 , 未必摔跤 - Evil goto’s? Maybe Not…

4. 先发制人 , 后发制于人 - Practice Defensive Coding

5. 见招拆招 , 滴水不漏 - Handle The Error Cases: They Will Occur!

Page 7: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

7

代码高手十大秘诀 (Cont.)

6. 熟习剑法刀术 , 所向无敌 - Learn Win32 API Seriously

7. 双手互搏 , 无坚不摧 - Test, but don’t stop there

8. 活用段言 - Use, don’t abuse, assertions

9. 草木皆兵 , 不可大意 - Avoid Assumptions

10.最高境界 , 无招胜有招 - Stop writing so much code

Page 8: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

8

• Your code may outlive you or your memory!• Think about the maintainer• Comment your code

– File Header– Function Header– Inline Commenting

• Pick a good name for your functions and variables• Set Tab/Indent to 4 characters• Align your code!• Less arguments

1. 集百家之长 , 归我所用 - Follow Basic Coding Style

Page 9: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

9

Coding Style (Cont.)

• Use all uppercase with underscores for macro.#define MY_MACRO_NUMBER 100

• Declare all your variables at the start of function.• Avoid hard coding constant. Use enum or macro.

#define MY_MAX_LENGTH 1024

• User braces for one line codeIf (m_fInitialized) {

hr = S_OK;

}

• Limit the length of a single function

Page 10: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

10

Spot the defect

DWORD    Status = NO_ERROR;

LPWSTR ptr;

Status = f( ..., &ptr );

if( Status isnot NO_ERROR or ptr is NULL ) goto cleanup;

Page 11: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

11

Spot the defect

DWORD    Status = NO_ERROR;

LPWSTR ptr;

Status = f( ..., &ptr );

if( Status isnot NO_ERROR or ptr is NULL ) goto cleanup;

is??? isnot??? or???new C/C++ keywords? No.overloaded operators? No.just some confusing macros …

Page 12: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

12

A better version

DWORD    Status = NO_ERROR;

LPWSTR ptr = NULL;

Status = f( ..., &ptr );

if( (Status != NO_ERROR) || (ptr == NULL) ) goto cleanup;

• Make your intent explicitUse existing language constructs

Everybody understands them

• Avoid future problems: Initializeparenthesize

Page 13: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

13

What’s the maximum length of a function?

• Look at this example

• Watch out for functions > 200 lines!

• Study in 1986 on IBM’s OS/360: the most error-prone routines => longer than 500 lines.

• Study in 1991 on 148,000-line of code: functions < 143 lines => 2.4 times less expensive to fix than longer functions.

Omsg_cxx.tlkOver 1400 lines!!!

Page 14: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

14

Look at these codes

• Let’s look at these codes from our own projects:– Small functions:– Larger functions:

• Then look at those from Windows 2000 source tree:– NT Kernel Mode Code:– NT User Mode CPP File:– NT User Mode Header File:

align_cpp.tlk

variable_cpp.tlk

Event_c.tlk

write_cpp.tlk

Write_h.tlk

Page 15: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

15

2. 取个好名字 - Use Naming Conventions

• Hungarian Notation– [Prefix]-BaseTag-Name

• Standard [Prefix]:p – A pointer. CHAR* psz;rg – An array. DWORD rgType[…];c – A count of items. DWORD cBook;h – A handle. HANDLE hFile;

• Standard “BaseTag”void -> v int -> iBOOL -> f UINT -> uiBYTE -> b CHAR -> chWCHAR -> wch ULONG -> ulLONG -> l DWORD -> dwHRESULT -> hr fn -> functionsz -> NULL str USHORT, SHORT, WORD -> w

• C++ member variables start with: m_• Global variables start with: g_

Page 16: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

16

Let’s try these…

• A flag indicates whether a C++ object has been initialized:• BOOL m_fInitialized;

• A Session ID:• DWORD dwSessionID;

• A ref count in a C++ object:• LONG m_cRef; // skip the ‘l’

• A Pointer to BYTE buffer:• BYTE* pbBuffer;

• A global logfile filename buffer:• CHAR g_szLogFile[MAX_PATH];

• A pointer to a global logfile:• CHAR* g_pszLogFile;

Page 17: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

17

3. 凌波微步 , 未必摔跤 - Evil goto’s? Maybe Not…

• Why goto is bad?– Creates unreadable code…– Causes compiler to generate non-optimal

code…

• Why goto is good?– Cleaner code– Single entry, single exit– Less duplicate code

Page 18: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

18

Spot the gotos…

START_LOOP:If (fStatusOk) { if (fDataAvaiable) { i = 10; goto MID_LOOP; } else { goto END_LOOP; }} else { for (I = 0; I < 100; I++) {MID_LOOP: // lots of code here … … } goto START_LOOP;}END_LOOP:

Page 19: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

19

BAD gotos!!!

START_LOOP:If (fStatusOk) { if (fDataAvaiable) { i = 10; goto MID_LOOP; } else { goto END_LOOP; }} else { for (I = 0; I < 100; I++) {MID_LOOP: // lots of code here … … } goto START_LOOP;}END_LOOP:

BAD!!!

Page 20: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

20

How about this one?

pszHisName = (CHAR*)malloc(256);

if (pszHisName == NULL) {

free(pszMyName);

free(pszHerName);

return hr;

}

free(pszMyName);

free(pszHerName);

free(pszHisName);

return hr;

}

HRESULT Init(){ pszMyName = (CHAR*)malloc(256); if (pszMyName == NULL) { return hr; } pszHerName = (CHAR*)malloc(256); if (pszHerName == NULL) { free(pszMyName); return hr; }

Page 21: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

21

Duplicate code…

pszHisName = (CHAR*)malloc(256);

if (pszHisName == NULL) {

free(pszMyName);

free(pszHerName);

return hr;

}

free(pszMyName);

free(pszHerName);

free(pszHisName);

return hr;

}

HRESULT Init(){ pszMyName = (CHAR*)malloc(256); if (pszMyName == NULL) { return hr; } pszHerName = (CHAR*)malloc(256); if (pszHerName == NULL) { free(pszMyName); return hr; }

Page 22: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

22

Harder to maintain…

pszHisName = (CHAR*)malloc(256);

if (pszHisName == NULL) {

free(pszMyName);

free(pszHerName);

free(pszItsName);

return hr;

}

free(pszMyName);

free(pszHerName);

free(pszHisName);

free(pszItsName);

return hr;

}

HRESULT Init(){ pszMyName = (CHAR*)malloc(256); if (pszMyName == NULL) { return hr; } pszHerName = (CHAR*)malloc(256); if (pszHerName == NULL) { free(pszMyName); return hr; } pszItsName = (CHAR*)malloc(256); if (pszItsName == NULL) { free(pszMyName); free(pszHerName); return hr; }

Page 23: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

23

How about this one?

… …

Exit:

if (pszMyName)

free(pszMyName);

if (pszHeName)

free(pszHeName);

if (pszHiName)

free(pszHiName);

return hr;

}

HRESULT Init(){ pszMyName =(CHAR*)malloc(…); if (pszMyName == NULL) goto Exit; pszHeName =(CHAR*)malloc(…); if (pszHeName == NULL) goto Exit; pszHiName =(CHAR*)malloc(…); if (pszHiName == NULL) goto Exit;

Page 24: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

24

Single entry, single exit. No duplicate code…

… …

Exit:

if (pszMyName)

free(pszMyName);

if (pszHeName)

free(pszHeName);

if (pszHiName)

free(pszHiName);

return hr;

}

HRESULT Init(){ pszMyName =(CHAR*)malloc(…); if (pszMyName == NULL) goto Exit; pszHeName =(CHAR*)malloc(…); if (pszHeName == NULL) goto Exit; pszHiName =(CHAR*)malloc(…); if (pszHiName == NULL) goto Exit;

Page 25: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

25

Easy to maintain…

… …

Exit:

if (pszMyName)

free(pszMyName);

if (pszHeName)

free(pszHeName);

if (pszItName)

free(pszItName);

if (pszHiName)

free(pszHiName);

return hr;

}

HRESULT Init(){ pszMyName =(CHAR*)malloc(…); if (pszMyName == NULL) goto Exit; pszHeName =(CHAR*)malloc(…); if (pszHeName == NULL) goto Exit; pszItName =(CHAR*)malloc(…); if (pszItName == NULL) goto Exit; pszHiName =(CHAR*)malloc(…); if (pszHiName == NULL) goto Exit;

Page 26: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

26

Use the following goto guidelines

• Single entry, single exit? – use goto

• Don’t use more than one goto labels

• Use goto’s that go forward, not backward

• Make sure a goto doesn’t create unreachable code

Page 27: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

27

4. 先发制人 , 后发制于人 - Practice Defensive Coding

• Initialize variables• Check return values• Keep it simples

– Usually, simplicity = performance

• Keep it clean– Multi-thread clean– Minimize casts

• Avoid miscalculations– Divide by Zero– Signed vs. Unsigned

Page 28: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

28

Spot the defect

HRESULT hr;

LPSTR str = (LPSTR)LocalAlloc(LPTR, size);

if (str){

... // process appropriately

hr = S_OK;

}

return hr;

Page 29: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

29

Spot the defect

HRESULT hr;

LPSTR str = (LPSTR)LocalAlloc(LPTR, size);

if (str){

... // process appropriately

hr = S_OK;

}

return hr;

Returning a random status on failure

Page 30: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

30

A better version???

HRESULT hr = S_OK;

LPSTR str = (LPSTR)LocalAlloc(LPTR, size);

if (str){

... // process appropriately

hr = S_OK;

}

return hr;

Page 31: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

31

Probably not

HRESULT hr = S_OK;

LPSTR str = (LPSTR)LocalAlloc(LPTR, size);

if (str){

... // process appropriately

hr = S_OK;

}

return hr;

Return success if nothing happened?• Think before you fix!

Page 32: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

32

A better version!

HRESULT hr = S_OK;

LPSTR str = (LPSTR)LocalAlloc(LPTR, size);

if (str){

... // process appropriately

hr = S_OK;

} else {

hr = E_OUTOFMEMORY;

}

return hr;

Page 33: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

33

Spot the detect

BOOL SomeProblem(USHORT x)

{

while (--x >= 0) {

[…]

}

return TRUE;

}

Page 34: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

34

Spot the detect

BOOL SomeProblem(USHORT x)

{

while (--x >= 0) {

[…]

}

return TRUE;

}

• Infinite loop! Unsigned never negative!

Page 35: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

35

Spot the detect

wcscpy(wcServerIpAdd, WinsAnsiToUnicode(cAddr, NULL));

Note: WinsAnsiToUnicode is a private API which allocates a new UNICODE string given an ANSI string.

Page 36: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

36

Spot the detect

wcscpy(wcServerIpAdd,  WinsAnsiToUnicode(cAddr, NULL));

WinsAnsiToUnicode can return NULL … which will cause a program crash

WinsAnsiToUnicode can return allocated memory … which will cause a leak

Page 37: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

37

A better version

LPWSTR tmp;

tmp = WinsAnsiToUnicode(cAddr, NULL);

if (tmp != NULL) {

wcscpy(wcServerIpAdd, tmp);

WinsFreeMemory((PVOID)tmp);

} else {

return(ERROR_NOT_ENOUGH_MEMORY);

}

Page 38: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

38

5. 见招拆招 , 滴水不漏 - Handle The Error Cases: They Will Occur!

• Common examples:– Out of memory– Exceptions being thrown– Registry keys missing– Networks going down

• This is the hardest code to test …– … so it better be right!

• Don’t fail in C++ object constructor, you can’t detect it!

Page 39: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

39

Error cases (continued)

• In an error case, the code should– Recover gracefully

• Get to a consistent state• Clean up any resources it has allocated

– Let its caller know• Interface should define and document the behavior (return

status, throw exception)• Code should adhere to that definition

• What not to do– Ignore the error– Crash– Exit

Page 40: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

40

Spot the detect

CWInfFile::CWInfFile() {

m_plLines = new TPtrList(); // ...

m_plSections = new TPtrList(); // ...

m_ReadContext.posLine = m_plLines->end();

. . .

}

Page 41: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

41

Spot the detect

CWInfFile::CWInfFile() {

m_plLines = new TPtrList(); // ...

m_plSections = new TPtrList(); // ...

m_ReadContext.posLine = m_plLines->end();

. . .

}

MFC’s operator new throws an exception– … causing a leak when out of memory

Page 42: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

42

A better version?

CWInfFile::CWInfFile() {try { m_plLines = new TPtrList(); // ... m_plSections = new TPtrList(); // ... m_ReadContext.posLine = m_plLines->end(); . . .} catch ( . . . ) { if (m_plLines) delete m_plLines; if (m_plSections) delete m_plSections; }}

Page 43: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

43

No!

CWInfFile::CWInfFile() {try { m_plLines = new TPtrList(); // ... m_plSections = new TPtrList(); // ... m_ReadContext.posLine = m_plLines->end(); . . .} catch ( . . . ) { if (m_plLines) delete m_plLines; if (m_plSections) delete m_plSections; }}

Values may be uninitializedValues may be uninitialized Think before you fix!Think before you fix!

Page 44: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

44

A better version

CWInfFile::CWInfFile() : m_plLines(NULL), m_plSections(NULL) {

try {

m_plLines = new TPtrList(); // ...

m_plSections = new TPtrList(); // ...

m_ReadContext.posLine = m_plLines->end();

. . .

} catch ( . . . ) {

if (m_plLines) delete m_plLines;

if (m_plSections) delete m_plSections;

}

}

Page 45: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

45

But wait, there is more

CWInfFile::CWInfFile() : m_plLines(NULL), m_plSections(NULL) {

try {

m_plLines = new TPtrList(); // ...

m_plSections = new TPtrList(); // ...

m_ReadContext.posLine = m_plLines->end();

. . .

} catch ( . . . ) {

if (m_plLines) delete m_plLines;

if (m_plSections) delete m_plSections;

}

}

When not use MFC, When not use MFC, operator newoperator new may may return NULL…return NULL…

Page 46: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

46

Spot the defect

Class foo {

private:

CHAR* m_pszName;

DWORD m_cbName;

public:

foo(CHAR* pszName);

CHAR* GetName()

{return m_pszName;}

};

foo::foo(CHAR* pszName){ m_pszName = (BYTE*) malloc(NAME_LEN); if (m_pszName == NULL) { return; } strcpy(m_pszName, pszName); m_cbName = strlen(pszName);}

Page 47: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

47

Spot the defect

Class foo {

private:

CHAR* m_pszName;

DWORD m_cbName;

public:

foo(CHAR* pszName);

CHAR* GetName()

{return m_pszName;}

};

foo::foo(CHAR* pszName){ m_pszName = (BYTE*) malloc(NAME_LEN); if (m_pszName == NULL) { return; } strcpy(m_pszName, pszName); m_cbName = strlen(pszName);}

If malloc() fails, this code will crash:foo* pfoo = new foo(“MyName”);if (pfoo) { CHAR c = *(pfoo->GetName());}

Page 48: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

48

A better version

Class foo {private: CHAR* m_pszName; DWORD m_cbName;

public: foo(); HRESULT Init(CHAR* pszName);…};

foo::foo(){ m_cbName = 0; m_pszName = NULL;}

HRESULT foo::Init(CHAR* pszName){ HRESULT hr = S_OK; if (pszName) { m_cbName = lstrlen(pszName); m_pszName = (CHAR*)malloc(m_cbName+1); if (m_pszName == NULL) { hr = E_OUTOFMEMORY; return hr; } strcpy(m_pszName, pszName); } else { hr = E_INVALIDARG; } return hr;}

Page 49: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

49

6. 熟习剑法刀术 , 所向无敌 - Learn Win32 API Seriously

• Learn Win32 APIs, from MSDN• Why Win32 APIs?

– Well designed– Well tested– Easy to understand– Improve performance dramatically

• Understand the ones you use, read the SDK doc in MSDN

• Pick the best one based on your need

Page 50: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

50

Spot the defect

for( i=0; i< 256; i++) {

re[i] = 0;

im[i] = 0;

}

for( k = 0; k < 128; k++)

AvrSpec[k] = 0;

… …

#define FrameLen 200

for(k=0; k<5*40*FrameLen; k++)

lsp[k] = lsp[k + 40*FrameLen];

… …

for(k = 0; k < FrameLen; k++) {

audio[k] = vect[k];

}

… …

Page 51: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

51

Better version

for( i=0; i< 256; i++) {

re[i] = 0;

im[i] = 0;

}

for( k = 0; k < 128; k++)

AvrSpec[k] = 0;

… …

#define FrameLen 200

for(k=0; k<5*40*FrameLen; k++)

lsp[k] = lsp[k + 40*FrameLen];

… …

for(k = 0; k < FrameLen; k++) {

audio[k] = vect[k];

}

… …

ZeroMemory(re, 256);

ZeroMemory(im, 256);

ZeroMemory(AvrSpec, 128);

… …

#define FrameLen 200

MoveMemory(lsp, &(lsp[40*FrameLen]), 5*40*FrameLen);

… …

CopyMemory(audio, vect, FrameLen);

… …

Page 52: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

52

Spot the defect

TCHAR WindowsDir[MAX_PATH];TCHAR WindowsDir[MAX_PATH];

TCHAR DriveLetter;TCHAR DriveLetter;

GetWindowsDirectory(WindowsDir, MAX_PATH);GetWindowsDirectory(WindowsDir, MAX_PATH);

DriveLetter = WindowsDir[0];DriveLetter = WindowsDir[0];

......

Page 53: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

53

Spot the defect

TCHAR WindowsDir[MAX_PATH];TCHAR WindowsDir[MAX_PATH];

TCHAR DriveLetter;TCHAR DriveLetter;

GetWindowsDirectoryGetWindowsDirectory(WindowsDir, MAX_PATH);(WindowsDir, MAX_PATH);

DriveLetter = DriveLetter = WindowsDir[0]WindowsDir[0];;

......

Missing return value check …Missing return value check …

Operating on a random drive if Operating on a random drive if GetWindowsDirectory() failsGetWindowsDirectory() fails

Page 54: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

54

Can GetWindowsDirectory fail?

• MSDN has always said “yes”

• Implementation has always said “no”…• …Until Terminal Server!

– Computing GetWindowsDirectory may allocate memory

– If allocation fails, the call returns FALSE

• Result: potential catastrophic errors on Terminal Server

Page 55: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

55

A better version

TCHAR WindowsDir[MAX_PATH];TCHAR WindowsDir[MAX_PATH];

TCHAR DriveLetter;TCHAR DriveLetter;

if (!if (!GetWindowsDirectory(WindowsDir, GetWindowsDirectory(WindowsDir,

MAX_PATH)) MAX_PATH)) {{

return E_OUTOFMEMORY; // proper recoveryreturn E_OUTOFMEMORY; // proper recovery

}}

DriveLetter = WindowsDir[0];DriveLetter = WindowsDir[0];

......

Page 56: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

56

7. 双手互搏 , 无坚不摧 - Test, but don’t stop there

• Step through in the debugger– Especially the error code paths

• Test as much of your code as you can– If you haven’t tested it, it probably doesn’t work

– If you don’t know how much you’ve tested, it’s less than you expect

• So find out!

• Code review– Have somebody else read it

– And read somebody else’s code!

Page 57: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

57

Spot the defect

HRESULT hr = NOERROR;

// Make sure the arguments passed are valid

if (NULL != m_paConnections) {

... // do the right thing

}

else hr = S_FALSE;

if (SUCCEEDED(hr)) {

if (NULL != m_paConnections[0].pUnk) . . .

Page 58: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

58

Spot the defect

HRESULT hr = NOERROR;

// Make sure the arguments passed are valid

if (NULL != m_paConnections) {

... // do the right thing

}

else hr = S_FALSE;

if (SUCCEEDED(hr)) {

if (NULL != m_paConnections[0].pUnk) . . .

• SUCCEEDED(S_FALSE) == TRUE• Crash if m_paConnections == NULL

– Easily simulatable in the debugger

Page 59: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

59

Better version

HRESULT hr = NOERROR;

// Make sure the arguments passed are valid

if (NULL != m_paConnections) {

... // do the right thing

}

else hr = E_INVALIDARG;

if (SUCCEEDED(hr)) {

if (NULL != m_paConnections[0].pUnk) . . .

Page 60: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

60

Spot the defect

//

// Get file size first

//

DWORD dwFileSize = GetFileSize( hFile, NULL );

if ( dwFileSize = -1 ) {

// what can we do ? keep silent

ErrorTrace(0, "GetFileSize failed with %d", GetLastError());

return;

}

Note: GetFileSize returns –1 if it fails.

Page 61: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

61

Spot the defect

//

// Get file size first

//

DWORD dwFileSize = GetFileSize( hFile, NULL );

if ( dwFileSize = -1 ) {

// what can we do ? keep silent

ErrorTrace(0, "GetFileSize failed with %d", GetLastError());

return;

}

Note: GetFileSize returns –1 if it fails.

Always return as errors occur without Always return as errors occur without proceedingproceeding

Page 62: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

62

Better version

//

// Get file size first

//

DWORD dwFileSize = GetFileSize( hFile, NULL );

if ( -1 == dwFileSize ) {

// what can we do ? keep silent

ErrorTrace(0, "GetFileSize failed with %d", GetLastError());

return;

}

Note: GetFileSize returns –1 if it fails.

Compiler will catch the error if you miss “=“Compiler will catch the error if you miss “=“

Page 63: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

63

8. 活用段言 - Use, don’t abuse, Assertions

• Use Assertions because– Useful for documentation

• Describe assumptions• Describe “impossible” situations

– Useful for debugging• Help diagnose problems• May detect problems earlier

• Don’t abuse Assertions because– Assertions are usually no-ops in a free/release build

• Don’t use Assertions in situations that can occur in practice

Page 64: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

64

Spot the defect

CHAR * pch;

pch = GlobalAlloc(10);

ASSERT(pch != NULL);

pch[0] = 0;

Page 65: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

65

Spot the defect

CHAR * pch;

pch = GlobalAlloc(10);

ASSERT(pch != NULL);

pch[0] = 0;

• ASSERT is (usually) a no-op in a free build– And asserts should not be used for things that will

happen in practice

Program crash when out of memory

Page 66: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

66

A better version!

CHAR * pch;

pch = GlobalAlloc(10);

If (NULL == pch) {

return E_OUTOFMEMORY;

}

pch[0] = 0;

Page 67: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

67

9. 草木皆兵 , 不可大意 - Avoid Assumptions

• Don’t assume good behavior– There will be badly-written apps

• Don’t assume you can trust data– Validate data you can’t trust

• Public interfaces• User input• File contents

– Test your validation code!

• Don’t assume you know all your users– An interface may have unexpected clients– … and not all of them are friendly

• Document the assumptions you make, use ASSERT

Page 68: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

68

Spot the defect

CIRRealExtractor::CIRRealExtractor( HRESULT* hr,

const BITMAPINFOHEADER *lpbmi, const void *lpvBits, const RECT *prectBlock)

{

… …

*hr = ContainDIB(lpbmi, lpvBits, prectBlock);

};

Page 69: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

69

Spot the defect

CIRRealExtractor::CIRRealExtractor( HRESULT* hr,

const BITMAPINFOHEADER *lpbmi, const void *lpvBits, const RECT *prectBlock)

{

… …

*hr = ContainDIB(lpbmi, lpvBits, prectBlock);

};

• Crash if “hr” is passed in as NULL!– Crash in a C++ constructor is the last thing

you want to do…

Page 70: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

70

A better version

CIRRealExtractor::CIRRealExtractor( HRESULT* hr,

const BITMAPINFOHEADER *lpbmi, const void *lpvBits, const RECT *prectBlock)

{

… …

if (hr) {

*hr = ContainDIB(lpbmi, lpvBits, prectBlock);

}

};

Page 71: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

71

But wait, a even better version…

CIRRealExtractor::CIRRealExtractor()… …HRESULT CIRRealExtractor::Init( const BITMAPINFOHEADER *lpbmi, const void *lpvBits,

const RECT *prectBlock){… … // lpbmi, lpvBits, and prectBlock can never be NULL ASSERT( lpbmi != NULL); ASSERT( lpvBits != NULL); ASSERT( prectBlock != NULL);

return ContainDIB(lpbmi, lpvBits, prectBlock);};

Page 72: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

72

Spot the defect

• Random reboots in low memory situations– Immediate cause: services.exe exits.– Why would services.exe exit?

• Would you believe …– … that services often call wscicmp (and other

string functions)?– … that calling these functions can cause your

program to exit?

Page 73: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

73

Code inside wscicmp()

… …

if ( _locktable[locknum] == NULL ) {

if ( (pcs =

_malloc_crt(sizeof(CRITICAL_SECTION)))

== NULL )

_amsg_exit(_RT_LOCK);

… …

Page 74: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

74

The defect

… …

if ( _locktable[locknum] == NULL ) {

if ( (pcs =

_malloc_crt(sizeof(CRITICAL_SECTION)))

== NULL )

_amsg_exit(_RT_LOCK);

… …

• Wscicmp assumed the lock allocated successfully, or it’s ok to exit!

• Good or bad, I am the one with the bug!

Page 75: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

75

10. 最高境界 , 无招胜有招 - Stop writing so much code

• More code is not better!• Do you really need your own …

– Memory allocator? global operator new?– Assertion macro?– String/Handle/Smart Pointer class?– Hash table?– …

• Think twice before you cut-and-paste– Copied code is copied bugs

Page 76: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

76

Some highlights…

• In the Win2K code base:– Over 100 different global operator news– Over 80 different string implementations– Hundreds (thousands?) of copied functions– 9 different JPEG implementations– 3 copies of atlimpl.cpp (3 different versions)– Exactly duplicated ANSI/UNICODE files – …

Page 77: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

77

欲练神功 , 端正态度It’s all about attitude

• Take pride in your code– Want it to be perfect

• Correct, reliable, clean, simple, high-performance (speed, memory, …)

– Want it to be a “role model”

• Don’t get your ego wrapped up in it– Admit that your code’s not perfect– Actively seek out problems– Strive to improve it

Page 78: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

78

Spot the detect

Comment from PREfix source code:/* In theory it might be possible to create the data

with the right scope, but my attempts to do so have all created crashes of one sort or another. Since we only use this information to improve the clarity of our output, I'm just not creating it in this situation. */

Page 79: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

79

Spot the detect

Comment from PREfix source code:/* In theory it might be possible to create the data

with the right scope, but my attempts to do so have all created crashes of one sort or another. Since we only use this information to improve the clarity of our output, I'm just not creating it in this situation. */

i.e., “I know the right thing to do but I couldn’t make it work so I gave up.”

Page 80: 10 Things You Can Do To Write Better Code 写好代码的十个秘诀

80

Happy Coding