C/C++常见考题深入分析之非完整版(我想永远也不可能完整的,呵呵)

首先请看如下代码:

#include <stdio.h>

int getLength(char a[]);

int main()
{
 char a[9]="123456789";
 char b[10]="123456789";
 char *b="123456789";
 printf("sizeof a[]:%d/n",sizeof(a)); /* return the length of array a,including '/0'*/
 printf("After the function getLength() was called:%d/n",getLength(a));
 printf("sizeof b:%d/n",sizeof(b));  /* return the length of pointer which type is int*/
 return 0;
}

int getLength(char a[])
{
 return sizeof(a);
}

这段代码将引起 编译错误,错误信息如下:

test_5.cpp(7) : error C2117: '123456789' : array bounds overflow

数组边界溢出这个不用做过多解释了吧。

我们将char a[9]="123456789";注释掉,继续编译。程序通过连编,结果如下:

sizeof a[]:10
After the function getLength() was called:4
sizeof b:4

这次我们发现同样是sizeof,通过函数传入参数和直接使用该参数,结果是不一样的。那么肯定是函数调用的过程中发生了什么。到底发生了什么呢?我也说不清楚,但是肯定有人能说清楚,=_=b

然后,我们对该程序进行调试,在我的机器上(以下测试均在我的机器上,你们可能会有不同结果),a的地址为0x12ff74,b的地址为0x00422fc,他们的内容为字符串123456789,注意:在9后面还有一个'/0'。

我们再在程序char *b="123456789";后面添加一句:char *c="123456789";然后再调试该程序,我们发现c的地址为0x00422fc,和b指向同一个地址。

我们再往语句char a[9]="123456789";的前面添加2行:int i,j;继续调试,我们发现i的地址为0x0012ff7c,j的地址为0x0012ff78。i和j均为局部变量,应该在栈上分配内存,按照顺序,先分配i的内存,然后分配j的内存,而i的地址大于j的地址,所以我们可以得出栈是从高地址到低地址的,也就是栈的底部在高地址端,栈的方向是向下的。

我们继续添加内容,在程序的开始加入:int *k;在return 0;之前加入k=new int(10);,这时我们可以通过调试发现执行k=new int(10);之前k的值是0xcccccccc,所有指针在初始化之前都是这个值(仅限这次测试的机器)。执行了k=new int(10);之后,k的值为0x00371000。由于new一般是在堆上分配内存,所以我们可以猜测堆就在0x00371000这附近,然后再继续加入语句:int *l;以及l=new int(20);调试发现l的地址为0x00371048。再继续加入语句:int *m;以及m=new(30);调试发现地址为0x00371090,这里我们能找到的规律就是越往后分配的内存其地址越大,所以它的方向是向上的,而且每次每次分配的内存块大小都是0x48字节。这样我们得出结论:动态分配内存按块分配,而且方向由低地址向高地址。

关于C++友元。我们需要记住的是友元不能是虚函数,因为友元不是类成员,而只有成员才能是虚函数。如果由于这个原因引起了设计问题,可以通过让友元函数调用虚拟成员函数来解决。

没有重新定义。假设创建了如下所示的代码:

class Dwelling
{
public:
 virtual void showperks(int a) const;
};

void Dwelling::showperks(int a) const
{}

class Hovel:public Dwelling
{
public:
 virtual void showperks() const;
};

void Hovel::showperks() const
{}

int main()
{
 Hovel trump;
 trump.showperks();
 return 0;
}

但是调用trump.showperks(5);则发生错误。这说明在子类中重新定义基类函数不会生成函数的两个重载版本,而是隐藏了接受一个int参数的基类版本。简而言之,重新定义继承的方法并不是重载。

这引出了两条经验规则:第一,如果重新定义继承的方法,应确保与原来的原型完全相同,但如果返回类型是基类引用或指针,则可以修改为指向派生类的引用或指针(这种例外是新出现的),但是VC 6好像不支持。

第二,如果基类声明被重载了,则应在派生类中重新定义所有的基类版本。

单件模式(Singleton)。代码如下。

#include <iostream>

using namespace std;

class TheOnlyInstance
{
public:
 static TheOnlyInstance* GetTheOnlyInstance();
 void OtherMethod(){cout<<"other method called./n";}
protected:
 TheOnlyInstance(){}
private:
 static TheOnlyInstance *instance;
};

TheOnlyInstance * TheOnlyInstance::instance=NULL;

TheOnlyInstance* TheOnlyInstance::GetTheOnlyInstance()
{
 if(instance==NULL)
 {
  instance=new TheOnlyInstance;
  return instance;
 }
 else return instance;
}

int main()
{
 TheOnlyInstance *pt=TheOnlyInstance::GetTheOnlyInstance();
 pt->OtherMethod();
 return 0;
}

赋值操作符重载。这点很容易犯错的,稍不注意,就让人觉得你没有经验。请看如下代码:

class BaseDMA
{
private:
 char *label;
public:
 BaseDMA(){};
 ~BaseDMA(){};
 BaseDMA & operator=(const BaseDMA &rs);
};

BaseDMA & BaseDMA::operator =(const BaseDMA &rs)
{
 if(this==&rs)
  return *this;
 delete [] label;
 label=new char[strlen(rs.label)+1];
 strcpy(label,rs.label);
 return *this;
}

我们很容易忽略的一点就是a=a的情况。

请看如下代码

#include <stdio.h>
int main()
{
 int i;
 int a[3]={1,2,3};
 int *b[3];
 for(i=0;i<3;i++)
 {
  b[i]=&a[i];
  printf("%d/n",b[i]);
 }
 printf("%d",b[1]-b[0]);
 return 0;
}

它的输出如下所示(在我的机器上):

1245040
1245044
1245048
1

我就一不小心中了陷阱,将最后一个写成了4,晕了。

随后的东西留到以后继续慢慢想(to be continued)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值