自由行

2005-03-07

养成好的编码习惯—-由一个bug想到的

Filed under: 程序员 — thomas @ 10:45

最近在做PCM音频合成项目,其中遇到了一个bug,花了大半天才解决,当然修改却很小,是一个很细节的问题。不过回想一下,之前也有类似的经历,觉得有点什么规律,写出来用以自勉,也许各位programmer也曾遇到过吧!:)

下面是部分摘录的代码(颜色visual assist生成的)

/**/

HRESULT CALMInputPin::Convert(IMediaSample *pIn, IMediaSample *pOut)

{

HRESULT hr = S_OK;

/* get suggested size of destination buffer */

long len = pIn->GetActualDataLength();

DWORD suggested_dest_size = 0;

hr = acmStreamSize(m_hStream, len, &suggested_dest_size, ACM_STREAMSIZEF_DESTINATION);

if (hr != MMSYSERR_NOERROR)

return hr;

ACMSTREAMHEADER acmhdr;

acmhdr.cbStruct = sizeof(acmhdr);

hr = pIn->GetPointer(&acmhdr.pbSrc);

hr = pOut->GetPointer(&acmhdr.pbDst);

acmhdr.cbSrcLength = pIn->GetActualDataLength();

acmhdr.cbDstLength = suggested_dest_size;

acmhdr.dwSrcUser = (DWORD)pIn;

acmhdr.dwDstUser = (DWORD)pOut;

acmhdr.dwUser = (DWORD)this;

acmhdr.fdwStatus = 0L;

// ???这里总是返回 error code 10

hr = acmStreamPrepareHeader(m_hStream, &acmhdr, 0);

if (hr != MMSYSERR_NOERROR)

return hr;

/* send buffer to compressor/uncompressor (ACM) */

hr = acmStreamConvert (m_hStream, &acmhdr, ACM_STREAMCONVERTF_BLOCKALIGN);

if (hr != MMSYSERR_NOERROR )

{

return hr;

}

hr = acmStreamUnprepareHeader(m_hStream, &acmhdr, 0);

if (hr != MMSYSERR_NOERROR)

return hr;

REFERENCE_TIME rtStart, rtEnd;

len = pIn->GetActualDataLength();

pIn->GetTime(&rtStart, &rtEnd);

rtEnd = rtStart + len;

pOut->SetTime(&rtStart, &rtEnd);

pOut->SetActualDataLength(acmhdr.cbDstLength);

return S_OK;

}

//

这里涉及到DirectShowACM的结构和函数,不过思路还比较清晰。细心的话可以看出这些代码,是“拼凑”出来的(提高开发速度嘛!),分别来自是网上的开源项目和DirectShow附带的源文件。从逻辑上说也没有什么问题,当然调试发现总是有一个错误。

唯一的线索是error code 10Error Lookup解释为“环境错误”,这能说明什么问题呢?当然笔者对ACM也不是很了解,于是花了几个小时看了文档,了解了一些背景知识,但没有什么发现什么有价值的东西。又参考了其它项目的代码,不过别人使用的c++模板语法,而且要拆解出关键的代码需要很多时间,所以搞来搞去也没有思路。

思路在哪里?在经过了一些其它的尝试的时候,觉得还是不行。最后又去调试别人的代码,可以运行通过。有一点是可以肯定的,程序的流程没有问题,可能是其中遗漏了某些细节。对比两段代码后发现,ACMSTREAMHEADER的某些成员变量没有初始化。难道真是这个问题吗?于是在结构体变量申明后面添加了

memset (&acmhdr, 0, sizeof(acmhdr));

这样所有的成员先初始化为0。在调试,问题解决了!后来在MSDN里面发现了ACMAPP Sample工程,里面有这么一段代码:

pash->cbStruct = sizeof(*pash);

pash->fdwStatus = 0L;

pash->dwUser = 0L;

pash->pbSrc = paacd->pbSrc;

pash->cbSrcLength = paacd->cbSrcReadSize;

pash->cbSrcLengthUsed = 0L;

pash->dwSrcUser = paacd->cbSrcReadSize;

pash->pbDst = paacd->pbDst;

pash->cbDstLength = paacd->cbDstBufSize;

pash->cbDstLengthUsed = 0L;

pash->dwDstUser = paacd->cbDstBufSize;

这个结构体的定义如下:

typedef struct tACMSTREAMHEADER

{

DWORD cbStruct; // sizeof(ACMSTREAMHEADER)

DWORD fdwStatus; // ACMSTREAMHEADER_STATUSF_*

DWORD dwUser; // user instance data for hdr

LPBYTE pbSrc;

DWORD cbSrcLength;

DWORD cbSrcLengthUsed;

DWORD dwSrcUser; // user instance data for src

LPBYTE pbDst;

DWORD cbDstLength;

DWORD cbDstLengthUsed;

DWORD dwDstUser; // user instance data for dst

DWORD dwReservedDriver[10]; // driver reserved work space

} ACMSTREAMHEADER, *PACMSTREAMHEADER, FAR *LPACMSTREAMHEADER;

11个成员虽然没有全部清零,但是除了dwReservedDriver[10]都赋了值。

总结:这类错误是由于某些结构体成员没有正确的赋值而造成的,在Debug模式中没有赋值的变量一般初始化为0xcc,在Release模式中一般是0×00,都是按字节序。所以编译器为了做了赋值,但是这些值是错误的,所以在编码的过程中必须处理这个细节。要么先全部初始化为0,要么对所有相关的变量都赋值。

笔者写程序也有3年了,但是仍然犯这个错误,虽然是在追的比较急的时候。所以必须在平时养成良好的习惯,同时克服外部的压力,写出高质量的代码;而不是风风火火赶代码,最后还得花很多时间去找错,得不偿失!这就是笔者要与大家共勉的啊!

Powered by WordPress