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

來源:互聯網
上載者:User

首先請看如下代碼:

#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)

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.