今天寫程序的時候,創建了一個結構體:
struct BufferObj { char* buf; int bufLen; SOCKADDR_STORAGE addr; int addrLen; struct BufferObj* next; };
該結構體有一個next指針,本意是這個指針初始的時候應該為NULL,如果有新的BufferObj添加,這個next指針就會指向這個新添加的BufferObj,其實就是一個鏈表。
程序中有一個生產者線程使用enqueueBufferObj方法向鏈表裡面添加BufferObj:
void enqueueBufferObj(ConnectionObj* connObj, BufferObj* bufferObj) { EnterCriticalSection(&connObj->sendRecvQueueCriticalSection); if (connObj->pendingSendHead == NULL) { connObj->pendingSendHead = connObj->pendingSendTail = bufferObj; } else { connObj->pendingSendTail->next = bufferObj; connObj->pendingSendTail = bufferObj; } LeaveCriticalSection(&connObj->sendRecvQueueCriticalSection); }
還有一個消費者線程從這個鏈表裡面取BufferObj:
BufferObj* dequeueBufferObj(ConnectionObj* connObj) { BufferObj* res = NULL; EnterCriticalSection(&connObj->sendRecvQueueCriticalSection); if (connObj->pendingSendTail != NULL) { res = connObj->pendingSendHead; connObj->pendingSendHead = connObj->pendingSendHead->next; if (connObj->pendingSendTail == res) { connObj->pendingSendTail = NULL; } } LeaveCriticalSection(&connObj->sendRecvQueueCriticalSection); return res; }
其中,ConnectionObj結構體如下:
struct ConnectionObj { SOCKET s; HANDLE hRecvSemaphore; struct BufferObj* pendingSendHead; struct BufferObj* pendingSendTail; CRITICAL_SECTION sendRecvQueueCriticalSection; };
剛開始的時候,由於程序中沒有顯示的將BufferObj的next屬性初始化NULL,導致程序運行到enqueueBufferObj方法時總是出現指針違法訪問:
connObj->pendingSendTail->next = bufferObj;
上面就是出錯的地方。程序中對於ConnectionObj中的pendingSendHead和pendingSendTail都已經顯示初始化為NULL了。經過查找發現,是因為程序中沒有顯式對
BufferObj中next進行NULL初始化,從而當消費者線程從隊列中BufferObj之後,會重新對隊列頭進行初始化,該代碼在dequeueBufferObj中:
connObj->pendingSendHead = connObj->pendingSendHead->next;
此時,如果BufferObj中的next顯示初始化為了NULL,那麼connObj->pendingSendHead的值應該為NULL,但是程序中沒有對next進行顯式初始化,所以,此時,
connObj->pendingSendHead的值為一個隨機值,這導致生產者線程使用enqueueBufferObj在向隊列中添加新BufferObj時出錯:
if (connObj->pendingSendHead == NULL) {//如果next顯式初始化了,這個條件檢測應該成立 connObj->pendingSendHead = connObj->pendingSendTail = bufferObj; } else {//但是由於next沒有顯示初始化,導致pendingSendHead的值不是NULL,而是一個隨機的,因此程序錯誤的運行到這裡,出現上述錯誤 connObj->pendingSendTail->next = bufferObj; connObj->pendingSendTail = bufferObj; }
在簡單的程序中,這中錯誤查找起來可能不是問題,但是如果程序很復雜,查找這種錯誤就會很浪費時間。因此,為了安全起見,以後對於C++中的結構體,類成員,在使用前,還是先進行初始化後為好。
1.
output函數中的for循環後面的分號去掉就行了
for(i=0; i<b; ++i);改成for(i=0; i<b; ++i)
2.結構體的初始化可以用memset,頭文件string.h
memset(jsq,0,a * sizeof( struct counter));
如果數據是private或protected,不能直接初始化,只能再寫單獨的函數來賦值。
你接著往後看吧,構造函數又好幾種的
很多用處的,舉幾個例子
1.處理const數據成員時,單獨寫函數是行不通的,只能用構造函數來初始化
2.構造函數的初始化列表是早於普通函數的
3.new表達式分配內存的同時還調用構造函數初始化,而普通函數做不到的