OK ......現在用您的評論(見下文)一起工作,我的回答將有希望更有用你,我完全用我的新答案替換它:
首先,你的PaintArray []緩衝區是一個指針數組,而不是一個PrintLines對象數組。您訪問它們的方式(使用 - >運算符)也將它們視爲指針,以便編譯。但是你永遠不會分配真正的PrintLines對象來分配給緩衝區,因此當你在調試器中調用vsprintf()時會發現NULL。
在我看來,這真的沒有需要動態分配這些PrintLines對象,所以你也可以定義PaintArray作爲打印行對象的數組,並用它做(分配,這是):
PrintLines PaintArray[MAX_PRINT_LINES]; // note this is an array of objects, not pointers
...但隨後在您訪問它們時,您需要使用點運算符而不是箭頭運算符。我不知道是否/爲什麼需要在「if(CurrentLine> = MAX_PRINT_LINES)」中將這些對象清零,並且我不確定InvalidateRect()調用會爲您做什麼。我也不確定爲什麼你不能只有一個全局的PrintLines對象,你不斷重用;當Print()被再次調用,或者你正在運行多個線程時,它不會被完成嗎?如果您正在運行多個線程,那麼我對您的「if(CurrentLine> = MAX_PRINT_LINES)」東西的正確性懷疑,除非您通過某種同步機制以某種方式調用Print()知道所有緩衝的消息已完成發送再次調用Print()的時間....其他代碼是否將CurrentLine設置爲零,或者是Print()CurrentLine變量的唯一用戶?如果Print()是它的唯一用戶,那麼(1),你可以使它成爲一個局部靜態變量而不是全局的,(2),我認爲你有一些真正的正確性問題:要麼你真的不需要這些東西的數組,否則當CurrentLine回到0時,你不應該全部清除它們。
一個結構良好的非平凡C++程序將其組件明確地與私有公共(如果它在繼承層次結構中是受保護的)接口隔離開來。這是通過類來完成的。類是與它們綁定的「方法」(函數)的數據結構。當你將你的代碼分解成小的,有凝聚力的單元,並且把你的整體項目建立爲一個可以協同工作的對象集合,而不是把你的項目建立成單一的代碼混亂的時候,事情會非常成功。從一個背景來看,這對你來說將是一個巨大的飛躍。我知道,我開始使用asm進行編程......對於Intel 8051,然後是8088,然後是Motorola 68K和PowerPC 850/860,並帶有一點Sparc,以達到良好的效果。從asm到C的步驟很少,如果你只是用C++程序風格編程,那也不是什麼大的飛躍,但是如果你希望作爲程序員能夠成爲市場,那麼你真的需要跨越到面向對象節目。現在有OO狂熱者會提倡以嚴格的OO風格進行編程,但也有很多項目將其大部分組件作爲對象實現,但其主要監督/控制代碼在程序代碼中實現,僅使用對象...這可能是一個非常好的開始,如果你能做到這一點的飛躍。
就這一點而言,我編寫了一個代碼版本,將您的緩衝區封裝在環形緩衝區類中。這假設你實際需要的只是一個看似永無止境的PrintLines對象的緩衝區(只要消費者保持不變,因此環形緩衝區不會填滿)。如果你正在嘗試學習C++,我建議你開始將有凝聚力的概念封裝成類,這些類一旦實現了&調試,就有助於減少其他代碼中與未正確使用原始數據相關的未來錯誤的可能性。下面的代碼是作爲一個結構體來實現的,所有的靜態數據都是靜態方法,這對於OO編程來說有點不尋常,但在這種情況下,你永遠不需要這些「對象」中的一個以上(實際上在這種情況下, t甚至有一個PaintBuffers「對象」,你只需要有一堆靜態數據&方法)。有些人會主張在這種情況下使用「單身」模式,但這是通過面向對象的優勢,而你並不真的需要這種模式。這讓你更接近面向對象思維,並且更容易直接從asm代碼訪問(幾乎)。我希望它對你有用。
#include <stdio.h>
#include <stdarg.h>
// ================================== This would go in a .h file.....
struct PrintLines // My own personal stand-in for whatever a "PrintLines" object is
{
int rgb;
int stringlen;
char string[400];
};
class PaintBuffers // encapsulates a circular buffer of PrintLines objects
{
public: // Public data: Anyone can have direct access to this stuff....
static const unsigned int maxPrintLines = 4; // formerly #define MAX_PRINT_LINES
private: // Private data: Only this class's methods can access this stuff....
static PrintLines PaintArray[maxPrintLines]; // note these are real objects, not pointers
static unsigned int producerIdx; // for data coming into this class
static unsigned int consumerIdx; // for data going out of this class
public: // Public methods: Anyone can call these methods....
static int numUsedBuffers() { return (producerIdx-consumerIdx) % maxPrintLines; }
// Side note, but important: The % above gives us what we want only if the
// lhs (left-hand-side) is positive. One way to ensure that is by simply
// treating the terms as unsigned; even though subtracting a larger
// number from a smaller "wraps around" to a very large number, after
// the % operation we still get what we want, so there's no need to
// compute the absolute value of a signed subtraction if we just make
// them unsigned (or cast them as unsigned) in the first place.
static int numFreeBuffers() { return maxPrintLines - numUsedBuffers(); }
// Producer calls this: Get the next 'write' buffer (to write into it)
static PrintLines* getWriteBuf()
{
if (numFreeBuffers() > 1) // The >1 implements the "always keep one slot open"
{ // solution to the full/empty ambiguity problem, thus
// there will ALWAYS be at least one unused buffer.
// There are alternative solutions that allow use of
// that one last buffer, but none which results in
// more efficient code.
PrintLines* ret = &PaintArray[producerIdx];
producerIdx = (producerIdx+1) % maxPrintLines;
// ...Note that if maxPrintLines is a power-of-2 (smart programmers only make
// circular buffers that are sized as powers-of-2), the compiler will
// automatically turn that % operation into an equivalent & for efficiency.
return ret;
}
else
{
return NULL; // Tell the caller there's no more buffer space.
}
}
// Consumer calls this: Get the next 'read' buffer (to read data from it)
static PrintLines* getReadBuf()
{
if (numUsedBuffers() > 0)
{
PrintLines* ret = &PaintArray[consumerIdx];
consumerIdx = (consumerIdx+1) % maxPrintLines;
return ret;
}
else
{
return NULL; // Tell the caller there's no data available.
}
}
};
// Because you can't (easily) call a C++ name-mangled function from assembly,
// I'll define a "C"-linkage interface to the PaintBuffers class below. Once
// your whole ASM project is ported to C++, you can blow the ASM interface away.
extern "C" int PaintBuffers_numUsedBuffers();
extern "C" int PaintBuffers_numFreeBuffers();
extern "C" PrintLines* PaintBuffers_getWriteBuf();
extern "C" PrintLines* PaintBuffers_getReadBuf();
// ================================== This would go in a .cpp file.....
// In the .h file, we declared that there are such functions (somewhere), now
// we need to actually define them....
extern "C" int PaintBuffers_numUsedBuffers() { return PaintBuffers::numUsedBuffers(); }
extern "C" int PaintBuffers_numFreeBuffers() { return PaintBuffers::numFreeBuffers(); }
extern "C" PrintLines* PaintBuffers_getWriteBuf() { return PaintBuffers::getWriteBuf(); }
extern "C" PrintLines* PaintBuffers_getReadBuf() { return PaintBuffers::getReadBuf(); }
// In the .h file, we declared that there are such variables (somewhere), now
// we need to actually define them....
PrintLines PaintBuffers::PaintArray[PaintBuffers::maxPrintLines];
unsigned int PaintBuffers::producerIdx=0;
unsigned int PaintBuffers::consumerIdx=0;
// Note that all of the PaintBuffers class's methods were defined inline in the
// class itself. You could also just declare them there (in the class definition),
// and define them here in a .cpp file.
void Print(/*HWND hWnd,*/ int rgb, const char* string, ...)
{
PrintLines* PaintObject = PaintBuffers::getWriteBuf();
if (!PaintObject) // Is it NULL?
{ // What should we do if there is no more buffer space???
return; // I guess just do nothing... Lost message.
}
// TODO: Is this needed somehow?.... InvalidateRect(hWnd, NULL, TRUE);
// MSG msg;
va_list argList;
va_start(argList, string);
PaintObject->stringlen = vsnprintf(PaintObject->string, sizeof(PaintObject->string)-1, string, argList);
va_end (argList);
PaintObject->rgb = rgb;
// msg.hwnd = hWnd;
// msg.message = WM_PAINT;
// DispatchMessage(&msg);
}
void Consume() // ...my stand-in for whatever your consumer is (still in ASM?)
{
PrintLines* PaintObject = PaintBuffers::getReadBuf();
if (PaintObject) // Was it non-NULL?
{
printf("Consume(): Got \"%s\"\n", PaintObject->string);
}
else // This is only here to show that we did get NULL.....
{
printf("Consume(): Got NULL! (no buffers with data in them)\n");
}
}
int main()
{
Consume();
Consume();
Print(0x11111111, "The %dst message.", 1);
Print(0x11111111, "The %dnd message.", 2);
Consume();
Consume();
Print(0x11111111, "The %drd message.", 3);
Print(0x11111111, "The %dth message.", 4);
Consume();
Print(0x11111111, "The %dth message.", 5);
Consume();
Consume();
Consume();
Consume();
Consume();
Print(0x11111111, "The %dth message.", 6);
Print(0x11111111, "The %dth message.", 7);
Print(0x11111111, "The %dth message.", 8);
Print(0x11111111, "The %dth message.", 9); // ...will be lost (no more buffer space)
Print(0x11111111, "The %dth message.", 10); // ...will be lost (no more buffer space)
Print(0x11111111, "The %dth message.", 11); // ...will be lost (no more buffer space)
Consume();
Consume();
Consume();
Consume();
Consume();
Consume();
Consume();
}
這是正確的工作功能,或只是在您的問題中的代碼的「編輯」?如果這是正確的工作功能,那麼你應該解釋問題是什麼,也許接受你自己的答案。如果只是編輯問題中的代碼,則應編輯您的問題,並刪除此「答案」。 – 2012-07-20 06:34:14
@JoachimPileborg - 這是他的問題中的代碼編輯。是的,他應該編輯他的問題並替換代碼。 – phonetagger 2012-07-20 17:58:37