找回密码

碧海潮声大学生网

查看: 1578|回复: 5
打印 上一主题 下一主题

〖转帖〗C++技术区最新随笔

[复制链接]
跳转到指定楼层
1#
发表于 2006-10-26 10:06 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
C++的学习不仅要严肃要勤奋,之后还要一点放松自己的幽默细胞:


下面摘一些C++知识篇章给大家阅读,觉得好的不防送些鲜花的哦.另外要说的是小雨小不

在了哦,负责这个版块似乎会有一些压力哦,希望喜欢看书的朋友多来顶顶啊,有什么要求

的随时可以发短信给我的哦~



从笑话中悟出C++开发管理之"道"

1. 程序员写出自认为没有Bug的代码。

2. 软件测试,发现了20个Bug。

3. 程序员修改了10个Bug,并告诉测试组另外10个不是Bug。

4. 测试组发现其中5个改动根本无法工作,同时又发现了15个新Bug。

5. 重复3次步骤3和步骤4。

6. 鉴于市场方面的压力,为了配合当初制定的过分乐观的发布时间表,产品终于上市了。

7. 用户发现了137个新Bug。

8. 已经领了项目奖金的程序员不知跑到哪里去了。

9. 新组建的项目组修正了差不多全部137个Bug,但又发现了456个新Bug。

10. 最初那个程序员从斐济给饱受拖欠工资之苦的测试组寄来了一张明信片。整个测试组集体辞职.

11. 公司被竞争对手恶意收购。收购时,软件的最终版本包含783个Bug。

12. 新CEO走马上任。公司雇了一名新程序员重写该软件。

13. 程序员写出自认为没有Bug的代码。

  要我说,如果真有这样的公司,不倒闭对不起人民。

 这个笑话从程序员开始,到程序员结束,从头到尾都在说程序员的不是。但是我要说的是,这完全是管理者的失败,从整个过程中,看不到任何管理工作。这种管理者不但无知无能,还很无耻——将自己的失败责任推给程序员。

 1、程序员凭什么证明他的代码没有BUG?有Test case吗?有Code review吗?这个环节管理缺失。

 2、测试发现BUG有进行BUG管理吗?有跟踪吗?这个环节管理缺失。
 3、凭什么证明程序员已经把那10个BUG修改好了?另10个又为什么不是BUG?BUG的评价标准难道是程序员说了算?这个环节管理缺失。

 4、5个不能工作的BUG修改问题有没有追究责任?增加新BUG是修改过程中不可避免的事情,但是如果有有效的单元测试机制,可以大大减少这种情况。这个环节管理缺失。

 5、迭代是正常的,但是问题处理于发散而不是收敛发展,可见没有有效的管理调控。这个环节管理缺失。

 6、过于乐观的时间表和不可能达到的最后期限,都表现出管理者的无知和无能。而在这样的情况下强行推出产品,那就是无知者无畏了。

 7、这是对用户的不负责任,管理者要负最大的责任。

 8、这样的情况还能发项目奖金,只能说管理者不是一般的愚蠢。

 9、管理工作没有任何的改进,问题仍然处于发散迭代状态。管理工作依然没有到位。

 10、拖欠测试部门工资体现出管理者对质量管理工作的忽视以及对人力资源管理方面一无所知。

 11、送被收购者两个字:活该。送收购者两个字:瞎眼。

 12、可见新管理者与原管理者半斤八两,都没有认识到问题的根本所在。不过也只有这样的管理者才会作出收购这种公司的决策。

 13、历史的重演是必然的。

 一个正常的企业或是项目,其运作必须应该是循环向上进行的。而保障这种运行的工作就是管理。而管理工作的主要内容就是控制,包括控制循环的节奏——不能太快也不能太慢,控制发展的方向——只能向上不能向下,控制运作的稳定——不能大起大落或时聚时散等。
 而这一切,在这个例子中都看不到。

 在这个笑话的例子中,一切都是以开发工作在驱动,这首先就是一个方向性错误,产品是为用户服务的,当然应该是以用户和市场作为驱动,并且结合自身的能力最终 确定工作的重点。这一错误折射出管理者对被管理的内容很不了解,只好任由比较了解的程序员摆布——事实上他们除了技术,并不会了解更多。

 一个管理者如果对自己所管理的内容不了解,他就不可能管理得好。

 这是一件毫无疑问的事,可是国内的软件业似乎总是不相信这一点。中国软件业中流毒最深的谎言之一就是:

 管理者只要懂管理就可以,不需要懂技术。

其实这不过是那些无知无能无耻的管理者为了骗钱而编出来的,相信这句话的人必将付出金钱的代价。

 其次是质量管理。基本的质量管理常识告诉我们,每次循环结束前,最重的工作就是总结改进。只有这样才能保证循环运作是向上发展,而不是失去控制地向下发展。 也只有有效的质量管理,才能保证迭代过程是收敛发展,并最终达到目标。但在这个例子中,这个部分显然是缺失的——其中虽然有测试部门,但是他们的作用仅仅 是质量管理中的质量检测环节,管理部分还是缺失的。

 然后是人力资源管理。软件开发是一项劳动密集型的工作,虽然这是脑力劳动,但同样意味着人在因素在其中占有决定性的地位。而例子中未改完BUG的程 序员拿到项目奖金,而同样辛苦工作的测试人员却被拖欠薪资,除了表现出管理者对他们的工作内容的不了解,以及对质量管理工作的不重视以外,还表现出管理者 完全不会管人,这是一种谋杀团队的行为——谋杀一个团队远比建设要容易得多。

 最后,这个失败的管理者把他的经历编成这个笑话,让大家看到他被程序员们害得多惨,把程序员妖魔化为一群骗子。但只要稍懂管理的人简单分析一下就可以看出来,只不过是这个人的无知和无能造成了他现在的结果,而把责任推给别人的行为更是表现出他的无耻。

 作为身居高位的管理者,如果连应该承担的责任都要推卸,他们还能胜任什么事情呢?

2006-10
分享到:  QQ好友和群QQ好友和群 QQ空间QQ空间 腾讯微博腾讯微博 腾讯朋友腾讯朋友
收藏收藏 分享分享 顶 踩
2#
 楼主| 发表于 2006-10-26 10:08 | 只看该作者
下面是个求助的帖子的转贴,有兴趣的朋友也可以考虑一下是怎么回事的哦?

CWnd* pWnd;
CRect rectWMP;
pWnd = GetDlgItem(IDC_STATIC);  
pWnd->GetWindowRect(&rectWMP);
CWMPPlayer4* player = new  CWMPPlayer4;
player->Create("",WS_CHILD|WS_VISIBLE,rectWMP,pWnd,IDC_OCX1);

player->put_URL(_T("E:\\CIMG2137.AVI"));

CWMPControls* control = (CWMPControls*)player->get_controls();
  control->Pause();

执行到最后一行的时候就挂了,我检查了一下control不为空,查了很多资料也没对这个问题进行过阐述,各位大虾有什么好主意没有。
注:player的各个函数的使用都是正确的,但是从player里面获取的其他接口的实例使用就有问题了。

2006-10-25 01:25 作者
3#
 楼主| 发表于 2006-10-26 10:09 | 只看该作者
如何编译boost
关键字:boost 编译 安装

boost编译。没啥新东西,老生常谈。

先给出英文的编译帮助,有什么不明白的或者我没说清楚的请查阅并确认。
www.boost.org/more/getting_started.html
或者你的boost的安装路径下的more/getting_started.html

这里以1.33.1为例。

1.下载boost包,并解压到某个文件夹下。这里用为方面起见$boost_dir代替,在说明路径的地方如果出现了$boost_dir请用实际的boost的解压路径替代。
注:如果你下载了boost的一些增补包,这些包一般是在boost从上一个大版本到新大版本之前被收录的一些新的库或者是新的编译工具,例如新的bjam。请不要以为是重名而把同名文件夹覆盖了。看准合适位置解压就是了。
2.打开命令行工具。以下的主要工作都将在命令行中进行。

3.首先是编译jam工具。
3.1 使用命令行
  SET PATH=%PATH%;$boost_dir\tools\build\jam_src\;
设置环境变量。这一步也可以在“我的电脑点右键->属性->高级->环境变量->user variable或system variable中"设置,而且是永久性的。使用set设置的环境变量只对当前命令行有效。
3.2  运行build.bat。在命令行中查看结果。如果结果显示“update 1 targets successful"这样的信息,则表明编译成功。此时“$boost_dir\tools\build\jam_src\”文件夹可发现一个新的文件夹btn.x86,在里面可以发现bjam.exe。(不知道会不会有btn.x64文件夹。。。我是X86的机器,不太清楚)
  将这个文件夹也添加到环境变量中。
  SET PATH=%PATH%;$boost_dir\tools\build\jam_src\btn.x86;
3.3 如果没能成功编译bjam,则可能是编译器的设置问题。对于Visual C++(2005Express有点特殊)编译器,找到common7\tools\vcvars32.bat,把它拖到命令行窗口中,运行一下,然后再执行build.bat。对于2005 Express,这个编译器携带的是精简的编译环境,你可以同样找到vcvars.txt,然后更名为vcvars.bat,按照刚才的方式执行一边即可。

4.bjam编译好后,就可以利用它编译库文件了。将命令行的工作目录放置到$boost_dir下,然后执行一下bjam --help,看能否正确的执行bjam.exe。如果提示找不到文件,将bjam所在的路径添加到环境变量path中,实在不行就把bjam复制到$boost_dir下。

5.如果试图使用boost.python库,则需要添加安装python,并设置对应的环境变量,这里的root对应的是你的python的安装路径,ver对应的是你的python版本。
SET PYTHON_ROOT=X:\Python2.3.4
SET PYTHON_VERSION=2.3

6.如果使用了boost.iostreams的compress或者unicode功能,请参阅对应的提示。通常你需要zlib和icu这两个库。

7.编译选项:
选项参见$boost_dir\more\getting_started.html,这里有详细的说明,以下仅列举一个很具代表性的选项。

bjam -sBOOST_ROOT=. -sTOOLS=vc-7_1 --with-thread "-sBUILD=debug release <runtime-link>static/dynamic"

上面的命令行设置环境变量BOOST_ROOT为当前路径,使用Visual C++ 7.1编译器,仅编译thread库(因为完整的编译耗时很长,所以建议使用--with-<library_name>来编译指定库。类似的还有--without-<library_name>选项)。

编译好的库都在$boost_dir\bin下。你可以进去搜索所有的lib/dll文件然后剪切出来放到一个文件夹中,再把其他的中间文件删掉就好了。

类似的你还可以编译其他的类库,具体的库可能需要依赖一些其他的库,你可以参见库的编译说明。另外生成的库运行时链接情况也是不一样的,例如有的库不能支持静态链接。这一点请详细阅读帮助文件。

8.使用:
这里可能不太好举例。先将$boost_dir加入到编译器的include目录列表中。然后我编译好了thread库,并且将所有的相关文件都统一放置到了$boost_dir\bin\thread\目录下,我便可以在我的工程中将该目录添加到链接文件的路径中。然后依据情况选择是否手工添加库文件。在帮助的“Automatic Linking on Windows”一节,文档说,很多需要编译的库,boost都使用了#pragma指示字指明了库名称,也就是说只需要加上库所在的路径就好了。

最后是关于运行时库的问题。最好把你的工程选用的CRT与编译boost库时使用的CRT一致起来。这一点可以根据boost文件的名称判断。否则的话可能会出现内存的使用错误(尤其是分配和释放不在一个堆的时候更是如此)。

最后是一篇中文文档。这篇也不错。如果不清楚,E文也不是太好的可以参看它
http://blog.csdn.net/billdavid/archive/2005/03/07/313347.aspx
4#
 楼主| 发表于 2006-10-26 10:09 | 只看该作者
函数指针的声明和回调的实现

  程序员常常需要实现回调。本文将讨论函数指针的基本原则并说明如何使用函数指针实现回调。注意这里针对的是普通的函数,不包括完全依赖于不同语法和语义规则的类成员函数(类成员指针将在另文中讨论)。

声明函数指针

  回调函数是一个程序员不能显式调用的函数;通过将回调函数的地址传给调用者从而实现调用。要实现回调,必须首先定义函数指针。尽管定义的语法有点不可思议,但如果你熟悉函数声明的一般方法,便会发现函数指针的声明与函数声明非常类似。请看下面的例子:

void f();// 函数原型

上面的语句声明了一个函数,没有输入参数并返回void。那么函数指针的声明方法如下:

void (*) ();

  让我们来分析一下,左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

// 获得函数指针的大小
unsigned psize = sizeof (void (*) ());

// 为函数指针声明类型定义
typedef void (*pfv) ();

pfv是一个函数指针,它指向的函数没有输入参数,返回类行为void。使用这个类型定义名可以隐藏复杂的函数指针语法。

指针变量应该有一个变量名:

void (*p) (); //p是指向某函数的指针

  p是指向某函数的指针,该函数无输入参数,返回值的类型为void。左边圆括弧里星号后的就是指针变量名。有了指针变量便可以赋值,值的内容是署名匹配的函数名和返回类型。例如:

void func()
{
/* do something */
}
p = func;

p的赋值可以不同,但一定要是函数的地址,并且署名和返回类型相同。

传递回调函数的地址给调用者

  现在可以将p传递给另一个函数(调用者)- caller(),它将调用p指向的函数,而此函数名是未知的:

void caller(void(*ptr)())
{
ptr(); /* 调用ptr指向的函数 */
}
void func();
int main()
{
p = func;
caller(p); /* 传递函数地址到调用者 */
}

  如果赋了不同的值给p(不同函数地址),那么调用者将调用不同地址的函数。赋值可以发生在运行时,这样使你能实现动态绑定。

调用规范

  到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。

  将调用规范看成是函数类型的一部分是很重要的;不能用不兼容的调用规范将地址赋值给函数指针。例如:

// 被调用函数是以int为参数,以int为返回值
__stdcall int callee(int);

// 调用函数以函数指针为参数
void caller( __cdecl int(*ptr)(int));

// 在p中企图存储被调用函数地址的非法操作
__cdecl int(*p)(int) = callee; // 出错


  指针p和callee()的类型不兼容,因为它们有不同的调用规范。因此不能将被调用者的地址赋值给指针p,尽管两者有相同的返回值和参数列。
5#
 楼主| 发表于 2006-10-26 10:10 | 只看该作者
C++面试题集4

一. 华为一道面试题-1-n排序

有N个大小不等的自然数(1--N),请将它们由小到大排序。
要求程序算法:时间复杂度为O(n),空间复杂度为O(1)。

网上转的,一开始也没有注意到最开始的半句。

算法:N个不等的自然数1~N,排序完成后必然为1~N。所以可以一次遍历,遇到a!=i的就把a和a[a]交换。

void sort(int a[], int n)
{
int i;
int t; /*临时变量:空间复杂度O(1)*/

for (i=1; i<n+1; i++) /*时间复杂度O(n)*/
{
while(a!=i)
  {
t = a[a];
a[a] = a;//排好一个元素
a = t;
  }
}
}

二. 一次遍历 找 链表倒数第n个节点

一道面试题目,阿明和晨晨看到并且告诉我答案的。要求通过一次遍历找到链表中倒数第n个节点,链表可能相当大,可使用辅助空间,但是辅助空间的数目必须固定,不能和n有关。
算法思想:两根指针,第一根先出发,相距n步后第二根出发。然后同时步进,直到第一根指针达到末尾。

struct iNode {
int value;
iNode * next;
};
iNode * getresult(iNode * head,int n)
{

iNode *pfirst;
iNode *psecond;

pfirst=head;
int counter;

for(counter=0;counter<n;counter++) {
pfirst=pfirst->next;
}

psecond=head;

while(pfirst!=NULL) {
pfirst=pfirst->next;
psecond=psecond->next;
}

return psecond;

}

三. VC++学习笔记

1.     日期转成字符串:

  COleDateTime   ww;
ww=COleDateTime::GetCurrentTime();
AfxMessageBox(ww.Format("%Y-%m-%d %H:%M:%S"));

2.     字符串转成日期:

COleDateTime dt;

     dt.ParseDateTime(“2006-08-08 08:08:08”);

3.     资源文件

资源文件名:xxx.rc,其中要包含的主要文件:resource.h和afxres.h

4.     vc开发环境没有自动提示时:

  删除 目录下的ncb文件 ,再打开一般就ok了

5.     利用_variant_t 取数据库数据的方法:

  _variant_t ibb;

     ibb=(_variant_t)rs->GetCollect("inta");

     if(ibb.vt!=VT_NULL)

     {

          m_b=ibb.lVal;

     }

6.     平时取记录集字段值的方法:

  (LPCTSTR)(_bstr_t)rs->GetCollect("datea")

7.     DoModal()可以返回两个结果 IDOK,IDCANCEL,他们都是int型,分别是:1,2。通过EndDialog(IDOK)的方式返回。

8.     一般将数据库连接方面的信息放到app中。则AfxGetApp()非常重要,如;

  CAdo2App* mapp=(CAdo2App*)AfxGetApp();

  Map->conn->Execute(sql,NULL,adCmdText);

9.     DECLARE_DYNCREATE(类名),IMPLEMENT_DYNCREATE(类名,基类名)  使得由CObject继承来的类在程序运行的时候能够动态的创建。

10.  DECLARE_DYNAMIC(类名),IMPLEMENT_DYNAMIC(类名,基类名)  可以在运行时获得该类的信息

11.  DECLARE_SERIAL(类名),IMPLEMENT_SERIAL(类名,基类名,0)为一个可以串行化的CObject派生类产生必要的C++标题代码

12.  获得文档的方法: CMainFrame * pFrame=(CMainFrame *) AfxGetMainWnd();

CPClientDoc * pDoc =(CPClientDoc *) pFrame->GetActiveDocument();
  

13.  获得视图的方法:CMainFrame * pFrame=(CMainFrame *) AfxGetMainWnd();

myView =(CPClientView*) pFrame->GetActiveView();

14.  如果要引用全局变量或者全局方法,须在当前类中引入:extern 名字;
6#
 楼主| 发表于 2006-10-26 10:10 | 只看该作者
C语言中可变参数的用法
我们在C语言编程中会遇到一些参数个数可变的函数,例如printf()这个函数,这里将介绍可变函数的写法以及原理.

* 1. 可变参数的宏

一般在调试打印Debug 信息的时候, 需要可变参数的宏. 从C99开始可以使编译器标准支持可变参数宏(variadic macros), 另外GCC 也支持可变参数宏, 但是两种在细节上可能存在区别.

1. __VA_ARGS__

__VA_ARGS__ 将"..." 传递给宏.如
#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)

在GCC中也支持这类表示, 但是在G++ 中不支持这个表示.

2. GCC 的复杂宏

GCC使用一种不同的语法从而可以使你可以给可变参数一个名字,如同其它参数一样。
#define debug(format, args...) fprintf (stderr, format, args)

这和上面举的那个定义的宏例子是完全一样的,但是这么写可读性更强并且更容易进行描述。

3. ##__VA_ARGS__

上面两个定义的宏, 如果出现debug("A Message") 的时候, 由于宏展开后有个多余的逗号, 所以将导致编译错误. 为了解决这个问题,CPP使用一个特殊的‘##’操作。

#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
这里,如果可变参数被忽略或为空,‘##’操作将使预处理器(preprocessor)去除掉它前面的那个逗号。如果你在宏调用时,确实提供了一些可变参数,GNU CPP也会工作正常,它会把这些可变参数放到逗号的后面。

4. 其他方法

一种流行的技巧是用一个单独的用括弧括起来的的 "参数" 定义和调用宏, 参数在宏扩展的时候成为类似 printf() 那样的函数的整个参数列表。
#define DEBUG(args) (printf("DEBUG: "), printf(args))


* 2. 可变参数的函数

写可变参数的C函数要在程序中用到以下这些宏:
void va_start( va_list arg_ptr, prev_param )
type va_arg( va_list arg_ptr, type )
void va_end( va_list arg_ptr )

va在这里是variable-argument(可变参数)的意思,这些宏定义在stdarg.h中.下面我们写一个简单的可变参数的函数,该函数至少有一个整数参数,第二个参数也是整数,是可选的.函数只是打印这两个参数的值.
void simple_va_fun(int i, ...)
{
   va_list arg_ptr;
   int j=0;
   
   va_start(arg_ptr, i);
   j=va_arg(arg_ptr, int);
   va_end(arg_ptr);
   printf("%d %d\n", i, j);
   return;
}

在程序中可以这样调用:
simple_va_fun(100);
simple_va_fun(100,200);

从这个函数的实现可以看到,使用可变参数应该有以下步骤:
1)首先在函数里定义一个va_list型的变量,这里是arg_ptr,这个变量是指向参数的指针.
2)然后用va_start宏初始化变量arg_ptr,这个宏的第二个参数是第一个可变参数的前一个参数,是一个固定的参数.
3)然后用va_arg返回可变的参数,并赋值给整数j. va_arg的第二个参数是你要返回的参数的类型,这里是int型.
4)最后用va_end宏结束可变参数的获取.然后你就可以在函数里使用第二个参数了.如果函数有多个可变参数的,依次调用va_arg获取各个参数.

如果我们用下面三种方法调用的话,都是合法的,但结果却不一样:
1)simple_va_fun(100);
结果是:100 -123456789(会变的值)
2)simple_va_fun(100,200);
结果是:100 200
3)simple_va_fun(100,200,300);
结果是:100 200

我们看到第一种调用有错误,第二种调用正确,第三种调用尽管结果正确,但和我们函数最初的设计有冲突.下面一节我们探讨出现这些结果的原因和可变参数在编译器中是如何处理的.
* 3. 可变参数函数原理

va_start,va_arg,va_end是在stdarg.h中被定义成宏的,由于硬件平台的不同,编译器的不同,所以定义的宏也有所不同,下面以VC++中stdarg.h里x86平台的宏定义摘录如下:

typedef char * va_list;
#define _INTSIZEOF(n) ((sizeof(n)+sizeof(int)-1)&~(sizeof(int) - 1) )
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
#define va_end(ap) ( ap = (va_list)0 )

定义_INTSIZEOF(n)主要是为了内存对齐,C语言的函数是从右向左压入堆栈的(设数据进栈方向为从高地址向低地址发展,即首先压入的数据在高地址). 下图是函数的参数在堆栈中的分布位置:

低地址   |-----------------------------|<-- &v
      |第n-1个参数(最后一个固定参数)|
      |-----------------------------|<--va_start后ap指向
      |第n个参数(第一个可变参数) |
      |-----------------------------|
      |....... |
      |-----------------------------|
      |函数返回地址 |
高地址  |-----------------------------|

1. va_list 被定义为char *
2. va_start 将地址ap定义为 &v+_INTSIZEOF(v),而&v是固定参数在堆栈的地址,所以va_start(ap, v)以后,ap指向第一个可变参数在堆栈的地址
3. va_arg 取得类型t的可变参数值,以int型为例,va_arg取int型的返回值:
  j= ( *(int*)((ap += _INTSIZEOF(int))-_INTSIZEOF(int)) );
4. va_end 使ap不再指向堆栈,而是跟NULL一样.这样编译器不会为va_end产生代码.

在不同的操作系统和硬件平台的定义有些不同,但原理却是相似的.


* 4. 小结

对于可变参数的函数,因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数需要在该函数中由程序代码控制;另外,编译器对可变参数的函数的原型检查不够严格,对编程查错不利.
所以我们写一个可变函数的C函数时,有利也有弊,所以在不必要的场合,无需用到可变参数.如果在C++里,我们应该利用C++的多态性来实现可变参数的功能,尽量避免用C语言的方式来实现.


* 5. 附一些代码

#define debug(format, ...) fprintf(stderr, fmt, __VA_ARGS__)
#define debug(format, args...) fprintf (stderr, format, args)
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)

// 使用va... 实现
void debug(const char *fmt, ...)
{
   int nBuf;
   char szBuffer[1024];
   va_list args;

   va_start(args, fmt);
   nBuf = vsprintf(szBuffer, fmt, args) ;
   assert(nBuf >= 0);

   printf("QDOGC ERROR:%s\n",szBuffer);
   va_end(args);
}
您需要登录后才可以回帖 登录 | 注册

本版积分规则

Archiver|小黑屋| 碧海潮声大学生网  

Copyright © 2001-2013 Comsenz Inc.   All Rights Reserved.

Powered by Discuz! X3.2( 浙ICP备11026473号 )

快速回复 返回顶部 返回列表