woka

2009年11月21日

MFC视口窗口

1.SetWindowOrg 是把设备坐标的原点移动到逻辑坐标的(X, Y)处 

2.SetViewportOrg 是把逻辑坐标的原点移动到设备坐标的(X, Y)处 

posted @ 2009-11-21 20:02 woka 阅读(178) | 评论 (0)编辑 收藏

zz:mfc中使用标准库stl出现xdebug、xlocale错误的解决方法

mfc中使用标准库stl出现xdebug、xlocale错误的解决方法

参考:

在2005mfc的单文档框架中使用#include<fstream>编译出错

初學者想請問 xlocale 以及 xdebug

Debug errors in VC++ .NET (xdebug)

最近因为编程需要,需要在控制台程序中使用mfc的CString,在建立console工程的时候,选择了mfc选项,

结果在include标准库stl的头文件的时候,出现了一大堆的xdebug、xlocale错误,该问题我是第一次碰见,

在一番google搜索之后,找到了解决方法,特此做一个收藏

// 来源:http://social.msdn.microsoft.com/Forums/zh-TW/234/thread/e27da79b-ac38-4db8-a1d8-5536070590e9

// 正常的 头部:

#include "stdafx.h"
#include "OLE DOC Viewer.h"
#include "OLE DOC ViewerDlg.h"
#include ".\ole doc viewerdlg.h"
#include "selectiondlg.h"

#include "QQCrypt.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// #include <iostream>

// 但是如果把使用了 STL的文件的#include 放在后面就出问题!!!

=============================================================

=============================================================

// 解决方法
#include "stdafx.h"
#include "OLE DOC Viewer.h"
#include "OLE DOC ViewerDlg.h"
#include ".\ole doc viewerdlg.h"
#include "selectiondlg.h"

// 在这里包含标准库的相关头文件

#include <iostream>

using namespace std;


#ifdef _DEBUG
#define new DEBUG_NEW
#endif


#include "QQCrypt.h"

// 原因是DEBUG中,new被重载了!

正如上面得代码所说,在debug版下,new被重载(标准库stl内部对new进行了重载,两者互相冲突)

必须保证标准库include语句出现在

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

之前;

最简单的方法就是将所有的需要的stl的头文件放在stdafx.h文件中(预编译头文件),这样肯定能够保证stl

的头文件出现在上面的debug代码的前面

ps:当然我并不推荐这种方法,将所有的头文件都丢到stdafx.h中,这是一种不负责任的编码习惯,

“应该合理的安排头文件之间的依赖关系”--参见effective c++

posted @ 2009-11-21 16:14 woka 阅读(397) | 评论 (0)编辑 收藏

2009年10月22日

什么是虚函数

多态性是将接口与实现进行分离;用形象的语言来解释就是实现以共同的方法,但因个体差异而采用不同的策略。
这其实也是软件工程设计的一个十分基本的原理:策略与机制分离,机制决定如何来做,策略决定做什么;多态性中的函数决定了做什么,而不同的类对这一相同的决策又有着不同的行为,这样使的我们可以以更简单的接口实现更加全面复杂灵活的功能。

posted @ 2009-10-22 22:25 woka 阅读(104) | 评论 (0)编辑 收藏

zz:extern "C"

时常在cpp的代码之中看到这样的代码: 

#ifdef __cplusplus 
extern "C" { 
#endif 

//一段代码 

#ifdef __cplusplus 

#endif

  这样的代码到底是什么意思呢?首先,__cplusplus是cpp中的自定义宏,那么定义了这个宏的话表示这是一段cpp的代码,也就是说,上面的代码的含义是:如果这是一段cpp的代码,那么加入extern "C"{和}处理其中的代码。 

  要明白为何使用extern "C",还得从cpp中对函数的重载处理开始说起。在c++中,为了支持重载机制,在编译生成的汇编码中,要对函数的名字进行一些处理,加入比如函数的返回类型等等.而在C中,只是简单的函数名字而已,不会加入其他的信息.也就是说:C++和C对产生的函数名字的处理是不一样的. 

  比如下面的一段简单的函数,我们看看加入和不加入extern "C"产生的汇编代码都有哪些变化: 

int f(void) 

return 1; 
}

  在加入extern "C"的时候产生的汇编代码是: 

.file "test.cxx" 
.text 
.align 2 
.globl _f 
.def _f; .scl 2; .type 32; .endef 
_f: 
pushl %ebp 
movl %esp, %ebp 
movl $1, %eax 
popl %ebp 
ret

  但是不加入了extern "C"之后 

.file "test.cxx" 
.text 
.align 2 
.globl __Z1fv 
.def __Z1fv; .scl 2; .type 32; .endef 
__Z1fv: 
pushl %ebp 
movl %esp, %ebp 
movl $1, %eax 
popl %ebp 
ret

  两段汇编代码同样都是使用gcc -S命令产生的,所有的地方都是一样的,唯独是产生的函数名,一个是_f,一个是__Z1fv。 

  明白了加入与不加入extern "C"之后对函数名称产生的影响,我们继续我们的讨论:为什么需要使用extern "C"呢?C++之父在设计C++之时,考虑到当时已经存在了大量的C代码,为了支持原来的C代码和已经写好C库,需要在C++中尽可能的支持C,而extern "C"就是其中的一个策略。 

  试想这样的情况:一个库文件已经用C写好了而且运行得很良好,这个时候我们需要使用这个库文件,但是我们需要使用C++来写这个新的代码。如果这个代码使用的是C++的方式链接这个C库文件的话,那么就会出现链接错误.我们来看一段代码:首先,我们使用C的处理方式来写一个函数,也就是说假设这个函数当时是用C写成的: 

//f1.c 
extern "C" 

void f1() 

return; 

}

  编译命令是:gcc -c f1.c -o f1.o 产生了一个叫f1.o的库文件。再写一段代码调用这个f1函数: 

// test.cxx 
//这个extern表示f1函数在别的地方定义,这样可以通过 
//编译,但是链接的时候还是需要 
//链接上原来的库文件. 
extern void f1(); 

int main() 

f1(); 

return 0; 
}

  通过gcc -c test.cxx -o test.o 产生一个叫test.o的文件。然后,我们使用gcc test.o f1.o来链接两个文件,可是出错了,错误的提示是: 

test.o(.text + 0x1f):test.cxx: undefine reference to 'f1()'
  也就是说,在编译test.cxx的时候编译器是使用C++的方式来处理f1()函数的,但是实际上链接的库文件却是用C的方式来处理函数的,所以就会出现链接过不去的错误:因为链接器找不到函数。 

  因此,为了在C++代码中调用用C写成的库文件,就需要用extern "C"来告诉编译器:这是一个用C写成的库文件,请用C的方式来链接它们。 

  比如,现在我们有了一个C库文件,它的头文件是f.h,产生的lib文件是f.lib,那么我们如果要在C++中使用这个库文件,我们需要这样写: 

extern "C" 

#include "f.h" 
}

  回到上面的问题,如果要改正链接错误,我们需要这样子改写test.cxx: 

extern "C" 

extern void f1(); 


int main() 

f1(); 

return 0; 
}

  重新编译并且链接就可以过去了. 

posted @ 2009-10-22 19:19 woka 阅读(85) | 评论 (0)编辑 收藏

2009年10月18日

转载:解析#pragma指令

在所有的预处理指令中,#Pragma 指令可能是最复杂的了,它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作。#pragma指令对每个编译器给出了一个方法,在保持与C和C++语言完全兼容的情况下,给出主机或操作系统专有的特征。依据定义,编译指示是机器或操作系统专有的,且对于每个编译器都是不同的。
其格式一般为: #Pragma Para
其中Para 为参数,下面来看一些常用的参数。

(1)message 参数。 Message 参数是我最喜欢的一个参数,它能够在编译信息输出窗
口中输出相应的信息,这对于源代码信息的控制是非常重要的。其使用方法为:
#Pragma message(“消息文本”)
当编译器遇到这条指令时就在编译输出窗口中将消息文本打印出来。
当我们在程序中定义了许多宏来控制源代码版本的时候,我们自己有可能都会忘记有没有正确的设置这些宏,此时我们可以用这条指令在编译的时候就进行检查。假设我们希望判断自己有没有在源代码的什么地方定义了_X86这个宏可以用下面的方法
#ifdef _X86
#Pragma message(“_X86 macro activated!”)
#endif
当我们定义了_X86这个宏以后,应用程序在编译时就会在编译输出窗口里显示“_
X86 macro activated!”。我们就不会因为不记得自己定义的一些特定的宏而抓耳挠腮了


(2)另一个使用得比较多的pragma参数是code_seg。格式如:
#pragma code_seg( ["section-name"[,"section-class"] ] )
它能够设置程序中函数代码存放的代码段,当我们开发驱动程序的时候就会使用到它。

(3)#pragma once (比较常用)
只要在头文件的最开始加入这条指令就能够保证头文件被编译一次,这条指令实际上在VC6中就已经有了,但是考虑到兼容性并没有太多的使用它。

(4)#pragma hdrstop表示预编译头文件到此为止,后面的头文件不进行预编译。BCB可以预编译头文件以加快链接的速度,但如果所有头文件都进行预编译又可能占太多磁盘空间,所以使用这个选项排除一些头文件。 
有时单元之间有依赖关系,比如单元A依赖单元B,所以单元B要先于单元A编译。你可以用#pragma startup指定编译优先级,如果使用了#pragma package(smart_init) ,BCB就会根据优先级的大小先后编译。 

(5)#pragma resource "*.dfm"表示把*.dfm文件中的资源加入工程。*.dfm中包括窗体
外观的定义。 

(6)#pragma warning( disable : 4507 34; once : 4385; error : 164 )
等价于:
#pragma warning(disable:4507 34) // 不显示4507和34号警告信息
#pragma warning(once:4385) // 4385号警告信息仅报告一次
#pragma warning(error:164) // 把164号警告信息作为一个错误。
同时这个pragma warning 也支持如下格式:
#pragma warning( push [ ,n ] )
#pragma warning( pop )
这里n代表一个警告等级(1---4)。
#pragma warning( push )保存所有警告信息的现有的警告状态。
#pragma warning( push, n)保存所有警告信息的现有的警告状态,并且把全局警告
等级设定为n。 
#pragma warning( pop )向栈中弹出最后一个警告信息,在入栈和出栈之间所作的
一切改动取消。例如:
#pragma warning( push )
#pragma warning( disable : 4705 )
#pragma warning( disable : 4706 )
#pragma warning( disable : 4707 )
//.......
#pragma warning( pop ) 
在这段代码的最后,重新保存所有的警告信息(包括4705,4706和4707)。
(7)pragma comment(...)
该指令将一个注释记录放入一个对象文件或可执行文件中。
常用的lib关键字,可以帮我们连入一个库文件。

posted @ 2009-10-18 22:35 woka 阅读(74) | 评论 (0)编辑 收藏

2009年10月14日

转载:const指针


const修饰指针和引用的用法,对于初学C++的人直是讳莫如深,不知所云.一旦你了解了其用法,一切便不值一哂了.下面我为读者一一释疑
   
大致说来其可分为三种情况: const修饰指针,const修饰引用,const修饰指针的引用
1.const
修饰指针 
   const
修饰指针又可分为三种情况
     const
修饰指针本身 
     const
修饰指针所指的变量(或对象
     const
修饰指针本身和指针所指的变量(或对象
(1).const
修饰指针本身 
    
这种情形下,指针本身为常量,不可改变,任何修改指针本身的行为都是非法的.例如
const int a = 1; 
const int b = 2; 
int i = 3; 
int j = 4; 
int* const pi = &i; //ok, pi
的类型为int* const , &i的类型为int* const 
int* const pi = &a; //error, pi
的类型为int* const, &a的类型为const int* const 
pi = &j; //error, 
指针是常量,不可变 
*pi = a; //ok, *pi
并没有限定是常量 ,可变 
    
由此看出,pi是常量,常量在初始化和赋值时,类型必须严格一致。也就是 
const
修饰指针本身时,=号两边的变量类型必须严格一致,否则不能匹配。 
(2).const
修饰指针指向的变量(或对象
    
此种情形下,通过间接引用指针不可改变变量的值,假设指针为p,*p不可变,下面以例子说明
const int *pi = &a; 
//or int const *pi = &a; 
//
两者毫无二致,不过BS喜欢前者,这也没什么技术上的优劣之分,也就是说const intint const可以互换.建议大家熟 
//
悉这两种形式,为简洁便,以后统统用前者
//ok, const
并不修饰指针本身,pi对赋值类型 
//
没要求 ,piint*型指针,所以所赋的必须是个地址值。 
const int *pi = &i; //ok ,pi
可赋值常量的地址,又可赋变量的地址 
const int *pi1 = &a; 
const int *pi = pi1; //ok 
*pi = j; //error,*pi 
不可变,不能更改指针的间接引用形式 
pi = &j; //ok,pi
可变 
pi = &b; //ok,pi
可变 
pi++; //ok 
--pi; //ok 
     
由此可见,pi是变量,可以赋值常量和变量的值,正如一个整型变量可赋整型数和整型变量一样.const修饰的不是指针本身,而是其间接引用,=号两边的类型不必严格匹配,如:const int* pi = &a;中,pi的类型为int*,&a的类型为const int* const,只要其中含有int* 就可以。又如:const int *pi = &j;中,pi的类型为int*,&j的类型为int* const,它向pi赋值并无大碍。 
(3)const
修饰指针本身和指针所指的变量(或对象
     
设有指针p,此种情形下,p*p都不可变.举例如下
const int* const pi = &a; 
//or int const* const pi = &a; 
//
const pi看作一体,就与(2)所述相同,只是要求pi必须为const,正如上所说,=号两边的类型不必严格匹配,但必须含有int*, &a的类型为const int* const,含有int*, 所以可以赋值。 
const int* const pi = &i; //ok, &i
类型为int* const,含有int*, 可赋值。 
const int *pi1 = &j; 
const int *const pi = pi1; //ok,  pi1
类型为int* 
pi = &b; //error, pi
不可变 
pi = &j; //error, pi
不可变 
*pi = b; //error, *pi
不可变 
*pi = j; //error, *pi
不可变 
pi++; //error 
pi不可变 
++i; //ok, =
号右边的变量(或对象)与所修饰的变量无关 
a--; //error, a
const 
     
这种情况,跟以上两种情形有联系。对const int* const pi = &a;我们可以这样看:const int*( const pi )= &a;(仅仅是表达需要),将const pi看作一体,就与上述分类(2)符合。只要含有int*便可

2.const
修饰引用 
    
这种情况比较简单,没有象修饰指针那样繁复,因为引用和引用对象是一体的,所以引用被const修饰只有一种类型。 
const
修饰引用,引用本身不可变,但引用的变量(或对象)可以改变.例如
const int& ri = a; //or int const & ri = a; ok, ri 
本身是常量,引用不区分类型 
const int& ri = i; //ok,
引用不区分类型 
ri++; //error, ri
为常量,不可变 
i++; //ok,=
右边的变量与引用无关 
ri=b; //error, ri
为常量 
i=j; //ok,=
右边的变量与引用无关 
int & const ri = i; //error,
不存在这种形式,没有意义 

3.const
修饰指针的引用 
    
引用只是个别名,这里与修饰指针类似,又分为三种情况
(1) 
     
先给个例子: 
const int *pi = &a; 
const int *&ri = pi; 
//or int const *&ri = pi; 
    
引用是引用对象的别名,正因为如此,ripi的别名,所以ri的类型必须与pi完全一致才行。这里pi的类型为int*,ri的类型也为int*,赋值可行。若const int *&ri = &a;正不正确?分析一下就知晓。ri类型为int*,&a的类型则为const int* const不匹配。 
const int *&ri = &i; //error,
类型不匹配,一为int*,一为int* const 
ri = &a; //ok 
ri = &i; //ok 
const int *pi1=&a; 
const int *pi2=&i; 
ri = pi1; //ok 
ri = pi2; //ok 
*ri = i; //error 
*ri = a; //error 
     
注意这与1-(2)的区别
(2) 
     
用例子说明
int *const &ri = &i; 
    
去掉ri左边的&,则为int *const ri,因为ri是别名,ri的类型应与赋值的数类型一致,ri类型为int *const,&iint *const,可以这么做
int *const &ri = pi; //error,
类型不合,一为int *const ,一为int * 
int *const &ri = &a; //error,
类型不合,一为int *const,一为const int* const 
(*ri)++; //ok 
i++; //ok 
ri = &i; //error 
   
这种情况下,ri为常量,不可更改
(3) 
     
用例子说明: 
const int* pi = &j; 
const int* const &ri = pi; //or int const * const &ri = pi;ok 
const int* const &ri = &i; //ok 
     ri
pi的别名,pi的类型应与ri一致。拿掉&,得const int* const ri ,把const  ri看作一体,很容易得出ri的类型信息,就象前面2-(3)所讨论的一样,可以得到赋给ri的只要含有类型int* 即可。pi的类型为int*,&i的类型为int* const ,可以这么做
const int * const &ri = &a; //ok 
ri++;  //error 
*ri = 6;  //error 
    
言尽于此,希望对初学者有所助益!

posted @ 2009-10-14 19:22 woka 阅读(930) | 评论 (0)编辑 收藏

2009年10月12日

结构体中变量的对齐

C/C++的实现中为了提高结构体的访问效率,进行了变量的对齐,而Visual C++中的标准有不一样 struct member alignment
因此如果是读一些结构比较固定的文件,如果用结构体定义文件中部分,如果用结构体,很容易出错,类中的变量不会进行对齐,因此用类更加简单(比如读BMP文件),如果用结构体也可以用伪指令
#pragma pack (n)

#pragma pack ()

posted @ 2009-10-12 23:48 woka 阅读(143) | 评论 (0)编辑 收藏

2009年9月9日

WM_CLOSE 和 WM_DESTORY

MFC程序的死亡相对于初生来说要简单的多,主要是以下几步:    
    1.使用者通过点击File/Close或程序窗口右上角的叉号发出WM_CLOSE消息。    
    2.程序没有设置WM_CLOSE处理程序,交给默认处理程序。    
    3.默认处理函数对于WM_CLOSE的处理方式为调用::DestoryWindow,并因而发出WM_DESTORY消息。    
    4.默认的WM_DESTORY处理方式为调用::PostQuitMessage,发出WM_QUIT。    
    5.CWinApp::Run收到WM_QUIT后结束内部消息循环,并调用ExinInstance函数,它是CWinApp的一个虚拟函数,可以由用户重载。    
    6.最后回到AfxWinMain,执行AfxWinTerm,结束程序。    
  ---------------------------------------------------  
  有时你看到有的程序当点右上角的叉的时候没有关闭程序而是最小化了,就是因为它重载了OnClose,把默认的发送Destory消息给删掉了
但是下面这个程序有什么错误,为什么关掉窗口后程序不能正常结束(窗口关闭,进程没有结束),希望高手帮忙。
#include <windows.h>

LRESULT CALLBACK WinProc(HWND, UINT, WPARAM, LPARAM);
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, LPSTR lpCmdLine, int nShowCmd){
    HWND hwnd;
    WNDCLASS wndclass;
    MSG msg;
    
char lpszClassName[] = "我的窗口";

    wndclass.lpszClassName 
= lpszClassName;
    wndclass.lpfnWndProc 
= WinProc;
    wndclass.hInstance 
= hInstance;
    wndclass.style 
= 0;
    wndclass.cbClsExtra 
= 0;
    wndclass.cbWndExtra 
= 0;
    wndclass.hbrBackground 
= (HBRUSH)GetStockObject(WHITE_BRUSH);
    wndclass.hCursor 
= LoadCursor(NULL, IDC_ARROW);
    wndclass.hIcon 
= LoadIcon(NULL, IDI_HAND);
    wndclass.lpszMenuName 
= NULL;

    
if(!RegisterClass(&wndclass)){
        MessageBeep(
0);
        
return FALSE;
    }


    
char lpszTitle[] = "My Window";
    hwnd 
= CreateWindow(
        lpszClassName,
        lpszTitle,
        WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        NULL,
        NULL,
        hInstance,
        NULL
        );
    ShowWindow(hwnd, nShowCmd);
    UpdateWindow(hwnd);
    
    
while(GetMessage(&msg, hwnd, 00)){
        TranslateMessage(
&msg);
        DispatchMessage(
&msg);
    }

    
return msg.wParam;
}


LRESULT CALLBACK WinProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    
switch(message){
        
case WM_PAINT:
            HDC hdc;
            hdc 
= GetDC(hwnd);
            TextOut(hdc, 
00"VC++", strlen("VC++"));
            DeleteDC(hdc);
            
break;
        
case WM_DESTROY:
            PostQuitMessage(
0);
            
return 0
        default:
            
return DefWindowProc(hwnd, message, wParam, lParam);
    }

    
return 0;
}

posted @ 2009-09-09 20:34 woka 阅读(2720) | 评论 (4)编辑 收藏

2009年6月14日

VC中的变态错误

在VC中(无论VC6.0还是VS2005),如果建立的是一个C文件(后缀为.c而不是.cpp),如以下一段程序:


#include <iostream>
using namespace std;
void pp(int a){
 cout << a << endl;
}

void p(int a){
 pp(a);
 int b = 0;//应该放在最前面
 pp(b);
}

int main(){
 int a = 0;
 p(a);
 return 0;
}


保存为 test.c
error C2143: 语法错误 : 缺少“{”(在“:”的前面)
error C2059: 语法错误 : “:”
总共一百多个错误
原来在VC中编译c程序,在一个大括号括起的范围内,如果变量声明放在了函数调用的后面,那么编译就会出错。而在其他一些标准C/C++编译器中是不会出现这个错误的。

posted @ 2009-06-14 00:45 woka 阅读(241) | 评论 (2)编辑 收藏

2009年5月24日

C++不定参数

今天了解到了C++还有不定参数一说:


C/C++语言有一个不同于其它语言的特性,即其支持可变参数,典型的函数如printf、scanf等可以接受数量不定的参数。如:

printf ( "I love you" );
printf ( "%d", a );
printf ( "%d,%d", a, b );

第一、二、三个printf分别接受1、2、3个参数,让我们看看printf函数的原型:

int printf ( const char *format, ... );

从函数原型可以看出,其除了接收一个固定的参数format以外,后面的参数用"…"表示。在C/C++语言中,"…"表示可以接受不定数量的参数,理论上来讲,可以是0或0以上的n个参数。

本文将对C/C++可变参数表的使用方法及C/C++支持可变参数表的深层机理进行探索。

可变参数表的用法

1、相关宏

标准C/C++包含头文件stdarg.h,该头文件中定义了如下三个宏:

void va_start ( va_list arg_ptr, prev_param ); /* ANSI version */
type va_arg ( va_list arg_ptr, type );
void va_end ( va_list arg_ptr );

在这些宏中,va就是variable argument(可变参数)的意思;arg_ptr是指向可变参数表的指针;prev_param则指可变参数表的前一个固定参数;type为可变参数的类型。va_list也是一个宏,其定义为typedef char * va_list,实质上是一char型指针。char型指针的特点是++、--操作对其作用的结果是增1和减1(因为sizeof(char)为1),与之不同的是int等其它类型指针的++、--操作对其作用的结果是增sizeof(type)或减sizeof(type),而且sizeof(type)大于1。

通过va_start宏我们可以取得可变参数表的首指针,这个宏的定义为:

#define va_start ( ap, v ) ( ap = (va_list)&v + _INTSIZEOF(v) )

显而易见,其含义为将最后那个固定参数的地址加上可变参数对其的偏移后赋值给ap,这样ap就是可变参数表的首地址。其中的_INTSIZEOF宏定义为:

#define _INTSIZEOF(n) ((sizeof ( n ) + sizeof ( int ) - 1 ) & ~( sizeof( int ) - 1 ) )

va_arg宏的意思则指取出当前arg_ptr所指的可变参数并将ap指针指向下一可变参数,其原型为:

#define va_arg(list, mode) ((mode *)(list =\
(char *) ((((int)list + (__builtin_alignof(mode)<=4?3:7)) &\
(__builtin_alignof(mode)<=4?-4:-8))+sizeof(mode))))[-1]

对这个宏的具体含义我们将在后面深入讨论。

而va_end宏被用来结束可变参数的获取,其定义为:

#define va_end ( list )

可以看出,va_end ( list )实际上被定义为空,没有任何真实对应的代码,用于代码对称,与va_start对应;另外,它还可能发挥代码的"自注释"作用。所谓代码的"自注释",指的是代码能自己注释自己。

下面我们以具体的例子来说明以上三个宏的使用方法。

2、一个简单的例子

#include <stdarg.h>
/* 函数名:max
* 功能:返回n个整数中的最大值
* 参数:num:整数的个数 ...:num个输入的整数
* 返回值:求得的最大整数
*/
int max ( int num, ... )
{
int m = -0x7FFFFFFF; /* 32系统中最小的整数 */
va_list ap;
va_start ( ap, num );
for ( int i= 0; i< num; i++ )
{
int t = va_arg (ap, int);
if ( t > m )
{
m = t;
}
}
va_end (ap);
return m;
}
/* 主函数调用max */
int main ( int argc, char* argv[] )
{
int n = max ( 5, 5, 6 ,3 ,8 ,5); /* 求5个整数中的最大值 */
cout << n;
return 0;
}

函数max中首先定义了可变参数表指针ap,而后通过va_start ( ap, num )取得了参数表首地址(赋给了ap),其后的for循环则用来遍历可变参数表。这种遍历方式与我们在数据结构教材中经常看到的遍历方式是类似的。


函数max看起来简洁明了,但是实际上printf的实现却远比这复杂。max函数之所以看起来简单,是因为:

(1) max函数可变参数表的长度是已知的,通过num参数传入;

(2) max函数可变参数表中参数的类型是已知的,都为int型。

而printf函数则没有这么幸运。首先,printf函数可变参数的个数不能轻易的得到,而可变参数的类型也不是固定的,需由格式字符串进行识别(由%f、%d、%s等确定),因此则涉及到可变参数表的更复杂应用。
参考资料:http://www.diybl.com/course/3_program/c++/cppsl/2008520/117226.html


引用自:http://zhidao.baidu.com/question/70029005.html

posted @ 2009-05-24 01:28 woka 阅读(1062) | 评论 (0)编辑 收藏

仅列出标题  下一页
<2018年8月>
2930311234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(2)

随笔档案

搜索

最新评论

阅读排行榜

评论排行榜