博文

Some Classical Websites on Programming(2009-05-24 14:58:00)

摘要:Cprogramming.com  http://www.cprogramming.com/ Programming in C UNIX System Calls and Subroutines using C. http://www.cs.cf.ac.uk/Dave/C/ C++ Reference  http://www.cppreference.com/ Cplusplus.com  http://www.cplusplus.com/ Tutorials on Selected C/C++ Topics http://www.augustcouncil.com/~tgibson/tutorial/ Doctor Dobb's Journal  C/C++ Features http://www.ddj.com/cpp/archives.jhtml   Visual C++ Knowledge base  http://www.vckbase.com/ 维C世界 http://vcer.net/ C++, Visual C++ and MFC - Tips and Tricks  http://visualcpp.net/ FunctionX  http://www.functionx.com/   JavaScript.com  http://www.javascript.com/ JAVASCRIPT KIT  http://www.javascriptkit.com/ THE JavaScript source  http://javascript.internet.com/ Share JavaScript http://www.sharejs.com/   Php.net  http://www.php.net/ PHP BUILDER  http://www.phpbuilder.com/ PHPDeveloper.org  http://www.phpdeveloper.org/ The PHP Resource Index  htt......

阅读全文(2486) | 评论:0

野指针小结(2009-05-19 23:07:00)

摘要: 1.指针的初始化 指针变量的零值是“空”(记为NULL)。在<stdio.h>中#define NULL 0,尽管NULL 的值与0 相同,但是两者意义不同。假设指针变量的名字为p,它与零值比较的标准if 语句如下: if (p == NULL) // p 与NULL 显式比较,强调p 是指针变量。 当我们试图析取(dereference)一个空指针NULL时,例如int *p = NULL;当我们试图cout<<*p;析取p时,将会出现内存读错误。因为0x00000000为进程私有地址,是不允许访问的,因此将会弹出应用程序错误:“0x********”指令引用的“0x00000000”内存。该内存不能“read”。 如果定义指针时把它初始化为NULL,我们的代码就能用if(ptr==NULL)来判断它是不是有效的指针。 因此,建议定义指针后将其初始化为NULL或指向合法内存。 典型错误:char *dest; char *src = "Fantasy"; strcpy(dest, src); 2.检查一个指针是否有效 malloc returns a void pointer to the allocated space, or NULL if there is insufficient memory available. If there is insufficient memory for the allocation request, by default operator new returns NULL. 用malloc 或new 申请内存之后,应该用if(p==NULL)检查指针值是否为NULL,防止使用指针值为NULL 的内存。如果指针p 是函数的参数,那么在函数的入口处用assert(p!=NULL)进行检查。 3.野指针 1)、指针变量在定义后如果没有初始化是野指针,其值不为NULL,指向一个随机地址。故在使用*析取(dereference)之前,应确保指针指向合法的地址。 2)、delete某个指针后,指针所指向的变量(对象)被释放(生命周期结束),但是该指针变为野指针。 4.delete干掉了什么 一般用new运算符动态分配出来的堆内存,需要我们配对调用delete来显式回收内存,以防......

阅读全文(4661) | 评论:0

多重继承和虚基类(2009-02-28 21:21:00)

摘要://派生类成员函数对基类成员函数的覆盖 #include "stdafx.h" #include<iostream>
class A { public:      void Show()      {          std::cout<<"A::Show\n";      } };   class B:public A { public:      void Show()      {          std::cout<<"B::Show\n";      }      void Display()      {          Show();//调用派生类B的成员函数,覆盖掉继承自基类的Show()成员方法          A::Show();//调用基类A的成员函数      } }; int _tmain(int argc, _TCHAR* argv[]) {      A a;      B b;      a.Show();      b.Show();      b.Display();      std::cin.get......

阅读全文(2954) | 评论:0

C++继承(2009-02-28 21:16:00)

摘要:(1)派生方式 class <派生类名>:[派生方式]<基类名> {     //派生类新增成员的声明 }     在上述派生类的定义中,“派生方式”决定了基类成员在派生类中的访问权限。派生的方式共有public、private、protected(默认派生方式为private)。     虽然派生类继承了基类的所有成员,但是为了不破坏基类的封装性,无论采用哪种继承方式,基类的私有(private)成员在派生类中都是不可见的,即不允许在派生类的成员函数中访问基类的私有成员。 class Point { private:      int prt; public:      void setPrt(int t)      {          prt=t;      }    public:      void getPrt()      {          std::cout<<prt;      }    }; class Circle:public Point { public:      void visitPrt()      {          std::cout<<prt; //提示出错 getPrt();//利用基类封装的公有接口间接访问基类私有成员      }&......

阅读全文(3094) | 评论:0

C++构造函数和析构函数(2009-02-26 18:39:00)

摘要:(1)构造函数、析构函数与赋值函数 构造函数、析构函数与赋值函数是每个类最基本的函数。它们太普通以致让人容易麻痹大意, 其实这些貌似简单的函数就象没有顶盖的下水道那样危险。 每个类只有一个析构函数和一个赋值函数,但可以有多个构造函数(包含一个拷贝构造函数,其它的称为普通构造函数)。对于任意一个类A,如果不想编写上述函数,C++编译器将自动为A 产生四个缺省的函数,例如: A(void); // 缺省的无参数构造函数 A(const A &a); // 缺省的拷贝构造函数 ~A(void); // 缺省的析构函数 A & operate =(const A &a); // 缺省的赋值函数 这不禁让人疑惑,既然能自动生成函数,为什么还要程序员编写?原因如下: <1>如果使用“缺省的无参数构造函数”和“缺省的析构函数”,等于放弃了自主“初始化”和“清除”的机会,C++发明人Stroustrup 的好心好意白费了。 <2>“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。 对于那些没有吃够苦头的C++程序员,如果他说编写构造函数、析构函数与赋值函数很容易,可以不用动脑筋,表明他的认识还比较肤浅,水平有待于提高。 下面以类String 的设计与实现为例,深入阐述被很多教科书忽视了的道理。String的结构如下: class String {  public:        String(const char *str = NULL); // 普通构造函数     String(const String &other); // 拷贝构造函数     ~ String(void); // 析构函数        String & operate =(const String &other); // 赋值函数 private:      char *m_data; // 用于保......

阅读全文(9367) | 评论:0

define、const&inline(2009-02-26 18:28:00)

摘要:(1)#define宏的用法 #define用宏名代替一个字符串,这样便于修改,提高了程序的可移植性。编译器在编译预 处理时只对宏做文本替换,而不进行类型检查,所以替换后可能产生一些副作用。 带参数的宏类似于函数调用,但是宏替换不是函数,二者不是一回事。 #define square(n) (n)*(n) for(int i=1;i<6;i++) printf("%d\n",square(i++)); 以上语句执行输出的结果为:1,9,25 因为square(i++)被替换为(i++)*( i++),第一次执行后,i执行两次自增变为3……。 const的常量是一个Run-Time的概念,他在程序中确确实实的存在可以被调用、传递。而#define常量则是一个Compile-Time概念,它的生命周期止于编译期:在实际程序中他只是一个常数、一个命令中的参数,没有实际的存在。故使用宏,程序运行得较快,宏替换不占运行时间,只占编译时间;而使用函数调用,占有空间较小。 (2)const声明常量的用法 int const a;<==>const int a;//声明一个整数a,其值不可修改。 const int a=15;//可以声明时进行初始化以保常值。 int *pi;//pi是一个普通的指向整型的指针。 int const *pci;//const修饰的是int,pci是一个指向整型常量的指针,可以修改指针的值,但是不能修改它所指向的值,即无论pci指向哪一个sizeof(int)内存单元,其单元内容都是常整值。 int * const cpi;//const修饰的是int*,cpi是一个指向整型的常量指针,此时指针是常量,它的值无法修改,但可以修改它所指向的整型值,即无论cpi指向一个固定的sizeof(int)内存单元,其单元内容可以改变。 int const *const cpci;//第一个const修饰int,即后面的指针cpci指向整型常量;第二个const修饰int*,即指针也是常量。故无论是指针本身还是它所指向的值都是常量,不允许修改。 以下为示例程序片段:     int n1 = 2009;     int n2 = 2012; &nbs......

阅读全文(3474) | 评论:0

字符串与指针(2009-02-26 11:07:00)

摘要: 字符'\0'和'0'的区别     字符’0’对应的ASCII码为十六进制30;而’\0’即为ASCII码中的0,其对应字符空字符NUL。      char c='\0';çèchar c=0;//NUL      char c='0';çèchar c=48;//0     最典型如memset函数:void *memset( void *buffer, int ch, size_t count );     将一段长为count字节的内存块初始化为ASCII码值0,字符为NUL:     memset(pBuffer, '\0', sizeof(pBuffer) );çèmemset(pBuffer, 0, sizeof(pBuffer) );   字符串 字符串常量是双引号括起的任意字符序列。如:"Hello World","Fantasy","Please enter your full name:",…… C语言没有专门定义字符串数据类型(如其他语言中的string) ,所谓的字符串,只是对字符数组的一种特殊应用而已,它用以'\0'结尾的字符数组来表示一个逻辑意义上的字符串。 在字符串常量中,显然不能直接写双引号,因为这将被认为是字符串的结束。转义序列在字符串常量中要包含双引号,需要用“\"”表示。如:"Hello \"Accp\"" 与字符数组不同的是:在存完字符串常量的所有字符之后,还要另存一个空字符'\0'作为结束的标志,空字符是ASCII码值为0的字符,C语言中用'\0'标识字符串的结束,所以也称为结束符。如果在程序里写了字符串:char hello[]="HELLO"或{"HELLO"};虽然只有5个字符,在内存中却需要占用6个字节存储,其中'\0'表示空字符。存储情况如: H E L L O \0 5005 5006 5007    5008 5009......

阅读全文(3682) | 评论:0

函数指针与指针传参(2009-02-26 10:51:00)

摘要:1.函数指针 函数指针形式说明如下: <类型>(*指针变量名)(); 其调用格式如下所示: int max(int x,int y); //定义函数max int (*funcp)();//定义返回值为整型的函数指针 funcp=max;//将函数名(函数入口地址)传给funcp,使其指向函数 //上两行代码等价于int (*funcp)(int,int)=&max; (*funcp)(3,4));//利用函数指针调用函数: (*funcp)=max; 若把指向函数的指针作为参数传递到其他函数中,则可以编一个通用的函数来完成各种专用的功能,每次调用函数时给出不同的函数名作为实参即可,这样大大增加了函数使用的灵活性。C#中的委托类似函数指针。 int max(int x,int y){  return (x>y?x:y);} int min(int x,int y){  return (x<y?x:y);} int add(int x,int y){  return x+y;} process(int x,int y,int(*funcp)()) {    printf("%d\n",(*funcp)(x,y));} void main() {      int a,b;      printf("Please input int a and int b:\n");      scanf("%d,%d",&a,&b);  printf("\n");      printf("max(a,b)=");process(a,b,max);      printf("min(a,b)=");process(a,b,min);      printf("add(a,b)=");process(a,b,add); }     函数指针是Windows回......

阅读全文(6406) | 评论:0

数组与指针(2009-02-26 10:48:00)

摘要:  任何能由数组下标完成的操作也可由指针来完成,一个不带下标的数组名就是一个指向此数组的指针,在C语言中数组名就是数组的地址。当一个指针变量被初始化为数组名时,就说该指针变量指向了数组。       char str[20],*pstr;     pstr=str等价于pstr=&str[0]; //指针被置为数组第一个元素的地址 访问数组第6个元素:str[5],pstr[5], *(str+5),*(pstr+5)。 值得注意的是pstr是一个可以变化的指针变量,因此pstr++;++pstr; pstr+=5都是正确的,而str是一个常数。因为数组一经说明,数组的地址也就被固定了,故str++;++str; str+=5都是错误的。 编译系统在处理str[i]时,实际上是将数组元素的形式str[i]转换为*(str+i),然后再进行运算的。相应的引用二维数组a[i][j]则等价于(*(a+i))[j]或*(*(a+i)+j),通常式子*(a+i)+j是用来计算元素所在内存地址,并不是它的内容。     int a[3][4]; //二维整型数组     int (*p)[4]; //整型指针数组 p=a; 则p+1不是指向a[0][1],而是指向a[1]。这是p的增值以一维数组长度为单位。 假设是这么一个数组:   int  arr[20];   则arr 的内存示意图为:
(数组 arr 的内存示意) 和指针变量相比, 数组没有一个单独的内存空间而存放其内存地址。即:指针变量p是一个独立的变量,只不过它的值指向另一段连续的内存空间;而数组arr,本身代表的就是一段连续空间。 如果拿房间来比喻。指针和数组都是存放地址。只不过,指针是你口袋里的那本通讯录上写着的地址,你可以随时改变它的内容,甚至擦除。而数组是你家门楣上钉着的地址,你家原来是“复兴路甲108号”,你绝对不能趁月黑天高,把它涂改为“唐宁街10号”。  数组是“实”的地址,不能改变。当你和定义一个数组,则这个数组就得根据它在内存中的位置,得到一个地址,如上图中的“0x1A000000”。......

阅读全文(2635) | 评论:1

指针(2009-02-26 10:37:00)

摘要:1.指针,变量的指针,指针变量
由于通过地址能找到所需的变量单元,我们可以说,地址“指向该变量单元”,在C语言中,将地址形象化的称为“指针”,一个变量的地址称为该“变量的指针”,意思是通过它能找到以它为地址的内存单元。指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。 在32位程序里,所有类型的指针的值都是一个32位整数。因为32位机中的程序里内存地址全都是32位长,即sizeof(pointer)的值总为4—指针本身占据了4个字节的长度。在64位机中,sizeof(pointer)的值为8. 如果一个变量专门用来存放另一个变量的地址,则它称为“指针变量”,我们说它用来存放指针。定义了一个变量p,它用来保存另一个变量var的地址,这样的p就是指向var的指针变量。 指针变量也是变量,其定义格式为:类型标识符 * 指针标识符,*号为(地址解析符,表示“指向……的指针”,可以左结合,也可以右结合,其中类型标识符 *为指针的类型,类型标识符为指针所指向的类型。例如: char *pc; pc具有char *类型,即pc指向char类型的变量,以1个字节为一个存取单元。 int *pi; pi具有int *类型,即pi指向int类型的变量,以4个字节为一个存取单元。 float* pf; pf具有float *类型,即pf指向float类型的变量,以4个字节为一个存取单元。 char *pc="hello"; <==>char *pc;pc="hello"; 2.指针变量的引用   C语言中对指针变量的引用主要通过运算符“&”和“*”来实现的。 &——取变量的地址。 *——取指针变量所指向的变量的值。 观察下面的程序段: int x,y,*p;//定义整型变量x、y和整型指针变量p     x=168;//初始化x     p=&x;//初始化p  y=*p;//初始化y 上述内存变化情况如图所示:     若int a=168;    int *p=&a;则*&a表示变量a本身,而&......

阅读全文(2970) | 评论:0