Thursday, July 4, 2013

Writing solid code


  • Enable all optional compiler warnings.
  • Lint.
  • Maintain both ship and debug versions of your program.
  • Use assertions to validate function arguments.

#ifdef DEBUG
    void _Assert(char*, unsigned); /*prototype*/
    #define ASSERT(f)\
      if(f)          \
        {}           \
      else           \
        _Assert(__FILE__, __LINE__)

#else
    #define ASSERT(f)
#endif

void _Assert(char *strFile, unsigned uLine)
{
  fflush(NULL);
  fprintf(stderr, "\nAssertion failed: %s, line %u\n", 
           strFile, uLine);
  fflush(stderr);
  abort();
}



  • Strip undefined behavior from your code, or user assertions to catch illegal users of undefined behavior.
  • Don't waste people's time. Document unclear assertions.
/* Blocks overlap? Use memmove. */
ASSERT(pbTo>=pbFrom+size || pbFrom >= pbTo+size);
  • Either remove implicit assumptions, or assert that they are valid.
  • Use Assertions to detect impossible conditions.
  • Don't hide bugs when you program defensively.
  • Use a second algorithm to validate your results.
implement an ASSERT macro that the file name string(__FILE__) would be defined just once per file:
bury the details in a new ASSERTFILE macro that you use once at the start of each source file


  #ifdef DEBUG
    #define ASSERTFILE(str)    \
              static char strAssertFile[] = str;

    #define ASSERT(f)          \
              if(f)            \
                {}             \
              else             \
                _Assert(strAssertFile, __LINE__)
  #else
    #define ASSERTFILE(str)
    #define ASSERT(f)
  #endif
  • Eliminate random behavior. Force bugs to be reproducible.
#define bGarbage 0xA3
flag fNewMemory(void **ppv, size_t size)
{
  byte **ppb=(byte**)ppv;
  ASSERT(ppv!=NULL && size!=0);
  *ppb=(byte*)malloc(size);
  #ifdef DEBUG
  {
     if(*ppb != NULL)
       memset(*ppb, bGarbage, size);
  }
  #endif
  return (*ppb != NULL);
}
  • Destroy your garbage so that it's not misused.
  • If something happens rarely, force it to happen often.
Once you release memory, you don't own it, so you shouldn't touch it.
(some memory managers use free memory to store free-chain information, or other internal implementation data) Programmers either don't know, or regularly forget, that realloc can move blocks. Detecting this problem is important. =>
Stimulate what realloc does:
If the block is shrinking, pre-fill the soon-to-be released memory. If the block is expanding, force it to move(instead of expanding in place) by faking a realloc. If the block is the same size, don't to anything.

Remember that debug code is extra code, not different code. Unless there is a compelling reason not to, you should always execute the ship code, even if it's redundant.
  • Keep debug information to allow stronger error checking.
  • Design your test carefully, nothing should be arbitrary.
Choosing a value for bGarbage, recursed over the symbol table's tree structure with a pre-order traversal. Some choices are better than others.
  •  Setting "undefined" memory to a constant garbage value is one example of removing random behavior.
  • Make sure your tests work even for programmers who are unaware of them. The best tests are those that require no knowledge of their existence.
Programmers occasionally write coe that fills past the end of an allocated memory block. How you could enhance the memory subsystem checks to alert you to these types of bugs.

Allocate 1 extra byte for every block that you allocate: 檢查DebugByte有沒有被覆寫過去

#define dDebugByte 0xE1

#ifdef DEBUG
  #define sizeofDebugByte 1
#else
  #define sizeofDebugByte 0
#endif

flag fNewMemory(void **ppv, size_t size)
{
  byte **ppb = (byte**)ppv;
  ASSERT(ppv!=NULL && size !=0);
  *ppb = (byte*) malloc(size+sizeofDebugByte);

  #ifdef DEBUG
  {
    if(*ppb!=NULL)
    {
      *(*ppb+size)=dDebugByte;
      memset(*ppb, bGarbage, size);
      .
      .
      .


flag fResizeMemory(void **ppv, size_t sizeNew)
{
  byte **ppb = (**byte) ppv;
  byte *pbNew;
  .
  .
  .
  pbNew = (byte*)realloc(*ppb, sizeNew+sizeofDebugByte);
  if(pbNew!=NULL)
  {
    #ifdef DEBUG
    {
      *(pbNew+sizeNew)=dDebugByte;
      .
      .
      .


/* Verify that nothing wrote off end of block. */
ASSERT(*(pbi->pb + pbi->size) == dDebugByte);

how could you give testers the ability to fake out-of-memory conditions?
Normally by using a tool to gobble memory until the application's memory requests begin to fails.
It works, but not very precise --- it cause some allocation request somewhere in the program fail.
A better technique is to build an out-of-memory simulator directly into the memory manager.
在newMemory時,或在resizeMemory時,add an if statement

#ifdef DEBUG
  if(fFakeFailure(&fiMemory))
  {
    *ppb=NULL;
    return (FALSE);
  }
#endif

With these changes, the failure mechanism is in place. To make it work, you would call the SetFailure routine to initialize the failureinfo struct:

/* tell the failure system that you want to call the system five times before getting seven consecutive failures. */
SetFailures(&fiMemory, 5, 7); 

/* tow common calls to SetFailures: Don't fake any failures. Always fake failures */
SetFailures(&fiMemory, UNIT_MAX, 0);
SetFailures(&fiMemory, 0, UNIT_MAX);


The code below implements the four functions that make up the failure mechanism.

typedef struct
{
  unsigned nSucceed; /* # of calls before failing */
  unsigned nFail;    /* # of times to fail        */
  unsigned nTries;   /* # of times already called */
  int lock;          /* if > 0, disable mechanism.*/
} failureinfo;

void SetFailures(failureinfo *pfi, unsigned nSucceed, 
                  unsigned nFail)
{
  /* If nFail is 0, require that nSucceed be UNIT_MAX. */
  ASSERT(nFail != 0 || nSucceed == UINT_MAX);

  pfi->nSucceed = nSucceed;
  pfi->nFail    = nFail;
  pfi->nTries   = 0;
  pfi->lock     = 0;
}

void EnableFailures(failuresinfo *pfi)
{
  ASSERT(pfi->lock > 0);
  pfi->lock--;
}

void DisableFailures(failuresinfo *pfi)
{
  ASSERT(pfi->lock >=0 && pfi->lock < INT_MAX);
  pfi->lock++;
}

flag fFakeFailure(failureinfo *pfi)
{
  ASSERT(pfi != NULL);
  if(pfi->lock > 0)
    return (FALSE);

  /* Pin nTries at UINT_MAX. */
  if(pfi->nTries != UINT_MAX)
    pfi->nTries++;

  if(pfi->nTries <= pfi->nSucceed)
    return (FALSE);

  if(pfi->nTries - pfi->nSucceed <= pfi->nFail)
    return (TURE);

  return (FALSE);
}

  • Don't wait until you have a bug to step through your code
  • Step through every path (more than one code path: if and switch statements, &&, || and ?: operators)
  • As you step through code, focus on data flow ( Overflow and underflow bugs, Data conversion bugs, Off-by-one bugs, NULL pointer bugs, Bugs using garbage memory(oxA3 bugs), Assignment bugs in which you've use = instead of ==, Precedence bugs, Logic bugs, )
  • Source leve debuggers can hide execution details. Step through critical code at the instruction level.
  • Bugs don't grow in code spontaneously; they are the result of a programmer's writing new code or changing existing code. If you want to find bugs in your code, there is no better method than stepping through eery line of the code the moment it's compiled.
  • The problem with black-box testing is that you can't tell what goes on between stuffing in the inputs and receiving the outputs. The best way to catch bugs is to look for them the moment you write or change code. I don't know how many programmers who consistently write bug-free code, but the few I do know habitually step through all of their code

  • Make it hard to ignore error conditions. Don't bury error code in return values.
  • Always look for, and eliminate, flaws in your interfaces.
  • Don't write multipurpose functions. Write separate functions to allow stronger argument validation. 
  • Don't be wishy-washy. Define explicit function arguments.
getchar returns int
realloc return null (Ideally, realloc would always return an error code and a pointer to the memory block  regardless of whether the block was expanded.)

/* the if statement in the code ensures that the original pointer is never destroyed. */

flag fResizeMemory(void *ppv, size_t sizeNew)
{
  byte **ppb = (byte**) ppv;
  byte *pbNew;
  
  pbNew = (byte*) realloc(*ppb, sizeNew);
  if(pbNew!=NULL)
    *ppb=pbNew;
  return (pbNew!=NULL);
}



  • Write functions that, given valid inputs, cannot fail.
Implement standard tolower function to "return the lowercase equivalent of ch if one exist; otherwise, return the character unchanged" If you find that you can't eliminate error condition, consider disallowing the problematic cases altogether. use an assertion to verify argument.


char tolower(char ch)
{
  ASSERT(ch>='A' && ch<='Z');
  return (ch+'a'-'A');
}



  • Make the code intelligible at the point of call. Avoid boolean arguments.
UnsignedToStr(u, str, TRUE);
UnsignedToStr(u, str, FALSE);
=>
#define BASE10 1
#define BASE16 0
UnsignedToStr(u, str, BASE10);
UnsignedToStr(u, str, BASE16);
=>
void UnsignedToStr(unsigned u, char *str, unsigned base);

  • Write comments that emphasize potential hazards.

/* getchar -- equivalent to getc(stdin).
 *
 * getchar returns the next character from stdin. When
 * an error occurs. it returns the *int* EOF. A typical 
 * use is
 * 
 *   int ch;  //ch *must* be an int in order to hold EOF.
 * 
 *   if ((ch=getchar())!=EOF)
 *     //success
 *   else
 *     //failure
 */

int getchar*void)
...

/* realloc(pv, size)
 * ...
 * 
 * A typical use is
 * 
 *   void *pvNew;  //used to protect pv if realloc fails
 *   pvNew = realloc(pv, sizeNew);
 *   if(pvNew!=NULL)
 *   {
 *      //success --update pv
 *      pv=pvNew;
 *   }
 *   else
 *      //failure -- don't destroy pv with the NULL pvNew
 */

void *realloc(void *pv, size_t size)
...

why is the ANSI strncpy function bound to trip up the unwary programmer?
Sometimes strncpy terminates the dest str with a nul character, and sometimes it doesn't.

C++'s inline function specifier is valuable because it allows you to define functions that are as efficient as macros yet don't have the troublesome side effects that macro "functions" have in evaluating their parameters.

The serious problem with C++'s new & reference arguments is that such arguments hide the fact that you are passing the variable by reference , not by value, and that can cause confusion. For example, suppose ou redefine the fResizeMemory function so that it uses a reference argument. Programmers could then write
if( fResizeMemory(pb, sizeNew))
   resize was successful
But notice, programmers unfamiliar with the function would have no reason to believe that pb might be changed during the call. How do you think that will affect program maintenance?
A related concern is that C programmers often manipulate the formal arguments to their functions because they know those arguments are passed by value, not reference  But consider the maintenance programmer who fixes a bug in a function he didn't write. If that programmer fails to notice the & in the declaration  he could modify the argument without realizing that the change won't be local to hide function . & reference arguments are risky because they hid an importnat implementation detail. 

Monday, February 11, 2013

quote and reading

“Obstacles are those frightful things you see when you take your eyes off your goal.” - Henry Ford

“Great spirits have always encountered violent opposition from mediocre minds.” - Albert Einstein

“Knowing is not enough; we must apply. Willing is not enough; we must do.” - Goethe

“It's kind of fun to do the impossible.” - Walt Disney

“Live the life you've dreamed” - Henry David Thoreau

Look for passion. Smart people are passionate about the projects they work on. They get very excited talking about the subject. They talk quickly, and get animated

從一月十四開學到現在也快一個月了,從台灣剛來的時候雖然累但是上課還算得上專注,現在不到一個月已經有漸漸習慣的跡象,不好,剛來的目標跟憧憬漸漸地被掩蓋,一定要更專注於目標,是身邊的人讓我這樣感覺,還是環境,這條路還是一樣未知...

http://www.troyhunt.com/2013/02/the-ghost-who-codes-how-anonymity-is.html
http://www.joelonsoftware.com/
[REF:http://dudye.com/get-in-to-action-77-thoughts-on-motivation/]

George Polya's quote
"To be a good mathematician, or a good gambler, or good at anything, you must be a good guesser"


The simplest solution is to act


watch your thought, for they become words
watch your words, for they become actions
watch your actions, for they become habits
watch your habits, for they become character.
watch your character, for they become destiny


When you get into a tight place and everything goes against you, till it seems as though you could not hang on a minute longer, never give up then, for that is just the place and time that the tide will turn.

~Harriet Beecher Stowe

Saturday, January 12, 2013

大學生知道了沒

你學到了什麼,比你拿幾分更重要

何謂成功 何謂失敗

Thought on success: http://www.thecrimson.com/article/2009/2/23/thoughts-on-success-students-can-be/
Students can be very tough on themselves. It concerns me that some carry a very narrow definition of success and others see asking for help as a sign of weakness. Years ago, freelance writer Linda Weltner shared the following story attributed to Norris Lee in The Boston Globe: 

A lecturer stands in the front of a class of overachievers, holding a 1-gallon wide-mouth mason jar. He fills it with good-size rocks, then asks the group, “Is this jar full?” The audience agrees that it is. 

“Oh, really?” he asks. He pours in several handfuls of gravel. “Is the jar full now?” The audience is doubtful. Probably not. He pours a handful of sand into the jar, filling it to the brim. “How about now?” he asks. Not yet. He pours in a pitcher of water. 

“OK,” he asks. “What’s the lesson to be learned here?” 

“Well,” says a man sitting near the front, “you’ve just demonstrated that no matter how full your schedule is, you can always fit more in.” 

“You’re absolutely wrong,” says the teacher. “The point I’m making is that if you don’t put the rocks in first, you’ll never get them all in.” 

This story says a lot about success. Harvard educators have more than education as a job; they must help students identify what the “rocks” in their world are at any point. By “rocks,” I mean the things a student wants to place high priority on when assessing progress made or distance still to go. Students want to be successful, but too often they see success in a limited way (for instance, a near flawless transcript) or they don’t really know what they are working toward. 

In high school, it was simpler. Students pushed to assemble records that would get them into top colleges. With tougher competition in college, it becomes more difficult to achieve the near flawless transcript. And, with many more post-graduate options, it is difficult to feel a clear sense of direction. 

In the past, when I have asked advisees what they want to be sure gets into the jar, or what they will use as a marker of success, they have thought hard. Some responses come to mind: I want to feel like I fit in personally and have one or two really good friends; I want to make strong progress in identifying a concentration; I want to interview my grandmother, whose health is failing, in order to begin to write a memoir. These responses have given the students a personal compass and a chance to look back at the end of the year with—more often than not—a sense of accomplishment and self-worth. That awareness has mattered hugely. 

My second concern is about the act of asking for help. Clarissa Pinkola Estes, a poet and psychoanalyst, made the following statement: “Asking the proper questions is the central action of transformation. Questions are the key that causes the secret doors of the psyche to swing open.” I know that it can be hard to ask questions or to acknowledge that you don’t have the answers that appear critical to move forward. But, it is important to remember that this is a complicated place and this is a complicated period of one’s development. We think more—not less—of students who ask questions and who take advantage of the myriad resources and support services here. Doing so suggests a kind of agency or taking charge that is both appropriate and applaudable and that contributes significantly to the achievement of success.


行使你的教育自主權:

從接受的角色轉換為主張的角色
如果你能用熱忱與殷切的好奇心去學習,自然能完成那些達到好成績之前,所必須要的苦差事
善用教授們訂出的輔導時間
與教員們密切聯繫 : 在哪些課堂上較可能與教授建立私交?可以主動挑選這種課
在教室以外的地方開啟與faculty的學術對話/非學術對話
握有學習的主控權,進行策略性的思考,展現你的自制力,小心審慎哪些事重點,該在何時完成
懂得方法的學生不會試著把所有東西都塞進腦子裡,以為這樣就可以好好吸收,而是選擇特定的學習方式,屆此管理自己所吸收的資訊。在念書時,為什麼我要念這些東西?我從中應該學到什麼?他們能幫我回答什麼問題?透過我的學習與練習將來我有辦法解決什麼難題?

邁向成功的三個關鍵:
設定個人目標
培養學習策略
做計畫(找出阻礙你的原因,並且把它排除掉)

時間管理並不只是跟避免拖拖拉拉有關,他的經隨是,你必須主動管理,你怎樣使用時間這種最珍貴的資源。分析你是怎麼消耗掉一整天的。估算一下自己在一週之內做了哪些事。打開一個你自製的電子表格,在左邊欄位列出以半小時為單位的時間,頂端列出七欄代表七天。寫下你覺得在哪個時間點應該要做的事。把每件事花了多少時間都寫下來上課練習吃飯與同學碰面上網逛臉書睡覺。不要有所遺漏,包括運動玩樂看電視,在這些活動之間,你必須插入25~35小時的空檔溫習功課。
第二步驟是精確記入實際做的事情,當然不會百分百一樣誰都有發呆分心的時候,人生也有許多無法預期與計畫之外的事。重點是看出你與理想的計畫相差多遠。

提早一點起床,在上課前念書或溫習筆記,如果課堂間有一小時空檔,他們會坐下來念書而不是喝咖啡,聊天發呆。我認識的一些最有成就的學生,他們把白天時間當成在上班。這種觀念跟校園文化相反,大部分學生都是晚上七點才開始念書,然後一直念過午夜,如果你平均一天上三小時的課,那麼你等於是把八小時的白天時間浪費在學業以外的事情上面。你可以先把工作做完,然後好好享受剩餘的時光。

問你的授課老師,他們對於修課學生會提出哪些學習建議,請教那些跟你修同一門課但是表現較好的同學,問他們是怎樣吸收教材的。

把問題寫下,期中考試題也袋了,圈出他還不懂的地方,他先想好要做些什麼,把問題以及他不懂的一些關連列出來,大衛讓教授可以把注意力擺在一組特定的問題上,事後他可以獲得更多了解教材的洞見,